#! /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 6 (of 11).  If kit 6 is complete, the line"
echo '"'"End of kit 6 (of 11)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting respond.c
cat >respond.c <<'!STUFFY!FUNK!'
/* $Id: respond.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: respond.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.1.1  1991/09/25  19:38:08  sob
 * Some adaptions for CNEWS
 *
 * 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 or 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 "intrp.h"
#include "head.h"
#include "term.h"
#include "ng.h"
#include "util.h"
#include "rn.h"
#include "artio.h"
#include "final.h"
#include "bits.h"
#include "bits.h"
#include "uudecode.h"
#include "INTERN.h"
#include "respond.h"

static char nullart[] = "\nNull article\n";

void
respond_init()
{
    ;
}

int
save_article()
{
    bool use_pref, cut_line();
    register char *s, *c;
    char altbuf[CBUFLEN];
    int iter;
    bool interactive = (buf[1] == FINISHCMD);
    char cmd = *buf;
    
    if (!finish_command(interactive))	/* get rest of command */
	return SAVE_ABORT;
    if ((use_pref = isupper(cmd)) != 0)
	cmd = tolower(cmd);
#ifdef ASYNC_PARSE
    parse_maybe(art);
#endif
    savefrom = (cmd == 'w' || cmd == 'e' ? htype[PAST_HEADER].ht_minpos : 0);
    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\n\
Saving null articles is not very productive!  :-)\n\
",stdout) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout) FLUSH;
#endif
	return SAVE_DONE;
    }
    if (chdir(cwd)) {
	printf(nocd,cwd) FLUSH;
	sig_catcher(0);
    }
    if (cmd == 'e') {		/* is this an extract command? */
	static bool custom_extract = FALSE;
	int cnt = 0;
	bool found_cut = FALSE;
	char art_buf[LBUFLEN], *cmdstr;

	s = buf+1;		/* skip e */
	while (*s == ' ') s++;	/* skip leading spaces */
	safecpy(altbuf,filexp(s),sizeof altbuf);
	s = altbuf;
	if (*s) {
	    cmdstr = cpytill(buf,s,'|');	/* check for | */
	    s = buf + strlen(buf)-1;
	    while (*s == ' ') s--;		/* trim trailing spaces */
	    *++s = '\0';
	    if (*cmdstr) {
		s = cmdstr+1;			/* skip | */
		while (*s == ' ') s++;
		if (*s)	{			/* if new command, use it */
		    if (extractprog)
			free(extractprog);
		    extractprog = savestr(s);	/* put extracter in %e */
		}
		else
		    cmdstr = extractprog;
	    }
	    else
		cmdstr = Nullch;
	    s = buf;
	}
	else {
	    if (extractdest)
		strcpy(s, extractdest);
	    if (custom_extract)
		cmdstr = extractprog;
	    else
		cmdstr = Nullch;
	}
	if (cmdstr) {
	    if (strEQ(extractprog,"-"))
		cmdstr = Nullch;
	    else if (uu_out != Nullfp)
		uud_end();
	}
	custom_extract = (cmdstr != 0);

	fseek(artfp,savefrom,0);
	if (*s != '/') {		/* relative path? */
	    c = (s==buf ? altbuf : buf);
	    interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR));
	    if (makedir(c,MD_DIR))	/* ensure directory exists */
		strcpy(c,cwd);
	    if (*s) {
		while (*c) c++;
		*c++ = '/';
		strcpy(c,s);		/* add filename */
	    }
	    s = (s==buf ? altbuf : buf);
	}
	if (*s != '/') {		/* path still relative? */
	    c = (s==buf ? altbuf : buf);
	    sprintf(c, "%s/%s", cwd, s);
	    s = c;			/* absolutize it */
	}
	if (uu_out != Nullfp) {
	    printf("Continuing %s:%s\n", uu_fname,
		cmd != '\0' && strNE(extractdest,s) ?
		 " (Ignoring conflicting directory)" : nullstr ) FLUSH;
	    uudecode(artfp);
	}
	else {
	    if (extractdest)
		free(extractdest);
	    s = extractdest = savestr(s); /* make it handy for %E */
	    if (makedir(s, MD_DIR)) {	/* ensure directory exists */
		int_count++;
		return SAVE_DONE;
	    }
	    if (chdir(s)) {
		printf(nocd,s) FLUSH;
		sig_catcher(0);
	    }
	    s = getwd(buf);		/* simplify path for output */
	    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
		if (*art_buf <= ' ')
		    continue;	/* Ignore empty or initially-whitespace lines */
		if (found_cut && custom_extract) {
		    printf("Extracting data into %s using %s:\n",
			s, extractprog) FLUSH;
		    goto extract_it;
		}
		if (((*art_buf == '#' || *art_buf == ':')
		  && (strnEQ(art_buf+1, "! /bin/sh", 9)
		   || strnEQ(art_buf+1, "!/bin/sh", 8)
		   || strnEQ(art_buf+2, "This is ", 8)))
		 || strnEQ(art_buf, "sed ", 4)
		 || strnEQ(art_buf, "cat ", 4)
		 || strnEQ(art_buf, "echo ", 5)) {
		    fseek(artfp,-(long)strlen(art_buf)-NL_SIZE+1,1);
		    savefrom = ftell(artfp);
		    if (custom_extract) {
			printf("Extracting shar into %s using %s:\n",
				s, extractprog) FLUSH;
			goto extract_it;
		    }
		    /* Check for special-case of shar'ed-uuencoded file */
		    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
			if (*art_buf == '#' || *art_buf == ':'
			 || strnEQ(art_buf, "echo ", 5)
			 || strnEQ(art_buf, "sed ", 4))
			    continue;
			if (strnEQ(art_buf, "Xbegin ", 7))
			    goto uu_decode;
			break;
		    }
		    printf("Extracting shar into %s:\n", s) FLUSH;
		    if (extractprog)
			free(extractprog);
		    extractprog = savestr(filexp(getval("UNSHAR",UNSHAR)));
		  extract_it:
		    cnt = 0;
		    interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER));
		    termlib_reset();
		    resetty();		/* restore tty state */
		    doshell(SH,cmd_buf);
		    noecho();		/* revert to cbreaking */
		    crmode();
		    termlib_init();
		    break;
		}
		else
		if (!custom_extract
		 && (strnEQ(art_buf,"table\n", 6)
		  || strnEQ(art_buf,"begin ", 6))) {
		 uu_decode:
		    printf("Extracting uuencoded file into %s:\n", s) FLUSH;
		    if (extractprog)
			free(extractprog);
		    extractprog = savestr("-");
		    cnt = 0;
		    fseek(artfp,(long)-strlen(art_buf)-NL_SIZE+1,1);
		    savefrom = ftell(artfp);
		    uud_start(extractdest);
		    uudecode(artfp);
		    break;
		}
		else {
		    if (cut_line(art_buf)) {
			savefrom = ftell(artfp);
			found_cut = TRUE;
		    }
		    else if (found_cut || ++cnt == 300) {
			break;
		    }
		}
	    }/* while */
	    if (cnt) {
		if (custom_extract)
		    printf("Didn't find cut line for extraction to '%s'.\n",
			extractprog) FLUSH;
		else
		    printf("Unable to determine type of file.\n") FLUSH;
	    }
	}/* if */
    }
    else if ((s = index(buf,'|')) != Nullch) {
				/* is it a pipe command? */
	s++;			/* skip the | */
	while (*s == ' ') s++;
	safecpy(altbuf,filexp(s),sizeof altbuf);
	if (savedest)
	    free(savedest);
	savedest = savestr(altbuf);
	interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER));
				/* then set up for command */
	termlib_reset();
	resetty();		/* restore tty state */
	if (use_pref)		/* use preferred shell? */
	    doshell(Nullch,cmd_buf);
				/* do command with it */
	else
	    doshell(sh,cmd_buf);	/* do command with sh */
	noecho();		/* and stop echoing */
	crmode();		/* and start cbreaking */
	termlib_init();
    }
    else {			/* normal save */
	bool there, mailbox;
	char *savename = getval("SAVENAME",SAVENAME);

	s = buf+1;		/* skip s or S */
	if (*s == '-') {	/* if they are confused, skip - also */
#ifdef VERBOSE
	    IF(verbose)
		fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout)
		  FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("'-' ignored.\n",stdout) FLUSH;
#endif
	    s++;
	}
	for (; *s == ' '; s++);	/* skip spaces */
	safecpy(altbuf,filexp(s),sizeof altbuf);
	s = altbuf;
	if (! index(s,'/')) {
	    interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
	    if (makedir(buf,MD_DIR))	/* ensure directory exists */
		strcpy(buf,cwd);
	    if (*s) {
		for (c = buf; *c; c++) ;
		*c++ = '/';
		strcpy(c,s);		/* add filename */
	    }
	    s = buf;
	}
	for (iter = 0;
	    (there = stat(s,&filestat) >= 0) &&
	    (filestat.st_mode & S_IFDIR);
	    iter++) {			/* is it a directory? */

	    c = (s+strlen(s));
	    *c++ = '/';			/* put a slash before filename */
	    interp(c, s==buf?(sizeof buf):(sizeof altbuf),
		iter ? "News" : savename );
				/* generate a default name somehow or other */
	}
	makedir(s,MD_FILE);
	if (*s != '/') {		/* relative path? */
	    c = (s==buf ? altbuf : buf);
	    sprintf(c, "%s/%s", cwd, s);
	    s = c;			/* absolutize it */
	}
	if (savedest)
	    free(savedest);
	s = savedest = savestr(s);	/* doesn't move any more */
					/* make it handy for %b */
	if (!there) {
	    if (mbox_always)
		mailbox = TRUE;
	    else if (norm_always)
		mailbox = FALSE;
	    else {
		char *dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq");
		
		sprintf(cmd_buf,
		"\nFile %s doesn't exist--\n	use mailbox format? [%s] ",
		  s,dflt);
	      reask_save:
		in_char(cmd_buf, 'M');
		putchar('\n') FLUSH;
		setdef(buf,dflt);
#ifdef VERIFY
		printcmd();
#endif
		if (*buf == 'h') {
#ifdef VERBOSE
		    IF(verbose)
			printf("\n\
Type y to create %s as a mailbox.\n\
Type n to create it as a normal file.\n\
Type q to abort the save.\n\
",s) FLUSH;
		    ELSE
#endif
#ifdef TERSE
			fputs("\n\
y to create mailbox.\n\
n to create normal file.\n\
q to abort.\n\
",stdout) FLUSH;
#endif
		    goto reask_save;
		}
		else if (*buf == 'n') {
		    mailbox = FALSE;
		}
		else if (*buf == 'y') {
		    mailbox = TRUE;
		}
		else if (*buf == 'q') {
		    goto s_bomb;
		}
		else {
		    fputs(hforhelp,stdout) FLUSH;
		    settle_down();
		    goto reask_save;
		}
	    }
	}
	else if (filestat.st_mode & S_IFCHR)
	    mailbox = FALSE;
	else {
	    int tmpfd;
	    
	    tmpfd = open(s,0);
	    if (tmpfd == -1)
		mailbox = FALSE;
	    else {
		if (read(tmpfd,buf,LBUFLEN)) {
		    c = buf;
		    if (!isspace(MBOXCHAR))   /* if non-zero, */
			while (isspace(*c))   /* check the first character */
			    c++;
		    mailbox = (*c == MBOXCHAR);
		} else {
		    mailbox = mbox_always;    /* if zero length, recheck -M */
		}
		close(tmpfd);
	    }
	}

	safecpy(cmd_buf, filexp(mailbox ?
	    getval("MBOXSAVER",MBOXSAVER) :
	    getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
				/* format the command */
	termlib_reset();
	resetty();		/* make terminal behave */
	if (doshell(use_pref?Nullch:SH,cmd_buf)) {
	    termlib_init();
	    fputs("Not saved",stdout);
	} else {
	    termlib_init();
	    printf("%s to %s %s",
	      there?"Appended":"Saved",
	      mailbox?"mailbox":"file",
	      s);
	}
	if (interactive)
	    putchar('\n') FLUSH;
	noecho();		/* make terminal do what we want */
	crmode();
    }
s_bomb:
#ifdef SERVER
    if (chdir(spool)) {
#else /* not SERVER */
    if (chdir(spool) || chdir(ngdir)) {
#endif /* SERVER */
	printf(nocd,ngdir) FLUSH;
	sig_catcher(0);
    }
    return SAVE_DONE;
}

int
cancel_article()
{
    char *artid_buf;
    char *ngs_buf;
    char *from_buf;
    char *reply_buf;
    int myuid = getuid();
    int r = -1;

    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\n\
Cancelling null articles is your idea of fun?  :-)\n\
",stdout) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout) FLUSH;
#endif
	return r;
    }
    reply_buf = fetchlines(art,REPLY_LINE);
    from_buf = fetchlines(art,FROM_LINE);
    artid_buf = fetchlines(art,ARTID_LINE);
    ngs_buf = fetchlines(art,NGS_LINE);
    if (!instr(from_buf,sitename,FALSE) ||
	(!instr(from_buf,logname,TRUE) &&
	 !instr(reply_buf,logname,TRUE) &&
#ifdef NEWSADMIN
	 myuid != newsuid &&
#endif
	 myuid != ROOTID ) ) {
#ifdef DEBUGGING
	    if (debug)
		printf("\n%s@%s != %s\n",logname,sitename,from_buf) FLUSH;
#endif
#ifdef VERBOSE
	    IF(verbose)
		fputs("\nYou can't cancel someone else's article\n",stdout)
		  FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("\nNot your article\n",stdout) FLUSH;
#endif
    }
    else {
	tmpfp = fopen(headname,"w");	/* open header file */
	if (tmpfp == Nullfp) {
	    printf(cantcreate,headname) FLUSH;
	    goto no_cancel;
	}
	interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER));
	fputs(buf,tmpfp);
	fclose(tmpfp);
	fputs("\nCanceling...\n",stdout) FLUSH;
	r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
    }
