DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T f

⟦bd51e5bec⟧ TextFile

    Length: 24933 (0x6165)
    Types: TextFile
    Names: »fix1«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/ditroff-fix/fix1« 

TextFile


The files and context-diffs that follow can be applied to ditroff (dwb 1) to:
o	add lots of error checking

o	make the existing error messages more helpful

o	allow user-defined fonts

o	add some new facilities

o	add ``enhancements'' for compatibility with UCB-Gremlin

o	fix a lot of bugs

All of the changes are conditionally compiled with "ifdef" -- each set of
changes has its own name.

The resulting version of troff is *much* easier to use.

Caveat: the existing -ms and -mm macro packages are full of syntax errors and
bugs. These will start to produce warning messages.  If you fix them, the
output will be better.  Perhaps you'd like to post your fixes!

I shall be leaving Warwick to move to a new job at the end of this week, so
although mail will be forwarded (& I'd be glad of any changes that you needed
to make), I may take a while to respond!

Russell

This part contains the extra fles, local.c and local.h

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	local.c
#	local.h
# This archive created: Thu Nov 20 21:32:30 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'local.c'" '(20607 characters)'
if test -f 'local.c'
then
	echo shar: will not over-write existing file "'local.c'"
else
sed 's/^X//' << \SHAR_EOF > 'local.c'
X/* local.c --- local features that didn't fit in elsewhere:
X *	error reporting:  warnings about local features
X *			  warnings about syntax errors &c
X *			  fatal error reporting
X */
X#include <ctype.h>
X/* give users warnings if they use non-standard features... */
X#ifndef MAXPTR
X#include "tdef.h"
X#endif MAXPTR
X#include "ext.h"
X#ifndef WARN_ONCE
X#include "local.h"
X#endif WARN_ONCE
X#if defined(WARNLEVELREG) || defined(REPORTERRS)
X/* v.h defines a single structure, to which the extern applies.
X * It contains the names of the general-purpose number-registers.
X * We use it for giving the line-number in error messages, and for
X * a "warning level" register to enable turning off warnings about local
X * features from within troff.
X * - req
X */
Xextern
X#include "v.h"
X#endif 
X#ifdef LOCALWARN
X
X/*ARGSUSED1*/
Xlwarn(s, a1, a2, a3, a4, a5)
X	char *s;
X{
X#ifdef WARNLEVELREG
X	if (v.wl) {
X		if (v.wl & WARN_ONCE) {
X			errmsg(EWARN, "Input uses local features; use troff -W for explicit information\n");
X			v.wl &= (~WARN_ONCE);
X		} else {
X			errmsg(0, 0, s, a1, a2, a3, a4, a5);
X		}
X	}
X#else !WARNLEVELREG
X	if (warninglevel) {
X		/* stderr comes from tdef.h; different to stdio.h's stderr! */
X		if (warninglevel & WARN_ONCE) {
X			errmsg(EWARN, "Input uses local features; use troff -W for explicit information\n");
X			warninglevel &= (~WARN_ONCE);
X		} else {
X			errmsg(0, 0, s, a1, a2, a3, a4, a5);
X		}
X	}
X#endif WARNLEVELREG
X}
X#endif LOCALWARN
X#ifdef REPORTERRS
X
Xchar *progname;   /* set in n1.c:main() */
Xchar *ifilename;  /* set by n1.c:nextfile() */
Xint reporterrs = LERR_EVERYTHING;
X
X/*ARGSUSED2*/
Xvoid
Xerrmsg(func, arg, mesg, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
X	int (*func)();	/* called after printing the message, if != 0 */
X	int arg;	/* passed to func */
X	char *mesg;	/* passed to printf */
X{
X
X	/* don't give warnings if they're turned off
X	 * note that this turns off warnings about local features too, but
X	 * you can set warninglevel to achieve that more cleanly!
X	 *
X	 */
X	if (func == 0 && !(reporterrs & LERR_WARNINGS)) {
X		return;
X	}
X	/* NOTE:  troff has its own version of printf and stdio! */
X	if (!progname || !*progname) {
X		progname = "ditroff";
X	}
X	/* progname might contain %.  Remember that we're not using stdio */
X	fprintf(stderr, "%s:", progname);
X	if (ifilename && *ifilename) {
X		fprintf(stderr, "%s:", ifilename);
X		/* ifilename set by n1.c:nextfile();  it might not always
X		 * change when .so happens, though!
X		 */
X	}
X	fprintf(stderr, "%d:%s", v.cd, (func == 0) ? "warning: " : " ");
X
X	if (mesg && *mesg) {
X		fprintf(stderr, mesg, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
X		if (mesg[strlen(mesg)-1] != '\n') { /* no trailing newline */
X			/* terminal modes may be odd, so add \r as well */
X			fprintf(stderr, "\r\n");
X		}
X	} else {
X		fprintf(stderr, "Internal error: mesg NULL (__FILE__:__LINE__)\n");
X	}
X
X	if (func != 0) {
X		(* func)(arg);
X	} /* else simply return */
X}
X
Xchar *
Xrealname(a)
X	tchar a;
X{
X	/* return string representation of the name.  Trivial now, but might
X	 * become complex if long names are allowed
X	 * -- req
X	 */
X
X
X#ifdef TCHARTOS
X	char one, two;
X	char *rp;
X#endif TCHARTOS
X	static char buffer[30];		/* max is 2 char name + 1 for \0 */
X					/* but also use "[none]" etc */
X	buffer[0] = buffer[1] = buffer[2] = '\0';
X	buffer[0] = (char) a;
X	buffer[1] = a >> BYTE;
X
X	if (!buffer[0] && !buffer[1]) {
X		return(strcpy(buffer, "[null name]"));
X	} 
X	if (!buffer[0]) {
X		/* don't know if this can happen */
X		buffer[0] = ' ';
X	}
X#ifdef TCHARTOS
X	one = buffer[0];
X	two = buffer[1];
X	if (buffer[0]) strcpy(buffer, tchartos((tchar) one));
X	if (buffer[1]) strcat(buffer, tchartos((tchar) two));
X#endif TCHARTOS
X	return buffer;
X}
X
X#endif REPORTERRS
X#ifdef ROTATEPAGE
X
Xint pageangle = 0;
X
Xvoid
Xcaserp()	/* .rp n --- rotate page by n degrees */
X		/* .rp n x y -- put topleft at x, y */
X{
X	static int oldangle = 0;
X	int newx = 0, newy = 0;
X
X#ifdef LOCALWARN
X	lwarn(".rp (rotate page) is non-standard");
X#endif LOCALWARN
X	if (skip()) {
X		pageangle = oldangle;
X	} else {
X		oldangle = pageangle;
X		/* does inumb() allow +n to be an increment? */
X		pageangle = inumb(&pageangle);
X	}
X	/* don't give % a non-portable -ve argument, in case it
X	 * ever matters... */
X	while (pageangle < 0) {
X		pageangle += 360;
X	}
X	if (pageangle >= 360) {
X		pageangle %= 360;
X	}
X	/* swap over width & length of page to apply to the nearest
X	 * diagonal
X	 */
X	if ((pageangle < 45 && pageangle > 0) ||
X	    (pageangle >= 315) || (pageangle >= 135 && pageangle < 225)) {
X		extern int paperwidth, paperlength;	/* in t10.c */
X
X		int tmpwid = paperwidth;
X
X		paperwidth = paperlength;
X		paperlength = tmpwid;
X	}
X	/* now look for newx, newy */
X	if (!skip()) {
X		newx = inumb(&newx);
X		if (skip()) {
X#ifdef REPORTERRS
X			errmsg(EWARN, ".rp: new y pos missing (assuming 0)");
X#endif REPORTERRS
X			newy = 0;
X		} else {
X			newy= inumb(&newy);
X			if (!skip()) {
X#ifdef REPORTERRS
X				errmsg(EWARN,
X				".rp: extra characters ignored from \"%s\"",
X							tchartos(ch));
X#endif REPORTERRS
X			}
X		}
X	}
X	ptpangle(newx, newy);
X}
X
X
X/* ptpangle() is called from newpage() in t10.c.  Probably won't be able to
X * affect the 1st page, as newpage() gets called at the end of each page.
X * But it should also be called on the 1st-page transition, to start page 1.
X * I haven't checked to see if it is.
X * - req
X */
Xptpangle(newx, newy)
X	int newx, newy;
X{
X	static int angle = 0;
X	/* only print out command if the angle
X	 * 	is non-zero (on every page, to help pre-processors)
X	 *	has changed (obvious!)  Note that it might change to 0.
X	 */
X	if (pageangle || pageangle != angle) {
X		fprintf(ptid, "x A %d %d %d\n", pageangle, newx, newy);
X		angle = pageangle;
X	}
X}
X#endif ROTATEPAGE
X#ifdef USEFONTPATH
Xchar *
Xfollowpath(path, file, mode)
X	char *path, *file;
X	int mode;
X{
X	/* "path" is interpreted as a colon-separated list of names.
X	 * We look in each of them for "file".
X	 */
X	int fd;	/* try to open the file at each stage */
X
X	register char *p = path;
X	static char result[NS];
X
X	while (*p) {
X		register char *q = result;
X
X		*q = '\0';
X		while (*p && *p != ':') {
X			if (q - result >= NS - (strlen(file) + 2)) {
X				/* 2 is for '/' and trailing '\0' */
X#ifdef REPORTERRS
X				errmsg(EWARN, "Name in path too long (%s)",
X							result);
X#else !REPORTERRS
X				fprintf(stderr, "troff: \"%s\" too long in \"%s\"",
X					result, path);
X#endif REPORTERRS
X				return 0;
X			}
X			*q++ = *p++;
X			*q = '\0';
X		}
X		if (*p && *p == ':')
X			p++;	/* step over the colon */
X		if (q == result)
X			*q++ = '.';
X		*q++ = '/';
X		*q = '\0';
X
X		if ((fd = open(strcat(result, file), mode)) >= 0) {
X			/* opened it OK... */
X			close(fd);
X			return(result);
X		}
X		q = result;
X	}
X	/* failed... */
X	return (char *) 0;	/* not using stdio -- NULL not defined */
X}
X#endif USEFONTPATH
X#ifdef FONTFAMILIES
X
X/* caseff -- fontfamily */
Xcaseff()
X{
X	char familyname[NS];
X	int j, k;
X	tchar i;
X
X	if (skip()) {
X#ifdef REPORTERRS
X		errmsg(EWARN, "call to .ff with no arguments ignored");
X		return;
X#endif REPORTERRS
X	}
X#ifdef LOCALWARN
X	lwarn("Font Family (.ff) is non-portable");
X#endif LOCALWARN
X	lgf++; /* don't want ligatures in our nice FamilyName */
X	for (k = 0; k < (NS - 1); k++) {
X		if (((j = cbits(i = getch())) <= ' ') || (j > 0176))
X			break;
X		familyname[k] = j;
X	}
X	familyname[k] = 0;
X	ch = i;
X	lgf--;
X	if (familyname[0]) {
X		setfamily(familyname);
X	}
X#ifdef REPORTERRS
X	else {
X		errmsg(EWARN, ".ff: illegal font family name");
X	}
X#endif REPORTERRS
X}
X
X/* setfamily(name) -- use font family ``name'' */
X/* We do this by reading a ``FamilyFile'' looking for a line that starts
X * with name followed by a colon.  After the colon there is a comma-
X * separated list of font names to load in successive positions.
X * Later I'd like to add more (that's why the commas are there):
X * S=n would set the slant to n on that font position, H the height,
X * b the bd factor, s the size, ....
X * But that's for later.  Maybe *after* breakfast.
X * - req 20/10/1986
X */
X
X#ifndef FAMILYFILENAME
X#define FAMILYFILENAME "FamilyFile"
X#endif !FAMILYFILENAME
Xint
Xsetfamily(name)
X	char *name;
X{
X	char tmp[NS]; /* to store filename if !fontpath */
X	char *FamilyFileName = tmp;
X	int famfile;
X	char linebuf[NS];
X	int linenumber = 0; /* for error reporting. */
X	int pos = 0;
X	int c;  /* current input char from family file */
X	int inextf; /* index into nextf[] */
X
X	extern char *fontpath;
X
X#ifdef USEFONTPATH
X	if (fontpath && *fontpath) {
X	       if (!(FamilyFileName = followpath(fontpath, FAMILYFILENAME, 0))){
X#ifdef REPORTERRS
X			errmsg(EWARN, "Can't find \"%s\" in path \"%s\" to load font family %s from",
X					FAMILYFILENAME, fontpath, name);
X#else !REPORTERRS
X			fprintf(stderr
X				"troff: can't find \"%s\" for font family %s\n",
X					FAMILYFILENAME, name);
X#endif REPORTERRS
X			return 0;
X		}
X	} else
X#endif USEFONTPATH
X		sprintf(tmp, "/usr/lib/troff/descs/dev%s/%s",
X						devname, FAMILYFILENAME);
X	if (FamilyFileName != tmp) {
X		(void) strcpy(tmp, FamilyFileName);
X		FamilyFileName = tmp;
X	}
X	if ((famfile = open(FamilyFileName, 0)) < 0) {
X#ifdef REPORTERRS
X		errmsg(EWARN,
X		"Can't open font family file \"%s\" looking for family \"%s\"",
X							FamilyFileName, name);
X#else !REPORTERRS
X		fprintf(stderr, "troff: can't open %s to load family %s\n"",
X							FamilyFileName, name);
X#endif REPORTERRS
X		return 0;
X	}
X	/* now have an open file `famfile' */
X
X	/* look for a line starting with name: */
X	
X	while ((c = getfamchar(famfile)) >= 0) {
X		linenumber++;
X		/* at start of line... */
X
X		/* skip initial whitespace */
X		while (c > 0 && (c == ' ' || c == '\t') )
X			c = getfamchar(famfile);
X
X		if (c == *name) {
X			char *p = name + 1;
X
X			while ((c = getfamchar(famfile)) > 0) {
X				if (!*p || *p++ != c) { 
X					break;
X				}
X			}
X			if (!*p && c == ':') { /* found */
X				break;
X			} else {
X				/* mismatch... */
X				c = '#';
X			}
X		} else {
X			c = '#'; /* ignore rest of line! */
X		}
X		if (c != '#' ) {
X#ifdef REPORTERRS
X			errmsg(EWARN,
X"Error reading font family file \"%s\" line %d: \'%c\' unexpected",
X						FamilyFileName, linenumber, c);
X#else !REPORTERRS
X			fprintf(stderr, "troff: %s: %d: format error\n",
X						FamilyFileName, linenumber);
X#endif REPORTERRS
X			close (famfile);
X			return 0;
X
X		} else { /* comment or a mismatch */
X			while ((c = getfamchar(famfile)) > 0 && c != '\n')
X				;
X			if (c < 0) { /* EOF */
X				break;
X			}
X		}
X	}
X	/* now either c < 0 (EOF or error) or c is 1 char past a match */
X	if (c < 0) {
X#ifdef REPORTERRS
X		errmsg(EWARN, "Font Family %s not found in \"%s\"",
X						name, FamilyFileName);
X#else !REPORTERRS
X		fprintf(stderr, "troff: Font Family %s not found in \"%s\"",
X						name, FamilyFileName);
X#endif REPORTERRS
X		return 0;
X	}
X	/* now we have a match.  Want to look for font names */
X	while ((c = getfamchar(famfile)) > 0 && c != '\n') {
X		char shortname[3];
X		char *p = shortname;
X
X		/* skip whitespace */
X		while (c > 0 && (c == ' ' || c == '\t') )
X			c = getfamchar(famfile);
X
X		if (c == '\n' || c < 0)
X			break;
X
X		/* now the font name */
X		/* c already contains the 1st char of this. */
X		shortname[0] = c;
X		if ((c = getfamchar(famfile)) > 0 && c != '\n') {
X			shortname[1] = c;
X		} else break;
X		if (c == ' ' || c == '\t')
X			shortname[1] = '\0';
X		else
X			shortname[2] = '\0';
X		
X		/* got a font name */
X		/* try to mount it on the next position */
X		if (setfp(++pos, PAIR(shortname[0], shortname[1]), 0) < 0) {
X			break; /* setfp does an error message */
X		}
X
X		/* skip whitespace */
X		while ((c = getfamchar(famfile)) > 0 && (c == ' '||c == '\t'))
X			;
X
X		if (c == ':') {
X			/* do a .so on the filename */
X			/* I'd like to push back ".so file" here!
X			* - req
X			*/
X			inextf = nextf[0] = 0;
X
X			/* skip whitespace */
X			while (((c = getfamchar(famfile)) > 0) &&
X						(c == ' ' || c == '\t')) {
X					continue;
X			}
X			while (c > 0 && c != ' ' && c != '\n' && c != '\t') {
X				nextf[inextf++] = c;
X				nextf[inextf] = 0;
X				c = getfamchar(famfile);
X			}
X
X			if (!inextf || !nextf[0]) {
X#ifdef REPORTERRS
X				errmsg(EWARN, "%s: %d: colon (:) unexpected",
X						FamilyFileName, linenumber);
X#else !REPORTERRS
X				fprintf(stderr,
X					"troff: %s: %d: colon (:) unexpected",
X						FamilyFileName, linenumber);
X#endif REPORTERRS
X				break;
X			}
X			/* skip trailing whitespace */
X
X			while (c > 0 && (c == ' '||c == '\t')) {
X				(c = getfamchar(famfile));
X			}
X#ifdef REPORTERRS
X			if (c != '\n' && c != '#') {
X				if (c < 0) {
X					errmsg(EWARN, "Unexpected EOF at line %d of font family file \"%s\"", linenumber, FamilyFileName);
X				} else {
X					errmsg(EWARN, "%s: %d: \"%c\" unexpected after filename \"%s\"", FamilyFileName, linenumber, c, nextf);
X					c = '#';
X				}
X			}
X#endif REPORTERRS
X			/* do this after checking the rest of the line so that
X			 * we can use nextf in the error message!
X			 */
X			dosofile("Loading Font Family");
X		}
X		if (c == '\n' || c == '#' || c < 0) {
X			break;
X		} else if (c == ',') {
X			continue;
X		} else {
X#ifdef REPORTERRS
X			errmsg(EWARN, "%s: %d: Expected a comma, found \"%c\"",
X					FamilyFileName, linenumber, c);
X#else !REPORTERRS
X			fprintf(stderr,
X				"troff: %s: %d: Expected a comma, found \"%c\"",
X					FamilyFileName, linenumber, c);
X#endif REPORTERRS
X			break;
X		}
X	}
X	close (famfile);
X#ifdef REPORTERRS
X	/* pos == 0 implies no fonts loaded
X	 * inextf == 0 implies no file loaded (via dosofile())
X	 */
X	if (pos == 0 && inextf == 0) {
X		errmsg(EWARN, ".ff: no action in \"%s\" for family \"%s\"",
X					FamilyFileName, name);
X	}
X#endif REPORTERRS
X	return pos;
X}
X
X/* this is the bulk of caseso().  I've factored it out to use the same code
X * in both places */
Xint
Xdosofile(wherefrom)
X	char *wherefrom;
X{
X	register i;
X	register char	*p, *q;
X	/* offl, ioff and ipl are from n1.c */
X	extern long offl[];
X	extern long ioff;
X	extern filep ipl[];
X
X	if ((i = open(nextf, 0)) < 0 || (ifi >= NSO)) {
X#ifdef REPORTERRS
X		errmsg(done, 02, "%s: can't open file %s", wherefrom, nextf);
X#else !REPORTERRS
X		fprintf(stderr, "troff: can't open file %s\n", nextf);
X		done(02);
X#endif REPORTERRS
X	}
X	flushi();
X	ifl[ifi] = ifile;
X	ifile = i;
X	offl[ifi] = ioff;
X	ioff = 0;
X	ipl[ifi] = ip;
X	ip = 0;
X	nx++;
X	nflush++;
X	if (!ifl[ifi++]) {
X		p = ibuf;
X		q = xbuf;
X		xbufp = ibufp;
X		xeibuf = eibuf;
X		while (p < eibuf)
X			*q++ = *p++;
X	}
X}
X
X
Xint
Xgetfamchar(f)
X	int f;
X{
X	/* return the next char from file 'fam' */
X	/* wouldn't it have been nice to have used stdio ? */
X	/* should probably do this like .cf I suppose.  */
X
X	static char smallbuf[NS];
X	static char *p = 0;
X
X	if (!p || !*p) {
X		if ((read(f, smallbuf, NS - 1)) <= 0) {
X			/* EINTR problem here? */
X			return -1;
X		}
X		p = smallbuf;
X	}
X	return (*p++) & 0377; /* discourage sign extension */
X}
X
X#endif FONTFAMILIES
X#ifdef ANYBASESTR
X/* would like to define setbasestr() here.
X * the idea is to allow text to be set on an arbitrary baseline.
X * \R'l 1i 1i'hello there'
X * should draw "hello there" at a 45 degree angle.
X * Perhaps it'd be easier to stick to straight lines for now, as then at
X * least we can work out the width of the things!  Perhaps it should be
X * \R'n'str'.
X * But I don't know how to do that, either!
X * - req
X */
X#endif ANYBASESTR
X#ifdef BLANKSMACRO
X
Xstatic int bmmacro = PAIR('s', 'p');
X
Xcasebm()
X{
X	lgf++;
X	skip();
X	if (!(bmmacro = getrq())) {
X		bmmacro = PAIR('s', 'p');
X	}
X}
X
Xint nrspaces, nrlines;
X/* this is really for the .B and .N registers in n4.c: the idea is that one
X * could write a paragraph macro that was invoked by magic on blank lines:
X * .de P
X * .sp \\n(.NV \" .N is the number of blank lines
X * .if \\n(.B>0  .ti +\\n(PDu \" .B is indent...
X * ..
X * with other processing as well.  This is a big improvement, as it means
X * that blank lines in the output can be handled consistently.  It also
X * allows for things like poetry, and for style parameters (e.g. the para
X * indent in "P" above is fixed irrespective of the number of spaces on
X * the input line.
X * 
X * bug: callblank is called for each blank line instead of once at the end
X *
X * req
X */
Xcallblank(spaces, lines)
X	int spaces;	/* number of leading spaces */
X	int lines;	/* after this many blank lines */
X{
X	/* 2nd arg to control() is a 1 if we have to collect args for a
X	 * user-defined macro. */
X	nrspaces = spaces;
X	nrlines = lines;
X	if (bmmacro == PAIR('s', 'p')) {
X		callsp();
X	} else {
X		control(bmmacro, 0);
X	}
X}
X#endif BLANKSMACRO
X#ifdef TCHARTOS
X
X/* routine to provide a string representation of a tchar
X * BUG:
X * uses a static buffer to hold the result.
X * req 1986
X */
X
Xchar *
Xtchar0(i)
X	tchar i;
X{
X	static char resultbuf0[5]; /* plenty! */
X	static char resultbuf1[5]; /* plenty! */
X	static char which = '0';
X
X	int ch = cbits(i);
X	register char *resultbuf = (which == '0') ? resultbuf0 : resultbuf1;
X
X	which ^= '0';
X	*resultbuf = '\0';	/* paranoia */
X
X	switch (ch) {
X	case 002: case 003: case 005: case 006: case 007: /* unfair? */
X	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
X	case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
X	case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
X	case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
X	case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
X	case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
X	case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
X	case 'X': case 'Y': case 'Z': case '0': case '1': case '2': case '3':
X	case '4': case '5': case '6': case '7': case '8': case '9': case '!':
X	case '@': case '#': case '$': case '%': case '^': case '&': case '*':
X	case '(': case ')': case '_': case '-': case '=': case '+': case '`':
X	case '~': case ']': case '[': case '{': case '}': case '\'': case '"':
X	case ';': case ':': case '<': case '>': case ',': case '.': case '/':
X	case '?': case '|': case '\\': case ' ':
X	case '\n': case '\r': case '\f':
X		resultbuf[0] = ch;
X		resultbuf[1] = '\0';
X		return resultbuf;
X
X	default:
X		ch &= 0177;	/* bottom 7 bits (must be ASCII) */
X		if (iscntrl(ch)) {
X			resultbuf[0] = '^';
X			resultbuf[1] = (ch + '@') & 0177;  /* ASCII ONLY */
X			resultbuf[2] = '\0';
X			return resultbuf;
X		}
X		/* ``shouldn't happen'' */
X		errmsg(EWARN, "Unknown char 0%o in tchar0()", ch);
X		return "[UNKNOWN]";
X	}
X}
X
Xchar *
Xtchartos(c)
X	tchar c;
X{
X	/* use 2 buffers and alternate, so 2 calls to tchartos() OK.  Ugh. */
X	static char resultbuf0[20]; /* biggest case currently \h'32767' */
X	static char resultbuf1[20];
X	static char which = '0';
X
X	register char *p = (which == '0') ? resultbuf0 : resultbuf1;
X	char *resultbuf = p;
X
X	which ^= '0';
X	*p = '\0';
X
X	if (ismot(c)) {
X		if (isvmot(c)) {
X			(void) strcpy(p, tchar0(eschar));
X			while (*p) {
X				p++;
X			}
X			sprintf(p, "v'%d'", sbits(c));
X			return resultbuf;
X		} else {
X			return "[\\h or TAB]";
X		}
X	}
X
X	if (iszbit(c)) {
X		(void) strcat(p, tchar0(eschar));
X		*p++ = 'z';
X		*p = '\0';
X	}
X
X	if (cbits(c) > 128) { /* special char */
X		(void) strcpy(p, tchar0(eschar));
X		strcat(p, "(");
X		while (*p) {
X			p++;
X		}
X		(void) strcpy(p, &chname[chtab[cbits(c) - 128]]);
X		return resultbuf;
X	}
X
X	switch(cbits(c)) {
X
X	case 0:		return "[NUL]";
X	case IMP:	return "[IMP]";
X	case TAB:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "t");
X	case RPT:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "l");
X	case CHARHT:	(void) strcat(p, tchar0(eschar));
X			while (*p)
X				p++;
X			sprintf(p, "H'%d'", sbits(c));
X			return resultbuf;
X	case SLANT:	(void) strcat(p, tchar0(eschar));
X			while (*p)
X				p++;
X			sprintf(p, "S'%d'", sbits(c) - 180);
X			return resultbuf;
X	case 027:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, " ");	/* em-space */
X	case FONTPOS:	(void) strcat(p, tchar0(eschar));
X			while (*p)
X				p++;
X			*p++ = 'f';
X			if (((c>>16) >> BYTE) && ((c>>16) >> BYTE))
X				*p++ = '(';
X			*p++ =  (c>>16) & BMASK;
X			*p++ =  ((c>>16) >> BYTE);
X			*p++ =  '\0';
X			return resultbuf;
X	case DRAWFCN:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "D");
X	case LEFT:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "{");
X	case RIGHT:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "}");
X	case FILLER:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "&");
X	case OHC:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "%"); /*WRONG*/
X	case CONT:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "c");
X	case PRESC:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "e");
X	case XPAR:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, "!");
X	case FLSS:	return "[FLSS]";
X	case WORDSP:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, " ");
X	case ESC:	(void) strcat(p, tchar0(eschar));
X			return strcat(p, tchar0(eschar));
X
X	default:	return strcpy(p, tchar0(c));
X	}
X
X	/*NOTREACHED*/
X}
X#endif TCHARTOS
SHAR_EOF
if test 20607 -ne "`wc -c < 'local.c'`"
then
	echo shar: error transmitting "'local.c'" '(should have been 20607 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'local.h'" '(1174 characters)'
