groff before CVS: release 1.07
[s-roff.git] / pic / main.cc
blobfe608ebd98a9e3159ca62c4d43edf37e68c73be1
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, 675 Mass Ave, Cambridge, MA 02139, 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 command_char = '.'; // the character that introduces lines
33 // that should be passed through tranparently
34 static int lf_flag = 1; // non-zero if we should attempt to understand
35 // lines beginning with `.lf'
37 void do_file(const char *filename);
39 class top_input : public input {
40 FILE *fp;
41 int bol;
42 int eof;
43 int push_back[3];
44 int start_lineno;
45 public:
46 top_input(FILE *);
47 int get();
48 int peek();
49 int get_location(const char **, int *);
52 top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
54 push_back[0] = push_back[1] = push_back[2] = EOF;
55 start_lineno = current_lineno;
58 int top_input::get()
60 if (eof)
61 return EOF;
62 if (push_back[2] != EOF) {
63 int c = push_back[2];
64 push_back[2] = EOF;
65 return c;
67 else if (push_back[1] != EOF) {
68 int c = push_back[1];
69 push_back[1] = EOF;
70 return c;
72 else if (push_back[0] != EOF) {
73 int c = push_back[0];
74 push_back[0] = EOF;
75 return c;
77 int c = getc(fp);
78 while (illegal_input_char(c)) {
79 error("illegal input character code %1", int(c));
80 c = getc(fp);
81 bol = 0;
83 if (bol && c == '.') {
84 c = getc(fp);
85 if (c == 'P') {
86 c = getc(fp);
87 if (c == 'F' || c == 'E') {
88 int d = getc(fp);
89 if (d != EOF)
90 ungetc(d, fp);
91 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
92 eof = 1;
93 flyback_flag = c == 'F';
94 return EOF;
96 push_back[0] = c;
97 push_back[1] = 'P';
98 return '.';
100 if (c == 'S') {
101 c = getc(fp);
102 if (c != EOF)
103 ungetc(c, fp);
104 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
105 error("nested .PS");
106 eof = 1;
107 return EOF;
109 push_back[0] = 'S';
110 push_back[1] = 'P';
111 return '.';
113 if (c != EOF)
114 ungetc(c, fp);
115 push_back[0] = 'P';
116 return '.';
118 else {
119 if (c != EOF)
120 ungetc(c, fp);
121 return '.';
124 if (c == '\n') {
125 bol = 1;
126 current_lineno++;
127 return '\n';
129 bol = 0;
130 if (c == EOF) {
131 eof = 1;
132 error("end of file before .PE or .PF");
133 error_with_file_and_line(current_filename, start_lineno - 1,
134 ".PS was here");
136 return c;
139 int top_input::peek()
141 if (eof)
142 return EOF;
143 if (push_back[2] != EOF)
144 return push_back[2];
145 if (push_back[1] != EOF)
146 return push_back[1];
147 if (push_back[0] != EOF)
148 return push_back[0];
149 int c = getc(fp);
150 while (illegal_input_char(c)) {
151 error("illegal input character code %1", int(c));
152 c = getc(fp);
153 bol = 0;
155 if (bol && c == '.') {
156 c = getc(fp);
157 if (c == 'P') {
158 c = getc(fp);
159 if (c == 'F' || c == 'E') {
160 int d = getc(fp);
161 if (d != EOF)
162 ungetc(d, fp);
163 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
164 eof = 1;
165 flyback_flag = c == 'F';
166 return EOF;
168 push_back[0] = c;
169 push_back[1] = 'P';
170 push_back[2] = '.';
171 return '.';
173 if (c == 'S') {
174 c = getc(fp);
175 if (c != EOF)
176 ungetc(c, fp);
177 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
178 error("nested .PS");
179 eof = 1;
180 return EOF;
182 push_back[0] = 'S';
183 push_back[1] = 'P';
184 push_back[2] = '.';
185 return '.';
187 if (c != EOF)
188 ungetc(c, fp);
189 push_back[0] = 'P';
190 push_back[1] = '.';
191 return '.';
193 else {
194 if (c != EOF)
195 ungetc(c, fp);
196 push_back[0] = '.';
197 return '.';
200 if (c != EOF)
201 ungetc(c, fp);
202 if (c == '\n')
203 return '\n';
204 return c;
207 int top_input::get_location(const char **filenamep, int *linenop)
209 *filenamep = current_filename;
210 *linenop = current_lineno;
211 return 1;
214 void do_picture(FILE *fp)
216 flyback_flag = 0;
217 int c;
218 while ((c = getc(fp)) == ' ')
220 if (c == '<') {
221 string filename;
222 while ((c = getc(fp)) == ' ')
224 while (c != EOF && c != ' ' && c != '\n') {
225 filename += char(c);
226 c = getc(fp);
228 if (c == ' ') {
229 do {
230 c = getc(fp);
231 } while (c != EOF && c != '\n');
233 if (c == '\n')
234 current_lineno++;
235 if (filename.length() == 0)
236 error("missing filename after `<'");
237 else {
238 filename += '\0';
239 const char *old_filename = current_filename;
240 int old_lineno = current_lineno;
241 // filenames must be permanent
242 do_file(strsave(filename.contents()));
243 current_filename = old_filename;
244 current_lineno = old_lineno;
246 out->set_location(current_filename, current_lineno);
248 else {
249 out->set_location(current_filename, current_lineno);
250 string start_line;
251 while (c != EOF) {
252 if (c == '\n') {
253 current_lineno++;
254 break;
256 start_line += c;
257 c = getc(fp);
259 if (c == EOF)
260 return;
261 start_line += '\0';
262 double wid, ht;
263 switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
264 case 1:
265 ht = 0.0;
266 break;
267 case 2:
268 break;
269 default:
270 ht = wid = 0.0;
271 break;
273 out->set_desired_width_height(wid, ht);
274 out->set_args(start_line.contents());
275 lex_init(new top_input(fp));
276 if (yyparse())
277 lex_error("giving up on this picture");
278 parse_cleanup();
279 lex_cleanup();
281 // skip the rest of the .PF/.PE line
282 while ((c = getc(fp)) != EOF && c != '\n')
284 if (c == '\n')
285 current_lineno++;
286 out->set_location(current_filename, current_lineno);
290 void do_file(const char *filename)
292 FILE *fp;
293 if (strcmp(filename, "-") == 0)
294 fp = stdin;
295 else {
296 errno = 0;
297 fp = fopen(filename, "r");
298 if (fp == 0)
299 fatal("can't open `%1': %2", filename, strerror(errno));
301 out->set_location(filename, 1);
302 current_filename = filename;
303 current_lineno = 1;
304 enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
305 for (;;) {
306 int c = getc(fp);
307 if (c == EOF)
308 break;
309 switch (state) {
310 case START:
311 if (c == '.')
312 state = HAD_DOT;
313 else {
314 putchar(c);
315 if (c == '\n') {
316 current_lineno++;
317 state = START;
319 else
320 state = MIDDLE;
322 break;
323 case MIDDLE:
324 putchar(c);
325 if (c == '\n') {
326 current_lineno++;
327 state = START;
329 break;
330 case HAD_DOT:
331 if (c == 'P')
332 state = HAD_P;
333 else if (lf_flag && c == 'l')
334 state = HAD_l;
335 else {
336 putchar('.');
337 putchar(c);
338 if (c == '\n') {
339 current_lineno++;
340 state = START;
342 else
343 state = MIDDLE;
345 break;
346 case HAD_P:
347 if (c == 'S')
348 state = HAD_PS;
349 else {
350 putchar('.');
351 putchar('P');
352 putchar(c);
353 if (c == '\n') {
354 current_lineno++;
355 state = START;
357 else
358 state = MIDDLE;
360 break;
361 case HAD_PS:
362 if (c == ' ' || c == '\n' || compatible_flag) {
363 ungetc(c, fp);
364 do_picture(fp);
365 state = START;
367 else {
368 fputs(".PS", stdout);
369 putchar(c);
370 state = MIDDLE;
372 break;
373 case HAD_l:
374 if (c == 'f')
375 state = HAD_lf;
376 else {
377 putchar('.');
378 putchar('l');
379 putchar(c);
380 if (c == '\n') {
381 current_lineno++;
382 state = START;
384 else
385 state = MIDDLE;
387 break;
388 case HAD_lf:
389 if (c == ' ' || c == '\n' || compatible_flag) {
390 string line;
391 while (c != EOF) {
392 line += c;
393 if (c == '\n') {
394 current_lineno++;
395 break;
397 c = getc(fp);
399 line += '\0';
400 interpret_lf_args(line.contents());
401 printf(".lf%s", line.contents());
402 state = START;
404 else {
405 fputs(".lf", stdout);
406 putchar(c);
407 state = MIDDLE;
409 break;
410 default:
411 assert(0);
414 switch (state) {
415 case START:
416 break;
417 case MIDDLE:
418 putchar('\n');
419 break;
420 case HAD_DOT:
421 fputs(".\n", stdout);
422 break;
423 case HAD_P:
424 fputs(".P\n", stdout);
425 break;
426 case HAD_PS:
427 fputs(".PS\n", stdout);
428 break;
429 case HAD_l:
430 fputs(".l\n", stdout);
431 break;
432 case HAD_lf:
433 fputs(".lf\n", stdout);
434 break;
436 if (fp != stdin)
437 fclose(fp);
440 #ifdef FIG_SUPPORT
441 void do_whole_file(const char *filename)
443 // Do not set current_filename.
444 FILE *fp;
445 if (strcmp(filename, "-") == 0)
446 fp = stdin;
447 else {
448 errno = 0;
449 fp = fopen(filename, "r");
450 if (fp == 0)
451 fatal("can't open `%1': %2", filename, strerror(errno));
453 lex_init(new file_input(fp, filename));
454 yyparse();
455 parse_cleanup();
456 lex_cleanup();
458 #endif
460 void usage()
462 fprintf(stderr, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
463 #ifdef TEX_SUPPORT
464 fprintf(stderr, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
465 #endif
466 #ifdef FIG_SUPPORT
467 fprintf(stderr, " %s -f [ -v ] [ filename ]\n", program_name);
468 #endif
469 exit(1);
472 #ifdef __MSDOS__
473 static char *fix_program_name(char *arg, char *dflt)
475 if (!arg)
476 return dflt;
477 char *prog = strchr(arg, '\0');
478 for (;;) {
479 if (prog == arg)
480 break;
481 --prog;
482 if (strchr("\\/:", *prog)) {
483 prog++;
484 break;
487 char *ext = strchr(prog, '.');
488 if (ext)
489 *ext = '\0';
490 for (char *p = prog; *p; p++)
491 if ('A' <= *p && *p <= 'Z')
492 *p = 'a' + (*p - 'A');
493 return prog;
495 #endif /* __MSDOS__ */
497 int main(int argc, char **argv)
499 #ifdef __MSDOS__
500 argv[0] = fix_program_name(argv[0], "pic");
501 #endif /* __MSDOS__ */
502 program_name = argv[0];
503 static char stderr_buf[BUFSIZ];
504 setbuf(stderr, stderr_buf);
505 int opt;
506 #ifdef TEX_SUPPORT
507 int tex_flag = 0;
508 int tpic_flag = 0;
509 #endif
510 #ifdef FIG_SUPPORT
511 int whole_file_flag = 0;
512 int fig_flag = 0;
513 #endif
514 while ((opt = getopt(argc, argv, "T:CDtcvnxzpf")) != EOF)
515 switch (opt) {
516 case 'C':
517 compatible_flag = 1;
518 break;
519 case 'D':
520 case 'T':
521 break;
522 case 'f':
523 #ifdef FIG_SUPPORT
524 whole_file_flag++;
525 fig_flag++;
526 #else
527 fatal("fig support not included");
528 #endif
529 break;
530 case 'n':
531 driver_extension_flag = 0;
532 break;
533 case 'p':
534 case 'x':
535 warning("-%1 option is obsolete", char(opt));
536 break;
537 case 't':
538 #ifdef TEX_SUPPORT
539 tex_flag++;
540 #else
541 fatal("TeX support not included");
542 #endif
543 break;
544 case 'c':
545 #ifdef TEX_SUPPORT
546 tpic_flag++;
547 #else
548 fatal("TeX support not included");
549 #endif
550 break;
551 case 'v':
553 extern const char *version_string;
554 fprintf(stderr, "GNU pic version %s\n", version_string);
555 fflush(stderr);
556 break;
558 case 'z':
559 // zero length lines will be printed as dots
560 zero_length_line_flag++;
561 break;
562 case '?':
563 usage();
564 break;
565 default:
566 assert(0);
568 parse_init();
569 #ifdef TEX_SUPPORT
570 if (tpic_flag) {
571 out = make_tpic_output();
572 lf_flag = 0;
574 else if (tex_flag) {
575 out = make_tex_output();
576 command_char = '\\';
577 lf_flag = 0;
579 else
580 #endif
581 #ifdef FIG_SUPPORT
582 if (fig_flag)
583 out = make_fig_output();
584 else
585 #endif
586 out = make_troff_output();
587 #ifdef FIG_SUPPORT
588 if (whole_file_flag) {
589 if (optind >= argc)
590 do_whole_file("-");
591 else if (argc - optind > 1)
592 usage();
593 else
594 do_whole_file(argv[optind]);
596 else {
597 #endif
598 if (optind >= argc)
599 do_file("-");
600 else
601 for (int i = optind; i < argc; i++)
602 do_file(argv[i]);
603 #ifdef FIG_SUPPORT
605 #endif
606 delete out;
607 if (ferror(stdout) || fflush(stdout) < 0)
608 fatal("output error");
609 exit(0);