no_cancel:
    free(artid_buf);
    free(ngs_buf);
    free(from_buf);
    free(reply_buf);
    return r;
}

int
supersede_article()		/* Supersedes: */
{
    char *artid_buf;
    char *ngs_buf;
    char *from_buf;
    char *reply_buf;
    int myuid = getuid();
    int r = -1;

    if (artopen(art) == Nullfp) {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\n\
Superceding null articles is your idea of fun?  :-)\n\
",stdout) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    fputs(nullart,stdout) FLUSH;
#endif
	return r;
    }
    reply_buf = fetchlines(art,REPLY_LINE);
    from_buf = fetchlines(art,FROM_LINE);
    artid_buf = fetchlines(art,ARTID_LINE);
    ngs_buf = fetchlines(art,NGS_LINE);
    if (!instr(from_buf,sitename,FALSE) ||
	(!instr(from_buf,logname,TRUE) &&
	 !instr(reply_buf,logname,TRUE) &&
#ifdef NEWSADMIN
	 myuid != newsuid &&
#endif
	 myuid != ROOTID ) ) {
#ifdef DEBUGGING
	    if (debug)
		printf("\n%s@%s != %s\n",logname,sitename,from_buf) FLUSH;
#endif
#ifdef VERBOSE
	    IF(verbose)
		fputs("\nYou can't supersede someone else's article\n",stdout)
		  FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("\nNot your article\n",stdout) FLUSH;
#endif
    }
    else {
	tmpfp = fopen(headname,"w");	/* open header file */
	if (tmpfp == Nullfp) {
	    printf(cantcreate,headname) FLUSH;
	    goto no_commute;
	}
	interp(buf, (sizeof buf), getval("SUPERSEDEHEADER",SUPERSEDEHEADER));
	fputs(buf,tmpfp);
	fclose(tmpfp);
    	safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),
		sizeof cmd_buf);
    	invoke(cmd_buf,origdir);
	r = 0;
    }
no_commute:
    free(artid_buf);
    free(ngs_buf);
    free(from_buf);
    free(reply_buf);
    return r;
}

void
reply()
{
    bool incl_body = (*buf == 'R');
    char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER)));

    artopen(art);
    tmpfp = fopen(headname,"w");	/* open header file */
    if (tmpfp == Nullfp) {
	printf(cantcreate,headname) FLUSH;
	goto no_reply;
    }
    interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER));
    fputs(buf,tmpfp);
    if (!instr(maildoer,"%h",TRUE))
#ifdef VERBOSE
	IF(verbose)
	    printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
	      FLUSH;
	ELSE
#endif
#ifdef TERSE
	    printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
#endif
    if (incl_body && artfp != Nullfp) {
	interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID));
	fprintf(tmpfp,"%s\n",buf);
#ifdef ASYNC_PARSE
	parse_maybe(art);
#endif
	fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
	    fprintf(tmpfp,"%s%s",indstr,buf);
	}
	fprintf(tmpfp,"\n");
    }
    fclose(tmpfp);
    interp(cmd_buf, (sizeof cmd_buf), maildoer);
    invoke(cmd_buf,origdir);
    UNLINK(headname);		/* kill the header file */
no_reply:
    free(maildoer);
}

