Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / preproc / pic / main.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001, 2002 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 2, or (at your option) any later
10 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 along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #include "pic.h"
22
23 extern int yyparse();
24 extern "C" const char *Version_string;
25
26 output *out;
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 tranparently
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   while ((c = getc(fp)) == ' ')
224     ;
225   if (c == '<') {
226     string filename;
227     while ((c = getc(fp)) == ' ')
228       ;
229     while (c != EOF && c != ' ' && c != '\n') {
230       filename += char(c);
231       c = getc(fp);
232     }
233     if (c == ' ') {
234       do {
235         c = getc(fp);
236       } while (c != EOF && c != '\n');
237     }
238     if (c == '\n') 
239       current_lineno++;
240     if (filename.length() == 0)
241       error("missing filename after `<'");
242     else {
243       filename += '\0';
244       const char *old_filename = current_filename;
245       int old_lineno = current_lineno;
246       // filenames must be permanent
247       do_file(strsave(filename.contents()));
248       current_filename = old_filename;
249       current_lineno = old_lineno;
250     }
251     out->set_location(current_filename, current_lineno);
252   }
253   else {
254     out->set_location(current_filename, current_lineno);
255     string start_line;
256     while (c != EOF) {
257       if (c == '\n') {
258         current_lineno++;
259         break;
260       }
261       start_line += c;
262       c = getc(fp);
263     }
264     if (c == EOF)
265       return;
266     start_line += '\0';
267     double wid, ht;
268     switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
269     case 1:
270       ht = 0.0;
271       break;
272     case 2:
273       break;
274     default:
275       ht = wid = 0.0;
276       break;
277     }
278     out->set_desired_width_height(wid, ht);
279     out->set_args(start_line.contents());
280     lex_init(new top_input(fp));
281     if (yyparse()) {
282       had_parse_error = 1;
283       lex_error("giving up on this picture");
284     }
285     parse_cleanup();
286     lex_cleanup();
287
288     // skip the rest of the .PF/.PE line
289     while ((c = getc(fp)) != EOF && c != '\n')
290       ;
291     if (c == '\n')
292       current_lineno++;
293     out->set_location(current_filename, current_lineno);
294   }
295 }
296
297 void do_file(const char *filename)
298 {
299   FILE *fp;
300   if (strcmp(filename, "-") == 0)
301     fp = stdin;
302   else {
303     errno = 0;
304     fp = fopen(filename, "r");
305     if (fp == 0)
306       fatal("can't open `%1': %2", filename, strerror(errno));
307   }
308   out->set_location(filename, 1);
309   current_filename = filename;
310   current_lineno = 1;
311   enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
312   for (;;) {
313     int c = getc(fp);
314     if (c == EOF)
315       break;
316     switch (state) {
317     case START:
318       if (c == '.')
319         state = HAD_DOT;
320       else {
321         putchar(c);
322         if (c == '\n') {
323           current_lineno++;
324           state = START;
325         }
326         else
327           state = MIDDLE;
328       }
329       break;
330     case MIDDLE:
331       putchar(c);
332       if (c == '\n') {
333         current_lineno++;
334         state = START;
335       }
336       break;
337     case HAD_DOT:
338       if (c == 'P')
339         state = HAD_P;
340       else if (lf_flag && c == 'l')
341         state = HAD_l;
342       else {
343         putchar('.');
344         putchar(c);
345         if (c == '\n') {
346           current_lineno++;
347           state = START;
348         }
349         else
350           state = MIDDLE;
351       }
352       break;
353     case HAD_P:
354       if (c == 'S')
355         state = HAD_PS;
356       else  {
357         putchar('.');
358         putchar('P');
359         putchar(c);
360         if (c == '\n') {
361           current_lineno++;
362           state = START;
363         }
364         else
365           state = MIDDLE;
366       }
367       break;
368     case HAD_PS:
369       if (c == ' ' || c == '\n' || compatible_flag) {
370         ungetc(c, fp);
371         do_picture(fp);
372         state = START;
373       }
374       else {
375         fputs(".PS", stdout);
376         putchar(c);
377         state = MIDDLE;
378       }
379       break;
380     case HAD_l:
381       if (c == 'f')
382         state = HAD_lf;
383       else {
384         putchar('.');
385         putchar('l');
386         putchar(c);
387         if (c == '\n') {
388           current_lineno++;
389           state = START;
390         }
391         else
392           state = MIDDLE;
393       }
394       break;
395     case HAD_lf:
396       if (c == ' ' || c == '\n' || compatible_flag) {
397         string line;
398         while (c != EOF) {
399           line += c;
400           if (c == '\n') {
401             current_lineno++;
402             break;
403           }
404           c = getc(fp);
405         }
406         line += '\0';
407         interpret_lf_args(line.contents());
408         printf(".lf%s", line.contents());
409         state = START;
410       }
411       else {
412         fputs(".lf", stdout);
413         putchar(c);
414         state = MIDDLE;
415       }
416       break;
417     default:
418       assert(0);
419     }
420   }
421   switch (state) {
422   case START:
423     break;
424   case MIDDLE:
425     putchar('\n');
426     break;
427   case HAD_DOT:
428     fputs(".\n", stdout);
429     break;
430   case HAD_P:
431     fputs(".P\n", stdout);
432     break;
433   case HAD_PS:
434     fputs(".PS\n", stdout);
435     break;
436   case HAD_l:
437     fputs(".l\n", stdout);
438     break;
439   case HAD_lf:
440     fputs(".lf\n", stdout);
441     break;
442   }
443   if (fp != stdin)
444     fclose(fp);
445 }
446
447 #ifdef FIG_SUPPORT
448 void do_whole_file(const char *filename)
449 {
450   // Do not set current_filename.
451   FILE *fp;
452   if (strcmp(filename, "-") == 0)
453     fp = stdin;
454   else {
455     errno = 0;
456     fp = fopen(filename, "r");
457     if (fp == 0)
458       fatal("can't open `%1': %2", filename, strerror(errno));
459   }
460   lex_init(new file_input(fp, filename));
461   if (yyparse())
462     had_parse_error = 1;
463   parse_cleanup();
464   lex_cleanup();
465 }
466 #endif
467
468 void usage(FILE *stream)
469 {
470   fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
471 #ifdef TEX_SUPPORT
472   fprintf(stream, "       %s -t [ -cvzC ] [ filename ... ]\n", program_name);
473 #endif
474 #ifdef FIG_SUPPORT
475   fprintf(stream, "       %s -f [ -v ] [ filename ]\n", program_name);
476 #endif
477 }
478
479 #if defined(__MSDOS__) || defined(__EMX__)
480 static char *fix_program_name(char *arg, char *dflt)
481 {
482   if (!arg)
483     return dflt;
484   char *prog = strchr(arg, '\0');
485   for (;;) {
486     if (prog == arg)
487       break;
488     --prog;
489     if (strchr("\\/:", *prog)) {
490       prog++;
491       break;
492     }
493   }     
494   char *ext = strchr(prog, '.');
495   if (ext)
496     *ext = '\0';
497   for (char *p = prog; *p; p++)
498     if ('A' <= *p && *p <= 'Z')
499       *p = 'a' + (*p - 'A');
500   return prog;
501 }
502 #endif /* __MSDOS__ || __EMX__ */
503
504 int main(int argc, char **argv)
505 {
506 #if defined(__MSDOS__) || defined(__EMX__)
507   argv[0] = fix_program_name(argv[0], "pic");
508 #endif /* __MSDOS__ || __EMX__ */
509   program_name = argv[0];
510   static char stderr_buf[BUFSIZ];
511   setbuf(stderr, stderr_buf);
512   int opt;
513 #ifdef TEX_SUPPORT
514   int tex_flag = 0;
515   int tpic_flag = 0;
516 #endif
517 #ifdef FIG_SUPPORT
518   int whole_file_flag = 0;
519   int fig_flag = 0;
520 #endif
521   static const struct option long_options[] = {
522     { "help", no_argument, 0, CHAR_MAX + 1 },
523     { "version", no_argument, 0, 'v' },
524     { NULL, 0, 0, 0 }
525   };
526   while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL))
527          != EOF)
528     switch (opt) {
529     case 'C':
530       compatible_flag = 1;
531       break;
532     case 'D':
533     case 'T':
534       break;
535     case 'S':
536       safer_flag = 1;
537       break;
538     case 'U':
539       safer_flag = 0;
540       break;
541     case 'f':
542 #ifdef FIG_SUPPORT
543       whole_file_flag++;
544       fig_flag++;
545 #else
546       fatal("fig support not included");
547 #endif
548       break;
549     case 'n':
550       driver_extension_flag = 0;
551       break;
552     case 'p':
553     case 'x':
554       warning("-%1 option is obsolete", char(opt));
555       break;
556     case 't':
557 #ifdef TEX_SUPPORT
558       tex_flag++;
559 #else
560       fatal("TeX support not included");
561 #endif
562       break;
563     case 'c':
564 #ifdef TEX_SUPPORT
565       tpic_flag++;
566 #else
567       fatal("TeX support not included");
568 #endif
569       break;
570     case 'v':
571       {
572         printf("GNU pic (groff) version %s\n", Version_string);
573         exit(0);
574         break;
575       }
576     case 'z':
577       // zero length lines will be printed as dots
578       zero_length_line_flag++;
579       break;
580     case CHAR_MAX + 1: // --help
581       usage(stdout);
582       exit(0);
583       break;
584     case '?':
585       usage(stderr);
586       exit(1);
587       break;
588     default:
589       assert(0);
590     }
591   parse_init();
592 #ifdef TEX_SUPPORT
593   if (tpic_flag) {
594     out = make_tpic_output();
595     lf_flag = 0;
596   }
597   else if (tex_flag) {
598     out = make_tex_output();
599     command_char = '\\';
600     lf_flag = 0;
601   }
602   else
603 #endif
604 #ifdef FIG_SUPPORT
605   if (fig_flag)
606     out = make_fig_output();
607   else
608 #endif
609     out = make_troff_output();
610 #ifdef FIG_SUPPORT
611   if (whole_file_flag) {
612     if (optind >= argc)
613       do_whole_file("-");
614     else if (argc - optind > 1) {
615       usage(stderr);
616       exit(1);
617     } else
618       do_whole_file(argv[optind]);
619   }
620   else {
621 #endif
622     if (optind >= argc)
623       do_file("-");
624     else
625       for (int i = optind; i < argc; i++)
626         do_file(argv[i]);
627 #ifdef FIG_SUPPORT
628   }
629 #endif
630   delete out;
631   if (ferror(stdout) || fflush(stdout) < 0)
632     fatal("output error");
633   return had_parse_error;
634 }
635