home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume25 / trn / part07 / bits.c next >
C/C++ Source or Header  |  1991-12-02  |  18KB  |  745 lines

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