1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005 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 "eqn_tab.h" 24#include "stringclass.h" 25#include "ptable.h" 26 27 28// declarations to avoid friend name injection problems 29int get_char(); 30int peek_char(); 31int get_location(char **, int *); 32 33struct definition { 34 char is_macro; 35 char is_simple; 36 union { 37 int tok; 38 char *contents; 39 }; 40 definition(); 41 ~definition(); 42}; 43 44definition::definition() : is_macro(1), is_simple(0) 45{ 46 contents = 0; 47} 48 49definition::~definition() 50{ 51 if (is_macro) 52 a_delete contents; 53} 54 55declare_ptable(definition) 56implement_ptable(definition) 57 58PTABLE(definition) macro_table; 59 60static struct { 61 const char *name; 62 int token; 63} token_table[] = { 64 { "over", OVER }, 65 { "smallover", SMALLOVER }, 66 { "sqrt", SQRT }, 67 { "sub", SUB }, 68 { "sup", SUP }, 69 { "lpile", LPILE }, 70 { "rpile", RPILE }, 71 { "cpile", CPILE }, 72 { "pile", PILE }, 73 { "left", LEFT }, 74 { "right", RIGHT }, 75 { "to", TO }, 76 { "from", FROM }, 77 { "size", SIZE }, 78 { "font", FONT }, 79 { "roman", ROMAN }, 80 { "bold", BOLD }, 81 { "italic", ITALIC }, 82 { "fat", FAT }, 83 { "bar", BAR }, 84 { "under", UNDER }, 85 { "accent", ACCENT }, 86 { "uaccent", UACCENT }, 87 { "above", ABOVE }, 88 { "fwd", FWD }, 89 { "back", BACK }, 90 { "down", DOWN }, 91 { "up", UP }, 92 { "matrix", MATRIX }, 93 { "col", COL }, 94 { "lcol", LCOL }, 95 { "rcol", RCOL }, 96 { "ccol", CCOL }, 97 { "mark", MARK }, 98 { "lineup", LINEUP }, 99 { "space", SPACE }, 100 { "gfont", GFONT }, 101 { "gsize", GSIZE }, 102 { "define", DEFINE }, 103 { "sdefine", SDEFINE }, 104 { "ndefine", NDEFINE }, 105 { "tdefine", TDEFINE }, 106 { "undef", UNDEF }, 107 { "ifdef", IFDEF }, 108 { "include", INCLUDE }, 109 { "copy", INCLUDE }, 110 { "delim", DELIM }, 111 { "chartype", CHARTYPE }, 112 { "type", TYPE }, 113 { "vcenter", VCENTER }, 114 { "set", SET }, 115 { "opprime", PRIME }, 116 { "grfont", GRFONT }, 117 { "gbfont", GBFONT }, 118 { "split", SPLIT }, 119 { "nosplit", NOSPLIT }, 120 { "special", SPECIAL }, 121}; 122 123static struct { 124 const char *name; 125 const char *def; 126} def_table[] = { 127 { "ALPHA", "\\(*A" }, 128 { "BETA", "\\(*B" }, 129 { "CHI", "\\(*X" }, 130 { "DELTA", "\\(*D" }, 131 { "EPSILON", "\\(*E" }, 132 { "ETA", "\\(*Y" }, 133 { "GAMMA", "\\(*G" }, 134 { "IOTA", "\\(*I" }, 135 { "KAPPA", "\\(*K" }, 136 { "LAMBDA", "\\(*L" }, 137 { "MU", "\\(*M" }, 138 { "NU", "\\(*N" }, 139 { "OMEGA", "\\(*W" }, 140 { "OMICRON", "\\(*O" }, 141 { "PHI", "\\(*F" }, 142 { "PI", "\\(*P" }, 143 { "PSI", "\\(*Q" }, 144 { "RHO", "\\(*R" }, 145 { "SIGMA", "\\(*S" }, 146 { "TAU", "\\(*T" }, 147 { "THETA", "\\(*H" }, 148 { "UPSILON", "\\(*U" }, 149 { "XI", "\\(*C" }, 150 { "ZETA", "\\(*Z" }, 151 { "Alpha", "\\(*A" }, 152 { "Beta", "\\(*B" }, 153 { "Chi", "\\(*X" }, 154 { "Delta", "\\(*D" }, 155 { "Epsilon", "\\(*E" }, 156 { "Eta", "\\(*Y" }, 157 { "Gamma", "\\(*G" }, 158 { "Iota", "\\(*I" }, 159 { "Kappa", "\\(*K" }, 160 { "Lambda", "\\(*L" }, 161 { "Mu", "\\(*M" }, 162 { "Nu", "\\(*N" }, 163 { "Omega", "\\(*W" }, 164 { "Omicron", "\\(*O" }, 165 { "Phi", "\\(*F" }, 166 { "Pi", "\\(*P" }, 167 { "Psi", "\\(*Q" }, 168 { "Rho", "\\(*R" }, 169 { "Sigma", "\\(*S" }, 170 { "Tau", "\\(*T" }, 171 { "Theta", "\\(*H" }, 172 { "Upsilon", "\\(*U" }, 173 { "Xi", "\\(*C" }, 174 { "Zeta", "\\(*Z" }, 175 { "alpha", "\\(*a" }, 176 { "beta", "\\(*b" }, 177 { "chi", "\\(*x" }, 178 { "delta", "\\(*d" }, 179 { "epsilon", "\\(*e" }, 180 { "eta", "\\(*y" }, 181 { "gamma", "\\(*g" }, 182 { "iota", "\\(*i" }, 183 { "kappa", "\\(*k" }, 184 { "lambda", "\\(*l" }, 185 { "mu", "\\(*m" }, 186 { "nu", "\\(*n" }, 187 { "omega", "\\(*w" }, 188 { "omicron", "\\(*o" }, 189 { "phi", "\\(*f" }, 190 { "pi", "\\(*p" }, 191 { "psi", "\\(*q" }, 192 { "rho", "\\(*r" }, 193 { "sigma", "\\(*s" }, 194 { "tau", "\\(*t" }, 195 { "theta", "\\(*h" }, 196 { "upsilon", "\\(*u" }, 197 { "xi", "\\(*c" }, 198 { "zeta", "\\(*z" }, 199 { "max", "{type \"operator\" roman \"max\"}" }, 200 { "min", "{type \"operator\" roman \"min\"}" }, 201 { "lim", "{type \"operator\" roman \"lim\"}" }, 202 { "sin", "{type \"operator\" roman \"sin\"}" }, 203 { "cos", "{type \"operator\" roman \"cos\"}" }, 204 { "tan", "{type \"operator\" roman \"tan\"}" }, 205 { "sinh", "{type \"operator\" roman \"sinh\"}" }, 206 { "cosh", "{type \"operator\" roman \"cosh\"}" }, 207 { "tanh", "{type \"operator\" roman \"tanh\"}" }, 208 { "arc", "{type \"operator\" roman \"arc\"}" }, 209 { "log", "{type \"operator\" roman \"log\"}" }, 210 { "ln", "{type \"operator\" roman \"ln\"}" }, 211 { "exp", "{type \"operator\" roman \"exp\"}" }, 212 { "Re", "{type \"operator\" roman \"Re\"}" }, 213 { "Im", "{type \"operator\" roman \"Im\"}" }, 214 { "det", "{type \"operator\" roman \"det\"}" }, 215 { "and", "{roman \"and\"}" }, 216 { "if", "{roman \"if\"}" }, 217 { "for", "{roman \"for\"}" }, 218 { "sum", "{type \"operator\" vcenter size +5 \\(*S}" }, 219 { "prod", "{type \"operator\" vcenter size +5 \\(*P}" }, 220 { "int", "{type \"operator\" vcenter size +8 \\(is}" }, 221 { "union", "{type \"operator\" vcenter size +5 \\(cu}" }, 222 { "inter", "{type \"operator\" vcenter size +5 \\(ca}" }, 223 { "times", "type \"binary\" \\(mu" }, 224 { "ldots", "type \"inner\" { . . . }" }, 225 { "inf", "\\(if" }, 226 { "partial", "\\(pd" }, 227 { "nothing", "\"\"" }, 228 { "half", "{1 smallover 2}" }, 229 { "hat_def", "roman \"^\"" }, 230 { "hat", "accent { hat_def }" }, 231 { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" }, 232 { "dot", "accent { dot_def }" }, 233 { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" }, 234 { "dotdot", "accent { dotdot_def }" }, 235 { "tilde_def", "\"~\"" }, 236 { "tilde", "accent { tilde_def }" }, 237 { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" }, 238 { "utilde", "uaccent { utilde_def }" }, 239 { "vec_def", "up 52 size -5 \\(->" }, 240 { "vec", "accent { vec_def }" }, 241 { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" }, 242 { "dyad", "accent { dyad_def }" }, 243 { "==", "type \"relation\" \\(==" }, 244 { "!=", "type \"relation\" \\(!=" }, 245 { "+-", "type \"binary\" \\(+-" }, 246 { "->", "type \"relation\" \\(->" }, 247 { "<-", "type \"relation\" \\(<-" }, 248 { "<<", "{ < back 20 < }" }, 249 { ">>", "{ > back 20 > }" }, 250 { "...", "type \"inner\" vcenter { . . . }" }, 251 { "prime", "'" }, 252 { "approx", "type \"relation\" \"\\(~=\"" }, 253 { "grad", "\\(gr" }, 254 { "del", "\\(gr" }, 255 { "cdot", "type \"binary\" vcenter ." }, 256 { "dollar", "$" }, 257}; 258 259void init_table(const char *device) 260{ 261 unsigned int i; 262 for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) { 263 definition *def = new definition[1]; 264 def->is_macro = 0; 265 def->tok = token_table[i].token; 266 macro_table.define(token_table[i].name, def); 267 } 268 for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) { 269 definition *def = new definition[1]; 270 def->is_macro = 1; 271 def->contents = strsave(def_table[i].def); 272 def->is_simple = 1; 273 macro_table.define(def_table[i].name, def); 274 } 275 definition *def = new definition[1]; 276 def->is_macro = 1; 277 def->contents = strsave("1"); 278 macro_table.define(device, def); 279} 280 281class input { 282 input *next; 283public: 284 input(input *p); 285 virtual ~input(); 286 virtual int get() = 0; 287 virtual int peek() = 0; 288 virtual int get_location(char **, int *); 289 290 friend int get_char(); 291 friend int peek_char(); 292 friend int get_location(char **, int *); 293 friend void init_lex(const char *str, const char *filename, int lineno); 294}; 295 296class file_input : public input { 297 FILE *fp; 298 char *filename; 299 int lineno; 300 string line; 301 const char *ptr; 302 int read_line(); 303public: 304 file_input(FILE *, const char *, input *); 305 ~file_input(); 306 int get(); 307 int peek(); 308 int get_location(char **, int *); 309}; 310 311 312class macro_input : public input { 313 char *s; 314 char *p; 315public: 316 macro_input(const char *, input *); 317 ~macro_input(); 318 int get(); 319 int peek(); 320}; 321 322class top_input : public macro_input { 323 char *filename; 324 int lineno; 325 public: 326 top_input(const char *, const char *, int, input *); 327 ~top_input(); 328 int get(); 329 int get_location(char **, int *); 330}; 331 332class argument_macro_input: public input { 333 char *s; 334 char *p; 335 char *ap; 336 int argc; 337 char *argv[9]; 338public: 339 argument_macro_input(const char *, int, char **, input *); 340 ~argument_macro_input(); 341 int get(); 342 int peek(); 343}; 344 345input::input(input *x) : next(x) 346{ 347} 348 349input::~input() 350{ 351} 352 353int input::get_location(char **, int *) 354{ 355 return 0; 356} 357 358file_input::file_input(FILE *f, const char *fn, input *p) 359: input(p), lineno(0), ptr("") 360{ 361 fp = f; 362 filename = strsave(fn); 363} 364 365file_input::~file_input() 366{ 367 a_delete filename; 368 fclose(fp); 369} 370 371int file_input::read_line() 372{ 373 for (;;) { 374 line.clear(); 375 lineno++; 376 for (;;) { 377 int c = getc(fp); 378 if (c == EOF) 379 break; 380 else if (invalid_input_char(c)) 381 lex_error("invalid input character code %1", c); 382 else { 383 line += char(c); 384 if (c == '\n') 385 break; 386 } 387 } 388 if (line.length() == 0) 389 return 0; 390 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E' 391 && (line[2] == 'Q' || line[2] == 'N') 392 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n' 393 || compatible_flag))) { 394 line += '\0'; 395 ptr = line.contents(); 396 return 1; 397 } 398 } 399} 400 401int file_input::get() 402{ 403 if (*ptr != '\0' || read_line()) 404 return *ptr++ & 0377; 405 else 406 return EOF; 407} 408 409int file_input::peek() 410{ 411 if (*ptr != '\0' || read_line()) 412 return *ptr; 413 else 414 return EOF; 415} 416 417int file_input::get_location(char **fnp, int *lnp) 418{ 419 *fnp = filename; 420 *lnp = lineno; 421 return 1; 422} 423 424macro_input::macro_input(const char *str, input *x) : input(x) 425{ 426 p = s = strsave(str); 427} 428 429macro_input::~macro_input() 430{ 431 a_delete s; 432} 433 434int macro_input::get() 435{ 436 if (p == 0 || *p == '\0') 437 return EOF; 438 else 439 return *p++ & 0377; 440} 441 442int macro_input::peek() 443{ 444 if (p == 0 || *p == '\0') 445 return EOF; 446 else 447 return *p & 0377; 448} 449 450top_input::top_input(const char *str, const char *fn, int ln, input *x) 451: macro_input(str, x), lineno(ln) 452{ 453 filename = strsave(fn); 454} 455 456top_input::~top_input() 457{ 458 a_delete filename; 459} 460 461int top_input::get() 462{ 463 int c = macro_input::get(); 464 if (c == '\n') 465 lineno++; 466 return c; 467} 468 469int top_input::get_location(char **fnp, int *lnp) 470{ 471 *fnp = filename; 472 *lnp = lineno; 473 return 1; 474} 475 476// Character representing $1. Must be invalid input character. 477#define ARG1 14 478 479argument_macro_input::argument_macro_input(const char *body, int ac, 480 char **av, input *x) 481: input(x), ap(0), argc(ac) 482{ 483 int i; 484 for (i = 0; i < argc; i++) 485 argv[i] = av[i]; 486 p = s = strsave(body); 487 int j = 0; 488 for (i = 0; s[i] != '\0'; i++) 489 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') { 490 if (s[i+1] != '0') 491 s[j++] = ARG1 + s[++i] - '1'; 492 } 493 else 494 s[j++] = s[i]; 495 s[j] = '\0'; 496} 497 498 499argument_macro_input::~argument_macro_input() 500{ 501 for (int i = 0; i < argc; i++) 502 a_delete argv[i]; 503 a_delete s; 504} 505 506int argument_macro_input::get() 507{ 508 if (ap) { 509 if (*ap != '\0') 510 return *ap++ & 0377; 511 ap = 0; 512 } 513 if (p == 0) 514 return EOF; 515 while (*p >= ARG1 && *p <= ARG1 + 8) { 516 int i = *p++ - ARG1; 517 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { 518 ap = argv[i]; 519 return *ap++ & 0377; 520 } 521 } 522 if (*p == '\0') 523 return EOF; 524 return *p++ & 0377; 525} 526 527int argument_macro_input::peek() 528{ 529 if (ap) { 530 if (*ap != '\0') 531 return *ap & 0377; 532 ap = 0; 533 } 534 if (p == 0) 535 return EOF; 536 while (*p >= ARG1 && *p <= ARG1 + 8) { 537 int i = *p++ - ARG1; 538 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { 539 ap = argv[i]; 540 return *ap & 0377; 541 } 542 } 543 if (*p == '\0') 544 return EOF; 545 return *p & 0377; 546} 547 548static input *current_input = 0; 549 550/* we insert a newline between input from different levels */ 551 552int get_char() 553{ 554 if (current_input == 0) 555 return EOF; 556 else { 557 int c = current_input->get(); 558 if (c != EOF) 559 return c; 560 else { 561 input *tem = current_input; 562 current_input = current_input->next; 563 delete tem; 564 return '\n'; 565 } 566 } 567} 568 569int peek_char() 570{ 571 if (current_input == 0) 572 return EOF; 573 else { 574 int c = current_input->peek(); 575 if (c != EOF) 576 return c; 577 else 578 return '\n'; 579 } 580} 581 582int get_location(char **fnp, int *lnp) 583{ 584 for (input *p = current_input; p; p = p->next) 585 if (p->get_location(fnp, lnp)) 586 return 1; 587 return 0; 588} 589 590string token_buffer; 591const int NCONTEXT = 4; 592string context_ring[NCONTEXT]; 593int context_index = 0; 594 595void flush_context() 596{ 597 for (int i = 0; i < NCONTEXT; i++) 598 context_ring[i] = ""; 599 context_index = 0; 600} 601 602void show_context() 603{ 604 int i = context_index; 605 fputs(" context is\n\t", stderr); 606 for (;;) { 607 int j = (i + 1) % NCONTEXT; 608 if (j == context_index) { 609 fputs(">>> ", stderr); 610 put_string(context_ring[i], stderr); 611 fputs(" <<<", stderr); 612 break; 613 } 614 else if (context_ring[i].length() > 0) { 615 put_string(context_ring[i], stderr); 616 putc(' ', stderr); 617 } 618 i = j; 619 } 620 putc('\n', stderr); 621} 622 623void add_context(const string &s) 624{ 625 context_ring[context_index] = s; 626 context_index = (context_index + 1) % NCONTEXT; 627} 628 629void add_context(char c) 630{ 631 context_ring[context_index] = c; 632 context_index = (context_index + 1) % NCONTEXT; 633} 634 635void add_quoted_context(const string &s) 636{ 637 string &r = context_ring[context_index]; 638 r = '"'; 639 for (int i = 0; i < s.length(); i++) 640 if (s[i] == '"') 641 r += "\\\""; 642 else 643 r += s[i]; 644 r += '"'; 645 context_index = (context_index + 1) % NCONTEXT; 646} 647 648void init_lex(const char *str, const char *filename, int lineno) 649{ 650 while (current_input != 0) { 651 input *tem = current_input; 652 current_input = current_input->next; 653 delete tem; 654 } 655 current_input = new top_input(str, filename, lineno, 0); 656 flush_context(); 657} 658 659 660void get_delimited_text() 661{ 662 char *filename; 663 int lineno; 664 int got_location = get_location(&filename, &lineno); 665 int start = get_char(); 666 while (start == ' ' || start == '\t' || start == '\n') 667 start = get_char(); 668 token_buffer.clear(); 669 if (start == EOF) { 670 if (got_location) 671 error_with_file_and_line(filename, lineno, 672 "end of input while defining macro"); 673 else 674 error("end of input while defining macro"); 675 return; 676 } 677 for (;;) { 678 int c = get_char(); 679 if (c == EOF) { 680 if (got_location) 681 error_with_file_and_line(filename, lineno, 682 "end of input while defining macro"); 683 else 684 error("end of input while defining macro"); 685 add_context(start + token_buffer); 686 return; 687 } 688 if (c == start) 689 break; 690 token_buffer += char(c); 691 } 692 add_context(start + token_buffer + start); 693} 694 695void interpolate_macro_with_args(const char *body) 696{ 697 char *argv[9]; 698 int argc = 0; 699 int i; 700 for (i = 0; i < 9; i++) 701 argv[i] = 0; 702 int level = 0; 703 int c; 704 do { 705 token_buffer.clear(); 706 for (;;) { 707 c = get_char(); 708 if (c == EOF) { 709 lex_error("end of input while scanning macro arguments"); 710 break; 711 } 712 if (level == 0 && (c == ',' || c == ')')) { 713 if (token_buffer.length() > 0) { 714 token_buffer += '\0'; 715 argv[argc] = strsave(token_buffer.contents()); 716 } 717 // for `foo()', argc = 0 718 if (argc > 0 || c != ')' || i > 0) 719 argc++; 720 break; 721 } 722 token_buffer += char(c); 723 if (c == '(') 724 level++; 725 else if (c == ')') 726 level--; 727 } 728 } while (c != ')' && c != EOF); 729 current_input = new argument_macro_input(body, argc, argv, current_input); 730} 731 732/* If lookup flag is non-zero the token will be looked up to see 733if it is macro. If it's 1, it will looked up to see if it's a token. 734*/ 735 736int get_token(int lookup_flag = 0) 737{ 738 for (;;) { 739 int c = get_char(); 740 while (c == ' ' || c == '\n') 741 c = get_char(); 742 switch (c) { 743 case EOF: 744 { 745 add_context("end of input"); 746 } 747 return 0; 748 case '"': 749 { 750 int quoted = 0; 751 token_buffer.clear(); 752 for (;;) { 753 c = get_char(); 754 if (c == EOF) { 755 lex_error("missing \""); 756 break; 757 } 758 else if (c == '\n') { 759 lex_error("newline before end of quoted text"); 760 break; 761 } 762 else if (c == '"') { 763 if (!quoted) 764 break; 765 token_buffer[token_buffer.length() - 1] = '"'; 766 quoted = 0; 767 } 768 else { 769 token_buffer += c; 770 quoted = quoted ? 0 : c == '\\'; 771 } 772 } 773 } 774 add_quoted_context(token_buffer); 775 return QUOTED_TEXT; 776 case '{': 777 case '}': 778 case '^': 779 case '~': 780 case '\t': 781 add_context(c); 782 return c; 783 default: 784 { 785 int break_flag = 0; 786 int quoted = 0; 787 token_buffer.clear(); 788 if (c == '\\') 789 quoted = 1; 790 else 791 token_buffer += c; 792 int done = 0; 793 while (!done) { 794 c = peek_char(); 795 if (!quoted && lookup_flag != 0 && c == '(') { 796 token_buffer += '\0'; 797 definition *def = macro_table.lookup(token_buffer.contents()); 798 if (def && def->is_macro && !def->is_simple) { 799 (void)get_char(); // skip initial '(' 800 interpolate_macro_with_args(def->contents); 801 break_flag = 1; 802 break; 803 } 804 token_buffer.set_length(token_buffer.length() - 1); 805 } 806 if (quoted) { 807 quoted = 0; 808 switch (c) { 809 case EOF: 810 lex_error("`\\' ignored at end of equation"); 811 done = 1; 812 break; 813 case '\n': 814 lex_error("`\\' ignored because followed by newline"); 815 done = 1; 816 break; 817 case '\t': 818 lex_error("`\\' ignored because followed by tab"); 819 done = 1; 820 break; 821 case '"': 822 (void)get_char(); 823 token_buffer += '"'; 824 break; 825 default: 826 (void)get_char(); 827 token_buffer += '\\'; 828 token_buffer += c; 829 break; 830 } 831 } 832 else { 833 switch (c) { 834 case EOF: 835 case '{': 836 case '}': 837 case '^': 838 case '~': 839 case '"': 840 case ' ': 841 case '\t': 842 case '\n': 843 done = 1; 844 break; 845 case '\\': 846 (void)get_char(); 847 quoted = 1; 848 break; 849 default: 850 (void)get_char(); 851 token_buffer += char(c); 852 break; 853 } 854 } 855 } 856 if (break_flag || token_buffer.length() == 0) 857 break; 858 if (lookup_flag != 0) { 859 token_buffer += '\0'; 860 definition *def = macro_table.lookup(token_buffer.contents()); 861 token_buffer.set_length(token_buffer.length() - 1); 862 if (def) { 863 if (def->is_macro) { 864 current_input = new macro_input(def->contents, current_input); 865 break; 866 } 867 else if (lookup_flag == 1) { 868 add_context(token_buffer); 869 return def->tok; 870 } 871 } 872 } 873 add_context(token_buffer); 874 return TEXT; 875 } 876 } 877 } 878} 879 880void do_include() 881{ 882 int t = get_token(2); 883 if (t != TEXT && t != QUOTED_TEXT) { 884 lex_error("bad filename for include"); 885 return; 886 } 887 token_buffer += '\0'; 888 const char *filename = token_buffer.contents(); 889 errno = 0; 890 FILE *fp = fopen(filename, "r"); 891 if (fp == 0) { 892 lex_error("can't open included file `%1'", filename); 893 return; 894 } 895 current_input = new file_input(fp, filename, current_input); 896} 897 898void ignore_definition() 899{ 900 int t = get_token(); 901 if (t != TEXT) { 902 lex_error("bad definition"); 903 return; 904 } 905 get_delimited_text(); 906} 907 908void do_definition(int is_simple) 909{ 910 int t = get_token(); 911 if (t != TEXT) { 912 lex_error("bad definition"); 913 return; 914 } 915 token_buffer += '\0'; 916 const char *name = token_buffer.contents(); 917 definition *def = macro_table.lookup(name); 918 if (def == 0) { 919 def = new definition[1]; 920 macro_table.define(name, def); 921 } 922 else if (def->is_macro) { 923 a_delete def->contents; 924 } 925 get_delimited_text(); 926 token_buffer += '\0'; 927 def->is_macro = 1; 928 def->contents = strsave(token_buffer.contents()); 929 def->is_simple = is_simple; 930} 931 932void do_undef() 933{ 934 int t = get_token(); 935 if (t != TEXT) { 936 lex_error("bad undef command"); 937 return; 938 } 939 token_buffer += '\0'; 940 macro_table.define(token_buffer.contents(), 0); 941} 942 943void do_gsize() 944{ 945 int t = get_token(2); 946 if (t != TEXT && t != QUOTED_TEXT) { 947 lex_error("bad argument to gsize command"); 948 return; 949 } 950 token_buffer += '\0'; 951 if (!set_gsize(token_buffer.contents())) 952 lex_error("invalid size `%1'", token_buffer.contents()); 953} 954 955void do_gfont() 956{ 957 int t = get_token(2); 958 if (t != TEXT && t != QUOTED_TEXT) { 959 lex_error("bad argument to gfont command"); 960 return; 961 } 962 token_buffer += '\0'; 963 set_gfont(token_buffer.contents()); 964} 965 966void do_grfont() 967{ 968 int t = get_token(2); 969 if (t != TEXT && t != QUOTED_TEXT) { 970 lex_error("bad argument to grfont command"); 971 return; 972 } 973 token_buffer += '\0'; 974 set_grfont(token_buffer.contents()); 975} 976 977void do_gbfont() 978{ 979 int t = get_token(2); 980 if (t != TEXT && t != QUOTED_TEXT) { 981 lex_error("bad argument to gbfont command"); 982 return; 983 } 984 token_buffer += '\0'; 985 set_gbfont(token_buffer.contents()); 986} 987 988void do_space() 989{ 990 int t = get_token(2); 991 if (t != TEXT && t != QUOTED_TEXT) { 992 lex_error("bad argument to space command"); 993 return; 994 } 995 token_buffer += '\0'; 996 char *ptr; 997 long n = strtol(token_buffer.contents(), &ptr, 10); 998 if (n == 0 && ptr == token_buffer.contents()) 999 lex_error("bad argument `%1' to space command", token_buffer.contents()); 1000 else 1001 set_space(int(n)); 1002} 1003 1004void do_ifdef() 1005{ 1006 int t = get_token(); 1007 if (t != TEXT) { 1008 lex_error("bad ifdef"); 1009 return; 1010 } 1011 token_buffer += '\0'; 1012 definition *def = macro_table.lookup(token_buffer.contents()); 1013 int result = def && def->is_macro && !def->is_simple; 1014 get_delimited_text(); 1015 if (result) { 1016 token_buffer += '\0'; 1017 current_input = new macro_input(token_buffer.contents(), current_input); 1018 } 1019} 1020 1021void do_delim() 1022{ 1023 int c = get_char(); 1024 while (c == ' ' || c == '\n') 1025 c = get_char(); 1026 int d; 1027 if (c == EOF || (d = get_char()) == EOF) 1028 lex_error("end of file while reading argument to `delim'"); 1029 else { 1030 if (c == 'o' && d == 'f' && peek_char() == 'f') { 1031 (void)get_char(); 1032 start_delim = end_delim = '\0'; 1033 } 1034 else { 1035 start_delim = c; 1036 end_delim = d; 1037 } 1038 } 1039} 1040 1041void do_chartype() 1042{ 1043 int t = get_token(2); 1044 if (t != TEXT && t != QUOTED_TEXT) { 1045 lex_error("bad chartype"); 1046 return; 1047 } 1048 token_buffer += '\0'; 1049 string type = token_buffer; 1050 t = get_token(); 1051 if (t != TEXT && t != QUOTED_TEXT) { 1052 lex_error("bad chartype"); 1053 return; 1054 } 1055 token_buffer += '\0'; 1056 set_char_type(type.contents(), strsave(token_buffer.contents())); 1057} 1058 1059void do_set() 1060{ 1061 int t = get_token(2); 1062 if (t != TEXT && t != QUOTED_TEXT) { 1063 lex_error("bad set"); 1064 return; 1065 } 1066 token_buffer += '\0'; 1067 string param = token_buffer; 1068 t = get_token(); 1069 if (t != TEXT && t != QUOTED_TEXT) { 1070 lex_error("bad set"); 1071 return; 1072 } 1073 token_buffer += '\0'; 1074 int n; 1075 if (sscanf(&token_buffer[0], "%d", &n) != 1) { 1076 lex_error("bad number `%1'", token_buffer.contents()); 1077 return; 1078 } 1079 set_param(param.contents(), n); 1080} 1081 1082int yylex() 1083{ 1084 for (;;) { 1085 int tk = get_token(1); 1086 switch(tk) { 1087 case UNDEF: 1088 do_undef(); 1089 break; 1090 case SDEFINE: 1091 do_definition(1); 1092 break; 1093 case DEFINE: 1094 do_definition(0); 1095 break; 1096 case TDEFINE: 1097 if (!nroff) 1098 do_definition(0); 1099 else 1100 ignore_definition(); 1101 break; 1102 case NDEFINE: 1103 if (nroff) 1104 do_definition(0); 1105 else 1106 ignore_definition(); 1107 break; 1108 case GSIZE: 1109 do_gsize(); 1110 break; 1111 case GFONT: 1112 do_gfont(); 1113 break; 1114 case GRFONT: 1115 do_grfont(); 1116 break; 1117 case GBFONT: 1118 do_gbfont(); 1119 break; 1120 case SPACE: 1121 do_space(); 1122 break; 1123 case INCLUDE: 1124 do_include(); 1125 break; 1126 case IFDEF: 1127 do_ifdef(); 1128 break; 1129 case DELIM: 1130 do_delim(); 1131 break; 1132 case CHARTYPE: 1133 do_chartype(); 1134 break; 1135 case SET: 1136 do_set(); 1137 break; 1138 case QUOTED_TEXT: 1139 case TEXT: 1140 token_buffer += '\0'; 1141 yylval.str = strsave(token_buffer.contents()); 1142 // fall through 1143 default: 1144 return tk; 1145 } 1146 } 1147} 1148 1149void lex_error(const char *message, 1150 const errarg &arg1, 1151 const errarg &arg2, 1152 const errarg &arg3) 1153{ 1154 char *filename; 1155 int lineno; 1156 if (!get_location(&filename, &lineno)) 1157 error(message, arg1, arg2, arg3); 1158 else 1159 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); 1160} 1161 1162void yyerror(const char *s) 1163{ 1164 char *filename; 1165 int lineno; 1166 if (!get_location(&filename, &lineno)) 1167 error(s); 1168 else 1169 error_with_file_and_line(filename, lineno, s); 1170 show_context(); 1171} 1172 1173