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: * expire - expire daemon runs around and nails all articles that 16: * have expired. 17: */ 18: 19: #ifdef SCCSID 20: static char *SccsId = "@(#)expire.c 2.43 3/19/86"; 21: #endif /* SCCSID */ 22: 23: #include "params.h" 24: #include <errno.h> 25: #if defined(BSD4_2) || defined(BSD4_1C) 26: # include <sys/dir.h> 27: #else 28: # include "ndir.h" 29: #endif 30: 31: char *Progname = "expire"; /* used by xerror to identify failing program */ 32: 33: /* Number of array entries to allocate at a time. */ 34: #define SPACE_INCREMENT 1000 35: 36: struct expdata { 37: char *e_name; 38: long e_min, e_max; 39: time_t e_droptime, e_expiretime; 40: char e_ignorexp; 41: char e_doarchive; 42: char e_doexpire; 43: }; 44: 45: extern int errno; 46: char NARTFILE[BUFLEN], OARTFILE[BUFLEN]; 47: char PAGFILE[BUFLEN], DIRFILE[BUFLEN]; 48: char NACTIVE[BUFLEN], OACTIVE[BUFLEN]; 49: char recdate[BUFLEN]; 50: long rectime, exptime; 51: extern char *OLDNEWS; 52: int verbose = 0; 53: int ignorexp = 0; 54: int doarchive = 0; 55: int nohistory = 0; 56: int dorebuild = 0; 57: int usepost = 0; 58: int frflag = 0; 59: int updateactive = 0; 60: char baduser[BUFLEN]; 61: extern char filename[], nbuf[]; 62: 63: /* 64: * This code uses realloc to get more of the multhist array. 65: */ 66: struct multhist { 67: char *mh_ident; 68: char *mh_file; 69: } *multhist; 70: unsigned int mh_size; 71: char *calloc(); 72: char *realloc(); 73: 74: typedef struct { 75: char *dptr; 76: int dsize; 77: } datum; 78: 79: long expincr; 80: long dropincr; 81: long atol(); 82: time_t cgtdate(), time(); 83: FILE *popen(); 84: struct passwd *pw; 85: struct group *gp; 86: char arpat[LBUFLEN]; 87: int arpatlen = 0; 88: char ngpat[LBUFLEN]; 89: int ngpatlen = 0; 90: char afline[BUFLEN]; 91: char grpsleft[BUFLEN]; 92: struct hbuf h; 93: 94: main(argc, argv) 95: int argc; 96: char **argv; 97: { 98: register char *p1, *p2, *p3; 99: register time_t now, newtime; 100: register FILE *fp = NULL; 101: FILE *ohfd, *nhfd; 102: DIR *ngdirp = NULL; 103: static struct direct *ngdir; 104: char fn[BUFLEN]; 105: int i; 106: #ifndef DBM 107: char *ptr, chr; 108: FILE *subfd[10]; 109: char *histfile(); 110: #endif /* !DBM */ 111: 112: pathinit(); 113: (void) umask(N_UMASK); 114: 115: /* 116: * Try to run as NEWSUSR/NEWSGRP 117: */ 118: if ((pw = getpwnam(NEWSUSR)) == NULL) 119: xerror("Cannot get NEWSUSR pw entry"); 120: 121: uid = pw->pw_uid; 122: if ((gp = getgrnam(NEWSGRP)) == NULL) 123: xerror("Cannot get NEWSGRP gr entry"); 124: gid = gp->gr_gid; 125: (void) setgid(gid); 126: (void) setuid(uid); 127: 128: expincr = DFLTEXP; 129: dropincr = HISTEXP; 130: ngpat[0] = ','; 131: arpat[0] = ','; 132: while (argc > 1) { 133: switch (argv[1][1]) { 134: case 'v': 135: if (isdigit(argv[1][2])) 136: verbose = argv[1][2] - '0'; 137: else if (argc > 2 && argv[2][0] != '-') { 138: 139: argv++; 140: argc--; 141: verbose = atoi(argv[1]); 142: } else 143: verbose = 1; 144: if (verbose < 3) 145: setbuf(stdout, (char *)NULL); 146: break; 147: case 'e': /* Use this as default expiration time */ 148: if (argc > 2 && argv[2][0] != '-') { 149: argv++; 150: argc--; 151: expincr = atol(argv[1]) * DAYS; 152: } else if (isdigit(argv[1][2])) 153: expincr = atol(&argv[1][2]) * DAYS; 154: break; 155: case 'E': /* Use this as default forget time */ 156: if (argc > 2 && argv[2][0] != '-') { 157: argv++; 158: argc--; 159: dropincr = atol(argv[1]) * DAYS; 160: } else if (isdigit(argv[1][2])) 161: dropincr = atol(&argv[1][2]) * DAYS; 162: if (dropincr < expincr) { 163: dropincr = HISTEXP; 164: fprintf(stderr, "History expiration time < article expiration time. Default used.\n"); 165: } 166: break; 167: case 'I': /* Ignore any existing expiration date */ 168: ignorexp = 2; 169: break; 170: case 'i': /* Ignore any existing expiration date */ 171: ignorexp = 1; 172: break; 173: case 'n': 174: if (argc > 2) { 175: argv++; 176: argc--; 177: while (argc > 1 && argv[1][0] != '-') { 178: int argvlen; 179: argvlen = strlen(argv[1]); 180: if (ngpatlen + argvlen + 2 > sizeof (ngpat)) { 181: xerror("Too many groups specified for -n\n"); 182: } 183: if (ngpat[ngpatlen] == '\0') { 184: ngpat[ngpatlen++] = ','; 185: ngpat[ngpatlen] = '\0'; 186: } 187: strcpy(&ngpat[ngpatlen], argv[1]); 188: ngpatlen += argvlen; 189: argv++; 190: argc--; 191: } 192: argv--; 193: argc++; 194: } 195: break; 196: case 'a': /* archive expired articles */ 197: if (access(OLDNEWS,0) < 0){ 198: perror(OLDNEWS); 199: xerror("No archiving possible\n"); 200: } 201: doarchive++; 202: if (argc > 2) { 203: argv++; 204: argc--; 205: while (argc > 1 && argv[1][0] != '-') { 206: int argvlen; 207: argvlen = strlen(argv[1]); 208: if (arpatlen + argvlen + 2 > sizeof (arpat)) { 209: xerror("Too many groups specified for -a\n"); 210: } 211: if (arpat[arpatlen] == '\0') { 212: arpat[arpatlen++] = ','; 213: arpat[arpatlen] = '\0'; 214: } 215: strcpy(&arpat[arpatlen], argv[1]); 216: arpatlen += argvlen; 217: argv++; 218: argc--; 219: } 220: argv--; 221: argc++; 222: } 223: break; 224: case 'h': /* ignore history */ 225: nohistory++; 226: break; 227: case 'r': /* rebuild history file */ 228: dorebuild++; 229: nohistory++; 230: break; 231: case 'p': 232: usepost++; 233: break; 234: case 'f': 235: frflag++; 236: if (argc > 2) { 237: strcpy(baduser, argv[2]); 238: argv++; 239: argc--; 240: } 241: break; 242: case 'u': 243: updateactive++; 244: break; 245: default: 246: printf("Usage: expire [ -v [level] ] [-e days ] [-i] [-a] [-r] [-h] [-p] [-u] [-f username] [-n newsgroups]\n"); 247: xxit(1); 248: } 249: argc--; 250: argv++; 251: } 252: if (ngpat[0] == ',') 253: (void) strcpy(ngpat, "all,"); 254: if (arpat[0] == ',') 255: (void) strcpy(arpat, "all,"); 256: now = time((time_t)0); 257: if (chdir(SPOOL)) 258: xerror("Cannot chdir %s", SPOOL); 259: 260: if (verbose) { 261: printf("expire: nohistory %d, rebuild %d, doarchive %d\n", 262: nohistory, dorebuild, doarchive); 263: printf("newsgroups: %s\n",ngpat); 264: if (doarchive) 265: printf("archiving: %s\n",arpat); 266: } 267: 268: (void) sprintf(OARTFILE, "%s/%s", LIB, "ohistory"); 269: (void) sprintf(NARTFILE, "%s/%s", LIB, "nhistory"); 270: 271: (void) sprintf(OACTIVE, "%s/%s", LIB, "oactive"); 272: (void) sprintf(NACTIVE, "%s/%s", LIB, "nactive"); 273: 274: if (updateactive) 275: goto doupdateactive; 276: 277: #ifdef DBM 278: if (!dorebuild) { 279: (void) sprintf(PAGFILE, "%s/%s", LIB, "nhistory.pag"); 280: (void) sprintf(DIRFILE, "%s/%s", LIB, "nhistory.dir"); 281: (void) close(creat(PAGFILE, 0666)); 282: (void) close(creat(DIRFILE, 0666)); 283: initdbm(NARTFILE); 284: } 285: #endif 286: 287: if (nohistory) { 288: ohfd = xfopen(ACTIVE, "r"); 289: if (dorebuild) { 290: /* Allocate initial space for multiple newsgroup (for an 291: article) array */ 292: multhist = (struct multhist *)calloc (SPACE_INCREMENT, 293: sizeof (struct multhist)); 294: mh_size = SPACE_INCREMENT; 295: 296: (void) sprintf(afline, "exec sort -t\t +1.6 -2 +1 >%s", NARTFILE); 297: if ((nhfd = popen(afline, "w")) == NULL) 298: xerror("Cannot exec %s", afline); 299: } else 300: nhfd = xfopen("/dev/null", "w"); 301: } else { 302: ohfd = xfopen(ARTFILE, "r"); 303: nhfd = xfopen(NARTFILE, "w"); 304: } 305: 306: for(i=0;i<NUNREC;i++) 307: h.unrec[i] = NULL; 308: 309: while (TRUE) { 310: fp = NULL; 311: if (nohistory) { 312: do { 313: if (ngdir == NULL) { 314: if ( ngdirp != NULL ) 315: closedir(ngdirp); 316: if (fgets(afline, BUFLEN, ohfd) == NULL) 317: goto out; 318: (void) strcpy(nbuf, afline); 319: p1 = index(nbuf, ' '); 320: if (p1 == NULL) 321: p1 = index(nbuf, '\n'); 322: if (p1 != NULL) 323: *p1 = NULL; 324: if (!ngmatch(nbuf, ngpat)) 325: continue; 326: 327: /* Change a group name from 328: a.b.c to a/b/c */ 329: for (p1=nbuf; *p1; p1++) 330: if (*p1 == '.') 331: *p1 = '/'; 332: 333: if ((ngdirp = opendir(nbuf)) == NULL) 334: continue; 335: 336: } 337: ngdir = readdir(ngdirp); 338: /* Continue looking if not an article. */ 339: } while (ngdir == NULL || !islegal(fn,nbuf,ngdir->d_name)); 340: 341: p2 = fn; 342: if (verbose > 2) 343: printf("article: %s\n", fn); 344: strcpy(filename, dirname(fn)); 345: fp = access(filename, 04) ? NULL : fopen(filename, "r"); 346: } else { 347: char dc; 348: if (fgets(afline, BUFLEN, ohfd) == NULL) 349: break; 350: if (verbose > 2) 351: printf("article: %s", afline); 352: p1 = index(afline, '\t'); 353: if (!p1) 354: continue; 355: *p1 = '\0'; 356: (void) strcpy(h.ident, afline); 357: *p1 = '\t'; 358: p2 = index(p1 + 1, '\t'); 359: if (!p2) 360: continue; 361: *p2 = '\0'; 362: (void) strcpy(recdate, p1+1); 363: rectime = cgtdate(recdate); 364: *p2++ = '\t'; 365: (void) strcpy(nbuf, p2); 366: p3 = index(nbuf, '/'); 367: if (p3) { 368: register char *p4; 369: 370: p4 = index(p3, '\n'); 371: if (p4) { 372: while (p4[-1] == ' ') 373: p4--; 374: *p4 = '\0'; 375: } 376: 377: /* 378: * convert list of newsgroups from 379: * ng1/num ng2/num ... 380: * to 381: * ng1,ng2,... 382: */ 383: p4 = p3; 384: do { 385: *p3++ = NGDELIM; 386: while (*p4 != '\0' && *p4 != ' ') 387: p4++; 388: if (*p4++ == '\0') { 389: *--p3 = '\0'; 390: break; 391: } 392: while (*p3 = *p4++) { 393: if (*p3 == '/') 394: break; 395: else 396: p3++; 397: } 398: } while (*p3); 399: } else { 400: /* 401: * Nothing after the 2nd tab. This happens 402: * when there's no message left in the spool 403: * directory, only the memory of it in the 404: * history file. Use date in the history file 405: * to decide if we should keep this article. 406: */ 407: grpsleft[0] = '\0'; 408: goto checkdate; 409: } 410: if (!ngmatch(nbuf, ngpat) || 411: ((rectime+expincr > now) && !dorebuild && !frflag 412: && !usepost && recdate[0] != ' ')) 413: goto keephist; 414: if (recdate[0] != ' ') { 415: grpsleft[0] = '\0'; 416: goto nailit; /* just expire it */ 417: } 418: 419: /* 420: * Look for the file--possibly several times, 421: * if it was posted to several news groups. 422: */ 423: dc = ' '; 424: p3 = p2; 425: while (dc != '\n') { 426: p1 = index(p3, ' '); 427: if (p1) { 428: dc = ' '; 429: *p1 = '\0'; 430: } else { 431: p1 = index(p3, '\n'); 432: if (p1 && p1 > p3) { 433: dc = '\n'; 434: *p1 = '\0'; 435: } else { 436: fp = NULL; 437: break; 438: } 439: } 440: strcpy(filename, dirname(p3)); 441: if (access(filename, 4) == 0 && 442: ((fp=fopen(filename, "r")) != NULL)) 443: break; 444: p3 = p1 + 1; 445: } 446: if (p1) 447: *p1 = dc; 448: } 449: 450: if (fp == NULL) { 451: /* 452: * this probably means that the article has been 453: * cancelled. Lets assume that, and make an 454: * entry in the history file to that effect. 455: */ 456: if (verbose) 457: perror(filename); 458: strcpy(p2, "cancelled\n"); 459: grpsleft[0] = '\0'; 460: goto checkdate; 461: } 462: for(i=0; i<NUNREC; i++) 463: if (h.unrec[i] != NULL) 464: free(h.unrec[i]); 465: else 466: break; 467: if (!hread(&h, fp, TRUE)) { 468: printf("Garbled article %s.\n", filename); 469: (void) fclose(fp); 470: /* 471: * Usually means disk ran out of space. 472: * Drop this article from our history file 473: * completely, so we have a chance of picking 474: * it up again from another feed .. 475: */ 476: goto nailit; 477: } 478: if (dorebuild) { 479: register char *cp, *lastslash; 480: register struct multhist *mhp; 481: 482: if (recdate[0] == '\0') { 483: struct stat statb; 484: if (fstat(fileno(fp), &statb) < 0) 485: rectime = cgtdate(h.subdate); 486: else 487: rectime = statb.st_mtime; 488: } else 489: rectime = cgtdate(recdate); 490: /* 491: * Format of filename until now was /SPOOL/a/b/c/4 492: * and this code changes it to a.b.c/4 (the correct 493: * kind of entry in the history file.) 494: * 495: * This can't be a strcpy because the addresses overlap 496: * and some machines can't handle that. 497: */ 498: p1 = filename; 499: cp = p1 + strlen(SPOOL); 500: while (*++cp) { 501: if (*cp == '/') { 502: lastslash = p1; 503: *p1++ = '.'; 504: } else 505: *p1++ = *cp; 506: } 507: *p1 = '\0'; 508: *lastslash = '/'; 509: 510: if ((cp = index(h.nbuf, NGDELIM)) == NULL) { 511: struct tm *tm; 512: saveit: 513: tm = localtime(&rectime); 514: #ifdef USG 515: fprintf(nhfd,"%s\t%s%2.2d/%2.2d/%d %2.2d:%2.2d\t%s\n", 516: #else /* !USG */ 517: fprintf(nhfd,"%s\t%s%02d/%02d/%d %02d:%02d\t%s\n", 518: #endif /* !USG */ 519: h.ident, h.expdate[0] ? " " : "", 520: tm->tm_mon+1, tm->tm_mday, tm->tm_year, 521: tm->tm_hour, tm->tm_min, filename); 522: (void) fclose(fp); 523: continue; 524: } 525: for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++) { 526: if (mhp->mh_file == NULL) 527: continue; 528: if (strcmp(mhp->mh_ident, h.ident)) 529: continue; 530: (void) strcat(filename, " "); 531: (void) strcat(filename, mhp->mh_file); 532: free(mhp->mh_file); 533: mhp->mh_file = NULL; 534: /* 535: * if we have all the links, write to hist now 536: */ 537: if (chrcnt(filename, ' ') == chrcnt(cp,NGDELIM)) 538: goto saveit; 539: break; 540: } 541: 542: /* 543: * Here is where we realloc the multhist space rather 544: * than the old way of static allocation. It's 545: * really trivial. We just clear out the space 546: * in case it was reused. The old static array was 547: * guaranteed to be cleared since it was cleared when 548: * the process started. 549: */ 550: if (mhp >= multhist + mh_size) { 551: multhist = (struct multhist *) 552: realloc ((char *)multhist, 553: sizeof (struct multhist) * 554: (SPACE_INCREMENT + mh_size)); 555: if (multhist == NULL) 556: xerror("Too many articles with multiple newsgroups"); 557: for (mhp = multhist + mh_size; 558: mhp < multhist+mh_size+SPACE_INCREMENT; 559: mhp++) { 560: mhp->mh_ident = NULL; 561: mhp->mh_file = NULL; 562: } 563: mhp = multhist + mh_size; 564: mh_size += SPACE_INCREMENT; 565: } 566: 567: if (mhp->mh_ident == NULL) { 568: mhp->mh_ident = malloc(strlen(h.ident)+1); 569: (void) strcpy(mhp->mh_ident, h.ident); 570: } 571: cp = malloc(strlen(filename) + 1); 572: if (cp == NULL) 573: xerror("Out of memory"); 574: (void) strcpy(cp, filename); 575: mhp->mh_file = cp; 576: (void) fclose(fp); 577: continue; 578: } 579: 580: (void) fclose(fp); 581: rectime = cgtdate(recdate); 582: 583: if (h.expdate[0]) 584: exptime = cgtdate(h.expdate); 585: newtime = (usepost ? cgtdate(h.subdate) : rectime) + expincr; 586: if (!h.expdate[0] || ignorexp == 2 || 587: (ignorexp == 1 && newtime < exptime)) 588: exptime = newtime; 589: if (frflag ? strcmp(baduser,h.from)==0 : now >= exptime) { 590: nailit: 591: #ifdef DEBUG 592: printf("cancel %s\n", filename); 593: #else /* !DEBUG */ 594: if (verbose) 595: printf("cancel %s\n", h.ident); 596: ulall(p2, &h); 597: (void) sprintf(p2, "%s\n", grpsleft); 598: if (verbose > 2 && grpsleft[0]) 599: printf("Some good in %s\n", h.ident); 600: #endif /* !DEBUG */ 601: } else { 602: if (verbose > 2) 603: printf("Good article %s\n", h.ident); 604: grpsleft[0] = '!'; 605: } 606: 607: checkdate: 608: if (grpsleft[0] == '\0' && now >= rectime + dropincr) { 609: if (verbose > 3) 610: printf("Drop history of %s - %s\n", 611: h.ident, recdate); 612: } else { 613: long hpos; 614: keephist: 615: hpos = ftell(nhfd); 616: 617: if (verbose > 3) 618: printf("Retain history of %s - %s\n", 619: h.ident, recdate); 620: if (fputs(afline, nhfd) == EOF) 621: xerror("history write failed"); 622: #ifdef DBM 623: if (!dorebuild) 624: remember(h.ident, hpos); 625: #endif /* DBM */ 626: } 627: } 628: out: 629: if (dorebuild) { 630: register struct multhist *mhp; 631: struct tm *tm; 632: for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++) 633: if (mhp->mh_file != NULL) { 634: if (verbose) 635: printf("Article: %s [%s] Cannot find all links\n", mhp->mh_ident, mhp->mh_file); 636: (void) sprintf(filename,"%s/%s",SPOOL,mhp->mh_file); 637: for (p1 = filename; *p1 != ' ' && *p1 != '\0'; p1++) 638: if (*p1 == '.') 639: *p1 = '/'; 640: *p1 = '\0'; 641: if ((fp = fopen(filename, "r")) == NULL) { 642: if (verbose) 643: printf("Can't open %s.\n", filename); 644: continue; 645: } 646: if (!hread(&h, fp, TRUE)) { 647: printf("Garbled article %s.\n", filename); 648: (void) fclose(fp); 649: continue; 650: } else { 651: struct stat statb; 652: if (fstat(fileno(fp), &statb) < 0) 653: rectime = cgtdate(h.subdate); 654: else 655: rectime = statb.st_mtime; 656: } 657: tm = localtime(&rectime); 658: if ( 659: #ifdef USG 660: fprintf(nhfd,"%s\t%s%2.2d/%2.2d/%d %2.2d:%2.2d\t%s\n", 661: #else /* !USG */ 662: fprintf(nhfd,"%s\t%s%02d/%02d/%d %02d:%02d\t%s\n", 663: #endif /* !USG */ 664: h.ident, h.expdate[0] ? " " : "", 665: tm->tm_mon+1, tm->tm_mday, tm->tm_year, 666: tm->tm_hour, tm->tm_min, mhp->mh_file) 667: == EOF ) 668: xerror("History write failed"); 669: (void) fclose(fp); 670: continue; 671: } 672: (void) pclose(nhfd); 673: free ((char *)multhist); 674: } else 675: fclose(nhfd); 676: 677: if (dorebuild || !nohistory) { 678: (void) rename(ARTFILE, OARTFILE); 679: (void) rename(NARTFILE, ARTFILE); 680: #ifdef DBM 681: if (dorebuild) 682: rebuilddbm( ); 683: else { 684: char tempname[BUFLEN]; 685: (void) sprintf(tempname,"%s.pag", ARTFILE); 686: (void) strcat(OARTFILE, ".pag"); 687: (void) strcat(NARTFILE, ".pag"); 688: (void) rename(tempname, OARTFILE); 689: (void) rename(NARTFILE, tempname); 690: (void) sprintf(tempname,"%s.dir", ARTFILE); 691: (void) strcpy(rindex(OARTFILE, '.'), ".dir"); 692: (void) strcpy(rindex(NARTFILE, '.'), ".dir"); 693: (void) rename(tempname, OARTFILE); 694: (void) rename(NARTFILE, tempname); 695: } 696: #endif 697: } 698: #ifndef DBM 699: /* rebuild history subfiles */ 700: for (i = 0; i < 10; i++) { 701: (void) sprintf(fn, "%s.d/%c", ARTFILE, i + '0'); 702: close(creat(fn, 0644)); 703: subfd[i] = xfopen(fn, "w+"); 704: } 705: ohfd = xfopen(ARTFILE, "r"); 706: while (fgets(fn, BUFLEN, ohfd) != NULL) { 707: ptr = histfile(fn); 708: chr = *(ptr + strlen(ptr) - 1); 709: if (isdigit(chr)) 710: i = chr - '0'; 711: else 712: i = 0; 713: fputs(fn, subfd[i]); 714: } 715: (void) fclose(ohfd); 716: for (i = 0; i < 10; i++) 717: fclose(subfd[i]); 718: #endif /* !DBM */ 719: 720: doupdateactive: 721: ohfd = xfopen(ACTIVE, "r"); 722: nhfd = xfopen(NACTIVE, "w"); 723: do { 724: long n; 725: long maxart, minart; 726: char cansub; 727: int gdsize, hassubs; 728: struct stat stbuf; 729: 730: if (fgets(afline, BUFLEN, ohfd) == NULL) 731: continue; 732: if (sscanf(afline,"%s %ld %ld %c",nbuf,&maxart, &minart, 733: &cansub) < 4) 734: xerror("Active file corrupt"); 735: if (!ngmatch(nbuf, ngpat)) { 736: if (fputs(afline, nhfd) == EOF) 737: xerror("active file write failed"); 738: continue; 739: } 740: minart = 99999L; 741: /* Change a group name from a.b.c to a/b/c */ 742: for (p1=nbuf; *p1; p1++) 743: if (*p1 == '.') 744: *p1 = '/'; 745: 746: hassubs = stat(nbuf, &stbuf) != 0 || stbuf.st_nlink != 2; 747: gdsize = strlen(nbuf); 748: if ((ngdirp = opendir(nbuf)) != NULL) { 749: while (ngdir = readdir(ngdirp)) { 750: nbuf[gdsize] = '/'; 751: (void) strcpy(&nbuf[gdsize+1], ngdir->d_name); 752: /* We have to do a stat because of micro.6809 */ 753: if (hassubs && (stat(nbuf, &stbuf) < 0 || 754: !(stbuf.st_mode&S_IFREG)) ) 755: continue; 756: n = atol(ngdir->d_name); 757: if (n > 0 && n < minart) 758: minart = n; 759: if (n > 0 && n > maxart) 760: maxart = n; 761: } 762: closedir(ngdirp); 763: } 764: afline[gdsize] = '\0'; 765: if (minart > maxart) 766: minart = maxart; 767: if (fprintf(nhfd,"%s %05ld %05ld %c\n", afline, maxart, 768: minart, cansub) == EOF) 769: xerror("Active file write failed"); 770: } while (!feof(ohfd)); 771: (void) fclose(nhfd); 772: (void) fclose(ohfd); 773: 774: (void) rename(ACTIVE, OACTIVE); 775: (void) rename(NACTIVE, ACTIVE); 776: 777: xxit(0); 778: } 779: 780: /* Unlink (using unwound tail recursion) all the articles in 'artlist'. */ 781: ulall(artlist, hp) 782: char *artlist; 783: struct hbuf *hp; 784: { 785: register char *p, *q; 786: int last = 0; 787: char newname[BUFLEN]; 788: time_t timep[2]; 789: char *fn; 790: 791: grpsleft[0] = '\0'; 792: do { 793: if (verbose > 2) 794: printf("ulall '%s', '%s'\n", artlist, hp->subdate); 795: if (nohistory) { 796: last = 1; 797: } else { 798: while (*artlist == ' ' || *artlist == '\n' || *artlist == ',') 799: artlist++; 800: if (*artlist == '\0') 801: return; 802: p = index(artlist, ' '); 803: if (p == NULL) { 804: last = 1; 805: p = index(artlist, '\n'); 806: } 807: if (p == NULL) { 808: last = 1; 809: fn = dirname(artlist); 810: if (unlink(fn) < 0 && errno != ENOENT) 811: perror(fn); 812: return; 813: } 814: if (p) 815: *p = 0; 816: } 817: strcpy(newname, artlist); 818: q = index(newname,'/'); 819: if (q) { 820: *q++ = NGDELIM; 821: *q = '\0'; 822: } else { 823: q = index(newname, '\0'); 824: if (q == artlist) /* null -> the end */ 825: return; 826: /* should be impossible to get here */ 827: } 828: fn = dirname(artlist); 829: if (ngmatch(newname, ngpat)) { 830: if (doarchive){ 831: if (ngmatch(newname, arpat)) { 832: q = fn + strlen(SPOOL) + 1; 833: (void) sprintf(newname, "%s/%s", OLDNEWS, q); 834: if (verbose) 835: printf("link %s to %s\n", fn, newname); 836: if (link(fn, newname) == -1) { 837: if (mkparents(newname) == 0) 838: if (link(fn, newname) == -1) 839: fcopy(fn, newname); 840: } 841: timep[0] = timep[1] = cgtdate(hp->subdate); 842: (void) utime(newname, timep); 843: } 844: } 845: if (verbose) 846: printf("unlink %s\n", fn); 847: if (unlink(fn) < 0 && errno != ENOENT) 848: perror(fn); 849: } else { 850: if (verbose > 3) 851: printf("retain %s (%s)\n", hp->ident, fn); 852: strcat(grpsleft, artlist); 853: strcat(grpsleft, " "); 854: } 855: artlist = p + 1; 856: } while (!last); 857: } 858: 859: fcopy(fn, newname) 860: char *fn, *newname; 861: { 862: int f1, f2; 863: int r; 864: char buf[BUFSIZ]; 865: f1 = open(fn, 0); 866: if (f1 < 0) 867: return -1; 868: f2 = open(newname, 1); 869: if (f2 < 0) { 870: if (errno == ENOENT) { 871: f2 = creat(newname,0644); 872: if (f2 < 0) { 873: close(f1); 874: return -1; 875: } 876: } else { 877: close(f1); 878: return -1; 879: } 880: } 881: while((r=read(f1, buf, BUFSIZ)) > 0) 882: write(f2, buf, r); 883: (void) close(f1); 884: (void) close(f2); 885: return 0; 886: } 887: 888: /* 889: * Count instances of c in s 890: */ 891: chrcnt(s, c) 892: register char *s; 893: register c; 894: { 895: register n = 0; 896: register cc; 897: 898: while (cc = *s++) 899: if (cc == c) 900: n++; 901: return n; 902: } 903: 904: /* 905: * If any parent directories of this dir don't exist, create them. 906: */ 907: mkparents(fullname) 908: char *fullname; 909: { 910: char buf[200]; 911: register char *p; 912: int rc; 913: 914: (void) strcpy(buf, fullname); 915: p = rindex(buf, '/'); 916: if (p) 917: *p = '\0'; 918: if (access(buf, 0) == 0) 919: return 0; 920: mkparents(buf); 921: if ((rc = mkdir(buf, 0755)) < 0) 922: perror("mkdir failed"); 923: if (verbose) 924: printf("mkdir %s, rc %d\n", buf, rc); 925: 926: return rc; 927: } 928: 929: 930: /* Make sure this file is a legal article. */ 931: islegal(fullname, path, name) 932: register char *fullname; 933: register char *path; 934: register char *name; 935: { 936: struct stat buffer; 937: 938: (void) sprintf(fullname, "%s/%s", path, name); 939: 940: /* make sure the article is numeric. */ 941: while (*name != '\0') 942: if (!isascii(*name) || !isdigit(*name)) 943: return 0; 944: else 945: name++; 946: 947: /* Now make sure we don't have a group like net.micro.432, 948: * which is numeric but not a regular file -- i.e., check 949: * for being a regular file. 950: */ 951: if ((stat(fullname, &buffer) == 0) && 952: ((buffer.st_mode & S_IFMT) == S_IFREG)) { 953: /* Now that we found a legal group in a/b/c/4 954: notation, switch it to a.b.c/4 notation. */ 955: for (name = fullname; name != NULL && *name != '\0'; name++) 956: if (*name == '/' && name != rindex (name, '/')) 957: *name = '.'; 958: 959: return 1; 960: } 961: return 0; 962: } 963: 964: #ifdef DBM 965: /* 966: * This is taken mostly intact from ../cvt/cvt.hist.c and is used at the 967: * end by the options that make a new history file. 968: * Routine to convert history file to dbm file. The old 3 field 969: * history file is still kept there, because we need it for expire 970: * and for a human readable copy. But we keep a dbm hashed copy 971: * around by message ID so we can answer the yes/no question "have 972: * we already seen this message". The content is the ftell offset 973: * into the real history file when we get the article - you can't 974: * really do much with this because the file gets compacted. 975: */ 976: 977: FILE *fd; 978: 979: char namebuf[BUFSIZ]; 980: char lb[BUFSIZ]; 981: 982: rebuilddbm() 983: { 984: register char *p; 985: long fpos; 986: 987: (void) umask(0); 988: (void) sprintf(namebuf, "%s.dir", ARTFILE); 989: (void) close(creat(namebuf, 0666)); 990: (void) sprintf(namebuf, "%s.pag", ARTFILE); 991: (void) close(creat(namebuf, 0666)); 992: (void) sprintf(namebuf, "%s", ARTFILE); 993: 994: fd = fopen(namebuf, "r"); 995: if (fd == NULL) { 996: perror(namebuf); 997: xxit(2); 998: } 999: 1000: initdbm(namebuf); 1001: while (fpos=ftell(fd), fgets(lb, BUFSIZ, fd) != NULL) { 1002: p = index(lb, '\t'); 1003: if (p) 1004: *p = 0; 1005: remember(lb, fpos); 1006: } 1007: } 1008: 1009: remember(article, fileoff) 1010: register char *article; 1011: long fileoff; 1012: { 1013: datum lhs, rhs; 1014: 1015: lcase(article); 1016: lhs.dptr = article; 1017: lhs.dsize = strlen(article) + 1; 1018: rhs.dptr = (char *) &fileoff; 1019: rhs.dsize = sizeof fileoff; 1020: 1021: if (verbose > 5) 1022: printf("remember: %s @ %ld\n", article, fileoff); 1023: if (store(lhs, rhs) < 0) 1024: xerror("dbm store failed"); 1025: } 1026: #endif /* DBM */ 1027: 1028: xxit(i) 1029: { 1030: exit(i); 1031: }