1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989-1992, 2000, 2001, 2003, 2004, 2005
3151497Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "lib.h"
23114402Sru
24114402Sru#include <ctype.h>
25114402Sru#include <assert.h>
26114402Sru#include <stdlib.h>
27114402Sru#include <errno.h>
28114402Sru#include "errarg.h"
29114402Sru#include "error.h"
30114402Sru#include "stringclass.h"
31114402Sru#include "nonposix.h"
32151497Sru#include "searchpath.h"
33114402Sru
34151497Sru// The include search path initially contains only the current directory.
35151497Srustatic search_path include_search_path(0, 0, 0, 1);
36114402Sru
37114402Sruint compatible_flag = 0;
38114402Sruint raw_flag = 0;
39114402Sruint tex_flag = 0;
40114402Sru
41114402Sruextern "C" const char *Version_string;
42114402Sru
43114402Sruint do_file(const char *filename);
44114402Sru
45114402Sru
46114402Sruvoid usage(FILE *stream)
47114402Sru{
48114402Sru  fprintf(stream, "usage: %s [ -Crtv ] [ -I file ] [ files ]\n", program_name);
49114402Sru}
50114402Sru
51114402Sruint main(int argc, char **argv)
52114402Sru{
53114402Sru  program_name = argv[0];
54114402Sru  int opt;
55114402Sru  static const struct option long_options[] = {
56114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
57114402Sru    { "version", no_argument, 0, 'v' },
58114402Sru    { NULL, 0, 0, 0 }
59114402Sru  };
60114402Sru  while ((opt = getopt_long(argc, argv, "CI:rtv", long_options, NULL)) != EOF)
61114402Sru    switch (opt) {
62114402Sru    case 'v':
63114402Sru      {
64114402Sru	printf("GNU soelim (groff) version %s\n", Version_string);
65114402Sru	exit(0);
66114402Sru	break;
67114402Sru      }
68114402Sru    case 'C':
69114402Sru      compatible_flag = 1;
70114402Sru      break;
71114402Sru    case 'I':
72151497Sru      include_search_path.command_line_dir(optarg);
73114402Sru      break;
74114402Sru    case 'r':
75114402Sru      raw_flag = 1;
76114402Sru      break;
77114402Sru    case 't':
78114402Sru      tex_flag = 1;
79114402Sru      break;
80114402Sru    case CHAR_MAX + 1: // --help
81114402Sru      usage(stdout);
82114402Sru      exit(0);
83114402Sru      break;
84114402Sru    case '?':
85114402Sru      usage(stderr);
86114402Sru      exit(1);
87114402Sru      break;
88114402Sru    default:
89114402Sru      assert(0);
90114402Sru    }
91114402Sru  int nbad = 0;
92114402Sru  if (optind >= argc)
93114402Sru    nbad += !do_file("-");
94114402Sru  else
95114402Sru    for (int i = optind; i < argc; i++)
96114402Sru      nbad += !do_file(argv[i]);
97114402Sru  if (ferror(stdout) || fflush(stdout) < 0)
98114402Sru    fatal("output error");
99114402Sru  return nbad != 0;
100114402Sru}
101114402Sru
102114402Sruvoid set_location()
103114402Sru{
104114402Sru  if(!raw_flag) {
105114402Sru    if(!tex_flag)
106114402Sru      printf(".lf %d %s\n", current_lineno, current_filename);
107114402Sru    else
108114402Sru      printf("%% file %s, line %d\n", current_filename, current_lineno);
109114402Sru  }
110114402Sru}
111114402Sru
112114402Sruvoid do_so(const char *line)
113114402Sru{
114114402Sru  const char *p = line;
115114402Sru  while (*p == ' ')
116114402Sru    p++;
117114402Sru  string filename;
118114402Sru  int success = 1;
119114402Sru  for (const char *q = p;
120114402Sru       success && *q != '\0' && *q != '\n' && *q != ' ';
121114402Sru       q++)
122114402Sru    if (*q == '\\') {
123114402Sru      switch (*++q) {
124114402Sru      case 'e':
125114402Sru      case '\\':
126114402Sru	filename += '\\';
127114402Sru	break;
128114402Sru      case ' ':
129114402Sru	filename += ' ';
130114402Sru	break;
131114402Sru      default:
132114402Sru	success = 0;
133114402Sru	break;
134114402Sru      }
135114402Sru    }
136114402Sru    else
137114402Sru      filename += char(*q);
138114402Sru  if (success && filename.length() > 0) {
139114402Sru    filename += '\0';
140114402Sru    const char *fn = current_filename;
141114402Sru    int ln = current_lineno;
142114402Sru    current_lineno--;
143114402Sru    if (do_file(filename.contents())) {
144114402Sru      current_filename = fn;
145114402Sru      current_lineno = ln;
146114402Sru      set_location();
147114402Sru      return;
148114402Sru    }
149114402Sru    current_lineno++;
150114402Sru  }
151114402Sru  fputs(".so", stdout);
152114402Sru  fputs(line, stdout);
153114402Sru}
154114402Sru
155114402Sruint do_file(const char *filename)
156114402Sru{
157151497Sru  char *file_name_in_path = 0;
158151497Sru  FILE *fp = include_search_path.open_file_cautious(filename,
159151497Sru						    &file_name_in_path);
160151497Sru  int err = errno;
161151497Sru  string whole_filename(file_name_in_path ? file_name_in_path : filename);
162151497Sru  whole_filename += '\0';
163151497Sru  a_delete file_name_in_path;
164151497Sru  if (fp == 0) {
165151497Sru    error("can't open `%1': %2", whole_filename.contents(), strerror(err));
166151497Sru    return 0;
167114402Sru  }
168114402Sru  current_filename = whole_filename.contents();
169114402Sru  current_lineno = 1;
170114402Sru  set_location();
171114402Sru  enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
172114402Sru  for (;;) {
173114402Sru    int c = getc(fp);
174114402Sru    if (c == EOF)
175114402Sru      break;
176114402Sru    switch (state) {
177114402Sru    case START:
178114402Sru      if (c == '.')
179114402Sru	state = HAD_DOT;
180114402Sru      else {
181114402Sru	putchar(c);
182114402Sru	if (c == '\n') {
183114402Sru	  current_lineno++;
184114402Sru	  state = START;
185114402Sru	}
186114402Sru	else
187114402Sru	  state = MIDDLE;
188114402Sru      }
189114402Sru      break;
190114402Sru    case MIDDLE:
191114402Sru      putchar(c);
192114402Sru      if (c == '\n') {
193114402Sru	current_lineno++;
194114402Sru	state = START;
195114402Sru      }
196114402Sru      break;
197114402Sru    case HAD_DOT:
198114402Sru      if (c == 's')
199114402Sru	state = HAD_s;
200114402Sru      else if (c == 'l')
201114402Sru	state = HAD_l;
202114402Sru      else {
203114402Sru	putchar('.');
204114402Sru	putchar(c);
205114402Sru	if (c == '\n') {
206114402Sru	  current_lineno++;
207114402Sru	  state = START;
208114402Sru	}
209114402Sru	else
210114402Sru	  state = MIDDLE;
211114402Sru      }
212114402Sru      break;
213114402Sru    case HAD_s:
214114402Sru      if (c == 'o')
215114402Sru	state = HAD_so;
216114402Sru      else  {
217114402Sru	putchar('.');
218114402Sru	putchar('s');
219114402Sru	putchar(c);
220114402Sru	if (c == '\n') {
221114402Sru	  current_lineno++;
222114402Sru	  state = START;
223114402Sru	}
224114402Sru	else
225114402Sru	  state = MIDDLE;
226114402Sru      }
227114402Sru      break;
228114402Sru    case HAD_so:
229114402Sru      if (c == ' ' || c == '\n' || compatible_flag) {
230114402Sru	string line;
231114402Sru	for (; c != EOF && c != '\n'; c = getc(fp))
232114402Sru	  line += c;
233114402Sru	current_lineno++;
234114402Sru	line += '\n';
235114402Sru	line += '\0';
236114402Sru	do_so(line.contents());
237114402Sru	state = START;
238114402Sru      }
239114402Sru      else {
240114402Sru	fputs(".so", stdout);
241114402Sru	putchar(c);
242114402Sru	state = MIDDLE;
243114402Sru      }
244114402Sru      break;
245114402Sru    case HAD_l:
246114402Sru      if (c == 'f')
247114402Sru	state = HAD_lf;
248114402Sru      else {
249114402Sru	putchar('.');
250114402Sru	putchar('l');
251114402Sru	putchar(c);
252114402Sru	if (c == '\n') {
253114402Sru	  current_lineno++;
254114402Sru	  state = START;
255114402Sru	}
256114402Sru	else
257114402Sru	  state = MIDDLE;
258114402Sru      }
259114402Sru      break;
260114402Sru    case HAD_lf:
261114402Sru      if (c == ' ' || c == '\n' || compatible_flag) {
262114402Sru	string line;
263114402Sru	for (; c != EOF && c != '\n'; c = getc(fp))
264114402Sru	  line += c;
265114402Sru	current_lineno++;
266114402Sru	line += '\n';
267114402Sru	line += '\0';
268114402Sru	interpret_lf_args(line.contents());
269114402Sru	printf(".lf%s", line.contents());
270114402Sru	state = START;
271114402Sru      }
272114402Sru      else {
273114402Sru	fputs(".lf", stdout);
274114402Sru	putchar(c);
275114402Sru	state = MIDDLE;
276114402Sru      }
277114402Sru      break;
278114402Sru    default:
279114402Sru      assert(0);
280114402Sru    }
281114402Sru  }
282114402Sru  switch (state) {
283114402Sru  case HAD_DOT:
284114402Sru    fputs(".\n", stdout);
285114402Sru    break;
286114402Sru  case HAD_l:
287114402Sru    fputs(".l\n", stdout);
288114402Sru    break;
289114402Sru  case HAD_s:
290114402Sru    fputs(".s\n", stdout);
291114402Sru    break;
292114402Sru  case HAD_lf:
293114402Sru    fputs(".lf\n", stdout);
294114402Sru    break;
295114402Sru  case HAD_so:
296114402Sru    fputs(".so\n", stdout);
297114402Sru    break;
298114402Sru  case MIDDLE:
299114402Sru    putc('\n', stdout);
300114402Sru    break;
301114402Sru  case START:
302114402Sru    break;
303114402Sru  }
304114402Sru  if (fp != stdin)
305114402Sru    fclose(fp);
306114402Sru  current_filename = 0;
307114402Sru  return 1;
308114402Sru}
309