Vnews part 3 (of 7)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Fri Jan 25 23:36:33 AEST 1985


# From: ka at hou3c
#
# Welcome to vnews release 2.11-B 1/17/85.
# This is part 3 out of 7.
# Feed me into sh (NOT csh).

if test ! -d lib
then	mkdir lib
fi

cat > lib/gethead.c <<\!E!O!F!
#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#include "defs.h"
#include "arthead.h"


static char *hdname[NHDNAME + 1] = {
      "Relay-Version",
      "Posting-Version",
      "Path",
      "From",
      "Newsgroups",
      "Subject",
      "Message-ID",
      "Date",
      "Article-I.D.",
      "Posted",
      "Date-Received",
      "Expires",
      "References",
      "Control",
      "Sender",
      "Reply-To",
      "Followup-To",
      "Distribution",
      "Organization",
      "Lines",
      "Keywords",
      "Approved",
      "Summary",
      "Priority",
      0
};



FILE *
gethead(hp, fp)
      register struct arthead *hp ;
      FILE *fp ;
      {
      char line[LBUFLEN] ;
      register char *p ;
      register char **pp ;
      register int i ;
      char *spnext ;
      int spleft ;
      int spnum ;
      int unreccnt ;
      extern char FULLSYSNAME[] ;
      char *index(), *ckmalloc() ;
      char *hfgets() ;

      for (pp = &hp->h_from, i = NHDNAME + NUNREC ; --i >= 0 ; *pp++ = 0) ;
      unreccnt = 0 ;
      spleft = 0 ;
      spnum = 0 ;
      while (hfgets(line, LBUFLEN, fp) != NULL && line[0] != '\n') {
            if ((p = index(line, ':')) == NULL)
                  goto bad ;
            *p = '\0' ;
            if (index(line, ' ') != NULL)	/* this test unnecessary */
                  goto bad ;
            for (pp = hdname ; *pp ; pp++) {
                  if (**pp == line[0] && strcmp(*pp, line) == 0)
                        break ;
            }
            if (*pp) {
                  while (*++p == ' ') ;
                  if (pp == hdname + 2) {	/* Path: */
                        if (prefix(p, FULLSYSNAME)
                         && index(NETCHRS, p[i = strlen(FULLSYSNAME)]))
                              p += i + 1 ;
                  }
                  pp = &hp->h_relayversion + (pp - hdname) ;
            } else {
                  *p = ':' ;
                  p = line ;
                  if (unreccnt >= NUNREC)
                        continue ;		/* ignore if won't fit */
                  pp = &hp->h_unrec[unreccnt++] ;
            }
            nstrip(p) ;
            i = strlen(p) + 1;
            if (i > spleft) {
                  if (spnum >= 8)
                        xerror("Out of header space") ;
                  if (hp->h_space[spnum] == NULL)
                        hp->h_space[spnum] = ckmalloc(H_SPACE) ;
                  spnext = hp->h_space[spnum++] ;
                  spleft = H_SPACE ;
            }
            *pp = spnext ;
            strcpy(spnext, p) ;
            spnext += i ;
            spleft -= i ;
      }
      if (hp->h_from != NULL && hp->h_path != NULL && hp->h_subdate != NULL && hp->h_ident != NULL)
            return fp ;
bad:
      return NULL ;
}
!E!O!F!

cat > lib/getopt.c <<\!E!O!F!
/*
 * getopt - get option letter from argv
 * This version of getopt was written by Henry Spencer
 * and is in the public domain.
 */

#include <stdio.h>
#include "config.h"

char	*optarg;	/* Global argument pointer. */
int	optind = 0;	/* Global argv index. */

static char	*scan = NULL;	/* Private scan pointer. */

extern char	*index();

int
getopt(argc, argv, optstring)
int argc;
char *argv[];
char *optstring;
{
	register char c;
	register char *place;

	optarg = NULL;

	if (scan == NULL || *scan == '\0') {
		if (optind == 0)
			optind++;
	
		place = argv[optind];
		if (optind >= argc || place[0] != '-' || place[1] == '\0')
			return(EOF);
		optind++;
		if (strcmp(place, "--")==0) {
			return(EOF);
		}
	
		scan = place + 1;
	}

	c = *scan++;
	place = index(optstring, c);

	if (place == NULL || c == ':') {
		fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
		return('?');
	}

	place++;
	if (*place == ':') {
		if (*scan != '\0') {
			optarg = scan;
			scan = NULL;
		} else {
			if ((optarg = argv[optind]) == NULL) {
				fprintf(stderr, "%s: no arg for -%c option\n", c);
				return('?');
			}
			optind++;
		}
	}

	return(c);
}
!E!O!F!

cat > lib/getuser.c <<\!E!O!F!
/*
 * Get the user's name and home directory.
 */

#include "config.h"
#include <stdio.h>


getuser() {
      extern char *username, *userhome;
      extern char *getenv();

#if USGREL > 6
      username = getenv("LOGNAME") ;
#else
      username = getenv("USER") ;
#endif
      if ((userhome = getenv("HOME")) == NULL)
            userhome = getenv("LOGDIR") ;		/* PWB??? */

      /* if not in environment, get from /etc/passwd file */
      if (username == NULL || userhome == NULL)
            pgetuser() ;
}
!E!O!F!

cat > lib/gfopen.c <<\!E!O!F!
#include <stdio.h>
#include "defs.h"
#include "libextern.h"

FILE *ngfp ;
int maxng ;


gfopen() {
      char buf[FPATHLEN] ;

      sprintf(buf, "%s/groupfile", LIB);
      if ((ngfp = fopen(buf, "r")) == NULL
       && (sleep(2), ngfp = fopen(buf, "r")) == NULL)
            xerror("can't open newsgroup file") ;
      if (fgets(buf, sizeof buf, ngfp) == NULL)
            xerror("newsgroup file is empty") ;
      maxng = atoi(buf) ;
}


gfclose() {
      fclose(ngfp) ;
      ngfp = NULL ;
}
!E!O!F!

cat > lib/hash.c <<\!E!O!F!
/*
 * Hashing function.
 * First arg is the string, second is the number of elements in the table.
 * Currently, we use a CRC.
 */

int
hash(string, nelem)
      char *string ;
      {
      register unsigned char *p ;
      register int h ;

      h = 0 ;
      for (p = string ; *p ; ) {
            h = (long)(((long)h << 8) + *p++) % nelem ;
      }
      return h ;
}
!E!O!F!

cat > lib/hfgets.c <<\!E!O!F!
#include <stdio.h>

/*
 * hfgets is like fgets, but deals with continuation lines.
 * It also ensures that even if a line that is too long is
 * received, the remainder of the line is thrown away
 * instead of treated like a second line.
 */

char *
hfgets(buf, len, fp)
      char *buf;
      int len;
      register FILE *fp;
      {
      register int c;
      register char *cp;

      cp = fgets(buf, len, fp);
      if (cp == NULL || *cp == '\n')
            return cp;

      c = strlen(cp);
      len -= c;
      cp += c - 1;
      if (*cp != '\n') {
            /* Line too long - part read didn't fit into a newline */
            while ((c = getc(fp)) != '\n' && c != EOF);
      }
      while ((c = getc(fp)) == ' ' || c == '\t') {	/* for each cont line */
            /* Continuation line. */
            while ((c = getc(fp)) == ' ' || c == '\t');	/* skip white space */
            if (--len > 0)
                  *cp++ = ' ';
            do {
                  if (--len > 0)
                        *cp++ = c ;
            } while ((c = getc(fp)) != '\n' && c != EOF) ;
      }
      *cp++ = '\n';
      *cp++ = '\0';
      if (c != EOF)		/* Bug in Apollo UN*X */
            ungetc(c, fp);	/* push back first char of next header */
      return buf;
}
!E!O!F!

cat > lib/hfree.c <<\!E!O!F!
/*
 * Free up the storage used by a header.
 */

#include <stdio.h>
#include <sys/types.h>
#include "arthead.h"


hfree(hp)
      struct arthead *hp ;
      {
      register int i ;
      register char **p ;

      for (i = 8, p = hp->h_space ; --i >= 0 ; p++) {
            if (*p != NULL) {
                  free(*p) ;
                  *p = NULL ;
            }
      }
}
!E!O!F!

cat > lib/hxchg.c <<\!E!O!F!
/*
 * Exchange two headers.
 */

#include <stdio.h>
#include <sys/types.h>
#include "arthead.h"

hxchg(hp1, hp2)
      struct arthead *hp1, *hp2 ;
      {
      register char *p, *q ;
      register char c ;
      register int i ;

      p = (char *)hp1, q = (char *)hp2 ;
      for (i = sizeof(struct arthead) ; --i >= 0 ; )
            c = *p, *p++ = *q, *q++ = c ;
}
!E!O!F!

cat > lib/index.c <<\!E!O!F!
/*
 * Return the ptr in sp at which the character c appears;
 * NULL if not found
 *
 * These are the v7 index and rindex routines.  The makefile does not
 * place them it the library because every version of UN*X that is
 * supported by this release of netnews has these routines (possibly
 * named strchr/strrchr).  If you are running V6, use these routines.
 */