void
followup()
{
    bool incl_body = (*buf == 'F');
    char hbuf[4*LBUFLEN];	/* four times the old size */
    ART_NUM oldart = art;

    if (!incl_body && art <= lastart) {
	in_char("\n\nAre you starting an unrelated topic? [yn] ", 'F');
	setdef(buf,"y");
	if (*buf != 'n')
	    art = lastart + 1;
    }
    artopen(art);
    tmpfp = fopen(headname,"w");
    if (tmpfp == Nullfp) {
	printf(cantcreate,headname) FLUSH;
	art = oldart;
	return;
    }
    interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER));
    fprintf(tmpfp,"%s",hbuf);
    if (incl_body && artfp != Nullfp) {
#ifdef VERBOSE
	if (verbose)
	    fputs("\n\
(Be sure to double-check the attribution against the signature, and\n\
trim the quoted article down as much as possible.)\n\
",stdout) FLUSH;
#endif
	interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION));
	fprintf(tmpfp,"%s\n",buf);
#ifdef ASYNC_PARSE
	parse_maybe(art);
#endif
	fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
	    fprintf(tmpfp,"%s%s",indstr,buf);
	}
	fprintf(tmpfp,"\n");
    }
    fclose(tmpfp);
    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
    invoke(cmd_buf,origdir);
    UNLINK(headname);
    art = oldart;
}

void
invoke(cmd,dir)
char *cmd,*dir;
{
    if (chdir(dir)) {
	printf(nocd,dir) FLUSH;
	return;
    }
    termlib_reset();
#ifdef VERBOSE
    IF(verbose)
	printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n",
	    dir,cmd) FLUSH;
    ELSE
#endif
#ifdef TERSE
	printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd) FLUSH;
#endif
    resetty();			/* make terminal well-behaved */
    doshell(sh,cmd);		/* do the command */
    noecho();			/* set no echo */
    crmode();			/* and cbreak mode */
#ifdef VERBOSE
    IF(verbose)
	fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH;
    ELSE
#endif
#ifdef TERSE
	fputs("\n(+cbreak)\n",stdout) FLUSH;
#endif
    termlib_init();
#ifdef SERVER
    if (chdir(spool)) {
#else /* not SERVER */
    if (chdir(spool) || chdir(ngdir)) {
#endif /* SERVER */
	printf(nocd,ngdir) FLUSH;
	sig_catcher(0);
    }
}

/*
** cut_line() determines if a line is meant as a "cut here" marker.
** Some examples that we understand:
**
**  BEGIN--cut here--cut here
**
**  ------------------ tear at this line ------------------
**
**  #----cut here-----cut here-----cut here-----cut here----#
*/
bool
cut_line(str)
char *str;
{
    char *cp, got_flag;
    char word[80];
    int  dash_cnt, equal_cnt, other_cnt;

    /* Disallow any single-/double-quoted, parenthetical or c-commented
    ** string lines.  Make sure it has the cut-phrase and at least six
    ** '-'s or '='s.  If only four '-'s are present, check for a duplicate
    ** of the cut phrase.  If over 20 unknown characters are encountered,
    ** assume it isn't a cut line.  If we succeed, return TRUE.
    */
    for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) {
	switch (*cp) {
	case '-':
	    dash_cnt++;
	    break;
	case '=':
	    equal_cnt++;
	    break;
	case '/':
	    if(*(cp+1) != '*') {
		break;
	    }
	case '"':
	case '\'':
	case '(':
	case ')':
	case '[':
	case ']':
	case '{':
	case '}':
	    return FALSE;
	default:
	    other_cnt++;
	    break;
	}
    }
    if (dash_cnt < 4 && equal_cnt < 6)
	return FALSE;

    got_flag = 0;

    for (*(cp = word) = '\0'; *str; str++) {
	if (islower(*str))
	    *cp++ = *str;
	else if (isupper(*str))
	    *cp++ = tolower(*str);
	else {
	    if (*word) {
		*cp = '\0';
		switch (got_flag) {
		case 2:
		    if (!strcmp(word, "line")
		     || !strcmp(word, "here"))
			if ((other_cnt -= 4) <= 20)
			    return TRUE;
		    break;
		case 1:
		    if (!strcmp(word, "this")) {
			got_flag = 2;
			other_cnt -= 4;
		    }
		    else if (!strcmp(word, "here")) {
			other_cnt -= 4;
			if ((dash_cnt >= 6 || equal_cnt >= 6)
			 && other_cnt <= 20)
			    return TRUE;
			dash_cnt = 6;
			got_flag = 0;
		    }
		    break;
		case 0:
		    if (!strcmp(word, "cut")
		     || !strcmp(word, "snip")
		     || !strcmp(word, "tear")) {
			got_flag = 1;
			other_cnt -= strlen(word);
		    }
		    break;
		}
		*(cp = word) = '\0';
	    }
	}
    } /* for *str */

    return FALSE;
}
!STUFFY!FUNK!
echo Extracting Pnews.SH
cat >Pnews.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . ./config.sh ;;
esac
echo "Extracting Pnews (with variable substitutions)"
$spitshell >Pnews <<!GROK!THIS!
$startsh
# $Id: Pnews.SH,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
#
# $Log: Pnews.SH,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
#
# 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. 
#
# syntax: Pnews -h headerfile			or
#	  Pnews -h headerfile oldarticle	or
#         Pnews newsgroup title			or just
#         Pnews

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

# System dependencies

mailer="${mailer-/bin/mail}"
# if you change this to something that does signatures, take out signature code

