source: trunk/athena/bin/discuss/edsc/do_cache.c @ 8855

Revision 8855, 16.1 KB checked in by ghudson, 28 years ago (diff)
BSD -> ANSI string and memory functions
Line 
1/*
2 *
3 *      Copyright (C) 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8
9/*
10 *
11 * do_cache.c -- Routines to implement transaction caching/look ahead
12 *
13 *      (Not yet working --- when it is, it will replace the emacs
14 * lisp code, and things will be Much Faster.)
15 */
16
17#include <stdio.h>
18#include <sys/types.h>
19#include <sys/time.h>
20#include <sys/file.h>
21#include <errno.h>
22#include <signal.h>
23#include <string.h>
24#include <sys/wait.h>
25#include <ctype.h>
26#include <netdb.h>
27#include <sys/ioctl.h>
28#include <stdio.h>
29
30#include "edsc.h"
31
32#ifdef EDSC_CACHE
33
34int     cache_working;
35
36struct cache_info *top_used, *bot_used, *top_empty;
37
38struct cache_info *cache;
39int     cachesize;
40
41struct cache_meeting *cache_meetings;
42
43char                    cache_directory[20];
44cache_dir               cache_strategy[CACHE_STRATEGY_SIZE];
45int                     cache_pc;
46struct cache_info       *cache_current, *cache_ptr;
47cache_dir               cache_current_direction;
48
49cache_dir               cache_default_strategy[] = {
50        CACHE_OP_GET_CUR,
51        CACHE_OP_FETCH_CUR | CACHE_DIR_NEXT,
52        CACHE_OP_FETCH_CUR | CACHE_DIR_NREF,
53        CACHE_OP_FETCH_CUR | CACHE_DIR_PREV,
54        CACHE_OP_FETCH_CUR | CACHE_DIR_PREF,
55        CACHE_OP_FETCH_CUR | CACHE_DIR_FREF,
56        CACHE_OP_FETCH_CUR | CACHE_DIR_LREF,
57        CACHE_OP_SET_PTR,
58        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
59        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
60        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
61        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
62        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
63        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
64        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
65        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
66        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
67        CACHE_OP_MFETCH_PTR | CACHE_DIR_CURRENT,
68        CACHE_OP_STOP
69        };
70
71/*
72 * This is called from the main loop of discuss.  We don't return
73 * until we see that there is pending input on stdin.
74 */
75void do_cache_work()
76{
77        cache_dir       ins;
78        int             skip_poll; /* If true, don't poll */
79        int             retval;
80        int             next_trn; /* Next transaction to get */
81        struct cache_info       *new_ptr;
82       
83        skip_poll = 1;
84        while (skip_poll || !poll_input(1)) {
85                skip_poll = 0;
86                ins = cache_strategy[cache_pc];
87#ifdef CACHE_DEBUG
88                fprintf(stderr, "- Cache instruction: %02x\n", ins);
89#endif
90                switch (ins & CACHE_OP_MASK) {
91                case CACHE_OP_STOP:
92                        cache_working = 0;
93                        return;
94                case CACHE_OP_GET_CUR:
95                        retval = cache_transaction(&cache_current->meeting->nb,
96                                                   cache_current->trn_num,
97                                                   NULL);
98                        if (retval) {
99                                if (retval == -1)
100                                        skip_poll++;
101                        } else
102                                cache_pc--;
103                        break;
104                case CACHE_OP_FETCH_CUR:
105                        next_trn = move_trn(cache_current, ins&CACHE_DIR_MASK);
106                        if (!next_trn) {
107                                skip_poll++;
108                                break;
109                        }
110                        retval = cache_transaction(&cache_current->meeting->nb,
111                                                   next_trn, NULL);
112                        if (retval) {
113                                if (retval == -1)
114                                        skip_poll++;
115                        } else
116                                cache_pc--;
117                        break;
118                case CACHE_OP_SET_PTR:
119                        cache_ptr = cache_current;
120                        skip_poll++;
121                        break;
122                case CACHE_OP_MFETCH_PTR:
123                        if (!cache_ptr) {
124                                skip_poll++;
125                                break;
126                        }
127                        next_trn = move_trn(cache_ptr, ins & CACHE_DIR_MASK);
128                        if (!next_trn) {
129                                skip_poll++;
130                                break;
131                        }
132                        retval = cache_transaction(&cache_ptr->meeting->nb,
133                                                   next_trn,
134                                                   &new_ptr);
135                        if (retval) {
136                                cache_ptr = new_ptr;
137                                if (retval == -1)
138                                        skip_poll++;
139                        } else
140                                cache_pc--;
141                        break;
142                }
143                cache_pc++;
144        }
145}
146
147int move_trn(entry, dir)
148        struct cache_info       *entry;
149        cache_dir               dir;
150{
151        if (!(entry->flags & CACHE_FLG_INFO) || entry->info_code)
152                return(0);
153        if (dir == CACHE_DIR_CURRENT)
154                dir = cache_current_direction;
155        switch (dir) {
156        case CACHE_DIR_NEXT:
157                return(entry->t_info.next);
158        case CACHE_DIR_PREV:
159                return(entry->t_info.prev);
160        case CACHE_DIR_NREF:
161                return(entry->t_info.nref);
162        case CACHE_DIR_PREF:
163                return(entry->t_info.pref);
164        case CACHE_DIR_FREF:
165                return(entry->t_info.fref);
166        case CACHE_DIR_LREF:
167                return(entry->t_info.lref);
168        }
169        return(0);
170}
171
172/*
173 * Set the current direction....
174 */
175do_scd(args)
176char *args;
177{
178     char *cp = args, delim, *dir_string;
179     int        newdir;
180     
181
182     /* we get the direction */
183     if (get_word(&cp, &dir_string, ")", &delim) < 0) {
184          printf(";Missing direction\n");
185          return;
186     }
187     newdir = atoi(dir_string);
188     if ((newdir > 0) && (newdir <= 6))
189             cache_current_direction = newdir;
190     printf("()\n");
191}
192
193/*
194 * Invalidate transaction
195 */
196do_it(args)
197char *args;
198{
199     char *cp = args, *mtg_name, delim, *trn_string;
200     trn_nums trn_num;
201     name_blk nb;
202     int        code;
203
204     /* First, we get the transaction number */
205     if (get_word(&cp, &trn_string, " ", &delim) < 0) {
206          printf(";Missing trn number\n");
207          return;
208     }
209
210     if (get_word(&cp, &mtg_name, ")", &delim) < 0) {
211          printf(";Missing meeting name\n");
212          return;
213     }
214
215     trn_num = atoi(trn_string);
216     dsc_get_mtg (user_id, mtg_name, &nb, &code);
217     if (code != 0) {
218          printf(";%s\n", error_message(code));
219          return;
220     }
221     cache_it(&nb, trn_num);
222     printf("()\n");
223}
224
225cache_it(nbp, trn_num)
226     name_blk *nbp;
227     trn_nums trn_num;
228{
229     struct     cache_info      *entry;
230
231     if (entry = cache_search(nbp, trn_num)) {
232             if (entry->flags & CACHE_FLG_INFO)
233                     dsc_destroy_trn_info3(&entry->t_info);
234             entry->flags &= ~(CACHE_FLG_INFO|CACHE_FLG_TEXT);
235             entry->info_code = entry->text_code = 0;
236     }
237}
238
239/*
240 * Invalidate transaction and neighbors
241 */
242do_itn(args)
243char *args;
244{
245     char *cp = args, *mtg_name, delim, *trn_string;
246     trn_nums trn_num;
247     name_blk nb;
248     int        code;
249
250     /* First, we get the transaction number */
251     if (get_word(&cp, &trn_string, " ", &delim) < 0) {
252          printf(";Missing trn number\n");
253          return;
254     }
255
256     if (get_word(&cp, &mtg_name, ")", &delim) < 0) {
257          printf(";Missing meeting name\n");
258          return;
259     }
260
261     trn_num = atoi(trn_string);
262     dsc_get_mtg (user_id, mtg_name, &nb, &code);
263     if (code != 0) {
264          printf(";%s\n", error_message(code));
265          return;
266     }
267     cache_itn(&nb, trn_num);
268     printf("()\n");
269}
270
271cache_itn(nbp, trn_num)
272     name_blk *nbp;
273     trn_nums trn_num;
274{
275     struct     cache_info      *current;
276             
277
278     if (!(current = cache_search(nbp, trn_num))) {
279             while (!current || !(current->flags & CACHE_FLG_INFO))
280                     cache_transaction(nbp, trn_num, &current);
281     }
282     cache_it(nbp, current->t_info.next);
283     cache_it(nbp, current->t_info.prev);
284     cache_it(nbp, current->t_info.nref);
285     cache_it(nbp, current->t_info.pref);
286     cache_it(nbp, current->t_info.fref);
287     cache_it(nbp, current->t_info.lref);
288     if (current->flags & CACHE_FLG_INFO)
289             dsc_destroy_trn_info3(&current->t_info);
290     current->info_code = current->text_code = 0;
291     current->flags &= ~(CACHE_FLG_INFO|CACHE_FLG_TEXT);             
292}
293       
294/*
295 * Cache flush meeting --- flush the cache of all transactions from a meeting
296 */
297do_im(args)
298char *args;
299{
300     char *cp = args, *mtg_name, delim;
301     name_blk nb;
302     int code;
303     struct cache_meeting       *meeting;
304     struct cache_info *curr = top_used, *next;
305
306     /* First, we get the meeting name */
307     if (get_word(&cp, &mtg_name, ")", &delim) < 0) {
308          printf(";Missing meeting name\n");
309          return;
310     }
311
312     dsc_get_mtg (user_id, mtg_name, &nb, &code);
313     if (code != 0) {
314          printf(";%s\n", error_message(code));
315          return;
316     }
317
318     if (meeting = search_cache_meetings(&nb)) {
319             while (curr) {
320                     next = curr->next;
321                     if (curr->meeting == meeting) {
322                             if (curr->prev)
323                                     curr->prev->next = curr->next;
324                             else
325                                     top_used = curr->next;
326                             if (curr->next)
327                                     curr->next->prev = curr->prev;
328                             else
329                                     bot_used = curr->prev;
330                             free_cache_entry(curr);
331                             curr->next = top_empty;
332                             top_empty = curr;
333                     }
334                     curr = next;
335             }
336     }
337     dsc_destroy_name_blk(&nb);
338     printf("()\n");
339}
340
341
342/*
343 * Suck a transaction into the cache.  Returns true when the
344 * transaction has been entered into the cache.  If it returns false,
345 * the cache_transaction should be called again with the same arguments.
346 */
347int cache_transaction(nbp, trn_num, trn_entry)
348        name_blk        *nbp;
349        trn_nums trn_num;
350        struct cache_info **trn_entry;
351{
352        struct  cache_info      *entry, *link;
353        trn_nums                trn;
354        cache_dir               i;
355
356        entry = cache_search(nbp, trn_num);
357#ifdef CACHE_DEBUG
358        fprintf(stderr, "- cache_transaction(%d)\n", trn_num);
359#endif
360        if (!entry) {
361                entry = cache_empty();
362                entry->meeting = get_cache_meeting(nbp);
363                entry->trn_num = trn_num;
364        }
365        if (trn_entry)
366                *trn_entry = entry;
367        if (!(entry->flags & CACHE_FLG_INFO)) {
368                dsc_get_trn_info3(&entry->meeting->nb, entry->trn_num,
369                                  &entry->t_info, &entry->info_code);
370                entry->flags |= CACHE_FLG_INFO;
371                if (!entry->info_code) {
372                        for (i=CACHE_DIR_NEXT; i <= CACHE_DIR_PREF; i++) {
373                                if ((trn = move_trn(entry, i)) &&
374                                    (link = cache_search(nbp, trn)) &&
375                                    (link->flags & CACHE_FLG_INFO) &&
376                                    (move_trn(link, ((i-1)^1)+1) != trn))
377                                        cache_it(nbp, trn);
378                        }
379                }
380                return (entry->flags & CACHE_FLG_TEXT);
381        }
382        if (!(entry->flags & CACHE_FLG_TEXT)) {
383                cache_get_transaction_text(entry);
384                return(1);
385        }
386        return(-1);
387}
388
389void cache_get_transaction_text(entry)
390        struct cache_info       *entry;
391{
392        trn_info3 tinfo;
393        tfile tf;
394        int fd;
395        char *plural;
396        char line[255];
397        int code;
398       
399        /*
400         * Flag the fact that we tried to get the transaction text.
401         */
402        entry->flags |= CACHE_FLG_TEXT;
403       
404        /*
405         * Find a filename that's not in use and read the
406         * transaction text into that file.
407         */
408        sprintf(entry->filename, "%s/edsc-%d-XXXXXX", cache_directory,
409                entry->trn_num);
410        if ((fd = mkstemp(entry->filename)) < 0) {
411                entry->text_code = errno;
412                return;
413        }       
414
415        tf = unix_tfile(fd);
416
417        tinfo = entry->t_info;
418       
419        if (tinfo.num_lines != 1)
420                plural = "s";
421        else
422                plural = "";
423     
424        if (tinfo.subject [0] == '\0')
425                tinfo.num_lines--;
426
427        if (tinfo.signature && strcmp(tinfo.signature, tinfo.author))
428                (void) sprintf (line, "[%04d]%c %s (%s) %s %s (%d line%s)\n",
429                                tinfo.current,
430                                tinfo.flags & TRN_FLAG1 ? 'F' : ' ',
431                                tinfo.author, tinfo.signature,
432                                entry->meeting->m_info.long_name,
433                                short_time (&tinfo.date_entered),
434                                tinfo.num_lines, plural);
435        else
436                (void) sprintf (line, "[%04d]%c %s %s %s (%d line%s)\n",
437                                tinfo.current,
438                                tinfo.flags & TRN_FLAG1 ? 'F' : ' ',
439                                tinfo.author,
440                                entry->meeting->m_info.long_name,
441                                short_time (&tinfo.date_entered),
442                                tinfo.num_lines, plural);
443        twrite (tf, line, strlen (line), &code);
444        if (tinfo.subject [0] != '\0') {
445                twrite (tf, "Subject: ", 9, &code);
446                twrite (tf, tinfo.subject, strlen (tinfo.subject), &code);
447                twrite (tf, "\n", 1, &code);
448        }
449
450        dsc_get_trn(&entry->meeting->nb, entry->trn_num, tf, &code);
451        if (code != 0) {
452                entry->text_code = code;
453                return;
454        }
455
456        if (tinfo.pref == 0 && tinfo.nref == 0)
457                (void) sprintf (line, "--[%04d]--\n", tinfo.current);
458        else if (tinfo.pref == 0)
459                (void) sprintf (line, "--[%04d]-- (nref = [%04d])\n",
460                                tinfo.current, tinfo.nref);
461        else if (tinfo.nref == 0)
462                (void) sprintf (line, "--[%04d]-- (pref = [%04d])\n",
463                                tinfo.current, tinfo.pref);
464        else
465                (void) sprintf (line,
466                                "--[%04d]-- (pref = [%04d], nref = [%04d])\n",
467                                tinfo.current, tinfo.pref, tinfo.nref);
468        twrite (tf, line, strlen (line), &code);
469       
470        tclose(tf, &code);
471        (void) close(fd);
472        if (code != 0) {
473                entry->text_code = code;
474                return;
475        }
476}
477
478/*
479 * Initialize the cache
480 */
481void cache_init(size)
482        int     size;
483{
484        int     i;
485       
486        if (cache)
487                cache_flush();
488        if (!size)
489                size = CACHE_DEFAULT_SIZE;
490        if (cachesize && (cachesize != size))
491                return;
492        if (cache)
493                free(cache);
494        cache = (struct cache_info *) malloc(sizeof(struct cache_info)*size);
495        memset(cache, 0, sizeof(struct cache_info)*size);
496        top_used = bot_used = NULL;
497        top_empty = cache;
498        for (i = 1; i < size; i++)
499                cache[i-1].next = cache+i;
500        cache[size-1].next = NULL;
501        cachesize = size;
502
503        i = 0;
504        while (1) {
505                if ((cache_strategy[i] = cache_default_strategy[i])
506                    == CACHE_OP_STOP)
507                        break;
508                i++;
509        }
510        cache_current_direction = CACHE_DIR_NEXT;
511try_again:
512        sprintf(cache_directory, "%s/.edscXXXXXX", CACHE_DIR);
513        mktemp(cache_directory);
514        if (mkdir(cache_directory, 0711)) {
515                if (errno == EEXIST)
516                        goto try_again;
517                printf("; Couldn't create cache directory %s! --- %s\n",
518                       cache_directory, error_message(errno));
519                exit(1);
520        }
521}
522
523/*
524 * Flush the cache
525 */
526void cache_flush()
527{
528        struct cache_info *curr = top_used;
529        struct cache_info *next = NULL;
530
531        while (curr) {
532                free_cache_entry(curr);
533                next = curr->next;
534                curr->next = top_empty;
535                top_empty = curr;
536                curr = next;
537        }
538        top_used = bot_used = NULL;
539}
540
541/*
542 * Cache shutdown
543 */
544cache_shutdown()
545{
546        cache_flush();
547        (void) rmdir(cache_directory);
548}
549
550/*
551 * Free a cache entry
552 */
553void free_cache_entry(entry)
554        struct cache_info       *entry;
555{
556        if (entry->filename[0])
557                unlink(entry->filename);
558        entry->filename[0]='\0';
559        if (entry->flags & CACHE_FLG_INFO)
560                dsc_destroy_trn_info3(&entry->t_info);
561        entry->flags = 0;
562        if (!(--entry->meeting->ref_count))
563                free_cache_meeting(entry->meeting);
564}
565
566/*
567 * Find empty cache entry, flushing an old entry if necessary....
568 * Splice new entry into the top of the LRU list.
569 */
570struct cache_info *cache_empty()
571{
572        struct cache_info       *empty;
573       
574        if (top_empty) {
575                empty = top_empty;
576                top_empty = top_empty->next;
577        } else {
578                empty = bot_used;
579                bot_used = bot_used->prev;
580                bot_used->next = NULL;
581                free_cache_entry(empty);
582        }
583        if (top_used)
584                top_used->prev = empty;
585        else
586                bot_used = empty;
587        empty->next= top_used;
588        empty->prev=NULL;
589        top_used = empty;
590        empty->flags = 0;
591        return(empty);
592}
593
594/*
595 * Search cache, placing found cache at the top of the LRU list
596 */
597struct cache_info *cache_search(nbp, trn)
598        name_blk        *nbp;
599        trn_nums trn;
600{
601        struct cache_info       *curr = top_used;
602       
603        while (curr) {
604                if ((trn == curr->trn_num) &&
605                    !strcasecmp(nbp->hostname, curr->meeting->nb.hostname) &&
606                    !strcasecmp(nbp->pathname, curr->meeting->nb.pathname))
607                        break;
608                curr = curr->next;
609        }
610        if (curr) {
611                if (curr->prev) {
612                        /* move cache entry to the top */
613                        curr->prev->next = curr->next;
614                        if (curr->next)
615                                curr->next->prev = curr->prev;
616                        else
617                                bot_used = curr->prev;
618                       
619                        top_used->prev = curr;
620                        curr->next = top_used;
621                       
622                        curr->prev = NULL;
623                        top_used = curr;
624                }
625                return(curr);
626        } else
627                return(NULL);
628}
629
630/*
631 * Cache meeting subroutines
632 */
633
634/*
635 * Get the cache_meeting structure for a meeting.  If it doesn't
636 * exist, create a cache_meeting structure for it.
637 */
638struct cache_meeting *get_cache_meeting(nbp)
639        name_blk        *nbp;
640{
641        struct cache_meeting    *curr;
642        int                     code;
643       
644        for (curr=cache_meetings; curr; curr = curr->next) {
645                if (!strcasecmp(nbp->hostname, curr->nb.hostname) &&
646                    !strcasecmp(nbp->pathname, curr->nb.pathname))
647                        break;
648        }
649        if (!curr) {
650                curr = (struct cache_meeting *)
651                        malloc(sizeof(struct cache_meeting));
652                if (!curr) {
653                        printf("; Abort!  malloc for cache_meeting failed!\n");
654                        abort();
655                }
656                curr->ref_count = 0;
657                dsc_copy_name_blk(nbp, &curr->nb);
658                dsc_get_mtg_info(nbp, &curr->m_info, &code);
659                while (code == MTG_MOVED)
660                        handle_moved_meeting(&curr->nb, &curr->m_info, &code,
661                                             curr->nb.aliases[0], TRUE);
662                curr->meeting_code = code;
663                curr->next = cache_meetings;
664                curr->prev = NULL;
665                if (cache_meetings)
666                        cache_meetings->prev = curr;
667                cache_meetings = curr;
668        }
669        curr->ref_count++;
670        return(curr);
671}
672
673struct cache_meeting *search_cache_meetings(nbp)
674        name_blk        *nbp;
675{
676        struct cache_meeting    *curr;
677       
678        for (curr=cache_meetings; curr; curr = curr->next) {
679                if (!strcasecmp(nbp->hostname, curr->nb.hostname) &&
680                    !strcasecmp(nbp->pathname, curr->nb.pathname))
681                        return(curr);
682        }
683        return(NULL);
684}
685
686
687/*
688 * Free a cache meeting
689 */
690void free_cache_meeting(meeting)
691        struct cache_meeting    *meeting;
692{
693        /*
694         * Split around the cache meeting information
695         */     
696        if (meeting->prev)
697                meeting->prev->next = meeting->next;
698        else
699                cache_meetings = meeting->next;
700        if (meeting->next)
701                meeting->next->prev = meeting->prev;
702        dsc_destroy_name_blk(&meeting->nb);
703        dsc_destroy_mtg_info(&meeting->m_info);
704        free(meeting);
705}
706
707/* Procedure to check for waiting input on a file descriptor.
708 * Does not actually read the input.
709 */
710
711int poll_input(f)
712        int f;
713{
714        struct timeval  timeout;
715        fd_set          readfds;
716
717#ifdef CACHE_DEBUG
718        fprintf(stderr, "(Polling)\n");
719#endif
720        FD_ZERO(&readfds);
721        FD_SET(f, &readfds);
722        timeout.tv_sec = 0;
723        timeout.tv_usec = 0;
724        return(select(f+1, &readfds, 0, 0, &timeout));
725}
726
727#endif
Note: See TracBrowser for help on using the repository browser.