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