/contrib/groff/src/roff/troff/input.cpp
C++ | 8214 lines | 7726 code | 378 blank | 110 comment | 1285 complexity | 62b837aa80c4497c3fc2edb1fd1e5325 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 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#define DEBUGGING 23 24#include "troff.h" 25#include "dictionary.h" 26#include "hvunits.h" 27#include "stringclass.h" 28#include "mtsm.h" 29#include "env.h" 30#include "request.h" 31#include "node.h" 32#include "token.h" 33#include "div.h" 34#include "reg.h" 35#include "charinfo.h" 36#include "macropath.h" 37#include "input.h" 38#include "defs.h" 39#include "font.h" 40#include "unicode.h" 41 42// Needed for getpid() and isatty() 43#include "posix.h" 44 45#include "nonposix.h" 46 47#ifdef NEED_DECLARATION_PUTENV 48extern "C" { 49 int putenv(const char *); 50} 51#endif /* NEED_DECLARATION_PUTENV */ 52 53#define MACRO_PREFIX "tmac." 54#define MACRO_POSTFIX ".tmac" 55#define INITIAL_STARTUP_FILE "troffrc" 56#define FINAL_STARTUP_FILE "troffrc-end" 57#define DEFAULT_INPUT_STACK_LIMIT 1000 58 59#ifndef DEFAULT_WARNING_MASK 60// warnings that are enabled by default 61#define DEFAULT_WARNING_MASK \ 62 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT) 63#endif 64 65// initial size of buffer for reading names; expanded as necessary 66#define ABUF_SIZE 16 67 68extern "C" const char *program_name; 69extern "C" const char *Version_string; 70 71#ifdef COLUMN 72void init_column_requests(); 73#endif /* COLUMN */ 74 75static node *read_draw_node(); 76static void read_color_draw_node(token &); 77static void push_token(const token &); 78void copy_file(); 79#ifdef COLUMN 80void vjustify(); 81#endif /* COLUMN */ 82void transparent_file(); 83 84token tok; 85int break_flag = 0; 86int color_flag = 1; // colors are on by default 87static int backtrace_flag = 0; 88#ifndef POPEN_MISSING 89char *pipe_command = 0; 90#endif 91charinfo *charset_table[256]; 92unsigned char hpf_code_table[256]; 93 94static int warning_mask = DEFAULT_WARNING_MASK; 95static int inhibit_errors = 0; 96static int ignoring = 0; 97 98static void enable_warning(const char *); 99static void disable_warning(const char *); 100 101static int escape_char = '\\'; 102static symbol end_macro_name; 103static symbol blank_line_macro_name; 104static int compatible_flag = 0; 105int ascii_output_flag = 0; 106int suppress_output_flag = 0; 107int is_html = 0; 108int begin_level = 0; // number of nested \O escapes 109 110int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M, 111 // \R, \s, or \S has been processed in 112 // token::next() 113int old_have_input = 0; // value of have_input right before \n 114int tcommand_flag = 0; 115int safer_flag = 1; // safer by default 116 117int have_string_arg = 0; // whether we have \*[foo bar...] 118 119double spread_limit = -3.0 - 1.0; // negative means deactivated 120 121double warn_scale; 122char warn_scaling_indicator; 123int debug_state = 0; // turns on debugging of the html troff state 124 125search_path *mac_path = &safer_macro_path; 126 127// Defaults to the current directory. 128search_path include_search_path(0, 0, 0, 1); 129 130static int get_copy(node**, int = 0); 131static void copy_mode_error(const char *, 132 const errarg & = empty_errarg, 133 const errarg & = empty_errarg, 134 const errarg & = empty_errarg); 135 136enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS }; 137static symbol read_escape_name(read_mode mode = NO_ARGS); 138static symbol read_long_escape_name(read_mode mode = NO_ARGS); 139static void interpolate_string(symbol); 140static void interpolate_string_with_args(symbol); 141static void interpolate_macro(symbol); 142static void interpolate_number_format(symbol); 143static void interpolate_environment_variable(symbol); 144 145static symbol composite_glyph_name(symbol); 146static void interpolate_arg(symbol); 147static request_or_macro *lookup_request(symbol); 148static int get_delim_number(units *, unsigned char); 149static int get_delim_number(units *, unsigned char, units); 150static symbol do_get_long_name(int, char); 151static int get_line_arg(units *res, unsigned char si, charinfo **cp); 152static int read_size(int *); 153static symbol get_delim_name(); 154static void init_registers(); 155static void trapping_blank_line(); 156 157class input_iterator; 158input_iterator *make_temp_iterator(const char *); 159const char *input_char_description(int); 160 161void process_input_stack(); 162void chop_macro(); // declare to avoid friend name injection 163 164 165void set_escape_char() 166{ 167 if (has_arg()) { 168 if (tok.ch() == 0) { 169 error("bad escape character"); 170 escape_char = '\\'; 171 } 172 else 173 escape_char = tok.ch(); 174 } 175 else 176 escape_char = '\\'; 177 skip_line(); 178} 179 180void escape_off() 181{ 182 escape_char = 0; 183 skip_line(); 184} 185 186static int saved_escape_char = '\\'; 187 188void save_escape_char() 189{ 190 saved_escape_char = escape_char; 191 skip_line(); 192} 193 194void restore_escape_char() 195{ 196 escape_char = saved_escape_char; 197 skip_line(); 198} 199 200class input_iterator { 201public: 202 input_iterator(); 203 input_iterator(int is_div); 204 virtual ~input_iterator() {} 205 int get(node **); 206 friend class input_stack; 207 int is_diversion; 208 statem *diversion_state; 209protected: 210 const unsigned char *ptr; 211 const unsigned char *eptr; 212 input_iterator *next; 213private: 214 virtual int fill(node **); 215 virtual int peek(); 216 virtual int has_args() { return 0; } 217 virtual int nargs() { return 0; } 218 virtual input_iterator *get_arg(int) { return 0; } 219 virtual int get_location(int, const char **, int *) { return 0; } 220 virtual void backtrace() {} 221 virtual int set_location(const char *, int) { return 0; } 222 virtual int next_file(FILE *, const char *) { return 0; } 223 virtual void shift(int) {} 224 virtual int is_boundary() {return 0; } 225 virtual int is_file() { return 0; } 226 virtual int is_macro() { return 0; } 227 virtual void save_compatible_flag(int) {} 228 virtual int get_compatible_flag() { return 0; } 229}; 230 231input_iterator::input_iterator() 232: is_diversion(0), ptr(0), eptr(0) 233{ 234} 235 236input_iterator::input_iterator(int is_div) 237: is_diversion(is_div), ptr(0), eptr(0) 238{ 239} 240 241int input_iterator::fill(node **) 242{ 243 return EOF; 244} 245 246int input_iterator::peek() 247{ 248 return EOF; 249} 250 251inline int input_iterator::get(node **p) 252{ 253 return ptr < eptr ? *ptr++ : fill(p); 254} 255 256class input_boundary : public input_iterator { 257public: 258 int is_boundary() { return 1; } 259}; 260 261class input_return_boundary : public input_iterator { 262public: 263 int is_boundary() { return 2; } 264}; 265 266class file_iterator : public input_iterator { 267 FILE *fp; 268 int lineno; 269 const char *filename; 270 int popened; 271 int newline_flag; 272 int seen_escape; 273 enum { BUF_SIZE = 512 }; 274 unsigned char buf[BUF_SIZE]; 275 void close(); 276public: 277 file_iterator(FILE *, const char *, int = 0); 278 ~file_iterator(); 279 int fill(node **); 280 int peek(); 281 int get_location(int, const char **, int *); 282 void backtrace(); 283 int set_location(const char *, int); 284 int next_file(FILE *, const char *); 285 int is_file(); 286}; 287 288file_iterator::file_iterator(FILE *f, const char *fn, int po) 289: fp(f), lineno(1), filename(fn), popened(po), 290 newline_flag(0), seen_escape(0) 291{ 292 if ((font::use_charnames_in_special) && (fn != 0)) { 293 if (!the_output) 294 init_output(); 295 the_output->put_filename(fn); 296 } 297} 298 299file_iterator::~file_iterator() 300{ 301 close(); 302} 303 304void file_iterator::close() 305{ 306 if (fp == stdin) 307 clearerr(stdin); 308#ifndef POPEN_MISSING 309 else if (popened) 310 pclose(fp); 311#endif /* not POPEN_MISSING */ 312 else 313 fclose(fp); 314} 315 316int file_iterator::is_file() 317{ 318 return 1; 319} 320 321int file_iterator::next_file(FILE *f, const char *s) 322{ 323 close(); 324 filename = s; 325 fp = f; 326 lineno = 1; 327 newline_flag = 0; 328 seen_escape = 0; 329 popened = 0; 330 ptr = 0; 331 eptr = 0; 332 return 1; 333} 334 335int file_iterator::fill(node **) 336{ 337 if (newline_flag) 338 lineno++; 339 newline_flag = 0; 340 unsigned char *p = buf; 341 ptr = p; 342 unsigned char *e = p + BUF_SIZE; 343 while (p < e) { 344 int c = getc(fp); 345 if (c == EOF) 346 break; 347 if (invalid_input_char(c)) 348 warning(WARN_INPUT, "invalid input character code %1", int(c)); 349 else { 350 *p++ = c; 351 if (c == '\n') { 352 seen_escape = 0; 353 newline_flag = 1; 354 break; 355 } 356 seen_escape = (c == '\\'); 357 } 358 } 359 if (p > buf) { 360 eptr = p; 361 return *ptr++; 362 } 363 else { 364 eptr = p; 365 return EOF; 366 } 367} 368 369int file_iterator::peek() 370{ 371 int c = getc(fp); 372 while (invalid_input_char(c)) { 373 warning(WARN_INPUT, "invalid input character code %1", int(c)); 374 c = getc(fp); 375 } 376 if (c != EOF) 377 ungetc(c, fp); 378 return c; 379} 380 381int file_iterator::get_location(int /*allow_macro*/, 382 const char **filenamep, int *linenop) 383{ 384 *linenop = lineno; 385 if (filename != 0 && strcmp(filename, "-") == 0) 386 *filenamep = "<standard input>"; 387 else 388 *filenamep = filename; 389 return 1; 390} 391 392void file_iterator::backtrace() 393{ 394 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno, 395 popened ? "process" : "file"); 396} 397 398int file_iterator::set_location(const char *f, int ln) 399{ 400 if (f) { 401 filename = f; 402 if (!the_output) 403 init_output(); 404 the_output->put_filename(f); 405 } 406 lineno = ln; 407 return 1; 408} 409 410input_iterator nil_iterator; 411 412class input_stack { 413public: 414 static int get(node **); 415 static int peek(); 416 static void push(input_iterator *); 417 static input_iterator *get_arg(int); 418 static int nargs(); 419 static int get_location(int, const char **, int *); 420 static int set_location(const char *, int); 421 static void backtrace(); 422 static void backtrace_all(); 423 static void next_file(FILE *, const char *); 424 static void end_file(); 425 static void shift(int n); 426 static void add_boundary(); 427 static void add_return_boundary(); 428 static int is_return_boundary(); 429 static void remove_boundary(); 430 static int get_level(); 431 static int get_div_level(); 432 static void increase_level(); 433 static void decrease_level(); 434 static void clear(); 435 static void pop_macro(); 436 static void save_compatible_flag(int); 437 static int get_compatible_flag(); 438 static statem *get_diversion_state(); 439 static void check_end_diversion(input_iterator *t); 440 static int limit; 441 static int div_level; 442 static statem *diversion_state; 443private: 444 static input_iterator *top; 445 static int level; 446 static int finish_get(node **); 447 static int finish_peek(); 448}; 449 450input_iterator *input_stack::top = &nil_iterator; 451int input_stack::level = 0; 452int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT; 453int input_stack::div_level = 0; 454statem *input_stack::diversion_state = NULL; 455int suppress_push=0; 456 457 458inline int input_stack::get_level() 459{ 460 return level; 461} 462 463inline void input_stack::increase_level() 464{ 465 level++; 466} 467 468inline void input_stack::decrease_level() 469{ 470 level--; 471} 472 473inline int input_stack::get_div_level() 474{ 475 return div_level; 476} 477 478inline int input_stack::get(node **np) 479{ 480 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np); 481 if (res == '\n') { 482 old_have_input = have_input; 483 have_input = 0; 484 } 485 return res; 486} 487 488int input_stack::finish_get(node **np) 489{ 490 for (;;) { 491 int c = top->fill(np); 492 if (c != EOF || top->is_boundary()) 493 return c; 494 if (top == &nil_iterator) 495 break; 496 input_iterator *tem = top; 497 check_end_diversion(tem); 498#if defined(DEBUGGING) 499 if (debug_state) 500 if (tem->is_diversion) 501 fprintf(stderr, 502 "in diversion level = %d\n", input_stack::get_div_level()); 503#endif 504 top = top->next; 505 level--; 506 delete tem; 507 if (top->ptr < top->eptr) 508 return *top->ptr++; 509 } 510 assert(level == 0); 511 return EOF; 512} 513 514inline int input_stack::peek() 515{ 516 return (top->ptr < top->eptr) ? *top->ptr : finish_peek(); 517} 518 519void input_stack::check_end_diversion(input_iterator *t) 520{ 521 if (t->is_diversion) { 522 div_level--; 523 diversion_state = t->diversion_state; 524 } 525} 526 527int input_stack::finish_peek() 528{ 529 for (;;) { 530 int c = top->peek(); 531 if (c != EOF || top->is_boundary()) 532 return c; 533 if (top == &nil_iterator) 534 break; 535 input_iterator *tem = top; 536 check_end_diversion(tem); 537 top = top->next; 538 level--; 539 delete tem; 540 if (top->ptr < top->eptr) 541 return *top->ptr; 542 } 543 assert(level == 0); 544 return EOF; 545} 546 547void input_stack::add_boundary() 548{ 549 push(new input_boundary); 550} 551 552void input_stack::add_return_boundary() 553{ 554 push(new input_return_boundary); 555} 556 557int input_stack::is_return_boundary() 558{ 559 return top->is_boundary() == 2; 560} 561 562void input_stack::remove_boundary() 563{ 564 assert(top->is_boundary()); 565 input_iterator *temp = top->next; 566 check_end_diversion(top); 567 568 delete top; 569 top = temp; 570 level--; 571} 572 573void input_stack::push(input_iterator *in) 574{ 575 if (in == 0) 576 return; 577 if (++level > limit && limit > 0) 578 fatal("input stack limit exceeded (probable infinite loop)"); 579 in->next = top; 580 top = in; 581 if (top->is_diversion) { 582 div_level++; 583 in->diversion_state = diversion_state; 584 diversion_state = curenv->construct_state(0); 585#if defined(DEBUGGING) 586 if (debug_state) { 587 curenv->dump_troff_state(); 588 fflush(stderr); 589 } 590#endif 591 } 592#if defined(DEBUGGING) 593 if (debug_state) 594 if (top->is_diversion) { 595 fprintf(stderr, 596 "in diversion level = %d\n", input_stack::get_div_level()); 597 fflush(stderr); 598 } 599#endif 600} 601 602statem *get_diversion_state() 603{ 604 return input_stack::get_diversion_state(); 605} 606 607statem *input_stack::get_diversion_state() 608{ 609 if (diversion_state == NULL) 610 return NULL; 611 else 612 return new statem(diversion_state); 613} 614 615input_iterator *input_stack::get_arg(int i) 616{ 617 input_iterator *p; 618 for (p = top; p != 0; p = p->next) 619 if (p->has_args()) 620 return p->get_arg(i); 621 return 0; 622} 623 624void input_stack::shift(int n) 625{ 626 for (input_iterator *p = top; p; p = p->next) 627 if (p->has_args()) { 628 p->shift(n); 629 return; 630 } 631} 632 633int input_stack::nargs() 634{ 635 for (input_iterator *p =top; p != 0; p = p->next) 636 if (p->has_args()) 637 return p->nargs(); 638 return 0; 639} 640 641int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop) 642{ 643 for (input_iterator *p = top; p; p = p->next) 644 if (p->get_location(allow_macro, filenamep, linenop)) 645 return 1; 646 return 0; 647} 648 649void input_stack::backtrace() 650{ 651 const char *f; 652 int n; 653 // only backtrace down to (not including) the topmost file 654 for (input_iterator *p = top; 655 p && !p->get_location(0, &f, &n); 656 p = p->next) 657 p->backtrace(); 658} 659 660void input_stack::backtrace_all() 661{ 662 for (input_iterator *p = top; p; p = p->next) 663 p->backtrace(); 664} 665 666int input_stack::set_location(const char *filename, int lineno) 667{ 668 for (input_iterator *p = top; p; p = p->next) 669 if (p->set_location(filename, lineno)) 670 return 1; 671 return 0; 672} 673 674void input_stack::next_file(FILE *fp, const char *s) 675{ 676 input_iterator **pp; 677 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 678 if ((*pp)->next_file(fp, s)) 679 return; 680 if (++level > limit && limit > 0) 681 fatal("input stack limit exceeded"); 682 *pp = new file_iterator(fp, s); 683 (*pp)->next = &nil_iterator; 684} 685 686void input_stack::end_file() 687{ 688 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 689 if ((*pp)->is_file()) { 690 input_iterator *tem = *pp; 691 check_end_diversion(tem); 692 *pp = (*pp)->next; 693 delete tem; 694 level--; 695 return; 696 } 697} 698 699void input_stack::clear() 700{ 701 int nboundaries = 0; 702 while (top != &nil_iterator) { 703 if (top->is_boundary()) 704 nboundaries++; 705 input_iterator *tem = top; 706 check_end_diversion(tem); 707 top = top->next; 708 level--; 709 delete tem; 710 } 711 // Keep while_request happy. 712 for (; nboundaries > 0; --nboundaries) 713 add_return_boundary(); 714} 715 716void input_stack::pop_macro() 717{ 718 int nboundaries = 0; 719 int is_macro = 0; 720 do { 721 if (top->next == &nil_iterator) 722 break; 723 if (top->is_boundary()) 724 nboundaries++; 725 is_macro = top->is_macro(); 726 input_iterator *tem = top; 727 check_end_diversion(tem); 728 top = top->next; 729 level--; 730 delete tem; 731 } while (!is_macro); 732 // Keep while_request happy. 733 for (; nboundaries > 0; --nboundaries) 734 add_return_boundary(); 735} 736 737inline void input_stack::save_compatible_flag(int f) 738{ 739 top->save_compatible_flag(f); 740} 741 742inline int input_stack::get_compatible_flag() 743{ 744 return top->get_compatible_flag(); 745} 746 747void backtrace_request() 748{ 749 input_stack::backtrace_all(); 750 fflush(stderr); 751 skip_line(); 752} 753 754void next_file() 755{ 756 symbol nm = get_long_name(); 757 while (!tok.newline() && !tok.eof()) 758 tok.next(); 759 if (nm.is_null()) 760 input_stack::end_file(); 761 else { 762 errno = 0; 763 FILE *fp = include_search_path.open_file_cautious(nm.contents()); 764 if (!fp) 765 error("can't open `%1': %2", nm.contents(), strerror(errno)); 766 else 767 input_stack::next_file(fp, nm.contents()); 768 } 769 tok.next(); 770} 771 772void shift() 773{ 774 int n; 775 if (!has_arg() || !get_integer(&n)) 776 n = 1; 777 input_stack::shift(n); 778 skip_line(); 779} 780 781static char get_char_for_escape_name(int allow_space = 0) 782{ 783 int c = get_copy(0); 784 switch (c) { 785 case EOF: 786 copy_mode_error("end of input in escape name"); 787 return '\0'; 788 default: 789 if (!invalid_input_char(c)) 790 break; 791 // fall through 792 case '\n': 793 if (c == '\n') 794 input_stack::push(make_temp_iterator("\n")); 795 // fall through 796 case ' ': 797 if (c == ' ' && allow_space) 798 break; 799 // fall through 800 case '\t': 801 case '\001': 802 case '\b': 803 copy_mode_error("%1 is not allowed in an escape name", 804 input_char_description(c)); 805 return '\0'; 806 } 807 return c; 808} 809 810static symbol read_two_char_escape_name() 811{ 812 char buf[3]; 813 buf[0] = get_char_for_escape_name(); 814 if (buf[0] != '\0') { 815 buf[1] = get_char_for_escape_name(); 816 if (buf[1] == '\0') 817 buf[0] = 0; 818 else 819 buf[2] = 0; 820 } 821 return symbol(buf); 822} 823 824static symbol read_long_escape_name(read_mode mode) 825{ 826 int start_level = input_stack::get_level(); 827 char abuf[ABUF_SIZE]; 828 char *buf = abuf; 829 int buf_size = ABUF_SIZE; 830 int i = 0; 831 char c; 832 int have_char = 0; 833 for (;;) { 834 c = get_char_for_escape_name(have_char && mode == WITH_ARGS); 835 if (c == 0) { 836 if (buf != abuf) 837 a_delete buf; 838 return NULL_SYMBOL; 839 } 840 have_char = 1; 841 if (mode == WITH_ARGS && c == ' ') 842 break; 843 if (i + 2 > buf_size) { 844 if (buf == abuf) { 845 buf = new char[ABUF_SIZE*2]; 846 memcpy(buf, abuf, buf_size); 847 buf_size = ABUF_SIZE*2; 848 } 849 else { 850 char *old_buf = buf; 851 buf = new char[buf_size*2]; 852 memcpy(buf, old_buf, buf_size); 853 buf_size *= 2; 854 a_delete old_buf; 855 } 856 } 857 if (c == ']' && input_stack::get_level() == start_level) 858 break; 859 buf[i++] = c; 860 } 861 buf[i] = 0; 862 if (c == ' ') 863 have_string_arg = 1; 864 if (buf == abuf) { 865 if (i == 0) { 866 if (mode != ALLOW_EMPTY) 867 copy_mode_error("empty escape name"); 868 return EMPTY_SYMBOL; 869 } 870 return symbol(abuf); 871 } 872 else { 873 symbol s(buf); 874 a_delete buf; 875 return s; 876 } 877} 878 879static symbol read_escape_name(read_mode mode) 880{ 881 char c = get_char_for_escape_name(); 882 if (c == 0) 883 return NULL_SYMBOL; 884 if (c == '(') 885 return read_two_char_escape_name(); 886 if (c == '[' && !compatible_flag) 887 return read_long_escape_name(mode); 888 char buf[2]; 889 buf[0] = c; 890 buf[1] = '\0'; 891 return symbol(buf); 892} 893 894static symbol read_increment_and_escape_name(int *incp) 895{ 896 char c = get_char_for_escape_name(); 897 switch (c) { 898 case 0: 899 *incp = 0; 900 return NULL_SYMBOL; 901 case '(': 902 *incp = 0; 903 return read_two_char_escape_name(); 904 case '+': 905 *incp = 1; 906 return read_escape_name(); 907 case '-': 908 *incp = -1; 909 return read_escape_name(); 910 case '[': 911 if (!compatible_flag) { 912 *incp = 0; 913 return read_long_escape_name(); 914 } 915 break; 916 } 917 *incp = 0; 918 char buf[2]; 919 buf[0] = c; 920 buf[1] = '\0'; 921 return symbol(buf); 922} 923 924static int get_copy(node **nd, int defining) 925{ 926 for (;;) { 927 int c = input_stack::get(nd); 928 if (c == PUSH_GROFF_MODE) { 929 input_stack::save_compatible_flag(compatible_flag); 930 compatible_flag = 0; 931 continue; 932 } 933 if (c == PUSH_COMP_MODE) { 934 input_stack::save_compatible_flag(compatible_flag); 935 compatible_flag = 1; 936 continue; 937 } 938 if (c == POP_GROFFCOMP_MODE) { 939 compatible_flag = input_stack::get_compatible_flag(); 940 continue; 941 } 942 if (c == BEGIN_QUOTE) { 943 input_stack::increase_level(); 944 continue; 945 } 946 if (c == END_QUOTE) { 947 input_stack::decrease_level(); 948 continue; 949 } 950 if (c == ESCAPE_NEWLINE) { 951 if (defining) 952 return c; 953 do { 954 c = input_stack::get(nd); 955 } while (c == ESCAPE_NEWLINE); 956 } 957 if (c != escape_char || escape_char <= 0) 958 return c; 959 c = input_stack::peek(); 960 switch(c) { 961 case 0: 962 return escape_char; 963 case '"': 964 (void)input_stack::get(0); 965 while ((c = input_stack::get(0)) != '\n' && c != EOF) 966 ; 967 return c; 968 case '#': // Like \" but newline is ignored. 969 (void)input_stack::get(0); 970 while ((c = input_stack::get(0)) != '\n') 971 if (c == EOF) 972 return EOF; 973 break; 974 case '$': 975 { 976 (void)input_stack::get(0); 977 symbol s = read_escape_name(); 978 if (!(s.is_null() || s.is_empty())) 979 interpolate_arg(s); 980 break; 981 } 982 case '*': 983 { 984 (void)input_stack::get(0); 985 symbol s = read_escape_name(WITH_ARGS); 986 if (!(s.is_null() || s.is_empty())) { 987 if (have_string_arg) { 988 have_string_arg = 0; 989 interpolate_string_with_args(s); 990 } 991 else 992 interpolate_string(s); 993 } 994 break; 995 } 996 case 'a': 997 (void)input_stack::get(0); 998 return '\001'; 999 case 'e': 1000 (void)input_stack::get(0); 1001 return ESCAPE_e; 1002 case 'E': 1003 (void)input_stack::get(0); 1004 return ESCAPE_E; 1005 case 'n': 1006 { 1007 (void)input_stack::get(0); 1008 int inc; 1009 symbol s = read_increment_and_escape_name(&inc); 1010 if (!(s.is_null() || s.is_empty())) 1011 interpolate_number_reg(s, inc); 1012 break; 1013 } 1014 case 'g': 1015 { 1016 (void)input_stack::get(0); 1017 symbol s = read_escape_name(); 1018 if (!(s.is_null() || s.is_empty())) 1019 interpolate_number_format(s); 1020 break; 1021 } 1022 case 't': 1023 (void)input_stack::get(0); 1024 return '\t'; 1025 case 'V': 1026 { 1027 (void)input_stack::get(0); 1028 symbol s = read_escape_name(); 1029 if (!(s.is_null() || s.is_empty())) 1030 interpolate_environment_variable(s); 1031 break; 1032 } 1033 case '\n': 1034 (void)input_stack::get(0); 1035 if (defining) 1036 return ESCAPE_NEWLINE; 1037 break; 1038 case ' ': 1039 (void)input_stack::get(0); 1040 return ESCAPE_SPACE; 1041 case '~': 1042 (void)input_stack::get(0); 1043 return ESCAPE_TILDE; 1044 case ':': 1045 (void)input_stack::get(0); 1046 return ESCAPE_COLON; 1047 case '|': 1048 (void)input_stack::get(0); 1049 return ESCAPE_BAR; 1050 case '^': 1051 (void)input_stack::get(0); 1052 return ESCAPE_CIRCUMFLEX; 1053 case '{': 1054 (void)input_stack::get(0); 1055 return ESCAPE_LEFT_BRACE; 1056 case '}': 1057 (void)input_stack::get(0); 1058 return ESCAPE_RIGHT_BRACE; 1059 case '`': 1060 (void)input_stack::get(0); 1061 return ESCAPE_LEFT_QUOTE; 1062 case '\'': 1063 (void)input_stack::get(0); 1064 return ESCAPE_RIGHT_QUOTE; 1065 case '-': 1066 (void)input_stack::get(0); 1067 return ESCAPE_HYPHEN; 1068 case '_': 1069 (void)input_stack::get(0); 1070 return ESCAPE_UNDERSCORE; 1071 case 'c': 1072 (void)input_stack::get(0); 1073 return ESCAPE_c; 1074 case '!': 1075 (void)input_stack::get(0); 1076 return ESCAPE_BANG; 1077 case '?': 1078 (void)input_stack::get(0); 1079 return ESCAPE_QUESTION; 1080 case '&': 1081 (void)input_stack::get(0); 1082 return ESCAPE_AMPERSAND; 1083 case ')': 1084 (void)input_stack::get(0); 1085 return ESCAPE_RIGHT_PARENTHESIS; 1086 case '.': 1087 (void)input_stack::get(0); 1088 return c; 1089 case '%': 1090 (void)input_stack::get(0); 1091 return ESCAPE_PERCENT; 1092 default: 1093 if (c == escape_char) { 1094 (void)input_stack::get(0); 1095 return c; 1096 } 1097 else 1098 return escape_char; 1099 } 1100 } 1101} 1102 1103class non_interpreted_char_node : public node { 1104 unsigned char c; 1105public: 1106 non_interpreted_char_node(unsigned char); 1107 node *copy(); 1108 int interpret(macro *); 1109 int same(node *); 1110 const char *type(); 1111 int force_tprint(); 1112 int is_tag(); 1113}; 1114 1115int non_interpreted_char_node::same(node *nd) 1116{ 1117 return c == ((non_interpreted_char_node *)nd)->c; 1118} 1119 1120const char *non_interpreted_char_node::type() 1121{ 1122 return "non_interpreted_char_node"; 1123} 1124 1125int non_interpreted_char_node::force_tprint() 1126{ 1127 return 0; 1128} 1129 1130int non_interpreted_char_node::is_tag() 1131{ 1132 return 0; 1133} 1134 1135non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n) 1136{ 1137 assert(n != 0); 1138} 1139 1140node *non_interpreted_char_node::copy() 1141{ 1142 return new non_interpreted_char_node(c); 1143} 1144 1145int non_interpreted_char_node::interpret(macro *mac) 1146{ 1147 mac->append(c); 1148 return 1; 1149} 1150 1151static void do_width(); 1152static node *do_non_interpreted(); 1153static node *do_special(); 1154static node *do_suppress(symbol nm); 1155static void do_register(); 1156 1157dictionary color_dictionary(501); 1158 1159static color *lookup_color(symbol nm) 1160{ 1161 assert(!nm.is_null()); 1162 if (nm == default_symbol) 1163 return &default_color; 1164 color *c = (color *)color_dictionary.lookup(nm); 1165 if (c == 0) 1166 warning(WARN_COLOR, "color `%1' not defined", nm.contents()); 1167 return c; 1168} 1169 1170void do_glyph_color(symbol nm) 1171{ 1172 if (nm.is_null()) 1173 return; 1174 if (nm.is_empty()) 1175 curenv->set_glyph_color(curenv->get_prev_glyph_color()); 1176 else { 1177 color *tem = lookup_color(nm); 1178 if (tem) 1179 curenv->set_glyph_color(tem); 1180 else 1181 (void)color_dictionary.lookup(nm, new color(nm)); 1182 } 1183} 1184 1185void do_fill_color(symbol nm) 1186{ 1187 if (nm.is_null()) 1188 return; 1189 if (nm.is_empty()) 1190 curenv->set_fill_color(curenv->get_prev_fill_color()); 1191 else { 1192 color *tem = lookup_color(nm); 1193 if (tem) 1194 curenv->set_fill_color(tem); 1195 else 1196 (void)color_dictionary.lookup(nm, new color(nm)); 1197 } 1198} 1199 1200static unsigned int get_color_element(const char *scheme, const char *col) 1201{ 1202 units val; 1203 if (!get_number(&val, 'f')) { 1204 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme); 1205 tok.next(); 1206 return 0; 1207 } 1208 if (val < 0) { 1209 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col); 1210 return 0; 1211 } 1212 if (val > color::MAX_COLOR_VAL+1) { 1213 warning(WARN_RANGE, "%1 cannot be greater than 1", col); 1214 // we change 0x10000 to 0xffff 1215 return color::MAX_COLOR_VAL; 1216 } 1217 return (unsigned int)val; 1218} 1219 1220static color *read_rgb(char end = 0) 1221{ 1222 symbol component = do_get_long_name(0, end); 1223 if (component.is_null()) { 1224 warning(WARN_COLOR, "missing rgb color values"); 1225 return 0; 1226 } 1227 const char *s = component.contents(); 1228 color *col = new color; 1229 if (*s == '#') { 1230 if (!col->read_rgb(s)) { 1231 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s); 1232 delete col; 1233 return 0; 1234 } 1235 } 1236 else { 1237 if (!end) 1238 input_stack::push(make_temp_iterator(" ")); 1239 input_stack::push(make_temp_iterator(s)); 1240 tok.next(); 1241 unsigned int r = get_color_element("rgb color", "red component"); 1242 unsigned int g = get_color_element("rgb color", "green component"); 1243 unsigned int b = get_color_element("rgb color", "blue component"); 1244 col->set_rgb(r, g, b); 1245 } 1246 return col; 1247} 1248 1249static color *read_cmy(char end = 0) 1250{ 1251 symbol component = do_get_long_name(0, end); 1252 if (component.is_null()) { 1253 warning(WARN_COLOR, "missing cmy color values"); 1254 return 0; 1255 } 1256 const char *s = component.contents(); 1257 color *col = new color; 1258 if (*s == '#') { 1259 if (!col->read_cmy(s)) { 1260 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s); 1261 delete col; 1262 return 0; 1263 } 1264 } 1265 else { 1266 if (!end) 1267 input_stack::push(make_temp_iterator(" ")); 1268 input_stack::push(make_temp_iterator(s)); 1269 tok.next(); 1270 unsigned int c = get_color_element("cmy color", "cyan component"); 1271 unsigned int m = get_color_element("cmy color", "magenta component"); 1272 unsigned int y = get_color_element("cmy color", "yellow component"); 1273 col->set_cmy(c, m, y); 1274 } 1275 return col; 1276} 1277 1278static color *read_cmyk(char end = 0) 1279{ 1280 symbol component = do_get_long_name(0, end); 1281 if (component.is_null()) { 1282 warning(WARN_COLOR, "missing cmyk color values"); 1283 return 0; 1284 } 1285 const char *s = component.contents(); 1286 color *col = new color; 1287 if (*s == '#') { 1288 if (!col->read_cmyk(s)) { 1289 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s); 1290 delete col; 1291 return 0; 1292 } 1293 } 1294 else { 1295 if (!end) 1296 input_stack::push(make_temp_iterator(" ")); 1297 input_stack::push(make_temp_iterator(s)); 1298 tok.next(); 1299 unsigned int c = get_color_element("cmyk color", "cyan component"); 1300 unsigned int m = get_color_element("cmyk color", "magenta component"); 1301 unsigned int y = get_color_element("cmyk color", "yellow component"); 1302 unsigned int k = get_color_element("cmyk color", "black component"); 1303 col->set_cmyk(c, m, y, k); 1304 } 1305 return col; 1306} 1307 1308static color *read_gray(char end = 0) 1309{ 1310 symbol component = do_get_long_name(0, end); 1311 if (component.is_null()) { 1312 warning(WARN_COLOR, "missing gray values"); 1313 return 0; 1314 } 1315 const char *s = component.contents(); 1316 color *col = new color; 1317 if (*s == '#') { 1318 if (!col->read_gray(s)) { 1319 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s); 1320 delete col; 1321 return 0; 1322 } 1323 } 1324 else { 1325 if (!end) 1326 input_stack::push(make_temp_iterator("\n")); 1327 input_stack::push(make_temp_iterator(s)); 1328 tok.next(); 1329 unsigned int g = get_color_element("gray", "gray value"); 1330 col->set_gray(g); 1331 } 1332 return col; 1333} 1334 1335static void activate_color() 1336{ 1337 int n; 1338 if (has_arg() && get_integer(&n)) 1339 color_flag = n != 0; 1340 else 1341 color_flag = 1; 1342 skip_line(); 1343} 1344 1345static void define_color() 1346{ 1347 symbol color_name = get_long_name(1); 1348 if (color_name.is_null()) { 1349 skip_line(); 1350 return; 1351 } 1352 if (color_name == default_symbol) { 1353 warning(WARN_COLOR, "default color can't be redefined"); 1354 skip_line(); 1355 return; 1356 } 1357 symbol style = get_long_name(1); 1358 if (style.is_null()) { 1359 skip_line(); 1360 return; 1361 } 1362 color *col; 1363 if (strcmp(style.contents(), "rgb") == 0) 1364 col = read_rgb(); 1365 else if (strcmp(style.contents(), "cmyk") == 0) 1366 col = read_cmyk(); 1367 else if (strcmp(style.contents(), "gray") == 0) 1368 col = read_gray(); 1369 else if (strcmp(style.contents(), "grey") == 0) 1370 col = read_gray(); 1371 else if (strcmp(style.contents(), "cmy") == 0) 1372 col = read_cmy(); 1373 else { 1374 warning(WARN_COLOR, 1375 "unknown color space `%1'; use rgb, cmyk, gray or cmy", 1376 style.contents()); 1377 skip_line(); 1378 return; 1379 } 1380 if (col) { 1381 col->nm = color_name; 1382 (void)color_dictionary.lookup(color_name, col); 1383 } 1384 skip_line(); 1385} 1386 1387static node *do_overstrike() 1388{ 1389 token start; 1390 overstrike_node *on = new overstrike_node; 1391 int start_level = input_stack::get_level(); 1392 start.next(); 1393 for (;;) { 1394 tok.next(); 1395 if (tok.newline() || tok.eof()) { 1396 warning(WARN_DELIM, "missing closing delimiter"); 1397 input_stack::push(make_temp_iterator("\n")); 1398 break; 1399 } 1400 if (tok == start 1401 && (compatible_flag || input_stack::get_level() == start_level)) 1402 break; 1403 charinfo *ci = tok.get_char(1); 1404 if (ci) { 1405 node *n = curenv->make_char_node(ci); 1406 if (n) 1407 on->overstrike(n); 1408 } 1409 } 1410 return on; 1411} 1412 1413static node *do_bracket() 1414{ 1415 token start; 1416 bracket_node *bn = new bracket_node; 1417 start.next(); 1418 int start_level = input_stack::get_level(); 1419 for (;;) { 1420 tok.next(); 1421 if (tok.eof()) { 1422 warning(WARN_DELIM, "missing closing delimiter"); 1423 break; 1424 } 1425 if (tok.newline()) { 1426 warning(WARN_DELIM, "missing closing delimiter"); 1427 input_stack::push(make_temp_iterator("\n")); 1428 break; 1429 } 1430 if (tok == start 1431 && (compatible_flag || input_stack::get_level() == start_level)) 1432 break; 1433 charinfo *ci = tok.get_char(1); 1434 if (ci) { 1435 node *n = curenv->make_char_node(ci); 1436 if (n) 1437 bn->bracket(n); 1438 } 1439 } 1440 return bn; 1441} 1442 1443static int do_name_test() 1444{ 1445 token start; 1446 start.next(); 1447 int start_level = input_stack::get_level(); 1448 int bad_char = 0; 1449 int some_char = 0; 1450 for (;;) { 1451 tok.next(); 1452 if (tok.newline() || tok.eof()) { 1453 warning(WARN_DELIM, "missing closing delimiter"); 1454 input_stack::push(make_temp_iterator("\n")); 1455 break; 1456 } 1457 if (tok == start 1458 && (compatible_flag || input_stack::get_level() == start_level)) 1459 break; 1460 if (!tok.ch()) 1461 bad_char = 1; 1462 some_char = 1; 1463 } 1464 return some_char && !bad_char; 1465} 1466 1467static int do_expr_test() 1468{ 1469 token start; 1470 start.next(); 1471 int start_level = input_stack::get_level(); 1472 if (!start.delimiter(1)) 1473 return 0; 1474 tok.next(); 1475 // disable all warning and error messages temporarily 1476 int saved_warning_mask = warning_mask; 1477 int saved_inhibit_errors = inhibit_errors; 1478 warning_mask = 0; 1479 inhibit_errors = 1; 1480 int dummy; 1481 int result = get_number_rigidly(&dummy, 'u'); 1482 warning_mask = saved_warning_mask; 1483 inhibit_errors = saved_inhibit_errors; 1484 if (tok == start && input_stack::get_level() == start_level) 1485 return result; 1486 // ignore everything up to the delimiter in case we aren't right there 1487 for (;;) { 1488 tok.next(); 1489 if (tok.newline() || tok.eof()) { 1490 warning(WARN_DELIM, "missing closing delimiter"); 1491 input_stack::push(make_temp_iterator("\n")); 1492 break; 1493 } 1494 if (tok == start && input_stack::get_level() == start_level) 1495 break; 1496 } 1497 return 0; 1498} 1499 1500#if 0 1501static node *do_zero_width() 1502{ 1503 token start; 1504 start.next(); 1505 int start_level = input_stack::get_level(); 1506 environment env(curenv); 1507 environment *oldenv = curenv; 1508 curenv = &env; 1509 for (;;) { 1510 tok.next(); 1511 if (tok.newline() || tok.eof()) { 1512 error("missing closing delimiter"); 1513 break; 1514 } 1515 if (tok == start 1516 && (compatible_flag || input_stack::get_level() == start_level)) 1517 break; 1518 tok.process(); 1519 } 1520 curenv = oldenv; 1521 node *rev = env.extract_output_line(); 1522 node *n = 0; 1523 while (rev) { 1524 node *tem = rev; 1525 rev = rev->next; 1526 tem->next = n; 1527 n = tem; 1528 } 1529 return new zero_width_node(n); 1530} 1531 1532#else 1533 1534// It's undesirable for \Z to change environments, because then 1535// \n(.w won't work as expected. 1536 1537static node *do_zero_width() 1538{ 1539 node *rev = new dummy_node; 1540 token start; 1541 start.next(); 1542 int start_level = input_stack::get_level(); 1543 for (;;) { 1544 tok.next(); 1545 if (tok.newline() || tok.eof()) { 1546 warning(WARN_DELIM, "missing closing delimiter"); 1547 input_stack::push(make_temp_iterator("\n")); 1548 break; 1549 } 1550 if (tok == start 1551 && (compatible_flag || input_stack::get_level() == start_level)) 1552 break; 1553 if (!tok.add_to_node_list(&rev)) 1554 error("invalid token in argument to \\Z"); 1555 } 1556 node *n = 0; 1557 while (rev) { 1558 node *tem = rev; 1559 rev = rev->next; 1560 tem->next = n; 1561 n = tem; 1562 } 1563 return new zero_width_node(n); 1564} 1565 1566#endif 1567 1568token_node *node::get_token_node() 1569{ 1570 return 0; 1571} 1572 1573class token_node : public node { 1574public: 1575 token tk; 1576 token_node(const token &t); 1577 node *copy(); 1578 token_node *get_token_node(); 1579 int same(node *); 1580 const char *type(); 1581 int force_tprint(); 1582 int is_tag(); 1583}; 1584 1585token_node::token_node(const token &t) : tk(t) 1586{ 1587} 1588 1589node *token_node::copy() 1590{ 1591 return new token_node(tk); 1592} 1593 1594token_node *token_node::get_token_node() 1595{ 1596 return this; 1597} 1598 1599int token_node::same(node *nd) 1600{ 1601 return tk == ((token_node *)nd)->tk; 1602} 1603 1604const char *token_node::type() 1605{ 1606 return "token_node"; 1607} 1608 1609int token_node::force_tprint() 1610{ 1611 return 0; 1612} 1613 1614int token_node::is_tag() 1615{ 1616 return 0; 1617} 1618 1619token::token() : nd(0), type(TOKEN_EMPTY) 1620{ 1621} 1622 1623token::~token() 1624{ 1625 delete nd; 1626} 1627 1628token::token(const token &t) 1629: nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type) 1630{ 1631 // Use two statements to work around bug in SGI C++. 1632 node *tem = t.nd; 1633 nd = tem ? tem->copy() : 0; 1634} 1635 1636void token::operator=(const token &t) 1637{ 1638 delete nd; 1639 nm = t.nm; 1640 // Use two statements to work around bug in SGI C++. 1641 node *tem = t.nd; 1642 nd = tem ? tem->copy() : 0; 1643 c = t.c; 1644 val = t.val; 1645 dim = t.dim; 1646 type = t.type; 1647} 1648 1649void token::skip() 1650{ 1651 while (space()) 1652 next(); 1653} 1654 1655int has_arg() 1656{ 1657 while (tok.space()) 1658 tok.next(); 1659 return !tok.newline(); 1660} 1661 1662void token::make_space() 1663{ 1664 type = TOKEN_SPACE; 1665} 1666 1667void token::make_newline() 1668{ 1669 type = TOKEN_NEWLINE; 1670} 1671 1672void token::next() 1673{ 1674 if (nd) { 1675 delete nd; 1676 nd = 0; 1677 } 1678 units x; 1679 for (;;) { 1680 node *n = 0; 1681 int cc = input_stack::get(&n); 1682 if (cc != escape_char || escape_char == 0) { 1683 handle_normal_char: 1684 switch(cc) { 1685 case PUSH_GROFF_MODE: 1686 input_stack::save_compatible_flag(compatible_flag); 1687 compatible_flag = 0; 1688 continue; 1689 case PUSH_COMP_MODE: 1690 input_stack::save_compatible_flag(compatible_flag); 1691 compatible_flag = 1; 1692 continue; 1693 case POP_GROFFCOMP_MODE: 1694 compatible_flag = input_stack::get_compatible_flag(); 1695 continue; 1696 case BEGIN_QUOTE: 1697 input_stack::increase_level(); 1698 continue; 1699 case END_QUOTE: 1700 input_stack::decrease_level(); 1701 continue; 1702 case EOF: 1703 type = TOKEN_EOF; 1704 return; 1705 case TRANSPARENT_FILE_REQUEST: 1706 case TITLE_REQUEST: 1707 case COPY_FILE_REQUEST: 1708#ifdef COLUMN 1709 case VJUSTIFY_REQUEST: 1710#endif /* COLUMN */ 1711 type = TOKEN_REQUEST; 1712 c = cc; 1713 return; 1714 case BEGIN_TRAP: 1715 type = TOKEN_BEGIN_TRAP; 1716 return; 1717 case END_TRAP: 1718 type = TOKEN_END_TRAP; 1719 return; 1720 case LAST_PAGE_EJECTOR: 1721 seen_last_page_ejector = 1; 1722 // fall through 1723 case PAGE_EJECTOR: 1724 type = TOKEN_PAGE_EJECTOR; 1725 return; 1726 case ESCAPE_PERCENT: 1727 ESCAPE_PERCENT: 1728 type = TOKEN_HYPHEN_INDICATOR; 1729 return; 1730 case ESCAPE_SPACE: 1731 ESCAPE_SPACE: 1732 type = TOKEN_UNSTRETCHABLE_SPACE; 1733 return; 1734 case ESCAPE_TILDE: 1735 ESCAPE_TILDE: 1736 type = TOKEN_STRETCHABLE_SPACE; 1737 return; 1738 case ESCAPE_COLON: 1739 ESCAPE_COLON: 1740 type = TOKEN_ZERO_WIDTH_BREAK; 1741 return; 1742 case ESCAPE_e: 1743 ESCAPE_e: 1744 type = TOKEN_ESCAPE; 1745 return; 1746 case ESCAPE_E: 1747 goto handle_escape_char; 1748 case ESCAPE_BAR: 1749 ESCAPE_BAR: 1750 type = TOKEN_NODE; 1751 nd = new hmotion_node(curenv->get_narrow_space_width(), 1752 curenv->get_fill_color()); 1753 return; 1754 case ESCAPE_CIRCUMFLEX: 1755 ESCAPE_CIRCUMFLEX: 1756 type = TOKEN_NODE; 1757 nd = new hmotion_node(curenv->get_half_narrow_space_width(), 1758 curenv->get_fill_color()); 1759 return; 1760 case ESCAPE_NEWLINE: 1761 have_input = 0; 1762 break; 1763 case ESCAPE_LEFT_BRACE: 1764 ESCAPE_LEFT_BRACE: 1765 type = TOKEN_LEFT_BRACE; 1766 return; 1767 case ESCAPE_RIGHT_BRACE: 1768 ESCAPE_RIGHT_BRACE: 1769 type = TOKEN_RIGHT_BRACE; 1770 return; 1771 case ESCAPE_LEFT_QUOTE: 1772 ESCAPE_LEFT_QUOTE: 1773 type = TOKEN_SPECIAL; 1774 nm = symbol("ga"); 1775 return; 1776 case ESCAPE_RIGHT_QUOTE: 1777 ESCAPE_RIGHT_QUOTE: 1778 type = TOKEN_SPECIAL; 1779 nm = symbol("aa"); 1780 return; 1781 case ESCAPE_HYPHEN: 1782 ESCAPE_HYPHEN: 1783 type = TOKEN_SPECIAL; 1784 nm = symbol("-"); 1785 return; 1786 case ESCAPE_UNDERSCORE: 1787 ESCAPE_UNDERSCORE: 1788 type = TOKEN_SPECIAL; 1789 nm = symbol("ul"); 1790 return; 1791 case ESCAPE_c: 1792 ESCAPE_c: 1793 type = TOKEN_INTERRUPT; 1794 return; 1795 case ESCAPE_BANG: 1796 ESCAPE_BANG: 1797 type = TOKEN_TRANSPARENT; 1798 return; 1799 case ESCAPE_QUESTION: 1800 ESCAPE_QUESTION: 1801 nd = do_non_interpreted(); 1802 if (nd) { 1803 type = TOKEN_NODE; 1804 return; 1805 } 1806 break; 1807 case ESCAPE_AMPERSAND: 1808 ESCAPE_AMPERSAND: 1809 type = TOKEN_DUMMY; 1810 return; 1811 case ESCAPE_RIGHT_PARENTHESIS: 1812 ESCAPE_RIGHT_PARENTHESIS: 1813 type = TOKEN_TRANSPARENT_DUMMY; 1814 return; 1815 case '\b': 1816 type = TOKEN_BACKSPACE; 1817 return; 1818 case ' ': 1819 type = TOKEN_SPACE; 1820 return; 1821 case '\t': 1822 type = TOKEN_TAB; 1823 return; 1824 case '\n': 1825 type = TOKEN_NEWLINE; 1826 return; 1827 case '\001': 1828 type = TOKEN_LEADER; 1829 return; 1830 case 0: 1831 { 1832 assert(n != 0); 1833 token_node *tn = n->get_token_node(); 1834 if (tn) { 1835 *this = tn->tk; 1836 delete tn; 1837 } 1838 else { 1839 nd = n; 1840 type = TOKEN_NODE; 1841 } 1842 } 1843 return; 1844 default: 1845 type = TOKEN_CHAR; 1846 c = cc; 1847 return; 1848 } 1849 } 1850 else { 1851 handle_escape_char: 1852 cc = input_stack::get(&n); 1853 switch(cc) { 1854 case '(': 1855 nm = read_two_char_escape_name(); 1856 type = TOKEN_SPECIAL; 1857 return; 1858 case EOF: 1859 type = TOKEN_EOF; 1860 error("end of input after escape character"); 1861 return; 1862 case '`': 1863 goto ESCAPE_LEFT_QUOTE; 1864 case '\'': 1865 goto ESCAPE_RIGHT_QUOTE; 1866 case '-': 1867 goto ESCAPE_HYPHEN; 1868 case '_': 1869 goto ESCAPE_UNDERSCORE; 1870 case '%': 1871 goto ESCAPE_PERCENT; 1872 case ' ': 1873 goto ESCAPE_SPACE; 1874 case '0': 1875 nd = new hmotion_node(curenv->get_digit_width(), 1876 curenv->get_fill_color()); 1877 type = TOKEN_NODE; 1878 return; 1879 case '|': 1880 goto ESCAPE_BAR; 1881 case '^': 1882 goto ESCAPE_CIRCUMFLEX; 1883 case '/': 1884 type = TOKEN_ITALIC_CORRECTION; 1885 return; 1886 case ',': 1887 type = TOKEN_NODE; 1888 nd = new left_italic_corrected_node; 1889 return; 1890 case '&': 1891 goto ESCAPE_AMPERSAND; 1892 case ')': 1893 goto ESCAPE_RIGHT_PARENTHESIS; 1894 case '!': 1895 goto ESCAPE_BANG; 1896 case '?': 1897 goto ESCAPE_QUESTION; 1898 case '~': 1899 goto ESCAPE_TILDE; 1900 case ':': 1901 goto ESCAPE_COLON; 1902 case '"': 1903 while ((cc = input_stack::get(0)) != '\n' && cc != EOF) 1904 ; 1905 if (cc == '\n') 1906 type = TOKEN_NEWLINE; 1907 else 1908 type = TOKEN_EOF; 1909 return; 1910 case '#': // Like \" but newline is ignored. 1911 while ((cc = input_stack::get(0)) != '\n') 1912 if (cc == EOF) { 1913 type = TOKEN_EOF; 1914 return; 1915 } 1916 break; 1917 case '$': 1918 { 1919 symbol s = read_escape_name(); 1920 if (!(s.is_null() || s.is_empty())) 1921 interpolate_arg(s); 1922 break; 1923 } 1924 case '*': 1925 { 1926 symbol s = read_escape_name(WITH_ARGS); 1927 if (!(s.is_null() || s.is_empty())) { 1928 if (have_string_arg) { 1929 have_string_arg = 0; 1930 interpolate_string_with_args(s); 1931 } 1932 else 1933 interpolate_string(s); 1934 } 1935 break; 1936 } 1937 case 'a': 1938 nd = new non_interpreted_char_node('\001'); 1939 type = TOKEN_NODE; 1940 return; 1941 case 'A': 1942 c = '0' + do_name_test(); 1943 type = TOKEN_CHAR; 1944 return; 1945 case 'b': 1946 nd = do_bracket(); 1947 type = TOKEN_NODE; 1948 return; 1949 case 'B': 1950 c = '0' + do_expr_test(); 1951 type = TOKEN_CHAR; 1952 return; 1953 case 'c': 1954 goto ESCAPE_c; 1955 case 'C': 1956 nm = get_delim_name(); 1957 if (nm.is_null()) 1958 break; 1959 type = TOKEN_SPECIAL; 1960 return; 1961 case 'd': 1962 type = TOKEN_NODE; 1963 nd = new vmotion_node(curenv->get_size() / 2, 1964 curenv->get_fill_color()); 1965 return; 1966 case 'D': 1967 nd = read_draw_node(); 1968 if (!nd) 1969 break; 1970 type = TOKEN_NODE; 1971 return; 1972 case 'e': 1973 goto ESCAPE_e; 1974 case 'E': 1975 goto handle_escape_char; 1976 case 'f': 1977 { 1978 symbol s = read_escape_name(ALLOW_EMPTY); 1979 if (s.is_null()) 1980 break; 1981 const char *p; 1982 for (p = s.contents(); *p != '\0'; p++) 1983 if (!csdigit(*p)) 1984 break; 1985 if (*p || s.is_empty()) 1986 curenv->set_font(s); 1987 else 1988 curenv->set_font(atoi(s.contents())); 1989 if (!compatible_flag) 1990 have_input = 1; 1991 break; 1992 } 1993 case 'F': 1994 { 1995 symbol s = read_escape_name(ALLOW_EMPTY); 1996 if (s.is_null()) 1997 break; 1998 curenv->set_family(s); 1999 have_input = 1; 2000 break; 2001 } 2002 case 'g': 2003 { 2004 symbol s = read_escape_name(); 2005 if (!(s.is_null() || s.is_empty())) 2006 interpolate_number_format(s); 2007 break; 2008 } 2009 case 'h': 2010 if (!get_delim_number(&x, 'm')) 2011 break; 2012 type = TOKEN_NODE; 2013 nd = new hmotion_node(x, curenv->get_fill_color()); 2014 return; 2015 case 'H': 2016 // don't take height increments relative to previous height if 2017 // in compatibility mode 2018 if (!compatible_flag && curenv->get_char_height()) 2019 { 2020 if (get_delim_number(&x, 'z', curenv->get_char_height())) 2021 curenv->set_char_height(x); 2022 } 2023 else 2024 { 2025 if (get_delim_number(&x, 'z', curenv->get_requested_point_size())) 2026 curenv->set_char_height(x); 2027 } 2028 if (!compatible_flag) 2029 have_input = 1; 2030 break; 2031 case 'k': 2032 nm = read_escape_name(); 2033 if (nm.is_null() || nm.is_empty()) 2034 break; 2035 type = TOKEN_MARK_INPUT; 2036 return; 2037 case 'l': 2038 case 'L': 2039 { 2040 charinfo *s = 0; 2041 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s)) 2042 break; 2043 if (s == 0) 2044 s = get_charinfo(cc == 'l' ? "ru" : "br"); 2045 type = TOKEN_NODE; 2046 node *char_node = curenv->make_char_node(s); 2047 if (cc == 'l') 2048 nd = new hline_node(x, char_node); 2049 else 2050 nd = new vline_node(x, char_node); 2051 return; 2052 } 2053 case 'm': 2054 do_glyph_color(read_escape_name(ALLOW_EMPTY)); 2055 if (!compatible_flag) 2056 have_input = 1; 2057 break; 2058 case 'M': 2059 do_fill_color(read_escape_name(ALLOW_EMPTY)); 2060 if (!compatible_flag) 2061 have_input = 1; 2062 break; 2063 case 'n': 2064 { 2065 int inc; 2066 symbol s = read_increment_and_escape_name(&inc); 2067 if (!(s.is_null() || s.is_empty())) 2068 interpolate_number_reg(s, inc); 2069 break; 2070 } 2071 case 'N': 2072 if (!get_delim_number(&val, 0)) 2073 break; 2074 type = TOKEN_NUMBERED_CHAR; 2075 return; 2076 case 'o': 2077 nd = do_overstrike(); 2078 type = TOKEN_NODE; 2079 return; 2080 case 'O': 2081 nd = do_suppress(read_escape_name()); 2082 if (!nd) 2083 break; 2084 type = TOKEN_NODE; 2085 return; 2086 case 'p': 2087 type = TOKEN_SPREAD; 2088 return; 2089 case 'r': 2090 type = TOKEN_NODE; 2091 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color()); 2092 return; 2093 case 'R': 2094 do_register(); 2095 if (!compatible_flag) 2096 have_input = 1; 2097 break; 2098 case 's': 2099 if (read_size(&x)) 2100 curenv->set_size(x); 2101 if (!compatible_flag) 2102 have_input = 1; 2103 break; 2104 case 'S': 2105 if (get_delim_number(&x, 0)) 2106 curenv->set_char_slant(x); 2107 if (!compatible_flag) 2108 have_input = 1; 2109 break; 2110 case 't': 2111 type = TOKEN_NODE; 2112 nd = new non_interpreted_char_node('\t'); 2113 return; 2114 case 'u': 2115 type = TOKEN_NODE; 2116 nd = new vmotion_node(-curenv->get_size() / 2, 2117 curenv->get_fill_color()); 2118 return; 2119 case 'v': 2120 if (!get_delim_number(&x, 'v')) 2121 break; 2122 type = TOKEN_NODE; 2123 nd = new vmotion_node(x, curenv->get_fill_color()); 2124 return; 2125 case 'V': 2126 { 2127 symbol s = read_escape_name(); 2128 if (!(s.is_null() || s.is_empty())) 2129 interpolate_environment_variable(s); 2130 break; 2131 } 2132 case 'w': 2133 do_width(); 2134 break; 2135 case 'x': 2136 if (!get_delim_number(&x, 'v')) 2137 break; 2138 type = TOKEN_NODE; 2139 nd = new extra_size_node(x); 2140 return; 2141 case 'X': 2142 nd = do_special(); 2143 if (!nd) 2144 break; 2145 type = TOKEN_NODE; 2146 return; 2147 case 'Y': 2148 { 2149 symbol s = read_escape_name(); 2150 if (s.is_null() || s.is_empty()) 2151 break; 2152 request_or_macro *p = lookup_request(s); 2153 macro *m = p->to_macro(); 2154 if (!m) { 2155 error("can't transparently throughput a request"); 2156 break; 2157 } 2158 nd = new special_node(*m); 2159 type = TOKEN_NODE; 2160 return; 2161 } 2162 case 'z': 2163 { 2164 next(); 2165 if (type == TOKEN_NODE) 2166 nd = new zero_width_node(nd); 2167 else { 2168 charinfo *ci = get_char(1); 2169 if (ci == 0) 2170 break; 2171 node *gn = curenv->make_char_node(ci); 2172 if (gn == 0) 2173 break; 2174 nd = new zero_width_node(gn); 2175 type = TOKEN_NODE; 2176 } 2177 return; 2178 } 2179 case 'Z': 2180 nd = do_zero_width(); 2181 if (nd == 0) 2182 break; 2183 type = TOKEN_NODE; 2184 return; 2185 case '{': 2186 goto ESCAPE_LEFT_BRACE; 2187 case '}': 2188 goto ESCAPE_RIGHT_BRACE; 2189 case '\n': 2190 break; 2191 case '[': 2192 if (!compatible_flag) { 2193 symbol s = read_long_escape_name(WITH_ARGS); 2194 if (s.is_null() || s.is_empty()) 2195 break; 2196 if (have_string_arg) { 2197 have_string_arg = 0; 2198 nm = composite_glyph_name(s); 2199 } 2200 else { 2201 const char *gn = check_unicode_name(s.contents()); 2202 if (gn) { 2203 const char *gn_decomposed = decompose_unicode(gn); 2204 if (gn_decomposed) 2205 gn = &gn_decomposed[1]; 2206 const char *groff_gn = unicode_to_glyph_name(gn); 2207 if (groff_gn) 2208 nm = symbol(groff_gn); 2209 else { 2210 char *buf = new char[strlen(gn) + 1 + 1]; 2211 strcpy(buf, "u"); 2212 strcat(buf, gn); 2213 nm = symbol(buf); 2214 a_delete buf; 2215 } 2216 } 2217 else 2218 nm = symbol(s.contents()); 2219 } 2220 type = TOKEN_SPECIAL; 2221 return; 2222 } 2223 goto handle_normal_char; 2224 default: 2225 if (cc != escape_char && cc != '.') 2226 warning(WARN_ESCAPE, "escape character ignored before %1", 2227 input_char_description(cc)); 2228 goto handle_normal_char; 2229 } 2230 } 2231 } 2232} 2233 2234int token::operator==(const token &t) 2235{ 2236 if (type != t.type) 2237 return 0; 2238 switch(type) { 2239 case TOKEN_CHAR: 2240 return c == t.c; 2241 case TOKEN_SPECIAL: 2242 return nm == t.nm; 2243 case TOKEN_NUMBERED_CHAR: 2244 return val == t.val; 2245 default: 2246 return 1; 2247 } 2248} 2249 2250int token::operator!=(const token &t) 2251{ 2252 return !(*this == t); 2253} 2254 2255// is token a suitable delimiter (like ')? 2256 2257int token::delimiter(int err) 2258{ 2259 switch(type) { 2260 case TOKEN_CHAR: 2261 switch(c) { 2262 case '0': 2263 case '1': 2264 case '2': 2265 case '3': 2266 case '4': 2267 case '5': 2268 case '6': 2269 case '7': 2270 case '8': 2271 case '9': 2272 case '+': 2273 case '-': 2274 case '/': 2275 case '*': 2276 case '%': 2277 case '<': 2278 case '>': 2279 case '=': 2280 case '&': 2281 case ':': 2282 case '(': 2283 case ')': 2284 case '.': 2285 if (err) 2286 error("cannot use character `%1' as a starting delimiter", char(c)); 2287 return 0; 2288 default: 2289 return 1; 2290 } 2291 case TOKEN_NODE: 2292 case TOKEN_SPACE: 2293 case TOKEN_STRETCHABLE_SPACE: 2294 case TOKEN_UNSTRETCHABLE_SPACE: 2295 case TOKEN_TAB: 2296 case TOKEN_NEWLINE: 2297 if (err) 2298 error("cannot use %1 as a starting delimiter", description()); 2299 return 0; 2300 default: 2301 return 1; 2302 } 2303} 2304 2305const char *token::description() 2306{ 2307 static char buf[4]; 2308 switch (type) { 2309 case TOKEN_BACKSPACE: 2310 return "a backspace character"; 2311 case TOKEN_CHAR: 2312 buf[0] = '`'; 2313 buf[1] = c; 2314 buf[2] = '\''; 2315 buf[3] = '\0'; 2316 return buf; 2317 case TOKEN_DUMMY: 2318 return "`\\&'"; 2319 case TOKEN_ESCAPE: 2320 return "`\\e'"; 2321 case TOKEN_HYPHEN_INDICATOR: 2322 return "`\\%'"; 2323 case TOKEN_INTERRUPT: 2324 return "`\\c'"; 2325 case TOKEN_ITALIC_CORRECTION: 2326 return "`\\/'"; 2327 case TOKEN_LEADER: 2328 return "a leader character"; 2329 case TOKEN_LEFT_BRACE: 2330 return "`\\{'"; 2331 case TOKEN_MARK_INPUT: 2332 return "`\\k'"; 2333 case TOKEN_NEWLINE: 2334 return "newline"; 2335 case TOKEN_NODE: 2336 return "a node"; 2337 case TOKEN_NUMBERED_CHAR: 2338 return "`\\N'"; 2339 case TOKEN_RIGHT_BRACE: 2340 return "`\…
Large files files are truncated, but you can click here to view the full file