Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / preproc / pic / main.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003, 2006, 2009
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff 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 3 of the License, or
11 (at your option) any later version.
12
13 groff 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.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 #include "pic.h"
22
23 extern int yyparse();
24 extern "C" const char *Version_string;
25
26 output *out;
27 char *graphname;                // the picture box name in TeX mode
28
29 int flyback_flag;
30 int zero_length_line_flag = 0;
31 // Non-zero means we're using a groff driver.
32 int driver_extension_flag = 1;
33 int compatible_flag = 0;
34 int safer_flag = 1;
35 int command_char = '.';         // the character that introduces lines
36                                 // that should be passed through transparently
37 static 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.
41 static int had_parse_error = 0;
42
43 void do_file(const char *filename);
44
45 class top_input : public input {
46   FILE *fp;
47   int bol;
48   int eof;
49   int push_back[3];
50   int start_lineno;
51 public:
52   top_input(FILE *);
53   int get();
54   int peek();
55   int get_location(const char **, int *);
56 };
57
58 top_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
64 int 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
145 int 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
213 int top_input::get_location(const char **filenamep, int *linenop)
214 {
215   *filenamep = current_filename;
216   *linenop = current_lineno;
217   return 1;
218 }
219
220 void 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
300 void 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
453 void 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
473 void usage(FILE *stream)
474 {
475   fprintf(stream, "usage: %s [ -nvCSU ] [ filename ... ]\n", program_name);
476 #ifdef TEX_SUPPORT
477   fprintf(stream, "       %s -t [ -cvzCSU ] [ filename ... ]\n", program_name);
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__)
485 static 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
509 int 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