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