1114402Sru// -*- C++ -*-
2114402Sru/* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "pic.h"
23114402Sru
24114402Sruextern int yyparse();
25114402Sruextern "C" const char *Version_string;
26114402Sru
27114402Sruoutput *out;
28114402Sruchar *graphname;		// the picture box name in TeX mode
29114402Sru
30114402Sruint flyback_flag;
31114402Sruint zero_length_line_flag = 0;
32114402Sru// Non-zero means we're using a groff driver.
33114402Sruint driver_extension_flag = 1;
34114402Sruint compatible_flag = 0;
35114402Sruint safer_flag = 1;
36114402Sruint command_char = '.';		// the character that introduces lines
37114402Sru				// that should be passed through tranparently
38114402Srustatic int lf_flag = 1;		// non-zero if we should attempt to understand
39114402Sru				// lines beginning with `.lf'
40114402Sru
41114402Sru// Non-zero means a parse error was encountered.
42114402Srustatic int had_parse_error = 0;
43114402Sru
44114402Sruvoid do_file(const char *filename);
45114402Sru
46114402Sruclass top_input : public input {
47114402Sru  FILE *fp;
48114402Sru  int bol;
49114402Sru  int eof;
50114402Sru  int push_back[3];
51114402Sru  int start_lineno;
52114402Srupublic:
53114402Sru  top_input(FILE *);
54114402Sru  int get();
55114402Sru  int peek();
56114402Sru  int get_location(const char **, int *);
57114402Sru};
58114402Sru
59114402Srutop_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
60114402Sru{
61114402Sru  push_back[0] = push_back[1] = push_back[2] = EOF;
62114402Sru  start_lineno = current_lineno;
63114402Sru}
64114402Sru
65114402Sruint top_input::get()
66114402Sru{
67114402Sru  if (eof)
68114402Sru    return EOF;
69114402Sru  if (push_back[2] != EOF) {
70114402Sru    int c = push_back[2];
71114402Sru    push_back[2] = EOF;
72114402Sru    return c;
73114402Sru  }
74114402Sru  else if (push_back[1] != EOF) {
75114402Sru    int c = push_back[1];
76114402Sru    push_back[1] = EOF;
77114402Sru    return c;
78114402Sru  }
79114402Sru  else if (push_back[0] != EOF) {
80114402Sru    int c = push_back[0];
81114402Sru    push_back[0] = EOF;
82114402Sru    return c;
83114402Sru  }
84114402Sru  int c = getc(fp);
85114402Sru  while (invalid_input_char(c)) {
86114402Sru    error("invalid input character code %1", int(c));
87114402Sru    c = getc(fp);
88114402Sru    bol = 0;
89114402Sru  }
90114402Sru  if (bol && c == '.') {
91114402Sru    c = getc(fp);
92114402Sru    if (c == 'P') {
93114402Sru      c = getc(fp);
94114402Sru      if (c == 'F' || c == 'E') {
95114402Sru	int d = getc(fp);
96114402Sru	if (d != EOF)
97114402Sru	  ungetc(d, fp);
98114402Sru	if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
99114402Sru	  eof = 1;
100114402Sru	  flyback_flag = c == 'F';
101114402Sru	  return EOF;
102114402Sru	}
103114402Sru	push_back[0] = c;
104114402Sru	push_back[1] = 'P';
105114402Sru	return '.';
106114402Sru      }
107114402Sru      if (c == 'S') {
108114402Sru	c = getc(fp);
109114402Sru	if (c != EOF)
110114402Sru	  ungetc(c, fp);
111114402Sru	if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
112114402Sru	  error("nested .PS");
113114402Sru	  eof = 1;
114114402Sru	  return EOF;
115114402Sru	}
116114402Sru	push_back[0] = 'S';
117114402Sru	push_back[1] = 'P';
118114402Sru	return '.';
119114402Sru      }
120114402Sru      if (c != EOF)
121114402Sru	ungetc(c, fp);
122114402Sru      push_back[0] = 'P';
123114402Sru      return '.';
124114402Sru    }
125114402Sru    else {
126114402Sru      if (c != EOF)
127114402Sru	ungetc(c, fp);
128114402Sru      return '.';
129114402Sru    }
130114402Sru  }
131114402Sru  if (c == '\n') {
132114402Sru    bol = 1;
133114402Sru    current_lineno++;
134114402Sru    return '\n';
135114402Sru  }
136114402Sru  bol = 0;
137114402Sru  if (c == EOF) {
138114402Sru    eof = 1;
139114402Sru    error("end of file before .PE or .PF");
140114402Sru    error_with_file_and_line(current_filename, start_lineno - 1,
141114402Sru			     ".PS was here");
142114402Sru  }
143114402Sru  return c;
144114402Sru}
145114402Sru
146114402Sruint top_input::peek()
147114402Sru{
148114402Sru  if (eof)
149114402Sru    return EOF;
150114402Sru  if (push_back[2] != EOF)
151114402Sru    return push_back[2];
152114402Sru  if (push_back[1] != EOF)
153114402Sru    return push_back[1];
154114402Sru  if (push_back[0] != EOF)
155114402Sru    return push_back[0];
156114402Sru  int c = getc(fp);
157114402Sru  while (invalid_input_char(c)) {
158114402Sru    error("invalid input character code %1", int(c));
159114402Sru    c = getc(fp);
160114402Sru    bol = 0;
161114402Sru  }
162114402Sru  if (bol && c == '.') {
163114402Sru    c = getc(fp);
164114402Sru    if (c == 'P') {
165114402Sru      c = getc(fp);
166114402Sru      if (c == 'F' || c == 'E') {
167114402Sru	int d = getc(fp);
168114402Sru	if (d != EOF)
169114402Sru	  ungetc(d, fp);
170114402Sru	if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
171114402Sru	  eof = 1;
172114402Sru	  flyback_flag = c == 'F';
173114402Sru	  return EOF;
174114402Sru	}
175114402Sru	push_back[0] = c;
176114402Sru	push_back[1] = 'P';
177114402Sru	push_back[2] = '.';
178114402Sru	return '.';
179114402Sru      }
180114402Sru      if (c == 'S') {
181114402Sru	c = getc(fp);
182114402Sru	if (c != EOF)
183114402Sru	  ungetc(c, fp);
184114402Sru	if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
185114402Sru	  error("nested .PS");
186114402Sru	  eof = 1;
187114402Sru	  return EOF;
188114402Sru	}
189114402Sru	push_back[0] = 'S';
190114402Sru	push_back[1] = 'P';
191114402Sru	push_back[2] = '.';
192114402Sru	return '.';
193114402Sru      }
194114402Sru      if (c != EOF)
195114402Sru	ungetc(c, fp);
196114402Sru      push_back[0] = 'P';
197114402Sru      push_back[1] = '.';
198114402Sru      return '.';
199114402Sru    }
200114402Sru    else {
201114402Sru      if (c != EOF)
202114402Sru	ungetc(c, fp);
203114402Sru      push_back[0] = '.';
204114402Sru      return '.';
205114402Sru    }
206114402Sru  }
207114402Sru  if (c != EOF)
208114402Sru    ungetc(c, fp);
209114402Sru  if (c == '\n')
210114402Sru    return '\n';
211114402Sru  return c;
212114402Sru}
213114402Sru
214114402Sruint top_input::get_location(const char **filenamep, int *linenop)
215114402Sru{
216114402Sru  *filenamep = current_filename;
217114402Sru  *linenop = current_lineno;
218114402Sru  return 1;
219114402Sru}
220114402Sru
221114402Sruvoid do_picture(FILE *fp)
222114402Sru{
223114402Sru  flyback_flag = 0;
224114402Sru  int c;
225114402Sru  a_delete graphname;
226114402Sru  graphname = strsave("graph");		// default picture name in TeX mode
227114402Sru  while ((c = getc(fp)) == ' ')
228114402Sru    ;
229114402Sru  if (c == '<') {
230114402Sru    string filename;
231114402Sru    while ((c = getc(fp)) == ' ')
232114402Sru      ;
233114402Sru    while (c != EOF && c != ' ' && c != '\n') {
234114402Sru      filename += char(c);
235114402Sru      c = getc(fp);
236114402Sru    }
237114402Sru    if (c == ' ') {
238114402Sru      do {
239114402Sru	c = getc(fp);
240114402Sru      } while (c != EOF && c != '\n');
241114402Sru    }
242114402Sru    if (c == '\n')
243114402Sru      current_lineno++;
244114402Sru    if (filename.length() == 0)
245114402Sru      error("missing filename after `<'");
246114402Sru    else {
247114402Sru      filename += '\0';
248114402Sru      const char *old_filename = current_filename;
249114402Sru      int old_lineno = current_lineno;
250114402Sru      // filenames must be permanent
251114402Sru      do_file(strsave(filename.contents()));
252114402Sru      current_filename = old_filename;
253114402Sru      current_lineno = old_lineno;
254114402Sru    }
255114402Sru    out->set_location(current_filename, current_lineno);
256114402Sru  }
257114402Sru  else {
258114402Sru    out->set_location(current_filename, current_lineno);
259114402Sru    string start_line;
260114402Sru    while (c != EOF) {
261114402Sru      if (c == '\n') {
262114402Sru	current_lineno++;
263114402Sru	break;
264114402Sru      }
265114402Sru      start_line += c;
266114402Sru      c = getc(fp);
267114402Sru    }
268114402Sru    if (c == EOF)
269114402Sru      return;
270114402Sru    start_line += '\0';
271114402Sru    double wid, ht;
272114402Sru    switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
273114402Sru    case 1:
274114402Sru      ht = 0.0;
275114402Sru      break;
276114402Sru    case 2:
277114402Sru      break;
278114402Sru    default:
279114402Sru      ht = wid = 0.0;
280114402Sru      break;
281114402Sru    }
282114402Sru    out->set_desired_width_height(wid, ht);
283114402Sru    out->set_args(start_line.contents());
284114402Sru    lex_init(new top_input(fp));
285114402Sru    if (yyparse()) {
286114402Sru      had_parse_error = 1;
287114402Sru      lex_error("giving up on this picture");
288114402Sru    }
289114402Sru    parse_cleanup();
290114402Sru    lex_cleanup();
291114402Sru
292114402Sru    // skip the rest of the .PF/.PE line
293114402Sru    while ((c = getc(fp)) != EOF && c != '\n')
294114402Sru      ;
295114402Sru    if (c == '\n')
296114402Sru      current_lineno++;
297114402Sru    out->set_location(current_filename, current_lineno);
298114402Sru  }
299114402Sru}
300114402Sru
301114402Sruvoid do_file(const char *filename)
302114402Sru{
303114402Sru  FILE *fp;
304114402Sru  if (strcmp(filename, "-") == 0)
305114402Sru    fp = stdin;
306114402Sru  else {
307114402Sru    errno = 0;
308114402Sru    fp = fopen(filename, "r");
309114402Sru    if (fp == 0) {
310114402Sru      delete out;
311114402Sru      fatal("can't open `%1': %2", filename, strerror(errno));
312114402Sru    }
313114402Sru  }
314114402Sru  out->set_location(filename, 1);
315114402Sru  current_filename = filename;
316114402Sru  current_lineno = 1;
317114402Sru  enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
318114402Sru  for (;;) {
319114402Sru    int c = getc(fp);
320114402Sru    if (c == EOF)
321114402Sru      break;
322114402Sru    switch (state) {
323114402Sru    case START:
324114402Sru      if (c == '.')
325114402Sru	state = HAD_DOT;
326114402Sru      else {
327114402Sru	putchar(c);
328114402Sru	if (c == '\n') {
329114402Sru	  current_lineno++;
330114402Sru	  state = START;
331114402Sru	}
332114402Sru	else
333114402Sru	  state = MIDDLE;
334114402Sru      }
335114402Sru      break;
336114402Sru    case MIDDLE:
337114402Sru      putchar(c);
338114402Sru      if (c == '\n') {
339114402Sru	current_lineno++;
340114402Sru	state = START;
341114402Sru      }
342114402Sru      break;
343114402Sru    case HAD_DOT:
344114402Sru      if (c == 'P')
345114402Sru	state = HAD_P;
346114402Sru      else if (lf_flag && c == 'l')
347114402Sru	state = HAD_l;
348114402Sru      else {
349114402Sru	putchar('.');
350114402Sru	putchar(c);
351114402Sru	if (c == '\n') {
352114402Sru	  current_lineno++;
353114402Sru	  state = START;
354114402Sru	}
355114402Sru	else
356114402Sru	  state = MIDDLE;
357114402Sru      }
358114402Sru      break;
359114402Sru    case HAD_P:
360114402Sru      if (c == 'S')
361114402Sru	state = HAD_PS;
362114402Sru      else  {
363114402Sru	putchar('.');
364114402Sru	putchar('P');
365114402Sru	putchar(c);
366114402Sru	if (c == '\n') {
367114402Sru	  current_lineno++;
368114402Sru	  state = START;
369114402Sru	}
370114402Sru	else
371114402Sru	  state = MIDDLE;
372114402Sru      }
373114402Sru      break;
374114402Sru    case HAD_PS:
375114402Sru      if (c == ' ' || c == '\n' || compatible_flag) {
376114402Sru	ungetc(c, fp);
377114402Sru	do_picture(fp);
378114402Sru	state = START;
379114402Sru      }
380114402Sru      else {
381114402Sru	fputs(".PS", stdout);
382114402Sru	putchar(c);
383114402Sru	state = MIDDLE;
384114402Sru      }
385114402Sru      break;
386114402Sru    case HAD_l:
387114402Sru      if (c == 'f')
388114402Sru	state = HAD_lf;
389114402Sru      else {
390114402Sru	putchar('.');
391114402Sru	putchar('l');
392114402Sru	putchar(c);
393114402Sru	if (c == '\n') {
394114402Sru	  current_lineno++;
395114402Sru	  state = START;
396114402Sru	}
397114402Sru	else
398114402Sru	  state = MIDDLE;
399114402Sru      }
400114402Sru      break;
401114402Sru    case HAD_lf:
402114402Sru      if (c == ' ' || c == '\n' || compatible_flag) {
403114402Sru	string line;
404114402Sru	while (c != EOF) {
405114402Sru	  line += c;
406114402Sru	  if (c == '\n') {
407114402Sru	    current_lineno++;
408114402Sru	    break;
409114402Sru	  }
410114402Sru	  c = getc(fp);
411114402Sru	}
412114402Sru	line += '\0';
413114402Sru	interpret_lf_args(line.contents());
414114402Sru	printf(".lf%s", line.contents());
415114402Sru	state = START;
416114402Sru      }
417114402Sru      else {
418114402Sru	fputs(".lf", stdout);
419114402Sru	putchar(c);
420114402Sru	state = MIDDLE;
421114402Sru      }
422114402Sru      break;
423114402Sru    default:
424114402Sru      assert(0);
425114402Sru    }
426114402Sru  }
427114402Sru  switch (state) {
428114402Sru  case START:
429114402Sru    break;
430114402Sru  case MIDDLE:
431114402Sru    putchar('\n');
432114402Sru    break;
433114402Sru  case HAD_DOT:
434114402Sru    fputs(".\n", stdout);
435114402Sru    break;
436114402Sru  case HAD_P:
437114402Sru    fputs(".P\n", stdout);
438114402Sru    break;
439114402Sru  case HAD_PS:
440114402Sru    fputs(".PS\n", stdout);
441114402Sru    break;
442114402Sru  case HAD_l:
443114402Sru    fputs(".l\n", stdout);
444114402Sru    break;
445114402Sru  case HAD_lf:
446114402Sru    fputs(".lf\n", stdout);
447114402Sru    break;
448114402Sru  }
449114402Sru  if (fp != stdin)
450114402Sru    fclose(fp);
451114402Sru}
452114402Sru
453114402Sru#ifdef FIG_SUPPORT
454114402Sruvoid do_whole_file(const char *filename)
455114402Sru{
456114402Sru  // Do not set current_filename.
457114402Sru  FILE *fp;
458114402Sru  if (strcmp(filename, "-") == 0)
459114402Sru    fp = stdin;
460114402Sru  else {
461114402Sru    errno = 0;
462114402Sru    fp = fopen(filename, "r");
463114402Sru    if (fp == 0)
464114402Sru      fatal("can't open `%1': %2", filename, strerror(errno));
465114402Sru  }
466114402Sru  lex_init(new file_input(fp, filename));
467114402Sru  if (yyparse())
468114402Sru    had_parse_error = 1;
469114402Sru  parse_cleanup();
470114402Sru  lex_cleanup();
471114402Sru}
472114402Sru#endif
473114402Sru
474114402Sruvoid usage(FILE *stream)
475114402Sru{
476114402Sru  fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
477114402Sru#ifdef TEX_SUPPORT
478114402Sru  fprintf(stream, "       %s -t [ -cvzC ] [ filename ... ]\n", program_name);
479114402Sru#endif
480114402Sru#ifdef FIG_SUPPORT
481114402Sru  fprintf(stream, "       %s -f [ -v ] [ filename ]\n", program_name);
482114402Sru#endif
483114402Sru}
484114402Sru
485114402Sru#if defined(__MSDOS__) || defined(__EMX__)
486114402Srustatic char *fix_program_name(char *arg, char *dflt)
487114402Sru{
488114402Sru  if (!arg)
489114402Sru    return dflt;
490114402Sru  char *prog = strchr(arg, '\0');
491114402Sru  for (;;) {
492114402Sru    if (prog == arg)
493114402Sru      break;
494114402Sru    --prog;
495114402Sru    if (strchr("\\/:", *prog)) {
496114402Sru      prog++;
497114402Sru      break;
498114402Sru    }
499114402Sru  }
500114402Sru  char *ext = strchr(prog, '.');
501114402Sru  if (ext)
502114402Sru    *ext = '\0';
503114402Sru  for (char *p = prog; *p; p++)
504114402Sru    if ('A' <= *p && *p <= 'Z')
505114402Sru      *p = 'a' + (*p - 'A');
506114402Sru  return prog;
507114402Sru}
508114402Sru#endif /* __MSDOS__ || __EMX__ */
509114402Sru
510114402Sruint main(int argc, char **argv)
511114402Sru{
512114402Sru  setlocale(LC_NUMERIC, "C");
513114402Sru#if defined(__MSDOS__) || defined(__EMX__)
514114402Sru  argv[0] = fix_program_name(argv[0], "pic");
515114402Sru#endif /* __MSDOS__ || __EMX__ */
516114402Sru  program_name = argv[0];
517114402Sru  static char stderr_buf[BUFSIZ];
518114402Sru  setbuf(stderr, stderr_buf);
519114402Sru  int opt;
520114402Sru#ifdef TEX_SUPPORT
521114402Sru  int tex_flag = 0;
522114402Sru  int tpic_flag = 0;
523114402Sru#endif
524114402Sru#ifdef FIG_SUPPORT
525114402Sru  int whole_file_flag = 0;
526114402Sru  int fig_flag = 0;
527114402Sru#endif
528114402Sru  static const struct option long_options[] = {
529114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
530114402Sru    { "version", no_argument, 0, 'v' },
531114402Sru    { NULL, 0, 0, 0 }
532114402Sru  };
533114402Sru  while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL))
534114402Sru	 != EOF)
535114402Sru    switch (opt) {
536114402Sru    case 'C':
537114402Sru      compatible_flag = 1;
538114402Sru      break;
539114402Sru    case 'D':
540114402Sru    case 'T':
541114402Sru      break;
542114402Sru    case 'S':
543114402Sru      safer_flag = 1;
544114402Sru      break;
545114402Sru    case 'U':
546114402Sru      safer_flag = 0;
547114402Sru      break;
548114402Sru    case 'f':
549114402Sru#ifdef FIG_SUPPORT
550114402Sru      whole_file_flag++;
551114402Sru      fig_flag++;
552114402Sru#else
553114402Sru      fatal("fig support not included");
554114402Sru#endif
555114402Sru      break;
556114402Sru    case 'n':
557114402Sru      driver_extension_flag = 0;
558114402Sru      break;
559114402Sru    case 'p':
560114402Sru    case 'x':
561114402Sru      warning("-%1 option is obsolete", char(opt));
562114402Sru      break;
563114402Sru    case 't':
564114402Sru#ifdef TEX_SUPPORT
565114402Sru      tex_flag++;
566114402Sru#else
567114402Sru      fatal("TeX support not included");
568114402Sru#endif
569114402Sru      break;
570114402Sru    case 'c':
571114402Sru#ifdef TEX_SUPPORT
572114402Sru      tpic_flag++;
573114402Sru#else
574114402Sru      fatal("TeX support not included");
575114402Sru#endif
576114402Sru      break;
577114402Sru    case 'v':
578114402Sru      {
579114402Sru	printf("GNU pic (groff) version %s\n", Version_string);
580114402Sru	exit(0);
581114402Sru	break;
582114402Sru      }
583114402Sru    case 'z':
584114402Sru      // zero length lines will be printed as dots
585114402Sru      zero_length_line_flag++;
586114402Sru      break;
587114402Sru    case CHAR_MAX + 1: // --help
588114402Sru      usage(stdout);
589114402Sru      exit(0);
590114402Sru      break;
591114402Sru    case '?':
592114402Sru      usage(stderr);
593114402Sru      exit(1);
594114402Sru      break;
595114402Sru    default:
596114402Sru      assert(0);
597114402Sru    }
598114402Sru  parse_init();
599114402Sru#ifdef TEX_SUPPORT
600114402Sru  if (tpic_flag) {
601114402Sru    out = make_tpic_output();
602114402Sru    lf_flag = 0;
603114402Sru  }
604114402Sru  else if (tex_flag) {
605114402Sru    out = make_tex_output();
606114402Sru    command_char = '\\';
607114402Sru    lf_flag = 0;
608114402Sru  }
609114402Sru  else
610114402Sru#endif
611114402Sru#ifdef FIG_SUPPORT
612114402Sru  if (fig_flag)
613114402Sru    out = make_fig_output();
614114402Sru  else
615114402Sru#endif
616114402Sru    out = make_troff_output();
617114402Sru#ifdef FIG_SUPPORT
618114402Sru  if (whole_file_flag) {
619114402Sru    if (optind >= argc)
620114402Sru      do_whole_file("-");
621114402Sru    else if (argc - optind > 1) {
622114402Sru      usage(stderr);
623114402Sru      exit(1);
624114402Sru    } else
625114402Sru      do_whole_file(argv[optind]);
626114402Sru  }
627114402Sru  else {
628114402Sru#endif
629114402Sru    if (optind >= argc)
630114402Sru      do_file("-");
631114402Sru    else
632114402Sru      for (int i = optind; i < argc; i++)
633114402Sru	do_file(argv[i]);
634114402Sru#ifdef FIG_SUPPORT
635114402Sru  }
636114402Sru#endif
637114402Sru  delete out;
638114402Sru  if (ferror(stdout) || fflush(stdout) < 0)
639114402Sru    fatal("output error");
640114402Sru  return had_parse_error;
641114402Sru}
642114402Sru
643