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