file_case: add read interface, extend abstraction..
[s-roff.git] / src / preproc / eqn / main.cpp
blob974a8d108d40edf3273635ce94e240dc97a83ff0
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2005, 2007
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
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.
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.
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. */
22 #include "eqn.h"
23 #include "stringclass.h"
24 #include "device.h"
25 #include "file_case.h"
26 #include "searchpath.h"
27 #include "macropath.h"
28 #include "htmlhint.h"
29 #include "pbox.h"
30 #include "ctype.h"
32 #define STARTUP_FILE "eqnrc"
34 extern int yyparse();
35 extern "C" const char *Version_string;
37 static char *delim_search (char *, int);
38 static int inline_equation (file_case *, string &, string &);
40 char start_delim = '\0';
41 char end_delim = '\0';
42 int non_empty_flag;
43 int inline_flag;
44 int draw_flag = 0;
45 int one_size_reduction_flag = 0;
46 int compatible_flag = 0;
47 int no_newline_in_delim_flag = 0;
48 int html = 0;
49 int xhtml = 0;
50 eqnmode_t output_format;
52 int read_line(file_case *fcp, string *p)
54 p->clear();
55 int c = -1;
56 while ((c = fcp->get_c()) != EOF) {
57 if (!invalid_input_char(c))
58 *p += char(c);
59 else
60 error("invalid input character code `%1'", c);
61 if (c == '\n')
62 break;
64 current_lineno++;
65 return p->length() > 0;
68 void do_file(file_case *fcp, const char *filename)
70 string linebuf;
71 string str;
72 if (output_format == troff)
73 printf(".lf 1 %s\n", filename);
74 current_filename = filename;
75 current_lineno = 0;
76 while (read_line(fcp, &linebuf)) {
77 if (linebuf.length() >= 4
78 && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f'
79 && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
80 put_string(linebuf, stdout);
81 linebuf += '\0';
82 if (interpret_lf_args(linebuf.contents() + 3))
83 current_lineno--;
85 else if (linebuf.length() >= 4
86 && linebuf[0] == '.'
87 && linebuf[1] == 'E'
88 && linebuf[2] == 'Q'
89 && (linebuf[3] == ' ' || linebuf[3] == '\n'
90 || compatible_flag)) {
91 put_string(linebuf, stdout);
92 int start_lineno = current_lineno + 1;
93 str.clear();
94 for (;;) {
95 if (!read_line(fcp, &linebuf))
96 fatal("end of file before .EN");
97 if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') {
98 if (linebuf[2] == 'N'
99 && (linebuf.length() == 3 || linebuf[3] == ' '
100 || linebuf[3] == '\n' || compatible_flag))
101 break;
102 else if (linebuf[2] == 'Q' && linebuf.length() > 3
103 && (linebuf[3] == ' ' || linebuf[3] == '\n'
104 || compatible_flag))
105 fatal("nested .EQ");
107 str += linebuf;
109 str += '\0';
110 start_string();
111 init_lex(str.contents(), current_filename, start_lineno);
112 non_empty_flag = 0;
113 inline_flag = 0;
114 yyparse();
115 restore_compatibility();
116 if (non_empty_flag)
117 if (output_format == mathml)
118 putchar('\n');
119 else {
120 printf(".lf %d\n", current_lineno - 1);
121 output_string();
123 if (output_format == troff)
124 printf(".lf %d\n", current_lineno);
125 put_string(linebuf, stdout);
127 else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
128 && inline_equation(fcp, linebuf, str))
130 else
131 put_string(linebuf, stdout);
133 current_filename = 0;
134 current_lineno = 0;
137 // Handle an inline equation. Return 1 if it was an inline equation,
138 // otherwise.
139 static int inline_equation(file_case *fcp, string &linebuf, string &str)
141 linebuf += '\0';
142 char *ptr = &linebuf[0];
143 char *start = delim_search(ptr, start_delim);
144 if (!start) {
145 // It wasn't a delimiter after all.
146 linebuf.set_length(linebuf.length() - 1); // strip the '\0'
147 return 0;
149 start_string();
150 inline_flag = 1;
151 for (;;) {
152 if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) {
153 error("missing `%1'", end_delim);
154 char *nl = strchr(start + 1, '\n');
155 if (nl != 0)
156 *nl = '\0';
157 do_text(ptr);
158 break;
160 int start_lineno = current_lineno;
161 *start = '\0';
162 do_text(ptr);
163 ptr = start + 1;
164 str.clear();
165 for (;;) {
166 char *end = strchr(ptr, end_delim);
167 if (end != 0) {
168 *end = '\0';
169 str += ptr;
170 ptr = end + 1;
171 break;
173 str += ptr;
174 if (!read_line(fcp, &linebuf))
175 fatal("unterminated `%1' at line %2, looking for `%3'",
176 start_delim, start_lineno, end_delim);
177 linebuf += '\0';
178 ptr = &linebuf[0];
180 str += '\0';
181 if (output_format == troff && html) {
182 printf(".as1 %s ", LINE_STRING);
183 html_begin_suppress();
184 printf("\n");
186 init_lex(str.contents(), current_filename, start_lineno);
187 yyparse();
188 if (output_format == troff && html) {
189 printf(".as1 %s ", LINE_STRING);
190 html_end_suppress();
191 printf("\n");
193 if (output_format == mathml)
194 printf("\n");
195 if (xhtml) {
196 /* skip leading spaces */
197 while ((*ptr != '\0') && (*ptr == ' '))
198 ptr++;
200 start = delim_search(ptr, start_delim);
201 if (start == 0) {
202 char *nl = strchr(ptr, '\n');
203 if (nl != 0)
204 *nl = '\0';
205 do_text(ptr);
206 break;
209 restore_compatibility();
210 if (output_format == troff)
211 printf(".lf %d\n", current_lineno);
212 output_string();
213 if (output_format == troff)
214 printf(".lf %d\n", current_lineno + 1);
215 return 1;
218 /* Search for delim. Skip over number register and string names etc. */
220 static char *delim_search(char *ptr, int delim)
222 while (*ptr) {
223 if (*ptr == delim)
224 return ptr;
225 if (*ptr++ == '\\') {
226 switch (*ptr) {
227 case 'n':
228 case '*':
229 case 'f':
230 case 'g':
231 case 'k':
232 switch (*++ptr) {
233 case '\0':
234 case '\\':
235 break;
236 case '(':
237 if (*++ptr != '\\' && *ptr != '\0'
238 && *++ptr != '\\' && *ptr != '\0')
239 ptr++;
240 break;
241 case '[':
242 while (*++ptr != '\0')
243 if (*ptr == ']') {
244 ptr++;
245 break;
247 break;
248 default:
249 ptr++;
250 break;
252 break;
253 case '\\':
254 case '\0':
255 break;
256 default:
257 ptr++;
258 break;
262 return 0;
265 void usage(FILE *stream)
267 fprintf(stream,
268 "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
269 program_name);
272 int main(int argc, char **argv)
274 program_name = argv[0];
275 static char stderr_buf[BUFSIZ];
276 setbuf(stderr, stderr_buf);
277 int opt;
278 int load_startup_file = 1;
279 static const struct option long_options[] = {
280 { "help", no_argument, 0, CHAR_MAX + 1 },
281 { "version", no_argument, 0, 'v' },
282 { NULL, 0, 0, 0 }
284 while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options,
285 NULL))
286 != EOF)
287 switch (opt) {
288 case 'C':
289 compatible_flag = 1;
290 break;
291 case 'R': // don't load eqnrc
292 load_startup_file = 0;
293 break;
294 case 'M':
295 config_macro_path.command_line_dir(optarg);
296 break;
297 case 'v':
298 printf("GNU eqn (groff) version %s\n", Version_string);
299 exit(0);
300 break;
301 case 'd':
302 if (optarg[0] == '\0' || optarg[1] == '\0')
303 error("-d requires two character argument");
304 else if (invalid_input_char(optarg[0]))
305 error("bad delimiter `%1'", optarg[0]);
306 else if (invalid_input_char(optarg[1]))
307 error("bad delimiter `%1'", optarg[1]);
308 else {
309 start_delim = optarg[0];
310 end_delim = optarg[1];
312 break;
313 case 'f':
314 set_gfont(optarg);
315 break;
316 case 'T':
317 device = optarg;
318 if (strcmp(device, "ps:html") == 0) {
319 device = "ps";
320 html = 1;
322 else if (strcmp(device, "MathML") == 0) {
323 output_format = mathml;
324 load_startup_file = 0;
326 else if (strcmp(device, "mathml:xhtml") == 0) {
327 device = "MathML";
328 output_format = mathml;
329 load_startup_file = 0;
330 xhtml = 1;
332 break;
333 case 's':
334 if (!set_gsize(optarg))
335 error("invalid size `%1'", optarg);
336 break;
337 case 'p':
339 int n;
340 if (sscanf(optarg, "%d", &n) == 1)
341 set_script_reduction(n);
342 else
343 error("bad size `%1'", optarg);
345 break;
346 case 'm':
348 int n;
349 if (sscanf(optarg, "%d", &n) == 1)
350 set_minimum_size(n);
351 else
352 error("bad size `%1'", optarg);
354 break;
355 case 'r':
356 one_size_reduction_flag = 1;
357 break;
358 case 'D':
359 warning("-D option is obsolete: use `set draw_lines 1' instead");
360 draw_flag = 1;
361 break;
362 case 'N':
363 no_newline_in_delim_flag = 1;
364 break;
365 case CHAR_MAX + 1: // --help
366 usage(stdout);
367 exit(0);
368 break;
369 case '?':
370 usage(stderr);
371 exit(1);
372 break;
373 default:
374 assert(0);
376 init_table(device);
377 init_char_table();
378 if (output_format == troff) {
379 printf(".if !'\\*(.T'%s' "
380 ".if !'\\*(.T'html' " // the html device uses `-Tps' to render
381 // equations as images
382 ".tm warning: %s should have been given a `-T\\*(.T' option\n",
383 device, program_name);
384 printf(".if '\\*(.T'html' "
385 ".if !'%s'ps' "
386 ".tm warning: %s should have been given a `-Tps' option\n",
387 device, program_name);
388 printf(".if '\\*(.T'html' "
389 ".if !'%s'ps' "
390 ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n",
391 device);
394 file_case *fcp;
395 if (load_startup_file) {
396 if ((fcp = config_macro_path.open_file(STARTUP_FILE, fcp->fc_const_path)
397 ) != NULL) {
398 do_file(fcp, fcp->path());
399 delete fcp;
402 int i = optind;
403 if (i >= argc)
404 goto jstdin;
405 for (; i < argc; ++i) {
406 if (!strcmp(argv[i], "-")) {
407 jstdin:
408 fcp = new file_case(stdin, "stdin",
409 fcp->fc_dont_close | fcp->fc_const_path /*| fcp->fc_have_stdio*/);
410 do_file(fcp, "-");
411 } else {
412 fcp = file_case::muxer(argv[i]);
413 if (fcp == NULL)
414 fatal("can't open `%1': %2", argv[i], strerror(errno));
415 do_file(fcp, argv[i]);
417 delete fcp;
419 if (ferror(stdout) || fflush(stdout) < 0)
420 fatal("output error");
421 return 0;