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 = &top; *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 = &top; *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 &macro::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, &macros);
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, &register_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 = &macro_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