char *
index(sp, c)
register char *sp, c;
{
	do {
		if (*sp == c)
			return(sp);
	} while (*sp++);
	return(NULL);
}

/*
 * Return the ptr in sp at which the character c last
 * appears; NULL if not found
 */

char *
rindex(sp, c)
register char *sp, c;
{
	register char *r;

	r = NULL;
	do {
		if (*sp == c)
			r = sp;
	} while (*sp++);
	return(r);
}
!E!O!F!

cat > lib/isadmin.c <<\!E!O!F!
#include <stdio.h>
#include "defs.h"
#ifndef ROOTID
#include <pwd.h>
#endif


/*
 * Return 1 if the user is the superuser or the netnews administrator.
 */
isadmin() {
      int uid ;
#ifndef ROOTID
      struct passwd *pw ;
#endif

      if ((uid = getuid()) == 0)		/* superuser */
            return 1 ;
#ifdef ROOTID
      return uid == ROOTID ;
#else
      return (pw = getpwnam(ADMIN)) != NULL && pw->pw_uid == uid ;
#endif
}


/* The following code is better for backward compatability, but
   too complex for my liking:

      (*
       * No hardwired administrator ID, so we have to do this the
       * hard way.  First we find the person to be notified.  If
       * that fails, we try the user HOME.
       *)

#ifdef NOTIFY
      char notify[64] ;
      char fname[64] ;
      FILE *nfd ;

      sprintf(fname, "%s/notify", LIB);
      nfd = fopen(fname, "r");
      if (nfd == NULL)
            strcpy(notify, NOTIFY) ;
      else if (fscanf(nfd, "%s", TELLME) == EOF)
            goto nonotify;
      if ((pw = getpwnam(notify)) == NULL)
            goto nonotify;
      return pw->pw_uid == uid;      
#endif

nonotify:
#ifdef HOME
      return (pw = getpwnam(HOME)) != NULL && pw->pw_uid == uid;
#else
      return 0;
#endif
#endif ROOTID
}
*/
!E!O!F!

cat > lib/isre.c <<\!E!O!F!
/*
 * Check for "Re: " at beginning of string.
 */

isre(t)
      register char *t ;
      {
      if (t == 0)		/* delete this later */
            return 0 ;
      if (strncmp(t, "Re:", 3) == 0
       || strncmp(t, "re:", 3) == 0
       || strncmp(t, "RE:", 3) == 0)
            return 1 ;
      return 0 ;
}
!E!O!F!

cat > lib/launder.c <<\!E!O!F!
/*
 * nglist is the list of newsgroups in an article we want to follow up.
 * Do any special fascist processing to prevent certain kinds of followups.
 * In this case, there are two things we want to do:
 *	All followups to "net.general" are fed to "net.followup".
 *	However, if "net.general" is mentioned along with "net.news.group",
 *		just remove the net.general.
 */

#include "config.h"

char *index();

launder(nglist)
char	*nglist;
{
	char	*cp;
	char	outbuf[128];
	int	seen_group = 0;

	for (cp = index(nglist, 'n'); cp; cp = index(cp + 1, 'n'))
		if (strncmp("news.group", cp, 10) == 0)
			seen_group++;
	for (cp = index(nglist, 'n'); cp; cp = index(cp + 1, 'n'))
		if (strncmp("net.general", cp, 11) == 0) {
			/* 11 = strlen("net.general") */
			strcpy(outbuf, cp + 11);
			if (!seen_group) {
				strcpy(cp, "net.followup");
				cp += 12;  /* 12 = strlen("net.followup") */
			}
			if (cp[-1] == ',' && outbuf[0] == ',')
				cp--;
			strcpy(cp, outbuf);
		}
}
!E!O!F!

cat > lib/lcase.c <<\!E!O!F!
#include <ctype.h>

#ifdef _tolower
#define tolower(c) _tolower(c)
#endif


lcase(s)
register char *s;
{
	register char *ptr;

	for (ptr = s; *ptr; ptr++)
		if (isupper(*ptr))
			*ptr = tolower(*ptr);
}
!E!O!F!

cat > lib/letter <<\!E!O!F!
To: cbosgd!uucp-news
Subject: Vnews status

I have done quite a bit to the vnews internals.

Vnews now generates the article index for a newsgroup using a file
called artfile instead of openning each article in the newsgroup;
this makes entering a new newsgroup faster.  Currently records
are added to artfile by a program called afinsert which is invoked
from the sys file.  This requires a mod to inews to pass the history
line to afinsert.  There is a program called afbuild which rebuilds
artfile from the history file.  Since afbuild has to open all the
articles in the spool file, it takes about as long to run as the
expire program does, which I hope is acceptable.  Of course, expire
could be rewritten to deal with both artfile and the history file
at some point.

I have changed the vnews source around quite a bit.  I have squeezed
it to the point where it should fit on a PDP-11.  One problem I faced
was that the files funcs.c and rfuncs.c contained a number of routines
that were not needed for netnews, so vnews currently does not share
any source code with the rest of netnews.  What I have done, however,
is to take many of the vnews routines and put them in a library called
rlib.a so that they can be used by other netnews programs.  The file
/usr/local/src/cmd/news/lib/routines.doc on cbosgd contains a copy of
the documentation on the routines in rlib.a.  I can mail a copy to
this list if people don't have access to cbosgd.

The library comes with a shell program called setup to handle compila-
tion options.  This is hopefully a simpler approach than the localize.sh
stuff that comes with release 2.10.  (For example, if you do not specify
which version of UNIX you are running under, setup figures it out.)
Setup creates files named config.h and newsdefs.h, which specify the
oeprating system and other news parameters, respectively.  It also
creates a file called makedefs, which can be prepended to a makefile or
shell procedure.  Setup currently does not define everything that pro-
grams like inews need, but it could be expanded without too much difficulty.

The "ud" (unsibscribe to discussion) command has been installed in vnews
but needs a little work.  The rest of vnews appears to be in pretty good
shape at this point.

I am not going to be able to do much on vnews for the next several weeks,
but I would like to try to get a release of netnews out sometime in the
forseeable future.  How are other pieces coming?
				Kenneth Almquist
!E!O!F!

cat > lib/libextern.h <<\!E!O!F!
/*
 * Extern variables defined by library routines.
 */

extern char LIB[];		/* /usr/lib/news */
extern char SPOOL[];		/* spool directory */
extern char FULLSYSNAME[];	/* system name */
extern char DOMAIN[];		/* append to FULLSYSNAME to get domain name */
extern char MAILPARSER[];	/* recmail program */
extern char XINEWS[];		/* inews program */
extern char CAESAR[];		/* caesar decryption program */
extern char newsrc[];		/* name of .newsrc file */
extern char *username;		/* user's login name */
extern char *userhome;		/* user's login directory */
extern char bfr[];		/* general purpose buffer */
!E!O!F!

cat > lib/logdir.c <<\!E!O!F!
/*
 *	UNIX shell - logdir routine
 *
 *	Joe Steffen
 *	Bell Telephone Laboratories
 *
 *	This routine does not use the getpwent(3) library routine
 *	because the latter uses the stdio package.  The allocation of
 *	storage in this package destroys the integrity of the shell's
 *	storage allocation.
 *
 *	Modified 2/82 by DJ Molny
 *
 *	This routine now implements name cacheing, so multiple requests
 *	for the same logdir do not result in multiple open/reads of
 *	/etc/passwd.  If the previous request was successful and the name
 *	is the same as the last request, the same login directory is returned.
 */

#define	BUFSIZ	160

static char line[BUFSIZ+1];

char *
logdir(name)
char *name;
{
	int	pwf;
	static char lastname[BUFSIZ+1];
	static char lastdir[BUFSIZ+1];
	register char *p;
	register int i, j;
	char *field();
	
	if (*lastdir && !strcmp(lastname,name))		/* djm */
		return(lastdir);

	strcpy(lastname,name);			/* djm */
	strcpy(lastdir,"");			/* djm */

	/* attempt to open the password file */
	if ((pwf = open("/etc/passwd", 0)) == -1)
		return(0);
		
	/* find the matching password entry */
	do {
		/* get the next line in the password file */
		i = read(pwf, line, BUFSIZ);
		for (j = 0; j < i; j++)
			if (line[j] == '\n')
				break;
		/* return a null pointer if the whole file has been read */
		if (j >= i)
			return(0);
		line[++j] = 0;			/* terminate the line */
		lseek(pwf, (long) (j - i), 1);	/* point at the next line */
		p = field(line);		/* get the logname */
	} while (strcmp(name, line) != 0);
	close(pwf);
	
	/* skip the intervening fields */
	p = field(p);
	p = field(p);
	p = field(p);
	p = field(p);
	
	/* return the login directory */
	field(p);
	strcpy(lastdir,p);			/* djm */
	return(p);
}

static char *
field(p)
register char *p;
{
	while (*p && *p != ':')
		++p;
	if (*p) *p++ = 0;
	return(p);
}
!E!O!F!

