386BSD 0.1 development
[unix-history] / usr / src / usr.bin / groff / pic / main.cc
CommitLineData
e79d4cdd
WJ
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 1, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file LICENSE. If not, write to the Free Software
19Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include "pic.h"
22
23extern int yyparse();
24
25output *out;
26
27int flyback_flag;
28int zero_length_line_flag = 0;
29int driver_extension_flag = 0;
30int compatible_flag = 0;
31int command_char = '.'; // the character that introduces lines
32 // that should be passed through tranparently
33static int lf_flag = 1; // non-zero if we should attempt to understand
34 // lines beginning with `.lf'
35
36void do_file(const char *filename);
37
38class top_input : public input {
39 FILE *fp;
40 int bol;
41 int eof;
42 int push_back[3];
43 int start_lineno;
44public:
45 top_input(FILE *);
46 int get();
47 int peek();
48 int get_location(const char **, int *);
49};
50
51top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
52{
53 push_back[0] = push_back[1] = push_back[2] = EOF;
54 start_lineno = current_lineno;
55}
56
57int top_input::get()
58{
59 if (eof)
60 return EOF;
61 if (push_back[2] != EOF) {
62 int c = push_back[2];
63 push_back[2] = EOF;
64 return c;
65 }
66 else if (push_back[1] != EOF) {
67 int c = push_back[1];
68 push_back[1] = EOF;
69 return c;
70 }
71 else if (push_back[0] != EOF) {
72 int c = push_back[0];
73 push_back[0] = EOF;
74 return c;
75 }
76 int c = getc(fp);
77 while (illegal_input_char(c)) {
78 error("illegal input character code %1", int(c));
79 c = getc(fp);
80 bol = 0;
81 }
82 if (bol && c == '.') {
83 c = getc(fp);
84 if (c == 'P') {
85 c = getc(fp);
86 if (c == 'F' || c == 'E') {
87 int d = getc(fp);
88 if (d != EOF)
89 ungetc(d, fp);
90 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
91 eof = 1;
92 flyback_flag = c == 'F';
93 return EOF;
94 }
95 push_back[0] = c;
96 push_back[1] = 'P';
97 return '.';
98 }
99 if (c == 'S') {
100 c = getc(fp);
101 if (c != EOF)
102 ungetc(c, fp);
103 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
104 error("nested .PS");
105 eof = 1;
106 return EOF;
107 }
108 push_back[0] = 'S';
109 push_back[1] = 'P';
110 return '.';
111 }
112 if (c != EOF)
113 ungetc(c, fp);
114 push_back[0] = 'P';
115 return '.';
116 }
117 else {
118 if (c != EOF)
119 ungetc(c, fp);
120 return '.';
121 }
122 }
123 if (c == '\n') {
124 bol = 1;
125 current_lineno++;
126 return '\n';
127 }
128 bol = 0;
129 if (c == EOF) {
130 eof = 1;
131 error("end of file before .PE or .PF");
132 error_with_file_and_line(current_filename, start_lineno - 1,
133 ".PS was here");
134 }
135 return c;
136}
137
138int top_input::peek()
139{
140 if (eof)
141 return EOF;
142 if (push_back[2] != EOF)
143 return push_back[2];
144 if (push_back[1] != EOF)
145 return push_back[1];
146 if (push_back[0] != EOF)
147 return push_back[0];
148 int c = getc(fp);
149 while (illegal_input_char(c)) {
150 error("illegal input character code %1", int(c));
151 c = getc(fp);
152 bol = 0;
153 }
154 if (bol && c == '.') {
155 c = getc(fp);
156 if (c == 'P') {
157 c = getc(fp);
158 if (c == 'F' || c == 'E') {
159 int d = getc(fp);
160 if (d != EOF)
161 ungetc(d, fp);
162 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
163 eof = 1;
164 flyback_flag = c == 'F';
165 return EOF;
166 }
167 push_back[0] = c;
168 push_back[1] = 'P';
169 push_back[2] = '.';
170 return '.';
171 }
172 if (c == 'S') {
173 c = getc(fp);
174 if (c != EOF)
175 ungetc(c, fp);
176 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
177 error("nested .PS");
178 eof = 1;
179 return EOF;
180 }
181 push_back[0] = 'S';
182 push_back[1] = 'P';
183 push_back[2] = '.';
184 return '.';
185 }
186 if (c != EOF)
187 ungetc(c, fp);
188 push_back[0] = 'P';
189 push_back[1] = '.';
190 return '.';
191 }
192 else {
193 if (c != EOF)
194 ungetc(c, fp);
195 push_back[0] = '.';
196 return '.';
197 }
198 }
199 if (c != EOF)
200 ungetc(c, fp);
201 if (c == '\n')
202 return '\n';
203 return c;
204}
205
206int top_input::get_location(const char **filenamep, int *linenop)
207{
208 *filenamep = current_filename;
209 *linenop = current_lineno;
210 return 1;
211}
212
213void do_picture(FILE *fp)
214{
215 flyback_flag = 0;
216 int c;
217 while ((c = getc(fp)) == ' ')
218 ;
219 if (c == '<') {
220 string filename;
221 while ((c = getc(fp)) != EOF) {
222 if (c == '\n') {
223 current_lineno++;
224 break;
225 }
226 filename += char(c);
227 }
228 filename += '\0';
229 const char *old_filename = current_filename;
230 int old_lineno = current_lineno;
231 // filenames must be permanent
232 do_file(strsave(filename.contents()));
233 current_filename = old_filename;
234 current_lineno = old_lineno;
235 out->set_location(current_filename, current_lineno);
236 }
237 else {
238 out->set_location(current_filename, current_lineno);
239 string start_line;
240 while (c != EOF) {
241 if (c == '\n') {
242 current_lineno++;
243 break;
244 }
245 start_line += c;
246 c = getc(fp);
247 }
248 if (c == EOF)
249 return;
250 start_line += '\0';
251 double wid, ht;
252 switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
253 case 1:
254 ht = 0.0;
255 break;
256 case 2:
257 break;
258 default:
259 ht = wid = 0.0;
260 break;
261 }
262 out->set_desired_width_height(wid, ht);
263 out->set_args(start_line.contents());
264 lex_init(new top_input(fp));
265 if (yyparse())
266 lex_error("giving up on this picture");
267 parse_cleanup();
268 lex_cleanup();
269
270 // skip the rest of the .PF/.PE line
271 while ((c = getc(fp)) != EOF && c != '\n')
272 ;
273 if (c == '\n')
274 current_lineno++;
275 out->set_location(current_filename, current_lineno);
276 }
277}
278
279void do_file(const char *filename)
280{
281 FILE *fp;
282 if (strcmp(filename, "-") == 0)
283 fp = stdin;
284 else {
285 fp = fopen(filename, "r");
286 if (fp == 0)
287 fatal("can't open `%1': %2", filename, strerror(errno));
288 }
289 out->set_location(filename, 1);
290 current_filename = filename;
291 current_lineno = 1;
292 enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
293 for (;;) {
294 int c = getc(fp);
295 if (c == EOF)
296 break;
297 switch (state) {
298 case START:
299 if (c == '.')
300 state = HAD_DOT;
301 else {
302 putchar(c);
303 if (c == '\n') {
304 current_lineno++;
305 state = START;
306 }
307 else
308 state = MIDDLE;
309 }
310 break;
311 case MIDDLE:
312 putchar(c);
313 if (c == '\n') {
314 current_lineno++;
315 state = START;
316 }
317 break;
318 case HAD_DOT:
319 if (c == 'P')
320 state = HAD_P;
321 else if (lf_flag && c == 'l')
322 state = HAD_l;
323 else {
324 putchar('.');
325 putchar(c);
326 if (c == '\n') {
327 current_lineno++;
328 state = START;
329 }
330 else
331 state = MIDDLE;
332 }
333 break;
334 case HAD_P:
335 if (c == 'S')
336 state = HAD_PS;
337 else {
338 putchar('.');
339 putchar('P');
340 putchar(c);
341 if (c == '\n') {
342 current_lineno++;
343 state = START;
344 }
345 else
346 state = MIDDLE;
347 }
348 break;
349 case HAD_PS:
350 if (c == ' ' || c == '\n' || compatible_flag) {
351 ungetc(c, fp);
352 do_picture(fp);
353 state = START;
354 }
355 else {
356 fputs(".PS", stdout);
357 putchar(c);
358 state = MIDDLE;
359 }
360 break;
361 case HAD_l:
362 if (c == 'f')
363 state = HAD_lf;
364 else {
365 putchar('.');
366 putchar('l');
367 putchar(c);
368 if (c == '\n') {
369 current_lineno++;
370 state = START;
371 }
372 else
373 state = MIDDLE;
374 }
375 break;
376 case HAD_lf:
377 if (c == ' ' || c == '\n' || compatible_flag) {
378 string line;
379 while (c != EOF) {
380 line += c;
381 if (c == '\n') {
382 current_lineno++;
383 break;
384 }
385 c = getc(fp);
386 }
387 line += '\0';
388 interpret_lf_args(line.contents());
389 printf(".lf%s", line.contents());
390 state = START;
391 }
392 else {
393 fputs(".lf", stdout);
394 putchar(c);
395 state = MIDDLE;
396 }
397 break;
398 default:
399 assert(0);
400 }
401 }
402 switch (state) {
403 case START:
404 break;
405 case MIDDLE:
406 putchar('\n');
407 break;
408 case HAD_DOT:
409 fputs(".\n", stdout);
410 break;
411 case HAD_P:
412 fputs(".P\n", stdout);
413 break;
414 case HAD_PS:
415 fputs(".PS\n", stdout);
416 break;
417 case HAD_l:
418 fputs(".l\n", stdout);
419 break;
420 case HAD_lf:
421 fputs(".lf\n", stdout);
422 break;
423 }
424 if (fp != stdin)
425 fclose(fp);
426}
427
428#ifdef FIG_SUPPORT
429void do_whole_file(const char *filename)
430{
431 // Do not set current_filename.
432 FILE *fp;
433 if (strcmp(filename, "-") == 0)
434 fp = stdin;
435 else {
436 fp = fopen(filename, "r");
437 if (fp == 0)
438 fatal("can't open `%1': %2", filename, strerror(errno));
439 }
440 lex_init(new file_input(fp, filename));
441 yyparse();
442 parse_cleanup();
443 lex_cleanup();
444}
445#endif
446
447void usage()
448{
449 fprintf(stderr, "usage: %s [ -pxvzC ] [ filename ... ]\n", program_name);
450#ifdef TEX_SUPPORT
451 fprintf(stderr, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
452#endif
453#ifdef FIG_SUPPORT
454 fprintf(stderr, " %s -f [ -v ] [ filename ]\n", program_name);
455#endif
456 exit(1);
457}
458
459int main(int argc, char **argv)
460{
461 program_name = argv[0];
462 static char stderr_buf[BUFSIZ];
463 setbuf(stderr, stderr_buf);
464 int opt;
465#ifdef TEX_SUPPORT
466 int tex_flag = 0;
467 int tpic_flag = 0;
468#endif
469 int grops_flag = 0;
470#ifdef FIG_SUPPORT
471 int whole_file_flag = 0;
472 int fig_flag = 0;
473#endif
474 while ((opt = getopt(argc, argv, "T:CDtcvxzpf")) != EOF)
475 switch (opt) {
476 case 'C':
477 compatible_flag = 1;
478 break;
479 case 'D':
480 case 'T':
481 break;
482 case 'f':
483#ifdef FIG_SUPPORT
484 whole_file_flag++;
485 fig_flag++;
486#else
487 fatal("fig support not included");
488#endif
489 break;
490 case 'p':
491 grops_flag++;
492 break;
493 case 't':
494#ifdef TEX_SUPPORT
495 tex_flag++;
496#else
497 fatal("TeX support not included");
498#endif
499 break;
500 case 'c':
501#ifdef TEX_SUPPORT
502 tpic_flag++;
503#else
504 fatal("TeX support not included");
505#endif
506 break;
507 case 'v':
508 {
509 extern const char *version_string;
510 fprintf(stderr, "GNU pic version %s\n", version_string);
511 fflush(stderr);
512 break;
513 }
514 case 'x':
515 driver_extension_flag++;
516 /* fall through */
517 case 'z':
518 // zero length lines will be printed as dots
519 zero_length_line_flag++;
520 break;
521 case '?':
522 usage();
523 break;
524 default:
525 assert(0);
526 }
527 parse_init();
528 if (grops_flag)
529 out = make_grops_output();
530#ifdef TEX_SUPPORT
531 else if (tpic_flag) {
532 out = make_tpic_output();
533 lf_flag = 0;
534 }
535 else if (tex_flag) {
536 out = make_tex_output();
537 command_char = '\\';
538 lf_flag = 0;
539 }
540#endif
541#ifdef FIG_SUPPORT
542 else if (fig_flag)
543 out = make_fig_output();
544#endif
545 else
546 out = make_troff_output();
547#ifdef FIG_SUPPORT
548 if (whole_file_flag) {
549 if (optind >= argc)
550 do_whole_file("-");
551 else if (argc - optind > 1)
552 usage();
553 else
554 do_whole_file(argv[optind]);
555 }
556 else {
557#endif
558 if (optind >= argc)
559 do_file("-");
560 else
561 for (int i = optind; i < argc; i++)
562 do_file(argv[i]);
563#ifdef FIG_SUPPORT
564 }
565#endif
566 delete out;
567 if (ferror(stdout) || fflush(stdout) < 0)
568 fatal("output error");
569 exit(0);
570}
571