🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.text
Article #2445

Re: Is there a tbl checker?

#2445
From: Bill "Bill" Tuthill
Date: Thu, 31 Jan 1991 00:41
222 lines
5595 bytes
mcbride@rust.zso.dec.com (Melinda McBride) writes:
> I know that DWB has eqncheck, but does anybody know of a tbl check?

Here's something I wrote years ago.  We don't use troff any more at Sun,
so this program has no commercial value to my company.  It checks many
common mistakes, but is a bit Prussian about upper/lower case letters,
mostly to make tbl source easier to read.  Good luck!

----------------------- cut here ----------------------- 
#include <stdio.h>			/* checkts.c */
#include <ctype.h>

#define isfmtltr(f) \
 (f=='l'||f=='r'||f=='c'||f=='n'||f=='a'||f=='s'||f=='^'||f=='_'||f=='=')
#define isfmtarg(a) \
 (a=='|'||a=='t'||a=='f'||a=='I'||a=='B'||a=='L'||a=='p'||a=='+'||a=='-'||\
  a=='v'||a=='w'||a=='('||a=='i'||a==')'||a=='e'||a==' '||a=='.'||isdigit(a))

long lno = 1;		/* current line number of input file */

main(argc, argv)	/* check validity of tbl input files */
int argc;
char *argv[];
{
	FILE *fp, *fopen();
	char s[BUFSIZ]; 
	int i;

	if (argc == 1) {	/* no filenames given */
		while (fgets(s, BUFSIZ, stdin)) {
			checkts(s);
			lno++;
		}
		exit(0);
	}
	for (i = 1; i < argc; i++) {
		if ((fp = fopen(argv[i],"r")) == NULL) {
			fprintf(stderr, "checkts: ");
			perror(argv[i]);
			continue;
		}
		printf("%s:\n", argv[i]);
		while (fgets(s, BUFSIZ, fp)) {
			checkts(s);
			lno++;
		}
		fclose(fp);
		lno = 1;
	}
	exit(0);
}

int n_txtblks = 0;	/* total number of text blocks in file */

checkts(s)		/* read a line at a time, checking syntax */
char *s;
{
	static int fields = 0, prevfld;
	static char check = 0, period;
	static char justTS, tab, txtblk = 0;
	char msg[64], lastchar(), *sprintf();
	int n;

	if (strncmp(s, ".TS", 3) == 0) {
		check = 2;
		period = 0;
		prevfld = 0;
		justTS = 1;
		tab = '\t';
		return;
	}
	if (strncmp(s, ".T&", 3) == 0) {
		check = 2;
		period = 0;
		prevfld = 0;
		return;
	}
	if (strncmp(s, ".TE", 3) == 0) {
		check = 0;
		if (txtblk)
			error("unmatched T{ - missing T} above");
		if (!period)
			error("NO PERIOD at end of format specification!!");
		n_txtblks = 0;
		return;
	}
	if (strncmp(s, ".TH", 3) == 0)
		return;

	if (check == 1) {	/* check data fields */
		if ((n = count(tab, s)) >= fields) {
			sprintf(msg, "more than %d fields of data (%d)",
				fields, n+1); 
			error(msg);
		}
		if ((n = strfind("T}", s)) >= 0) {
			if (!txtblk)
				error("unmatched T} - missing T{ above");
			if (n != 0)
				error("T} must be at beginning of line");
			if (s[n+2] != '\n' && s[n+2] != tab)
				error("T} must be followed by tab");
			txtblk = 0;
		}
		if ((n = strfind("T{", s)) >= 0) {
			if (txtblk)
				error("unmatched T{ - missing T} above");
			if (++n_txtblks > 80)
				error("too many text blocks - limit is 80");
			if (s[n+2] != '\n')
				error("T{ must be at end of line");
			if (n > 0 && s[n-1] != tab)
				error("T{ must be preceded by tab");
			txtblk = 1;
		}
		if (strlen(s) > 132)
			error("line probably too long");
	}
	if (check == 2) {	/* check format specs */
		if (lastchar(s) == ';')
			rd_options(s, &tab, justTS);
		else {
			fields = rd_format(s);
			if (!prevfld)
				prevfld = fields;
			else if (fields != prevfld)
				error("unequal number of columns in format");
			if (fields > 20)
				error("too many columns - limit about 20");
			if (lastchar(s) == '.') {
				check = 1;
				period = 1;
			}
		}
	}
	justTS = 0;
	return;
}

rd_options(s, tab, justTS)	/* read tbl option specifications */
char s[], *tab, justTS;
{
	int i;

	if (!justTS)
		error("option specifications must follow .TS line");
	if (strfind("center", s) >= 0 && strfind("expand", s) >= 0)
		error("center and expand are incompatible options"); 
	if ((i = strfind("tab(", s)) >= 0) {
		*tab = s[i+4];
		if (*tab == '#')
			error("dangerous to use # as tab character");
	}
	else if ((i = strfind("tab (", s)) >= 0) {
		*tab = s[i+5];
		if (*tab == '#')
			error("dangerous to use # as tab character");
	}
}

rd_format(s)		/* read tbl format specifications */
char *s;
{
	int flds = 0;
	char msg[64];

	for (; *s != '\n'; s++) {
		if (isfmtltr(*s))
			flds++;
		else if (isfmtarg(*s))
			;
		else {
			sprintf(msg, "illegal character in format (%c)", *s);
			error(msg);
		}
	}
	return(flds);
}

count(c, s)		/* count instances of char c in string s */
char c, *s;
{
	int n = 0;

	for (; *s != '\n'; s++)
		if (*s == c)
			n++;
	return(n);
}

char
lastchar(str)		/* return last non-blank character in str */
char *str;
{
	int n;
	
	n = strlen(str);
	while (n && !isspace(str[--n]))
		;
	return(str[n-1]);
}

strfind(pat, str)	/* return position of pattern in string */
char pat[], str[];
{
	register int i, j, k;

	for (i = 0; str[i]; i++) {
		for (j = i, k = 0; pat[k] && str[j] == pat[k]; j++, k++)
			;
		if (pat[k] == NULL)
			return(i);
	}
	return(-1);
}

error(str)		/* print linenumber and error message string */
char *str;
{
	printf("\t%ld: %s\n", lno, str);
	fflush(stdout);
}
Technical Details
Message-ID: <7034@exodus.Eng.Sun.COM>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!thunder.mcrcim.mcgill.edu!snorkelwacker.mit.edu!think.com!samsung!sdd.hp.com!elroy.jpl.nasa.gov!lll-winken!sun-barr!newstop!exodus!cairo.Eng.Sun.COM!tut
References: <1393@rust.zso.dec.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!thunder.mcrcim.mcgill.edu!snorkelwacker.mit.edu!think.com!samsung!sdd.hp.com!elroy.jpl.nasa.gov!lll-winken!sun-barr!newstop!exodus!cairo.Eng.Sun.COM!tut
From: tut@cairo.Eng.Sun.COM (Bill "Bill" Tuthill)
Newsgroups: comp.text
Subject: Re: Is there a tbl checker?
Keywords: tbl troff
Message-ID: <7034@exodus.Eng.Sun.COM>
Date: 31 Jan 91 00:41:16 GMT
References: <1393@rust.zso.dec.com>
Sender: news@exodus.Eng.Sun.COM
Lines: 222