home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume30 / tin / part08 / art.c next >
C/C++ Source or Header  |  1992-05-20  |  23KB  |  1,088 lines

  1. /*
  2.  *  Project   : tin - a threaded Netnews reader
  3.  *  Module    : art.c
  4.  *  Author    : I.Lea & R.Skrenta
  5.  *  Created   : 01-04-91
  6.  *  Updated   : 12-05-92
  7.  *  Notes     :
  8.  *  Copyright : (c) Copyright 1991-92 by Iain Lea & Rich Skrenta
  9.  *              You may  freely  copy or  redistribute  this software,
  10.  *              so  long as there is no profit made from its use, sale
  11.  *              trade or  reproduction.  You may not change this copy-
  12.  *              right notice, and it must be included in any copy made
  13.  */
  14.  
  15. #include    "tin.h"
  16.  
  17. char index_file[PATH_LEN];
  18. char *glob_art_group;
  19. static long last_read_article;
  20.  
  21.  
  22. /*
  23.  *  Construct the pointers to the basenotes of each thread
  24.  *  arts[] contains every article in the group.  inthread is
  25.  *  set on each article that is after the first article in the
  26.  *  thread.  Articles which have been expired have their thread
  27.  *  set to -2 (ART_EXPIRED).
  28.  */
  29.  
  30. void find_base (only_unread)
  31.     int only_unread;
  32. {
  33.     register int i;
  34.     register int j;
  35.  
  36.     top_base = 0;
  37.  
  38.     debug_print_arts ();
  39.  
  40.     if (only_unread) {
  41.         for (i = 0; i < top; i++) {
  42.             if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
  43.                 continue;
  44.             }    
  45.             if (top_base >= max_art) {
  46.                 expand_art ();
  47.             }
  48.             if (arts[i].unread == ART_UNREAD) {
  49.                 base[top_base++] = i;
  50.             } else {
  51.                 for (j = i ; j >= 0 ; j = arts[j].thread) {
  52.                     if (arts[j].unread) {
  53.                         base[top_base++] = i;
  54.                         break;
  55.                     }
  56.                 }
  57.             }
  58.         }
  59.     } else {
  60.         for (i = 0; i < top; i++) {
  61.             if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
  62.                 continue;
  63.             }    
  64.             if (top_base >= max_art) {
  65.                 expand_art ();
  66.             }
  67.             base[top_base++] = i;
  68.         }
  69.     }
  70. }
  71.  
  72. /* 
  73.  *  Count the number of non-expired articles in arts[]
  74.  */
  75.  
  76. int num_of_arts ()
  77. {
  78.     int sum = 0;
  79.     register int i;
  80.  
  81.     for (i = 0; i < top; i++) {
  82.         if (arts[i].thread != ART_EXPIRED) {
  83.             sum++;
  84.         }
  85.     }
  86.  
  87.     return sum;
  88. }
  89.  
  90. /*
  91.  *  Do we have an entry for article art?
  92.  */
  93.  
  94. int valid_artnum (art)
  95.     long art;
  96. {
  97.     register int i;
  98.  
  99.     for (i = 0; i < top; i++)
  100.         if (arts[i].artnum == art)
  101.             return i;
  102.  
  103.     return -1;
  104. }
  105.  
  106. /*
  107.  *  Return TRUE if arts[] contains any expired articles
  108.  *  (articles we have an entry for which don't have a corresponding
  109.  *   article file in the spool directory)
  110.  */
  111.  
  112. int purge_needed ()
  113. {
  114.     register int i;
  115.  
  116.     for (i = 0; i < top; i++)
  117.         if (arts[i].thread == ART_EXPIRED)
  118.             return TRUE;
  119.  
  120.     return FALSE;
  121. }
  122.  
  123. /*
  124.  *  Main group indexing routine.  Group should be the name of the
  125.  *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
  126.  *  same but with the .'s turned into /'s: "comp/unix/amiga"
  127.  *
  128.  *  Will read any existing index, create or incrementally update
  129.  *  the index by looking at the articles in the spool directory,
  130.  *  and attempt to write a new index if necessary.
  131.  */
  132.  
  133. void index_group (group, group_path)
  134.     char *group;
  135.     char *group_path;
  136. {
  137.     int killed = FALSE;
  138.     int modified = FALSE;
  139.     glob_art_group = group;
  140.  
  141.     set_signals_art ();
  142.     
  143.     if (! update) {
  144.         sprintf (msg, txt_group, group);
  145.         wait_message (msg);
  146.     }
  147.     hash_reclaim ();
  148.     free_art_array ();
  149.  
  150.     /*
  151.      *  load articles from index file if it exists
  152.      */
  153.     read_index_file (group);
  154.  
  155.     /*
  156.      *  add any articles to arts[] that are new or were killed
  157.      */
  158.     modified = read_group (group, group_path);
  159.  
  160.     if (modified || purge_needed ()) {
  161.         write_index_file (group);
  162.     }
  163.     read_newsrc_line (group);
  164.     killed = kill_any_articles (group); /* do after read_newsrc_line() */
  165.     make_threads (FALSE);
  166.     find_base (show_only_unread);
  167.     
  168.     if ((modified || killed) && ! update) {
  169.         clear_message ();
  170.     }
  171. }
  172.  
  173. /*
  174.  *  Index a group.  Assumes any existing index has already been
  175.  *  loaded.
  176.  */
  177.  
  178. int read_group (group, group_path)
  179.     char *group;
  180.     char *group_path;
  181. {
  182.     FILE *fp;
  183.     int count = 0;
  184.     int modified = FALSE;
  185.     int respnum;
  186.     long art;
  187.     register int i;
  188.  
  189.     setup_base (group, group_path);    /* load article numbers into base[] */
  190.  
  191.     for (i = 0; i < top_base; i++) {    /* for each article # */
  192.         art = base[i];
  193.  
  194. /*
  195.  *  Do we already have this article in our index?  Change thread from
  196.  *  (ART_EXPIRED) to (ART_NORMAL) if so and skip the header eating.
  197.  */
  198.  
  199.         if ((respnum = valid_artnum (art)) >= 0 || art <= last_read_article) {
  200.             if (respnum >= 0) {
  201.                 arts[respnum].thread = ART_NORMAL;
  202.                 arts[respnum].unread = ART_UNREAD;
  203.             }    
  204.             continue;
  205.         }
  206.  
  207.         if (! modified) {
  208.             modified = TRUE;   /* we've modified the index */
  209.                                /* it will need to be re-written */
  210.         }
  211.  
  212.         if ((fp = open_header_fp (group_path, art)) == (FILE *) 0) {
  213.             continue;
  214.         }
  215.         
  216.         /*
  217.          *  Add article to arts[]
  218.          */
  219.         if (top >= max_art)
  220.             expand_art();
  221.  
  222.         arts[top].artnum = art;
  223.         arts[top].thread = ART_NORMAL;
  224.  
  225.         set_article (&arts[top]);
  226.  
  227.         if (! parse_headers (fp, &arts[top])) {
  228.             debug_nntp ("read_group", "FAILED parse_header()");
  229.             continue;
  230.         }
  231.  
  232.         fclose (fp);
  233.         last_read_article = arts[top].artnum;    /* used if arts are killed */
  234.         top++;
  235.  
  236.         if (++count % MODULO_COUNT_NUM == 0 && ! update) {
  237. #ifndef SLOW_SCREEN_UPDATE
  238.             sprintf (msg, txt_indexing_num, group, count);
  239. #else
  240.             sprintf (msg, txt_indexing, group);
  241. #endif
  242.             wait_message (msg);
  243.         }
  244.     }
  245.  
  246.     return modified;
  247. }
  248.  
  249.  
  250. /*
  251.  *  Go through the articles in arts[] and use .thread to snake threads
  252.  *  through them.  Use the subject line to construct threads.  The
  253.  *  first article in a thread should have .inthread set to FALSE, the
  254.  *  rest TRUE.  Only do unexprired articles we haven't visited yet
  255.  *  (arts[].thread == -1 ART_NORMAL).
  256.  */
  257.  
  258. void make_threads (rethread)
  259.     int rethread;
  260. {
  261.     extern int cur_groupnum;
  262.     register int i;
  263.     register int j;
  264.  
  265.     if (!cmd_line) {
  266.         if (thread_arts) {
  267.             wait_message (txt_threading_arts);
  268.         } else {
  269.             wait_message (txt_unthreading_arts);
  270.         }
  271.     }
  272.  
  273.     /*
  274.      *  .thread & .inthread need to be reset if re-threading arts[]
  275.      */
  276.     if (rethread && active[my_group[cur_groupnum]].attribute.thread) {
  277.         for (i=0 ; i < top ; i++) {
  278.             arts[i].thread = ART_NORMAL;
  279.             arts[i].inthread = FALSE;
  280.         }
  281.     }
  282.  
  283.     switch (sort_art_type) {
  284.         case SORT_BY_NOTHING:        /* don't sort at all */
  285.             qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
  286.             break;
  287.         case SORT_BY_SUBJ_DESCEND:
  288.         case SORT_BY_SUBJ_ASCEND:
  289.             qsort ((char *) arts, top, sizeof (struct article_t), subj_comp);
  290.             break;
  291.         case SORT_BY_FROM_DESCEND:
  292.         case SORT_BY_FROM_ASCEND:
  293.             qsort ((char *) arts, top, sizeof (struct article_t), from_comp);
  294.             break;
  295.         case SORT_BY_DATE_DESCEND:
  296.         case SORT_BY_DATE_ASCEND:
  297.             qsort ((char *) arts, top, sizeof (struct article_t), date_comp);
  298.             break;
  299.         default:
  300.             break;
  301.     }
  302.  
  303.     if (thread_arts == 0 || active[my_group[cur_groupnum]].attribute.thread == 0) {
  304.         return;
  305.     }
  306.  
  307.     for (i = 0; i < top; i++) {
  308.         if (arts[i].thread != ART_NORMAL || IGNORE_ART(i)) {
  309.             continue;
  310.         }    
  311.         for (j = i+1; j < top; j++) {
  312.             if (! IGNORE_ART(j) && 
  313.                ((arts[i].subject == arts[j].subject) ||
  314.                ((arts[i].part || arts[i].patch) &&
  315.                arts[i].archive == arts[j].archive))) {
  316.                 arts[i].thread = j;
  317.                 arts[j].inthread = TRUE;
  318.                 break;
  319.             }
  320.         }
  321.     }
  322. }
  323.  
  324.  
  325. int parse_headers (fp, h)
  326.     FILE *fp;
  327.     struct article_t *h;
  328. {
  329.     char buf[HEADER_LEN];
  330.     char buf2[HEADER_LEN];
  331.     char art_from_addr[LEN];
  332.     char art_full_name[LEN];
  333.     char *ptr, *ptrline, *s;
  334.     int n = 0, len = 0, lineno = 0;
  335.     int flag;
  336.     int got_subject = FALSE;
  337.     int got_from = FALSE;
  338.     int got_date = FALSE;
  339.     int got_archive = FALSE;
  340.     extern int errno;
  341.     
  342.     buf[HEADER_LEN-1] = '\0';
  343.  
  344.     while (fread (buf, sizeof (buf)-1, 1, fp) != 1 && errno == EINTR)
  345.         ;        /* spin on signal interrupts */
  346.  
  347.     if ((n = strlen (buf)) == 0) {
  348.         return FALSE;
  349.     }
  350.  
  351.     buf[n-1] = '\0';
  352.  
  353.     ptr = buf;
  354.  
  355.     while (1) {
  356.         for (ptrline = ptr; *ptr && *ptr != '\n'; ptr++) {
  357.             if (((*ptr) & 0xFF) < ' ') {
  358.                 *ptr = ' ';
  359.             }
  360.         }
  361.         flag = *ptr;
  362.         *ptr++ = '\0';
  363.         lineno++;
  364.  
  365.         if (! got_from && match_header (ptrline, "From", buf2, HEADER_LEN)) {
  366.             parse_from (buf2, art_from_addr, art_full_name); 
  367.             h->from = hash_str (art_from_addr);
  368.             h->name = hash_str (art_full_name);
  369.             got_from = TRUE;
  370.         } else if (! got_subject && match_header (ptrline, "Subject", buf2, HEADER_LEN)) {
  371.             s = eat_re (buf2);
  372.             h->subject = hash_str (eat_re (s));
  373.             got_subject = TRUE;
  374.         } else if (! got_date && match_header (ptrline, "Date", buf2, HEADER_LEN)) {
  375.             parse_date (buf2, h->date);
  376.             got_date = TRUE;
  377.         } else if (match_header (ptrline, "Archive-name", buf2, HEADER_LEN) ||
  378.                     match_header (ptrline, "Archive-Name", buf2, HEADER_LEN)) {
  379.             if ((s = (char *) strchr (buf2, '/')) != NULL) {
  380.                 if (strncmp (s+1, "part", 4) == 0 ||
  381.                     strncmp (s+1, "Part", 4) == 0) {
  382.                     h->part = str_dup (s+5);
  383.                     len = (int) strlen (h->part);
  384.                     if (h->part[len-1] == '\n') {
  385.                         h->part[len-1] = '\0';
  386.                     }
  387.                 } else {
  388.                     if (strncmp (s+1,"patch",5) == 0 ||
  389.                         strncmp (s+1,"Patch",5) == 0) {
  390.                         h->patch = str_dup (s+6);
  391.                         len = (int) strlen (h->patch);
  392.                         if (h->patch[len-1] == '\n') {
  393.                             h->patch[len-1] = '\0';
  394.                         }
  395.                     }
  396.                 }
  397.                 if (h->part || h->patch) {
  398.                     s = buf2;
  399.                     while (*s && *s != '/')
  400.                         s++;
  401.                     *s = '\0';    
  402.                     s = buf2;
  403.                     h->archive = hash_str (s);
  404.                     got_archive = TRUE;
  405.                 }
  406.             }
  407.         }
  408.  
  409.         if (! flag || lineno > 25 || got_archive) {
  410.             if (got_subject && got_from && got_date) {
  411.                 debug_print_header (h);
  412.                 return TRUE;
  413.             } else {
  414.                 return FALSE;
  415.             }    
  416.         }
  417.     }
  418.     /* NOTREACHED */
  419. }
  420.  
  421. /* 
  422.  *  Write out  an index file.  Write the group name first so if
  423.  *  local indexing is done so we can disambiguate between group
  424.  *  name hash collisions by looking at the index file.
  425.  */
  426.  
  427. void write_index_file (group)
  428.     char *group;
  429. {
  430.     char nam[LEN];
  431.     FILE *fp;
  432.     int *iptr;
  433.     int realnum;
  434.     register int i;
  435.  
  436.     set_tin_uid_gid();
  437.  
  438.         sprintf (nam, "%s.%d", index_file, process_id);
  439.     if ((fp = fopen (nam, "w")) == NULL) {
  440.         perror_message (txt_cannot_open, nam);
  441.         return;
  442.     }
  443.  
  444.     /*
  445.      *  dump group header info.
  446.      */
  447.     if (sort_art_type != SORT_BY_NOTHING) {
  448.         qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
  449.     }
  450.     fprintf (fp, "%s\n", group);
  451.     fprintf (fp, "%d\n", num_of_arts ());
  452.     if (top <= 0) {
  453.         fprintf (fp, "0\n");
  454.     } else {
  455.         if (last_read_article > arts[top-1].artnum) {
  456.             fprintf (fp, "%ld\n", last_read_article);
  457.         } else {
  458.             fprintf (fp, "%ld\n", arts[top-1].artnum);
  459.         }
  460.     }
  461.  
  462.     /*
  463.      *  dump articles
  464.      */
  465.     realnum = 0; 
  466.     for (i = 0; i < top; i++) {
  467.         if (arts[i].thread == ART_EXPIRED) { 
  468.             continue;
  469.         }
  470. #ifdef DEBUG            
  471.         debug_print_header (&arts[i]);
  472. #endif
  473.         fprintf(fp, "%ld\n", arts[i].artnum);
  474.  
  475.         iptr = (int *) arts[i].subject;
  476.         iptr--;
  477.  
  478.         if (! arts[i].subject) {
  479.             fprintf(fp, " \n");
  480.         } else if (*iptr < 0 || *iptr > top) {
  481.             fprintf(fp, " %s\n", arts[i].subject);
  482.             *iptr = realnum;
  483.         } else if (*iptr == i) {
  484.             fprintf(fp, " %s\n", arts[i].subject);
  485.         } else {
  486.             fprintf(fp, "%%%d\n", *iptr);
  487.         }
  488.     
  489.         iptr = (int *) arts[i].from;
  490.         iptr--;
  491.  
  492.         if (! arts[i].from) {
  493.             fprintf (fp, " \n");
  494.         } else if (*iptr < 0 || *iptr > top) {
  495.             fprintf (fp, " %s\n", arts[i].from);
  496.             *iptr = realnum;
  497.         } else if (*iptr == i) {
  498.             fprintf(fp, " %s\n", arts[i].from);
  499.         } else {
  500.             fprintf(fp, "%%%d\n", *iptr);
  501.         }
  502.  
  503.         iptr = (int *) arts[i].name;
  504.         iptr--;
  505.  
  506.         if (! arts[i].name) {
  507.             fprintf (fp, " \n");
  508.         } else if (*iptr < 0 || *iptr > top) {
  509.             fprintf (fp, " %s\n", arts[i].name);
  510.             *iptr = realnum;
  511.         } else if (*iptr == i) {
  512.             fprintf(fp, " %s\n", arts[i].name);
  513.         } else {
  514.             fprintf(fp, "%%%d\n", *iptr);
  515.         }
  516.  
  517.         fprintf (fp, "%s\n", arts[i].date);
  518.             
  519.         iptr = (int *) arts[i].archive;
  520.         iptr--;
  521.  
  522.         if (! arts[i].archive) {
  523.             fprintf (fp, "\n");
  524.         } else if (*iptr < 0 || *iptr > top) {
  525.             fprintf (fp, " %s\n", arts[i].archive);
  526.             *iptr = realnum;
  527.         } else if (arts[i].part || arts[i].patch) {
  528.             if (*iptr == i) {
  529.                 fprintf(fp, " %s\n", arts[i].archive);
  530.             } else {
  531.                 fprintf (fp, "%%%d\n", *iptr);
  532.             }
  533.         } else {
  534.             fprintf (fp, "\n");
  535.         }
  536.             
  537.         if (! arts[i].part) {
  538.             fprintf (fp, " \n");
  539.         } else {
  540.             fprintf (fp, "%s\n", arts[i].part);
  541.         }
  542.  
  543.         if (! arts[i].patch) {
  544.             fprintf (fp, " \n");
  545.         } else {
  546.             fprintf (fp, "%s\n", arts[i].patch);
  547.         }
  548.  
  549.         realnum++;
  550.     }
  551.     fclose (fp);
  552.     rename_file (nam, index_file);
  553.     chmod (index_file, 0644);
  554.     set_real_uid_gid();
  555.     if (debug == 2) {
  556.         sprintf (msg, "cp %s INDEX", index_file);
  557.         system (msg);
  558.     }
  559. }
  560.  
  561. /*
  562.  *  Read in an index file.
  563.  *
  564.  *  index file header 
  565.  *    1.  newsgroup name (ie. alt.sources)
  566.  *    2.  number of articles (ie. 26)
  567.  *    3.  number of last read article (ie. 210)
  568.  *    4.  Is this a complete/killed index file (ie. COMPLETE/KILLED)
  569.  *
  570.  *  index file record
  571.  *    1.  article number    (ie. 183)               [mandatory]
  572.  *    2.  Subject: line     (ie. Which newsreader?) [mandatory]
  573.  *    3.  From: line (addr) (ie. iain@norisc)       [mandatory]
  574.  *    4.  From: line (name) (ie. Iain Lea)          [mandatory]
  575.  *    5.  Date: of posting  (ie. 911231125959)      [mandatory]
  576.  *    6.  Archive: name     (ie. compiler)          [optional]
  577.  *    7.  Part number of Archive: name  (ie. 01)    [optional]
  578.  *    8.  Patch number of Archive: name (ie. 01)    [optional]
  579.  */
  580.  
  581. int read_index_file (group_name)
  582.     char *group_name;
  583. {
  584.     int error = 0;
  585.     int i, n;
  586.     char buf[LEN], *p;
  587.     FILE *fp = NULL;
  588.  
  589.     top = 0;
  590.     last_read_article = 0L;
  591.  
  592.     if ((fp = open_index_fp (group_name)) == NULL) {
  593.         return FALSE;
  594.     }
  595.  
  596.     /*
  597.      *  load header - discard group name, num. of arts in index file after any arts were killed
  598.      */
  599.     if (fgets(buf, sizeof buf, fp) == NULL ||
  600.         fgets(buf, sizeof buf, fp) == NULL) {
  601.         error = 0;            
  602.         goto corrupt_index;    
  603.     }
  604.     i = atoi (buf);
  605.  
  606.     /*
  607.      * num. of last_read_article including any that were killed
  608.      */
  609.     if (fgets(buf, sizeof buf, fp) == NULL) {
  610.         error = 1;                
  611.         goto corrupt_index;    
  612.     }                            
  613.     last_read_article = (long) atol (buf);
  614.     
  615.     /*
  616.      *  load articles
  617.      */
  618.     for (; top < i ; top++) {
  619.         if (top >= max_art) {
  620.             expand_art ();
  621.         }
  622.  
  623.         arts[top].thread = ART_EXPIRED;
  624.         set_article (&arts[top]);
  625.  
  626.         /*
  627.          * Article no.
  628.          */
  629.         if (fgets(buf, sizeof buf, fp) == NULL) {
  630.             error = 2;
  631.             goto corrupt_index;
  632.         }
  633.         arts[top].artnum = (long) atol (buf);
  634.  
  635.         /*
  636.          * Subject:
  637.          */
  638.         if (fgets(buf, sizeof buf, fp) == NULL) {
  639.             error = 3;
  640.             goto corrupt_index;
  641.         }
  642.  
  643.         if (buf[0] == '%') {
  644.             n = atoi (&buf[1]);
  645.             if (n >= top || n < 0) {
  646.                 error = 4;
  647.                 goto corrupt_index;
  648.             }
  649.             arts[top].subject = arts[n].subject;
  650.         } else if (buf[0] == ' ') {
  651.             for (p = &buf[1];  *p && *p != '\n'; p++)
  652.                 continue;    
  653.             *p = '\0';
  654.             arts[top].subject = hash_str (&buf[1]);
  655.         } else {
  656.             error = 5;
  657.             goto corrupt_index;
  658.         }
  659.             
  660.         /*
  661.          * From: (addr part)
  662.          */
  663.         if (fgets(buf, sizeof buf, fp) == NULL) {
  664.             error = 6;
  665.             goto corrupt_index;
  666.         }
  667.  
  668.         if (buf[0] == '%') {
  669.             n = atoi (&buf[1]);
  670.             if (n >= top || n < 0) {
  671.                 error = 7;
  672.                 goto corrupt_index;
  673.             }
  674.             arts[top].from = arts[n].from;
  675.         } else if (buf[0] == ' ') {
  676.             for (p = &buf[1];  *p && *p != '\n'; p++)
  677.                 continue;
  678.             *p = '\0';
  679.             arts[top].from = hash_str (&buf[1]);
  680.         } else {
  681.             error = 8;
  682.             goto corrupt_index;
  683.         }
  684.  
  685.         /*
  686.          * From: (full name)
  687.          */
  688.         if (fgets(buf, sizeof buf, fp) == NULL) {
  689.             error = 9;
  690.             goto corrupt_index;
  691.         }
  692.  
  693.         if (buf[0] == '%') {
  694.             n = atoi (&buf[1]);
  695.             if (n > top || n < 0) {
  696.                 error = 10;
  697.                 goto corrupt_index;
  698.             }
  699.             if (n == top) {        /* no full name so .name = .from */
  700.                 arts[top].name = arts[top].from;
  701.             } else {
  702.                 arts[top].name = arts[n].name;
  703.             }
  704.         } else if (buf[0] == ' ') {
  705.             for (p = &buf[1];  *p && *p != '\n'; p++)
  706.                 continue;
  707.             *p = '\0';
  708.             arts[top].name = hash_str (&buf[1]);
  709.         } else {
  710.             error = 11;
  711.             goto corrupt_index;
  712.         }
  713.  
  714.         /*
  715.          * Date:
  716.          */
  717.         if (fgets(buf, sizeof buf, fp) == NULL) {
  718.             error = 12;
  719.             goto corrupt_index;
  720.         }
  721.  
  722.         buf[strlen (buf)-1] = '\0';
  723.         my_strncpy (arts[top].date, buf, 12);
  724.  
  725.         /*
  726.          * Archive-name:
  727.          */
  728.         if (fgets(buf, sizeof buf, fp) == NULL) {
  729.             error = 13;
  730.             goto corrupt_index;
  731.         }
  732.  
  733.         if (buf[0] == '\n') {
  734.             arts[top].archive = (char *) 0;
  735.         } else if (buf[0] == '%') {
  736.             n = atoi (&buf[1]);
  737.             if (n > top || n < 0) {
  738.                 error = 14;
  739.                 goto corrupt_index;
  740.             }
  741.             arts[top].archive = arts[n].archive;
  742.         } else if (buf[0] == ' ') {
  743.             for (p = &buf[1]; *p && *p != '\n' ; p++)
  744.                 continue;
  745.             *p = '\0';
  746.             arts[top].archive = hash_str (&buf[1]);
  747.         } else {
  748.             error = 15;
  749.             goto corrupt_index;
  750.         }
  751.  
  752.         /*
  753.          * part no.
  754.          */
  755.         if (fgets(buf, sizeof buf, fp) == NULL) {
  756.             error = 16;
  757.             goto corrupt_index;
  758.         }
  759.  
  760.         if (buf[0] != ' ') { 
  761.             buf[strlen (buf)-1] = '\0';
  762.             arts[top].part = str_dup (buf);
  763.         }
  764.  
  765.         /*
  766.          * patch no.
  767.          */
  768.         if (fgets(buf, sizeof buf, fp) == NULL) {
  769.             error = 17;
  770.             goto corrupt_index;
  771.         }
  772.  
  773.         if (buf[0] != ' ') { 
  774.             buf[strlen (buf)-1] = '\0';
  775.             arts[top].patch = str_dup (buf);
  776.         }
  777.  
  778.         debug_print_header (&arts[top]);
  779.     }
  780.  
  781.     fclose(fp);
  782.     return TRUE;
  783.  
  784. corrupt_index:
  785.     if (! update) {
  786.         sprintf (msg, txt_corrupt_index, index_file, error, top); 
  787.         error_message (msg, "");
  788.     }
  789.  
  790.     if (debug == 2) {
  791.         sprintf (msg, "cp %s INDEX.BAD", index_file);
  792.         system (msg);
  793.     }
  794.  
  795.     last_read_article = 0L;
  796.     if (fp) {
  797.         fclose(fp);
  798.     }    
  799.     set_tin_uid_gid();
  800.     unlink (index_file);
  801.     set_real_uid_gid();
  802.     top = 0;
  803.     return FALSE;
  804. }
  805.  
  806.  
  807. /*
  808.  *  Look in the local $HOME/RCDIR/INDEXDIR (or wherever) directory for the
  809.  *  index file for the given group.  Hashing the group name gets
  810.  *  a number.  See if that #.1 file exists; if so, read first line.
  811.  *  Group we want?  If no, try #.2.  Repeat until no such file or
  812.  *  we find an existing file that matches our group.
  813.  */
  814.  
  815. void find_index_file (group)
  816.     char *group;
  817. {
  818.     char *p;
  819.     FILE *fp;
  820.     int i = 1;
  821.     static char buf[LEN];
  822.     char dir[PATH_LEN];
  823.     unsigned long h;
  824.  
  825.     h = hash_groupname (group);
  826.  
  827.     if (read_news_via_nntp && xindex_supported) {
  828.         sprintf (index_file, "/tmp/xindex.%d", process_id);
  829.         return;
  830.     }
  831.     
  832.     if (local_index) {
  833.         my_strncpy (dir, indexdir, sizeof (dir));
  834.     } else {
  835.         sprintf (dir, "%s/%s", spooldir, INDEXDIR);
  836.     }
  837.     
  838.     while (TRUE) {
  839.         sprintf (index_file, "%s/%lu.%d", dir, h, i);
  840.         
  841.         if ((fp = fopen (index_file, "r")) == (FILE *) 0) {
  842.             return;
  843.         }
  844.  
  845.         if (fgets (buf, sizeof (buf), fp) == (char *) 0) {
  846.             fclose (fp);
  847.             return;
  848.         }
  849.         fclose (fp);
  850.  
  851.         for (p = buf; *p && *p != '\n'; p++) {
  852.             continue;
  853.         }    
  854.         *p = '\0';
  855.  
  856.         if (strcmp (buf, group) == 0) {
  857.             return;
  858.         }    
  859.         i++;
  860.     }    
  861. }
  862.  
  863. /*
  864.  *  Run the index file updater only for the groups we've loaded.
  865.  */
  866.  
  867. void do_update ()
  868. {
  869.     int i, j;
  870.     char group_path[PATH_LEN];
  871.     char *p;
  872.     long beg_epoch, end_epoch;
  873.     
  874.     if (verbose) {
  875.         time (&beg_epoch);
  876.     }
  877.  
  878.     for (i = 0; i < group_top; i++) {
  879.         my_strncpy (group_path, active[my_group[i]].name, sizeof (group_path));
  880.         for (p = group_path ; *p ; p++) {
  881.             if (*p == '.') {
  882.                 *p = '/';
  883.             }
  884.         }
  885.         if (verbose) {
  886.             printf ("%s %s\n", (catchup ? "Catchup" : "Updating"),
  887.                     active[my_group[i]].name);
  888.             fflush (stdout);
  889.         }
  890.         index_group (active[my_group[i]].name, group_path);
  891.         if (catchup) {
  892.             for (j = 0; j < top; j++) {
  893.                 arts[j].unread = ART_READ;
  894.             }
  895.             update_newsrc (active[my_group[i]].name, my_group[i], FALSE);
  896.         }
  897.     }
  898.  
  899.     if (verbose) {
  900.         time (&end_epoch);
  901.         sprintf (msg, "%s %d groups in %ld seconds\n", 
  902.             (catchup ? "Caughtup" : "Updated"), group_top, end_epoch - beg_epoch);
  903.         wait_message (msg);
  904.     }
  905. }
  906.  
  907. /*
  908.  * convert date from ctime format to sortable format
  909.  * "24 Jul 91 12:59:59", "Mon, 24 Jul 91 12:59:59" and
  910.  * "Mon, 24 Jul 1991 12:59:59" are parsed and produce
  911.  * output of the form "910724125959"
  912.  */
  913.  
  914. char *parse_date (date, str)
  915.     char *date;
  916.     char *str;
  917. {
  918.     char buf[4];
  919.     int i = 0;
  920.  
  921.     /* Check for extraneous day-of-week at start of date */
  922.     while (isalpha(date[i]) || date[i] == ',' || date[i] == ' ') {
  923.         i++;
  924.     }
  925.     
  926.     if (date[i+1] == ' ') {    /* ie. "2 Aug..." instead of "12 Aug... */
  927.         str[4] = '0';        /* day */
  928.         str[5] = date[i++];
  929.         i++;
  930.     } else {
  931.         str[4] = date[i++];        /* day */
  932.         str[5] = date[i++];
  933.         i++;
  934.     }
  935.     
  936.     buf[0] = date[i++];        /* month in Jan,Feb,.. form */
  937.     buf[1] = date[i++];
  938.     buf[2] = date[i++];
  939.     buf[3] = '\0';
  940.  
  941.     i++;
  942.     
  943.     str[0] = date[i++];        /* year */
  944.     str[1] = date[i++];
  945.     if (isdigit(date[i])) {         /* 19xx format */
  946.         str[0] = date[i++];
  947.         str[1] = date[i++];
  948.     }
  949.     
  950.     i++;
  951.     
  952.     if (strcmp (buf, "Jan") == 0) {        /* convert Jan to 01 etc */
  953.         str[2] = '0';
  954.         str[3] = '1';
  955.     } else if (strcmp (buf, "Feb") == 0) {
  956.         str[2] = '0';
  957.         str[3] = '2';
  958.     } else if (strcmp (buf, "Mar") == 0) {
  959.         str[2] = '0';
  960.         str[3] = '3';
  961.     } else if (strcmp (buf, "Apr") == 0) {
  962.         str[2] = '0';
  963.         str[3] = '4';
  964.     } else if (strcmp (buf, "May") == 0) {
  965.         str[2] = '0';
  966.         str[3] = '5';
  967.     } else if (strcmp (buf, "Jun") == 0) {
  968.         str[2] = '0';
  969.         str[3] = '6';
  970.     } else if (strcmp (buf, "Jul") == 0) {
  971.         str[2] = '0';
  972.         str[3] = '7';
  973.     } else if (strcmp (buf, "Aug") == 0) {
  974.         str[2] = '0';
  975.         str[3] = '8';
  976.     } else if (strcmp (buf, "Sep") == 0) {
  977.         str[2] = '0';
  978.         str[3] = '9';
  979.     } else if (strcmp (buf, "Oct") == 0) {
  980.         str[2] = '1';
  981.         str[3] = '0';
  982.     } else if (strcmp (buf, "Nov") == 0) {
  983.         str[2] = '1';
  984.         str[3] = '1';
  985.     } else if (strcmp (buf, "Dec") == 0) {
  986.         str[2] = '1';
  987.         str[3] = '2';
  988.     } else {
  989.         str[2] = '0';
  990.         str[3] = '0';
  991.     }
  992.     
  993.     str[6] = date[i++];        /* hour */
  994.     str[7] = date[i++];
  995.  
  996.     i++;
  997.     
  998.     str[8] = date[i++];        /* minutes */
  999.     str[9] = date[i++];
  1000.     
  1001.     i++;
  1002.     
  1003.     str[10] = date[i++];    /* seconds */
  1004.     str[11] = date[i++];
  1005.  
  1006.     str[12] = '\0';        /* terminate string */
  1007.  
  1008.     return (str);
  1009. }
  1010.  
  1011.  
  1012. int artnum_comp (p1, p2)
  1013.     char *p1;
  1014.     char *p2;
  1015. {
  1016.     struct article_t *s1 = (struct article_t *) p1;
  1017.     struct article_t *s2 = (struct article_t *) p2;
  1018.  
  1019.     /* s1->artnum less than s2->artnum */
  1020.     if (s1->artnum < s2->artnum) {
  1021.         return -1;
  1022.     }
  1023.     /* s1->artnum greater than s2->artnum */
  1024.     if (s1->artnum > s2->artnum) {
  1025.         return 1;
  1026.     }
  1027.     return 0;
  1028. }
  1029.  
  1030.  
  1031. int subj_comp (p1, p2)
  1032.     char *p1;
  1033.     char *p2;
  1034. {
  1035.     struct article_t *s1 = (struct article_t *) p1;
  1036.     struct article_t *s2 = (struct article_t *) p2;
  1037.  
  1038.     /* return result of strcmp (reversed for descending) */
  1039.     return (sort_art_type == SORT_BY_SUBJ_ASCEND 
  1040.             ? my_stricmp (s1->subject, s2->subject) 
  1041.             : my_stricmp (s2->subject, s1->subject));
  1042. }
  1043.  
  1044.  
  1045. int from_comp (p1, p2)
  1046.     char *p1;
  1047.     char *p2;
  1048. {
  1049.     struct article_t *s1 = (struct article_t *) p1;
  1050.     struct article_t *s2 = (struct article_t *) p2;
  1051.  
  1052.     /* return result of strcmp (reversed for descending) */
  1053.     return (sort_art_type == SORT_BY_FROM_ASCEND 
  1054.             ? my_stricmp (s1->from, s2->from) 
  1055.             : my_stricmp (s2->from, s1->from));
  1056. }
  1057.  
  1058.  
  1059. int date_comp (p1, p2)
  1060.     char *p1;
  1061.     char *p2;
  1062. {
  1063.     struct article_t *s1 = (struct article_t *) p1;
  1064.     struct article_t *s2 = (struct article_t *) p2;
  1065.     /* return result of strcmp (reversed for descending) */
  1066.     return (sort_art_type == SORT_BY_DATE_ASCEND 
  1067.             ? strcmp (s1->date, s2->date) 
  1068.             : strcmp (s2->date, s1->date));
  1069. }
  1070.  
  1071.  
  1072. void set_article (art)
  1073.     struct article_t *art;
  1074. {    
  1075.     art->subject = (char *) 0;
  1076.     art->from = (char *) 0;
  1077.     art->name = (char *) 0;
  1078.     art->date[0] = '\0';
  1079.     art->archive = (char *) 0;
  1080.     art->part = (char *) 0;
  1081.     art->patch = (char *) 0;
  1082.     art->unread = ART_UNREAD;
  1083.     art->inthread = FALSE;
  1084.     art->killed = FALSE;
  1085.     art->tagged = FALSE;
  1086.     art->hot = FALSE;
  1087. }
  1088.