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 "`\\}'"; 2341 case TOKEN_SPACE: 2342 return "a space"; 2343 case TOKEN_SPECIAL: 2344 return "a special character"; 2345 case TOKEN_SPREAD: 2346 return "`\\p'"; 2347 case TOKEN_STRETCHABLE_SPACE: 2348 return "`\\~'"; 2349 case TOKEN_UNSTRETCHABLE_SPACE: 2350 return "`\\ '"; 2351 case TOKEN_TAB: 2352 return "a tab character"; 2353 case TOKEN_TRANSPARENT: 2354 return "`\\!'"; 2355 case TOKEN_TRANSPARENT_DUMMY: 2356 return "`\\)'"; 2357 case TOKEN_ZERO_WIDTH_BREAK: 2358 return "`\\:'"; 2359 case TOKEN_EOF: 2360 return "end of input"; 2361 default: 2362 break; 2363 } 2364 return "a magic token"; 2365} 2366 2367void skip_line() 2368{ 2369 while (!tok.newline()) 2370 if (tok.eof()) 2371 return; 2372 else 2373 tok.next(); 2374 tok.next(); 2375} 2376 2377void compatible() 2378{ 2379 int n; 2380 if (has_arg() && get_integer(&n)) 2381 compatible_flag = n != 0; 2382 else 2383 compatible_flag = 1; 2384 skip_line(); 2385} 2386 2387static void empty_name_warning(int required) 2388{ 2389 if (tok.newline() || tok.eof()) { 2390 if (required) 2391 warning(WARN_MISSING, "missing name"); 2392 } 2393 else if (tok.right_brace() || tok.tab()) { 2394 const char *start = tok.description(); 2395 do { 2396 tok.next(); 2397 } while (tok.space() || tok.right_brace() || tok.tab()); 2398 if (!tok.newline() && !tok.eof()) 2399 error("%1 is not allowed before an argument", start); 2400 else if (required) 2401 warning(WARN_MISSING, "missing name"); 2402 } 2403 else if (required) 2404 error("name expected (got %1)", tok.description()); 2405 else 2406 error("name expected (got %1): treated as missing", tok.description()); 2407} 2408 2409static void non_empty_name_warning() 2410{ 2411 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab() 2412 && !tok.right_brace() 2413 // We don't want to give a warning for .el\{ 2414 && !tok.left_brace()) 2415 error("%1 is not allowed in a name", tok.description()); 2416} 2417 2418symbol get_name(int required) 2419{ 2420 if (compatible_flag) { 2421 char buf[3]; 2422 tok.skip(); 2423 if ((buf[0] = tok.ch()) != 0) { 2424 tok.next(); 2425 if ((buf[1] = tok.ch()) != 0) { 2426 buf[2] = 0; 2427 tok.make_space(); 2428 } 2429 else 2430 non_empty_name_warning(); 2431 return symbol(buf); 2432 } 2433 else { 2434 empty_name_warning(required); 2435 return NULL_SYMBOL; 2436 } 2437 } 2438 else 2439 return get_long_name(required); 2440} 2441 2442symbol get_long_name(int required) 2443{ 2444 return do_get_long_name(required, 0); 2445} 2446 2447static symbol do_get_long_name(int required, char end) 2448{ 2449 while (tok.space()) 2450 tok.next(); 2451 char abuf[ABUF_SIZE]; 2452 char *buf = abuf; 2453 int buf_size = ABUF_SIZE; 2454 int i = 0; 2455 for (;;) { 2456 // If end != 0 we normally have to append a null byte 2457 if (i + 2 > buf_size) { 2458 if (buf == abuf) { 2459 buf = new char[ABUF_SIZE*2]; 2460 memcpy(buf, abuf, buf_size); 2461 buf_size = ABUF_SIZE*2; 2462 } 2463 else { 2464 char *old_buf = buf; 2465 buf = new char[buf_size*2]; 2466 memcpy(buf, old_buf, buf_size); 2467 buf_size *= 2; 2468 a_delete old_buf; 2469 } 2470 } 2471 if ((buf[i] = tok.ch()) == 0 || buf[i] == end) 2472 break; 2473 i++; 2474 tok.next(); 2475 } 2476 if (i == 0) { 2477 empty_name_warning(required); 2478 return NULL_SYMBOL; 2479 } 2480 if (end && buf[i] == end) 2481 buf[i+1] = '\0'; 2482 else 2483 non_empty_name_warning(); 2484 if (buf == abuf) 2485 return symbol(buf); 2486 else { 2487 symbol s(buf); 2488 a_delete buf; 2489 return s; 2490 } 2491} 2492 2493void exit_troff() 2494{ 2495 exit_started = 1; 2496 topdiv->set_last_page(); 2497 if (!end_macro_name.is_null()) { 2498 spring_trap(end_macro_name); 2499 tok.next(); 2500 process_input_stack(); 2501 } 2502 curenv->final_break(); 2503 tok.next(); 2504 process_input_stack(); 2505 end_diversions(); 2506 if (topdiv->get_page_length() > 0) { 2507 done_end_macro = 1; 2508 topdiv->set_ejecting(); 2509 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' }; 2510 input_stack::push(make_temp_iterator((char *)buf)); 2511 topdiv->space(topdiv->get_page_length(), 1); 2512 tok.next(); 2513 process_input_stack(); 2514 seen_last_page_ejector = 1; // should be set already 2515 topdiv->set_ejecting(); 2516 push_page_ejector(); 2517 topdiv->space(topdiv->get_page_length(), 1); 2518 tok.next(); 2519 process_input_stack(); 2520 } 2521 // This will only happen if a trap-invoked macro starts a diversion, 2522 // or if vertical position traps have been disabled. 2523 cleanup_and_exit(0); 2524} 2525 2526// This implements .ex. The input stack must be cleared before calling 2527// exit_troff(). 2528 2529void exit_request() 2530{ 2531 input_stack::clear(); 2532 if (exit_started) 2533 tok.next(); 2534 else 2535 exit_troff(); 2536} 2537 2538void return_macro_request() 2539{ 2540 if (has_arg() && tok.ch()) 2541 input_stack::pop_macro(); 2542 input_stack::pop_macro(); 2543 tok.next(); 2544} 2545 2546void end_macro() 2547{ 2548 end_macro_name = get_name(); 2549 skip_line(); 2550} 2551 2552void blank_line_macro() 2553{ 2554 blank_line_macro_name = get_name(); 2555 skip_line(); 2556} 2557 2558static void trapping_blank_line() 2559{ 2560 if (!blank_line_macro_name.is_null()) 2561 spring_trap(blank_line_macro_name); 2562 else 2563 blank_line(); 2564} 2565 2566void do_request() 2567{ 2568 int old_compatible_flag = compatible_flag; 2569 compatible_flag = 0; 2570 symbol nm = get_name(); 2571 if (nm.is_null()) 2572 skip_line(); 2573 else 2574 interpolate_macro(nm); 2575 compatible_flag = old_compatible_flag; 2576} 2577 2578inline int possibly_handle_first_page_transition() 2579{ 2580 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) { 2581 handle_first_page_transition(); 2582 return 1; 2583 } 2584 else 2585 return 0; 2586} 2587 2588static int transparent_translate(int cc) 2589{ 2590 if (!invalid_input_char(cc)) { 2591 charinfo *ci = charset_table[cc]; 2592 switch (ci->get_special_translation(1)) { 2593 case charinfo::TRANSLATE_SPACE: 2594 return ' '; 2595 case charinfo::TRANSLATE_STRETCHABLE_SPACE: 2596 return ESCAPE_TILDE; 2597 case charinfo::TRANSLATE_DUMMY: 2598 return ESCAPE_AMPERSAND; 2599 case charinfo::TRANSLATE_HYPHEN_INDICATOR: 2600 return ESCAPE_PERCENT; 2601 } 2602 // This is really ugly. 2603 ci = ci->get_translation(1); 2604 if (ci) { 2605 int c = ci->get_ascii_code(); 2606 if (c != '\0') 2607 return c; 2608 error("can't translate %1 to special character `%2'" 2609 " in transparent throughput", 2610 input_char_description(cc), 2611 ci->nm.contents()); 2612 } 2613 } 2614 return cc; 2615} 2616 2617class int_stack { 2618 struct int_stack_element { 2619 int n; 2620 int_stack_element *next; 2621 } *top; 2622public: 2623 int_stack(); 2624 ~int_stack(); 2625 void push(int); 2626 int is_empty(); 2627 int pop(); 2628}; 2629 2630int_stack::int_stack() 2631{ 2632 top = 0; 2633} 2634 2635int_stack::~int_stack() 2636{ 2637 while (top != 0) { 2638 int_stack_element *temp = top; 2639 top = top->next; 2640 delete temp; 2641 } 2642} 2643 2644int int_stack::is_empty() 2645{ 2646 return top == 0; 2647} 2648 2649void int_stack::push(int n) 2650{ 2651 int_stack_element *p = new int_stack_element; 2652 p->next = top; 2653 p->n = n; 2654 top = p; 2655} 2656 2657int int_stack::pop() 2658{ 2659 assert(top != 0); 2660 int_stack_element *p = top; 2661 top = top->next; 2662 int n = p->n; 2663 delete p; 2664 return n; 2665} 2666 2667int node::reread(int *) 2668{ 2669 return 0; 2670} 2671 2672int global_diverted_space = 0; 2673 2674int diverted_space_node::reread(int *bolp) 2675{ 2676 global_diverted_space = 1; 2677 if (curenv->get_fill()) 2678 trapping_blank_line(); 2679 else 2680 curdiv->space(n); 2681 global_diverted_space = 0; 2682 *bolp = 1; 2683 return 1; 2684} 2685 2686int diverted_copy_file_node::reread(int *bolp) 2687{ 2688 curdiv->copy_file(filename.contents()); 2689 *bolp = 1; 2690 return 1; 2691} 2692 2693int word_space_node::reread(int *) 2694{ 2695 if (unformat) { 2696 for (width_list *w = orig_width; w; w = w->next) 2697 curenv->space(w->width, w->sentence_width); 2698 unformat = 0; 2699 return 1; 2700 } 2701 return 0; 2702} 2703 2704int unbreakable_space_node::reread(int *) 2705{ 2706 return 0; 2707} 2708 2709int hmotion_node::reread(int *) 2710{ 2711 if (unformat && was_tab) { 2712 curenv->handle_tab(0); 2713 unformat = 0; 2714 return 1; 2715 } 2716 return 0; 2717} 2718 2719void process_input_stack() 2720{ 2721 int_stack trap_bol_stack; 2722 int bol = 1; 2723 for (;;) { 2724 int suppress_next = 0; 2725 switch (tok.type) { 2726 case token::TOKEN_CHAR: 2727 { 2728 unsigned char ch = tok.c; 2729 if (bol && !have_input 2730 && (ch == curenv->control_char 2731 || ch == curenv->no_break_control_char)) { 2732 break_flag = ch == curenv->control_char; 2733 // skip tabs as well as spaces here 2734 do { 2735 tok.next(); 2736 } while (tok.white_space()); 2737 symbol nm = get_name(); 2738#if defined(DEBUGGING) 2739 if (debug_state) { 2740 if (! nm.is_null()) { 2741 if (strcmp(nm.contents(), "test") == 0) { 2742 fprintf(stderr, "found it!\n"); 2743 fflush(stderr); 2744 } 2745 fprintf(stderr, "interpreting [%s]", nm.contents()); 2746 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv) 2747 fprintf(stderr, " currently in diversion: %s", 2748 curdiv->get_diversion_name()); 2749 fprintf(stderr, "\n"); 2750 fflush(stderr); 2751 } 2752 } 2753#endif 2754 if (nm.is_null()) 2755 skip_line(); 2756 else { 2757 interpolate_macro(nm); 2758#if defined(DEBUGGING) 2759 if (debug_state) { 2760 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents()); 2761 curenv->dump_troff_state(); 2762 } 2763#endif 2764 } 2765 suppress_next = 1; 2766 } 2767 else { 2768 if (possibly_handle_first_page_transition()) 2769 ; 2770 else { 2771 for (;;) { 2772#if defined(DEBUGGING) 2773 if (debug_state) { 2774 fprintf(stderr, "found [%c]\n", ch); fflush(stderr); 2775 } 2776#endif 2777 curenv->add_char(charset_table[ch]); 2778 tok.next(); 2779 if (tok.type != token::TOKEN_CHAR) 2780 break; 2781 ch = tok.c; 2782 } 2783 suppress_next = 1; 2784 bol = 0; 2785 } 2786 } 2787 break; 2788 } 2789 case token::TOKEN_TRANSPARENT: 2790 { 2791 if (bol) { 2792 if (possibly_handle_first_page_transition()) 2793 ; 2794 else { 2795 int cc; 2796 do { 2797 node *n; 2798 cc = get_copy(&n); 2799 if (cc != EOF) 2800 if (cc != '\0') 2801 curdiv->transparent_output(transparent_translate(cc)); 2802 else 2803 curdiv->transparent_output(n); 2804 } while (cc != '\n' && cc != EOF); 2805 if (cc == EOF) 2806 curdiv->transparent_output('\n'); 2807 } 2808 } 2809 break; 2810 } 2811 case token::TOKEN_NEWLINE: 2812 { 2813 if (bol && !old_have_input 2814 && !curenv->get_prev_line_interrupted()) 2815 trapping_blank_line(); 2816 else { 2817 curenv->newline(); 2818 bol = 1; 2819 } 2820 break; 2821 } 2822 case token::TOKEN_REQUEST: 2823 { 2824 int request_code = tok.c; 2825 tok.next(); 2826 switch (request_code) { 2827 case TITLE_REQUEST: 2828 title(); 2829 break; 2830 case COPY_FILE_REQUEST: 2831 copy_file(); 2832 break; 2833 case TRANSPARENT_FILE_REQUEST: 2834 transparent_file(); 2835 break; 2836#ifdef COLUMN 2837 case VJUSTIFY_REQUEST: 2838 vjustify(); 2839 break; 2840#endif /* COLUMN */ 2841 default: 2842 assert(0); 2843 break; 2844 } 2845 suppress_next = 1; 2846 break; 2847 } 2848 case token::TOKEN_SPACE: 2849 { 2850 if (possibly_handle_first_page_transition()) 2851 ; 2852 else if (bol && !curenv->get_prev_line_interrupted()) { 2853 int nspaces = 0; 2854 // save space_width now so that it isn't changed by \f or \s 2855 // which we wouldn't notice here 2856 hunits space_width = curenv->get_space_width(); 2857 do { 2858 nspaces += tok.nspaces(); 2859 tok.next(); 2860 } while (tok.space()); 2861 if (tok.newline()) 2862 trapping_blank_line(); 2863 else { 2864 push_token(tok); 2865 curenv->do_break(); 2866 curenv->add_node(new hmotion_node(space_width * nspaces, 2867 curenv->get_fill_color())); 2868 bol = 0; 2869 } 2870 } 2871 else { 2872 curenv->space(); 2873 bol = 0; 2874 } 2875 break; 2876 } 2877 case token::TOKEN_EOF: 2878 return; 2879 case token::TOKEN_NODE: 2880 { 2881 if (possibly_handle_first_page_transition()) 2882 ; 2883 else if (tok.nd->reread(&bol)) { 2884 delete tok.nd; 2885 tok.nd = 0; 2886 } 2887 else { 2888 curenv->add_node(tok.nd); 2889 tok.nd = 0; 2890 bol = 0; 2891 curenv->possibly_break_line(1); 2892 } 2893 break; 2894 } 2895 case token::TOKEN_PAGE_EJECTOR: 2896 { 2897 continue_page_eject(); 2898 // I think we just want to preserve bol. 2899 // bol = 1; 2900 break; 2901 } 2902 case token::TOKEN_BEGIN_TRAP: 2903 { 2904 trap_bol_stack.push(bol); 2905 bol = 1; 2906 have_input = 0; 2907 break; 2908 } 2909 case token::TOKEN_END_TRAP: 2910 { 2911 if (trap_bol_stack.is_empty()) 2912 error("spurious end trap token detected!"); 2913 else 2914 bol = trap_bol_stack.pop(); 2915 have_input = 0; 2916 2917 /* I'm not totally happy about this. But I can't think of any other 2918 way to do it. Doing an output_pending_lines() whenever a 2919 TOKEN_END_TRAP is detected doesn't work: for example, 2920 2921 .wh -1i x 2922 .de x 2923 'bp 2924 .. 2925 .wh -.5i y 2926 .de y 2927 .tl ''-%-'' 2928 .. 2929 .br 2930 .ll .5i 2931 .sp |\n(.pu-1i-.5v 2932 a\%very\%very\%long\%word 2933 2934 will print all but the first lines from the word immediately 2935 after the footer, rather than on the next page. */ 2936 2937 if (trap_bol_stack.is_empty()) 2938 curenv->output_pending_lines(); 2939 break; 2940 } 2941 default: 2942 { 2943 bol = 0; 2944 tok.process(); 2945 break; 2946 } 2947 } 2948 if (!suppress_next) 2949 tok.next(); 2950 trap_sprung_flag = 0; 2951 } 2952} 2953 2954#ifdef WIDOW_CONTROL 2955 2956void flush_pending_lines() 2957{ 2958 while (!tok.newline() && !tok.eof()) 2959 tok.next(); 2960 curenv->output_pending_lines(); 2961 tok.next(); 2962} 2963 2964#endif /* WIDOW_CONTROL */ 2965 2966request_or_macro::request_or_macro() 2967{ 2968} 2969 2970macro *request_or_macro::to_macro() 2971{ 2972 return 0; 2973} 2974 2975request::request(REQUEST_FUNCP pp) : p(pp) 2976{ 2977} 2978 2979void request::invoke(symbol) 2980{ 2981 (*p)(); 2982} 2983 2984struct char_block { 2985 enum { SIZE = 128 }; 2986 unsigned char s[SIZE]; 2987 char_block *next; 2988 char_block(); 2989}; 2990 2991char_block::char_block() 2992: next(0) 2993{ 2994} 2995 2996class char_list { 2997public: 2998 char_list(); 2999 ~char_list(); 3000 void append(unsigned char); 3001 void set(unsigned char, int); 3002 unsigned char get(int); 3003 int length(); 3004private: 3005 unsigned char *ptr; 3006 int len; 3007 char_block *head; 3008 char_block *tail; 3009 friend class macro_header; 3010 friend class string_iterator; 3011}; 3012 3013char_list::char_list() 3014: ptr(0), len(0), head(0), tail(0) 3015{ 3016} 3017 3018char_list::~char_list() 3019{ 3020 while (head != 0) { 3021 char_block *tem = head; 3022 head = head->next; 3023 delete tem; 3024 } 3025} 3026 3027int char_list::length() 3028{ 3029 return len; 3030} 3031 3032void char_list::append(unsigned char c) 3033{ 3034 if (tail == 0) { 3035 head = tail = new char_block; 3036 ptr = tail->s; 3037 } 3038 else { 3039 if (ptr >= tail->s + char_block::SIZE) { 3040 tail->next = new char_block; 3041 tail = tail->next; 3042 ptr = tail->s; 3043 } 3044 } 3045 *ptr++ = c; 3046 len++; 3047} 3048 3049void char_list::set(unsigned char c, int offset) 3050{ 3051 assert(len > offset); 3052 // optimization for access at the end 3053 int boundary = len - len % char_block::SIZE; 3054 if (offset >= boundary) { 3055 *(tail->s + offset - boundary) = c; 3056 return; 3057 } 3058 char_block *tem = head; 3059 int l = 0; 3060 for (;;) { 3061 l += char_block::SIZE; 3062 if (l > offset) { 3063 *(tem->s + offset % char_block::SIZE) = c; 3064 return; 3065 } 3066 tem = tem->next; 3067 } 3068} 3069 3070unsigned char char_list::get(int offset) 3071{ 3072 assert(len > offset); 3073 // optimization for access at the end 3074 int boundary = len - len % char_block::SIZE; 3075 if (offset >= boundary) 3076 return *(tail->s + offset - boundary); 3077 char_block *tem = head; 3078 int l = 0; 3079 for (;;) { 3080 l += char_block::SIZE; 3081 if (l > offset) 3082 return *(tem->s + offset % char_block::SIZE); 3083 tem = tem->next; 3084 } 3085} 3086 3087class node_list { 3088 node *head; 3089 node *tail; 3090public: 3091 node_list(); 3092 ~node_list(); 3093 void append(node *); 3094 int length(); 3095 node *extract(); 3096 3097 friend class macro_header; 3098 friend class string_iterator; 3099}; 3100 3101void node_list::append(node *n) 3102{ 3103 if (head == 0) { 3104 n->next = 0; 3105 head = tail = n; 3106 } 3107 else { 3108 n->next = 0; 3109 tail = tail->next = n; 3110 } 3111} 3112 3113int node_list::length() 3114{ 3115 int total = 0; 3116 for (node *n = head; n != 0; n = n->next) 3117 ++total; 3118 return total; 3119} 3120 3121node_list::node_list() 3122{ 3123 head = tail = 0; 3124} 3125 3126node *node_list::extract() 3127{ 3128 node *temp = head; 3129 head = tail = 0; 3130 return temp; 3131} 3132 3133node_list::~node_list() 3134{ 3135 delete_node_list(head); 3136} 3137 3138class macro_header { 3139public: 3140 int count; 3141 char_list cl; 3142 node_list nl; 3143 macro_header() { count = 1; } 3144 macro_header *copy(int); 3145}; 3146 3147macro::~macro() 3148{ 3149 if (p != 0 && --(p->count) <= 0) 3150 delete p; 3151} 3152 3153macro::macro() 3154: is_a_diversion(0) 3155{ 3156 if (!input_stack::get_location(1, &filename, &lineno)) { 3157 filename = 0; 3158 lineno = 0; 3159 } 3160 len = 0; 3161 empty_macro = 1; 3162 p = 0; 3163} 3164 3165macro::macro(const macro &m) 3166: filename(m.filename), lineno(m.lineno), len(m.len), 3167 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p) 3168{ 3169 if (p != 0) 3170 p->count++; 3171} 3172 3173macro::macro(int is_div) 3174 : is_a_diversion(is_div) 3175{ 3176 if (!input_stack::get_location(1, &filename, &lineno)) { 3177 filename = 0; 3178 lineno = 0; 3179 } 3180 len = 0; 3181 empty_macro = 1; 3182 p = 0; 3183} 3184 3185int macro::is_diversion() 3186{ 3187 return is_a_diversion; 3188} 3189 3190macro ¯o::operator=(const macro &m) 3191{ 3192 // don't assign object 3193 if (m.p != 0) 3194 m.p->count++; 3195 if (p != 0 && --(p->count) <= 0) 3196 delete p; 3197 p = m.p; 3198 filename = m.filename; 3199 lineno = m.lineno; 3200 len = m.len; 3201 empty_macro = m.empty_macro; 3202 is_a_diversion = m.is_a_diversion; 3203 return *this; 3204} 3205 3206void macro::append(unsigned char c) 3207{ 3208 assert(c != 0); 3209 if (p == 0) 3210 p = new macro_header; 3211 if (p->cl.length() != len) { 3212 macro_header *tem = p->copy(len); 3213 if (--(p->count) <= 0) 3214 delete p; 3215 p = tem; 3216 } 3217 p->cl.append(c); 3218 ++len; 3219 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE) 3220 empty_macro = 0; 3221} 3222 3223void macro::set(unsigned char c, int offset) 3224{ 3225 assert(p != 0); 3226 assert(c != 0); 3227 p->cl.set(c, offset); 3228} 3229 3230unsigned char macro::get(int offset) 3231{ 3232 assert(p != 0); 3233 return p->cl.get(offset); 3234} 3235 3236int macro::length() 3237{ 3238 return len; 3239} 3240 3241void macro::append_str(const char *s) 3242{ 3243 int i = 0; 3244 3245 if (s) { 3246 while (s[i] != (char)0) { 3247 append(s[i]); 3248 i++; 3249 } 3250 } 3251} 3252 3253void macro::append(node *n) 3254{ 3255 assert(n != 0); 3256 if (p == 0) 3257 p = new macro_header; 3258 if (p->cl.length() != len) { 3259 macro_header *tem = p->copy(len); 3260 if (--(p->count) <= 0) 3261 delete p; 3262 p = tem; 3263 } 3264 p->cl.append(0); 3265 p->nl.append(n); 3266 ++len; 3267 empty_macro = 0; 3268} 3269 3270void macro::append_unsigned(unsigned int i) 3271{ 3272 unsigned int j = i / 10; 3273 if (j != 0) 3274 append_unsigned(j); 3275 append(((unsigned char)(((int)'0') + i % 10))); 3276} 3277 3278void macro::append_int(int i) 3279{ 3280 if (i < 0) { 3281 append('-'); 3282 i = -i; 3283 } 3284 append_unsigned((unsigned int)i); 3285} 3286 3287void macro::print_size() 3288{ 3289 errprint("%1", len); 3290} 3291 3292// make a copy of the first n bytes 3293 3294macro_header *macro_header::copy(int n) 3295{ 3296 macro_header *p = new macro_header; 3297 char_block *bp = cl.head; 3298 unsigned char *ptr = bp->s; 3299 node *nd = nl.head; 3300 while (--n >= 0) { 3301 if (ptr >= bp->s + char_block::SIZE) { 3302 bp = bp->next; 3303 ptr = bp->s; 3304 } 3305 unsigned char c = *ptr++; 3306 p->cl.append(c); 3307 if (c == 0) { 3308 p->nl.append(nd->copy()); 3309 nd = nd->next; 3310 } 3311 } 3312 return p; 3313} 3314 3315void print_macros() 3316{ 3317 object_dictionary_iterator iter(request_dictionary); 3318 request_or_macro *rm; 3319 symbol s; 3320 while (iter.get(&s, (object **)&rm)) { 3321 assert(!s.is_null()); 3322 macro *m = rm->to_macro(); 3323 if (m) { 3324 errprint("%1\t", s.contents()); 3325 m->print_size(); 3326 errprint("\n"); 3327 } 3328 } 3329 fflush(stderr); 3330 skip_line(); 3331} 3332 3333class string_iterator : public input_iterator { 3334 macro mac; 3335 const char *how_invoked; 3336 int newline_flag; 3337 int lineno; 3338 char_block *bp; 3339 int count; // of characters remaining 3340 node *nd; 3341 int saved_compatible_flag; 3342protected: 3343 symbol nm; 3344 string_iterator(); 3345public: 3346 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL); 3347 int fill(node **); 3348 int peek(); 3349 int get_location(int, const char **, int *); 3350 void backtrace(); 3351 void save_compatible_flag(int f) { saved_compatible_flag = f; } 3352 int get_compatible_flag() { return saved_compatible_flag; } 3353 int is_diversion(); 3354}; 3355 3356string_iterator::string_iterator(const macro &m, const char *p, symbol s) 3357: input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0), 3358 lineno(1), nm(s) 3359{ 3360 count = mac.len; 3361 if (count != 0) { 3362 bp = mac.p->cl.head; 3363 nd = mac.p->nl.head; 3364 ptr = eptr = bp->s; 3365 } 3366 else { 3367 bp = 0; 3368 nd = 0; 3369 ptr = eptr = 0; 3370 } 3371} 3372 3373string_iterator::string_iterator() 3374{ 3375 bp = 0; 3376 nd = 0; 3377 ptr = eptr = 0; 3378 newline_flag = 0; 3379 how_invoked = 0; 3380 lineno = 1; 3381 count = 0; 3382} 3383 3384int string_iterator::is_diversion() 3385{ 3386 return mac.is_diversion(); 3387} 3388 3389int string_iterator::fill(node **np) 3390{ 3391 if (newline_flag) 3392 lineno++; 3393 newline_flag = 0; 3394 if (count <= 0) 3395 return EOF; 3396 const unsigned char *p = eptr; 3397 if (p >= bp->s + char_block::SIZE) { 3398 bp = bp->next; 3399 p = bp->s; 3400 } 3401 if (*p == '\0') { 3402 if (np) { 3403 *np = nd->copy(); 3404 if (is_diversion()) 3405 (*np)->div_nest_level = input_stack::get_div_level(); 3406 else 3407 (*np)->div_nest_level = 0; 3408 } 3409 nd = nd->next; 3410 eptr = ptr = p + 1; 3411 count--; 3412 return 0; 3413 } 3414 const unsigned char *e = bp->s + char_block::SIZE; 3415 if (e - p > count) 3416 e = p + count; 3417 ptr = p; 3418 while (p < e) { 3419 unsigned char c = *p; 3420 if (c == '\n' || c == ESCAPE_NEWLINE) { 3421 newline_flag = 1; 3422 p++; 3423 break; 3424 } 3425 if (c == '\0') 3426 break; 3427 p++; 3428 } 3429 eptr = p; 3430 count -= p - ptr; 3431 return *ptr++; 3432} 3433 3434int string_iterator::peek() 3435{ 3436 if (count <= 0) 3437 return EOF; 3438 const unsigned char *p = eptr; 3439 if (p >= bp->s + char_block::SIZE) { 3440 p = bp->next->s; 3441 } 3442 return *p; 3443} 3444 3445int string_iterator::get_location(int allow_macro, 3446 const char **filep, int *linep) 3447{ 3448 if (!allow_macro) 3449 return 0; 3450 if (mac.filename == 0) 3451 return 0; 3452 *filep = mac.filename; 3453 *linep = mac.lineno + lineno - 1; 3454 return 1; 3455} 3456 3457void string_iterator::backtrace() 3458{ 3459 if (mac.filename) { 3460 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1); 3461 if (how_invoked) { 3462 if (!nm.is_null()) 3463 errprint(": %1 `%2'\n", how_invoked, nm.contents()); 3464 else 3465 errprint(": %1\n", how_invoked); 3466 } 3467 else 3468 errprint("\n"); 3469 } 3470} 3471 3472class temp_iterator : public input_iterator { 3473 unsigned char *base; 3474 temp_iterator(const char *, int len); 3475public: 3476 ~temp_iterator(); 3477 friend input_iterator *make_temp_iterator(const char *); 3478}; 3479 3480#ifdef __GNUG__ 3481inline 3482#endif 3483temp_iterator::temp_iterator(const char *s, int len) 3484{ 3485 base = new unsigned char[len]; 3486 memcpy(base, s, len); 3487 ptr = base; 3488 eptr = base + len; 3489} 3490 3491temp_iterator::~temp_iterator() 3492{ 3493 a_delete base; 3494} 3495 3496class small_temp_iterator : public input_iterator { 3497private: 3498 small_temp_iterator(const char *, int); 3499 ~small_temp_iterator(); 3500 enum { BLOCK = 16 }; 3501 static small_temp_iterator *free_list; 3502 void *operator new(size_t); 3503 void operator delete(void *); 3504 enum { SIZE = 12 }; 3505 unsigned char buf[SIZE]; 3506 friend input_iterator *make_temp_iterator(const char *); 3507}; 3508 3509small_temp_iterator *small_temp_iterator::free_list = 0; 3510 3511void *small_temp_iterator::operator new(size_t n) 3512{ 3513 assert(n == sizeof(small_temp_iterator)); 3514 if (!free_list) { 3515 free_list = 3516 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK]; 3517 for (int i = 0; i < BLOCK - 1; i++) 3518 free_list[i].next = free_list + i + 1; 3519 free_list[BLOCK-1].next = 0; 3520 } 3521 small_temp_iterator *p = free_list; 3522 free_list = (small_temp_iterator *)(free_list->next); 3523 p->next = 0; 3524 return p; 3525} 3526 3527#ifdef __GNUG__ 3528inline 3529#endif 3530void small_temp_iterator::operator delete(void *p) 3531{ 3532 if (p) { 3533 ((small_temp_iterator *)p)->next = free_list; 3534 free_list = (small_temp_iterator *)p; 3535 } 3536} 3537 3538small_temp_iterator::~small_temp_iterator() 3539{ 3540} 3541 3542#ifdef __GNUG__ 3543inline 3544#endif 3545small_temp_iterator::small_temp_iterator(const char *s, int len) 3546{ 3547 for (int i = 0; i < len; i++) 3548 buf[i] = s[i]; 3549 ptr = buf; 3550 eptr = buf + len; 3551} 3552 3553input_iterator *make_temp_iterator(const char *s) 3554{ 3555 if (s == 0) 3556 return new small_temp_iterator(s, 0); 3557 else { 3558 int n = strlen(s); 3559 if (n <= small_temp_iterator::SIZE) 3560 return new small_temp_iterator(s, n); 3561 else 3562 return new temp_iterator(s, n); 3563 } 3564} 3565 3566// this is used when macros with arguments are interpolated 3567 3568struct arg_list { 3569 macro mac; 3570 arg_list *next; 3571 arg_list(const macro &); 3572 ~arg_list(); 3573}; 3574 3575arg_list::arg_list(const macro &m) : mac(m), next(0) 3576{ 3577} 3578 3579arg_list::~arg_list() 3580{ 3581} 3582 3583class macro_iterator : public string_iterator { 3584 arg_list *args; 3585 int argc; 3586public: 3587 macro_iterator(symbol, macro &, const char *how_invoked = "macro"); 3588 macro_iterator(); 3589 ~macro_iterator(); 3590 int has_args() { return 1; } 3591 input_iterator *get_arg(int i); 3592 int nargs() { return argc; } 3593 void add_arg(const macro &m); 3594 void shift(int n); 3595 int is_macro() { return 1; } 3596 int is_diversion(); 3597}; 3598 3599input_iterator *macro_iterator::get_arg(int i) 3600{ 3601 if (i == 0) 3602 return make_temp_iterator(nm.contents()); 3603 if (i > 0 && i <= argc) { 3604 arg_list *p = args; 3605 for (int j = 1; j < i; j++) { 3606 assert(p != 0); 3607 p = p->next; 3608 } 3609 return new string_iterator(p->mac); 3610 } 3611 else 3612 return 0; 3613} 3614 3615void macro_iterator::add_arg(const macro &m) 3616{ 3617 arg_list **p; 3618 for (p = &args; *p; p = &((*p)->next)) 3619 ; 3620 *p = new arg_list(m); 3621 ++argc; 3622} 3623 3624void macro_iterator::shift(int n) 3625{ 3626 while (n > 0 && argc > 0) { 3627 arg_list *tem = args; 3628 args = args->next; 3629 delete tem; 3630 --argc; 3631 --n; 3632 } 3633} 3634 3635// This gets used by eg .if '\?xxx\?''. 3636 3637int operator==(const macro &m1, const macro &m2) 3638{ 3639 if (m1.len != m2.len) 3640 return 0; 3641 string_iterator iter1(m1); 3642 string_iterator iter2(m2); 3643 int n = m1.len; 3644 while (--n >= 0) { 3645 node *nd1 = 0; 3646 int c1 = iter1.get(&nd1); 3647 assert(c1 != EOF); 3648 node *nd2 = 0; 3649 int c2 = iter2.get(&nd2); 3650 assert(c2 != EOF); 3651 if (c1 != c2) { 3652 if (c1 == 0) 3653 delete nd1; 3654 else if (c2 == 0) 3655 delete nd2; 3656 return 0; 3657 } 3658 if (c1 == 0) { 3659 assert(nd1 != 0); 3660 assert(nd2 != 0); 3661 int are_same = nd1->type() == nd2->type() && nd1->same(nd2); 3662 delete nd1; 3663 delete nd2; 3664 if (!are_same) 3665 return 0; 3666 } 3667 } 3668 return 1; 3669} 3670 3671static void interpolate_macro(symbol nm) 3672{ 3673 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 3674 if (p == 0) { 3675 int warned = 0; 3676 const char *s = nm.contents(); 3677 if (strlen(s) > 2) { 3678 request_or_macro *r; 3679 char buf[3]; 3680 buf[0] = s[0]; 3681 buf[1] = s[1]; 3682 buf[2] = '\0'; 3683 r = (request_or_macro *)request_dictionary.lookup(symbol(buf)); 3684 if (r) { 3685 macro *m = r->to_macro(); 3686 if (!m || !m->empty()) 3687 warned = warning(WARN_SPACE, 3688 "macro `%1' not defined " 3689 "(probably missing space after `%2')", 3690 nm.contents(), buf); 3691 } 3692 } 3693 if (!warned) { 3694 warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 3695 p = new macro; 3696 request_dictionary.define(nm, p); 3697 } 3698 } 3699 if (p) 3700 p->invoke(nm); 3701 else { 3702 skip_line(); 3703 return; 3704 } 3705} 3706 3707static void decode_args(macro_iterator *mi) 3708{ 3709 if (!tok.newline() && !tok.eof()) { 3710 node *n; 3711 int c = get_copy(&n); 3712 for (;;) { 3713 while (c == ' ') 3714 c = get_copy(&n); 3715 if (c == '\n' || c == EOF) 3716 break; 3717 macro arg; 3718 int quote_input_level = 0; 3719 int done_tab_warning = 0; 3720 if (c == '"') { 3721 quote_input_level = input_stack::get_level(); 3722 c = get_copy(&n); 3723 } 3724 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE); 3725 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) { 3726 if (quote_input_level > 0 && c == '"' 3727 && (compatible_flag 3728 || input_stack::get_level() == quote_input_level)) { 3729 c = get_copy(&n); 3730 if (c == '"') { 3731 arg.append(c); 3732 c = get_copy(&n); 3733 } 3734 else 3735 break; 3736 } 3737 else { 3738 if (c == 0) 3739 arg.append(n); 3740 else { 3741 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3742 warning(WARN_TAB, "tab character in unquoted macro argument"); 3743 done_tab_warning = 1; 3744 } 3745 arg.append(c); 3746 } 3747 c = get_copy(&n); 3748 } 3749 } 3750 arg.append(POP_GROFFCOMP_MODE); 3751 mi->add_arg(arg); 3752 } 3753 } 3754} 3755 3756static void decode_string_args(macro_iterator *mi) 3757{ 3758 node *n; 3759 int c = get_copy(&n); 3760 for (;;) { 3761 while (c == ' ') 3762 c = get_copy(&n); 3763 if (c == '\n' || c == EOF) { 3764 error("missing `]'"); 3765 break; 3766 } 3767 if (c == ']') 3768 break; 3769 macro arg; 3770 int quote_input_level = 0; 3771 int done_tab_warning = 0; 3772 if (c == '"') { 3773 quote_input_level = input_stack::get_level(); 3774 c = get_copy(&n); 3775 } 3776 while (c != EOF && c != '\n' 3777 && !(c == ']' && quote_input_level == 0) 3778 && !(c == ' ' && quote_input_level == 0)) { 3779 if (quote_input_level > 0 && c == '"' 3780 && input_stack::get_level() == quote_input_level) { 3781 c = get_copy(&n); 3782 if (c == '"') { 3783 arg.append(c); 3784 c = get_copy(&n); 3785 } 3786 else 3787 break; 3788 } 3789 else { 3790 if (c == 0) 3791 arg.append(n); 3792 else { 3793 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3794 warning(WARN_TAB, "tab character in unquoted string argument"); 3795 done_tab_warning = 1; 3796 } 3797 arg.append(c); 3798 } 3799 c = get_copy(&n); 3800 } 3801 } 3802 mi->add_arg(arg); 3803 } 3804} 3805 3806void macro::invoke(symbol nm) 3807{ 3808 macro_iterator *mi = new macro_iterator(nm, *this); 3809 decode_args(mi); 3810 input_stack::push(mi); 3811 tok.next(); 3812} 3813 3814macro *macro::to_macro() 3815{ 3816 return this; 3817} 3818 3819int macro::empty() 3820{ 3821 return empty_macro == 1; 3822} 3823 3824macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called) 3825: string_iterator(m, how_called, s), args(0), argc(0) 3826{ 3827} 3828 3829macro_iterator::macro_iterator() : args(0), argc(0) 3830{ 3831} 3832 3833macro_iterator::~macro_iterator() 3834{ 3835 while (args != 0) { 3836 arg_list *tem = args; 3837 args = args->next; 3838 delete tem; 3839 } 3840} 3841 3842dictionary composite_dictionary(17); 3843 3844void composite_request() 3845{ 3846 symbol from = get_name(1); 3847 if (!from.is_null()) { 3848 const char *from_gn = glyph_name_to_unicode(from.contents()); 3849 if (!from_gn) { 3850 from_gn = check_unicode_name(from.contents()); 3851 if (!from_gn) { 3852 error("invalid composite glyph name `%1'", from.contents()); 3853 skip_line(); 3854 return; 3855 } 3856 } 3857 const char *from_decomposed = decompose_unicode(from_gn); 3858 if (from_decomposed) 3859 from_gn = &from_decomposed[1]; 3860 symbol to = get_name(1); 3861 if (to.is_null()) 3862 composite_dictionary.remove(symbol(from_gn)); 3863 else { 3864 const char *to_gn = glyph_name_to_unicode(to.contents()); 3865 if (!to_gn) { 3866 to_gn = check_unicode_name(to.contents()); 3867 if (!to_gn) { 3868 error("invalid composite glyph name `%1'", to.contents()); 3869 skip_line(); 3870 return; 3871 } 3872 } 3873 const char *to_decomposed = decompose_unicode(to_gn); 3874 if (to_decomposed) 3875 to_gn = &to_decomposed[1]; 3876 if (strcmp(from_gn, to_gn) == 0) 3877 composite_dictionary.remove(symbol(from_gn)); 3878 else 3879 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn); 3880 } 3881 } 3882 skip_line(); 3883} 3884 3885static symbol composite_glyph_name(symbol nm) 3886{ 3887 macro_iterator *mi = new macro_iterator(); 3888 decode_string_args(mi); 3889 input_stack::push(mi); 3890 const char *gn = glyph_name_to_unicode(nm.contents()); 3891 if (!gn) { 3892 gn = check_unicode_name(nm.contents()); 3893 if (!gn) { 3894 error("invalid base glyph `%1' in composite glyph name", nm.contents()); 3895 return EMPTY_SYMBOL; 3896 } 3897 } 3898 const char *gn_decomposed = decompose_unicode(gn); 3899 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn); 3900 string gl; 3901 int n = input_stack::nargs(); 3902 for (int i = 1; i <= n; i++) { 3903 glyph_name += '_'; 3904 input_iterator *p = input_stack::get_arg(i); 3905 gl.clear(); 3906 int c; 3907 while ((c = p->get(0)) != EOF) 3908 gl += c; 3909 gl += '\0'; 3910 const char *u = glyph_name_to_unicode(gl.contents()); 3911 if (!u) { 3912 u = check_unicode_name(gl.contents()); 3913 if (!u) { 3914 error("invalid component `%1' in composite glyph name", 3915 gl.contents()); 3916 return EMPTY_SYMBOL; 3917 } 3918 } 3919 const char *decomposed = decompose_unicode(u); 3920 if (decomposed) 3921 u = &decomposed[1]; 3922 void *mapped_composite = composite_dictionary.lookup(symbol(u)); 3923 if (mapped_composite) 3924 u = (const char *)mapped_composite; 3925 glyph_name += u; 3926 } 3927 glyph_name += '\0'; 3928 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents()); 3929 if (groff_gn) 3930 return symbol(groff_gn); 3931 gl.clear(); 3932 gl += 'u'; 3933 gl += glyph_name; 3934 return symbol(gl.contents()); 3935} 3936 3937int trap_sprung_flag = 0; 3938int postpone_traps_flag = 0; 3939symbol postponed_trap; 3940 3941void spring_trap(symbol nm) 3942{ 3943 assert(!nm.is_null()); 3944 trap_sprung_flag = 1; 3945 if (postpone_traps_flag) { 3946 postponed_trap = nm; 3947 return; 3948 } 3949 static char buf[2] = { BEGIN_TRAP, 0 }; 3950 static char buf2[2] = { END_TRAP, '\0' }; 3951 input_stack::push(make_temp_iterator(buf2)); 3952 request_or_macro *p = lookup_request(nm); 3953 macro *m = p->to_macro(); 3954 if (m) 3955 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro")); 3956 else 3957 error("you can't invoke a request with a trap"); 3958 input_stack::push(make_temp_iterator(buf)); 3959} 3960 3961void postpone_traps() 3962{ 3963 postpone_traps_flag = 1; 3964} 3965 3966int unpostpone_traps() 3967{ 3968 postpone_traps_flag = 0; 3969 if (!postponed_trap.is_null()) { 3970 spring_trap(postponed_trap); 3971 postponed_trap = NULL_SYMBOL; 3972 return 1; 3973 } 3974 else 3975 return 0; 3976} 3977 3978void read_request() 3979{ 3980 macro_iterator *mi = new macro_iterator; 3981 int reading_from_terminal = isatty(fileno(stdin)); 3982 int had_prompt = 0; 3983 if (!tok.newline() && !tok.eof()) { 3984 int c = get_copy(0); 3985 while (c == ' ') 3986 c = get_copy(0); 3987 while (c != EOF && c != '\n' && c != ' ') { 3988 if (!invalid_input_char(c)) { 3989 if (reading_from_terminal) 3990 fputc(c, stderr); 3991 had_prompt = 1; 3992 } 3993 c = get_copy(0); 3994 } 3995 if (c == ' ') { 3996 tok.make_space(); 3997 decode_args(mi); 3998 } 3999 } 4000 if (reading_from_terminal) { 4001 fputc(had_prompt ? ':' : '\a', stderr); 4002 fflush(stderr); 4003 } 4004 input_stack::push(mi); 4005 macro mac; 4006 int nl = 0; 4007 int c; 4008 while ((c = getchar()) != EOF) { 4009 if (invalid_input_char(c)) 4010 warning(WARN_INPUT, "invalid input character code %1", int(c)); 4011 else { 4012 if (c == '\n') { 4013 if (nl) 4014 break; 4015 else 4016 nl = 1; 4017 } 4018 else 4019 nl = 0; 4020 mac.append(c); 4021 } 4022 } 4023 if (reading_from_terminal) 4024 clearerr(stdin); 4025 input_stack::push(new string_iterator(mac)); 4026 tok.next(); 4027} 4028 4029enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE }; 4030enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT }; 4031enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE }; 4032 4033void do_define_string(define_mode mode, comp_mode comp) 4034{ 4035 symbol nm; 4036 node *n = 0; // pacify compiler 4037 int c; 4038 nm = get_name(1); 4039 if (nm.is_null()) { 4040 skip_line(); 4041 return; 4042 } 4043 if (tok.newline()) 4044 c = '\n'; 4045 else if (tok.tab()) 4046 c = '\t'; 4047 else if (!tok.space()) { 4048 error("bad string definition"); 4049 skip_line(); 4050 return; 4051 } 4052 else 4053 c = get_copy(&n); 4054 while (c == ' ') 4055 c = get_copy(&n); 4056 if (c == '"') 4057 c = get_copy(&n); 4058 macro mac; 4059 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); 4060 macro *mm = rm ? rm->to_macro() : 0; 4061 if (mode == DEFINE_APPEND && mm) 4062 mac = *mm; 4063 if (comp == COMP_DISABLE) 4064 mac.append(PUSH_GROFF_MODE); 4065 else if (comp == COMP_ENABLE) 4066 mac.append(PUSH_COMP_MODE); 4067 while (c != '\n' && c != EOF) { 4068 if (c == 0) 4069 mac.append(n); 4070 else 4071 mac.append((unsigned char)c); 4072 c = get_copy(&n); 4073 } 4074 if (!mm) { 4075 mm = new macro; 4076 request_dictionary.define(nm, mm); 4077 } 4078 if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4079 mac.append(POP_GROFFCOMP_MODE); 4080 *mm = mac; 4081 tok.next(); 4082} 4083 4084void define_string() 4085{ 4086 do_define_string(DEFINE_NORMAL, 4087 compatible_flag ? COMP_ENABLE: COMP_IGNORE); 4088} 4089 4090void define_nocomp_string() 4091{ 4092 do_define_string(DEFINE_NORMAL, COMP_DISABLE); 4093} 4094 4095void append_string() 4096{ 4097 do_define_string(DEFINE_APPEND, 4098 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4099} 4100 4101void append_nocomp_string() 4102{ 4103 do_define_string(DEFINE_APPEND, COMP_DISABLE); 4104} 4105 4106void do_define_character(char_mode mode, const char *font_name) 4107{ 4108 node *n = 0; // pacify compiler 4109 int c; 4110 tok.skip(); 4111 charinfo *ci = tok.get_char(1); 4112 if (ci == 0) { 4113 skip_line(); 4114 return; 4115 } 4116 if (font_name) { 4117 string s(font_name); 4118 s += ' '; 4119 s += ci->nm.contents(); 4120 s += '\0'; 4121 ci = get_charinfo(symbol(s.contents())); 4122 } 4123 tok.next(); 4124 if (tok.newline()) 4125 c = '\n'; 4126 else if (tok.tab()) 4127 c = '\t'; 4128 else if (!tok.space()) { 4129 error("bad character definition"); 4130 skip_line(); 4131 return; 4132 } 4133 else 4134 c = get_copy(&n); 4135 while (c == ' ' || c == '\t') 4136 c = get_copy(&n); 4137 if (c == '"') 4138 c = get_copy(&n); 4139 macro *m = new macro; 4140 while (c != '\n' && c != EOF) { 4141 if (c == 0) 4142 m->append(n); 4143 else 4144 m->append((unsigned char)c); 4145 c = get_copy(&n); 4146 } 4147 m = ci->setx_macro(m, mode); 4148 if (m) 4149 delete m; 4150 tok.next(); 4151} 4152 4153void define_character() 4154{ 4155 do_define_character(CHAR_NORMAL); 4156} 4157 4158void define_fallback_character() 4159{ 4160 do_define_character(CHAR_FALLBACK); 4161} 4162 4163void define_special_character() 4164{ 4165 do_define_character(CHAR_SPECIAL); 4166} 4167 4168static void remove_character() 4169{ 4170 tok.skip(); 4171 while (!tok.newline() && !tok.eof()) { 4172 if (!tok.space() && !tok.tab()) { 4173 charinfo *ci = tok.get_char(1); 4174 if (!ci) 4175 break; 4176 macro *m = ci->set_macro(0); 4177 if (m) 4178 delete m; 4179 } 4180 tok.next(); 4181 } 4182 skip_line(); 4183} 4184 4185static void interpolate_string(symbol nm) 4186{ 4187 request_or_macro *p = lookup_request(nm); 4188 macro *m = p->to_macro(); 4189 if (!m) 4190 error("you can only invoke a string or macro using \\*"); 4191 else { 4192 string_iterator *si = new string_iterator(*m, "string", nm); 4193 input_stack::push(si); 4194 } 4195} 4196 4197static void interpolate_string_with_args(symbol s) 4198{ 4199 request_or_macro *p = lookup_request(s); 4200 macro *m = p->to_macro(); 4201 if (!m) 4202 error("you can only invoke a string or macro using \\*"); 4203 else { 4204 macro_iterator *mi = new macro_iterator(s, *m); 4205 decode_string_args(mi); 4206 input_stack::push(mi); 4207 } 4208} 4209 4210static void interpolate_arg(symbol nm) 4211{ 4212 const char *s = nm.contents(); 4213 if (!s || *s == '\0') 4214 copy_mode_error("missing argument name"); 4215 else if (s[1] == 0 && csdigit(s[0])) 4216 input_stack::push(input_stack::get_arg(s[0] - '0')); 4217 else if (s[0] == '*' && s[1] == '\0') { 4218 int limit = input_stack::nargs(); 4219 string args; 4220 for (int i = 1; i <= limit; i++) { 4221 input_iterator *p = input_stack::get_arg(i); 4222 int c; 4223 while ((c = p->get(0)) != EOF) 4224 args += c; 4225 if (i != limit) 4226 args += ' '; 4227 } 4228 if (limit > 0) { 4229 args += '\0'; 4230 input_stack::push(make_temp_iterator(args.contents())); 4231 } 4232 } 4233 else if (s[0] == '@' && s[1] == '\0') { 4234 int limit = input_stack::nargs(); 4235 string args; 4236 for (int i = 1; i <= limit; i++) { 4237 args += '"'; 4238 args += BEGIN_QUOTE; 4239 input_iterator *p = input_stack::get_arg(i); 4240 int c; 4241 while ((c = p->get(0)) != EOF) 4242 args += c; 4243 args += END_QUOTE; 4244 args += '"'; 4245 if (i != limit) 4246 args += ' '; 4247 } 4248 if (limit > 0) { 4249 args += '\0'; 4250 input_stack::push(make_temp_iterator(args.contents())); 4251 } 4252 } 4253 else { 4254 const char *p; 4255 for (p = s; *p && csdigit(*p); p++) 4256 ; 4257 if (*p) 4258 copy_mode_error("bad argument name `%1'", s); 4259 else 4260 input_stack::push(input_stack::get_arg(atoi(s))); 4261 } 4262} 4263 4264void handle_first_page_transition() 4265{ 4266 push_token(tok); 4267 topdiv->begin_page(); 4268} 4269 4270// We push back a token by wrapping it up in a token_node, and 4271// wrapping that up in a string_iterator. 4272 4273static void push_token(const token &t) 4274{ 4275 macro m; 4276 m.append(new token_node(t)); 4277 input_stack::push(new string_iterator(m)); 4278} 4279 4280void push_page_ejector() 4281{ 4282 static char buf[2] = { PAGE_EJECTOR, '\0' }; 4283 input_stack::push(make_temp_iterator(buf)); 4284} 4285 4286void handle_initial_request(unsigned char code) 4287{ 4288 char buf[2]; 4289 buf[0] = code; 4290 buf[1] = '\0'; 4291 macro mac; 4292 mac.append(new token_node(tok)); 4293 input_stack::push(new string_iterator(mac)); 4294 input_stack::push(make_temp_iterator(buf)); 4295 topdiv->begin_page(); 4296 tok.next(); 4297} 4298 4299void handle_initial_title() 4300{ 4301 handle_initial_request(TITLE_REQUEST); 4302} 4303 4304// this should be local to define_macro, but cfront 1.2 doesn't support that 4305static symbol dot_symbol("."); 4306 4307void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp) 4308{ 4309 symbol nm, term; 4310 if (calling == CALLING_INDIRECT) { 4311 symbol temp1 = get_name(1); 4312 if (temp1.is_null()) { 4313 skip_line(); 4314 return; 4315 } 4316 symbol temp2 = get_name(); 4317 input_stack::push(make_temp_iterator("\n")); 4318 if (!temp2.is_null()) { 4319 interpolate_string(temp2); 4320 input_stack::push(make_temp_iterator(" ")); 4321 } 4322 interpolate_string(temp1); 4323 input_stack::push(make_temp_iterator(" ")); 4324 tok.next(); 4325 } 4326 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4327 nm = get_name(1); 4328 if (nm.is_null()) { 4329 skip_line(); 4330 return; 4331 } 4332 } 4333 term = get_name(); // the request that terminates the definition 4334 if (term.is_null()) 4335 term = dot_symbol; 4336 while (!tok.newline() && !tok.eof()) 4337 tok.next(); 4338 const char *start_filename; 4339 int start_lineno; 4340 int have_start_location = input_stack::get_location(0, &start_filename, 4341 &start_lineno); 4342 node *n; 4343 // doing this here makes the line numbers come out right 4344 int c = get_copy(&n, 1); 4345 macro mac; 4346 macro *mm = 0; 4347 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4348 request_or_macro *rm = 4349 (request_or_macro *)request_dictionary.lookup(nm); 4350 if (rm) 4351 mm = rm->to_macro(); 4352 if (mm && mode == DEFINE_APPEND) 4353 mac = *mm; 4354 } 4355 int bol = 1; 4356 if (comp == COMP_DISABLE) 4357 mac.append(PUSH_GROFF_MODE); 4358 else if (comp == COMP_ENABLE) 4359 mac.append(PUSH_COMP_MODE); 4360 for (;;) { 4361 while (c == ESCAPE_NEWLINE) { 4362 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) 4363 mac.append(c); 4364 c = get_copy(&n, 1); 4365 } 4366 if (bol && c == '.') { 4367 const char *s = term.contents(); 4368 int d = 0; 4369 // see if it matches term 4370 int i = 0; 4371 if (s[0] != 0) { 4372 while ((d = get_copy(&n)) == ' ' || d == '\t') 4373 ; 4374 if ((unsigned char)s[0] == d) { 4375 for (i = 1; s[i] != 0; i++) { 4376 d = get_copy(&n); 4377 if ((unsigned char)s[i] != d) 4378 break; 4379 } 4380 } 4381 } 4382 if (s[i] == 0 4383 && ((i == 2 && compatible_flag) 4384 || (d = get_copy(&n)) == ' ' 4385 || d == '\n')) { // we found it 4386 if (d == '\n') 4387 tok.make_newline(); 4388 else 4389 tok.make_space(); 4390 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4391 if (!mm) { 4392 mm = new macro; 4393 request_dictionary.define(nm, mm); 4394 } 4395 if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4396 mac.append(POP_GROFFCOMP_MODE); 4397 *mm = mac; 4398 } 4399 if (term != dot_symbol) { 4400 ignoring = 0; 4401 interpolate_macro(term); 4402 } 4403 else 4404 skip_line(); 4405 return; 4406 } 4407 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4408 mac.append(c); 4409 for (int j = 0; j < i; j++) 4410 mac.append(s[j]); 4411 } 4412 c = d; 4413 } 4414 if (c == EOF) { 4415 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4416 if (have_start_location) 4417 error_with_file_and_line(start_filename, start_lineno, 4418 "end of file while defining macro `%1'", 4419 nm.contents()); 4420 else 4421 error("end of file while defining macro `%1'", nm.contents()); 4422 } 4423 else { 4424 if (have_start_location) 4425 error_with_file_and_line(start_filename, start_lineno, 4426 "end of file while ignoring input lines"); 4427 else 4428 error("end of file while ignoring input lines"); 4429 } 4430 tok.next(); 4431 return; 4432 } 4433 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4434 if (c == 0) 4435 mac.append(n); 4436 else 4437 mac.append(c); 4438 } 4439 bol = (c == '\n'); 4440 c = get_copy(&n, 1); 4441 } 4442} 4443 4444void define_macro() 4445{ 4446 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, 4447 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4448} 4449 4450void define_nocomp_macro() 4451{ 4452 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE); 4453} 4454 4455void define_indirect_macro() 4456{ 4457 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, 4458 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4459} 4460 4461void define_indirect_nocomp_macro() 4462{ 4463 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE); 4464} 4465 4466void append_macro() 4467{ 4468 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, 4469 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4470} 4471 4472void append_nocomp_macro() 4473{ 4474 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE); 4475} 4476 4477void append_indirect_macro() 4478{ 4479 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, 4480 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4481} 4482 4483void append_indirect_nocomp_macro() 4484{ 4485 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE); 4486} 4487 4488void ignore() 4489{ 4490 ignoring = 1; 4491 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE); 4492 ignoring = 0; 4493} 4494 4495void remove_macro() 4496{ 4497 for (;;) { 4498 symbol s = get_name(); 4499 if (s.is_null()) 4500 break; 4501 request_dictionary.remove(s); 4502 } 4503 skip_line(); 4504} 4505 4506void rename_macro() 4507{ 4508 symbol s1 = get_name(1); 4509 if (!s1.is_null()) { 4510 symbol s2 = get_name(1); 4511 if (!s2.is_null()) 4512 request_dictionary.rename(s1, s2); 4513 } 4514 skip_line(); 4515} 4516 4517void alias_macro() 4518{ 4519 symbol s1 = get_name(1); 4520 if (!s1.is_null()) { 4521 symbol s2 = get_name(1); 4522 if (!s2.is_null()) { 4523 if (!request_dictionary.alias(s1, s2)) 4524 warning(WARN_MAC, "macro `%1' not defined", s2.contents()); 4525 } 4526 } 4527 skip_line(); 4528} 4529 4530void chop_macro() 4531{ 4532 symbol s = get_name(1); 4533 if (!s.is_null()) { 4534 request_or_macro *p = lookup_request(s); 4535 macro *m = p->to_macro(); 4536 if (!m) 4537 error("cannot chop request"); 4538 else if (m->empty()) 4539 error("cannot chop empty macro"); 4540 else { 4541 int have_restore = 0; 4542 // we have to check for additional save/restore pairs which could be 4543 // there due to empty am1 requests. 4544 for (;;) { 4545 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE) 4546 break; 4547 have_restore = 1; 4548 m->len -= 1; 4549 if (m->get(m->len - 1) != PUSH_GROFF_MODE 4550 && m->get(m->len - 1) != PUSH_COMP_MODE) 4551 break; 4552 have_restore = 0; 4553 m->len -= 1; 4554 if (m->len == 0) 4555 break; 4556 } 4557 if (m->len == 0) 4558 error("cannot chop empty macro"); 4559 else { 4560 if (have_restore) 4561 m->set(POP_GROFFCOMP_MODE, m->len - 1); 4562 else 4563 m->len -= 1; 4564 } 4565 } 4566 } 4567 skip_line(); 4568} 4569 4570void substring_request() 4571{ 4572 int start; // 0, 1, ..., n-1 or -1, -2, ... 4573 symbol s = get_name(1); 4574 if (!s.is_null() && get_integer(&start)) { 4575 request_or_macro *p = lookup_request(s); 4576 macro *m = p->to_macro(); 4577 if (!m) 4578 error("cannot apply `substring' on a request"); 4579 else { 4580 int end = -1; 4581 if (!has_arg() || get_integer(&end)) { 4582 int real_length = 0; // 1, 2, ..., n 4583 string_iterator iter1(*m); 4584 for (int l = 0; l < m->len; l++) { 4585 int c = iter1.get(0); 4586 if (c == PUSH_GROFF_MODE 4587 || c == PUSH_COMP_MODE 4588 || c == POP_GROFFCOMP_MODE) 4589 continue; 4590 if (c == EOF) 4591 break; 4592 real_length++; 4593 } 4594 if (start < 0) 4595 start += real_length; 4596 if (end < 0) 4597 end += real_length; 4598 if (start > end) { 4599 int tem = start; 4600 start = end; 4601 end = tem; 4602 } 4603 if (start >= real_length || end < 0) { 4604 warning(WARN_RANGE, 4605 "start and end index of substring out of range"); 4606 m->len = 0; 4607 if (m->p) { 4608 if (--(m->p->count) <= 0) 4609 delete m->p; 4610 m->p = 0; 4611 } 4612 skip_line(); 4613 return; 4614 } 4615 if (start < 0) { 4616 warning(WARN_RANGE, 4617 "start index of substring out of range, set to 0"); 4618 start = 0; 4619 } 4620 if (end >= real_length) { 4621 warning(WARN_RANGE, 4622 "end index of substring out of range, set to string length"); 4623 end = real_length - 1; 4624 } 4625 // now extract the substring 4626 string_iterator iter(*m); 4627 int i; 4628 for (i = 0; i < start; i++) { 4629 int c = iter.get(0); 4630 while (c == PUSH_GROFF_MODE 4631 || c == PUSH_COMP_MODE 4632 || c == POP_GROFFCOMP_MODE) 4633 c = iter.get(0); 4634 if (c == EOF) 4635 break; 4636 } 4637 macro mac; 4638 for (; i <= end; i++) { 4639 node *nd = 0; // pacify compiler 4640 int c = iter.get(&nd); 4641 while (c == PUSH_GROFF_MODE 4642 || c == PUSH_COMP_MODE 4643 || c == POP_GROFFCOMP_MODE) 4644 c = iter.get(0); 4645 if (c == EOF) 4646 break; 4647 if (c == 0) 4648 mac.append(nd); 4649 else 4650 mac.append((unsigned char)c); 4651 } 4652 *m = mac; 4653 } 4654 } 4655 } 4656 skip_line(); 4657} 4658 4659void length_request() 4660{ 4661 symbol ret; 4662 ret = get_name(1); 4663 if (ret.is_null()) { 4664 skip_line(); 4665 return; 4666 } 4667 int c; 4668 node *n; 4669 if (tok.newline()) 4670 c = '\n'; 4671 else if (tok.tab()) 4672 c = '\t'; 4673 else if (!tok.space()) { 4674 error("bad string definition"); 4675 skip_line(); 4676 return; 4677 } 4678 else 4679 c = get_copy(&n); 4680 while (c == ' ') 4681 c = get_copy(&n); 4682 if (c == '"') 4683 c = get_copy(&n); 4684 int len = 0; 4685 while (c != '\n' && c != EOF) { 4686 ++len; 4687 c = get_copy(&n); 4688 } 4689 reg *r = (reg*)number_reg_dictionary.lookup(ret); 4690 if (r) 4691 r->set_value(len); 4692 else 4693 set_number_reg(ret, len); 4694 tok.next(); 4695} 4696 4697void asciify_macro() 4698{ 4699 symbol s = get_name(1); 4700 if (!s.is_null()) { 4701 request_or_macro *p = lookup_request(s); 4702 macro *m = p->to_macro(); 4703 if (!m) 4704 error("cannot asciify request"); 4705 else { 4706 macro am; 4707 string_iterator iter(*m); 4708 for (;;) { 4709 node *nd = 0; // pacify compiler 4710 int c = iter.get(&nd); 4711 if (c == EOF) 4712 break; 4713 if (c != 0) 4714 am.append(c); 4715 else 4716 nd->asciify(&am); 4717 } 4718 *m = am; 4719 } 4720 } 4721 skip_line(); 4722} 4723 4724void unformat_macro() 4725{ 4726 symbol s = get_name(1); 4727 if (!s.is_null()) { 4728 request_or_macro *p = lookup_request(s); 4729 macro *m = p->to_macro(); 4730 if (!m) 4731 error("cannot unformat request"); 4732 else { 4733 macro am; 4734 string_iterator iter(*m); 4735 for (;;) { 4736 node *nd = 0; // pacify compiler 4737 int c = iter.get(&nd); 4738 if (c == EOF) 4739 break; 4740 if (c != 0) 4741 am.append(c); 4742 else { 4743 if (nd->set_unformat_flag()) 4744 am.append(nd); 4745 } 4746 } 4747 *m = am; 4748 } 4749 } 4750 skip_line(); 4751} 4752 4753static void interpolate_environment_variable(symbol nm) 4754{ 4755 const char *s = getenv(nm.contents()); 4756 if (s && *s) 4757 input_stack::push(make_temp_iterator(s)); 4758} 4759 4760void interpolate_number_reg(symbol nm, int inc) 4761{ 4762 reg *r = lookup_number_reg(nm); 4763 if (inc < 0) 4764 r->decrement(); 4765 else if (inc > 0) 4766 r->increment(); 4767 input_stack::push(make_temp_iterator(r->get_string())); 4768} 4769 4770static void interpolate_number_format(symbol nm) 4771{ 4772 reg *r = (reg *)number_reg_dictionary.lookup(nm); 4773 if (r) 4774 input_stack::push(make_temp_iterator(r->get_format())); 4775} 4776 4777static int get_delim_number(units *n, unsigned char si, int prev_value) 4778{ 4779 token start; 4780 start.next(); 4781 if (start.delimiter(1)) { 4782 tok.next(); 4783 if (get_number(n, si, prev_value)) { 4784 if (start != tok) 4785 warning(WARN_DELIM, "closing delimiter does not match"); 4786 return 1; 4787 } 4788 } 4789 return 0; 4790} 4791 4792static int get_delim_number(units *n, unsigned char si) 4793{ 4794 token start; 4795 start.next(); 4796 if (start.delimiter(1)) { 4797 tok.next(); 4798 if (get_number(n, si)) { 4799 if (start != tok) 4800 warning(WARN_DELIM, "closing delimiter does not match"); 4801 return 1; 4802 } 4803 } 4804 return 0; 4805} 4806 4807static int get_line_arg(units *n, unsigned char si, charinfo **cp) 4808{ 4809 token start; 4810 start.next(); 4811 int start_level = input_stack::get_level(); 4812 if (!start.delimiter(1)) 4813 return 0; 4814 tok.next(); 4815 if (get_number(n, si)) { 4816 if (tok.dummy() || tok.transparent_dummy()) 4817 tok.next(); 4818 if (!(start == tok && input_stack::get_level() == start_level)) { 4819 *cp = tok.get_char(1); 4820 tok.next(); 4821 } 4822 if (!(start == tok && input_stack::get_level() == start_level)) 4823 warning(WARN_DELIM, "closing delimiter does not match"); 4824 return 1; 4825 } 4826 return 0; 4827} 4828 4829static int read_size(int *x) 4830{ 4831 tok.next(); 4832 int c = tok.ch(); 4833 int inc = 0; 4834 if (c == '-') { 4835 inc = -1; 4836 tok.next(); 4837 c = tok.ch(); 4838 } 4839 else if (c == '+') { 4840 inc = 1; 4841 tok.next(); 4842 c = tok.ch(); 4843 } 4844 int val = 0; // pacify compiler 4845 int bad = 0; 4846 if (c == '(') { 4847 tok.next(); 4848 c = tok.ch(); 4849 if (!inc) { 4850 // allow an increment either before or after the left parenthesis 4851 if (c == '-') { 4852 inc = -1; 4853 tok.next(); 4854 c = tok.ch(); 4855 } 4856 else if (c == '+') { 4857 inc = 1; 4858 tok.next(); 4859 c = tok.ch(); 4860 } 4861 } 4862 if (!csdigit(c)) 4863 bad = 1; 4864 else { 4865 val = c - '0'; 4866 tok.next(); 4867 c = tok.ch(); 4868 if (!csdigit(c)) 4869 bad = 1; 4870 else { 4871 val = val*10 + (c - '0'); 4872 val *= sizescale; 4873 } 4874 } 4875 } 4876 else if (csdigit(c)) { 4877 val = c - '0'; 4878 if (!inc && c != '0' && c < '4') { 4879 tok.next(); 4880 c = tok.ch(); 4881 if (!csdigit(c)) 4882 bad = 1; 4883 else 4884 val = val*10 + (c - '0'); 4885 } 4886 val *= sizescale; 4887 } 4888 else if (!tok.delimiter(1)) 4889 return 0; 4890 else { 4891 token start(tok); 4892 tok.next(); 4893 if (!(inc 4894 ? get_number(&val, 'z') 4895 : get_number(&val, 'z', curenv->get_requested_point_size()))) 4896 return 0; 4897 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) { 4898 if (start.ch() == '[') 4899 error("missing `]'"); 4900 else 4901 error("missing closing delimiter"); 4902 return 0; 4903 } 4904 } 4905 if (!bad) { 4906 switch (inc) { 4907 case 0: 4908 if (val == 0) { 4909 // special case -- \s[0] and \s0 means to revert to previous size 4910 *x = 0; 4911 return 1; 4912 } 4913 *x = val; 4914 break; 4915 case 1: 4916 *x = curenv->get_requested_point_size() + val; 4917 break; 4918 case -1: 4919 *x = curenv->get_requested_point_size() - val; 4920 break; 4921 default: 4922 assert(0); 4923 } 4924 if (*x <= 0) { 4925 warning(WARN_RANGE, 4926 "\\s request results in non-positive point size; set to 1"); 4927 *x = 1; 4928 } 4929 return 1; 4930 } 4931 else { 4932 error("bad digit in point size"); 4933 return 0; 4934 } 4935} 4936 4937static symbol get_delim_name() 4938{ 4939 token start; 4940 start.next(); 4941 if (start.eof()) { 4942 error("end of input at start of delimited name"); 4943 return NULL_SYMBOL; 4944 } 4945 if (start.newline()) { 4946 error("can't delimit name with a newline"); 4947 return NULL_SYMBOL; 4948 } 4949 int start_level = input_stack::get_level(); 4950 char abuf[ABUF_SIZE]; 4951 char *buf = abuf; 4952 int buf_size = ABUF_SIZE; 4953 int i = 0; 4954 for (;;) { 4955 if (i + 1 > buf_size) { 4956 if (buf == abuf) { 4957 buf = new char[ABUF_SIZE*2]; 4958 memcpy(buf, abuf, buf_size); 4959 buf_size = ABUF_SIZE*2; 4960 } 4961 else { 4962 char *old_buf = buf; 4963 buf = new char[buf_size*2]; 4964 memcpy(buf, old_buf, buf_size); 4965 buf_size *= 2; 4966 a_delete old_buf; 4967 } 4968 } 4969 tok.next(); 4970 if (tok == start 4971 && (compatible_flag || input_stack::get_level() == start_level)) 4972 break; 4973 if ((buf[i] = tok.ch()) == 0) { 4974 error("missing delimiter (got %1)", tok.description()); 4975 if (buf != abuf) 4976 a_delete buf; 4977 return NULL_SYMBOL; 4978 } 4979 i++; 4980 } 4981 buf[i] = '\0'; 4982 if (buf == abuf) { 4983 if (i == 0) { 4984 error("empty delimited name"); 4985 return NULL_SYMBOL; 4986 } 4987 else 4988 return symbol(buf); 4989 } 4990 else { 4991 symbol s(buf); 4992 a_delete buf; 4993 return s; 4994 } 4995} 4996 4997// Implement \R 4998 4999static void do_register() 5000{ 5001 token start; 5002 start.next(); 5003 if (!start.delimiter(1)) 5004 return; 5005 tok.next(); 5006 symbol nm = get_long_name(1); 5007 if (nm.is_null()) 5008 return; 5009 while (tok.space()) 5010 tok.next(); 5011 reg *r = (reg *)number_reg_dictionary.lookup(nm); 5012 int prev_value; 5013 if (!r || !r->get_value(&prev_value)) 5014 prev_value = 0; 5015 int val; 5016 if (!get_number(&val, 'u', prev_value)) 5017 return; 5018 if (start != tok) 5019 warning(WARN_DELIM, "closing delimiter does not match"); 5020 if (r) 5021 r->set_value(val); 5022 else 5023 set_number_reg(nm, val); 5024} 5025 5026// this implements the \w escape sequence 5027 5028static void do_width() 5029{ 5030 token start; 5031 start.next(); 5032 int start_level = input_stack::get_level(); 5033 environment env(curenv); 5034 environment *oldenv = curenv; 5035 curenv = &env; 5036 for (;;) { 5037 tok.next(); 5038 if (tok.eof()) { 5039 warning(WARN_DELIM, "missing closing delimiter"); 5040 break; 5041 } 5042 if (tok.newline()) { 5043 warning(WARN_DELIM, "missing closing delimiter"); 5044 input_stack::push(make_temp_iterator("\n")); 5045 break; 5046 } 5047 if (tok == start 5048 && (compatible_flag || input_stack::get_level() == start_level)) 5049 break; 5050 tok.process(); 5051 } 5052 env.wrap_up_tab(); 5053 units x = env.get_input_line_position().to_units(); 5054 input_stack::push(make_temp_iterator(i_to_a(x))); 5055 env.width_registers(); 5056 curenv = oldenv; 5057 have_input = 0; 5058} 5059 5060charinfo *page_character; 5061 5062void set_page_character() 5063{ 5064 page_character = get_optional_char(); 5065 skip_line(); 5066} 5067 5068static const symbol percent_symbol("%"); 5069 5070void read_title_parts(node **part, hunits *part_width) 5071{ 5072 tok.skip(); 5073 if (tok.newline() || tok.eof()) 5074 return; 5075 token start(tok); 5076 int start_level = input_stack::get_level(); 5077 tok.next(); 5078 for (int i = 0; i < 3; i++) { 5079 while (!tok.newline() && !tok.eof()) { 5080 if (tok == start 5081 && (compatible_flag || input_stack::get_level() == start_level)) { 5082 tok.next(); 5083 break; 5084 } 5085 if (page_character != 0 && tok.get_char() == page_character) 5086 interpolate_number_reg(percent_symbol, 0); 5087 else 5088 tok.process(); 5089 tok.next(); 5090 } 5091 curenv->wrap_up_tab(); 5092 part_width[i] = curenv->get_input_line_position(); 5093 part[i] = curenv->extract_output_line(); 5094 } 5095 while (!tok.newline() && !tok.eof()) 5096 tok.next(); 5097} 5098 5099class non_interpreted_node : public node { 5100 macro mac; 5101public: 5102 non_interpreted_node(const macro &); 5103 int interpret(macro *); 5104 node *copy(); 5105 int ends_sentence(); 5106 int same(node *); 5107 const char *type(); 5108 int force_tprint(); 5109 int is_tag(); 5110}; 5111 5112non_interpreted_node::non_interpreted_node(const macro &m) : mac(m) 5113{ 5114} 5115 5116int non_interpreted_node::ends_sentence() 5117{ 5118 return 2; 5119} 5120 5121int non_interpreted_node::same(node *nd) 5122{ 5123 return mac == ((non_interpreted_node *)nd)->mac; 5124} 5125 5126const char *non_interpreted_node::type() 5127{ 5128 return "non_interpreted_node"; 5129} 5130 5131int non_interpreted_node::force_tprint() 5132{ 5133 return 0; 5134} 5135 5136int non_interpreted_node::is_tag() 5137{ 5138 return 0; 5139} 5140 5141node *non_interpreted_node::copy() 5142{ 5143 return new non_interpreted_node(mac); 5144} 5145 5146int non_interpreted_node::interpret(macro *m) 5147{ 5148 string_iterator si(mac); 5149 node *n = 0; // pacify compiler 5150 for (;;) { 5151 int c = si.get(&n); 5152 if (c == EOF) 5153 break; 5154 if (c == 0) 5155 m->append(n); 5156 else 5157 m->append(c); 5158 } 5159 return 1; 5160} 5161 5162static node *do_non_interpreted() 5163{ 5164 node *n; 5165 int c; 5166 macro mac; 5167 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n') 5168 if (c == 0) 5169 mac.append(n); 5170 else 5171 mac.append(c); 5172 if (c == EOF || c == '\n') { 5173 error("missing \\?"); 5174 return 0; 5175 } 5176 return new non_interpreted_node(mac); 5177} 5178 5179static void encode_char(macro *mac, char c) 5180{ 5181 if (c == '\0') { 5182 if ((font::use_charnames_in_special) && tok.special()) { 5183 charinfo *ci = tok.get_char(1); 5184 const char *s = ci->get_symbol()->contents(); 5185 if (s[0] != (char)0) { 5186 mac->append('\\'); 5187 mac->append('('); 5188 int i = 0; 5189 while (s[i] != (char)0) { 5190 mac->append(s[i]); 5191 i++; 5192 } 5193 mac->append('\\'); 5194 mac->append(')'); 5195 } 5196 } 5197 else if (tok.stretchable_space() 5198 || tok.unstretchable_space()) 5199 mac->append(' '); 5200 else if (!(tok.hyphen_indicator() 5201 || tok.dummy() 5202 || tok.transparent_dummy() 5203 || tok.zero_width_break())) 5204 error("%1 is invalid within \\X", tok.description()); 5205 } 5206 else { 5207 if ((font::use_charnames_in_special) && (c == '\\')) { 5208 /* 5209 * add escape escape sequence 5210 */ 5211 mac->append(c); 5212 } 5213 mac->append(c); 5214 } 5215} 5216 5217node *do_special() 5218{ 5219 token start; 5220 start.next(); 5221 int start_level = input_stack::get_level(); 5222 macro mac; 5223 for (tok.next(); 5224 tok != start || input_stack::get_level() != start_level; 5225 tok.next()) { 5226 if (tok.eof()) { 5227 warning(WARN_DELIM, "missing closing delimiter"); 5228 return 0; 5229 } 5230 if (tok.newline()) { 5231 input_stack::push(make_temp_iterator("\n")); 5232 warning(WARN_DELIM, "missing closing delimiter"); 5233 break; 5234 } 5235 unsigned char c; 5236 if (tok.space()) 5237 c = ' '; 5238 else if (tok.tab()) 5239 c = '\t'; 5240 else if (tok.leader()) 5241 c = '\001'; 5242 else if (tok.backspace()) 5243 c = '\b'; 5244 else 5245 c = tok.ch(); 5246 encode_char(&mac, c); 5247 } 5248 return new special_node(mac); 5249} 5250 5251void output_request() 5252{ 5253 if (!tok.newline() && !tok.eof()) { 5254 int c; 5255 for (;;) { 5256 c = get_copy(0); 5257 if (c == '"') { 5258 c = get_copy(0); 5259 break; 5260 } 5261 if (c != ' ' && c != '\t') 5262 break; 5263 } 5264 for (; c != '\n' && c != EOF; c = get_copy(0)) 5265 topdiv->transparent_output(c); 5266 topdiv->transparent_output('\n'); 5267 } 5268 tok.next(); 5269} 5270 5271extern int image_no; // from node.cpp 5272 5273static node *do_suppress(symbol nm) 5274{ 5275 if (nm.is_null() || nm.is_empty()) { 5276 error("expecting an argument to escape \\O"); 5277 return 0; 5278 } 5279 const char *s = nm.contents(); 5280 switch (*s) { 5281 case '0': 5282 if (begin_level == 0) 5283 // suppress generation of glyphs 5284 return new suppress_node(0, 0); 5285 break; 5286 case '1': 5287 if (begin_level == 0) 5288 // enable generation of glyphs 5289 return new suppress_node(1, 0); 5290 break; 5291 case '2': 5292 if (begin_level == 0) 5293 return new suppress_node(1, 1); 5294 break; 5295 case '3': 5296 begin_level++; 5297 break; 5298 case '4': 5299 begin_level--; 5300 break; 5301 case '5': 5302 { 5303 s++; // move over '5' 5304 char position = *s; 5305 if (*s == (char)0) { 5306 error("missing position and filename in \\O"); 5307 return 0; 5308 } 5309 if (!(position == 'l' 5310 || position == 'r' 5311 || position == 'c' 5312 || position == 'i')) { 5313 error("l, r, c, or i position expected (got %1 in \\O)", position); 5314 return 0; 5315 } 5316 s++; // onto image name 5317 if (s == (char *)0) { 5318 error("missing image name for \\O"); 5319 return 0; 5320 } 5321 image_no++; 5322 if (begin_level == 0) 5323 return new suppress_node(symbol(s), position, image_no); 5324 } 5325 break; 5326 default: 5327 error("`%1' is an invalid argument to \\O", *s); 5328 } 5329 return 0; 5330} 5331 5332void special_node::tprint(troff_output_file *out) 5333{ 5334 tprint_start(out); 5335 string_iterator iter(mac); 5336 for (;;) { 5337 int c = iter.get(0); 5338 if (c == EOF) 5339 break; 5340 for (const char *s = ::asciify(c); *s; s++) 5341 tprint_char(out, *s); 5342 } 5343 tprint_end(out); 5344} 5345 5346int get_file_line(const char **filename, int *lineno) 5347{ 5348 return input_stack::get_location(0, filename, lineno); 5349} 5350 5351void line_file() 5352{ 5353 int n; 5354 if (get_integer(&n)) { 5355 const char *filename = 0; 5356 if (has_arg()) { 5357 symbol s = get_long_name(); 5358 filename = s.contents(); 5359 } 5360 (void)input_stack::set_location(filename, n-1); 5361 } 5362 skip_line(); 5363} 5364 5365static int nroff_mode = 0; 5366 5367static void nroff_request() 5368{ 5369 nroff_mode = 1; 5370 skip_line(); 5371} 5372 5373static void troff_request() 5374{ 5375 nroff_mode = 0; 5376 skip_line(); 5377} 5378 5379static void skip_alternative() 5380{ 5381 int level = 0; 5382 // ensure that ``.if 0\{'' works as expected 5383 if (tok.left_brace()) 5384 level++; 5385 int c; 5386 for (;;) { 5387 c = input_stack::get(0); 5388 if (c == EOF) 5389 break; 5390 if (c == ESCAPE_LEFT_BRACE) 5391 ++level; 5392 else if (c == ESCAPE_RIGHT_BRACE) 5393 --level; 5394 else if (c == escape_char && escape_char > 0) 5395 switch(input_stack::get(0)) { 5396 case '{': 5397 ++level; 5398 break; 5399 case '}': 5400 --level; 5401 break; 5402 case '"': 5403 while ((c = input_stack::get(0)) != '\n' && c != EOF) 5404 ; 5405 } 5406 /* 5407 Note that the level can properly be < 0, eg 5408 5409 .if 1 \{\ 5410 .if 0 \{\ 5411 .\}\} 5412 5413 So don't give an error message in this case. 5414 */ 5415 if (level <= 0 && c == '\n') 5416 break; 5417 } 5418 tok.next(); 5419} 5420 5421static void begin_alternative() 5422{ 5423 while (tok.space() || tok.left_brace()) 5424 tok.next(); 5425} 5426 5427void nop_request() 5428{ 5429 while (tok.space()) 5430 tok.next(); 5431} 5432 5433static int_stack if_else_stack; 5434 5435int do_if_request() 5436{ 5437 int invert = 0; 5438 while (tok.space()) 5439 tok.next(); 5440 while (tok.ch() == '!') { 5441 tok.next(); 5442 invert = !invert; 5443 } 5444 int result; 5445 unsigned char c = tok.ch(); 5446 if (c == 't') { 5447 tok.next(); 5448 result = !nroff_mode; 5449 } 5450 else if (c == 'n') { 5451 tok.next(); 5452 result = nroff_mode; 5453 } 5454 else if (c == 'v') { 5455 tok.next(); 5456 result = 0; 5457 } 5458 else if (c == 'o') { 5459 result = (topdiv->get_page_number() & 1); 5460 tok.next(); 5461 } 5462 else if (c == 'e') { 5463 result = !(topdiv->get_page_number() & 1); 5464 tok.next(); 5465 } 5466 else if (c == 'd' || c == 'r') { 5467 tok.next(); 5468 symbol nm = get_name(1); 5469 if (nm.is_null()) { 5470 skip_alternative(); 5471 return 0; 5472 } 5473 result = (c == 'd' 5474 ? request_dictionary.lookup(nm) != 0 5475 : number_reg_dictionary.lookup(nm) != 0); 5476 } 5477 else if (c == 'm') { 5478 tok.next(); 5479 symbol nm = get_long_name(1); 5480 if (nm.is_null()) { 5481 skip_alternative(); 5482 return 0; 5483 } 5484 result = (nm == default_symbol 5485 || color_dictionary.lookup(nm) != 0); 5486 } 5487 else if (c == 'c') { 5488 tok.next(); 5489 tok.skip(); 5490 charinfo *ci = tok.get_char(1); 5491 if (ci == 0) { 5492 skip_alternative(); 5493 return 0; 5494 } 5495 result = character_exists(ci, curenv); 5496 tok.next(); 5497 } 5498 else if (c == 'F') { 5499 tok.next(); 5500 symbol nm = get_long_name(1); 5501 if (nm.is_null()) { 5502 skip_alternative(); 5503 return 0; 5504 } 5505 result = check_font(curenv->get_family()->nm, nm); 5506 } 5507 else if (c == 'S') { 5508 tok.next(); 5509 symbol nm = get_long_name(1); 5510 if (nm.is_null()) { 5511 skip_alternative(); 5512 return 0; 5513 } 5514 result = check_style(nm); 5515 } 5516 else if (tok.space()) 5517 result = 0; 5518 else if (tok.delimiter()) { 5519 token delim = tok; 5520 int delim_level = input_stack::get_level(); 5521 environment env1(curenv); 5522 environment env2(curenv); 5523 environment *oldenv = curenv; 5524 curenv = &env1; 5525 suppress_push = 1; 5526 for (int i = 0; i < 2; i++) { 5527 for (;;) { 5528 tok.next(); 5529 if (tok.newline() || tok.eof()) { 5530 warning(WARN_DELIM, "missing closing delimiter"); 5531 tok.next(); 5532 curenv = oldenv; 5533 return 0; 5534 } 5535 if (tok == delim 5536 && (compatible_flag || input_stack::get_level() == delim_level)) 5537 break; 5538 tok.process(); 5539 } 5540 curenv = &env2; 5541 } 5542 node *n1 = env1.extract_output_line(); 5543 node *n2 = env2.extract_output_line(); 5544 result = same_node_list(n1, n2); 5545 delete_node_list(n1); 5546 delete_node_list(n2); 5547 curenv = oldenv; 5548 have_input = 0; 5549 suppress_push = 0; 5550 tok.next(); 5551 } 5552 else { 5553 units n; 5554 if (!get_number(&n, 'u')) { 5555 skip_alternative(); 5556 return 0; 5557 } 5558 else 5559 result = n > 0; 5560 } 5561 if (invert) 5562 result = !result; 5563 if (result) 5564 begin_alternative(); 5565 else 5566 skip_alternative(); 5567 return result; 5568} 5569 5570void if_else_request() 5571{ 5572 if_else_stack.push(do_if_request()); 5573} 5574 5575void if_request() 5576{ 5577 do_if_request(); 5578} 5579 5580void else_request() 5581{ 5582 if (if_else_stack.is_empty()) { 5583 warning(WARN_EL, "unbalanced .el request"); 5584 skip_alternative(); 5585 } 5586 else { 5587 if (if_else_stack.pop()) 5588 skip_alternative(); 5589 else 5590 begin_alternative(); 5591 } 5592} 5593 5594static int while_depth = 0; 5595static int while_break_flag = 0; 5596 5597void while_request() 5598{ 5599 macro mac; 5600 int escaped = 0; 5601 int level = 0; 5602 mac.append(new token_node(tok)); 5603 for (;;) { 5604 node *n = 0; // pacify compiler 5605 int c = input_stack::get(&n); 5606 if (c == EOF) 5607 break; 5608 if (c == 0) { 5609 escaped = 0; 5610 mac.append(n); 5611 } 5612 else if (escaped) { 5613 if (c == '{') 5614 level += 1; 5615 else if (c == '}') 5616 level -= 1; 5617 escaped = 0; 5618 mac.append(c); 5619 } 5620 else { 5621 if (c == ESCAPE_LEFT_BRACE) 5622 level += 1; 5623 else if (c == ESCAPE_RIGHT_BRACE) 5624 level -= 1; 5625 else if (c == escape_char) 5626 escaped = 1; 5627 mac.append(c); 5628 if (c == '\n' && level <= 0) 5629 break; 5630 } 5631 } 5632 if (level != 0) 5633 error("unbalanced \\{ \\}"); 5634 else { 5635 while_depth++; 5636 input_stack::add_boundary(); 5637 for (;;) { 5638 input_stack::push(new string_iterator(mac, "while loop")); 5639 tok.next(); 5640 if (!do_if_request()) { 5641 while (input_stack::get(0) != EOF) 5642 ; 5643 break; 5644 } 5645 process_input_stack(); 5646 if (while_break_flag || input_stack::is_return_boundary()) { 5647 while_break_flag = 0; 5648 break; 5649 } 5650 } 5651 input_stack::remove_boundary(); 5652 while_depth--; 5653 } 5654 tok.next(); 5655} 5656 5657void while_break_request() 5658{ 5659 if (!while_depth) { 5660 error("no while loop"); 5661 skip_line(); 5662 } 5663 else { 5664 while_break_flag = 1; 5665 while (input_stack::get(0) != EOF) 5666 ; 5667 tok.next(); 5668 } 5669} 5670 5671void while_continue_request() 5672{ 5673 if (!while_depth) { 5674 error("no while loop"); 5675 skip_line(); 5676 } 5677 else { 5678 while (input_stack::get(0) != EOF) 5679 ; 5680 tok.next(); 5681 } 5682} 5683 5684// .so 5685 5686void source() 5687{ 5688 symbol nm = get_long_name(1); 5689 if (nm.is_null()) 5690 skip_line(); 5691 else { 5692 while (!tok.newline() && !tok.eof()) 5693 tok.next(); 5694 errno = 0; 5695 FILE *fp = include_search_path.open_file_cautious(nm.contents()); 5696 if (fp) 5697 input_stack::push(new file_iterator(fp, nm.contents())); 5698 else 5699 error("can't open `%1': %2", nm.contents(), strerror(errno)); 5700 tok.next(); 5701 } 5702} 5703 5704// like .so but use popen() 5705 5706void pipe_source() 5707{ 5708 if (safer_flag) { 5709 error(".pso request not allowed in safer mode"); 5710 skip_line(); 5711 } 5712 else { 5713#ifdef POPEN_MISSING 5714 error("pipes not available on this system"); 5715 skip_line(); 5716#else /* not POPEN_MISSING */ 5717 if (tok.newline() || tok.eof()) 5718 error("missing command"); 5719 else { 5720 int c; 5721 while ((c = get_copy(0)) == ' ' || c == '\t') 5722 ; 5723 int buf_size = 24; 5724 char *buf = new char[buf_size]; 5725 int buf_used = 0; 5726 for (; c != '\n' && c != EOF; c = get_copy(0)) { 5727 const char *s = asciify(c); 5728 int slen = strlen(s); 5729 if (buf_used + slen + 1> buf_size) { 5730 char *old_buf = buf; 5731 int old_buf_size = buf_size; 5732 buf_size *= 2; 5733 buf = new char[buf_size]; 5734 memcpy(buf, old_buf, old_buf_size); 5735 a_delete old_buf; 5736 } 5737 strcpy(buf + buf_used, s); 5738 buf_used += slen; 5739 } 5740 buf[buf_used] = '\0'; 5741 errno = 0; 5742 FILE *fp = popen(buf, POPEN_RT); 5743 if (fp) 5744 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1)); 5745 else 5746 error("can't open pipe to process `%1': %2", buf, strerror(errno)); 5747 a_delete buf; 5748 } 5749 tok.next(); 5750#endif /* not POPEN_MISSING */ 5751 } 5752} 5753 5754// .psbb 5755 5756static int llx_reg_contents = 0; 5757static int lly_reg_contents = 0; 5758static int urx_reg_contents = 0; 5759static int ury_reg_contents = 0; 5760 5761struct bounding_box { 5762 int llx, lly, urx, ury; 5763}; 5764 5765/* Parse the argument to a %%BoundingBox comment. Return 1 if it 5766contains 4 numbers, 2 if it contains (atend), 0 otherwise. */ 5767 5768int parse_bounding_box(char *p, bounding_box *bb) 5769{ 5770 if (sscanf(p, "%d %d %d %d", 5771 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4) 5772 return 1; 5773 else { 5774 /* The Document Structuring Conventions say that the numbers 5775 should be integers. Unfortunately some broken applications 5776 get this wrong. */ 5777 double x1, x2, x3, x4; 5778 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) { 5779 bb->llx = (int)x1; 5780 bb->lly = (int)x2; 5781 bb->urx = (int)x3; 5782 bb->ury = (int)x4; 5783 return 1; 5784 } 5785 else { 5786 for (; *p == ' ' || *p == '\t'; p++) 5787 ; 5788 if (strncmp(p, "(atend)", 7) == 0) { 5789 return 2; 5790 } 5791 } 5792 } 5793 bb->llx = bb->lly = bb->urx = bb->ury = 0; 5794 return 0; 5795} 5796 5797// This version is taken from psrm.cpp 5798 5799#define PS_LINE_MAX 255 5800cset white_space("\n\r \t"); 5801 5802int ps_get_line(char *buf, FILE *fp, const char* filename) 5803{ 5804 int c = getc(fp); 5805 if (c == EOF) { 5806 buf[0] = '\0'; 5807 return 0; 5808 } 5809 int i = 0; 5810 int err = 0; 5811 while (c != '\r' && c != '\n' && c != EOF) { 5812 if ((c < 0x1b && !white_space(c)) || c == 0x7f) 5813 error("invalid input character code %1 in `%2'", int(c), filename); 5814 else if (i < PS_LINE_MAX) 5815 buf[i++] = c; 5816 else if (!err) { 5817 err = 1; 5818 error("PostScript file `%1' is non-conforming " 5819 "because length of line exceeds 255", filename); 5820 } 5821 c = getc(fp); 5822 } 5823 buf[i++] = '\n'; 5824 buf[i] = '\0'; 5825 if (c == '\r') { 5826 c = getc(fp); 5827 if (c != EOF && c != '\n') 5828 ungetc(c, fp); 5829 } 5830 return 1; 5831} 5832 5833inline void assign_registers(int llx, int lly, int urx, int ury) 5834{ 5835 llx_reg_contents = llx; 5836 lly_reg_contents = lly; 5837 urx_reg_contents = urx; 5838 ury_reg_contents = ury; 5839} 5840 5841void do_ps_file(FILE *fp, const char* filename) 5842{ 5843 bounding_box bb; 5844 int bb_at_end = 0; 5845 char buf[PS_LINE_MAX]; 5846 llx_reg_contents = lly_reg_contents = 5847 urx_reg_contents = ury_reg_contents = 0; 5848 if (!ps_get_line(buf, fp, filename)) { 5849 error("`%1' is empty", filename); 5850 return; 5851 } 5852 if (strncmp("%!PS-Adobe-", buf, 11) != 0) { 5853 error("`%1' is not conforming to the Document Structuring Conventions", 5854 filename); 5855 return; 5856 } 5857 while (ps_get_line(buf, fp, filename) != 0) { 5858 if (buf[0] != '%' || buf[1] != '%' 5859 || strncmp(buf + 2, "EndComments", 11) == 0) 5860 break; 5861 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5862 int res = parse_bounding_box(buf + 14, &bb); 5863 if (res == 1) { 5864 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5865 return; 5866 } 5867 else if (res == 2) { 5868 bb_at_end = 1; 5869 break; 5870 } 5871 else { 5872 error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5873 filename); 5874 return; 5875 } 5876 } 5877 } 5878 if (bb_at_end) { 5879 long offset; 5880 int last_try = 0; 5881 /* in the trailer, the last BoundingBox comment is significant */ 5882 for (offset = 512; !last_try; offset *= 2) { 5883 int had_trailer = 0; 5884 int got_bb = 0; 5885 if (offset > 32768 || fseek(fp, -offset, 2) == -1) { 5886 last_try = 1; 5887 if (fseek(fp, 0L, 0) == -1) 5888 break; 5889 } 5890 while (ps_get_line(buf, fp, filename) != 0) { 5891 if (buf[0] == '%' && buf[1] == '%') { 5892 if (!had_trailer) { 5893 if (strncmp(buf + 2, "Trailer", 7) == 0) 5894 had_trailer = 1; 5895 } 5896 else { 5897 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5898 int res = parse_bounding_box(buf + 14, &bb); 5899 if (res == 1) 5900 got_bb = 1; 5901 else if (res == 2) { 5902 error("`(atend)' not allowed in trailer of `%1'", filename); 5903 return; 5904 } 5905 else { 5906 error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5907 filename); 5908 return; 5909 } 5910 } 5911 } 5912 } 5913 } 5914 if (got_bb) { 5915 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5916 return; 5917 } 5918 } 5919 } 5920 error("%%%%BoundingBox comment not found in `%1'", filename); 5921} 5922 5923void ps_bbox_request() 5924{ 5925 symbol nm = get_long_name(1); 5926 if (nm.is_null()) 5927 skip_line(); 5928 else { 5929 while (!tok.newline() && !tok.eof()) 5930 tok.next(); 5931 errno = 0; 5932 // PS files might contain non-printable characters, such as ^Z 5933 // and CRs not followed by an LF, so open them in binary mode. 5934 FILE *fp = include_search_path.open_file_cautious(nm.contents(), 5935 0, FOPEN_RB); 5936 if (fp) { 5937 do_ps_file(fp, nm.contents()); 5938 fclose(fp); 5939 } 5940 else 5941 error("can't open `%1': %2", nm.contents(), strerror(errno)); 5942 tok.next(); 5943 } 5944} 5945 5946const char *asciify(int c) 5947{ 5948 static char buf[3]; 5949 buf[0] = escape_char == '\0' ? '\\' : escape_char; 5950 buf[1] = buf[2] = '\0'; 5951 switch (c) { 5952 case ESCAPE_QUESTION: 5953 buf[1] = '?'; 5954 break; 5955 case ESCAPE_AMPERSAND: 5956 buf[1] = '&'; 5957 break; 5958 case ESCAPE_RIGHT_PARENTHESIS: 5959 buf[1] = ')'; 5960 break; 5961 case ESCAPE_UNDERSCORE: 5962 buf[1] = '_'; 5963 break; 5964 case ESCAPE_BAR: 5965 buf[1] = '|'; 5966 break; 5967 case ESCAPE_CIRCUMFLEX: 5968 buf[1] = '^'; 5969 break; 5970 case ESCAPE_LEFT_BRACE: 5971 buf[1] = '{'; 5972 break; 5973 case ESCAPE_RIGHT_BRACE: 5974 buf[1] = '}'; 5975 break; 5976 case ESCAPE_LEFT_QUOTE: 5977 buf[1] = '`'; 5978 break; 5979 case ESCAPE_RIGHT_QUOTE: 5980 buf[1] = '\''; 5981 break; 5982 case ESCAPE_HYPHEN: 5983 buf[1] = '-'; 5984 break; 5985 case ESCAPE_BANG: 5986 buf[1] = '!'; 5987 break; 5988 case ESCAPE_c: 5989 buf[1] = 'c'; 5990 break; 5991 case ESCAPE_e: 5992 buf[1] = 'e'; 5993 break; 5994 case ESCAPE_E: 5995 buf[1] = 'E'; 5996 break; 5997 case ESCAPE_PERCENT: 5998 buf[1] = '%'; 5999 break; 6000 case ESCAPE_SPACE: 6001 buf[1] = ' '; 6002 break; 6003 case ESCAPE_TILDE: 6004 buf[1] = '~'; 6005 break; 6006 case ESCAPE_COLON: 6007 buf[1] = ':'; 6008 break; 6009 case PUSH_GROFF_MODE: 6010 case PUSH_COMP_MODE: 6011 case POP_GROFFCOMP_MODE: 6012 buf[0] = '\0'; 6013 break; 6014 default: 6015 if (invalid_input_char(c)) 6016 buf[0] = '\0'; 6017 else 6018 buf[0] = c; 6019 break; 6020 } 6021 return buf; 6022} 6023 6024const char *input_char_description(int c) 6025{ 6026 switch (c) { 6027 case '\n': 6028 return "a newline character"; 6029 case '\b': 6030 return "a backspace character"; 6031 case '\001': 6032 return "a leader character"; 6033 case '\t': 6034 return "a tab character"; 6035 case ' ': 6036 return "a space character"; 6037 case '\0': 6038 return "a node"; 6039 } 6040 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS]; 6041 if (invalid_input_char(c)) { 6042 const char *s = asciify(c); 6043 if (*s) { 6044 buf[0] = '`'; 6045 strcpy(buf + 1, s); 6046 strcat(buf, "'"); 6047 return buf; 6048 } 6049 sprintf(buf, "magic character code %d", c); 6050 return buf; 6051 } 6052 if (csprint(c)) { 6053 buf[0] = '`'; 6054 buf[1] = c; 6055 buf[2] = '\''; 6056 return buf; 6057 } 6058 sprintf(buf, "character code %d", c); 6059 return buf; 6060} 6061 6062void tag() 6063{ 6064 if (!tok.newline() && !tok.eof()) { 6065 string s; 6066 int c; 6067 for (;;) { 6068 c = get_copy(0); 6069 if (c == '"') { 6070 c = get_copy(0); 6071 break; 6072 } 6073 if (c != ' ' && c != '\t') 6074 break; 6075 } 6076 s = "x X "; 6077 for (; c != '\n' && c != EOF; c = get_copy(0)) 6078 s += (char)c; 6079 s += '\n'; 6080 curenv->add_node(new tag_node(s, 0)); 6081 } 6082 tok.next(); 6083} 6084 6085void taga() 6086{ 6087 if (!tok.newline() && !tok.eof()) { 6088 string s; 6089 int c; 6090 for (;;) { 6091 c = get_copy(0); 6092 if (c == '"') { 6093 c = get_copy(0); 6094 break; 6095 } 6096 if (c != ' ' && c != '\t') 6097 break; 6098 } 6099 s = "x X "; 6100 for (; c != '\n' && c != EOF; c = get_copy(0)) 6101 s += (char)c; 6102 s += '\n'; 6103 curenv->add_node(new tag_node(s, 1)); 6104 } 6105 tok.next(); 6106} 6107 6108// .tm, .tm1, and .tmc 6109 6110void do_terminal(int newline, int string_like) 6111{ 6112 if (!tok.newline() && !tok.eof()) { 6113 int c; 6114 for (;;) { 6115 c = get_copy(0); 6116 if (string_like && c == '"') { 6117 c = get_copy(0); 6118 break; 6119 } 6120 if (c != ' ' && c != '\t') 6121 break; 6122 } 6123 for (; c != '\n' && c != EOF; c = get_copy(0)) 6124 fputs(asciify(c), stderr); 6125 } 6126 if (newline) 6127 fputc('\n', stderr); 6128 fflush(stderr); 6129 tok.next(); 6130} 6131 6132void terminal() 6133{ 6134 do_terminal(1, 0); 6135} 6136 6137void terminal1() 6138{ 6139 do_terminal(1, 1); 6140} 6141 6142void terminal_continue() 6143{ 6144 do_terminal(0, 1); 6145} 6146 6147dictionary stream_dictionary(20); 6148 6149void do_open(int append) 6150{ 6151 symbol stream = get_name(1); 6152 if (!stream.is_null()) { 6153 symbol filename = get_long_name(1); 6154 if (!filename.is_null()) { 6155 errno = 0; 6156 FILE *fp = fopen(filename.contents(), append ? "a" : "w"); 6157 if (!fp) { 6158 error("can't open `%1' for %2: %3", 6159 filename.contents(), 6160 append ? "appending" : "writing", 6161 strerror(errno)); 6162 fp = (FILE *)stream_dictionary.remove(stream); 6163 } 6164 else 6165 fp = (FILE *)stream_dictionary.lookup(stream, fp); 6166 if (fp) 6167 fclose(fp); 6168 } 6169 } 6170 skip_line(); 6171} 6172 6173void open_request() 6174{ 6175 if (safer_flag) { 6176 error(".open request not allowed in safer mode"); 6177 skip_line(); 6178 } 6179 else 6180 do_open(0); 6181} 6182 6183void opena_request() 6184{ 6185 if (safer_flag) { 6186 error(".opena request not allowed in safer mode"); 6187 skip_line(); 6188 } 6189 else 6190 do_open(1); 6191} 6192 6193void close_request() 6194{ 6195 symbol stream = get_name(1); 6196 if (!stream.is_null()) { 6197 FILE *fp = (FILE *)stream_dictionary.remove(stream); 6198 if (!fp) 6199 error("no stream named `%1'", stream.contents()); 6200 else 6201 fclose(fp); 6202 } 6203 skip_line(); 6204} 6205 6206// .write and .writec 6207 6208void do_write_request(int newline) 6209{ 6210 symbol stream = get_name(1); 6211 if (stream.is_null()) { 6212 skip_line(); 6213 return; 6214 } 6215 FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6216 if (!fp) { 6217 error("no stream named `%1'", stream.contents()); 6218 skip_line(); 6219 return; 6220 } 6221 int c; 6222 while ((c = get_copy(0)) == ' ') 6223 ; 6224 if (c == '"') 6225 c = get_copy(0); 6226 for (; c != '\n' && c != EOF; c = get_copy(0)) 6227 fputs(asciify(c), fp); 6228 if (newline) 6229 fputc('\n', fp); 6230 fflush(fp); 6231 tok.next(); 6232} 6233 6234void write_request() 6235{ 6236 do_write_request(1); 6237} 6238 6239void write_request_continue() 6240{ 6241 do_write_request(0); 6242} 6243 6244void write_macro_request() 6245{ 6246 symbol stream = get_name(1); 6247 if (stream.is_null()) { 6248 skip_line(); 6249 return; 6250 } 6251 FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6252 if (!fp) { 6253 error("no stream named `%1'", stream.contents()); 6254 skip_line(); 6255 return; 6256 } 6257 symbol s = get_name(1); 6258 if (s.is_null()) { 6259 skip_line(); 6260 return; 6261 } 6262 request_or_macro *p = lookup_request(s); 6263 macro *m = p->to_macro(); 6264 if (!m) 6265 error("cannot write request"); 6266 else { 6267 string_iterator iter(*m); 6268 for (;;) { 6269 int c = iter.get(0); 6270 if (c == EOF) 6271 break; 6272 fputs(asciify(c), fp); 6273 } 6274 fflush(fp); 6275 } 6276 skip_line(); 6277} 6278 6279void warnscale_request() 6280{ 6281 if (has_arg()) { 6282 char c = tok.ch(); 6283 if (c == 'u') 6284 warn_scale = 1.0; 6285 else if (c == 'i') 6286 warn_scale = (double)units_per_inch; 6287 else if (c == 'c') 6288 warn_scale = (double)units_per_inch / 2.54; 6289 else if (c == 'p') 6290 warn_scale = (double)units_per_inch / 72.0; 6291 else if (c == 'P') 6292 warn_scale = (double)units_per_inch / 6.0; 6293 else { 6294 warning(WARN_SCALE, 6295 "invalid scaling indicator `%1', using `i' instead", c); 6296 c = 'i'; 6297 } 6298 warn_scaling_indicator = c; 6299 } 6300 skip_line(); 6301} 6302 6303void spreadwarn_request() 6304{ 6305 hunits n; 6306 if (has_arg() && get_hunits(&n, 'm')) { 6307 if (n < 0) 6308 n = 0; 6309 hunits em = curenv->get_size(); 6310 spread_limit = (double)n.to_units() 6311 / (em.is_zero() ? hresolution : em.to_units()); 6312 } 6313 else 6314 spread_limit = -spread_limit - 1; // no arg toggles on/off without 6315 // changing value; we mirror at 6316 // -0.5 to make zero a valid value 6317 skip_line(); 6318} 6319 6320static void init_charset_table() 6321{ 6322 char buf[16]; 6323 strcpy(buf, "char"); 6324 for (int i = 0; i < 256; i++) { 6325 strcpy(buf + 4, i_to_a(i)); 6326 charset_table[i] = get_charinfo(symbol(buf)); 6327 charset_table[i]->set_ascii_code(i); 6328 if (csalpha(i)) 6329 charset_table[i]->set_hyphenation_code(cmlower(i)); 6330 } 6331 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE); 6332 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE); 6333 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE); 6334 charset_table['-']->set_flags(charinfo::BREAK_AFTER); 6335 charset_table['"']->set_flags(charinfo::TRANSPARENT); 6336 charset_table['\'']->set_flags(charinfo::TRANSPARENT); 6337 charset_table[')']->set_flags(charinfo::TRANSPARENT); 6338 charset_table[']']->set_flags(charinfo::TRANSPARENT); 6339 charset_table['*']->set_flags(charinfo::TRANSPARENT); 6340 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT); 6341 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT); 6342 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER); 6343 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6344 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6345 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6346 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6347 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6348 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY); 6349 page_character = charset_table['%']; 6350} 6351 6352static void init_hpf_code_table() 6353{ 6354 for (int i = 0; i < 256; i++) 6355 hpf_code_table[i] = i; 6356} 6357 6358static void do_translate(int translate_transparent, int translate_input) 6359{ 6360 tok.skip(); 6361 while (!tok.newline() && !tok.eof()) { 6362 if (tok.space()) { 6363 // This is a really bizarre troff feature. 6364 tok.next(); 6365 translate_space_to_dummy = tok.dummy(); 6366 if (tok.newline() || tok.eof()) 6367 break; 6368 tok.next(); 6369 continue; 6370 } 6371 charinfo *ci1 = tok.get_char(1); 6372 if (ci1 == 0) 6373 break; 6374 tok.next(); 6375 if (tok.newline() || tok.eof()) { 6376 ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6377 translate_transparent); 6378 break; 6379 } 6380 if (tok.space()) 6381 ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6382 translate_transparent); 6383 else if (tok.stretchable_space()) 6384 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE, 6385 translate_transparent); 6386 else if (tok.dummy()) 6387 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY, 6388 translate_transparent); 6389 else if (tok.hyphen_indicator()) 6390 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR, 6391 translate_transparent); 6392 else { 6393 charinfo *ci2 = tok.get_char(1); 6394 if (ci2 == 0) 6395 break; 6396 if (ci1 == ci2) 6397 ci1->set_translation(0, translate_transparent, translate_input); 6398 else 6399 ci1->set_translation(ci2, translate_transparent, translate_input); 6400 } 6401 tok.next(); 6402 } 6403 skip_line(); 6404} 6405 6406void translate() 6407{ 6408 do_translate(1, 0); 6409} 6410 6411void translate_no_transparent() 6412{ 6413 do_translate(0, 0); 6414} 6415 6416void translate_input() 6417{ 6418 do_translate(1, 1); 6419} 6420 6421void char_flags() 6422{ 6423 int flags; 6424 if (get_integer(&flags)) 6425 while (has_arg()) { 6426 charinfo *ci = tok.get_char(1); 6427 if (ci) { 6428 charinfo *tem = ci->get_translation(); 6429 if (tem) 6430 ci = tem; 6431 ci->set_flags(flags); 6432 } 6433 tok.next(); 6434 } 6435 skip_line(); 6436} 6437 6438void hyphenation_code() 6439{ 6440 tok.skip(); 6441 while (!tok.newline() && !tok.eof()) { 6442 charinfo *ci = tok.get_char(1); 6443 if (ci == 0) 6444 break; 6445 tok.next(); 6446 tok.skip(); 6447 unsigned char c = tok.ch(); 6448 if (c == 0) { 6449 error("hyphenation code must be ordinary character"); 6450 break; 6451 } 6452 if (csdigit(c)) { 6453 error("hyphenation code cannot be digit"); 6454 break; 6455 } 6456 ci->set_hyphenation_code(c); 6457 if (ci->get_translation() 6458 && ci->get_translation()->get_translation_input()) 6459 ci->get_translation()->set_hyphenation_code(c); 6460 tok.next(); 6461 tok.skip(); 6462 } 6463 skip_line(); 6464} 6465 6466void hyphenation_patterns_file_code() 6467{ 6468 tok.skip(); 6469 while (!tok.newline() && !tok.eof()) { 6470 int n1, n2; 6471 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) { 6472 if (!has_arg()) { 6473 error("missing output hyphenation code"); 6474 break; 6475 } 6476 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) { 6477 hpf_code_table[n1] = n2; 6478 tok.skip(); 6479 } 6480 else { 6481 error("output hyphenation code must be integer in the range 0..255"); 6482 break; 6483 } 6484 } 6485 else { 6486 error("input hyphenation code must be integer in the range 0..255"); 6487 break; 6488 } 6489 } 6490 skip_line(); 6491} 6492 6493charinfo *token::get_char(int required) 6494{ 6495 if (type == TOKEN_CHAR) 6496 return charset_table[c]; 6497 if (type == TOKEN_SPECIAL) 6498 return get_charinfo(nm); 6499 if (type == TOKEN_NUMBERED_CHAR) 6500 return get_charinfo_by_number(val); 6501 if (type == TOKEN_ESCAPE) { 6502 if (escape_char != 0) 6503 return charset_table[escape_char]; 6504 else { 6505 error("`\\e' used while no current escape character"); 6506 return 0; 6507 } 6508 } 6509 if (required) { 6510 if (type == TOKEN_EOF || type == TOKEN_NEWLINE) 6511 warning(WARN_MISSING, "missing normal or special character"); 6512 else 6513 error("normal or special character expected (got %1)", description()); 6514 } 6515 return 0; 6516} 6517 6518charinfo *get_optional_char() 6519{ 6520 while (tok.space()) 6521 tok.next(); 6522 charinfo *ci = tok.get_char(); 6523 if (!ci) 6524 check_missing_character(); 6525 else 6526 tok.next(); 6527 return ci; 6528} 6529 6530void check_missing_character() 6531{ 6532 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab()) 6533 error("normal or special character expected (got %1): " 6534 "treated as missing", 6535 tok.description()); 6536} 6537 6538// this is for \Z 6539 6540int token::add_to_node_list(node **pp) 6541{ 6542 hunits w; 6543 int s; 6544 node *n = 0; 6545 switch (type) { 6546 case TOKEN_CHAR: 6547 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s); 6548 break; 6549 case TOKEN_DUMMY: 6550 n = new dummy_node; 6551 break; 6552 case TOKEN_ESCAPE: 6553 if (escape_char != 0) 6554 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s); 6555 break; 6556 case TOKEN_HYPHEN_INDICATOR: 6557 *pp = (*pp)->add_discretionary_hyphen(); 6558 break; 6559 case TOKEN_ITALIC_CORRECTION: 6560 *pp = (*pp)->add_italic_correction(&w); 6561 break; 6562 case TOKEN_LEFT_BRACE: 6563 break; 6564 case TOKEN_MARK_INPUT: 6565 set_number_reg(nm, curenv->get_input_line_position().to_units()); 6566 break; 6567 case TOKEN_NODE: 6568 n = nd; 6569 nd = 0; 6570 break; 6571 case TOKEN_NUMBERED_CHAR: 6572 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s); 6573 break; 6574 case TOKEN_RIGHT_BRACE: 6575 break; 6576 case TOKEN_SPACE: 6577 n = new hmotion_node(curenv->get_space_width(), 6578 curenv->get_fill_color()); 6579 break; 6580 case TOKEN_SPECIAL: 6581 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s); 6582 break; 6583 case TOKEN_STRETCHABLE_SPACE: 6584 n = new unbreakable_space_node(curenv->get_space_width(), 6585 curenv->get_fill_color()); 6586 break; 6587 case TOKEN_UNSTRETCHABLE_SPACE: 6588 n = new space_char_hmotion_node(curenv->get_space_width(), 6589 curenv->get_fill_color()); 6590 break; 6591 case TOKEN_TRANSPARENT_DUMMY: 6592 n = new transparent_dummy_node; 6593 break; 6594 case TOKEN_ZERO_WIDTH_BREAK: 6595 n = new space_node(H0, curenv->get_fill_color()); 6596 n->freeze_space(); 6597 n->is_escape_colon(); 6598 break; 6599 default: 6600 return 0; 6601 } 6602 if (n) { 6603 n->next = *pp; 6604 *pp = n; 6605 } 6606 return 1; 6607} 6608 6609void token::process() 6610{ 6611 if (possibly_handle_first_page_transition()) 6612 return; 6613 switch (type) { 6614 case TOKEN_BACKSPACE: 6615 curenv->add_node(new hmotion_node(-curenv->get_space_width(), 6616 curenv->get_fill_color())); 6617 break; 6618 case TOKEN_CHAR: 6619 curenv->add_char(charset_table[c]); 6620 break; 6621 case TOKEN_DUMMY: 6622 curenv->add_node(new dummy_node); 6623 break; 6624 case TOKEN_EMPTY: 6625 assert(0); 6626 break; 6627 case TOKEN_EOF: 6628 assert(0); 6629 break; 6630 case TOKEN_ESCAPE: 6631 if (escape_char != 0) 6632 curenv->add_char(charset_table[escape_char]); 6633 break; 6634 case TOKEN_BEGIN_TRAP: 6635 case TOKEN_END_TRAP: 6636 case TOKEN_PAGE_EJECTOR: 6637 // these are all handled in process_input_stack() 6638 break; 6639 case TOKEN_HYPHEN_INDICATOR: 6640 curenv->add_hyphen_indicator(); 6641 break; 6642 case TOKEN_INTERRUPT: 6643 curenv->interrupt(); 6644 break; 6645 case TOKEN_ITALIC_CORRECTION: 6646 curenv->add_italic_correction(); 6647 break; 6648 case TOKEN_LEADER: 6649 curenv->handle_tab(1); 6650 break; 6651 case TOKEN_LEFT_BRACE: 6652 break; 6653 case TOKEN_MARK_INPUT: 6654 set_number_reg(nm, curenv->get_input_line_position().to_units()); 6655 break; 6656 case TOKEN_NEWLINE: 6657 curenv->newline(); 6658 break; 6659 case TOKEN_NODE: 6660 curenv->add_node(nd); 6661 nd = 0; 6662 break; 6663 case TOKEN_NUMBERED_CHAR: 6664 curenv->add_char(get_charinfo_by_number(val)); 6665 break; 6666 case TOKEN_REQUEST: 6667 // handled in process_input_stack() 6668 break; 6669 case TOKEN_RIGHT_BRACE: 6670 break; 6671 case TOKEN_SPACE: 6672 curenv->space(); 6673 break; 6674 case TOKEN_SPECIAL: 6675 curenv->add_char(get_charinfo(nm)); 6676 break; 6677 case TOKEN_SPREAD: 6678 curenv->spread(); 6679 break; 6680 case TOKEN_STRETCHABLE_SPACE: 6681 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(), 6682 curenv->get_fill_color())); 6683 break; 6684 case TOKEN_UNSTRETCHABLE_SPACE: 6685 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(), 6686 curenv->get_fill_color())); 6687 break; 6688 case TOKEN_TAB: 6689 curenv->handle_tab(0); 6690 break; 6691 case TOKEN_TRANSPARENT: 6692 break; 6693 case TOKEN_TRANSPARENT_DUMMY: 6694 curenv->add_node(new transparent_dummy_node); 6695 break; 6696 case TOKEN_ZERO_WIDTH_BREAK: 6697 { 6698 node *tmp = new space_node(H0, curenv->get_fill_color()); 6699 tmp->freeze_space(); 6700 tmp->is_escape_colon(); 6701 curenv->add_node(tmp); 6702 break; 6703 } 6704 default: 6705 assert(0); 6706 } 6707} 6708 6709class nargs_reg : public reg { 6710public: 6711 const char *get_string(); 6712}; 6713 6714const char *nargs_reg::get_string() 6715{ 6716 return i_to_a(input_stack::nargs()); 6717} 6718 6719class lineno_reg : public reg { 6720public: 6721 const char *get_string(); 6722}; 6723 6724const char *lineno_reg::get_string() 6725{ 6726 int line; 6727 const char *file; 6728 if (!input_stack::get_location(0, &file, &line)) 6729 line = 0; 6730 return i_to_a(line); 6731} 6732 6733class writable_lineno_reg : public general_reg { 6734public: 6735 writable_lineno_reg(); 6736 void set_value(units); 6737 int get_value(units *); 6738}; 6739 6740writable_lineno_reg::writable_lineno_reg() 6741{ 6742} 6743 6744int writable_lineno_reg::get_value(units *res) 6745{ 6746 int line; 6747 const char *file; 6748 if (!input_stack::get_location(0, &file, &line)) 6749 return 0; 6750 *res = line; 6751 return 1; 6752} 6753 6754void writable_lineno_reg::set_value(units n) 6755{ 6756 input_stack::set_location(0, n); 6757} 6758 6759class filename_reg : public reg { 6760public: 6761 const char *get_string(); 6762}; 6763 6764const char *filename_reg::get_string() 6765{ 6766 int line; 6767 const char *file; 6768 if (input_stack::get_location(0, &file, &line)) 6769 return file; 6770 else 6771 return 0; 6772} 6773 6774class constant_reg : public reg { 6775 const char *s; 6776public: 6777 constant_reg(const char *); 6778 const char *get_string(); 6779}; 6780 6781constant_reg::constant_reg(const char *p) : s(p) 6782{ 6783} 6784 6785const char *constant_reg::get_string() 6786{ 6787 return s; 6788} 6789 6790constant_int_reg::constant_int_reg(int *q) : p(q) 6791{ 6792} 6793 6794const char *constant_int_reg::get_string() 6795{ 6796 return i_to_a(*p); 6797} 6798 6799void abort_request() 6800{ 6801 int c; 6802 if (tok.eof()) 6803 c = EOF; 6804 else if (tok.newline()) 6805 c = '\n'; 6806 else { 6807 while ((c = get_copy(0)) == ' ') 6808 ; 6809 } 6810 if (c == EOF || c == '\n') 6811 fputs("User Abort.", stderr); 6812 else { 6813 for (; c != '\n' && c != EOF; c = get_copy(0)) 6814 fputs(asciify(c), stderr); 6815 } 6816 fputc('\n', stderr); 6817 cleanup_and_exit(1); 6818} 6819 6820char *read_string() 6821{ 6822 int len = 256; 6823 char *s = new char[len]; 6824 int c; 6825 while ((c = get_copy(0)) == ' ') 6826 ; 6827 int i = 0; 6828 while (c != '\n' && c != EOF) { 6829 if (!invalid_input_char(c)) { 6830 if (i + 2 > len) { 6831 char *tem = s; 6832 s = new char[len*2]; 6833 memcpy(s, tem, len); 6834 len *= 2; 6835 a_delete tem; 6836 } 6837 s[i++] = c; 6838 } 6839 c = get_copy(0); 6840 } 6841 s[i] = '\0'; 6842 tok.next(); 6843 if (i == 0) { 6844 a_delete s; 6845 return 0; 6846 } 6847 return s; 6848} 6849 6850void pipe_output() 6851{ 6852 if (safer_flag) { 6853 error(".pi request not allowed in safer mode"); 6854 skip_line(); 6855 } 6856 else { 6857#ifdef POPEN_MISSING 6858 error("pipes not available on this system"); 6859 skip_line(); 6860#else /* not POPEN_MISSING */ 6861 if (the_output) { 6862 error("can't pipe: output already started"); 6863 skip_line(); 6864 } 6865 else { 6866 char *pc; 6867 if ((pc = read_string()) == 0) 6868 error("can't pipe to empty command"); 6869 if (pipe_command) { 6870 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1]; 6871 strcpy(s, pipe_command); 6872 strcat(s, "|"); 6873 strcat(s, pc); 6874 a_delete pipe_command; 6875 a_delete pc; 6876 pipe_command = s; 6877 } 6878 else 6879 pipe_command = pc; 6880 } 6881#endif /* not POPEN_MISSING */ 6882 } 6883} 6884 6885static int system_status; 6886 6887void system_request() 6888{ 6889 if (safer_flag) { 6890 error(".sy request not allowed in safer mode"); 6891 skip_line(); 6892 } 6893 else { 6894 char *command = read_string(); 6895 if (!command) 6896 error("empty command"); 6897 else { 6898 system_status = system(command); 6899 a_delete command; 6900 } 6901 } 6902} 6903 6904void copy_file() 6905{ 6906 if (curdiv == topdiv && topdiv->before_first_page) { 6907 handle_initial_request(COPY_FILE_REQUEST); 6908 return; 6909 } 6910 symbol filename = get_long_name(1); 6911 while (!tok.newline() && !tok.eof()) 6912 tok.next(); 6913 if (break_flag) 6914 curenv->do_break(); 6915 if (!filename.is_null()) 6916 curdiv->copy_file(filename.contents()); 6917 tok.next(); 6918} 6919 6920#ifdef COLUMN 6921 6922void vjustify() 6923{ 6924 if (curdiv == topdiv && topdiv->before_first_page) { 6925 handle_initial_request(VJUSTIFY_REQUEST); 6926 return; 6927 } 6928 symbol type = get_long_name(1); 6929 if (!type.is_null()) 6930 curdiv->vjustify(type); 6931 skip_line(); 6932} 6933 6934#endif /* COLUMN */ 6935 6936void transparent_file() 6937{ 6938 if (curdiv == topdiv && topdiv->before_first_page) { 6939 handle_initial_request(TRANSPARENT_FILE_REQUEST); 6940 return; 6941 } 6942 symbol filename = get_long_name(1); 6943 while (!tok.newline() && !tok.eof()) 6944 tok.next(); 6945 if (break_flag) 6946 curenv->do_break(); 6947 if (!filename.is_null()) { 6948 errno = 0; 6949 FILE *fp = include_search_path.open_file_cautious(filename.contents()); 6950 if (!fp) 6951 error("can't open `%1': %2", filename.contents(), strerror(errno)); 6952 else { 6953 int bol = 1; 6954 for (;;) { 6955 int c = getc(fp); 6956 if (c == EOF) 6957 break; 6958 if (invalid_input_char(c)) 6959 warning(WARN_INPUT, "invalid input character code %1", int(c)); 6960 else { 6961 curdiv->transparent_output(c); 6962 bol = c == '\n'; 6963 } 6964 } 6965 if (!bol) 6966 curdiv->transparent_output('\n'); 6967 fclose(fp); 6968 } 6969 } 6970 tok.next(); 6971} 6972 6973class page_range { 6974 int first; 6975 int last; 6976public: 6977 page_range *next; 6978 page_range(int, int, page_range *); 6979 int contains(int n); 6980}; 6981 6982page_range::page_range(int i, int j, page_range *p) 6983: first(i), last(j), next(p) 6984{ 6985} 6986 6987int page_range::contains(int n) 6988{ 6989 return n >= first && (last <= 0 || n <= last); 6990} 6991 6992page_range *output_page_list = 0; 6993 6994int in_output_page_list(int n) 6995{ 6996 if (!output_page_list) 6997 return 1; 6998 for (page_range *p = output_page_list; p; p = p->next) 6999 if (p->contains(n)) 7000 return 1; 7001 return 0; 7002} 7003 7004static void parse_output_page_list(char *p) 7005{ 7006 for (;;) { 7007 int i; 7008 if (*p == '-') 7009 i = 1; 7010 else if (csdigit(*p)) { 7011 i = 0; 7012 do 7013 i = i*10 + *p++ - '0'; 7014 while (csdigit(*p)); 7015 } 7016 else 7017 break; 7018 int j; 7019 if (*p == '-') { 7020 p++; 7021 j = 0; 7022 if (csdigit(*p)) { 7023 do 7024 j = j*10 + *p++ - '0'; 7025 while (csdigit(*p)); 7026 } 7027 } 7028 else 7029 j = i; 7030 if (j == 0) 7031 last_page_number = -1; 7032 else if (last_page_number >= 0 && j > last_page_number) 7033 last_page_number = j; 7034 output_page_list = new page_range(i, j, output_page_list); 7035 if (*p != ',') 7036 break; 7037 ++p; 7038 } 7039 if (*p != '\0') { 7040 error("bad output page list"); 7041 output_page_list = 0; 7042 } 7043} 7044 7045static FILE *open_mac_file(const char *mac, char **path) 7046{ 7047 // Try first FOOBAR.tmac, then tmac.FOOBAR 7048 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1]; 7049 strcpy(s1, mac); 7050 strcat(s1, MACRO_POSTFIX); 7051 FILE *fp = mac_path->open_file(s1, path); 7052 a_delete s1; 7053 if (!fp) { 7054 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1]; 7055 strcpy(s2, MACRO_PREFIX); 7056 strcat(s2, mac); 7057 fp = mac_path->open_file(s2, path); 7058 a_delete s2; 7059 } 7060 return fp; 7061} 7062 7063static void process_macro_file(const char *mac) 7064{ 7065 char *path; 7066 FILE *fp = open_mac_file(mac, &path); 7067 if (!fp) 7068 fatal("can't find macro file %1", mac); 7069 const char *s = symbol(path).contents(); 7070 a_delete path; 7071 input_stack::push(new file_iterator(fp, s)); 7072 tok.next(); 7073 process_input_stack(); 7074} 7075 7076static void process_startup_file(const char *filename) 7077{ 7078 char *path; 7079 search_path *orig_mac_path = mac_path; 7080 mac_path = &config_macro_path; 7081 FILE *fp = mac_path->open_file(filename, &path); 7082 if (fp) { 7083 input_stack::push(new file_iterator(fp, symbol(path).contents())); 7084 a_delete path; 7085 tok.next(); 7086 process_input_stack(); 7087 } 7088 mac_path = orig_mac_path; 7089} 7090 7091void macro_source() 7092{ 7093 symbol nm = get_long_name(1); 7094 if (nm.is_null()) 7095 skip_line(); 7096 else { 7097 while (!tok.newline() && !tok.eof()) 7098 tok.next(); 7099 char *path; 7100 FILE *fp = mac_path->open_file(nm.contents(), &path); 7101 // .mso doesn't (and cannot) go through open_mac_file, so we 7102 // need to do it here manually: If we have tmac.FOOBAR, try 7103 // FOOBAR.tmac and vice versa 7104 if (!fp) { 7105 const char *fn = nm.contents(); 7106 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) { 7107 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)]; 7108 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1); 7109 strcat(s, MACRO_POSTFIX); 7110 fp = mac_path->open_file(s, &path); 7111 a_delete s; 7112 } 7113 if (!fp) { 7114 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1, 7115 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) { 7116 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)]; 7117 strcpy(s, MACRO_PREFIX); 7118 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1); 7119 fp = mac_path->open_file(s, &path); 7120 a_delete s; 7121 } 7122 } 7123 } 7124 if (fp) { 7125 input_stack::push(new file_iterator(fp, symbol(path).contents())); 7126 a_delete path; 7127 } 7128 else 7129 error("can't find macro file `%1'", nm.contents()); 7130 tok.next(); 7131 } 7132} 7133 7134static void process_input_file(const char *name) 7135{ 7136 FILE *fp; 7137 if (strcmp(name, "-") == 0) { 7138 clearerr(stdin); 7139 fp = stdin; 7140 } 7141 else { 7142 errno = 0; 7143 fp = include_search_path.open_file_cautious(name); 7144 if (!fp) 7145 fatal("can't open `%1': %2", name, strerror(errno)); 7146 } 7147 input_stack::push(new file_iterator(fp, name)); 7148 tok.next(); 7149 process_input_stack(); 7150} 7151 7152// make sure the_input is empty before calling this 7153 7154static int evaluate_expression(const char *expr, units *res) 7155{ 7156 input_stack::push(make_temp_iterator(expr)); 7157 tok.next(); 7158 int success = get_number(res, 'u'); 7159 while (input_stack::get(0) != EOF) 7160 ; 7161 return success; 7162} 7163 7164static void do_register_assignment(const char *s) 7165{ 7166 const char *p = strchr(s, '='); 7167 if (!p) { 7168 char buf[2]; 7169 buf[0] = s[0]; 7170 buf[1] = 0; 7171 units n; 7172 if (evaluate_expression(s + 1, &n)) 7173 set_number_reg(buf, n); 7174 } 7175 else { 7176 char *buf = new char[p - s + 1]; 7177 memcpy(buf, s, p - s); 7178 buf[p - s] = 0; 7179 units n; 7180 if (evaluate_expression(p + 1, &n)) 7181 set_number_reg(buf, n); 7182 a_delete buf; 7183 } 7184} 7185 7186static void set_string(const char *name, const char *value) 7187{ 7188 macro *m = new macro; 7189 for (const char *p = value; *p; p++) 7190 if (!invalid_input_char((unsigned char)*p)) 7191 m->append(*p); 7192 request_dictionary.define(name, m); 7193} 7194 7195static void do_string_assignment(const char *s) 7196{ 7197 const char *p = strchr(s, '='); 7198 if (!p) { 7199 char buf[2]; 7200 buf[0] = s[0]; 7201 buf[1] = 0; 7202 set_string(buf, s + 1); 7203 } 7204 else { 7205 char *buf = new char[p - s + 1]; 7206 memcpy(buf, s, p - s); 7207 buf[p - s] = 0; 7208 set_string(buf, p + 1); 7209 a_delete buf; 7210 } 7211} 7212 7213struct string_list { 7214 const char *s; 7215 string_list *next; 7216 string_list(const char *ss) : s(ss), next(0) {} 7217}; 7218 7219#if 0 7220static void prepend_string(const char *s, string_list **p) 7221{ 7222 string_list *l = new string_list(s); 7223 l->next = *p; 7224 *p = l; 7225} 7226#endif 7227 7228static void add_string(const char *s, string_list **p) 7229{ 7230 while (*p) 7231 p = &((*p)->next); 7232 *p = new string_list(s); 7233} 7234 7235void usage(FILE *stream, const char *prog) 7236{ 7237 fprintf(stream, 7238"usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" 7239" -rcn -Tname -Fdir -Idir -Mdir [files...]\n", 7240 prog); 7241} 7242 7243int main(int argc, char **argv) 7244{ 7245 program_name = argv[0]; 7246 static char stderr_buf[BUFSIZ]; 7247 setbuf(stderr, stderr_buf); 7248 int c; 7249 string_list *macros = 0; 7250 string_list *register_assignments = 0; 7251 string_list *string_assignments = 0; 7252 int iflag = 0; 7253 int tflag = 0; 7254 int fflag = 0; 7255 int nflag = 0; 7256 int no_rc = 0; // don't process troffrc and troffrc-end 7257 int next_page_number = 0; // pacify compiler 7258 opterr = 0; 7259 hresolution = vresolution = 1; 7260 // restore $PATH if called from groff 7261 char* groff_path = getenv("GROFF_PATH__"); 7262 if (groff_path) { 7263 string e = "PATH"; 7264 e += '='; 7265 if (*groff_path) 7266 e += groff_path; 7267 e += '\0'; 7268 if (putenv(strsave(e.contents()))) 7269 fatal("putenv failed"); 7270 } 7271 static const struct option long_options[] = { 7272 { "help", no_argument, 0, CHAR_MAX + 1 }, 7273 { "version", no_argument, 0, 'v' }, 7274 { 0, 0, 0, 0 } 7275 }; 7276#if defined(DEBUGGING) 7277#define DEBUG_OPTION "D" 7278#endif 7279 while ((c = getopt_long(argc, argv, 7280 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU" 7281 DEBUG_OPTION, long_options, 0)) 7282 != EOF) 7283 switch(c) { 7284 case 'v': 7285 { 7286 printf("GNU troff (groff) version %s\n", Version_string); 7287 exit(0); 7288 break; 7289 } 7290 case 'I': 7291 // Search path for .psbb files 7292 // and most other non-system input files. 7293 include_search_path.command_line_dir(optarg); 7294 break; 7295 case 'T': 7296 device = optarg; 7297 tflag = 1; 7298 is_html = (strcmp(device, "html") == 0); 7299 break; 7300 case 'C': 7301 compatible_flag = 1; 7302 // fall through 7303 case 'c': 7304 color_flag = 0; 7305 break; 7306 case 'M': 7307 macro_path.command_line_dir(optarg); 7308 safer_macro_path.command_line_dir(optarg); 7309 config_macro_path.command_line_dir(optarg); 7310 break; 7311 case 'F': 7312 font::command_line_font_dir(optarg); 7313 break; 7314 case 'm': 7315 add_string(optarg, ¯os); 7316 break; 7317 case 'E': 7318 inhibit_errors = 1; 7319 break; 7320 case 'R': 7321 no_rc = 1; 7322 break; 7323 case 'w': 7324 enable_warning(optarg); 7325 break; 7326 case 'W': 7327 disable_warning(optarg); 7328 break; 7329 case 'i': 7330 iflag = 1; 7331 break; 7332 case 'b': 7333 backtrace_flag = 1; 7334 break; 7335 case 'a': 7336 ascii_output_flag = 1; 7337 break; 7338 case 'z': 7339 suppress_output_flag = 1; 7340 break; 7341 case 'n': 7342 if (sscanf(optarg, "%d", &next_page_number) == 1) 7343 nflag++; 7344 else 7345 error("bad page number"); 7346 break; 7347 case 'o': 7348 parse_output_page_list(optarg); 7349 break; 7350 case 'd': 7351 if (*optarg == '\0') 7352 error("`-d' requires non-empty argument"); 7353 else 7354 add_string(optarg, &string_assignments); 7355 break; 7356 case 'r': 7357 if (*optarg == '\0') 7358 error("`-r' requires non-empty argument"); 7359 else 7360 add_string(optarg, ®ister_assignments); 7361 break; 7362 case 'f': 7363 default_family = symbol(optarg); 7364 fflag = 1; 7365 break; 7366 case 'q': 7367 case 's': 7368 case 't': 7369 // silently ignore these 7370 break; 7371 case 'U': 7372 safer_flag = 0; // unsafe behaviour 7373 break; 7374#if defined(DEBUGGING) 7375 case 'D': 7376 debug_state = 1; 7377 break; 7378#endif 7379 case CHAR_MAX + 1: // --help 7380 usage(stdout, argv[0]); 7381 exit(0); 7382 break; 7383 case '?': 7384 usage(stderr, argv[0]); 7385 exit(1); 7386 break; // never reached 7387 default: 7388 assert(0); 7389 } 7390 if (!safer_flag) 7391 mac_path = ¯o_path; 7392 set_string(".T", device); 7393 init_charset_table(); 7394 init_hpf_code_table(); 7395 if (!font::load_desc()) 7396 fatal("sorry, I can't continue"); 7397 units_per_inch = font::res; 7398 hresolution = font::hor; 7399 vresolution = font::vert; 7400 sizescale = font::sizescale; 7401 tcommand_flag = font::tcommand; 7402 warn_scale = (double)units_per_inch; 7403 warn_scaling_indicator = 'i'; 7404 if (!fflag && font::family != 0 && *font::family != '\0') 7405 default_family = symbol(font::family); 7406 font_size::init_size_table(font::sizes); 7407 int i; 7408 int j = 1; 7409 if (font::style_table) { 7410 for (i = 0; font::style_table[i]; i++) 7411 mount_style(j++, symbol(font::style_table[i])); 7412 } 7413 for (i = 0; font::font_name_table[i]; i++, j++) 7414 // In the DESC file a font name of 0 (zero) means leave this 7415 // position empty. 7416 if (strcmp(font::font_name_table[i], "0") != 0) 7417 mount_font(j, symbol(font::font_name_table[i])); 7418 curdiv = topdiv = new top_level_diversion; 7419 if (nflag) 7420 topdiv->set_next_page_number(next_page_number); 7421 init_input_requests(); 7422 init_env_requests(); 7423 init_div_requests(); 7424#ifdef COLUMN 7425 init_column_requests(); 7426#endif /* COLUMN */ 7427 init_node_requests(); 7428 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); 7429 init_registers(); 7430 init_reg_requests(); 7431 init_hyphen_requests(); 7432 init_environments(); 7433 while (string_assignments) { 7434 do_string_assignment(string_assignments->s); 7435 string_list *tem = string_assignments; 7436 string_assignments = string_assignments->next; 7437 delete tem; 7438 } 7439 while (register_assignments) { 7440 do_register_assignment(register_assignments->s); 7441 string_list *tem = register_assignments; 7442 register_assignments = register_assignments->next; 7443 delete tem; 7444 } 7445 if (!no_rc) 7446 process_startup_file(INITIAL_STARTUP_FILE); 7447 while (macros) { 7448 process_macro_file(macros->s); 7449 string_list *tem = macros; 7450 macros = macros->next; 7451 delete tem; 7452 } 7453 if (!no_rc) 7454 process_startup_file(FINAL_STARTUP_FILE); 7455 for (i = optind; i < argc; i++) 7456 process_input_file(argv[i]); 7457 if (optind >= argc || iflag) 7458 process_input_file("-"); 7459 exit_troff(); 7460 return 0; // not reached 7461} 7462 7463void warn_request() 7464{ 7465 int n; 7466 if (has_arg() && get_integer(&n)) { 7467 if (n & ~WARN_TOTAL) { 7468 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL); 7469 n &= WARN_TOTAL; 7470 } 7471 warning_mask = n; 7472 } 7473 else 7474 warning_mask = WARN_TOTAL; 7475 skip_line(); 7476} 7477 7478static void init_registers() 7479{ 7480#ifdef LONG_FOR_TIME_T 7481 long 7482#else /* not LONG_FOR_TIME_T */ 7483 time_t 7484#endif /* not LONG_FOR_TIME_T */ 7485 t = time(0); 7486 // Use struct here to work around misfeature in old versions of g++. 7487 struct tm *tt = localtime(&t); 7488 set_number_reg("seconds", int(tt->tm_sec)); 7489 set_number_reg("minutes", int(tt->tm_min)); 7490 set_number_reg("hours", int(tt->tm_hour)); 7491 set_number_reg("dw", int(tt->tm_wday + 1)); 7492 set_number_reg("dy", int(tt->tm_mday)); 7493 set_number_reg("mo", int(tt->tm_mon + 1)); 7494 set_number_reg("year", int(1900 + tt->tm_year)); 7495 set_number_reg("yr", int(tt->tm_year)); 7496 set_number_reg("$$", getpid()); 7497 number_reg_dictionary.define(".A", 7498 new constant_reg(ascii_output_flag 7499 ? "1" 7500 : "0")); 7501} 7502 7503/* 7504 * registers associated with \O 7505 */ 7506 7507static int output_reg_minx_contents = -1; 7508static int output_reg_miny_contents = -1; 7509static int output_reg_maxx_contents = -1; 7510static int output_reg_maxy_contents = -1; 7511 7512void check_output_limits(int x, int y) 7513{ 7514 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) 7515 output_reg_minx_contents = x; 7516 if (x > output_reg_maxx_contents) 7517 output_reg_maxx_contents = x; 7518 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) 7519 output_reg_miny_contents = y; 7520 if (y > output_reg_maxy_contents) 7521 output_reg_maxy_contents = y; 7522} 7523 7524void reset_output_registers() 7525{ 7526 output_reg_minx_contents = -1; 7527 output_reg_miny_contents = -1; 7528 output_reg_maxx_contents = -1; 7529 output_reg_maxy_contents = -1; 7530} 7531 7532void get_output_registers(int *minx, int *miny, int *maxx, int *maxy) 7533{ 7534 *minx = output_reg_minx_contents; 7535 *miny = output_reg_miny_contents; 7536 *maxx = output_reg_maxx_contents; 7537 *maxy = output_reg_maxy_contents; 7538} 7539 7540void init_input_requests() 7541{ 7542 init_request("ab", abort_request); 7543 init_request("als", alias_macro); 7544 init_request("am", append_macro); 7545 init_request("am1", append_nocomp_macro); 7546 init_request("ami", append_indirect_macro); 7547 init_request("ami1", append_indirect_nocomp_macro); 7548 init_request("as", append_string); 7549 init_request("as1", append_nocomp_string); 7550 init_request("asciify", asciify_macro); 7551 init_request("backtrace", backtrace_request); 7552 init_request("blm", blank_line_macro); 7553 init_request("break", while_break_request); 7554 init_request("cf", copy_file); 7555 init_request("cflags", char_flags); 7556 init_request("char", define_character); 7557 init_request("chop", chop_macro); 7558 init_request("close", close_request); 7559 init_request("color", activate_color); 7560 init_request("composite", composite_request); 7561 init_request("continue", while_continue_request); 7562 init_request("cp", compatible); 7563 init_request("de", define_macro); 7564 init_request("de1", define_nocomp_macro); 7565 init_request("defcolor", define_color); 7566 init_request("dei", define_indirect_macro); 7567 init_request("dei1", define_indirect_nocomp_macro); 7568 init_request("do", do_request); 7569 init_request("ds", define_string); 7570 init_request("ds1", define_nocomp_string); 7571 init_request("ec", set_escape_char); 7572 init_request("ecr", restore_escape_char); 7573 init_request("ecs", save_escape_char); 7574 init_request("el", else_request); 7575 init_request("em", end_macro); 7576 init_request("eo", escape_off); 7577 init_request("ex", exit_request); 7578 init_request("fchar", define_fallback_character); 7579#ifdef WIDOW_CONTROL 7580 init_request("fpl", flush_pending_lines); 7581#endif /* WIDOW_CONTROL */ 7582 init_request("hcode", hyphenation_code); 7583 init_request("hpfcode", hyphenation_patterns_file_code); 7584 init_request("ie", if_else_request); 7585 init_request("if", if_request); 7586 init_request("ig", ignore); 7587 init_request("length", length_request); 7588 init_request("lf", line_file); 7589 init_request("mso", macro_source); 7590 init_request("nop", nop_request); 7591 init_request("nroff", nroff_request); 7592 init_request("nx", next_file); 7593 init_request("open", open_request); 7594 init_request("opena", opena_request); 7595 init_request("output", output_request); 7596 init_request("pc", set_page_character); 7597 init_request("pi", pipe_output); 7598 init_request("pm", print_macros); 7599 init_request("psbb", ps_bbox_request); 7600#ifndef POPEN_MISSING 7601 init_request("pso", pipe_source); 7602#endif /* not POPEN_MISSING */ 7603 init_request("rchar", remove_character); 7604 init_request("rd", read_request); 7605 init_request("return", return_macro_request); 7606 init_request("rm", remove_macro); 7607 init_request("rn", rename_macro); 7608 init_request("schar", define_special_character); 7609 init_request("shift", shift); 7610 init_request("so", source); 7611 init_request("spreadwarn", spreadwarn_request); 7612 init_request("substring", substring_request); 7613 init_request("sy", system_request); 7614 init_request("tag", tag); 7615 init_request("taga", taga); 7616 init_request("tm", terminal); 7617 init_request("tm1", terminal1); 7618 init_request("tmc", terminal_continue); 7619 init_request("tr", translate); 7620 init_request("trf", transparent_file); 7621 init_request("trin", translate_input); 7622 init_request("trnt", translate_no_transparent); 7623 init_request("troff", troff_request); 7624 init_request("unformat", unformat_macro); 7625#ifdef COLUMN 7626 init_request("vj", vjustify); 7627#endif /* COLUMN */ 7628 init_request("warn", warn_request); 7629 init_request("warnscale", warnscale_request); 7630 init_request("while", while_request); 7631 init_request("write", write_request); 7632 init_request("writec", write_request_continue); 7633 init_request("writem", write_macro_request); 7634 number_reg_dictionary.define(".$", new nargs_reg); 7635 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag)); 7636 number_reg_dictionary.define(".c", new lineno_reg); 7637 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag)); 7638 number_reg_dictionary.define(".F", new filename_reg); 7639 number_reg_dictionary.define(".g", new constant_reg("1")); 7640 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution)); 7641 number_reg_dictionary.define(".R", new constant_reg("10000")); 7642 number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag)); 7643 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); 7644 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask)); 7645 extern const char *major_version; 7646 number_reg_dictionary.define(".x", new constant_reg(major_version)); 7647 extern const char *revision; 7648 number_reg_dictionary.define(".Y", new constant_reg(revision)); 7649 extern const char *minor_version; 7650 number_reg_dictionary.define(".y", new constant_reg(minor_version)); 7651 number_reg_dictionary.define("c.", new writable_lineno_reg); 7652 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents)); 7653 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents)); 7654 number_reg_dictionary.define("opmaxx", 7655 new variable_reg(&output_reg_maxx_contents)); 7656 number_reg_dictionary.define("opmaxy", 7657 new variable_reg(&output_reg_maxy_contents)); 7658 number_reg_dictionary.define("opminx", 7659 new variable_reg(&output_reg_minx_contents)); 7660 number_reg_dictionary.define("opminy", 7661 new variable_reg(&output_reg_miny_contents)); 7662 number_reg_dictionary.define("slimit", 7663 new variable_reg(&input_stack::limit)); 7664 number_reg_dictionary.define("systat", new variable_reg(&system_status)); 7665 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents)); 7666 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents)); 7667} 7668 7669object_dictionary request_dictionary(501); 7670 7671void init_request(const char *s, REQUEST_FUNCP f) 7672{ 7673 request_dictionary.define(s, new request(f)); 7674} 7675 7676static request_or_macro *lookup_request(symbol nm) 7677{ 7678 assert(!nm.is_null()); 7679 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 7680 if (p == 0) { 7681 warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 7682 p = new macro; 7683 request_dictionary.define(nm, p); 7684 } 7685 return p; 7686} 7687 7688node *charinfo_to_node_list(charinfo *ci, const environment *envp) 7689{ 7690 // Don't interpret character definitions in compatible mode. 7691 int old_compatible_flag = compatible_flag; 7692 compatible_flag = 0; 7693 int old_escape_char = escape_char; 7694 escape_char = '\\'; 7695 macro *mac = ci->set_macro(0); 7696 assert(mac != 0); 7697 environment *oldenv = curenv; 7698 environment env(envp); 7699 curenv = &env; 7700 curenv->set_composite(); 7701 token old_tok = tok; 7702 input_stack::add_boundary(); 7703 string_iterator *si = 7704 new string_iterator(*mac, "composite character", ci->nm); 7705 input_stack::push(si); 7706 // we don't use process_input_stack, because we don't want to recognise 7707 // requests 7708 for (;;) { 7709 tok.next(); 7710 if (tok.eof()) 7711 break; 7712 if (tok.newline()) { 7713 error("composite character mustn't contain newline"); 7714 while (!tok.eof()) 7715 tok.next(); 7716 break; 7717 } 7718 else 7719 tok.process(); 7720 } 7721 node *n = curenv->extract_output_line(); 7722 input_stack::remove_boundary(); 7723 ci->set_macro(mac); 7724 tok = old_tok; 7725 curenv = oldenv; 7726 compatible_flag = old_compatible_flag; 7727 escape_char = old_escape_char; 7728 have_input = 0; 7729 return n; 7730} 7731 7732static node *read_draw_node() 7733{ 7734 token start; 7735 start.next(); 7736 if (!start.delimiter(1)){ 7737 do { 7738 tok.next(); 7739 } while (tok != start && !tok.newline() && !tok.eof()); 7740 } 7741 else { 7742 tok.next(); 7743 if (tok == start) 7744 error("missing argument"); 7745 else { 7746 unsigned char type = tok.ch(); 7747 if (type == 'F') { 7748 read_color_draw_node(start); 7749 return 0; 7750 } 7751 tok.next(); 7752 int maxpoints = 10; 7753 hvpair *point = new hvpair[maxpoints]; 7754 int npoints = 0; 7755 int no_last_v = 0; 7756 int err = 0; 7757 int i; 7758 for (i = 0; tok != start; i++) { 7759 if (i == maxpoints) { 7760 hvpair *oldpoint = point; 7761 point = new hvpair[maxpoints*2]; 7762 for (int j = 0; j < maxpoints; j++) 7763 point[j] = oldpoint[j]; 7764 maxpoints *= 2; 7765 a_delete oldpoint; 7766 } 7767 if (!get_hunits(&point[i].h, 7768 type == 'f' || type == 't' ? 'u' : 'm')) { 7769 err = 1; 7770 break; 7771 } 7772 ++npoints; 7773 tok.skip(); 7774 point[i].v = V0; 7775 if (tok == start) { 7776 no_last_v = 1; 7777 break; 7778 } 7779 if (!get_vunits(&point[i].v, 'v')) { 7780 err = 1; 7781 break; 7782 } 7783 tok.skip(); 7784 } 7785 while (tok != start && !tok.newline() && !tok.eof()) 7786 tok.next(); 7787 if (!err) { 7788 switch (type) { 7789 case 'l': 7790 if (npoints != 1 || no_last_v) { 7791 error("two arguments needed for line"); 7792 npoints = 1; 7793 } 7794 break; 7795 case 'c': 7796 if (npoints != 1 || !no_last_v) { 7797 error("one argument needed for circle"); 7798 npoints = 1; 7799 point[0].v = V0; 7800 } 7801 break; 7802 case 'e': 7803 if (npoints != 1 || no_last_v) { 7804 error("two arguments needed for ellipse"); 7805 npoints = 1; 7806 } 7807 break; 7808 case 'a': 7809 if (npoints != 2 || no_last_v) { 7810 error("four arguments needed for arc"); 7811 npoints = 2; 7812 } 7813 break; 7814 case '~': 7815 if (no_last_v) 7816 error("even number of arguments needed for spline"); 7817 break; 7818 case 'f': 7819 if (npoints != 1 || !no_last_v) { 7820 error("one argument needed for gray shade"); 7821 npoints = 1; 7822 point[0].v = V0; 7823 } 7824 default: 7825 // silently pass it through 7826 break; 7827 } 7828 draw_node *dn = new draw_node(type, point, npoints, 7829 curenv->get_font_size(), 7830 curenv->get_glyph_color(), 7831 curenv->get_fill_color()); 7832 a_delete point; 7833 return dn; 7834 } 7835 else { 7836 a_delete point; 7837 } 7838 } 7839 } 7840 return 0; 7841} 7842 7843static void read_color_draw_node(token &start) 7844{ 7845 tok.next(); 7846 if (tok == start) { 7847 error("missing color scheme"); 7848 return; 7849 } 7850 unsigned char scheme = tok.ch(); 7851 tok.next(); 7852 color *col = 0; 7853 char end = start.ch(); 7854 switch (scheme) { 7855 case 'c': 7856 col = read_cmy(end); 7857 break; 7858 case 'd': 7859 col = &default_color; 7860 break; 7861 case 'g': 7862 col = read_gray(end); 7863 break; 7864 case 'k': 7865 col = read_cmyk(end); 7866 break; 7867 case 'r': 7868 col = read_rgb(end); 7869 break; 7870 } 7871 if (col) 7872 curenv->set_fill_color(col); 7873 while (tok != start) { 7874 if (tok.newline() || tok.eof()) { 7875 warning(WARN_DELIM, "missing closing delimiter"); 7876 input_stack::push(make_temp_iterator("\n")); 7877 break; 7878 } 7879 tok.next(); 7880 } 7881 have_input = 1; 7882} 7883 7884static struct { 7885 const char *name; 7886 int mask; 7887} warning_table[] = { 7888 { "char", WARN_CHAR }, 7889 { "range", WARN_RANGE }, 7890 { "break", WARN_BREAK }, 7891 { "delim", WARN_DELIM }, 7892 { "el", WARN_EL }, 7893 { "scale", WARN_SCALE }, 7894 { "number", WARN_NUMBER }, 7895 { "syntax", WARN_SYNTAX }, 7896 { "tab", WARN_TAB }, 7897 { "right-brace", WARN_RIGHT_BRACE }, 7898 { "missing", WARN_MISSING }, 7899 { "input", WARN_INPUT }, 7900 { "escape", WARN_ESCAPE }, 7901 { "space", WARN_SPACE }, 7902 { "font", WARN_FONT }, 7903 { "di", WARN_DI }, 7904 { "mac", WARN_MAC }, 7905 { "reg", WARN_REG }, 7906 { "ig", WARN_IG }, 7907 { "color", WARN_COLOR }, 7908 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) }, 7909 { "w", WARN_TOTAL }, 7910 { "default", DEFAULT_WARNING_MASK }, 7911}; 7912 7913static int lookup_warning(const char *name) 7914{ 7915 for (unsigned int i = 0; 7916 i < sizeof(warning_table)/sizeof(warning_table[0]); 7917 i++) 7918 if (strcmp(name, warning_table[i].name) == 0) 7919 return warning_table[i].mask; 7920 return 0; 7921} 7922 7923static void enable_warning(const char *name) 7924{ 7925 int mask = lookup_warning(name); 7926 if (mask) 7927 warning_mask |= mask; 7928 else 7929 error("unknown warning `%1'", name); 7930} 7931 7932static void disable_warning(const char *name) 7933{ 7934 int mask = lookup_warning(name); 7935 if (mask) 7936 warning_mask &= ~mask; 7937 else 7938 error("unknown warning `%1'", name); 7939} 7940 7941static void copy_mode_error(const char *format, 7942 const errarg &arg1, 7943 const errarg &arg2, 7944 const errarg &arg3) 7945{ 7946 if (ignoring) { 7947 static const char prefix[] = "(in ignored input) "; 7948 char *s = new char[sizeof(prefix) + strlen(format)]; 7949 strcpy(s, prefix); 7950 strcat(s, format); 7951 warning(WARN_IG, s, arg1, arg2, arg3); 7952 a_delete s; 7953 } 7954 else 7955 error(format, arg1, arg2, arg3); 7956} 7957 7958enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL }; 7959 7960static void do_error(error_type type, 7961 const char *format, 7962 const errarg &arg1, 7963 const errarg &arg2, 7964 const errarg &arg3) 7965{ 7966 const char *filename; 7967 int lineno; 7968 if (inhibit_errors && type < FATAL) 7969 return; 7970 if (backtrace_flag) 7971 input_stack::backtrace(); 7972 if (!get_file_line(&filename, &lineno)) 7973 filename = 0; 7974 if (filename) 7975 errprint("%1:%2: ", filename, lineno); 7976 else if (program_name) 7977 fprintf(stderr, "%s: ", program_name); 7978 switch (type) { 7979 case FATAL: 7980 fputs("fatal error: ", stderr); 7981 break; 7982 case ERROR: 7983 break; 7984 case WARNING: 7985 fputs("warning: ", stderr); 7986 break; 7987 case OUTPUT_WARNING: 7988 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale; 7989 fprintf(stderr, "warning [p %d, %.1f%c", 7990 topdiv->get_page_number(), fromtop, warn_scaling_indicator); 7991 if (topdiv != curdiv) { 7992 double fromtop1 = curdiv->get_vertical_position().to_units() 7993 / warn_scale; 7994 fprintf(stderr, ", div `%s', %.1f%c", 7995 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator); 7996 } 7997 fprintf(stderr, "]: "); 7998 break; 7999 } 8000 errprint(format, arg1, arg2, arg3); 8001 fputc('\n', stderr); 8002 fflush(stderr); 8003 if (type == FATAL) 8004 cleanup_and_exit(1); 8005} 8006 8007int warning(warning_type t, 8008 const char *format, 8009 const errarg &arg1, 8010 const errarg &arg2, 8011 const errarg &arg3) 8012{ 8013 if ((t & warning_mask) != 0) { 8014 do_error(WARNING, format, arg1, arg2, arg3); 8015 return 1; 8016 } 8017 else 8018 return 0; 8019} 8020 8021int output_warning(warning_type t, 8022 const char *format, 8023 const errarg &arg1, 8024 const errarg &arg2, 8025 const errarg &arg3) 8026{ 8027 if ((t & warning_mask) != 0) { 8028 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3); 8029 return 1; 8030 } 8031 else 8032 return 0; 8033} 8034 8035void error(const char *format, 8036 const errarg &arg1, 8037 const errarg &arg2, 8038 const errarg &arg3) 8039{ 8040 do_error(ERROR, format, arg1, arg2, arg3); 8041} 8042 8043void fatal(const char *format, 8044 const errarg &arg1, 8045 const errarg &arg2, 8046 const errarg &arg3) 8047{ 8048 do_error(FATAL, format, arg1, arg2, arg3); 8049} 8050 8051void fatal_with_file_and_line(const char *filename, int lineno, 8052 const char *format, 8053 const errarg &arg1, 8054 const errarg &arg2, 8055 const errarg &arg3) 8056{ 8057 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno); 8058 errprint(format, arg1, arg2, arg3); 8059 fputc('\n', stderr); 8060 fflush(stderr); 8061 cleanup_and_exit(1); 8062} 8063 8064void error_with_file_and_line(const char *filename, int lineno, 8065 const char *format, 8066 const errarg &arg1, 8067 const errarg &arg2, 8068 const errarg &arg3) 8069{ 8070 fprintf(stderr, "%s:%d: error: ", filename, lineno); 8071 errprint(format, arg1, arg2, arg3); 8072 fputc('\n', stderr); 8073 fflush(stderr); 8074} 8075 8076dictionary charinfo_dictionary(501); 8077 8078charinfo *get_charinfo(symbol nm) 8079{ 8080 void *p = charinfo_dictionary.lookup(nm); 8081 if (p != 0) 8082 return (charinfo *)p; 8083 charinfo *cp = new charinfo(nm); 8084 (void)charinfo_dictionary.lookup(nm, cp); 8085 return cp; 8086} 8087 8088int charinfo::next_index = 0; 8089 8090charinfo::charinfo(symbol s) 8091: translation(0), mac(0), special_translation(TRANSLATE_NONE), 8092 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0), 8093 not_found(0), transparent_translate(1), translate_input(0), 8094 mode(CHAR_NORMAL), nm(s) 8095{ 8096 index = next_index++; 8097} 8098 8099void charinfo::set_hyphenation_code(unsigned char c) 8100{ 8101 hyphenation_code = c; 8102} 8103 8104void charinfo::set_translation(charinfo *ci, int tt, int ti) 8105{ 8106 translation = ci; 8107 if (ci && ti) { 8108 if (hyphenation_code != 0) 8109 ci->set_hyphenation_code(hyphenation_code); 8110 if (asciify_code != 0) 8111 ci->set_asciify_code(asciify_code); 8112 else if (ascii_code != 0) 8113 ci->set_asciify_code(ascii_code); 8114 ci->set_translation_input(); 8115 } 8116 special_translation = TRANSLATE_NONE; 8117 transparent_translate = tt; 8118} 8119 8120void charinfo::set_special_translation(int c, int tt) 8121{ 8122 special_translation = c; 8123 translation = 0; 8124 transparent_translate = tt; 8125} 8126 8127void charinfo::set_ascii_code(unsigned char c) 8128{ 8129 ascii_code = c; 8130} 8131 8132void charinfo::set_asciify_code(unsigned char c) 8133{ 8134 asciify_code = c; 8135} 8136 8137macro *charinfo::set_macro(macro *m) 8138{ 8139 macro *tem = mac; 8140 mac = m; 8141 return tem; 8142} 8143 8144macro *charinfo::setx_macro(macro *m, char_mode cm) 8145{ 8146 macro *tem = mac; 8147 mac = m; 8148 mode = cm; 8149 return tem; 8150} 8151 8152void charinfo::set_number(int n) 8153{ 8154 number = n; 8155 flags |= NUMBERED; 8156} 8157 8158int charinfo::get_number() 8159{ 8160 assert(flags & NUMBERED); 8161 return number; 8162} 8163 8164symbol UNNAMED_SYMBOL("---"); 8165 8166// For numbered characters not between 0 and 255, we make a symbol out 8167// of the number and store them in this dictionary. 8168 8169dictionary numbered_charinfo_dictionary(11); 8170 8171charinfo *get_charinfo_by_number(int n) 8172{ 8173 static charinfo *number_table[256]; 8174 8175 if (n >= 0 && n < 256) { 8176 charinfo *ci = number_table[n]; 8177 if (!ci) { 8178 ci = new charinfo(UNNAMED_SYMBOL); 8179 ci->set_number(n); 8180 number_table[n] = ci; 8181 } 8182 return ci; 8183 } 8184 else { 8185 symbol ns(i_to_a(n)); 8186 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns); 8187 if (!ci) { 8188 ci = new charinfo(UNNAMED_SYMBOL); 8189 ci->set_number(n); 8190 (void)numbered_charinfo_dictionary.lookup(ns, ci); 8191 } 8192 return ci; 8193 } 8194} 8195 8196int font::name_to_index(const char *nm) 8197{ 8198 charinfo *ci; 8199 if (nm[1] == 0) 8200 ci = charset_table[nm[0] & 0xff]; 8201 else if (nm[0] == '\\' && nm[2] == 0) 8202 ci = get_charinfo(symbol(nm + 1)); 8203 else 8204 ci = get_charinfo(symbol(nm)); 8205 if (ci == 0) 8206 return -1; 8207 else 8208 return ci->get_index(); 8209} 8210 8211int font::number_to_index(int n) 8212{ 8213 return get_charinfo_by_number(n)->get_index(); 8214} 8215