1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22151497Sru#define DEBUGGING 23151497Sru 24114402Sru#include "troff.h" 25114402Sru#include "dictionary.h" 26114402Sru#include "hvunits.h" 27151497Sru#include "stringclass.h" 28151497Sru#include "mtsm.h" 29114402Sru#include "env.h" 30114402Sru#include "request.h" 31114402Sru#include "node.h" 32114402Sru#include "token.h" 33114402Sru#include "div.h" 34151497Sru#include "reg.h" 35114402Sru#include "charinfo.h" 36114402Sru#include "macropath.h" 37151497Sru#include "input.h" 38114402Sru#include "defs.h" 39151497Sru#include "font.h" 40114402Sru#include "unicode.h" 41114402Sru 42114402Sru// Needed for getpid() and isatty() 43114402Sru#include "posix.h" 44114402Sru 45114402Sru#include "nonposix.h" 46114402Sru 47114402Sru#ifdef NEED_DECLARATION_PUTENV 48114402Sruextern "C" { 49114402Sru int putenv(const char *); 50114402Sru} 51114402Sru#endif /* NEED_DECLARATION_PUTENV */ 52114402Sru 53114402Sru#define MACRO_PREFIX "tmac." 54114402Sru#define MACRO_POSTFIX ".tmac" 55114402Sru#define INITIAL_STARTUP_FILE "troffrc" 56114402Sru#define FINAL_STARTUP_FILE "troffrc-end" 57114402Sru#define DEFAULT_INPUT_STACK_LIMIT 1000 58114402Sru 59114402Sru#ifndef DEFAULT_WARNING_MASK 60114402Sru// warnings that are enabled by default 61114402Sru#define DEFAULT_WARNING_MASK \ 62114402Sru (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT) 63114402Sru#endif 64114402Sru 65114402Sru// initial size of buffer for reading names; expanded as necessary 66114402Sru#define ABUF_SIZE 16 67114402Sru 68151497Sruextern "C" const char *program_name; 69114402Sruextern "C" const char *Version_string; 70114402Sru 71114402Sru#ifdef COLUMN 72114402Sruvoid init_column_requests(); 73114402Sru#endif /* COLUMN */ 74114402Sru 75114402Srustatic node *read_draw_node(); 76114402Srustatic void read_color_draw_node(token &); 77114402Srustatic void push_token(const token &); 78114402Sruvoid copy_file(); 79114402Sru#ifdef COLUMN 80114402Sruvoid vjustify(); 81114402Sru#endif /* COLUMN */ 82114402Sruvoid transparent_file(); 83114402Sru 84114402Srutoken tok; 85114402Sruint break_flag = 0; 86114402Sruint color_flag = 1; // colors are on by default 87114402Srustatic int backtrace_flag = 0; 88114402Sru#ifndef POPEN_MISSING 89114402Sruchar *pipe_command = 0; 90114402Sru#endif 91114402Srucharinfo *charset_table[256]; 92114402Sruunsigned char hpf_code_table[256]; 93114402Sru 94114402Srustatic int warning_mask = DEFAULT_WARNING_MASK; 95114402Srustatic int inhibit_errors = 0; 96114402Srustatic int ignoring = 0; 97114402Sru 98114402Srustatic void enable_warning(const char *); 99114402Srustatic void disable_warning(const char *); 100114402Sru 101114402Srustatic int escape_char = '\\'; 102114402Srustatic symbol end_macro_name; 103114402Srustatic symbol blank_line_macro_name; 104114402Srustatic int compatible_flag = 0; 105114402Sruint ascii_output_flag = 0; 106114402Sruint suppress_output_flag = 0; 107114402Sruint is_html = 0; 108151497Sruint begin_level = 0; // number of nested \O escapes 109114402Sru 110114402Sruint have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M, 111114402Sru // \R, \s, or \S has been processed in 112114402Sru // token::next() 113151497Sruint old_have_input = 0; // value of have_input right before \n 114114402Sruint tcommand_flag = 0; 115114402Sruint safer_flag = 1; // safer by default 116114402Sru 117114402Sruint have_string_arg = 0; // whether we have \*[foo bar...] 118114402Sru 119114402Srudouble spread_limit = -3.0 - 1.0; // negative means deactivated 120114402Sru 121114402Srudouble warn_scale; 122114402Sruchar warn_scaling_indicator; 123151497Sruint debug_state = 0; // turns on debugging of the html troff state 124114402Sru 125114402Srusearch_path *mac_path = &safer_macro_path; 126114402Sru 127151497Sru// Defaults to the current directory. 128151497Srusearch_path include_search_path(0, 0, 0, 1); 129151497Sru 130114402Srustatic int get_copy(node**, int = 0); 131114402Srustatic void copy_mode_error(const char *, 132114402Sru const errarg & = empty_errarg, 133114402Sru const errarg & = empty_errarg, 134114402Sru const errarg & = empty_errarg); 135114402Sru 136114402Sruenum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS }; 137114402Srustatic symbol read_escape_name(read_mode mode = NO_ARGS); 138114402Srustatic symbol read_long_escape_name(read_mode mode = NO_ARGS); 139114402Srustatic void interpolate_string(symbol); 140114402Srustatic void interpolate_string_with_args(symbol); 141114402Srustatic void interpolate_macro(symbol); 142114402Srustatic void interpolate_number_format(symbol); 143114402Srustatic void interpolate_environment_variable(symbol); 144114402Sru 145114402Srustatic symbol composite_glyph_name(symbol); 146114402Srustatic void interpolate_arg(symbol); 147114402Srustatic request_or_macro *lookup_request(symbol); 148151497Srustatic int get_delim_number(units *, unsigned char); 149151497Srustatic int get_delim_number(units *, unsigned char, units); 150114402Srustatic symbol do_get_long_name(int, char); 151151497Srustatic int get_line_arg(units *res, unsigned char si, charinfo **cp); 152114402Srustatic int read_size(int *); 153114402Srustatic symbol get_delim_name(); 154114402Srustatic void init_registers(); 155114402Srustatic void trapping_blank_line(); 156114402Sru 157151497Sruclass input_iterator; 158114402Sruinput_iterator *make_temp_iterator(const char *); 159114402Sruconst char *input_char_description(int); 160114402Sru 161151497Sruvoid process_input_stack(); 162151497Sruvoid chop_macro(); // declare to avoid friend name injection 163114402Sru 164151497Sru 165114402Sruvoid set_escape_char() 166114402Sru{ 167114402Sru if (has_arg()) { 168114402Sru if (tok.ch() == 0) { 169114402Sru error("bad escape character"); 170114402Sru escape_char = '\\'; 171114402Sru } 172114402Sru else 173114402Sru escape_char = tok.ch(); 174114402Sru } 175114402Sru else 176114402Sru escape_char = '\\'; 177114402Sru skip_line(); 178114402Sru} 179114402Sru 180114402Sruvoid escape_off() 181114402Sru{ 182114402Sru escape_char = 0; 183114402Sru skip_line(); 184114402Sru} 185114402Sru 186114402Srustatic int saved_escape_char = '\\'; 187114402Sru 188114402Sruvoid save_escape_char() 189114402Sru{ 190114402Sru saved_escape_char = escape_char; 191114402Sru skip_line(); 192114402Sru} 193114402Sru 194114402Sruvoid restore_escape_char() 195114402Sru{ 196114402Sru escape_char = saved_escape_char; 197114402Sru skip_line(); 198114402Sru} 199114402Sru 200114402Sruclass input_iterator { 201114402Srupublic: 202114402Sru input_iterator(); 203151497Sru input_iterator(int is_div); 204114402Sru virtual ~input_iterator() {} 205114402Sru int get(node **); 206114402Sru friend class input_stack; 207151497Sru int is_diversion; 208151497Sru statem *diversion_state; 209114402Sruprotected: 210114402Sru const unsigned char *ptr; 211114402Sru const unsigned char *eptr; 212114402Sru input_iterator *next; 213114402Sruprivate: 214114402Sru virtual int fill(node **); 215114402Sru virtual int peek(); 216114402Sru virtual int has_args() { return 0; } 217114402Sru virtual int nargs() { return 0; } 218114402Sru virtual input_iterator *get_arg(int) { return 0; } 219114402Sru virtual int get_location(int, const char **, int *) { return 0; } 220114402Sru virtual void backtrace() {} 221114402Sru virtual int set_location(const char *, int) { return 0; } 222114402Sru virtual int next_file(FILE *, const char *) { return 0; } 223114402Sru virtual void shift(int) {} 224114402Sru virtual int is_boundary() {return 0; } 225114402Sru virtual int is_file() { return 0; } 226114402Sru virtual int is_macro() { return 0; } 227114402Sru virtual void save_compatible_flag(int) {} 228114402Sru virtual int get_compatible_flag() { return 0; } 229114402Sru}; 230114402Sru 231114402Sruinput_iterator::input_iterator() 232151497Sru: is_diversion(0), ptr(0), eptr(0) 233114402Sru{ 234114402Sru} 235114402Sru 236151497Sruinput_iterator::input_iterator(int is_div) 237151497Sru: is_diversion(is_div), ptr(0), eptr(0) 238151497Sru{ 239151497Sru} 240151497Sru 241114402Sruint input_iterator::fill(node **) 242114402Sru{ 243114402Sru return EOF; 244114402Sru} 245114402Sru 246114402Sruint input_iterator::peek() 247114402Sru{ 248114402Sru return EOF; 249114402Sru} 250114402Sru 251114402Sruinline int input_iterator::get(node **p) 252114402Sru{ 253114402Sru return ptr < eptr ? *ptr++ : fill(p); 254114402Sru} 255114402Sru 256114402Sruclass input_boundary : public input_iterator { 257114402Srupublic: 258114402Sru int is_boundary() { return 1; } 259114402Sru}; 260114402Sru 261114402Sruclass input_return_boundary : public input_iterator { 262114402Srupublic: 263114402Sru int is_boundary() { return 2; } 264114402Sru}; 265114402Sru 266114402Sruclass file_iterator : public input_iterator { 267114402Sru FILE *fp; 268114402Sru int lineno; 269114402Sru const char *filename; 270114402Sru int popened; 271114402Sru int newline_flag; 272114402Sru int seen_escape; 273114402Sru enum { BUF_SIZE = 512 }; 274114402Sru unsigned char buf[BUF_SIZE]; 275114402Sru void close(); 276114402Srupublic: 277114402Sru file_iterator(FILE *, const char *, int = 0); 278114402Sru ~file_iterator(); 279114402Sru int fill(node **); 280114402Sru int peek(); 281114402Sru int get_location(int, const char **, int *); 282114402Sru void backtrace(); 283114402Sru int set_location(const char *, int); 284114402Sru int next_file(FILE *, const char *); 285114402Sru int is_file(); 286114402Sru}; 287114402Sru 288114402Srufile_iterator::file_iterator(FILE *f, const char *fn, int po) 289114402Sru: fp(f), lineno(1), filename(fn), popened(po), 290114402Sru newline_flag(0), seen_escape(0) 291114402Sru{ 292114402Sru if ((font::use_charnames_in_special) && (fn != 0)) { 293114402Sru if (!the_output) 294114402Sru init_output(); 295114402Sru the_output->put_filename(fn); 296114402Sru } 297114402Sru} 298114402Sru 299114402Srufile_iterator::~file_iterator() 300114402Sru{ 301114402Sru close(); 302114402Sru} 303114402Sru 304114402Sruvoid file_iterator::close() 305114402Sru{ 306114402Sru if (fp == stdin) 307114402Sru clearerr(stdin); 308114402Sru#ifndef POPEN_MISSING 309114402Sru else if (popened) 310114402Sru pclose(fp); 311114402Sru#endif /* not POPEN_MISSING */ 312114402Sru else 313114402Sru fclose(fp); 314114402Sru} 315114402Sru 316114402Sruint file_iterator::is_file() 317114402Sru{ 318114402Sru return 1; 319114402Sru} 320114402Sru 321114402Sruint file_iterator::next_file(FILE *f, const char *s) 322114402Sru{ 323114402Sru close(); 324114402Sru filename = s; 325114402Sru fp = f; 326114402Sru lineno = 1; 327114402Sru newline_flag = 0; 328114402Sru seen_escape = 0; 329114402Sru popened = 0; 330114402Sru ptr = 0; 331114402Sru eptr = 0; 332114402Sru return 1; 333114402Sru} 334114402Sru 335114402Sruint file_iterator::fill(node **) 336114402Sru{ 337114402Sru if (newline_flag) 338114402Sru lineno++; 339114402Sru newline_flag = 0; 340114402Sru unsigned char *p = buf; 341114402Sru ptr = p; 342114402Sru unsigned char *e = p + BUF_SIZE; 343114402Sru while (p < e) { 344114402Sru int c = getc(fp); 345114402Sru if (c == EOF) 346114402Sru break; 347114402Sru if (invalid_input_char(c)) 348114402Sru warning(WARN_INPUT, "invalid input character code %1", int(c)); 349114402Sru else { 350114402Sru *p++ = c; 351114402Sru if (c == '\n') { 352114402Sru seen_escape = 0; 353114402Sru newline_flag = 1; 354114402Sru break; 355114402Sru } 356114402Sru seen_escape = (c == '\\'); 357114402Sru } 358114402Sru } 359114402Sru if (p > buf) { 360114402Sru eptr = p; 361114402Sru return *ptr++; 362114402Sru } 363114402Sru else { 364114402Sru eptr = p; 365114402Sru return EOF; 366114402Sru } 367114402Sru} 368114402Sru 369114402Sruint file_iterator::peek() 370114402Sru{ 371114402Sru int c = getc(fp); 372114402Sru while (invalid_input_char(c)) { 373114402Sru warning(WARN_INPUT, "invalid input character code %1", int(c)); 374114402Sru c = getc(fp); 375114402Sru } 376114402Sru if (c != EOF) 377114402Sru ungetc(c, fp); 378114402Sru return c; 379114402Sru} 380114402Sru 381114402Sruint file_iterator::get_location(int /*allow_macro*/, 382114402Sru const char **filenamep, int *linenop) 383114402Sru{ 384114402Sru *linenop = lineno; 385114402Sru if (filename != 0 && strcmp(filename, "-") == 0) 386114402Sru *filenamep = "<standard input>"; 387114402Sru else 388114402Sru *filenamep = filename; 389114402Sru return 1; 390114402Sru} 391114402Sru 392114402Sruvoid file_iterator::backtrace() 393114402Sru{ 394114402Sru errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno, 395114402Sru popened ? "process" : "file"); 396114402Sru} 397114402Sru 398114402Sruint file_iterator::set_location(const char *f, int ln) 399114402Sru{ 400114402Sru if (f) { 401114402Sru filename = f; 402114402Sru if (!the_output) 403114402Sru init_output(); 404114402Sru the_output->put_filename(f); 405114402Sru } 406114402Sru lineno = ln; 407114402Sru return 1; 408114402Sru} 409114402Sru 410114402Sruinput_iterator nil_iterator; 411114402Sru 412114402Sruclass input_stack { 413114402Srupublic: 414114402Sru static int get(node **); 415114402Sru static int peek(); 416114402Sru static void push(input_iterator *); 417114402Sru static input_iterator *get_arg(int); 418114402Sru static int nargs(); 419114402Sru static int get_location(int, const char **, int *); 420114402Sru static int set_location(const char *, int); 421114402Sru static void backtrace(); 422114402Sru static void backtrace_all(); 423114402Sru static void next_file(FILE *, const char *); 424114402Sru static void end_file(); 425114402Sru static void shift(int n); 426114402Sru static void add_boundary(); 427114402Sru static void add_return_boundary(); 428114402Sru static int is_return_boundary(); 429114402Sru static void remove_boundary(); 430114402Sru static int get_level(); 431151497Sru static int get_div_level(); 432151497Sru static void increase_level(); 433151497Sru static void decrease_level(); 434114402Sru static void clear(); 435114402Sru static void pop_macro(); 436114402Sru static void save_compatible_flag(int); 437114402Sru static int get_compatible_flag(); 438151497Sru static statem *get_diversion_state(); 439151497Sru static void check_end_diversion(input_iterator *t); 440114402Sru static int limit; 441151497Sru static int div_level; 442151497Sru static statem *diversion_state; 443114402Sruprivate: 444114402Sru static input_iterator *top; 445114402Sru static int level; 446114402Sru static int finish_get(node **); 447114402Sru static int finish_peek(); 448114402Sru}; 449114402Sru 450114402Sruinput_iterator *input_stack::top = &nil_iterator; 451114402Sruint input_stack::level = 0; 452114402Sruint input_stack::limit = DEFAULT_INPUT_STACK_LIMIT; 453151497Sruint input_stack::div_level = 0; 454151497Srustatem *input_stack::diversion_state = NULL; 455151497Sruint suppress_push=0; 456114402Sru 457151497Sru 458114402Sruinline int input_stack::get_level() 459114402Sru{ 460151497Sru return level; 461114402Sru} 462114402Sru 463151497Sruinline void input_stack::increase_level() 464151497Sru{ 465151497Sru level++; 466151497Sru} 467151497Sru 468151497Sruinline void input_stack::decrease_level() 469151497Sru{ 470151497Sru level--; 471151497Sru} 472151497Sru 473151497Sruinline int input_stack::get_div_level() 474151497Sru{ 475151497Sru return div_level; 476151497Sru} 477151497Sru 478114402Sruinline int input_stack::get(node **np) 479114402Sru{ 480114402Sru int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np); 481151497Sru if (res == '\n') { 482151497Sru old_have_input = have_input; 483114402Sru have_input = 0; 484151497Sru } 485114402Sru return res; 486114402Sru} 487114402Sru 488114402Sruint input_stack::finish_get(node **np) 489114402Sru{ 490114402Sru for (;;) { 491114402Sru int c = top->fill(np); 492114402Sru if (c != EOF || top->is_boundary()) 493114402Sru return c; 494114402Sru if (top == &nil_iterator) 495114402Sru break; 496114402Sru input_iterator *tem = top; 497151497Sru check_end_diversion(tem); 498151497Sru#if defined(DEBUGGING) 499151497Sru if (debug_state) 500151497Sru if (tem->is_diversion) 501151497Sru fprintf(stderr, 502151497Sru "in diversion level = %d\n", input_stack::get_div_level()); 503151497Sru#endif 504114402Sru top = top->next; 505114402Sru level--; 506114402Sru delete tem; 507114402Sru if (top->ptr < top->eptr) 508114402Sru return *top->ptr++; 509114402Sru } 510114402Sru assert(level == 0); 511114402Sru return EOF; 512114402Sru} 513114402Sru 514114402Sruinline int input_stack::peek() 515114402Sru{ 516114402Sru return (top->ptr < top->eptr) ? *top->ptr : finish_peek(); 517114402Sru} 518114402Sru 519151497Sruvoid input_stack::check_end_diversion(input_iterator *t) 520151497Sru{ 521151497Sru if (t->is_diversion) { 522151497Sru div_level--; 523151497Sru diversion_state = t->diversion_state; 524151497Sru } 525151497Sru} 526151497Sru 527114402Sruint input_stack::finish_peek() 528114402Sru{ 529114402Sru for (;;) { 530114402Sru int c = top->peek(); 531114402Sru if (c != EOF || top->is_boundary()) 532114402Sru return c; 533114402Sru if (top == &nil_iterator) 534114402Sru break; 535114402Sru input_iterator *tem = top; 536151497Sru check_end_diversion(tem); 537114402Sru top = top->next; 538114402Sru level--; 539114402Sru delete tem; 540114402Sru if (top->ptr < top->eptr) 541114402Sru return *top->ptr; 542114402Sru } 543114402Sru assert(level == 0); 544114402Sru return EOF; 545114402Sru} 546114402Sru 547114402Sruvoid input_stack::add_boundary() 548114402Sru{ 549114402Sru push(new input_boundary); 550114402Sru} 551114402Sru 552114402Sruvoid input_stack::add_return_boundary() 553114402Sru{ 554114402Sru push(new input_return_boundary); 555114402Sru} 556114402Sru 557114402Sruint input_stack::is_return_boundary() 558114402Sru{ 559114402Sru return top->is_boundary() == 2; 560114402Sru} 561114402Sru 562114402Sruvoid input_stack::remove_boundary() 563114402Sru{ 564114402Sru assert(top->is_boundary()); 565114402Sru input_iterator *temp = top->next; 566151497Sru check_end_diversion(top); 567151497Sru 568114402Sru delete top; 569114402Sru top = temp; 570114402Sru level--; 571114402Sru} 572114402Sru 573114402Sruvoid input_stack::push(input_iterator *in) 574114402Sru{ 575114402Sru if (in == 0) 576114402Sru return; 577114402Sru if (++level > limit && limit > 0) 578114402Sru fatal("input stack limit exceeded (probable infinite loop)"); 579114402Sru in->next = top; 580114402Sru top = in; 581151497Sru if (top->is_diversion) { 582151497Sru div_level++; 583151497Sru in->diversion_state = diversion_state; 584151497Sru diversion_state = curenv->construct_state(0); 585151497Sru#if defined(DEBUGGING) 586151497Sru if (debug_state) { 587151497Sru curenv->dump_troff_state(); 588151497Sru fflush(stderr); 589151497Sru } 590151497Sru#endif 591151497Sru } 592151497Sru#if defined(DEBUGGING) 593151497Sru if (debug_state) 594151497Sru if (top->is_diversion) { 595151497Sru fprintf(stderr, 596151497Sru "in diversion level = %d\n", input_stack::get_div_level()); 597151497Sru fflush(stderr); 598151497Sru } 599151497Sru#endif 600114402Sru} 601114402Sru 602151497Srustatem *get_diversion_state() 603151497Sru{ 604151497Sru return input_stack::get_diversion_state(); 605151497Sru} 606151497Sru 607151497Srustatem *input_stack::get_diversion_state() 608151497Sru{ 609151497Sru if (diversion_state == NULL) 610151497Sru return NULL; 611151497Sru else 612151497Sru return new statem(diversion_state); 613151497Sru} 614151497Sru 615114402Sruinput_iterator *input_stack::get_arg(int i) 616114402Sru{ 617114402Sru input_iterator *p; 618114402Sru for (p = top; p != 0; p = p->next) 619114402Sru if (p->has_args()) 620114402Sru return p->get_arg(i); 621114402Sru return 0; 622114402Sru} 623114402Sru 624114402Sruvoid input_stack::shift(int n) 625114402Sru{ 626114402Sru for (input_iterator *p = top; p; p = p->next) 627114402Sru if (p->has_args()) { 628114402Sru p->shift(n); 629114402Sru return; 630114402Sru } 631114402Sru} 632114402Sru 633114402Sruint input_stack::nargs() 634114402Sru{ 635114402Sru for (input_iterator *p =top; p != 0; p = p->next) 636114402Sru if (p->has_args()) 637114402Sru return p->nargs(); 638114402Sru return 0; 639114402Sru} 640114402Sru 641114402Sruint input_stack::get_location(int allow_macro, const char **filenamep, int *linenop) 642114402Sru{ 643114402Sru for (input_iterator *p = top; p; p = p->next) 644114402Sru if (p->get_location(allow_macro, filenamep, linenop)) 645114402Sru return 1; 646114402Sru return 0; 647114402Sru} 648114402Sru 649114402Sruvoid input_stack::backtrace() 650114402Sru{ 651114402Sru const char *f; 652114402Sru int n; 653114402Sru // only backtrace down to (not including) the topmost file 654114402Sru for (input_iterator *p = top; 655114402Sru p && !p->get_location(0, &f, &n); 656114402Sru p = p->next) 657114402Sru p->backtrace(); 658114402Sru} 659114402Sru 660114402Sruvoid input_stack::backtrace_all() 661114402Sru{ 662114402Sru for (input_iterator *p = top; p; p = p->next) 663114402Sru p->backtrace(); 664114402Sru} 665114402Sru 666114402Sruint input_stack::set_location(const char *filename, int lineno) 667114402Sru{ 668114402Sru for (input_iterator *p = top; p; p = p->next) 669114402Sru if (p->set_location(filename, lineno)) 670114402Sru return 1; 671114402Sru return 0; 672114402Sru} 673114402Sru 674114402Sruvoid input_stack::next_file(FILE *fp, const char *s) 675114402Sru{ 676114402Sru input_iterator **pp; 677114402Sru for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 678114402Sru if ((*pp)->next_file(fp, s)) 679114402Sru return; 680114402Sru if (++level > limit && limit > 0) 681114402Sru fatal("input stack limit exceeded"); 682114402Sru *pp = new file_iterator(fp, s); 683114402Sru (*pp)->next = &nil_iterator; 684114402Sru} 685114402Sru 686114402Sruvoid input_stack::end_file() 687114402Sru{ 688114402Sru for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) 689114402Sru if ((*pp)->is_file()) { 690114402Sru input_iterator *tem = *pp; 691151497Sru check_end_diversion(tem); 692114402Sru *pp = (*pp)->next; 693114402Sru delete tem; 694114402Sru level--; 695114402Sru return; 696114402Sru } 697114402Sru} 698114402Sru 699114402Sruvoid input_stack::clear() 700114402Sru{ 701114402Sru int nboundaries = 0; 702114402Sru while (top != &nil_iterator) { 703114402Sru if (top->is_boundary()) 704114402Sru nboundaries++; 705114402Sru input_iterator *tem = top; 706151497Sru check_end_diversion(tem); 707114402Sru top = top->next; 708114402Sru level--; 709114402Sru delete tem; 710114402Sru } 711114402Sru // Keep while_request happy. 712114402Sru for (; nboundaries > 0; --nboundaries) 713114402Sru add_return_boundary(); 714114402Sru} 715114402Sru 716114402Sruvoid input_stack::pop_macro() 717114402Sru{ 718114402Sru int nboundaries = 0; 719114402Sru int is_macro = 0; 720114402Sru do { 721114402Sru if (top->next == &nil_iterator) 722114402Sru break; 723114402Sru if (top->is_boundary()) 724114402Sru nboundaries++; 725114402Sru is_macro = top->is_macro(); 726114402Sru input_iterator *tem = top; 727151497Sru check_end_diversion(tem); 728114402Sru top = top->next; 729114402Sru level--; 730114402Sru delete tem; 731114402Sru } while (!is_macro); 732114402Sru // Keep while_request happy. 733114402Sru for (; nboundaries > 0; --nboundaries) 734114402Sru add_return_boundary(); 735114402Sru} 736114402Sru 737114402Sruinline void input_stack::save_compatible_flag(int f) 738114402Sru{ 739114402Sru top->save_compatible_flag(f); 740114402Sru} 741114402Sru 742114402Sruinline int input_stack::get_compatible_flag() 743114402Sru{ 744114402Sru return top->get_compatible_flag(); 745114402Sru} 746114402Sru 747114402Sruvoid backtrace_request() 748114402Sru{ 749114402Sru input_stack::backtrace_all(); 750114402Sru fflush(stderr); 751114402Sru skip_line(); 752114402Sru} 753114402Sru 754114402Sruvoid next_file() 755114402Sru{ 756114402Sru symbol nm = get_long_name(); 757114402Sru while (!tok.newline() && !tok.eof()) 758114402Sru tok.next(); 759114402Sru if (nm.is_null()) 760114402Sru input_stack::end_file(); 761114402Sru else { 762114402Sru errno = 0; 763151497Sru FILE *fp = include_search_path.open_file_cautious(nm.contents()); 764114402Sru if (!fp) 765114402Sru error("can't open `%1': %2", nm.contents(), strerror(errno)); 766114402Sru else 767114402Sru input_stack::next_file(fp, nm.contents()); 768114402Sru } 769114402Sru tok.next(); 770114402Sru} 771114402Sru 772114402Sruvoid shift() 773114402Sru{ 774114402Sru int n; 775114402Sru if (!has_arg() || !get_integer(&n)) 776114402Sru n = 1; 777114402Sru input_stack::shift(n); 778114402Sru skip_line(); 779114402Sru} 780114402Sru 781151497Srustatic char get_char_for_escape_name(int allow_space = 0) 782114402Sru{ 783114402Sru int c = get_copy(0); 784114402Sru switch (c) { 785114402Sru case EOF: 786114402Sru copy_mode_error("end of input in escape name"); 787114402Sru return '\0'; 788114402Sru default: 789114402Sru if (!invalid_input_char(c)) 790114402Sru break; 791114402Sru // fall through 792114402Sru case '\n': 793114402Sru if (c == '\n') 794114402Sru input_stack::push(make_temp_iterator("\n")); 795114402Sru // fall through 796114402Sru case ' ': 797114402Sru if (c == ' ' && allow_space) 798114402Sru break; 799114402Sru // fall through 800114402Sru case '\t': 801114402Sru case '\001': 802114402Sru case '\b': 803114402Sru copy_mode_error("%1 is not allowed in an escape name", 804114402Sru input_char_description(c)); 805114402Sru return '\0'; 806114402Sru } 807114402Sru return c; 808114402Sru} 809114402Sru 810114402Srustatic symbol read_two_char_escape_name() 811114402Sru{ 812114402Sru char buf[3]; 813114402Sru buf[0] = get_char_for_escape_name(); 814114402Sru if (buf[0] != '\0') { 815114402Sru buf[1] = get_char_for_escape_name(); 816114402Sru if (buf[1] == '\0') 817114402Sru buf[0] = 0; 818114402Sru else 819114402Sru buf[2] = 0; 820114402Sru } 821114402Sru return symbol(buf); 822114402Sru} 823114402Sru 824114402Srustatic symbol read_long_escape_name(read_mode mode) 825114402Sru{ 826114402Sru int start_level = input_stack::get_level(); 827114402Sru char abuf[ABUF_SIZE]; 828114402Sru char *buf = abuf; 829114402Sru int buf_size = ABUF_SIZE; 830114402Sru int i = 0; 831151497Sru char c; 832114402Sru int have_char = 0; 833114402Sru for (;;) { 834114402Sru c = get_char_for_escape_name(have_char && mode == WITH_ARGS); 835114402Sru if (c == 0) { 836114402Sru if (buf != abuf) 837114402Sru a_delete buf; 838114402Sru return NULL_SYMBOL; 839114402Sru } 840114402Sru have_char = 1; 841114402Sru if (mode == WITH_ARGS && c == ' ') 842114402Sru break; 843114402Sru if (i + 2 > buf_size) { 844114402Sru if (buf == abuf) { 845114402Sru buf = new char[ABUF_SIZE*2]; 846114402Sru memcpy(buf, abuf, buf_size); 847114402Sru buf_size = ABUF_SIZE*2; 848114402Sru } 849114402Sru else { 850114402Sru char *old_buf = buf; 851114402Sru buf = new char[buf_size*2]; 852114402Sru memcpy(buf, old_buf, buf_size); 853114402Sru buf_size *= 2; 854114402Sru a_delete old_buf; 855114402Sru } 856114402Sru } 857114402Sru if (c == ']' && input_stack::get_level() == start_level) 858114402Sru break; 859114402Sru buf[i++] = c; 860114402Sru } 861114402Sru buf[i] = 0; 862114402Sru if (c == ' ') 863114402Sru have_string_arg = 1; 864114402Sru if (buf == abuf) { 865114402Sru if (i == 0) { 866114402Sru if (mode != ALLOW_EMPTY) 867114402Sru copy_mode_error("empty escape name"); 868114402Sru return EMPTY_SYMBOL; 869114402Sru } 870114402Sru return symbol(abuf); 871114402Sru } 872114402Sru else { 873114402Sru symbol s(buf); 874114402Sru a_delete buf; 875114402Sru return s; 876114402Sru } 877114402Sru} 878114402Sru 879114402Srustatic symbol read_escape_name(read_mode mode) 880114402Sru{ 881151497Sru char c = get_char_for_escape_name(); 882114402Sru if (c == 0) 883114402Sru return NULL_SYMBOL; 884114402Sru if (c == '(') 885114402Sru return read_two_char_escape_name(); 886114402Sru if (c == '[' && !compatible_flag) 887114402Sru return read_long_escape_name(mode); 888114402Sru char buf[2]; 889114402Sru buf[0] = c; 890114402Sru buf[1] = '\0'; 891114402Sru return symbol(buf); 892114402Sru} 893114402Sru 894114402Srustatic symbol read_increment_and_escape_name(int *incp) 895114402Sru{ 896151497Sru char c = get_char_for_escape_name(); 897114402Sru switch (c) { 898114402Sru case 0: 899114402Sru *incp = 0; 900114402Sru return NULL_SYMBOL; 901114402Sru case '(': 902114402Sru *incp = 0; 903114402Sru return read_two_char_escape_name(); 904114402Sru case '+': 905114402Sru *incp = 1; 906114402Sru return read_escape_name(); 907114402Sru case '-': 908114402Sru *incp = -1; 909114402Sru return read_escape_name(); 910114402Sru case '[': 911114402Sru if (!compatible_flag) { 912114402Sru *incp = 0; 913114402Sru return read_long_escape_name(); 914114402Sru } 915114402Sru break; 916114402Sru } 917114402Sru *incp = 0; 918114402Sru char buf[2]; 919114402Sru buf[0] = c; 920114402Sru buf[1] = '\0'; 921114402Sru return symbol(buf); 922114402Sru} 923114402Sru 924114402Srustatic int get_copy(node **nd, int defining) 925114402Sru{ 926114402Sru for (;;) { 927114402Sru int c = input_stack::get(nd); 928151497Sru if (c == PUSH_GROFF_MODE) { 929151497Sru input_stack::save_compatible_flag(compatible_flag); 930151497Sru compatible_flag = 0; 931151497Sru continue; 932151497Sru } 933151497Sru if (c == PUSH_COMP_MODE) { 934151497Sru input_stack::save_compatible_flag(compatible_flag); 935151497Sru compatible_flag = 1; 936151497Sru continue; 937151497Sru } 938151497Sru if (c == POP_GROFFCOMP_MODE) { 939151497Sru compatible_flag = input_stack::get_compatible_flag(); 940151497Sru continue; 941151497Sru } 942151497Sru if (c == BEGIN_QUOTE) { 943151497Sru input_stack::increase_level(); 944151497Sru continue; 945151497Sru } 946151497Sru if (c == END_QUOTE) { 947151497Sru input_stack::decrease_level(); 948151497Sru continue; 949151497Sru } 950114402Sru if (c == ESCAPE_NEWLINE) { 951114402Sru if (defining) 952114402Sru return c; 953114402Sru do { 954114402Sru c = input_stack::get(nd); 955114402Sru } while (c == ESCAPE_NEWLINE); 956114402Sru } 957114402Sru if (c != escape_char || escape_char <= 0) 958114402Sru return c; 959114402Sru c = input_stack::peek(); 960114402Sru switch(c) { 961114402Sru case 0: 962114402Sru return escape_char; 963114402Sru case '"': 964114402Sru (void)input_stack::get(0); 965114402Sru while ((c = input_stack::get(0)) != '\n' && c != EOF) 966114402Sru ; 967114402Sru return c; 968114402Sru case '#': // Like \" but newline is ignored. 969114402Sru (void)input_stack::get(0); 970114402Sru while ((c = input_stack::get(0)) != '\n') 971114402Sru if (c == EOF) 972114402Sru return EOF; 973114402Sru break; 974114402Sru case '$': 975114402Sru { 976114402Sru (void)input_stack::get(0); 977114402Sru symbol s = read_escape_name(); 978114402Sru if (!(s.is_null() || s.is_empty())) 979114402Sru interpolate_arg(s); 980114402Sru break; 981114402Sru } 982114402Sru case '*': 983114402Sru { 984114402Sru (void)input_stack::get(0); 985114402Sru symbol s = read_escape_name(WITH_ARGS); 986114402Sru if (!(s.is_null() || s.is_empty())) { 987114402Sru if (have_string_arg) { 988114402Sru have_string_arg = 0; 989114402Sru interpolate_string_with_args(s); 990114402Sru } 991114402Sru else 992114402Sru interpolate_string(s); 993114402Sru } 994114402Sru break; 995114402Sru } 996114402Sru case 'a': 997114402Sru (void)input_stack::get(0); 998114402Sru return '\001'; 999114402Sru case 'e': 1000114402Sru (void)input_stack::get(0); 1001114402Sru return ESCAPE_e; 1002114402Sru case 'E': 1003114402Sru (void)input_stack::get(0); 1004114402Sru return ESCAPE_E; 1005114402Sru case 'n': 1006114402Sru { 1007114402Sru (void)input_stack::get(0); 1008114402Sru int inc; 1009114402Sru symbol s = read_increment_and_escape_name(&inc); 1010114402Sru if (!(s.is_null() || s.is_empty())) 1011114402Sru interpolate_number_reg(s, inc); 1012114402Sru break; 1013114402Sru } 1014114402Sru case 'g': 1015114402Sru { 1016114402Sru (void)input_stack::get(0); 1017114402Sru symbol s = read_escape_name(); 1018114402Sru if (!(s.is_null() || s.is_empty())) 1019114402Sru interpolate_number_format(s); 1020114402Sru break; 1021114402Sru } 1022114402Sru case 't': 1023114402Sru (void)input_stack::get(0); 1024114402Sru return '\t'; 1025114402Sru case 'V': 1026114402Sru { 1027114402Sru (void)input_stack::get(0); 1028114402Sru symbol s = read_escape_name(); 1029114402Sru if (!(s.is_null() || s.is_empty())) 1030114402Sru interpolate_environment_variable(s); 1031114402Sru break; 1032114402Sru } 1033114402Sru case '\n': 1034114402Sru (void)input_stack::get(0); 1035114402Sru if (defining) 1036114402Sru return ESCAPE_NEWLINE; 1037114402Sru break; 1038114402Sru case ' ': 1039114402Sru (void)input_stack::get(0); 1040114402Sru return ESCAPE_SPACE; 1041114402Sru case '~': 1042114402Sru (void)input_stack::get(0); 1043114402Sru return ESCAPE_TILDE; 1044114402Sru case ':': 1045114402Sru (void)input_stack::get(0); 1046114402Sru return ESCAPE_COLON; 1047114402Sru case '|': 1048114402Sru (void)input_stack::get(0); 1049114402Sru return ESCAPE_BAR; 1050114402Sru case '^': 1051114402Sru (void)input_stack::get(0); 1052114402Sru return ESCAPE_CIRCUMFLEX; 1053114402Sru case '{': 1054114402Sru (void)input_stack::get(0); 1055114402Sru return ESCAPE_LEFT_BRACE; 1056114402Sru case '}': 1057114402Sru (void)input_stack::get(0); 1058114402Sru return ESCAPE_RIGHT_BRACE; 1059114402Sru case '`': 1060114402Sru (void)input_stack::get(0); 1061114402Sru return ESCAPE_LEFT_QUOTE; 1062114402Sru case '\'': 1063114402Sru (void)input_stack::get(0); 1064114402Sru return ESCAPE_RIGHT_QUOTE; 1065114402Sru case '-': 1066114402Sru (void)input_stack::get(0); 1067114402Sru return ESCAPE_HYPHEN; 1068114402Sru case '_': 1069114402Sru (void)input_stack::get(0); 1070114402Sru return ESCAPE_UNDERSCORE; 1071114402Sru case 'c': 1072114402Sru (void)input_stack::get(0); 1073114402Sru return ESCAPE_c; 1074114402Sru case '!': 1075114402Sru (void)input_stack::get(0); 1076114402Sru return ESCAPE_BANG; 1077114402Sru case '?': 1078114402Sru (void)input_stack::get(0); 1079114402Sru return ESCAPE_QUESTION; 1080114402Sru case '&': 1081114402Sru (void)input_stack::get(0); 1082114402Sru return ESCAPE_AMPERSAND; 1083114402Sru case ')': 1084114402Sru (void)input_stack::get(0); 1085114402Sru return ESCAPE_RIGHT_PARENTHESIS; 1086114402Sru case '.': 1087114402Sru (void)input_stack::get(0); 1088114402Sru return c; 1089114402Sru case '%': 1090114402Sru (void)input_stack::get(0); 1091114402Sru return ESCAPE_PERCENT; 1092114402Sru default: 1093114402Sru if (c == escape_char) { 1094114402Sru (void)input_stack::get(0); 1095114402Sru return c; 1096114402Sru } 1097114402Sru else 1098114402Sru return escape_char; 1099114402Sru } 1100114402Sru } 1101114402Sru} 1102114402Sru 1103114402Sruclass non_interpreted_char_node : public node { 1104114402Sru unsigned char c; 1105114402Srupublic: 1106114402Sru non_interpreted_char_node(unsigned char); 1107114402Sru node *copy(); 1108114402Sru int interpret(macro *); 1109114402Sru int same(node *); 1110114402Sru const char *type(); 1111114402Sru int force_tprint(); 1112151497Sru int is_tag(); 1113114402Sru}; 1114114402Sru 1115114402Sruint non_interpreted_char_node::same(node *nd) 1116114402Sru{ 1117114402Sru return c == ((non_interpreted_char_node *)nd)->c; 1118114402Sru} 1119114402Sru 1120114402Sruconst char *non_interpreted_char_node::type() 1121114402Sru{ 1122114402Sru return "non_interpreted_char_node"; 1123114402Sru} 1124114402Sru 1125114402Sruint non_interpreted_char_node::force_tprint() 1126114402Sru{ 1127114402Sru return 0; 1128114402Sru} 1129114402Sru 1130151497Sruint non_interpreted_char_node::is_tag() 1131151497Sru{ 1132151497Sru return 0; 1133151497Sru} 1134151497Sru 1135114402Srunon_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n) 1136114402Sru{ 1137114402Sru assert(n != 0); 1138114402Sru} 1139114402Sru 1140114402Srunode *non_interpreted_char_node::copy() 1141114402Sru{ 1142114402Sru return new non_interpreted_char_node(c); 1143114402Sru} 1144114402Sru 1145114402Sruint non_interpreted_char_node::interpret(macro *mac) 1146114402Sru{ 1147114402Sru mac->append(c); 1148114402Sru return 1; 1149114402Sru} 1150114402Sru 1151114402Srustatic void do_width(); 1152114402Srustatic node *do_non_interpreted(); 1153114402Srustatic node *do_special(); 1154114402Srustatic node *do_suppress(symbol nm); 1155114402Srustatic void do_register(); 1156114402Sru 1157114402Srudictionary color_dictionary(501); 1158114402Sru 1159114402Srustatic color *lookup_color(symbol nm) 1160114402Sru{ 1161114402Sru assert(!nm.is_null()); 1162114402Sru if (nm == default_symbol) 1163114402Sru return &default_color; 1164114402Sru color *c = (color *)color_dictionary.lookup(nm); 1165114402Sru if (c == 0) 1166114402Sru warning(WARN_COLOR, "color `%1' not defined", nm.contents()); 1167114402Sru return c; 1168114402Sru} 1169114402Sru 1170114402Sruvoid do_glyph_color(symbol nm) 1171114402Sru{ 1172114402Sru if (nm.is_null()) 1173114402Sru return; 1174114402Sru if (nm.is_empty()) 1175114402Sru curenv->set_glyph_color(curenv->get_prev_glyph_color()); 1176114402Sru else { 1177114402Sru color *tem = lookup_color(nm); 1178114402Sru if (tem) 1179114402Sru curenv->set_glyph_color(tem); 1180114402Sru else 1181151497Sru (void)color_dictionary.lookup(nm, new color(nm)); 1182114402Sru } 1183114402Sru} 1184114402Sru 1185114402Sruvoid do_fill_color(symbol nm) 1186114402Sru{ 1187114402Sru if (nm.is_null()) 1188114402Sru return; 1189114402Sru if (nm.is_empty()) 1190114402Sru curenv->set_fill_color(curenv->get_prev_fill_color()); 1191114402Sru else { 1192114402Sru color *tem = lookup_color(nm); 1193114402Sru if (tem) 1194114402Sru curenv->set_fill_color(tem); 1195114402Sru else 1196151497Sru (void)color_dictionary.lookup(nm, new color(nm)); 1197114402Sru } 1198114402Sru} 1199114402Sru 1200114402Srustatic unsigned int get_color_element(const char *scheme, const char *col) 1201114402Sru{ 1202114402Sru units val; 1203114402Sru if (!get_number(&val, 'f')) { 1204114402Sru warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme); 1205114402Sru tok.next(); 1206114402Sru return 0; 1207114402Sru } 1208114402Sru if (val < 0) { 1209114402Sru warning(WARN_RANGE, "%1 cannot be negative: set to 0", col); 1210114402Sru return 0; 1211114402Sru } 1212114402Sru if (val > color::MAX_COLOR_VAL+1) { 1213114402Sru warning(WARN_RANGE, "%1 cannot be greater than 1", col); 1214114402Sru // we change 0x10000 to 0xffff 1215114402Sru return color::MAX_COLOR_VAL; 1216114402Sru } 1217114402Sru return (unsigned int)val; 1218114402Sru} 1219114402Sru 1220114402Srustatic color *read_rgb(char end = 0) 1221114402Sru{ 1222114402Sru symbol component = do_get_long_name(0, end); 1223114402Sru if (component.is_null()) { 1224114402Sru warning(WARN_COLOR, "missing rgb color values"); 1225114402Sru return 0; 1226114402Sru } 1227114402Sru const char *s = component.contents(); 1228114402Sru color *col = new color; 1229114402Sru if (*s == '#') { 1230114402Sru if (!col->read_rgb(s)) { 1231114402Sru warning(WARN_COLOR, "expecting rgb color definition not `%1'", s); 1232114402Sru delete col; 1233114402Sru return 0; 1234114402Sru } 1235114402Sru } 1236114402Sru else { 1237114402Sru if (!end) 1238114402Sru input_stack::push(make_temp_iterator(" ")); 1239114402Sru input_stack::push(make_temp_iterator(s)); 1240114402Sru tok.next(); 1241114402Sru unsigned int r = get_color_element("rgb color", "red component"); 1242114402Sru unsigned int g = get_color_element("rgb color", "green component"); 1243114402Sru unsigned int b = get_color_element("rgb color", "blue component"); 1244114402Sru col->set_rgb(r, g, b); 1245114402Sru } 1246114402Sru return col; 1247114402Sru} 1248114402Sru 1249114402Srustatic color *read_cmy(char end = 0) 1250114402Sru{ 1251114402Sru symbol component = do_get_long_name(0, end); 1252114402Sru if (component.is_null()) { 1253114402Sru warning(WARN_COLOR, "missing cmy color values"); 1254114402Sru return 0; 1255114402Sru } 1256114402Sru const char *s = component.contents(); 1257114402Sru color *col = new color; 1258114402Sru if (*s == '#') { 1259114402Sru if (!col->read_cmy(s)) { 1260114402Sru warning(WARN_COLOR, "expecting cmy color definition not `%1'", s); 1261114402Sru delete col; 1262114402Sru return 0; 1263114402Sru } 1264114402Sru } 1265114402Sru else { 1266114402Sru if (!end) 1267114402Sru input_stack::push(make_temp_iterator(" ")); 1268114402Sru input_stack::push(make_temp_iterator(s)); 1269114402Sru tok.next(); 1270114402Sru unsigned int c = get_color_element("cmy color", "cyan component"); 1271114402Sru unsigned int m = get_color_element("cmy color", "magenta component"); 1272114402Sru unsigned int y = get_color_element("cmy color", "yellow component"); 1273114402Sru col->set_cmy(c, m, y); 1274114402Sru } 1275114402Sru return col; 1276114402Sru} 1277114402Sru 1278114402Srustatic color *read_cmyk(char end = 0) 1279114402Sru{ 1280114402Sru symbol component = do_get_long_name(0, end); 1281114402Sru if (component.is_null()) { 1282114402Sru warning(WARN_COLOR, "missing cmyk color values"); 1283114402Sru return 0; 1284114402Sru } 1285114402Sru const char *s = component.contents(); 1286114402Sru color *col = new color; 1287114402Sru if (*s == '#') { 1288114402Sru if (!col->read_cmyk(s)) { 1289114402Sru warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s); 1290114402Sru delete col; 1291114402Sru return 0; 1292114402Sru } 1293114402Sru } 1294114402Sru else { 1295114402Sru if (!end) 1296114402Sru input_stack::push(make_temp_iterator(" ")); 1297114402Sru input_stack::push(make_temp_iterator(s)); 1298114402Sru tok.next(); 1299114402Sru unsigned int c = get_color_element("cmyk color", "cyan component"); 1300114402Sru unsigned int m = get_color_element("cmyk color", "magenta component"); 1301114402Sru unsigned int y = get_color_element("cmyk color", "yellow component"); 1302114402Sru unsigned int k = get_color_element("cmyk color", "black component"); 1303114402Sru col->set_cmyk(c, m, y, k); 1304114402Sru } 1305114402Sru return col; 1306114402Sru} 1307114402Sru 1308114402Srustatic color *read_gray(char end = 0) 1309114402Sru{ 1310114402Sru symbol component = do_get_long_name(0, end); 1311114402Sru if (component.is_null()) { 1312114402Sru warning(WARN_COLOR, "missing gray values"); 1313114402Sru return 0; 1314114402Sru } 1315114402Sru const char *s = component.contents(); 1316114402Sru color *col = new color; 1317114402Sru if (*s == '#') { 1318114402Sru if (!col->read_gray(s)) { 1319114402Sru warning(WARN_COLOR, "`expecting a gray definition not `%1'", s); 1320114402Sru delete col; 1321114402Sru return 0; 1322114402Sru } 1323114402Sru } 1324114402Sru else { 1325114402Sru if (!end) 1326114402Sru input_stack::push(make_temp_iterator("\n")); 1327114402Sru input_stack::push(make_temp_iterator(s)); 1328114402Sru tok.next(); 1329114402Sru unsigned int g = get_color_element("gray", "gray value"); 1330114402Sru col->set_gray(g); 1331114402Sru } 1332114402Sru return col; 1333114402Sru} 1334114402Sru 1335114402Srustatic void activate_color() 1336114402Sru{ 1337114402Sru int n; 1338114402Sru if (has_arg() && get_integer(&n)) 1339114402Sru color_flag = n != 0; 1340114402Sru else 1341114402Sru color_flag = 1; 1342114402Sru skip_line(); 1343114402Sru} 1344114402Sru 1345114402Srustatic void define_color() 1346114402Sru{ 1347114402Sru symbol color_name = get_long_name(1); 1348114402Sru if (color_name.is_null()) { 1349114402Sru skip_line(); 1350114402Sru return; 1351114402Sru } 1352114402Sru if (color_name == default_symbol) { 1353114402Sru warning(WARN_COLOR, "default color can't be redefined"); 1354114402Sru skip_line(); 1355114402Sru return; 1356114402Sru } 1357114402Sru symbol style = get_long_name(1); 1358114402Sru if (style.is_null()) { 1359114402Sru skip_line(); 1360114402Sru return; 1361114402Sru } 1362114402Sru color *col; 1363114402Sru if (strcmp(style.contents(), "rgb") == 0) 1364114402Sru col = read_rgb(); 1365114402Sru else if (strcmp(style.contents(), "cmyk") == 0) 1366114402Sru col = read_cmyk(); 1367114402Sru else if (strcmp(style.contents(), "gray") == 0) 1368114402Sru col = read_gray(); 1369114402Sru else if (strcmp(style.contents(), "grey") == 0) 1370114402Sru col = read_gray(); 1371114402Sru else if (strcmp(style.contents(), "cmy") == 0) 1372114402Sru col = read_cmy(); 1373114402Sru else { 1374114402Sru warning(WARN_COLOR, 1375114402Sru "unknown color space `%1'; use rgb, cmyk, gray or cmy", 1376114402Sru style.contents()); 1377114402Sru skip_line(); 1378114402Sru return; 1379114402Sru } 1380151497Sru if (col) { 1381151497Sru col->nm = color_name; 1382114402Sru (void)color_dictionary.lookup(color_name, col); 1383151497Sru } 1384114402Sru skip_line(); 1385114402Sru} 1386114402Sru 1387114402Srustatic node *do_overstrike() 1388114402Sru{ 1389114402Sru token start; 1390114402Sru overstrike_node *on = new overstrike_node; 1391114402Sru int start_level = input_stack::get_level(); 1392114402Sru start.next(); 1393114402Sru for (;;) { 1394114402Sru tok.next(); 1395114402Sru if (tok.newline() || tok.eof()) { 1396114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1397114402Sru input_stack::push(make_temp_iterator("\n")); 1398114402Sru break; 1399114402Sru } 1400114402Sru if (tok == start 1401114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 1402114402Sru break; 1403114402Sru charinfo *ci = tok.get_char(1); 1404114402Sru if (ci) { 1405114402Sru node *n = curenv->make_char_node(ci); 1406114402Sru if (n) 1407114402Sru on->overstrike(n); 1408114402Sru } 1409114402Sru } 1410114402Sru return on; 1411114402Sru} 1412114402Sru 1413114402Srustatic node *do_bracket() 1414114402Sru{ 1415114402Sru token start; 1416114402Sru bracket_node *bn = new bracket_node; 1417114402Sru start.next(); 1418114402Sru int start_level = input_stack::get_level(); 1419114402Sru for (;;) { 1420114402Sru tok.next(); 1421114402Sru if (tok.eof()) { 1422114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1423114402Sru break; 1424114402Sru } 1425114402Sru if (tok.newline()) { 1426114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1427114402Sru input_stack::push(make_temp_iterator("\n")); 1428114402Sru break; 1429114402Sru } 1430114402Sru if (tok == start 1431114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 1432114402Sru break; 1433114402Sru charinfo *ci = tok.get_char(1); 1434114402Sru if (ci) { 1435114402Sru node *n = curenv->make_char_node(ci); 1436114402Sru if (n) 1437114402Sru bn->bracket(n); 1438114402Sru } 1439114402Sru } 1440114402Sru return bn; 1441114402Sru} 1442114402Sru 1443114402Srustatic int do_name_test() 1444114402Sru{ 1445114402Sru token start; 1446114402Sru start.next(); 1447114402Sru int start_level = input_stack::get_level(); 1448114402Sru int bad_char = 0; 1449114402Sru int some_char = 0; 1450114402Sru for (;;) { 1451114402Sru tok.next(); 1452114402Sru if (tok.newline() || tok.eof()) { 1453114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1454114402Sru input_stack::push(make_temp_iterator("\n")); 1455114402Sru break; 1456114402Sru } 1457114402Sru if (tok == start 1458114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 1459114402Sru break; 1460114402Sru if (!tok.ch()) 1461114402Sru bad_char = 1; 1462114402Sru some_char = 1; 1463114402Sru } 1464114402Sru return some_char && !bad_char; 1465114402Sru} 1466114402Sru 1467114402Srustatic int do_expr_test() 1468114402Sru{ 1469114402Sru token start; 1470114402Sru start.next(); 1471114402Sru int start_level = input_stack::get_level(); 1472114402Sru if (!start.delimiter(1)) 1473114402Sru return 0; 1474114402Sru tok.next(); 1475114402Sru // disable all warning and error messages temporarily 1476114402Sru int saved_warning_mask = warning_mask; 1477114402Sru int saved_inhibit_errors = inhibit_errors; 1478114402Sru warning_mask = 0; 1479114402Sru inhibit_errors = 1; 1480114402Sru int dummy; 1481114402Sru int result = get_number_rigidly(&dummy, 'u'); 1482114402Sru warning_mask = saved_warning_mask; 1483114402Sru inhibit_errors = saved_inhibit_errors; 1484114402Sru if (tok == start && input_stack::get_level() == start_level) 1485114402Sru return result; 1486114402Sru // ignore everything up to the delimiter in case we aren't right there 1487114402Sru for (;;) { 1488114402Sru tok.next(); 1489114402Sru if (tok.newline() || tok.eof()) { 1490114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1491114402Sru input_stack::push(make_temp_iterator("\n")); 1492114402Sru break; 1493114402Sru } 1494114402Sru if (tok == start && input_stack::get_level() == start_level) 1495114402Sru break; 1496114402Sru } 1497114402Sru return 0; 1498114402Sru} 1499114402Sru 1500114402Sru#if 0 1501114402Srustatic node *do_zero_width() 1502114402Sru{ 1503114402Sru token start; 1504114402Sru start.next(); 1505114402Sru int start_level = input_stack::get_level(); 1506114402Sru environment env(curenv); 1507114402Sru environment *oldenv = curenv; 1508114402Sru curenv = &env; 1509114402Sru for (;;) { 1510114402Sru tok.next(); 1511114402Sru if (tok.newline() || tok.eof()) { 1512114402Sru error("missing closing delimiter"); 1513114402Sru break; 1514114402Sru } 1515114402Sru if (tok == start 1516114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 1517114402Sru break; 1518114402Sru tok.process(); 1519114402Sru } 1520114402Sru curenv = oldenv; 1521114402Sru node *rev = env.extract_output_line(); 1522114402Sru node *n = 0; 1523114402Sru while (rev) { 1524114402Sru node *tem = rev; 1525114402Sru rev = rev->next; 1526114402Sru tem->next = n; 1527114402Sru n = tem; 1528114402Sru } 1529114402Sru return new zero_width_node(n); 1530114402Sru} 1531114402Sru 1532114402Sru#else 1533114402Sru 1534114402Sru// It's undesirable for \Z to change environments, because then 1535114402Sru// \n(.w won't work as expected. 1536114402Sru 1537114402Srustatic node *do_zero_width() 1538114402Sru{ 1539114402Sru node *rev = new dummy_node; 1540114402Sru token start; 1541114402Sru start.next(); 1542114402Sru int start_level = input_stack::get_level(); 1543114402Sru for (;;) { 1544114402Sru tok.next(); 1545114402Sru if (tok.newline() || tok.eof()) { 1546114402Sru warning(WARN_DELIM, "missing closing delimiter"); 1547114402Sru input_stack::push(make_temp_iterator("\n")); 1548114402Sru break; 1549114402Sru } 1550114402Sru if (tok == start 1551114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 1552114402Sru break; 1553114402Sru if (!tok.add_to_node_list(&rev)) 1554114402Sru error("invalid token in argument to \\Z"); 1555114402Sru } 1556114402Sru node *n = 0; 1557114402Sru while (rev) { 1558114402Sru node *tem = rev; 1559114402Sru rev = rev->next; 1560114402Sru tem->next = n; 1561114402Sru n = tem; 1562114402Sru } 1563114402Sru return new zero_width_node(n); 1564114402Sru} 1565114402Sru 1566114402Sru#endif 1567114402Sru 1568114402Srutoken_node *node::get_token_node() 1569114402Sru{ 1570114402Sru return 0; 1571114402Sru} 1572114402Sru 1573114402Sruclass token_node : public node { 1574114402Srupublic: 1575114402Sru token tk; 1576114402Sru token_node(const token &t); 1577114402Sru node *copy(); 1578114402Sru token_node *get_token_node(); 1579114402Sru int same(node *); 1580114402Sru const char *type(); 1581114402Sru int force_tprint(); 1582151497Sru int is_tag(); 1583114402Sru}; 1584114402Sru 1585114402Srutoken_node::token_node(const token &t) : tk(t) 1586114402Sru{ 1587114402Sru} 1588114402Sru 1589114402Srunode *token_node::copy() 1590114402Sru{ 1591114402Sru return new token_node(tk); 1592114402Sru} 1593114402Sru 1594114402Srutoken_node *token_node::get_token_node() 1595114402Sru{ 1596114402Sru return this; 1597114402Sru} 1598114402Sru 1599114402Sruint token_node::same(node *nd) 1600114402Sru{ 1601114402Sru return tk == ((token_node *)nd)->tk; 1602114402Sru} 1603114402Sru 1604114402Sruconst char *token_node::type() 1605114402Sru{ 1606114402Sru return "token_node"; 1607114402Sru} 1608114402Sru 1609114402Sruint token_node::force_tprint() 1610114402Sru{ 1611114402Sru return 0; 1612114402Sru} 1613114402Sru 1614151497Sruint token_node::is_tag() 1615151497Sru{ 1616151497Sru return 0; 1617151497Sru} 1618151497Sru 1619114402Srutoken::token() : nd(0), type(TOKEN_EMPTY) 1620114402Sru{ 1621114402Sru} 1622114402Sru 1623114402Srutoken::~token() 1624114402Sru{ 1625114402Sru delete nd; 1626114402Sru} 1627114402Sru 1628114402Srutoken::token(const token &t) 1629114402Sru: nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type) 1630114402Sru{ 1631114402Sru // Use two statements to work around bug in SGI C++. 1632114402Sru node *tem = t.nd; 1633114402Sru nd = tem ? tem->copy() : 0; 1634114402Sru} 1635114402Sru 1636114402Sruvoid token::operator=(const token &t) 1637114402Sru{ 1638114402Sru delete nd; 1639114402Sru nm = t.nm; 1640114402Sru // Use two statements to work around bug in SGI C++. 1641114402Sru node *tem = t.nd; 1642114402Sru nd = tem ? tem->copy() : 0; 1643114402Sru c = t.c; 1644114402Sru val = t.val; 1645114402Sru dim = t.dim; 1646114402Sru type = t.type; 1647114402Sru} 1648114402Sru 1649114402Sruvoid token::skip() 1650114402Sru{ 1651114402Sru while (space()) 1652114402Sru next(); 1653114402Sru} 1654114402Sru 1655114402Sruint has_arg() 1656114402Sru{ 1657114402Sru while (tok.space()) 1658114402Sru tok.next(); 1659114402Sru return !tok.newline(); 1660114402Sru} 1661114402Sru 1662114402Sruvoid token::make_space() 1663114402Sru{ 1664114402Sru type = TOKEN_SPACE; 1665114402Sru} 1666114402Sru 1667114402Sruvoid token::make_newline() 1668114402Sru{ 1669114402Sru type = TOKEN_NEWLINE; 1670114402Sru} 1671114402Sru 1672114402Sruvoid token::next() 1673114402Sru{ 1674114402Sru if (nd) { 1675114402Sru delete nd; 1676114402Sru nd = 0; 1677114402Sru } 1678114402Sru units x; 1679114402Sru for (;;) { 1680151497Sru node *n = 0; 1681114402Sru int cc = input_stack::get(&n); 1682114402Sru if (cc != escape_char || escape_char == 0) { 1683114402Sru handle_normal_char: 1684114402Sru switch(cc) { 1685151497Sru case PUSH_GROFF_MODE: 1686114402Sru input_stack::save_compatible_flag(compatible_flag); 1687114402Sru compatible_flag = 0; 1688114402Sru continue; 1689151497Sru case PUSH_COMP_MODE: 1690151497Sru input_stack::save_compatible_flag(compatible_flag); 1691151497Sru compatible_flag = 1; 1692151497Sru continue; 1693151497Sru case POP_GROFFCOMP_MODE: 1694114402Sru compatible_flag = input_stack::get_compatible_flag(); 1695114402Sru continue; 1696151497Sru case BEGIN_QUOTE: 1697151497Sru input_stack::increase_level(); 1698151497Sru continue; 1699151497Sru case END_QUOTE: 1700151497Sru input_stack::decrease_level(); 1701151497Sru continue; 1702114402Sru case EOF: 1703114402Sru type = TOKEN_EOF; 1704114402Sru return; 1705114402Sru case TRANSPARENT_FILE_REQUEST: 1706114402Sru case TITLE_REQUEST: 1707114402Sru case COPY_FILE_REQUEST: 1708114402Sru#ifdef COLUMN 1709114402Sru case VJUSTIFY_REQUEST: 1710114402Sru#endif /* COLUMN */ 1711114402Sru type = TOKEN_REQUEST; 1712114402Sru c = cc; 1713114402Sru return; 1714114402Sru case BEGIN_TRAP: 1715114402Sru type = TOKEN_BEGIN_TRAP; 1716114402Sru return; 1717114402Sru case END_TRAP: 1718114402Sru type = TOKEN_END_TRAP; 1719114402Sru return; 1720114402Sru case LAST_PAGE_EJECTOR: 1721114402Sru seen_last_page_ejector = 1; 1722114402Sru // fall through 1723114402Sru case PAGE_EJECTOR: 1724114402Sru type = TOKEN_PAGE_EJECTOR; 1725114402Sru return; 1726114402Sru case ESCAPE_PERCENT: 1727114402Sru ESCAPE_PERCENT: 1728114402Sru type = TOKEN_HYPHEN_INDICATOR; 1729114402Sru return; 1730114402Sru case ESCAPE_SPACE: 1731114402Sru ESCAPE_SPACE: 1732114402Sru type = TOKEN_UNSTRETCHABLE_SPACE; 1733114402Sru return; 1734114402Sru case ESCAPE_TILDE: 1735114402Sru ESCAPE_TILDE: 1736114402Sru type = TOKEN_STRETCHABLE_SPACE; 1737114402Sru return; 1738114402Sru case ESCAPE_COLON: 1739114402Sru ESCAPE_COLON: 1740114402Sru type = TOKEN_ZERO_WIDTH_BREAK; 1741114402Sru return; 1742114402Sru case ESCAPE_e: 1743114402Sru ESCAPE_e: 1744114402Sru type = TOKEN_ESCAPE; 1745114402Sru return; 1746114402Sru case ESCAPE_E: 1747114402Sru goto handle_escape_char; 1748114402Sru case ESCAPE_BAR: 1749114402Sru ESCAPE_BAR: 1750114402Sru type = TOKEN_NODE; 1751114402Sru nd = new hmotion_node(curenv->get_narrow_space_width(), 1752114402Sru curenv->get_fill_color()); 1753114402Sru return; 1754114402Sru case ESCAPE_CIRCUMFLEX: 1755114402Sru ESCAPE_CIRCUMFLEX: 1756114402Sru type = TOKEN_NODE; 1757114402Sru nd = new hmotion_node(curenv->get_half_narrow_space_width(), 1758114402Sru curenv->get_fill_color()); 1759114402Sru return; 1760114402Sru case ESCAPE_NEWLINE: 1761114402Sru have_input = 0; 1762114402Sru break; 1763114402Sru case ESCAPE_LEFT_BRACE: 1764114402Sru ESCAPE_LEFT_BRACE: 1765114402Sru type = TOKEN_LEFT_BRACE; 1766114402Sru return; 1767114402Sru case ESCAPE_RIGHT_BRACE: 1768114402Sru ESCAPE_RIGHT_BRACE: 1769114402Sru type = TOKEN_RIGHT_BRACE; 1770114402Sru return; 1771114402Sru case ESCAPE_LEFT_QUOTE: 1772114402Sru ESCAPE_LEFT_QUOTE: 1773114402Sru type = TOKEN_SPECIAL; 1774114402Sru nm = symbol("ga"); 1775114402Sru return; 1776114402Sru case ESCAPE_RIGHT_QUOTE: 1777114402Sru ESCAPE_RIGHT_QUOTE: 1778114402Sru type = TOKEN_SPECIAL; 1779114402Sru nm = symbol("aa"); 1780114402Sru return; 1781114402Sru case ESCAPE_HYPHEN: 1782114402Sru ESCAPE_HYPHEN: 1783114402Sru type = TOKEN_SPECIAL; 1784114402Sru nm = symbol("-"); 1785114402Sru return; 1786114402Sru case ESCAPE_UNDERSCORE: 1787114402Sru ESCAPE_UNDERSCORE: 1788114402Sru type = TOKEN_SPECIAL; 1789114402Sru nm = symbol("ul"); 1790114402Sru return; 1791114402Sru case ESCAPE_c: 1792114402Sru ESCAPE_c: 1793114402Sru type = TOKEN_INTERRUPT; 1794114402Sru return; 1795114402Sru case ESCAPE_BANG: 1796114402Sru ESCAPE_BANG: 1797114402Sru type = TOKEN_TRANSPARENT; 1798114402Sru return; 1799114402Sru case ESCAPE_QUESTION: 1800114402Sru ESCAPE_QUESTION: 1801114402Sru nd = do_non_interpreted(); 1802114402Sru if (nd) { 1803114402Sru type = TOKEN_NODE; 1804114402Sru return; 1805114402Sru } 1806114402Sru break; 1807114402Sru case ESCAPE_AMPERSAND: 1808114402Sru ESCAPE_AMPERSAND: 1809114402Sru type = TOKEN_DUMMY; 1810114402Sru return; 1811114402Sru case ESCAPE_RIGHT_PARENTHESIS: 1812114402Sru ESCAPE_RIGHT_PARENTHESIS: 1813114402Sru type = TOKEN_TRANSPARENT_DUMMY; 1814114402Sru return; 1815114402Sru case '\b': 1816114402Sru type = TOKEN_BACKSPACE; 1817114402Sru return; 1818114402Sru case ' ': 1819114402Sru type = TOKEN_SPACE; 1820114402Sru return; 1821114402Sru case '\t': 1822114402Sru type = TOKEN_TAB; 1823114402Sru return; 1824114402Sru case '\n': 1825114402Sru type = TOKEN_NEWLINE; 1826114402Sru return; 1827114402Sru case '\001': 1828114402Sru type = TOKEN_LEADER; 1829114402Sru return; 1830114402Sru case 0: 1831114402Sru { 1832114402Sru assert(n != 0); 1833114402Sru token_node *tn = n->get_token_node(); 1834114402Sru if (tn) { 1835114402Sru *this = tn->tk; 1836114402Sru delete tn; 1837114402Sru } 1838114402Sru else { 1839114402Sru nd = n; 1840114402Sru type = TOKEN_NODE; 1841114402Sru } 1842114402Sru } 1843114402Sru return; 1844114402Sru default: 1845114402Sru type = TOKEN_CHAR; 1846114402Sru c = cc; 1847114402Sru return; 1848114402Sru } 1849114402Sru } 1850114402Sru else { 1851114402Sru handle_escape_char: 1852151497Sru cc = input_stack::get(&n); 1853114402Sru switch(cc) { 1854114402Sru case '(': 1855114402Sru nm = read_two_char_escape_name(); 1856114402Sru type = TOKEN_SPECIAL; 1857114402Sru return; 1858114402Sru case EOF: 1859114402Sru type = TOKEN_EOF; 1860114402Sru error("end of input after escape character"); 1861114402Sru return; 1862114402Sru case '`': 1863114402Sru goto ESCAPE_LEFT_QUOTE; 1864114402Sru case '\'': 1865114402Sru goto ESCAPE_RIGHT_QUOTE; 1866114402Sru case '-': 1867114402Sru goto ESCAPE_HYPHEN; 1868114402Sru case '_': 1869114402Sru goto ESCAPE_UNDERSCORE; 1870114402Sru case '%': 1871114402Sru goto ESCAPE_PERCENT; 1872114402Sru case ' ': 1873114402Sru goto ESCAPE_SPACE; 1874114402Sru case '0': 1875114402Sru nd = new hmotion_node(curenv->get_digit_width(), 1876114402Sru curenv->get_fill_color()); 1877114402Sru type = TOKEN_NODE; 1878114402Sru return; 1879114402Sru case '|': 1880114402Sru goto ESCAPE_BAR; 1881114402Sru case '^': 1882114402Sru goto ESCAPE_CIRCUMFLEX; 1883114402Sru case '/': 1884114402Sru type = TOKEN_ITALIC_CORRECTION; 1885114402Sru return; 1886114402Sru case ',': 1887114402Sru type = TOKEN_NODE; 1888114402Sru nd = new left_italic_corrected_node; 1889114402Sru return; 1890114402Sru case '&': 1891114402Sru goto ESCAPE_AMPERSAND; 1892114402Sru case ')': 1893114402Sru goto ESCAPE_RIGHT_PARENTHESIS; 1894114402Sru case '!': 1895114402Sru goto ESCAPE_BANG; 1896114402Sru case '?': 1897114402Sru goto ESCAPE_QUESTION; 1898114402Sru case '~': 1899114402Sru goto ESCAPE_TILDE; 1900114402Sru case ':': 1901114402Sru goto ESCAPE_COLON; 1902114402Sru case '"': 1903114402Sru while ((cc = input_stack::get(0)) != '\n' && cc != EOF) 1904114402Sru ; 1905114402Sru if (cc == '\n') 1906114402Sru type = TOKEN_NEWLINE; 1907114402Sru else 1908114402Sru type = TOKEN_EOF; 1909114402Sru return; 1910114402Sru case '#': // Like \" but newline is ignored. 1911114402Sru while ((cc = input_stack::get(0)) != '\n') 1912114402Sru if (cc == EOF) { 1913114402Sru type = TOKEN_EOF; 1914114402Sru return; 1915114402Sru } 1916114402Sru break; 1917114402Sru case '$': 1918114402Sru { 1919114402Sru symbol s = read_escape_name(); 1920114402Sru if (!(s.is_null() || s.is_empty())) 1921114402Sru interpolate_arg(s); 1922114402Sru break; 1923114402Sru } 1924114402Sru case '*': 1925114402Sru { 1926114402Sru symbol s = read_escape_name(WITH_ARGS); 1927114402Sru if (!(s.is_null() || s.is_empty())) { 1928114402Sru if (have_string_arg) { 1929114402Sru have_string_arg = 0; 1930114402Sru interpolate_string_with_args(s); 1931114402Sru } 1932114402Sru else 1933114402Sru interpolate_string(s); 1934114402Sru } 1935114402Sru break; 1936114402Sru } 1937114402Sru case 'a': 1938114402Sru nd = new non_interpreted_char_node('\001'); 1939114402Sru type = TOKEN_NODE; 1940114402Sru return; 1941114402Sru case 'A': 1942114402Sru c = '0' + do_name_test(); 1943114402Sru type = TOKEN_CHAR; 1944114402Sru return; 1945114402Sru case 'b': 1946114402Sru nd = do_bracket(); 1947114402Sru type = TOKEN_NODE; 1948114402Sru return; 1949114402Sru case 'B': 1950114402Sru c = '0' + do_expr_test(); 1951114402Sru type = TOKEN_CHAR; 1952114402Sru return; 1953114402Sru case 'c': 1954114402Sru goto ESCAPE_c; 1955114402Sru case 'C': 1956114402Sru nm = get_delim_name(); 1957114402Sru if (nm.is_null()) 1958114402Sru break; 1959114402Sru type = TOKEN_SPECIAL; 1960114402Sru return; 1961114402Sru case 'd': 1962114402Sru type = TOKEN_NODE; 1963114402Sru nd = new vmotion_node(curenv->get_size() / 2, 1964114402Sru curenv->get_fill_color()); 1965114402Sru return; 1966114402Sru case 'D': 1967114402Sru nd = read_draw_node(); 1968114402Sru if (!nd) 1969114402Sru break; 1970114402Sru type = TOKEN_NODE; 1971114402Sru return; 1972114402Sru case 'e': 1973114402Sru goto ESCAPE_e; 1974114402Sru case 'E': 1975114402Sru goto handle_escape_char; 1976114402Sru case 'f': 1977114402Sru { 1978114402Sru symbol s = read_escape_name(ALLOW_EMPTY); 1979114402Sru if (s.is_null()) 1980114402Sru break; 1981114402Sru const char *p; 1982114402Sru for (p = s.contents(); *p != '\0'; p++) 1983114402Sru if (!csdigit(*p)) 1984114402Sru break; 1985114402Sru if (*p || s.is_empty()) 1986114402Sru curenv->set_font(s); 1987114402Sru else 1988114402Sru curenv->set_font(atoi(s.contents())); 1989114402Sru if (!compatible_flag) 1990114402Sru have_input = 1; 1991114402Sru break; 1992114402Sru } 1993114402Sru case 'F': 1994114402Sru { 1995114402Sru symbol s = read_escape_name(ALLOW_EMPTY); 1996114402Sru if (s.is_null()) 1997114402Sru break; 1998114402Sru curenv->set_family(s); 1999114402Sru have_input = 1; 2000114402Sru break; 2001114402Sru } 2002114402Sru case 'g': 2003114402Sru { 2004114402Sru symbol s = read_escape_name(); 2005114402Sru if (!(s.is_null() || s.is_empty())) 2006114402Sru interpolate_number_format(s); 2007114402Sru break; 2008114402Sru } 2009114402Sru case 'h': 2010114402Sru if (!get_delim_number(&x, 'm')) 2011114402Sru break; 2012114402Sru type = TOKEN_NODE; 2013114402Sru nd = new hmotion_node(x, curenv->get_fill_color()); 2014114402Sru return; 2015114402Sru case 'H': 2016114402Sru // don't take height increments relative to previous height if 2017114402Sru // in compatibility mode 2018114402Sru if (!compatible_flag && curenv->get_char_height()) 2019114402Sru { 2020114402Sru if (get_delim_number(&x, 'z', curenv->get_char_height())) 2021114402Sru curenv->set_char_height(x); 2022114402Sru } 2023114402Sru else 2024114402Sru { 2025114402Sru if (get_delim_number(&x, 'z', curenv->get_requested_point_size())) 2026114402Sru curenv->set_char_height(x); 2027114402Sru } 2028114402Sru if (!compatible_flag) 2029114402Sru have_input = 1; 2030114402Sru break; 2031114402Sru case 'k': 2032114402Sru nm = read_escape_name(); 2033114402Sru if (nm.is_null() || nm.is_empty()) 2034114402Sru break; 2035114402Sru type = TOKEN_MARK_INPUT; 2036114402Sru return; 2037114402Sru case 'l': 2038114402Sru case 'L': 2039114402Sru { 2040114402Sru charinfo *s = 0; 2041114402Sru if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s)) 2042114402Sru break; 2043114402Sru if (s == 0) 2044114402Sru s = get_charinfo(cc == 'l' ? "ru" : "br"); 2045114402Sru type = TOKEN_NODE; 2046151497Sru node *char_node = curenv->make_char_node(s); 2047114402Sru if (cc == 'l') 2048151497Sru nd = new hline_node(x, char_node); 2049114402Sru else 2050151497Sru nd = new vline_node(x, char_node); 2051114402Sru return; 2052114402Sru } 2053114402Sru case 'm': 2054114402Sru do_glyph_color(read_escape_name(ALLOW_EMPTY)); 2055114402Sru if (!compatible_flag) 2056114402Sru have_input = 1; 2057114402Sru break; 2058114402Sru case 'M': 2059114402Sru do_fill_color(read_escape_name(ALLOW_EMPTY)); 2060114402Sru if (!compatible_flag) 2061114402Sru have_input = 1; 2062114402Sru break; 2063114402Sru case 'n': 2064114402Sru { 2065114402Sru int inc; 2066114402Sru symbol s = read_increment_and_escape_name(&inc); 2067114402Sru if (!(s.is_null() || s.is_empty())) 2068114402Sru interpolate_number_reg(s, inc); 2069114402Sru break; 2070114402Sru } 2071114402Sru case 'N': 2072114402Sru if (!get_delim_number(&val, 0)) 2073114402Sru break; 2074114402Sru type = TOKEN_NUMBERED_CHAR; 2075114402Sru return; 2076114402Sru case 'o': 2077114402Sru nd = do_overstrike(); 2078114402Sru type = TOKEN_NODE; 2079114402Sru return; 2080114402Sru case 'O': 2081114402Sru nd = do_suppress(read_escape_name()); 2082114402Sru if (!nd) 2083114402Sru break; 2084114402Sru type = TOKEN_NODE; 2085114402Sru return; 2086114402Sru case 'p': 2087114402Sru type = TOKEN_SPREAD; 2088114402Sru return; 2089114402Sru case 'r': 2090114402Sru type = TOKEN_NODE; 2091114402Sru nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color()); 2092114402Sru return; 2093114402Sru case 'R': 2094114402Sru do_register(); 2095114402Sru if (!compatible_flag) 2096114402Sru have_input = 1; 2097114402Sru break; 2098114402Sru case 's': 2099114402Sru if (read_size(&x)) 2100114402Sru curenv->set_size(x); 2101114402Sru if (!compatible_flag) 2102114402Sru have_input = 1; 2103114402Sru break; 2104114402Sru case 'S': 2105114402Sru if (get_delim_number(&x, 0)) 2106114402Sru curenv->set_char_slant(x); 2107114402Sru if (!compatible_flag) 2108114402Sru have_input = 1; 2109114402Sru break; 2110114402Sru case 't': 2111114402Sru type = TOKEN_NODE; 2112114402Sru nd = new non_interpreted_char_node('\t'); 2113114402Sru return; 2114114402Sru case 'u': 2115114402Sru type = TOKEN_NODE; 2116114402Sru nd = new vmotion_node(-curenv->get_size() / 2, 2117114402Sru curenv->get_fill_color()); 2118114402Sru return; 2119114402Sru case 'v': 2120114402Sru if (!get_delim_number(&x, 'v')) 2121114402Sru break; 2122114402Sru type = TOKEN_NODE; 2123114402Sru nd = new vmotion_node(x, curenv->get_fill_color()); 2124114402Sru return; 2125114402Sru case 'V': 2126114402Sru { 2127114402Sru symbol s = read_escape_name(); 2128114402Sru if (!(s.is_null() || s.is_empty())) 2129114402Sru interpolate_environment_variable(s); 2130114402Sru break; 2131114402Sru } 2132114402Sru case 'w': 2133114402Sru do_width(); 2134114402Sru break; 2135114402Sru case 'x': 2136114402Sru if (!get_delim_number(&x, 'v')) 2137114402Sru break; 2138114402Sru type = TOKEN_NODE; 2139114402Sru nd = new extra_size_node(x); 2140114402Sru return; 2141114402Sru case 'X': 2142114402Sru nd = do_special(); 2143114402Sru if (!nd) 2144114402Sru break; 2145114402Sru type = TOKEN_NODE; 2146114402Sru return; 2147114402Sru case 'Y': 2148114402Sru { 2149114402Sru symbol s = read_escape_name(); 2150114402Sru if (s.is_null() || s.is_empty()) 2151114402Sru break; 2152114402Sru request_or_macro *p = lookup_request(s); 2153114402Sru macro *m = p->to_macro(); 2154114402Sru if (!m) { 2155114402Sru error("can't transparently throughput a request"); 2156114402Sru break; 2157114402Sru } 2158114402Sru nd = new special_node(*m); 2159114402Sru type = TOKEN_NODE; 2160114402Sru return; 2161114402Sru } 2162114402Sru case 'z': 2163114402Sru { 2164114402Sru next(); 2165114402Sru if (type == TOKEN_NODE) 2166114402Sru nd = new zero_width_node(nd); 2167114402Sru else { 2168114402Sru charinfo *ci = get_char(1); 2169114402Sru if (ci == 0) 2170114402Sru break; 2171114402Sru node *gn = curenv->make_char_node(ci); 2172114402Sru if (gn == 0) 2173114402Sru break; 2174114402Sru nd = new zero_width_node(gn); 2175114402Sru type = TOKEN_NODE; 2176114402Sru } 2177114402Sru return; 2178114402Sru } 2179114402Sru case 'Z': 2180114402Sru nd = do_zero_width(); 2181114402Sru if (nd == 0) 2182114402Sru break; 2183114402Sru type = TOKEN_NODE; 2184114402Sru return; 2185114402Sru case '{': 2186114402Sru goto ESCAPE_LEFT_BRACE; 2187114402Sru case '}': 2188114402Sru goto ESCAPE_RIGHT_BRACE; 2189114402Sru case '\n': 2190114402Sru break; 2191114402Sru case '[': 2192114402Sru if (!compatible_flag) { 2193114402Sru symbol s = read_long_escape_name(WITH_ARGS); 2194114402Sru if (s.is_null() || s.is_empty()) 2195114402Sru break; 2196114402Sru if (have_string_arg) { 2197114402Sru have_string_arg = 0; 2198114402Sru nm = composite_glyph_name(s); 2199114402Sru } 2200114402Sru else { 2201114402Sru const char *gn = check_unicode_name(s.contents()); 2202114402Sru if (gn) { 2203114402Sru const char *gn_decomposed = decompose_unicode(gn); 2204114402Sru if (gn_decomposed) 2205114402Sru gn = &gn_decomposed[1]; 2206114402Sru const char *groff_gn = unicode_to_glyph_name(gn); 2207114402Sru if (groff_gn) 2208114402Sru nm = symbol(groff_gn); 2209114402Sru else { 2210114402Sru char *buf = new char[strlen(gn) + 1 + 1]; 2211114402Sru strcpy(buf, "u"); 2212114402Sru strcat(buf, gn); 2213114402Sru nm = symbol(buf); 2214114402Sru a_delete buf; 2215114402Sru } 2216114402Sru } 2217114402Sru else 2218114402Sru nm = symbol(s.contents()); 2219114402Sru } 2220114402Sru type = TOKEN_SPECIAL; 2221114402Sru return; 2222114402Sru } 2223114402Sru goto handle_normal_char; 2224114402Sru default: 2225114402Sru if (cc != escape_char && cc != '.') 2226114402Sru warning(WARN_ESCAPE, "escape character ignored before %1", 2227114402Sru input_char_description(cc)); 2228114402Sru goto handle_normal_char; 2229114402Sru } 2230114402Sru } 2231114402Sru } 2232114402Sru} 2233114402Sru 2234114402Sruint token::operator==(const token &t) 2235114402Sru{ 2236114402Sru if (type != t.type) 2237114402Sru return 0; 2238114402Sru switch(type) { 2239114402Sru case TOKEN_CHAR: 2240114402Sru return c == t.c; 2241114402Sru case TOKEN_SPECIAL: 2242114402Sru return nm == t.nm; 2243114402Sru case TOKEN_NUMBERED_CHAR: 2244114402Sru return val == t.val; 2245114402Sru default: 2246114402Sru return 1; 2247114402Sru } 2248114402Sru} 2249114402Sru 2250114402Sruint token::operator!=(const token &t) 2251114402Sru{ 2252114402Sru return !(*this == t); 2253114402Sru} 2254114402Sru 2255114402Sru// is token a suitable delimiter (like ')? 2256114402Sru 2257114402Sruint token::delimiter(int err) 2258114402Sru{ 2259114402Sru switch(type) { 2260114402Sru case TOKEN_CHAR: 2261114402Sru switch(c) { 2262114402Sru case '0': 2263114402Sru case '1': 2264114402Sru case '2': 2265114402Sru case '3': 2266114402Sru case '4': 2267114402Sru case '5': 2268114402Sru case '6': 2269114402Sru case '7': 2270114402Sru case '8': 2271114402Sru case '9': 2272114402Sru case '+': 2273114402Sru case '-': 2274114402Sru case '/': 2275114402Sru case '*': 2276114402Sru case '%': 2277114402Sru case '<': 2278114402Sru case '>': 2279114402Sru case '=': 2280114402Sru case '&': 2281114402Sru case ':': 2282114402Sru case '(': 2283114402Sru case ')': 2284114402Sru case '.': 2285114402Sru if (err) 2286114402Sru error("cannot use character `%1' as a starting delimiter", char(c)); 2287114402Sru return 0; 2288114402Sru default: 2289114402Sru return 1; 2290114402Sru } 2291114402Sru case TOKEN_NODE: 2292114402Sru case TOKEN_SPACE: 2293114402Sru case TOKEN_STRETCHABLE_SPACE: 2294114402Sru case TOKEN_UNSTRETCHABLE_SPACE: 2295114402Sru case TOKEN_TAB: 2296114402Sru case TOKEN_NEWLINE: 2297114402Sru if (err) 2298114402Sru error("cannot use %1 as a starting delimiter", description()); 2299114402Sru return 0; 2300114402Sru default: 2301114402Sru return 1; 2302114402Sru } 2303114402Sru} 2304114402Sru 2305114402Sruconst char *token::description() 2306114402Sru{ 2307114402Sru static char buf[4]; 2308114402Sru switch (type) { 2309114402Sru case TOKEN_BACKSPACE: 2310114402Sru return "a backspace character"; 2311114402Sru case TOKEN_CHAR: 2312114402Sru buf[0] = '`'; 2313114402Sru buf[1] = c; 2314114402Sru buf[2] = '\''; 2315114402Sru buf[3] = '\0'; 2316114402Sru return buf; 2317114402Sru case TOKEN_DUMMY: 2318114402Sru return "`\\&'"; 2319114402Sru case TOKEN_ESCAPE: 2320114402Sru return "`\\e'"; 2321114402Sru case TOKEN_HYPHEN_INDICATOR: 2322114402Sru return "`\\%'"; 2323114402Sru case TOKEN_INTERRUPT: 2324114402Sru return "`\\c'"; 2325114402Sru case TOKEN_ITALIC_CORRECTION: 2326114402Sru return "`\\/'"; 2327114402Sru case TOKEN_LEADER: 2328114402Sru return "a leader character"; 2329114402Sru case TOKEN_LEFT_BRACE: 2330114402Sru return "`\\{'"; 2331114402Sru case TOKEN_MARK_INPUT: 2332114402Sru return "`\\k'"; 2333114402Sru case TOKEN_NEWLINE: 2334114402Sru return "newline"; 2335114402Sru case TOKEN_NODE: 2336114402Sru return "a node"; 2337114402Sru case TOKEN_NUMBERED_CHAR: 2338114402Sru return "`\\N'"; 2339114402Sru case TOKEN_RIGHT_BRACE: 2340114402Sru return "`\\}'"; 2341114402Sru case TOKEN_SPACE: 2342114402Sru return "a space"; 2343114402Sru case TOKEN_SPECIAL: 2344114402Sru return "a special character"; 2345114402Sru case TOKEN_SPREAD: 2346114402Sru return "`\\p'"; 2347114402Sru case TOKEN_STRETCHABLE_SPACE: 2348114402Sru return "`\\~'"; 2349114402Sru case TOKEN_UNSTRETCHABLE_SPACE: 2350114402Sru return "`\\ '"; 2351114402Sru case TOKEN_TAB: 2352114402Sru return "a tab character"; 2353114402Sru case TOKEN_TRANSPARENT: 2354114402Sru return "`\\!'"; 2355114402Sru case TOKEN_TRANSPARENT_DUMMY: 2356114402Sru return "`\\)'"; 2357114402Sru case TOKEN_ZERO_WIDTH_BREAK: 2358114402Sru return "`\\:'"; 2359114402Sru case TOKEN_EOF: 2360114402Sru return "end of input"; 2361114402Sru default: 2362114402Sru break; 2363114402Sru } 2364114402Sru return "a magic token"; 2365114402Sru} 2366114402Sru 2367114402Sruvoid skip_line() 2368114402Sru{ 2369114402Sru while (!tok.newline()) 2370114402Sru if (tok.eof()) 2371114402Sru return; 2372114402Sru else 2373114402Sru tok.next(); 2374114402Sru tok.next(); 2375114402Sru} 2376114402Sru 2377114402Sruvoid compatible() 2378114402Sru{ 2379114402Sru int n; 2380114402Sru if (has_arg() && get_integer(&n)) 2381114402Sru compatible_flag = n != 0; 2382114402Sru else 2383114402Sru compatible_flag = 1; 2384114402Sru skip_line(); 2385114402Sru} 2386114402Sru 2387114402Srustatic void empty_name_warning(int required) 2388114402Sru{ 2389114402Sru if (tok.newline() || tok.eof()) { 2390114402Sru if (required) 2391114402Sru warning(WARN_MISSING, "missing name"); 2392114402Sru } 2393114402Sru else if (tok.right_brace() || tok.tab()) { 2394114402Sru const char *start = tok.description(); 2395114402Sru do { 2396114402Sru tok.next(); 2397114402Sru } while (tok.space() || tok.right_brace() || tok.tab()); 2398114402Sru if (!tok.newline() && !tok.eof()) 2399114402Sru error("%1 is not allowed before an argument", start); 2400114402Sru else if (required) 2401114402Sru warning(WARN_MISSING, "missing name"); 2402114402Sru } 2403114402Sru else if (required) 2404114402Sru error("name expected (got %1)", tok.description()); 2405114402Sru else 2406114402Sru error("name expected (got %1): treated as missing", tok.description()); 2407114402Sru} 2408114402Sru 2409114402Srustatic void non_empty_name_warning() 2410114402Sru{ 2411114402Sru if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab() 2412114402Sru && !tok.right_brace() 2413114402Sru // We don't want to give a warning for .el\{ 2414114402Sru && !tok.left_brace()) 2415114402Sru error("%1 is not allowed in a name", tok.description()); 2416114402Sru} 2417114402Sru 2418114402Srusymbol get_name(int required) 2419114402Sru{ 2420114402Sru if (compatible_flag) { 2421114402Sru char buf[3]; 2422114402Sru tok.skip(); 2423114402Sru if ((buf[0] = tok.ch()) != 0) { 2424114402Sru tok.next(); 2425114402Sru if ((buf[1] = tok.ch()) != 0) { 2426114402Sru buf[2] = 0; 2427114402Sru tok.make_space(); 2428114402Sru } 2429114402Sru else 2430114402Sru non_empty_name_warning(); 2431114402Sru return symbol(buf); 2432114402Sru } 2433114402Sru else { 2434114402Sru empty_name_warning(required); 2435114402Sru return NULL_SYMBOL; 2436114402Sru } 2437114402Sru } 2438114402Sru else 2439114402Sru return get_long_name(required); 2440114402Sru} 2441114402Sru 2442114402Srusymbol get_long_name(int required) 2443114402Sru{ 2444114402Sru return do_get_long_name(required, 0); 2445114402Sru} 2446114402Sru 2447114402Srustatic symbol do_get_long_name(int required, char end) 2448114402Sru{ 2449114402Sru while (tok.space()) 2450114402Sru tok.next(); 2451114402Sru char abuf[ABUF_SIZE]; 2452114402Sru char *buf = abuf; 2453114402Sru int buf_size = ABUF_SIZE; 2454114402Sru int i = 0; 2455114402Sru for (;;) { 2456114402Sru // If end != 0 we normally have to append a null byte 2457114402Sru if (i + 2 > buf_size) { 2458114402Sru if (buf == abuf) { 2459114402Sru buf = new char[ABUF_SIZE*2]; 2460114402Sru memcpy(buf, abuf, buf_size); 2461114402Sru buf_size = ABUF_SIZE*2; 2462114402Sru } 2463114402Sru else { 2464114402Sru char *old_buf = buf; 2465114402Sru buf = new char[buf_size*2]; 2466114402Sru memcpy(buf, old_buf, buf_size); 2467114402Sru buf_size *= 2; 2468114402Sru a_delete old_buf; 2469114402Sru } 2470114402Sru } 2471114402Sru if ((buf[i] = tok.ch()) == 0 || buf[i] == end) 2472114402Sru break; 2473114402Sru i++; 2474114402Sru tok.next(); 2475114402Sru } 2476114402Sru if (i == 0) { 2477114402Sru empty_name_warning(required); 2478114402Sru return NULL_SYMBOL; 2479114402Sru } 2480114402Sru if (end && buf[i] == end) 2481114402Sru buf[i+1] = '\0'; 2482114402Sru else 2483114402Sru non_empty_name_warning(); 2484114402Sru if (buf == abuf) 2485114402Sru return symbol(buf); 2486114402Sru else { 2487114402Sru symbol s(buf); 2488114402Sru a_delete buf; 2489114402Sru return s; 2490114402Sru } 2491114402Sru} 2492114402Sru 2493114402Sruvoid exit_troff() 2494114402Sru{ 2495114402Sru exit_started = 1; 2496114402Sru topdiv->set_last_page(); 2497114402Sru if (!end_macro_name.is_null()) { 2498114402Sru spring_trap(end_macro_name); 2499114402Sru tok.next(); 2500114402Sru process_input_stack(); 2501114402Sru } 2502114402Sru curenv->final_break(); 2503114402Sru tok.next(); 2504114402Sru process_input_stack(); 2505114402Sru end_diversions(); 2506114402Sru if (topdiv->get_page_length() > 0) { 2507114402Sru done_end_macro = 1; 2508114402Sru topdiv->set_ejecting(); 2509114402Sru static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' }; 2510114402Sru input_stack::push(make_temp_iterator((char *)buf)); 2511114402Sru topdiv->space(topdiv->get_page_length(), 1); 2512114402Sru tok.next(); 2513114402Sru process_input_stack(); 2514114402Sru seen_last_page_ejector = 1; // should be set already 2515114402Sru topdiv->set_ejecting(); 2516114402Sru push_page_ejector(); 2517114402Sru topdiv->space(topdiv->get_page_length(), 1); 2518114402Sru tok.next(); 2519114402Sru process_input_stack(); 2520114402Sru } 2521114402Sru // This will only happen if a trap-invoked macro starts a diversion, 2522114402Sru // or if vertical position traps have been disabled. 2523114402Sru cleanup_and_exit(0); 2524114402Sru} 2525114402Sru 2526114402Sru// This implements .ex. The input stack must be cleared before calling 2527114402Sru// exit_troff(). 2528114402Sru 2529114402Sruvoid exit_request() 2530114402Sru{ 2531114402Sru input_stack::clear(); 2532114402Sru if (exit_started) 2533114402Sru tok.next(); 2534114402Sru else 2535114402Sru exit_troff(); 2536114402Sru} 2537114402Sru 2538114402Sruvoid return_macro_request() 2539114402Sru{ 2540151497Sru if (has_arg() && tok.ch()) 2541151497Sru input_stack::pop_macro(); 2542114402Sru input_stack::pop_macro(); 2543114402Sru tok.next(); 2544114402Sru} 2545114402Sru 2546114402Sruvoid end_macro() 2547114402Sru{ 2548114402Sru end_macro_name = get_name(); 2549114402Sru skip_line(); 2550114402Sru} 2551114402Sru 2552114402Sruvoid blank_line_macro() 2553114402Sru{ 2554114402Sru blank_line_macro_name = get_name(); 2555114402Sru skip_line(); 2556114402Sru} 2557114402Sru 2558114402Srustatic void trapping_blank_line() 2559114402Sru{ 2560114402Sru if (!blank_line_macro_name.is_null()) 2561114402Sru spring_trap(blank_line_macro_name); 2562114402Sru else 2563114402Sru blank_line(); 2564114402Sru} 2565114402Sru 2566114402Sruvoid do_request() 2567114402Sru{ 2568114402Sru int old_compatible_flag = compatible_flag; 2569114402Sru compatible_flag = 0; 2570114402Sru symbol nm = get_name(); 2571114402Sru if (nm.is_null()) 2572114402Sru skip_line(); 2573114402Sru else 2574114402Sru interpolate_macro(nm); 2575114402Sru compatible_flag = old_compatible_flag; 2576114402Sru} 2577114402Sru 2578114402Sruinline int possibly_handle_first_page_transition() 2579114402Sru{ 2580114402Sru if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) { 2581114402Sru handle_first_page_transition(); 2582114402Sru return 1; 2583114402Sru } 2584114402Sru else 2585114402Sru return 0; 2586114402Sru} 2587114402Sru 2588114402Srustatic int transparent_translate(int cc) 2589114402Sru{ 2590114402Sru if (!invalid_input_char(cc)) { 2591114402Sru charinfo *ci = charset_table[cc]; 2592114402Sru switch (ci->get_special_translation(1)) { 2593114402Sru case charinfo::TRANSLATE_SPACE: 2594114402Sru return ' '; 2595114402Sru case charinfo::TRANSLATE_STRETCHABLE_SPACE: 2596114402Sru return ESCAPE_TILDE; 2597114402Sru case charinfo::TRANSLATE_DUMMY: 2598114402Sru return ESCAPE_AMPERSAND; 2599114402Sru case charinfo::TRANSLATE_HYPHEN_INDICATOR: 2600114402Sru return ESCAPE_PERCENT; 2601114402Sru } 2602114402Sru // This is really ugly. 2603114402Sru ci = ci->get_translation(1); 2604114402Sru if (ci) { 2605114402Sru int c = ci->get_ascii_code(); 2606114402Sru if (c != '\0') 2607114402Sru return c; 2608114402Sru error("can't translate %1 to special character `%2'" 2609114402Sru " in transparent throughput", 2610114402Sru input_char_description(cc), 2611114402Sru ci->nm.contents()); 2612114402Sru } 2613114402Sru } 2614114402Sru return cc; 2615114402Sru} 2616114402Sru 2617114402Sruclass int_stack { 2618114402Sru struct int_stack_element { 2619114402Sru int n; 2620114402Sru int_stack_element *next; 2621114402Sru } *top; 2622114402Srupublic: 2623114402Sru int_stack(); 2624114402Sru ~int_stack(); 2625114402Sru void push(int); 2626114402Sru int is_empty(); 2627114402Sru int pop(); 2628114402Sru}; 2629114402Sru 2630114402Sruint_stack::int_stack() 2631114402Sru{ 2632114402Sru top = 0; 2633114402Sru} 2634114402Sru 2635114402Sruint_stack::~int_stack() 2636114402Sru{ 2637114402Sru while (top != 0) { 2638114402Sru int_stack_element *temp = top; 2639114402Sru top = top->next; 2640114402Sru delete temp; 2641114402Sru } 2642114402Sru} 2643114402Sru 2644114402Sruint int_stack::is_empty() 2645114402Sru{ 2646114402Sru return top == 0; 2647114402Sru} 2648114402Sru 2649114402Sruvoid int_stack::push(int n) 2650114402Sru{ 2651114402Sru int_stack_element *p = new int_stack_element; 2652114402Sru p->next = top; 2653114402Sru p->n = n; 2654114402Sru top = p; 2655114402Sru} 2656114402Sru 2657114402Sruint int_stack::pop() 2658114402Sru{ 2659114402Sru assert(top != 0); 2660114402Sru int_stack_element *p = top; 2661114402Sru top = top->next; 2662114402Sru int n = p->n; 2663114402Sru delete p; 2664114402Sru return n; 2665114402Sru} 2666114402Sru 2667114402Sruint node::reread(int *) 2668114402Sru{ 2669114402Sru return 0; 2670114402Sru} 2671114402Sru 2672151497Sruint global_diverted_space = 0; 2673151497Sru 2674114402Sruint diverted_space_node::reread(int *bolp) 2675114402Sru{ 2676151497Sru global_diverted_space = 1; 2677114402Sru if (curenv->get_fill()) 2678114402Sru trapping_blank_line(); 2679114402Sru else 2680114402Sru curdiv->space(n); 2681151497Sru global_diverted_space = 0; 2682114402Sru *bolp = 1; 2683114402Sru return 1; 2684114402Sru} 2685114402Sru 2686114402Sruint diverted_copy_file_node::reread(int *bolp) 2687114402Sru{ 2688114402Sru curdiv->copy_file(filename.contents()); 2689114402Sru *bolp = 1; 2690114402Sru return 1; 2691114402Sru} 2692114402Sru 2693114402Sruint word_space_node::reread(int *) 2694114402Sru{ 2695114402Sru if (unformat) { 2696114402Sru for (width_list *w = orig_width; w; w = w->next) 2697114402Sru curenv->space(w->width, w->sentence_width); 2698114402Sru unformat = 0; 2699114402Sru return 1; 2700114402Sru } 2701114402Sru return 0; 2702114402Sru} 2703114402Sru 2704114402Sruint unbreakable_space_node::reread(int *) 2705114402Sru{ 2706114402Sru return 0; 2707114402Sru} 2708114402Sru 2709114402Sruint hmotion_node::reread(int *) 2710114402Sru{ 2711114402Sru if (unformat && was_tab) { 2712114402Sru curenv->handle_tab(0); 2713114402Sru unformat = 0; 2714114402Sru return 1; 2715114402Sru } 2716114402Sru return 0; 2717114402Sru} 2718114402Sru 2719114402Sruvoid process_input_stack() 2720114402Sru{ 2721114402Sru int_stack trap_bol_stack; 2722114402Sru int bol = 1; 2723114402Sru for (;;) { 2724114402Sru int suppress_next = 0; 2725114402Sru switch (tok.type) { 2726114402Sru case token::TOKEN_CHAR: 2727114402Sru { 2728114402Sru unsigned char ch = tok.c; 2729114402Sru if (bol && !have_input 2730114402Sru && (ch == curenv->control_char 2731114402Sru || ch == curenv->no_break_control_char)) { 2732114402Sru break_flag = ch == curenv->control_char; 2733114402Sru // skip tabs as well as spaces here 2734114402Sru do { 2735114402Sru tok.next(); 2736114402Sru } while (tok.white_space()); 2737114402Sru symbol nm = get_name(); 2738151497Sru#if defined(DEBUGGING) 2739151497Sru if (debug_state) { 2740151497Sru if (! nm.is_null()) { 2741151497Sru if (strcmp(nm.contents(), "test") == 0) { 2742151497Sru fprintf(stderr, "found it!\n"); 2743151497Sru fflush(stderr); 2744151497Sru } 2745151497Sru fprintf(stderr, "interpreting [%s]", nm.contents()); 2746151497Sru if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv) 2747151497Sru fprintf(stderr, " currently in diversion: %s", 2748151497Sru curdiv->get_diversion_name()); 2749151497Sru fprintf(stderr, "\n"); 2750151497Sru fflush(stderr); 2751151497Sru } 2752151497Sru } 2753151497Sru#endif 2754114402Sru if (nm.is_null()) 2755114402Sru skip_line(); 2756151497Sru else { 2757114402Sru interpolate_macro(nm); 2758151497Sru#if defined(DEBUGGING) 2759151497Sru if (debug_state) { 2760151497Sru fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents()); 2761151497Sru curenv->dump_troff_state(); 2762151497Sru } 2763151497Sru#endif 2764151497Sru } 2765114402Sru suppress_next = 1; 2766114402Sru } 2767114402Sru else { 2768114402Sru if (possibly_handle_first_page_transition()) 2769114402Sru ; 2770114402Sru else { 2771114402Sru for (;;) { 2772151497Sru#if defined(DEBUGGING) 2773151497Sru if (debug_state) { 2774151497Sru fprintf(stderr, "found [%c]\n", ch); fflush(stderr); 2775151497Sru } 2776151497Sru#endif 2777114402Sru curenv->add_char(charset_table[ch]); 2778114402Sru tok.next(); 2779114402Sru if (tok.type != token::TOKEN_CHAR) 2780114402Sru break; 2781114402Sru ch = tok.c; 2782114402Sru } 2783114402Sru suppress_next = 1; 2784114402Sru bol = 0; 2785114402Sru } 2786114402Sru } 2787114402Sru break; 2788114402Sru } 2789114402Sru case token::TOKEN_TRANSPARENT: 2790114402Sru { 2791114402Sru if (bol) { 2792114402Sru if (possibly_handle_first_page_transition()) 2793114402Sru ; 2794114402Sru else { 2795114402Sru int cc; 2796114402Sru do { 2797114402Sru node *n; 2798114402Sru cc = get_copy(&n); 2799114402Sru if (cc != EOF) 2800114402Sru if (cc != '\0') 2801114402Sru curdiv->transparent_output(transparent_translate(cc)); 2802114402Sru else 2803114402Sru curdiv->transparent_output(n); 2804114402Sru } while (cc != '\n' && cc != EOF); 2805114402Sru if (cc == EOF) 2806114402Sru curdiv->transparent_output('\n'); 2807114402Sru } 2808114402Sru } 2809114402Sru break; 2810114402Sru } 2811114402Sru case token::TOKEN_NEWLINE: 2812114402Sru { 2813151497Sru if (bol && !old_have_input 2814114402Sru && !curenv->get_prev_line_interrupted()) 2815114402Sru trapping_blank_line(); 2816114402Sru else { 2817114402Sru curenv->newline(); 2818114402Sru bol = 1; 2819114402Sru } 2820114402Sru break; 2821114402Sru } 2822114402Sru case token::TOKEN_REQUEST: 2823114402Sru { 2824114402Sru int request_code = tok.c; 2825114402Sru tok.next(); 2826114402Sru switch (request_code) { 2827114402Sru case TITLE_REQUEST: 2828114402Sru title(); 2829114402Sru break; 2830114402Sru case COPY_FILE_REQUEST: 2831114402Sru copy_file(); 2832114402Sru break; 2833114402Sru case TRANSPARENT_FILE_REQUEST: 2834114402Sru transparent_file(); 2835114402Sru break; 2836114402Sru#ifdef COLUMN 2837114402Sru case VJUSTIFY_REQUEST: 2838114402Sru vjustify(); 2839114402Sru break; 2840114402Sru#endif /* COLUMN */ 2841114402Sru default: 2842114402Sru assert(0); 2843114402Sru break; 2844114402Sru } 2845114402Sru suppress_next = 1; 2846114402Sru break; 2847114402Sru } 2848114402Sru case token::TOKEN_SPACE: 2849114402Sru { 2850114402Sru if (possibly_handle_first_page_transition()) 2851114402Sru ; 2852114402Sru else if (bol && !curenv->get_prev_line_interrupted()) { 2853114402Sru int nspaces = 0; 2854114402Sru // save space_width now so that it isn't changed by \f or \s 2855114402Sru // which we wouldn't notice here 2856114402Sru hunits space_width = curenv->get_space_width(); 2857114402Sru do { 2858114402Sru nspaces += tok.nspaces(); 2859114402Sru tok.next(); 2860114402Sru } while (tok.space()); 2861114402Sru if (tok.newline()) 2862114402Sru trapping_blank_line(); 2863114402Sru else { 2864114402Sru push_token(tok); 2865114402Sru curenv->do_break(); 2866114402Sru curenv->add_node(new hmotion_node(space_width * nspaces, 2867114402Sru curenv->get_fill_color())); 2868114402Sru bol = 0; 2869114402Sru } 2870114402Sru } 2871114402Sru else { 2872114402Sru curenv->space(); 2873114402Sru bol = 0; 2874114402Sru } 2875114402Sru break; 2876114402Sru } 2877114402Sru case token::TOKEN_EOF: 2878114402Sru return; 2879114402Sru case token::TOKEN_NODE: 2880114402Sru { 2881114402Sru if (possibly_handle_first_page_transition()) 2882114402Sru ; 2883114402Sru else if (tok.nd->reread(&bol)) { 2884114402Sru delete tok.nd; 2885114402Sru tok.nd = 0; 2886114402Sru } 2887114402Sru else { 2888114402Sru curenv->add_node(tok.nd); 2889114402Sru tok.nd = 0; 2890114402Sru bol = 0; 2891114402Sru curenv->possibly_break_line(1); 2892114402Sru } 2893114402Sru break; 2894114402Sru } 2895114402Sru case token::TOKEN_PAGE_EJECTOR: 2896114402Sru { 2897114402Sru continue_page_eject(); 2898114402Sru // I think we just want to preserve bol. 2899114402Sru // bol = 1; 2900114402Sru break; 2901114402Sru } 2902114402Sru case token::TOKEN_BEGIN_TRAP: 2903114402Sru { 2904114402Sru trap_bol_stack.push(bol); 2905114402Sru bol = 1; 2906114402Sru have_input = 0; 2907114402Sru break; 2908114402Sru } 2909114402Sru case token::TOKEN_END_TRAP: 2910114402Sru { 2911114402Sru if (trap_bol_stack.is_empty()) 2912114402Sru error("spurious end trap token detected!"); 2913114402Sru else 2914114402Sru bol = trap_bol_stack.pop(); 2915114402Sru have_input = 0; 2916114402Sru 2917114402Sru /* I'm not totally happy about this. But I can't think of any other 2918114402Sru way to do it. Doing an output_pending_lines() whenever a 2919114402Sru TOKEN_END_TRAP is detected doesn't work: for example, 2920114402Sru 2921114402Sru .wh -1i x 2922114402Sru .de x 2923114402Sru 'bp 2924114402Sru .. 2925114402Sru .wh -.5i y 2926114402Sru .de y 2927114402Sru .tl ''-%-'' 2928114402Sru .. 2929114402Sru .br 2930114402Sru .ll .5i 2931114402Sru .sp |\n(.pu-1i-.5v 2932114402Sru a\%very\%very\%long\%word 2933114402Sru 2934114402Sru will print all but the first lines from the word immediately 2935114402Sru after the footer, rather than on the next page. */ 2936114402Sru 2937114402Sru if (trap_bol_stack.is_empty()) 2938114402Sru curenv->output_pending_lines(); 2939114402Sru break; 2940114402Sru } 2941114402Sru default: 2942114402Sru { 2943114402Sru bol = 0; 2944114402Sru tok.process(); 2945114402Sru break; 2946114402Sru } 2947114402Sru } 2948114402Sru if (!suppress_next) 2949114402Sru tok.next(); 2950114402Sru trap_sprung_flag = 0; 2951114402Sru } 2952114402Sru} 2953114402Sru 2954114402Sru#ifdef WIDOW_CONTROL 2955114402Sru 2956114402Sruvoid flush_pending_lines() 2957114402Sru{ 2958114402Sru while (!tok.newline() && !tok.eof()) 2959114402Sru tok.next(); 2960114402Sru curenv->output_pending_lines(); 2961114402Sru tok.next(); 2962114402Sru} 2963114402Sru 2964114402Sru#endif /* WIDOW_CONTROL */ 2965114402Sru 2966114402Srurequest_or_macro::request_or_macro() 2967114402Sru{ 2968114402Sru} 2969114402Sru 2970114402Srumacro *request_or_macro::to_macro() 2971114402Sru{ 2972114402Sru return 0; 2973114402Sru} 2974114402Sru 2975114402Srurequest::request(REQUEST_FUNCP pp) : p(pp) 2976114402Sru{ 2977114402Sru} 2978114402Sru 2979114402Sruvoid request::invoke(symbol) 2980114402Sru{ 2981114402Sru (*p)(); 2982114402Sru} 2983114402Sru 2984114402Srustruct char_block { 2985114402Sru enum { SIZE = 128 }; 2986114402Sru unsigned char s[SIZE]; 2987114402Sru char_block *next; 2988114402Sru char_block(); 2989114402Sru}; 2990114402Sru 2991114402Sruchar_block::char_block() 2992114402Sru: next(0) 2993114402Sru{ 2994114402Sru} 2995114402Sru 2996114402Sruclass char_list { 2997114402Srupublic: 2998114402Sru char_list(); 2999114402Sru ~char_list(); 3000114402Sru void append(unsigned char); 3001114402Sru void set(unsigned char, int); 3002114402Sru unsigned char get(int); 3003114402Sru int length(); 3004114402Sruprivate: 3005114402Sru unsigned char *ptr; 3006114402Sru int len; 3007114402Sru char_block *head; 3008114402Sru char_block *tail; 3009114402Sru friend class macro_header; 3010114402Sru friend class string_iterator; 3011114402Sru}; 3012114402Sru 3013114402Sruchar_list::char_list() 3014114402Sru: ptr(0), len(0), head(0), tail(0) 3015114402Sru{ 3016114402Sru} 3017114402Sru 3018114402Sruchar_list::~char_list() 3019114402Sru{ 3020114402Sru while (head != 0) { 3021114402Sru char_block *tem = head; 3022114402Sru head = head->next; 3023114402Sru delete tem; 3024114402Sru } 3025114402Sru} 3026114402Sru 3027114402Sruint char_list::length() 3028114402Sru{ 3029114402Sru return len; 3030114402Sru} 3031114402Sru 3032114402Sruvoid char_list::append(unsigned char c) 3033114402Sru{ 3034114402Sru if (tail == 0) { 3035114402Sru head = tail = new char_block; 3036114402Sru ptr = tail->s; 3037114402Sru } 3038114402Sru else { 3039114402Sru if (ptr >= tail->s + char_block::SIZE) { 3040114402Sru tail->next = new char_block; 3041114402Sru tail = tail->next; 3042114402Sru ptr = tail->s; 3043114402Sru } 3044114402Sru } 3045114402Sru *ptr++ = c; 3046114402Sru len++; 3047114402Sru} 3048114402Sru 3049114402Sruvoid char_list::set(unsigned char c, int offset) 3050114402Sru{ 3051114402Sru assert(len > offset); 3052114402Sru // optimization for access at the end 3053114402Sru int boundary = len - len % char_block::SIZE; 3054114402Sru if (offset >= boundary) { 3055114402Sru *(tail->s + offset - boundary) = c; 3056114402Sru return; 3057114402Sru } 3058114402Sru char_block *tem = head; 3059114402Sru int l = 0; 3060114402Sru for (;;) { 3061114402Sru l += char_block::SIZE; 3062114402Sru if (l > offset) { 3063114402Sru *(tem->s + offset % char_block::SIZE) = c; 3064114402Sru return; 3065114402Sru } 3066114402Sru tem = tem->next; 3067114402Sru } 3068114402Sru} 3069114402Sru 3070114402Sruunsigned char char_list::get(int offset) 3071114402Sru{ 3072114402Sru assert(len > offset); 3073114402Sru // optimization for access at the end 3074114402Sru int boundary = len - len % char_block::SIZE; 3075114402Sru if (offset >= boundary) 3076114402Sru return *(tail->s + offset - boundary); 3077114402Sru char_block *tem = head; 3078114402Sru int l = 0; 3079114402Sru for (;;) { 3080114402Sru l += char_block::SIZE; 3081114402Sru if (l > offset) 3082114402Sru return *(tem->s + offset % char_block::SIZE); 3083114402Sru tem = tem->next; 3084114402Sru } 3085114402Sru} 3086114402Sru 3087114402Sruclass node_list { 3088114402Sru node *head; 3089114402Sru node *tail; 3090114402Srupublic: 3091114402Sru node_list(); 3092114402Sru ~node_list(); 3093114402Sru void append(node *); 3094114402Sru int length(); 3095114402Sru node *extract(); 3096114402Sru 3097114402Sru friend class macro_header; 3098114402Sru friend class string_iterator; 3099114402Sru}; 3100114402Sru 3101114402Sruvoid node_list::append(node *n) 3102114402Sru{ 3103114402Sru if (head == 0) { 3104114402Sru n->next = 0; 3105114402Sru head = tail = n; 3106114402Sru } 3107114402Sru else { 3108114402Sru n->next = 0; 3109114402Sru tail = tail->next = n; 3110114402Sru } 3111114402Sru} 3112114402Sru 3113114402Sruint node_list::length() 3114114402Sru{ 3115114402Sru int total = 0; 3116114402Sru for (node *n = head; n != 0; n = n->next) 3117114402Sru ++total; 3118114402Sru return total; 3119114402Sru} 3120114402Sru 3121114402Srunode_list::node_list() 3122114402Sru{ 3123114402Sru head = tail = 0; 3124114402Sru} 3125114402Sru 3126114402Srunode *node_list::extract() 3127114402Sru{ 3128114402Sru node *temp = head; 3129114402Sru head = tail = 0; 3130114402Sru return temp; 3131114402Sru} 3132114402Sru 3133114402Srunode_list::~node_list() 3134114402Sru{ 3135114402Sru delete_node_list(head); 3136114402Sru} 3137114402Sru 3138151497Sruclass macro_header { 3139114402Srupublic: 3140114402Sru int count; 3141114402Sru char_list cl; 3142114402Sru node_list nl; 3143114402Sru macro_header() { count = 1; } 3144114402Sru macro_header *copy(int); 3145114402Sru}; 3146114402Sru 3147114402Srumacro::~macro() 3148114402Sru{ 3149114402Sru if (p != 0 && --(p->count) <= 0) 3150114402Sru delete p; 3151114402Sru} 3152114402Sru 3153114402Srumacro::macro() 3154151497Sru: is_a_diversion(0) 3155114402Sru{ 3156114402Sru if (!input_stack::get_location(1, &filename, &lineno)) { 3157114402Sru filename = 0; 3158114402Sru lineno = 0; 3159114402Sru } 3160114402Sru len = 0; 3161114402Sru empty_macro = 1; 3162114402Sru p = 0; 3163114402Sru} 3164114402Sru 3165114402Srumacro::macro(const macro &m) 3166151497Sru: filename(m.filename), lineno(m.lineno), len(m.len), 3167151497Sru empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p) 3168114402Sru{ 3169114402Sru if (p != 0) 3170114402Sru p->count++; 3171114402Sru} 3172114402Sru 3173151497Srumacro::macro(int is_div) 3174151497Sru : is_a_diversion(is_div) 3175151497Sru{ 3176151497Sru if (!input_stack::get_location(1, &filename, &lineno)) { 3177151497Sru filename = 0; 3178151497Sru lineno = 0; 3179151497Sru } 3180151497Sru len = 0; 3181151497Sru empty_macro = 1; 3182151497Sru p = 0; 3183151497Sru} 3184151497Sru 3185151497Sruint macro::is_diversion() 3186151497Sru{ 3187151497Sru return is_a_diversion; 3188151497Sru} 3189151497Sru 3190114402Srumacro ¯o::operator=(const macro &m) 3191114402Sru{ 3192114402Sru // don't assign object 3193114402Sru if (m.p != 0) 3194114402Sru m.p->count++; 3195114402Sru if (p != 0 && --(p->count) <= 0) 3196114402Sru delete p; 3197114402Sru p = m.p; 3198114402Sru filename = m.filename; 3199114402Sru lineno = m.lineno; 3200114402Sru len = m.len; 3201114402Sru empty_macro = m.empty_macro; 3202151497Sru is_a_diversion = m.is_a_diversion; 3203114402Sru return *this; 3204114402Sru} 3205114402Sru 3206114402Sruvoid macro::append(unsigned char c) 3207114402Sru{ 3208114402Sru assert(c != 0); 3209114402Sru if (p == 0) 3210114402Sru p = new macro_header; 3211114402Sru if (p->cl.length() != len) { 3212114402Sru macro_header *tem = p->copy(len); 3213114402Sru if (--(p->count) <= 0) 3214114402Sru delete p; 3215114402Sru p = tem; 3216114402Sru } 3217114402Sru p->cl.append(c); 3218114402Sru ++len; 3219151497Sru if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE) 3220114402Sru empty_macro = 0; 3221114402Sru} 3222114402Sru 3223114402Sruvoid macro::set(unsigned char c, int offset) 3224114402Sru{ 3225114402Sru assert(p != 0); 3226114402Sru assert(c != 0); 3227114402Sru p->cl.set(c, offset); 3228114402Sru} 3229114402Sru 3230114402Sruunsigned char macro::get(int offset) 3231114402Sru{ 3232114402Sru assert(p != 0); 3233114402Sru return p->cl.get(offset); 3234114402Sru} 3235114402Sru 3236114402Sruint macro::length() 3237114402Sru{ 3238114402Sru return len; 3239114402Sru} 3240114402Sru 3241114402Sruvoid macro::append_str(const char *s) 3242114402Sru{ 3243114402Sru int i = 0; 3244114402Sru 3245114402Sru if (s) { 3246114402Sru while (s[i] != (char)0) { 3247114402Sru append(s[i]); 3248114402Sru i++; 3249114402Sru } 3250114402Sru } 3251114402Sru} 3252114402Sru 3253114402Sruvoid macro::append(node *n) 3254114402Sru{ 3255114402Sru assert(n != 0); 3256114402Sru if (p == 0) 3257114402Sru p = new macro_header; 3258114402Sru if (p->cl.length() != len) { 3259114402Sru macro_header *tem = p->copy(len); 3260114402Sru if (--(p->count) <= 0) 3261114402Sru delete p; 3262114402Sru p = tem; 3263114402Sru } 3264114402Sru p->cl.append(0); 3265114402Sru p->nl.append(n); 3266114402Sru ++len; 3267114402Sru empty_macro = 0; 3268114402Sru} 3269114402Sru 3270114402Sruvoid macro::append_unsigned(unsigned int i) 3271114402Sru{ 3272114402Sru unsigned int j = i / 10; 3273114402Sru if (j != 0) 3274114402Sru append_unsigned(j); 3275114402Sru append(((unsigned char)(((int)'0') + i % 10))); 3276114402Sru} 3277114402Sru 3278114402Sruvoid macro::append_int(int i) 3279114402Sru{ 3280114402Sru if (i < 0) { 3281114402Sru append('-'); 3282114402Sru i = -i; 3283114402Sru } 3284114402Sru append_unsigned((unsigned int)i); 3285114402Sru} 3286114402Sru 3287114402Sruvoid macro::print_size() 3288114402Sru{ 3289114402Sru errprint("%1", len); 3290114402Sru} 3291114402Sru 3292114402Sru// make a copy of the first n bytes 3293114402Sru 3294114402Srumacro_header *macro_header::copy(int n) 3295114402Sru{ 3296114402Sru macro_header *p = new macro_header; 3297114402Sru char_block *bp = cl.head; 3298114402Sru unsigned char *ptr = bp->s; 3299114402Sru node *nd = nl.head; 3300114402Sru while (--n >= 0) { 3301114402Sru if (ptr >= bp->s + char_block::SIZE) { 3302114402Sru bp = bp->next; 3303114402Sru ptr = bp->s; 3304114402Sru } 3305151497Sru unsigned char c = *ptr++; 3306114402Sru p->cl.append(c); 3307114402Sru if (c == 0) { 3308114402Sru p->nl.append(nd->copy()); 3309114402Sru nd = nd->next; 3310114402Sru } 3311114402Sru } 3312114402Sru return p; 3313114402Sru} 3314114402Sru 3315114402Sruvoid print_macros() 3316114402Sru{ 3317114402Sru object_dictionary_iterator iter(request_dictionary); 3318114402Sru request_or_macro *rm; 3319114402Sru symbol s; 3320114402Sru while (iter.get(&s, (object **)&rm)) { 3321114402Sru assert(!s.is_null()); 3322114402Sru macro *m = rm->to_macro(); 3323114402Sru if (m) { 3324114402Sru errprint("%1\t", s.contents()); 3325114402Sru m->print_size(); 3326114402Sru errprint("\n"); 3327114402Sru } 3328114402Sru } 3329114402Sru fflush(stderr); 3330114402Sru skip_line(); 3331114402Sru} 3332114402Sru 3333114402Sruclass string_iterator : public input_iterator { 3334114402Sru macro mac; 3335114402Sru const char *how_invoked; 3336114402Sru int newline_flag; 3337114402Sru int lineno; 3338114402Sru char_block *bp; 3339114402Sru int count; // of characters remaining 3340114402Sru node *nd; 3341114402Sru int saved_compatible_flag; 3342114402Sruprotected: 3343114402Sru symbol nm; 3344114402Sru string_iterator(); 3345114402Srupublic: 3346114402Sru string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL); 3347114402Sru int fill(node **); 3348114402Sru int peek(); 3349114402Sru int get_location(int, const char **, int *); 3350114402Sru void backtrace(); 3351114402Sru void save_compatible_flag(int f) { saved_compatible_flag = f; } 3352114402Sru int get_compatible_flag() { return saved_compatible_flag; } 3353151497Sru int is_diversion(); 3354114402Sru}; 3355114402Sru 3356114402Srustring_iterator::string_iterator(const macro &m, const char *p, symbol s) 3357151497Sru: input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0), 3358151497Sru lineno(1), nm(s) 3359114402Sru{ 3360114402Sru count = mac.len; 3361114402Sru if (count != 0) { 3362114402Sru bp = mac.p->cl.head; 3363114402Sru nd = mac.p->nl.head; 3364114402Sru ptr = eptr = bp->s; 3365114402Sru } 3366114402Sru else { 3367114402Sru bp = 0; 3368114402Sru nd = 0; 3369114402Sru ptr = eptr = 0; 3370114402Sru } 3371114402Sru} 3372114402Sru 3373114402Srustring_iterator::string_iterator() 3374114402Sru{ 3375114402Sru bp = 0; 3376114402Sru nd = 0; 3377114402Sru ptr = eptr = 0; 3378114402Sru newline_flag = 0; 3379114402Sru how_invoked = 0; 3380114402Sru lineno = 1; 3381114402Sru count = 0; 3382114402Sru} 3383114402Sru 3384151497Sruint string_iterator::is_diversion() 3385151497Sru{ 3386151497Sru return mac.is_diversion(); 3387151497Sru} 3388151497Sru 3389114402Sruint string_iterator::fill(node **np) 3390114402Sru{ 3391114402Sru if (newline_flag) 3392114402Sru lineno++; 3393114402Sru newline_flag = 0; 3394114402Sru if (count <= 0) 3395114402Sru return EOF; 3396114402Sru const unsigned char *p = eptr; 3397114402Sru if (p >= bp->s + char_block::SIZE) { 3398114402Sru bp = bp->next; 3399114402Sru p = bp->s; 3400114402Sru } 3401114402Sru if (*p == '\0') { 3402151497Sru if (np) { 3403114402Sru *np = nd->copy(); 3404151497Sru if (is_diversion()) 3405151497Sru (*np)->div_nest_level = input_stack::get_div_level(); 3406151497Sru else 3407151497Sru (*np)->div_nest_level = 0; 3408151497Sru } 3409114402Sru nd = nd->next; 3410114402Sru eptr = ptr = p + 1; 3411114402Sru count--; 3412114402Sru return 0; 3413114402Sru } 3414114402Sru const unsigned char *e = bp->s + char_block::SIZE; 3415114402Sru if (e - p > count) 3416114402Sru e = p + count; 3417114402Sru ptr = p; 3418114402Sru while (p < e) { 3419114402Sru unsigned char c = *p; 3420114402Sru if (c == '\n' || c == ESCAPE_NEWLINE) { 3421114402Sru newline_flag = 1; 3422114402Sru p++; 3423114402Sru break; 3424114402Sru } 3425114402Sru if (c == '\0') 3426114402Sru break; 3427114402Sru p++; 3428114402Sru } 3429114402Sru eptr = p; 3430114402Sru count -= p - ptr; 3431114402Sru return *ptr++; 3432114402Sru} 3433114402Sru 3434114402Sruint string_iterator::peek() 3435114402Sru{ 3436114402Sru if (count <= 0) 3437114402Sru return EOF; 3438114402Sru const unsigned char *p = eptr; 3439114402Sru if (p >= bp->s + char_block::SIZE) { 3440114402Sru p = bp->next->s; 3441114402Sru } 3442114402Sru return *p; 3443114402Sru} 3444114402Sru 3445114402Sruint string_iterator::get_location(int allow_macro, 3446114402Sru const char **filep, int *linep) 3447114402Sru{ 3448114402Sru if (!allow_macro) 3449114402Sru return 0; 3450114402Sru if (mac.filename == 0) 3451114402Sru return 0; 3452114402Sru *filep = mac.filename; 3453114402Sru *linep = mac.lineno + lineno - 1; 3454114402Sru return 1; 3455114402Sru} 3456114402Sru 3457114402Sruvoid string_iterator::backtrace() 3458114402Sru{ 3459114402Sru if (mac.filename) { 3460114402Sru errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1); 3461114402Sru if (how_invoked) { 3462114402Sru if (!nm.is_null()) 3463114402Sru errprint(": %1 `%2'\n", how_invoked, nm.contents()); 3464114402Sru else 3465114402Sru errprint(": %1\n", how_invoked); 3466114402Sru } 3467114402Sru else 3468114402Sru errprint("\n"); 3469114402Sru } 3470114402Sru} 3471114402Sru 3472114402Sruclass temp_iterator : public input_iterator { 3473114402Sru unsigned char *base; 3474114402Sru temp_iterator(const char *, int len); 3475114402Srupublic: 3476114402Sru ~temp_iterator(); 3477114402Sru friend input_iterator *make_temp_iterator(const char *); 3478114402Sru}; 3479114402Sru 3480114402Sru#ifdef __GNUG__ 3481114402Sruinline 3482114402Sru#endif 3483114402Srutemp_iterator::temp_iterator(const char *s, int len) 3484114402Sru{ 3485114402Sru base = new unsigned char[len]; 3486114402Sru memcpy(base, s, len); 3487114402Sru ptr = base; 3488114402Sru eptr = base + len; 3489114402Sru} 3490114402Sru 3491114402Srutemp_iterator::~temp_iterator() 3492114402Sru{ 3493114402Sru a_delete base; 3494114402Sru} 3495114402Sru 3496114402Sruclass small_temp_iterator : public input_iterator { 3497114402Sruprivate: 3498114402Sru small_temp_iterator(const char *, int); 3499114402Sru ~small_temp_iterator(); 3500114402Sru enum { BLOCK = 16 }; 3501114402Sru static small_temp_iterator *free_list; 3502114402Sru void *operator new(size_t); 3503114402Sru void operator delete(void *); 3504114402Sru enum { SIZE = 12 }; 3505114402Sru unsigned char buf[SIZE]; 3506114402Sru friend input_iterator *make_temp_iterator(const char *); 3507114402Sru}; 3508114402Sru 3509114402Srusmall_temp_iterator *small_temp_iterator::free_list = 0; 3510114402Sru 3511114402Sruvoid *small_temp_iterator::operator new(size_t n) 3512114402Sru{ 3513114402Sru assert(n == sizeof(small_temp_iterator)); 3514114402Sru if (!free_list) { 3515114402Sru free_list = 3516114402Sru (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK]; 3517114402Sru for (int i = 0; i < BLOCK - 1; i++) 3518114402Sru free_list[i].next = free_list + i + 1; 3519114402Sru free_list[BLOCK-1].next = 0; 3520114402Sru } 3521114402Sru small_temp_iterator *p = free_list; 3522114402Sru free_list = (small_temp_iterator *)(free_list->next); 3523114402Sru p->next = 0; 3524114402Sru return p; 3525114402Sru} 3526114402Sru 3527114402Sru#ifdef __GNUG__ 3528114402Sruinline 3529114402Sru#endif 3530114402Sruvoid small_temp_iterator::operator delete(void *p) 3531114402Sru{ 3532114402Sru if (p) { 3533114402Sru ((small_temp_iterator *)p)->next = free_list; 3534114402Sru free_list = (small_temp_iterator *)p; 3535114402Sru } 3536114402Sru} 3537114402Sru 3538114402Srusmall_temp_iterator::~small_temp_iterator() 3539114402Sru{ 3540114402Sru} 3541114402Sru 3542114402Sru#ifdef __GNUG__ 3543114402Sruinline 3544114402Sru#endif 3545114402Srusmall_temp_iterator::small_temp_iterator(const char *s, int len) 3546114402Sru{ 3547114402Sru for (int i = 0; i < len; i++) 3548114402Sru buf[i] = s[i]; 3549114402Sru ptr = buf; 3550114402Sru eptr = buf + len; 3551114402Sru} 3552114402Sru 3553114402Sruinput_iterator *make_temp_iterator(const char *s) 3554114402Sru{ 3555114402Sru if (s == 0) 3556114402Sru return new small_temp_iterator(s, 0); 3557114402Sru else { 3558114402Sru int n = strlen(s); 3559114402Sru if (n <= small_temp_iterator::SIZE) 3560114402Sru return new small_temp_iterator(s, n); 3561114402Sru else 3562114402Sru return new temp_iterator(s, n); 3563114402Sru } 3564114402Sru} 3565114402Sru 3566114402Sru// this is used when macros with arguments are interpolated 3567114402Sru 3568114402Srustruct arg_list { 3569114402Sru macro mac; 3570114402Sru arg_list *next; 3571114402Sru arg_list(const macro &); 3572114402Sru ~arg_list(); 3573114402Sru}; 3574114402Sru 3575114402Sruarg_list::arg_list(const macro &m) : mac(m), next(0) 3576114402Sru{ 3577114402Sru} 3578114402Sru 3579114402Sruarg_list::~arg_list() 3580114402Sru{ 3581114402Sru} 3582114402Sru 3583114402Sruclass macro_iterator : public string_iterator { 3584114402Sru arg_list *args; 3585114402Sru int argc; 3586114402Srupublic: 3587114402Sru macro_iterator(symbol, macro &, const char *how_invoked = "macro"); 3588114402Sru macro_iterator(); 3589114402Sru ~macro_iterator(); 3590114402Sru int has_args() { return 1; } 3591114402Sru input_iterator *get_arg(int i); 3592114402Sru int nargs() { return argc; } 3593114402Sru void add_arg(const macro &m); 3594114402Sru void shift(int n); 3595114402Sru int is_macro() { return 1; } 3596151497Sru int is_diversion(); 3597114402Sru}; 3598114402Sru 3599114402Sruinput_iterator *macro_iterator::get_arg(int i) 3600114402Sru{ 3601114402Sru if (i == 0) 3602114402Sru return make_temp_iterator(nm.contents()); 3603114402Sru if (i > 0 && i <= argc) { 3604114402Sru arg_list *p = args; 3605114402Sru for (int j = 1; j < i; j++) { 3606114402Sru assert(p != 0); 3607114402Sru p = p->next; 3608114402Sru } 3609114402Sru return new string_iterator(p->mac); 3610114402Sru } 3611114402Sru else 3612114402Sru return 0; 3613114402Sru} 3614114402Sru 3615114402Sruvoid macro_iterator::add_arg(const macro &m) 3616114402Sru{ 3617114402Sru arg_list **p; 3618114402Sru for (p = &args; *p; p = &((*p)->next)) 3619114402Sru ; 3620114402Sru *p = new arg_list(m); 3621114402Sru ++argc; 3622114402Sru} 3623114402Sru 3624114402Sruvoid macro_iterator::shift(int n) 3625114402Sru{ 3626114402Sru while (n > 0 && argc > 0) { 3627114402Sru arg_list *tem = args; 3628114402Sru args = args->next; 3629114402Sru delete tem; 3630114402Sru --argc; 3631114402Sru --n; 3632114402Sru } 3633114402Sru} 3634114402Sru 3635114402Sru// This gets used by eg .if '\?xxx\?''. 3636114402Sru 3637114402Sruint operator==(const macro &m1, const macro &m2) 3638114402Sru{ 3639114402Sru if (m1.len != m2.len) 3640114402Sru return 0; 3641114402Sru string_iterator iter1(m1); 3642114402Sru string_iterator iter2(m2); 3643114402Sru int n = m1.len; 3644114402Sru while (--n >= 0) { 3645114402Sru node *nd1 = 0; 3646114402Sru int c1 = iter1.get(&nd1); 3647114402Sru assert(c1 != EOF); 3648114402Sru node *nd2 = 0; 3649114402Sru int c2 = iter2.get(&nd2); 3650114402Sru assert(c2 != EOF); 3651114402Sru if (c1 != c2) { 3652114402Sru if (c1 == 0) 3653114402Sru delete nd1; 3654114402Sru else if (c2 == 0) 3655114402Sru delete nd2; 3656114402Sru return 0; 3657114402Sru } 3658114402Sru if (c1 == 0) { 3659114402Sru assert(nd1 != 0); 3660114402Sru assert(nd2 != 0); 3661114402Sru int are_same = nd1->type() == nd2->type() && nd1->same(nd2); 3662114402Sru delete nd1; 3663114402Sru delete nd2; 3664114402Sru if (!are_same) 3665114402Sru return 0; 3666114402Sru } 3667114402Sru } 3668114402Sru return 1; 3669114402Sru} 3670114402Sru 3671114402Srustatic void interpolate_macro(symbol nm) 3672114402Sru{ 3673114402Sru request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 3674114402Sru if (p == 0) { 3675114402Sru int warned = 0; 3676114402Sru const char *s = nm.contents(); 3677114402Sru if (strlen(s) > 2) { 3678114402Sru request_or_macro *r; 3679114402Sru char buf[3]; 3680114402Sru buf[0] = s[0]; 3681114402Sru buf[1] = s[1]; 3682114402Sru buf[2] = '\0'; 3683114402Sru r = (request_or_macro *)request_dictionary.lookup(symbol(buf)); 3684114402Sru if (r) { 3685114402Sru macro *m = r->to_macro(); 3686114402Sru if (!m || !m->empty()) 3687114402Sru warned = warning(WARN_SPACE, 3688114402Sru "macro `%1' not defined " 3689114402Sru "(probably missing space after `%2')", 3690114402Sru nm.contents(), buf); 3691114402Sru } 3692114402Sru } 3693114402Sru if (!warned) { 3694114402Sru warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 3695114402Sru p = new macro; 3696114402Sru request_dictionary.define(nm, p); 3697114402Sru } 3698114402Sru } 3699114402Sru if (p) 3700114402Sru p->invoke(nm); 3701114402Sru else { 3702114402Sru skip_line(); 3703114402Sru return; 3704114402Sru } 3705114402Sru} 3706114402Sru 3707114402Srustatic void decode_args(macro_iterator *mi) 3708114402Sru{ 3709114402Sru if (!tok.newline() && !tok.eof()) { 3710114402Sru node *n; 3711114402Sru int c = get_copy(&n); 3712114402Sru for (;;) { 3713114402Sru while (c == ' ') 3714114402Sru c = get_copy(&n); 3715114402Sru if (c == '\n' || c == EOF) 3716114402Sru break; 3717114402Sru macro arg; 3718114402Sru int quote_input_level = 0; 3719114402Sru int done_tab_warning = 0; 3720151497Sru if (c == '"') { 3721114402Sru quote_input_level = input_stack::get_level(); 3722114402Sru c = get_copy(&n); 3723114402Sru } 3724151497Sru arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE); 3725114402Sru while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) { 3726151497Sru if (quote_input_level > 0 && c == '"' 3727114402Sru && (compatible_flag 3728114402Sru || input_stack::get_level() == quote_input_level)) { 3729114402Sru c = get_copy(&n); 3730114402Sru if (c == '"') { 3731114402Sru arg.append(c); 3732114402Sru c = get_copy(&n); 3733114402Sru } 3734114402Sru else 3735114402Sru break; 3736114402Sru } 3737114402Sru else { 3738114402Sru if (c == 0) 3739114402Sru arg.append(n); 3740114402Sru else { 3741114402Sru if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3742114402Sru warning(WARN_TAB, "tab character in unquoted macro argument"); 3743114402Sru done_tab_warning = 1; 3744114402Sru } 3745114402Sru arg.append(c); 3746114402Sru } 3747114402Sru c = get_copy(&n); 3748114402Sru } 3749114402Sru } 3750151497Sru arg.append(POP_GROFFCOMP_MODE); 3751114402Sru mi->add_arg(arg); 3752114402Sru } 3753114402Sru } 3754114402Sru} 3755114402Sru 3756114402Srustatic void decode_string_args(macro_iterator *mi) 3757114402Sru{ 3758114402Sru node *n; 3759114402Sru int c = get_copy(&n); 3760114402Sru for (;;) { 3761114402Sru while (c == ' ') 3762114402Sru c = get_copy(&n); 3763114402Sru if (c == '\n' || c == EOF) { 3764114402Sru error("missing `]'"); 3765114402Sru break; 3766114402Sru } 3767114402Sru if (c == ']') 3768114402Sru break; 3769114402Sru macro arg; 3770114402Sru int quote_input_level = 0; 3771114402Sru int done_tab_warning = 0; 3772151497Sru if (c == '"') { 3773114402Sru quote_input_level = input_stack::get_level(); 3774114402Sru c = get_copy(&n); 3775114402Sru } 3776114402Sru while (c != EOF && c != '\n' 3777114402Sru && !(c == ']' && quote_input_level == 0) 3778114402Sru && !(c == ' ' && quote_input_level == 0)) { 3779151497Sru if (quote_input_level > 0 && c == '"' 3780114402Sru && input_stack::get_level() == quote_input_level) { 3781114402Sru c = get_copy(&n); 3782114402Sru if (c == '"') { 3783114402Sru arg.append(c); 3784114402Sru c = get_copy(&n); 3785114402Sru } 3786114402Sru else 3787114402Sru break; 3788114402Sru } 3789114402Sru else { 3790114402Sru if (c == 0) 3791114402Sru arg.append(n); 3792114402Sru else { 3793114402Sru if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { 3794114402Sru warning(WARN_TAB, "tab character in unquoted string argument"); 3795114402Sru done_tab_warning = 1; 3796114402Sru } 3797114402Sru arg.append(c); 3798114402Sru } 3799114402Sru c = get_copy(&n); 3800114402Sru } 3801114402Sru } 3802114402Sru mi->add_arg(arg); 3803114402Sru } 3804114402Sru} 3805114402Sru 3806114402Sruvoid macro::invoke(symbol nm) 3807114402Sru{ 3808114402Sru macro_iterator *mi = new macro_iterator(nm, *this); 3809114402Sru decode_args(mi); 3810114402Sru input_stack::push(mi); 3811114402Sru tok.next(); 3812114402Sru} 3813114402Sru 3814114402Srumacro *macro::to_macro() 3815114402Sru{ 3816114402Sru return this; 3817114402Sru} 3818114402Sru 3819114402Sruint macro::empty() 3820114402Sru{ 3821114402Sru return empty_macro == 1; 3822114402Sru} 3823114402Sru 3824151497Srumacro_iterator::macro_iterator(symbol s, macro &m, const char *how_called) 3825151497Sru: string_iterator(m, how_called, s), args(0), argc(0) 3826114402Sru{ 3827114402Sru} 3828114402Sru 3829114402Srumacro_iterator::macro_iterator() : args(0), argc(0) 3830114402Sru{ 3831114402Sru} 3832114402Sru 3833114402Srumacro_iterator::~macro_iterator() 3834114402Sru{ 3835114402Sru while (args != 0) { 3836114402Sru arg_list *tem = args; 3837114402Sru args = args->next; 3838114402Sru delete tem; 3839114402Sru } 3840114402Sru} 3841114402Sru 3842114402Srudictionary composite_dictionary(17); 3843114402Sru 3844114402Sruvoid composite_request() 3845114402Sru{ 3846114402Sru symbol from = get_name(1); 3847114402Sru if (!from.is_null()) { 3848114402Sru const char *from_gn = glyph_name_to_unicode(from.contents()); 3849114402Sru if (!from_gn) { 3850114402Sru from_gn = check_unicode_name(from.contents()); 3851114402Sru if (!from_gn) { 3852114402Sru error("invalid composite glyph name `%1'", from.contents()); 3853114402Sru skip_line(); 3854114402Sru return; 3855114402Sru } 3856114402Sru } 3857114402Sru const char *from_decomposed = decompose_unicode(from_gn); 3858114402Sru if (from_decomposed) 3859114402Sru from_gn = &from_decomposed[1]; 3860114402Sru symbol to = get_name(1); 3861114402Sru if (to.is_null()) 3862114402Sru composite_dictionary.remove(symbol(from_gn)); 3863114402Sru else { 3864114402Sru const char *to_gn = glyph_name_to_unicode(to.contents()); 3865114402Sru if (!to_gn) { 3866114402Sru to_gn = check_unicode_name(to.contents()); 3867114402Sru if (!to_gn) { 3868114402Sru error("invalid composite glyph name `%1'", to.contents()); 3869114402Sru skip_line(); 3870114402Sru return; 3871114402Sru } 3872114402Sru } 3873114402Sru const char *to_decomposed = decompose_unicode(to_gn); 3874114402Sru if (to_decomposed) 3875114402Sru to_gn = &to_decomposed[1]; 3876114402Sru if (strcmp(from_gn, to_gn) == 0) 3877114402Sru composite_dictionary.remove(symbol(from_gn)); 3878114402Sru else 3879114402Sru (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn); 3880114402Sru } 3881114402Sru } 3882114402Sru skip_line(); 3883114402Sru} 3884114402Sru 3885114402Srustatic symbol composite_glyph_name(symbol nm) 3886114402Sru{ 3887114402Sru macro_iterator *mi = new macro_iterator(); 3888114402Sru decode_string_args(mi); 3889114402Sru input_stack::push(mi); 3890114402Sru const char *gn = glyph_name_to_unicode(nm.contents()); 3891114402Sru if (!gn) { 3892114402Sru gn = check_unicode_name(nm.contents()); 3893114402Sru if (!gn) { 3894114402Sru error("invalid base glyph `%1' in composite glyph name", nm.contents()); 3895114402Sru return EMPTY_SYMBOL; 3896114402Sru } 3897114402Sru } 3898114402Sru const char *gn_decomposed = decompose_unicode(gn); 3899114402Sru string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn); 3900114402Sru string gl; 3901114402Sru int n = input_stack::nargs(); 3902114402Sru for (int i = 1; i <= n; i++) { 3903114402Sru glyph_name += '_'; 3904114402Sru input_iterator *p = input_stack::get_arg(i); 3905114402Sru gl.clear(); 3906114402Sru int c; 3907114402Sru while ((c = p->get(0)) != EOF) 3908114402Sru gl += c; 3909114402Sru gl += '\0'; 3910114402Sru const char *u = glyph_name_to_unicode(gl.contents()); 3911114402Sru if (!u) { 3912114402Sru u = check_unicode_name(gl.contents()); 3913114402Sru if (!u) { 3914114402Sru error("invalid component `%1' in composite glyph name", 3915114402Sru gl.contents()); 3916114402Sru return EMPTY_SYMBOL; 3917114402Sru } 3918114402Sru } 3919114402Sru const char *decomposed = decompose_unicode(u); 3920114402Sru if (decomposed) 3921114402Sru u = &decomposed[1]; 3922114402Sru void *mapped_composite = composite_dictionary.lookup(symbol(u)); 3923114402Sru if (mapped_composite) 3924114402Sru u = (const char *)mapped_composite; 3925114402Sru glyph_name += u; 3926114402Sru } 3927114402Sru glyph_name += '\0'; 3928114402Sru const char *groff_gn = unicode_to_glyph_name(glyph_name.contents()); 3929114402Sru if (groff_gn) 3930114402Sru return symbol(groff_gn); 3931114402Sru gl.clear(); 3932114402Sru gl += 'u'; 3933114402Sru gl += glyph_name; 3934114402Sru return symbol(gl.contents()); 3935114402Sru} 3936114402Sru 3937114402Sruint trap_sprung_flag = 0; 3938114402Sruint postpone_traps_flag = 0; 3939114402Srusymbol postponed_trap; 3940114402Sru 3941114402Sruvoid spring_trap(symbol nm) 3942114402Sru{ 3943114402Sru assert(!nm.is_null()); 3944114402Sru trap_sprung_flag = 1; 3945114402Sru if (postpone_traps_flag) { 3946114402Sru postponed_trap = nm; 3947114402Sru return; 3948114402Sru } 3949114402Sru static char buf[2] = { BEGIN_TRAP, 0 }; 3950114402Sru static char buf2[2] = { END_TRAP, '\0' }; 3951114402Sru input_stack::push(make_temp_iterator(buf2)); 3952114402Sru request_or_macro *p = lookup_request(nm); 3953114402Sru macro *m = p->to_macro(); 3954114402Sru if (m) 3955114402Sru input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro")); 3956114402Sru else 3957114402Sru error("you can't invoke a request with a trap"); 3958114402Sru input_stack::push(make_temp_iterator(buf)); 3959114402Sru} 3960114402Sru 3961114402Sruvoid postpone_traps() 3962114402Sru{ 3963114402Sru postpone_traps_flag = 1; 3964114402Sru} 3965114402Sru 3966114402Sruint unpostpone_traps() 3967114402Sru{ 3968114402Sru postpone_traps_flag = 0; 3969114402Sru if (!postponed_trap.is_null()) { 3970114402Sru spring_trap(postponed_trap); 3971114402Sru postponed_trap = NULL_SYMBOL; 3972114402Sru return 1; 3973114402Sru } 3974114402Sru else 3975114402Sru return 0; 3976114402Sru} 3977114402Sru 3978114402Sruvoid read_request() 3979114402Sru{ 3980114402Sru macro_iterator *mi = new macro_iterator; 3981114402Sru int reading_from_terminal = isatty(fileno(stdin)); 3982114402Sru int had_prompt = 0; 3983114402Sru if (!tok.newline() && !tok.eof()) { 3984114402Sru int c = get_copy(0); 3985114402Sru while (c == ' ') 3986114402Sru c = get_copy(0); 3987114402Sru while (c != EOF && c != '\n' && c != ' ') { 3988114402Sru if (!invalid_input_char(c)) { 3989114402Sru if (reading_from_terminal) 3990114402Sru fputc(c, stderr); 3991114402Sru had_prompt = 1; 3992114402Sru } 3993114402Sru c = get_copy(0); 3994114402Sru } 3995114402Sru if (c == ' ') { 3996114402Sru tok.make_space(); 3997114402Sru decode_args(mi); 3998114402Sru } 3999114402Sru } 4000114402Sru if (reading_from_terminal) { 4001114402Sru fputc(had_prompt ? ':' : '\a', stderr); 4002114402Sru fflush(stderr); 4003114402Sru } 4004114402Sru input_stack::push(mi); 4005114402Sru macro mac; 4006114402Sru int nl = 0; 4007114402Sru int c; 4008114402Sru while ((c = getchar()) != EOF) { 4009114402Sru if (invalid_input_char(c)) 4010114402Sru warning(WARN_INPUT, "invalid input character code %1", int(c)); 4011114402Sru else { 4012114402Sru if (c == '\n') { 4013114402Sru if (nl) 4014114402Sru break; 4015114402Sru else 4016114402Sru nl = 1; 4017114402Sru } 4018114402Sru else 4019114402Sru nl = 0; 4020114402Sru mac.append(c); 4021114402Sru } 4022114402Sru } 4023114402Sru if (reading_from_terminal) 4024114402Sru clearerr(stdin); 4025114402Sru input_stack::push(new string_iterator(mac)); 4026114402Sru tok.next(); 4027114402Sru} 4028114402Sru 4029114402Sruenum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE }; 4030151497Sruenum calling_mode { CALLING_NORMAL, CALLING_INDIRECT }; 4031151497Sruenum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE }; 4032114402Sru 4033151497Sruvoid do_define_string(define_mode mode, comp_mode comp) 4034114402Sru{ 4035114402Sru symbol nm; 4036151497Sru node *n = 0; // pacify compiler 4037114402Sru int c; 4038114402Sru nm = get_name(1); 4039114402Sru if (nm.is_null()) { 4040114402Sru skip_line(); 4041114402Sru return; 4042114402Sru } 4043114402Sru if (tok.newline()) 4044114402Sru c = '\n'; 4045114402Sru else if (tok.tab()) 4046114402Sru c = '\t'; 4047114402Sru else if (!tok.space()) { 4048114402Sru error("bad string definition"); 4049114402Sru skip_line(); 4050114402Sru return; 4051114402Sru } 4052114402Sru else 4053114402Sru c = get_copy(&n); 4054114402Sru while (c == ' ') 4055114402Sru c = get_copy(&n); 4056114402Sru if (c == '"') 4057114402Sru c = get_copy(&n); 4058114402Sru macro mac; 4059114402Sru request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); 4060114402Sru macro *mm = rm ? rm->to_macro() : 0; 4061114402Sru if (mode == DEFINE_APPEND && mm) 4062114402Sru mac = *mm; 4063151497Sru if (comp == COMP_DISABLE) 4064151497Sru mac.append(PUSH_GROFF_MODE); 4065151497Sru else if (comp == COMP_ENABLE) 4066151497Sru mac.append(PUSH_COMP_MODE); 4067114402Sru while (c != '\n' && c != EOF) { 4068114402Sru if (c == 0) 4069114402Sru mac.append(n); 4070114402Sru else 4071114402Sru mac.append((unsigned char)c); 4072114402Sru c = get_copy(&n); 4073114402Sru } 4074114402Sru if (!mm) { 4075114402Sru mm = new macro; 4076114402Sru request_dictionary.define(nm, mm); 4077114402Sru } 4078151497Sru if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4079151497Sru mac.append(POP_GROFFCOMP_MODE); 4080114402Sru *mm = mac; 4081114402Sru tok.next(); 4082114402Sru} 4083114402Sru 4084114402Sruvoid define_string() 4085114402Sru{ 4086151497Sru do_define_string(DEFINE_NORMAL, 4087151497Sru compatible_flag ? COMP_ENABLE: COMP_IGNORE); 4088114402Sru} 4089114402Sru 4090114402Sruvoid define_nocomp_string() 4091114402Sru{ 4092151497Sru do_define_string(DEFINE_NORMAL, COMP_DISABLE); 4093114402Sru} 4094114402Sru 4095114402Sruvoid append_string() 4096114402Sru{ 4097151497Sru do_define_string(DEFINE_APPEND, 4098151497Sru compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4099114402Sru} 4100114402Sru 4101114402Sruvoid append_nocomp_string() 4102114402Sru{ 4103151497Sru do_define_string(DEFINE_APPEND, COMP_DISABLE); 4104114402Sru} 4105114402Sru 4106114402Sruvoid do_define_character(char_mode mode, const char *font_name) 4107114402Sru{ 4108151497Sru node *n = 0; // pacify compiler 4109114402Sru int c; 4110114402Sru tok.skip(); 4111114402Sru charinfo *ci = tok.get_char(1); 4112114402Sru if (ci == 0) { 4113114402Sru skip_line(); 4114114402Sru return; 4115114402Sru } 4116114402Sru if (font_name) { 4117114402Sru string s(font_name); 4118114402Sru s += ' '; 4119114402Sru s += ci->nm.contents(); 4120114402Sru s += '\0'; 4121114402Sru ci = get_charinfo(symbol(s.contents())); 4122114402Sru } 4123114402Sru tok.next(); 4124114402Sru if (tok.newline()) 4125114402Sru c = '\n'; 4126114402Sru else if (tok.tab()) 4127114402Sru c = '\t'; 4128114402Sru else if (!tok.space()) { 4129114402Sru error("bad character definition"); 4130114402Sru skip_line(); 4131114402Sru return; 4132114402Sru } 4133114402Sru else 4134114402Sru c = get_copy(&n); 4135114402Sru while (c == ' ' || c == '\t') 4136114402Sru c = get_copy(&n); 4137114402Sru if (c == '"') 4138114402Sru c = get_copy(&n); 4139114402Sru macro *m = new macro; 4140114402Sru while (c != '\n' && c != EOF) { 4141114402Sru if (c == 0) 4142114402Sru m->append(n); 4143114402Sru else 4144114402Sru m->append((unsigned char)c); 4145114402Sru c = get_copy(&n); 4146114402Sru } 4147114402Sru m = ci->setx_macro(m, mode); 4148114402Sru if (m) 4149114402Sru delete m; 4150114402Sru tok.next(); 4151114402Sru} 4152114402Sru 4153114402Sruvoid define_character() 4154114402Sru{ 4155114402Sru do_define_character(CHAR_NORMAL); 4156114402Sru} 4157114402Sru 4158114402Sruvoid define_fallback_character() 4159114402Sru{ 4160114402Sru do_define_character(CHAR_FALLBACK); 4161114402Sru} 4162114402Sru 4163114402Sruvoid define_special_character() 4164114402Sru{ 4165114402Sru do_define_character(CHAR_SPECIAL); 4166114402Sru} 4167114402Sru 4168114402Srustatic void remove_character() 4169114402Sru{ 4170114402Sru tok.skip(); 4171114402Sru while (!tok.newline() && !tok.eof()) { 4172114402Sru if (!tok.space() && !tok.tab()) { 4173114402Sru charinfo *ci = tok.get_char(1); 4174114402Sru if (!ci) 4175114402Sru break; 4176114402Sru macro *m = ci->set_macro(0); 4177114402Sru if (m) 4178114402Sru delete m; 4179114402Sru } 4180114402Sru tok.next(); 4181114402Sru } 4182114402Sru skip_line(); 4183114402Sru} 4184114402Sru 4185114402Srustatic void interpolate_string(symbol nm) 4186114402Sru{ 4187114402Sru request_or_macro *p = lookup_request(nm); 4188114402Sru macro *m = p->to_macro(); 4189114402Sru if (!m) 4190114402Sru error("you can only invoke a string or macro using \\*"); 4191114402Sru else { 4192114402Sru string_iterator *si = new string_iterator(*m, "string", nm); 4193114402Sru input_stack::push(si); 4194114402Sru } 4195114402Sru} 4196114402Sru 4197114402Srustatic void interpolate_string_with_args(symbol s) 4198114402Sru{ 4199114402Sru request_or_macro *p = lookup_request(s); 4200114402Sru macro *m = p->to_macro(); 4201114402Sru if (!m) 4202114402Sru error("you can only invoke a string or macro using \\*"); 4203114402Sru else { 4204114402Sru macro_iterator *mi = new macro_iterator(s, *m); 4205114402Sru decode_string_args(mi); 4206114402Sru input_stack::push(mi); 4207114402Sru } 4208114402Sru} 4209114402Sru 4210114402Srustatic void interpolate_arg(symbol nm) 4211114402Sru{ 4212114402Sru const char *s = nm.contents(); 4213114402Sru if (!s || *s == '\0') 4214114402Sru copy_mode_error("missing argument name"); 4215114402Sru else if (s[1] == 0 && csdigit(s[0])) 4216114402Sru input_stack::push(input_stack::get_arg(s[0] - '0')); 4217114402Sru else if (s[0] == '*' && s[1] == '\0') { 4218151497Sru int limit = input_stack::nargs(); 4219151497Sru string args; 4220151497Sru for (int i = 1; i <= limit; i++) { 4221151497Sru input_iterator *p = input_stack::get_arg(i); 4222151497Sru int c; 4223151497Sru while ((c = p->get(0)) != EOF) 4224151497Sru args += c; 4225151497Sru if (i != limit) 4226151497Sru args += ' '; 4227114402Sru } 4228151497Sru if (limit > 0) { 4229151497Sru args += '\0'; 4230151497Sru input_stack::push(make_temp_iterator(args.contents())); 4231151497Sru } 4232114402Sru } 4233114402Sru else if (s[0] == '@' && s[1] == '\0') { 4234151497Sru int limit = input_stack::nargs(); 4235151497Sru string args; 4236151497Sru for (int i = 1; i <= limit; i++) { 4237151497Sru args += '"'; 4238151497Sru args += BEGIN_QUOTE; 4239151497Sru input_iterator *p = input_stack::get_arg(i); 4240151497Sru int c; 4241151497Sru while ((c = p->get(0)) != EOF) 4242151497Sru args += c; 4243151497Sru args += END_QUOTE; 4244151497Sru args += '"'; 4245151497Sru if (i != limit) 4246151497Sru args += ' '; 4247114402Sru } 4248151497Sru if (limit > 0) { 4249151497Sru args += '\0'; 4250151497Sru input_stack::push(make_temp_iterator(args.contents())); 4251151497Sru } 4252114402Sru } 4253114402Sru else { 4254114402Sru const char *p; 4255114402Sru for (p = s; *p && csdigit(*p); p++) 4256114402Sru ; 4257114402Sru if (*p) 4258114402Sru copy_mode_error("bad argument name `%1'", s); 4259114402Sru else 4260114402Sru input_stack::push(input_stack::get_arg(atoi(s))); 4261114402Sru } 4262114402Sru} 4263114402Sru 4264114402Sruvoid handle_first_page_transition() 4265114402Sru{ 4266114402Sru push_token(tok); 4267114402Sru topdiv->begin_page(); 4268114402Sru} 4269114402Sru 4270114402Sru// We push back a token by wrapping it up in a token_node, and 4271114402Sru// wrapping that up in a string_iterator. 4272114402Sru 4273114402Srustatic void push_token(const token &t) 4274114402Sru{ 4275114402Sru macro m; 4276114402Sru m.append(new token_node(t)); 4277114402Sru input_stack::push(new string_iterator(m)); 4278114402Sru} 4279114402Sru 4280114402Sruvoid push_page_ejector() 4281114402Sru{ 4282114402Sru static char buf[2] = { PAGE_EJECTOR, '\0' }; 4283114402Sru input_stack::push(make_temp_iterator(buf)); 4284114402Sru} 4285114402Sru 4286114402Sruvoid handle_initial_request(unsigned char code) 4287114402Sru{ 4288114402Sru char buf[2]; 4289114402Sru buf[0] = code; 4290114402Sru buf[1] = '\0'; 4291114402Sru macro mac; 4292114402Sru mac.append(new token_node(tok)); 4293114402Sru input_stack::push(new string_iterator(mac)); 4294114402Sru input_stack::push(make_temp_iterator(buf)); 4295114402Sru topdiv->begin_page(); 4296114402Sru tok.next(); 4297114402Sru} 4298114402Sru 4299114402Sruvoid handle_initial_title() 4300114402Sru{ 4301114402Sru handle_initial_request(TITLE_REQUEST); 4302114402Sru} 4303114402Sru 4304114402Sru// this should be local to define_macro, but cfront 1.2 doesn't support that 4305114402Srustatic symbol dot_symbol("."); 4306114402Sru 4307151497Sruvoid do_define_macro(define_mode mode, calling_mode calling, comp_mode comp) 4308114402Sru{ 4309114402Sru symbol nm, term; 4310114402Sru if (calling == CALLING_INDIRECT) { 4311114402Sru symbol temp1 = get_name(1); 4312114402Sru if (temp1.is_null()) { 4313114402Sru skip_line(); 4314114402Sru return; 4315114402Sru } 4316114402Sru symbol temp2 = get_name(); 4317114402Sru input_stack::push(make_temp_iterator("\n")); 4318114402Sru if (!temp2.is_null()) { 4319114402Sru interpolate_string(temp2); 4320114402Sru input_stack::push(make_temp_iterator(" ")); 4321114402Sru } 4322114402Sru interpolate_string(temp1); 4323114402Sru input_stack::push(make_temp_iterator(" ")); 4324114402Sru tok.next(); 4325114402Sru } 4326114402Sru if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4327114402Sru nm = get_name(1); 4328114402Sru if (nm.is_null()) { 4329114402Sru skip_line(); 4330114402Sru return; 4331114402Sru } 4332114402Sru } 4333114402Sru term = get_name(); // the request that terminates the definition 4334114402Sru if (term.is_null()) 4335114402Sru term = dot_symbol; 4336114402Sru while (!tok.newline() && !tok.eof()) 4337114402Sru tok.next(); 4338114402Sru const char *start_filename; 4339114402Sru int start_lineno; 4340114402Sru int have_start_location = input_stack::get_location(0, &start_filename, 4341114402Sru &start_lineno); 4342114402Sru node *n; 4343114402Sru // doing this here makes the line numbers come out right 4344114402Sru int c = get_copy(&n, 1); 4345114402Sru macro mac; 4346114402Sru macro *mm = 0; 4347114402Sru if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4348114402Sru request_or_macro *rm = 4349114402Sru (request_or_macro *)request_dictionary.lookup(nm); 4350114402Sru if (rm) 4351114402Sru mm = rm->to_macro(); 4352114402Sru if (mm && mode == DEFINE_APPEND) 4353114402Sru mac = *mm; 4354114402Sru } 4355114402Sru int bol = 1; 4356151497Sru if (comp == COMP_DISABLE) 4357151497Sru mac.append(PUSH_GROFF_MODE); 4358151497Sru else if (comp == COMP_ENABLE) 4359151497Sru mac.append(PUSH_COMP_MODE); 4360114402Sru for (;;) { 4361114402Sru while (c == ESCAPE_NEWLINE) { 4362114402Sru if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) 4363114402Sru mac.append(c); 4364114402Sru c = get_copy(&n, 1); 4365114402Sru } 4366114402Sru if (bol && c == '.') { 4367114402Sru const char *s = term.contents(); 4368114402Sru int d = 0; 4369114402Sru // see if it matches term 4370114402Sru int i = 0; 4371114402Sru if (s[0] != 0) { 4372114402Sru while ((d = get_copy(&n)) == ' ' || d == '\t') 4373114402Sru ; 4374114402Sru if ((unsigned char)s[0] == d) { 4375114402Sru for (i = 1; s[i] != 0; i++) { 4376114402Sru d = get_copy(&n); 4377114402Sru if ((unsigned char)s[i] != d) 4378114402Sru break; 4379114402Sru } 4380114402Sru } 4381114402Sru } 4382114402Sru if (s[i] == 0 4383114402Sru && ((i == 2 && compatible_flag) 4384114402Sru || (d = get_copy(&n)) == ' ' 4385114402Sru || d == '\n')) { // we found it 4386114402Sru if (d == '\n') 4387114402Sru tok.make_newline(); 4388114402Sru else 4389114402Sru tok.make_space(); 4390114402Sru if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4391114402Sru if (!mm) { 4392114402Sru mm = new macro; 4393114402Sru request_dictionary.define(nm, mm); 4394114402Sru } 4395151497Sru if (comp == COMP_DISABLE || comp == COMP_ENABLE) 4396151497Sru mac.append(POP_GROFFCOMP_MODE); 4397114402Sru *mm = mac; 4398114402Sru } 4399114402Sru if (term != dot_symbol) { 4400114402Sru ignoring = 0; 4401114402Sru interpolate_macro(term); 4402114402Sru } 4403114402Sru else 4404114402Sru skip_line(); 4405114402Sru return; 4406114402Sru } 4407114402Sru if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { 4408114402Sru mac.append(c); 4409114402Sru for (int j = 0; j < i; j++) 4410114402Sru mac.append(s[j]); 4411114402Sru } 4412114402Sru c = d; 4413114402Sru } 4414114402Sru if (c == EOF) { 4415114402Sru if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4416114402Sru if (have_start_location) 4417114402Sru error_with_file_and_line(start_filename, start_lineno, 4418114402Sru "end of file while defining macro `%1'", 4419114402Sru nm.contents()); 4420114402Sru else 4421114402Sru error("end of file while defining macro `%1'", nm.contents()); 4422114402Sru } 4423114402Sru else { 4424114402Sru if (have_start_location) 4425114402Sru error_with_file_and_line(start_filename, start_lineno, 4426114402Sru "end of file while ignoring input lines"); 4427114402Sru else 4428114402Sru error("end of file while ignoring input lines"); 4429114402Sru } 4430114402Sru tok.next(); 4431114402Sru return; 4432114402Sru } 4433114402Sru if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { 4434114402Sru if (c == 0) 4435114402Sru mac.append(n); 4436114402Sru else 4437114402Sru mac.append(c); 4438114402Sru } 4439114402Sru bol = (c == '\n'); 4440114402Sru c = get_copy(&n, 1); 4441114402Sru } 4442114402Sru} 4443114402Sru 4444114402Sruvoid define_macro() 4445114402Sru{ 4446151497Sru do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, 4447151497Sru compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4448114402Sru} 4449114402Sru 4450114402Sruvoid define_nocomp_macro() 4451114402Sru{ 4452151497Sru do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE); 4453114402Sru} 4454114402Sru 4455114402Sruvoid define_indirect_macro() 4456114402Sru{ 4457151497Sru do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, 4458151497Sru compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4459114402Sru} 4460114402Sru 4461151497Sruvoid define_indirect_nocomp_macro() 4462151497Sru{ 4463151497Sru do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE); 4464151497Sru} 4465151497Sru 4466114402Sruvoid append_macro() 4467114402Sru{ 4468151497Sru do_define_macro(DEFINE_APPEND, CALLING_NORMAL, 4469151497Sru compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4470114402Sru} 4471114402Sru 4472151497Sruvoid append_nocomp_macro() 4473151497Sru{ 4474151497Sru do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE); 4475151497Sru} 4476151497Sru 4477114402Sruvoid append_indirect_macro() 4478114402Sru{ 4479151497Sru do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, 4480151497Sru compatible_flag ? COMP_ENABLE : COMP_IGNORE); 4481114402Sru} 4482114402Sru 4483151497Sruvoid append_indirect_nocomp_macro() 4484114402Sru{ 4485151497Sru do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE); 4486114402Sru} 4487114402Sru 4488114402Sruvoid ignore() 4489114402Sru{ 4490114402Sru ignoring = 1; 4491151497Sru do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE); 4492114402Sru ignoring = 0; 4493114402Sru} 4494114402Sru 4495114402Sruvoid remove_macro() 4496114402Sru{ 4497114402Sru for (;;) { 4498114402Sru symbol s = get_name(); 4499114402Sru if (s.is_null()) 4500114402Sru break; 4501114402Sru request_dictionary.remove(s); 4502114402Sru } 4503114402Sru skip_line(); 4504114402Sru} 4505114402Sru 4506114402Sruvoid rename_macro() 4507114402Sru{ 4508114402Sru symbol s1 = get_name(1); 4509114402Sru if (!s1.is_null()) { 4510114402Sru symbol s2 = get_name(1); 4511114402Sru if (!s2.is_null()) 4512114402Sru request_dictionary.rename(s1, s2); 4513114402Sru } 4514114402Sru skip_line(); 4515114402Sru} 4516114402Sru 4517114402Sruvoid alias_macro() 4518114402Sru{ 4519114402Sru symbol s1 = get_name(1); 4520114402Sru if (!s1.is_null()) { 4521114402Sru symbol s2 = get_name(1); 4522114402Sru if (!s2.is_null()) { 4523114402Sru if (!request_dictionary.alias(s1, s2)) 4524114402Sru warning(WARN_MAC, "macro `%1' not defined", s2.contents()); 4525114402Sru } 4526114402Sru } 4527114402Sru skip_line(); 4528114402Sru} 4529114402Sru 4530114402Sruvoid chop_macro() 4531114402Sru{ 4532114402Sru symbol s = get_name(1); 4533114402Sru if (!s.is_null()) { 4534114402Sru request_or_macro *p = lookup_request(s); 4535114402Sru macro *m = p->to_macro(); 4536114402Sru if (!m) 4537114402Sru error("cannot chop request"); 4538114402Sru else if (m->empty()) 4539114402Sru error("cannot chop empty macro"); 4540114402Sru else { 4541114402Sru int have_restore = 0; 4542114402Sru // we have to check for additional save/restore pairs which could be 4543114402Sru // there due to empty am1 requests. 4544114402Sru for (;;) { 4545151497Sru if (m->get(m->len - 1) != POP_GROFFCOMP_MODE) 4546114402Sru break; 4547114402Sru have_restore = 1; 4548114402Sru m->len -= 1; 4549151497Sru if (m->get(m->len - 1) != PUSH_GROFF_MODE 4550151497Sru && m->get(m->len - 1) != PUSH_COMP_MODE) 4551114402Sru break; 4552114402Sru have_restore = 0; 4553114402Sru m->len -= 1; 4554114402Sru if (m->len == 0) 4555114402Sru break; 4556114402Sru } 4557114402Sru if (m->len == 0) 4558114402Sru error("cannot chop empty macro"); 4559114402Sru else { 4560114402Sru if (have_restore) 4561151497Sru m->set(POP_GROFFCOMP_MODE, m->len - 1); 4562114402Sru else 4563114402Sru m->len -= 1; 4564114402Sru } 4565114402Sru } 4566114402Sru } 4567114402Sru skip_line(); 4568114402Sru} 4569114402Sru 4570114402Sruvoid substring_request() 4571114402Sru{ 4572114402Sru int start; // 0, 1, ..., n-1 or -1, -2, ... 4573114402Sru symbol s = get_name(1); 4574114402Sru if (!s.is_null() && get_integer(&start)) { 4575114402Sru request_or_macro *p = lookup_request(s); 4576114402Sru macro *m = p->to_macro(); 4577114402Sru if (!m) 4578114402Sru error("cannot apply `substring' on a request"); 4579114402Sru else { 4580114402Sru int end = -1; 4581114402Sru if (!has_arg() || get_integer(&end)) { 4582114402Sru int real_length = 0; // 1, 2, ..., n 4583114402Sru string_iterator iter1(*m); 4584114402Sru for (int l = 0; l < m->len; l++) { 4585114402Sru int c = iter1.get(0); 4586151497Sru if (c == PUSH_GROFF_MODE 4587151497Sru || c == PUSH_COMP_MODE 4588151497Sru || c == POP_GROFFCOMP_MODE) 4589114402Sru continue; 4590114402Sru if (c == EOF) 4591114402Sru break; 4592114402Sru real_length++; 4593114402Sru } 4594114402Sru if (start < 0) 4595114402Sru start += real_length; 4596114402Sru if (end < 0) 4597114402Sru end += real_length; 4598114402Sru if (start > end) { 4599114402Sru int tem = start; 4600114402Sru start = end; 4601114402Sru end = tem; 4602114402Sru } 4603114402Sru if (start >= real_length || end < 0) { 4604114402Sru warning(WARN_RANGE, 4605114402Sru "start and end index of substring out of range"); 4606114402Sru m->len = 0; 4607114402Sru if (m->p) { 4608114402Sru if (--(m->p->count) <= 0) 4609114402Sru delete m->p; 4610114402Sru m->p = 0; 4611114402Sru } 4612114402Sru skip_line(); 4613114402Sru return; 4614114402Sru } 4615114402Sru if (start < 0) { 4616114402Sru warning(WARN_RANGE, 4617114402Sru "start index of substring out of range, set to 0"); 4618114402Sru start = 0; 4619114402Sru } 4620114402Sru if (end >= real_length) { 4621114402Sru warning(WARN_RANGE, 4622114402Sru "end index of substring out of range, set to string length"); 4623114402Sru end = real_length - 1; 4624114402Sru } 4625114402Sru // now extract the substring 4626114402Sru string_iterator iter(*m); 4627114402Sru int i; 4628114402Sru for (i = 0; i < start; i++) { 4629114402Sru int c = iter.get(0); 4630151497Sru while (c == PUSH_GROFF_MODE 4631151497Sru || c == PUSH_COMP_MODE 4632151497Sru || c == POP_GROFFCOMP_MODE) 4633114402Sru c = iter.get(0); 4634114402Sru if (c == EOF) 4635114402Sru break; 4636114402Sru } 4637114402Sru macro mac; 4638114402Sru for (; i <= end; i++) { 4639151497Sru node *nd = 0; // pacify compiler 4640114402Sru int c = iter.get(&nd); 4641151497Sru while (c == PUSH_GROFF_MODE 4642151497Sru || c == PUSH_COMP_MODE 4643151497Sru || c == POP_GROFFCOMP_MODE) 4644114402Sru c = iter.get(0); 4645114402Sru if (c == EOF) 4646114402Sru break; 4647114402Sru if (c == 0) 4648114402Sru mac.append(nd); 4649114402Sru else 4650114402Sru mac.append((unsigned char)c); 4651114402Sru } 4652114402Sru *m = mac; 4653114402Sru } 4654114402Sru } 4655114402Sru } 4656114402Sru skip_line(); 4657114402Sru} 4658114402Sru 4659114402Sruvoid length_request() 4660114402Sru{ 4661114402Sru symbol ret; 4662114402Sru ret = get_name(1); 4663114402Sru if (ret.is_null()) { 4664114402Sru skip_line(); 4665114402Sru return; 4666114402Sru } 4667114402Sru int c; 4668114402Sru node *n; 4669114402Sru if (tok.newline()) 4670114402Sru c = '\n'; 4671114402Sru else if (tok.tab()) 4672114402Sru c = '\t'; 4673114402Sru else if (!tok.space()) { 4674114402Sru error("bad string definition"); 4675114402Sru skip_line(); 4676114402Sru return; 4677114402Sru } 4678114402Sru else 4679114402Sru c = get_copy(&n); 4680114402Sru while (c == ' ') 4681114402Sru c = get_copy(&n); 4682114402Sru if (c == '"') 4683114402Sru c = get_copy(&n); 4684114402Sru int len = 0; 4685114402Sru while (c != '\n' && c != EOF) { 4686114402Sru ++len; 4687114402Sru c = get_copy(&n); 4688114402Sru } 4689114402Sru reg *r = (reg*)number_reg_dictionary.lookup(ret); 4690114402Sru if (r) 4691114402Sru r->set_value(len); 4692114402Sru else 4693114402Sru set_number_reg(ret, len); 4694114402Sru tok.next(); 4695114402Sru} 4696114402Sru 4697114402Sruvoid asciify_macro() 4698114402Sru{ 4699114402Sru symbol s = get_name(1); 4700114402Sru if (!s.is_null()) { 4701114402Sru request_or_macro *p = lookup_request(s); 4702114402Sru macro *m = p->to_macro(); 4703114402Sru if (!m) 4704114402Sru error("cannot asciify request"); 4705114402Sru else { 4706114402Sru macro am; 4707114402Sru string_iterator iter(*m); 4708114402Sru for (;;) { 4709151497Sru node *nd = 0; // pacify compiler 4710114402Sru int c = iter.get(&nd); 4711114402Sru if (c == EOF) 4712114402Sru break; 4713114402Sru if (c != 0) 4714114402Sru am.append(c); 4715114402Sru else 4716114402Sru nd->asciify(&am); 4717114402Sru } 4718114402Sru *m = am; 4719114402Sru } 4720114402Sru } 4721114402Sru skip_line(); 4722114402Sru} 4723114402Sru 4724114402Sruvoid unformat_macro() 4725114402Sru{ 4726114402Sru symbol s = get_name(1); 4727114402Sru if (!s.is_null()) { 4728114402Sru request_or_macro *p = lookup_request(s); 4729114402Sru macro *m = p->to_macro(); 4730114402Sru if (!m) 4731114402Sru error("cannot unformat request"); 4732114402Sru else { 4733114402Sru macro am; 4734114402Sru string_iterator iter(*m); 4735114402Sru for (;;) { 4736151497Sru node *nd = 0; // pacify compiler 4737114402Sru int c = iter.get(&nd); 4738114402Sru if (c == EOF) 4739114402Sru break; 4740114402Sru if (c != 0) 4741114402Sru am.append(c); 4742114402Sru else { 4743114402Sru if (nd->set_unformat_flag()) 4744114402Sru am.append(nd); 4745114402Sru } 4746114402Sru } 4747114402Sru *m = am; 4748114402Sru } 4749114402Sru } 4750114402Sru skip_line(); 4751114402Sru} 4752114402Sru 4753114402Srustatic void interpolate_environment_variable(symbol nm) 4754114402Sru{ 4755114402Sru const char *s = getenv(nm.contents()); 4756114402Sru if (s && *s) 4757114402Sru input_stack::push(make_temp_iterator(s)); 4758114402Sru} 4759114402Sru 4760114402Sruvoid interpolate_number_reg(symbol nm, int inc) 4761114402Sru{ 4762114402Sru reg *r = lookup_number_reg(nm); 4763114402Sru if (inc < 0) 4764114402Sru r->decrement(); 4765114402Sru else if (inc > 0) 4766114402Sru r->increment(); 4767114402Sru input_stack::push(make_temp_iterator(r->get_string())); 4768114402Sru} 4769114402Sru 4770114402Srustatic void interpolate_number_format(symbol nm) 4771114402Sru{ 4772114402Sru reg *r = (reg *)number_reg_dictionary.lookup(nm); 4773114402Sru if (r) 4774114402Sru input_stack::push(make_temp_iterator(r->get_format())); 4775114402Sru} 4776114402Sru 4777151497Srustatic int get_delim_number(units *n, unsigned char si, int prev_value) 4778114402Sru{ 4779114402Sru token start; 4780114402Sru start.next(); 4781114402Sru if (start.delimiter(1)) { 4782114402Sru tok.next(); 4783114402Sru if (get_number(n, si, prev_value)) { 4784114402Sru if (start != tok) 4785114402Sru warning(WARN_DELIM, "closing delimiter does not match"); 4786114402Sru return 1; 4787114402Sru } 4788114402Sru } 4789114402Sru return 0; 4790114402Sru} 4791114402Sru 4792151497Srustatic int get_delim_number(units *n, unsigned char si) 4793114402Sru{ 4794114402Sru token start; 4795114402Sru start.next(); 4796114402Sru if (start.delimiter(1)) { 4797114402Sru tok.next(); 4798114402Sru if (get_number(n, si)) { 4799114402Sru if (start != tok) 4800114402Sru warning(WARN_DELIM, "closing delimiter does not match"); 4801114402Sru return 1; 4802114402Sru } 4803114402Sru } 4804114402Sru return 0; 4805114402Sru} 4806114402Sru 4807151497Srustatic int get_line_arg(units *n, unsigned char si, charinfo **cp) 4808114402Sru{ 4809114402Sru token start; 4810114402Sru start.next(); 4811114402Sru int start_level = input_stack::get_level(); 4812114402Sru if (!start.delimiter(1)) 4813114402Sru return 0; 4814114402Sru tok.next(); 4815114402Sru if (get_number(n, si)) { 4816114402Sru if (tok.dummy() || tok.transparent_dummy()) 4817114402Sru tok.next(); 4818114402Sru if (!(start == tok && input_stack::get_level() == start_level)) { 4819114402Sru *cp = tok.get_char(1); 4820114402Sru tok.next(); 4821114402Sru } 4822114402Sru if (!(start == tok && input_stack::get_level() == start_level)) 4823114402Sru warning(WARN_DELIM, "closing delimiter does not match"); 4824114402Sru return 1; 4825114402Sru } 4826114402Sru return 0; 4827114402Sru} 4828114402Sru 4829114402Srustatic int read_size(int *x) 4830114402Sru{ 4831114402Sru tok.next(); 4832114402Sru int c = tok.ch(); 4833114402Sru int inc = 0; 4834114402Sru if (c == '-') { 4835114402Sru inc = -1; 4836114402Sru tok.next(); 4837114402Sru c = tok.ch(); 4838114402Sru } 4839114402Sru else if (c == '+') { 4840114402Sru inc = 1; 4841114402Sru tok.next(); 4842114402Sru c = tok.ch(); 4843114402Sru } 4844151497Sru int val = 0; // pacify compiler 4845114402Sru int bad = 0; 4846114402Sru if (c == '(') { 4847114402Sru tok.next(); 4848114402Sru c = tok.ch(); 4849114402Sru if (!inc) { 4850114402Sru // allow an increment either before or after the left parenthesis 4851114402Sru if (c == '-') { 4852114402Sru inc = -1; 4853114402Sru tok.next(); 4854114402Sru c = tok.ch(); 4855114402Sru } 4856114402Sru else if (c == '+') { 4857114402Sru inc = 1; 4858114402Sru tok.next(); 4859114402Sru c = tok.ch(); 4860114402Sru } 4861114402Sru } 4862114402Sru if (!csdigit(c)) 4863114402Sru bad = 1; 4864114402Sru else { 4865114402Sru val = c - '0'; 4866114402Sru tok.next(); 4867114402Sru c = tok.ch(); 4868114402Sru if (!csdigit(c)) 4869114402Sru bad = 1; 4870114402Sru else { 4871114402Sru val = val*10 + (c - '0'); 4872114402Sru val *= sizescale; 4873114402Sru } 4874114402Sru } 4875114402Sru } 4876114402Sru else if (csdigit(c)) { 4877114402Sru val = c - '0'; 4878114402Sru if (!inc && c != '0' && c < '4') { 4879114402Sru tok.next(); 4880114402Sru c = tok.ch(); 4881114402Sru if (!csdigit(c)) 4882114402Sru bad = 1; 4883114402Sru else 4884114402Sru val = val*10 + (c - '0'); 4885114402Sru } 4886114402Sru val *= sizescale; 4887114402Sru } 4888114402Sru else if (!tok.delimiter(1)) 4889114402Sru return 0; 4890114402Sru else { 4891114402Sru token start(tok); 4892114402Sru tok.next(); 4893114402Sru if (!(inc 4894114402Sru ? get_number(&val, 'z') 4895114402Sru : get_number(&val, 'z', curenv->get_requested_point_size()))) 4896114402Sru return 0; 4897114402Sru if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) { 4898114402Sru if (start.ch() == '[') 4899114402Sru error("missing `]'"); 4900114402Sru else 4901114402Sru error("missing closing delimiter"); 4902114402Sru return 0; 4903114402Sru } 4904114402Sru } 4905114402Sru if (!bad) { 4906114402Sru switch (inc) { 4907114402Sru case 0: 4908114402Sru if (val == 0) { 4909114402Sru // special case -- \s[0] and \s0 means to revert to previous size 4910114402Sru *x = 0; 4911114402Sru return 1; 4912114402Sru } 4913114402Sru *x = val; 4914114402Sru break; 4915114402Sru case 1: 4916114402Sru *x = curenv->get_requested_point_size() + val; 4917114402Sru break; 4918114402Sru case -1: 4919114402Sru *x = curenv->get_requested_point_size() - val; 4920114402Sru break; 4921114402Sru default: 4922114402Sru assert(0); 4923114402Sru } 4924114402Sru if (*x <= 0) { 4925114402Sru warning(WARN_RANGE, 4926114402Sru "\\s request results in non-positive point size; set to 1"); 4927114402Sru *x = 1; 4928114402Sru } 4929114402Sru return 1; 4930114402Sru } 4931114402Sru else { 4932114402Sru error("bad digit in point size"); 4933114402Sru return 0; 4934114402Sru } 4935114402Sru} 4936114402Sru 4937114402Srustatic symbol get_delim_name() 4938114402Sru{ 4939114402Sru token start; 4940114402Sru start.next(); 4941114402Sru if (start.eof()) { 4942114402Sru error("end of input at start of delimited name"); 4943114402Sru return NULL_SYMBOL; 4944114402Sru } 4945114402Sru if (start.newline()) { 4946114402Sru error("can't delimit name with a newline"); 4947114402Sru return NULL_SYMBOL; 4948114402Sru } 4949114402Sru int start_level = input_stack::get_level(); 4950114402Sru char abuf[ABUF_SIZE]; 4951114402Sru char *buf = abuf; 4952114402Sru int buf_size = ABUF_SIZE; 4953114402Sru int i = 0; 4954114402Sru for (;;) { 4955114402Sru if (i + 1 > buf_size) { 4956114402Sru if (buf == abuf) { 4957114402Sru buf = new char[ABUF_SIZE*2]; 4958114402Sru memcpy(buf, abuf, buf_size); 4959114402Sru buf_size = ABUF_SIZE*2; 4960114402Sru } 4961114402Sru else { 4962114402Sru char *old_buf = buf; 4963114402Sru buf = new char[buf_size*2]; 4964114402Sru memcpy(buf, old_buf, buf_size); 4965114402Sru buf_size *= 2; 4966114402Sru a_delete old_buf; 4967114402Sru } 4968114402Sru } 4969114402Sru tok.next(); 4970114402Sru if (tok == start 4971114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 4972114402Sru break; 4973114402Sru if ((buf[i] = tok.ch()) == 0) { 4974114402Sru error("missing delimiter (got %1)", tok.description()); 4975114402Sru if (buf != abuf) 4976114402Sru a_delete buf; 4977114402Sru return NULL_SYMBOL; 4978114402Sru } 4979114402Sru i++; 4980114402Sru } 4981114402Sru buf[i] = '\0'; 4982114402Sru if (buf == abuf) { 4983114402Sru if (i == 0) { 4984114402Sru error("empty delimited name"); 4985114402Sru return NULL_SYMBOL; 4986114402Sru } 4987114402Sru else 4988114402Sru return symbol(buf); 4989114402Sru } 4990114402Sru else { 4991114402Sru symbol s(buf); 4992114402Sru a_delete buf; 4993114402Sru return s; 4994114402Sru } 4995114402Sru} 4996114402Sru 4997114402Sru// Implement \R 4998114402Sru 4999114402Srustatic void do_register() 5000114402Sru{ 5001114402Sru token start; 5002114402Sru start.next(); 5003114402Sru if (!start.delimiter(1)) 5004114402Sru return; 5005114402Sru tok.next(); 5006114402Sru symbol nm = get_long_name(1); 5007114402Sru if (nm.is_null()) 5008114402Sru return; 5009114402Sru while (tok.space()) 5010114402Sru tok.next(); 5011114402Sru reg *r = (reg *)number_reg_dictionary.lookup(nm); 5012114402Sru int prev_value; 5013114402Sru if (!r || !r->get_value(&prev_value)) 5014114402Sru prev_value = 0; 5015114402Sru int val; 5016114402Sru if (!get_number(&val, 'u', prev_value)) 5017114402Sru return; 5018114402Sru if (start != tok) 5019114402Sru warning(WARN_DELIM, "closing delimiter does not match"); 5020114402Sru if (r) 5021114402Sru r->set_value(val); 5022114402Sru else 5023114402Sru set_number_reg(nm, val); 5024114402Sru} 5025114402Sru 5026114402Sru// this implements the \w escape sequence 5027114402Sru 5028114402Srustatic void do_width() 5029114402Sru{ 5030114402Sru token start; 5031114402Sru start.next(); 5032114402Sru int start_level = input_stack::get_level(); 5033114402Sru environment env(curenv); 5034114402Sru environment *oldenv = curenv; 5035114402Sru curenv = &env; 5036114402Sru for (;;) { 5037114402Sru tok.next(); 5038114402Sru if (tok.eof()) { 5039114402Sru warning(WARN_DELIM, "missing closing delimiter"); 5040114402Sru break; 5041114402Sru } 5042114402Sru if (tok.newline()) { 5043114402Sru warning(WARN_DELIM, "missing closing delimiter"); 5044114402Sru input_stack::push(make_temp_iterator("\n")); 5045114402Sru break; 5046114402Sru } 5047114402Sru if (tok == start 5048114402Sru && (compatible_flag || input_stack::get_level() == start_level)) 5049114402Sru break; 5050114402Sru tok.process(); 5051114402Sru } 5052114402Sru env.wrap_up_tab(); 5053114402Sru units x = env.get_input_line_position().to_units(); 5054114402Sru input_stack::push(make_temp_iterator(i_to_a(x))); 5055114402Sru env.width_registers(); 5056114402Sru curenv = oldenv; 5057114402Sru have_input = 0; 5058114402Sru} 5059114402Sru 5060114402Srucharinfo *page_character; 5061114402Sru 5062114402Sruvoid set_page_character() 5063114402Sru{ 5064114402Sru page_character = get_optional_char(); 5065114402Sru skip_line(); 5066114402Sru} 5067114402Sru 5068114402Srustatic const symbol percent_symbol("%"); 5069114402Sru 5070114402Sruvoid read_title_parts(node **part, hunits *part_width) 5071114402Sru{ 5072114402Sru tok.skip(); 5073114402Sru if (tok.newline() || tok.eof()) 5074114402Sru return; 5075114402Sru token start(tok); 5076114402Sru int start_level = input_stack::get_level(); 5077114402Sru tok.next(); 5078114402Sru for (int i = 0; i < 3; i++) { 5079114402Sru while (!tok.newline() && !tok.eof()) { 5080114402Sru if (tok == start 5081114402Sru && (compatible_flag || input_stack::get_level() == start_level)) { 5082114402Sru tok.next(); 5083114402Sru break; 5084114402Sru } 5085114402Sru if (page_character != 0 && tok.get_char() == page_character) 5086114402Sru interpolate_number_reg(percent_symbol, 0); 5087114402Sru else 5088114402Sru tok.process(); 5089114402Sru tok.next(); 5090114402Sru } 5091114402Sru curenv->wrap_up_tab(); 5092114402Sru part_width[i] = curenv->get_input_line_position(); 5093114402Sru part[i] = curenv->extract_output_line(); 5094114402Sru } 5095114402Sru while (!tok.newline() && !tok.eof()) 5096114402Sru tok.next(); 5097114402Sru} 5098114402Sru 5099114402Sruclass non_interpreted_node : public node { 5100114402Sru macro mac; 5101114402Srupublic: 5102114402Sru non_interpreted_node(const macro &); 5103114402Sru int interpret(macro *); 5104114402Sru node *copy(); 5105151497Sru int ends_sentence(); 5106114402Sru int same(node *); 5107114402Sru const char *type(); 5108114402Sru int force_tprint(); 5109151497Sru int is_tag(); 5110114402Sru}; 5111114402Sru 5112114402Srunon_interpreted_node::non_interpreted_node(const macro &m) : mac(m) 5113114402Sru{ 5114114402Sru} 5115114402Sru 5116151497Sruint non_interpreted_node::ends_sentence() 5117151497Sru{ 5118151497Sru return 2; 5119151497Sru} 5120151497Sru 5121114402Sruint non_interpreted_node::same(node *nd) 5122114402Sru{ 5123114402Sru return mac == ((non_interpreted_node *)nd)->mac; 5124114402Sru} 5125114402Sru 5126114402Sruconst char *non_interpreted_node::type() 5127114402Sru{ 5128114402Sru return "non_interpreted_node"; 5129114402Sru} 5130114402Sru 5131114402Sruint non_interpreted_node::force_tprint() 5132114402Sru{ 5133114402Sru return 0; 5134114402Sru} 5135114402Sru 5136151497Sruint non_interpreted_node::is_tag() 5137151497Sru{ 5138151497Sru return 0; 5139151497Sru} 5140151497Sru 5141114402Srunode *non_interpreted_node::copy() 5142114402Sru{ 5143114402Sru return new non_interpreted_node(mac); 5144114402Sru} 5145114402Sru 5146114402Sruint non_interpreted_node::interpret(macro *m) 5147114402Sru{ 5148114402Sru string_iterator si(mac); 5149151497Sru node *n = 0; // pacify compiler 5150114402Sru for (;;) { 5151114402Sru int c = si.get(&n); 5152114402Sru if (c == EOF) 5153114402Sru break; 5154114402Sru if (c == 0) 5155114402Sru m->append(n); 5156114402Sru else 5157114402Sru m->append(c); 5158114402Sru } 5159114402Sru return 1; 5160114402Sru} 5161114402Sru 5162114402Srustatic node *do_non_interpreted() 5163114402Sru{ 5164114402Sru node *n; 5165114402Sru int c; 5166114402Sru macro mac; 5167114402Sru while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n') 5168114402Sru if (c == 0) 5169114402Sru mac.append(n); 5170114402Sru else 5171114402Sru mac.append(c); 5172114402Sru if (c == EOF || c == '\n') { 5173114402Sru error("missing \\?"); 5174114402Sru return 0; 5175114402Sru } 5176114402Sru return new non_interpreted_node(mac); 5177114402Sru} 5178114402Sru 5179114402Srustatic void encode_char(macro *mac, char c) 5180114402Sru{ 5181114402Sru if (c == '\0') { 5182114402Sru if ((font::use_charnames_in_special) && tok.special()) { 5183114402Sru charinfo *ci = tok.get_char(1); 5184114402Sru const char *s = ci->get_symbol()->contents(); 5185114402Sru if (s[0] != (char)0) { 5186114402Sru mac->append('\\'); 5187114402Sru mac->append('('); 5188114402Sru int i = 0; 5189114402Sru while (s[i] != (char)0) { 5190114402Sru mac->append(s[i]); 5191114402Sru i++; 5192114402Sru } 5193114402Sru mac->append('\\'); 5194114402Sru mac->append(')'); 5195114402Sru } 5196114402Sru } 5197114402Sru else if (tok.stretchable_space() 5198114402Sru || tok.unstretchable_space()) 5199114402Sru mac->append(' '); 5200114402Sru else if (!(tok.hyphen_indicator() 5201114402Sru || tok.dummy() 5202114402Sru || tok.transparent_dummy() 5203114402Sru || tok.zero_width_break())) 5204114402Sru error("%1 is invalid within \\X", tok.description()); 5205114402Sru } 5206114402Sru else { 5207114402Sru if ((font::use_charnames_in_special) && (c == '\\')) { 5208114402Sru /* 5209114402Sru * add escape escape sequence 5210114402Sru */ 5211114402Sru mac->append(c); 5212114402Sru } 5213114402Sru mac->append(c); 5214114402Sru } 5215114402Sru} 5216114402Sru 5217114402Srunode *do_special() 5218114402Sru{ 5219114402Sru token start; 5220114402Sru start.next(); 5221114402Sru int start_level = input_stack::get_level(); 5222114402Sru macro mac; 5223114402Sru for (tok.next(); 5224114402Sru tok != start || input_stack::get_level() != start_level; 5225114402Sru tok.next()) { 5226114402Sru if (tok.eof()) { 5227114402Sru warning(WARN_DELIM, "missing closing delimiter"); 5228114402Sru return 0; 5229114402Sru } 5230114402Sru if (tok.newline()) { 5231114402Sru input_stack::push(make_temp_iterator("\n")); 5232114402Sru warning(WARN_DELIM, "missing closing delimiter"); 5233114402Sru break; 5234114402Sru } 5235114402Sru unsigned char c; 5236114402Sru if (tok.space()) 5237114402Sru c = ' '; 5238114402Sru else if (tok.tab()) 5239114402Sru c = '\t'; 5240114402Sru else if (tok.leader()) 5241114402Sru c = '\001'; 5242114402Sru else if (tok.backspace()) 5243114402Sru c = '\b'; 5244114402Sru else 5245114402Sru c = tok.ch(); 5246114402Sru encode_char(&mac, c); 5247114402Sru } 5248114402Sru return new special_node(mac); 5249114402Sru} 5250114402Sru 5251114402Sruvoid output_request() 5252114402Sru{ 5253114402Sru if (!tok.newline() && !tok.eof()) { 5254114402Sru int c; 5255114402Sru for (;;) { 5256114402Sru c = get_copy(0); 5257114402Sru if (c == '"') { 5258114402Sru c = get_copy(0); 5259114402Sru break; 5260114402Sru } 5261114402Sru if (c != ' ' && c != '\t') 5262114402Sru break; 5263114402Sru } 5264114402Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 5265114402Sru topdiv->transparent_output(c); 5266114402Sru topdiv->transparent_output('\n'); 5267114402Sru } 5268114402Sru tok.next(); 5269114402Sru} 5270114402Sru 5271114402Sruextern int image_no; // from node.cpp 5272114402Sru 5273114402Srustatic node *do_suppress(symbol nm) 5274114402Sru{ 5275114402Sru if (nm.is_null() || nm.is_empty()) { 5276114402Sru error("expecting an argument to escape \\O"); 5277114402Sru return 0; 5278114402Sru } 5279114402Sru const char *s = nm.contents(); 5280114402Sru switch (*s) { 5281114402Sru case '0': 5282114402Sru if (begin_level == 0) 5283114402Sru // suppress generation of glyphs 5284114402Sru return new suppress_node(0, 0); 5285114402Sru break; 5286114402Sru case '1': 5287114402Sru if (begin_level == 0) 5288114402Sru // enable generation of glyphs 5289114402Sru return new suppress_node(1, 0); 5290114402Sru break; 5291114402Sru case '2': 5292114402Sru if (begin_level == 0) 5293114402Sru return new suppress_node(1, 1); 5294114402Sru break; 5295114402Sru case '3': 5296114402Sru begin_level++; 5297114402Sru break; 5298114402Sru case '4': 5299114402Sru begin_level--; 5300114402Sru break; 5301114402Sru case '5': 5302114402Sru { 5303114402Sru s++; // move over '5' 5304114402Sru char position = *s; 5305114402Sru if (*s == (char)0) { 5306114402Sru error("missing position and filename in \\O"); 5307114402Sru return 0; 5308114402Sru } 5309114402Sru if (!(position == 'l' 5310114402Sru || position == 'r' 5311114402Sru || position == 'c' 5312114402Sru || position == 'i')) { 5313114402Sru error("l, r, c, or i position expected (got %1 in \\O)", position); 5314114402Sru return 0; 5315114402Sru } 5316114402Sru s++; // onto image name 5317114402Sru if (s == (char *)0) { 5318114402Sru error("missing image name for \\O"); 5319114402Sru return 0; 5320114402Sru } 5321114402Sru image_no++; 5322114402Sru if (begin_level == 0) 5323114402Sru return new suppress_node(symbol(s), position, image_no); 5324114402Sru } 5325114402Sru break; 5326114402Sru default: 5327114402Sru error("`%1' is an invalid argument to \\O", *s); 5328114402Sru } 5329114402Sru return 0; 5330114402Sru} 5331114402Sru 5332114402Sruvoid special_node::tprint(troff_output_file *out) 5333114402Sru{ 5334114402Sru tprint_start(out); 5335114402Sru string_iterator iter(mac); 5336114402Sru for (;;) { 5337114402Sru int c = iter.get(0); 5338114402Sru if (c == EOF) 5339114402Sru break; 5340114402Sru for (const char *s = ::asciify(c); *s; s++) 5341114402Sru tprint_char(out, *s); 5342114402Sru } 5343114402Sru tprint_end(out); 5344114402Sru} 5345114402Sru 5346114402Sruint get_file_line(const char **filename, int *lineno) 5347114402Sru{ 5348114402Sru return input_stack::get_location(0, filename, lineno); 5349114402Sru} 5350114402Sru 5351114402Sruvoid line_file() 5352114402Sru{ 5353114402Sru int n; 5354114402Sru if (get_integer(&n)) { 5355114402Sru const char *filename = 0; 5356114402Sru if (has_arg()) { 5357114402Sru symbol s = get_long_name(); 5358114402Sru filename = s.contents(); 5359114402Sru } 5360114402Sru (void)input_stack::set_location(filename, n-1); 5361114402Sru } 5362114402Sru skip_line(); 5363114402Sru} 5364114402Sru 5365114402Srustatic int nroff_mode = 0; 5366114402Sru 5367114402Srustatic void nroff_request() 5368114402Sru{ 5369114402Sru nroff_mode = 1; 5370114402Sru skip_line(); 5371114402Sru} 5372114402Sru 5373114402Srustatic void troff_request() 5374114402Sru{ 5375114402Sru nroff_mode = 0; 5376114402Sru skip_line(); 5377114402Sru} 5378114402Sru 5379114402Srustatic void skip_alternative() 5380114402Sru{ 5381114402Sru int level = 0; 5382114402Sru // ensure that ``.if 0\{'' works as expected 5383114402Sru if (tok.left_brace()) 5384114402Sru level++; 5385114402Sru int c; 5386114402Sru for (;;) { 5387114402Sru c = input_stack::get(0); 5388114402Sru if (c == EOF) 5389114402Sru break; 5390114402Sru if (c == ESCAPE_LEFT_BRACE) 5391114402Sru ++level; 5392114402Sru else if (c == ESCAPE_RIGHT_BRACE) 5393114402Sru --level; 5394114402Sru else if (c == escape_char && escape_char > 0) 5395114402Sru switch(input_stack::get(0)) { 5396114402Sru case '{': 5397114402Sru ++level; 5398114402Sru break; 5399114402Sru case '}': 5400114402Sru --level; 5401114402Sru break; 5402114402Sru case '"': 5403114402Sru while ((c = input_stack::get(0)) != '\n' && c != EOF) 5404114402Sru ; 5405114402Sru } 5406114402Sru /* 5407114402Sru Note that the level can properly be < 0, eg 5408114402Sru 5409114402Sru .if 1 \{\ 5410114402Sru .if 0 \{\ 5411114402Sru .\}\} 5412114402Sru 5413114402Sru So don't give an error message in this case. 5414114402Sru */ 5415114402Sru if (level <= 0 && c == '\n') 5416114402Sru break; 5417114402Sru } 5418114402Sru tok.next(); 5419114402Sru} 5420114402Sru 5421114402Srustatic void begin_alternative() 5422114402Sru{ 5423114402Sru while (tok.space() || tok.left_brace()) 5424114402Sru tok.next(); 5425114402Sru} 5426114402Sru 5427114402Sruvoid nop_request() 5428114402Sru{ 5429114402Sru while (tok.space()) 5430114402Sru tok.next(); 5431114402Sru} 5432114402Sru 5433114402Srustatic int_stack if_else_stack; 5434114402Sru 5435114402Sruint do_if_request() 5436114402Sru{ 5437114402Sru int invert = 0; 5438114402Sru while (tok.space()) 5439114402Sru tok.next(); 5440114402Sru while (tok.ch() == '!') { 5441114402Sru tok.next(); 5442114402Sru invert = !invert; 5443114402Sru } 5444114402Sru int result; 5445114402Sru unsigned char c = tok.ch(); 5446114402Sru if (c == 't') { 5447114402Sru tok.next(); 5448114402Sru result = !nroff_mode; 5449114402Sru } 5450114402Sru else if (c == 'n') { 5451114402Sru tok.next(); 5452114402Sru result = nroff_mode; 5453114402Sru } 5454114402Sru else if (c == 'v') { 5455114402Sru tok.next(); 5456114402Sru result = 0; 5457114402Sru } 5458114402Sru else if (c == 'o') { 5459114402Sru result = (topdiv->get_page_number() & 1); 5460114402Sru tok.next(); 5461114402Sru } 5462114402Sru else if (c == 'e') { 5463114402Sru result = !(topdiv->get_page_number() & 1); 5464114402Sru tok.next(); 5465114402Sru } 5466114402Sru else if (c == 'd' || c == 'r') { 5467114402Sru tok.next(); 5468114402Sru symbol nm = get_name(1); 5469114402Sru if (nm.is_null()) { 5470114402Sru skip_alternative(); 5471114402Sru return 0; 5472114402Sru } 5473114402Sru result = (c == 'd' 5474114402Sru ? request_dictionary.lookup(nm) != 0 5475114402Sru : number_reg_dictionary.lookup(nm) != 0); 5476114402Sru } 5477114402Sru else if (c == 'm') { 5478114402Sru tok.next(); 5479114402Sru symbol nm = get_long_name(1); 5480114402Sru if (nm.is_null()) { 5481114402Sru skip_alternative(); 5482114402Sru return 0; 5483114402Sru } 5484114402Sru result = (nm == default_symbol 5485114402Sru || color_dictionary.lookup(nm) != 0); 5486114402Sru } 5487114402Sru else if (c == 'c') { 5488114402Sru tok.next(); 5489114402Sru tok.skip(); 5490114402Sru charinfo *ci = tok.get_char(1); 5491114402Sru if (ci == 0) { 5492114402Sru skip_alternative(); 5493114402Sru return 0; 5494114402Sru } 5495114402Sru result = character_exists(ci, curenv); 5496114402Sru tok.next(); 5497114402Sru } 5498151497Sru else if (c == 'F') { 5499151497Sru tok.next(); 5500151497Sru symbol nm = get_long_name(1); 5501151497Sru if (nm.is_null()) { 5502151497Sru skip_alternative(); 5503151497Sru return 0; 5504151497Sru } 5505151497Sru result = check_font(curenv->get_family()->nm, nm); 5506151497Sru } 5507151497Sru else if (c == 'S') { 5508151497Sru tok.next(); 5509151497Sru symbol nm = get_long_name(1); 5510151497Sru if (nm.is_null()) { 5511151497Sru skip_alternative(); 5512151497Sru return 0; 5513151497Sru } 5514151497Sru result = check_style(nm); 5515151497Sru } 5516114402Sru else if (tok.space()) 5517114402Sru result = 0; 5518114402Sru else if (tok.delimiter()) { 5519114402Sru token delim = tok; 5520114402Sru int delim_level = input_stack::get_level(); 5521114402Sru environment env1(curenv); 5522114402Sru environment env2(curenv); 5523114402Sru environment *oldenv = curenv; 5524114402Sru curenv = &env1; 5525151497Sru suppress_push = 1; 5526114402Sru for (int i = 0; i < 2; i++) { 5527114402Sru for (;;) { 5528114402Sru tok.next(); 5529114402Sru if (tok.newline() || tok.eof()) { 5530114402Sru warning(WARN_DELIM, "missing closing delimiter"); 5531114402Sru tok.next(); 5532114402Sru curenv = oldenv; 5533114402Sru return 0; 5534114402Sru } 5535114402Sru if (tok == delim 5536114402Sru && (compatible_flag || input_stack::get_level() == delim_level)) 5537114402Sru break; 5538114402Sru tok.process(); 5539114402Sru } 5540114402Sru curenv = &env2; 5541114402Sru } 5542114402Sru node *n1 = env1.extract_output_line(); 5543114402Sru node *n2 = env2.extract_output_line(); 5544114402Sru result = same_node_list(n1, n2); 5545114402Sru delete_node_list(n1); 5546114402Sru delete_node_list(n2); 5547114402Sru curenv = oldenv; 5548114402Sru have_input = 0; 5549151497Sru suppress_push = 0; 5550114402Sru tok.next(); 5551114402Sru } 5552114402Sru else { 5553114402Sru units n; 5554114402Sru if (!get_number(&n, 'u')) { 5555114402Sru skip_alternative(); 5556114402Sru return 0; 5557114402Sru } 5558114402Sru else 5559114402Sru result = n > 0; 5560114402Sru } 5561114402Sru if (invert) 5562114402Sru result = !result; 5563114402Sru if (result) 5564114402Sru begin_alternative(); 5565114402Sru else 5566114402Sru skip_alternative(); 5567114402Sru return result; 5568114402Sru} 5569114402Sru 5570114402Sruvoid if_else_request() 5571114402Sru{ 5572114402Sru if_else_stack.push(do_if_request()); 5573114402Sru} 5574114402Sru 5575114402Sruvoid if_request() 5576114402Sru{ 5577114402Sru do_if_request(); 5578114402Sru} 5579114402Sru 5580114402Sruvoid else_request() 5581114402Sru{ 5582114402Sru if (if_else_stack.is_empty()) { 5583114402Sru warning(WARN_EL, "unbalanced .el request"); 5584114402Sru skip_alternative(); 5585114402Sru } 5586114402Sru else { 5587114402Sru if (if_else_stack.pop()) 5588114402Sru skip_alternative(); 5589114402Sru else 5590114402Sru begin_alternative(); 5591114402Sru } 5592114402Sru} 5593114402Sru 5594114402Srustatic int while_depth = 0; 5595114402Srustatic int while_break_flag = 0; 5596114402Sru 5597114402Sruvoid while_request() 5598114402Sru{ 5599114402Sru macro mac; 5600114402Sru int escaped = 0; 5601114402Sru int level = 0; 5602114402Sru mac.append(new token_node(tok)); 5603114402Sru for (;;) { 5604151497Sru node *n = 0; // pacify compiler 5605114402Sru int c = input_stack::get(&n); 5606114402Sru if (c == EOF) 5607114402Sru break; 5608114402Sru if (c == 0) { 5609114402Sru escaped = 0; 5610114402Sru mac.append(n); 5611114402Sru } 5612114402Sru else if (escaped) { 5613114402Sru if (c == '{') 5614114402Sru level += 1; 5615114402Sru else if (c == '}') 5616114402Sru level -= 1; 5617114402Sru escaped = 0; 5618114402Sru mac.append(c); 5619114402Sru } 5620114402Sru else { 5621114402Sru if (c == ESCAPE_LEFT_BRACE) 5622114402Sru level += 1; 5623114402Sru else if (c == ESCAPE_RIGHT_BRACE) 5624114402Sru level -= 1; 5625114402Sru else if (c == escape_char) 5626114402Sru escaped = 1; 5627114402Sru mac.append(c); 5628114402Sru if (c == '\n' && level <= 0) 5629114402Sru break; 5630114402Sru } 5631114402Sru } 5632114402Sru if (level != 0) 5633114402Sru error("unbalanced \\{ \\}"); 5634114402Sru else { 5635114402Sru while_depth++; 5636114402Sru input_stack::add_boundary(); 5637114402Sru for (;;) { 5638114402Sru input_stack::push(new string_iterator(mac, "while loop")); 5639114402Sru tok.next(); 5640114402Sru if (!do_if_request()) { 5641114402Sru while (input_stack::get(0) != EOF) 5642114402Sru ; 5643114402Sru break; 5644114402Sru } 5645114402Sru process_input_stack(); 5646114402Sru if (while_break_flag || input_stack::is_return_boundary()) { 5647114402Sru while_break_flag = 0; 5648114402Sru break; 5649114402Sru } 5650114402Sru } 5651114402Sru input_stack::remove_boundary(); 5652114402Sru while_depth--; 5653114402Sru } 5654114402Sru tok.next(); 5655114402Sru} 5656114402Sru 5657114402Sruvoid while_break_request() 5658114402Sru{ 5659114402Sru if (!while_depth) { 5660114402Sru error("no while loop"); 5661114402Sru skip_line(); 5662114402Sru } 5663114402Sru else { 5664114402Sru while_break_flag = 1; 5665114402Sru while (input_stack::get(0) != EOF) 5666114402Sru ; 5667114402Sru tok.next(); 5668114402Sru } 5669114402Sru} 5670114402Sru 5671114402Sruvoid while_continue_request() 5672114402Sru{ 5673114402Sru if (!while_depth) { 5674114402Sru error("no while loop"); 5675114402Sru skip_line(); 5676114402Sru } 5677114402Sru else { 5678114402Sru while (input_stack::get(0) != EOF) 5679114402Sru ; 5680114402Sru tok.next(); 5681114402Sru } 5682114402Sru} 5683114402Sru 5684114402Sru// .so 5685114402Sru 5686114402Sruvoid source() 5687114402Sru{ 5688114402Sru symbol nm = get_long_name(1); 5689114402Sru if (nm.is_null()) 5690114402Sru skip_line(); 5691114402Sru else { 5692114402Sru while (!tok.newline() && !tok.eof()) 5693114402Sru tok.next(); 5694114402Sru errno = 0; 5695151497Sru FILE *fp = include_search_path.open_file_cautious(nm.contents()); 5696114402Sru if (fp) 5697114402Sru input_stack::push(new file_iterator(fp, nm.contents())); 5698114402Sru else 5699114402Sru error("can't open `%1': %2", nm.contents(), strerror(errno)); 5700114402Sru tok.next(); 5701114402Sru } 5702114402Sru} 5703114402Sru 5704114402Sru// like .so but use popen() 5705114402Sru 5706114402Sruvoid pipe_source() 5707114402Sru{ 5708114402Sru if (safer_flag) { 5709114402Sru error(".pso request not allowed in safer mode"); 5710114402Sru skip_line(); 5711114402Sru } 5712114402Sru else { 5713114402Sru#ifdef POPEN_MISSING 5714114402Sru error("pipes not available on this system"); 5715114402Sru skip_line(); 5716114402Sru#else /* not POPEN_MISSING */ 5717114402Sru if (tok.newline() || tok.eof()) 5718114402Sru error("missing command"); 5719114402Sru else { 5720114402Sru int c; 5721114402Sru while ((c = get_copy(0)) == ' ' || c == '\t') 5722114402Sru ; 5723114402Sru int buf_size = 24; 5724114402Sru char *buf = new char[buf_size]; 5725114402Sru int buf_used = 0; 5726114402Sru for (; c != '\n' && c != EOF; c = get_copy(0)) { 5727114402Sru const char *s = asciify(c); 5728114402Sru int slen = strlen(s); 5729114402Sru if (buf_used + slen + 1> buf_size) { 5730114402Sru char *old_buf = buf; 5731114402Sru int old_buf_size = buf_size; 5732114402Sru buf_size *= 2; 5733114402Sru buf = new char[buf_size]; 5734114402Sru memcpy(buf, old_buf, old_buf_size); 5735114402Sru a_delete old_buf; 5736114402Sru } 5737114402Sru strcpy(buf + buf_used, s); 5738114402Sru buf_used += slen; 5739114402Sru } 5740114402Sru buf[buf_used] = '\0'; 5741114402Sru errno = 0; 5742114402Sru FILE *fp = popen(buf, POPEN_RT); 5743114402Sru if (fp) 5744114402Sru input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1)); 5745114402Sru else 5746114402Sru error("can't open pipe to process `%1': %2", buf, strerror(errno)); 5747114402Sru a_delete buf; 5748114402Sru } 5749114402Sru tok.next(); 5750114402Sru#endif /* not POPEN_MISSING */ 5751114402Sru } 5752114402Sru} 5753114402Sru 5754114402Sru// .psbb 5755114402Sru 5756114402Srustatic int llx_reg_contents = 0; 5757114402Srustatic int lly_reg_contents = 0; 5758114402Srustatic int urx_reg_contents = 0; 5759114402Srustatic int ury_reg_contents = 0; 5760114402Sru 5761114402Srustruct bounding_box { 5762114402Sru int llx, lly, urx, ury; 5763114402Sru}; 5764114402Sru 5765114402Sru/* Parse the argument to a %%BoundingBox comment. Return 1 if it 5766114402Srucontains 4 numbers, 2 if it contains (atend), 0 otherwise. */ 5767114402Sru 5768114402Sruint parse_bounding_box(char *p, bounding_box *bb) 5769114402Sru{ 5770114402Sru if (sscanf(p, "%d %d %d %d", 5771114402Sru &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4) 5772114402Sru return 1; 5773114402Sru else { 5774114402Sru /* The Document Structuring Conventions say that the numbers 5775114402Sru should be integers. Unfortunately some broken applications 5776114402Sru get this wrong. */ 5777114402Sru double x1, x2, x3, x4; 5778114402Sru if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) { 5779114402Sru bb->llx = (int)x1; 5780114402Sru bb->lly = (int)x2; 5781114402Sru bb->urx = (int)x3; 5782114402Sru bb->ury = (int)x4; 5783114402Sru return 1; 5784114402Sru } 5785114402Sru else { 5786114402Sru for (; *p == ' ' || *p == '\t'; p++) 5787114402Sru ; 5788114402Sru if (strncmp(p, "(atend)", 7) == 0) { 5789114402Sru return 2; 5790114402Sru } 5791114402Sru } 5792114402Sru } 5793114402Sru bb->llx = bb->lly = bb->urx = bb->ury = 0; 5794114402Sru return 0; 5795114402Sru} 5796114402Sru 5797114402Sru// This version is taken from psrm.cpp 5798114402Sru 5799114402Sru#define PS_LINE_MAX 255 5800114402Srucset white_space("\n\r \t"); 5801114402Sru 5802114402Sruint ps_get_line(char *buf, FILE *fp, const char* filename) 5803114402Sru{ 5804114402Sru int c = getc(fp); 5805114402Sru if (c == EOF) { 5806114402Sru buf[0] = '\0'; 5807114402Sru return 0; 5808114402Sru } 5809114402Sru int i = 0; 5810114402Sru int err = 0; 5811114402Sru while (c != '\r' && c != '\n' && c != EOF) { 5812114402Sru if ((c < 0x1b && !white_space(c)) || c == 0x7f) 5813114402Sru error("invalid input character code %1 in `%2'", int(c), filename); 5814114402Sru else if (i < PS_LINE_MAX) 5815114402Sru buf[i++] = c; 5816114402Sru else if (!err) { 5817114402Sru err = 1; 5818114402Sru error("PostScript file `%1' is non-conforming " 5819114402Sru "because length of line exceeds 255", filename); 5820114402Sru } 5821114402Sru c = getc(fp); 5822114402Sru } 5823114402Sru buf[i++] = '\n'; 5824114402Sru buf[i] = '\0'; 5825114402Sru if (c == '\r') { 5826114402Sru c = getc(fp); 5827114402Sru if (c != EOF && c != '\n') 5828114402Sru ungetc(c, fp); 5829114402Sru } 5830114402Sru return 1; 5831114402Sru} 5832114402Sru 5833114402Sruinline void assign_registers(int llx, int lly, int urx, int ury) 5834114402Sru{ 5835114402Sru llx_reg_contents = llx; 5836114402Sru lly_reg_contents = lly; 5837114402Sru urx_reg_contents = urx; 5838114402Sru ury_reg_contents = ury; 5839114402Sru} 5840114402Sru 5841114402Sruvoid do_ps_file(FILE *fp, const char* filename) 5842114402Sru{ 5843114402Sru bounding_box bb; 5844114402Sru int bb_at_end = 0; 5845114402Sru char buf[PS_LINE_MAX]; 5846114402Sru llx_reg_contents = lly_reg_contents = 5847114402Sru urx_reg_contents = ury_reg_contents = 0; 5848114402Sru if (!ps_get_line(buf, fp, filename)) { 5849114402Sru error("`%1' is empty", filename); 5850114402Sru return; 5851114402Sru } 5852114402Sru if (strncmp("%!PS-Adobe-", buf, 11) != 0) { 5853114402Sru error("`%1' is not conforming to the Document Structuring Conventions", 5854114402Sru filename); 5855114402Sru return; 5856114402Sru } 5857114402Sru while (ps_get_line(buf, fp, filename) != 0) { 5858114402Sru if (buf[0] != '%' || buf[1] != '%' 5859114402Sru || strncmp(buf + 2, "EndComments", 11) == 0) 5860114402Sru break; 5861114402Sru if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5862114402Sru int res = parse_bounding_box(buf + 14, &bb); 5863114402Sru if (res == 1) { 5864114402Sru assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5865114402Sru return; 5866114402Sru } 5867114402Sru else if (res == 2) { 5868114402Sru bb_at_end = 1; 5869114402Sru break; 5870114402Sru } 5871114402Sru else { 5872114402Sru error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5873114402Sru filename); 5874114402Sru return; 5875114402Sru } 5876114402Sru } 5877114402Sru } 5878114402Sru if (bb_at_end) { 5879114402Sru long offset; 5880114402Sru int last_try = 0; 5881114402Sru /* in the trailer, the last BoundingBox comment is significant */ 5882114402Sru for (offset = 512; !last_try; offset *= 2) { 5883114402Sru int had_trailer = 0; 5884114402Sru int got_bb = 0; 5885114402Sru if (offset > 32768 || fseek(fp, -offset, 2) == -1) { 5886114402Sru last_try = 1; 5887114402Sru if (fseek(fp, 0L, 0) == -1) 5888114402Sru break; 5889114402Sru } 5890114402Sru while (ps_get_line(buf, fp, filename) != 0) { 5891114402Sru if (buf[0] == '%' && buf[1] == '%') { 5892114402Sru if (!had_trailer) { 5893114402Sru if (strncmp(buf + 2, "Trailer", 7) == 0) 5894114402Sru had_trailer = 1; 5895114402Sru } 5896114402Sru else { 5897114402Sru if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { 5898114402Sru int res = parse_bounding_box(buf + 14, &bb); 5899114402Sru if (res == 1) 5900114402Sru got_bb = 1; 5901114402Sru else if (res == 2) { 5902114402Sru error("`(atend)' not allowed in trailer of `%1'", filename); 5903114402Sru return; 5904114402Sru } 5905114402Sru else { 5906114402Sru error("the arguments to the %%%%BoundingBox comment in `%1' are bad", 5907114402Sru filename); 5908114402Sru return; 5909114402Sru } 5910114402Sru } 5911114402Sru } 5912114402Sru } 5913114402Sru } 5914114402Sru if (got_bb) { 5915114402Sru assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); 5916114402Sru return; 5917114402Sru } 5918114402Sru } 5919114402Sru } 5920114402Sru error("%%%%BoundingBox comment not found in `%1'", filename); 5921114402Sru} 5922114402Sru 5923114402Sruvoid ps_bbox_request() 5924114402Sru{ 5925114402Sru symbol nm = get_long_name(1); 5926114402Sru if (nm.is_null()) 5927114402Sru skip_line(); 5928114402Sru else { 5929114402Sru while (!tok.newline() && !tok.eof()) 5930114402Sru tok.next(); 5931114402Sru errno = 0; 5932114402Sru // PS files might contain non-printable characters, such as ^Z 5933114402Sru // and CRs not followed by an LF, so open them in binary mode. 5934151497Sru FILE *fp = include_search_path.open_file_cautious(nm.contents(), 5935151497Sru 0, FOPEN_RB); 5936114402Sru if (fp) { 5937114402Sru do_ps_file(fp, nm.contents()); 5938114402Sru fclose(fp); 5939114402Sru } 5940114402Sru else 5941114402Sru error("can't open `%1': %2", nm.contents(), strerror(errno)); 5942114402Sru tok.next(); 5943114402Sru } 5944114402Sru} 5945114402Sru 5946114402Sruconst char *asciify(int c) 5947114402Sru{ 5948114402Sru static char buf[3]; 5949114402Sru buf[0] = escape_char == '\0' ? '\\' : escape_char; 5950114402Sru buf[1] = buf[2] = '\0'; 5951114402Sru switch (c) { 5952114402Sru case ESCAPE_QUESTION: 5953114402Sru buf[1] = '?'; 5954114402Sru break; 5955114402Sru case ESCAPE_AMPERSAND: 5956114402Sru buf[1] = '&'; 5957114402Sru break; 5958114402Sru case ESCAPE_RIGHT_PARENTHESIS: 5959114402Sru buf[1] = ')'; 5960114402Sru break; 5961114402Sru case ESCAPE_UNDERSCORE: 5962114402Sru buf[1] = '_'; 5963114402Sru break; 5964114402Sru case ESCAPE_BAR: 5965114402Sru buf[1] = '|'; 5966114402Sru break; 5967114402Sru case ESCAPE_CIRCUMFLEX: 5968114402Sru buf[1] = '^'; 5969114402Sru break; 5970114402Sru case ESCAPE_LEFT_BRACE: 5971114402Sru buf[1] = '{'; 5972114402Sru break; 5973114402Sru case ESCAPE_RIGHT_BRACE: 5974114402Sru buf[1] = '}'; 5975114402Sru break; 5976114402Sru case ESCAPE_LEFT_QUOTE: 5977114402Sru buf[1] = '`'; 5978114402Sru break; 5979114402Sru case ESCAPE_RIGHT_QUOTE: 5980114402Sru buf[1] = '\''; 5981114402Sru break; 5982114402Sru case ESCAPE_HYPHEN: 5983114402Sru buf[1] = '-'; 5984114402Sru break; 5985114402Sru case ESCAPE_BANG: 5986114402Sru buf[1] = '!'; 5987114402Sru break; 5988114402Sru case ESCAPE_c: 5989114402Sru buf[1] = 'c'; 5990114402Sru break; 5991114402Sru case ESCAPE_e: 5992114402Sru buf[1] = 'e'; 5993114402Sru break; 5994114402Sru case ESCAPE_E: 5995114402Sru buf[1] = 'E'; 5996114402Sru break; 5997114402Sru case ESCAPE_PERCENT: 5998114402Sru buf[1] = '%'; 5999114402Sru break; 6000114402Sru case ESCAPE_SPACE: 6001114402Sru buf[1] = ' '; 6002114402Sru break; 6003114402Sru case ESCAPE_TILDE: 6004114402Sru buf[1] = '~'; 6005114402Sru break; 6006114402Sru case ESCAPE_COLON: 6007114402Sru buf[1] = ':'; 6008114402Sru break; 6009151497Sru case PUSH_GROFF_MODE: 6010151497Sru case PUSH_COMP_MODE: 6011151497Sru case POP_GROFFCOMP_MODE: 6012114402Sru buf[0] = '\0'; 6013114402Sru break; 6014114402Sru default: 6015114402Sru if (invalid_input_char(c)) 6016114402Sru buf[0] = '\0'; 6017114402Sru else 6018114402Sru buf[0] = c; 6019114402Sru break; 6020114402Sru } 6021114402Sru return buf; 6022114402Sru} 6023114402Sru 6024114402Sruconst char *input_char_description(int c) 6025114402Sru{ 6026114402Sru switch (c) { 6027114402Sru case '\n': 6028114402Sru return "a newline character"; 6029114402Sru case '\b': 6030114402Sru return "a backspace character"; 6031114402Sru case '\001': 6032114402Sru return "a leader character"; 6033114402Sru case '\t': 6034114402Sru return "a tab character"; 6035114402Sru case ' ': 6036114402Sru return "a space character"; 6037114402Sru case '\0': 6038114402Sru return "a node"; 6039114402Sru } 6040114402Sru static char buf[sizeof("magic character code ") + 1 + INT_DIGITS]; 6041114402Sru if (invalid_input_char(c)) { 6042114402Sru const char *s = asciify(c); 6043114402Sru if (*s) { 6044114402Sru buf[0] = '`'; 6045114402Sru strcpy(buf + 1, s); 6046114402Sru strcat(buf, "'"); 6047114402Sru return buf; 6048114402Sru } 6049114402Sru sprintf(buf, "magic character code %d", c); 6050114402Sru return buf; 6051114402Sru } 6052114402Sru if (csprint(c)) { 6053114402Sru buf[0] = '`'; 6054114402Sru buf[1] = c; 6055114402Sru buf[2] = '\''; 6056114402Sru return buf; 6057114402Sru } 6058114402Sru sprintf(buf, "character code %d", c); 6059114402Sru return buf; 6060114402Sru} 6061114402Sru 6062151497Sruvoid tag() 6063151497Sru{ 6064151497Sru if (!tok.newline() && !tok.eof()) { 6065151497Sru string s; 6066151497Sru int c; 6067151497Sru for (;;) { 6068151497Sru c = get_copy(0); 6069151497Sru if (c == '"') { 6070151497Sru c = get_copy(0); 6071151497Sru break; 6072151497Sru } 6073151497Sru if (c != ' ' && c != '\t') 6074151497Sru break; 6075151497Sru } 6076151497Sru s = "x X "; 6077151497Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 6078151497Sru s += (char)c; 6079151497Sru s += '\n'; 6080151497Sru curenv->add_node(new tag_node(s, 0)); 6081151497Sru } 6082151497Sru tok.next(); 6083151497Sru} 6084151497Sru 6085151497Sruvoid taga() 6086151497Sru{ 6087151497Sru if (!tok.newline() && !tok.eof()) { 6088151497Sru string s; 6089151497Sru int c; 6090151497Sru for (;;) { 6091151497Sru c = get_copy(0); 6092151497Sru if (c == '"') { 6093151497Sru c = get_copy(0); 6094151497Sru break; 6095151497Sru } 6096151497Sru if (c != ' ' && c != '\t') 6097151497Sru break; 6098151497Sru } 6099151497Sru s = "x X "; 6100151497Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 6101151497Sru s += (char)c; 6102151497Sru s += '\n'; 6103151497Sru curenv->add_node(new tag_node(s, 1)); 6104151497Sru } 6105151497Sru tok.next(); 6106151497Sru} 6107151497Sru 6108114402Sru// .tm, .tm1, and .tmc 6109114402Sru 6110114402Sruvoid do_terminal(int newline, int string_like) 6111114402Sru{ 6112114402Sru if (!tok.newline() && !tok.eof()) { 6113114402Sru int c; 6114114402Sru for (;;) { 6115114402Sru c = get_copy(0); 6116114402Sru if (string_like && c == '"') { 6117114402Sru c = get_copy(0); 6118114402Sru break; 6119114402Sru } 6120114402Sru if (c != ' ' && c != '\t') 6121114402Sru break; 6122114402Sru } 6123114402Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 6124114402Sru fputs(asciify(c), stderr); 6125114402Sru } 6126114402Sru if (newline) 6127114402Sru fputc('\n', stderr); 6128114402Sru fflush(stderr); 6129114402Sru tok.next(); 6130114402Sru} 6131114402Sru 6132114402Sruvoid terminal() 6133114402Sru{ 6134114402Sru do_terminal(1, 0); 6135114402Sru} 6136114402Sru 6137114402Sruvoid terminal1() 6138114402Sru{ 6139114402Sru do_terminal(1, 1); 6140114402Sru} 6141114402Sru 6142114402Sruvoid terminal_continue() 6143114402Sru{ 6144114402Sru do_terminal(0, 1); 6145114402Sru} 6146114402Sru 6147114402Srudictionary stream_dictionary(20); 6148114402Sru 6149114402Sruvoid do_open(int append) 6150114402Sru{ 6151114402Sru symbol stream = get_name(1); 6152114402Sru if (!stream.is_null()) { 6153114402Sru symbol filename = get_long_name(1); 6154114402Sru if (!filename.is_null()) { 6155114402Sru errno = 0; 6156114402Sru FILE *fp = fopen(filename.contents(), append ? "a" : "w"); 6157114402Sru if (!fp) { 6158114402Sru error("can't open `%1' for %2: %3", 6159114402Sru filename.contents(), 6160114402Sru append ? "appending" : "writing", 6161114402Sru strerror(errno)); 6162114402Sru fp = (FILE *)stream_dictionary.remove(stream); 6163114402Sru } 6164114402Sru else 6165114402Sru fp = (FILE *)stream_dictionary.lookup(stream, fp); 6166114402Sru if (fp) 6167114402Sru fclose(fp); 6168114402Sru } 6169114402Sru } 6170114402Sru skip_line(); 6171114402Sru} 6172114402Sru 6173114402Sruvoid open_request() 6174114402Sru{ 6175114402Sru if (safer_flag) { 6176114402Sru error(".open request not allowed in safer mode"); 6177114402Sru skip_line(); 6178114402Sru } 6179114402Sru else 6180114402Sru do_open(0); 6181114402Sru} 6182114402Sru 6183114402Sruvoid opena_request() 6184114402Sru{ 6185114402Sru if (safer_flag) { 6186114402Sru error(".opena request not allowed in safer mode"); 6187114402Sru skip_line(); 6188114402Sru } 6189114402Sru else 6190114402Sru do_open(1); 6191114402Sru} 6192114402Sru 6193114402Sruvoid close_request() 6194114402Sru{ 6195114402Sru symbol stream = get_name(1); 6196114402Sru if (!stream.is_null()) { 6197114402Sru FILE *fp = (FILE *)stream_dictionary.remove(stream); 6198114402Sru if (!fp) 6199114402Sru error("no stream named `%1'", stream.contents()); 6200114402Sru else 6201114402Sru fclose(fp); 6202114402Sru } 6203114402Sru skip_line(); 6204114402Sru} 6205114402Sru 6206114402Sru// .write and .writec 6207114402Sru 6208114402Sruvoid do_write_request(int newline) 6209114402Sru{ 6210114402Sru symbol stream = get_name(1); 6211114402Sru if (stream.is_null()) { 6212114402Sru skip_line(); 6213114402Sru return; 6214114402Sru } 6215114402Sru FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6216114402Sru if (!fp) { 6217114402Sru error("no stream named `%1'", stream.contents()); 6218114402Sru skip_line(); 6219114402Sru return; 6220114402Sru } 6221114402Sru int c; 6222114402Sru while ((c = get_copy(0)) == ' ') 6223114402Sru ; 6224114402Sru if (c == '"') 6225114402Sru c = get_copy(0); 6226114402Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 6227114402Sru fputs(asciify(c), fp); 6228114402Sru if (newline) 6229114402Sru fputc('\n', fp); 6230114402Sru fflush(fp); 6231114402Sru tok.next(); 6232114402Sru} 6233114402Sru 6234114402Sruvoid write_request() 6235114402Sru{ 6236114402Sru do_write_request(1); 6237114402Sru} 6238114402Sru 6239114402Sruvoid write_request_continue() 6240114402Sru{ 6241114402Sru do_write_request(0); 6242114402Sru} 6243114402Sru 6244114402Sruvoid write_macro_request() 6245114402Sru{ 6246114402Sru symbol stream = get_name(1); 6247114402Sru if (stream.is_null()) { 6248114402Sru skip_line(); 6249114402Sru return; 6250114402Sru } 6251114402Sru FILE *fp = (FILE *)stream_dictionary.lookup(stream); 6252114402Sru if (!fp) { 6253114402Sru error("no stream named `%1'", stream.contents()); 6254114402Sru skip_line(); 6255114402Sru return; 6256114402Sru } 6257114402Sru symbol s = get_name(1); 6258114402Sru if (s.is_null()) { 6259114402Sru skip_line(); 6260114402Sru return; 6261114402Sru } 6262114402Sru request_or_macro *p = lookup_request(s); 6263114402Sru macro *m = p->to_macro(); 6264114402Sru if (!m) 6265114402Sru error("cannot write request"); 6266114402Sru else { 6267114402Sru string_iterator iter(*m); 6268114402Sru for (;;) { 6269114402Sru int c = iter.get(0); 6270114402Sru if (c == EOF) 6271114402Sru break; 6272114402Sru fputs(asciify(c), fp); 6273114402Sru } 6274114402Sru fflush(fp); 6275114402Sru } 6276114402Sru skip_line(); 6277114402Sru} 6278114402Sru 6279114402Sruvoid warnscale_request() 6280114402Sru{ 6281114402Sru if (has_arg()) { 6282114402Sru char c = tok.ch(); 6283114402Sru if (c == 'u') 6284114402Sru warn_scale = 1.0; 6285114402Sru else if (c == 'i') 6286114402Sru warn_scale = (double)units_per_inch; 6287114402Sru else if (c == 'c') 6288114402Sru warn_scale = (double)units_per_inch / 2.54; 6289114402Sru else if (c == 'p') 6290114402Sru warn_scale = (double)units_per_inch / 72.0; 6291114402Sru else if (c == 'P') 6292114402Sru warn_scale = (double)units_per_inch / 6.0; 6293114402Sru else { 6294114402Sru warning(WARN_SCALE, 6295114402Sru "invalid scaling indicator `%1', using `i' instead", c); 6296114402Sru c = 'i'; 6297114402Sru } 6298114402Sru warn_scaling_indicator = c; 6299114402Sru } 6300114402Sru skip_line(); 6301114402Sru} 6302114402Sru 6303114402Sruvoid spreadwarn_request() 6304114402Sru{ 6305114402Sru hunits n; 6306114402Sru if (has_arg() && get_hunits(&n, 'm')) { 6307114402Sru if (n < 0) 6308114402Sru n = 0; 6309114402Sru hunits em = curenv->get_size(); 6310114402Sru spread_limit = (double)n.to_units() 6311114402Sru / (em.is_zero() ? hresolution : em.to_units()); 6312114402Sru } 6313114402Sru else 6314114402Sru spread_limit = -spread_limit - 1; // no arg toggles on/off without 6315114402Sru // changing value; we mirror at 6316114402Sru // -0.5 to make zero a valid value 6317114402Sru skip_line(); 6318114402Sru} 6319114402Sru 6320114402Srustatic void init_charset_table() 6321114402Sru{ 6322114402Sru char buf[16]; 6323114402Sru strcpy(buf, "char"); 6324114402Sru for (int i = 0; i < 256; i++) { 6325114402Sru strcpy(buf + 4, i_to_a(i)); 6326114402Sru charset_table[i] = get_charinfo(symbol(buf)); 6327114402Sru charset_table[i]->set_ascii_code(i); 6328114402Sru if (csalpha(i)) 6329114402Sru charset_table[i]->set_hyphenation_code(cmlower(i)); 6330114402Sru } 6331114402Sru charset_table['.']->set_flags(charinfo::ENDS_SENTENCE); 6332114402Sru charset_table['?']->set_flags(charinfo::ENDS_SENTENCE); 6333114402Sru charset_table['!']->set_flags(charinfo::ENDS_SENTENCE); 6334114402Sru charset_table['-']->set_flags(charinfo::BREAK_AFTER); 6335114402Sru charset_table['"']->set_flags(charinfo::TRANSPARENT); 6336114402Sru charset_table['\'']->set_flags(charinfo::TRANSPARENT); 6337114402Sru charset_table[')']->set_flags(charinfo::TRANSPARENT); 6338114402Sru charset_table[']']->set_flags(charinfo::TRANSPARENT); 6339114402Sru charset_table['*']->set_flags(charinfo::TRANSPARENT); 6340114402Sru get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT); 6341114402Sru get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT); 6342114402Sru get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER); 6343114402Sru get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6344114402Sru get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6345114402Sru get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6346114402Sru get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6347114402Sru get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); 6348114402Sru get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY); 6349114402Sru page_character = charset_table['%']; 6350114402Sru} 6351114402Sru 6352114402Srustatic void init_hpf_code_table() 6353114402Sru{ 6354114402Sru for (int i = 0; i < 256; i++) 6355114402Sru hpf_code_table[i] = i; 6356114402Sru} 6357114402Sru 6358114402Srustatic void do_translate(int translate_transparent, int translate_input) 6359114402Sru{ 6360114402Sru tok.skip(); 6361114402Sru while (!tok.newline() && !tok.eof()) { 6362114402Sru if (tok.space()) { 6363114402Sru // This is a really bizarre troff feature. 6364114402Sru tok.next(); 6365114402Sru translate_space_to_dummy = tok.dummy(); 6366114402Sru if (tok.newline() || tok.eof()) 6367114402Sru break; 6368114402Sru tok.next(); 6369114402Sru continue; 6370114402Sru } 6371114402Sru charinfo *ci1 = tok.get_char(1); 6372114402Sru if (ci1 == 0) 6373114402Sru break; 6374114402Sru tok.next(); 6375114402Sru if (tok.newline() || tok.eof()) { 6376114402Sru ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6377114402Sru translate_transparent); 6378114402Sru break; 6379114402Sru } 6380114402Sru if (tok.space()) 6381114402Sru ci1->set_special_translation(charinfo::TRANSLATE_SPACE, 6382114402Sru translate_transparent); 6383114402Sru else if (tok.stretchable_space()) 6384114402Sru ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE, 6385114402Sru translate_transparent); 6386114402Sru else if (tok.dummy()) 6387114402Sru ci1->set_special_translation(charinfo::TRANSLATE_DUMMY, 6388114402Sru translate_transparent); 6389114402Sru else if (tok.hyphen_indicator()) 6390114402Sru ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR, 6391114402Sru translate_transparent); 6392114402Sru else { 6393114402Sru charinfo *ci2 = tok.get_char(1); 6394114402Sru if (ci2 == 0) 6395114402Sru break; 6396114402Sru if (ci1 == ci2) 6397114402Sru ci1->set_translation(0, translate_transparent, translate_input); 6398114402Sru else 6399114402Sru ci1->set_translation(ci2, translate_transparent, translate_input); 6400114402Sru } 6401114402Sru tok.next(); 6402114402Sru } 6403114402Sru skip_line(); 6404114402Sru} 6405114402Sru 6406114402Sruvoid translate() 6407114402Sru{ 6408114402Sru do_translate(1, 0); 6409114402Sru} 6410114402Sru 6411114402Sruvoid translate_no_transparent() 6412114402Sru{ 6413114402Sru do_translate(0, 0); 6414114402Sru} 6415114402Sru 6416114402Sruvoid translate_input() 6417114402Sru{ 6418114402Sru do_translate(1, 1); 6419114402Sru} 6420114402Sru 6421114402Sruvoid char_flags() 6422114402Sru{ 6423114402Sru int flags; 6424114402Sru if (get_integer(&flags)) 6425114402Sru while (has_arg()) { 6426114402Sru charinfo *ci = tok.get_char(1); 6427114402Sru if (ci) { 6428114402Sru charinfo *tem = ci->get_translation(); 6429114402Sru if (tem) 6430114402Sru ci = tem; 6431114402Sru ci->set_flags(flags); 6432114402Sru } 6433114402Sru tok.next(); 6434114402Sru } 6435114402Sru skip_line(); 6436114402Sru} 6437114402Sru 6438114402Sruvoid hyphenation_code() 6439114402Sru{ 6440114402Sru tok.skip(); 6441114402Sru while (!tok.newline() && !tok.eof()) { 6442114402Sru charinfo *ci = tok.get_char(1); 6443114402Sru if (ci == 0) 6444114402Sru break; 6445114402Sru tok.next(); 6446114402Sru tok.skip(); 6447114402Sru unsigned char c = tok.ch(); 6448114402Sru if (c == 0) { 6449114402Sru error("hyphenation code must be ordinary character"); 6450114402Sru break; 6451114402Sru } 6452114402Sru if (csdigit(c)) { 6453114402Sru error("hyphenation code cannot be digit"); 6454114402Sru break; 6455114402Sru } 6456114402Sru ci->set_hyphenation_code(c); 6457114402Sru if (ci->get_translation() 6458114402Sru && ci->get_translation()->get_translation_input()) 6459114402Sru ci->get_translation()->set_hyphenation_code(c); 6460114402Sru tok.next(); 6461114402Sru tok.skip(); 6462114402Sru } 6463114402Sru skip_line(); 6464114402Sru} 6465114402Sru 6466114402Sruvoid hyphenation_patterns_file_code() 6467114402Sru{ 6468114402Sru tok.skip(); 6469114402Sru while (!tok.newline() && !tok.eof()) { 6470114402Sru int n1, n2; 6471114402Sru if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) { 6472114402Sru if (!has_arg()) { 6473114402Sru error("missing output hyphenation code"); 6474114402Sru break; 6475114402Sru } 6476114402Sru if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) { 6477114402Sru hpf_code_table[n1] = n2; 6478114402Sru tok.skip(); 6479114402Sru } 6480114402Sru else { 6481114402Sru error("output hyphenation code must be integer in the range 0..255"); 6482114402Sru break; 6483114402Sru } 6484114402Sru } 6485114402Sru else { 6486114402Sru error("input hyphenation code must be integer in the range 0..255"); 6487114402Sru break; 6488114402Sru } 6489114402Sru } 6490114402Sru skip_line(); 6491114402Sru} 6492114402Sru 6493114402Srucharinfo *token::get_char(int required) 6494114402Sru{ 6495114402Sru if (type == TOKEN_CHAR) 6496114402Sru return charset_table[c]; 6497114402Sru if (type == TOKEN_SPECIAL) 6498114402Sru return get_charinfo(nm); 6499114402Sru if (type == TOKEN_NUMBERED_CHAR) 6500114402Sru return get_charinfo_by_number(val); 6501114402Sru if (type == TOKEN_ESCAPE) { 6502114402Sru if (escape_char != 0) 6503114402Sru return charset_table[escape_char]; 6504114402Sru else { 6505114402Sru error("`\\e' used while no current escape character"); 6506114402Sru return 0; 6507114402Sru } 6508114402Sru } 6509114402Sru if (required) { 6510114402Sru if (type == TOKEN_EOF || type == TOKEN_NEWLINE) 6511114402Sru warning(WARN_MISSING, "missing normal or special character"); 6512114402Sru else 6513114402Sru error("normal or special character expected (got %1)", description()); 6514114402Sru } 6515114402Sru return 0; 6516114402Sru} 6517114402Sru 6518114402Srucharinfo *get_optional_char() 6519114402Sru{ 6520114402Sru while (tok.space()) 6521114402Sru tok.next(); 6522114402Sru charinfo *ci = tok.get_char(); 6523114402Sru if (!ci) 6524114402Sru check_missing_character(); 6525114402Sru else 6526114402Sru tok.next(); 6527114402Sru return ci; 6528114402Sru} 6529114402Sru 6530114402Sruvoid check_missing_character() 6531114402Sru{ 6532114402Sru if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab()) 6533114402Sru error("normal or special character expected (got %1): " 6534114402Sru "treated as missing", 6535114402Sru tok.description()); 6536114402Sru} 6537114402Sru 6538114402Sru// this is for \Z 6539114402Sru 6540114402Sruint token::add_to_node_list(node **pp) 6541114402Sru{ 6542114402Sru hunits w; 6543114402Sru int s; 6544114402Sru node *n = 0; 6545114402Sru switch (type) { 6546114402Sru case TOKEN_CHAR: 6547114402Sru *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s); 6548114402Sru break; 6549114402Sru case TOKEN_DUMMY: 6550114402Sru n = new dummy_node; 6551114402Sru break; 6552114402Sru case TOKEN_ESCAPE: 6553114402Sru if (escape_char != 0) 6554114402Sru *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s); 6555114402Sru break; 6556114402Sru case TOKEN_HYPHEN_INDICATOR: 6557114402Sru *pp = (*pp)->add_discretionary_hyphen(); 6558114402Sru break; 6559114402Sru case TOKEN_ITALIC_CORRECTION: 6560114402Sru *pp = (*pp)->add_italic_correction(&w); 6561114402Sru break; 6562114402Sru case TOKEN_LEFT_BRACE: 6563114402Sru break; 6564114402Sru case TOKEN_MARK_INPUT: 6565114402Sru set_number_reg(nm, curenv->get_input_line_position().to_units()); 6566114402Sru break; 6567114402Sru case TOKEN_NODE: 6568114402Sru n = nd; 6569114402Sru nd = 0; 6570114402Sru break; 6571114402Sru case TOKEN_NUMBERED_CHAR: 6572114402Sru *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s); 6573114402Sru break; 6574114402Sru case TOKEN_RIGHT_BRACE: 6575114402Sru break; 6576114402Sru case TOKEN_SPACE: 6577114402Sru n = new hmotion_node(curenv->get_space_width(), 6578114402Sru curenv->get_fill_color()); 6579114402Sru break; 6580114402Sru case TOKEN_SPECIAL: 6581114402Sru *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s); 6582114402Sru break; 6583114402Sru case TOKEN_STRETCHABLE_SPACE: 6584114402Sru n = new unbreakable_space_node(curenv->get_space_width(), 6585114402Sru curenv->get_fill_color()); 6586114402Sru break; 6587114402Sru case TOKEN_UNSTRETCHABLE_SPACE: 6588114402Sru n = new space_char_hmotion_node(curenv->get_space_width(), 6589114402Sru curenv->get_fill_color()); 6590114402Sru break; 6591114402Sru case TOKEN_TRANSPARENT_DUMMY: 6592114402Sru n = new transparent_dummy_node; 6593114402Sru break; 6594114402Sru case TOKEN_ZERO_WIDTH_BREAK: 6595114402Sru n = new space_node(H0, curenv->get_fill_color()); 6596114402Sru n->freeze_space(); 6597114402Sru n->is_escape_colon(); 6598114402Sru break; 6599114402Sru default: 6600114402Sru return 0; 6601114402Sru } 6602114402Sru if (n) { 6603114402Sru n->next = *pp; 6604114402Sru *pp = n; 6605114402Sru } 6606114402Sru return 1; 6607114402Sru} 6608114402Sru 6609114402Sruvoid token::process() 6610114402Sru{ 6611114402Sru if (possibly_handle_first_page_transition()) 6612114402Sru return; 6613114402Sru switch (type) { 6614114402Sru case TOKEN_BACKSPACE: 6615114402Sru curenv->add_node(new hmotion_node(-curenv->get_space_width(), 6616114402Sru curenv->get_fill_color())); 6617114402Sru break; 6618114402Sru case TOKEN_CHAR: 6619114402Sru curenv->add_char(charset_table[c]); 6620114402Sru break; 6621114402Sru case TOKEN_DUMMY: 6622114402Sru curenv->add_node(new dummy_node); 6623114402Sru break; 6624114402Sru case TOKEN_EMPTY: 6625114402Sru assert(0); 6626114402Sru break; 6627114402Sru case TOKEN_EOF: 6628114402Sru assert(0); 6629114402Sru break; 6630114402Sru case TOKEN_ESCAPE: 6631114402Sru if (escape_char != 0) 6632114402Sru curenv->add_char(charset_table[escape_char]); 6633114402Sru break; 6634114402Sru case TOKEN_BEGIN_TRAP: 6635114402Sru case TOKEN_END_TRAP: 6636114402Sru case TOKEN_PAGE_EJECTOR: 6637114402Sru // these are all handled in process_input_stack() 6638114402Sru break; 6639114402Sru case TOKEN_HYPHEN_INDICATOR: 6640114402Sru curenv->add_hyphen_indicator(); 6641114402Sru break; 6642114402Sru case TOKEN_INTERRUPT: 6643114402Sru curenv->interrupt(); 6644114402Sru break; 6645114402Sru case TOKEN_ITALIC_CORRECTION: 6646114402Sru curenv->add_italic_correction(); 6647114402Sru break; 6648114402Sru case TOKEN_LEADER: 6649114402Sru curenv->handle_tab(1); 6650114402Sru break; 6651114402Sru case TOKEN_LEFT_BRACE: 6652114402Sru break; 6653114402Sru case TOKEN_MARK_INPUT: 6654114402Sru set_number_reg(nm, curenv->get_input_line_position().to_units()); 6655114402Sru break; 6656114402Sru case TOKEN_NEWLINE: 6657114402Sru curenv->newline(); 6658114402Sru break; 6659114402Sru case TOKEN_NODE: 6660114402Sru curenv->add_node(nd); 6661114402Sru nd = 0; 6662114402Sru break; 6663114402Sru case TOKEN_NUMBERED_CHAR: 6664114402Sru curenv->add_char(get_charinfo_by_number(val)); 6665114402Sru break; 6666114402Sru case TOKEN_REQUEST: 6667114402Sru // handled in process_input_stack() 6668114402Sru break; 6669114402Sru case TOKEN_RIGHT_BRACE: 6670114402Sru break; 6671114402Sru case TOKEN_SPACE: 6672114402Sru curenv->space(); 6673114402Sru break; 6674114402Sru case TOKEN_SPECIAL: 6675114402Sru curenv->add_char(get_charinfo(nm)); 6676114402Sru break; 6677114402Sru case TOKEN_SPREAD: 6678114402Sru curenv->spread(); 6679114402Sru break; 6680114402Sru case TOKEN_STRETCHABLE_SPACE: 6681114402Sru curenv->add_node(new unbreakable_space_node(curenv->get_space_width(), 6682114402Sru curenv->get_fill_color())); 6683114402Sru break; 6684114402Sru case TOKEN_UNSTRETCHABLE_SPACE: 6685114402Sru curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(), 6686114402Sru curenv->get_fill_color())); 6687114402Sru break; 6688114402Sru case TOKEN_TAB: 6689114402Sru curenv->handle_tab(0); 6690114402Sru break; 6691114402Sru case TOKEN_TRANSPARENT: 6692114402Sru break; 6693114402Sru case TOKEN_TRANSPARENT_DUMMY: 6694114402Sru curenv->add_node(new transparent_dummy_node); 6695114402Sru break; 6696114402Sru case TOKEN_ZERO_WIDTH_BREAK: 6697114402Sru { 6698114402Sru node *tmp = new space_node(H0, curenv->get_fill_color()); 6699114402Sru tmp->freeze_space(); 6700114402Sru tmp->is_escape_colon(); 6701114402Sru curenv->add_node(tmp); 6702114402Sru break; 6703114402Sru } 6704114402Sru default: 6705114402Sru assert(0); 6706114402Sru } 6707114402Sru} 6708114402Sru 6709114402Sruclass nargs_reg : public reg { 6710114402Srupublic: 6711114402Sru const char *get_string(); 6712114402Sru}; 6713114402Sru 6714114402Sruconst char *nargs_reg::get_string() 6715114402Sru{ 6716114402Sru return i_to_a(input_stack::nargs()); 6717114402Sru} 6718114402Sru 6719114402Sruclass lineno_reg : public reg { 6720114402Srupublic: 6721114402Sru const char *get_string(); 6722114402Sru}; 6723114402Sru 6724114402Sruconst char *lineno_reg::get_string() 6725114402Sru{ 6726114402Sru int line; 6727114402Sru const char *file; 6728114402Sru if (!input_stack::get_location(0, &file, &line)) 6729114402Sru line = 0; 6730114402Sru return i_to_a(line); 6731114402Sru} 6732114402Sru 6733114402Sruclass writable_lineno_reg : public general_reg { 6734114402Srupublic: 6735114402Sru writable_lineno_reg(); 6736114402Sru void set_value(units); 6737114402Sru int get_value(units *); 6738114402Sru}; 6739114402Sru 6740114402Sruwritable_lineno_reg::writable_lineno_reg() 6741114402Sru{ 6742114402Sru} 6743114402Sru 6744114402Sruint writable_lineno_reg::get_value(units *res) 6745114402Sru{ 6746114402Sru int line; 6747114402Sru const char *file; 6748114402Sru if (!input_stack::get_location(0, &file, &line)) 6749114402Sru return 0; 6750114402Sru *res = line; 6751114402Sru return 1; 6752114402Sru} 6753114402Sru 6754114402Sruvoid writable_lineno_reg::set_value(units n) 6755114402Sru{ 6756114402Sru input_stack::set_location(0, n); 6757114402Sru} 6758114402Sru 6759114402Sruclass filename_reg : public reg { 6760114402Srupublic: 6761114402Sru const char *get_string(); 6762114402Sru}; 6763114402Sru 6764114402Sruconst char *filename_reg::get_string() 6765114402Sru{ 6766114402Sru int line; 6767114402Sru const char *file; 6768114402Sru if (input_stack::get_location(0, &file, &line)) 6769114402Sru return file; 6770114402Sru else 6771114402Sru return 0; 6772114402Sru} 6773114402Sru 6774114402Sruclass constant_reg : public reg { 6775114402Sru const char *s; 6776114402Srupublic: 6777114402Sru constant_reg(const char *); 6778114402Sru const char *get_string(); 6779114402Sru}; 6780114402Sru 6781114402Sruconstant_reg::constant_reg(const char *p) : s(p) 6782114402Sru{ 6783114402Sru} 6784114402Sru 6785114402Sruconst char *constant_reg::get_string() 6786114402Sru{ 6787114402Sru return s; 6788114402Sru} 6789114402Sru 6790114402Sruconstant_int_reg::constant_int_reg(int *q) : p(q) 6791114402Sru{ 6792114402Sru} 6793114402Sru 6794114402Sruconst char *constant_int_reg::get_string() 6795114402Sru{ 6796114402Sru return i_to_a(*p); 6797114402Sru} 6798114402Sru 6799114402Sruvoid abort_request() 6800114402Sru{ 6801114402Sru int c; 6802114402Sru if (tok.eof()) 6803114402Sru c = EOF; 6804114402Sru else if (tok.newline()) 6805114402Sru c = '\n'; 6806114402Sru else { 6807114402Sru while ((c = get_copy(0)) == ' ') 6808114402Sru ; 6809114402Sru } 6810114402Sru if (c == EOF || c == '\n') 6811114402Sru fputs("User Abort.", stderr); 6812114402Sru else { 6813114402Sru for (; c != '\n' && c != EOF; c = get_copy(0)) 6814114402Sru fputs(asciify(c), stderr); 6815114402Sru } 6816114402Sru fputc('\n', stderr); 6817114402Sru cleanup_and_exit(1); 6818114402Sru} 6819114402Sru 6820114402Sruchar *read_string() 6821114402Sru{ 6822114402Sru int len = 256; 6823114402Sru char *s = new char[len]; 6824114402Sru int c; 6825114402Sru while ((c = get_copy(0)) == ' ') 6826114402Sru ; 6827114402Sru int i = 0; 6828114402Sru while (c != '\n' && c != EOF) { 6829114402Sru if (!invalid_input_char(c)) { 6830114402Sru if (i + 2 > len) { 6831114402Sru char *tem = s; 6832114402Sru s = new char[len*2]; 6833114402Sru memcpy(s, tem, len); 6834114402Sru len *= 2; 6835114402Sru a_delete tem; 6836114402Sru } 6837114402Sru s[i++] = c; 6838114402Sru } 6839114402Sru c = get_copy(0); 6840114402Sru } 6841114402Sru s[i] = '\0'; 6842114402Sru tok.next(); 6843114402Sru if (i == 0) { 6844114402Sru a_delete s; 6845114402Sru return 0; 6846114402Sru } 6847114402Sru return s; 6848114402Sru} 6849114402Sru 6850114402Sruvoid pipe_output() 6851114402Sru{ 6852114402Sru if (safer_flag) { 6853114402Sru error(".pi request not allowed in safer mode"); 6854114402Sru skip_line(); 6855114402Sru } 6856114402Sru else { 6857114402Sru#ifdef POPEN_MISSING 6858114402Sru error("pipes not available on this system"); 6859114402Sru skip_line(); 6860114402Sru#else /* not POPEN_MISSING */ 6861114402Sru if (the_output) { 6862114402Sru error("can't pipe: output already started"); 6863114402Sru skip_line(); 6864114402Sru } 6865114402Sru else { 6866114402Sru char *pc; 6867114402Sru if ((pc = read_string()) == 0) 6868114402Sru error("can't pipe to empty command"); 6869114402Sru if (pipe_command) { 6870114402Sru char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1]; 6871114402Sru strcpy(s, pipe_command); 6872114402Sru strcat(s, "|"); 6873114402Sru strcat(s, pc); 6874114402Sru a_delete pipe_command; 6875114402Sru a_delete pc; 6876114402Sru pipe_command = s; 6877114402Sru } 6878114402Sru else 6879114402Sru pipe_command = pc; 6880114402Sru } 6881114402Sru#endif /* not POPEN_MISSING */ 6882114402Sru } 6883114402Sru} 6884114402Sru 6885114402Srustatic int system_status; 6886114402Sru 6887114402Sruvoid system_request() 6888114402Sru{ 6889114402Sru if (safer_flag) { 6890114402Sru error(".sy request not allowed in safer mode"); 6891114402Sru skip_line(); 6892114402Sru } 6893114402Sru else { 6894114402Sru char *command = read_string(); 6895114402Sru if (!command) 6896114402Sru error("empty command"); 6897114402Sru else { 6898114402Sru system_status = system(command); 6899114402Sru a_delete command; 6900114402Sru } 6901114402Sru } 6902114402Sru} 6903114402Sru 6904114402Sruvoid copy_file() 6905114402Sru{ 6906114402Sru if (curdiv == topdiv && topdiv->before_first_page) { 6907114402Sru handle_initial_request(COPY_FILE_REQUEST); 6908114402Sru return; 6909114402Sru } 6910114402Sru symbol filename = get_long_name(1); 6911114402Sru while (!tok.newline() && !tok.eof()) 6912114402Sru tok.next(); 6913114402Sru if (break_flag) 6914114402Sru curenv->do_break(); 6915114402Sru if (!filename.is_null()) 6916114402Sru curdiv->copy_file(filename.contents()); 6917114402Sru tok.next(); 6918114402Sru} 6919114402Sru 6920114402Sru#ifdef COLUMN 6921114402Sru 6922114402Sruvoid vjustify() 6923114402Sru{ 6924114402Sru if (curdiv == topdiv && topdiv->before_first_page) { 6925114402Sru handle_initial_request(VJUSTIFY_REQUEST); 6926114402Sru return; 6927114402Sru } 6928114402Sru symbol type = get_long_name(1); 6929114402Sru if (!type.is_null()) 6930114402Sru curdiv->vjustify(type); 6931114402Sru skip_line(); 6932114402Sru} 6933114402Sru 6934114402Sru#endif /* COLUMN */ 6935114402Sru 6936114402Sruvoid transparent_file() 6937114402Sru{ 6938114402Sru if (curdiv == topdiv && topdiv->before_first_page) { 6939114402Sru handle_initial_request(TRANSPARENT_FILE_REQUEST); 6940114402Sru return; 6941114402Sru } 6942114402Sru symbol filename = get_long_name(1); 6943114402Sru while (!tok.newline() && !tok.eof()) 6944114402Sru tok.next(); 6945114402Sru if (break_flag) 6946114402Sru curenv->do_break(); 6947114402Sru if (!filename.is_null()) { 6948114402Sru errno = 0; 6949151497Sru FILE *fp = include_search_path.open_file_cautious(filename.contents()); 6950114402Sru if (!fp) 6951114402Sru error("can't open `%1': %2", filename.contents(), strerror(errno)); 6952114402Sru else { 6953114402Sru int bol = 1; 6954114402Sru for (;;) { 6955114402Sru int c = getc(fp); 6956114402Sru if (c == EOF) 6957114402Sru break; 6958114402Sru if (invalid_input_char(c)) 6959114402Sru warning(WARN_INPUT, "invalid input character code %1", int(c)); 6960114402Sru else { 6961114402Sru curdiv->transparent_output(c); 6962114402Sru bol = c == '\n'; 6963114402Sru } 6964114402Sru } 6965114402Sru if (!bol) 6966114402Sru curdiv->transparent_output('\n'); 6967114402Sru fclose(fp); 6968114402Sru } 6969114402Sru } 6970114402Sru tok.next(); 6971114402Sru} 6972114402Sru 6973114402Sruclass page_range { 6974114402Sru int first; 6975114402Sru int last; 6976114402Srupublic: 6977114402Sru page_range *next; 6978114402Sru page_range(int, int, page_range *); 6979114402Sru int contains(int n); 6980114402Sru}; 6981114402Sru 6982114402Srupage_range::page_range(int i, int j, page_range *p) 6983114402Sru: first(i), last(j), next(p) 6984114402Sru{ 6985114402Sru} 6986114402Sru 6987114402Sruint page_range::contains(int n) 6988114402Sru{ 6989114402Sru return n >= first && (last <= 0 || n <= last); 6990114402Sru} 6991114402Sru 6992114402Srupage_range *output_page_list = 0; 6993114402Sru 6994114402Sruint in_output_page_list(int n) 6995114402Sru{ 6996114402Sru if (!output_page_list) 6997114402Sru return 1; 6998114402Sru for (page_range *p = output_page_list; p; p = p->next) 6999114402Sru if (p->contains(n)) 7000114402Sru return 1; 7001114402Sru return 0; 7002114402Sru} 7003114402Sru 7004114402Srustatic void parse_output_page_list(char *p) 7005114402Sru{ 7006114402Sru for (;;) { 7007114402Sru int i; 7008114402Sru if (*p == '-') 7009114402Sru i = 1; 7010114402Sru else if (csdigit(*p)) { 7011114402Sru i = 0; 7012114402Sru do 7013114402Sru i = i*10 + *p++ - '0'; 7014114402Sru while (csdigit(*p)); 7015114402Sru } 7016114402Sru else 7017114402Sru break; 7018114402Sru int j; 7019114402Sru if (*p == '-') { 7020114402Sru p++; 7021114402Sru j = 0; 7022114402Sru if (csdigit(*p)) { 7023114402Sru do 7024114402Sru j = j*10 + *p++ - '0'; 7025114402Sru while (csdigit(*p)); 7026114402Sru } 7027114402Sru } 7028114402Sru else 7029114402Sru j = i; 7030114402Sru if (j == 0) 7031114402Sru last_page_number = -1; 7032114402Sru else if (last_page_number >= 0 && j > last_page_number) 7033114402Sru last_page_number = j; 7034114402Sru output_page_list = new page_range(i, j, output_page_list); 7035114402Sru if (*p != ',') 7036114402Sru break; 7037114402Sru ++p; 7038114402Sru } 7039114402Sru if (*p != '\0') { 7040114402Sru error("bad output page list"); 7041114402Sru output_page_list = 0; 7042114402Sru } 7043114402Sru} 7044114402Sru 7045114402Srustatic FILE *open_mac_file(const char *mac, char **path) 7046114402Sru{ 7047114402Sru // Try first FOOBAR.tmac, then tmac.FOOBAR 7048114402Sru char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1]; 7049114402Sru strcpy(s1, mac); 7050114402Sru strcat(s1, MACRO_POSTFIX); 7051114402Sru FILE *fp = mac_path->open_file(s1, path); 7052114402Sru a_delete s1; 7053114402Sru if (!fp) { 7054114402Sru char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1]; 7055114402Sru strcpy(s2, MACRO_PREFIX); 7056114402Sru strcat(s2, mac); 7057114402Sru fp = mac_path->open_file(s2, path); 7058114402Sru a_delete s2; 7059114402Sru } 7060114402Sru return fp; 7061114402Sru} 7062114402Sru 7063114402Srustatic void process_macro_file(const char *mac) 7064114402Sru{ 7065114402Sru char *path; 7066114402Sru FILE *fp = open_mac_file(mac, &path); 7067114402Sru if (!fp) 7068114402Sru fatal("can't find macro file %1", mac); 7069114402Sru const char *s = symbol(path).contents(); 7070114402Sru a_delete path; 7071114402Sru input_stack::push(new file_iterator(fp, s)); 7072114402Sru tok.next(); 7073114402Sru process_input_stack(); 7074114402Sru} 7075114402Sru 7076114402Srustatic void process_startup_file(const char *filename) 7077114402Sru{ 7078114402Sru char *path; 7079114402Sru search_path *orig_mac_path = mac_path; 7080114402Sru mac_path = &config_macro_path; 7081114402Sru FILE *fp = mac_path->open_file(filename, &path); 7082114402Sru if (fp) { 7083114402Sru input_stack::push(new file_iterator(fp, symbol(path).contents())); 7084114402Sru a_delete path; 7085114402Sru tok.next(); 7086114402Sru process_input_stack(); 7087114402Sru } 7088114402Sru mac_path = orig_mac_path; 7089114402Sru} 7090114402Sru 7091114402Sruvoid macro_source() 7092114402Sru{ 7093114402Sru symbol nm = get_long_name(1); 7094114402Sru if (nm.is_null()) 7095114402Sru skip_line(); 7096114402Sru else { 7097114402Sru while (!tok.newline() && !tok.eof()) 7098114402Sru tok.next(); 7099114402Sru char *path; 7100114402Sru FILE *fp = mac_path->open_file(nm.contents(), &path); 7101114402Sru // .mso doesn't (and cannot) go through open_mac_file, so we 7102114402Sru // need to do it here manually: If we have tmac.FOOBAR, try 7103114402Sru // FOOBAR.tmac and vice versa 7104114402Sru if (!fp) { 7105114402Sru const char *fn = nm.contents(); 7106114402Sru if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) { 7107114402Sru char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)]; 7108114402Sru strcpy(s, fn + sizeof(MACRO_PREFIX) - 1); 7109114402Sru strcat(s, MACRO_POSTFIX); 7110114402Sru fp = mac_path->open_file(s, &path); 7111114402Sru a_delete s; 7112114402Sru } 7113114402Sru if (!fp) { 7114114402Sru if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1, 7115114402Sru MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) { 7116114402Sru char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)]; 7117114402Sru strcpy(s, MACRO_PREFIX); 7118114402Sru strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1); 7119114402Sru fp = mac_path->open_file(s, &path); 7120114402Sru a_delete s; 7121114402Sru } 7122114402Sru } 7123114402Sru } 7124114402Sru if (fp) { 7125114402Sru input_stack::push(new file_iterator(fp, symbol(path).contents())); 7126114402Sru a_delete path; 7127114402Sru } 7128114402Sru else 7129114402Sru error("can't find macro file `%1'", nm.contents()); 7130114402Sru tok.next(); 7131114402Sru } 7132114402Sru} 7133114402Sru 7134114402Srustatic void process_input_file(const char *name) 7135114402Sru{ 7136114402Sru FILE *fp; 7137114402Sru if (strcmp(name, "-") == 0) { 7138114402Sru clearerr(stdin); 7139114402Sru fp = stdin; 7140114402Sru } 7141114402Sru else { 7142114402Sru errno = 0; 7143151497Sru fp = include_search_path.open_file_cautious(name); 7144114402Sru if (!fp) 7145114402Sru fatal("can't open `%1': %2", name, strerror(errno)); 7146114402Sru } 7147114402Sru input_stack::push(new file_iterator(fp, name)); 7148114402Sru tok.next(); 7149114402Sru process_input_stack(); 7150114402Sru} 7151114402Sru 7152114402Sru// make sure the_input is empty before calling this 7153114402Sru 7154114402Srustatic int evaluate_expression(const char *expr, units *res) 7155114402Sru{ 7156114402Sru input_stack::push(make_temp_iterator(expr)); 7157114402Sru tok.next(); 7158114402Sru int success = get_number(res, 'u'); 7159114402Sru while (input_stack::get(0) != EOF) 7160114402Sru ; 7161114402Sru return success; 7162114402Sru} 7163114402Sru 7164114402Srustatic void do_register_assignment(const char *s) 7165114402Sru{ 7166114402Sru const char *p = strchr(s, '='); 7167114402Sru if (!p) { 7168114402Sru char buf[2]; 7169114402Sru buf[0] = s[0]; 7170114402Sru buf[1] = 0; 7171114402Sru units n; 7172114402Sru if (evaluate_expression(s + 1, &n)) 7173114402Sru set_number_reg(buf, n); 7174114402Sru } 7175114402Sru else { 7176114402Sru char *buf = new char[p - s + 1]; 7177114402Sru memcpy(buf, s, p - s); 7178114402Sru buf[p - s] = 0; 7179114402Sru units n; 7180114402Sru if (evaluate_expression(p + 1, &n)) 7181114402Sru set_number_reg(buf, n); 7182114402Sru a_delete buf; 7183114402Sru } 7184114402Sru} 7185114402Sru 7186114402Srustatic void set_string(const char *name, const char *value) 7187114402Sru{ 7188114402Sru macro *m = new macro; 7189114402Sru for (const char *p = value; *p; p++) 7190114402Sru if (!invalid_input_char((unsigned char)*p)) 7191114402Sru m->append(*p); 7192114402Sru request_dictionary.define(name, m); 7193114402Sru} 7194114402Sru 7195114402Srustatic void do_string_assignment(const char *s) 7196114402Sru{ 7197114402Sru const char *p = strchr(s, '='); 7198114402Sru if (!p) { 7199114402Sru char buf[2]; 7200114402Sru buf[0] = s[0]; 7201114402Sru buf[1] = 0; 7202114402Sru set_string(buf, s + 1); 7203114402Sru } 7204114402Sru else { 7205114402Sru char *buf = new char[p - s + 1]; 7206114402Sru memcpy(buf, s, p - s); 7207114402Sru buf[p - s] = 0; 7208114402Sru set_string(buf, p + 1); 7209114402Sru a_delete buf; 7210114402Sru } 7211114402Sru} 7212114402Sru 7213114402Srustruct string_list { 7214114402Sru const char *s; 7215114402Sru string_list *next; 7216114402Sru string_list(const char *ss) : s(ss), next(0) {} 7217114402Sru}; 7218114402Sru 7219114402Sru#if 0 7220114402Srustatic void prepend_string(const char *s, string_list **p) 7221114402Sru{ 7222114402Sru string_list *l = new string_list(s); 7223114402Sru l->next = *p; 7224114402Sru *p = l; 7225114402Sru} 7226114402Sru#endif 7227114402Sru 7228114402Srustatic void add_string(const char *s, string_list **p) 7229114402Sru{ 7230114402Sru while (*p) 7231114402Sru p = &((*p)->next); 7232114402Sru *p = new string_list(s); 7233114402Sru} 7234114402Sru 7235114402Sruvoid usage(FILE *stream, const char *prog) 7236114402Sru{ 7237114402Sru fprintf(stream, 7238114402Sru"usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" 7239151497Sru" -rcn -Tname -Fdir -Idir -Mdir [files...]\n", 7240114402Sru prog); 7241114402Sru} 7242114402Sru 7243114402Sruint main(int argc, char **argv) 7244114402Sru{ 7245114402Sru program_name = argv[0]; 7246114402Sru static char stderr_buf[BUFSIZ]; 7247114402Sru setbuf(stderr, stderr_buf); 7248114402Sru int c; 7249114402Sru string_list *macros = 0; 7250114402Sru string_list *register_assignments = 0; 7251114402Sru string_list *string_assignments = 0; 7252114402Sru int iflag = 0; 7253114402Sru int tflag = 0; 7254114402Sru int fflag = 0; 7255114402Sru int nflag = 0; 7256114402Sru int no_rc = 0; // don't process troffrc and troffrc-end 7257151497Sru int next_page_number = 0; // pacify compiler 7258114402Sru opterr = 0; 7259114402Sru hresolution = vresolution = 1; 7260114402Sru // restore $PATH if called from groff 7261114402Sru char* groff_path = getenv("GROFF_PATH__"); 7262114402Sru if (groff_path) { 7263114402Sru string e = "PATH"; 7264114402Sru e += '='; 7265114402Sru if (*groff_path) 7266114402Sru e += groff_path; 7267114402Sru e += '\0'; 7268114402Sru if (putenv(strsave(e.contents()))) 7269114402Sru fatal("putenv failed"); 7270114402Sru } 7271114402Sru static const struct option long_options[] = { 7272114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 7273114402Sru { "version", no_argument, 0, 'v' }, 7274114402Sru { 0, 0, 0, 0 } 7275114402Sru }; 7276151497Sru#if defined(DEBUGGING) 7277151497Sru#define DEBUG_OPTION "D" 7278151497Sru#endif 7279151497Sru while ((c = getopt_long(argc, argv, 7280151497Sru "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU" 7281151497Sru DEBUG_OPTION, long_options, 0)) 7282114402Sru != EOF) 7283114402Sru switch(c) { 7284114402Sru case 'v': 7285114402Sru { 7286114402Sru printf("GNU troff (groff) version %s\n", Version_string); 7287114402Sru exit(0); 7288114402Sru break; 7289114402Sru } 7290151497Sru case 'I': 7291151497Sru // Search path for .psbb files 7292151497Sru // and most other non-system input files. 7293151497Sru include_search_path.command_line_dir(optarg); 7294151497Sru break; 7295114402Sru case 'T': 7296114402Sru device = optarg; 7297114402Sru tflag = 1; 7298114402Sru is_html = (strcmp(device, "html") == 0); 7299114402Sru break; 7300114402Sru case 'C': 7301114402Sru compatible_flag = 1; 7302114402Sru // fall through 7303114402Sru case 'c': 7304114402Sru color_flag = 0; 7305114402Sru break; 7306114402Sru case 'M': 7307114402Sru macro_path.command_line_dir(optarg); 7308114402Sru safer_macro_path.command_line_dir(optarg); 7309114402Sru config_macro_path.command_line_dir(optarg); 7310114402Sru break; 7311114402Sru case 'F': 7312114402Sru font::command_line_font_dir(optarg); 7313114402Sru break; 7314114402Sru case 'm': 7315114402Sru add_string(optarg, ¯os); 7316114402Sru break; 7317114402Sru case 'E': 7318114402Sru inhibit_errors = 1; 7319114402Sru break; 7320114402Sru case 'R': 7321114402Sru no_rc = 1; 7322114402Sru break; 7323114402Sru case 'w': 7324114402Sru enable_warning(optarg); 7325114402Sru break; 7326114402Sru case 'W': 7327114402Sru disable_warning(optarg); 7328114402Sru break; 7329114402Sru case 'i': 7330114402Sru iflag = 1; 7331114402Sru break; 7332114402Sru case 'b': 7333114402Sru backtrace_flag = 1; 7334114402Sru break; 7335114402Sru case 'a': 7336114402Sru ascii_output_flag = 1; 7337114402Sru break; 7338114402Sru case 'z': 7339114402Sru suppress_output_flag = 1; 7340114402Sru break; 7341114402Sru case 'n': 7342114402Sru if (sscanf(optarg, "%d", &next_page_number) == 1) 7343114402Sru nflag++; 7344114402Sru else 7345114402Sru error("bad page number"); 7346114402Sru break; 7347114402Sru case 'o': 7348114402Sru parse_output_page_list(optarg); 7349114402Sru break; 7350114402Sru case 'd': 7351114402Sru if (*optarg == '\0') 7352114402Sru error("`-d' requires non-empty argument"); 7353114402Sru else 7354114402Sru add_string(optarg, &string_assignments); 7355114402Sru break; 7356114402Sru case 'r': 7357114402Sru if (*optarg == '\0') 7358114402Sru error("`-r' requires non-empty argument"); 7359114402Sru else 7360114402Sru add_string(optarg, ®ister_assignments); 7361114402Sru break; 7362114402Sru case 'f': 7363114402Sru default_family = symbol(optarg); 7364114402Sru fflag = 1; 7365114402Sru break; 7366114402Sru case 'q': 7367114402Sru case 's': 7368114402Sru case 't': 7369114402Sru // silently ignore these 7370114402Sru break; 7371114402Sru case 'U': 7372114402Sru safer_flag = 0; // unsafe behaviour 7373114402Sru break; 7374151497Sru#if defined(DEBUGGING) 7375151497Sru case 'D': 7376151497Sru debug_state = 1; 7377151497Sru break; 7378151497Sru#endif 7379114402Sru case CHAR_MAX + 1: // --help 7380114402Sru usage(stdout, argv[0]); 7381114402Sru exit(0); 7382114402Sru break; 7383114402Sru case '?': 7384114402Sru usage(stderr, argv[0]); 7385114402Sru exit(1); 7386114402Sru break; // never reached 7387114402Sru default: 7388114402Sru assert(0); 7389114402Sru } 7390114402Sru if (!safer_flag) 7391114402Sru mac_path = ¯o_path; 7392114402Sru set_string(".T", device); 7393114402Sru init_charset_table(); 7394114402Sru init_hpf_code_table(); 7395114402Sru if (!font::load_desc()) 7396114402Sru fatal("sorry, I can't continue"); 7397114402Sru units_per_inch = font::res; 7398114402Sru hresolution = font::hor; 7399114402Sru vresolution = font::vert; 7400114402Sru sizescale = font::sizescale; 7401114402Sru tcommand_flag = font::tcommand; 7402114402Sru warn_scale = (double)units_per_inch; 7403114402Sru warn_scaling_indicator = 'i'; 7404114402Sru if (!fflag && font::family != 0 && *font::family != '\0') 7405114402Sru default_family = symbol(font::family); 7406114402Sru font_size::init_size_table(font::sizes); 7407114402Sru int i; 7408114402Sru int j = 1; 7409114402Sru if (font::style_table) { 7410114402Sru for (i = 0; font::style_table[i]; i++) 7411114402Sru mount_style(j++, symbol(font::style_table[i])); 7412114402Sru } 7413114402Sru for (i = 0; font::font_name_table[i]; i++, j++) 7414114402Sru // In the DESC file a font name of 0 (zero) means leave this 7415114402Sru // position empty. 7416114402Sru if (strcmp(font::font_name_table[i], "0") != 0) 7417114402Sru mount_font(j, symbol(font::font_name_table[i])); 7418114402Sru curdiv = topdiv = new top_level_diversion; 7419114402Sru if (nflag) 7420114402Sru topdiv->set_next_page_number(next_page_number); 7421114402Sru init_input_requests(); 7422114402Sru init_env_requests(); 7423114402Sru init_div_requests(); 7424114402Sru#ifdef COLUMN 7425114402Sru init_column_requests(); 7426114402Sru#endif /* COLUMN */ 7427114402Sru init_node_requests(); 7428114402Sru number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); 7429114402Sru init_registers(); 7430114402Sru init_reg_requests(); 7431114402Sru init_hyphen_requests(); 7432114402Sru init_environments(); 7433114402Sru while (string_assignments) { 7434114402Sru do_string_assignment(string_assignments->s); 7435114402Sru string_list *tem = string_assignments; 7436114402Sru string_assignments = string_assignments->next; 7437114402Sru delete tem; 7438114402Sru } 7439114402Sru while (register_assignments) { 7440114402Sru do_register_assignment(register_assignments->s); 7441114402Sru string_list *tem = register_assignments; 7442114402Sru register_assignments = register_assignments->next; 7443114402Sru delete tem; 7444114402Sru } 7445114402Sru if (!no_rc) 7446114402Sru process_startup_file(INITIAL_STARTUP_FILE); 7447114402Sru while (macros) { 7448114402Sru process_macro_file(macros->s); 7449114402Sru string_list *tem = macros; 7450114402Sru macros = macros->next; 7451114402Sru delete tem; 7452114402Sru } 7453114402Sru if (!no_rc) 7454114402Sru process_startup_file(FINAL_STARTUP_FILE); 7455114402Sru for (i = optind; i < argc; i++) 7456114402Sru process_input_file(argv[i]); 7457114402Sru if (optind >= argc || iflag) 7458114402Sru process_input_file("-"); 7459114402Sru exit_troff(); 7460114402Sru return 0; // not reached 7461114402Sru} 7462114402Sru 7463114402Sruvoid warn_request() 7464114402Sru{ 7465114402Sru int n; 7466114402Sru if (has_arg() && get_integer(&n)) { 7467114402Sru if (n & ~WARN_TOTAL) { 7468114402Sru warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL); 7469114402Sru n &= WARN_TOTAL; 7470114402Sru } 7471114402Sru warning_mask = n; 7472114402Sru } 7473114402Sru else 7474114402Sru warning_mask = WARN_TOTAL; 7475114402Sru skip_line(); 7476114402Sru} 7477114402Sru 7478114402Srustatic void init_registers() 7479114402Sru{ 7480114402Sru#ifdef LONG_FOR_TIME_T 7481114402Sru long 7482114402Sru#else /* not LONG_FOR_TIME_T */ 7483114402Sru time_t 7484114402Sru#endif /* not LONG_FOR_TIME_T */ 7485114402Sru t = time(0); 7486114402Sru // Use struct here to work around misfeature in old versions of g++. 7487114402Sru struct tm *tt = localtime(&t); 7488114402Sru set_number_reg("seconds", int(tt->tm_sec)); 7489114402Sru set_number_reg("minutes", int(tt->tm_min)); 7490114402Sru set_number_reg("hours", int(tt->tm_hour)); 7491114402Sru set_number_reg("dw", int(tt->tm_wday + 1)); 7492114402Sru set_number_reg("dy", int(tt->tm_mday)); 7493114402Sru set_number_reg("mo", int(tt->tm_mon + 1)); 7494114402Sru set_number_reg("year", int(1900 + tt->tm_year)); 7495114402Sru set_number_reg("yr", int(tt->tm_year)); 7496114402Sru set_number_reg("$$", getpid()); 7497114402Sru number_reg_dictionary.define(".A", 7498114402Sru new constant_reg(ascii_output_flag 7499114402Sru ? "1" 7500114402Sru : "0")); 7501114402Sru} 7502114402Sru 7503114402Sru/* 7504114402Sru * registers associated with \O 7505114402Sru */ 7506114402Sru 7507114402Srustatic int output_reg_minx_contents = -1; 7508114402Srustatic int output_reg_miny_contents = -1; 7509114402Srustatic int output_reg_maxx_contents = -1; 7510114402Srustatic int output_reg_maxy_contents = -1; 7511114402Sru 7512114402Sruvoid check_output_limits(int x, int y) 7513114402Sru{ 7514114402Sru if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) 7515114402Sru output_reg_minx_contents = x; 7516114402Sru if (x > output_reg_maxx_contents) 7517114402Sru output_reg_maxx_contents = x; 7518114402Sru if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) 7519114402Sru output_reg_miny_contents = y; 7520114402Sru if (y > output_reg_maxy_contents) 7521114402Sru output_reg_maxy_contents = y; 7522114402Sru} 7523114402Sru 7524114402Sruvoid reset_output_registers() 7525114402Sru{ 7526114402Sru output_reg_minx_contents = -1; 7527114402Sru output_reg_miny_contents = -1; 7528114402Sru output_reg_maxx_contents = -1; 7529114402Sru output_reg_maxy_contents = -1; 7530114402Sru} 7531114402Sru 7532114402Sruvoid get_output_registers(int *minx, int *miny, int *maxx, int *maxy) 7533114402Sru{ 7534114402Sru *minx = output_reg_minx_contents; 7535114402Sru *miny = output_reg_miny_contents; 7536114402Sru *maxx = output_reg_maxx_contents; 7537114402Sru *maxy = output_reg_maxy_contents; 7538114402Sru} 7539114402Sru 7540114402Sruvoid init_input_requests() 7541114402Sru{ 7542114402Sru init_request("ab", abort_request); 7543114402Sru init_request("als", alias_macro); 7544114402Sru init_request("am", append_macro); 7545114402Sru init_request("am1", append_nocomp_macro); 7546114402Sru init_request("ami", append_indirect_macro); 7547151497Sru init_request("ami1", append_indirect_nocomp_macro); 7548114402Sru init_request("as", append_string); 7549114402Sru init_request("as1", append_nocomp_string); 7550114402Sru init_request("asciify", asciify_macro); 7551114402Sru init_request("backtrace", backtrace_request); 7552114402Sru init_request("blm", blank_line_macro); 7553114402Sru init_request("break", while_break_request); 7554114402Sru init_request("cf", copy_file); 7555114402Sru init_request("cflags", char_flags); 7556114402Sru init_request("char", define_character); 7557114402Sru init_request("chop", chop_macro); 7558114402Sru init_request("close", close_request); 7559114402Sru init_request("color", activate_color); 7560114402Sru init_request("composite", composite_request); 7561114402Sru init_request("continue", while_continue_request); 7562114402Sru init_request("cp", compatible); 7563114402Sru init_request("de", define_macro); 7564114402Sru init_request("de1", define_nocomp_macro); 7565114402Sru init_request("defcolor", define_color); 7566114402Sru init_request("dei", define_indirect_macro); 7567151497Sru init_request("dei1", define_indirect_nocomp_macro); 7568114402Sru init_request("do", do_request); 7569114402Sru init_request("ds", define_string); 7570114402Sru init_request("ds1", define_nocomp_string); 7571114402Sru init_request("ec", set_escape_char); 7572114402Sru init_request("ecr", restore_escape_char); 7573114402Sru init_request("ecs", save_escape_char); 7574114402Sru init_request("el", else_request); 7575114402Sru init_request("em", end_macro); 7576114402Sru init_request("eo", escape_off); 7577114402Sru init_request("ex", exit_request); 7578114402Sru init_request("fchar", define_fallback_character); 7579114402Sru#ifdef WIDOW_CONTROL 7580114402Sru init_request("fpl", flush_pending_lines); 7581114402Sru#endif /* WIDOW_CONTROL */ 7582114402Sru init_request("hcode", hyphenation_code); 7583114402Sru init_request("hpfcode", hyphenation_patterns_file_code); 7584114402Sru init_request("ie", if_else_request); 7585114402Sru init_request("if", if_request); 7586114402Sru init_request("ig", ignore); 7587114402Sru init_request("length", length_request); 7588114402Sru init_request("lf", line_file); 7589114402Sru init_request("mso", macro_source); 7590114402Sru init_request("nop", nop_request); 7591114402Sru init_request("nroff", nroff_request); 7592114402Sru init_request("nx", next_file); 7593114402Sru init_request("open", open_request); 7594114402Sru init_request("opena", opena_request); 7595114402Sru init_request("output", output_request); 7596114402Sru init_request("pc", set_page_character); 7597114402Sru init_request("pi", pipe_output); 7598114402Sru init_request("pm", print_macros); 7599114402Sru init_request("psbb", ps_bbox_request); 7600114402Sru#ifndef POPEN_MISSING 7601114402Sru init_request("pso", pipe_source); 7602114402Sru#endif /* not POPEN_MISSING */ 7603114402Sru init_request("rchar", remove_character); 7604114402Sru init_request("rd", read_request); 7605114402Sru init_request("return", return_macro_request); 7606114402Sru init_request("rm", remove_macro); 7607114402Sru init_request("rn", rename_macro); 7608114402Sru init_request("schar", define_special_character); 7609114402Sru init_request("shift", shift); 7610114402Sru init_request("so", source); 7611114402Sru init_request("spreadwarn", spreadwarn_request); 7612114402Sru init_request("substring", substring_request); 7613114402Sru init_request("sy", system_request); 7614151497Sru init_request("tag", tag); 7615151497Sru init_request("taga", taga); 7616114402Sru init_request("tm", terminal); 7617114402Sru init_request("tm1", terminal1); 7618114402Sru init_request("tmc", terminal_continue); 7619114402Sru init_request("tr", translate); 7620114402Sru init_request("trf", transparent_file); 7621114402Sru init_request("trin", translate_input); 7622114402Sru init_request("trnt", translate_no_transparent); 7623114402Sru init_request("troff", troff_request); 7624114402Sru init_request("unformat", unformat_macro); 7625114402Sru#ifdef COLUMN 7626114402Sru init_request("vj", vjustify); 7627114402Sru#endif /* COLUMN */ 7628114402Sru init_request("warn", warn_request); 7629114402Sru init_request("warnscale", warnscale_request); 7630114402Sru init_request("while", while_request); 7631114402Sru init_request("write", write_request); 7632114402Sru init_request("writec", write_request_continue); 7633114402Sru init_request("writem", write_macro_request); 7634114402Sru number_reg_dictionary.define(".$", new nargs_reg); 7635114402Sru number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag)); 7636114402Sru number_reg_dictionary.define(".c", new lineno_reg); 7637114402Sru number_reg_dictionary.define(".color", new constant_int_reg(&color_flag)); 7638114402Sru number_reg_dictionary.define(".F", new filename_reg); 7639114402Sru number_reg_dictionary.define(".g", new constant_reg("1")); 7640114402Sru number_reg_dictionary.define(".H", new constant_int_reg(&hresolution)); 7641114402Sru number_reg_dictionary.define(".R", new constant_reg("10000")); 7642151497Sru number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag)); 7643114402Sru number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); 7644114402Sru number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask)); 7645114402Sru extern const char *major_version; 7646114402Sru number_reg_dictionary.define(".x", new constant_reg(major_version)); 7647114402Sru extern const char *revision; 7648114402Sru number_reg_dictionary.define(".Y", new constant_reg(revision)); 7649114402Sru extern const char *minor_version; 7650114402Sru number_reg_dictionary.define(".y", new constant_reg(minor_version)); 7651114402Sru number_reg_dictionary.define("c.", new writable_lineno_reg); 7652114402Sru number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents)); 7653114402Sru number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents)); 7654114402Sru number_reg_dictionary.define("opmaxx", 7655114402Sru new variable_reg(&output_reg_maxx_contents)); 7656114402Sru number_reg_dictionary.define("opmaxy", 7657114402Sru new variable_reg(&output_reg_maxy_contents)); 7658114402Sru number_reg_dictionary.define("opminx", 7659114402Sru new variable_reg(&output_reg_minx_contents)); 7660114402Sru number_reg_dictionary.define("opminy", 7661114402Sru new variable_reg(&output_reg_miny_contents)); 7662114402Sru number_reg_dictionary.define("slimit", 7663114402Sru new variable_reg(&input_stack::limit)); 7664114402Sru number_reg_dictionary.define("systat", new variable_reg(&system_status)); 7665114402Sru number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents)); 7666114402Sru number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents)); 7667114402Sru} 7668114402Sru 7669114402Sruobject_dictionary request_dictionary(501); 7670114402Sru 7671114402Sruvoid init_request(const char *s, REQUEST_FUNCP f) 7672114402Sru{ 7673114402Sru request_dictionary.define(s, new request(f)); 7674114402Sru} 7675114402Sru 7676114402Srustatic request_or_macro *lookup_request(symbol nm) 7677114402Sru{ 7678114402Sru assert(!nm.is_null()); 7679114402Sru request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); 7680114402Sru if (p == 0) { 7681114402Sru warning(WARN_MAC, "macro `%1' not defined", nm.contents()); 7682114402Sru p = new macro; 7683114402Sru request_dictionary.define(nm, p); 7684114402Sru } 7685114402Sru return p; 7686114402Sru} 7687114402Sru 7688114402Srunode *charinfo_to_node_list(charinfo *ci, const environment *envp) 7689114402Sru{ 7690114402Sru // Don't interpret character definitions in compatible mode. 7691114402Sru int old_compatible_flag = compatible_flag; 7692114402Sru compatible_flag = 0; 7693114402Sru int old_escape_char = escape_char; 7694114402Sru escape_char = '\\'; 7695114402Sru macro *mac = ci->set_macro(0); 7696114402Sru assert(mac != 0); 7697114402Sru environment *oldenv = curenv; 7698114402Sru environment env(envp); 7699114402Sru curenv = &env; 7700114402Sru curenv->set_composite(); 7701114402Sru token old_tok = tok; 7702114402Sru input_stack::add_boundary(); 7703114402Sru string_iterator *si = 7704114402Sru new string_iterator(*mac, "composite character", ci->nm); 7705114402Sru input_stack::push(si); 7706114402Sru // we don't use process_input_stack, because we don't want to recognise 7707114402Sru // requests 7708114402Sru for (;;) { 7709114402Sru tok.next(); 7710114402Sru if (tok.eof()) 7711114402Sru break; 7712114402Sru if (tok.newline()) { 7713114402Sru error("composite character mustn't contain newline"); 7714114402Sru while (!tok.eof()) 7715114402Sru tok.next(); 7716114402Sru break; 7717114402Sru } 7718114402Sru else 7719114402Sru tok.process(); 7720114402Sru } 7721114402Sru node *n = curenv->extract_output_line(); 7722114402Sru input_stack::remove_boundary(); 7723114402Sru ci->set_macro(mac); 7724114402Sru tok = old_tok; 7725114402Sru curenv = oldenv; 7726114402Sru compatible_flag = old_compatible_flag; 7727114402Sru escape_char = old_escape_char; 7728114402Sru have_input = 0; 7729114402Sru return n; 7730114402Sru} 7731114402Sru 7732114402Srustatic node *read_draw_node() 7733114402Sru{ 7734114402Sru token start; 7735114402Sru start.next(); 7736114402Sru if (!start.delimiter(1)){ 7737114402Sru do { 7738114402Sru tok.next(); 7739114402Sru } while (tok != start && !tok.newline() && !tok.eof()); 7740114402Sru } 7741114402Sru else { 7742114402Sru tok.next(); 7743114402Sru if (tok == start) 7744114402Sru error("missing argument"); 7745114402Sru else { 7746114402Sru unsigned char type = tok.ch(); 7747114402Sru if (type == 'F') { 7748114402Sru read_color_draw_node(start); 7749114402Sru return 0; 7750114402Sru } 7751114402Sru tok.next(); 7752114402Sru int maxpoints = 10; 7753114402Sru hvpair *point = new hvpair[maxpoints]; 7754114402Sru int npoints = 0; 7755114402Sru int no_last_v = 0; 7756114402Sru int err = 0; 7757114402Sru int i; 7758114402Sru for (i = 0; tok != start; i++) { 7759114402Sru if (i == maxpoints) { 7760114402Sru hvpair *oldpoint = point; 7761114402Sru point = new hvpair[maxpoints*2]; 7762114402Sru for (int j = 0; j < maxpoints; j++) 7763114402Sru point[j] = oldpoint[j]; 7764114402Sru maxpoints *= 2; 7765114402Sru a_delete oldpoint; 7766114402Sru } 7767114402Sru if (!get_hunits(&point[i].h, 7768114402Sru type == 'f' || type == 't' ? 'u' : 'm')) { 7769114402Sru err = 1; 7770114402Sru break; 7771114402Sru } 7772114402Sru ++npoints; 7773114402Sru tok.skip(); 7774114402Sru point[i].v = V0; 7775114402Sru if (tok == start) { 7776114402Sru no_last_v = 1; 7777114402Sru break; 7778114402Sru } 7779114402Sru if (!get_vunits(&point[i].v, 'v')) { 7780114402Sru err = 1; 7781114402Sru break; 7782114402Sru } 7783114402Sru tok.skip(); 7784114402Sru } 7785114402Sru while (tok != start && !tok.newline() && !tok.eof()) 7786114402Sru tok.next(); 7787114402Sru if (!err) { 7788114402Sru switch (type) { 7789114402Sru case 'l': 7790114402Sru if (npoints != 1 || no_last_v) { 7791114402Sru error("two arguments needed for line"); 7792114402Sru npoints = 1; 7793114402Sru } 7794114402Sru break; 7795114402Sru case 'c': 7796114402Sru if (npoints != 1 || !no_last_v) { 7797114402Sru error("one argument needed for circle"); 7798114402Sru npoints = 1; 7799114402Sru point[0].v = V0; 7800114402Sru } 7801114402Sru break; 7802114402Sru case 'e': 7803114402Sru if (npoints != 1 || no_last_v) { 7804114402Sru error("two arguments needed for ellipse"); 7805114402Sru npoints = 1; 7806114402Sru } 7807114402Sru break; 7808114402Sru case 'a': 7809114402Sru if (npoints != 2 || no_last_v) { 7810114402Sru error("four arguments needed for arc"); 7811114402Sru npoints = 2; 7812114402Sru } 7813114402Sru break; 7814114402Sru case '~': 7815114402Sru if (no_last_v) 7816114402Sru error("even number of arguments needed for spline"); 7817114402Sru break; 7818114402Sru case 'f': 7819114402Sru if (npoints != 1 || !no_last_v) { 7820114402Sru error("one argument needed for gray shade"); 7821114402Sru npoints = 1; 7822114402Sru point[0].v = V0; 7823114402Sru } 7824114402Sru default: 7825114402Sru // silently pass it through 7826114402Sru break; 7827114402Sru } 7828114402Sru draw_node *dn = new draw_node(type, point, npoints, 7829114402Sru curenv->get_font_size(), 7830114402Sru curenv->get_glyph_color(), 7831114402Sru curenv->get_fill_color()); 7832114402Sru a_delete point; 7833114402Sru return dn; 7834114402Sru } 7835114402Sru else { 7836114402Sru a_delete point; 7837114402Sru } 7838114402Sru } 7839114402Sru } 7840114402Sru return 0; 7841114402Sru} 7842114402Sru 7843114402Srustatic void read_color_draw_node(token &start) 7844114402Sru{ 7845114402Sru tok.next(); 7846114402Sru if (tok == start) { 7847114402Sru error("missing color scheme"); 7848114402Sru return; 7849114402Sru } 7850114402Sru unsigned char scheme = tok.ch(); 7851114402Sru tok.next(); 7852151497Sru color *col = 0; 7853114402Sru char end = start.ch(); 7854114402Sru switch (scheme) { 7855114402Sru case 'c': 7856114402Sru col = read_cmy(end); 7857114402Sru break; 7858114402Sru case 'd': 7859114402Sru col = &default_color; 7860114402Sru break; 7861114402Sru case 'g': 7862114402Sru col = read_gray(end); 7863114402Sru break; 7864114402Sru case 'k': 7865114402Sru col = read_cmyk(end); 7866114402Sru break; 7867114402Sru case 'r': 7868114402Sru col = read_rgb(end); 7869114402Sru break; 7870114402Sru } 7871114402Sru if (col) 7872114402Sru curenv->set_fill_color(col); 7873114402Sru while (tok != start) { 7874114402Sru if (tok.newline() || tok.eof()) { 7875114402Sru warning(WARN_DELIM, "missing closing delimiter"); 7876114402Sru input_stack::push(make_temp_iterator("\n")); 7877114402Sru break; 7878114402Sru } 7879114402Sru tok.next(); 7880114402Sru } 7881114402Sru have_input = 1; 7882114402Sru} 7883114402Sru 7884114402Srustatic struct { 7885114402Sru const char *name; 7886114402Sru int mask; 7887114402Sru} warning_table[] = { 7888114402Sru { "char", WARN_CHAR }, 7889114402Sru { "range", WARN_RANGE }, 7890114402Sru { "break", WARN_BREAK }, 7891114402Sru { "delim", WARN_DELIM }, 7892114402Sru { "el", WARN_EL }, 7893114402Sru { "scale", WARN_SCALE }, 7894114402Sru { "number", WARN_NUMBER }, 7895114402Sru { "syntax", WARN_SYNTAX }, 7896114402Sru { "tab", WARN_TAB }, 7897114402Sru { "right-brace", WARN_RIGHT_BRACE }, 7898114402Sru { "missing", WARN_MISSING }, 7899114402Sru { "input", WARN_INPUT }, 7900114402Sru { "escape", WARN_ESCAPE }, 7901114402Sru { "space", WARN_SPACE }, 7902114402Sru { "font", WARN_FONT }, 7903114402Sru { "di", WARN_DI }, 7904114402Sru { "mac", WARN_MAC }, 7905114402Sru { "reg", WARN_REG }, 7906114402Sru { "ig", WARN_IG }, 7907114402Sru { "color", WARN_COLOR }, 7908114402Sru { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) }, 7909114402Sru { "w", WARN_TOTAL }, 7910114402Sru { "default", DEFAULT_WARNING_MASK }, 7911114402Sru}; 7912114402Sru 7913114402Srustatic int lookup_warning(const char *name) 7914114402Sru{ 7915114402Sru for (unsigned int i = 0; 7916114402Sru i < sizeof(warning_table)/sizeof(warning_table[0]); 7917114402Sru i++) 7918114402Sru if (strcmp(name, warning_table[i].name) == 0) 7919114402Sru return warning_table[i].mask; 7920114402Sru return 0; 7921114402Sru} 7922114402Sru 7923114402Srustatic void enable_warning(const char *name) 7924114402Sru{ 7925114402Sru int mask = lookup_warning(name); 7926114402Sru if (mask) 7927114402Sru warning_mask |= mask; 7928114402Sru else 7929114402Sru error("unknown warning `%1'", name); 7930114402Sru} 7931114402Sru 7932114402Srustatic void disable_warning(const char *name) 7933114402Sru{ 7934114402Sru int mask = lookup_warning(name); 7935114402Sru if (mask) 7936114402Sru warning_mask &= ~mask; 7937114402Sru else 7938114402Sru error("unknown warning `%1'", name); 7939114402Sru} 7940114402Sru 7941114402Srustatic void copy_mode_error(const char *format, 7942114402Sru const errarg &arg1, 7943114402Sru const errarg &arg2, 7944114402Sru const errarg &arg3) 7945114402Sru{ 7946114402Sru if (ignoring) { 7947114402Sru static const char prefix[] = "(in ignored input) "; 7948114402Sru char *s = new char[sizeof(prefix) + strlen(format)]; 7949114402Sru strcpy(s, prefix); 7950114402Sru strcat(s, format); 7951114402Sru warning(WARN_IG, s, arg1, arg2, arg3); 7952114402Sru a_delete s; 7953114402Sru } 7954114402Sru else 7955114402Sru error(format, arg1, arg2, arg3); 7956114402Sru} 7957114402Sru 7958114402Sruenum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL }; 7959114402Sru 7960114402Srustatic void do_error(error_type type, 7961114402Sru const char *format, 7962114402Sru const errarg &arg1, 7963114402Sru const errarg &arg2, 7964114402Sru const errarg &arg3) 7965114402Sru{ 7966114402Sru const char *filename; 7967114402Sru int lineno; 7968114402Sru if (inhibit_errors && type < FATAL) 7969114402Sru return; 7970114402Sru if (backtrace_flag) 7971114402Sru input_stack::backtrace(); 7972114402Sru if (!get_file_line(&filename, &lineno)) 7973114402Sru filename = 0; 7974114402Sru if (filename) 7975114402Sru errprint("%1:%2: ", filename, lineno); 7976114402Sru else if (program_name) 7977114402Sru fprintf(stderr, "%s: ", program_name); 7978114402Sru switch (type) { 7979114402Sru case FATAL: 7980114402Sru fputs("fatal error: ", stderr); 7981114402Sru break; 7982114402Sru case ERROR: 7983114402Sru break; 7984114402Sru case WARNING: 7985114402Sru fputs("warning: ", stderr); 7986114402Sru break; 7987114402Sru case OUTPUT_WARNING: 7988114402Sru double fromtop = topdiv->get_vertical_position().to_units() / warn_scale; 7989114402Sru fprintf(stderr, "warning [p %d, %.1f%c", 7990114402Sru topdiv->get_page_number(), fromtop, warn_scaling_indicator); 7991114402Sru if (topdiv != curdiv) { 7992114402Sru double fromtop1 = curdiv->get_vertical_position().to_units() 7993114402Sru / warn_scale; 7994114402Sru fprintf(stderr, ", div `%s', %.1f%c", 7995114402Sru curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator); 7996114402Sru } 7997114402Sru fprintf(stderr, "]: "); 7998114402Sru break; 7999114402Sru } 8000114402Sru errprint(format, arg1, arg2, arg3); 8001114402Sru fputc('\n', stderr); 8002114402Sru fflush(stderr); 8003114402Sru if (type == FATAL) 8004114402Sru cleanup_and_exit(1); 8005114402Sru} 8006114402Sru 8007114402Sruint warning(warning_type t, 8008114402Sru const char *format, 8009114402Sru const errarg &arg1, 8010114402Sru const errarg &arg2, 8011114402Sru const errarg &arg3) 8012114402Sru{ 8013114402Sru if ((t & warning_mask) != 0) { 8014114402Sru do_error(WARNING, format, arg1, arg2, arg3); 8015114402Sru return 1; 8016114402Sru } 8017114402Sru else 8018114402Sru return 0; 8019114402Sru} 8020114402Sru 8021114402Sruint output_warning(warning_type t, 8022114402Sru const char *format, 8023114402Sru const errarg &arg1, 8024114402Sru const errarg &arg2, 8025114402Sru const errarg &arg3) 8026114402Sru{ 8027114402Sru if ((t & warning_mask) != 0) { 8028114402Sru do_error(OUTPUT_WARNING, format, arg1, arg2, arg3); 8029114402Sru return 1; 8030114402Sru } 8031114402Sru else 8032114402Sru return 0; 8033114402Sru} 8034114402Sru 8035114402Sruvoid error(const char *format, 8036114402Sru const errarg &arg1, 8037114402Sru const errarg &arg2, 8038114402Sru const errarg &arg3) 8039114402Sru{ 8040114402Sru do_error(ERROR, format, arg1, arg2, arg3); 8041114402Sru} 8042114402Sru 8043114402Sruvoid fatal(const char *format, 8044114402Sru const errarg &arg1, 8045114402Sru const errarg &arg2, 8046114402Sru const errarg &arg3) 8047114402Sru{ 8048114402Sru do_error(FATAL, format, arg1, arg2, arg3); 8049114402Sru} 8050114402Sru 8051114402Sruvoid fatal_with_file_and_line(const char *filename, int lineno, 8052114402Sru const char *format, 8053114402Sru const errarg &arg1, 8054114402Sru const errarg &arg2, 8055114402Sru const errarg &arg3) 8056114402Sru{ 8057114402Sru fprintf(stderr, "%s:%d: fatal error: ", filename, lineno); 8058114402Sru errprint(format, arg1, arg2, arg3); 8059114402Sru fputc('\n', stderr); 8060114402Sru fflush(stderr); 8061114402Sru cleanup_and_exit(1); 8062114402Sru} 8063114402Sru 8064114402Sruvoid error_with_file_and_line(const char *filename, int lineno, 8065114402Sru const char *format, 8066114402Sru const errarg &arg1, 8067114402Sru const errarg &arg2, 8068114402Sru const errarg &arg3) 8069114402Sru{ 8070114402Sru fprintf(stderr, "%s:%d: error: ", filename, lineno); 8071114402Sru errprint(format, arg1, arg2, arg3); 8072114402Sru fputc('\n', stderr); 8073114402Sru fflush(stderr); 8074114402Sru} 8075114402Sru 8076114402Srudictionary charinfo_dictionary(501); 8077114402Sru 8078114402Srucharinfo *get_charinfo(symbol nm) 8079114402Sru{ 8080114402Sru void *p = charinfo_dictionary.lookup(nm); 8081114402Sru if (p != 0) 8082114402Sru return (charinfo *)p; 8083114402Sru charinfo *cp = new charinfo(nm); 8084114402Sru (void)charinfo_dictionary.lookup(nm, cp); 8085114402Sru return cp; 8086114402Sru} 8087114402Sru 8088114402Sruint charinfo::next_index = 0; 8089114402Sru 8090114402Srucharinfo::charinfo(symbol s) 8091114402Sru: translation(0), mac(0), special_translation(TRANSLATE_NONE), 8092114402Sru hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0), 8093114402Sru not_found(0), transparent_translate(1), translate_input(0), 8094114402Sru mode(CHAR_NORMAL), nm(s) 8095114402Sru{ 8096114402Sru index = next_index++; 8097114402Sru} 8098114402Sru 8099114402Sruvoid charinfo::set_hyphenation_code(unsigned char c) 8100114402Sru{ 8101114402Sru hyphenation_code = c; 8102114402Sru} 8103114402Sru 8104114402Sruvoid charinfo::set_translation(charinfo *ci, int tt, int ti) 8105114402Sru{ 8106114402Sru translation = ci; 8107114402Sru if (ci && ti) { 8108114402Sru if (hyphenation_code != 0) 8109114402Sru ci->set_hyphenation_code(hyphenation_code); 8110114402Sru if (asciify_code != 0) 8111114402Sru ci->set_asciify_code(asciify_code); 8112114402Sru else if (ascii_code != 0) 8113114402Sru ci->set_asciify_code(ascii_code); 8114114402Sru ci->set_translation_input(); 8115114402Sru } 8116114402Sru special_translation = TRANSLATE_NONE; 8117114402Sru transparent_translate = tt; 8118114402Sru} 8119114402Sru 8120114402Sruvoid charinfo::set_special_translation(int c, int tt) 8121114402Sru{ 8122114402Sru special_translation = c; 8123114402Sru translation = 0; 8124114402Sru transparent_translate = tt; 8125114402Sru} 8126114402Sru 8127114402Sruvoid charinfo::set_ascii_code(unsigned char c) 8128114402Sru{ 8129114402Sru ascii_code = c; 8130114402Sru} 8131114402Sru 8132114402Sruvoid charinfo::set_asciify_code(unsigned char c) 8133114402Sru{ 8134114402Sru asciify_code = c; 8135114402Sru} 8136114402Sru 8137114402Srumacro *charinfo::set_macro(macro *m) 8138114402Sru{ 8139114402Sru macro *tem = mac; 8140114402Sru mac = m; 8141114402Sru return tem; 8142114402Sru} 8143114402Sru 8144114402Srumacro *charinfo::setx_macro(macro *m, char_mode cm) 8145114402Sru{ 8146114402Sru macro *tem = mac; 8147114402Sru mac = m; 8148114402Sru mode = cm; 8149114402Sru return tem; 8150114402Sru} 8151114402Sru 8152114402Sruvoid charinfo::set_number(int n) 8153114402Sru{ 8154114402Sru number = n; 8155114402Sru flags |= NUMBERED; 8156114402Sru} 8157114402Sru 8158114402Sruint charinfo::get_number() 8159114402Sru{ 8160114402Sru assert(flags & NUMBERED); 8161114402Sru return number; 8162114402Sru} 8163114402Sru 8164114402Srusymbol UNNAMED_SYMBOL("---"); 8165114402Sru 8166114402Sru// For numbered characters not between 0 and 255, we make a symbol out 8167114402Sru// of the number and store them in this dictionary. 8168114402Sru 8169114402Srudictionary numbered_charinfo_dictionary(11); 8170114402Sru 8171114402Srucharinfo *get_charinfo_by_number(int n) 8172114402Sru{ 8173114402Sru static charinfo *number_table[256]; 8174114402Sru 8175114402Sru if (n >= 0 && n < 256) { 8176114402Sru charinfo *ci = number_table[n]; 8177114402Sru if (!ci) { 8178114402Sru ci = new charinfo(UNNAMED_SYMBOL); 8179114402Sru ci->set_number(n); 8180114402Sru number_table[n] = ci; 8181114402Sru } 8182114402Sru return ci; 8183114402Sru } 8184114402Sru else { 8185114402Sru symbol ns(i_to_a(n)); 8186114402Sru charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns); 8187114402Sru if (!ci) { 8188114402Sru ci = new charinfo(UNNAMED_SYMBOL); 8189114402Sru ci->set_number(n); 8190114402Sru (void)numbered_charinfo_dictionary.lookup(ns, ci); 8191114402Sru } 8192114402Sru return ci; 8193114402Sru } 8194114402Sru} 8195114402Sru 8196114402Sruint font::name_to_index(const char *nm) 8197114402Sru{ 8198114402Sru charinfo *ci; 8199114402Sru if (nm[1] == 0) 8200114402Sru ci = charset_table[nm[0] & 0xff]; 8201114402Sru else if (nm[0] == '\\' && nm[2] == 0) 8202114402Sru ci = get_charinfo(symbol(nm + 1)); 8203114402Sru else 8204114402Sru ci = get_charinfo(symbol(nm)); 8205114402Sru if (ci == 0) 8206114402Sru return -1; 8207114402Sru else 8208114402Sru return ci->get_index(); 8209114402Sru} 8210114402Sru 8211114402Sruint font::number_to_index(int n) 8212114402Sru{ 8213114402Sru return get_charinfo_by_number(n)->get_index(); 8214114402Sru} 8215