case $portable in
define)
# your site name
case "$hostcmd" in
'') sitename="$sitename" ;;
*)  sitename=\`$hostcmd\` ;;
esac
case \$sitename in
	*.*)
		;;
	*)
		sitename=\${sitename}.$domain
		;;
esac
# where recordings, distributions and moderators are kept
lib=\`$filexp $lib\`
# where important rn things are kept
rnlib=\`$filexp $rnlib\`
;;
undef)
# your site name
sitename="$sitename"
# where recordings, distributions and moderators are kept
lib="$lib"
# where important rn things are kept
rnlib="$rnlib"
;;
esac

# your organization name
orgname="$orgname"
# what pager you use--if you have kernal paging use cat
pager="\${PAGER-$pager}"
# how you derive full names, bsd, usg, or other
nametype="$nametype"
# default editor
defeditor="$defeditor"
# how not to echo with newline
n="$n"
c="$c"

# You should also look at the distribution warnings below marked !DIST!
# to make sure any distribution regions you are a member of are included.
# The following are some prototypical distribution groups.  If you do not
# use them all set the unused ones to a non-null string such as 'none'.
loc="$locpref"
org="$orgpref"
multistate="$multistatepref"
city="$citypref"
state="$statepref"
cntry="$cntrypref"
cont="$contpref"

test=${test-test}
sed=${sed-sed}
echo=${echo-echo}
cat=${cat-cat}
egrep=${egrep-egrep}
grep=${grep-grep}
rm=${rm-rm}
tr=${tr-tr}
inews=${inews-inews}
nidump=${nidump}
ypmatch=${ypmatch}

!GROK!THIS!
case "$ignoreorg" in
define) $spitshell >>Pnews <<'!NO!SUBS!'
orgname=${NEWSORG-$orgname}
!NO!SUBS!
	;;
*)	$spitshell >>Pnews <<'!NO!SUBS!'
orgname=${NEWSORG-${ORGANIZATION-$orgname}}
!NO!SUBS!
	;;
esac
$spitshell >>Pnews <<'!NO!SUBS!'
dotdir=${DOTDIR-${HOME-$LOGDIR}}
tmpart=$dotdir/.article

if $test -f $dotdir/.pnewsexpert; then
    expertise=expert
else
    $cat <<'EOM'
I see you've never used this version of Pnews before.  I will give you extra
help this first time through, but then you must remember what you learned.
If you don't understand any question, type h and a CR (carriage return) for
help.

If you've never posted an article to the net before, it is HIGHLY recommended
that you read the netiquette document found in news.announce.newusers so
that you'll know to avoid the commonest blunders.  To do that, interrupt
Pnews, and get to the top-level prompt of rn.  Type "g news.announce.newusers"
and you are on your way.

EOM
    expertise=beginner
fi

case $cntry in
  can) Stpr=Province ; stpr=province ;;
  *)   Stpr=State ; stpr=state ;;
esac

case $multistate in
  pnw) multistpr="Pacific NorthWest" ;;
  *)   multistpr="Multi-State Area" ;;
esac

headerfile=""
case $# in
0) ;;
*)  case $1 in
    -h)
	headerfile="$2"
	shift
	shift
	case $# in
	0)
	    oldart=""
	    ;;
	*)
	    oldart="$1"
	    shift
	    ;;
	esac
	;;
    esac
    ;;
esac

case $headerfile in
'')
    . $rnlib/Pnews.header
    ;;
*)
    $cat < $headerfile  > $tmpart
    ;;
esac
    rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo Article appended to ${HOME-$LOGDIR}/dead.article ; exit"
    trap "$rescue" 1
    trap "$rescue" 2

$echo ""

# extract the newsgroups list and distribution
hdr_newsgroups=`$sed -n -e '/^Newsgroups:/{' -e 's///' -e 's/,/ /g' -e p -e q -e '}' $tmpart`
hdr_distribution=`$sed -n -e '/^Distribution:/{' -e 's///' -e p -e q -e '}' $tmpart`

# check for "poster" magic cookie
flag=0
for ng in $hdr_newsgroups ; do
    case "$ng" in
	poster)	flag=1 ;;
	*)	;;
    esac
done
case $flag in
1)
    $echo " "
    $echo "The original author has requested that messages be sent back via"
    $echo "mail rather than posting to news.  Do you want to jump out of this"
    $echo $n "and mail your reply instead? [yn] $c"
    read ans
    case $ans in
	n*) ;;
	*)  exit ;;
    esac
    $echo " "
    $echo "OK, but you will have to edit the 'Newsgroups:' line in the message."
    ;;
esac
  
# play recorded message
if $test -s ${lib}/recording ; then
     for ng in $hdr_newsgroups ; do
	_rec1=${lib}/`$sed -n "/^$ng/s/^.*	//p" ${lib}/recording`
	_tmp=`$echo $ng |$sed "s/\..*//"`
	_rec2=${lib}/`$cat -s ${lib}/recording|$grep ${_tmp}.all|$sed "s/^.*	//"`
	if $test -f ${_rec1} ; then
	    $cat -s ${_rec1}
	fi
	if $test -f ${_rec2} ; then
	    $cat -s ${_rec2}
	fi
    done
fi

# determine the distribution of this message
set X $hdr_distribution
shift
if $test $# -gt 0 ; then
    dist=$1.whatever
else
    set X $hdr_newsgroups
    shift
    if $test $# -gt 0 ; then
	dist=$1.whatever
    else
	dist=misc.whatever
    fi
fi
case $dist in
*.*)
    ;;
*)
    dist=$dist.whatever
    ;;
esac

# tell them what we think they are doing... !DIST!
case $dist in
world.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*)
    $cat <<'EOM'
This program posts news to thousands of machines throughout the entire
civilized world.  Your message will cost the net hundreds if not thousands of
dollars to send everywhere.  Please be sure you know what you are doing.

EOM
    ;;
vmsnet.*)
    $echo 'This program posts news to many machines.'
    ;;
bit.*)
    $echo 'This program posts news to many machines on BITNET.'
    ;;
ddn.*)
    $echo 'This program posts news to many machines throughout the internet.'
    ;;
$cont.*)
    $echo 'This program posts news to many machines throughout the continent.'
    ;;
$cntry.*)
    $echo 'This program posts news to many machines throughout the country.'
    ;;
$multistate.*)
    $echo "This program posts news to many machines throughout the ${multistpr}."
    ;;
$state.*)
    $echo "This program posts news to many machines throughout the ${stpr}."
    ;;
$city.*)
    $echo 'This program posts news to many machines throughout the city.'
    ;;
$org.*)
    $echo 'This program posts news to machines throughout the organization.'
    ;;
$loc.*)
    $echo 'This program posts news to machines throughout the local organization.'
    ;;
*.*)
    $echo 'This program may post news to many machines.'
    ;;
to.*)
    $echo 'This program may post news to a particular machine.'
    ;;
*)
    $echo 'This program posts news to everyone on the machine.'
    ;;
esac
ans=""
while $test "$ans" = "" ; do
    $echo $n "Are you absolutely sure that you want to do this? [ny] $c"
    read ans
    case $ans in
    y*) ;;
    f*) ;;
    h*) $cat <<'EOH'

Type n or CR to exit, y to post.

EOH
	ans="" ;;
    *) exit ;;
    esac
done

file=h
while $test "$file" = h ; do
    $echo ""
    $echo $n "Prepared file to include [none]: $c"
    read file
    case $file in
    h)
	$cat <<'EOH'

If you have already produced the body of your article, type the filename
for it here.  If you just want to proceed directly to the editor, type a
RETURN.  In any event, you will be allowed to edit as many times as you
want before you send off the article.
EOH
	;;
    '')
	$echo "" >> $tmpart
	state=edit
	;;
    *)
	$cat $file >>$tmpart
	state=ask
	;;
    esac
done

$echo ""

while true ; do
    case $state in
    edit)
	case $expertise in
	beginner)
	    $cat </dev/null >$dotdir/.pnewsexpert
	    $cat <<'EOMessage'
A temporary file has been created for you to edit.  Be sure to leave at
least one blank line between the header and the body of your message.
(And until a certain bug is fixed all over the net, don't start the body of
your message with any indentation, or it may get eaten.)

Within the header may be fields that you don't understand.  If you don't
understand a field (or even if you do), you can simply leave it blank, and
it will go away when the article is posted.

Type return to get the default editor, or type the name of your favorite
editor.

EOMessage
	    ;;
	esac
	case "${VISUAL-${EDITOR-}}" in
	'')
	    tmp=h
	    ;;
	*)
	    tmp=''
	    ;;
	esac
	while $test "$tmp" = h ; do
	    $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c"
	    read tmp
	    case $tmp in
	    h)
		$cat <<'EOH'

Type a return to get the default editor, or type the name of the editor you
prefer.  The default editor depends on the VISUAL and EDITOR environment
variables.

EOH
		;;
	    '')
		;;
	    *)
		VISUAL=$tmp
		export VISUAL
		;;
	    esac
	done
	trap : 2
	${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
	trap "$rescue" 2
	state=ask
	;;
	
    ask)
	$echo ""
	$echo $n "Send, abort, edit, or list? $c"
	read ans
	
	case "$ans" in
	a*)
	    state=rescue
	    ;;
	e*)
	    set $ans
	    case $# in
	    2)  VISUAL="$2" ;;
	    esac
	    state=edit
	    ;;
	l*)
	    $pager $tmpart
	    state=ask
	    ;;
	s*)
	    state=send
	    ;;
	h*)
	    $cat <<'EOH'

Type s to send the article, a to abort and append the article to dead.article,
e to edit the article again, or l to list the article.

To invoke an alternate editor, type 'e editor'.
EOH
	esac
	;;
    
    send)
	set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
	shift
	case $# in
	2)
	    state=cleanup
	    if $test -f $lib/moderators; then
		tryinews=no
		shift
		case "$1" in
		*,*) set `$echo $1 | tr ',' ' '`;;
		esac
		for newsgroup in $*; do
# the following screwy sed should prevent Eunice from hanging on no match
		    moderator=`$sed <$lib/moderators \
		    -e "/^$newsgroup[ 	]/!s/.*//" \
		    -e "s/^$newsgroup[ 	]//"`
		    case ${moderator}X in
		    X)  tryinews=yes
			;;
		    *)
			$echo Mailing to moderator $moderator
			case "$sign" in
			n*) ;;
			*)
			    if $test -f $dotdir/.signature; then
				$echo $n "Append .signature file? [y] $c"
				read ans
				case $ans in
				''|y*)
				    $echo "-- " >> $tmpart
				    $cat $dotdir/.signature >> $tmpart
				    ;;
				esac
			    fi
			    sign=no
			    ;;
			esac
			case "$mailer" in
			*recmail)
			    $echo To: $moderator | $cat - $tmpart | $mailer
			    ;;
			*)
			    $mailer $moderator < $tmpart
			    ;;
			esac
			case $? in
			0) ;;
			*)
			    $echo Unable to mail to moderator $moderator
			    state=rescue
			    ;;
			esac
			;;
		    esac
		done
	    else
		tryinews=yes
	    fi
	    case "$tryinews" in
	    yes)
		if $sed '1,/^[	 ]*$/{/^[A-Z][-A-Za-z0-9]*:[	 ]*$/d;}' $tmpart |
			$inews -h ; then
		    : null
		else
		    state=rescue
		fi
		;;
	    esac
	    ;;
	*)
	    $echo ""
	    $echo "Malformed Newsgroups line."
	    $echo ""
	    sleep 1
	    state=edit
	    ;;
	esac
	;;
    rescue)
	if $test -s $tmpart; then
		$cat $tmpart >> ${HOME-$LOGDIR}/dead.article
		$echo "Article appended to ${HOME-$LOGDIR}/dead.article"
		$echo "A copy may be temporarily found in $tmpart"
	else
		$echo "Null article discarded."
	fi
	exit
	;;
    cleanup)
	case "${AUTHORCOPY-none}" in
	none)
	    ;;
	*)
	    set X ${USER-${LOGNAME-`who am i`}} unknown
	    shift
	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `date`"
	    if $test $? -eq 0 ; then
		$echo "Article appended to $AUTHORCOPY"
	    else
		$echo "Cannot append to $AUTHORCOPY"
	    fi
	    ;;
	esac
	exit
	;;
    esac
done
!NO!SUBS!
$eunicefix Pnews
chmod 755 Pnews
$spitshell >Pnews.header <<'!NO!SUBS!'
case $# in
0)
    ng=h
    while $test "$ng" = h ; do
	$echo ""
	$echo $n "Newsgroup(s): $c"
	read ng
	case $ng in
	h)
	    $cat <<'EOH'

Type the name of one or more newsgroups to which you wish to post an article.
If you want to post to multiple newsgroups, it is better to do them all at
once than to post to each newsgroup individually, which defeats the news
reading programs' strategies of eliminating duplicates.

Separate multiple newsgroup names with commas.
EOH
	    ;;
	esac
    done
    ;;
*)
    ng=$1
    shift
    ;;
esac
case $ng in
*\ *)
    ng=`$echo "$ng" | $sed 's/[, ] */,/g'`
    ;;
esac
case $ng in
bit.*|pubnet.*|bionet.*|vmsnet.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*)
    defdist=world
    dist=h
    ;;
ddn.*)
    defdist=inet
    dist=h
    ;;
to.*)
    defdist=''
    dist=''
    ;;
*.*)
    defdist=`expr "X$ng" : 'X\([a-z0-9]*\)'`
    dist=h
    ;;
*)
    defdist=''
    dist=''
    ;;
esac

while $test "$dist" = h ; do
    if $test -f $lib/distributions; then
	$echo " "
	$echo "Your local distribution prefixes are:"
	$cat $lib/distributions
	$echo " "
    else
	$egrep -v '[	 ]none$' <<EOM

Your local distribution prefixes are:
    Local organization:	$loc
    Organization:	$org
    City:		$city
    $Stpr:  		$state
    $multistpr:	$multistate
    Country:		$cntry
    Continent:		$cont
    Everywhere:		world

EOM
    fi
    $echo $n "Distribution ($defdist): $c"
    read dist
    case $dist in
    '') dist=$defdist ;;
    esac
    case $dist in
    h)
	$cat <<'EOH'

The Distribution line may be used to limit the distribution of an article
to some subset of the systems that would receive the article based only on
the Newsgroups line.  For example, if you want to sell your car in talk.auto,
and you live in New Jersey, you might want to put "nj" on the Distribution
line to avoid advertising in California, which has enough problems of its own.
The actual area designators to use depend on where you are, of course.
EOH
	;;
    ''|$loc*|$org*|$city*|$state*|$multistate*|$cntry*|$cont*|$defdist)
	;;
    world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*)
	dist=''
	;;
    *)  
	if $test -f $lib/distributions && \
	  $egrep "^$dist[ 	]" $lib/distributions >$tmpart && \
	  $test -s $tmpart; then
	    : null
	else
	    $echo "Unrecognized distribution prefix--type h for help, CR to use anyway."
	    defdist=$dist
	    dist=h
	fi
	;;
    esac
done

follow=""

# LCP 16-Oct-91 Subject line is required.  Make it a little more
# difficult to omit.  Added "while : ; do", ... "done", and "if"
# at end of while loop.
while :
do
  case $# in
  0)
    title=h
    while $test "$title" = h ; do
	$echo ""
	$echo $n "Title/Subject: $c"
	read title
	case $title in
	h)
	    $cat <<'EOH'

Type the title for your article.  Please make it as informative as possible
(within reason) so that people who aren't interested won't have to read the
article to find out they aren't interested.  This includes marking movie
spoilers as (spoiler), and rotated jokes as (rot 13).
EOH
	;;
	esac
    done
    ;;
  *)
    title="$*"
    # LCP 16-Oct-91 Added "set" and "shift".  Must insure $# is 0
    # in case the title is all white space and we make another
    # pass thru this loop.
    set X
    shift
    ;;
  esac
  if expr "X$title" : "^X[    ]*$" > /dev/null 2>&1
  then
    $cat <<'EOH'

Articles without a "Subject:" line will not be accepted by the News
system.  Please give a Title/Subject line for your article.
EOH
  else
     break
  fi
done


# now build a file with a header for them to edit

set X ${USER-${LOGNAME-`who am i`}}
shift
logname=$1
case $logname in
*!*) logname=`expr "$logname" : '!\(.*\)$'` ;;
esac
case ${NAME-$nametype} in
bsd)
	if $test "$ypmatch" != ""; then
		fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"`
	elif $test "$nidump" != ""; then
		fullname=`$nidump passwd / | $sed -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
	fi
     if $test "$fullname" = ""; then
		fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
	fi
    case $fullname in
    *'&'*) : GACK
	lname=`$echo $logname | $tr 'a-z' 'A-Z'`
	lname=`$echo $lname $logname | $sed 's/^\(.\)[^ ]* ./\1/'`
	fullname=`$echo "$fullname" | $sed "s/&/${lname}/"`
	;;
    esac
    ;;
usg)
	if $test "$ypmatch" != ""; then
		fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"`
	fi
     if $test "$fullname" = ""; then
    fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q" -e "}" -e "d"`
	fi
    ;;
