home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume12
/
cnews
/
part10
< prev
next >
Wrap
Text File
|
1987-10-22
|
51KB
|
2,252 lines
Subject: v12i035: C News alpha release, Part10/14
Newsgroups: comp.sources.unix
Sender: sources
Approved: rs@uunet.UU.NET
Submitted-by: utzoo!henry (Henry Spencer)
Posting-number: Volume 12, Issue 35
Archive-name: cnews/part10
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 10 (of 14)."
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rna/funcs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rna/funcs.c'\"
else
echo shar: Extracting \"'rna/funcs.c'\" \(9767 characters\)
sed "s/^X//" >'rna/funcs.c' <<'END_OF_FILE'
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
END_OF_FILE
if test 9767 -ne `wc -c <'rna/funcs.c'`; then
echo shar: \"'rna/funcs.c'\" unpacked with wrong size!
fi
# end of 'rna/funcs.c'
fi
if test -f 'rna/header.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rna/header.c'\"
else
echo shar: Extracting \"'rna/header.c'\" \(10079 characters\)
sed "s/^X//" >'rna/header.c' <<'END_OF_FILE'
X/*
X * extract/output headers
X */
X
X#include "defs.h"
X
X#if AUSAM
Xextern struct pwent pe;
X#else
Xextern struct passwd *pp;
X#endif
Xextern char systemid[];
Xextern long now;
X
Xchar tzone[] = TIMEZONE;
Xchar hform[] = "%s: %s\n";
X
X/* Mandatory Headers */
Xchar t_relayversion[] = "Relay-Version";
Xchar t_postversion[] = "Posting-Version";
Xchar t_from[] = "From";
Xchar t_date[] = "Date";
Xchar t_newsgroups[] = "Newsgroups";
Xchar t_subject[] = "Subject";
Xchar t_messageid[] = "Message-ID";
Xchar t_path[] = "Path";
X
X/* Optional Headers */
Xchar t_replyto[] = "Reply-To";
Xchar t_sender[] = "Sender";
Xchar t_followupto[] = "Followup-To";
Xchar t_datereceived[] = "Date-Received";
Xchar t_expires[] = "Expires";
Xchar t_references[] = "References";
Xchar t_control[] = "Control";
Xchar t_distribution[] = "Distribution";
Xchar t_organization[] = "Organization";
Xchar t_lines[] = "Lines";
X
Xtypedef enum ft
X{
X f_control, f_date, f_datereceived, f_distribution,
X f_expires, f_followupto, f_from, f_lines, f_messageid,
X f_newsgroups, f_organization, f_path, f_postversion,
X f_references, f_relayversion, f_replyto, f_sender,
X f_subject
X}
X
X
Xftype;
X
Xtypedef struct field {
X char *f_name;
X ftype f_type;
X} field;
X
Xstatic field fields[] =
X{
X { t_control, f_control },
X { t_date, f_date },
X { t_datereceived, f_datereceived },
X { t_distribution, f_distribution },
X { t_expires, f_expires },
X { t_followupto, f_followupto },
X { t_from, f_from },
X { t_lines, f_lines },
X { t_messageid, f_messageid },
X { t_newsgroups, f_newsgroups },
X { t_organization, f_organization },
X { t_path, f_path },
X { t_postversion, f_postversion },
X { t_references, f_references },
X { t_relayversion, f_relayversion },
X { t_replyto, f_replyto },
X { t_sender, f_sender },
X { t_subject, f_subject }
X};
X
X
Xchar *weekdays[7] =
X{
X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
X};
X
X
Xchar *months[12] =
X{
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X};
X
X
Xstatic
Xfieldcmp(a, b)
Xfield *a, *b;
X{
X return CMP(a->f_name, b->f_name);
X}
X
X
X/*
X * extract headers from file,
X * position file to start of body
X */
Xgethead(f, hp)
XFILE *f;
Xheader *hp;
X{
X register char *colon, *space, *s;
X register field *fp;
X field af;
X char buf[BUFLEN*2];
X
X char *hfgets();
X
X memset((char *) hp, 0, sizeof(header));
X while (hfgets(buf, sizeof(buf), f)) {
X if (buf[0] == '\n')
X return;
X if (isupper(buf[0]) && (colon = strchr(buf, ':')) && (space =
X strchr(buf, ' ')) && (colon + 1 == space)) {
X *colon = '\0';
X af.f_name = buf;
X fp = (field * ) bsearch((char *) & af, (char *) fields,
X sizeof(fields) / sizeof(fields[0]), sizeof(fields[0]),
X fieldcmp);
X *colon = ':';
X } else
X fp = NIL(field);
X if (!fp)
X if (hp->h_others)
X hp->h_others = catstr(hp->h_others, buf);
X else
X hp->h_others = newstr(buf);
X else
X {
X if (colon = strchr(space + 1, '\n'))
X *colon = '\0';
X s = newstr(space + 1);
X switch (fp->f_type) {
X case f_control:
X hp->h_control = s;
X break;
X case f_date:
X hp->h_date = s;
X break;
X case f_datereceived:
X hp->h_datereceived = s;
X break;
X case f_distribution:
X hp->h_distribution = s;
X break;
X case f_expires:
X hp->h_expires = s;
X break;
X case f_followupto:
X hp->h_followupto = s;
X break;
X case f_from:
X hp->h_from = s;
X break;
X case f_lines:
X hp->h_lines = s;
X break;
X case f_messageid:
X hp->h_messageid = s;
X break;
X case f_newsgroups:
X hp->h_newsgroups = s;
X break;
X case f_organization:
X hp->h_organisation = s;
X break;
X case f_path:
X hp->h_path = s;
X break;
X case f_postversion:
X hp->h_postversion = s;
X break;
X case f_references:
X hp->h_references = s;
X break;
X case f_relayversion:
X hp->h_relayversion = s;
X break;
X case f_replyto:
X hp->h_replyto = s;
X break;
X case f_sender:
X hp->h_sender = s;
X break;
X case f_subject:
X hp->h_subject = s;
X break;
X }
X }
X }
X}
X
X
X/*
X * put headers to file
X */
Xputhead(hp, f, com)
Xheader *hp;
XFILE *f;
Xpheadcom com;
X{
X register char *s;
X char *getunique();
X extern char *getenv();
X
X if (hp->h_relayversion && com == printing)
X (void) fprintf(f, hform, t_relayversion, hp->h_relayversion);
X else if (com != printing)
X (void) fprintf(f, "%s: version %s; site %s.%s\n", t_relayversion, NEWSVERSION,
X systemid, MYDOMAIN);
X
X if (hp->h_postversion)
X (void) fprintf(f, hform, t_postversion, hp->h_postversion);
X else if (com == making)
X (void) fprintf(f, "%s: version %s; site %s.%s\n", t_postversion, NEWSVERSION,
X systemid, MYDOMAIN);
X
X
X if (hp->h_from)
X (void) fprintf(f, hform, t_from, hp->h_from);
X else if(com == making) {
X if(s = getenv("NAME"))
X (void) fprintf(f, "%s: %s@%s.%s (%s)\n", t_from,
X#if AUSAM
X pe.pw_strings[LNAME],
X#else
X pp->pw_name,
X#endif
X systemid, MYDOMAIN, s);
X else
X (void) fprintf(f,
X#if AUSAM
X "%s: %s@%s.%s (%s %s)\n",
X#else
X "%s: %s@%s.%s\n",
X#endif
X t_from,
X#if AUSAM
X pe.pw_strings[LNAME],
X#else
X pp->pw_name,
X#endif
X systemid, MYDOMAIN
X#if AUSAM
X ,
X pe.pw_strings[FIRSTNAME],
X pe.pw_strings[LASTNAME]
X#endif
X );
X }
X
X if (hp->h_date)
X (void) fprintf(f, hform, t_date, hp->h_date);
X else if (com == making)
X (void) fprintf(f, hform, t_date, ttoa(now));
X
X if (hp->h_newsgroups)
X (void) fprintf(f, hform, t_newsgroups, hp->h_newsgroups);
X else if (com == making)
X (void) fprintf(f, hform, t_newsgroups, DFLTGRP);
X
X if (hp->h_subject)
X (void) fprintf(f, hform, t_subject, hp->h_subject);
X else if (com == making)
X error("No subject field.");
X
X if (hp->h_messageid)
X (void) fprintf(f, hform, t_messageid, hp->h_messageid);
X else if (com == making)
X error("No messageid.");
X
X if (hp->h_path && com == passing)
X (void) fprintf(f, "%s: %s!%s\n", t_path, systemid, hp->h_path);
X else if (hp->h_path)
X (void) fprintf(f, hform, t_path, hp->h_path);
X else if(com == making)
X (void) fprintf(f, "%s: %s!%s\n", t_path, systemid,
X#if AUSAM
X pe.pw_strings[LNAME]
X#else
X pp->pw_name
X#endif
X );
X
X /* optional */
X
X if (hp->h_replyto)
X (void) fprintf(f, hform, t_replyto, hp->h_replyto);
X
X if (hp->h_sender)
X (void) fprintf(f, hform, t_sender, hp->h_sender);
X
X if (hp->h_followupto)
X (void) fprintf(f, hform, t_followupto, hp->h_followupto);
X
X if (hp->h_datereceived && com == printing)
X (void) fprintf(f, hform, t_datereceived, hp->h_datereceived);
X else if (com != printing)
X (void) fprintf(f, hform, t_datereceived, ttoa(now));
X
X if (hp->h_expires)
X (void) fprintf(f, hform, t_expires, hp->h_expires);
X
X if (hp->h_references)
X (void) fprintf(f, hform, t_references, hp->h_references);
X
X if (hp->h_control)
X (void) fprintf(f, hform, t_control, hp->h_control);
X
X if (hp->h_distribution)
X (void) fprintf(f, hform, t_distribution, hp->h_distribution);
X
X if (hp->h_organisation)
X (void) fprintf(f, hform, t_organization, hp->h_organisation);
X else if (com == making)
X (void) fprintf(f, hform, t_organization, (s = getenv("ORGANIZATION")) ?
X s : MYORG);
X
X if (hp->h_lines)
X (void) fprintf(f, hform, t_lines, hp->h_lines);
X
X if (hp->h_others)
X fputs(hp->h_others, f);
X}
X
X
X/*
X * free all strings allocated to header
X */
Xfreehead(hp)
Xregister header *hp;
X{
X if (hp->h_relayversion)
X free(hp->h_relayversion);
X if (hp->h_postversion)
X free(hp->h_postversion);
X if (hp->h_from)
X free(hp->h_from);
X if (hp->h_date)
X free(hp->h_date);
X if (hp->h_newsgroups)
X free(hp->h_newsgroups);
X if (hp->h_subject)
X free(hp->h_subject);
X if (hp->h_messageid)
X free(hp->h_messageid);
X if (hp->h_path)
X free(hp->h_path);
X if (hp->h_replyto)
X free(hp->h_replyto);
X if (hp->h_sender)
X free(hp->h_sender);
X if (hp->h_followupto)
X free(hp->h_followupto);
X if (hp->h_datereceived)
X free(hp->h_datereceived);
X if (hp->h_expires)
X free(hp->h_expires);
X if (hp->h_references)
X free(hp->h_references);
X if (hp->h_control)
X free(hp->h_control);
X if (hp->h_distribution)
X free(hp->h_distribution);
X if (hp->h_organisation)
X free(hp->h_organisation);
X if (hp->h_lines)
X free(hp->h_lines);
X if (hp->h_others)
X free(hp->h_others);
X}
X
X
X/*
X * hfgets is like fgets, but deals with continuation lines.
X * It also ensures that even if a line that is too long is
X * received, the remainder of the line is thrown away
X * instead of treated like a second line.
X */
Xchar *
Xhfgets(buf, len, fp)
Xchar *buf;
Xint len;
XFILE *fp;
X{
X register int c;
X register char *cp, *tp;
X
X if ((cp = fgets(buf, len, fp)) == NIL(char))
X return NIL(char);
X
X if (*cp == '\n')
X return cp;
X
X tp = cp + strlen(cp);
X if (tp[-1] != '\n') {
X /* Line too long - part read didn't fit into a newline */
X while ((c = getc(fp)) != '\n' && c != EOF)
X ;
X } else
X *--tp = '\0'; /* clobber newline */
X
X while ((c = getc(fp)) == ' ' || c == '\t') {
X /* Continuation line. */
X while ((c = getc(fp)) == ' ' || c == '\t')
X ;
X if (tp - cp < len) {
X *tp++ = ' ';
X *tp++ = c;
X }
X while ((c = getc(fp)) != '\n' && c != EOF)
X if (tp - cp < len)
X *tp++ = c;
X }
X *tp++ = '\n';
X *tp++ = '\0';
X if (c != EOF)
X ungetc(c, fp); /* push back first char of next header */
X return cp;
X}
X
X
X/*
X * time to ascii
X * leave time in static var
X */
Xchar *
Xttoa(t)
Xlong t;
X{
X static char buf[40];
X struct tm *tp;
X extern struct tm *localtime();
X
X tp = localtime(&t);
X sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s", weekdays[tp->tm_wday],
X tp->tm_mday, months[tp->tm_mon], tp->tm_year, tp->tm_hour, tp->tm_min,
X tp->tm_sec, tzone);
X return buf;
X
X}
X
X
X/*
X * ascii to time
X * return 0L on error
X */
Xlong
Xatot(s)
Xchar *s;
X{
X char *argv[4];
X int day, year, hour, min, sec;
X char month[10], sday[10], stime[10], syear[10];
X extern long maketime();
X
X if (sscanf(s, "%*s %d %*[ -] %9[^ -] %*[ -] %d %2d:%2d:%2d", &day, month,
X &year, &hour, &min, &sec) != 6)
X return 0L;
X sprintf(sday, "%d", day);
X sprintf(stime, "%d:%d:%d", hour, min, sec);
X sprintf(syear, "%d", 1900 + year);
X argv[0] = sday;
X argv[1] = month;
X argv[2] = stime;
X argv[3] = syear;
X return maketime(4, argv, STIMES);
X}
X
X
END_OF_FILE
if test 10079 -ne `wc -c <'rna/header.c'`; then
echo shar: \"'rna/header.c'\" unpacked with wrong size!
fi
# end of 'rna/header.c'
fi
if test -f 'rna/maketime.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rna/maketime.c'\"
else
echo shar: Extracting \"'rna/maketime.c'\" \(8785 characters\)
sed "s/^X//" >'rna/maketime.c' <<'END_OF_FILE'
X/*
X * long
X * maketime(argc, argv, type)
X *
X * A standard routine to convert a future time (in English) to seconds.
X * Arguments are order-independent (except for suffixes), and words
X * may be shortened to a non-ambiguous abbreviation.
X * As the time must be in the future, unspecified years, months and days default
X * to the "next" year, month or day if necessary; otherwise the current
X * month, day and hour are used.
X *
X * type is either TIMES in which days, times are recognised, or just DAYS.
X *
X * Tries hard to give meaningful messages, and make sure the user
X * gets the time she/he wanted!
X *
X * Return is in seconds or 0 if error.
X * Error messages to stderr.
X *
X * Michael Rourke (UNSW) Christmas 1982
X *
X * Syntax:
X *
X * timespec ::= { time | day | month | year } .
X *
X * time ::= [ hour [ ":" min [ ":" second ] ] ] [ timemodifier ] .
X *
X * timemodifier ::= "am" | "pm" | "noon" | "midday" | "midnight" | "now" .
X *
X * day ::= ( dayofweek [ "week" ] ) | number .
X *
X * dayofweek ::= "sunday" | "monday" | "tuesday" | "wednesday" |
X * "thursday" | "friday" | "saturday" | "tomorrow" |
X * "today" .
X *
X * month ::= "january" | "february" | "march" | "april" | "may" | "june" |
X * "july" | "august" | "september" | "october" | "november" |
X * "december" .
X *
X * year ::= "19" number .
X *
X */
X
X#include "defs.h"
X
X#define NOW -1
X
Xstatic timemod(), noonmid(), daymod(), weekday(), smonth();
X
Xstatic struct slist {
X char *s_name;
X int (*s_action)();
X char s_val;
X char s_type;
X} slist[] =
X{
X { "am", timemod, 0, TIMES, },
X { "pm", timemod, 12, TIMES, },
X { "noon", noonmid, 12, TIMES, },
X { "midday", noonmid, 12, TIMES, },
X { "midnight", noonmid, 0, TIMES, },
X { "now", noonmid, NOW, TIMES, },
X { "week", daymod, 0, DAYS, },
X { "sunday", weekday, 0, DAYS, },
X { "monday", weekday, 1, DAYS, },
X { "tuesday", weekday, 2, DAYS, },
X { "wednesday", weekday, 3, DAYS, },
X { "thursday", weekday, 4, DAYS, },
X { "friday", weekday, 5, DAYS, },
X { "saturday", weekday, 6, DAYS, },
X { "tomorrow", weekday, 7, DAYS, },
X { "today", weekday, 8, DAYS, },
X { "january", smonth, 0, DAYS, },
X { "february", smonth, 1, DAYS, },
X { "march", smonth, 2, DAYS, },
X { "april", smonth, 3, DAYS, },
X { "may", smonth, 4, DAYS, },
X { "june", smonth, 5, DAYS, },
X { "july", smonth, 6, DAYS, },
X { "august", smonth, 7, DAYS, },
X { "september", smonth, 8, DAYS, },
X { "october", smonth, 9, DAYS, },
X { "november", smonth, 10, DAYS, },
X { "december", smonth, 11, DAYS, },
X { "", 0, 0, 0, }
X};
X
X
Xstatic char daysinmonth[12] =
X{
X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
X
X
Xstatic int hour, minute, second, day, year, dayofweek, month;
Xstatic int settime, setday, setyear, setdayofweek, setmonth;
Xstatic int setweek, err, setmod;
Xstatic char *curarg;
Xstatic struct tm *tim;
Xstatic int gtype; /* global `type' arg */
Xstatic short silent;
X
Xlong
Xmaketime(argc, argv, type)
Xint argc;
Xchar **argv;
Xint type;
X{
X struct tm *localtime();
X long time(), construct(), now, then;
X
X if (type == STIMES)
X type = TIMES, silent = 1;
X else
X silent = 0;
X gtype = type;
X now = time((long *) 0);
X tim = localtime(&now);
X
X /*
X * set defaults
X */
X hour = tim->tm_hour;
X minute = tim->tm_min;
X second = tim->tm_sec;
X day = tim->tm_mday;
X year = tim->tm_year + 1900;
X dayofweek = tim->tm_wday;
X month = tim->tm_mon;
X
X settime = setday = setyear = setdayofweek = setmonth = 0;
X setweek = err = setmod = 0;
X
X while (argc--)
X timearg(curarg = *argv++);
X if (err)
X return (long) 0;
X
X checktime();
X if (err)
X return (long) 0;
X
X then = construct();
X /*
X if(now > then)
X {
X error("Time specified has passed.");
X return (long) 0;
X }
X*/
X return then;
X}
X
X
Xstatic
Xtimearg(s)
Xchar *s;
X{
X lower(s);
X if (isdigit(*s))
X numbers(s);
X else
X words(s);
X}
X
X
Xstatic
Xlower(s)
Xregister char *s;
X{
X while (*s) {
X *s = tolower(*s);
X s++;
X }
X}
X
X
Xstatic
Xnumbers(s)
Xregister char *s;
X{
X register int val;
X
X val = 0;
X while (isdigit(*s))
X val = val * 10 + *s++ - '0';
X if (val > 1900)
X if (setyear++)
X reperror("year");
X else
X year = val;
X else if (*s == '\0')
X if (setday++)
X reperror("day");
X else
X day = val;
X else if (settime++)
X reperror("time");
X else
X {
X hour = val;
X if (*s == ':') {
X s++;
X val = 0;
X while (isdigit(*s))
X val = val * 10 + *s++ - '0';
X minute = val;
X if (*s == ':') {
X s++;
X val = 0;
X while (isdigit(*s))
X val = val * 10 + *s++ - '0';
X second = val;
X } else
X second = 0;
X } else
X minute = second = 0;
X }
X if (*s)
X words(curarg = s);
X}
X
X
Xstatic
Xreperror(s)
Xchar *s;
X{
X error("Repeated %s argument: \"%s\"", s, curarg);
X}
X
X
X/* VARARGS1 */
Xstatic
Xerror(s, a1, a2, a3, a4)
Xchar *s;
Xint a1, a2, a3, a4;
X{
X err++;
X if (silent)
X return;
X (void) fprintf(stderr, "Error in time specification: ");
X (void) fprintf(stderr, s, a1, a2, a3, a4);
X (void) fprintf(stderr, "\n");
X}
X
X
Xstatic
Xwords(s)
Xchar *s;
X{
X register struct slist *sp, *found;
X register int size;
X register char *wstart;
X
X sp = slist;
X wstart = s;
X size = 0;
X while (*s && !isdigit(*s))
X size++, s++;
X found = (struct slist *) 0;
X while (*(sp->s_name)) {
X if (sp->s_type <= gtype && CMPN(sp->s_name, wstart, size) ==
X 0)
X if (!found) {
X found = sp;
X if (strlen(sp->s_name) == size)
X break; /* otherwise an abbreviation */
X }
X else
X {
X error("Ambiguous abbreviation: \"%.*s\"", size,
X wstart);
X return;
X }
X sp++;
X }
X if (found)
X (*(found->s_action))(found->s_val);
X else
X error("Unknown word: \"%.*s\"", size, wstart);
X if (*s)
X numbers(curarg = s);
X}
X
X
Xstatic
Xtimemod(val)
Xint val;
X{
X if (!settime)
X error("Can only use \"am\" or \"pm\" after a time.");
X else if (setmod++)
X reperror("time modifier");
X else if (hour < 12)
X hour += val;
X else if (hour > 12)
X error("Can't use \"am\" or \"pm\" with 24 hour clock.");
X else if (val == 0) /* am && hour == 12 */
X hour = 0; /* 12am correction */
X}
X
X
Xstatic
Xnoonmid(val)
Xint val;
X{
X if (val < 0) /* NOW */ {
X if (settime++)
X reperror("time");
X /* let defaults work */
X } else if (setmod++) /* noon, midnight */
X reperror("time modifier");
X else
X {
X if (!settime)
X settime++;
X else if (hour != 12 || minute != 0 || second != 0)
X error("Illegal time: %02d:%02d:%02d %s", hour, minute,
X second, curarg);
X hour = val;
X minute = second = 0;
X }
X}
X
X
Xstatic
Xdaymod()
X{
X if (setweek++)
X reperror("\b");
X else if (!setdayofweek)
X error("Can only use \"week\" after a weekday name.");
X else
X dayofweek += 7;
X}
X
X
Xstatic
Xweekday(val)
Xint val;
X{
X if (setday++)
X reperror("day");
X else
X {
X setdayofweek++;
X if (val < 7) {
X dayofweek = val - dayofweek; /* now a displacement */
X if (dayofweek <= 0)
X dayofweek += 7;
X } else if (val == 7) /* tomorrow */
X dayofweek = 1;
X else /* today */
X dayofweek = 0;
X }
X}
X
X
Xstatic
Xsmonth(val)
Xint val;
X{
X if (setmonth++)
X reperror("day of month");
X else
X month = val;
X}
X
X
Xstatic
Xchecktime()
X{
X register int dim;
X
X if (gtype == DAYS && settime)
X error("Times are not accepted.");
X if (year < 1983 || year > 2038)
X error("Year out of range.");
X if (hour > 23 || minute > 59 || second > 59)
X error("Illegal time: %02d:%02d:%02d", hour, minute, second);
X if (!setdayofweek) {
X dim = daysinmonth[month] + (month == 1 ? leapyear(year) : 0);
X if (day > dim)
X error("Month day out of range. (> %d)", dim);
X }
X if (setdayofweek && (setmonth || setyear))
X error("Can't specify a weekday as well as a month or year.");
X}
X
X
Xstatic
Xleapyear(y)
Xint y;
X{
X return ((y % 4) == 0 && (y % 100) != 0) || (y % 400 == 0);
X}
X
X
Xstatic long
Xconstruct()
X{
X register int i, days;
X
X adjust();
X days = DAYSTO1983;
X for (i = 1983; i < year; i++)
X days += 365 + leapyear(i);
X for (i = 0; i < month; i++)
X days += daysinmonth[i] + (i == 1 ? leapyear(year) : 0);
X days += day - 1; /* days since 1 Jan 1970 */
X if (setdayofweek)
X days += dayofweek;
X return days * SECINDAY + hour * SECINHOUR + minute * SECINMIN + second;
X}
X
X
Xstatic
Xadjust()
X{
X register int dim;
X
X /*
X * make sure time defaults to the future
X */
X if (setdayofweek || setyear || month > tim->tm_mon)
X return;
X if (month < tim->tm_mon) {
X year++;
X return;
X }
X /*
X * month == tim->tm_mon
X */
X if (day > tim->tm_mday)
X return;
X if (day < tim->tm_mday) {
X if (setmonth || ++month / 12)
X year++, month %= 12;
X return;
X }
X /*
X * month == tim->tm_mon && day == tim->tm_mday
X */
X if ((long)(hour*SECINHOUR + minute*SECINMIN + second) <
X (long)(tim->tm_hour*SECINHOUR + tim->tm_min*SECINMIN + tim->tm_sec)) {
X dim = daysinmonth[month] + (month == 1? leapyear(month): 0);
X if (setday || ++day / dim) {
X if (setmonth || ++month / 12)
X year++, month %= 12;
X day %= dim;
X }
X return;
X }
X}
X
X
END_OF_FILE
if test 8785 -ne `wc -c <'rna/maketime.c'`; then
echo shar: \"'rna/maketime.c'\" unpacked with wrong size!
fi
# end of 'rna/maketime.c'
fi
if test -f 'rnews/relaynews.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rnews/relaynews.c'\"
else
echo shar: Extracting \"'rnews/relaynews.c'\" \(9783 characters\)
sed "s/^X//" >'rnews/relaynews.c' <<'END_OF_FILE'
X/*
X * relaynews - relay Usenet news (version C)
X * See the file COPYRIGHT for the copyright notice.
X *
X * relaynews should be setuid-news, setgid-news. You'll need to install
X * setnewsids setuid-root if setuid(geteuid()) doesn't work on your
X * machine (e.g. on V7 and possibly SystemIII).
X *
X * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
X * since.
X *
X * relaynews parses Newsgroups: headers, rejects articles by newsgroup &
X * message-id, files articles, updates the active & history files,
X * transmits articles, and honours (infrequent) control messages, which do
X * all sorts of varied and rococo things. Control messages are implemented
X * by separate programs. relaynews reads a "sys" file to control the
X * transmission of articles but can function as a promiscuous leaf node
X * without one.
X *
X * A truly radical notion: people may over-ride via environment variables
X * the compiled-in default directories so IHCC kludges are not needed and
X * testing is possible (and encouraged) in alternate directories. This
X * does cause a loss of privilege, to avoid spoofing.
X *
X * The disused I-have/send-me protocol is going to work; it's been broken
X * in B news for ages but no one has noticed because it's essentially
X * useless on the uucp network, especially when batching news articles,
X * but NNTP may breathe new life into it.
X *
X * Portability vs SystemV. relaynews uses dbm(3) and makes no apologies
X * for so doing. Imitation UNIX (registered trademark of AT&T in the
X * United States) brand operating systems that lack dbm are going to
X * have to use my incredibly slow dbm simulation.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X#include <signal.h> /* to make locking safe */
X#include <sys/types.h>
X
X#include "news.h"
X#include "newspaths.h"
X#include "active.h"
X#include "cpu.h"
X#include "headers.h"
X#include "system.h"
X
X/*
X * setuid-root program to set ids to news/news & rexec rnews with
X * NEWSPERMS in the environment to break loops.
X */
X#ifndef SETNEWSIDS
X#define SETNEWSIDS "setnewsids"
X#endif
X
X#ifndef NEWSUSER
X#define NEWSUSER "news"
X#endif
X#ifndef NEWSGROUP
X#define NEWSGROUP "news"
X#endif
X
Xchar *progname;
X
Xint remote = NO; /* articles are being relayed? */
X
Xchar *exclude = NULL; /* site to exclude, for erik */
Xstatic int userealids = NO;
X
X/*
X * main - parse arguments and handle options, lock & unlock news system.
X */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X int c, errflg = 0;
X int status = ST_OKAY;
X char origdir[MAXFILE]; /* current directory at start */
X char *newpath;
X extern int optind;
X extern char *optarg;
X
X progname = argv[0];
X
X /* setuid daemon prelude; various precautions */
X (void) umask(newsumask()); /* undo silly umasks */
X (void) alarm(0); /* cancel any pending alarm */
X /*
X * Reset certain environment variables to sane values.
X */
X newpath = malloc(STRLEN("PATH=") + STRLEN(STDPATH) + 1);
X if (newpath == NULL)
X exit(1); /* no chatter until stdfdopen */
X (void) strcpy(newpath, "PATH=");
X (void) strcat(newpath, STDPATH);
X if (!putenv(newpath) ||
X !putenv("IFS= \t\n"))
X exit(1); /* no chatter until stdfdopen */
X closeall(1); /* closes all but std descriptors */
X stdfdopen(); /* ensure standard descriptors are open */
X
X setids(argv); /* change of real and effective ids */
X /* we are now running as news, so you can all relax */
X
X /* ignore signals (for locking). relaynews runs quickly, so don't worry. */
X (void) signal(SIGINT, (sigarg_t)SIG_IGN);
X (void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
X (void) signal(SIGHUP, (sigarg_t)SIG_IGN);
X (void) signal(SIGTERM, (sigarg_t)SIG_IGN);
X
X /* parse options & set flags */
X while ((c = getopt(argc, argv, "pd:x:")) != EOF)
X switch (c) {
X case 'p': /* "rnews" mode: */
X ++remote; /* just relay, don't fuck about */
X break;
X /* all options below are new in C news */
X case 'd': /* -d debug-options; thanks, henry */
X if (!debugon(optarg))
X errflg++; /* debugon already complained */
X break;
X case 'x': /* -x site: don't send to site */
X /* you're welcome, erik */
X if (exclude != NULL) {
X (void) fprintf(stderr,
X "%s: more than one -x site (%s)\n",
X progname, optarg);
X errflg++;
X } else
X exclude = optarg;
X break;
X default:
X errflg++;
X break;
X }
X if (errflg) {
X (void) fprintf(stderr, "usage: %s [-p][-d fhlmt][-x site]\n",
X progname);
X exit(2);
X }
X
X /* lock the news system, set up log files */
X newslock(); /* done here due to dbm internal cacheing */
X if (remote) { /* TODO: test this some other way */
X redirectlogs(); /* rnews daemon: redirect to logs */
X#ifdef MANYERRORS
X (void) putc('\n', stderr); /* leave a blank line */
X /* prints "Jun 5 12:34:56" */
X timestamp(stderr, (time_t *)NULL, (char **)NULL);
X (void) putc('\n', stderr);
X#endif
X }
X
X /* process file name arguments */
X#ifdef RELATIVE_FILES_ALLOWED
X if (getwd(origdir) == 0)
X#endif
X (void) strncpy(origdir, "/dunno/man/like/somewhere.", MAXFILE);
X cd(fullartfile((char *)NULL)); /* move to spool directory */
X
X if (optind == argc)
X status |= process(stdin, "stdin");
X else
X for (; optind < argc; optind++)
X status |= relnmprocess(argv[optind], origdir);
X
X trclose(); /* close open batch files */
X status |= synccaches(); /* just being cautious */
X newsunlock(); /* unlock the news system */
X exit(status);
X}
X
Xsetids(argv) /* change of real and effective ids */
Xchar **argv;
X{
X int newsuid = getuid(), newsgid = getgid(); /* default to real ids */
X
X (void) ctlfile((char *)NULL); /* trigger unprivileged(), set userealids */
X if (!userealids) {
X /*
X * If setuid(geteuid()) fails, try execing a small,
X * setuid-root program to just do getpwnam(), getgrnam()
X * (with NEWSPERMS set), setgid(), setuid(),
X * and exec this program again. If NEWSPERMS is set,
X * the failure is a fatal error (recursive loop).
X * Then this program can be setuid-news.
X */
X (void) setgid(getegid());
X if (setuid(geteuid()) < 0) {
X if (getenv("NEWSPERMS") != 0)
X error("recursive loop setting ids", "");
X execv(libfile(SETNEWSIDS), argv);
X error("can't exec %s to set ids", libfile(SETNEWSIDS));
X /* NOTREACHED */
X }
X /* you can relax, we are now running as news */
X } else {
X (void) setgid(newsgid);
X (void) setuid(newsuid);
X }
X /* we are now running as news, so you can all relax */
X}
X
Xvoid
Xunprivileged() /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
X{
X userealids = YES;
X}
X
Xint /* YES/NO */
Xdebugon(dbopt)
Xregister char *dbopt;
X{
X int status = YES;
X
X for (; *dbopt != '\0'; dbopt++)
X switch (*dbopt) {
X case 'f':
X filedebug(YES);
X break;
X case 'h':
X hdrdebug(YES);
X break;
X case 'l':
X lockdebug(YES);
X break;
X case 'm':
X matchdebug(YES);
X break;
X case 't':
X transdebug(YES);
X break;
X default:
X status = NO; /* unknown debugging option */
X (void) fprintf(stderr, "%s: bad -d %c\n",
X progname, *dbopt);
X break;
X }
X return status;
X}
X
X/*
X * Redirect stdout and stderr into log files at known locations.
X */
Xredirectlogs()
X{
X logfile(stdout, libfile("log"));
X logfile(stderr, libfile("errlog"));
X}
X
Xlogfile(stream, name) /* redirect stream into name */
XFILE *stream;
Xchar *name;
X{
X if (freopen(name, "a", stream) == NULL)
X errunlock("can't redirect standard stream to %s", name);
X}
X
Xint /* status */
Xrelnmprocess(name, origdir) /* process a (relative) file name */
Xchar *name, *origdir;
X{
X register int status = ST_OKAY;
X register FILE *in;
X register char *fullname;
X
X fullname = emalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
X strlen(name) + 1);
X fullname[0] = '\0';
X
X if (name[0] != FNDELIM) { /* relative path */
X (void) strcat(fullname, origdir);
X (void) strcat(fullname, SFNDELIM);
X }
X (void) strcat(fullname, name);
X
X in = fopen(fullname, "r");
X if (in == NULL)
X warning("can't open argument `%s'", fullname);
X else {
X status |= process(in, fullname);
X (void) fclose(in);
X }
X free(fullname);
X return status;
X}
X
X/*
X * process - process input file
X * If it starts with '#', assume it's a batch and unravel it,
X * else it's a single article, so just inject it.
X */
Xint
Xprocess(in, inname)
XFILE *in;
Xchar *inname;
X{
X register int c;
X
X if ((c = getc(in)) == EOF)
X return ST_OKAY; /* normal EOF */
X (void) ungetc(c, in);
X if (c == '#')
X return unbatch(in, inname);
X else
X /* ST_SHORT should always come on with a count of MAXLONG */
X return cpinsart(in, inname, MAXLONG) & ~ST_SHORT;
X}
X
X/*
X * Unwind in and insert each article.
X * For each article, call cpinsart to copy the article
X * from in into a temporary file and rename the temp file
X * into the news spool directory.
X */
Xint
Xunbatch(in, inname)
XFILE *in;
Xchar *inname;
X{
X register int c;
X long charcnt;
X int status = ST_OKAY;
X char line[MAXLINE];
X
X while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
X (void) ungetc(c, in);
X /*
X * While out of sync, eat input lines,
X * then eat the tail end of the "#! rnews" line.
X */
X while (fgets(line, sizeof line, in) != NULL &&
X !batchln(line, &charcnt)) {
X status |= ST_DROPPED; /* argh! a bad batch */
X (void) fprintf(stderr, "%s: unbatcher out of synch, tossing: ",
X progname);
X (void) fputs(line, stderr);
X }
X if (!feof(in))
X status |= cpinsart(in, inname, charcnt);
X }
X return status;
X}
X
X/*
X * Is line a batcher-produced line (#! rnews count)?
X * If so, return the count through charcntp.
X * This is slightly less convenient than sscanf, but a lot smaller.
X */
Xint /* YES/NO */
Xbatchln(line, charcntp)
Xregister char *line;
Xregister long *charcntp;
X{
X register char *countp;
X static char batchtext[] = "#! rnews ";
X
X countp = line + STRLEN(batchtext);
X if (STREQN(line, batchtext, STRLEN(batchtext)) &&
X isascii(*countp) && isdigit(*countp)) {
X *charcntp = atol(countp);
X return YES;
X } else {
X *charcntp = 0;
X return NO;
X }
X}
END_OF_FILE
if test 9783 -ne `wc -c <'rnews/relaynews.c'`; then
echo shar: \"'rnews/relaynews.c'\" unpacked with wrong size!
fi
# end of 'rnews/relaynews.c'
fi
if test -f 'rnews/transmit.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rnews/transmit.c'\"
else
echo shar: Extracting \"'rnews/transmit.c'\" \(7923 characters\)
sed "s/^X//" >'rnews/transmit.c' <<'END_OF_FILE'
X/*
X * transmit - transmit incoming articles to neighbouring machines
X * TODO: deal with multiple references to the same batch file.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "news.h"
X#include "newspaths.h"
X#include "headers.h"
X#include "system.h"
X
X#define NOPENTFS 10 /* # of file descriptors kept open for batching */
X
Xstatic FILE *tfs[NOPENTFS]; /* keep these open always */
Xstatic int debug = NO;
X
Xtransdebug(state)
Xint 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 * hdrs->h_distr (which may be just a copy of hdrs->h_ngs).
X */
Xint
Xtransmit(hdrs, rmt, exclude)
Xregister struct headers *hdrs;
Xint rmt; /* inews/rnews flag */
Xchar *exclude; /* no copy to him */
X{
X register struct system *sys;
X register int fno = 0;
X int status = 0;
X
X rewsys();
X if (debug)
X (void) fprintf(stderr, "just rewound sys file\n");
X while ((sys = nextsys()) != NULL) {
X if (debug) {
X (void) fprintf(stderr, "hdrs->h_distr=%s\n",
X hdrs->h_distr);
X (void) fprintf(stderr, "sy_name=%s sy_ngs=%s\n",
X sys->sy_name, sys->sy_ngs);
X }
X if (oktransmit(hdrs, sys, sys->sy_flags, rmt, exclude))
X status |= ejaculate(hdrs, sys, fno);
X if (sys->sy_flags&(FLG_BATCH|FLG_SZBATCH))
X ++fno; /* count lines with F or f flag */
X }
X if (debug)
X (void) fprintf(stderr, "just finished reading sys file\n");
X return status;
X}
X
X/*
X * Is it okay to send the article corresponding to hdrs to sys,
X * given flags (derived from sys) and rmt?
X *
X * Never send to this host, nor any host named in Path:.
X * Newsgroups must match sys's subscription list.
X * Also, Distribution: must match sys's distribution list.
X * If L flag is on, must be a local posting.
X *
X * TODO: implement Ln restriction:
X * forward articles generated within sysp->sy_lochops hops of here.
X * TODO: implement exclusions by site, from sy_excl (can be NULL).
X */
Xstatic int
Xoktransmit(hdrs, sys, flags, rmt, exclude)
Xregister struct headers *hdrs;
Xregister struct system *sys;
Xint flags, rmt;
Xchar *exclude; /* no copy to him */
X{
X return (!(flags&FLG_LOCAL) || !rmt) && /* local & */
X !STREQ(hostname(), sys->sy_name) && /* not to ME & */
X (exclude == NULL || !STREQ(exclude, sys->sy_name)) && /* not excluded & */
X (hdrs->h_path == NULL || !hostin(hdrs->h_path, sys->sy_name)) && /* not been here & */
X ngmatch(sys->sy_ngs, hdrs->h_ngs) && /* ngs match & */
X /* RFC 850 is wrong, yea, verily: Distribution:s are *not* patterns */
X ngmatch(sys->sy_distr, hdrs->h_distr); /* distrs match! */
X}
X
X/*
X * send the article denoted by hdrs to the system denoted by sys.
X */
XSTATIC int /* status */
Xejaculate(hdrs, sys, fno) /* kick the article to its reward */
Xregister struct headers *hdrs;
Xregister struct system *sys;
Xint fno;
X{
X int status = ST_OKAY;
X char filename[MAXLINE];
X char *fullname;
X
X /* can't use hdrs->h_tmpf because we need a permanent name */
X first(hdrs->h_files, filename);
X mkfilenm(filename);
X (void) printf(" %s", sys->sy_name); /* logging */
X if (debug)
X (void) fprintf(stderr, "transmitting %s to %s\n",
X hdrs->h_msgid, sys->sy_name);
X
X /* must supply a full pathname to the outside world */
X fullname = fullspoolfile(filename);
X if (sys->sy_flags&(FLG_BATCH|FLG_SZBATCH))
X status |= trbatch(sys, hdrs, fullname, fno);
X else
X status |= trcmd(sys, fullname);
X return status;
X}
X
X/*
X * Append filename to sys->sy_cmd. fno is the ordinal number of this sys line.
X * If fno is low enough, use the tfs cache of batch file descriptors.
X */
Xint
Xtrbatch(sys, hdrs, filename, fno)
Xstruct system *sys;
Xstruct headers *hdrs;
Xchar *filename;
Xregister int fno;
X{
X register int status = 0;
X char *batfile = sys->sy_cmd;
X
X if (fno >= NOPENTFS) { /* not cachable */
X register FILE *batchf = fopenclex(batfile, "a");
X
X if (batchf == NULL) {
X warning("can't open batch file %s", batfile);
X status |= ST_DROPPED;
X } else {
X status |= trappend(batchf, sys, hdrs, filename);
X if (fclose(batchf) == EOF)
X status = fulldisk(status|ST_DROPPED, batfile);
X }
X } else { /* cachable */
X if (tfs[fno] == NULL) { /* closed */
X tfs[fno] = fopenclex(batfile, "a");
X if (tfs[fno] == NULL) { /* didn't open */
X register int openf;
X
X /*
X * Assume open failed due to lack of file
X * descriptors. Find an open one and close it,
X * then retry the open. Honk at someone too?
X */
X for (openf = 0; openf < NOPENTFS; openf++)
X if (tfs[openf] != NULL) /* open */
X break;
X if (openf < NOPENTFS && tfs[openf] != NULL) {
X if (fclose(tfs[openf]) == EOF)
X status = fulldisk(status|ST_DROPPED,
X "some batch file");
X tfs[openf] = NULL; /* mark closed */
X tfs[fno] = fopenclex(batfile, "a");
X }
X }
X }
X if (tfs[fno] == NULL) { /* still closed! */
X warning("can't open batch file %s", batfile);
X status |= ST_DROPPED;
X } else
X status |= trappend(tfs[fno], sys, hdrs, filename);
X }
X return status;
X}
X
Xstatic int
Xtrappend(fp, sys, hdrs, name) /* write name\n on fp */
Xregister FILE *fp;
Xregister struct system *sys;
Xregister struct headers *hdrs;
Xchar *name;
X{
X int status = ST_OKAY;
X
X if (fputs(name, fp) == EOF) /* append to batch file */
X status = fulldisk(status|ST_DROPPED, "some batch file");
X /* for Henry's new batcher */
X if (sys->sy_flags&FLG_SZBATCH &&
X fprintf(fp, " %ld", hdrs->h_charswritten) == EOF)
X status = fulldisk(status|ST_DROPPED, "some batch file");
X /* don't check putc return value for portability; use ferror */
X (void) putc('\n', fp);
X (void) fflush(fp); /* for crash-proofness */
X if (ferror(fp))
X status = fulldisk(status|ST_DROPPED, "some batch file");
X return status;
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 */
Xint
Xtrcmd(sys, filename)
Xstruct system *sys;
Xchar *filename;
X{
X int status = ST_OKAY, exitstat;
X char *cmd;
X char *syscmd = sys->sy_cmd;
X
X cmd = emalloc((unsigned)STRLEN("PATH=") + STRLEN(STDPATH) + STRLEN(" <") +
X strlen(filename) + STRLEN(" ") + strlen(syscmd) +
X strlen(filename) + 1);
X (void) strcpy(cmd, "PATH=");
X (void) strcat(cmd, STDPATH);
X (void) strcat(cmd, " <");
X /*
X * redirect stdin to prevent consuming my stdin & so cmd's stdin
X * is filename by default.
X */
X (void) strcat(cmd, filename);
X (void) strcat(cmd, " ");
X (void) sprintf(cmd+strlen(cmd), syscmd, filename);
X exitstat = system(cmd);
X if (exitstat != 0) {
X extern char *progname;
X
X status |= ST_DROPPED;
X (void) fprintf(stderr, "%s: `%s' returned exit status 0%o\n",
X progname, cmd, exitstat);
X }
X free(cmd);
X return status;
X}
X
Xtrclose()
X{
X register int fno;
X
X for (fno = 0; fno < NOPENTFS; fno++)
X if (tfs[fno] != NULL) {
X (void) fclose(tfs[fno]);
X tfs[fno] = NULL;
X }
X}
X
X/*
X * Return true iff host appears in s, with no characters from the alphabet
X * of legal hostname characters immediately adjacent.
X * This function is a profiling hot spot, so it has been optimised.
X */
Xint
Xhostin(s, host)
Xregister char *s, *host;
X{
X register int hostlen = strlen(host);
X register int ch; /* use by hostchar macro */
X
X/* If c is NUL, hostchar will be false, so don't test (optimisation: ==). */
X#define nothostchar(c) (!hostchar(c) /* || (c) == '\0' */ ) /* ! or EOS */
X/*
X * True if c can be part of a hostname. RFC 850 allows letters, digits, periods,
X * and hyphens and specifically disallows blanks. False may mean c is NUL.
X */
X#define hostchar(c) (ch = (c) , \
X (isascii(ch) && (isalnum(ch) || (ch) == '.' || (ch) == '-')))
X
X /*
X * Special case: match host!path or host.
X */
X if (STREQN(s, host, hostlen) && nothostchar(s[hostlen]))
X return YES;
X /*
X * Match path2!host!path or path2!host.
X */
X while (*s != '\0')
X if (hostchar(s[0])) /* can't start after here */
X ++s;
X else if ((++s, STREQN(s, host, hostlen)) &&
X nothostchar(s[hostlen]))
X return YES;
X return NO;
X}
END_OF_FILE
if test 7923 -ne `wc -c <'rnews/transmit.c'`; then
echo shar: \"'rnews/transmit.c'\" unpacked with wrong size!
fi
# end of 'rnews/transmit.c'
fi
echo shar: End of archive 10 \(of 14\).
## End of shell archive.
exit 0