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