*)
    fullname=${NAME-`$cat $dotdir/.fullname`}
    ;;
esac

case $orgname in
/*) orgname=`$cat $orgname` ;;
esac

$sed -e '/^Reply-To: $/d' > $tmpart <<EOHeader
Newsgroups: $ng
Subject: $title
Summary: 
Reply-To: $REPLYTO
Followup-To: $follow
Distribution: $dist
Organization: $orgname
Keywords: 

EOHeader

!NO!SUBS!
case "$isrrn" in
define) sed < Pnews.header -e '/^#NORMAL/d' > Pnews.h.new ;;
*)  sed < Pnews.header -e '/^#NORMAL/s/^#NORMAL//' > Pnews.h.new ;;
esac
mv Pnews.h.new Pnews.header
$eunicefix Pnews.header
!STUFFY!FUNK!
echo Extracting bits.c
cat >bits.c <<'!STUFFY!FUNK!'
/* $Id: bits.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: bits.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: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 "rcstuff.h"
#include "head.h"
#include "util.h"
#include "final.h"
#include "rn.h"
#include "cheat.h"
#include "ng.h"
#include "artio.h"
#include "intrp.h"
#include "ngdata.h"
#include "rcln.h"
#include "kfile.h"
#include "INTERN.h"
#include "bits.h"

#ifdef DBM
#    ifdef NULL
#	undef NULL
#    endif
#    include <dbm.h>
#endif
MEM_SIZE ctlsize;			/* size of bitmap in bytes */

void
bits_init()
{
#ifdef DELAYMARK
    dmname = savestr(filexp(RNDELNAME));
#else
    ;
#endif
}

/* checkpoint the .newsrc */

void
checkpoint_rc()
{
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(ckpt)",stdout);
	fflush(stdout);
    }
#endif
    if (doing_ng)
	restore_ng();			/* do not restore M articles */
    if (rc_changed)
	write_rc();
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(done)",stdout);
	fflush(stdout);
    }
#endif
}

/* reconstruct the .newsrc line in a human readable form */

void
restore_ng()
{
    register char *s, *mybuf = buf;
    register ART_NUM i;
    ART_NUM count=0;
    int safelen = LBUFLEN - 16;

    strcpy(buf,rcline[ng]);		/* start with the newsgroup name */
    s = buf + rcnums[ng] - 1;		/* use s for buffer pointer */
    *s++ = rcchar[ng];			/* put the requisite : or !*/
    *s++ = ' ';				/* put the not-so-requisite space */
    for (i=1; i<=lastart; i++) {	/* for each article in newsgroup */
	if (s-mybuf > safelen) {	/* running out of room? */
	    safelen *= 2;
	    if (mybuf == buf) {		/* currently static? */
		*s = '\0';
		mybuf = safemalloc((MEM_SIZE)safelen + 16);
		strcpy(mybuf,buf);	/* so we must copy it */
		s = mybuf + (s-buf);
					/* fix the pointer, too */
	    }
	    else {			/* just grow in place, if possible */
		char *newbuf;

		newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
		s = newbuf + (s-mybuf);
		mybuf = newbuf;
	    }
	}
	if (!was_read(i))		/* still unread? */
	    count++;			/* then count it */
	else {				/* article was read */
	    ART_NUM oldi;

	    sprintf(s,"%ld",(long)i);	/* put out the min of the range */
	    s += strlen(s);		/* keeping house */
	    oldi = i;			/* remember this spot */
	    do i++; while (i <= lastart && was_read(i));
					/* find 1st unread article or end */
	    i--;			/* backup to last read article */
	    if (i > oldi) {		/* range of more than 1? */
		sprintf(s,"-%ld,",(long)i);
					/* then it out as a range */
		s += strlen(s);		/* and housekeep */
	    }
	    else
		*s++ = ',';		/* otherwise, just a comma will do */
	}
    }
    if (*(s-1) == ',')			/* is there a final ','? */
	s--;				/* take it back */
    *s++ = '\0';			/* and terminate string */
#ifdef DEBUGGING
    if (debug & DEB_NEWSRC_LINE && !panic) {
	printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH;
	printf("%s\n",mybuf) FLUSH;
    }
#endif
    free(rcline[ng]);			/* return old rc line */
    if (mybuf == buf) {
	rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
					/* grab a new rc line */
	strcpy(rcline[ng], buf);	/* and load it */
    }
    else {
	mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
					/* be nice to the heap */
	rcline[ng] = mybuf;
    }
    *(rcline[ng] + rcnums[ng] - 1) = '\0';
    if (rcchar[ng] == NEGCHAR) {	/* did they unsubscribe? */
	printf(unsubto,ngname) FLUSH;
	toread[ng] = TR_UNSUB;		/* make line invisible */
    }
    else
	/*NOSTRICT*/
	toread[ng] = (ART_UNREAD)count;		/* remember how many unread there are */
}

