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