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