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