/* mark an article unread, keeping track of toread[] */

void
onemore(artnum)
ART_NUM artnum;
{
#ifdef DEBUGGING
    if (debug && artnum < firstbit) {
	printf("onemore: %d < %d\n",artnum,firstbit) FLUSH;
	return;
    }
#endif
    if (ctl_read(artnum)) {
	ctl_clear(artnum);
	++toread[ng];
    }
}

/* mark an article read, keeping track of toread[] */

void
oneless(artnum)
ART_NUM artnum;
{
#ifdef DEBUGGING
    if (debug && artnum < firstbit) {
	printf("oneless: %d < %d\n",artnum,firstbit) FLUSH;
	return;
    }
#endif
    if (!ctl_read(artnum)) {
	ctl_set(artnum);
	if (toread[ng] > TR_NONE)
	    --toread[ng];
    }
}

/* mark an article as unread, making sure that firstbit is properly handled */
/* cross-references are left as read in the other newsgroups */

void
unmark_as_read()
{
    check_first(art);
    onemore(art);
#ifdef MCHASE
    if (!parse_maybe(art))
	chase_xrefs(art,FALSE);
#endif
}

#ifdef DELAYMARK
/* temporarily mark article as read.  When newsgroup is exited, articles */
/* will be marked as unread.  Called via M command */

void
delay_unmark(artnum)
ART_NUM artnum;
{
    if (dmfp == Nullfp) {
	dmfp = fopen(dmname,"w+");
	if (dmfp == Nullfp) {
	    printf(cantcreate,dmname) FLUSH;
	    sig_catcher(0);
	}
    }
    oneless(artnum);			/* set the correct bit */
    dmcount++;
    fprintf(dmfp,"%ld\n",(long)artnum);
}
#endif

/* mark article as read.  If article is cross referenced to other */
/* newsgroups, mark them read there also. */

void
mark_as_read()
{
    oneless(art);			/* set the correct bit */
    checkcount++;			/* get more worried about crashes */
    chase_xrefs(art,TRUE);
}

/* make sure we have bits set correctly down to firstbit */

void
check_first(min)
ART_NUM min;
{
    register ART_NUM i = firstbit;

    if (min < absfirst)
	min = absfirst;
    if (min < i) {
	for (i--; i>=min; i--)
	    ctl_set(i);		/* mark as read */
	firstart = firstbit = min;
    }
}

/* bring back articles marked with M */

#ifdef DELAYMARK
void
yankback()
{
    if (dmfp) {			/* delayed unmarks pending? */
#ifdef VERBOSE
	printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
	    dmcount == 1 ? nullstr : "s") FLUSH;
#endif
	rewind(dmfp);
	while (fgets(buf,sizeof buf,dmfp) != Nullch) {
	    art = (ART_NUM)atol(buf);
	    unmark_as_read();
	}
	fclose(dmfp);
	dmfp = Nullfp;
	UNLINK(dmname);		/* and be tidy */
    }
    dmcount = 0;
}
#endif
    
/* run down xref list and mark as read or unread */

int
chase_xrefs(artnum,markread)
ART_NUM artnum;
int markread;
{
#ifdef ASYNC_PARSE
    if (parse_maybe(artnum))		/* make sure we have right header */
	return -1;
#endif
#ifdef DBM
    {
	datum lhs, rhs;
	datum fetch();
	register char *idp;
	char *ident_buf;
	static FILE * hist_file = Nullfp;
#else
    if (
#ifdef DEBUGGING
	debug & DEB_FEED_XREF ||
#endif
	htype[XREF_LINE].ht_minpos >= 0) {
					/* are there article# xrefs? */
#endif /* DBM */
	char *xref_buf, *curxref;
	register char *xartnum;
	char *rver_buf = Nullch;
	static char *inews_site = Nullch;
	register ART_NUM x;
	char tmpbuf[128];
#ifdef DBM
	long pos;
	rver_buf = fetchlines(artnum,NGS_LINE);
					/* get Newsgroups */
#ifdef XREF_WITH_COMMAS
	if (!index(rver_buf,','))	/* if no comma, no Xref! */
	    return 0;
#endif
	if (hist_file == Nullfp) {	/* Init. file accesses */
#ifdef DEBUGGING
	    if (debug)
		printf ("chase_xref: opening files\n");
#endif
	    dbminit(filexp(ARTFILE));
	    if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp)
		return 0;
	}
	xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
	ident_buf = fetchlines(artnum,MESSID_LINE);
					/* get Message-ID */
#ifdef DEBUGGING
	if (debug)
	    printf ("chase_xref: Message-ID: %s\n", ident_buf);
#endif
	idp = ident_buf;
	while (*++idp)			/* make message-id case insensitive */
	    if (isupper(*idp))
	        *idp = tolower (*idp);
	lhs.dptr = ident_buf;		/* look up article by id */
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs = fetch(lhs);		/* fetch the record */
	if (rhs.dptr == NULL)		/* if null, nothing there */
	    goto wild_goose;
	bcopy((void *)rhs.dptr,(void *)&pos, 4);
	fseek (hist_file, pos, 0);
				/* datum returned is position in hist file */
	fgets (xref_buf, BUFSIZ, hist_file);
#ifdef DEBUGGING
	if (debug)
	    printf ("Xref from history: %s\n", xref_buf);
#endif
	curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
	curxref = cpytill(tmpbuf, curxref, '\t') + 1;
#ifdef DEBUGGING
	if (debug)
	    printf ("chase_xref: curxref: %s\n", curxref);
#endif
#else /* !DBM */
#ifdef DEBUGGING
	if (htype[XREF_LINE].ht_minpos >= 0)
#endif
	    xref_buf = fetchlines(artnum,XREF_LINE);
					/* get xrefs list */
#ifdef DEBUGGING
	else {
	    xref_buf = safemalloc((MEM_SIZE)100);
	    printf("Give Xref: ") FLUSH;
	    gets(xref_buf);
	}
#endif
#ifdef DEBUGGING
	if (debug & DEB_XREF_MARKER)
	    printf("Xref: %s\n",xref_buf) FLUSH;
#endif
	curxref = cpytill(tmpbuf,xref_buf,' ') + 1;

	/* Make sure site name on Xref matches what inews thinks site is.
	 * Check first against last inews_site.  If it matches, fine.
	 * If not, fetch inews_site from current Relay-Version line and
	 * check again.  This is so that if the new administrator decides
	 * to change the system name as known to inews, rn will still do
	 * Xrefs correctly--each article need only match itself to be valid.
	 */ 
	if (inews_site == Nullch || strNE(tmpbuf,inews_site)) {
#ifndef NORELAY
	    char *t;
#endif
	    if (inews_site != Nullch)
		free(inews_site);
#ifndef NORELAY
	    rver_buf = fetchlines(artnum,RVER_LINE);
	    if ((t = instr(rver_buf,"; site ", TRUE)) == Nullch)
#else /* NORELAY */
          /* In version 2.10.3 of news or afterwards, the Relay-Version
           * and Posting-Version header lines have been removed.  For
           * the code below to work as intended, I have modified it to
           * extract the first component of the Path header line.  This
           * should give the same effect as did the old code with respect
           * to the use of the Relay-Version site name.
           */
          rver_buf = fetchlines(artnum,PATH_LINE);
          if (instr(rver_buf,"!", TRUE) == Nullch)
#endif /* NORELAY */
		inews_site = savestr(nullstr);
	    else {
		char new_site[128];

#ifndef NORELAY
		cpytill(new_site,t + 7,'.');
#else /* NORELAY */
              cpytill(new_site,rver_buf,'!');
#endif /* NORELAY */
		inews_site = savestr(new_site);
	    }
	    if (strNE(tmpbuf,inews_site)) {
#ifdef DEBUGGING
		if (debug)
		    printf("Xref not from %s--ignoring\n",inews_site) FLUSH;
#endif
		goto wild_goose;
	    }
	}
#endif /* DBM */
	while (*curxref) {
					/* for each newsgroup */
	    curxref = cpytill(tmpbuf,curxref,' ');
#ifdef DBM
	    xartnum = index(tmpbuf,'/');
#else
	    xartnum = index(tmpbuf,':');
#endif /* DBM */
	    if (!xartnum)		/* probably an old-style Xref */
		break;
	    *xartnum++ = '\0';
	    if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
		x = atol(xartnum);
		if (x)
		    if (markread) {
			if (addartnum(x,tmpbuf))
			    goto wild_goose;
		    }
#ifdef MCHASE
		    else
			subartnum(x,tmpbuf);
#endif
	    }
	    while (*curxref && isspace(*curxref))
		curxref++;
	}
      wild_goose:
	free(xref_buf);
#ifdef DBM
	free(ident_buf);
#endif /* DBM */
	if (rver_buf != Nullch)
	    free(rver_buf);
    }
    return 0;
}