cat > lib/lookup.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"


/*
 * Look up an article using the article id.
 */

DPTR
lookart(id, a)
      char *id ;
      struct artrec *a ;
      {
      DPTR dp ;

      dp = readptr(hashid(id)) ;
      while (dp != DNULL) {
            readrec(dp, a) ;
            if (equal(id, a->a_ident)) {
                  /*printf("lookart %s succeeded\n", id) ;	/*DEBUG*/
                  return dp ;
            }
            dp = a->a_idchain ;
      }
      /*printf("lookart %s failed\n", id) ;			/*DEBUG*/
      return DNULL ;
}
!E!O!F!

cat > lib/makehimask.c <<\!E!O!F!
/*
 * Unless the newsgroup is asked for explicitly, delete it from the
 * subscription list.
 */

#include <stdio.h>
#include "config.h"
#include "str.h"

makehimask(sublist, ngrp)
      register char *sublist ;
      char *ngrp ;
      {
      char c, *p ;
      char *index() ;

      for (;;) {
            if (prefix(sublist, ngrp)
             && (c = sublist[strlen(ngrp)]) == '\0' || c == ',')
                  return ;
            if ((p = index(sublist, ',')) == NULL)
                  break ;
            sublist = p + 1 ;
      }
      while (*sublist)
            sublist++ ;
      if (sublist[-1] != ',')
            *sublist++ = ',' ;
      *sublist++ = '!' ;
      scopy(ngrp, sublist) ;
}
!E!O!F!

cat > lib/mkconfig <<\!E!O!F!
#!/bin/sh
if test "$1" = ""
then	echo "Usage: mkconfig dir"
	echo "where dir is the directory containing the 2.10.1 source"
	exit 1
fi

if test ! -d $1
then	echo "mkconfig: $1: no such directory"
	exit 1
fi

(
sed -n	-e 's/^NEWSUSR *= */newsusr /p' \
	-e 's/^NEWSGRP *= */newsgrp /p' \
	-e 's/^LIBDIR *= */lib /p' \
	-e 's/^BINDIR *= */bin /p' \
	-e 's/^SPOOLDIR *= */spool /p' \
		$1/Makefile
sed -n	-e 's/^#define[ 	]NOTIFY[ 	]*"\([^"]*\)".*/notify \1/p' \
	-e 's/^#define[ 	]MYDOMAIN[ 	]*"\([^"]*\)".*/mydomain \1/p' \
	-e 's/^#define[ 	]MYORG[ 	]*"\([^"]*\)".*/myorg \1/p' \
	-e 's/^#define[ 	]DFLTSUB[ 	]*"\([^"]*\)".*/dfltsub \1/p' \
	-e 's/^#define[ 	]ADMSUB[ 	]*"\([^"]*\)".*/admsub \1/p' \
	-e 's/^#define[ 	]UNAME.*/uname/p' \
	-e 's/^#define[ 	]GHNAME.*/ghname/p' \
	-e 's/^\/\*[ 	]*#define[ 	]INTERNET.*/internet no/p' \
	-e 's/^\/\*[ 	]*#define[ 	]V7MAIL.*/v7mail no/p' \
	-e 's/^#define[ 	]X[ 	]*"\([^"]*\)".*/x \1/p' \
		$1/defs.h
rootid=`sed -n -e 's/^#define[ 	]ROOTID[ 	]*\([0-9]*\).*/\1/p' $1/defs.h`
rootname=`sed -n -e "/^[^:]*:[^:]*:$rootid:/s/:.*//p" /etc/passwd | sed '2,$d'`
if test "$rootname" != ""
then	echo admin $rootname
fi
) | sed	-e '/lib \/usr\/lib\/news/d' \
	-e '/bin \/usr\/bin/d' \
	-e '/spool \/usr\/spool\/news/d' \
	-e '/dfltsub all/d' \
	-e '/admsub general,all\.announce/d' \
		> config
!E!O!F!
chmod +x lib/mkconfig

cat > lib/mypathinit.c <<\!E!O!F!
#include <stdio.h>
#include "config.h"
#include "defs.h"


#ifdef HOME
char SPOOL[FPATHLEN];
char LIB[FPATHLEN-15];
char CAESAR[FPATHLEN];
#else
char SPOOL[] = SPOOLDIR";
char LIB[] = LIBDIR";
char CAESAR[] = LIBDIR/caesar";
#endif HOME
#ifdef MYNAME
char FULLSYSNAME[] = MYNAME;
#else
#ifdef UNAME
#include <sys/utsname.h>
char FULLSYSNAME[9];
#else
char FULLSYSNAME[20];
#endif UNAME
#endif MYNAME
char DOMAIN[] = MYDOMAIN;
#ifdef SENDMAIL
char MAILPARSER[] = SENDMAIL;
#else
#ifdef HOME
char MAILPARSER[FPATHLEN];
#else
char MAILPARSER[] = LIBDIR/recmail";
#endif HOME
#endif SENDMAIL
char XINEWS[] = INEWS;
static char verpfx[4] = {'@', '(', '#', ')'};
char NEWS_VERSION[] = news_version;


pathinit() {
#ifdef HOME
      /* Relative to the home directory of user HOME */
      sprintf(SPOOL, "%s/%s", logdir(HOME), SPOOLDIR");
      sprintf(LIB, "%s/%s", logdir(HOME), LIBDIR");
#endif
      if (strlen(LIB) >= FPATHLEN-15)
            xerror("LIB too long");
      if (strlen(SPOOL) > FPATHLEN)
            xerror("SPOOL too long");
#if defined(HOME) && !defined(SENDMAIL)
      sprintf(MAILPARSER, "%s/recmail", LIB);
#endif
#ifndef MYNAME
#ifdef UNAME
      {
            struct utsname ubuf;
            uname(&ubuf);
            strcpy(FULLSYSNAME, ubuf.nodename);
      }
#else
#ifdef GHNAME
      gethostname(FULLSYSNAME, sizeof FULLSYSNAME);
#else
      whoami();
#endif GHNAME
#endif UNAME
#endif MYNAME
}


#if !defined(UNAME) && !defined(GHNAME) && !defined(MYNAME)
#define	HDRFILE "/usr/include/whoami.h"

whoami()
{
	char buf[BUFSIZ];
	FILE *fd;
	
	fd = fopen(HDRFILE, "r");
	if (fd == NULL) {
		fprintf(stderr, "Cannot open %s\n", HDRFILE);
		exit(1);
	}
	
	for (;;) {	/* each line in the file */
		if (fgets(buf, sizeof buf, fd) == NULL) {
			fprintf(stderr, "no sysname in %s\n", HDRFILE);
			fclose(fd);
			exit(2);
		}
		if (sscanf(buf, "#define sysname \"%[^\"]\"", FULLSYSNAME) == 1) {
			fclose(fd);
			return;
		}
	}
}
#endif
!E!O!F!

cat > lib/ndir.h <<\!E!O!F!
#if BSDREL >= 42
#include <sys/dir.h>
#else

#if defined(pdp11) || USGREL == 30
#define DIRBLKSIZ 512
#else
#define DIRBLKSIZ 1024
#endif

struct	direct {
	long	d_ino;			/* inode number of entry */
	short	d_namlen;		/* length of string in d_name */
	char	d_name[16];		/* name must be no longer than this */
};

/*
 * Definitions for library routines operating on directories.
 */
typedef struct {
	int	dd_fd;
	int	dd_nleft;
	char	*dd_nextc;
	char	dd_buf[DIRBLKSIZ];
} DIR;

#ifndef NULL
#define NULL 0
#endif
extern	DIR *opendir();
extern	struct direct *readdir();
extern	void closedir();
#endif
!E!O!F!

cat > lib/newer.c <<\!E!O!F!
/*
 * Program to determine whether file2 needs to be updated from file1.
 * that another one.  Usage:  newer file1 file2
 * Returns:
 *	1  file2 is newer than file1.
 *	0  file2 is older than file1 or one of the files does not exist.
 *	8  arg count.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>


main(argc, argv)
      char **argv ;
      {
      struct stat st1, st2;

      if (argc != 3) {
            fputs("Usage: newer file1 file2\n", stderr);
            exit(8);
      }
      if (stat(argv[1], &st1) < 0) {
            fputs("newer: can't stat ", stderr);
            fputs(argv[1], stderr);
            fputs("\n", stderr);
            exit(0);			/* force update attempt */
      }
      if (stat(argv[2], &st2) < 0 || st2.st_mtime < st1.st_mtime)
            exit(0);
      exit(1);				/* up to date */
}
!E!O!F!

cat > lib/newsrc.doc <<\!E!O!F!
1.  FORMAT OF .NEWSRC FILE

Since the .newsrc file is shared between a number of news reading
programs, care should be taken to allow for the evolution of this file.
Lines in the file fall into several types.

1) Newsgroups lines.

A newsgroup line consists of a newsgroup name, followed by subscription
indication, followed by a flags field, followed by a list of article
numbers.  A line should not be considered to be a newsgroup line unless
the first character of the line is a lowercase letter and the
subscription indication immediately follows the newsgroup name.

