File:  [MW Coherent from dump] / coherent / d / bin / nroff / getf.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * getf.c
 * Nroff/Troff.
 * Input handling.
 */

#include <ctype.h>
#include "roff.h"

#define	ok_name(name) ((name[0]>0 && name[0]<128) && (name[1]>=0 && name[1]<128))

/*
 * Get a character handling escapes.
 * Flag bit 0: 0 means die if EOF seen, 1 means return EOF.
 * Flag bit 1: 2 means copy mode.
 * If character is escaped but not recognized as a special escape sequence,
 * set escflag and return character unchanged.
 */
getf(flags) int flags;
{
	register REG *rp;
	register int c, n;
	SPECIAL *sp;
	char charbuf[CBFSIZE], name[2];

	escflag = 0;
	for (;;) {
		if ((c = getl(flags&1)) == esc) {
			if ((c = getl(flags&1)) == EOF)
				return c;
			if (c >= ASCSIZE || esctab[c] == ENUL) {
				escflag = 1;
				return c;
			}
			c = esctab[c] & 0377;
		}
		switch (c) {
		case EIGN:
			continue;
		case ECOM:
			while ((c=getl(0)) != '\n')
				;
			return c;
		case EARG:
			c = getf(0);
			if (!isdigit(c)) {
				printe("bad argument reference");
				break;
			}
			if (strp->x1.s_argp[c-='0'])
				adscore(strp->x1.s_argp[c]);
			continue;
		case ESTR:
			if ((c=getf(0)) != '(') {
				name[0] = c;
				name[1] = '\0';
			} else {
				name[0] = getf(0);
				name[1] = getf(0);
			}
			if (!ok_name(name)) {
				printe("illegal string name");
				break;
			} else if ((rp=findreg(name, RTEXT)))
				adstreg(rp);
			continue;
		case ECHR:
			name[0] = getf(0);
			name[1] = getf(0);
			if (!ok_name(name)) {
				printe("illegal special character name");
				break;
			} else if ((sp = spc_find(name)) != NULL)
				adscore(sp->spc_val);
			continue;
		case ENUM:
			n = 0;
			if ((c=getf(0)) == '+') {
				n = 1;
				c = getf(0);
			} else if (c == '-') {
				n = -1;
				c = getf(0);
			}
			if (c != '(') {
				name[0] = c;
				name[1] = '\0';
			} else {
				name[0] = getf(0);
				name[1] = getf(0);
			}
			if (!ok_name(name)) {
				printe("illegal register name");
				break;
			} else if ((rp=findreg(name, RNUMR)) == NULL)
				spcnreg(name);
			else {
				rp->n_reg.r_nval += n*rp->n_reg.r_incr;
				adsnval(rp->n_reg.r_nval, rp->n_reg.r_form);
			}
			continue;
		case EWID:
			if (flags&2)		/* Copy mode */
				return c;
			if (scandel(charbuf, CBFSIZE) == 0)
				adsnval(0, '1');
			else
				adsnval(getwidth(charbuf), '1');
			continue;
		case EBEG:
			if (flags&2)
				return c;
			++bracelevel;
			return c;
		case EEND:
			if (flags&2)
				return c;
			if (bracelevel == 0)
				printe("\\} without matching \\{");
			else
				--bracelevel;
			return c;
		case EHEX:
			n = hexdigit(getf(0)) * 0x10;
			n += hexdigit(getf(0));
			return n;
		default:
			return c;
		}
	}
}

/*
 * Get width of string by processing into temporary environment.
 */
int
getwidth(cp) char *cp;
{
	int n;

	envsave(ENVWIDTH);
	setline();
	ccc = ind = tif = fill = 0;
	adscore(cp);
	strp->x1.s_eoff = 1;
	process();
	wordbreak(DNULL);
	n = nlinsiz;
	envload(ENVWIDTH);
	return n;
}

/*
 * Like scandel(), except the delimiter is optional.
 * Accepts digits with optional sign without delimiter.
 * Currently called only for pointsize changes, e.g. "\s10" or "\s+1".
 */
