1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
20 
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include "lib.h"
29 #include "errarg.h"
30 #include "error.h"
31 #include "stringclass.h"
32 
33 int compatible_flag = 0;
34 
35 extern int interpret_lf_args(const char *);
36 
37 int do_file(const char *filename);
38 
39 void usage()
40 {
41   fprintf(stderr, "usage: %s [ -vC ] [ files ]\n", program_name);
42   exit(1);
43 }
44 
45 int main(int argc, char **argv)
46 {
47   program_name = argv[0];
48   int opt;
49   while ((opt = getopt(argc, argv, "vC")) != EOF)
50     switch (opt) {
51     case 'v':
52       {
53 	extern const char *version_string;
54 	fprintf(stderr, "GNU soelim version %s\n", version_string);
55 	fflush(stderr);
56 	break;
57       }
58     case 'C':
59       compatible_flag = 1;
60       break;
61     case '?':
62       usage();
63       break;
64     default:
65       assert(0);
66     }
67   int nbad = 0;
68   if (optind >= argc)
69     nbad += !do_file("-");
70   else
71     for (int i = optind; i < argc; i++)
72       nbad += !do_file(argv[i]);
73   if (ferror(stdout) || fflush(stdout) < 0)
74     fatal("output error");
75   exit(nbad != 0);
76 }
77 
78 void set_location()
79 {
80   printf(".lf %d %s\n", current_lineno, current_filename);
81 }
82 
83 void do_so(const char *line)
84 {
85   const char *p = line;
86   while (*p == ' ')
87     p++;
88   string filename;
89   int success = 1;
90   for (const char *q = p;
91        success && *q != '\0' && *q != '\n' && *q != ' ';
92        q++)
93     if (*q == '\\') {
94       switch (*++q) {
95       case 'e':
96       case '\\':
97 	filename += '\\';
98 	break;
99       case ' ':
100 	filename += ' ';
101 	break;
102       default:
103 	success = 0;
104 	break;
105       }
106     }
107     else
108       filename += char(*q);
109   if (success && filename.length() > 0) {
110     filename += '\0';
111     const char *fn = current_filename;
112     int ln = current_lineno;
113     current_lineno--;
114     if (do_file(filename.contents())) {
115       current_filename = fn;
116       current_lineno = ln;
117       set_location();
118       return;
119     }
120     current_lineno++;
121   }
122   fputs(".so", stdout);
123   fputs(line, stdout);
124 }
125 
126 int do_file(const char *filename)
127 {
128   FILE *fp;
129   if (strcmp(filename, "-") == 0)
130     fp = stdin;
131   else {
132     errno = 0;
133     fp = fopen(filename, "r");
134     if (fp == 0) {
135       error("can't open `%1': %2", filename, strerror(errno));
136       return 0;
137     }
138   }
139   current_filename = filename;
140   current_lineno = 1;
141   set_location();
142   enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
143   for (;;) {
144     int c = getc(fp);
145     if (c == EOF)
146       break;
147     switch (state) {
148     case START:
149       if (c == '.')
150 	state = HAD_DOT;
151       else {
152 	putchar(c);
153 	if (c == '\n') {
154 	  current_lineno++;
155 	  state = START;
156 	}
157 	else
158 	  state = MIDDLE;
159       }
160       break;
161     case MIDDLE:
162       putchar(c);
163       if (c == '\n') {
164 	current_lineno++;
165 	state = START;
166       }
167       break;
168     case HAD_DOT:
169       if (c == 's')
170 	state = HAD_s;
171       else if (c == 'l')
172 	state = HAD_l;
173       else {
174 	putchar('.');
175 	putchar(c);
176 	if (c == '\n') {
177 	  current_lineno++;
178 	  state = START;
179 	}
180 	else
181 	  state = MIDDLE;
182       }
183       break;
184     case HAD_s:
185       if (c == 'o')
186 	state = HAD_so;
187       else  {
188 	putchar('.');
189 	putchar('s');
190 	putchar(c);
191 	if (c == '\n') {
192 	  current_lineno++;
193 	  state = START;
194 	}
195 	else
196 	  state = MIDDLE;
197       }
198       break;
199     case HAD_so:
200       if (c == ' ' || c == '\n' || compatible_flag) {
201 	string line;
202 	for (; c != EOF && c != '\n'; c = getc(fp))
203 	  line += c;
204 	current_lineno++;
205 	line += '\n';
206 	line += '\0';
207 	do_so(line.contents());
208 	state = START;
209       }
210       else {
211 	fputs(".so", stdout);
212 	putchar(c);
213 	state = MIDDLE;
214       }
215       break;
216     case HAD_l:
217       if (c == 'f')
218 	state = HAD_lf;
219       else {
220 	putchar('.');
221 	putchar('l');
222 	putchar(c);
223 	if (c == '\n') {
224 	  current_lineno++;
225 	  state = START;
226 	}
227 	else
228 	  state = MIDDLE;
229       }
230       break;
231     case HAD_lf:
232       if (c == ' ' || c == '\n' || compatible_flag) {
233 	string line;
234 	for (; c != EOF && c != '\n'; c = getc(fp))
235 	  line += c;
236 	current_lineno++;
237 	line += '\n';
238 	line += '\0';
239 	interpret_lf_args(line.contents());
240 	printf(".lf%s", line.contents());
241 	state = START;
242       }
243       else {
244 	fputs(".lf", stdout);
245 	putchar(c);
246 	state = MIDDLE;
247       }
248       break;
249     default:
250       assert(0);
251     }
252   }
253   switch (state) {
254   case HAD_DOT:
255     fputs(".\n", stdout);
256     break;
257   case HAD_l:
258     fputs(".l\n", stdout);
259     break;
260   case HAD_s:
261     fputs(".s\n", stdout);
262     break;
263   case HAD_lf:
264     fputs(".lf\n", stdout);
265     break;
266   case HAD_so:
267     fputs(".so\n", stdout);
268     break;
269   case MIDDLE:
270     putc('\n', stdout);
271     break;
272   case START:
273     break;
274   }
275   if (fp != stdin)
276     fclose(fp);
277   current_filename = 0;
278   return 1;
279 }
280