DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

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

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦1eb1d5d58⟧ TextFile

    Length: 9487 (0x250f)
    Types: TextFile
    Notes: UNIX file
    Names: »deroff.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX Filesystem
        └─ ⟦this⟧ »cmd/deroff.c« 

TextFile

/*
 * Strip nroff/troff control lines and eqn
 * and tbl sequences from input.
 * Also, optionally, produce the
 * output as a set of words.
 */

#include <stdio.h>
#include <ctype.h>

#define	NLINE	500		/* Input line length */
#define	NFNEST	15		/* Depth of .so file nesting */

FILE	*ofiles[NFNEST];
FILE	**ofpp = &ofiles[0];
char	**flist;		/* list of files to open */

typedef	struct	FNAME {
	struct	FNAME	*fn_next;
	char	fn_name[];
}	FNAME;

FNAME	*fnames;

char	line[NLINE];

int	delim1 = EOF;		/* Start embedded eqn */
int	delim2 = EOF;		/* End embedd eqn */
int	skipcnt;		/* Number of lines to skip */
int	skiptitle;		/* Skip title text (until next nroff command) */
int	sflag;			/* Divide into sentences */
int	wflag;			/* Divide output into words */
int	xflag;			/* Extra knowledge of macros (style/diction) */

int	ineqn;			/* Inside embedded eqn escape */
FILE	*dopen();
char	*dgets();

main(argc, argv)
int argc;
char *argv[];
{
	register char *ap;

	while (argc>1 && *argv[1]=='-') {
		for (ap = &argv[1][1]; *ap!='\0'; ap++)
			switch (*ap) {
			case 's':
				sflag = 1;
				break;

			case 'w':
				wflag = 1;
				break;

			case 'x':
				xflag = 1;
				break;

			default:
				usage();
			}
		argv++;
		argc--;
	}
	if (argc < 2)
		ofiles[0] = stdin;
	flist = &argv[1];
	exit (deroff());
}

/*
 * Read until end-of-file
 * and process the special nroff/troff/eqn/tbl
 * lines in the file.
 */
deroff()
{

	while (dgets(line) != NULL) {
		if (!ineqn && line[0]=='.') {
			nroff(line);
			continue;
		}
		output(line);
	}
}

/*
 * O▶15◀utput for that line which isn't an nroff
 * control line.  This has to look for embedded
 * eqn stuff and back-slash troff/nroff escapes.
 * The embedded escapes are all handled to some degree
 * but such things as nested quotes (e.g. \w or \h)
 * do not quite work.  However, these occur almost never
 * in text so it should be sufficient.
 */
