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