1: /* 2: * This software is Copyright (c) 1986 by Rick Adams. 3: * 4: * Permission is hereby granted to copy, reproduce, redistribute or 5: * otherwise use this software as long as: there is no monetary 6: * profit gained specifically from the use or reproduction or this 7: * software, it is not sold, rented, traded or otherwise marketed, and 8: * this copyright notice is included prominently in any copy 9: * made. 10: * 11: * The author make no claims as to the fitness or correctness of 12: * this software for any use whatsoever, and it is provided as is. 13: * Any use of this software is at the user's own risk. 14: * 15: * rfuncs - functions for readnews. 16: */ 17: 18: #ifdef SCCSID 19: static char *SccsId = "@(#)rfuncs.c 2.29 3/19/86"; 20: #endif /* SCCSID */ 21: 22: /*LINTLIBRARY*/ 23: 24: #include "rparams.h" 25: 26: char lentab[LINES]; /* length of newsgroupname for each rcline */ 27: long nngsize; /* The next upcoming value of ngsize. */ 28: long nminartno; /* Smallest article number in this group */ 29: int BITMAPSIZE = 0; 30: 31: nextng() 32: { 33: long curpos; 34: #ifdef DEBUG 35: fprintf(stderr, "nextng()\n"); 36: #endif 37: curpos = ftell(actfp); 38: 39: next: 40: #ifdef DEBUG 41: fprintf(stderr, "next:\n"); 42: #endif 43: if (actdirect == BACKWARD) { 44: if (back()) { 45: (void) fseek(actfp, curpos, 0); 46: return 1; 47: } 48: if (back()) { 49: (void) fseek(actfp, curpos, 0); 50: return 1; 51: } 52: } 53: if (fgets(afline, BUFLEN, actfp) == NULL) 54: return 1; 55: if (sscanf(afline, "%s %ld %ld", bfr, &nngsize, &nminartno) < 3) { 56: bfr[0] = '\0'; 57: nngsize = 0; 58: nminartno = 0; 59: } 60: #ifdef DEBUG 61: fprintf(stderr, "bfr = '%s'\n", bfr); 62: #endif 63: 64: if (!ngmatch(bfr, header.nbuf)) 65: goto next; 66: if (xflag) 67: readmode = SPEC; 68: else 69: readmode = NEXT; 70: if (selectng(bfr, TRUE, FALSE)) 71: goto next; 72: return 0; 73: } 74: 75: 76: selectng(name, fastcheck, resubscribe) 77: char *name; 78: { 79: register char *ptr, punct = ','; 80: register int i; 81: register char *p; 82: register long cur; 83: long next = 0; 84: FILE *af; 85: long s, sm; 86: char buf[100], n[100]; 87: 88: #ifdef DEBUG 89: fprintf(stderr,"selectng: groupdir = %s\n", groupdir); 90: #endif /* DEBUG */ 91: if (*groupdir) 92: updaterc(); 93: last = 1; 94: if (strcmp(name, bfr)) { 95: af = xfopen(ACTIVE, "r"); 96: while (fgets(buf, sizeof buf, af) != NULL) { 97: if (sscanf(buf, "%s %ld %ld", n, &s, &sm) == 3 && 98: strcmp(n, name) == 0) { 99: ngsize = s; 100: minartno = sm; 101: break; 102: } 103: } 104: (void) fclose(af); 105: } else { 106: ngsize = nngsize; 107: minartno = nminartno; 108: } 109: #ifdef DEBUG 110: fprintf(stderr, "selectng(%s) sets ngsize to %ld, minartno to %ld\n", 111: name, ngsize, minartno); 112: #endif 113: (void) strcpy(groupdir, name); 114: if (!xflag) { 115: i = findrcline(name); 116: if (i >= 0) { 117: if (p = index(rcline[i], '!')) { 118: switch (resubscribe) { 119: case FALSE: 120: groupdir[0] = 0; 121: return 1; 122: case TRUE: 123: *p = ':'; 124: break; 125: case PERHAPS: 126: zapng = TRUE; 127: break; 128: } 129: } else 130: p = index(rcline[i], ':'); 131: if (!p) /* shouldn't happen */ 132: p = rcline[i]; 133: while (*++p == ' ') 134: ; 135: (void) sprintf(rcbuf, "%s%s%ld", rcline[i], 136: *p == '\0' ? " " : ",", ngsize+1); 137: } 138: else 139: (void) sprintf(rcbuf, "ng: %ld", ngsize+1); 140: } else 141: (void) sprintf(rcbuf, "ng: %ld", ngsize+1); 142: #ifdef DEBUG 143: fprintf(stderr, "rcbuf set to %s\n", rcbuf); 144: #endif /* DEBUG */ 145: 146: /* 147: * Fast check for common case: 1-### 148: */ 149: if (fastcheck) { 150: p = rcbuf; 151: while (*p != ' ') 152: p++; 153: while (*p == ' ') 154: p++; 155: if (*p++ == '1' && *p++ == '-') { 156: cur = 0; 157: while (isdigit(*p)) 158: cur = 10 * cur + *p++ - '0'; 159: if (*p == ',' && cur == ngsize) { 160: #ifdef DEBUG 161: fprintf(stderr, "Group: %s, all read\n", groupdir); 162: #endif 163: groupdir[0] = 0; 164: return 1; 165: } 166: if (cur > ngsize) { 167: /* 168: * Claim to have read articles 169: * which "active" believes have 170: * never existed - we believe "active" 171: */ 172: fprintf(stderr, 173: "%s %s...\r\n\t%s %ld to %ld\r\n", 174: "Warning: newsgroup", groupdir, 175: "last article claimed read reset from", 176: cur, ngsize); 177: } 178: } 179: } 180: 181: /* 182: * The key to understanding this piece of code is that a bit is set iff 183: * that article has NOT been read. Thus, we fill in the holes when 184: * commas are found (e.g. 1-20,30-35 will result in filling in the 21-29 185: * holes), and so we assume the newsrc file is properly ordered, the way 186: * we write it out. 187: */ 188: if ((ngsize-minartno) > BITMAPSIZE) { 189: /* This should never happen */ 190: (void) xerror("Bitmap not large enough for newsgroup %s", groupdir); 191: } 192: 193: cur = 0; 194: bzero(bitmap, (int) (ngsize-minartno)/8+1); /* 8 bits per character */ 195: 196: /* Decode the .newsrc line indicating what we have read. */ 197: for (ptr = rcbuf; *ptr && *ptr != ':'; ptr++) 198: ; 199: while (*ptr) { 200: while (!isdigit(*ptr) && *ptr) 201: ptr++; 202: if (!*ptr) 203: break; 204: (void) sscanf(ptr, "%ld", &next); 205: if (punct == ',') { 206: while (++cur < next) { 207: set(cur); 208: } 209: } 210: cur = next; 211: while (!ispunct(*ptr) && *ptr) 212: ptr++; 213: punct = *ptr; 214: } 215: if (rflag) 216: bit = ngsize+1; 217: else 218: bit = minartno -1; 219: nextbit(); 220: ngrp = 1; 221: return 0; 222: } 223: 224: #ifdef TMAIL 225: catchterm() 226: { 227: (void) unlink(infile); 228: (void) unlink(outfile); 229: xxit(0); 230: } 231: 232: 233: /* 234: * The -M (Mail) interface. This code is a reasonably simple model for 235: * writing other interfaces. We write out all relevant articles to 236: * a temp file, then invoke Mail with an option to have it tell us which 237: * articles it read. Finally we count those articles as really read. 238: */ 239: Mail() 240: { 241: register FILE *fp = NULL, *ofp; 242: struct hbuf h; 243: register char *ptr, *fname; 244: int news = 0; 245: register int i; 246: 247: for(i=0;i<NUNREC;i++) 248: h.unrec[i] = NULL; 249: 250: ofp = xfopen(mktemp(outfile), "w"); 251: if (aflag && *datebuf) 252: if ((atime = cgtdate(datebuf)) == -1) 253: xerror("Cannot parse date string"); 254: while (!nextng()) 255: while (bit <= ngsize) { 256: (void) sprintf(filename, "%s/%ld", dirname(groupdir), bit); 257: if (access(filename, 4) 258: || ((fp = fopen(filename, "r")) == NULL) 259: || (hread(&h, fp, TRUE) == NULL) 260: || !aselect(&h, FALSE)) { 261: #ifdef DEBUG 262: fprintf(stderr, "Bad article '%s'\n", filename); 263: #endif 264: if (fp != NULL) { 265: (void) fclose(fp); 266: fp = NULL; 267: } 268: clear(bit); 269: nextbit(); 270: continue; 271: } 272: fname = ptr = index(h.from, '('); 273: if (fname) { 274: while (ptr && ptr[-1] == ' ') 275: ptr--; 276: if (ptr) 277: *ptr = 0; 278: fname++; 279: ptr = fname + strlen(fname) - 1; 280: if (*ptr == ')') 281: *ptr = 0; 282: } 283: h.subtime = cgtdate(h.subdate); 284: fprintf(ofp, "From %s %s", 285: #ifdef INTERNET 286: h.from[0] ? h.from : 287: #endif 288: h.path, ctime(&h.subtime)); 289: if (fname) 290: fprintf(ofp, "Full-Name: %s\n", fname); 291: fprintf(ofp, "Newsgroups: %s\n", h.nbuf); 292: fprintf(ofp, "Subject: %s\n", h.title); 293: fprintf(ofp, "Article-ID: %s/%ld\n\n", groupdir, bit); 294: tprint(fp, ofp, TRUE); 295: putc('\n', ofp); 296: news = TRUE; 297: (void) fclose(fp); 298: fp = NULL; 299: nextbit(); 300: } 301: updaterc(); 302: (void) fclose(ofp); 303: if (!news) { 304: if (!checkngs()) 305: fprintf(stderr, "No news.\n"); 306: (void) unlink(outfile); 307: return; 308: } 309: (void) signal(SIGHUP, catchterm); 310: (void) signal(SIGTERM, catchterm); 311: (void) sprintf(bfr, "%s -f %s -T %s", TMAIL, outfile, mktemp(infile)); 312: fwait(fsubr(ushell, bfr, (char *)NULL)); 313: ofp = xfopen(infile, "r"); 314: (void) fseek(actfp, 0L, 0); 315: while (fgets(afline, BUFLEN, actfp) != NULL) { 316: last = 0; 317: if (sscanf(afline, "%s %ld", bfr, &nngsize) < 2) { 318: bfr[0] = '\0'; 319: nngsize = 0; 320: } 321: if (!ngmatch(bfr, header.nbuf)) 322: continue; 323: *groupdir = 0; 324: if (selectng(bfr, TRUE, FALSE)) 325: continue; 326: (void) fseek(ofp, 0L, 0); 327: while (fgets(groupdir, BUFLEN, ofp) != NULL) { 328: (void) nstrip(groupdir); 329: ptr = index(groupdir, '/'); 330: *ptr = 0; 331: if (strcmp(bfr, groupdir)) 332: continue; 333: (void) sscanf(++ptr, "%ld", &last); 334: clear(last); 335: } 336: if (last) { 337: (void) strcpy(groupdir, bfr); 338: updaterc(); 339: } 340: } 341: (void) unlink(infile); 342: (void) unlink(outfile); 343: } 344: #endif 345: 346: updaterc() 347: { 348: register long cur = 1, next = 1; 349: register int i; 350: register char *ptr; 351: char oldptr; 352: 353: sprintf(rcbuf, "%s%c ", groupdir, zapng ? '!' : ':'); 354: 355: zapng = FALSE; 356: again: 357: ptr = &rcbuf[strlen(rcbuf)]; 358: while (get(next) && next <= ngsize) 359: next++; 360: cur = next; 361: while (!(get(next)) && next <= ngsize) 362: next++; 363: if (cur == next) { 364: next = ngsize + 1; 365: goto skip; 366: } 367: if (ptr[-1] != ' ') 368: *ptr++ = ','; 369: if (cur + 1 == next) 370: (void) sprintf(ptr, "%ld", cur); 371: else 372: (void) sprintf(ptr, "%ld-%ld", cur, next - 1); 373: skip: 374: if ((long) next > ngsize) { 375: if (strpbrk(rcbuf, ":!") == NULL) /* bad line, huh?? */ 376: return; 377: ptr = index(rcbuf, ' '); 378: if (ptr == NULL) /* impossible */ 379: return; 380: ptr--; 381: oldptr = *ptr; 382: ptr[0] = ':'; 383: ptr[1] = '\0'; 384: i = findrcline(groupdir); 385: if (i >= 0) { 386: ptr[0] = oldptr; 387: ptr[1] = ' '; 388: rcline[i] = realloc(rcline[i], (unsigned)(strlen(rcbuf) + 1)); 389: if (rcline[i] == NULL) 390: xerror("Cannot realloc"); 391: (void) strcpy(rcline[i], rcbuf); 392: #ifdef DEBUG 393: fprintf(stderr," new rcline = %s\n", rcline[i]); 394: #endif /* DEBUG */ 395: return; 396: } 397: if (++line > LINES) 398: xerror("Too many newsgroups"); 399: ptr[0] = oldptr; 400: ptr[1] = ' '; 401: if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL) 402: xerror("Not enough memory"); 403: (void) strcpy(rcline[line], rcbuf); 404: #ifdef DEBUG 405: fprintf(stderr," new rcline2 = %s\n", rcline[line]); 406: #endif /* DEBUG */ 407: return; 408: } 409: cur = next; 410: goto again; 411: } 412: 413: newrc(rcname) 414: char *rcname; 415: { 416: register FILE *fp; 417: 418: if (close(creat(rcname, 0666))) { 419: (void) sprintf(bfr, "Cannot create %s", newsrc); 420: xerror(bfr); 421: } 422: 423: sprintf(bfr, "%s/users", LIB); 424: if ((fp = fopen(bfr, "a")) != NULL) { 425: fprintf(fp, "%s\n", username); 426: (void) fclose(fp); 427: (void) chmod(bfr, 0666); 428: } 429: } 430: 431: nextbit() 432: { 433: #ifdef DEBUG 434: fprintf(stderr,"nextbit() bit = %ld\n", bit); 435: #endif /* DEBUG */ 436: last = bit; 437: if (readmode == SPEC || xflag) { 438: if (rflag) 439: bit--; 440: else 441: bit++; 442: return; 443: } 444: if (rflag) 445: while (--bit, !get(bit) && bit > minartno) 446: ; 447: else 448: while (++bit, !get(bit) && bit <= ngsize) 449: ; 450: #ifdef DEBUG 451: fprintf(stderr,"nextng leaves bit as %ld\n", bit); 452: #endif /* DEBUG */ 453: } 454: 455: /* 456: * Return TRUE if the user has not ruled out this article. 457: */ 458: aselect(hp, insist) 459: register struct hbuf *hp; 460: int insist; 461: { 462: if (insist) 463: return TRUE; 464: if (tflag && !titmat(hp, header.title)) 465: return FALSE; 466: if (aflag && cgtdate(hp->subdate) < atime) 467: return FALSE; 468: if (index(hp->nbuf, ',') && !rightgroup(hp)) 469: return FALSE; 470: if (fflag && (hp->followid[0] || prefix(hp->title, "Re:"))) 471: return FALSE; 472: return TRUE; 473: } 474: 475: /* 476: * Code to avoid showing multiple articles for news. 477: * Works even if you exit news. 478: * Returns nonzero if we should show this article. 479: */ 480: rightgroup(hp) 481: struct hbuf *hp; 482: { 483: char ng[BUFLEN]; 484: register char *p, *g; 485: int i, flag; 486: 487: strcpy(ng, hp->nbuf); 488: g = ng; 489: flag = 1; 490: while (g != NULL) { 491: p = index(g, ','); 492: if (p != NULL) { 493: *p++ = '\0'; 494: while (*p == ' ') 495: p++; 496: } 497: if (strcmp(g, groupdir) == 0) 498: return flag; 499: if (ngmatch(g, header.nbuf) 500: && ((i = findrcline(g)) >= 0 501: && index(rcline[i], '!') == NULL)) 502: flag = 0; 503: g = p; 504: } 505: /* we must be in "junk" or "control" */ 506: return TRUE; 507: } 508: 509: back() 510: { 511: while (fseek(actfp, -2L, 1) != -1 && ftell(actfp) > 0L) { 512: if (getc(actfp) == '\n') 513: return 0; 514: } 515: if (ftell(actfp) == 0L) 516: return 0; 517: return 1; 518: } 519: 520: /* 521: * Trap interrupts. 522: */ 523: onsig(n) 524: int n; 525: { 526: (void) signal(n, onsig); 527: SigTrap = n; 528: if (rcreadok < 2) { 529: fprintf(stderr, "Aborted early\n"); 530: xxit(0); 531: } 532: } 533: 534: /* 535: * finds the line in your .newsrc file (actually the in-core "rcline" 536: * copy of it) and returns the index into the array where it was found. 537: * -1 means it didn't find it. 538: * 539: * We play clever games here to make this faster. It's inherently 540: * quadratic - we spend lots of CPU time here because we search through 541: * the whole .newsrc for each line. The "prev" variable remembers where 542: * the last match was found; we start the search there and loop around 543: * to the beginning, in the hopes that the calls will be roughly in order. 544: */ 545: int 546: findrcline(name) 547: register char *name; 548: { 549: register char * p; 550: register int i; 551: register int top; 552: register int len; 553: static int prev; 554: static int didthru; 555: 556: for ( ; didthru <= line; ++didthru) 557: if ((p = index(rcline[didthru], '!')) != 0 || 558: (p = index(rcline[didthru], ':')) != 0) { 559: lentab[didthru] = (int)(p - rcline[didthru]); 560: } 561: len = strlen(name); 562: top = line; 563: i = prev; 564: loop: 565: for ( ; i <= top; ++i) 566: if (lentab[i] == len && rcline[i] != NULL && 567: strncmp(name, rcline[i], len) == 0) 568: return prev = i; 569: if (i > line && line > prev - 1) { 570: i = 0; 571: top = prev - 1; 572: goto loop; 573: } 574: return -1; 575: } 576: 577: /* 578: * sortactive - make a local copy of the active file, sorted according 579: * to the user's preferences, according to his .newsrc file. 580: */ 581: 582: struct table_elt { 583: int rcindex; 584: long maxart, minart; 585: char yn; 586: }; 587: 588: #ifdef SORTACTIVE 589: static int 590: rcsort(a, b) 591: char *a, *b; 592: { 593: return(((struct table_elt *)a)->rcindex - 594: ((struct table_elt *)b)->rcindex); 595: } 596: 597: static char *newactivename = "/tmp/newsaXXXXXX"; 598: #endif /* SORTACTIVE */ 599: 600: sortactive() 601: { 602: register struct table_elt *tp; 603: register char *p; 604: register FILE *nfp, *afp; 605: char aline[BUFLEN], ngname[BUFLEN]; 606: struct table_elt table[LINES]; 607: int nlines = 0, i, delta, lastline; 608: 609: #ifdef SORTACTIVE 610: /* make a new sorted copy of ACTIVE */ 611: nfp = fopen(mktemp(newactivename), "w"); 612: (void) chmod(newactivename, 0600); 613: if (nfp == NULL) { 614: perror(newactivename); 615: return; 616: } 617: 618: /* look up all the lines in ACTIVE, finding their positions in .newsrc */ 619: p = ACTIVE; 620: ACTIVE = newactivename; 621: afp = xfopen(p, "r"); 622: tp = table; 623: #else /* !SORTACTIVE */ 624: afp = xfopen(ACTIVE, "r"); 625: #endif /* !SORTACTIVE */ 626: while (fgets(aline, sizeof aline, afp) != NULL) { 627: if (sscanf(aline,"%s %ld %ld %c", ngname, &tp->maxart, &tp->minart, &tp->yn) != 4) 628: xerror("Active file corrupt"); 629: delta = tp->maxart - tp->minart; 630: if (delta >= BITMAPSIZE) 631: BITMAPSIZE = delta+ 1; 632: #ifdef SORTACTIVE 633: tp->rcindex = findrcline(ngname); 634: if (tp->rcindex < 0) { 635: register FILE *f; 636: /* it's not in his .newsrc, maybe it's aliased? */ 637: f = xfopen(ALIASES,"r"); 638: while (fscanf(f,"%s %s", afline, bfr) == 2 639: && strcmp(ngname, bfr)) 640: ; 641: (void) fclose(f); 642: if (strcmp(ngname, bfr) == 0) { 643: int j; 644: j = findrcline(afline); 645: if (j >= 0) { 646: p = rcline[j]; 647: while (*p != ':' && *p != '!' && *p) 648: p++; 649: strcat(bfr, p); 650: rcline[j] = realloc(rcline[j], (unsigned)(strlen(bfr)+1)); 651: if (rcline[j] == NULL) 652: xerror("Not enough memory"); 653: strcpy(rcline[j], bfr); 654: tp++->rcindex = j; 655: continue; 656: } 657: } 658: if (++line > LINES) 659: xerror("Too many newsgroups"); 660: strcat(ngname, ":"); 661: rcline[line] = malloc((unsigned)(strlen(ngname) + 1)); 662: if (rcline[line] == NULL) 663: xerror("Not enough memory"); 664: strcpy(rcline[line], ngname); 665: tp->rcindex = line; 666: } 667: tp++; 668: #endif /* SORTACTIVE */ 669: } 670: (void) fclose(afp); 671: BITMAPSIZE = 8 * ((BITMAPSIZE+7) / 8); 672: bitmap = malloc((unsigned)BITMAPSIZE/8); 673: if (bitmap == NULL) 674: xerror("Can't malloc bitmap"); 675: 676: #ifdef SORTACTIVE 677: /* sort by position in user's .newsrc file (new groups come up last) */ 678: nlines = tp - table; 679: qsort((char *)table, nlines, sizeof table[0], rcsort); 680: 681: tp = table; 682: lastline = tp->rcindex - 1; 683: /* copy active to newactive, in the new order */ 684: for (i = 0; i < nlines; i++) { 685: while (++lastline < tp->rcindex) { 686: fprintf(stderr, "Duplicate .newsrc line or bad group %s\n", 687: rcline[lastline]); 688: lentab[lastline] = 0; 689: free(rcline[lastline]); 690: rcline[lastline] = NULL; 691: } 692: if (rcline[tp->rcindex] == NULL) 693: continue; 694: p = rcline[tp->rcindex]; 695: while (*p != ':' && *p != '!') 696: fputc(*p++, nfp); 697: (void) fprintf(nfp, " %ld %ld %c\n", tp->maxart, tp->minart, 698: tp->yn); 699: tp++; 700: } 701: (void) fclose(nfp); 702: #endif /* SORTACTIVE */ 703: } 704: 705: /* ARGSUSED */ 706: checkngs(nbuf, f) 707: char *nbuf; 708: FILE *f; 709: { 710: return 0; 711: }