home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / checknr next >
Text File  |  1990-06-07  |  20KB  |  785 lines

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