1114402Sru// -*- C++ -*- 2114402Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 3114402Sru 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 "eqn.h" 23114402Sru#include "stringclass.h" 24114402Sru#include "device.h" 25114402Sru#include "searchpath.h" 26114402Sru#include "macropath.h" 27114402Sru#include "htmlhint.h" 28114402Sru#include "pbox.h" 29114402Sru#include "ctype.h" 30114402Sru 31114402Sru#define STARTUP_FILE "eqnrc" 32114402Sru 33114402Sruextern int yyparse(); 34114402Sruextern "C" const char *Version_string; 35114402Sru 36114402Srustatic char *delim_search (char *, int); 37114402Srustatic int inline_equation (FILE *, string &, string &); 38114402Sru 39114402Sruchar start_delim = '\0'; 40114402Sruchar end_delim = '\0'; 41114402Sruint non_empty_flag; 42114402Sruint inline_flag; 43114402Sruint draw_flag = 0; 44114402Sruint one_size_reduction_flag = 0; 45114402Sruint compatible_flag = 0; 46114402Sruint no_newline_in_delim_flag = 0; 47114402Sruint html = 0; 48114402Sru 49114402Sru 50114402Sruint read_line(FILE *fp, string *p) 51114402Sru{ 52114402Sru p->clear(); 53114402Sru int c = -1; 54114402Sru while ((c = getc(fp)) != EOF) { 55114402Sru if (!invalid_input_char(c)) 56114402Sru *p += char(c); 57114402Sru else 58114402Sru error("invalid input character code `%1'", c); 59114402Sru if (c == '\n') 60114402Sru break; 61114402Sru } 62114402Sru current_lineno++; 63114402Sru return p->length() > 0; 64114402Sru} 65114402Sru 66114402Sruvoid do_file(FILE *fp, const char *filename) 67114402Sru{ 68114402Sru string linebuf; 69114402Sru string str; 70114402Sru printf(".lf 1 %s\n", filename); 71114402Sru current_filename = filename; 72114402Sru current_lineno = 0; 73114402Sru while (read_line(fp, &linebuf)) { 74114402Sru if (linebuf.length() >= 4 75114402Sru && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f' 76114402Sru && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) { 77114402Sru put_string(linebuf, stdout); 78114402Sru linebuf += '\0'; 79114402Sru if (interpret_lf_args(linebuf.contents() + 3)) 80114402Sru current_lineno--; 81114402Sru } 82114402Sru else if (linebuf.length() >= 4 83114402Sru && linebuf[0] == '.' 84114402Sru && linebuf[1] == 'E' 85114402Sru && linebuf[2] == 'Q' 86114402Sru && (linebuf[3] == ' ' || linebuf[3] == '\n' 87114402Sru || compatible_flag)) { 88114402Sru put_string(linebuf, stdout); 89114402Sru int start_lineno = current_lineno + 1; 90114402Sru str.clear(); 91114402Sru for (;;) { 92114402Sru if (!read_line(fp, &linebuf)) 93114402Sru fatal("end of file before .EN"); 94114402Sru if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') { 95114402Sru if (linebuf[2] == 'N' 96114402Sru && (linebuf.length() == 3 || linebuf[3] == ' ' 97114402Sru || linebuf[3] == '\n' || compatible_flag)) 98114402Sru break; 99114402Sru else if (linebuf[2] == 'Q' && linebuf.length() > 3 100114402Sru && (linebuf[3] == ' ' || linebuf[3] == '\n' 101114402Sru || compatible_flag)) 102114402Sru fatal("nested .EQ"); 103114402Sru } 104114402Sru str += linebuf; 105114402Sru } 106114402Sru str += '\0'; 107114402Sru start_string(); 108114402Sru init_lex(str.contents(), current_filename, start_lineno); 109114402Sru non_empty_flag = 0; 110114402Sru inline_flag = 0; 111114402Sru yyparse(); 112114402Sru restore_compatibility(); 113114402Sru if (non_empty_flag) { 114114402Sru printf(".lf %d\n", current_lineno - 1); 115114402Sru output_string(); 116114402Sru } 117114402Sru printf(".lf %d\n", current_lineno); 118114402Sru put_string(linebuf, stdout); 119114402Sru } 120114402Sru else if (start_delim != '\0' && linebuf.search(start_delim) >= 0 121114402Sru && inline_equation(fp, linebuf, str)) 122114402Sru ; 123114402Sru else 124114402Sru put_string(linebuf, stdout); 125114402Sru } 126114402Sru current_filename = 0; 127114402Sru current_lineno = 0; 128114402Sru} 129114402Sru 130114402Sru// Handle an inline equation. Return 1 if it was an inline equation, 131114402Sru// otherwise. 132114402Srustatic int inline_equation(FILE *fp, string &linebuf, string &str) 133114402Sru{ 134114402Sru linebuf += '\0'; 135114402Sru char *ptr = &linebuf[0]; 136114402Sru char *start = delim_search(ptr, start_delim); 137114402Sru if (!start) { 138114402Sru // It wasn't a delimiter after all. 139114402Sru linebuf.set_length(linebuf.length() - 1); // strip the '\0' 140114402Sru return 0; 141114402Sru } 142114402Sru start_string(); 143114402Sru inline_flag = 1; 144114402Sru for (;;) { 145114402Sru if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) { 146114402Sru error("missing `%1'", end_delim); 147114402Sru char *nl = strchr(start + 1, '\n'); 148114402Sru if (nl != 0) 149114402Sru *nl = '\0'; 150114402Sru do_text(ptr); 151114402Sru break; 152114402Sru } 153114402Sru int start_lineno = current_lineno; 154114402Sru *start = '\0'; 155114402Sru do_text(ptr); 156114402Sru ptr = start + 1; 157114402Sru str.clear(); 158114402Sru for (;;) { 159114402Sru char *end = strchr(ptr, end_delim); 160114402Sru if (end != 0) { 161114402Sru *end = '\0'; 162114402Sru str += ptr; 163114402Sru ptr = end + 1; 164114402Sru break; 165114402Sru } 166114402Sru str += ptr; 167114402Sru if (!read_line(fp, &linebuf)) 168114402Sru fatal("unterminated `%1' at line %2, looking for `%3'", 169114402Sru start_delim, start_lineno, end_delim); 170114402Sru linebuf += '\0'; 171114402Sru ptr = &linebuf[0]; 172114402Sru } 173114402Sru str += '\0'; 174114402Sru if (html) { 175114402Sru printf(".as1 %s ", LINE_STRING); 176114402Sru html_begin_suppress(); 177114402Sru printf("\n"); 178114402Sru } 179114402Sru init_lex(str.contents(), current_filename, start_lineno); 180114402Sru yyparse(); 181114402Sru if (html) { 182114402Sru printf(".as1 %s ", LINE_STRING); 183114402Sru html_end_suppress(); 184114402Sru printf("\n"); 185114402Sru } 186114402Sru start = delim_search(ptr, start_delim); 187114402Sru if (start == 0) { 188114402Sru char *nl = strchr(ptr, '\n'); 189114402Sru if (nl != 0) 190114402Sru *nl = '\0'; 191114402Sru do_text(ptr); 192114402Sru break; 193114402Sru } 194114402Sru } 195114402Sru restore_compatibility(); 196114402Sru printf(".lf %d\n", current_lineno); 197114402Sru output_string(); 198114402Sru printf(".lf %d\n", current_lineno + 1); 199114402Sru return 1; 200114402Sru} 201114402Sru 202114402Sru/* Search for delim. Skip over number register and string names etc. */ 203114402Sru 204114402Srustatic char *delim_search(char *ptr, int delim) 205114402Sru{ 206114402Sru while (*ptr) { 207114402Sru if (*ptr == delim) 208114402Sru return ptr; 209114402Sru if (*ptr++ == '\\') { 210114402Sru switch (*ptr) { 211114402Sru case 'n': 212114402Sru case '*': 213114402Sru case 'f': 214114402Sru case 'g': 215114402Sru case 'k': 216114402Sru switch (*++ptr) { 217114402Sru case '\0': 218114402Sru case '\\': 219114402Sru break; 220114402Sru case '(': 221114402Sru if (*++ptr != '\\' && *ptr != '\0' 222114402Sru && *++ptr != '\\' && *ptr != '\0') 223114402Sru ptr++; 224114402Sru break; 225114402Sru case '[': 226114402Sru while (*++ptr != '\0') 227114402Sru if (*ptr == ']') { 228114402Sru ptr++; 229114402Sru break; 230114402Sru } 231114402Sru break; 232114402Sru default: 233114402Sru ptr++; 234114402Sru break; 235114402Sru } 236114402Sru break; 237114402Sru case '\\': 238114402Sru case '\0': 239114402Sru break; 240114402Sru default: 241114402Sru ptr++; 242114402Sru break; 243114402Sru } 244114402Sru } 245114402Sru } 246114402Sru return 0; 247114402Sru} 248114402Sru 249114402Sruvoid usage(FILE *stream) 250114402Sru{ 251114402Sru fprintf(stream, 252114402Sru "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n", 253114402Sru program_name); 254114402Sru} 255114402Sru 256114402Sruint main(int argc, char **argv) 257114402Sru{ 258114402Sru program_name = argv[0]; 259114402Sru static char stderr_buf[BUFSIZ]; 260114402Sru setbuf(stderr, stderr_buf); 261114402Sru int opt; 262114402Sru int load_startup_file = 1; 263114402Sru static const struct option long_options[] = { 264114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 265114402Sru { "version", no_argument, 0, 'v' }, 266114402Sru { NULL, 0, 0, 0 } 267114402Sru }; 268114402Sru while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options, 269114402Sru NULL)) 270114402Sru != EOF) 271114402Sru switch (opt) { 272114402Sru case 'C': 273114402Sru compatible_flag = 1; 274114402Sru break; 275114402Sru case 'R': // don't load eqnrc 276114402Sru load_startup_file = 0; 277114402Sru break; 278114402Sru case 'M': 279114402Sru config_macro_path.command_line_dir(optarg); 280114402Sru break; 281114402Sru case 'v': 282114402Sru { 283114402Sru printf("GNU eqn (groff) version %s\n", Version_string); 284114402Sru exit(0); 285114402Sru break; 286114402Sru } 287114402Sru case 'd': 288114402Sru if (optarg[0] == '\0' || optarg[1] == '\0') 289114402Sru error("-d requires two character argument"); 290114402Sru else if (invalid_input_char(optarg[0])) 291114402Sru error("bad delimiter `%1'", optarg[0]); 292114402Sru else if (invalid_input_char(optarg[1])) 293114402Sru error("bad delimiter `%1'", optarg[1]); 294114402Sru else { 295114402Sru start_delim = optarg[0]; 296114402Sru end_delim = optarg[1]; 297114402Sru } 298114402Sru break; 299114402Sru case 'f': 300114402Sru set_gfont(optarg); 301114402Sru break; 302114402Sru case 'T': 303114402Sru device = optarg; 304114402Sru if (strcmp(device, "ps:html") == 0) { 305114402Sru device = "ps"; 306114402Sru html = 1; 307114402Sru } 308114402Sru break; 309114402Sru case 's': 310114402Sru if (!set_gsize(optarg)) 311114402Sru error("invalid size `%1'", optarg); 312114402Sru break; 313114402Sru case 'p': 314114402Sru { 315114402Sru int n; 316114402Sru if (sscanf(optarg, "%d", &n) == 1) 317114402Sru set_script_reduction(n); 318114402Sru else 319114402Sru error("bad size `%1'", optarg); 320114402Sru } 321114402Sru break; 322114402Sru case 'm': 323114402Sru { 324114402Sru int n; 325114402Sru if (sscanf(optarg, "%d", &n) == 1) 326114402Sru set_minimum_size(n); 327114402Sru else 328114402Sru error("bad size `%1'", optarg); 329114402Sru } 330114402Sru break; 331114402Sru case 'r': 332114402Sru one_size_reduction_flag = 1; 333114402Sru break; 334114402Sru case 'D': 335114402Sru warning("-D option is obsolete: use `set draw_lines 1' instead"); 336114402Sru draw_flag = 1; 337114402Sru break; 338114402Sru case 'N': 339114402Sru no_newline_in_delim_flag = 1; 340114402Sru break; 341114402Sru case CHAR_MAX + 1: // --help 342114402Sru usage(stdout); 343114402Sru exit(0); 344114402Sru break; 345114402Sru case '?': 346114402Sru usage(stderr); 347114402Sru exit(1); 348114402Sru break; 349114402Sru default: 350114402Sru assert(0); 351114402Sru } 352114402Sru init_table(device); 353114402Sru init_char_table(); 354114402Sru printf(".if !'\\*(.T'%s' " 355114402Sru ".if !'\\*(.T'html' " // the html device uses `-Tps' to render 356114402Sru // equations as images 357114402Sru ".tm warning: %s should have been given a `-T\\*(.T' option\n", 358114402Sru device, program_name); 359114402Sru printf(".if '\\*(.T'html' " 360114402Sru ".if !'%s'ps' " 361114402Sru ".tm warning: %s should have been given a `-Tps' option\n", 362114402Sru device, program_name); 363114402Sru printf(".if '\\*(.T'html' " 364114402Sru ".if !'%s'ps' " 365114402Sru ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n", 366114402Sru device); 367114402Sru if (load_startup_file) { 368114402Sru char *path; 369114402Sru FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path); 370114402Sru if (fp) { 371114402Sru do_file(fp, path); 372114402Sru fclose(fp); 373114402Sru a_delete path; 374114402Sru } 375114402Sru } 376114402Sru if (optind >= argc) 377114402Sru do_file(stdin, "-"); 378114402Sru else 379114402Sru for (int i = optind; i < argc; i++) 380114402Sru if (strcmp(argv[i], "-") == 0) 381114402Sru do_file(stdin, "-"); 382114402Sru else { 383114402Sru errno = 0; 384114402Sru FILE *fp = fopen(argv[i], "r"); 385114402Sru if (!fp) 386114402Sru fatal("can't open `%1': %2", argv[i], strerror(errno)); 387114402Sru else { 388114402Sru do_file(fp, argv[i]); 389114402Sru fclose(fp); 390114402Sru } 391114402Sru } 392114402Sru if (ferror(stdout) || fflush(stdout) < 0) 393114402Sru fatal("output error"); 394114402Sru return 0; 395114402Sru} 396