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 '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 X#include X#include X#include /* to make locking safe */ X#include 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 X#include X#include 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