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