[net.sources] news 2.10.2 src part 6

rick@seismo.UUCP (Rick Adams) (09/10/84)

if test ! -d src
then
	mkdir src
	echo mkdir src
fi
echo x - src/rfuncs.c
sed 's/^X//' >src/rfuncs.c <<'*-*-END-of-src/rfuncs.c-*-*'
X/*
X * rfuncs - functions for readnews.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)rfuncs.c	2.20	9/3/84";
X#endif !lint
X
X#include "rparams.h"
X
Xlong nngsize;	/* The next upcoming value of ngsize. */
Xlong nminartno;	/* Smallest article number in this group */
X
Xnextng()
X{
X	long	curpos;
X#ifdef DEBUG
X	fprintf(stderr, "nextng()\n");
X#endif
X	curpos = ftell(actfp);
X
Xnext:
X#ifdef DEBUG
X	fprintf(stderr, "next:\n");
X#endif
X	if (actdirect == BACKWARD) {
X		if (back()) {
X			fseek(actfp, curpos, 0);
X			return 1;
X		}
X		if (back()) {
X			fseek(actfp, curpos, 0);
X			return 1;
X		}
X	}
X	if (fgets(afline, BUFLEN, actfp) == NULL)
X		return 1;
X	if (sscanf(afline, "%s %ld %ld", bfr, &nngsize, &nminartno) < 3) {
X		bfr[0] = '\0';
X		nngsize = 0;
X		nminartno = 0;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "bfr = '%s'\n", bfr);
X#endif
X
X	ngcat(bfr);
X	if (!ngmatch(bfr, header.nbuf))
X		goto next;
X	ngdel(bfr);
X	if (xflag)
X		readmode = SPEC;
X	else
X		readmode = NEXT;
X	if (selectng(bfr, TRUE))
X		goto next;
X	return 0;
X}
X
X
Xselectng(name, fastcheck)
Xchar	*name;
X{
X	register char	*ptr, punct = ',';
X	register int	i;
X	register char	*p;
X	register long	cur;
X	long	next = 0;
X	FILE *af;
X	long s, sm;
X	char buf[100], n[100];
X
X	if (*groupdir)
X		updaterc();
X	last = 1;
X	if (strcmp(name, bfr)) {
X		af = xfopen(ACTIVE, "r");
X		while (fgets(buf, sizeof buf, af) != NULL) {
X			if (sscanf(buf, "%s %ld %ld", n, &s, &sm) == 3 &&
X			     strcmp(n, name) == 0) {
X				ngsize = s;
X				minartno = sm;
X				break;
X			}
X		}
X		fclose(af);
X	} else {
X		ngsize = nngsize;
X		minartno = nminartno;
X	}
X#ifdef DEBUG
X	fprintf(stderr, "selectng(%s) sets ngsize to %ld, minartno to %ld\n",
X		name, ngsize, minartno);
X#endif
X	strcpy(groupdir, name);
X	if (!xflag) {
X		i = findrcline(name);
X		if (i >= 0) {
X			if (index(rcline[i], '!')) {
X				groupdir[0] = 0;
X				return 1;
X			}
X			sprintf(rcbuf, "%s,%ld", rcline[i], ngsize+1);
X		}
X		else
X			sprintf(rcbuf, "ng: %ld", ngsize+1);
X	} else
X		sprintf(rcbuf, "ng: %ld", ngsize+1);
X#ifdef DEBUG
X	fprintf(stderr, "rcbuf set to %s\n", rcbuf);
X#endif DEBUG
X
X	/*
X	 * Fast check for common case: 1-###
X	 */
X	if (fastcheck) {
X		p = rcbuf;
X		while (*p != ' ')
X			p++;
X		while (*p == ' ')
X			p++;
X		if (*p++ == '1' && *p++ == '-') {
X			i = 0;
X			while (isdigit(*p))
X				i = 10 * i + *p++ - '0';
X			if (*p == ',' && i >= ngsize) {
X				groupdir[0] = 0;
X				return 1;
X			}
X		}
X	}
X
X/*
X * The key to understanding this piece of code is that a bit is set iff
X * that article has NOT been read.  Thus, we fill in the holes when
X * commas are found (e.g. 1-20,30-35 will result in filling in the 21-29
X * holes), and so we assume the newsrc file is properly ordered, the way
X * we write it out.
X */
X	if ((ngsize-minartno) > BITMAPSIZE) {
X		sprintf(buf," Bitmap not large enough for newsgroup %s", groupdir);
X		xerror(buf);
X	}
X
X	cur = 0;
X	/* Zero out the bitmap */
X	p = &bitmap[(ngsize-minartno)/8+1];
X	for (ptr = bitmap; ptr <= p;)
X		*ptr++ = 0;
X
X	/* Decode the .newsrc line indicating what we have read. */
X	for (ptr = rcbuf; *ptr && *ptr != ':'; ptr++)
X		;
X	while (*ptr) {
X		while (!isdigit(*ptr) && *ptr)
X			ptr++;
X		if (!*ptr)
X			break;
X		sscanf(ptr, "%ld", &next);
X		if (punct == ',') {
X			while (++cur < next) {
X				set(cur);
X			}
X		}
X		cur = next;
X		while (!ispunct(*ptr) && *ptr)
X			ptr++;
X		punct = *ptr;
X	}
X	if (rflag)
X		bit = ngsize+1;
X	else
X		bit = minartno -1;
X	nextbit();
X	ngrp = 1;
X	return 0;
X}
X
X#ifdef TMAIL
Xcatchterm()
X{
X	unlink(infile);
X	unlink(outfile);
X	xxit(0);
X}
X
X
X/*
X * The -M (Mail) interface.  This code is a reasonably simple model for
X * writing other interfaces.  We write out all relavent articles to
X * a temp file, then invoke Mail with an option to have it tell us which
X * articles it read.  Finally we count those articles as really read.
X */
XMail()
X{
X	register FILE *fp = NULL, *ofp;
X	struct hbuf h;
X	register char	*ptr, *fname;
X	int	news = 0;
X	register int i;
X
X	for(i=0;i<NUNREC;i++)
X		h.unrec[i] = NULL;
X
X	ofp = xfopen(mktemp(outfile), "w");
X	if (aflag && *datebuf)
X		if ((atime = cgtdate(datebuf)) == -1)
X			xerror("Cannot parse date string");
X	while (!nextng())
X		while (bit <= ngsize) {
X			sprintf(filename, "%s/%ld", dirname(groupdir), bit);
X			if (access(filename, 4)
X			|| ((fp = fopen(filename, "r")) == NULL)
X			|| (hread(&h, fp, TRUE) == NULL)
X			|| !aselect(&h, FALSE)) {
X#ifdef DEBUG
X				fprintf(stderr, "Bad article '%s'\n", filename);
X#endif
X				if (fp != NULL) {
X					fclose(fp);
X					fp = NULL;
X				}
X				clear(bit);
X				nextbit();
X				continue;
X			}
X			fname = ptr = index(h.from, '(');
X			if (fname) {
X				while (ptr && ptr[-1] == ' ')
X					ptr--;
X				if (ptr)
X					*ptr = 0;
X				fname++;
X				ptr = fname + strlen(fname) - 1;
X				if (*ptr == ')')
X					*ptr = 0;
X			}
X			h.subtime = cgtdate(h.subdate);
X			fprintf(ofp, "From %s %s",
X#ifdef INTERNET
X			    h.from[0] ? h.from :
X#endif
X			    h.path, ctime(&h.subtime));
X			if (fname)
X				fprintf(ofp, "Full-Name: %s\n", fname);
X			fprintf(ofp, "Newsgroups: %s\n", h.nbuf);
X			fprintf(ofp, "Subject: %s\n", h.title);
X			fprintf(ofp, "Article-ID: %s/%ld\n\n", groupdir, bit);
X			tprint(fp, ofp, TRUE);
X			putc('\n', ofp);
X			news = TRUE;
X			fclose(fp);
X			fp = NULL;
X			nextbit();
X		}
X	updaterc();
X	fclose(ofp);
X	if (!news) {
X		fprintf(stderr, "No news.\n");
X		unlink(outfile);
X		return;
X	}
X	signal(SIGHUP, catchterm);
X	signal(SIGTERM, catchterm);
X	sprintf(bfr, "%s -f %s -T %s", TMAIL, outfile, mktemp(infile));
X	fwait(fsubr(ushell, bfr, (char *)NULL));
X	ofp = xfopen(infile, "r");
X	fseek(actfp, 0L, 0);
X	while (fgets(afline, BUFLEN, actfp) != NULL) {
X		last = 0;
X		if (sscanf(afline, "%s %ld", bfr, &nngsize) < 2) {
X			bfr[0] = '\0';
X			nngsize = 0;
X		}
X		ngcat(bfr);
X		if (!ngmatch(bfr, header.nbuf))
X			continue;
X		ngdel(bfr);
X		*groupdir = 0;
X		if (selectng(bfr, TRUE))
X			continue;
X		fseek(ofp, 0L, 0);
X		while (fgets(groupdir, BUFLEN, ofp) != NULL) {
X			nstrip(groupdir);
X			ptr = index(groupdir, '/');
X			*ptr = 0;
X			if (strcmp(bfr, groupdir))
X				continue;
X			sscanf(++ptr, "%ld", &last);
X			clear(last);
X		}
X		if (last) {
X			strcpy(groupdir, bfr);
X			updaterc();
X		}
X	}
X	unlink(infile);
X	unlink(outfile);
X}
X#endif
X
Xupdaterc()
X{
X	register long	cur = 1, next = 1;
X	register int i;
X	register char	*ptr;
X	char	oldptr;
X
X	sprintf(rcbuf, "%s%c ", groupdir, zapng ? '!' : ':');
X
X	zapng = FALSE;
Xagain:
X	ptr = &rcbuf[strlen(rcbuf)];
X	while (get(next) && next <= ngsize)
X		next++;
X	cur = next;
X	while (!(get(next)) && next <= ngsize)
X		next++;
X	if (cur == next) {
X		next = ngsize + 1;
X		goto skip;
X	}
X	if (cur + 1 == next)
X		sprintf(ptr, "%ld,", cur);
X	else
X		sprintf(ptr, "%ld-%ld,", cur, next - 1);
Xskip:
X	if ((long) next > ngsize) {
X		if (index(rcbuf, ',') != NULL)
X			ngdel(rcbuf);
X		else if (index(rcbuf, '!') == NULL)
X			return;
X		ptr = index(rcbuf, ' ');
X		ptr--;
X		oldptr = *ptr;
X		ptr[0] = ':';
X		ptr[1] = '\0';
X		i = findrcline(groupdir);
X		if (i >= 0) {
X			ptr[0] = oldptr;
X			ptr[1] = ' ';
X			rcline[i] = realloc(rcline[i], (unsigned)(strlen(rcbuf) + 1));
X			if (rcline[i] == NULL)
X				xerror("Cannot realloc");
X			strcpy(rcline[i], rcbuf);
X			return;
X		}
X		if (++line > LINES)
X			xerror("Too many newsgroups");
X		ptr[0] = oldptr;
X		ptr[1] = ' ';
X		if ((rcline[line] = malloc((unsigned)(strlen(rcbuf) + 1))) == NULL)
X			xerror("Not enough memory");
X		strcpy(rcline[line], rcbuf);
X		return;
X	}
X	cur = next;
X	goto again;
X}
X
Xnewrc(rcname)
Xchar *rcname;
X{
X	register FILE *fp;
X
X	if (close(creat(rcname, 0666))) {
X		sprintf(bfr, "Cannot create %s", newsrc);
X		xerror(bfr);
X	}
X
X	sprintf(bfr, "%s/users", LIB);
X	if ((fp = fopen(bfr, "a")) != NULL) {
X		fprintf(fp, "%s\n", username);
X		fclose(fp);
X		chmod(bfr, 0666);
X	}
X}
X
Xnextbit()
X{
X#ifdef DEBUG
X	fprintf(stderr,"nextbit() bit = %ld\n", bit);
X#endif DEBUG
X	last = bit;
X	if (readmode == SPEC || xflag) {
X		if (rflag)
X			bit--;
X		else
X			bit++;
X		return;
X	}
X	if (rflag)
X		while (--bit, !get(bit) && bit > minartno)
X			;
X	else
X		while (++bit, !get(bit) && bit <= ngsize)
X			;
X#ifdef DEBUG
X	fprintf(stderr,"nextng leaves bit as %ld\n", bit);
X#endif DEBUG
X}
X
Xxxit(status)
Xint	status;
X{
X	unlink(infile);
X	unlink(outfile);
X#ifdef SORTACTIVE
X	if (strncmp(ACTIVE,"/tmp/",4) == 0)	/* Paranoia */
X		unlink(ACTIVE);
X#endif SORTACTIVE
X	exit(status);
X}
X
X/*
X * Return TRUE if the user has not ruled out this article.
X */
Xaselect(hp, insist)
Xregister struct hbuf *hp;
Xint	insist;
X{
X	if (insist)
X		return TRUE;
X	if (tflag && !titmat(hp, header.title))
X		return FALSE;
X	if (aflag && cgtdate(hp->recdate) < atime)
X		return FALSE;
X	if (index(hp->nbuf, ',') && !rightgroup(hp))
X		return FALSE;
X	if (fflag && isfol(hp))
X		return FALSE;
X	return TRUE;
X}
X
X/*
X * Code to avoid showing multiple articles for news.
X * Works even if you exit news.
X * Returns nonzero if we should show this article.
X */
Xrightgroup(hp)
Xstruct hbuf *hp;
X{
X	char ng[BUFLEN];
X	register char *p, *g;
X	int i, flag;
X
X	strcpy(ng, hp->nbuf);
X	ngcat(ng);
X	g = ng;
X	flag = 1;
X	while ((p = index(g, ',')) != NULL) {
X		*p++ = '\0';
X		while (*p == ' ')
X			p++;
X		if (strcmp(g, groupdir) == 0)
X			return flag;
X		if (ngmatch(g, header.nbuf)
X		    && ((i = findrcline(g)) >= 0
X		    && index(rcline[i], '!') == NULL))
X			flag = 0;
X		g = p;
X	}
X	/* we must be in "junk" or "control" */
X	return TRUE;
X}
X
X/*
X * Return TRUE if this article is a followup to something.
X */
Xisfol(hp)
Xregister struct hbuf *hp;
X{
X	if (hp->followid[0])
X		return TRUE;
X	if (strncmp(hp->title, "Re:", 3) == 0)
X		return TRUE;
X	return FALSE;
X}
X
Xback()
X{
X	while (fseek(actfp, -2L, 1) != -1 && ftell(actfp) > 0L) {
X		if (getc(actfp) == '\n')
X			return 0;
X	}
X	if (ftell(actfp) == 0L)
X		return 0;
X	return 1;
X}
X
X/*
X * Trap interrupts.
X */
Xonsig(n)
Xint	n;
X{
X	signal(n, onsig);
X	sigtrap = n;
X	if (rcreadok < 2) {
X		fprintf(stderr, "Aborted early\n");
X		xxit(0);
X	}
X}
X
X/*
X * finds the line in your .newsrc file (actually the in-core "rcline"
X * copy of it) and returns the index into the array where it was found.
X * -1 means it didn't find it.
X *
X * We play clever games here to make this faster.  It's inherently
X * quadratic - we spend lots of CPU time here because we search through
X * the whole .newsrc for each line.  The "prev" variable remembers where
X * the last match was found; we start the search there and loop around
X * to the beginning, in the hopes that the calls will be roughly in order.
X */
Xint
Xfindrcline(name)
Xchar *name;
X{
X	register char *p, *ptr;
X	register int cur;
X	register int i;
X	register int top;
X	static int prev = 0;
X
X	top = line; i = prev;
Xloop:
X	for (; i <= top; i++) {
X		for (p = name, ptr = rcline[i]; (cur = *p++); ) {
X			if (cur != *ptr++)
X				goto contin2;
X		}
X		if (*ptr != ':' && *ptr != '!')
X			continue;
X		prev = i;
X		return i;
Xcontin2:
X		;
X	}
X	if (i > line && line > prev-1) {
X		i = 0;
X		top = prev-1;
X		goto loop;
X	}
X	return -1;
X}
X
X#ifdef SORTACTIVE
X/*
X * sortactive - make a local copy of the active file, sorted according
X *   to the user's preferences, according to his .newsrc file.
X * Marvin Solomon, University of Wisconsin, 1/2/84
X */
X
Xstruct table_elt {
X	long actpos, rcpos;
X};
X
Xstatic int
Xrcsort(a,b)
Xstruct table_elt *a, *b;
X{
X	return(a->rcpos - b->rcpos);
X}
X
Xsortactive()
X{
X	char newactivename[BUFLEN], aline[BUFLEN], *strcpy(), *p;
X	register FILE *nfp, *afp;
X	struct table_elt table[LINES];
X	int nlines = 0, i;
X	long actpos;
X
X	/* make a new sorted copy of ACTIVE in the user's home directory */
X	strcpy(newactivename, mktemp("/tmp/newsaXXXXXX"));
X	nfp = fopen(newactivename, "w");
X	if (nfp == NULL) {
X		perror(newactivename);
X		return;
X	}
X
X	/* look up all the lines in ACTIVE, finding their positions in .newsrc */
X	afp = xfopen(ACTIVE, "r");
X	actpos = ftell(afp);
X	while (fgets(aline, sizeof aline, afp) != NULL) {
X		if (p = index(aline, ' '))
X			*p = '\0';
X		table[nlines].rcpos = findrcline(aline);
X		table[nlines].actpos = actpos;
X		nlines++;
X		actpos = ftell(afp);
X	}
X
X	/* sort by position in user's .newsrc file (new groups come up first) */
X	qsort((char *)table, nlines, sizeof table[0], rcsort);
X
X	/* copy active to newactive, in the new order */
X	for (i=0; i<nlines; i++) {
X		(void) fseek(afp, table[i].actpos, 0);
X		(void) fgets(aline, sizeof aline, afp);
X		(void) fprintf(nfp, "%s", aline);
X	}
X	(void) fclose(afp);
X	(void) fclose(nfp);
X
X	/* make the rest of readnews think the local active file is the real one */
X	free(ACTIVE);
X	ACTIVE = AllocCpy(newactivename);
X}
X#endif SORTACTIVE
*-*-END-of-src/rfuncs.c-*-*
echo x - src/rfuncs2.c
sed 's/^X//' >src/rfuncs2.c <<'*-*-END-of-src/rfuncs2.c-*-*'
X/*
X * rfuncs2 - more routines needed by readr.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)rfuncs2.c	1.17	9/3/84";
X#endif !lint
X
X#include "rparams.h"
X
Xstatic char	lbuf[BUFLEN*2];
X
XFILE *popen();
X
X/*
X * Match title.
X */
Xtitmat(h, titlist)
Xregister struct hbuf *h;
Xregister char	*titlist;
X{
X	register char	*p;
X
X	while (*titlist != '\0') {
X
X		for (p = h->title; *p != '\0'; p++)
X			if (prefix(p, titlist)) {
X				return(TRUE);
X			}
X		while (*titlist++ != '\0')
X			;
X	}
X	return(FALSE);
X}
X
X
X/*
X * Save the news item in the user's file.
X * Fri Mar 12 20:04:43 EST 1982: (ittvax!swatt)
X *	Allow files with first character as '|' to write article
X *	to program across a pipe.
X */
X
X#define PIPECHAR '|'
X
Xsave(file, to)
Xregister char	*file, *to;
X{
X	register FILE *ufp, *hfp;
X	struct hbuf hh;
X	int	isprogram = 0;
X	int	isnew = 1;
X	register int i;
X
X	for(i=0;i<NUNREC;i++)
X		hh.unrec[i] = NULL;
X
X	if ((hfp = fopen(file, "r")) == NULL) {
X		fprintf(stderr, "Can't get article.\n");
X		return;
X	}
X	if (hread(&hh, hfp, TRUE) == NULL) {
X		fprintf(stderr, "Article is garbled.\n");
X		return;
X	}
X	ufp = fopen(to, "r");
X	if (ufp != NULL) {
X		fclose(ufp);
X		isnew = 0;
X	}
X	setgid(gid);
X	setuid(uid);
X	umask(savmask);
X
X	if (*to == PIPECHAR) {
X		if ((ufp = popen (&to[1], "w")) == NULL) {
X			fprintf(stderr,"Cannot execute %s\n", &to[1]);
X			return;
X		}
X		isprogram++;
X	} else if ((ufp = fopen(to, "a")) == NULL) {
X		fprintf(stderr,"Cannot append to %s.\n", to);
X		return;
X	}
X	/*
X	 * V7MAIL code is here to conform to V7 mail format.
X	 * If you need a different format to be able to
X	 * use your local mail command (such as four ^A's
X	 * on the end of articles) substitute it here.
X	 */
X#ifdef V7MAIL
X	hh.subtime = cgtdate(hh.subdate);
X	fprintf(ufp, "From %s %s",
X#ifdef INTERNET
X				hh.from,
X#else
X				hh.path,
X#endif
X					ctime(&hh.subtime));
X#endif
X	hprint(&hh, ufp, 2);
X#ifdef V7MAIL
X	tprint(hfp, ufp, TRUE);
X	putc('\n', ufp);	/* force blank line at end (ugh) */
X#else
X	tprint(hfp, ufp, FALSE);
X#endif
X	fclose(hfp);
X	if (isprogram)
X		pclose (ufp);
X	else
X		fclose(ufp);
X	if (!isprogram)
X		printf("%s: %s\n", to, isnew ? "New file" : "Appended");
X}
X
X
X/*
X * Print out the rest of the article.
X */
Xtprint(ifp, ofp, checkfrom)
Xregister FILE *ifp, *ofp;
Xint checkfrom;
X{
X	while ((fgets(bfr, sizeof bfr, ifp)) != NULL && !sigtrap) {
X		if (checkfrom && strncmp(bfr, "From ", 5) == 0)
X			putc('>', ofp);
X		fputs(bfr, ofp);
X	}
X	if (sigtrap)
X		qfflush(ofp);
X	fflush(ofp);
X	fprintf(ofp, (sigtrap ? "\n\n" : "\n"));
X	sigtrap = FALSE;
X}
X
X
X/*
X * Print the file header.
X */
Xhprint(hp, ofp, verbose)
Xregister struct hbuf *hp;
Xint	verbose;
Xregister FILE *ofp;
X{
X	register char	*p1, *p2;
X	char	fname[BUFLEN];
X	char *tailpath();
X
X	fname[0] = '\0';		/* init name holder */
X
X	if (verbose == 2) {
X		lhwrite(hp, ofp);
X		return;
X	}
X
X	if (lflag || eflag) {
X		char buf1[80], buf2[200];
X		char *cp;
X
X		strcpy(bfr, groupdir);
X		for (cp=bfr; *cp; cp++)
X			if (*cp == '/')
X				*cp = '.';
X		sprintf(buf1, "%s/%ld", bfr, bit);
X		sprintf(buf2, "%-20s %s", buf1, hp->title);
X		fprintf(ofp, "%.76s\n", buf2);
X		return;
X	}
X
X	p1 = index(hp->from, '(');	/* Find the sender's full name. */
X	if (p1 == NULL && hp->path[0])
X		p1 = index(hp->path, '(');
X	if (p1 != NULL) {
X		strcpy(fname, p1+1);
X		p2 = index(fname, ')');
X		if (p2 != NULL)
X			*p2 = '\0';
X	}
X
X	fprintf(ofp, "Subject: %s\n", hp->title);
X	if (!hflag && hp->keywords[0])
X		fprintf(ofp, "Keywords: %s\n", hp->keywords);
X	if (verbose) {
X		fprintf(ofp, "From: %s\n", hp->from);
X		fprintf(ofp, "Path: %s\n", hp->path);
X		if (hp->organization[0])
X			fprintf(ofp, "Organization: %s\n", hp->organization);
X	}
X	else {
X		if (p1 != NULL)
X			*--p1 = '\0';		/* bump over the '(' */
X#ifdef INTERNET
X		/*
X		 * Prefer Path line if it's in internet format, or if we don't
X		 * understand internet format here, or if there is no reply-to.
X		 */
X		fprintf(ofp, "From: %s", hp->from);
X#else
X		fprintf(ofp, "Path: %s", tailpath(hp));
X#endif
X		if (fname[0] || hp->organization[0]) {
X			if (fname[0] == '\0') {
X				strcpy(fname,hp->from);
X				p2 = index(fname,'@');
X				if (p2)
X					*p2 = '\0';
X			}
X			fprintf(ofp, " (%s", fname);
X			if (hp->organization[0] && !hflag)
X				fprintf(ofp, " @ %s", hp->organization);
X			fprintf(ofp, ")");
X		}
X		fprintf(ofp, "\n");
X		if (p1 != NULL)
X			*p1 = ' ';
X	}
X
X	ngdel(strcpy(bfr, hp->nbuf));
X	if (verbose) {
X		fprintf(ofp, "Newsgroups: %s\n", bfr);
X		fprintf(ofp, "Date: %s\n", hp->subdate);
X		if (hp->sender[0])
X			fprintf(ofp, "Sender: %s\n", hp->sender);
X		if (hp->replyto[0])
X			fprintf(ofp, "Reply-To: %s\n", hp->replyto);
X		if (hp->followto[0])
X			fprintf(ofp, "Followup-To: %s\n", hp->followto);
X	}
X	else if (index(bfr, ',') || strcmp(groupdir, "junk") == 0)
X		fprintf(ofp, "Newsgroups: %s\n", bfr);
X
X	if (pflag || ofp != stdout)
X		putc('\n', ofp);
X}
X
X
X/*
X * If ofp != stdout, close it and run the script in coptbuf.
X */
Xcout(ofp)
XFILE *ofp;
X{
X	register char	*p, *q, *r;
X
X	if (ofp == stdout)
X		return;
X	fclose(ofp);
X	p = coptbuf;
X	q = lbuf;
X	while ((*q = *p++) != '\0')
X		if (*q++ == FMETA) {
X			q--;
X			r = outfile;
X			while ((*q++ = *r++) != '\0')
X				;
X			q--;
X		}
X	fwait(fsubr(ushell, lbuf, (char *)NULL));
X	unlink(outfile);
X}
X
X
Xcdump(ofp)
Xregister FILE *ofp;
X{
X	if (ofp == stdout)
X		return;
X	fclose(ofp);
X	unlink(outfile);
X}
X
X
X/*
X * Quiet 'flush'.
X * Empty (without fflush()) the buffer for stream fp.
X */
X/* ARGSUSED */
Xqfflush(fp)
XFILE *fp;
X{
X	/* Alas, stdio does not permit this */
X}
X
X
X/*
X * Count the number of remaining lines in file fp.
X * Do not move the file pointer.
X */
Xlinecnt(fp)
XFILE *fp;
X{
X	long	curpos;
X	register int	nlines = 0;
X	register int	c;
X
X	if (fp == NULL)
X		return 0;
X	curpos = ftell(fp);
X	while ((c = getc(fp)) != EOF)
X		if (c == '\n')
X			nlines++;
X	fseek(fp, curpos, 0);
X	return nlines;
X}
X
X
X/*
X * Transmit file to system.
X */
Xtransmit(sp, file)
Xregister struct srec *sp;
Xchar	*file;
X{
X	register FILE *ifp, *ofp;
X	register int	c;
X	struct hbuf hh;
X	char	TRANS[BUFLEN];
X
X#ifdef DEBUG
X	fprintf(stderr, "xmit %s to %s using %s\n", file, sp->s_name, sp->s_xmit);
X#endif
X	ifp = xfopen(file, "r");
X	for(c=0;c<NUNREC;c++)
X		hh.unrec[c] = NULL;
X	if (hread(&hh, ifp, TRUE) == NULL)
X		return;
X	strcpy(TRANS, "/tmp/trXXXXXX");
X	ofp = xfopen(mktemp(TRANS), "w");
X	if (index(sp->s_flags, 'A') == NULL)
X		hwrite(&hh, ofp);
X	else
X		ohwrite(&hh, ofp);
X	while ((c = getc(ifp)) != EOF)
X		putc(c, ofp);
X	fclose(ifp);
X	fclose(ofp);
X	if (*sp->s_xmit == '\0' || index(sp->s_flags, 'F') || index(sp->s_flags, 'U'))
X		sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
X	else
X		sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
X#ifdef DEBUG
X	fprintf(stderr, "%s\n", bfr);
X#endif
X	fwait(fsubr(pshell, bfr, (char *)NULL));
X	unlink(TRANS);
X}
X
X
X/*
X * Cancel the article whose header is in hp, by posting a control message
X * to cancel it.  The scope of the control message depends on who would
X * really be willing to cancel it.  It is sent as far as it will do any good.
X * notauthor is true iff the person posting this article is not the
X * real author of the article being cancelled.
X */
Xcancel(ofp, hp, notauthor)
XFILE *ofp;
Xstruct hbuf *hp;
Xint	notauthor;
X{
X	int	pid;
X
X	fflush(ofp);
X	pid = fork();
X	if (pid < 0) {
X		perror("readnews: cancel");
X		return 0;
X	}
X	if (pid > 0)
X		return 0;
X	if (notauthor)
X		sprintf(bfr, "%s/%s -t 'cmsg cancel %s' -n %s -d local < /dev/null",
X		    LIB, "inews", hp->ident, hp->nbuf);
X	else {
X		if (hp->distribution[0] == '\0')
X			sprintf(bfr, "%s/%s -t 'cmsg cancel %s' -n %s < /dev/null",
X			    LIB, "inews", hp->ident, hp->nbuf);
X		else
X			sprintf(bfr, "%s/%s -t 'cmsg cancel %s' -n %s -d %s < /dev/null",
X			    LIB, "inews", hp->ident, hp->nbuf, hp->distribution);
X	}
X	execl("/bin/sh", "sh", "-c", bfr, (char *) 0);
X	perror(bfr);
X	exit(1);
X#ifdef lint
X	return 0;
X#endif lint
X}
X
X
Xdash(num, ofp)
Xregister int	num;
Xregister FILE *ofp;
X{
X	register int	i;
X
X	for (i = 0; i < num; i++)
X		putc('-', ofp);
X	putc('\n', ofp);
X}
X
X
Xhelp(ofp)
Xregister FILE *ofp;
X{
X	register FILE *fp;
X	register int	c;
X	char temp[BUFLEN];
X
X	if (cflag) {
Xoneline:
X		fprintf(ofp, "(n)ext re(p)rint (w)rite (q)uit (r)eply\
X (c)ancel -[n] +[n] (f)ollowup (N)ext (U)nsubscribe (v)ersion\n");
X		return;
X	}
X	sprintf(temp, "%s/%s", LIB, "help");
X	if ((fp = fopen(temp, "r")) == NULL) {
X		fprintf(ofp, "No help file.\n");
X		goto oneline;
X	}
X	while ((c = getc(fp)) != EOF && !sigtrap)
X		putc(c, ofp);
X	fclose(fp);
X}
X
X
Xpout(ofp)
XFILE *ofp;
X{
X	register char	*p, *q, *r;
X
X	p = PAGER;
X	q = lbuf;
X	while ((*q = *p++) != '\0')
X		if (*q++ == FMETA) {
X			q--;
X			r = filename;
X			while ((*q++ = *r++) != '\0')
X				;
X			q--;
X		}
X	fwait(fsubr(ushell, lbuf, (char *)NULL));
X	fprintf(ofp, "\n");
X}
X
X/*
X * Print a very brief version of the date in question.
X */
Xchar *
Xbriefdate(datestr)
Xchar *datestr;
X{
X	time_t dt, now;
X	char *tmstr;
X	char *wkday, *monthdate, *timeofday;
X	static char rbuf[20];
X
X	dt = cgtdate(datestr);
X	tmstr = ctime(&dt);
X
X	wkday = tmstr; tmstr[3] = '\0';
X	monthdate = tmstr+4; tmstr[10] = '\0';
X	timeofday = tmstr+11; tmstr[16] = '\0';
X
X	(void) time(&now);
X	if (now - dt < 7 * DAYS)
X		strcpy(rbuf, wkday);
X	else
X		strcpy(rbuf, monthdate);
X	strcat(rbuf, " ");
X	strcat(rbuf, timeofday);
X	return rbuf;
X}
X
X/*
X * Return TRUE iff stdout is /dev/null.
X */
Xignoring()
X{
X	struct stat ss, ns;
X
X	fstat(1, &ss);
X	stat("/dev/null", &ns);
X	if (ss.st_dev == ns.st_dev && ss.st_rdev == ns.st_rdev)
X		return TRUE;
X	return FALSE;
X}
*-*-END-of-src/rfuncs2.c-*-*
echo x - src/rmgroup.sh
sed 's/^X//' >src/rmgroup.sh <<'*-*-END-of-src/rmgroup.sh-*-*'
X: '@(#)rmgroup.sh	1.3 8/21/84'
Xfor group
Xdo
X	echo "Removing newsgroup $group"
X	qgrp="`echo $group | sed 's/\./\\\./g'`"
X	if
X		grep -s "^$qgrp " LIBDIR/active
X	then
X		ed - LIBDIR/active << E_O_F
X/^$qgrp /d
Xw
Xq
XE_O_F
X		dir=SPOOLDIR/"`echo $group | sed 's/\./\//g'`"
X		if
X			[ -d "$dir" ]
X		then
X			rm -r "$dir"
X		else
X			echo "$0: $dir: no spool directory" 2>&1
X		fi
X	else
X		echo "$0: $group: no such newsgroup" 2>&1
X	fi
Xdone
Xexit 0
*-*-END-of-src/rmgroup.sh-*-*
echo x - src/rparams.h
sed 's/^X//' >src/rparams.h <<'*-*-END-of-src/rparams.h-*-*'
X/*
X * rparams.h - parameters for readnews, rfuncs, and readr.
X */
X
X/*	@(#)rparams.h	2.14	8/28/84	*/
X
X#include "params.h"
X
X/* flags for readnews */
X#define pflag	options[0].flag
X#define tflag	options[1].flag
X#define aflag	options[2].flag
X#define nflag	options[3].flag
X#define cflag	options[4].flag
X#define lflag	options[5].flag
X#define rflag	options[6].flag
X#define sflag	options[7].flag
X#define xflag	options[8].flag
X#define hflag	options[9].flag
X#define Mflag	options[10].flag
X#define fflag	options[11].flag
X#define uflag	options[12].flag
X#define eflag	options[13].flag
X
X#define	NEXT	0
X#define SPEC	1
X
X#define	FORWARD	0
X#define BACKWARD 1
X
X#define UNKNOWN 0001	/* possible modes for news program */
X#define MAIL	0004
X#define ANY	0007
X
X#define BITMAPSIZE	2048	/* Must be a power of 2 */
X
Xstruct optable {			/* options table. */
X	char	optlet;		/* option character. */
X	char	filchar;	/* if to pickup string, fill character. */
X	int	flag;		/* TRUE if have seen this opt. */
X	int	newstate;	/* STRING if takes arg, else OPTION */
X	int	oldmode;	/* OR of legal input modes. */
X	int	newmode;	/* output mode. */
X	char	*buf;		/* string buffer */
X};
X
X/* external declarations specific to readnews */
Xextern	char	*infile, *outfile, PAGER[];
Xextern	char	bitmap[], *temprc, *MAILER;
X
X#ifndef ROOTID
Xextern	int	ROOTID;
X#endif
X
X#ifdef NOTIFY
Xextern	char	*TELLME;
X#endif
X
Xextern char	filename[],coptbuf[],datebuf[],titlebuf[],afline[];
Xextern char	newsrc[],groupdir[],rcbuf[],*rcline[],*argvrc[];
Xextern int	mode, ngrp, line, newrc(), readmode;
Xextern long	bit, obit, last, ngsize, minartno;
Xextern FILE	*rcfp,*actfp;
Xextern time_t	atime;
Xextern struct stat statbuf;
Xextern struct optable *optpt, options[];
Xextern int	actdirect, rcreadok, zapng;
X
X#ifndef lint
X/* lint gets very mad about i-minartno, this is one way of shutting it up */
X/* macros */
X#define get(i)	((i<minartno)? 0 : (bitmap[(i-minartno) >> 3] & (1 << (i-minartno) % 8)))
X#define set(i)	if (i>=minartno) bitmap[(i-minartno) >> 3] |= (1 << (i-minartno) % 8);else
X#define clear(i) if (i>=minartno) bitmap[(i-minartno) >> 3] &= ~(1 << (i-minartno) % 8);else
X#endif !lint
X
X#define FCLOSE(fp)	{if (fp != NULL) {fclose(fp);fp = NULL;}}
*-*-END-of-src/rparams.h-*-*
echo x - src/sendbatch.sh
sed 's/^X//' >src/sendbatch.sh <<'*-*-END-of-src/sendbatch.sh-*-*'
X: '@(#)sendbatch.sh	1.3 8/21/84'
Xfor rmt in $*
Xdo
X	while test $? -eq 0 -a \( -s BATCHDIR/$rmt -o -s BATCHDIR/$rmt.work \)
X	do
X		LIBDIR/batch BATCHDIR/$rmt 50000 | \
X			if test -s BATCHDIR/$rmt.cmd
X			then
X				BATCHDIR/$rmt.cmd
X			else
X				uux - UUXFLAGS $rmt!rnews
X			fi
X	done
Xdone
*-*-END-of-src/sendbatch.sh-*-*
echo x - src/sendnews.c
sed 's/^X//' >src/sendnews.c <<'*-*-END-of-src/sendnews.c-*-*'
X/*
X * sendnews - send news article by mail.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)sendnews.c	2.8	8/14/84";
X#endif !lint
X
X#include <stdio.h>
X#include <ctype.h>
X#ifndef USG
Xstruct utsname {
X	char	Sysname[9];
X	char	nodename[33];
X	char	release[9];
X	char	version[9];
X};
X#else
X#include <sys/utsname.h>
X#endif
X
X#define	eq(a,b)	(strcmp(a,b) == 0)
X#define	LNLEN	7			/* strlen("ucbvax!") */
X
Xchar *index();
Xchar buffer[BUFSIZ];
Xint linecount, oflag = 0, aflag = 0, bflag = 0, toflag = 0;
X
XFILE *popen();
Xmain(argc, argv)
Xchar **argv;
X{
X	FILE *out;
X	char newsgroup[100];
X	char sysn[20];
X	struct utsname ubuf;
X#ifdef lint
X	argc = argc;
X#endif lint
X
X	while (**(++argv) == '-') {
X		if (*++*argv == 'o')
X			oflag++;
X		else if (**argv == 'a')
X			aflag++;
X		else if (**argv == 'b')
X			bflag++;
X		else if (**argv == 'n')
X			strcpy(newsgroup, *(++argv));
X	}
X	if (aflag && bflag) {
X		fprintf(stderr, "'-a' and '-b' options mutually exclusive.\n");
X		exit(1);
X	}
X
X#ifdef debug
X	printf("mail %s\n", *argv);
X	sprintf(buffer, "cat");
X#else
X	sprintf(buffer, "mail %s", *argv);
X#endif
X	out = popen(buffer, "w");
X	uname(&ubuf);
X	strcpy(sysn, ubuf.nodename);
X	strcat(sysn, "!");
X
X	/* Standard mail prelude to make the formatters happy */
X	fprintf(out, "To: %s\n", *argv);
X	fprintf(out, "Subject: network news article\n");
X	fprintf(out, "\n");
X
X	while (fgets(buffer, sizeof buffer, stdin)) {
X		if (*newsgroup && ngline()) {
X			if (oflag)
X				sprintf(buffer, "%s\n", newsgroup);
X			else
X				sprintf(buffer, "Newsgroups: %s\n", newsgroup);
X		}
X		putc('N', out);
X		fputs(buffer, out);
X	}
X	pclose(out);
X	exit(0);
X}
X
Xngline()
X{
X	if (oflag)
X		return linecount == 2;
X	if (!toflag && (!strncmp("Newsgroups: ", buffer, 12) ||
X		!strncmp("To: ",buffer, 4)))
X		return ++toflag;
X	return 0;
X}
X
X/*
X * Return the ptr in sp at which the character c appears;
X * NULL if not found
X */
X
Xchar *
Xindex(sp, c)
Xregister char *sp, c;
X{
X	do {
X		if (*sp == c)
X			return(sp);
X	} while (*sp++);
X	return(NULL);
X}
*-*-END-of-src/sendnews.c-*-*
echo x - src/uname.c
sed 's/^X//' >src/uname.c <<'*-*-END-of-src/uname.c-*-*'
X/*
X * This routine is compatible with the Unix T/S system call uname,
X * which figures out the name of the local system.
X * However, we do it by reading the file /usr/include/whoami.h.
X * This avoids having to recompile uucp for each site and hence
X * avoids having to distribute the source to uucp to people who
X * have only binary licenses.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)uname.c	2.6	8/13/84";
X#endif !lint
X
X#include "params.h"
X
X#ifdef UNAME
X# define DONE
X#endif
X
X#ifdef GHNAME
Xuname(uptr)
Xstruct utsname *uptr;
X{
X	gethostname(uptr->nodename, sizeof (uptr->nodename));
X}
X# define DONE
X#endif
X
X#ifndef DONE
X#define	HDRFILE "/usr/include/whoami.h"
X
Xuname(uptr)
Xstruct utsname *uptr;
X{
X	char buf[BUFSIZ];
X	FILE *fd;
X
X	fd = fopen(HDRFILE, "r");
X	if (fd == NULL) {
X		fprintf(stderr, "Cannot open %s\n", HDRFILE);
X		exit(1);
X	}
X
X	for (;;) {	/* each line in the file */
X		if (fgets(buf, sizeof buf, fd) == NULL) {
X			fprintf(stderr, "no sysname in %s\n", HDRFILE);
X			fclose(fd);
X			exit(2);
X		}
X		if (sscanf(buf, "#define sysname \"%[^\"]\"", uptr->nodename) == 1) {
X			fclose(fd);
X			return;
X		}
X	}
X}
X#endif
*-*-END-of-src/uname.c-*-*
echo x - src/unbatch.c
sed 's/^X//' >src/unbatch.c <<'*-*-END-of-src/unbatch.c-*-*'
X/*
X * unbatchnews: extract news in batched format and process it one article
X * at a time.  The format looks like
X *	#! rnews 1234
X *	article containing 1234 characters
X *	#! rnews 4321
X *	article containing 4321 characters
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)unbatch.c	1.8	9/3/84";
X#endif !lint
X
X# include <stdio.h>
X
Xchar buf[512];
X
Xmain()
X{
X	register int c;
X	register FILE *pfn;
X	register long size;
X	char *filename;
X	int pid, wpid, exstat;
X	char *mktemp(), *gets();
X	long atol();
X
X	filename = mktemp("/tmp/unbnewsXXXXXX");
X	while(gets(buf) != NULL) {
X		while (strncmp(buf, "#! rnews ", 9)) {
X			fprintf(stderr, "out of sync, skipping %s\n", buf);
X			if (gets(buf) == NULL)
X				exit(0);
X		}
X		size = atol(buf+9);
X		if(size <= 0)
X			break;
X		pfn = fopen(filename, "w");
X		while(--size >= 0 && (c = getc(stdin)) != EOF)
X			putc(c, pfn);
X		fclose(pfn);
X
X		/*
X		 * If we got a truncated batch, don't process the
X		 * last article; it will probably be received again.
X		 */
X		if (size > 0)
X			break;
X
X		/*
X		 * rnews < filename
X		 */
X		while ((pid = fork()) == -1) {
X			fprintf(stderr, "fork failed, waiting...\r\n");
X			sleep(60);
X		}
X		if (pid == 0) {
X			close(0);
X			open(filename, 0);
X#ifdef IHCC
X			sprintf(buf, "%s/%s/rnews", logdir(HOME), LIBDIR);
X			execlp(buf, "rnews", (char *)0);
X#else
X			execlp("rnews", "rnews", (char *)0);
X#endif
X			perror("rnews");
X			exit(1);
X		}
X		while ((wpid = wait(&exstat)) >= 0 && wpid != pid)
X			;
X	}
X	unlink(filename);
X}
*-*-END-of-src/unbatch.c-*-*
echo x - src/uurec.c
sed 's/^X//' >src/uurec.c <<'*-*-END-of-src/uurec.c-*-*'
X/*
X * uurec - receive articles via /bin/mail.
X */
X
X#ifndef lint
Xstatic char	*SccsId = "@(#)uurec.c	2.7	9/3/84";
X#endif !lint
X
X#include "defs.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X/*
X * Process a news article which has been shipped via /bin/mail.
X */
X
X#define FROM	01
X#define NLIN	02
X#define BLANK	03
X#define OTHER	04
X
X#define SKIPPING	010
X#define READING		020
X
X#define BFSZ 250
X
X#define EOT	'\004'
X
X#define A	01
X#define B	02
X
X#ifdef debug
X# define RNEWS "cat"
X#endif
Xextern	char	*strcat(), *strcpy();
Xextern	char	*frombreak();
Xextern	FILE	*popen();
X
X/* ARGSUSED */
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[BFSZ], fbuf[BFSZ];
X	char bfr[BFSZ], *pbfr = bfr;
X	register char *p = NULL;
X	register FILE *pipe = stdout;
X	register int mode, frmflg, pathcnt, format;
X	char *index();
X
X	mode = SKIPPING;
X	frmflg = FALSE;
X	while (fgets(buf, BFSZ, stdin) != NULL) {
X#ifdef debug
X		printf("%o\t%s", mode|type(buf), buf);
X#endif
X		switch (mode | type(buf)) {
X
X		case FROM | SKIPPING:
X			if (frmflg)
X				p = frombreak(p, buf);
X			else
X				p = fbuf;
X			frmflg = TRUE;
X			break;
X
X		case FROM | READING:
X			if (!frmflg) {
X				frmflg = TRUE;
X				p = fbuf;
X				pclose(pipe);
X			}
X			p = frombreak(p, buf);
X			break;
X
X		case NLIN | SKIPPING:
X			if ((isupper(buf[1]) && index(buf, ':')) || !strncmp(buf, "From ", 5))
X				format = B;
X			else
X				format = A;
X#ifdef debug
X			printf("format = %d\n", format);
X#endif
X			mode = READING;
X
X		case NLIN | READING:
X			if (frmflg) {
X				frmflg = FALSE;
X				--p;
X				while (p >= fbuf && *--p != '!')
X					;
X				*++p = '\0';
X				pathcnt = 0;
X#ifdef IHCC
X				sprintf(pbfr, "%s/%s/%s", logdir(HOME),
X						LIBDIR, "rnews");
X#else
X				pbfr = RNEWS;
X#endif
X				if ((pipe = popen(pbfr, "w")) == NULL) {
X					perror("uurec: popen failed");
X					exit(1);
X				}
X			}
X			if (format == A) {
X				if (++pathcnt == 3)
X					fputs(fbuf, pipe);
X				fputs(buf+1, pipe);
X			} else {
X				if (!pathcnt && (!strncmp(buf+1, "From: ", 6) || !strncmp(buf+1, "From ", 5))) {
X					pathcnt++;
X					fprintf(pipe, "From: %s", fbuf);
X					sscanf(buf, "%s %s[^\n]", fbuf, fbuf);
X					fprintf(pipe, "%s\n", fbuf);
X				} else
X					fputs(buf+1, pipe);
X			}
X			break;
X
X		case OTHER | SKIPPING:
X			break;
X
X		case OTHER | READING:
X			pclose(pipe);
X			mode = SKIPPING;
X		}
X	}
X	if (pipe)
X		pclose(pipe);
X	exit(0);
X}
X
Xtype(p)
Xregister char *p;
X{
X	while (*p == ' ' || *p == '?')
X		++p;
X
X	if (*p == 'N')
X		return (NLIN);
X
X	if (strncmp(p, ">From ", 6) == 0)
X		return (FROM);
X
X	if (strncmp(p, "From ", 5) == 0)
X		return (FROM);
X
X	return(OTHER);
X}
X
X/*
X * Get the system name out of a from line.
X */
Xchar *
Xfrombreak(buf, fbuf)
Xregister char *buf, *fbuf;
X{
X	register char *p;
X
X	/* break the line into tokens. */
X	p = fbuf;
X	while (*++p != '\0')
X		switch (*p) {
X		case '\n':
X		case '\t':
X		case ' ':
X			*p = '\0';
X			break;
X		case EOT:
X			goto garbled;
X		default:;
X		}
X	*++p = EOT;
X	*++p = '\0';
X
X	for (p=fbuf; *p != EOT  || p[1] != '\0'; p += strlen(p)+1) {
X		if (strcmp(p, "forwarded") == 0)
X			return(buf);
X		if (strcmp(p, "remote") == 0) {
X			p += strlen(p)+1;
X			if (strcmp(p, "from") == 0) {
X				p += strlen(p)+1;
X				strcpy(buf, p);
X				strcat(buf, "!");
X				return(buf+strlen(buf));
X			}
X		}
X	}
X    garbled:
X	strcat(buf, "???!");
X	return(buf+4);
X}
*-*-END-of-src/uurec.c-*-*
echo x - src/virtterm.c
sed 's/^X//' >src/virtterm.c <<'*-*-END-of-src/virtterm.c-*-*'
X/*
X *  Virtual terminal handler for the HP-2621 terminal.
X *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
X *  Modified by Stephen Hemminger, to use TERMCAP (without curses)
X */
Xstatic char	*SccsId = "@(#)virtterm.c	1.4	7/17/84";
X
X#include <stdio.h>
X#include <ctype.h>
X
X#define MAXPLEN 24
X#define MAXLLEN 80
X#define BOTLINE (ROWS - 1)
X#define DIRTY 01
X
X/* terminal escape sequences from termcap */
X#define HO _tstr[0]		/* home */
X#define CL _tstr[1]		/* clear screen */
X#define CD _tstr[2]		/* clear to end of screen */
X#define CE _tstr[3]		/* clear to end of line */
X#define xUP _tstr[4]		/* up one line */
X#define DO _tstr[5]		/* down one line */
X#define US _tstr[6]		/* underline */
X#define UE _tstr[7]		/* underline end */
X#define BT _tstr[8]		/* backtab */
X#define xBC _tstr[9]		/* backspace */
X#define AL _tstr[10]		/* insert line */
X#define DL _tstr[11]		/* delete line */
X#define CM _tstr[12]		/* cursor move */
X#define CH _tstr[13]		/* cursor horizontal move */
X#define CV _tstr[14]		/* cursor vertical move */
X#define CS _tstr[15]		/* scrolling region */
X#define SF _tstr[16]		/* scroll forwards */
X#define SR _tstr[17]		/* scroll backwards */
X#define TI _tstr[18]		/* start cursor mode */
X#define TE _tstr[19]		/* end cursor mode */
X#define TA _tstr[20]		/* tab char (if not \t) */
X#define CR _tstr[21]		/* carriage return (if not \r) */
X#define xPC _tstr[22]		/* for reading pad character */
Xchar PC;			/* pad character */
Xchar *BC, *UP;			/* external variables for tgoto */
X
Xstatic char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc";
Xchar *_tstr[23];
Xint     HOlen;			/* length of HO string */
X
X
X/* terminal flags */
X#define BS _tflg[0]		/* can backspace */
X#define AM _tflg[1]		/* has auto margins */
X#define XN _tflg[2]		/* no newline after wrap */
X#define RET !_tflg[3]		/* has carriage return */
X#define NS _tflg[4]		/* has SF (scroll forward) */
X#define PT _tflg[5]		/* has tabs */
X#define XT _tflg[6]		/* tabs are destructive */
Xint	GT = 1;			/* tab stops on terminal are set */
X
Xstatic char bname[] = "bsamxnncnsptxt";
Xchar _tflg[7];
X
X
Xextern char *tgoto(), *tgetstr();
Xextern char *getenv(), *strcpy();
X
X#define ULINE 0200
X#define CURSEEN 1
X
X/* Constants accessable by user */
Xint     hasscroll;		/* scrolling type, 0 == no scrolling */
Xint     ROWS;			/* number of lines on screen */
Xint     COLS;			/* width of screen */
X
Xstruct line {
X	char    len;
X	char    flags;
X	char    l[MAXLLEN];
X};
X
Xint     _row, _col;
Xint     _srow, _scol;
Xstruct line _virt[MAXPLEN], _actual[MAXPLEN];
Xint     _uline = 0;
Xint     _junked = 1;
Xint     _curjunked;
Xint     _dir = 1;
Xint	_shifttop, _shiftbot;
Xint	_shift;
Xint	_scratched;
Xint     vputc();
X
X
X
X/*
X * Tell refresh to shift lines in region upwards count lines.  Count
X * may be negative.  The virtual image is not shifted; this may change
X * later.  The variable _scratched is set to supress all attempts to
X * shift.
X */
X
Xushift(top, bot, count) {
X	if (_scratched)
X		return;
X	if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) {
X		_scratched++;
X		return;
X	}
X	_shifttop = top;
X	_shiftbot = bot;
X	_shift += count;
X}
X
X
X
X/*
X * generate a beep on the terminal
X */
X
Xbeep() {
X	vputc('\7');
X}
X
X/*
X * Move to one line below the bottom of the screen.
X */
X
Xbotscreen() {
X	_amove(BOTLINE, 0);
X	vputc('\n');
X	vflush();
X}
X
X
X
Xmove(row, col) {
X	if (row < 0 || row >= ROWS || col < 0 || col >= COLS)
X		return;
X	_row = row;
X	_col = col;
X}
X
X
X
X/*
X * Output string at specified location.
X */
X
Xmvaddstr(row, col, str)
X	char *str;
X	{
X	move(row, col);
X	addstr(str);
X}
X
X
Xaddstr(s)
Xchar   *s;
X{
X	register char  *p;
X	register struct line   *lp;
X	register int    col = _col;
X
X	lp = &_virt[_row];
X	if (lp->len < col) {
X		p = &lp->l[lp->len];
X		while (lp->len < col) {
X			*p++ = ' ';
X			lp->len++;
X		}
X	}
X	for (p = s; *p != '\0'; p++) {
X		if (*p == '\n') {
X			lp->len = col;
X			lp->flags |= DIRTY;
X			col = 0;
X			if (++_row >= ROWS)
X				_row = 0;
X			lp = &_virt[_row];
X		}
X		else {
X			lp->l[col] = *p;
X			lp->flags |= DIRTY;
X			if (++col >= COLS) {
X				lp->len = COLS;
X				col = 0;
X				if (++_row >= ROWS)
X					_row = 0;
X				lp = &_virt[_row];
X			}
X		}
X	}
X	if (lp->len <= col)
X		lp->len = col;
X	_col = col;
X}
X
X
X
Xaddch (c) {
X	register struct line   *lp;
X	register char  *p;
X
X	lp = &_virt[_row];
X	if (lp->len < _col) {
X		p = &lp->l[lp->len];
X		while (lp->len < _col) {
X			*p++ = ' ';
X			lp->len++;
X		}
X	}
X	lp->l[_col] = c;
X	if (lp->len == _col)
X		lp->len++;
X	if (++_col >= COLS) {
X		_col = 0;
X		if (++_row >= ROWS)
X			_row = 0;
X	}
X	lp->flags |= DIRTY;
X}
X
X
X
Xclrtoeol() {
X	register struct line   *lp;
X
X	lp = &_virt[_row];
X	if (lp->len > _col) {
X		lp->len = _col;
X		lp->flags |= DIRTY;
X	}
X}
X
X
X/*
X * Clear an entire line.
X */
X
Xclrline(row) {
X	register struct line   *lp;
X
X	lp = &_virt[row];
X	if (lp->len > 0) {
X		lp->len = 0;
X		lp->flags |= DIRTY;
X	}
X}
X
X
X
Xclear() {
X	erase();
X	_junked++;
X}
X
X
X
Xerase() {
X	register    i;
X
X	for (i = 0; i < ROWS; i++) {
X		_virt[i].len = 0;
X		_virt[i].flags |= DIRTY;
X	}
X}
X
X
X
Xrefresh() {
X	register i;
X	int j, len;
X	register char *p, *q;
X
X	if (checkin())
X		return;
X	i = 1;
X	if (_junked) {
X		_sclear();
X		_junked = 0;
X	} else if (! _scratched) {
X		if (_shift > 0) {
X			_ushift(_shifttop, _shiftbot, _shift);
X		} else if (_shift < 0) {
X			i = _dshift(_shifttop, _shiftbot, -_shift);
X		} else {
X			i = _dir;
X		}
X	}
X	_dir = i;
X	_shift = 0;
X	if (checkin())
X		return;
X	_fixlines();
X	for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) {
X		if ((_virt[i].flags & DIRTY) == 0)
X			continue;
X		_ckclrlin(i);		/* decide whether to do a clear line */
X					/* probably should consider cd as well */
X		len = _virt[i].len;
X		if (_actual[i].len < len)
X			len = _actual[i].len;
X		p = _virt[i].l;
X		q = _actual[i].l;
X		for (j = 0; j < len; j++) {
X			if (*p != *q) {
X				_amove(i, j);
X				_aputc(*p);
X				*q = *p;
X			}
X			p++, q++;
X		}
X		len = _virt[i].len;
X		if (_actual[i].len > len) {
X			_clrtoeol(i, len);
X		}
X		else {
X			for (; j < len; j++) {
X				if (*p != ' ') {
X					_amove(i, j);
X					_aputc(*p);
X				}
X				*q++ = *p++;
X			}
X			_actual[i].len = len;
X		}
X		if (checkin())
X			return;
X	}
X	_dir = 1;
X	if (CURSEEN)
X		_amove(_row, _col);
X	vflush();			/* flush output buffer */
X	_scratched = 0;
X}
X
X
X_dshift(top, bot, count) {
X	register    i;
X
X	if (count >= bot - top || hasscroll < 4) {  /* must have CS or AL/DL */
X		_scratched++;
X		return 1;
X	}
X	for (i = bot - count; _actual[i].len == 0; i--)
X		if (i == top)
X			return 1;
X	for (i = top; i <= bot; i++)
X		_virt[i].flags |= DIRTY;
X	for (i = bot; i >= top + count; i--)
X		_actual[i] = _actual[i - count];
X	for (; i >= top; i--)
X		_actual[i].len = 0;
X
X	if (hasscroll != 5) {		/* can we define scrolling region, and scroll back */
X		tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */
X		_curjunked = 1;
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(SR, 1, vputc);/* scroll back */
X		tputs(tgoto(CS, BOTLINE, 0), 1, vputc);
X		_curjunked = 1;
X	} else {
X		_amove(bot - count + 1, 0);
X		if (CD && bot == BOTLINE)
X			tputs(CD, 1, vputc);
X		else {
X			for (i = count; --i >= 0;)
X				tputs(DL, ROWS - _srow, vputc);
X		}
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(AL, ROWS - _srow, vputc);
X	}
X	return -1;
X}
X
X
X_ushift(top, bot, count) {
X	register    i;
X
X	if (count >= bot - top || hasscroll == 0) {
X		_scratched++;
X		return;
X	}
X	for (i = top + count; _actual[i].len == 0; i++)
X		if (i == bot)
X			return;
X	if (hasscroll == 1 || hasscroll == 3) {
X		/* we cheat and shift the entire screen */
X		/* be sure we are shifting more lines into than out of position */
X		if ((bot - top + 1) - count <= ROWS - (bot - top + 1))
X			return;
X		top = 0, bot = BOTLINE;
X	}
X	for (i = top; i <= bot; i++)
X		_virt[i].flags |= DIRTY;
X	for (i = top; i <= bot - count; i++)
X		_actual[i] = _actual[i + count];
X	for (; i <= bot; i++)
X		_actual[i].len = 0;
X
X	if (hasscroll != 5) {
X		if (top != 0 || bot != BOTLINE) {
X			tputs(tgoto(CS, bot, top), 0, vputc);
X			_curjunked = 1;
X		}
X		_amove(bot, 0);	/* move to bottom */
X		for (i = 0; i < count; i++) {
X			if (SF)		/* scroll forward */
X				tputs(SF, 1, vputc);
X			else
X				vputc('\n');
X		}
X		if (top != 0 || bot != BOTLINE) {
X			tputs(tgoto(CS, BOTLINE, 0), 0, vputc);
X			_curjunked = 1;
X		}
X	} else {
X		_amove(top, 0);
X		for (i = count; --i >= 0;)
X			tputs(DL, ROWS - _srow, vputc);
X		if (bot < BOTLINE) {
X			_amove(bot - count + 1, 0);
X			for (i = count; --i >= 0;)
X				tputs(AL, ROWS - _srow, vputc);
X		}
X	}
X}
X
X
X_sclear() {
X	register struct line   *lp;
X
X	tputs(CL, 0, vputc);
X	_srow = _scol = 0;
X	for (lp = _actual; lp < &_actual[ROWS]; lp++) {
X		lp->len = 0;
X	}
X	for (lp = _virt; lp < &_virt[ROWS]; lp++) {
X		if (lp->len != 0)
X			lp->flags |= DIRTY;
X	}
X}
X
X
X_clrtoeol(row, col) {
X	register struct line *lp = &_actual[row];
X	register i;
X
X	if (CE && lp->len > col + 1) {
X		_amove(row, col);
X		tputs(CE, 1, vputc);
X	} else {
X		for (i = col ; i < lp->len ; i++) {
X			if (lp->l[i] != ' ') {
X				_amove(row, i);
X				_aputc(' ');
X			}
X		}
X	}
X	lp->len = col;
X}
X
X
X_fixlines() {
X	register struct line   *lp;
X	register char  *p;
X	register int    i;
X
X	for (i = 0; i < ROWS; i++) {
X		lp = &_virt[i];
X		if (lp->flags & DIRTY) {
X			lp = &_virt[i];
X			for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';);
X			lp->len = p + 1 - lp->l;
X			if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
X				lp->flags &= ~DIRTY;
X		}
X	}
X}
X
X
X/*
X * Consider clearing the line before overwriting it.
X * We always clear a line if it has underlined characters in it
X * because these can cause problems.  Otherwise decide whether
X * that will decrease the number of characters to change.  This
X * routine could probably be simplified with no great loss.
X */
X
X_ckclrlin(i) {
X	int     eval;
X	int     len;
X	int     first;
X	register struct line   *vp, *ap;
X	register int    j;
X
X	if (!CE)
X		return;
X	ap = &_actual[i];
X	vp = &_virt[i];
X	len = ap->len;
X	eval = -strlen(CE);
X	if (len > vp->len) {
X		len = vp->len;
X		eval = 0;
X	}
X	for (j = 0; j < len && vp->l[j] == ap->l[j]; j++);
X	if (j == len)
X		return;
X	first = j;
X	while (j < len) {
X		if (vp->l[j] == ' ') {
X			if (ap->l[j] != ' ') {
X				while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') {
X					eval++;
X				}
X				if (j == len)
X					eval++;
X				continue;
X			}
X		}
X		else {
X			if (vp->l[j] == ap->l[j]) {
X				while (++j < len && vp->l[j] == ap->l[j]) {
X					eval--;
X				}
X				continue;
X			}
X		}
X		j++;
X	}
X	if (US) {
X		for (j = 0 ; j < ap->len ; j++) {
X			if (ap->l[j] & ULINE) {
X				eval = 999;
X				if (first > j)
X					first = j;
X				break;
X			}
X		}
X	}
X	for (j = first; --j >= 0;)
X		if (vp->l[j] != ' ')
X			break;
X	if (j < 0)
X		first = 0;
X	if (eval > 0) {
X		_amove(i, first);
X		tputs(CE, 0, vputc);
X		_actual[i].len = first;
X	}
X}
X
X
X
X/*
X * Move routine
X * 	first compute direct cursor address string and cost
X *	then relative motion string and cost,
X *	then home then relative and cost
X *	choose smallest and do it.
X *
X *	The plod stuff is to build the strings (with padding) then decide
X */
Xstatic char *plodstr;		/* current location in relmove string */
X
Xplodput(c) { *plodstr++ = c; }
X
X_amove(row, col) {
X	char direct[20];
X	char rel[MAXPLEN*10 + MAXLLEN*10];    /* longest move is full screen */
X	char ho[MAXPLEN*10 + MAXLLEN*10];
X	int cost, newcost;
X	register char *movstr;
X
X	if (row == _srow && col == _scol && _curjunked == 0)
X		return;
X	_setul(0);
X
X	cost = 999;
X	if (CM) {
X		plodstr = direct;
X		tputs(tgoto(CM, col, row), 0, plodput);
X		*plodstr = '\0';
X		cost = plodstr - direct;
X		movstr = direct;
X	}
X	if (_curjunked == 0) {
X		plodstr = rel;
X		if (_vmove(_srow, row) >= 0
X		 && _hmove(_scol, col, row) >= 0
X		 && (newcost = plodstr - rel) < cost) {
X			*plodstr = '\0';
X			cost = newcost;
X			movstr = rel;
X		}
X	}
X	if (cost > HOlen) {	/* is it worth calculating */
X		plodstr = ho;
X		tputs(HO, 0, plodput);
X		if (_vmove(0, row) >= 0
X		 && _hmove(0, col, row) >= 0
X		 && (newcost = plodstr - ho) < cost) {
X			*plodstr = '\0';
X			cost = newcost;
X			movstr = ho;
X		}
X	}
X
X	while (--cost >= 0) {
X		vputc(*movstr++);
X	}
X	_srow = row, _scol = col;
X	_curjunked = 0;
X}
X
X
X_vmove(orow, nrow) {
X	char direct[128];
X	char *saveplod = plodstr;
X
X	if (CV) {
X		plodstr = direct;
X		tputs(tgoto(CV, nrow, nrow), 0, plodput);
X		*plodstr = '\0';
X		plodstr = saveplod;
X	}
X	if (orow > nrow) {		/* cursor up */
X		if (! UP)
X			return -1;
X		while (orow > nrow) {
X			tputs(UP, 1, plodput);
X			orow--;
X		}
X	}
X	while (orow < nrow) {		/* cursor down */
X		if (DO)
X			tputs(DO, 1, plodput);
X		else
X			*plodstr++ = '\n';
X		orow++;
X	}
X	if (CV && plodstr - saveplod >= strlen(direct)) {
X		register char *p;
X		plodstr = saveplod;
X		for (p = direct ; *plodstr = *p++ ; plodstr++);
X	}
X	return 0;
X}
X
X
X_hmove(ocol, ncol, row) {
X	char direct[128];
X	char ret[MAXLLEN*10];
X	char *saveplod = plodstr;
X	char *movstr;
X	int cost, newcost;
X
X	cost = 999;
X	if (CH) {
X		plodstr = direct;
X		tputs(tgoto(CH, ncol, ncol), 0, plodput);
X		cost = plodstr - direct;
X		movstr = direct;
X		plodstr = saveplod;
X	}
X	if (RET && ocol > ncol) {	/* consider doing carriage return */
X		plodstr = ret;
X		if (CR)
X			tputs(CR, 1, plodput);
X		else
X			*plodstr++ = '\r';
X		if (_relhmove(0, ncol, row) >= 0
X		 && (newcost = plodstr - ret) < cost) {
X			cost = newcost;
X			movstr = ret;
X		}
X		plodstr = saveplod;
X	}
X	if (_relhmove(ocol, ncol, row) < 0) {
X		if (cost == 999)
X			return -1;
X		goto copy;
X	}
X	if (plodstr - saveplod > cost) {
Xcopy:		plodstr = saveplod;
X		while (--cost >= 0)
X			*plodstr++ = *movstr++;
X	}
X	return 0;
X}
X
X
X
X_relhmove(ocol, ncol, row) {
X	int tab;
X
X	if (ocol < ncol && PT && GT) {	/* tab (nondestructive) */
X		while ((tab = (ocol + 8) & ~07) <= ncol) {
X			if (TA)
X				tputs(TA, 1, plodput);
X			else
X				*plodstr++ = '\t';
X			ocol = tab;
X		}
X		if (tab < COLS && tab - ncol < ncol - ocol) {
X			if (TA)
X				tputs(TA, 1, plodput);
X			else
X				*plodstr++ = '\t';
X			ocol = tab;
X		}
X	} else if (BT && GT && ocol > ncol) {	/* backwards tab */
X		while ((tab = (ocol - 1) &~ 07) >= ncol) {
X			if (BS && tab == ocol - 1) {
X				if (BC)
X					tputs(BC, 1, plodput);
X				else
X					*plodstr++ = '\b';
X			} else
X				tputs(BT, 1, plodput);
X			ocol = tab;
X		}
X		if (ncol - tab + 1 < ocol - ncol) {
X			tputs(BT, 1, plodput);
X			ocol = tab;
X		}
X	}
X	if (ocol > ncol) {			/* cursor left */
X		if (! BS)
X			return -1;
X		while (ocol > ncol) {
X			if (BC != NULL)
X				tputs(BC, 1, plodput);
X			else
X				*plodstr++ = '\b';
X			ocol--;
X		}
X	}
X	if (ocol < ncol) {			/* cursor right */
X		register struct line *lp = &_actual[row];
X		/*
X		 * This code doesn't move over underlined characters properly,
X		 * but in practice this doesn't seem to matter.
X		 */
X		while (ocol < ncol) {
X			if (ocol < lp->len)
X				*plodstr++ = lp->l[ocol];
X			else
X				*plodstr++ = ' ';
X			ocol++;
X		}
X	}
X	return 0;
X}
X
X
X
X_aputc(c) {
X	if (AM && _scol == COLS - 1 && _srow == ROWS - 1)
X		return;
X	_setul(c & ULINE);
X	vputc(c & ~ULINE);
X	if (++_scol >= COLS) {
X		if (! AM) {
X			_scol--;
X		} else  if (XN) {
X			_curjunked++;
X		} else {
X			_scol = 0;
X			++_srow;
X		}
X	}
X}
X
X
X_setul(on) {
X	if (on) {
X		if (_uline == 0 && US != NULL) {
X			tputs(US, 1, vputc);
X			_uline = 1;
X		}
X	}
X	else {
X		if (_uline != 0 && UE != NULL) {
X			tputs(UE, 1, vputc);
X			_uline = 0;
X		}
X	}
X}
X
X
X/*
X * Initialize termcap strings for later use.
X */
Xinitterm() {
X	static char tcbuf[1024];	/* termcap buffer */
X	register char  *cp;
X
X	if ((cp = getenv("TERM")) == NULL)
X		xerror("TERM not set in environment");
X
X	switch (tgetent(tcbuf, cp)) {
X		case 0:
X			xerror("Terminal not found in TERMCAP");
X		case -1:
X			xerror("Can't open /etc/termcap");
X		case 1:
X			break;
X	}
X
X	if ((ROWS = tgetnum("li")) == -1
X	 || (COLS = tgetnum("co")) == -1)
X		xerror("Can't get screen size");
X	_zap();
X
X	if (CL == NULL)
X		xerror ("No clear screen defined");
X
X	if (HO == NULL && CM == NULL)
X		xerror("No home or cursor addressing");
X	if (HO)
X		HOlen = strlen(HO);
X	else
X		HOlen = 999;
X
X	PC = xPC ? xPC[0] : 0;
X	BC = xBC;
X	UP = xUP;
X
X	if (tgetnum("ug") > 0)
X		US = UE = NULL;
X
X	if (XT)				/* Destructive tab code not included */
X		PT = 0;			/* to keep things simple */
X
X	if (ROWS > MAXPLEN)
X		ROWS = MAXPLEN;
X	if (COLS > MAXLLEN) {
X		COLS = MAXLLEN;
X		AM = XN = 1;
X	}
X
X	/* Select article scrolling algorithm.  We prefer scrolling region
X	   over insert/delete line because it's faster on the HP */
X	hasscroll = 0;
X	if (!NS) {
X		hasscroll = 1;
X		if (SR)
X			hasscroll = 3;
X		if (CS)
X			hasscroll++;
X	}
X	if (AL && DL && hasscroll != 4)
X		hasscroll = 5;
X}
X
X
Xrawterm() {
X	if (TI != NULL)
X		tputs(TI, 0, vputc);
X}
X
X
Xcookedterm() {
X	if (TE != NULL)
X		tputs(TE, 0, vputc);
X}
X
X
X/* get strings from termcap */
X_zap() {
X	static char tstrbuf[1024];
X	static char *tp;
X	register char  *namp, **sp, *bp;
X
X	tp = tstrbuf;
X	sp = _tstr;
X	for (namp = sname; *namp; namp += 2) {
X		*sp++ = tgetstr(namp, &tp);
X	}
X	bp = _tflg;
X	for (namp = bname; *namp; namp += 2) {
X		*bp++ = tgetflag(namp, &tp);
X	}
X}
*-*-END-of-src/virtterm.c-*-*
exit