home *** CD-ROM | disk | FTP | other *** search
/ ftp.wwiv.com / ftp.wwiv.com.zip / ftp.wwiv.com / pub / OFFLINE / UQWK18.ZIP / UQWK18.TAR / news.c < prev    next >
C/C++ Source or Header  |  1994-01-21  |  24KB  |  1,308 lines

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <string.h>
  5. #include "uqwk.h"
  6.  
  7. #define MAXFILENAME 128
  8.  
  9. #ifdef SERVER
  10. #include "nntp.h"
  11. extern FILE *getactive_nntp(); 
  12. extern void group_nntp();
  13. extern FILE *nntpopen();
  14. #endif
  15.  
  16. /*
  17.  *  All sorts of stuff to do news processing
  18.  */
  19.  
  20. DoNews ()
  21. /*
  22.  *  Collect unread news into QWK packet
  23.  */
  24. {
  25.     char active_name[MAXFILENAME];
  26.     struct act_ent *ap;
  27.     struct nrc_ent *np;
  28.     struct ng_ent *ngp;
  29.  
  30.     /* Open ZipNews files */
  31.     if (zip_mode)
  32.     {
  33.         if (!OpenZipFiles()) return (0);
  34.     }
  35.  
  36.     /* Read .newsrc file */
  37.     if (!ReadNewsrc()) return (0);
  38.  
  39.     /* And active file */
  40.     if (!ReadActive()) return (0);
  41.  
  42.     /* Handle selection mode */
  43.     if (sel_mode)
  44.     {
  45.         DoSelect ();
  46. #ifdef SERVER
  47.         sprintf(active_name,"/tmp/rrnact.%d",getpid());
  48.         unlink(active_name);
  49. #endif
  50.         /* ZipNews "join" file */
  51.         if (zip_mode)
  52.         {
  53.             WriteJoin ();
  54.             fclose (nws_fd);
  55.             fclose (idx_fd);
  56.         }
  57.         return (0);
  58.     }
  59.  
  60.     /* Use .newsrc or desire newsgroup file? */
  61.     if (ng_list == NULL)
  62.     {
  63.         /*
  64.          *  No desired ng file, use newsrc
  65.          */
  66.         np = nrc_list;
  67.         while (np != NULL)
  68.         {
  69.             /* Check if too many blocks already */
  70.             if ( (blk_cnt >= max_blks) && (max_blks > 0) )
  71.             {
  72. #ifdef SERVER
  73.                 sprintf(active_name,"/tmp/rrnact.%d",getpid());
  74.                 unlink(active_name);
  75. #endif
  76.                 /* ZipNews "join" file */
  77.                 if (zip_mode)
  78.                 {
  79.                     WriteJoin ();
  80.                     fclose (nws_fd);
  81.                     fclose (idx_fd);
  82.                 }
  83.                 return (0);
  84.             }
  85.  
  86.             if (np->subscribed)
  87.             {
  88.                 /* Lookup group in active file */
  89.                 ap = FindActive (np->name);
  90.  
  91.                 /* Do this group */
  92.                 DoGroup (np, ap);
  93.             }
  94.             np = np->next;
  95.         }
  96. #ifdef SERVER
  97.         sprintf(active_name,"/tmp/rrnact.%d",getpid());
  98.         unlink(active_name);
  99. #endif
  100.         /* ZipNews "join" file */
  101.         if (zip_mode)
  102.         {
  103.             WriteJoin ();
  104.             fclose (nws_fd);
  105.             fclose (idx_fd);
  106.         }
  107.     }
  108.     else
  109.     {
  110.         /*
  111.          *  Read desired ng file for groups
  112.          */
  113.         for (ngp=ng_list; ngp!=NULL; ngp=ngp->next)
  114.         {
  115.             /* Check if too many blocks already */
  116.             if ( (blk_cnt >= max_blks) && (max_blks > 0) )
  117.             {
  118. #ifdef SERVER
  119.                 sprintf(active_name,"/tmp/rrnact.%d",getpid());
  120.                 unlink(active_name);
  121. #endif
  122.                 /* ZipNews "join" file */
  123.                 if (zip_mode)
  124.                 {
  125.                     WriteJoin ();
  126.                     fclose (nws_fd);
  127.                     fclose (idx_fd);
  128.                 }
  129.                 return (0);
  130.             }
  131.  
  132.             /* Find .newsrc entry, book if none */
  133.             for (np=nrc_list; np!=NULL; np=np->next)
  134.             {
  135.                 if (!strcmp (ngp->name, np->name)) break;
  136.             }
  137.  
  138.             if (np == NULL)
  139.             {
  140.                 fprintf (stderr, "%s: %s not in .newsrc\n",
  141.                         progname, ngp->name);
  142.             }
  143.             else
  144.             {
  145.                 /* Lookup group in active file */
  146.                 ap = FindActive (np->name);
  147.  
  148.                 /* Do this group */
  149.                 DoGroup (np, ap);
  150.             }
  151.         }
  152. #ifdef SERVER
  153.         sprintf(active_name,"/tmp/rrnact.%d",getpid());
  154.         unlink(active_name);
  155. #endif
  156.         /* ZipNews "join" file */
  157.         if (zip_mode)
  158.         {
  159.             WriteJoin ();
  160.             fclose (nws_fd);
  161.             fclose (idx_fd);
  162.         }
  163.     }
  164.  
  165.     return (1);
  166. }
  167.  
  168. int ReadNewsrc()
  169. /*
  170.  *  Read the .newsrc file
  171.  */
  172. {
  173.     char group_name[PATH_LEN];
  174.     struct nrc_ent *np, *lnp;
  175.     int i, n, c;
  176.  
  177.     /* lnp points to last entry */
  178.     lnp = NULL;
  179.  
  180.     /* Don't bother if we've alread read it */
  181.     if (nrc_list != NULL) return (1);
  182.  
  183.     /* Open it */
  184.     if (NULL == (nrc_fd = fopen (nrc_file, "r")))
  185.     {
  186.         fprintf (stderr, "%s: can't open %s\n", progname, nrc_file);
  187.         return (0);
  188.     }
  189.  
  190.     /* Read through */
  191.     while (NULL != Fgets (buf, BUF_LEN, nrc_fd))
  192.     {
  193.         /* Allocate a new nrc entry */
  194.         np = (struct nrc_ent *) malloc (sizeof (struct nrc_ent));
  195.         if (np == NULL) OutOfMemory();
  196.  
  197.         if (waf_mode)
  198.         {
  199.             /* Waffle is easy */
  200.             sscanf (buf, "%s %d", group_name, &n);
  201.             np->subscribed = 1;
  202.  
  203.             /* Allocate sub entry */
  204.             if (NULL == (np->sub = (struct sub_ent *) malloc
  205.                 (sizeof (struct sub_ent)))) OutOfMemory();
  206.  
  207.             np->sub->lo = 0;
  208.             np->sub->hi = n;
  209.             np->sub->next = NULL;
  210.         }
  211.         else  /* Regular Unix-style .newsrc */
  212.         {
  213.             /* Assume not subscribed */
  214.             np->subscribed = 0;
  215.  
  216.             /* Parse group name */
  217.             n = strlen (buf);
  218.             for (i=0; i<n; i++)
  219.             {
  220.                 /* Some .newsrc's don't have a space after the
  221.                     newsgroup name, so kludge it like this */
  222.                 if (buf[i] == ':')
  223.                 {
  224.                     np->subscribed = 1;
  225.                     buf[i] = ' ';
  226.  
  227.                     /* Parse subscription list */
  228.                     np->sub = SubList (&buf[i+1]);
  229.                 }
  230.                 if (buf[i] == '!')
  231.                 {
  232.                     np->subscribed = 0;
  233.                     buf[i] = ' ';
  234.  
  235.                     /* Parse subscription list */
  236.                     np->sub = SubList (&buf[i+1]);
  237.                 }
  238.             }
  239.             sscanf (buf, "%s", group_name);
  240.         }
  241.  
  242.         n = strlen (group_name);
  243.  
  244.         np->name = (char *) malloc (n+1);
  245.         if (np->name == NULL) OutOfMemory();
  246.         strcpy (np->name, group_name);
  247.         np->next = NULL;
  248.  
  249.         /* Add to nrc list */
  250.         if (lnp == NULL)
  251.         {
  252.             /* First one */
  253.             nrc_list = np;
  254.         }
  255.         else
  256.         {
  257.             /* Add to end */
  258.             lnp->next = np;
  259.         }
  260.         lnp = np;
  261.     }
  262.  
  263.     /* Walk through the nrc list, assign conference numbers */
  264.     np = nrc_list;
  265.     c = 0;
  266.     while (np != NULL)
  267.     {
  268.         if (np->subscribed)
  269.         {
  270.             np->conf = c;
  271.             c++;
  272.         }
  273.         else
  274.         {
  275.             np->conf = (-1);
  276.         }
  277.         np = np->next;
  278.     }
  279.  
  280.     fclose (nrc_fd);
  281.     return (1);
  282. }
  283.  
  284. int ReadActive()
  285. /*
  286.  *  Read active file
  287.  */
  288. {
  289.     char group_name[PATH_LEN];
  290.     struct act_ent *ap;
  291.  
  292.     /* Don't bother if it's already here */
  293.     if (act_list != NULL) return (1);
  294.  
  295. #ifdef SERVER
  296.     /* retrieve active file from news server */
  297.     if (NULL == (act_fd = getactive_nntp()))
  298.     {
  299.         fprintf (stderr, "%s: can't open %s\n", progname, act_file);
  300.         return (0);
  301.     }
  302. #else
  303.     /* Open the active file */
  304.     if (NULL == (act_fd = fopen (act_file, "r")))
  305.     {
  306.         fprintf (stderr, "%s: can't open %s\n", progname, act_file);
  307.         return (0);
  308.     }
  309. #endif
  310.  
  311.     /* Read through it */
  312.     while (NULL != Fgets (buf, BUF_LEN, act_fd))
  313.     {
  314.         /* Get new act entry */
  315.         ap = (struct act_ent *) malloc (sizeof (struct act_ent));
  316.         if (ap == NULL) OutOfMemory();
  317.  
  318.         /* Parse name, message numbers */
  319.         sscanf (buf, "%s %d %d", group_name, &ap->hi, &ap->lo);
  320.         ap->name = (char *) malloc (1+strlen(group_name));
  321.         if (ap->name == NULL) OutOfMemory();
  322.         strcpy (ap->name, group_name);
  323.  
  324.         /* Add to list */
  325.         ap->next = act_list;
  326.         act_list = ap;
  327.     }
  328.     fclose (act_fd);
  329.     return (1);
  330. }
  331.  
  332. struct act_ent *FindActive (c)
  333. char *c;
  334. /*
  335.  *  Look for group's active entry given group name
  336.  */
  337. {
  338.     struct act_ent *ap;
  339.  
  340.     ap = act_list;
  341.     while (NULL != ap)
  342.     {
  343.         if (!strcmp (c, ap->name)) return (ap);
  344.         ap = ap->next;
  345.     }
  346.     return (NULL);
  347. }
  348.  
  349. DoGroup (np, ap)
  350. struct nrc_ent *np;
  351. struct act_ent *ap;
  352. /*
  353.  *  Process given group
  354.  */
  355. {
  356.     char news_path[PATH_LEN];
  357.     int i, n;
  358.     struct conf_ent *cp;
  359.  
  360.     printf ("%s: %s\n", progname, np->name);
  361.  
  362.     /* Make a new conference with this name */
  363.     cp = NewConference (np->name, np->conf);
  364.  
  365.     /* Remember no ZipNews index entry yet */
  366.     if (zip_mode) zip_flag = 0;
  367.  
  368. #ifdef SERVER
  369.     /* select group name from news server */
  370.     group_nntp(np->name);
  371. #else
  372.     /* Construct path name for articles in this group */
  373.     strcpy (news_path, news_dir);
  374.     strcat (news_path, "/");
  375.     strcat (news_path, np->name);
  376.     strcat (news_path, "/");
  377.     n = strlen (news_path);
  378.     for (i=0; i<n; i++) if (news_path[i] == '.') news_path[i] = '/';
  379. #endif
  380.  
  381.     /* If the group doesn't exist (that is, doesn't appear in
  382.        the active file), do nothing else */
  383.     if (ap == NULL)
  384.     {
  385.         if (!slnp_mode && !zip_mode && !sum_mode) NdxClose (ndx_fd);
  386.         if (slnp_mode) MsgClose (msg_fd);
  387.         return (0);
  388.     }
  389.  
  390.     /* Fix up the subscription list */
  391.     np->sub = FixSub (np->sub, ap->lo, ap->hi);
  392.  
  393.     /* Look through unread articles */
  394.     for (i=ap->lo; i<=ap->hi; i++)
  395.     {
  396.         /* Check max block count */
  397.         if ( (blk_cnt >= max_blks) && (max_blks > 0) )
  398.         {
  399.             if (!slnp_mode && !zip_mode && !sum_mode)
  400.                             NdxClose (ndx_fd);
  401.             if (slnp_mode) MsgClose (msg_fd);
  402.             return (0);
  403.         }
  404.  
  405.         /* Process this article */
  406.         if (!IsRead (i, np->sub))
  407.         {
  408.             /* Mark as read */
  409.             np->sub = MarkRead (i, np->sub);
  410.  
  411.             /* Process the article */
  412.             DoArticle (news_path, i, np, cp);
  413.         }
  414.     }
  415.     if (!slnp_mode && !zip_mode && !sum_mode) NdxClose (ndx_fd);
  416.     if (slnp_mode) MsgClose (msg_fd);
  417.  
  418.     return (1);
  419. }
  420.  
  421. DoArticle (news_path, artnum, np, cp)
  422. char *news_path;
  423. int artnum;
  424. struct nrc_ent *np;
  425. struct conf_ent *cp;
  426. {
  427.     struct qwk_hdr hdr;
  428.     struct stat stat_buf;
  429.     long txt_offset, end_offset;
  430.     int n, out_bytes;
  431.     char ndx[5], *eof;
  432.     FILE *art_fd;
  433.     char art_file[PATH_LEN];
  434.  
  435. #ifdef SERVER
  436.     /* retrieve article from news server */
  437. /*    printf("retrieving article %d\n",artnum); */
  438.     art_fd = nntpopen(artnum, GET_ARTICLE);
  439.     if (art_fd == NULL) return (0);
  440.  
  441.     /* Construct article's file name */ 
  442.         sprintf(art_file,"/tmp/rrn%ld.%ld", (long) artnum, getpid());
  443. #else
  444.     /* Construct article's file name */ 
  445.     sprintf (art_file, "%s%d", news_path, artnum);
  446.  
  447.     /* Forget it if we can't open the article */
  448.     if (NULL == (art_fd = fopen (art_file, "r"))) return (0);
  449.  
  450. #endif
  451.     /* stat() the article to get file size */
  452.     if (0 != stat (art_file, &stat_buf))
  453.     {
  454.         fclose (art_fd);
  455. #ifdef SERVER
  456.         unlink (art_file);
  457. #endif
  458.         return (0);
  459.     }
  460.  
  461.     end_offset = stat_buf.st_size;
  462.  
  463.     /* Skip empty articles */
  464.     if (end_offset == 0)
  465.     {
  466.         fclose (art_fd);
  467. #ifdef SERVER
  468.         unlink (art_file);
  469. #endif
  470.         return (0);
  471.     }
  472.  
  473.     /* We now assume the article is for real, so we can
  474.        bump this conference's article count */
  475.     cp->count++;
  476.  
  477.     /* Check the Xref line if desired */
  478.     if (xrf_mode) DoXref (art_fd, end_offset);
  479.  
  480.     /* Do SLNP stuff */
  481.     if (slnp_mode)
  482.     {
  483.         SLNPArticle (art_fd, end_offset);
  484.         fclose (art_fd);
  485. #ifdef SERVER
  486.         unlink(art_file);
  487. #endif
  488.         return (1);
  489.     }
  490.  
  491.     /* Do ZipNews stuff */
  492.     if (zip_mode)
  493.     {
  494.         ZipArticle (art_fd, end_offset, np);
  495.         fclose (art_fd);
  496. #ifdef SERVER
  497.         unlink(art_file);
  498. #endif
  499.         return (1);
  500.     }
  501.  
  502.     /* Do summary stuff */
  503.     if (sum_mode)
  504.     {
  505.         SumArticle (art_fd, artnum, end_offset, np);
  506.         fclose (art_fd);
  507. #ifdef SERVER
  508.         unlink(art_file);
  509. #endif
  510.         return (1);
  511.     }
  512.  
  513.     /* Write the index file entry */
  514.     inttoms (blk_cnt, ndx);
  515.     ndx[4] = np->conf;
  516.     fwrite (ndx, 5, 1, ndx_fd);
  517.  
  518.     Spaces (&hdr, 128);
  519.  
  520.     /* Fill in some header fields */
  521.     hdr.status = QWK_PUBLIC;
  522.     PadNum (msg_cnt, hdr.number, 7);
  523.     Spaces (hdr.password, 12);
  524.     Spaces (hdr.refer, 8);
  525.     hdr.flag = QWK_ACT_FLAG;
  526.     IntNum (np->conf, hdr.conference);
  527.     IntNum (msg_cnt+1, hdr.msg_num);
  528.     hdr.tag = ' ';
  529.     PadString ("ALL", hdr.to, 25);
  530.  
  531.     msg_cnt++;
  532.  
  533.     /* Process header lines */
  534.     eof = Fgets (buf, BUF_LEN, art_fd);
  535.     while ( (0 != strlen(buf)) && (eof != NULL) )
  536.     {
  537.         if (!strncmp (buf, "Date: ", 6))
  538.         {
  539.             ParseDate (&buf[6], &hdr);
  540.         }
  541.         else if (!strncmp (buf, "Subject: ", 9))
  542.         {
  543.             PadString (&buf[9], hdr.subject, 25);
  544.         }
  545.         else if (!strncmp (buf, "From: ", 6))
  546.         {
  547.             PadString (ParseFrom(&buf[6]), hdr.from, 25);
  548.         }
  549.  
  550.         eof = Fgets (buf, BUF_LEN, art_fd);
  551.     }
  552.  
  553.     txt_offset = ftell (art_fd);
  554.  
  555.     /* Compute block count */
  556.     if (inc_hdrs)
  557.     {
  558.         PadNum (2+end_offset/128, hdr.blocks, 6);
  559.         blk_cnt += 1+end_offset/128;
  560.     }
  561.     else
  562.     {
  563.         PadNum (2+(end_offset-txt_offset)/128, hdr.blocks, 6);
  564.         blk_cnt += 1+(end_offset-txt_offset)/128;
  565.     }
  566.  
  567.     /* Write the message header */
  568.     fwrite (&hdr, 128, 1, msg_fd);
  569.     blk_cnt++;
  570.  
  571.     /* Now write the article's text */
  572.     if (inc_hdrs) fseek (art_fd, 0, 0);
  573.     out_bytes = 0;
  574.  
  575.     while (NULL != Fgets (buf, BUF_LEN, art_fd))
  576.     {
  577.         n = strlen (buf);
  578.         fwrite (buf, n, 1, msg_fd);
  579.         out_bytes += n;
  580.  
  581.         if (n < BUF_LEN-1)
  582.         {
  583.             fputc (QWK_EOL, msg_fd);
  584.             out_bytes++;
  585.         }
  586.     }
  587.  
  588.     /* Pad block as necessary */
  589.     n = out_bytes % 128;
  590.     for (;n<128;n++) fputc (' ', msg_fd);
  591.  
  592.     fclose (art_fd);
  593. #ifdef SERVER
  594.     unlink(art_file);
  595. #endif
  596.  
  597.     return (1);
  598. }
  599.  
  600. SLNPArticle (art_fd, bytes)
  601. FILE *art_fd;
  602. int bytes;
  603. /*
  604.  *  Convert an article to SLNP format
  605.  */
  606. {
  607.         int c;
  608.  
  609.         /* Write "rnews" line */
  610.         fprintf (msg_fd, "#! rnews %d\n", bytes);
  611.  
  612.     /* Maintain block count */
  613.     blk_cnt += (bytes + 64) / 128;
  614.  
  615.         /* Copy bytes */
  616.         while (bytes--)
  617.         {
  618.                 c = fgetc (art_fd);
  619.                 fputc ((0xff & c), msg_fd);
  620.         }
  621.         return (1);
  622. }
  623.  
  624. ZipArticle (art_fd, bytes, np)
  625. FILE *art_fd;
  626. int bytes;
  627. struct nrc_ent *np;
  628. /*
  629.  *  Convert article to ZipNews format
  630.  */
  631. {
  632.     int c;
  633.     long offset;
  634.  
  635.     /* Write separator */
  636.     for (c=0; c<20; c++) fputc (1, nws_fd);
  637.     fprintf (nws_fd, "\r\n");
  638.  
  639.     /* Write index file entry for this group if there isn't
  640.        already one */
  641.     if (!zip_flag)
  642.     {
  643.         offset = ftell (nws_fd);
  644.         fprintf (idx_fd, "N %08d %s\r\n", offset, np->name);
  645.         zip_flag = 1;
  646.     }
  647.  
  648.     /* Maintain block count */
  649.     blk_cnt += (bytes + 64) / 128;
  650.  
  651.         /* Copy bytes */
  652.         while (bytes--)
  653.         {
  654.                 c = fgetc (art_fd);
  655.  
  656.         /* ZipNews doesn't like ^Z's */
  657.         if (c == 26) c = 32;
  658.  
  659.         /* Map LF to CRLF */
  660.         if (c == 10) fputc (13, nws_fd);
  661.  
  662.                 fputc ((0xff & c), nws_fd);
  663.         }
  664.         return (1);
  665. }
  666.  
  667. OutOfMemory()
  668. {
  669.     fprintf (stderr, "%s: out of memory\n", progname);
  670.     exit (0);
  671. }
  672.  
  673. struct sub_ent *SubList (c)
  674. char *c;
  675. /*
  676.  *  Parse a newsrc subscription line, make into subscription list
  677.  */
  678. {
  679.     struct sub_ent *sp, *sub_list;
  680.     int lo, hi;
  681.     char *range;
  682.  
  683.     /* Initialize subscription list */
  684.     sub_list = NULL;
  685.  
  686.     /* Loop through line entries */
  687.     range = strtok (c, ",");
  688.     while (range != NULL)
  689.     {
  690.         /* Get space for new list entry */
  691.         if (NULL == (sp = (struct sub_ent *)
  692.             malloc (sizeof (struct sub_ent)))) OutOfMemory();
  693.  
  694.         /* Determine if it's a range or single entry */
  695.         if (2 == sscanf (range, "%d-%d", &lo, &hi))
  696.         {
  697.             sp->lo = lo;
  698.             sp->hi = hi;
  699.  
  700.             /* Reverse them in case they're backwards */
  701.             if (hi < lo)
  702.             {
  703.                 sp->lo = hi;
  704.                 sp->hi = lo;
  705.             }
  706.         }
  707.         else    /* Not a range */
  708.         {
  709.             sp->lo = atoi (range);
  710.             sp->hi = sp->lo;
  711.         }
  712.  
  713.         /* Check if range overlaps last one */
  714.         if ( (sub_list != NULL) && (sp->lo <= (sub_list->hi + 1)))
  715.         {
  716.             /* Combine ranges */
  717.             if (sp->lo < sub_list->lo) sub_list->lo = sp->lo;
  718.             if (sp->hi > sub_list->hi) sub_list->hi = sp->hi;
  719.  
  720.             /* Free old one */
  721.             free (sp);
  722.         }
  723.         else
  724.         {
  725.             /* No overlap, maintain pointers */
  726.             sp->next = sub_list;
  727.             sub_list = sp;
  728.         }
  729.  
  730.         range = strtok (NULL, ",");
  731.     }
  732.  
  733.     return (sub_list);
  734. }
  735.  
  736. int IsRead (num, sp)
  737. int num;
  738. struct sub_ent *sp;
  739. /*
  740.  *  Determine if an article has been read
  741.  */
  742. {
  743.     /* Remember the list is from hi number to lo number */
  744.  
  745.     /* Look through the list */
  746.     while (sp != NULL)
  747.     {
  748.         if (num > sp->hi) return (0);
  749.         if ( (num >= sp->lo) && (num <= sp->hi) ) return (1);
  750.  
  751.         sp = sp->next;
  752.     }
  753.  
  754.     return (0);
  755. }
  756.  
  757. struct sub_ent *MarkRead (num, sp_head)
  758. int num;
  759. struct sub_ent *sp_head;
  760. /*
  761.  *  Mark article as read
  762.  *
  763.  *  Remember, the list is stored from highest numbers to lowest numbers.
  764.  */
  765. {
  766.     struct sub_ent *sp, *tsp, *lsp;
  767.  
  768.     sp = sp_head;
  769.  
  770.     /* If num is much higher than highest list, or the list is
  771.        empty, we need new entry */
  772.     if ( (sp == NULL) || (num > (sp->hi + 1)))
  773.     {
  774.         if (NULL == (tsp = (struct sub_ent *)
  775.             malloc (sizeof (struct sub_ent)))) OutOfMemory();
  776.  
  777.         tsp->lo = tsp->hi = num;
  778.         tsp->next = sp;
  779.  
  780.         return (tsp);
  781.     }
  782.  
  783.     /* lsp remembers last entry in case we need to add a new entry */
  784.     lsp = NULL;
  785.  
  786.     /* Find appropriate entry for this number */
  787.     while (sp != NULL)
  788.     {
  789.         /* Have to squeeze one in before this one? */
  790.         if (num > (sp->hi + 1))
  791.         {
  792.             if (NULL == (tsp = (struct sub_ent *)
  793.                 malloc (sizeof (struct sub_ent))))
  794.                     OutOfMemory();
  795.  
  796.             tsp->lo = tsp->hi = num;
  797.             tsp->next = sp;
  798.             lsp->next = tsp;
  799.  
  800.             return (sp_head);
  801.         }
  802.  
  803.         /* One greater than entry's hi? */
  804.         if (num == (sp->hi + 1))
  805.         {
  806.             sp->hi = num;
  807.             return (sp_head);
  808.         }
  809.  
  810.         /* In middle of range, do nothing */
  811.         if ( (num >= sp->lo) && (num <= sp->hi) ) return (sp_head);
  812.  
  813.         /* One too lo, must check if we merge with next entry */
  814.         if (num == (sp->lo - 1))
  815.         {
  816.             if (NULL == sp->next)
  817.             {
  818.                 /* No next entry to merge with */
  819.                 sp->lo = num;
  820.                 return (sp_head);
  821.             }
  822.  
  823.             /* Check for merge */
  824.             if (num == (sp->next->hi + 1))
  825.             {
  826.                 tsp = sp->next;
  827.                 sp->lo = tsp->lo;
  828.                 sp->next = tsp->next;
  829.                 free (tsp);
  830.                 return (sp_head);
  831.             }
  832.             else
  833.             {
  834.                 /* No merge */
  835.                 sp->lo = num;
  836.                 return (sp_head);
  837.             }
  838.         }
  839.  
  840.         lsp = sp;
  841.         sp = sp->next;
  842.     }
  843.  
  844.     /* We flew off the end and need a new entry */
  845.     if (NULL == (tsp = (struct sub_ent *)
  846.         malloc (sizeof (struct sub_ent)))) OutOfMemory();
  847.  
  848.     tsp->lo = tsp->hi = num;
  849.     tsp->next = NULL;
  850.     lsp->next = tsp;
  851.  
  852.     return (sp_head);
  853. }
  854.  
  855. WriteSub (fd, sp)
  856. FILE *fd;
  857. struct sub_ent *sp;
  858. /*
  859.  *  Write the subscription list
  860.  */
  861. {
  862.     /* Do this recursively to reverse the order */
  863.     ws (fd, sp, sp);
  864.     fprintf (fd, "\n");
  865. }
  866.  
  867. ws (fd, sp, sp_head)
  868. FILE *fd;
  869. struct sub_ent *sp, *sp_head;
  870. {
  871.     if (sp == NULL) return (0);
  872.  
  873.     /* Do the rest of them */
  874.     ws (fd, sp->next, sp_head);
  875.  
  876.     /* Do this one */
  877.     if (sp->lo == sp->hi)
  878.     {
  879.         fprintf (fd, "%d", sp->lo); fflush (fd);
  880.     }
  881.     else
  882.     {
  883.         fprintf (fd, "%d-%d", sp->lo, sp->hi); fflush (fd);
  884.     }
  885.  
  886.     if (sp != sp_head) fprintf (fd, ",");
  887. }
  888.  
  889. struct sub_ent *FixSub (sp, lo, hi)
  890. struct sub_ent *sp;
  891. int lo, hi;
  892. /*
  893.  *  Sanity fixes to the subscription list
  894.  */
  895. {
  896.     struct sub_ent *tsp1, *tsp2, *tsp3;
  897.  
  898.     /* If the list is empty, make one new entry marking everything
  899.        up to the lowest available article as read */
  900.     if (sp == NULL)
  901.     {
  902.         if (NULL == (tsp1 = (struct sub_ent *) malloc
  903.             (sizeof (struct sub_ent)))) OutOfMemory();
  904.  
  905.         tsp1->lo = 1;
  906.         tsp1->hi = (lo > 1) ? (lo-1) : 1;
  907.         tsp1->next = NULL;
  908.         return (tsp1);
  909.     }
  910.  
  911.     /* If the highest read article is greater than the highest
  912.        available article, assume the group has been reset */
  913.     if (sp->hi > hi)
  914.     {
  915.         /* Mark everything as unread */
  916.         sp->lo = 1;
  917.         sp->hi = (lo > 1) ? (lo-1) : 1;
  918.  
  919.         /* Free the rest */
  920.         tsp1 = sp->next;
  921.         while (tsp1 != NULL)
  922.         {
  923.             tsp2 = tsp1->next;
  924.             free (tsp1);
  925.             tsp1 = tsp2;
  926.         }
  927.         sp->next = NULL;
  928.         return (sp);
  929.     }
  930.  
  931.     /* Now walk through the list and eliminate ranges lower
  932.        than the lowest available article */
  933.     tsp1 = sp;
  934.     while (tsp1 != NULL)
  935.     {
  936.         /* If lowest read article of this range is smaller
  937.            than the lowest available article, all the rest
  938.            of the ranges are unnecessary */
  939.  
  940.         if (tsp1->lo < lo)
  941.         {
  942.             /* Make this range from 1 to lo */
  943.             tsp1->lo = 1;
  944.             if (tsp1->hi < lo) tsp1->hi = lo - 1;
  945.  
  946.             /* Free the rest */
  947.             tsp2 = tsp1->next;
  948.             while (tsp2 != NULL)
  949.             {
  950.                 tsp3 = tsp2->next;
  951.                 free (tsp2);
  952.                 tsp2 = tsp3;
  953.             }
  954.             tsp1->next = NULL;
  955.             return (sp);
  956.         }
  957.         tsp1 = tsp1->next;
  958.     }
  959.     return (sp);    /* Probably shouldn't get here */
  960. }
  961.  
  962. int OpenZipFiles ()
  963. /*
  964.  *  Open files for a ZipNews news packet
  965.  */
  966. {
  967.     char fn[PATH_LEN];
  968.  
  969.     /* Open .nws file */
  970.     sprintf (fn, "%s/%s.nws", home_dir, user_name);
  971.     if (NULL == (nws_fd = fopen (fn, "w")))
  972.     {
  973.         fprintf (stderr, "%s: can't open %s\n", progname, fn);
  974.         return (0);
  975.     }
  976.  
  977.     /* Open .idx file */
  978.     sprintf (fn, "%s/%s.idx", home_dir, user_name);
  979.     if (NULL == (idx_fd = fopen (fn, "w")))
  980.     {
  981.         fprintf (stderr, "%s: can't open %s\n", progname, fn);
  982.         fclose (nws_fd);
  983.         return (0);
  984.     }
  985.  
  986.     return (1);
  987. }
  988.  
  989. WriteJoin ()
  990. /*
  991.  *  Write the ZipNews join file
  992.  */
  993. {
  994.     FILE *fd;
  995.     char fn[PATH_LEN];
  996.     struct nrc_ent *np;
  997.  
  998.     /* Open join file */
  999.     sprintf (fn, "%s/%s.jn", home_dir, user_name);
  1000.     if (NULL == (fd = fopen (fn, "w")))
  1001.     {
  1002.         fprintf (stderr, "%s: can't open %s\n", progname, fn);
  1003.         return (0);
  1004.     }
  1005.  
  1006.     /* Walk through the newsrc */
  1007.     np = nrc_list;
  1008.     while (np != NULL)
  1009.     {
  1010.         if (np->subscribed)
  1011.         {
  1012.             if (np->sub == NULL)
  1013.             {
  1014.                 fprintf (fd, "%s 0\r\n", np->name);
  1015.             }
  1016.             else
  1017.             {
  1018.                 fprintf (fd, "%s %d\r\n",
  1019.                     np->name, np->sub->hi);
  1020.             }
  1021.         }
  1022.         np = np->next;
  1023.     }
  1024.  
  1025.     fclose (fd);
  1026.     return (1);
  1027. }
  1028.  
  1029. NdxClose (fd)
  1030. FILE *fd;
  1031. /*
  1032.  *  Close ndx file, remove if empty
  1033.  */
  1034. {
  1035.     long offset;
  1036.  
  1037.     /* See how big it is */
  1038.     offset = ftell (fd);
  1039.  
  1040.     /* Close it */
  1041.     fclose (fd);
  1042.  
  1043.     /* Remove it if it's empty */
  1044.     if (offset == 0) unlink (ndx_fn);
  1045. }
  1046.  
  1047. MsgClose (fd)
  1048. FILE *fd;
  1049. /*
  1050.  *  Close msg file, remove if empty
  1051.  */
  1052. {
  1053.     long offset;
  1054.  
  1055.     /* See how big it is */
  1056.     offset = ftell (fd);
  1057.  
  1058.     /* Close it */
  1059.     fclose (fd);
  1060.  
  1061.     /* Remove it if it's empty */
  1062.     if (offset == 0) unlink (msg_fn);
  1063. }
  1064.  
  1065. DoXref (fd, bytes)
  1066. FILE *fd;
  1067. long bytes;
  1068. /*
  1069.  *  Look for Xref line.
  1070.  */
  1071. {
  1072.     long offset;
  1073.     int n;
  1074.     char *rc;
  1075.  
  1076.     /* Remember file position */
  1077.     offset = ftell (fd);
  1078.  
  1079.     /* Look through header */
  1080.     rc = Fgets (buf, BUF_LEN, fd);
  1081.     n = strlen (buf);
  1082.     while ( (rc != NULL) && (bytes > 0) && (n > 0) )
  1083.     {
  1084.         /* Xref: line? */
  1085.         if (!strncmp (buf, "Xref: ", 6))
  1086.         {
  1087.             /* Found one, process it */
  1088.             Xref (buf);
  1089.  
  1090.             /* Restore position, return */
  1091.             fseek (fd, offset, 0);
  1092.             return (1);
  1093.         }
  1094.  
  1095.         /* Get next line */
  1096.         bytes -= n;
  1097.         rc = Fgets (buf, BUF_LEN, fd);
  1098.         if (rc != NULL) n = strlen (buf);
  1099.     }    
  1100.  
  1101.     /* Reposition file */
  1102.     fseek (fd, offset, 0);
  1103.     return (0);
  1104. }
  1105.  
  1106. Xref (s)
  1107. char *s;
  1108. /*
  1109.  *  Process an Xref line.
  1110.  */
  1111. {
  1112.     char *c, *p, name[PATH_LEN];
  1113.     int num;
  1114.     struct nrc_ent *np;
  1115.  
  1116.     /* Skip the first two fields (Xref: and host) */
  1117.     c = strtok (s, " \t");
  1118.     if (c == NULL) return (0);
  1119.     c = strtok (NULL, " \t");
  1120.     if (c == NULL) return (0);
  1121.  
  1122.     /* Look through the rest of the fields */
  1123.     c = strtok (NULL, " \t");
  1124.     while (c != NULL)
  1125.     {
  1126.         /* Change : to space */
  1127.         for (p=c; *p; p++) if (*p == ':') *p = ' ';
  1128.  
  1129.         /* Parse xref entry */
  1130.         if (2 == sscanf (c, "%s %d", name, &num))
  1131.         {
  1132.             /* Find nrc entry for this group */
  1133.             for (np=nrc_list; np!=NULL; np=np->next)
  1134.             {
  1135.                 /* Match? */
  1136.                 if (!strcmp (np->name, name))
  1137.                 {
  1138.                     /* Mark as read */
  1139.                     np->sub = MarkRead (num, np->sub);
  1140.                     break;
  1141.                 }
  1142.             }
  1143.         }
  1144.  
  1145.         /* Next xref entry */
  1146.         c = strtok (NULL, " \t");
  1147.     }
  1148.  
  1149.     return (0);
  1150. }
  1151.  
  1152. SumArticle (fd, artnum, bytes, np)
  1153. FILE *fd;
  1154. long bytes;
  1155. struct nrc_ent *np;
  1156. int artnum;
  1157. /*
  1158.  *  Summarize article
  1159.  */
  1160. {
  1161.     char subject[PATH_LEN];
  1162.  
  1163.     /* Get subject line */
  1164.     if (!GetHdr (fd, subject, bytes, "Subject: ")) return (0);
  1165.  
  1166.     /* Write group name if this is first article for group */
  1167.     if (sum_flag == 0)
  1168.     {
  1169.         sum_flag = 1;
  1170.         fprintf (sum_fd, "\r\n*** %s\r\n", np->name);
  1171.     }
  1172.  
  1173.     /* Write article number, subject line */
  1174.     fprintf (sum_fd, "%d:%s\r\n", artnum, subject);
  1175. }
  1176.  
  1177. DoSelect ()
  1178. /*
  1179.  *  Select articles from a summary file
  1180.  */
  1181. {
  1182.     char group[PATH_LEN], news_path[PATH_LEN], *p;
  1183.     int artnum;
  1184.     struct act_ent *ap;
  1185.     struct nrc_ent *np, *tnp;
  1186.     struct conf_ent *cp;
  1187.  
  1188.     /* Open selection file */
  1189.     if (NULL == (sel_fd = fopen (sel_file, "r")))
  1190.     {
  1191.         fprintf (stderr, "%s: can't open %s\n", progname, sel_file);
  1192.         return (0);
  1193.     }
  1194.  
  1195.     /* Show no group yet */
  1196.     group[0] = 0;
  1197.  
  1198.     /* Read through lines */
  1199.     while (NULL != Fgets (buf, BUF_LEN, sel_fd))
  1200.     {
  1201.         /* Check if too many blocks already */
  1202.         if ( (blk_cnt >= max_blks) && (max_blks > 0) ) break;
  1203.  
  1204.         /* Ignore empty lines */
  1205.         if (0 == strlen (buf)) continue;
  1206.  
  1207.         /* First character determines type of line */
  1208.         switch (buf[0])
  1209.         {
  1210.         case '*':    /* New group */
  1211.  
  1212.             /* Close old index files if any */
  1213.             if (group[0] != 0)
  1214.             {
  1215.                 if (!slnp_mode && !zip_mode && !sum_mode)
  1216.                             NdxClose (ndx_fd);
  1217.                 if (slnp_mode) MsgClose (msg_fd);
  1218.             }
  1219.  
  1220.             /* New newsgroup */
  1221.             if (1 != sscanf (buf, "%*s %s", group))
  1222.             {
  1223.                 group[0] = 0;
  1224.                 break;
  1225.             }
  1226.  
  1227.             /* Find it in newsrc, bail if not there */
  1228.             np = NULL;
  1229.             for (tnp=nrc_list; tnp!=NULL; tnp=tnp->next)
  1230.             {
  1231.                 if (!strcmp (tnp->name, group)) np = tnp;
  1232.             }
  1233.             if (np == NULL)
  1234.             {
  1235.                 group[0] = 0;
  1236.                 break;
  1237.             }
  1238.  
  1239.             printf ("%s: %s\n", progname, group);
  1240.  
  1241.             /* Make new conference */
  1242.             cp = NewConference (group, np->conf);
  1243.  
  1244.             /* No ZipNews index yet */
  1245.             if (zip_mode) zip_flag = 0;
  1246. #ifdef SERVER
  1247.             /* Select group from NNTP server */
  1248.             group_nntp (group);
  1249. #else
  1250.             /* Make news path */
  1251.             sprintf (news_path, "%s/%s/", news_dir, group);
  1252.             for (p=(&news_path[0]); *p; p++)
  1253.                 if (*p == '.') *p = '/';
  1254. #endif
  1255.             /* Look up in active file */
  1256.             ap = FindActive (group);
  1257.  
  1258.             /* Do nothing if not in active file */
  1259.             if (ap == NULL)
  1260.             {
  1261.                 /* Show no valid group */
  1262.                 group[0] = 0;
  1263.  
  1264.                 if (!slnp_mode && !zip_mode && !sum_mode)
  1265.                             NdxClose (ndx_fd);
  1266.                 if (slnp_mode) MsgClose (msg_fd);
  1267.             }
  1268.             else
  1269.             {
  1270.                 /* Fix up the subscription list */
  1271.                 np->sub = FixSub (np->sub, ap->lo, ap->hi);
  1272.             }
  1273.  
  1274.             break;
  1275.  
  1276.         case '0':
  1277.         case '1':
  1278.         case '2':
  1279.         case '3':
  1280.         case '4':
  1281.         case '5':
  1282.         case '6':
  1283.         case '7':
  1284.         case '8':
  1285.         case '9':
  1286.             /* Wants article; skip if no group */
  1287.             if (group[0] != 0)
  1288.             {
  1289.                 /* Get article number */
  1290.                 sscanf (buf, "%d", &artnum);
  1291.  
  1292.                 /* Process it */
  1293.                 DoArticle (news_path, artnum, np, cp);
  1294.  
  1295.                 /* Mark as read */
  1296.                 np->sub = MarkRead (artnum, np->sub);
  1297.             }
  1298.             break;
  1299.  
  1300.         default:
  1301.             break;
  1302.         }
  1303.     }
  1304.  
  1305.     fclose (sel_fd);
  1306.     return (1);
  1307. }
  1308.