|
|
1.1 ! root 1: /* ! 2: * Strip nroff/troff control lines and eqn ! 3: * and tbl sequences from input. ! 4: * Also, optionally, produce the ! 5: * output as a set of words. ! 6: */ ! 7: ! 8: #include <stdio.h> ! 9: #include <ctype.h> ! 10: ! 11: #define NLINE 500 /* Input line length */ ! 12: #define NFNEST 15 /* Depth of .so file nesting */ ! 13: ! 14: FILE *ofiles[NFNEST]; ! 15: FILE **ofpp = &ofiles[0]; ! 16: char **flist; /* list of files to open */ ! 17: ! 18: typedef struct FNAME { ! 19: struct FNAME *fn_next; ! 20: char fn_name[]; ! 21: } FNAME; ! 22: ! 23: FNAME *fnames; ! 24: ! 25: char line[NLINE]; ! 26: ! 27: int delim1 = EOF; /* Start embedded eqn */ ! 28: int delim2 = EOF; /* End embedd eqn */ ! 29: int skipcnt; /* Number of lines to skip */ ! 30: int skiptitle; /* Skip title text (until next nroff command) */ ! 31: int sflag; /* Divide into sentences */ ! 32: int wflag; /* Divide output into words */ ! 33: int xflag; /* Extra knowledge of macros (style/diction) */ ! 34: ! 35: int ineqn; /* Inside embedded eqn escape */ ! 36: FILE *dopen(); ! 37: char *dgets(); ! 38: ! 39: main(argc, argv) ! 40: int argc; ! 41: char *argv[]; ! 42: { ! 43: register char *ap; ! 44: ! 45: while (argc>1 && *argv[1]=='-') { ! 46: for (ap = &argv[1][1]; *ap!='\0'; ap++) ! 47: switch (*ap) { ! 48: case 's': ! 49: sflag = 1; ! 50: break; ! 51: ! 52: case 'w': ! 53: wflag = 1; ! 54: break; ! 55: ! 56: case 'x': ! 57: xflag = 1; ! 58: break; ! 59: ! 60: default: ! 61: usage(); ! 62: } ! 63: argv++; ! 64: argc--; ! 65: } ! 66: if (argc < 2) ! 67: ofiles[0] = stdin; ! 68: flist = &argv[1]; ! 69: exit (deroff()); ! 70: } ! 71: ! 72: /* ! 73: * Read until end-of-file ! 74: * and process the special nroff/troff/eqn/tbl ! 75: * lines in the file. ! 76: */ ! 77: deroff() ! 78: { ! 79: ! 80: while (dgets(line) != NULL) { ! 81: if (!ineqn && line[0]=='.') { ! 82: nroff(line); ! 83: continue; ! 84: } ! 85: output(line); ! 86: } ! 87: } ! 88: ! 89: /* ! 90: * Output for that line which isn't an nroff ! 91: * control line. This has to look for embedded ! 92: * eqn stuff and back-slash troff/nroff escapes. ! 93: * The embedded escapes are all handled to some degree ! 94: * but such things as nested quotes (e.g. \w or \h) ! 95: * do not quite work. However, these occur almost never ! 96: * in text so it should be sufficient. ! 97: */ ! 98: output(l) ! 99: register char *l; ! 100: { ! 101: register int c; ! 102: register int inword = 0; ! 103: register int hyphen; ! 104: ! 105: if (skipcnt) { ! 106: skipcnt--; ! 107: return; ! 108: } ! 109: if (skiptitle) ! 110: return; ! 111: while ((c = *l++) != '\0') { ! 112: if (ineqn) { ! 113: if (c == delim2) ! 114: ineqn = 0; ! 115: continue; ! 116: } ! 117: if (c == delim1) { ! 118: ineqn = 1; ! 119: continue; ! 120: } ! 121: if (c == '\\') { ! 122: if ((c = *l++) == '\0') ! 123: break; ! 124: switch (c) { ! 125: case '0': /* digit width space */ ! 126: case '|': /* Narrow space */ ! 127: case '&': /* Non-printing, 0-width char */ ! 128: case '!': /* Transparent line indicator */ ! 129: case '%': /* Optional hyphenation char */ ! 130: case 't': /* Non-interpreted tab */ ! 131: case 'u': /* Up 1/2 */ ! 132: case 'd': /* Down 1/2 */ ! 133: case 'a': /* Non-interpeted leader */ ! 134: case 'c': /* Interrupt text processing */ ! 135: case 'p': /* Break and spread */ ! 136: case 'r': /* Rerverse vertical motion */ ! 137: case '{': /* Begin conditional */ ! 138: case '}': /* End conditional */ ! 139: c = ' '; ! 140: break; ! 141: ! 142: case 'e': /* Current escape */ ! 143: c = '\\'; ! 144: break; ! 145: case '$': /* argument */ ! 146: if (*l != '\0') ! 147: l++; ! 148: case '^': /* Half narrow space */ ! 149: continue; ! 150: ! 151: case '(': /* Char named `xx' */ ! 152: if (*l != '\0') ! 153: l++; ! 154: if (*l != '\0') ! 155: l++; ! 156: c = ' '; ! 157: break; ! 158: ! 159: case 'z': /* Zero-width character */ ! 160: if (*l != '\0') ! 161: c = *l++; ! 162: break; ! 163: ! 164: case 'k': /* Mark input place in `x' */ ! 165: case 'n': /* Expand reggister x */ ! 166: case '*': /* Interpolate string */ ! 167: case 'f': /* Change font */ ! 168: if (*l != '\0') ! 169: if ((c = *l++) == '(') { ! 170: if (*l != '\0') ! 171: l++; ! 172: if (*l != '\0') ! 173: l++; ! 174: } ! 175: continue; ! 176: ! 177: case 's': /* Change point size */ ! 178: if (*l == '\0') ! 179: continue; ! 180: if ((c = *l++)=='-' || c=='+') { ! 181: if (*l == '\0') ! 182: continue; ! 183: c = *l++; ! 184: } ! 185: while (*l!='\0' && isdigit(c)) ! 186: c = *l++; ! 187: break; ! 188: ! 189: case 'x': /* Extra line space */ ! 190: case 'w': /* Width function */ ! 191: case 'v': /* Local vertical motion */ ! 192: case 'o': /* Overstrike function */ ! 193: case 'L': /* Vertical line */ ! 194: case 'l': /* Horizontal line */ ! 195: case 'h': /* Local horizontal motion */ ! 196: case 'b': /* Bracket-builder */ ! 197: if ((c = *l) != '\0') ! 198: while (*l!='\0' && *l!=c) ! 199: l++; ! 200: continue; ! 201: ! 202: case '"': /* Beginning of comment */ ! 203: while (*l != '\0') ! 204: l++; ! 205: continue; ! 206: } ! 207: } ! 208: if (wflag) { ! 209: if (c == '\n') ! 210: continue; ! 211: if (!inword) ! 212: if (isalpha(c)) ! 213: inword = 1; ! 214: else ! 215: continue; ! 216: if (c=='-' && !hyphen) { ! 217: hyphen = 1; ! 218: continue; ! 219: } ! 220: hyphen = 0; ! 221: if (c == '\'') ! 222: continue; ! 223: if (!isalpha(c) && !isdigit(c)) { ! 224: inword = 0; ! 225: putchar('\n'); ! 226: continue; ! 227: } ! 228: } ! 229: putchar(c); ! 230: } ! 231: if (wflag && inword && !hyphen) ! 232: putchar('\n'); ! 233: } ! 234: ! 235: /* ! 236: * Process nroff control lines. ! 237: * Remove EQN, TBL, macro defintions. ! 238: * Process .so and .nx here. ! 239: * Other lines have the rest of the line used. ! 240: */ ! 241: nroff(l) ! 242: register char *l; ! 243: { ! 244: skiptitle = 0; ! 245: if (l[1]=='E' && l[2]=='Q') ! 246: eqn(); ! 247: else if (l[1]=='T' && l[2]=='S') ! 248: tbl(); ! 249: else if (l[1]=='F' && l[2]=='S') ! 250: footnote(); ! 251: else if (l[1]=='c' && l[2]=='e') ! 252: centre(l); ! 253: else if (l[1]=='n' && l[2]=='f') ! 254: nofill(); ! 255: else if (l[1]=='D' && l[2]=='S') ! 256: display(); ! 257: else if (l[1]=='K' && (l[2]=='F' || l[2]=='S')) ! 258: display(); ! 259: else if (l[1]=='T' && l[2]=='L') ! 260: titles(); ! 261: else if (l[1]=='A' && (l[2]=='I' || l[2]=='U')) ! 262: titles(); ! 263: else if (l[2]=='H' && (l[1]=='S' || l[1]=='N')) ! 264: titles(); ! 265: else if (l[1]=='n' && l[2]=='x') ! 266: include(line+3, 'n'); ! 267: else if (l[1]=='s' && l[2]=='o') ! 268: include(line+3, 's'); ! 269: else if (l[1]=='d' && l[2]=='e') ! 270: macdef(); ! 271: else if (l[1]=='d' && l[2]=='s') ! 272: return; ! 273: else { ! 274: while (*l!=' ' && *l!='\t' && *l!='\0') ! 275: l++; ! 276: while (*l==' ' || *l=='\t') ! 277: l++; ! 278: if (*l != '\0') ! 279: output(l); ! 280: } ! 281: } ! 282: ! 283: /* ! 284: * Process included files. ! 285: * The first argument is the pointer to where ! 286: * the filename is (it may have junk before and after it) ! 287: * The second is 's' for .so and 'n' for .nx. ! 288: */ ! 289: include(fn, type) ! 290: register char *fn; ! 291: char type; ! 292: { ! 293: register int c; ! 294: register char *ep; ! 295: ! 296: while (*fn==' ' || *fn=='\t') ! 297: fn++; ! 298: for (ep = fn; (c = *ep)!='\0'; ep++) ! 299: if (c=='\n' || c==' ' || c=='\t' || c=='\\') ! 300: break; ! 301: *ep = '\0'; ! 302: if (type == 's') ! 303: dotso(fn); ! 304: else ! 305: *ofpp = dopen(fn); ! 306: } ! 307: ! 308: /* ! 309: * Process eqn directives. ! 310: * Currently, this simply looks for ! 311: * .EN lines as the terminator ! 312: * and delim lines to set the eqn delimiters. ! 313: */ ! 314: eqn() ! 315: { ! 316: register char *cp; ! 317: ! 318: while (dgets(line) != NULL) { ! 319: if (strncmp(line, ".EN", 3) == 0) ! 320: break; ! 321: if (strncmp(line, "delim", 5) == 0) { ! 322: for (cp = line+5; *cp==' ' || *cp=='\t'; cp++) ! 323: ; ! 324: if (*cp=='\n' || *cp=='\0') ! 325: continue; ! 326: if (strncmp(cp, "off", 3) == 0) { ! 327: delim1 = EOF; ! 328: delim2 = EOF; ! 329: continue; ! 330: } ! 331: delim1 = *cp++; ! 332: delim2 = *cp; ! 333: } ! 334: } ! 335: } ! 336: ! 337: /* ! 338: * Process tbl directives. At this time, ! 339: * all this does is look for the terminating ! 340: * .TE to end tables. ! 341: */ ! 342: tbl() ! 343: { ! 344: while (dgets(line) != NULL) ! 345: if (strncmp(line, ".TE", 3) == 0) ! 346: break; ! 347: } ! 348: ! 349: /* ! 350: * In extended knowledge mode (-ms macros), ! 351: * remove footnotes. This mode is for ! 352: * style and diction. ! 353: */ ! 354: footnote() ! 355: { ! 356: if (!xflag) ! 357: return; ! 358: while (dgets(line) != NULL) ! 359: if (strncmp(line, ".FE", 3) == 0) ! 360: break; ! 361: } ! 362: ! 363: /* ! 364: * Throw away nofilled text as with footnotes above. ! 365: */ ! 366: nofill() ! 367: { ! 368: if (!xflag) ! 369: return; ! 370: while (dgets(line) != NULL) ! 371: if (strncmp(line, ".fi", 3) == 0) ! 372: break; ! 373: } ! 374: ! 375: /* ! 376: * Skip centred lines, in extended mode, ! 377: * by setting a skip counter on text. ! 378: */ ! 379: centre(l) ! 380: char *l; ! 381: { ! 382: if ((skipcnt = atoi(l)) == 0) ! 383: skipcnt = 1; ! 384: } ! 385: ! 386: /* ! 387: * Skip displays in extended mode. ! 388: */ ! 389: display() ! 390: { ! 391: if (!xflag) ! 392: return; ! 393: while (dgets(line) != NULL) { ! 394: if (strncmp(line, ".KE", 3) == 0) ! 395: break; ! 396: if (strncmp(line, ".DE", 3) == 0) ! 397: break; ! 398: } ! 399: } ! 400: ! 401: /* ! 402: * If in extended mode, skip titles and author's ! 403: * names. Set a flag to skip until next nroff command. ! 404: */ ! 405: titles() ! 406: { ! 407: if (xflag) ! 408: skiptitle = 1; ! 409: } ! 410: ! 411: /* ! 412: * Remove a macro defintion. ! 413: */ ! 414: macdef() ! 415: { ! 416: while (dgets(line) != NULL) ! 417: if (strcmp(line, "..\n") == 0) ! 418: break; ! 419: } ! 420: ! 421: /* ! 422: * Get a character from the next file stream. ! 423: */ ! 424: dgetc() ! 425: { ! 426: register int c; ! 427: ! 428: again: ! 429: if (*ofpp==NULL || (c = getc(*ofpp))==EOF) { ! 430: if (*ofpp!=stdin && *ofpp!=NULL) ! 431: fclose(*ofpp); ! 432: if (ofpp > ofiles) { ! 433: ofpp--; ! 434: goto again; ! 435: } ! 436: while (*flist != NULL) ! 437: if ((*ofpp = dopen(*flist++)) != NULL) ! 438: goto again; ! 439: return (EOF); ! 440: } ! 441: return (c); ! 442: } ! 443: ! 444: /* ! 445: * Like fgets, only always reads using `dgetc' ! 446: * into a buffer of `NLINE' characters. ! 447: */ ! 448: char * ! 449: dgets(as) ! 450: char *as; ! 451: { ! 452: register unsigned n = NLINE; ! 453: register char *s; ! 454: register int c; ! 455: ! 456: s = as; ! 457: while (--n>0 && (c = dgetc())!=EOF) ! 458: if ((*s++ = c) == '\n') ! 459: break; ! 460: *s = '\0'; ! 461: return (c==EOF && s==as ? NULL : as); ! 462: } ! 463: ! 464: /* ! 465: * Open input files (for .so, .nx, and from ! 466: * command line). Do not open any files twice. ! 467: */ ! 468: FILE * ! 469: dopen(fname) ! 470: register char *fname; ! 471: { ! 472: register FNAME *fnp; ! 473: register FILE *fp; ! 474: ! 475: for (fnp = fnames; fnp != NULL; fnp = fnp->fn_next) ! 476: if (strcmp(fnp->fn_name, fname) == 0) ! 477: return (NULL); ! 478: if ((fp = fopen(fname, "r")) == NULL) ! 479: fprintf(stderr, "deroff: cannot open `%s'\n", fname); ! 480: else if ((fnp=(FNAME *)malloc(sizeof(FNAME)+strlen(fname)+2)) != NULL) { ! 481: fnp->fn_next = fnames; ! 482: fnames = fnp; ! 483: strcpy(fnp->fn_name, fname); ! 484: } ! 485: return (fp); ! 486: } ! 487: ! 488: /* ! 489: * Include a file as per the `.so' request ! 490: * line. ! 491: */ ! 492: dotso(fname) ! 493: char *fname; ! 494: { ! 495: if (++ofpp >= &ofiles[NFNEST]) { ! 496: fprintf(stderr, "deroff: .so nested too deep--%s\n", fname); ! 497: ofpp--; ! 498: return; ! 499: } ! 500: if ((*ofpp = dopen(fname)) == NULL) ! 501: ofpp--; ! 502: } ! 503: ! 504: usage() ! 505: { ! 506: fprintf(stderr, "Usage: deroff [ -w ] [ -x ] [file ...]\n"); ! 507: exit(1); ! 508: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.