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