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