1 // -*- C++ -*-
2 /* Copyright (C) 1989-2018 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4 
5 This file is part of groff.
6 
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19 
20 #include "lib.h"
21 
22 #include <ctype.h>
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include "errarg.h"
27 #include "error.h"
28 #include "stringclass.h"
29 #include "nonposix.h"
30 #include "searchpath.h"
31 #include "lf.h"
32 
33 // The include search path initially contains only the current directory.
34 static search_path include_search_path(0, 0, 0, 1);
35 
36 int compatible_flag = 0;
37 int raw_flag = 0;
38 int tex_flag = 0;
39 
40 extern "C" const char *Version_string;
41 
42 int do_file(const char *);
43 
44 
usage(FILE * stream)45 void usage(FILE *stream)
46 {
47   fprintf(stream, "usage: %s [ -Crtv ] [ -I dir ] [ files ]\n", program_name);
48 }
49 
main(int argc,char ** argv)50 int main(int argc, char **argv)
51 {
52   program_name = argv[0];
53   int opt;
54   static const struct option long_options[] = {
55     { "help", no_argument, 0, CHAR_MAX + 1 },
56     { "version", no_argument, 0, 'v' },
57     { NULL, 0, 0, 0 }
58   };
59   while ((opt = getopt_long(argc, argv, "CI:rtv", long_options, NULL)) != EOF)
60     switch (opt) {
61     case 'v':
62       printf("GNU soelim (groff) version %s\n", Version_string);
63       exit(0);
64       break;
65     case 'C':
66       compatible_flag = 1;
67       break;
68     case 'I':
69       include_search_path.command_line_dir(optarg);
70       break;
71     case 'r':
72       raw_flag = 1;
73       break;
74     case 't':
75       tex_flag = 1;
76       break;
77     case CHAR_MAX + 1: // --help
78       usage(stdout);
79       exit(0);
80       break;
81     case '?':
82       usage(stderr);
83       exit(1);
84       break;
85     default:
86       assert(0);
87     }
88   int nbad = 0;
89   if (optind >= argc)
90     nbad += !do_file("-");
91   else
92     for (int i = optind; i < argc; i++)
93       nbad += !do_file(argv[i]);
94   if (ferror(stdout) || fflush(stdout) < 0)
95     fatal("output error");
96   return nbad != 0;
97 }
98 
set_location()99 void set_location()
100 {
101   if (!raw_flag) {
102     if (!tex_flag)
103       printf(".lf %d %s\n", current_lineno, current_filename);
104     else
105       printf("%% file %s, line %d\n", current_filename, current_lineno);
106   }
107 }
108 
do_so(const char * line)109 void do_so(const char *line)
110 {
111   const char *p = line;
112   while (*p == ' ')
113     p++;
114   string filename;
115   int success = 1;
116   for (const char *q = p;
117        success && *q != '\0' && *q != '\n' && *q != ' ';
118        q++)
119     if (*q == '\\') {
120       switch (*++q) {
121       case 'e':
122       case '\\':
123 	filename += '\\';
124 	break;
125       case ' ':
126 	filename += ' ';
127 	break;
128       default:
129 	success = 0;
130 	break;
131       }
132     }
133     else
134       filename += char(*q);
135   if (success && filename.length() > 0) {
136     filename += '\0';
137     const char *fn = current_filename;
138     int ln = current_lineno;
139     current_lineno--;
140     if (do_file(filename.contents())) {
141       current_filename = fn;
142       current_lineno = ln;
143       set_location();
144       return;
145     }
146     current_lineno++;
147   }
148   fputs(".so", stdout);
149   fputs(line, stdout);
150 }
151 
do_file(const char * filename)152 int do_file(const char *filename)
153 {
154   char *file_name_in_path = 0;
155   FILE *fp = include_search_path.open_file_cautious(filename,
156 						    &file_name_in_path);
157   int err = errno;
158   string whole_filename(file_name_in_path ? file_name_in_path : filename);
159   whole_filename += '\0';
160   free(file_name_in_path);
161   if (fp == 0) {
162     error("can't open '%1': %2", whole_filename.contents(), strerror(err));
163     return 0;
164   }
165   normalize_for_lf(whole_filename);
166   current_filename = whole_filename.contents();
167   current_lineno = 1;
168   set_location();
169   enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
170   for (;;) {
171     int c = getc(fp);
172     if (c == EOF)
173       break;
174     switch (state) {
175     case START:
176       if (c == '.')
177 	state = HAD_DOT;
178       else {
179 	putchar(c);
180 	if (c == '\n') {
181 	  current_lineno++;
182 	  state = START;
183 	}
184 	else
185 	  state = MIDDLE;
186       }
187       break;
188     case MIDDLE:
189       putchar(c);
190       if (c == '\n') {
191 	current_lineno++;
192 	state = START;
193       }
194       break;
195     case HAD_DOT:
196       if (c == 's')
197 	state = HAD_s;
198       else if (c == 'l')
199 	state = HAD_l;
200       else {
201 	putchar('.');
202 	putchar(c);
203 	if (c == '\n') {
204 	  current_lineno++;
205 	  state = START;
206 	}
207 	else
208 	  state = MIDDLE;
209       }
210       break;
211     case HAD_s:
212       if (c == 'o')
213 	state = HAD_so;
214       else  {
215 	putchar('.');
216 	putchar('s');
217 	putchar(c);
218 	if (c == '\n') {
219 	  current_lineno++;
220 	  state = START;
221 	}
222 	else
223 	  state = MIDDLE;
224       }
225       break;
226     case HAD_so:
227       if (c == ' ' || c == '\n' || compatible_flag) {
228 	string line;
229 	for (; c != EOF && c != '\n'; c = getc(fp))
230 	  line += c;
231 	current_lineno++;
232 	line += '\n';
233 	line += '\0';
234 	do_so(line.contents());
235 	state = START;
236       }
237       else {
238 	fputs(".so", stdout);
239 	putchar(c);
240 	state = MIDDLE;
241       }
242       break;
243     case HAD_l:
244       if (c == 'f')
245 	state = HAD_lf;
246       else {
247 	putchar('.');
248 	putchar('l');
249 	putchar(c);
250 	if (c == '\n') {
251 	  current_lineno++;
252 	  state = START;
253 	}
254 	else
255 	  state = MIDDLE;
256       }
257       break;
258     case HAD_lf:
259       if (c == ' ' || c == '\n' || compatible_flag) {
260 	string line;
261 	for (; c != EOF && c != '\n'; c = getc(fp))
262 	  line += c;
263 	current_lineno++;
264 	line += '\n';
265 	line += '\0';
266 	interpret_lf_args(line.contents());
267 	printf(".lf%s", line.contents());
268 	state = START;
269       }
270       else {
271 	fputs(".lf", stdout);
272 	putchar(c);
273 	state = MIDDLE;
274       }
275       break;
276     default:
277       assert(0);
278     }
279   }
280   switch (state) {
281   case HAD_DOT:
282     fputs(".\n", stdout);
283     break;
284   case HAD_l:
285     fputs(".l\n", stdout);
286     break;
287   case HAD_s:
288     fputs(".s\n", stdout);
289     break;
290   case HAD_lf:
291     fputs(".lf\n", stdout);
292     break;
293   case HAD_so:
294     fputs(".so\n", stdout);
295     break;
296   case MIDDLE:
297     putc('\n', stdout);
298     break;
299   case START:
300     break;
301   }
302   if (fp != stdin)
303     fclose(fp);
304   current_filename = 0;
305   return 1;
306 }
307