Add groff 1.19.1, stripped down appropriately.
[dragonfly.git] / contrib / groff-1.19 / src / preproc / soelim / soelim.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001, 2003, 2004
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
46 void usage(FILE *stream)
47 {
48   fprintf(stream, "usage: %s [ -Crtv ] [ -I file ] [ files ]\n", program_name);
49 }
50
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
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
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
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   a_delete file_name_in_path;
163   if (fp == 0) {
164     error("can't open `%1': %2", whole_filename.contents(), strerror(err));
165     return 0;
166   }
167   whole_filename += '\0';
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 }