The subscription indication consists of a ':' to indicate that the user
subscribes to this group, or an '!' to indicate that the user does not
subscribe to this group.

The flags field is currently unused but is reserved for future use.  It
consists of a word not beginning with a digit, and terminated by a
space.

The list of article numbers should not have any spaces in it.  It
consists of a comma separated list of articles which have been read.  A
range of articles may be indicated by two numbers separated by a minus
sign.

Ideally, newsgroups should be presented in the order in which they
appear in the .newsrc file, and newsgroups not appearing in the .newsrc
file should be shown last.  At any rate, the order of newsgroups in
the .newrc file should not be disturbed.

The crash recovery code writes updated lines to the end of the .newsrc
file as they are modified.  Therefore, the last entry in the .newsrc
file should be believed.  However, the first occurence of a newsgroup
in the .newsrc file gives it's display position.

2)  Unsubscribed discussions.

Any line beginning with a '<' contains a message-ID of a discussion
which has been unsubscribed to.  Followups to such articles should not
be shown.

3)  Comment lines.

All other lines should be treated as comment lines.  When the .newsrc
file is rewritten, comment lines should be output first.  2.10 deletes
certain comment lines; this is unacceptable.
!E!O!F!

cat > lib/newsrc.h <<\!E!O!F!
#define MAXUNSUB 200	/* max number discussion unsubscribed from */
#define MAXRCJUNK 128	/* maximum number of comment lines */

#define NG_UNSUB 01

struct ngentry {		/* newsgroup entry */
	char *ng_name;		/* name of newsgroup */
	char *ng_bits;		/* which articles have been read */
	struct ngentry *ng_next;/* next newgroup in .newsrc order */
	char ng_unsub;		/* set if we have unsubscribed */
};

#define ng_num(ngp)	((ngp) - ngtable)
#define numtong(num)	(&ngtable[num])
#ifdef DEBUG
#define isunread(i)	(i > 0 && i <= maxartno ? _isunread(i) : abort())
#define clrunread(i)	(i > 0 && i <= maxartno ? _clrunread(i) : abort())
#define setunread(i)	(i > 0 && i <= maxartno ? _setunread(i) : abort())
#define _isunread(i)	(bitmap[(i-1) >> 3] & (1 << (i-1) % 8))
#define _setunread(i)	(bitmap[(i-1) >> 3] |= (1 << (i-1) % 8))
#define _clrunread(i)	(bitmap[(i-1) >> 3] &= ~(1 << (i-1) % 8))
#else
#define isunread(i)	(bitmap[(i-1) >> 3] & (1 << (i-1) % 8))
#define setunread(i)	(bitmap[(i-1) >> 3] |= (1 << (i-1) % 8))
#define clrunread(i)	(bitmap[(i-1) >> 3] &= ~(1 << (i-1) % 8))
#endif


struct ngentry *findgroup();
struct ngentry *prevgrp(), *nextgrp();


extern struct ngentry ngtable[MAXGROUPS];
extern struct ngentry *curng, *firstng, *lastng;
extern int ndunsub;
extern long dunsub[MAXUNSUB];
extern char *rcjunk[MAXRCJUNK];
extern char **nextrcjunk;
extern int minartno;
extern int maxartno;
extern char *bitmap;
!E!O!F!

cat > lib/nextgrp.c <<\!E!O!F!
#include <stdio.h>
#include "defs.h"
#include "newsrc.h"


struct ngentry *
nextgrp(ngp)
	struct ngentry *ngp;
	{
	if (ngp == NULL)
		return firstng;
	else
		return ngp->ng_next;
}
!E!O!F!

cat > lib/ng.h <<\!E!O!F!
#define MAXNGNAME 32

#define G_MOD 01		/* indicates moderated or fa.all group */

struct ngrec {
      char  g_name[MAXNGNAME];	/* newsgroup name */
      short g_num;		/* newsgroup number */
      short g_flags;		/* various flags */
} ;

extern int maxng;		/* max value for g_num */
extern FILE *ngfp;

#define ALL_GROUPS(ngrec)	for (nginit() ; ngread(&(ngrec)) ; )
!E!O!F!

cat > lib/ngchain.c <<\!E!O!F!
/*
 * Routines step through all the elements of a newsgroup.
 * Called by the BNG macro.
 */

#include <stdio.h>
#include "af.h"


DPTR nglnext ;


DPTR
nglfirst(ngnum) {
      return nglnext = readptr(ngchain(ngnum)) ;
}


ARTNO
ngltest(ngnum, a)
      int ngnum ;
      struct artrec *a ;
      {
      register int i ;

      if (nglnext == DNULL)
            return -1 ;
      readrec(nglnext, a) ;
      for (i = 0 ; i < a->a_ngroups ; i++) {
            if (a->a_group[i].a_ngnum == ngnum) {
                  nglnext = a->a_group[i].a_ngchain ;
                  return a->a_group[i].a_artno ;
            }
      }
      xerror("bad newsgroup chain") ;
}
!E!O!F!

cat > lib/ngmatch.c <<\!E!O!F!
#include "defs.h"

/*
 * News group matching.
 *
 * nglist is a list of newsgroups.
 * sublist is a list of subscriptions.
 * sublist may have "meta newsgroups" in it.
 * All fields are NGDELIM separated,
 *
 * Currently implemented glitches:
 * sublist uses 'all' like shell uses '*', and '.' like shell '/'.
 * If subscription X matches Y, it also matches Y.anything.
 */
ngmatch(nglist, sublist)
register char *nglist, *sublist;
{
	register char *n, *s;
	register int rc;

	rc = 0;
	for (n = nglist; *n != '\0' && rc == 0;) {
		for (s = sublist; *s != '\0';) {
			if (*s != NEGCHAR)
				rc |= ptrncmp(s, n);
			else
				rc &= ~ptrncmp(s+1, n);
			while (*s && *s++ != NGDELIM);
		}
		while (*n && *n++ != NGDELIM);
	}
	return(rc);
}

/*
 * Compare two newsgroups for equality.
 * The first one may be a "meta" newsgroup.
 */
ptrncmp(ng1, ng2)
register char *ng1, *ng2;
{
	while (*ng1 && *ng1 != NGDELIM) {
		if (ng1[0]=='a' && ng1[1]=='l' && ng1[2]=='l') {
			ng1 += 3;
			while (*ng2 && *ng2 != NGDELIM && *ng2 != '.')
				if (ptrncmp(ng1, ng2++))
					return(1);
			return (ptrncmp(ng1, ng2));
		} else if (*ng1++ != *ng2++)
			return(0);
	}
	return (*ng2 == '\0' || *ng2 == '.' || *ng2 == NGDELIM);
}
!E!O!F!

cat > lib/nsavestr.c <<\!E!O!F!
/*
 * Make a copy of a string.  The copy cannot be freed.
 */

#include "str.h"

#define BLOCKSIZE 1024

static char *savearea;
static char *savep;

char *
nsavestr(s)
      char *s ;
      {
      int size = strlen(s) + 1;
      char *p ;
      char *ckmalloc() ;

      if (size > BLOCKSIZE)
            return savestr(s) ;
      if (savearea == 0 || savearea + BLOCKSIZE - savep < size) {
            nsaveclean() ;
            savearea = savep = ckmalloc(BLOCKSIZE) ;
      }
      scopy(s, p = savep) ;
      savep += size ;
      return p ;
}


/*
 * Release unused storage.
 */

nsaveclean() {
      char *realloc() ;
      if (savearea) {
            if (realloc(savearea, savep - savearea) != savearea)
                  xerror("realloc blew it") ;
      }
      savearea = 0 ;
}
!E!O!F!

cat > lib/nstrip.c <<\!E!O!F!
/*
 * Strip trailing newlines, blanks, and tabs from 's'.
 * Return 1 if newline was found, else 0.
 */
nstrip(s)
register char *s;
{
	register char *p;
	register int rc;

	rc = 0;
	p = s;
	while (*p)
		if (*p++ == '\n')
			rc = 1;
	while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
	*++p = '\0';
	return(rc);
}
!E!O!F!

cat > lib/openrc.c <<\!E!O!F!
/*
 * Open the .newsrc file, creating it if necessary.
 */

#include <stdio.h>
#include "defs.h"
#include "libextern.h"

char newsrc[64] ;

FILE *
openrc() {
	register char *myrc ;
	register FILE *rcfp ;
	static char READ[] = "r" ;
	char *getenv() ;

	if ((myrc = getenv("NEWSRC")) != NULL)
		strcpy(newsrc, myrc);
	else
		sprintf(newsrc, "%s/%s", userhome, NEWSRC);
	if ((rcfp = fopen(newsrc, READ)) == NULL) {
		newrc() ;
		if ((rcfp = fopen(newsrc, READ)) == NULL)
			xerror("Can't create .newsrc file") ;
	}
	return rcfp ;
}


