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