scanoptdel(cp, n) register char *cp; int n;
{
	register int c, retval;
	static char buf[2];

	c = getf(0);
	if (c != '-' && c != '+' && !isdigit(c)) {
		buf[0] = c;
		adscore(buf);			/* push back c */
		return scandel(cp, n);		/* and use scandel() */
	}
	retval = 1;
	do {
		if (--n == 0) {
			printe("delimiter argument too large");
			retval = 0;
		} else
			*cp++ = c;
		c = getf(0);
	} while (isdigit(c));
	buf[0] = c;
	adscore(buf);				/* push back nondigit */
	return retval;
}

/*
 * Given a buffer pointer, 'bp', and the size of the buffer, 'n',
 * get an argument surrounded by delimiters and store it in the buffer.
 */
scandel(cp, n) register char *cp; int n;
{
	int retval;
	register int c;
	register int endc;

	retval = 1;
	endc = getf(0);
	while ((c=getf(0)) != endc) {
		if (c == '\n') {
			printe("syntax error");
			return 0;
		}
		if (--n == 0) {
			printe("delimiter argument too large");
			retval = 0;
		} else if (n > 0)
			*cp++ = c;
	}
	*cp++ = '\0';
	return retval;
}

/*
 * Get a character from the current input source.
 * If EOF is seen: return EOF if eofflag, die if !eofflag.
 */
getl(eofflag) int eofflag;
{
	register STR *sp;
	int n;
	register int c;
	register char *cp;

	sp = strp;
	if (sp != NULL)
		sp->x1.s_clnc = sp->x1.s_nlnc;
	for (;;) {
		if (sp == NULL) {
			if (eofflag == 0)
				panic("unexpected end of file");
			return EOF;
		}
		switch (sp->x1.s_type) {
		case SSINP:
			if ((c=getc(stdin)) == EOF)
				break;
			if (c == '\0')
				break;
			if (c == '\n') {
				if ((c=getc(stdin)) == '\n')
					c = '\0';
				ungetc(c, stdin);
				c = '\n';
			}
			goto ret;
		case SFILE:
			if ((c=getc(sp->x2.s_fp)) == EOF) {
				fclose(sp->x2.s_fp);
				nfree(sp->x2.s_fname);
				break;
			}
			goto ret;
		case SCORE:
			if ((c = *sp->x3.s_cp++) == '\0') {
				if (sp->x3.s_srel)
					nfree(sp->x3.s_srel);
				break;
			}
			goto ret;
		case SCTEX:
			if (sp->x4.s_disk  &&  sp->x4.s_sp >= sp->x4.s_bufend) {
				nread((long)sp->x4.s_seek,
					(char *)sp->x4.s_bufp);
				sp->x4.s_seek += DBFSIZE;
				sp->x4.s_sp = sp->x4.s_bufp;
			}
			if ((c = *sp->x4.s_sp++) == '\0') {
				if (strnext())
					continue;
				nfree(sp->x4.s_bufp);
				break;
			}
			goto ret;
		case SCDIV:
			if (sp->x4.s_n-- == 0) {
				if (strnext())
					continue;
				nfree(sp->x4.s_bufp);
				break;
			}
			n = sizeof (CODE);
			cp = (char *) &codeval;
			while (n--)
				*cp++ = geth();
			return ECOD;
		}
		if (sp->x4.s_abuf)
			nfree(sp->x4.s_abuf);
		n = sp->x4.s_eoff;
		strp = strp->x4.s_next;
		nfree((char *)sp);
		sp = strp;
		if (n) {
			if (eofflag == 0)
				panic("incomplete macro in trap");
			return EOF;
		}
	}
ret:
	if ((c&=0377) == '\n')
		sp->x4.s_nlnc++;
	return c;
}

/*
 * In a chained macro, set up input stack to point to next element in chain.
 */
