Path: 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 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 /* checkts.c */ #include #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); }