int
initctl()
{
    char *mybuf = buf;			/* place to decode rc line */
    register char *s, *c, *h;
    register long i;
    register ART_NUM unread;
    
#ifdef DELAYMARK
    dmcount = 0;
#endif
    if ((lastart = getngsize(ng)) < 0)	/* this cannot happen (laugh here) */
	return -1;

    absfirst = getabsfirst(ng,lastart);	/* remember first existing article */
    if (!absfirst)			/* no articles at all? */
	absfirst = 1;			/* pretend there is one */
#ifndef lint
    ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
#endif /* lint */
    ctlarea = safemalloc(ctlsize);	/* allocate control area */

    /* now modify ctlarea to reflect what has already been read */

    for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
					/* find numbers in rc line */
    i = strlen(s);
#ifndef lint
    if (i >= LBUFLEN-2)			/* bigger than buf? */
	mybuf = safemalloc((MEM_SIZE)(i+2));
#endif /* lint */
    strcpy(mybuf,s);			/* make scratch copy of line */
    mybuf[i++] = ',';			/* put extra comma on the end */
    mybuf[i] = '\0';
    s = mybuf;				/* initialize the for loop below */
    if (strnEQ(s,"1-",2)) {		/* can we save some time here? */
	firstbit = atol(s+2)+1;		/* ignore first range thusly */
	s=index(s,',') + 1;
    }
    else
	firstbit = 1;			/* all the bits are valid for now */
    if (absfirst > firstbit) {		/* do we know already? */
	firstbit = absfirst;		/* no point calling getngmin again */
    }
    else if (artopen(firstbit) == Nullfp) {
					/* first unread article missing? */
	i = getngmin(".",firstbit);	/* see if expire has been busy */
	if (i) {			/* avoid a bunch of extra opens */
	    firstbit = i;
	}
    }
    firstart = firstbit;		/* firstart > firstbit in KILL */
#ifdef PENDING
#   ifdef CACHESUBJ
	subj_to_get = firstbit;
#   endif
#endif
    unread = lastart - firstbit + 1;	/* assume this range unread */
    for (i=OFFSET(firstbit)/BITSPERBYTE; i<ctlsize; i++)
	ctlarea[i] = 0;			/* assume unread */
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	printf("\n%s\n",mybuf) FLUSH;
	for (i=1; i <= lastart; i++)
	    if (! was_read(i))
		printf("%ld ",(long)i) FLUSH;
    }
#endif
    for ( ; (c = index(s,',')) != Nullch; s = ++c) {
					/* for each range */
	ART_NUM min, max;

	*c = '\0';			/* do not let index see past comma */
	if ((h = index(s,'-')) != Nullch) {	/* is there a -? */
	    min = atol(s);
	    max = atol(h+1);
	    if (min < firstbit)		/* make sure range is in range */
		min = firstbit;
	    if (max > lastart)
		max = lastart;
	    if (min <= max)		/* non-null range? */
		unread -= max - min + 1;/* adjust unread count */
	    for (i=min; i<=max; i++)	/* for all articles in range */
		ctl_set(i);		/* mark them read */
	}
	else if ((i = atol(s)) >= firstbit && i <= lastart) {
					/* is single number reasonable? */
	    ctl_set(i);			/* mark it read */
	    unread--;			/* decrement articles to read */
	}
#ifdef DEBUGGING
	if (debug & DEB_CTLAREA_BITMAP) {
	    printf("\n%s\n",s) FLUSH;
	    for (i=1; i <= lastart; i++)
		if (! was_read(i))
		    printf("%ld ",(long)i) FLUSH;
	}
#endif
    }
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	fputs("\n(hit CR)",stdout) FLUSH;
	gets(cmd_buf);
    }
#endif
    if (mybuf != buf)
	free(mybuf);
    toread[ng] = unread;
    return 0;
}