newrc()
{
	register FILE *fp;
	char users[FPATHLEN];

	if (close(creat(newsrc, 0666))) {
		xerror("Cannot create %s", newsrc);
	}

	sprintf(users, "%s/users", LIB);
	if ((fp = fopen(users, "a")) != NULL) {
		fprintf(fp, "%s\n", username);
		fclose(fp);
		chmod(users, 0666);
	}
}
!E!O!F!

cat > lib/pgetuser.c <<\!E!O!F!
/*
 * Get user name and home directory.  This version always goes to the
 * password file, so it cannot be subverted by modifying the environment.
 */

#include <stdio.h>
#include <pwd.h>

char *username, *userhome;

pgetuser()
{
	register struct passwd *p;
	char *savestr();
	struct passwd *getpwuid();

	if ((p = getpwuid(getuid())) == NULL)
		xerror("Cannot get user's name");
	username = savestr(p->pw_name);
	userhome = savestr(p->pw_dir);
}
!E!O!F!

cat > lib/prefix.c <<\!E!O!F!
int
prefix(full, pref)
      register char *full, *pref ;
      {
      register char c ;

      while ((c = *pref++) != '\0')
            if (*full++ != c)
                  return 0 ;
      return 1 ;
}
!E!O!F!

cat > lib/prevgrp.c <<\!E!O!F!
#include <stdio.h>
#include "defs.h"
#include "newsrc.h"


struct ngentry *
prevgrp(ngp)
	struct ngentry *ngp;
	{
	register struct ngentry *p;

	if (ngp == NULL)
		return NULL;
	for (p = ngtable ; p->ng_next != ngp ; p++)
		if (p >= ngtable + MAXGROUPS - 1)
			return NULL;
	return p;
}
!E!O!F!

cat > lib/process.c <<\!E!O!F!
/*
 * process - process options for readnews
 */

static char *SccsId = "%W%	%G%";

#include <stdio.h>
#include <ctype.h>
#include "roptions.h"
#define TRUE 1
#define FALSE 0

int mode = UNKNOWN;


procopt(argv)
register char **argv;
{
      register int state = OPTION;
      register char *p;
      register struct optable *optpt;
      extern char *progname;
      char *savestr(), *realloc();

      /* loop once per arg. */

      while (*argv != NULL) {
          if (state == OPTION) {
                  if (**argv != '-') {
                        xerror("Bad option string \"%s\"", *argv);
                  }
                  while (*++*argv != '\0') {
                        for (optpt = options; optpt->optlet != '\0'; ++optpt) {
                              if (optpt->optlet == **argv)
                                    goto found;
                        }
                        /* unknown option letter */
                        fprintf(stderr, "Usage: %s [ -a [ date ]] [ -n newsgroups ] [ -t titles ] [ -lprxhfuM ]\n", progname);
                        fprintf(stderr, "\t[ -c [ ``mailer'' ]]\n\n");
                        fprintf(stderr, "       %s -s\n", progname);
                        exit(1);

                      found:;
                        if (mode != UNKNOWN && (mode&optpt->oldmode) == 0) {
                              xerror("Bad %c option", **argv);
                        }
                        if (mode == UNKNOWN)
                              mode = optpt->newmode;
                        optpt->flag = TRUE;
                        state = optpt->newstate;
                  }

                  argv++;            /* done with this option arg. */

          } else {

                  /*
                   * Pick up a piece of a string and put it into
                   * the appropriate buffer.
                   */
                  if (**argv == '-') {
                        state = OPTION;
                        continue;
                  }

                  p = optpt->buf;
                  if (state == STRING) {
                        if (optpt->buf != NULL)
                              free(optpt->buf);
                        optpt->buf = savestr(*argv);
                        state++;
                  } else {
                        if ((p = realloc(p, strlen(p) + strlen(*argv) + 2)) == NULL)
                              xerror("No space");
                        optpt->buf = p;
                        while (*p)  p++;
                        *p++ = optpt->filchar;
                        strcpy(p, *argv);
                  }
                  argv++;
            }
      }

      /*
       * At this point, we are done with vanilla flag processing.
       * Now deal with defaults and upward compatibility stuff.
       */
      return;
}


/*
 * Process a string containing options.  The string is broken up into
 * space separated options and then passed to procopt.
 */

#define MAXOPTS 63

procostr(rcbuf)
      char *rcbuf;
      {
      register char *ptr;
      char *rcline[MAXOPTS + 1];
      int line = -1;

      strcat(rcbuf, " \1");
      ptr = rcbuf;
      while (*++ptr)
            if (isspace(*ptr))
                  *ptr = '\0';
      for (ptr = rcbuf; ; ptr++) {
            if (!*ptr)
                  continue;
            if (*ptr == '\1')
                  break;
            if (++line >= MAXOPTS)
                  xerror("Too many options");
            rcline[line] = ptr;
            while (*ptr)
                  ptr++;
      }
      rcline[++line] = NULL;
      procopt(rcline);
}
!E!O!F!

cat > lib/read.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"
#include "libextern.h"		/* defines bfr */

long lseek() ;


/*
 * Read in an article record.
 */

readrec(dp, a)
      DPTR dp ;
      struct artrec *a ;
      {
      register int len ;
      int i ;
      register char *p ;
      register char *q ;
      char **pp ;

      /*printf("readrec %ld\n", (long)dp) ;		/*DEBUG*/
      if (lseek(affd, (long)dp, 0) < 0)
            xerror("seek %ld failed, readrec\n", dp) ;
      len = BSIZE - ((int)dp & (BSIZE - 1)) ;
      if (len <= 1 + A_WRTLEN + MAXNG * sizeof(struct artgroup))
#if BSIZE == 512
            len += BSIZE ;
#else
            len = 1 + sizeof(struct artrec) ;
#endif
      if ((len = read(affd, bfr, len)) < 1 + A_WRTLEN + MAXNG * sizeof(struct artgroup)) {
            printf("read returned %d\n", len) ;
            xerror("Bad read in readrec, %ld", dp) ;
      }
      if (bfr[0] != (char)A_PREFIX) {
            printf("len=%d, dp=%ld\n", len, (long)dp);
            fprintf(stderr, "bfr: %o %o %o %o %o\n", bfr[0], bfr[1],
bfr[2], bfr[3], bfr[4]);
            fprintf(stderr, "A_PREFIX = %o, (char)A_PREFIX = %o\n", A_PREFIX, (char)A_PREFIX);
            xerror("bad article pointer %ld", dp) ;
      }
      bcopy(bfr + 1, (char *)a, A_WRTLEN) ;
      bcopy(bfr + 1 + A_WRTLEN, (char *)a->a_group, i = a->a_ngroups * sizeof(*a->a_group)) ;
      pp = &a->a_ident ;
      p = bfr + 1 + A_WRTLEN + i ;
      q = a->a_space ;
      len -= 1 + A_WRTLEN + i ;
      i = (a->a_flags & A_DUMMY)? 1 : 4 + a->a_nkwords ;
      while (--i >= 0) {
            *pp++ = q ;
            do if (--len < 0) {
                  read(affd, bfr, 512) ;
                  len = 512 ;
                  p = bfr ;
               }
            while (*q++ = *p++) ;
      }
}



/*
 * Read a pointer from the article file
 */

DPTR
readptr(dp)
      DPTR dp ;
      {
      DPTR new ;

      if (lseek(affd, (long)dp, 0) < 0)
            xerror("seek %ld failed, readptr\n", dp) ;
      if (read(affd, (char *)&new, sizeof(new)) != sizeof(new))
            xerror("read failed, readptr(%ld)\n", dp) ;
      return new ;
}
!E!O!F!

cat > lib/readinrc.c <<\!E!O!F!
/*
 * Read in the .newsrc file and the newsgroups file.  If rcfp is NULL,
 * only the newsgroups file is read.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include "defs.h"
#include "artfile.h"
#include "ng.h"
#include "newsrc.h"
#include "libextern.h"
#include "str.h"

static int rcmode;		/* file modes for .newsrc file */
int rcreadok;			/* .newsrc file has been read */
int rcsaved;			/* .newsrc file has been backed up */
char *rcjunk[MAXRCJUNK];	/* comment lines in .newsrc file */
char **nextrcjunk = rcjunk;	/* first unused entry in rcjunk */
struct ngentry ngtable[MAXGROUPS];	/* newsgroup table */
int ndunsub;			/* number of discussions unsubscribed to */
DPTR dunsub[MAXUNSUB];		/* discussions unsubscribed to */
struct ngentry *firstng, *lastng;	/* articles in .newsrc file */
struct ngentry *curng;		/* current newsgroup */

struct ngentry **nghashtab, *hashng();


