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