Research V7 development
[unix-history] / usr / src / cmd / deroff.c
CommitLineData
4df08032
LC
1char *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
7a file. Has one flag argument, -w, to cause output one word per line
8rather than in the original format.
9Deroff follows .so and .nx commands, removes contents of macro
10definitions, equations (both .EQ ... .EN and $...$),
11Tbl command sequences, and Troff backslash constructions.
12
13All 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
29int wordflag = NO;
30int inmacro = NO;
31int intable = NO;
32
33char chars[128]; /* SPECIAL, APOS, DIGIT, or LETTER */
34
35char line[512];
36char *lp;
37
38int c;
39int ldelim = NOCHAR;
40int rdelim = NOCHAR;
41
42
43int argc;
44char **argv;
45
46char fname[50];
47FILE *files[15];
48FILE **filesp;
49FILE *infile;
50
51char *calloc();
52
53
54
55main(ac, av)
56int ac;
57char **av;
58{
59register int i;
60register char *p;
61static char onechar[2] = "X";
62FILE *opn();
63
64argc = ac - 1;
65argv = av + 1;
66
67while(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
82if(argc == 0)
83 infile = stdin;
84else {
85 infile = opn(argv[0]);
86 --argc;
87 ++argv;
88 }
89
90files[0] = infile;
91filesp = &files[0];
92
93for(i='a'; i<='z' ; ++i)
94 chars[i] = LETTER;
95for(i='A'; i<='Z'; ++i)
96 chars[i] = LETTER;
97for(i='0'; i<='9'; ++i)
98 chars[i] = DIGIT;
99chars['\''] = APOS;
100chars['&'] = APOS;
101
102work();
103}
104
105
106
107skeqn()
108{
109while((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();
119return(c = ' ');
120}
121
122
123FILE *opn(p)
124register char *p;
125{
126FILE *fd;
127
128if(p[0]=='-' && p[1]=='\0')
129 fd = stdin;
130else if( (fd = fopen(p, "r")) == NULL)
131 fatal("Cannot open file %s\n", p);
132
133return(fd);
134}
135
136
137
138eof()
139{
140if(infile != stdin)
141 fclose(infile);
142if(filesp > files)
143 infile = *--filesp;
144else if(argc > 0)
145 {
146 infile = opn(argv[0]);
147 --argc;
148 ++argv;
149 }
150else
151 exit(0);
152
153return(C);
154}
155
156
157
158getfname()
159{
160register char *p;
161struct chain { struct chain *nextp; char *datap; } *chainblock;
162register struct chain *q;
163static struct chain *namechain = NULL;
164char *copys();
165
166while(C == ' ') ;
167
168for(p = fname ; (*p=c)!= '\n' && c!=' ' && c!='\t' && c!='\\' ; ++p)
169 C;
170*p = '\0';
171while(c != '\n')
172 C;
173
174/* see if this name has already been used */
175
176for(q = namechain ; q; q = q->nextp)
177 if( ! strcmp(fname, q->datap))
178 {
179 fname[0] = '\0';
180 return;
181 }
182
183q = (struct chain *) calloc(1, sizeof(*chainblock));
184q->nextp = namechain;
185q->datap = copys(fname);
186namechain = q;
187}
188
189
190
191
192fatal(s,p)
193char *s, *p;
194{
195fprintf(stderr, "Deroff: ");
196fprintf(stderr, s, p);
197exit(1);
198}
199\f
200work()
201{
202
203for( ;; )
204 {
205 if(C == '.' || c == '\'')
206 comline();
207 else
208 regline(NO);
209 }
210}
211
212
213
214
215regline(macline)
216int macline;
217{
218line[0] = c;
219lp = line;
220for( ; ; )
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
242if(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
254putmac(s)
255register char *s;
256{
257register char *t;
258
259while(*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 }
271putchar('\n');
272}
273
274
275
276putwords(macline) /* break into words for -w option */
277int macline;
278{
279register char *p, *p1;
280int i, nlet;
281
282
283for(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
308comline()
309{
310register int c1, c2;
311
312while(C==' ' || c=='\t')
313 ;
314if( (c1=c) == '\n')
315 return;
316c2 = C;
317if(c1=='.' && c2!='.')
318 inmacro = NO;
319if(c2 == '\n')
320 return;
321
322if(c1=='E' && c2=='Q' && filesp==files)
323 eqn();
324else if(c1=='T' && (c2=='S' || c2=='C' || c2=='&') && filesp==files)
325 tbl();
326else if(c1=='T' && c2=='E')
327 intable = NO;
328else if(!inmacro && c1=='d' && c2=='e')
329 macro();
330else if(!inmacro && c1=='i' && c2=='g')
331 macro();
332else if(!inmacro && c1=='a' && c2 == 'm')
333 macro();
334else if(c1=='s' && c2=='o')
335 {
336 getfname();
337 if( fname[0] )
338 infile = *++filesp = opn( fname );
339 }
340else if(c1=='n' && c2=='x')
341 {
342 getfname();
343 if(fname[0] == '\0') exit(0);
344 if(infile != stdin)
345 fclose(infile);
346 infile = *filesp = opn(fname);
347 }
348else if(c1=='h' && c2=='w')
349 { SKIP; }
350else
351 {
352 if(c1=='.' && c2=='.')
353 while(C == '.')
354 ;
355 ++inmacro;
356 regline(YES);
357 --inmacro;
358 }
359}
360
361
362
363macro()
364{
365/*
366do { SKIP; }
367 while(C!='.' || C!='.' || C=='.'); /* look for .. */
368SKIP;
369inmacro = YES;
370}
371
372
373
374
375tbl()
376{
377while(C != '.');
378SKIP;
379intable = YES;
380}
381
382eqn()
383{
384register int c1, c2;
385
386SKIP;
387
388for( ;;)
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
425backsl() /* skip over a complete backslash construction */
426{
427int bdelim;
428
429sw: 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
483char *copys(s)
484register char *s;
485{
486register char *t, *t0;
487
488if( (t0 = t = calloc( strlen(s)+1, sizeof(*t) ) ) == NULL)
489 fatal("Cannot allocate memory", (char *) NULL);
490
491while( *t++ = *s++ )
492 ;
493return(t0);
494}