readinrc(rcfp)
	FILE *rcfp;
	{
	register struct ngentry *ngp;
	register char *p;
	char unsub;
	int i;
	DPTR dp;
	struct artrec a;
	struct ngrec g;
	struct stat statb;

	gfopen();
	if (maxng >= MAXGROUPS)
		xerror("Too many groups, increase MAXGROUPS");
	nghashtab = ckmalloc(i = (MAXGROUPS * 2 + 1) * sizeof *nghashtab);
	bzero((char *)nghashtab, i);
	ALL_GROUPS(g) {
		ngtable[g.g_num].ng_name = nsavestr(g.g_name);
		hashng(g.g_name, &ngtable[g.g_num]);
	}
	gfclose();
	if (rcfp == NULL)
		goto out;
	if (fstat(fileno(rcfp), &statb) >= 0)
		rcmode = statb.st_mode;
	while (fgets(bfr, LBUFLEN, rcfp) != NULL) {
		if (!nstrip(bfr))
			xerror(".newsrc line too long");
		if (bfr[0] == '<') {
			char ident[NAMELEN];
			scopyn(bfr, ident, NAMELEN);
			if (ndunsub >= MAXUNSUB)
				xerror("Unsubscribed to too many discussions");
			if ((dp = lookart(ident, &a)) != DNULL)
				dunsub[ndunsub++] = dp;
		} else if (islower(bfr[0])) {		/* newsgroup line? */
			for (p = bfr ; *p != ':' && *p != '!' ; p++)
				if (*p == '\0' || *p == ' ' || *p == '\t')
					goto junk;
			unsub = (*p == '!');
			*p++ = '\0';
			if (*p == ' ')
				p++;
			ngp = hashng(bfr, (struct ngentry *)0);
			if (ngp != NULL) {
				if (ngp->ng_bits != NULL) {
					free(ngp->ng_bits);
				} else {
					addrc(ngp);
				}
				ngp->ng_bits = savestr(p);
				ngp->ng_unsub = unsub;
			}
		} else {
junk:			if (nextrcjunk >= &rcjunk[MAXRCJUNK])
				xerror("Too many lines in .newsrc");
			*nextrcjunk++ = savestr(bfr);
		}
	}
	free(nghashtab);
	rcreadok = 1;

out:
	/* add new groups to the .newsrc file */
	for (ngp = ngtable ; ngp < ngtable + MAXGROUPS ; ngp++) {
		if (ngp->ng_name != NULL && ngp->ng_bits == NULL && wewant(ngp->ng_name))
			addrc(ngp);
	}
}



/*
 * Write out the .newsrc file.
 */
writeoutrc()
{
	FILE *rcfp;
	register int i;
	char **jp;
	struct ngentry *ngp;
	char newstmp[FPATHLEN];
	char newsbak[FPATHLEN];
	struct artrec a;
	FILE *ckfopen();

	if (!rcreadok)
		return;
	updaterc();
	sprintf(newstmp, "%s.tmp", newsrc);
	rcfp = ckfopen(newstmp, "w");
	if (rcmode)
		chmod(newstmp, rcmode);

	/* Write out options line, continuations, and comments. */
	for (jp = rcjunk ; jp < nextrcjunk ; jp++)
		fprintf(rcfp, "%s\n", *jp);

	/* Write out the newsgroup lines. */
	for (ngp = firstng ; ngp != NULL ; ngp = ngp->ng_next) {
		fprintf(rcfp, "%s%c", ngp->ng_name, ngp->ng_unsub? '!' : ':');
		if (ngp->ng_bits != NULL && *ngp->ng_bits != '\0')
			fprintf(rcfp, " %s", ngp->ng_bits);
		putc('\n', rcfp);
	}
	/* write out a list of discussions we have unsubscribed to */
	for (i = 0 ; i < ndunsub ; i++) {
		readrec(dunsub[i], &a);
		fprintf(rcfp, "%s\n", a.a_ident);
	}
	if (ferror(rcfp) || fclose(rcfp) == EOF)
		xerror("write error on .newsrc.tmp");
	sprintf(newsbak, "%s.bak", newsrc);
	if (! rcsaved) {
		rename(newsrc, newsbak);
		rcsaved++;
	}
	if (rename(newstmp, newsrc) < 0)
		xerror("rename .newsrc.tmp to .newsrc failed");
}


struct ngentry *
hashng(name, entry)
      char *name;
      struct ngentry *entry;
      {
      register char *p;
      register int i;
      register struct ngentry *ngp;

      i = 0;
      for (p = name ; *p ; i += *p++);
      if (p > name + 2)
            i += (p[-2] << 2) + (p[-1] << 1);
      i &= 077777;			/* protect against negetive chars */
      for (i = i % (MAXGROUPS * 2 + 1) ; (ngp = nghashtab[i]) != NULL ; ) {
            if (strcmp(ngp->ng_name, name) == 0)
                  return ngp;
            if (++i >= (MAXGROUPS * 2 + 1))
                  i = 0;
      }
      if (entry != NULL)
            nghashtab[i] = entry;
      return ngp;
}
!E!O!F!

cat > lib/rename.c <<\!E!O!F!
#include <errno.h>
#include "config.h"

#if BSDREL < 42

int
rename(from, to)
      char *from, *to ;
      {
      extern int errno ;

      if (link(from, to) < 0) {
            if (errno != EEXIST)
                  return -1 ;
            if (unlink(to) < 0 || link(from, to) < 0)
                  return -1 ;
      }
      return unlink(from) ;
}

#endif
!E!O!F!

cat > lib/replyname.c <<\!E!O!F!
/*
 * Generate an address for replying to this article
 */

#include <stdio.h>
#include "config.h"
#include "defs.h"
#include <sys/types.h>
#include "arthead.h"

char *
replyname(hptr, tbuf)
struct arthead *hptr;
char tbuf[PATHLEN];
{
	register char *ptr;
	extern char FULLSYSNAME[];
	char *getaddr();

#ifndef INTERNET
	strcpy(tbuf, hptr->h_path);
	/*
	 * Play games stripping off multiple berknet
	 * addresses (a!b!c:d:e => a!b!d:e) here.
	 */
	for (ptr=tbuf; *ptr; ptr++)
		if (index(NETCHRS, *ptr) && *ptr == ':' && index(ptr+1, ':'))
			strcpy(ptr, index(ptr+1, ':'));
#else
	ptr = hptr->h_from;
	if (hset(hptr->h_replyto))
		ptr = hptr->h_replyto;
	getaddr(ptr, tbuf);
#endif
	return tbuf;
}
!E!O!F!

cat > lib/rewinddir.c <<\!E!O!F!
#include "config.h"
#if BSDREL < 42
#include <sys/types.h>
#include <sys/param.h>
#include "ndir.h"

/*
 * Go back to the beginning of a directory.
 */

rewinddir(dirp)
	DIR *dirp;
	{
	dirp->dd_nleft = 0;
	lseek(dirp->dd_fd, 0L, 0);
}
#endif
!E!O!F!

cat > lib/rmnf.c <<\!E!O!F!
/*
 * Zap the notesfile indicator.
 */

#define equal(s1, s2)	(strcmp(s1, s2) == 0)

rmnf(title)
      char *title ;
      {
      register char *p ;

      p = title + strlen(title) - 7 ;
      if (p > title && equal(p, " - (nf)")) {
            *p = '\0' ;
            return 1 ;
      }
      return 0 ;
}
!E!O!F!

cat > lib/roptions.c <<\!E!O!F!
#include <stdio.h>
#include "config.h"
#include "defs.h"
#include "roptions.h"


char *savestr(), *getenv(), *index(), *hfgets();


struct optable options[] = { /*
optlet	filchar	flag	newstate oldmode	newmode	buf	*/
'p',	'\0',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,NULL,	
't',	'\1',	FALSE,	STRING,	ANY,		UNKNOWN,NULL,
'a',	' ',	FALSE,	STRING,	ANY,		UNKNOWN,NULL,
'n',   NGDELIM,	FALSE,	STRING,	ANY,		UNKNOWN,NULL,
'c',	' ',	FALSE,	STRING,	UNKNOWN,	UNKNOWN,NULL,
'l',	' ',	FALSE,	OPTION,	UNKNOWN,	UNKNOWN,NULL,
'r',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
/* the s option used to read a list of newsgroups.  Not any more */
's',   NGDELIM,	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'x',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'h',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'M',	'\0',	FALSE,	OPTION,	UNKNOWN,	MAIL,	NULL,
'f',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'u',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'e',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'1',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'2',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'A',	'\0',	FALSE,	OPTION,	ANY,		UNKNOWN,NULL,
'\0',	'\0',	0,	0,	0,		0,	NULL
};

char *progname ;
char *titles[10] ;		/* list of titles */
extern char bfr[] ;


