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