output(l)
register char *l;
{
	register int c;
	register int inword = 0;
	register int hyphen;

	if (skipcnt) {
		skipcnt--;
		return;
	}
	if (skiptitle)
		return;
	while ((c = *l++) != '\0') {
		if (ineqn) {
			if (c == delim2)
				ineqn = 0;
			continue;
		}
		if (c == delim1) {
			ineqn = 1;
			continue;
		}
		if (c == '\\') {
			if ((c = *l++) == '\0')
				break;
			switch (c) {
			case '0':		/* digit width space */
			case '|':		/* Narrow space */
			case '^':		/* Half narrow space */
			case '&':		/* Non-printing, 0-width char */
			case '!':		/* Transparent line indicator */
			case 'e':		/* Current escape */
			case '%':		/* Optional hyphenation char */
			case 't':		/* Non-interpreted tab */
			case 'u':		/* Up 1/2 */
			case 'd':		/* Down 1/2 */
			case 'a':		/* Non-interpeted leader */
			case 'c':		/* Interrupt text processing */
			case 'p':		/* Break and spread */
			case 'r':		/* Rerverse vertical motion */
			case '{':		/* Begin conditional */
			case '}':		/* End conditional */
				c = ' ';
				break;

			case '$':		/* argument */
				if (*l != '\0')
					l++;
				continue;

			case '(':		/* Char named `xx' */
				if (*l != '\0')
					l++;
				if (*l != '\0')
					l++;
				c = ' ';
				break;

			case 'z':		/* Zero-width character */
				if (*l != '\0')
					c = *l++;
				break;

			case 'k':		/* Mark input place in `x' */
			case 'n':		/* Expand reggister x */
			case '*':		/* Interpolate string */
			case 'f':		/* Change font */
				if (*l != '\0')
					if ((c = *l++) == '(') {
						if (*l != '\0')
							l++;
						if (*l != '\0')
							l++;
					}
				continue;

			case 's':		/* Change point size */
				if (*l == '\0')
					continue;
				if ((c = *l++)=='-' || c=='+') {
					if (*l == '\0')
						continue;
					c = *l++;
				}
				while (*l!='\0' && isdigit(c))
					c = *l++;
				break;

			case 'x':		/* Extra line space */
			case 'w':		/* Width function */
			case 'v':		/* Local vertical motion */
			case 'o':		/* Overstrike function */
			case 'L':		/* Vertical line */
			case 'l':		/* Horizontal line */
			case 'h':		/* Local horizontal motion */
			case 'b':		/* Bracket-builder */
				if ((c = *l) != '\0')
					while (*l!='\0' && *l!=c)
						l++;
				continue;

			case '"':		/* Beginning of comment */
				while (*l != '\0')
					l++;
				continue;
			}
		}
		if (wflag) {
			if (c == '\n')
				continue;
			if (!inword)
				if (isalpha(c))
					inword = 1;
				else
					continue;
			if (c=='-' && !hyphen) {
				hyphen = 1;
				continue;
			}
			hyphen = 0;
			if (c == '\'')
				continue;
			if (!isalpha(c) && !isdigit(c)) {
				inword = 0;
				putchar('\n');
				continue;
			}
		}
		putchar(c);
	}
	if (wflag && inword && !hyphen)
		putchar('\n');
}

/*
 * Process nroff control lines.
 * Remove EQN, TBL, macro defintions.
 * Process .so and .nx here.
 * Other lines have the rest of the line used.
 */
nroff(l)
register char *l;
{
	skiptitle = 0;
	if (l[1]=='E' && l[2]=='Q')
		eqn();
	else if (l[1]=='T' && l[2]=='S')
		tbl();
	else if (l[1]=='F' && l[2]=='S')
		footnote();
	else if (l[1]=='c' && l[2]=='e')
		centre(l);
	else if (l[1]=='n' && l[2]=='f')
		nofill();
	else if (l[1]=='D' && l[2]=='S')
		display();
	else if (l[1]=='K' && (l[2]=='F' || l[2]=='S'))
		display();
	else if (l[1]=='T' && l[2]=='L')
		titles();
	else if (l[1]=='A' && (l[2]=='I' || l[2]=='U'))
		titles();
	else if (l[2]=='H' && (l[1]=='S' || l[1]=='N'))
		titles();
	else if (l[1]=='n' && l[2]=='x')
		include(line+3, 'n');
	else if (l[1]=='s' && l[2]=='o')
		include(line+3, 's');
	else if (l[1]=='d' && l[2]=='e')
		macdef();
	else if (l[1]=='d' && l[2]=='s')
		return;
	else {
		while (*l!=' ' && *l!='\t' && *l!='\0')
			l++;
		while (*l==' ' || *l=='\t')
			l++;
		if (*l != '\0')
			output(l);
	}
}

/*
 * Process included files.
 * The first argument is the pointer to where
 * the filename is (it may have junk before and after it)
 * The second is 's' for .so and 'n' for .nx.
 */
include(fn, type)
register char *fn;
char type;
{
	register int c;
	register char *ep;

	while (*fn==' ' || *fn=='\t')
		fn++;
	for (ep = fn; (c = *ep)!='\0'; ep++)
		if (c=='\n' || c==' ' || c=='\t' || c=='\\')
			break;
	*ep = '\0';
	if (type == 's')
		dotso(fn);
	else
		*ofpp = dopen(fn);
}

/*
 * Process eqn directives.
 * Currently, this simply looks for
 * .EN lines as the terminator
 * and delim lines to set the eqn delimiters.
 */