roptions(argv, rcfp)
      char **argv ;
      FILE *rcfp ;
      {
      char *p ;
      char **pp ;

      progname = *argv++ ;
      if ((p = getenv("NEWSOPTS")) != NULL) {
            scopyn(p, bfr, LBUFLEN - 2) ;
            procostr(bfr) ;
      }
      if (rcfp != NULL && hfgets(bfr, LBUFLEN - 2, rcfp) != NULL && prefix(bfr, "options ")) {
            procostr(bfr + 8) ;
      }
      if (sublist == NULL)
            sublist = savestr(DFLTSUB);
#ifdef ADMSUB
      sprintf(bfr, "%s,%s", sublist, ADMSUB);
      free(sublist);
      sublist = savestr(bfr);
#endif

      procopt(argv) ;

      if (cflag) {
            oneflag = FALSE;
            twoflag = TRUE;
      }
      if (pflag) {
            oneflag = FALSE;
            twoflag = FALSE;
      }
      if (!oneflag && !twoflag && !pflag) {
            oneflag = TRUE;
            twoflag = TRUE;
      }
      if (oneflag && !twoflag)
            cflag = 1;
      strcpy(bfr, sublist);
      makehimask(bfr, "junk");
      makehimask(bfr, "control");
      /* makehimask(bfr, "test"); */
      free(sublist);
      sublist = savestr(bfr);
      if (toptbuf) {
            pp = titles ;
            p = toptbuf ;
            for (;;) {
                  if (pp >= titles + 9)
                        xerror("Too many titles") ;
                  *pp++ = p ;
                  if ((p = index(p, '\1')) == NULL)
                        break ;
                  *p++ = '\0' ;
            }
      }
}
!E!O!F!

cat > lib/roptions.h <<\!E!O!F!
/* flags for readnews */
#define pflag	options[0].flag
#define tflag	options[1].flag
#define toptbuf	options[1].buf
#define aflag	options[2].flag
#define datebuf	options[2].buf
#define nflag	options[3].flag
#define sublist	options[3].buf
#define cflag	options[4].flag
#define coptbuf	options[4].buf
#define lflag	options[5].flag
#define rflag	options[6].flag
#define sflag	options[7].flag
#define xflag	options[8].flag
#define hflag	options[9].flag
#define Mflag	options[10].flag
#define fflag	options[11].flag
#define uflag	options[12].flag
#define eflag	options[13].flag
#define oneflag	options[14].flag
#define twoflag	options[15].flag
#define Aflag	options[16].flag

#define UNKNOWN 0001	/* possible modes for news program */
#define MAIL	0004
#define ANY	0007

#define OPTION	0	/* pick up an option string */
#define STRING	1	/* pick up a string of arguments */

struct optable {			/* options table. */
	char	optlet;		/* option character. */
	char	filchar;	/* if to pickup string, fill character. */
	int	flag;		/* TRUE if have seen this opt. */
	int	newstate;	/* STRING if takes arg, else OPTION */
	int	oldmode;	/* OR of legal input modes. */
	int	newmode;	/* output mode. */
	char	*buf;		/* string buffer */
};

extern struct optable options[];
extern int mode;
!E!O!F!

cat > lib/savestr.c <<\!E!O!F!
/*
 * Make a copy of a string
 */

char
*savestr(s)
      char *s ;
      {
      char *p ;
      char *ckmalloc() ;

      p = ckmalloc(strlen(s) + 1) ;
      strcpy(p, s) ;
      return p ;
}
!E!O!F!

cat > lib/schar.c <<\!E!O!F!
/*
 * Convert a character to an integer, performing sign extension even on
 * machines that don't provide hardware support for signed characters.
 * This routine should be replaced by the macro
 *	#define schar(c)	((char)(c))
 * for compilers that support signed characters.
 */

/* The high bit of a character */
#define SIGNBIT (int)((unsigned char)(-1) &~ ((unsigned char)(-1) >> 1))
#define EXTBITS (int)(-1 &~ (unsigned char)(-1))

int
schar(c)
      char c ;
      {
      return (c & SIGNBIT) ? c | EXTBITS : c ;
}
!E!O!F!

cat > lib/scopyn.c <<\!E!O!F!
scopyn(from, to, n)
      register char *from, *to ;
      register int n ;
      {
      while (--n > 0 && (*to++ = *from++) != '\0') ;
      *to = '\0' ;
}
!E!O!F!

cat > lib/scopyn.pdp <<\!E!O!F!
/ SCOPYN(FROM, TO, N)  CHAR *FROM, *TO;
/
/	Copy string "from" to string "to", truncating "to" to n - 1
/	characters to avoid overflow.
/

.globl	_scopyn

_scopyn:
	mov  sp, r0			/ prepare to access args
	mov  r2, -(sp)			/ save r2
	tst  (r0)+			/
	mov  (r0)+, r1			/ get from
	mov  (r0)+, r2			/ get to
	mov  (r0)+, r0			/ get n
	br   2f				/ enter middle of loop
1:	movb (r1)+, (r2)+		/   copy a byte
	beq  3f				/   until a nul is encountered
2:	sob  r0, 1b			/   or count is exhausted
3:	clrb (r2)			/ nul terminate "to" string
	mov  (sp)+, r2			/ restore r2
	rts  pc				/ and return
!E!O!F!

cat > lib/scopyn.u3b <<\!E!O!F!
	.file "scopyn.u3b"		# give me a break!
	.globl	scopyn
	.align	4
scopyn:	save	&1			# save r8
	movw	0(%ap), %r0		# get from
	movw	4(%ap), %r1		# get to
	subw3	&1, 8(%ap), %r2		# get count - 1 to leave room for nul
	movw	&0, %r8			# string terminator
	movcce	%r0, %r2, %r1, %r8	# copy the string
	movb	&0, 0(%r1)		# append nul terminator
	ret	&1			# and return
!E!O!F!

cat > lib/scopyn.vax <<\!E!O!F!
# SCOPYN(SRC, DEST, N)  CHAR *SRC, *DEST
#
#	Copy src to dest, truncating the source string to n - 1 charaters
#	if necessary.  Dest will always get a nul terminator.
#
	.globl	_scopyn
	.align	2
_scopyn:.word	0			# no registers saved
	movq	4(ap), r4		# get src and dest
	subl3	$1, 12(ap), r0		# get n, leaving space for nul
	locc	$0, r0, (r4)		# scan until nul or count exhausted
	subl2	r4, r1			# find length
	movc3	r1, (r4), (r5)		# move the characters
	clrb	(r3)			# nul terminate dest
	ret				# and return
!E!O!F!

cat > lib/setup <<\!E!O!F!
#!/bin/sh

: This shell procedure reads in the config file, generates defaults for
: all the values not specified, and generated the config.h, newsdefs.h,
: makedefs, and archive.  It should be run to bring up a new system.
:
: This shell procedure uses the value ",,,," to indicate that a variable
: has not been set.

exec 3<&0
if test ! -f config
then	echo "Please create config file and run again."
	exit 1
fi

: figure out who is running this program
user=${LOGNAME-${USER-,,,,}}
if test "$user" = ,,,,
then	ls -l config > junk
	exec < junk
	read x y user rest
	rm -f junk
	if test "$user" = ""
	then	 user=news
	fi
fi