if test -f 'local.h'
then
	echo shar: will not over-write existing file "'local.h'"
else
sed 's/^X//' << \SHAR_EOF > 'local.h'
X#ifdef LOCALWARN
X/* lwarn.c::lwarn() printfs it's arguments as a warning message if
X * warninglevel & WARN_ALL; if warninglevel & WARN_ONCE, it just prints a
X * message the 1st time.
X *
X * REQ Sept. 86
X */
X#define WARN_ONCE	1
X#define WARN_ALL	2
X#endif LOCALWARN
X#ifdef REPORTERRS
X/* note that EWARN provides 2 arguments to a call to error.  One day the way
X * troff exits should be rationalised, and then EWARN would be a single value!
X */
X#define EWARN 0,0
Xextern int errno;
Xchar *realname();
Xvoid errmsg();
X
X/* flags for diffent kingds of error */
X/* these are OR'd together -- mostly used in atoi() */
X#define LERR_BADEXPSTART 01	/* no expression found */
X#define LERR_PSNUMWARN   02	/* "\s94 is a 9 point 4" msg enabled */
X#define LERR_WARNINGS	 04	/* no warnings at all if false */
X
X/* LERR_EVERYTHING is the result of ORing all of the LERR values together;
X * it's used to initialise the repoerterrs variable in local.h
X */
X#define LERR_EVERYTHING (LERR_BADEXPSTART|LERR_WARNINGS)
X/* let's not use LERR_PSNUMWARN for now! */
X#endif REPORTERRS
X#if defined(REPORTERRS) || defined(TCHARTOS)
Xextern char *strcpy();
Xextern char *strcat();
X#endif /* REPORTERRS||TCHARTOS */
SHAR_EOF
if test 1174 -ne "`wc -c < 'local.h'`"
then
	echo shar: error transmitting "'local.h'" '(should have been 1174 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
ARPA		req%uu.warwick.ac.uk@ucl-cs.arpa
EARN/BITNET	req%UK.AC.WARWICK.UU@AC.UK
JANET		req@uk.ac.warwick.uu
UUCP		seismo!mcvax!ukc!warwick!req  (req@warwick.UUCP)
PHONE		+44 203 523485
For mail.frplist, use "frplist" instead of "req" in the above lines.
The BITNET path only works from sites that have AC.UK in their tables.  Sorry.