home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / cnews2 / part08 < prev    next >
Text File  |  1989-06-29  |  54KB  |  2,226 lines

  1. Subject:  v19i085:  Cnews production release, Part08/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: utzoo!henry
  7. Posting-number: Volume 19, Issue 85
  8. Archive-name: cnews2/part08
  9.  
  10. : ---CUT HERE---
  11. echo 'expire/expire.c':
  12. sed 's/^X//' >'expire/expire.c' <<'!'
  13. X/*
  14. X * expire - expire old news
  15. X *
  16. X * One modest flaw:  links are not preserved in archived copies, i.e. you
  17. X * get multiple copies of multiply-posted articles.  Since link preservation
  18. X * is arbitrarily hard when control files get complex, to hell with it.
  19. X */
  20. X
  21. X#include <stdio.h>
  22. X#include <ctype.h>
  23. X#include <string.h>
  24. X#include <errno.h>
  25. X#include <time.h>
  26. X#include <signal.h>
  27. X#include <sys/types.h>
  28. X#include <sys/timeb.h>
  29. X#include <sys/stat.h>
  30. X#include "libc.h"
  31. X#include "news.h"
  32. X#include "config.h"
  33. X#include "fgetmfs.h"
  34. X
  35. X#ifndef EPOCH
  36. X#define    EPOCH    ((time_t)0)
  37. X#endif
  38. X
  39. X
  40. X/* structure for dbm */
  41. Xtypedef struct {
  42. X    char *dptr;
  43. X    int dsize;
  44. X} datum;
  45. X
  46. X#define    DAY    ((double)24*60*60)
  47. X
  48. X/* structure for expiry-control records */
  49. Xstruct ctl {
  50. X    struct ctl *next;
  51. X    char *groups;        /* newsgroups */
  52. X    int ismod;        /* moderated? */
  53. X#        define    UNMOD    'u'
  54. X#        define    MOD    'm'
  55. X#        define    EITHER    'x'
  56. X    time_t retain;        /* earliest arrival date not expired */
  57. X    time_t normal;        /* earliest not expired in default case */
  58. X    time_t purge;        /* latest arrival date always expired */
  59. X    char *dir;        /* Archive dir or NULL. */
  60. X};
  61. X
  62. X/* header for internal form of control file */
  63. Xstruct ctl *ctls = NULL;
  64. Xstruct ctl *lastctl = NULL;
  65. X
  66. X/*
  67. X * Headers for lists by newsgroup, derived (mostly) from active file.
  68. X * Hashing is by length of newsgroup name; this is quick and works well,
  69. X * and there is no simple variation that does much better.
  70. X */
  71. X#define    NHASH    80
  72. Xstruct ctl *ngs[NHASH] = { NULL };
  73. X
  74. Xstruct ctl *holdover = NULL;    /* "/expired/" control record */
  75. Xstruct ctl *bounds = NULL;    /* "/bounds/" control record */
  76. X
  77. Xint debug = 0;            /* for inews routines */
  78. Xint expdebug = 0;        /* expire debugging */
  79. X
  80. Xint printexpiring = 0;        /* print info line for expiring articles? */
  81. Xchar *defarch = NULL;        /* default archive dir */
  82. Xint spacetight = 0;        /* error-recovery actions remove evidence? */
  83. X
  84. Xchar *subsep = "~";        /* subfield separator in middle field */
  85. Xint checkonly = 0;        /* check control information only */
  86. Xint testing = 0;        /* testing only, leave articles alone */
  87. Xint leaders = 0;        /* only first link ("leader") is hard link */
  88. Xint verbose = 0;        /* report statistics */
  89. X
  90. Xlong nkept = 0;            /* count of articles not expired */
  91. Xlong ngone = 0;            /* count of articles removed (no links left) */
  92. Xlong nresid = 0;        /* count of residual entries kept */
  93. Xlong narched = 0;        /* count of links archived */
  94. Xlong njunked = 0;        /* count of links just removed */
  95. Xlong nmissing = 0;        /* count of links missing at cp/rm time */
  96. X
  97. Xchar dont[] = "don't";        /* magic cookie for whereexpire() return */
  98. X
  99. Xtime_t now;
  100. Xstruct timeb ftnow;        /* ftime() result for getdate() */
  101. X#define    NODATE    ((time_t)(-1))    /* time_t value indicating date not given */
  102. X
  103. Xchar subject[200] = "";        /* Subject line for -p, minus header */
  104. X
  105. X/* Buffer etc. for readline and friends. */
  106. Xchar rlbuf[BUFSIZ];
  107. Xint rlnleft = 0;
  108. Xchar *rest;
  109. Xint nlocked = 0;        /* has readline() locked the news system? */
  110. X
  111. X/*
  112. X * Archive-copying buffer.
  113. X * 8KB buffer is large enough to take most articles at one gulp,
  114. X * and also large enough for virtual certainty of getting the
  115. X * Subject: line in the first bufferload.
  116. X */
  117. X#ifdef SMALLMEM
  118. Xchar abuf[2*1024];        /* expire reported to be tight on 11 */
  119. X#else
  120. Xchar abuf[8*1024];
  121. X#endif
  122. X
  123. Xchar *progname;
  124. X
  125. Xextern int errno;
  126. Xextern long atol();
  127. Xextern double atof();
  128. Xextern char *malloc();
  129. Xextern struct tm *gmtime();
  130. Xextern time_t time();
  131. X
  132. Xextern time_t getdate();
  133. X
  134. X/* forwards */
  135. XFILE *eufopen();
  136. Xvoid eufclose();
  137. Xvoid euclose();
  138. Xchar *whereexpire();
  139. Xtime_t back();
  140. Xvoid checkdir();
  141. Xvoid fail();
  142. Xvoid control();
  143. Xvoid prime();
  144. Xvoid doit();
  145. Xvoid cd();
  146. Xchar *doline();
  147. Xtime_t readdate();
  148. Xchar *doarticle();
  149. Xvoid warning();
  150. Xvoid printstuff();
  151. Xvoid expire();
  152. Xchar *readline();
  153. Xvoid mkparents();
  154. Xvoid getsubj();
  155. Xvoid refill();
  156. Xvoid printlists();
  157. Xvoid pctl();
  158. Xvoid fillin();
  159. Xvoid ctlline();
  160. Xchar *strvsave();
  161. X
  162. X/*
  163. X - main - parse arguments and handle options
  164. X */
  165. Xmain(argc, argv)
  166. Xint argc;
  167. Xchar *argv[];
  168. X{
  169. X    register int c;
  170. X    register int errflg = 0;
  171. X    register FILE *cf;
  172. X    extern int optind;
  173. X    extern char *optarg;
  174. X
  175. X    progname = argv[0];
  176. X    now = time((time_t *)NULL);
  177. X    ftime(&ftnow);
  178. X
  179. X    while ((c = getopt(argc, argv, "pa:sF:cn:tlvd")) != EOF)
  180. X        switch (c) {
  181. X        case 'p':    /* print info line for archived articles */
  182. X            printexpiring = 1;
  183. X            break;
  184. X        case 'a':    /* archive in this directory */
  185. X            defarch = optarg;
  186. X            break;
  187. X        case 's':    /* maximize space during error recovery */
  188. X            spacetight = 1;
  189. X            break;
  190. X        case 'F':    /* subfield separator in middle field */
  191. X            subsep = optarg;
  192. X            break;
  193. X        case 'c':    /* check control-file format only */
  194. X            checkonly = 1;
  195. X            break;
  196. X        case 'n':    /* set value of "now" for testing */
  197. X            now = atol(optarg);
  198. X            break;
  199. X        case 't':    /* testing, do not mess with articles */
  200. X            testing = 1;
  201. X            break;
  202. X        case 'l':    /* leaders */
  203. X            leaders = 1;
  204. X            break;
  205. X        case 'v':    /* verbose -- report some statistics */
  206. X            verbose = 1;
  207. X            break;
  208. X        case 'd':    /* debug */
  209. X            expdebug = 1;
  210. X            break;
  211. X        case '?':
  212. X        default:
  213. X            errflg++;
  214. X            break;
  215. X        }
  216. X    if (errflg || optind < argc-1) {
  217. X        fprintf(stderr, "Usage: %s [-p] [-s] [-c] [-a archdir] [ctlfile]\n",
  218. X                                progname);
  219. X        exit(2);
  220. X    }
  221. X    if (expdebug)
  222. X        setbuf(stderr, (char *)NULL);
  223. X
  224. X    if (optind < argc) {
  225. X        cf = eufopen(argv[optind], "r");
  226. X        control(cf);
  227. X        (void) fclose(cf);
  228. X    } else
  229. X        control(stdin);
  230. X    prime(ctlfile("active"));
  231. X
  232. X    if (expdebug)
  233. X        printlists();
  234. X    if (defarch != NULL)
  235. X        checkdir(defarch);
  236. X    if (checkonly)
  237. X        exit(0);
  238. X
  239. X
  240. X    (void) umask(newsumask());
  241. X    doit();            /* side effect: newslock() */
  242. X    newsunlock();
  243. X
  244. X    if (verbose) {
  245. X        fprintf(stderr, "%ld kept, %ld expired\n", nkept, ngone);
  246. X        fprintf(stderr, "%ld residual lines\n", nresid);
  247. X        fprintf(stderr, "%ld links archived, %ld junked, %ld missing\n",
  248. X                        narched, njunked, nmissing);
  249. X    }
  250. X    exit(0);
  251. X}
  252. X
  253. X/*
  254. X - control - pick up a control file
  255. X */
  256. Xvoid
  257. Xcontrol(f)
  258. Xregister FILE *f;
  259. X{
  260. X    char line[200];        /* long enough for any sane line */
  261. X    register char *p;
  262. X    void ctlline();
  263. X
  264. X    while (fgets(line, sizeof(line), f) != NULL) {
  265. X        p = &line[strlen(line) - 1];
  266. X        if (*p != '\n')
  267. X            fail("control line `%.30s...' too long", line);
  268. X        *p = '\0';
  269. X        if (line[0] != '#' && line[0] != '\0')
  270. X            ctlline(line);
  271. X    }
  272. X}
  273. X
  274. X/*
  275. X - ctlline - process one control-file line
  276. X */
  277. Xvoid
  278. Xctlline(ctl)
  279. Xchar *ctl;
  280. X{
  281. X    register struct ctl *ct;
  282. X    char *field[4];
  283. X    char datebuf[50];
  284. X    char *dates[3];
  285. X    register int nf;
  286. X    int ndates;
  287. X
  288. X    errno = 0;
  289. X    nf = split(ctl, field, 4, "\t ");
  290. X    if (nf != 4)
  291. X        fail("control line `%.20s...' hasn't got 4 fields", ctl);
  292. X
  293. X    errno = 0;
  294. X    ct = (struct ctl *)malloc(sizeof(struct ctl));
  295. X    if (ct == NULL)
  296. X        fail("out of memory for control list", "");
  297. X
  298. X
  299. X    ct->groups = strsave(field[0]);
  300. X    if (STREQ(field[1], "m"))
  301. X        ct->ismod = MOD;
  302. X    else if (STREQ(field[1], "u"))
  303. X        ct->ismod = UNMOD;
  304. X    else if (STREQ(field[1], "x"))
  305. X        ct->ismod = EITHER;
  306. X    else
  307. X        fail("strange mod field `%s' in control file", field[1]);
  308. X
  309. X    if (strlen(field[2]) > sizeof(datebuf)-1)
  310. X        fail("date specification `%s' too long", field[2]);
  311. X    (void) strcpy(datebuf, field[2]);
  312. X    ndates = split(datebuf, dates, 3, "-");
  313. X    switch (ndates) {
  314. X    case 3:
  315. X        ct->retain = back(atof(dates[0]));
  316. X        ct->normal = back(atof(dates[1]));
  317. X        ct->purge = back(atof(dates[2]));
  318. X        break;
  319. X    case 2:
  320. X        ct->retain = (bounds != NULL) ? bounds->retain : back(0.0);
  321. X        ct->normal = back(atof(dates[0]));
  322. X        ct->purge = back(atof(dates[1]));
  323. X        break;
  324. X    case 1:
  325. X        ct->retain = (bounds != NULL) ? bounds->retain : back(0.0);
  326. X        ct->normal = back(atof(dates[0]));
  327. X        ct->purge = (bounds != NULL) ? bounds->purge : EPOCH;
  328. X        break;
  329. X    default:
  330. X        fail("date processing foulup in `%s'", field[1]);
  331. X        /* NOTREACHED */
  332. X        break;
  333. X    }
  334. X    if (ct->retain < ct->normal || ct->normal < ct->purge)
  335. X        fail("preposterous dates: `%s'", field[2]);
  336. X
  337. X    if (STREQ(field[3], "-"))
  338. X        ct->dir = NULL;
  339. X    else if (STREQ(field[3], "@")) {
  340. X        if (defarch == NULL)
  341. X            fail("@ in control file but no -a", "");
  342. X        ct->dir = defarch;
  343. X    } else {
  344. X        ct->dir = strsave(field[3]);
  345. X        checkdir(ct->dir);
  346. X    }
  347. X
  348. X    /* put it where it belongs */
  349. X    if (STREQ(ct->groups, "/expired/"))
  350. X        holdover = ct;
  351. X    else if (STREQ(ct->groups, "/bounds/"))
  352. X        bounds = ct;
  353. X    else {
  354. X        ct->next = NULL;
  355. X        if (lastctl == NULL)
  356. X            ctls = ct;
  357. X        else
  358. X            lastctl->next = ct;
  359. X        lastctl = ct;
  360. X    }
  361. X}
  362. X
  363. X/*
  364. X - prime - prime control lists from active file
  365. X */
  366. Xvoid
  367. Xprime(afile)
  368. Xchar *afile;
  369. X{
  370. X    register char *line;
  371. X    register FILE *af;
  372. X    register struct ctl *ct;
  373. X#    define    NFACT    4
  374. X    char *field[NFACT];
  375. X    int nf;
  376. X    register int hash;
  377. X
  378. X    af = eufopen(afile, "r");
  379. X    while ((line = fgetms(af)) != NULL) {
  380. X        nf = split(line, field, NFACT, " \t");
  381. X        if (nf != NFACT)
  382. X            fail("bad active-file line for `%s'", field[0]);
  383. X        ct = (struct ctl *)malloc(sizeof(struct ctl));
  384. X        if (ct == NULL)
  385. X            fail("out of memory at newsgroup `%s'", field[0]);
  386. X        ct->groups = strsave(field[0]);
  387. X        ct->ismod = (strchr(field[3], 'm') != NULL) ? MOD : UNMOD;
  388. X        fillin(ct);
  389. X        hash = strlen(field[0]);
  390. X        if (hash > NHASH-1)
  391. X            hash = NHASH-1;
  392. X        ct->next = ngs[hash];
  393. X        ngs[hash] = ct;
  394. X        free(line);
  395. X    }
  396. X    (void) fclose(af);
  397. X}
  398. X
  399. X/*
  400. X - fillin - fill in a ctl struct for a newsgroup from the control-file list
  401. X */
  402. Xvoid
  403. Xfillin(ct)
  404. Xregister struct ctl *ct;
  405. X{
  406. X    register struct ctl *cscan;
  407. X    char grump[100];
  408. X
  409. X    for (cscan = ctls; cscan != NULL; cscan = cscan->next)
  410. X        if (ngmatch(cscan->groups, ct->groups) &&
  411. X                (cscan->ismod == ct->ismod ||
  412. X                        cscan->ismod == EITHER)) {
  413. X            ct->retain = cscan->retain;
  414. X            ct->normal = cscan->normal;
  415. X            ct->purge = cscan->purge;
  416. X            ct->dir = cscan->dir;
  417. X            return;
  418. X        }
  419. X
  420. X    /* oooooops... */
  421. X    sprintf(grump, "group `%%s' (%smoderated) not covered by control file",
  422. X                    (ct->ismod == MOD) ? "" : "un");
  423. X    fail(grump, ct->groups);
  424. X}
  425. X
  426. X/*
  427. X - doit - file manipulation and master control
  428. X */
  429. Xvoid
  430. Xdoit()
  431. X{
  432. X    register int old;
  433. X    register FILE *new;
  434. X    char *line;
  435. X    long here;
  436. X    register char *nameend;
  437. X    datum lhs;
  438. X    datum rhs;
  439. X    register int ret;
  440. X
  441. X    cd(ctlfile((char *)NULL));
  442. X    old = open("history", 0);
  443. X    if (old < 0)
  444. X        fail("cannot open `%s'", "history");
  445. X    (void) unlink("history.n");
  446. X    (void) unlink("history.n.dir");
  447. X    (void) unlink("history.n.pag");
  448. X    if (spacetight)
  449. X        (void) unlink("history.o");
  450. X    new = eufopen("history.n", "w");
  451. X    (void) fclose(eufopen("history.n.dir", "w"));
  452. X    (void) fclose(eufopen("history.n.pag", "w"));
  453. X    if (dbminit("history.n") < 0)
  454. X        fail("dbminit(history.n) failed", "");
  455. X
  456. X    cd(artfile((char *)NULL));
  457. X    while ((line = readline(old)) != NULL) {
  458. X        line = doline(line);
  459. X        if (line != NULL) {
  460. X            /* extract the message-id */
  461. X            nameend = strchr(line, '\t');
  462. X            if (nameend == NULL) {
  463. X                errno = 0;
  464. X                fail("bad return from doline(): `%.75s'", line);
  465. X            }
  466. X
  467. X            /* make the DBM entry */
  468. X            *nameend = '\0';
  469. X            lhs.dptr = line;
  470. X            lhs.dsize = strlen(line)+1;
  471. X            here = ftell(new);
  472. X            rhs.dptr = (char *)&here;
  473. X            rhs.dsize = sizeof(here);
  474. X            errno = 0;
  475. X            ret = store(lhs, rhs);
  476. X            if (ret < 0)
  477. X                fail("dbm failure on `%s'", line);
  478. X            *nameend = '\t';
  479. X
  480. X            /* and the history entry */
  481. X            fputs(line, new);
  482. X            putc('\n', new);
  483. X            free(line);
  484. X        }
  485. X    }
  486. X    /* side effect of readline() == NULL:  newslock() */
  487. X
  488. X    (void) close(old);
  489. X    eufclose(new, "history.n");
  490. X
  491. X    if (testing)
  492. X        return;
  493. X    cd(ctlfile((char *)NULL));
  494. X    (void) unlink("history.o");
  495. X    if (link("history", "history.o") < 0)
  496. X        fail("can't move history", "");
  497. X    if (unlink("history") < 0)
  498. X        fail("can't finish moving history", "");
  499. X    if (link("history.n", "history") < 0)
  500. X        fail("disaster -- can't reinstate history!", "");
  501. X    if (unlink("history.n") < 0)
  502. X        fail("disaster -- can't unlink history.n!", "");
  503. X    if (unlink("history.dir") < 0)
  504. X        fail("disaster -- can't unlink history.dir!", "");
  505. X    if (unlink("history.pag") < 0)
  506. X        fail("disaster -- can't unlink history.pag!", "");
  507. X    if (link("history.n.dir", "history.dir") < 0)
  508. X        fail("disaster -- can't reinstate history.dir!", "");
  509. X    if (link("history.n.pag", "history.pag") < 0)
  510. X        fail("disaster -- can't reinstate history.pag!", "");
  511. X    if (unlink("history.n.dir") < 0)
  512. X        fail("disaster -- can't unlink history.n.dir!", "");
  513. X    if (unlink("history.n.pag") < 0)
  514. X        fail("disaster -- can't unlink history.n.pag!", "");
  515. X}
  516. X
  517. X/*
  518. X - doline - handle one history line, modifying it if appropriate
  519. X */
  520. Xchar *                /* new (malloced) line; NULL means none */
  521. Xdoline(line)
  522. Xchar *line;            /* malloced; freed here */
  523. X{
  524. X    char *work;
  525. X#    define    NF    3
  526. X    char *field[NF];    /* fields in line */
  527. X    register int nf;
  528. X#    define    NSF    10
  529. X    char *subfield[NSF];    /* subfields in middle field */
  530. X    register int nsf;
  531. X    register time_t recdate;
  532. X    register time_t expdate;
  533. X    char expbuf[25];        /* plenty for decimal time_t */
  534. X    int wasreal;
  535. X
  536. X    if (expdebug) {
  537. X        fputs("\ndoline `", stderr);
  538. X        fputs(line, stderr);
  539. X        fputs("'\n", stderr);
  540. X    }
  541. X
  542. X    /* pull the incoming line apart */
  543. X    work = strsave(line);
  544. X    nf = split(work, field, NF, "\t");
  545. X    if (nf != 3 && nf != 2) {
  546. X        free(work);
  547. X        errno = 0;
  548. X        warning("wrong number of fields in `%.40s...'", line);
  549. X        return(line);    /* leaving the line in the new history file */
  550. X    }
  551. X    if (nf == 2)
  552. X        field[2] = NULL;
  553. X    nsf = split(field[1], subfield, NSF, subsep);
  554. X
  555. X    /* sort out the dates */
  556. X    if (nsf < 2 || STREQ(subfield[1], "-") || STREQ(subfield[1], ""))
  557. X        expdate = NODATE;
  558. X    else {
  559. X        expdate = readdate(subfield[1]);
  560. X        if (expdate == NODATE) {
  561. X            errno = 0;
  562. X            warning("bad expiry date in `%.40s...',", line);
  563. X            warning(" specifically, `%s' -- ignored", subfield[1]);
  564. X        }
  565. X    }
  566. X    recdate = readdate(subfield[0]);
  567. X    if (recdate == NODATE) {
  568. X        free(work);
  569. X        errno = 0;
  570. X        warning("bad arrival date in `%.40s...' -- fix by hand", line);
  571. X        return(line);
  572. X    }
  573. X    free(line);
  574. X    if (expdebug)
  575. X        fprintf(stderr, "rec %ld, expire %ld\n", (long)recdate,
  576. X                                (long)expdate);
  577. X
  578. X    /* deal with it */
  579. X    if (nf > 2 && STREQ(field[2], "/"))    /* old C news cancellation */
  580. X        field[2] = NULL;
  581. X    else if (nf > 2 && STREQ(field[2], "cancelled"))    /* B 2.11 */
  582. X        field[2] = NULL;
  583. X    wasreal = (field[2] != NULL);
  584. X    field[2] = doarticle(field[2], recdate, expdate, field[0]);
  585. X    if (wasreal) {
  586. X        if (field[2] == NULL)
  587. X            ngone++;
  588. X        else
  589. X            nkept++;
  590. X    }
  591. X
  592. X    /* construct new line */
  593. X    if (field[2] != NULL || (holdover != NULL &&
  594. X                !shouldgo(recdate, NODATE, holdover))) {
  595. X        if (expdate != NODATE) {
  596. X            sprintf(expbuf, "%ld", (long)expdate);
  597. X            subfield[1] = expbuf;
  598. X        } else
  599. X            subfield[1] = "-";
  600. X        field[1] = strvsave(subfield, nsf, *subsep);
  601. X        line = strvsave(field, 3, '\t');
  602. X        free(field[1]);
  603. X        if (expdebug) {
  604. X            fputs("new line `", stderr);
  605. X            fputs(line, stderr);
  606. X            fputs("'\n", stderr);
  607. X        }
  608. X        if (field[2] == NULL)
  609. X            nresid++;
  610. X    } else
  611. X        line = NULL;
  612. X
  613. X    free(work);
  614. X    return(line);
  615. X}
  616. X
  617. X/*
  618. X - readdate - turn a date into internal form
  619. X */
  620. Xtime_t
  621. Xreaddate(text)
  622. Xchar *text;
  623. X{
  624. X    time_t ret;
  625. X
  626. X    if (strspn(text, "0123456789") == strlen(text))
  627. X        ret = atol(text);
  628. X    else
  629. X        ret = getdate(text, &ftnow);
  630. X    if (ret == -1)
  631. X        ret = NODATE;
  632. X
  633. X    return(ret);
  634. X}
  635. X
  636. X/*
  637. X - doarticle - possibly expire an article
  638. X *
  639. X * Re-uses the space of its first argument.
  640. X */
  641. Xchar *                /* new name list, in space of old, or NULL */
  642. Xdoarticle(oldnames, recdate, expdate, msgid)
  643. Xchar *oldnames;            /* may be destroyed */
  644. Xtime_t recdate;
  645. Xtime_t expdate;
  646. Xchar *msgid;            /* for printstuff() */
  647. X{
  648. X    register char *src;
  649. X    register char *dst;
  650. X    register char *name;
  651. X    register char *dir;
  652. X    register char *p;
  653. X    register char srcc;
  654. X    register int nleft;
  655. X    register int nexpired;
  656. X#    define    NDELIM    " ,"
  657. X
  658. X    if (oldnames == NULL)
  659. X        return(NULL);
  660. X
  661. X    src = oldnames;
  662. X    dst = oldnames;
  663. X    nleft = 0;
  664. X    nexpired = 0;
  665. X    for (;;) {
  666. X        src += strspn(src, NDELIM);
  667. X        name = src;
  668. X        src += strcspn(src, NDELIM);
  669. X        srcc = *src;
  670. X        *src = '\0';
  671. X        if (*name == '\0')
  672. X            break;        /* NOTE BREAK OUT */
  673. X        if (expdebug)
  674. X            fprintf(stderr, "name `%s'\n", name);
  675. X
  676. X        dir = whereexpire(recdate, expdate, name);
  677. X        if (dir != dont && !(leaders && nleft == 0 && srcc != '\0')) {
  678. X            if (expdebug)
  679. X                fprintf(stderr, "expire into `%s'\n",
  680. X                    (dir == NULL) ? "(null)" : dir);
  681. X            for (p = strchr(name, '.'); p != NULL;
  682. X                            p = strchr(p+1, '.'))
  683. X                *p = '/';
  684. X            expire(name, dir);
  685. X            if (dir != NULL && printexpiring)
  686. X                printstuff(msgid, name, recdate);
  687. X            nexpired++;
  688. X        } else {
  689. X            if (dst != oldnames)
  690. X                *dst++ = ' ';
  691. X            while (*name != '\0')
  692. X                *dst++ = *name++;
  693. X            nleft++;
  694. X        }
  695. X        *src = srcc;
  696. X    }
  697. X
  698. X    if (nleft == 0)
  699. X        return(NULL);
  700. X    *dst++ = '\0';
  701. X    if (leaders && nleft == 1 && nexpired > 0)    /* aging leader */
  702. X        return(doarticle(oldnames, recdate, expdate, msgid));
  703. X    return(oldnames);
  704. X}
  705. X
  706. X/*
  707. X - whereexpire - where should this name expire to, and should it?
  708. X *
  709. X * The "dont" variable's address is used as the don't-expire return value,
  710. X * since NULL means "to nowhere".
  711. X */
  712. Xchar *                /* archive directory, NULL, or dont */
  713. Xwhereexpire(recdate, expdate, name)
  714. Xtime_t recdate;
  715. Xtime_t expdate;
  716. Xchar *name;
  717. X{
  718. X    register char *group;
  719. X    register char *slash;
  720. X    register struct ctl *ct;
  721. X    register int hash;
  722. X
  723. X    group = name;
  724. X    slash = strchr(group, '/');
  725. X    if (slash == NULL) {
  726. X        errno = 0;
  727. X        fail("no slash in article path `%s'", name);
  728. X    } else
  729. X        *slash = '\0';
  730. X    if (strchr(slash+1, '/') != NULL) {
  731. X        *slash = '/';
  732. X        errno = 0;
  733. X        fail("multiple slashes in article path `%s'", name);
  734. X    }
  735. X
  736. X    /* find applicable expiry-control struct (make it if necessary) */
  737. X    hash = strlen(group);
  738. X    if (hash > NHASH-1)
  739. X        hash = NHASH-1;
  740. X    for (ct = ngs[hash]; ct != NULL && !STREQ(ct->groups, group);
  741. X                                ct = ct->next)
  742. X        continue;
  743. X    if (ct == NULL) {    /* oops, there wasn't one */
  744. X        if (expdebug)
  745. X            fprintf(stderr, "new group `%s'\n", group);
  746. X        ct = (struct ctl *)malloc(sizeof(struct ctl));
  747. X        if (ct == NULL)
  748. X            fail("out of memory for newsgroup `%s'", group);
  749. X        ct->groups = strsave(group);
  750. X        ct->ismod = UNMOD;    /* unknown -- treat it as mundane */
  751. X        fillin(ct);
  752. X        ct->next = ngs[hash];
  753. X        ngs[hash] = ct;
  754. X    }
  755. X    *slash = '/';
  756. X
  757. X    /* and decide */
  758. X    if (shouldgo(recdate, expdate, ct))
  759. X        return(ct->dir);
  760. X    else
  761. X        return(dont);
  762. X}
  763. X
  764. X/*
  765. X - shouldgo - should article with these dates expire now?
  766. X */
  767. Xint                /* predicate */
  768. Xshouldgo(recdate, expdate, ct)
  769. Xtime_t recdate;
  770. Xtime_t expdate;
  771. Xregister struct ctl *ct;
  772. X{
  773. X    if (recdate >= ct->retain)    /* within retention period */
  774. X        return(0);
  775. X    if (recdate <= ct->purge)    /* past purge date */
  776. X        return(1);
  777. X    if (expdate != NODATE) {
  778. X        if (now >= expdate)    /* past its explicit date */
  779. X            return(1);
  780. X        else
  781. X            return(0);
  782. X    } else {
  783. X        if (recdate < ct->normal)    /* past default date */
  784. X            return(1);
  785. X        else
  786. X            return(0);
  787. X    }
  788. X    /* NOTREACHED */
  789. X}
  790. X
  791. X/*
  792. X - expire - expire an article
  793. X */
  794. Xvoid
  795. Xexpire(name, dir)
  796. Xchar *name;
  797. Xchar *dir;
  798. X{
  799. X    register char *old;
  800. X    register char *new;
  801. X    struct stat stbuf;
  802. X
  803. X    if (testing) {
  804. X        if (dir != NULL)
  805. X            fprintf(stderr, "copy %s %s ; ", name, dir);
  806. X        fprintf(stderr, "remove %s\n", name);
  807. X        return;
  808. X    }
  809. X
  810. X    old = strsave(artfile(name));
  811. X    if (dir != NULL) {
  812. X        if (*dir == '=') {
  813. X            errno = 0;
  814. X            new = strrchr(name, '/');
  815. X            if (new == NULL)
  816. X                fail("no slash in `%s'", name);
  817. X            new++;
  818. X            new = str3save(dir+1, "/", new);
  819. X        } else
  820. X            new = str3save(dir, "/", name);
  821. X        /* cp() usually succeeds, so try it before getting fancy */
  822. X        if (cp(old, new) < 0) {
  823. X            if (stat(old, &stbuf) < 0) {
  824. X                nmissing++;
  825. X                free(old);
  826. X                free(new);
  827. X                return;        /* nonexistent */
  828. X            }
  829. X            if (*dir != '=')
  830. X                mkparents(name, dir);
  831. X            if (cp(old, new) < 0) {
  832. X                warning("can't archive `%s'", name);
  833. X                free(old);
  834. X                free(new);
  835. X                return;        /* without removing it */
  836. X            }
  837. X        }
  838. X        free(new);
  839. X        narched++;
  840. X    }
  841. X    if (unlink(old) < 0) {
  842. X        if (errno != ENOENT)
  843. X            warning("can't remove `%s'", name);
  844. X        else
  845. X            nmissing++;
  846. X    } else if (dir == NULL)
  847. X        njunked++;
  848. X    free(old);
  849. X}
  850. X
  851. X/*
  852. X - cp - try to copy an article
  853. X */
  854. Xint                /* 0 success, -1 failure */
  855. Xcp(src, dst)
  856. Xchar *src;            /* absolute pathnames */
  857. Xchar *dst;
  858. X{
  859. X    register int ret;
  860. X    register int count;
  861. X    register int in, out;
  862. X    register int firstblock = 1;
  863. X
  864. X    in = open(src, 0);
  865. X    if (in < 0)
  866. X        return(-1);
  867. X    out = creat(dst, 0666);
  868. X    if (out < 0) {
  869. X        (void) close(in);
  870. X        return(-1);
  871. X    }
  872. X
  873. X    while ((count = read(in, abuf, sizeof(abuf))) > 0) {
  874. X        ret = write(out, abuf, count);
  875. X        if (ret != count)
  876. X            fail("write error in copying `%s'", src);
  877. X        if (firstblock) {
  878. X            getsubj(abuf, count);
  879. X            firstblock = 0;
  880. X        }
  881. X    }
  882. X    if (count < 0)
  883. X        fail("read error in copying `%s'", src);
  884. X
  885. X    (void) close(in);
  886. X    euclose(out, dst);
  887. X    return(0);
  888. X}
  889. X
  890. X/*
  891. X - getsubj - try to find the Subject: line in a buffer
  892. X *
  893. X * Result goes in "subject", and is never empty.  Tabs become spaces,
  894. X * since they are the output delimiters.
  895. X */
  896. Xvoid
  897. Xgetsubj(buf, bsize)
  898. Xchar *buf;
  899. Xint bsize;
  900. X{
  901. X    register char *scan;
  902. X    register char *limit;
  903. X    register int len;
  904. X    register int clipped;
  905. X    static char sline[] = "Subject:";
  906. X
  907. X    len = strlen(sline);
  908. X    limit = buf + bsize - len;
  909. X    for (scan = buf; scan < limit; scan++)
  910. X        if (STREQN(scan, sline, len) &&
  911. X                (scan == buf || *(scan-1) == '\n')) {
  912. X            scan += len;
  913. X            for (limit = scan; limit < buf+bsize; limit++)
  914. X                if (*limit == '\n')
  915. X                    break;
  916. X            while (scan < limit && isspace(*scan))
  917. X                scan++;
  918. X            len = limit-scan;
  919. X            clipped = 0;
  920. X            if (len > sizeof(subject)-1) {
  921. X                len = sizeof(subject) - 1 - strlen("...");
  922. X                clipped = 1;
  923. X            }
  924. X            if (len > 0) {
  925. X                (void) strncpy(subject, scan, len);
  926. X                subject[len] = '\0';
  927. X            } else
  928. X                (void) strcpy(subject, "???");
  929. X            if (clipped)
  930. X                (void) strcat(subject, "...");
  931. X            for (scan = strchr(subject, '\t'); scan != NULL;
  932. X                    scan = strchr(scan+1, '\t'))
  933. X                *scan = ' ';
  934. X            return;
  935. X        } else if (*scan == '\n' && scan+1 < limit && *(scan+1) == '\n')
  936. X            break;        /* empty line terminates header */
  937. X
  938. X    /* didn't find one -- fill in *something* */
  939. X    (void) strcpy(subject, "???");
  940. X}
  941. X
  942. X/*
  943. X - mkparents - try to make directories for archiving an article
  944. X *
  945. X * Assumes it can mess with first argument if it puts it all back at the end.
  946. X */
  947. Xvoid
  948. Xmkparents(art, dir)
  949. Xchar *art;            /* name relative to dir */
  950. Xchar *dir;
  951. X{
  952. X    register char *cmd;
  953. X    register char *ocmd;
  954. X    register char *p;
  955. X
  956. X    p = strchr(art, '/');
  957. X    cmd = str3save(binfile((char *)NULL), "/expire/mkadir ", dir);
  958. X    while (p != NULL) {
  959. X        *p = '\0';
  960. X        ocmd = cmd;
  961. X        cmd = str3save(ocmd, " ", art);
  962. X        free(ocmd);
  963. X        *p = '/';
  964. X        p = strchr(p+1, '/');
  965. X    }
  966. X    (void) system(cmd);
  967. X    free(cmd);
  968. X}
  969. X
  970. Xchar *months[12] = {
  971. X    "Jan",
  972. X    "Feb",
  973. X    "Mar",
  974. X    "Apr",
  975. X    "May",
  976. X    "Jun",
  977. X    "Jul",
  978. X    "Aug",
  979. X    "Sep",
  980. X    "Oct",
  981. X    "Nov",
  982. X    "Dec",
  983. X};
  984. X
  985. X/*
  986. X - printstuff - print information about an expiring article
  987. X */
  988. Xvoid
  989. Xprintstuff(msgid, name, recdate)
  990. Xchar *msgid;
  991. Xchar *name;
  992. Xtime_t recdate;
  993. X{
  994. X    struct tm *gmt;
  995. X
  996. X    gmt = gmtime(&recdate);
  997. X    printf("%s\t%s\t%d-%s-%d\t%s\n", name, msgid, gmt->tm_mday,
  998. X            months[gmt->tm_mon], gmt->tm_year+1900, subject);
  999. X}
  1000. X
  1001. X/*
  1002. X - split - divide a line into fields, like awk split()
  1003. X */
  1004. Xint                /* number of fields */
  1005. Xsplit(line, fields, nfmax, sep)
  1006. Xchar *line;
  1007. Xchar *fields[];
  1008. Xint nfmax;
  1009. Xchar *sep;
  1010. X{
  1011. X    register int i;
  1012. X    register char *p;
  1013. X
  1014. X    i = 0;
  1015. X    for (p = strtok(line, sep); p != NULL; p = strtok((char *)NULL, sep)) {
  1016. X        if (i < nfmax)
  1017. X            fields[i] = p;
  1018. X        i++;
  1019. X    }
  1020. X
  1021. X    return(i);
  1022. X}
  1023. X
  1024. X/*
  1025. X - eufopen - fopen, with fail if doesn't succeed
  1026. X */
  1027. XFILE *
  1028. Xeufopen(name, mode)
  1029. Xchar *name;
  1030. Xchar *mode;
  1031. X{
  1032. X    FILE *f;
  1033. X    static char grump[50] = "can't open `%s' for `";
  1034. X
  1035. X    f = fopen(name, mode);
  1036. X    if (f == NULL) {
  1037. X        (void) strcat(grump, mode);
  1038. X        (void) strcat(grump, "'");
  1039. X        fail(grump, name);
  1040. X    }
  1041. X    return(f);
  1042. X}
  1043. X
  1044. X/*
  1045. X - eufclose - fclose with failure checking
  1046. X */
  1047. Xvoid
  1048. Xeufclose(f, name)
  1049. XFILE *f;
  1050. Xchar *name;
  1051. X{
  1052. X    if (nfclose(f) == EOF)
  1053. X        fail("error in closing file `%s'", name);
  1054. X}
  1055. X
  1056. X/*
  1057. X - euclose - close with failure checking
  1058. X */
  1059. Xvoid
  1060. Xeuclose(f, name)
  1061. Xint f;
  1062. Xchar *name;
  1063. X{
  1064. X    if (fsync(f) < 0 || close(f) < 0)
  1065. X        fail("error in closing file `%s'", name);
  1066. X}
  1067. X
  1068. X/*
  1069. X - checkdir - check that a directory is real, writeable, and full pathname
  1070. X */
  1071. Xvoid                /* fail() if not */
  1072. Xcheckdir(dir)
  1073. Xchar *dir;
  1074. X{
  1075. X    struct stat stbuf;
  1076. X#    define    GRUMP(a,b)    {if (checkonly) {warning(a, b);return;} else fail(a, b);}
  1077. X
  1078. X    if (*dir == '=')    /* disregard leading '=' */
  1079. X        dir++;
  1080. X    errno = 0;
  1081. X    if (stat(dir, &stbuf) < 0 || (stbuf.st_mode&S_IFMT) != S_IFDIR)
  1082. X        GRUMP("`%s' is not a directory", dir);
  1083. X    if (access(dir, 02) < 0)
  1084. X        GRUMP("directory `%s' not writeable", dir);
  1085. X    if (dir[0] != '/')
  1086. X        GRUMP("directory `%s' not an absolute pathname", dir);
  1087. X}
  1088. X
  1089. X/*
  1090. X - back - get a date n days back, with overflow check
  1091. X *
  1092. X * Requires that "now" be set first.
  1093. X */
  1094. Xtime_t
  1095. Xback(ndays)
  1096. Xdouble ndays;
  1097. X{
  1098. X    if (ndays > now / DAY)    /* past beginning of time */
  1099. X        return((time_t)0);
  1100. X    return(now - ndays*DAY);
  1101. X}
  1102. X
  1103. X/*
  1104. X - printlists - print control lists for debugging
  1105. X */
  1106. Xvoid
  1107. Xprintlists()
  1108. X{
  1109. X    register int i;
  1110. X    register struct ctl *ct;
  1111. X
  1112. X    fprintf(stderr, "control file:\n");
  1113. X    for (ct = ctls; ct != NULL; ct = ct->next)
  1114. X        pctl(ct);
  1115. X    fprintf(stderr, "\n");
  1116. X
  1117. X    for (i = 0; i < NHASH; i++)
  1118. X        if (ngs[i] != NULL) {
  1119. X            fprintf(stderr, "list %d:\n", i);
  1120. X            for (ct = ngs[i]; ct != NULL; ct = ct->next)
  1121. X                pctl(ct);
  1122. X        }
  1123. X    fprintf(stderr, "\n");
  1124. X}
  1125. X
  1126. X/*
  1127. X - pctl - print one control-list entry
  1128. X */
  1129. Xvoid
  1130. Xpctl(ct)
  1131. Xregister struct ctl *ct;
  1132. X{
  1133. X#    define    DAYS(x)    ((now-(x))/DAY)
  1134. X
  1135. X    fprintf(stderr, "%s(%c) %.2f-%.2f-%.2f %s\n", ct->groups, ct->ismod,
  1136. X            DAYS(ct->retain), DAYS(ct->normal), DAYS(ct->purge),
  1137. X            (ct->dir == NULL) ? "(null)" : ct->dir);
  1138. X}
  1139. X
  1140. X/*
  1141. X - unprivileged - no-op needed to keep the pathname stuff happy
  1142. X */
  1143. Xvoid
  1144. Xunprivileged()
  1145. X{
  1146. X}
  1147. X
  1148. X/*
  1149. X - fail - call errunlock, possibly after cleanup
  1150. X */
  1151. Xvoid
  1152. Xfail(s1, s2)
  1153. Xchar *s1;
  1154. Xchar *s2;
  1155. X{
  1156. X    if (spacetight) {
  1157. X        cd(ctlfile((char *)NULL));
  1158. X        (void) unlink("history.n");
  1159. X        (void) unlink("history.n.dir");
  1160. X        (void) unlink("history.n.pag");
  1161. X    }
  1162. X    errunlock(s1, s2);
  1163. X    /* NOTREACHED */
  1164. X}
  1165. X
  1166. X/*
  1167. X - readline - read history line (sans newline), with locking when we hit EOF
  1168. X *
  1169. X * Minor flaw:  will lose a last line which lacks a newline.
  1170. X */
  1171. Xchar *                /* NULL is EOF */
  1172. Xreadline(fd)
  1173. Xint fd;                /* Note descriptor, not FILE *. */
  1174. X{
  1175. X    register char *line;
  1176. X    register int nline;
  1177. X    register char *p;
  1178. X    register int c;
  1179. X    register int n;
  1180. X    extern void refill();
  1181. X
  1182. X    nline = 100;        /* reasonable starter */
  1183. X    line = malloc(nline);
  1184. X    if (line == NULL)
  1185. X        fail("out of space when reading history", "");
  1186. X    p = line;
  1187. X
  1188. X    for (;;) {
  1189. X        if (rlnleft <= 0) {
  1190. X            refill(fd);
  1191. X            if (rlnleft <= 0)    /* refill gave up. */
  1192. X                return(NULL);
  1193. X        }
  1194. X        c = *rest++;
  1195. X        rlnleft--;
  1196. X
  1197. X        if (c == '\n') {
  1198. X            *p++ = '\0';
  1199. X            return(line);
  1200. X        }
  1201. X        if (p - line >= nline - 1) {
  1202. X            nline = (nline * 3) / 2;
  1203. X            n = p - line;
  1204. X            line = realloc(line, nline);
  1205. X            if (line == NULL)
  1206. X                fail("out of memory in readline", "");
  1207. X            p = line + n;
  1208. X        }
  1209. X        *p++ = c;
  1210. X    }
  1211. X    /* NOTREACHED */
  1212. X}
  1213. X
  1214. X/*
  1215. X - refill - refill readline's buffer, with locking on EOF
  1216. X */
  1217. Xvoid
  1218. Xrefill(fd)
  1219. Xint fd;
  1220. X{
  1221. X    register int ret;
  1222. X
  1223. X    /* Just in case... */
  1224. X    if (rlnleft > 0)
  1225. X        return;
  1226. X
  1227. X    /* Try ordinary read. */
  1228. X    ret = read(fd, rlbuf, (int)sizeof(rlbuf));
  1229. X    if (ret < 0)
  1230. X        fail("read error in history", "");
  1231. X    if (ret > 0) {
  1232. X        rlnleft = ret;
  1233. X        rest = rlbuf;
  1234. X        return;
  1235. X    }
  1236. X
  1237. X    /* EOF. */
  1238. X    if (nlocked)
  1239. X        return;        /* We're really done. */
  1240. X
  1241. X    /* EOF but we haven't locked yet.  Lock and try again. */
  1242. X    (void) signal(SIGINT, (sigarg_t)SIG_IGN);
  1243. X    (void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
  1244. X    (void) signal(SIGHUP, (sigarg_t)SIG_IGN);
  1245. X    (void) signal(SIGTERM, (sigarg_t)SIG_IGN);
  1246. X    newslock();
  1247. X    nlocked = 1;
  1248. X    refill(fd);
  1249. X}
  1250. X
  1251. X/*
  1252. X - strvsave - sort of like strsave, but for a vector of strings
  1253. X */
  1254. Xchar *
  1255. Xstrvsave(v, nv, delim)
  1256. Xchar **v;
  1257. Xint nv;
  1258. Xchar delim;
  1259. X{
  1260. X    register char **p;
  1261. X    register int i;
  1262. X    register char *result;
  1263. X    register char *rp;
  1264. X    register int len;
  1265. X
  1266. X    if (nv <= 0)
  1267. X        return(strsave(""));
  1268. X
  1269. X    len = 0;
  1270. X    for (i = nv, p = v; i > 0; i--, p++)
  1271. X        if (*p != NULL)
  1272. X            len += strlen(*p) + 1;
  1273. X    result = malloc(len);
  1274. X    if (result == NULL)
  1275. X        fail("out of memory in strvsave", "");
  1276. X
  1277. X    rp = result;
  1278. X    for (i = nv, p = v; i > 0; i--, p++)
  1279. X        if (*p != NULL) {
  1280. X            (void) strcpy(rp, *p);
  1281. X            rp += strlen(rp);
  1282. X            *rp++ = delim;
  1283. X        }
  1284. X    rp--;
  1285. X    *rp = '\0';
  1286. X
  1287. X    return(result);
  1288. X}
  1289. !
  1290. echo 'expire/histdups':
  1291. sed 's/^X//' >'expire/histdups' <<'!'
  1292. X# Awk program to merge history lines for the same article in a sorted history
  1293. X# file (such as is generated during the mkhistory processing).
  1294. XBEGIN { FS = "\t" ; OFS = "\t" ; mesgid = "" }
  1295. X{
  1296. X    if ($1 != mesgid) {
  1297. X        if (mesgid != "")
  1298. X            print mesgid, dates, names
  1299. X        mesgid = $1
  1300. X        dates = $2
  1301. X        names = $3
  1302. X    } else
  1303. X        names = names " " $3
  1304. X}
  1305. XEND { print mesgid, dates, names }
  1306. !
  1307. echo 'expire/histinfo.c':
  1308. sed 's/^X//' >'expire/histinfo.c' <<'!'
  1309. X/*
  1310. X * histinfo - print history file lines for articles named on stdin
  1311. X */
  1312. X
  1313. X#include <stdio.h>
  1314. X#include <sys/types.h>
  1315. X#include <sys/stat.h>        /* for modified time (date received) */
  1316. X#include "config.h"
  1317. X
  1318. X#define MAXLINE 1024
  1319. X#define STRLEN(s) (sizeof(s) - 1)    /* s must be a char array */
  1320. X
  1321. Xchar *progname;
  1322. Xint debug;
  1323. X
  1324. XFILE *efopen();
  1325. X
  1326. Xchar *spdir;
  1327. Xint spdirlen;
  1328. X
  1329. X/*
  1330. X * main - parse arguments and handle options
  1331. X */
  1332. Xmain(argc, argv)
  1333. Xint argc;
  1334. Xchar *argv[];
  1335. X{
  1336. X    int c;
  1337. X    int errflg = 0;
  1338. X    FILE *in;
  1339. X    char inname[MAXLINE];
  1340. X    extern int optind;
  1341. X    extern char *optarg;
  1342. X
  1343. X    progname = argv[0];
  1344. X    while ((c = getopt(argc, argv, "d")) != EOF)
  1345. X        switch (c) {
  1346. X        case 'd':
  1347. X            ++debug;
  1348. X            break;
  1349. X        default:
  1350. X            errflg++;
  1351. X            break;
  1352. X        }
  1353. X    if (optind < argc || errflg) {
  1354. X        (void) fprintf(stderr, "usage: %s [-d]\n", progname);
  1355. X        exit(2);
  1356. X    }
  1357. X
  1358. X    spdir = artfile((char *)NULL);
  1359. X    spdirlen = strlen(spdir);
  1360. X    
  1361. X    while (fgets(inname, sizeof(inname), stdin) != NULL) {
  1362. X        inname[strlen(inname)-1] = '\0';    /* kill newline */
  1363. X        in = efopen(inname, "r");
  1364. X        process(in, inname);
  1365. X        (void) fclose(in);
  1366. X    }
  1367. X    exit(0);
  1368. X}
  1369. X
  1370. X/*
  1371. X * process - process input file
  1372. X */
  1373. Xprocess(in, inname)
  1374. XFILE *in;
  1375. Xchar *inname;
  1376. X{
  1377. X    char *nl, *files;
  1378. X    char line[MAXLINE], msgid[MAXLINE], expiry[MAXLINE];
  1379. X    char datercv[30];
  1380. X    struct stat statb;
  1381. X    static char msgidnm[] =  "Message-ID: ";
  1382. X    static char expnm[] =    "Expires: ";
  1383. X    register char *p;
  1384. X    extern char *strrchr();
  1385. X    extern char *strchr();
  1386. X    extern char *strcpy();
  1387. X
  1388. X    /* set defaults */
  1389. X    (void) strcpy(expiry, "-");
  1390. X    (void) strcpy(msgid, "<swill@trash>");
  1391. X
  1392. X    /* read until EOF or blank line (end of headers) */
  1393. X    while (fgets(line, sizeof line, in) != NULL && strcmp(line, "\n") != 0) {
  1394. X        if ((nl = strrchr(line, '\n')) != NULL)
  1395. X            *nl = '\0';            /* trim newline */
  1396. X        if (strncmp(line, msgidnm, STRLEN(msgidnm)) == 0)
  1397. X            (void) strcpy(msgid, line+STRLEN(msgidnm));
  1398. X        else if (strncmp(line, expnm, STRLEN(expnm)) == 0)
  1399. X            (void) strcpy(expiry, line+STRLEN(expnm));
  1400. X    }
  1401. X
  1402. X    /* generate the file name */
  1403. X    files = inname;
  1404. X    if (strncmp(files, spdir, spdirlen) == 0 &&
  1405. X        files[spdirlen] == '/')
  1406. X        files += spdirlen + 1;    /* skip spool dir. & slash */
  1407. X
  1408. X    /* generate the date received */
  1409. X    (void) fstat(fileno(in), &statb);
  1410. X    (void) sprintf(datercv, "%ld", statb.st_mtime);
  1411. X
  1412. X    /* de-tab the message id */
  1413. X    for (p = strchr(msgid, '\t'); p != NULL; p = strchr(p, '\t'))
  1414. X        *p = ' ';
  1415. X
  1416. X    /* whomp out the history line */
  1417. X    (void) fputs(msgid, stdout);
  1418. X    (void) putchar('\t');
  1419. X    (void) fputs(datercv, stdout);
  1420. X    (void) putchar('~');
  1421. X    (void) fputs(expiry, stdout);
  1422. X    (void) putchar('\t');
  1423. X    (void) fputs(files, stdout);
  1424. X    (void) putchar('\n');
  1425. X    (void) fflush(stdout);
  1426. X}
  1427. X
  1428. X/*
  1429. X * unprivileged - no-op to keep pathname stuff happy
  1430. X */
  1431. Xvoid
  1432. Xunprivileged()
  1433. X{
  1434. X}
  1435. !
  1436. echo 'expire/histslash.c':
  1437. sed 's/^X//' >'expire/histslash.c' <<'!'
  1438. X/*
  1439. X * Convert slashed filenames to dotted group/article names in a history
  1440. X * file, for use in mkhistory.  Input comes only from stdin.
  1441. X */
  1442. X#include <stdio.h>
  1443. X#include <assert.h>
  1444. X
  1445. Xchar *progname = "histslash";
  1446. X
  1447. Xchar buf[4096];            /* paranoia -- ought to be lots */
  1448. X
  1449. Xmain()
  1450. X{
  1451. X    register char *scan;
  1452. X    register char *last;
  1453. X    extern char *strchr();
  1454. X
  1455. X    while (fgets(buf, sizeof(buf), stdin) != NULL) {
  1456. X        scan = strchr(buf, '\t');
  1457. X        scan = strchr(scan+1, '\t');
  1458. X        scan++;
  1459. X        last = NULL;
  1460. X        while (*scan != '\0') {
  1461. X            if (*scan == '/') {
  1462. X                *scan = '.';
  1463. X                last = scan;
  1464. X            }
  1465. X            if (*scan == ' ' || *scan == '\n') {
  1466. X                assert(last != NULL);
  1467. X                *last = '/';
  1468. X            }
  1469. X            scan++;
  1470. X        }
  1471. X        fputs(buf, stdout);
  1472. X    }
  1473. X}
  1474. !
  1475. echo 'expire/lowest.c':
  1476. sed 's/^X//' >'expire/lowest.c' <<'!'
  1477. X/*
  1478. X * lowest - print the number of the lowest article in a directory
  1479. X */
  1480. X
  1481. X#include <stdio.h>
  1482. X#include <sys/types.h>
  1483. X#include <dirent.h>
  1484. X
  1485. X#define    HUGE    999999999L    /* Bigger than any valid article number. */
  1486. X
  1487. Xchar *progname;
  1488. X
  1489. X/*
  1490. X - main - parse arguments and handle options
  1491. X */
  1492. Xmain(argc, argv)
  1493. Xint argc;
  1494. Xchar *argv[];
  1495. X{
  1496. X    DIR *d;
  1497. X    register struct dirent *dp;
  1498. X    long lowest = HUGE;
  1499. X    long this;
  1500. X    extern long atol();
  1501. X
  1502. X    progname = argv[0];
  1503. X
  1504. X    if (argc != 2) {
  1505. X        fprintf(stderr, "usage: %s directory\n", progname);
  1506. X        exit(2);
  1507. X    }
  1508. X
  1509. X    d = opendir(argv[1]);
  1510. X    if (d == NULL) {
  1511. X        fprintf(stderr, "%s: can't read directory %s\n", progname,
  1512. X                                argv[1]);
  1513. X        exit(1);
  1514. X    }
  1515. X    while ((dp = readdir(d)) != NULL) {
  1516. X        if (strspn(dp->d_name, "0123456789") == strlen(dp->d_name)) {
  1517. X            this = atol(dp->d_name);
  1518. X            if (this < lowest)
  1519. X                lowest = this;
  1520. X        }
  1521. X    }
  1522. X    closedir(d);
  1523. X
  1524. X    if (lowest != HUGE)
  1525. X        printf("%ld\n", lowest);
  1526. X}
  1527. !
  1528. echo 'expire/mkdbm.c':
  1529. sed 's/^X//' >'expire/mkdbm.c' <<'!'
  1530. X/*
  1531. X * mkdbm - rebuild dbm file for a history file
  1532. X *
  1533. X * History file on standard input; the dbm database is generated as
  1534. X * hist.dir and hist.pag.
  1535. X */
  1536. X#include <stdio.h>
  1537. X#include <string.h>
  1538. X#include <ctype.h>
  1539. X
  1540. Xchar buf[4096];            /* ought to be plenty */
  1541. X
  1542. X
  1543. Xchar *progname = "mkdbm";
  1544. Xtypedef struct { char *dptr; int dsize; } datum;
  1545. X
  1546. Xmain()
  1547. X{
  1548. X    register char *scan;
  1549. X    long place;
  1550. X    datum lhs;
  1551. X    datum rhs;
  1552. X    register int ret;
  1553. X
  1554. X    close(creat("hist.dir", 0666));
  1555. X    close(creat("hist.pag", 0666));
  1556. X    dbminit("hist");
  1557. X
  1558. X    for (;;) {
  1559. X        place = ftell(stdin);
  1560. X        if (fgets(buf, sizeof(buf), stdin) == NULL)
  1561. X            break;
  1562. X
  1563. X        scan = strchr(buf, '\t');
  1564. X        if (scan == NULL || buf[strlen(buf)-1] != '\n') {
  1565. X            fprintf(stderr, "bad format: %s", buf);
  1566. X            exit(1);
  1567. X        }
  1568. X        *scan = '\0';
  1569. X
  1570. X        lhs.dptr = buf;
  1571. X        lhs.dsize = strlen(buf) + 1;
  1572. X        rhs.dptr = (char *)&place;
  1573. X        rhs.dsize = sizeof place;
  1574. X        ret = store(lhs, rhs);
  1575. X        if (ret < 0)
  1576. X            fprintf(stderr, "dbm failure '%s' @ %ld\n", buf, place);
  1577. X    }
  1578. X    exit(0);
  1579. X}
  1580. !
  1581. echo 'expire/mkhistory':
  1582. sed 's/^X//' >'expire/mkhistory' <<'!'
  1583. X#! /bin/sh
  1584. X# mkhistory - rebuild history file and friends
  1585. X
  1586. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  1587. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  1588. X
  1589. XPATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
  1590. Xumask $NEWSUMASK
  1591. X
  1592. Xlock="$NEWSCTL/LOCK"        # modify name as appropriate
  1593. Xltemp="$NEWSCTL/L.$$"
  1594. Xecho $$ >$ltemp
  1595. Xtrap "rm -f $ltemp ; exit 0" 0 1 2 15
  1596. Xwhile true
  1597. Xdo
  1598. X    if newslock $ltemp $lock
  1599. X    then
  1600. X        trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
  1601. X        break
  1602. X    fi
  1603. X    sleep 30
  1604. Xdone
  1605. X
  1606. Xcd $NEWSARTS
  1607. Xfind `ls | egrep -v '\.'` -type f -name '[0-9]*' -print | histinfo | sort |
  1608. X    awk -f $NEWSBIN/expire/histdups | histslash >$NEWSCTL/history.n
  1609. X
  1610. Xcd $NEWSCTL
  1611. Xif egrep '^<swill@trash>    ' history.n >/dev/null
  1612. Xthen
  1613. X    echo "$0: <swill@trash> found in history.n -- aborting" >&2
  1614. X    exit 1
  1615. Xfi
  1616. Xmkdbm <history.n
  1617. Xmv history history.o &&        # install new ASCII history file
  1618. Xmv history.n history &&
  1619. Xrm -f history.pag &&        # and related dbm files
  1620. Xrm -f history.dir &&
  1621. Xmv hist.pag history.pag && mv hist.dir history.dir
  1622. !
  1623. echo 'expire/upact':
  1624. sed 's/^X//' >'expire/upact' <<'!'
  1625. X#! /bin/sh
  1626. X# Update 3rd field (minimum art. #) of a 4-field active file.
  1627. X
  1628. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  1629. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  1630. X
  1631. XPATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
  1632. Xumask $NEWSUMASK
  1633. X
  1634. Xcd $NEWSCTL || { echo "$0: can't cd to $NEWSCTL" >&2; exit 1; }
  1635. X
  1636. X# check active file format
  1637. Xset ""`sed 1q active`
  1638. Xcase $# in
  1639. X4)    ;;
  1640. X*)    echo "$0: active file has other than 4 fields" >&2
  1641. X    exit 1 ;;
  1642. Xesac
  1643. X
  1644. X# lock news system
  1645. Xlock="$NEWSCTL/LOCK"
  1646. Xltemp="$NEWSCTL/L.$$"
  1647. Xecho $$ >$ltemp
  1648. Xtrap "rm -f $ltemp ; exit 0" 0 1 2 15
  1649. Xwhile true
  1650. Xdo
  1651. X    if newslock $ltemp $lock
  1652. X    then
  1653. X        trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
  1654. X        break
  1655. X    fi
  1656. X    sleep 30
  1657. Xdone
  1658. X
  1659. Xwhile read group max min fourth
  1660. Xdo
  1661. X    dir=`echo $group | tr . / `    # map ng name to directory name
  1662. X    min=
  1663. X    if test -d $NEWSARTS/$dir
  1664. X    then
  1665. X        # min=`lowest $NEWSARTS/$dir`
  1666. X        min=`ls $NEWSARTS/$dir | egrep '^[0-9]+$' | sort -nr | tail -1`
  1667. X    fi
  1668. X    case "$min" in        # no files, so
  1669. X    "")    min=$max ;;    # use maximum
  1670. X    esac
  1671. X    case "$min" in
  1672. X    [0-9]|[0-9][0-9]|[0-9][0-9][0-9]|[0-9][0-9][0-9][0-9])    # short
  1673. X        min=`expr 00000$min : '.*\(.....\)$'` ;;
  1674. X    esac
  1675. X
  1676. X    echo $group $max $min $fourth
  1677. Xdone <active >active.new
  1678. X
  1679. X# replace active, carefully
  1680. Xrm -f active.old
  1681. Xln active active.old
  1682. Xmv active.new active
  1683. X
  1684. Xexit 0
  1685. !
  1686. echo 'expire/superkludge':
  1687. sed 's/^X//' >'expire/superkludge' <<'!'
  1688. X#! /bin/sh
  1689. X# superkludge - implement the stupid Supersedes header for specific groups
  1690. X
  1691. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  1692. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  1693. X
  1694. XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
  1695. Xumask $NEWSUMASK
  1696. Xtrap "rm -f /tmp/sup*$$ ; exit 0" 0 1 2
  1697. X
  1698. Xverbose=
  1699. Xcase "$1" in
  1700. X-v)    verbose=yes ; shift ;;
  1701. Xesac
  1702. X
  1703. Xfor ng
  1704. Xdo
  1705. X    dir=`echo $ng | sed 's;\.;/;g'`
  1706. X    if test ! -d $NEWSARTS/$dir
  1707. X    then
  1708. X        echo "$0: no directory for newsgroup \`$ng'" >&2
  1709. X        exit 1
  1710. X    fi
  1711. X    cd $NEWSARTS/$dir
  1712. X    >/tmp/sup$$
  1713. X    for f in `ls | egrep '^[0-9]+$'`
  1714. X    do
  1715. X        awk 'BEGIN { mid = "xxx" }
  1716. X            /^$/ { exit }    # goes to END
  1717. X            /^Supersedes:[     ]/ { sup = $2 }
  1718. X            /^Message-ID:[     ]/ { mid = $2 }
  1719. X            END { print FILENAME, mid, sup ; exit }' $f >>/tmp/sup$$
  1720. X    done
  1721. X    awk 'NF > 3 || $2 !~ /^<.*>$/ || $3 !~ /^(<.*>)?$/' /tmp/sup$$ >/tmp/supx$$
  1722. X    if test -s /tmp/supx$$
  1723. X    then
  1724. X        echo "$0: message-id format problems:" >&2
  1725. X        cat /tmp/supx$$ >&2
  1726. X        exit 1
  1727. X    fi
  1728. X    awk 'NF == 3 { print $3 }' /tmp/sup$$ | sort >/tmp/supd$$
  1729. X    sort +1 /tmp/sup$$ -o /tmp/sup$$
  1730. X    join -j2 2 -o 2.1 /tmp/supd$$ /tmp/sup$$ >/tmp/supx$$
  1731. X    rm -f `cat /tmp/supx$$`
  1732. X    if test " $verbose" = " yes"
  1733. X    then
  1734. X        echo "`wc -l </tmp/supx$$` superseded in $ng"
  1735. X    fi
  1736. Xdone
  1737. !
  1738. echo 'expire/dircheck':
  1739. sed 's/^X//' >'expire/dircheck' <<'!'
  1740. X#! /bin/sh
  1741. X# dircheck -- checker for expire regression testing
  1742. X
  1743. Xcase "$1" in
  1744. X-n)    invert=yes ; shift ;;
  1745. Xesac
  1746. X
  1747. Xfor f in `sed 's/.*    //;s/\./\//g'`
  1748. Xdo
  1749. X    if test " $invert" = " "
  1750. X    then
  1751. X        if test ! -f $1/$f
  1752. X        then
  1753. X            echo "cannot find $1/$f" >&2
  1754. X            exit 1
  1755. X        fi
  1756. X        it="`cat $1/$f`"
  1757. X        if test " $it" = " $f" || expr " $it" : ".*:$f:.*" >/dev/null
  1758. X        then
  1759. X            : okay
  1760. X        else
  1761. X            echo "contents of $1/$f are wrong" >&2
  1762. X            exit 1
  1763. X        fi
  1764. X    else
  1765. X        if test -f $1/$f
  1766. X        then
  1767. X            echo "found $1/$f, shouldn't have" >&2
  1768. X            exit 1
  1769. X        fi
  1770. X    fi
  1771. Xdone
  1772. Xexit 0                # for stupid shells
  1773. !
  1774. echo 'expire/tgood':
  1775. sed 's/^X//' >'expire/tgood' <<'!'
  1776. Xcopy foo/2 P/arch ; remove foo/2
  1777. Xcopy foo/3 P/arch ; remove foo/3
  1778. Xcopy bar/4 P/arch2 ; remove bar/4
  1779. Xcopy bar/ugh/5 P/arch ; remove bar/ugh/5
  1780. Xremove urp/6
  1781. Xremove urp/8
  1782. Xremove urp/9
  1783. Xcopy foo/11 P/arch ; remove foo/11
  1784. Xcopy mod/mod/12 P/arch ; remove mod/mod/12
  1785. Xremove mod/unmod/14
  1786. Xremove mod/unmod/15
  1787. Xcopy bletch/17 =P/arch3/bletch ; remove bletch/17
  1788. Xremove urp/98
  1789. Xcopy bar/99 P/arch2 ; remove bar/99
  1790. Xremove urp/99
  1791. !
  1792. echo 'expire/pgood':
  1793. sed 's/^X//' >'expire/pgood' <<'!'
  1794. Xfoo/2    <will2>    1-Jan-1970    ???
  1795. Xfoo/3    <will3>    1-Jan-1970    ???
  1796. Xbar/4    <two4>    1-Jan-1970    yes
  1797. Xbar/ugh/5    <will5>    1-Jan-1970    ???
  1798. Xfoo/11    <will11>    1-Jan-1970    ???
  1799. Xmod/mod/12    <will12>    1-Jan-1970    ???
  1800. Xbletch/17    <three17>    1-Jan-1970    ???
  1801. Xbar/99    <multi99>    1-Jan-1970    ???
  1802. !
  1803. echo 'expire/doexpire':
  1804. sed 's/^X//' >'expire/doexpire' <<'!'
  1805. X#! /bin/sh
  1806. X# doexpire - overall administration for expire
  1807. X
  1808. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  1809. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  1810. X
  1811. XPATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
  1812. Xumask $NEWSUMASK
  1813. X
  1814. Xlock="$NEWSCTL/LOCKexpire"
  1815. Xltemp="$NEWSCTL/L.$$"
  1816. Xecho $$ >$ltemp
  1817. Xtrap "rm -f $ltemp ; exit 0" 0 1 2 15
  1818. Xif newslock $ltemp $lock
  1819. Xthen
  1820. X    trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
  1821. Xelse
  1822. X    echo "$0: expire apparently already running" | mail "$NEWSMASTER"
  1823. X    exit 1
  1824. Xfi
  1825. X
  1826. Xcd $NEWSCTL
  1827. X
  1828. Xfirstctl=
  1829. Xfirstar=
  1830. Xwhile true
  1831. Xdo
  1832. X    size="`sizeof history history.pag history.dir`"
  1833. X    if test " `spacefor $size control`" -le 0
  1834. X    then
  1835. X        if test " $firstctl" = " "
  1836. X        then
  1837. X            echo "$0: trouble finding space for work files" |
  1838. X                            mail "$NEWSMASTER"
  1839. X            firstctl=n
  1840. X        fi
  1841. X    elif test " `spacefor 1 archive`" -le 0
  1842. X    then
  1843. X        if test " $firstar" = " "
  1844. X        then
  1845. X            echo "$0: trouble finding space for archiving" |
  1846. X                            mail "$NEWSMASTER"
  1847. X            firstar=n
  1848. X        fi
  1849. X    else            # enough space both places
  1850. X        break
  1851. X    fi
  1852. X    sleep 600        # and hope it will improve
  1853. Xdone
  1854. X
  1855. Xexpire $* $NEWSCTL/explist 2>/tmp/doex$$
  1856. Xif test -s /tmp/doex$$
  1857. Xthen
  1858. X    (echo 'expire problems:' ; cat /tmp/doex$$ ) | mail "$NEWSMASTER"
  1859. X    rm -f /tmp/doex$$
  1860. X    exit 1
  1861. Xfi
  1862. Xrm -f /tmp/doex$$
  1863. Xexit 0
  1864. !
  1865. echo 'h/alloc.h':
  1866. sed 's/^X//' >'h/alloc.h' <<'!'
  1867. X#ifndef ALLOC_H
  1868. X#define ALLOC_H
  1869. X
  1870. X#ifndef notdef
  1871. Xextern char *malloc(), *realloc(), *emalloc();
  1872. Xextern char *nemalloc(), *strsave(), *str3save();
  1873. X#else                /* notdef */
  1874. X/* setup for UT debugging malloc */
  1875. X#define MALLOC_TRACE
  1876. X#define TRACE
  1877. X#include "malloc.h"        /* defines CSRIMALLOC */
  1878. Xextern char *_nemalloc(), *_strsave(), *_str3save();
  1879. X#define nemalloc(x)        _nemalloc((x), __FILE__, __LINE__)
  1880. X#define strsave(x)        _strsave((x), __FILE__, __LINE__)    /* TODO: conflict with malloc.h; fix */
  1881. X#define str3save(x, y, z)    _str3save((x), y, z, __FILE__, __LINE__)
  1882. X#endif                /* notdef */
  1883. X
  1884. X#endif                /* ALLOC_H */
  1885. !
  1886. echo 'h/config.h':
  1887. sed 's/^X//' >'h/config.h' <<'!'
  1888. X/*
  1889. X * configuration-inquiry functions
  1890. X */
  1891. X
  1892. Xextern char *artfile();        /* article pathname, may be relative */
  1893. Xextern char *fullartfile();    /* article full pathname */
  1894. Xextern char *ctlfile();        /* control-file name */
  1895. Xextern char *binfile();        /* program pathname */
  1896. Xextern char *newspath();    /* PATH */
  1897. Xextern int newsumask();        /* umask */
  1898. Xextern char *newsmaster();    /* place to mail complaints to */
  1899. X
  1900. Xextern void cd();        /* chdir() with errunlock() on failure */
  1901. X
  1902. Xextern void unprivileged();    /* user-supplied privilege dropper */
  1903. !
  1904. echo 'h/README':
  1905. sed 's/^X//' >'h/README' <<'!'
  1906. XThis is C News header files.
  1907. !
  1908. echo 'h/fgetmfs.h':
  1909. sed 's/^X//' >'h/fgetmfs.h' <<'!'
  1910. X/* values for fgetmfs flag */
  1911. X#define CONT_NO 0        /* no continuations */
  1912. X#define CONT_NOSPC 1        /* continue & remove leading whitespace */
  1913. X#define CONT_SPC 2        /* continue & keep leading whitespace */
  1914. X
  1915. Xextern char *fgetmfs();            /* internal interface */
  1916. X
  1917. X/* external interfaces */
  1918. X#define fgetms(fp) fgetmfs(fp, -1, CONT_NO)    /* unbounded read */
  1919. X#define cfgetms(fp) fgetmfs(fp, -1, CONT_NOSPC)    /* unbounded read w continuation */
  1920. !
  1921. echo 'h/libc.h':
  1922. sed 's/^X//' >'h/libc.h' <<'!'
  1923. X
  1924. X#ifndef LIBC_H
  1925. X#define LIBC_H
  1926. X/*
  1927. X * declarations of (supposedly) standard C library functions and types.
  1928. X * we don't declare functions that once returned int but may now return void
  1929. X * to avoid fatal but spurious compilation errors.  VOID is an attempt to deal
  1930. X * with this transition.  sprvalue is similar.
  1931. X *
  1932. X * The function declarations need to be prototyped to give ansi compilers
  1933. X * less gastric distress.
  1934. X */
  1935. X
  1936. X#ifndef VOID
  1937. X#define VOID void
  1938. X#endif
  1939. X#ifndef sprvalue
  1940. X/*#define sprvalue char *    /* for stupid archaic 4BSD */
  1941. X#define sprvalue int
  1942. X#endif
  1943. X
  1944. X/* Unix system calls */
  1945. X/* signal types: tailor to suite local tastes */
  1946. Xtypedef VOID (*sigret_t)();
  1947. Xtypedef VOID (*sigarg_t)();
  1948. X
  1949. X#ifdef A_STABLE_WORLD
  1950. Xextern VOID _exit();
  1951. Xextern int access(), chown(), fork(), link(), mkdir(), umask(), unlink(), wait();
  1952. Xextern int alarm();            /* really unsigned? */
  1953. Xextern int getuid(), geteuid(), getgid(), getegid();
  1954. Xextern int setuid(), setgid();
  1955. Xextern int gethostname();
  1956. Xextern int execv(), execl(), execve(), execle();
  1957. X#endif                    /* A_STABLE_WORLD */
  1958. Xextern time_t time();            /* sys/timeb.h? */
  1959. X
  1960. Xextern int errno;            /* errno.h */
  1961. Xextern char **environ;
  1962. X
  1963. X/* C library */
  1964. X#ifdef A_STABLE_WORLD
  1965. Xextern int strcmp(), strncmp(), strlen();    /* strings.h */
  1966. X#endif                    /* A_STABLE_WORLD */
  1967. Xextern char *strcpy(), *strcat(), *strncpy(), *strncat();    /* strings.h */
  1968. Xextern char *index(), *rindex();    /* strings.h */
  1969. Xextern char *memcpy();            /* memory.h */
  1970. X
  1971. X#ifdef A_STABLE_WORLD
  1972. Xextern int fflush(), fputs(), ungetc();    /* stdio.h */
  1973. Xextern int fread(), fwrite(), fseek();    /* stdio.h */
  1974. Xextern int pclose();            /* stdio.h */
  1975. Xextern VOID rewind();            /* stdio.h */
  1976. Xextern VOID exit();            /* stdio.h */
  1977. X#endif                    /* A_STABLE_WORLD */
  1978. Xextern FILE *popen();            /* stdio.h */
  1979. X#ifdef __STDC__
  1980. Xextern int printf(char *fmt, ...), fprintf(FILE *, char *fmt, ...); /* stdio.h */
  1981. Xextern sprvalue sprintf(char *buf, char *fmt, ...);        /* stdio.h */
  1982. X#else                    /* __STDC__ */
  1983. Xextern int printf(), fprintf();        /* stdio.h */
  1984. Xextern sprvalue sprintf();        /* stdio.h */
  1985. X#endif                    /* __STDC__ */
  1986. X
  1987. X/* these unfortunately cannot be relied upon to be in the right header */
  1988. Xextern struct passwd *getpwnam();    /* pwd.h */
  1989. Xextern struct group *getgrnam();    /* grp.h */
  1990. Xextern char *ctime();            /* time.h */
  1991. X
  1992. Xextern long atol();
  1993. Xextern char *mktemp();
  1994. Xextern char *getenv();
  1995. X
  1996. X#ifdef A_STABLE_WORLD
  1997. Xextern int putenv(), system();
  1998. Xextern int getopt();
  1999. X#endif                    /* A_STABLE_WORLD */
  2000. Xextern int optind;
  2001. Xextern char *optarg;
  2002. X
  2003. X#include "alloc.h"            /* ugh */
  2004. X#endif                    /* LIBC_H */
  2005. !
  2006. echo 'h/Makefile':
  2007. sed 's/^X//' >'h/Makefile' <<'!'
  2008. XI = ../include
  2009. XINCLS = $(I)/alloc.h $(I)/config.h $(I)/fgetmfs.h $(I)/libc.h $(I)/news.h
  2010. X
  2011. Xall:    $(INCLS)
  2012. X
  2013. X$(I)/alloc.h:    alloc.h
  2014. X    cp alloc.h $@
  2015. X$(I)/config.h:    config.h
  2016. X    cp config.h $@
  2017. X$(I)/fgetmfs.h:    fgetmfs.h
  2018. X    cp fgetmfs.h $@
  2019. X$(I)/libc.h:    libc.h
  2020. X    cp libc.h $@
  2021. X$(I)/news.h:    news.h newshsed
  2022. X    sed -f newshsed news.h >$@
  2023. X
  2024. Xclean:
  2025. X    rm -f newshsed
  2026. !
  2027. echo 'h/news.h':
  2028. sed 's/^X//' >'h/news.h' <<'!'
  2029. X/*
  2030. X * definitions unique to all of C news
  2031. X * things marked with qqq are subject to being configured by "build"
  2032. X */
  2033. X
  2034. X/*
  2035. X * tunable parameters
  2036. X * which actually very seldom need to be tuned
  2037. X * in particular, don't get alarmed about MAXCOMP, it's not used for
  2038. X *  anything where it matters
  2039. X */
  2040. X#define MAXPATH 1024        /* max. length of pwd output */
  2041. X#define MAXCOMP 14        /* file name component length */
  2042. X#define MAXHOST 128        /* max. length of this host's name */
  2043. X#define SPOOLTMP ".tmpXXXXXX"    /* template for NEWSARTS temporary link */
  2044. X
  2045. X
  2046. X/* STATIC & FORWARD must agree to avoid redeclarations(!) */
  2047. X#define STATIC    static        /* "static" when not debugging|profiling */
  2048. X
  2049. X/* adapt to compiler limitations */
  2050. X#ifdef pdp11
  2051. X#define FORWARD            /* "static" except for dmr's 11 compiler */
  2052. X#else
  2053. X#define FORWARD static        /* "static" except for dmr's 11 compiler */
  2054. X#endif
  2055. X/* #define void int        /* if your compiler doesn't understand void's */
  2056. X/* #define MAXLONG 017777777777L    /* if your compiler lacks "unsigned long" type */
  2057. X
  2058. X/* adapt to library limitations */
  2059. X#define NOSTOREVAL    /* qqq if your dbm store() returns no value (as in orig. v7) */
  2060. X
  2061. X/* fundamental constants of the implementation */
  2062. X#define SMALLMEM    /* qqq for PDP-11s, PDP-8s, IBM PCs, etc. */
  2063. X#define    FASTINDEX    /* qqq if string functions are very fast */
  2064. X
  2065. X/* automatic configuration */
  2066. X#ifdef pdp11
  2067. X#ifndef SMALLMEM
  2068. X#define SMALLMEM
  2069. X#endif                /* SMALLMEM */
  2070. X#endif                /* pdp11 */
  2071. X
  2072. X
  2073. X/* types */
  2074. Xtypedef short statust;
  2075. Xtypedef char boolean;
  2076. X
  2077. X/* status bits */
  2078. X#define ST_OKAY        0    /* nothing wrong */
  2079. X#define ST_SHORT    (1<<1)    /* article shorter than byte count; truncated? */
  2080. X#define ST_ACCESS    (1<<2)    /* no access permission */
  2081. X#define ST_REFUSED    (1<<3)    /* article was deliberately refused - OK */
  2082. X#define ST_DROPPED    (1<<4)    /* article was accidentally dropped */
  2083. X#define ST_DISKFULL    (1<<5)    /* disk full - give up */
  2084. X#define ST_JUNKED    (1<<6)    /* article was accepted, but junked */
  2085. X
  2086. X/* newsgroup specific definitions */
  2087. X#define NGSEP ','        /* separates groups */
  2088. X#define NGNEG '!'        /* preceding a pattern, negates it */
  2089. X#define NGDELIM '.'        /* within a group */
  2090. X#define FNDELIM '/'        /* within a group, on disk */
  2091. X#define SFNDELIM "/"        /* string of FNDELIM */
  2092. X
  2093. X/* macros, replacing functions for speed */
  2094. X#define max(a,b) ((a) > (b)? (a): (b))
  2095. X#define min(a,b) ((a) < (b)? (a): (b))
  2096. X#define iswhite(c) ((c) == ' ' || (c) == '\t')
  2097. X/* STREQ is an optimised strcmp(a,b)==0 */
  2098. X#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
  2099. X/* STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0 */
  2100. X#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
  2101. X#define STRLEN(s) (sizeof (s) - 1)    /* s must be a char array */
  2102. X#ifdef FASTINDEX
  2103. X#define INDEX(src, chr, dest) (dest) = index(src, chr)
  2104. Xextern char *index();
  2105. X#else
  2106. X#define INDEX(src, chr, dest) \
  2107. X    for ((dest) = (src); *(dest) != '\0' && *(dest) != (chr); ++(dest)) \
  2108. X        ; \
  2109. X    if (*(dest) == '\0') \
  2110. X        (dest) = NULL        /* N.B.: missing semi-colon */
  2111. X#endif
  2112. X
  2113. X/* macros, of necessity */
  2114. X/* nnafree(any **) where "any" is any type; must be a macro */
  2115. X#define nnafree(mempp) (*(mempp) != 0? (free((char *)*(mempp)), (*(mempp) = 0)): 0)
  2116. X#ifdef lint
  2117. Xnnfree(mempp)        /* If *mempp is non-null, free it and zero it. */
  2118. Xregister char **mempp;            /* pointer to malloc'ed ptr. */
  2119. X{
  2120. X    if (*mempp != 0) {
  2121. X        free(*mempp);
  2122. X        *mempp = 0;
  2123. X    }
  2124. X}
  2125. X#else                    /* lint */
  2126. X#define nnfree nnafree
  2127. X#endif                    /* lint */
  2128. X
  2129. X#define YES 1
  2130. X#define NO 0
  2131. X
  2132. X#define NOTALLHDRS NO            /* hdrdump flags for "all headers seen?" */
  2133. X#define ALLHDRS YES
  2134. X
  2135. X#define DEFEXP "-"            /* default expiry period */
  2136. X
  2137. X/* imports from news */
  2138. Xextern char *progname;
  2139. X
  2140. Xextern void fclsexec();                /* from ../libos */
  2141. Xextern FILE *fopenexcl();            /* from ../libos */
  2142. Xextern char *getcwd();                /* from ../libos */
  2143. X
  2144. Xextern FILE *fopenclex(), *fopenwclex();    /* from ../libcnews/fopenclex.c */
  2145. Xextern char *gethdr();                /* from ../libcnews/gethdr.c */
  2146. Xextern char *hostname();            /* from ../libcnews/hostname.c */
  2147. Xextern void lockdebug(), newslock(), newsunlock();    /* from ../libcnews/lock.c */
  2148. Xextern void errunlock();            /* from ../libcnews/lock.c */
  2149. Xextern int ltozan(), ltoza();            /* from ../libcnews/ltoza.c */
  2150. Xextern void matchdebug();            /* from ../libcnews/ngmatch.c */
  2151. Xextern boolean ngmatch();            /* from ../libcnews/ngmatch.c */
  2152. Xextern void mkfilenm(), trim();            /* from ../libcnews/string.c */
  2153. Xextern boolean anyhostin(), hostin();        /* from ../libcnews/string.c */
  2154. Xextern int hopcount();                /* from ../libcnews/string.c */
  2155. Xextern char *skipsp(), *first(), *strsvto();    /* from ../libcnews/string.c */
  2156. Xextern char *sendersite(), *nullify();        /* from ../libcnews/string.c */
  2157. Xextern char *canonpath();            /* from ../libcnews/string.c */
  2158. Xextern void timestamp();            /* from ../libcnews/time.c */
  2159. X
  2160. Xextern void warning(), error();            /* from ../libc */
  2161. Xextern void standard();                /* from ../libc */
  2162. Xextern void closeall();                /* from ../libc */
  2163. Xextern void stdfdopen();            /* from ../libc */
  2164. Xextern int nfclose();                /* from ../libc */
  2165. X
  2166. X#include "alloc.h"                /* ugh */
  2167. !
  2168. echo 'hfake/README':
  2169. sed 's/^X//' >'hfake/README' <<'!'
  2170. XThis is fake header files that C News provides in case your system doesn't
  2171. Xhave the corresponding real ones.
  2172. !
  2173. echo 'hfake/stdlib.h':
  2174. sed 's/^X//' >'hfake/stdlib.h' <<'!'
  2175. X/* ANSI (draft) definitions */
  2176. X
  2177. Xtypedef struct {
  2178. X    long quot, rem;
  2179. X} ldiv_t;
  2180. X
  2181. Xextern ldiv_t ldiv();
  2182. !
  2183. echo 'hfake/string.h':
  2184. sed 's/^X//' >'hfake/string.h' <<'!'
  2185. X/*
  2186. X * String functions.
  2187. X */
  2188. X
  2189. Xchar *memcpy(/*char *dst, const char *src, int size*/);
  2190. Xchar *memccpy(/*char *dst, const char *src, int ucharstop, int size*/);
  2191. Xchar *strcpy(/*char *dst, const char *src*/);
  2192. Xchar *strncpy(/*char *dst, const char *src, int size*/);
  2193. Xchar *strcat(/*char *dst, const char *src*/);
  2194. Xchar *strncat(/*char *dst, const char *src, int size*/);
  2195. Xint memcmp(/*const char *s1, const char *s2, int size*/);
  2196. Xint strcmp(/*const char *s1, const char *s2*/);
  2197. Xint strncmp(/*const char *s1, const char *s2, int size*/);
  2198. Xchar *memchr(/*const char *s, int ucharwanted, int size*/);
  2199. Xchar *strchr(/*const char *s, int charwanted*/);
  2200. Xint strcspn(/*const char *s, const char *reject*/);
  2201. Xchar *strpbrk(/*const char *s, const char *breakat*/);
  2202. Xchar *strrchr(/*const char *s, int charwanted*/);
  2203. Xint strspn(/*const char *s, const char *accept*/);
  2204. Xchar *strstr(/*const char *s, const char *wanted*/);
  2205. Xchar *strtok(/*char *s, const char *delim*/);
  2206. Xchar *memset(/*char *s, int ucharfill, int size*/);
  2207. Xint strlen(/*const char *s*/);
  2208. X
  2209. X/*
  2210. X * V7 and Berklix compatibility.
  2211. X */
  2212. Xchar *index(/*const char *s, int charwanted*/);
  2213. Xchar *rindex(/*const char *s, int charwanted*/);
  2214. Xint bcopy(/*const char *src, char *dst, int length*/);
  2215. Xint bcmp(/*const char *s1, const char *s2, int length*/);
  2216. Xint bzero(/*char *dst, int length*/);
  2217. X
  2218. X/*
  2219. X * Putting this in here is really silly, but who am I to argue with X3J11?
  2220. X */
  2221. Xchar *strerror(/*int errnum*/);
  2222. !
  2223. echo done
  2224.  
  2225.  
  2226.