Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / preproc / eqn / main.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2005, 2007,
3                  2009
4    Free Software Foundation, Inc.
5      Written by James Clark (jjc@jclark.com)
6
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 #include "eqn.h"
23 #include "stringclass.h"
24 #include "device.h"
25 #include "searchpath.h"
26 #include "macropath.h"
27 #include "htmlhint.h"
28 #include "pbox.h"
29 #include "ctype.h"
30
31 #define STARTUP_FILE "eqnrc"
32
33 extern int yyparse();
34 extern "C" const char *Version_string;
35
36 static char *delim_search    (char *, int);
37 static int   inline_equation (FILE *, string &, string &);
38
39 char start_delim = '\0';
40 char end_delim = '\0';
41 int non_empty_flag;
42 int inline_flag;
43 int draw_flag = 0;
44 int one_size_reduction_flag = 0;
45 int compatible_flag = 0;
46 int no_newline_in_delim_flag = 0;
47 int html = 0;
48 int xhtml = 0;
49 eqnmode_t output_format;
50
51 int read_line(FILE *fp, string *p)
52 {
53   p->clear();
54   int c = -1;
55   while ((c = getc(fp)) != EOF) {
56     if (!invalid_input_char(c))
57       *p += char(c);
58     else
59       error("invalid input character code `%1'", c);
60     if (c == '\n')
61       break;
62   }
63   current_lineno++;
64   return p->length() > 0;
65 }
66
67 void do_file(FILE *fp, const char *filename)
68 {
69   string linebuf;
70   string str;
71   if (output_format == troff)
72     printf(".lf 1 %s\n", filename);
73   current_filename = filename;
74   current_lineno = 0;
75   while (read_line(fp, &linebuf)) {
76     if (linebuf.length() >= 4
77         && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f'
78         && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
79       put_string(linebuf, stdout);
80       linebuf += '\0';
81       if (interpret_lf_args(linebuf.contents() + 3))
82         current_lineno--;
83     }
84     else if (linebuf.length() >= 4
85              && linebuf[0] == '.'
86              && linebuf[1] == 'E'
87              && linebuf[2] == 'Q'
88              && (linebuf[3] == ' ' || linebuf[3] == '\n'
89                  || compatible_flag)) {
90       put_string(linebuf, stdout);
91       int start_lineno = current_lineno + 1;
92       str.clear();
93       for (;;) {
94         if (!read_line(fp, &linebuf))
95           fatal("end of file before .EN");
96         if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') {
97           if (linebuf[2] == 'N'
98               && (linebuf.length() == 3 || linebuf[3] == ' '
99                   || linebuf[3] == '\n' || compatible_flag))
100             break;
101           else if (linebuf[2] == 'Q' && linebuf.length() > 3
102                    && (linebuf[3] == ' ' || linebuf[3] == '\n'
103                        || compatible_flag))
104             fatal("nested .EQ");
105         }
106         str += linebuf;
107       }
108       str += '\0';
109       start_string();
110       init_lex(str.contents(), current_filename, start_lineno);
111       non_empty_flag = 0;
112       inline_flag = 0;
113       yyparse();
114       restore_compatibility();
115       if (non_empty_flag) 
116         if (output_format == mathml)
117           putchar('\n');
118         else {
119           printf(".lf %d\n", current_lineno - 1);
120           output_string();
121         }
122       if (output_format == troff)
123         printf(".lf %d\n", current_lineno);
124       put_string(linebuf, stdout);
125     }
126     else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
127              && inline_equation(fp, linebuf, str))
128       ;
129     else
130       put_string(linebuf, stdout);
131   }
132   current_filename = 0;
133   current_lineno = 0;
134 }
135
136 // Handle an inline equation.  Return 1 if it was an inline equation,
137 // otherwise.
138 static int inline_equation(FILE *fp, string &linebuf, string &str)
139 {
140   linebuf += '\0';
141   char *ptr = &linebuf[0];
142   char *start = delim_search(ptr, start_delim);
143   if (!start) {
144     // It wasn't a delimiter after all.
145     linebuf.set_length(linebuf.length() - 1); // strip the '\0'
146     return 0;
147   }
148   start_string();
149   inline_flag = 1;
150   for (;;) {
151     if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) {
152       error("missing `%1'", end_delim);
153       char *nl = strchr(start + 1, '\n');
154       if (nl != 0)
155         *nl = '\0';
156       do_text(ptr);
157       break;
158     }
159     int start_lineno = current_lineno;
160     *start = '\0';
161     do_text(ptr);
162     ptr = start + 1;
163     str.clear();
164     for (;;) {
165       char *end = strchr(ptr, end_delim);
166       if (end != 0) {
167         *end = '\0';
168         str += ptr;
169         ptr = end + 1;
170         break;
171       }
172       str += ptr;
173       if (!read_line(fp, &linebuf))
174         fatal("unterminated `%1' at line %2, looking for `%3'",
175               start_delim, start_lineno, end_delim);
176       linebuf += '\0';
177       ptr = &linebuf[0];
178     }
179     str += '\0';
180     if (output_format == troff && html) {
181       printf(".as1 %s ", LINE_STRING);
182       html_begin_suppress();
183       printf("\n");
184     }
185     init_lex(str.contents(), current_filename, start_lineno);
186     yyparse();
187     if (output_format == troff && html) {
188       printf(".as1 %s ", LINE_STRING);
189       html_end_suppress();
190       printf("\n");
191     }
192     if (output_format == mathml)
193       printf("\n");
194     if (xhtml) {
195       /* skip leading spaces */
196       while ((*ptr != '\0') && (*ptr == ' '))
197         ptr++;
198     }
199     start = delim_search(ptr, start_delim);
200     if (start == 0) {
201       char *nl = strchr(ptr, '\n');
202       if (nl != 0)
203         *nl = '\0';
204       do_text(ptr);
205       break;
206     }
207   }
208   restore_compatibility();
209   if (output_format == troff)
210     printf(".lf %d\n", current_lineno);
211   output_string();
212   if (output_format == troff)
213     printf(".lf %d\n", current_lineno + 1);
214   return 1;
215 }
216
217 /* Search for delim.  Skip over number register and string names etc. */
218
219 static char *delim_search(char *ptr, int delim)
220 {
221   while (*ptr) {
222     if (*ptr == delim)
223       return ptr;
224     if (*ptr++ == '\\') {
225       switch (*ptr) {
226       case 'n':
227       case '*':
228       case 'f':
229       case 'g':
230       case 'k':
231         switch (*++ptr) {
232         case '\0':
233         case '\\':
234           break;
235         case '(':
236           if (*++ptr != '\\' && *ptr != '\0'
237               && *++ptr != '\\' && *ptr != '\0')
238               ptr++;
239           break;
240         case '[':
241           while (*++ptr != '\0')
242             if (*ptr == ']') {
243               ptr++;
244               break;
245             }
246           break;
247         default:
248           ptr++;
249           break;
250         }
251         break;
252       case '\\':
253       case '\0':
254         break;
255       default:
256         ptr++;
257         break;
258       }
259     }
260   }
261   return 0;
262 }
263
264 void usage(FILE *stream)
265 {
266   fprintf(stream,
267     "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
268     program_name);
269 }
270
271 int main(int argc, char **argv)
272 {
273   program_name = argv[0];
274   static char stderr_buf[BUFSIZ];
275   setbuf(stderr, stderr_buf);
276   int opt;
277   int load_startup_file = 1;
278   static const struct option long_options[] = {
279     { "help", no_argument, 0, CHAR_MAX + 1 },
280     { "version", no_argument, 0, 'v' },
281     { NULL, 0, 0, 0 }
282   };
283   while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options,
284                             NULL))
285          != EOF)
286     switch (opt) {
287     case 'C':
288       compatible_flag = 1;
289       break;
290     case 'R':                   // don't load eqnrc
291       load_startup_file = 0;
292       break;
293     case 'M':
294       config_macro_path.command_line_dir(optarg);
295       break;
296     case 'v':
297       printf("GNU eqn (groff) version %s\n", Version_string);
298       exit(0);
299       break;
300     case 'd':
301       if (optarg[0] == '\0' || optarg[1] == '\0')
302         error("-d requires two character argument");
303       else if (invalid_input_char(optarg[0]))
304         error("bad delimiter `%1'", optarg[0]);
305       else if (invalid_input_char(optarg[1]))
306         error("bad delimiter `%1'", optarg[1]);
307       else {
308         start_delim = optarg[0];
309         end_delim = optarg[1];
310       }
311       break;
312     case 'f':
313       set_gfont(optarg);
314       break;
315     case 'T':
316       device = optarg;
317       if (strcmp(device, "ps:html") == 0) {
318         device = "ps";
319         html = 1;
320       }
321       else if (strcmp(device, "MathML") == 0) {
322         output_format = mathml;
323         load_startup_file = 0;
324       }
325       else if (strcmp(device, "mathml:xhtml") == 0) {
326         device = "MathML";
327         output_format = mathml;
328         load_startup_file = 0;
329         xhtml = 1;
330       }
331       break;
332     case 's':
333       if (!set_gsize(optarg))
334         error("invalid size `%1'", optarg);
335       break;
336     case 'p':
337       {
338         int n;
339         if (sscanf(optarg, "%d", &n) == 1)
340           set_script_reduction(n);
341         else
342           error("bad size `%1'", optarg);
343       }
344       break;
345     case 'm':
346       {
347         int n;
348         if (sscanf(optarg, "%d", &n) == 1)
349           set_minimum_size(n);
350         else
351           error("bad size `%1'", optarg);
352       }
353       break;
354     case 'r':
355       one_size_reduction_flag = 1;
356       break;
357     case 'D':
358       warning("-D option is obsolete: use `set draw_lines 1' instead");
359       draw_flag = 1;
360       break;
361     case 'N':
362       no_newline_in_delim_flag = 1;
363       break;
364     case CHAR_MAX + 1: // --help
365       usage(stdout);
366       exit(0);
367       break;
368     case '?':
369       usage(stderr);
370       exit(1);
371       break;
372     default:
373       assert(0);
374     }
375   init_table(device);
376   init_char_table();
377   if (output_format == troff) {
378     printf(".if !'\\*(.T'%s' "
379            ".if !'\\*(.T'html' "        // the html device uses `-Tps' to render
380                                   // equations as images
381            ".tm warning: %s should have been given a `-T\\*(.T' option\n",
382            device, program_name);
383     printf(".if '\\*(.T'html' "
384            ".if !'%s'ps' "
385            ".tm warning: %s should have been given a `-Tps' option\n",
386            device, program_name);
387     printf(".if '\\*(.T'html' "
388            ".if !'%s'ps' "
389            ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n",
390            device);
391   }
392   if (load_startup_file) {
393     char *path;
394     FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path);
395     if (fp) {
396       do_file(fp, path);
397       fclose(fp);
398       a_delete path;
399     }
400   }
401   if (optind >= argc)
402     do_file(stdin, "-");
403   else
404     for (int i = optind; i < argc; i++)
405       if (strcmp(argv[i], "-") == 0)
406         do_file(stdin, "-");
407       else {
408         errno = 0;
409         FILE *fp = fopen(argv[i], "r");
410         if (!fp)
411           fatal("can't open `%1': %2", argv[i], strerror(errno));
412         else {
413           do_file(fp, argv[i]);
414           fclose(fp);
415         }
416       }
417   if (ferror(stdout) || fflush(stdout) < 0)
418     fatal("output error");
419   return 0;
420 }