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().
44 #ifdef NEED_DECLARATION_PUTENV
46 int putenv(const char *);
48 #endif /* NEED_DECLARATION_PUTENV */
53 #else /* not ISATTY_MISSING */
58 #endif /* not isatty */
59 #endif /* not ISATTY_MISSING */
61 #define MACRO_PREFIX "tmac."
62 #define MACRO_POSTFIX ".tmac"
63 #define INITIAL_STARTUP_FILE "troffrc"
64 #define FINAL_STARTUP_FILE "troffrc-end"
65 #define DEFAULT_INPUT_STACK_LIMIT 1000
67 #ifndef DEFAULT_WARNING_MASK
68 // warnings that are enabled by default
69 #define DEFAULT_WARNING_MASK \
70 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
73 // initial size of buffer for reading names; expanded as necessary
76 extern "C" const char *Version_string
;
79 void init_column_requests();
82 static node
*read_draw_node();
83 void handle_first_page_transition();
84 static void push_token(const token
&);
90 void transparent_file();
91 void process_input_stack();
93 const char *program_name
= 0;
96 int disable_color_flag
= 0;
97 static int backtrace_flag
= 0;
99 char *pipe_command
= 0;
101 charinfo
*charset_table
[256];
102 unsigned char hpf_code_table
[256];
104 static int warning_mask
= DEFAULT_WARNING_MASK
;
105 static int inhibit_errors
= 0;
106 static int ignoring
= 0;
108 static void enable_warning(const char *);
109 static void disable_warning(const char *);
111 static int escape_char
= '\\';
112 static symbol end_macro_name
;
113 static symbol blank_line_macro_name
;
114 static int compatible_flag
= 0;
115 int ascii_output_flag
= 0;
116 int suppress_output_flag
= 0;
118 int begin_level
= 0; // number of nested .begin requests
120 int have_input
= 0; // whether \f, \H, \R, \s, or \S has
121 // been processed in token::next()
122 int tcommand_flag
= 0;
123 int safer_flag
= 1; // safer by default
125 double spread_limit
= -3.0 - 1.0; // negative means deactivated
128 char warn_scaling_indicator
;
130 search_path
*mac_path
= &safer_macro_path
;
132 static int get_copy(node
**, int = 0);
133 static void copy_mode_error(const char *,
134 const errarg
& = empty_errarg
,
135 const errarg
& = empty_errarg
,
136 const errarg
& = empty_errarg
);
138 static symbol
read_escape_name(int no_empty
= 1);
139 static symbol
read_long_escape_name(int no_empty
= 1);
140 static void interpolate_string(symbol
);
141 static void interpolate_macro(symbol
);
142 static void interpolate_number_format(symbol
);
143 static void interpolate_environment_variable(symbol
);
145 static void interpolate_arg(symbol
);
146 static request_or_macro
*lookup_request(symbol
);
147 static int get_delim_number(units
*, int);
148 static int get_delim_number(units
*, int, units
);
149 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
150 static int read_size(int *);
151 static symbol
get_delim_name();
152 static void init_registers();
153 static void trapping_blank_line();
155 struct input_iterator
;
156 input_iterator
*make_temp_iterator(const char *);
157 const char *input_char_description(int);
160 void set_escape_char()
164 error("bad escape character");
168 escape_char
= tok
.ch();
181 static int saved_escape_char
= '\\';
183 void save_escape_char()
185 saved_escape_char
= escape_char
;
189 void restore_escape_char()
191 escape_char
= saved_escape_char
;
195 class input_iterator
{
198 virtual ~input_iterator() {}
200 friend class input_stack
;
202 const unsigned char *ptr
;
203 const unsigned char *eptr
;
204 input_iterator
*next
;
206 virtual int fill(node
**);
208 virtual int has_args() { return 0; }
209 virtual int nargs() { return 0; }
210 virtual input_iterator
*get_arg(int) { return 0; }
211 virtual int get_location(int, const char **, int *) { return 0; }
212 virtual void backtrace() {}
213 virtual int set_location(const char *, int) { return 0; }
214 virtual int next_file(FILE *, const char *) { return 0; }
215 virtual void shift(int) {}
216 virtual int is_boundary() {return 0; }
217 virtual int internal_level() { return 0; }
218 virtual int is_file() { return 0; }
219 virtual int is_macro() { return 0; }
220 virtual void save_compatible_flag(int) {}
221 virtual int get_compatible_flag() { return 0; }
224 input_iterator::input_iterator()
229 int input_iterator::fill(node
**)
234 int input_iterator::peek()
239 inline int input_iterator::get(node
**p
)
241 return ptr
< eptr
? *ptr
++ : fill(p
);
244 class input_boundary
: public input_iterator
{
246 int is_boundary() { return 1; }
249 class input_return_boundary
: public input_iterator
{
251 int is_boundary() { return 2; }
254 class file_iterator
: public input_iterator
{
257 const char *filename
;
260 int suppress_newline_flag
; // used by html
262 enum { BUF_SIZE
= 512 };
263 unsigned char buf
[BUF_SIZE
];
266 file_iterator(FILE *, const char *, int = 0);
270 int get_location(int, const char **, int *);
272 int set_location(const char *, int);
273 int next_file(FILE *, const char *);
277 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
278 : fp(f
), lineno(1), filename(fn
), popened(po
),
279 newline_flag(0), suppress_newline_flag(0), seen_escape(0)
281 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
284 the_output
->put_filename(fn
);
288 file_iterator::~file_iterator()
293 void file_iterator::close()
297 #ifndef POPEN_MISSING
300 #endif /* not POPEN_MISSING */
305 int file_iterator::is_file()
310 int file_iterator::next_file(FILE *f
, const char *s
)
317 suppress_newline_flag
= 0;
325 int file_iterator::fill(node
**)
327 if (newline_flag
&& !suppress_newline_flag
) {
328 curenv
->add_html_tag_eol();
332 suppress_newline_flag
= 0;
333 unsigned char *p
= buf
;
335 unsigned char *e
= p
+ BUF_SIZE
;
340 if (invalid_input_char(c
))
341 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
345 if (seen_escape
&& is_html
)
346 suppress_newline_flag
= 1;
351 seen_escape
= (c
== '\\');
364 int file_iterator::peek()
367 while (invalid_input_char(c
)) {
368 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
376 int file_iterator::get_location(int /*allow_macro*/,
377 const char **filenamep
, int *linenop
)
380 if (filename
!= 0 && strcmp(filename
, "-") == 0)
381 *filenamep
= "<standard input>";
383 *filenamep
= filename
;
387 void file_iterator::backtrace()
389 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
390 popened
? "process" : "file");
393 int file_iterator::set_location(const char *f
, int ln
)
399 the_output
->put_filename(f
);
405 input_iterator nil_iterator
;
409 static int get(node
**);
411 static void push(input_iterator
*);
412 static input_iterator
*get_arg(int);
414 static int get_location(int, const char **, int *);
415 static int set_location(const char *, int);
416 static void backtrace();
417 static void backtrace_all();
418 static void next_file(FILE *, const char *);
419 static void end_file();
420 static void shift(int n
);
421 static void add_boundary();
422 static void add_return_boundary();
423 static int is_return_boundary();
424 static void remove_boundary();
425 static int get_level();
427 static void pop_macro();
428 static void save_compatible_flag(int);
429 static int get_compatible_flag();
433 static input_iterator
*top
;
436 static int finish_get(node
**);
437 static int finish_peek();
440 input_iterator
*input_stack::top
= &nil_iterator
;
441 int input_stack::level
= 0;
442 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
444 inline int input_stack::get_level()
446 return level
+ top
->internal_level();
449 inline int input_stack::get(node
**np
)
451 return (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
454 int input_stack::finish_get(node
**np
)
457 int c
= top
->fill(np
);
458 if (c
!= EOF
|| top
->is_boundary())
460 if (top
== &nil_iterator
)
462 input_iterator
*tem
= top
;
466 if (top
->ptr
< top
->eptr
)
473 inline int input_stack::peek()
475 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
478 int input_stack::finish_peek()
482 if (c
!= EOF
|| top
->is_boundary())
484 if (top
== &nil_iterator
)
486 input_iterator
*tem
= top
;
490 if (top
->ptr
< top
->eptr
)
497 void input_stack::add_boundary()
499 push(new input_boundary
);
502 void input_stack::add_return_boundary()
504 push(new input_return_boundary
);
507 int input_stack::is_return_boundary()
509 return top
->is_boundary() == 2;
512 void input_stack::remove_boundary()
514 assert(top
->is_boundary());
515 input_iterator
*temp
= top
->next
;
521 void input_stack::push(input_iterator
*in
)
525 if (++level
> limit
&& limit
> 0)
526 fatal("input stack limit exceeded (probable infinite loop)");
531 input_iterator
*input_stack::get_arg(int i
)
534 for (p
= top
; p
!= 0; p
= p
->next
)
536 return p
->get_arg(i
);
540 void input_stack::shift(int n
)
542 for (input_iterator
*p
= top
; p
; p
= p
->next
)
549 int input_stack::nargs()
551 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
557 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
559 for (input_iterator
*p
= top
; p
; p
= p
->next
)
560 if (p
->get_location(allow_macro
, filenamep
, linenop
))
565 void input_stack::backtrace()
569 // only backtrace down to (not including) the topmost file
570 for (input_iterator
*p
= top
;
571 p
&& !p
->get_location(0, &f
, &n
);
576 void input_stack::backtrace_all()
578 for (input_iterator
*p
= top
; p
; p
= p
->next
)
582 int input_stack::set_location(const char *filename
, int lineno
)
584 for (input_iterator
*p
= top
; p
; p
= p
->next
)
585 if (p
->set_location(filename
, lineno
))
590 void input_stack::next_file(FILE *fp
, const char *s
)
593 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
594 if ((*pp
)->next_file(fp
, s
))
596 if (++level
> limit
&& limit
> 0)
597 fatal("input stack limit exceeded");
598 *pp
= new file_iterator(fp
, s
);
599 (*pp
)->next
= &nil_iterator
;
602 void input_stack::end_file()
604 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
605 if ((*pp
)->is_file()) {
606 input_iterator
*tem
= *pp
;
614 void input_stack::clear()
617 while (top
!= &nil_iterator
) {
618 if (top
->is_boundary())
620 input_iterator
*tem
= top
;
625 // Keep while_request happy.
626 for (; nboundaries
> 0; --nboundaries
)
627 add_return_boundary();
630 void input_stack::pop_macro()
635 if (top
->next
== &nil_iterator
)
637 if (top
->is_boundary())
639 is_macro
= top
->is_macro();
640 input_iterator
*tem
= top
;
645 // Keep while_request happy.
646 for (; nboundaries
> 0; --nboundaries
)
647 add_return_boundary();
650 inline void input_stack::save_compatible_flag(int f
)
652 top
->save_compatible_flag(f
);
655 inline int input_stack::get_compatible_flag()
657 return top
->get_compatible_flag();
660 void backtrace_request()
662 input_stack::backtrace_all();
669 symbol nm
= get_long_name(0);
670 while (!tok
.newline() && !tok
.eof())
673 input_stack::end_file();
676 FILE *fp
= fopen(nm
.contents(), "r");
678 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
680 input_stack::next_file(fp
, nm
.contents());
688 if (!has_arg() || !get_integer(&n
))
690 input_stack::shift(n
);
694 static int get_char_for_escape_name()
699 copy_mode_error("end of input in escape name");
702 if (!invalid_input_char(c
))
707 input_stack::push(make_temp_iterator("\n"));
712 copy_mode_error("%1 is not allowed in an escape name",
713 input_char_description(c
));
719 static symbol
read_two_char_escape_name()
722 buf
[0] = get_char_for_escape_name();
723 if (buf
[0] != '\0') {
724 buf
[1] = get_char_for_escape_name();
733 static symbol
read_long_escape_name(int no_empty
)
735 int start_level
= input_stack::get_level();
736 char abuf
[ABUF_SIZE
];
738 int buf_size
= ABUF_SIZE
;
741 int c
= get_char_for_escape_name();
747 if (i
+ 2 > buf_size
) {
749 buf
= new char[ABUF_SIZE
*2];
750 memcpy(buf
, abuf
, buf_size
);
751 buf_size
= ABUF_SIZE
*2;
755 buf
= new char[buf_size
*2];
756 memcpy(buf
, old_buf
, buf_size
);
761 if (c
== ']' && input_stack::get_level() == start_level
)
769 copy_mode_error("empty escape name");
781 static symbol
read_escape_name(int no_empty
)
783 int c
= get_char_for_escape_name();
787 return read_two_char_escape_name();
788 if (c
== '[' && !compatible_flag
)
789 return read_long_escape_name(no_empty
);
796 static symbol
read_increment_and_escape_name(int *incp
)
798 int c
= get_char_for_escape_name();
805 return read_two_char_escape_name();
808 return read_escape_name();
811 return read_escape_name();
813 if (!compatible_flag
) {
815 return read_long_escape_name();
826 static int get_copy(node
**nd
, int defining
)
829 int c
= input_stack::get(nd
);
830 if (c
== ESCAPE_NEWLINE
) {
834 c
= input_stack::get(nd
);
835 } while (c
== ESCAPE_NEWLINE
);
837 if (c
!= escape_char
|| escape_char
<= 0)
839 c
= input_stack::peek();
844 (void)input_stack::get(0);
845 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
848 case '#': // Like \" but newline is ignored.
849 (void)input_stack::get(0);
850 while ((c
= input_stack::get(0)) != '\n')
856 (void)input_stack::get(0);
857 symbol s
= read_escape_name();
858 if (!(s
.is_null() || s
.is_empty()))
864 (void)input_stack::get(0);
865 symbol s
= read_escape_name();
866 if (!(s
.is_null() || s
.is_empty()))
867 interpolate_string(s
);
871 (void)input_stack::get(0);
874 (void)input_stack::get(0);
877 (void)input_stack::get(0);
881 (void)input_stack::get(0);
883 symbol s
= read_increment_and_escape_name(&inc
);
884 if (!(s
.is_null() || s
.is_empty()))
885 interpolate_number_reg(s
, inc
);
890 (void)input_stack::get(0);
891 symbol s
= read_escape_name();
892 if (!(s
.is_null() || s
.is_empty()))
893 interpolate_number_format(s
);
897 (void)input_stack::get(0);
901 (void)input_stack::get(0);
902 symbol s
= read_escape_name();
903 if (!(s
.is_null() || s
.is_empty()))
904 interpolate_environment_variable(s
);
908 (void)input_stack::get(0);
910 return ESCAPE_NEWLINE
;
913 (void)input_stack::get(0);
916 (void)input_stack::get(0);
919 (void)input_stack::get(0);
922 (void)input_stack::get(0);
925 (void)input_stack::get(0);
926 return ESCAPE_CIRCUMFLEX
;
928 (void)input_stack::get(0);
929 return ESCAPE_LEFT_BRACE
;
931 (void)input_stack::get(0);
932 return ESCAPE_RIGHT_BRACE
;
934 (void)input_stack::get(0);
935 return ESCAPE_LEFT_QUOTE
;
937 (void)input_stack::get(0);
938 return ESCAPE_RIGHT_QUOTE
;
940 (void)input_stack::get(0);
941 return ESCAPE_HYPHEN
;
943 (void)input_stack::get(0);
944 return ESCAPE_UNDERSCORE
;
946 (void)input_stack::get(0);
949 (void)input_stack::get(0);
952 (void)input_stack::get(0);
953 return ESCAPE_QUESTION
;
955 (void)input_stack::get(0);
956 return ESCAPE_AMPERSAND
;
958 (void)input_stack::get(0);
959 return ESCAPE_RIGHT_PARENTHESIS
;
961 (void)input_stack::get(0);
964 (void)input_stack::get(0);
965 return ESCAPE_PERCENT
;
967 if (c
== escape_char
) {
968 (void)input_stack::get(0);
977 class non_interpreted_char_node
: public node
{
980 non_interpreted_char_node(unsigned char);
982 int interpret(macro
*);
988 int non_interpreted_char_node::same(node
*nd
)
990 return c
== ((non_interpreted_char_node
*)nd
)->c
;
993 const char *non_interpreted_char_node::type()
995 return "non_interpreted_char_node";
998 int non_interpreted_char_node::force_tprint()
1003 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1008 node
*non_interpreted_char_node::copy()
1010 return new non_interpreted_char_node(c
);
1013 int non_interpreted_char_node::interpret(macro
*mac
)
1019 static void do_width();
1020 static node
*do_non_interpreted();
1021 static node
*do_special();
1022 static node
*do_suppress(symbol nm
);
1023 static void do_register();
1025 dictionary
color_dictionary(501);
1026 static symbol
default_symbol("default");
1028 static color
*lookup_color(symbol nm
)
1030 assert(!nm
.is_null());
1031 if (nm
== default_symbol
)
1032 return &default_color
;
1033 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1035 warning(WARN_COLOR
, "`%1' not defined", nm
.contents());
1039 static node
*do_glyph_color(symbol nm
)
1044 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1046 color
*tem
= lookup_color(nm
);
1048 curenv
->set_glyph_color(tem
);
1050 (void)color_dictionary
.lookup(nm
, new color
);
1052 return new glyph_color_node(curenv
->get_glyph_color());
1055 static node
*do_fill_color(symbol nm
)
1060 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1062 color
*tem
= lookup_color(nm
);
1064 curenv
->set_fill_color(tem
);
1066 (void)color_dictionary
.lookup(nm
, new color
);
1068 return new fill_color_node(curenv
->get_fill_color());
1071 static unsigned int get_color_element(const char *scheme
, const char *col
)
1074 if (!get_number(&val
, 'f')) {
1075 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1080 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1083 if (val
> color::MAX_COLOR_VAL
+1) {
1084 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1085 // we change 0x10000 to 0xffff
1086 return color::MAX_COLOR_VAL
;
1088 return (unsigned int)val
;
1091 static color
*read_rgb()
1093 symbol component
= get_long_name(0);
1094 if (component
.is_null()) {
1095 warning(WARN_COLOR
, "missing rgb color values");
1098 const char *s
= component
.contents();
1099 color
*col
= new color
;
1101 if (!col
->read_rgb(s
)) {
1102 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1108 input_stack::push(make_temp_iterator(" "));
1109 input_stack::push(make_temp_iterator(s
));
1110 unsigned int r
= get_color_element("rgb color", "red component");
1111 unsigned int g
= get_color_element("rgb color", "green component");
1112 unsigned int b
= get_color_element("rgb color", "blue component");
1113 col
->set_rgb(r
, g
, b
);
1118 static color
*read_cmy()
1120 symbol component
= get_long_name(0);
1121 if (component
.is_null()) {
1122 warning(WARN_COLOR
, "missing cmy color values");
1125 const char *s
= component
.contents();
1126 color
*col
= new color
;
1128 if (!col
->read_cmy(s
)) {
1129 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1135 input_stack::push(make_temp_iterator(" "));
1136 input_stack::push(make_temp_iterator(s
));
1137 unsigned int c
= get_color_element("cmy color", "cyan component");
1138 unsigned int m
= get_color_element("cmy color", "magenta component");
1139 unsigned int y
= get_color_element("cmy color", "yellow component");
1140 col
->set_cmy(c
, m
, y
);
1145 static color
*read_cmyk()
1147 symbol component
= get_long_name(0);
1148 if (component
.is_null()) {
1149 warning(WARN_COLOR
, "missing cmyk color values");
1152 const char *s
= component
.contents();
1153 color
*col
= new color
;
1155 if (!col
->read_cmyk(s
)) {
1156 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1162 input_stack::push(make_temp_iterator(" "));
1163 input_stack::push(make_temp_iterator(s
));
1164 unsigned int c
= get_color_element("cmyk color", "cyan component");
1165 unsigned int m
= get_color_element("cmyk color", "magenta component");
1166 unsigned int y
= get_color_element("cmyk color", "yellow component");
1167 unsigned int k
= get_color_element("cmyk color", "black component");
1168 col
->set_cmyk(c
, m
, y
, k
);
1173 static color
*read_gray()
1175 symbol component
= get_long_name(0);
1176 if (component
.is_null()) {
1177 warning(WARN_COLOR
, "missing gray values");
1180 const char *s
= component
.contents();
1181 color
*col
= new color
;
1183 if (!col
->read_gray(s
)) {
1184 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1190 input_stack::push(make_temp_iterator(" "));
1191 input_stack::push(make_temp_iterator(s
));
1192 unsigned int g
= get_color_element("gray", "gray value");
1198 static void define_color()
1200 symbol color_name
= get_long_name(1);
1201 if (color_name
.is_null()) {
1205 if (color_name
== default_symbol
) {
1206 warning(WARN_COLOR
, "default color can't be redefined");
1210 symbol style
= get_long_name(1);
1211 if (style
.is_null()) {
1216 if (strcmp(style
.contents(), "rgb") == 0)
1218 else if (strcmp(style
.contents(), "cmyk") == 0)
1220 else if (strcmp(style
.contents(), "gray") == 0)
1222 else if (strcmp(style
.contents(), "grey") == 0)
1224 else if (strcmp(style
.contents(), "cmy") == 0)
1228 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1234 (void)color_dictionary
.lookup(color_name
, col
);
1238 static node
*do_overstrike()
1241 overstrike_node
*on
= new overstrike_node
;
1242 int start_level
= input_stack::get_level();
1246 if (tok
.newline() || tok
.eof()) {
1247 warning(WARN_DELIM
, "missing closing delimiter");
1251 && (compatible_flag
|| input_stack::get_level() == start_level
))
1253 charinfo
*ci
= tok
.get_char(1);
1255 node
*n
= curenv
->make_char_node(ci
);
1263 static node
*do_bracket()
1266 bracket_node
*bn
= new bracket_node
;
1268 int start_level
= input_stack::get_level();
1272 warning(WARN_DELIM
, "missing closing delimiter");
1275 if (tok
.newline()) {
1276 warning(WARN_DELIM
, "missing closing delimiter");
1277 input_stack::push(make_temp_iterator("\n"));
1281 && (compatible_flag
|| input_stack::get_level() == start_level
))
1283 charinfo
*ci
= tok
.get_char(1);
1285 node
*n
= curenv
->make_char_node(ci
);
1293 static int do_name_test()
1297 int start_level
= input_stack::get_level();
1302 if (tok
.newline() || tok
.eof()) {
1303 warning(WARN_DELIM
, "missing closing delimiter");
1307 && (compatible_flag
|| input_stack::get_level() == start_level
))
1313 return some_char
&& !bad_char
;
1316 static int do_expr_test()
1320 int start_level
= input_stack::get_level();
1321 if (!start
.delimiter(1))
1324 // disable all warning and error messages temporarily
1325 int saved_warning_mask
= warning_mask
;
1326 int saved_inhibit_errors
= inhibit_errors
;
1330 int result
= get_number_rigidly(&dummy
, 'u');
1331 warning_mask
= saved_warning_mask
;
1332 inhibit_errors
= saved_inhibit_errors
;
1333 if (tok
== start
&& input_stack::get_level() == start_level
)
1335 // ignore everything up to the delimiter in case we aren't right there
1338 if (tok
.newline() || tok
.eof()) {
1339 warning(WARN_DELIM
, "missing closing delimiter");
1342 if (tok
== start
&& input_stack::get_level() == start_level
)
1349 static node
*do_zero_width()
1353 int start_level
= input_stack::get_level();
1354 environment
env(curenv
);
1355 environment
*oldenv
= curenv
;
1359 if (tok
.newline() || tok
.eof()) {
1360 error("missing closing delimiter");
1364 && (compatible_flag
|| input_stack::get_level() == start_level
))
1369 node
*rev
= env
.extract_output_line();
1377 return new zero_width_node(n
);
1382 // It's undesirable for \Z to change environments, because then
1383 // \n(.w won't work as expected.
1385 static node
*do_zero_width()
1387 node
*rev
= new dummy_node
;
1390 int start_level
= input_stack::get_level();
1393 if (tok
.newline() || tok
.eof()) {
1394 warning(WARN_DELIM
, "missing closing delimiter");
1398 && (compatible_flag
|| input_stack::get_level() == start_level
))
1400 if (!tok
.add_to_node_list(&rev
))
1401 error("invalid token in argument to \\Z");
1410 return new zero_width_node(n
);
1415 token_node
*node::get_token_node()
1420 class token_node
: public node
{
1423 token_node(const token
&t
);
1425 token_node
*get_token_node();
1431 token_node::token_node(const token
&t
) : tk(t
)
1435 node
*token_node::copy()
1437 return new token_node(tk
);
1440 token_node
*token_node::get_token_node()
1445 int token_node::same(node
*nd
)
1447 return tk
== ((token_node
*)nd
)->tk
;
1450 const char *token_node::type()
1452 return "token_node";
1455 int token_node::force_tprint()
1460 token::token() : nd(0), type(TOKEN_EMPTY
)
1469 token::token(const token
&t
)
1470 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1472 // Use two statements to work around bug in SGI C++.
1474 nd
= tem
? tem
->copy() : 0;
1477 void token::operator=(const token
&t
)
1481 // Use two statements to work around bug in SGI C++.
1483 nd
= tem
? tem
->copy() : 0;
1500 return !tok
.newline();
1503 void token::make_space()
1508 void token::make_newline()
1510 type
= TOKEN_NEWLINE
;
1522 int cc
= input_stack::get(&n
);
1523 if (cc
!= escape_char
|| escape_char
== 0) {
1526 case COMPATIBLE_SAVE
:
1527 input_stack::save_compatible_flag(compatible_flag
);
1528 compatible_flag
= 0;
1530 case COMPATIBLE_RESTORE
:
1531 compatible_flag
= input_stack::get_compatible_flag();
1536 case TRANSPARENT_FILE_REQUEST
:
1538 case COPY_FILE_REQUEST
:
1540 case VJUSTIFY_REQUEST
:
1542 type
= TOKEN_REQUEST
;
1546 type
= TOKEN_BEGIN_TRAP
;
1549 type
= TOKEN_END_TRAP
;
1551 case LAST_PAGE_EJECTOR
:
1552 seen_last_page_ejector
= 1;
1555 type
= TOKEN_PAGE_EJECTOR
;
1557 case ESCAPE_PERCENT
:
1559 type
= TOKEN_HYPHEN_INDICATOR
;
1564 nd
= new space_char_hmotion_node(curenv
->get_space_width());
1568 type
= TOKEN_STRETCHABLE_SPACE
;
1572 type
= TOKEN_ZERO_WIDTH_BREAK
;
1576 type
= TOKEN_ESCAPE
;
1579 goto handle_escape_char
;
1583 nd
= new hmotion_node(curenv
->get_narrow_space_width());
1585 case ESCAPE_CIRCUMFLEX
:
1588 nd
= new hmotion_node(curenv
->get_half_narrow_space_width());
1590 case ESCAPE_NEWLINE
:
1592 case ESCAPE_LEFT_BRACE
:
1594 type
= TOKEN_LEFT_BRACE
;
1596 case ESCAPE_RIGHT_BRACE
:
1598 type
= TOKEN_RIGHT_BRACE
;
1600 case ESCAPE_LEFT_QUOTE
:
1602 type
= TOKEN_SPECIAL
;
1605 case ESCAPE_RIGHT_QUOTE
:
1607 type
= TOKEN_SPECIAL
;
1612 type
= TOKEN_SPECIAL
;
1615 case ESCAPE_UNDERSCORE
:
1617 type
= TOKEN_SPECIAL
;
1622 type
= TOKEN_INTERRUPT
;
1626 type
= TOKEN_TRANSPARENT
;
1628 case ESCAPE_QUESTION
:
1630 nd
= do_non_interpreted();
1636 case ESCAPE_AMPERSAND
:
1640 case ESCAPE_RIGHT_PARENTHESIS
:
1641 ESCAPE_RIGHT_PARENTHESIS
:
1642 type
= TOKEN_TRANSPARENT_DUMMY
;
1645 type
= TOKEN_BACKSPACE
;
1654 type
= TOKEN_NEWLINE
;
1657 type
= TOKEN_LEADER
;
1662 token_node
*tn
= n
->get_token_node();
1681 cc
= input_stack::get(0);
1684 nm
= read_two_char_escape_name();
1685 type
= TOKEN_SPECIAL
;
1689 error("end of input after escape character");
1692 goto ESCAPE_LEFT_QUOTE
;
1694 goto ESCAPE_RIGHT_QUOTE
;
1698 goto ESCAPE_UNDERSCORE
;
1700 goto ESCAPE_PERCENT
;
1704 nd
= new hmotion_node(curenv
->get_digit_width());
1710 goto ESCAPE_CIRCUMFLEX
;
1712 type
= TOKEN_ITALIC_CORRECTION
;
1716 nd
= new left_italic_corrected_node
;
1719 goto ESCAPE_AMPERSAND
;
1721 goto ESCAPE_RIGHT_PARENTHESIS
;
1725 goto ESCAPE_QUESTION
;
1731 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1734 type
= TOKEN_NEWLINE
;
1738 case '#': // Like \" but newline is ignored.
1739 while ((cc
= input_stack::get(0)) != '\n')
1747 symbol nm
= read_escape_name();
1748 if (!(nm
.is_null() || nm
.is_empty()))
1749 interpolate_arg(nm
);
1754 symbol nm
= read_escape_name();
1755 if (!(nm
.is_null() || nm
.is_empty()))
1756 interpolate_string(nm
);
1760 nd
= new non_interpreted_char_node('\001');
1764 c
= '0' + do_name_test();
1772 c
= '0' + do_expr_test();
1778 nm
= get_delim_name();
1781 type
= TOKEN_SPECIAL
;
1785 nd
= new vmotion_node(curenv
->get_size()/2);
1788 nd
= read_draw_node();
1796 goto handle_escape_char
;
1799 symbol s
= read_escape_name(0);
1803 for (p
= s
.contents(); *p
!= '\0'; p
++)
1806 if (*p
|| s
.is_empty())
1807 curenv
->set_font(s
);
1809 curenv
->set_font(atoi(s
.contents()));
1810 if (!compatible_flag
)
1816 symbol s
= read_escape_name(0);
1819 curenv
->set_family(s
);
1824 symbol s
= read_escape_name();
1825 if (!(s
.is_null() || s
.is_empty()))
1826 interpolate_number_format(s
);
1830 if (!get_delim_number(&x
, 'm'))
1833 nd
= new hmotion_node(x
);
1836 // don't take height increments relative to previous height if
1837 // in compatibility mode
1838 if (!compatible_flag
&& curenv
->get_char_height())
1840 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
1841 curenv
->set_char_height(x
);
1845 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1846 curenv
->set_char_height(x
);
1848 if (!compatible_flag
)
1852 nm
= read_escape_name();
1853 if (nm
.is_null() || nm
.is_empty())
1855 type
= TOKEN_MARK_INPUT
;
1861 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1864 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1866 node
*n
= curenv
->make_char_node(s
);
1868 nd
= new hline_node(x
, n
);
1870 nd
= new vline_node(x
, n
);
1874 nd
= do_glyph_color(read_escape_name(0));
1880 nd
= do_fill_color(read_escape_name(0));
1888 symbol nm
= read_increment_and_escape_name(&inc
);
1889 if (!(nm
.is_null() || nm
.is_empty()))
1890 interpolate_number_reg(nm
, inc
);
1894 if (!get_delim_number(&val
, 0))
1896 type
= TOKEN_NUMBERED_CHAR
;
1899 nd
= do_overstrike();
1903 nd
= do_suppress(read_escape_name());
1909 type
= TOKEN_SPREAD
;
1913 nd
= new vmotion_node(-curenv
->get_size());
1917 if (!compatible_flag
)
1922 curenv
->set_size(x
);
1923 if (!compatible_flag
)
1927 if (get_delim_number(&x
, 0))
1928 curenv
->set_char_slant(x
);
1929 if (!compatible_flag
)
1934 nd
= new non_interpreted_char_node('\t');
1938 nd
= new vmotion_node(-curenv
->get_size()/2);
1941 if (!get_delim_number(&x
, 'v'))
1944 nd
= new vmotion_node(x
);
1948 symbol nm
= read_escape_name();
1949 if (!(nm
.is_null() || nm
.is_empty()))
1950 interpolate_environment_variable(nm
);
1957 if (!get_delim_number(&x
, 'v'))
1960 nd
= new extra_size_node(x
);
1970 symbol s
= read_escape_name();
1971 if (s
.is_null() || s
.is_empty())
1973 request_or_macro
*p
= lookup_request(s
);
1974 macro
*m
= p
->to_macro();
1976 error("can't transparently throughput a request");
1979 nd
= new special_node(*m
);
1986 if (type
== TOKEN_NODE
)
1987 nd
= new zero_width_node(nd
);
1989 charinfo
*ci
= get_char(1);
1992 node
*gn
= curenv
->make_char_node(ci
);
1995 nd
= new zero_width_node(gn
);
2001 nd
= do_zero_width();
2007 goto ESCAPE_LEFT_BRACE
;
2009 goto ESCAPE_RIGHT_BRACE
;
2013 if (!compatible_flag
) {
2014 nm
= read_long_escape_name();
2015 if (nm
.is_null() || nm
.is_empty())
2017 type
= TOKEN_SPECIAL
;
2020 goto handle_normal_char
;
2022 if (cc
!= escape_char
&& cc
!= '.')
2023 warning(WARN_ESCAPE
, "escape character ignored before %1",
2024 input_char_description(cc
));
2025 goto handle_normal_char
;
2031 int token::operator==(const token
&t
)
2040 case TOKEN_NUMBERED_CHAR
:
2041 return val
== t
.val
;
2047 int token::operator!=(const token
&t
)
2049 return !(*this == t
);
2052 // is token a suitable delimiter (like ')?
2054 int token::delimiter(int err
)
2083 error("cannot use character `%1' as a starting delimiter", char(c
));
2090 case TOKEN_STRETCHABLE_SPACE
:
2094 error("cannot use %1 as a starting delimiter", description());
2101 const char *token::description()
2105 case TOKEN_BACKSPACE
:
2106 return "a backspace character";
2117 case TOKEN_HYPHEN_INDICATOR
:
2119 case TOKEN_INTERRUPT
:
2121 case TOKEN_ITALIC_CORRECTION
:
2124 return "a leader character";
2125 case TOKEN_LEFT_BRACE
:
2127 case TOKEN_MARK_INPUT
:
2133 case TOKEN_NUMBERED_CHAR
:
2135 case TOKEN_RIGHT_BRACE
:
2140 return "a special character";
2143 case TOKEN_STRETCHABLE_SPACE
:
2146 return "a tab character";
2147 case TOKEN_TRANSPARENT
:
2149 case TOKEN_TRANSPARENT_DUMMY
:
2151 case TOKEN_ZERO_WIDTH_BREAK
:
2154 return "end of input";
2158 return "a magic token";
2163 while (!tok
.newline())
2174 if (has_arg() && get_integer(&n
))
2175 compatible_flag
= n
!= 0;
2177 compatible_flag
= 1;
2181 static void empty_name_warning(int required
)
2183 if (tok
.newline() || tok
.eof()) {
2185 warning(WARN_MISSING
, "missing name");
2187 else if (tok
.right_brace() || tok
.tab()) {
2188 const char *start
= tok
.description();
2191 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2192 if (!tok
.newline() && !tok
.eof())
2193 error("%1 is not allowed before an argument", start
);
2195 warning(WARN_MISSING
, "missing name");
2198 error("name expected (got %1)", tok
.description());
2200 error("name expected (got %1): treated as missing", tok
.description());
2203 static void non_empty_name_warning()
2205 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2206 && !tok
.right_brace()
2207 // We don't want to give a warning for .el\{
2208 && !tok
.left_brace())
2209 error("%1 is not allowed in a name", tok
.description());
2212 symbol
get_name(int required
)
2214 if (compatible_flag
) {
2217 if ((buf
[0] = tok
.ch()) != 0) {
2219 if ((buf
[1] = tok
.ch()) != 0) {
2224 non_empty_name_warning();
2228 empty_name_warning(required
);
2233 return get_long_name(required
);
2236 symbol
get_long_name(int required
)
2240 char abuf
[ABUF_SIZE
];
2242 int buf_size
= ABUF_SIZE
;
2245 if (i
+ 1 > buf_size
) {
2247 buf
= new char[ABUF_SIZE
*2];
2248 memcpy(buf
, abuf
, buf_size
);
2249 buf_size
= ABUF_SIZE
*2;
2252 char *old_buf
= buf
;
2253 buf
= new char[buf_size
*2];
2254 memcpy(buf
, old_buf
, buf_size
);
2259 if ((buf
[i
] = tok
.ch()) == 0)
2265 empty_name_warning(required
);
2268 non_empty_name_warning();
2281 topdiv
->set_last_page();
2282 if (!end_macro_name
.is_null()) {
2283 spring_trap(end_macro_name
);
2285 process_input_stack();
2287 curenv
->final_break();
2289 process_input_stack();
2291 if (topdiv
->get_page_length() > 0) {
2293 topdiv
->set_ejecting();
2294 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2295 input_stack::push(make_temp_iterator((char *)buf
));
2296 topdiv
->space(topdiv
->get_page_length(), 1);
2298 process_input_stack();
2299 seen_last_page_ejector
= 1; // should be set already
2300 topdiv
->set_ejecting();
2301 push_page_ejector();
2302 topdiv
->space(topdiv
->get_page_length(), 1);
2304 process_input_stack();
2306 // This will only happen if a trap-invoked macro starts a diversion,
2307 // or if vertical position traps have been disabled.
2308 cleanup_and_exit(0);
2311 // This implements .ex. The input stack must be cleared before calling
2316 input_stack::clear();
2323 void return_macro_request()
2325 input_stack::pop_macro();
2331 end_macro_name
= get_name();
2335 void blank_line_macro()
2337 blank_line_macro_name
= get_name();
2341 static void trapping_blank_line()
2343 if (!blank_line_macro_name
.is_null())
2344 spring_trap(blank_line_macro_name
);
2351 int old_compatible_flag
= compatible_flag
;
2352 compatible_flag
= 0;
2353 symbol nm
= get_name();
2357 interpolate_macro(nm
);
2358 compatible_flag
= old_compatible_flag
;
2361 inline int possibly_handle_first_page_transition()
2363 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2364 handle_first_page_transition();
2371 static int transparent_translate(int cc
)
2373 if (!invalid_input_char(cc
)) {
2374 charinfo
*ci
= charset_table
[cc
];
2375 switch (ci
->get_special_translation(1)) {
2376 case charinfo::TRANSLATE_SPACE
:
2378 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2379 return ESCAPE_TILDE
;
2380 case charinfo::TRANSLATE_DUMMY
:
2381 return ESCAPE_AMPERSAND
;
2382 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2383 return ESCAPE_PERCENT
;
2385 // This is really ugly.
2386 ci
= ci
->get_translation(1);
2388 int c
= ci
->get_ascii_code();
2391 error("can't translate %1 to special character `%2'"
2392 " in transparent throughput",
2393 input_char_description(cc
),
2401 struct int_stack_element
{
2403 int_stack_element
*next
;
2413 int_stack::int_stack()
2418 int_stack::~int_stack()
2421 int_stack_element
*temp
= top
;
2427 int int_stack::is_empty()
2432 void int_stack::push(int n
)
2434 int_stack_element
*p
= new int_stack_element
;
2440 int int_stack::pop()
2443 int_stack_element
*p
= top
;
2450 int node::reread(int *)
2455 int diverted_space_node::reread(int *bolp
)
2457 if (curenv
->get_fill())
2458 trapping_blank_line();
2465 int diverted_copy_file_node::reread(int *bolp
)
2467 curdiv
->copy_file(filename
.contents());
2472 int word_space_node::reread(int *bolp
)
2475 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2476 curenv
->space(w
->width
, w
->sentence_width
);
2483 int unbreakable_space_node::reread(int *)
2488 int hmotion_node::reread(int *bolp
)
2490 if (unformat
&& was_tab
) {
2491 curenv
->handle_tab(0);
2498 void process_input_stack()
2500 int_stack trap_bol_stack
;
2503 int suppress_next
= 0;
2505 case token::TOKEN_CHAR
:
2507 unsigned char ch
= tok
.c
;
2508 if (bol
&& !have_input
2509 && (ch
== curenv
->control_char
2510 || ch
== curenv
->no_break_control_char
)) {
2511 break_flag
= ch
== curenv
->control_char
;
2512 // skip tabs as well as spaces here
2515 } while (tok
.white_space());
2516 symbol nm
= get_name();
2520 interpolate_macro(nm
);
2525 if (possibly_handle_first_page_transition())
2529 curenv
->add_char(charset_table
[ch
]);
2531 if (tok
.type
!= token::TOKEN_CHAR
)
2541 case token::TOKEN_TRANSPARENT
:
2544 if (possibly_handle_first_page_transition())
2553 curdiv
->transparent_output(transparent_translate(cc
));
2555 curdiv
->transparent_output(n
);
2556 } while (cc
!= '\n' && cc
!= EOF
);
2558 curdiv
->transparent_output('\n');
2563 case token::TOKEN_NEWLINE
:
2565 if (bol
&& !have_input
2566 && !curenv
->get_prev_line_interrupted())
2567 trapping_blank_line();
2575 case token::TOKEN_REQUEST
:
2577 int request_code
= tok
.c
;
2579 switch (request_code
) {
2583 case COPY_FILE_REQUEST
:
2586 case TRANSPARENT_FILE_REQUEST
:
2590 case VJUSTIFY_REQUEST
:
2602 case token::TOKEN_SPACE
:
2604 if (possibly_handle_first_page_transition())
2606 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2608 // save space_width now so that it isn't changed by \f or \s
2609 // which we wouldn't notice here
2610 hunits space_width
= curenv
->get_space_width();
2612 nspaces
+= tok
.nspaces();
2614 } while (tok
.space());
2616 trapping_blank_line();
2620 curenv
->add_node(new hmotion_node(space_width
* nspaces
));
2630 case token::TOKEN_EOF
:
2632 case token::TOKEN_NODE
:
2634 if (possibly_handle_first_page_transition())
2636 else if (tok
.nd
->reread(&bol
)) {
2641 curenv
->add_node(tok
.nd
);
2644 curenv
->possibly_break_line(1);
2648 case token::TOKEN_PAGE_EJECTOR
:
2650 continue_page_eject();
2651 // I think we just want to preserve bol.
2655 case token::TOKEN_BEGIN_TRAP
:
2657 trap_bol_stack
.push(bol
);
2662 case token::TOKEN_END_TRAP
:
2664 if (trap_bol_stack
.is_empty())
2665 error("spurious end trap token detected!");
2667 bol
= trap_bol_stack
.pop();
2669 /* I'm not totally happy about this. But I can't think of any other
2670 way to do it. Doing an output_pending_lines() whenever a
2671 TOKEN_END_TRAP is detected doesn't work: for example,
2684 a\%very\%very\%long\%word
2686 will print all but the first lines from the word immediately
2687 after the footer, rather than on the next page. */
2689 if (trap_bol_stack
.is_empty())
2690 curenv
->output_pending_lines();
2702 trap_sprung_flag
= 0;
2706 #ifdef WIDOW_CONTROL
2708 void flush_pending_lines()
2710 while (!tok
.newline() && !tok
.eof())
2712 curenv
->output_pending_lines();
2716 #endif /* WIDOW_CONTROL */
2718 request_or_macro::request_or_macro()
2722 macro
*request_or_macro::to_macro()
2727 request::request(REQUEST_FUNCP pp
) : p(pp
)
2731 void request::invoke(symbol
)
2737 enum { SIZE
= 128 };
2738 unsigned char s
[SIZE
];
2743 char_block::char_block()
2752 void append(unsigned char);
2759 friend class macro_header
;
2760 friend class string_iterator
;
2763 char_list::char_list()
2764 : ptr(0), len(0), head(0), tail(0)
2768 char_list::~char_list()
2771 char_block
*tem
= head
;
2777 int char_list::length()
2782 void char_list::append(unsigned char c
)
2785 head
= tail
= new char_block
;
2789 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2790 tail
->next
= new char_block
;
2805 void append(node
*);
2809 friend class macro_header
;
2810 friend class string_iterator
;
2813 void node_list::append(node
*n
)
2821 tail
= tail
->next
= n
;
2825 int node_list::length()
2828 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2833 node_list::node_list()
2838 node
*node_list::extract()
2845 node_list::~node_list()
2847 delete_node_list(head
);
2850 struct macro_header
{
2855 macro_header() { count
= 1; }
2856 macro_header
*copy(int);
2861 if (p
!= 0 && --(p
->count
) <= 0)
2867 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2875 macro::macro(const macro
&m
)
2876 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), length(m
.length
)
2882 macro
¯o::operator=(const macro
&m
)
2884 // don't assign object
2887 if (p
!= 0 && --(p
->count
) <= 0)
2890 filename
= m
.filename
;
2896 void macro::append(unsigned char c
)
2900 p
= new macro_header
;
2901 if (p
->cl
.length() != length
) {
2902 macro_header
*tem
= p
->copy(length
);
2903 if (--(p
->count
) <= 0)
2911 void macro::append_str(const char *s
)
2916 while (s
[i
] != (char)0) {
2923 void macro::append(node
*n
)
2927 p
= new macro_header
;
2928 if (p
->cl
.length() != length
) {
2929 macro_header
*tem
= p
->copy(length
);
2930 if (--(p
->count
) <= 0)
2939 void macro::append_unsigned(unsigned int i
)
2941 unsigned int j
= i
/ 10;
2944 append(((unsigned char)(((int)'0') + i
% 10)));
2947 void macro::append_int(int i
)
2953 append_unsigned((unsigned int)i
);
2956 void macro::print_size()
2958 errprint("%1", length
);
2961 // make a copy of the first n bytes
2963 macro_header
*macro_header::copy(int n
)
2965 macro_header
*p
= new macro_header
;
2966 char_block
*bp
= cl
.head
;
2967 unsigned char *ptr
= bp
->s
;
2970 if (ptr
>= bp
->s
+ char_block::SIZE
) {
2977 p
->nl
.append(nd
->copy());
2986 object_dictionary_iterator
iter(request_dictionary
);
2987 request_or_macro
*rm
;
2989 while (iter
.get(&s
, (object
**)&rm
)) {
2990 assert(!s
.is_null());
2991 macro
*m
= rm
->to_macro();
2993 errprint("%1\t", s
.contents());
3002 class string_iterator
: public input_iterator
{
3004 const char *how_invoked
;
3006 int suppress_newline_flag
; // used by html
3009 int count
; // of characters remaining
3011 int saved_compatible_flag
;
3016 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3019 int get_location(int, const char **, int *);
3021 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3022 int get_compatible_flag() { return saved_compatible_flag
; }
3025 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3026 : mac(m
), how_invoked(p
),
3027 newline_flag(0), suppress_newline_flag(0), lineno(1), nm(s
)
3031 bp
= mac
.p
->cl
.head
;
3032 nd
= mac
.p
->nl
.head
;
3042 string_iterator::string_iterator()
3048 suppress_newline_flag
= 0;
3054 int string_iterator::fill(node
**np
)
3061 const unsigned char *p
= eptr
;
3062 if (p
>= bp
->s
+ char_block::SIZE
) {
3074 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3079 unsigned char c
= *p
;
3080 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3082 if (is_html
&& c
== ESCAPE_NEWLINE
)
3083 suppress_newline_flag
= 1;
3096 int string_iterator::peek()
3100 const unsigned char *p
= eptr
;
3103 if (p
>= bp
->s
+ char_block::SIZE
) {
3109 int string_iterator::get_location(int allow_macro
,
3110 const char **filep
, int *linep
)
3114 if (mac
.filename
== 0)
3116 *filep
= mac
.filename
;
3117 *linep
= mac
.lineno
+ lineno
- 1;
3121 void string_iterator::backtrace()
3124 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3127 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3129 errprint(": %1\n", how_invoked
);
3136 class temp_iterator
: public input_iterator
{
3137 unsigned char *base
;
3138 temp_iterator(const char *, int len
);
3141 friend input_iterator
*make_temp_iterator(const char *);
3147 temp_iterator::temp_iterator(const char *s
, int len
)
3149 base
= new unsigned char[len
];
3150 memcpy(base
, s
, len
);
3155 temp_iterator::~temp_iterator()
3160 class small_temp_iterator
: public input_iterator
{
3162 small_temp_iterator(const char *, int);
3163 ~small_temp_iterator();
3164 enum { BLOCK
= 16 };
3165 static small_temp_iterator
*free_list
;
3166 void *operator new(size_t);
3167 void operator delete(void *);
3169 unsigned char buf
[SIZE
];
3170 friend input_iterator
*make_temp_iterator(const char *);
3173 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3175 void *small_temp_iterator::operator new(size_t n
)
3177 assert(n
== sizeof(small_temp_iterator
));
3180 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3181 for (int i
= 0; i
< BLOCK
- 1; i
++)
3182 free_list
[i
].next
= free_list
+ i
+ 1;
3183 free_list
[BLOCK
-1].next
= 0;
3185 small_temp_iterator
*p
= free_list
;
3186 free_list
= (small_temp_iterator
*)(free_list
->next
);
3194 void small_temp_iterator::operator delete(void *p
)
3197 ((small_temp_iterator
*)p
)->next
= free_list
;
3198 free_list
= (small_temp_iterator
*)p
;
3202 small_temp_iterator::~small_temp_iterator()
3209 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3211 for (int i
= 0; i
< len
; i
++)
3217 input_iterator
*make_temp_iterator(const char *s
)
3220 return new small_temp_iterator(s
, 0);
3223 if (n
<= small_temp_iterator::SIZE
)
3224 return new small_temp_iterator(s
, n
);
3226 return new temp_iterator(s
, n
);
3230 // this is used when macros are interpolated using the .macro_name notation
3235 arg_list(const macro
&);
3239 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3243 arg_list::~arg_list()
3247 class macro_iterator
: public string_iterator
{
3251 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3254 int has_args() { return 1; }
3255 input_iterator
*get_arg(int i
);
3256 int nargs() { return argc
; }
3257 void add_arg(const macro
&m
);
3259 int is_macro() { return 1; }
3262 input_iterator
*macro_iterator::get_arg(int i
)
3265 return make_temp_iterator(nm
.contents());
3266 if (i
> 0 && i
<= argc
) {
3268 for (int j
= 1; j
< i
; j
++) {
3272 return new string_iterator(p
->mac
);
3278 void macro_iterator::add_arg(const macro
&m
)
3281 for (p
= &args
; *p
; p
= &((*p
)->next
))
3283 *p
= new arg_list(m
);
3287 void macro_iterator::shift(int n
)
3289 while (n
> 0 && argc
> 0) {
3290 arg_list
*tem
= args
;
3298 // This gets used by eg .if '\?xxx\?''.
3300 int operator==(const macro
&m1
, const macro
&m2
)
3302 if (m1
.length
!= m2
.length
)
3304 string_iterator
iter1(m1
);
3305 string_iterator
iter2(m2
);
3309 int c1
= iter1
.get(&nd1
);
3312 int c2
= iter2
.get(&nd2
);
3324 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3334 static void interpolate_macro(symbol nm
)
3336 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3339 const char *s
= nm
.contents();
3340 if (strlen(s
) > 2) {
3341 request_or_macro
*r
;
3346 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3348 macro
*m
= r
->to_macro();
3349 if (!m
|| !m
->empty())
3350 warned
= warning(WARN_SPACE
,
3351 "`%1' not defined (probable missing space after `%2')",
3352 nm
.contents(), buf
);
3356 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
3358 request_dictionary
.define(nm
, p
);
3369 static void decode_args(macro_iterator
*mi
)
3371 if (!tok
.newline() && !tok
.eof()) {
3373 int c
= get_copy(&n
);
3377 if (c
== '\n' || c
== EOF
)
3380 int quote_input_level
= 0;
3381 int done_tab_warning
= 0;
3383 quote_input_level
= input_stack::get_level();
3386 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3387 if (quote_input_level
> 0 && c
== '\"'
3389 || input_stack::get_level() == quote_input_level
)) {
3402 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3403 warning(WARN_TAB
, "tab character in unquoted macro argument");
3404 done_tab_warning
= 1;
3416 void macro::invoke(symbol nm
)
3418 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3420 input_stack::push(mi
);
3424 macro
*macro::to_macro()
3434 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
3435 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
3439 macro_iterator::macro_iterator() : args(0), argc(0)
3443 macro_iterator::~macro_iterator()
3446 arg_list
*tem
= args
;
3452 int trap_sprung_flag
= 0;
3453 int postpone_traps_flag
= 0;
3454 symbol postponed_trap
;
3456 void spring_trap(symbol nm
)
3458 assert(!nm
.is_null());
3459 trap_sprung_flag
= 1;
3460 if (postpone_traps_flag
) {
3461 postponed_trap
= nm
;
3464 static char buf
[2] = { BEGIN_TRAP
, 0 };
3465 static char buf2
[2] = { END_TRAP
, '\0' };
3466 input_stack::push(make_temp_iterator(buf2
));
3467 request_or_macro
*p
= lookup_request(nm
);
3468 macro
*m
= p
->to_macro();
3470 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3472 error("you can't invoke a request with a trap");
3473 input_stack::push(make_temp_iterator(buf
));
3476 void postpone_traps()
3478 postpone_traps_flag
= 1;
3481 int unpostpone_traps()
3483 postpone_traps_flag
= 0;
3484 if (!postponed_trap
.is_null()) {
3485 spring_trap(postponed_trap
);
3486 postponed_trap
= NULL_SYMBOL
;
3495 macro_iterator
*mi
= new macro_iterator
;
3496 int reading_from_terminal
= isatty(fileno(stdin
));
3498 if (!tok
.newline() && !tok
.eof()) {
3499 int c
= get_copy(0);
3502 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3503 if (!invalid_input_char(c
)) {
3504 if (reading_from_terminal
)
3515 if (reading_from_terminal
) {
3516 fputc(had_prompt
? ':' : '\a', stderr
);
3519 input_stack::push(mi
);
3523 while ((c
= getchar()) != EOF
) {
3524 if (invalid_input_char(c
))
3525 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
3538 if (reading_from_terminal
)
3540 input_stack::push(new string_iterator(mac
));
3544 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3545 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
, CALLING_DISABLE_COMP
};
3547 void do_define_string(define_mode mode
, calling_mode calling
)
3561 else if (!tok
.space()) {
3562 error("bad string definition");
3573 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3574 macro
*mm
= rm
? rm
->to_macro() : 0;
3575 if (mode
== DEFINE_APPEND
&& mm
)
3577 if (calling
== CALLING_DISABLE_COMP
)
3578 mac
.append(COMPATIBLE_SAVE
);
3579 while (c
!= '\n' && c
!= EOF
) {
3583 mac
.append((unsigned char)c
);
3588 request_dictionary
.define(nm
, mm
);
3590 if (calling
== CALLING_DISABLE_COMP
)
3591 mac
.append(COMPATIBLE_RESTORE
);
3596 void define_string()
3598 do_define_string(DEFINE_NORMAL
, CALLING_NORMAL
);
3601 void define_nocomp_string()
3603 do_define_string(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3606 void append_string()
3608 do_define_string(DEFINE_APPEND
, CALLING_NORMAL
);
3611 void append_nocomp_string()
3613 do_define_string(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3616 void do_define_character(int fallback
)
3621 charinfo
*ci
= tok
.get_char(1);
3631 else if (!tok
.space()) {
3632 error("bad character definition");
3638 while (c
== ' ' || c
== '\t')
3642 macro
*m
= new macro
;
3643 while (c
!= '\n' && c
!= EOF
) {
3647 m
->append((unsigned char)c
);
3650 m
= ci
->set_macro(m
, fallback
);
3656 void define_character()
3658 do_define_character(0);
3661 void define_fallback_character()
3663 do_define_character(1);
3666 static void remove_character()
3669 while (!tok
.newline() && !tok
.eof()) {
3670 if (!tok
.space() && !tok
.tab()) {
3671 charinfo
*ci
= tok
.get_char(1);
3674 macro
*m
= ci
->set_macro(0);
3683 static void interpolate_string(symbol nm
)
3685 request_or_macro
*p
= lookup_request(nm
);
3686 macro
*m
= p
->to_macro();
3688 error("you can only invoke a string using \\*");
3690 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3691 input_stack::push(si
);
3695 /* This class is used for the implementation of \$@. It is used for
3696 each of the closing double quotes. It artificially increases the
3697 input level by 2, so that the closing double quote will appear to have
3698 the same input level as the opening quote. */
3700 class end_quote_iterator
: public input_iterator
{
3701 unsigned char buf
[1];
3703 end_quote_iterator();
3704 ~end_quote_iterator() { }
3705 int internal_level() { return 2; }
3708 end_quote_iterator::end_quote_iterator()
3715 static void interpolate_arg(symbol nm
)
3717 const char *s
= nm
.contents();
3718 if (!s
|| *s
== '\0')
3719 copy_mode_error("missing argument name");
3720 else if (s
[1] == 0 && csdigit(s
[0]))
3721 input_stack::push(input_stack::get_arg(s
[0] - '0'));
3722 else if (s
[0] == '*' && s
[1] == '\0') {
3723 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3724 input_stack::push(input_stack::get_arg(i
));
3726 input_stack::push(make_temp_iterator(" "));
3729 else if (s
[0] == '@' && s
[1] == '\0') {
3730 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3731 input_stack::push(new end_quote_iterator
);
3732 input_stack::push(input_stack::get_arg(i
));
3733 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
3738 for (p
= s
; *p
&& csdigit(*p
); p
++)
3741 copy_mode_error("bad argument name `%1'", s
);
3743 input_stack::push(input_stack::get_arg(atoi(s
)));
3747 void handle_first_page_transition()
3750 topdiv
->begin_page();
3753 // We push back a token by wrapping it up in a token_node, and
3754 // wrapping that up in a string_iterator.
3756 static void push_token(const token
&t
)
3759 m
.append(new token_node(t
));
3760 input_stack::push(new string_iterator(m
));
3763 void push_page_ejector()
3765 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
3766 input_stack::push(make_temp_iterator(buf
));
3769 void handle_initial_request(unsigned char code
)
3775 mac
.append(new token_node(tok
));
3776 input_stack::push(new string_iterator(mac
));
3777 input_stack::push(make_temp_iterator(buf
));
3778 topdiv
->begin_page();
3782 void handle_initial_title()
3784 handle_initial_request(TITLE_REQUEST
);
3787 // this should be local to define_macro, but cfront 1.2 doesn't support that
3788 static symbol
dot_symbol(".");
3790 void do_define_macro(define_mode mode
, calling_mode calling
)
3793 if (calling
== CALLING_INDIRECT
) {
3794 symbol temp1
= get_name(1);
3795 if (temp1
.is_null()) {
3799 symbol temp2
= get_name();
3800 input_stack::push(make_temp_iterator("\n"));
3801 if (!temp2
.is_null()) {
3802 interpolate_string(temp2
);
3803 input_stack::push(make_temp_iterator(" "));
3805 interpolate_string(temp1
);
3806 input_stack::push(make_temp_iterator(" "));
3809 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3816 term
= get_name(); // the request that terminates the definition
3819 while (!tok
.newline() && !tok
.eof())
3821 const char *start_filename
;
3823 int have_start_location
= input_stack::get_location(0, &start_filename
,
3826 // doing this here makes the line numbers come out right
3827 int c
= get_copy(&n
, 1);
3830 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3831 request_or_macro
*rm
=
3832 (request_or_macro
*)request_dictionary
.lookup(nm
);
3834 mm
= rm
->to_macro();
3835 if (mm
&& mode
== DEFINE_APPEND
)
3839 if (calling
== CALLING_DISABLE_COMP
)
3840 mac
.append(COMPATIBLE_SAVE
);
3842 while (c
== ESCAPE_NEWLINE
) {
3843 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
3845 c
= get_copy(&n
, 1);
3847 if (bol
&& c
== '.') {
3848 const char *s
= term
.contents();
3850 // see if it matches term
3853 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
3855 if ((unsigned char)s
[0] == d
) {
3856 for (i
= 1; s
[i
] != 0; i
++) {
3858 if ((unsigned char)s
[i
] != d
)
3864 && ((i
== 2 && compatible_flag
)
3865 || (d
= get_copy(&n
)) == ' '
3866 || d
== '\n')) { // we found it
3871 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3874 request_dictionary
.define(nm
, mm
);
3876 if (calling
== CALLING_DISABLE_COMP
)
3877 mac
.append(COMPATIBLE_RESTORE
);
3880 if (term
!= dot_symbol
) {
3882 interpolate_macro(term
);
3888 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3890 for (int j
= 0; j
< i
; j
++)
3896 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3897 if (have_start_location
)
3898 error_with_file_and_line(start_filename
, start_lineno
,
3899 "end of file while defining macro `%1'",
3902 error("end of file while defining macro `%1'", nm
.contents());
3905 if (have_start_location
)
3906 error_with_file_and_line(start_filename
, start_lineno
,
3907 "end of file while ignoring input lines");
3909 error("end of file while ignoring input lines");
3914 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3921 c
= get_copy(&n
, 1);
3927 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
);
3930 void define_nocomp_macro()
3932 do_define_macro(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3935 void define_indirect_macro()
3937 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
);
3942 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
);
3945 void append_indirect_macro()
3947 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
);
3950 void append_nocomp_macro()
3952 do_define_macro(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3958 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
);
3965 symbol s
= get_name();
3968 request_dictionary
.remove(s
);
3975 symbol s1
= get_name(1);
3976 if (!s1
.is_null()) {
3977 symbol s2
= get_name(1);
3979 request_dictionary
.rename(s1
, s2
);
3986 symbol s1
= get_name(1);
3987 if (!s1
.is_null()) {
3988 symbol s2
= get_name(1);
3989 if (!s2
.is_null()) {
3990 if (!request_dictionary
.alias(s1
, s2
))
3991 warning(WARN_MAC
, "`%1' not defined", s2
.contents());
3999 symbol s
= get_name(1);
4001 request_or_macro
*p
= lookup_request(s
);
4002 macro
*m
= p
->to_macro();
4004 error("cannot chop request");
4005 else if (m
->length
== 0)
4006 error("cannot chop empty macro");
4013 void substring_macro()
4016 symbol s
= get_name(1);
4017 if (!s
.is_null() && get_integer(&start
)) {
4018 request_or_macro
*p
= lookup_request(s
);
4019 macro
*m
= p
->to_macro();
4021 error("cannot substring request");
4024 start
+= m
->length
- 1;
4028 if (!has_arg() || get_integer(&end
)) {
4030 end
+= m
->length
- 1;
4038 if (start
>= m
->length
|| end
< 0) {
4041 if (--(m
->p
->count
) <= 0)
4050 if (end
>= m
->length
)
4051 end
= m
->length
- 1;
4053 m
->length
= end
+ 1;
4055 string_iterator
iter(*m
);
4057 for (i
= 0; i
< start
; i
++)
4058 if (iter
.get(0) == EOF
)
4061 for (; i
<= end
; i
++) {
4063 int c
= iter
.get(&nd
);
4069 mac
.append((unsigned char)c
);
4083 if (ret
.is_null()) {
4093 else if (!tok
.space()) {
4094 error("bad string definition");
4105 while (c
!= '\n' && c
!= EOF
) {
4110 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4114 set_number_reg(ret
, len
);
4117 void asciify_macro()
4119 symbol s
= get_name(1);
4121 request_or_macro
*p
= lookup_request(s
);
4122 macro
*m
= p
->to_macro();
4124 error("cannot asciify request");
4127 string_iterator
iter(*m
);
4130 int c
= iter
.get(&nd
);
4144 void unformat_macro()
4146 symbol s
= get_name(1);
4148 request_or_macro
*p
= lookup_request(s
);
4149 macro
*m
= p
->to_macro();
4151 error("cannot unformat request");
4154 string_iterator
iter(*m
);
4157 int c
= iter
.get(&nd
);
4163 if (nd
->set_unformat_flag())
4173 static void interpolate_environment_variable(symbol nm
)
4175 const char *s
= getenv(nm
.contents());
4177 input_stack::push(make_temp_iterator(s
));
4180 void interpolate_number_reg(symbol nm
, int inc
)
4182 reg
*r
= lookup_number_reg(nm
);
4187 input_stack::push(make_temp_iterator(r
->get_string()));
4190 static void interpolate_number_format(symbol nm
)
4192 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4194 input_stack::push(make_temp_iterator(r
->get_format()));
4197 static int get_delim_number(units
*n
, int si
, int prev_value
)
4201 if (start
.delimiter(1)) {
4203 if (get_number(n
, si
, prev_value
)) {
4205 warning(WARN_DELIM
, "closing delimiter does not match");
4212 static int get_delim_number(units
*n
, int si
)
4216 if (start
.delimiter(1)) {
4218 if (get_number(n
, si
)) {
4220 warning(WARN_DELIM
, "closing delimiter does not match");
4227 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
4231 int start_level
= input_stack::get_level();
4232 if (!start
.delimiter(1))
4235 if (get_number(n
, si
)) {
4236 if (tok
.dummy() || tok
.transparent_dummy())
4238 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4239 *cp
= tok
.get_char(1);
4242 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4243 warning(WARN_DELIM
, "closing delimiter does not match");
4249 static int read_size(int *x
)
4259 else if (c
== '+') {
4270 // allow an increment either before or after the left parenthesis
4276 else if (c
== '+') {
4291 val
= val
*10 + (c
- '0');
4296 else if (csdigit(c
)) {
4298 if (!inc
&& c
!= '0' && c
< '4') {
4304 val
= val
*10 + (c
- '0');
4308 else if (!tok
.delimiter(1))
4314 ? get_number(&val
, 'z')
4315 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4317 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4318 if (start
.ch() == '[')
4319 error("missing `]'");
4321 error("missing closing delimiter");
4329 // special case -- \s[0] and \s0 means to revert to previous size
4336 *x
= curenv
->get_requested_point_size() + val
;
4339 *x
= curenv
->get_requested_point_size() - val
;
4346 "\\s request results in non-positive point size; set to 1");
4352 error("bad digit in point size");
4357 static symbol
get_delim_name()
4362 error("end of input at start of delimited name");
4365 if (start
.newline()) {
4366 error("can't delimit name with a newline");
4369 int start_level
= input_stack::get_level();
4370 char abuf
[ABUF_SIZE
];
4372 int buf_size
= ABUF_SIZE
;
4375 if (i
+ 1 > buf_size
) {
4377 buf
= new char[ABUF_SIZE
*2];
4378 memcpy(buf
, abuf
, buf_size
);
4379 buf_size
= ABUF_SIZE
*2;
4382 char *old_buf
= buf
;
4383 buf
= new char[buf_size
*2];
4384 memcpy(buf
, old_buf
, buf_size
);
4391 && (compatible_flag
|| input_stack::get_level() == start_level
))
4393 if ((buf
[i
] = tok
.ch()) == 0) {
4394 error("missing delimiter (got %1)", tok
.description());
4404 error("empty delimited name");
4419 static void do_register()
4423 if (!start
.delimiter(1))
4426 symbol nm
= get_long_name(1);
4431 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4433 if (!r
|| !r
->get_value(&prev_value
))
4436 if (!get_number(&val
, 'u', prev_value
))
4439 warning(WARN_DELIM
, "closing delimiter does not match");
4443 set_number_reg(nm
, val
);
4446 // this implements the \w escape sequence
4448 static void do_width()
4452 int start_level
= input_stack::get_level();
4453 environment
env(curenv
);
4454 environment
*oldenv
= curenv
;
4459 warning(WARN_DELIM
, "missing closing delimiter");
4462 if (tok
.newline()) {
4463 warning(WARN_DELIM
, "missing closing delimiter");
4464 input_stack::push(make_temp_iterator("\n"));
4468 && (compatible_flag
|| input_stack::get_level() == start_level
))
4473 units x
= env
.get_input_line_position().to_units();
4474 input_stack::push(make_temp_iterator(i_to_a(x
)));
4475 env
.width_registers();
4479 charinfo
*page_character
;
4481 void set_page_character()
4483 page_character
= get_optional_char();
4487 static const symbol
percent_symbol("%");
4489 void read_title_parts(node
**part
, hunits
*part_width
)
4492 if (tok
.newline() || tok
.eof())
4495 int start_level
= input_stack::get_level();
4497 for (int i
= 0; i
< 3; i
++) {
4498 while (!tok
.newline() && !tok
.eof()) {
4500 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
4504 if (page_character
!= 0 && tok
.get_char() == page_character
)
4505 interpolate_number_reg(percent_symbol
, 0);
4510 curenv
->wrap_up_tab();
4511 part_width
[i
] = curenv
->get_input_line_position();
4512 part
[i
] = curenv
->extract_output_line();
4514 while (!tok
.newline() && !tok
.eof())
4518 class non_interpreted_node
: public node
{
4521 non_interpreted_node(const macro
&);
4522 int interpret(macro
*);
4529 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
4533 int non_interpreted_node::same(node
*nd
)
4535 return mac
== ((non_interpreted_node
*)nd
)->mac
;
4538 const char *non_interpreted_node::type()
4540 return "non_interpreted_node";
4543 int non_interpreted_node::force_tprint()
4548 node
*non_interpreted_node::copy()
4550 return new non_interpreted_node(mac
);
4553 int non_interpreted_node::interpret(macro
*m
)
4555 string_iterator
si(mac
);
4569 static node
*do_non_interpreted()
4574 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4579 if (c
== EOF
|| c
== '\n') {
4580 error("missing \\?");
4583 return new non_interpreted_node(mac
);
4586 static void encode_char(macro
*mac
, char c
)
4589 if ((font::use_charnames_in_special
) && tok
.special()) {
4590 charinfo
*ci
= tok
.get_char(1);
4591 const char *s
= ci
->get_symbol()->contents();
4592 if (s
[0] != (char)0) {
4596 while (s
[i
] != (char)0) {
4604 else if (!(tok
.hyphen_indicator()
4606 || tok
.transparent_dummy()
4607 || tok
.zero_width_break()))
4608 error("%1 is invalid within \\X", tok
.description());
4611 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4613 * add escape escape sequence
4625 int start_level
= input_stack::get_level();
4628 tok
!= start
|| input_stack::get_level() != start_level
;
4631 warning(WARN_DELIM
, "missing closing delimiter");
4634 if (tok
.newline()) {
4635 input_stack::push(make_temp_iterator("\n"));
4636 warning(WARN_DELIM
, "missing closing delimiter");
4644 else if (tok
.leader())
4646 else if (tok
.backspace())
4650 encode_char(&mac
, c
);
4652 return new special_node(mac
);
4655 extern int image_no
; // from node.cc
4657 static node
*do_suppress(symbol nm
)
4659 if (nm
.is_null() || nm
.is_empty()) {
4660 error("expecting an argument to escape \\O");
4663 const char *s
= nm
.contents();
4666 if (begin_level
== 1)
4667 return new suppress_node(0, 0);
4670 if (begin_level
== 1)
4671 return new suppress_node(1, 0);
4674 if (begin_level
== 1)
4675 return new suppress_node(1, 1);
4679 if ((begin_level
== 1) && (!is_html
)) {
4680 if (curdiv
== topdiv
) {
4681 if (topdiv
->before_first_page
) {
4683 if (!topdiv
->no_space_mode
)
4684 topdiv
->begin_page();
4686 else if (topdiv
->no_space_mode
)
4687 topdiv
->begin_page();
4689 push_page_ejector();
4690 topdiv
->begin_page();
4691 topdiv
->set_ejecting();
4695 push_page_ejector();
4698 if (!topdiv
->no_space_mode
)
4699 topdiv
->set_ejecting();
4709 s
++; // move over '5'
4711 if (*s
== (char)0) {
4712 error("missing position and filename in \\O");
4715 if (!(position
== 'l'
4718 || position
== 'i')) {
4719 error("l, r, c, or i position expected (got %1 in \\O)", position
);
4722 s
++; // onto image name
4723 if (s
== (char *)0) {
4724 error("missing image name for \\O");
4728 if (begin_level
== 1)
4729 return new suppress_node(symbol(s
), position
, image_no
);
4733 error("`%1' is an invalid argument to \\O", *s
);
4738 void special_node::tprint(troff_output_file
*out
)
4741 string_iterator
iter(mac
);
4743 int c
= iter
.get(0);
4746 for (const char *s
= ::asciify(c
); *s
; s
++)
4747 tprint_char(out
, *s
);
4752 int get_file_line(const char **filename
, int *lineno
)
4754 return input_stack::get_location(0, filename
, lineno
);
4760 if (get_integer(&n
)) {
4761 const char *filename
= 0;
4763 symbol s
= get_long_name();
4764 filename
= s
.contents();
4766 (void)input_stack::set_location(filename
, n
-1);
4771 static int nroff_mode
= 0;
4773 static void nroff_request()
4779 static void troff_request()
4785 static void skip_alternative()
4788 // ensure that ``.if 0\{'' works as expected
4789 if (tok
.left_brace())
4793 c
= input_stack::get(0);
4796 if (c
== ESCAPE_LEFT_BRACE
)
4798 else if (c
== ESCAPE_RIGHT_BRACE
)
4800 else if (c
== escape_char
&& escape_char
> 0)
4801 switch(input_stack::get(0)) {
4809 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
4813 Note that the level can properly be < 0, eg
4819 So don't give an error message in this case.
4821 if (level
<= 0 && c
== '\n')
4827 static void begin_alternative()
4829 while (tok
.space() || tok
.left_brace())
4839 static int_stack if_else_stack
;
4846 while (tok
.ch() == '!') {
4851 unsigned char c
= tok
.ch();
4854 result
= !nroff_mode
;
4856 else if (c
== 'n') {
4858 result
= nroff_mode
;
4860 else if (c
== 'v') {
4864 else if (c
== 'o') {
4865 result
= (topdiv
->get_page_number() & 1);
4868 else if (c
== 'e') {
4869 result
= !(topdiv
->get_page_number() & 1);
4872 else if (c
== 'd' || c
== 'r') {
4874 symbol nm
= get_name(1);
4880 ? request_dictionary
.lookup(nm
) != 0
4881 : number_reg_dictionary
.lookup(nm
) != 0);
4883 else if (c
== 'm') {
4885 symbol nm
= get_long_name(1);
4890 result
= (nm
== default_symbol
4891 || color_dictionary
.lookup(nm
) != 0);
4893 else if (c
== 'c') {
4896 charinfo
*ci
= tok
.get_char(1);
4901 result
= character_exists(ci
, curenv
);
4904 else if (tok
.space())
4906 else if (tok
.delimiter()) {
4908 int delim_level
= input_stack::get_level();
4909 environment
env1(curenv
);
4910 environment
env2(curenv
);
4911 environment
*oldenv
= curenv
;
4913 for (int i
= 0; i
< 2; i
++) {
4916 if (tok
.newline() || tok
.eof()) {
4917 warning(WARN_DELIM
, "missing closing delimiter");
4923 && (compatible_flag
|| input_stack::get_level() == delim_level
))
4929 node
*n1
= env1
.extract_output_line();
4930 node
*n2
= env2
.extract_output_line();
4931 result
= same_node_list(n1
, n2
);
4932 delete_node_list(n1
);
4933 delete_node_list(n2
);
4939 if (!get_number(&n
, 'u')) {
4949 begin_alternative();
4955 void if_else_request()
4957 if_else_stack
.push(do_if_request());
4967 if (if_else_stack
.is_empty()) {
4968 warning(WARN_EL
, "unbalanced .el request");
4972 if (if_else_stack
.pop())
4975 begin_alternative();
4979 static int while_depth
= 0;
4980 static int while_break_flag
= 0;
4982 void while_request()
4987 mac
.append(new token_node(tok
));
4990 int c
= input_stack::get(&n
);
5006 if (c
== ESCAPE_LEFT_BRACE
)
5008 else if (c
== ESCAPE_RIGHT_BRACE
)
5010 else if (c
== escape_char
)
5013 if (c
== '\n' && level
<= 0)
5018 error("unbalanced \\{ \\}");
5021 input_stack::add_boundary();
5023 input_stack::push(new string_iterator(mac
, "while loop"));
5025 if (!do_if_request()) {
5026 while (input_stack::get(0) != EOF
)
5030 process_input_stack();
5031 if (while_break_flag
|| input_stack::is_return_boundary()) {
5032 while_break_flag
= 0;
5036 input_stack::remove_boundary();
5042 void while_break_request()
5045 error("no while loop");
5049 while_break_flag
= 1;
5050 while (input_stack::get(0) != EOF
)
5056 void while_continue_request()
5059 error("no while loop");
5063 while (input_stack::get(0) != EOF
)
5073 symbol nm
= get_long_name(1);
5077 while (!tok
.newline() && !tok
.eof())
5080 FILE *fp
= fopen(nm
.contents(), "r");
5082 input_stack::push(new file_iterator(fp
, nm
.contents()));
5084 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5089 // like .so but use popen()
5094 error(".pso request not allowed in safer mode");
5098 #ifdef POPEN_MISSING
5099 error("pipes not available on this system");
5101 #else /* not POPEN_MISSING */
5102 if (tok
.newline() || tok
.eof())
5103 error("missing command");
5106 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5109 char *buf
= new char[buf_size
];
5111 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5112 const char *s
= asciify(c
);
5113 int slen
= strlen(s
);
5114 if (buf_used
+ slen
+ 1> buf_size
) {
5115 char *old_buf
= buf
;
5116 int old_buf_size
= buf_size
;
5118 buf
= new char[buf_size
];
5119 memcpy(buf
, old_buf
, old_buf_size
);
5122 strcpy(buf
+ buf_used
, s
);
5125 buf
[buf_used
] = '\0';
5127 FILE *fp
= popen(buf
, POPEN_RT
);
5129 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5131 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5135 #endif /* not POPEN_MISSING */
5141 static int llx_reg_contents
= 0;
5142 static int lly_reg_contents
= 0;
5143 static int urx_reg_contents
= 0;
5144 static int ury_reg_contents
= 0;
5146 struct bounding_box
{
5147 int llx
, lly
, urx
, ury
;
5150 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5151 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5153 int parse_bounding_box(char *p
, bounding_box
*bb
)
5155 if (sscanf(p
, "%d %d %d %d",
5156 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5159 /* The Document Structuring Conventions say that the numbers
5160 should be integers. Unfortunately some broken applications
5162 double x1
, x2
, x3
, x4
;
5163 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5171 for (; *p
== ' ' || *p
== '\t'; p
++)
5173 if (strncmp(p
, "(atend)", 7) == 0) {
5178 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5182 // This version is taken from psrm.cc
5184 #define PS_LINE_MAX 255
5185 cset
white_space("\n\r \t");
5187 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5196 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5197 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5198 error("invalid input character code %1 in `%2'", int(c
), filename
);
5199 else if (i
< PS_LINE_MAX
)
5203 error("PostScript file `%1' is non-conforming "
5204 "because length of line exceeds 255", filename
);
5212 if (c
!= EOF
&& c
!= '\n')
5218 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5220 llx_reg_contents
= llx
;
5221 lly_reg_contents
= lly
;
5222 urx_reg_contents
= urx
;
5223 ury_reg_contents
= ury
;
5226 void do_ps_file(FILE *fp
, const char* filename
)
5230 char buf
[PS_LINE_MAX
];
5231 llx_reg_contents
= lly_reg_contents
=
5232 urx_reg_contents
= ury_reg_contents
= 0;
5233 if (!ps_get_line(buf
, fp
, filename
)) {
5234 error("`%1' is empty", filename
);
5237 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5238 error("`%1' is not conforming to the Document Structuring Conventions",
5242 while (ps_get_line(buf
, fp
, filename
) != 0) {
5243 if (buf
[0] != '%' || buf
[1] != '%'
5244 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5246 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5247 int res
= parse_bounding_box(buf
+ 14, &bb
);
5249 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5252 else if (res
== 2) {
5257 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5266 /* in the trailer, the last BoundingBox comment is significant */
5267 for (offset
= 512; !last_try
; offset
*= 2) {
5268 int had_trailer
= 0;
5270 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5272 if (fseek(fp
, 0L, 0) == -1)
5275 while (ps_get_line(buf
, fp
, filename
) != 0) {
5276 if (buf
[0] == '%' && buf
[1] == '%') {
5278 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5282 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5283 int res
= parse_bounding_box(buf
+ 14, &bb
);
5286 else if (res
== 2) {
5287 error("`(atend)' not allowed in trailer of `%1'", filename
);
5291 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5300 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5305 error("%%%%BoundingBox comment not found in `%1'", filename
);
5308 void ps_bbox_request()
5310 symbol nm
= get_long_name(1);
5314 while (!tok
.newline() && !tok
.eof())
5317 // PS files might contain non-printable characters, such as ^Z
5318 // and CRs not followed by an LF, so open them in binary mode.
5319 FILE *fp
= fopen(nm
.contents(), FOPEN_RB
);
5321 do_ps_file(fp
, nm
.contents());
5325 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5330 const char *asciify(int c
)
5333 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5334 buf
[1] = buf
[2] = '\0';
5336 case ESCAPE_QUESTION
:
5339 case ESCAPE_AMPERSAND
:
5342 case ESCAPE_RIGHT_PARENTHESIS
:
5345 case ESCAPE_UNDERSCORE
:
5351 case ESCAPE_CIRCUMFLEX
:
5354 case ESCAPE_LEFT_BRACE
:
5357 case ESCAPE_RIGHT_BRACE
:
5360 case ESCAPE_LEFT_QUOTE
:
5363 case ESCAPE_RIGHT_QUOTE
:
5381 case ESCAPE_PERCENT
:
5394 if (invalid_input_char(c
))
5403 const char *input_char_description(int c
)
5407 return "a newline character";
5409 return "a backspace character";
5411 return "a leader character";
5413 return "a tab character";
5415 return "a space character";
5419 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
5420 if (invalid_input_char(c
)) {
5421 const char *s
= asciify(c
);
5428 sprintf(buf
, "magic character code %d", c
);
5437 sprintf(buf
, "character code %d", c
);
5441 // .tm, .tm1, and .tmc
5443 void do_terminal(int newline
, int string_like
)
5445 if (!tok
.newline() && !tok
.eof()) {
5449 if (string_like
&& c
== '"') {
5453 if (c
!= ' ' && c
!= '\t')
5456 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5457 fputs(asciify(c
), stderr
);
5460 fputc('\n', stderr
);
5475 void terminal_continue()
5480 dictionary
stream_dictionary(20);
5482 void do_open(int append
)
5484 symbol stream
= get_name(1);
5485 if (!stream
.is_null()) {
5486 symbol filename
= get_long_name(1);
5487 if (!filename
.is_null()) {
5489 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
5491 error("can't open `%1' for %2: %3",
5492 filename
.contents(),
5493 append
? "appending" : "writing",
5495 fp
= (FILE *)stream_dictionary
.remove(stream
);
5498 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
5509 error(".open request not allowed in safer mode");
5516 void opena_request()
5519 error(".opena request not allowed in safer mode");
5526 void close_request()
5528 symbol stream
= get_name(1);
5529 if (!stream
.is_null()) {
5530 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
5532 error("no stream named `%1'", stream
.contents());
5539 // .write and .writec
5541 void do_write_request(int newline
)
5543 symbol stream
= get_name(1);
5544 if (stream
.is_null()) {
5548 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5550 error("no stream named `%1'", stream
.contents());
5555 while ((c
= get_copy(0)) == ' ')
5559 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5560 fputs(asciify(c
), fp
);
5567 void write_request()
5569 do_write_request(1);
5572 void write_request_continue()
5574 do_write_request(0);
5577 void write_macro_request()
5579 symbol stream
= get_name(1);
5580 if (stream
.is_null()) {
5584 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5586 error("no stream named `%1'", stream
.contents());
5590 symbol s
= get_name(1);
5595 request_or_macro
*p
= lookup_request(s
);
5596 macro
*m
= p
->to_macro();
5598 error("cannot write request");
5600 string_iterator
iter(*m
);
5602 int c
= iter
.get(0);
5605 fputs(asciify(c
), fp
);
5612 void warnscale_request()
5619 warn_scale
= (double)units_per_inch
;
5621 warn_scale
= (double)units_per_inch
/ 2.54;
5623 warn_scale
= (double)units_per_inch
/ 72.0;
5625 warn_scale
= (double)units_per_inch
/ 6.0;
5628 "invalid scaling indicator `%1', using `i' instead", c
);
5631 warn_scaling_indicator
= c
;
5636 void spreadwarn_request()
5639 if (has_arg() && get_hunits(&n
, 'm')) {
5642 hunits em
= curenv
->get_size();
5643 spread_limit
= (double)n
.to_units()
5644 / (em
.is_zero() ? hresolution
: em
.to_units());
5647 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
5648 // changing value; we mirror at
5649 // -0.5 to make zero a valid value
5653 static void init_charset_table()
5656 strcpy(buf
, "char");
5657 for (int i
= 0; i
< 256; i
++) {
5658 strcpy(buf
+ 4, i_to_a(i
));
5659 charset_table
[i
] = get_charinfo(symbol(buf
));
5660 charset_table
[i
]->set_ascii_code(i
);
5662 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
5664 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
5665 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
5666 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
5667 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
5668 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
5669 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
5670 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
5671 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
5672 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
5673 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
5674 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
5675 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
5676 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5677 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5678 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5679 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5680 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
5681 page_character
= charset_table
['%'];
5684 static void init_hpf_code_table()
5686 for (int i
= 0; i
< 256; i
++)
5687 hpf_code_table
[i
] = i
;
5690 static void do_translate(int translate_transparent
, int translate_input
)
5693 while (!tok
.newline() && !tok
.eof()) {
5695 // This is a really bizarre troff feature.
5697 translate_space_to_dummy
= tok
.dummy();
5698 if (tok
.newline() || tok
.eof())
5703 charinfo
*ci1
= tok
.get_char(1);
5707 if (tok
.newline() || tok
.eof()) {
5708 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5709 translate_transparent
);
5713 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5714 translate_transparent
);
5715 else if (tok
.stretchable_space())
5716 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
5717 translate_transparent
);
5718 else if (tok
.dummy())
5719 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
5720 translate_transparent
);
5721 else if (tok
.hyphen_indicator())
5722 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
5723 translate_transparent
);
5725 charinfo
*ci2
= tok
.get_char(1);
5729 ci1
->set_translation(0, translate_transparent
, translate_input
);
5731 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
5743 void translate_no_transparent()
5748 void translate_input()
5756 if (get_integer(&flags
))
5758 charinfo
*ci
= tok
.get_char(1);
5760 charinfo
*tem
= ci
->get_translation();
5763 ci
->set_flags(flags
);
5770 void hyphenation_code()
5773 while (!tok
.newline() && !tok
.eof()) {
5774 charinfo
*ci
= tok
.get_char(1);
5779 unsigned char c
= tok
.ch();
5781 error("hyphenation code must be ordinary character");
5785 error("hyphenation code cannot be digit");
5788 ci
->set_hyphenation_code(c
);
5789 if (ci
->get_translation()
5790 && ci
->get_translation()->get_translation_input())
5791 ci
->get_translation()->set_hyphenation_code(c
);
5798 void hyphenation_patterns_file_code()
5801 while (!tok
.newline() && !tok
.eof()) {
5803 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
5805 error("missing output hyphenation code");
5808 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
5809 hpf_code_table
[n1
] = n2
;
5813 error("output hyphenation code must be integer in the range 0..255");
5818 error("input hyphenation code must be integer in the range 0..255");
5825 charinfo
*token::get_char(int required
)
5827 if (type
== TOKEN_CHAR
)
5828 return charset_table
[c
];
5829 if (type
== TOKEN_SPECIAL
)
5830 return get_charinfo(nm
);
5831 if (type
== TOKEN_NUMBERED_CHAR
)
5832 return get_charinfo_by_number(val
);
5833 if (type
== TOKEN_ESCAPE
) {
5834 if (escape_char
!= 0)
5835 return charset_table
[escape_char
];
5837 error("`\\e' used while no current escape character");
5842 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
5843 warning(WARN_MISSING
, "missing normal or special character");
5845 error("normal or special character expected (got %1)", description());
5850 charinfo
*get_optional_char()
5854 charinfo
*ci
= tok
.get_char();
5856 check_missing_character();
5862 void check_missing_character()
5864 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
5865 error("normal or special character expected (got %1): "
5866 "treated as missing",
5872 int token::add_to_node_list(node
**pp
)
5879 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
5885 if (escape_char
!= 0)
5886 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
5888 case TOKEN_HYPHEN_INDICATOR
:
5889 *pp
= (*pp
)->add_discretionary_hyphen();
5891 case TOKEN_ITALIC_CORRECTION
:
5892 *pp
= (*pp
)->add_italic_correction(&w
);
5894 case TOKEN_LEFT_BRACE
:
5896 case TOKEN_MARK_INPUT
:
5897 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
5903 case TOKEN_NUMBERED_CHAR
:
5904 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
5906 case TOKEN_RIGHT_BRACE
:
5909 n
= new hmotion_node(curenv
->get_space_width());
5912 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
5914 case TOKEN_STRETCHABLE_SPACE
:
5915 n
= new unbreakable_space_node(curenv
->get_space_width());
5917 case TOKEN_TRANSPARENT_DUMMY
:
5918 n
= new transparent_dummy_node
;
5920 case TOKEN_ZERO_WIDTH_BREAK
:
5921 n
= new space_node(H0
);
5923 n
->is_escape_colon();
5935 void token::process()
5937 if (possibly_handle_first_page_transition())
5940 case TOKEN_BACKSPACE
:
5941 curenv
->add_node(new hmotion_node(-curenv
->get_space_width()));
5944 curenv
->add_char(charset_table
[c
]);
5947 curenv
->add_node(new dummy_node
);
5956 if (escape_char
!= 0)
5957 curenv
->add_char(charset_table
[escape_char
]);
5959 case TOKEN_BEGIN_TRAP
:
5960 case TOKEN_END_TRAP
:
5961 case TOKEN_PAGE_EJECTOR
:
5962 // these are all handled in process_input_stack()
5964 case TOKEN_HYPHEN_INDICATOR
:
5965 curenv
->add_hyphen_indicator();
5967 case TOKEN_INTERRUPT
:
5968 curenv
->interrupt();
5970 case TOKEN_ITALIC_CORRECTION
:
5971 curenv
->add_italic_correction();
5974 curenv
->handle_tab(1);
5976 case TOKEN_LEFT_BRACE
:
5978 case TOKEN_MARK_INPUT
:
5979 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
5985 curenv
->add_node(nd
);
5988 case TOKEN_NUMBERED_CHAR
:
5989 curenv
->add_char(get_charinfo_by_number(val
));
5992 // handled in process_input_stack()
5994 case TOKEN_RIGHT_BRACE
:
6000 curenv
->add_char(get_charinfo(nm
));
6005 case TOKEN_STRETCHABLE_SPACE
:
6006 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width()));
6009 curenv
->handle_tab(0);
6011 case TOKEN_TRANSPARENT
:
6013 case TOKEN_TRANSPARENT_DUMMY
:
6014 curenv
->add_node(new transparent_dummy_node
);
6016 case TOKEN_ZERO_WIDTH_BREAK
:
6018 node
*tmp
= new space_node(H0
);
6019 tmp
->freeze_space();
6020 tmp
->is_escape_colon();
6021 curenv
->add_node(tmp
);
6029 class nargs_reg
: public reg
{
6031 const char *get_string();
6034 const char *nargs_reg::get_string()
6036 return i_to_a(input_stack::nargs());
6039 class lineno_reg
: public reg
{
6041 const char *get_string();
6044 const char *lineno_reg::get_string()
6048 if (!input_stack::get_location(0, &file
, &line
))
6050 return i_to_a(line
);
6053 class writable_lineno_reg
: public general_reg
{
6055 writable_lineno_reg();
6056 void set_value(units
);
6057 int get_value(units
*);
6060 writable_lineno_reg::writable_lineno_reg()
6064 int writable_lineno_reg::get_value(units
*res
)
6068 if (!input_stack::get_location(0, &file
, &line
))
6074 void writable_lineno_reg::set_value(units n
)
6076 input_stack::set_location(0, n
);
6079 class filename_reg
: public reg
{
6081 const char *get_string();
6084 const char *filename_reg::get_string()
6088 if (input_stack::get_location(0, &file
, &line
))
6094 class constant_reg
: public reg
{
6097 constant_reg(const char *);
6098 const char *get_string();
6101 constant_reg::constant_reg(const char *p
) : s(p
)
6105 const char *constant_reg::get_string()
6110 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6114 const char *constant_int_reg::get_string()
6119 void abort_request()
6124 else if (tok
.newline())
6127 while ((c
= get_copy(0)) == ' ')
6130 if (c
== EOF
|| c
== '\n')
6131 fputs("User Abort.", stderr
);
6133 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6134 fputs(asciify(c
), stderr
);
6136 fputc('\n', stderr
);
6137 cleanup_and_exit(1);
6143 char *s
= new char[len
];
6145 while ((c
= get_copy(0)) == ' ')
6148 while (c
!= '\n' && c
!= EOF
) {
6149 if (!invalid_input_char(c
)) {
6152 s
= new char[len
*2];
6153 memcpy(s
, tem
, len
);
6173 error(".pi request not allowed in safer mode");
6177 #ifdef POPEN_MISSING
6178 error("pipes not available on this system");
6180 #else /* not POPEN_MISSING */
6182 error("can't pipe: output already started");
6187 if ((pc
= read_string()) == 0)
6188 error("can't pipe to empty command");
6190 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6191 strcpy(s
, pipe_command
);
6194 a_delete pipe_command
;
6201 #endif /* not POPEN_MISSING */
6205 static int system_status
;
6207 void system_request()
6210 error(".sy request not allowed in safer mode");
6214 char *command
= read_string();
6216 error("empty command");
6218 system_status
= system(command
);
6226 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6227 handle_initial_request(COPY_FILE_REQUEST
);
6230 symbol filename
= get_long_name(1);
6231 while (!tok
.newline() && !tok
.eof())
6235 if (!filename
.is_null())
6236 curdiv
->copy_file(filename
.contents());
6244 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6245 handle_initial_request(VJUSTIFY_REQUEST
);
6248 symbol type
= get_long_name(1);
6249 if (!type
.is_null())
6250 curdiv
->vjustify(type
);
6256 void transparent_file()
6258 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6259 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6262 symbol filename
= get_long_name(1);
6263 while (!tok
.newline() && !tok
.eof())
6267 if (!filename
.is_null()) {
6269 FILE *fp
= fopen(filename
.contents(), "r");
6271 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6278 if (invalid_input_char(c
))
6279 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6281 curdiv
->transparent_output(c
);
6286 curdiv
->transparent_output('\n');
6298 page_range(int, int, page_range
*);
6299 int contains(int n
);
6302 page_range::page_range(int i
, int j
, page_range
*p
)
6303 : first(i
), last(j
), next(p
)
6307 int page_range::contains(int n
)
6309 return n
>= first
&& (last
<= 0 || n
<= last
);
6312 page_range
*output_page_list
= 0;
6314 int in_output_page_list(int n
)
6316 if (!output_page_list
)
6318 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
6324 static void parse_output_page_list(char *p
)
6330 else if (csdigit(*p
)) {
6333 i
= i
*10 + *p
++ - '0';
6334 while (csdigit(*p
));
6344 j
= j
*10 + *p
++ - '0';
6345 while (csdigit(*p
));
6351 last_page_number
= -1;
6352 else if (last_page_number
>= 0 && j
> last_page_number
)
6353 last_page_number
= j
;
6354 output_page_list
= new page_range(i
, j
, output_page_list
);
6360 error("bad output page list");
6361 output_page_list
= 0;
6365 static FILE *open_mac_file(const char *mac
, char **path
)
6367 // Try first FOOBAR.tmac, then tmac.FOOBAR
6368 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
6370 strcat(s1
, MACRO_POSTFIX
);
6371 FILE *fp
= mac_path
->open_file(s1
, path
);
6374 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
6375 strcpy(s2
, MACRO_PREFIX
);
6377 fp
= mac_path
->open_file(s2
, path
);
6383 static void process_macro_file(const char *mac
)
6386 FILE *fp
= open_mac_file(mac
, &path
);
6388 fatal("can't find macro file %1", mac
);
6389 const char *s
= symbol(path
).contents();
6391 input_stack::push(new file_iterator(fp
, s
));
6393 process_input_stack();
6396 static void process_startup_file(char *filename
)
6399 search_path
*orig_mac_path
= mac_path
;
6400 mac_path
= &config_macro_path
;
6401 FILE *fp
= mac_path
->open_file(filename
, &path
);
6403 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6406 process_input_stack();
6408 mac_path
= orig_mac_path
;
6413 symbol nm
= get_long_name(1);
6417 while (!tok
.newline() && !tok
.eof())
6420 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
6421 // .mso doesn't (and cannot) go through open_mac_file, so we
6422 // need to do it here manually: If we have tmac.FOOBAR, try
6423 // FOOBAR.tmac and vice versa
6425 const char *fn
= nm
.contents();
6426 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
6427 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
6428 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
6429 strcat(s
, MACRO_POSTFIX
);
6430 fp
= mac_path
->open_file(s
, &path
);
6434 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
6435 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
6436 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
6437 strcpy(s
, MACRO_PREFIX
);
6438 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
6439 fp
= mac_path
->open_file(s
, &path
);
6445 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6449 error("can't find macro file `%1'", nm
.contents());
6454 static void process_input_file(const char *name
)
6457 if (strcmp(name
, "-") == 0) {
6463 fp
= fopen(name
, "r");
6465 fatal("can't open `%1': %2", name
, strerror(errno
));
6467 input_stack::push(new file_iterator(fp
, name
));
6469 process_input_stack();
6472 // make sure the_input is empty before calling this
6474 static int evaluate_expression(const char *expr
, units
*res
)
6476 input_stack::push(make_temp_iterator(expr
));
6478 int success
= get_number(res
, 'u');
6479 while (input_stack::get(0) != EOF
)
6484 static void do_register_assignment(const char *s
)
6486 const char *p
= strchr(s
, '=');
6492 if (evaluate_expression(s
+ 1, &n
))
6493 set_number_reg(buf
, n
);
6496 char *buf
= new char[p
- s
+ 1];
6497 memcpy(buf
, s
, p
- s
);
6500 if (evaluate_expression(p
+ 1, &n
))
6501 set_number_reg(buf
, n
);
6506 static void set_string(const char *name
, const char *value
)
6508 macro
*m
= new macro
;
6509 for (const char *p
= value
; *p
; p
++)
6510 if (!invalid_input_char((unsigned char)*p
))
6512 request_dictionary
.define(name
, m
);
6515 static void do_string_assignment(const char *s
)
6517 const char *p
= strchr(s
, '=');
6522 set_string(buf
, s
+ 1);
6525 char *buf
= new char[p
- s
+ 1];
6526 memcpy(buf
, s
, p
- s
);
6528 set_string(buf
, p
+ 1);
6533 struct string_list
{
6536 string_list(const char *ss
) : s(ss
), next(0) {}
6540 static void prepend_string(const char *s
, string_list
**p
)
6542 string_list
*l
= new string_list(s
);
6548 static void add_string(const char *s
, string_list
**p
)
6552 *p
= new string_list(s
);
6555 void usage(FILE *stream
, const char *prog
)
6558 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6559 " -rcn -Tname -Fdir -Mdir [files...]\n",
6563 int main(int argc
, char **argv
)
6565 program_name
= argv
[0];
6566 static char stderr_buf
[BUFSIZ
];
6567 setbuf(stderr
, stderr_buf
);
6569 string_list
*macros
= 0;
6570 string_list
*register_assignments
= 0;
6571 string_list
*string_assignments
= 0;
6576 int no_rc
= 0; // don't process troffrc and troffrc-end
6577 int next_page_number
;
6579 hresolution
= vresolution
= 1;
6580 // restore $PATH if called from groff
6581 char* groff_path
= getenv("GROFF_PATH__");
6588 if (putenv(strsave(e
.contents())))
6589 fatal("putenv failed");
6591 static const struct option long_options
[] = {
6592 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
6593 { "version", no_argument
, 0, 'v' },
6596 while ((c
= getopt_long(argc
, argv
, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6602 printf("GNU troff (groff) version %s\n", Version_string
);
6609 is_html
= (strcmp(device
, "html") == 0);
6612 compatible_flag
= 1;
6615 disable_color_flag
= 1;
6618 macro_path
.command_line_dir(optarg
);
6619 safer_macro_path
.command_line_dir(optarg
);
6620 config_macro_path
.command_line_dir(optarg
);
6623 font::command_line_font_dir(optarg
);
6626 add_string(optarg
, ¯os
);
6635 enable_warning(optarg
);
6638 disable_warning(optarg
);
6647 ascii_output_flag
= 1;
6650 suppress_output_flag
= 1;
6653 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
6656 error("bad page number");
6659 parse_output_page_list(optarg
);
6662 if (*optarg
== '\0')
6663 error("`-d' requires non-empty argument");
6665 add_string(optarg
, &string_assignments
);
6668 if (*optarg
== '\0')
6669 error("`-r' requires non-empty argument");
6671 add_string(optarg
, ®ister_assignments
);
6674 default_family
= symbol(optarg
);
6680 // silently ignore these
6683 safer_flag
= 0; // unsafe behaviour
6685 case CHAR_MAX
+ 1: // --help
6686 usage(stdout
, argv
[0]);
6690 usage(stderr
, argv
[0]);
6692 break; // never reached
6697 mac_path
= ¯o_path
;
6698 set_string(".T", device
);
6699 init_charset_table();
6700 init_hpf_code_table();
6701 if (!font::load_desc())
6702 fatal("sorry, I can't continue");
6703 units_per_inch
= font::res
;
6704 hresolution
= font::hor
;
6705 vresolution
= font::vert
;
6706 sizescale
= font::sizescale
;
6707 tcommand_flag
= font::tcommand
;
6708 warn_scale
= (double)units_per_inch
;
6709 warn_scaling_indicator
= 'i';
6710 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
6711 default_family
= symbol(font::family
);
6712 font_size::init_size_table(font::sizes
);
6715 if (font::style_table
) {
6716 for (i
= 0; font::style_table
[i
]; i
++)
6717 mount_style(j
++, symbol(font::style_table
[i
]));
6719 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
6720 // In the DESC file a font name of 0 (zero) means leave this
6722 if (strcmp(font::font_name_table
[i
], "0") != 0)
6723 mount_font(j
, symbol(font::font_name_table
[i
]));
6724 curdiv
= topdiv
= new top_level_diversion
;
6726 topdiv
->set_next_page_number(next_page_number
);
6727 init_input_requests();
6728 init_env_requests();
6729 init_div_requests();
6731 init_column_requests();
6733 init_node_requests();
6734 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
6736 init_reg_requests();
6737 init_hyphen_requests();
6738 init_environments();
6739 while (string_assignments
) {
6740 do_string_assignment(string_assignments
->s
);
6741 string_list
*tem
= string_assignments
;
6742 string_assignments
= string_assignments
->next
;
6745 while (register_assignments
) {
6746 do_register_assignment(register_assignments
->s
);
6747 string_list
*tem
= register_assignments
;
6748 register_assignments
= register_assignments
->next
;
6752 process_startup_file(INITIAL_STARTUP_FILE
);
6754 process_macro_file(macros
->s
);
6755 string_list
*tem
= macros
;
6756 macros
= macros
->next
;
6760 process_startup_file(FINAL_STARTUP_FILE
);
6761 for (i
= optind
; i
< argc
; i
++)
6762 process_input_file(argv
[i
]);
6763 if (optind
>= argc
|| iflag
)
6764 process_input_file("-");
6766 return 0; // not reached
6772 if (has_arg() && get_integer(&n
)) {
6773 if (n
& ~WARN_TOTAL
) {
6774 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
6780 warning_mask
= WARN_TOTAL
;
6784 static void init_registers()
6786 #ifdef LONG_FOR_TIME_T
6788 #else /* not LONG_FOR_TIME_T */
6790 #endif /* not LONG_FOR_TIME_T */
6792 // Use struct here to work around misfeature in old versions of g++.
6793 struct tm
*tt
= localtime(&t
);
6794 set_number_reg("dw", int(tt
->tm_wday
+ 1));
6795 set_number_reg("dy", int(tt
->tm_mday
));
6796 set_number_reg("mo", int(tt
->tm_mon
+ 1));
6797 set_number_reg("year", int(1900 + tt
->tm_year
));
6798 set_number_reg("yr", int(tt
->tm_year
));
6799 set_number_reg("$$", getpid());
6800 number_reg_dictionary
.define(".A",
6801 new constant_reg(ascii_output_flag
6807 * registers associated with \O
6810 static int output_reg_minx_contents
= -1;
6811 static int output_reg_miny_contents
= -1;
6812 static int output_reg_maxx_contents
= -1;
6813 static int output_reg_maxy_contents
= -1;
6815 void check_output_limits(int x
, int y
)
6817 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
6818 output_reg_minx_contents
= x
;
6819 if (x
> output_reg_maxx_contents
)
6820 output_reg_maxx_contents
= x
;
6821 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
6822 output_reg_miny_contents
= y
;
6823 if (y
> output_reg_maxy_contents
)
6824 output_reg_maxy_contents
= y
;
6827 void reset_output_registers(int miny
)
6829 // fprintf(stderr, "reset_output_registers\n");
6830 output_reg_minx_contents
= -1;
6831 output_reg_miny_contents
= -1;
6832 output_reg_maxx_contents
= -1;
6833 output_reg_maxy_contents
= -1;
6836 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
6838 *minx
= output_reg_minx_contents
;
6839 *miny
= output_reg_miny_contents
;
6840 *maxx
= output_reg_maxx_contents
;
6841 *maxy
= output_reg_maxy_contents
;
6844 void init_input_requests()
6846 init_request("ds", define_string
);
6847 init_request("as", append_string
);
6848 init_request("ds1", define_nocomp_string
);
6849 init_request("as1", append_nocomp_string
);
6850 init_request("de", define_macro
);
6851 init_request("dei", define_indirect_macro
);
6852 init_request("de1", define_nocomp_macro
);
6853 init_request("am", append_macro
);
6854 init_request("ami", append_indirect_macro
);
6855 init_request("am1", append_nocomp_macro
);
6856 init_request("ig", ignore
);
6857 init_request("rm", remove_macro
);
6858 init_request("rn", rename_macro
);
6859 init_request("nop", nop_request
);
6860 init_request("if", if_request
);
6861 init_request("ie", if_else_request
);
6862 init_request("el", else_request
);
6863 init_request("so", source
);
6864 init_request("nx", next_file
);
6865 init_request("pm", print_macros
);
6866 init_request("eo", escape_off
);
6867 init_request("ec", set_escape_char
);
6868 init_request("ecs", save_escape_char
);
6869 init_request("ecr", restore_escape_char
);
6870 init_request("pc", set_page_character
);
6871 init_request("tm", terminal
);
6872 init_request("tm1", terminal1
);
6873 init_request("tmc", terminal_continue
);
6874 init_request("ex", exit_request
);
6875 init_request("return", return_macro_request
);
6876 init_request("em", end_macro
);
6877 init_request("blm", blank_line_macro
);
6878 init_request("tr", translate
);
6879 init_request("trnt", translate_no_transparent
);
6880 init_request("trin", translate_input
);
6881 init_request("ab", abort_request
);
6882 init_request("pi", pipe_output
);
6883 init_request("cf", copy_file
);
6884 init_request("sy", system_request
);
6885 init_request("lf", line_file
);
6886 init_request("cflags", char_flags
);
6887 init_request("shift", shift
);
6888 init_request("rd", read_request
);
6889 init_request("cp", compatible
);
6890 init_request("char", define_character
);
6891 init_request("fchar", define_fallback_character
);
6892 init_request("rchar", remove_character
);
6893 init_request("hcode", hyphenation_code
);
6894 init_request("hpfcode", hyphenation_patterns_file_code
);
6895 init_request("while", while_request
);
6896 init_request("break", while_break_request
);
6897 init_request("continue", while_continue_request
);
6898 init_request("als", alias_macro
);
6899 init_request("backtrace", backtrace_request
);
6900 init_request("chop", chop_macro
);
6901 init_request("substring", substring_macro
);
6902 init_request("length", length_macro
);
6903 init_request("asciify", asciify_macro
);
6904 init_request("unformat", unformat_macro
);
6905 init_request("warn", warn_request
);
6906 init_request("open", open_request
);
6907 init_request("opena", opena_request
);
6908 init_request("close", close_request
);
6909 init_request("write", write_request
);
6910 init_request("writec", write_request_continue
);
6911 init_request("writem", write_macro_request
);
6912 init_request("trf", transparent_file
);
6913 #ifdef WIDOW_CONTROL
6914 init_request("fpl", flush_pending_lines
);
6915 #endif /* WIDOW_CONTROL */
6916 init_request("nroff", nroff_request
);
6917 init_request("troff", troff_request
);
6919 init_request("vj", vjustify
);
6921 init_request("mso", macro_source
);
6922 init_request("do", do_request
);
6923 #ifndef POPEN_MISSING
6924 init_request("pso", pipe_source
);
6925 #endif /* not POPEN_MISSING */
6926 init_request("psbb", ps_bbox_request
);
6927 init_request("defcolor", define_color
);
6928 init_request("warnscale", warnscale_request
);
6929 init_request("spreadwarn", spreadwarn_request
);
6930 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
6931 number_reg_dictionary
.define("slimit",
6932 new variable_reg(&input_stack::limit
));
6933 number_reg_dictionary
.define(".$", new nargs_reg
);
6934 number_reg_dictionary
.define(".c", new lineno_reg
);
6935 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
6936 number_reg_dictionary
.define(".F", new filename_reg
);
6937 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
6938 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
6939 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
6940 number_reg_dictionary
.define(".R", new constant_reg("10000"));
6941 extern const char *major_version
;
6942 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
6943 extern const char *minor_version
;
6944 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
6945 extern const char *revision
;
6946 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
6947 number_reg_dictionary
.define(".g", new constant_reg("1"));
6948 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
6949 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
6950 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
6951 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
6952 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
6953 number_reg_dictionary
.define("opminx",
6954 new variable_reg(&output_reg_minx_contents
));
6955 number_reg_dictionary
.define("opminy",
6956 new variable_reg(&output_reg_miny_contents
));
6957 number_reg_dictionary
.define("opmaxx",
6958 new variable_reg(&output_reg_maxx_contents
));
6959 number_reg_dictionary
.define("opmaxy",
6960 new variable_reg(&output_reg_maxy_contents
));
6963 object_dictionary
request_dictionary(501);
6965 void init_request(const char *s
, REQUEST_FUNCP f
)
6967 request_dictionary
.define(s
, new request(f
));
6970 static request_or_macro
*lookup_request(symbol nm
)
6972 assert(!nm
.is_null());
6973 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
6975 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
6977 request_dictionary
.define(nm
, p
);
6982 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
6984 // Don't interpret character definitions in compatible mode.
6985 int old_compatible_flag
= compatible_flag
;
6986 compatible_flag
= 0;
6987 int old_escape_char
= escape_char
;
6989 macro
*mac
= ci
->set_macro(0);
6991 environment
*oldenv
= curenv
;
6992 environment
env(envp
);
6994 curenv
->set_composite();
6995 token old_tok
= tok
;
6996 input_stack::add_boundary();
6997 string_iterator
*si
=
6998 new string_iterator(*mac
, "composite character", ci
->nm
);
6999 input_stack::push(si
);
7000 // we don't use process_input_stack, because we don't want to recognise
7006 if (tok
.newline()) {
7007 error("composite character mustn't contain newline");
7015 node
*n
= curenv
->extract_output_line();
7016 input_stack::remove_boundary();
7020 compatible_flag
= old_compatible_flag
;
7021 escape_char
= old_escape_char
;
7025 static node
*read_draw_node()
7029 if (!start
.delimiter(1)){
7032 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7037 error("missing argument");
7039 unsigned char type
= tok
.ch();
7042 hvpair
*point
= new hvpair
[maxpoints
];
7047 for (i
= 0; tok
!= start
; i
++) {
7048 if (i
== maxpoints
) {
7049 hvpair
*oldpoint
= point
;
7050 point
= new hvpair
[maxpoints
*2];
7051 for (int j
= 0; j
< maxpoints
; j
++)
7052 point
[j
] = oldpoint
[j
];
7056 if (!get_hunits(&point
[i
].h
,
7057 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7068 if (!get_vunits(&point
[i
].v
, 'v')) {
7074 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7079 if (npoints
!= 1 || no_last_v
) {
7080 error("two arguments needed for line");
7085 if (npoints
!= 1 || !no_last_v
) {
7086 error("one argument needed for circle");
7092 if (npoints
!= 1 || no_last_v
) {
7093 error("two arguments needed for ellipse");
7098 if (npoints
!= 2 || no_last_v
) {
7099 error("four arguments needed for arc");
7105 error("even number of arguments needed for spline");
7108 if (npoints
!= 1 || !no_last_v
) {
7109 error("one argument needed for gray shade");
7114 // silently pass it through
7117 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7118 curenv
->get_font_size());
7133 } warning_table
[] = {
7134 { "char", WARN_CHAR
},
7135 { "range", WARN_RANGE
},
7136 { "break", WARN_BREAK
},
7137 { "delim", WARN_DELIM
},
7139 { "scale", WARN_SCALE
},
7140 { "number", WARN_NUMBER
},
7141 { "syntax", WARN_SYNTAX
},
7142 { "tab", WARN_TAB
},
7143 { "right-brace", WARN_RIGHT_BRACE
},
7144 { "missing", WARN_MISSING
},
7145 { "input", WARN_INPUT
},
7146 { "escape", WARN_ESCAPE
},
7147 { "space", WARN_SPACE
},
7148 { "font", WARN_FONT
},
7150 { "mac", WARN_MAC
},
7151 { "reg", WARN_REG
},
7153 { "color", WARN_COLOR
},
7154 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7155 { "w", WARN_TOTAL
},
7156 { "default", DEFAULT_WARNING_MASK
},
7159 static int lookup_warning(const char *name
)
7161 for (unsigned int i
= 0;
7162 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7164 if (strcmp(name
, warning_table
[i
].name
) == 0)
7165 return warning_table
[i
].mask
;
7169 static void enable_warning(const char *name
)
7171 int mask
= lookup_warning(name
);
7173 warning_mask
|= mask
;
7175 error("unknown warning `%1'", name
);
7178 static void disable_warning(const char *name
)
7180 int mask
= lookup_warning(name
);
7182 warning_mask
&= ~mask
;
7184 error("unknown warning `%1'", name
);
7187 static void copy_mode_error(const char *format
,
7193 static const char prefix
[] = "(in ignored input) ";
7194 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7197 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7201 error(format
, arg1
, arg2
, arg3
);
7204 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7206 static void do_error(error_type type
,
7212 const char *filename
;
7214 if (inhibit_errors
&& type
< FATAL
)
7217 input_stack::backtrace();
7218 if (!get_file_line(&filename
, &lineno
))
7221 errprint("%1:%2: ", filename
, lineno
);
7222 else if (program_name
)
7223 fprintf(stderr
, "%s: ", program_name
);
7226 fputs("fatal error: ", stderr
);
7231 fputs("warning: ", stderr
);
7233 case OUTPUT_WARNING
:
7234 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7235 fprintf(stderr
, "warning [p %d, %.1f%c",
7236 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7237 if (topdiv
!= curdiv
) {
7238 double fromtop1
= curdiv
->get_vertical_position().to_units()
7240 fprintf(stderr
, ", div `%s', %.1f%c",
7241 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7243 fprintf(stderr
, "]: ");
7246 errprint(format
, arg1
, arg2
, arg3
);
7247 fputc('\n', stderr
);
7250 cleanup_and_exit(1);
7253 int warning(warning_type t
,
7259 if ((t
& warning_mask
) != 0) {
7260 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
7267 int output_warning(warning_type t
,
7273 if ((t
& warning_mask
) != 0) {
7274 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
7281 void error(const char *format
,
7286 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
7289 void fatal(const char *format
,
7294 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
7297 void fatal_with_file_and_line(const char *filename
, int lineno
,
7303 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
7304 errprint(format
, arg1
, arg2
, arg3
);
7305 fputc('\n', stderr
);
7307 cleanup_and_exit(1);
7310 void error_with_file_and_line(const char *filename
, int lineno
,
7316 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
7317 errprint(format
, arg1
, arg2
, arg3
);
7318 fputc('\n', stderr
);
7322 dictionary
charinfo_dictionary(501);
7324 charinfo
*get_charinfo(symbol nm
)
7326 void *p
= charinfo_dictionary
.lookup(nm
);
7328 return (charinfo
*)p
;
7329 charinfo
*cp
= new charinfo(nm
);
7330 (void)charinfo_dictionary
.lookup(nm
, cp
);
7334 int charinfo::next_index
= 0;
7336 charinfo::charinfo(symbol s
)
7337 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
7338 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7339 not_found(0), transparent_translate(1), translate_input(0),
7342 index
= next_index
++;
7345 void charinfo::set_hyphenation_code(unsigned char c
)
7347 hyphenation_code
= c
;
7350 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
7354 if (hyphenation_code
!= 0)
7355 ci
->set_hyphenation_code(hyphenation_code
);
7356 if (asciify_code
!= 0)
7357 ci
->set_asciify_code(asciify_code
);
7358 else if (ascii_code
!= 0)
7359 ci
->set_asciify_code(ascii_code
);
7360 ci
->set_translation_input();
7362 special_translation
= TRANSLATE_NONE
;
7363 transparent_translate
= tt
;
7366 void charinfo::set_special_translation(int c
, int tt
)
7368 special_translation
= c
;
7370 transparent_translate
= tt
;
7373 void charinfo::set_ascii_code(unsigned char c
)
7378 void charinfo::set_asciify_code(unsigned char c
)
7383 macro
*charinfo::set_macro(macro
*m
, int f
)
7391 void charinfo::set_number(int n
)
7397 int charinfo::get_number()
7399 assert(flags
& NUMBERED
);
7403 symbol
UNNAMED_SYMBOL("---");
7405 // For numbered characters not between 0 and 255, we make a symbol out
7406 // of the number and store them in this dictionary.
7408 dictionary
numbered_charinfo_dictionary(11);
7410 charinfo
*get_charinfo_by_number(int n
)
7412 static charinfo
*number_table
[256];
7414 if (n
>= 0 && n
< 256) {
7415 charinfo
*ci
= number_table
[n
];
7417 ci
= new charinfo(UNNAMED_SYMBOL
);
7419 number_table
[n
] = ci
;
7424 symbol
ns(i_to_a(n
));
7425 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
7427 ci
= new charinfo(UNNAMED_SYMBOL
);
7429 numbered_charinfo_dictionary
.lookup(ns
, ci
);
7435 int font::name_to_index(const char *nm
)
7439 ci
= charset_table
[nm
[0] & 0xff];
7440 else if (nm
[0] == '\\' && nm
[2] == 0)
7441 ci
= get_charinfo(symbol(nm
+ 1));
7443 ci
= get_charinfo(symbol(nm
));
7447 return ci
->get_index();
7450 int font::number_to_index(int n
)
7452 return get_charinfo_by_number(n
)->get_index();