* All affected files: Update postal address of FSF.
[s-roff.git] / src / preproc / eqn / main.cpp
blob7971e2c2dc3c0fbc51d93fbdbbca159e3fa63b02
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)
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 "searchpath.h"
26 #include "macropath.h"
27 #include "htmlhint.h"
28 #include "pbox.h"
29 #include "ctype.h"
31 #define STARTUP_FILE "eqnrc"
33 extern int yyparse();
34 extern "C" const char *Version_string;
36 static char *delim_search (char *, int);
37 static int inline_equation (FILE *, string &, string &);
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;
50 int read_line(FILE *fp, string *p)
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;
62 current_lineno++;
63 return p->length() > 0;
66 void do_file(FILE *fp, const char *filename)
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--;
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");
104 str += linebuf;
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();
117 printf(".lf %d\n", current_lineno);
118 put_string(linebuf, stdout);
120 else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
121 && inline_equation(fp, linebuf, str))
123 else
124 put_string(linebuf, stdout);
126 current_filename = 0;
127 current_lineno = 0;
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)
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;
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;
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;
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];
173 str += '\0';
174 if (html) {
175 printf(".as1 %s ", LINE_STRING);
176 html_begin_suppress();
177 printf("\n");
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");
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;
195 restore_compatibility();
196 printf(".lf %d\n", current_lineno);
197 output_string();
198 printf(".lf %d\n", current_lineno + 1);
199 return 1;
202 /* Search for delim. Skip over number register and string names etc. */
204 static char *delim_search(char *ptr, int delim)
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;
231 break;
232 default:
233 ptr++;
234 break;
236 break;
237 case '\\':
238 case '\0':
239 break;
240 default:
241 ptr++;
242 break;
246 return 0;
249 void usage(FILE *stream)
251 fprintf(stream,
252 "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
253 program_name);
256 int main(int argc, char **argv)
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 }
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':
283 printf("GNU eqn (groff) version %s\n", Version_string);
284 exit(0);
285 break;
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];
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;
308 break;
309 case 's':
310 if (!set_gsize(optarg))
311 error("invalid size `%1'", optarg);
312 break;
313 case 'p':
315 int n;
316 if (sscanf(optarg, "%d", &n) == 1)
317 set_script_reduction(n);
318 else
319 error("bad size `%1'", optarg);
321 break;
322 case 'm':
324 int n;
325 if (sscanf(optarg, "%d", &n) == 1)
326 set_minimum_size(n);
327 else
328 error("bad size `%1'", optarg);
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);
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;
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);
392 if (ferror(stdout) || fflush(stdout) < 0)
393 fatal("output error");
394 return 0;