#! /bin/sh
# Make a new directory for the rn sources, cd to it, and run kits 1 thru 11 
# through sh.  When all 11 kits have been run, read README.

echo "This is rn kit 3 (of 11).  If kit 3 is complete, the line"
echo '"'"End of kit 3 (of 11)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting rcstuff.c
cat >rcstuff.c <<'!STUFFY!FUNK!'
/* $Id: rcstuff.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: rcstuff.c,v $
 * Revision 4.4.3.1  1992/02/01  03:09:32  sob
 * Release 4.4 Patchlevel 3
 *
 * Revision 4.4.2.1  1991/12/01  18:05:42  sob
 * Patchlevel 2 changes
 *
 * Revision 4.4  1991/09/09  20:27:37  sob
 * release 4.4
 *
 *
 * 
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "ngdata.h"
#include "term.h"
#include "final.h"
#include "rn.h"
#include "intrp.h"
#include "only.h"
#include "rcln.h"
#ifdef SERVER
#include "server.h"
#endif
#include "autosub.h"
#include "INTERN.h"
#include "rcstuff.h"

char *rcname INIT(Nullch);		/* path name of .newsrc file */
char *rctname INIT(Nullch);		/* path name of temp .newsrc file */
char *rcbname INIT(Nullch);		/* path name of backup .newsrc file */
char *softname INIT(Nullch);		/* path name of .rnsoft file */
FILE *rcfp INIT(Nullfp);		/* .newsrc file pointer */

static void grow_rc_arrays ANSI((int));
static void parse_rcline ANSI((NG_NUM));

#ifdef HASHNG
    static int hashsiz;
    static short *hashtbl = NULL;
#endif

bool
rcstuff_init()
{
    register NG_NUM newng;
    register int i;
    register bool foundany = FALSE;
    char *some_buf;
    long length;
#ifdef SERVER
    char *cp;
#endif /* SERVER */
    bool found = FALSE;

    /* make filenames */

#ifdef SERVER

    if (cp = getenv("NEWSRC"))
	rcname = savestr(filexp(cp));
    else
	rcname = savestr(filexp(RCNAME));

#else /* not SERVER */

    rcname = savestr(filexp(RCNAME));

#endif /* SERVER */

    rctname = savestr(filexp(RCTNAME));
    rcbname = savestr(filexp(RCBNAME));
    softname = savestr(filexp(SOFTNAME));
    
    /* make sure the .newsrc file exists */

    newsrc_check();

    /* open .rnsoft file containing soft ptrs to active file */

    tmpfp = fopen(softname,"r");
    if (tmpfp == Nullfp)
	writesoft = TRUE;

    /* allocate memory for rc file globals */
    grow_rc_arrays(1500);

    /* read in the .newsrc file */

    for (nextrcline = 0;
	(some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch;
	nextrcline++) {
					/* for each line in .newsrc */
	char tmpbuf[10];

	newng = nextrcline;		/* get it into a register */
	length = len_last_line_got;	/* side effect of get_a_line */
	if (length <= 1) {		/* only a newline??? */
	    nextrcline--;		/* compensate for loop increment */
	    continue;
	}
	if (newng >= maxrcline)		/* check for overflow */
	    grow_rc_arrays(maxrcline + 500);
	if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
	    softptr[newng] = atol(tmpbuf);
	else
	    softptr[newng] = 0;
	some_buf[--length] = '\0';	/* wipe out newline */
	if (checkflag)			/* no extra mallocs for -c */
	    rcline[newng] = some_buf;
	else if (some_buf == buf) {
	    rcline[newng] = savestr(some_buf);
					/* make a semipermanent copy */
	}
	else {
	    /*NOSTRICT*/
#ifndef lint
	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
#endif /* lint */
	    rcline[newng] = some_buf;
	}
#ifdef NOTDEF
	if (strnEQ(some_buf,"to.",3)) {	/* is this a non-newsgroup? */
	    nextrcline--;		/* destroy this line */
	    continue;
	}
#endif
	if (*some_buf == ' ' ||
	  *some_buf == '\t' ||
	  strnEQ(some_buf,"options",7)) {		/* non-useful line? */
	    toread[newng] = TR_JUNK;
	    rcchar[newng] = ' ';
	    rcnums[newng] = 0;
	    continue;
	}
	parse_rcline(newng);
	if (rcchar[newng] == NEGCHAR) {
	    toread[newng] = TR_UNSUB;
	    continue;
	}

	/* now find out how much there is to read */

	if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
	    toread[newng] = TR_NONE;	/* no need to calculate now */
	else
	    set_toread(newng);
#ifdef VERBOSE
	if (!checkflag && softmisses == 1) {
	    softmisses++;		/* lie a little */
	    fputs("(Revising soft pointers--be patient.)\n",stdout) FLUSH;
	}
#endif
	if (toread[newng] > TR_NONE) {	/* anything unread? */
	    if (!foundany) {
		starthere = newng;
		foundany = TRUE;	/* remember that fact*/
	    }
	    if (suppress_cn) {		/* if no listing desired */
		if (checkflag) {	/* if that is all they wanted */
		    finalize(1);	/* then bomb out */
		}
	    }
	    else {
#ifdef VERBOSE
		IF(verbose)
		    printf("Unread news in %-40s %5ld article%s\n",
			rcline[newng],(long)toread[newng],
			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
		ELSE
#endif
#ifdef TERSE
		    printf("%s: %ld article%s\n",
			rcline[newng],(long)toread[newng],
			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
#endif
		if (int_count) {
		    countdown = 1;
		    int_count = 0;
		}
		if (countdown) {
		    if (! --countdown) {
			fputs("etc.\n",stdout) FLUSH;
			if (checkflag)
			    finalize(1);
			suppress_cn = TRUE;
		    }
		}
	    }
	}
    }
    fclose(rcfp);			/* close .newsrc */
    if (tmpfp != Nullfp)
	fclose(tmpfp);			/* close .rnsoft */
    if (checkflag) {			/* were we just checking? */
	finalize(foundany);		/* tell them what we found */
    }
    if (paranoid)
	cleanup_rc();

#ifdef HASHNG

    /* find a good hash size given num of newsgroups */

    hashsiz = maxrcline * 1.15;
    if ((hashsiz & 1) == 0)	/* must be odd */
	hashsiz++;

    if (hashsiz <= activeitems)
	hashsiz = activeitems * 1.15;
    /* find one that's prime */
    while (! found) {
	for (i=3; ; i+=2) {
	    if ( (float)(hashsiz)/i == hashsiz/i )
		break;
	    if ( i > hashsiz/i ) {
		found = TRUE;
		break;
	    }
	}
	if (! found)
	    hashsiz += 2;
    }

    hashtbl = (short *) safemalloc( hashsiz * sizeof(short) );

    for (i=0; i<hashsiz; i++)
	hashtbl[i] = -1;

    if (!checkflag)
	for (i=0; i<nextrcline; i++)
	    sethash(i);
#endif

#ifdef DEBUGGING
    if (debug & DEB_HASH) {
	page_init();
	for (i=0; i<hashsiz; i++) {
	    sprintf(buf,"%d	%d",i,hashtbl[i]);
	    print_lines(buf,NOMARKING);
	}
    }
#endif

    return foundany;
}

static void
parse_rcline(ngnum)
NG_NUM ngnum;
{
#ifdef M_XENIX
    char *s;			/* bypass a compiler bug (ugh!) */
#else
    register char *s;
#endif

    for (s = rcline[ngnum]; *s && *s != ':' && *s != NEGCHAR; s++) ;
    if (!*s && !checkflag) {
#ifndef lint
	rcline[ngnum] = saferealloc(rcline[ngnum],
				(MEM_SIZE)(s - rcline[ngnum]) + 3);
#endif /* lint */
	strcpy(s, ": ");
    }
    rcchar[ngnum] = *s;	/* salt away the : or ! */
    rcnums[ngnum] = (char)(s - rcline[ngnum]) + 1;
				/* remember where the numbers are */
    *s = '\0';			/* null terminate newsgroup name */
}

void
abandon_ng(ngnum)
NG_NUM ngnum;
{
    char *some_buf = Nullch;

    /* open .oldnewsrc and try to find the prior value for the group. */
    if ((rcfp = fopen(rcbname, "r")) != Nullfp) {
	int length = rcnums[ngnum] - 1;

	while ((some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch) {
	    if (len_last_line_got <= 0)
		continue;
	    some_buf[len_last_line_got-1] = '\0';	/* wipe out newline */
	    if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
	     && strnEQ(rcline[ngnum], some_buf, length)) {
		break;
	    }
	    if (some_buf != buf)
		free(some_buf);
	}
	fclose(rcfp);
    } else if (errno != ENOENT) {
	printf("Unable to open %s.\n", rcbname) FLUSH;
	return;
    }
    if (some_buf == Nullch) {
	some_buf = rcline[ngnum] + rcnums[ngnum];
	if (*some_buf == ' ')
	    some_buf++;
	*some_buf = '\0';
#ifdef CACHEFIRST
	abs1st[ngnum] = 0;	/* force group to be re-calculated */
#endif
    }
    else {
	free(rcline[ngnum]);
	if (some_buf == buf) {
	    rcline[ngnum] = savestr(some_buf);
	}
	else {
	    /*NOSTRICT*/
#ifndef lint
	    some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
#endif /* lint */
	    rcline[ngnum] = some_buf;
	}
    }
    parse_rcline(ngnum);
    if (rcchar[ngnum] == NEGCHAR)
	rcchar[ngnum] = ':';
    set_toread(ngnum);
}

/* try to find or add an explicitly specified newsgroup */
/* returns TRUE if found or added, FALSE if not. */
/* assumes that we are chdir'ed to SPOOL */

#define ADDNEW_SUB ':'
#define ADDNEW_UNSUB '!'

bool
get_ng(what,do_reloc)
char *what;
bool_int do_reloc;
{
    char *ntoforget;
    char promptbuf[128];
    int autosub;

#ifdef VERBOSE
    IF(verbose)
	ntoforget = "Type n to forget about this newsgroup.\n";
    ELSE
#endif
#ifdef TERSE
	ntoforget = "n to forget it.\n";
#endif
    if (index(what,'/')) {
	dingaling();
	printf("\nBad newsgroup name.\n") FLUSH;
	return FALSE;
    }
    set_ngname(what);
    ng = find_ng(ngname);
    if (ng == nextrcline) {		/* not in .newsrc? */

#ifdef SERVER
	sprintf(ser_line, "GROUP %s", ngname);
	put_server(ser_line);
	if (nntp_get(ser_line, sizeof(ser_line)) < 0) {
	    fprintf(stderr, "\nrrn: Unexpected close of server socket.\n");
	    finalize(1);
	}
	if (*ser_line != CHAR_OK) {
	    if (atoi(ser_line) != ERR_NOGROUP) {
		fprintf(stderr, "\nServer response to GROUP %s:\n%s\n",
		    ngname, ser_line);
		finalize(1);
	    }
#else /* not SERVER */

	if (ng >= maxrcline)		/* check for overflow */
	    grow_rc_arrays(maxrcline + 25);

	if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0 ) {

#endif /* SERVER */

	    dingaling();
#ifdef VERBOSE
	    IF(verbose)
		printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		printf("\nNo %s!\n",ngname) FLUSH;
#endif
	    sleep(2);
	    return FALSE;
	}
	autosub = auto_subscribe(ngname);
	if (!autosub) autosub = addnewbydefault;
	if (autosub) {
		printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
		       ngname, (autosub == ADDNEW_SUB) ? "" : "un");
		ng = add_newsgroup(ngname, autosub);
	        do_reloc = FALSE;
	} else {
#ifdef VERBOSE
	IF(verbose)
	    sprintf(promptbuf,"\nNewsgroup %s not in .newsrc--subscribe? [ynYN] ",ngname);
	ELSE
#endif
#ifdef TERSE
	    sprintf(promptbuf,"\nSubscribe %s? [ynYN] ",ngname);
#endif
reask_add:
	in_char(promptbuf,'A');
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	putchar('\n') FLUSH;
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		printf("Type y or SP to add %s to your .newsrc.\nType Y to add all new groups to the end of your .newsrc.\nType N to add all new groups to the end of your .newsrc unsubscribed.\n", ngname)
		  FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("y or SP to add, Y to add all new groups, N to add all new groups unsubscribed\n",stdout) FLUSH;
#endif
	    fputs(ntoforget,stdout) FLUSH;
	    goto reask_add;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    ng = add_newsgroup(ngname, '!');
	    return FALSE;
	}
	else if (*buf == 'y') {
	    ng = add_newsgroup(ngname, ':');
	    do_reloc = TRUE;
	}
	else if (*buf == 'Y') {
	    fputs(
    "(I'll add all new newsgroups (subscribed) to the end of your .newsrc.)\n",
		  stdout);
	    addnewbydefault = ADDNEW_SUB;
	    printf("(Adding %s to end of your .newsrc subscribed)\n", ngname);
	    ng = add_newsgroup(ngname, ':');
	    do_reloc = FALSE;
	}
	else if (*buf == 'N') {
	    fputs(
  "(I'll add all new newsgroups (unsubscribed) to the end of your .newsrc.)\n",
		  stdout);
	    addnewbydefault = ADDNEW_UNSUB;
	    printf("(Adding %s to end of your .newsrc unsubscribed)\n", ngname);
	    ng = add_newsgroup(ngname, '!');
	    do_reloc = FALSE;
	}
	else {
	    fputs(hforhelp,stdout) FLUSH;
	    settle_down();
	    goto reask_add;
	}
      }
    }
    else if (mode == 'i')		/* adding new groups during init? */
	return FALSE;
    else if (rcchar[ng] == NEGCHAR) {	/* unsubscribed? */
#ifdef VERBOSE
	IF(verbose)
	    sprintf(promptbuf,
"\nNewsgroup %s is currently unsubscribed to--resubscribe? [yn] ",ngname)
  FLUSH;
	ELSE
#endif
#ifdef TERSE
	    sprintf(promptbuf,"\n%s unsubscribed--resubscribe? [yn] ",ngname)
	      FLUSH;
#endif
reask_unsub:
	in_char(promptbuf,'R');
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	putchar('\n') FLUSH;
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("y or SP to resubscribe.\n",stdout) FLUSH;
#endif
	    fputs(ntoforget,stdout) FLUSH;
	    goto reask_unsub;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    return FALSE;
	}
	else if (*buf == 'y') {
	    rcchar[ng] = ':';
	}
	else {
	    fputs(hforhelp,stdout) FLUSH;
	    settle_down();
	    goto reask_unsub;
	}
    }

    /* now calculate how many unread articles in newsgroup */

    set_toread(ng);
#ifdef RELOCATE
    if (do_reloc)
	ng = relocate_newsgroup(ng,-1);
#endif
    return toread[ng] >= TR_NONE;
}

