Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!usc!samsung!munnari.oz.au!basser!ultima!nick From: nick@ultima.cs.uts.oz (Nick Andrew) Newsgroups: comp.os.minix Subject: News for Minix (part 3 of 12) Keywords: news Message-ID: <16745@ultima.cs.uts.oz> Date: 7 Dec 89 11:51:10 GMT Organization: Comp Sci, NSWIT, Australia Lines: 2711 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'ifuncs.c' <<'END_OF_FILE' X/* X * This software is Copyright (c) 1986 by Rick Adams. X * X * Permission is hereby granted to copy, reproduce, redistribute or X * otherwise use this software as long as: there is no monetary X * profit gained specifically from the use or reproduction or this X * software, it is not sold, rented, traded or otherwise marketed, and X * this copyright notice is included prominently in any copy X * made. X * X * The author make no claims as to the fitness or correctness of X * this software for any use whatsoever, and it is provided as is. X * Any use of this software is at the user's own risk. X * X * ifuncs - functions used by inews. X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)ifuncs.c 2.65 4/10/87"; X#endif /* SCCSID */ X X#include "iparams.h" X X/*LINTLIBRARY*/ X X/* X * Transmit this article to all interested systems. X */ X X#ifdef u370 Xstatic struct srec srec; X#endif /* u370 */ X Xstatic struct hbuf h, hh; X X#ifdef MULTICAST X#define MAXMCAST 20 X#define MAXMCS 10 X Xstruct multicast { X char mc_name[SBUFLEN]; /* "multi-cast" name */ X short mc_syscnt; X char mc_tosys[MAXMCAST][SBUFLEN]; X} mcast[MAXMCS]; X Xstatic int mccount; X#endif /* MULTICAST */ X Xlong lseek(); X X#ifndef DBM Xchar *histfile(); X#endif /* !DBM */ X X#ifdef VMS X/* X * For VMS/Eunice there are no links: article was moved to firstbufname X * before broadcast is reached. So we read it from there. X */ Xextern char firstbufname[]; X#endif X Xbroadcast(is_rnews) Xint is_rnews; X{ X register char *hptr; X register char *sptr; X register FILE *fp; X#ifndef u370 X struct srec srec; X#endif X char sentbuf[LBUFLEN]; X int nsent = 0; X char *sentsys; X#ifdef GENERICPATH X int len; X#endif /* GENERICPATH */ X X /* h is a local copy of the header we can scribble on */ X#ifdef VMS X fp = xfopen (firstbufname, "r"); X#else X fp = xfopen(ARTICLE, "r"); X#endif X if (hread(&h, fp, TRUE) == NULL) X xerror("Cannot reread article"); X (void) fclose(fp); X X (void) strcpy(sentbuf, h.ident); X (void) strcat(sentbuf, " sent to "); X sentsys = index(sentbuf, '\0'); X nsent = 0; X /* break path into list of systems. */ X hptr = h.path; X#ifdef GENERICPATH X if (!is_rnews && X strncmp(PATHSYSNAME, h.path, (len = strlen(PATHSYSNAME))) == 0 X && index(NETCHRS, h.path[len])) X (void) strcpy(h.path, &(h.path[len+1])); X#endif /* GENERICPATH */ X sptr = hptr = h.path; X while ((hptr=strpbrk(hptr, NETCHRS)) != NULL) { X *hptr++ = '\0'; X sptr = hptr; X } X *sptr = '\0'; X X#ifdef MULTICAST X mccount = 0; X#endif /* MULTICAST */ X X /* loop once per system. */ X s_openr(); X while (s_read(&srec)) { X char *dist = h.distribution; X if (strncmp(srec.s_name, LOCALPATHSYSNAME, SNLN) == 0) X continue; X if (sptr = srec.s_nosend) { X while (*sptr) { X while (*sptr && *sptr != ',') X sptr++; X if (*sptr == ',') X *sptr++ = '\0'; X } X *++sptr = '\0'; X } X hptr = h.path; X while (*hptr != '\0') { X if (strncmp(srec.s_name, hptr, SNLN) == 0) X goto contin; X if (sptr = srec.s_nosend) { X while (*sptr != '\0') { X if (strncmp(sptr, hptr, SNLN) == 0) X goto contin; X while (*sptr++) X ; X } X } X while (*hptr++ != '\0') X ; X } X if (!ngmatch(h.nbuf, srec.s_nbuf)) X continue; X if (*dist == '\0') X dist = "world"; X if (!ngmatch(dist, srec.s_nbuf) && !ngmatch(srec.s_nbuf, dist)) X continue; X X if (nsent) { X hptr = sentsys; X while ((sptr = index(hptr, ',')) != NULL) { X *sptr = '\0'; X if (strcmp(hptr, srec.s_name) == 0) { X *sptr = ','; X goto contin; X } X *sptr++ = ','; X for (hptr = sptr; isspace(*hptr); hptr++) X ; X } X if (strcmp(hptr, srec.s_name) == 0) X continue; X } X /* now we've found a system to send this article to */ X#ifdef MULTICAST X if (index(srec.s_flags, 'M')) { X /* do a "multi-cast" transmit */ X register struct multicast *m; X X if (strlen(srec.s_name) >= SBUFLEN || X strlen(srec.s_xmit) >= SBUFLEN) X xerror("system name too long for multicast"); X for (m = mcast; m < &mcast[mccount]; m++) X if (strcmp(srec.s_xmit, m->mc_name) == 0) X break; X if (m >= &mcast[MAXMCS]) X xerror("Too many multicasts"); X if (m == &mcast[mccount]) { X mccount++; X m->mc_syscnt = 0; X strcpy(m->mc_name, srec.s_xmit); X } X if (m->mc_syscnt >= MAXMCAST) X xerror("Too many systems for multicast"); X strcpy(m->mc_tosys[m->mc_syscnt++], srec.s_name); X } else { X register struct multicast *m; X register char **yptr; X char *sysptrs[MAXMCAST]; X int mc; X X mc = 0; X for (m = mcast; m < &mcast[mccount]; m++) X if (strcmp(m->mc_name, srec.s_name) == 0) { X yptr = sysptrs; X while (mc < m->mc_syscnt) X *yptr++ = m->mc_tosys[mc++]; X break; X } X#ifdef VMS X if (!transmit(&srec, xfopen(firstbufname,"r"), X#else /* !VMS */ X if (!transmit(&srec, xfopen(ARTICLE,"r"), X#endif /* !VMS */ X (strncmp(h.nbuf, "to.", 3) != 0), X sysptrs, mc)) X continue; X } X#else /* !MULTICAST */ X#ifdef VMS X if (!transmit(&srec, xfopen(firstbufname, "r"), X#else /* !VMS */ X if (!transmit(&srec, xfopen(ARTICLE, "r"), X#endif /* !VMS */ X (strncmp(h.nbuf, "to.", 3) != 0), X (char **) NULL, FALSE)) X continue; X#endif /* !MULTICAST */ X if (nsent) X (void) strcat(sentbuf, ", "); X (void) strcat(sentbuf, srec.s_name); X nsent++; X contin:; X } X if (nsent) X log(sentbuf); X s_close(); X} X X/* X * Transmit file to system. X */ X#define PROC 0004 X#ifndef MULTICAST X/* ARGSUSED */ X#endif /* !MULTICAST */ Xtransmit(sp, ifp, maynotify, sysnames, mc) Xregister struct srec *sp; Xregister FILE *ifp; Xint maynotify; Xchar **sysnames; Xint mc; X{ X register FILE *ofp; X register int c; X register char *ptr; X char TRANS[BUFLEN]; X char *argv[20]; X register int pid; X extern char firstbufname[]; X X/* A: afmt: the other machine runs an A news, so we xmit in A format */ X int afmt = (index(sp->s_flags, 'A') != NULL); X/* B: use B format (this is the default - don't use this letter elsewise). */ X/* F: append name to file */ X int appfile = (index(sp->s_flags, 'F') != NULL); X/* L: local: don't send the article unless it was generated locally */ X int local = ((ptr = index(sp->s_flags, 'L')) != NULL); X/* H: interpolate history line into command, use existing file */ X int history = (index(sp->s_flags, 'H') != NULL); X/* m: moderated: only send if group is moderated */ X int sendifmoderated = (index(sp->s_flags, 'm') != NULL); X/* u: unmoderated: only send if group is unmoderated */ X int sendifunmoderated = (index(sp->s_flags, 'u') != NULL); X/* M: multi-cast: this is taken care of above, but don't reuse flag */ X#ifdef MULTICAST X/* O: multi-cast only, don't send article if not multicast hosts */ X int multisend = (index(sp->s_flags, 'O') != NULL); X#endif /* MULTICAST */ X/* N: notify: don't send the article, just tell him we have it */ X int notify = maynotify && (index(sp->s_flags, 'N') != NULL); X/* S: noshell: don't fork a shell to execute the xmit command */ X int noshell = (index(sp->s_flags, 'S') != NULL); X/* U: useexist: use the -c option to uux to use the existing copy */ X int useexist = (index(sp->s_flags, 'U') != NULL); X/* I: append messageid to file. implies F flag */ X int appmsgid = maynotify && (index(sp->s_flags, 'I') != NULL); X X if (notify) X appfile = appmsgid = FALSE; X X if (local && mode == PROC) { X local = 0; X while (isdigit(*++ptr)) X local = local * 10 + *ptr - '0'; X for (ptr = h.path; *ptr != '\0' && local >= 0; local--) X while (*ptr++ != '\0') X ; X if (local < 0) { X (void) fclose(ifp); X return FALSE; X } X } X X /* X ** Do not transmit to system specified in -x flag. X */ X if (not_here[0] && strcmp(not_here, sp->s_name) == 0) { X (void) fclose(ifp); X return FALSE; X } X X#ifdef DEBUG X printf("Transmitting to '%s'\n", sp->s_name); X#endif /* DEBUG */ X X#ifdef MULTICAST X if (multisend && mc == 0) { X (void) fclose(ifp); X return FALSE; X } X#endif /* MULTICAST */ X X if ((sendifmoderated && is_mod[0] == '\0') || X (sendifunmoderated && is_mod[0] != '\0')) { X fclose(ifp); X return FALSE; X } X X if (appmsgid || (!appfile && !useexist && !history)) { X if (!hread(&hh, ifp, TRUE)) { X logerr("Bad header, not transmitting %s re %s to %s", X hh.ident, hh.title, sp->s_name); X (void) fclose(ifp); X return FALSE; X } X if (hh.nbuf[0] == '\0') { X fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name); X (void) fclose(ifp); X return FALSE; X } X (void) sprintf(TRANS, "%s/trXXXXXX", SPOOL); X } X X if (notify) { X char oldid[50]; X (void) sprintf(hh.title, "ihave %s %s", hh.ident, PATHSYSNAME); X (void) strcpy(hh.ctlmsg, hh.title); X (void) strcpy(hh.numlines, "0"); X (void) sprintf(hh.nbuf, "to.%s.ctl", sp->s_name); X (void) strcpy(oldid, hh.ident); X getident(&hh); X log("tell %s about %s, notif. id %s", X sp->s_name, oldid, hh.ident); X } X X if (appfile || appmsgid) { X if (firstbufname[0] == '\0') { X extern char histline[]; X localize("junk"); X savehist(histline); X xerror("No file name to xmit from"); X } X if (sp->s_xmit[0] == '\0') X sprintf(sp->s_xmit, "%s/%s%s", BATCHDIR, sp->s_name, X appmsgid ? ".ihave" : ""); X#ifdef IHCC X (void) sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit); X ofp = fopen(TRANS, "a"); X#else /* !IHCC */ X ofp = fopen(sp->s_xmit, "a"); X#endif /* !IHCC */ X if (ofp == NULL) X xerror("Cannot append to %s", sp->s_xmit); X fprintf(ofp, "%s", appmsgid ? hh.ident : firstbufname); X#ifdef MULTICAST X while (--mc >= 0) X fprintf(ofp, " %s", *sysnames++); X#endif /* !MULTICAST */ X putc('\n', ofp); X (void) fclose(ofp); X (void) fclose(ifp); X return TRUE; X } X else if (useexist) { X if (firstbufname[0] == '\0') X xerror("No file name to xmit from"); X if (*sp->s_xmit == '\0') X#ifdef UXMIT X (void) sprintf(bfr, UXMIT, sp->s_name, firstbufname); X#else X xerror("UXMIT not defined for U flag"); X#endif X else X#ifdef MULTICAST X makeargs(bfr, sp->s_xmit, firstbufname, sysnames, mc); X#else X (void) sprintf(bfr, sp->s_xmit, firstbufname); X#endif X (void) fclose(ifp); X } else if (history) { X extern char histline[]; X X if (*sp->s_xmit == '\0') X xerror("no xmit command with H flag"); X#ifdef MULTICAST X makeargs(bfr, sp->s_xmit, histline, sysnames, mc); X#else X (void) sprintf(bfr, sp->s_xmit, histline); X#endif X } else { X ofp = xfopen(mktemp(TRANS), "w"); X if (afmt) { X#ifdef OLD X fprintf(ofp, "A%s\n%s\n%s!%s\n%s\n%s\n", oident(hh.ident), hh.nbuf, PATHSYSNAME, X hh.path, hh.subdate, hh.title); X#else /* !OLD */ X logerr("Must have OLD defined to use A flag for xmit"); X return FALSE; X#endif /* !OLD */ X } else X hwrite(&hh, ofp); X if (!notify) X while ((c = getc(ifp)) != EOF) X putc(c, ofp); X if (ferror(ofp)) X xerror("write failed on transmit"); X (void) fclose(ifp); X (void) fclose(ofp); X if (*sp->s_xmit == '\0') X (void) sprintf(bfr, DFTXMIT, sp->s_name, TRANS); X else X#ifdef MULTICAST X makeargs(bfr, sp->s_xmit, TRANS, sysnames, mc); X#else /* !MULTICAST */ X (void) sprintf(bfr, sp->s_xmit, TRANS); X#endif /* !MULTICAST */ X } X X /* At this point, the command to be executed is in bfr. */ X if (noshell) { X if (pid = vfork()) X fwait(pid); X else { X (void) close(0); X (void) open(TRANS, 0); X ptr = bfr; X for (pid = 0; pid < 19; pid++) { X while (isspace(*ptr)) X *ptr++ = 0; X argv[pid] = ptr; X while (!isspace(*++ptr) && *ptr) X ; X if (!*ptr) X break; X } X argv[++pid] = 0; X (void) setgid(gid); X (void) setuid(uid); X execvp(argv[0], argv); X xerror("Can't execv %s", argv[0]); X } X } else { X if (!history && sp->s_xmit[0] && !index(bfr, '<')) { X char newcmd[LBUFLEN]; X X (void) sprintf(newcmd, "(%s) <%s", bfr, X useexist ? firstbufname : TRANS); X system(newcmd); X } else X system(bfr); X } X if (!appfile && !useexist && !history) X (void) unlink(TRANS); X (void) fclose(ifp); X return TRUE; X} X X#ifdef MULTICAST Xmakeargs(buf, cmd, arg2, sysargs, sac) Xchar *buf; Xchar *cmd; Xchar *arg2; Xregister char **sysargs; Xint sac; X{ X register char *p = cmd; X register char *q; X register ac = 0; X register char *b = buf; X X q = p; X do { X if (q = index(q, ' ')) X *q = '\0'; X if (index(p, '%')) { X switch (++ac) { X case 1: X while (--sac >= 0) { X sprintf(b, p, *sysargs++); X b = index(b, '\0'); X } X break; X case 2: X sprintf(b, p, arg2); X b = index(b, '\0'); X break; X default: X if (q) X *q = ' '; X xerror("badly formed command: %s", cmd); X } X } else { X strcpy(b, p); X b = index(b, '\0'); X } X if (q) { X *q = ' '; X p = q; X while (isspace(*q)) X q++; X } X } while (q != NULL); X} X#endif /* MULTICAST */ X Xtypedef struct { X char *dptr; X int dsize; X} datum; X X/* X * Return TRUE if we have seen this file before, else FALSE. X */ Xhistory(hp) Xstruct hbuf *hp; X{ X#ifdef DBM X datum lhs, rhs; X datum fetch(); X#else /* !DBM */ X register FILE *hfp; X register char *p; X#endif /* !DBM */ X char lcident[BUFLEN]; X extern char histline[]; X X#ifdef DEBUG X fprintf(stderr,"history(%s)\n", hp->ident); X#endif /* DEBUG */ X /* X * Make the article ID case insensitive. X */ X (void) strcpy(lcident, hp->ident); X lcase(lcident); X X idlock(lcident); X#ifdef DBM X initdbm(ARTFILE); X lhs.dptr = lcident; X lhs.dsize = strlen(lhs.dptr) + 1; X rhs = fetch(lhs); X if (rhs.dptr) { X idunlock(); X return(TRUE); X } X#else /* !DBM */ X hfp = xfopen(histfile(lcident), "r"); X while (fgets(bfr, BUFLEN, hfp) != NULL) { X p = index(bfr, '\t'); X if (p == NULL) X p = index(bfr, '\n'); X if (p != NULL) /* can happen if nulls in file */ X *p = 0; X lcase(bfr); X X if (strcmp(bfr, lcident) == 0) { X (void) fclose(hfp); X idunlock(); X#ifdef DEBUG X fprintf(stderr,"history returns true\n"); X#endif /* DEBUG */ X return TRUE; X } X } X (void) fclose(hfp); X#endif /* !DBM */ X histline[0] = '\0'; X addhist(hp->ident); X addhist("\t"); X#ifdef DEBUG X fprintf(stderr,"history returns false\n"); X#endif X return FALSE; X} X Xchar histline[PATHLEN]; X Xaddhist(msg) Xchar *msg; X{ X (void) strcat(histline, msg); X} X Xsavehist(hline) Xchar *hline; X{ X register FILE *hfp; X register char *p; X#ifdef DBM X long fpos; X#endif /* !DBM */ X X#ifndef DBM X if (strcmp((p = histfile(hline)), ARTFILE) != 0) { X /* If the history subfile is accessible */ X if ((hfp = xfopen(p, "a")) != NULL ) { /* If we can append */ X fprintf(hfp, "%s\n", hline); /* Append */ X (void) fclose(hfp); X } else X logerr("Unable to append to %s: %s", p, errmsg(errno)); X } else X#endif /* !DBM */ X { X hfp = xfopen(ARTFILE, "a"); X (void) fseek(hfp, 0L, 2); /* Unisoft 5.1 doesn't seek to EOF on 'a' */ X#ifdef DBM X fpos = ftell(hfp); X#endif /* !DBM */ X fprintf(hfp, "%s\n", hline); X (void) fclose(hfp); X } X#ifdef DBM X { X datum lhs, rhs; X /* We assume that history has already been called, calling dbminit. */ X p = index(hline, '\t'); X if (p) X *p = 0; X lcase(hline); X lhs.dptr = hline; X lhs.dsize = strlen(lhs.dptr) + 1; X rhs.dptr = (char *)&fpos; X rhs.dsize = sizeof fpos; X store(lhs, rhs); X } X#endif /* DBM */ X idunlock(); X} X X/* X * Save partial news. X */ X/* ARGSUSED */ Xnewssave(fd, dummy) XFILE *fd; Xchar *dummy; X{ X register FILE *tofd, *fromfd; X char sfname[BUFLEN]; X register int c; X time_t tim; X X if (fd == NULL) X fromfd = xfopen(INFILE, "r"); X else X fromfd = fd; X (void) umask(savmask); X (void) setgid(gid); X (void) setuid(uid); X X (void) sprintf(sfname, "%s/%s", userhome, PARTIAL); X if ((tofd = fopen(sfname, "a")) == NULL) X xerror("Cannot save partial news in %s", sfname); X (void) time(&tim); X fprintf(tofd, "----- News saved at %s\n", arpadate(&tim)); X while ((c = getc(fromfd)) != EOF) X putc(c, tofd); X (void) fclose(fromfd); X (void) fclose(tofd); X printf("News saved in %s\n", sfname); X xxit(1); X} X X/* X * Handle dates in header. X */ X Xdates(hp) Xstruct hbuf *hp; X{ X time_t edt; X X if (*hp->subdate) { X if (cgtdate(hp->subdate) < 0) { X error("Cannot parse submittal date '%s'", hp->subdate); X } X } else { X (void) time(&edt); X (void) strcpy(hp->subdate, arpadate(&edt)); X } X} X X#define LOCKSIZE 128 Xchar lockname[LOCKSIZE]; X Xidlock(str) Xchar *str; X{ X register int i; X register char *cp, *scp; X char tempname[LOCKSIZE]; X time_t now; X struct stat sbuf; X extern int errno; X#ifdef VMS X int fd; X/* The name here is because of the peculiar properties of version numbers X * in Eunice. We eliminate any troublesome characters also. X */ X (void) sprintf(lockname, "/tmp/%.10s.l.1", str); X for (cp = lockname; *cp; cp++) X if (*cp == '/' || *cp == '[' || *cp == ']') *cp = '.'; X while ((fd = creat(lockname, 0444)) < 0) { X#else /* !VMS */ X (void) strcpy(tempname, "/tmp/LTMP.XXXXXX"); X (void) mktemp(tempname); X (void) strcpy(lockname, "/tmp/L"); X i = strlen(lockname); X cp = &lockname[i]; X scp = str - 1; X while (i++ < LOCKSIZE && *++scp != '\0') X if (*scp == '/') /* slash screws up the open */ X *cp++ = '.'; X else X *cp++ = *scp; X *cp = '\0'; X#ifdef FOURTEENMAX X lockname[5 /* /tmp/ */ + 14] = '\0'; X#endif X i = creat(tempname, 0666); X if (i < 0) X xerror("Cannot creat %s: errno %d", tempname, errno); X (void) close(i); X while (link(tempname, lockname)) { X#endif /* !VMS */ X (void) time(&now); X if (stat(lockname, &sbuf) < 0) X xerror("Directory permission problem in /tmp"); X X if (sbuf.st_mtime + 10*60 < now) { X (void) unlink(lockname); X logerr("Article %s locked up", str); X break; X } X log("waiting on lock for %s", lockname); X sleep((unsigned)60); X } X#ifdef VMS X (void) close(fd); X#endif X (void) unlink(tempname); X} X Xidunlock() X{ X (void) unlink(lockname); X} X X/* X * Put a unique name into header.ident. X */ Xgetident(hp) Xstruct hbuf *hp; X{ X long seqn; X register FILE *fp; X extern char *mydomain(); X X lock(); X fp = xfopen(SEQFILE, "r"); X (void) fgets(bfr, BUFLEN, fp); X (void) fclose(fp); X seqn = atol(bfr) + 1; X/* X * For Eunice, this breaks if SEQFILE is not in Unix format. X */ X fp = xfopen(SEQFILE, "r+w"); X fprintf(fp, "%ld\n", seqn); X (void) fclose(fp); X unlock(); X (void) sprintf(hp->ident, "<%ld@%s>", seqn, LOCALSYSNAME); X} X X/* X * Check that header.nbuf contains only valid newsgroup names; X * exit with error if not valid. X */ Xngfcheck(isproc) X{ X register FILE * f; X register char * cp; X register int i, j; X register int ngcount, okcount, havealiased; X register int pass; X char * ngs[sizeof header.nbuf / 2]; X char uses[sizeof header.nbuf / 2]; X char tbuf[sizeof header.nbuf]; X char abuf[BUFLEN]; X X havealiased = ngcount = 0; X is_mod[0] = '\0'; X /* X ** Split header.nbuf into constituent newsgroups. X ** Zap "local" newsgroups of articles from remote sites. X */ X cp = tbuf; X (void) strcpy(cp, header.nbuf); X for ( ; ; ) { X while (*cp == NGDELIM || *cp == ' ') X ++cp; X if (*cp == '\0') X break; X ngs[ngcount] = cp; X do { X ++cp; X } while (*cp != '\0' && *cp != NGDELIM && *cp != ' '); X if (*cp != '\0') X *cp++ = '\0'; X /* X ** Check for local only distribution on incoming X ** newsgroups. This might occur if someone posted to X ** general,net.unix X */ X if (isproc && index(ngs[ngcount], '.') == NULL && X index(header.nbuf, '.') != NULL) { X logerr("Local group %s removed", X ngs[ngcount]); X continue; X } X uses[ngcount] = 1; /* it should go in "Newsgroups" line */ X ++ngcount; X } X /* X ** Check groups against active file. X */ Xrecheck: X okcount = 0; X rewind(actfp); clearerr(actfp); X while (okcount < ngcount && fgets(bfr, BUFLEN, actfp) == bfr) { X if ((cp = index(bfr, ' ')) == NULL) X continue; /* strange line in input! */ X /* newsgroup 12345 12345 X */ X /* cp + 01234567890123 */ X if (!isproc && cp[13] == 'n') X continue; /* can't post to this group! */ X *cp = '\0'; X for (i = 0; i < ngcount; ++i) X if (uses[i] >= 1 && strcmp(bfr, ngs[i]) == 0) { X uses[i] = 2; /* it should be localized too */ X if (cp[13] == 'm') X strcpy(is_mod, bfr); X ++okcount; X } X } X#ifdef ALWAYSALIAS X okcount = 0; X#endif /* ALWAYSALIAS */ X /* X ** Handle groups absent from active file. X */ X if (havealiased == 0 && okcount < ngcount) { X /* X ** See if remaining groups are in our alias list. X */ X f = xfopen(ALIASES, "r"); X while (okcount < ngcount && fscanf(f, "%s %s", abuf, bfr) == 2) X for (i = 0; i < ngcount; ++i) { X#ifndef ALWAYSALIAS X if (uses[i] == 2) X continue; X#endif /* ALWAYSALIAS */ X if (strcmp(ngs[i], abuf) != 0) X continue; X if (isproc) X cp = "Aliased newsgroup %s to %s"; X else X cp = "Please change %s to %s"; X logerr(cp, abuf, bfr); X ngs[i] = AllocCpy(bfr); X uses[i] = 2; X ++havealiased; X ++okcount; X } X (void) fclose(f); X for (i = 0; i < ngcount; ++i) { X if (uses[i] == 2) X continue; X if (isproc) X log("Unknown newsgroup %s not localized", X ngs[i]); X else X logerr("Unknown newsgroup %s", ngs[i]); X#ifdef ALWAYSALIAS X ++okcount; /* so we know to exit below */ X } X if (!isproc && okcount > 0) X#else /* !ALWAYSALIAS */ X } X if (!isproc) X#endif /* !ALWAYSALIAS */ X newssave(infp, (char *) NULL); X /* X * Unfortunately, if you alias an unmoderated group to a X * moderated group, you must recheck the active file to see X * if the new group is moderated. Rude but necessary. X */ X if (havealiased) X goto recheck; X } X /* X ** Zap duplicates. X */ X for (i = 0; i < ngcount - 1; ++i) { X if (uses[i] == 0) X continue; X for (j = i + 1; j < ngcount; ++j) { X if (uses[j] == 0) X continue; X if (strcmp(ngs[i], ngs[j]) != 0) X continue; X logerr("Duplicate %s removed", ngs[j]); X if (uses[i] < uses[j]) X uses[i] = uses[j]; X uses[j] = 0; X } X } X for (pass = 1; pass <= 2; ++pass) { X register int avail; X X if (pass == 1) { X /* X ** Rewrite header.nbuf. X */ X cp = header.nbuf; X avail = sizeof header.nbuf; X } else { X /* X ** Fill in nbuf. X */ X cp = nbuf; X avail = sizeof nbuf; X } X for (i = 0; i < ngcount; ++i) { X if (uses[i] < pass) X continue; X j = strlen(ngs[i]); X if (j + 2 > avail) { X logerr("Redone Newsgroups too long"); X break; X } X (void) strcpy(cp, ngs[i]); X cp += j; X *cp++ = (pass == 1) ? NGDELIM : '\0'; X avail -= (j + 1); X } X if (pass == 1) { X if (cp == header.nbuf) X *cp = '\0'; X else *(cp - 1) = '\0'; X } else *cp = '\0'; X } X /* X ** Free aliases. X */ X for (i = 0; i < ngcount; ++i) X if (ngs[i] < tbuf || ngs[i] > &tbuf[sizeof tbuf - 1]) X free(ngs[i]); X return nbuf[0] == '\0'; X} X X/* X * Figure out who posted the article (which is locally entered). X * The results are placed in the header structure hp. X */ Xgensender(hp, logname) Xstruct hbuf *hp; Xchar *logname; X{ X register char *fn, *p; X char buf[BUFLEN]; X char *fullname(), *getenv(); X int fd, n; X extern char *mydomain(); X X if ((fn = getenv("NAME")) == NULL) { X (void) sprintf(buf, "%s/%s", userhome, ".name"); X if ((fd = open(buf, 0)) >= 0) { X n = read(fd, buf, sizeof buf); X (void) close(fd); X if (n > 0 && buf[0] >= 'A') { X for (p = fn = buf; *p; p++) X if (*p < ' ') X *p = '\0'; X } X } X } X X if (fn == NULL) X fn = fullname(logname); X X (void) sprintf(hp->path, "%s", logname); X (void) sprintf(hp->from, "%s@%s (%s)", logname, FROMSYSNAME, fn); X} X X/* X * Trap interrupts. X */ Xonsig(n) Xint n; X{ X static int numsigs = 0; X /* X * Most UNIX systems reset caught signals to SIG_DFL. X * This bad design requires that the trap be set again here. X * Unfortunately, if the signal recurs before the trap is set, X * the program will die, possibly leaving the lock in place. X */ X if (++numsigs > 100) { X xerror("inews ran away looping on signal %d", n); X } X (void) signal(n, onsig); X SigTrap = n; X} X X/* X * If the stdin begins with "#" the input is some kind of batch. if X * the first line is: X * #!cunbatch X * or X * #!c7unbatch X * then fork off a pipe to do the either a X * "compress -d" X * or a X * "decode | compress -d" X * and check their output for more batch headers. They probably X * contain a batch format that looks like this: X * #! rnews 1234 X * article with 1234 chars X * #! rnews 4321 X * article with 4321 chars X * If so, then for each article, copy the indicated number of chars into X * a temp file, fork a copy of ourselves, make its input the temp file, X * and allow the copy to process the article. This avoids an exec of X * rnews for each article. X */ X Xcheckbatch() X{ X int c; X char *cp; X X setbuf(infp, (char *)NULL); X while ((c = getc(infp)) == '#') { X /* some kind of batch, investigate further */ X int i; X char cmd[BUFLEN]; X cmd[0] = c; X fgets(cmd + 1, BUFLEN, infp); X if (strncmp(cmd, "#! cunbatch", 11) == 0) { X (void) sprintf(cmd, "%s/compress", LIB); X input_pipe(cmd, "compress", "-d", (char *) 0); X continue; /* look for the #! rnews */ X } else if (strncmp(cmd, "#! c7unbatch", 12) == 0) { X (void) sprintf(cmd, "%s/decode | %s/compress -d", X LIB, LIB); X input_pipe("/bin/sh", "news-unpack", "-c", cmd); X continue; /* look for the #! rnews */ X } else if (strncmp(cmd, "#! rnews ", 9) == 0 || X strncmp(cmd, "! rnews ", 8) == 0) { X /* instead of execing unbatch do it ourselves */ X register int fd, rc, wc; X int piped[2]; X register long size, asize; X char *filename; X int pid, wpid, exstat; X#define CPBFSZ 8192 X char buf[CPBFSZ]; X X filename = 0; X do { X while (strncmp(cmd, "#! rnews ", 9)) { X fprintf(stderr, "out of sync, skipping %s\n", cmd); X if (fgets(cmd, BUFLEN, infp) == NULL) X exit(0); X } X asize = atol(cmd + 9); X if (asize <= 0) X xerror("checkbatch: bad batch count %ld", asize); X fd = -1; X size = asize; X do { X if (size > CPBFSZ) X rc = CPBFSZ; X else X rc = size; X rc = fread(buf, 1, rc, infp); X if (rc <= 0) X break; X if (fd < 0) { X if (rc == asize) X break; /* fits in buffer */ X if (!filename) X filename = mktemp("/tmp/unbnewsXXXXXX"); X if ((fd = creat(filename, 0666)) < 0) { X fprintf(stderr, "rnews: creat of \"%s\" failed", X filename); X perror(" "); X exit(1); X } X } X wc = write(fd, buf, rc); /* write to temp file */ X if (wc != rc) { X fprintf(stderr, "write of %d to \"%s\" returned %d", X rc, filename, wc); X perror(" "); X exit(1); X } X size -= rc; X } while (size > 0); X if (fd >= 0) X (void) close(fd); X X /* X * If we got a truncated batch, don't process X * the last article; it will probably be X * received again. X */ X if ((rc < asize) && (size > 0)) X break; X X /* X * This differs from the old unbatcher in X * that we don't exec rnews, mainly because X * we ARE rnews. Instead we fork off a copy X * of ourselves for each article and allow it X * to process. X */ X if (rc == asize) { X /* X * article fits in buffer, use a pipe X * instead of a temporary file. X */ X if (pipe(piped) != 0) X xerror("checkbatch: pipe() failed"); X } X while ((pid = fork()) == -1) { X fprintf(stderr, "fork failed, waiting...\r\n"); X sleep(60); X } X if (pid == 0) { X if (rc == asize) { X /* article fits in buffer X * make the output of the X * pipe for STDIN X */ X (void) fclose(infp); X /* redundant but why not */ X (void) close(0); X if ((i = dup(piped[0])) != 0) X xerror("dup() returned %d, should be 0", i); X (void) close(piped[0]); X (void) close(piped[1]); X infp = fdopen(0, "r"); X } else /* supstitute temp file as X * input */ X freopen(filename, "r", infp); X return; /* from checkbatch as if X * normal article */ X } X /* parent of fork */ X if (rc == asize) { X /* article fits in buffer */ X wc = write(piped[1], buf, rc); X if (wc != rc) { X fprintf(stderr, "write of %d to pipe returned %d", X rc, wc); X perror("rnews: write"); X exit(1); X } X (void) close(piped[0]); X (void) close(piped[1]); X } X while ((wpid = wait(&exstat)) >= 0 && wpid != pid); X } while (fgets(cmd, BUFLEN, infp) != NULL); X (void) unlink(filename); X exit(0);/* all done */ X X } else { X docmd(cmd); X xxit(0); X } X } /* while a batch */ X cp = malloc((unsigned)BUFSIZ); X if (cp != NULL) X setbuf(infp, cp); X if (c != EOF) X (void) ungetc(c, infp); X clearerr(infp); X} X X/* X * The input requires some processing so fork and exec the indicated command X * with its output piped to our input. X */ Xstatic Xinput_pipe(cmd, arg0, arg1, arg2) Xchar *cmd, *arg0, *arg1, *arg2; X{ X int i, pid; X int piped[2]; X X if (pipe(piped) != 0) { X perror("checkbatch: pipe() failed"); X exit(1); X } X fflush(stdout); X while ((pid = vfork()) == -1) { X perror("checkbatch: fork failed, waiting"); X sleep(60); X } X if (pid == 0) { /* child process */ X /* X * setup a pipe such that the exec'ed process will read our X * input file and write to the pipe X */ X (void) close(1); X if ((i = dup(piped[1])) != 1) X xerror("dup() returned %d, should be 1", i); X (void) close(piped[0]); X (void) close(piped[1]); X execl(cmd, arg0, arg1, arg2, (char *) 0); X perror("checkbatch"); X xerror("Unable to exec %s to unpack news.", cmd); X } else { /* parent process */ X /* make the output of the pipe for STDIN */ X (void) fclose(infp); X (void) close(0); X if ((i = dup(piped[0])) != 0) X xerror("dup() returned %d, should be 0", i); X (void) close(piped[0]); X (void) close(piped[1]); X /* X * there should be a way to clear any buffered input and just X * replace file descriptor 0 but I can't find a portable way. X */ X infp = fdopen(0, "r"); X } X} X X#define MAXARGS 32 X Xdocmd(p) Xregister char *p; X{ X char *args[MAXARGS]; X register char **ap = args; X char path[BUFSIZ]; X char *rindex(), *cp; X X while (*p && !isspace(*p)) /* skip leading #! crud */ X p++; X X while (isspace(*p)) X p++; X X while (*p != '\0') { X *ap++ = p; X if (ap >= &args[MAXARGS]) { X logerr("inews: unbatch: Too many args to %s", args[0]); X exit(2); X } X while (*p && !isspace(*p)) X p++; X if (*p) X *p++ = '\0'; X while (isspace(*p)) X p++; X } X *ap = (char *)0; X X if (ap == args) { X logerr("inews: unbatch: no command to execute"); X exit(2); X } X X /* strip off any leading pathname in case someone gets tricky */ X cp = rindex(args[0], '/'); X if (cp++ == NULL) X cp = args[0]; X X# ifdef HOME X sprintf(path, "%s/%s/%s", logdir(HOME), LIBDIR, cp); X# else /* !HOME */ X sprintf(path, "%s/%s", LIBDIR, cp); X# endif /* HOME */ X X /* X * "path" is absolute, no searching is needed, we use X * 'execvp' solely so that sh scripts will be handled X */ X (void) execvp(path, args); X perror(path); X xxit(2); X} X X/* X * Exit and cleanup. X */ Xxxit(status) Xint status; X{ X (void) unlink(INFILE); X (void) unlink(ARTICLE); X while (lockcount > 0) X unlock(); X idunlock(); X exit(status); X} X Xrwaccess(fname) Xchar *fname; X{ X int fd; X X fd = open(fname, 2); X if (fd < 0) X return 0; X (void) close(fd); X return 1; X} X Xexists(fname) Xchar *fname; X{ X int fd; X X fd = open(fname, 0); X if (fd < 0) X return 0; X (void) close(fd); X return 1; X} X Xint lockcount = 0; /* no. of times we've called lock */ X X#ifdef VMS X X#define SUBLOCK "/tmp/netnews.lck.1" X X/* X * Newsystem locking. X * These routines are different for VMS because we can not X * effectively simulate links, and VMS supports multiple X * version numbers of files X */ Xlock() X{ X register int i; X register int fd; X X if (lockcount++ == 0) { X i = DEADTIME; X while ((fd = creat(SUBLOCK, 0444)) < 0) { X if (--i < 0) { X (void) unlink(SUBLOCK); X logerr("News system locked up"); X } X if (i < -3) X xerror("Unable to unlock news system"); X sleep((unsigned)1); X } X (void) close(fd); X } X} X Xunlock() X{ X if (--lockcount == 0) X (void) unlink(SUBLOCK); X} X X#else /* !VMS */ X X/* X * Newsystem locking. X */ X X#if defined(BSD4_2) || defined(LOCKF) X#ifdef LOCKF X#include X#else /* !LOCKF */ X#include X#endif /* !LOCKF */ Xstatic int LockFd = -1; Xlock() X{ X LockFd = open(SUBFILE, 2); X if (LockFd < 0) X logerr("Can't open(\"%s\",2) to lock", SUBFILE); X /* This will sleep until the other program releases the lock */ X /* We may need to alarm out of this, but I don't think so */ X#ifdef LOCKF X if (lockf(LockFd, F_LOCK, 0) < 0) X#else X if (flock(LockFd, LOCK_EX) < 0) X#endif X xerror("Can't get lock on %s: %s", SUBFILE, errmsg(errno)); X} X Xunlock() X{ X (void) close(LockFd); X} X#else /* !BSD4_2 */ Xlock() X{ X register int i; X extern int errno; X X if (lockcount++ == 0) { X i = DEADTIME; X while (link(SUBFILE, LOCKFILE)) { X if (errno != EEXIST) X break; X if (--i < 0) X xerror("News system locked up"); X sleep((unsigned)1); X } X } X} X Xunlock() X{ X if (--lockcount == 0) X (void) unlink(LOCKFILE); X} X#endif /* !BSD4_2 */ X#endif /* !VMS */ X X/* VARARGS1 */ Xerror(message, arg1, arg2, arg3) Xchar *message; Xlong arg1, arg2, arg3; X{ X char buffer[LBUFLEN]; X X fflush(stdout); X (void) sprintf(buffer, message, arg1, arg2, arg3); X logerr(buffer); X xxit(mode == PROC ? 0 : 1); X} END_OF_FILE if test 32337 -ne `wc -c <'ifuncs.c'`; then echo shar: \"'ifuncs.c'\" unpacked with wrong size! fi # end of 'ifuncs.c' fi if test -f 'expire.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'expire.c'\" else echo shar: Extracting \"'expire.c'\" \(28180 characters\) sed "s/^X//" >'expire.c' <<'END_OF_FILE' X/* X * This software is Copyright (c) 1986 by Rick Adams. X * X * Permission is hereby granted to copy, reproduce, redistribute or X * otherwise use this software as long as: there is no monetary X * profit gained specifically from the use or reproduction or this X * software, it is not sold, rented, traded or otherwise marketed, and X * this copyright notice is included prominently in any copy X * made. X * X * The author make no claims as to the fitness or correctness of X * this software for any use whatsoever, and it is provided as is. X * Any use of this software is at the user's own risk. X * X * expire - expire daemon runs around and nails all articles that X * have expired. X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)expire.c 2.53 4/6/87"; X#endif /* SCCSID */ X X#include "params.h" X#include X#if defined(BSD4_2) || defined(BSD4_1C) X# include X# include X#else X# include "ndir.h" X#endif X X#ifdef LOCKF X#include X#endif /* LOCKF */ X Xchar *Progname = "expire"; /* used by xerror to identify failing program */ X X/* Number of array entries to allocate at a time. */ X#define SPACE_INCREMENT 1000 X Xstruct expdata { X char *e_name; X long e_min, e_max; X time_t e_droptime, e_expiretime; X char e_ignorexp; X char e_doarchive; X char e_doexpire; X}; X Xextern int errno; Xchar NARTFILE[BUFLEN], OARTFILE[BUFLEN]; Xchar PAGFILE[BUFLEN], DIRFILE[BUFLEN]; Xchar NACTIVE[BUFLEN], OACTIVE[BUFLEN]; Xchar recdate[BUFLEN]; Xlong rectime, exptime; Xextern char *OLDNEWS; Xint verbose = 0; /* output trace information */ Xint ignorexp = 0; /* ignore Expire: lines */ Xint doarchive = 0; /* archive articles in SPOOL/oldnews */ Xint nohistory = 0; /* ignore history file */ Xint dorebuild = 0; /* rebuild history file */ Xint dorbldhistory = 0; /* rebuild history.d directory */ Xint usepost = 0; /* use posting date to expire */ Xint frflag = 0; /* expire specific user */ Xint doupdateactive = 0; /* update ACTIVE file */ Xchar baduser[BUFLEN]; Xextern char filename[], nbuf[]; X Xstruct timeb Now; X X/* X * This code uses realloc to get more of the multhist array. X */ Xstruct multhist { X char *mh_ident; X char *mh_file; X} *multhist; Xunsigned int mh_size; Xchar *calloc(); Xchar *realloc(); Xstruct tm *gmtime(); X X#ifdef DBM Xtypedef struct { X char *dptr; X int dsize; X} datum; X#else XFILE *nexthistfile(); X#endif /* !DBM */ X Xlong expincr; Xlong dropincr; Xlong atol(); Xtime_t cgtdate(), time(); XFILE *popen(); Xstruct passwd *pw; Xstruct group *gp; Xchar arpat[LBUFLEN]; Xint arpatlen = 0; Xchar ngpat[LBUFLEN]; Xint ngpatlen = 0; Xchar afline[BUFLEN]; Xchar grpsleft[BUFLEN]; Xstruct hbuf h; Xint ExpireLock; Xint rmlock(); Xtime_t today; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X pathinit(); X (void) umask(N_UMASK); X username = NEWSUSR; X X /* X * Try to run as NEWSUSR/NEWSGRP X */ X if ((pw = getpwnam(NEWSUSR)) == NULL) X xerror("Cannot get NEWSUSR pw entry"); X X uid = pw->pw_uid; X if ((gp = getgrnam(NEWSGRP)) == NULL) X xerror("Cannot get NEWSGRP gr entry"); X gid = gp->gr_gid; X (void) setgid(gid); X (void) setuid(uid); X X if (signal(SIGHUP, SIG_IGN) != SIG_IGN) X signal(SIGHUP, rmlock); X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X signal(SIGINT, rmlock); X expincr = DFLTEXP; X dropincr = HISTEXP; X ngpat[0] = ','; X arpat[0] = ','; X while (argc > 1) { X switch (argv[1][1]) { X case 'v': X if (isdigit(argv[1][2])) X verbose = argv[1][2] - '0'; X else if (argc > 2 && argv[2][0] != '-') { X X argv++; X argc--; X verbose = atoi(argv[1]); X } else X verbose = 1; X if (verbose < 3) X setbuf(stdout, (char *)NULL); X break; X case 'e': /* Use this as default expiration time */ X if (argc > 2 && argv[2][0] != '-') { X argv++; X argc--; X expincr = atol(argv[1]) * DAYS; X } else if (isdigit(argv[1][2])) X expincr = atol(&argv[1][2]) * DAYS; X break; X case 'E': /* Use this as default forget time */ X if (argc > 2 && argv[2][0] != '-') { X argv++; X argc--; X dropincr = atol(argv[1]) * DAYS; X } else if (isdigit(argv[1][2])) X dropincr = atol(&argv[1][2]) * DAYS; X break; X case 'I': /* Ignore any existing expiration date */ X ignorexp = 2; X break; X case 'i': /* Ignore any existing expiration date */ X ignorexp = 1; X break; X case 'n': X if (argc > 2) { X argv++; X argc--; X while (argc > 1 && argv[1][0] != '-') { X int argvlen; X argvlen = strlen(argv[1]); X if (ngpatlen + argvlen + 2 > sizeof (ngpat)) { X xerror("Too many groups specified for -n\n"); X } X if (ngpat[ngpatlen] == '\0') { X ngpat[ngpatlen++] = ','; X ngpat[ngpatlen] = '\0'; X } X strcpy(&ngpat[ngpatlen], argv[1]); X ngpatlen += argvlen; X argv++; X argc--; X } X argv--; X argc++; X } X break; X case 'a': /* archive expired articles */ X if (access(OLDNEWS,0) < 0){ X perror(OLDNEWS); X xerror("No archiving possible\n"); X } X doarchive++; X if (argc > 2) { X argv++; X argc--; X while (argc > 1 && argv[1][0] != '-') { X int argvlen; X argvlen = strlen(argv[1]); X if (arpatlen + argvlen + 2 > sizeof (arpat)) { X xerror("Too many groups specified for -a\n"); X } X if (arpat[arpatlen] == '\0') { X arpat[arpatlen++] = ','; X arpat[arpatlen] = '\0'; X } X strcpy(&arpat[arpatlen], argv[1]); X arpatlen += argvlen; X argv++; X argc--; X } X argv--; X argc++; X } X break; X case 'h': /* ignore history */ X nohistory++; X break; X case 'r': /* rebuild history file */ X dorebuild++; X nohistory++; X break; X case 'R': /* just rebuild the dbm files */ X#ifdef DBM X rebuilddbm(); X xxit(0); X#else /* !DBM */ X fprintf(stderr, "You have not compiled expire with DBM, so -R is meaningless\n"); X xxit(1); X#endif /* !DBM */ X X case 'p': /* use posting date to expire */ X usepost++; X break; X case 'f': /* expire messages from baduser */ X frflag++; X if (argc > 2) { X strcpy(baduser, argv[2]); X argv++; X argc--; X } X break; X case 'u': /* update the active file from 2.10.1 fmt */ X doupdateactive++; X break; X case 'H': /* convert to history.d format */ X dorbldhistory++; X break; X default: X printf("Usage: expire [ -v [level] ] [-e days ] [-i] [-a] [-r] [-h] [-p] [-u] [-f username] [-n newsgroups] [-H]\n"); X xxit(1); X } X argc--; X argv++; X } X if (dorbldhistory) { X#ifndef DBM X rebuildhistorydir(); X#endif /* !DBM */ X exit(0); X } X if (dropincr < expincr) { X dropincr = HISTEXP; X fprintf(stderr, "History expiration time < article expiration time. Default used.\n"); X } X if (ngpat[0] == ',') X (void) strcpy(ngpat, "all,"); X if (arpat[0] == ',') X (void) strcpy(arpat, "all,"); X (void) ftime(&Now); X today = Now.time; X if (chdir(SPOOL)) X xerror("Cannot chdir %s", SPOOL); X X if (verbose) { X printf("expire: nohistory %d, rebuild %d, doarchive %d\n", X nohistory, dorebuild, doarchive); X printf("newsgroups: %s\n",ngpat); X if (doarchive) X printf("archiving: %s\n",arpat); X } X X#ifdef DBM X (void) sprintf(OARTFILE, "%s/%s", LIB, "ohistory"); X#endif /* DBM */ X (void) sprintf(NARTFILE, "%s/%s", LIB, "nhistory"); X X (void) sprintf(OACTIVE, "%s/%s", LIB, "oactive"); X (void) sprintf(NACTIVE, "%s/%s", LIB, "nactive"); X X if (!doupdateactive) { X expire(); X#ifndef DBM X rebuildhistorydir(); X#endif X } X X updateactive(); X rmlock(); X X /* X * Now read in any saved news. X */ X#ifdef PROFILING X monitor((int(*)())0,(int(*)())0,0,0,0); X#endif /* PROFILING */ X#ifdef IHCC X /*afline happens to be available - (we're getting out anyway)*/ X sprintf(afline, "%s/%s", logdir(HOME), RNEWS); X execl(afline, "rnews", "-U", (char *)NULL); X#else /* ! IHCC */ X execl(RNEWS, "rnews", "-U", (char *)NULL); X#endif /* ! IHCC */ X perror(RNEWS); X xxit(1); X /* NOTREACHED */ X} X Xexpire() X{ X register char *p1, *p2, *p3; X register time_t newtime; X register FILE *fp = NULL; X FILE *ohfd, *nhfd; X int i; X char fn[BUFLEN]; X DIR *ngdirp = NULL; X static struct direct *ngdir; X X#ifdef DBM X if (!dorebuild) { X (void) sprintf(PAGFILE, "%s/%s", LIB, "nhistory.pag"); X (void) sprintf(DIRFILE, "%s/%s", LIB, "nhistory.dir"); X (void) close(creat(PAGFILE, 0666)); X (void) close(creat(DIRFILE, 0666)); X initdbm(NARTFILE); X } X#endif X X if (nohistory) { X ohfd = xfopen(ACTIVE, "r"); X if (dorebuild) { X /* Allocate initial space for multiple newsgroup (for X an article) array */ X multhist = (struct multhist *)calloc (SPACE_INCREMENT, X sizeof (struct multhist)); X mh_size = SPACE_INCREMENT; X X (void) sprintf(afline, "exec sort -t\t +1.6 -2 +1 >%s", NARTFILE); X printf("%s\n",afline); X if ((nhfd = popen(afline, "w")) == NULL) X xerror("Cannot exec %s", afline); X } else X nhfd = xfopen("/dev/null", "w"); X } else { X#ifdef DBM X ohfd = xfopen(ARTFILE, "r"); X#else X ohfd = nexthistfile((FILE *)NULL); X#endif /* DBM */ X nhfd = xfopen(NARTFILE, "w"); X } X X dolock(); X X for(i=0;id_name)); X X p2 = fn; X if (verbose > 2) X printf("article: %s\n", fn); X strcpy(filename, dirname(fn)); X fp = access(filename, 04) ? NULL : art_open(filename, "r"); X } else { X char dc; X#ifdef DBM X if (fgets(afline, BUFLEN, ohfd) == NULL) X break; X#else X if (fgets(afline, BUFLEN, ohfd) == NULL) X if (!(ohfd = nexthistfile(ohfd))) X break; X else X continue; X#endif /* DBM */ X if (verbose > 2) X printf("article: %s", afline); X p1 = index(afline, '\t'); X if (!p1) X continue; X *p1 = '\0'; X (void) strcpy(h.ident, afline); X *p1 = '\t'; X p2 = index(p1 + 1, '\t'); X if (!p2) X continue; X *p2 = '\0'; X (void) strcpy(recdate, p1+1); X rectime = cgtdate(recdate); X *p2++ = '\t'; X (void) strcpy(nbuf, p2); X p3 = index(nbuf, '/'); X if (p3) { X register char *p4; X X p4 = index(p3, '\n'); X if (p4) { X while (p4[-1] == ' ') X p4--; X *p4 = '\0'; X } X X /* X * convert list of newsgroups from X * ng1/num ng2/num ... X * to X * ng1,ng2,... X */ X p4 = p3; X do { X *p3++ = NGDELIM; X while (*p4 != '\0' && *p4 != ' ') X p4++; X if (*p4++ == '\0') { X *--p3 = '\0'; X break; X } X while (*p3 = *p4++) { X if (*p3 == '/') X break; X else X p3++; X } X } while (*p3); X } else { X /* X * Nothing after the 2nd tab. This happens X * when there is no message left in the spool X * directory, only the memory of it in the X * history file. (That is, it got cancelled X * or expired.) Use date in the history file X * to decide if we should keep the memory. X */ X grpsleft[0] = '\0'; X goto checkdate; X } X if (!ngmatch(nbuf, ngpat) || X ((rectime+expincr > today) && !dorebuild && X !frflag && !usepost && recdate[0] != ' ')) X goto keephist; X if (!dorebuild && !frflag && !usepost && X recdate[0] != ' ') { X grpsleft[0] = '\0'; X goto nailit; /* just expire it */ X } X X /* X * Look for the file--possibly several times, X * if it was posted to several news groups. X */ X dc = ' '; X p3 = p2; X while (dc != '\n') { X p1 = index(p3, ' '); X if (p1) { X dc = ' '; X *p1 = '\0'; X } else { X p1 = index(p3, '\n'); X if (p1 && p1 > p3) { X dc = '\n'; X *p1 = '\0'; X } else { X fp = NULL; X break; X } X } X strcpy(filename, dirname(p3)); X if (access(filename, 4) == 0 && X ((fp=art_open(filename, "r")) != NULL)) X break; X p3 = p1 + 1; X } X if (p1) X *p1 = dc; X } X X if (fp == NULL) { X /* X * this probably means that the article has been X * cancelled. Lets assume that, and make an X * entry in the history file to that effect. X */ X if (verbose) X perror(filename); X strcpy(p2, "cancelled\n"); X grpsleft[0] = '\0'; X goto checkdate; X } X for(i=0; itm_mon+1, tm->tm_mday, tm->tm_year, X tm->tm_hour, tm->tm_min, filename) X == EOF) X xerror("History write failed"); X (void) fclose(fp); X continue; X } X for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++) { X if (mhp->mh_file == NULL) X continue; X if (strcmp(mhp->mh_ident, h.ident)) X continue; X (void) strcat(filename, " "); X (void) strcat(filename, mhp->mh_file); X free(mhp->mh_file); X mhp->mh_file = NULL; X /* X * if we have all the links, write to hist now X */ X if (chrcnt(filename, ' ') == chrcnt(cp,NGDELIM)) X goto saveit; X break; X } X X /* X * Here is where we realloc the multhist space rather X * than the old way of static allocation. It is X * really trivial. We just clear out the space X * in case it was reused. The old static array was X * guaranteed to be cleared since it was cleared when X * the process started. X */ X if (mhp >= multhist + mh_size) { X multhist = (struct multhist *) X realloc ((char *)multhist, X sizeof (struct multhist) * X (SPACE_INCREMENT + mh_size)); X if (multhist == NULL) X xerror("Too many articles with multiple newsgroups"); X for (mhp = multhist + mh_size; X mhp < multhist+mh_size+SPACE_INCREMENT; X mhp++) { X mhp->mh_ident = NULL; X mhp->mh_file = NULL; X } X mhp = multhist + mh_size; X mh_size += SPACE_INCREMENT; X } X X if (mhp->mh_ident == NULL) { X mhp->mh_ident = malloc(strlen(h.ident)+1); X (void) strcpy(mhp->mh_ident, h.ident); X } X cp = malloc(strlen(filename) + 1); X if (cp == NULL) X xerror("Out of memory"); X (void) strcpy(cp, filename); X mhp->mh_file = cp; X (void) fclose(fp); X continue; X } X X (void) fclose(fp); X X if (h.expdate[0]) { X Now.time = rectime; X exptime = cgtdate(h.expdate); X } X newtime = (usepost ? cgtdate(h.subdate) : rectime) + expincr; X if (!h.expdate[0] || ignorexp == 2 || X (ignorexp == 1 && newtime < exptime)) X exptime = newtime; X if (frflag ? strcmp(baduser,h.from)==0 : today >= exptime) { Xnailit: X#ifdef DEBUG X printf("cancel %s\n", filename); X#else /* !DEBUG */ X printf("cancel %s\n", filename); X if (verbose) X printf("cancel %s\n", h.ident); X ulall(p2, &h); X (void) sprintf(p2, "%s\n", grpsleft); X if (verbose > 2 && grpsleft[0]) X printf("Some good in %s\n", h.ident); X#endif /* !DEBUG */ X } else { X if (verbose > 2) X printf("Good article %s\n", h.ident); X grpsleft[0] = '!'; X } X Xcheckdate: X if (grpsleft[0] == '\0' && today >= rectime + dropincr) { X if (verbose > 3) X printf("Drop history of %s - %s\n", X h.ident, recdate); X } else { X#ifdef DBM X long hpos; X#endif /* DBM */ Xkeephist: X#ifdef DBM X hpos = ftell(nhfd); X#endif /* DBM */ X X if (verbose > 3) X printf("Retain history of %s - %s\n", X h.ident, recdate); X if (fputs(afline, nhfd) == EOF) X xerror("history write failed"); X#ifdef DBM X if (!dorebuild) X remember(h.ident, hpos); X#endif /* DBM */ X } X } Xout: X if (dorebuild) { X register struct multhist *mhp; X struct tm *tm; X for (mhp = multhist; mhp < multhist+mh_size && mhp->mh_ident != NULL; mhp++) X if (mhp->mh_file != NULL) { X if (verbose) X printf("Article: %s [%s] Cannot find all links\n", mhp->mh_ident, mhp->mh_file); X (void) sprintf(filename,"%s/%s",SPOOL,mhp->mh_file); X for (p1 = filename; *p1 != ' ' && *p1 != '\0'; p1++) X if (*p1 == '.') X *p1 = '/'; X *p1 = '\0'; X if ((fp = art_open(filename, "r")) == NULL) { X if (verbose) X printf("Can't open %s.\n", filename); X continue; X } X if (!hread(&h, fp, TRUE)) { X printf("Garbled article %s.\n", filename); X (void) fclose(fp); X continue; X } else { X struct stat statb; X if (fstat(fileno(fp), &statb) < 0) X rectime = cgtdate(h.subdate); X else X rectime = statb.st_mtime; X } X tm = gmtime(&rectime); X if ( fprintf(nhfd, X#ifdef USG X "%s\t%s%2.2d/%2.2d/%d %2.2d:%2.2d\t%s\n", X#else /* !USG */ X "%s\t%s%02d/%02d/%d %02d:%02d\t%s\n", X#endif /* !USG */ X h.ident, h.expdate[0] ? " " : "", X tm->tm_mon+1, tm->tm_mday, tm->tm_year, X tm->tm_hour, tm->tm_min, mhp->mh_file) X == EOF ) X xerror("History write failed"); X (void) fclose(fp); X continue; X } X (void) pclose(nhfd); X free ((char *)multhist); X } else X if (fclose(nhfd)) X xerror("History write failed, %s", errmsg(errno)); X X if (dorebuild || !nohistory) { X#ifndef DBM X (void) rename(ARTFILE, OARTFILE); X#endif /* !DBM */ X (void) rename(NARTFILE, ARTFILE); X#ifdef DBM X if (dorebuild) X rebuilddbm( ); X else { X char tempname[BUFLEN]; X (void) sprintf(tempname,"%s.pag", ARTFILE); X (void) strcat(NARTFILE, ".pag"); X (void) rename(NARTFILE, tempname); X (void) sprintf(tempname,"%s.dir", ARTFILE); X (void) strcpy(rindex(NARTFILE, '.'), ".dir"); X (void) rename(NARTFILE, tempname); X } X#endif X } X} X X#if defined(BSD4_2) || defined(LOCKF) Xstatic int LockFd = -1; X#endif X Xdolock() X{ X /* set up exclusive locking so inews does not run while expire does */ X#if defined(BSD4_2) || defined(LOCKF) X LockFd = open(ACTIVE, 2); X# ifdef LOCKF X if (lockf(LockFd, F_LOCK, 0) < 0) X# else /* BSD4_2 */ X if (flock(LockFd, LOCK_EX) < 0) X# endif /* BSD4_2 */ X xerror("Can't get lock for expire: %s", errmsg(errno)); X#else /* !BSD4_2 && !LOCKF */ X int i = 0; X sprintf(afline,"%s.lock", ACTIVE); X while (LINK(ACTIVE, afline) < 0 && errno == EEXIST) { X if (i++ > 5) X xerror("Can't get lock for expire"); X sleep(i*2); X } X#endif /* !BSD4_2 && !LOCKF */ X} X Xrmlock() X{ X#if defined(BSD4_2) || defined(LOCKF) X close(LockFd); X#else X sprintf(bfr, "%s.lock", ACTIVE); X (void) UNLINK(bfr); X#endif /* !BSD4_2 */ X} X Xupdateactive() X{ X register char *p1; X FILE *ohfd, *nhfd; X DIR *ngdirp = NULL; X static struct direct *ngdir; X X if (verbose) X printf("updating active file %s\n", ACTIVE); X ohfd = xfopen(ACTIVE, "r"); X nhfd = xfopen(NACTIVE, "w"); X do { X long n; X long maxart, minart; X char cansub; X int gdsize, hassubs; X struct stat stbuf; X X if (fgets(afline, BUFLEN, ohfd) == NULL) X continue; X if (sscanf(afline,"%s %ld %ld %*c%c",nbuf,&maxart, &minart, X &cansub) < 4) X xerror("Active file corrupt"); X if (verbose > 3) X printf("looking at group %s\n", nbuf); X if (!ngmatch(nbuf, ngpat)) { X if (fputs(afline, nhfd) == EOF) X xerror("active file write failed"); X continue; X } X minart = 99999L; X /* Change a group name from a.b.c to a/b/c */ X for (p1=nbuf; *p1; p1++) X if (*p1 == '.') X *p1 = '/'; X X hassubs = stat(nbuf, &stbuf) != 0 || stbuf.st_nlink != 2; X gdsize = strlen(nbuf); X if ((ngdirp = opendir(nbuf)) != NULL) { X while (ngdir = readdir(ngdirp)) { X nbuf[gdsize] = '/'; X (void) strcpy(&nbuf[gdsize+1], ngdir->d_name); X /* We have to do a stat because of micro.6809 */ X if (hassubs && (stat(nbuf, &stbuf) < 0 || X !(stbuf.st_mode&S_IFREG)) ) X continue; X n = atol(ngdir->d_name); X if (n > 0 && n < minart) X minart = n; X if (n > 0 && n > maxart) X maxart = n; X } X closedir(ngdirp); X } X afline[gdsize] = '\0'; X if (minart > maxart) X minart = maxart; X#ifdef USG X if (verbose > 4) X printf("\tmaxart = %5.5ld, minart = %5.5ld\n", X maxart, minart); X if (fprintf(nhfd,"%s %5.5ld %5.5ld %c\n", afline, maxart, X minart, cansub) == EOF) X xerror("Active file write failed"); X#else X if (verbose > 4) X printf("\tmaxart = %05ld, minart = %05ld\n", X maxart, minart); X if (fprintf(nhfd,"%s %05ld %05ld %c\n", afline, maxart, X minart, cansub) == EOF) X xerror("Active file write failed"); X#endif /* !USG */ X } while (!feof(ohfd)); X if (fclose(nhfd)) X xerror("Active file write failed, %s", errmsg(errno)); X (void) fclose(ohfd); /* this might unlock inews as a side effect */ X X (void) rename(ACTIVE, OACTIVE); X (void) rename(NACTIVE, ACTIVE); X} X X/* Unlink (using unwound tail recursion) all the articles in 'artlist'. */ Xulall(artlist, hp) Xchar *artlist; Xstruct hbuf *hp; X{ X register char *p, *q; X int last = 0; X char newname[BUFLEN]; X time_t timep[2]; X char *fn; X X grpsleft[0] = '\0'; X do { X if (verbose > 2) X printf("ulall '%s', '%s'\n", artlist, hp->subdate); X if (nohistory) { X last = 1; X } else { X while (*artlist == ' ' || *artlist == '\n' || *artlist == ',') X artlist++; X if (*artlist == '\0') X return; X p = index(artlist, ' '); X if (p == NULL) { X last = 1; X p = index(artlist, '\n'); X } X if (p == NULL) { X last = 1; X fn = dirname(artlist); X if (UNLINK(fn) < 0 && errno != ENOENT) X perror(fn); X return; X } X if (p) X *p = 0; X } X strcpy(newname, artlist); X q = index(newname,'/'); X if (q) { X *q++ = NGDELIM; X *q = '\0'; X } else { X q = index(newname, '\0'); X if (q == artlist) /* null -> the end */ X return; X /* should be impossible to get here */ X } X fn = dirname(artlist); X if (ngmatch(newname, ngpat)) { X if (doarchive){ X if (ngmatch(newname, arpat)) { X q = fn + strlen(SPOOL) + 1; X (void) sprintf(newname, "%s/%s", OLDNEWS, q); X if (verbose) X printf("link %s to %s\n", fn, newname); X if (LINK(fn, newname) == -1) { X if (mkparents(newname) == 0) X if (LINK(fn, newname) == -1) X fcopy(fn, newname); X } X timep[0] = timep[1] = cgtdate(hp->subdate); X (void) utime(newname, timep); X } X } X if (verbose) X printf("unlink %s\n", fn); X if (UNLINK(fn) < 0 && errno != ENOENT) X perror(fn); X } else { X if (verbose > 3) X printf("retain %s (%s)\n", hp->ident, fn); X strcat(grpsleft, artlist); X strcat(grpsleft, " "); X } X artlist = p + 1; X } while (!last); X} X Xfcopy(fn, newname) Xchar *fn, *newname; X{ X int f1, f2; X int r; X char buf[BUFSIZ]; X f1 = open(fn, 0); X if (f1 < 0) X return -1; X f2 = open(newname, 1); X if (f2 < 0) { X if (errno == ENOENT) { X f2 = creat(newname,0644); X if (f2 < 0) { X close(f1); X return -1; X } X } else { X close(f1); X return -1; X } X } X while((r=read(f1, buf, BUFSIZ)) > 0) X write(f2, buf, r); X (void) close(f1); X (void) close(f2); X return 0; X} X X/* X * Count instances of c in s X */ Xchrcnt(s, c) Xregister char *s; Xregister c; X{ X register n = 0; X register cc; X X while (cc = *s++) X if (cc == c) X n++; X return n; X} X X/* X * If any parent directories of this dir don't exist, create them. X */ Xmkparents(fullname) Xchar *fullname; X{ X char buf[200]; X register char *p; X int rc; X X (void) strcpy(buf, fullname); X p = rindex(buf, '/'); X if (p) X *p = '\0'; X if (access(buf, 0) == 0) X return 0; X mkparents(buf); X if ((rc = mkdir(buf, 0755)) < 0) X perror("mkdir failed"); X if (verbose) X printf("mkdir %s, rc %d\n", buf, rc); X X return rc; X} X X/* Make sure this file is a legal article. */ Xislegal(fullname, path, name) Xregister char *fullname; Xregister char *path; Xregister char *name; X{ X struct stat buffer; X X (void) sprintf(fullname, "%s/%s", path, name); X X /* make sure the article is numeric. */ X while (*name != '\0') X if (!isascii(*name) || !isdigit(*name)) X return 0; X else X name++; X X /* Now make sure we don't have a group like net.micro.432, X * which is numeric but not a regular file -- i.e., check X * for being a regular file. X */ X if ((stat(fullname, &buffer) == 0) && X ((buffer.st_mode & S_IFMT) == S_IFREG)) { X /* Now that we found a legal group in a/b/c/4 X notation, switch it to a.b.c/4 notation. */ X for (name = fullname; name != NULL && *name != '\0'; name++) X if (*name == '/' && name != rindex (name, '/')) X *name = '.'; X X return 1; X } X return 0; X} X X#ifdef DBM X/* X * This is taken mostly intact from ../cvt/cvt.hist.c and is used at the X * end by the options that make a new history file. X * Routine to convert history file to dbm file. The old 3 field X * history file is still kept there, because we need it for expire X * and for a human readable copy. But we keep a dbm hashed copy X * around by message ID so we can answer the yes/no question "have X * we already seen this message". The content is the ftell offset X * into the real history file when we get the article - you can't X * really do much with this because the file gets compacted. X */ X XFILE *fd; X Xchar namebuf[BUFSIZ]; Xchar lb[BUFSIZ]; X Xrebuilddbm() X{ X register char *p; X long fpos; X X (void) sprintf(namebuf, "%s.dir", ARTFILE); X (void) close(creat(namebuf, 0666)); X (void) sprintf(namebuf, "%s.pag", ARTFILE); X (void) close(creat(namebuf, 0666)); X (void) sprintf(namebuf, "%s", ARTFILE); X X fd = fopen(namebuf, "r"); X if (fd == NULL) { X perror(namebuf); X xxit(2); X } X X initdbm(namebuf); X while (fpos=ftell(fd), fgets(lb, BUFSIZ, fd) != NULL) { X p = index(lb, '\t'); X if (p) X *p = 0; X remember(lb, fpos); X } X} X Xremember(article, fileoff) Xregister char *article; Xlong fileoff; X{ X datum lhs, rhs; X X lcase(article); X lhs.dptr = article; X lhs.dsize = strlen(article) + 1; X rhs.dptr = (char *) &fileoff; X rhs.dsize = sizeof fileoff; X X if (verbose > 5) X printf("remember: %s @ %ld\n", article, fileoff); X if (store(lhs, rhs) < 0) X xerror("dbm store failed"); X} X#else X/* X * Open the next history subdirectory file X */ X XFILE *nexthistfile(ofp) XFILE *ofp; X{ X static int histfilecounter = -1; X X if (ofp) X fclose(ofp); X do { X if (++histfilecounter > 9) X return NULL; X sprintf(bfr, "%s.d/%d", ARTFILE, histfilecounter); X if (verbose > 3) X printf("reading history file %s\n", bfr); X ofp = xfopen(bfr, "r"); X } while (ofp == NULL); X return ofp; X} X X/* X * Rebuild the history subdirectory from LIBDIR/history X */ Xrebuildhistorydir() X{ X char fn[BUFLEN], ofn[BUFLEN]; X register int i; X FILE *subfd[10], *ohfd; X X /* rebuild history subfiles */ X (void) sprintf(fn, "%s.od", ARTFILE); X if (access(fn,0) != 0) X (void) mkdir(fn, 0755); X (void) sprintf(fn, "%s.d", ARTFILE); X if (verbose) X printf("Rebuilding history subfile directory %s.\n", fn); X if (access(fn,0) != 0) X (void) mkdir(fn, 0755); X for (i = 0; i < 10; i++) { X (void) sprintf(fn, "%s.d/%c", ARTFILE, i + '0'); X (void) sprintf(ofn, "%s.od/%c", ARTFILE, i + '0'); X (void) rename(fn, ofn); X close(creat(fn, 0644)); X subfd[i] = xfopen(fn, "w+"); X } X ohfd = xfopen(ARTFILE, "r"); X while (fgets(fn, BUFLEN, ohfd) != NULL) { X i = findhfdigit(fn) - '0'; X fputs(fn, subfd[i]); X } X (void) fclose(ohfd); X for (i = 0; i < 10; i++) X if (ferror(subfd[i]) || fclose(subfd[i])) X xerror("History subfile write"); X (void) UNLINK(ARTFILE); X} X#endif /* !DBM */ X Xxxit(i) X{ X rmlock(); X exit(i); X} END_OF_FILE if test 28180 -ne `wc -c <'expire.c'`; then echo shar: \"'expire.c'\" unpacked with wrong size! fi # end of 'expire.c' fi echo shar: End of shell archive. exit 0 -- "Zeta Microcomputer Software" ACSnet: nick@ultima.cs.uts.oz UUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick Fidonet: Nick Andrew on 3:713/602 (Zeta)