xref: /NextBSD/contrib/groff/src/roff/troff/input.cpp (revision 5e568154a01fb6be74908baed265f265a56f002f)
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 	      }
2805 	    } while (cc != '\n' && cc != EOF);
2806 	    if (cc == EOF)
2807 	      curdiv->transparent_output('\n');
2808 	  }
2809 	}
2810 	break;
2811       }
2812     case token::TOKEN_NEWLINE:
2813       {
2814 	if (bol && !old_have_input
2815 	    && !curenv->get_prev_line_interrupted()) {
2816 	  trapping_blank_line();
2817 	} else {
2818 	  curenv->newline();
2819 	  bol = 1;
2820 	}
2821 	break;
2822       }
2823     case token::TOKEN_REQUEST:
2824       {
2825 	int request_code = tok.c;
2826 	tok.next();
2827 	switch (request_code) {
2828 	case TITLE_REQUEST:
2829 	  title();
2830 	  break;
2831 	case COPY_FILE_REQUEST:
2832 	  copy_file();
2833 	  break;
2834 	case TRANSPARENT_FILE_REQUEST:
2835 	  transparent_file();
2836 	  break;
2837 #ifdef COLUMN
2838 	case VJUSTIFY_REQUEST:
2839 	  vjustify();
2840 	  break;
2841 #endif /* COLUMN */
2842 	default:
2843 	  assert(0);
2844 	  break;
2845 	}
2846 	suppress_next = 1;
2847 	break;
2848       }
2849     case token::TOKEN_SPACE:
2850       {
2851 	if (possibly_handle_first_page_transition())
2852 	  ;
2853 	else if (bol && !curenv->get_prev_line_interrupted()) {
2854 	  int nspaces = 0;
2855 	  // save space_width now so that it isn't changed by \f or \s
2856 	  // which we wouldn't notice here
2857 	  hunits space_width = curenv->get_space_width();
2858 	  do {
2859 	    nspaces += tok.nspaces();
2860 	    tok.next();
2861 	  } while (tok.space());
2862 	  if (tok.newline())
2863 	    trapping_blank_line();
2864 	  else {
2865 	    push_token(tok);
2866 	    curenv->do_break();
2867 	    curenv->add_node(new hmotion_node(space_width * nspaces,
2868 					      curenv->get_fill_color()));
2869 	    bol = 0;
2870 	  }
2871 	}
2872 	else {
2873 	  curenv->space();
2874 	  bol = 0;
2875 	}
2876 	break;
2877       }
2878     case token::TOKEN_EOF:
2879       return;
2880     case token::TOKEN_NODE:
2881       {
2882 	if (possibly_handle_first_page_transition())
2883 	  ;
2884 	else if (tok.nd->reread(&bol)) {
2885 	  delete tok.nd;
2886 	  tok.nd = 0;
2887 	}
2888 	else {
2889 	  curenv->add_node(tok.nd);
2890 	  tok.nd = 0;
2891 	  bol = 0;
2892 	  curenv->possibly_break_line(1);
2893 	}
2894 	break;
2895       }
2896     case token::TOKEN_PAGE_EJECTOR:
2897       {
2898 	continue_page_eject();
2899 	// I think we just want to preserve bol.
2900 	// bol = 1;
2901 	break;
2902       }
2903     case token::TOKEN_BEGIN_TRAP:
2904       {
2905 	trap_bol_stack.push(bol);
2906 	bol = 1;
2907 	have_input = 0;
2908 	break;
2909       }
2910     case token::TOKEN_END_TRAP:
2911       {
2912 	if (trap_bol_stack.is_empty())
2913 	  error("spurious end trap token detected!");
2914 	else
2915 	  bol = trap_bol_stack.pop();
2916 	have_input = 0;
2917 
2918 	/* I'm not totally happy about this.  But I can't think of any other
2919 	  way to do it.  Doing an output_pending_lines() whenever a
2920 	  TOKEN_END_TRAP is detected doesn't work: for example,
2921 
2922 	  .wh -1i x
2923 	  .de x
2924 	  'bp
2925 	  ..
2926 	  .wh -.5i y
2927 	  .de y
2928 	  .tl ''-%-''
2929 	  ..
2930 	  .br
2931 	  .ll .5i
2932 	  .sp |\n(.pu-1i-.5v
2933 	  a\%very\%very\%long\%word
2934 
2935 	  will print all but the first lines from the word immediately
2936 	  after the footer, rather than on the next page. */
2937 
2938 	if (trap_bol_stack.is_empty())
2939 	  curenv->output_pending_lines();
2940 	break;
2941       }
2942     default:
2943       {
2944 	bol = 0;
2945 	tok.process();
2946 	break;
2947       }
2948     }
2949     if (!suppress_next)
2950       tok.next();
2951     trap_sprung_flag = 0;
2952   }
2953 }
2954 
2955 #ifdef WIDOW_CONTROL
2956 
flush_pending_lines()2957 void flush_pending_lines()
2958 {
2959   while (!tok.newline() && !tok.eof())
2960     tok.next();
2961   curenv->output_pending_lines();
2962   tok.next();
2963 }
2964 
2965 #endif /* WIDOW_CONTROL */
2966 
request_or_macro()2967 request_or_macro::request_or_macro()
2968 {
2969 }
2970 
to_macro()2971 macro *request_or_macro::to_macro()
2972 {
2973   return 0;
2974 }
2975 
request(REQUEST_FUNCP pp)2976 request::request(REQUEST_FUNCP pp) : p(pp)
2977 {
2978 }
2979 
invoke(symbol)2980 void request::invoke(symbol)
2981 {
2982   (*p)();
2983 }
2984 
2985 struct char_block {
2986   enum { SIZE = 128 };
2987   unsigned char s[SIZE];
2988   char_block *next;
2989   char_block();
2990 };
2991 
char_block()2992 char_block::char_block()
2993 : next(0)
2994 {
2995 }
2996 
2997 class char_list {
2998 public:
2999   char_list();
3000   ~char_list();
3001   void append(unsigned char);
3002   void set(unsigned char, int);
3003   unsigned char get(int);
3004   int length();
3005 private:
3006   unsigned char *ptr;
3007   int len;
3008   char_block *head;
3009   char_block *tail;
3010   friend class macro_header;
3011   friend class string_iterator;
3012 };
3013 
char_list()3014 char_list::char_list()
3015 : ptr(0), len(0), head(0), tail(0)
3016 {
3017 }
3018 
~char_list()3019 char_list::~char_list()
3020 {
3021   while (head != 0) {
3022     char_block *tem = head;
3023     head = head->next;
3024     delete tem;
3025   }
3026 }
3027 
length()3028 int char_list::length()
3029 {
3030   return len;
3031 }
3032 
append(unsigned char c)3033 void char_list::append(unsigned char c)
3034 {
3035   if (tail == 0) {
3036     head = tail = new char_block;
3037     ptr = tail->s;
3038   }
3039   else {
3040     if (ptr >= tail->s + char_block::SIZE) {
3041       tail->next = new char_block;
3042       tail = tail->next;
3043       ptr = tail->s;
3044     }
3045   }
3046   *ptr++ = c;
3047   len++;
3048 }
3049 
set(unsigned char c,int offset)3050 void char_list::set(unsigned char c, int offset)
3051 {
3052   assert(len > offset);
3053   // optimization for access at the end
3054   int boundary = len - len % char_block::SIZE;
3055   if (offset >= boundary) {
3056     *(tail->s + offset - boundary) = c;
3057     return;
3058   }
3059   char_block *tem = head;
3060   int l = 0;
3061   for (;;) {
3062     l += char_block::SIZE;
3063     if (l > offset) {
3064       *(tem->s + offset % char_block::SIZE) = c;
3065       return;
3066     }
3067     tem = tem->next;
3068   }
3069 }
3070 
get(int offset)3071 unsigned char char_list::get(int offset)
3072 {
3073   assert(len > offset);
3074   // optimization for access at the end
3075   int boundary = len - len % char_block::SIZE;
3076   if (offset >= boundary)
3077     return *(tail->s + offset - boundary);
3078   char_block *tem = head;
3079   int l = 0;
3080   for (;;) {
3081     l += char_block::SIZE;
3082     if (l > offset)
3083       return *(tem->s + offset % char_block::SIZE);
3084     tem = tem->next;
3085   }
3086 }
3087 
3088 class node_list {
3089   node *head;
3090   node *tail;
3091 public:
3092   node_list();
3093   ~node_list();
3094   void append(node *);
3095   int length();
3096   node *extract();
3097 
3098   friend class macro_header;
3099   friend class string_iterator;
3100 };
3101 
append(node * n)3102 void node_list::append(node *n)
3103 {
3104   if (head == 0) {
3105     n->next = 0;
3106     head = tail = n;
3107   }
3108   else {
3109     n->next = 0;
3110     tail = tail->next = n;
3111   }
3112 }
3113 
length()3114 int node_list::length()
3115 {
3116   int total = 0;
3117   for (node *n = head; n != 0; n = n->next)
3118     ++total;
3119   return total;
3120 }
3121 
node_list()3122 node_list::node_list()
3123 {
3124   head = tail = 0;
3125 }
3126 
extract()3127 node *node_list::extract()
3128 {
3129   node *temp = head;
3130   head = tail = 0;
3131   return temp;
3132 }
3133 
~node_list()3134 node_list::~node_list()
3135 {
3136   delete_node_list(head);
3137 }
3138 
3139 class macro_header {
3140 public:
3141   int count;
3142   char_list cl;
3143   node_list nl;
macro_header()3144   macro_header() { count = 1; }
3145   macro_header *copy(int);
3146 };
3147 
~macro()3148 macro::~macro()
3149 {
3150   if (p != 0 && --(p->count) <= 0)
3151     delete p;
3152 }
3153 
macro()3154 macro::macro()
3155 : is_a_diversion(0)
3156 {
3157   if (!input_stack::get_location(1, &filename, &lineno)) {
3158     filename = 0;
3159     lineno = 0;
3160   }
3161   len = 0;
3162   empty_macro = 1;
3163   p = 0;
3164 }
3165 
macro(const macro & m)3166 macro::macro(const macro &m)
3167 : filename(m.filename), lineno(m.lineno), len(m.len),
3168   empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p)
3169 {
3170   if (p != 0)
3171     p->count++;
3172 }
3173 
macro(int is_div)3174 macro::macro(int is_div)
3175   : is_a_diversion(is_div)
3176 {
3177   if (!input_stack::get_location(1, &filename, &lineno)) {
3178     filename = 0;
3179     lineno = 0;
3180   }
3181   len = 0;
3182   empty_macro = 1;
3183   p = 0;
3184 }
3185 
is_diversion()3186 int macro::is_diversion()
3187 {
3188   return is_a_diversion;
3189 }
3190 
operator =(const macro & m)3191 macro &macro::operator=(const macro &m)
3192 {
3193   // don't assign object
3194   if (m.p != 0)
3195     m.p->count++;
3196   if (p != 0 && --(p->count) <= 0)
3197     delete p;
3198   p = m.p;
3199   filename = m.filename;
3200   lineno = m.lineno;
3201   len = m.len;
3202   empty_macro = m.empty_macro;
3203   is_a_diversion = m.is_a_diversion;
3204   return *this;
3205 }
3206 
append(unsigned char c)3207 void macro::append(unsigned char c)
3208 {
3209   assert(c != 0);
3210   if (p == 0)
3211     p = new macro_header;
3212   if (p->cl.length() != len) {
3213     macro_header *tem = p->copy(len);
3214     if (--(p->count) <= 0)
3215       delete p;
3216     p = tem;
3217   }
3218   p->cl.append(c);
3219   ++len;
3220   if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3221     empty_macro = 0;
3222 }
3223 
set(unsigned char c,int offset)3224 void macro::set(unsigned char c, int offset)
3225 {
3226   assert(p != 0);
3227   assert(c != 0);
3228   p->cl.set(c, offset);
3229 }
3230 
get(int offset)3231 unsigned char macro::get(int offset)
3232 {
3233   assert(p != 0);
3234   return p->cl.get(offset);
3235 }
3236 
length()3237 int macro::length()
3238 {
3239   return len;
3240 }
3241 
append_str(const char * s)3242 void macro::append_str(const char *s)
3243 {
3244   int i = 0;
3245 
3246   if (s) {
3247     while (s[i] != (char)0) {
3248       append(s[i]);
3249       i++;
3250     }
3251   }
3252 }
3253 
append(node * n)3254 void macro::append(node *n)
3255 {
3256   assert(n != 0);
3257   if (p == 0)
3258     p = new macro_header;
3259   if (p->cl.length() != len) {
3260     macro_header *tem = p->copy(len);
3261     if (--(p->count) <= 0)
3262       delete p;
3263     p = tem;
3264   }
3265   p->cl.append(0);
3266   p->nl.append(n);
3267   ++len;
3268   empty_macro = 0;
3269 }
3270 
append_unsigned(unsigned int i)3271 void macro::append_unsigned(unsigned int i)
3272 {
3273   unsigned int j = i / 10;
3274   if (j != 0)
3275     append_unsigned(j);
3276   append(((unsigned char)(((int)'0') + i % 10)));
3277 }
3278 
append_int(int i)3279 void macro::append_int(int i)
3280 {
3281   if (i < 0) {
3282     append('-');
3283     i = -i;
3284   }
3285   append_unsigned((unsigned int)i);
3286 }
3287 
print_size()3288 void macro::print_size()
3289 {
3290   errprint("%1", len);
3291 }
3292 
3293 // make a copy of the first n bytes
3294 
copy(int n)3295 macro_header *macro_header::copy(int n)
3296 {
3297   macro_header *p = new macro_header;
3298   char_block *bp = cl.head;
3299   unsigned char *ptr = bp->s;
3300   node *nd = nl.head;
3301   while (--n >= 0) {
3302     if (ptr >= bp->s + char_block::SIZE) {
3303       bp = bp->next;
3304       ptr = bp->s;
3305     }
3306     unsigned char c = *ptr++;
3307     p->cl.append(c);
3308     if (c == 0) {
3309       p->nl.append(nd->copy());
3310       nd = nd->next;
3311     }
3312   }
3313   return p;
3314 }
3315 
print_macros()3316 void print_macros()
3317 {
3318   object_dictionary_iterator iter(request_dictionary);
3319   request_or_macro *rm;
3320   symbol s;
3321   while (iter.get(&s, (object **)&rm)) {
3322     assert(!s.is_null());
3323     macro *m = rm->to_macro();
3324     if (m) {
3325       errprint("%1\t", s.contents());
3326       m->print_size();
3327       errprint("\n");
3328     }
3329   }
3330   fflush(stderr);
3331   skip_line();
3332 }
3333 
3334 class string_iterator : public input_iterator {
3335   macro mac;
3336   const char *how_invoked;
3337   int newline_flag;
3338   int lineno;
3339   char_block *bp;
3340   int count;			// of characters remaining
3341   node *nd;
3342   int saved_compatible_flag;
3343 protected:
3344   symbol nm;
3345   string_iterator();
3346 public:
3347   string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
3348   int fill(node **);
3349   int peek();
3350   int get_location(int, const char **, int *);
3351   void backtrace();
save_compatible_flag(int f)3352   void save_compatible_flag(int f) { saved_compatible_flag = f; }
get_compatible_flag()3353   int get_compatible_flag() { return saved_compatible_flag; }
3354   int is_diversion();
3355 };
3356 
string_iterator(const macro & m,const char * p,symbol s)3357 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3358 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3359   lineno(1), nm(s)
3360 {
3361   count = mac.len;
3362   if (count != 0) {
3363     bp = mac.p->cl.head;
3364     nd = mac.p->nl.head;
3365     ptr = eptr = bp->s;
3366   }
3367   else {
3368     bp = 0;
3369     nd = 0;
3370     ptr = eptr = 0;
3371   }
3372 }
3373 
string_iterator()3374 string_iterator::string_iterator()
3375 {
3376   bp = 0;
3377   nd = 0;
3378   ptr = eptr = 0;
3379   newline_flag = 0;
3380   how_invoked = 0;
3381   lineno = 1;
3382   count = 0;
3383 }
3384 
is_diversion()3385 int string_iterator::is_diversion()
3386 {
3387   return mac.is_diversion();
3388 }
3389 
fill(node ** np)3390 int string_iterator::fill(node **np)
3391 {
3392   if (newline_flag)
3393     lineno++;
3394   newline_flag = 0;
3395   if (count <= 0)
3396     return EOF;
3397   const unsigned char *p = eptr;
3398   if (p >= bp->s + char_block::SIZE) {
3399     bp = bp->next;
3400     p = bp->s;
3401   }
3402   if (*p == '\0') {
3403     if (np) {
3404       *np = nd->copy();
3405       if (is_diversion())
3406 	(*np)->div_nest_level = input_stack::get_div_level();
3407       else
3408 	(*np)->div_nest_level = 0;
3409     }
3410     nd = nd->next;
3411     eptr = ptr = p + 1;
3412     count--;
3413     return 0;
3414   }
3415   const unsigned char *e = bp->s + char_block::SIZE;
3416   if (e - p > count)
3417     e = p + count;
3418   ptr = p;
3419   while (p < e) {
3420     unsigned char c = *p;
3421     if (c == '\n' || c == ESCAPE_NEWLINE) {
3422       newline_flag = 1;
3423       p++;
3424       break;
3425     }
3426     if (c == '\0')
3427       break;
3428     p++;
3429   }
3430   eptr = p;
3431   count -= p - ptr;
3432   return *ptr++;
3433 }
3434 
peek()3435 int string_iterator::peek()
3436 {
3437   if (count <= 0)
3438     return EOF;
3439   const unsigned char *p = eptr;
3440   if (p >= bp->s + char_block::SIZE) {
3441     p = bp->next->s;
3442   }
3443   return *p;
3444 }
3445 
get_location(int allow_macro,const char ** filep,int * linep)3446 int string_iterator::get_location(int allow_macro,
3447 				  const char **filep, int *linep)
3448 {
3449   if (!allow_macro)
3450     return 0;
3451   if (mac.filename == 0)
3452     return 0;
3453   *filep = mac.filename;
3454   *linep = mac.lineno + lineno - 1;
3455   return 1;
3456 }
3457 
backtrace()3458 void string_iterator::backtrace()
3459 {
3460   if (mac.filename) {
3461     errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3462     if (how_invoked) {
3463       if (!nm.is_null())
3464 	errprint(": %1 `%2'\n", how_invoked, nm.contents());
3465       else
3466 	errprint(": %1\n", how_invoked);
3467     }
3468     else
3469       errprint("\n");
3470   }
3471 }
3472 
3473 class temp_iterator : public input_iterator {
3474   unsigned char *base;
3475   temp_iterator(const char *, int len);
3476 public:
3477   ~temp_iterator();
3478   friend input_iterator *make_temp_iterator(const char *);
3479 };
3480 
3481 #ifdef __GNUG__
3482 inline
3483 #endif
temp_iterator(const char * s,int len)3484 temp_iterator::temp_iterator(const char *s, int len)
3485 {
3486   base = new unsigned char[len];
3487   memcpy(base, s, len);
3488   ptr = base;
3489   eptr = base + len;
3490 }
3491 
~temp_iterator()3492 temp_iterator::~temp_iterator()
3493 {
3494   a_delete base;
3495 }
3496 
3497 class small_temp_iterator : public input_iterator {
3498 private:
3499   small_temp_iterator(const char *, int);
3500   ~small_temp_iterator();
3501   enum { BLOCK = 16 };
3502   static small_temp_iterator *free_list;
3503   void *operator new(size_t);
3504   void operator delete(void *);
3505   enum { SIZE = 12 };
3506   unsigned char buf[SIZE];
3507   friend input_iterator *make_temp_iterator(const char *);
3508 };
3509 
3510 small_temp_iterator *small_temp_iterator::free_list = 0;
3511 
operator new(size_t n)3512 void *small_temp_iterator::operator new(size_t n)
3513 {
3514   assert(n == sizeof(small_temp_iterator));
3515   if (!free_list) {
3516     free_list =
3517       (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3518     for (int i = 0; i < BLOCK - 1; i++)
3519       free_list[i].next = free_list + i + 1;
3520     free_list[BLOCK-1].next = 0;
3521   }
3522   small_temp_iterator *p = free_list;
3523   free_list = (small_temp_iterator *)(free_list->next);
3524   p->next = 0;
3525   return p;
3526 }
3527 
3528 #ifdef __GNUG__
3529 inline
3530 #endif
operator delete(void * p)3531 void small_temp_iterator::operator delete(void *p)
3532 {
3533   if (p) {
3534     ((small_temp_iterator *)p)->next = free_list;
3535     free_list = (small_temp_iterator *)p;
3536   }
3537 }
3538 
~small_temp_iterator()3539 small_temp_iterator::~small_temp_iterator()
3540 {
3541 }
3542 
3543 #ifdef __GNUG__
3544 inline
3545 #endif
small_temp_iterator(const char * s,int len)3546 small_temp_iterator::small_temp_iterator(const char *s, int len)
3547 {
3548   for (int i = 0; i < len; i++)
3549     buf[i] = s[i];
3550   ptr = buf;
3551   eptr = buf + len;
3552 }
3553 
make_temp_iterator(const char * s)3554 input_iterator *make_temp_iterator(const char *s)
3555 {
3556   if (s == 0)
3557     return new small_temp_iterator(s, 0);
3558   else {
3559     int n = strlen(s);
3560     if (n <= small_temp_iterator::SIZE)
3561       return new small_temp_iterator(s, n);
3562     else
3563       return new temp_iterator(s, n);
3564   }
3565 }
3566 
3567 // this is used when macros with arguments are interpolated
3568 
3569 struct arg_list {
3570   macro mac;
3571   arg_list *next;
3572   arg_list(const macro &);
3573   ~arg_list();
3574 };
3575 
arg_list(const macro & m)3576 arg_list::arg_list(const macro &m) : mac(m), next(0)
3577 {
3578 }
3579 
~arg_list()3580 arg_list::~arg_list()
3581 {
3582 }
3583 
3584 class macro_iterator : public string_iterator {
3585   arg_list *args;
3586   int argc;
3587 public:
3588   macro_iterator(symbol, macro &, const char *how_invoked = "macro");
3589   macro_iterator();
3590   ~macro_iterator();
has_args()3591   int has_args() { return 1; }
3592   input_iterator *get_arg(int i);
nargs()3593   int nargs() { return argc; }
3594   void add_arg(const macro &m);
3595   void shift(int n);
is_macro()3596   int is_macro() { return 1; }
3597   int is_diversion();
3598 };
3599 
get_arg(int i)3600 input_iterator *macro_iterator::get_arg(int i)
3601 {
3602   if (i == 0)
3603     return make_temp_iterator(nm.contents());
3604   if (i > 0 && i <= argc) {
3605     arg_list *p = args;
3606     for (int j = 1; j < i; j++) {
3607       assert(p != 0);
3608       p = p->next;
3609     }
3610     return new string_iterator(p->mac);
3611   }
3612   else
3613     return 0;
3614 }
3615 
add_arg(const macro & m)3616 void macro_iterator::add_arg(const macro &m)
3617 {
3618   arg_list **p;
3619   for (p = &args; *p; p = &((*p)->next))
3620     ;
3621   *p = new arg_list(m);
3622   ++argc;
3623 }
3624 
shift(int n)3625 void macro_iterator::shift(int n)
3626 {
3627   while (n > 0 && argc > 0) {
3628     arg_list *tem = args;
3629     args = args->next;
3630     delete tem;
3631     --argc;
3632     --n;
3633   }
3634 }
3635 
3636 // This gets used by eg .if '\?xxx\?''.
3637 
operator ==(const macro & m1,const macro & m2)3638 int operator==(const macro &m1, const macro &m2)
3639 {
3640   if (m1.len != m2.len)
3641     return 0;
3642   string_iterator iter1(m1);
3643   string_iterator iter2(m2);
3644   int n = m1.len;
3645   while (--n >= 0) {
3646     node *nd1 = 0;
3647     int c1 = iter1.get(&nd1);
3648     assert(c1 != EOF);
3649     node *nd2 = 0;
3650     int c2 = iter2.get(&nd2);
3651     assert(c2 != EOF);
3652     if (c1 != c2) {
3653       if (c1 == 0)
3654 	delete nd1;
3655       else if (c2 == 0)
3656 	delete nd2;
3657       return 0;
3658     }
3659     if (c1 == 0) {
3660       assert(nd1 != 0);
3661       assert(nd2 != 0);
3662       int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3663       delete nd1;
3664       delete nd2;
3665       if (!are_same)
3666 	return 0;
3667     }
3668   }
3669   return 1;
3670 }
3671 
interpolate_macro(symbol nm)3672 static void interpolate_macro(symbol nm)
3673 {
3674   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3675   if (p == 0) {
3676     int warned = 0;
3677     const char *s = nm.contents();
3678     if (strlen(s) > 2) {
3679       request_or_macro *r;
3680       char buf[3];
3681       buf[0] = s[0];
3682       buf[1] = s[1];
3683       buf[2] = '\0';
3684       r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3685       if (r) {
3686 	macro *m = r->to_macro();
3687 	if (!m || !m->empty())
3688 	  warned = warning(WARN_SPACE,
3689 			   "macro `%1' not defined "
3690 			   "(probably missing space after `%2')",
3691 			   nm.contents(), buf);
3692       }
3693     }
3694     if (!warned) {
3695       warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3696       p = new macro;
3697       request_dictionary.define(nm, p);
3698     }
3699   }
3700   if (p)
3701     p->invoke(nm);
3702   else {
3703     skip_line();
3704     return;
3705   }
3706 }
3707 
decode_args(macro_iterator * mi)3708 static void decode_args(macro_iterator *mi)
3709 {
3710   if (!tok.newline() && !tok.eof()) {
3711     node *n;
3712     int c = get_copy(&n);
3713     for (;;) {
3714       while (c == ' ')
3715 	c = get_copy(&n);
3716       if (c == '\n' || c == EOF)
3717 	break;
3718       macro arg;
3719       int quote_input_level = 0;
3720       int done_tab_warning = 0;
3721       if (c == '"') {
3722 	quote_input_level = input_stack::get_level();
3723 	c = get_copy(&n);
3724       }
3725       arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3726       while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3727 	if (quote_input_level > 0 && c == '"'
3728 	    && (compatible_flag
3729 		|| input_stack::get_level() == quote_input_level)) {
3730 	  c = get_copy(&n);
3731 	  if (c == '"') {
3732 	    arg.append(c);
3733 	    c = get_copy(&n);
3734 	  }
3735 	  else
3736 	    break;
3737 	}
3738 	else {
3739 	  if (c == 0)
3740 	    arg.append(n);
3741 	  else {
3742 	    if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3743 	      warning(WARN_TAB, "tab character in unquoted macro argument");
3744 	      done_tab_warning = 1;
3745 	    }
3746 	    arg.append(c);
3747 	  }
3748 	  c = get_copy(&n);
3749 	}
3750       }
3751       arg.append(POP_GROFFCOMP_MODE);
3752       mi->add_arg(arg);
3753     }
3754   }
3755 }
3756 
decode_string_args(macro_iterator * mi)3757 static void decode_string_args(macro_iterator *mi)
3758 {
3759   node *n;
3760   int c = get_copy(&n);
3761   for (;;) {
3762     while (c == ' ')
3763       c = get_copy(&n);
3764     if (c == '\n' || c == EOF) {
3765       error("missing `]'");
3766       break;
3767     }
3768     if (c == ']')
3769       break;
3770     macro arg;
3771     int quote_input_level = 0;
3772     int done_tab_warning = 0;
3773     if (c == '"') {
3774       quote_input_level = input_stack::get_level();
3775       c = get_copy(&n);
3776     }
3777     while (c != EOF && c != '\n'
3778 	   && !(c == ']' && quote_input_level == 0)
3779 	   && !(c == ' ' && quote_input_level == 0)) {
3780       if (quote_input_level > 0 && c == '"'
3781 	  && input_stack::get_level() == quote_input_level) {
3782 	c = get_copy(&n);
3783 	if (c == '"') {
3784 	  arg.append(c);
3785 	  c = get_copy(&n);
3786 	}
3787 	else
3788 	  break;
3789       }
3790       else {
3791 	if (c == 0)
3792 	  arg.append(n);
3793 	else {
3794 	  if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3795 	    warning(WARN_TAB, "tab character in unquoted string argument");
3796 	    done_tab_warning = 1;
3797 	  }
3798 	  arg.append(c);
3799 	}
3800 	c = get_copy(&n);
3801       }
3802     }
3803     mi->add_arg(arg);
3804   }
3805 }
3806 
invoke(symbol nm)3807 void macro::invoke(symbol nm)
3808 {
3809   macro_iterator *mi = new macro_iterator(nm, *this);
3810   decode_args(mi);
3811   input_stack::push(mi);
3812   tok.next();
3813 }
3814 
to_macro()3815 macro *macro::to_macro()
3816 {
3817   return this;
3818 }
3819 
empty()3820 int macro::empty()
3821 {
3822   return empty_macro == 1;
3823 }
3824 
macro_iterator(symbol s,macro & m,const char * how_called)3825 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called)
3826 : string_iterator(m, how_called, s), args(0), argc(0)
3827 {
3828 }
3829 
macro_iterator()3830 macro_iterator::macro_iterator() : args(0), argc(0)
3831 {
3832 }
3833 
~macro_iterator()3834 macro_iterator::~macro_iterator()
3835 {
3836   while (args != 0) {
3837     arg_list *tem = args;
3838     args = args->next;
3839     delete tem;
3840   }
3841 }
3842 
3843 dictionary composite_dictionary(17);
3844 
composite_request()3845 void composite_request()
3846 {
3847   symbol from = get_name(1);
3848   if (!from.is_null()) {
3849     const char *from_gn = glyph_name_to_unicode(from.contents());
3850     if (!from_gn) {
3851       from_gn = check_unicode_name(from.contents());
3852       if (!from_gn) {
3853 	error("invalid composite glyph name `%1'", from.contents());
3854 	skip_line();
3855 	return;
3856       }
3857     }
3858     const char *from_decomposed = decompose_unicode(from_gn);
3859     if (from_decomposed)
3860       from_gn = &from_decomposed[1];
3861     symbol to = get_name(1);
3862     if (to.is_null())
3863       composite_dictionary.remove(symbol(from_gn));
3864     else {
3865       const char *to_gn = glyph_name_to_unicode(to.contents());
3866       if (!to_gn) {
3867 	to_gn = check_unicode_name(to.contents());
3868 	if (!to_gn) {
3869 	  error("invalid composite glyph name `%1'", to.contents());
3870 	  skip_line();
3871 	  return;
3872 	}
3873       }
3874       const char *to_decomposed = decompose_unicode(to_gn);
3875       if (to_decomposed)
3876 	to_gn = &to_decomposed[1];
3877       if (strcmp(from_gn, to_gn) == 0)
3878 	composite_dictionary.remove(symbol(from_gn));
3879       else
3880 	(void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
3881     }
3882   }
3883   skip_line();
3884 }
3885 
composite_glyph_name(symbol nm)3886 static symbol composite_glyph_name(symbol nm)
3887 {
3888   macro_iterator *mi = new macro_iterator();
3889   decode_string_args(mi);
3890   input_stack::push(mi);
3891   const char *gn = glyph_name_to_unicode(nm.contents());
3892   if (!gn) {
3893     gn = check_unicode_name(nm.contents());
3894     if (!gn) {
3895       error("invalid base glyph `%1' in composite glyph name", nm.contents());
3896       return EMPTY_SYMBOL;
3897     }
3898   }
3899   const char *gn_decomposed = decompose_unicode(gn);
3900   string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
3901   string gl;
3902   int n = input_stack::nargs();
3903   for (int i = 1; i <= n; i++) {
3904     glyph_name += '_';
3905     input_iterator *p = input_stack::get_arg(i);
3906     gl.clear();
3907     int c;
3908     while ((c = p->get(0)) != EOF)
3909       gl += c;
3910     gl += '\0';
3911     const char *u = glyph_name_to_unicode(gl.contents());
3912     if (!u) {
3913       u = check_unicode_name(gl.contents());
3914       if (!u) {
3915 	error("invalid component `%1' in composite glyph name",
3916 	      gl.contents());
3917 	return EMPTY_SYMBOL;
3918       }
3919     }
3920     const char *decomposed = decompose_unicode(u);
3921     if (decomposed)
3922       u = &decomposed[1];
3923     void *mapped_composite = composite_dictionary.lookup(symbol(u));
3924     if (mapped_composite)
3925       u = (const char *)mapped_composite;
3926     glyph_name += u;
3927   }
3928   glyph_name += '\0';
3929   const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
3930   if (groff_gn)
3931     return symbol(groff_gn);
3932   gl.clear();
3933   gl += 'u';
3934   gl += glyph_name;
3935   return symbol(gl.contents());
3936 }
3937 
3938 int trap_sprung_flag = 0;
3939 int postpone_traps_flag = 0;
3940 symbol postponed_trap;
3941 
spring_trap(symbol nm)3942 void spring_trap(symbol nm)
3943 {
3944   assert(!nm.is_null());
3945   trap_sprung_flag = 1;
3946   if (postpone_traps_flag) {
3947     postponed_trap = nm;
3948     return;
3949   }
3950   static char buf[2] = { BEGIN_TRAP, 0 };
3951   static char buf2[2] = { END_TRAP, '\0' };
3952   input_stack::push(make_temp_iterator(buf2));
3953   request_or_macro *p = lookup_request(nm);
3954   macro *m = p->to_macro();
3955   if (m)
3956     input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
3957   else
3958     error("you can't invoke a request with a trap");
3959   input_stack::push(make_temp_iterator(buf));
3960 }
3961 
postpone_traps()3962 void postpone_traps()
3963 {
3964   postpone_traps_flag = 1;
3965 }
3966 
unpostpone_traps()3967 int unpostpone_traps()
3968 {
3969   postpone_traps_flag = 0;
3970   if (!postponed_trap.is_null()) {
3971     spring_trap(postponed_trap);
3972     postponed_trap = NULL_SYMBOL;
3973     return 1;
3974   }
3975   else
3976     return 0;
3977 }
3978 
read_request()3979 void read_request()
3980 {
3981   macro_iterator *mi = new macro_iterator;
3982   int reading_from_terminal = isatty(fileno(stdin));
3983   int had_prompt = 0;
3984   if (!tok.newline() && !tok.eof()) {
3985     int c = get_copy(0);
3986     while (c == ' ')
3987       c = get_copy(0);
3988     while (c != EOF && c != '\n' && c != ' ') {
3989       if (!invalid_input_char(c)) {
3990 	if (reading_from_terminal)
3991 	  fputc(c, stderr);
3992 	had_prompt = 1;
3993       }
3994       c = get_copy(0);
3995     }
3996     if (c == ' ') {
3997       tok.make_space();
3998       decode_args(mi);
3999     }
4000   }
4001   if (reading_from_terminal) {
4002     fputc(had_prompt ? ':' : '\a', stderr);
4003     fflush(stderr);
4004   }
4005   input_stack::push(mi);
4006   macro mac;
4007   int nl = 0;
4008   int c;
4009   while ((c = getchar()) != EOF) {
4010     if (invalid_input_char(c))
4011       warning(WARN_INPUT, "invalid input character code %1", int(c));
4012     else {
4013       if (c == '\n') {
4014 	if (nl)
4015 	  break;
4016 	else
4017 	  nl = 1;
4018       }
4019       else
4020 	nl = 0;
4021       mac.append(c);
4022     }
4023   }
4024   if (reading_from_terminal)
4025     clearerr(stdin);
4026   input_stack::push(new string_iterator(mac));
4027   tok.next();
4028 }
4029 
4030 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4031 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4032 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4033 
do_define_string(define_mode mode,comp_mode comp)4034 void do_define_string(define_mode mode, comp_mode comp)
4035 {
4036   symbol nm;
4037   node *n = 0;		// pacify compiler
4038   int c;
4039   nm = get_name(1);
4040   if (nm.is_null()) {
4041     skip_line();
4042     return;
4043   }
4044   if (tok.newline())
4045     c = '\n';
4046   else if (tok.tab())
4047     c = '\t';
4048   else if (!tok.space()) {
4049     error("bad string definition");
4050     skip_line();
4051     return;
4052   }
4053   else
4054     c = get_copy(&n);
4055   while (c == ' ')
4056     c = get_copy(&n);
4057   if (c == '"')
4058     c = get_copy(&n);
4059   macro mac;
4060   request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4061   macro *mm = rm ? rm->to_macro() : 0;
4062   if (mode == DEFINE_APPEND && mm)
4063     mac = *mm;
4064   if (comp == COMP_DISABLE)
4065     mac.append(PUSH_GROFF_MODE);
4066   else if (comp == COMP_ENABLE)
4067     mac.append(PUSH_COMP_MODE);
4068   while (c != '\n' && c != EOF) {
4069     if (c == 0)
4070       mac.append(n);
4071     else
4072       mac.append((unsigned char)c);
4073     c = get_copy(&n);
4074   }
4075   if (!mm) {
4076     mm = new macro;
4077     request_dictionary.define(nm, mm);
4078   }
4079   if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4080     mac.append(POP_GROFFCOMP_MODE);
4081   *mm = mac;
4082   tok.next();
4083 }
4084 
define_string()4085 void define_string()
4086 {
4087   do_define_string(DEFINE_NORMAL,
4088 		   compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4089 }
4090 
define_nocomp_string()4091 void define_nocomp_string()
4092 {
4093   do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4094 }
4095 
append_string()4096 void append_string()
4097 {
4098   do_define_string(DEFINE_APPEND,
4099 		   compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4100 }
4101 
append_nocomp_string()4102 void append_nocomp_string()
4103 {
4104   do_define_string(DEFINE_APPEND, COMP_DISABLE);
4105 }
4106 
do_define_character(char_mode mode,const char * font_name)4107 void do_define_character(char_mode mode, const char *font_name)
4108 {
4109   node *n = 0;		// pacify compiler
4110   int c;
4111   tok.skip();
4112   charinfo *ci = tok.get_char(1);
4113   if (ci == 0) {
4114     skip_line();
4115     return;
4116   }
4117   if (font_name) {
4118     string s(font_name);
4119     s += ' ';
4120     s += ci->nm.contents();
4121     s += '\0';
4122     ci = get_charinfo(symbol(s.contents()));
4123   }
4124   tok.next();
4125   if (tok.newline())
4126     c = '\n';
4127   else if (tok.tab())
4128     c = '\t';
4129   else if (!tok.space()) {
4130     error("bad character definition");
4131     skip_line();
4132     return;
4133   }
4134   else
4135     c = get_copy(&n);
4136   while (c == ' ' || c == '\t')
4137     c = get_copy(&n);
4138   if (c == '"')
4139     c = get_copy(&n);
4140   macro *m = new macro;
4141   while (c != '\n' && c != EOF) {
4142     if (c == 0)
4143       m->append(n);
4144     else
4145       m->append((unsigned char)c);
4146     c = get_copy(&n);
4147   }
4148   m = ci->setx_macro(m, mode);
4149   if (m)
4150     delete m;
4151   tok.next();
4152 }
4153 
define_character()4154 void define_character()
4155 {
4156   do_define_character(CHAR_NORMAL);
4157 }
4158 
define_fallback_character()4159 void define_fallback_character()
4160 {
4161   do_define_character(CHAR_FALLBACK);
4162 }
4163 
define_special_character()4164 void define_special_character()
4165 {
4166   do_define_character(CHAR_SPECIAL);
4167 }
4168 
remove_character()4169 static void remove_character()
4170 {
4171   tok.skip();
4172   while (!tok.newline() && !tok.eof()) {
4173     if (!tok.space() && !tok.tab()) {
4174       charinfo *ci = tok.get_char(1);
4175       if (!ci)
4176 	break;
4177       macro *m = ci->set_macro(0);
4178       if (m)
4179 	delete m;
4180     }
4181     tok.next();
4182   }
4183   skip_line();
4184 }
4185 
interpolate_string(symbol nm)4186 static void interpolate_string(symbol nm)
4187 {
4188   request_or_macro *p = lookup_request(nm);
4189   macro *m = p->to_macro();
4190   if (!m)
4191     error("you can only invoke a string or macro using \\*");
4192   else {
4193     string_iterator *si = new string_iterator(*m, "string", nm);
4194     input_stack::push(si);
4195   }
4196 }
4197 
interpolate_string_with_args(symbol s)4198 static void interpolate_string_with_args(symbol s)
4199 {
4200   request_or_macro *p = lookup_request(s);
4201   macro *m = p->to_macro();
4202   if (!m)
4203     error("you can only invoke a string or macro using \\*");
4204   else {
4205     macro_iterator *mi = new macro_iterator(s, *m);
4206     decode_string_args(mi);
4207     input_stack::push(mi);
4208   }
4209 }
4210 
interpolate_arg(symbol nm)4211 static void interpolate_arg(symbol nm)
4212 {
4213   const char *s = nm.contents();
4214   if (!s || *s == '\0')
4215     copy_mode_error("missing argument name");
4216   else if (s[1] == 0 && csdigit(s[0]))
4217     input_stack::push(input_stack::get_arg(s[0] - '0'));
4218   else if (s[0] == '*' && s[1] == '\0') {
4219     int limit = input_stack::nargs();
4220     string args;
4221     for (int i = 1; i <= limit; i++) {
4222       input_iterator *p = input_stack::get_arg(i);
4223       int c;
4224       while ((c = p->get(0)) != EOF)
4225 	args += c;
4226       if (i != limit)
4227 	args += ' ';
4228     }
4229     if (limit > 0) {
4230       args += '\0';
4231       input_stack::push(make_temp_iterator(args.contents()));
4232     }
4233   }
4234   else if (s[0] == '@' && s[1] == '\0') {
4235     int limit = input_stack::nargs();
4236     string args;
4237     for (int i = 1; i <= limit; i++) {
4238       args += '"';
4239       args += BEGIN_QUOTE;
4240       input_iterator *p = input_stack::get_arg(i);
4241       int c;
4242       while ((c = p->get(0)) != EOF)
4243 	args += c;
4244       args += END_QUOTE;
4245       args += '"';
4246       if (i != limit)
4247 	args += ' ';
4248     }
4249     if (limit > 0) {
4250       args += '\0';
4251       input_stack::push(make_temp_iterator(args.contents()));
4252     }
4253   }
4254   else {
4255     const char *p;
4256     for (p = s; *p && csdigit(*p); p++)
4257       ;
4258     if (*p)
4259       copy_mode_error("bad argument name `%1'", s);
4260     else
4261       input_stack::push(input_stack::get_arg(atoi(s)));
4262   }
4263 }
4264 
handle_first_page_transition()4265 void handle_first_page_transition()
4266 {
4267   push_token(tok);
4268   topdiv->begin_page();
4269 }
4270 
4271 // We push back a token by wrapping it up in a token_node, and
4272 // wrapping that up in a string_iterator.
4273 
push_token(const token & t)4274 static void push_token(const token &t)
4275 {
4276   macro m;
4277   m.append(new token_node(t));
4278   input_stack::push(new string_iterator(m));
4279 }
4280 
push_page_ejector()4281 void push_page_ejector()
4282 {
4283   static char buf[2] = { PAGE_EJECTOR, '\0' };
4284   input_stack::push(make_temp_iterator(buf));
4285 }
4286 
handle_initial_request(unsigned char code)4287 void handle_initial_request(unsigned char code)
4288 {
4289   char buf[2];
4290   buf[0] = code;
4291   buf[1] = '\0';
4292   macro mac;
4293   mac.append(new token_node(tok));
4294   input_stack::push(new string_iterator(mac));
4295   input_stack::push(make_temp_iterator(buf));
4296   topdiv->begin_page();
4297   tok.next();
4298 }
4299 
handle_initial_title()4300 void handle_initial_title()
4301 {
4302   handle_initial_request(TITLE_REQUEST);
4303 }
4304 
4305 // this should be local to define_macro, but cfront 1.2 doesn't support that
4306 static symbol dot_symbol(".");
4307 
do_define_macro(define_mode mode,calling_mode calling,comp_mode comp)4308 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4309 {
4310   symbol nm, term;
4311   if (calling == CALLING_INDIRECT) {
4312     symbol temp1 = get_name(1);
4313     if (temp1.is_null()) {
4314       skip_line();
4315       return;
4316     }
4317     symbol temp2 = get_name();
4318     input_stack::push(make_temp_iterator("\n"));
4319     if (!temp2.is_null()) {
4320       interpolate_string(temp2);
4321       input_stack::push(make_temp_iterator(" "));
4322     }
4323     interpolate_string(temp1);
4324     input_stack::push(make_temp_iterator(" "));
4325     tok.next();
4326   }
4327   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4328     nm = get_name(1);
4329     if (nm.is_null()) {
4330       skip_line();
4331       return;
4332     }
4333   }
4334   term = get_name();	// the request that terminates the definition
4335   if (term.is_null())
4336     term = dot_symbol;
4337   while (!tok.newline() && !tok.eof())
4338     tok.next();
4339   const char *start_filename;
4340   int start_lineno;
4341   int have_start_location = input_stack::get_location(0, &start_filename,
4342 						      &start_lineno);
4343   node *n;
4344   // doing this here makes the line numbers come out right
4345   int c = get_copy(&n, 1);
4346   macro mac;
4347   macro *mm = 0;
4348   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4349     request_or_macro *rm =
4350       (request_or_macro *)request_dictionary.lookup(nm);
4351     if (rm)
4352       mm = rm->to_macro();
4353     if (mm && mode == DEFINE_APPEND)
4354       mac = *mm;
4355   }
4356   int bol = 1;
4357   if (comp == COMP_DISABLE)
4358     mac.append(PUSH_GROFF_MODE);
4359   else if (comp == COMP_ENABLE)
4360     mac.append(PUSH_COMP_MODE);
4361   for (;;) {
4362     while (c == ESCAPE_NEWLINE) {
4363       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4364 	mac.append(c);
4365       c = get_copy(&n, 1);
4366     }
4367     if (bol && c == '.') {
4368       const char *s = term.contents();
4369       int d = 0;
4370       // see if it matches term
4371       int i = 0;
4372       if (s[0] != 0) {
4373 	while ((d = get_copy(&n)) == ' ' || d == '\t')
4374 	  ;
4375 	if ((unsigned char)s[0] == d) {
4376 	  for (i = 1; s[i] != 0; i++) {
4377 	    d = get_copy(&n);
4378 	    if ((unsigned char)s[i] != d)
4379 	      break;
4380 	  }
4381 	}
4382       }
4383       if (s[i] == 0
4384 	  && ((i == 2 && compatible_flag)
4385 	      || (d = get_copy(&n)) == ' '
4386 	      || d == '\n')) {	// we found it
4387 	if (d == '\n')
4388 	  tok.make_newline();
4389 	else
4390 	  tok.make_space();
4391 	if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4392 	  if (!mm) {
4393 	    mm = new macro;
4394 	    request_dictionary.define(nm, mm);
4395 	  }
4396 	  if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4397 	    mac.append(POP_GROFFCOMP_MODE);
4398 	  *mm = mac;
4399 	}
4400 	if (term != dot_symbol) {
4401 	  ignoring = 0;
4402 	  interpolate_macro(term);
4403 	}
4404 	else
4405 	  skip_line();
4406 	return;
4407       }
4408       if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4409 	mac.append(c);
4410 	for (int j = 0; j < i; j++)
4411 	  mac.append(s[j]);
4412       }
4413       c = d;
4414     }
4415     if (c == EOF) {
4416       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4417 	if (have_start_location)
4418 	  error_with_file_and_line(start_filename, start_lineno,
4419 				   "end of file while defining macro `%1'",
4420 				   nm.contents());
4421 	else
4422 	  error("end of file while defining macro `%1'", nm.contents());
4423       }
4424       else {
4425 	if (have_start_location)
4426 	  error_with_file_and_line(start_filename, start_lineno,
4427 				   "end of file while ignoring input lines");
4428 	else
4429 	  error("end of file while ignoring input lines");
4430       }
4431       tok.next();
4432       return;
4433     }
4434     if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4435       if (c == 0)
4436 	mac.append(n);
4437       else
4438 	mac.append(c);
4439     }
4440     bol = (c == '\n');
4441     c = get_copy(&n, 1);
4442   }
4443 }
4444 
define_macro()4445 void define_macro()
4446 {
4447   do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4448 		  compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4449 }
4450 
define_nocomp_macro()4451 void define_nocomp_macro()
4452 {
4453   do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4454 }
4455 
define_indirect_macro()4456 void define_indirect_macro()
4457 {
4458   do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4459 		  compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4460 }
4461 
define_indirect_nocomp_macro()4462 void define_indirect_nocomp_macro()
4463 {
4464   do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4465 }
4466 
append_macro()4467 void append_macro()
4468 {
4469   do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4470 		  compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4471 }
4472 
append_nocomp_macro()4473 void append_nocomp_macro()
4474 {
4475   do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4476 }
4477 
append_indirect_macro()4478 void append_indirect_macro()
4479 {
4480   do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4481 		  compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4482 }
4483 
append_indirect_nocomp_macro()4484 void append_indirect_nocomp_macro()
4485 {
4486   do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4487 }
4488 
ignore()4489 void ignore()
4490 {
4491   ignoring = 1;
4492   do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4493   ignoring = 0;
4494 }
4495 
remove_macro()4496 void remove_macro()
4497 {
4498   for (;;) {
4499     symbol s = get_name();
4500     if (s.is_null())
4501       break;
4502     request_dictionary.remove(s);
4503   }
4504   skip_line();
4505 }
4506 
rename_macro()4507 void rename_macro()
4508 {
4509   symbol s1 = get_name(1);
4510   if (!s1.is_null()) {
4511     symbol s2 = get_name(1);
4512     if (!s2.is_null())
4513       request_dictionary.rename(s1, s2);
4514   }
4515   skip_line();
4516 }
4517 
alias_macro()4518 void alias_macro()
4519 {
4520   symbol s1 = get_name(1);
4521   if (!s1.is_null()) {
4522     symbol s2 = get_name(1);
4523     if (!s2.is_null()) {
4524       if (!request_dictionary.alias(s1, s2))
4525 	warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4526     }
4527   }
4528   skip_line();
4529 }
4530 
chop_macro()4531 void chop_macro()
4532 {
4533   symbol s = get_name(1);
4534   if (!s.is_null()) {
4535     request_or_macro *p = lookup_request(s);
4536     macro *m = p->to_macro();
4537     if (!m)
4538       error("cannot chop request");
4539     else if (m->empty())
4540       error("cannot chop empty macro");
4541     else {
4542       int have_restore = 0;
4543       // we have to check for additional save/restore pairs which could be
4544       // there due to empty am1 requests.
4545       for (;;) {
4546 	if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4547           break;
4548 	have_restore = 1;
4549 	m->len -= 1;
4550 	if (m->get(m->len - 1) != PUSH_GROFF_MODE
4551 	    && m->get(m->len - 1) != PUSH_COMP_MODE)
4552           break;
4553 	have_restore = 0;
4554 	m->len -= 1;
4555 	if (m->len == 0)
4556 	  break;
4557       }
4558       if (m->len == 0)
4559 	error("cannot chop empty macro");
4560       else {
4561 	if (have_restore)
4562 	  m->set(POP_GROFFCOMP_MODE, m->len - 1);
4563 	else
4564 	  m->len -= 1;
4565       }
4566     }
4567   }
4568   skip_line();
4569 }
4570 
substring_request()4571 void substring_request()
4572 {
4573   int start;				// 0, 1, ..., n-1  or  -1, -2, ...
4574   symbol s = get_name(1);
4575   if (!s.is_null() && get_integer(&start)) {
4576     request_or_macro *p = lookup_request(s);
4577     macro *m = p->to_macro();
4578     if (!m)
4579       error("cannot apply `substring' on a request");
4580     else {
4581       int end = -1;
4582       if (!has_arg() || get_integer(&end)) {
4583 	int real_length = 0;			// 1, 2, ..., n
4584 	string_iterator iter1(*m);
4585 	for (int l = 0; l < m->len; l++) {
4586 	  int c = iter1.get(0);
4587 	  if (c == PUSH_GROFF_MODE
4588 	      || c == PUSH_COMP_MODE
4589 	      || c == POP_GROFFCOMP_MODE)
4590 	    continue;
4591 	  if (c == EOF)
4592 	    break;
4593 	  real_length++;
4594 	}
4595 	if (start < 0)
4596 	  start += real_length;
4597 	if (end < 0)
4598 	  end += real_length;
4599 	if (start > end) {
4600 	  int tem = start;
4601 	  start = end;
4602 	  end = tem;
4603 	}
4604 	if (start >= real_length || end < 0) {
4605 	  warning(WARN_RANGE,
4606 		  "start and end index of substring out of range");
4607 	  m->len = 0;
4608 	  if (m->p) {
4609 	    if (--(m->p->count) <= 0)
4610 	      delete m->p;
4611 	    m->p = 0;
4612 	  }
4613 	  skip_line();
4614 	  return;
4615 	}
4616 	if (start < 0) {
4617 	  warning(WARN_RANGE,
4618 		  "start index of substring out of range, set to 0");
4619 	  start = 0;
4620 	}
4621 	if (end >= real_length) {
4622 	  warning(WARN_RANGE,
4623 		  "end index of substring out of range, set to string length");
4624 	  end = real_length - 1;
4625 	}
4626 	// now extract the substring
4627 	string_iterator iter(*m);
4628 	int i;
4629 	for (i = 0; i < start; i++) {
4630 	  int c = iter.get(0);
4631 	  while (c == PUSH_GROFF_MODE
4632 		 || c == PUSH_COMP_MODE
4633 		 || c == POP_GROFFCOMP_MODE)
4634 	    c = iter.get(0);
4635 	  if (c == EOF)
4636 	    break;
4637 	}
4638 	macro mac;
4639 	for (; i <= end; i++) {
4640 	  node *nd = 0;		// pacify compiler
4641 	  int c = iter.get(&nd);
4642 	  while (c == PUSH_GROFF_MODE
4643 		 || c == PUSH_COMP_MODE
4644 		 || c == POP_GROFFCOMP_MODE)
4645 	    c = iter.get(0);
4646 	  if (c == EOF)
4647 	    break;
4648 	  if (c == 0)
4649 	    mac.append(nd);
4650 	  else
4651 	    mac.append((unsigned char)c);
4652 	}
4653 	*m = mac;
4654       }
4655     }
4656   }
4657   skip_line();
4658 }
4659 
length_request()4660 void length_request()
4661 {
4662   symbol ret;
4663   ret = get_name(1);
4664   if (ret.is_null()) {
4665     skip_line();
4666     return;
4667   }
4668   int c;
4669   node *n;
4670   if (tok.newline())
4671     c = '\n';
4672   else if (tok.tab())
4673     c = '\t';
4674   else if (!tok.space()) {
4675     error("bad string definition");
4676     skip_line();
4677     return;
4678   }
4679   else
4680     c = get_copy(&n);
4681   while (c == ' ')
4682     c = get_copy(&n);
4683   if (c == '"')
4684     c = get_copy(&n);
4685   int len = 0;
4686   while (c != '\n' && c != EOF) {
4687     ++len;
4688     c = get_copy(&n);
4689   }
4690   reg *r = (reg*)number_reg_dictionary.lookup(ret);
4691   if (r)
4692     r->set_value(len);
4693   else
4694     set_number_reg(ret, len);
4695   tok.next();
4696 }
4697 
asciify_macro()4698 void asciify_macro()
4699 {
4700   symbol s = get_name(1);
4701   if (!s.is_null()) {
4702     request_or_macro *p = lookup_request(s);
4703     macro *m = p->to_macro();
4704     if (!m)
4705       error("cannot asciify request");
4706     else {
4707       macro am;
4708       string_iterator iter(*m);
4709       for (;;) {
4710 	node *nd = 0;		// pacify compiler
4711 	int c = iter.get(&nd);
4712 	if (c == EOF)
4713 	  break;
4714 	if (c != 0)
4715 	  am.append(c);
4716 	else
4717 	  nd->asciify(&am);
4718       }
4719       *m = am;
4720     }
4721   }
4722   skip_line();
4723 }
4724 
unformat_macro()4725 void unformat_macro()
4726 {
4727   symbol s = get_name(1);
4728   if (!s.is_null()) {
4729     request_or_macro *p = lookup_request(s);
4730     macro *m = p->to_macro();
4731     if (!m)
4732       error("cannot unformat request");
4733     else {
4734       macro am;
4735       string_iterator iter(*m);
4736       for (;;) {
4737 	node *nd = 0;		// pacify compiler
4738 	int c = iter.get(&nd);
4739 	if (c == EOF)
4740 	  break;
4741 	if (c != 0)
4742 	  am.append(c);
4743 	else {
4744 	  if (nd->set_unformat_flag())
4745 	    am.append(nd);
4746 	}
4747       }
4748       *m = am;
4749     }
4750   }
4751   skip_line();
4752 }
4753 
interpolate_environment_variable(symbol nm)4754 static void interpolate_environment_variable(symbol nm)
4755 {
4756   const char *s = getenv(nm.contents());
4757   if (s && *s)
4758     input_stack::push(make_temp_iterator(s));
4759 }
4760 
interpolate_number_reg(symbol nm,int inc)4761 void interpolate_number_reg(symbol nm, int inc)
4762 {
4763   reg *r = lookup_number_reg(nm);
4764   if (inc < 0)
4765     r->decrement();
4766   else if (inc > 0)
4767     r->increment();
4768   input_stack::push(make_temp_iterator(r->get_string()));
4769 }
4770 
interpolate_number_format(symbol nm)4771 static void interpolate_number_format(symbol nm)
4772 {
4773   reg *r = (reg *)number_reg_dictionary.lookup(nm);
4774   if (r)
4775     input_stack::push(make_temp_iterator(r->get_format()));
4776 }
4777 
get_delim_number(units * n,unsigned char si,int prev_value)4778 static int get_delim_number(units *n, unsigned char si, int prev_value)
4779 {
4780   token start;
4781   start.next();
4782   if (start.delimiter(1)) {
4783     tok.next();
4784     if (get_number(n, si, prev_value)) {
4785       if (start != tok)
4786 	warning(WARN_DELIM, "closing delimiter does not match");
4787       return 1;
4788     }
4789   }
4790   return 0;
4791 }
4792 
get_delim_number(units * n,unsigned char si)4793 static int get_delim_number(units *n, unsigned char si)
4794 {
4795   token start;
4796   start.next();
4797   if (start.delimiter(1)) {
4798     tok.next();
4799     if (get_number(n, si)) {
4800       if (start != tok)
4801 	warning(WARN_DELIM, "closing delimiter does not match");
4802       return 1;
4803     }
4804   }
4805   return 0;
4806 }
4807 
get_line_arg(units * n,unsigned char si,charinfo ** cp)4808 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4809 {
4810   token start;
4811   start.next();
4812   int start_level = input_stack::get_level();
4813   if (!start.delimiter(1))
4814     return 0;
4815   tok.next();
4816   if (get_number(n, si)) {
4817     if (tok.dummy() || tok.transparent_dummy())
4818       tok.next();
4819     if (!(start == tok && input_stack::get_level() == start_level)) {
4820       *cp = tok.get_char(1);
4821       tok.next();
4822     }
4823     if (!(start == tok && input_stack::get_level() == start_level))
4824       warning(WARN_DELIM, "closing delimiter does not match");
4825     return 1;
4826   }
4827   return 0;
4828 }
4829 
read_size(int * x)4830 static int read_size(int *x)
4831 {
4832   tok.next();
4833   int c = tok.ch();
4834   int inc = 0;
4835   if (c == '-') {
4836     inc = -1;
4837     tok.next();
4838     c = tok.ch();
4839   }
4840   else if (c == '+') {
4841     inc = 1;
4842     tok.next();
4843     c = tok.ch();
4844   }
4845   int val = 0;		// pacify compiler
4846   int bad = 0;
4847   if (c == '(') {
4848     tok.next();
4849     c = tok.ch();
4850     if (!inc) {
4851       // allow an increment either before or after the left parenthesis
4852       if (c == '-') {
4853 	inc = -1;
4854 	tok.next();
4855 	c = tok.ch();
4856       }
4857       else if (c == '+') {
4858 	inc = 1;
4859 	tok.next();
4860 	c = tok.ch();
4861       }
4862     }
4863     if (!csdigit(c))
4864       bad = 1;
4865     else {
4866       val = c - '0';
4867       tok.next();
4868       c = tok.ch();
4869       if (!csdigit(c))
4870 	bad = 1;
4871       else {
4872 	val = val*10 + (c - '0');
4873 	val *= sizescale;
4874       }
4875     }
4876   }
4877   else if (csdigit(c)) {
4878     val = c - '0';
4879     if (!inc && c != '0' && c < '4') {
4880       tok.next();
4881       c = tok.ch();
4882       if (!csdigit(c))
4883 	bad = 1;
4884       else
4885 	val = val*10 + (c - '0');
4886     }
4887     val *= sizescale;
4888   }
4889   else if (!tok.delimiter(1))
4890     return 0;
4891   else {
4892     token start(tok);
4893     tok.next();
4894     if (!(inc
4895 	  ? get_number(&val, 'z')
4896 	  : get_number(&val, 'z', curenv->get_requested_point_size())))
4897       return 0;
4898     if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
4899       if (start.ch() == '[')
4900 	error("missing `]'");
4901       else
4902 	error("missing closing delimiter");
4903       return 0;
4904     }
4905   }
4906   if (!bad) {
4907     switch (inc) {
4908     case 0:
4909       if (val == 0) {
4910 	// special case -- \s[0] and \s0 means to revert to previous size
4911 	*x = 0;
4912 	return 1;
4913       }
4914       *x = val;
4915       break;
4916     case 1:
4917       *x = curenv->get_requested_point_size() + val;
4918       break;
4919     case -1:
4920       *x = curenv->get_requested_point_size() - val;
4921       break;
4922     default:
4923       assert(0);
4924     }
4925     if (*x <= 0) {
4926       warning(WARN_RANGE,
4927 	      "\\s request results in non-positive point size; set to 1");
4928       *x = 1;
4929     }
4930     return 1;
4931   }
4932   else {
4933     error("bad digit in point size");
4934     return 0;
4935   }
4936 }
4937 
get_delim_name()4938 static symbol get_delim_name()
4939 {
4940   token start;
4941   start.next();
4942   if (start.eof()) {
4943     error("end of input at start of delimited name");
4944     return NULL_SYMBOL;
4945   }
4946   if (start.newline()) {
4947     error("can't delimit name with a newline");
4948     return NULL_SYMBOL;
4949   }
4950   int start_level = input_stack::get_level();
4951   char abuf[ABUF_SIZE];
4952   char *buf = abuf;
4953   int buf_size = ABUF_SIZE;
4954   int i = 0;
4955   for (;;) {
4956     if (i + 1 > buf_size) {
4957       if (buf == abuf) {
4958 	buf = new char[ABUF_SIZE*2];
4959 	memcpy(buf, abuf, buf_size);
4960 	buf_size = ABUF_SIZE*2;
4961       }
4962       else {
4963 	char *old_buf = buf;
4964 	buf = new char[buf_size*2];
4965 	memcpy(buf, old_buf, buf_size);
4966 	buf_size *= 2;
4967 	a_delete old_buf;
4968       }
4969     }
4970     tok.next();
4971     if (tok == start
4972 	&& (compatible_flag || input_stack::get_level() == start_level))
4973       break;
4974     if ((buf[i] = tok.ch()) == 0) {
4975       error("missing delimiter (got %1)", tok.description());
4976       if (buf != abuf)
4977 	a_delete buf;
4978       return NULL_SYMBOL;
4979     }
4980     i++;
4981   }
4982   buf[i] = '\0';
4983   if (buf == abuf) {
4984     if (i == 0) {
4985       error("empty delimited name");
4986       return NULL_SYMBOL;
4987     }
4988     else
4989       return symbol(buf);
4990   }
4991   else {
4992     symbol s(buf);
4993     a_delete buf;
4994     return s;
4995   }
4996 }
4997 
4998 // Implement \R
4999 
do_register()5000 static void do_register()
5001 {
5002   token start;
5003   start.next();
5004   if (!start.delimiter(1))
5005     return;
5006   tok.next();
5007   symbol nm = get_long_name(1);
5008   if (nm.is_null())
5009     return;
5010   while (tok.space())
5011     tok.next();
5012   reg *r = (reg *)number_reg_dictionary.lookup(nm);
5013   int prev_value;
5014   if (!r || !r->get_value(&prev_value))
5015     prev_value = 0;
5016   int val;
5017   if (!get_number(&val, 'u', prev_value))
5018     return;
5019   if (start != tok)
5020     warning(WARN_DELIM, "closing delimiter does not match");
5021   if (r)
5022     r->set_value(val);
5023   else
5024     set_number_reg(nm, val);
5025 }
5026 
5027 // this implements the \w escape sequence
5028 
do_width()5029 static void do_width()
5030 {
5031   token start;
5032   start.next();
5033   int start_level = input_stack::get_level();
5034   environment env(curenv);
5035   environment *oldenv = curenv;
5036   curenv = &env;
5037   for (;;) {
5038     tok.next();
5039     if (tok.eof()) {
5040       warning(WARN_DELIM, "missing closing delimiter");
5041       break;
5042     }
5043     if (tok.newline()) {
5044       warning(WARN_DELIM, "missing closing delimiter");
5045       input_stack::push(make_temp_iterator("\n"));
5046       break;
5047     }
5048     if (tok == start
5049 	&& (compatible_flag || input_stack::get_level() == start_level))
5050       break;
5051     tok.process();
5052   }
5053   env.wrap_up_tab();
5054   units x = env.get_input_line_position().to_units();
5055   input_stack::push(make_temp_iterator(i_to_a(x)));
5056   env.width_registers();
5057   curenv = oldenv;
5058   have_input = 0;
5059 }
5060 
5061 charinfo *page_character;
5062 
set_page_character()5063 void set_page_character()
5064 {
5065   page_character = get_optional_char();
5066   skip_line();
5067 }
5068 
5069 static const symbol percent_symbol("%");
5070 
read_title_parts(node ** part,hunits * part_width)5071 void read_title_parts(node **part, hunits *part_width)
5072 {
5073   tok.skip();
5074   if (tok.newline() || tok.eof())
5075     return;
5076   token start(tok);
5077   int start_level = input_stack::get_level();
5078   tok.next();
5079   for (int i = 0; i < 3; i++) {
5080     while (!tok.newline() && !tok.eof()) {
5081       if (tok == start
5082 	  && (compatible_flag || input_stack::get_level() == start_level)) {
5083 	tok.next();
5084 	break;
5085       }
5086       if (page_character != 0 && tok.get_char() == page_character)
5087 	interpolate_number_reg(percent_symbol, 0);
5088       else
5089 	tok.process();
5090       tok.next();
5091     }
5092     curenv->wrap_up_tab();
5093     part_width[i] = curenv->get_input_line_position();
5094     part[i] = curenv->extract_output_line();
5095   }
5096   while (!tok.newline() && !tok.eof())
5097     tok.next();
5098 }
5099 
5100 class non_interpreted_node : public node {
5101   macro mac;
5102 public:
5103   non_interpreted_node(const macro &);
5104   int interpret(macro *);
5105   node *copy();
5106   int ends_sentence();
5107   int same(node *);
5108   const char *type();
5109   int force_tprint();
5110   int is_tag();
5111 };
5112 
non_interpreted_node(const macro & m)5113 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5114 {
5115 }
5116 
ends_sentence()5117 int non_interpreted_node::ends_sentence()
5118 {
5119   return 2;
5120 }
5121 
same(node * nd)5122 int non_interpreted_node::same(node *nd)
5123 {
5124   return mac == ((non_interpreted_node *)nd)->mac;
5125 }
5126 
type()5127 const char *non_interpreted_node::type()
5128 {
5129   return "non_interpreted_node";
5130 }
5131 
force_tprint()5132 int non_interpreted_node::force_tprint()
5133 {
5134   return 0;
5135 }
5136 
is_tag()5137 int non_interpreted_node::is_tag()
5138 {
5139   return 0;
5140 }
5141 
copy()5142 node *non_interpreted_node::copy()
5143 {
5144   return new non_interpreted_node(mac);
5145 }
5146 
interpret(macro * m)5147 int non_interpreted_node::interpret(macro *m)
5148 {
5149   string_iterator si(mac);
5150   node *n = 0;		// pacify compiler
5151   for (;;) {
5152     int c = si.get(&n);
5153     if (c == EOF)
5154       break;
5155     if (c == 0)
5156       m->append(n);
5157     else
5158       m->append(c);
5159   }
5160   return 1;
5161 }
5162 
do_non_interpreted()5163 static node *do_non_interpreted()
5164 {
5165   node *n;
5166   int c;
5167   macro mac;
5168   while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5169     if (c == 0)
5170       mac.append(n);
5171     else
5172       mac.append(c);
5173   if (c == EOF || c == '\n') {
5174     error("missing \\?");
5175     return 0;
5176   }
5177   return new non_interpreted_node(mac);
5178 }
5179 
encode_char(macro * mac,char c)5180 static void encode_char(macro *mac, char c)
5181 {
5182   if (c == '\0') {
5183     if ((font::use_charnames_in_special) && tok.special()) {
5184       charinfo *ci = tok.get_char(1);
5185       const char *s = ci->get_symbol()->contents();
5186       if (s[0] != (char)0) {
5187 	mac->append('\\');
5188 	mac->append('(');
5189 	int i = 0;
5190 	while (s[i] != (char)0) {
5191 	  mac->append(s[i]);
5192 	  i++;
5193 	}
5194 	mac->append('\\');
5195 	mac->append(')');
5196       }
5197     }
5198     else if (tok.stretchable_space()
5199 	     || tok.unstretchable_space())
5200       mac->append(' ');
5201     else if (!(tok.hyphen_indicator()
5202 	       || tok.dummy()
5203 	       || tok.transparent_dummy()
5204 	       || tok.zero_width_break()))
5205       error("%1 is invalid within \\X", tok.description());
5206   }
5207   else {
5208     if ((font::use_charnames_in_special) && (c == '\\')) {
5209       /*
5210        * add escape escape sequence
5211        */
5212       mac->append(c);
5213     }
5214     mac->append(c);
5215   }
5216 }
5217 
do_special()5218 node *do_special()
5219 {
5220   token start;
5221   start.next();
5222   int start_level = input_stack::get_level();
5223   macro mac;
5224   for (tok.next();
5225        tok != start || input_stack::get_level() != start_level;
5226        tok.next()) {
5227     if (tok.eof()) {
5228       warning(WARN_DELIM, "missing closing delimiter");
5229       return 0;
5230     }
5231     if (tok.newline()) {
5232       input_stack::push(make_temp_iterator("\n"));
5233       warning(WARN_DELIM, "missing closing delimiter");
5234       break;
5235     }
5236     unsigned char c;
5237     if (tok.space())
5238       c = ' ';
5239     else if (tok.tab())
5240       c = '\t';
5241     else if (tok.leader())
5242       c = '\001';
5243     else if (tok.backspace())
5244       c = '\b';
5245     else
5246       c = tok.ch();
5247     encode_char(&mac, c);
5248   }
5249   return new special_node(mac);
5250 }
5251 
output_request()5252 void output_request()
5253 {
5254   if (!tok.newline() && !tok.eof()) {
5255     int c;
5256     for (;;) {
5257       c = get_copy(0);
5258       if (c == '"') {
5259 	c = get_copy(0);
5260 	break;
5261       }
5262       if (c != ' ' && c != '\t')
5263 	break;
5264     }
5265     for (; c != '\n' && c != EOF; c = get_copy(0))
5266       topdiv->transparent_output(c);
5267     topdiv->transparent_output('\n');
5268   }
5269   tok.next();
5270 }
5271 
5272 extern int image_no;		// from node.cpp
5273 
do_suppress(symbol nm)5274 static node *do_suppress(symbol nm)
5275 {
5276   if (nm.is_null() || nm.is_empty()) {
5277     error("expecting an argument to escape \\O");
5278     return 0;
5279   }
5280   const char *s = nm.contents();
5281   switch (*s) {
5282   case '0':
5283     if (begin_level == 0)
5284       // suppress generation of glyphs
5285       return new suppress_node(0, 0);
5286     break;
5287   case '1':
5288     if (begin_level == 0)
5289       // enable generation of glyphs
5290       return new suppress_node(1, 0);
5291     break;
5292   case '2':
5293     if (begin_level == 0)
5294       return new suppress_node(1, 1);
5295     break;
5296   case '3':
5297     begin_level++;
5298     break;
5299   case '4':
5300     begin_level--;
5301     break;
5302   case '5':
5303     {
5304       s++;			// move over '5'
5305       char position = *s;
5306       if (*s == (char)0) {
5307 	error("missing position and filename in \\O");
5308 	return 0;
5309       }
5310       if (!(position == 'l'
5311 	    || position == 'r'
5312 	    || position == 'c'
5313 	    || position == 'i')) {
5314 	error("l, r, c, or i position expected (got %1 in \\O)", position);
5315 	return 0;
5316       }
5317       s++;			// onto image name
5318       if (s == (char *)0) {
5319 	error("missing image name for \\O");
5320 	return 0;
5321       }
5322       image_no++;
5323       if (begin_level == 0)
5324 	return new suppress_node(symbol(s), position, image_no);
5325     }
5326     break;
5327   default:
5328     error("`%1' is an invalid argument to \\O", *s);
5329   }
5330   return 0;
5331 }
5332 
tprint(troff_output_file * out)5333 void special_node::tprint(troff_output_file *out)
5334 {
5335   tprint_start(out);
5336   string_iterator iter(mac);
5337   for (;;) {
5338     int c = iter.get(0);
5339     if (c == EOF)
5340       break;
5341     for (const char *s = ::asciify(c); *s; s++)
5342       tprint_char(out, *s);
5343   }
5344   tprint_end(out);
5345 }
5346 
get_file_line(const char ** filename,int * lineno)5347 int get_file_line(const char **filename, int *lineno)
5348 {
5349   return input_stack::get_location(0, filename, lineno);
5350 }
5351 
line_file()5352 void line_file()
5353 {
5354   int n;
5355   if (get_integer(&n)) {
5356     const char *filename = 0;
5357     if (has_arg()) {
5358       symbol s = get_long_name();
5359       filename = s.contents();
5360     }
5361     (void)input_stack::set_location(filename, n-1);
5362   }
5363   skip_line();
5364 }
5365 
5366 static int nroff_mode = 0;
5367 
nroff_request()5368 static void nroff_request()
5369 {
5370   nroff_mode = 1;
5371   skip_line();
5372 }
5373 
troff_request()5374 static void troff_request()
5375 {
5376   nroff_mode = 0;
5377   skip_line();
5378 }
5379 
skip_alternative()5380 static void skip_alternative()
5381 {
5382   int level = 0;
5383   // ensure that ``.if 0\{'' works as expected
5384   if (tok.left_brace())
5385     level++;
5386   int c;
5387   for (;;) {
5388     c = input_stack::get(0);
5389     if (c == EOF)
5390       break;
5391     if (c == ESCAPE_LEFT_BRACE)
5392       ++level;
5393     else if (c == ESCAPE_RIGHT_BRACE)
5394       --level;
5395     else if (c == escape_char && escape_char > 0)
5396       switch(input_stack::get(0)) {
5397       case '{':
5398 	++level;
5399 	break;
5400       case '}':
5401 	--level;
5402 	break;
5403       case '"':
5404 	while ((c = input_stack::get(0)) != '\n' && c != EOF)
5405 	  ;
5406       }
5407     /*
5408       Note that the level can properly be < 0, eg
5409 
5410 	.if 1 \{\
5411 	.if 0 \{\
5412 	.\}\}
5413 
5414       So don't give an error message in this case.
5415     */
5416     if (level <= 0 && c == '\n')
5417       break;
5418   }
5419   tok.next();
5420 }
5421 
begin_alternative()5422 static void begin_alternative()
5423 {
5424   while (tok.space() || tok.left_brace())
5425     tok.next();
5426 }
5427 
nop_request()5428 void nop_request()
5429 {
5430   while (tok.space())
5431     tok.next();
5432 }
5433 
5434 static int_stack if_else_stack;
5435 
do_if_request()5436 int do_if_request()
5437 {
5438   int invert = 0;
5439   while (tok.space())
5440     tok.next();
5441   while (tok.ch() == '!') {
5442     tok.next();
5443     invert = !invert;
5444   }
5445   int result;
5446   unsigned char c = tok.ch();
5447   if (c == 't') {
5448     tok.next();
5449     result = !nroff_mode;
5450   }
5451   else if (c == 'n') {
5452     tok.next();
5453     result = nroff_mode;
5454   }
5455   else if (c == 'v') {
5456     tok.next();
5457     result = 0;
5458   }
5459   else if (c == 'o') {
5460     result = (topdiv->get_page_number() & 1);
5461     tok.next();
5462   }
5463   else if (c == 'e') {
5464     result = !(topdiv->get_page_number() & 1);
5465     tok.next();
5466   }
5467   else if (c == 'd' || c == 'r') {
5468     tok.next();
5469     symbol nm = get_name(1);
5470     if (nm.is_null()) {
5471       skip_alternative();
5472       return 0;
5473     }
5474     result = (c == 'd'
5475 	      ? request_dictionary.lookup(nm) != 0
5476 	      : number_reg_dictionary.lookup(nm) != 0);
5477   }
5478   else if (c == 'm') {
5479     tok.next();
5480     symbol nm = get_long_name(1);
5481     if (nm.is_null()) {
5482       skip_alternative();
5483       return 0;
5484     }
5485     result = (nm == default_symbol
5486 	      || color_dictionary.lookup(nm) != 0);
5487   }
5488   else if (c == 'c') {
5489     tok.next();
5490     tok.skip();
5491     charinfo *ci = tok.get_char(1);
5492     if (ci == 0) {
5493       skip_alternative();
5494       return 0;
5495     }
5496     result = character_exists(ci, curenv);
5497     tok.next();
5498   }
5499   else if (c == 'F') {
5500     tok.next();
5501     symbol nm = get_long_name(1);
5502     if (nm.is_null()) {
5503       skip_alternative();
5504       return 0;
5505     }
5506     result = check_font(curenv->get_family()->nm, nm);
5507   }
5508   else if (c == 'S') {
5509     tok.next();
5510     symbol nm = get_long_name(1);
5511     if (nm.is_null()) {
5512       skip_alternative();
5513       return 0;
5514     }
5515     result = check_style(nm);
5516   }
5517   else if (tok.space())
5518     result = 0;
5519   else if (tok.delimiter()) {
5520     token delim = tok;
5521     int delim_level = input_stack::get_level();
5522     environment env1(curenv);
5523     environment env2(curenv);
5524     environment *oldenv = curenv;
5525     curenv = &env1;
5526     suppress_push = 1;
5527     for (int i = 0; i < 2; i++) {
5528       for (;;) {
5529 	tok.next();
5530 	if (tok.newline() || tok.eof()) {
5531 	  warning(WARN_DELIM, "missing closing delimiter");
5532 	  tok.next();
5533 	  curenv = oldenv;
5534 	  return 0;
5535 	}
5536 	if (tok == delim
5537 	    && (compatible_flag || input_stack::get_level() == delim_level))
5538 	  break;
5539 	tok.process();
5540       }
5541       curenv = &env2;
5542     }
5543     node *n1 = env1.extract_output_line();
5544     node *n2 = env2.extract_output_line();
5545     result = same_node_list(n1, n2);
5546     delete_node_list(n1);
5547     delete_node_list(n2);
5548     curenv = oldenv;
5549     have_input = 0;
5550     suppress_push = 0;
5551     tok.next();
5552   }
5553   else {
5554     units n;
5555     if (!get_number(&n, 'u')) {
5556       skip_alternative();
5557       return 0;
5558     }
5559     else
5560       result = n > 0;
5561   }
5562   if (invert)
5563     result = !result;
5564   if (result)
5565     begin_alternative();
5566   else
5567     skip_alternative();
5568   return result;
5569 }
5570 
if_else_request()5571 void if_else_request()
5572 {
5573   if_else_stack.push(do_if_request());
5574 }
5575 
if_request()5576 void if_request()
5577 {
5578   do_if_request();
5579 }
5580 
else_request()5581 void else_request()
5582 {
5583   if (if_else_stack.is_empty()) {
5584     warning(WARN_EL, "unbalanced .el request");
5585     skip_alternative();
5586   }
5587   else {
5588     if (if_else_stack.pop())
5589       skip_alternative();
5590     else
5591       begin_alternative();
5592   }
5593 }
5594 
5595 static int while_depth = 0;
5596 static int while_break_flag = 0;
5597 
while_request()5598 void while_request()
5599 {
5600   macro mac;
5601   int escaped = 0;
5602   int level = 0;
5603   mac.append(new token_node(tok));
5604   for (;;) {
5605     node *n = 0;		// pacify compiler
5606     int c = input_stack::get(&n);
5607     if (c == EOF)
5608       break;
5609     if (c == 0) {
5610       escaped = 0;
5611       mac.append(n);
5612     }
5613     else if (escaped) {
5614       if (c == '{')
5615 	level += 1;
5616       else if (c == '}')
5617 	level -= 1;
5618       escaped = 0;
5619       mac.append(c);
5620     }
5621     else {
5622       if (c == ESCAPE_LEFT_BRACE)
5623 	level += 1;
5624       else if (c == ESCAPE_RIGHT_BRACE)
5625 	level -= 1;
5626       else if (c == escape_char)
5627 	escaped = 1;
5628       mac.append(c);
5629       if (c == '\n' && level <= 0)
5630 	break;
5631     }
5632   }
5633   if (level != 0)
5634     error("unbalanced \\{ \\}");
5635   else {
5636     while_depth++;
5637     input_stack::add_boundary();
5638     for (;;) {
5639       input_stack::push(new string_iterator(mac, "while loop"));
5640       tok.next();
5641       if (!do_if_request()) {
5642 	while (input_stack::get(0) != EOF)
5643 	  ;
5644 	break;
5645       }
5646       process_input_stack();
5647       if (while_break_flag || input_stack::is_return_boundary()) {
5648 	while_break_flag = 0;
5649 	break;
5650       }
5651     }
5652     input_stack::remove_boundary();
5653     while_depth--;
5654   }
5655   tok.next();
5656 }
5657 
while_break_request()5658 void while_break_request()
5659 {
5660   if (!while_depth) {
5661     error("no while loop");
5662     skip_line();
5663   }
5664   else {
5665     while_break_flag = 1;
5666     while (input_stack::get(0) != EOF)
5667       ;
5668     tok.next();
5669   }
5670 }
5671 
while_continue_request()5672 void while_continue_request()
5673 {
5674   if (!while_depth) {
5675     error("no while loop");
5676     skip_line();
5677   }
5678   else {
5679     while (input_stack::get(0) != EOF)
5680       ;
5681     tok.next();
5682   }
5683 }
5684 
5685 // .so
5686 
source()5687 void source()
5688 {
5689   symbol nm = get_long_name(1);
5690   if (nm.is_null())
5691     skip_line();
5692   else {
5693     while (!tok.newline() && !tok.eof())
5694       tok.next();
5695     errno = 0;
5696     FILE *fp = include_search_path.open_file_cautious(nm.contents());
5697     if (fp)
5698       input_stack::push(new file_iterator(fp, nm.contents()));
5699     else
5700       error("can't open `%1': %2", nm.contents(), strerror(errno));
5701     tok.next();
5702   }
5703 }
5704 
5705 // like .so but use popen()
5706 
pipe_source()5707 void pipe_source()
5708 {
5709   if (safer_flag) {
5710     error(".pso request not allowed in safer mode");
5711     skip_line();
5712   }
5713   else {
5714 #ifdef POPEN_MISSING
5715     error("pipes not available on this system");
5716     skip_line();
5717 #else /* not POPEN_MISSING */
5718     if (tok.newline() || tok.eof())
5719       error("missing command");
5720     else {
5721       int c;
5722       while ((c = get_copy(0)) == ' ' || c == '\t')
5723 	;
5724       int buf_size = 24;
5725       char *buf = new char[buf_size];
5726       int buf_used = 0;
5727       for (; c != '\n' && c != EOF; c = get_copy(0)) {
5728 	const char *s = asciify(c);
5729 	int slen = strlen(s);
5730 	if (buf_used + slen + 1> buf_size) {
5731 	  char *old_buf = buf;
5732 	  int old_buf_size = buf_size;
5733 	  buf_size *= 2;
5734 	  buf = new char[buf_size];
5735 	  memcpy(buf, old_buf, old_buf_size);
5736 	  a_delete old_buf;
5737 	}
5738 	strcpy(buf + buf_used, s);
5739 	buf_used += slen;
5740       }
5741       buf[buf_used] = '\0';
5742       errno = 0;
5743       FILE *fp = popen(buf, POPEN_RT);
5744       if (fp)
5745 	input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5746       else
5747 	error("can't open pipe to process `%1': %2", buf, strerror(errno));
5748       a_delete buf;
5749     }
5750     tok.next();
5751 #endif /* not POPEN_MISSING */
5752   }
5753 }
5754 
5755 // .psbb
5756 
5757 static int llx_reg_contents = 0;
5758 static int lly_reg_contents = 0;
5759 static int urx_reg_contents = 0;
5760 static int ury_reg_contents = 0;
5761 
5762 struct bounding_box {
5763   int llx, lly, urx, ury;
5764 };
5765 
5766 /* Parse the argument to a %%BoundingBox comment.  Return 1 if it
5767 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5768 
parse_bounding_box(char * p,bounding_box * bb)5769 int parse_bounding_box(char *p, bounding_box *bb)
5770 {
5771   if (sscanf(p, "%d %d %d %d",
5772 	     &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5773     return 1;
5774   else {
5775     /* The Document Structuring Conventions say that the numbers
5776        should be integers.  Unfortunately some broken applications
5777        get this wrong. */
5778     double x1, x2, x3, x4;
5779     if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5780       bb->llx = (int)x1;
5781       bb->lly = (int)x2;
5782       bb->urx = (int)x3;
5783       bb->ury = (int)x4;
5784       return 1;
5785     }
5786     else {
5787       for (; *p == ' ' || *p == '\t'; p++)
5788 	;
5789       if (strncmp(p, "(atend)", 7) == 0) {
5790 	return 2;
5791       }
5792     }
5793   }
5794   bb->llx = bb->lly = bb->urx = bb->ury = 0;
5795   return 0;
5796 }
5797 
5798 // This version is taken from psrm.cpp
5799 
5800 #define PS_LINE_MAX 255
5801 cset white_space("\n\r \t");
5802 
ps_get_line(char * buf,FILE * fp,const char * filename)5803 int ps_get_line(char *buf, FILE *fp, const char* filename)
5804 {
5805   int c = getc(fp);
5806   if (c == EOF) {
5807     buf[0] = '\0';
5808     return 0;
5809   }
5810   int i = 0;
5811   int err = 0;
5812   while (c != '\r' && c != '\n' && c != EOF) {
5813     if ((c < 0x1b && !white_space(c)) || c == 0x7f)
5814       error("invalid input character code %1 in `%2'", int(c), filename);
5815     else if (i < PS_LINE_MAX)
5816       buf[i++] = c;
5817     else if (!err) {
5818       err = 1;
5819       error("PostScript file `%1' is non-conforming "
5820 	    "because length of line exceeds 255", filename);
5821     }
5822     c = getc(fp);
5823   }
5824   buf[i++] = '\n';
5825   buf[i] = '\0';
5826   if (c == '\r') {
5827     c = getc(fp);
5828     if (c != EOF && c != '\n')
5829       ungetc(c, fp);
5830   }
5831   return 1;
5832 }
5833 
assign_registers(int llx,int lly,int urx,int ury)5834 inline void assign_registers(int llx, int lly, int urx, int ury)
5835 {
5836   llx_reg_contents = llx;
5837   lly_reg_contents = lly;
5838   urx_reg_contents = urx;
5839   ury_reg_contents = ury;
5840 }
5841 
do_ps_file(FILE * fp,const char * filename)5842 void do_ps_file(FILE *fp, const char* filename)
5843 {
5844   bounding_box bb;
5845   int bb_at_end = 0;
5846   char buf[PS_LINE_MAX];
5847   llx_reg_contents = lly_reg_contents =
5848     urx_reg_contents = ury_reg_contents = 0;
5849   if (!ps_get_line(buf, fp, filename)) {
5850     error("`%1' is empty", filename);
5851     return;
5852   }
5853   if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
5854     error("`%1' is not conforming to the Document Structuring Conventions",
5855 	  filename);
5856     return;
5857   }
5858   while (ps_get_line(buf, fp, filename) != 0) {
5859     if (buf[0] != '%' || buf[1] != '%'
5860 	|| strncmp(buf + 2, "EndComments", 11) == 0)
5861       break;
5862     if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5863       int res = parse_bounding_box(buf + 14, &bb);
5864       if (res == 1) {
5865 	assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5866 	return;
5867       }
5868       else if (res == 2) {
5869 	bb_at_end = 1;
5870 	break;
5871       }
5872       else {
5873 	error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5874 	      filename);
5875 	return;
5876       }
5877     }
5878   }
5879   if (bb_at_end) {
5880     long offset;
5881     int last_try = 0;
5882     /* in the trailer, the last BoundingBox comment is significant */
5883     for (offset = 512; !last_try; offset *= 2) {
5884       int had_trailer = 0;
5885       int got_bb = 0;
5886       if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
5887 	last_try = 1;
5888 	if (fseek(fp, 0L, 0) == -1)
5889 	  break;
5890       }
5891       while (ps_get_line(buf, fp, filename) != 0) {
5892 	if (buf[0] == '%' && buf[1] == '%') {
5893 	  if (!had_trailer) {
5894 	    if (strncmp(buf + 2, "Trailer", 7) == 0)
5895 	      had_trailer = 1;
5896 	  }
5897 	  else {
5898 	    if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5899 	      int res = parse_bounding_box(buf + 14, &bb);
5900 	      if (res == 1)
5901 		got_bb = 1;
5902 	      else if (res == 2) {
5903 		error("`(atend)' not allowed in trailer of `%1'", filename);
5904 		return;
5905 	      }
5906 	      else {
5907 		error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5908 		      filename);
5909 		return;
5910 	      }
5911 	    }
5912 	  }
5913 	}
5914       }
5915       if (got_bb) {
5916 	assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5917 	return;
5918       }
5919     }
5920   }
5921   error("%%%%BoundingBox comment not found in `%1'", filename);
5922 }
5923 
ps_bbox_request()5924 void ps_bbox_request()
5925 {
5926   symbol nm = get_long_name(1);
5927   if (nm.is_null())
5928     skip_line();
5929   else {
5930     while (!tok.newline() && !tok.eof())
5931       tok.next();
5932     errno = 0;
5933     // PS files might contain non-printable characters, such as ^Z
5934     // and CRs not followed by an LF, so open them in binary mode.
5935     FILE *fp = include_search_path.open_file_cautious(nm.contents(),
5936 						      0, FOPEN_RB);
5937     if (fp) {
5938       do_ps_file(fp, nm.contents());
5939       fclose(fp);
5940     }
5941     else
5942       error("can't open `%1': %2", nm.contents(), strerror(errno));
5943     tok.next();
5944   }
5945 }
5946 
asciify(int c)5947 const char *asciify(int c)
5948 {
5949   static char buf[3];
5950   buf[0] = escape_char == '\0' ? '\\' : escape_char;
5951   buf[1] = buf[2] = '\0';
5952   switch (c) {
5953   case ESCAPE_QUESTION:
5954     buf[1] = '?';
5955     break;
5956   case ESCAPE_AMPERSAND:
5957     buf[1] = '&';
5958     break;
5959   case ESCAPE_RIGHT_PARENTHESIS:
5960     buf[1] = ')';
5961     break;
5962   case ESCAPE_UNDERSCORE:
5963     buf[1] = '_';
5964     break;
5965   case ESCAPE_BAR:
5966     buf[1] = '|';
5967     break;
5968   case ESCAPE_CIRCUMFLEX:
5969     buf[1] = '^';
5970     break;
5971   case ESCAPE_LEFT_BRACE:
5972     buf[1] = '{';
5973     break;
5974   case ESCAPE_RIGHT_BRACE:
5975     buf[1] = '}';
5976     break;
5977   case ESCAPE_LEFT_QUOTE:
5978     buf[1] = '`';
5979     break;
5980   case ESCAPE_RIGHT_QUOTE:
5981     buf[1] = '\'';
5982     break;
5983   case ESCAPE_HYPHEN:
5984     buf[1] = '-';
5985     break;
5986   case ESCAPE_BANG:
5987     buf[1] = '!';
5988     break;
5989   case ESCAPE_c:
5990     buf[1] = 'c';
5991     break;
5992   case ESCAPE_e:
5993     buf[1] = 'e';
5994     break;
5995   case ESCAPE_E:
5996     buf[1] = 'E';
5997     break;
5998   case ESCAPE_PERCENT:
5999     buf[1] = '%';
6000     break;
6001   case ESCAPE_SPACE:
6002     buf[1] = ' ';
6003     break;
6004   case ESCAPE_TILDE:
6005     buf[1] = '~';
6006     break;
6007   case ESCAPE_COLON:
6008     buf[1] = ':';
6009     break;
6010   case PUSH_GROFF_MODE:
6011   case PUSH_COMP_MODE:
6012   case POP_GROFFCOMP_MODE:
6013     buf[0] = '\0';
6014     break;
6015   default:
6016     if (invalid_input_char(c))
6017       buf[0] = '\0';
6018     else
6019       buf[0] = c;
6020     break;
6021   }
6022   return buf;
6023 }
6024 
input_char_description(int c)6025 const char *input_char_description(int c)
6026 {
6027   switch (c) {
6028   case '\n':
6029     return "a newline character";
6030   case '\b':
6031     return "a backspace character";
6032   case '\001':
6033     return "a leader character";
6034   case '\t':
6035     return "a tab character";
6036   case ' ':
6037     return "a space character";
6038   case '\0':
6039     return "a node";
6040   }
6041   static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6042   if (invalid_input_char(c)) {
6043     const char *s = asciify(c);
6044     if (*s) {
6045       buf[0] = '`';
6046       strcpy(buf + 1, s);
6047       strcat(buf, "'");
6048       return buf;
6049     }
6050     sprintf(buf, "magic character code %d", c);
6051     return buf;
6052   }
6053   if (csprint(c)) {
6054     buf[0] = '`';
6055     buf[1] = c;
6056     buf[2] = '\'';
6057     return buf;
6058   }
6059   sprintf(buf, "character code %d", c);
6060   return buf;
6061 }
6062 
tag()6063 void tag()
6064 {
6065   if (!tok.newline() && !tok.eof()) {
6066     string s;
6067     int c;
6068     for (;;) {
6069       c = get_copy(0);
6070       if (c == '"') {
6071 	c = get_copy(0);
6072 	break;
6073       }
6074       if (c != ' ' && c != '\t')
6075 	break;
6076     }
6077     s = "x X ";
6078     for (; c != '\n' && c != EOF; c = get_copy(0))
6079       s += (char)c;
6080     s += '\n';
6081     curenv->add_node(new tag_node(s, 0));
6082   }
6083   tok.next();
6084 }
6085 
taga()6086 void taga()
6087 {
6088   if (!tok.newline() && !tok.eof()) {
6089     string s;
6090     int c;
6091     for (;;) {
6092       c = get_copy(0);
6093       if (c == '"') {
6094 	c = get_copy(0);
6095 	break;
6096       }
6097       if (c != ' ' && c != '\t')
6098 	break;
6099     }
6100     s = "x X ";
6101     for (; c != '\n' && c != EOF; c = get_copy(0))
6102       s += (char)c;
6103     s += '\n';
6104     curenv->add_node(new tag_node(s, 1));
6105   }
6106   tok.next();
6107 }
6108 
6109 // .tm, .tm1, and .tmc
6110 
do_terminal(int newline,int string_like)6111 void do_terminal(int newline, int string_like)
6112 {
6113   if (!tok.newline() && !tok.eof()) {
6114     int c;
6115     for (;;) {
6116       c = get_copy(0);
6117       if (string_like && c == '"') {
6118 	c = get_copy(0);
6119 	break;
6120       }
6121       if (c != ' ' && c != '\t')
6122 	break;
6123     }
6124     for (; c != '\n' && c != EOF; c = get_copy(0))
6125       fputs(asciify(c), stderr);
6126   }
6127   if (newline)
6128     fputc('\n', stderr);
6129   fflush(stderr);
6130   tok.next();
6131 }
6132 
terminal()6133 void terminal()
6134 {
6135   do_terminal(1, 0);
6136 }
6137 
terminal1()6138 void terminal1()
6139 {
6140   do_terminal(1, 1);
6141 }
6142 
terminal_continue()6143 void terminal_continue()
6144 {
6145   do_terminal(0, 1);
6146 }
6147 
6148 dictionary stream_dictionary(20);
6149 
do_open(int append)6150 void do_open(int append)
6151 {
6152   symbol stream = get_name(1);
6153   if (!stream.is_null()) {
6154     symbol filename = get_long_name(1);
6155     if (!filename.is_null()) {
6156       errno = 0;
6157       FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6158       if (!fp) {
6159 	error("can't open `%1' for %2: %3",
6160 	      filename.contents(),
6161 	      append ? "appending" : "writing",
6162 	      strerror(errno));
6163 	fp = (FILE *)stream_dictionary.remove(stream);
6164       }
6165       else
6166 	fp = (FILE *)stream_dictionary.lookup(stream, fp);
6167       if (fp)
6168 	fclose(fp);
6169     }
6170   }
6171   skip_line();
6172 }
6173 
open_request()6174 void open_request()
6175 {
6176   if (safer_flag) {
6177     error(".open request not allowed in safer mode");
6178     skip_line();
6179   }
6180   else
6181     do_open(0);
6182 }
6183 
opena_request()6184 void opena_request()
6185 {
6186   if (safer_flag) {
6187     error(".opena request not allowed in safer mode");
6188     skip_line();
6189   }
6190   else
6191     do_open(1);
6192 }
6193 
close_request()6194 void close_request()
6195 {
6196   symbol stream = get_name(1);
6197   if (!stream.is_null()) {
6198     FILE *fp = (FILE *)stream_dictionary.remove(stream);
6199     if (!fp)
6200       error("no stream named `%1'", stream.contents());
6201     else
6202       fclose(fp);
6203   }
6204   skip_line();
6205 }
6206 
6207 // .write and .writec
6208 
do_write_request(int newline)6209 void do_write_request(int newline)
6210 {
6211   symbol stream = get_name(1);
6212   if (stream.is_null()) {
6213     skip_line();
6214     return;
6215   }
6216   FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6217   if (!fp) {
6218     error("no stream named `%1'", stream.contents());
6219     skip_line();
6220     return;
6221   }
6222   int c;
6223   while ((c = get_copy(0)) == ' ')
6224     ;
6225   if (c == '"')
6226     c = get_copy(0);
6227   for (; c != '\n' && c != EOF; c = get_copy(0))
6228     fputs(asciify(c), fp);
6229   if (newline)
6230     fputc('\n', fp);
6231   fflush(fp);
6232   tok.next();
6233 }
6234 
write_request()6235 void write_request()
6236 {
6237   do_write_request(1);
6238 }
6239 
write_request_continue()6240 void write_request_continue()
6241 {
6242   do_write_request(0);
6243 }
6244 
write_macro_request()6245 void write_macro_request()
6246 {
6247   symbol stream = get_name(1);
6248   if (stream.is_null()) {
6249     skip_line();
6250     return;
6251   }
6252   FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6253   if (!fp) {
6254     error("no stream named `%1'", stream.contents());
6255     skip_line();
6256     return;
6257   }
6258   symbol s = get_name(1);
6259   if (s.is_null()) {
6260     skip_line();
6261     return;
6262   }
6263   request_or_macro *p = lookup_request(s);
6264   macro *m = p->to_macro();
6265   if (!m)
6266     error("cannot write request");
6267   else {
6268     string_iterator iter(*m);
6269     for (;;) {
6270       int c = iter.get(0);
6271       if (c == EOF)
6272 	break;
6273       fputs(asciify(c), fp);
6274     }
6275     fflush(fp);
6276   }
6277   skip_line();
6278 }
6279 
warnscale_request()6280 void warnscale_request()
6281 {
6282   if (has_arg()) {
6283     char c = tok.ch();
6284     if (c == 'u')
6285       warn_scale = 1.0;
6286     else if (c == 'i')
6287       warn_scale = (double)units_per_inch;
6288     else if (c == 'c')
6289       warn_scale = (double)units_per_inch / 2.54;
6290     else if (c == 'p')
6291       warn_scale = (double)units_per_inch / 72.0;
6292     else if (c == 'P')
6293       warn_scale = (double)units_per_inch / 6.0;
6294     else {
6295       warning(WARN_SCALE,
6296 	      "invalid scaling indicator `%1', using `i' instead", c);
6297       c = 'i';
6298     }
6299     warn_scaling_indicator = c;
6300   }
6301   skip_line();
6302 }
6303 
spreadwarn_request()6304 void spreadwarn_request()
6305 {
6306   hunits n;
6307   if (has_arg() && get_hunits(&n, 'm')) {
6308     if (n < 0)
6309       n = 0;
6310     hunits em = curenv->get_size();
6311     spread_limit = (double)n.to_units()
6312 		   / (em.is_zero() ? hresolution : em.to_units());
6313   }
6314   else
6315     spread_limit = -spread_limit - 1;	// no arg toggles on/off without
6316 					// changing value; we mirror at
6317 					// -0.5 to make zero a valid value
6318   skip_line();
6319 }
6320 
init_charset_table()6321 static void init_charset_table()
6322 {
6323   char buf[16];
6324   strcpy(buf, "char");
6325   for (int i = 0; i < 256; i++) {
6326     strcpy(buf + 4, i_to_a(i));
6327     charset_table[i] = get_charinfo(symbol(buf));
6328     charset_table[i]->set_ascii_code(i);
6329     if (csalpha(i))
6330       charset_table[i]->set_hyphenation_code(cmlower(i));
6331   }
6332   charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6333   charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6334   charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6335   charset_table['-']->set_flags(charinfo::BREAK_AFTER);
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   charset_table['*']->set_flags(charinfo::TRANSPARENT);
6341   get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6342   get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6343   get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6344   get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6345   get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6346   get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6347   get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6348   get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6349   get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6350   page_character = charset_table['%'];
6351 }
6352 
init_hpf_code_table()6353 static void init_hpf_code_table()
6354 {
6355   for (int i = 0; i < 256; i++)
6356     hpf_code_table[i] = i;
6357 }
6358 
do_translate(int translate_transparent,int translate_input)6359 static void do_translate(int translate_transparent, int translate_input)
6360 {
6361   tok.skip();
6362   while (!tok.newline() && !tok.eof()) {
6363     if (tok.space()) {
6364       // This is a really bizarre troff feature.
6365       tok.next();
6366       translate_space_to_dummy = tok.dummy();
6367       if (tok.newline() || tok.eof())
6368 	break;
6369       tok.next();
6370       continue;
6371     }
6372     charinfo *ci1 = tok.get_char(1);
6373     if (ci1 == 0)
6374       break;
6375     tok.next();
6376     if (tok.newline() || tok.eof()) {
6377       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6378 				   translate_transparent);
6379       break;
6380     }
6381     if (tok.space())
6382       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6383 				   translate_transparent);
6384     else if (tok.stretchable_space())
6385       ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6386 				   translate_transparent);
6387     else if (tok.dummy())
6388       ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6389 				   translate_transparent);
6390     else if (tok.hyphen_indicator())
6391       ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6392 				   translate_transparent);
6393     else {
6394       charinfo *ci2 = tok.get_char(1);
6395       if (ci2 == 0)
6396 	break;
6397       if (ci1 == ci2)
6398 	ci1->set_translation(0, translate_transparent, translate_input);
6399       else
6400 	ci1->set_translation(ci2, translate_transparent, translate_input);
6401     }
6402     tok.next();
6403   }
6404   skip_line();
6405 }
6406 
translate()6407 void translate()
6408 {
6409   do_translate(1, 0);
6410 }
6411 
translate_no_transparent()6412 void translate_no_transparent()
6413 {
6414   do_translate(0, 0);
6415 }
6416 
translate_input()6417 void translate_input()
6418 {
6419   do_translate(1, 1);
6420 }
6421 
char_flags()6422 void char_flags()
6423 {
6424   int flags;
6425   if (get_integer(&flags))
6426     while (has_arg()) {
6427       charinfo *ci = tok.get_char(1);
6428       if (ci) {
6429 	charinfo *tem = ci->get_translation();
6430 	if (tem)
6431 	  ci = tem;
6432 	ci->set_flags(flags);
6433       }
6434       tok.next();
6435     }
6436   skip_line();
6437 }
6438 
hyphenation_code()6439 void hyphenation_code()
6440 {
6441   tok.skip();
6442   while (!tok.newline() && !tok.eof()) {
6443     charinfo *ci = tok.get_char(1);
6444     if (ci == 0)
6445       break;
6446     tok.next();
6447     tok.skip();
6448     unsigned char c = tok.ch();
6449     if (c == 0) {
6450       error("hyphenation code must be ordinary character");
6451       break;
6452     }
6453     if (csdigit(c)) {
6454       error("hyphenation code cannot be digit");
6455       break;
6456     }
6457     ci->set_hyphenation_code(c);
6458     if (ci->get_translation()
6459 	&& ci->get_translation()->get_translation_input())
6460       ci->get_translation()->set_hyphenation_code(c);
6461     tok.next();
6462     tok.skip();
6463   }
6464   skip_line();
6465 }
6466 
hyphenation_patterns_file_code()6467 void hyphenation_patterns_file_code()
6468 {
6469   tok.skip();
6470   while (!tok.newline() && !tok.eof()) {
6471     int n1, n2;
6472     if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6473       if (!has_arg()) {
6474 	error("missing output hyphenation code");
6475 	break;
6476       }
6477       if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6478 	hpf_code_table[n1] = n2;
6479 	tok.skip();
6480       }
6481       else {
6482 	error("output hyphenation code must be integer in the range 0..255");
6483 	break;
6484       }
6485     }
6486     else {
6487       error("input hyphenation code must be integer in the range 0..255");
6488       break;
6489     }
6490   }
6491   skip_line();
6492 }
6493 
get_char(int required)6494 charinfo *token::get_char(int required)
6495 {
6496   if (type == TOKEN_CHAR)
6497     return charset_table[c];
6498   if (type == TOKEN_SPECIAL)
6499     return get_charinfo(nm);
6500   if (type == TOKEN_NUMBERED_CHAR)
6501     return get_charinfo_by_number(val);
6502   if (type == TOKEN_ESCAPE) {
6503     if (escape_char != 0)
6504       return charset_table[escape_char];
6505     else {
6506       error("`\\e' used while no current escape character");
6507       return 0;
6508     }
6509   }
6510   if (required) {
6511     if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6512       warning(WARN_MISSING, "missing normal or special character");
6513     else
6514       error("normal or special character expected (got %1)", description());
6515   }
6516   return 0;
6517 }
6518 
get_optional_char()6519 charinfo *get_optional_char()
6520 {
6521   while (tok.space())
6522     tok.next();
6523   charinfo *ci = tok.get_char();
6524   if (!ci)
6525     check_missing_character();
6526   else
6527     tok.next();
6528   return ci;
6529 }
6530 
check_missing_character()6531 void check_missing_character()
6532 {
6533   if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6534     error("normal or special character expected (got %1): "
6535 	  "treated as missing",
6536 	  tok.description());
6537 }
6538 
6539 // this is for \Z
6540 
add_to_node_list(node ** pp)6541 int token::add_to_node_list(node **pp)
6542 {
6543   hunits w;
6544   int s;
6545   node *n = 0;
6546   switch (type) {
6547   case TOKEN_CHAR:
6548     *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6549     break;
6550   case TOKEN_DUMMY:
6551     n = new dummy_node;
6552     break;
6553   case TOKEN_ESCAPE:
6554     if (escape_char != 0)
6555       *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6556     break;
6557   case TOKEN_HYPHEN_INDICATOR:
6558     *pp = (*pp)->add_discretionary_hyphen();
6559     break;
6560   case TOKEN_ITALIC_CORRECTION:
6561     *pp = (*pp)->add_italic_correction(&w);
6562     break;
6563   case TOKEN_LEFT_BRACE:
6564     break;
6565   case TOKEN_MARK_INPUT:
6566     set_number_reg(nm, curenv->get_input_line_position().to_units());
6567     break;
6568   case TOKEN_NODE:
6569     n = nd;
6570     nd = 0;
6571     break;
6572   case TOKEN_NUMBERED_CHAR:
6573     *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6574     break;
6575   case TOKEN_RIGHT_BRACE:
6576     break;
6577   case TOKEN_SPACE:
6578     n = new hmotion_node(curenv->get_space_width(),
6579 			 curenv->get_fill_color());
6580     break;
6581   case TOKEN_SPECIAL:
6582     *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6583     break;
6584   case TOKEN_STRETCHABLE_SPACE:
6585     n = new unbreakable_space_node(curenv->get_space_width(),
6586 				   curenv->get_fill_color());
6587     break;
6588   case TOKEN_UNSTRETCHABLE_SPACE:
6589     n = new space_char_hmotion_node(curenv->get_space_width(),
6590 				    curenv->get_fill_color());
6591     break;
6592   case TOKEN_TRANSPARENT_DUMMY:
6593     n = new transparent_dummy_node;
6594     break;
6595   case TOKEN_ZERO_WIDTH_BREAK:
6596     n = new space_node(H0, curenv->get_fill_color());
6597     n->freeze_space();
6598     n->is_escape_colon();
6599     break;
6600   default:
6601     return 0;
6602   }
6603   if (n) {
6604     n->next = *pp;
6605     *pp = n;
6606   }
6607   return 1;
6608 }
6609 
process()6610 void token::process()
6611 {
6612   if (possibly_handle_first_page_transition())
6613     return;
6614   switch (type) {
6615   case TOKEN_BACKSPACE:
6616     curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6617 				      curenv->get_fill_color()));
6618     break;
6619   case TOKEN_CHAR:
6620     curenv->add_char(charset_table[c]);
6621     break;
6622   case TOKEN_DUMMY:
6623     curenv->add_node(new dummy_node);
6624     break;
6625   case TOKEN_EMPTY:
6626     assert(0);
6627     break;
6628   case TOKEN_EOF:
6629     assert(0);
6630     break;
6631   case TOKEN_ESCAPE:
6632     if (escape_char != 0)
6633       curenv->add_char(charset_table[escape_char]);
6634     break;
6635   case TOKEN_BEGIN_TRAP:
6636   case TOKEN_END_TRAP:
6637   case TOKEN_PAGE_EJECTOR:
6638     // these are all handled in process_input_stack()
6639     break;
6640   case TOKEN_HYPHEN_INDICATOR:
6641     curenv->add_hyphen_indicator();
6642     break;
6643   case TOKEN_INTERRUPT:
6644     curenv->interrupt();
6645     break;
6646   case TOKEN_ITALIC_CORRECTION:
6647     curenv->add_italic_correction();
6648     break;
6649   case TOKEN_LEADER:
6650     curenv->handle_tab(1);
6651     break;
6652   case TOKEN_LEFT_BRACE:
6653     break;
6654   case TOKEN_MARK_INPUT:
6655     set_number_reg(nm, curenv->get_input_line_position().to_units());
6656     break;
6657   case TOKEN_NEWLINE:
6658     curenv->newline();
6659     break;
6660   case TOKEN_NODE:
6661     curenv->add_node(nd);
6662     nd = 0;
6663     break;
6664   case TOKEN_NUMBERED_CHAR:
6665     curenv->add_char(get_charinfo_by_number(val));
6666     break;
6667   case TOKEN_REQUEST:
6668     // handled in process_input_stack()
6669     break;
6670   case TOKEN_RIGHT_BRACE:
6671     break;
6672   case TOKEN_SPACE:
6673     curenv->space();
6674     break;
6675   case TOKEN_SPECIAL:
6676     curenv->add_char(get_charinfo(nm));
6677     break;
6678   case TOKEN_SPREAD:
6679     curenv->spread();
6680     break;
6681   case TOKEN_STRETCHABLE_SPACE:
6682     curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6683 						curenv->get_fill_color()));
6684     break;
6685   case TOKEN_UNSTRETCHABLE_SPACE:
6686     curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6687 						 curenv->get_fill_color()));
6688     break;
6689   case TOKEN_TAB:
6690     curenv->handle_tab(0);
6691     break;
6692   case TOKEN_TRANSPARENT:
6693     break;
6694   case TOKEN_TRANSPARENT_DUMMY:
6695     curenv->add_node(new transparent_dummy_node);
6696     break;
6697   case TOKEN_ZERO_WIDTH_BREAK:
6698     {
6699       node *tmp = new space_node(H0, curenv->get_fill_color());
6700       tmp->freeze_space();
6701       tmp->is_escape_colon();
6702       curenv->add_node(tmp);
6703       break;
6704     }
6705   default:
6706     assert(0);
6707   }
6708 }
6709 
6710 class nargs_reg : public reg {
6711 public:
6712   const char *get_string();
6713 };
6714 
get_string()6715 const char *nargs_reg::get_string()
6716 {
6717   return i_to_a(input_stack::nargs());
6718 }
6719 
6720 class lineno_reg : public reg {
6721 public:
6722   const char *get_string();
6723 };
6724 
get_string()6725 const char *lineno_reg::get_string()
6726 {
6727   int line;
6728   const char *file;
6729   if (!input_stack::get_location(0, &file, &line))
6730     line = 0;
6731   return i_to_a(line);
6732 }
6733 
6734 class writable_lineno_reg : public general_reg {
6735 public:
6736   writable_lineno_reg();
6737   void set_value(units);
6738   int get_value(units *);
6739 };
6740 
writable_lineno_reg()6741 writable_lineno_reg::writable_lineno_reg()
6742 {
6743 }
6744 
get_value(units * res)6745 int writable_lineno_reg::get_value(units *res)
6746 {
6747   int line;
6748   const char *file;
6749   if (!input_stack::get_location(0, &file, &line))
6750     return 0;
6751   *res = line;
6752   return 1;
6753 }
6754 
set_value(units n)6755 void writable_lineno_reg::set_value(units n)
6756 {
6757   input_stack::set_location(0, n);
6758 }
6759 
6760 class filename_reg : public reg {
6761 public:
6762   const char *get_string();
6763 };
6764 
get_string()6765 const char *filename_reg::get_string()
6766 {
6767   int line;
6768   const char *file;
6769   if (input_stack::get_location(0, &file, &line))
6770     return file;
6771   else
6772     return 0;
6773 }
6774 
6775 class constant_reg : public reg {
6776   const char *s;
6777 public:
6778   constant_reg(const char *);
6779   const char *get_string();
6780 };
6781 
constant_reg(const char * p)6782 constant_reg::constant_reg(const char *p) : s(p)
6783 {
6784 }
6785 
get_string()6786 const char *constant_reg::get_string()
6787 {
6788   return s;
6789 }
6790 
constant_int_reg(int * q)6791 constant_int_reg::constant_int_reg(int *q) : p(q)
6792 {
6793 }
6794 
get_string()6795 const char *constant_int_reg::get_string()
6796 {
6797   return i_to_a(*p);
6798 }
6799 
abort_request()6800 void abort_request()
6801 {
6802   int c;
6803   if (tok.eof())
6804     c = EOF;
6805   else if (tok.newline())
6806     c = '\n';
6807   else {
6808     while ((c = get_copy(0)) == ' ')
6809       ;
6810   }
6811   if (c == EOF || c == '\n')
6812     fputs("User Abort.", stderr);
6813   else {
6814     for (; c != '\n' && c != EOF; c = get_copy(0))
6815       fputs(asciify(c), stderr);
6816   }
6817   fputc('\n', stderr);
6818   cleanup_and_exit(1);
6819 }
6820 
read_string()6821 char *read_string()
6822 {
6823   int len = 256;
6824   char *s = new char[len];
6825   int c;
6826   while ((c = get_copy(0)) == ' ')
6827     ;
6828   int i = 0;
6829   while (c != '\n' && c != EOF) {
6830     if (!invalid_input_char(c)) {
6831       if (i + 2 > len) {
6832 	char *tem = s;
6833 	s = new char[len*2];
6834 	memcpy(s, tem, len);
6835 	len *= 2;
6836 	a_delete tem;
6837       }
6838       s[i++] = c;
6839     }
6840     c = get_copy(0);
6841   }
6842   s[i] = '\0';
6843   tok.next();
6844   if (i == 0) {
6845     a_delete s;
6846     return 0;
6847   }
6848   return s;
6849 }
6850 
pipe_output()6851 void pipe_output()
6852 {
6853   if (safer_flag) {
6854     error(".pi request not allowed in safer mode");
6855     skip_line();
6856   }
6857   else {
6858 #ifdef POPEN_MISSING
6859     error("pipes not available on this system");
6860     skip_line();
6861 #else /* not POPEN_MISSING */
6862     if (the_output) {
6863       error("can't pipe: output already started");
6864       skip_line();
6865     }
6866     else {
6867       char *pc;
6868       if ((pc = read_string()) == 0)
6869 	error("can't pipe to empty command");
6870       if (pipe_command) {
6871 	char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
6872 	strcpy(s, pipe_command);
6873 	strcat(s, "|");
6874 	strcat(s, pc);
6875 	a_delete pipe_command;
6876 	a_delete pc;
6877 	pipe_command = s;
6878       }
6879       else
6880         pipe_command = pc;
6881     }
6882 #endif /* not POPEN_MISSING */
6883   }
6884 }
6885 
6886 static int system_status;
6887 
system_request()6888 void system_request()
6889 {
6890   if (safer_flag) {
6891     error(".sy request not allowed in safer mode");
6892     skip_line();
6893   }
6894   else {
6895     char *command = read_string();
6896     if (!command)
6897       error("empty command");
6898     else {
6899       system_status = system(command);
6900       a_delete command;
6901     }
6902   }
6903 }
6904 
copy_file()6905 void copy_file()
6906 {
6907   if (curdiv == topdiv && topdiv->before_first_page) {
6908     handle_initial_request(COPY_FILE_REQUEST);
6909     return;
6910   }
6911   symbol filename = get_long_name(1);
6912   while (!tok.newline() && !tok.eof())
6913     tok.next();
6914   if (break_flag)
6915     curenv->do_break();
6916   if (!filename.is_null())
6917     curdiv->copy_file(filename.contents());
6918   tok.next();
6919 }
6920 
6921 #ifdef COLUMN
6922 
vjustify()6923 void vjustify()
6924 {
6925   if (curdiv == topdiv && topdiv->before_first_page) {
6926     handle_initial_request(VJUSTIFY_REQUEST);
6927     return;
6928   }
6929   symbol type = get_long_name(1);
6930   if (!type.is_null())
6931     curdiv->vjustify(type);
6932   skip_line();
6933 }
6934 
6935 #endif /* COLUMN */
6936 
transparent_file()6937 void transparent_file()
6938 {
6939   if (curdiv == topdiv && topdiv->before_first_page) {
6940     handle_initial_request(TRANSPARENT_FILE_REQUEST);
6941     return;
6942   }
6943   symbol filename = get_long_name(1);
6944   while (!tok.newline() && !tok.eof())
6945     tok.next();
6946   if (break_flag)
6947     curenv->do_break();
6948   if (!filename.is_null()) {
6949     errno = 0;
6950     FILE *fp = include_search_path.open_file_cautious(filename.contents());
6951     if (!fp)
6952       error("can't open `%1': %2", filename.contents(), strerror(errno));
6953     else {
6954       int bol = 1;
6955       for (;;) {
6956 	int c = getc(fp);
6957 	if (c == EOF)
6958 	  break;
6959 	if (invalid_input_char(c))
6960 	  warning(WARN_INPUT, "invalid input character code %1", int(c));
6961 	else {
6962 	  curdiv->transparent_output(c);
6963 	  bol = c == '\n';
6964 	}
6965       }
6966       if (!bol)
6967 	curdiv->transparent_output('\n');
6968       fclose(fp);
6969     }
6970   }
6971   tok.next();
6972 }
6973 
6974 class page_range {
6975   int first;
6976   int last;
6977 public:
6978   page_range *next;
6979   page_range(int, int, page_range *);
6980   int contains(int n);
6981 };
6982 
page_range(int i,int j,page_range * p)6983 page_range::page_range(int i, int j, page_range *p)
6984 : first(i), last(j), next(p)
6985 {
6986 }
6987 
contains(int n)6988 int page_range::contains(int n)
6989 {
6990   return n >= first && (last <= 0 || n <= last);
6991 }
6992 
6993 page_range *output_page_list = 0;
6994 
in_output_page_list(int n)6995 int in_output_page_list(int n)
6996 {
6997   if (!output_page_list)
6998     return 1;
6999   for (page_range *p = output_page_list; p; p = p->next)
7000     if (p->contains(n))
7001       return 1;
7002   return 0;
7003 }
7004 
parse_output_page_list(char * p)7005 static void parse_output_page_list(char *p)
7006 {
7007   for (;;) {
7008     int i;
7009     if (*p == '-')
7010       i = 1;
7011     else if (csdigit(*p)) {
7012       i = 0;
7013       do
7014 	i = i*10 + *p++ - '0';
7015       while (csdigit(*p));
7016     }
7017     else
7018       break;
7019     int j;
7020     if (*p == '-') {
7021       p++;
7022       j = 0;
7023       if (csdigit(*p)) {
7024 	do
7025 	  j = j*10 + *p++ - '0';
7026 	while (csdigit(*p));
7027       }
7028     }
7029     else
7030       j = i;
7031     if (j == 0)
7032       last_page_number = -1;
7033     else if (last_page_number >= 0 && j > last_page_number)
7034       last_page_number = j;
7035     output_page_list = new page_range(i, j, output_page_list);
7036     if (*p != ',')
7037       break;
7038     ++p;
7039   }
7040   if (*p != '\0') {
7041     error("bad output page list");
7042     output_page_list = 0;
7043   }
7044 }
7045 
open_mac_file(const char * mac,char ** path)7046 static FILE *open_mac_file(const char *mac, char **path)
7047 {
7048   // Try first FOOBAR.tmac, then tmac.FOOBAR
7049   char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7050   strcpy(s1, mac);
7051   strcat(s1, MACRO_POSTFIX);
7052   FILE *fp = mac_path->open_file(s1, path);
7053   a_delete s1;
7054   if (!fp) {
7055     char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7056     strcpy(s2, MACRO_PREFIX);
7057     strcat(s2, mac);
7058     fp = mac_path->open_file(s2, path);
7059     a_delete s2;
7060   }
7061   return fp;
7062 }
7063 
process_macro_file(const char * mac)7064 static void process_macro_file(const char *mac)
7065 {
7066   char *path;
7067   FILE *fp = open_mac_file(mac, &path);
7068   if (!fp)
7069     fatal("can't find macro file %1", mac);
7070   const char *s = symbol(path).contents();
7071   a_delete path;
7072   input_stack::push(new file_iterator(fp, s));
7073   tok.next();
7074   process_input_stack();
7075 }
7076 
process_startup_file(const char * filename)7077 static void process_startup_file(const char *filename)
7078 {
7079   char *path;
7080   search_path *orig_mac_path = mac_path;
7081   mac_path = &config_macro_path;
7082   FILE *fp = mac_path->open_file(filename, &path);
7083   if (fp) {
7084     input_stack::push(new file_iterator(fp, symbol(path).contents()));
7085     a_delete path;
7086     tok.next();
7087     process_input_stack();
7088   }
7089   mac_path = orig_mac_path;
7090 }
7091 
macro_source()7092 void macro_source()
7093 {
7094   symbol nm = get_long_name(1);
7095   if (nm.is_null())
7096     skip_line();
7097   else {
7098     while (!tok.newline() && !tok.eof())
7099       tok.next();
7100     char *path;
7101     FILE *fp = mac_path->open_file(nm.contents(), &path);
7102     // .mso doesn't (and cannot) go through open_mac_file, so we
7103     // need to do it here manually: If we have tmac.FOOBAR, try
7104     // FOOBAR.tmac and vice versa
7105     if (!fp) {
7106       const char *fn = nm.contents();
7107       if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7108 	char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7109 	strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7110 	strcat(s, MACRO_POSTFIX);
7111 	fp = mac_path->open_file(s, &path);
7112 	a_delete s;
7113       }
7114       if (!fp) {
7115 	if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7116 			MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7117 	  char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7118 	  strcpy(s, MACRO_PREFIX);
7119 	  strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7120 	  fp = mac_path->open_file(s, &path);
7121 	  a_delete s;
7122 	}
7123       }
7124     }
7125     if (fp) {
7126       input_stack::push(new file_iterator(fp, symbol(path).contents()));
7127       a_delete path;
7128     }
7129     else
7130       error("can't find macro file `%1'", nm.contents());
7131     tok.next();
7132   }
7133 }
7134 
process_input_file(const char * name)7135 static void process_input_file(const char *name)
7136 {
7137   FILE *fp;
7138   if (strcmp(name, "-") == 0) {
7139     clearerr(stdin);
7140     fp = stdin;
7141   }
7142   else {
7143     errno = 0;
7144     fp = include_search_path.open_file_cautious(name);
7145     if (!fp)
7146       fatal("can't open `%1': %2", name, strerror(errno));
7147   }
7148   input_stack::push(new file_iterator(fp, name));
7149   tok.next();
7150   process_input_stack();
7151 }
7152 
7153 // make sure the_input is empty before calling this
7154 
evaluate_expression(const char * expr,units * res)7155 static int evaluate_expression(const char *expr, units *res)
7156 {
7157   input_stack::push(make_temp_iterator(expr));
7158   tok.next();
7159   int success = get_number(res, 'u');
7160   while (input_stack::get(0) != EOF)
7161     ;
7162   return success;
7163 }
7164 
do_register_assignment(const char * s)7165 static void do_register_assignment(const char *s)
7166 {
7167   const char *p = strchr(s, '=');
7168   if (!p) {
7169     char buf[2];
7170     buf[0] = s[0];
7171     buf[1] = 0;
7172     units n;
7173     if (evaluate_expression(s + 1, &n))
7174       set_number_reg(buf, n);
7175   }
7176   else {
7177     char *buf = new char[p - s + 1];
7178     memcpy(buf, s, p - s);
7179     buf[p - s] = 0;
7180     units n;
7181     if (evaluate_expression(p + 1, &n))
7182       set_number_reg(buf, n);
7183     a_delete buf;
7184   }
7185 }
7186 
set_string(const char * name,const char * value)7187 static void set_string(const char *name, const char *value)
7188 {
7189   macro *m = new macro;
7190   for (const char *p = value; *p; p++)
7191     if (!invalid_input_char((unsigned char)*p))
7192       m->append(*p);
7193   request_dictionary.define(name, m);
7194 }
7195 
do_string_assignment(const char * s)7196 static void do_string_assignment(const char *s)
7197 {
7198   const char *p = strchr(s, '=');
7199   if (!p) {
7200     char buf[2];
7201     buf[0] = s[0];
7202     buf[1] = 0;
7203     set_string(buf, s + 1);
7204   }
7205   else {
7206     char *buf = new char[p - s + 1];
7207     memcpy(buf, s, p - s);
7208     buf[p - s] = 0;
7209     set_string(buf, p + 1);
7210     a_delete buf;
7211   }
7212 }
7213 
7214 struct string_list {
7215   const char *s;
7216   string_list *next;
string_liststring_list7217   string_list(const char *ss) : s(ss), next(0) {}
7218 };
7219 
7220 #if 0
7221 static void prepend_string(const char *s, string_list **p)
7222 {
7223   string_list *l = new string_list(s);
7224   l->next = *p;
7225   *p = l;
7226 }
7227 #endif
7228 
add_string(const char * s,string_list ** p)7229 static void add_string(const char *s, string_list **p)
7230 {
7231   while (*p)
7232     p = &((*p)->next);
7233   *p = new string_list(s);
7234 }
7235 
usage(FILE * stream,const char * prog)7236 void usage(FILE *stream, const char *prog)
7237 {
7238   fprintf(stream,
7239 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7240 "       -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7241 	  prog);
7242 }
7243 
main(int argc,char ** argv)7244 int main(int argc, char **argv)
7245 {
7246   program_name = argv[0];
7247   static char stderr_buf[BUFSIZ];
7248   setbuf(stderr, stderr_buf);
7249   int c;
7250   string_list *macros = 0;
7251   string_list *register_assignments = 0;
7252   string_list *string_assignments = 0;
7253   int iflag = 0;
7254   int tflag = 0;
7255   int fflag = 0;
7256   int nflag = 0;
7257   int no_rc = 0;		// don't process troffrc and troffrc-end
7258   int next_page_number = 0;	// pacify compiler
7259   opterr = 0;
7260   hresolution = vresolution = 1;
7261   // restore $PATH if called from groff
7262   char* groff_path = getenv("GROFF_PATH__");
7263   if (groff_path) {
7264     string e = "PATH";
7265     e += '=';
7266     if (*groff_path)
7267       e += groff_path;
7268     e += '\0';
7269     if (putenv(strsave(e.contents())))
7270       fatal("putenv failed");
7271   }
7272   static const struct option long_options[] = {
7273     { "help", no_argument, 0, CHAR_MAX + 1 },
7274     { "version", no_argument, 0, 'v' },
7275     { 0, 0, 0, 0 }
7276   };
7277 #if defined(DEBUGGING)
7278 #define DEBUG_OPTION "D"
7279 #endif
7280   while ((c = getopt_long(argc, argv,
7281 			  "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7282 			  DEBUG_OPTION, long_options, 0))
7283 	 != EOF)
7284     switch(c) {
7285     case 'v':
7286       {
7287 	printf("GNU troff (groff) version %s\n", Version_string);
7288 	exit(0);
7289 	break;
7290       }
7291     case 'I':
7292       // Search path for .psbb files
7293       // and most other non-system input files.
7294       include_search_path.command_line_dir(optarg);
7295       break;
7296     case 'T':
7297       device = optarg;
7298       tflag = 1;
7299       is_html = (strcmp(device, "html") == 0);
7300       break;
7301     case 'C':
7302       compatible_flag = 1;
7303       // fall through
7304     case 'c':
7305       color_flag = 0;
7306       break;
7307     case 'M':
7308       macro_path.command_line_dir(optarg);
7309       safer_macro_path.command_line_dir(optarg);
7310       config_macro_path.command_line_dir(optarg);
7311       break;
7312     case 'F':
7313       font::command_line_font_dir(optarg);
7314       break;
7315     case 'm':
7316       add_string(optarg, &macros);
7317       break;
7318     case 'E':
7319       inhibit_errors = 1;
7320       break;
7321     case 'R':
7322       no_rc = 1;
7323       break;
7324     case 'w':
7325       enable_warning(optarg);
7326       break;
7327     case 'W':
7328       disable_warning(optarg);
7329       break;
7330     case 'i':
7331       iflag = 1;
7332       break;
7333     case 'b':
7334       backtrace_flag = 1;
7335       break;
7336     case 'a':
7337       ascii_output_flag = 1;
7338       break;
7339     case 'z':
7340       suppress_output_flag = 1;
7341       break;
7342     case 'n':
7343       if (sscanf(optarg, "%d", &next_page_number) == 1)
7344 	nflag++;
7345       else
7346 	error("bad page number");
7347       break;
7348     case 'o':
7349       parse_output_page_list(optarg);
7350       break;
7351     case 'd':
7352       if (*optarg == '\0')
7353 	error("`-d' requires non-empty argument");
7354       else
7355 	add_string(optarg, &string_assignments);
7356       break;
7357     case 'r':
7358       if (*optarg == '\0')
7359 	error("`-r' requires non-empty argument");
7360       else
7361 	add_string(optarg, &register_assignments);
7362       break;
7363     case 'f':
7364       default_family = symbol(optarg);
7365       fflag = 1;
7366       break;
7367     case 'q':
7368     case 's':
7369     case 't':
7370       // silently ignore these
7371       break;
7372     case 'U':
7373       safer_flag = 0;	// unsafe behaviour
7374       break;
7375 #if defined(DEBUGGING)
7376     case 'D':
7377       debug_state = 1;
7378       break;
7379 #endif
7380     case CHAR_MAX + 1: // --help
7381       usage(stdout, argv[0]);
7382       exit(0);
7383       break;
7384     case '?':
7385       usage(stderr, argv[0]);
7386       exit(1);
7387       break;		// never reached
7388     default:
7389       assert(0);
7390     }
7391   if (!safer_flag)
7392     mac_path = &macro_path;
7393   set_string(".T", device);
7394   init_charset_table();
7395   init_hpf_code_table();
7396   if (!font::load_desc())
7397     fatal("sorry, I can't continue");
7398   units_per_inch = font::res;
7399   hresolution = font::hor;
7400   vresolution = font::vert;
7401   sizescale = font::sizescale;
7402   tcommand_flag = font::tcommand;
7403   warn_scale = (double)units_per_inch;
7404   warn_scaling_indicator = 'i';
7405   if (!fflag && font::family != 0 && *font::family != '\0')
7406     default_family = symbol(font::family);
7407   font_size::init_size_table(font::sizes);
7408   int i;
7409   int j = 1;
7410   if (font::style_table) {
7411     for (i = 0; font::style_table[i]; i++)
7412       mount_style(j++, symbol(font::style_table[i]));
7413   }
7414   for (i = 0; font::font_name_table[i]; i++, j++)
7415     // In the DESC file a font name of 0 (zero) means leave this
7416     // position empty.
7417     if (strcmp(font::font_name_table[i], "0") != 0)
7418       mount_font(j, symbol(font::font_name_table[i]));
7419   curdiv = topdiv = new top_level_diversion;
7420   if (nflag)
7421     topdiv->set_next_page_number(next_page_number);
7422   init_input_requests();
7423   init_env_requests();
7424   init_div_requests();
7425 #ifdef COLUMN
7426   init_column_requests();
7427 #endif /* COLUMN */
7428   init_node_requests();
7429   number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7430   init_registers();
7431   init_reg_requests();
7432   init_hyphen_requests();
7433   init_environments();
7434   while (string_assignments) {
7435     do_string_assignment(string_assignments->s);
7436     string_list *tem = string_assignments;
7437     string_assignments = string_assignments->next;
7438     delete tem;
7439   }
7440   while (register_assignments) {
7441     do_register_assignment(register_assignments->s);
7442     string_list *tem = register_assignments;
7443     register_assignments = register_assignments->next;
7444     delete tem;
7445   }
7446   if (!no_rc)
7447     process_startup_file(INITIAL_STARTUP_FILE);
7448   while (macros) {
7449     process_macro_file(macros->s);
7450     string_list *tem = macros;
7451     macros = macros->next;
7452     delete tem;
7453   }
7454   if (!no_rc)
7455     process_startup_file(FINAL_STARTUP_FILE);
7456   for (i = optind; i < argc; i++)
7457     process_input_file(argv[i]);
7458   if (optind >= argc || iflag)
7459     process_input_file("-");
7460   exit_troff();
7461   return 0;			// not reached
7462 }
7463 
warn_request()7464 void warn_request()
7465 {
7466   int n;
7467   if (has_arg() && get_integer(&n)) {
7468     if (n & ~WARN_TOTAL) {
7469       warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7470       n &= WARN_TOTAL;
7471     }
7472     warning_mask = n;
7473   }
7474   else
7475     warning_mask = WARN_TOTAL;
7476   skip_line();
7477 }
7478 
init_registers()7479 static void init_registers()
7480 {
7481 #ifdef LONG_FOR_TIME_T
7482   long
7483 #else /* not LONG_FOR_TIME_T */
7484   time_t
7485 #endif /* not LONG_FOR_TIME_T */
7486     t = time(0);
7487   // Use struct here to work around misfeature in old versions of g++.
7488   struct tm *tt = localtime(&t);
7489   set_number_reg("seconds", int(tt->tm_sec));
7490   set_number_reg("minutes", int(tt->tm_min));
7491   set_number_reg("hours", int(tt->tm_hour));
7492   set_number_reg("dw", int(tt->tm_wday + 1));
7493   set_number_reg("dy", int(tt->tm_mday));
7494   set_number_reg("mo", int(tt->tm_mon + 1));
7495   set_number_reg("year", int(1900 + tt->tm_year));
7496   set_number_reg("yr", int(tt->tm_year));
7497   set_number_reg("$$", getpid());
7498   number_reg_dictionary.define(".A",
7499 			       new constant_reg(ascii_output_flag
7500 						? "1"
7501 						: "0"));
7502 }
7503 
7504 /*
7505  *  registers associated with \O
7506  */
7507 
7508 static int output_reg_minx_contents = -1;
7509 static int output_reg_miny_contents = -1;
7510 static int output_reg_maxx_contents = -1;
7511 static int output_reg_maxy_contents = -1;
7512 
check_output_limits(int x,int y)7513 void check_output_limits(int x, int y)
7514 {
7515   if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7516     output_reg_minx_contents = x;
7517   if (x > output_reg_maxx_contents)
7518     output_reg_maxx_contents = x;
7519   if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7520     output_reg_miny_contents = y;
7521   if (y > output_reg_maxy_contents)
7522     output_reg_maxy_contents = y;
7523 }
7524 
reset_output_registers()7525 void reset_output_registers()
7526 {
7527   output_reg_minx_contents = -1;
7528   output_reg_miny_contents = -1;
7529   output_reg_maxx_contents = -1;
7530   output_reg_maxy_contents = -1;
7531 }
7532 
get_output_registers(int * minx,int * miny,int * maxx,int * maxy)7533 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7534 {
7535   *minx = output_reg_minx_contents;
7536   *miny = output_reg_miny_contents;
7537   *maxx = output_reg_maxx_contents;
7538   *maxy = output_reg_maxy_contents;
7539 }
7540 
init_input_requests()7541 void init_input_requests()
7542 {
7543   init_request("ab", abort_request);
7544   init_request("als", alias_macro);
7545   init_request("am", append_macro);
7546   init_request("am1", append_nocomp_macro);
7547   init_request("ami", append_indirect_macro);
7548   init_request("ami1", append_indirect_nocomp_macro);
7549   init_request("as", append_string);
7550   init_request("as1", append_nocomp_string);
7551   init_request("asciify", asciify_macro);
7552   init_request("backtrace", backtrace_request);
7553   init_request("blm", blank_line_macro);
7554   init_request("break", while_break_request);
7555   init_request("cf", copy_file);
7556   init_request("cflags", char_flags);
7557   init_request("char", define_character);
7558   init_request("chop", chop_macro);
7559   init_request("close", close_request);
7560   init_request("color", activate_color);
7561   init_request("composite", composite_request);
7562   init_request("continue", while_continue_request);
7563   init_request("cp", compatible);
7564   init_request("de", define_macro);
7565   init_request("de1", define_nocomp_macro);
7566   init_request("defcolor", define_color);
7567   init_request("dei", define_indirect_macro);
7568   init_request("dei1", define_indirect_nocomp_macro);
7569   init_request("do", do_request);
7570   init_request("ds", define_string);
7571   init_request("ds1", define_nocomp_string);
7572   init_request("ec", set_escape_char);
7573   init_request("ecr", restore_escape_char);
7574   init_request("ecs", save_escape_char);
7575   init_request("el", else_request);
7576   init_request("em", end_macro);
7577   init_request("eo", escape_off);
7578   init_request("ex", exit_request);
7579   init_request("fchar", define_fallback_character);
7580 #ifdef WIDOW_CONTROL
7581   init_request("fpl", flush_pending_lines);
7582 #endif /* WIDOW_CONTROL */
7583   init_request("hcode", hyphenation_code);
7584   init_request("hpfcode", hyphenation_patterns_file_code);
7585   init_request("ie", if_else_request);
7586   init_request("if", if_request);
7587   init_request("ig", ignore);
7588   init_request("length", length_request);
7589   init_request("lf", line_file);
7590   init_request("mso", macro_source);
7591   init_request("nop", nop_request);
7592   init_request("nroff", nroff_request);
7593   init_request("nx", next_file);
7594   init_request("open", open_request);
7595   init_request("opena", opena_request);
7596   init_request("output", output_request);
7597   init_request("pc", set_page_character);
7598   init_request("pi", pipe_output);
7599   init_request("pm", print_macros);
7600   init_request("psbb", ps_bbox_request);
7601 #ifndef POPEN_MISSING
7602   init_request("pso", pipe_source);
7603 #endif /* not POPEN_MISSING */
7604   init_request("rchar", remove_character);
7605   init_request("rd", read_request);
7606   init_request("return", return_macro_request);
7607   init_request("rm", remove_macro);
7608   init_request("rn", rename_macro);
7609   init_request("schar", define_special_character);
7610   init_request("shift", shift);
7611   init_request("so", source);
7612   init_request("spreadwarn", spreadwarn_request);
7613   init_request("substring", substring_request);
7614   init_request("sy", system_request);
7615   init_request("tag", tag);
7616   init_request("taga", taga);
7617   init_request("tm", terminal);
7618   init_request("tm1", terminal1);
7619   init_request("tmc", terminal_continue);
7620   init_request("tr", translate);
7621   init_request("trf", transparent_file);
7622   init_request("trin", translate_input);
7623   init_request("trnt", translate_no_transparent);
7624   init_request("troff", troff_request);
7625   init_request("unformat", unformat_macro);
7626 #ifdef COLUMN
7627   init_request("vj", vjustify);
7628 #endif /* COLUMN */
7629   init_request("warn", warn_request);
7630   init_request("warnscale", warnscale_request);
7631   init_request("while", while_request);
7632   init_request("write", write_request);
7633   init_request("writec", write_request_continue);
7634   init_request("writem", write_macro_request);
7635   number_reg_dictionary.define(".$", new nargs_reg);
7636   number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7637   number_reg_dictionary.define(".c", new lineno_reg);
7638   number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7639   number_reg_dictionary.define(".F", new filename_reg);
7640   number_reg_dictionary.define(".g", new constant_reg("1"));
7641   number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7642   number_reg_dictionary.define(".R", new constant_reg("10000"));
7643   number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag));
7644   number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7645   number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7646   extern const char *major_version;
7647   number_reg_dictionary.define(".x", new constant_reg(major_version));
7648   extern const char *revision;
7649   number_reg_dictionary.define(".Y", new constant_reg(revision));
7650   extern const char *minor_version;
7651   number_reg_dictionary.define(".y", new constant_reg(minor_version));
7652   number_reg_dictionary.define("c.", new writable_lineno_reg);
7653   number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7654   number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7655   number_reg_dictionary.define("opmaxx",
7656 			       new variable_reg(&output_reg_maxx_contents));
7657   number_reg_dictionary.define("opmaxy",
7658 			       new variable_reg(&output_reg_maxy_contents));
7659   number_reg_dictionary.define("opminx",
7660 			       new variable_reg(&output_reg_minx_contents));
7661   number_reg_dictionary.define("opminy",
7662 			       new variable_reg(&output_reg_miny_contents));
7663   number_reg_dictionary.define("slimit",
7664 			       new variable_reg(&input_stack::limit));
7665   number_reg_dictionary.define("systat", new variable_reg(&system_status));
7666   number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7667   number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7668 }
7669 
7670 object_dictionary request_dictionary(501);
7671 
init_request(const char * s,REQUEST_FUNCP f)7672 void init_request(const char *s, REQUEST_FUNCP f)
7673 {
7674   request_dictionary.define(s, new request(f));
7675 }
7676 
lookup_request(symbol nm)7677 static request_or_macro *lookup_request(symbol nm)
7678 {
7679   assert(!nm.is_null());
7680   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7681   if (p == 0) {
7682     warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7683     p = new macro;
7684     request_dictionary.define(nm, p);
7685   }
7686   return p;
7687 }
7688 
charinfo_to_node_list(charinfo * ci,const environment * envp)7689 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7690 {
7691   // Don't interpret character definitions in compatible mode.
7692   int old_compatible_flag = compatible_flag;
7693   compatible_flag = 0;
7694   int old_escape_char = escape_char;
7695   escape_char = '\\';
7696   macro *mac = ci->set_macro(0);
7697   assert(mac != 0);
7698   environment *oldenv = curenv;
7699   environment env(envp);
7700   curenv = &env;
7701   curenv->set_composite();
7702   token old_tok = tok;
7703   input_stack::add_boundary();
7704   string_iterator *si =
7705     new string_iterator(*mac, "composite character", ci->nm);
7706   input_stack::push(si);
7707   // we don't use process_input_stack, because we don't want to recognise
7708   // requests
7709   for (;;) {
7710     tok.next();
7711     if (tok.eof())
7712       break;
7713     if (tok.newline()) {
7714       error("composite character mustn't contain newline");
7715       while (!tok.eof())
7716 	tok.next();
7717       break;
7718     }
7719     else
7720       tok.process();
7721   }
7722   node *n = curenv->extract_output_line();
7723   input_stack::remove_boundary();
7724   ci->set_macro(mac);
7725   tok = old_tok;
7726   curenv = oldenv;
7727   compatible_flag = old_compatible_flag;
7728   escape_char = old_escape_char;
7729   have_input = 0;
7730   return n;
7731 }
7732 
read_draw_node()7733 static node *read_draw_node()
7734 {
7735   token start;
7736   start.next();
7737   if (!start.delimiter(1)){
7738     do {
7739       tok.next();
7740     } while (tok != start && !tok.newline() && !tok.eof());
7741   }
7742   else {
7743     tok.next();
7744     if (tok == start)
7745       error("missing argument");
7746     else {
7747       unsigned char type = tok.ch();
7748       if (type == 'F') {
7749 	read_color_draw_node(start);
7750 	return 0;
7751       }
7752       tok.next();
7753       int maxpoints = 10;
7754       hvpair *point = new hvpair[maxpoints];
7755       int npoints = 0;
7756       int no_last_v = 0;
7757       int err = 0;
7758       int i;
7759       for (i = 0; tok != start; i++) {
7760 	if (i == maxpoints) {
7761 	  hvpair *oldpoint = point;
7762 	  point = new hvpair[maxpoints*2];
7763 	  for (int j = 0; j < maxpoints; j++)
7764 	    point[j] = oldpoint[j];
7765 	  maxpoints *= 2;
7766 	  a_delete oldpoint;
7767 	}
7768 	if (!get_hunits(&point[i].h,
7769 			type == 'f' || type == 't' ? 'u' : 'm')) {
7770 	  err = 1;
7771 	  break;
7772 	}
7773 	++npoints;
7774 	tok.skip();
7775 	point[i].v = V0;
7776 	if (tok == start) {
7777 	  no_last_v = 1;
7778 	  break;
7779 	}
7780 	if (!get_vunits(&point[i].v, 'v')) {
7781 	  err = 1;
7782 	  break;
7783 	}
7784 	tok.skip();
7785       }
7786       while (tok != start && !tok.newline() && !tok.eof())
7787 	tok.next();
7788       if (!err) {
7789 	switch (type) {
7790 	case 'l':
7791 	  if (npoints != 1 || no_last_v) {
7792 	    error("two arguments needed for line");
7793 	    npoints = 1;
7794 	  }
7795 	  break;
7796 	case 'c':
7797 	  if (npoints != 1 || !no_last_v) {
7798 	    error("one argument needed for circle");
7799 	    npoints = 1;
7800 	    point[0].v = V0;
7801 	  }
7802 	  break;
7803 	case 'e':
7804 	  if (npoints != 1 || no_last_v) {
7805 	    error("two arguments needed for ellipse");
7806 	    npoints = 1;
7807 	  }
7808 	  break;
7809 	case 'a':
7810 	  if (npoints != 2 || no_last_v) {
7811 	    error("four arguments needed for arc");
7812 	    npoints = 2;
7813 	  }
7814 	  break;
7815 	case '~':
7816 	  if (no_last_v)
7817 	    error("even number of arguments needed for spline");
7818 	  break;
7819 	case 'f':
7820 	  if (npoints != 1 || !no_last_v) {
7821 	    error("one argument needed for gray shade");
7822 	    npoints = 1;
7823 	    point[0].v = V0;
7824 	  }
7825 	default:
7826 	  // silently pass it through
7827 	  break;
7828 	}
7829 	draw_node *dn = new draw_node(type, point, npoints,
7830 				      curenv->get_font_size(),
7831 				      curenv->get_glyph_color(),
7832 				      curenv->get_fill_color());
7833 	a_delete point;
7834 	return dn;
7835       }
7836       else {
7837 	a_delete point;
7838       }
7839     }
7840   }
7841   return 0;
7842 }
7843 
read_color_draw_node(token & start)7844 static void read_color_draw_node(token &start)
7845 {
7846   tok.next();
7847   if (tok == start) {
7848     error("missing color scheme");
7849     return;
7850   }
7851   unsigned char scheme = tok.ch();
7852   tok.next();
7853   color *col = 0;
7854   char end = start.ch();
7855   switch (scheme) {
7856   case 'c':
7857     col = read_cmy(end);
7858     break;
7859   case 'd':
7860     col = &default_color;
7861     break;
7862   case 'g':
7863     col = read_gray(end);
7864     break;
7865   case 'k':
7866     col = read_cmyk(end);
7867     break;
7868   case 'r':
7869     col = read_rgb(end);
7870     break;
7871   }
7872   if (col)
7873     curenv->set_fill_color(col);
7874   while (tok != start) {
7875     if (tok.newline() || tok.eof()) {
7876       warning(WARN_DELIM, "missing closing delimiter");
7877       input_stack::push(make_temp_iterator("\n"));
7878       break;
7879     }
7880     tok.next();
7881   }
7882   have_input = 1;
7883 }
7884 
7885 static struct {
7886   const char *name;
7887   int mask;
7888 } warning_table[] = {
7889   { "char", WARN_CHAR },
7890   { "range", WARN_RANGE },
7891   { "break", WARN_BREAK },
7892   { "delim", WARN_DELIM },
7893   { "el", WARN_EL },
7894   { "scale", WARN_SCALE },
7895   { "number", WARN_NUMBER },
7896   { "syntax", WARN_SYNTAX },
7897   { "tab", WARN_TAB },
7898   { "right-brace", WARN_RIGHT_BRACE },
7899   { "missing", WARN_MISSING },
7900   { "input", WARN_INPUT },
7901   { "escape", WARN_ESCAPE },
7902   { "space", WARN_SPACE },
7903   { "font", WARN_FONT },
7904   { "di", WARN_DI },
7905   { "mac", WARN_MAC },
7906   { "reg", WARN_REG },
7907   { "ig", WARN_IG },
7908   { "color", WARN_COLOR },
7909   { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
7910   { "w", WARN_TOTAL },
7911   { "default", DEFAULT_WARNING_MASK },
7912 };
7913 
lookup_warning(const char * name)7914 static int lookup_warning(const char *name)
7915 {
7916   for (unsigned int i = 0;
7917        i < sizeof(warning_table)/sizeof(warning_table[0]);
7918        i++)
7919     if (strcmp(name, warning_table[i].name) == 0)
7920       return warning_table[i].mask;
7921   return 0;
7922 }
7923 
enable_warning(const char * name)7924 static void enable_warning(const char *name)
7925 {
7926   int mask = lookup_warning(name);
7927   if (mask)
7928     warning_mask |= mask;
7929   else
7930     error("unknown warning `%1'", name);
7931 }
7932 
disable_warning(const char * name)7933 static void disable_warning(const char *name)
7934 {
7935   int mask = lookup_warning(name);
7936   if (mask)
7937     warning_mask &= ~mask;
7938   else
7939     error("unknown warning `%1'", name);
7940 }
7941 
copy_mode_error(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)7942 static void copy_mode_error(const char *format,
7943 			    const errarg &arg1,
7944 			    const errarg &arg2,
7945 			    const errarg &arg3)
7946 {
7947   if (ignoring) {
7948     static const char prefix[] = "(in ignored input) ";
7949     char *s = new char[sizeof(prefix) + strlen(format)];
7950     strcpy(s, prefix);
7951     strcat(s, format);
7952     warning(WARN_IG, s, arg1, arg2, arg3);
7953     a_delete s;
7954   }
7955   else
7956     error(format, arg1, arg2, arg3);
7957 }
7958 
7959 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
7960 
do_error(error_type type,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)7961 static void do_error(error_type type,
7962 		     const char *format,
7963 		     const errarg &arg1,
7964 		     const errarg &arg2,
7965 		     const errarg &arg3)
7966 {
7967   const char *filename;
7968   int lineno;
7969   if (inhibit_errors && type < FATAL)
7970     return;
7971   if (backtrace_flag)
7972     input_stack::backtrace();
7973   if (!get_file_line(&filename, &lineno))
7974     filename = 0;
7975   if (filename)
7976     errprint("%1:%2: ", filename, lineno);
7977   else if (program_name)
7978     fprintf(stderr, "%s: ", program_name);
7979   switch (type) {
7980   case FATAL:
7981     fputs("fatal error: ", stderr);
7982     break;
7983   case ERROR:
7984     break;
7985   case WARNING:
7986     fputs("warning: ", stderr);
7987     break;
7988   case OUTPUT_WARNING:
7989     double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
7990     fprintf(stderr, "warning [p %d, %.1f%c",
7991 	    topdiv->get_page_number(), fromtop, warn_scaling_indicator);
7992     if (topdiv != curdiv) {
7993       double fromtop1 = curdiv->get_vertical_position().to_units()
7994 			/ warn_scale;
7995       fprintf(stderr, ", div `%s', %.1f%c",
7996 	      curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
7997     }
7998     fprintf(stderr, "]: ");
7999     break;
8000   }
8001   errprint(format, arg1, arg2, arg3);
8002   fputc('\n', stderr);
8003   fflush(stderr);
8004   if (type == FATAL)
8005     cleanup_and_exit(1);
8006 }
8007 
warning(warning_type t,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8008 int warning(warning_type t,
8009 	    const char *format,
8010 	    const errarg &arg1,
8011 	    const errarg &arg2,
8012 	    const errarg &arg3)
8013 {
8014   if ((t & warning_mask) != 0) {
8015     do_error(WARNING, format, arg1, arg2, arg3);
8016     return 1;
8017   }
8018   else
8019     return 0;
8020 }
8021 
output_warning(warning_type t,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8022 int output_warning(warning_type t,
8023 		   const char *format,
8024 		   const errarg &arg1,
8025 		   const errarg &arg2,
8026 		   const errarg &arg3)
8027 {
8028   if ((t & warning_mask) != 0) {
8029     do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8030     return 1;
8031   }
8032   else
8033     return 0;
8034 }
8035 
error(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8036 void error(const char *format,
8037 	   const errarg &arg1,
8038 	   const errarg &arg2,
8039 	   const errarg &arg3)
8040 {
8041   do_error(ERROR, format, arg1, arg2, arg3);
8042 }
8043 
fatal(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8044 void fatal(const char *format,
8045 	   const errarg &arg1,
8046 	   const errarg &arg2,
8047 	   const errarg &arg3)
8048 {
8049   do_error(FATAL, format, arg1, arg2, arg3);
8050 }
8051 
fatal_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8052 void fatal_with_file_and_line(const char *filename, int lineno,
8053 			      const char *format,
8054 			      const errarg &arg1,
8055 			      const errarg &arg2,
8056 			      const errarg &arg3)
8057 {
8058   fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8059   errprint(format, arg1, arg2, arg3);
8060   fputc('\n', stderr);
8061   fflush(stderr);
8062   cleanup_and_exit(1);
8063 }
8064 
error_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)8065 void error_with_file_and_line(const char *filename, int lineno,
8066 			      const char *format,
8067 			      const errarg &arg1,
8068 			      const errarg &arg2,
8069 			      const errarg &arg3)
8070 {
8071   fprintf(stderr, "%s:%d: error: ", filename, lineno);
8072   errprint(format, arg1, arg2, arg3);
8073   fputc('\n', stderr);
8074   fflush(stderr);
8075 }
8076 
8077 dictionary charinfo_dictionary(501);
8078 
get_charinfo(symbol nm)8079 charinfo *get_charinfo(symbol nm)
8080 {
8081   void *p = charinfo_dictionary.lookup(nm);
8082   if (p != 0)
8083     return (charinfo *)p;
8084   charinfo *cp = new charinfo(nm);
8085   (void)charinfo_dictionary.lookup(nm, cp);
8086   return cp;
8087 }
8088 
8089 int charinfo::next_index = 0;
8090 
charinfo(symbol s)8091 charinfo::charinfo(symbol s)
8092 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8093   hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8094   not_found(0), transparent_translate(1), translate_input(0),
8095   mode(CHAR_NORMAL), nm(s)
8096 {
8097   index = next_index++;
8098 }
8099 
set_hyphenation_code(unsigned char c)8100 void charinfo::set_hyphenation_code(unsigned char c)
8101 {
8102   hyphenation_code = c;
8103 }
8104 
set_translation(charinfo * ci,int tt,int ti)8105 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8106 {
8107   translation = ci;
8108   if (ci && ti) {
8109     if (hyphenation_code != 0)
8110       ci->set_hyphenation_code(hyphenation_code);
8111     if (asciify_code != 0)
8112       ci->set_asciify_code(asciify_code);
8113     else if (ascii_code != 0)
8114       ci->set_asciify_code(ascii_code);
8115     ci->set_translation_input();
8116   }
8117   special_translation = TRANSLATE_NONE;
8118   transparent_translate = tt;
8119 }
8120 
set_special_translation(int c,int tt)8121 void charinfo::set_special_translation(int c, int tt)
8122 {
8123   special_translation = c;
8124   translation = 0;
8125   transparent_translate = tt;
8126 }
8127 
set_ascii_code(unsigned char c)8128 void charinfo::set_ascii_code(unsigned char c)
8129 {
8130   ascii_code = c;
8131 }
8132 
set_asciify_code(unsigned char c)8133 void charinfo::set_asciify_code(unsigned char c)
8134 {
8135   asciify_code = c;
8136 }
8137 
set_macro(macro * m)8138 macro *charinfo::set_macro(macro *m)
8139 {
8140   macro *tem = mac;
8141   mac = m;
8142   return tem;
8143 }
8144 
setx_macro(macro * m,char_mode cm)8145 macro *charinfo::setx_macro(macro *m, char_mode cm)
8146 {
8147   macro *tem = mac;
8148   mac = m;
8149   mode = cm;
8150   return tem;
8151 }
8152 
set_number(int n)8153 void charinfo::set_number(int n)
8154 {
8155   number = n;
8156   flags |= NUMBERED;
8157 }
8158 
get_number()8159 int charinfo::get_number()
8160 {
8161   assert(flags & NUMBERED);
8162   return number;
8163 }
8164 
8165 symbol UNNAMED_SYMBOL("---");
8166 
8167 // For numbered characters not between 0 and 255, we make a symbol out
8168 // of the number and store them in this dictionary.
8169 
8170 dictionary numbered_charinfo_dictionary(11);
8171 
get_charinfo_by_number(int n)8172 charinfo *get_charinfo_by_number(int n)
8173 {
8174   static charinfo *number_table[256];
8175 
8176   if (n >= 0 && n < 256) {
8177     charinfo *ci = number_table[n];
8178     if (!ci) {
8179       ci = new charinfo(UNNAMED_SYMBOL);
8180       ci->set_number(n);
8181       number_table[n] = ci;
8182     }
8183     return ci;
8184   }
8185   else {
8186     symbol ns(i_to_a(n));
8187     charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8188     if (!ci) {
8189       ci = new charinfo(UNNAMED_SYMBOL);
8190       ci->set_number(n);
8191       (void)numbered_charinfo_dictionary.lookup(ns, ci);
8192     }
8193     return ci;
8194   }
8195 }
8196 
name_to_index(const char * nm)8197 int font::name_to_index(const char *nm)
8198 {
8199   charinfo *ci;
8200   if (nm[1] == 0)
8201     ci = charset_table[nm[0] & 0xff];
8202   else if (nm[0] == '\\' && nm[2] == 0)
8203     ci = get_charinfo(symbol(nm + 1));
8204   else
8205     ci = get_charinfo(symbol(nm));
8206   if (ci == 0)
8207     return -1;
8208   else
8209     return ci->get_index();
8210 }
8211 
number_to_index(int n)8212 int font::number_to_index(int n)
8213 {
8214   return get_charinfo_by_number(n)->get_index();
8215 }
8216