/* add a newsgroup to the .newsrc file (eventually) */

NG_NUM
add_newsgroup(ngn, c)
char *ngn;
char_int c;
{
    register NG_NUM newng = nextrcline++;
					/* increment max rcline index */

    if (newng >= maxrcline)		/* check for overflow */
	grow_rc_arrays(maxrcline + 25);

    rcnums[newng] = strlen(ngn) + 1;
    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 2));
    strcpy(rcline[newng],ngn);		/* and copy over the name */
    strcpy(rcline[newng]+rcnums[newng], " ");
    rcchar[newng] = c;			/* subscribe or unsubscribe */
    toread[newng] = TR_NONE;	/* just for prettiness */
#ifdef HASHNG
    sethash(newng);			/* so we can find it again */
#endif
    return newng;
}

#ifdef RELOCATE
NG_NUM
relocate_newsgroup(ngx,newng)
NG_NUM ngx;
NG_NUM newng;
{
    char *dflt = (ngx!=current_ng ? "$^.L" : "$^L");
    char *tmprcline;
    ART_UNREAD tmptoread;
    char tmprcchar;
    char tmprcnums;
    ACT_POS tmpsoftptr;
    register NG_NUM i;
    ART_NUM tmpngmax;
#ifdef CACHEFIRST
    ART_NUM tmpabs1st;
#endif
    
    starthere = 0;                      /* Disable this optimization */
    writesoft = TRUE;			/* Update soft pointer file */
    if (ngx < nextrcline-1) {
#ifdef HASHNG
	for (i=0; i<hashsiz; i++) {
	    if (hashtbl[i] > ngx)
		--hashtbl[i];
	    else if (hashtbl[i] == ngx)
		hashtbl[i] = nextrcline-1;
	}
#endif
	tmprcline = rcline[ngx];
	tmptoread = toread[ngx];
	tmprcchar = rcchar[ngx];
	tmprcnums = rcnums[ngx];
	tmpsoftptr = softptr[ngx];
	tmpngmax = ngmax[ngx];
#ifdef CACHEFIRST
	tmpabs1st = abs1st[ngx];
#endif
	for (i=ngx+1; i<nextrcline; i++) {
	    rcline[i-1] = rcline[i];
	    toread[i-1] = toread[i];
	    rcchar[i-1] = rcchar[i];
	    rcnums[i-1] = rcnums[i];
	    softptr[i-1] = softptr[i];
	    ngmax[i-1] = ngmax[i];
#ifdef CACHEFIRST
	    abs1st[i-1] = abs1st[i];
#endif
	}
	rcline[nextrcline-1] = tmprcline;
	toread[nextrcline-1] = tmptoread;
	rcchar[nextrcline-1] = tmprcchar;
	rcnums[nextrcline-1] = tmprcnums;
	softptr[nextrcline-1] = tmpsoftptr;
	ngmax[nextrcline-1] = tmpngmax;
#ifdef CACHEFIRST
	abs1st[nextrcline-1] = tmpabs1st;
#endif
    }
    if (current_ng > ngx)
	current_ng--;
    if (newng < 0) {
      reask_reloc:
	unflush_output();		/* disable any ^O in effect */
#ifdef VERBOSE
	IF(verbose)
	    printf("\nPut newsgroup where? [%s] ", dflt);
	ELSE
#endif
#ifdef TERSE
	    printf("\nPut where? [%s] ", dflt);
#endif
	fflush(stdout);
      reinp_reloc:
	eat_typeahead();
	getcmd(buf);
	if (errno || *buf == '\f') {
			    /* if return from stop signal */
	    goto reask_reloc;	/* give them a prompt again */
	}
	setdef(buf,dflt);
#ifdef VERIFY
	printcmd();
#endif
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose) {
		printf("\n\n\
Type ^ to put the newsgroup first (position 0).\n\
Type $ to put the newsgroup last (position %d).\n", nextrcline-1);
		printf("\
Type . to put it before the current newsgroup (position %d).\n", current_ng);
		printf("\
Type -newsgroup name to put it before that newsgroup.\n\
Type +newsgroup name to put it after that newsgroup.\n\
Type a number between 0 and %d to put it at that position.\n", nextrcline-1);
		printf("\
Type L for a listing of newsgroups and their positions.\n") FLUSH;
	    }
	    ELSE
#endif
#ifdef TERSE
	    {
		printf("\n\n\
^ to put newsgroup first (pos 0).\n\
$ to put last (pos %d).\n", nextrcline-1);
		printf("\
. to put before current newsgroup (pos %d).\n", current_ng);
		printf("\
-newsgroup to put before newsgroup.\n\
+newsgroup to put after.\n\
number in 0-%d to put at that pos.\n", nextrcline-1);
		printf("\
L for list of .newsrc.\n") FLUSH;
	    }
#endif
	    goto reask_reloc;
	}
	else if (*buf == 'L') {
	    putchar('\n') FLUSH;
	    list_newsgroups();
	    goto reask_reloc;
	}
	else if (isdigit(*buf)) {
	    if (!finish_command(TRUE))	/* get rest of command */
		goto reinp_reloc;
	    newng = atol(buf);
	    if (newng < 0)
		newng = 0;
	    if (newng >= nextrcline)
		return nextrcline-1;
	}
	else if (*buf == '^') {
	    putchar('\n') FLUSH;
	    newng = 0;
	}
	else if (*buf == '$') {
	    newng = nextrcline-1;
	}
	else if (*buf == '.') {
	    putchar('\n') FLUSH;
	    newng = current_ng;
	}
	else if (*buf == '-' || *buf == '+') {
	    if (!finish_command(TRUE))	/* get rest of command */
		goto reinp_reloc;
	    newng = find_ng(buf+1);
	    if (newng == nextrcline) {
		fputs("Not found.",stdout) FLUSH;
		goto reask_reloc;
	    }
	    if (*buf == '+')
		newng++;
	}
	else {
	    printf("\n%s",hforhelp) FLUSH;
	    settle_down();
	    goto reask_reloc;
	}
    }
    if (newng < nextrcline-1) {
#ifdef HASHNG
	for (i=0; i<hashsiz; i++) {
	    if (hashtbl[i] == nextrcline-1)
		hashtbl[i] = newng;
	    else if (hashtbl[i] >= newng)
		++hashtbl[i];
	}
#endif
	tmprcline = rcline[nextrcline-1];
	tmptoread = toread[nextrcline-1];
	tmprcchar = rcchar[nextrcline-1];
	tmprcnums = rcnums[nextrcline-1];
	tmpsoftptr = softptr[nextrcline-1];
	tmpngmax = ngmax[nextrcline-1];
#ifdef CACHEFIRST
	tmpabs1st = abs1st[nextrcline-1];
#endif
	for (i=nextrcline-2; i>=newng; i--) {
	    rcline[i+1] = rcline[i];
	    toread[i+1] = toread[i];
	    rcchar[i+1] = rcchar[i];
	    rcnums[i+1] = rcnums[i];
	    softptr[i+1] = softptr[i];
	    ngmax[i+1] = ngmax[i];
#ifdef CACHEFIRST
	    abs1st[i+1] = abs1st[i];
#endif
	}
	rcline[newng] = tmprcline;
	toread[newng] = tmptoread;
	rcchar[newng] = tmprcchar;
	rcnums[newng] = tmprcnums;
	softptr[newng] = tmpsoftptr;
	ngmax[newng] = tmpngmax;
#ifdef CACHEFIRST
	abs1st[newng] = tmpabs1st;
#endif
    }
    if (current_ng >= newng)
	current_ng++;
    return newng;
}
#endif

