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