Commit | Line | Data |
---|---|---|
dfa1d33f BJ |
1 | char *xxxvers = "\nDeroff Version 1.02 24 July 1978\n"; |
2 | ||
3 | ||
4 | #include <stdio.h> | |
5 | ||
6 | /* Deroff command -- strip troff, eqn, and Tbl sequences from | |
7 | a file. Has one flag argument, -w, to cause output one word per line | |
8 | rather than in the original format. | |
9 | Deroff follows .so and .nx commands, removes contents of macro | |
10 | definitions, equations (both .EQ ... .EN and $...$), | |
11 | Tbl command sequences, and Troff backslash constructions. | |
12 | ||
13 | All input is through the C macro; the most recently read character is in c. | |
14 | */ | |
15 | ||
16 | #define C ( (c=getc(infile)) == EOF ? eof() : ((c==ldelim)&&(filesp==files) ? skeqn() : c) ) | |
17 | #define C1 ( (c=getc(infile)) == EOF ? eof() : c) | |
18 | #define SKIP while(C != '\n') | |
19 | ||
20 | #define YES 1 | |
21 | #define NO 0 | |
22 | ||
23 | #define NOCHAR -2 | |
24 | #define SPECIAL 0 | |
25 | #define APOS 1 | |
26 | #define DIGIT 2 | |
27 | #define LETTER 3 | |
28 | ||
29 | int wordflag = NO; | |
30 | int inmacro = NO; | |
31 | int intable = NO; | |
32 | ||
33 | char chars[128]; /* SPECIAL, APOS, DIGIT, or LETTER */ | |
34 | ||
35 | char line[BUFSIZ]; | |
36 | char *lp; | |
37 | ||
38 | int c; | |
39 | int ldelim = NOCHAR; | |
40 | int rdelim = NOCHAR; | |
41 | ||
42 | ||
43 | int argc; | |
44 | char **argv; | |
45 | ||
46 | char fname[50]; | |
47 | FILE *files[15]; | |
48 | FILE **filesp; | |
49 | FILE *infile; | |
50 | ||
51 | char *calloc(); | |
52 | ||
53 | ||
54 | ||
55 | main(ac, av) | |
56 | int ac; | |
57 | char **av; | |
58 | { | |
59 | register int i; | |
60 | register char *p; | |
61 | static char onechar[2] = "X"; | |
62 | FILE *opn(); | |
63 | ||
64 | argc = ac - 1; | |
65 | argv = av + 1; | |
66 | ||
67 | while(argc>0 && argv[0][0]=='-' && argv[0][1]!='\0') | |
68 | { | |
69 | for(p=argv[0]+1; *p; ++p) switch(*p) | |
70 | { | |
71 | case 'w': | |
72 | wordflag = YES; | |
73 | break; | |
74 | default: | |
75 | onechar[0] = *p; | |
76 | fatal("Invalid flag %s\n", onechar); | |
77 | } | |
78 | --argc; | |
79 | ++argv; | |
80 | } | |
81 | ||
82 | if(argc == 0) | |
83 | infile = stdin; | |
84 | else { | |
85 | infile = opn(argv[0]); | |
86 | --argc; | |
87 | ++argv; | |
88 | } | |
89 | ||
90 | files[0] = infile; | |
91 | filesp = &files[0]; | |
92 | ||
93 | for(i='a'; i<='z' ; ++i) | |
94 | chars[i] = LETTER; | |
95 | for(i='A'; i<='Z'; ++i) | |
96 | chars[i] = LETTER; | |
97 | for(i='0'; i<='9'; ++i) | |
98 | chars[i] = DIGIT; | |
99 | chars['\''] = APOS; | |
100 | chars['&'] = APOS; | |
101 | ||
102 | work(); | |
103 | } | |
104 | ||
105 | ||
106 | ||
107 | skeqn() | |
108 | { | |
109 | while((c = getc(infile)) != rdelim) | |
110 | if(c == EOF) | |
111 | c = eof(); | |
112 | else if(c == '"') | |
113 | while( (c = getc(infile)) != '"') | |
114 | if(c == EOF) | |
115 | c = eof(); | |
116 | else if(c == '\\') | |
117 | if((c = getc(infile)) == EOF) | |
118 | c = eof(); | |
119 | return(C); | |
120 | } | |
121 | ||
122 | ||
123 | FILE *opn(p) | |
124 | register char *p; | |
125 | { | |
126 | FILE *fd; | |
127 | ||
128 | if(p[0]=='-' && p[1]=='\0') | |
129 | fd = stdin; | |
130 | else if( (fd = fopen(p, "r")) == NULL) | |
131 | fatal("Cannot open file %s\n", p); | |
132 | ||
133 | return(fd); | |
134 | } | |
135 | ||
136 | ||
137 | ||
138 | eof() | |
139 | { | |
140 | if(infile != stdin) | |
141 | fclose(infile); | |
142 | if(filesp > files) | |
143 | infile = *--filesp; | |
144 | else if(argc > 0) | |
145 | { | |
146 | infile = opn(argv[0]); | |
147 | --argc; | |
148 | ++argv; | |
149 | } | |
150 | else | |
151 | exit(0); | |
152 | ||
153 | return(C); | |
154 | } | |
155 | ||
156 | ||
157 | ||
158 | getfname() | |
159 | { | |
160 | register char *p; | |
161 | struct chain { struct chain *nextp; char *datap; } *chainblock; | |
162 | register struct chain *q; | |
163 | static struct chain *namechain = NULL; | |
164 | char *copys(); | |
165 | ||
166 | while(C == ' ') ; | |
167 | ||
168 | for(p = fname ; (*p=c)!= '\n' && c!=' ' && c!='\t' && c!='\\' ; ++p) | |
169 | C; | |
170 | *p = '\0'; | |
171 | while(c != '\n') | |
172 | C; | |
173 | ||
174 | /* see if this name has already been used */ | |
175 | ||
176 | for(q = namechain ; q; q = q->nextp) | |
177 | if( ! strcmp(fname, q->datap)) | |
178 | { | |
179 | fname[0] = '\0'; | |
180 | return; | |
181 | } | |
182 | ||
183 | q = (struct chain *) calloc(1, sizeof(*chainblock)); | |
184 | q->nextp = namechain; | |
185 | q->datap = copys(fname); | |
186 | namechain = q; | |
187 | } | |
188 | ||
189 | ||
190 | ||
191 | ||
192 | fatal(s,p) | |
193 | char *s, *p; | |
194 | { | |
195 | fprintf(stderr, "Deroff: "); | |
196 | fprintf(stderr, s, p); | |
197 | exit(1); | |
198 | } | |
199 | \f | |
200 | work() | |
201 | { | |
202 | ||
203 | for( ;; ) | |
204 | { | |
205 | if(C == '.' || c == '\'') | |
206 | comline(); | |
207 | else | |
208 | regline(NO); | |
209 | } | |
210 | } | |
211 | ||
212 | ||
213 | ||
214 | ||
215 | regline(macline) | |
216 | int macline; | |
217 | { | |
218 | line[0] = c; | |
219 | lp = line; | |
220 | for( ; ; ) | |
221 | { | |
222 | if(c == '\\') | |
223 | { | |
224 | *lp = ' '; | |
225 | backsl(); | |
226 | } | |
227 | if(c == '\n') break; | |
228 | if(intable && c=='T') | |
229 | { | |
230 | *++lp = C; | |
231 | if(c=='{' || c=='}') | |
232 | { | |
233 | lp[-1] = ' '; | |
234 | *lp = C; | |
235 | } | |
236 | } | |
237 | else *++lp = C; | |
238 | } | |
239 | ||
240 | *lp = '\0'; | |
241 | ||
242 | if(line[0] != '\0') | |
243 | if(wordflag) | |
244 | putwords(macline); | |
245 | else if(macline) | |
246 | putmac(line); | |
247 | else | |
248 | puts(line); | |
249 | } | |
250 | ||
251 | ||
252 | ||
253 | ||
254 | putmac(s) | |
255 | register char *s; | |
256 | { | |
257 | register char *t; | |
258 | ||
259 | while(*s) | |
260 | { | |
261 | while(*s==' ' || *s=='\t') | |
262 | putchar(*s++); | |
263 | for(t = s ; *t!=' ' && *t!='\t' && *t!='\0' ; ++t) | |
264 | ; | |
265 | if(t>s+2 && chars[ s[0] ]==LETTER && chars[ s[1] ]==LETTER) | |
266 | while(s < t) | |
267 | putchar(*s++); | |
268 | else | |
269 | s = t; | |
270 | } | |
271 | putchar('\n'); | |
272 | } | |
273 | ||
274 | ||
275 | ||
276 | putwords(macline) /* break into words for -w option */ | |
277 | int macline; | |
278 | { | |
279 | register char *p, *p1; | |
280 | int i, nlet; | |
281 | ||
282 | ||
283 | for(p1 = line ; ;) | |
284 | { | |
285 | /* skip initial specials ampersands and apostrophes */ | |
286 | while( chars[*p1] < DIGIT) | |
287 | if(*p1++ == '\0') return; | |
288 | nlet = 0; | |
289 | for(p = p1 ; (i=chars[*p]) != SPECIAL ; ++p) | |
290 | if(i == LETTER) ++nlet; | |
291 | ||
292 | if( (!macline && nlet>1) /* MDM definition of word */ | |
293 | || (macline && nlet>2 && chars[ p1[0] ]==LETTER && chars[ p1[1] ]==LETTER) ) | |
294 | { | |
295 | /* delete trailing ampersands and apostrophes */ | |
296 | while(p[-1]=='\'' || p[-1]=='&') | |
297 | --p; | |
298 | while(p1 < p) putchar(*p1++); | |
299 | putchar('\n'); | |
300 | } | |
301 | else | |
302 | p1 = p; | |
303 | } | |
304 | } | |
305 | ||
306 | \f | |
307 | ||
308 | comline() | |
309 | { | |
310 | register int c1, c2; | |
311 | ||
312 | while(C==' ' || c=='\t') | |
313 | ; | |
314 | if( (c1=c) == '\n') | |
315 | return; | |
316 | if(c1 == '.') | |
317 | { | |
318 | inmacro = NO; | |
319 | SKIP; | |
320 | return; | |
321 | } | |
322 | if( (c2=C) == '\n') | |
323 | return; | |
324 | ||
325 | if(c1=='E' && c2=='Q' && filesp==files) | |
326 | eqn(); | |
327 | else if(c1=='T' && (c2=='S' || c2=='C' || c2=='&') && filesp==files) | |
328 | tbl(); | |
329 | else if(c1=='T' && c2=='E') | |
330 | intable = NO; | |
331 | else if(!inmacro && c1=='d' && c2=='e') | |
332 | macro(); | |
333 | else if(!inmacro && c1=='i' && c2=='g') | |
334 | macro(); | |
335 | else if(!inmacro && c1=='a' && c2 == 'm') | |
336 | macro(); | |
337 | else if(c1=='s' && c2=='o') | |
338 | { | |
339 | getfname(); | |
340 | if( fname[0] ) | |
341 | infile = *++filesp = opn( fname ); | |
342 | } | |
343 | else if(c1=='n' && c2=='x') | |
344 | { | |
345 | getfname(); | |
346 | if(fname[0] == '\0') exit(0); | |
347 | if(infile != stdin) | |
348 | fclose(infile); | |
349 | infile = *filesp = opn(fname); | |
350 | } | |
351 | else if(c1=='h' && c2=='w') | |
352 | { SKIP; } | |
353 | else | |
354 | { | |
355 | ++inmacro; | |
356 | regline(YES); | |
357 | --inmacro; | |
358 | } | |
359 | } | |
360 | ||
361 | ||
362 | ||
363 | macro() | |
364 | { | |
365 | /* | |
366 | do { SKIP; } | |
367 | while(C!='.' || C!='.'); /* look for .EN */ | |
368 | SKIP; | |
369 | inmacro = YES; | |
370 | } | |
371 | ||
372 | ||
373 | ||
374 | ||
375 | tbl() | |
376 | { | |
377 | while(C != '.'); | |
378 | SKIP; | |
379 | intable = YES; | |
380 | } | |
381 | ||
382 | eqn() | |
383 | { | |
384 | register int c1, c2; | |
385 | ||
386 | SKIP; | |
387 | ||
388 | for( ;;) | |
389 | { | |
390 | if(C == '.' || c == '\'') | |
391 | { | |
392 | while(C==' ' || c=='\t') | |
393 | ; | |
394 | if(c=='E' && C=='N') | |
395 | { | |
396 | SKIP; | |
397 | return; | |
398 | } | |
399 | } | |
400 | else if(c == 'd') /* look for delim */ | |
401 | { | |
402 | if(C=='e' && C=='l') | |
403 | if( C=='i' && C=='m') | |
404 | { | |
405 | while(C1 == ' '); | |
406 | if((c1=c)=='\n' || (c2=C1)=='\n' | |
407 | || (c1=='o' && c2=='f' && C1=='f') ) | |
408 | { | |
409 | ldelim = NOCHAR; | |
410 | rdelim = NOCHAR; | |
411 | } | |
412 | else { | |
413 | ldelim = c1; | |
414 | rdelim = c2; | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
419 | if(c != '\n') SKIP; | |
420 | } | |
421 | } | |
422 | ||
423 | \f | |
424 | ||
425 | backsl() /* skip over a complete backslash construction */ | |
426 | { | |
427 | int bdelim; | |
428 | ||
429 | sw: switch(C) | |
430 | { | |
431 | case '"': | |
432 | SKIP; | |
433 | return; | |
434 | case 's': | |
435 | if(C == '\\') backsl(); | |
436 | else { | |
437 | while(C>='0' && c<='9') ; | |
438 | ungetc(c,infile); | |
439 | c = '0'; | |
440 | } | |
441 | --lp; | |
442 | return; | |
443 | ||
444 | case 'f': | |
445 | case 'n': | |
446 | case '*': | |
447 | if(C != '(') | |
448 | return; | |
449 | ||
450 | case '(': | |
451 | if(C != '\n') C; | |
452 | return; | |
453 | ||
454 | case '$': | |
455 | C; /* discard argument number */ | |
456 | return; | |
457 | ||
458 | case 'b': | |
459 | case 'x': | |
460 | case 'v': | |
461 | case 'h': | |
462 | case 'w': | |
463 | case 'o': | |
464 | case 'l': | |
465 | case 'L': | |
466 | if( (bdelim=C) == '\n') | |
467 | return; | |
468 | while(C!='\n' && c!=bdelim) | |
469 | if(c == '\\') backsl(); | |
470 | return; | |
471 | ||
472 | case '\\': | |
473 | if(inmacro) | |
474 | goto sw; | |
475 | default: | |
476 | return; | |
477 | } | |
478 | } | |
479 | ||
480 | ||
481 | ||
482 | ||
483 | char *copys(s) | |
484 | register char *s; | |
485 | { | |
486 | register char *t, *t0; | |
487 | ||
488 | if( (t0 = t = calloc( strlen(s)+1, sizeof(*t) ) ) == NULL) | |
489 | fatal("Cannot allocate memory", (char *) NULL); | |
490 | ||
491 | while( *t++ = *s++ ) | |
492 | ; | |
493 | return(t0); | |
494 | } |