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