rsalz@uunet.uu.net (Rich Salz) (06/30/89)
Submitted-by: utzoo!henry Posting-number: Volume 19, Issue 94 Archive-name: cnews2/part17 : ---CUT HERE--- echo 'relay/sh/postnews': sed 's/^X//' >'relay/sh/postnews' <<'!' X#! /bin/sh X# postnews - post news article X X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()= X. ${NEWSCONFIG-/usr/lib/news/bin/config} X XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH # but do not export it Xumask 077 # private X Xtmp=/tmp/pn$$ Xterm="rm -f $tmp ; exit 0" Xtrap "$term" 0 1 2 X Xif test " $VISUAL" != " "; then X edit="$VISUAL" Xelif test " $EDITOR" != " "; then X edit="$EDITOR" Xelse X edit=/bin/ed Xfi X Xcase $# in X0) X if test -r $NEWSCTL/postdefltgroup X then X defg="`cat $NEWSCTL/postdefltgroup`" X dprompt=" [$defg]" X else X defg= X dprompt= X fi X ans= X while test " $ans" = " " X do X echo "Newsgroup(s)$dprompt? " | tr -d '\012' X read ans X case "$ans" in X '') if test " $defg" != " " X then X ans="$defg" X fi X ;; X esac X done X echo "Newsgroups: $ans" >>$tmp X ;; X X1) X echo "Newsgroups: $1" >>$tmp X ;; X X*) X echo 'Usage: postnews [newsgroups]' >&2 X exit 2 X ;; Xesac X Xsubj= Xwhile test " $subj" = " " Xdo X echo 'Subject: ' | tr -d '\012' X read subj Xdone Xecho "Subject: $subj" >>$tmp Xif test -r $NEWSCTL/postdefltdist Xthen X echo "Distribution: `cat $NEWSCTL/postdefltdist`" >>$tmp Xfi X Xecho >>$tmp Xecho DELETE THIS LINE "(but DO NOT delete the blank line after the headers above)" >>$tmp Xif test -r $NEWSCTL/postdefltdist Xthen X echo 'DELETE THIS LINE (You may want to change the "Distribution" header)' >>$tmp Xfi Xecho REPLACE THIS LINE WITH YOUR TEXT >>$tmp X Xtrap : 2 X$edit $tmp Xtrap "$term" 2 X Xwhile egrep '^(DELETE|REPLACE) THIS LINE' $tmp >/dev/null Xdo X echo 'This posting does not appear to have been edited properly.' X echo 'Abandon it [y] ? ' | tr -d '\012' X read ans X case "$ans" in X ''|y*|Y*) X rm -f $tmp X exit 0 X ;; X esac X X echo 'Editing again... Please check it over carefully.' X trap : 2 X $edit $tmp X trap "$term" 2 Xdone X Xecho 'Posting...' Xinews -h <$tmp ! echo 'relay/sh/tear': sed 's/^X//' >'relay/sh/tear' <<'!' X#! /bin/sh X# tear prefix [file...] - tear RFC822 header and body apart X# output files are $1hdr and $1body XPATH=/bin:/usr/bin; export PATH X Xcase $# in X0) X echo "usage: tear prefix [file...]" >&2 X exit 1 X ;; Xesac X Xhdr="$1hdr" Xbody="$1body" Xshift X X>>$hdr # create files just in case X>>$body Xcase $# in X0) args="-" ;; # awk needs a filename due to cmd. line assignments X*) args="$@" ;; Xesac Xexec awk 'inbody == 0 && $0 ~ /^([ \t]|[^ \t]*:)/ { print >hdr; next } X { inbody = 1; print >body } X' hdr="$hdr" body="$body" $args ! echo 'relay/sys.c': sed 's/^X//' >'relay/sys.c' <<'!' X/* X * news sys file reading functions X */ X X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <sys/stat.h> X#include "libc.h" X#include "fgetmfs.h" X#include "news.h" X#include "config.h" X#include "system.h" X X#define BTCHDIR "out.going/" /* prefix of relative batch file name */ X#define BTCHPFX BTCHDIR /* prefix of default batch file name */ X#define BTCHSFX "/togo" /* suffix of same */ X#define CMDPFX "uux - -r -z " /* prefix of default command */ X#define CMDSFX "!rnews" /* suffix of same */ X X/* private */ Xstatic FILE *fp = NULL; /* stream for ctlfile(filerelname) */ Xstatic char filerelname[] = "sys"; /* filename relative to $NEWSCTL */ X X/* forward decls */ XFORWARD char *parsecolon(), *reparse(); XFORWARD void readsys(), parsesysln(), parse(), parseflags(); X X/* exports */ Xstruct system *firstsys = NULL; /* cache: 1st sys of in-core sys file */ Xstruct system *currsys = NULL; /* current system */ X X/* imports */ Xextern boolean justone; Xextern struct system *mysysincache(); Xextern void rewsys(), remmysys(), freecurrsys(); X Xstruct system * Xoursys() /* return our sys entry */ X{ X register struct system *sys = mysysincache(); X static struct system fakesys; X X if (sys == NULL) { X rewsys(fp); X while ((sys = nextsys()) != NULL && X !STREQ(sys->sy_name, hostname())) X ; X if (sys == NULL) { X /* no entry: cook one up; no need to malloc members */ X fakesys.sy_name = hostname(); X fakesys.sy_excl = NULL; X fakesys.sy_ngs = "all"; X fakesys.sy_distr = "all"; X fakesys.sy_flags = 0; X fakesys.sy_lochops = 0; X fakesys.sy_cmd = ""; X fakesys.sy_next = NULL; X sys = &fakesys; X } X remmysys(sys); /* for future reference */ X } X return sys; X} X X/* X * Return the next sys entry, which may span multiple lines. X * Returned pointer points at a static struct whose members X * point at static storage. X * X * It would be clearer to rewrite the justone/nextsys/readsys dance X * to get rid of justone, but I haven't the energy. Sorry. Beware that X * justone is set in either ../libbig/sys.fast.c or ../libsmall/sys.slow.c. X */ Xstruct system * Xnextsys() X{ X struct system *retsys; X X if (firstsys == NULL && fp == NULL) X if ((fp = fopenwclex(ctlfile(filerelname), "r")) == NULL) X return NULL; X if (fp != NULL && firstsys == NULL) X readsys(); X retsys = currsys; X if (currsys != NULL) X currsys = currsys->sy_next; X return retsys; X} X X/* X * If justone, read one entry; else read whole sys file (done once only). X * Ignores '#' comments and blank lines; uses cfgetms to read possibly- X * continued lines of arbitrary length. X */ XSTATIC void Xreadsys() X{ X register char *sysline; X X if (justone) X freecurrsys(); X else X rewind(fp); X while ((sysline = cfgetms(fp)) != NULL) { X if (sysline[0] != '#' && sysline[0] != '\n') X parsesysln(sysline); X free(sysline); X if (justone && firstsys != NULL) { /* parsed an entry */ X firstsys = NULL; X return; X } X } X (void) nfclose(fp); X fp = NULL; X rewsys(fp); X} X Xstatic char *curr, *next; /* parsing state */ X X/* X * Parse (and modify) sysline into *currsys, which is malloced here X * and freed iff "justone", in readsys(), see freecursys(). X * X * Side-effect: sysline has a trailing newline removed. X */ XSTATIC void Xparsesysln(sysline) Xregister char *sysline; X{ X register struct system *sysp =(struct system *)nemalloc(sizeof *sysp); X char *flagstring; X X trim(sysline); X next = sysline; X parse(&sysp->sy_name); X parse(&sysp->sy_ngs); X parse(&flagstring); X parse(&sysp->sy_cmd); X /* could check for extra fields here */ X X parseflags(flagstring, sysp); X free(flagstring); /* malloced by parse */ X sysp->sy_next = NULL; X X /* reparse for embedded slashes */ X sysp->sy_excl = reparse(sysp->sy_name, '/'); X sysp->sy_distr = reparse(sysp->sy_ngs, '/'); X if (sysp->sy_distr == NULL) /* default distr is ngs... */ X sysp->sy_distr = sysp->sy_ngs; X X sysdeflt(sysp); /* fill in any defaults */ X X /* stash *sysp away on the tail of the current list of systems */ X if (firstsys == NULL) X firstsys = sysp; X else X currsys->sy_next = sysp; X currsys = sysp; X} X X/* X * fill in defaults in sysp. X * X * expand a name of "ME" to hostname(). X * If an empty batch file name was given, supply a default X * ($NEWSARTS/BTCHPFX system BTCHSFX). X * Prepend $NEWSARTS/BTCHDIR to relative file names. X * If an empty command was given, supply a default (uux - -r -z system!rnews). X * (This *is* yucky and uucp-version-dependent.) X */ Xvoid Xsysdeflt(sysp) Xregister struct system *sysp; X{ X if (STREQ(sysp->sy_name, "ME")) { X free(sysp->sy_name); /* malloced by parse */ X sysp->sy_name = strsave(hostname()); X } X if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] == '\0') { X register char *deffile = nemalloc((unsigned) STRLEN(BTCHPFX) + X strlen(sysp->sy_name) + STRLEN(BTCHSFX) + 1); X X (void) strcpy(deffile, BTCHPFX); X (void) strcat(deffile, sysp->sy_name); X (void) strcat(deffile, BTCHSFX); X free(sysp->sy_cmd); /* malloced by parse */ X sysp->sy_cmd = strsave(fullartfile(deffile)); X free(deffile); X } X if (sysp->sy_flags&FLG_BATCH && sysp->sy_cmd[0] != FNDELIM) { X register char *absfile = nemalloc((unsigned) STRLEN(BTCHDIR) + X strlen(sysp->sy_cmd) + 1); X X (void) strcpy(absfile, BTCHDIR); X (void) strcat(absfile, sysp->sy_cmd); X free(sysp->sy_cmd); /* malloced by parse */ X sysp->sy_cmd = strsave(artfile(absfile)); X free(absfile); X } X if (!(sysp->sy_flags&FLG_BATCH) && sysp->sy_cmd[0] == '\0') { X free(sysp->sy_cmd); /* malloced by parse */ X sysp->sy_cmd = nemalloc((unsigned) STRLEN(CMDPFX) + X strlen(sysp->sy_name) + STRLEN(CMDSFX) + 1); X (void) strcpy(sysp->sy_cmd, CMDPFX); X (void) strcat(sysp->sy_cmd, sysp->sy_name); X (void) strcat(sysp->sy_cmd, CMDSFX); X } X} X X/* X * Parse "next" to colon into malloced storage, return its ptr via "into". X * *into is freed iff "justone", in readsys(), see freecursys(). X */ XSTATIC void Xparse(into) Xregister char **into; X{ X curr = next; X if (curr == NULL) X *into = strsave(""); X else { X next = parsecolon(curr); X *into = strsave(curr); X } X} X XSTATIC char * Xparsecolon(line) /* return NULL or ptr. to byte after colon */ Xchar *line; X{ X register char *colon; X X INDEX(line, ':', colon); X if (colon != NULL) X *colon++ = '\0'; X return colon; X} X X/* X * replace "delim" in "field" with a NUL and return the address of the byte X * after the NUL (the address of the second subfield), or NULL if no X * "delim" was present. X */ XSTATIC char * Xreparse(field, delim) Xchar *field; Xint delim; X{ X register char *delimp = index(field, delim); X X if (delimp != NULL) X *delimp++ = '\0'; X return delimp; X} X X/* X * Parse sys file flags into sysp. X */ XSTATIC void Xparseflags(flags, sysp) Xregister char *flags; Xregister struct system *sysp; X{ X sysp->sy_flags = 0; X sysp->sy_lochops = 0; /* default L value */ X for (; *flags != '\0'; flags++) X switch (*flags) { X case 'A': X errunlock("A news format not supported", ""); X /* NOTREACHED */ X case 'B': /* mostly harmless */ X break; X case 'f': X sysp->sy_flags |= FLG_BATCH|FLG_SZBATCH; X break; X case 'F': X sysp->sy_flags |= FLG_BATCH; X break; X case 'I': /* NNTP hook: write msgids, !files */ X sysp->sy_flags |= FLG_BATCH|FLG_IHAVE; X break; X case 'L': /* Ln */ X sysp->sy_flags |= FLG_LOCAL; X sysp->sy_lochops = 0; X while (isascii(flags[1]) && isdigit(flags[1])) { X sysp->sy_lochops *= 10; X sysp->sy_lochops += *++flags - '0'; X } X break; X case 'm': /* send only moderated groups */ X sysp->sy_flags |= FLG_MOD; X break; X case 'N': X errunlock( X "The N flag is a wasteful old kludge; see the I flag instead.", ""); X /* NOTREACHED */ X case 'n': /* NNTP hook: write files+msgids */ X sysp->sy_flags |= FLG_BATCH|FLG_NBATCH; X break; X case 'u': /* send only unmoderated groups */ X sysp->sy_flags |= FLG_UNMOD; X break; X case 'U': /* mostly harmless */ X break; X case 'H': /* bugger off */ X case 'S': /* bugger off */ X case 'M': /* multicast: obs., see batcher */ X case 'O': /* multicast: obs., see batcher */ X default: X errunlock("unknown sys flag `%s' given", flags); X /* NOTREACHED */ X } X} X Xvoid Xrewndsys() X{ X rewsys(fp); X} ! echo 'relay/system.h': sed 's/^X//' >'relay/system.h' <<'!' X/* X * parsed form of the "sys" file X * Beware that in C++, struct system collides with system(3) in transmit.c X * This can be fixed by using "::system(...)" or by renaming struct system. X */ Xstruct system { X char *sy_name; /* machine name */ X char *sy_excl; /* exclusion list of machines */ X char *sy_ngs; /* newsgroup subscription list */ X char *sy_distr; /* distribution list */ X char *sy_cmd; /* command to transmit articles */ X unsigned sy_lochops; /* flags Ln value: local hops */ X char sy_flags; /* ornaments, encoded as bits */ X struct system *sy_next; /* link to next system */ X}; X X/* sy_flags bits */ X#define FLG_BATCH (1<<0) /* F: sy_cmd is batch filename */ X#define FLG_SZBATCH (1<<1) /* f: F, and include byte count */ X#define FLG_IHAVE (1<<2) /* I: NNTP ihave - F, write msg. ids */ X#define FLG_LOCAL (1<<3) /* L: send local articles only */ X#define FLG_MOD (1<<4) /* m: send moderated groups only */ X#define FLG_UNMOD (1<<5) /* u: send unmoderated groups only */ X#define FLG_NBATCH (1<<6) /* n: NNTP batch: filename & msg-id */ X X/* imports from system.c */ Xextern struct system *oursys(), *nextsys(); Xextern void sysdeflt(), rewndsys(); ! echo 'relay/transmit.c': sed 's/^X//' >'relay/transmit.c' <<'!' X/* X * transmit - transmit incoming articles to neighbouring machines X */ X X#include <stdio.h> X#include <sys/types.h> X#include "libc.h" X#include "news.h" X#include "config.h" X#include "headers.h" X#include "active.h" X#include "article.h" X#include "msgs.h" X#include "system.h" X#include "trbatch.h" X#include "transmit.h" X X/* forwards */ XFORWARD boolean oktransmit(); XFORWARD void ejaculate(), trappend(); X X/* private */ Xstatic boolean debug = NO; X Xvoid Xtransdebug(state) Xboolean state; X{ X debug = state; X} X X/* X * For each system in "sys" other than this one, X * transmit this article when its ng pattern matches X * art->h.h_distr (which may be just a copy of art->h.h_ngs). X */ Xvoid Xtransmit(art, exclude) Xregister struct article *art; Xchar *exclude; /* no copy to this site */ X{ X register struct system *sys; X register int bsysno = 0; /* ordinal # of batch sys entry */ X X rewndsys(); X if (debug) X (void) fprintf(stderr, "just rewound sys file\n"); X while ((sys = nextsys()) != NULL) { X if (debug) X (void) fprintf(stderr, X "sy_name=%s sy_ngs=%s sy_distr=%s\n", X sys->sy_name, sys->sy_ngs, sys->sy_distr); X if (oktransmit(art, sys, exclude)) X ejaculate(art, sys, bsysno); X if (sys->sy_flags&FLG_BATCH) X ++bsysno; X } X if (debug) X (void) fprintf(stderr, "just finished reading sys file\n"); X} X X/* X * Is it okay to send the article corresponding to "art" to site "sys", X * excluding site "exclude"? X * X * If L(n) flag is on, must have been posted within n hops of here. X * Never send to this site, nor the "exclude" site, nor any site with a host X * in sys->sy_excl named in Path:, nor any site named in Path:. X * X * Newsgroups: must match sys's subscription list. X * Distribution: must match sys's distribution list. (RFC 850 is wrong: X * Distribution:s are *not* patterns, they are lists. See RFC 1036.) X * X * If m flag is on, group(s) must be moderated; if u flag is on, X * must be unmoderated. (If both are on, act as if neither is on.) X */ XSTATIC boolean Xoktransmit(art, sys, exclude) Xregister struct article *art; Xregister struct system *sys; Xchar *exclude; /* no copy to him */ X{ X register int flags = sys->sy_flags; X register char *site = sys->sy_name; X register char *path = X canonpath(art->h.h_path, art->h.h_approved, art->h.h_sender); X register int result; X X if (flags&FLG_LOCAL && hopcount(path) > sys->sy_lochops || X STREQ(hostname(), site) || X exclude != NULL && STREQ(exclude, site) || hostin(site, path) || X sys->sy_excl != NULL && anyhostin(sys->sy_excl, path) || X !ngmatch(sys->sy_ngs, art->h.h_ngs) || X !ngmatch(sys->sy_distr, art->h.h_distr)) X result = NO; X else if (flags&(FLG_MOD|FLG_UNMOD)) { /* u, m flag selection */ X if ((flags&(FLG_MOD|FLG_UNMOD)) == (FLG_MOD|FLG_UNMOD)) X result = YES; /* too silly */ X else X result = (flags&FLG_MOD? moderated(art->h.h_ngs): X !moderated(art->h.h_ngs)); X } else X result = YES; X free(path); X return result; X} X X/* X * Send the article denoted by art to the system denoted by sys. X * X * When a filename is needed, we use the first one in art->a_files X * rather than art->a_tmpf because we want a permanent name, and X * translate it to a full path name to avoid ambiguity. X * X * Side-effect: prints the system name on stdout for logging. X */ XSTATIC void Xejaculate(art, sys, bsysno) Xregister struct article *art; Xregister struct system *sys; Xint bsysno; X{ X register char *fullname; /* sometimes is a message-id */ X X if (debug) X (void) fprintf(stderr, "transmitting %s to %s\n", X art->h.h_msgid, sys->sy_name); X (void) printf(" %s", sys->sy_name); /* logging */ X if (sys->sy_flags&FLG_IHAVE) X fullname = art->h.h_msgid; X else { X register char *filename = first(art->a_files); X X mkfilenm(filename); X fullname = fullartfile(filename); X free(filename); X } X#ifdef PARANOID X fullname = strsave(fullname); X#endif X if (sys->sy_flags&FLG_BATCH) X trbatch(art, sys, fullname, bsysno); X else X trcmd(art, sys, fullname); X#ifdef PARANOID X free(fullname); X#endif X} X X/* X * Execute sys->sy_cmd with the current article as stdin X * and filename substituted for %s in sys->sy_cmd (if any). X * X * Search path includes $NEWSCTL/bin and $NEWSBIN/relay. X * redirect stdin to prevent consuming my stdin & so cmd's stdin X * is filename by default. X * X * We use strcat instead of sprintf if syscmd contains no %. X * this avoids the 128-byte restriction on printf output X * (see printf(3) BUGS, at least in V7). X */ Xvoid Xtrcmd(art, sys, filename) Xstruct article *art; Xstruct system *sys; Xchar *filename; X{ X register char *cmd; X int exitstat; X char *syscmd = sys->sy_cmd, *percent; X static char *ctlcmd = NULL, *bincmd = NULL; X X if (ctlcmd == NULL) X ctlcmd = strsave(ctlfile("bin")); X if (bincmd == NULL) X bincmd = strsave(binfile("relay")); X cmd = nemalloc((unsigned)(STRLEN("PATH=") + strlen(ctlcmd) + X STRLEN(":") + strlen(bincmd) + STRLEN(":") + strlen(newspath()) + X STRLEN(" <") + strlen(filename) + STRLEN(" ") + X strlen(syscmd) + strlen(filename) + 1)); X (void) strcpy(cmd, "PATH="); X (void) strcat(cmd, ctlcmd); X (void) strcat(cmd, ":"); X (void) strcat(cmd, bincmd); X (void) strcat(cmd, ":"); X (void) strcat(cmd, newspath()); X (void) strcat(cmd, " <"); X (void) strcat(cmd, filename); X (void) strcat(cmd, " "); X percent = index(syscmd, '%'); X if (percent == NULL) X (void) strcat(cmd, syscmd); X else { X char *pcent2; X X ++percent; X pcent2 = index(percent, '%'); X if (pcent2 != NULL) { X art->a_status |= ST_DROPPED; X (void) fprintf(stderr, "%s: `%s' contains two %%'s\n", X progname, cmd); X } else if (*percent != 's' && *percent != '%') { X art->a_status |= ST_DROPPED; X (void) fprintf(stderr, "%s: `%s' contains %%%c, not %%s\n", X progname, cmd, *percent); X } else X (void) sprintf(cmd+strlen(cmd), syscmd, filename); X } X exitstat = system(cmd); X if (exitstat != 0) { X art->a_status |= ST_DROPPED; X (void) fprintf(stderr, "%s: `%s' returned exit status 0%o\n", X progname, cmd, exitstat); X } X free(cmd); X} X X/* X * Append "filename" to sys->sy_cmd. bsysno is the ordinal # of this batch X * sys line. If bsysno is low enough, use the batchfile cache of batch file X * descriptors. X */ Xvoid Xtrbatch(art, sys, filename, bsysno) Xregister struct article *art; Xstruct system *sys; Xchar *filename; Xregister int bsysno; X{ X register struct batchfile *bf = bfopen(sys->sy_cmd, bsysno); X X if (bf == NULL || bf->bf_str == NULL) X art->a_status |= ST_DROPPED; X else { X trappend(art, sys, bf, filename); X art->a_status |= bffkclose(bsysno); X } X} X X/* X * write filename, message-id or size on batch file "bf". X * under the 'f' flag (FLG_SZBATCH), include the size in bytes of the article X * after "name" to assist the C news batcher. under the 'n' flag (FLG_NBATCH), X * write the article's message-id. afterward, flush "bf" in case X * the machine crashes before the stream is closed. X */ XSTATIC void Xtrappend(art, sys, bf, name) Xregister struct article *art; Xregister struct system *sys; Xregister struct batchfile *bf; Xchar *name; X{ X if (fputs(name, bf->bf_str) == EOF) X fulldisk(art, bf->bf_name); X if (sys->sy_flags&FLG_SZBATCH && X fprintf(bf->bf_str, " %ld", art->a_charswritten) == EOF) X fulldisk(art, bf->bf_name); X if (sys->sy_flags&FLG_NBATCH && X fprintf(bf->bf_str, " %s", art->h.h_msgid) == EOF) X fulldisk(art, bf->bf_name); X X /* don't check putc return value for portability; use ferror */ X (void) putc('\n', bf->bf_str); X if (ferror(bf->bf_str) || bfflush(bf) == EOF) X fulldisk(art, bf->bf_name); X} X X/* X * really close all the open batch files X */ Xstatust Xtrclose() X{ X return bfrealclose(); X} ! echo 'relay/transmit.h': sed 's/^X//' >'relay/transmit.h' <<'!' X/* imports from transmit.c */ Xextern statust trclose(); Xextern void transdebug(), transmit(), trcmd(), trbatch(); ! echo 'relay/trbatch.c': sed 's/^X//' >'relay/trbatch.c' <<'!' X/* X * transmit batch file management X */ X#include <stdio.h> X#include <sys/types.h> X#include "libc.h" X#include "news.h" X#include "msgs.h" X#include "trbatch.h" X X/* tunable parameters */ X#ifndef FLUSHEVERY X#define FLUSHEVERY 1 /* fflush batch files every this many lines */ X#endif /* FLUSHEVERY */ X#ifndef NOPENBFS X#define NOPENBFS 10 /* # batchfiles kept open for batching (arbitrary) */ X#endif /* NOPENBFS */ X Xstatic struct batchfile batchfile[NOPENBFS]; /* try to keep open always */ X#define lastbf &batchfile[NOPENBFS-1] X/* X * More than one pointer in ordtobfs may point at a given batchfile, X * to permit sharing of open batch files among multiple sys entries. X * ordtobfs[ordinal # of batch sys entry] -> (usually open) batch file, X * if the index is in range. X */ Xstatic struct batchfile *ordtobfs[NOPENBFS]; Xstatic struct batchfile fakebatf; /* for non-cached batch files */ X X/* forwards */ XFORWARD statust bfclose(), bfrclose(); XFORWARD struct batchfile *bfincache(), *fakebf(); X X/* X * open "name" for appending, for batch sys entry with ordinal # "ord". X * X * if ord is too big, see if any batchfile has been assigned to "name" yet; X * if not, set up a fake batchfile for temporary use. if ord is in range, X * ensure that (name, ord) are mapped to a batchfile. X * X * if an attempt to open the batchfile's stream fails, close a random X * batchfile stream and retry the open. X */ Xstruct batchfile * Xbfopen(name, ord) Xregister char *name; Xregister int ord; X{ X register struct batchfile *bf; X X if (ord >= NOPENBFS) { /* no mapping possible */ X bf = bfisopen(name); X if (bf == NULL) X bf = fakebf((FILE *)NULL, name); X } else X bf = bfincache(name, ord); X X if (bf->bf_str == NULL) X bf->bf_str = fopenclex(name, "a"); X if (bf->bf_str == NULL) { X if (bfrclose() != ST_OKAY) X return NULL; X bf->bf_str = fopenwclex(name, "a"); /* retry, may bitch */ X } X return bf; X} X X/* X * returns a batchfile, never NULL, corresponding to name and ord. X * if ord isn't mapped, search the batchfile cache for name; X * if missing, initialise batchfile[ord] and map ord to it. X * if ord wasn't mapped, but name was in the cache, map ord to the cache hit. X */ XSTATIC struct batchfile * Xbfincache(name, ord) Xchar *name; Xint ord; X{ X register struct batchfile *bf = ordtobfs[ord]; X X if (bf == NULL) { X bf = bfisopen(name); X if (bf == NULL) { X /* establish new mapping for a new file */ X bf = &batchfile[ord]; X bf->bf_name = strsave(name); X bf->bf_str = NULL; /* paranoia */ X#ifdef notdef X bf->bf_ref = 0; X#endif X bf->bf_lines = FLUSHEVERY; X } X ordtobfs[ord] = bf; X } X /* mapping is now set (ord -> bf) */ X return bf; X} X Xstatust Xbffkclose(ord) /* close ord's batchfile, if fake */ Xint ord; X{ X register statust status = ST_OKAY; X X if (ord >= NOPENBFS) X status |= bfclose(&fakebatf); X return status; X} X XSTATIC statust Xbfclose(bf) Xregister struct batchfile *bf; X{ X register statust status = ST_OKAY; X X if (nfclose(bf->bf_str) == EOF) X status = prfulldisk(bf->bf_name); X bf->bf_str = NULL; /* prevent accidents; mark as closed */ X return status; X} X XSTATIC struct batchfile * Xfakebf(stream, name) XFILE *stream; Xchar *name; X{ X fakebatf.bf_name = name; X fakebatf.bf_str = stream; X return &fakebatf; X} X X/* X * search the batchfile cache for "name"; return the hit, if any. X */ Xstruct batchfile * Xbfisopen(name) Xregister char *name; X{ X register struct batchfile *bf; X X for (bf = batchfile; bf <= lastbf; bf++) X if (bf->bf_name != NULL && STREQ(name, bf->bf_name)) X return bf; X return NULL; X} X X/* X * a performance hack: only fflush bf->bf_str every FLUSHEVERY calls. X */ Xint Xbfflush(bf) Xregister struct batchfile *bf; X{ X register int ret = 0; X X if (--bf->bf_lines <= 0) { X bf->bf_lines = FLUSHEVERY; X ret = fflush(bf->bf_str); X } X return ret; X} X XSTATIC statust Xbfrclose() /* close a random batchfile */ X{ X register struct batchfile *bf; X register statust status = ST_OKAY; X X for (bf = batchfile; bf <= lastbf; bf++) X if (bf->bf_str != NULL) { X status |= bfclose(bf); X break; X } X return status; X} X Xstatust Xbfrealclose() /* close all open batch files */ X{ X register struct batchfile *bf; X register statust status = ST_OKAY; X X for (bf = batchfile; bf <= lastbf; bf++) { X if (bf->bf_str != NULL) /* batch file stream open */ X status |= bfclose(bf); X nnfree(&bf->bf_name); X#ifdef notdef X bf->bf_ref = 0; X#endif X ordtobfs[bf - batchfile] = NULL; /* unmap batch file */ X } X return status; X} ! echo 'relay/trbatch.h': sed 's/^X//' >'relay/trbatch.h' <<'!' X/* X * interface to the transmit batch files X */ X Xstruct batchfile { X#ifdef notdef X int bf_ref; /* reference count */ X#endif X FILE *bf_str; /* stream */ X char *bf_name; /* file name */ X int bf_lines; /* until fflush */ X}; X X/* imports from trbatch.c */ Xextern struct batchfile *bfopen(), *bfisopen(); Xextern statust bffkclose(), bfrealclose(); Xextern int bfflush(); ! echo 'relay/ihave.not.c': sed 's/^X//' >'relay/ihave.not.c' <<'!' X/* X * Reject the Usenet ihave/sendme control messages. X */ X X#include <stdio.h> X#include <sys/types.h> X X#include "news.h" X#include "headers.h" X#include "article.h" X Xstatic void Xignore(cmd, args) Xchar *cmd, *args; X{ X (void) fprintf(stderr, "%s: `%s %s' control ignored\n", progname, cmd, args); X} X X/* ARGSUSED art */ Xvoid Xihave(args, art) Xchar *args; Xstruct article *art; X{ X ignore("ihave", args); X} X X/* ARGSUSED art */ Xvoid Xsendme(args, art) Xchar *args; Xstruct article *art; X{ X ignore("sendme", args); X} ! echo 'relay/README.relay': sed 's/^X//' >'relay/README.relay' <<'!' X``yer about to be boarded, ye scurvy network news dogs! har har ...'' X -- Oliver Wendell Jones, Bloom County Hacker & Cracker X X``No news is good news.'' X``When bigger machines are built, netnews will saturate them.'' X``USENET -- All the news that's fit to `N'.'' X -- /usr/games/fortune X X``Net news is the television of computing.'' X -- Geoff Collyer X XOn older systems, you will to also install a small program, setnewids, Xsetuid-root. If this worries you, read setnewsids.c; all it does is Xexecute setgid(), setuid() to the "news" group and user if they exist, Xotherwise relaynews's real ids. Setnewsids can be found in ../conf. X XYou can test relaynews by giving NEWSCTL, NEWSBIN or NEWSARTS Xenvironment variables to change the library, binary or spool Xdirectories and I encourage this. X XIf you plan to run rn, you'll need the rn patches to allow Xref: to Xwork without Relay-Version:, which has been banished. X XYou will need to put your site name in /usr/lib/news/mailname (../conf/build Xlooks after all this). No upper case letters in your name Xplease, there is no call for it and it just looks ugly. X XYou must only permit relaynews to run on file servers since newsboot clears Xall locks in /usr/lib/news. X XYou'll need compress for compressing or uncompressing batches of news. XSee the contact person of your news feed or the moderator of the Xnewsgroup comp.sources.unix (try uunet!sources). X XSee the anews directory for conversion filters from A to B and back. X XYou'll need to install /usr/lib/newsbin/gngp (see ../misc) before inews Xwill work. X XB-2.11-isms. Your /usr/lib/news/mailpaths file must be updated to Xpoint at your nearest backbone site. A 5th sys file field for XDistribution: patterns is available (add them in sys after the Xsubscription list, separated by "/"), and a 6th field for excluded Xhosts is also, separated by "/" from the system name. X XGood Luck. X X Geoff Collyer, 8 June 1989 ! echo 'rna/README': sed 's/^X//' >'rna/README' <<'!' XThis is the "Australian readnews", written by Michael Rourke at UNSW. XIt's a simple and reasonably well-put-together news reader suitable for Xgiving to naive users who aren't going to be reading news much. X XYou'll need to fiddle defs.h for your machine, then type "make". X XNote that README.aus assumes you are installing the entire UNSW news Xsystem, not just readnews; some of it is inapplicable. ! echo 'rna/README.aus': sed 's/^X//' >'rna/README.aus' <<'!' XThe files in this distribution are: X X Makefile X README X active.c X at.h X defs.h X expire.c X funcs.c X header.c X history.c X lib X lib/bsearch.c X lib/memset.c X lib/strpbrk.c X lib/tmpfile.c X lib/tmpnam.c X maketime.c X man X man/postnews.1 X man/readnews.1 X man/uurec.8 X man/uusend.8 X mtempnam.c X news.help X newsrc.c X postnews.c X readnews.c X sample.sys X uurec.c X uusend.c X XThis news system is modelled on the USENET news system Xby Mark Horton (and others). X XApart from some minor programs the system has been completely re-written. XThe aim of re-writing was to produce a system that was: X 1. smaller X 2. cleaner X 3. faster X 4. was compatible at the site <--> site level with USENET X 5. had a better user interface ("readnews" and "postnews") X XThese goals have been met. XThe programs "readnews" and "postnews" are 1/3 the previous size, and Xdoes not require separate I/D space to run on pdp11/70's. XAlso far fewer processes are needed to use "postnews". X XThis system is compatible with USENET at the site <--> site level, provided Xcommunication is done with Version B format messages (the current 'standard'). XThe messages meet the Standard for the format of ARPA Internet Text messages X(RFC 822). X X"postnews" methods of editing messages is compatible with our local "mail" Xprogram (also re-written locally). X XTo aid someone familiar with USENET to find his/her way around the source: X Program changes: X "checknews" has become a function of "readnews" (readnews -cC) X "postnews" and "inews" are combined into "postnews" X "readnews" has the same function (simplified user interface) X "expire" has the same function (simplified arguments) X "recnews" is not needed X "sendnews" has been renamed "uusend" (and simplified) X "uurec" has the same function X Files: X The layout of the news database is the same, except that articles X are named #<number> rather than <number>, so that numbers can X be a valid newsgroup (like class.6.621). X X "/usr/lib/news/active" has an extra field - the lowest numbered article X present in a newsgroup. X "/usr/lib/news/history" has a sightly different format. X "/usr/lib/news/sys" is compatible, except that the third field X is ignored (always expects format B site); colons are allowed in X the last field. X XTo setup the news system: X 1. edit the "defs.h" file and make any changes necessary X in particular: MYDOMAIN, MYORG and the paths of SEQ, SYS etc. X MANGRPS should not be defined without making suitable X modifications to getmangrps() in readnews.c X UNSWMAIL is set if you have the version of mail from UNSW, X in particular it allows arguments "-s subject -i include_file" X to specify the subject, and make include_file available to X a ".i" command (like postnews). X AUSAM should not be set unless you have the hashed passwd file, X and locked file facilities of AUSAM. X 1a. edit "Makefile" for the pathnames of LIBDIR, BINDIR and NETDIR. X 2. create the account NEWSROOT (defined in defs.h) (this is where X the messages are kept). X 3. Run the makefile. If you don't have the routines found in X lib/* (bsearch, memset etc.) these can be compiled and X linked in as required. X 4. Create any groups (using "postnews -c 'newgroup <name>'"), X that require immediate local posting, otherwise groups will X be created automatically when news is received from other sites. X Root and NEWSROOT can also mail to non-existent groups, and X will be asked whether or not to create the new group. X 5. Set up a pseudo user "rnews" to direct received news into X "postnews -p" (with uid set to NEWSROOT). X How this is done will depend on your network implementation. X It may require a deamon emptying a mail box regularly X (see rnews.sh in this case). X If a mail interface is required, series of messages can be X piped into /usr/lib/news/uurec instead. X 6. Set up "/usr/lib/news/sys". See sample.sys for an example. X Each line in the "sys" file specifies: X host name X distribution newsgroups X (empty field (system assumes type B interchange)) X the command needed to send the item to the host. X Note the current host must have the first two fields also. X News transmission can be via "mail" or directly as a X network file transfer. X 7. Test the system by posting to "to.mysite". X 8. Arrange for "expire" to be run periodically (via "cron" or "at"). X XIf you had an existing (old) news system, and wish to transfer the Xarticles. The best way to do it is run the command: X X find oldnewsdir -type f -a -print ^ X while read F X do X postnews -p < $F X done X XMichael Rourke XUniversity of New South Wales, Australia 13 June 1984 X(decvax!mulga!michaelr:elecvax) X(vax135!mulga!michaelr:elecvax) ! echo 'rna/active.c': sed 's/^X//' >'rna/active.c' <<'!' X/* X * active file handling routines X * X * format of file: X * <groupname> ' ' <5 digit #> ' ' <5 digit #> ' ' flag '\n' X * (seq) (low) X */ X X#include "defs.h" X Xstatic char actname[] = ACTIVE; Xstatic int lineno; Xstatic active *alist; X X/* X * getseq - Get next sequence number for this group X * and update active file. X * If group missing append to file. X */ Xchar * Xgetseq(group) Xchar *group; X{ X register FILE *f; X register int i; X char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4]; X extern char *itoa(); X X f = fopenl(actname); X lineno = 0; X while (getline(f, gbuf, dbuf, dbuf2)) X if (CMP(gbuf, group) == 0) { X i = atoi(dbuf); X i++; X fseek(f, -12L, 1); X (void) fprintf(f, "%05d", i); X fclose(f); X#if !AUSAM X unlock(actname); X#endif X return itoa(i); X } X (void) fprintf(f, "%s 00001 00001 y\n", group); X fclose(f); X#if !AUSAM X unlock(actname); X#endif X return itoa(1); X} X X Xstatic Xgetline(f, g, d, d2) Xregister FILE *f; Xchar *g, *d, *d2; X{ X register int c; X register char *s; X X lineno++; X s = g; X while ((c = getc(f)) != ' ' && c != EOF) X *s++ = c; X *s = '\0'; X X if (c != EOF) { X s = d; X while ((c = getc(f)) != EOF && isdigit(c)) X *s++ = c; X *s = '\0'; X X s = d2; X if (c == ' ') X while ((c = getc(f)) != EOF && isdigit(c)) X *s++ = c; X *s = '\0'; X X if (c == ' ') X while ((c = getc(f)) != EOF && c != '\n') X ; /* eat flag */ X } X X if (c != EOF && (c != '\n' || !*d || !*d2)) X error("%s: bad format: line %d", actname, lineno); X return c != EOF; X} X X X/* X * build internal active file structure X */ Xactive * Xreadactive() X{ X register FILE *f; X register active *ap, *last; X char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4]; X X alist = last = NIL(active); X f = fopenf(actname, "r"); X lineno = 0; X while (getline(f, gbuf, dbuf, dbuf2)) { X ap = NEW(active); X ap->a_name = newstr(gbuf); X ap->a_seq = atoi(dbuf); X ap->a_low = atoi(dbuf2); X ap->a_next = NIL(active); X if (!alist) X alist = ap; X else X last->a_next = ap; X last = ap; X } X fclose(f); X return alist; X} X X X/* X * return pointer to named group X */ Xactive * Xactivep(grp) Xregister char *grp; X{ X register active *ap; X X for (ap = alist; ap; ap = ap->a_next) X if (CMP(grp, ap->a_name) == 0) X break; X return ap; X} X X X/* X * setlow - set the low number for this group X */ Xsetlow(group, low) Xchar *group; Xint low; X{ X register FILE *f; X char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4]; X extern char *itoa(); X X f = fopenl(actname); X lineno = 0; X while (getline(f, gbuf, dbuf, dbuf2)) X if (CMP(gbuf, group) == 0) { X fseek(f, -6L, 1); X (void) fprintf(f, "%05d", low); X break; X } X fclose(f); X#if !AUSAM X unlock(actname); X#endif X} X X X X/* X * initgrp - initialise an entry for this group X */ Xinitgrp(group) Xchar *group; X{ X register FILE *f; X char gbuf[BUFSIZ / 2], dbuf[BUFSIZ / 4], dbuf2[BUFSIZ / 4]; X X f = fopenl(actname); X lineno = 0; X while (getline(f, gbuf, dbuf, dbuf2)) X if (CMP(gbuf, group) == 0) { X#if !AUSAM X unlock(actname); X#endif X return; X } X (void) fprintf(f, "%s 00000 00001\n", group); X X} ! echo 'rna/at.h': sed 's/^X//' >'rna/at.h' <<'!' X#define SECINWEEK 604800L X#define SECINDAY 86400L X#define SECINHOUR 3600L X#define SECINMIN 60L X#define DAYSTO1983 (10*365 + 3*366) X#define MAXTIME 0x7fffffffL X X/* X * frequencies X */ X#define HOURLY 1 X#define DAILY 2 X#define WEEKLY 3 X#define MONTHLY 4 X#define BOOT 5 X#define BATCHTIME 6 /* not really a frequency - just looks like one */ X X/* X * time types recognised X */ X#define DAYS 0 /* days only */ X#define TIMES 1 /* days, times */ X#define FULL 2 /* days, times, frequencies */ X#define STIMES 3 /* days, times - be silent about errors */ ! echo 'rna/defs.h': sed 's/^X//' >'rna/defs.h' <<'!' X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/dir.h> X#include <stdio.h> X#include <ctype.h> X#include <time.h> X#ifdef USG X#include <fcntl.h> X#endif X#include <signal.h> X#include <sgtty.h> X#include "at.h" X X#define NEWSVERSION "B UNSW 1.1 19 Sep 1984" X X/* Things that very well may require local configuration */ X X#define TIMEZONE "EST" /* name of time zone */ X X#define DFLTSUB "general,general.all" /* default subscription list */ X#define ADMSUB "general" /* Mandatory subscription list */ X#define MODGROUPS "mod.all,all.mod,all.announce" /* Moderated groups */ X#define DFLTGRP "general" /* default newsgroup (for postnews) */ X/* #define MANGRPS 1 /* if you have mandatory subscriptions X tailored per-person (uses X getclasses()) */ X/*#define OZ 1*/ /* if on Australian network, used X in readnews to get correct return X address */ X/*#define AUSAM 1*/ /* hashed passwd file, locked files */ X#if AUSAM X#include <passwd.h> X#else X#include <pwd.h> X#endif X X#ifdef vax X/* #define NETPATH 1 /* if you have path finding program X /bin/netpath */ X#endif X/*#define UNSWMAIL 1*/ /* if you have UNSW "mail" which X allows "-s subject -i include_file" X arguments */ X#define NETID "utzoo" X#ifndef NETID X#define NETID "utstat" /* else define it here */ X#endif X X#ifndef NETID X#include <table.h> /* UNSW only */ X#endif X X/* #define MC "/usr/bin/p" /* pager */ X#define UUNAME "/usr/bin/uuname" X#define RNEWS "exec rnews" /* rnews for uurec to fork */ X#define POSTNEWS "/usr/bin/inews" X#define CHOWN "/etc/chown" /* pathname of chown command */ X#define SHELL "/bin/sh" /* if not bourne shell see postnews.c */ X#define MKDIR "/bin/mkdir" X#define MAIL "/bin/mail" X#if UNSWMAIL X#define FASTMAIL "/bin/mail" X#else X#define FASTMAIL MAIL X#endif X X#define HELP "/usr/lib/news/help.readnews" /* Help text */ X#define SEQ "/usr/lib/news/seq" /* Next sequence number */ X#define SYS "/usr/lib/news/sys" /* System subscription lists */ X#define ACTIVE "/usr/lib/news/active" /* Active newsgroups */ X#define HISTORY "/usr/lib/news/history" /* Current articles */ X X#define MYDOMAIN "uucp" /* Local domain */ X#define MYORG "U of Toronto Zoology" /* My organization */ X#define NEWSROOT "news" /* news editor */ X X/* Things you might want to change */ X X#define NEWSRC ".newsrc" /* name of .newsrc file */ X#define PAGESIZE 24 /* lines on screen */ X#define ARTICLES "articles" /* default place to save articles */ X#define NEGCHAR '!' /* newsgroup negation character */ X#define NEGS "!" /* ditto (string) */ X#define BADGRPCHARS "/#!" /* illegal chars in group name */ X#define BUFLEN 256 /* standard buffer size */ X#define ED "/bin/ed" /* default, if $EDITOR not set */ X X/* Things you probably won't want to change */ X X#define NGSEPCHAR ',' /* delimit character in news group line */ X#define NGSEPS "," /* ditto */ X#define PSEPS "!" /* separator in Path: */ X#define PSEPCHAR '!' /* ditto */ X#define PATHPREF "..!" /* prefix for addresses worked out from Path: */ X#define TRUE 1 X#define FALSE 0 X X#ifndef F_SETFD X#ifdef F_SETFL X#define F_SETFD F_SETFL /* SETFL becomes SETFD (close on exec arg X to fcntl) */ X#endif X#endif X Xtypedef enum booltype { false = 0, true } bool; Xtypedef enum applytype { stop, next, nextgroup, searchgroup } applycom; Xtypedef applycom (*apcmfunc)(); Xtypedef enum pheadtype { printing, passing, making } pheadcom; X X/* X * header structure X */ Xtypedef struct header { X /* mandatory fields */ X char *h_relayversion; X char *h_postversion; X char *h_from; X char *h_date; X char *h_newsgroups; X char *h_subject; X char *h_messageid; X char *h_path; X /* optional fields */ X char *h_replyto; X char *h_sender; X char *h_followupto; X char *h_datereceived; X char *h_expires; X char *h_references; X char *h_control; X char *h_distribution; X char *h_organisation; X char *h_lines; X /* any we don't recognise */ X char *h_others; X} header; X X/* X * internal structure for active file X */ Xtypedef struct active active; Xstruct active { X char *a_name; X short a_seq; X short a_low; X active *a_next; X}; X X/* X * internal struct for newsrc file X */ Xtypedef struct newsrc newsrc; Xstruct newsrc { X char *n_name; X bool n_subscribe; X short n_last; X newsrc *n_next; X}; X Xchar *strrchr(), *strchr(), *strcat(), *strcpy(), *strpbrk(); Xchar *itoa(), *convg(), *ngsquash(), *ttoa(), *mgets(), *rconvg(); Xchar *newstr(), *newstr2(), *newstr3(), *newstr4(), *newstr5(), *catstr(); Xchar *catstr2(), *bsearch(), *mtempnam(), *newstr6(); Xchar *getunique(), *getretaddr(), *getsubject(); XFILE *fopenl(), *fopenf(); Xchar *memset(), *myalloc(), *myrealloc(); Xlong time(), atol(), atot(); Xint strpcmp(); Xactive *readactive(); Xchar *getenv(); X X#define NIL(type) ((type *) 0) X#define NEW(type) ((type *) myalloc(sizeof(type))) X#define CMP(a, b) (*(a) != *(b) ? *(a) - *(b) : strcmp(a, b)) X#define CMPN(a, b, n) (*(a) != *(b) ? *(a) - *(b) : strncmp(a, b, n)) X X/* bw 9/15/84 */ X#define uid_t int X#define index strchr X#define rindex strrchr ! echo 'rna/funcs.c': sed 's/^X//' >'rna/funcs.c' <<'!' X#include "defs.h" X X/* X * string handling functions X */ Xchar * Xmyalloc(size) Xint size; X{ X register char *cp; X X extern char *malloc(); X X if ((cp = malloc((unsigned) size)) == NIL(char)) X error("No more memory."); X return cp; X} X X Xchar * Xmyrealloc(ptr, size) Xchar *ptr; Xint size; X{ X register char *cp; X X extern char *realloc(); X X if ((cp = realloc(ptr, (unsigned) size)) == NIL(char)) X error("No more memory."); X return cp; X} X X Xchar * Xnewstr(s) Xchar *s; X{ X return strcpy(myalloc(strlen(s) + 1), s); X} X X Xchar * Xnewstr2(s1, s2) Xchar *s1, *s2; X{ X return strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + 1), s1), s2); X} X X Xchar * Xnewstr3(s1, s2, s3) Xchar *s1, *s2, *s3; X{ X return strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + strlen(s3) + X 1), s1), s2), s3); X} X X Xchar * Xnewstr4(s1, s2, s3, s4) Xchar *s1, *s2, *s3, *s4; X{ X return strcat(strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + X strlen(s3) + strlen(s4) + 1), s1), s2), s3), s4); X} X X Xchar * Xnewstr5(s1, s2, s3, s4, s5) Xchar *s1, *s2, *s3, *s4, *s5; X{ X return strcat(strcat(strcat(strcat(strcpy(myalloc(strlen(s1) + strlen(s2) + X strlen(s3) + strlen(s4) + strlen(s5) + 1), s1), s2), s3), s4), s5); X} X X Xchar * Xnewstr6(s1, s2, s3, s4, s5, s6) Xchar *s1, *s2, *s3, *s4, *s5, *s6; X{ X return strcat(strcat(strcat(strcat(strcat(strcpy(myalloc(strlen(s1) + X strlen(s2) + strlen(s3) + strlen(s4) + strlen(s5) + strlen(s6) + 1), X s1), s2), s3), s4), s5), s6); X} X X Xchar * Xcatstr(old, s) Xchar *old, *s; X{ X return strcat(myrealloc(old, strlen(old) + strlen(s) + 1), s); X} X X Xchar * Xcatstr2(old, s1, s2) Xchar *old, *s1, *s2; X{ X return strcat(strcat(myrealloc(old, strlen(old) + strlen(s1) + strlen(s2) + X 1), s1), s2); X} X X X/* X * News group matching. X * X * nglist is a list of newsgroups. X * sublist is a list of subscriptions. X * sublist may have "meta newsgroups" in it. X * All fields are NGSEPCHAR separated. X * X * sublist uses "all" like shell uses "*", and "." like shell uses "/" X * if subscription X matches Y, it also matches Y.anything X */ Xngmatch(nglist, sublist) Xchar *nglist, *sublist; X{ X register char *n, *s, *nd, *sd; X register int rc; X X rc = 0; X n = nglist; X while (*n && rc == 0) { X if (nd = strchr(n, NGSEPCHAR)) X *nd = '\0'; X s = sublist; X while (*s) { X if (sd = strchr(s, NGSEPCHAR)) X *sd = '\0'; X if (*s != NEGCHAR) X rc |= ptrncmp(s, n); X else X rc &= ~ptrncmp(s + 1, n); X if (sd) X *sd = NGSEPCHAR, s = sd + 1; X else X break; X } X if (nd) X *nd = NGSEPCHAR, n = nd + 1; X else X break; X } X return rc; X} X X X/* X * Compare two newsgroups for equality. X * The first one may be a "meta" newsgroup. X */ Xstatic Xptrncmp(ng1, ng2) Xregister char *ng1, *ng2; X{ X X while (1) { X if (ng1[0] == 'a' && ng1[1] == 'l' && ng1[2] == 'l' && (ng1[3] == X '\0' || ng1[3] == '.')) { X if (ng1[3] == '\0') /* "all" matches anything */ X return 1; X while (*ng2 && *ng2 != '.') X ng2++; X if (*ng2 != '.') /* "all." doesn't match "xx" */ X return 0; X ng1 += 4, ng2++; X continue; X } X while (*ng1 && *ng1 != '.' && *ng1 == *ng2) X ng1++, ng2++; X if (*ng1 == '.') { X if (*ng2 != '.' && *ng2 != '\0') X return 0; /* "."'s don't line up */ X if (*ng2) X ng2++; X ng1++; /* "."'s line up - keep going */ X } else if (*ng1 == '\0') X return (*ng2 == '\0' || *ng2 == '.'); X /* full match or X matching X.thing */ X else X return 0; X } X /* NOTREACHED */ X} X X X/* X * return new newsgroup composed of only those from 'nglist' X * subscribed to by 'sublist' X * return NULL for empty list X */ Xchar * Xngsquash(nglist, sublist) Xregister char *nglist, *sublist; X{ X register char *delim; X register char *newg; X X newg = NIL(char); X while (*nglist) { X if (delim = strchr(nglist, NGSEPCHAR)) X *delim = '\0'; X if (ngmatch(nglist, sublist)) X newg = (newg ? catstr2(newg, NGSEPS, nglist) : newstr(nglist)); X if (delim) X *delim = NGSEPCHAR, nglist = delim + 1; X else X break; X } X return newg; X} X X X/* X * get unique sequence number from SEQ X */ Xchar * Xgetunique() X{ X register long number; X register FILE *f; X static char buf[12]; X X f = fopenl(SEQ); X if (fread(buf, 1, sizeof(buf), f) > 0) X number = atol(buf); X else X number = 1; X X rewind(f); X (void) fprintf(f, "%ld\n", number + 1); X fclose(f); X#if !AUSAM X unlock(SEQ); X#endif X X sprintf(buf, "%ld", number); X return buf; X} X X X/* X * open a locked file (or create) for reading and writing X */ XFILE * Xfopenl(fname) Xchar *fname; X{ X register FILE *f; X#ifdef AUSAM X struct stat sbuf; X#endif X X extern uid_t newsuid; X X if ((f = fopen(fname, "r+")) == NIL(FILE) && (f = fopen(fname, "w+")) == X NIL(FILE)) X error("Can't open %s", fname); X X#if AUSAM X if (fstat(fileno(f), &sbuf) != 0) X error("Can't stat %s", fname); X if ((sbuf.st_mode & S_IFMT) != S_IFALK && (chmod(fname, (int) (sbuf.st_mode X &~S_IFMT) | S_IFALK) != 0 || chown(fname, (int) newsuid, (int) newsuid) != X 0 || fclose(f) == EOF || (f = fopen(fname, "r+")) == NIL(FILE))) X error("Can't create %s", fname); X#else X chown(fname, (int) newsuid, (int) newsuid); X lock(fname); X#endif X X return f; X} X X X#if !AUSAM X X#define LSUFFIX ".lock" /* suffix for lock files */ X Xlock(fname) Xchar *fname; X{ X register char *lname; X register int i, f; X X lname = newstr2(fname, LSUFFIX); X for (i = 0; i < 10; i++) { X if ((f = creat(lname, 0)) != -1) { X close(f); X free(lname); X return; X } X sleep(2); X } X error("Can't creat %s after %d tries", lname, i); X} X X Xunlock(fname) Xchar *fname; X{ X register char *lname; X X lname = newstr2(fname, LSUFFIX); X unlink(lname); X free(lname); X} X X X#endif X X/* X * open a file X */ XFILE * Xfopenf(name, mode) Xchar *name, *mode; X{ X register FILE *f; X X if ((f = fopen(name, mode)) == NIL(FILE)) X error("Can't %s %s", *mode == 'r' ? "open" : "create", name); X return f; X} X X X/* X * replace all '.''s with '/' X */ Xchar * Xconvg(s) Xregister char *s; X{ X register char *sav; X X sav = s; X while (s = strchr(s, '.')) X *s = '/'; X return sav; X} X X X/* X * replace all '/''s with '.' X */ Xchar * Xrconvg(s) Xregister char *s; X{ X register char *sav; X X sav = s; X while (s = strchr(s, '/')) X *s = '.'; X return sav; X} X X X/* X * get a line from stdin X * trim leading and trailing blanks X */ Xchar * Xmgets() X{ X register char *s; X static char buf[BUFSIZ]; X X fflush(stdout); X if (fgets(buf, sizeof(buf), stdin) == NIL(char)) { X (void) printf("\n"); X return NIL(char); X } X if (s = strchr(buf, '\n')) X while (isspace(*s) && s > buf) X *s-- = '\0'; X else X { X (void) printf("Input line too long.\n"); X return NIL(char); X } X s = buf; X while (isspace(*s)) X s++; X return s; X} X X Xreadln(f) XFILE *f; X{ X register int c; X X if (feof(f) || ferror(f)) X return; X while ((c = getc(f)) != '\n' && c != EOF) X ; X} X X X/* X * compare string pointers X */ Xstrpcmp(a, b) Xchar **a, **b; X{ X return CMP(*a, *b); X} X X X/* X * apply the given function to each member in the newsgroup X */ X/* VARARGS2 */ Xapplyng(ng, func, arg1) Xregister char *ng; Xregister int (*func)(); Xchar *arg1; X{ X register char *delim; X register int err; X X err = 0; X while (*ng) { X if (delim = strchr(ng, NGSEPCHAR)) X *delim = '\0'; X err += (*func)(ng, arg1); X if (delim) X *delim = NGSEPCHAR, ng = delim + 1; X else X break; X } X return err; X} X X X/* X * generate a return address X */ Xchar * Xgetretaddr(hp) Xheader *hp; X{ X register char *ra; X X extern char *getpath(), *exaddress(); X#ifdef NETPATH X extern char *getnetpath(); X#endif X X if (hp->h_replyto) X ra = exaddress(hp->h_replyto); X else if (hp->h_from) X ra = exaddress(hp->h_from); X else X ra = NIL(char); X if (hp->h_path && !ra) X ra = getpath(hp->h_path); X#ifdef NETPATH X if (CMPN(ra, PATHPREF, sizeof(PATHPREF) - 1) == 0) X ra = getnetpath(ra); X#endif X return ra; X} X X X/* X * try and make a proper address X */ Xchar * Xexaddress(addr) Xchar *addr; X{ X register char *space, *dot, *at; X register char *raddr; X X raddr = NIL(char); X if (space = strchr(addr, ' ')) X *space = '\0'; X if (at = strchr(addr, '@')) { X *at = '\0'; X if (dot = strchr(at + 1, '.')) { X *dot = '\0'; X#if OZ X if (CMP(dot + 1, MYDOMAIN) == 0) X raddr = newstr3(addr, ":", at + 1); X else X#endif X raddr = newstr4(PATHPREF, at + 1, PSEPS, addr); X *dot = '.'; X } X *at = '@'; X } X if (space) X *space = ' '; X return raddr; X X} X X X/* X * return the last two components of the path X */ Xchar * Xgetpath(path) Xchar *path; X{ X register char *exlast, *ex; X register char *raddr; X X if (exlast = strrchr(path, PSEPCHAR)) { X *exlast = '\0'; X if (ex = strrchr(path, PSEPCHAR)) X raddr = newstr4(PATHPREF, ex + 1, PSEPS, exlast + 1); X else X raddr = newstr3(path, PSEPS, exlast + 1); X *exlast = PSEPCHAR; X } else X raddr = NIL(char); X return raddr; X} X X X#ifdef NETPATH X/* X * try and work out a path from our "netpath" database X */ Xchar * Xgetnetpath(path) Xchar *path; X{ X FILE * f; X register char *ex1, *ex2, *com, *new; X char buf[BUFSIZ]; X X if ((ex1 = strchr(path, PSEPCHAR)) && (ex2 = strchr(ex1 + 1, PSEPCHAR))) { X *ex2 = '\0'; X com = newstr4("exec ", NETPATH, " mulga ", ex1 + 1); X if ((f = popen(com, "r")) == NIL(FILE)) X (void) printf("Couldn't run \"%s\"\n", com); X else X { X fread(buf, sizeof(buf), 1, f); X if (pclose(f) != 0) { X (void) printf("Error in running \"%s\"\n", com); X fflush(stdout); X } else if (CMPN(buf, "mulga!", 6) == 0) { X if (ex1 = strchr(buf, '\n')) X *ex1 = '\0'; X new = newstr4(buf + 6, PSEPS, ex2 + 1, ":mulga"); X free(path); X path = new; X } X } X free(com); X *ex2 = PSEPCHAR; X } X return path; X X} X X X#endif X X/* X * remove extra spaces, and insert separators if necessary in X * newsgroups specification X */ Xconvgrps(sp) Xregister char *sp; X{ X register char *sep; X X sep = NIL(char); X while (*sp) { X if (sep) X sp++; X while (*sp && (isspace(*sp) || *sp == NGSEPCHAR)) X strcpy(sp, sp + 1); X if (sep) X *sep = (*sp ? NGSEPCHAR : '\0'); X while (*sp && !isspace(*sp) && *sp != NGSEPCHAR) X sp++; X sep = sp; X } X} X X ! echo done -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.