Imported Upstream version 1.22.4
[platform/upstream/groff.git] / src / preproc / soelim / soelim.cpp
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
45 void usage(FILE *stream)
46 {
47   fprintf(stream, "usage: %s [ -Crtv ] [ -I dir ] [ files ]\n", program_name);
48 }
49
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
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
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
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 }