strnext()
{
	register STR *sp;
	register MAC *mp;

	sp = strp;
	if ((mp=sp->x4.s_macp) == NULL)
		return 0;
	sp->x4.s_macp = mp->t_div.m_next;
	switch (mp->t_div.m_type) {
	case MTEXT:
		sp->x1.s_type = SCTEX;
		break;
	case MDIVN:
		sp->x4.s_type = SCDIV;
		if ((sp->x4.s_n=mp->t_div.m_size) == 0)
			return strnext();
		break;
	}
	if (mp->t_div.m_core) {
		sp->x4.s_disk = 0;
		sp->x4.s_sp = mp->t_div.m_core;
	} else {
		sp->x4.s_disk = 1;
		sp->x4.s_seek = mp->t_div.m_seek/DBFSIZE * DBFSIZE;
		sp->x4.s_sp = &(sp->x4.s_bufp[mp->t_div.m_seek%DBFSIZE]);
		nread((long)sp->x4.s_seek,(char *)sp->x4.s_bufp);
		sp->x4.s_seek += DBFSIZE;
	}
	return 1;
}

/*
 * Get a single character assuming the stack has been set up
 * for chained I/O.
 */
geth()
{
	if (strp->x4.s_disk  &&  strp->x4.s_sp >= strp->x4.s_bufend) {
		nread((long)strp->x4.s_seek,(char *)strp->x4.s_bufp);
		strp->x4.s_seek += DBFSIZE;
		strp->x4.s_sp = strp->x4.s_bufp;
	}
	return (*strp->x4.s_sp++);
}

/*
 * Push down input stack and add the given file on top.
 */
adsfile(file)
char *file;
{
	register STR *sp;
	register FILE *fp;

	if ((fp = fopen(file, "r")) == NULL) {
		printe("cannot open file \"%s\"", file);
		return 0;
	}
	sp = allstr(SFILE);
	sp->x2.s_fname = duplstr(file);
	sp->x2.s_fp = fp;
	return 1;
}

/*
 * Push down input stack and add the given FILE.
 */
adsunit(fp) FILE *fp;
{
	register STR *sp;

	sp = allstr(SFILE);
	sp->x2.s_fname = duplstr("<stdin>");
	sp->x2.s_fp = fp;
}

/*
 * Push down input stack and add the given register.
 */
adstreg(rp)
register REG *rp;
{
	register STR *sp;

	if (rp->t_reg.r_macd.t_div.m_type == MREQS)
		return 1;	/* return had been random, 1 is guess by cef */
	sp = allstr(0);
	sp->x4.s_bufp = nalloc(DBFSIZE);
	sp->x4.s_bufend = &sp->x4.s_bufp[DBFSIZE];
	sp->x4.s_macp = &rp->t_reg.r_macd;
	if (strnext() == 0) {
		strp = strp->x4.s_next;
		nfree(sp->x4.s_bufp);
		nfree((char *)sp);
		return 0;
	}
	return 1;
}

/*
 * Given the name of a special number register, expand it and
 * add it onto the input stack.
 */
spcnreg(name)
char name[2];
{
	register STR *sp;
	register int n;
	register char *cp;

	n = 0;
	if (name[0] == '.') {
		switch (name[1]) {
		case '$':
			if ((n=strp->x1.s_argc) > 0)
				--n;
			break;
		case 'A':	n = A_reg;			break;
		case 'F':			/* Filename, not in V7. */
			for (sp=strp; sp; sp=sp->x1.s_next) {
				if (sp->x1.s_type == SFILE) {
					adscore(sp->x2.s_fname);
					return;
				}
			}
			return;
		case 'H':	n = unit(SMHRES, SDHRES);	break;
		/* case 'L': TO_DO */
		/* case 'P': TO_DO */
		case 'T':	n = T_reg;			break;
		case 'V':	n = unit(SMVRES, SDVRES);	break;
		case 'a':	n = a_reg;			break;
		case 'c':
			n = 0;
			for (sp=strp; sp; sp=sp->x1.s_next) {
				if (sp->x1.s_type == SFILE) {
					n = sp->x1.s_clnc;
					break;
				}
			}
			break;
		case 'd':	n = cdivp->d_rpos;		break;
		case 'f':	n = 0;				break;
		case 'h':	n = cdivp->d_maxh;		break;
		case 'i':	n = ind;			break;
		/* case 'j': TO_DO */
		/* case 'k': TO_DO */
		case 'l':	n = lln;			break;
		case 'n':	n = n_reg;			break;
		case 'o':	n = pof;			break;
		case 'p':	n = pgl;			break;
		case 's':	n = unit(psz*SDPOIN, SMPOIN);	break;
		case 't':
			n = cdivp->d_ctpp ? cdivp->d_ctpp->t_apos : pgl;
			if (n > pgl)
				n = pgl;
			n -= cdivp->d_rpos;
			break;
		case 'u':	n = fill;			break;
		case 'v':	n = vls;			break;
		case 'w':	n = 0;				break;
		case 'x':	n = 0;				break;
		case 'y':	n = 0;				break;
		case 'z':
			cp = nalloc(3);
			cp[0] = cdivp->d_name[0];
			cp[1] = cdivp->d_name[1];
			cp[2] = '\0';
			adscore(cp);
			strp->x3.s_srel = cp;
			return;
		}
	}
	adsnval(n, '1');
}

