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