home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / TELECOM / tass.lzh / art.c next >
Text File  |  1993-01-24  |  24KB  |  979 lines

  1.  
  2.  
  3. #include       <stdio.h>
  4. #include       <time.h>
  5. #include       <signal.h>
  6. #include       <errno.h>
  7. #include       "tass.h"
  8.  
  9.  
  10. /* Hopefully one of these is right for you. */
  11.  
  12. #ifdef BSD
  13. #      include <sys/types.h>
  14. #      include <sys/dir.h>
  15. #      define          DIR_BUF         struct direct
  16. #      define          D_LENGTH        d_namlen
  17. #endif
  18. #ifdef M_XENIX
  19. #      include <sys/ndir.h>
  20. #      define          DIR_BUF         struct direct
  21. #      define          D_LENGTH        d_namlen
  22. #endif
  23. #ifdef OSK
  24. #      include <ctype.h>
  25. #      include <modes.h>
  26. #      include <dir.h>
  27. #      define          DIR_BUF         struct direct
  28. #      define          D_LENGTH        d_namlen
  29. #      undef           SPOOLDIR
  30. #      define          SPOOLDIR        spooldir
  31. #endif /* OSK */
  32. #ifndef DIR_BUF
  33. #      include <sys/types.h>
  34. #      include <dirent.h>
  35. #      define          DIR_BUF         struct dirent
  36. #      define          D_LENGTH        d_reclen
  37. #endif
  38.  
  39. extern char    *index ();
  40. extern char    *realloc ();
  41.  
  42. char index_file[LEN+1];
  43. char *glob_art_group;
  44.  
  45. static int     complete_count;
  46. static int     complete_expire;
  47.  
  48. #ifdef SIGTSTP
  49. void
  50. art_susp(i)
  51. int i;
  52. {
  53.  
  54.        Raw(FALSE);
  55.        putchar('\n');
  56.        signal(SIGTSTP, SIG_DFL);
  57.        kill(0, SIGTSTP);
  58.  
  59.        signal(SIGTSTP, art_susp);
  60.        Raw(TRUE);
  61.  
  62.        mail_setup();
  63.        ClearScreen();
  64.        MoveCursor(LINES, 0);
  65.        printf("Group %s...    ", glob_art_group);
  66.        fflush(stdout);
  67. }
  68. #endif
  69.  
  70.  
  71. /*
  72.  *  Convert a string to a long, only look at first n characters
  73.  */
  74.  
  75. my_atol(s, n)
  76. char *s;
  77. int n;
  78. {
  79.        long ret = 0;
  80.  
  81.        while (*s && n--) {
  82.                if (*s >= '0' && *s <= '9')
  83.                        ret = ret * 10 + (*s - '0');
  84.                else
  85.                        return -1;
  86.                s++;
  87.        }
  88.  
  89.        return ret;
  90. }
  91.  
  92. # ifdef                LOCK_INDEX
  93. /*
  94.  *     Lock the index when modifieng it (only on global indicies)
  95.  */
  96. static char *
  97. lock_filename (fle)
  98. char *fle;
  99. {
  100. #ifndef        OSK
  101.        static int      siz = -1;
  102.        static char     *fname = NULL;
  103.        int             len;
  104.  
  105.        if ((len = strlen (fle)) > siz) {
  106.                siz = len;
  107.                if (! (fname = realloc (fname, siz + 16)))
  108.                        return (NULL);
  109.        }
  110.        sprintf (fname, "%s_lock", fle);
  111.        return (fname);
  112. #else
  113.        static char     fname[64];
  114.        unsigned long   n;
  115.        char            *ptr;
  116.  
  117.        for (ptr = fle, n = 0; *ptr; n += *ptr++ & 0xff)
  118.                ;
  119.        sprintf (fname, "/dd/TMP/%u_lock", n);
  120.        return (fname);
  121. #endif
  122. }
  123.  
  124. # ifdef                OSK
  125. static void
  126. stop_signals ()
  127. {
  128.        sigmask (1);
  129. }
  130.  
  131. static void
  132. start_signals ()
  133. {
  134.        sigmask (-1);
  135. }
  136. # else         /* OSK */
  137. typedef int    SIGTYPE;
  138.  
  139. static SIGTYPE (*signals)()[NSIG];
  140.  
  141. static void
  142. stop_signals ()
  143. {
  144.        int     t;
  145.  
  146.        for (t = 0; t < NSIG; ++t)
  147.                signals[t] = signal (t, SIG_IGN);
  148. }
  149.  
  150. static void
  151. start_signals ()
  152. {
  153.        int     t;
  154.  
  155.        for (t = 0; t < NSIG; ++t)
  156.                signal (t, signals[t]);
  157. }
  158. # endif                /* OSK */
  159.  
  160. static int     lock_fd = -1;
  161.  
  162. static int
  163. lock_index (fle)
  164. char *fle;
  165. {
  166.        int             tout;
  167.        char            *fn;
  168.  
  169.        if (lock_fd != -1)
  170.                close (lock_fd);
  171.        if (! (fn = lock_filename (fle)))
  172.                return (-1);
  173.        for (tout = 20; tout > 0; --tout) {
  174. # ifndef       OSK
  175.                if ((lock_fd = creat (fn, 0444)) == -1)
  176. # else         /* OSK */
  177.                unlink (fn);
  178.                if (((lock_fd = create (fn, S_IWRITE, 033)) == -1) && (errno == E_CEF))
  179. # endif                /* OSK */
  180.                        sleep (1);
  181.                else
  182.                        break;
  183.        }
  184.        if (lock_fd != -1)
  185.                stop_signals ();
  186.        return (lock_fd == -1 ? -1 : 0);
  187. }
  188.  
  189. static void
  190. unlock_index (fle)
  191. char *fle;
  192. {
  193.        char    *fn;
  194.  
  195.        if (lock_fd != -1) {
  196.                close (lock_fd);
  197.                lock_fd = -1;
  198.                if (fn = lock_filename (fle)) {
  199. # ifndef       OSK
  200.                        chmod (fn, 0666);
  201. # endif                /* OSK */
  202.                        unlink (fn);
  203.                }
  204.                start_signals ();
  205.        }
  206. }
  207. # endif                /* LOCK_INDEX */
  208.  
  209. /*
  210.  *  Construct the pointers to the basenotes of each thread
  211.  *  arts[] contains every article in the group.  inthread is
  212.  *  set on each article that is after the first article in the
  213.  *  thread.  Articles which have been expired have their thread
  214.  *  set to -2.
  215.  */
  216.  
  217. find_base() {
  218.        int i;
  219.  
  220.        top_base = 0;
  221.  
  222.        for (i = 0; i < top; i++)
  223.                if (!arts[i].inthread && arts[i].thread != -2) {
  224.                        if (top_base >= max_art)
  225.                                expand_art();
  226.                        base[top_base++] = i;
  227.                }
  228. }
  229.  
  230.  
  231. /* 
  232.  *  Count the number of non-expired articles in arts[]
  233.  */
  234.  
  235. num_arts() {
  236.        int sum = 0;
  237.        int i;
  238.  
  239.        for (i = 0; i < top; i++)
  240.                if (arts[i].thread != -2)
  241.                        sum++;
  242.  
  243.        return sum;
  244. }
  245.  
  246.  
  247. /*
  248.  *  Do we have an entry for article art?
  249.  */
  250.  
  251. valid_artnum(art)
  252. long art;
  253. {
  254.        int i;
  255.  
  256.        for (i = 0; i < top; i++)
  257.                if (arts[i].artnum == art)
  258.                        return i;
  259.  
  260.        return -1;
  261. }
  262.  
  263.  
  264. /*
  265.  *  Return TRUE if arts[] contains any expired articles
  266.  *  (articles we have an entry for which don't have a corresponding
  267.  *   article file in the spool directory)
  268.  */
  269.  
  270. purge_needed() {
  271.        int i;
  272.  
  273.        for (i = 0; i < top; i++)
  274.                if (arts[i].thread == -2)
  275.                        return TRUE;
  276.  
  277.        return FALSE;
  278. }
  279.  
  280.  
  281. /*
  282.  *  Main group indexing routine.  Group should be the name of the
  283.  *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
  284.  *  same but with the .'s turned into /'s: "comp/unix/amiga"
  285.  *
  286.  *  Will read any existing index, create or incrementally update
  287.  *  the index by looking at the articles in the spool directory,
  288.  *  and attempt to write a new index if necessary.
  289.  */
  290.  
  291. index_group(group, group_path)
  292. char *group;
  293. char *group_path;
  294. {
  295.        int modified;
  296.  
  297.        glob_art_group = group;
  298.  
  299. #ifdef SIGTSTP
  300.        signal(SIGTSTP, art_susp);
  301. #endif
  302.  
  303.        if (!update) {
  304.                clear_message();
  305.                MoveCursor(LINES, 0);
  306.                printf("Group %s...    ", group);
  307.                fflush(stdout);
  308.        }
  309.  
  310.        if (local_index)
  311.                find_local_index(group);
  312.        else
  313.                sprintf(index_file, "%s/%s/.tindex", SPOOLDIR, group_path);
  314.  
  315. #ifdef LOCK_INDEX
  316.        if (! local_index)
  317.                if (lock_index (index_file) < 0)
  318.                        return -1;
  319. #endif /* LOCK_INDEX */
  320.  
  321.        load_index();
  322.        modified = read_group(group_path);
  323.        make_threads();
  324.        if (modified || purge_needed()) {
  325. #ifdef USE_UID
  326.                if (local_index) {      /* writing index in home directory */
  327.                        setuid(real_uid);       /* so become them */
  328.                        setgid(real_gid);
  329.                }
  330. #endif /* USE_UID */
  331.                dump_index(group);
  332. #ifdef USE_UID
  333.                if (local_index) {
  334.                        setuid(tass_uid);
  335.                        setgid(tass_gid);
  336.                }
  337. #endif /* USE_UID */
  338.        }
  339.  
  340. #ifdef LOCK_INDEX
  341.        if (! local_index)
  342.                unlock_index (index_file);
  343. #endif /* LOCK_INDEX */
  344.  
  345.        find_base();
  346.  
  347.        if (modified && !update)
  348.                clear_message();
  349. }
  350.  
  351.  
  352. /*
  353.  *  Longword comparison routine for the qsort()
  354.  */
  355.  
  356. base_comp(a, b)
  357. long *a;
  358. long *b;
  359. {
  360.  
  361.        if (*a < *b)
  362.                return -1;
  363.        if (*a > *b)
  364.                return 1;
  365.        return 0;
  366. }
  367.  
  368.  
  369. /*
  370.  *  Read the article numbers existing in a group's spool directory
  371.  *  into base[] and sort them.  base_top is one past top.
  372.  */
  373.  
  374. scan_dir(group)
  375. char *group;
  376. {
  377.        DIR *d;
  378.        DIR_BUF *e;
  379.        long art;
  380.        char buf[200];
  381.  
  382.        top_base = 0;
  383.  
  384.        sprintf(buf, "%s/%s", SPOOLDIR, group);
  385.  
  386.        d = opendir(buf);
  387.        if (d != NULL) {
  388.                while ((e = readdir(d)) != NULL) {
  389. #ifdef         OSK
  390.                        e->D_LENGTH = strlen (e->d_name);
  391. #endif         /* OSK */
  392.                        art = my_atol(e->d_name, e->D_LENGTH);
  393.                        if (art >= 0) {
  394.                                if (top_base >= max_art)
  395.                                        expand_art();
  396.                                base[top_base++] = art;
  397.                        }
  398.                }
  399.                closedir(d);
  400.        }
  401.  
  402.        qsort(base, top_base, sizeof(long), base_comp);
  403. }
  404.  
  405.  
  406. /*
  407.  *  Index a group.  Assumes any existing index has already been
  408.  *  loaded.
  409.  */
  410.  
  411. read_group(group)
  412. char *group;
  413. {
  414.        char buf[200];
  415.        int fd;
  416.        long art;
  417.        int count;
  418.        int modified = FALSE;
  419.        int respnum;
  420.        int i;
  421.  
  422.        scan_dir(group);        /* load article numbers into base[] */
  423.  
  424.        count = 0;
  425.  
  426.        for (i = 0; i < top_base; i++) {        /* for each article # */
  427.                art = base[i];
  428.  
  429. /*
  430.  *  Do we already have this article in our index?  Change thread from
  431.  *  -2 to -1 if so and skip the header eating.
  432.  */
  433.  
  434.                if ((respnum = valid_artnum(art)) >= 0) {
  435.                        arts[respnum].thread = -1;
  436.                        arts[respnum].unread = 1;
  437.                        continue;
  438.                }
  439.  
  440.                if (!modified) {
  441.                        modified = TRUE;   /* we've modified the index */
  442.                                           /* it will need to be re-written */
  443. #if 0
  444.                        if (!update) {
  445.                                MoveCursor(LINES, 0);
  446.                                fputs("Indexing...    ", stdout);
  447.                                fflush(stdout);
  448.                        }
  449. #endif
  450.                }
  451.  
  452. /*
  453.  *  Add article to arts[]
  454.  */
  455.                if (top >= max_art)
  456.                        expand_art();
  457.  
  458.                arts[top].artnum = art;
  459.                arts[top].thread = -1;
  460.                arts[top].inthread = FALSE;
  461.                arts[top].unread = 1;
  462.  
  463.                sprintf(buf, "%s/%s/%ld", SPOOLDIR, group, art);
  464. #ifndef        MNEWS
  465.                fd = open(buf, 0);
  466. #else  /* MNEWS */
  467. #ifndef        OSK
  468.                if ((fd = open (buf, O_RDONLY)) != -1) {
  469. #else  /* OSK */
  470.                if ((fd = open (buf, S_IREAD)) != -1) {
  471. #endif /* OSK */
  472.                        int     n;
  473.                        n = readln (fd, buf, 180);
  474.                        lseek (fd, 0, 0);
  475.                        if (n > 0) {
  476.                                buf[n - 1] = '\0';
  477.                                if (!strncmp (buf, "%(#)$ ", 6)) {
  478.                                        char    sav[200];
  479.                                        strcpy (sav, buf + 6);
  480.                                        sprintf (buf, "%s/%s", spooldir, sav);
  481.                                        close (fd);
  482. #ifndef        OSK
  483.                                        fd = open (buf, O_RDONLY);
  484. #else  /* OSK */
  485.                                        fd = open (buf, S_IREAD);
  486. #endif /* OSK */
  487.                                }
  488.                        }
  489.                }
  490. #endif /* MNEWS */
  491.  
  492.                if (fd < 0) {
  493.                        fprintf(stderr, "can't open article %s: ", buf);
  494.                        perror("");
  495.                        continue;
  496.                }
  497.  
  498.                if (!parse_headers(fd, &arts[top]))
  499.                        continue;
  500.                top++;
  501.                close(fd);
  502.  
  503.                if (++count % 10 == 0 && !update) {
  504.                        printf("\b\b\b\b%4d", count);
  505.                        fflush(stdout);
  506.                }
  507.        }
  508.  
  509.        complete_count += count;
  510.  
  511.        return modified;
  512. }
  513.  
  514.  
  515. /*
  516.  *  Go through the articles in arts[] and use .thread to snake threads
  517.  *  through them.  Use the subject line to construct threads.  The
  518.  *  first article in a thread should have .inthread set to FALSE, the
  519.  *  rest TRUE.  Only do unexprired articles we haven't visited yet
  520.  *  (arts[].thread == -1).
  521.  */
  522.  
  523. make_threads() {
  524.        int i;
  525.        int j;
  526.  
  527.        for (i = 0; i < top; i++) {
  528.                if (arts[i].thread == -1)
  529.                    for (j = i+1; j < top; j++)
  530.                        if (arts[i].hash == arts[j].hash
  531.                        &&  arts[j].thread != -2
  532.                        &&  strncmp(arts[i].nore, arts[j].nore, 10) == 0) {
  533.                                arts[i].thread = j;
  534.                                arts[j].inthread = TRUE;
  535.                                break;
  536.                        }
  537.        }
  538. }
  539.  
  540.  
  541. /*
  542.  *  Return a pointer into s eliminating any leading Re:'s.  Example:
  543.  *
  544.  *       Re: Reorganization of misc.jobs
  545.  *       ^   ^
  546.  */
  547.  
  548. char *
  549. eat_re(s)
  550. char *s;
  551. {
  552.  
  553. #if 1
  554.        while (*s == 'r' || *s == 'R') {
  555.                if (*(s+1) == 'e' || *(s+1) == 'E') {
  556.                        if (*(s+2) == ':')
  557.                                s += 3;
  558.                        else if ((*(s+2) == '^') && isdigit (*(s+3))) {
  559.                                s += 4;
  560.                                if (*s == ':')
  561.                                        ++s;
  562.                        } else
  563.                                break;
  564.                } else
  565.                        break;
  566.                while (isspace (*s))
  567.                        ++s;
  568.        }
  569. #else
  570.        while (*s == 'R') {
  571.                if (strncmp(s, "Re: ", 4) == 0)
  572.                        s += 4;
  573.                else if (strncmp(s, "Re:", 3) == 0)
  574.                        s += 3;
  575.                else if (strncmp(s, "Re^2: ", 6) == 0)
  576.                        s += 6;
  577.                else
  578.                        break;
  579.        }
  580. #endif
  581.  
  582.        return s;
  583. }
  584.  
  585.  
  586. /*
  587.  *  Hash the subjects (after eating the Re's off) for a quicker
  588.  *  thread search later.  We store the hashes for subjects in the
  589.  *  index file for speed.
  590.  */
  591.  
  592. long
  593. hash_s(s)
  594. char *s;
  595. {
  596.        long h = 0;
  597.  
  598.        while (*s)
  599.                h = h + (*s++ & 0xff);
  600.  
  601.        return h;
  602. }
  603.  
  604.  
  605. parse_headers(fd, h)
  606. int fd;
  607. struct header *h;
  608. {
  609.        char buf[1024];
  610.        char *p, *q;
  611.        char flag;
  612.        char *ptr;
  613.        int found_from, found_subj;
  614.        int n;
  615.  
  616.        if ((n = read(fd, buf, 1024)) <= 0)
  617.                return FALSE;
  618.  
  619.        buf[n > 1023 ? 1023 : n] = '\0';
  620.  
  621.        p = buf;
  622.        while (p = index (p, '\n'))
  623.                if (*(p + 1) == '\n') {
  624.                        *p = '\0';
  625.                        break;
  626.                } else
  627.                        ++p;
  628.  
  629.        p = buf;
  630.  
  631.        h->from[0] = '\0';
  632.        h->subject[0] = '\0';
  633.        h->nore = h->subject;
  634.        h->hash = 0;
  635.  
  636.        found_from = FALSE;
  637.        found_subj = FALSE;
  638.        while (1) {
  639.                q = p;
  640.                while (*p && *p != '\n') {
  641.                        if ((unsigned char) *p & 0x7F < 32)
  642.                                *p = ' ';
  643.                        p++;
  644.                }
  645.                flag = *p;
  646.                *p++ = '\0';
  647.  
  648.                if ((!found_from) && (strncmp(q, "From: ", 6) == 0)) {
  649.                        if (ptr = index (&q[6], '('))
  650.                                ++ptr;
  651.                        else
  652.                                ptr = &q[6];
  653.                        strncpy (h->from, ptr, MAX_FROM);
  654.                        h->from[MAX_FROM-1] = '\0';
  655.                        if (ptr = index (h->from, ')'))
  656.                                *ptr = '\0';
  657.                } else if ((!found_subj) && (strncmp(q, "Subject: ", 9) == 0)) {
  658.                        h->hash = hash_s(eat_re(&q[9]));
  659.                        strncpy(h->subject, &q[9], MAX_SUBJ);
  660.                        h->subject[MAX_SUBJ-1] = '\0';
  661.                        h->nore = eat_re(h->subject);
  662.                }
  663.  
  664.                if ((!flag) || (found_from && found_subj))
  665.                        break;
  666.        }
  667.  
  668.        return TRUE;
  669. }
  670.  
  671.  
  672. /* 
  673.  *  Write out a .tindex file.  Write the group name first so if
  674.  *  local indexing is done we can disambiguate between group name
  675.  *  hash collisions by looking at the index file.
  676.  */
  677. void
  678. dump_index(group)
  679. char *group;
  680. {
  681.        int i;
  682.        char buf[200];
  683.        FILE *fp;
  684.        char *p, *q;
  685.        long l;
  686. #ifndef        USE_UID
  687.        char lockfn[64];
  688.        int lockfd;
  689.  
  690.        if (! local_index) {
  691. #ifndef        OSK
  692.                sprintf (lockfn, "/tmp/%-.10s", group);
  693.                lockfd = creat (lockfn, 0, 0444);
  694. #else  /* OSK */
  695.                sprintf (lockfn, "/dd/TMP/%-.20s", group);
  696.                for (p = lockfn; *p; ++p)
  697.                        if (((unsigned char) *p >= 0x80) || ((!isalnum (*p)) && (*p != '/')))
  698.                                *p = '_';
  699.                lockfd = create (lockfn, 0, 033);
  700. #endif /* OSK */
  701.                if (lockfd < 0) {
  702.                        sleep (1);
  703.                        return;
  704.                }
  705.        }
  706. #endif /* USE_UID */
  707.  
  708.        fp = fopen(index_file, "w");
  709.        if (fp == NULL)
  710. #ifndef        USE_UID
  711.                goto dump_index_finish;
  712. #else  /* USE_UID */
  713.                return;
  714. #endif /* USE_UID */
  715.  
  716.        fprintf(fp, "%s\n", group);
  717.        fprintf(fp, "%d\n", num_arts());
  718.        for (i = 0; i < top; i++)
  719.            if (arts[i].thread != -2) {
  720.                p = arts[i].nore;
  721.                q = arts[i].subject;
  722.                l = p - q;
  723.                fprintf(fp, "%ld\n%s\n%s\n%ld\n%ld\n",
  724.                                arts[i].artnum,
  725.                                arts[i].subject,
  726.                                arts[i].from,
  727.                                arts[i].hash,
  728. #if 0
  729.                                (long) arts[i].nore - (long) arts[i].subject);
  730. #else
  731.                                l);
  732. #endif
  733.        } else
  734.                ++complete_expire;
  735.  
  736.        fclose(fp);
  737. #ifdef USE_UID
  738. #ifndef        OSK
  739.        chmod(index_file, 0644);
  740. #else  /* OSK */
  741.        chmod(index_file, 013);
  742. #endif /* OSK */
  743. #else  /* USE_UID */
  744. dump_index_finish:
  745.        if (local_index)
  746. #ifndef        OSK
  747.                chmod(index_file, 0644);
  748. #else  /* OSK */
  749.                chmod(index_file, 013);
  750. #endif /* OSK */
  751.        else if (lockfd != -1) {
  752.                close (lockfd);
  753. #ifndef        OSK
  754.                chmod (lockfn, 0666);
  755. #endif /* OSK */
  756.                unlink (lockfn);
  757. #ifndef        OSK
  758.                chmod(index_file, 0666);
  759. #else  /* OSK */
  760.                chmod(index_file, 033);
  761. #endif /* OSK */
  762.        }
  763. #endif /* USE_UID */
  764. }
  765.  
  766.  
  767. /*
  768.  *  strncpy that stops at a newline and null terminates
  769.  */
  770.  
  771. my_strncpy(p, q, n)
  772. char *p;
  773. char *q;
  774. int n;
  775. {
  776.  
  777.        while (n--) {
  778.                if (!*q || *q == '\n')
  779.                        break;
  780.                *p++ = *q++;
  781.        }
  782.        *p = '\0';
  783. }
  784.  
  785.  
  786. /*
  787.  *  Read in a .tindex file.
  788.  */
  789. void
  790. load_index()
  791. {
  792.        int i;
  793.        long j;
  794.        char buf[200];
  795.        FILE *fp;
  796.        int first = TRUE;
  797.  
  798.        top = 0;
  799.  
  800.        fp = fopen(index_file, "r");
  801.        if (fp == NULL)
  802.                return;
  803.  
  804.        if (fgets(buf, 200, fp) == NULL
  805.        ||  fgets(buf, 200, fp) == NULL) {
  806.                fprintf(stderr, "one\n");
  807.                goto corrupt_index;
  808.        }
  809.  
  810.        i = atol(buf);
  811.        while (top < i) {
  812.                if (top >= max_art)
  813.                        expand_art();
  814.  
  815.                arts[top].thread = -2;
  816.                arts[top].inthread = FALSE;
  817.  
  818.                if (fgets(buf, 200, fp) == NULL) {
  819.                        fprintf(stderr, "two\n");
  820.                        goto corrupt_index;
  821.                }
  822.                arts[top].artnum = atol(buf);
  823.  
  824.                if (fgets(buf, 200, fp) == NULL) {
  825.                        fprintf(stderr, "three\n");
  826.                        goto corrupt_index;
  827.                }
  828.  
  829.                my_strncpy(arts[top].subject, buf, MAX_SUBJ-1);
  830.                        
  831.                if (fgets(buf, 200, fp) == NULL) {
  832.                        fprintf(stderr, "four\n");
  833.                        goto corrupt_index;
  834.                }
  835.                my_strncpy(arts[top].from, buf, MAX_FROM-1);
  836.  
  837.                if (fgets(buf, 200, fp) == NULL) {
  838.                        fprintf(stderr, "five\n");
  839.                        goto corrupt_index;
  840.                }
  841.                arts[top].hash = atol(buf);
  842.  
  843.                if (fgets(buf, 200, fp) == NULL) {
  844.                        fprintf(stderr, "six\n");
  845.                        goto corrupt_index;
  846.                }
  847.  
  848.                j = atol(buf);
  849. #if 0
  850.                if (j < 0 || j > 100) {
  851. #if 0
  852.                        goto corrupt_index;
  853. #else
  854.                        arts[top].nore = eat_re(arts[top].subject);
  855. #endif
  856.                } else
  857.                        arts[top].nore = arts[top].subject + j;
  858. #else
  859.                arts[top].nore = eat_re(arts[top].subject);
  860. #endif
  861.  
  862.                top++;
  863.        }
  864.  
  865.        fclose(fp);
  866.        return;
  867.  
  868. corrupt_index:
  869.        fprintf(stderr, "index file %s corrupt\n", index_file);
  870.        fprintf(stderr, "top = %d\n", top);
  871.        exit(1);
  872.        unlink(index_file);
  873.        top = 0;
  874. }
  875.  
  876.  
  877. /*
  878.  *  Look in the local $HOME/.tindex (or wherever) directory for the
  879.  *  index file for the given group.  Hashing the group name gets
  880.  *  a number.  See if that #.1 file exists; if so, read first line.
  881.  *  Group we want?  If no, try #.2.  Repeat until no such file or
  882.  *  we find an existing file that matches our group.
  883.  */
  884. void
  885. find_local_index(group)
  886. char *group;
  887. {
  888.        unsigned long h;
  889.        static char buf[200];
  890.        int i;
  891.        char *p;
  892.        FILE *fp;
  893.  
  894.        {
  895.                char *t = group;
  896.  
  897.                h = *t++;
  898.                while (*t)
  899.                        h = (h << 1) + (*t++ & 0xff);
  900.        }
  901.  
  902.        i = 1;
  903.        while (1) {
  904.                sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
  905.                fp = fopen(index_file, "r");
  906.                if (fp == NULL)
  907.                        return;
  908.  
  909.                if (fgets(buf, 200, fp) == NULL) {
  910.                        fclose(fp);
  911.                        return;
  912.                }
  913.                fclose(fp);
  914.  
  915.                for (p = buf; *p && *p != '\n'; p++) ;
  916.                *p = '\0';
  917.  
  918.                if (strcmp(buf, group) == 0)
  919.                        return;
  920.  
  921.                i++;
  922.        }
  923. }
  924.  
  925.  
  926. /*
  927.  *  Run the index file updater only for the groups we've loaded.
  928.  */
  929.  
  930. do_update() {
  931.        int i;
  932.        char group_path[200];
  933.        char *p;
  934. #ifdef OSK
  935.        int len = 0;
  936. #endif /* OSK */
  937.        time_t tim;
  938.        struct tm *tt;
  939.  
  940.        complete_count = 0;
  941.        complete_expire = 0;
  942.  
  943.        for (i = 0; i < local_top; i++) {
  944.                strcpy(group_path, active[my_group[i]].name);
  945.                for (p = group_path; *p; p++)
  946. #ifndef        OSK
  947.                        if (*p == '.')
  948.                                *p = '/';
  949. #else  /* OSK */
  950.                {
  951.                        if (*p == '.') {
  952.                                *p = '/';
  953.                                len = 0;
  954.                        } else if ((! isalnum (*p)) && (! index ("$_", *p)))
  955.                                *p = '_';
  956.                        if (++len > 26)
  957.                                while (*(p + 1) && (*(p + 1) != '.'))
  958.                                        ++p;
  959.                }
  960. #endif /* OSK */
  961.  
  962.                index_group(active[my_group[i]].name, group_path);
  963.        }
  964.  
  965.        time (&tim);
  966.        tt = localtime (&tim);
  967.        printf ("[%02d.%02d.%04d %2d:%2d] Collected %d articles",
  968.                tt -> tm_mday,
  969.                tt -> tm_mon + 1,
  970.                tt -> tm_year + 1900,
  971.                tt -> tm_hour,
  972.                tt -> tm_min,
  973.                complete_count);
  974.  
  975.        if (complete_expire)
  976.                printf (" (%d articles expired)", complete_expire);
  977.        putchar ('\n');
  978. }
  979.