2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include "dictionary.h"
33 #include "stringclass.h"
35 #include "macropath.h"
39 // Needed for getpid() and isatty()
44 #ifdef NEED_DECLARATION_PUTENV
46 int putenv(const char *);
48 #endif /* NEED_DECLARATION_PUTENV */
50 #define MACRO_PREFIX "tmac."
51 #define MACRO_POSTFIX ".tmac"
52 #define INITIAL_STARTUP_FILE "troffrc"
53 #define FINAL_STARTUP_FILE "troffrc-end"
54 #define DEFAULT_INPUT_STACK_LIMIT 1000
56 #ifndef DEFAULT_WARNING_MASK
57 // warnings that are enabled by default
58 #define DEFAULT_WARNING_MASK \
59 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
62 // initial size of buffer for reading names; expanded as necessary
65 extern "C" const char *Version_string
;
68 void init_column_requests();
71 static node
*read_draw_node();
72 void handle_first_page_transition();
73 static void push_token(const token
&);
78 void transparent_file();
79 void process_input_stack();
81 const char *program_name
= 0;
84 int color_flag
= 1; // colors are on by default
85 static int backtrace_flag
= 0;
87 char *pipe_command
= 0;
89 charinfo
*charset_table
[256];
90 unsigned char hpf_code_table
[256];
92 static int warning_mask
= DEFAULT_WARNING_MASK
;
93 static int inhibit_errors
= 0;
94 static int ignoring
= 0;
96 static void enable_warning(const char *);
97 static void disable_warning(const char *);
99 static int escape_char
= '\\';
100 static symbol end_macro_name
;
101 static symbol blank_line_macro_name
;
102 static int compatible_flag
= 0;
103 int ascii_output_flag
= 0;
104 int suppress_output_flag
= 0;
106 int begin_level
= 0; // number of nested .begin requests
108 int have_input
= 0; // whether \f, \H, \R, \s, or \S has
109 // been processed in token::next()
110 int tcommand_flag
= 0;
111 int safer_flag
= 1; // safer by default
113 int have_string_arg
= 0; // whether we have \*[foo bar...]
115 double spread_limit
= -3.0 - 1.0; // negative means deactivated
118 char warn_scaling_indicator
;
120 search_path
*mac_path
= &safer_macro_path
;
122 static int get_copy(node
**, int = 0);
123 static void copy_mode_error(const char *,
124 const errarg
& = empty_errarg
,
125 const errarg
& = empty_errarg
,
126 const errarg
& = empty_errarg
);
128 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
129 static symbol
read_escape_name(read_mode mode
= NO_ARGS
);
130 static symbol
read_long_escape_name(read_mode mode
= NO_ARGS
);
131 static void interpolate_string(symbol
);
132 static void interpolate_string_with_args(symbol
);
133 static void interpolate_macro(symbol
);
134 static void interpolate_number_format(symbol
);
135 static void interpolate_environment_variable(symbol
);
137 static void interpolate_arg(symbol
);
138 static request_or_macro
*lookup_request(symbol
);
139 static int get_delim_number(units
*, int);
140 static int get_delim_number(units
*, int, units
);
141 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
142 static int read_size(int *);
143 static symbol
get_delim_name();
144 static void init_registers();
145 static void trapping_blank_line();
147 struct input_iterator
;
148 input_iterator
*make_temp_iterator(const char *);
149 const char *input_char_description(int);
152 void set_escape_char()
156 error("bad escape character");
160 escape_char
= tok
.ch();
173 static int saved_escape_char
= '\\';
175 void save_escape_char()
177 saved_escape_char
= escape_char
;
181 void restore_escape_char()
183 escape_char
= saved_escape_char
;
187 class input_iterator
{
190 virtual ~input_iterator() {}
192 friend class input_stack
;
194 const unsigned char *ptr
;
195 const unsigned char *eptr
;
196 input_iterator
*next
;
198 virtual int fill(node
**);
200 virtual int has_args() { return 0; }
201 virtual int nargs() { return 0; }
202 virtual input_iterator
*get_arg(int) { return 0; }
203 virtual int get_location(int, const char **, int *) { return 0; }
204 virtual void backtrace() {}
205 virtual int set_location(const char *, int) { return 0; }
206 virtual int next_file(FILE *, const char *) { return 0; }
207 virtual void shift(int) {}
208 virtual int is_boundary() {return 0; }
209 virtual int internal_level() { return 0; }
210 virtual int is_file() { return 0; }
211 virtual int is_macro() { return 0; }
212 virtual void save_compatible_flag(int) {}
213 virtual int get_compatible_flag() { return 0; }
216 input_iterator::input_iterator()
221 int input_iterator::fill(node
**)
226 int input_iterator::peek()
231 inline int input_iterator::get(node
**p
)
233 return ptr
< eptr
? *ptr
++ : fill(p
);
236 class input_boundary
: public input_iterator
{
238 int is_boundary() { return 1; }
241 class input_return_boundary
: public input_iterator
{
243 int is_boundary() { return 2; }
246 class file_iterator
: public input_iterator
{
249 const char *filename
;
253 enum { BUF_SIZE
= 512 };
254 unsigned char buf
[BUF_SIZE
];
257 file_iterator(FILE *, const char *, int = 0);
261 int get_location(int, const char **, int *);
263 int set_location(const char *, int);
264 int next_file(FILE *, const char *);
268 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
269 : fp(f
), lineno(1), filename(fn
), popened(po
),
270 newline_flag(0), seen_escape(0)
272 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
275 the_output
->put_filename(fn
);
279 file_iterator::~file_iterator()
284 void file_iterator::close()
288 #ifndef POPEN_MISSING
291 #endif /* not POPEN_MISSING */
296 int file_iterator::is_file()
301 int file_iterator::next_file(FILE *f
, const char *s
)
315 int file_iterator::fill(node
**)
320 unsigned char *p
= buf
;
322 unsigned char *e
= p
+ BUF_SIZE
;
327 if (invalid_input_char(c
))
328 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
336 seen_escape
= (c
== '\\');
349 int file_iterator::peek()
352 while (invalid_input_char(c
)) {
353 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
361 int file_iterator::get_location(int /*allow_macro*/,
362 const char **filenamep
, int *linenop
)
365 if (filename
!= 0 && strcmp(filename
, "-") == 0)
366 *filenamep
= "<standard input>";
368 *filenamep
= filename
;
372 void file_iterator::backtrace()
374 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
375 popened
? "process" : "file");
378 int file_iterator::set_location(const char *f
, int ln
)
384 the_output
->put_filename(f
);
390 input_iterator nil_iterator
;
394 static int get(node
**);
396 static void push(input_iterator
*);
397 static input_iterator
*get_arg(int);
399 static int get_location(int, const char **, int *);
400 static int set_location(const char *, int);
401 static void backtrace();
402 static void backtrace_all();
403 static void next_file(FILE *, const char *);
404 static void end_file();
405 static void shift(int n
);
406 static void add_boundary();
407 static void add_return_boundary();
408 static int is_return_boundary();
409 static void remove_boundary();
410 static int get_level();
412 static void pop_macro();
413 static void save_compatible_flag(int);
414 static int get_compatible_flag();
418 static input_iterator
*top
;
421 static int finish_get(node
**);
422 static int finish_peek();
425 input_iterator
*input_stack::top
= &nil_iterator
;
426 int input_stack::level
= 0;
427 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
429 inline int input_stack::get_level()
431 return level
+ top
->internal_level();
434 inline int input_stack::get(node
**np
)
436 return (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
439 int input_stack::finish_get(node
**np
)
442 int c
= top
->fill(np
);
443 if (c
!= EOF
|| top
->is_boundary())
445 if (top
== &nil_iterator
)
447 input_iterator
*tem
= top
;
451 if (top
->ptr
< top
->eptr
)
458 inline int input_stack::peek()
460 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
463 int input_stack::finish_peek()
467 if (c
!= EOF
|| top
->is_boundary())
469 if (top
== &nil_iterator
)
471 input_iterator
*tem
= top
;
475 if (top
->ptr
< top
->eptr
)
482 void input_stack::add_boundary()
484 push(new input_boundary
);
487 void input_stack::add_return_boundary()
489 push(new input_return_boundary
);
492 int input_stack::is_return_boundary()
494 return top
->is_boundary() == 2;
497 void input_stack::remove_boundary()
499 assert(top
->is_boundary());
500 input_iterator
*temp
= top
->next
;
506 void input_stack::push(input_iterator
*in
)
510 if (++level
> limit
&& limit
> 0)
511 fatal("input stack limit exceeded (probable infinite loop)");
516 input_iterator
*input_stack::get_arg(int i
)
519 for (p
= top
; p
!= 0; p
= p
->next
)
521 return p
->get_arg(i
);
525 void input_stack::shift(int n
)
527 for (input_iterator
*p
= top
; p
; p
= p
->next
)
534 int input_stack::nargs()
536 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
542 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
544 for (input_iterator
*p
= top
; p
; p
= p
->next
)
545 if (p
->get_location(allow_macro
, filenamep
, linenop
))
550 void input_stack::backtrace()
554 // only backtrace down to (not including) the topmost file
555 for (input_iterator
*p
= top
;
556 p
&& !p
->get_location(0, &f
, &n
);
561 void input_stack::backtrace_all()
563 for (input_iterator
*p
= top
; p
; p
= p
->next
)
567 int input_stack::set_location(const char *filename
, int lineno
)
569 for (input_iterator
*p
= top
; p
; p
= p
->next
)
570 if (p
->set_location(filename
, lineno
))
575 void input_stack::next_file(FILE *fp
, const char *s
)
578 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
579 if ((*pp
)->next_file(fp
, s
))
581 if (++level
> limit
&& limit
> 0)
582 fatal("input stack limit exceeded");
583 *pp
= new file_iterator(fp
, s
);
584 (*pp
)->next
= &nil_iterator
;
587 void input_stack::end_file()
589 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
590 if ((*pp
)->is_file()) {
591 input_iterator
*tem
= *pp
;
599 void input_stack::clear()
602 while (top
!= &nil_iterator
) {
603 if (top
->is_boundary())
605 input_iterator
*tem
= top
;
610 // Keep while_request happy.
611 for (; nboundaries
> 0; --nboundaries
)
612 add_return_boundary();
615 void input_stack::pop_macro()
620 if (top
->next
== &nil_iterator
)
622 if (top
->is_boundary())
624 is_macro
= top
->is_macro();
625 input_iterator
*tem
= top
;
630 // Keep while_request happy.
631 for (; nboundaries
> 0; --nboundaries
)
632 add_return_boundary();
635 inline void input_stack::save_compatible_flag(int f
)
637 top
->save_compatible_flag(f
);
640 inline int input_stack::get_compatible_flag()
642 return top
->get_compatible_flag();
645 void backtrace_request()
647 input_stack::backtrace_all();
654 symbol nm
= get_long_name(0);
655 while (!tok
.newline() && !tok
.eof())
658 input_stack::end_file();
661 FILE *fp
= fopen(nm
.contents(), "r");
663 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
665 input_stack::next_file(fp
, nm
.contents());
673 if (!has_arg() || !get_integer(&n
))
675 input_stack::shift(n
);
679 static int get_char_for_escape_name(int allow_space
= 0)
684 copy_mode_error("end of input in escape name");
687 if (!invalid_input_char(c
))
692 input_stack::push(make_temp_iterator("\n"));
695 if (c
== ' ' && allow_space
)
701 copy_mode_error("%1 is not allowed in an escape name",
702 input_char_description(c
));
708 static symbol
read_two_char_escape_name()
711 buf
[0] = get_char_for_escape_name();
712 if (buf
[0] != '\0') {
713 buf
[1] = get_char_for_escape_name();
722 static symbol
read_long_escape_name(read_mode mode
)
724 int start_level
= input_stack::get_level();
725 char abuf
[ABUF_SIZE
];
727 int buf_size
= ABUF_SIZE
;
732 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
739 if (mode
== WITH_ARGS
&& c
== ' ')
741 if (i
+ 2 > buf_size
) {
743 buf
= new char[ABUF_SIZE
*2];
744 memcpy(buf
, abuf
, buf_size
);
745 buf_size
= ABUF_SIZE
*2;
749 buf
= new char[buf_size
*2];
750 memcpy(buf
, old_buf
, buf_size
);
755 if (c
== ']' && input_stack::get_level() == start_level
)
764 if (mode
!= ALLOW_EMPTY
)
765 copy_mode_error("empty escape name");
777 static symbol
read_escape_name(read_mode mode
)
779 int c
= get_char_for_escape_name();
783 return read_two_char_escape_name();
784 if (c
== '[' && !compatible_flag
)
785 return read_long_escape_name(mode
);
792 static symbol
read_increment_and_escape_name(int *incp
)
794 int c
= get_char_for_escape_name();
801 return read_two_char_escape_name();
804 return read_escape_name();
807 return read_escape_name();
809 if (!compatible_flag
) {
811 return read_long_escape_name();
822 static int get_copy(node
**nd
, int defining
)
825 int c
= input_stack::get(nd
);
826 if (c
== ESCAPE_NEWLINE
) {
830 c
= input_stack::get(nd
);
831 } while (c
== ESCAPE_NEWLINE
);
833 if (c
!= escape_char
|| escape_char
<= 0)
835 c
= input_stack::peek();
840 (void)input_stack::get(0);
841 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
844 case '#': // Like \" but newline is ignored.
845 (void)input_stack::get(0);
846 while ((c
= input_stack::get(0)) != '\n')
852 (void)input_stack::get(0);
853 symbol s
= read_escape_name();
854 if (!(s
.is_null() || s
.is_empty()))
860 (void)input_stack::get(0);
861 symbol s
= read_escape_name(WITH_ARGS
);
862 if (!(s
.is_null() || s
.is_empty())) {
863 if (have_string_arg
) {
865 interpolate_string_with_args(s
);
868 interpolate_string(s
);
873 (void)input_stack::get(0);
876 (void)input_stack::get(0);
879 (void)input_stack::get(0);
883 (void)input_stack::get(0);
885 symbol s
= read_increment_and_escape_name(&inc
);
886 if (!(s
.is_null() || s
.is_empty()))
887 interpolate_number_reg(s
, inc
);
892 (void)input_stack::get(0);
893 symbol s
= read_escape_name();
894 if (!(s
.is_null() || s
.is_empty()))
895 interpolate_number_format(s
);
899 (void)input_stack::get(0);
903 (void)input_stack::get(0);
904 symbol s
= read_escape_name();
905 if (!(s
.is_null() || s
.is_empty()))
906 interpolate_environment_variable(s
);
910 (void)input_stack::get(0);
912 return ESCAPE_NEWLINE
;
915 (void)input_stack::get(0);
918 (void)input_stack::get(0);
921 (void)input_stack::get(0);
924 (void)input_stack::get(0);
927 (void)input_stack::get(0);
928 return ESCAPE_CIRCUMFLEX
;
930 (void)input_stack::get(0);
931 return ESCAPE_LEFT_BRACE
;
933 (void)input_stack::get(0);
934 return ESCAPE_RIGHT_BRACE
;
936 (void)input_stack::get(0);
937 return ESCAPE_LEFT_QUOTE
;
939 (void)input_stack::get(0);
940 return ESCAPE_RIGHT_QUOTE
;
942 (void)input_stack::get(0);
943 return ESCAPE_HYPHEN
;
945 (void)input_stack::get(0);
946 return ESCAPE_UNDERSCORE
;
948 (void)input_stack::get(0);
951 (void)input_stack::get(0);
954 (void)input_stack::get(0);
955 return ESCAPE_QUESTION
;
957 (void)input_stack::get(0);
958 return ESCAPE_AMPERSAND
;
960 (void)input_stack::get(0);
961 return ESCAPE_RIGHT_PARENTHESIS
;
963 (void)input_stack::get(0);
966 (void)input_stack::get(0);
967 return ESCAPE_PERCENT
;
969 if (c
== escape_char
) {
970 (void)input_stack::get(0);
979 class non_interpreted_char_node
: public node
{
982 non_interpreted_char_node(unsigned char);
984 int interpret(macro
*);
990 int non_interpreted_char_node::same(node
*nd
)
992 return c
== ((non_interpreted_char_node
*)nd
)->c
;
995 const char *non_interpreted_char_node::type()
997 return "non_interpreted_char_node";
1000 int non_interpreted_char_node::force_tprint()
1005 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1010 node
*non_interpreted_char_node::copy()
1012 return new non_interpreted_char_node(c
);
1015 int non_interpreted_char_node::interpret(macro
*mac
)
1021 static void do_width();
1022 static node
*do_non_interpreted();
1023 static node
*do_special();
1024 static node
*do_suppress(symbol nm
);
1025 static void do_register();
1027 dictionary
color_dictionary(501);
1028 static symbol
default_symbol("default");
1030 static color
*lookup_color(symbol nm
)
1032 assert(!nm
.is_null());
1033 if (nm
== default_symbol
)
1034 return &default_color
;
1035 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1037 warning(WARN_COLOR
, "`%1' not defined", nm
.contents());
1041 static node
*do_glyph_color(symbol nm
)
1046 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1048 color
*tem
= lookup_color(nm
);
1050 curenv
->set_glyph_color(tem
);
1052 (void)color_dictionary
.lookup(nm
, new color
);
1054 return new glyph_color_node(curenv
->get_glyph_color());
1057 static node
*do_fill_color(symbol nm
)
1062 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1064 color
*tem
= lookup_color(nm
);
1066 curenv
->set_fill_color(tem
);
1068 (void)color_dictionary
.lookup(nm
, new color
);
1070 return new fill_color_node(curenv
->get_fill_color());
1073 static unsigned int get_color_element(const char *scheme
, const char *col
)
1076 if (!get_number(&val
, 'f')) {
1077 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1082 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1085 if (val
> color::MAX_COLOR_VAL
+1) {
1086 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1087 // we change 0x10000 to 0xffff
1088 return color::MAX_COLOR_VAL
;
1090 return (unsigned int)val
;
1093 static color
*read_rgb()
1095 symbol component
= get_long_name(0);
1096 if (component
.is_null()) {
1097 warning(WARN_COLOR
, "missing rgb color values");
1100 const char *s
= component
.contents();
1101 color
*col
= new color
;
1103 if (!col
->read_rgb(s
)) {
1104 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1110 input_stack::push(make_temp_iterator(" "));
1111 input_stack::push(make_temp_iterator(s
));
1113 unsigned int r
= get_color_element("rgb color", "red component");
1114 unsigned int g
= get_color_element("rgb color", "green component");
1115 unsigned int b
= get_color_element("rgb color", "blue component");
1116 col
->set_rgb(r
, g
, b
);
1121 static color
*read_cmy()
1123 symbol component
= get_long_name(0);
1124 if (component
.is_null()) {
1125 warning(WARN_COLOR
, "missing cmy color values");
1128 const char *s
= component
.contents();
1129 color
*col
= new color
;
1131 if (!col
->read_cmy(s
)) {
1132 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1138 input_stack::push(make_temp_iterator(" "));
1139 input_stack::push(make_temp_iterator(s
));
1141 unsigned int c
= get_color_element("cmy color", "cyan component");
1142 unsigned int m
= get_color_element("cmy color", "magenta component");
1143 unsigned int y
= get_color_element("cmy color", "yellow component");
1144 col
->set_cmy(c
, m
, y
);
1149 static color
*read_cmyk()
1151 symbol component
= get_long_name(0);
1152 if (component
.is_null()) {
1153 warning(WARN_COLOR
, "missing cmyk color values");
1156 const char *s
= component
.contents();
1157 color
*col
= new color
;
1159 if (!col
->read_cmyk(s
)) {
1160 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1166 input_stack::push(make_temp_iterator(" "));
1167 input_stack::push(make_temp_iterator(s
));
1169 unsigned int c
= get_color_element("cmyk color", "cyan component");
1170 unsigned int m
= get_color_element("cmyk color", "magenta component");
1171 unsigned int y
= get_color_element("cmyk color", "yellow component");
1172 unsigned int k
= get_color_element("cmyk color", "black component");
1173 col
->set_cmyk(c
, m
, y
, k
);
1178 static color
*read_gray()
1180 symbol component
= get_long_name(0);
1181 if (component
.is_null()) {
1182 warning(WARN_COLOR
, "missing gray values");
1185 const char *s
= component
.contents();
1186 color
*col
= new color
;
1188 if (!col
->read_gray(s
)) {
1189 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1195 input_stack::push(make_temp_iterator("\n"));
1196 input_stack::push(make_temp_iterator(s
));
1198 unsigned int g
= get_color_element("gray", "gray value");
1204 static void activate_color()
1207 if (has_arg() && get_integer(&n
))
1208 color_flag
= n
!= 0;
1214 static void define_color()
1216 symbol color_name
= get_long_name(1);
1217 if (color_name
.is_null()) {
1221 if (color_name
== default_symbol
) {
1222 warning(WARN_COLOR
, "default color can't be redefined");
1226 symbol style
= get_long_name(1);
1227 if (style
.is_null()) {
1232 if (strcmp(style
.contents(), "rgb") == 0)
1234 else if (strcmp(style
.contents(), "cmyk") == 0)
1236 else if (strcmp(style
.contents(), "gray") == 0)
1238 else if (strcmp(style
.contents(), "grey") == 0)
1240 else if (strcmp(style
.contents(), "cmy") == 0)
1244 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1250 (void)color_dictionary
.lookup(color_name
, col
);
1254 static node
*do_overstrike()
1257 overstrike_node
*on
= new overstrike_node
;
1258 int start_level
= input_stack::get_level();
1262 if (tok
.newline() || tok
.eof()) {
1263 warning(WARN_DELIM
, "missing closing delimiter");
1267 && (compatible_flag
|| input_stack::get_level() == start_level
))
1269 charinfo
*ci
= tok
.get_char(1);
1271 node
*n
= curenv
->make_char_node(ci
);
1279 static node
*do_bracket()
1282 bracket_node
*bn
= new bracket_node
;
1284 int start_level
= input_stack::get_level();
1288 warning(WARN_DELIM
, "missing closing delimiter");
1291 if (tok
.newline()) {
1292 warning(WARN_DELIM
, "missing closing delimiter");
1293 input_stack::push(make_temp_iterator("\n"));
1297 && (compatible_flag
|| input_stack::get_level() == start_level
))
1299 charinfo
*ci
= tok
.get_char(1);
1301 node
*n
= curenv
->make_char_node(ci
);
1309 static int do_name_test()
1313 int start_level
= input_stack::get_level();
1318 if (tok
.newline() || tok
.eof()) {
1319 warning(WARN_DELIM
, "missing closing delimiter");
1323 && (compatible_flag
|| input_stack::get_level() == start_level
))
1329 return some_char
&& !bad_char
;
1332 static int do_expr_test()
1336 int start_level
= input_stack::get_level();
1337 if (!start
.delimiter(1))
1340 // disable all warning and error messages temporarily
1341 int saved_warning_mask
= warning_mask
;
1342 int saved_inhibit_errors
= inhibit_errors
;
1346 int result
= get_number_rigidly(&dummy
, 'u');
1347 warning_mask
= saved_warning_mask
;
1348 inhibit_errors
= saved_inhibit_errors
;
1349 if (tok
== start
&& input_stack::get_level() == start_level
)
1351 // ignore everything up to the delimiter in case we aren't right there
1354 if (tok
.newline() || tok
.eof()) {
1355 warning(WARN_DELIM
, "missing closing delimiter");
1358 if (tok
== start
&& input_stack::get_level() == start_level
)
1365 static node
*do_zero_width()
1369 int start_level
= input_stack::get_level();
1370 environment
env(curenv
);
1371 environment
*oldenv
= curenv
;
1375 if (tok
.newline() || tok
.eof()) {
1376 error("missing closing delimiter");
1380 && (compatible_flag
|| input_stack::get_level() == start_level
))
1385 node
*rev
= env
.extract_output_line();
1393 return new zero_width_node(n
);
1398 // It's undesirable for \Z to change environments, because then
1399 // \n(.w won't work as expected.
1401 static node
*do_zero_width()
1403 node
*rev
= new dummy_node
;
1406 int start_level
= input_stack::get_level();
1409 if (tok
.newline() || tok
.eof()) {
1410 warning(WARN_DELIM
, "missing closing delimiter");
1414 && (compatible_flag
|| input_stack::get_level() == start_level
))
1416 if (!tok
.add_to_node_list(&rev
))
1417 error("invalid token in argument to \\Z");
1426 return new zero_width_node(n
);
1431 token_node
*node::get_token_node()
1436 class token_node
: public node
{
1439 token_node(const token
&t
);
1441 token_node
*get_token_node();
1447 token_node::token_node(const token
&t
) : tk(t
)
1451 node
*token_node::copy()
1453 return new token_node(tk
);
1456 token_node
*token_node::get_token_node()
1461 int token_node::same(node
*nd
)
1463 return tk
== ((token_node
*)nd
)->tk
;
1466 const char *token_node::type()
1468 return "token_node";
1471 int token_node::force_tprint()
1476 token::token() : nd(0), type(TOKEN_EMPTY
)
1485 token::token(const token
&t
)
1486 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1488 // Use two statements to work around bug in SGI C++.
1490 nd
= tem
? tem
->copy() : 0;
1493 void token::operator=(const token
&t
)
1497 // Use two statements to work around bug in SGI C++.
1499 nd
= tem
? tem
->copy() : 0;
1516 return !tok
.newline();
1519 void token::make_space()
1524 void token::make_newline()
1526 type
= TOKEN_NEWLINE
;
1538 int cc
= input_stack::get(&n
);
1539 if (cc
!= escape_char
|| escape_char
== 0) {
1542 case COMPATIBLE_SAVE
:
1543 input_stack::save_compatible_flag(compatible_flag
);
1544 compatible_flag
= 0;
1546 case COMPATIBLE_RESTORE
:
1547 compatible_flag
= input_stack::get_compatible_flag();
1552 case TRANSPARENT_FILE_REQUEST
:
1554 case COPY_FILE_REQUEST
:
1556 case VJUSTIFY_REQUEST
:
1558 type
= TOKEN_REQUEST
;
1562 type
= TOKEN_BEGIN_TRAP
;
1565 type
= TOKEN_END_TRAP
;
1567 case LAST_PAGE_EJECTOR
:
1568 seen_last_page_ejector
= 1;
1571 type
= TOKEN_PAGE_EJECTOR
;
1573 case ESCAPE_PERCENT
:
1575 type
= TOKEN_HYPHEN_INDICATOR
;
1579 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1583 type
= TOKEN_STRETCHABLE_SPACE
;
1587 type
= TOKEN_ZERO_WIDTH_BREAK
;
1591 type
= TOKEN_ESCAPE
;
1594 goto handle_escape_char
;
1598 nd
= new hmotion_node(curenv
->get_narrow_space_width());
1600 case ESCAPE_CIRCUMFLEX
:
1603 nd
= new hmotion_node(curenv
->get_half_narrow_space_width());
1605 case ESCAPE_NEWLINE
:
1607 case ESCAPE_LEFT_BRACE
:
1609 type
= TOKEN_LEFT_BRACE
;
1611 case ESCAPE_RIGHT_BRACE
:
1613 type
= TOKEN_RIGHT_BRACE
;
1615 case ESCAPE_LEFT_QUOTE
:
1617 type
= TOKEN_SPECIAL
;
1620 case ESCAPE_RIGHT_QUOTE
:
1622 type
= TOKEN_SPECIAL
;
1627 type
= TOKEN_SPECIAL
;
1630 case ESCAPE_UNDERSCORE
:
1632 type
= TOKEN_SPECIAL
;
1637 type
= TOKEN_INTERRUPT
;
1641 type
= TOKEN_TRANSPARENT
;
1643 case ESCAPE_QUESTION
:
1645 nd
= do_non_interpreted();
1651 case ESCAPE_AMPERSAND
:
1655 case ESCAPE_RIGHT_PARENTHESIS
:
1656 ESCAPE_RIGHT_PARENTHESIS
:
1657 type
= TOKEN_TRANSPARENT_DUMMY
;
1660 type
= TOKEN_BACKSPACE
;
1669 type
= TOKEN_NEWLINE
;
1672 type
= TOKEN_LEADER
;
1677 token_node
*tn
= n
->get_token_node();
1696 cc
= input_stack::get(0);
1699 nm
= read_two_char_escape_name();
1700 type
= TOKEN_SPECIAL
;
1704 error("end of input after escape character");
1707 goto ESCAPE_LEFT_QUOTE
;
1709 goto ESCAPE_RIGHT_QUOTE
;
1713 goto ESCAPE_UNDERSCORE
;
1715 goto ESCAPE_PERCENT
;
1719 nd
= new hmotion_node(curenv
->get_digit_width());
1725 goto ESCAPE_CIRCUMFLEX
;
1727 type
= TOKEN_ITALIC_CORRECTION
;
1731 nd
= new left_italic_corrected_node
;
1734 goto ESCAPE_AMPERSAND
;
1736 goto ESCAPE_RIGHT_PARENTHESIS
;
1740 goto ESCAPE_QUESTION
;
1746 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1749 type
= TOKEN_NEWLINE
;
1753 case '#': // Like \" but newline is ignored.
1754 while ((cc
= input_stack::get(0)) != '\n')
1762 symbol nm
= read_escape_name();
1763 if (!(nm
.is_null() || nm
.is_empty()))
1764 interpolate_arg(nm
);
1769 symbol nm
= read_escape_name(WITH_ARGS
);
1770 if (!(nm
.is_null() || nm
.is_empty())) {
1771 if (have_string_arg
) {
1772 have_string_arg
= 0;
1773 interpolate_string_with_args(nm
);
1776 interpolate_string(nm
);
1781 nd
= new non_interpreted_char_node('\001');
1785 c
= '0' + do_name_test();
1793 c
= '0' + do_expr_test();
1799 nm
= get_delim_name();
1802 type
= TOKEN_SPECIAL
;
1806 nd
= new vmotion_node(curenv
->get_size()/2);
1809 nd
= read_draw_node();
1817 goto handle_escape_char
;
1820 symbol s
= read_escape_name(ALLOW_EMPTY
);
1824 for (p
= s
.contents(); *p
!= '\0'; p
++)
1827 if (*p
|| s
.is_empty())
1828 curenv
->set_font(s
);
1830 curenv
->set_font(atoi(s
.contents()));
1831 if (!compatible_flag
)
1837 symbol s
= read_escape_name(ALLOW_EMPTY
);
1840 curenv
->set_family(s
);
1845 symbol s
= read_escape_name();
1846 if (!(s
.is_null() || s
.is_empty()))
1847 interpolate_number_format(s
);
1851 if (!get_delim_number(&x
, 'm'))
1854 nd
= new hmotion_node(x
);
1857 // don't take height increments relative to previous height if
1858 // in compatibility mode
1859 if (!compatible_flag
&& curenv
->get_char_height())
1861 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
1862 curenv
->set_char_height(x
);
1866 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1867 curenv
->set_char_height(x
);
1869 if (!compatible_flag
)
1873 nm
= read_escape_name();
1874 if (nm
.is_null() || nm
.is_empty())
1876 type
= TOKEN_MARK_INPUT
;
1882 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1885 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1887 node
*n
= curenv
->make_char_node(s
);
1889 nd
= new hline_node(x
, n
);
1891 nd
= new vline_node(x
, n
);
1895 nd
= do_glyph_color(read_escape_name(ALLOW_EMPTY
));
1901 nd
= do_fill_color(read_escape_name(ALLOW_EMPTY
));
1909 symbol nm
= read_increment_and_escape_name(&inc
);
1910 if (!(nm
.is_null() || nm
.is_empty()))
1911 interpolate_number_reg(nm
, inc
);
1915 if (!get_delim_number(&val
, 0))
1917 type
= TOKEN_NUMBERED_CHAR
;
1920 nd
= do_overstrike();
1924 nd
= do_suppress(read_escape_name());
1930 type
= TOKEN_SPREAD
;
1934 nd
= new vmotion_node(-curenv
->get_size());
1938 if (!compatible_flag
)
1943 curenv
->set_size(x
);
1944 if (!compatible_flag
)
1948 if (get_delim_number(&x
, 0))
1949 curenv
->set_char_slant(x
);
1950 if (!compatible_flag
)
1955 nd
= new non_interpreted_char_node('\t');
1959 nd
= new vmotion_node(-curenv
->get_size()/2);
1962 if (!get_delim_number(&x
, 'v'))
1965 nd
= new vmotion_node(x
);
1969 symbol nm
= read_escape_name();
1970 if (!(nm
.is_null() || nm
.is_empty()))
1971 interpolate_environment_variable(nm
);
1978 if (!get_delim_number(&x
, 'v'))
1981 nd
= new extra_size_node(x
);
1991 symbol s
= read_escape_name();
1992 if (s
.is_null() || s
.is_empty())
1994 request_or_macro
*p
= lookup_request(s
);
1995 macro
*m
= p
->to_macro();
1997 error("can't transparently throughput a request");
2000 nd
= new special_node(*m
);
2007 if (type
== TOKEN_NODE
)
2008 nd
= new zero_width_node(nd
);
2010 charinfo
*ci
= get_char(1);
2013 node
*gn
= curenv
->make_char_node(ci
);
2016 nd
= new zero_width_node(gn
);
2022 nd
= do_zero_width();
2028 goto ESCAPE_LEFT_BRACE
;
2030 goto ESCAPE_RIGHT_BRACE
;
2034 if (!compatible_flag
) {
2035 nm
= read_long_escape_name();
2036 if (nm
.is_null() || nm
.is_empty())
2038 type
= TOKEN_SPECIAL
;
2041 goto handle_normal_char
;
2043 if (cc
!= escape_char
&& cc
!= '.')
2044 warning(WARN_ESCAPE
, "escape character ignored before %1",
2045 input_char_description(cc
));
2046 goto handle_normal_char
;
2052 int token::operator==(const token
&t
)
2061 case TOKEN_NUMBERED_CHAR
:
2062 return val
== t
.val
;
2068 int token::operator!=(const token
&t
)
2070 return !(*this == t
);
2073 // is token a suitable delimiter (like ')?
2075 int token::delimiter(int err
)
2104 error("cannot use character `%1' as a starting delimiter", char(c
));
2111 case TOKEN_STRETCHABLE_SPACE
:
2112 case TOKEN_UNSTRETCHABLE_SPACE
:
2116 error("cannot use %1 as a starting delimiter", description());
2123 const char *token::description()
2127 case TOKEN_BACKSPACE
:
2128 return "a backspace character";
2139 case TOKEN_HYPHEN_INDICATOR
:
2141 case TOKEN_INTERRUPT
:
2143 case TOKEN_ITALIC_CORRECTION
:
2146 return "a leader character";
2147 case TOKEN_LEFT_BRACE
:
2149 case TOKEN_MARK_INPUT
:
2155 case TOKEN_NUMBERED_CHAR
:
2157 case TOKEN_RIGHT_BRACE
:
2162 return "a special character";
2165 case TOKEN_STRETCHABLE_SPACE
:
2167 case TOKEN_UNSTRETCHABLE_SPACE
:
2170 return "a tab character";
2171 case TOKEN_TRANSPARENT
:
2173 case TOKEN_TRANSPARENT_DUMMY
:
2175 case TOKEN_ZERO_WIDTH_BREAK
:
2178 return "end of input";
2182 return "a magic token";
2187 while (!tok
.newline())
2198 if (has_arg() && get_integer(&n
))
2199 compatible_flag
= n
!= 0;
2201 compatible_flag
= 1;
2205 static void empty_name_warning(int required
)
2207 if (tok
.newline() || tok
.eof()) {
2209 warning(WARN_MISSING
, "missing name");
2211 else if (tok
.right_brace() || tok
.tab()) {
2212 const char *start
= tok
.description();
2215 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2216 if (!tok
.newline() && !tok
.eof())
2217 error("%1 is not allowed before an argument", start
);
2219 warning(WARN_MISSING
, "missing name");
2222 error("name expected (got %1)", tok
.description());
2224 error("name expected (got %1): treated as missing", tok
.description());
2227 static void non_empty_name_warning()
2229 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2230 && !tok
.right_brace()
2231 // We don't want to give a warning for .el\{
2232 && !tok
.left_brace())
2233 error("%1 is not allowed in a name", tok
.description());
2236 symbol
get_name(int required
)
2238 if (compatible_flag
) {
2241 if ((buf
[0] = tok
.ch()) != 0) {
2243 if ((buf
[1] = tok
.ch()) != 0) {
2248 non_empty_name_warning();
2252 empty_name_warning(required
);
2257 return get_long_name(required
);
2260 symbol
get_long_name(int required
)
2264 char abuf
[ABUF_SIZE
];
2266 int buf_size
= ABUF_SIZE
;
2269 if (i
+ 1 > buf_size
) {
2271 buf
= new char[ABUF_SIZE
*2];
2272 memcpy(buf
, abuf
, buf_size
);
2273 buf_size
= ABUF_SIZE
*2;
2276 char *old_buf
= buf
;
2277 buf
= new char[buf_size
*2];
2278 memcpy(buf
, old_buf
, buf_size
);
2283 if ((buf
[i
] = tok
.ch()) == 0)
2289 empty_name_warning(required
);
2292 non_empty_name_warning();
2305 topdiv
->set_last_page();
2306 if (!end_macro_name
.is_null()) {
2307 spring_trap(end_macro_name
);
2309 process_input_stack();
2311 curenv
->final_break();
2313 process_input_stack();
2315 if (topdiv
->get_page_length() > 0) {
2317 topdiv
->set_ejecting();
2318 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2319 input_stack::push(make_temp_iterator((char *)buf
));
2320 topdiv
->space(topdiv
->get_page_length(), 1);
2322 process_input_stack();
2323 seen_last_page_ejector
= 1; // should be set already
2324 topdiv
->set_ejecting();
2325 push_page_ejector();
2326 topdiv
->space(topdiv
->get_page_length(), 1);
2328 process_input_stack();
2330 // This will only happen if a trap-invoked macro starts a diversion,
2331 // or if vertical position traps have been disabled.
2332 cleanup_and_exit(0);
2335 // This implements .ex. The input stack must be cleared before calling
2340 input_stack::clear();
2347 void return_macro_request()
2349 input_stack::pop_macro();
2355 end_macro_name
= get_name();
2359 void blank_line_macro()
2361 blank_line_macro_name
= get_name();
2365 static void trapping_blank_line()
2367 if (!blank_line_macro_name
.is_null())
2368 spring_trap(blank_line_macro_name
);
2375 int old_compatible_flag
= compatible_flag
;
2376 compatible_flag
= 0;
2377 symbol nm
= get_name();
2381 interpolate_macro(nm
);
2382 compatible_flag
= old_compatible_flag
;
2385 inline int possibly_handle_first_page_transition()
2387 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2388 handle_first_page_transition();
2395 static int transparent_translate(int cc
)
2397 if (!invalid_input_char(cc
)) {
2398 charinfo
*ci
= charset_table
[cc
];
2399 switch (ci
->get_special_translation(1)) {
2400 case charinfo::TRANSLATE_SPACE
:
2402 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2403 return ESCAPE_TILDE
;
2404 case charinfo::TRANSLATE_DUMMY
:
2405 return ESCAPE_AMPERSAND
;
2406 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2407 return ESCAPE_PERCENT
;
2409 // This is really ugly.
2410 ci
= ci
->get_translation(1);
2412 int c
= ci
->get_ascii_code();
2415 error("can't translate %1 to special character `%2'"
2416 " in transparent throughput",
2417 input_char_description(cc
),
2425 struct int_stack_element
{
2427 int_stack_element
*next
;
2437 int_stack::int_stack()
2442 int_stack::~int_stack()
2445 int_stack_element
*temp
= top
;
2451 int int_stack::is_empty()
2456 void int_stack::push(int n
)
2458 int_stack_element
*p
= new int_stack_element
;
2464 int int_stack::pop()
2467 int_stack_element
*p
= top
;
2474 int node::reread(int *)
2479 int diverted_space_node::reread(int *bolp
)
2481 if (curenv
->get_fill())
2482 trapping_blank_line();
2489 int diverted_copy_file_node::reread(int *bolp
)
2491 curdiv
->copy_file(filename
.contents());
2496 int word_space_node::reread(int *bolp
)
2499 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2500 curenv
->space(w
->width
, w
->sentence_width
);
2507 int unbreakable_space_node::reread(int *)
2512 int hmotion_node::reread(int *bolp
)
2514 if (unformat
&& was_tab
) {
2515 curenv
->handle_tab(0);
2522 void process_input_stack()
2524 int_stack trap_bol_stack
;
2527 int suppress_next
= 0;
2529 case token::TOKEN_CHAR
:
2531 unsigned char ch
= tok
.c
;
2532 if (bol
&& !have_input
2533 && (ch
== curenv
->control_char
2534 || ch
== curenv
->no_break_control_char
)) {
2535 break_flag
= ch
== curenv
->control_char
;
2536 // skip tabs as well as spaces here
2539 } while (tok
.white_space());
2540 symbol nm
= get_name();
2544 interpolate_macro(nm
);
2549 if (possibly_handle_first_page_transition())
2553 curenv
->add_char(charset_table
[ch
]);
2555 if (tok
.type
!= token::TOKEN_CHAR
)
2565 case token::TOKEN_TRANSPARENT
:
2568 if (possibly_handle_first_page_transition())
2577 curdiv
->transparent_output(transparent_translate(cc
));
2579 curdiv
->transparent_output(n
);
2580 } while (cc
!= '\n' && cc
!= EOF
);
2582 curdiv
->transparent_output('\n');
2587 case token::TOKEN_NEWLINE
:
2589 if (bol
&& !have_input
2590 && !curenv
->get_prev_line_interrupted())
2591 trapping_blank_line();
2599 case token::TOKEN_REQUEST
:
2601 int request_code
= tok
.c
;
2603 switch (request_code
) {
2607 case COPY_FILE_REQUEST
:
2610 case TRANSPARENT_FILE_REQUEST
:
2614 case VJUSTIFY_REQUEST
:
2626 case token::TOKEN_SPACE
:
2628 if (possibly_handle_first_page_transition())
2630 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2632 // save space_width now so that it isn't changed by \f or \s
2633 // which we wouldn't notice here
2634 hunits space_width
= curenv
->get_space_width();
2636 nspaces
+= tok
.nspaces();
2638 } while (tok
.space());
2640 trapping_blank_line();
2644 curenv
->add_node(new hmotion_node(space_width
* nspaces
));
2654 case token::TOKEN_EOF
:
2656 case token::TOKEN_NODE
:
2658 if (possibly_handle_first_page_transition())
2660 else if (tok
.nd
->reread(&bol
)) {
2665 curenv
->add_node(tok
.nd
);
2668 curenv
->possibly_break_line(1);
2672 case token::TOKEN_PAGE_EJECTOR
:
2674 continue_page_eject();
2675 // I think we just want to preserve bol.
2679 case token::TOKEN_BEGIN_TRAP
:
2681 trap_bol_stack
.push(bol
);
2686 case token::TOKEN_END_TRAP
:
2688 if (trap_bol_stack
.is_empty())
2689 error("spurious end trap token detected!");
2691 bol
= trap_bol_stack
.pop();
2693 /* I'm not totally happy about this. But I can't think of any other
2694 way to do it. Doing an output_pending_lines() whenever a
2695 TOKEN_END_TRAP is detected doesn't work: for example,
2708 a\%very\%very\%long\%word
2710 will print all but the first lines from the word immediately
2711 after the footer, rather than on the next page. */
2713 if (trap_bol_stack
.is_empty())
2714 curenv
->output_pending_lines();
2726 trap_sprung_flag
= 0;
2730 #ifdef WIDOW_CONTROL
2732 void flush_pending_lines()
2734 while (!tok
.newline() && !tok
.eof())
2736 curenv
->output_pending_lines();
2740 #endif /* WIDOW_CONTROL */
2742 request_or_macro::request_or_macro()
2746 macro
*request_or_macro::to_macro()
2751 request::request(REQUEST_FUNCP pp
) : p(pp
)
2755 void request::invoke(symbol
)
2761 enum { SIZE
= 128 };
2762 unsigned char s
[SIZE
];
2767 char_block::char_block()
2776 void append(unsigned char);
2777 void set(unsigned char, int);
2778 unsigned char get(int);
2785 friend class macro_header
;
2786 friend class string_iterator
;
2789 char_list::char_list()
2790 : ptr(0), len(0), head(0), tail(0)
2794 char_list::~char_list()
2797 char_block
*tem
= head
;
2803 int char_list::length()
2808 void char_list::append(unsigned char c
)
2811 head
= tail
= new char_block
;
2815 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2816 tail
->next
= new char_block
;
2825 void char_list::set(unsigned char c
, int offset
)
2827 assert(len
> offset
);
2828 // optimization for access at the end
2829 int boundary
= len
- len
% char_block::SIZE
;
2830 if (offset
>= boundary
) {
2831 *(tail
->s
+ offset
- boundary
) = c
;
2834 char_block
*tem
= head
;
2837 l
+= char_block::SIZE
;
2839 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
2846 unsigned char char_list::get(int offset
)
2848 assert(len
> offset
);
2849 // optimization for access at the end
2850 int boundary
= len
- len
% char_block::SIZE
;
2851 if (offset
>= boundary
)
2852 return *(tail
->s
+ offset
- boundary
);
2853 char_block
*tem
= head
;
2856 l
+= char_block::SIZE
;
2858 return *(tem
->s
+ offset
% char_block::SIZE
);
2869 void append(node
*);
2873 friend class macro_header
;
2874 friend class string_iterator
;
2877 void node_list::append(node
*n
)
2885 tail
= tail
->next
= n
;
2889 int node_list::length()
2892 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2897 node_list::node_list()
2902 node
*node_list::extract()
2909 node_list::~node_list()
2911 delete_node_list(head
);
2914 struct macro_header
{
2919 macro_header() { count
= 1; }
2920 macro_header
*copy(int);
2925 if (p
!= 0 && --(p
->count
) <= 0)
2931 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2940 macro::macro(const macro
&m
)
2941 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
2942 empty_macro(m
.empty_macro
)
2948 macro
¯o::operator=(const macro
&m
)
2950 // don't assign object
2953 if (p
!= 0 && --(p
->count
) <= 0)
2956 filename
= m
.filename
;
2959 empty_macro
= m
.empty_macro
;
2963 void macro::append(unsigned char c
)
2967 p
= new macro_header
;
2968 if (p
->cl
.length() != len
) {
2969 macro_header
*tem
= p
->copy(len
);
2970 if (--(p
->count
) <= 0)
2976 if (c
!= COMPATIBLE_SAVE
&& c
!= COMPATIBLE_RESTORE
)
2980 void macro::set(unsigned char c
, int offset
)
2984 p
->cl
.set(c
, offset
);
2987 unsigned char macro::get(int offset
)
2990 return p
->cl
.get(offset
);
2998 void macro::append_str(const char *s
)
3003 while (s
[i
] != (char)0) {
3010 void macro::append(node
*n
)
3014 p
= new macro_header
;
3015 if (p
->cl
.length() != len
) {
3016 macro_header
*tem
= p
->copy(len
);
3017 if (--(p
->count
) <= 0)
3027 void macro::append_unsigned(unsigned int i
)
3029 unsigned int j
= i
/ 10;
3032 append(((unsigned char)(((int)'0') + i
% 10)));
3035 void macro::append_int(int i
)
3041 append_unsigned((unsigned int)i
);
3044 void macro::print_size()
3046 errprint("%1", len
);
3049 // make a copy of the first n bytes
3051 macro_header
*macro_header::copy(int n
)
3053 macro_header
*p
= new macro_header
;
3054 char_block
*bp
= cl
.head
;
3055 unsigned char *ptr
= bp
->s
;
3058 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3065 p
->nl
.append(nd
->copy());
3074 object_dictionary_iterator
iter(request_dictionary
);
3075 request_or_macro
*rm
;
3077 while (iter
.get(&s
, (object
**)&rm
)) {
3078 assert(!s
.is_null());
3079 macro
*m
= rm
->to_macro();
3081 errprint("%1\t", s
.contents());
3090 class string_iterator
: public input_iterator
{
3092 const char *how_invoked
;
3096 int count
; // of characters remaining
3098 int saved_compatible_flag
;
3103 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3106 int get_location(int, const char **, int *);
3108 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3109 int get_compatible_flag() { return saved_compatible_flag
; }
3112 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3113 : mac(m
), how_invoked(p
),
3114 newline_flag(0), lineno(1), nm(s
)
3118 bp
= mac
.p
->cl
.head
;
3119 nd
= mac
.p
->nl
.head
;
3129 string_iterator::string_iterator()
3140 int string_iterator::fill(node
**np
)
3147 const unsigned char *p
= eptr
;
3148 if (p
>= bp
->s
+ char_block::SIZE
) {
3160 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3165 unsigned char c
= *p
;
3166 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3180 int string_iterator::peek()
3184 const unsigned char *p
= eptr
;
3185 if (p
>= bp
->s
+ char_block::SIZE
) {
3191 int string_iterator::get_location(int allow_macro
,
3192 const char **filep
, int *linep
)
3196 if (mac
.filename
== 0)
3198 *filep
= mac
.filename
;
3199 *linep
= mac
.lineno
+ lineno
- 1;
3203 void string_iterator::backtrace()
3206 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3209 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3211 errprint(": %1\n", how_invoked
);
3218 class temp_iterator
: public input_iterator
{
3219 unsigned char *base
;
3220 temp_iterator(const char *, int len
);
3223 friend input_iterator
*make_temp_iterator(const char *);
3229 temp_iterator::temp_iterator(const char *s
, int len
)
3231 base
= new unsigned char[len
];
3232 memcpy(base
, s
, len
);
3237 temp_iterator::~temp_iterator()
3242 class small_temp_iterator
: public input_iterator
{
3244 small_temp_iterator(const char *, int);
3245 ~small_temp_iterator();
3246 enum { BLOCK
= 16 };
3247 static small_temp_iterator
*free_list
;
3248 void *operator new(size_t);
3249 void operator delete(void *);
3251 unsigned char buf
[SIZE
];
3252 friend input_iterator
*make_temp_iterator(const char *);
3255 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3257 void *small_temp_iterator::operator new(size_t n
)
3259 assert(n
== sizeof(small_temp_iterator
));
3262 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3263 for (int i
= 0; i
< BLOCK
- 1; i
++)
3264 free_list
[i
].next
= free_list
+ i
+ 1;
3265 free_list
[BLOCK
-1].next
= 0;
3267 small_temp_iterator
*p
= free_list
;
3268 free_list
= (small_temp_iterator
*)(free_list
->next
);
3276 void small_temp_iterator::operator delete(void *p
)
3279 ((small_temp_iterator
*)p
)->next
= free_list
;
3280 free_list
= (small_temp_iterator
*)p
;
3284 small_temp_iterator::~small_temp_iterator()
3291 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3293 for (int i
= 0; i
< len
; i
++)
3299 input_iterator
*make_temp_iterator(const char *s
)
3302 return new small_temp_iterator(s
, 0);
3305 if (n
<= small_temp_iterator::SIZE
)
3306 return new small_temp_iterator(s
, n
);
3308 return new temp_iterator(s
, n
);
3312 // this is used when macros with arguments are interpolated
3317 arg_list(const macro
&);
3321 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3325 arg_list::~arg_list()
3329 class macro_iterator
: public string_iterator
{
3333 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3336 int has_args() { return 1; }
3337 input_iterator
*get_arg(int i
);
3338 int nargs() { return argc
; }
3339 void add_arg(const macro
&m
);
3341 int is_macro() { return 1; }
3344 input_iterator
*macro_iterator::get_arg(int i
)
3347 return make_temp_iterator(nm
.contents());
3348 if (i
> 0 && i
<= argc
) {
3350 for (int j
= 1; j
< i
; j
++) {
3354 return new string_iterator(p
->mac
);
3360 void macro_iterator::add_arg(const macro
&m
)
3363 for (p
= &args
; *p
; p
= &((*p
)->next
))
3365 *p
= new arg_list(m
);
3369 void macro_iterator::shift(int n
)
3371 while (n
> 0 && argc
> 0) {
3372 arg_list
*tem
= args
;
3380 // This gets used by eg .if '\?xxx\?''.
3382 int operator==(const macro
&m1
, const macro
&m2
)
3384 if (m1
.len
!= m2
.len
)
3386 string_iterator
iter1(m1
);
3387 string_iterator
iter2(m2
);
3391 int c1
= iter1
.get(&nd1
);
3394 int c2
= iter2
.get(&nd2
);
3406 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3416 static void interpolate_macro(symbol nm
)
3418 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3421 const char *s
= nm
.contents();
3422 if (strlen(s
) > 2) {
3423 request_or_macro
*r
;
3428 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3430 macro
*m
= r
->to_macro();
3431 if (!m
|| !m
->empty())
3432 warned
= warning(WARN_SPACE
,
3433 "`%1' not defined (probable missing space after `%2')",
3434 nm
.contents(), buf
);
3438 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
3440 request_dictionary
.define(nm
, p
);
3451 static void decode_args(macro_iterator
*mi
)
3453 if (!tok
.newline() && !tok
.eof()) {
3455 int c
= get_copy(&n
);
3459 if (c
== '\n' || c
== EOF
)
3462 int quote_input_level
= 0;
3463 int done_tab_warning
= 0;
3465 quote_input_level
= input_stack::get_level();
3468 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3469 if (quote_input_level
> 0 && c
== '\"'
3471 || input_stack::get_level() == quote_input_level
)) {
3484 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3485 warning(WARN_TAB
, "tab character in unquoted macro argument");
3486 done_tab_warning
= 1;
3498 static void decode_string_args(macro_iterator
*mi
)
3501 int c
= get_copy(&n
);
3505 if (c
== '\n' || c
== EOF
) {
3506 error("missing `]'");
3512 int quote_input_level
= 0;
3513 int done_tab_warning
= 0;
3515 quote_input_level
= input_stack::get_level();
3518 while (c
!= EOF
&& c
!= '\n'
3519 && !(c
== ']' && quote_input_level
== 0)
3520 && !(c
== ' ' && quote_input_level
== 0)) {
3521 if (quote_input_level
> 0 && c
== '\"'
3522 && input_stack::get_level() == quote_input_level
) {
3535 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3536 warning(WARN_TAB
, "tab character in unquoted string argument");
3537 done_tab_warning
= 1;
3548 void macro::invoke(symbol nm
)
3550 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3552 input_stack::push(mi
);
3556 macro
*macro::to_macro()
3563 return empty_macro
== 1;
3566 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
3567 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
3571 macro_iterator::macro_iterator() : args(0), argc(0)
3575 macro_iterator::~macro_iterator()
3578 arg_list
*tem
= args
;
3584 int trap_sprung_flag
= 0;
3585 int postpone_traps_flag
= 0;
3586 symbol postponed_trap
;
3588 void spring_trap(symbol nm
)
3590 assert(!nm
.is_null());
3591 trap_sprung_flag
= 1;
3592 if (postpone_traps_flag
) {
3593 postponed_trap
= nm
;
3596 static char buf
[2] = { BEGIN_TRAP
, 0 };
3597 static char buf2
[2] = { END_TRAP
, '\0' };
3598 input_stack::push(make_temp_iterator(buf2
));
3599 request_or_macro
*p
= lookup_request(nm
);
3600 macro
*m
= p
->to_macro();
3602 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3604 error("you can't invoke a request with a trap");
3605 input_stack::push(make_temp_iterator(buf
));
3608 void postpone_traps()
3610 postpone_traps_flag
= 1;
3613 int unpostpone_traps()
3615 postpone_traps_flag
= 0;
3616 if (!postponed_trap
.is_null()) {
3617 spring_trap(postponed_trap
);
3618 postponed_trap
= NULL_SYMBOL
;
3627 macro_iterator
*mi
= new macro_iterator
;
3628 int reading_from_terminal
= isatty(fileno(stdin
));
3630 if (!tok
.newline() && !tok
.eof()) {
3631 int c
= get_copy(0);
3634 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3635 if (!invalid_input_char(c
)) {
3636 if (reading_from_terminal
)
3647 if (reading_from_terminal
) {
3648 fputc(had_prompt
? ':' : '\a', stderr
);
3651 input_stack::push(mi
);
3655 while ((c
= getchar()) != EOF
) {
3656 if (invalid_input_char(c
))
3657 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
3670 if (reading_from_terminal
)
3672 input_stack::push(new string_iterator(mac
));
3676 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3677 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
, CALLING_DISABLE_COMP
};
3679 void do_define_string(define_mode mode
, calling_mode calling
)
3693 else if (!tok
.space()) {
3694 error("bad string definition");
3705 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3706 macro
*mm
= rm
? rm
->to_macro() : 0;
3707 if (mode
== DEFINE_APPEND
&& mm
)
3709 if (calling
== CALLING_DISABLE_COMP
)
3710 mac
.append(COMPATIBLE_SAVE
);
3711 while (c
!= '\n' && c
!= EOF
) {
3715 mac
.append((unsigned char)c
);
3720 request_dictionary
.define(nm
, mm
);
3722 if (calling
== CALLING_DISABLE_COMP
)
3723 mac
.append(COMPATIBLE_RESTORE
);
3728 void define_string()
3730 do_define_string(DEFINE_NORMAL
, CALLING_NORMAL
);
3733 void define_nocomp_string()
3735 do_define_string(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3738 void append_string()
3740 do_define_string(DEFINE_APPEND
, CALLING_NORMAL
);
3743 void append_nocomp_string()
3745 do_define_string(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3748 void do_define_character(int fallback
)
3753 charinfo
*ci
= tok
.get_char(1);
3763 else if (!tok
.space()) {
3764 error("bad character definition");
3770 while (c
== ' ' || c
== '\t')
3774 macro
*m
= new macro
;
3775 while (c
!= '\n' && c
!= EOF
) {
3779 m
->append((unsigned char)c
);
3782 m
= ci
->set_macro(m
, fallback
);
3788 void define_character()
3790 do_define_character(0);
3793 void define_fallback_character()
3795 do_define_character(1);
3798 static void remove_character()
3801 while (!tok
.newline() && !tok
.eof()) {
3802 if (!tok
.space() && !tok
.tab()) {
3803 charinfo
*ci
= tok
.get_char(1);
3806 macro
*m
= ci
->set_macro(0);
3815 static void interpolate_string(symbol nm
)
3817 request_or_macro
*p
= lookup_request(nm
);
3818 macro
*m
= p
->to_macro();
3820 error("you can only invoke a string or macro using \\*");
3822 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3823 input_stack::push(si
);
3827 static void interpolate_string_with_args(symbol s
)
3829 request_or_macro
*p
= lookup_request(s
);
3830 macro
*m
= p
->to_macro();
3832 error("you can only invoke a string or macro using \\*");
3834 macro_iterator
*mi
= new macro_iterator(s
, *m
);
3835 decode_string_args(mi
);
3836 input_stack::push(mi
);
3840 /* This class is used for the implementation of \$@. It is used for
3841 each of the closing double quotes. It artificially increases the
3842 input level by 2, so that the closing double quote will appear to have
3843 the same input level as the opening quote. */
3845 class end_quote_iterator
: public input_iterator
{
3846 unsigned char buf
[1];
3848 end_quote_iterator();
3849 ~end_quote_iterator() { }
3850 int internal_level() { return 2; }
3853 end_quote_iterator::end_quote_iterator()
3860 static void interpolate_arg(symbol nm
)
3862 const char *s
= nm
.contents();
3863 if (!s
|| *s
== '\0')
3864 copy_mode_error("missing argument name");
3865 else if (s
[1] == 0 && csdigit(s
[0]))
3866 input_stack::push(input_stack::get_arg(s
[0] - '0'));
3867 else if (s
[0] == '*' && s
[1] == '\0') {
3868 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3869 input_stack::push(input_stack::get_arg(i
));
3871 input_stack::push(make_temp_iterator(" "));
3874 else if (s
[0] == '@' && s
[1] == '\0') {
3875 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3876 input_stack::push(new end_quote_iterator
);
3877 input_stack::push(input_stack::get_arg(i
));
3878 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
3883 for (p
= s
; *p
&& csdigit(*p
); p
++)
3886 copy_mode_error("bad argument name `%1'", s
);
3888 input_stack::push(input_stack::get_arg(atoi(s
)));
3892 void handle_first_page_transition()
3895 topdiv
->begin_page();
3898 // We push back a token by wrapping it up in a token_node, and
3899 // wrapping that up in a string_iterator.
3901 static void push_token(const token
&t
)
3904 m
.append(new token_node(t
));
3905 input_stack::push(new string_iterator(m
));
3908 void push_page_ejector()
3910 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
3911 input_stack::push(make_temp_iterator(buf
));
3914 void handle_initial_request(unsigned char code
)
3920 mac
.append(new token_node(tok
));
3921 input_stack::push(new string_iterator(mac
));
3922 input_stack::push(make_temp_iterator(buf
));
3923 topdiv
->begin_page();
3927 void handle_initial_title()
3929 handle_initial_request(TITLE_REQUEST
);
3932 // this should be local to define_macro, but cfront 1.2 doesn't support that
3933 static symbol
dot_symbol(".");
3935 void do_define_macro(define_mode mode
, calling_mode calling
)
3938 if (calling
== CALLING_INDIRECT
) {
3939 symbol temp1
= get_name(1);
3940 if (temp1
.is_null()) {
3944 symbol temp2
= get_name();
3945 input_stack::push(make_temp_iterator("\n"));
3946 if (!temp2
.is_null()) {
3947 interpolate_string(temp2
);
3948 input_stack::push(make_temp_iterator(" "));
3950 interpolate_string(temp1
);
3951 input_stack::push(make_temp_iterator(" "));
3954 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3961 term
= get_name(); // the request that terminates the definition
3964 while (!tok
.newline() && !tok
.eof())
3966 const char *start_filename
;
3968 int have_start_location
= input_stack::get_location(0, &start_filename
,
3971 // doing this here makes the line numbers come out right
3972 int c
= get_copy(&n
, 1);
3975 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3976 request_or_macro
*rm
=
3977 (request_or_macro
*)request_dictionary
.lookup(nm
);
3979 mm
= rm
->to_macro();
3980 if (mm
&& mode
== DEFINE_APPEND
)
3984 if (calling
== CALLING_DISABLE_COMP
)
3985 mac
.append(COMPATIBLE_SAVE
);
3987 while (c
== ESCAPE_NEWLINE
) {
3988 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
3990 c
= get_copy(&n
, 1);
3992 if (bol
&& c
== '.') {
3993 const char *s
= term
.contents();
3995 // see if it matches term
3998 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4000 if ((unsigned char)s
[0] == d
) {
4001 for (i
= 1; s
[i
] != 0; i
++) {
4003 if ((unsigned char)s
[i
] != d
)
4009 && ((i
== 2 && compatible_flag
)
4010 || (d
= get_copy(&n
)) == ' '
4011 || d
== '\n')) { // we found it
4016 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4019 request_dictionary
.define(nm
, mm
);
4021 if (calling
== CALLING_DISABLE_COMP
)
4022 mac
.append(COMPATIBLE_RESTORE
);
4025 if (term
!= dot_symbol
) {
4027 interpolate_macro(term
);
4033 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4035 for (int j
= 0; j
< i
; j
++)
4041 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4042 if (have_start_location
)
4043 error_with_file_and_line(start_filename
, start_lineno
,
4044 "end of file while defining macro `%1'",
4047 error("end of file while defining macro `%1'", nm
.contents());
4050 if (have_start_location
)
4051 error_with_file_and_line(start_filename
, start_lineno
,
4052 "end of file while ignoring input lines");
4054 error("end of file while ignoring input lines");
4059 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4066 c
= get_copy(&n
, 1);
4072 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
);
4075 void define_nocomp_macro()
4077 do_define_macro(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
4080 void define_indirect_macro()
4082 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
);
4087 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
);
4090 void append_indirect_macro()
4092 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
);
4095 void append_nocomp_macro()
4097 do_define_macro(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
4103 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
);
4110 symbol s
= get_name();
4113 request_dictionary
.remove(s
);
4120 symbol s1
= get_name(1);
4121 if (!s1
.is_null()) {
4122 symbol s2
= get_name(1);
4124 request_dictionary
.rename(s1
, s2
);
4131 symbol s1
= get_name(1);
4132 if (!s1
.is_null()) {
4133 symbol s2
= get_name(1);
4134 if (!s2
.is_null()) {
4135 if (!request_dictionary
.alias(s1
, s2
))
4136 warning(WARN_MAC
, "`%1' not defined", s2
.contents());
4144 symbol s
= get_name(1);
4146 request_or_macro
*p
= lookup_request(s
);
4147 macro
*m
= p
->to_macro();
4149 error("cannot chop request");
4150 else if (m
->empty())
4151 error("cannot chop empty macro");
4153 int have_restore
= 0;
4154 // we have to check for additional save/restore pairs which could be
4155 // there due to empty am1 requests.
4157 if (m
->get(m
->len
- 1) != COMPATIBLE_RESTORE
)
4161 if (m
->get(m
->len
- 1) != COMPATIBLE_SAVE
)
4169 error("cannot chop empty macro");
4172 m
->set(COMPATIBLE_RESTORE
, m
->len
- 1);
4181 void substring_request()
4183 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4184 symbol s
= get_name(1);
4185 if (!s
.is_null() && get_integer(&start
)) {
4186 request_or_macro
*p
= lookup_request(s
);
4187 macro
*m
= p
->to_macro();
4189 error("cannot apply `substring' on a request");
4192 if (!has_arg() || get_integer(&end
)) {
4193 int real_length
= 0; // 1, 2, ..., n
4194 string_iterator
iter1(*m
);
4195 for (int l
= 0; l
< m
->len
; l
++) {
4196 int c
= iter1
.get(0);
4197 if (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4204 start
+= real_length
;
4212 if (start
>= real_length
|| end
< 0) {
4214 "start and end index of substring out of range");
4217 if (--(m
->p
->count
) <= 0)
4226 "start index of substring out of range, set to 0");
4229 if (end
>= real_length
) {
4231 "end index of substring out of range, set to string length");
4232 end
= real_length
- 1;
4234 // now extract the substring
4235 string_iterator
iter(*m
);
4237 for (i
= 0; i
< start
; i
++) {
4238 int c
= iter
.get(0);
4239 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4245 for (; i
<= end
; i
++) {
4247 int c
= iter
.get(&nd
);
4248 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4255 mac
.append((unsigned char)c
);
4264 void length_request()
4268 if (ret
.is_null()) {
4278 else if (!tok
.space()) {
4279 error("bad string definition");
4290 while (c
!= '\n' && c
!= EOF
) {
4294 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4298 set_number_reg(ret
, len
);
4302 void asciify_macro()
4304 symbol s
= get_name(1);
4306 request_or_macro
*p
= lookup_request(s
);
4307 macro
*m
= p
->to_macro();
4309 error("cannot asciify request");
4312 string_iterator
iter(*m
);
4315 int c
= iter
.get(&nd
);
4329 void unformat_macro()
4331 symbol s
= get_name(1);
4333 request_or_macro
*p
= lookup_request(s
);
4334 macro
*m
= p
->to_macro();
4336 error("cannot unformat request");
4339 string_iterator
iter(*m
);
4342 int c
= iter
.get(&nd
);
4348 if (nd
->set_unformat_flag())
4358 static void interpolate_environment_variable(symbol nm
)
4360 const char *s
= getenv(nm
.contents());
4362 input_stack::push(make_temp_iterator(s
));
4365 void interpolate_number_reg(symbol nm
, int inc
)
4367 reg
*r
= lookup_number_reg(nm
);
4372 input_stack::push(make_temp_iterator(r
->get_string()));
4375 static void interpolate_number_format(symbol nm
)
4377 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4379 input_stack::push(make_temp_iterator(r
->get_format()));
4382 static int get_delim_number(units
*n
, int si
, int prev_value
)
4386 if (start
.delimiter(1)) {
4388 if (get_number(n
, si
, prev_value
)) {
4390 warning(WARN_DELIM
, "closing delimiter does not match");
4397 static int get_delim_number(units
*n
, int si
)
4401 if (start
.delimiter(1)) {
4403 if (get_number(n
, si
)) {
4405 warning(WARN_DELIM
, "closing delimiter does not match");
4412 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
4416 int start_level
= input_stack::get_level();
4417 if (!start
.delimiter(1))
4420 if (get_number(n
, si
)) {
4421 if (tok
.dummy() || tok
.transparent_dummy())
4423 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4424 *cp
= tok
.get_char(1);
4427 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4428 warning(WARN_DELIM
, "closing delimiter does not match");
4434 static int read_size(int *x
)
4444 else if (c
== '+') {
4455 // allow an increment either before or after the left parenthesis
4461 else if (c
== '+') {
4476 val
= val
*10 + (c
- '0');
4481 else if (csdigit(c
)) {
4483 if (!inc
&& c
!= '0' && c
< '4') {
4489 val
= val
*10 + (c
- '0');
4493 else if (!tok
.delimiter(1))
4499 ? get_number(&val
, 'z')
4500 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4502 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4503 if (start
.ch() == '[')
4504 error("missing `]'");
4506 error("missing closing delimiter");
4514 // special case -- \s[0] and \s0 means to revert to previous size
4521 *x
= curenv
->get_requested_point_size() + val
;
4524 *x
= curenv
->get_requested_point_size() - val
;
4531 "\\s request results in non-positive point size; set to 1");
4537 error("bad digit in point size");
4542 static symbol
get_delim_name()
4547 error("end of input at start of delimited name");
4550 if (start
.newline()) {
4551 error("can't delimit name with a newline");
4554 int start_level
= input_stack::get_level();
4555 char abuf
[ABUF_SIZE
];
4557 int buf_size
= ABUF_SIZE
;
4560 if (i
+ 1 > buf_size
) {
4562 buf
= new char[ABUF_SIZE
*2];
4563 memcpy(buf
, abuf
, buf_size
);
4564 buf_size
= ABUF_SIZE
*2;
4567 char *old_buf
= buf
;
4568 buf
= new char[buf_size
*2];
4569 memcpy(buf
, old_buf
, buf_size
);
4576 && (compatible_flag
|| input_stack::get_level() == start_level
))
4578 if ((buf
[i
] = tok
.ch()) == 0) {
4579 error("missing delimiter (got %1)", tok
.description());
4589 error("empty delimited name");
4604 static void do_register()
4608 if (!start
.delimiter(1))
4611 symbol nm
= get_long_name(1);
4616 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4618 if (!r
|| !r
->get_value(&prev_value
))
4621 if (!get_number(&val
, 'u', prev_value
))
4624 warning(WARN_DELIM
, "closing delimiter does not match");
4628 set_number_reg(nm
, val
);
4631 // this implements the \w escape sequence
4633 static void do_width()
4637 int start_level
= input_stack::get_level();
4638 environment
env(curenv
);
4639 environment
*oldenv
= curenv
;
4644 warning(WARN_DELIM
, "missing closing delimiter");
4647 if (tok
.newline()) {
4648 warning(WARN_DELIM
, "missing closing delimiter");
4649 input_stack::push(make_temp_iterator("\n"));
4653 && (compatible_flag
|| input_stack::get_level() == start_level
))
4658 units x
= env
.get_input_line_position().to_units();
4659 input_stack::push(make_temp_iterator(i_to_a(x
)));
4660 env
.width_registers();
4664 charinfo
*page_character
;
4666 void set_page_character()
4668 page_character
= get_optional_char();
4672 static const symbol
percent_symbol("%");
4674 void read_title_parts(node
**part
, hunits
*part_width
)
4677 if (tok
.newline() || tok
.eof())
4680 int start_level
= input_stack::get_level();
4682 for (int i
= 0; i
< 3; i
++) {
4683 while (!tok
.newline() && !tok
.eof()) {
4685 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
4689 if (page_character
!= 0 && tok
.get_char() == page_character
)
4690 interpolate_number_reg(percent_symbol
, 0);
4695 curenv
->wrap_up_tab();
4696 part_width
[i
] = curenv
->get_input_line_position();
4697 part
[i
] = curenv
->extract_output_line();
4699 while (!tok
.newline() && !tok
.eof())
4703 class non_interpreted_node
: public node
{
4706 non_interpreted_node(const macro
&);
4707 int interpret(macro
*);
4714 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
4718 int non_interpreted_node::same(node
*nd
)
4720 return mac
== ((non_interpreted_node
*)nd
)->mac
;
4723 const char *non_interpreted_node::type()
4725 return "non_interpreted_node";
4728 int non_interpreted_node::force_tprint()
4733 node
*non_interpreted_node::copy()
4735 return new non_interpreted_node(mac
);
4738 int non_interpreted_node::interpret(macro
*m
)
4740 string_iterator
si(mac
);
4754 static node
*do_non_interpreted()
4759 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4764 if (c
== EOF
|| c
== '\n') {
4765 error("missing \\?");
4768 return new non_interpreted_node(mac
);
4771 static void encode_char(macro
*mac
, char c
)
4774 if ((font::use_charnames_in_special
) && tok
.special()) {
4775 charinfo
*ci
= tok
.get_char(1);
4776 const char *s
= ci
->get_symbol()->contents();
4777 if (s
[0] != (char)0) {
4781 while (s
[i
] != (char)0) {
4789 else if (tok
.stretchable_space()
4790 || tok
.unstretchable_space())
4792 else if (!(tok
.hyphen_indicator()
4794 || tok
.transparent_dummy()
4795 || tok
.zero_width_break()))
4796 error("%1 is invalid within \\X", tok
.description());
4799 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4801 * add escape escape sequence
4813 int start_level
= input_stack::get_level();
4816 tok
!= start
|| input_stack::get_level() != start_level
;
4819 warning(WARN_DELIM
, "missing closing delimiter");
4822 if (tok
.newline()) {
4823 input_stack::push(make_temp_iterator("\n"));
4824 warning(WARN_DELIM
, "missing closing delimiter");
4832 else if (tok
.leader())
4834 else if (tok
.backspace())
4838 encode_char(&mac
, c
);
4840 return new special_node(mac
);
4843 void output_request()
4845 if (!tok
.newline() && !tok
.eof()) {
4853 if (c
!= ' ' && c
!= '\t')
4856 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
4857 topdiv
->transparent_output(c
);
4858 topdiv
->transparent_output('\n');
4863 extern int image_no
; // from node.cc
4865 static node
*do_suppress(symbol nm
)
4867 if (nm
.is_null() || nm
.is_empty()) {
4868 error("expecting an argument to escape \\O");
4871 const char *s
= nm
.contents();
4874 if (begin_level
== 0)
4875 // suppress generation of glyphs
4876 return new suppress_node(0, 0);
4879 if (begin_level
== 0)
4880 // enable generation of glyphs
4881 return new suppress_node(1, 0);
4884 if (begin_level
== 0)
4885 return new suppress_node(1, 1);
4895 s
++; // move over '5'
4897 if (*s
== (char)0) {
4898 error("missing position and filename in \\O");
4901 if (!(position
== 'l'
4904 || position
== 'i')) {
4905 error("l, r, c, or i position expected (got %1 in \\O)", position
);
4908 s
++; // onto image name
4909 if (s
== (char *)0) {
4910 error("missing image name for \\O");
4914 if (begin_level
== 0)
4915 return new suppress_node(symbol(s
), position
, image_no
);
4919 error("`%1' is an invalid argument to \\O", *s
);
4924 void special_node::tprint(troff_output_file
*out
)
4927 string_iterator
iter(mac
);
4929 int c
= iter
.get(0);
4932 for (const char *s
= ::asciify(c
); *s
; s
++)
4933 tprint_char(out
, *s
);
4938 int get_file_line(const char **filename
, int *lineno
)
4940 return input_stack::get_location(0, filename
, lineno
);
4946 if (get_integer(&n
)) {
4947 const char *filename
= 0;
4949 symbol s
= get_long_name();
4950 filename
= s
.contents();
4952 (void)input_stack::set_location(filename
, n
-1);
4957 static int nroff_mode
= 0;
4959 static void nroff_request()
4965 static void troff_request()
4971 static void skip_alternative()
4974 // ensure that ``.if 0\{'' works as expected
4975 if (tok
.left_brace())
4979 c
= input_stack::get(0);
4982 if (c
== ESCAPE_LEFT_BRACE
)
4984 else if (c
== ESCAPE_RIGHT_BRACE
)
4986 else if (c
== escape_char
&& escape_char
> 0)
4987 switch(input_stack::get(0)) {
4995 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
4999 Note that the level can properly be < 0, eg
5005 So don't give an error message in this case.
5007 if (level
<= 0 && c
== '\n')
5013 static void begin_alternative()
5015 while (tok
.space() || tok
.left_brace())
5025 static int_stack if_else_stack
;
5032 while (tok
.ch() == '!') {
5037 unsigned char c
= tok
.ch();
5040 result
= !nroff_mode
;
5042 else if (c
== 'n') {
5044 result
= nroff_mode
;
5046 else if (c
== 'v') {
5050 else if (c
== 'o') {
5051 result
= (topdiv
->get_page_number() & 1);
5054 else if (c
== 'e') {
5055 result
= !(topdiv
->get_page_number() & 1);
5058 else if (c
== 'd' || c
== 'r') {
5060 symbol nm
= get_name(1);
5066 ? request_dictionary
.lookup(nm
) != 0
5067 : number_reg_dictionary
.lookup(nm
) != 0);
5069 else if (c
== 'm') {
5071 symbol nm
= get_long_name(1);
5076 result
= (nm
== default_symbol
5077 || color_dictionary
.lookup(nm
) != 0);
5079 else if (c
== 'c') {
5082 charinfo
*ci
= tok
.get_char(1);
5087 result
= character_exists(ci
, curenv
);
5090 else if (tok
.space())
5092 else if (tok
.delimiter()) {
5094 int delim_level
= input_stack::get_level();
5095 environment
env1(curenv
);
5096 environment
env2(curenv
);
5097 environment
*oldenv
= curenv
;
5099 for (int i
= 0; i
< 2; i
++) {
5102 if (tok
.newline() || tok
.eof()) {
5103 warning(WARN_DELIM
, "missing closing delimiter");
5109 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5115 node
*n1
= env1
.extract_output_line();
5116 node
*n2
= env2
.extract_output_line();
5117 result
= same_node_list(n1
, n2
);
5118 delete_node_list(n1
);
5119 delete_node_list(n2
);
5125 if (!get_number(&n
, 'u')) {
5135 begin_alternative();
5141 void if_else_request()
5143 if_else_stack
.push(do_if_request());
5153 if (if_else_stack
.is_empty()) {
5154 warning(WARN_EL
, "unbalanced .el request");
5158 if (if_else_stack
.pop())
5161 begin_alternative();
5165 static int while_depth
= 0;
5166 static int while_break_flag
= 0;
5168 void while_request()
5173 mac
.append(new token_node(tok
));
5176 int c
= input_stack::get(&n
);
5192 if (c
== ESCAPE_LEFT_BRACE
)
5194 else if (c
== ESCAPE_RIGHT_BRACE
)
5196 else if (c
== escape_char
)
5199 if (c
== '\n' && level
<= 0)
5204 error("unbalanced \\{ \\}");
5207 input_stack::add_boundary();
5209 input_stack::push(new string_iterator(mac
, "while loop"));
5211 if (!do_if_request()) {
5212 while (input_stack::get(0) != EOF
)
5216 process_input_stack();
5217 if (while_break_flag
|| input_stack::is_return_boundary()) {
5218 while_break_flag
= 0;
5222 input_stack::remove_boundary();
5228 void while_break_request()
5231 error("no while loop");
5235 while_break_flag
= 1;
5236 while (input_stack::get(0) != EOF
)
5242 void while_continue_request()
5245 error("no while loop");
5249 while (input_stack::get(0) != EOF
)
5259 symbol nm
= get_long_name(1);
5263 while (!tok
.newline() && !tok
.eof())
5266 FILE *fp
= fopen(nm
.contents(), "r");
5268 input_stack::push(new file_iterator(fp
, nm
.contents()));
5270 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5275 // like .so but use popen()
5280 error(".pso request not allowed in safer mode");
5284 #ifdef POPEN_MISSING
5285 error("pipes not available on this system");
5287 #else /* not POPEN_MISSING */
5288 if (tok
.newline() || tok
.eof())
5289 error("missing command");
5292 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5295 char *buf
= new char[buf_size
];
5297 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5298 const char *s
= asciify(c
);
5299 int slen
= strlen(s
);
5300 if (buf_used
+ slen
+ 1> buf_size
) {
5301 char *old_buf
= buf
;
5302 int old_buf_size
= buf_size
;
5304 buf
= new char[buf_size
];
5305 memcpy(buf
, old_buf
, old_buf_size
);
5308 strcpy(buf
+ buf_used
, s
);
5311 buf
[buf_used
] = '\0';
5313 FILE *fp
= popen(buf
, POPEN_RT
);
5315 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5317 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5321 #endif /* not POPEN_MISSING */
5327 static int llx_reg_contents
= 0;
5328 static int lly_reg_contents
= 0;
5329 static int urx_reg_contents
= 0;
5330 static int ury_reg_contents
= 0;
5332 struct bounding_box
{
5333 int llx
, lly
, urx
, ury
;
5336 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5337 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5339 int parse_bounding_box(char *p
, bounding_box
*bb
)
5341 if (sscanf(p
, "%d %d %d %d",
5342 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5345 /* The Document Structuring Conventions say that the numbers
5346 should be integers. Unfortunately some broken applications
5348 double x1
, x2
, x3
, x4
;
5349 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5357 for (; *p
== ' ' || *p
== '\t'; p
++)
5359 if (strncmp(p
, "(atend)", 7) == 0) {
5364 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5368 // This version is taken from psrm.cc
5370 #define PS_LINE_MAX 255
5371 cset
white_space("\n\r \t");
5373 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5382 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5383 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5384 error("invalid input character code %1 in `%2'", int(c
), filename
);
5385 else if (i
< PS_LINE_MAX
)
5389 error("PostScript file `%1' is non-conforming "
5390 "because length of line exceeds 255", filename
);
5398 if (c
!= EOF
&& c
!= '\n')
5404 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5406 llx_reg_contents
= llx
;
5407 lly_reg_contents
= lly
;
5408 urx_reg_contents
= urx
;
5409 ury_reg_contents
= ury
;
5412 void do_ps_file(FILE *fp
, const char* filename
)
5416 char buf
[PS_LINE_MAX
];
5417 llx_reg_contents
= lly_reg_contents
=
5418 urx_reg_contents
= ury_reg_contents
= 0;
5419 if (!ps_get_line(buf
, fp
, filename
)) {
5420 error("`%1' is empty", filename
);
5423 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5424 error("`%1' is not conforming to the Document Structuring Conventions",
5428 while (ps_get_line(buf
, fp
, filename
) != 0) {
5429 if (buf
[0] != '%' || buf
[1] != '%'
5430 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5432 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5433 int res
= parse_bounding_box(buf
+ 14, &bb
);
5435 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5438 else if (res
== 2) {
5443 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5452 /* in the trailer, the last BoundingBox comment is significant */
5453 for (offset
= 512; !last_try
; offset
*= 2) {
5454 int had_trailer
= 0;
5456 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5458 if (fseek(fp
, 0L, 0) == -1)
5461 while (ps_get_line(buf
, fp
, filename
) != 0) {
5462 if (buf
[0] == '%' && buf
[1] == '%') {
5464 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5468 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5469 int res
= parse_bounding_box(buf
+ 14, &bb
);
5472 else if (res
== 2) {
5473 error("`(atend)' not allowed in trailer of `%1'", filename
);
5477 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5486 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5491 error("%%%%BoundingBox comment not found in `%1'", filename
);
5494 void ps_bbox_request()
5496 symbol nm
= get_long_name(1);
5500 while (!tok
.newline() && !tok
.eof())
5503 // PS files might contain non-printable characters, such as ^Z
5504 // and CRs not followed by an LF, so open them in binary mode.
5505 FILE *fp
= fopen(nm
.contents(), FOPEN_RB
);
5507 do_ps_file(fp
, nm
.contents());
5511 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5516 const char *asciify(int c
)
5519 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5520 buf
[1] = buf
[2] = '\0';
5522 case ESCAPE_QUESTION
:
5525 case ESCAPE_AMPERSAND
:
5528 case ESCAPE_RIGHT_PARENTHESIS
:
5531 case ESCAPE_UNDERSCORE
:
5537 case ESCAPE_CIRCUMFLEX
:
5540 case ESCAPE_LEFT_BRACE
:
5543 case ESCAPE_RIGHT_BRACE
:
5546 case ESCAPE_LEFT_QUOTE
:
5549 case ESCAPE_RIGHT_QUOTE
:
5567 case ESCAPE_PERCENT
:
5579 case COMPATIBLE_SAVE
:
5580 case COMPATIBLE_RESTORE
:
5584 if (invalid_input_char(c
))
5593 const char *input_char_description(int c
)
5597 return "a newline character";
5599 return "a backspace character";
5601 return "a leader character";
5603 return "a tab character";
5605 return "a space character";
5609 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
5610 if (invalid_input_char(c
)) {
5611 const char *s
= asciify(c
);
5618 sprintf(buf
, "magic character code %d", c
);
5627 sprintf(buf
, "character code %d", c
);
5631 // .tm, .tm1, and .tmc
5633 void do_terminal(int newline
, int string_like
)
5635 if (!tok
.newline() && !tok
.eof()) {
5639 if (string_like
&& c
== '"') {
5643 if (c
!= ' ' && c
!= '\t')
5646 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5647 fputs(asciify(c
), stderr
);
5650 fputc('\n', stderr
);
5665 void terminal_continue()
5670 dictionary
stream_dictionary(20);
5672 void do_open(int append
)
5674 symbol stream
= get_name(1);
5675 if (!stream
.is_null()) {
5676 symbol filename
= get_long_name(1);
5677 if (!filename
.is_null()) {
5679 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
5681 error("can't open `%1' for %2: %3",
5682 filename
.contents(),
5683 append
? "appending" : "writing",
5685 fp
= (FILE *)stream_dictionary
.remove(stream
);
5688 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
5699 error(".open request not allowed in safer mode");
5706 void opena_request()
5709 error(".opena request not allowed in safer mode");
5716 void close_request()
5718 symbol stream
= get_name(1);
5719 if (!stream
.is_null()) {
5720 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
5722 error("no stream named `%1'", stream
.contents());
5729 // .write and .writec
5731 void do_write_request(int newline
)
5733 symbol stream
= get_name(1);
5734 if (stream
.is_null()) {
5738 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5740 error("no stream named `%1'", stream
.contents());
5745 while ((c
= get_copy(0)) == ' ')
5749 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5750 fputs(asciify(c
), fp
);
5757 void write_request()
5759 do_write_request(1);
5762 void write_request_continue()
5764 do_write_request(0);
5767 void write_macro_request()
5769 symbol stream
= get_name(1);
5770 if (stream
.is_null()) {
5774 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5776 error("no stream named `%1'", stream
.contents());
5780 symbol s
= get_name(1);
5785 request_or_macro
*p
= lookup_request(s
);
5786 macro
*m
= p
->to_macro();
5788 error("cannot write request");
5790 string_iterator
iter(*m
);
5792 int c
= iter
.get(0);
5795 fputs(asciify(c
), fp
);
5802 void warnscale_request()
5809 warn_scale
= (double)units_per_inch
;
5811 warn_scale
= (double)units_per_inch
/ 2.54;
5813 warn_scale
= (double)units_per_inch
/ 72.0;
5815 warn_scale
= (double)units_per_inch
/ 6.0;
5818 "invalid scaling indicator `%1', using `i' instead", c
);
5821 warn_scaling_indicator
= c
;
5826 void spreadwarn_request()
5829 if (has_arg() && get_hunits(&n
, 'm')) {
5832 hunits em
= curenv
->get_size();
5833 spread_limit
= (double)n
.to_units()
5834 / (em
.is_zero() ? hresolution
: em
.to_units());
5837 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
5838 // changing value; we mirror at
5839 // -0.5 to make zero a valid value
5843 static void init_charset_table()
5846 strcpy(buf
, "char");
5847 for (int i
= 0; i
< 256; i
++) {
5848 strcpy(buf
+ 4, i_to_a(i
));
5849 charset_table
[i
] = get_charinfo(symbol(buf
));
5850 charset_table
[i
]->set_ascii_code(i
);
5852 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
5854 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
5855 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
5856 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
5857 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
5858 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
5859 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
5860 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
5861 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
5862 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
5863 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
5864 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
5865 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
5866 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5867 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5868 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5869 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5870 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
5871 page_character
= charset_table
['%'];
5874 static void init_hpf_code_table()
5876 for (int i
= 0; i
< 256; i
++)
5877 hpf_code_table
[i
] = i
;
5880 static void do_translate(int translate_transparent
, int translate_input
)
5883 while (!tok
.newline() && !tok
.eof()) {
5885 // This is a really bizarre troff feature.
5887 translate_space_to_dummy
= tok
.dummy();
5888 if (tok
.newline() || tok
.eof())
5893 charinfo
*ci1
= tok
.get_char(1);
5897 if (tok
.newline() || tok
.eof()) {
5898 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5899 translate_transparent
);
5903 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5904 translate_transparent
);
5905 else if (tok
.stretchable_space())
5906 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
5907 translate_transparent
);
5908 else if (tok
.dummy())
5909 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
5910 translate_transparent
);
5911 else if (tok
.hyphen_indicator())
5912 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
5913 translate_transparent
);
5915 charinfo
*ci2
= tok
.get_char(1);
5919 ci1
->set_translation(0, translate_transparent
, translate_input
);
5921 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
5933 void translate_no_transparent()
5938 void translate_input()
5946 if (get_integer(&flags
))
5948 charinfo
*ci
= tok
.get_char(1);
5950 charinfo
*tem
= ci
->get_translation();
5953 ci
->set_flags(flags
);
5960 void hyphenation_code()
5963 while (!tok
.newline() && !tok
.eof()) {
5964 charinfo
*ci
= tok
.get_char(1);
5969 unsigned char c
= tok
.ch();
5971 error("hyphenation code must be ordinary character");
5975 error("hyphenation code cannot be digit");
5978 ci
->set_hyphenation_code(c
);
5979 if (ci
->get_translation()
5980 && ci
->get_translation()->get_translation_input())
5981 ci
->get_translation()->set_hyphenation_code(c
);
5988 void hyphenation_patterns_file_code()
5991 while (!tok
.newline() && !tok
.eof()) {
5993 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
5995 error("missing output hyphenation code");
5998 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
5999 hpf_code_table
[n1
] = n2
;
6003 error("output hyphenation code must be integer in the range 0..255");
6008 error("input hyphenation code must be integer in the range 0..255");
6015 charinfo
*token::get_char(int required
)
6017 if (type
== TOKEN_CHAR
)
6018 return charset_table
[c
];
6019 if (type
== TOKEN_SPECIAL
)
6020 return get_charinfo(nm
);
6021 if (type
== TOKEN_NUMBERED_CHAR
)
6022 return get_charinfo_by_number(val
);
6023 if (type
== TOKEN_ESCAPE
) {
6024 if (escape_char
!= 0)
6025 return charset_table
[escape_char
];
6027 error("`\\e' used while no current escape character");
6032 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6033 warning(WARN_MISSING
, "missing normal or special character");
6035 error("normal or special character expected (got %1)", description());
6040 charinfo
*get_optional_char()
6044 charinfo
*ci
= tok
.get_char();
6046 check_missing_character();
6052 void check_missing_character()
6054 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6055 error("normal or special character expected (got %1): "
6056 "treated as missing",
6062 int token::add_to_node_list(node
**pp
)
6069 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6075 if (escape_char
!= 0)
6076 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6078 case TOKEN_HYPHEN_INDICATOR
:
6079 *pp
= (*pp
)->add_discretionary_hyphen();
6081 case TOKEN_ITALIC_CORRECTION
:
6082 *pp
= (*pp
)->add_italic_correction(&w
);
6084 case TOKEN_LEFT_BRACE
:
6086 case TOKEN_MARK_INPUT
:
6087 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6093 case TOKEN_NUMBERED_CHAR
:
6094 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6096 case TOKEN_RIGHT_BRACE
:
6099 n
= new hmotion_node(curenv
->get_space_width());
6102 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6104 case TOKEN_STRETCHABLE_SPACE
:
6105 n
= new unbreakable_space_node(curenv
->get_space_width());
6107 case TOKEN_UNSTRETCHABLE_SPACE
:
6108 n
= new space_char_hmotion_node(curenv
->get_space_width());
6110 case TOKEN_TRANSPARENT_DUMMY
:
6111 n
= new transparent_dummy_node
;
6113 case TOKEN_ZERO_WIDTH_BREAK
:
6114 n
= new space_node(H0
);
6116 n
->is_escape_colon();
6128 void token::process()
6130 if (possibly_handle_first_page_transition())
6133 case TOKEN_BACKSPACE
:
6134 curenv
->add_node(new hmotion_node(-curenv
->get_space_width()));
6137 curenv
->add_char(charset_table
[c
]);
6140 curenv
->add_node(new dummy_node
);
6149 if (escape_char
!= 0)
6150 curenv
->add_char(charset_table
[escape_char
]);
6152 case TOKEN_BEGIN_TRAP
:
6153 case TOKEN_END_TRAP
:
6154 case TOKEN_PAGE_EJECTOR
:
6155 // these are all handled in process_input_stack()
6157 case TOKEN_HYPHEN_INDICATOR
:
6158 curenv
->add_hyphen_indicator();
6160 case TOKEN_INTERRUPT
:
6161 curenv
->interrupt();
6163 case TOKEN_ITALIC_CORRECTION
:
6164 curenv
->add_italic_correction();
6167 curenv
->handle_tab(1);
6169 case TOKEN_LEFT_BRACE
:
6171 case TOKEN_MARK_INPUT
:
6172 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6178 curenv
->add_node(nd
);
6181 case TOKEN_NUMBERED_CHAR
:
6182 curenv
->add_char(get_charinfo_by_number(val
));
6185 // handled in process_input_stack()
6187 case TOKEN_RIGHT_BRACE
:
6193 curenv
->add_char(get_charinfo(nm
));
6198 case TOKEN_STRETCHABLE_SPACE
:
6199 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width()));
6201 case TOKEN_UNSTRETCHABLE_SPACE
:
6202 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width()));
6205 curenv
->handle_tab(0);
6207 case TOKEN_TRANSPARENT
:
6209 case TOKEN_TRANSPARENT_DUMMY
:
6210 curenv
->add_node(new transparent_dummy_node
);
6212 case TOKEN_ZERO_WIDTH_BREAK
:
6214 node
*tmp
= new space_node(H0
);
6215 tmp
->freeze_space();
6216 tmp
->is_escape_colon();
6217 curenv
->add_node(tmp
);
6225 class nargs_reg
: public reg
{
6227 const char *get_string();
6230 const char *nargs_reg::get_string()
6232 return i_to_a(input_stack::nargs());
6235 class lineno_reg
: public reg
{
6237 const char *get_string();
6240 const char *lineno_reg::get_string()
6244 if (!input_stack::get_location(0, &file
, &line
))
6246 return i_to_a(line
);
6249 class writable_lineno_reg
: public general_reg
{
6251 writable_lineno_reg();
6252 void set_value(units
);
6253 int get_value(units
*);
6256 writable_lineno_reg::writable_lineno_reg()
6260 int writable_lineno_reg::get_value(units
*res
)
6264 if (!input_stack::get_location(0, &file
, &line
))
6270 void writable_lineno_reg::set_value(units n
)
6272 input_stack::set_location(0, n
);
6275 class filename_reg
: public reg
{
6277 const char *get_string();
6280 const char *filename_reg::get_string()
6284 if (input_stack::get_location(0, &file
, &line
))
6290 class constant_reg
: public reg
{
6293 constant_reg(const char *);
6294 const char *get_string();
6297 constant_reg::constant_reg(const char *p
) : s(p
)
6301 const char *constant_reg::get_string()
6306 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6310 const char *constant_int_reg::get_string()
6315 void abort_request()
6320 else if (tok
.newline())
6323 while ((c
= get_copy(0)) == ' ')
6326 if (c
== EOF
|| c
== '\n')
6327 fputs("User Abort.", stderr
);
6329 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6330 fputs(asciify(c
), stderr
);
6332 fputc('\n', stderr
);
6333 cleanup_and_exit(1);
6339 char *s
= new char[len
];
6341 while ((c
= get_copy(0)) == ' ')
6344 while (c
!= '\n' && c
!= EOF
) {
6345 if (!invalid_input_char(c
)) {
6348 s
= new char[len
*2];
6349 memcpy(s
, tem
, len
);
6369 error(".pi request not allowed in safer mode");
6373 #ifdef POPEN_MISSING
6374 error("pipes not available on this system");
6376 #else /* not POPEN_MISSING */
6378 error("can't pipe: output already started");
6383 if ((pc
= read_string()) == 0)
6384 error("can't pipe to empty command");
6386 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6387 strcpy(s
, pipe_command
);
6390 a_delete pipe_command
;
6397 #endif /* not POPEN_MISSING */
6401 static int system_status
;
6403 void system_request()
6406 error(".sy request not allowed in safer mode");
6410 char *command
= read_string();
6412 error("empty command");
6414 system_status
= system(command
);
6422 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6423 handle_initial_request(COPY_FILE_REQUEST
);
6426 symbol filename
= get_long_name(1);
6427 while (!tok
.newline() && !tok
.eof())
6431 if (!filename
.is_null())
6432 curdiv
->copy_file(filename
.contents());
6440 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6441 handle_initial_request(VJUSTIFY_REQUEST
);
6444 symbol type
= get_long_name(1);
6445 if (!type
.is_null())
6446 curdiv
->vjustify(type
);
6452 void transparent_file()
6454 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6455 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6458 symbol filename
= get_long_name(1);
6459 while (!tok
.newline() && !tok
.eof())
6463 if (!filename
.is_null()) {
6465 FILE *fp
= fopen(filename
.contents(), "r");
6467 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6474 if (invalid_input_char(c
))
6475 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6477 curdiv
->transparent_output(c
);
6482 curdiv
->transparent_output('\n');
6494 page_range(int, int, page_range
*);
6495 int contains(int n
);
6498 page_range::page_range(int i
, int j
, page_range
*p
)
6499 : first(i
), last(j
), next(p
)
6503 int page_range::contains(int n
)
6505 return n
>= first
&& (last
<= 0 || n
<= last
);
6508 page_range
*output_page_list
= 0;
6510 int in_output_page_list(int n
)
6512 if (!output_page_list
)
6514 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
6520 static void parse_output_page_list(char *p
)
6526 else if (csdigit(*p
)) {
6529 i
= i
*10 + *p
++ - '0';
6530 while (csdigit(*p
));
6540 j
= j
*10 + *p
++ - '0';
6541 while (csdigit(*p
));
6547 last_page_number
= -1;
6548 else if (last_page_number
>= 0 && j
> last_page_number
)
6549 last_page_number
= j
;
6550 output_page_list
= new page_range(i
, j
, output_page_list
);
6556 error("bad output page list");
6557 output_page_list
= 0;
6561 static FILE *open_mac_file(const char *mac
, char **path
)
6563 // Try first FOOBAR.tmac, then tmac.FOOBAR
6564 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
6566 strcat(s1
, MACRO_POSTFIX
);
6567 FILE *fp
= mac_path
->open_file(s1
, path
);
6570 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
6571 strcpy(s2
, MACRO_PREFIX
);
6573 fp
= mac_path
->open_file(s2
, path
);
6579 static void process_macro_file(const char *mac
)
6582 FILE *fp
= open_mac_file(mac
, &path
);
6584 fatal("can't find macro file %1", mac
);
6585 const char *s
= symbol(path
).contents();
6587 input_stack::push(new file_iterator(fp
, s
));
6589 process_input_stack();
6592 static void process_startup_file(char *filename
)
6595 search_path
*orig_mac_path
= mac_path
;
6596 mac_path
= &config_macro_path
;
6597 FILE *fp
= mac_path
->open_file(filename
, &path
);
6599 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6602 process_input_stack();
6604 mac_path
= orig_mac_path
;
6609 symbol nm
= get_long_name(1);
6613 while (!tok
.newline() && !tok
.eof())
6616 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
6617 // .mso doesn't (and cannot) go through open_mac_file, so we
6618 // need to do it here manually: If we have tmac.FOOBAR, try
6619 // FOOBAR.tmac and vice versa
6621 const char *fn
= nm
.contents();
6622 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
6623 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
6624 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
6625 strcat(s
, MACRO_POSTFIX
);
6626 fp
= mac_path
->open_file(s
, &path
);
6630 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
6631 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
6632 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
6633 strcpy(s
, MACRO_PREFIX
);
6634 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
6635 fp
= mac_path
->open_file(s
, &path
);
6641 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6645 error("can't find macro file `%1'", nm
.contents());
6650 static void process_input_file(const char *name
)
6653 if (strcmp(name
, "-") == 0) {
6659 fp
= fopen(name
, "r");
6661 fatal("can't open `%1': %2", name
, strerror(errno
));
6663 input_stack::push(new file_iterator(fp
, name
));
6665 process_input_stack();
6668 // make sure the_input is empty before calling this
6670 static int evaluate_expression(const char *expr
, units
*res
)
6672 input_stack::push(make_temp_iterator(expr
));
6674 int success
= get_number(res
, 'u');
6675 while (input_stack::get(0) != EOF
)
6680 static void do_register_assignment(const char *s
)
6682 const char *p
= strchr(s
, '=');
6688 if (evaluate_expression(s
+ 1, &n
))
6689 set_number_reg(buf
, n
);
6692 char *buf
= new char[p
- s
+ 1];
6693 memcpy(buf
, s
, p
- s
);
6696 if (evaluate_expression(p
+ 1, &n
))
6697 set_number_reg(buf
, n
);
6702 static void set_string(const char *name
, const char *value
)
6704 macro
*m
= new macro
;
6705 for (const char *p
= value
; *p
; p
++)
6706 if (!invalid_input_char((unsigned char)*p
))
6708 request_dictionary
.define(name
, m
);
6711 static void do_string_assignment(const char *s
)
6713 const char *p
= strchr(s
, '=');
6718 set_string(buf
, s
+ 1);
6721 char *buf
= new char[p
- s
+ 1];
6722 memcpy(buf
, s
, p
- s
);
6724 set_string(buf
, p
+ 1);
6729 struct string_list
{
6732 string_list(const char *ss
) : s(ss
), next(0) {}
6736 static void prepend_string(const char *s
, string_list
**p
)
6738 string_list
*l
= new string_list(s
);
6744 static void add_string(const char *s
, string_list
**p
)
6748 *p
= new string_list(s
);
6751 void usage(FILE *stream
, const char *prog
)
6754 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6755 " -rcn -Tname -Fdir -Mdir [files...]\n",
6759 int main(int argc
, char **argv
)
6761 program_name
= argv
[0];
6762 static char stderr_buf
[BUFSIZ
];
6763 setbuf(stderr
, stderr_buf
);
6765 string_list
*macros
= 0;
6766 string_list
*register_assignments
= 0;
6767 string_list
*string_assignments
= 0;
6772 int no_rc
= 0; // don't process troffrc and troffrc-end
6773 int next_page_number
;
6775 hresolution
= vresolution
= 1;
6776 // restore $PATH if called from groff
6777 char* groff_path
= getenv("GROFF_PATH__");
6784 if (putenv(strsave(e
.contents())))
6785 fatal("putenv failed");
6787 static const struct option long_options
[] = {
6788 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
6789 { "version", no_argument
, 0, 'v' },
6792 while ((c
= getopt_long(argc
, argv
, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6798 printf("GNU troff (groff) version %s\n", Version_string
);
6805 is_html
= (strcmp(device
, "html") == 0);
6808 compatible_flag
= 1;
6814 macro_path
.command_line_dir(optarg
);
6815 safer_macro_path
.command_line_dir(optarg
);
6816 config_macro_path
.command_line_dir(optarg
);
6819 font::command_line_font_dir(optarg
);
6822 add_string(optarg
, ¯os
);
6831 enable_warning(optarg
);
6834 disable_warning(optarg
);
6843 ascii_output_flag
= 1;
6846 suppress_output_flag
= 1;
6849 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
6852 error("bad page number");
6855 parse_output_page_list(optarg
);
6858 if (*optarg
== '\0')
6859 error("`-d' requires non-empty argument");
6861 add_string(optarg
, &string_assignments
);
6864 if (*optarg
== '\0')
6865 error("`-r' requires non-empty argument");
6867 add_string(optarg
, ®ister_assignments
);
6870 default_family
= symbol(optarg
);
6876 // silently ignore these
6879 safer_flag
= 0; // unsafe behaviour
6881 case CHAR_MAX
+ 1: // --help
6882 usage(stdout
, argv
[0]);
6886 usage(stderr
, argv
[0]);
6888 break; // never reached
6893 mac_path
= ¯o_path
;
6894 set_string(".T", device
);
6895 init_charset_table();
6896 init_hpf_code_table();
6897 if (!font::load_desc())
6898 fatal("sorry, I can't continue");
6899 units_per_inch
= font::res
;
6900 hresolution
= font::hor
;
6901 vresolution
= font::vert
;
6902 sizescale
= font::sizescale
;
6903 tcommand_flag
= font::tcommand
;
6904 warn_scale
= (double)units_per_inch
;
6905 warn_scaling_indicator
= 'i';
6906 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
6907 default_family
= symbol(font::family
);
6908 font_size::init_size_table(font::sizes
);
6911 if (font::style_table
) {
6912 for (i
= 0; font::style_table
[i
]; i
++)
6913 mount_style(j
++, symbol(font::style_table
[i
]));
6915 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
6916 // In the DESC file a font name of 0 (zero) means leave this
6918 if (strcmp(font::font_name_table
[i
], "0") != 0)
6919 mount_font(j
, symbol(font::font_name_table
[i
]));
6920 curdiv
= topdiv
= new top_level_diversion
;
6922 topdiv
->set_next_page_number(next_page_number
);
6923 init_input_requests();
6924 init_env_requests();
6925 init_div_requests();
6927 init_column_requests();
6929 init_node_requests();
6930 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
6932 init_reg_requests();
6933 init_hyphen_requests();
6934 init_environments();
6935 while (string_assignments
) {
6936 do_string_assignment(string_assignments
->s
);
6937 string_list
*tem
= string_assignments
;
6938 string_assignments
= string_assignments
->next
;
6941 while (register_assignments
) {
6942 do_register_assignment(register_assignments
->s
);
6943 string_list
*tem
= register_assignments
;
6944 register_assignments
= register_assignments
->next
;
6948 process_startup_file(INITIAL_STARTUP_FILE
);
6950 process_macro_file(macros
->s
);
6951 string_list
*tem
= macros
;
6952 macros
= macros
->next
;
6956 process_startup_file(FINAL_STARTUP_FILE
);
6957 for (i
= optind
; i
< argc
; i
++)
6958 process_input_file(argv
[i
]);
6959 if (optind
>= argc
|| iflag
)
6960 process_input_file("-");
6962 return 0; // not reached
6968 if (has_arg() && get_integer(&n
)) {
6969 if (n
& ~WARN_TOTAL
) {
6970 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
6976 warning_mask
= WARN_TOTAL
;
6980 static void init_registers()
6982 #ifdef LONG_FOR_TIME_T
6984 #else /* not LONG_FOR_TIME_T */
6986 #endif /* not LONG_FOR_TIME_T */
6988 // Use struct here to work around misfeature in old versions of g++.
6989 struct tm
*tt
= localtime(&t
);
6990 set_number_reg("seconds", int(tt
->tm_sec
));
6991 set_number_reg("minutes", int(tt
->tm_min
));
6992 set_number_reg("hours", int(tt
->tm_hour
));
6993 set_number_reg("dw", int(tt
->tm_wday
+ 1));
6994 set_number_reg("dy", int(tt
->tm_mday
));
6995 set_number_reg("mo", int(tt
->tm_mon
+ 1));
6996 set_number_reg("year", int(1900 + tt
->tm_year
));
6997 set_number_reg("yr", int(tt
->tm_year
));
6998 set_number_reg("$$", getpid());
6999 number_reg_dictionary
.define(".A",
7000 new constant_reg(ascii_output_flag
7006 * registers associated with \O
7009 static int output_reg_minx_contents
= -1;
7010 static int output_reg_miny_contents
= -1;
7011 static int output_reg_maxx_contents
= -1;
7012 static int output_reg_maxy_contents
= -1;
7014 void check_output_limits(int x
, int y
)
7016 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7017 output_reg_minx_contents
= x
;
7018 if (x
> output_reg_maxx_contents
)
7019 output_reg_maxx_contents
= x
;
7020 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7021 output_reg_miny_contents
= y
;
7022 if (y
> output_reg_maxy_contents
)
7023 output_reg_maxy_contents
= y
;
7026 void reset_output_registers(int miny
)
7028 // fprintf(stderr, "reset_output_registers\n");
7029 output_reg_minx_contents
= -1;
7030 output_reg_miny_contents
= -1;
7031 output_reg_maxx_contents
= -1;
7032 output_reg_maxy_contents
= -1;
7035 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7037 *minx
= output_reg_minx_contents
;
7038 *miny
= output_reg_miny_contents
;
7039 *maxx
= output_reg_maxx_contents
;
7040 *maxy
= output_reg_maxy_contents
;
7043 void init_input_requests()
7045 init_request("ab", abort_request
);
7046 init_request("als", alias_macro
);
7047 init_request("am", append_macro
);
7048 init_request("am1", append_nocomp_macro
);
7049 init_request("ami", append_indirect_macro
);
7050 init_request("as", append_string
);
7051 init_request("as1", append_nocomp_string
);
7052 init_request("asciify", asciify_macro
);
7053 init_request("backtrace", backtrace_request
);
7054 init_request("blm", blank_line_macro
);
7055 init_request("break", while_break_request
);
7056 init_request("cf", copy_file
);
7057 init_request("cflags", char_flags
);
7058 init_request("char", define_character
);
7059 init_request("chop", chop_macro
);
7060 init_request("close", close_request
);
7061 init_request("color", activate_color
);
7062 init_request("continue", while_continue_request
);
7063 init_request("cp", compatible
);
7064 init_request("de", define_macro
);
7065 init_request("de1", define_nocomp_macro
);
7066 init_request("defcolor", define_color
);
7067 init_request("dei", define_indirect_macro
);
7068 init_request("do", do_request
);
7069 init_request("ds", define_string
);
7070 init_request("ds1", define_nocomp_string
);
7071 init_request("ec", set_escape_char
);
7072 init_request("ecr", restore_escape_char
);
7073 init_request("ecs", save_escape_char
);
7074 init_request("el", else_request
);
7075 init_request("em", end_macro
);
7076 init_request("eo", escape_off
);
7077 init_request("ex", exit_request
);
7078 init_request("fchar", define_fallback_character
);
7079 #ifdef WIDOW_CONTROL
7080 init_request("fpl", flush_pending_lines
);
7081 #endif /* WIDOW_CONTROL */
7082 init_request("hcode", hyphenation_code
);
7083 init_request("hpfcode", hyphenation_patterns_file_code
);
7084 init_request("ie", if_else_request
);
7085 init_request("if", if_request
);
7086 init_request("ig", ignore
);
7087 init_request("length", length_request
);
7088 init_request("lf", line_file
);
7089 init_request("mso", macro_source
);
7090 init_request("nop", nop_request
);
7091 init_request("nx", next_file
);
7092 init_request("open", open_request
);
7093 init_request("opena", opena_request
);
7094 init_request("output", output_request
);
7095 init_request("pc", set_page_character
);
7096 init_request("pi", pipe_output
);
7097 init_request("pm", print_macros
);
7098 init_request("psbb", ps_bbox_request
);
7099 #ifndef POPEN_MISSING
7100 init_request("pso", pipe_source
);
7101 #endif /* not POPEN_MISSING */
7102 init_request("rchar", remove_character
);
7103 init_request("rd", read_request
);
7104 init_request("return", return_macro_request
);
7105 init_request("rm", remove_macro
);
7106 init_request("rn", rename_macro
);
7107 init_request("shift", shift
);
7108 init_request("so", source
);
7109 init_request("spreadwarn", spreadwarn_request
);
7110 init_request("substring", substring_request
);
7111 init_request("sy", system_request
);
7112 init_request("tm", terminal
);
7113 init_request("tm1", terminal1
);
7114 init_request("tmc", terminal_continue
);
7115 init_request("tr", translate
);
7116 init_request("trf", transparent_file
);
7117 init_request("trin", translate_input
);
7118 init_request("trnt", translate_no_transparent
);
7119 init_request("unformat", unformat_macro
);
7120 init_request("warn", warn_request
);
7121 init_request("while", while_request
);
7122 init_request("write", write_request
);
7123 init_request("writec", write_request_continue
);
7124 init_request("writem", write_macro_request
);
7125 init_request("nroff", nroff_request
);
7126 init_request("troff", troff_request
);
7128 init_request("vj", vjustify
);
7130 init_request("warnscale", warnscale_request
);
7131 number_reg_dictionary
.define(".$", new nargs_reg
);
7132 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7133 number_reg_dictionary
.define(".F", new filename_reg
);
7134 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7135 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7136 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7137 extern const char *revision
;
7138 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7139 number_reg_dictionary
.define(".c", new lineno_reg
);
7140 number_reg_dictionary
.define(".g", new constant_reg("1"));
7141 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7142 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7143 extern const char *major_version
;
7144 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7145 extern const char *minor_version
;
7146 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7147 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7148 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7149 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7150 number_reg_dictionary
.define("opmaxx",
7151 new variable_reg(&output_reg_maxx_contents
));
7152 number_reg_dictionary
.define("opmaxy",
7153 new variable_reg(&output_reg_maxy_contents
));
7154 number_reg_dictionary
.define("opminx",
7155 new variable_reg(&output_reg_minx_contents
));
7156 number_reg_dictionary
.define("opminy",
7157 new variable_reg(&output_reg_miny_contents
));
7158 number_reg_dictionary
.define("slimit",
7159 new variable_reg(&input_stack::limit
));
7160 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7161 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7162 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7165 object_dictionary
request_dictionary(501);
7167 void init_request(const char *s
, REQUEST_FUNCP f
)
7169 request_dictionary
.define(s
, new request(f
));
7172 static request_or_macro
*lookup_request(symbol nm
)
7174 assert(!nm
.is_null());
7175 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7177 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
7179 request_dictionary
.define(nm
, p
);
7184 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7186 // Don't interpret character definitions in compatible mode.
7187 int old_compatible_flag
= compatible_flag
;
7188 compatible_flag
= 0;
7189 int old_escape_char
= escape_char
;
7191 macro
*mac
= ci
->set_macro(0);
7193 environment
*oldenv
= curenv
;
7194 environment
env(envp
);
7196 curenv
->set_composite();
7197 token old_tok
= tok
;
7198 input_stack::add_boundary();
7199 string_iterator
*si
=
7200 new string_iterator(*mac
, "composite character", ci
->nm
);
7201 input_stack::push(si
);
7202 // we don't use process_input_stack, because we don't want to recognise
7208 if (tok
.newline()) {
7209 error("composite character mustn't contain newline");
7217 node
*n
= curenv
->extract_output_line();
7218 input_stack::remove_boundary();
7222 compatible_flag
= old_compatible_flag
;
7223 escape_char
= old_escape_char
;
7227 static node
*read_draw_node()
7231 if (!start
.delimiter(1)){
7234 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7239 error("missing argument");
7241 unsigned char type
= tok
.ch();
7244 hvpair
*point
= new hvpair
[maxpoints
];
7249 for (i
= 0; tok
!= start
; i
++) {
7250 if (i
== maxpoints
) {
7251 hvpair
*oldpoint
= point
;
7252 point
= new hvpair
[maxpoints
*2];
7253 for (int j
= 0; j
< maxpoints
; j
++)
7254 point
[j
] = oldpoint
[j
];
7258 if (!get_hunits(&point
[i
].h
,
7259 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7270 if (!get_vunits(&point
[i
].v
, 'v')) {
7276 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7281 if (npoints
!= 1 || no_last_v
) {
7282 error("two arguments needed for line");
7287 if (npoints
!= 1 || !no_last_v
) {
7288 error("one argument needed for circle");
7294 if (npoints
!= 1 || no_last_v
) {
7295 error("two arguments needed for ellipse");
7300 if (npoints
!= 2 || no_last_v
) {
7301 error("four arguments needed for arc");
7307 error("even number of arguments needed for spline");
7310 if (npoints
!= 1 || !no_last_v
) {
7311 error("one argument needed for gray shade");
7316 // silently pass it through
7319 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7320 curenv
->get_font_size());
7335 } warning_table
[] = {
7336 { "char", WARN_CHAR
},
7337 { "range", WARN_RANGE
},
7338 { "break", WARN_BREAK
},
7339 { "delim", WARN_DELIM
},
7341 { "scale", WARN_SCALE
},
7342 { "number", WARN_NUMBER
},
7343 { "syntax", WARN_SYNTAX
},
7344 { "tab", WARN_TAB
},
7345 { "right-brace", WARN_RIGHT_BRACE
},
7346 { "missing", WARN_MISSING
},
7347 { "input", WARN_INPUT
},
7348 { "escape", WARN_ESCAPE
},
7349 { "space", WARN_SPACE
},
7350 { "font", WARN_FONT
},
7352 { "mac", WARN_MAC
},
7353 { "reg", WARN_REG
},
7355 { "color", WARN_COLOR
},
7356 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7357 { "w", WARN_TOTAL
},
7358 { "default", DEFAULT_WARNING_MASK
},
7361 static int lookup_warning(const char *name
)
7363 for (unsigned int i
= 0;
7364 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7366 if (strcmp(name
, warning_table
[i
].name
) == 0)
7367 return warning_table
[i
].mask
;
7371 static void enable_warning(const char *name
)
7373 int mask
= lookup_warning(name
);
7375 warning_mask
|= mask
;
7377 error("unknown warning `%1'", name
);
7380 static void disable_warning(const char *name
)
7382 int mask
= lookup_warning(name
);
7384 warning_mask
&= ~mask
;
7386 error("unknown warning `%1'", name
);
7389 static void copy_mode_error(const char *format
,
7395 static const char prefix
[] = "(in ignored input) ";
7396 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7399 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7403 error(format
, arg1
, arg2
, arg3
);
7406 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7408 static void do_error(error_type type
,
7414 const char *filename
;
7416 if (inhibit_errors
&& type
< FATAL
)
7419 input_stack::backtrace();
7420 if (!get_file_line(&filename
, &lineno
))
7423 errprint("%1:%2: ", filename
, lineno
);
7424 else if (program_name
)
7425 fprintf(stderr
, "%s: ", program_name
);
7428 fputs("fatal error: ", stderr
);
7433 fputs("warning: ", stderr
);
7435 case OUTPUT_WARNING
:
7436 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7437 fprintf(stderr
, "warning [p %d, %.1f%c",
7438 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7439 if (topdiv
!= curdiv
) {
7440 double fromtop1
= curdiv
->get_vertical_position().to_units()
7442 fprintf(stderr
, ", div `%s', %.1f%c",
7443 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7445 fprintf(stderr
, "]: ");
7448 errprint(format
, arg1
, arg2
, arg3
);
7449 fputc('\n', stderr
);
7452 cleanup_and_exit(1);
7455 int warning(warning_type t
,
7461 if ((t
& warning_mask
) != 0) {
7462 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
7469 int output_warning(warning_type t
,
7475 if ((t
& warning_mask
) != 0) {
7476 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
7483 void error(const char *format
,
7488 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
7491 void fatal(const char *format
,
7496 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
7499 void fatal_with_file_and_line(const char *filename
, int lineno
,
7505 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
7506 errprint(format
, arg1
, arg2
, arg3
);
7507 fputc('\n', stderr
);
7509 cleanup_and_exit(1);
7512 void error_with_file_and_line(const char *filename
, int lineno
,
7518 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
7519 errprint(format
, arg1
, arg2
, arg3
);
7520 fputc('\n', stderr
);
7524 dictionary
charinfo_dictionary(501);
7526 charinfo
*get_charinfo(symbol nm
)
7528 void *p
= charinfo_dictionary
.lookup(nm
);
7530 return (charinfo
*)p
;
7531 charinfo
*cp
= new charinfo(nm
);
7532 (void)charinfo_dictionary
.lookup(nm
, cp
);
7536 int charinfo::next_index
= 0;
7538 charinfo::charinfo(symbol s
)
7539 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
7540 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7541 not_found(0), transparent_translate(1), translate_input(0),
7544 index
= next_index
++;
7547 void charinfo::set_hyphenation_code(unsigned char c
)
7549 hyphenation_code
= c
;
7552 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
7556 if (hyphenation_code
!= 0)
7557 ci
->set_hyphenation_code(hyphenation_code
);
7558 if (asciify_code
!= 0)
7559 ci
->set_asciify_code(asciify_code
);
7560 else if (ascii_code
!= 0)
7561 ci
->set_asciify_code(ascii_code
);
7562 ci
->set_translation_input();
7564 special_translation
= TRANSLATE_NONE
;
7565 transparent_translate
= tt
;
7568 void charinfo::set_special_translation(int c
, int tt
)
7570 special_translation
= c
;
7572 transparent_translate
= tt
;
7575 void charinfo::set_ascii_code(unsigned char c
)
7580 void charinfo::set_asciify_code(unsigned char c
)
7585 macro
*charinfo::set_macro(macro
*m
, int f
)
7593 void charinfo::set_number(int n
)
7599 int charinfo::get_number()
7601 assert(flags
& NUMBERED
);
7605 symbol
UNNAMED_SYMBOL("---");
7607 // For numbered characters not between 0 and 255, we make a symbol out
7608 // of the number and store them in this dictionary.
7610 dictionary
numbered_charinfo_dictionary(11);
7612 charinfo
*get_charinfo_by_number(int n
)
7614 static charinfo
*number_table
[256];
7616 if (n
>= 0 && n
< 256) {
7617 charinfo
*ci
= number_table
[n
];
7619 ci
= new charinfo(UNNAMED_SYMBOL
);
7621 number_table
[n
] = ci
;
7626 symbol
ns(i_to_a(n
));
7627 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
7629 ci
= new charinfo(UNNAMED_SYMBOL
);
7631 numbered_charinfo_dictionary
.lookup(ns
, ci
);
7637 int font::name_to_index(const char *nm
)
7641 ci
= charset_table
[nm
[0] & 0xff];
7642 else if (nm
[0] == '\\' && nm
[2] == 0)
7643 ci
= get_charinfo(symbol(nm
+ 1));
7645 ci
= get_charinfo(symbol(nm
));
7649 return ci
->get_index();
7652 int font::number_to_index(int n
)
7654 return get_charinfo_by_number(n
)->get_index();