rsalz@uunet.UU.NET (Rich Salz) (10/23/87)
Submitted-by: utzoo!henry (Henry Spencer) Posting-number: Volume 12, Issue 38 Archive-name: cnews/part13 #! /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 <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 13 (of 14)." PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'expire/expire.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'expire/expire.c'\" else echo shar: Extracting \"'expire/expire.c'\" \(20350 characters\) sed "s/^X//" >'expire/expire.c' <<'END_OF_FILE' X/* X * expire - expire old news X * X * One modest flaw: links are not preserved in archived copies, i.e. you X * get multiple copies of multiply-posted articles. Since link preservation X * is arbitrarily hard when control files get complex, to hell with it. X */ X X#include <stdio.h> X#include <ctype.h> X#include <string.h> X#include <errno.h> X#include <time.h> X#include <sys/types.h> X#include <sys/timeb.h> X#include <sys/stat.h> X#include "news.h" X#include "newspaths.h" X X#ifndef EPOCH X#define EPOCH ((time_t)0) X#endif X X X/* structure for dbm */ Xtypedef struct { X char *dptr; X int dsize; X} datum; X X#define DAY (24L*60L*60L) X X/* structure for expiry-control lists */ Xstruct ctl { X struct ctl *next; X char *groups; /* newsgroups */ X int ismod; /* moderated? */ X# define UNMOD 'u' X# define MOD 'm' X# define EITHER 'x' X time_t retain; /* earliest arrival date not expired */ X time_t normal; /* earliest not expired in default case */ X time_t purge; /* latest arrival date always expired */ X char *dir; /* Archive dir or NULL. */ X}; X X/* header for internal form of control file */ Xstruct ctl *ctls = NULL; Xstruct ctl *lastctl = NULL; X X/* X * Headers for lists by newsgroup, derived (mostly) from active file. X * Hashing is by length of newsgroup name; this is quick and works well, X * and there is no simple variation that does much better. X */ X#define NHASH 80 Xstruct ctl *ngs[NHASH] = { NULL }; X Xint debug = 0; /* for inews routines */ Xint expdebug = 0; /* expire debugging */ X Xint printexpiring = 0; /* print info line for expiring articles? */ Xchar *defarch = NULL; /* default archive dir */ X Xint fourfields = 0; /* old 4-field C news history file? */ X Xchar dont[] = "don't"; /* magic cookie for checkexpire() return */ X Xtime_t now; Xstruct timeb ftnow; /* ftime() result for getdate() */ X Xchar subject[MAXLINE] = ""; /* buffer for subject line, minus header */ X X/* X * Archive-copying buffer. X * 8KB buffer is large enough to take most articles at one gulp, X * and also large enough for virtual certainty of getting the X * Subject: line in the first bufferload. X */ Xchar abuf[8*1024]; X Xchar *progname; X Xextern int errno; Xextern long atol(); Xextern double atof(); Xextern char *malloc(); Xextern struct tm *gmtime(); Xextern time_t time(); X Xextern time_t getdate(); X X/* forwards */ XFILE *eufopen(); Xchar *enstring(); Xchar *checkexpire(); Xtime_t back(); Xvoid checkdir(); Xvoid errunlock(); Xvoid control(); Xvoid prime(); Xvoid doit(); Xvoid cd(); Xvoid process(); Xvoid warning(); Xvoid printstuff(); Xvoid expire(); Xvoid mkparents(); Xvoid getsubj(); Xvoid refill(); Xvoid printlists(); Xvoid pctl(); Xvoid fillin(); Xvoid ctlline(); X X/* X - main - parse arguments and handle options X */ Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X register int c; X register int errflg = 0; X register struct ctl *ct; X register FILE *cf; X extern int optind; X extern char *optarg; X X progname = argv[0]; X now = time((time_t *)NULL); X ftime(&ftnow); X X while ((c = getopt(argc, argv, "pa:od")) != EOF) X switch (c) { X case 'p': /* print info line for archived articles */ X printexpiring = 1; X break; X case 'a': /* archive in this directory */ X defarch = optarg; X break; X case 'o': /* old 4-field history file */ X fourfields = 1; X break; X case 'd': /* debug */ X expdebug = 1; X break; X case '?': X default: X errflg++; X break; X } X if (errflg || optind < argc-1) { X fprintf(stderr, "Usage: %s [-p] [-a archdir] [ctlfile]\n", X progname); X exit(2); X } X X if (optind < argc) { X cf = eufopen(argv[optind], "r"); X control(cf); X fclose(cf); X } else X control(stdin); X X prime(ctlfile("active")); X X if (defarch != NULL) X checkdir(defarch); X X if (expdebug) X printlists(); X X (void) umask(newsumask()); X doit(); /* side effect: newslock() */ X newsunlock(); X exit(0); X} X X/* X - control - pick up a control file X */ Xvoid Xcontrol(f) Xregister FILE *f; X{ X char ctl[MAXLINE]; X void ctlline(); X X while (fgets(ctl, sizeof(ctl), f) != NULL) { X ctl[strlen(ctl)-1] = '\0'; /* get rid of newline */ X ctlline(ctl); X } X} X X/* X - ctlline - process one control-file line X */ Xvoid Xctlline(ctl) Xchar *ctl; X{ X register struct ctl *ct; X char *field[4]; X char datebuf[50]; X char *dates[3]; X register int nf; X int ndates; X X errno = 0; X ct = (struct ctl *)malloc(sizeof(struct ctl)); X if (ct == NULL) X errunlock("out of memory for control list", ""); X X errno = 0; X nf = split(ctl, field, 4, "\t "); X if (nf != 4) X errunlock("bad control line: `%s...'", ctl); X X ct->groups = enstring(field[0]); X if (STREQ(field[1], "m")) X ct->ismod = MOD; X else if (STREQ(field[1], "u")) X ct->ismod = UNMOD; X else if (STREQ(field[1], "x")) X ct->ismod = EITHER; X else X errunlock("strange mod field `%s' in control file", field[1]); X X if (strlen(field[2]) > sizeof(datebuf)-1) X errunlock("date specification `%s' too long", field[2]); X (void) strcpy(datebuf, field[2]); X ndates = split(datebuf, dates, 3, "-"); X switch (ndates) { X case 3: X ct->retain = back(atof(dates[0])); X ct->normal = back(atof(dates[1])); X ct->purge = back(atof(dates[2])); X break; X case 2: X ct->retain = back(0.0); X ct->normal = back(atof(dates[0])); X ct->purge = back(atof(dates[1])); X break; X case 1: X ct->retain = back(0.0); X ct->normal = back(atof(dates[0])); X ct->purge = EPOCH; X break; X default: X errunlock("date processing foulup in `%s'", field[1]); X /* NOTREACHED */ X break; X } X if (ct->retain < ct->normal || ct->normal < ct->purge) X errunlock("preposterous dates: `%s'", field[2]); X X if (STREQ(field[3], "-")) X ct->dir = NULL; X else if (STREQ(field[3], "@")) { X if (defarch == NULL) X errunlock("@ in control file but no -a", ""); X ct->dir = defarch; X } else { X ct->dir = enstring(field[3]); X checkdir(ct->dir); X } X X /* put on end of list */ X ct->next = NULL; X if (lastctl == NULL) X ctls = ct; X else X lastctl->next = ct; X lastctl = ct; X} X X/* X - prime - prime control lists from active file X */ Xvoid Xprime(afile) Xchar *afile; X{ X char buf[MAXLINE]; X register FILE *af; X register struct ctl *ct; X# define NFACT 4 X char *field[NFACT]; X int nf; X register int hash; X void fillin(); X X af = eufopen(afile, "r"); X while (fgets(buf, sizeof(buf), af) != NULL) { X nf = split(buf, field, NFACT, " \t"); X if (nf != NFACT) X errunlock("bad active-file line for `%s'", field[0]); X ct = (struct ctl *)malloc(sizeof(struct ctl)); X if (ct == NULL) X errunlock("out of memory at newsgroup `%s'", field[0]); X ct->groups = enstring(field[0]); X ct->ismod = (strchr(field[3], 'm') != NULL) ? MOD : UNMOD; X fillin(ct); X hash = strlen(field[0]); X if (hash > NHASH-1) X hash = NHASH-1; X ct->next = ngs[hash]; X ngs[hash] = ct; X } X (void) fclose(af); X} X X/* X - fillin - fill in a ctl struct for a newsgroup from the control-file list X */ Xvoid Xfillin(ct) Xregister struct ctl *ct; X{ X register struct ctl *cscan; X X for (cscan = ctls; cscan != NULL; cscan = cscan->next) X if (ngmatch(cscan->groups, ct->groups) && X (cscan->ismod == ct->ismod || X cscan->ismod == EITHER)) { X ct->retain = cscan->retain; X ct->normal = cscan->normal; X ct->purge = cscan->purge; X ct->dir = cscan->dir; X return; X } X X /* oooooops... */ X errunlock("group `%s' not covered by control file", ct->groups); X} X X/* X - doit - file manipulation and master control X */ Xvoid Xdoit() X{ X register int old; X register FILE *new; X struct stat stbuf; X char buf[MAXLINE]; X char name[MAXLINE]; X long here; X register char *nameend; X datum lhs; X datum rhs; X register int ret; X X cd(ctlfile((char *)NULL)); X old = open("history", 0); X if (old < 0) X errunlock("cannot open `%s'", "history"); X errno = 0; X if (stat("history.n", &stbuf) >= 0) X errunlock("disaster -- history.n already exists!", ""); X new = eufopen("history.n", "w"); X fclose(eufopen("history.n.dir", "w")); X fclose(eufopen("history.n.pag", "w")); X if (dbminit("history.n") < 0) X errunlock("dbminit(history.n) failed", ""); X X cd(artfile((char *)NULL)); X while (readline(buf, sizeof(buf), old) >= 0) { X process(buf); X if (buf[0] != '\0') { X register char *p; X X /* extract the message-id, lowercase it */ X (void) strcpy(name, buf); X nameend = strchr(name, '\t'); X if (nameend == NULL) { X errno = 0; X errunlock("bad return from process(): `%s'", X name); X } X *nameend = '\0'; X for (p = name; *p != '\0'; p++) X if (isascii(*p) && isupper(*p)) X *p = tolower(*p); X X /* make the DBM entry */ X lhs.dptr = name; X lhs.dsize = strlen(name)+1; X here = ftell(new); X rhs.dptr = (char *)&here; X rhs.dsize = sizeof(here); X errno = 0; X ret = store(lhs, rhs); X if (ret < 0) X errunlock("dbm failure on `%s'", name); X X /* and the history entry */ X fputs(buf, new); X putc('\n', new); X } X } X /* side effect of readline() < 0: newslock() */ X X close(old); X fclose(new); X X cd(ctlfile((char *)NULL)); X (void) unlink("history.o"); X if (link("history", "history.o") < 0) X errunlock("can't move history", ""); X if (unlink("history") < 0) X errunlock("can't finish moving history", ""); X if (link("history.n", "history") < 0) X errunlock("disaster -- can't reinstate history!", ""); X if (unlink("history.n") < 0) X errunlock("disaster -- can't unlink history.n!", ""); X if (unlink("history.dir") < 0) X errunlock("disaster -- can't unlink history.dir!", ""); X if (unlink("history.pag") < 0) X errunlock("disaster -- can't unlink history.pag!", ""); X if (link("history.n.dir", "history.dir") < 0) X errunlock("disaster -- can't reinstate history.dir!", ""); X if (link("history.n.pag", "history.pag") < 0) X errunlock("disaster -- can't reinstate history.pag!", ""); X if (unlink("history.n.dir") < 0) X errunlock("disaster -- can't unlink history.n.dir!", ""); X if (unlink("history.n.pag") < 0) X errunlock("disaster -- can't unlink history.n.pag!", ""); X} X X/* X - process - handle one history line, modifying it if appropriate X * X * The line as supplied is modified if necessary, so that whatever X * is in that buffer when process() returns is what should be written X * to the new history file. An empty string ("") means "write nothing". X */ Xvoid Xprocess(line) Xchar *line; X{ X char work[MAXLINE]; X# define NF 4 /* number of fields in history line */ X char *field[NF]; X register int nf; X char keepnames[MAXLINE]; X# define NN 50 /* number of pathnames for article */ X char *names[NN]; X register int nn; X register char *dir; X time_t recdate; X time_t expdate; X register int i; X register char *scan; X char delim; X X if (expdebug) X fprintf(stderr, "\nprocess(%s)\n", line); X (void) strcpy(work, line); X nf = split(work, field, NF, "\t"); X if (!fourfields && nf == NF-1) { X field[3] = field[2]; X scan = strchr(field[1], '~'); X if (scan == NULL) X errunlock("bad date format `%s'", field[1]); X *scan++ = '\0'; X field[2] = scan; X nf++; X } X if (nf != NF) { X errno = 0; X warning("invalid history line: `%s'", line); X return; /* leaving the line in the new history file */ X } X if (strspn(field[1], "0123456789") == strlen(field[1])) X recdate = atol(field[1]); X else X recdate = getdate(field[1], &ftnow); X if (strspn(field[2], "0123456789") == strlen(field[2])) X expdate = atol(field[2]); X else if (!STREQ(field[2], "-") && !STREQ(field[2], "X")) X expdate = getdate(field[2], &ftnow); X else X expdate = 0; /* any old value */ X if (expdebug) X fprintf(stderr, "rec %ld, expire %ld\n", recdate, expdate); X X nn = split(field[3], names, NN, " ,"); X if (nn > NN) { X errno = 0; X warning("too many cross-postings in line `%s'", line); X return; /* leaving line in history file */ X } X X keepnames[0] = '\0'; X for (i = 0; i < nn; i++) { X if (expdebug) X fprintf(stderr, "link %s\n", names[i]); X dir = checkexpire(field, recdate, expdate, names[i]); X if (dir != dont) { X if (expdebug) X fprintf(stderr, "expire into %s\n", X (dir == NULL) ? "(null)" : dir); X for (scan = strchr(names[i], '.'); scan != NULL; X scan = strchr(scan+1, '.')) X *scan = '/'; X expire(names[i], dir); X if (dir != NULL && printexpiring) X printstuff(field, names[i], recdate); X } else { X (void) strcat(keepnames, " "); X (void) strcat(keepnames, names[i]); X } X } X X delim = (fourfields) ? '\t' : '~'; X if (keepnames[0] != '\0') { X if (STREQ(field[2], "X") || STREQ(field[2], "-")) X sprintf(line, "%s\t%ld%c%s\t%s", field[0], recdate, X delim, field[2], keepnames+1); X else X sprintf(line, "%s\t%ld%c%ld\t%s", field[0], recdate, X delim, expdate, keepnames+1); X } else X line[0] = '\0'; X X if (expdebug) X fprintf(stderr, "new line `%s'\n", line); X} X X/* X - checkexpire - should this article get expired? X * X * The "dont" variable's address is used as the don't-expire return value, X * since NULL is a legitimate archive-directory value internally. X * X * The check for an expiry date of 'X' is to support a possible future X * scheme in which the dbm file is not rebuilt every time. X */ Xchar * /* archive directory, NULL, or dont */ Xcheckexpire(field, recdate, expdate, name) Xchar *field[]; Xtime_t recdate; Xtime_t expdate; Xchar *name; X{ X char group[MAXFILE]; X register char *scan; X register struct ctl *ct; X time_t when; X register int hash; X X if (STREQ(field[2], "X")) X return(dont); /* vestigial entry */ X X (void) strcpy(group, name); X scan = strchr(group, '/'); X if (*scan == NULL) { X errno = 0; X errunlock("bad format in article path `%s'", name); X } else X *scan = '\0'; X X /* find applicable expiry-control struct (make it if necessary) */ X hash = strlen(group); X if (hash > NHASH-1) X hash = NHASH-1; X for (ct = ngs[hash]; ct != NULL && !STREQ(ct->groups, group); X ct = ct->next) X continue; X if (ct == NULL) { /* oops, there wasn't one */ X if (expdebug) X fprintf(stderr, "new group `%s'\n", group); X ct = (struct ctl *)malloc(sizeof(struct ctl)); X if (ct == NULL) X errunlock("out of memory for newsgroup `%s'", group); X ct->groups = enstring(group); X ct->ismod = UNMOD; /* unknown -- treat it as mundane */ X fillin(ct); X ct->next = ngs[hash]; X ngs[hash] = ct; X } X X /* decide whether it ought to expire */ X if (recdate >= ct->retain) /* within retention period */ X return(dont); X if (recdate <= ct->purge) /* past purge date */ X return(ct->dir); X if (!STREQ(field[2], "-")) { /* explicit expiry date */ X if (now >= expdate) /* past its explicit date */ X return(ct->dir); X else X return(dont); X } else { X if (recdate < ct->normal) /* past default date */ X return(ct->dir); X else X return(dont); X } X} X X/* X - expire - expire an article X */ Xvoid Xexpire(name, dir) Xchar *name; Xchar *dir; X{ X char old[MAXFILE]; X char new[MAXFILE]; X struct stat stbuf; X X (void) strcpy(old, artfile(name)); X if (dir != NULL) { X sprintf(new, "%s/%s", dir, name); X /* cp() usually succeeds, so try it before getting fancy */ X if (cp(old, new) < 0) { X if (stat(old, &stbuf) < 0) X return; /* nonexistent */ X mkparents(name, dir); X if (cp(old, new) < 0) { X warning("can't archive `%s'", name); X return; /* without removing it */ X } X } X } X if (unlink(old) < 0 && errno != ENOENT) X warning("can't remove `%s'", name); X} X X/* X - cp - try to copy an article X */ Xint /* 0 success, -1 failure */ Xcp(src, dst) Xchar *src; /* absolute pathnames */ Xchar *dst; X{ X register int ret; X register int count; X register int in, out; X register int first = 1; X X in = open(src, 0); X if (in < 0) X return(-1); X out = creat(dst, 0666); X if (out < 0) { X close(in); X return(-1); X } X X while ((count = read(in, abuf, sizeof(abuf))) > 0) { X ret = write(out, abuf, count); X if (ret != count) X errunlock("write error in copying `%s'", src); X if (first) { X getsubj(abuf, count); X first = 0; X } X } X if (count < 0) X errunlock("read error in copying `%s'", src); X X close(in); X close(out); X return(0); X} X X/* X - getsubj - try to find the Subject: line in a buffer X * X * Result goes in "subject", and is never empty. Tabs become spaces, X * since they are the output delimiters. X */ Xvoid Xgetsubj(buf, bsize) Xchar *buf; Xint bsize; X{ X register char *scan; X register char *limit; X register int len; X register int clipped; X static char sline[] = "Subject:"; X X len = strlen(sline); X limit = buf + bsize - len; X for (scan = buf; scan < limit; scan++) X if (STREQN(scan, sline, len) && X (scan == buf || *(scan-1) == '\n')) { X scan += len; X for (limit = scan; limit < buf+bsize; limit++) X if (*limit == '\n') X break; X while (scan < limit && isspace(*scan)) X scan++; X len = limit-scan; X clipped = 0; X if (len > sizeof(subject)-1) { X len = sizeof(subject) - 1 - strlen("..."); X clipped = 1; X } X if (len > 0) { X (void) strncpy(subject, scan, len); X subject[len] = '\0'; X } else X (void) strcpy(subject, "???"); X if (clipped) X (void) strcat(subject, "..."); X for (scan = strchr(subject, '\t'); scan != NULL; X scan = strchr(scan+1, '\t')) X *scan = ' '; X return; X } else if (*scan == '\n' && scan+1 < limit && *(scan+1) == '\n') X break; /* empty line terminates header */ X X /* didn't find one -- fill in *something* */ X (void) strcpy(subject, "???"); X} X X/* X - mkparents - try to make directories for archiving an article X */ Xvoid Xmkparents(art, dir) Xchar *art; /* name relative to dir */ Xchar *dir; X{ X char name[MAXFILE]; X char cmd[MAXLINE]; X register char *p; X register int len; X static char quiet[] = " >/dev/null 2>/dev/null"; X X (void) strcpy(name, art); X sprintf(cmd, "cd %s; PATH=/bin:/usr/bin mkdir ", dir); X len = strlen(cmd) + sizeof(quiet); X for (p = strchr(name, '/'); p != NULL; p = strchr(p+1, '/')) { X *p = '\0'; X if (len + (p - name) + 1 >= sizeof(cmd)-1) X errunlock("line overflow in mkdiring for `%s'", art); X (void) strcat(cmd, name); X (void) strcat(cmd, " "); X *p = '/'; X } X (void) strcat(cmd, quiet); X (void) system(cmd); X} X Xchar *months[12] = { X "Jan", X "Feb", X "Mar", X "Apr", X "May", X "Jun", X "Jul", X "Aug", X "Sep", X "Oct", X "Nov", X "Dec", X}; X X/* X - printstuff - print information about an expiring article X */ Xvoid Xprintstuff(field, name, recdate) Xchar *field[]; Xchar *name; Xtime_t recdate; X{ X struct tm *gmt; X X gmt = gmtime(&recdate); X printf("%s\t%s\t%d-%s-%d\t%s\n", name, field[1], gmt->tm_mday, X months[gmt->tm_mon], gmt->tm_year+1900, subject); X} X X/* X - split - divide a line into fields, like awk split() X */ Xint /* number of fields */ Xsplit(line, fields, nfmax, sep) Xchar *line; Xchar *fields[]; Xint nfmax; Xchar *sep; X{ X register int i; X register char *p; X X i = 0; X for (p = strtok(line, sep); p != NULL; p = strtok((char *)NULL, sep)) { X if (i < nfmax) X fields[i] = p; X i++; X } X X return(i); X} X X/* X - eufopen - fopen, with errunlock if doesn't succeed X */ XFILE * Xeufopen(name, mode) Xchar *name; Xchar *mode; X{ X FILE *f; X X f = fopen(name, mode); X if (f == NULL) X errunlock("can't open `%s'", name); X return(f); X} X X/* X - checkdir - check that a directory is real, writeable, and full pathname X */ Xvoid /* errunlock() if not */ Xcheckdir(dir) Xchar *dir; X{ X struct stat stbuf; X X if (stat(dir, &stbuf) < 0 || (stbuf.st_mode&S_IFMT) != S_IFDIR) X errunlock("`%s' is not a directory", dir); X if (access(dir, 02) < 0) X errunlock("directory `%s' not writeable", dir); X errno = 0; X if (dir[0] != '/') X errunlock("directory `%s' not an absolute pathname", dir); X} X X/* X - enstring - malloc space for a string X */ Xchar * /* errunlock() if malloc fails */ Xenstring(s) Xchar *s; X{ X register char *p; X X errno = 0; X p = malloc((unsigned)strlen(s)+1); /* +1 for NUL */ X if (p == NULL) X errunlock("cannot malloc for `%s'", s); X (void) strcpy(p, s); X return(p); X} X X/* X - back - get a date n days back, with overflow check X * X * Requires that "now" be set first. X */ Xtime_t Xback(ndays) Xdouble ndays; X{ X if (ndays > now / DAY) /* past beginning of time */ X return((time_t)0); X return(now - ndays*DAY); X} X X/* X - printlists - print control lists for debugging X */ Xvoid Xprintlists() X{ X register int i; X register struct ctl *ct; X X fprintf(stderr, "control file:\n"); X for (ct = ctls; ct != NULL; ct = ct->next) X pctl(ct); X fprintf(stderr, "\n"); X X for (i = 0; i < NHASH; i++) X if (ngs[i] != NULL) { X fprintf(stderr, "list %d:\n", i); X for (ct = ngs[i]; ct != NULL; ct = ct->next) X pctl(ct); X } X fprintf(stderr, "\n"); X} X X/* X - pctl - print one control-list entry X */ Xvoid Xpctl(ct) Xregister struct ctl *ct; X{ X# define DAYS(x) ((now-(x))/(double)DAY) X X fprintf(stderr, "%s(%c) %.1f-%.1f-%.1f %s\n", ct->groups, ct->ismod, X DAYS(ct->retain), DAYS(ct->normal), DAYS(ct->purge), X (ct->dir == NULL) ? "(null)" : ct->dir); X} X X/* X - unprivileged - no-op needed to keep the pathname stuff happy X */ Xvoid Xunprivileged() X{ X} END_OF_FILE if test 20350 -ne `wc -c <'expire/expire.c'`; then echo shar: \"'expire/expire.c'\" unpacked with wrong size! fi # end of 'expire/expire.c' fi if test -f 'rna/postnews.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rna/postnews.c'\" else echo shar: Extracting \"'rna/postnews.c'\" \(20673 characters\) sed "s/^X//" >'rna/postnews.c' <<'END_OF_FILE' X/* X * postnews [-h] [-s subject] [-n newsgroups] [-e expiredate] X * [-r references] [-i interpfile] [-d distribution] X * postnews -c control_command [-n newsgroups] [-d distribution] X * postnews -p X * X * SETUID to NEWSROOT "news" X * X * Michael Rourke (UNSW) April 1984 X */ X X#include "defs.h" X Xchar sys[] = SYS; Xextern char mydomain[]; Xextern char newsversion[]; X#ifndef NETID Xchar systemid[DIRSIZ]; X#else Xchar systemid[] = NETID; X#endif X#ifdef UUNAME Xchar uuname[] = UUNAME; X#endif X Xchar *sflag; /* subject */ Xchar *nflag; /* newsgroups */ Xchar *dflag; /* distribution */ Xlong eflag; /* expire date */ Xchar *cflag; /* control function */ Xchar *rflag; /* references */ Xchar *iflag; /* interp file */ Xbool hflag; /* headers in input */ Xbool pflag; /* from other host (su only) */ Xbool tty; /* isatty */ X X#if AUSAM Xstruct pwent pe; /* current user passwd struct */ Xchar sbuf[SSIZ]; /* strings thereof */ X#else Xstruct passwd *pp; /* current user passwd struct */ X#endif Xlong now; /* current time */ Xint oumask; /* old umask */ Xheader h; /* current articles header */ Xbool su; /* if news super-user */ Xchar *tname; /* name of temp file */ XFILE *tmp; /* file ptr */ Xchar *newsdir; /* %news */ Xuid_t newsuid; /* news uid */ X Xint checkng(), linkng(); Xchar *dfltgrp(); Xextern FILE *mailreply(), *mailnewsroot(); Xextern bool chkhist(), cancel(); X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X register int i; X extern long maketime(); X X time(&now); X for (argv++, argc--; argc > 0; argc--, argv++) { X if (argv[0][0] != '-' && argv[0][2] != '\0') X break; X switch (argv[0][1]) { X case 'h': X hflag = true; X continue; X case 'p': X pflag = true; X continue; X case 's': X sflag = argv[1]; X break; X case 'n': X nflag = argv[1]; X break; X case 'r': X rflag = argv[1]; X break; X case 'i': X iflag = argv[1]; X break; X case 'd': X dflag = argv[1]; X break; X case 'e': X for (i = 1; i < argc; i++) X if (argv[i][0] == '-' && argv[i][2] == '\0') X break; X if (i == 1) { X argc = -1; X break; X } X i--; X if ((eflag = maketime(i, &argv[1], TIMES)) == 0L) X exit(1); X argc -= i, argv += i; X continue; X case 'c': X cflag = argv[1]; X break; X default: X argc = -1; X break; X } X argv++, argc--; X } X if (argc != 0 || (cflag && pflag) || (pflag && (nflag || dflag)) || X ((cflag || pflag) && (hflag || sflag || eflag || rflag || iflag))) { X fprintf(stderr, "Usage: postnews [-h] [-s subject] [-n newsgroups] [-e expiredate ..]\n"); X fprintf(stderr, " [-r references] [-i interpfile] [-d distribution]\n"); X fprintf(stderr, " postnews -c control_command [-n newsgroups] [-d distribution]\n"); X fprintf(stderr, " postnews -p\n"); X exit(1); X } X oumask = umask(022); X X#if AUSAM X pe.pw_strings[LNAME] = NEWSROOT; X if (getpwuid(&pe, sbuf, sizeof(sbuf)) == PWERROR) X error("Password file error."); X newsdir = newstr(pe.pw_strings[DIRPATH]); X newsuid = pe.pw_limits.l_uid; X#else X if ((pp = getpwnam(NEWSROOT)) == (struct passwd *) NULL) X error("Password file error."); X newsdir = newstr(pp->pw_dir); X newsuid = pp->pw_uid; X#endif X X#if AUSAM X pe.pw_limits.l_uid = getuid(); X if (getpwlog(&pe, sbuf, sizeof(sbuf)) == PWERROR) X error("Password file error."); X pwclose(); X su = (bool) (pe.pw_limits.l_uid == 0 || pe.pw_limits.l_uid == newsuid); X#else X if ((pp = getpwuid(getuid())) == (struct passwd *) NULL) X error("Password file error."); X endpwent(); X su = (bool) ((pp->pw_uid == 0) || (pp->pw_uid = newsuid)); X#endif X X#ifndef NETID X getaddr(G_SYSNAME, systemid); X#endif X tty = (bool) isatty(fileno(stdin)); X X#if AUSAM X if (!su && (pe.pw_limits.l_flags & USENET) == 0) X error("Net permission is required to post news."); X#endif X X if (!su && pflag) X error("Permission denied."); X X if (pflag || hflag) X gethead(stdin, &h); X if (sflag) X h.h_subject = sflag; X if (cflag) { X h.h_control = cflag; X h.h_subject = "Control"; X if (!h.h_replyto) X h.h_replyto = newstr5( X#if AUSAM X pe.pw_strings[LNAME], X#else X pp->pw_name, X#endif X "@", systemid, ".", mydomain); X } X if (nflag) X h.h_newsgroups = nflag; X if (rflag) X h.h_references = rflag; X if (eflag) X if (eflag < now) X error("Time specified has passed."); X else X h.h_expires = newstr(ttoa(eflag)); X if (dflag) X h.h_distribution = dflag; X if (tty) X askheads(); X if (h.h_newsgroups) X convgrps(h.h_newsgroups); X else X h.h_newsgroups = DFLTGRP; X if (h.h_distribution) { X convgrps(h.h_distribution); X if (CMP(h.h_newsgroups, h.h_distribution) == 0) X h.h_distribution = NIL(char); X } X if (!h.h_subject) X error("No subject specified."); X if (pflag) { X if (chkhist(h.h_messageid)) X error("Duplicate article %s rejected.", h.h_messageid); X } else X { X if (h.h_relayversion || h.h_postversion || h.h_from || h.h_date || X h.h_messageid || h.h_path || h.h_sender || h.h_datereceived || X h.h_lines) X error("Illegally specified field(s)."); X X if (!su && ngmatch(h.h_newsgroups, MODGROUPS)) X error("Moderated newsgroup:\n\tArticle must be mailed to the newsgroup moderator."); X X if (!cflag && applyng(h.h_newsgroups, checkng)) X exit(1); X if (!cflag && h.h_distribution && applyng(h.h_distribution, X checkng)) X exit(1); X } X install(&h, stdin); X exit(0); X} X X X/* X * create all the directories required for a given group X */ Xcreatgroup(grp) Xchar *grp; X{ X register char *dname; X register char *s, *slash; X X if (strpbrk(grp, BADGRPCHARS)) X error("%s: Illegal char in newsgroup.", grp); X initgrp(grp); /* make entry in active file */ X dname = convg(newstr3(newsdir, "/", grp)); X s = dname + strlen(newsdir); X while (*s) { X slash = strchr(s, '/'); X if (slash) X *slash = '\0'; X if (access(dname, 0) != 0) X mkdir(dname); X if (slash) X *slash = '/', s = slash + 1; X else X break; X } X free(dname); X} X X X/* X * create dir X */ Xmkdir(s) Xchar *s; X{ X int pid, status, r; X int (*istat)(), (*qstat)(); X X switch (pid = fork()) { X default: X /* parent */ X break; X case 0: X /* child */ X execl(MKDIR, "mkdir", s, 0); X error("Can't exec %s", MKDIR); X exit(1); X case -1: X error("Can't fork."); X } X X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X X while ((r = wait(&status)) != pid && r != -1) X ; X if (r == -1 || status) X error("Couldn't mkdir %s", s); X X if (getuid() == 0) { X switch (pid = fork()) { X default: X /* parent */ X break; X case 0: X /* child */ X setgid(0); X setuid(0); X execl(CHOWN, "chown", NEWSROOT, s, 0); X error("Can't exec %s", CHOWN); X exit(1); X case -1: X error("Can't fork."); X } X while ((r = wait(&status)) != pid && r != -1) X ; X if (r == -1 || status) X error("Couldn't chown %s", s); X } X X chmod(s, 0755); X X signal(SIGINT, istat); X signal(SIGQUIT, qstat); X} X X X/* X * get unset headers from stdin X */ Xaskheads() X{ X static char form[] = "%s: %s\n"; X X char *geth(); X X extern char t_subject[], t_newsgroups[], t_distribution[]; X extern char t_references[], t_expires[]; X X if (h.h_subject) X printf(form, t_subject, h.h_subject); X else X h.h_subject = geth(t_subject, NIL(char)); X if (h.h_newsgroups) X printf(form, t_newsgroups, h.h_newsgroups); X else X h.h_newsgroups = geth(t_newsgroups, dfltgrp()); X if (h.h_distribution) X printf(form, t_distribution, h.h_distribution); X else X h.h_distribution = geth(t_distribution, h.h_newsgroups); X if (h.h_expires) X printf(form, t_expires, h.h_expires); X if (h.h_references) X printf(form, t_references, h.h_references); X} X X X/* X * get a header from stdin X */ Xchar * Xgeth(fname, def) Xchar *fname, *def; X{ X register char *s; X X while (1) { X if (def) X printf("%s (%s): ", fname, def); X else X printf("%s: ", fname); X if ((s = mgets()) && *s) X return newstr(s); X if (def) X return newstr(def); X printf("%s field is mandatory.\n", fname); X } X} X X X/* X * install the news item X */ Xinstall(hp, f) Xregister header *hp; XFILE *f; X{ X register FILE *sf; X register int c; X register char *mach, *subs, *type, *com, *end; X char buf[BUFLEN]; X X int cleanup(); X FILE * getbody(), *trimit(); X X signal(SIGINT, cleanup); X signal(SIGQUIT, cleanup); X X if (tty && !cflag) X f = getbody(f); X X if (!hp->h_lines && !cflag) X f = trimit(hp, f); X X if ((tname = mtempnam(newsdir, "itmp")) == NIL(char) || (tmp = fopen(tname, X "w+")) == NIL(FILE)) X error("Can't create itmp file."); X X chown(tname, (int) newsuid, (int) newsuid); /* in case we are currently root */ X X if (pflag) X puthead(hp, tmp, passing); X else X { X h.h_messageid = newstr5("<", getunique(), "@", systemid, "."); X h.h_messageid = catstr2(h.h_messageid, mydomain, ">"); X puthead(hp, tmp, making); X } X X putc('\n', tmp); X if (!cflag) X while ((c = getc(f)) != EOF) X putc(c, tmp); X X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X X sf = fopenf(sys, "r"); X while (fgets(buf, sizeof(buf), sf)) { X mach = buf; X if ((subs = strchr(buf, ':')) == NIL(char) || (type = strchr(subs + X 1, ':')) == NIL(char) || (com = strchr(type + 1, ':')) == NIL(char) X || (end = strchr(com + 1, '\n')) == NIL(char)) X error("%s: Bad format.", sys); X *subs++ = *type++ = *com++ = *end = '\0'; X if ((hp->h_distribution && ngmatch(hp->h_distribution, subs) X || !hp->h_distribution) && ngmatch(hp->h_newsgroups, subs)) X if (CMP(mach, systemid) == 0) X local(hp, tname, subs); X else if ((pflag && checkpath(hp->h_path, mach) || !pflag) X && remote(tmp, com)) X printf("Couldn't transmit to %s\n", mach); X } X fclose(sf); X fclose(tmp); X unlink(tname); X} X X X/* X * have to count the number of lines X * and trim leading and trailing empty lines X */ XFILE * Xtrimit(hp, f) Xheader *hp; XFILE *f; X{ X register int ccount, lcount, llcount; X register int c, lastc; X X ccount = lcount = llcount = 0; X if ((tmp = tmpfile()) == NIL(FILE)) X error("Can't create tempfile."); X while ((c = getc(f)) != EOF && c == '\n') X ; X lastc = c; X if (c != EOF) X do X { X if (c == '\n' && lastc == '\n') { X llcount++; X continue; X } X while (llcount > 0) { X putc('\n', tmp); X lcount++; X llcount--; X } X if (isprint(c)) X putc(c, tmp), ccount++; X else if (isspace(c) || c == '\b') { X if (c == '\n') X lcount++; X putc(c, tmp); X } else X putc('?', tmp); X lastc = c; X } while ((c = getc(f)) != EOF); X if (ccount < 5 && !pflag) X error("Article too short."); X hp->h_lines = newstr(itoa(lcount)); X rewind(tmp); X fclose(f); X return tmp; X} X X X/* X * input new article interactivly X * and place on standard input X */ XFILE * Xgetbody(f) XFILE *f; X{ X register int c; X char buf[BUFSIZ]; X bool quit; X X printf("\n"); X quit = false; X if ((tname = mtempnam(newsdir, "itmp")) == NIL(char) || (tmp = fopen(tname, X "w+")) == NIL(FILE)) X error("Can't create itmp file."); X /* X * read article text, interpreting escape commands X */ X while (!quit && (c = getc(f)) != EOF) X if (c == '.' || c == '!') { X switch (c = (c == '!' ? c : getc(f))) { X case 'e': X fclose(tmp); X readln(f); X doed(tname); X if ((tmp = fopen(tname, "a+")) == NIL(FILE)) X error("Can't re-open %s", tname); X break; X case 'i': X if (!iflag) { X readln(f); X printf("Can't interpolate.\n"); X break; X } X icopy(iflag, tmp); X break; X case '!': X msystem(mgets()); X break; X case '\n': X quit = true; X continue; X default: X printf("Unknown escape command: \"%c\"\n", c); X printf("Commands are:\n"); X printf("\t.e - edit article so far\n"); X printf("\t.i - interpolate article\n"); X printf("\t.!command - shell escape\n"); X readln(f); X } X printf("(continue)\n"); X fflush(stdout); X } X else X { X if (c == '\\' && (c = getc(f)) != '.') X fprintf(tmp, "\\"); X fprintf(tmp, "%c", c); X if (c != '\n' && fgets(buf, sizeof buf, f) != NIL(char)) X fputs(buf, tmp); X } X rewind(tmp); X fclose(f); X unlink(tname); X free(tname); X tname = NIL(char); X return tmp; X} X X X/* X * interpolate fname X */ Xicopy(fname, to) Xchar *fname; XFILE *to; X{ X register FILE *f; X register int c, lastc; X X if ((f = fopen(fname, "r")) == NIL(FILE)) { X perror(fname); X return; X } X lastc = '\n'; X while ((c = getc(f)) != EOF) { X if (lastc == '\n') X fprintf(to, " "); X putc(lastc = c, to); X } X fclose(f); X} X X X/* X * invoke an editor on fname X */ Xdoed(fname) Xchar *fname; X{ X register int i; X register char *editor; X register char *edname; X int pid, stat; X extern char *getenv(); X static char ed[] = ED; X X chmod(fname, 0660); X if ((pid = fork()) == 0) { X setuid(getuid()); X if ((editor = getenv("EDITOR")) == NIL(char)) X editor = ed; X edname = strrchr(editor, '/'); X edname = (edname ? edname + 1 : editor); X execl(editor, edname, fname, 0); X perror(editor); X exit(1); X } X if (pid == -1) { X error("Can't fork ed."); X return; X } X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X X while ((i = wait(&stat)) != pid && i != -1) X ; X X chmod(fname, 0644); X X signal(SIGINT, cleanup); X signal(SIGQUIT, cleanup); X} X X X/* X * remove temp file, and perhaps save in $HOME/dead.article X */ Xcleanup(sig) Xint sig; X{ X if (sig) X printf("\nInterrupt\n"); X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X if (tname && *tname) { X unlink(tname); X tname = NIL(char); X if (tty && tmp) X saveit(); X } X exit(1); X} X X X/* X * save the current temporary file X * in $HOME/dead.article X */ Xsaveit() X{ X register FILE *f; X register char *nname; X register int c; X X extern char *getenv(); X X rewind(tmp); X if ((nname = getenv("HOME")) == NIL(char)) X return; X nname = newstr2(nname, "/dead.article"); X X umask(oumask); X setgid(getgid()); X setuid(getuid()); X X f = fopenf(nname, "w"); X h.h_messageid = NIL(char); X puthead(&h, f, printing); X putc('\n', f); X while ((c = getc(tmp)) != EOF) X putc(c, f); X fclose(f); X printf("Article saved in %s\n", nname); X free(nname); X} X X X/* X * check that mach is not in path X * so we won't send it back again X */ Xcheckpath(path, mach) Xchar *path; Xchar *mach; X{ X register char *ex; X register int r; X X while (*path && (ex = strchr(path, PSEPCHAR))) { X *ex = '\0'; X r = (CMP(path, mach) == 0); X *ex = PSEPCHAR; X if (r) X return 0; X path = ex + 1; X } X return 1; X} X X X/* VARARGS1 */ Xerror(s, a0, a1, a2, a3) Xchar *s; X{ X fprintf(stderr, "postnews: "); X fprintf(stderr, s, a0, a1, a2, a3); X fprintf(stderr, "\n"); X cleanup(0); X} X X X/* X * check that a newsgroup exists X */ Xcheckng(g) Xchar *g; X{ X register char *dname; X register char *s, *com; X X dname = convg(newstr3(newsdir, "/", g)); X free(dname); X if (access(dname, 0) != 0) { X if (su && tty) { X printf("%s: Nonexistent newgroup. Create? ", g); X if ((s = mgets()) == NIL(char) || !*s || (CMP(s, "y") != X 0 && CMP(s, "yes") != 0)) X return 1; X com = newstr6("exec ", POSTNEWS, " -c 'newgroup ", g, X "' -n ", g); X system(com); X free(com); X return 0; X } X printf("%s: Nonexistent newsgroup.\n", g); X return 1; X } X return 0; X} X X X/* X * place news locally X * by linking tmp file into each newsgroup directory X */ Xlocal(hp, tmpname, subs) Xheader *hp; Xchar *tmpname; Xchar *subs; X{ X static char junk[] = "junk"; X X register char *newg; X register FILE *pp; X X if (hp->h_control) X control(hp->h_control); X else if (newg = ngsquash(hp->h_newsgroups, subs)) { X openhist(hp); X if (!applyng(newg, linkng, tmpname)) { X while (!linkng(junk, tmpname)) X creatgroup(junk); X pp = mailnewsroot("Postnews: Article placed in \"junk\""); X fprintf(pp, "Article placed in \"junk\" because no groups active from:\n\t%s\n", X newg); X mailclose(pp); X } X closehist(); X free(newg); X } else X error("Snark: in local"); X} X X X/* X * link news items into local dirs X */ Xlinkng(g, t) Xchar *g, *t; X{ X register char *lname, *dname; X extern char *getseq(); X X dname = convg(newstr3(newsdir, "/", g)); X free(dname); X if (access(dname, 0) != 0) X return 0; /* don't save - not an active group */ X dname = convg(newstr3(g, "/#", getseq(g))); X writehist(dname); X lname = newstr3(newsdir, "/", dname); X if (link(t, lname) != 0) X error("Link from %s to %s failed.", t, lname); X free(lname); X free(dname); X return 1; X} X X X/* X * execute control messages X */ Xcontrol(com) Xchar *com; X{ X register char *s; X register FILE *pp; X X if (s = strchr(com, ' ')) { X *s++ = '\0'; X while (*s && isspace(*s)) X s++; X } X if (CMP(com, "cancel") == 0) { X if (!s || !*s) X error("cancel: message-id not specified."); X cancel(s); X } else if (CMP(com, "newgroup") == 0 || CMP(com, "rmgroup") == 0) { X if (!s || !*s) X error("%s: group name not specified.", com); X if (!su) X error("%s: permission denied.", com); X else if (CMP(com, "newgroup") == 0) X creatgroup(s); X else X { X pp = mailnewsroot("Postnews: rmgroup request"); X fprintf(pp, "rmgroup %s, sent from %s\n", s, (h.h_replyto ? X h.h_replyto : (h.h_from ? h.h_from : "Unknown?"))); X fprintf(pp, "run '/usr/lib/news/rmgroup %s' if sender is authorised.\n", X s); X mailclose(pp); X } X } else if (CMP(com, "sendsys") == 0) X sendsys(); X else if (CMP(com, "version") == 0) X version(); X else if (CMP(com, "senduuname") == 0) X senduuname(); X else X { X sorry(com); X error("Unknown control command: %s %s\n(Valid: cancel, newgroup, sendsys, senduuname, version)\nSent from: %s", X com, s ? s : "", (h.h_replyto ? h.h_replyto : (h.h_from ? X h.h_from : "Unknown?"))); X } X} X X X/* X * send sys file to originator X */ Xsendsys() X{ X register FILE *pp, *fp; X register int c; X X fp = fopenf(sys, "r"); X pp = mailreply("News sendsys request"); X while ((c = getc(fp)) != EOF) X putc(c, pp); X mailclose(pp); X fclose(fp); X} X X X/* X * send version name and number to originator X */ Xversion() X{ X register FILE *pp; X X pp = mailreply("News version request"); X fprintf(pp, "Current version: %s\n", newsversion); X mailclose(pp); X fclose(pp); X} X X X/* X * send uuname data to originator X */ Xsenduuname() X{ X register FILE *pp, *f; X register int c; X X extern FILE *tmpfile(); X X#ifndef UUNAME X sorry("uuname"); X#else X if ((pp = popen(uuname, "r")) == NIL(FILE)) X error("Couldn't run \"%s\"", uuname); X if ((f = tmpfile()) == NIL(FILE)) X error("Can't open tmp file."); X X while ((c = getc(pp)) != EOF) X putc(c, f); X X if (pclose(pp) != 0) X error("\"%s\" had bad exit status.", uuname); X rewind(f); X X pp = mailreply("News senduuname request"); X while ((c = getc(f)) != EOF) X putc(c, pp); X X fclose(f); X mailclose(pp); X#endif X} X X X/* X * send message about unimplemented command X */ Xsorry(com) Xchar *com; X{ X register FILE *pp; X X pp = mailreply("Unimplemented news control message"); X fprintf(pp, "The control message \"%s\" is not implemented at this site.\n", X com); X fprintf(pp, "Our current version of news is: %s\n", newsversion); X mailclose(pp); X} X X X/* X * set up a pipe to a mail program to reply to control requests X */ XFILE * Xmailreply(s) Xchar *s; X{ X register FILE *pp; X register char *com, *ra; X X if ((ra = getretaddr(&h)) == NIL(char)) X error("Can't form return address for control message"); X com = newstr4("exec ", MAIL, " ", ra); X if ((pp = popen(com, "w")) == NIL(FILE)) X error("Couldn't run \"%s\"", com); X fprintf(pp, "Subject: %s\n", s); X fprintf(pp, "Responding-system: %s.%s\n\n", systemid, mydomain); X free(com); X free(ra); X return pp; X} X X X/* X * set up a pipe to mail to NEWSROOT X */ XFILE * Xmailnewsroot(s) Xchar *s; X{ X register FILE *pp; X register char *com; X X com = newstr4("exec ", MAIL, " ", NEWSROOT); X if ((pp = popen(com, "w")) == NIL(FILE)) X error("Couldn't run \"%s\"", com); X fprintf(pp, "Subject: %s\n", s); X free(com); X return pp; X} X X X/* X * close the mail pipe X */ Xmailclose(pp) XFILE *pp; X{ X if (pclose(pp) != 0) X error("Mail failed"); X} X X X/* X * send item to remote hosts X */ Xremote(f, com) XFILE *f; Xchar *com; X{ X register int c; X FILE * out; X X if ((out = popen(com, "w")) == NIL(FILE)) X return 1; X rewind(f); X while ((c = getc(f)) != EOF) X putc(c, out); X return pclose(out); X} X X X/* X * shell escape X */ Xmsystem(s) Xchar *s; X{ X int status, pid, w; X X if ((pid = fork()) == 0) { X close(fileno(tmp)); X /* X * remember we are setuid to NEWS so... X */ X umask(oumask); X setgid(getgid()); X setuid(getuid()); X execl(SHELL, "sh", "-c", s, 0); X _exit(127); X } X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X X while ((w = wait(&status)) != pid && w != -1) X ; X if (w == -1) X status = -1; X X signal(SIGINT, cleanup); X signal(SIGQUIT, cleanup); X return(status); X} X X X/* X * work out the default group for this user X */ Xchar * Xdfltgrp() X{ X#if MANGRPS X register char **sp; X extern char **getclasses(); X X for (sp = getclasses(pe.pw_cmask); *sp; sp++) { X if (CMP(*sp, "C-Staff") == 0 || CMP(*sp, "System") == 0) X return "system"; X if (CMP(*sp, "CLUBS") == 0) X return newstr2("general.club.", pe.pw_strings[LNAME]); X if (CMP(*sp, "Languages") == 0) X return newstr2("general.lang.", pe.pw_strings[LNAME]); X if (CMP(*sp, "Classaccount") == 0) X return newstr2("class.", pe.pw_strings[LNAME]); X } X#endif X return DFLTGRP; X} END_OF_FILE if test 20673 -ne `wc -c <'rna/postnews.c'`; then echo shar: \"'rna/postnews.c'\" unpacked with wrong size! fi # end of 'rna/postnews.c' fi echo shar: End of archive 13 \(of 14\). ## End of shell archive. exit 0