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