|
|
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.