home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / trn_12.zip / src / bits.c < prev    next >
C/C++ Source or Header  |  1993-12-04  |  19KB  |  748 lines

  1. /* $Id: bits.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  2.  *
  3.  * $Log: bits.c,v $
  4.  * Revision 4.4.3.1  1992/02/01  03:09:32  sob
  5.  * Release 4.4 Patchlevel 3
  6.  *
  7.  * Revision 4.4  1991/09/09  20:18:23  sob
  8.  * release 4.4
  9.  *
  10.  *
  11.  * 
  12.  */
  13. /* This software is Copyright 1991 by Stan Barber. 
  14.  *
  15.  * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  16.  * use this software as long as: there is no monetary profit gained
  17.  * specifically from the use or reproduction of this software, it is not
  18.  * sold, rented, traded or otherwise marketed, and this copyright notice is
  19.  * included prominently in any copy made. 
  20.  *
  21.  * The author make no claims as to the fitness or correctness of this software
  22.  * for any use whatsoever, and it is provided as is. Any use of this software
  23.  * is at the user's own risk. 
  24.  */
  25.  
  26. #include "EXTERN.h"
  27. #include "common.h"
  28. #include "rcstuff.h"
  29. #include "head.h"
  30. #include "util.h"
  31. #include "final.h"
  32. #include "rn.h"
  33. #include "cheat.h"
  34. #include "ng.h"
  35. #include "artio.h"
  36. #include "intrp.h"
  37. #include "ngdata.h"
  38. #include "rcln.h"
  39. #include "kfile.h"
  40. #ifdef USETHREADS
  41. #include "threads.h"
  42. #include "rthreads.h"
  43. #endif
  44. #include "INTERN.h"
  45. #include "bits.h"
  46.  
  47. #ifdef DBM
  48. #    ifdef NULL
  49. #    undef NULL
  50. #    endif
  51. #    include <dbm.h>
  52. #endif
  53. MEM_SIZE ctlsize;            /* size of bitmap in bytes */
  54.  
  55. void
  56. bits_init()
  57. {
  58. #ifdef DELAYMARK
  59.     dmname = savestr(filexp(RNDELNAME));
  60. #else
  61.     ;
  62. #endif
  63. }
  64.  
  65. /* checkpoint the .newsrc */
  66.  
  67. void
  68. checkpoint_rc()
  69. {
  70. #ifdef DEBUGGING
  71.     if (debug & DEB_CHECKPOINTING) {
  72.     fputs("(ckpt)",stdout);
  73.     fflush(stdout);
  74.     }
  75. #endif
  76.     if (doing_ng)
  77.     restore_ng();            /* do not restore M articles */
  78.     if (rc_changed)
  79.     write_rc();
  80. #ifdef DEBUGGING
  81.     if (debug & DEB_CHECKPOINTING) {
  82.     fputs("(done)",stdout);
  83.     fflush(stdout);
  84.     }
  85. #endif
  86. }
  87.  
  88. /* reconstruct the .newsrc line in a human readable form */
  89.  
  90. void
  91. restore_ng()
  92. {
  93.     register char *s, *mybuf = buf;
  94.     register ART_NUM i;
  95.     ART_NUM count=0;
  96.     int safelen = LBUFLEN - 16;
  97.  
  98.     strcpy(buf,rcline[ng]);        /* start with the newsgroup name */
  99.     s = buf + rcnums[ng] - 1;        /* use s for buffer pointer */
  100. #ifdef USETHREADS
  101.     *s++ = RCCHAR(rcchar[ng]);        /* put the requisite : or !*/
  102. #else
  103.     *s++ = rcchar[ng];            /* put the requisite : or !*/
  104. #endif
  105.     *s++ = ' ';                /* put the not-so-requisite space */
  106.     for (i=1; i<=lastart; i++) {    /* for each article in newsgroup */
  107.     if (s-mybuf > safelen) {    /* running out of room? */
  108.         safelen *= 2;
  109.         if (mybuf == buf) {        /* currently static? */
  110.         *s = '\0';
  111.         mybuf = safemalloc((MEM_SIZE)safelen + 16);
  112.         strcpy(mybuf,buf);    /* so we must copy it */
  113.         s = mybuf + (s-buf);
  114.                     /* fix the pointer, too */
  115.         }
  116.         else {            /* just grow in place, if possible */
  117.         char *newbuf;
  118.  
  119.         newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
  120.         s = newbuf + (s-mybuf);
  121.         mybuf = newbuf;
  122.         }
  123.     }
  124.     if (!was_read(i))        /* still unread? */
  125.         count++;            /* then count it */
  126.     else {                /* article was read */
  127.         ART_NUM oldi;
  128.  
  129.         sprintf(s,"%ld",(long)i);    /* put out the min of the range */
  130.         s += strlen(s);        /* keeping house */
  131.         oldi = i;            /* remember this spot */
  132.         do i++; while (i <= lastart && was_read(i));
  133.                     /* find 1st unread article or end */
  134.         i--;            /* backup to last read article */
  135.         if (i > oldi) {        /* range of more than 1? */
  136.         sprintf(s,"-%ld,",(long)i);
  137.                     /* then it out as a range */
  138.         s += strlen(s);        /* and housekeep */
  139.         }
  140.         else
  141.         *s++ = ',';        /* otherwise, just a comma will do */
  142.     }
  143.     }
  144.     if (*(s-1) == ',')            /* is there a final ','? */
  145.     s--;                /* take it back */
  146.     *s++ = '\0';            /* and terminate string */
  147. #ifdef DEBUGGING
  148.     if (debug & DEB_NEWSRC_LINE && !panic) {
  149.     printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) ; FLUSH;
  150.     printf("%s\n",mybuf) ; FLUSH;
  151.     }
  152. #endif
  153.     free(rcline[ng]);            /* return old rc line */
  154.     if (mybuf == buf) {
  155.     rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
  156.                     /* grab a new rc line */
  157.     strcpy(rcline[ng], buf);    /* and load it */
  158.     }
  159.     else {
  160.     mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
  161.                     /* be nice to the heap */
  162.     rcline[ng] = mybuf;
  163.     }
  164.     *(rcline[ng] + rcnums[ng] - 1) = '\0';
  165.     if (rcchar[ng] == NEGCHAR) {    /* did they unsubscribe? */
  166.     printf(unsubto,ngname) ; FLUSH;
  167.     toread[ng] = TR_UNSUB;        /* make line invisible */
  168.     }
  169.     else
  170.     /*NOSTRICT*/
  171.     toread[ng] = (ART_UNREAD)count;        /* remember how many unread there are */
  172. }
  173.  
  174. /* mark an article unread, keeping track of toread[] */
  175.  
  176. void
  177. onemore(artnum)
  178. ART_NUM artnum;
  179. {
  180. #ifdef DEBUGGING
  181.     if (debug && artnum < firstbit) {
  182.     printf("onemore: %d < %d\n",artnum,firstbit) ; FLUSH;
  183.     return;
  184.     }
  185. #endif
  186.     if (ctl_read(artnum)) {
  187.     ctl_clear(artnum);
  188.     ++toread[ng];
  189.     }
  190. }
  191.  
  192. /* mark an article read, keeping track of toread[] */
  193.  
  194. void
  195. oneless(artnum)
  196. ART_NUM artnum;
  197. {
  198. #ifdef DEBUGGING
  199.     if (debug && artnum < firstbit) {
  200.     printf("oneless: %d < %d\n",artnum,firstbit) ; FLUSH;
  201.     return;
  202.     }
  203. #endif
  204.     if (!ctl_read(artnum)) {
  205.     ctl_set(artnum);
  206.     if (toread[ng] > TR_NONE)
  207.         --toread[ng];
  208.     }
  209. }
  210.  
  211. /* mark an article as unread, making sure that firstbit is properly handled */
  212. /* cross-references are left as read in the other newsgroups */
  213.  
  214. void
  215. unmark_as_read()
  216. {
  217.     check_first(art);
  218. #ifdef USETHREADS
  219.     find_article(art);
  220.     /* Keep selected_count accurate */
  221.     if (ctl_read(art)) {
  222.     if (p_art) {
  223.         if (selected_roots[p_art->root]) {
  224.         selected_count++;
  225.         }
  226.         root_article_cnts[p_art->root] = 1;
  227.     } else
  228.         unthreaded++;
  229.     }
  230.     scan_all_roots = FALSE;
  231. #endif
  232.     onemore(art);
  233. #ifdef MCHASE
  234. # ifdef USETHREADS
  235.     if ((olden_days > 1 || !p_art || (p_art->flags & HAS_XREFS))
  236.      && !parse_maybe(art))
  237. # else
  238.     if (!parse_maybe(art))
  239. # endif
  240.     chase_xrefs(art,FALSE);
  241. #endif
  242. }
  243.  
  244. #ifdef USETHREADS
  245. /* mark an article as read in this newsgroup and possibly chase xrefs.
  246. ** p_art must be set to the current article's data.
  247. */
  248.  
  249. void
  250. set_read(artnum,selected,chase_xrefs_flag)
  251. ART_NUM artnum;
  252. int selected;
  253. bool_int chase_xrefs_flag;
  254. {
  255.     if (!ctl_read(artnum)) {
  256.     oneless(artnum);
  257.     if (p_art->subject != -1)
  258.         selected_count -= selected;
  259.     }
  260.     if (chase_xrefs_flag && (p_art->flags & HAS_XREFS)) {
  261.     if (output_chase_phrase) {
  262. #ifdef VERBOSE
  263.         IF(verbose)
  264.         fputs("\nChasing xrefs", stdout);
  265.         ELSE
  266. #endif
  267. #ifdef TERSE
  268.         fputs("\nXrefs", stdout);
  269. #endif
  270.         output_chase_phrase = 0;
  271.     }
  272.     putchar('.'), fflush(stdout);
  273.     chase_xrefs(artnum, TRUE);
  274.     }
  275. }
  276.  
  277. /* mark an article as unread in this newsgroup only.
  278. ** p_art must be set to the current article's data.
  279. */
  280.  
  281. void
  282. set_unread(artnum,selected)
  283. ART_NUM artnum;
  284. int selected;
  285. {
  286.     if (artnum >= absfirst) {
  287.     check_first(artnum);
  288.     if (ctl_read(artnum)) {
  289.         onemore(artnum);
  290.         selected_count += selected;
  291.         root_article_cnts[p_art->root] = 1;
  292.         scan_all_roots = FALSE;
  293.     }
  294.     }
  295. }
  296. #endif
  297.  
  298. #ifdef DELAYMARK
  299. /* temporarily mark article as read.  When newsgroup is exited, articles */
  300. /* will be marked as unread.  Called via M command */
  301.  
  302. void
  303. delay_unmark(artnum)
  304. ART_NUM artnum;
  305. {
  306.     if (dmfp == Nullfp) {
  307.     dmfp = fos2open(dmname,"w+");
  308.     if (dmfp == Nullfp) {
  309.         printf(cantcreate,dmname) ; FLUSH;
  310.         sig_catcher(0);
  311.     }
  312.     }
  313. #ifdef USETHREADS
  314.     /* Keep selected_count accurate */
  315.     if (!ctl_read(artnum)) {
  316.     find_article(artnum);
  317.     if (p_art) {
  318.         if (selected_roots[p_art->root])
  319.         selected_count--;
  320.     } else
  321.         unthreaded--;
  322.     }
  323. #endif
  324.     oneless(artnum);            /* set the correct bit */
  325.     dmcount++;
  326.     fprintf(dmfp,"%ld\n",(long)artnum);
  327. }
  328. #endif
  329.  
  330. /* mark article as read.  If article is cross referenced to other */
  331. /* newsgroups, mark them read there also. */
  332.  
  333. void
  334. mark_as_read()
  335. {
  336. #ifdef USETHREADS
  337.     find_article(art);
  338.     /* Keep selected_count accurate */
  339.     if (!ctl_read(art)) {
  340.     if (p_art) {
  341.         if (selected_roots[p_art->root])
  342.         selected_count--;
  343.     } else
  344.         unthreaded--;
  345.     }
  346. #endif
  347.     oneless(art);            /* set the correct bit */
  348.     checkcount++;            /* get more worried about crashes */
  349. #ifdef USETHREADS
  350.     if (olden_days > 1 || !p_art || (p_art->flags & HAS_XREFS))
  351. #endif
  352.     chase_xrefs(art,TRUE);
  353. }
  354.  
  355. /* make sure we have bits set correctly down to firstbit */
  356.  
  357. void
  358. check_first(min)
  359. ART_NUM min;
  360. {
  361.     register ART_NUM i = firstbit;
  362.  
  363.     if (min < absfirst)
  364.     min = absfirst;
  365.     if (min < i) {
  366.     for (i--; i>=min; i--)
  367.         ctl_set(i);        /* mark as read */
  368.     firstart = firstbit = min;
  369.     }
  370. }
  371.  
  372. /* bring back articles marked with M */
  373.  
  374. #ifdef DELAYMARK
  375. void
  376. yankback()
  377. {
  378.     if (dmfp) {            /* delayed unmarks pending? */
  379. #ifdef VERBOSE
  380.     printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
  381.         dmcount == 1 ? nullstr : "s") ; FLUSH;
  382. #endif
  383.     rewind(dmfp);
  384.     while (fgets(buf,sizeof buf,dmfp) != Nullch) {
  385.         art = (ART_NUM)atol(buf);
  386.         unmark_as_read();
  387.     }
  388.     fclose(dmfp);
  389.     dmfp = Nullfp;
  390.     UNLINK(dmname);        /* and be tidy */
  391.     }
  392.     dmcount = 0;
  393. }
  394. #endif
  395.     
  396. /* run down xref list and mark as read or unread */
  397.  
  398. int
  399. chase_xrefs(artnum,markread)
  400. ART_NUM artnum;
  401. int markread;
  402. {
  403. #ifdef ASYNC_PARSE
  404.     if (parse_maybe(artnum))        /* make sure we have right header */
  405.     return -1;
  406. #endif
  407. #ifdef DBM
  408.     {
  409.     datum lhs, rhs;
  410.     datum fetch();
  411.     register char *idp;
  412.     char *ident_buf;
  413.     static FILE * hist_file = Nullfp;
  414. #else
  415.     if (
  416. #ifdef DEBUGGING
  417.     debug & DEB_FEED_XREF ||
  418. #endif
  419.     htype[XREF_LINE].ht_minpos >= 0) {
  420.                     /* are there article# xrefs? */
  421. #endif /* DBM */
  422.     char *xref_buf, *curxref;
  423.     register char *xartnum;
  424.     char *rver_buf = Nullch;
  425.     static char *inews_site = Nullch;
  426.     register ART_NUM x;
  427.     char tmpbuf[128];
  428. #ifdef DBM
  429.     long pos;
  430.     rver_buf = fetchlines(artnum,NGS_LINE);
  431.                     /* get Newsgroups */
  432. #ifdef XREF_WITH_COMMAS
  433.     if (!index(rver_buf,','))    /* if no comma, no Xref! */
  434.         return 0;
  435. #endif
  436.     if (hist_file == Nullfp) {    /* Init. file accesses */
  437. #ifdef DEBUGGING
  438.         if (debug)
  439.         printf ("chase_xref: opening files\n");
  440. #endif
  441.         dbminit(filexp(ARTFILE));
  442.         if ((hist_file = fos2open(filexp(ARTFILE), "r")) == Nullfp)
  443.         return 0;
  444.     }
  445.     xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
  446.     ident_buf = fetchlines(artnum,MESSID_LINE);
  447.                     /* get Message-ID */
  448. #ifdef DEBUGGING
  449.     if (debug)
  450.         printf ("chase_xref: Message-ID: %s\n", ident_buf);
  451. #endif
  452.     idp = ident_buf;
  453.     while (*++idp)            /* make message-id case insensitive */
  454.         if (isupper(*idp))
  455.             *idp = tolower (*idp);
  456.     lhs.dptr = ident_buf;        /* look up article by id */
  457.     lhs.dsize = strlen(lhs.dptr) + 1;
  458.     rhs = fetch(lhs);        /* fetch the record */
  459.     if (rhs.dptr == NULL)        /* if null, nothing there */
  460.         goto wild_goose;
  461.     bcopy((void *)rhs.dptr,(void *)&pos, 4);
  462.     fseek (hist_file, pos, 0);
  463.                 /* datum returned is position in hist file */
  464.     fgets (xref_buf, BUFSIZ, hist_file);
  465. #ifdef DEBUGGING
  466.     if (debug)
  467.         printf ("Xref from history: %s\n", xref_buf);
  468. #endif
  469.     curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
  470.     curxref = cpytill(tmpbuf, curxref, '\t') + 1;
  471. #ifdef DEBUGGING
  472.     if (debug)
  473.         printf ("chase_xref: curxref: %s\n", curxref);
  474. #endif
  475. #else /* !DBM */
  476. #ifdef DEBUGGING
  477.     if (htype[XREF_LINE].ht_minpos >= 0)
  478. #endif
  479.         xref_buf = fetchlines(artnum,XREF_LINE);
  480.                     /* get xrefs list */
  481. #ifdef DEBUGGING
  482.     else {
  483.         xref_buf = safemalloc((MEM_SIZE)100);
  484.         printf("Give Xref: ") ; FLUSH;
  485.         gets(xref_buf);
  486.     }
  487. #endif
  488. #ifdef DEBUGGING
  489.     if (debug & DEB_XREF_MARKER)
  490.         printf("Xref: %s\n",xref_buf) ; FLUSH;
  491. #endif
  492.     curxref = cpytill(tmpbuf,xref_buf,' ') + 1;
  493.  
  494.     /* Make sure site name on Xref matches what inews thinks site is.
  495.      * Check first against last inews_site.  If it matches, fine.
  496.      * If not, fetch inews_site from current Relay-Version line and
  497.      * check again.  This is so that if the new administrator decides
  498.      * to change the system name as known to inews, rn will still do
  499.      * Xrefs correctly--each article need only match itself to be valid.
  500.      */ 
  501.     if (inews_site == Nullch || strNE(tmpbuf,inews_site)) {
  502. #ifndef NORELAY
  503.         char *t;
  504. #endif
  505.         if (inews_site != Nullch)
  506.         free(inews_site);
  507. #ifndef NORELAY
  508.         rver_buf = fetchlines(artnum,RVER_LINE);
  509.         if ((t = instr(rver_buf,"; site ", TRUE)) == Nullch)
  510. #else /* NORELAY */
  511.           /* In version 2.10.3 of news or afterwards, the Relay-Version
  512.            * and Posting-Version header lines have been removed.  For
  513.            * the code below to work as intended, I have modified it to
  514.            * extract the first component of the Path header line.  This
  515.            * should give the same effect as did the old code with respect
  516.            * to the use of the Relay-Version site name.
  517.            */
  518.           rver_buf = fetchlines(artnum,PATH_LINE);
  519.           if (instr(rver_buf,"!", TRUE) == Nullch)
  520. #endif /* NORELAY */
  521.         inews_site = savestr(nullstr);
  522.         else {
  523.         char new_site[128];
  524.  
  525. #ifndef NORELAY
  526.         cpytill(new_site,t + 7,'.');
  527. #else /* NORELAY */
  528.               cpytill(new_site,rver_buf,'!');
  529. #endif /* NORELAY */
  530.         inews_site = savestr(new_site);
  531.         }
  532.         if (strNE(tmpbuf,inews_site)) {
  533. #ifdef DEBUGGING
  534.         if (debug)
  535.             printf("Xref not from %s--ignoring\n",inews_site) ; FLUSH;
  536. #endif
  537.         goto wild_goose;
  538.         }
  539.     }
  540. #endif /* DBM */
  541.     while (*curxref) {
  542.                     /* for each newsgroup */
  543.         curxref = cpytill(tmpbuf,curxref,' ');
  544. #ifdef DBM
  545.         xartnum = index(tmpbuf,'/');
  546. #else
  547.         xartnum = index(tmpbuf,':');
  548. #endif /* DBM */
  549.         if (!xartnum)        /* probably an old-style Xref */
  550.         break;
  551.         *xartnum++ = '\0';
  552.         if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
  553.         x = atol(xartnum);
  554.         if (x)
  555.             if (markread) {
  556.             if (addartnum(x,tmpbuf))
  557.                 goto wild_goose;
  558.             }
  559. #ifdef MCHASE
  560.             else
  561.             subartnum(x,tmpbuf);
  562. #endif
  563.         }
  564.         while (*curxref && isspace(*curxref))
  565.         curxref++;
  566.     }
  567.       wild_goose:
  568.     free(xref_buf);
  569. #ifdef DBM
  570.     free(ident_buf);
  571. #endif /* DBM */
  572.     if (rver_buf != Nullch)
  573.         free(rver_buf);
  574.     }
  575.     return 0;
  576. }
  577.  
  578. int
  579. initctl()
  580. {
  581.     char *mybuf = buf;            /* place to decode rc line */
  582.     register char *s, *c, *h;
  583.     register long i;
  584.     register ART_NUM unread;
  585.     
  586. #ifdef DELAYMARK
  587.     dmcount = 0;
  588. #endif
  589.     if ((lastart = getngsize(ng)) < 0)    /* this cannot happen (laugh here) */
  590.     return -1;
  591.  
  592.     absfirst = getabsfirst(ng,lastart);    /* remember first existing article */
  593.     if (!absfirst)            /* no articles at all? */
  594.     absfirst = 1;            /* pretend there is one */
  595. #ifndef lint
  596.     ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
  597. #endif /* lint */
  598.     ctlarea = safemalloc(ctlsize);    /* allocate control area */
  599.  
  600.     /* now modify ctlarea to reflect what has already been read */
  601.  
  602.     for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
  603.                     /* find numbers in rc line */
  604.     i = strlen(s);
  605. #ifndef lint
  606.     if (i >= LBUFLEN-2)            /* bigger than buf? */
  607.     mybuf = safemalloc((MEM_SIZE)(i+2));
  608. #endif /* lint */
  609.     strcpy(mybuf,s);            /* make scratch copy of line */
  610.     mybuf[i++] = ',';            /* put extra comma on the end */
  611.     mybuf[i] = '\0';
  612.     s = mybuf;                /* initialize the for loop below */
  613. #ifdef USETHREADS
  614.     if (strnEQ(s,"1-",2)
  615.      || strnEQ(s,"0-",2)) {        /* can we save some time here? */
  616. #else
  617.     if (strnEQ(s,"1-",2)) {        /* can we save some time here? */
  618. #endif
  619.     firstbit = atol(s+2)+1;        /* ignore first range thusly */
  620.     s=index(s,',') + 1;
  621.     }
  622.     else
  623.     firstbit = 1;            /* all the bits are valid for now */
  624.     if (absfirst > firstbit) {        /* do we know already? */
  625.     firstbit = absfirst;        /* no point calling getngmin again */
  626.     }
  627.     else if (artopen(firstbit) == Nullfp) {
  628.                     /* first unread article missing? */
  629.     i = getngmin(".",firstbit);    /* see if expire has been busy */
  630.     if (i) {            /* avoid a bunch of extra opens */
  631.         firstbit = i;
  632.     }
  633.     }
  634.     firstart = firstbit;        /* firstart > firstbit in KILL */
  635. #ifdef PENDING
  636. #   ifdef CACHESUBJ
  637.     subj_to_get = firstbit;
  638. #   endif
  639. #endif
  640.     unread = lastart - firstbit + 1;    /* assume this range unread */
  641.     for (i=OFFSET(firstbit)/BITSPERBYTE; i<ctlsize; i++)
  642.     ctlarea[i] = 0;            /* assume unread */
  643. #ifdef DEBUGGING
  644.     if (debug & DEB_CTLAREA_BITMAP) {
  645.     printf("\n%s\n",mybuf) ; FLUSH;
  646.     for (i=1; i <= lastart; i++)
  647.         if (! was_read(i))
  648.         printf("%ld ",(long)i) ; FLUSH;
  649.     }
  650. #endif
  651.     for ( ; (c = index(s,',')) != Nullch; s = ++c) {
  652.                     /* for each range */
  653.     ART_NUM min, max;
  654.  
  655.     *c = '\0';            /* do not let index see past comma */
  656.     if ((h = index(s,'-')) != Nullch) {    /* is there a -? */
  657.         min = atol(s);
  658.         max = atol(h+1);
  659.         if (min < firstbit)        /* make sure range is in range */
  660.         min = firstbit;
  661.         if (max > lastart)
  662.         max = lastart;
  663.         if (min <= max)        /* non-null range? */
  664.         unread -= max - min + 1;/* adjust unread count */
  665.         for (i=min; i<=max; i++)    /* for all articles in range */
  666.         ctl_set(i);        /* mark them read */
  667.     }
  668.     else if ((i = atol(s)) >= firstbit && i <= lastart) {
  669.                     /* is single number reasonable? */
  670.         ctl_set(i);            /* mark it read */
  671.         unread--;            /* decrement articles to read */
  672.     }
  673. #ifdef DEBUGGING
  674.     if (debug & DEB_CTLAREA_BITMAP) {
  675.         printf("\n%s\n",s) ; FLUSH;
  676.         for (i=1; i <= lastart; i++)
  677.         if (! was_read(i))
  678.             printf("%ld ",(long)i) ; FLUSH;
  679.     }
  680. #endif
  681.     }
  682. #ifdef DEBUGGING
  683.     if (debug & DEB_CTLAREA_BITMAP) {
  684.     fputs("\n(hit CR)",stdout) ; FLUSH;
  685.     gets(cmd_buf);
  686.     }
  687. #endif
  688.     if (mybuf != buf)
  689.     free(mybuf);
  690.     toread[ng] = unread;
  691.     return 0;
  692. }
  693.  
  694. void
  695. grow_ctl(newlast)
  696. ART_NUM newlast;
  697. {
  698.     ART_NUM tmpfirst;
  699.     MEM_SIZE newsize;
  700.     register ART_NUM i;
  701.  
  702.     forcegrow = FALSE;
  703.     if (newlast > lastart) {
  704.     ART_NUM tmpart = art;
  705. #ifndef lint
  706.     newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
  707. #else
  708.     newsize = Null(MEM_SIZE);
  709. #endif /* lint */
  710.     if (newsize > ctlsize) {
  711.         newsize += 20;
  712.         ctlarea = saferealloc(ctlarea,newsize);
  713.         ctlsize = newsize;
  714.     }
  715.     toread[ng] += (ART_UNREAD)(newlast-lastart);
  716.     for (i=lastart+1; i<=newlast; i++)
  717.         ctl_clear(i);    /* these articles are unread */
  718. #ifdef CACHESUBJ
  719.     if (subj_list != Null(char**)) {
  720. #ifndef lint
  721.         subj_list = (char**)saferealloc((char*)subj_list,
  722.           (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
  723. #endif /* lint */
  724.         for (i=lastart+1; i<=newlast; i++)
  725.         subj_list[OFFSET(i)] = Nullch;
  726.     }
  727. #endif
  728.     tmpfirst = lastart+1;
  729.     lastart = newlast;
  730. #ifdef KILLFILES
  731. #ifdef VERBOSE
  732.     IF(verbose)
  733.         sprintf(buf,
  734.         "%ld more article%s arrived--looking for more to kill...\n\n",
  735.         (long)(lastart - tmpfirst + 1),
  736.         (lastart > tmpfirst ? "s have" : " has" ) );
  737.     ELSE            /* my, my, how clever we are */
  738. #endif
  739. #ifdef TERSE
  740.         strcpy(buf, "More news--killing...\n\n");
  741. #endif
  742.     kill_unwanted(tmpfirst,buf,TRUE);
  743. #endif
  744.     art = tmpart;
  745.     }
  746. }
  747.  
  748.