1/* $NetBSD: input.cpp,v 1.1.1.3 2006/02/06 18:14:11 wiz Exp $ */ 2 3// -*- C++ -*- 4/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 5 Free Software Foundation, Inc. 6 Written by James Clark (jjc@jclark.com) 7 8This file is part of groff. 9 10groff is free software; you can redistribute it and/or modify it under 11the terms of the GNU General Public License as published by the Free 12Software Foundation; either version 2, or (at your option) any later 13version. 14 15groff is distributed in the hope that it will be useful, but WITHOUT ANY 16WARRANTY; without even the implied warranty of MERCHANTABILITY or 17FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18for more details. 19 20You should have received a copy of the GNU General Public License along 21with groff; see the file COPYING. If not, write to the Free Software 22Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 23 24#define DEBUGGING 25 26#include "troff.h" 27#include "dictionary.h" 28#include "hvunits.h" 29#include "stringclass.h" 30#include "mtsm.h" 31#include "env.h" 32#include "request.h" 33#include "node.h" 34#include "token.h" 35#include "div.h" 36#include "reg.h" 37#include "charinfo.h" 38#include "macropath.h" 39#include "input.h" 40#include "defs.h" 41#include "font.h" 42#include "unicode.h" 43 44// Needed for getpid() and isatty() 45#include "posix.h" 46 47#include "nonposix.h" 48 49#ifdef NEED_DECLARATION_PUTENV 50extern "C" { 51 int putenv(const char *); 52} 53#endif /* NEED_DECLARATION_PUTENV */ 54 55#define MACRO_PREFIX "tmac." 56#define MACRO_POSTFIX ".tmac" 57#define INITIAL_STARTUP_FILE "troffrc" 58#define FINAL_STARTUP_FILE "troffrc-end" 59#define DEFAULT_INPUT_STACK_LIMIT 1000 60 61#ifndef DEFAULT_WARNING_MASK 62// warnings that are enabled by default 63#define DEFAULT_WARNING_MASK \ 64 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT) 65#endif 66 67// initial size of buffer for reading names; expanded as necessary 68#define ABUF_SIZE 16 69 70extern "C" const char *program_name; 71extern "C" const char *Version_string; 72 73#ifdef COLUMN 74void init_column_requests(); 75#endif /* COLUMN */ 76 77static node *read_draw_node(); 78static void read_color_draw_node(token &); 79static void push_token(const token &); 80void copy_file(); 81#ifdef COLUMN 82void vjustify(); 83#endif /* COLUMN */ 84void transparent_file(); 85 86token tok; 87int break_flag = 0; 88int color_flag = 1; // colors are on by default 89static int backtrace_flag = 0; 90#ifndef POPEN_MISSING 91char *pipe_command = 0; 92#endif 93charinfo *charset_table[256]; 94unsigned char hpf_code_table[256]; 95 96static int warning_mask = DEFAULT_WARNING_MASK; 97static int inhibit_errors = 0; 98static int ignoring = 0; 99 100static void enable_warning(const char *); 101static void disable_warning(const char *); 102 103static int escape_char = '\\'; 104static symbol end_macro_name; 105static symbol blank_line_macro_name; 106static int compatible_flag = 0; 107int ascii_output_flag = 0; 108int suppress_output_flag = 0; 109int is_html = 0; 110int begin_level = 0; // number of nested \O escapes 111 112int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M, 113 // \R, \s, or \S has been processed in 114 // token::next() 115int old_have_input = 0; // value of have_input right before \n 116int tcommand_flag = 0; 117int safer_flag = 1; // safer by default 118 119int have_string_arg = 0; // whether we have \*[foo bar...] 120 121double spread_limit = -3.0 - 1.0; // negative means deactivated 122 123double warn_scale; 124char warn_scaling_indicator; 125int debug_state = 0; // turns on debugging of the html troff state 126 127search_path *mac_path = &safer_macro_path; 128 129// Defaults to the current directory. 130search_path include_search_path(0, 0, 0, 1); 131 132static int get_copy(node**, int = 0); 133static void copy_mode_error(const char *, 134 const errarg & = empty_errarg, 135 const errarg & = empty_errarg, 136 const errarg & = empty_errarg); 137 138enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS }; 139static symbol read_escape_name(read_mode mode = NO_ARGS); 140static symbol read_long_escape_name(read_mode mode = NO_ARGS); 141static void interpolate_string(symbol); 142static void interpolate_string_with_args(symbol); 143static void interpolate_macro(symbol); 144static void interpolate_number_format(symbol); 145static void interpolate_environment_variable(symbol); 146 147static symbol composite_glyph_name(symbol); 148static void interpolate_arg(symbol); 149static request_or_macro *lookup_request(symbol); 150static int get_delim_number(units *, unsigned char); 151static int get_delim_number(units *, unsigned char, units); 152static symbol do_get_long_name(int, char); 153static int get_line_arg(units *res, unsigned char si, charinfo **cp); 154static int read_size(int *); 155static symbol get_delim_name(); 156static void init_registers(); 157static void trapping_blank_line(); 158 159class input_iterator; 160input_iterator *make_temp_iterator(const char *); 161const char *input_char_description(int); 162 163void process_input_stack(); 164void chop_macro(); // declare to avoid friend name injection 165 166 167void set_escape_char() 168{ 169 if (has_arg()) { 170 if (tok.ch() == 0) { 171 error("bad escape character"); 172 escape_char = '\\'; 173 } 174 else 175 escape_char = tok.ch(); 176 } 177 else 178 escape_char = '\\'; 179 skip_line(); 180} 181 182void escape_off() 183{ 184 escape_char = 0; 185 skip_line(); 186} 187 188static int saved_escape_char = '\\'; 189 190void save_escape_char() 191{ 192 saved_escape_char = escape_char; 193 skip_line(); 194} 195 196void restore_escape_char() 197{ 198 escape_char = saved_escape_char; 199 skip_line(); 200} 201 202class input_iterator { 203public: 204 input_iterator(); 205 input_iterator(int is_div); 206 virtual ~input_iterator() {} 207 int get(node **); 208 friend class input_stack; 209 int is_diversion; 210 statem *diversion_state; 211protected: 212 const unsigned char *ptr; 213 const unsigned char *eptr; 214 input_iterator *next; 215private: 216 virtual int fill(node **); 217 virtual int peek(); 218 virtual int has_args() { return 0; } 219 virtual int nargs() { return 0; } 220 virtual input_iterator *get_arg(int) { return 0; } 221 virtual int get_location(int, const char **, int *) { return 0; } 222 virtual void backtrace() {} 223 virtual int set_location(const char *, int) { return 0; } 224 virtual int next_file(FILE *, const char *) { return 0; } 225 virtual void shift(int) {} 226 virtual int is_boundary() {return 0; } 227 virtual int is_file() { return 0; } 228 virtual int is_macro() { return 0; } 229 virtual void save_compatible_flag(int) {} 230 virtual int get_compatible_flag() { return 0; } 231}; 232 233input_iterator::input_iterator() 234: is_diversion(0), ptr(0), eptr(0) 235{ 236} 237 238input_iterator::input_iterator(int is_div) 239: is_diversion(is_div), ptr(0), eptr(0) 240{ 241} 242 243int input_iterator::fill(node **) 244{ 245 return EOF; 246} 247 248int input_iterator::peek() 249{ 250 return EOF; 251} 252 253inline int input_iterator::get(node **p) 254{ 255 return ptr < eptr ? *ptr++ : fill(p); 256} 257 258class input_boundary : public input_iterator { 259public: 260 int is_boundary() { return 1; } 261}; 262 263class input_return_boundary : public input_iterator { 264public: 265 int is_boundary() { return 2; } 266}; 267 268class file_iterator : public input_iterator { 269 FILE *fp; 270 int lineno; 271 const char *filename; 272 int popened; 273 int newline_flag; 274 int seen_escape; 275 enum { BUF_SIZE = 512 }; 276 unsigned char buf[BUF_SIZE]; 277 void close(); 278public: 279 file_iterator(FILE *, const char *, int = 0); 280 ~file_iterator(); 281 int fill(node **); 282 int peek(); 283 int get_location(int, const char **, int *); 284 void backtrace(); 285 int set_location(const char *, int); 286 int next_file(FILE *, const char *); 287 int is_file(); 288}; 289 290file_iterator::file_iterator(FILE *f, const char *fn, int po) 291: fp(f), lineno(1), filename(fn), popened(po), 292 newline_flag(0), seen_escape(0) 293{ 294 if ((font::use_charnames_in_special) && (fn != 0)) { 295 if (!the_output) 296 init_output(); 297 the_output->put_filename(fn); 298 } 299} 300 301file_iterator::~file_iterator() 302{ 303 close(); 304} 305 306void file_iterator::close() 307{ 308 if (fp == stdin) 309 clearerr(stdin); 310#ifndef POPEN_MISSING 311 else if (popened) 312 pclose(fp); 313#endif /* not POPEN_MISSING */ 314 else 315 fclose(fp); 316} 317 318int file_iterator::is_file() 319{ 320 return 1; 321} 322 323int file_iterator::next_file(FILE *f, const char *s) 324{ 325 close(); 326 filename = s; 327 fp = f; 328 lineno = 1; 329 newline_flag = 0; 330 seen_escape = 0; 331 popened = 0; 332 ptr = 0; 333 eptr = 0; 334 return 1; 335} 336 337int file_iterator::fill(node **) 338{ 339 if (newline_flag) 340 lineno++; 341 newline_flag = 0; 342 unsigned char *p = buf; 343 ptr = p; 344 unsigned char *e = p + BUF_SIZE; 345 while (p < e) { 346 int c = getc(fp); 347 if (c == EOF) 348 break; 349 if (invalid_input_char(c)) 350 warning(WARN_INPUT, "invalid input character code %1", int(c)); 351 else { 352 *p++ = c; 353 if (c == '\n') { 354 seen_escape = 0; 355 newline_flag = 1; 356 break; 357 } 358 seen_escape = (c == '\\'); 359 } 360 } 361 if (p > buf) { 362 eptr = p; 363 return *ptr++; 364 } 365 else { 366 eptr = p; 367 return EOF; 368 } 369} 370 371int file_iterator::peek() 372{ 373 int c = getc(fp); 374 while (invalid_input_char(c)) { 375 warning(WARN_INPUT, "invalid input character code %1", int(c)); 376 c = getc(fp); 377 } 378 if (c != EOF) 379 ungetc(c, fp); 380 return c; 381} 382 383int file_iterator::get_location(int /*allow_macro*/, 384 const char **filenamep, int *linenop) 385{ 386 *linenop = lineno; 387 if (filename != 0 && strcmp(filename, "-") == 0) 388 *filenamep = "<standard input>"; 389 else 390 *filenamep = filename; 391 return 1; 392} 393 394void file_iterator::backtrace() 395{ 396 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno, 397 popened ? "process" : "file"); 398} 399 400int file_iterator::set_location(const char *f, int ln) 401{ 402 if (f) { 403 filename = f; 404 if (!the_output) 405 init_output(); 406 the_output->put_filename(f); 407 } 408 lineno = ln; 409 return 1; 410} 411 412input_iterator nil_iterator; 413 414class input_stack { 415public: 416 static int get(node **); 417 static int peek(); 418 static void push(input_iterator *); 419 static input_iterator *get_arg(int); 420 static int nargs(); 421 static int get_location(int, const char **, int *); 422 static int set_location(const char *, int); 423 static void backtrace(); 424 static void backtrace_all(); 425 static void next_file(FILE *, const char *); 426 static void end_file(); 427 static void shift(int n); 428 static void add_boundary(); 429 static void add_return_boundary(); 430 static int is_return_boundary(); 431 static void remove_boundary(); 432 static int get_level(); 433 static int get_div_level(); 434 static void increase_level(); 435 static void decrease_level(); 436 static void clear(); 437 static void pop_macro(); 438 static void save_compatible_flag(int); 439 static int get_compatible_flag(); 440 static statem *get_diversion_state(); 441 static void check_end_diversion(input_iterator *t); 442 static int limit; 443 static int div_level; 444 static statem *diversion_state; 445private: 446 static input_iterator *top; 447 static int level; 448 static int finish_get(node **); 449 static int finish_peek(); 450}; 451 452input_iterator *input_stack::top = &nil_iterator; 453int input_stack::level = 0; 454int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT; 455int input_stack::div_level = 0; 456statem *input_stack::diversion_state = NULL; 457int suppress_push=0; 458 459 460inline int input_stack::get_level() 461{ 462 return level; 463} 464 465inline void input_stack::increase_level() 466{ 467 level++; 468} 469 470inline void input_stack::decrease_level() 471{ 472 level--; 473} 474 475inline int input_stack::get_div_level() 476{ 477 return div_level; 478} 479 480inline int input_stack::get(node **np) 481{ 482 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np); 483 if (res == '\n') { 484 old_have_input = have_input; 485 have_input = 0; 486 } 487 return res; 488} 489 490int input_stack::finish_get(node **np) 491{ 492 for (;;) { 493 int c = top->fill(np); 494 if (c != EOF || top->is_boundary()) 495 return c; 496 if (top == &nil_iterator) 497 break; 498 input_iterator *tem = top; 499 check_end_diversion(tem); 500#if defined(DEBUGGING) 501 if (debug_state) 502 if (tem->is_diversion) 503 fprintf(stderr, 504 "in diversion level = %d\n", input_stack::get_div_level()); 505#endif 506 top = top->next; 507 level--; 508 delete tem; 509 if (top->ptr < top->eptr) 510 return *top->ptr++; 511 } 512 assert(level == 0); 513 return EOF; 514} 515 516inline int input_stack::peek() 517{ 518 return (top->ptr < top->eptr) ? *top->ptr : finish_peek(); 519} 520 521void input_stack::check_end_diversion(input_iterator *t) 522{ 523 if (t->is_diversion) { 524 div_level--; 525 diversion_state = t->diversion_state; 526 } 527} 528 529int input_stack::finish_peek() 530{ 531 for (;;) { 532 int c = top->peek(); 533 if (c != EOF || top->is_boundary()) 534 return c; 535 if (top == &nil_iterator) 536 break; 537 input_iterator *tem = top; 538 check_end_diversion(tem); 539 top = top->next; 540 level--; 541 delete tem; 542 if (top->ptr < top->eptr) 543 return *top->ptr; 544 } 545 assert(level == 0); 546 return EOF; 547} 548 549void input_stack::add_boundary() 550{ 551 push(new input_boundary); 552} 553 554void input_stack::add_return_boundary() 555{ 556 push(new input_return_boundary); 557} 558 559int input_stack::is_return_boundary() 560{ 561 return top->is_boundary() == 2; 562} 563 564void input_stack::remove_boundary() 565{ 566 assert(top->is_boundary()); 567 input_iterator *temp = top->next; 568 check_end_diversion(top); 569 570 delete top; 571 top = temp; 572 level--; 573} 574 575void input_stack::push(input_iterator *in) 576{ 577 if (in == 0) 578 return; 579 if (++level > limit && limit > 0) 580 fatal("input stack limit exceeded (probable infinite loop)"); 581 in->next = top; 582 top = in; 583 if (top->is_diversion) { 584 div_level++; 585 in->diversion_state = diversion_state; 586 diversion_state = curenv->construct_state(0); 587#if defined(DEBUGGING) 588 if (debug_state) { 589 curenv->dump_troff_state(); 590 fflush(stderr); 591 } 592#endif 593 } 594#if defined(DEBUGGING) 595 if (debug_state) 596 if (top->is_diversion) { 597 fprintf(stderr, 598 "in diversion level = %d\n", input_stack::get_div_level()); 599 fflush(stderr); 600 } 601#endif 602} 603 604statem *get_diversion_state() 605{ 606 return input_stack::get_diversion_state(); 607} 608 609statem *input_stack::get_diversion_state() 610{ 611 if (diversion_state == NULL) 612 return NULL; 613 else 614 return new statem(diversion_state); 615} 616 617input_iterator *input_stack::get_arg(int i) 618{ 619 input_iterator *p; 620 for (p = top; p != 0; p = p->next) 621 if (p->has_args()) 622 return p->get_arg(i); 623 return 0; 624} 625 626void input_stack::shift(int n) 627{ 628 for (input_iterator *p = top; p; p = p->next) 629 if (p->has_args()) { 630 p->shift(n); 631 return; 632 } 633} 634 635int input_stack::nargs() 636{ 637 for (input_iterator *p =top; p != 0; p = p->next) 638 if (p->has_args()) 639 return p->nargs(); 640 return 0; 641} 642 643int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop) 644{ 645 for (input_iterator *p = top; p; p = p->next) 646 if (p->get_location(allow_macro, filenamep, linenop)) 647 return 1; 648 return 0; 649} 650 651void input_stack::backtrace() 652{ 653 const char *f; 654 int n; 655 // only backtrace down to (not including) the topmost file 656 for (input_iterator *p = top; 657 p && !p->get_location(0, &f, &n); 658 p = p->next) 659 p->backtrace(); 660} 661 662void input_stack::backtrace_all() 663{ 664 for (input_iterator *p = top; p; p = p->next) 665 p->backtrace(); 666} 667 668int input_stack::set_location(const char *filename, int lineno) 669{ 670 for (input_iterator *p = top; p; p = p->next) 671 if (p->set_location(filename, lineno)) 672 return 1; 673 return 0; 674} 675 676void input_stack::next_file(FILE *fp, const char *s) 677{ 678 input_iterator **pp; 679 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 680 if ((*pp)->next_file(fp, s)) 681 return; 682 if (++level > limit && limit > 0) 683 fatal("input stack limit exceeded"); 684 *pp = new file_iterator(fp, s); 685 (*pp)->next = &nil_iterator; 686} 687 688void input_stack::end_file() 689{ 690 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 691 if ((*pp)->is_file()) { 692 input_iterator *tem = *pp; 693 check_end_diversion(tem); 694 *pp = (*pp)->next; 695 delete tem; 696 level--; 697 return; 698 } 699} 700 701void input_stack::clear() 702{ 703 int nboundaries = 0; 704 while (top != &nil_iterator) { 705 if (top->is_boundary()) 706 nboundaries++; 707 input_iterator *tem = top; 708 check_end_diversion(tem); 709 top = top->next; 710 level--; 711 delete tem; 712 } 713 // Keep while_request happy. 714 for (; nboundaries > 0; --nboundaries) 715 add_return_boundary(); 716} 717 718void input_stack::pop_macro() 719{ 720 int nboundaries = 0; 721 int is_macro = 0; 722 do { 723 if (top->next == &nil_iterator) 724 break; 725 if (top->is_boundary()) 726 nboundaries++; 727 is_macro = top->is_macro(); 728 input_iterator *tem = top; 729 check_end_diversion(tem); 730 top = top->next; 731 level--; 732 delete tem; 733 } while (!is_macro); 734 // Keep while_request happy. 735 for (; nboundaries > 0; --nboundaries) 736 add_return_boundary(); 737} 738 739inline void input_stack::save_compatible_flag(int f) 740{ 741 top->save_compatible_flag(f); 742} 743 744inline int input_stack::get_compatible_flag() 745{ 746 return top->get_compatible_flag(); 747} 748 749void backtrace_request() 750{ 751 input_stack::backtrace_all(); 752 fflush(stderr); 753 skip_line(); 754} 755 756void next_file() 757{ 758 symbol nm = get_long_name(); 759 while (!tok.newline() && !tok.eof()) 760 tok.next(); 761 if (nm.is_null()) 762 input_stack::end_file(); 763 else { 764 errno = 0; 765 FILE *fp = include_search_path.open_file_cautious(nm.contents()); 766 if (!fp) 767 error("can't open `%1': %2", nm.contents(), strerror(errno)); 768 else 769 input_stack::next_file(fp, nm.contents()); 770 } 771 tok.next(); 772} 773 774void shift() 775{ 776 int n; 777 if (!has_arg() || !get_integer(&n)) 778 n = 1; 779 input_stack::shift(n); 780 skip_line(); 781} 782 783static char get_char_for_escape_name(int allow_space = 0) 784{ 785 int c = get_copy(0); 786 switch (c) { 787 case EOF: 788 copy_mode_error("end of input in escape name"); 789 return '\0'; 790 default: 791 if (!invalid_input_char(c)) 792 break; 793 // fall through 794 case '\n': 795 if (c == '\n') 796 input_stack::push(make_temp_iterator("\n")); 797 // fall through 798 case ' ': 799 if (c == ' ' && allow_space) 800 break; 801 // fall through 802 case '\t': 803 case '\001': 804 case '\b': 805 copy_mode_error("%1 is not allowed in an escape name", 806 input_char_description(c)); 807 return '\0'; 808 } 809 return c; 810} 811 812static symbol read_two_char_escape_name() 813{ 814 char buf[3]; 815 buf[0] = get_char_for_escape_name(); 816 if (buf[0] != '\0') { 817 buf[1] = get_char_for_escape_name(); 818 if (buf[1] == '\0') 819 buf[0] = 0; 820 else 821 buf[2] = 0; 822 } 823 return symbol(buf); 824} 825 826static symbol read_long_escape_name(read_mode mode) 827{ 828 int start_level = input_stack::get_level(); 829 char abuf[ABUF_SIZE]; 830 char *buf = abuf; 831 int buf_size = ABUF_SIZE; 832 int i = 0; 833 char c; 834 int have_char = 0; 835 for (;;) { 836 c = get_char_for_escape_name(have_char && mode == WITH_ARGS); 837 if (c == 0) { 838 if (buf != abuf) 839 a_delete buf; 840 return NULL_SYMBOL; 841 } 842 have_char = 1; 843 if (mode == WITH_ARGS && c == ' ') 844 break; 845 if (i + 2 > buf_size) { 846 if (buf == abuf) { 847 buf = new char[ABUF_SIZE*2]; 848 memcpy(buf, abuf, buf_size); 849 buf_size = ABUF_SIZE*2; 850 } 851 else { 852 char *old_buf = buf; 853 buf = new char[buf_size*2]; 854 memcpy(buf, old_buf, buf_size); 855 buf_size *= 2; 856 a_delete old_buf; 857 } 858 } 859 if (c == ']' && input_stack::get_level() == start_level) 860 break; 861 buf[i++] = c; 862 } 863 buf[i] = 0; 864 if (c == ' ') 865 have_string_arg = 1; 866 if (buf == abuf) { 867 if (i == 0) { 868 if (mode != ALLOW_EMPTY) 869 copy_mode_error("empty escape name"); 870 return EMPTY_SYMBOL; 871 } 872 return symbol(abuf); 873 } 874 else { 875 symbol s(buf); 876 a_delete buf; 877 return s; 878 } 879} 880 881static symbol read_escape_name(read_mode mode) 882{ 883 char c = get_char_for_escape_name(); 884 if (c == 0) 885 return NULL_SYMBOL; 886 if (c == '(') 887 return read_two_char_escape_name(); 888 if (c == '[' && !compatible_flag) 889 return read_long_escape_name(mode); 890 char buf[2]; 891 buf[0] = c; 892 buf[1] = '\0'; 893 return symbol(buf); 894} 895 896static symbol read_increment_and_escape_name(int *incp) 897{ 898 char c = get_char_for_escape_name(); 899 switch (c) { 900 case 0: 901 *incp = 0; 902 return NULL_SYMBOL; 903 case '(': 904 *incp = 0; 905 return read_two_char_escape_name(); 906 case '+': 907 *incp = 1; 908 return read_escape_name(); 909 case '-': 910 *incp = -1; 911 return read_escape_name(); 912 case '[': 913 if (!compatible_flag) { 914 *incp = 0; 915 return read_long_escape_name(); 916 } 917 break; 918 } 919 *incp = 0; 920 char buf[2]; 921 buf[0] = c; 922 buf[1] = '\0'; 923 return symbol(buf); 924} 925 926static int get_copy(node **nd, int defining) 927{ 928 for (;;) { 929 int c = input_stack::get(nd); 930 if (c == PUSH_GROFF_MODE) { 931 input_stack::save_compatible_flag(compatible_flag); 932 compatible_flag = 0; 933 continue; 934 } 935 if (c == PUSH_COMP_MODE) { 936 input_stack::save_compatible_flag(compatible_flag); 937 compatible_flag = 1; 938 continue; 939 } 940 if (c == POP_GROFFCOMP_MODE) { 941 compatible_flag = input_stack::get_compatible_flag(); 942 continue; 943 } 944 if (c == BEGIN_QUOTE) { 945 input_stack::increase_level(); 946 continue; 947 } 948 if (c == END_QUOTE) { 949 input_stack::decrease_level(); 950 continue; 951 } 952 if (c == ESCAPE_NEWLINE) { 953 if (defining) 954 return c; 955 do { 956 c = input_stack::get(nd); 957 } while (c == ESCAPE_NEWLINE); 958 } 959 if (c != escape_char || escape_char <= 0) 960 return c; 961 c = input_stack::peek(); 962 switch(c) { 963 case 0: 964 return escape_char; 965 case '"': 966 (void)input_stack::get(0); 967 while ((c = input_stack::get(0)) != '\n' && c != EOF) 968 ; 969 return c; 970 case '#': // Like \" but newline is ignored. 971 (void)input_stack::get(0); 972 while ((c = input_stack::get(0)) != '\n') 973 if (c == EOF) 974 return EOF; 975 break; 976 case '$': 977 { 978 (void)input_stack::get(0); 979 symbol s = read_escape_name(); 980 if (!(s.is_null() || s.is_empty())) 981 interpolate_arg(s); 982 break; 983 } 984 case '*': 985 { 986 (void)input_stack::get(0); 987 symbol s = read_escape_name(WITH_ARGS); 988 if (!(s.is_null() || s.is_empty())) { 989 if (have_string_arg) { 990 have_string_arg = 0; 991 interpolate_string_with_args(s); 992 } 993 else 994 interpolate_string(s); 995 } 996 break; 997 } 998 case 'a': 999 (void)input_stack::get(0); 1000 return '\001'; 1001 case 'e': 1002 (void)input_stack::get(0); 1003 return ESCAPE_e; 1004 case 'E': 1005 (void)input_stack::get(0); 1006 return ESCAPE_E; 1007 case 'n': 1008 { 1009 (void)input_stack::get(0); 1010 int inc; 1011 symbol s = read_increment_and_escape_name(&inc); 1012 if (!(s.is_null() || s.is_empty())) 1013 interpolate_number_reg(s, inc); 1014 break; 1015 } 1016 case 'g': 1017 { 1018 (void)input_stack::get(0); 1019 symbol s = read_escape_name(); 1020 if (!(s.is_null() || s.is_empty())) 1021 interpolate_number_format(s); 1022 break; 1023 } 1024 case 't': 1025 (void)input_stack::get(0); 1026 return '\t'; 1027 case 'V': 1028 { 1029 (void)input_stack::get(0); 1030 symbol s = read_escape_name(); 1031 if (!(s.is_null() || s.is_empty())) 1032 interpolate_environment_variable(s); 1033 break; 1034 } 1035 case '\n': 1036 (void)input_stack::get(0); 1037 if (defining) 1038 return ESCAPE_NEWLINE; 1039 break; 1040 case ' ': 1041 (void)input_stack::get(0); 1042 return ESCAPE_SPACE; 1043 case '~': 1044 (void)input_stack::get(0); 1045 return ESCAPE_TILDE; 1046 case ':': 1047 (void)input_stack::get(0); 1048 return ESCAPE_COLON; 1049 case '|': 1050 (void)input_stack::get(0); 1051 return ESCAPE_BAR; 1052 case '^': 1053 (void)input_stack::get(0); 1054 return ESCAPE_CIRCUMFLEX; 1055 case '{': 1056 (void)input_stack::get(0); 1057 return ESCAPE_LEFT_BRACE; 1058 case '}': 1059 (void)input_stack::get(0); 1060 return ESCAPE_RIGHT_BRACE; 1061 case '`': 1062 (void)input_stack::get(0); 1063 return ESCAPE_LEFT_QUOTE; 1064 case '\'': 1065 (void)input_stack::get(0); 1066 return ESCAPE_RIGHT_QUOTE; 1067 case '-': 1068 (void)input_stack::get(0); 1069 return ESCAPE_HYPHEN; 1070 case '_': 1071 (void)input_stack::get(0); 1072 return ESCAPE_UNDERSCORE; 1073 case 'c': 1074 (void)input_stack::get(0); 1075 return ESCAPE_c; 1076 case '!': 1077 (void)input_stack::get(0); 1078 return ESCAPE_BANG; 1079 case '?': 1080 (void)input_stack::get(0); 1081 return ESCAPE_QUESTION; 1082 case '&': 1083 (void)input_stack::get(0); 1084 return ESCAPE_AMPERSAND; 1085 case ')': 1086 (void)input_stack::get(0); 1087 return ESCAPE_RIGHT_PARENTHESIS; 1088 case '.': 1089 (void)input_stack::get(0); 1090 return c; 1091 case '%': 1092 (void)input_stack::get(0); 1093 return ESCAPE_PERCENT; 1094 default: 1095 if (c == escape_char) { 1096 (void)input_stack::get(0); 1097 return c; 1098 } 1099 else 1100 return escape_char; 1101 } 1102 } 1103} 1104 1105class non_interpreted_char_node : public node { 1106 unsigned char c; 1107public: 1108 non_interpreted_char_node(unsigned char); 1109 node *copy(); 1110 int interpret(macro *); 1111 int same(node *); 1112 const char *type(); 1113 int force_tprint(); 1114 int is_tag(); 1115}; 1116 1117int non_interpreted_char_node::same(node *nd) 1118{ 1119 return c == ((non_interpreted_char_node *)nd)->c; 1120} 1121 1122const char *non_interpreted_char_node::type() 1123{ 1124 return "non_interpreted_char_node"; 1125} 1126 1127int non_interpreted_char_node::force_tprint() 1128{ 1129 return 0; 1130} 1131 1132int non_interpreted_char_node::is_tag() 1133{ 1134 return 0; 1135} 1136 1137non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n) 1138{ 1139 assert(n != 0); 1140} 1141 1142node *non_interpreted_char_node::copy() 1143{ 1144 return new non_interpreted_char_node(c); 1145} 1146 1147int non_interpreted_char_node::interpret(macro *mac) 1148{ 1149 mac->append(c); 1150 return 1; 1151} 1152 1153static void do_width(); 1154static node *do_non_interpreted(); 1155static node *do_special(); 1156static node *do_suppress(symbol nm); 1157static void do_register(); 1158 1159dictionary color_dictionary(501); 1160 1161static color *lookup_color(symbol nm) 1162{ 1163 assert(!nm.is_null()); 1164 if (nm == default_symbol) 1165 return &default_color; 1166 color *c = (color *)color_dictionary.lookup(nm); 1167 if (c == 0) 1168 warning(WARN_COLOR, "color `%1' not defined", nm.contents()); 1169 return c; 1170} 1171 1172void do_glyph_color(symbol nm) 1173{ 1174 if (nm.is_null()) 1175 return; 1176 if (nm.is_empty()) 1177 curenv->set_glyph_color(curenv->get_prev_glyph_color()); 1178 else { 1179 color *tem = lookup_color(nm); 1180 if (tem) 1181 curenv->set_glyph_color(tem); 1182 else 1183 (void)color_dictionary.lookup(nm, new color(nm)); 1184 } 1185} 1186 1187void do_fill_color(symbol nm) 1188{ 1189 if (nm.is_null()) 1190 return; 1191 if (nm.is_empty()) 1192 curenv->set_fill_color(curenv->get_prev_fill_color()); 1193 else { 1194 color *tem = lookup_color(nm); 1195 if (tem) 1196 curenv->set_fill_color(tem); 1197 else 1198 (void)color_dictionary.lookup(nm, new color(nm)); 1199 } 1200} 1201 1202static unsigned int get_color_element(const char *scheme, const char *col) 1203{ 1204 units val; 1205 if (!get_number(&val, 'f')) { 1206 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme); 1207 tok.next(); 1208 return 0; 1209 } 1210 if (val < 0) { 1211 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col); 1212 return 0; 1213 } 1214 if (val > color::MAX_COLOR_VAL+1) { 1215 warning(WARN_RANGE, "%1 cannot be greater than 1", col); 1216 // we change 0x10000 to 0xffff 1217 return color::MAX_COLOR_VAL; 1218 } 1219 return (unsigned int)val; 1220} 1221 1222static color *read_rgb(char end = 0) 1223{ 1224 symbol component = do_get_long_name(0, end); 1225 if (component.is_null()) { 1226 warning(WARN_COLOR, "missing rgb color values"); 1227 return 0; 1228 } 1229 const char *s = component.contents(); 1230 color *col = new color; 1231 if (*s == '#') { 1232 if (!col->read_rgb(s)) { 1233 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s); 1234 delete col; 1235 return 0; 1236 } 1237 } 1238 else { 1239 if (!end) 1240 input_stack::push(make_temp_iterator(" ")); 1241 input_stack::push(make_temp_iterator(s)); 1242 tok.next(); 1243 unsigned int r = get_color_element("rgb color", "red component"); 1244 unsigned int g = get_color_element("rgb color", "green component"); 1245 unsigned int b = get_color_element("rgb color", "blue component"); 1246 col->set_rgb(r, g, b); 1247 } 1248 return col; 1249} 1250 1251static color *read_cmy(char end = 0) 1252{ 1253 symbol component = do_get_long_name(0, end); 1254 if (component.is_null()) { 1255 warning(WARN_COLOR, "missing cmy color values"); 1256 return 0; 1257 } 1258 const char *s = component.contents(); 1259 color *col = new color; 1260 if (*s == '#') { 1261 if (!col->read_cmy(s)) { 1262 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s); 1263 delete col; 1264 return 0; 1265 } 1266 } 1267 else { 1268 if (!end) 1269 input_stack::push(make_temp_iterator(" ")); 1270 input_stack::push(make_temp_iterator(s)); 1271 tok.next(); 1272 unsigned int c = get_color_element("cmy color", "cyan component"); 1273 unsigned int m = get_color_element("cmy color", "magenta component"); 1274 unsigned int y = get_color_element("cmy color", "yellow component"); 1275 col->set_cmy(c, m, y); 1276 } 1277 return col; 1278} 1279 1280static color *read_cmyk(char end = 0) 1281{ 1282 symbol component = do_get_long_name(0, end); 1283 if (component.is_null()) { 1284 warning(WARN_COLOR, "missing cmyk color values"); 1285 return 0; 1286 } 1287 const char *s = component.contents(); 1288 color *col = new color; 1289 if (*s == '#') { 1290 if (!col->read_cmyk(s)) { 1291 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s); 1292 delete col; 1293 return 0; 1294 } 1295 } 1296 else { 1297 if (!end) 1298 input_stack::push(make_temp_iterator(" ")); 1299 input_stack::push(make_temp_iterator(s)); 1300 tok.next(); 1301 unsigned int c = get_color_element("cmyk color", "cyan component"); 1302 unsigned int m = get_color_element("cmyk color", "magenta component"); 1303 unsigned int y = get_color_element("cmyk color", "yellow component"); 1304 unsigned int k = get_color_element("cmyk color", "black component"); 1305 col->set_cmyk(c, m, y, k); 1306 } 1307 return col; 1308} 1309 1310static color *read_gray(char end = 0) 1311{ 1312 symbol component = do_get_long_name(0, end); 1313 if (component.is_null()) { 1314 warning(WARN_COLOR, "missing gray values"); 1315 return 0; 1316 } 1317 const char *s = component.contents(); 1318 color *col = new color; 1319 if (*s == '#') { 1320 if (!col->read_gray(s)) { 1321 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s); 1322 delete col; 1323 return 0; 1324 } 1325 } 1326 else { 1327 if (!end) 1328 input_stack::push(make_temp_iterator("\n")); 1329 input_stack::push(make_temp_iterator(s)); 1330 tok.next(); 1331 unsigned int g = get_color_element("gray", "gray value"); 1332 col->set_gray(g); 1333 } 1334 return col; 1335} 1336 1337static void activate_color() 1338{ 1339 int n; 1340 if (has_arg() && get_integer(&n)) 1341 color_flag = n != 0; 1342 else 1343 color_flag = 1; 1344 skip_line(); 1345} 1346 1347static void define_color() 1348{ 1349 symbol color_name = get_long_name(1); 1350 if (color_name.is_null()) { 1351 skip_line(); 1352 return; 1353 } 1354 if (color_name == default_symbol) { 1355 warning(WARN_COLOR, "default color can't be redefined"); 1356 skip_line(); 1357 return; 1358 } 1359 symbol style = get_long_name(1); 1360 if (style.is_null()) { 1361 skip_line(); 1362 return; 1363 } 1364 color *col; 1365 if (strcmp(style.contents(), "rgb") == 0) 1366 col = read_rgb(); 1367 else if (strcmp(style.contents(), "cmyk") == 0) 1368 col = read_cmyk(); 1369 else if (strcmp(style.contents(), "gray") == 0) 1370 col = read_gray(); 1371 else if (strcmp(style.contents(), "grey") == 0) 1372 col = read_gray(); 1373 else if (strcmp(style.contents(), "cmy") == 0) 1374 col = read_cmy(); 1375 else { 1376 warning(WARN_COLOR, 1377 "unknown color space `%1'; use rgb, cmyk, gray or cmy", 1378 style.contents()); 1379 skip_line(); 1380 return; 1381 } 1382 if (col) { 1383 col->nm = color_name; 1384 (void)color_dictionary.lookup(color_name, col); 1385 } 1386 skip_line(); 1387} 1388 1389static node *do_overstrike() 1390{ 1391 token start; 1392 overstrike_node *on = new overstrike_node; 1393 int start_level = input_stack::get_level(); 1394 start.next(); 1395 for (;;) { 1396 tok.next(); 1397 if (tok.newline() || tok.eof()) { 1398 warning(WARN_DELIM, "missing closing delimiter"); 1399 input_stack::push(make_temp_iterator("\n")); 1400 break; 1401 } 1402 if (tok == start 1403 && (compatible_flag || input_stack::get_level() == start_level)) 1404 break; 1405 charinfo *ci = tok.get_char(1); 1406 if (ci) { 1407 node *n = curenv->make_char_node(ci); 1408 if (n) 1409 on->overstrike(n); 1410 } 1411 } 1412 return on; 1413} 1414 1415static node *do_bracket() 1416{ 1417 token start; 1418 bracket_node *bn = new bracket_node; 1419 start.next(); 1420 int start_level = input_stack::get_level(); 1421 for (;;) { 1422 tok.next(); 1423 if (tok.eof()) { 1424 warning(WARN_DELIM, "missing closing delimiter"); 1425 break; 1426 } 1427 if (tok.newline()) { 1428 warning(WARN_DELIM, "missing closing delimiter"); 1429 input_stack::push(make_temp_iterator("\n")); 1430 break; 1431 } 1432 if (tok == start 1433 && (compatible_flag || input_stack::get_level() == start_level)) 1434 break; 1435 charinfo *ci = tok.get_char(1); 1436 if (ci) { 1437 node *n = curenv->make_char_node(ci); 1438 if (n) 1439 bn->bracket(n); 1440 } 1441 } 1442 return bn; 1443} 1444 1445static int do_name_test() 1446{ 1447 token start; 1448 start.next(); 1449 int start_level = input_stack::get_level(); 1450 int bad_char = 0; 1451 int some_char = 0; 1452 for (;;) { 1453 tok.next(); 1454 if (tok.newline() || tok.eof()) { 1455 warning(WARN_DELIM, "missing closing delimiter"); 1456 input_stack::push(make_temp_iterator("\n")); 1457 break; 1458 } 1459 if (tok == start 1460 && (compatible_flag || input_stack::get_level() == start_level)) 1461 break; 1462 if (!tok.ch()) 1463 bad_char = 1; 1464 some_char = 1; 1465 } 1466 return some_char && !bad_char; 1467} 1468 1469static int do_expr_test() 1470{ 1471 token start; 1472 start.next(); 1473 int start_level = input_stack::get_level(); 1474 if (!start.delimiter(1)) 1475 return 0; 1476 tok.next(); 1477 // disable all warning and error messages temporarily 1478 int saved_warning_mask = warning_mask; 1479 int saved_inhibit_errors = inhibit_errors; 1480 warning_mask = 0; 1481 inhibit_errors = 1; 1482 int dummy; 1483 int result = get_number_rigidly(&dummy, 'u'); 1484 warning_mask = saved_warning_mask; 1485 inhibit_errors = saved_inhibit_errors; 1486 if (tok == start && input_stack::get_level() == start_level) 1487 return result; 1488 // ignore everything up to the delimiter in case we aren't right there 1489 for (;;) { 1490 tok.next(); 1491 if (tok.newline() || tok.eof()) { 1492 warning(WARN_DELIM, "missing closing delimiter"); 1493 input_stack::push(make_temp_iterator("\n")); 1494 break; 1495 } 1496 if (tok == start && input_stack::get_level() == start_level) 1497 break; 1498 } 1499 return 0; 1500} 1501 1502#if 0 1503static node *do_zero_width() 1504{ 1505 token start; 1506 start.next(); 1507 int start_level = input_stack::get_level(); 1508 environment env(curenv); 1509 environment *oldenv = curenv; 1510 curenv = &env; 1511 for (;;) { 1512 tok.next(); 1513 if (tok.newline() || tok.eof()) { 1514 error("missing closing delimiter"); 1515 break; 1516 } 1517 if (tok == start 1518 && (compatible_flag || input_stack::get_level() == start_level)) 1519 break; 1520 tok.process(); 1521 } 1522 curenv = oldenv; 1523 node *rev = env.extract_output_line(); 1524 node *n = 0; 1525 while (rev) { 1526 node *tem = rev; 1527 rev = rev->next; 1528 tem->next = n; 1529 n = tem; 1530 } 1531 return new zero_width_node(n); 1532} 1533 1534#else 1535 1536// It's undesirable for \Z to change environments, because then 1537// \n(.w won't work as expected. 1538 1539static node *do_zero_width() 1540{ 1541 node *rev = new dummy_node; 1542 token start; 1543 start.next(); 1544 int start_level = input_stack::get_level(); 1545 for (;;) { 1546 tok.next(); 1547 if (tok.newline() || tok.eof()) { 1548 warning(WARN_DELIM, "missing closing delimiter"); 1549 input_stack::push(make_temp_iterator("\n")); 1550 break; 1551 } 1552 if (tok == start 1553 && (compatible_flag || input_stack::get_level() == start_level)) 1554 break; 1555 if (!tok.add_to_node_list(&rev)) 1556 error("invalid token in argument to \\Z"); 1557 } 1558 node *n = 0; 1559 while (rev) { 1560 node *tem = rev; 1561 rev = rev->next; 1562 tem->next = n; 1563 n = tem; 1564 } 1565 return new zero_width_node(n); 1566} 1567 1568#endif 1569 1570token_node *node::get_token_node() 1571{ 1572 return 0; 1573} 1574 1575class token_node : public node { 1576public: 1577 token tk; 1578 token_node(const token &t); 1579 node *copy(); 1580 token_node *get_token_node(); 1581 int same(node *); 1582 const char *type(); 1583 int force_tprint(); 1584 int is_tag(); 1585}; 1586 1587token_node::token_node(const token &t) : tk(t) 1588{ 1589} 1590 1591node *token_node::copy() 1592{ 1593 return new token_node(tk); 1594} 1595 1596token_node *token_node::get_token_node() 1597{ 1598 return this; 1599} 1600 1601int token_node::same(node *nd) 1602{ 1603 return tk == ((token_node *)nd)->tk; 1604} 1605 1606const char *token_node::type() 1607{ 1608 return "token_node"; 1609} 1610 1611int token_node::force_tprint() 1612{ 1613 return 0; 1614} 1615 1616int token_node::is_tag() 1617{ 1618 return 0; 1619} 1620 1621token::token() : nd(0), type(TOKEN_EMPTY) 1622{ 1623} 1624 1625token::~token() 1626{ 1627 delete nd; 1628} 1629 1630token::token(const token &t) 1631: nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type) 1632{ 1633 // Use two statements to work around bug in SGI C++. 1634 node *tem = t.nd; 1635 nd = tem ? tem->copy() : 0; 1636} 1637 1638void token::operator=(const token &t) 1639{ 1640 delete nd; 1641 nm = t.nm; 1642 // Use two statements to work around bug in SGI C++. 1643 node *tem = t.nd; 1644 nd = tem ? tem->copy() : 0; 1645 c = t.c; 1646 val = t.val; 1647 dim = t.dim; 1648 type = t.type; 1649} 1650 1651void token::skip() 1652{ 1653 while (space()) 1654 next(); 1655} 1656 1657int has_arg() 1658{ 1659 while (tok.space()) 1660 tok.next(); 1661 return !tok.newline(); 1662} 1663 1664void token::make_space() 1665{ 1666 type = TOKEN_SPACE; 1667} 1668 1669void token::make_newline() 1670{ 1671 type = TOKEN_NEWLINE; 1672} 1673 1674void token::next() 1675{ 1676 if (nd) { 1677 delete nd; 1678 nd = 0; 1679 } 1680 units x; 1681 for (;;) { 1682 node *n = 0; 1683 int cc = input_stack::get(&n); 1684 if (cc != escape_char || escape_char == 0) { 1685 handle_normal_char: 1686 switch(cc) { 1687 case PUSH_GROFF_MODE: 1688 input_stack::save_compatible_flag(compatible_flag); 1689 compatible_flag = 0; 1690 continue; 1691 case PUSH_COMP_MODE: 1692 input_stack::save_compatible_flag(compatible_flag); 1693 compatible_flag = 1; 1694 continue; 1695 case POP_GROFFCOMP_MODE: 1696 compatible_flag = input_stack::get_compatible_flag(); 1697 continue; 1698 case BEGIN_QUOTE: 1699 input_stack::increase_level(); 1700 continue; 1701 case END_QUOTE: 1702 input_stack::decrease_level(); 1703 continue; 1704 case EOF: 1705 type = TOKEN_EOF; 1706 return; 1707 case TRANSPARENT_FILE_REQUEST: 1708 case TITLE_REQUEST: 1709 case COPY_FILE_REQUEST: 1710#ifdef COLUMN 1711 case VJUSTIFY_REQUEST: 1712#endif /* COLUMN */ 1713 type = TOKEN_REQUEST; 1714 c = cc; 1715 return; 1716 case BEGIN_TRAP: 1717 type = TOKEN_BEGIN_TRAP; 1718 return; 1719 case END_TRAP: 1720 type = TOKEN_END_TRAP; 1721 return; 1722 case LAST_PAGE_EJECTOR: 1723 seen_last_page_ejector = 1; 1724 // fall through 1725 case PAGE_EJECTOR: 1726 type = TOKEN_PAGE_EJECTOR; 1727 return; 1728 case ESCAPE_PERCENT: 1729 ESCAPE_PERCENT: 1730 type = TOKEN_HYPHEN_INDICATOR; 1731 return; 1732 case ESCAPE_SPACE: 1733 ESCAPE_SPACE: 1734 type = TOKEN_UNSTRETCHABLE_SPACE; 1735 return; 1736 case ESCAPE_TILDE: 1737 ESCAPE_TILDE: 1738 type = TOKEN_STRETCHABLE_SPACE; 1739 return; 1740 case ESCAPE_COLON: 1741 ESCAPE_COLON: 1742 type = TOKEN_ZERO_WIDTH_BREAK; 1743 return; 1744 case ESCAPE_e: 1745 ESCAPE_e: 1746 type = TOKEN_ESCAPE; 1747 return; 1748 case ESCAPE_E: 1749 goto handle_escape_char; 1750 case ESCAPE_BAR: 1751 ESCAPE_BAR: 1752 type = TOKEN_NODE; 1753 nd = new hmotion_node(curenv->get_narrow_space_width(), 1754 curenv->get_fill_color()); 1755 return; 1756 case ESCAPE_CIRCUMFLEX: 1757 ESCAPE_CIRCUMFLEX: 1758 type = TOKEN_NODE; 1759 nd = new hmotion_node(curenv->get_half_narrow_space_width(), 1760 curenv->get_fill_color()); 1761 return; 1762 case ESCAPE_NEWLINE: 1763 have_input = 0; 1764 break; 1765 case ESCAPE_LEFT_BRACE: 1766 ESCAPE_LEFT_BRACE: 1767 type = TOKEN_LEFT_BRACE; 1768 return; 1769 case ESCAPE_RIGHT_BRACE: 1770 ESCAPE_RIGHT_BRACE: 1771 type = TOKEN_RIGHT_BRACE; 1772 return; 1773 case ESCAPE_LEFT_QUOTE: 1774 ESCAPE_LEFT_QUOTE: 1775 type = TOKEN_SPECIAL; 1776 nm = symbol("ga"); 1777 return; 1778 case ESCAPE_RIGHT_QUOTE: 1779 ESCAPE_RIGHT_QUOTE: 1780 type = TOKEN_SPECIAL; 1781 nm = symbol("aa"); 1782 return; 1783 case ESCAPE_HYPHEN: 1784 ESCAPE_HYPHEN: 1785 type = TOKEN_SPECIAL; 1786 nm = symbol("-"); 1787 return; 1788 case ESCAPE_UNDERSCORE: 1789 ESCAPE_UNDERSCORE: 1790 type = TOKEN_SPECIAL; 1791 nm = symbol("ul"); 1792 return; 1793 case ESCAPE_c: 1794 ESCAPE_c: 1795 type = TOKEN_INTERRUPT; 1796 return; 1797 case ESCAPE_BANG: 1798 ESCAPE_BANG: 1799 type = TOKEN_TRANSPARENT; 1800 return; 1801 case ESCAPE_QUESTION: 1802 ESCAPE_QUESTION: 1803 nd = do_non_interpreted(); 1804 if (nd) { 1805 type = TOKEN_NODE; 1806 return; 1807 } 1808 break; 1809 case ESCAPE_AMPERSAND: 1810 ESCAPE_AMPERSAND: 1811 type = TOKEN_DUMMY; 1812 return; 1813 case ESCAPE_RIGHT_PARENTHESIS: 1814 ESCAPE_RIGHT_PARENTHESIS: 1815 type = TOKEN_TRANSPARENT_DUMMY; 1816 return; 1817 case '\b': 1818 type = TOKEN_BACKSPACE; 1819 return; 1820 case ' ': 1821 type = TOKEN_SPACE; 1822 return; 1823 case '\t': 1824 type = TOKEN_TAB; 1825 return; 1826 case '\n': 1827 type = TOKEN_NEWLINE; 1828 return; 1829 case '\001': 1830 type = TOKEN_LEADER; 1831 return; 1832 case 0: 1833 { 1834 assert(n != 0); 1835 token_node *tn = n->get_token_node(); 1836 if (tn) { 1837 *this = tn->tk; 1838 delete tn; 1839 } 1840 else { 1841 nd = n; 1842 type = TOKEN_NODE; 1843 } 1844 } 1845 return; 1846 default: 1847 type = TOKEN_CHAR; 1848 c = cc; 1849 return; 1850 } 1851 } 1852 else { 1853 handle_escape_char: 1854 cc = input_stack::get(&n); 1855 switch(cc) { 1856 case '(': 1857 nm = read_two_char_escape_name(); 1858 type = TOKEN_SPECIAL; 1859 return; 1860 case EOF: 1861 type = TOKEN_EOF; 1862 error("end of input after escape character"); 1863 return; 1864 case '`': 1865 goto ESCAPE_LEFT_QUOTE; 1866 case '\'': 1867 goto ESCAPE_RIGHT_QUOTE; 1868 case '-': 1869 goto ESCAPE_HYPHEN; 1870 case '_': 1871 goto ESCAPE_UNDERSCORE; 1872 case '%': 1873 goto ESCAPE_PERCENT; 1874 case ' ': 1875 goto ESCAPE_SPACE; 1876 case '0': 1877 nd = new hmotion_node(curenv->get_digit_width(), 1878 curenv->get_fill_color()); 1879 type = TOKEN_NODE; 1880 return; 1881 case '|': 1882 goto ESCAPE_BAR; 1883 case '^': 1884 goto ESCAPE_CIRCUMFLEX; 1885 case '/': 1886 type = TOKEN_ITALIC_CORRECTION; 1887 return; 1888 case ',': 1889 type = TOKEN_NODE; 1890 nd = new left_italic_corrected_node; 1891 return; 1892 case '&': 1893 goto ESCAPE_AMPERSAND; 1894 case ')': 1895 goto ESCAPE_RIGHT_PARENTHESIS; 1896 case '!': 1897 goto ESCAPE_BANG; 1898 case '?': 1899 goto ESCAPE_QUESTION; 1900 case '~': 1901 goto ESCAPE_TILDE; 1902 case ':': 1903 goto ESCAPE_COLON; 1904 case '"': 1905 while ((cc = input_stack::get(0)) != '\n' && cc != EOF) 1906 ; 1907 if (cc == '\n') 1908 type = TOKEN_NEWLINE; 1909 else 1910 type = TOKEN_EOF; 1911 return; 1912 case '#': // Like \" but newline is ignored. 1913 while ((cc = input_stack::get(0)) != '\n') 1914 if (cc == EOF) { 1915 type = TOKEN_EOF; 1916 return; 1917 } 1918 break; 1919 case '$': 1920 { 1921 symbol s = read_escape_name(); 1922 if (!(s.is_null() || s.is_empty())) 1923 interpolate_arg(s); 1924 break; 1925 } 1926 case '*': 1927 { 1928 symbol s = read_escape_name(WITH_ARGS); 1929 if (!(s.is_null() || s.is_empty())) { 1930 if (have_string_arg) { 1931 have_string_arg = 0; 1932 interpolate_string_with_args(s); 1933 } 1934 else 1935 interpolate_string(s); 1936 } 1937 break; 1938 } 1939 case 'a': 1940 nd = new non_interpreted_char_node('\001'); 1941 type = TOKEN_NODE; 1942 return; 1943 case 'A': 1944 c = '0' + do_name_test(); 1945 type = TOKEN_CHAR; 1946 return; 1947 case 'b': 1948 nd = do_bracket(); 1949 type = TOKEN_NODE; 1950 return; 1951 case 'B': 1952 c = '0' + do_expr_test(); 1953 type = TOKEN_CHAR; 1954 return; 1955 case 'c': 1956 goto ESCAPE_c; 1957 case 'C': 1958 nm = get_delim_name(); 1959 if (nm.is_null()) 1960 break; 1961 type = TOKEN_SPECIAL; 1962 return; 1963 case 'd': 1964 type = TOKEN_NODE; 1965 nd = new vmotion_node(curenv->get_size() / 2, 1966 curenv->get_fill_color()); 1967 return; 1968 case 'D': 1969 nd = read_draw_node(); 1970 if (!nd) 1971 break; 1972 type = TOKEN_NODE; 1973 return; 1974 case 'e': 1975 goto ESCAPE_e; 1976 case 'E': 1977 goto handle_escape_char; 1978 case 'f': 1979 { 1980 symbol s = read_escape_name(ALLOW_EMPTY); 1981 if (s.is_null()) 1982 break; 1983 const char *p; 1984 for (p = s.contents(); *p != '\0'; p++) 1985 if (!csdigit(*p)) 1986 break; 1987 if (*p || s.is_empty()) 1988 curenv->set_font(s); 1989 else 1990 curenv->set_font(atoi(s.contents())); 1991 if (!compatible_flag) 1992 have_input = 1; 1993 break; 1994 } 1995 case 'F': 1996 { 1997 symbol s = read_escape_name(ALLOW_EMPTY); 1998 if (s.is_null()) 1999 break; 2000 curenv->set_family(s); 2001 have_input = 1; 2002 break; 2003 } 2004 case 'g': 2005 { 2006 symbol s = read_escape_name(); 2007 if (!(s.is_null() || s.is_empty())) 2008 interpolate_number_format(s); 2009 break; 2010 } 2011 case 'h': 2012 if (!get_delim_number(&x, 'm')) 2013 break; 2014 type = TOKEN_NODE; 2015 nd = new hmotion_node(x, curenv->get_fill_color()); 2016 return; 2017 case 'H': 2018 // don't take height increments relative to previous height if 2019 // in compatibility mode 2020 if (!compatible_flag && curenv->get_char_height()) 2021 { 2022 if (get_delim_number(&x, 'z', curenv->get_char_height())) 2023 curenv->set_char_height(x); 2024 } 2025 else 2026 { 2027 if (get_delim_number(&x, 'z', curenv->get_requested_point_size())) 2028 curenv->set_char_height(x); 2029 } 2030 if (!compatible_flag) 2031 have_input = 1; 2032 break; 2033 case 'k': 2034 nm = read_escape_name(); 2035 if (nm.is_null() || nm.is_empty()) 2036 break; 2037 type = TOKEN_MARK_INPUT; 2038 return; 2039 case 'l': 2040 case 'L': 2041 { 2042 charinfo *s = 0; 2043 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s)) 2044 break; 2045 if (s == 0) 2046 s = get_charinfo(cc == 'l' ? "ru" : "br"); 2047 type = TOKEN_NODE; 2048 node *char_node = curenv->make_char_node(s); 2049 if (cc == 'l') 2050 nd = new hline_node(x, char_node); 2051 else 2052 nd = new vline_node(x, char_node); 2053 return; 2054 } 2055 case 'm': 2056 do_glyph_color(read_escape_name(ALLOW_EMPTY)); 2057 if (!compatible_flag) 2058 have_input = 1; 2059 break; 2060 case 'M': 2061 do_fill_color(read_escape_name(ALLOW_EMPTY)); 2062 if (!compatible_flag) 2063 have_input = 1; 2064 break; 2065 case 'n': 2066 { 2067 int inc; 2068 symbol s = read_increment_and_escape_name(&inc); 2069 if (!(s.is_null() || s.is_empty())) 2070 interpolate_number_reg(s, inc); 2071 break; 2072 } 2073 case 'N': 2074 if (!get_delim_number(&val, 0)) 2075 break; 2076 type = TOKEN_NUMBERED_CHAR; 2077 return; 2078 case 'o': 2079 nd = do_overstrike(); 2080 type = TOKEN_NODE; 2081 return; 2082 case 'O': 2083 nd = do_suppress(read_escape_name()); 2084 if (!nd) 2085 break; 2086 type = TOKEN_NODE; 2087 return; 2088 case 'p': 2089 type = TOKEN_SPREAD; 2090 return; 2091 case 'r': 2092 type = TOKEN_NODE; 2093 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color()); 2094 return; 2095 case 'R': 2096 do_register(); 2097 if (!compatible_flag) 2098 have_input = 1; 2099 break; 2100 case 's': 2101 if (read_size(&x)) 2102 curenv->set_size(x); 2103 if (!compatible_flag) 2104 have_input = 1; 2105 break; 2106 case 'S': 2107 if (get_delim_number(&x, 0)) 2108 curenv->set_char_slant(x); 2109 if (!compatible_flag) 2110 have_input = 1; 2111 break; 2112 case 't': 2113 type = TOKEN_NODE; 2114 nd = new non_interpreted_char_node('\t'); 2115 return; 2116 case 'u': 2117 type = TOKEN_NODE; 2118 nd = new vmotion_node(-curenv->get_size() / 2, 2119 curenv->get_fill_color()); 2120 return; 2121 case 'v': 2122 if (!get_delim_number(&x, 'v')) 2123 break; 2124 type = TOKEN_NODE; 2125 nd = new vmotion_node(x, curenv->get_fill_color()); 2126 return; 2127 case 'V': 2128 { 2129 symbol s = read_escape_name(); 2130 if (!(s.is_null() || s.is_empty())) 2131 interpolate_environment_variable(s); 2132 break; 2133 } 2134 case 'w': 2135 do_width(); 2136 break; 2137 case 'x': 2138 if (!get_delim_number(&x, 'v')) 2139 break; 2140 type = TOKEN_NODE; 2141 nd = new extra_size_node(x); 2142 return; 2143 case 'X': 2144 nd = do_special(); 2145 if (!nd) 2146 break; 2147 type = TOKEN_NODE; 2148 return; 2149 case 'Y': 2150 { 2151 symbol s = read_escape_name(); 2152 if (s.is_null() || s.is_empty()) 2153 break; 2154 request_or_macro *p = lookup_request(s); 2155 macro *m = p->to_macro(); 2156 if (!m) { 2157 error("can't transparently throughput a request"); 2158 break; 2159 } 2160 nd = new special_node(*m); 2161 type = TOKEN_NODE; 2162 return; 2163 } 2164 case 'z': 2165 { 2166 next(); 2167 if (type == TOKEN_NODE) 2168 nd = new zero_width_node(nd); 2169 else { 2170 charinfo *ci = get_char(1); 2171 if (ci == 0) 2172 break; 2173 node *gn = curenv->make_char_node(ci); 2174 if (gn == 0) 2175 break; 2176 nd = new zero_width_node(gn); 2177 type = TOKEN_NODE; 2178 } 2179 return; 2180 } 2181 case 'Z': 2182 nd = do_zero_width(); 2183 if (nd == 0) 2184 break; 2185 type = TOKEN_NODE; 2186 return; 2187 case '{': 2188 goto ESCAPE_LEFT_BRACE; 2189 case '}': 2190 goto ESCAPE_RIGHT_BRACE; 2191 case '\n': 2192 break; 2193 case '[': 2194 if (!compatible_flag) { 2195 symbol s = read_long_escape_name(WITH_ARGS); 2196 if (s.is_null() || s.is_empty()) 2197 break; 2198 if (have_string_arg) { 2199 have_string_arg = 0; 2200 nm = composite_glyph_name(s); 2201 } 2202 else { 2203 const char *gn = check_unicode_name(s.contents()); 2204 if (gn) { 2205 const char *gn_decomposed = decompose_unicode(gn); 2206 if (gn_decomposed) 2207 gn = &gn_decomposed[1]; 2208 const char *groff_gn = unicode_to_glyph_name(gn); 2209 if (groff_gn) 2210 nm = symbol(groff_gn); 2211 else { 2212 char *buf = new char[strlen(gn) + 1 + 1]; 2213 strcpy(buf, "u"); 2214 strcat(buf, gn); 2215 nm = symbol(buf); 2216 a_delete buf; 2217 } 2218 } 2219 else 2220 nm = symbol(s.contents()); 2221 } 2222 type = TOKEN_SPECIAL; 2223 return; 2224 } 2225 goto handle_normal_char; 2226 default: 2227 if (cc != escape_char && cc != '.') 2228 warning(WARN_ESCAPE, "escape character ignored before %1", 2229 input_char_description(cc)); 2230 goto handle_normal_char; 2231 } 2232 } 2233 } 2234} 2235 2236int token::operator==(const token &t) 2237{ 2238 if (type != t.type) 2239 return 0; 2240 switch(type) { 2241 case TOKEN_CHAR: 2242 return c == t.c; 2243 case TOKEN_SPECIAL: 2244 return nm == t.nm; 2245 case TOKEN_NUMBERED_CHAR: 2246 return val == t.val; 2247 default: 2248 return 1; 2249 } 2250} 2251 2252int token::operator!=(const token &t) 2253{ 2254 return !(*this == t); 2255} 2256 2257// is token a suitable delimiter (like ')? 2258 2259int token::delimiter(int err) 2260{ 2261 switch(type) { 2262 case TOKEN_CHAR: 2263 switch(c) { 2264 case '0': 2265 case '1': 2266 case '2': 2267 case '3': 2268 case '4': 2269 case '5': 2270 case '6': 2271 case '7': 2272 case '8': 2273 case '9': 2274 case '+': 2275 case '-': 2276 case '/': 2277 case '*': 2278 case '%': 2279 case '<': 2280 case '>': 2281 case '=': 2282 case '&': 2283 case ':': 2284 case '(': 2285 case ')': 2286 case '.': 2287 if (err) 2288 error("cannot use character `%1' as a starting delimiter", char(c)); 2289 return 0; 2290 default: 2291 return 1; 2292 } 2293 case TOKEN_NODE: 2294 case TOKEN_SPACE: 2295 case TOKEN_STRETCHABLE_SPACE: 2296 case TOKEN_UNSTRETCHABLE_SPACE: 2297 case TOKEN_TAB: 2298 case TOKEN_NEWLINE: 2299 if (err) 2300 error("cannot use %1 as a starting delimiter", description()); 2301 return 0; 2302 default: 2303 return 1; 2304 } 2305} 2306 2307const char *token::description() 2308{ 2309 static char buf[4]; 2310 switch (type) { 2311 case TOKEN_BACKSPACE: 2312 return "a backspace character"; 2313 case TOKEN_CHAR: 2314 buf[0] = '`'; 2315 buf[1] = c; 2316 buf[2] = '\''; 2317 buf[3] = '\0'; 2318 return buf; 2319 case TOKEN_DUMMY: 2320 return "`\\&'"; 2321 case TOKEN_ESCAPE: 2322 return "`\\e'"; 2323 case TOKEN_HYPHEN_INDICATOR: 2324 return "`\\%'"; 2325 case TOKEN_INTERRUPT: 2326 return "`\\c'"; 2327 case TOKEN_ITALIC_CORRECTION: 2328 return "`\\/'"; 2329 case TOKEN_LEADER: 2330 return "a leader character"; 2331 case TOKEN_LEFT_BRACE: 2332 return "`\\{'"; 2333 case TOKEN_MARK_INPUT: 2334 return "`\\k'"; 2335 case TOKEN_NEWLINE: 2336 return "newline"; 2337 case TOKEN_NODE: 2338 return "a node"; 2339 case TOKEN_NUMBERED_CHAR: 2340 return "`\\N'"; 2341 case TOKEN_RIGHT_BRACE: 2342 return "`\\}'"; 2343 case TOKEN_SPACE: 2344 return "a space"; 2345 case TOKEN_SPECIAL: 2346 return "a special character"; 2347 case TOKEN_SPREAD: 2348 return "`\\p'"; 2349 case TOKEN_STRETCHABLE_SPACE: 2350 return "`\\~'"; 2351 case TOKEN_UNSTRETCHABLE_SPACE: 2352 return "`\\ '"; 2353 case TOKEN_TAB: 2354 return "a tab character"; 2355 case TOKEN_TRANSPARENT: 2356 return "`\\!'"; 2357 case TOKEN_TRANSPARENT_DUMMY: 2358 return "`\\)'"; 2359 case TOKEN_ZERO_WIDTH_BREAK: 2360 return "`\\:'"; 2361 case TOKEN_EOF: 2362 return "end of input"; 2363 default: 2364 break; 2365 } 2366 return "a magic token"; 2367} 2368 2369void skip_line() 2370{ 2371 while (!tok.newline()) 2372 if (tok.eof()) 2373 return; 2374 else 2375 tok.next(); 2376 tok.next(); 2377} 2378 2379void compatible() 2380{ 2381 int n; 2382 if (has_arg() && get_integer(&n)) 2383 compatible_flag = n != 0; 2384 else 2385 compatible_flag = 1; 2386 skip_line(); 2387} 2388 2389static void empty_name_warning(int required) 2390{ 2391 if (tok.newline() || tok.eof()) { 2392 if (required) 2393 warning(WARN_MISSING, "missing name"); 2394 } 2395 else if (tok.right_brace() || tok.tab()) { 2396 const char *start = tok.description(); 2397 do { 2398 tok.next(); 2399 } while (tok.space() || tok.right_brace() || tok.tab()); 2400 if (!tok.newline() && !tok.eof()) 2401 error("%1 is not allowed before an argument", start); 2402 else if (required) 2403 warning(WARN_MISSING, "missing name"); 2404 } 2405 else if (required) 2406 error("name expected (got %1)", tok.description()); 2407 else 2408 error("name expected (got %1): treated as missing", tok.description()); 2409} 2410 2411static void non_empty_name_warning() 2412{ 2413 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab() 2414 && !tok.right_brace() 2415 // We don't want to give a warning for .el\{ 2416 && !tok.left_brace()) 2417 error("%1 is not allowed in a name", tok.description()); 2418} 2419 2420symbol get_name(int required) 2421{ 2422 if (compatible_flag) { 2423 char buf[3]; 2424 tok.skip(); 2425 if ((buf[0] = tok.ch()) != 0) { 2426 tok.next(); 2427 if ((buf[1] = tok.ch()) != 0) { 2428 buf[2] = 0; 2429 tok.make_space(); 2430 } 2431 else 2432 non_empty_name_warning(); 2433 return symbol(buf); 2434 } 2435 else { 2436 empty_name_warning(required); 2437 return NULL_SYMBOL; 2438 } 2439 } 2440 else 2441 return get_long_name(required); 2442} 2443 2444symbol get_long_name(int required) 2445{ 2446 return do_get_long_name(required, 0); 2447} 2448 2449static symbol do_get_long_name(int required, char end) 2450{ 2451 while (tok.space()) 2452 tok.next(); 2453 char abuf[ABUF_SIZE]; 2454 char *buf = abuf; 2455 int buf_size = ABUF_SIZE; 2456 int i = 0; 2457 for (;;) { 2458 // If end != 0 we normally have to append a null byte 2459 if (i + 2 > buf_size) { 2460 if (buf == abuf) { 2461 buf = new char[ABUF_SIZE*2]; 2462 memcpy(buf, abuf, buf_size); 2463 buf_size = ABUF_SIZE*2; 2464 } 2465 else { 2466 char *old_buf = buf; 2467 buf = new char[buf_size*2]; 2468 memcpy(buf, old_buf, buf_size); 2469 buf_size *= 2; 2470 a_delete old_buf; 2471 } 2472 } 2473 if ((buf[i] = tok.ch()) == 0 || buf[i] == end) 2474 break; 2475 i++; 2476 tok.next(); 2477 } 2478 if (i == 0) { 2479 empty_name_warning(required); 2480 return NULL_SYMBOL; 2481 } 2482 if (end && buf[i] == end) 2483 buf[i+1] = '\0'; 2484 else 2485 non_empty_name_warning(); 2486 if (buf == abuf) 2487 return symbol(buf); 2488 else { 2489 symbol s(buf); 2490 a_delete buf; 2491 return s; 2492 } 2493} 2494 2495void exit_troff() 2496{ 2497 exit_started = 1; 2498 topdiv->set_last_page(); 2499 if (!end_macro_name.is_null()) { 2500 spring_trap(end_macro_name); 2501 tok.next(); 2502 process_input_stack(); 2503 } 2504 curenv->final_break(); 2505 tok.next(); 2506 process_input_stack(); 2507 end_diversions(); 2508 if (topdiv->get_page_length() > 0) { 2509 done_end_macro = 1; 2510 topdiv->set_ejecting(); 2511 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' }; 2512 input_stack::push(make_temp_iterator((char *)buf)); 2513 topdiv->space(topdiv->get_page_length(), 1); 2514 tok.next(); 2515 process_input_stack(); 2516 seen_last_page_ejector = 1; // should be set already 2517 topdiv->set_ejecting(); 2518 push_page_ejector(); 2519 topdiv->space(topdiv->get_page_length(), 1); 2520 tok.next(); 2521 process_input_stack(); 2522 } 2523 // This will only happen if a trap-invoked macro starts a diversion, 2524 // or if vertical position traps have been disabled. 2525 cleanup_and_exit(0); 2526} 2527 2528// This implements .ex. The input stack must be cleared before calling 2529// exit_troff(). 2530 2531void exit_request() 2532{ 2533 input_stack::clear(); 2534 if (exit_started) 2535 tok.next(); 2536 else 2537 exit_troff(); 2538} 2539 2540void return_macro_request() 2541{ 2542 if (has_arg() && tok.ch()) 2543 input_stack::pop_macro(); 2544 input_stack::pop_macro(); 2545 tok.next(); 2546} 2547 2548void end_macro() 2549{ 2550 end_macro_name = get_name(); 2551 skip_line(); 2552} 2553 2554void blank_line_macro() 2555{ 2556 blank_line_macro_name = get_name(); 2557 skip_line(); 2558} 2559 2560static void trapping_blank_line() 2561{ 2562 if (!blank_line_macro_name.is_null()) 2563 spring_trap(blank_line_macro_name); 2564 else 2565 blank_line(); 2566} 2567 2568void do_request() 2569{ 2570 int old_compatible_flag = compatible_flag; 2571 compatible_flag = 0; 2572 symbol nm = get_name(); 2573 if (nm.is_null()) 2574 skip_line(); 2575 else 2576 interpolate_macro(nm); 2577 compatible_flag = old_compatible_flag; 2578} 2579 2580inline int possibly_handle_first_page_transition() 2581{ 2582 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) { 2583 handle_first_page_transition(); 2584 return 1; 2585 } 2586 else 2587 return 0; 2588} 2589 2590static int transparent_translate(int cc) 2591{ 2592 if (!invalid_input_char(cc)) { 2593 charinfo *ci = charset_table[cc]; 2594 switch (ci->get_special_translation(1)) { 2595 case charinfo::TRANSLATE_SPACE: 2596 return ' '; 2597 case charinfo::TRANSLATE_STRETCHABLE_SPACE: 2598 return ESCAPE_TILDE; 2599 case charinfo::TRANSLATE_DUMMY: 2600 return ESCAPE_AMPERSAND; 2601 case charinfo::TRANSLATE_HYPHEN_INDICATOR: 2602 return ESCAPE_PERCENT; 2603 } 2604 // This is really ugly. 2605 ci = ci->get_translation(1); 2606 if (ci) { 2607 int c = ci->get_ascii_code(); 2608 if (c != '\0') 2609 return c; 2610 error("can't translate %1 to special character `%2'" 2611 " in transparent throughput", 2612 input_char_description(cc), 2613 ci->nm.contents()); 2614 } 2615 } 2616 return cc; 2617} 2618 2619class int_stack { 2620 struct int_stack_element { 2621 int n; 2622 int_stack_element *next; 2623 } *top; 2624public: 2625 int_stack(); 2626 ~int_stack(); 2627 void push(int); 2628 int is_empty(); 2629 int pop(); 2630}; 2631 2632int_stack::int_stack() 2633{ 2634 top = 0; 2635} 2636 2637int_stack::~int_stack() 2638{ 2639 while (top != 0) { 2640 int_stack_element *temp = top; 2641 top = top->next; 2642 delete temp; 2643 } 2644} 2645 2646int int_stack::is_empty() 2647{ 2648 return top == 0; 2649} 2650 2651void int_stack::push(int n) 2652{ 2653 int_stack_element *p = new int_stack_element; 2654 p->next = top; 2655 p->n = n; 2656 top = p; 2657} 2658 2659int int_stack::pop() 2660{ 2661 assert(top != 0); 2662 int_stack_element *p = top; 2663 top = top->next; 2664 int n = p->n; 2665 delete p; 2666 return n; 2667} 2668 2669int node::reread(int *) 2670{ 2671 return 0; 2672} 2673 2674int global_diverted_space = 0; 2675 2676int diverted_space_node::reread(int *bolp) 2677{ 2678 global_diverted_space = 1; 2679 if (curenv->get_fill()) 2680 trapping_blank_line(); 2681 else 2682 curdiv->space(n); 2683 global_diverted_space = 0; 2684 *bolp = 1; 2685 return 1; 2686} 2687 2688int diverted_copy_file_node::reread(int *bolp) 2689{ 2690 curdiv->copy_file(filename.contents()); 2691 *bolp = 1; 2692 return 1; 2693} 2694 2695int word_space_node::reread(int *) 2696{ 2697 if (unformat) { 2698 for (width_list *w = orig_width; w; w = w->next) 2699 curenv->space(w->width, w->sentence_width); 2700 unformat = 0; 2701 return 1; 2702 } 2703 return 0; 2704} 2705 2706int unbreakable_space_node::reread(int *) 2707{ 2708 return 0; 2709} 2710 2711int hmotion_node::reread(int *) 2712{ 2713 if (unformat && was_tab) { 2714 curenv->handle_tab(0); 2715 unformat = 0; 2716 return 1; 2717 } 2718 return 0; 2719} 2720 2721void process_input_stack() 2722{ 2723 int_stack trap_bol_stack; 2724 int bol = 1; 2725 for (;;) { 2726 int suppress_next = 0; 2727 switch (tok.type) { 2728 case token::TOKEN_CHAR: 2729 { 2730 unsigned char ch = tok.c; 2731 if (bol && !have_input 2732 && (ch == curenv->control_char 2733 || ch == curenv->no_break_control_char)) { 2734 break_flag = ch == curenv->control_char; 2735 // skip tabs as well as spaces here 2736 do { 2737 tok.next(); 2738 } while (tok.white_space()); 2739 symbol nm = get_name(); 2740#if defined(DEBUGGING) 2741 if (debug_state) { 2742 if (! nm.is_null()) { 2743 if (strcmp(nm.contents(), "test") == 0) { 2744 fprintf(stderr, "found it!\n"); 2745 fflush(stderr); 2746 } 2747 fprintf(stderr, "interpreting [%s]", nm.contents()); 2748 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv) 2749 fprintf(stderr, " currently in diversion: %s", 2750 curdiv->get_diversion_name()); 2751 fprintf(stderr, "\n"); 2752 fflush(stderr); 2753 } 2754 } 2755#endif 2756 if (nm.is_null()) 2757 skip_line(); 2758 else { 2759 interpolate_macro(nm); 2760#if defined(DEBUGGING) 2761 if (debug_state) { 2762 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents()); 2763 curenv->dump_troff_state(); 2764 } 2765#endif 2766 } 2767 suppress_next = 1; 2768 } 2769 else { 2770 if (possibly_handle_first_page_transition()) 2771 ; 2772 else { 2773 for (;;) { 2774#if defined(DEBUGGING) 2775 if (debug_state) { 2776 fprintf(stderr, "found [%c]\n", ch); fflush(stderr); 2777 } 2778#endif 2779 curenv->add_char(charset_table[ch]); 2780 tok.next(); 2781 if (tok.type != token::TOKEN_CHAR) 2782 break; 2783 ch = tok.c; 2784 } 2785 suppress_next = 1; 2786 bol = 0; 2787 } 2788 } 2789 break; 2790 } 2791 case token::TOKEN_TRANSPARENT: 2792 { 2793 if (bol) { 2794 if (possibly_handle_first_page_transition()) 2795 ; 2796 else { 2797 int cc; 2798 do { 2799 node *n; 2800 cc = get_copy(&n); 2801 if (cc != EOF) { 2802 if (cc != '\0') 2803 curdiv->transparent_output(transparent_translate(cc)); 2804 else 2805 curdiv->transparent_output(n); 2806 } 2807 } while (cc != '\n' && cc != EOF); 2808 if (cc == EOF) 2809 curdiv->transparent_output('\n'); 2810 } 2811 } 2812 break; 2813 } 2814 case token::TOKEN_NEWLINE: 2815 { 2816 if (bol && !old_have_input 2817 && !curenv->get_prev_line_interrupted()) 2818 trapping_blank_line(); 2819 else { 2820 curenv->newline(); 2821 bol = 1; 2822 } 2823 break; 2824 } 2825 case token::TOKEN_REQUEST: 2826 { 2827 int request_code = tok.c; 2828 tok.next(); 2829 switch (request_code) { 2830 case TITLE_REQUEST: 2831 title(); 2832 break; 2833 case COPY_FILE_REQUEST: 2834 copy_file(); 2835 break; 2836 case TRANSPARENT_FILE_REQUEST: 2837 transparent_file(); 2838 break; 2839#ifdef COLUMN 2840 case VJUSTIFY_REQUEST: 2841 vjustify(); 2842 break; 2843#endif /* COLUMN */ 2844 default: 2845 assert(0); 2846 break; 2847 } 2848 suppress_next = 1; 2849 break; 2850 } 2851 case token::TOKEN_SPACE: 2852 { 2853 if (possibly_handle_first_page_transition()) 2854 ; 2855 else if (bol && !curenv->get_prev_line_interrupted()) { 2856 int nspaces = 0; 2857 // save space_width now so that it isn't changed by \f or \s 2858 // which we wouldn't notice here 2859 hunits space_width = curenv->get_space_width(); 2860 do { 2861 nspaces += tok.nspaces(); 2862 tok.next(); 2863 } while (tok.space()); 2864 if (tok.newline()) 2865 trapping_blank_line(); 2866 else { 2867 push_token(tok); 2868 curenv->do_break(); 2869 curenv->add_node(new hmotion_node(space_width * nspaces, 2870 curenv->get_fill_color())); 2871 bol = 0; 2872 } 2873 } 2874 else { 2875 curenv->space(); 2876 bol = 0; 2877 } 2878 break; 2879 } 2880 case token::TOKEN_EOF: 2881 return; 2882 case token::TOKEN_NODE: 2883 { 2884 if (possibly_handle_first_page_transition()) 2885 ; 2886 else if (tok.nd->reread(&bol)) { 2887 delete tok.nd; 2888 tok.nd = 0; 2889 } 2890 else { 2891 curenv->add_node(tok.nd); 2892 tok.nd = 0; 2893 bol = 0; 2894 curenv->possibly_break_line(1); 2895 } 2896 break; 2897 } 2898 case token::TOKEN_PAGE_EJECTOR: 2899 { 2900 continue_page_eject(); 2901 // I think we just want to preserve bol. 2902 // bol = 1; 2903 break; 2904 } 2905 case token::TOKEN_BEGIN_TRAP: 2906 { 2907 trap_bol_stack.push(bol); 2908 bol = 1; 2909 have_input = 0; 2910 break; 2911 } 2912 case token::TOKEN_END_TRAP: 2913 { 2914 if (trap_bol_stack.is_empty()) 2915 error("spurious end trap token detected!"); 2916 else 2917 bol = trap_bol_stack.pop(); 2918 have_input = 0; 2919 2920 /* I'm not totally happy about this. But I can't think of any other 2921 way to do it. Doing an output_pending_lines() whenever a 2922 TOKEN_END_TRAP is detected doesn't work: for example, 2923 2924 .wh -1i x 2925 .de x 2926 'bp 2927 .. 2928 .wh -.5i y 2929 .de y 2930 .tl ''-%-'' 2931 .. 2932 .br 2933 .ll .5i 2934 .sp |\n(.pu-1i-.5v 2935 a\%very\%very\%long\%word 2936 2937 will print all but the first lines from the word immediately 2938 after the footer, rather than on the next page. */ 2939 2940 if (trap_bol_stack.is_empty()) 2941 curenv->output_pending_lines(); 2942 break; 2943 } 2944 default: 2945 { 2946 bol = 0; 2947 tok.process(); 2948 break; 2949 } 2950 } 2951 if (!suppress_next) 2952 tok.next(); 2953 trap_sprung_flag = 0; 2954 } 2955} 2956 2957#ifdef WIDOW_CONTROL 2958 2959void flush_pending_lines() 2960{ 2961 while (!tok.newline() && !tok.eof()) 2962 tok.next(); 2963 curenv->output_pending_lines(); 2964 tok.next(); 2965} 2966 2967#endif /* WIDOW_CONTROL */ 2968 2969request_or_macro::request_or_macro() 2970{ 2971} 2972 2973macro *request_or_macro::to_macro() 2974{ 2975 return 0; 2976} 2977 2978request::request(REQUEST_FUNCP pp) : p(pp) 2979{ 2980} 2981 2982void request::invoke(symbol) 2983{ 2984 (*p)(); 2985} 2986 2987struct char_block { 2988 enum { SIZE = 128 }; 2989 unsigned char s[SIZE]; 2990 char_block *next; 2991 char_block(); 2992}; 2993 2994char_block::char_block() 2995: next(0) 2996{ 2997} 2998 2999class char_list { 3000public: 3001 char_list(); 3002 ~char_list(); 3003 void append(unsigned char); 3004 void set(unsigned char, int); 3005 unsigned char get(int); 3006 int length(); 3007private: 3008 unsigned char *ptr; 3009 int len; 3010 char_block *head; 3011 char_block *tail; 3012 friend class macro_header; 3013 friend class string_iterator; 3014}; 3015 3016char_list::char_list() 3017: ptr(0), len(0), head(0), tail(0) 3018{ 3019} 3020 3021char_list::~char_list() 3022{ 3023 while (head != 0) { 3024 char_block *tem = head; 3025 head = head->next; 3026 delete tem; 3027 } 3028} 3029 3030int char_list::length() 3031{ 3032 return len; 3033} 3034 3035void char_list::append(unsigned char c) 3036{ 3037 if (tail == 0) { 3038 head = tail = new char_block; 3039 ptr = tail->s; 3040 } 3041 else { 3042 if (ptr >= tail->s + char_block::SIZE) { 3043 tail->next = new char_block; 3044 tail = tail->next; 3045 ptr = tail->s; 3046 } 3047 } 3048 *ptr++ = c; 3049 len++; 3050} 3051 3052void char_list::set(unsigned char c, int offset) 3053{ 3054 assert(len > offset); 3055 // optimization for access at the end 3056 int boundary = len - len % char_block::SIZE; 3057 if (offset >= boundary) { 3058 *(tail->s + offset - boundary) = c; 3059 return; 3060 } 3061 char_block *tem = head; 3062 int l = 0; 3063 for (;;) { 3064 l += char_block::SIZE; 3065 if (l > offset) { 3066 *(tem->s + offset % char_block::SIZE) = c; 3067 return; 3068 } 3069 tem = tem->next; 3070 } 3071} 3072 3073unsigned char char_list::get(int offset) 3074{ 3075 assert(len > offset); 3076 // optimization for access at the end 3077 int boundary = len - len % char_block::SIZE; 3078 if (offset >= boundary) 3079 return *(tail->s + offset - boundary); 3080 char_block *tem = head; 3081 int l = 0; 3082 for (;;) { 3083 l += char_block::SIZE; 3084 if (l > offset) 3085 return *(tem->s + offset % char_block::SIZE); 3086 tem = tem->next; 3087 } 3088} 3089 3090class node_list { 3091 node *head; 3092 node *tail; 3093public: 3094 node_list(); 3095 ~node_list(); 3096 void append(node *); 3097 int length(); 3098 node *extract(); 3099 3100 friend class macro_header; 3101 friend class string_iterator; 3102}; 3103 3104void node_list::append(node *n) 3105{ 3106 if (head == 0) { 3107 n->next = 0; 3108 head = tail = n; 3109 } 3110 else { 3111 n->next = 0; 3112 tail = tail->next = n; 3113 } 3114} 3115 3116int node_list::length() 3117{ 3118 int total = 0; 3119 for (node *n = head; n != 0; n = n->next) 3120 ++total; 3121 return total; 3122} 3123 3124node_list::node_list() 3125{ 3126 head = tail = 0; 3127} 3128 3129node *node_list::extract() 3130{ 3131 node *temp = head; 3132 head = tail = 0; 3133 return temp; 3134} 3135 3136node_list::~node_list() 3137{ 3138 delete_node_list(head); 3139} 3140 3141class macro_header { 3142public: 3143 int count; 3144 char_list cl; 3145 node_list nl; 3146 macro_header() { count = 1; } 3147 macro_header *copy(int); 3148}; 3149 3150macro::~macro() 3151{ 3152 if (p != 0 && --(p->count) <= 0) 3153 delete p; 3154} 3155 3156macro::macro() 3157: is_a_diversion(0) 3158{ 3159 if (!input_stack::get_location(1, &filename, &lineno)) { 3160 filename = 0; 3161 lineno = 0; 3162 } 3163 len = 0; 3164 empty_macro = 1; 3165 p = 0; 3166} 3167 3168macro::macro(const macro &m) 3169: filename(m.filename), lineno(m.lineno), len(m.len), 3170 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p) 3171{ 3172 if (p != 0) 3173 p->count++; 3174} 3175 3176macro::macro(int is_div) 3177 : is_a_diversion(is_div) 3178{ 3179 if (!input_stack::get_location(1, &filename, &lineno)) { 3180 filename = 0; 3181 lineno = 0; 3182 } 3183 len = 0; 3184 empty_macro = 1; 3185 p = 0; 3186} 3187 3188int macro::is_diversion() 3189{ 3190 return is_a_diversion; 3191} 3192 3193macro ¯o::operator=(const macro &m) 3194{ 3195 // don't assign object 3196 if (m.p != 0) 3197 m.p->count++; 3198 if (p != 0 && --(p->count) <= 0) 3199 delete p; 3200 p = m.p; 3201 filename = m.filename; 3202 lineno = m.lineno; 3203 len = m.len; 3204 empty_macro = m.empty_macro; 3205 is_a_diversion = m.is_a_diversion; 3206 return *this; 3207} 3208 3209void macro::append(unsigned char c) 3210{ 3211 assert(c != 0); 3212 if (p == 0) 3213 p = new macro_header; 3214 if (p->cl.length() != len) { 3215 macro_header *tem = p->copy(len); 3216 if (--(p->count) <= 0) 3217 delete p; 3218 p = tem; 3219 } 3220 p->cl.append(c); 3221 ++len; 3222 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE) 3223 empty_macro = 0; 3224} 3225 3226void macro::set(unsigned char c, int offset) 3227{ 3228 assert(p != 0); 3229 assert(c != 0); 3230 p->cl.set(c, offset); 3231} 3232 3233unsigned char macro::get(int offset) 3234{ 3235 assert(p != 0); 3236 return p->cl.get(offset); 3237} 3238 3239int macro::length() 3240{ 3241 return len; 3242} 3243 3244void macro::append_str(const char *s) 3245{ 3246 int i = 0; 3247 3248 if (s) { 3249 while (s[i] != (char)0) { 3250 append(s[i]); 3251 i++; 3252 } 3253 } 3254} 3255 3256void macro::append(node *n) 3257{ 3258 assert(n != 0); 3259 if (p == 0) 3260 p = new macro_header; 3261 if (p->cl.length() != len) { 3262 macro_header *tem = p->copy(len); 3263 if (--(p->count) <= 0) 3264 delete p; 3265 p = tem; 3266 } 3267 p->cl.append(0); 3268 p->nl.append(n); 3269 ++len; 3270 empty_macro = 0; 3271} 3272 3273void macro::append_unsigned(unsigned int i) 3274{ 3275 unsigned int j = i / 10; 3276 if (j != 0) 3277 append_unsigned(j); 3278 append(((unsigned char)(((int)'0') + i % 10))); 3279} 3280 3281void macro::append_int(int i) 3282{ 3283 if (i < 0) { 3284 append('-'); 3285 i = -i; 3286 } 3287 append_unsigned((unsigned int)i); 3288} 3289 3290void macro::print_size() 3291{ 3292 errprint("%1", len); 3293} 3294 3295// make a copy of the first n bytes 3296 3297macro_header *macro_header::copy(int n) 3298{ 3299 macro_header *p = new macro_header; 3300 char_block *bp = cl.head; 3301 unsigned char *ptr = bp->s; 3302 node *nd = nl.head; 3303 while (--n >= 0) { 3304 if (ptr >= bp->s + char_block::SIZE) { 3305 bp = bp->next; 3306 ptr = bp->s; 3307 } 3308 unsigned char c = *ptr++; 3309 p->cl.append(c); 3310 if (c == 0) { 3311 p->nl.append(nd->copy()); 3312 nd = nd->next; 3313 } 3314 } 3315 return p; 3316} 3317 3318void print_macros() 3319{ 3320 object_dictionary_iterator iter(request_dictionary); 3321 request_or_macro *rm; 3322 symbol s; 3323 while (iter.get(&s, (object **)&rm)) { 3324 assert(!s.is_null()); 3325 macro *m = rm->to_macro(); 3326 if (m) { 3327 errprint("%1\t", s.contents()); 3328 m->print_size(); 3329 errprint("\n"); 3330 } 3331 } 3332 fflush(stderr); 3333 skip_line(); 3334} 3335 3336class string_iterator : public input_iterator { 3337 macro mac; 3338 const char *how_invoked; 3339 int newline_flag; 3340 int lineno; 3341 char_block *bp; 3342 int count; // of characters remaining 3343 node *nd; 3344 int saved_compatible_flag; 3345protected: 3346 symbol nm; 3347 string_iterator(); 3348public: 3349 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL); 3350 int fill(node **); 3351 int peek(); 3352 int get_location(int, const char **, int *); 3353 void backtrace(); 3354 void save_compatible_flag(int f) { saved_compatible_flag = f; } 3355 int get_compatible_flag() { return saved_compatible_flag; } 3356 int is_diversion(); 3357}; 3358 3359string_iterator::string_iterator(const macro &m, const char *p, symbol s) 3360: input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0), 3361 lineno(1), nm(s) 3362{ 3363 count = mac.len; 3364 if (count != 0) { 3365 bp = mac.p->cl.head; 3366 nd = mac.p->nl.head; 3367 ptr = eptr = bp->s; 3368 } 3369 else { 3370 bp = 0; 3371 nd = 0; 3372 ptr = eptr = 0; 3373 } 3374} 3375 3376string_iterator::string_iterator() 3377{ 3378 bp = 0; 3379 nd = 0; 3380 ptr = eptr = 0; 3381 newline_flag = 0; 3382 how_invoked = 0; 3383 lineno = 1; 3384 count = 0; 3385} 3386 3387int string_iterator::is_diversion() 3388{ 3389 return mac.is_diversion(); 3390} 3391 3392int string_iterator::fill(node **np) 3393{ 3394 if (newline_flag) 3395 lineno++; 3396 newline_flag = 0; 3397 if (count <= 0) 3398 return EOF; 3399 const unsigned char *p = eptr; 3400 if (p >= bp->s + char_block::SIZE) { 3401 bp = bp->next; 3402 p = bp->s; 3403 } 3404 if (*p == '\0') { 3405 if (np) { 3406 *np = nd->copy(); 3407 if (is_diversion()) 3408 (*np)->div_nest_level = input_stack::get_div_level(); 3409 else 3410 (*np)->div_nest_level = 0; 3411 } 3412 nd = nd->next; 3413 eptr = ptr = p + 1; 3414 count--; 3415 return 0; 3416 } 3417 const unsigned char *e = bp->s + char_block::SIZE; 3418 if (e - p > count) 3419 e = p + count; 3420 ptr = p; 3421 while (p < e) { 3422 unsigned char c = *p; 3423 if (c == '\n' || c == ESCAPE_NEWLINE) { 3424 newline_flag = 1; 3425 p++; 3426 break; 3427 } 3428 if (c == '\0') 3429 break; 3430 p++; 3431 } 3432 eptr = p; 3433 count -= p - ptr; 3434 return *ptr++; 3435} 3436 3437int string_iterator::peek() 3438{ 3439 if (count <= 0) 3440 return EOF; 3441 const unsigned char *p = eptr; 3442 if (p >= bp->s + char_block::SIZE) { 3443 p = bp->next->s; 3444 } 3445 return *p; 3446} 3447 3448int string_iterator::get_location(int allow_macro, 3449 const char **filep, int *linep) 3450{ 3451 if (!allow_macro) 3452 return 0; 3453 if (mac.filename == 0) 3454 return 0; 3455 *filep = mac.filename; 3456 *linep = mac.lineno + lineno - 1; 3457 return 1; 3458} 3459 3460void string_iterator::backtrace() 3461{ 3462 if (mac.filename) { 3463 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1); 3464 if (how_invoked) { 3465 if (!nm.is_null()) 3466 errprint(": %1 `%2'\n", how_invoked, nm.contents()); 3467 else 3468 errprint(": %1\n", how_invoked); 3469 } 3470 else 3471 errprint("\n"); 3472 } 3473} 3474 3475class temp_iterator : public input_iterator { 3476 unsigned char *base; 3477 temp_iterator(const char *, int len); 3478public: 3479 ~temp_iterator(); 3480 friend input_iterator *make_temp_iterator(const char *); 3481}; 3482 3483#ifdef __GNUG__ 3484inline 3485#endif 3486temp_iterator::temp_iterator(const char *s, int len) 3487{ 3488 base = new unsigned char[len]; 3489 memcpy(base, s, len); 3490 ptr = base; 3491 eptr = base + len; 3492} 3493 3494temp_iterator::~temp_iterator() 3495{ 3496 a_delete base; 3497} 3498 3499class small_temp_iterator : public input_iterator { 3500private: 3501 small_temp_iterator(const char *, int); 3502 ~small_temp_iterator(); 3503 enum { BLOCK = 16 }; 3504 static small_temp_iterator *free_list; 3505 void *operator new(size_t); 3506 void operator delete(void *); 3507 enum { SIZE = 12 }; 3508 unsigned char buf[SIZE]; 3509 friend input_iterator *make_temp_iterator(const char *); 3510}; 3511 3512small_temp_iterator *small_temp_iterator::free_list = 0; 3513 3514void *small_temp_iterator::operator new(size_t n) 3515{ 3516 assert(n == sizeof(small_temp_iterator)); 3517 if (!free_list) { 3518 free_list = 3519 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK]; 3520 for (int i = 0; i < BLOCK - 1; i++) 3521 free_list[i].next = free_list + i + 1; 3522 free_list[BLOCK-1].next = 0; 3523 } 3524 small_temp_iterator *p = free_list; 3525 free_list = (small_temp_iterator *)(free_list->next); 3526 p->next = 0; 3527 return p; 3528} 3529 3530#ifdef __GNUG__ 3531inline 3532#endif 3533void small_temp_iterator::operator delete(void *p) 3534{ 3535 if (p) { 3536 ((small_temp_iterator *)p)->next = free_list; 3537 free_list = (small_temp_iterator *)p; 3538 } 3539} 3540 3541small_temp_iterator::~small_temp_iterator() 3542{ 3543} 3544 3545#ifdef __GNUG__ 3546inline 3547#endif 3548small_temp_iterator::small_temp_iterator(const char *s, int len) 3549{ 3550 for (int i = 0; i < len; i++) 3551 buf[i] = s[i]; 3552 ptr = buf; 3553 eptr = buf + len; 3554} 3555 3556input_iterator *make_temp_iterator(const char *s) 3557{ 3558 if (s == 0) 3559 return new small_temp_iterator(s, 0); 3560 else { 3561 int n = strlen(s); 3562 if (n <= small_temp_iterator::SIZE) 3563 return new small_temp_iterator(s, n); 3564 else 3565 return new temp_iterator(s, n); 3566 } 3567} 3568 3569// this is used when macros with arguments are interpolated 3570 3571struct arg_list { 3572 macro mac; 3573 arg_list *next; 3574 arg_list(const macro &); 3575 ~arg_list(); 3576}; 3577 3578arg_list::arg_list(const macro &m) : mac(m), next(0) 3579{ 3580} 3581 3582arg_list::~arg_list() 3583{ 3584} 3585 3586class macro_iterator : public string_iterator { 3587 arg_list *args; 3588 int argc; 3589public: 3590 macro_iterator(symbol, macro &, const char *how_invoked = "macro"); 3591 macro_iterator(); 3592 ~macro_iterator(); 3593 int has_args() { return 1; } 3594 input_iterator *get_arg(int i); 3595 int nargs() { return argc; } 3596 void add_arg(const macro &m); 3597 void shift(int n); 3598 int is_macro() { return 1; } 3599 int is_diversion(); 3600}; 3601 3602input_iterator *macro_iterator::get_arg(int i) 3603{ 3604 if (i == 0) 3605 return make_temp_iterator(nm.contents()); 3606 if (i > 0 && i <= argc) { 3607 arg_list *p = args; 3608 for (int j = 1; j < i; j++) { 3609 assert(p != 0); 3610 p = p->next; 3611 } 3612 return new string_iterator(p->mac); 3613 } 3614 else 3615 return 0; 3616} 3617 3618void macro_iterator::add_arg(const macro &m) 3619{ 3620 arg_list **p; 3621 for (p = &args; *p; p = &((*p)->next)) 3622 ; 3623 *p = new arg_list(m); 3624 ++argc; 3625} 3626 3627void macro_iterator::shift(int n) 3628{ 3629 while (n > 0 && argc > 0) { 3630 arg_list *tem = args; 3631 args = args->next; 3632 delete tem; 3633 --argc; 3634 --n; 3635 } 3636} 3637 3638// This gets used by eg .if '\?xxx\?''. 3639 3640int operator==(const macro &m1, const macro &m2) 3641{ 3642 if (m1.len != m2.len) 3643 return 0; 3644 string_iterator iter1(m1); 3645 string_iterator iter2(m2); 3646 int n = m1.len; 3647 while (--n >= 0) { 3648 node *nd1 = 0; 3649 int c1 = iter1.get(&nd1); 3650 assert(c1 != EOF); 3651 node *nd2 = 0; 3652 int c2 = iter2.get(&nd2); 3653 assert(c2 != EOF); 3654 if (c1 != c2) { 3655 if (c1 == 0) 3656 delete nd1; 3657 else if (c2 == 0) 3658 delete nd2; 3659 return 0; 3660 } 3661 if (c1 == 0) { 3662 assert(nd1 != 0); 3663 assert(nd2 != 0); 3664 int are_same = nd1->type() == nd2->type() && nd1->same(nd2); 3665 delete nd1; 3666 delete nd2; 3667 if (!are_same) 3668 return 0; 3669 } 3670 } 3671 return 1; 3672} 3673 3674static void interpolate_macro(symbol nm) 3675{ 3676 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 3677 if (p == 0) { 3678 int warned = 0; 3679 const char *s = nm.contents(); 3680 if (strlen(s) > 2) { 3681 request_or_macro *r; 3682 char buf[3]; 3683 buf[0] = s[0]; 3684 buf[1] = s[1]; 3685 buf[2] = '\0'; 3686 r = (request_or_macro *)request_dictionary.lookup(symbol(buf)); 3687 if (r) { 3688 macro *m = r->to_macro(); 3689 if (!m || !m->empty()) 3690 warned = warning(WARN_SPACE, 3691 "macro `%1' not defined " 3692 "(probably missing space after `%2')", 3693 nm.contents(), buf); 3694 } 3695 } 3696 if (!warned) { 3697 warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 3698 p = new macro; 3699 request_dictionary.define(nm, p); 3700 } 3701 } 3702 if (p) 3703 p->invoke(nm); 3704 else { 3705 skip_line(); 3706 return; 3707 } 3708} 3709 3710static void decode_args(macro_iterator *mi) 3711{ 3712 if (!tok.newline() && !tok.eof()) { 3713 node *n; 3714 int c = get_copy(&n); 3715 for (;;) { 3716 while (c == ' ') 3717 c = get_copy(&n); 3718 if (c == '\n' || c == EOF) 3719 break; 3720 macro arg; 3721 int quote_input_level = 0; 3722 int done_tab_warning = 0; 3723 if (c == '"') { 3724 quote_input_level = input_stack::get_level(); 3725 c = get_copy(&n); 3726 } 3727 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE); 3728 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) { 3729 if (quote_input_level > 0 && c == '"' 3730 && (compatible_flag 3731 || input_stack::get_level() == quote_input_level)) { 3732 c = get_copy(&n); 3733 if (c == '"') { 3734 arg.append(c); 3735 c = get_copy(&n); 3736 } 3737 else 3738 break; 3739 } 3740 else { 3741 if (c == 0) 3742 arg.append(n); 3743 else { 3744 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3745 warning(WARN_TAB, "tab character in unquoted macro argument"); 3746 done_tab_warning = 1; 3747 } 3748 arg.append(c); 3749 } 3750 c = get_copy(&n); 3751 } 3752 } 3753 arg.append(POP_GROFFCOMP_MODE); 3754 mi->add_arg(arg); 3755 } 3756 } 3757} 3758 3759static void decode_string_args(macro_iterator *mi) 3760{ 3761 node *n; 3762 int c = get_copy(&n); 3763 for (;;) { 3764 while (c == ' ') 3765 c = get_copy(&n); 3766 if (c == '\n' || c == EOF) { 3767 error("missing `]'"); 3768 break; 3769 } 3770 if (c == ']') 3771 break; 3772 macro arg; 3773 int quote_input_level = 0; 3774 int done_tab_warning = 0; 3775 if (c == '"') { 3776 quote_input_level = input_stack::get_level(); 3777 c = get_copy(&n); 3778 } 3779 while (c != EOF && c != '\n' 3780 && !(c == ']' && quote_input_level == 0) 3781 && !(c == ' ' && quote_input_level == 0)) { 3782 if (quote_input_level > 0 && c == '"' 3783 && input_stack::get_level() == quote_input_level) { 3784 c = get_copy(&n); 3785 if (c == '"') { 3786 arg.append(c); 3787 c = get_copy(&n); 3788 } 3789 else 3790 break; 3791 } 3792 else { 3793 if (c == 0) 3794 arg.append(n); 3795 else { 3796 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3797 warning(WARN_TAB, "tab character in unquoted string argument"); 3798 done_tab_warning = 1; 3799 } 3800 arg.append(c); 3801 } 3802 c = get_copy(&n); 3803 } 3804 } 3805 mi->add_arg(arg); 3806 } 3807} 3808 3809void macro::invoke(symbol nm) 3810{ 3811 macro_iterator *mi = new macro_iterator(nm, *this); 3812 decode_args(mi); 3813 input_stack::push(mi); 3814 tok.next(); 3815} 3816 3817macro *macro::to_macro() 3818{ 3819 return this; 3820} 3821 3822int macro::empty() 3823{ 3824 return empty_macro == 1; 3825} 3826 3827macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called) 3828: string_iterator(m, how_called, s), args(0), argc(0) 3829{ 3830} 3831 3832macro_iterator::macro_iterator() : args(0), argc(0) 3833{ 3834} 3835 3836macro_iterator::~macro_iterator() 3837{ 3838 while (args != 0) { 3839 arg_list *tem = args; 3840 args = args->next; 3841 delete tem; 3842 } 3843} 3844 3845dictionary composite_dictionary(17); 3846 3847void composite_request() 3848{ 3849 symbol from = get_name(1); 3850 if (!from.is_null()) { 3851 const char *from_gn = glyph_name_to_unicode(from.contents()); 3852 if (!from_gn) { 3853 from_gn = check_unicode_name(from.contents()); 3854 if (!from_gn) { 3855 error("invalid composite glyph name `%1'", from.contents()); 3856 skip_line(); 3857 return; 3858 } 3859 } 3860 const char *from_decomposed = decompose_unicode(from_gn); 3861 if (from_decomposed) 3862 from_gn = &from_decomposed[1]; 3863 symbol to = get_name(1); 3864 if (to.is_null()) 3865 composite_dictionary.remove(symbol(from_gn)); 3866 else { 3867 const char *to_gn = glyph_name_to_unicode(to.contents()); 3868 if (!to_gn) { 3869 to_gn = check_unicode_name(to.contents()); 3870 if (!to_gn) { 3871 error("invalid composite glyph name `%1'", to.contents()); 3872 skip_line(); 3873 return; 3874 } 3875 } 3876 const char *to_decomposed = decompose_unicode(to_gn); 3877 if (to_decomposed) 3878 to_gn = &to_decomposed[1]; 3879 if (strcmp(from_gn, to_gn) == 0) 3880 composite_dictionary.remove(symbol(from_gn)); 3881 else 3882 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn); 3883 } 3884 } 3885 skip_line(); 3886} 3887 3888static symbol composite_glyph_name(symbol nm) 3889{ 3890 macro_iterator *mi = new macro_iterator(); 3891 decode_string_args(mi); 3892 input_stack::push(mi); 3893 const char *gn = glyph_name_to_unicode(nm.contents()); 3894 if (!gn) { 3895 gn = check_unicode_name(nm.contents()); 3896 if (!gn) { 3897 error("invalid base glyph `%1' in composite glyph name", nm.contents()); 3898 return EMPTY_SYMBOL; 3899 } 3900 } 3901 const char *gn_decomposed = decompose_unicode(gn); 3902 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn); 3903 string gl; 3904 int n = input_stack::nargs(); 3905 for (int i = 1; i <= n; i++) { 3906 glyph_name += '_'; 3907 input_iterator *p = input_stack::get_arg(i); 3908 gl.clear(); 3909 int c; 3910 while ((c = p->get(0)) != EOF) 3911 gl += c; 3912 gl += '\0'; 3913 const char *u = glyph_name_to_unicode(gl.contents()); 3914 if (!u) { 3915 u = check_unicode_name(gl.contents()); 3916 if (!u) { 3917 error("invalid component `%1' in composite glyph name", 3918 gl.contents()); 3919 return EMPTY_SYMBOL; 3920 } 3921 } 3922 const char *decomposed = decompose_unicode(u); 3923 if (decomposed) 3924 u = &decomposed[1]; 3925 void *mapped_composite = composite_dictionary.lookup(symbol(u)); 3926 if (mapped_composite) 3927 u = (const char *)mapped_composite; 3928 glyph_name += u; 3929 } 3930 glyph_name += '\0'; 3931 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents()); 3932 if (groff_gn) 3933 return symbol(groff_gn); 3934 gl.clear(); 3935 gl += 'u'; 3936 gl += glyph_name; 3937 return symbol(gl.contents()); 3938} 3939 3940int trap_sprung_flag = 0; 3941int postpone_traps_flag = 0; 3942symbol postponed_trap; 3943 3944void spring_trap(symbol nm) 3945{ 3946 assert(!nm.is_null()); 3947 trap_sprung_flag = 1; 3948 if (postpone_traps_flag) { 3949 postponed_trap = nm; 3950 return; 3951 } 3952 static char buf[2] = { BEGIN_TRAP, 0 }; 3953 static char buf2[2] = { END_TRAP, '\0' }; 3954 input_stack::push(make_temp_iterator(buf2)); 3955 request_or_macro *p = lookup_request(nm); 3956 macro *m = p->to_macro(); 3957 if (m) 3958 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro")); 3959 else 3960 error("you can't invoke a request with a trap"); 3961 input_stack::push(make_temp_iterator(buf)); 3962} 3963 3964void postpone_traps() 3965{ 3966 postpone_traps_flag = 1; 3967} 3968 3969int unpostpone_traps() 3970{ 3971 postpone_traps_flag = 0; 3972 if (!postponed_trap.is_null()) { 3973 spring_trap(postponed_trap); 3974 postponed_trap = NULL_SYMBOL; 3975 return 1; 3976 } 3977 else 3978 return 0; 3979} 3980 3981void read_request() 3982{ 3983 macro_iterator *mi = new macro_iterator; 3984 int reading_from_terminal = isatty(fileno(stdin)); 3985 int had_prompt = 0; 3986 if (!tok.newline() && !tok.eof()) { 3987 int c = get_copy(0); 3988 while (c == ' ') 3989 c = get_copy(0); 3990 while (c != EOF && c != '\n' && c != ' ') { 3991 if (!invalid_input_char(c)) { 3992 if (reading_from_terminal) 3993 fputc(c, stderr); 3994 had_prompt = 1; 3995 } 3996 c = get_copy(0); 3997 } 3998 if (c == ' ') { 3999 tok.make_space(); 4000 decode_args(mi); 4001 } 4002 } 4003 if (reading_from_terminal) { 4004 fputc(had_prompt ? ':' : '\a', stderr); 4005 fflush(stderr); 4006 } 4007 input_stack::push(mi); 4008 macro mac; 4009 int nl = 0; 4010 int c; 4011 while ((c = getchar()) != EOF) { 4012 if (invalid_input_char(c)) 4013 warning(WARN_INPUT, "invalid input character code %1", int(c)); 4014 else { 4015 if (c == '\n') { 4016 if (nl) 4017 break; 4018 else 4019 nl = 1; 4020 } 4021 else 4022 nl = 0; 4023 mac.append(c); 4024 } 4025 } 4026 if (reading_from_terminal) 4027 clearerr(stdin); 4028 input_stack::push(new string_iterator(mac)); 4029 tok.next(); 4030} 4031 4032enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE }; 4033enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT }; 4034enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE }; 4035 4036void do_define_string(define_mode mode, comp_mode comp) 4037{ 4038 symbol nm; 4039 node *n = 0; // pacify compiler 4040 int c; 4041 nm = get_name(1); 4042 if (nm.is_null()) { 4043 skip_line(); 4044 return; 4045 } 4046 if (tok.newline()) 4047 c = '\n'; 4048 else if (tok.tab()) 4049 c = '\t'; 4050 else if (!tok.space()) { 4051 error("bad string definition"); 4052 skip_line(); 4053 return; 4054 } 4055 else 4056 c = get_copy(&n); 4057 while (c == ' ') 4058 c = get_copy(&n); 4059 if (c == '"') 4060 c = get_copy(&n); 4061 macro mac; 4062 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); 4063 macro *mm = rm ? rm->to_macro() : 0; 4064 if (mode == DEFINE_APPEND && mm) 4065 mac = *mm; 4066 if (comp == COMP_DISABLE) 4067 mac.append(PUSH_GROFF_MODE); 4068 else if (comp == COMP_ENABLE) 4069 mac.append(PUSH_COMP_MODE); 4070 while (c != '\n' && c != EOF) { 4071 if (c == 0) 4072 mac.append(n); 4073 else 4074 mac.append((unsigned char)c); 4075 c = get_copy(&n); 4076 } 4077 if (!mm) { 4078 mm = new macro; 4079 request_dictionary.define(nm, mm); 4080 } 4081 if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4082 mac.append(POP_GROFFCOMP_MODE); 4083 *mm = mac; 4084 tok.next(); 4085} 4086 4087void define_string() 4088{ 4089 do_define_string(DEFINE_NORMAL, 4090 compatible_flag ? COMP_ENABLE: COMP_IGNORE); 4091} 4092 4093void define_nocomp_string() 4094{ 4095 do_define_string(DEFINE_NORMAL, COMP_DISABLE); 4096} 4097 4098void append_string() 4099{ 4100 do_define_string(DEFINE_APPEND, 4101 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4102} 4103 4104void append_nocomp_string() 4105{ 4106 do_define_string(DEFINE_APPEND, COMP_DISABLE); 4107} 4108 4109void do_define_character(char_mode mode, const char *font_name) 4110{ 4111 node *n = 0; // pacify compiler 4112 int c; 4113 tok.skip(); 4114 charinfo *ci = tok.get_char(1); 4115 if (ci == 0) { 4116 skip_line(); 4117 return; 4118 } 4119 if (font_name) { 4120 string s(font_name); 4121 s += ' '; 4122 s += ci->nm.contents(); 4123 s += '\0'; 4124 ci = get_charinfo(symbol(s.contents())); 4125 } 4126 tok.next(); 4127 if (tok.newline()) 4128 c = '\n'; 4129 else if (tok.tab()) 4130 c = '\t'; 4131 else if (!tok.space()) { 4132 error("bad character definition"); 4133 skip_line(); 4134 return; 4135 } 4136 else 4137 c = get_copy(&n); 4138 while (c == ' ' || c == '\t') 4139 c = get_copy(&n); 4140 if (c == '"') 4141 c = get_copy(&n); 4142 macro *m = new macro; 4143 while (c != '\n' && c != EOF) { 4144 if (c == 0) 4145 m->append(n); 4146 else 4147 m->append((unsigned char)c); 4148 c = get_copy(&n); 4149 } 4150 m = ci->setx_macro(m, mode); 4151 if (m) 4152 delete m; 4153 tok.next(); 4154} 4155 4156void define_character() 4157{ 4158 do_define_character(CHAR_NORMAL); 4159} 4160 4161void define_fallback_character() 4162{ 4163 do_define_character(CHAR_FALLBACK); 4164} 4165 4166void define_special_character() 4167{ 4168 do_define_character(CHAR_SPECIAL); 4169} 4170 4171static void remove_character() 4172{ 4173 tok.skip(); 4174 while (!tok.newline() && !tok.eof()) { 4175 if (!tok.space() && !tok.tab()) { 4176 charinfo *ci = tok.get_char(1); 4177 if (!ci) 4178 break; 4179 macro *m = ci->set_macro(0); 4180 if (m) 4181 delete m; 4182 } 4183 tok.next(); 4184 } 4185 skip_line(); 4186} 4187 4188static void interpolate_string(symbol nm) 4189{ 4190 request_or_macro *p = lookup_request(nm); 4191 macro *m = p->to_macro(); 4192 if (!m) 4193 error("you can only invoke a string or macro using \\*"); 4194 else { 4195 string_iterator *si = new string_iterator(*m, "string", nm); 4196 input_stack::push(si); 4197 } 4198} 4199 4200static void interpolate_string_with_args(symbol s) 4201{ 4202 request_or_macro *p = lookup_request(s); 4203 macro *m = p->to_macro(); 4204 if (!m) 4205 error("you can only invoke a string or macro using \\*"); 4206 else { 4207 macro_iterator *mi = new macro_iterator(s, *m); 4208 decode_string_args(mi); 4209 input_stack::push(mi); 4210 } 4211} 4212 4213static void interpolate_arg(symbol nm) 4214{ 4215 const char *s = nm.contents(); 4216 if (!s || *s == '\0') 4217 copy_mode_error("missing argument name"); 4218 else if (s[1] == 0 && csdigit(s[0])) 4219 input_stack::push(input_stack::get_arg(s[0] - '0')); 4220 else if (s[0] == '*' && s[1] == '\0') { 4221 int limit = input_stack::nargs(); 4222 string args; 4223 for (int i = 1; i <= limit; i++) { 4224 input_iterator *p = input_stack::get_arg(i); 4225 int c; 4226 while ((c = p->get(0)) != EOF) 4227 args += c; 4228 if (i != limit) 4229 args += ' '; 4230 } 4231 if (limit > 0) { 4232 args += '\0'; 4233 input_stack::push(make_temp_iterator(args.contents())); 4234 } 4235 } 4236 else if (s[0] == '@' && s[1] == '\0') { 4237 int limit = input_stack::nargs(); 4238 string args; 4239 for (int i = 1; i <= limit; i++) { 4240 args += '"'; 4241 args += BEGIN_QUOTE; 4242 input_iterator *p = input_stack::get_arg(i); 4243 int c; 4244 while ((c = p->get(0)) != EOF) 4245 args += c; 4246 args += END_QUOTE; 4247 args += '"'; 4248 if (i != limit) 4249 args += ' '; 4250 } 4251 if (limit > 0) { 4252 args += '\0'; 4253 input_stack::push(make_temp_iterator(args.contents())); 4254 } 4255 } 4256 else { 4257 const char *p; 4258 for (p = s; *p && csdigit(*p); p++) 4259 ; 4260 if (*p) 4261 copy_mode_error("bad argument name `%1'", s); 4262 else 4263 input_stack::push(input_stack::get_arg(atoi(s))); 4264 } 4265} 4266 4267void handle_first_page_transition() 4268{ 4269 push_token(tok); 4270 topdiv->begin_page(); 4271} 4272 4273// We push back a token by wrapping it up in a token_node, and 4274// wrapping that up in a string_iterator. 4275 4276static void push_token(const token &t) 4277{ 4278 macro m; 4279 m.append(new token_node(t)); 4280 input_stack::push(new string_iterator(m)); 4281} 4282 4283void push_page_ejector() 4284{ 4285 static char buf[2] = { PAGE_EJECTOR, '\0' }; 4286 input_stack::push(make_temp_iterator(buf)); 4287} 4288 4289void handle_initial_request(unsigned char code) 4290{ 4291 char buf[2]; 4292 buf[0] = code; 4293 buf[1] = '\0'; 4294 macro mac; 4295 mac.append(new token_node(tok)); 4296 input_stack::push(new string_iterator(mac)); 4297 input_stack::push(make_temp_iterator(buf)); 4298 topdiv->begin_page(); 4299 tok.next(); 4300} 4301 4302void handle_initial_title() 4303{ 4304 handle_initial_request(TITLE_REQUEST); 4305} 4306 4307// this should be local to define_macro, but cfront 1.2 doesn't support that 4308static symbol dot_symbol("."); 4309 4310void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp) 4311{ 4312 symbol nm, term; 4313 if (calling == CALLING_INDIRECT) { 4314 symbol temp1 = get_name(1); 4315 if (temp1.is_null()) { 4316 skip_line(); 4317 return; 4318 } 4319 symbol temp2 = get_name(); 4320 input_stack::push(make_temp_iterator("\n")); 4321 if (!temp2.is_null()) { 4322 interpolate_string(temp2); 4323 input_stack::push(make_temp_iterator(" ")); 4324 } 4325 interpolate_string(temp1); 4326 input_stack::push(make_temp_iterator(" ")); 4327 tok.next(); 4328 } 4329 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4330 nm = get_name(1); 4331 if (nm.is_null()) { 4332 skip_line(); 4333 return; 4334 } 4335 } 4336 term = get_name(); // the request that terminates the definition 4337 if (term.is_null()) 4338 term = dot_symbol; 4339 while (!tok.newline() && !tok.eof()) 4340 tok.next(); 4341 const char *start_filename; 4342 int start_lineno; 4343 int have_start_location = input_stack::get_location(0, &start_filename, 4344 &start_lineno); 4345 node *n; 4346 // doing this here makes the line numbers come out right 4347 int c = get_copy(&n, 1); 4348 macro mac; 4349 macro *mm = 0; 4350 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4351 request_or_macro *rm = 4352 (request_or_macro *)request_dictionary.lookup(nm); 4353 if (rm) 4354 mm = rm->to_macro(); 4355 if (mm && mode == DEFINE_APPEND) 4356 mac = *mm; 4357 } 4358 int bol = 1; 4359 if (comp == COMP_DISABLE) 4360 mac.append(PUSH_GROFF_MODE); 4361 else if (comp == COMP_ENABLE) 4362 mac.append(PUSH_COMP_MODE); 4363 for (;;) { 4364 while (c == ESCAPE_NEWLINE) { 4365 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) 4366 mac.append(c); 4367 c = get_copy(&n, 1); 4368 } 4369 if (bol && c == '.') { 4370 const char *s = term.contents(); 4371 int d = 0; 4372 // see if it matches term 4373 int i = 0; 4374 if (s[0] != 0) { 4375 while ((d = get_copy(&n)) == ' ' || d == '\t') 4376 ; 4377 if ((unsigned char)s[0] == d) { 4378 for (i = 1; s[i] != 0; i++) { 4379 d = get_copy(&n); 4380 if ((unsigned char)s[i] != d) 4381 break; 4382 } 4383 } 4384 } 4385 if (s[i] == 0 4386 && ((i == 2 && compatible_flag) 4387 || (d = get_copy(&n)) == ' ' 4388 || d == '\n')) { // we found it 4389 if (d == '\n') 4390 tok.make_newline(); 4391 else 4392 tok.make_space(); 4393 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4394 if (!mm) { 4395 mm = new macro; 4396 request_dictionary.define(nm, mm); 4397 } 4398 if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4399 mac.append(POP_GROFFCOMP_MODE); 4400 *mm = mac; 4401 } 4402 if (term != dot_symbol) { 4403 ignoring = 0; 4404 interpolate_macro(term); 4405 } 4406 else 4407 skip_line(); 4408 return; 4409 } 4410 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4411 mac.append(c); 4412 for (int j = 0; j < i; j++) 4413 mac.append(s[j]); 4414 } 4415 c = d; 4416 } 4417 if (c == EOF) { 4418 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4419 if (have_start_location) 4420 error_with_file_and_line(start_filename, start_lineno, 4421 "end of file while defining macro `%1'", 4422 nm.contents()); 4423 else 4424 error("end of file while defining macro `%1'", nm.contents()); 4425 } 4426 else { 4427 if (have_start_location) 4428 error_with_file_and_line(start_filename, start_lineno, 4429 "end of file while ignoring input lines"); 4430 else 4431 error("end of file while ignoring input lines"); 4432 } 4433 tok.next(); 4434 return; 4435 } 4436 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4437 if (c == 0) 4438 mac.append(n); 4439 else 4440 mac.append(c); 4441 } 4442 bol = (c == '\n'); 4443 c = get_copy(&n, 1); 4444 } 4445} 4446 4447void define_macro() 4448{ 4449 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, 4450 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4451} 4452 4453void define_nocomp_macro() 4454{ 4455 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE); 4456} 4457 4458void define_indirect_macro() 4459{ 4460 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, 4461 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4462} 4463 4464void define_indirect_nocomp_macro() 4465{ 4466 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE); 4467} 4468 4469void append_macro() 4470{ 4471 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, 4472 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4473} 4474 4475void append_nocomp_macro() 4476{ 4477 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE); 4478} 4479 4480void append_indirect_macro() 4481{ 4482 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, 4483 compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4484} 4485 4486void append_indirect_nocomp_macro() 4487{ 4488 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE); 4489} 4490 4491void ignore() 4492{ 4493 ignoring = 1; 4494 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE); 4495 ignoring = 0; 4496} 4497 4498void remove_macro() 4499{ 4500 for (;;) { 4501 symbol s = get_name(); 4502 if (s.is_null()) 4503 break; 4504 request_dictionary.remove(s); 4505 } 4506 skip_line(); 4507} 4508 4509void rename_macro() 4510{ 4511 symbol s1 = get_name(1); 4512 if (!s1.is_null()) { 4513 symbol s2 = get_name(1); 4514 if (!s2.is_null()) 4515 request_dictionary.rename(s1, s2); 4516 } 4517 skip_line(); 4518} 4519 4520void alias_macro() 4521{ 4522 symbol s1 = get_name(1); 4523 if (!s1.is_null()) { 4524 symbol s2 = get_name(1); 4525 if (!s2.is_null()) { 4526 if (!request_dictionary.alias(s1, s2)) 4527 warning(WARN_MAC, "macro `%1' not defined", s2.contents()); 4528 } 4529 } 4530 skip_line(); 4531} 4532 4533void chop_macro() 4534{ 4535 symbol s = get_name(1); 4536 if (!s.is_null()) { 4537 request_or_macro *p = lookup_request(s); 4538 macro *m = p->to_macro(); 4539 if (!m) 4540 error("cannot chop request"); 4541 else if (m->empty()) 4542 error("cannot chop empty macro"); 4543 else { 4544 int have_restore = 0; 4545 // we have to check for additional save/restore pairs which could be 4546 // there due to empty am1 requests. 4547 for (;;) { 4548 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE) 4549 break; 4550 have_restore = 1; 4551 m->len -= 1; 4552 if (m->get(m->len - 1) != PUSH_GROFF_MODE 4553 && m->get(m->len - 1) != PUSH_COMP_MODE) 4554 break; 4555 have_restore = 0; 4556 m->len -= 1; 4557 if (m->len == 0) 4558 break; 4559 } 4560 if (m->len == 0) 4561 error("cannot chop empty macro"); 4562 else { 4563 if (have_restore) 4564 m->set(POP_GROFFCOMP_MODE, m->len - 1); 4565 else 4566 m->len -= 1; 4567 } 4568 } 4569 } 4570 skip_line(); 4571} 4572 4573void substring_request() 4574{ 4575 int start; // 0, 1, ..., n-1 or -1, -2, ... 4576 symbol s = get_name(1); 4577 if (!s.is_null() && get_integer(&start)) { 4578 request_or_macro *p = lookup_request(s); 4579 macro *m = p->to_macro(); 4580 if (!m) 4581 error("cannot apply `substring' on a request"); 4582 else { 4583 int end = -1; 4584 if (!has_arg() || get_integer(&end)) { 4585 int real_length = 0; // 1, 2, ..., n 4586 string_iterator iter1(*m); 4587 for (int l = 0; l < m->len; l++) { 4588 int c = iter1.get(0); 4589 if (c == PUSH_GROFF_MODE 4590 || c == PUSH_COMP_MODE 4591 || c == POP_GROFFCOMP_MODE) 4592 continue; 4593 if (c == EOF) 4594 break; 4595 real_length++; 4596 } 4597 if (start < 0) 4598 start += real_length; 4599 if (end < 0) 4600 end += real_length; 4601 if (start > end) { 4602 int tem = start; 4603 start = end; 4604 end = tem; 4605 } 4606 if (start >= real_length || end < 0) { 4607 warning(WARN_RANGE, 4608 "start and end index of substring out of range"); 4609 m->len = 0; 4610 if (m->p) { 4611 if (--(m->p->count) <= 0) 4612 delete m->p; 4613 m->p = 0; 4614 } 4615 skip_line(); 4616 return; 4617 } 4618 if (start < 0) { 4619 warning(WARN_RANGE, 4620 "start index of substring out of range, set to 0"); 4621 start = 0; 4622 } 4623 if (end >= real_length) { 4624 warning(WARN_RANGE, 4625 "end index of substring out of range, set to string length"); 4626 end = real_length - 1; 4627 } 4628 // now extract the substring 4629 string_iterator iter(*m); 4630 int i; 4631 for (i = 0; i < start; i++) { 4632 int c = iter.get(0); 4633 while (c == PUSH_GROFF_MODE 4634 || c == PUSH_COMP_MODE 4635 || c == POP_GROFFCOMP_MODE) 4636 c = iter.get(0); 4637 if (c == EOF) 4638 break; 4639 } 4640 macro mac; 4641 for (; i <= end; i++) { 4642 node *nd = 0; // pacify compiler 4643 int c = iter.get(&nd); 4644 while (c == PUSH_GROFF_MODE 4645 || c == PUSH_COMP_MODE 4646 || c == POP_GROFFCOMP_MODE) 4647 c = iter.get(0); 4648 if (c == EOF) 4649 break; 4650 if (c == 0) 4651 mac.append(nd); 4652 else 4653 mac.append((unsigned char)c); 4654 } 4655 *m = mac; 4656 } 4657 } 4658 } 4659 skip_line(); 4660} 4661 4662void length_request() 4663{ 4664 symbol ret; 4665 ret = get_name(1); 4666 if (ret.is_null()) { 4667 skip_line(); 4668 return; 4669 } 4670 int c; 4671 node *n; 4672 if (tok.newline()) 4673 c = '\n'; 4674 else if (tok.tab()) 4675 c = '\t'; 4676 else if (!tok.space()) { 4677 error("bad string definition"); 4678 skip_line(); 4679 return; 4680 } 4681 else 4682 c = get_copy(&n); 4683 while (c == ' ') 4684 c = get_copy(&n); 4685 if (c == '"') 4686 c = get_copy(&n); 4687 int len = 0; 4688 while (c != '\n' && c != EOF) { 4689 ++len; 4690 c = get_copy(&n); 4691 } 4692 reg *r = (reg*)number_reg_dictionary.lookup(ret); 4693 if (r) 4694 r->set_value(len); 4695 else 4696 set_number_reg(ret, len); 4697 tok.next(); 4698} 4699 4700void asciify_macro() 4701{ 4702 symbol s = get_name(1); 4703 if (!s.is_null()) { 4704 request_or_macro *p = lookup_request(s); 4705 macro *m = p->to_macro(); 4706 if (!m) 4707 error("cannot asciify request"); 4708 else { 4709 macro am; 4710 string_iterator iter(*m); 4711 for (;;) { 4712 node *nd = 0; // pacify compiler 4713 int c = iter.get(&nd); 4714 if (c == EOF) 4715 break; 4716 if (c != 0) 4717 am.append(c); 4718 else 4719 nd->asciify(&am); 4720 } 4721 *m = am; 4722 } 4723 } 4724 skip_line(); 4725} 4726 4727void unformat_macro() 4728{ 4729 symbol s = get_name(1); 4730 if (!s.is_null()) { 4731 request_or_macro *p = lookup_request(s); 4732 macro *m = p->to_macro(); 4733 if (!m) 4734 error("cannot unformat request"); 4735 else { 4736 macro am; 4737 string_iterator iter(*m); 4738 for (;;) { 4739 node *nd = 0; // pacify compiler 4740 int c = iter.get(&nd); 4741 if (c == EOF) 4742 break; 4743 if (c != 0) 4744 am.append(c); 4745 else { 4746 if (nd->set_unformat_flag()) 4747 am.append(nd); 4748 } 4749 } 4750 *m = am; 4751 } 4752 } 4753 skip_line(); 4754} 4755 4756static void interpolate_environment_variable(symbol nm) 4757{ 4758 const char *s = getenv(nm.contents()); 4759 if (s && *s) 4760 input_stack::push(make_temp_iterator(s)); 4761} 4762 4763void interpolate_number_reg(symbol nm, int inc) 4764{ 4765 reg *r = lookup_number_reg(nm); 4766 if (inc < 0) 4767 r->decrement(); 4768 else if (inc > 0) 4769 r->increment(); 4770 input_stack::push(make_temp_iterator(r->get_string())); 4771} 4772 4773static void interpolate_number_format(symbol nm) 4774{ 4775 reg *r = (reg *)number_reg_dictionary.lookup(nm); 4776 if (r) 4777 input_stack::push(make_temp_iterator(r->get_format())); 4778} 4779 4780static int get_delim_number(units *n, unsigned char si, int prev_value) 4781{ 4782 token start; 4783 start.next(); 4784 if (start.delimiter(1)) { 4785 tok.next(); 4786 if (get_number(n, si, prev_value)) { 4787 if (start != tok) 4788 warning(WARN_DELIM, "closing delimiter does not match"); 4789 return 1; 4790 } 4791 } 4792 return 0; 4793} 4794 4795static int get_delim_number(units *n, unsigned char si) 4796{ 4797 token start; 4798 start.next(); 4799 if (start.delimiter(1)) { 4800 tok.next(); 4801 if (get_number(n, si)) { 4802 if (start != tok) 4803 warning(WARN_DELIM, "closing delimiter does not match"); 4804 return 1; 4805 } 4806 } 4807 return 0; 4808} 4809 4810static int get_line_arg(units *n, unsigned char si, charinfo **cp) 4811{ 4812 token start; 4813 start.next(); 4814 int start_level = input_stack::get_level(); 4815 if (!start.delimiter(1)) 4816 return 0; 4817 tok.next(); 4818 if (get_number(n, si)) { 4819 if (tok.dummy() || tok.transparent_dummy()) 4820 tok.next(); 4821 if (!(start == tok && input_stack::get_level() == start_level)) { 4822 *cp = tok.get_char(1); 4823 tok.next(); 4824 } 4825 if (!(start == tok && input_stack::get_level() == start_level)) 4826 warning(WARN_DELIM, "closing delimiter does not match"); 4827 return 1; 4828 } 4829 return 0; 4830} 4831 4832static int read_size(int *x) 4833{ 4834 tok.next(); 4835 int c = tok.ch(); 4836 int inc = 0; 4837 if (c == '-') { 4838 inc = -1; 4839 tok.next(); 4840 c = tok.ch(); 4841 } 4842 else if (c == '+') { 4843 inc = 1; 4844 tok.next(); 4845 c = tok.ch(); 4846 } 4847 int val = 0; // pacify compiler 4848 int bad = 0; 4849 if (c == '(') { 4850 tok.next(); 4851 c = tok.ch(); 4852 if (!inc) { 4853 // allow an increment either before or after the left parenthesis 4854 if (c == '-') { 4855 inc = -1; 4856 tok.next(); 4857 c = tok.ch(); 4858 } 4859 else if (c == '+') { 4860 inc = 1; 4861 tok.next(); 4862 c = tok.ch(); 4863 } 4864 } 4865 if (!csdigit(c)) 4866 bad = 1; 4867 else { 4868 val = c - '0'; 4869 tok.next(); 4870 c = tok.ch(); 4871 if (!csdigit(c)) 4872 bad = 1; 4873 else { 4874 val = val*10 + (c - '0'); 4875 val *= sizescale; 4876 } 4877 } 4878 } 4879 else if (csdigit(c)) { 4880 val = c - '0'; 4881 if (!inc && c != '0' && c < '4') { 4882 tok.next(); 4883 c = tok.ch(); 4884 if (!csdigit(c)) 4885 bad = 1; 4886 else 4887 val = val*10 + (c - '0'); 4888 } 4889 val *= sizescale; 4890 } 4891 else if (!tok.delimiter(1)) 4892 return 0; 4893 else { 4894 token start(tok); 4895 tok.next(); 4896 if (!(inc 4897 ? get_number(&val, 'z') 4898 : get_number(&val, 'z', curenv->get_requested_point_size()))) 4899 return 0; 4900 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) { 4901 if (start.ch() == '[') 4902 error("missing `]'"); 4903 else 4904 error("missing closing delimiter"); 4905 return 0; 4906 } 4907 } 4908 if (!bad) { 4909 switch (inc) { 4910 case 0: 4911 if (val == 0) { 4912 // special case -- \s[0] and \s0 means to revert to previous size 4913 *x = 0; 4914 return 1; 4915 } 4916 *x = val; 4917 break; 4918 case 1: 4919 *x = curenv->get_requested_point_size() + val; 4920 break; 4921 case -1: 4922 *x = curenv->get_requested_point_size() - val; 4923 break; 4924 default: 4925 assert(0); 4926 } 4927 if (*x <= 0) { 4928 warning(WARN_RANGE, 4929 "\\s request results in non-positive point size; set to 1"); 4930 *x = 1; 4931 } 4932 return 1; 4933 } 4934 else { 4935 error("bad digit in point size"); 4936 return 0; 4937 } 4938} 4939 4940static symbol get_delim_name() 4941{ 4942 token start; 4943 start.next(); 4944 if (start.eof()) { 4945 error("end of input at start of delimited name"); 4946 return NULL_SYMBOL; 4947 } 4948 if (start.newline()) { 4949 error("can't delimit name with a newline"); 4950 return NULL_SYMBOL; 4951 } 4952 int start_level = input_stack::get_level(); 4953 char abuf[ABUF_SIZE]; 4954 char *buf = abuf; 4955 int buf_size = ABUF_SIZE; 4956 int i = 0; 4957 for (;;) { 4958 if (i + 1 > buf_size) { 4959 if (buf == abuf) { 4960 buf = new char[ABUF_SIZE*2]; 4961 memcpy(buf, abuf, buf_size); 4962 buf_size = ABUF_SIZE*2; 4963 } 4964 else { 4965 char *old_buf = buf; 4966 buf = new char[buf_size*2]; 4967 memcpy(buf, old_buf, buf_size); 4968 buf_size *= 2; 4969 a_delete old_buf; 4970 } 4971 } 4972 tok.next(); 4973 if (tok == start 4974 && (compatible_flag || input_stack::get_level() == start_level)) 4975 break; 4976 if ((buf[i] = tok.ch()) == 0) { 4977 error("missing delimiter (got %1)", tok.description()); 4978 if (buf != abuf) 4979 a_delete buf; 4980 return NULL_SYMBOL; 4981 } 4982 i++; 4983 } 4984 buf[i] = '\0'; 4985 if (buf == abuf) { 4986 if (i == 0) { 4987 error("empty delimited name"); 4988 return NULL_SYMBOL; 4989 } 4990 else 4991 return symbol(buf); 4992 } 4993 else { 4994 symbol s(buf); 4995 a_delete buf; 4996 return s; 4997 } 4998} 4999 5000// Implement \R 5001 5002static void do_register() 5003{ 5004 token start; 5005 start.next(); 5006 if (!start.delimiter(1)) 5007 return; 5008 tok.next(); 5009 symbol nm = get_long_name(1); 5010 if (nm.is_null()) 5011 return; 5012 while (tok.space()) 5013 tok.next(); 5014 reg *r = (reg *)number_reg_dictionary.lookup(nm); 5015 int prev_value; 5016 if (!r || !r->get_value(&prev_value)) 5017 prev_value = 0; 5018 int val; 5019 if (!get_number(&val, 'u', prev_value)) 5020 return; 5021 if (start != tok) 5022 warning(WARN_DELIM, "closing delimiter does not match"); 5023 if (r) 5024 r->set_value(val); 5025 else 5026 set_number_reg(nm, val); 5027} 5028 5029// this implements the \w escape sequence 5030 5031static void do_width() 5032{ 5033 token start; 5034 start.next(); 5035 int start_level = input_stack::get_level(); 5036 environment env(curenv); 5037 environment *oldenv = curenv; 5038 curenv = &env; 5039 for (;;) { 5040 tok.next(); 5041 if (tok.eof()) { 5042 warning(WARN_DELIM, "missing closing delimiter"); 5043 break; 5044 } 5045 if (tok.newline()) { 5046 warning(WARN_DELIM, "missing closing delimiter"); 5047 input_stack::push(make_temp_iterator("\n")); 5048 break; 5049 } 5050 if (tok == start 5051 && (compatible_flag || input_stack::get_level() == start_level)) 5052 break; 5053 tok.process(); 5054 } 5055 env.wrap_up_tab(); 5056 units x = env.get_input_line_position().to_units(); 5057 input_stack::push(make_temp_iterator(i_to_a(x))); 5058 env.width_registers(); 5059 curenv = oldenv; 5060 have_input = 0; 5061} 5062 5063charinfo *page_character; 5064 5065void set_page_character() 5066{ 5067 page_character = get_optional_char(); 5068 skip_line(); 5069} 5070 5071static const symbol percent_symbol("%"); 5072 5073void read_title_parts(node **part, hunits *part_width) 5074{ 5075 tok.skip(); 5076 if (tok.newline() || tok.eof()) 5077 return; 5078 token start(tok); 5079 int start_level = input_stack::get_level(); 5080 tok.next(); 5081 for (int i = 0; i < 3; i++) { 5082 while (!tok.newline() && !tok.eof()) { 5083 if (tok == start 5084 && (compatible_flag || input_stack::get_level() == start_level)) { 5085 tok.next(); 5086 break; 5087 } 5088 if (page_character != 0 && tok.get_char() == page_character) 5089 interpolate_number_reg(percent_symbol, 0); 5090 else 5091 tok.process(); 5092 tok.next(); 5093 } 5094 curenv->wrap_up_tab(); 5095 part_width[i] = curenv->get_input_line_position(); 5096 part[i] = curenv->extract_output_line(); 5097 } 5098 while (!tok.newline() && !tok.eof()) 5099 tok.next(); 5100} 5101 5102class non_interpreted_node : public node { 5103 macro mac; 5104public: 5105 non_interpreted_node(const macro &); 5106 int interpret(macro *); 5107 node *copy(); 5108 int ends_sentence(); 5109 int same(node *); 5110 const char *type(); 5111 int force_tprint(); 5112 int is_tag(); 5113}; 5114 5115non_interpreted_node::non_interpreted_node(const macro &m) : mac(m) 5116{ 5117} 5118 5119int non_interpreted_node::ends_sentence() 5120{ 5121 return 2; 5122} 5123 5124int non_interpreted_node::same(node *nd) 5125{ 5126 return mac == ((non_interpreted_node *)nd)->mac; 5127} 5128 5129const char *non_interpreted_node::type() 5130{ 5131 return "non_interpreted_node"; 5132} 5133 5134int non_interpreted_node::force_tprint() 5135{ 5136 return 0; 5137} 5138 5139int non_interpreted_node::is_tag() 5140{ 5141 return 0; 5142} 5143 5144node *non_interpreted_node::copy() 5145{ 5146 return new non_interpreted_node(mac); 5147} 5148 5149int non_interpreted_node::interpret(macro *m) 5150{ 5151 string_iterator si(mac); 5152 node *n = 0; // pacify compiler 5153 for (;;) { 5154 int c = si.get(&n); 5155 if (c == EOF) 5156 break; 5157 if (c == 0) 5158 m->append(n); 5159 else 5160 m->append(c); 5161 } 5162 return 1; 5163} 5164 5165static node *do_non_interpreted() 5166{ 5167 node *n; 5168 int c; 5169 macro mac; 5170 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n') 5171 if (c == 0) 5172 mac.append(n); 5173 else 5174 mac.append(c); 5175 if (c == EOF || c == '\n') { 5176 error("missing \\?"); 5177 return 0; 5178 } 5179 return new non_interpreted_node(mac); 5180} 5181 5182static void encode_char(macro *mac, char c) 5183{ 5184 if (c == '\0') { 5185 if ((font::use_charnames_in_special) && tok.special()) { 5186 charinfo *ci = tok.get_char(1); 5187 const char *s = ci->get_symbol()->contents(); 5188 if (s[0] != (char)0) { 5189 mac->append('\\'); 5190 mac->append('('); 5191 int i = 0; 5192 while (s[i] != (char)0) { 5193 mac->append(s[i]); 5194 i++; 5195 } 5196 mac->append('\\'); 5197 mac->append(')'); 5198 } 5199 } 5200 else if (tok.stretchable_space() 5201 || tok.unstretchable_space()) 5202 mac->append(' '); 5203 else if (!(tok.hyphen_indicator() 5204 || tok.dummy() 5205 || tok.transparent_dummy() 5206 || tok.zero_width_break())) 5207 error("%1 is invalid within \\X", tok.description()); 5208 } 5209 else { 5210 if ((font::use_charnames_in_special) && (c == '\\')) { 5211 /* 5212 * add escape escape sequence 5213 */ 5214 mac->append(c); 5215 } 5216 mac->append(c); 5217 } 5218} 5219 5220node *do_special() 5221{ 5222 token start; 5223 start.next(); 5224 int start_level = input_stack::get_level(); 5225 macro mac; 5226 for (tok.next(); 5227 tok != start || input_stack::get_level() != start_level; 5228 tok.next()) { 5229 if (tok.eof()) { 5230 warning(WARN_DELIM, "missing closing delimiter"); 5231 return 0; 5232 } 5233 if (tok.newline()) { 5234 input_stack::push(make_temp_iterator("\n")); 5235 warning(WARN_DELIM, "missing closing delimiter"); 5236 break; 5237 } 5238 unsigned char c; 5239 if (tok.space()) 5240 c = ' '; 5241 else if (tok.tab()) 5242 c = '\t'; 5243 else if (tok.leader()) 5244 c = '\001'; 5245 else if (tok.backspace()) 5246 c = '\b'; 5247 else 5248 c = tok.ch(); 5249 encode_char(&mac, c); 5250 } 5251 return new special_node(mac); 5252} 5253 5254void output_request() 5255{ 5256 if (!tok.newline() && !tok.eof()) { 5257 int c; 5258 for (;;) { 5259 c = get_copy(0); 5260 if (c == '"') { 5261 c = get_copy(0); 5262 break; 5263 } 5264 if (c != ' ' && c != '\t') 5265 break; 5266 } 5267 for (; c != '\n' && c != EOF; c = get_copy(0)) 5268 topdiv->transparent_output(c); 5269 topdiv->transparent_output('\n'); 5270 } 5271 tok.next(); 5272} 5273 5274extern int image_no; // from node.cpp 5275 5276static node *do_suppress(symbol nm) 5277{ 5278 if (nm.is_null() || nm.is_empty()) { 5279 error("expecting an argument to escape \\O"); 5280 return 0; 5281 } 5282 const char *s = nm.contents(); 5283 switch (*s) { 5284 case '0': 5285 if (begin_level == 0) 5286 // suppress generation of glyphs 5287 return new suppress_node(0, 0); 5288 break; 5289 case '1': 5290 if (begin_level == 0) 5291 // enable generation of glyphs 5292 return new suppress_node(1, 0); 5293 break; 5294 case '2': 5295 if (begin_level == 0) 5296 return new suppress_node(1, 1); 5297 break; 5298 case '3': 5299 begin_level++; 5300 break; 5301 case '4': 5302 begin_level--; 5303 break; 5304 case '5': 5305 { 5306 s++; // move over '5' 5307 char position = *s; 5308 if (*s == (char)0) { 5309 error("missing position and filename in \\O"); 5310 return 0; 5311 } 5312 if (!(position == 'l' 5313 || position == 'r' 5314 || position == 'c' 5315 || position == 'i')) { 5316 error("l, r, c, or i position expected (got %1 in \\O)", position); 5317 return 0; 5318 } 5319 s++; // onto image name 5320 if (s == (char *)0) { 5321 error("missing image name for \\O"); 5322 return 0; 5323 } 5324 image_no++; 5325 if (begin_level == 0) 5326 return new suppress_node(symbol(s), position, image_no); 5327 } 5328 break; 5329 default: 5330 error("`%1' is an invalid argument to \\O", *s); 5331 } 5332 return 0; 5333} 5334 5335void special_node::tprint(troff_output_file *out) 5336{ 5337 tprint_start(out); 5338 string_iterator iter(mac); 5339 for (;;) { 5340 int c = iter.get(0); 5341 if (c == EOF) 5342 break; 5343 for (const char *s = ::asciify(c); *s; s++) 5344 tprint_char(out, *s); 5345 } 5346 tprint_end(out); 5347} 5348 5349int get_file_line(const char **filename, int *lineno) 5350{ 5351 return input_stack::get_location(0, filename, lineno); 5352} 5353 5354void line_file() 5355{ 5356 int n; 5357 if (get_integer(&n)) { 5358 const char *filename = 0; 5359 if (has_arg()) { 5360 symbol s = get_long_name(); 5361 filename = s.contents(); 5362 } 5363 (void)input_stack::set_location(filename, n-1); 5364 } 5365 skip_line(); 5366} 5367 5368static int nroff_mode = 0; 5369 5370static void nroff_request() 5371{ 5372 nroff_mode = 1; 5373 skip_line(); 5374} 5375 5376static void troff_request() 5377{ 5378 nroff_mode = 0; 5379 skip_line(); 5380} 5381 5382static void skip_alternative() 5383{ 5384 int level = 0; 5385 // ensure that ``.if 0\{'' works as expected 5386 if (tok.left_brace()) 5387 level++; 5388 int c; 5389 for (;;) { 5390 c = input_stack::get(0); 5391 if (c == EOF) 5392 break; 5393 if (c == ESCAPE_LEFT_BRACE) 5394 ++level; 5395 else if (c == ESCAPE_RIGHT_BRACE) 5396 --level; 5397 else if (c == escape_char && escape_char > 0) 5398 switch(input_stack::get(0)) { 5399 case '{': 5400 ++level; 5401 break; 5402 case '}': 5403 --level; 5404 break; 5405 case '"': 5406 while ((c = input_stack::get(0)) != '\n' && c != EOF) 5407 ; 5408 } 5409 /* 5410 Note that the level can properly be < 0, eg 5411 5412 .if 1 \{\ 5413 .if 0 \{\ 5414 .\}\} 5415 5416 So don't give an error message in this case. 5417 */ 5418 if (level <= 0 && c == '\n') 5419 break; 5420 } 5421 tok.next(); 5422} 5423 5424static void begin_alternative() 5425{ 5426 while (tok.space() || tok.left_brace()) 5427 tok.next(); 5428} 5429 5430void nop_request() 5431{ 5432 while (tok.space()) 5433 tok.next(); 5434} 5435 5436static int_stack if_else_stack; 5437 5438int do_if_request() 5439{ 5440 int invert = 0; 5441 while (tok.space()) 5442 tok.next(); 5443 while (tok.ch() == '!') { 5444 tok.next(); 5445 invert = !invert; 5446 } 5447 int result; 5448 unsigned char c = tok.ch(); 5449 if (c == 't') { 5450 tok.next(); 5451 result = !nroff_mode; 5452 } 5453 else if (c == 'n') { 5454 tok.next(); 5455 result = nroff_mode; 5456 } 5457 else if (c == 'v') { 5458 tok.next(); 5459 result = 0; 5460 } 5461 else if (c == 'o') { 5462 result = (topdiv->get_page_number() & 1); 5463 tok.next(); 5464 } 5465 else if (c == 'e') { 5466 result = !(topdiv->get_page_number() & 1); 5467 tok.next(); 5468 } 5469 else if (c == 'd' || c == 'r') { 5470 tok.next(); 5471 symbol nm = get_name(1); 5472 if (nm.is_null()) { 5473 skip_alternative(); 5474 return 0; 5475 } 5476 result = (c == 'd' 5477 ? request_dictionary.lookup(nm) != 0 5478 : number_reg_dictionary.lookup(nm) != 0); 5479 } 5480 else if (c == 'm') { 5481 tok.next(); 5482 symbol nm = get_long_name(1); 5483 if (nm.is_null()) { 5484 skip_alternative(); 5485 return 0; 5486 } 5487 result = (nm == default_symbol 5488 || color_dictionary.lookup(nm) != 0); 5489 } 5490 else if (c == 'c') { 5491 tok.next(); 5492 tok.skip(); 5493 charinfo *ci = tok.get_char(1); 5494 if (ci == 0) { 5495 skip_alternative(); 5496 return 0; 5497 } 5498 result = character_exists(ci, curenv); 5499 tok.next(); 5500 } 5501 else if (c == 'F') { 5502 tok.next(); 5503 symbol nm = get_long_name(1); 5504 if (nm.is_null()) { 5505 skip_alternative(); 5506 return 0; 5507 } 5508 result = check_font(curenv->get_family()->nm, nm); 5509 } 5510 else if (c == 'S') { 5511 tok.next(); 5512 symbol nm = get_long_name(1); 5513 if (nm.is_null()) { 5514 skip_alternative(); 5515 return 0; 5516 } 5517 result = check_style(nm); 5518 } 5519 else if (tok.space()) 5520 result = 0; 5521 else if (tok.delimiter()) { 5522 token delim = tok; 5523 int delim_level = input_stack::get_level(); 5524 environment env1(curenv); 5525 environment env2(curenv); 5526 environment *oldenv = curenv; 5527 curenv = &env1; 5528 suppress_push = 1; 5529 for (int i = 0; i < 2; i++) { 5530 for (;;) { 5531 tok.next(); 5532 if (tok.newline() || tok.eof()) { 5533 warning(WARN_DELIM, "missing closing delimiter"); 5534 tok.next(); 5535 curenv = oldenv; 5536 return 0; 5537 } 5538 if (tok == delim 5539 && (compatible_flag || input_stack::get_level() == delim_level)) 5540 break; 5541 tok.process(); 5542 } 5543 curenv = &env2; 5544 } 5545 node *n1 = env1.extract_output_line(); 5546 node *n2 = env2.extract_output_line(); 5547 result = same_node_list(n1, n2); 5548 delete_node_list(n1); 5549 delete_node_list(n2); 5550 curenv = oldenv; 5551 have_input = 0; 5552 suppress_push = 0; 5553 tok.next(); 5554 } 5555 else { 5556 units n; 5557 if (!get_number(&n, 'u')) { 5558 skip_alternative(); 5559 return 0; 5560 } 5561 else 5562 result = n > 0; 5563 } 5564 if (invert) 5565 result = !result; 5566 if (result) 5567 begin_alternative(); 5568 else 5569 skip_alternative(); 5570 return result; 5571} 5572 5573void if_else_request() 5574{ 5575 if_else_stack.push(do_if_request()); 5576} 5577 5578void if_request() 5579{ 5580 do_if_request(); 5581} 5582 5583void else_request() 5584{ 5585 if (if_else_stack.is_empty()) { 5586 warning(WARN_EL, "unbalanced .el request"); 5587 skip_alternative(); 5588 } 5589 else { 5590 if (if_else_stack.pop()) 5591 skip_alternative(); 5592 else 5593 begin_alternative(); 5594 } 5595} 5596 5597static int while_depth = 0; 5598static int while_break_flag = 0; 5599 5600void while_request() 5601{ 5602 macro mac; 5603 int escaped = 0; 5604 int level = 0; 5605 mac.append(new token_node(tok)); 5606 for (;;) { 5607 node *n = 0; // pacify compiler 5608 int c = input_stack::get(&n); 5609 if (c == EOF) 5610 break; 5611 if (c == 0) { 5612 escaped = 0; 5613 mac.append(n); 5614 } 5615 else if (escaped) { 5616 if (c == '{') 5617 level += 1; 5618 else if (c == '}') 5619 level -= 1; 5620 escaped = 0; 5621 mac.append(c); 5622 } 5623 else { 5624 if (c == ESCAPE_LEFT_BRACE) 5625 level += 1; 5626 else if (c == ESCAPE_RIGHT_BRACE) 5627 level -= 1; 5628 else if (c == escape_char) 5629 escaped = 1; 5630 mac.append(c); 5631 if (c == '\n' && level <= 0) 5632 break; 5633 } 5634 } 5635 if (level != 0) 5636 error("unbalanced \\{ \\}"); 5637 else { 5638 while_depth++; 5639 input_stack::add_boundary(); 5640 for (;;) { 5641 input_stack::push(new string_iterator(mac, "while loop")); 5642 tok.next(); 5643 if (!do_if_request()) { 5644 while (input_stack::get(0) != EOF) 5645 ; 5646 break; 5647 } 5648 process_input_stack(); 5649 if (while_break_flag || input_stack::is_return_boundary()) { 5650 while_break_flag = 0; 5651 break; 5652 } 5653 } 5654 input_stack::remove_boundary(); 5655 while_depth--; 5656 } 5657 tok.next(); 5658} 5659 5660void while_break_request() 5661{ 5662 if (!while_depth) { 5663 error("no while loop"); 5664 skip_line(); 5665 } 5666 else { 5667 while_break_flag = 1; 5668 while (input_stack::get(0) != EOF) 5669 ; 5670 tok.next(); 5671 } 5672} 5673 5674void while_continue_request() 5675{ 5676 if (!while_depth) { 5677 error("no while loop"); 5678 skip_line(); 5679 } 5680 else { 5681 while (input_stack::get(0) != EOF) 5682 ; 5683 tok.next(); 5684 } 5685} 5686 5687// .so 5688 5689void source() 5690{ 5691 symbol nm = get_long_name(1); 5692 if (nm.is_null()) 5693 skip_line(); 5694 else { 5695 while (!tok.newline() && !tok.eof()) 5696 tok.next(); 5697 errno = 0; 5698 FILE *fp = include_search_path.open_file_cautious(nm.contents()); 5699 if (fp) 5700 input_stack::push(new file_iterator(fp, nm.contents())); 5701 else 5702 error("can't open `%1': %2", nm.contents(), strerror(errno)); 5703 tok.next(); 5704 } 5705} 5706 5707// like .so but use popen() 5708 5709void pipe_source() 5710{ 5711 if (safer_flag) { 5712 error(".pso request not allowed in safer mode"); 5713 skip_line(); 5714 } 5715 else { 5716#ifdef POPEN_MISSING 5717 error("pipes not available on this system"); 5718 skip_line(); 5719#else /* not POPEN_MISSING */ 5720 if (tok.newline() || tok.eof()) 5721 error("missing command"); 5722 else { 5723 int c; 5724 while ((c = get_copy(0)) == ' ' || c == '\t') 5725 ; 5726 int buf_size = 24; 5727 char *buf = new char[buf_size]; 5728 int buf_used = 0; 5729 for (; c != '\n' && c != EOF; c = get_copy(0)) { 5730 const char *s = asciify(c); 5731 int slen = strlen(s); 5732 if (buf_used + slen + 1> buf_size) { 5733 char *old_buf = buf; 5734 int old_buf_size = buf_size; 5735 buf_size *= 2; 5736 buf = new char[buf_size]; 5737 memcpy(buf, old_buf, old_buf_size); 5738 a_delete old_buf; 5739 } 5740 strcpy(buf + buf_used, s); 5741 buf_used += slen; 5742 } 5743 buf[buf_used] = '\0'; 5744 errno = 0; 5745 FILE *fp = popen(buf, POPEN_RT); 5746 if (fp) 5747 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1)); 5748 else 5749 error("can't open pipe to process `%1': %2", buf, strerror(errno)); 5750 a_delete buf; 5751 } 5752 tok.next(); 5753#endif /* not POPEN_MISSING */ 5754 } 5755} 5756 5757// .psbb 5758 5759static int llx_reg_contents = 0; 5760static int lly_reg_contents = 0; 5761static int urx_reg_contents = 0; 5762static int ury_reg_contents = 0; 5763 5764struct bounding_box { 5765 int llx, lly, urx, ury; 5766}; 5767 5768/* Parse the argument to a %%BoundingBox comment. Return 1 if it 5769contains 4 numbers, 2 if it contains (atend), 0 otherwise. */ 5770 5771int parse_bounding_box(char *p, bounding_box *bb) 5772{ 5773 if (sscanf(p, "%d %d %d %d", 5774 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4) 5775 return 1; 5776 else { 5777 /* The Document Structuring Conventions say that the numbers 5778 should be integers. Unfortunately some broken applications 5779 get this wrong. */ 5780 double x1, x2, x3, x4; 5781 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) { 5782 bb->llx = (int)x1; 5783 bb->lly = (int)x2; 5784 bb->urx = (int)x3; 5785 bb->ury = (int)x4; 5786 return 1; 5787 } 5788 else { 5789 for (; *p == ' ' || *p == '\t'; p++) 5790 ; 5791 if (strncmp(p, "(atend)", 7) == 0) { 5792 return 2; 5793 } 5794 } 5795 } 5796 bb->llx = bb->lly = bb->urx = bb->ury = 0; 5797 return 0; 5798} 5799 5800// This version is taken from psrm.cpp 5801 5802#define PS_LINE_MAX 255 5803cset white_space("\n\r \t"); 5804 5805int ps_get_line(char *buf, FILE *fp, const char* filename) 5806{ 5807 int c = getc(fp); 5808 if (c == EOF) { 5809 buf[0] = '\0'; 5810 return 0; 5811 } 5812 int i = 0; 5813 int err = 0; 5814 while (c != '\r' && c != '\n' && c != EOF) { 5815 if ((c < 0x1b && !white_space(c)) || c == 0x7f) 5816 error("invalid input character code %1 in `%2'", int(c), filename); 5817 else if (i < PS_LINE_MAX) 5818 buf[i++] = c; 5819 else if (!err) { 5820 err = 1; 5821 error("PostScript file `%1' is non-conforming " 5822 "because length of line exceeds 255", filename); 5823 } 5824 c = getc(fp); 5825 } 5826 buf[i++] = '\n'; 5827 buf[i] = '\0'; 5828 if (c == '\r') { 5829 c = getc(fp); 5830 if (c != EOF && c != '\n') 5831 ungetc(c, fp); 5832 } 5833 return 1; 5834} 5835 5836inline void assign_registers(int llx, int lly, int urx, int ury) 5837{ 5838 llx_reg_contents = llx; 5839 lly_reg_contents = lly; 5840 urx_reg_contents = urx; 5841 ury_reg_contents = ury; 5842} 5843 5844void do_ps_file(FILE *fp, const char* filename) 5845{ 5846 bounding_box bb; 5847 int bb_at_end = 0; 5848 char buf[PS_LINE_MAX]; 5849 llx_reg_contents = lly_reg_contents = 5850 urx_reg_contents = ury_reg_contents = 0; 5851 if (!ps_get_line(buf, fp, filename)) { 5852 error("`%1' is empty", filename); 5853 return; 5854 } 5855 if (strncmp("%!PS-Adobe-", buf, 11) != 0) { 5856 error("`%1' is not conforming to the Document Structuring Conventions", 5857 filename); 5858 return; 5859 } 5860 while (ps_get_line(buf, fp, filename) != 0) { 5861 if (buf[0] != '%' || buf[1] != '%' 5862 || strncmp(buf + 2, "EndComments", 11) == 0) 5863 break; 5864 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5865 int res = parse_bounding_box(buf + 14, &bb); 5866 if (res == 1) { 5867 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5868 return; 5869 } 5870 else if (res == 2) { 5871 bb_at_end = 1; 5872 break; 5873 } 5874 else { 5875 error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5876 filename); 5877 return; 5878 } 5879 } 5880 } 5881 if (bb_at_end) { 5882 long offset; 5883 int last_try = 0; 5884 /* in the trailer, the last BoundingBox comment is significant */ 5885 for (offset = 512; !last_try; offset *= 2) { 5886 int had_trailer = 0; 5887 int got_bb = 0; 5888 if (offset > 32768 || fseek(fp, -offset, 2) == -1) { 5889 last_try = 1; 5890 if (fseek(fp, 0L, 0) == -1) 5891 break; 5892 } 5893 while (ps_get_line(buf, fp, filename) != 0) { 5894 if (buf[0] == '%' && buf[1] == '%') { 5895 if (!had_trailer) { 5896 if (strncmp(buf + 2, "Trailer", 7) == 0) 5897 had_trailer = 1; 5898 } 5899 else { 5900 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5901 int res = parse_bounding_box(buf + 14, &bb); 5902 if (res == 1) 5903 got_bb = 1; 5904 else if (res == 2) { 5905 error("`(atend)' not allowed in trailer of `%1'", filename); 5906 return; 5907 } 5908 else { 5909 error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5910 filename); 5911 return; 5912 } 5913 } 5914 } 5915 } 5916 } 5917 if (got_bb) { 5918 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5919 return; 5920 } 5921 } 5922 } 5923 error("%%%%BoundingBox comment not found in `%1'", filename); 5924} 5925 5926void ps_bbox_request() 5927{ 5928 symbol nm = get_long_name(1); 5929 if (nm.is_null()) 5930 skip_line(); 5931 else { 5932 while (!tok.newline() && !tok.eof()) 5933 tok.next(); 5934 errno = 0; 5935 // PS files might contain non-printable characters, such as ^Z 5936 // and CRs not followed by an LF, so open them in binary mode. 5937 FILE *fp = include_search_path.open_file_cautious(nm.contents(), 5938 0, FOPEN_RB); 5939 if (fp) { 5940 do_ps_file(fp, nm.contents()); 5941 fclose(fp); 5942 } 5943 else 5944 error("can't open `%1': %2", nm.contents(), strerror(errno)); 5945 tok.next(); 5946 } 5947} 5948 5949const char *asciify(int c) 5950{ 5951 static char buf[3]; 5952 buf[0] = escape_char == '\0' ? '\\' : escape_char; 5953 buf[1] = buf[2] = '\0'; 5954 switch (c) { 5955 case ESCAPE_QUESTION: 5956 buf[1] = '?'; 5957 break; 5958 case ESCAPE_AMPERSAND: 5959 buf[1] = '&'; 5960 break; 5961 case ESCAPE_RIGHT_PARENTHESIS: 5962 buf[1] = ')'; 5963 break; 5964 case ESCAPE_UNDERSCORE: 5965 buf[1] = '_'; 5966 break; 5967 case ESCAPE_BAR: 5968 buf[1] = '|'; 5969 break; 5970 case ESCAPE_CIRCUMFLEX: 5971 buf[1] = '^'; 5972 break; 5973 case ESCAPE_LEFT_BRACE: 5974 buf[1] = '{'; 5975 break; 5976 case ESCAPE_RIGHT_BRACE: 5977 buf[1] = '}'; 5978 break; 5979 case ESCAPE_LEFT_QUOTE: 5980 buf[1] = '`'; 5981 break; 5982 case ESCAPE_RIGHT_QUOTE: 5983 buf[1] = '\''; 5984 break; 5985 case ESCAPE_HYPHEN: 5986 buf[1] = '-'; 5987 break; 5988 case ESCAPE_BANG: 5989 buf[1] = '!'; 5990 break; 5991 case ESCAPE_c: 5992 buf[1] = 'c'; 5993 break; 5994 case ESCAPE_e: 5995 buf[1] = 'e'; 5996 break; 5997 case ESCAPE_E: 5998 buf[1] = 'E'; 5999 break; 6000 case ESCAPE_PERCENT: 6001 buf[1] = '%'; 6002 break; 6003 case ESCAPE_SPACE: 6004 buf[1] = ' '; 6005 break; 6006 case ESCAPE_TILDE: 6007 buf[1] = '~'; 6008 break; 6009 case ESCAPE_COLON: 6010 buf[1] = ':'; 6011 break; 6012 case PUSH_GROFF_MODE: 6013 case PUSH_COMP_MODE: 6014 case POP_GROFFCOMP_MODE: 6015 buf[0] = '\0'; 6016 break; 6017 default: 6018 if (invalid_input_char(c)) 6019 buf[0] = '\0'; 6020 else 6021 buf[0] = c; 6022 break; 6023 } 6024 return buf; 6025} 6026 6027const char *input_char_description(int c) 6028{ 6029 switch (c) { 6030 case '\n': 6031 return "a newline character"; 6032 case '\b': 6033 return "a backspace character"; 6034 case '\001': 6035 return "a leader character"; 6036 case '\t': 6037 return "a tab character"; 6038 case ' ': 6039 return "a space character"; 6040 case '\0': 6041 return "a node"; 6042 } 6043 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS]; 6044 if (invalid_input_char(c)) { 6045 const char *s = asciify(c); 6046 if (*s) { 6047 buf[0] = '`'; 6048 strcpy(buf + 1, s); 6049 strcat(buf, "'"); 6050 return buf; 6051 } 6052 sprintf(buf, "magic character code %d", c); 6053 return buf; 6054 } 6055 if (csprint(c)) { 6056 buf[0] = '`'; 6057 buf[1] = c; 6058 buf[2] = '\''; 6059 return buf; 6060 } 6061 sprintf(buf, "character code %d", c); 6062 return buf; 6063} 6064 6065void tag() 6066{ 6067 if (!tok.newline() && !tok.eof()) { 6068 string s; 6069 int c; 6070 for (;;) { 6071 c = get_copy(0); 6072 if (c == '"') { 6073 c = get_copy(0); 6074 break; 6075 } 6076 if (c != ' ' && c != '\t') 6077 break; 6078 } 6079 s = "x X "; 6080 for (; c != '\n' && c != EOF; c = get_copy(0)) 6081 s += (char)c; 6082 s += '\n'; 6083 curenv->add_node(new tag_node(s, 0)); 6084 } 6085 tok.next(); 6086} 6087 6088void taga() 6089{ 6090 if (!tok.newline() && !tok.eof()) { 6091 string s; 6092 int c; 6093 for (;;) { 6094 c = get_copy(0); 6095 if (c == '"') { 6096 c = get_copy(0); 6097 break; 6098 } 6099 if (c != ' ' && c != '\t') 6100 break; 6101 } 6102 s = "x X "; 6103 for (; c != '\n' && c != EOF; c = get_copy(0)) 6104 s += (char)c; 6105 s += '\n'; 6106 curenv->add_node(new tag_node(s, 1)); 6107 } 6108 tok.next(); 6109} 6110 6111// .tm, .tm1, and .tmc 6112 6113void do_terminal(int newline, int string_like) 6114{ 6115 if (!tok.newline() && !tok.eof()) { 6116 int c; 6117 for (;;) { 6118 c = get_copy(0); 6119 if (string_like && c == '"') { 6120 c = get_copy(0); 6121 break; 6122 } 6123 if (c != ' ' && c != '\t') 6124 break; 6125 } 6126 for (; c != '\n' && c != EOF; c = get_copy(0)) 6127 fputs(asciify(c), stderr); 6128 } 6129 if (newline) 6130 fputc('\n', stderr); 6131 fflush(stderr); 6132 tok.next(); 6133} 6134 6135void terminal() 6136{ 6137 do_terminal(1, 0); 6138} 6139 6140void terminal1() 6141{ 6142 do_terminal(1, 1); 6143} 6144 6145void terminal_continue() 6146{ 6147 do_terminal(0, 1); 6148} 6149 6150dictionary stream_dictionary(20); 6151 6152void do_open(int append) 6153{ 6154 symbol stream = get_name(1); 6155 if (!stream.is_null()) { 6156 symbol filename = get_long_name(1); 6157 if (!filename.is_null()) { 6158 errno = 0; 6159 FILE *fp = fopen(filename.contents(), append ? "a" : "w"); 6160 if (!fp) { 6161 error("can't open `%1' for %2: %3", 6162 filename.contents(), 6163 append ? "appending" : "writing", 6164 strerror(errno)); 6165 fp = (FILE *)stream_dictionary.remove(stream); 6166 } 6167 else 6168 fp = (FILE *)stream_dictionary.lookup(stream, fp); 6169 if (fp) 6170 fclose(fp); 6171 } 6172 } 6173 skip_line(); 6174} 6175 6176void open_request() 6177{ 6178 if (safer_flag) { 6179 error(".open request not allowed in safer mode"); 6180 skip_line(); 6181 } 6182 else 6183 do_open(0); 6184} 6185 6186void opena_request() 6187{ 6188 if (safer_flag) { 6189 error(".opena request not allowed in safer mode"); 6190 skip_line(); 6191 } 6192 else 6193 do_open(1); 6194} 6195 6196void close_request() 6197{ 6198 symbol stream = get_name(1); 6199 if (!stream.is_null()) { 6200 FILE *fp = (FILE *)stream_dictionary.remove(stream); 6201 if (!fp) 6202 error("no stream named `%1'", stream.contents()); 6203 else 6204 fclose(fp); 6205 } 6206 skip_line(); 6207} 6208 6209// .write and .writec 6210 6211void do_write_request(int newline) 6212{ 6213 symbol stream = get_name(1); 6214 if (stream.is_null()) { 6215 skip_line(); 6216 return; 6217 } 6218 FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6219 if (!fp) { 6220 error("no stream named `%1'", stream.contents()); 6221 skip_line(); 6222 return; 6223 } 6224 int c; 6225 while ((c = get_copy(0)) == ' ') 6226 ; 6227 if (c == '"') 6228 c = get_copy(0); 6229 for (; c != '\n' && c != EOF; c = get_copy(0)) 6230 fputs(asciify(c), fp); 6231 if (newline) 6232 fputc('\n', fp); 6233 fflush(fp); 6234 tok.next(); 6235} 6236 6237void write_request() 6238{ 6239 do_write_request(1); 6240} 6241 6242void write_request_continue() 6243{ 6244 do_write_request(0); 6245} 6246 6247void write_macro_request() 6248{ 6249 symbol stream = get_name(1); 6250 if (stream.is_null()) { 6251 skip_line(); 6252 return; 6253 } 6254 FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6255 if (!fp) { 6256 error("no stream named `%1'", stream.contents()); 6257 skip_line(); 6258 return; 6259 } 6260 symbol s = get_name(1); 6261 if (s.is_null()) { 6262 skip_line(); 6263 return; 6264 } 6265 request_or_macro *p = lookup_request(s); 6266 macro *m = p->to_macro(); 6267 if (!m) 6268 error("cannot write request"); 6269 else { 6270 string_iterator iter(*m); 6271 for (;;) { 6272 int c = iter.get(0); 6273 if (c == EOF) 6274 break; 6275 fputs(asciify(c), fp); 6276 } 6277 fflush(fp); 6278 } 6279 skip_line(); 6280} 6281 6282void warnscale_request() 6283{ 6284 if (has_arg()) { 6285 char c = tok.ch(); 6286 if (c == 'u') 6287 warn_scale = 1.0; 6288 else if (c == 'i') 6289 warn_scale = (double)units_per_inch; 6290 else if (c == 'c') 6291 warn_scale = (double)units_per_inch / 2.54; 6292 else if (c == 'p') 6293 warn_scale = (double)units_per_inch / 72.0; 6294 else if (c == 'P') 6295 warn_scale = (double)units_per_inch / 6.0; 6296 else { 6297 warning(WARN_SCALE, 6298 "invalid scaling indicator `%1', using `i' instead", c); 6299 c = 'i'; 6300 } 6301 warn_scaling_indicator = c; 6302 } 6303 skip_line(); 6304} 6305 6306void spreadwarn_request() 6307{ 6308 hunits n; 6309 if (has_arg() && get_hunits(&n, 'm')) { 6310 if (n < 0) 6311 n = 0; 6312 hunits em = curenv->get_size(); 6313 spread_limit = (double)n.to_units() 6314 / (em.is_zero() ? hresolution : em.to_units()); 6315 } 6316 else 6317 spread_limit = -spread_limit - 1; // no arg toggles on/off without 6318 // changing value; we mirror at 6319 // -0.5 to make zero a valid value 6320 skip_line(); 6321} 6322 6323static void init_charset_table() 6324{ 6325 char buf[16]; 6326 strcpy(buf, "char"); 6327 for (int i = 0; i < 256; i++) { 6328 strcpy(buf + 4, i_to_a(i)); 6329 charset_table[i] = get_charinfo(symbol(buf)); 6330 charset_table[i]->set_ascii_code(i); 6331 if (csalpha(i)) 6332 charset_table[i]->set_hyphenation_code(cmlower(i)); 6333 } 6334 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE); 6335 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE); 6336 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE); 6337 charset_table['-']->set_flags(charinfo::BREAK_AFTER); 6338 charset_table['"']->set_flags(charinfo::TRANSPARENT); 6339 charset_table['\'']->set_flags(charinfo::TRANSPARENT); 6340 charset_table[')']->set_flags(charinfo::TRANSPARENT); 6341 charset_table[']']->set_flags(charinfo::TRANSPARENT); 6342 charset_table['*']->set_flags(charinfo::TRANSPARENT); 6343 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT); 6344 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT); 6345 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER); 6346 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6347 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6348 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6349 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6350 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6351 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY); 6352 page_character = charset_table['%']; 6353} 6354 6355static void init_hpf_code_table() 6356{ 6357 for (int i = 0; i < 256; i++) 6358 hpf_code_table[i] = i; 6359} 6360 6361static void do_translate(int translate_transparent, int translate_input) 6362{ 6363 tok.skip(); 6364 while (!tok.newline() && !tok.eof()) { 6365 if (tok.space()) { 6366 // This is a really bizarre troff feature. 6367 tok.next(); 6368 translate_space_to_dummy = tok.dummy(); 6369 if (tok.newline() || tok.eof()) 6370 break; 6371 tok.next(); 6372 continue; 6373 } 6374 charinfo *ci1 = tok.get_char(1); 6375 if (ci1 == 0) 6376 break; 6377 tok.next(); 6378 if (tok.newline() || tok.eof()) { 6379 ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6380 translate_transparent); 6381 break; 6382 } 6383 if (tok.space()) 6384 ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6385 translate_transparent); 6386 else if (tok.stretchable_space()) 6387 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE, 6388 translate_transparent); 6389 else if (tok.dummy()) 6390 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY, 6391 translate_transparent); 6392 else if (tok.hyphen_indicator()) 6393 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR, 6394 translate_transparent); 6395 else { 6396 charinfo *ci2 = tok.get_char(1); 6397 if (ci2 == 0) 6398 break; 6399 if (ci1 == ci2) 6400 ci1->set_translation(0, translate_transparent, translate_input); 6401 else 6402 ci1->set_translation(ci2, translate_transparent, translate_input); 6403 } 6404 tok.next(); 6405 } 6406 skip_line(); 6407} 6408 6409void translate() 6410{ 6411 do_translate(1, 0); 6412} 6413 6414void translate_no_transparent() 6415{ 6416 do_translate(0, 0); 6417} 6418 6419void translate_input() 6420{ 6421 do_translate(1, 1); 6422} 6423 6424void char_flags() 6425{ 6426 int flags; 6427 if (get_integer(&flags)) 6428 while (has_arg()) { 6429 charinfo *ci = tok.get_char(1); 6430 if (ci) { 6431 charinfo *tem = ci->get_translation(); 6432 if (tem) 6433 ci = tem; 6434 ci->set_flags(flags); 6435 } 6436 tok.next(); 6437 } 6438 skip_line(); 6439} 6440 6441void hyphenation_code() 6442{ 6443 tok.skip(); 6444 while (!tok.newline() && !tok.eof()) { 6445 charinfo *ci = tok.get_char(1); 6446 if (ci == 0) 6447 break; 6448 tok.next(); 6449 tok.skip(); 6450 unsigned char c = tok.ch(); 6451 if (c == 0) { 6452 error("hyphenation code must be ordinary character"); 6453 break; 6454 } 6455 if (csdigit(c)) { 6456 error("hyphenation code cannot be digit"); 6457 break; 6458 } 6459 ci->set_hyphenation_code(c); 6460 if (ci->get_translation() 6461 && ci->get_translation()->get_translation_input()) 6462 ci->get_translation()->set_hyphenation_code(c); 6463 tok.next(); 6464 tok.skip(); 6465 } 6466 skip_line(); 6467} 6468 6469void hyphenation_patterns_file_code() 6470{ 6471 tok.skip(); 6472 while (!tok.newline() && !tok.eof()) { 6473 int n1, n2; 6474 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) { 6475 if (!has_arg()) { 6476 error("missing output hyphenation code"); 6477 break; 6478 } 6479 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) { 6480 hpf_code_table[n1] = n2; 6481 tok.skip(); 6482 } 6483 else { 6484 error("output hyphenation code must be integer in the range 0..255"); 6485 break; 6486 } 6487 } 6488 else { 6489 error("input hyphenation code must be integer in the range 0..255"); 6490 break; 6491 } 6492 } 6493 skip_line(); 6494} 6495 6496charinfo *token::get_char(int required) 6497{ 6498 if (type == TOKEN_CHAR) 6499 return charset_table[c]; 6500 if (type == TOKEN_SPECIAL) 6501 return get_charinfo(nm); 6502 if (type == TOKEN_NUMBERED_CHAR) 6503 return get_charinfo_by_number(val); 6504 if (type == TOKEN_ESCAPE) { 6505 if (escape_char != 0) 6506 return charset_table[escape_char]; 6507 else { 6508 error("`\\e' used while no current escape character"); 6509 return 0; 6510 } 6511 } 6512 if (required) { 6513 if (type == TOKEN_EOF || type == TOKEN_NEWLINE) 6514 warning(WARN_MISSING, "missing normal or special character"); 6515 else 6516 error("normal or special character expected (got %1)", description()); 6517 } 6518 return 0; 6519} 6520 6521charinfo *get_optional_char() 6522{ 6523 while (tok.space()) 6524 tok.next(); 6525 charinfo *ci = tok.get_char(); 6526 if (!ci) 6527 check_missing_character(); 6528 else 6529 tok.next(); 6530 return ci; 6531} 6532 6533void check_missing_character() 6534{ 6535 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab()) 6536 error("normal or special character expected (got %1): " 6537 "treated as missing", 6538 tok.description()); 6539} 6540 6541// this is for \Z 6542 6543int token::add_to_node_list(node **pp) 6544{ 6545 hunits w; 6546 int s; 6547 node *n = 0; 6548 switch (type) { 6549 case TOKEN_CHAR: 6550 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s); 6551 break; 6552 case TOKEN_DUMMY: 6553 n = new dummy_node; 6554 break; 6555 case TOKEN_ESCAPE: 6556 if (escape_char != 0) 6557 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s); 6558 break; 6559 case TOKEN_HYPHEN_INDICATOR: 6560 *pp = (*pp)->add_discretionary_hyphen(); 6561 break; 6562 case TOKEN_ITALIC_CORRECTION: 6563 *pp = (*pp)->add_italic_correction(&w); 6564 break; 6565 case TOKEN_LEFT_BRACE: 6566 break; 6567 case TOKEN_MARK_INPUT: 6568 set_number_reg(nm, curenv->get_input_line_position().to_units()); 6569 break; 6570 case TOKEN_NODE: 6571 n = nd; 6572 nd = 0; 6573 break; 6574 case TOKEN_NUMBERED_CHAR: 6575 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s); 6576 break; 6577 case TOKEN_RIGHT_BRACE: 6578 break; 6579 case TOKEN_SPACE: 6580 n = new hmotion_node(curenv->get_space_width(), 6581 curenv->get_fill_color()); 6582 break; 6583 case TOKEN_SPECIAL: 6584 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s); 6585 break; 6586 case TOKEN_STRETCHABLE_SPACE: 6587 n = new unbreakable_space_node(curenv->get_space_width(), 6588 curenv->get_fill_color()); 6589 break; 6590 case TOKEN_UNSTRETCHABLE_SPACE: 6591 n = new space_char_hmotion_node(curenv->get_space_width(), 6592 curenv->get_fill_color()); 6593 break; 6594 case TOKEN_TRANSPARENT_DUMMY: 6595 n = new transparent_dummy_node; 6596 break; 6597 case TOKEN_ZERO_WIDTH_BREAK: 6598 n = new space_node(H0, curenv->get_fill_color()); 6599 n->freeze_space(); 6600 n->is_escape_colon(); 6601 break; 6602 default: 6603 return 0; 6604 } 6605 if (n) { 6606 n->next = *pp; 6607 *pp = n; 6608 } 6609 return 1; 6610} 6611 6612void token::process() 6613{ 6614 if (possibly_handle_first_page_transition()) 6615 return; 6616 switch (type) { 6617 case TOKEN_BACKSPACE: 6618 curenv->add_node(new hmotion_node(-curenv->get_space_width(), 6619 curenv->get_fill_color())); 6620 break; 6621 case TOKEN_CHAR: 6622 curenv->add_char(charset_table[c]); 6623 break; 6624 case TOKEN_DUMMY: 6625 curenv->add_node(new dummy_node); 6626 break; 6627 case TOKEN_EMPTY: 6628 assert(0); 6629 break; 6630 case TOKEN_EOF: 6631 assert(0); 6632 break; 6633 case TOKEN_ESCAPE: 6634 if (escape_char != 0) 6635 curenv->add_char(charset_table[escape_char]); 6636 break; 6637 case TOKEN_BEGIN_TRAP: 6638 case TOKEN_END_TRAP: 6639 case TOKEN_PAGE_EJECTOR: 6640 // these are all handled in process_input_stack() 6641 break; 6642 case TOKEN_HYPHEN_INDICATOR: 6643 curenv->add_hyphen_indicator(); 6644 break; 6645 case TOKEN_INTERRUPT: 6646 curenv->interrupt(); 6647 break; 6648 case TOKEN_ITALIC_CORRECTION: 6649 curenv->add_italic_correction(); 6650 break; 6651 case TOKEN_LEADER: 6652 curenv->handle_tab(1); 6653 break; 6654 case TOKEN_LEFT_BRACE: 6655 break; 6656 case TOKEN_MARK_INPUT: 6657 set_number_reg(nm, curenv->get_input_line_position().to_units()); 6658 break; 6659 case TOKEN_NEWLINE: 6660 curenv->newline(); 6661 break; 6662 case TOKEN_NODE: 6663 curenv->add_node(nd); 6664 nd = 0; 6665 break; 6666 case TOKEN_NUMBERED_CHAR: 6667 curenv->add_char(get_charinfo_by_number(val)); 6668 break; 6669 case TOKEN_REQUEST: 6670 // handled in process_input_stack() 6671 break; 6672 case TOKEN_RIGHT_BRACE: 6673 break; 6674 case TOKEN_SPACE: 6675 curenv->space(); 6676 break; 6677 case TOKEN_SPECIAL: 6678 curenv->add_char(get_charinfo(nm)); 6679 break; 6680 case TOKEN_SPREAD: 6681 curenv->spread(); 6682 break; 6683 case TOKEN_STRETCHABLE_SPACE: 6684 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(), 6685 curenv->get_fill_color())); 6686 break; 6687 case TOKEN_UNSTRETCHABLE_SPACE: 6688 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(), 6689 curenv->get_fill_color())); 6690 break; 6691 case TOKEN_TAB: 6692 curenv->handle_tab(0); 6693 break; 6694 case TOKEN_TRANSPARENT: 6695 break; 6696 case TOKEN_TRANSPARENT_DUMMY: 6697 curenv->add_node(new transparent_dummy_node); 6698 break; 6699 case TOKEN_ZERO_WIDTH_BREAK: 6700 { 6701 node *tmp = new space_node(H0, curenv->get_fill_color()); 6702 tmp->freeze_space(); 6703 tmp->is_escape_colon(); 6704 curenv->add_node(tmp); 6705 break; 6706 } 6707 default: 6708 assert(0); 6709 } 6710} 6711 6712class nargs_reg : public reg { 6713public: 6714 const char *get_string(); 6715}; 6716 6717const char *nargs_reg::get_string() 6718{ 6719 return i_to_a(input_stack::nargs()); 6720} 6721 6722class lineno_reg : public reg { 6723public: 6724 const char *get_string(); 6725}; 6726 6727const char *lineno_reg::get_string() 6728{ 6729 int line; 6730 const char *file; 6731 if (!input_stack::get_location(0, &file, &line)) 6732 line = 0; 6733 return i_to_a(line); 6734} 6735 6736class writable_lineno_reg : public general_reg { 6737public: 6738 writable_lineno_reg(); 6739 void set_value(units); 6740 int get_value(units *); 6741}; 6742 6743writable_lineno_reg::writable_lineno_reg() 6744{ 6745} 6746 6747int writable_lineno_reg::get_value(units *res) 6748{ 6749 int line; 6750 const char *file; 6751 if (!input_stack::get_location(0, &file, &line)) 6752 return 0; 6753 *res = line; 6754 return 1; 6755} 6756 6757void writable_lineno_reg::set_value(units n) 6758{ 6759 input_stack::set_location(0, n); 6760} 6761 6762class filename_reg : public reg { 6763public: 6764 const char *get_string(); 6765}; 6766 6767const char *filename_reg::get_string() 6768{ 6769 int line; 6770 const char *file; 6771 if (input_stack::get_location(0, &file, &line)) 6772 return file; 6773 else 6774 return 0; 6775} 6776 6777class constant_reg : public reg { 6778 const char *s; 6779public: 6780 constant_reg(const char *); 6781 const char *get_string(); 6782}; 6783 6784constant_reg::constant_reg(const char *p) : s(p) 6785{ 6786} 6787 6788const char *constant_reg::get_string() 6789{ 6790 return s; 6791} 6792 6793constant_int_reg::constant_int_reg(int *q) : p(q) 6794{ 6795} 6796 6797const char *constant_int_reg::get_string() 6798{ 6799 return i_to_a(*p); 6800} 6801 6802void abort_request() 6803{ 6804 int c; 6805 if (tok.eof()) 6806 c = EOF; 6807 else if (tok.newline()) 6808 c = '\n'; 6809 else { 6810 while ((c = get_copy(0)) == ' ') 6811 ; 6812 } 6813 if (c == EOF || c == '\n') 6814 fputs("User Abort.", stderr); 6815 else { 6816 for (; c != '\n' && c != EOF; c = get_copy(0)) 6817 fputs(asciify(c), stderr); 6818 } 6819 fputc('\n', stderr); 6820 cleanup_and_exit(1); 6821} 6822 6823char *read_string() 6824{ 6825 int len = 256; 6826 char *s = new char[len]; 6827 int c; 6828 while ((c = get_copy(0)) == ' ') 6829 ; 6830 int i = 0; 6831 while (c != '\n' && c != EOF) { 6832 if (!invalid_input_char(c)) { 6833 if (i + 2 > len) { 6834 char *tem = s; 6835 s = new char[len*2]; 6836 memcpy(s, tem, len); 6837 len *= 2; 6838 a_delete tem; 6839 } 6840 s[i++] = c; 6841 } 6842 c = get_copy(0); 6843 } 6844 s[i] = '\0'; 6845 tok.next(); 6846 if (i == 0) { 6847 a_delete s; 6848 return 0; 6849 } 6850 return s; 6851} 6852 6853void pipe_output() 6854{ 6855 if (safer_flag) { 6856 error(".pi request not allowed in safer mode"); 6857 skip_line(); 6858 } 6859 else { 6860#ifdef POPEN_MISSING 6861 error("pipes not available on this system"); 6862 skip_line(); 6863#else /* not POPEN_MISSING */ 6864 if (the_output) { 6865 error("can't pipe: output already started"); 6866 skip_line(); 6867 } 6868 else { 6869 char *pc; 6870 if ((pc = read_string()) == 0) 6871 error("can't pipe to empty command"); 6872 if (pipe_command) { 6873 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1]; 6874 strcpy(s, pipe_command); 6875 strcat(s, "|"); 6876 strcat(s, pc); 6877 a_delete pipe_command; 6878 a_delete pc; 6879 pipe_command = s; 6880 } 6881 else 6882 pipe_command = pc; 6883 } 6884#endif /* not POPEN_MISSING */ 6885 } 6886} 6887 6888static int system_status; 6889 6890void system_request() 6891{ 6892 if (safer_flag) { 6893 error(".sy request not allowed in safer mode"); 6894 skip_line(); 6895 } 6896 else { 6897 char *command = read_string(); 6898 if (!command) 6899 error("empty command"); 6900 else { 6901 system_status = system(command); 6902 a_delete command; 6903 } 6904 } 6905} 6906 6907void copy_file() 6908{ 6909 if (curdiv == topdiv && topdiv->before_first_page) { 6910 handle_initial_request(COPY_FILE_REQUEST); 6911 return; 6912 } 6913 symbol filename = get_long_name(1); 6914 while (!tok.newline() && !tok.eof()) 6915 tok.next(); 6916 if (break_flag) 6917 curenv->do_break(); 6918 if (!filename.is_null()) 6919 curdiv->copy_file(filename.contents()); 6920 tok.next(); 6921} 6922 6923#ifdef COLUMN 6924 6925void vjustify() 6926{ 6927 if (curdiv == topdiv && topdiv->before_first_page) { 6928 handle_initial_request(VJUSTIFY_REQUEST); 6929 return; 6930 } 6931 symbol type = get_long_name(1); 6932 if (!type.is_null()) 6933 curdiv->vjustify(type); 6934 skip_line(); 6935} 6936 6937#endif /* COLUMN */ 6938 6939void transparent_file() 6940{ 6941 if (curdiv == topdiv && topdiv->before_first_page) { 6942 handle_initial_request(TRANSPARENT_FILE_REQUEST); 6943 return; 6944 } 6945 symbol filename = get_long_name(1); 6946 while (!tok.newline() && !tok.eof()) 6947 tok.next(); 6948 if (break_flag) 6949 curenv->do_break(); 6950 if (!filename.is_null()) { 6951 errno = 0; 6952 FILE *fp = include_search_path.open_file_cautious(filename.contents()); 6953 if (!fp) 6954 error("can't open `%1': %2", filename.contents(), strerror(errno)); 6955 else { 6956 int bol = 1; 6957 for (;;) { 6958 int c = getc(fp); 6959 if (c == EOF) 6960 break; 6961 if (invalid_input_char(c)) 6962 warning(WARN_INPUT, "invalid input character code %1", int(c)); 6963 else { 6964 curdiv->transparent_output(c); 6965 bol = c == '\n'; 6966 } 6967 } 6968 if (!bol) 6969 curdiv->transparent_output('\n'); 6970 fclose(fp); 6971 } 6972 } 6973 tok.next(); 6974} 6975 6976class page_range { 6977 int first; 6978 int last; 6979public: 6980 page_range *next; 6981 page_range(int, int, page_range *); 6982 int contains(int n); 6983}; 6984 6985page_range::page_range(int i, int j, page_range *p) 6986: first(i), last(j), next(p) 6987{ 6988} 6989 6990int page_range::contains(int n) 6991{ 6992 return n >= first && (last <= 0 || n <= last); 6993} 6994 6995page_range *output_page_list = 0; 6996 6997int in_output_page_list(int n) 6998{ 6999 if (!output_page_list) 7000 return 1; 7001 for (page_range *p = output_page_list; p; p = p->next) 7002 if (p->contains(n)) 7003 return 1; 7004 return 0; 7005} 7006 7007static void parse_output_page_list(char *p) 7008{ 7009 for (;;) { 7010 int i; 7011 if (*p == '-') 7012 i = 1; 7013 else if (csdigit(*p)) { 7014 i = 0; 7015 do 7016 i = i*10 + *p++ - '0'; 7017 while (csdigit(*p)); 7018 } 7019 else 7020 break; 7021 int j; 7022 if (*p == '-') { 7023 p++; 7024 j = 0; 7025 if (csdigit(*p)) { 7026 do 7027 j = j*10 + *p++ - '0'; 7028 while (csdigit(*p)); 7029 } 7030 } 7031 else 7032 j = i; 7033 if (j == 0) 7034 last_page_number = -1; 7035 else if (last_page_number >= 0 && j > last_page_number) 7036 last_page_number = j; 7037 output_page_list = new page_range(i, j, output_page_list); 7038 if (*p != ',') 7039 break; 7040 ++p; 7041 } 7042 if (*p != '\0') { 7043 error("bad output page list"); 7044 output_page_list = 0; 7045 } 7046} 7047 7048static FILE *open_mac_file(const char *mac, char **path) 7049{ 7050 // Try first FOOBAR.tmac, then tmac.FOOBAR 7051 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1]; 7052 strcpy(s1, mac); 7053 strcat(s1, MACRO_POSTFIX); 7054 FILE *fp = mac_path->open_file(s1, path); 7055 a_delete s1; 7056 if (!fp) { 7057 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1]; 7058 strcpy(s2, MACRO_PREFIX); 7059 strcat(s2, mac); 7060 fp = mac_path->open_file(s2, path); 7061 a_delete s2; 7062 } 7063 return fp; 7064} 7065 7066static void process_macro_file(const char *mac) 7067{ 7068 char *path; 7069 FILE *fp = open_mac_file(mac, &path); 7070 if (!fp) 7071 fatal("can't find macro file %1", mac); 7072 const char *s = symbol(path).contents(); 7073 a_delete path; 7074 input_stack::push(new file_iterator(fp, s)); 7075 tok.next(); 7076 process_input_stack(); 7077} 7078 7079static void process_startup_file(const char *filename) 7080{ 7081 char *path; 7082 search_path *orig_mac_path = mac_path; 7083 mac_path = &config_macro_path; 7084 FILE *fp = mac_path->open_file(filename, &path); 7085 if (fp) { 7086 input_stack::push(new file_iterator(fp, symbol(path).contents())); 7087 a_delete path; 7088 tok.next(); 7089 process_input_stack(); 7090 } 7091 mac_path = orig_mac_path; 7092} 7093 7094void macro_source() 7095{ 7096 symbol nm = get_long_name(1); 7097 if (nm.is_null()) 7098 skip_line(); 7099 else { 7100 while (!tok.newline() && !tok.eof()) 7101 tok.next(); 7102 char *path; 7103 FILE *fp = mac_path->open_file(nm.contents(), &path); 7104 // .mso doesn't (and cannot) go through open_mac_file, so we 7105 // need to do it here manually: If we have tmac.FOOBAR, try 7106 // FOOBAR.tmac and vice versa 7107 if (!fp) { 7108 const char *fn = nm.contents(); 7109 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) { 7110 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)]; 7111 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1); 7112 strcat(s, MACRO_POSTFIX); 7113 fp = mac_path->open_file(s, &path); 7114 a_delete s; 7115 } 7116 if (!fp) { 7117 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1, 7118 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) { 7119 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)]; 7120 strcpy(s, MACRO_PREFIX); 7121 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1); 7122 fp = mac_path->open_file(s, &path); 7123 a_delete s; 7124 } 7125 } 7126 } 7127 if (fp) { 7128 input_stack::push(new file_iterator(fp, symbol(path).contents())); 7129 a_delete path; 7130 } 7131 else 7132 error("can't find macro file `%1'", nm.contents()); 7133 tok.next(); 7134 } 7135} 7136 7137static void process_input_file(const char *name) 7138{ 7139 FILE *fp; 7140 if (strcmp(name, "-") == 0) { 7141 clearerr(stdin); 7142 fp = stdin; 7143 } 7144 else { 7145 errno = 0; 7146 fp = include_search_path.open_file_cautious(name); 7147 if (!fp) 7148 fatal("can't open `%1': %2", name, strerror(errno)); 7149 } 7150 input_stack::push(new file_iterator(fp, name)); 7151 tok.next(); 7152 process_input_stack(); 7153} 7154 7155// make sure the_input is empty before calling this 7156 7157static int evaluate_expression(const char *expr, units *res) 7158{ 7159 input_stack::push(make_temp_iterator(expr)); 7160 tok.next(); 7161 int success = get_number(res, 'u'); 7162 while (input_stack::get(0) != EOF) 7163 ; 7164 return success; 7165} 7166 7167static void do_register_assignment(const char *s) 7168{ 7169 const char *p = strchr(s, '='); 7170 if (!p) { 7171 char buf[2]; 7172 buf[0] = s[0]; 7173 buf[1] = 0; 7174 units n; 7175 if (evaluate_expression(s + 1, &n)) 7176 set_number_reg(buf, n); 7177 } 7178 else { 7179 char *buf = new char[p - s + 1]; 7180 memcpy(buf, s, p - s); 7181 buf[p - s] = 0; 7182 units n; 7183 if (evaluate_expression(p + 1, &n)) 7184 set_number_reg(buf, n); 7185 a_delete buf; 7186 } 7187} 7188 7189static void set_string(const char *name, const char *value) 7190{ 7191 macro *m = new macro; 7192 for (const char *p = value; *p; p++) 7193 if (!invalid_input_char((unsigned char)*p)) 7194 m->append(*p); 7195 request_dictionary.define(name, m); 7196} 7197 7198static void do_string_assignment(const char *s) 7199{ 7200 const char *p = strchr(s, '='); 7201 if (!p) { 7202 char buf[2]; 7203 buf[0] = s[0]; 7204 buf[1] = 0; 7205 set_string(buf, s + 1); 7206 } 7207 else { 7208 char *buf = new char[p - s + 1]; 7209 memcpy(buf, s, p - s); 7210 buf[p - s] = 0; 7211 set_string(buf, p + 1); 7212 a_delete buf; 7213 } 7214} 7215 7216struct string_list { 7217 const char *s; 7218 string_list *next; 7219 string_list(const char *ss) : s(ss), next(0) {} 7220}; 7221 7222#if 0 7223static void prepend_string(const char *s, string_list **p) 7224{ 7225 string_list *l = new string_list(s); 7226 l->next = *p; 7227 *p = l; 7228} 7229#endif 7230 7231static void add_string(const char *s, string_list **p) 7232{ 7233 while (*p) 7234 p = &((*p)->next); 7235 *p = new string_list(s); 7236} 7237 7238void usage(FILE *stream, const char *prog) 7239{ 7240 fprintf(stream, 7241"usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" 7242" -rcn -Tname -Fdir -Idir -Mdir [files...]\n", 7243 prog); 7244} 7245 7246int main(int argc, char **argv) 7247{ 7248 program_name = argv[0]; 7249 static char stderr_buf[BUFSIZ]; 7250 setbuf(stderr, stderr_buf); 7251 int c; 7252 string_list *macros = 0; 7253 string_list *register_assignments = 0; 7254 string_list *string_assignments = 0; 7255 int iflag = 0; 7256 int tflag = 0; 7257 int fflag = 0; 7258 int nflag = 0; 7259 int no_rc = 0; // don't process troffrc and troffrc-end 7260 int next_page_number = 0; // pacify compiler 7261 opterr = 0; 7262 hresolution = vresolution = 1; 7263 // restore $PATH if called from groff 7264 char* groff_path = getenv("GROFF_PATH__"); 7265 if (groff_path) { 7266 string e = "PATH"; 7267 e += '='; 7268 if (*groff_path) 7269 e += groff_path; 7270 e += '\0'; 7271 if (putenv(strsave(e.contents()))) 7272 fatal("putenv failed"); 7273 } 7274 static const struct option long_options[] = { 7275 { "help", no_argument, 0, CHAR_MAX + 1 }, 7276 { "version", no_argument, 0, 'v' }, 7277 { 0, 0, 0, 0 } 7278 }; 7279#if defined(DEBUGGING) 7280#define DEBUG_OPTION "D" 7281#endif 7282 while ((c = getopt_long(argc, argv, 7283 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU" 7284 DEBUG_OPTION, long_options, 0)) 7285 != EOF) 7286 switch(c) { 7287 case 'v': 7288 { 7289 printf("GNU troff (groff) version %s\n", Version_string); 7290 exit(0); 7291 break; 7292 } 7293 case 'I': 7294 // Search path for .psbb files 7295 // and most other non-system input files. 7296 include_search_path.command_line_dir(optarg); 7297 break; 7298 case 'T': 7299 device = optarg; 7300 tflag = 1; 7301 is_html = (strcmp(device, "html") == 0); 7302 break; 7303 case 'C': 7304 compatible_flag = 1; 7305 // fall through 7306 case 'c': 7307 color_flag = 0; 7308 break; 7309 case 'M': 7310 macro_path.command_line_dir(optarg); 7311 safer_macro_path.command_line_dir(optarg); 7312 config_macro_path.command_line_dir(optarg); 7313 break; 7314 case 'F': 7315 font::command_line_font_dir(optarg); 7316 break; 7317 case 'm': 7318 add_string(optarg, ¯os); 7319 break; 7320 case 'E': 7321 inhibit_errors = 1; 7322 break; 7323 case 'R': 7324 no_rc = 1; 7325 break; 7326 case 'w': 7327 enable_warning(optarg); 7328 break; 7329 case 'W': 7330 disable_warning(optarg); 7331 break; 7332 case 'i': 7333 iflag = 1; 7334 break; 7335 case 'b': 7336 backtrace_flag = 1; 7337 break; 7338 case 'a': 7339 ascii_output_flag = 1; 7340 break; 7341 case 'z': 7342 suppress_output_flag = 1; 7343 break; 7344 case 'n': 7345 if (sscanf(optarg, "%d", &next_page_number) == 1) 7346 nflag++; 7347 else 7348 error("bad page number"); 7349 break; 7350 case 'o': 7351 parse_output_page_list(optarg); 7352 break; 7353 case 'd': 7354 if (*optarg == '\0') 7355 error("`-d' requires non-empty argument"); 7356 else 7357 add_string(optarg, &string_assignments); 7358 break; 7359 case 'r': 7360 if (*optarg == '\0') 7361 error("`-r' requires non-empty argument"); 7362 else 7363 add_string(optarg, ®ister_assignments); 7364 break; 7365 case 'f': 7366 default_family = symbol(optarg); 7367 fflag = 1; 7368 break; 7369 case 'q': 7370 case 's': 7371 case 't': 7372 // silently ignore these 7373 break; 7374 case 'U': 7375 safer_flag = 0; // unsafe behaviour 7376 break; 7377#if defined(DEBUGGING) 7378 case 'D': 7379 debug_state = 1; 7380 break; 7381#endif 7382 case CHAR_MAX + 1: // --help 7383 usage(stdout, argv[0]); 7384 exit(0); 7385 break; 7386 case '?': 7387 usage(stderr, argv[0]); 7388 exit(1); 7389 break; // never reached 7390 default: 7391 assert(0); 7392 } 7393 if (!safer_flag) 7394 mac_path = ¯o_path; 7395 set_string(".T", device); 7396 init_charset_table(); 7397 init_hpf_code_table(); 7398 if (!font::load_desc()) 7399 fatal("sorry, I can't continue"); 7400 units_per_inch = font::res; 7401 hresolution = font::hor; 7402 vresolution = font::vert; 7403 sizescale = font::sizescale; 7404 tcommand_flag = font::tcommand; 7405 warn_scale = (double)units_per_inch; 7406 warn_scaling_indicator = 'i'; 7407 if (!fflag && font::family != 0 && *font::family != '\0') 7408 default_family = symbol(font::family); 7409 font_size::init_size_table(font::sizes); 7410 int i; 7411 int j = 1; 7412 if (font::style_table) { 7413 for (i = 0; font::style_table[i]; i++) 7414 mount_style(j++, symbol(font::style_table[i])); 7415 } 7416 for (i = 0; font::font_name_table[i]; i++, j++) 7417 // In the DESC file a font name of 0 (zero) means leave this 7418 // position empty. 7419 if (strcmp(font::font_name_table[i], "0") != 0) 7420 mount_font(j, symbol(font::font_name_table[i])); 7421 curdiv = topdiv = new top_level_diversion; 7422 if (nflag) 7423 topdiv->set_next_page_number(next_page_number); 7424 init_input_requests(); 7425 init_env_requests(); 7426 init_div_requests(); 7427#ifdef COLUMN 7428 init_column_requests(); 7429#endif /* COLUMN */ 7430 init_node_requests(); 7431 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); 7432 init_registers(); 7433 init_reg_requests(); 7434 init_hyphen_requests(); 7435 init_environments(); 7436 while (string_assignments) { 7437 do_string_assignment(string_assignments->s); 7438 string_list *tem = string_assignments; 7439 string_assignments = string_assignments->next; 7440 delete tem; 7441 } 7442 while (register_assignments) { 7443 do_register_assignment(register_assignments->s); 7444 string_list *tem = register_assignments; 7445 register_assignments = register_assignments->next; 7446 delete tem; 7447 } 7448 if (!no_rc) 7449 process_startup_file(INITIAL_STARTUP_FILE); 7450 while (macros) { 7451 process_macro_file(macros->s); 7452 string_list *tem = macros; 7453 macros = macros->next; 7454 delete tem; 7455 } 7456 if (!no_rc) 7457 process_startup_file(FINAL_STARTUP_FILE); 7458 for (i = optind; i < argc; i++) 7459 process_input_file(argv[i]); 7460 if (optind >= argc || iflag) 7461 process_input_file("-"); 7462 exit_troff(); 7463 return 0; // not reached 7464} 7465 7466void warn_request() 7467{ 7468 int n; 7469 if (has_arg() && get_integer(&n)) { 7470 if (n & ~WARN_TOTAL) { 7471 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL); 7472 n &= WARN_TOTAL; 7473 } 7474 warning_mask = n; 7475 } 7476 else 7477 warning_mask = WARN_TOTAL; 7478 skip_line(); 7479} 7480 7481static void init_registers() 7482{ 7483#ifdef LONG_FOR_TIME_T 7484 long 7485#else /* not LONG_FOR_TIME_T */ 7486 time_t 7487#endif /* not LONG_FOR_TIME_T */ 7488 t = time(0); 7489 // Use struct here to work around misfeature in old versions of g++. 7490 struct tm *tt = localtime(&t); 7491 set_number_reg("seconds", int(tt->tm_sec)); 7492 set_number_reg("minutes", int(tt->tm_min)); 7493 set_number_reg("hours", int(tt->tm_hour)); 7494 set_number_reg("dw", int(tt->tm_wday + 1)); 7495 set_number_reg("dy", int(tt->tm_mday)); 7496 set_number_reg("mo", int(tt->tm_mon + 1)); 7497 set_number_reg("year", int(1900 + tt->tm_year)); 7498 set_number_reg("yr", int(tt->tm_year)); 7499 set_number_reg("$$", getpid()); 7500 number_reg_dictionary.define(".A", 7501 new constant_reg(ascii_output_flag 7502 ? "1" 7503 : "0")); 7504} 7505 7506/* 7507 * registers associated with \O 7508 */ 7509 7510static int output_reg_minx_contents = -1; 7511static int output_reg_miny_contents = -1; 7512static int output_reg_maxx_contents = -1; 7513static int output_reg_maxy_contents = -1; 7514 7515void check_output_limits(int x, int y) 7516{ 7517 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) 7518 output_reg_minx_contents = x; 7519 if (x > output_reg_maxx_contents) 7520 output_reg_maxx_contents = x; 7521 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) 7522 output_reg_miny_contents = y; 7523 if (y > output_reg_maxy_contents) 7524 output_reg_maxy_contents = y; 7525} 7526 7527void reset_output_registers() 7528{ 7529 output_reg_minx_contents = -1; 7530 output_reg_miny_contents = -1; 7531 output_reg_maxx_contents = -1; 7532 output_reg_maxy_contents = -1; 7533} 7534 7535void get_output_registers(int *minx, int *miny, int *maxx, int *maxy) 7536{ 7537 *minx = output_reg_minx_contents; 7538 *miny = output_reg_miny_contents; 7539 *maxx = output_reg_maxx_contents; 7540 *maxy = output_reg_maxy_contents; 7541} 7542 7543void init_input_requests() 7544{ 7545 init_request("ab", abort_request); 7546 init_request("als", alias_macro); 7547 init_request("am", append_macro); 7548 init_request("am1", append_nocomp_macro); 7549 init_request("ami", append_indirect_macro); 7550 init_request("ami1", append_indirect_nocomp_macro); 7551 init_request("as", append_string); 7552 init_request("as1", append_nocomp_string); 7553 init_request("asciify", asciify_macro); 7554 init_request("backtrace", backtrace_request); 7555 init_request("blm", blank_line_macro); 7556 init_request("break", while_break_request); 7557 init_request("cf", copy_file); 7558 init_request("cflags", char_flags); 7559 init_request("char", define_character); 7560 init_request("chop", chop_macro); 7561 init_request("close", close_request); 7562 init_request("color", activate_color); 7563 init_request("composite", composite_request); 7564 init_request("continue", while_continue_request); 7565 init_request("cp", compatible); 7566 init_request("de", define_macro); 7567 init_request("de1", define_nocomp_macro); 7568 init_request("defcolor", define_color); 7569 init_request("dei", define_indirect_macro); 7570 init_request("dei1", define_indirect_nocomp_macro); 7571 init_request("do", do_request); 7572 init_request("ds", define_string); 7573 init_request("ds1", define_nocomp_string); 7574 init_request("ec", set_escape_char); 7575 init_request("ecr", restore_escape_char); 7576 init_request("ecs", save_escape_char); 7577 init_request("el", else_request); 7578 init_request("em", end_macro); 7579 init_request("eo", escape_off); 7580 init_request("ex", exit_request); 7581 init_request("fchar", define_fallback_character); 7582#ifdef WIDOW_CONTROL 7583 init_request("fpl", flush_pending_lines); 7584#endif /* WIDOW_CONTROL */ 7585 init_request("hcode", hyphenation_code); 7586 init_request("hpfcode", hyphenation_patterns_file_code); 7587 init_request("ie", if_else_request); 7588 init_request("if", if_request); 7589 init_request("ig", ignore); 7590 init_request("length", length_request); 7591 init_request("lf", line_file); 7592 init_request("mso", macro_source); 7593 init_request("nop", nop_request); 7594 init_request("nroff", nroff_request); 7595 init_request("nx", next_file); 7596 init_request("open", open_request); 7597 init_request("opena", opena_request); 7598 init_request("output", output_request); 7599 init_request("pc", set_page_character); 7600 init_request("pi", pipe_output); 7601 init_request("pm", print_macros); 7602 init_request("psbb", ps_bbox_request); 7603#ifndef POPEN_MISSING 7604 init_request("pso", pipe_source); 7605#endif /* not POPEN_MISSING */ 7606 init_request("rchar", remove_character); 7607 init_request("rd", read_request); 7608 init_request("return", return_macro_request); 7609 init_request("rm", remove_macro); 7610 init_request("rn", rename_macro); 7611 init_request("schar", define_special_character); 7612 init_request("shift", shift); 7613 init_request("so", source); 7614 init_request("spreadwarn", spreadwarn_request); 7615 init_request("substring", substring_request); 7616 init_request("sy", system_request); 7617 init_request("tag", tag); 7618 init_request("taga", taga); 7619 init_request("tm", terminal); 7620 init_request("tm1", terminal1); 7621 init_request("tmc", terminal_continue); 7622 init_request("tr", translate); 7623 init_request("trf", transparent_file); 7624 init_request("trin", translate_input); 7625 init_request("trnt", translate_no_transparent); 7626 init_request("troff", troff_request); 7627 init_request("unformat", unformat_macro); 7628#ifdef COLUMN 7629 init_request("vj", vjustify); 7630#endif /* COLUMN */ 7631 init_request("warn", warn_request); 7632 init_request("warnscale", warnscale_request); 7633 init_request("while", while_request); 7634 init_request("write", write_request); 7635 init_request("writec", write_request_continue); 7636 init_request("writem", write_macro_request); 7637 number_reg_dictionary.define(".$", new nargs_reg); 7638 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag)); 7639 number_reg_dictionary.define(".c", new lineno_reg); 7640 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag)); 7641 number_reg_dictionary.define(".F", new filename_reg); 7642 number_reg_dictionary.define(".g", new constant_reg("1")); 7643 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution)); 7644 number_reg_dictionary.define(".R", new constant_reg("10000")); 7645 number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag)); 7646 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); 7647 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask)); 7648 extern const char *major_version; 7649 number_reg_dictionary.define(".x", new constant_reg(major_version)); 7650 extern const char *revision; 7651 number_reg_dictionary.define(".Y", new constant_reg(revision)); 7652 extern const char *minor_version; 7653 number_reg_dictionary.define(".y", new constant_reg(minor_version)); 7654 number_reg_dictionary.define("c.", new writable_lineno_reg); 7655 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents)); 7656 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents)); 7657 number_reg_dictionary.define("opmaxx", 7658 new variable_reg(&output_reg_maxx_contents)); 7659 number_reg_dictionary.define("opmaxy", 7660 new variable_reg(&output_reg_maxy_contents)); 7661 number_reg_dictionary.define("opminx", 7662 new variable_reg(&output_reg_minx_contents)); 7663 number_reg_dictionary.define("opminy", 7664 new variable_reg(&output_reg_miny_contents)); 7665 number_reg_dictionary.define("slimit", 7666 new variable_reg(&input_stack::limit)); 7667 number_reg_dictionary.define("systat", new variable_reg(&system_status)); 7668 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents)); 7669 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents)); 7670} 7671 7672object_dictionary request_dictionary(501); 7673 7674void init_request(const char *s, REQUEST_FUNCP f) 7675{ 7676 request_dictionary.define(s, new request(f)); 7677} 7678 7679static request_or_macro *lookup_request(symbol nm) 7680{ 7681 assert(!nm.is_null()); 7682 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 7683 if (p == 0) { 7684 warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 7685 p = new macro; 7686 request_dictionary.define(nm, p); 7687 } 7688 return p; 7689} 7690 7691node *charinfo_to_node_list(charinfo *ci, const environment *envp) 7692{ 7693 // Don't interpret character definitions in compatible mode. 7694 int old_compatible_flag = compatible_flag; 7695 compatible_flag = 0; 7696 int old_escape_char = escape_char; 7697 escape_char = '\\'; 7698 macro *mac = ci->set_macro(0); 7699 assert(mac != 0); 7700 environment *oldenv = curenv; 7701 environment env(envp); 7702 curenv = &env; 7703 curenv->set_composite(); 7704 token old_tok = tok; 7705 input_stack::add_boundary(); 7706 string_iterator *si = 7707 new string_iterator(*mac, "composite character", ci->nm); 7708 input_stack::push(si); 7709 // we don't use process_input_stack, because we don't want to recognise 7710 // requests 7711 for (;;) { 7712 tok.next(); 7713 if (tok.eof()) 7714 break; 7715 if (tok.newline()) { 7716 error("composite character mustn't contain newline"); 7717 while (!tok.eof()) 7718 tok.next(); 7719 break; 7720 } 7721 else 7722 tok.process(); 7723 } 7724 node *n = curenv->extract_output_line(); 7725 input_stack::remove_boundary(); 7726 ci->set_macro(mac); 7727 tok = old_tok; 7728 curenv = oldenv; 7729 compatible_flag = old_compatible_flag; 7730 escape_char = old_escape_char; 7731 have_input = 0; 7732 return n; 7733} 7734 7735static node *read_draw_node() 7736{ 7737 token start; 7738 start.next(); 7739 if (!start.delimiter(1)){ 7740 do { 7741 tok.next(); 7742 } while (tok != start && !tok.newline() && !tok.eof()); 7743 } 7744 else { 7745 tok.next(); 7746 if (tok == start) 7747 error("missing argument"); 7748 else { 7749 unsigned char type = tok.ch(); 7750 if (type == 'F') { 7751 read_color_draw_node(start); 7752 return 0; 7753 } 7754 tok.next(); 7755 int maxpoints = 10; 7756 hvpair *point = new hvpair[maxpoints]; 7757 int npoints = 0; 7758 int no_last_v = 0; 7759 int err = 0; 7760 int i; 7761 for (i = 0; tok != start; i++) { 7762 if (i == maxpoints) { 7763 hvpair *oldpoint = point; 7764 point = new hvpair[maxpoints*2]; 7765 for (int j = 0; j < maxpoints; j++) 7766 point[j] = oldpoint[j]; 7767 maxpoints *= 2; 7768 a_delete oldpoint; 7769 } 7770 if (!get_hunits(&point[i].h, 7771 type == 'f' || type == 't' ? 'u' : 'm')) { 7772 err = 1; 7773 break; 7774 } 7775 ++npoints; 7776 tok.skip(); 7777 point[i].v = V0; 7778 if (tok == start) { 7779 no_last_v = 1; 7780 break; 7781 } 7782 if (!get_vunits(&point[i].v, 'v')) { 7783 err = 1; 7784 break; 7785 } 7786 tok.skip(); 7787 } 7788 while (tok != start && !tok.newline() && !tok.eof()) 7789 tok.next(); 7790 if (!err) { 7791 switch (type) { 7792 case 'l': 7793 if (npoints != 1 || no_last_v) { 7794 error("two arguments needed for line"); 7795 npoints = 1; 7796 } 7797 break; 7798 case 'c': 7799 if (npoints != 1 || !no_last_v) { 7800 error("one argument needed for circle"); 7801 npoints = 1; 7802 point[0].v = V0; 7803 } 7804 break; 7805 case 'e': 7806 if (npoints != 1 || no_last_v) { 7807 error("two arguments needed for ellipse"); 7808 npoints = 1; 7809 } 7810 break; 7811 case 'a': 7812 if (npoints != 2 || no_last_v) { 7813 error("four arguments needed for arc"); 7814 npoints = 2; 7815 } 7816 break; 7817 case '~': 7818 if (no_last_v) 7819 error("even number of arguments needed for spline"); 7820 break; 7821 case 'f': 7822 if (npoints != 1 || !no_last_v) { 7823 error("one argument needed for gray shade"); 7824 npoints = 1; 7825 point[0].v = V0; 7826 } 7827 default: 7828 // silently pass it through 7829 break; 7830 } 7831 draw_node *dn = new draw_node(type, point, npoints, 7832 curenv->get_font_size(), 7833 curenv->get_glyph_color(), 7834 curenv->get_fill_color()); 7835 a_delete point; 7836 return dn; 7837 } 7838 else { 7839 a_delete point; 7840 } 7841 } 7842 } 7843 return 0; 7844} 7845 7846static void read_color_draw_node(token &start) 7847{ 7848 tok.next(); 7849 if (tok == start) { 7850 error("missing color scheme"); 7851 return; 7852 } 7853 unsigned char scheme = tok.ch(); 7854 tok.next(); 7855 color *col = 0; 7856 char end = start.ch(); 7857 switch (scheme) { 7858 case 'c': 7859 col = read_cmy(end); 7860 break; 7861 case 'd': 7862 col = &default_color; 7863 break; 7864 case 'g': 7865 col = read_gray(end); 7866 break; 7867 case 'k': 7868 col = read_cmyk(end); 7869 break; 7870 case 'r': 7871 col = read_rgb(end); 7872 break; 7873 } 7874 if (col) 7875 curenv->set_fill_color(col); 7876 while (tok != start) { 7877 if (tok.newline() || tok.eof()) { 7878 warning(WARN_DELIM, "missing closing delimiter"); 7879 input_stack::push(make_temp_iterator("\n")); 7880 break; 7881 } 7882 tok.next(); 7883 } 7884 have_input = 1; 7885} 7886 7887static struct { 7888 const char *name; 7889 int mask; 7890} warning_table[] = { 7891 { "char", WARN_CHAR }, 7892 { "range", WARN_RANGE }, 7893 { "break", WARN_BREAK }, 7894 { "delim", WARN_DELIM }, 7895 { "el", WARN_EL }, 7896 { "scale", WARN_SCALE }, 7897 { "number", WARN_NUMBER }, 7898 { "syntax", WARN_SYNTAX }, 7899 { "tab", WARN_TAB }, 7900 { "right-brace", WARN_RIGHT_BRACE }, 7901 { "missing", WARN_MISSING }, 7902 { "input", WARN_INPUT }, 7903 { "escape", WARN_ESCAPE }, 7904 { "space", WARN_SPACE }, 7905 { "font", WARN_FONT }, 7906 { "di", WARN_DI }, 7907 { "mac", WARN_MAC }, 7908 { "reg", WARN_REG }, 7909 { "ig", WARN_IG }, 7910 { "color", WARN_COLOR }, 7911 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) }, 7912 { "w", WARN_TOTAL }, 7913 { "default", DEFAULT_WARNING_MASK }, 7914}; 7915 7916static int lookup_warning(const char *name) 7917{ 7918 for (unsigned int i = 0; 7919 i < sizeof(warning_table)/sizeof(warning_table[0]); 7920 i++) 7921 if (strcmp(name, warning_table[i].name) == 0) 7922 return warning_table[i].mask; 7923 return 0; 7924} 7925 7926static void enable_warning(const char *name) 7927{ 7928 int mask = lookup_warning(name); 7929 if (mask) 7930 warning_mask |= mask; 7931 else 7932 error("unknown warning `%1'", name); 7933} 7934 7935static void disable_warning(const char *name) 7936{ 7937 int mask = lookup_warning(name); 7938 if (mask) 7939 warning_mask &= ~mask; 7940 else 7941 error("unknown warning `%1'", name); 7942} 7943 7944static void copy_mode_error(const char *format, 7945 const errarg &arg1, 7946 const errarg &arg2, 7947 const errarg &arg3) 7948{ 7949 if (ignoring) { 7950 static const char prefix[] = "(in ignored input) "; 7951 char *s = new char[sizeof(prefix) + strlen(format)]; 7952 strcpy(s, prefix); 7953 strcat(s, format); 7954 warning(WARN_IG, s, arg1, arg2, arg3); 7955 a_delete s; 7956 } 7957 else 7958 error(format, arg1, arg2, arg3); 7959} 7960 7961enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL }; 7962 7963static void do_error(error_type type, 7964 const char *format, 7965 const errarg &arg1, 7966 const errarg &arg2, 7967 const errarg &arg3) 7968{ 7969 const char *filename; 7970 int lineno; 7971 if (inhibit_errors && type < FATAL) 7972 return; 7973 if (backtrace_flag) 7974 input_stack::backtrace(); 7975 if (!get_file_line(&filename, &lineno)) 7976 filename = 0; 7977 if (filename) 7978 errprint("%1:%2: ", filename, lineno); 7979 else if (program_name) 7980 fprintf(stderr, "%s: ", program_name); 7981 switch (type) { 7982 case FATAL: 7983 fputs("fatal error: ", stderr); 7984 break; 7985 case ERROR: 7986 break; 7987 case WARNING: 7988 fputs("warning: ", stderr); 7989 break; 7990 case OUTPUT_WARNING: 7991 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale; 7992 fprintf(stderr, "warning [p %d, %.1f%c", 7993 topdiv->get_page_number(), fromtop, warn_scaling_indicator); 7994 if (topdiv != curdiv) { 7995 double fromtop1 = curdiv->get_vertical_position().to_units() 7996 / warn_scale; 7997 fprintf(stderr, ", div `%s', %.1f%c", 7998 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator); 7999 } 8000 fprintf(stderr, "]: "); 8001 break; 8002 } 8003 errprint(format, arg1, arg2, arg3); 8004 fputc('\n', stderr); 8005 fflush(stderr); 8006 if (type == FATAL) 8007 cleanup_and_exit(1); 8008} 8009 8010int warning(warning_type t, 8011 const char *format, 8012 const errarg &arg1, 8013 const errarg &arg2, 8014 const errarg &arg3) 8015{ 8016 if ((t & warning_mask) != 0) { 8017 do_error(WARNING, format, arg1, arg2, arg3); 8018 return 1; 8019 } 8020 else 8021 return 0; 8022} 8023 8024int output_warning(warning_type t, 8025 const char *format, 8026 const errarg &arg1, 8027 const errarg &arg2, 8028 const errarg &arg3) 8029{ 8030 if ((t & warning_mask) != 0) { 8031 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3); 8032 return 1; 8033 } 8034 else 8035 return 0; 8036} 8037 8038void error(const char *format, 8039 const errarg &arg1, 8040 const errarg &arg2, 8041 const errarg &arg3) 8042{ 8043 do_error(ERROR, format, arg1, arg2, arg3); 8044} 8045 8046void fatal(const char *format, 8047 const errarg &arg1, 8048 const errarg &arg2, 8049 const errarg &arg3) 8050{ 8051 do_error(FATAL, format, arg1, arg2, arg3); 8052} 8053 8054void fatal_with_file_and_line(const char *filename, int lineno, 8055 const char *format, 8056 const errarg &arg1, 8057 const errarg &arg2, 8058 const errarg &arg3) 8059{ 8060 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno); 8061 errprint(format, arg1, arg2, arg3); 8062 fputc('\n', stderr); 8063 fflush(stderr); 8064 cleanup_and_exit(1); 8065} 8066 8067void error_with_file_and_line(const char *filename, int lineno, 8068 const char *format, 8069 const errarg &arg1, 8070 const errarg &arg2, 8071 const errarg &arg3) 8072{ 8073 fprintf(stderr, "%s:%d: error: ", filename, lineno); 8074 errprint(format, arg1, arg2, arg3); 8075 fputc('\n', stderr); 8076 fflush(stderr); 8077} 8078 8079dictionary charinfo_dictionary(501); 8080 8081charinfo *get_charinfo(symbol nm) 8082{ 8083 void *p = charinfo_dictionary.lookup(nm); 8084 if (p != 0) 8085 return (charinfo *)p; 8086 charinfo *cp = new charinfo(nm); 8087 (void)charinfo_dictionary.lookup(nm, cp); 8088 return cp; 8089} 8090 8091int charinfo::next_index = 0; 8092 8093charinfo::charinfo(symbol s) 8094: translation(0), mac(0), special_translation(TRANSLATE_NONE), 8095 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0), 8096 not_found(0), transparent_translate(1), translate_input(0), 8097 mode(CHAR_NORMAL), nm(s) 8098{ 8099 index = next_index++; 8100} 8101 8102void charinfo::set_hyphenation_code(unsigned char c) 8103{ 8104 hyphenation_code = c; 8105} 8106 8107void charinfo::set_translation(charinfo *ci, int tt, int ti) 8108{ 8109 translation = ci; 8110 if (ci && ti) { 8111 if (hyphenation_code != 0) 8112 ci->set_hyphenation_code(hyphenation_code); 8113 if (asciify_code != 0) 8114 ci->set_asciify_code(asciify_code); 8115 else if (ascii_code != 0) 8116 ci->set_asciify_code(ascii_code); 8117 ci->set_translation_input(); 8118 } 8119 special_translation = TRANSLATE_NONE; 8120 transparent_translate = tt; 8121} 8122 8123void charinfo::set_special_translation(int c, int tt) 8124{ 8125 special_translation = c; 8126 translation = 0; 8127 transparent_translate = tt; 8128} 8129 8130void charinfo::set_ascii_code(unsigned char c) 8131{ 8132 ascii_code = c; 8133} 8134 8135void charinfo::set_asciify_code(unsigned char c) 8136{ 8137 asciify_code = c; 8138} 8139 8140macro *charinfo::set_macro(macro *m) 8141{ 8142 macro *tem = mac; 8143 mac = m; 8144 return tem; 8145} 8146 8147macro *charinfo::setx_macro(macro *m, char_mode cm) 8148{ 8149 macro *tem = mac; 8150 mac = m; 8151 mode = cm; 8152 return tem; 8153} 8154 8155void charinfo::set_number(int n) 8156{ 8157 number = n; 8158 flags |= NUMBERED; 8159} 8160 8161int charinfo::get_number() 8162{ 8163 assert(flags & NUMBERED); 8164 return number; 8165} 8166 8167symbol UNNAMED_SYMBOL("---"); 8168 8169// For numbered characters not between 0 and 255, we make a symbol out 8170// of the number and store them in this dictionary. 8171 8172dictionary numbered_charinfo_dictionary(11); 8173 8174charinfo *get_charinfo_by_number(int n) 8175{ 8176 static charinfo *number_table[256]; 8177 8178 if (n >= 0 && n < 256) { 8179 charinfo *ci = number_table[n]; 8180 if (!ci) { 8181 ci = new charinfo(UNNAMED_SYMBOL); 8182 ci->set_number(n); 8183 number_table[n] = ci; 8184 } 8185 return ci; 8186 } 8187 else { 8188 symbol ns(i_to_a(n)); 8189 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns); 8190 if (!ci) { 8191 ci = new charinfo(UNNAMED_SYMBOL); 8192 ci->set_number(n); 8193 (void)numbered_charinfo_dictionary.lookup(ns, ci); 8194 } 8195 return ci; 8196 } 8197} 8198 8199int font::name_to_index(const char *nm) 8200{ 8201 charinfo *ci; 8202 if (nm[1] == 0) 8203 ci = charset_table[nm[0] & 0xff]; 8204 else if (nm[0] == '\\' && nm[2] == 0) 8205 ci = get_charinfo(symbol(nm + 1)); 8206 else 8207 ci = get_charinfo(symbol(nm)); 8208 if (ci == 0) 8209 return -1; 8210 else 8211 return ci->get_index(); 8212} 8213 8214int font::number_to_index(int n) 8215{ 8216 return get_charinfo_by_number(n)->get_index(); 8217} 8218