/* List out the newsrc with annotations */

void
list_newsgroups()
{
    register NG_NUM i;
    char tmpbuf[2048];
    static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"};
    int cmd;

    page_init();
    print_lines("\
  #  Status  Newsgroup\n\
",STANDOUT);
    for (i=0; i<nextrcline && !int_count; i++) {
	if (toread[i] >= 0)
	    set_toread(i);
	*(rcline[i] + rcnums[i] - 1) = rcchar[i];
	if (toread[i] > 0)
	    sprintf(tmpbuf,"%3d %6ld   ",i,(long)toread[i]);
	else
	    sprintf(tmpbuf,"%3d %7s  ",i,status[-toread[i]]);
	safecpy(tmpbuf+13,rcline[i],2034);
	*(rcline[i] + rcnums[i] - 1) = '\0';
	if (cmd = print_lines(tmpbuf,NOMARKING)) {
	    if (cmd > 0)
		pushchar(cmd);
	    break;
	}
    }
    int_count = 0;
}

/* find a newsgroup in .newsrc */

NG_NUM
find_ng(ngnam)
char *ngnam;
{
    register NG_NUM ngnum;
#ifdef HASHNG
    register int hashix = hash(ngnam);
    register int incr = 1;

    while ((ngnum = hashtbl[hashix]) >= 0) {
	if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB)
	    return ngnum;
	hashix = (hashix + incr) % hashsiz;
	incr += 2;			/* offsets from original are in n*2 */
    }
    return nextrcline;			/* = notfound */

#else /* just do linear search */

    for (ngnum = 0; ngnum < nextrcline; ngnum++) {
	if (strEQ(rcline[ngnum],ngnam))
	    break;
    }
    return ngnum;
#endif
}

void
cleanup_rc()
{
    register NG_NUM ngx;
    register NG_NUM bogosity = 0;

#ifdef VERBOSE
    IF(verbose)
	fputs("Checking out your .newsrc--hang on a second...\n",stdout)
	  FLUSH;
    ELSE
#endif
#ifdef TERSE
	fputs("Checking .newsrc--hang on...\n",stdout) FLUSH;
#endif
    for (ngx = 0; ngx < nextrcline; ngx++) {
	if (toread[ngx] >= TR_UNSUB) {
	    set_toread(ngx);		/* this may reset newsgroup */
					/* or declare it bogus */
	}
	if (toread[ngx] == TR_BOGUS)
	    bogosity++;
    }
    for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--)
	bogosity--;			/* discount already moved ones */
    if (nextrcline > 5 && bogosity > nextrcline / 2) {
	fputs(
"It looks like the active file is messed up.  Contact your news administrator,\n\
",stdout);
	fputs(
"leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
",stdout) FLUSH;
    }
#ifdef RELOCATE
    else if (bogosity) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("Moving bogus newsgroups to the end of your .newsrc.\n",
		stdout) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    fputs("Moving boguses to the end.\n",stdout) FLUSH;
#endif
	for (; ngx >= 0; ngx--) {
	    if (toread[ngx] == TR_BOGUS)
		relocate_newsgroup(ngx,nextrcline-1);
	}
#ifdef DELBOGUS
reask_bogus:
	in_char("Delete bogus newsgroups? [ny] ", 'D');
	setdef(buf,"n");
#ifdef VERIFY
	printcmd();
#endif
	putchar('\n') FLUSH;
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\
Type y to delete bogus newsgroups.\n\
Type n or SP to leave them at the end in case they return.\n\
",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("y to delete, n to keep\n",stdout) FLUSH;
#endif
	    goto reask_bogus;
	}
	else if (*buf == 'n' || *buf == 'q')
	    ;
	else if (*buf == 'y') {
	    while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0)
		--nextrcline;		/* real tough, huh? */
	}
	else {
	    fputs(hforhelp,stdout) FLUSH;
	    settle_down();
	    goto reask_bogus;
	}
#endif
    }
#else
#ifdef VERBOSE
    IF(verbose)
	fputs("You should edit bogus newsgroups out of your .newsrc.\n",
	    stdout) FLUSH;
    ELSE
#endif
#ifdef TERSE
	fputs("Edit boguses from .newsrc.\n",stdout) FLUSH;
#endif
#endif
    paranoid = FALSE;
}

#ifdef HASHNG
/* make an entry in the hash table for the current newsgroup */

void
sethash(thisng)
NG_NUM thisng;
{
    register int hashix = hash(rcline[thisng]);
    register int incr = 1;
#ifdef DEBUGGING
    static int hashhits = 0, hashtries = 0;
#endif

#ifdef DEBUGGING
    hashtries++;
#endif
    while (hashtbl[hashix] >= 0) {
#ifdef DEBUGGING
	hashhits++;
	if (debug & DEB_HASH) {
	    printf("  Hash hits: %d / %d\n",hashhits, hashtries) FLUSH;
	}
	hashtries++;
#endif
	hashix = (hashix + incr) % hashsiz;
	incr += 2;			/* offsets from original are in n*2 */
    }
    hashtbl[hashix] = thisng;
}

short prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59,
    -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1};

int
hash(ngnam)
register char *ngnam;
{
    register int i = 0;
    register int ch;
    register int sum = 0;
#ifdef DEBUGGING
    char *ngn = ngnam;
#endif

    while (ch = *ngnam++) {
	sum += (ch + i) * prime[i];   /* gives ~ 10% hits at 25% full */
	i++;
    }
#ifdef DEBUGGING
    if (debug & DEB_HASH)
	printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%hashsiz)
	  FLUSH;
#endif
    if (sum < 0)
	sum = -sum;
    return (sum % hashsiz);
}

#endif

void
newsrc_check()
{
    rcfp = fopen(rcname,"r");		/* open it */
    if (rcfp == Nullfp) {			/* not there? */
#ifdef VERBOSE
	IF(verbose)
	    fputs("\nTrying to set up a .newsrc file--running newsetup...\n\n\
",stdout) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    fputs("Setting up .newsrc...\n",stdout) FLUSH;
#endif
	if (doshell(sh,filexp(NEWSETUP)) ||
	    (rcfp = fopen(rcname,"r")) == Nullfp) {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\nCan't create a .newsrc--you must do it yourself.\n\
",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("(Fatal)\n",stdout) FLUSH;
#endif
	    finalize(1);
	}
    }
    else {
	UNLINK(rcbname);		/* unlink backup file name */
	link(rcname,rcbname);		/* and backup current name */
    }
}

/* write out the (presumably) revised .newsrc */

void
write_rc()
{
    register NG_NUM tmpng;
    register char *delim;

    rcfp = fopen(rctname, "w");		/* open .newnewsrc */
    if (rcfp == Nullfp) {
	printf(cantrecreate,".newsrc") FLUSH;
	finalize(1);
    }
    if (stat(rcname,&filestat)>=0) {	/* preserve permissions */
	chmod(rctname,filestat.st_mode&0666);
	chown(rctname,filestat.st_uid,filestat.st_gid);	/* if possible */
    }

    /* write out each line*/

    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
	if (rcnums[tmpng]) {
	    delim = rcline[tmpng] + rcnums[tmpng] - 1;
	    *delim = rcchar[tmpng];
	}
	else
	    delim = Nullch;
#ifdef DEBUGGING
	if (debug & DEB_NEWSRC_LINE)
	    printf("%s\n",rcline[tmpng]) FLUSH;
#endif
	if (fprintf(rcfp,"%s\n",rcline[tmpng]) < 0) {
	write_error:
	    printf(cantrecreate,".newsrc") FLUSH;
	    fclose(rcfp);		/* close .newnewsrc */
	    UNLINK(rctname);
	    finalize(1);
	}
	if (delim)
	    *delim = '\0';		/* might still need this line */
    }
    fflush(rcfp);
    if (ferror(rcfp))
	goto write_error;

    fclose(rcfp);			/* close .newnewsrc */
    UNLINK(rcname);
    link(rctname,rcname);
    UNLINK(rctname);

    if (writesoft) {
	tmpfp = fopen(filexp(softname), "w");	/* open .rnsoft */
	if (tmpfp == Nullfp) {
	    printf(cantcreate,filexp(softname)) FLUSH;
	    return;
	}
	for (tmpng = 0; tmpng < nextrcline; tmpng++) {
	    fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]);
	}
	fclose(tmpfp);
    }
}

void
get_old_rc()
{
    UNLINK(rctname);
#ifdef RENAME
    rename(rcname,rctname);
    rename(rcbname,rcname);
#else
    link(rcname,rctname);
    UNLINK(rcname);
    link(rcbname,rcname);
    UNLINK(rcbname);
#endif
}

static char *
grow(ptr, elem, size)
char *ptr;
int elem;
int size;
{
    if (ptr != NULL)
	return saferealloc(ptr, (MEM_SIZE)elem * size);
    else
	return safemalloc((MEM_SIZE)elem * size);
}

static void
grow_rc_arrays(newsize)
int newsize;
{
    register int i;

#ifdef CACHEFIRST
    abs1st = (ART_NUM *) grow((char*)abs1st, newsize, sizeof(ART_NUM));
#endif
    ngmax = (ART_NUM *) grow((char*)ngmax, newsize, sizeof(ART_NUM));
    rcline = (char **) grow((char*)rcline, newsize, sizeof(char*));
    toread = (ART_UNREAD *) grow((char*)toread, newsize, sizeof(ART_UNREAD));
    rcchar = (char *) grow(rcchar, newsize, sizeof(char));
    rcnums = (char *) grow(rcnums, newsize, sizeof(char));
    softptr = (ACT_POS *) grow((char*)softptr, newsize, sizeof(ACT_POS));

    for (i=maxrcline; i < newsize; i++) {
#ifdef CACHEFIRST
	abs1st[i] = 0;
#endif
	ngmax[i] = 0;
    }
    maxrcline = newsize;
    return;
}
!STUFFY!FUNK!
echo Extracting intrp.c
cat >intrp.c <<'!STUFFY!FUNK!'
/* $Id: intrp.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: intrp.c,v $
 * Revision 4.4.3.1  1992/02/01  03:09:32  sob
 * Release 4.4 Patchlevel 3
 *
 * Revision 4.4.2.1  1991/12/01  18:05:42  sob
 * Patchlevel 2 changes
 *
 * Revision 4.4  1991/09/09  20:18:23  sob
 * release 4.4
 *
 *
 * 
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */
#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "search.h"
#include "head.h"
#include "rn.h"
#include "artsrch.h"
#include "ng.h"
#include "respond.h"
#include "rcstuff.h"
#include "bits.h"
#include "artio.h"
#include "term.h"
#include "final.h"
#include "INTERN.h"
#include "intrp.h"

static char * regexp_specials = "^$.*[\\/?";

char orgname[] = ORGNAME;

/* name of this site */
#ifdef GETHOSTNAME
    char *hostname;
#   undef SITENAME
#   define SITENAME hostname
#else /* !GETHOSTNAME */
#   ifdef DOUNAME
#	include <sys/utsname.h>
	struct utsname utsn;
#	undef SITENAME
#	define SITENAME utsn.nodename
#   else /* !DOUNAME */
#	ifdef PHOSTNAME
	    char *hostname;
#	    undef SITENAME
#	    define SITENAME hostname
#	else /* !PHOSTNAME */
#	    ifdef WHOAMI
#		undef SITENAME
#		define SITENAME sysname
#	    endif /* WHOAMI */
#	endif /* PHOSTNAME */
#   endif /* DOUNAME */
#endif /* GETHOSTNAME */

#ifdef TILDENAME
static char *tildename = Nullch;
static char *tildedir = Nullch;
#endif

char *realname INIT(Nullch);	/* real name of sender from /etc/passwd */

#ifdef CONDSUB
char *skipinterp ANSI((char *,char *));
#endif

static void abort_interp ANSI((void));

void
intrp_init(tcbuf)
char *tcbuf;
{
    char *getlogin();

    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */
    
    /* get environmental stuff */

#ifdef NEWSADMIN
    {
#ifdef GETPWENT
	struct passwd *getpwnam();
	struct passwd *pwd = getpwnam(NEWSADMIN);

	if (pwd != NULL)
	    newsuid = pwd->pw_uid;
#else
#ifdef TILDENAME
	char tildenews[2+sizeof NEWSADMIN];
	strcpy(tildenews, "~");
	strcat(tildenews, NEWSADMIN);
	(void) filexp(tildenews);
#else
	#error "At least one of GETPWENT or TILDENAME needed to get NEWSADMIN"
#endif  /* TILDENAME */
#endif	/* GETPWENT */
    }
#endif	/* NEWSADMIN */
    /* get home directory */

    homedir = getenv("HOME");
    if (homedir == Nullch)
	homedir = getenv("LOGDIR");

    dotdir = getval("DOTDIR",homedir);

    /* get login name */

    logname = getenv("USER");
    if (logname == Nullch)
	logname = getenv("LOGNAME");
#ifdef GETLOGIN
    if (logname == Nullch)
	logname = savestr(getlogin());
#endif

#ifdef NEWSADMIN
    /* if this is the news admin than load his UID into newsuid */

    if ( strEQ(logname,NEWSADMIN) )
	newsuid = getuid();
#endif

    if (checkflag)			/* that getwd below takes ~1/3 sec. */
	return;				/* and we do not need it for -c */
    getwd(tcbuf);			/* find working directory name */
    origdir = savestr(tcbuf);		/* and remember it */

    /* get the real name of the person (%N) */
    /* Must be done after logname is read in because BERKNAMES uses that */

    strcpy(tcbuf,getrealname((long)getuid()));
    realname = savestr(tcbuf);

    /* name of header file (%h) */

    headname = savestr(filexp(HEADNAME));

    /* name of this site (%H) */

#ifdef HOSTFILE
    if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
	hostname = "unknown";
	printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE); 
    } else {
	fgets(buf, sizeof(buf), tmpfp);
	buf[strlen(buf)-1] = 0;
	hostname = savestr(buf);
	fclose(tmpfp);
    }
