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