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

Defined functions

addcmd defined in line 471; used 1 times
addmac defined in line 502; used 4 times
binsrch defined in line 534; used 2 times
checkknown defined in line 453; used 1 times
chkcmd defined in line 356; used 1 times
complain defined in line 331; used 2 times
eq defined in line 438; used 6 times
main defined in line 149; never used
nomatch defined in line 393; used 1 times
pe defined in line 445; used 9 times
process defined in line 235; used 2 times
prop defined in line 339; used 3 times
usage defined in line 229; used 4 times

Defined variables

br defined in line 48; used 15 times
cfilename defined in line 140; used 5 times
copyright defined in line 8; never used
fflag defined in line 142; used 2 times
knowncmds defined in line 104; used 9 times
line defined in line 139; used 21 times
lineno defined in line 138; used 15 times
ncmds defined in line 144; used 7 times
nfiles defined in line 141; used 3 times
sccsid defined in line 14; never used
sflag defined in line 143; used 2 times
slot defined in line 145; used 8 times
stk defined in line 39; used 32 times
stktop defined in line 40; used 27 times

Defined struct's

brstr defined in line 45; never used
stkstr defined in line 34; never used

Defined macros

FT defined in line 52; used 4 times
MAXBR defined in line 28; used 1 times
  • in line 48
MAXCMDS defined in line 29; used 3 times
MAXSTK defined in line 27; used 1 times
  • in line 39
SZ defined in line 50; used 2 times
Last modified: 1987-02-17
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3699
Valid CSS Valid XHTML 1.0 Strict