#else
#ifdef GETHOSTNAME
    gethostname(buf,sizeof buf);
    hostname = savestr(buf);
#else
#ifdef DOUNAME
    /* get sysname */
    uname(&utsn);
#else
#ifdef PHOSTNAME
    {
	FILE *popen();
	FILE *pipefp = popen(PHOSTNAME,"r");
	
	if (pipefp == Nullfp) {
	    printf("Can't find hostname\n");
	    sig_catcher(0);
	}
	fgets(buf,sizeof buf,pipefp);
	buf[strlen(buf)-1] = '\0';	/* wipe out newline */
	hostname = savestr(buf);
	pclose(pipefp);
    }
#endif	/* PHOSTNAME */
#endif	/* DOUNAME */
#endif	/* GETHOSTNAME */
#endif	/* HOSTFILE */
    if (index(SITENAME,'.') == NULL) {
	sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
	sitename = savestr(buf);
    } else
	sitename = savestr(SITENAME);
}

/* expand filename via %, ~, and $ interpretation */
/* returns pointer to static area */
/* Note that there is a 1-deep cache of ~name interpretation */

char *
filexp(s)
register char *s;
{
    static char filename[CBUFLEN];
    char scrbuf[CBUFLEN];
    register char *d;

#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("< %s\n",s) FLUSH;
#endif
    interp(filename, (sizeof filename), s);	
					/* interpret any % escapes */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("%% %s\n",filename) FLUSH;
#endif
    s = filename;
    if (*s == '~') {	/* does destination start with ~? */
	if (!*(++s) || *s == '/') {
	    sprintf(scrbuf,"%s%s",homedir,s);
				/* swap $HOME for it */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("~ %s\n",scrbuf) FLUSH;
#endif
	    strcpy(filename,scrbuf);
	}
	else {
#ifdef TILDENAME
	    for (d=scrbuf; isalnum(*s); s++,d++)
		*d = *s;
	    *d = '\0';
	    if (tildedir && strEQ(tildename,scrbuf)) {
		strcpy(scrbuf,tildedir);
		strcat(scrbuf, s);
		strcpy(filename, scrbuf);
#ifdef DEBUGGING
		if (debug & DEB_FILEXP)
		    printf("r %s %s\n",tildename,tildedir) FLUSH;
#endif
	    }
	    else {
		if (tildename) {
		    free(tildename);
		    free(tildedir);
		}
		tildedir = Nullch;
		tildename = savestr(scrbuf);
#ifdef GETPWENT		/* getpwnam() is not the paragon of efficiency */
		{
#ifdef notdef
		    struct passwd *getpwnam ANSI((char*));
#endif
		    struct passwd *pwd = getpwnam(tildename);
		    if ( pwd == NULL){
			printf("%s is an unknown user. Using default.\n",tildename) FLUSH;
			return(Nullch);
		    }
		    sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
		    tildedir = savestr(pwd->pw_dir);
		    strcpy(filename,scrbuf);
		    endpwent();
		}
#else			/* this will run faster, and is less D space */
		{	/* just be sure LOGDIRFIELD is correct */
		    FILE *pfp = fopen("/etc/passwd","r");
		    char tmpbuf[512];
		    int i;
		    
		    if (pfp == Nullfp) {
			printf(cantopen,"passwd") FLUSH;
			sig_catcher(0);
		    }
		    while (fgets(tmpbuf,512,pfp) != Nullch) {
			d = cpytill(scrbuf,tmpbuf,':');
#ifdef DEBUGGING
			if (debug & DEB_FILEXP)
			    printf("p %s\n",tmpbuf) FLUSH;
#endif
			if (strEQ(scrbuf,tildename)) {
			    for (i=LOGDIRFIELD-2; i; i--) {
				if (d)
				    d = index(d+1,':');
			    }
			    if (d) {
				cpytill(scrbuf,d+1,':');
				tildedir = savestr(scrbuf);
				strcat(scrbuf,s);
				strcpy(filename,scrbuf);
			    }
			    break;
			}
		    }
		    fclose(pfp);
		}
#endif
	    }
#else /* !TILDENAME */
#ifdef VERBOSE
	    IF(verbose)
		fputs("~loginname not implemented.\n",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("~login not impl.\n",stdout) FLUSH;
#endif
#endif
	}
    }
    else if (*s == '$') {	/* starts with some env variable? */
	d = scrbuf;
	*d++ = '%';
	if (s[1] == '{')
	    strcpy(d,s+2);
	else {
	    *d++ = '{';
	    for (s++; isalnum(*s); s++) *d++ = *s;
				/* skip over token */
	    *d++ = '}';
	    strcpy(d,s);
	}
#ifdef DEBUGGING
	if (debug & DEB_FILEXP)
	    printf("$ %s\n",scrbuf) FLUSH;
#endif
	interp(filename, (sizeof filename), scrbuf);
					/* this might do some extra '%'s but */
					/* that is how the Mercedes Benz */
    }
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("> %s\n",filename) FLUSH;
#endif
    return filename;
}

#ifdef CONDSUB
/* skip interpolations */

char *
skipinterp(pattern,stoppers)
register char *pattern;
char *stoppers;
{

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUGGING
	if (debug & 8)
	    printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	    switch (*++pattern) {
	    case '{':
		for (pattern++; *pattern && *pattern != '}'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		break;
	    case '[':
		for (pattern++; *pattern && *pattern != ']'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		break;
#ifdef CONDSUB
	    case '(': {
		pattern = skipinterp(pattern+1,"!=");
		if (!*pattern)
		    goto getout;
		for (pattern++; *pattern && *pattern != '?'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		if (!*pattern)
		    goto getout;
		pattern = skipinterp(pattern+1,":)");
		if (*pattern == ':')
		    pattern = skipinterp(pattern+1,")");
		break;
	    }
#endif
#ifdef BACKTICK
	    case '`': {
		pattern = skipinterp(pattern+1,"`");
		break;
	    }
#endif
#ifdef PROMPTTTY
	    case '"':
		pattern = skipinterp(pattern+1,"\"");
		break;
#endif
	    default:
		break;
	    }
	    pattern++;
	}
	else {
	    if (*pattern == '^' && pattern[1])
		pattern += 2;
	    else if (*pattern == '\\' && pattern[1])
		pattern += 2;
	    else
		pattern++;
	}
    }
getout:
    return pattern;			/* where we left off */
}
#endif

/* interpret interpolations */

char *
dointerp(dest,destsize,pattern,stoppers)
register char *dest;
register int destsize;
register char *pattern;
char *stoppers;
{
    char *subj_buf = Nullch;
    char *ngs_buf = Nullch;
    char *refs_buf = Nullch;
    char *artid_buf = Nullch;
    char *reply_buf = Nullch;
    char *from_buf = Nullch;
    char *path_buf = Nullch;
    char *follow_buf = Nullch;
    char *dist_buf = Nullch;
    char *line_buf = Nullch;
    register char *s, *h;
    register int i;
    char scrbuf[512];
    bool upper = FALSE;
    bool lastcomp = FALSE;
    bool re_quote = FALSE;
    int metabit = 0;

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUGGING
	if (debug & 8)
	    printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	    upper = FALSE;
	    lastcomp = FALSE;
	    re_quote = FALSE;
	    for (s=Nullch; !s; ) {
		switch (*++pattern) {
		case '^':
		    upper = TRUE;
		    break;
		case '_':
		    lastcomp = TRUE;
		    break;
		case '\\':
		    re_quote = TRUE;
		    break;
		case '/':
#ifdef ARTSRCH
		    s = scrbuf;
		    if (!index("/?g",pattern[-2]))
			*s++ = '/';
		    strcpy(s,lastpat);
		    s += strlen(s);
		    if (pattern[-2] != 'g') {
			if (index("/?",pattern[-2]))
			    *s++ = pattern[-2];
			else
			    *s++ = '/';
			if (art_howmuch == 1)
			    *s++ = 'h';
			else if (art_howmuch == 2)
			    *s++ = 'a';
			if (art_doread)
			    *s++ = 'r';
		    }
		    *s = '\0';
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case '{':
		    pattern = cpytill(scrbuf,pattern+1,'}');
		    if (s = index(scrbuf,'-'))
			*s++ = '\0';
		    else
			s = nullstr;
		    s = getval(scrbuf,s);
		    break;
		case '[':
		    pattern = cpytill(scrbuf,pattern+1,']');
		    i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
		    if (line_buf)
			free(line_buf);
		    s = line_buf = fetchlines(art,i);
		    break;
#ifdef CONDSUB
		case '(': {
		    COMPEX *oldbra_compex = bra_compex;
		    COMPEX cond_compex;
		    char rch;
		    bool matched;
		    
		    init_compex(&cond_compex);
		    pattern = dointerp(dest,destsize,pattern+1,"!=");
		    rch = *pattern;
		    if (rch == '!')
			pattern++;
		    if (*pattern != '=')
			goto getout;
		    pattern = cpytill(scrbuf,pattern+1,'?');
		    if (!*pattern)
			goto getout;
		    if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
			printf("%s: %s\n",scrbuf,s) FLUSH;
			pattern += strlen(pattern);
			goto getout;
		    }
		    matched = (execute(&cond_compex,dest) != Nullch);
		    if (cond_compex.nbra)	/* were there brackets? */
			bra_compex = &cond_compex;
		    if (matched==(rch == '=')) {
			pattern = dointerp(dest,destsize,pattern+1,":)");
			if (*pattern == ':')
			    pattern = skipinterp(pattern+1,")");
		    }
		    else {
			pattern = skipinterp(pattern+1,":)");
			if (*pattern == ':')
			    pattern++;
			pattern = dointerp(dest,destsize,pattern,")");
		    }
		    s = dest;
		    bra_compex = oldbra_compex;
		    free_compex(&cond_compex);
		    break;
		}
#endif
#ifdef BACKTICK
		case '`': {
		    FILE *pipefp, *popen();

		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
		    pipefp = popen(scrbuf,"r");
		    if (pipefp != Nullfp) {
			int len;

			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
			    pipefp);
			scrbuf[len] = '\0';
			pclose(pipefp);
		    }
		    else {
			printf("\nCan't run %s\n",scrbuf);
			*scrbuf = '\0';
		    }
		    for (s=scrbuf; *s; s++) {
			if (*s == '\n') {
			    if (s[1])
				*s = ' ';
			    else
				*s = '\0';
			}
		    }
		    s = scrbuf;
		    break;
		}
#endif
#ifdef PROMPTTTY
		case '"':
		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
		    fputs(scrbuf,stdout) FLUSH;
		    resetty();
		    gets(scrbuf);
		    noecho();
		    crmode();
		    s = scrbuf;
		    break;
#endif
		case '~':
		    s = homedir;
		    break;
		case '.':
		    s = dotdir;
		    break;
		case '$':
		    s = scrbuf;
		    sprintf(s,"%d",getpid());
		    break;
		case '#':
		    s = scrbuf;
		    sprintf(s,"%d",perform_cnt);
		    break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
#ifdef CONDSUB
		    s = getbracket(bra_compex,*pattern - '0');
#else
		    s = nullstr;
#endif
		    break;
		case 'a':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
		    break;
		case 'A':
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
#ifdef SERVER
		    sprintf(s,"%s/rrn%ld.%d",spool,(long)art,getpid());
#else
		    sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
#endif
#endif
		    break;
		case 'b':
		    s = savedest;
		    break;
		case 'B':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)savefrom);
		    break;
		case 'c':
		    s = ngdir;
		    break;
		case 'C':
		    s = ngname;
		    break;
		case 'd':
		    s = scrbuf;
		    sprintf(s,"%s/%s",spool,ngdir);
		    break;
		case 'D':
		    s = dist_buf = fetchlines(art,DIST_LINE);
		    break;
		case 'e':
		    s = (extractprog ? extractprog : "-");
		    break;
		case 'E':
		    s = extractdest;
		    break;
		case 'f':			/* from line */
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
						/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    break;
		case 'F':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[FOLLOW_LINE].ht_minpos >= 0)
					/* is there a Followup-To line? */
			s = follow_buf = fetchlines(art,FOLLOW_LINE);
		    else 
			s = ngs_buf = fetchlines(art,NGS_LINE);
		    break;
		case 'h':			/* header file name */
		    s = headname;
		    break;
		case 'H':			/* host name */
		    s = sitename;
		    break;
		case 'i':
		    if (!(s=artid_buf))
			s = artid_buf = fetchlines(art,MESSID_LINE);
		    if (*s && *s != '<') {
			sprintf(scrbuf,"<%s>",artid_buf);
			s = scrbuf;
		    }
		    break;
		case 'I':			/* ref article indicator */
		    s = scrbuf;
		    sprintf(scrbuf,"'%s'",indstr);
		    break;
		case 'l':			/* rn library */
