home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume22 / checknr next >
Encoding:
Internet Message Format  |  1990-05-04  |  19.9 KB

  1. Path: wuarchive!usc!bbn.com!papaya.bbn.com!rsalz
  2. From: rsalz@uunet.uu.net (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v22i012:  The BSD checknr program -- "lint for ?roff documents"
  5. Message-ID: <2486@papaya.bbn.com>
  6. Date: 3 May 90 20:46:54 GMT
  7. Lines: 782
  8. Approved: rsalz@uunet.UU.NET
  9. X-Checksum-Snefru: 6bda5a79 ccd92020 a4c93fde 79b03aa1
  10.  
  11. Submitted-by: Keith Bostic <bostic%okeeffe.Berkeley.EDU@ucbvax.berkeley.edu>
  12. Posting-number: Volume 22, Issue 12
  13. Archive-name: checknr
  14.  
  15. [ I asked Keith for a copy of checknr that could be posted, and he was
  16.   happy to give the following reply.  --r$  ]
  17.  
  18. It's definitely available, it just wasn't marked when uunet got their copy.
  19.  
  20. --keith
  21.  
  22. This program checks for matching font changes, size changes, matching
  23. commands like .DS/.DE, .FS/.FE, etc.  As the manpage suggests, think of
  24. it as lint for your nroff/troff documents.
  25.     /r$
  26.  
  27. # This is a shell archive.  Save it in a file, remove anything before
  28. # this line, and then unpack it by entering "sh file".  Note, it may
  29. # create directories; files and directories will be owned by you and
  30. # have default permissions.
  31. #
  32. # This archive contains:
  33. #
  34. #    Makefile
  35. #    checknr.1
  36. #    checknr.c
  37. #
  38. echo x - Makefile
  39. sed 's/^X//' >Makefile << 'END-of-Makefile'
  40. X#
  41. X# Copyright (c) 1988 Regents of the University of California.
  42. X# All rights reserved.
  43. X#
  44. X# Redistribution and use in source and binary forms are permitted
  45. X# provided that the above copyright notice and this paragraph are
  46. X# duplicated in all such forms and that any documentation, advertising
  47. X# materials, and other materials related to such redistribution and
  48. X# use acknowledge that the software was developed by the University
  49. X# of California, Berkeley.  The name of the University may not be
  50. X# used to endorse or promote products derived from this software
  51. X# without specific prior written permission.  THIS SOFTWARE IS PROVIDED
  52. X# ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  53. X# WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
  54. X# FITNESS FOR A PARTICULAR PURPOSE.
  55. X#
  56. X# @(#)Makefile    5.2 (Berkeley) 5/11/89
  57. X#
  58. X
  59. XCFLAGS=    -O
  60. XLIBC=    /lib/libc.a
  61. XSRCS=    checknr.c
  62. XOBJS=
  63. XMAN=    checknr.0
  64. X
  65. Xall: checknr
  66. X
  67. Xchecknr: ${LIBC}
  68. X    ${CC} -o $@ ${CFLAGS} $@.c
  69. X
  70. Xclean:
  71. X    rm -f ${OBJS} core checknr
  72. X
  73. Xcleandir: clean
  74. X    rm -f ${MAN} tags .depend
  75. X
  76. Xdepend: ${SRCS}
  77. X    mkdep -p ${CFLAGS} ${SRCS}
  78. X
  79. Xinstall: ${MAN}
  80. X    install -s -o bin -g bin -m 755 checknr ${DESTDIR}/usr/bin
  81. X    install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat1
  82. X
  83. Xlint: ${SRCS}
  84. X    lint ${CFLAGS} ${SRCS}
  85. X
  86. Xtags: ${SRCS}
  87. X    ctags ${SRCS}
  88. END-of-Makefile
  89. echo x - checknr.1
  90. sed 's/^X//' >checknr.1 << 'END-of-checknr.1'
  91. X.\" Copyright (c) 1980 The Regents of the University of California.
  92. X.\" All rights reserved.
  93. X.\"
  94. X.\" Redistribution and use in source and binary forms are permitted
  95. X.\" provided that the above copyright notice and this paragraph are
  96. X.\" duplicated in all such forms and that any documentation,
  97. X.\" advertising materials, and other materials related to such
  98. X.\" distribution and use acknowledge that the software was developed
  99. X.\" by the University of California, Berkeley.  The name of the
  100. X.\" University may not be used to endorse or promote products derived
  101. X.\" from this software without specific prior written permission.
  102. X.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  103. X.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  104. X.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  105. X.\"
  106. X.\"    @(#)checknr.1    6.3 (Berkeley) 10/30/88
  107. X.\"
  108. X.TH CHECKNR 1 "October 30, 1988"
  109. X.UC 4
  110. X.SH NAME
  111. Xchecknr \- check nroff/troff files
  112. X.SH SYNOPSIS
  113. X.B checknr
  114. X[
  115. X.B \-s
  116. X] [
  117. X.B \-f
  118. X] [
  119. X.BR \-a ".x1.y1.x2.y2. ... .xn.yn"
  120. X] [
  121. X.BR \-c ".x1.x2.x3 ... .xn"
  122. X] [
  123. X\fIfile\fP ...
  124. X]
  125. X.SH DESCRIPTION
  126. X.I Checknr
  127. Xchecks a list of
  128. X.IR nroff (1)
  129. Xor
  130. X.IR troff (1)
  131. Xinput files for certain kinds of errors
  132. Xinvolving mismatched opening and closing delimiters
  133. Xand unknown commands.
  134. XIf no files are specified,
  135. X.I checknr
  136. Xchecks the standard input.
  137. XDelimeters checked are:
  138. X.IP (1)
  139. XFont changes using \efx ... \efP.
  140. X.IP (2)
  141. XSize changes using \esx ... \es0.
  142. X.IP (3)
  143. XMacros that come in open ... close forms, for example,
  144. Xthe .TS and .TE macros which must always come in pairs.
  145. X.PP
  146. X.I Checknr
  147. Xknows about the
  148. X.IR ms (7)
  149. Xand
  150. X.IR me (7)
  151. Xmacro packages.
  152. X.PP
  153. XAdditional pairs of macros can be added to the list using the
  154. X.B \-a
  155. Xoption.
  156. XThis must be followed by groups of six characters, each group defining
  157. Xa pair of macros.
  158. XThe six characters are
  159. Xa period,
  160. Xthe first macro name,
  161. Xanother period,
  162. Xand the second macro name.
  163. XFor example, to define a pair .BS and .ES, use \-\fBa\fP.BS.ES
  164. X.PP
  165. XThe
  166. X.B \-c
  167. Xoption defines commands which would otherwise be complained about
  168. Xas undefined.
  169. X.PP
  170. XThe
  171. X.B \-f
  172. Xoption requests
  173. X.I checknr
  174. Xto ignore \ef font changes.
  175. X.PP
  176. XThe
  177. X.B \-s
  178. Xoption requests
  179. X.I checknr
  180. Xto ignore \es size changes.
  181. X.PP
  182. X.I Checknr
  183. Xis intended to be used on documents that are prepared with
  184. X.I checknr
  185. Xin mind, much the same as
  186. X.I lint.
  187. XIt expects a certain document writing style for \ef and \es commands,
  188. Xin that each \efx must be terminated with \efP and
  189. Xeach \esx must be terminated with \es0.
  190. XWhile it will work to directly go into the next font or explicitly
  191. Xspecify the original font or point size,
  192. Xand many existing documents actually do this,
  193. Xsuch a practice will produce complaints from
  194. X.I checknr.
  195. XSince it is probably better to use the \efP and \es0 forms anyway,
  196. Xyou should think of this as a contribution to your document
  197. Xpreparation style.
  198. X.SH SEE\ ALSO
  199. Xnroff(1), troff(1), checkeq(1), ms(7), me(7)
  200. X.SH DIAGNOSTICS
  201. XComplaints about unmatched delimiters.
  202. X.br
  203. XComplaints about unrecognized commands.
  204. X.br
  205. XVarious complaints about the syntax of commands.
  206. X.SH BUGS
  207. XThere is no way to define a 1 character macro name using
  208. X.BR \-a .
  209. X.br
  210. XDoes not correctly recognize certain reasonable constructs,
  211. Xsuch as conditionals.
  212. END-of-checknr.1
  213. echo x - checknr.c
  214. sed 's/^X//' >checknr.c << 'END-of-checknr.c'
  215. X/*
  216. X * Copyright (c) 1980 The Regents of the University of California.
  217. X * All rights reserved.
  218. X *
  219. X * Redistribution and use in source and binary forms are permitted
  220. X * provided that the above copyright notice and this paragraph are
  221. X * duplicated in all such forms and that any documentation,
  222. X * advertising materials, and other materials related to such
  223. X * distribution and use acknowledge that the software was developed
  224. X * by the University of California, Berkeley.  The name of the
  225. X * University may not be used to endorse or promote products derived
  226. X * from this software without specific prior written permission.
  227. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  228. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  229. X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  230. X */
  231. X
  232. X#ifndef lint
  233. Xchar copyright[] =
  234. X"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
  235. X All rights reserved.\n";
  236. X#endif /* not lint */
  237. X
  238. X#ifndef lint
  239. Xstatic char sccsid[] = "@(#)checknr.c    5.3 (Berkeley) 10/30/88";
  240. X#endif /* not lint */
  241. X
  242. X/*
  243. X * checknr: check an nroff/troff input file for matching macro calls.
  244. X * we also attempt to match size and font changes, but only the embedded
  245. X * kind.  These must end in \s0 and \fP resp.  Maybe more sophistication
  246. X * later but for now think of these restrictions as contributions to
  247. X * structured typesetting.
  248. X */
  249. X#include <stdio.h>
  250. X#include <ctype.h>
  251. X
  252. X#define MAXSTK    100    /* Stack size */
  253. X#define MAXBR    100    /* Max number of bracket pairs known */
  254. X#define MAXCMDS    500    /* Max number of commands known */
  255. X
  256. X/*
  257. X * The stack on which we remember what we've seen so far.
  258. X */
  259. Xstruct stkstr {
  260. X    int opno;    /* number of opening bracket */
  261. X    int pl;        /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
  262. X    int parm;    /* parm to size, font, etc */
  263. X    int lno;    /* line number the thing came in in */
  264. X} stk[MAXSTK];
  265. Xint stktop;
  266. X
  267. X/*
  268. X * The kinds of opening and closing brackets.
  269. X */
  270. Xstruct brstr {
  271. X    char *opbr;
  272. X    char *clbr;
  273. X} br[MAXBR] = {
  274. X    /* A few bare bones troff commands */
  275. X#define SZ    0
  276. X    "sz",    "sz",    /* also \s */
  277. X#define FT    1
  278. X    "ft",    "ft",    /* also \f */
  279. X    /* the -mm package */
  280. X    "AL",    "LE",
  281. X    "AS",    "AE",
  282. X    "BL",    "LE",
  283. X    "BS",    "BE",
  284. X    "DF",    "DE",
  285. X    "DL",    "LE",
  286. X    "DS",    "DE",
  287. X    "FS",    "FE",
  288. X    "ML",    "LE",
  289. X    "NS",    "NE",
  290. X    "RL",    "LE",
  291. X    "VL",    "LE",
  292. X    /* the -ms package */
  293. X    "AB",    "AE",
  294. X    "BD",    "DE",
  295. X    "CD",    "DE",
  296. X    "DS",    "DE",
  297. X    "FS",    "FE",
  298. X    "ID",    "DE",
  299. X    "KF",    "KE",
  300. X    "KS",    "KE",
  301. X    "LD",    "DE",
  302. X    "LG",    "NL",
  303. X    "QS",    "QE",
  304. X    "RS",    "RE",
  305. X    "SM",    "NL",
  306. X    "XA",    "XE",
  307. X    "XS",    "XE",
  308. X    /* The -me package */
  309. X    "(b",    ")b",
  310. X    "(c",    ")c",
  311. X    "(d",    ")d",
  312. X    "(f",    ")f",
  313. X    "(l",    ")l",
  314. X    "(q",    ")q",
  315. X    "(x",    ")x",
  316. X    "(z",    ")z",
  317. X    /* Things needed by preprocessors */
  318. X    "EQ",    "EN",
  319. X    "TS",    "TE",
  320. X    /* Refer */
  321. X    "[",    "]",
  322. X    0,    0
  323. X};
  324. X
  325. X/*
  326. X * All commands known to nroff, plus macro packages.
  327. X * Used so we can complain about unrecognized commands.
  328. X */
  329. Xchar *knowncmds[MAXCMDS] = {
  330. X"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t",
  331. X"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++",
  332. X"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M",
  333. X"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB",
  334. X"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B",  "B1", "B2",
  335. X"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT",
  336. X"D",  "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM",
  337. X"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO",
  338. X"FQ", "FS", "FV", "FX", "H",  "HC", "HD", "HM", "HO", "HU", "I",  "ID",
  339. X"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB",
  340. X"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR",
  341. X"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P",
  342. X"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R",  "RA",
  343. X"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S",  "S0", "S2", "S3", "SA",
  344. X"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE",
  345. X"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL",
  346. X"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[",  "[-", "[0",
  347. X"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]",  "]-", "]<", "]>",
  348. X"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b",  "ba", "bc", "bd",
  349. X"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs",
  350. X"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec",
  351. X"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo",
  352. X"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i",
  353. X"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
  354. X"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1",
  355. X"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx",
  356. X"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps",
  357. X"q",  "r",  "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb",
  358. X"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th",
  359. X"ti", "tl", "tm", "tp", "tr", "u",  "uf", "uh", "ul", "vs", "wh", "xp",
  360. X"yr", 0
  361. X};
  362. X
  363. Xint    lineno;        /* current line number in input file */
  364. Xchar    line[256];    /* the current line */
  365. Xchar    *cfilename;    /* name of current file */
  366. Xint    nfiles;        /* number of files to process */
  367. Xint    fflag;        /* -f: ignore \f */
  368. Xint    sflag;        /* -s: ignore \s */
  369. Xint    ncmds;        /* size of knowncmds */
  370. Xint    slot;        /* slot in knowncmds found by binsrch */
  371. X
  372. Xchar    *malloc();
  373. X
  374. Xmain(argc, argv)
  375. Xint argc;
  376. Xchar **argv;
  377. X{
  378. X    FILE *f;
  379. X    int i;
  380. X    char *cp;
  381. X    char b1[4];
  382. X
  383. X    /* Figure out how many known commands there are */
  384. X    while (knowncmds[ncmds])
  385. X        ncmds++;
  386. X    while (argc > 1 && argv[1][0] == '-') {
  387. X        switch(argv[1][1]) {
  388. X
  389. X        /* -a: add pairs of macros */
  390. X        case 'a':
  391. X            i = strlen(argv[1]) - 2;
  392. X            if (i % 6 != 0)
  393. X                usage();
  394. X            /* look for empty macro slots */
  395. X            for (i=0; br[i].opbr; i++)
  396. X                ;
  397. X            for (cp=argv[1]+3; cp[-1]; cp += 6) {
  398. X                br[i].opbr = malloc(3);
  399. X                strncpy(br[i].opbr, cp, 2);
  400. X                br[i].clbr = malloc(3);
  401. X                strncpy(br[i].clbr, cp+3, 2);
  402. X                addmac(br[i].opbr);    /* knows pairs are also known cmds */
  403. X                addmac(br[i].clbr);
  404. X                i++;
  405. X            }
  406. X            break;
  407. X
  408. X        /* -c: add known commands */
  409. X        case 'c':
  410. X            i = strlen(argv[1]) - 2;
  411. X            if (i % 3 != 0)
  412. X                usage();
  413. X            for (cp=argv[1]+3; cp[-1]; cp += 3) {
  414. X                if (cp[2] && cp[2] != '.')
  415. X                    usage();
  416. X                strncpy(b1, cp, 2);
  417. X                addmac(b1);
  418. X            }
  419. X            break;
  420. X
  421. X        /* -f: ignore font changes */
  422. X        case 'f':
  423. X            fflag = 1;
  424. X            break;
  425. X
  426. X        /* -s: ignore size changes */
  427. X        case 's':
  428. X            sflag = 1;
  429. X            break;
  430. X        default:
  431. X            usage();
  432. X        }
  433. X        argc--; argv++;
  434. X    }
  435. X
  436. X    nfiles = argc - 1;
  437. X
  438. X    if (nfiles > 0) {
  439. X        for (i=1; i<argc; i++) {
  440. X            cfilename = argv[i];
  441. X            f = fopen(cfilename, "r");
  442. X            if (f == NULL)
  443. X                perror(cfilename);
  444. X            else
  445. X                process(f);
  446. X        }
  447. X    } else {
  448. X        cfilename = "stdin";
  449. X        process(stdin);
  450. X    }
  451. X    exit(0);
  452. X}
  453. X
  454. Xusage()
  455. X{
  456. X    printf("Usage: checknr -s -f -a.xx.yy.xx.yy... -c.xx.xx.xx...\n");
  457. X    exit(1);
  458. X}
  459. X
  460. Xprocess(f)
  461. XFILE *f;
  462. X{
  463. X    register int i, n;
  464. X    char mac[5];    /* The current macro or nroff command */
  465. X    int pl;
  466. X
  467. X    stktop = -1;
  468. X    for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
  469. X        if (line[0] == '.') {
  470. X            /*
  471. X             * find and isolate the macro/command name.
  472. X             */
  473. X            strncpy(mac, line+1, 4);
  474. X            if (isspace(mac[0])) {
  475. X                pe(lineno);
  476. X                printf("Empty command\n");
  477. X            } else if (isspace(mac[1])) {
  478. X                mac[1] = 0;
  479. X            } else if (isspace(mac[2])) {
  480. X                mac[2] = 0;
  481. X            } else if (mac[0] != '\\' || mac[1] != '\"') {
  482. X                pe(lineno);
  483. X                printf("Command too long\n");
  484. X            }
  485. X
  486. X            /*
  487. X             * Is it a known command?
  488. X             */
  489. X            checkknown(mac);
  490. X
  491. X            /*
  492. X             * Should we add it?
  493. X             */
  494. X            if (eq(mac, "de"))
  495. X                addcmd(line);
  496. X
  497. X            chkcmd(line, mac);
  498. X        }
  499. X
  500. X        /*
  501. X         * At this point we process the line looking
  502. X         * for \s and \f.
  503. X         */
  504. X        for (i=0; line[i]; i++)
  505. X            if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
  506. X                if (!sflag && line[++i]=='s') {
  507. X                    pl = line[++i];
  508. X                    if (isdigit(pl)) {
  509. X                        n = pl - '0';
  510. X                        pl = ' ';
  511. X                    } else
  512. X                        n = 0;
  513. X                    while (isdigit(line[++i]))
  514. X                        n = 10 * n + line[i] - '0';
  515. X                    i--;
  516. X                    if (n == 0) {
  517. X                        if (stk[stktop].opno == SZ) {
  518. X                            stktop--;
  519. X                        } else {
  520. X                            pe(lineno);
  521. X                            printf("unmatched \\s0\n");
  522. X                        }
  523. X                    } else {
  524. X                        stk[++stktop].opno = SZ;
  525. X                        stk[stktop].pl = pl;
  526. X                        stk[stktop].parm = n;
  527. X                        stk[stktop].lno = lineno;
  528. X                    }
  529. X                } else if (!fflag && line[i]=='f') {
  530. X                    n = line[++i];
  531. X                    if (n == 'P') {
  532. X                        if (stk[stktop].opno == FT) {
  533. X                            stktop--;
  534. X                        } else {
  535. X                            pe(lineno);
  536. X                            printf("unmatched \\fP\n");
  537. X                        }
  538. X                    } else {
  539. X                        stk[++stktop].opno = FT;
  540. X                        stk[stktop].pl = 1;
  541. X                        stk[stktop].parm = n;
  542. X                        stk[stktop].lno = lineno;
  543. X                    }
  544. X                }
  545. X            }
  546. X    }
  547. X    /*
  548. X     * We've hit the end and look at all this stuff that hasn't been
  549. X     * matched yet!  Complain, complain.
  550. X     */
  551. X    for (i=stktop; i>=0; i--) {
  552. X        complain(i);
  553. X    }
  554. X}
  555. X
  556. Xcomplain(i)
  557. X{
  558. X    pe(stk[i].lno);
  559. X    printf("Unmatched ");
  560. X    prop(i);
  561. X    printf("\n");
  562. X}
  563. X
  564. Xprop(i)
  565. X{
  566. X    if (stk[i].pl == 0)
  567. X        printf(".%s", br[stk[i].opno].opbr);
  568. X    else switch(stk[i].opno) {
  569. X    case SZ:
  570. X        printf("\\s%c%d", stk[i].pl, stk[i].parm);
  571. X        break;
  572. X    case FT:
  573. X        printf("\\f%c", stk[i].parm);
  574. X        break;
  575. X    default:
  576. X        printf("Bug: stk[%d].opno = %d = .%s, .%s",
  577. X            i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr);
  578. X    }
  579. X}
  580. X
  581. Xchkcmd(line, mac)
  582. Xchar *line;
  583. Xchar *mac;
  584. X{
  585. X    register int i, n;
  586. X
  587. X    /*
  588. X     * Check to see if it matches top of stack.
  589. X     */
  590. X    if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
  591. X        stktop--;    /* OK. Pop & forget */
  592. X    else {
  593. X        /* No. Maybe it's an opener */
  594. X        for (i=0; br[i].opbr; i++) {
  595. X            if (eq(mac, br[i].opbr)) {
  596. X                /* Found. Push it. */
  597. X                stktop++;
  598. X                stk[stktop].opno = i;
  599. X                stk[stktop].pl = 0;
  600. X                stk[stktop].parm = 0;
  601. X                stk[stktop].lno = lineno;
  602. X                break;
  603. X            }
  604. X            /*
  605. X             * Maybe it's an unmatched closer.
  606. X             * NOTE: this depends on the fact
  607. X             * that none of the closers can be
  608. X             * openers too.
  609. X             */
  610. X            if (eq(mac, br[i].clbr)) {
  611. X                nomatch(mac);
  612. X                break;
  613. X            }
  614. X        }
  615. X    }
  616. X}
  617. X
  618. Xnomatch(mac)
  619. Xchar *mac;
  620. X{
  621. X    register int i, j;
  622. X
  623. X    /*
  624. X     * Look for a match further down on stack
  625. X     * If we find one, it suggests that the stuff in
  626. X     * between is supposed to match itself.
  627. X     */
  628. X    for (j=stktop; j>=0; j--)
  629. X        if (eq(mac,br[stk[j].opno].clbr)) {
  630. X            /* Found.  Make a good diagnostic. */
  631. X            if (j == stktop-2) {
  632. X                /*
  633. X                 * Check for special case \fx..\fR and don't
  634. X                 * complain.
  635. X                 */
  636. X                if (stk[j+1].opno==FT && stk[j+1].parm!='R'
  637. X                 && stk[j+2].opno==FT && stk[j+2].parm=='R') {
  638. X                    stktop = j -1;
  639. X                    return;
  640. X                }
  641. X                /*
  642. X                 * We have two unmatched frobs.  Chances are
  643. X                 * they were intended to match, so we mention
  644. X                 * them together.
  645. X                 */
  646. X                pe(stk[j+1].lno);
  647. X                prop(j+1);
  648. X                printf(" does not match %d: ", stk[j+2].lno);
  649. X                prop(j+2);
  650. X                printf("\n");
  651. X            } else for (i=j+1; i <= stktop; i++) {
  652. X                complain(i);
  653. X            }
  654. X            stktop = j-1;
  655. X            return;
  656. X        }
  657. X    /* Didn't find one.  Throw this away. */
  658. X    pe(lineno);
  659. X    printf("Unmatched .%s\n", mac);
  660. X}
  661. X
  662. X/* eq: are two strings equal? */
  663. Xeq(s1, s2)
  664. Xchar *s1, *s2;
  665. X{
  666. X    return (strcmp(s1, s2) == 0);
  667. X}
  668. X
  669. X/* print the first part of an error message, given the line number */
  670. Xpe(lineno)
  671. Xint lineno;
  672. X{
  673. X    if (nfiles > 1)
  674. X        printf("%s: ", cfilename);
  675. X    printf("%d: ", lineno);
  676. X}
  677. X
  678. Xcheckknown(mac)
  679. Xchar *mac;
  680. X{
  681. X
  682. X    if (eq(mac, "."))
  683. X        return;
  684. X    if (binsrch(mac) >= 0)
  685. X        return;
  686. X    if (mac[0] == '\\' && mac[1] == '"')    /* comments */
  687. X        return;
  688. X
  689. X    pe(lineno);
  690. X    printf("Unknown command: .%s\n", mac);
  691. X}
  692. X
  693. X/*
  694. X * We have a .de xx line in "line".  Add xx to the list of known commands.
  695. X */
  696. Xaddcmd(line)
  697. Xchar *line;
  698. X{
  699. X    char *mac;
  700. X
  701. X    /* grab the macro being defined */
  702. X    mac = line+4;
  703. X    while (isspace(*mac))
  704. X        mac++;
  705. X    if (*mac == 0) {
  706. X        pe(lineno);
  707. X        printf("illegal define: %s\n", line);
  708. X        return;
  709. X    }
  710. X    mac[2] = 0;
  711. X    if (isspace(mac[1]) || mac[1] == '\\')
  712. X        mac[1] = 0;
  713. X    if (ncmds >= MAXCMDS) {
  714. X        printf("Only %d known commands allowed\n", MAXCMDS);
  715. X        exit(1);
  716. X    }
  717. X    addmac(mac);
  718. X}
  719. X
  720. X/*
  721. X * Add mac to the list.  We should really have some kind of tree
  722. X * structure here but this is a quick-and-dirty job and I just don't
  723. X * have time to mess with it.  (I wonder if this will come back to haunt
  724. X * me someday?)  Anyway, I claim that .de is fairly rare in user
  725. X * nroff programs, and the register loop below is pretty fast.
  726. X */
  727. Xaddmac(mac)
  728. Xchar *mac;
  729. X{
  730. X    register char **src, **dest, **loc;
  731. X
  732. X    if (binsrch(mac) >= 0){    /* it's OK to redefine something */
  733. X#ifdef DEBUG
  734. X        printf("binsrch(%s) -> already in table\n", mac);
  735. X#endif DEBUG
  736. X        return;
  737. X    }
  738. X    /* binsrch sets slot as a side effect */
  739. X#ifdef DEBUG
  740. Xprintf("binsrch(%s) -> %d\n", mac, slot);
  741. X#endif
  742. X    loc = &knowncmds[slot];
  743. X    src = &knowncmds[ncmds-1];
  744. X    dest = src+1;
  745. X    while (dest > loc)
  746. X        *dest-- = *src--;
  747. X    *loc = malloc(3);
  748. X    strcpy(*loc, mac);
  749. X    ncmds++;
  750. X#ifdef DEBUG
  751. Xprintf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds);
  752. X#endif
  753. X}
  754. X
  755. X/*
  756. X * Do a binary search in knowncmds for mac.
  757. X * If found, return the index.  If not, return -1.
  758. X */
  759. Xbinsrch(mac)
  760. Xchar *mac;
  761. X{
  762. X    register char *p;    /* pointer to current cmd in list */
  763. X    register int d;        /* difference if any */
  764. X    register int mid;    /* mid point in binary search */
  765. X    register int top, bot;    /* boundaries of bin search, inclusive */
  766. X
  767. X    top = ncmds-1;
  768. X    bot = 0;
  769. X    while (top >= bot) {
  770. X        mid = (top+bot)/2;
  771. X        p = knowncmds[mid];
  772. X        d = p[0] - mac[0];
  773. X        if (d == 0)
  774. X            d = p[1] - mac[1];
  775. X        if (d == 0)
  776. X            return mid;
  777. X        if (d < 0)
  778. X            bot = mid + 1;
  779. X        else
  780. X            top = mid - 1;
  781. X    }
  782. X    slot = bot;    /* place it would have gone */
  783. X    return -1;
  784. X}
  785. END-of-checknr.c
  786. exit
  787.  
  788.  
  789. exit 0 # Just in case...
  790. -- 
  791. Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
  792. Use a domain-based address or give alternate paths, or you may lose out.
  793.