groff: remove version tag from directory
[dragonfly.git] / contrib / groff / src / preproc / eqn / main.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
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 "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                       
49
50 int read_line(FILE *fp, string *p)
51 {
52   p->clear();
53   int c = -1;
54   while ((c = getc(fp)) != EOF) {
55     if (!invalid_input_char(c))
56       *p += char(c);
57     else
58       error("invalid input character code `%1'", c);
59     if (c == '\n')
60       break;
61   }
62   current_lineno++;
63   return p->length() > 0;
64 }
65
66 void do_file(FILE *fp, const char *filename)
67 {
68   string linebuf;
69   string str;
70   printf(".lf 1 %s\n", filename);
71   current_filename = filename;
72   current_lineno = 0;
73   while (read_line(fp, &linebuf)) {
74     if (linebuf.length() >= 4
75         && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f'
76         && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
77       put_string(linebuf, stdout);
78       linebuf += '\0';
79       if (interpret_lf_args(linebuf.contents() + 3))
80         current_lineno--;
81     }
82     else if (linebuf.length() >= 4
83              && linebuf[0] == '.'
84              && linebuf[1] == 'E'
85              && linebuf[2] == 'Q'
86              && (linebuf[3] == ' ' || linebuf[3] == '\n'
87                  || compatible_flag)) {
88       put_string(linebuf, stdout);
89       int start_lineno = current_lineno + 1;
90       str.clear();
91       for (;;) {
92         if (!read_line(fp, &linebuf))
93           fatal("end of file before .EN");
94         if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') {
95           if (linebuf[2] == 'N'
96               && (linebuf.length() == 3 || linebuf[3] == ' '
97                   || linebuf[3] == '\n' || compatible_flag))
98             break;
99           else if (linebuf[2] == 'Q' && linebuf.length() > 3
100                    && (linebuf[3] == ' ' || linebuf[3] == '\n'
101                        || compatible_flag))
102             fatal("nested .EQ");
103         }
104         str += linebuf;
105       }
106       str += '\0';
107       start_string();
108       init_lex(str.contents(), current_filename, start_lineno);
109       non_empty_flag = 0;
110       inline_flag = 0;
111       yyparse();
112       restore_compatibility();
113       if (non_empty_flag) {
114         printf(".lf %d\n", current_lineno - 1);
115         output_string();
116       }
117       printf(".lf %d\n", current_lineno);
118       put_string(linebuf, stdout);
119     }
120     else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
121              && inline_equation(fp, linebuf, str))
122       ;
123     else
124       put_string(linebuf, stdout);
125   }
126   current_filename = 0;
127   current_lineno = 0;
128 }
129
130 // Handle an inline equation.  Return 1 if it was an inline equation,
131 // otherwise.
132 static int inline_equation(FILE *fp, string &linebuf, string &str)
133 {
134   linebuf += '\0';
135   char *ptr = &linebuf[0];
136   char *start = delim_search(ptr, start_delim);
137   if (!start) {
138     // It wasn't a delimiter after all.
139     linebuf.set_length(linebuf.length() - 1); // strip the '\0'
140     return 0;
141   }
142   start_string();
143   inline_flag = 1;
144   for (;;) {
145     if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) {
146       error("missing `%1'", end_delim);
147       char *nl = strchr(start + 1, '\n');
148       if (nl != 0)
149         *nl = '\0';
150       do_text(ptr);
151       break;
152     }
153     int start_lineno = current_lineno;
154     *start = '\0';
155     do_text(ptr);
156     ptr = start + 1;
157     str.clear();
158     for (;;) {
159       char *end = strchr(ptr, end_delim);
160       if (end != 0) {
161         *end = '\0';
162         str += ptr;
163         ptr = end + 1;
164         break;
165       }
166       str += ptr;
167       if (!read_line(fp, &linebuf))
168         fatal("unterminated `%1' at line %2, looking for `%3'",
169               start_delim, start_lineno, end_delim);
170       linebuf += '\0';
171       ptr = &linebuf[0];
172     }
173     str += '\0';
174     if (html) {
175       printf(".as1 %s ", LINE_STRING);
176       html_begin_suppress();
177       printf("\n");
178     }
179     init_lex(str.contents(), current_filename, start_lineno);
180     yyparse();
181     if (html) {
182       printf(".as1 %s ", LINE_STRING);
183       html_end_suppress();
184       printf("\n");
185     }
186     start = delim_search(ptr, start_delim);
187     if (start == 0) {
188       char *nl = strchr(ptr, '\n');
189       if (nl != 0)
190         *nl = '\0';
191       do_text(ptr);
192       break;
193     }
194   }
195   restore_compatibility();
196   printf(".lf %d\n", current_lineno);
197   output_string();
198   printf(".lf %d\n", current_lineno + 1);
199   return 1;
200 }
201
202 /* Search for delim.  Skip over number register and string names etc. */
203
204 static char *delim_search(char *ptr, int delim)
205 {
206   while (*ptr) {
207     if (*ptr == delim)
208       return ptr;
209     if (*ptr++ == '\\') {
210       switch (*ptr) {
211       case 'n':
212       case '*':
213       case 'f':
214       case 'g':
215       case 'k':
216         switch (*++ptr) {
217         case '\0':
218         case '\\':
219           break;
220         case '(':
221           if (*++ptr != '\\' && *ptr != '\0'
222               && *++ptr != '\\' && *ptr != '\0')
223               ptr++;
224           break;
225         case '[':
226           while (*++ptr != '\0')
227             if (*ptr == ']') {
228               ptr++;
229               break;
230             }
231           break;
232         default:
233           ptr++;
234           break;
235         }
236         break;
237       case '\\':
238       case '\0':
239         break;
240       default:
241         ptr++;
242         break;
243       }
244     }
245   }
246   return 0;
247 }
248
249 void usage(FILE *stream)
250 {
251   fprintf(stream,
252     "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
253     program_name);
254 }
255
256 int main(int argc, char **argv)
257 {
258   program_name = argv[0];
259   static char stderr_buf[BUFSIZ];
260   setbuf(stderr, stderr_buf);
261   int opt;
262   int load_startup_file = 1;
263   static const struct option long_options[] = {
264     { "help", no_argument, 0, CHAR_MAX + 1 },
265     { "version", no_argument, 0, 'v' },
266     { NULL, 0, 0, 0 }
267   };
268   while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options,
269                             NULL))
270          != EOF)
271     switch (opt) {
272     case 'C':
273       compatible_flag = 1;
274       break;
275     case 'R':                   // don't load eqnrc
276       load_startup_file = 0;
277       break;
278     case 'M':
279       config_macro_path.command_line_dir(optarg);
280       break;
281     case 'v':
282       {
283         printf("GNU eqn (groff) version %s\n", Version_string);
284         exit(0);
285         break;
286       }
287     case 'd':
288       if (optarg[0] == '\0' || optarg[1] == '\0')
289         error("-d requires two character argument");
290       else if (invalid_input_char(optarg[0]))
291         error("bad delimiter `%1'", optarg[0]);
292       else if (invalid_input_char(optarg[1]))
293         error("bad delimiter `%1'", optarg[1]);
294       else {
295         start_delim = optarg[0];
296         end_delim = optarg[1];
297       }
298       break;
299     case 'f':
300       set_gfont(optarg);
301       break;
302     case 'T':
303       device = optarg;
304       if (strcmp(device, "ps:html") == 0) {
305         device = "ps";
306         html = 1;
307       }
308       break;
309     case 's':
310       if (!set_gsize(optarg))
311         error("invalid size `%1'", optarg);
312       break;
313     case 'p':
314       {
315         int n;
316         if (sscanf(optarg, "%d", &n) == 1)
317           set_script_reduction(n);
318         else
319           error("bad size `%1'", optarg);
320       }
321       break;
322     case 'm':
323       {
324         int n;
325         if (sscanf(optarg, "%d", &n) == 1)
326           set_minimum_size(n);
327         else
328           error("bad size `%1'", optarg);
329       }
330       break;
331     case 'r':
332       one_size_reduction_flag = 1;
333       break;
334     case 'D':
335       warning("-D option is obsolete: use `set draw_lines 1' instead");
336       draw_flag = 1;
337       break;
338     case 'N':
339       no_newline_in_delim_flag = 1;
340       break;
341     case CHAR_MAX + 1: // --help
342       usage(stdout);
343       exit(0);
344       break;
345     case '?':
346       usage(stderr);
347       exit(1);
348       break;
349     default:
350       assert(0);
351     }
352   init_table(device);
353   init_char_table();
354   printf(".if !'\\*(.T'%s' "
355          ".if !'\\*(.T'html' "  // the html device uses `-Tps' to render
356                                 // equations as images
357          ".tm warning: %s should have been given a `-T\\*(.T' option\n",
358          device, program_name);
359   printf(".if '\\*(.T'html' "
360          ".if !'%s'ps' "
361          ".tm warning: %s should have been given a `-Tps' option\n",
362          device, program_name);
363   printf(".if '\\*(.T'html' "
364          ".if !'%s'ps' "
365          ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n",
366          device);
367   if (load_startup_file) {
368     char *path;
369     FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path);
370     if (fp) {
371       do_file(fp, path);
372       fclose(fp);
373       a_delete path;
374     }
375   }
376   if (optind >= argc)
377     do_file(stdin, "-");
378   else
379     for (int i = optind; i < argc; i++)
380       if (strcmp(argv[i], "-") == 0)
381         do_file(stdin, "-");
382       else {
383         errno = 0;
384         FILE *fp = fopen(argv[i], "r");
385         if (!fp)
386           fatal("can't open `%1': %2", argv[i], strerror(errno));
387         else {
388           do_file(fp, argv[i]);
389           fclose(fp);
390         }
391       }
392   if (ferror(stdout) || fflush(stdout) < 0)
393     fatal("output error");
394   return 0;
395 }