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