1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #include "troff.h"
22 #include "symbol.h"
23 #include "dictionary.h"
24 #include "hvunits.h"
25 #include "env.h"
26 #include "request.h"
27 #include "node.h"
28 #include "reg.h"
29 #include "token.h"
30 #include "div.h"
31 #include "charinfo.h"
32 #include "font.h"
33 #include "searchpath.h"
34 #include "macropath.h"
35
36 // Needed for getpid().
37 #include "posix.h"
38
39 #define USAGE_EXIT_CODE 1
40 #define MACRO_PREFIX "tmac."
41 #define STARTUP_FILE "troffrc"
42 #define DEFAULT_INPUT_STACK_LIMIT 1000
43
44 #ifndef DEFAULT_WARNING_MASK
45 // warnings that are enabled by default
46 #define DEFAULT_WARNING_MASK \
47 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
48 #endif
49
50 // initial size of buffer for reading names; expanded as necessary
51 #define ABUF_SIZE 16
52
53 #ifdef COLUMN
54 void init_column_requests();
55 #endif /* COLUMN */
56
57 static node *read_draw_node();
58 void handle_first_page_transition();
59 static void push_token(const token &);
60 void copy_file();
61 #ifdef COLUMN
62 void vjustify();
63 #endif /* COLUMN */
64 void transparent();
65 void transparent_file();
66
67 const char *program_name = 0;
68 token tok;
69 int break_flag = 0;
70 static int backtrace_flag = 0;
71 char *pipe_command = 0;
72 charinfo *charset_table[256];
73
74 static int warning_mask = DEFAULT_WARNING_MASK;
75 static int inhibit_errors = 0;
76
77 static void enable_warning(const char *);
78 static void disable_warning(const char *);
79
80 static int escape_char = '\\';
81 static symbol end_macro_name;
82 static int compatible_flag = 0;
83 static void process_input_stack();
84 int ascii_output_flag = 0;
85 int suppress_output_flag = 0;
86
87 int tcommand_flag = 0;
88
89 static int get_copy(node**, int = 0);
90 static symbol read_escape_name();
91 static void interpolate_string(symbol);
92 static void interpolate_macro(symbol);
93 static void interpolate_number_format(symbol);
94 static void interpolate_environment_variable(symbol);
95
96 static void interpolate_arg(symbol);
97 static request_or_macro *lookup_request(symbol);
98 static int get_delim_number(units *, int);
99 static int get_delim_number(units *, int, units);
100 static int get_line_arg(units *res, int si, charinfo **cp);
101 static int read_size(int *);
102 static symbol get_delim_name();
103 static void init_registers();
104
105 struct input_iterator;
106 input_iterator *make_temp_iterator(const char *);
107 const char *input_char_description(int);
108
109 const int ESCAPE_QUESTION = 015;
110 const int BEGIN_TRAP = 016;
111 const int END_TRAP = 017;
112 const int PAGE_EJECTOR = 020;
113 const int ESCAPE_NEWLINE = 021;
114 const int ESCAPE_AMPERSAND = 022;
115 const int ESCAPE_UNDERSCORE = 023;
116 const int ESCAPE_BAR = 024;
117 const int ESCAPE_CIRCUMFLEX = 025;
118 const int ESCAPE_LEFT_BRACE = 026;
119 const int ESCAPE_RIGHT_BRACE = 027;
120 const int ESCAPE_LEFT_QUOTE = 030;
121 const int ESCAPE_RIGHT_QUOTE = 031;
122 const int ESCAPE_HYPHEN = 032;
123 const int ESCAPE_BANG = 033;
124 const int ESCAPE_c = 034;
125 const int ESCAPE_e = 035;
126 const int ESCAPE_PERCENT = 036;
127 const int ESCAPE_SPACE = 037;
128
129 const int TITLE_REQUEST = 0200;
130 const int COPY_FILE_REQUEST = 0201;
131 const int TRANSPARENT_FILE_REQUEST = 0202;
132 #ifdef COLUMN
133 const int VJUSTIFY_REQUEST = 0203;
134 #endif /* COLUMN */
135 const int ESCAPE_E = 0204;
136 const int LAST_PAGE_EJECTOR = 0205;
137 const int ESCAPE_RIGHT_PARENTHESIS = 0206;
138
set_escape_char()139 void set_escape_char()
140 {
141 if (has_arg()) {
142 if (tok.ch() == 0) {
143 error("bad escape character");
144 escape_char = '\\';
145 }
146 else
147 escape_char = tok.ch();
148 }
149 else
150 escape_char = '\\';
151 skip_line();
152 }
153
escape_off()154 void escape_off()
155 {
156 escape_char = 0;
157 skip_line();
158 }
159
160 class input_iterator {
161 public:
162 input_iterator();
163 virtual ~input_iterator();
164 int get(node **);
165 friend class input_stack;
166 protected:
167 const unsigned char *ptr;
168 const unsigned char *eptr;
169 input_iterator *next;
170 private:
171 virtual int fill(node **);
172 virtual int peek();
has_args()173 virtual int has_args() { return 0; }
nargs()174 virtual int nargs() { return 0; }
get_arg(int)175 virtual input_iterator *get_arg(int) { return NULL; }
get_location(int,const char **,int *)176 virtual int get_location(int, const char **, int *)
177 { return 0; }
backtrace()178 virtual void backtrace() {}
set_location(const char *,int)179 virtual int set_location(const char *, int)
180 { return 0; }
next_file(FILE *,const char *)181 virtual int next_file(FILE *, const char *) { return 0; }
shift(int)182 virtual void shift(int) {}
183 virtual int is_boundary();
internal_level()184 virtual int internal_level() { return 0; }
is_file()185 virtual int is_file() { return 0; }
186 };
187
input_iterator()188 input_iterator::input_iterator()
189 : ptr(0), eptr(0)
190 {
191 }
192
~input_iterator()193 input_iterator::~input_iterator()
194 {
195 }
196
fill(node **)197 int input_iterator::fill(node **)
198 {
199 return EOF;
200 }
201
peek()202 int input_iterator::peek()
203 {
204 return EOF;
205 }
206
is_boundary()207 int input_iterator::is_boundary()
208 {
209 return 0;
210 }
211
get(node ** p)212 inline int input_iterator::get(node **p)
213 {
214 return ptr < eptr ? *ptr++ : fill(p);
215 }
216
217
218 class input_boundary : public input_iterator {
219 public:
is_boundary()220 int is_boundary() { return 1; }
221 };
222
223 class file_iterator : public input_iterator {
224 FILE *fp;
225 int lineno;
226 const char *filename;
227 int newline_flag;
228 enum { BUF_SIZE = 512 };
229 unsigned char buf[BUF_SIZE];
230 public:
231 file_iterator(FILE *, const char *);
232 ~file_iterator();
233 int fill(node **);
234 int peek();
235 int get_location(int, const char **, int *);
236 void backtrace();
237 int set_location(const char *, int);
238 int next_file(FILE *, const char *);
239 int is_file();
240 };
241
file_iterator(FILE * f,const char * fn)242 file_iterator::file_iterator(FILE *f, const char *fn)
243 : fp(f), filename(fn), lineno(1), newline_flag(0)
244 {
245 }
246
~file_iterator()247 file_iterator::~file_iterator()
248 {
249 if (fp != stdin)
250 fclose(fp);
251 else
252 clearerr(stdin);
253 }
254
is_file()255 int file_iterator::is_file()
256 {
257 return 1;
258 }
259
next_file(FILE * f,const char * s)260 int file_iterator::next_file(FILE *f, const char *s)
261 {
262 if (fp != stdin)
263 fclose(fp);
264 else
265 clearerr(stdin);
266 filename = s;
267 fp = f;
268 lineno = 1;
269 newline_flag = 0;
270 ptr = 0;
271 eptr = 0;
272 return 1;
273 }
274
fill(node **)275 int file_iterator::fill(node **)
276 {
277 if (newline_flag)
278 lineno++;
279 newline_flag = 0;
280 unsigned char *p = buf;
281 ptr = p;
282 unsigned char *e = p + BUF_SIZE;
283 while (p < e) {
284 int c = getc(fp);
285 if (c == EOF)
286 break;
287 if (illegal_input_char(c))
288 warning(WARN_INPUT, "illegal input character code %1", int(c));
289 else {
290 *p++ = c;
291 if (c == '\n') {
292 newline_flag = 1;
293 break;
294 }
295 }
296 }
297 if (p > buf) {
298 eptr = p;
299 return *ptr++;
300 }
301 else {
302 eptr = p;
303 return EOF;
304 }
305 }
306
peek()307 int file_iterator::peek()
308 {
309 int c = getc(fp);
310 while (illegal_input_char(c)) {
311 warning(WARN_INPUT, "illegal input character code %1", int(c));
312 c = getc(fp);
313 }
314 if (c != EOF)
315 ungetc(c, fp);
316 return c;
317 }
318
get_location(int,const char ** filenamep,int * linenop)319 int file_iterator::get_location(int /*allow_macro*/,
320 const char **filenamep, int *linenop)
321 {
322 *linenop = lineno;
323 if (filename != 0 && strcmp(filename, "-") == 0)
324 *filenamep = "<standard input>";
325 else
326 *filenamep = filename;
327 return 1;
328 }
329
backtrace()330 void file_iterator::backtrace()
331 {
332 errprint("%1:%2: backtrace: file `%1'\n", filename, lineno);
333 }
334
set_location(const char * f,int ln)335 int file_iterator::set_location(const char *f, int ln)
336 {
337 if (f)
338 filename = f;
339 lineno = ln;
340 return 1;
341 }
342
343 input_iterator nil_iterator;
344
345 class input_stack {
346 public:
347 static int get(node **);
348 static int peek();
349 static void push(input_iterator *);
350 static input_iterator *get_arg(int);
351 static int nargs();
352 static int get_location(int, const char **, int *);
353 static int set_location(const char *, int);
354 static void backtrace();
355 static void backtrace_all();
356 static void next_file(FILE *, const char *);
357 static void end_file();
358 static void shift(int n);
359 static void add_boundary();
360 static void remove_boundary();
361 static int get_level();
362 static void clear();
363
364 static int limit;
365 private:
366 static input_iterator *top;
367 static int level;
368
369 static int finish_get(node **);
370 static int finish_peek();
371 };
372
373 input_iterator *input_stack::top = &nil_iterator;
374 int input_stack::level = 0;
375 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
376
get_level()377 inline int input_stack::get_level()
378 {
379 return level + top->internal_level();
380 }
381
get(node ** np)382 inline int input_stack::get(node **np)
383 {
384 return (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
385 }
386
finish_get(node ** np)387 int input_stack::finish_get(node **np)
388 {
389 for (;;) {
390 int c = top->fill(np);
391 if (c != EOF || top->is_boundary())
392 return c;
393 if (top == &nil_iterator)
394 break;
395 input_iterator *tem = top;
396 top = top->next;
397 level--;
398 delete tem;
399 if (top->ptr < top->eptr)
400 return *top->ptr++;
401 }
402 assert(level == 0);
403 return EOF;
404 }
405
peek()406 inline int input_stack::peek()
407 {
408 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
409 }
410
finish_peek()411 int input_stack::finish_peek()
412 {
413 for (;;) {
414 int c = top->peek();
415 if (c != EOF || top->is_boundary())
416 return c;
417 if (top == &nil_iterator)
418 break;
419 input_iterator *tem = top;
420 top = top->next;
421 level--;
422 delete tem;
423 if (top->ptr < top->eptr)
424 return *top->ptr;
425 }
426 assert(level == 0);
427 return EOF;
428 }
429
add_boundary()430 void input_stack::add_boundary()
431 {
432 push(new input_boundary);
433 }
434
remove_boundary()435 void input_stack::remove_boundary()
436 {
437 assert(top->is_boundary());
438 input_iterator *temp = top->next;
439 delete top;
440 top = temp;
441 level--;
442 }
443
push(input_iterator * in)444 void input_stack::push(input_iterator *in)
445 {
446 if (in == 0)
447 return;
448 if (++level > limit && limit > 0)
449 fatal("input stack limit exceeded (probable infinite loop)");
450 in->next = top;
451 top = in;
452 }
453
get_arg(int i)454 input_iterator *input_stack::get_arg(int i)
455 {
456 input_iterator *p;
457 for (p = top; p != NULL; p = p->next)
458 if (p->has_args())
459 return p->get_arg(i);
460 return 0;
461 }
462
shift(int n)463 void input_stack::shift(int n)
464 {
465 for (input_iterator *p = top; p; p = p->next)
466 if (p->has_args()) {
467 p->shift(n);
468 return;
469 }
470 }
471
nargs()472 int input_stack::nargs()
473 {
474 for (input_iterator *p =top; p != 0; p = p->next)
475 if (p->has_args())
476 return p->nargs();
477 return 0;
478 }
479
get_location(int allow_macro,const char ** filenamep,int * linenop)480 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
481 {
482 for (input_iterator *p = top; p; p = p->next)
483 if (p->get_location(allow_macro, filenamep, linenop))
484 return 1;
485 return 0;
486 }
487
backtrace()488 void input_stack::backtrace()
489 {
490 const char *f;
491 int n;
492 // only backtrace down to (not including) the topmost file
493 for (input_iterator *p = top;
494 p && !p->get_location(0, &f, &n);
495 p = p->next)
496 p->backtrace();
497 }
498
backtrace_all()499 void input_stack::backtrace_all()
500 {
501 for (input_iterator *p = top; p; p = p->next)
502 p->backtrace();
503 }
504
set_location(const char * filename,int lineno)505 int input_stack::set_location(const char *filename, int lineno)
506 {
507 for (input_iterator *p = top; p; p = p->next)
508 if (p->set_location(filename, lineno))
509 return 1;
510 return 0;
511 }
512
next_file(FILE * fp,const char * s)513 void input_stack::next_file(FILE *fp, const char *s)
514 {
515 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
516 if ((*pp)->next_file(fp, s))
517 return;
518 if (++level > limit && limit > 0)
519 fatal("input stack limit exceeded");
520 *pp = new file_iterator(fp, s);
521 (*pp)->next = &nil_iterator;
522 }
523
end_file()524 void input_stack::end_file()
525 {
526 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
527 if ((*pp)->is_file()) {
528 input_iterator *tem = *pp;
529 *pp = (*pp)->next;
530 delete tem;
531 level--;
532 return;
533 }
534 }
535
clear()536 void input_stack::clear()
537 {
538 int nboundaries = 0;
539 while (top != &nil_iterator) {
540 if (top->is_boundary())
541 nboundaries++;
542 input_iterator *tem = top;
543 top = top->next;
544 level--;
545 delete tem;
546 }
547 // Keep while_request happy.
548 for (; nboundaries > 0; --nboundaries)
549 add_boundary();
550 }
551
backtrace_request()552 void backtrace_request()
553 {
554 input_stack::backtrace_all();
555 fflush(stderr);
556 skip_line();
557 }
558
next_file()559 void next_file()
560 {
561 symbol nm = get_long_name(0);
562 while (!tok.newline() && !tok.eof())
563 tok.next();
564 if (nm.is_null())
565 input_stack::end_file();
566 else {
567 errno = 0;
568 FILE *fp = fopen(nm.contents(), "r");
569 if (!fp)
570 error("can't open `%1': %2", nm.contents(), strerror(errno));
571 else
572 input_stack::next_file(fp, nm.contents());
573 }
574 tok.next();
575 }
576
shift()577 void shift()
578 {
579 int n;
580 if (!has_arg() || !get_integer(&n))
581 n = 1;
582 input_stack::shift(n);
583 skip_line();
584 }
585
get_char_for_escape_name()586 static int get_char_for_escape_name()
587 {
588 int c = get_copy(NULL);
589 switch (c) {
590 case EOF:
591 error("end of input in escape name");
592 return '\0';
593 default:
594 if (!illegal_input_char(c))
595 break;
596 // fall through
597 case ' ':
598 case '\n':
599 case '\t':
600 case '\001':
601 case '\b':
602 error("%1 is not allowed in an escape name", input_char_description(c));
603 return '\0';
604 }
605 return c;
606 }
607
read_two_char_escape_name()608 static symbol read_two_char_escape_name()
609 {
610 char buf[3];
611 buf[0] = get_char_for_escape_name();
612 if (buf[0] != '\0') {
613 buf[1] = get_char_for_escape_name();
614 if (buf[1] == '\0')
615 buf[0] = 0;
616 else
617 buf[2] = 0;
618 }
619 return symbol(buf);
620 }
621
read_long_escape_name()622 static symbol read_long_escape_name()
623 {
624 int start_level = input_stack::get_level();
625 char abuf[ABUF_SIZE];
626 char *buf = abuf;
627 int buf_size = ABUF_SIZE;
628 int i = 0;
629 for (;;) {
630 int c = get_char_for_escape_name();
631 if (c == 0) {
632 if (buf != abuf)
633 a_delete buf;
634 return NULL_SYMBOL;
635 }
636 if (i + 2 > buf_size) {
637 if (buf == abuf) {
638 buf = new char [ABUF_SIZE*2];
639 memcpy(buf, abuf, buf_size);
640 buf_size = ABUF_SIZE*2;
641 }
642 else {
643 char *old_buf = buf;
644 buf = new char[buf_size*2];
645 memcpy(buf, old_buf, buf_size);
646 buf_size *= 2;
647 a_delete old_buf;
648 }
649 }
650 if (c == ']' && input_stack::get_level() == start_level)
651 break;
652 buf[i++] = c;
653 }
654 buf[i] = 0;
655 if (buf == abuf) {
656 if (i == 0) {
657 error("empty escape name");
658 return NULL_SYMBOL;
659 }
660 return symbol(abuf);
661 }
662 else {
663 symbol s(buf);
664 a_delete buf;
665 return s;
666 }
667 }
668
read_escape_name()669 static symbol read_escape_name()
670 {
671 int c = get_char_for_escape_name();
672 if (c == 0)
673 return NULL_SYMBOL;
674 if (c == '(')
675 return read_two_char_escape_name();
676 if (c == '[' && !compatible_flag)
677 return read_long_escape_name();
678 char buf[2];
679 buf[0] = c;
680 buf[1] = '\0';
681 return symbol(buf);
682 }
683
read_increment_and_escape_name(int * incp)684 static symbol read_increment_and_escape_name(int *incp)
685 {
686 int c = get_char_for_escape_name();
687 switch (c) {
688 case 0:
689 *incp = 0;
690 return NULL_SYMBOL;
691 case '(':
692 *incp = 0;
693 return read_two_char_escape_name();
694 case '+':
695 *incp = 1;
696 return read_escape_name();
697 case '-':
698 *incp = -1;
699 return read_escape_name();
700 case '[':
701 if (!compatible_flag) {
702 *incp = 0;
703 return read_long_escape_name();
704 }
705 break;
706 }
707 *incp = 0;
708 char buf[2];
709 buf[0] = c;
710 buf[1] = '\0';
711 return symbol(buf);
712 }
713
get_copy(node ** nd,int defining)714 static int get_copy(node **nd, int defining)
715 {
716 for (;;) {
717 int c = input_stack::get(nd);
718 if (c == ESCAPE_NEWLINE) {
719 if (defining)
720 return c;
721 do {
722 c = input_stack::get(nd);
723 } while (c == ESCAPE_NEWLINE);
724 }
725 if (c != escape_char || escape_char <= 0)
726 return c;
727 c = input_stack::peek();
728 switch(c) {
729 case 0:
730 return escape_char;
731 case '"':
732 (void)input_stack::get(NULL);
733 while ((c = input_stack::get(NULL)) != '\n' && c != EOF)
734 ;
735 return c;
736 case '#': // Like \" but newline is ignored.
737 (void)input_stack::get(NULL);
738 while ((c = input_stack::get(NULL)) != '\n')
739 if (c == EOF)
740 return EOF;
741 break;
742 case '$':
743 {
744 (void)input_stack::get(NULL);
745 symbol s = read_escape_name();
746 if (!s.is_null())
747 interpolate_arg(s);
748 break;
749 }
750 case '*':
751 {
752 (void)input_stack::get(NULL);
753 symbol s = read_escape_name();
754 if (!s.is_null())
755 interpolate_string(s);
756 break;
757 }
758 case 'a':
759 (void)input_stack::get(NULL);
760 return '\001';
761 case 'e':
762 (void)input_stack::get(NULL);
763 return ESCAPE_e;
764 case 'E':
765 (void)input_stack::get(NULL);
766 return ESCAPE_E;
767 case 'n':
768 {
769 (void)input_stack::get(NULL);
770 int inc;
771 symbol s = read_increment_and_escape_name(&inc);
772 if (!s.is_null())
773 interpolate_number_reg(s, inc);
774 break;
775 }
776 case 'g':
777 {
778 (void)input_stack::get(NULL);
779 symbol s = read_escape_name();
780 if (!s.is_null())
781 interpolate_number_format(s);
782 break;
783 }
784 case 't':
785 (void)input_stack::get(NULL);
786 return '\t';
787 case 'V':
788 {
789 (void)input_stack::get(NULL);
790 symbol s = read_escape_name();
791 if (!s.is_null())
792 interpolate_environment_variable(s);
793 break;
794 }
795 case '\n':
796 (void)input_stack::get(NULL);
797 if (defining)
798 return ESCAPE_NEWLINE;
799 break;
800 case ' ':
801 (void)input_stack::get(NULL);
802 return ESCAPE_SPACE;
803 case '|':
804 (void)input_stack::get(NULL);
805 return ESCAPE_BAR;
806 case '^':
807 (void)input_stack::get(NULL);
808 return ESCAPE_CIRCUMFLEX;
809 case '{':
810 (void)input_stack::get(NULL);
811 return ESCAPE_LEFT_BRACE;
812 case '}':
813 (void)input_stack::get(NULL);
814 return ESCAPE_RIGHT_BRACE;
815 case '`':
816 (void)input_stack::get(NULL);
817 return ESCAPE_LEFT_QUOTE;
818 case '\'':
819 (void)input_stack::get(NULL);
820 return ESCAPE_RIGHT_QUOTE;
821 case '-':
822 (void)input_stack::get(NULL);
823 return ESCAPE_HYPHEN;
824 case '_':
825 (void)input_stack::get(NULL);
826 return ESCAPE_UNDERSCORE;
827 case 'c':
828 (void)input_stack::get(NULL);
829 return ESCAPE_c;
830 case '!':
831 (void)input_stack::get(NULL);
832 return ESCAPE_BANG;
833 case '?':
834 (void)input_stack::get(NULL);
835 return ESCAPE_QUESTION;
836 case '&':
837 (void)input_stack::get(NULL);
838 return ESCAPE_AMPERSAND;
839 case ')':
840 (void)input_stack::get(NULL);
841 return ESCAPE_RIGHT_PARENTHESIS;
842 case '.':
843 (void)input_stack::get(NULL);
844 return c;
845 case '%':
846 (void)input_stack::get(NULL);
847 return ESCAPE_PERCENT;
848 default:
849 if (c == escape_char) {
850 (void)input_stack::get(NULL);
851 return c;
852 }
853 else
854 return escape_char;
855 }
856 }
857 }
858
859 class non_interpreted_char_node : public node {
860 unsigned char c;
861 public:
862 non_interpreted_char_node(unsigned char);
863 node *copy();
864 int interpret(macro *);
865 int same(node *);
866 const char *type();
867 };
868
same(node * nd)869 int non_interpreted_char_node::same(node *nd)
870 {
871 return c == ((non_interpreted_char_node *)nd)->c;
872 }
873
type()874 const char *non_interpreted_char_node::type()
875 {
876 return "non_interpreted_char_node";
877 }
878
non_interpreted_char_node(unsigned char n)879 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
880 {
881 assert(n != 0);
882 }
883
copy()884 node *non_interpreted_char_node::copy()
885 {
886 return new non_interpreted_char_node(c);
887 }
888
interpret(macro * mac)889 int non_interpreted_char_node::interpret(macro *mac)
890 {
891 mac->append(c);
892 return 1;
893 }
894
895 static void do_width();
896 static node *do_non_interpreted();
897 static node *do_special();
898 static void do_register();
899
do_overstrike()900 static node *do_overstrike()
901 {
902 token start;
903 overstrike_node *on = new overstrike_node;
904 start.next();
905 tok.next();
906 while (tok != start) {
907 if (tok.newline() || tok.eof()) {
908 warning(WARN_DELIM, "missing closing delimiter");
909 break;
910 }
911 charinfo *ci = tok.get_char(1);
912 if (ci) {
913 node *n = curenv->make_char_node(ci);
914 if (n)
915 on->overstrike(n);
916 }
917 tok.next();
918 }
919 return on;
920 }
921
do_bracket()922 static node *do_bracket()
923 {
924 token start;
925 bracket_node *bn = new bracket_node;
926 start.next();
927 tok.next();
928 while (tok != start) {
929 if (tok.eof()) {
930 warning(WARN_DELIM, "missing closing delimiter");
931 break;
932 }
933 if (tok.newline()) {
934 warning(WARN_DELIM, "missing closing delimiter");
935 input_stack::push(make_temp_iterator("\n"));
936 break;
937 }
938 charinfo *ci = tok.get_char(1);
939 if (ci) {
940 node *n = curenv->make_char_node(ci);
941 if (n)
942 bn->bracket(n);
943 }
944 tok.next();
945 }
946 return bn;
947 }
948
do_name_test()949 static int do_name_test()
950 {
951 token start;
952 start.next();
953 int start_level = input_stack::get_level();
954 int bad_char = 0;
955 int some_char = 0;
956 for (;;) {
957 tok.next();
958 if (tok.newline() || tok.eof()) {
959 warning(WARN_DELIM, "missing closing delimiter");
960 break;
961 }
962 if (tok == start
963 && (compatible_flag || input_stack::get_level() == start_level))
964 break;
965 if (!tok.ch())
966 bad_char = 1;
967 some_char = 1;
968 }
969 return some_char && !bad_char;
970 }
971
972 #if 0
973 static node *do_zero_width()
974 {
975 token start;
976 start.next();
977 int start_level = input_stack::get_level();
978 environment env(curenv);
979 environment *oldenv = curenv;
980 curenv = &env;
981 for (;;) {
982 tok.next();
983 if (tok.newline() || tok.eof()) {
984 error("missing closing delimiter");
985 break;
986 }
987 if (tok == start
988 && (compatible_flag || input_stack::get_level() == start_level))
989 break;
990 tok.process();
991 }
992 curenv = oldenv;
993 node *rev = env.extract_output_line();
994 node *n = 0;
995 while (rev) {
996 node *tem = rev;
997 rev = rev->next;
998 tem->next = n;
999 n = tem;
1000 }
1001 return new zero_width_node(n);
1002 }
1003
1004 #else
1005
1006 // It's undesirable for \Z to change environments, because then
1007 // \n(.w won't work as expected.
1008
do_zero_width()1009 static node *do_zero_width()
1010 {
1011 node *rev = new dummy_node;
1012 token start;
1013 start.next();
1014 int start_level = input_stack::get_level();
1015 for (;;) {
1016 tok.next();
1017 if (tok.newline() || tok.eof()) {
1018 warning(WARN_DELIM, "missing closing delimiter");
1019 break;
1020 }
1021 if (tok == start
1022 && (compatible_flag || input_stack::get_level() == start_level))
1023 break;
1024 if (!tok.add_to_node_list(&rev))
1025 error("illegal token in argument to \\Z");
1026 }
1027 node *n = 0;
1028 while (rev) {
1029 node *tem = rev;
1030 rev = rev->next;
1031 tem->next = n;
1032 n = tem;
1033 }
1034 return new zero_width_node(n);
1035 }
1036
1037 #endif
1038
get_token_node()1039 token_node *node::get_token_node()
1040 {
1041 return 0;
1042 }
1043
1044 class token_node : public node {
1045 public:
1046 token tk;
1047 token_node(const token &t);
1048 node *copy();
1049 token_node *get_token_node();
1050 int same(node *);
1051 const char *type();
1052 };
1053
token_node(const token & t)1054 token_node::token_node(const token &t) : tk(t)
1055 {
1056 }
1057
copy()1058 node *token_node::copy()
1059 {
1060 return new token_node(tk);
1061 }
1062
get_token_node()1063 token_node *token_node::get_token_node()
1064 {
1065 return this;
1066 }
1067
same(node * nd)1068 int token_node::same(node *nd)
1069 {
1070 return tk == ((token_node *)nd)->tk;
1071 }
1072
type()1073 const char *token_node::type()
1074 {
1075 return "token_node";
1076 }
1077
token()1078 token::token() : nd(0), type(TOKEN_EMPTY)
1079 {
1080 }
1081
~token()1082 token::~token()
1083 {
1084 delete nd;
1085 }
1086
token(const token & t)1087 token::token(const token &t)
1088 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1089 {
1090 // Use two statements to work around bug in SGI C++.
1091 node *tem = t.nd;
1092 nd = tem ? tem->copy() : 0;
1093 }
1094
operator =(const token & t)1095 void token::operator=(const token &t)
1096 {
1097 delete nd;
1098 nm = t.nm;
1099 // Use two statements to work around bug in SGI C++.
1100 node *tem = t.nd;
1101 nd = tem ? tem->copy() : 0;
1102 c = t.c;
1103 val = t.val;
1104 dim = t.dim;
1105 type = t.type;
1106 }
1107
skip()1108 void token::skip()
1109 {
1110 while (space())
1111 next();
1112 }
1113
has_arg()1114 int has_arg()
1115 {
1116 while (tok.space())
1117 tok.next();
1118 return !tok.newline();
1119 }
1120
make_space()1121 void token::make_space()
1122 {
1123 type = TOKEN_SPACE;
1124 }
1125
make_newline()1126 void token::make_newline()
1127 {
1128 type = TOKEN_NEWLINE;
1129 }
1130
next()1131 void token::next()
1132 {
1133 if (nd) {
1134 delete nd;
1135 nd = 0;
1136 }
1137 units x;
1138 for (;;) {
1139 node *n;
1140 int cc = input_stack::get(&n);
1141 if (cc != escape_char || escape_char == 0) {
1142 handle_normal_char:
1143 switch(cc) {
1144 case EOF:
1145 type = TOKEN_EOF;
1146 return;
1147 case TRANSPARENT_FILE_REQUEST:
1148 case TITLE_REQUEST:
1149 case COPY_FILE_REQUEST:
1150 #ifdef COLUMN
1151 case VJUSTIFY_REQUEST:
1152 #endif /* COLUMN */
1153 type = TOKEN_REQUEST;
1154 c = cc;
1155 return;
1156 case BEGIN_TRAP:
1157 type = TOKEN_BEGIN_TRAP;
1158 return;
1159 case END_TRAP:
1160 type = TOKEN_END_TRAP;
1161 return;
1162 case LAST_PAGE_EJECTOR:
1163 seen_last_page_ejector = 1;
1164 // fall through
1165 case PAGE_EJECTOR:
1166 type = TOKEN_PAGE_EJECTOR;
1167 return;
1168 case ESCAPE_PERCENT:
1169 ESCAPE_PERCENT:
1170 type = TOKEN_HYPHEN_INDICATOR;
1171 return;
1172 case ESCAPE_SPACE:
1173 ESCAPE_SPACE:
1174 type = TOKEN_NODE;
1175 nd = new space_char_hmotion_node(curenv->get_space_width());
1176 return;
1177 case ESCAPE_e:
1178 ESCAPE_e:
1179 type = TOKEN_ESCAPE;
1180 return;
1181 case ESCAPE_E:
1182 goto handle_escape_char;
1183 case ESCAPE_BAR:
1184 ESCAPE_BAR:
1185 type = TOKEN_NODE;
1186 nd = new hmotion_node(curenv->get_narrow_space_width());
1187 return;
1188 case ESCAPE_CIRCUMFLEX:
1189 ESCAPE_CIRCUMFLEX:
1190 type = TOKEN_NODE;
1191 nd = new hmotion_node(curenv->get_half_narrow_space_width());
1192 return;
1193 case ESCAPE_NEWLINE:
1194 break;
1195 case ESCAPE_LEFT_BRACE:
1196 ESCAPE_LEFT_BRACE:
1197 type = TOKEN_LEFT_BRACE;
1198 return;
1199 case ESCAPE_RIGHT_BRACE:
1200 ESCAPE_RIGHT_BRACE:
1201 type = TOKEN_RIGHT_BRACE;
1202 return;
1203 case ESCAPE_LEFT_QUOTE:
1204 ESCAPE_LEFT_QUOTE:
1205 type = TOKEN_SPECIAL;
1206 nm = symbol("ga");
1207 return;
1208 case ESCAPE_RIGHT_QUOTE:
1209 ESCAPE_RIGHT_QUOTE:
1210 type = TOKEN_SPECIAL;
1211 nm = symbol("aa");
1212 return;
1213 case ESCAPE_HYPHEN:
1214 ESCAPE_HYPHEN:
1215 type = TOKEN_SPECIAL;
1216 nm = symbol("-");
1217 return;
1218 case ESCAPE_UNDERSCORE:
1219 ESCAPE_UNDERSCORE:
1220 type = TOKEN_SPECIAL;
1221 nm = symbol("ul");
1222 return;
1223 case ESCAPE_c:
1224 ESCAPE_c:
1225 type = TOKEN_INTERRUPT;
1226 return;
1227 case ESCAPE_BANG:
1228 ESCAPE_BANG:
1229 type = TOKEN_TRANSPARENT;
1230 return;
1231 case ESCAPE_QUESTION:
1232 ESCAPE_QUESTION:
1233 nd = do_non_interpreted();
1234 if (nd) {
1235 type = TOKEN_NODE;
1236 return;
1237 }
1238 break;
1239 case ESCAPE_AMPERSAND:
1240 ESCAPE_AMPERSAND:
1241 type = TOKEN_DUMMY;
1242 return;
1243 case ESCAPE_RIGHT_PARENTHESIS:
1244 ESCAPE_RIGHT_PARENTHESIS:
1245 type = TOKEN_NODE;
1246 nd = new transparent_dummy_node;
1247 return;
1248 case '\b':
1249 type = TOKEN_BACKSPACE;
1250 return;
1251 case ' ':
1252 type = TOKEN_SPACE;
1253 return;
1254 case '\t':
1255 type = TOKEN_TAB;
1256 return;
1257 case '\n':
1258 type = TOKEN_NEWLINE;
1259 return;
1260 case '\001':
1261 type = TOKEN_LEADER;
1262 return;
1263 case 0:
1264 {
1265 assert(n != 0);
1266 token_node *tn = n->get_token_node();
1267 if (tn) {
1268 *this = tn->tk;
1269 delete tn;
1270 }
1271 else {
1272 nd = n;
1273 type = TOKEN_NODE;
1274 }
1275 }
1276 return;
1277 default:
1278 type = TOKEN_CHAR;
1279 c = cc;
1280 return;
1281 }
1282 }
1283 else {
1284 handle_escape_char:
1285 cc = input_stack::get(NULL);
1286 switch(cc) {
1287 case '(':
1288 nm = read_two_char_escape_name();
1289 type = TOKEN_SPECIAL;
1290 return;
1291 case EOF:
1292 type = TOKEN_EOF;
1293 error("end of input after escape character");
1294 return;
1295 case '`':
1296 goto ESCAPE_LEFT_QUOTE;
1297 case '\'':
1298 goto ESCAPE_RIGHT_QUOTE;
1299 case '-':
1300 goto ESCAPE_HYPHEN;
1301 case '_':
1302 goto ESCAPE_UNDERSCORE;
1303 case '%':
1304 goto ESCAPE_PERCENT;
1305 case ' ':
1306 goto ESCAPE_SPACE;
1307 case '0':
1308 nd = new hmotion_node(curenv->get_digit_width());
1309 type = TOKEN_NODE;
1310 return;
1311 case '|':
1312 goto ESCAPE_BAR;
1313 case '^':
1314 goto ESCAPE_CIRCUMFLEX;
1315 case '/':
1316 type = TOKEN_ITALIC_CORRECTION;
1317 return;
1318 case ',':
1319 type = TOKEN_NODE;
1320 nd = new left_italic_corrected_node;
1321 return;
1322 case '&':
1323 goto ESCAPE_AMPERSAND;
1324 case ')':
1325 goto ESCAPE_RIGHT_PARENTHESIS;
1326 case '!':
1327 goto ESCAPE_BANG;
1328 case '?':
1329 goto ESCAPE_QUESTION;
1330 case '~':
1331 nd = new unbreakable_space_node(curenv->get_space_width());
1332 type = TOKEN_NODE;
1333 return;
1334 case '"':
1335 while ((cc = input_stack::get(NULL)) != '\n' && cc != EOF)
1336 ;
1337 if (cc == '\n')
1338 type = TOKEN_NEWLINE;
1339 else
1340 type = TOKEN_EOF;
1341 return;
1342 case '#': // Like \" but newline is ignored.
1343 while ((cc = input_stack::get(NULL)) != '\n')
1344 if (cc == EOF) {
1345 type = TOKEN_EOF;
1346 return;
1347 }
1348 break;
1349 case '$':
1350 {
1351 symbol nm = read_escape_name();
1352 if (!nm.is_null())
1353 interpolate_arg(nm);
1354 break;
1355 }
1356 case '*':
1357 {
1358 symbol nm = read_escape_name();
1359 if (!nm.is_null())
1360 interpolate_string(nm);
1361 break;
1362 }
1363 case 'a':
1364 nd = new non_interpreted_char_node('\001');
1365 type = TOKEN_NODE;
1366 return;
1367 case 'A':
1368 c = '0' + do_name_test();
1369 type = TOKEN_CHAR;
1370 return;
1371 case 'b':
1372 nd = do_bracket();
1373 type = TOKEN_NODE;
1374 return;
1375 case 'c':
1376 goto ESCAPE_c;
1377 case 'C':
1378 nm = get_delim_name();
1379 if (nm.is_null())
1380 break;
1381 type = TOKEN_SPECIAL;
1382 return;
1383 case 'd':
1384 type = TOKEN_NODE;
1385 nd = new vmotion_node(curenv->get_size()/2);
1386 return;
1387 case 'D':
1388 nd = read_draw_node();
1389 if (!nd)
1390 break;
1391 type = TOKEN_NODE;
1392 return;
1393 case 'e':
1394 goto ESCAPE_e;
1395 case 'E':
1396 goto handle_escape_char;
1397 case 'f':
1398 {
1399 symbol s = read_escape_name();
1400 if (s.is_null())
1401 break;
1402 for (const char *p = s.contents(); *p != '\0'; p++)
1403 if (!csdigit(*p))
1404 break;
1405 if (*p)
1406 curenv->set_font(s);
1407 else
1408 curenv->set_font(atoi(s.contents()));
1409 break;
1410 }
1411 case 'g':
1412 {
1413 symbol s = read_escape_name();
1414 if (!s.is_null())
1415 interpolate_number_format(s);
1416 break;
1417 }
1418 case 'h':
1419 if (!get_delim_number(&x, 'm'))
1420 break;
1421 type = TOKEN_NODE;
1422 nd = new hmotion_node(x);
1423 return;
1424 case 'H':
1425 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
1426 curenv->set_char_height(x);
1427 break;
1428 case 'k':
1429 nm = read_escape_name();
1430 if (nm.is_null())
1431 break;
1432 type = TOKEN_MARK_INPUT;
1433 return;
1434 case 'l':
1435 case 'L':
1436 {
1437 charinfo *s = 0;
1438 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
1439 break;
1440 if (s == 0)
1441 s = get_charinfo(cc == 'l' ? "ru" : "br");
1442 type = TOKEN_NODE;
1443 node *n = curenv->make_char_node(s);
1444 if (cc == 'l')
1445 nd = new hline_node(x, n);
1446 else
1447 nd = new vline_node(x, n);
1448 return;
1449 }
1450 case 'n':
1451 {
1452 int inc;
1453 symbol nm = read_increment_and_escape_name(&inc);
1454 if (!nm.is_null())
1455 interpolate_number_reg(nm, inc);
1456 break;
1457 }
1458 case 'N':
1459 if (!get_delim_number(&val, 0))
1460 break;
1461 type = TOKEN_NUMBERED_CHAR;
1462 return;
1463 case 'o':
1464 nd = do_overstrike();
1465 type = TOKEN_NODE;
1466 return;
1467 case 'p':
1468 type = TOKEN_SPREAD;
1469 return;
1470 case 'r':
1471 type = TOKEN_NODE;
1472 nd = new vmotion_node(-curenv->get_size());
1473 return;
1474 case 'R':
1475 do_register();
1476 break;
1477 case 's':
1478 if (read_size(&x))
1479 curenv->set_size(x);
1480 break;
1481 case 'S':
1482 if (get_delim_number(&x, 0))
1483 curenv->set_char_slant(x);
1484 break;
1485 case 't':
1486 type = TOKEN_NODE;
1487 nd = new non_interpreted_char_node('\t');
1488 return;
1489 case 'u':
1490 type = TOKEN_NODE;
1491 nd = new vmotion_node(-curenv->get_size()/2);
1492 return;
1493 case 'v':
1494 if (!get_delim_number(&x, 'v'))
1495 break;
1496 type = TOKEN_NODE;
1497 nd = new vmotion_node(x);
1498 return;
1499 case 'V':
1500 {
1501 symbol nm = read_escape_name();
1502 if (!nm.is_null())
1503 interpolate_environment_variable(nm);
1504 break;
1505 }
1506 case 'w':
1507 do_width();
1508 break;
1509 case 'x':
1510 if (!get_delim_number(&x, 'v'))
1511 break;
1512 type = TOKEN_NODE;
1513 nd = new extra_size_node(x);
1514 return;
1515 case 'X':
1516 nd = do_special();
1517 if (!nd)
1518 break;
1519 type = TOKEN_NODE;
1520 return;
1521 case 'Y':
1522 {
1523 symbol s = read_escape_name();
1524 if (s.is_null())
1525 break;
1526 request_or_macro *p = lookup_request(s);
1527 macro *m = p->to_macro();
1528 if (!m) {
1529 error("can't transparently throughput a request");
1530 break;
1531 }
1532 nd = new special_node(*m);
1533 type = TOKEN_NODE;
1534 return;
1535 }
1536 case 'z':
1537 {
1538 next();
1539 if (type == TOKEN_NODE)
1540 nd = new zero_width_node(nd);
1541 else {
1542 charinfo *ci = get_char(1);
1543 if (ci == 0)
1544 break;
1545 node *gn = curenv->make_char_node(ci);
1546 if (gn == 0)
1547 break;
1548 nd = new zero_width_node(gn);
1549 type = TOKEN_NODE;
1550 }
1551 return;
1552 }
1553 case 'Z':
1554 nd = do_zero_width();
1555 if (nd == 0)
1556 break;
1557 type = TOKEN_NODE;
1558 return;
1559 case '{':
1560 goto ESCAPE_LEFT_BRACE;
1561 case '}':
1562 goto ESCAPE_RIGHT_BRACE;
1563 case '\n':
1564 break;
1565 case '[':
1566 if (!compatible_flag) {
1567 nm = read_long_escape_name();
1568 if (nm.is_null())
1569 break;
1570 type = TOKEN_SPECIAL;
1571 return;
1572 }
1573 goto handle_normal_char;
1574 default:
1575 if (cc != escape_char && cc != '.')
1576 warning(WARN_ESCAPE, "escape character ignored before %1",
1577 input_char_description(cc));
1578 goto handle_normal_char;
1579 }
1580 }
1581 }
1582 }
1583
operator ==(const token & t)1584 int token::operator==(const token &t)
1585 {
1586 if (type != t.type)
1587 return 0;
1588 switch(type) {
1589 case TOKEN_CHAR:
1590 return c == t.c;
1591 case TOKEN_SPECIAL:
1592 return nm == t.nm;
1593 case TOKEN_NUMBERED_CHAR:
1594 return val == t.val;
1595 default:
1596 return 1;
1597 }
1598 }
1599
operator !=(const token & t)1600 int token::operator!=(const token &t)
1601 {
1602 return !(*this == t);
1603 }
1604
1605 // is token a suitable delimiter (like ')?
1606
delimiter(int err)1607 int token::delimiter(int err)
1608 {
1609 switch(type) {
1610 case TOKEN_CHAR:
1611 switch(c) {
1612 case '0':
1613 case '1':
1614 case '2':
1615 case '3':
1616 case '4':
1617 case '5':
1618 case '6':
1619 case '7':
1620 case '8':
1621 case '9':
1622 case '+':
1623 case '-':
1624 case '/':
1625 case '*':
1626 case '%':
1627 case '<':
1628 case '>':
1629 case '=':
1630 case '&':
1631 case ':':
1632 case '(':
1633 case ')':
1634 case '.':
1635 if (err)
1636 error("cannot use character `%1' as a starting delimiter", char(c));
1637 return 0;
1638 default:
1639 return 1;
1640 }
1641 case TOKEN_NODE:
1642 case TOKEN_SPACE:
1643 case TOKEN_TAB:
1644 case TOKEN_NEWLINE:
1645 if (err)
1646 error("cannot use %1 as a starting delimiter", description());
1647 return 0;
1648 default:
1649 return 1;
1650 }
1651 }
1652
description()1653 const char *token::description()
1654 {
1655 static char buf[4];
1656 switch (type) {
1657 case TOKEN_BACKSPACE:
1658 return "a backspace character";
1659 case TOKEN_CHAR:
1660 buf[0] = '`';
1661 buf[1] = c;
1662 buf[2] = '\'';
1663 buf[3] = '\0';
1664 return buf;
1665 case TOKEN_DUMMY:
1666 return "`\\&'";
1667 case TOKEN_ESCAPE:
1668 return "`\\e'";
1669 case TOKEN_HYPHEN_INDICATOR:
1670 return "`\\%'";
1671 case TOKEN_INTERRUPT:
1672 return "`\\c'";
1673 case TOKEN_ITALIC_CORRECTION:
1674 return "`\\/'";
1675 case TOKEN_LEADER:
1676 return "a leader character";
1677 case TOKEN_LEFT_BRACE:
1678 return "`\\{'";
1679 case TOKEN_MARK_INPUT:
1680 return "`\\k'";
1681 case TOKEN_NEWLINE:
1682 return "newline";
1683 case TOKEN_NODE:
1684 return "a node";
1685 case TOKEN_NUMBERED_CHAR:
1686 return "`\\N'";
1687 case TOKEN_RIGHT_BRACE:
1688 return "`\\}'";
1689 case TOKEN_SPACE:
1690 return "a space";
1691 case TOKEN_SPECIAL:
1692 return "a special character";
1693 case TOKEN_SPREAD:
1694 return "`\\p'";
1695 case TOKEN_TAB:
1696 return "a tab character";
1697 case TOKEN_TRANSPARENT:
1698 return "`\\!'";
1699 case TOKEN_EOF:
1700 return "end of input";
1701 default:
1702 break;
1703 }
1704 return "a magic token";
1705 }
1706
skip_line()1707 void skip_line()
1708 {
1709 while (!tok.newline())
1710 if (tok.eof())
1711 return;
1712 else
1713 tok.next();
1714 tok.next();
1715 }
1716
compatible()1717 void compatible()
1718 {
1719 int n;
1720 if (has_arg() && get_integer(&n))
1721 compatible_flag = n != 0;
1722 else
1723 compatible_flag = 1;
1724 skip_line();
1725 }
1726
empty_name_warning(int required)1727 static void empty_name_warning(int required)
1728 {
1729 if (tok.newline() || tok.eof()) {
1730 if (required)
1731 warning(WARN_MISSING, "missing name");
1732 }
1733 else if (tok.right_brace() || tok.tab()) {
1734 const char *start = tok.description();
1735 do {
1736 tok.next();
1737 } while (tok.space() || tok.right_brace() || tok.tab());
1738 if (!tok.newline() && !tok.eof())
1739 error("%1 is not allowed before an argument", start);
1740 else if (required)
1741 warning(WARN_MISSING, "missing name");
1742 }
1743 else if (required)
1744 error("name expected (got %1)", tok.description());
1745 else
1746 error("name expected (got %1): treated as missing", tok.description());
1747 }
1748
non_empty_name_warning()1749 static void non_empty_name_warning()
1750 {
1751 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
1752 && !tok.right_brace()
1753 // We don't want to give a warning for .el\{
1754 && !tok.left_brace())
1755 error("%1 is not allowed in a name", tok.description());
1756 }
1757
get_name(int required)1758 symbol get_name(int required)
1759 {
1760 if (compatible_flag) {
1761 char buf[3];
1762 tok.skip();
1763 if ((buf[0] = tok.ch()) != 0) {
1764 tok.next();
1765 if ((buf[1] = tok.ch()) != 0) {
1766 buf[2] = 0;
1767 tok.make_space();
1768 }
1769 else
1770 non_empty_name_warning();
1771 return symbol(buf);
1772 }
1773 else {
1774 empty_name_warning(required);
1775 return NULL_SYMBOL;
1776 }
1777 }
1778 else
1779 return get_long_name(required);
1780 }
1781
get_long_name(int required)1782 symbol get_long_name(int required)
1783 {
1784 while (tok.space())
1785 tok.next();
1786 char abuf[ABUF_SIZE];
1787 char *buf = abuf;
1788 int buf_size = ABUF_SIZE;
1789 int i = 0;
1790 for (;;) {
1791 if (i + 1 > buf_size) {
1792 if (buf == abuf) {
1793 buf = new char [ABUF_SIZE*2];
1794 memcpy(buf, abuf, buf_size);
1795 buf_size = ABUF_SIZE*2;
1796 }
1797 else {
1798 char *old_buf = buf;
1799 buf = new char[buf_size*2];
1800 memcpy(buf, old_buf, buf_size);
1801 buf_size *= 2;
1802 a_delete old_buf;
1803 }
1804 }
1805 if ((buf[i] = tok.ch()) == 0)
1806 break;
1807 i++;
1808 tok.next();
1809 }
1810 if (i == 0) {
1811 empty_name_warning(required);
1812 return NULL_SYMBOL;
1813 }
1814 non_empty_name_warning();
1815 if (buf == abuf)
1816 return symbol(buf);
1817 else {
1818 symbol s(buf);
1819 a_delete buf;
1820 return s;
1821 }
1822 }
1823
exit_troff()1824 NO_RETURN void exit_troff()
1825 {
1826 exit_started = 1;
1827 topdiv->set_last_page();
1828 if (!end_macro_name.is_null()) {
1829 spring_trap(end_macro_name);
1830 tok.next();
1831 process_input_stack();
1832 }
1833 curenv->final_break();
1834 tok.next();
1835 process_input_stack();
1836 end_diversions();
1837 done_end_macro = 1;
1838 topdiv->set_ejecting();
1839 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
1840 input_stack::push(make_temp_iterator((char *)buf));
1841 topdiv->space(topdiv->get_page_length(), 1);
1842 tok.next();
1843 process_input_stack();
1844 seen_last_page_ejector = 1; // should be set already
1845 topdiv->set_ejecting();
1846 push_page_ejector();
1847 topdiv->space(topdiv->get_page_length(), 1);
1848 tok.next();
1849 process_input_stack();
1850 // This will only happen if a trap-invoked macro starts a diversion,
1851 // or if vertical position traps have been disabled.
1852 cleanup_and_exit(0);
1853 }
1854
1855 // This implements .ex. The input stack must be cleared before calling
1856 // exit_troff().
1857
exit_request()1858 void exit_request()
1859 {
1860 input_stack::clear();
1861 if (exit_started)
1862 tok.next();
1863 else
1864 exit_troff();
1865 }
1866
end_macro()1867 void end_macro()
1868 {
1869 end_macro_name = get_name();
1870 skip_line();
1871 }
1872
do_request()1873 void do_request()
1874 {
1875 int saved_compatible_flag = compatible_flag;
1876 compatible_flag = 0;
1877 symbol nm = get_name();
1878 if (nm.is_null())
1879 skip_line();
1880 else
1881 interpolate_macro(nm);
1882 compatible_flag = saved_compatible_flag;
1883 }
1884
possibly_handle_first_page_transition()1885 inline int possibly_handle_first_page_transition()
1886 {
1887 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
1888 handle_first_page_transition();
1889 return 1;
1890 }
1891 else
1892 return 0;
1893 }
1894
transparent_translate(int cc)1895 static int transparent_translate(int cc)
1896 {
1897 if (!illegal_input_char(cc)) {
1898 charinfo *ci = charset_table[cc];
1899 switch (ci->get_special_translation(1)) {
1900 case charinfo::TRANSLATE_SPACE:
1901 return ' ';
1902 case charinfo::TRANSLATE_DUMMY:
1903 return ESCAPE_AMPERSAND;
1904 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
1905 return ESCAPE_PERCENT;
1906 }
1907 // This is realy ugly.
1908 ci = ci->get_translation(1);
1909 if (ci) {
1910 int c = ci->get_ascii_code();
1911 if (c != '\0')
1912 return c;
1913 error("can't translate %1 to special character `%2'"
1914 " in transparent throughput",
1915 input_char_description(cc),
1916 ci->nm.contents());
1917 }
1918 }
1919 return cc;
1920 }
1921
1922 class int_stack {
1923 struct int_stack_element {
1924 int n;
1925 int_stack_element *next;
1926 } *top;
1927 public:
1928 int_stack();
1929 ~int_stack();
1930 void push(int);
1931 int is_empty();
1932 int pop();
1933 };
1934
int_stack()1935 int_stack::int_stack()
1936 {
1937 top = 0;
1938 }
1939
~int_stack()1940 int_stack::~int_stack()
1941 {
1942 while (top != 0) {
1943 int_stack_element *temp = top;
1944 top = top->next;
1945 delete temp;
1946 }
1947
1948 }
1949
is_empty()1950 int int_stack::is_empty()
1951 {
1952 return top == 0;
1953 }
1954
push(int n)1955 void int_stack::push(int n)
1956 {
1957 int_stack_element *p = new int_stack_element;
1958 p->next = top;
1959 p->n = n;
1960 top = p;
1961 }
1962
1963
pop()1964 int int_stack::pop()
1965 {
1966 assert(top != 0);
1967 int_stack_element *p = top;
1968 top = top->next;
1969 int n = p->n;
1970 delete p;
1971 return n;
1972 }
1973
reread(int *)1974 int node::reread(int *)
1975 {
1976 return 0;
1977 }
1978
reread(int * bolp)1979 int diverted_space_node::reread(int *bolp)
1980 {
1981 if (curenv->get_fill())
1982 blank_line();
1983 else
1984 curdiv->space(n);
1985 *bolp = 1;
1986 return 1;
1987 }
1988
reread(int * bolp)1989 int diverted_copy_file_node::reread(int *bolp)
1990 {
1991 curdiv->copy_file(filename.contents());
1992 *bolp = 1;
1993 return 1;
1994 }
1995
process_input_stack()1996 static void process_input_stack()
1997 {
1998 int_stack trap_bol_stack;
1999 int bol = 1;
2000 for (;;) {
2001 int suppress_next = 0;
2002 switch (tok.type) {
2003 case token::TOKEN_CHAR:
2004 {
2005 unsigned char ch = tok.c;
2006 if (bol &&
2007 (ch == curenv->control_char
2008 || ch == curenv->no_break_control_char)) {
2009 break_flag = ch == curenv->control_char;
2010 // skip tabs as well as spaces here
2011 do {
2012 tok.next();
2013 } while (tok.white_space());
2014 symbol nm = get_name();
2015 if (nm.is_null())
2016 skip_line();
2017 else
2018 interpolate_macro(nm);
2019 suppress_next = 1;
2020 }
2021 else {
2022 if (possibly_handle_first_page_transition())
2023 ;
2024 else {
2025 for (;;) {
2026 curenv->add_char(charset_table[ch]);
2027 tok.next();
2028 if (tok.type != token::TOKEN_CHAR)
2029 break;
2030 ch = tok.c;
2031 }
2032 suppress_next = 1;
2033 bol = 0;
2034 }
2035 }
2036 break;
2037 }
2038 case token::TOKEN_TRANSPARENT:
2039 {
2040 if (bol) {
2041 if (possibly_handle_first_page_transition())
2042 ;
2043 else {
2044 int cc;
2045 do {
2046 node *n;
2047 cc = get_copy(&n);
2048 if (cc != EOF)
2049 if (cc != '\0')
2050 curdiv->transparent_output(transparent_translate(cc));
2051 else
2052 curdiv->transparent_output(n);
2053 } while (cc != '\n' && cc != EOF);
2054 if (cc == EOF)
2055 curdiv->transparent_output('\n');
2056 }
2057 }
2058 break;
2059 }
2060 case token::TOKEN_NEWLINE:
2061 {
2062 if (bol && !curenv->get_prev_line_interrupted())
2063 blank_line();
2064 else {
2065 curenv->newline();
2066 bol = 1;
2067 }
2068 break;
2069 }
2070 case token::TOKEN_REQUEST:
2071 {
2072 int request_code = tok.c;
2073 tok.next();
2074 switch (request_code) {
2075 case TITLE_REQUEST:
2076 title();
2077 break;
2078 case COPY_FILE_REQUEST:
2079 copy_file();
2080 break;
2081 case TRANSPARENT_FILE_REQUEST:
2082 transparent_file();
2083 break;
2084 #ifdef COLUMN
2085 case VJUSTIFY_REQUEST:
2086 vjustify();
2087 break;
2088 #endif /* COLUMN */
2089 default:
2090 assert(0);
2091 break;
2092 }
2093 suppress_next = 1;
2094 break;
2095 }
2096 case token::TOKEN_SPACE:
2097 {
2098 if (possibly_handle_first_page_transition())
2099 ;
2100 else if (bol && !curenv->get_prev_line_interrupted()) {
2101 int nspaces = 0;
2102 do {
2103 nspaces += tok.nspaces();
2104 tok.next();
2105 } while (tok.space());
2106 if (tok.newline())
2107 blank_line();
2108 else {
2109 push_token(tok);
2110 curenv->do_break();
2111 curenv->add_node(new hmotion_node(curenv->get_space_width()*nspaces));
2112 bol = 0;
2113 }
2114 }
2115 else {
2116 curenv->space();
2117 bol = 0;
2118 }
2119 break;
2120 }
2121 case token::TOKEN_EOF:
2122 return;
2123 case token::TOKEN_NODE:
2124 {
2125 if (possibly_handle_first_page_transition())
2126 ;
2127 else if (tok.nd->reread(&bol)) {
2128 delete tok.nd;
2129 tok.nd = 0;
2130 }
2131 else {
2132 curenv->add_node(tok.nd);
2133 tok.nd = 0;
2134 bol = 0;
2135 }
2136 break;
2137 }
2138 case token::TOKEN_PAGE_EJECTOR:
2139 {
2140 continue_page_eject();
2141 // I think we just want to preserve bol.
2142 // bol = 1;
2143 break;
2144 }
2145 case token::TOKEN_BEGIN_TRAP:
2146 {
2147 trap_bol_stack.push(bol);
2148 bol = 1;
2149 break;
2150 }
2151 case token::TOKEN_END_TRAP:
2152 {
2153 if (trap_bol_stack.is_empty())
2154 error("spurious end trap token detected!");
2155 else
2156 bol = trap_bol_stack.pop();
2157
2158 /* I'm not totally happy about this. But I can't think of any other
2159 way to do it. Doing an output_pending_lines() whenever a
2160 TOKEN_END_TRAP is detected doesn't work: for example,
2161
2162 .wh -1i x
2163 .de x
2164 'bp
2165 ..
2166 .wh -.5i y
2167 .de y
2168 .tl ''-%-''
2169 ..
2170 .br
2171 .ll .5i
2172 .sp |\n(.pu-1i-.5v
2173 a\%very\%very\%long\%word
2174
2175 will print all but the first lines from the word immediately
2176 after the footer, rather than on the next page. */
2177
2178 if (trap_bol_stack.is_empty())
2179 curenv->output_pending_lines();
2180 break;
2181 }
2182 default:
2183 {
2184 bol = 0;
2185 tok.process();
2186 break;
2187 }
2188 }
2189 if (!suppress_next)
2190 tok.next();
2191 trap_sprung_flag = 0;
2192 }
2193 }
2194
2195 #ifdef WIDOW_CONTROL
2196
flush_pending_lines()2197 void flush_pending_lines()
2198 {
2199 while (!tok.newline() && !tok.eof())
2200 tok.next();
2201 curenv->output_pending_lines();
2202 tok.next();
2203 }
2204
2205 #endif /* WIDOW_CONTROL */
2206
request_or_macro()2207 request_or_macro::request_or_macro()
2208 {
2209 }
2210
to_macro()2211 macro *request_or_macro::to_macro()
2212 {
2213 return 0;
2214 }
2215
request(REQUEST_FUNCP pp)2216 request::request(REQUEST_FUNCP pp) : p(pp)
2217 {
2218 }
2219
invoke(symbol)2220 void request::invoke(symbol)
2221 {
2222 (*p)();
2223 }
2224
2225 struct char_block {
2226 enum { SIZE = 128 };
2227 unsigned char s[SIZE];
2228 char_block *next;
2229 char_block();
2230 };
2231
char_block()2232 char_block::char_block()
2233 : next(0)
2234 {
2235 }
2236
2237 class char_list {
2238 public:
2239 char_list();
2240 ~char_list();
2241 void append(unsigned char);
2242 int length();
2243 private:
2244 unsigned char *ptr;
2245 int len;
2246 char_block *head;
2247 char_block *tail;
2248 friend class macro_header;
2249 friend class string_iterator;
2250 };
2251
char_list()2252 char_list::char_list()
2253 : head(0), tail(0), ptr(0), len(0)
2254 {
2255 }
2256
~char_list()2257 char_list::~char_list()
2258 {
2259 while (head != 0) {
2260 char_block *tem = head;
2261 head = head->next;
2262 delete tem;
2263 }
2264 }
2265
length()2266 int char_list::length()
2267 {
2268 return len;
2269 }
2270
append(unsigned char c)2271 void char_list::append(unsigned char c)
2272 {
2273 if (tail == 0) {
2274 head = tail = new char_block;
2275 ptr = tail->s;
2276 }
2277 else {
2278 if (ptr >= tail->s + char_block::SIZE) {
2279 tail->next = new char_block;
2280 tail = tail->next;
2281 ptr = tail->s;
2282 }
2283 }
2284 *ptr++ = c;
2285 len++;
2286 }
2287
2288 class node_list {
2289 node *head;
2290 node *tail;
2291 public:
2292 node_list();
2293 ~node_list();
2294 void append(node *);
2295 int length();
2296 node *extract();
2297
2298 friend class macro_header;
2299 friend class string_iterator;
2300 };
2301
append(node * n)2302 void node_list::append(node *n)
2303 {
2304 if (head == 0) {
2305 n->next = 0;
2306 head = tail = n;
2307 }
2308 else {
2309 n->next = 0;
2310 tail = tail->next = n;
2311 }
2312 }
2313
length()2314 int node_list::length()
2315 {
2316 int total = 0;
2317 for (node *n = head; n != 0; n = n->next)
2318 ++total;
2319 return total;
2320 }
2321
node_list()2322 node_list::node_list()
2323 {
2324 head = tail = 0;
2325 }
2326
extract()2327 node *node_list::extract()
2328 {
2329 node *temp = head;
2330 head = tail = 0;
2331 return temp;
2332 }
2333
2334
~node_list()2335 node_list::~node_list()
2336 {
2337 delete_node_list(head);
2338 }
2339
2340 struct macro_header {
2341 int count;
2342 char_list cl;
2343 node_list nl;
macro_headermacro_header2344 macro_header() { count = 1; }
2345 macro_header *copy(int);
2346 };
2347
2348
~macro()2349 macro::~macro()
2350 {
2351 if (p != 0 && --(p->count) <= 0)
2352 delete p;
2353 }
2354
macro()2355 macro::macro()
2356 {
2357 if (!input_stack::get_location(1, &filename, &lineno)) {
2358 filename = 0;
2359 lineno = 0;
2360 }
2361 length = 0;
2362 p = 0;
2363 }
2364
macro(const macro & m)2365 macro::macro(const macro &m)
2366 : filename(m.filename), lineno(m.lineno), p(m.p), length(m.length)
2367 {
2368 if (p != 0)
2369 p->count++;
2370 }
2371
operator =(const macro & m)2372 macro ¯o::operator=(const macro &m)
2373 {
2374 // don't assign object
2375 if (m.p != 0)
2376 m.p->count++;
2377 if (p != 0 && --(p->count) <= 0)
2378 delete p;
2379 p = m.p;
2380 filename = m.filename;
2381 lineno = m.lineno;
2382 length = m.length;
2383 return *this;
2384 }
2385
append(unsigned char c)2386 void macro::append(unsigned char c)
2387 {
2388 assert(c != 0);
2389 if (p == 0)
2390 p = new macro_header;
2391 if (p->cl.length() != length) {
2392 macro_header *tem = p->copy(length);
2393 if (--(p->count) <= 0)
2394 delete p;
2395 p = tem;
2396 }
2397 p->cl.append(c);
2398 ++length;
2399 }
2400
append(node * n)2401 void macro::append(node *n)
2402 {
2403 assert(n != 0);
2404 if (p == 0)
2405 p = new macro_header;
2406 if (p->cl.length() != length) {
2407 macro_header *tem = p->copy(length);
2408 if (--(p->count) <= 0)
2409 delete p;
2410 p = tem;
2411 }
2412 p->cl.append(0);
2413 p->nl.append(n);
2414 ++length;
2415 }
2416
print_size()2417 void macro::print_size()
2418 {
2419 errprint("%1", length);
2420 }
2421
2422 // make a copy of the first n bytes
2423
copy(int n)2424 macro_header *macro_header::copy(int n)
2425 {
2426 macro_header *p = new macro_header;
2427 char_block *bp = cl.head;
2428 unsigned char *ptr = bp->s;
2429 node *nd = nl.head;
2430 while (--n >= 0) {
2431 if (ptr >= bp->s + char_block::SIZE) {
2432 bp = bp->next;
2433 ptr = bp->s;
2434 }
2435 int c = *ptr++;
2436 p->cl.append(c);
2437 if (c == 0) {
2438 p->nl.append(nd->copy());
2439 nd = nd->next;
2440 }
2441 }
2442 return p;
2443 }
2444
print_macros()2445 void print_macros()
2446 {
2447 object_dictionary_iterator iter(request_dictionary);
2448 request_or_macro *rm;
2449 symbol s;
2450 while (iter.get(&s, (object **)&rm)) {
2451 assert(!s.is_null());
2452 macro *m = rm->to_macro();
2453 if (m) {
2454 errprint("%1\t", s.contents());
2455 m->print_size();
2456 errprint("\n");
2457 }
2458 }
2459 fflush(stderr);
2460 skip_line();
2461 }
2462
2463 class string_iterator : public input_iterator {
2464 macro mac;
2465 const char *how_invoked;
2466 int newline_flag;
2467 int lineno;
2468 char_block *bp;
2469 int count; // of characters remaining
2470 node *nd;
2471 protected:
2472 symbol nm;
2473 string_iterator();
2474 public:
2475 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
2476 int fill(node **);
2477 int peek();
2478 int get_location(int, const char **, int *);
2479 void backtrace();
2480 };
2481
string_iterator(const macro & m,const char * p,symbol s)2482 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
2483 : lineno(1), mac(m), newline_flag(0), how_invoked(p), nm(s)
2484 {
2485 count = mac.length;
2486 if (count != 0) {
2487 bp = mac.p->cl.head;
2488 nd = mac.p->nl.head;
2489 ptr = eptr = bp->s;
2490 }
2491 else {
2492 bp = 0;
2493 nd = 0;
2494 ptr = eptr = 0;
2495 }
2496 }
2497
string_iterator()2498 string_iterator::string_iterator()
2499 {
2500 bp = 0;
2501 nd = 0;
2502 ptr = eptr = 0;
2503 newline_flag = 0;
2504 how_invoked = 0;
2505 lineno = 1;
2506 count = 0;
2507 }
2508
fill(node ** np)2509 int string_iterator::fill(node **np)
2510 {
2511 if (newline_flag)
2512 lineno++;
2513 newline_flag = 0;
2514 if (count <= 0)
2515 return EOF;
2516 const unsigned char *p = eptr;
2517 if (p >= bp->s + char_block::SIZE) {
2518 bp = bp->next;
2519 p = bp->s;
2520 }
2521 if (*p == '\0') {
2522 if (np)
2523 *np = nd->copy();
2524 nd = nd->next;
2525 eptr = ptr = p + 1;
2526 count--;
2527 return 0;
2528 }
2529 const unsigned char *e = bp->s + char_block::SIZE;
2530 if (e - p > count)
2531 e = p + count;
2532 ptr = p;
2533 while (p < e) {
2534 unsigned char c = *p;
2535 if (c == '\n' || c == ESCAPE_NEWLINE) {
2536 newline_flag = 1;
2537 p++;
2538 break;
2539 }
2540 if (c == '\0')
2541 break;
2542 p++;
2543 }
2544 eptr = p;
2545 count -= p - ptr;
2546 return *ptr++;
2547 }
2548
peek()2549 int string_iterator::peek()
2550 {
2551 if (count <= 0)
2552 return EOF;
2553 const unsigned char *p = eptr;
2554 if (count <= 0)
2555 return EOF;
2556 if (p >= bp->s + char_block::SIZE) {
2557 p = bp->next->s;
2558 }
2559 return *p;
2560 }
2561
get_location(int allow_macro,const char ** filep,int * linep)2562 int string_iterator::get_location(int allow_macro,
2563 const char **filep, int *linep)
2564 {
2565 if (!allow_macro)
2566 return 0;
2567 if (mac.filename == 0)
2568 return 0;
2569 *filep = mac.filename;
2570 *linep = mac.lineno + lineno - 1;
2571 return 1;
2572 }
2573
backtrace()2574 void string_iterator::backtrace()
2575 {
2576 if (mac.filename) {
2577 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
2578 if (how_invoked) {
2579 if (!nm.is_null())
2580 errprint(": %1 `%2'\n", how_invoked, nm.contents());
2581 else
2582 errprint(": %1\n", how_invoked);
2583 }
2584 else
2585 errprint("\n");
2586 }
2587 }
2588
2589 class temp_iterator : public input_iterator {
2590 unsigned char *base;
2591 temp_iterator(const char *, int len);
2592 public:
2593 ~temp_iterator();
2594 friend input_iterator *make_temp_iterator(const char *);
2595 };
2596
2597 #ifdef __GNUG__
2598 inline
2599 #endif
temp_iterator(const char * s,int len)2600 temp_iterator::temp_iterator(const char *s, int len)
2601 {
2602 base = new unsigned char[len];
2603 memcpy(base, s, len);
2604 ptr = base;
2605 eptr = base + len;
2606 }
2607
~temp_iterator()2608 temp_iterator::~temp_iterator()
2609 {
2610 a_delete base;
2611 }
2612
2613 class small_temp_iterator : public input_iterator {
2614 private:
2615 small_temp_iterator(const char *, int);
2616 ~small_temp_iterator();
2617 enum { BLOCK = 16 };
2618 static small_temp_iterator *free_list;
2619 void *operator new(size_t);
2620 void operator delete(void *);
2621 enum { SIZE = 12 };
2622 unsigned char buf[SIZE];
2623 friend input_iterator *make_temp_iterator(const char *);
2624 };
2625
2626 small_temp_iterator *small_temp_iterator::free_list = 0;
2627
operator new(size_t n)2628 void *small_temp_iterator::operator new(size_t n)
2629 {
2630 assert(n == sizeof(small_temp_iterator));
2631 if (!free_list) {
2632 free_list = (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
2633 for (int i = 0; i < BLOCK - 1; i++)
2634 free_list[i].next = free_list + i + 1;
2635 free_list[BLOCK-1].next = 0;
2636 }
2637 small_temp_iterator *p = free_list;
2638 free_list = (small_temp_iterator *)(free_list->next);
2639 p->next = 0;
2640 return p;
2641 }
2642
2643 #ifdef __GNUG__
2644 inline
2645 #endif
operator delete(void * p)2646 void small_temp_iterator::operator delete(void *p)
2647 {
2648 if (p) {
2649 ((small_temp_iterator *)p)->next = free_list;
2650 free_list = (small_temp_iterator *)p;
2651 }
2652 }
2653
~small_temp_iterator()2654 small_temp_iterator::~small_temp_iterator()
2655 {
2656 }
2657
2658
2659 #ifdef __GNUG__
2660 inline
2661 #endif
small_temp_iterator(const char * s,int len)2662 small_temp_iterator::small_temp_iterator(const char *s, int len)
2663 {
2664 for (int i = 0; i < len; i++)
2665 buf[i] = s[i];
2666 ptr = buf;
2667 eptr = buf + len;
2668 }
2669
make_temp_iterator(const char * s)2670 input_iterator *make_temp_iterator(const char *s)
2671 {
2672 if (s == 0)
2673 return new small_temp_iterator(s, 0);
2674 else {
2675 int n = strlen(s);
2676 if (n <= small_temp_iterator::SIZE)
2677 return new small_temp_iterator(s, n);
2678 else
2679 return new temp_iterator(s, n);
2680 }
2681 }
2682
2683 // this is used when macros are interpolated using the .macro_name notation
2684
2685 struct arg_list {
2686 macro mac;
2687 arg_list *next;
2688 arg_list(const macro &);
2689 ~arg_list();
2690 };
2691
arg_list(const macro & m)2692 arg_list::arg_list(const macro &m) : mac(m), next(0)
2693 {
2694 }
2695
~arg_list()2696 arg_list::~arg_list()
2697 {
2698 }
2699
2700 class macro_iterator : public string_iterator {
2701 arg_list *args;
2702 int argc;
2703 public:
2704 macro_iterator(symbol, macro &);
2705 macro_iterator();
2706 ~macro_iterator();
has_args()2707 int has_args() { return 1; }
2708 input_iterator *get_arg(int i);
nargs()2709 int nargs() { return argc; }
2710 void add_arg(const macro &m);
2711 void shift(int n);
2712 };
2713
get_arg(int i)2714 input_iterator *macro_iterator::get_arg(int i)
2715 {
2716 if (i == 0)
2717 return make_temp_iterator(nm.contents());
2718 if (i > 0 && i <= argc) {
2719 arg_list *p = args;
2720 for (int j = 1; j < i; j++) {
2721 assert(p != 0);
2722 p = p->next;
2723 }
2724 return new string_iterator(p->mac);
2725 }
2726 else
2727 return 0;
2728 }
2729
add_arg(const macro & m)2730 void macro_iterator::add_arg(const macro &m)
2731 {
2732 for (arg_list **p = &args; *p; p = &((*p)->next))
2733 ;
2734 *p = new arg_list(m);
2735 ++argc;
2736 }
2737
shift(int n)2738 void macro_iterator::shift(int n)
2739 {
2740 while (n > 0 && argc > 0) {
2741 arg_list *tem = args;
2742 args = args->next;
2743 delete tem;
2744 --argc;
2745 --n;
2746 }
2747 }
2748
2749 // This gets used by eg .if '\?xxx\?''.
2750
operator ==(const macro & m1,const macro & m2)2751 int operator==(const macro &m1, const macro &m2)
2752 {
2753 if (m1.length != m2.length)
2754 return 0;
2755 string_iterator iter1(m1);
2756 string_iterator iter2(m2);
2757 int n = m1.length;
2758 while (--n >= 0) {
2759 node *nd1 = 0;
2760 int c1 = iter1.get(&nd1);
2761 assert(c1 != EOF);
2762 node *nd2 = 0;
2763 int c2 = iter2.get(&nd2);
2764 assert(c2 != EOF);
2765 if (c1 != c2) {
2766 if (c1 == 0)
2767 delete nd1;
2768 else if (c2 == 0)
2769 delete nd2;
2770 return 0;
2771 }
2772 if (c1 == 0) {
2773 assert(nd1 != 0);
2774 assert(nd2 != 0);
2775 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
2776 delete nd1;
2777 delete nd2;
2778 if (!are_same)
2779 return 0;
2780 }
2781 }
2782 return 1;
2783 }
2784
interpolate_macro(symbol nm)2785 static void interpolate_macro(symbol nm)
2786 {
2787 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
2788 if (p == 0) {
2789 int warned = 0;
2790 const char *s = nm.contents();
2791 if (strlen(s) > 2) {
2792 request_or_macro *r;
2793 char buf[3];
2794 buf[0] = s[0];
2795 buf[1] = s[1];
2796 buf[2] = '\0';
2797 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
2798 if (r) {
2799 macro *m = r->to_macro();
2800 if (!m || !m->empty())
2801 warned = warning(WARN_SPACE,
2802 "space required between `%1' and argument", buf);
2803 }
2804 }
2805 if (!warned) {
2806 warning(WARN_MAC, "`%1' not defined", nm.contents());
2807 p = new macro;
2808 request_dictionary.define(nm, p);
2809 }
2810 }
2811 if (p)
2812 p->invoke(nm);
2813 else {
2814 skip_line();
2815 return;
2816 }
2817 }
2818
decode_args(macro_iterator * mi)2819 static void decode_args(macro_iterator *mi)
2820 {
2821 if (!tok.newline() && !tok.eof()) {
2822 node *n;
2823 int c = get_copy(&n);
2824 for (;;) {
2825 while (c == ' ')
2826 c = get_copy(&n);
2827 if (c == '\n' || c == EOF)
2828 break;
2829 macro arg;
2830 int quote_input_level = 0;
2831 if (c == '\"') {
2832 quote_input_level = input_stack::get_level();
2833 c = get_copy(&n);
2834 }
2835 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
2836 if (quote_input_level > 0 && c == '\"'
2837 && (compatible_flag
2838 || input_stack::get_level() == quote_input_level)) {
2839 c = get_copy(&n);
2840 if (c == '"') {
2841 arg.append(c);
2842 c = get_copy(&n);
2843 }
2844 else
2845 break;
2846 }
2847 else {
2848 if (c == 0)
2849 arg.append(n);
2850 else
2851 arg.append(c);
2852 c = get_copy(&n);
2853 }
2854 }
2855 mi->add_arg(arg);
2856 }
2857 }
2858 }
2859
invoke(symbol nm)2860 void macro::invoke(symbol nm)
2861 {
2862 macro_iterator *mi = new macro_iterator(nm, *this);
2863 decode_args(mi);
2864 input_stack::push(mi);
2865 tok.next();
2866 }
2867
to_macro()2868 macro *macro::to_macro()
2869 {
2870 return this;
2871 }
2872
empty()2873 int macro::empty()
2874 {
2875 return length == 0;
2876 }
2877
macro_iterator(symbol s,macro & m)2878 macro_iterator::macro_iterator(symbol s, macro &m)
2879 : string_iterator(m, "macro", s), args(0), argc(0)
2880 {
2881 }
2882
macro_iterator()2883 macro_iterator::macro_iterator() : args(0), argc(0)
2884 {
2885 }
2886
~macro_iterator()2887 macro_iterator::~macro_iterator()
2888 {
2889 while (args != 0) {
2890 arg_list *tem = args;
2891 args = args->next;
2892 delete tem;
2893 }
2894 }
2895
read_request()2896 void read_request()
2897 {
2898 macro_iterator *mi = new macro_iterator;
2899 int had_prompt = 0;
2900 if (!tok.newline() && !tok.eof()) {
2901 int c = get_copy(NULL);
2902 while (c == ' ')
2903 c = get_copy(NULL);
2904 while (c != EOF && c != '\n' && c != ' ') {
2905 if (!illegal_input_char(c)) {
2906 fputc(c, stderr);
2907 had_prompt = 1;
2908 }
2909 c = get_copy(NULL);
2910 }
2911 if (c == ' ') {
2912 tok.make_space();
2913 decode_args(mi);
2914 }
2915 }
2916 fputc(had_prompt ? ':' : '\007', stderr);
2917 fflush(stderr);
2918 input_stack::push(mi);
2919 macro mac;
2920 int nl = 0;
2921 int c;
2922 while ((c = getchar()) != EOF) {
2923 if (illegal_input_char(c))
2924 warning(WARN_INPUT, "illegal input character code %1", int(c));
2925 else {
2926 if (c == '\n') {
2927 if (nl)
2928 break;
2929 else
2930 nl = 1;
2931 }
2932 else
2933 nl = 0;
2934 mac.append(c);
2935 }
2936 }
2937 input_stack::push(new string_iterator(mac));
2938 tok.next();
2939 }
2940
2941
do_define_string(int append)2942 void do_define_string(int append)
2943 {
2944 symbol nm;
2945 node *n;
2946 int c;
2947 nm = get_name(1);
2948 if (nm.is_null()) {
2949 skip_line();
2950 return;
2951 }
2952 if (tok.newline())
2953 c = '\n';
2954 else if (tok.tab())
2955 c = '\t';
2956 else if (!tok.space()) {
2957 error("bad string definition");
2958 skip_line();
2959 return;
2960 }
2961 else
2962 c = get_copy(&n);
2963 while (c == ' ')
2964 c = get_copy(&n);
2965 if (c == '"')
2966 c = get_copy(&n);
2967 macro mac;
2968 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
2969 macro *mm = rm ? rm->to_macro() : 0;
2970 if (append && mm)
2971 mac = *mm;
2972 while (c != '\n' && c != EOF) {
2973 if (c == 0)
2974 mac.append(n);
2975 else
2976 mac.append((unsigned char)c);
2977 c = get_copy(&n);
2978 }
2979 if (!mm) {
2980 mm = new macro;
2981 request_dictionary.define(nm, mm);
2982 }
2983 *mm = mac;
2984 tok.next();
2985 }
2986
define_string()2987 void define_string()
2988 {
2989 do_define_string(0);
2990 }
2991
append_string()2992 void append_string()
2993 {
2994 do_define_string(1);
2995 }
2996
define_character()2997 void define_character()
2998 {
2999 node *n;
3000 int c;
3001 tok.skip();
3002 charinfo *ci = tok.get_char(1);
3003 if (ci == 0) {
3004 skip_line();
3005 return;
3006 }
3007 tok.next();
3008 if (tok.newline())
3009 c = '\n';
3010 else if (tok.tab())
3011 c = '\t';
3012 else if (!tok.space()) {
3013 error("bad character definition");
3014 skip_line();
3015 return;
3016 }
3017 else
3018 c = get_copy(&n);
3019 while (c == ' ' || c == '\t')
3020 c = get_copy(&n);
3021 if (c == '"')
3022 c = get_copy(&n);
3023 macro *m = new macro;
3024 while (c != '\n' && c != EOF) {
3025 if (c == 0)
3026 m->append(n);
3027 else
3028 m->append((unsigned char)c);
3029 c = get_copy(&n);
3030 }
3031 m = ci->set_macro(m);
3032 if (m)
3033 delete m;
3034 tok.next();
3035 }
3036
3037
remove_character()3038 static void remove_character()
3039 {
3040 tok.skip();
3041 while (!tok.newline() && !tok.eof()) {
3042 if (!tok.space() && !tok.tab()) {
3043 charinfo *ci = tok.get_char(1);
3044 if (!ci)
3045 break;
3046 macro *m = ci->set_macro(0);
3047 if (m)
3048 delete m;
3049 }
3050 tok.next();
3051 }
3052 skip_line();
3053 }
3054
interpolate_string(symbol nm)3055 static void interpolate_string(symbol nm)
3056 {
3057 request_or_macro *p = lookup_request(nm);
3058 macro *m = p->to_macro();
3059 if (!m)
3060 error("you can only invoke a string using \\*");
3061 else {
3062 string_iterator *si = new string_iterator(*m, "string", nm);
3063 input_stack::push(si);
3064 }
3065 }
3066
3067 /* This class is used for the implementation of \$@. It is used for
3068 each of the closing double quotes. It artificially increases the
3069 input level by 2, so that the closing double quote will appear to have
3070 the same input level as the opening quote. */
3071
3072 class end_quote_iterator : public input_iterator {
3073 unsigned char buf[1];
3074 public:
3075 end_quote_iterator();
~end_quote_iterator()3076 ~end_quote_iterator() { }
internal_level()3077 int internal_level() { return 2; }
3078 };
3079
end_quote_iterator()3080 end_quote_iterator::end_quote_iterator()
3081 {
3082 buf[0] = '"';
3083 ptr = buf;
3084 eptr = buf + 1;
3085 }
3086
interpolate_arg(symbol nm)3087 static void interpolate_arg(symbol nm)
3088 {
3089 const char *s = nm.contents();
3090 if (!s || *s == '\0')
3091 error("missing argument name");
3092 else if (s[1] == 0 && csdigit(s[0]))
3093 input_stack::push(input_stack::get_arg(s[0] - '0'));
3094 else if (s[0] == '*' && s[1] == '\0') {
3095 for (int i = input_stack::nargs(); i > 0; i--) {
3096 input_stack::push(input_stack::get_arg(i));
3097 if (i != 1)
3098 input_stack::push(make_temp_iterator(" "));
3099 }
3100 }
3101 else if (s[0] == '@' && s[1] == '\0') {
3102 for (int i = input_stack::nargs(); i > 0; i--) {
3103 input_stack::push(new end_quote_iterator);
3104 input_stack::push(input_stack::get_arg(i));
3105 input_stack::push(make_temp_iterator(i == 1 ? "\"" : " \""));
3106 }
3107 }
3108 else {
3109 for (const char *p = s; *p && csdigit(*p); p++)
3110 ;
3111 if (*p)
3112 error("bad argument name `%1'", s);
3113 else
3114 input_stack::push(input_stack::get_arg(atoi(s)));
3115 }
3116 }
3117
handle_first_page_transition()3118 void handle_first_page_transition()
3119 {
3120 push_token(tok);
3121 topdiv->begin_page();
3122 }
3123
3124 // We push back a token by wrapping it up in a token_node, and
3125 // wrapping that up in a string_iterator.
3126
push_token(const token & t)3127 static void push_token(const token &t)
3128 {
3129 macro m;
3130 m.append(new token_node(t));
3131 input_stack::push(new string_iterator(m));
3132 }
3133
push_page_ejector()3134 void push_page_ejector()
3135 {
3136 static char buf[2] = { PAGE_EJECTOR, '\0' };
3137 input_stack::push(make_temp_iterator(buf));
3138 }
3139
handle_initial_request(unsigned char code)3140 void handle_initial_request(unsigned char code)
3141 {
3142 char buf[2];
3143 buf[0] = code;
3144 buf[1] = '\0';
3145 macro mac;
3146 mac.append(new token_node(tok));
3147 input_stack::push(new string_iterator(mac));
3148 input_stack::push(make_temp_iterator(buf));
3149 topdiv->begin_page();
3150 tok.next();
3151 }
3152
handle_initial_title()3153 void handle_initial_title()
3154 {
3155 handle_initial_request(TITLE_REQUEST);
3156 }
3157
3158 int trap_sprung_flag = 0;
3159 int postpone_traps_flag = 0;
3160 symbol postponed_trap;
3161
spring_trap(symbol nm)3162 void spring_trap(symbol nm)
3163 {
3164 assert(!nm.is_null());
3165 trap_sprung_flag = 1;
3166 if (postpone_traps_flag) {
3167 postponed_trap = nm;
3168 return;
3169 }
3170 static char buf[2] = { BEGIN_TRAP, 0 };
3171 static char buf2[2] = { END_TRAP, '\0' };
3172 input_stack::push(make_temp_iterator(buf2));
3173 request_or_macro *p = lookup_request(nm);
3174 macro *m = p->to_macro();
3175 if (m)
3176 input_stack::push(new string_iterator(*m, "trap-invoked macro", nm));
3177 else
3178 error("you can't invoke a request with a trap");
3179 input_stack::push(make_temp_iterator(buf));
3180 }
3181
postpone_traps()3182 void postpone_traps()
3183 {
3184 postpone_traps_flag = 1;
3185 }
3186
unpostpone_traps()3187 int unpostpone_traps()
3188 {
3189 postpone_traps_flag = 0;
3190 if (!postponed_trap.is_null()) {
3191 spring_trap(postponed_trap);
3192 postponed_trap = NULL_SYMBOL;
3193 return 1;
3194 }
3195 else
3196 return 0;
3197 }
3198
3199 // this should be local to define_macro, but cfront 1.2 doesn't support that
3200 static symbol dot_symbol(".");
3201
3202 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
3203
do_define_macro(define_mode mode)3204 void do_define_macro(define_mode mode)
3205 {
3206 symbol nm;
3207 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3208 nm = get_name(1);
3209 if (nm.is_null()) {
3210 skip_line();
3211 return;
3212 }
3213 }
3214 symbol term = get_name(); // the request that terminates the definition
3215 if (term.is_null())
3216 term = dot_symbol;
3217 while (!tok.newline() && !tok.eof())
3218 tok.next();
3219 const char *start_filename;
3220 int start_lineno;
3221 int have_start_location = input_stack::get_location(0, &start_filename,
3222 &start_lineno);
3223 node *n;
3224 // doing this here makes the line numbers come out right
3225 int c = get_copy(&n, 1);
3226 macro mac;
3227 macro *mm = 0;
3228 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3229 request_or_macro *rm =
3230 (request_or_macro *)request_dictionary.lookup(nm);
3231 if (rm)
3232 mm = rm->to_macro();
3233 if (mm && mode == DEFINE_APPEND)
3234 mac = *mm;
3235 }
3236 int bol = 1;
3237 for (;;) {
3238 while (c == ESCAPE_NEWLINE) {
3239 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
3240 mac.append(c);
3241 c = get_copy(&n, 1);
3242 }
3243 if (bol && c == '.') {
3244 const char *s = term.contents();
3245 int d;
3246 // see if it matches term
3247 for (int i = 0; s[i] != 0; i++) {
3248 d = get_copy(&n);
3249 if ((unsigned char)s[i] != d)
3250 break;
3251 }
3252 if (s[i] == 0
3253 && ((i == 2 && compatible_flag)
3254 || (d = get_copy(&n)) == ' '
3255 || d == '\n')) { // we found it
3256 if (d == '\n')
3257 tok.make_newline();
3258 else
3259 tok.make_space();
3260 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
3261 if (!mm) {
3262 mm = new macro;
3263 request_dictionary.define(nm, mm);
3264 }
3265 *mm = mac;
3266 }
3267 if (term != dot_symbol)
3268 interpolate_macro(term);
3269 else
3270 skip_line();
3271 return;
3272 }
3273 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
3274 mac.append(c);
3275 for (int j = 0; j < i; j++)
3276 mac.append(s[j]);
3277 }
3278 c = d;
3279 }
3280 if (c == EOF) {
3281 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3282 if (have_start_location)
3283 error_with_file_and_line(start_filename, start_lineno,
3284 "end of file while defining macro `%1'",
3285 nm.contents());
3286 else
3287 error("end of file while defining macro `%1'", nm.contents());
3288 }
3289 else {
3290 if (have_start_location)
3291 error_with_file_and_line(start_filename, start_lineno,
3292 "end of file while ignoring input lines");
3293 else
3294 error("end of file while ignoring input lines");
3295 }
3296 tok.next();
3297 return;
3298 }
3299 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3300 if (c == 0)
3301 mac.append(n);
3302 else
3303 mac.append(c);
3304 }
3305 bol = (c == '\n');
3306 c = get_copy(&n, 1);
3307 }
3308 }
3309
define_macro()3310 void define_macro()
3311 {
3312 do_define_macro(DEFINE_NORMAL);
3313 }
3314
append_macro()3315 void append_macro()
3316 {
3317 do_define_macro(DEFINE_APPEND);
3318 }
3319
ignore()3320 void ignore()
3321 {
3322 do_define_macro(DEFINE_IGNORE);
3323 }
3324
remove_macro()3325 void remove_macro()
3326 {
3327 for (;;) {
3328 symbol s = get_name();
3329 if (s.is_null())
3330 break;
3331 request_dictionary.remove(s);
3332 }
3333 skip_line();
3334 }
3335
rename_macro()3336 void rename_macro()
3337 {
3338 symbol s1 = get_name(1);
3339 if (!s1.is_null()) {
3340 symbol s2 = get_name(1);
3341 if (!s2.is_null())
3342 request_dictionary.rename(s1, s2);
3343 }
3344 skip_line();
3345 }
3346
alias_macro()3347 void alias_macro()
3348 {
3349 symbol s1 = get_name(1);
3350 if (!s1.is_null()) {
3351 symbol s2 = get_name(1);
3352 if (!s2.is_null()) {
3353 if (!request_dictionary.alias(s1, s2))
3354 warning(WARN_MAC, "`%1' not defined", s2.contents());
3355 }
3356 }
3357 skip_line();
3358 }
3359
chop_macro()3360 void chop_macro()
3361 {
3362 symbol s = get_name(1);
3363 if (!s.is_null()) {
3364 request_or_macro *p = lookup_request(s);
3365 macro *m = p->to_macro();
3366 if (!m)
3367 error("cannot chop request");
3368 else if (m->length == 0)
3369 error("cannot chop empty macro");
3370 else
3371 m->length -= 1;
3372 }
3373 skip_line();
3374 }
3375
asciify_macro()3376 void asciify_macro()
3377 {
3378 symbol s = get_name(1);
3379 if (!s.is_null()) {
3380 request_or_macro *p = lookup_request(s);
3381 macro *m = p->to_macro();
3382 if (!m)
3383 error("cannot asciify request");
3384 else {
3385 macro am;
3386 string_iterator iter(*m);
3387 for (;;) {
3388 node *nd;
3389 int c = iter.get(&nd);
3390 if (c == EOF)
3391 break;
3392 if (c != 0)
3393 am.append(c);
3394 else
3395 nd->asciify(&am);
3396 }
3397 *m = am;
3398 }
3399 }
3400 skip_line();
3401 }
3402
interpolate_environment_variable(symbol nm)3403 static void interpolate_environment_variable(symbol nm)
3404 {
3405 const char *s = getenv(nm.contents());
3406 if (s && *s)
3407 input_stack::push(make_temp_iterator(s));
3408 }
3409
interpolate_number_reg(symbol nm,int inc)3410 void interpolate_number_reg(symbol nm, int inc)
3411 {
3412 reg *r = lookup_number_reg(nm);
3413 if (inc < 0)
3414 r->decrement();
3415 else if (inc > 0)
3416 r->increment();
3417 input_stack::push(make_temp_iterator(r->get_string()));
3418 }
3419
interpolate_number_format(symbol nm)3420 static void interpolate_number_format(symbol nm)
3421 {
3422 reg *r = (reg *)number_reg_dictionary.lookup(nm);
3423 if (r)
3424 input_stack::push(make_temp_iterator(r->get_format()));
3425 }
3426
get_delim_number(units * n,int si,int prev_value)3427 static int get_delim_number(units *n, int si, int prev_value)
3428 {
3429 token start;
3430 start.next();
3431 if (start.delimiter(1)) {
3432 tok.next();
3433 if (get_number(n, si, prev_value)) {
3434 if (start != tok)
3435 warning(WARN_DELIM, "closing delimiter does not match");
3436 return 1;
3437 }
3438 }
3439 return 0;
3440 }
3441
get_delim_number(units * n,int si)3442 static int get_delim_number(units *n, int si)
3443 {
3444 token start;
3445 start.next();
3446 if (start.delimiter(1)) {
3447 tok.next();
3448 if (get_number(n, si)) {
3449 if (start != tok)
3450 warning(WARN_DELIM, "closing delimiter does not match");
3451 return 1;
3452 }
3453 }
3454 return 0;
3455 }
3456
get_line_arg(units * n,int si,charinfo ** cp)3457 static int get_line_arg(units *n, int si, charinfo **cp)
3458 {
3459 token start;
3460 start.next();
3461 if (start.delimiter(1)) {
3462 tok.next();
3463 if (get_number(n, si)) {
3464 if (tok.dummy())
3465 tok.next();
3466 if (start != tok) {
3467 *cp = tok.get_char(1);
3468 tok.next();
3469 }
3470 if (start != tok)
3471 warning(WARN_DELIM, "closing delimiter does not match");
3472 return 1;
3473 }
3474 }
3475 return 0;
3476 }
3477
read_size(int * x)3478 static int read_size(int *x)
3479 {
3480 tok.next();
3481 int c = tok.ch();
3482 int inc = 0;
3483 if (c == '-') {
3484 inc = -1;
3485 tok.next();
3486 c = tok.ch();
3487 }
3488 else if (c == '+') {
3489 inc = 1;
3490 tok.next();
3491 c = tok.ch();
3492 }
3493 int val;
3494 int bad = 0;
3495 if (c == '(') {
3496 tok.next();
3497 c = tok.ch();
3498 if (!inc) {
3499 // allow an increment either before or after the left parenthesis
3500 if (c == '-') {
3501 inc = -1;
3502 tok.next();
3503 c = tok.ch();
3504 }
3505 else if (c == '+') {
3506 inc = 1;
3507 tok.next();
3508 c = tok.ch();
3509 }
3510 }
3511 if (!csdigit(c))
3512 bad = 1;
3513 else {
3514 val = c - '0';
3515 tok.next();
3516 c = tok.ch();
3517 if (!csdigit(c))
3518 bad = 1;
3519 else {
3520 val = val*10 + (c - '0');
3521 val *= sizescale;
3522 }
3523 }
3524 }
3525 else if (csdigit(c)) {
3526 val = c - '0';
3527 if (!inc && c != '0' && c < '4') {
3528 tok.next();
3529 c = tok.ch();
3530 if (!csdigit(c))
3531 bad = 1;
3532 else
3533 val = val*10 + (c - '0');
3534 }
3535 val *= sizescale;
3536 }
3537 else if (!tok.delimiter(1))
3538 return 0;
3539 else {
3540 token start(tok);
3541 tok.next();
3542 if (!(inc
3543 ? get_number(&val, 'z')
3544 : get_number(&val, 'z', curenv->get_requested_point_size())))
3545 return 0;
3546 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
3547 if (start.ch() == '[')
3548 error("missing `]'");
3549 else
3550 error("missing closing delimiter");
3551 return 0;
3552 }
3553 }
3554 if (!bad) {
3555 switch (inc) {
3556 case 0:
3557 *x = val;
3558 break;
3559 case 1:
3560 *x = curenv->get_requested_point_size() + val;
3561 break;
3562 case -1:
3563 *x = curenv->get_requested_point_size() - val;
3564 break;
3565 default:
3566 assert(0);
3567 }
3568 return 1;
3569 }
3570 else {
3571 error("bad digit in point size");
3572 return 0;
3573 }
3574 }
3575
get_delim_name()3576 static symbol get_delim_name()
3577 {
3578 token start;
3579 start.next();
3580 if (start.eof()) {
3581 error("end of input at start of delimited name");
3582 return NULL_SYMBOL;
3583 }
3584 if (start.newline()) {
3585 error("can't delimit name with a newline");
3586 return NULL_SYMBOL;
3587 }
3588 int start_level = input_stack::get_level();
3589 char abuf[ABUF_SIZE];
3590 char *buf = abuf;
3591 int buf_size = ABUF_SIZE;
3592 int i = 0;
3593 for (;;) {
3594 if (i + 1 > buf_size) {
3595 if (buf == abuf) {
3596 buf = new char [ABUF_SIZE*2];
3597 memcpy(buf, abuf, buf_size);
3598 buf_size = ABUF_SIZE*2;
3599 }
3600 else {
3601 char *old_buf = buf;
3602 buf = new char[buf_size*2];
3603 memcpy(buf, old_buf, buf_size);
3604 buf_size *= 2;
3605 a_delete old_buf;
3606 }
3607 }
3608 tok.next();
3609 if (tok == start
3610 && (compatible_flag || input_stack::get_level() == start_level))
3611 break;
3612 if ((buf[i] = tok.ch()) == 0) {
3613 error("missing delimiter (got %1)", tok.description());
3614 if (buf != abuf)
3615 a_delete buf;
3616 return NULL_SYMBOL;
3617 }
3618 i++;
3619 }
3620 buf[i] = '\0';
3621 if (buf == abuf) {
3622 if (i == 0) {
3623 error("empty delimited name");
3624 return NULL_SYMBOL;
3625 }
3626 else
3627 return symbol(buf);
3628 }
3629 else {
3630 symbol s(buf);
3631 a_delete buf;
3632 return s;
3633 }
3634 }
3635
3636
3637 // Implement \R
3638
do_register()3639 static void do_register()
3640 {
3641 token start;
3642 start.next();
3643 if (!start.delimiter(1))
3644 return;
3645 tok.next();
3646 symbol nm = get_long_name(1);
3647 if (nm.is_null())
3648 return;
3649 while (tok.space())
3650 tok.next();
3651 reg *r = (reg *)number_reg_dictionary.lookup(nm);
3652 int prev_value;
3653 if (!r || !r->get_value(&prev_value))
3654 prev_value = 0;
3655 int val;
3656 if (!get_number(&val, 'u', prev_value))
3657 return;
3658 if (start != tok)
3659 warning(WARN_DELIM, "closing delimiter does not match");
3660 if (r)
3661 r->set_value(val);
3662 else
3663 set_number_reg(nm, val);
3664 }
3665
3666 // this implements the \w escape sequence
3667
do_width()3668 static void do_width()
3669 {
3670 token start;
3671 start.next();
3672 int start_level = input_stack::get_level();
3673 environment env(curenv);
3674 environment *oldenv = curenv;
3675 curenv = &env;
3676 for (;;) {
3677 tok.next();
3678 if (tok.eof()) {
3679 warning(WARN_DELIM, "missing closing delimiter");
3680 break;
3681 }
3682 if (tok.newline()) {
3683 warning(WARN_DELIM, "missing closing delimiter");
3684 input_stack::push(make_temp_iterator("\n"));
3685 break;
3686 }
3687 if (tok == start
3688 && (compatible_flag || input_stack::get_level() == start_level))
3689 break;
3690 tok.process();
3691 }
3692 env.wrap_up_tab();
3693 units x = env.get_input_line_position().to_units();
3694 input_stack::push(make_temp_iterator(itoa(x)));
3695 env.width_registers();
3696 curenv = oldenv;
3697 }
3698
3699 charinfo *page_character;
3700
set_page_character()3701 void set_page_character()
3702 {
3703 page_character = get_optional_char();
3704 skip_line();
3705 }
3706
3707 static const symbol percent_symbol("%");
3708
read_title_parts(node ** part,hunits * part_width)3709 void read_title_parts(node **part, hunits *part_width)
3710 {
3711 tok.skip();
3712 if (tok.newline() || tok.eof())
3713 return;
3714 token start(tok);
3715 int start_level = input_stack::get_level();
3716 tok.next();
3717 for (int i = 0; i < 3; i++) {
3718 while (!tok.newline() && !tok.eof()) {
3719 if (tok == start
3720 && (compatible_flag || input_stack::get_level() == start_level)) {
3721 tok.next();
3722 break;
3723 }
3724 if (page_character != 0 && tok.get_char() == page_character)
3725 interpolate_number_reg(percent_symbol, 0);
3726 else
3727 tok.process();
3728 tok.next();
3729 }
3730 curenv->wrap_up_tab();
3731 part_width[i] = curenv->get_input_line_position();
3732 part[i] = curenv->extract_output_line();
3733 }
3734 while (!tok.newline() && !tok.eof())
3735 tok.next();
3736 }
3737
3738 class non_interpreted_node : public node {
3739 macro mac;
3740 public:
3741 non_interpreted_node(const macro &);
3742 int interpret(macro *);
3743 node *copy();
3744 int same(node *);
3745 const char *type();
3746 };
3747
non_interpreted_node(const macro & m)3748 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
3749 {
3750 }
3751
same(node * nd)3752 int non_interpreted_node::same(node *nd)
3753 {
3754 return mac == ((non_interpreted_node *)nd)->mac;
3755 }
3756
type()3757 const char *non_interpreted_node::type()
3758 {
3759 return "non_interpreted_node";
3760 }
3761
copy()3762 node *non_interpreted_node::copy()
3763 {
3764 return new non_interpreted_node(mac);
3765 }
3766
interpret(macro * m)3767 int non_interpreted_node::interpret(macro *m)
3768 {
3769 string_iterator si(mac);
3770 node *n;
3771 for (;;) {
3772 int c = si.get(&n);
3773 if (c == EOF)
3774 break;
3775 if (c == 0)
3776 m->append(n);
3777 else
3778 m->append(c);
3779 }
3780 return 1;
3781 }
3782
do_non_interpreted()3783 static node *do_non_interpreted()
3784 {
3785 node *n;
3786 int c;
3787 macro mac;
3788 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
3789 if (c == 0)
3790 mac.append(n);
3791 else
3792 mac.append(c);
3793 if (c == EOF || c == '\n') {
3794 error("missing \\?");
3795 return 0;
3796 }
3797 return new non_interpreted_node(mac);
3798 }
3799
do_special()3800 node *do_special()
3801 {
3802 token start;
3803 start.next();
3804 int start_level = input_stack::get_level();
3805 macro mac;
3806 for (tok.next();
3807 tok != start || input_stack::get_level() != start_level;
3808 tok.next()) {
3809 if (tok.eof()) {
3810 warning(WARN_DELIM, "missing closing delimiter");
3811 return 0;
3812 }
3813 if (tok.newline()) {
3814 input_stack::push(make_temp_iterator("\n"));
3815 warning(WARN_DELIM, "missing closing delimiter");
3816 break;
3817 }
3818 unsigned char c;
3819 if (tok.space())
3820 c = ' ';
3821 else if (tok.tab())
3822 c = '\t';
3823 else if (tok.leader())
3824 c = '\001';
3825 else if (tok.backspace())
3826 c = '\b';
3827 else
3828 c = tok.ch();
3829 if (c == '\0')
3830 error("%1 is illegal within \\X", tok.description());
3831 else
3832 mac.append(c);
3833 }
3834 return new special_node(mac);
3835 }
3836
tprint(troff_output_file * out)3837 void special_node::tprint(troff_output_file *out)
3838 {
3839 tprint_start(out);
3840 string_iterator iter(mac);
3841 for (;;) {
3842 int c = iter.get(NULL);
3843 if (c == EOF)
3844 break;
3845 for (const char *s = ::asciify(c); *s; s++)
3846 tprint_char(out, *s);
3847 }
3848 tprint_end(out);
3849 }
3850
get_file_line(const char ** filename,int * lineno)3851 int get_file_line(const char **filename, int *lineno)
3852 {
3853 return input_stack::get_location(0, filename, lineno);
3854 }
3855
line_file()3856 void line_file()
3857 {
3858 int n;
3859 if (get_integer(&n)) {
3860 const char *filename = 0;
3861 if (has_arg()) {
3862 symbol s = get_long_name();
3863 filename = s.contents();
3864 }
3865 (void)input_stack::set_location(filename, n-1);
3866 }
3867 skip_line();
3868 }
3869
3870 static int nroff_mode = 0;
3871
nroff_request()3872 static void nroff_request()
3873 {
3874 nroff_mode = 1;
3875 skip_line();
3876 }
3877
troff_request()3878 static void troff_request()
3879 {
3880 nroff_mode = 0;
3881 skip_line();
3882 }
3883
skip_alternative()3884 static void skip_alternative()
3885 {
3886 int level = 0;
3887 // ensure that ``.if 0\{'' works as expected
3888 if (tok.left_brace())
3889 level++;
3890 int c;
3891 for (;;) {
3892 c = input_stack::get(NULL);
3893 if (c == EOF)
3894 break;
3895 if (c == ESCAPE_LEFT_BRACE)
3896 ++level;
3897 else if (c == ESCAPE_RIGHT_BRACE)
3898 --level;
3899 else if (c == escape_char && escape_char > 0)
3900 switch(input_stack::get(NULL)) {
3901 case '{':
3902 ++level;
3903 break;
3904 case '}':
3905 --level;
3906 break;
3907 case '"':
3908 while ((c = input_stack::get(NULL)) != '\n' && c != EOF)
3909 ;
3910 }
3911 /*
3912 Note that the level can properly be < 0, eg
3913
3914 .if 1 \{\
3915 .if 0 \{\
3916 .\}\}
3917
3918 So don't give an error message in this case.
3919 */
3920 if (level <= 0 && c == '\n')
3921 break;
3922 }
3923 tok.next();
3924 }
3925
begin_alternative()3926 static void begin_alternative()
3927 {
3928 while (tok.space() || tok.left_brace())
3929 tok.next();
3930 }
3931
3932
3933 static int_stack if_else_stack;
3934
do_if_request()3935 int do_if_request()
3936 {
3937 int invert = 0;
3938 while (tok.space())
3939 tok.next();
3940 while (tok.ch() == '!') {
3941 tok.next();
3942 invert = !invert;
3943 }
3944 int result;
3945 unsigned char c = tok.ch();
3946 if (c == 't') {
3947 tok.next();
3948 result = !nroff_mode;
3949 }
3950 else if (c == 'n') {
3951 tok.next();
3952 result = nroff_mode;
3953 }
3954 else if (c == 'v') {
3955 tok.next();
3956 result = 0;
3957 }
3958 else if (c == 'o') {
3959 result = (topdiv->get_page_number() & 1);
3960 tok.next();
3961 }
3962 else if (c == 'e') {
3963 result = !(topdiv->get_page_number() & 1);
3964 tok.next();
3965 }
3966 else if (c == 'd' || c == 'r') {
3967 tok.next();
3968 symbol nm = get_name(1);
3969 if (nm.is_null()) {
3970 skip_alternative();
3971 return 0;
3972 }
3973 result = (c == 'd'
3974 ? request_dictionary.lookup(nm) != 0
3975 : number_reg_dictionary.lookup(nm) != 0);
3976 }
3977 else if (c == 'c') {
3978 tok.next();
3979 tok.skip();
3980 charinfo *ci = tok.get_char(1);
3981 if (ci == 0) {
3982 skip_alternative();
3983 return 0;
3984 }
3985 result = character_exists(ci, curenv);
3986 tok.next();
3987 }
3988 else if (tok.space())
3989 result = 0;
3990 else if (tok.delimiter()) {
3991 token delim = tok;
3992 int delim_level = input_stack::get_level();
3993 environment env1(curenv);
3994 environment env2(curenv);
3995 environment *oldenv = curenv;
3996 curenv = &env1;
3997 for (int i = 0; i < 2; i++) {
3998 for (;;) {
3999 tok.next();
4000 if (tok.newline() || tok.eof()) {
4001 warning(WARN_DELIM, "missing closing delimiter");
4002 tok.next();
4003 curenv = oldenv;
4004 return 0;
4005 }
4006 if (tok == delim
4007 && (compatible_flag || input_stack::get_level() == delim_level))
4008 break;
4009 tok.process();
4010 }
4011 curenv = &env2;
4012 }
4013 node *n1 = env1.extract_output_line();
4014 node *n2 = env2.extract_output_line();
4015 result = same_node_list(n1, n2);
4016 delete_node_list(n1);
4017 delete_node_list(n2);
4018 tok.next();
4019 curenv = oldenv;
4020 }
4021 else {
4022 units n;
4023 if (!get_number(&n, 'u')) {
4024 skip_alternative();
4025 return 0;
4026 }
4027 else
4028 result = n > 0;
4029 }
4030 if (invert)
4031 result = !result;
4032 if (result)
4033 begin_alternative();
4034 else
4035 skip_alternative();
4036 return result;
4037 }
4038
if_else_request()4039 void if_else_request()
4040 {
4041 if_else_stack.push(do_if_request());
4042 }
4043
if_request()4044 void if_request()
4045 {
4046 do_if_request();
4047 }
4048
else_request()4049 void else_request()
4050 {
4051 if (if_else_stack.is_empty()) {
4052 warning(WARN_EL, "unbalanced .el request");
4053 skip_alternative();
4054 }
4055 else {
4056 if (if_else_stack.pop())
4057 skip_alternative();
4058 else
4059 begin_alternative();
4060 }
4061 }
4062
4063 static int while_depth = 0;
4064 static int while_break_flag = 0;
4065
while_request()4066 void while_request()
4067 {
4068 macro mac;
4069 int escaped = 0;
4070 int level = 0;
4071 mac.append(new token_node(tok));
4072 for (;;) {
4073 node *n;
4074 int c = input_stack::get(&n);
4075 if (c == EOF)
4076 break;
4077 if (c == 0) {
4078 escaped = 0;
4079 mac.append(n);
4080 }
4081 else if (escaped) {
4082 if (c == '{')
4083 level += 1;
4084 else if (c == '}')
4085 level -= 1;
4086 escaped = 0;
4087 mac.append(c);
4088 }
4089 else {
4090 if (c == ESCAPE_LEFT_BRACE)
4091 level += 1;
4092 else if (c == ESCAPE_RIGHT_BRACE)
4093 level -= 1;
4094 else if (c == escape_char)
4095 escaped = 1;
4096 mac.append(c);
4097 if (c == '\n' && level <= 0)
4098 break;
4099 }
4100 }
4101 if (level != 0)
4102 error("unbalanced \\{ \\}");
4103 else {
4104 while_depth++;
4105 input_stack::add_boundary();
4106 for (;;) {
4107 input_stack::push(new string_iterator(mac, "while loop"));
4108 tok.next();
4109 if (!do_if_request()) {
4110 while (input_stack::get(NULL) != EOF)
4111 ;
4112 break;
4113 }
4114 process_input_stack();
4115 if (while_break_flag) {
4116 while_break_flag = 0;
4117 break;
4118 }
4119 }
4120 input_stack::remove_boundary();
4121 while_depth--;
4122 }
4123 tok.next();
4124 }
4125
while_break_request()4126 void while_break_request()
4127 {
4128 if (!while_depth) {
4129 error("no while loop");
4130 skip_line();
4131 }
4132 else {
4133 while_break_flag = 1;
4134 while (input_stack::get(NULL) != EOF)
4135 ;
4136 tok.next();
4137 }
4138 }
4139
while_continue_request()4140 void while_continue_request()
4141 {
4142 if (!while_depth) {
4143 error("no while loop");
4144 skip_line();
4145 }
4146 else {
4147 while (input_stack::get(NULL) != EOF)
4148 ;
4149 tok.next();
4150 }
4151 }
4152
4153 // .so
4154
source()4155 void source()
4156 {
4157 symbol nm = get_long_name(1);
4158 if (nm.is_null())
4159 skip_line();
4160 else {
4161 while (!tok.newline() && !tok.eof())
4162 tok.next();
4163 errno = 0;
4164 FILE *fp = fopen(nm.contents(), "r");
4165 if (fp)
4166 input_stack::push(new file_iterator(fp, nm.contents()));
4167 else
4168 error("can't open `%1': %2", nm.contents(), strerror(errno));
4169 tok.next();
4170 }
4171 }
4172
asciify(int c)4173 const char *asciify(int c)
4174 {
4175 static char buf[3];
4176 buf[0] = escape_char == '\0' ? '\\' : escape_char;
4177 buf[1] = buf[2] = '\0';
4178 switch (c) {
4179 case ESCAPE_QUESTION:
4180 buf[1] = '?';
4181 break;
4182 case ESCAPE_AMPERSAND:
4183 buf[1] = '&';
4184 break;
4185 case ESCAPE_UNDERSCORE:
4186 buf[1] = '_';
4187 break;
4188 case ESCAPE_BAR:
4189 buf[1] = '|';
4190 break;
4191 case ESCAPE_CIRCUMFLEX:
4192 buf[1] = '^';
4193 break;
4194 case ESCAPE_LEFT_BRACE:
4195 buf[1] = '{';
4196 break;
4197 case ESCAPE_RIGHT_BRACE:
4198 buf[1] = '}';
4199 break;
4200 case ESCAPE_LEFT_QUOTE:
4201 buf[1] = '`';
4202 break;
4203 case ESCAPE_RIGHT_QUOTE:
4204 buf[1] = '\'';
4205 break;
4206 case ESCAPE_HYPHEN:
4207 buf[1] = '-';
4208 break;
4209 case ESCAPE_BANG:
4210 buf[1] = '!';
4211 break;
4212 case ESCAPE_c:
4213 buf[1] = 'c';
4214 break;
4215 case ESCAPE_e:
4216 buf[1] = 'e';
4217 break;
4218 case ESCAPE_E:
4219 buf[1] = 'E';
4220 break;
4221 case ESCAPE_PERCENT:
4222 buf[1] = '%';
4223 break;
4224 case ESCAPE_SPACE:
4225 buf[1] = ' ';
4226 break;
4227 default:
4228 if (illegal_input_char(c))
4229 buf[0] = '\0';
4230 else
4231 buf[0] = c;
4232 break;
4233 }
4234 return buf;
4235 }
4236
4237
input_char_description(int c)4238 const char *input_char_description(int c)
4239 {
4240 switch (c) {
4241 case '\n':
4242 return "a newline character";
4243 case '\b':
4244 return "a backspace character";
4245 case '\001':
4246 return "a leader character";
4247 case '\t':
4248 return "a tab character ";
4249 case ' ':
4250 return "a space character";
4251 case '\0':
4252 return "a node";
4253 }
4254 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
4255 if (illegal_input_char(c)) {
4256 const char *s = asciify(c);
4257 if (*s) {
4258 buf[0] = '`';
4259 strcpy(buf + 1, s);
4260 strcat(buf, "'");
4261 return buf;
4262 }
4263 sprintf(buf, "magic character code %d", c);
4264 return buf;
4265 }
4266 if (csprint(c)) {
4267 buf[0] = '`';
4268 buf[1] = c;
4269 buf[2] = '\'';
4270 return buf;
4271 }
4272 sprintf(buf, "character code %d", c);
4273 return buf;
4274 }
4275
4276 // .tm
4277
terminal()4278 void terminal()
4279 {
4280 if (!tok.newline() && !tok.eof()) {
4281 int c;
4282 while ((c = get_copy(NULL)) == ' ' || c == '\t')
4283 ;
4284 for (; c != '\n' && c != EOF; c = get_copy(NULL))
4285 fputs(asciify(c), stderr);
4286 }
4287 fputc('\n', stderr);
4288 fflush(stderr);
4289 tok.next();
4290 }
4291
4292 dictionary stream_dictionary(20);
4293
do_open(int append)4294 void do_open(int append)
4295 {
4296 symbol stream = get_name(1);
4297 if (!stream.is_null()) {
4298 symbol filename = get_long_name(1);
4299 if (!filename.is_null()) {
4300 errno = 0;
4301 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
4302 if (!fp) {
4303 error("can't open `%1' for %2: %3",
4304 filename.contents(),
4305 append ? "appending" : "writing",
4306 strerror(errno));
4307 fp = (FILE *)stream_dictionary.remove(stream);
4308 }
4309 else
4310 fp = (FILE *)stream_dictionary.lookup(stream, fp);
4311 if (fp)
4312 fclose(fp);
4313 }
4314 }
4315 skip_line();
4316 }
4317
open_request()4318 void open_request()
4319 {
4320 do_open(0);
4321 }
4322
opena_request()4323 void opena_request()
4324 {
4325 do_open(1);
4326 }
4327
close_request()4328 void close_request()
4329 {
4330 symbol stream = get_name(1);
4331 if (!stream.is_null()) {
4332 FILE *fp = (FILE *)stream_dictionary.remove(stream);
4333 if (!fp)
4334 error("no stream named `%1'", stream.contents());
4335 else
4336 fclose(fp);
4337 }
4338 skip_line();
4339 }
4340
write_request()4341 void write_request()
4342 {
4343 symbol stream = get_name(1);
4344 if (stream.is_null()) {
4345 skip_line();
4346 return;
4347 }
4348 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
4349 if (!fp) {
4350 error("no stream named `%1'", stream.contents());
4351 skip_line();
4352 return;
4353 }
4354 int c;
4355 while ((c = get_copy(NULL)) == ' ')
4356 ;
4357 if (c == '"')
4358 c = get_copy(NULL);
4359 for (; c != '\n' && c != EOF; c = get_copy(NULL))
4360 fputs(asciify(c), fp);
4361 fputc('\n', fp);
4362 fflush(fp);
4363 tok.next();
4364 }
4365
init_charset_table()4366 static void init_charset_table()
4367 {
4368 char buf[16];
4369 strcpy(buf, "char");
4370 for (int i = 0; i < 256; i++) {
4371 strcpy(buf + 4, itoa(i));
4372 charset_table[i] = get_charinfo(symbol(buf));
4373 charset_table[i]->set_ascii_code(i);
4374 if (csalpha(i))
4375 charset_table[i]->set_hyphenation_code(cmlower(i));
4376 }
4377 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
4378 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
4379 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
4380 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
4381 charset_table['"']->set_flags(charinfo::TRANSPARENT);
4382 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
4383 charset_table[')']->set_flags(charinfo::TRANSPARENT);
4384 charset_table[']']->set_flags(charinfo::TRANSPARENT);
4385 charset_table['*']->set_flags(charinfo::TRANSPARENT);
4386 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
4387 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
4388 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
4389 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4390 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4391 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4392 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
4393 page_character = charset_table['%'];
4394 }
4395
4396 static
do_translate(int translate_transparent)4397 void do_translate(int translate_transparent)
4398 {
4399 tok.skip();
4400 while (!tok.newline() && !tok.eof()) {
4401 if (tok.space()) {
4402 // This is a really bizarre troff feature.
4403 tok.next();
4404 translate_space_to_dummy = tok.dummy();
4405 if (tok.newline() || tok.eof())
4406 break;
4407 tok.next();
4408 continue;
4409 }
4410 charinfo *ci1 = tok.get_char(1);
4411 if (ci1 == 0)
4412 break;
4413 tok.next();
4414 if (tok.newline() || tok.eof()) {
4415 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
4416 translate_transparent);
4417 break;
4418 }
4419 if (tok.space())
4420 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
4421 translate_transparent);
4422 else if (tok.dummy())
4423 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
4424 translate_transparent);
4425 else if (tok.hyphen_indicator())
4426 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
4427 translate_transparent);
4428 else {
4429 charinfo *ci2 = tok.get_char(1);
4430 if (ci2 == 0)
4431 break;
4432 if (ci1 == ci2)
4433 ci1->set_translation(0, translate_transparent);
4434 else
4435 ci1->set_translation(ci2, translate_transparent);
4436 }
4437 tok.next();
4438 }
4439 skip_line();
4440 }
4441
translate()4442 void translate()
4443 {
4444 do_translate(1);
4445 }
4446
translate_no_transparent()4447 void translate_no_transparent()
4448 {
4449 do_translate(0);
4450 }
4451
char_flags()4452 void char_flags()
4453 {
4454 int flags;
4455 if (get_integer(&flags))
4456 while (has_arg()) {
4457 charinfo *ci = tok.get_char(1);
4458 if (ci) {
4459 charinfo *tem = ci->get_translation();
4460 if (tem)
4461 ci = tem;
4462 ci->set_flags(flags);
4463 }
4464 tok.next();
4465 }
4466 skip_line();
4467 }
4468
hyphenation_code()4469 void hyphenation_code()
4470 {
4471 tok.skip();
4472 while (!tok.newline() && !tok.eof()) {
4473 charinfo *ci = tok.get_char(1);
4474 if (ci == 0)
4475 break;
4476 tok.next();
4477 tok.skip();
4478 unsigned char c = tok.ch();
4479 if (c == 0) {
4480 error("hyphenation code must be ordinary character");
4481 break;
4482 }
4483 if (csdigit(c)) {
4484 error("hyphenation code cannot be digit");
4485 break;
4486 }
4487 ci->set_hyphenation_code(c);
4488 tok.next();
4489 }
4490 skip_line();
4491 }
4492
get_char(int required)4493 charinfo *token::get_char(int required)
4494 {
4495 if (type == TOKEN_CHAR)
4496 return charset_table[c];
4497 if (type == TOKEN_SPECIAL)
4498 return get_charinfo(nm);
4499 if (type == TOKEN_NUMBERED_CHAR)
4500 return get_charinfo_by_number(val);
4501 if (type == TOKEN_ESCAPE) {
4502 if (escape_char != 0)
4503 return charset_table[escape_char];
4504 else {
4505 error("`\\e' used while no current escape character");
4506 return 0;
4507 }
4508 }
4509 if (required) {
4510 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
4511 warning(WARN_MISSING, "missing normal or special character");
4512 else
4513 error("normal or special character expected (got %1)", description());
4514 }
4515 return 0;
4516 }
4517
get_optional_char()4518 charinfo *get_optional_char()
4519 {
4520 while (tok.space())
4521 tok.next();
4522 charinfo *ci = tok.get_char();
4523 if (!ci) {
4524 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
4525 error("normal or special character expected (got %1): "
4526 "treated as missing",
4527 tok.description());
4528 }
4529 else
4530 tok.next();
4531 return ci;
4532 }
4533
add_to_node_list(node ** pp)4534 int token::add_to_node_list(node **pp)
4535 {
4536 hunits w;
4537 node *n = 0;
4538 switch (type) {
4539 case TOKEN_CHAR:
4540 *pp = (*pp)->add_char(charset_table[c], curenv, &w);
4541 break;
4542 case TOKEN_DUMMY:
4543 n = new dummy_node;
4544 break;
4545 case TOKEN_ESCAPE:
4546 if (escape_char != 0)
4547 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w);
4548 break;
4549 case TOKEN_HYPHEN_INDICATOR:
4550 *pp = (*pp)->add_discretionary_hyphen();
4551 break;
4552 case TOKEN_ITALIC_CORRECTION:
4553 *pp = (*pp)->add_italic_correction(&w);
4554 break;
4555 case TOKEN_LEFT_BRACE:
4556 break;
4557 case TOKEN_MARK_INPUT:
4558 set_number_reg(nm, curenv->get_input_line_position().to_units());
4559 break;
4560 case TOKEN_NODE:
4561 n = nd;
4562 nd = 0;
4563 break;
4564 case TOKEN_NUMBERED_CHAR:
4565 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w);
4566 break;
4567 case TOKEN_RIGHT_BRACE:
4568 break;
4569 case TOKEN_SPACE:
4570 n = new hmotion_node(curenv->get_space_width());
4571 break;
4572 case TOKEN_SPECIAL:
4573 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w);
4574 break;
4575 default:
4576 return 0;
4577 }
4578 if (n) {
4579 n->next = *pp;
4580 *pp = n;
4581 }
4582 return 1;
4583 }
4584
process()4585 void token::process()
4586 {
4587 if (possibly_handle_first_page_transition())
4588 return;
4589 switch (type) {
4590 case TOKEN_BACKSPACE:
4591 curenv->add_node(new hmotion_node(-curenv->get_space_width()));
4592 break;
4593 case TOKEN_CHAR:
4594 curenv->add_char(charset_table[c]);
4595 break;
4596 case TOKEN_DUMMY:
4597 curenv->add_node(new dummy_node);
4598 break;
4599 case TOKEN_EOF:
4600 assert(0);
4601 break;
4602 case TOKEN_EMPTY:
4603 assert(0);
4604 break;
4605 case TOKEN_ESCAPE:
4606 if (escape_char != 0)
4607 curenv->add_char(charset_table[escape_char]);
4608 break;
4609 case TOKEN_BEGIN_TRAP:
4610 case TOKEN_END_TRAP:
4611 case TOKEN_PAGE_EJECTOR:
4612 // these are all handled in process_input_stack()
4613 break;
4614 case TOKEN_HYPHEN_INDICATOR:
4615 curenv->add_hyphen_indicator();
4616 break;
4617 case TOKEN_INTERRUPT:
4618 curenv->interrupt();
4619 break;
4620 case TOKEN_ITALIC_CORRECTION:
4621 curenv->add_italic_correction();
4622 break;
4623 case TOKEN_LEADER:
4624 curenv->handle_tab(1);
4625 break;
4626 case TOKEN_LEFT_BRACE:
4627 break;
4628 case TOKEN_MARK_INPUT:
4629 set_number_reg(nm, curenv->get_input_line_position().to_units());
4630 break;
4631 case TOKEN_NEWLINE:
4632 curenv->newline();
4633 break;
4634 case TOKEN_NODE:
4635 curenv->add_node(nd);
4636 nd = 0;
4637 break;
4638 case TOKEN_NUMBERED_CHAR:
4639 curenv->add_char(get_charinfo_by_number(val));
4640 break;
4641 case TOKEN_REQUEST:
4642 // handled in process_input_stack
4643 break;
4644 case TOKEN_RIGHT_BRACE:
4645 break;
4646 case TOKEN_SPACE:
4647 curenv->space();
4648 break;
4649 case TOKEN_SPECIAL:
4650 curenv->add_char(get_charinfo(nm));
4651 break;
4652 case TOKEN_SPREAD:
4653 curenv->spread();
4654 break;
4655 case TOKEN_TAB:
4656 curenv->handle_tab(0);
4657 break;
4658 case TOKEN_TRANSPARENT:
4659 break;
4660 default:
4661 assert(0);
4662 }
4663 }
4664
4665 class nargs_reg : public reg {
4666 public:
4667 const char *get_string();
4668 };
4669
get_string()4670 const char *nargs_reg::get_string()
4671 {
4672 return itoa(input_stack::nargs());
4673 }
4674
4675 class lineno_reg : public reg {
4676 public:
4677 const char *get_string();
4678 };
4679
get_string()4680 const char *lineno_reg::get_string()
4681 {
4682 int line;
4683 const char *file;
4684 if (!input_stack::get_location(0, &file, &line))
4685 line = 0;
4686 return itoa(line);
4687 }
4688
4689
4690 class writable_lineno_reg : public general_reg {
4691 public:
4692 writable_lineno_reg();
4693 void set_value(units);
4694 int get_value(units *);
4695 };
4696
writable_lineno_reg()4697 writable_lineno_reg::writable_lineno_reg()
4698 {
4699 }
4700
get_value(units * res)4701 int writable_lineno_reg::get_value(units *res)
4702 {
4703 int line;
4704 const char *file;
4705 if (!input_stack::get_location(0, &file, &line))
4706 return 0;
4707 *res = line;
4708 return 1;
4709 }
4710
set_value(units n)4711 void writable_lineno_reg::set_value(units n)
4712 {
4713 input_stack::set_location(0, n);
4714 }
4715
4716 class filename_reg : public reg {
4717 public:
4718 const char *get_string();
4719 };
4720
get_string()4721 const char *filename_reg::get_string()
4722 {
4723 int line;
4724 const char *file;
4725 if (input_stack::get_location(0, &file, &line))
4726 return file;
4727 else
4728 return 0;
4729 }
4730
4731
4732 class constant_reg : public reg {
4733 const char *s;
4734 public:
4735 constant_reg(const char *);
4736 const char *get_string();
4737 };
4738
constant_reg(const char * p)4739 constant_reg::constant_reg(const char *p) : s(p)
4740 {
4741 }
4742
get_string()4743 const char *constant_reg::get_string()
4744 {
4745 return s;
4746 }
4747
constant_int_reg(int * q)4748 constant_int_reg::constant_int_reg(int *q) : p(q)
4749 {
4750 }
4751
get_string()4752 const char *constant_int_reg::get_string()
4753 {
4754 return itoa(*p);
4755 }
4756
abort_request()4757 void abort_request()
4758 {
4759 int c;
4760 while ((c = get_copy(0)) == ' ')
4761 ;
4762 if (c == EOF || c == '\n')
4763 fputs("User Abort.", stderr);
4764 else {
4765 for (; c != '\n' && c != EOF; c = get_copy(NULL))
4766 fputs(asciify(c), stderr);
4767 }
4768 fputc('\n', stderr);
4769 cleanup_and_exit(1);
4770 }
4771
read_string()4772 char *read_string()
4773 {
4774 int len = 256;
4775 char *s = new char[len];
4776 int c;
4777 while ((c = get_copy(0)) == ' ')
4778 ;
4779 int i = 0;
4780 while (c != '\n' && c != EOF) {
4781 if (!illegal_input_char(c)) {
4782 if (i + 2 > len) {
4783 char *tem = s;
4784 s = new char[len*2];
4785 memcpy(s, tem, len);
4786 len *= 2;
4787 a_delete tem;
4788 }
4789 s[i++] = c;
4790 }
4791 c = get_copy(0);
4792 }
4793 s[i] = '\0';
4794 tok.next();
4795 if (i == 0) {
4796 a_delete s;
4797 return 0;
4798 }
4799 return s;
4800 }
4801
pipe_output()4802 void pipe_output()
4803 {
4804 if (the_output) {
4805 error("can't pipe: output already started");
4806 skip_line();
4807 }
4808 else {
4809 if ((pipe_command = read_string()) == 0)
4810 error("can't pipe to empty command");
4811 }
4812 }
4813
4814 static int system_status;
4815
system_request()4816 void system_request()
4817 {
4818 char *command = read_string();
4819 if (!command)
4820 error("empty command");
4821 else {
4822 system_status = system(command);
4823 a_delete command;
4824 }
4825 }
4826
copy_file()4827 void copy_file()
4828 {
4829 if (curdiv == topdiv && topdiv->before_first_page) {
4830 handle_initial_request(COPY_FILE_REQUEST);
4831 return;
4832 }
4833 symbol filename = get_long_name(1);
4834 while (!tok.newline() && !tok.eof())
4835 tok.next();
4836 if (break_flag)
4837 curenv->do_break();
4838 if (!filename.is_null())
4839 curdiv->copy_file(filename.contents());
4840 tok.next();
4841 }
4842
4843 #ifdef COLUMN
4844
vjustify()4845 void vjustify()
4846 {
4847 if (curdiv == topdiv && topdiv->before_first_page) {
4848 handle_initial_request(VJUSTIFY_REQUEST);
4849 return;
4850 }
4851 symbol type = get_long_name(1);
4852 if (!type.is_null())
4853 curdiv->vjustify(type);
4854 skip_line();
4855 }
4856
4857 #endif /* COLUMN */
4858
transparent_file()4859 void transparent_file()
4860 {
4861 if (curdiv == topdiv && topdiv->before_first_page) {
4862 handle_initial_request(TRANSPARENT_FILE_REQUEST);
4863 return;
4864 }
4865 symbol filename = get_long_name(1);
4866 while (!tok.newline() && !tok.eof())
4867 tok.next();
4868 if (break_flag)
4869 curenv->do_break();
4870 if (!filename.is_null()) {
4871 errno = 0;
4872 FILE *fp = fopen(filename.contents(), "r");
4873 if (!fp)
4874 error("can't open `%1': %2", filename.contents(), strerror(errno));
4875 else {
4876 int bol = 1;
4877 for (;;) {
4878 int c = getc(fp);
4879 if (c == EOF)
4880 break;
4881 if (illegal_input_char(c))
4882 warning(WARN_INPUT, "illegal input character code %1", int(c));
4883 else {
4884 curdiv->transparent_output(c);
4885 bol = c == '\n';
4886 }
4887 }
4888 if (!bol)
4889 curdiv->transparent_output('\n');
4890 fclose(fp);
4891 }
4892 }
4893 tok.next();
4894 }
4895
4896 class page_range {
4897 int first;
4898 int last;
4899 public:
4900 page_range *next;
4901 page_range(int, int, page_range *);
4902 int contains(int n);
4903 };
4904
page_range(int i,int j,page_range * p)4905 page_range::page_range(int i, int j, page_range *p)
4906 : first(i), last(j), next(p)
4907 {
4908 }
4909
contains(int n)4910 int page_range::contains(int n)
4911 {
4912 return n >= first && (last <= 0 || n <= last);
4913 }
4914
4915 page_range *output_page_list = 0;
4916
in_output_page_list(int n)4917 int in_output_page_list(int n)
4918 {
4919 if (!output_page_list)
4920 return 1;
4921 for (page_range *p = output_page_list; p; p = p->next)
4922 if (p->contains(n))
4923 return 1;
4924 return 0;
4925 }
4926
parse_output_page_list(char * p)4927 static void parse_output_page_list(char *p)
4928 {
4929 for (;;) {
4930 int i;
4931 if (*p == '-')
4932 i = 1;
4933 else if (csdigit(*p)) {
4934 i = 0;
4935 do
4936 i = i*10 + *p++ - '0';
4937 while (csdigit(*p));
4938 }
4939 else
4940 break;
4941 int j;
4942 if (*p == '-') {
4943 p++;
4944 j = 0;
4945 if (csdigit(*p)) {
4946 do
4947 j = j*10 + *p++ - '0';
4948 while (csdigit(*p));
4949 }
4950 }
4951 else
4952 j = i;
4953 output_page_list = new page_range(i, j, output_page_list);
4954 if (*p != ',')
4955 break;
4956 ++p;
4957 }
4958 if (*p != '\0') {
4959 error("bad output page list");
4960 output_page_list = 0;
4961 }
4962 }
4963
open_mac_file(const char * mac,char ** path)4964 static FILE *open_mac_file(const char *mac, char **path)
4965 {
4966 char *s = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
4967 strcpy(s, MACRO_PREFIX);
4968 strcat(s, mac);
4969 FILE *fp = macro_path.open_file(s, path);
4970 a_delete s;
4971 return fp;
4972 }
4973
process_macro_file(const char * mac)4974 static void process_macro_file(const char *mac)
4975 {
4976 char *path;
4977 FILE *fp = open_mac_file(mac, &path);
4978 if (!fp)
4979 fatal("can't find macro file %1", mac);
4980 const char *s = symbol(path).contents();
4981 a_delete path;
4982 input_stack::push(new file_iterator(fp, s));
4983 tok.next();
4984 process_input_stack();
4985 }
4986
process_startup_file()4987 static void process_startup_file()
4988 {
4989 char *path;
4990 FILE *fp = macro_path.open_file(STARTUP_FILE, &path);
4991 if (fp) {
4992 input_stack::push(new file_iterator(fp, symbol(path).contents()));
4993 a_delete path;
4994 tok.next();
4995 process_input_stack();
4996 }
4997 }
4998
macro_source()4999 void macro_source()
5000 {
5001 symbol nm = get_long_name(1);
5002 if (nm.is_null())
5003 skip_line();
5004 else {
5005 while (!tok.newline() && !tok.eof())
5006 tok.next();
5007 char *path;
5008 FILE *fp = macro_path.open_file(nm.contents(), &path);
5009 if (fp) {
5010 input_stack::push(new file_iterator(fp, symbol(path).contents()));
5011 a_delete path;
5012 }
5013 else
5014 error("can't find macro file `%1'", nm.contents());
5015 tok.next();
5016 }
5017 }
5018
process_input_file(const char * name)5019 static void process_input_file(const char *name)
5020 {
5021 FILE *fp;
5022 if (strcmp(name, "-") == 0) {
5023 clearerr(stdin);
5024 fp = stdin;
5025 }
5026 else {
5027 errno = 0;
5028 fp = fopen(name, "r");
5029 if (!fp)
5030 fatal("can't open `%1': %2", name, strerror(errno));
5031 }
5032 input_stack::push(new file_iterator(fp, name));
5033 tok.next();
5034 process_input_stack();
5035 }
5036
5037 // make sure the_input is empty before calling this
5038
evaluate_expression(const char * expr,units * res)5039 static int evaluate_expression(const char *expr, units *res)
5040 {
5041 input_stack::push(make_temp_iterator(expr));
5042 tok.next();
5043 int success = get_number(res, 'u');
5044 while (input_stack::get(NULL) != EOF)
5045 ;
5046 return success;
5047 }
5048
do_register_assignment(const char * s)5049 static void do_register_assignment(const char *s)
5050 {
5051 const char *p = strchr(s, '=');
5052 if (!p) {
5053 char buf[2];
5054 buf[0] = s[0];
5055 buf[1] = 0;
5056 units n;
5057 if (evaluate_expression(s + 1, &n))
5058 set_number_reg(buf, n);
5059 }
5060 else {
5061 char *buf = new char[p - s + 1];
5062 memcpy(buf, s, p - s);
5063 buf[p - s] = 0;
5064 units n;
5065 if (evaluate_expression(p + 1, &n))
5066 set_number_reg(buf, n);
5067 a_delete buf;
5068 }
5069 }
5070
set_string(const char * name,const char * value)5071 static void set_string(const char *name, const char *value)
5072 {
5073 macro *m = new macro;
5074 for (const char *p = value; *p; p++)
5075 if (!illegal_input_char((unsigned char)*p))
5076 m->append(*p);
5077 request_dictionary.define(name, m);
5078 }
5079
5080
do_string_assignment(const char * s)5081 static void do_string_assignment(const char *s)
5082 {
5083 const char *p = strchr(s, '=');
5084 if (!p) {
5085 char buf[2];
5086 buf[0] = s[0];
5087 buf[1] = 0;
5088 set_string(buf, s + 1);
5089 }
5090 else {
5091 char *buf = new char[p - s + 1];
5092 memcpy(buf, s, p - s);
5093 buf[p - s] = 0;
5094 set_string(buf, p + 1);
5095 a_delete buf;
5096 }
5097 }
5098
5099 struct string_list {
5100 const char *s;
5101 string_list *next;
string_liststring_list5102 string_list(const char *ss) : s(ss), next(0) {}
5103 };
5104
add_string(const char * s,string_list ** p)5105 static void add_string(const char *s, string_list **p)
5106 {
5107 while (*p)
5108 p = &((*p)->next);
5109 *p = new string_list(s);
5110 }
5111
usage(const char * prog)5112 void usage(const char *prog)
5113 {
5114 errprint(
5115 "usage: %1 -abivzCER -wname -Wname -dcstring -mname -nN -olist -rcN\n"
5116 " -Tname -Fdir -Mdir [ files ]\n",
5117 prog);
5118 exit(USAGE_EXIT_CODE);
5119 }
5120
main(int argc,char ** argv)5121 int main(int argc, char **argv)
5122 {
5123 program_name = argv[0];
5124 static char stderr_buf[BUFSIZ];
5125 setbuf(stderr, stderr_buf);
5126 int c;
5127 string_list *macros = 0;
5128 string_list *register_assignments = 0;
5129 string_list *string_assignments = 0;
5130 int iflag = 0;
5131 int tflag = 0;
5132 int fflag = 0;
5133 int nflag = 0;
5134 int no_rc = 0; // don't process troffrc
5135 int next_page_number;
5136 opterr = 0;
5137 hresolution = vresolution = 1;
5138 while ((c = getopt(argc, argv, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:R"))
5139 != EOF)
5140 switch(c) {
5141 case 'v':
5142 {
5143 extern const char *version_string;
5144 fprintf(stderr, "GNU troff version %s\n", version_string);
5145 fflush(stderr);
5146 break;
5147 }
5148 case 'T':
5149 device = optarg;
5150 tflag = 1;
5151 break;
5152 case 'C':
5153 compatible_flag = 1;
5154 break;
5155 case 'M':
5156 macro_path.command_line_dir(optarg);
5157 break;
5158 case 'F':
5159 font::command_line_font_dir(optarg);
5160 break;
5161 case 'm':
5162 add_string(optarg, ¯os);
5163 break;
5164 case 'E':
5165 inhibit_errors = 1;
5166 break;
5167 case 'R':
5168 no_rc = 1;
5169 break;
5170 case 'w':
5171 enable_warning(optarg);
5172 break;
5173 case 'W':
5174 disable_warning(optarg);
5175 break;
5176 case 'i':
5177 iflag = 1;
5178 break;
5179 case 'b':
5180 backtrace_flag = 1;
5181 break;
5182 case 'a':
5183 ascii_output_flag = 1;
5184 break;
5185 case 'z':
5186 suppress_output_flag = 1;
5187 break;
5188 case 'n':
5189 if (sscanf(optarg, "%d", &next_page_number) == 1)
5190 nflag++;
5191 else
5192 error("bad page number");
5193 break;
5194 case 'o':
5195 parse_output_page_list(optarg);
5196 break;
5197 case 'd':
5198 if (*optarg == '\0')
5199 error("`-d' requires non-empty argument");
5200 else
5201 add_string(optarg, &string_assignments);
5202 break;
5203 case 'r':
5204 if (*optarg == '\0')
5205 error("`-r' requires non-empty argument");
5206 else
5207 add_string(optarg, ®ister_assignments);
5208 break;
5209 case 'f':
5210 default_family = symbol(optarg);
5211 fflag = 1;
5212 break;
5213 case 'q':
5214 case 's':
5215 case 't':
5216 // silently ignore these
5217 break;
5218 case '?':
5219 usage(argv[0]);
5220 default:
5221 assert(0);
5222 }
5223 set_string(".T", device);
5224 init_charset_table();
5225 if (!font::load_desc())
5226 fatal("sorry, I can't continue");
5227 units_per_inch = font::res;
5228 hresolution = font::hor;
5229 vresolution = font::vert;
5230 sizescale = font::sizescale;
5231 tcommand_flag = font::tcommand;
5232 if (!fflag && font::family != 0 && *font::family != '\0')
5233 default_family = symbol(font::family);
5234 font_size::init_size_table(font::sizes);
5235 int i;
5236 int j = 1;
5237 if (font::style_table) {
5238 for (i = 0; font::style_table[i]; i++)
5239 mount_style(j++, symbol(font::style_table[i]));
5240 }
5241 for (i = 0; font::font_name_table[i]; i++, j++)
5242 // In the DESC file a font name of 0 (zero) means leave this
5243 // position empty.
5244 if (strcmp(font::font_name_table[i], "0") != 0)
5245 mount_font(j, symbol(font::font_name_table[i]));
5246 curdiv = topdiv = new top_level_diversion;
5247 if (nflag)
5248 topdiv->set_next_page_number(next_page_number);
5249 init_input_requests();
5250 init_env_requests();
5251 init_div_requests();
5252 #ifdef COLUMN
5253 init_column_requests();
5254 #endif /* COLUMN */
5255 init_node_requests();
5256 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
5257 init_registers();
5258 init_reg_requests();
5259 init_hyphen_requests();
5260 init_environments();
5261 while (string_assignments) {
5262 do_string_assignment(string_assignments->s);
5263 string_list *tem = string_assignments;
5264 string_assignments = string_assignments->next;
5265 delete tem;
5266 }
5267 while (register_assignments) {
5268 do_register_assignment(register_assignments->s);
5269 string_list *tem = register_assignments;
5270 register_assignments = register_assignments->next;
5271 delete tem;
5272 }
5273 if (!no_rc)
5274 process_startup_file();
5275 while (macros) {
5276 process_macro_file(macros->s);
5277 string_list *tem = macros;
5278 macros = macros->next;
5279 delete tem;
5280 }
5281 for (i = optind; i < argc; i++)
5282 process_input_file(argv[i]);
5283 if (optind >= argc || iflag)
5284 process_input_file("-");
5285 exit_troff();
5286 }
5287
warn_request()5288 void warn_request()
5289 {
5290 int n;
5291 if (has_arg() && get_integer(&n)) {
5292 if (n & ~WARN_TOTAL) {
5293 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
5294 n &= WARN_TOTAL;
5295 }
5296 warning_mask = n;
5297 }
5298 else
5299 warning_mask = WARN_TOTAL;
5300 skip_line();
5301 }
5302
init_registers()5303 static void init_registers()
5304 {
5305 #ifdef LONG_FOR_TIME_T
5306 long
5307 #else /* not LONG_FOR_TIME_T */
5308 time_t
5309 #endif /* not LONG_FOR_TIME_T */
5310 t = time(0);
5311 // Use struct here to work around misfeature in old versions of g++.
5312 struct tm *tt = localtime(&t);
5313 set_number_reg("dw", int(tt->tm_wday + 1));
5314 set_number_reg("dy", int(tt->tm_mday));
5315 set_number_reg("mo", int(tt->tm_mon + 1));
5316 set_number_reg("yr", int(tt->tm_year));
5317 set_number_reg("$$", getpid());
5318 number_reg_dictionary.define(".A",
5319 new constant_reg(ascii_output_flag
5320 ? "1"
5321 : "0"));
5322 }
5323
init_input_requests()5324 void init_input_requests()
5325 {
5326 init_request("ds", define_string);
5327 init_request("as", append_string);
5328 init_request("de", define_macro);
5329 init_request("am", append_macro);
5330 init_request("ig", ignore);
5331 init_request("rm", remove_macro);
5332 init_request("rn", rename_macro);
5333 init_request("if", if_request);
5334 init_request("ie", if_else_request);
5335 init_request("el", else_request);
5336 init_request("so", source);
5337 init_request("nx", next_file);
5338 init_request("pm", print_macros);
5339 init_request("eo", escape_off);
5340 init_request("ec", set_escape_char);
5341 init_request("pc", set_page_character);
5342 init_request("tm", terminal);
5343 init_request("ex", exit_request);
5344 init_request("em", end_macro);
5345 init_request("tr", translate);
5346 init_request("trnt", translate_no_transparent);
5347 init_request("ab", abort_request);
5348 init_request("pi", pipe_output);
5349 init_request("cf", copy_file);
5350 init_request("sy", system_request);
5351 init_request("lf", line_file);
5352 init_request("cflags", char_flags);
5353 init_request("shift", shift);
5354 init_request("rd", read_request);
5355 init_request("cp", compatible);
5356 init_request("char", define_character);
5357 init_request("rchar", remove_character);
5358 init_request("hcode", hyphenation_code);
5359 init_request("while", while_request);
5360 init_request("break", while_break_request);
5361 init_request("continue", while_continue_request);
5362 init_request("als", alias_macro);
5363 init_request("backtrace", backtrace_request);
5364 init_request("chop", chop_macro);
5365 init_request("asciify", asciify_macro);
5366 init_request("warn", warn_request);
5367 init_request("open", open_request);
5368 init_request("opena", opena_request);
5369 init_request("close", close_request);
5370 init_request("write", write_request);
5371 init_request("trf", transparent_file);
5372 #ifdef WIDOW_CONTROL
5373 init_request("fpl", flush_pending_lines);
5374 #endif /* WIDOW_CONTROL */
5375 init_request("nroff", nroff_request);
5376 init_request("troff", troff_request);
5377 #ifdef COLUMN
5378 init_request("vj", vjustify);
5379 #endif /* COLUMN */
5380 init_request("mso", macro_source);
5381 init_request("do", do_request);
5382 number_reg_dictionary.define("systat", new variable_reg(&system_status));
5383 number_reg_dictionary.define("slimit",
5384 new variable_reg(&input_stack::limit));
5385 number_reg_dictionary.define(".$", new nargs_reg);
5386 number_reg_dictionary.define(".c", new lineno_reg);
5387 number_reg_dictionary.define("c.", new writable_lineno_reg);
5388 number_reg_dictionary.define(".F", new filename_reg);
5389 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
5390 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
5391 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
5392 number_reg_dictionary.define(".R", new constant_reg("10000"));
5393 extern const char *major_version;
5394 number_reg_dictionary.define(".x", new constant_reg(major_version));
5395 extern const char *minor_version;
5396 number_reg_dictionary.define(".y", new constant_reg(minor_version));
5397 number_reg_dictionary.define(".g", new constant_reg("1"));
5398 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
5399 }
5400
5401 object_dictionary request_dictionary(501);
5402
init_request(const char * s,REQUEST_FUNCP f)5403 void init_request(const char *s, REQUEST_FUNCP f)
5404 {
5405 request_dictionary.define(s, new request(f));
5406 }
5407
lookup_request(symbol nm)5408 static request_or_macro *lookup_request(symbol nm)
5409 {
5410 assert(!nm.is_null());
5411 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
5412 if (p == 0) {
5413 warning(WARN_MAC, "`%1' not defined", nm.contents());
5414 p = new macro;
5415 request_dictionary.define(nm, p);
5416 }
5417 return p;
5418 }
5419
5420
charinfo_to_node_list(charinfo * ci,const environment * envp)5421 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
5422 {
5423 // Don't interpret character definitions in compatible mode.
5424 int old_compatible_flag = compatible_flag;
5425 compatible_flag = 0;
5426 int old_escape_char = escape_char;
5427 escape_char = '\\';
5428 macro *mac = ci->set_macro(0);
5429 assert(mac != 0);
5430 environment *oldenv = curenv;
5431 environment env(envp);
5432 curenv = &env;
5433 curenv->set_composite();
5434 token old_tok = tok;
5435 input_stack::add_boundary();
5436 string_iterator *si = new string_iterator(*mac, "composite character", ci->nm);
5437 input_stack::push(si);
5438 // we don't use process_input_stack, because we don't want to recognise
5439 // requests
5440 for (;;) {
5441 tok.next();
5442 if (tok.eof())
5443 break;
5444 if (tok.newline()) {
5445 error("composite character mustn't contain newline");
5446 while (!tok.eof())
5447 tok.next();
5448 break;
5449 }
5450 else
5451 tok.process();
5452 }
5453 node *n = curenv->extract_output_line();
5454 input_stack::remove_boundary();
5455 ci->set_macro(mac);
5456 tok = old_tok;
5457 curenv = oldenv;
5458 compatible_flag = old_compatible_flag;
5459 escape_char = old_escape_char;
5460 return n;
5461 }
5462
read_draw_node()5463 static node *read_draw_node()
5464 {
5465 token start;
5466 start.next();
5467 if (!start.delimiter(1)){
5468 do {
5469 tok.next();
5470 } while (tok != start && !tok.newline() && !tok.eof());
5471 }
5472 else {
5473 tok.next();
5474 if (tok == start)
5475 error("missing argument");
5476 else {
5477 unsigned char type = tok.ch();
5478 tok.next();
5479 int maxpoints = 10;
5480 hvpair *point = new hvpair[maxpoints];
5481 int npoints = 0;
5482 int no_last_v = 0;
5483 int err = 0;
5484 int i;
5485 for (i = 0; tok != start; i++) {
5486 if (i == maxpoints) {
5487 hvpair *oldpoint = point;
5488 point = new hvpair[maxpoints*2];
5489 for (int j = 0; j < maxpoints; j++)
5490 point[j] = oldpoint[j];
5491 maxpoints *= 2;
5492 a_delete oldpoint;
5493 }
5494 if (!get_hunits(&point[i].h, 'm')) {
5495 err = 1;
5496 break;
5497 }
5498 ++npoints;
5499 tok.skip();
5500 point[i].v = V0;
5501 if (tok == start) {
5502 no_last_v = 1;
5503 break;
5504 }
5505 if (!get_vunits(&point[i].v, 'v')) {
5506 err = 1;
5507 break;
5508 }
5509 tok.skip();
5510 }
5511 while (tok != start && !tok.newline() && !tok.eof())
5512 tok.next();
5513 if (!err) {
5514 switch (type) {
5515 case 'l':
5516 if (npoints != 1 || no_last_v) {
5517 error("two arguments needed for line");
5518 npoints = 1;
5519 }
5520 break;
5521 case 'c':
5522 if (npoints != 1 || !no_last_v) {
5523 error("one argument needed for circle");
5524 npoints = 1;
5525 point[0].v = V0;
5526 }
5527 break;
5528 case 'e':
5529 if (npoints != 1 || no_last_v) {
5530 error("two arguments needed for ellipse");
5531 npoints = 1;
5532 }
5533 break;
5534 case 'a':
5535 if (npoints != 2 || no_last_v) {
5536 error("four arguments needed for arc");
5537 npoints = 2;
5538 }
5539 break;
5540 case '~':
5541 if (no_last_v)
5542 error("even number of arguments needed for spline");
5543 break;
5544 default:
5545 // silently pass it through
5546 break;
5547 }
5548 draw_node *dn = new draw_node(type, point, npoints,
5549 curenv->get_font_size());
5550 a_delete point;
5551 return dn;
5552 }
5553 else {
5554 a_delete point;
5555 }
5556 }
5557 }
5558 return 0;
5559 }
5560
5561 static struct {
5562 const char *name;
5563 int mask;
5564 } warning_table[] = {
5565 "char", WARN_CHAR,
5566 "range", WARN_RANGE,
5567 "break", WARN_BREAK,
5568 "delim", WARN_DELIM,
5569 "el", WARN_EL,
5570 "scale", WARN_SCALE,
5571 "number", WARN_NUMBER,
5572 "syntax", WARN_SYNTAX,
5573 "tab", WARN_TAB,
5574 "right-brace", WARN_RIGHT_BRACE,
5575 "missing", WARN_MISSING,
5576 "input", WARN_INPUT,
5577 "escape", WARN_ESCAPE,
5578 "space", WARN_SPACE,
5579 "font", WARN_FONT,
5580 "di", WARN_DI,
5581 "mac", WARN_MAC,
5582 "reg", WARN_REG,
5583 "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG),
5584 "w", WARN_TOTAL,
5585 "default", DEFAULT_WARNING_MASK,
5586 };
5587
lookup_warning(const char * name)5588 static int lookup_warning(const char *name)
5589 {
5590 for (int i = 0;
5591 i < sizeof(warning_table)/sizeof(warning_table[0]);
5592 i++)
5593 if (strcmp(name, warning_table[i].name) == 0)
5594 return warning_table[i].mask;
5595 return 0;
5596 }
5597
enable_warning(const char * name)5598 static void enable_warning(const char *name)
5599 {
5600 int mask = lookup_warning(name);
5601 if (mask)
5602 warning_mask |= mask;
5603 else
5604 error("unknown warning `%1'", name);
5605 }
5606
disable_warning(const char * name)5607 static void disable_warning(const char *name)
5608 {
5609 int mask = lookup_warning(name);
5610 if (mask)
5611 warning_mask &= ~mask;
5612 else
5613 error("unknown warning `%1'", name);
5614 }
5615
5616 enum error_type { WARNING, ERROR, FATAL };
5617
do_error(error_type type,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5618 static void do_error(error_type type,
5619 const char *format,
5620 const errarg &arg1,
5621 const errarg &arg2,
5622 const errarg &arg3)
5623 {
5624 const char *filename;
5625 int lineno;
5626 if (inhibit_errors && type < FATAL)
5627 return;
5628 if (backtrace_flag)
5629 input_stack::backtrace();
5630 if (!get_file_line(&filename, &lineno))
5631 filename = 0;
5632 if (filename)
5633 errprint("%1:%2: ", filename, lineno);
5634 else if (program_name)
5635 fprintf(stderr, "%s: ", program_name);
5636 switch (type) {
5637 case FATAL:
5638 fputs("fatal error: ", stderr);
5639 break;
5640 case ERROR:
5641 break;
5642 case WARNING:
5643 fputs("warning: ", stderr);
5644 break;
5645 }
5646 errprint(format, arg1, arg2, arg3);
5647 fputc('\n', stderr);
5648 fflush(stderr);
5649 if (type == FATAL)
5650 cleanup_and_exit(1);
5651 }
5652
warning(warning_type t,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5653 int warning(warning_type t,
5654 const char *format,
5655 const errarg &arg1,
5656 const errarg &arg2,
5657 const errarg &arg3)
5658 {
5659 if ((t & warning_mask) != 0) {
5660 do_error(WARNING, format, arg1, arg2, arg3);
5661 return 1;
5662 }
5663 else
5664 return 0;
5665 }
5666
error(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5667 void error(const char *format,
5668 const errarg &arg1,
5669 const errarg &arg2,
5670 const errarg &arg3)
5671 {
5672 do_error(ERROR, format, arg1, arg2, arg3);
5673 }
5674
fatal(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5675 void fatal(const char *format,
5676 const errarg &arg1,
5677 const errarg &arg2,
5678 const errarg &arg3)
5679 {
5680 do_error(FATAL, format, arg1, arg2, arg3);
5681 }
5682
fatal_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5683 void fatal_with_file_and_line(const char *filename, int lineno,
5684 const char *format,
5685 const errarg &arg1,
5686 const errarg &arg2,
5687 const errarg &arg3)
5688 {
5689 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
5690 errprint(format, arg1, arg2, arg3);
5691 fputc('\n', stderr);
5692 fflush(stderr);
5693 cleanup_and_exit(1);
5694 }
5695
error_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5696 void error_with_file_and_line(const char *filename, int lineno,
5697 const char *format,
5698 const errarg &arg1,
5699 const errarg &arg2,
5700 const errarg &arg3)
5701 {
5702 fprintf(stderr, "%s:%d: error: ", filename, lineno);
5703 errprint(format, arg1, arg2, arg3);
5704 fputc('\n', stderr);
5705 fflush(stderr);
5706 }
5707
5708 dictionary charinfo_dictionary(501);
5709
get_charinfo(symbol nm)5710 charinfo *get_charinfo(symbol nm)
5711 {
5712 void *p = charinfo_dictionary.lookup(nm);
5713 if (p != 0)
5714 return (charinfo *)p;
5715 charinfo *cp = new charinfo(nm);
5716 (void)charinfo_dictionary.lookup(nm, cp);
5717 return cp;
5718 }
5719
5720 int charinfo::next_index = 0;
5721
charinfo(symbol s)5722 charinfo::charinfo(symbol s)
5723 : nm(s), hyphenation_code(0), translation(0), flags(0), ascii_code(0),
5724 special_translation(TRANSLATE_NONE), mac(0), not_found(0),
5725 transparent_translate(1)
5726 {
5727 index = next_index++;
5728 }
5729
set_hyphenation_code(unsigned char c)5730 void charinfo::set_hyphenation_code(unsigned char c)
5731 {
5732 hyphenation_code = c;
5733 }
5734
set_translation(charinfo * ci,int tt)5735 void charinfo::set_translation(charinfo *ci, int tt)
5736 {
5737 translation = ci;
5738 special_translation = TRANSLATE_NONE;
5739 transparent_translate = tt;
5740 }
5741
set_special_translation(int c,int tt)5742 void charinfo::set_special_translation(int c, int tt)
5743 {
5744 special_translation = c;
5745 translation = 0;
5746 transparent_translate = tt;
5747 }
5748
set_ascii_code(unsigned char c)5749 void charinfo::set_ascii_code(unsigned char c)
5750 {
5751 ascii_code = c;
5752 }
5753
set_macro(macro * m)5754 macro *charinfo::set_macro(macro *m)
5755 {
5756 macro *tem = mac;
5757 mac = m;
5758 return tem;
5759 }
5760
set_number(int n)5761 void charinfo::set_number(int n)
5762 {
5763 number = n;
5764 flags |= NUMBERED;
5765 }
5766
get_number()5767 int charinfo::get_number()
5768 {
5769 assert(flags & NUMBERED);
5770 return number;
5771 }
5772
5773 symbol UNNAMED_SYMBOL("---");
5774
5775 // For numbered characters not between 0 and 255, we make a symbol out
5776 // of the number and store them in this dictionary.
5777
5778 dictionary numbered_charinfo_dictionary(11);
5779
get_charinfo_by_number(int n)5780 charinfo *get_charinfo_by_number(int n)
5781 {
5782 static charinfo *number_table[256];
5783
5784 if (n >= 0 && n < 256) {
5785 charinfo *ci = number_table[n];
5786 if (!ci) {
5787 ci = new charinfo(UNNAMED_SYMBOL);
5788 ci->set_number(n);
5789 number_table[n] = ci;
5790 }
5791 return ci;
5792 }
5793 else {
5794 symbol ns(itoa(n));
5795 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
5796 if (!ci) {
5797 ci = new charinfo(UNNAMED_SYMBOL);
5798 ci->set_number(n);
5799 numbered_charinfo_dictionary.lookup(ns, ci);
5800 }
5801 return ci;
5802 }
5803 }
5804
name_to_index(const char * nm)5805 int font::name_to_index(const char *nm)
5806 {
5807 charinfo *ci;
5808 if (nm[1] == 0)
5809 ci = charset_table[nm[0] & 0xff];
5810 else if (nm[0] == '\\' && nm[2] == 0)
5811 ci = get_charinfo(symbol(nm + 1));
5812 else
5813 ci = get_charinfo(symbol(nm));
5814 if (ci == 0)
5815 return -1;
5816 else
5817 return ci->get_index();
5818 }
5819
number_to_index(int n)5820 int font::number_to_index(int n)
5821 {
5822 return get_charinfo_by_number(n)->get_index();
5823 }
5824