groff before CVS: release 1.10
[s-roff.git] / pic / main.cc
blob87beb5dc285f66b752b2f2bd192c94814e48a5b3
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #include "pic.h"
23 extern int yyparse();
25 output *out;
27 int flyback_flag;
28 int zero_length_line_flag = 0;
29 // Non-zero means we're using a groff driver.
30 int driver_extension_flag = 1;
31 int compatible_flag = 0;
32 int safer_flag = 0;
33 int command_char = '.'; // the character that introduces lines
34 // that should be passed through tranparently
35 static int lf_flag = 1; // non-zero if we should attempt to understand
36 // lines beginning with `.lf'
38 // Non-zero means a parse error was encountered.
39 static int had_parse_error = 0;
41 void do_file(const char *filename);
43 class top_input : public input {
44 FILE *fp;
45 int bol;
46 int eof;
47 int push_back[3];
48 int start_lineno;
49 public:
50 top_input(FILE *);
51 int get();
52 int peek();
53 int get_location(const char **, int *);
56 top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
58 push_back[0] = push_back[1] = push_back[2] = EOF;
59 start_lineno = current_lineno;
62 int top_input::get()
64 if (eof)
65 return EOF;
66 if (push_back[2] != EOF) {
67 int c = push_back[2];
68 push_back[2] = EOF;
69 return c;
71 else if (push_back[1] != EOF) {
72 int c = push_back[1];
73 push_back[1] = EOF;
74 return c;
76 else if (push_back[0] != EOF) {
77 int c = push_back[0];
78 push_back[0] = EOF;
79 return c;
81 int c = getc(fp);
82 while (illegal_input_char(c)) {
83 error("illegal input character code %1", int(c));
84 c = getc(fp);
85 bol = 0;
87 if (bol && c == '.') {
88 c = getc(fp);
89 if (c == 'P') {
90 c = getc(fp);
91 if (c == 'F' || c == 'E') {
92 int d = getc(fp);
93 if (d != EOF)
94 ungetc(d, fp);
95 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
96 eof = 1;
97 flyback_flag = c == 'F';
98 return EOF;
100 push_back[0] = c;
101 push_back[1] = 'P';
102 return '.';
104 if (c == 'S') {
105 c = getc(fp);
106 if (c != EOF)
107 ungetc(c, fp);
108 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
109 error("nested .PS");
110 eof = 1;
111 return EOF;
113 push_back[0] = 'S';
114 push_back[1] = 'P';
115 return '.';
117 if (c != EOF)
118 ungetc(c, fp);
119 push_back[0] = 'P';
120 return '.';
122 else {
123 if (c != EOF)
124 ungetc(c, fp);
125 return '.';
128 if (c == '\n') {
129 bol = 1;
130 current_lineno++;
131 return '\n';
133 bol = 0;
134 if (c == EOF) {
135 eof = 1;
136 error("end of file before .PE or .PF");
137 error_with_file_and_line(current_filename, start_lineno - 1,
138 ".PS was here");
140 return c;
143 int top_input::peek()
145 if (eof)
146 return EOF;
147 if (push_back[2] != EOF)
148 return push_back[2];
149 if (push_back[1] != EOF)
150 return push_back[1];
151 if (push_back[0] != EOF)
152 return push_back[0];
153 int c = getc(fp);
154 while (illegal_input_char(c)) {
155 error("illegal input character code %1", int(c));
156 c = getc(fp);
157 bol = 0;
159 if (bol && c == '.') {
160 c = getc(fp);
161 if (c == 'P') {
162 c = getc(fp);
163 if (c == 'F' || c == 'E') {
164 int d = getc(fp);
165 if (d != EOF)
166 ungetc(d, fp);
167 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
168 eof = 1;
169 flyback_flag = c == 'F';
170 return EOF;
172 push_back[0] = c;
173 push_back[1] = 'P';
174 push_back[2] = '.';
175 return '.';
177 if (c == 'S') {
178 c = getc(fp);
179 if (c != EOF)
180 ungetc(c, fp);
181 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
182 error("nested .PS");
183 eof = 1;
184 return EOF;
186 push_back[0] = 'S';
187 push_back[1] = 'P';
188 push_back[2] = '.';
189 return '.';
191 if (c != EOF)
192 ungetc(c, fp);
193 push_back[0] = 'P';
194 push_back[1] = '.';
195 return '.';
197 else {
198 if (c != EOF)
199 ungetc(c, fp);
200 push_back[0] = '.';
201 return '.';
204 if (c != EOF)
205 ungetc(c, fp);
206 if (c == '\n')
207 return '\n';
208 return c;
211 int top_input::get_location(const char **filenamep, int *linenop)
213 *filenamep = current_filename;
214 *linenop = current_lineno;
215 return 1;
218 void do_picture(FILE *fp)
220 flyback_flag = 0;
221 int c;
222 while ((c = getc(fp)) == ' ')
224 if (c == '<') {
225 string filename;
226 while ((c = getc(fp)) == ' ')
228 while (c != EOF && c != ' ' && c != '\n') {
229 filename += char(c);
230 c = getc(fp);
232 if (c == ' ') {
233 do {
234 c = getc(fp);
235 } while (c != EOF && c != '\n');
237 if (c == '\n')
238 current_lineno++;
239 if (filename.length() == 0)
240 error("missing filename after `<'");
241 else {
242 filename += '\0';
243 const char *old_filename = current_filename;
244 int old_lineno = current_lineno;
245 // filenames must be permanent
246 do_file(strsave(filename.contents()));
247 current_filename = old_filename;
248 current_lineno = old_lineno;
250 out->set_location(current_filename, current_lineno);
252 else {
253 out->set_location(current_filename, current_lineno);
254 string start_line;
255 while (c != EOF) {
256 if (c == '\n') {
257 current_lineno++;
258 break;
260 start_line += c;
261 c = getc(fp);
263 if (c == EOF)
264 return;
265 start_line += '\0';
266 double wid, ht;
267 switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
268 case 1:
269 ht = 0.0;
270 break;
271 case 2:
272 break;
273 default:
274 ht = wid = 0.0;
275 break;
277 out->set_desired_width_height(wid, ht);
278 out->set_args(start_line.contents());
279 lex_init(new top_input(fp));
280 if (yyparse()) {
281 had_parse_error = 1;
282 lex_error("giving up on this picture");
284 parse_cleanup();
285 lex_cleanup();
287 // skip the rest of the .PF/.PE line
288 while ((c = getc(fp)) != EOF && c != '\n')
290 if (c == '\n')
291 current_lineno++;
292 out->set_location(current_filename, current_lineno);
296 void do_file(const char *filename)
298 FILE *fp;
299 if (strcmp(filename, "-") == 0)
300 fp = stdin;
301 else {
302 errno = 0;
303 fp = fopen(filename, "r");
304 if (fp == 0)
305 fatal("can't open `%1': %2", filename, strerror(errno));
307 out->set_location(filename, 1);
308 current_filename = filename;
309 current_lineno = 1;
310 enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
311 for (;;) {
312 int c = getc(fp);
313 if (c == EOF)
314 break;
315 switch (state) {
316 case START:
317 if (c == '.')
318 state = HAD_DOT;
319 else {
320 putchar(c);
321 if (c == '\n') {
322 current_lineno++;
323 state = START;
325 else
326 state = MIDDLE;
328 break;
329 case MIDDLE:
330 putchar(c);
331 if (c == '\n') {
332 current_lineno++;
333 state = START;
335 break;
336 case HAD_DOT:
337 if (c == 'P')
338 state = HAD_P;
339 else if (lf_flag && c == 'l')
340 state = HAD_l;
341 else {
342 putchar('.');
343 putchar(c);
344 if (c == '\n') {
345 current_lineno++;
346 state = START;
348 else
349 state = MIDDLE;
351 break;
352 case HAD_P:
353 if (c == 'S')
354 state = HAD_PS;
355 else {
356 putchar('.');
357 putchar('P');
358 putchar(c);
359 if (c == '\n') {
360 current_lineno++;
361 state = START;
363 else
364 state = MIDDLE;
366 break;
367 case HAD_PS:
368 if (c == ' ' || c == '\n' || compatible_flag) {
369 ungetc(c, fp);
370 do_picture(fp);
371 state = START;
373 else {
374 fputs(".PS", stdout);
375 putchar(c);
376 state = MIDDLE;
378 break;
379 case HAD_l:
380 if (c == 'f')
381 state = HAD_lf;
382 else {
383 putchar('.');
384 putchar('l');
385 putchar(c);
386 if (c == '\n') {
387 current_lineno++;
388 state = START;
390 else
391 state = MIDDLE;
393 break;
394 case HAD_lf:
395 if (c == ' ' || c == '\n' || compatible_flag) {
396 string line;
397 while (c != EOF) {
398 line += c;
399 if (c == '\n') {
400 current_lineno++;
401 break;
403 c = getc(fp);
405 line += '\0';
406 interpret_lf_args(line.contents());
407 printf(".lf%s", line.contents());
408 state = START;
410 else {
411 fputs(".lf", stdout);
412 putchar(c);
413 state = MIDDLE;
415 break;
416 default:
417 assert(0);
420 switch (state) {
421 case START:
422 break;
423 case MIDDLE:
424 putchar('\n');
425 break;
426 case HAD_DOT:
427 fputs(".\n", stdout);
428 break;
429 case HAD_P:
430 fputs(".P\n", stdout);
431 break;
432 case HAD_PS:
433 fputs(".PS\n", stdout);
434 break;
435 case HAD_l:
436 fputs(".l\n", stdout);
437 break;
438 case HAD_lf:
439 fputs(".lf\n", stdout);
440 break;
442 if (fp != stdin)
443 fclose(fp);
446 #ifdef FIG_SUPPORT
447 void do_whole_file(const char *filename)
449 // Do not set current_filename.
450 FILE *fp;
451 if (strcmp(filename, "-") == 0)
452 fp = stdin;
453 else {
454 errno = 0;
455 fp = fopen(filename, "r");
456 if (fp == 0)
457 fatal("can't open `%1': %2", filename, strerror(errno));
459 lex_init(new file_input(fp, filename));
460 if (yyparse())
461 had_parse_error = 1;
462 parse_cleanup();
463 lex_cleanup();
465 #endif
467 void usage()
469 fprintf(stderr, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
470 #ifdef TEX_SUPPORT
471 fprintf(stderr, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
472 #endif
473 #ifdef FIG_SUPPORT
474 fprintf(stderr, " %s -f [ -v ] [ filename ]\n", program_name);
475 #endif
476 exit(1);
479 #ifdef __MSDOS__
480 static char *fix_program_name(char *arg, char *dflt)
482 if (!arg)
483 return dflt;
484 char *prog = strchr(arg, '\0');
485 for (;;) {
486 if (prog == arg)
487 break;
488 --prog;
489 if (strchr("\\/:", *prog)) {
490 prog++;
491 break;
494 char *ext = strchr(prog, '.');
495 if (ext)
496 *ext = '\0';
497 for (char *p = prog; *p; p++)
498 if ('A' <= *p && *p <= 'Z')
499 *p = 'a' + (*p - 'A');
500 return prog;
502 #endif /* __MSDOS__ */
504 int main(int argc, char **argv)
506 #ifdef __MSDOS__
507 argv[0] = fix_program_name(argv[0], "pic");
508 #endif /* __MSDOS__ */
509 program_name = argv[0];
510 static char stderr_buf[BUFSIZ];
511 setbuf(stderr, stderr_buf);
512 int opt;
513 #ifdef TEX_SUPPORT
514 int tex_flag = 0;
515 int tpic_flag = 0;
516 #endif
517 #ifdef FIG_SUPPORT
518 int whole_file_flag = 0;
519 int fig_flag = 0;
520 #endif
521 while ((opt = getopt(argc, argv, "T:CDStcvnxzpf")) != EOF)
522 switch (opt) {
523 case 'C':
524 compatible_flag = 1;
525 break;
526 case 'D':
527 case 'T':
528 break;
529 case 'S':
530 safer_flag = 1;
531 break;
532 case 'f':
533 #ifdef FIG_SUPPORT
534 whole_file_flag++;
535 fig_flag++;
536 #else
537 fatal("fig support not included");
538 #endif
539 break;
540 case 'n':
541 driver_extension_flag = 0;
542 break;
543 case 'p':
544 case 'x':
545 warning("-%1 option is obsolete", char(opt));
546 break;
547 case 't':
548 #ifdef TEX_SUPPORT
549 tex_flag++;
550 #else
551 fatal("TeX support not included");
552 #endif
553 break;
554 case 'c':
555 #ifdef TEX_SUPPORT
556 tpic_flag++;
557 #else
558 fatal("TeX support not included");
559 #endif
560 break;
561 case 'v':
563 extern const char *version_string;
564 fprintf(stderr, "GNU pic version %s\n", version_string);
565 fflush(stderr);
566 break;
568 case 'z':
569 // zero length lines will be printed as dots
570 zero_length_line_flag++;
571 break;
572 case '?':
573 usage();
574 break;
575 default:
576 assert(0);
578 parse_init();
579 #ifdef TEX_SUPPORT
580 if (tpic_flag) {
581 out = make_tpic_output();
582 lf_flag = 0;
584 else if (tex_flag) {
585 out = make_tex_output();
586 command_char = '\\';
587 lf_flag = 0;
589 else
590 #endif
591 #ifdef FIG_SUPPORT
592 if (fig_flag)
593 out = make_fig_output();
594 else
595 #endif
596 out = make_troff_output();
597 #ifdef FIG_SUPPORT
598 if (whole_file_flag) {
599 if (optind >= argc)
600 do_whole_file("-");
601 else if (argc - optind > 1)
602 usage();
603 else
604 do_whole_file(argv[optind]);
606 else {
607 #endif
608 if (optind >= argc)
609 do_file("-");
610 else
611 for (int i = optind; i < argc; i++)
612 do_file(argv[i]);
613 #ifdef FIG_SUPPORT
615 #endif
616 delete out;
617 if (ferror(stdout) || fflush(stdout) < 0)
618 fatal("output error");
619 return had_parse_error;