#ifdef NEWSADMIN
		    s = newsadmin;
#else
		    s = "???";
#endif
		    break;
		case 'L':			/* login id */
		    s = logname;
		    break;
		case 'm':		/* current mode */
		    s = scrbuf;
		    *s = mode;
		    s[1] = '\0';
		    break;
		case 'M':
#ifdef DELAYMARK
		    sprintf(scrbuf,"%ld",(long)dmcount);
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case 'n':			/* newsgroups */
		    s = ngs_buf = fetchlines(art,NGS_LINE);
		    break;
		case 'N':			/* full name */
		    s = getval("NAME",realname);
		    break;
		case 'o':			/* organization */
#ifdef IGNOREORG
		    s = getval("NEWSORG",orgname); 
#else
		    s = getenv("NEWSORG");
		    if (s == Nullch) 
			s = getval("ORGANIZATION",orgname); 
#endif
#ifdef ORGFILE
		    if (*s == '/') {
			FILE *ofp = fopen(s,"r");

			if (ofp) {
			    fgets(scrbuf,sizeof scrbuf,ofp);
			    fclose(ofp);
			    s = scrbuf+strlen(scrbuf)-1;
			    if (*s == '\n')
				*s = '\0';
			    s = scrbuf;
			}
		    }
#endif
		    break;
		case 'O':
		    s = origdir;
		    break;
		case 'p':
		    s = cwd;
		    break;
		case 'P':
		    s = spool;
		    break;
		case 'r':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,(sizeof scrbuf),refs_buf);
		    }
		    else
			*scrbuf = '\0';
		    s = rindex(scrbuf,'<');
		    break;
		case 'R':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,(sizeof scrbuf),refs_buf);
			/* no more than 3 prior references allowed,
			** including the one concatenated below */
			if ((s = rindex(scrbuf,'<')) > scrbuf) {
			    *s = '\0';
			    h = rindex(scrbuf,'<');
			    *s = '<';
			    if (h > scrbuf)
				strcpy(scrbuf,h);
			}
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,MESSID_LINE);
		    if (artid_buf[0] == '<')
			safecat(scrbuf,artid_buf,sizeof(scrbuf));
		    else if (artid_buf[0]) {
			char tmpbuf[64];
    
			sprintf(tmpbuf,"<%s>",artid_buf);
			safecat(scrbuf,tmpbuf,sizeof(scrbuf));
		    }
		    s = scrbuf;
		    break;
		case 's':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    if (h = instr(s,"- (nf", TRUE))
			*h = '\0';
		    break;
		case 'S':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    break;
		case 't':
		case 'T':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
					/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    if (*pattern == 'T') {
			if (htype[PATH_LINE].ht_minpos >= 0) {
					/* should we substitute path? */
			    s = path_buf = fetchlines(art,PATH_LINE);
			}
			i = strlen(sitename);
			if (strnEQ(sitename,s,i) && s[i] == '!')
			    s += i + 1;
		    }
		    if ((h=index(s,'(')) != Nullch)
						/* strip garbage from end */
			*(h-1) = '\0';
		    else if ((h=index(s,'<')) != Nullch) {
						/* or perhaps from beginning */
			s = h+1;
			if ((h=index(s,'>')) != Nullch)
			    *h = '\0';
		    }
		    break;
		case 'u':
		    sprintf(scrbuf,"%ld",(long)toread[ng]);
		    s = scrbuf;
		    break;
		case 'U': {
		    int unseen;

		    unseen = (art <= lastart) && !was_read(art);
		    sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
		    s = scrbuf;
		    break;
		}
		case 'x':			/* news library */
		    s = lib;
		    break;
		case 'X':			/* rn library */
		    s = rnlib;
		    break;
		case 'z':
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
#endif
		    if (stat(s,&filestat) < 0)
			filestat.st_size = 0L;
		    sprintf(scrbuf,"%5ld",(long)filestat.st_size);
		    s = scrbuf;
		    break;
		default:
		    if (--destsize <= 0)
			abort_interp();
		    *dest++ = *pattern | metabit;
		    s = nullstr;
		    break;
		}
	    }
	    if (!s)
		s = nullstr;
	    pattern++;
	    if (upper || lastcomp) {
		char *t;

		if (s != scrbuf) {
		    safecpy(scrbuf,s,(sizeof scrbuf));
		    s = scrbuf;
		}
		if (upper || !(t=rindex(s,'/')))
		    t = s;
		while (*t && !isalpha(*t))
		    t++;
		if (islower(*t))
		    *t = toupper(*t);
	    }
	    /* Do we have room left? */
	    i = strlen(s);
	    if (destsize <= i)
		abort_interp();
	    destsize -= i;	/* adjust the size now. */

	    /* A maze of twisty little conditions, all alike... */
	    if (metabit) {
		/* set meta bit while copying. */
		i = metabit;		/* maybe get into register */
		if (s == dest) {
		    while (*dest)
			*dest++ |= i;
		} else {
		    while (*s)
			*dest++ = *s++ | i;
		}

	    } else if (re_quote) {
		/* put a backslash before regexp specials while copying. */
		if (s == dest) {
		    /* copy out so we can copy in. */
		    safecpy(scrbuf, s, sizeof scrbuf);
		    s = scrbuf;
		    if (i > sizeof scrbuf)	/* we truncated, ack! */
			destsize += i - sizeof scrbuf;
		}
		while (*s) {
		    if (index(regexp_specials, *s)) {
			if (--destsize <= 0)
			    abort_interp();
			*dest++ = '\\';
		    }
		    *dest++ = *s++;
		}

	    } else {
		/* straight copy. */
		if (s == dest) {
		    dest += i;
		} else {
		    while (*s)
			*dest++ = *s++;
		}
	    }
	}
	else {
	    if (--destsize <= 0)
		abort_interp();
	    if (*pattern == '^' && pattern[1]) {
		++pattern;			/* skip uparrow */
		i = *pattern;		/* get char into a register */
		if (i == '?')
		    *dest++ = '\177' | metabit;
		else if (i == '(') {
		    metabit = 0200;
		    destsize++;
		}
		else if (i == ')') {
		    metabit = 0;
		    destsize++;
		}
		else
		    *dest++ = i & 037 | metabit;
		pattern++;
	    }
	    else if (*pattern == '\\' && pattern[1]) {
		++pattern;			/* skip backslash */
		i = *pattern;		/* get char into a register */
    
		/* this used to be a switch but the if may save space */
		
		if (i >= '0' && i <= '7') {
		    i = 1;
		    while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
			i <<= 3;
			i += *pattern++ - '0';
		    }
		    *dest++ = i & 0377 | metabit;
		    --pattern;
		}
		else if (i == 'b')
		    *dest++ = '\b' | metabit;
		else if (i == 'f')
		    *dest++ = '\f' | metabit;
		else if (i == 'n')
		    *dest++ = '\n' | metabit;
		else if (i == 'r')
		    *dest++ = '\r' | metabit;
		else if (i == 't')
		    *dest++ = '\t' | metabit;
		else
		    *dest++ = i | metabit;
		pattern++;
	    }
	    else
		*dest++ = *pattern++ | metabit;
	}
    }
    *dest = '\0';
