1 /* $NetBSD: main.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $ */ 2 3 // -*- C++ -*- 4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 5 Free Software Foundation, Inc. 6 Written by James Clark (jjc@jclark.com) 7 8 This file is part of groff. 9 10 groff is free software; you can redistribute it and/or modify it under 11 the terms of the GNU General Public License as published by the Free 12 Software Foundation; either version 2, or (at your option) any later 13 version. 14 15 groff is distributed in the hope that it will be useful, but WITHOUT ANY 16 WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18 for more details. 19 20 You should have received a copy of the GNU General Public License along 21 with groff; see the file COPYING. If not, write to the Free Software 22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 23 24 #include "eqn.h" 25 #include "stringclass.h" 26 #include "device.h" 27 #include "searchpath.h" 28 #include "macropath.h" 29 #include "htmlhint.h" 30 #include "pbox.h" 31 #include "ctype.h" 32 33 #define STARTUP_FILE "eqnrc" 34 35 extern int yyparse(); 36 extern "C" const char *Version_string; 37 38 static char *delim_search (char *, int); 39 static int inline_equation (FILE *, string &, string &); 40 41 char start_delim = '\0'; 42 char end_delim = '\0'; 43 int non_empty_flag; 44 int inline_flag; 45 int draw_flag = 0; 46 int one_size_reduction_flag = 0; 47 int compatible_flag = 0; 48 int no_newline_in_delim_flag = 0; 49 int html = 0; 50 51 52 int read_line(FILE *fp, string *p) 53 { 54 p->clear(); 55 int c = -1; 56 while ((c = getc(fp)) != EOF) { 57 if (!invalid_input_char(c)) 58 *p += char(c); 59 else 60 error("invalid input character code `%1'", c); 61 if (c == '\n') 62 break; 63 } 64 current_lineno++; 65 return p->length() > 0; 66 } 67 68 void do_file(FILE *fp, const char *filename) 69 { 70 string linebuf; 71 string str; 72 printf(".lf 1 %s\n", filename); 73 current_filename = filename; 74 current_lineno = 0; 75 while (read_line(fp, &linebuf)) { 76 if (linebuf.length() >= 4 77 && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f' 78 && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) { 79 put_string(linebuf, stdout); 80 linebuf += '\0'; 81 if (interpret_lf_args(linebuf.contents() + 3)) 82 current_lineno--; 83 } 84 else if (linebuf.length() >= 4 85 && linebuf[0] == '.' 86 && linebuf[1] == 'E' 87 && linebuf[2] == 'Q' 88 && (linebuf[3] == ' ' || linebuf[3] == '\n' 89 || compatible_flag)) { 90 put_string(linebuf, stdout); 91 int start_lineno = current_lineno + 1; 92 str.clear(); 93 for (;;) { 94 if (!read_line(fp, &linebuf)) 95 fatal("end of file before .EN"); 96 if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') { 97 if (linebuf[2] == 'N' 98 && (linebuf.length() == 3 || linebuf[3] == ' ' 99 || linebuf[3] == '\n' || compatible_flag)) 100 break; 101 else if (linebuf[2] == 'Q' && linebuf.length() > 3 102 && (linebuf[3] == ' ' || linebuf[3] == '\n' 103 || compatible_flag)) 104 fatal("nested .EQ"); 105 } 106 str += linebuf; 107 } 108 str += '\0'; 109 start_string(); 110 init_lex(str.contents(), current_filename, start_lineno); 111 non_empty_flag = 0; 112 inline_flag = 0; 113 yyparse(); 114 restore_compatibility(); 115 if (non_empty_flag) { 116 printf(".lf %d\n", current_lineno - 1); 117 output_string(); 118 } 119 printf(".lf %d\n", current_lineno); 120 put_string(linebuf, stdout); 121 } 122 else if (start_delim != '\0' && linebuf.search(start_delim) >= 0 123 && inline_equation(fp, linebuf, str)) 124 ; 125 else 126 put_string(linebuf, stdout); 127 } 128 current_filename = 0; 129 current_lineno = 0; 130 } 131 132 // Handle an inline equation. Return 1 if it was an inline equation, 133 // otherwise. 134 static int inline_equation(FILE *fp, string &linebuf, string &str) 135 { 136 linebuf += '\0'; 137 char *ptr = &linebuf[0]; 138 char *start = delim_search(ptr, start_delim); 139 if (!start) { 140 // It wasn't a delimiter after all. 141 linebuf.set_length(linebuf.length() - 1); // strip the '\0' 142 return 0; 143 } 144 start_string(); 145 inline_flag = 1; 146 for (;;) { 147 if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) { 148 error("missing `%1'", end_delim); 149 char *nl = strchr(start + 1, '\n'); 150 if (nl != 0) 151 *nl = '\0'; 152 do_text(ptr); 153 break; 154 } 155 int start_lineno = current_lineno; 156 *start = '\0'; 157 do_text(ptr); 158 ptr = start + 1; 159 str.clear(); 160 for (;;) { 161 char *end = strchr(ptr, end_delim); 162 if (end != 0) { 163 *end = '\0'; 164 str += ptr; 165 ptr = end + 1; 166 break; 167 } 168 str += ptr; 169 if (!read_line(fp, &linebuf)) 170 fatal("unterminated `%1' at line %2, looking for `%3'", 171 start_delim, start_lineno, end_delim); 172 linebuf += '\0'; 173 ptr = &linebuf[0]; 174 } 175 str += '\0'; 176 if (html) { 177 printf(".as1 %s ", LINE_STRING); 178 html_begin_suppress(); 179 printf("\n"); 180 } 181 init_lex(str.contents(), current_filename, start_lineno); 182 yyparse(); 183 if (html) { 184 printf(".as1 %s ", LINE_STRING); 185 html_end_suppress(); 186 printf("\n"); 187 } 188 start = delim_search(ptr, start_delim); 189 if (start == 0) { 190 char *nl = strchr(ptr, '\n'); 191 if (nl != 0) 192 *nl = '\0'; 193 do_text(ptr); 194 break; 195 } 196 } 197 restore_compatibility(); 198 printf(".lf %d\n", current_lineno); 199 output_string(); 200 printf(".lf %d\n", current_lineno + 1); 201 return 1; 202 } 203 204 /* Search for delim. Skip over number register and string names etc. */ 205 206 static char *delim_search(char *ptr, int delim) 207 { 208 while (*ptr) { 209 if (*ptr == delim) 210 return ptr; 211 if (*ptr++ == '\\') { 212 switch (*ptr) { 213 case 'n': 214 case '*': 215 case 'f': 216 case 'g': 217 case 'k': 218 switch (*++ptr) { 219 case '\0': 220 case '\\': 221 break; 222 case '(': 223 if (*++ptr != '\\' && *ptr != '\0' 224 && *++ptr != '\\' && *ptr != '\0') 225 ptr++; 226 break; 227 case '[': 228 while (*++ptr != '\0') 229 if (*ptr == ']') { 230 ptr++; 231 break; 232 } 233 break; 234 default: 235 ptr++; 236 break; 237 } 238 break; 239 case '\\': 240 case '\0': 241 break; 242 default: 243 ptr++; 244 break; 245 } 246 } 247 } 248 return 0; 249 } 250 251 void usage(FILE *stream) 252 { 253 fprintf(stream, 254 "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n", 255 program_name); 256 } 257 258 int main(int argc, char **argv) 259 { 260 program_name = argv[0]; 261 static char stderr_buf[BUFSIZ]; 262 setbuf(stderr, stderr_buf); 263 int opt; 264 int load_startup_file = 1; 265 static const struct option long_options[] = { 266 { "help", no_argument, 0, CHAR_MAX + 1 }, 267 { "version", no_argument, 0, 'v' }, 268 { NULL, 0, 0, 0 } 269 }; 270 while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options, 271 NULL)) 272 != EOF) 273 switch (opt) { 274 case 'C': 275 compatible_flag = 1; 276 break; 277 case 'R': // don't load eqnrc 278 load_startup_file = 0; 279 break; 280 case 'M': 281 config_macro_path.command_line_dir(optarg); 282 break; 283 case 'v': 284 { 285 printf("GNU eqn (groff) version %s\n", Version_string); 286 exit(0); 287 break; 288 } 289 case 'd': 290 if (optarg[0] == '\0' || optarg[1] == '\0') 291 error("-d requires two character argument"); 292 else if (invalid_input_char(optarg[0])) 293 error("bad delimiter `%1'", optarg[0]); 294 else if (invalid_input_char(optarg[1])) 295 error("bad delimiter `%1'", optarg[1]); 296 else { 297 start_delim = optarg[0]; 298 end_delim = optarg[1]; 299 } 300 break; 301 case 'f': 302 set_gfont(optarg); 303 break; 304 case 'T': 305 device = optarg; 306 if (strcmp(device, "ps:html") == 0) { 307 device = "ps"; 308 html = 1; 309 } 310 break; 311 case 's': 312 if (!set_gsize(optarg)) 313 error("invalid size `%1'", optarg); 314 break; 315 case 'p': 316 { 317 int n; 318 if (sscanf(optarg, "%d", &n) == 1) 319 set_script_reduction(n); 320 else 321 error("bad size `%1'", optarg); 322 } 323 break; 324 case 'm': 325 { 326 int n; 327 if (sscanf(optarg, "%d", &n) == 1) 328 set_minimum_size(n); 329 else 330 error("bad size `%1'", optarg); 331 } 332 break; 333 case 'r': 334 one_size_reduction_flag = 1; 335 break; 336 case 'D': 337 warning("-D option is obsolete: use `set draw_lines 1' instead"); 338 draw_flag = 1; 339 break; 340 case 'N': 341 no_newline_in_delim_flag = 1; 342 break; 343 case CHAR_MAX + 1: // --help 344 usage(stdout); 345 exit(0); 346 break; 347 case '?': 348 usage(stderr); 349 exit(1); 350 break; 351 default: 352 assert(0); 353 } 354 init_table(device); 355 init_char_table(); 356 printf(".if !'\\*(.T'%s' " 357 ".if !'\\*(.T'html' " // the html device uses `-Tps' to render 358 // equations as images 359 ".tm warning: %s should have been given a `-T\\*(.T' option\n", 360 device, program_name); 361 printf(".if '\\*(.T'html' " 362 ".if !'%s'ps' " 363 ".tm warning: %s should have been given a `-Tps' option\n", 364 device, program_name); 365 printf(".if '\\*(.T'html' " 366 ".if !'%s'ps' " 367 ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n", 368 device); 369 if (load_startup_file) { 370 char *path; 371 FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path); 372 if (fp) { 373 do_file(fp, path); 374 fclose(fp); 375 a_delete path; 376 } 377 } 378 if (optind >= argc) 379 do_file(stdin, "-"); 380 else 381 for (int i = optind; i < argc; i++) 382 if (strcmp(argv[i], "-") == 0) 383 do_file(stdin, "-"); 384 else { 385 errno = 0; 386 FILE *fp = fopen(argv[i], "r"); 387 if (!fp) 388 fatal("can't open `%1': %2", argv[i], strerror(errno)); 389 else { 390 do_file(fp, argv[i]); 391 fclose(fp); 392 } 393 } 394 if (ferror(stdout) || fflush(stdout) < 0) 395 fatal("output error"); 396 return 0; 397 } 398