eqn()
{
	register char *cp;

	while (dgets(line) != NULL) {
		if (strncmp(line, ".EN", 3) == 0)
			break;
		if (strncmp(line, "delim", 5) == 0) {
			for (cp = line+5; *cp==' ' || *cp=='\t'; cp++)
				;
			if (*cp=='\n' || *cp=='\0')
				continue;
			if (strncmp(cp, "off", 3) == 0) {
				delim1 = EOF;
				delim2 = EOF;
				continue;
			}
			delim1 = *cp++;
			delim2 = *cp;
		}
	}
}

/*
 * Process tbl directives.  At this time,
 * all this does is look for the terminating
 * .TE to end tables.
 */
tbl()
{
	while (dgets(line) != NULL)
		if (strncmp(line, ".TE", 3) == 0)
			break;
}

/*
 * In extended knowledge mode (-ms macros),
 * remove footnotes.  This mode is for
 * style and diction.
 */
footnote()
{
	if (!xflag)
		return;
	while (dgets(line) != NULL)
		if (strncmp(line, ".FE", 3) == 0)
			break;
}

/*
 * Throw away nofilled text as with footnotes above.
 */
nofill()
{
	if (!xflag)
		return;
	while (dgets(line) != NULL)
		if (strncmp(line, ".fi", 3) == 0)
			break;
}

/*
 * Skip centred lines, in extended mode,
 * by setting a skip counter on text.
 */
centre(l)
char *l;
{
	if ((skipcnt = atoi(l)) == 0)
		skipcnt = 1;
}

/*
 * Skip displays in extended mode.
 */
display()
{
	if (!xflag)
		return;
	while (dgets(line) != NULL) {
		if (strncmp(line, ".KE", 3) == 0)
			break;
		if (strncmp(line, ".DE", 3) == 0)
			break;
	}
}

/*
 * If in extended mode, skip titles and author's
 * names. Set a flag to skip until next nroff command.
 */
titles()
{
	if (xflag)
		skiptitle = 1;
}

/*
 * Remove a macro defintion.
 */
macdef()
{
	while (dgets(line) != NULL)
		if (strcmp(line, "..\n") == 0)
			break;
}

/*
 * Get a character from the next file stream.
 */
dgetc()
{
	register int c;

again:
	if (*ofpp==NULL || (c = getc(*ofpp))==EOF) {
		if (*ofpp!=stdin && *ofpp!=NULL)
			fclose(*ofpp);
		if (ofpp > ofiles) {
			ofpp--;
			goto again;
		}
		while (*flist != NULL)
			if ((*ofpp = dopen(*flist++)) != NULL)
				goto again;
		return (EOF);
	}
	return (c);
}

/*
 * Like fgets, only always reads using `dgetc'
 * into a buffer of `NLINE' characters.
 */
char *
dgets(as)
char *as;
{
	register unsigned n = NLINE;
	register char *s;
	register int c;

	s = as;
	while (--n>0 && (c = dgetc())!=EOF)
		if ((*s++ = c) == '\n')
			break;
	*s = '\0';
	return (c==EOF && s==as ? NULL : as);
}

/*
 * Open input files (for .so, .nx, and from 
 * command line).  Do not open any files twice.
 */
FILE *
dopen(fname)
register char *fname;
{
	register FNAME *fnp;
	register FILE *fp;

	for (fnp = fnames; fnp != NULL; fnp = fnp->fn_next)
		if (strcmp(fnp->fn_name, fname) == 0)
			return (NULL);
	if ((fp = fopen(fname, "r")) == NULL)
		fprintf(stderr, "deroff: cannot open `%s'\n", fname);
	else if ((fnp=(FNAME *)malloc(sizeof(FNAME)+strlen(fname)+2)) != NULL) {
		fnp->fn_next = fnames;
		fnames = fnp;
		strcpy(fnp->fn_name, fname);
	}
	return (fp);
}

/*
 * Include a file as per the `.so' request
 * line.
 */
dotso(fname)
char *fname;
{
	if (++ofpp >= &ofiles[NFNEST]) {
		fprintf(stderr, "deroff: .so nested too deep--%s\n", fname);
		ofpp--;
		return;
	}
	if ((*ofpp = dopen(fname)) == NULL)
		ofpp--;
}

usage()
{
	fprintf(stderr, "Usage: deroff [ -w ] [ -x ] [file ...]\n");
	exit(1);
}