1*7c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 2*7c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 3*7c478bd9Sstevel@tonic-gate 4*7c478bd9Sstevel@tonic-gate 5*7c478bd9Sstevel@tonic-gate /* 6*7c478bd9Sstevel@tonic-gate * Copyright (c) 1980 Regents of the University of California. 7*7c478bd9Sstevel@tonic-gate * All rights reserved. The Berkeley software License Agreement 8*7c478bd9Sstevel@tonic-gate * specifies the terms and conditions for redistribution. 9*7c478bd9Sstevel@tonic-gate */ 10*7c478bd9Sstevel@tonic-gate 11*7c478bd9Sstevel@tonic-gate /* 12*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 13*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 14*7c478bd9Sstevel@tonic-gate */ 15*7c478bd9Sstevel@tonic-gate 16*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 17*7c478bd9Sstevel@tonic-gate 18*7c478bd9Sstevel@tonic-gate /* 19*7c478bd9Sstevel@tonic-gate * checknr: check an nroff/troff input file for matching macro calls. 20*7c478bd9Sstevel@tonic-gate * we also attempt to match size and font changes, but only the embedded 21*7c478bd9Sstevel@tonic-gate * kind. These must end in \s0 and \fP resp. Maybe more sophistication 22*7c478bd9Sstevel@tonic-gate * later but for now think of these restrictions as contributions to 23*7c478bd9Sstevel@tonic-gate * structured typesetting. 24*7c478bd9Sstevel@tonic-gate */ 25*7c478bd9Sstevel@tonic-gate #include <stdio.h> 26*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 27*7c478bd9Sstevel@tonic-gate #include <unistd.h> 28*7c478bd9Sstevel@tonic-gate #include <string.h> 29*7c478bd9Sstevel@tonic-gate #include <ctype.h> 30*7c478bd9Sstevel@tonic-gate #include <locale.h> 31*7c478bd9Sstevel@tonic-gate 32*7c478bd9Sstevel@tonic-gate #define MAXSTK 100 /* Stack size */ 33*7c478bd9Sstevel@tonic-gate static int maxstk; 34*7c478bd9Sstevel@tonic-gate #define MAXBR 100 /* Max number of bracket pairs known */ 35*7c478bd9Sstevel@tonic-gate #define MAXCMDS 500 /* Max number of commands known */ 36*7c478bd9Sstevel@tonic-gate 37*7c478bd9Sstevel@tonic-gate /* 38*7c478bd9Sstevel@tonic-gate * The stack on which we remember what we've seen so far. 39*7c478bd9Sstevel@tonic-gate */ 40*7c478bd9Sstevel@tonic-gate static struct stkstr { 41*7c478bd9Sstevel@tonic-gate int opno; /* number of opening bracket */ 42*7c478bd9Sstevel@tonic-gate int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ 43*7c478bd9Sstevel@tonic-gate int parm; /* parm to size, font, etc */ 44*7c478bd9Sstevel@tonic-gate int lno; /* line number the thing came in in */ 45*7c478bd9Sstevel@tonic-gate } *stk; 46*7c478bd9Sstevel@tonic-gate static int stktop; 47*7c478bd9Sstevel@tonic-gate 48*7c478bd9Sstevel@tonic-gate /* 49*7c478bd9Sstevel@tonic-gate * The kinds of opening and closing brackets. 50*7c478bd9Sstevel@tonic-gate */ 51*7c478bd9Sstevel@tonic-gate static struct brstr { 52*7c478bd9Sstevel@tonic-gate char *opbr; 53*7c478bd9Sstevel@tonic-gate char *clbr; 54*7c478bd9Sstevel@tonic-gate } br[MAXBR] = { 55*7c478bd9Sstevel@tonic-gate /* A few bare bones troff commands */ 56*7c478bd9Sstevel@tonic-gate #define SZ 0 57*7c478bd9Sstevel@tonic-gate "sz", "sz", /* also \s */ 58*7c478bd9Sstevel@tonic-gate #define FT 1 59*7c478bd9Sstevel@tonic-gate "ft", "ft", /* also \f */ 60*7c478bd9Sstevel@tonic-gate /* the -mm package */ 61*7c478bd9Sstevel@tonic-gate "AL", "LE", 62*7c478bd9Sstevel@tonic-gate "AS", "AE", 63*7c478bd9Sstevel@tonic-gate "BL", "LE", 64*7c478bd9Sstevel@tonic-gate "BS", "BE", 65*7c478bd9Sstevel@tonic-gate "DF", "DE", 66*7c478bd9Sstevel@tonic-gate "DL", "LE", 67*7c478bd9Sstevel@tonic-gate "DS", "DE", 68*7c478bd9Sstevel@tonic-gate "FS", "FE", 69*7c478bd9Sstevel@tonic-gate "ML", "LE", 70*7c478bd9Sstevel@tonic-gate "NS", "NE", 71*7c478bd9Sstevel@tonic-gate "RL", "LE", 72*7c478bd9Sstevel@tonic-gate "VL", "LE", 73*7c478bd9Sstevel@tonic-gate /* the -ms package */ 74*7c478bd9Sstevel@tonic-gate "AB", "AE", 75*7c478bd9Sstevel@tonic-gate "BD", "DE", 76*7c478bd9Sstevel@tonic-gate "CD", "DE", 77*7c478bd9Sstevel@tonic-gate "DS", "DE", 78*7c478bd9Sstevel@tonic-gate "FS", "FE", 79*7c478bd9Sstevel@tonic-gate "ID", "DE", 80*7c478bd9Sstevel@tonic-gate "KF", "KE", 81*7c478bd9Sstevel@tonic-gate "KS", "KE", 82*7c478bd9Sstevel@tonic-gate "LD", "DE", 83*7c478bd9Sstevel@tonic-gate "LG", "NL", 84*7c478bd9Sstevel@tonic-gate "QS", "QE", 85*7c478bd9Sstevel@tonic-gate "RS", "RE", 86*7c478bd9Sstevel@tonic-gate "SM", "NL", 87*7c478bd9Sstevel@tonic-gate "XA", "XE", 88*7c478bd9Sstevel@tonic-gate "XS", "XE", 89*7c478bd9Sstevel@tonic-gate /* The -me package */ 90*7c478bd9Sstevel@tonic-gate "(b", ")b", 91*7c478bd9Sstevel@tonic-gate "(c", ")c", 92*7c478bd9Sstevel@tonic-gate "(d", ")d", 93*7c478bd9Sstevel@tonic-gate "(f", ")f", 94*7c478bd9Sstevel@tonic-gate "(l", ")l", 95*7c478bd9Sstevel@tonic-gate "(q", ")q", 96*7c478bd9Sstevel@tonic-gate "(x", ")x", 97*7c478bd9Sstevel@tonic-gate "(z", ")z", 98*7c478bd9Sstevel@tonic-gate /* Things needed by preprocessors */ 99*7c478bd9Sstevel@tonic-gate "EQ", "EN", 100*7c478bd9Sstevel@tonic-gate "TS", "TE", 101*7c478bd9Sstevel@tonic-gate /* Refer */ 102*7c478bd9Sstevel@tonic-gate "[", "]", 103*7c478bd9Sstevel@tonic-gate 0, 0 104*7c478bd9Sstevel@tonic-gate }; 105*7c478bd9Sstevel@tonic-gate 106*7c478bd9Sstevel@tonic-gate /* 107*7c478bd9Sstevel@tonic-gate * All commands known to nroff, plus macro packages. 108*7c478bd9Sstevel@tonic-gate * Used so we can complain about unrecognized commands. 109*7c478bd9Sstevel@tonic-gate */ 110*7c478bd9Sstevel@tonic-gate static char *knowncmds[MAXCMDS] = { 111*7c478bd9Sstevel@tonic-gate "$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t", 112*7c478bd9Sstevel@tonic-gate "(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", 113*7c478bd9Sstevel@tonic-gate "+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", 114*7c478bd9Sstevel@tonic-gate "@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", 115*7c478bd9Sstevel@tonic-gate "AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B", "B1", "B2", 116*7c478bd9Sstevel@tonic-gate "BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT", 117*7c478bd9Sstevel@tonic-gate "D", "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM", 118*7c478bd9Sstevel@tonic-gate "EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", 119*7c478bd9Sstevel@tonic-gate "FQ", "FS", "FV", "FX", "H", "HC", "HD", "HM", "HO", "HU", "I", "ID", 120*7c478bd9Sstevel@tonic-gate "IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB", 121*7c478bd9Sstevel@tonic-gate "LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR", 122*7c478bd9Sstevel@tonic-gate "MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P", 123*7c478bd9Sstevel@tonic-gate "P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R", "RA", 124*7c478bd9Sstevel@tonic-gate "RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S", "S0", "S2", "S3", "SA", 125*7c478bd9Sstevel@tonic-gate "SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE", 126*7c478bd9Sstevel@tonic-gate "TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL", 127*7c478bd9Sstevel@tonic-gate "WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[", "[-", "[0", 128*7c478bd9Sstevel@tonic-gate "[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]", "]-", "]<", "]>", 129*7c478bd9Sstevel@tonic-gate "][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "ba", "bc", "bd", 130*7c478bd9Sstevel@tonic-gate "bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs", 131*7c478bd9Sstevel@tonic-gate "ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec", 132*7c478bd9Sstevel@tonic-gate "ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo", 133*7c478bd9Sstevel@tonic-gate "fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i", 134*7c478bd9Sstevel@tonic-gate "ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln", 135*7c478bd9Sstevel@tonic-gate "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1", 136*7c478bd9Sstevel@tonic-gate "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx", 137*7c478bd9Sstevel@tonic-gate "of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps", 138*7c478bd9Sstevel@tonic-gate "q", "r", "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb", 139*7c478bd9Sstevel@tonic-gate "sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th", 140*7c478bd9Sstevel@tonic-gate "ti", "tl", "tm", "tp", "tr", "u", "uf", "uh", "ul", "vs", "wh", "xp", 141*7c478bd9Sstevel@tonic-gate "yr", 0 142*7c478bd9Sstevel@tonic-gate }; 143*7c478bd9Sstevel@tonic-gate 144*7c478bd9Sstevel@tonic-gate static int lineno; /* current line number in input file */ 145*7c478bd9Sstevel@tonic-gate static char line[256]; /* the current line */ 146*7c478bd9Sstevel@tonic-gate static char *cfilename; /* name of current file */ 147*7c478bd9Sstevel@tonic-gate static int nfiles; /* number of files to process */ 148*7c478bd9Sstevel@tonic-gate static int fflag; /* -f: ignore \f */ 149*7c478bd9Sstevel@tonic-gate static int sflag; /* -s: ignore \s */ 150*7c478bd9Sstevel@tonic-gate static int ncmds; /* size of knowncmds */ 151*7c478bd9Sstevel@tonic-gate static int slot; /* slot in knowncmds found by binsrch */ 152*7c478bd9Sstevel@tonic-gate 153*7c478bd9Sstevel@tonic-gate static void growstk(); 154*7c478bd9Sstevel@tonic-gate static void usage(); 155*7c478bd9Sstevel@tonic-gate static void process(FILE *f); 156*7c478bd9Sstevel@tonic-gate static void complain(int i); 157*7c478bd9Sstevel@tonic-gate static void prop(int i); 158*7c478bd9Sstevel@tonic-gate static void chkcmd(char *line, char *mac); 159*7c478bd9Sstevel@tonic-gate static void nomatch(char *mac); 160*7c478bd9Sstevel@tonic-gate static int eq(char *s1, char *s2); 161*7c478bd9Sstevel@tonic-gate static void pe(int lineno); 162*7c478bd9Sstevel@tonic-gate static void checkknown(char *mac); 163*7c478bd9Sstevel@tonic-gate static void addcmd(char *line); 164*7c478bd9Sstevel@tonic-gate static void addmac(char *mac); 165*7c478bd9Sstevel@tonic-gate static int binsrch(char *mac); 166*7c478bd9Sstevel@tonic-gate 167*7c478bd9Sstevel@tonic-gate static void 168*7c478bd9Sstevel@tonic-gate growstk() 169*7c478bd9Sstevel@tonic-gate { 170*7c478bd9Sstevel@tonic-gate stktop++; 171*7c478bd9Sstevel@tonic-gate if (stktop >= maxstk) { 172*7c478bd9Sstevel@tonic-gate maxstk *= 2; 173*7c478bd9Sstevel@tonic-gate stk = (struct stkstr *)realloc(stk, 174*7c478bd9Sstevel@tonic-gate sizeof (struct stkstr) * maxstk); 175*7c478bd9Sstevel@tonic-gate } 176*7c478bd9Sstevel@tonic-gate } 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate int 179*7c478bd9Sstevel@tonic-gate main(argc, argv) 180*7c478bd9Sstevel@tonic-gate int argc; 181*7c478bd9Sstevel@tonic-gate char **argv; 182*7c478bd9Sstevel@tonic-gate { 183*7c478bd9Sstevel@tonic-gate FILE *f; 184*7c478bd9Sstevel@tonic-gate int i; 185*7c478bd9Sstevel@tonic-gate char *cp; 186*7c478bd9Sstevel@tonic-gate char b1[4]; 187*7c478bd9Sstevel@tonic-gate 188*7c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 189*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) 190*7c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 191*7c478bd9Sstevel@tonic-gate #endif 192*7c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 193*7c478bd9Sstevel@tonic-gate stk = (struct stkstr *)calloc(sizeof (struct stkstr), 100); 194*7c478bd9Sstevel@tonic-gate maxstk = 100; 195*7c478bd9Sstevel@tonic-gate /* Figure out how many known commands there are */ 196*7c478bd9Sstevel@tonic-gate while (knowncmds[ncmds]) 197*7c478bd9Sstevel@tonic-gate ncmds++; 198*7c478bd9Sstevel@tonic-gate while (argc > 1 && argv[1][0] == '-') { 199*7c478bd9Sstevel@tonic-gate switch (argv[1][1]) { 200*7c478bd9Sstevel@tonic-gate 201*7c478bd9Sstevel@tonic-gate /* -a: add pairs of macros */ 202*7c478bd9Sstevel@tonic-gate case 'a': 203*7c478bd9Sstevel@tonic-gate i = strlen(argv[1]) - 2; 204*7c478bd9Sstevel@tonic-gate if (i % 6 != 0) 205*7c478bd9Sstevel@tonic-gate usage(); 206*7c478bd9Sstevel@tonic-gate /* look for empty macro slots */ 207*7c478bd9Sstevel@tonic-gate for (i = 0; br[i].opbr; i++) 208*7c478bd9Sstevel@tonic-gate ; 209*7c478bd9Sstevel@tonic-gate for (cp = argv[1]+3; cp[-1]; cp += 6) { 210*7c478bd9Sstevel@tonic-gate br[i].opbr = malloc(3); 211*7c478bd9Sstevel@tonic-gate (void) strncpy(br[i].opbr, cp, 2); 212*7c478bd9Sstevel@tonic-gate br[i].clbr = malloc(3); 213*7c478bd9Sstevel@tonic-gate (void) strncpy(br[i].clbr, cp+3, 2); 214*7c478bd9Sstevel@tonic-gate /* knows pairs are also known cmds */ 215*7c478bd9Sstevel@tonic-gate addmac(br[i].opbr); 216*7c478bd9Sstevel@tonic-gate addmac(br[i].clbr); 217*7c478bd9Sstevel@tonic-gate i++; 218*7c478bd9Sstevel@tonic-gate } 219*7c478bd9Sstevel@tonic-gate break; 220*7c478bd9Sstevel@tonic-gate 221*7c478bd9Sstevel@tonic-gate /* -c: add known commands */ 222*7c478bd9Sstevel@tonic-gate case 'c': 223*7c478bd9Sstevel@tonic-gate i = strlen(argv[1]) - 2; 224*7c478bd9Sstevel@tonic-gate if (i % 3 != 0) 225*7c478bd9Sstevel@tonic-gate usage(); 226*7c478bd9Sstevel@tonic-gate for (cp = argv[1]+3; cp[-1]; cp += 3) { 227*7c478bd9Sstevel@tonic-gate if (cp[2] && cp[2] != '.') 228*7c478bd9Sstevel@tonic-gate usage(); 229*7c478bd9Sstevel@tonic-gate (void) strncpy(b1, cp, 2); 230*7c478bd9Sstevel@tonic-gate addmac(b1); 231*7c478bd9Sstevel@tonic-gate } 232*7c478bd9Sstevel@tonic-gate break; 233*7c478bd9Sstevel@tonic-gate 234*7c478bd9Sstevel@tonic-gate /* -f: ignore font changes */ 235*7c478bd9Sstevel@tonic-gate case 'f': 236*7c478bd9Sstevel@tonic-gate fflag = 1; 237*7c478bd9Sstevel@tonic-gate break; 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate /* -s: ignore size changes */ 240*7c478bd9Sstevel@tonic-gate case 's': 241*7c478bd9Sstevel@tonic-gate sflag = 1; 242*7c478bd9Sstevel@tonic-gate break; 243*7c478bd9Sstevel@tonic-gate default: 244*7c478bd9Sstevel@tonic-gate usage(); 245*7c478bd9Sstevel@tonic-gate } 246*7c478bd9Sstevel@tonic-gate argc--; argv++; 247*7c478bd9Sstevel@tonic-gate } 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate nfiles = argc - 1; 250*7c478bd9Sstevel@tonic-gate 251*7c478bd9Sstevel@tonic-gate if (nfiles > 0) { 252*7c478bd9Sstevel@tonic-gate for (i = 1; i < argc; i++) { 253*7c478bd9Sstevel@tonic-gate cfilename = argv[i]; 254*7c478bd9Sstevel@tonic-gate f = fopen(cfilename, "r"); 255*7c478bd9Sstevel@tonic-gate if (f == NULL) { 256*7c478bd9Sstevel@tonic-gate perror(cfilename); 257*7c478bd9Sstevel@tonic-gate exit(1); 258*7c478bd9Sstevel@tonic-gate } 259*7c478bd9Sstevel@tonic-gate else 260*7c478bd9Sstevel@tonic-gate process(f); 261*7c478bd9Sstevel@tonic-gate } 262*7c478bd9Sstevel@tonic-gate } else { 263*7c478bd9Sstevel@tonic-gate cfilename = "stdin"; 264*7c478bd9Sstevel@tonic-gate process(stdin); 265*7c478bd9Sstevel@tonic-gate } 266*7c478bd9Sstevel@tonic-gate return (0); 267*7c478bd9Sstevel@tonic-gate } 268*7c478bd9Sstevel@tonic-gate 269*7c478bd9Sstevel@tonic-gate static void 270*7c478bd9Sstevel@tonic-gate usage() 271*7c478bd9Sstevel@tonic-gate { 272*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Usage: \ 273*7c478bd9Sstevel@tonic-gate checknr [ -fs ] [ -a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [ filename .. ]\n")); 274*7c478bd9Sstevel@tonic-gate exit(1); 275*7c478bd9Sstevel@tonic-gate } 276*7c478bd9Sstevel@tonic-gate 277*7c478bd9Sstevel@tonic-gate static void 278*7c478bd9Sstevel@tonic-gate process(FILE *f) 279*7c478bd9Sstevel@tonic-gate { 280*7c478bd9Sstevel@tonic-gate int i, n; 281*7c478bd9Sstevel@tonic-gate char mac[5]; /* The current macro or nroff command */ 282*7c478bd9Sstevel@tonic-gate int pl; 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate stktop = -1; 285*7c478bd9Sstevel@tonic-gate for (lineno = 1; fgets(line, sizeof (line), f); lineno++) { 286*7c478bd9Sstevel@tonic-gate if (line[0] == '.') { 287*7c478bd9Sstevel@tonic-gate /* 288*7c478bd9Sstevel@tonic-gate * find and isolate the macro/command name. 289*7c478bd9Sstevel@tonic-gate */ 290*7c478bd9Sstevel@tonic-gate (void) strncpy(mac, line+1, 4); 291*7c478bd9Sstevel@tonic-gate if (isspace(mac[0])) { 292*7c478bd9Sstevel@tonic-gate pe(lineno); 293*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Empty command\n")); 294*7c478bd9Sstevel@tonic-gate } else if (isspace(mac[1])) { 295*7c478bd9Sstevel@tonic-gate mac[1] = 0; 296*7c478bd9Sstevel@tonic-gate } else if (isspace(mac[2])) { 297*7c478bd9Sstevel@tonic-gate mac[2] = 0; 298*7c478bd9Sstevel@tonic-gate } else if (mac[0] != '\\' || mac[1] != '\"') { 299*7c478bd9Sstevel@tonic-gate pe(lineno); 300*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Command too long\n")); 301*7c478bd9Sstevel@tonic-gate } 302*7c478bd9Sstevel@tonic-gate 303*7c478bd9Sstevel@tonic-gate /* 304*7c478bd9Sstevel@tonic-gate * Is it a known command? 305*7c478bd9Sstevel@tonic-gate */ 306*7c478bd9Sstevel@tonic-gate checkknown(mac); 307*7c478bd9Sstevel@tonic-gate 308*7c478bd9Sstevel@tonic-gate /* 309*7c478bd9Sstevel@tonic-gate * Should we add it? 310*7c478bd9Sstevel@tonic-gate */ 311*7c478bd9Sstevel@tonic-gate if (eq(mac, "de")) 312*7c478bd9Sstevel@tonic-gate addcmd(line); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate chkcmd(line, mac); 315*7c478bd9Sstevel@tonic-gate } 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate /* 318*7c478bd9Sstevel@tonic-gate * At this point we process the line looking 319*7c478bd9Sstevel@tonic-gate * for \s and \f. 320*7c478bd9Sstevel@tonic-gate */ 321*7c478bd9Sstevel@tonic-gate for (i = 0; line[i]; i++) 322*7c478bd9Sstevel@tonic-gate if (line[i] == '\\' && (i == 0 || line[i-1] != '\\')) { 323*7c478bd9Sstevel@tonic-gate if (!sflag && line[++i] == 's') { 324*7c478bd9Sstevel@tonic-gate pl = line[++i]; 325*7c478bd9Sstevel@tonic-gate if (isdigit(pl)) { 326*7c478bd9Sstevel@tonic-gate n = pl - '0'; 327*7c478bd9Sstevel@tonic-gate pl = ' '; 328*7c478bd9Sstevel@tonic-gate } else 329*7c478bd9Sstevel@tonic-gate n = 0; 330*7c478bd9Sstevel@tonic-gate while (isdigit(line[++i])) 331*7c478bd9Sstevel@tonic-gate n = 10 * n + line[i] - '0'; 332*7c478bd9Sstevel@tonic-gate i--; 333*7c478bd9Sstevel@tonic-gate if (n == 0) { 334*7c478bd9Sstevel@tonic-gate if (stk[stktop].opno == SZ) { 335*7c478bd9Sstevel@tonic-gate stktop--; 336*7c478bd9Sstevel@tonic-gate } else { 337*7c478bd9Sstevel@tonic-gate pe(lineno); 338*7c478bd9Sstevel@tonic-gate (void) printf( 339*7c478bd9Sstevel@tonic-gate gettext("unmatched \\s0\n")); 340*7c478bd9Sstevel@tonic-gate } 341*7c478bd9Sstevel@tonic-gate } else { 342*7c478bd9Sstevel@tonic-gate growstk(); 343*7c478bd9Sstevel@tonic-gate stk[stktop].opno = SZ; 344*7c478bd9Sstevel@tonic-gate stk[stktop].pl = pl; 345*7c478bd9Sstevel@tonic-gate stk[stktop].parm = n; 346*7c478bd9Sstevel@tonic-gate stk[stktop].lno = lineno; 347*7c478bd9Sstevel@tonic-gate } 348*7c478bd9Sstevel@tonic-gate } else if (!fflag && line[i] == 'f') { 349*7c478bd9Sstevel@tonic-gate n = line[++i]; 350*7c478bd9Sstevel@tonic-gate if (n == 'P') { 351*7c478bd9Sstevel@tonic-gate if (stk[stktop].opno == FT) { 352*7c478bd9Sstevel@tonic-gate stktop--; 353*7c478bd9Sstevel@tonic-gate } else { 354*7c478bd9Sstevel@tonic-gate pe(lineno); 355*7c478bd9Sstevel@tonic-gate (void) printf( 356*7c478bd9Sstevel@tonic-gate gettext("unmatched \\fP\n")); 357*7c478bd9Sstevel@tonic-gate } 358*7c478bd9Sstevel@tonic-gate } else { 359*7c478bd9Sstevel@tonic-gate growstk(); 360*7c478bd9Sstevel@tonic-gate stk[stktop].opno = FT; 361*7c478bd9Sstevel@tonic-gate stk[stktop].pl = 1; 362*7c478bd9Sstevel@tonic-gate stk[stktop].parm = n; 363*7c478bd9Sstevel@tonic-gate stk[stktop].lno = lineno; 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate } 366*7c478bd9Sstevel@tonic-gate } 367*7c478bd9Sstevel@tonic-gate } 368*7c478bd9Sstevel@tonic-gate /* 369*7c478bd9Sstevel@tonic-gate * We've hit the end and look at all this stuff that hasn't been 370*7c478bd9Sstevel@tonic-gate * matched yet! Complain, complain. 371*7c478bd9Sstevel@tonic-gate */ 372*7c478bd9Sstevel@tonic-gate for (i = stktop; i >= 0; i--) { 373*7c478bd9Sstevel@tonic-gate complain(i); 374*7c478bd9Sstevel@tonic-gate } 375*7c478bd9Sstevel@tonic-gate } 376*7c478bd9Sstevel@tonic-gate 377*7c478bd9Sstevel@tonic-gate static void 378*7c478bd9Sstevel@tonic-gate complain(int i) 379*7c478bd9Sstevel@tonic-gate { 380*7c478bd9Sstevel@tonic-gate pe(stk[i].lno); 381*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Unmatched ")); 382*7c478bd9Sstevel@tonic-gate prop(i); 383*7c478bd9Sstevel@tonic-gate (void) printf("\n"); 384*7c478bd9Sstevel@tonic-gate } 385*7c478bd9Sstevel@tonic-gate 386*7c478bd9Sstevel@tonic-gate static void 387*7c478bd9Sstevel@tonic-gate prop(int i) 388*7c478bd9Sstevel@tonic-gate { 389*7c478bd9Sstevel@tonic-gate if (stk[i].pl == 0) 390*7c478bd9Sstevel@tonic-gate (void) printf(".%s", br[stk[i].opno].opbr); 391*7c478bd9Sstevel@tonic-gate else switch (stk[i].opno) { 392*7c478bd9Sstevel@tonic-gate case SZ: 393*7c478bd9Sstevel@tonic-gate (void) printf("\\s%c%d", stk[i].pl, stk[i].parm); 394*7c478bd9Sstevel@tonic-gate break; 395*7c478bd9Sstevel@tonic-gate case FT: 396*7c478bd9Sstevel@tonic-gate (void) printf("\\f%c", stk[i].parm); 397*7c478bd9Sstevel@tonic-gate break; 398*7c478bd9Sstevel@tonic-gate default: 399*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Bug: stk[%d].opno = %d = .%s, .%s"), 400*7c478bd9Sstevel@tonic-gate i, stk[i].opno, br[stk[i].opno].opbr, 401*7c478bd9Sstevel@tonic-gate br[stk[i].opno].clbr); 402*7c478bd9Sstevel@tonic-gate } 403*7c478bd9Sstevel@tonic-gate } 404*7c478bd9Sstevel@tonic-gate 405*7c478bd9Sstevel@tonic-gate /* ARGSUSED */ 406*7c478bd9Sstevel@tonic-gate static void 407*7c478bd9Sstevel@tonic-gate chkcmd(char *line, char *mac) 408*7c478bd9Sstevel@tonic-gate { 409*7c478bd9Sstevel@tonic-gate int i; 410*7c478bd9Sstevel@tonic-gate 411*7c478bd9Sstevel@tonic-gate /* 412*7c478bd9Sstevel@tonic-gate * Check to see if it matches top of stack. 413*7c478bd9Sstevel@tonic-gate */ 414*7c478bd9Sstevel@tonic-gate if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) 415*7c478bd9Sstevel@tonic-gate stktop--; /* OK. Pop & forget */ 416*7c478bd9Sstevel@tonic-gate else { 417*7c478bd9Sstevel@tonic-gate /* No. Maybe it's an opener */ 418*7c478bd9Sstevel@tonic-gate for (i = 0; br[i].opbr; i++) { 419*7c478bd9Sstevel@tonic-gate if (eq(mac, br[i].opbr)) { 420*7c478bd9Sstevel@tonic-gate /* Found. Push it. */ 421*7c478bd9Sstevel@tonic-gate growstk(); 422*7c478bd9Sstevel@tonic-gate stk[stktop].opno = i; 423*7c478bd9Sstevel@tonic-gate stk[stktop].pl = 0; 424*7c478bd9Sstevel@tonic-gate stk[stktop].parm = 0; 425*7c478bd9Sstevel@tonic-gate stk[stktop].lno = lineno; 426*7c478bd9Sstevel@tonic-gate break; 427*7c478bd9Sstevel@tonic-gate } 428*7c478bd9Sstevel@tonic-gate /* 429*7c478bd9Sstevel@tonic-gate * Maybe it's an unmatched closer. 430*7c478bd9Sstevel@tonic-gate * NOTE: this depends on the fact 431*7c478bd9Sstevel@tonic-gate * that none of the closers can be 432*7c478bd9Sstevel@tonic-gate * openers too. 433*7c478bd9Sstevel@tonic-gate */ 434*7c478bd9Sstevel@tonic-gate if (eq(mac, br[i].clbr)) { 435*7c478bd9Sstevel@tonic-gate nomatch(mac); 436*7c478bd9Sstevel@tonic-gate break; 437*7c478bd9Sstevel@tonic-gate } 438*7c478bd9Sstevel@tonic-gate } 439*7c478bd9Sstevel@tonic-gate } 440*7c478bd9Sstevel@tonic-gate } 441*7c478bd9Sstevel@tonic-gate 442*7c478bd9Sstevel@tonic-gate static void 443*7c478bd9Sstevel@tonic-gate nomatch(char *mac) 444*7c478bd9Sstevel@tonic-gate { 445*7c478bd9Sstevel@tonic-gate int i, j; 446*7c478bd9Sstevel@tonic-gate 447*7c478bd9Sstevel@tonic-gate /* 448*7c478bd9Sstevel@tonic-gate * Look for a match further down on stack 449*7c478bd9Sstevel@tonic-gate * If we find one, it suggests that the stuff in 450*7c478bd9Sstevel@tonic-gate * between is supposed to match itself. 451*7c478bd9Sstevel@tonic-gate */ 452*7c478bd9Sstevel@tonic-gate for (j = stktop; j >= 0; j--) 453*7c478bd9Sstevel@tonic-gate if (eq(mac, br[stk[j].opno].clbr)) { 454*7c478bd9Sstevel@tonic-gate /* Found. Make a good diagnostic. */ 455*7c478bd9Sstevel@tonic-gate if (j == stktop-2) { 456*7c478bd9Sstevel@tonic-gate /* 457*7c478bd9Sstevel@tonic-gate * Check for special case \fx..\fR and don't 458*7c478bd9Sstevel@tonic-gate * complain. 459*7c478bd9Sstevel@tonic-gate */ 460*7c478bd9Sstevel@tonic-gate if (stk[j+1].opno == FT && 461*7c478bd9Sstevel@tonic-gate stk[j+1].parm != 'R' && 462*7c478bd9Sstevel@tonic-gate stk[j+2].opno == FT && 463*7c478bd9Sstevel@tonic-gate stk[j+2].parm == 'R') { 464*7c478bd9Sstevel@tonic-gate stktop = j -1; 465*7c478bd9Sstevel@tonic-gate return; 466*7c478bd9Sstevel@tonic-gate } 467*7c478bd9Sstevel@tonic-gate /* 468*7c478bd9Sstevel@tonic-gate * We have two unmatched frobs. Chances are 469*7c478bd9Sstevel@tonic-gate * they were intended to match, so we mention 470*7c478bd9Sstevel@tonic-gate * them together. 471*7c478bd9Sstevel@tonic-gate */ 472*7c478bd9Sstevel@tonic-gate pe(stk[j+1].lno); 473*7c478bd9Sstevel@tonic-gate prop(j+1); 474*7c478bd9Sstevel@tonic-gate (void) printf(gettext(" does not match %d: "), 475*7c478bd9Sstevel@tonic-gate stk[j+2].lno); 476*7c478bd9Sstevel@tonic-gate prop(j+2); 477*7c478bd9Sstevel@tonic-gate (void) printf("\n"); 478*7c478bd9Sstevel@tonic-gate } else for (i = j+1; i <= stktop; i++) { 479*7c478bd9Sstevel@tonic-gate complain(i); 480*7c478bd9Sstevel@tonic-gate } 481*7c478bd9Sstevel@tonic-gate stktop = j-1; 482*7c478bd9Sstevel@tonic-gate return; 483*7c478bd9Sstevel@tonic-gate } 484*7c478bd9Sstevel@tonic-gate /* Didn't find one. Throw this away. */ 485*7c478bd9Sstevel@tonic-gate pe(lineno); 486*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Unmatched .%s\n"), mac); 487*7c478bd9Sstevel@tonic-gate } 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate /* eq: are two strings equal? */ 490*7c478bd9Sstevel@tonic-gate static int 491*7c478bd9Sstevel@tonic-gate eq(char *s1, char *s2) 492*7c478bd9Sstevel@tonic-gate { 493*7c478bd9Sstevel@tonic-gate return (strcmp(s1, s2) == 0); 494*7c478bd9Sstevel@tonic-gate } 495*7c478bd9Sstevel@tonic-gate 496*7c478bd9Sstevel@tonic-gate /* print the first part of an error message, given the line number */ 497*7c478bd9Sstevel@tonic-gate static void 498*7c478bd9Sstevel@tonic-gate pe(int lineno) 499*7c478bd9Sstevel@tonic-gate { 500*7c478bd9Sstevel@tonic-gate if (nfiles > 1) 501*7c478bd9Sstevel@tonic-gate (void) printf("%s: ", cfilename); 502*7c478bd9Sstevel@tonic-gate (void) printf("%d: ", lineno); 503*7c478bd9Sstevel@tonic-gate } 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate static void 506*7c478bd9Sstevel@tonic-gate checkknown(char *mac) 507*7c478bd9Sstevel@tonic-gate { 508*7c478bd9Sstevel@tonic-gate 509*7c478bd9Sstevel@tonic-gate if (eq(mac, ".")) 510*7c478bd9Sstevel@tonic-gate return; 511*7c478bd9Sstevel@tonic-gate if (binsrch(mac) >= 0) 512*7c478bd9Sstevel@tonic-gate return; 513*7c478bd9Sstevel@tonic-gate if (mac[0] == '\\' && mac[1] == '"') /* comments */ 514*7c478bd9Sstevel@tonic-gate return; 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate pe(lineno); 517*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Unknown command: .%s\n"), mac); 518*7c478bd9Sstevel@tonic-gate } 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate /* 521*7c478bd9Sstevel@tonic-gate * We have a .de xx line in "line". Add xx to the list of known commands. 522*7c478bd9Sstevel@tonic-gate */ 523*7c478bd9Sstevel@tonic-gate static void 524*7c478bd9Sstevel@tonic-gate addcmd(char *line) 525*7c478bd9Sstevel@tonic-gate { 526*7c478bd9Sstevel@tonic-gate char *mac; 527*7c478bd9Sstevel@tonic-gate 528*7c478bd9Sstevel@tonic-gate /* grab the macro being defined */ 529*7c478bd9Sstevel@tonic-gate mac = line+4; 530*7c478bd9Sstevel@tonic-gate while (isspace(*mac)) 531*7c478bd9Sstevel@tonic-gate mac++; 532*7c478bd9Sstevel@tonic-gate if (*mac == 0) { 533*7c478bd9Sstevel@tonic-gate pe(lineno); 534*7c478bd9Sstevel@tonic-gate (void) printf(gettext("illegal define: %s\n"), line); 535*7c478bd9Sstevel@tonic-gate return; 536*7c478bd9Sstevel@tonic-gate } 537*7c478bd9Sstevel@tonic-gate mac[2] = 0; 538*7c478bd9Sstevel@tonic-gate if (isspace(mac[1]) || mac[1] == '\\') 539*7c478bd9Sstevel@tonic-gate mac[1] = 0; 540*7c478bd9Sstevel@tonic-gate if (ncmds >= MAXCMDS) { 541*7c478bd9Sstevel@tonic-gate (void) printf(gettext("Only %d known commands allowed\n"), 542*7c478bd9Sstevel@tonic-gate MAXCMDS); 543*7c478bd9Sstevel@tonic-gate exit(1); 544*7c478bd9Sstevel@tonic-gate } 545*7c478bd9Sstevel@tonic-gate addmac(mac); 546*7c478bd9Sstevel@tonic-gate } 547*7c478bd9Sstevel@tonic-gate 548*7c478bd9Sstevel@tonic-gate /* 549*7c478bd9Sstevel@tonic-gate * Add mac to the list. We should really have some kind of tree 550*7c478bd9Sstevel@tonic-gate * structure here but this is a quick-and-dirty job and I just don't 551*7c478bd9Sstevel@tonic-gate * have time to mess with it. (I wonder if this will come back to haunt 552*7c478bd9Sstevel@tonic-gate * me someday?) Anyway, I claim that .de is fairly rare in user 553*7c478bd9Sstevel@tonic-gate * nroff programs, and the loop below is pretty fast. 554*7c478bd9Sstevel@tonic-gate */ 555*7c478bd9Sstevel@tonic-gate static void 556*7c478bd9Sstevel@tonic-gate addmac(char *mac) 557*7c478bd9Sstevel@tonic-gate { 558*7c478bd9Sstevel@tonic-gate char **src, **dest, **loc; 559*7c478bd9Sstevel@tonic-gate 560*7c478bd9Sstevel@tonic-gate if (binsrch(mac) >= 0) { /* it's OK to redefine something */ 561*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 562*7c478bd9Sstevel@tonic-gate (void) printf("binsrch(%s) -> already in table\n", mac); 563*7c478bd9Sstevel@tonic-gate #endif 564*7c478bd9Sstevel@tonic-gate return; 565*7c478bd9Sstevel@tonic-gate } 566*7c478bd9Sstevel@tonic-gate /* binsrch sets slot as a side effect */ 567*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 568*7c478bd9Sstevel@tonic-gate printf("binsrch(%s) -> %d\n", mac, slot); 569*7c478bd9Sstevel@tonic-gate #endif 570*7c478bd9Sstevel@tonic-gate loc = &knowncmds[slot]; 571*7c478bd9Sstevel@tonic-gate src = &knowncmds[ncmds-1]; 572*7c478bd9Sstevel@tonic-gate dest = src+1; 573*7c478bd9Sstevel@tonic-gate while (dest > loc) 574*7c478bd9Sstevel@tonic-gate *dest-- = *src--; 575*7c478bd9Sstevel@tonic-gate *loc = malloc(3); 576*7c478bd9Sstevel@tonic-gate (void) strcpy(*loc, mac); 577*7c478bd9Sstevel@tonic-gate ncmds++; 578*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 579*7c478bd9Sstevel@tonic-gate (void) printf("after: %s %s %s %s %s, %d cmds\n", 580*7c478bd9Sstevel@tonic-gate knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], 581*7c478bd9Sstevel@tonic-gate knowncmds[slot+1], knowncmds[slot+2], ncmds); 582*7c478bd9Sstevel@tonic-gate #endif 583*7c478bd9Sstevel@tonic-gate } 584*7c478bd9Sstevel@tonic-gate 585*7c478bd9Sstevel@tonic-gate /* 586*7c478bd9Sstevel@tonic-gate * Do a binary search in knowncmds for mac. 587*7c478bd9Sstevel@tonic-gate * If found, return the index. If not, return -1. 588*7c478bd9Sstevel@tonic-gate */ 589*7c478bd9Sstevel@tonic-gate static int 590*7c478bd9Sstevel@tonic-gate binsrch(char *mac) 591*7c478bd9Sstevel@tonic-gate { 592*7c478bd9Sstevel@tonic-gate char *p; /* pointer to current cmd in list */ 593*7c478bd9Sstevel@tonic-gate int d; /* difference if any */ 594*7c478bd9Sstevel@tonic-gate int mid; /* mid point in binary search */ 595*7c478bd9Sstevel@tonic-gate int top, bot; /* boundaries of bin search, inclusive */ 596*7c478bd9Sstevel@tonic-gate 597*7c478bd9Sstevel@tonic-gate top = ncmds-1; 598*7c478bd9Sstevel@tonic-gate bot = 0; 599*7c478bd9Sstevel@tonic-gate while (top >= bot) { 600*7c478bd9Sstevel@tonic-gate mid = (top+bot)/2; 601*7c478bd9Sstevel@tonic-gate p = knowncmds[mid]; 602*7c478bd9Sstevel@tonic-gate d = p[0] - mac[0]; 603*7c478bd9Sstevel@tonic-gate if (d == 0) 604*7c478bd9Sstevel@tonic-gate d = p[1] - mac[1]; 605*7c478bd9Sstevel@tonic-gate if (d == 0) 606*7c478bd9Sstevel@tonic-gate return (mid); 607*7c478bd9Sstevel@tonic-gate if (d < 0) 608*7c478bd9Sstevel@tonic-gate bot = mid + 1; 609*7c478bd9Sstevel@tonic-gate else 610*7c478bd9Sstevel@tonic-gate top = mid - 1; 611*7c478bd9Sstevel@tonic-gate } 612*7c478bd9Sstevel@tonic-gate slot = bot; /* place it would have gone */ 613*7c478bd9Sstevel@tonic-gate return (-1); 614*7c478bd9Sstevel@tonic-gate } 615