getout:
    if (subj_buf != Nullch)	/* return any checked out storage */
	free(subj_buf);
    if (ngs_buf != Nullch)
	free(ngs_buf);
    if (refs_buf != Nullch)
	free(refs_buf);
    if (artid_buf != Nullch)
	free(artid_buf);
    if (reply_buf != Nullch)
	free(reply_buf);
    if (from_buf != Nullch)
	free(from_buf);
    if (path_buf != Nullch)
	free(path_buf);
    if (follow_buf != Nullch)
	free(follow_buf);
    if (dist_buf != Nullch)
	free(dist_buf);
    if (line_buf != Nullch)
	free(line_buf);
    return pattern;			/* where we left off */
}

void
interp(dest,destsize,pattern)
char *dest;
int destsize;
char *pattern;
{
    dointerp(dest,destsize,pattern,Nullch);
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	fputs(dest,stdout);
#endif
}

/* copy a references line, normalizing as we go */

void
refscpy(dest,destsize,src)
register char *dest, *src;
register int destsize;
{
    register char *dot, *at, *beg;
    char tmpbuf[64];
    
    while (*src) {
	if (*src != '<') {
	    if (--destsize <= 0)
		break;
	    *dest++ = '<';
	    at = dot = Nullch;
	    beg = src;
	    while (*src && *src != ' ' && *src != ',') {
		if (*src == '.')
		    dot = src;
		else if (*src == '@')
		    at = src;
		if (--destsize <= 0)
		    break;
		*dest++ = *src++;
	    }
	    if (destsize <= 0)
		break;
	    if (dot && !at) {
		int len;

		*dest = *dot++ = '\0';
		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
		len = strlen(tmpbuf);
		if (destsize > len) {
		    strcpy(dest,tmpbuf);
		    dest = dest + len;
		    destsize -= len;
		}
	    }
	    if (--destsize <= 0)
		break;
	    *dest++ = '>';
	}
	else {
	    while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
	    if (destsize <= 0)
		break;
	}
	while (*src == ' ' || *src == ',') src++;
	if (*src && --destsize > 0)
	    *dest++ = ' ';
    }
    *dest = '\0';
} 

/* get the person's real name from /etc/passwd */
/* (string is overwritten, so it must be copied) */

char *
getrealname(uid)
long uid;
{
    char *s, *c;

#ifdef PASSNAMES
#ifdef GETPWENT
#ifdef notdef
    struct passwd *getpwuid ANSI((uid_t));
#endif
    struct passwd *pwd = getpwuid(uid);
    
    s = pwd->pw_gecos;
#else
    char tmpbuf[512];
    int i;

    getpw(uid, tmpbuf);
    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
	if (s)
	    s = index(s,':')+1;
    }
    if (!s)
	return nullstr;
    cpytill(tmpbuf,s,':');
    s = tmpbuf;
#endif
#ifdef BERKNAMES
#ifdef BERKJUNK
    while (*s && !isalnum(*s) && *s != '&') s++;
#endif
    if ((c = index(s, ',')) != Nullch)
	*c = '\0';
    if ((c = index(s, ';')) != Nullch)
	*c = '\0';
    s = cpytill(buf,s,'&');
    if (*s == '&') {			/* whoever thought this one up was */
	c = buf + strlen(buf);		/* in the middle of the night */
	strcat(c,logname);		/* before the morning after */
	strcat(c,s+1);
	if (islower(*c))
	    *c = toupper(*c);		/* gack and double gack */
    }
#else
    if ((c = index(s, '(')) != Nullch)
	*c = '\0';
    if ((c = index(s, '-')) != Nullch)
	s = c;
    strcpy(buf,s);
#endif
#ifdef GETPWENT
    endpwent();
#endif
    return buf;				/* return something static */
#else
    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
	fgets(buf,sizeof buf,tmpfp);
	fclose(tmpfp);
	buf[strlen(buf)-1] = '\0';
	return buf;
    }
    return "PUT YOUR NAME HERE";
#endif
}

static void
abort_interp()
{
    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
    sig_catcher(0);
}


!STUFFY!FUNK!
echo Extracting util.c
cat >util.c <<'!STUFFY!FUNK!'
/* $Id: util.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: util.c,v $
 * Revision 4.4.3.1  1992/02/01  03:09:32  sob
 * Release 4.4 Patchlevel 3
 *
 * Revision 4.4  1991/09/09  20:27:37  sob
 * release 4.4
 *
 *
 * 
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */

#include "EXTERN.h"
#include "common.h"
#include "final.h"
#include "INTERN.h"
#include "util.h"

void
util_init()
{
    ;
}
    
/* fork and exec a shell command */

int
doshell(shl,s)
char *s, *shl;
{
    int status, pid, w;
    char *shell;

#ifdef SIGTSTP
    sigset(SIGTSTP,SIG_DFL);
    sigset(SIGTTOU,SIG_DFL);
    sigset(SIGTTIN,SIG_DFL);
#endif
    if (shl != Nullch)
	shell = shl;
    else if ((shell = getenv("SHELL")) == Nullch || !*shell)
	shell = PREFSHELL;
    if ((pid = vfork()) == 0) {
#ifdef SERVER
        int i;

	/* This is necessary to keep bourne shell from puking */

        for (i = 3; i < 10; ++i)
                close(i);
#endif /* SERVER */

	if (*s)
	    execl(shell, shell, "-c", s, Nullch);
	else
	    execl(shell, shell, Nullch, Nullch, Nullch);
	_exit(127);
    }
    signal(SIGINT, SIG_IGN);
#ifdef SIGQUIT
    signal(SIGQUIT, SIG_IGN);
#endif 
    waiting = TRUE;
    while ((w = wait(&status)) != pid)
	if (w == -1 && errno != EINTR)
	    break;
    if (w == -1)
	status = -1;
    waiting = FALSE;
    sigset(SIGINT, int_catcher);	/* always catch interrupts */
#ifdef SIGQUIT
    signal(SIGQUIT, SIG_DFL);
#endif 
#ifdef SIGTSTP
    sigset(SIGTSTP,stop_catcher);
    sigset(SIGTTOU,stop_catcher);
    sigset(SIGTTIN,stop_catcher);
#endif
    return status;
}

static char nomem[] = "rn: out of memory!\n";

/* paranoid version of malloc */

char *
safemalloc(size)
MEM_SIZE size;
{
    char *ptr;
    char *malloc();

    ptr = malloc(size ? size : (MEM_SIZE)1);
    if (ptr == Nullch) {
	fputs(nomem,stdout) FLUSH;
	sig_catcher(0);
    }
    return ptr;
}

/* paranoid version of realloc */

char *
saferealloc(where,size)
char *where;
MEM_SIZE size;
{
    char *ptr;
    char *realloc();

    ptr = realloc(where,size?size:1);	/* realloc(0) is NASTY on our system */
    if (ptr == Nullch) {
	fputs(nomem,stdout) FLUSH;
	sig_catcher(0);
    }
    return ptr;
}

/* safe version of string copy */

