No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / pic / main.cpp
blob9b932b7086d0c476c6c91f46dcd3a9804e1f916b
1 /* $NetBSD$ */
3 // -*- C++ -*-
4 /* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "pic.h"
26 extern int yyparse();
27 extern "C" const char *Version_string;
29 output *out;
30 char *graphname; // the picture box name in TeX mode
32 int flyback_flag;
33 int zero_length_line_flag = 0;
34 // Non-zero means we're using a groff driver.
35 int driver_extension_flag = 1;
36 int compatible_flag = 0;
37 int safer_flag = 1;
38 int command_char = '.'; // the character that introduces lines
39 // that should be passed through tranparently
40 static int lf_flag = 1; // non-zero if we should attempt to understand
41 // lines beginning with `.lf'
43 // Non-zero means a parse error was encountered.
44 static int had_parse_error = 0;
46 void do_file(const char *filename);
48 class top_input : public input {
49 FILE *fp;
50 int bol;
51 int eof;
52 int push_back[3];
53 int start_lineno;
54 public:
55 top_input(FILE *);
56 int get();
57 int peek();
58 int get_location(const char **, int *);
61 top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
63 push_back[0] = push_back[1] = push_back[2] = EOF;
64 start_lineno = current_lineno;
67 int top_input::get()
69 if (eof)
70 return EOF;
71 if (push_back[2] != EOF) {
72 int c = push_back[2];
73 push_back[2] = EOF;
74 return c;
76 else if (push_back[1] != EOF) {
77 int c = push_back[1];
78 push_back[1] = EOF;
79 return c;
81 else if (push_back[0] != EOF) {
82 int c = push_back[0];
83 push_back[0] = EOF;
84 return c;
86 int c = getc(fp);
87 while (invalid_input_char(c)) {
88 error("invalid input character code %1", int(c));
89 c = getc(fp);
90 bol = 0;
92 if (bol && c == '.') {
93 c = getc(fp);
94 if (c == 'P') {
95 c = getc(fp);
96 if (c == 'F' || c == 'E') {
97 int d = getc(fp);
98 if (d != EOF)
99 ungetc(d, fp);
100 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
101 eof = 1;
102 flyback_flag = c == 'F';
103 return EOF;
105 push_back[0] = c;
106 push_back[1] = 'P';
107 return '.';
109 if (c == 'S') {
110 c = getc(fp);
111 if (c != EOF)
112 ungetc(c, fp);
113 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
114 error("nested .PS");
115 eof = 1;
116 return EOF;
118 push_back[0] = 'S';
119 push_back[1] = 'P';
120 return '.';
122 if (c != EOF)
123 ungetc(c, fp);
124 push_back[0] = 'P';
125 return '.';
127 else {
128 if (c != EOF)
129 ungetc(c, fp);
130 return '.';
133 if (c == '\n') {
134 bol = 1;
135 current_lineno++;
136 return '\n';
138 bol = 0;
139 if (c == EOF) {
140 eof = 1;
141 error("end of file before .PE or .PF");
142 error_with_file_and_line(current_filename, start_lineno - 1,
143 ".PS was here");
145 return c;
148 int top_input::peek()
150 if (eof)
151 return EOF;
152 if (push_back[2] != EOF)
153 return push_back[2];
154 if (push_back[1] != EOF)
155 return push_back[1];
156 if (push_back[0] != EOF)
157 return push_back[0];
158 int c = getc(fp);
159 while (invalid_input_char(c)) {
160 error("invalid input character code %1", int(c));
161 c = getc(fp);
162 bol = 0;
164 if (bol && c == '.') {
165 c = getc(fp);
166 if (c == 'P') {
167 c = getc(fp);
168 if (c == 'F' || c == 'E') {
169 int d = getc(fp);
170 if (d != EOF)
171 ungetc(d, fp);
172 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
173 eof = 1;
174 flyback_flag = c == 'F';
175 return EOF;
177 push_back[0] = c;
178 push_back[1] = 'P';
179 push_back[2] = '.';
180 return '.';
182 if (c == 'S') {
183 c = getc(fp);
184 if (c != EOF)
185 ungetc(c, fp);
186 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
187 error("nested .PS");
188 eof = 1;
189 return EOF;
191 push_back[0] = 'S';
192 push_back[1] = 'P';
193 push_back[2] = '.';
194 return '.';
196 if (c != EOF)
197 ungetc(c, fp);
198 push_back[0] = 'P';
199 push_back[1] = '.';
200 return '.';
202 else {
203 if (c != EOF)
204 ungetc(c, fp);
205 push_back[0] = '.';
206 return '.';
209 if (c != EOF)
210 ungetc(c, fp);
211 if (c == '\n')
212 return '\n';
213 return c;
216 int top_input::get_location(const char **filenamep, int *linenop)
218 *filenamep = current_filename;
219 *linenop = current_lineno;
220 return 1;
223 void do_picture(FILE *fp)
225 flyback_flag = 0;
226 int c;
227 a_delete graphname;
228 graphname = strsave("graph"); // default picture name in TeX mode
229 while ((c = getc(fp)) == ' ')
231 if (c == '<') {
232 string filename;
233 while ((c = getc(fp)) == ' ')
235 while (c != EOF && c != ' ' && c != '\n') {
236 filename += char(c);
237 c = getc(fp);
239 if (c == ' ') {
240 do {
241 c = getc(fp);
242 } while (c != EOF && c != '\n');
244 if (c == '\n')
245 current_lineno++;
246 if (filename.length() == 0)
247 error("missing filename after `<'");
248 else {
249 filename += '\0';
250 const char *old_filename = current_filename;
251 int old_lineno = current_lineno;
252 // filenames must be permanent
253 do_file(strsave(filename.contents()));
254 current_filename = old_filename;
255 current_lineno = old_lineno;
257 out->set_location(current_filename, current_lineno);
259 else {
260 out->set_location(current_filename, current_lineno);
261 string start_line;
262 while (c != EOF) {
263 if (c == '\n') {
264 current_lineno++;
265 break;
267 start_line += c;
268 c = getc(fp);
270 if (c == EOF)
271 return;
272 start_line += '\0';
273 double wid, ht;
274 switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
275 case 1:
276 ht = 0.0;
277 break;
278 case 2:
279 break;
280 default:
281 ht = wid = 0.0;
282 break;
284 out->set_desired_width_height(wid, ht);
285 out->set_args(start_line.contents());
286 lex_init(new top_input(fp));
287 if (yyparse()) {
288 had_parse_error = 1;
289 lex_error("giving up on this picture");
291 parse_cleanup();
292 lex_cleanup();
294 // skip the rest of the .PF/.PE line
295 while ((c = getc(fp)) != EOF && c != '\n')
297 if (c == '\n')
298 current_lineno++;
299 out->set_location(current_filename, current_lineno);
303 void do_file(const char *filename)
305 FILE *fp;
306 if (strcmp(filename, "-") == 0)
307 fp = stdin;
308 else {
309 errno = 0;
310 fp = fopen(filename, "r");
311 if (fp == 0) {
312 delete out;
313 fatal("can't open `%1': %2", filename, strerror(errno));
316 out->set_location(filename, 1);
317 current_filename = filename;
318 current_lineno = 1;
319 enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
320 for (;;) {
321 int c = getc(fp);
322 if (c == EOF)
323 break;
324 switch (state) {
325 case START:
326 if (c == '.')
327 state = HAD_DOT;
328 else {
329 putchar(c);
330 if (c == '\n') {
331 current_lineno++;
332 state = START;
334 else
335 state = MIDDLE;
337 break;
338 case MIDDLE:
339 putchar(c);
340 if (c == '\n') {
341 current_lineno++;
342 state = START;
344 break;
345 case HAD_DOT:
346 if (c == 'P')
347 state = HAD_P;
348 else if (lf_flag && c == 'l')
349 state = HAD_l;
350 else {
351 putchar('.');
352 putchar(c);
353 if (c == '\n') {
354 current_lineno++;
355 state = START;
357 else
358 state = MIDDLE;
360 break;
361 case HAD_P:
362 if (c == 'S')
363 state = HAD_PS;
364 else {
365 putchar('.');
366 putchar('P');
367 putchar(c);
368 if (c == '\n') {
369 current_lineno++;
370 state = START;
372 else
373 state = MIDDLE;
375 break;
376 case HAD_PS:
377 if (c == ' ' || c == '\n' || compatible_flag) {
378 ungetc(c, fp);
379 do_picture(fp);
380 state = START;
382 else {
383 fputs(".PS", stdout);
384 putchar(c);
385 state = MIDDLE;
387 break;
388 case HAD_l:
389 if (c == 'f')
390 state = HAD_lf;
391 else {
392 putchar('.');
393 putchar('l');
394 putchar(c);
395 if (c == '\n') {
396 current_lineno++;
397 state = START;
399 else
400 state = MIDDLE;
402 break;
403 case HAD_lf:
404 if (c == ' ' || c == '\n' || compatible_flag) {
405 string line;
406 while (c != EOF) {
407 line += c;
408 if (c == '\n') {
409 current_lineno++;
410 break;
412 c = getc(fp);
414 line += '\0';
415 interpret_lf_args(line.contents());
416 printf(".lf%s", line.contents());
417 state = START;
419 else {
420 fputs(".lf", stdout);
421 putchar(c);
422 state = MIDDLE;
424 break;
425 default:
426 assert(0);
429 switch (state) {
430 case START:
431 break;
432 case MIDDLE:
433 putchar('\n');
434 break;
435 case HAD_DOT:
436 fputs(".\n", stdout);
437 break;
438 case HAD_P:
439 fputs(".P\n", stdout);
440 break;
441 case HAD_PS:
442 fputs(".PS\n", stdout);
443 break;
444 case HAD_l:
445 fputs(".l\n", stdout);
446 break;
447 case HAD_lf:
448 fputs(".lf\n", stdout);
449 break;
451 if (fp != stdin)
452 fclose(fp);
455 #ifdef FIG_SUPPORT
456 void do_whole_file(const char *filename)
458 // Do not set current_filename.
459 FILE *fp;
460 if (strcmp(filename, "-") == 0)
461 fp = stdin;
462 else {
463 errno = 0;
464 fp = fopen(filename, "r");
465 if (fp == 0)
466 fatal("can't open `%1': %2", filename, strerror(errno));
468 lex_init(new file_input(fp, filename));
469 if (yyparse())
470 had_parse_error = 1;
471 parse_cleanup();
472 lex_cleanup();
474 #endif
476 void usage(FILE *stream)
478 fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
479 #ifdef TEX_SUPPORT
480 fprintf(stream, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
481 #endif
482 #ifdef FIG_SUPPORT
483 fprintf(stream, " %s -f [ -v ] [ filename ]\n", program_name);
484 #endif
487 #if defined(__MSDOS__) || defined(__EMX__)
488 static char *fix_program_name(char *arg, char *dflt)
490 if (!arg)
491 return dflt;
492 char *prog = strchr(arg, '\0');
493 for (;;) {
494 if (prog == arg)
495 break;
496 --prog;
497 if (strchr("\\/:", *prog)) {
498 prog++;
499 break;
502 char *ext = strchr(prog, '.');
503 if (ext)
504 *ext = '\0';
505 for (char *p = prog; *p; p++)
506 if ('A' <= *p && *p <= 'Z')
507 *p = 'a' + (*p - 'A');
508 return prog;
510 #endif /* __MSDOS__ || __EMX__ */
512 int main(int argc, char **argv)
514 setlocale(LC_NUMERIC, "C");
515 #if defined(__MSDOS__) || defined(__EMX__)
516 argv[0] = fix_program_name(argv[0], "pic");
517 #endif /* __MSDOS__ || __EMX__ */
518 program_name = argv[0];
519 static char stderr_buf[BUFSIZ];
520 setbuf(stderr, stderr_buf);
521 int opt;
522 #ifdef TEX_SUPPORT
523 int tex_flag = 0;
524 int tpic_flag = 0;
525 #endif
526 #ifdef FIG_SUPPORT
527 int whole_file_flag = 0;
528 int fig_flag = 0;
529 #endif
530 static const struct option long_options[] = {
531 { "help", no_argument, 0, CHAR_MAX + 1 },
532 { "version", no_argument, 0, 'v' },
533 { NULL, 0, 0, 0 }
535 while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL))
536 != EOF)
537 switch (opt) {
538 case 'C':
539 compatible_flag = 1;
540 break;
541 case 'D':
542 case 'T':
543 break;
544 case 'S':
545 safer_flag = 1;
546 break;
547 case 'U':
548 safer_flag = 0;
549 break;
550 case 'f':
551 #ifdef FIG_SUPPORT
552 whole_file_flag++;
553 fig_flag++;
554 #else
555 fatal("fig support not included");
556 #endif
557 break;
558 case 'n':
559 driver_extension_flag = 0;
560 break;
561 case 'p':
562 case 'x':
563 warning("-%1 option is obsolete", char(opt));
564 break;
565 case 't':
566 #ifdef TEX_SUPPORT
567 tex_flag++;
568 #else
569 fatal("TeX support not included");
570 #endif
571 break;
572 case 'c':
573 #ifdef TEX_SUPPORT
574 tpic_flag++;
575 #else
576 fatal("TeX support not included");
577 #endif
578 break;
579 case 'v':
581 printf("GNU pic (groff) version %s\n", Version_string);
582 exit(0);
583 break;
585 case 'z':
586 // zero length lines will be printed as dots
587 zero_length_line_flag++;
588 break;
589 case CHAR_MAX + 1: // --help
590 usage(stdout);
591 exit(0);
592 break;
593 case '?':
594 usage(stderr);
595 exit(1);
596 break;
597 default:
598 assert(0);
600 parse_init();
601 #ifdef TEX_SUPPORT
602 if (tpic_flag) {
603 out = make_tpic_output();
604 lf_flag = 0;
606 else if (tex_flag) {
607 out = make_tex_output();
608 command_char = '\\';
609 lf_flag = 0;
611 else
612 #endif
613 #ifdef FIG_SUPPORT
614 if (fig_flag)
615 out = make_fig_output();
616 else
617 #endif
618 out = make_troff_output();
619 #ifdef FIG_SUPPORT
620 if (whole_file_flag) {
621 if (optind >= argc)
622 do_whole_file("-");
623 else if (argc - optind > 1) {
624 usage(stderr);
625 exit(1);
626 } else
627 do_whole_file(argv[optind]);
629 else {
630 #endif
631 if (optind >= argc)
632 do_file("-");
633 else
634 for (int i = optind; i < argc; i++)
635 do_file(argv[i]);
636 #ifdef FIG_SUPPORT
638 #endif
639 delete out;
640 if (ferror(stdout) || fflush(stdout) < 0)
641 fatal("output error");
642 return had_parse_error;