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