char *
safecpy(to,from,len)
char *to;
register char *from;
register int len;
{
    register char *dest = to;

    if (from != Nullch) 
	for (len--; len && (*dest++ = *from++); len--) ;
    *dest = '\0';
    return to;
}

/* safe version of string concatenate, with \n deletion and space padding */

char *
safecat(to,from,len)
char *to;
register char *from;
register int len;
{
    register char *dest = to;

    len--;				/* leave room for null */
    if (*dest) {
	while (len && *dest++) len--;
	if (len) {
	    len--;
	    *(dest-1) = ' ';
	}
    }
    if (from != Nullch)
	while (len && (*dest++ = *from++)) len--;
    if (len)
	dest--;
    if (*(dest-1) == '\n')
	dest--;
    *dest = '\0';
    return to;
}

/* copy a string up to some (non-backslashed) delimiter, if any */

char *
cpytill(to,from,delim)
register char *to, *from;
register int delim;
{
    for (; *from; from++,to++) {
	if (*from == '\\' && from[1] == delim)
	    from++;
	else if (*from == delim)
	    break;
	*to = *from;
    }
    *to = '\0';
    return from;
}

/* return ptr to little string in big string, NULL if not found */

char *
instr(big, little, case_matters)
char *big, *little;
int case_matters;
{
    register char *t, *s, *x;

    for (t = big; *t; t++) {
	for (x=t,s=little; *s; x++,s++) {
	    if (!*x)
		return Nullch;
	    if (case_matters == TRUE) {
		if(*s != *x)
			break;
	    } else {
		register char c,d;
		if (isupper(*s)) 
			c = tolower(*s);
		else
			c = *s;
		if (isupper(*x)) 
			d = tolower(*x);
		else
			d = *x;
		if ( c != d )
			break;
	   }
	}
	if (!*s)
	    return t;
    }
    return Nullch;
}

/* effective access */

#ifdef SETUIDGID
int
eaccess(filename, mod)
char *filename;
int mod;
{
    int protection, euid;
    
    mod &= 7;				/* remove extraneous garbage */
    if (stat(filename, &filestat) < 0)
	return -1;
    euid = geteuid();
    if (euid == ROOTID)
	return 0;
    protection = 7 & (filestat.st_mode >>
      (filestat.st_uid == euid ? 6 :
        (filestat.st_gid == getegid() ? 3 : 0)
      ));
    if ((mod & protection) == mod)
	return 0;
    errno = EACCES;
    return -1;
}
#endif

/*
 * Get working directory
 */
#ifndef GETWD
#ifdef GETCWD
char *
getwd(np)
char *np;
{
    char * name;
	extern char * getcwd();
    name = getcwd(np,512);
    return(name);
}
#else
char *
getwd(np)			/* shorter but slower */
char *np;
{
    FILE *popen();
    FILE *pipefp = popen("/bin/pwd","r");

    if (pipefp == Nullfp) {
	printf("Can't run /bin/pwd\n") FLUSH;
	finalize(1);
    }
    fgets(np,512,pipefp);
    np[strlen(np)-1] = '\0';	/* wipe out newline */
    pclose(pipefp);
    return np;
}
#endif
#endif
/* just like fgets but will make bigger buffer as necessary */

char *
get_a_line(original_buffer,buffer_length,fp)
char *original_buffer;
register int buffer_length;
FILE *fp;
{
    register int bufix = 0;
    register int nextch;
    register char *some_buffer_or_other = original_buffer;

    do {
	if (bufix >= buffer_length) {
	    buffer_length *= 2;
	    if (some_buffer_or_other == original_buffer) {
					/* currently static? */
		some_buffer_or_other = safemalloc((MEM_SIZE)buffer_length+1);
		strncpy(some_buffer_or_other,original_buffer,buffer_length/2);
					/* so we must copy it */
	    }
	    else {			/* just grow in place, if possible */
		some_buffer_or_other = saferealloc(some_buffer_or_other,
		    (MEM_SIZE)buffer_length+1);
	    }
	}
	if ((nextch = getc(fp)) == EOF)
	    return Nullch;
	some_buffer_or_other[bufix++] = (char) nextch;
    } while (nextch && nextch != '\n');
    some_buffer_or_other[bufix] = '\0';
    len_last_line_got = bufix;
    return some_buffer_or_other;
}

/* copy a string to a safe spot */

char *
savestr(str)
char *str;
{
    register char *newaddr = safemalloc((MEM_SIZE)(strlen(str)+1));

    strcpy(newaddr,str);
    return newaddr;
}

int
makedir(dirname,nametype)
register char *dirname;
int nametype;
{
#ifdef MAKEDIR
    register char *end;
    register char *s;
    char tmpbuf[1024];
    register char *tbptr = tmpbuf+5;

    for (end = dirname; *end; end++) ;	/* find the end */
    if (nametype == MD_FILE) {		/* not to create last component? */
	for (--end; end != dirname && *end != '/'; --end) ;
	if (*end != '/')
	    return 0;			/* nothing to make */
	*end = '\0';			/* isolate file name */
    }
    strcpy(tmpbuf,"mkdir");

    s = end;
    for (;;) {
	if (stat(dirname,&filestat) >= 0 && (filestat.st_mode & S_IFDIR)) {
					/* does this much exist as a dir? */
	    *s = '/';			/* mark this as existing */
	    break;
	}
	s = rindex(dirname,'/');	/* shorten name */
	if (!s)				/* relative path! */
	    break;			/* hope they know what they are doing */
	*s = '\0';			/* mark as not existing */
    }
    
    for (s=dirname; s <= end; s++) {	/* this is grody but efficient */
	if (!*s) {			/* something to make? */
	    sprintf(tbptr," %s",dirname);
	    tbptr += strlen(tbptr);	/* make it, sort of */
	    *s = '/';			/* mark it made */
	}
    }
    if (nametype == MD_DIR)		/* don't need final slash unless */
	*end = '\0';			/*  a filename follows the dir name */

    return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf));
					/* exercise our faith */
#else
    sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype);
    return doshell(sh,cmd_buf);
#endif
}

#ifdef SETENV
static bool firstsetenv = TRUE;
extern char **environ;

void
setenv(nam,val)
char *nam, *val;
{
    register int i=envix(nam);		/* where does it go? */

    if (!environ[i]) {			/* does not exist yet */
	if (firstsetenv) {		/* need we copy environment? */
	    int j;
#ifndef lint
	    char **tmpenv = (char**)	/* point our wand at memory */
		safemalloc((MEM_SIZE) (i+2) * sizeof(char*));
#else
	    char **tmpenv = Null(char **);
#endif /* lint */
    
	    firstsetenv = FALSE;
	    for (j=0; j<i; j++)		/* copy environment */
		tmpenv[j] = environ[j];
	    environ = tmpenv;		/* tell exec where it is now */
	}
#ifndef lint
	else
	    environ = (char**) saferealloc((char*) environ,
		(MEM_SIZE) (i+2) * sizeof(char*));
					/* just expand it a bit */
#endif /* lint */
	environ[i+1] = Nullch;	/* make sure it's null terminated */
    }
    environ[i] = safemalloc((MEM_SIZE) strlen(nam) + strlen(val) + 2);
					/* this may or may not be in */
					/* the old environ structure */
    sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */
}

int
envix(nam)
char *nam;
{
    register int i, len = strlen(nam);

    for (i = 0; environ[i]; i++) {
	if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
	    break;			/* strnEQ must come first to avoid */
    }					/* potential SEGV's */
    return i;
}
#endif

void
notincl(feature)
char *feature;
{
    printf("\nNo room for feature \"%s\" on this machine.\n",feature) FLUSH;
}

char *
getval(nam,def)
char *nam,*def;
{
    char *val;

    if ((val = getenv(nam)) == Nullch || !*val)
	val = def;
    return val;
}

/* grow a static string to at least a certain length */

void
growstr(strptr,curlen,newlen)
char **strptr;
int *curlen;
int newlen;
{
    if (newlen > *curlen) {		/* need more room? */
	if (*curlen)
	    *strptr = saferealloc(*strptr,(MEM_SIZE)newlen);
	else
	    *strptr = safemalloc((MEM_SIZE)newlen);
	*curlen = newlen;
    }
}

void
setdef(buffer,dflt)
char *buffer,*dflt;
{
#ifdef STRICTCR
    if (*buffer == ' ')
#else
    if (*buffer == ' ' || *buffer == '\n')
#endif
    {
	if (*dflt == '^' && isupper(dflt[1]))
	    *buffer = Ctl(dflt[1]);
	else
	    *buffer = *dflt;
    }
}

#ifdef SERVER
int nntp_get(buf, len)
char *buf;
int  len;
{
 	int n;
#ifdef HAVESIGHOLD
 	sighold(SIGINT);
#endif
 	n = get_server(buf, len);
#ifdef HAVESIGHOLD
 	sigrelse(SIGINT);
#endif
 	return n;
}
#endif
!STUFFY!FUNK!
echo ""
echo "End of kit 3 (of 11)"
cat /dev/null >kit3isdone
config=true
for iskit in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit
