home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume19
/
cnews2
/
part17
< prev
next >
Wrap
Text File
|
1989-06-29
|
53KB
|
2,222 lines
Subject: v19i094: Cnews production release, Part17/19
Newsgroups: comp.sources.unix
Sender: sources
Approved: rsalz@uunet.UU.NET
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