/*
 * Given a value and a format, expand out the value to the
 * particular format and add it onto the top of the stack.
 */
adsnval(val, f) int val, f;
{
	register ROM *op;
	int w;
	register int n;
	char charbuf[CBFSIZE], *str, *cp2;
	register char *cp1;

	n = val;
	if (n <= 0) {
		if ((n = -n) <= 0) {
			n = 0;
			if (!isdigit(f))
				f = '0';
		}
	}
	cp1 = charbuf;
	switch (f) {
	case 'a':
	case 'A':
		while (n) {
			n--;
			*cp1++ = 'a' + n%26;
			n /= 26;
		}
		break;
	case 'i':
	case 'I':
		for (cp2="ivxlcdm"; *cp2!='m'; cp2+=2) {
			op = &romtab[n%10];
			n /= 10;
			while (op != &romtab[0]) {
				*cp1++ = cp2[op->o_digit];
				op = &romtab[op->o_state];
			}
		}
		if (cp1+n >= &charbuf[CBFSIZE]) {
			*cp1++ = '*';
			break;
		}
		while (n--)
			*cp1++ = 'm';
		break;
	default:
		w = f - '0';
		while (n) {
			--w;
			*cp1++ = n%10 + '0';
			n /= 10;
		}
		while (w-- > 0)
			*cp1++ = '0';
		if (val < 0)
			*cp1++ = '-';
	}
	cp2 = str = nalloc(cp1-charbuf + 1);
	if (isupper(f)) {
		while (cp1 > charbuf)
			if (islower(*--cp1))
				*cp2++ = toupper(*cp1);
			else    *cp2++ = *cp1;
	} else {
		while (cp1 > charbuf)
			*cp2++ = *--cp1;
	}
	*cp2 = '\0';
	adscore(str);
	strp->x3.s_srel = str;
}

/*
 * Divert input to the given string.
 */
adscore(cp) char *cp;
{
	register STR *sp;

	sp = allstr(SCORE);
	sp->x3.s_cp = cp;
	sp->x3.s_srel = NULL;
}

/*
 * Allocate an entry and add it to the input stack.
 */
STR *
allstr(type)
{
	register STR *sp;
	register int i;

	sp = (STR *) nalloc(sizeof *sp);
	sp->x1.s_next = strp;
	strp = sp;
	sp->x1.s_type = type;
	sp->x1.s_eoff = 0;
	sp->x1.s_clnc = 1;
	sp->x1.s_nlnc = 1;
	sp->x1.s_argc = 0;
	for (i = 0; i < ARGSIZE; i++)
		sp->x1.s_argp[i] = "";
	sp->x1.s_abuf = NULL;
	return sp;
}

hexdigit(c) register int c;
{
	if (isdigit(c))
		return c - '0';
	else if (c >= 'A' && c <= 'F')
		return c - 'A' + 10;
	else if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	printe("illegal hex digit");
	return 0;
}

		
/* end of getf.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.