soelim.cpp revision 114402
1// -*- C++ -*-
2/* Copyright (C) 1989-1992, 2000, 2001, 2003 Free Software Foundation, Inc.
3     Written by James Clark (jjc@jclark.com)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file COPYING.  If not, write to the Free Software
19Foundation, 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
32static size_t include_list_length;
33static const char **include_list;
34
35int compatible_flag = 0;
36int raw_flag = 0;
37int tex_flag = 0;
38
39extern int interpret_lf_args(const char *);
40extern "C" const char *Version_string;
41
42int do_file(const char *filename);
43
44
45static void
46include_path_append(const char *path)
47{
48  ++include_list_length;
49  size_t nbytes = include_list_length * sizeof(char *);
50  if (include_list)
51    include_list = (const char **)realloc((void *)include_list, nbytes);
52  else
53    include_list = (const char **)malloc(nbytes);
54  if (include_list == NULL)
55    {
56      fprintf(stderr, "%s: out of memory\n", program_name);
57      exit(2);
58    }
59  include_list[include_list_length - 1] = path;
60}
61
62
63void usage(FILE *stream)
64{
65  fprintf(stream, "usage: %s [ -Crtv ] [ -I file ] [ files ]\n", program_name);
66}
67
68int main(int argc, char **argv)
69{
70  program_name = argv[0];
71  include_path_append(".");
72  int opt;
73  static const struct option long_options[] = {
74    { "help", no_argument, 0, CHAR_MAX + 1 },
75    { "version", no_argument, 0, 'v' },
76    { NULL, 0, 0, 0 }
77  };
78  while ((opt = getopt_long(argc, argv, "CI:rtv", long_options, NULL)) != EOF)
79    switch (opt) {
80    case 'v':
81      {
82	printf("GNU soelim (groff) version %s\n", Version_string);
83	exit(0);
84	break;
85      }
86    case 'C':
87      compatible_flag = 1;
88      break;
89    case 'I':
90      include_path_append(optarg);
91      break;
92    case 'r':
93      raw_flag = 1;
94      break;
95    case 't':
96      tex_flag = 1;
97      break;
98    case CHAR_MAX + 1: // --help
99      usage(stdout);
100      exit(0);
101      break;
102    case '?':
103      usage(stderr);
104      exit(1);
105      break;
106    default:
107      assert(0);
108    }
109  int nbad = 0;
110  if (optind >= argc)
111    nbad += !do_file("-");
112  else
113    for (int i = optind; i < argc; i++)
114      nbad += !do_file(argv[i]);
115  if (ferror(stdout) || fflush(stdout) < 0)
116    fatal("output error");
117  return nbad != 0;
118}
119
120void set_location()
121{
122  if(!raw_flag) {
123    if(!tex_flag)
124      printf(".lf %d %s\n", current_lineno, current_filename);
125    else
126      printf("%% file %s, line %d\n", current_filename, current_lineno);
127  }
128}
129
130void do_so(const char *line)
131{
132  const char *p = line;
133  while (*p == ' ')
134    p++;
135  string filename;
136  int success = 1;
137  for (const char *q = p;
138       success && *q != '\0' && *q != '\n' && *q != ' ';
139       q++)
140    if (*q == '\\') {
141      switch (*++q) {
142      case 'e':
143      case '\\':
144	filename += '\\';
145	break;
146      case ' ':
147	filename += ' ';
148	break;
149      default:
150	success = 0;
151	break;
152      }
153    }
154    else
155      filename += char(*q);
156  if (success && filename.length() > 0) {
157    filename += '\0';
158    const char *fn = current_filename;
159    int ln = current_lineno;
160    current_lineno--;
161    if (do_file(filename.contents())) {
162      current_filename = fn;
163      current_lineno = ln;
164      set_location();
165      return;
166    }
167    current_lineno++;
168  }
169  fputs(".so", stdout);
170  fputs(line, stdout);
171}
172
173int do_file(const char *filename)
174{
175  FILE *fp;
176  string whole_filename;
177  if (strcmp(filename, "-") == 0) {
178    fp = stdin;
179    whole_filename = filename;
180    whole_filename += '\0';
181  }
182  else if (IS_ABSOLUTE(filename)) {
183    whole_filename = filename;
184    whole_filename += '\0';
185    errno = 0;
186    fp = fopen(filename, "r");
187    if (fp == 0) {
188      error("can't open `%1': %2", filename, strerror(errno));
189      return 0;
190    }
191  }
192  else {
193    size_t j;
194    for (j = 0; j < include_list_length; ++j)
195    {
196      const char *path = include_list[j];
197      if (0 == strcmp(path, "."))
198	whole_filename = filename;
199      else
200	whole_filename = string(path) + "/" + filename;
201      whole_filename += '\0';
202      errno = 0;
203      fp = fopen(whole_filename.contents(), "r");
204      if (fp != 0)
205	break;
206      if (errno != ENOENT) {
207	error("can't open `%1': %2",
208	      whole_filename.contents(), strerror(errno));
209	return 0;
210      }
211    }
212    if (j >= include_list_length)
213    {
214      errno = ENOENT;
215      error("can't open `%1': %2", filename, strerror(errno));
216      return 0;
217    }
218  }
219  current_filename = whole_filename.contents();
220  current_lineno = 1;
221  set_location();
222  enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
223  for (;;) {
224    int c = getc(fp);
225    if (c == EOF)
226      break;
227    switch (state) {
228    case START:
229      if (c == '.')
230	state = HAD_DOT;
231      else {
232	putchar(c);
233	if (c == '\n') {
234	  current_lineno++;
235	  state = START;
236	}
237	else
238	  state = MIDDLE;
239      }
240      break;
241    case MIDDLE:
242      putchar(c);
243      if (c == '\n') {
244	current_lineno++;
245	state = START;
246      }
247      break;
248    case HAD_DOT:
249      if (c == 's')
250	state = HAD_s;
251      else if (c == 'l')
252	state = HAD_l;
253      else {
254	putchar('.');
255	putchar(c);
256	if (c == '\n') {
257	  current_lineno++;
258	  state = START;
259	}
260	else
261	  state = MIDDLE;
262      }
263      break;
264    case HAD_s:
265      if (c == 'o')
266	state = HAD_so;
267      else  {
268	putchar('.');
269	putchar('s');
270	putchar(c);
271	if (c == '\n') {
272	  current_lineno++;
273	  state = START;
274	}
275	else
276	  state = MIDDLE;
277      }
278      break;
279    case HAD_so:
280      if (c == ' ' || c == '\n' || compatible_flag) {
281	string line;
282	for (; c != EOF && c != '\n'; c = getc(fp))
283	  line += c;
284	current_lineno++;
285	line += '\n';
286	line += '\0';
287	do_so(line.contents());
288	state = START;
289      }
290      else {
291	fputs(".so", stdout);
292	putchar(c);
293	state = MIDDLE;
294      }
295      break;
296    case HAD_l:
297      if (c == 'f')
298	state = HAD_lf;
299      else {
300	putchar('.');
301	putchar('l');
302	putchar(c);
303	if (c == '\n') {
304	  current_lineno++;
305	  state = START;
306	}
307	else
308	  state = MIDDLE;
309      }
310      break;
311    case HAD_lf:
312      if (c == ' ' || c == '\n' || compatible_flag) {
313	string line;
314	for (; c != EOF && c != '\n'; c = getc(fp))
315	  line += c;
316	current_lineno++;
317	line += '\n';
318	line += '\0';
319	interpret_lf_args(line.contents());
320	printf(".lf%s", line.contents());
321	state = START;
322      }
323      else {
324	fputs(".lf", stdout);
325	putchar(c);
326	state = MIDDLE;
327      }
328      break;
329    default:
330      assert(0);
331    }
332  }
333  switch (state) {
334  case HAD_DOT:
335    fputs(".\n", stdout);
336    break;
337  case HAD_l:
338    fputs(".l\n", stdout);
339    break;
340  case HAD_s:
341    fputs(".s\n", stdout);
342    break;
343  case HAD_lf:
344    fputs(".lf\n", stdout);
345    break;
346  case HAD_so:
347    fputs(".so\n", stdout);
348    break;
349  case MIDDLE:
350    putc('\n', stdout);
351    break;
352  case START:
353    break;
354  }
355  if (fp != stdin)
356    fclose(fp);
357  current_filename = 0;
358  return 1;
359}
360