PageRenderTime 182ms CodeModel.GetById 28ms app.highlight 133ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/groff/src/roff/troff/input.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 8214 lines | 7726 code | 378 blank | 110 comment | 1285 complexity | 62b837aa80c4497c3fc2edb1fd1e5325 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file