: look for other setup programs
setups= special=
for x in ../*
do	if test -f $x/config.parms
	then	setups="$setups $x/setup"
		special="$special`cat $x/config.parms`
"
	fi
done

: set up all the defaults
lib=/usr/lib/news
spool=/usr/spool/news
bin=/usr/bin
admin=,,,,
compadmin=no
newsusr=,,,,
newsgrp=,,,,
home=,,,,
sys=,,,,
cpu=,,,,
small=,,,,
umask=000
dftexp=14
tmail=,,,,
mailer=,,,,
dftsub=all
admsub=general,all.announce
page=,,,,
notify=,,,,
dfteditor=,,,,
manually=
internet=
mydomain=,,,,
myname=,,,,
myorg=,,,,
sendmail=,,,,
uname=,,,,
ghname=,,,,
v7mail=
newsrc=.newsrc
maxgroups=350
buflen=,,,,
lbuflen=1024
pathlen=512
datelen=48
namelen=64
fpathlen=64
termcap=,,,,

: read in the config file
exec <config
while read name value
do	case $name in
	path)		PATH=$value ;;
	lib)		lib=$value ;;
	spool)		spool=$value ;;
	bin)		bin=$value ;;
	admin)		admin=$value ;;
	compadmin)	compadmin=$value ;;
	newsusr)	newsusr=$value ;;
	newsgrp)	newsgrp=$value ;;
	home)		home=$value ;;
	sys)		sys=$value ;;
	cpu)		cpu=$value ;;
	small)		small=$value ;;
	umask)		umask=$value ;;
	dftexp)		dftexp=$value ;;
	tmail)		tmail=$value ;;
	mailer)		mailer=$value ;;
	dftsub)		dftsub=$value ;;
	dfltsub)	dftsub=$value ;;
	admsub)		admsub=$value ;;
	page)		page=$value ;;
	notify)		notify=$value ;;
	dfteditor)	dfteditor=$value ;;
	manually)	manually=$value ;;
	internet)	internet=$value ;;
	mydomain)	mydomain=$value ;;
	myname)		myname=$value ;;
	myorg)		myorg=$value ;;
	uname)		uname=$value ;;
	ghname)		ghname=$value ;;
	v7mail)		v7mail=$value ;;
	newsrc)		newsrc=$value ;;
	maxgroups)	maxgroups=$value ;;
	buflen)		buflen=$value ;;
	lbuflen)	lbuflen=$value ;;
	pathlen)	pathlen=$value ;;
	datelen)	datelen=$value ;;
	namelen)	namelen=$value ;;
	fpathlen)	fpathlen=$value ;;
	termcap)	termcap=$value ;;
	"")	;;
	"#"*)	;;
	*)	for x in $special
		do	if test "$name" = "$x"
			then	name=
			fi
		done
		if test "$name" != ""
		then	echo "Unknown config keyword:  $name"
			exit 2
		fi
		;;
	esac
done

if test "$sys" = ,,,,
then	if test -f /usr/include/termio.h
	then	if test -f /bin/shl -o -f /usr/bin/shl
		then	sys=sys5r2
		elif test -f /usr/bin/cflow
		then	sys=sys5
		else	sys=sys3
		fi
	else	if test -f /usr/include/sys/time.h
		then	sys=4.2bsd
		elif test -d /usr/ucb
		then	sys=4.1bsd
		else	sys=v7
		fi
	fi
	echo system assumed to be $sys.
fi

case "$sys" in
sys3)	USGREL=30 BSDREL=6 ;;
sys5)	USGREL=50 BSDREL=6 ;;
sys5r2)	USGREL=52 BSDREL=6 ;;
v7)	BSDREL=7  USGREL=6 ;;
4.1bsd)	BSDREL=41 USGREL=6 ;;
4.2bsd) BSDREL=42 USGREL=6 ;;
*)	echo "Unknown system type $sys"
	exit 1 ;;
esac

if test "$cpu" = ,,,,
then	cc -P cpu.c
	cpu=`sed -n s/X//p cpu.i`
	/bin/rm -f cpu.i
fi
case "$cpu" in
pdp11|vax|u3b|other) ;;
"")	cpu=other ;;
*)	echo "Unknown cpu type $cpu"
	exit 1
	;;
esac

if test "$small" = ,,,, -a $cpu = pdp11
then	small=yes
fi
if test "$buflen" = ,,,,
then	if test "$small" = "no" -o "$small" = ,,,,
	then	buflen=256
	else	buflen=128
	fi
fi

if test "$uname" = ,,,, -a $USGREL -ge 20
then	uname=yes
fi
if test "$ghname" = ,,,, -a $BSDREL -ge 42
then	ghname=yes
fi

: check all the boolean flags
for x in manually internet uname ghname v7mail small compadmin
do	eval y=\$$x
	case "$y" in
	no|,,,,)eval $x=,,,, ;;
	""|yes)	eval $x= ;;
	*)	echo $x is a boolean flag.
		exit 1 ;;
	esac
done

: check that all numeric parameters are set and are numbers
for x in umask dftexp maxgroups buflen lbuflen pathlen datelen namelen fpathlen
do	eval y=\$$x
	case "$y" in
	[0-9]|[0-9][0-9]|[0-9][0-9][0-9]|[0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9]) ;;
	,,,,)	echo "$x not set"
		exit 1 ;;
	*)	echo "$x value should be numeric"
		exit 1 ;;
	esac
done

: Check that required values are specified
for x in myorg mydomain newsusr newsgrp
do	eval y=\$$x
	if test "$y" = ,,,,
	then	echo $x not set
		exit 1
	fi
done

case "$mydomain" in
""|.*)	;;
*)	echo domain name should begin with a dot.
	exit 1 ;;
esac

if test "$admin" = ,,,,
then	admin=$user
fi
if test "$notify" = ""
then	notify=$admin
fi
if test "$termcap" = ,,,,
then	if test -f /usr/lib/libtermcap.a
	then	termcap=-ltermcap
	elif test -f /usr/lib/libtermlib.a
	then	termcap=-ltermlib
	elif test -f /usr/lib/libcurses.a
	then	termcap=-lcurses
	else	termcap=-ltermcap
	fi
fi
if test "$sendmail" = ,,,,
then	if test -f /usr/lib/sendmail
	then	sendmail=/usr/lib/sendmail
	fi
fi

: on BSD systems, prefer /bin/mail over /usr/ucb/mail
if test "$mailer" = ,,,, -a -f /bin/mail
then	mailer=/bin/mail
fi

: do some path searches
y=
for x in page more page pg dfteditor vi dfteditor ed mailer mail
do	if test "$y" = ""
	then	y=$x
	else	eval v=\$$y
		if test "$v" = ,,,,
		then	IFS=:
			for z in $PATH
			do	if test -f $z/$x
				then	eval $y=$z/$x
					break
				fi
			done
			IFS=' 	
'
		fi
		y=
	fi
done

if test "$mailer" = ,,,
then	echo Can\'t find mail
	exit 1
fi
if test "$dfteditor" = ,,,,
then	echo Can\'t find ed
	exit 1
fi

for x in admsub page sendmail
do	eval y=\$$x
	if test "$y" = ""
	then	eval $x=,,,,
	fi
done

if test "$compadmin" = ""
then	adminid=,,,,
else	adminid=`sed -n "s/^$admin:[^:]*:\([^:]*\):.*/\1/p" /etc/passwd`
	if test "$adminid" = ""
	then	echo Can\'t find $admin in /etc/passwd file.
		exit 1
	fi
fi

: 2.10.2 moved inews [Grrr!]
if test -f $lib/inews
then	inews=$lib/inews
else	inews=$bin/inews
fi

if test $USGREL -ge 30
then	index=strchr
	rindex=strrchr
	usg=
else	index=,,,,
	rindex=,,,,
	usg=,,,,
fi

grep -v ,,,, > config.h.tmp <<!
#define USGREL $USGREL
#define BSDREL $BSDREL
#define USG$usg
#define SMALL$small
#define index $index
#define rindex $rindex
!

grep -v ,,,, > newsdefs.h.tmp <<!
/*
 * newsdefs.h - defines for news related programs.
 *
 * This file is created by the setup program, and should be changed
 * by modifying the entries in config.
 */

#define N_UMASK	$umask
#define DFLTEXP	($dftexp*24L*3600L)
#define DFTEDITOR "$dfteditor"
#define	DFLTSUB	"$dftsub"
#define ADMSUB	"$admsub"
#define PAGE	"$page"
#define TMAIL	"$tmail"
#define MAILER	"$mailer"
#define INEWS	"$inews"
#define RNEWS	"$bin/rnews"
#define POSTNM	"$bin/postnm"
#define LIBDIR	"$lib
#define SPOOLDIR "$spool
#define BINDIR	"$bin
#define HOME	"$home"
#define NOTIFY	"$notify"
#define ADMIN	"$admin"
#define ROOTID	$adminid
#define MANUALLY$manually
#define INTERNET$internet
#define MYDOMAIN "$mydomain"
#define MYNAME	"$myname"
#define UNAME$uname
#define GHNAME$ghname
#define SENDMAIL "$sendmail"
#define V7MAIL$v7mail
#define MYORG	"$myorg"
#define NEWSRC	"$newsrc"
#define MAXGROUPS $maxgroups
#define BUFLEN	$buflen
#define LBUFLEN	$lbuflen
#define PATHLEN	$pathlen
#define DATELEN	$datelen
#define NAMELEN	$namelen
#define FPATHLEN $fpathlen
!

if test "$home" != ,,,,
then	hdir=`logdir $home`/
else	hdir=
fi

grep -v ,,,, > makedefs.tmp <<!
LIBDIR=$hdir$lib
SPOOLDIR=$hdir$spool
BINDIR=$bin
NEWSUSR=$newsusr
NEWSGRP=$newsgrp
HOME=$home
CPU=$cpu
USGREL=$USGREL
BSDREL=$BSDREL
TERMCAP=$termcap
!

if cmp -s config.h.tmp config.h
then	/bin/rm config.h.tmp
else	/bin/mv config.h.tmp config.h
fi
if cmp -s newsdefs.h.tmp newsdefs.h
then	/bin/rm newsdefs.h.tmp
else	/bin/mv newsdefs.h.tmp newsdefs.h
fi
if cmp -s makedefs.tmp makedefs
then	/bin/rm makedefs.tmp
else	/bin/mv makedefs.tmp makedefs
fi

for x in $setups
do	$x -M
done
echo 'The modified time of this file indicates when setup was last run.' > setuptime

sed -e 's/B,,,,/no/' -e 's/B$/yes/' -e '/,,,,/s/^/#/' -e 's/[ 	],,,,//' > config.used <<!
path	$PATH
lib	$lib
spool	$spool
bin	$bin
admin	$admin
compadmin B$compadmin
newsusr	$newsusr
newsgrp	$newsgrp
home	$home
sys	$sys
cpu	$cpu
small	B$small
umask	$umask
dftexp	$dftexp
tmail	$tmail
mailer	$mailer
dftsub	$dftsub
admsub	$admsub
page	$page
notify	$notify
dfteditor $dfteditor
manually B$manually
mydomain $mydomain
myname	$myname
myorg	$myorg
uname	B$uname
ghname	B$ghname
v7mail	B$v7mail
newsrc	$newsrc
maxgroups $maxgroups
buflen	$buflen
lbuflen	$lbuflen
pathlen	$pathlen
datelen	$datelen
namelen	$namelen
fpathlen $fpathlen
termcap $termcap
!
!E!O!F!
chmod +x lib/setup

echo Part 3 of 7 extracted.



More information about the Mod.sources mailing list