xref: /NextBSD/contrib/groff/src/preproc/soelim/soelim.cpp (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001, 2003, 2004, 2005
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 "lib.h"
23 
24 #include <ctype.h>
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include "errarg.h"
29 #include "error.h"
30 #include "stringclass.h"
31 #include "nonposix.h"
32 #include "searchpath.h"
33 
34 // The include search path initially contains only the current directory.
35 static search_path include_search_path(0, 0, 0, 1);
36 
37 int compatible_flag = 0;
38 int raw_flag = 0;
39 int tex_flag = 0;
40 
41 extern "C" const char *Version_string;
42 
43 int do_file(const char *filename);
44 
45 
usage(FILE * stream)46 void usage(FILE *stream)
47 {
48   fprintf(stream, "usage: %s [ -Crtv ] [ -I file ] [ files ]\n", program_name);
49 }
50 
main(int argc,char ** argv)51 int main(int argc, char **argv)
52 {
53   program_name = argv[0];
54   int opt;
55   static const struct option long_options[] = {
56     { "help", no_argument, 0, CHAR_MAX + 1 },
57     { "version", no_argument, 0, 'v' },
58     { NULL, 0, 0, 0 }
59   };
60   while ((opt = getopt_long(argc, argv, "CI:rtv", long_options, NULL)) != EOF)
61     switch (opt) {
62     case 'v':
63       {
64 	printf("GNU soelim (groff) version %s\n", Version_string);
65 	exit(0);
66 	break;
67       }
68     case 'C':
69       compatible_flag = 1;
70       break;
71     case 'I':
72       include_search_path.command_line_dir(optarg);
73       break;
74     case 'r':
75       raw_flag = 1;
76       break;
77     case 't':
78       tex_flag = 1;
79       break;
80     case CHAR_MAX + 1: // --help
81       usage(stdout);
82       exit(0);
83       break;
84     case '?':
85       usage(stderr);
86       exit(1);
87       break;
88     default:
89       assert(0);
90     }
91   int nbad = 0;
92   if (optind >= argc)
93     nbad += !do_file("-");
94   else
95     for (int i = optind; i < argc; i++)
96       nbad += !do_file(argv[i]);
97   if (ferror(stdout) || fflush(stdout) < 0)
98     fatal("output error");
99   return nbad != 0;
100 }
101 
set_location()102 void set_location()
103 {
104   if(!raw_flag) {
105     if(!tex_flag)
106       printf(".lf %d %s\n", current_lineno, current_filename);
107     else
108       printf("%% file %s, line %d\n", current_filename, current_lineno);
109   }
110 }
111 
do_so(const char * line)112 void do_so(const char *line)
113 {
114   const char *p = line;
115   while (*p == ' ')
116     p++;
117   string filename;
118   int success = 1;
119   for (const char *q = p;
120        success && *q != '\0' && *q != '\n' && *q != ' ';
121        q++)
122     if (*q == '\\') {
123       switch (*++q) {
124       case 'e':
125       case '\\':
126 	filename += '\\';
127 	break;
128       case ' ':
129 	filename += ' ';
130 	break;
131       default:
132 	success = 0;
133 	break;
134       }
135     }
136     else
137       filename += char(*q);
138   if (success && filename.length() > 0) {
139     filename += '\0';
140     const char *fn = current_filename;
141     int ln = current_lineno;
142     current_lineno--;
143     if (do_file(filename.contents())) {
144       current_filename = fn;
145       current_lineno = ln;
146       set_location();
147       return;
148     }
149     current_lineno++;
150   }
151   fputs(".so", stdout);
152   fputs(line, stdout);
153 }
154 
do_file(const char * filename)155 int do_file(const char *filename)
156 {
157   char *file_name_in_path = 0;
158   FILE *fp = include_search_path.open_file_cautious(filename,
159 						    &file_name_in_path);
160   int err = errno;
161   string whole_filename(file_name_in_path ? file_name_in_path : filename);
162   whole_filename += '\0';
163   a_delete file_name_in_path;
164   if (fp == 0) {
165     error("can't open `%1': %2", whole_filename.contents(), strerror(err));
166     return 0;
167   }
168   current_filename = whole_filename.contents();
169   current_lineno = 1;
170   set_location();
171   enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
172   for (;;) {
173     int c = getc(fp);
174     if (c == EOF)
175       break;
176     switch (state) {
177     case START:
178       if (c == '.')
179 	state = HAD_DOT;
180       else {
181 	putchar(c);
182 	if (c == '\n') {
183 	  current_lineno++;
184 	  state = START;
185 	}
186 	else
187 	  state = MIDDLE;
188       }
189       break;
190     case MIDDLE:
191       putchar(c);
192       if (c == '\n') {
193 	current_lineno++;
194 	state = START;
195       }
196       break;
197     case HAD_DOT:
198       if (c == 's')
199 	state = HAD_s;
200       else if (c == 'l')
201 	state = HAD_l;
202       else {
203 	putchar('.');
204 	putchar(c);
205 	if (c == '\n') {
206 	  current_lineno++;
207 	  state = START;
208 	}
209 	else
210 	  state = MIDDLE;
211       }
212       break;
213     case HAD_s:
214       if (c == 'o')
215 	state = HAD_so;
216       else  {
217 	putchar('.');
218 	putchar('s');
219 	putchar(c);
220 	if (c == '\n') {
221 	  current_lineno++;
222 	  state = START;
223 	}
224 	else
225 	  state = MIDDLE;
226       }
227       break;
228     case HAD_so:
229       if (c == ' ' || c == '\n' || compatible_flag) {
230 	string line;
231 	for (; c != EOF && c != '\n'; c = getc(fp))
232 	  line += c;
233 	current_lineno++;
234 	line += '\n';
235 	line += '\0';
236 	do_so(line.contents());
237 	state = START;
238       }
239       else {
240 	fputs(".so", stdout);
241 	putchar(c);
242 	state = MIDDLE;
243       }
244       break;
245     case HAD_l:
246       if (c == 'f')
247 	state = HAD_lf;
248       else {
249 	putchar('.');
250 	putchar('l');
251 	putchar(c);
252 	if (c == '\n') {
253 	  current_lineno++;
254 	  state = START;
255 	}
256 	else
257 	  state = MIDDLE;
258       }
259       break;
260     case HAD_lf:
261       if (c == ' ' || c == '\n' || compatible_flag) {
262 	string line;
263 	for (; c != EOF && c != '\n'; c = getc(fp))
264 	  line += c;
265 	current_lineno++;
266 	line += '\n';
267 	line += '\0';
268 	interpret_lf_args(line.contents());
269 	printf(".lf%s", line.contents());
270 	state = START;
271       }
272       else {
273 	fputs(".lf", stdout);
274 	putchar(c);
275 	state = MIDDLE;
276       }
277       break;
278     default:
279       assert(0);
280     }
281   }
282   switch (state) {
283   case HAD_DOT:
284     fputs(".\n", stdout);
285     break;
286   case HAD_l:
287     fputs(".l\n", stdout);
288     break;
289   case HAD_s:
290     fputs(".s\n", stdout);
291     break;
292   case HAD_lf:
293     fputs(".lf\n", stdout);
294     break;
295   case HAD_so:
296     fputs(".so\n", stdout);
297     break;
298   case MIDDLE:
299     putc('\n', stdout);
300     break;
301   case START:
302     break;
303   }
304   if (fp != stdin)
305     fclose(fp);
306   current_filename = 0;
307   return 1;
308 }
309