[comp.text] Is there a tbl checker?

mcbride@rust.zso.dec.com (Melinda McBride) (01/31/91)

I support a group of writers who use troff (ditroff).  It seems that
about 90% of the problems I see are coding problems in tables.  I know
that DWB has eqncheck, but does anybody know of a tbl check?  It would
sure be handy.


Melinda McBride

Open Systems Publishing Tools Group	decwrl!rust!mcbride
Digital Equipment Corporation		(206)865-8705
14475 NE 24th St.
Bellevue, WA 98007

bin@primate.wisc.edu (Brain in Neutral) (01/31/91)

From article <1393@rust.zso.dec.com>, by mcbride@rust.zso.dec.com (Melinda McBride):
> I support a group of writers who use troff (ditroff).  It seems that
> about 90% of the problems I see are coding problems in tables.  I know
> that DWB has eqncheck, but does anybody know of a tbl check?  It would
> sure be handy.

Sure, it's called "tbl", and its helpful and informative error
message is "Line n: bad table specification character. tbl quits".

What more could anyone ask for? :-)
--
Paul DuBois
dubois@primate.wisc.edu

tut@cairo.Eng.Sun.COM (Bill "Bill" Tuthill) (01/31/91)

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);
}

npn@cbnewsl.att.com (nils-peter.nelson) (02/01/91)

The new checkdoc command in DWB 3.1 supersedes (yes, that's
how it's spelled) eqncheck. It checks for proper usage
of: mm macros, tbl, pic, eqn, grap.  It is neither foolproof
nor perfect, but it does a nice job of warning you about a
variety of common errors, and in a much more polite way
than the language processors do. Here's a sample snarfed
off my screen:
$ cat bad.mm
.TL
Title
.MT 0
First line.
.TS
center, allbox
c c c
1 2 3
.TE
.SG
$ checkdoc bad.mm
checkdoc version 1.0 diagnostics:
bad.mm:
Line 3: Missing a .AU before the .MT macro
Line 7: format lines must have same number of columns
Line 8: format lines must have same number of columns
Line 9: Missing table format data detected
Line 10: Missing a .AU before the .SG macro
10 lines done
===

Best of all, checkdoc has no options!