void
grow_ctl(newlast)
ART_NUM newlast;
{
    ART_NUM tmpfirst;
    MEM_SIZE newsize;
    register ART_NUM i;

    forcegrow = FALSE;
    if (newlast > lastart) {
	ART_NUM tmpart = art;
#ifndef lint
	newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
#else
	newsize = Null(MEM_SIZE);
#endif /* lint */
	if (newsize > ctlsize) {
	    newsize += 20;
	    ctlarea = saferealloc(ctlarea,newsize);
	    ctlsize = newsize;
	}
	toread[ng] += (ART_UNREAD)(newlast-lastart);
	for (i=lastart+1; i<=newlast; i++)
	    ctl_clear(i);	/* these articles are unread */
#ifdef CACHESUBJ
	if (subj_list != Null(char**)) {
#ifndef lint
	    subj_list = (char**)saferealloc((char*)subj_list,
		  (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
#endif /* lint */
	    for (i=lastart+1; i<=newlast; i++)
		subj_list[OFFSET(i)] = Nullch;
	}
#endif
	tmpfirst = lastart+1;
	lastart = newlast;
#ifdef KILLFILES
#ifdef VERBOSE
	IF(verbose)
	    sprintf(buf,
		"%ld more article%s arrived--looking for more to kill...\n\n",
		(long)(lastart - tmpfirst + 1),
		(lastart > tmpfirst ? "s have" : " has" ) );
	ELSE			/* my, my, how clever we are */
#endif
#ifdef TERSE
	    strcpy(buf, "More news--killing...\n\n");
#endif
	kill_unwanted(tmpfirst,buf,TRUE);
#endif
	art = tmpart;
    }
}

!STUFFY!FUNK!
echo Extracting uudecode.c
cat >uudecode.c <<'!STUFFY!FUNK!'
/* $Id: uudecode.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
 *
 * $Log: uudecode.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
 *
 * 
 * Decode one or more uuencoded articles back to binary form.
 * Adapted to rn 4.4 by Stan Barber
 * Trn version created by Wayne Davison.
 * Adapted from the nn version by Kim Storm.
 * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
 */
/*
 * 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 "INTERN.h"
#include "uudecode.h"

#define MAXCHAR 256
#define NORMLEN 64	/* allows for 84 encoded chars per line */

#define SEQMAX 'z'
#define SEQMIN 'a'

static char seqc;
static int first, secnd, check, numl;

static char *target;
static char dest[MAXFILENAME];
static char blank;
static int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
static int state;
static bool Xflag;
static int expecting_part;

static decode_line(), inittbls(), gettable();

#define	NO_ADVANCE		0x10

#define	FIND_BEGIN		0x01
#define	AFTER_ERROR_FIND_BEGIN	0x02
#define	DECODE_TEXT		0x03
#define	SKIP_TRAILING	       (0x04 | NO_ADVANCE)
#define	SKIP_LEADING		0x05
#define	FOUND_END	       (0x06 | NO_ADVANCE)
#define DECODE_ERROR	       (0x07 | NO_ADVANCE)
#define OTHER_ERROR	       (0x08 | NO_ADVANCE)
#define NEW_BEGIN	       (0x09 | NO_ADVANCE)

uud_start(dir)
char *dir;
{
    target = dir;
    uu_out = Nullfp;
    Xflag = FALSE;
    expecting_part = 0;
    seqc = SEQMAX;
    check = 1;
    first = 1;
    secnd = 0;
    state = FIND_BEGIN;
}

uud_end()
{
    if (uu_out != Nullfp) {
	fclose(uu_out);
	uu_out = Nullfp;
	printf("\n%s INCOMPLETE -- removed\n", dest);
	unlink(dest);
    }
}


uudecode(in)
FILE *in;
{
    int mode, onedone, lens;
    char buff[LBUFLEN];

    numl = onedone = 0;

    if (state == FIND_BEGIN)
	inittbls();

    /*
     * search for header or translation table line.
     */

    while ((state & NO_ADVANCE) || fgets(buff, sizeof buff, in) != Nullch) {
	numl++;

	switch (state) {
	 case NEW_BEGIN:
	    if (uu_out != Nullfp) {
		if (expecting_part) {
		    register int got_part = 0;

		    if (strnEQ(buff + 6, "part ", 5)) {
			register char *bp;

			for (bp = buff + 11; islower(*bp); bp++)
			    got_part = got_part * 26 + *bp - 'a';
		    }
		    if (expecting_part == got_part) {
			state = DECODE_TEXT;
			break;
		    }
		    printf("Expecting part %d; got part %d.\n",
			 expecting_part + 1, got_part + 1);
		    if (got_part) {
			state = SKIP_LEADING;
			return -1;
		    }
		}
		uud_end();
		sleep(2);
		Xflag = FALSE;
		expecting_part = 0;
	    }
	    state = FIND_BEGIN;
	    /* fall thru */

	 case FIND_BEGIN:
	 case AFTER_ERROR_FIND_BEGIN:
	    if (strnEQ(buff, "table", 5)) {
		gettable(in);
		continue;
	    }

	    if (strnEQ(buff, "begin ", 6)
	     || strnEQ(buff, "Xbegin ", 7)) {
		lens = strlen(buff)-1;
		if (buff[lens] == '\n')
		    buff[lens] = '\0';

		if(sscanf(buff+6,"%o%s", &mode, uu_fname) != 2) {
		    register char *bp = buff + 6;

		    if (*bp == ' ')
			bp++;
		    if (strnEQ(bp, "part ", 5)) {
			register int got_part = 0;

			for (bp = bp + 5; islower(*bp); bp++)
			    got_part = got_part * 26 + *bp - 'a';
			printf("Expecting part 1; got part %d.\n",
				got_part + 1);
			return -1;
		    }
		    continue;
		}

		Xflag = (*buff == 'X');

		if (target != Nullch)
		    sprintf(dest, "%s/%s", target, uu_fname);
		else
		    strcpy(dest, uu_fname);

		if ((uu_out = fopen(dest, FOPEN_WB)) == Nullfp) {
		    printf("Cannot create file: %s\n", dest);
		    goto err;
		}
		chmod(dest, mode);
		printf("Decoding: %s\n", uu_fname);
		state = DECODE_TEXT;
	    }
	    continue;

	 case SKIP_LEADING:
	    state = decode_line(buff);
	    continue;

	 case DECODE_TEXT:
	    state = decode_line(buff);
	    onedone = 1;
	    continue;

	 case FOUND_END:
	    fclose(uu_out);
	    uu_out = Nullfp;
	    Xflag = FALSE;
	    expecting_part = 0;
	    state = FIND_BEGIN;
	    printf("Done.\n");
	    continue;

	 case SKIP_TRAILING:
	    printf("(Continued)\n");
	    state = SKIP_LEADING;
	    return 0;

	 case DECODE_ERROR:
	    state = SKIP_TRAILING;
	    continue;

	 case OTHER_ERROR:
	    fclose(uu_out);
	    uu_out = Nullfp;
	    Xflag = FALSE;
	    expecting_part = 0;
	    state = AFTER_ERROR_FIND_BEGIN;
	    goto err;
	}
    }

    if (onedone) {
	if (state == DECODE_TEXT) {
	    printf("(Continued)\n");
	    state = SKIP_LEADING;
	}
	return 0;
    }

    if (state == AFTER_ERROR_FIND_BEGIN)
	return -1;
    printf("Couldn't find anything to decode.\n");

 err:
    sleep(2);
    return -1;
}

/*
 * decode one line, write on uu_out file
 */

static decode_line(buff)
char *buff;
{
    char outl[LBUFLEN];
    register char *bp, *ut;
    register int *trtbl = chtbl;
    register int n;
    register int blen;		/* binary length (from decoded file) */
    register int rlen;		/* calculated input line length */
    register int len;		/* actual input line length */
    register int dash;		/* number of '-'s encountered on a line */
				/* If it's too high, we reject the line */

#   define REJECT(buf,rlen,len) /* Comment for makedepend to	 \
				** ignore the backslash above */ \
	((*buf == 'M' && len > rlen + 5) \
	 || (*buf != 'M' && len != rlen && len != rlen+1) \
	 || (strnEQ(buf, "BEGIN", 5)) \
	 || (strnEQ(buf, "END", 3)))

    if (Xflag) {
	if (*buff == 'X')
	    buff++;
	else
	    *buff = 'x';	/* force a mis-parse of a non-x'ed line */
    }
    len = strlen(buff);
    if (--len <= 0)
	return state;

    buff[len] = '\0';

    /*
     * Get the binary line length.
     */
    if ((blen = trtbl[buff[0]]) < 0) {
	if (state == SKIP_LEADING) {
	    if (strnEQ(buff, "begin ", 6))
		return NEW_BEGIN;

	    return SKIP_LEADING;
	}
	/*
	 * end of uuencoded file ?
	 */
	if (strnEQ(buff, "end", 3))
	    return FOUND_END;

	/*
	 * end of current file ? : get next one.
	 */
	if (strnEQ(buff, "include ", 8)) {
	    for (bp = buff + 8; *bp; bp++) {
		if (bp[0] == '.' && bp[1] == 'u') {
		    expecting_part = (bp[2] - 'a') * 26 + bp[3] - 'a';
		    break;
		}
	    }
	}

	/*
	 * trailing garbage
	 */
	return SKIP_TRAILING;
    }

    rlen = cdlen[blen];
    if (state == SKIP_LEADING && REJECT(buff,rlen,len))
	return SKIP_LEADING;

    /*
     * Is it the empty line before the end line ?
     */
    if (blen == 0)
	return state;

    if (REJECT(buff,rlen,len))
	return SKIP_TRAILING;

    /*
     * Pad with blanks.
     */
    for (bp = buff + len, n = rlen - len; --n >= 0; )
	*bp++ = blank;

    /*
     * Verify
     */
    for (n = rlen, bp = buff, dash = 0; --n >= 0; bp++) {
	if (trtbl[*bp] < 0) {
	    if (state == SKIP_LEADING)
		return SKIP_LEADING;
	    return DECODE_ERROR;
	}
	if (*bp == '-')
	    dash++;
    }
    if (dash * 100 / rlen > 33)		/* more than 1/3 dashes? */
	if (state == SKIP_LEADING)
	    return SKIP_LEADING;	/* -> reject */
	else
	    return SKIP_TRAILING;

    /*
     * Check for uuencodes that append a 'z' to each line....
     */
    if (check)
	if (secnd) {
	    secnd = 0;
	    if (buff[rlen] == SEQMAX)
		check = 0;
	} else if (first) {
	    first = 0;
	    secnd = 1;
	    if (buff[rlen] != SEQMAX)
		check = 0;
	}

    /*
     * There we check.
     */
    if (check) {
	if (buff[rlen] != seqc) {
	    if (state == SKIP_LEADING)
		return SKIP_LEADING;
	    return DECODE_ERROR;
	}

	if (--seqc < SEQMIN)
	    seqc = SEQMAX;
    }

    /*
     * output a group of 3 bytes (4 input characters).
     * the input chars are pointed to by p, they are to
     * be output to file f. blen is used to tell us not to
     * output all of them at the end of the file.
     */
    ut = outl;
    n = blen;
    bp = &buff[1];
    while (--n >= 0) {
	*(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
	if (n > 0) {
	    *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
	    n--;
	}
	if (n > 0) {
	    *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
	    n--;
	}
	bp += 4;
    }
    if (fwrite(outl, 1, blen, uu_out) <= 0) {
	printf("Error on writing decoded file\n");
	return OTHER_ERROR;
    }

    return DECODE_TEXT;
}



/*
 * Install the table in memory for later use.
 */
static inittbls()
{
    register int i, j;

    /*
     * Set up the default translation table.
     */
    for (i = 0; i < ' '; i++)
	chtbl[i] = -1;
    for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
	chtbl[i] = j;
    for (i = ' ' + 64; i < MAXCHAR; i++)
	chtbl[i] = -1;
    chtbl['`'] = chtbl[' '];	/* common mutation */
    chtbl['~'] = chtbl['^'];	/* another common mutation */
    blank = ' ';
    /*
     * set up the line length table, to avoid computing lotsa * and / ...
     */
    cdlen[0] = 1;
    for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
	cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
}

static gettable(in)
FILE *in;
{
    char buff[LBUFLEN];
    register int c, n = 0;
    register char *cpt;

    for (c = 0; c < MAXCHAR; c++)
	chtbl[c] = -1;

    for (;;) {
	if (fgets(buff, sizeof buff, in) == Nullch) {
	    printf("EOF while in translation table.\n");
	    return -1;
	}
	numl++;
	if (strnEQ(buff, "begin", 5)) {
	    printf("Incomplete translation table.\n");
	    return -1;
	}
	cpt = buff + strlen(buff) - 1;
	*cpt = ' ';
	while (*cpt == ' ') {
	    *cpt = 0;
	    cpt--;
	}
	cpt = buff;
	while (c = *cpt) {
	    if (chtbl[c] != -1) {
		printf("Duplicate char in translation table.\n");
		return -1;
	    }
	    if (n == 0)
		blank = c;
	    chtbl[c] = n++;
	    if (n >= 64)
		return 0;
	    cpt++;
	}
    }
}

!STUFFY!FUNK!
echo ""
echo "End of kit 6 (of 11)"
cat /dev/null >kit6isdone
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
