Initial import from FreeBSD RELENG_4:
[games.git] / contrib / groff / src / preproc / soelim / soelim.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001 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 2, or (at your option) any later
10 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 along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #include "lib.h"
22
23 #include <ctype.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include "errarg.h"
28 #include "error.h"
29 #include "stringclass.h"
30 #include "nonposix.h"
31
32 static size_t include_list_length;
33 static char **include_list;
34
35 int compatible_flag = 0;
36
37 extern int interpret_lf_args(const char *);
38 extern "C" const char *Version_string;
39
40 int do_file(const char *filename);
41
42
43 static void
44 include_path_append(char *path)
45 {
46   ++include_list_length;
47   size_t nbytes = include_list_length * sizeof(char *);
48   if (include_list)
49     include_list = (char **)realloc((void *)include_list, nbytes);
50   else
51     include_list = (char **)malloc(nbytes);
52   if (include_list == NULL)
53     {
54       fprintf(stderr, "%s: out of memory\n", program_name);
55       exit(2);
56     }
57   include_list[include_list_length - 1] = path;
58 }
59
60
61 void usage(FILE *stream)
62 {
63   fprintf(stream, "usage: %s [ -vC ] [ -I file ] [ files ]\n", program_name);
64 }
65
66 int main(int argc, char **argv)
67 {
68   program_name = argv[0];
69   include_path_append(".");
70   int opt;
71   static const struct option long_options[] = {
72     { "help", no_argument, 0, CHAR_MAX + 1 },
73     { "version", no_argument, 0, 'v' },
74     { NULL, 0, 0, 0 }
75   };
76   while ((opt = getopt_long(argc, argv, "CI:v", long_options, NULL)) != EOF)
77     switch (opt) {
78     case 'v':
79       {
80         printf("GNU soelim (groff) version %s\n", Version_string);
81         exit(0);
82         break;
83       }
84     case 'C':
85       compatible_flag = 1;
86       break;
87     case 'I':
88       include_path_append(optarg);
89       break;
90     case CHAR_MAX + 1: // --help
91       usage(stdout);
92       exit(0);
93       break;
94     case '?':
95       usage(stderr);
96       exit(1);
97       break;
98     default:
99       assert(0);
100     }
101   int nbad = 0;
102   if (optind >= argc)
103     nbad += !do_file("-");
104   else
105     for (int i = optind; i < argc; i++)
106       nbad += !do_file(argv[i]);
107   if (ferror(stdout) || fflush(stdout) < 0)
108     fatal("output error");
109   return nbad != 0;
110 }
111
112 void set_location()
113 {
114   printf(".lf %d %s\n", current_lineno, current_filename);
115 }
116
117 void do_so(const char *line)
118 {
119   const char *p = line;
120   while (*p == ' ')
121     p++;
122   string filename;
123   int success = 1;
124   for (const char *q = p;
125        success && *q != '\0' && *q != '\n' && *q != ' ';
126        q++)
127     if (*q == '\\') {
128       switch (*++q) {
129       case 'e':
130       case '\\':
131         filename += '\\';
132         break;
133       case ' ':
134         filename += ' ';
135         break;
136       default:
137         success = 0;
138         break;
139       }
140     }
141     else
142       filename += char(*q);
143   if (success && filename.length() > 0) {
144     filename += '\0';
145     const char *fn = current_filename;
146     int ln = current_lineno;
147     current_lineno--;
148     if (do_file(filename.contents())) {
149       current_filename = fn;
150       current_lineno = ln;
151       set_location();
152       return;
153     }
154     current_lineno++;
155   }
156   fputs(".so", stdout);
157   fputs(line, stdout);
158 }
159
160 int do_file(const char *filename)
161 {
162   FILE *fp;
163   string whole_filename;
164   if (strcmp(filename, "-") == 0) {
165     fp = stdin;
166     whole_filename = filename;
167     whole_filename += '\0';
168   }
169   else if (IS_ABSOLUTE(filename)) {
170     whole_filename = filename;
171     whole_filename += '\0';
172     errno = 0;
173     fp = fopen(filename, "r");
174     if (fp == 0) {
175       error("can't open `%1': %2", filename, strerror(errno));
176       return 0;
177     }
178   }
179   else {
180     size_t j;
181     for (j = 0; j < include_list_length; ++j)
182     {
183       char *path = include_list[j];
184       if (0 == strcmp(path, "."))
185         whole_filename = filename;
186       else
187         whole_filename = string(path) + "/" + filename;
188       whole_filename += '\0';
189       errno = 0;
190       fp = fopen(whole_filename.contents(), "r");
191       if (fp != 0)
192         break;
193       if (errno != ENOENT) {
194         error("can't open `%1': %2",
195               whole_filename.contents(), strerror(errno));
196         return 0;
197       }
198     }
199     if (j >= include_list_length)
200     {
201       errno = ENOENT;
202       error("can't open `%1': %2", filename, strerror(errno));
203       return 0;
204     }
205   }
206   current_filename = whole_filename.contents();
207   current_lineno = 1;
208   set_location();
209   enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
210   for (;;) {
211     int c = getc(fp);
212     if (c == EOF)
213       break;
214     switch (state) {
215     case START:
216       if (c == '.')
217         state = HAD_DOT;
218       else {
219         putchar(c);
220         if (c == '\n') {
221           current_lineno++;
222           state = START;
223         }
224         else
225           state = MIDDLE;
226       }
227       break;
228     case MIDDLE:
229       putchar(c);
230       if (c == '\n') {
231         current_lineno++;
232         state = START;
233       }
234       break;
235     case HAD_DOT:
236       if (c == 's')
237         state = HAD_s;
238       else if (c == 'l')
239         state = HAD_l;
240       else {
241         putchar('.');
242         putchar(c);
243         if (c == '\n') {
244           current_lineno++;
245           state = START;
246         }
247         else
248           state = MIDDLE;
249       }
250       break;
251     case HAD_s:
252       if (c == 'o')
253         state = HAD_so;
254       else  {
255         putchar('.');
256         putchar('s');
257         putchar(c);
258         if (c == '\n') {
259           current_lineno++;
260           state = START;
261         }
262         else
263           state = MIDDLE;
264       }
265       break;
266     case HAD_so:
267       if (c == ' ' || c == '\n' || compatible_flag) {
268         string line;
269         for (; c != EOF && c != '\n'; c = getc(fp))
270           line += c;
271         current_lineno++;
272         line += '\n';
273         line += '\0';
274         do_so(line.contents());
275         state = START;
276       }
277       else {
278         fputs(".so", stdout);
279         putchar(c);
280         state = MIDDLE;
281       }
282       break;
283     case HAD_l:
284       if (c == 'f')
285         state = HAD_lf;
286       else {
287         putchar('.');
288         putchar('l');
289         putchar(c);
290         if (c == '\n') {
291           current_lineno++;
292           state = START;
293         }
294         else
295           state = MIDDLE;
296       }
297       break;
298     case HAD_lf:
299       if (c == ' ' || c == '\n' || compatible_flag) {
300         string line;
301         for (; c != EOF && c != '\n'; c = getc(fp))
302           line += c;
303         current_lineno++;
304         line += '\n';
305         line += '\0';
306         interpret_lf_args(line.contents());
307         printf(".lf%s", line.contents());
308         state = START;
309       }
310       else {
311         fputs(".lf", stdout);
312         putchar(c);
313         state = MIDDLE;
314       }
315       break;
316     default:
317       assert(0);
318     }
319   }
320   switch (state) {
321   case HAD_DOT:
322     fputs(".\n", stdout);
323     break;
324   case HAD_l:
325     fputs(".l\n", stdout);
326     break;
327   case HAD_s:
328     fputs(".s\n", stdout);
329     break;
330   case HAD_lf:
331     fputs(".lf\n", stdout);
332     break;
333   case HAD_so:
334     fputs(".so\n", stdout);
335     break;
336   case MIDDLE:
337     putc('\n', stdout);
338     break;
339   case START:
340     break;
341   }
342   if (fp != stdin)
343     fclose(fp);
344   current_filename = 0;
345   return 1;
346 }