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
&);
89 void transparent_file();
90 void process_input_stack();
92 const char *program_name
= 0;
95 int disable_color_flag
= 0;
96 static int backtrace_flag
= 0;
98 char *pipe_command
= 0;
100 charinfo
*charset_table
[256];
101 unsigned char hpf_code_table
[256];
103 static int warning_mask
= DEFAULT_WARNING_MASK
;
104 static int inhibit_errors
= 0;
105 static int ignoring
= 0;
107 static void enable_warning(const char *);
108 static void disable_warning(const char *);
110 static int escape_char
= '\\';
111 static symbol end_macro_name
;
112 static symbol blank_line_macro_name
;
113 static int compatible_flag
= 0;
114 int ascii_output_flag
= 0;
115 int suppress_output_flag
= 0;
117 int begin_level
= 0; // number of nested .begin requests
119 int have_input
= 0; // whether \f, \H, \R, \s, or \S has
120 // been processed in token::next()
121 int tcommand_flag
= 0;
122 int safer_flag
= 1; // safer by default
124 double spread_limit
= -3.0 - 1.0; // negative means deactivated
127 char warn_scaling_indicator
;
129 search_path
*mac_path
= &safer_macro_path
;
131 static int get_copy(node
**, int = 0);
132 static void copy_mode_error(const char *,
133 const errarg
& = empty_errarg
,
134 const errarg
& = empty_errarg
,
135 const errarg
& = empty_errarg
);
137 static symbol
read_escape_name(int no_empty
= 1);
138 static symbol
read_long_escape_name(int no_empty
= 1);
139 static void interpolate_string(symbol
);
140 static void interpolate_macro(symbol
);
141 static void interpolate_number_format(symbol
);
142 static void interpolate_environment_variable(symbol
);
144 static void interpolate_arg(symbol
);
145 static request_or_macro
*lookup_request(symbol
);
146 static int get_delim_number(units
*, int);
147 static int get_delim_number(units
*, int, units
);
148 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
149 static int read_size(int *);
150 static symbol
get_delim_name();
151 static void init_registers();
152 static void trapping_blank_line();
154 struct input_iterator
;
155 input_iterator
*make_temp_iterator(const char *);
156 const char *input_char_description(int);
159 void set_escape_char()
163 error("bad escape character");
167 escape_char
= tok
.ch();
180 static int saved_escape_char
= '\\';
182 void save_escape_char()
184 saved_escape_char
= escape_char
;
188 void restore_escape_char()
190 escape_char
= saved_escape_char
;
194 class input_iterator
{
197 virtual ~input_iterator() {}
199 friend class input_stack
;
201 const unsigned char *ptr
;
202 const unsigned char *eptr
;
203 input_iterator
*next
;
205 virtual int fill(node
**);
207 virtual int has_args() { return 0; }
208 virtual int nargs() { return 0; }
209 virtual input_iterator
*get_arg(int) { return 0; }
210 virtual int get_location(int, const char **, int *) { return 0; }
211 virtual void backtrace() {}
212 virtual int set_location(const char *, int) { return 0; }
213 virtual int next_file(FILE *, const char *) { return 0; }
214 virtual void shift(int) {}
215 virtual int is_boundary() {return 0; }
216 virtual int internal_level() { return 0; }
217 virtual int is_file() { return 0; }
218 virtual int is_macro() { return 0; }
219 virtual void save_compatible_flag(int) {}
220 virtual int get_compatible_flag() { return 0; }
223 input_iterator::input_iterator()
228 int input_iterator::fill(node
**)
233 int input_iterator::peek()
238 inline int input_iterator::get(node
**p
)
240 return ptr
< eptr
? *ptr
++ : fill(p
);
243 class input_boundary
: public input_iterator
{
245 int is_boundary() { return 1; }
248 class input_return_boundary
: public input_iterator
{
250 int is_boundary() { return 2; }
253 class file_iterator
: public input_iterator
{
256 const char *filename
;
259 int suppress_newline_flag
; // used by html
261 enum { BUF_SIZE
= 512 };
262 unsigned char buf
[BUF_SIZE
];
265 file_iterator(FILE *, const char *, int = 0);
269 int get_location(int, const char **, int *);
271 int set_location(const char *, int);
272 int next_file(FILE *, const char *);
276 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
277 : fp(f
), lineno(1), filename(fn
), popened(po
),
278 newline_flag(0), suppress_newline_flag(0), seen_escape(0)
280 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
283 the_output
->put_filename(fn
);
287 file_iterator::~file_iterator()
292 void file_iterator::close()
296 #ifndef POPEN_MISSING
299 #endif /* not POPEN_MISSING */
304 int file_iterator::is_file()
309 int file_iterator::next_file(FILE *f
, const char *s
)
316 suppress_newline_flag
= 0;
324 int file_iterator::fill(node
**)
326 if (newline_flag
&& !suppress_newline_flag
) {
327 curenv
->add_html_tag_eol();
331 suppress_newline_flag
= 0;
332 unsigned char *p
= buf
;
334 unsigned char *e
= p
+ BUF_SIZE
;
339 if (invalid_input_char(c
))
340 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
344 if (seen_escape
&& is_html
)
345 suppress_newline_flag
= 1;
350 seen_escape
= (c
== '\\');
363 int file_iterator::peek()
366 while (invalid_input_char(c
)) {
367 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
375 int file_iterator::get_location(int /*allow_macro*/,
376 const char **filenamep
, int *linenop
)
379 if (filename
!= 0 && strcmp(filename
, "-") == 0)
380 *filenamep
= "<standard input>";
382 *filenamep
= filename
;
386 void file_iterator::backtrace()
388 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
389 popened
? "process" : "file");
392 int file_iterator::set_location(const char *f
, int ln
)
398 the_output
->put_filename(f
);
404 input_iterator nil_iterator
;
408 static int get(node
**);
410 static void push(input_iterator
*);
411 static input_iterator
*get_arg(int);
413 static int get_location(int, const char **, int *);
414 static int set_location(const char *, int);
415 static void backtrace();
416 static void backtrace_all();
417 static void next_file(FILE *, const char *);
418 static void end_file();
419 static void shift(int n
);
420 static void add_boundary();
421 static void add_return_boundary();
422 static int is_return_boundary();
423 static void remove_boundary();
424 static int get_level();
426 static void pop_macro();
427 static void save_compatible_flag(int);
428 static int get_compatible_flag();
432 static input_iterator
*top
;
435 static int finish_get(node
**);
436 static int finish_peek();
439 input_iterator
*input_stack::top
= &nil_iterator
;
440 int input_stack::level
= 0;
441 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
443 inline int input_stack::get_level()
445 return level
+ top
->internal_level();
448 inline int input_stack::get(node
**np
)
450 return (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
453 int input_stack::finish_get(node
**np
)
456 int c
= top
->fill(np
);
457 if (c
!= EOF
|| top
->is_boundary())
459 if (top
== &nil_iterator
)
461 input_iterator
*tem
= top
;
465 if (top
->ptr
< top
->eptr
)
472 inline int input_stack::peek()
474 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
477 int input_stack::finish_peek()
481 if (c
!= EOF
|| top
->is_boundary())
483 if (top
== &nil_iterator
)
485 input_iterator
*tem
= top
;
489 if (top
->ptr
< top
->eptr
)
496 void input_stack::add_boundary()
498 push(new input_boundary
);
501 void input_stack::add_return_boundary()
503 push(new input_return_boundary
);
506 int input_stack::is_return_boundary()
508 return top
->is_boundary() == 2;
511 void input_stack::remove_boundary()
513 assert(top
->is_boundary());
514 input_iterator
*temp
= top
->next
;
520 void input_stack::push(input_iterator
*in
)
524 if (++level
> limit
&& limit
> 0)
525 fatal("input stack limit exceeded (probable infinite loop)");
530 input_iterator
*input_stack::get_arg(int i
)
533 for (p
= top
; p
!= 0; p
= p
->next
)
535 return p
->get_arg(i
);
539 void input_stack::shift(int n
)
541 for (input_iterator
*p
= top
; p
; p
= p
->next
)
548 int input_stack::nargs()
550 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
556 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
558 for (input_iterator
*p
= top
; p
; p
= p
->next
)
559 if (p
->get_location(allow_macro
, filenamep
, linenop
))
564 void input_stack::backtrace()
568 // only backtrace down to (not including) the topmost file
569 for (input_iterator
*p
= top
;
570 p
&& !p
->get_location(0, &f
, &n
);
575 void input_stack::backtrace_all()
577 for (input_iterator
*p
= top
; p
; p
= p
->next
)
581 int input_stack::set_location(const char *filename
, int lineno
)
583 for (input_iterator
*p
= top
; p
; p
= p
->next
)
584 if (p
->set_location(filename
, lineno
))
589 void input_stack::next_file(FILE *fp
, const char *s
)
592 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
593 if ((*pp
)->next_file(fp
, s
))
595 if (++level
> limit
&& limit
> 0)
596 fatal("input stack limit exceeded");
597 *pp
= new file_iterator(fp
, s
);
598 (*pp
)->next
= &nil_iterator
;
601 void input_stack::end_file()
603 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
604 if ((*pp
)->is_file()) {
605 input_iterator
*tem
= *pp
;
613 void input_stack::clear()
616 while (top
!= &nil_iterator
) {
617 if (top
->is_boundary())
619 input_iterator
*tem
= top
;
624 // Keep while_request happy.
625 for (; nboundaries
> 0; --nboundaries
)
626 add_return_boundary();
629 void input_stack::pop_macro()
634 if (top
->next
== &nil_iterator
)
636 if (top
->is_boundary())
638 is_macro
= top
->is_macro();
639 input_iterator
*tem
= top
;
644 // Keep while_request happy.
645 for (; nboundaries
> 0; --nboundaries
)
646 add_return_boundary();
649 inline void input_stack::save_compatible_flag(int f
)
651 top
->save_compatible_flag(f
);
654 inline int input_stack::get_compatible_flag()
656 return top
->get_compatible_flag();
659 void backtrace_request()
661 input_stack::backtrace_all();
668 symbol nm
= get_long_name(0);
669 while (!tok
.newline() && !tok
.eof())
672 input_stack::end_file();
675 FILE *fp
= fopen(nm
.contents(), "r");
677 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
679 input_stack::next_file(fp
, nm
.contents());
687 if (!has_arg() || !get_integer(&n
))
689 input_stack::shift(n
);
693 static int get_char_for_escape_name()
698 copy_mode_error("end of input in escape name");
701 if (!invalid_input_char(c
))
706 input_stack::push(make_temp_iterator("\n"));
711 copy_mode_error("%1 is not allowed in an escape name",
712 input_char_description(c
));
718 static symbol
read_two_char_escape_name()
721 buf
[0] = get_char_for_escape_name();
722 if (buf
[0] != '\0') {
723 buf
[1] = get_char_for_escape_name();
732 static symbol
read_long_escape_name(int no_empty
)
734 int start_level
= input_stack::get_level();
735 char abuf
[ABUF_SIZE
];
737 int buf_size
= ABUF_SIZE
;
740 int c
= get_char_for_escape_name();
746 if (i
+ 2 > buf_size
) {
748 buf
= new char[ABUF_SIZE
*2];
749 memcpy(buf
, abuf
, buf_size
);
750 buf_size
= ABUF_SIZE
*2;
754 buf
= new char[buf_size
*2];
755 memcpy(buf
, old_buf
, buf_size
);
760 if (c
== ']' && input_stack::get_level() == start_level
)
768 copy_mode_error("empty escape name");
780 static symbol
read_escape_name(int no_empty
)
782 int c
= get_char_for_escape_name();
786 return read_two_char_escape_name();
787 if (c
== '[' && !compatible_flag
)
788 return read_long_escape_name(no_empty
);
795 static symbol
read_increment_and_escape_name(int *incp
)
797 int c
= get_char_for_escape_name();
804 return read_two_char_escape_name();
807 return read_escape_name();
810 return read_escape_name();
812 if (!compatible_flag
) {
814 return read_long_escape_name();
825 static int get_copy(node
**nd
, int defining
)
828 int c
= input_stack::get(nd
);
829 if (c
== ESCAPE_NEWLINE
) {
833 c
= input_stack::get(nd
);
834 } while (c
== ESCAPE_NEWLINE
);
836 if (c
!= escape_char
|| escape_char
<= 0)
838 c
= input_stack::peek();
843 (void)input_stack::get(0);
844 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
847 case '#': // Like \" but newline is ignored.
848 (void)input_stack::get(0);
849 while ((c
= input_stack::get(0)) != '\n')
855 (void)input_stack::get(0);
856 symbol s
= read_escape_name();
857 if (!(s
.is_null() || s
.is_empty()))
863 (void)input_stack::get(0);
864 symbol s
= read_escape_name();
865 if (!(s
.is_null() || s
.is_empty()))
866 interpolate_string(s
);
870 (void)input_stack::get(0);
873 (void)input_stack::get(0);
876 (void)input_stack::get(0);
880 (void)input_stack::get(0);
882 symbol s
= read_increment_and_escape_name(&inc
);
883 if (!(s
.is_null() || s
.is_empty()))
884 interpolate_number_reg(s
, inc
);
889 (void)input_stack::get(0);
890 symbol s
= read_escape_name();
891 if (!(s
.is_null() || s
.is_empty()))
892 interpolate_number_format(s
);
896 (void)input_stack::get(0);
900 (void)input_stack::get(0);
901 symbol s
= read_escape_name();
902 if (!(s
.is_null() || s
.is_empty()))
903 interpolate_environment_variable(s
);
907 (void)input_stack::get(0);
909 return ESCAPE_NEWLINE
;
912 (void)input_stack::get(0);
915 (void)input_stack::get(0);
918 (void)input_stack::get(0);
921 (void)input_stack::get(0);
924 (void)input_stack::get(0);
925 return ESCAPE_CIRCUMFLEX
;
927 (void)input_stack::get(0);
928 return ESCAPE_LEFT_BRACE
;
930 (void)input_stack::get(0);
931 return ESCAPE_RIGHT_BRACE
;
933 (void)input_stack::get(0);
934 return ESCAPE_LEFT_QUOTE
;
936 (void)input_stack::get(0);
937 return ESCAPE_RIGHT_QUOTE
;
939 (void)input_stack::get(0);
940 return ESCAPE_HYPHEN
;
942 (void)input_stack::get(0);
943 return ESCAPE_UNDERSCORE
;
945 (void)input_stack::get(0);
948 (void)input_stack::get(0);
951 (void)input_stack::get(0);
952 return ESCAPE_QUESTION
;
954 (void)input_stack::get(0);
955 return ESCAPE_AMPERSAND
;
957 (void)input_stack::get(0);
958 return ESCAPE_RIGHT_PARENTHESIS
;
960 (void)input_stack::get(0);
963 (void)input_stack::get(0);
964 return ESCAPE_PERCENT
;
966 if (c
== escape_char
) {
967 (void)input_stack::get(0);
976 class non_interpreted_char_node
: public node
{
979 non_interpreted_char_node(unsigned char);
981 int interpret(macro
*);
987 int non_interpreted_char_node::same(node
*nd
)
989 return c
== ((non_interpreted_char_node
*)nd
)->c
;
992 const char *non_interpreted_char_node::type()
994 return "non_interpreted_char_node";
997 int non_interpreted_char_node::force_tprint()
1002 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1007 node
*non_interpreted_char_node::copy()
1009 return new non_interpreted_char_node(c
);
1012 int non_interpreted_char_node::interpret(macro
*mac
)
1018 static void do_width();
1019 static node
*do_non_interpreted();
1020 static node
*do_special();
1021 static node
*do_suppress(symbol nm
);
1022 static void do_register();
1024 dictionary
color_dictionary(501);
1025 static symbol
default_symbol("default");
1027 static color
*lookup_color(symbol nm
)
1029 assert(!nm
.is_null());
1030 if (nm
== default_symbol
)
1031 return &default_color
;
1032 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1034 warning(WARN_COLOR
, "`%1' not defined", nm
.contents());
1038 static node
*do_glyph_color(symbol nm
)
1043 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1045 color
*tem
= lookup_color(nm
);
1047 curenv
->set_glyph_color(tem
);
1049 (void)color_dictionary
.lookup(nm
, new color
);
1051 return new glyph_color_node(curenv
->get_glyph_color());
1054 static node
*do_fill_color(symbol nm
)
1059 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1061 color
*tem
= lookup_color(nm
);
1063 curenv
->set_fill_color(tem
);
1065 (void)color_dictionary
.lookup(nm
, new color
);
1067 return new fill_color_node(curenv
->get_fill_color());
1070 static unsigned int get_color_element(const char *scheme
, const char *col
)
1073 if (!get_number(&val
, 'f')) {
1074 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1079 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1082 if (val
> color::MAX_COLOR_VAL
+1) {
1083 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1084 // we change 0x10000 to 0xffff
1085 return color::MAX_COLOR_VAL
;
1087 return (unsigned int)val
;
1090 static color
*read_rgb()
1092 symbol component
= get_long_name(0);
1093 if (component
.is_null()) {
1094 warning(WARN_COLOR
, "missing rgb color values");
1097 const char *s
= component
.contents();
1098 color
*col
= new color
;
1100 if (!col
->read_rgb(s
)) {
1101 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1107 input_stack::push(make_temp_iterator(" "));
1108 input_stack::push(make_temp_iterator(s
));
1109 unsigned int r
= get_color_element("rgb color", "red component");
1110 unsigned int g
= get_color_element("rgb color", "green component");
1111 unsigned int b
= get_color_element("rgb color", "blue component");
1112 col
->set_rgb(r
, g
, b
);
1117 static color
*read_cmy()
1119 symbol component
= get_long_name(0);
1120 if (component
.is_null()) {
1121 warning(WARN_COLOR
, "missing cmy color values");
1124 const char *s
= component
.contents();
1125 color
*col
= new color
;
1127 if (!col
->read_cmy(s
)) {
1128 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1134 input_stack::push(make_temp_iterator(" "));
1135 input_stack::push(make_temp_iterator(s
));
1136 unsigned int c
= get_color_element("cmy color", "cyan component");
1137 unsigned int m
= get_color_element("cmy color", "magenta component");
1138 unsigned int y
= get_color_element("cmy color", "yellow component");
1139 col
->set_cmy(c
, m
, y
);
1144 static color
*read_cmyk()
1146 symbol component
= get_long_name(0);
1147 if (component
.is_null()) {
1148 warning(WARN_COLOR
, "missing cmyk color values");
1151 const char *s
= component
.contents();
1152 color
*col
= new color
;
1154 if (!col
->read_cmyk(s
)) {
1155 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1161 input_stack::push(make_temp_iterator(" "));
1162 input_stack::push(make_temp_iterator(s
));
1163 unsigned int c
= get_color_element("cmyk color", "cyan component");
1164 unsigned int m
= get_color_element("cmyk color", "magenta component");
1165 unsigned int y
= get_color_element("cmyk color", "yellow component");
1166 unsigned int k
= get_color_element("cmyk color", "black component");
1167 col
->set_cmyk(c
, m
, y
, k
);
1172 static color
*read_gray()
1174 symbol component
= get_long_name(0);
1175 if (component
.is_null()) {
1176 warning(WARN_COLOR
, "missing gray values");
1179 const char *s
= component
.contents();
1180 color
*col
= new color
;
1182 if (!col
->read_gray(s
)) {
1183 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1189 input_stack::push(make_temp_iterator(" "));
1190 input_stack::push(make_temp_iterator(s
));
1191 unsigned int g
= get_color_element("gray", "gray value");
1197 static void define_color()
1199 symbol color_name
= get_long_name(1);
1200 if (color_name
.is_null()) {
1204 if (color_name
== default_symbol
) {
1205 warning(WARN_COLOR
, "default color can't be redefined");
1209 symbol style
= get_long_name(1);
1210 if (style
.is_null()) {
1215 if (strcmp(style
.contents(), "rgb") == 0)
1217 else if (strcmp(style
.contents(), "cmyk") == 0)
1219 else if (strcmp(style
.contents(), "gray") == 0)
1221 else if (strcmp(style
.contents(), "grey") == 0)
1223 else if (strcmp(style
.contents(), "cmy") == 0)
1227 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1233 (void)color_dictionary
.lookup(color_name
, col
);
1237 static node
*do_overstrike()
1240 overstrike_node
*on
= new overstrike_node
;
1241 int start_level
= input_stack::get_level();
1245 if (tok
.newline() || tok
.eof()) {
1246 warning(WARN_DELIM
, "missing closing delimiter");
1250 && (compatible_flag
|| input_stack::get_level() == start_level
))
1252 charinfo
*ci
= tok
.get_char(1);
1254 node
*n
= curenv
->make_char_node(ci
);
1262 static node
*do_bracket()
1265 bracket_node
*bn
= new bracket_node
;
1267 int start_level
= input_stack::get_level();
1271 warning(WARN_DELIM
, "missing closing delimiter");
1274 if (tok
.newline()) {
1275 warning(WARN_DELIM
, "missing closing delimiter");
1276 input_stack::push(make_temp_iterator("\n"));
1280 && (compatible_flag
|| input_stack::get_level() == start_level
))
1282 charinfo
*ci
= tok
.get_char(1);
1284 node
*n
= curenv
->make_char_node(ci
);
1292 static int do_name_test()
1296 int start_level
= input_stack::get_level();
1301 if (tok
.newline() || tok
.eof()) {
1302 warning(WARN_DELIM
, "missing closing delimiter");
1306 && (compatible_flag
|| input_stack::get_level() == start_level
))
1312 return some_char
&& !bad_char
;
1315 static int do_expr_test()
1319 int start_level
= input_stack::get_level();
1320 if (!start
.delimiter(1))
1323 // disable all warning and error messages temporarily
1324 int saved_warning_mask
= warning_mask
;
1325 int saved_inhibit_errors
= inhibit_errors
;
1329 int result
= get_number_rigidly(&dummy
, 'u');
1330 warning_mask
= saved_warning_mask
;
1331 inhibit_errors
= saved_inhibit_errors
;
1332 if (tok
== start
&& input_stack::get_level() == start_level
)
1334 // ignore everything up to the delimiter in case we aren't right there
1337 if (tok
.newline() || tok
.eof()) {
1338 warning(WARN_DELIM
, "missing closing delimiter");
1341 if (tok
== start
&& input_stack::get_level() == start_level
)
1348 static node
*do_zero_width()
1352 int start_level
= input_stack::get_level();
1353 environment
env(curenv
);
1354 environment
*oldenv
= curenv
;
1358 if (tok
.newline() || tok
.eof()) {
1359 error("missing closing delimiter");
1363 && (compatible_flag
|| input_stack::get_level() == start_level
))
1368 node
*rev
= env
.extract_output_line();
1376 return new zero_width_node(n
);
1381 // It's undesirable for \Z to change environments, because then
1382 // \n(.w won't work as expected.
1384 static node
*do_zero_width()
1386 node
*rev
= new dummy_node
;
1389 int start_level
= input_stack::get_level();
1392 if (tok
.newline() || tok
.eof()) {
1393 warning(WARN_DELIM
, "missing closing delimiter");
1397 && (compatible_flag
|| input_stack::get_level() == start_level
))
1399 if (!tok
.add_to_node_list(&rev
))
1400 error("invalid token in argument to \\Z");
1409 return new zero_width_node(n
);
1414 token_node
*node::get_token_node()
1419 class token_node
: public node
{
1422 token_node(const token
&t
);
1424 token_node
*get_token_node();
1430 token_node::token_node(const token
&t
) : tk(t
)
1434 node
*token_node::copy()
1436 return new token_node(tk
);
1439 token_node
*token_node::get_token_node()
1444 int token_node::same(node
*nd
)
1446 return tk
== ((token_node
*)nd
)->tk
;
1449 const char *token_node::type()
1451 return "token_node";
1454 int token_node::force_tprint()
1459 token::token() : nd(0), type(TOKEN_EMPTY
)
1468 token::token(const token
&t
)
1469 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1471 // Use two statements to work around bug in SGI C++.
1473 nd
= tem
? tem
->copy() : 0;
1476 void token::operator=(const token
&t
)
1480 // Use two statements to work around bug in SGI C++.
1482 nd
= tem
? tem
->copy() : 0;
1499 return !tok
.newline();
1502 void token::make_space()
1507 void token::make_newline()
1509 type
= TOKEN_NEWLINE
;
1521 int cc
= input_stack::get(&n
);
1522 if (cc
!= escape_char
|| escape_char
== 0) {
1525 case COMPATIBLE_SAVE
:
1526 input_stack::save_compatible_flag(compatible_flag
);
1527 compatible_flag
= 0;
1529 case COMPATIBLE_RESTORE
:
1530 compatible_flag
= input_stack::get_compatible_flag();
1535 case TRANSPARENT_FILE_REQUEST
:
1537 case COPY_FILE_REQUEST
:
1539 case VJUSTIFY_REQUEST
:
1541 type
= TOKEN_REQUEST
;
1545 type
= TOKEN_BEGIN_TRAP
;
1548 type
= TOKEN_END_TRAP
;
1550 case LAST_PAGE_EJECTOR
:
1551 seen_last_page_ejector
= 1;
1554 type
= TOKEN_PAGE_EJECTOR
;
1556 case ESCAPE_PERCENT
:
1558 type
= TOKEN_HYPHEN_INDICATOR
;
1563 nd
= new space_char_hmotion_node(curenv
->get_space_width());
1567 type
= TOKEN_STRETCHABLE_SPACE
;
1571 type
= TOKEN_ZERO_WIDTH_BREAK
;
1575 type
= TOKEN_ESCAPE
;
1578 goto handle_escape_char
;
1582 nd
= new hmotion_node(curenv
->get_narrow_space_width());
1584 case ESCAPE_CIRCUMFLEX
:
1587 nd
= new hmotion_node(curenv
->get_half_narrow_space_width());
1589 case ESCAPE_NEWLINE
:
1591 case ESCAPE_LEFT_BRACE
:
1593 type
= TOKEN_LEFT_BRACE
;
1595 case ESCAPE_RIGHT_BRACE
:
1597 type
= TOKEN_RIGHT_BRACE
;
1599 case ESCAPE_LEFT_QUOTE
:
1601 type
= TOKEN_SPECIAL
;
1604 case ESCAPE_RIGHT_QUOTE
:
1606 type
= TOKEN_SPECIAL
;
1611 type
= TOKEN_SPECIAL
;
1614 case ESCAPE_UNDERSCORE
:
1616 type
= TOKEN_SPECIAL
;
1621 type
= TOKEN_INTERRUPT
;
1625 type
= TOKEN_TRANSPARENT
;
1627 case ESCAPE_QUESTION
:
1629 nd
= do_non_interpreted();
1635 case ESCAPE_AMPERSAND
:
1639 case ESCAPE_RIGHT_PARENTHESIS
:
1640 ESCAPE_RIGHT_PARENTHESIS
:
1641 type
= TOKEN_TRANSPARENT_DUMMY
;
1644 type
= TOKEN_BACKSPACE
;
1653 type
= TOKEN_NEWLINE
;
1656 type
= TOKEN_LEADER
;
1661 token_node
*tn
= n
->get_token_node();
1680 cc
= input_stack::get(0);
1683 nm
= read_two_char_escape_name();
1684 type
= TOKEN_SPECIAL
;
1688 error("end of input after escape character");
1691 goto ESCAPE_LEFT_QUOTE
;
1693 goto ESCAPE_RIGHT_QUOTE
;
1697 goto ESCAPE_UNDERSCORE
;
1699 goto ESCAPE_PERCENT
;
1703 nd
= new hmotion_node(curenv
->get_digit_width());
1709 goto ESCAPE_CIRCUMFLEX
;
1711 type
= TOKEN_ITALIC_CORRECTION
;
1715 nd
= new left_italic_corrected_node
;
1718 goto ESCAPE_AMPERSAND
;
1720 goto ESCAPE_RIGHT_PARENTHESIS
;
1724 goto ESCAPE_QUESTION
;
1730 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1733 type
= TOKEN_NEWLINE
;
1737 case '#': // Like \" but newline is ignored.
1738 while ((cc
= input_stack::get(0)) != '\n')
1746 symbol nm
= read_escape_name();
1747 if (!(nm
.is_null() || nm
.is_empty()))
1748 interpolate_arg(nm
);
1753 symbol nm
= read_escape_name();
1754 if (!(nm
.is_null() || nm
.is_empty()))
1755 interpolate_string(nm
);
1759 nd
= new non_interpreted_char_node('\001');
1763 c
= '0' + do_name_test();
1771 c
= '0' + do_expr_test();
1777 nm
= get_delim_name();
1780 type
= TOKEN_SPECIAL
;
1784 nd
= new vmotion_node(curenv
->get_size()/2);
1787 nd
= read_draw_node();
1795 goto handle_escape_char
;
1798 symbol s
= read_escape_name(0);
1802 for (p
= s
.contents(); *p
!= '\0'; p
++)
1805 if (*p
|| s
.is_empty())
1806 curenv
->set_font(s
);
1808 curenv
->set_font(atoi(s
.contents()));
1809 if (!compatible_flag
)
1815 symbol s
= read_escape_name(0);
1818 curenv
->set_family(s
);
1823 symbol s
= read_escape_name();
1824 if (!(s
.is_null() || s
.is_empty()))
1825 interpolate_number_format(s
);
1829 if (!get_delim_number(&x
, 'm'))
1832 nd
= new hmotion_node(x
);
1835 // don't take height increments relative to previous height if
1836 // in compatibility mode
1837 if (!compatible_flag
&& curenv
->get_char_height())
1839 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
1840 curenv
->set_char_height(x
);
1844 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1845 curenv
->set_char_height(x
);
1847 if (!compatible_flag
)
1851 nm
= read_escape_name();
1852 if (nm
.is_null() || nm
.is_empty())
1854 type
= TOKEN_MARK_INPUT
;
1860 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1863 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1865 node
*n
= curenv
->make_char_node(s
);
1867 nd
= new hline_node(x
, n
);
1869 nd
= new vline_node(x
, n
);
1873 nd
= do_glyph_color(read_escape_name(0));
1879 nd
= do_fill_color(read_escape_name(0));
1887 symbol nm
= read_increment_and_escape_name(&inc
);
1888 if (!(nm
.is_null() || nm
.is_empty()))
1889 interpolate_number_reg(nm
, inc
);
1893 if (!get_delim_number(&val
, 0))
1895 type
= TOKEN_NUMBERED_CHAR
;
1898 nd
= do_overstrike();
1902 nd
= do_suppress(read_escape_name());
1908 type
= TOKEN_SPREAD
;
1912 nd
= new vmotion_node(-curenv
->get_size());
1916 if (!compatible_flag
)
1921 curenv
->set_size(x
);
1922 if (!compatible_flag
)
1926 if (get_delim_number(&x
, 0))
1927 curenv
->set_char_slant(x
);
1928 if (!compatible_flag
)
1933 nd
= new non_interpreted_char_node('\t');
1937 nd
= new vmotion_node(-curenv
->get_size()/2);
1940 if (!get_delim_number(&x
, 'v'))
1943 nd
= new vmotion_node(x
);
1947 symbol nm
= read_escape_name();
1948 if (!(nm
.is_null() || nm
.is_empty()))
1949 interpolate_environment_variable(nm
);
1956 if (!get_delim_number(&x
, 'v'))
1959 nd
= new extra_size_node(x
);
1969 symbol s
= read_escape_name();
1970 if (s
.is_null() || s
.is_empty())
1972 request_or_macro
*p
= lookup_request(s
);
1973 macro
*m
= p
->to_macro();
1975 error("can't transparently throughput a request");
1978 nd
= new special_node(*m
);
1985 if (type
== TOKEN_NODE
)
1986 nd
= new zero_width_node(nd
);
1988 charinfo
*ci
= get_char(1);
1991 node
*gn
= curenv
->make_char_node(ci
);
1994 nd
= new zero_width_node(gn
);
2000 nd
= do_zero_width();
2006 goto ESCAPE_LEFT_BRACE
;
2008 goto ESCAPE_RIGHT_BRACE
;
2012 if (!compatible_flag
) {
2013 nm
= read_long_escape_name();
2014 if (nm
.is_null() || nm
.is_empty())
2016 type
= TOKEN_SPECIAL
;
2019 goto handle_normal_char
;
2021 if (cc
!= escape_char
&& cc
!= '.')
2022 warning(WARN_ESCAPE
, "escape character ignored before %1",
2023 input_char_description(cc
));
2024 goto handle_normal_char
;
2030 int token::operator==(const token
&t
)
2039 case TOKEN_NUMBERED_CHAR
:
2040 return val
== t
.val
;
2046 int token::operator!=(const token
&t
)
2048 return !(*this == t
);
2051 // is token a suitable delimiter (like ')?
2053 int token::delimiter(int err
)
2082 error("cannot use character `%1' as a starting delimiter", char(c
));
2089 case TOKEN_STRETCHABLE_SPACE
:
2093 error("cannot use %1 as a starting delimiter", description());
2100 const char *token::description()
2104 case TOKEN_BACKSPACE
:
2105 return "a backspace character";
2116 case TOKEN_HYPHEN_INDICATOR
:
2118 case TOKEN_INTERRUPT
:
2120 case TOKEN_ITALIC_CORRECTION
:
2123 return "a leader character";
2124 case TOKEN_LEFT_BRACE
:
2126 case TOKEN_MARK_INPUT
:
2132 case TOKEN_NUMBERED_CHAR
:
2134 case TOKEN_RIGHT_BRACE
:
2139 return "a special character";
2142 case TOKEN_STRETCHABLE_SPACE
:
2145 return "a tab character";
2146 case TOKEN_TRANSPARENT
:
2148 case TOKEN_TRANSPARENT_DUMMY
:
2150 case TOKEN_ZERO_WIDTH_BREAK
:
2153 return "end of input";
2157 return "a magic token";
2162 while (!tok
.newline())
2173 if (has_arg() && get_integer(&n
))
2174 compatible_flag
= n
!= 0;
2176 compatible_flag
= 1;
2180 static void empty_name_warning(int required
)
2182 if (tok
.newline() || tok
.eof()) {
2184 warning(WARN_MISSING
, "missing name");
2186 else if (tok
.right_brace() || tok
.tab()) {
2187 const char *start
= tok
.description();
2190 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2191 if (!tok
.newline() && !tok
.eof())
2192 error("%1 is not allowed before an argument", start
);
2194 warning(WARN_MISSING
, "missing name");
2197 error("name expected (got %1)", tok
.description());
2199 error("name expected (got %1): treated as missing", tok
.description());
2202 static void non_empty_name_warning()
2204 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2205 && !tok
.right_brace()
2206 // We don't want to give a warning for .el\{
2207 && !tok
.left_brace())
2208 error("%1 is not allowed in a name", tok
.description());
2211 symbol
get_name(int required
)
2213 if (compatible_flag
) {
2216 if ((buf
[0] = tok
.ch()) != 0) {
2218 if ((buf
[1] = tok
.ch()) != 0) {
2223 non_empty_name_warning();
2227 empty_name_warning(required
);
2232 return get_long_name(required
);
2235 symbol
get_long_name(int required
)
2239 char abuf
[ABUF_SIZE
];
2241 int buf_size
= ABUF_SIZE
;
2244 if (i
+ 1 > buf_size
) {
2246 buf
= new char[ABUF_SIZE
*2];
2247 memcpy(buf
, abuf
, buf_size
);
2248 buf_size
= ABUF_SIZE
*2;
2251 char *old_buf
= buf
;
2252 buf
= new char[buf_size
*2];
2253 memcpy(buf
, old_buf
, buf_size
);
2258 if ((buf
[i
] = tok
.ch()) == 0)
2264 empty_name_warning(required
);
2267 non_empty_name_warning();
2280 topdiv
->set_last_page();
2281 if (!end_macro_name
.is_null()) {
2282 spring_trap(end_macro_name
);
2284 process_input_stack();
2286 curenv
->final_break();
2288 process_input_stack();
2290 if (topdiv
->get_page_length() > 0) {
2292 topdiv
->set_ejecting();
2293 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2294 input_stack::push(make_temp_iterator((char *)buf
));
2295 topdiv
->space(topdiv
->get_page_length(), 1);
2297 process_input_stack();
2298 seen_last_page_ejector
= 1; // should be set already
2299 topdiv
->set_ejecting();
2300 push_page_ejector();
2301 topdiv
->space(topdiv
->get_page_length(), 1);
2303 process_input_stack();
2305 // This will only happen if a trap-invoked macro starts a diversion,
2306 // or if vertical position traps have been disabled.
2307 cleanup_and_exit(0);
2310 // This implements .ex. The input stack must be cleared before calling
2315 input_stack::clear();
2322 void return_macro_request()
2324 input_stack::pop_macro();
2330 end_macro_name
= get_name();
2334 void blank_line_macro()
2336 blank_line_macro_name
= get_name();
2340 static void trapping_blank_line()
2342 if (!blank_line_macro_name
.is_null())
2343 spring_trap(blank_line_macro_name
);
2350 int old_compatible_flag
= compatible_flag
;
2351 compatible_flag
= 0;
2352 symbol nm
= get_name();
2356 interpolate_macro(nm
);
2357 compatible_flag
= old_compatible_flag
;
2360 inline int possibly_handle_first_page_transition()
2362 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2363 handle_first_page_transition();
2370 static int transparent_translate(int cc
)
2372 if (!invalid_input_char(cc
)) {
2373 charinfo
*ci
= charset_table
[cc
];
2374 switch (ci
->get_special_translation(1)) {
2375 case charinfo::TRANSLATE_SPACE
:
2377 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2378 return ESCAPE_TILDE
;
2379 case charinfo::TRANSLATE_DUMMY
:
2380 return ESCAPE_AMPERSAND
;
2381 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2382 return ESCAPE_PERCENT
;
2384 // This is really ugly.
2385 ci
= ci
->get_translation(1);
2387 int c
= ci
->get_ascii_code();
2390 error("can't translate %1 to special character `%2'"
2391 " in transparent throughput",
2392 input_char_description(cc
),
2400 struct int_stack_element
{
2402 int_stack_element
*next
;
2412 int_stack::int_stack()
2417 int_stack::~int_stack()
2420 int_stack_element
*temp
= top
;
2426 int int_stack::is_empty()
2431 void int_stack::push(int n
)
2433 int_stack_element
*p
= new int_stack_element
;
2439 int int_stack::pop()
2442 int_stack_element
*p
= top
;
2449 int node::reread(int *)
2454 int diverted_space_node::reread(int *bolp
)
2456 if (curenv
->get_fill())
2457 trapping_blank_line();
2464 int diverted_copy_file_node::reread(int *bolp
)
2466 curdiv
->copy_file(filename
.contents());
2471 int word_space_node::reread(int *bolp
)
2474 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2475 curenv
->space(w
->width
, w
->sentence_width
);
2482 int unbreakable_space_node::reread(int *)
2487 int hmotion_node::reread(int *bolp
)
2489 if (unformat
&& was_tab
) {
2490 curenv
->handle_tab(0);
2497 void process_input_stack()
2499 int_stack trap_bol_stack
;
2502 int suppress_next
= 0;
2504 case token::TOKEN_CHAR
:
2506 unsigned char ch
= tok
.c
;
2507 if (bol
&& !have_input
2508 && (ch
== curenv
->control_char
2509 || ch
== curenv
->no_break_control_char
)) {
2510 break_flag
= ch
== curenv
->control_char
;
2511 // skip tabs as well as spaces here
2514 } while (tok
.white_space());
2515 symbol nm
= get_name();
2519 interpolate_macro(nm
);
2524 if (possibly_handle_first_page_transition())
2528 curenv
->add_char(charset_table
[ch
]);
2530 if (tok
.type
!= token::TOKEN_CHAR
)
2540 case token::TOKEN_TRANSPARENT
:
2543 if (possibly_handle_first_page_transition())
2552 curdiv
->transparent_output(transparent_translate(cc
));
2554 curdiv
->transparent_output(n
);
2555 } while (cc
!= '\n' && cc
!= EOF
);
2557 curdiv
->transparent_output('\n');
2562 case token::TOKEN_NEWLINE
:
2564 if (bol
&& !have_input
2565 && !curenv
->get_prev_line_interrupted())
2566 trapping_blank_line();
2574 case token::TOKEN_REQUEST
:
2576 int request_code
= tok
.c
;
2578 switch (request_code
) {
2582 case COPY_FILE_REQUEST
:
2585 case TRANSPARENT_FILE_REQUEST
:
2589 case VJUSTIFY_REQUEST
:
2601 case token::TOKEN_SPACE
:
2603 if (possibly_handle_first_page_transition())
2605 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2607 // save space_width now so that it isn't changed by \f or \s
2608 // which we wouldn't notice here
2609 hunits space_width
= curenv
->get_space_width();
2611 nspaces
+= tok
.nspaces();
2613 } while (tok
.space());
2615 trapping_blank_line();
2619 curenv
->add_node(new hmotion_node(space_width
* nspaces
));
2629 case token::TOKEN_EOF
:
2631 case token::TOKEN_NODE
:
2633 if (possibly_handle_first_page_transition())
2635 else if (tok
.nd
->reread(&bol
)) {
2640 curenv
->add_node(tok
.nd
);
2643 curenv
->possibly_break_line(1);
2647 case token::TOKEN_PAGE_EJECTOR
:
2649 continue_page_eject();
2650 // I think we just want to preserve bol.
2654 case token::TOKEN_BEGIN_TRAP
:
2656 trap_bol_stack
.push(bol
);
2661 case token::TOKEN_END_TRAP
:
2663 if (trap_bol_stack
.is_empty())
2664 error("spurious end trap token detected!");
2666 bol
= trap_bol_stack
.pop();
2668 /* I'm not totally happy about this. But I can't think of any other
2669 way to do it. Doing an output_pending_lines() whenever a
2670 TOKEN_END_TRAP is detected doesn't work: for example,
2683 a\%very\%very\%long\%word
2685 will print all but the first lines from the word immediately
2686 after the footer, rather than on the next page. */
2688 if (trap_bol_stack
.is_empty())
2689 curenv
->output_pending_lines();
2701 trap_sprung_flag
= 0;
2705 #ifdef WIDOW_CONTROL
2707 void flush_pending_lines()
2709 while (!tok
.newline() && !tok
.eof())
2711 curenv
->output_pending_lines();
2715 #endif /* WIDOW_CONTROL */
2717 request_or_macro::request_or_macro()
2721 macro
*request_or_macro::to_macro()
2726 request::request(REQUEST_FUNCP pp
) : p(pp
)
2730 void request::invoke(symbol
)
2736 enum { SIZE
= 128 };
2737 unsigned char s
[SIZE
];
2742 char_block::char_block()
2751 void append(unsigned char);
2758 friend class macro_header
;
2759 friend class string_iterator
;
2762 char_list::char_list()
2763 : ptr(0), len(0), head(0), tail(0)
2767 char_list::~char_list()
2770 char_block
*tem
= head
;
2776 int char_list::length()
2781 void char_list::append(unsigned char c
)
2784 head
= tail
= new char_block
;
2788 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2789 tail
->next
= new char_block
;
2804 void append(node
*);
2808 friend class macro_header
;
2809 friend class string_iterator
;
2812 void node_list::append(node
*n
)
2820 tail
= tail
->next
= n
;
2824 int node_list::length()
2827 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2832 node_list::node_list()
2837 node
*node_list::extract()
2844 node_list::~node_list()
2846 delete_node_list(head
);
2849 struct macro_header
{
2854 macro_header() { count
= 1; }
2855 macro_header
*copy(int);
2860 if (p
!= 0 && --(p
->count
) <= 0)
2866 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2874 macro::macro(const macro
&m
)
2875 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), length(m
.length
)
2881 macro
¯o::operator=(const macro
&m
)
2883 // don't assign object
2886 if (p
!= 0 && --(p
->count
) <= 0)
2889 filename
= m
.filename
;
2895 void macro::append(unsigned char c
)
2899 p
= new macro_header
;
2900 if (p
->cl
.length() != length
) {
2901 macro_header
*tem
= p
->copy(length
);
2902 if (--(p
->count
) <= 0)
2910 void macro::append_str(const char *s
)
2915 while (s
[i
] != (char)0) {
2922 void macro::append(node
*n
)
2926 p
= new macro_header
;
2927 if (p
->cl
.length() != length
) {
2928 macro_header
*tem
= p
->copy(length
);
2929 if (--(p
->count
) <= 0)
2938 void macro::append_unsigned(unsigned int i
)
2940 unsigned int j
= i
/ 10;
2943 append(((unsigned char)(((int)'0') + i
% 10)));
2946 void macro::append_int(int i
)
2952 append_unsigned((unsigned int)i
);
2955 void macro::print_size()
2957 errprint("%1", length
);
2960 // make a copy of the first n bytes
2962 macro_header
*macro_header::copy(int n
)
2964 macro_header
*p
= new macro_header
;
2965 char_block
*bp
= cl
.head
;
2966 unsigned char *ptr
= bp
->s
;
2969 if (ptr
>= bp
->s
+ char_block::SIZE
) {
2976 p
->nl
.append(nd
->copy());
2985 object_dictionary_iterator
iter(request_dictionary
);
2986 request_or_macro
*rm
;
2988 while (iter
.get(&s
, (object
**)&rm
)) {
2989 assert(!s
.is_null());
2990 macro
*m
= rm
->to_macro();
2992 errprint("%1\t", s
.contents());
3001 class string_iterator
: public input_iterator
{
3003 const char *how_invoked
;
3005 int suppress_newline_flag
; // used by html
3008 int count
; // of characters remaining
3010 int saved_compatible_flag
;
3015 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3018 int get_location(int, const char **, int *);
3020 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3021 int get_compatible_flag() { return saved_compatible_flag
; }
3024 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3025 : mac(m
), how_invoked(p
),
3026 newline_flag(0), suppress_newline_flag(0), lineno(1), nm(s
)
3030 bp
= mac
.p
->cl
.head
;
3031 nd
= mac
.p
->nl
.head
;
3041 string_iterator::string_iterator()
3047 suppress_newline_flag
= 0;
3053 int string_iterator::fill(node
**np
)
3060 const unsigned char *p
= eptr
;
3061 if (p
>= bp
->s
+ char_block::SIZE
) {
3073 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3078 unsigned char c
= *p
;
3079 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3081 if (is_html
&& c
== ESCAPE_NEWLINE
)
3082 suppress_newline_flag
= 1;
3095 int string_iterator::peek()
3099 const unsigned char *p
= eptr
;
3102 if (p
>= bp
->s
+ char_block::SIZE
) {
3108 int string_iterator::get_location(int allow_macro
,
3109 const char **filep
, int *linep
)
3113 if (mac
.filename
== 0)
3115 *filep
= mac
.filename
;
3116 *linep
= mac
.lineno
+ lineno
- 1;
3120 void string_iterator::backtrace()
3123 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3126 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3128 errprint(": %1\n", how_invoked
);
3135 class temp_iterator
: public input_iterator
{
3136 unsigned char *base
;
3137 temp_iterator(const char *, int len
);
3140 friend input_iterator
*make_temp_iterator(const char *);
3146 temp_iterator::temp_iterator(const char *s
, int len
)
3148 base
= new unsigned char[len
];
3149 memcpy(base
, s
, len
);
3154 temp_iterator::~temp_iterator()
3159 class small_temp_iterator
: public input_iterator
{
3161 small_temp_iterator(const char *, int);
3162 ~small_temp_iterator();
3163 enum { BLOCK
= 16 };
3164 static small_temp_iterator
*free_list
;
3165 void *operator new(size_t);
3166 void operator delete(void *);
3168 unsigned char buf
[SIZE
];
3169 friend input_iterator
*make_temp_iterator(const char *);
3172 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3174 void *small_temp_iterator::operator new(size_t n
)
3176 assert(n
== sizeof(small_temp_iterator
));
3179 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3180 for (int i
= 0; i
< BLOCK
- 1; i
++)
3181 free_list
[i
].next
= free_list
+ i
+ 1;
3182 free_list
[BLOCK
-1].next
= 0;
3184 small_temp_iterator
*p
= free_list
;
3185 free_list
= (small_temp_iterator
*)(free_list
->next
);
3193 void small_temp_iterator::operator delete(void *p
)
3196 ((small_temp_iterator
*)p
)->next
= free_list
;
3197 free_list
= (small_temp_iterator
*)p
;
3201 small_temp_iterator::~small_temp_iterator()
3208 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3210 for (int i
= 0; i
< len
; i
++)
3216 input_iterator
*make_temp_iterator(const char *s
)
3219 return new small_temp_iterator(s
, 0);
3222 if (n
<= small_temp_iterator::SIZE
)
3223 return new small_temp_iterator(s
, n
);
3225 return new temp_iterator(s
, n
);
3229 // this is used when macros are interpolated using the .macro_name notation
3234 arg_list(const macro
&);
3238 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3242 arg_list::~arg_list()
3246 class macro_iterator
: public string_iterator
{
3250 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3253 int has_args() { return 1; }
3254 input_iterator
*get_arg(int i
);
3255 int nargs() { return argc
; }
3256 void add_arg(const macro
&m
);
3258 int is_macro() { return 1; }
3261 input_iterator
*macro_iterator::get_arg(int i
)
3264 return make_temp_iterator(nm
.contents());
3265 if (i
> 0 && i
<= argc
) {
3267 for (int j
= 1; j
< i
; j
++) {
3271 return new string_iterator(p
->mac
);
3277 void macro_iterator::add_arg(const macro
&m
)
3280 for (p
= &args
; *p
; p
= &((*p
)->next
))
3282 *p
= new arg_list(m
);
3286 void macro_iterator::shift(int n
)
3288 while (n
> 0 && argc
> 0) {
3289 arg_list
*tem
= args
;
3297 // This gets used by eg .if '\?xxx\?''.
3299 int operator==(const macro
&m1
, const macro
&m2
)
3301 if (m1
.length
!= m2
.length
)
3303 string_iterator
iter1(m1
);
3304 string_iterator
iter2(m2
);
3308 int c1
= iter1
.get(&nd1
);
3311 int c2
= iter2
.get(&nd2
);
3323 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3333 static void interpolate_macro(symbol nm
)
3335 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3338 const char *s
= nm
.contents();
3339 if (strlen(s
) > 2) {
3340 request_or_macro
*r
;
3345 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3347 macro
*m
= r
->to_macro();
3348 if (!m
|| !m
->empty())
3349 warned
= warning(WARN_SPACE
,
3350 "`%1' not defined (probable missing space after `%2')",
3351 nm
.contents(), buf
);
3355 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
3357 request_dictionary
.define(nm
, p
);
3368 static void decode_args(macro_iterator
*mi
)
3370 if (!tok
.newline() && !tok
.eof()) {
3372 int c
= get_copy(&n
);
3376 if (c
== '\n' || c
== EOF
)
3379 int quote_input_level
= 0;
3380 int done_tab_warning
= 0;
3382 quote_input_level
= input_stack::get_level();
3385 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3386 if (quote_input_level
> 0 && c
== '\"'
3388 || input_stack::get_level() == quote_input_level
)) {
3401 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3402 warning(WARN_TAB
, "tab character in unquoted macro argument");
3403 done_tab_warning
= 1;
3415 void macro::invoke(symbol nm
)
3417 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3419 input_stack::push(mi
);
3423 macro
*macro::to_macro()
3433 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
3434 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
3438 macro_iterator::macro_iterator() : args(0), argc(0)
3442 macro_iterator::~macro_iterator()
3445 arg_list
*tem
= args
;
3451 int trap_sprung_flag
= 0;
3452 int postpone_traps_flag
= 0;
3453 symbol postponed_trap
;
3455 void spring_trap(symbol nm
)
3457 assert(!nm
.is_null());
3458 trap_sprung_flag
= 1;
3459 if (postpone_traps_flag
) {
3460 postponed_trap
= nm
;
3463 static char buf
[2] = { BEGIN_TRAP
, 0 };
3464 static char buf2
[2] = { END_TRAP
, '\0' };
3465 input_stack::push(make_temp_iterator(buf2
));
3466 request_or_macro
*p
= lookup_request(nm
);
3467 macro
*m
= p
->to_macro();
3469 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3471 error("you can't invoke a request with a trap");
3472 input_stack::push(make_temp_iterator(buf
));
3475 void postpone_traps()
3477 postpone_traps_flag
= 1;
3480 int unpostpone_traps()
3482 postpone_traps_flag
= 0;
3483 if (!postponed_trap
.is_null()) {
3484 spring_trap(postponed_trap
);
3485 postponed_trap
= NULL_SYMBOL
;
3494 macro_iterator
*mi
= new macro_iterator
;
3495 int reading_from_terminal
= isatty(fileno(stdin
));
3497 if (!tok
.newline() && !tok
.eof()) {
3498 int c
= get_copy(0);
3501 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3502 if (!invalid_input_char(c
)) {
3503 if (reading_from_terminal
)
3514 if (reading_from_terminal
) {
3515 fputc(had_prompt
? ':' : '\a', stderr
);
3518 input_stack::push(mi
);
3522 while ((c
= getchar()) != EOF
) {
3523 if (invalid_input_char(c
))
3524 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
3537 if (reading_from_terminal
)
3539 input_stack::push(new string_iterator(mac
));
3543 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3544 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
, CALLING_DISABLE_COMP
};
3546 void do_define_string(define_mode mode
, calling_mode calling
)
3560 else if (!tok
.space()) {
3561 error("bad string definition");
3572 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3573 macro
*mm
= rm
? rm
->to_macro() : 0;
3574 if (mode
== DEFINE_APPEND
&& mm
)
3576 if (calling
== CALLING_DISABLE_COMP
)
3577 mac
.append(COMPATIBLE_SAVE
);
3578 while (c
!= '\n' && c
!= EOF
) {
3582 mac
.append((unsigned char)c
);
3587 request_dictionary
.define(nm
, mm
);
3589 if (calling
== CALLING_DISABLE_COMP
)
3590 mac
.append(COMPATIBLE_RESTORE
);
3595 void define_string()
3597 do_define_string(DEFINE_NORMAL
, CALLING_NORMAL
);
3600 void define_nocomp_string()
3602 do_define_string(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3605 void append_string()
3607 do_define_string(DEFINE_APPEND
, CALLING_NORMAL
);
3610 void append_nocomp_string()
3612 do_define_string(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3615 void do_define_character(int fallback
)
3620 charinfo
*ci
= tok
.get_char(1);
3630 else if (!tok
.space()) {
3631 error("bad character definition");
3637 while (c
== ' ' || c
== '\t')
3641 macro
*m
= new macro
;
3642 while (c
!= '\n' && c
!= EOF
) {
3646 m
->append((unsigned char)c
);
3649 m
= ci
->set_macro(m
, fallback
);
3655 void define_character()
3657 do_define_character(0);
3660 void define_fallback_character()
3662 do_define_character(1);
3665 static void remove_character()
3668 while (!tok
.newline() && !tok
.eof()) {
3669 if (!tok
.space() && !tok
.tab()) {
3670 charinfo
*ci
= tok
.get_char(1);
3673 macro
*m
= ci
->set_macro(0);
3682 static void interpolate_string(symbol nm
)
3684 request_or_macro
*p
= lookup_request(nm
);
3685 macro
*m
= p
->to_macro();
3687 error("you can only invoke a string using \\*");
3689 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3690 input_stack::push(si
);
3694 /* This class is used for the implementation of \$@. It is used for
3695 each of the closing double quotes. It artificially increases the
3696 input level by 2, so that the closing double quote will appear to have
3697 the same input level as the opening quote. */
3699 class end_quote_iterator
: public input_iterator
{
3700 unsigned char buf
[1];
3702 end_quote_iterator();
3703 ~end_quote_iterator() { }
3704 int internal_level() { return 2; }
3707 end_quote_iterator::end_quote_iterator()
3714 static void interpolate_arg(symbol nm
)
3716 const char *s
= nm
.contents();
3717 if (!s
|| *s
== '\0')
3718 copy_mode_error("missing argument name");
3719 else if (s
[1] == 0 && csdigit(s
[0]))
3720 input_stack::push(input_stack::get_arg(s
[0] - '0'));
3721 else if (s
[0] == '*' && s
[1] == '\0') {
3722 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3723 input_stack::push(input_stack::get_arg(i
));
3725 input_stack::push(make_temp_iterator(" "));
3728 else if (s
[0] == '@' && s
[1] == '\0') {
3729 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3730 input_stack::push(new end_quote_iterator
);
3731 input_stack::push(input_stack::get_arg(i
));
3732 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
3737 for (p
= s
; *p
&& csdigit(*p
); p
++)
3740 copy_mode_error("bad argument name `%1'", s
);
3742 input_stack::push(input_stack::get_arg(atoi(s
)));
3746 void handle_first_page_transition()
3749 topdiv
->begin_page();
3752 // We push back a token by wrapping it up in a token_node, and
3753 // wrapping that up in a string_iterator.
3755 static void push_token(const token
&t
)
3758 m
.append(new token_node(t
));
3759 input_stack::push(new string_iterator(m
));
3762 void push_page_ejector()
3764 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
3765 input_stack::push(make_temp_iterator(buf
));
3768 void handle_initial_request(unsigned char code
)
3774 mac
.append(new token_node(tok
));
3775 input_stack::push(new string_iterator(mac
));
3776 input_stack::push(make_temp_iterator(buf
));
3777 topdiv
->begin_page();
3781 void handle_initial_title()
3783 handle_initial_request(TITLE_REQUEST
);
3786 // this should be local to define_macro, but cfront 1.2 doesn't support that
3787 static symbol
dot_symbol(".");
3789 void do_define_macro(define_mode mode
, calling_mode calling
)
3792 if (calling
== CALLING_INDIRECT
) {
3793 symbol temp1
= get_name(1);
3794 if (temp1
.is_null()) {
3798 symbol temp2
= get_name();
3799 input_stack::push(make_temp_iterator("\n"));
3800 if (!temp2
.is_null()) {
3801 interpolate_string(temp2
);
3802 input_stack::push(make_temp_iterator(" "));
3804 interpolate_string(temp1
);
3805 input_stack::push(make_temp_iterator(" "));
3808 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3815 term
= get_name(); // the request that terminates the definition
3818 while (!tok
.newline() && !tok
.eof())
3820 const char *start_filename
;
3822 int have_start_location
= input_stack::get_location(0, &start_filename
,
3825 // doing this here makes the line numbers come out right
3826 int c
= get_copy(&n
, 1);
3829 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3830 request_or_macro
*rm
=
3831 (request_or_macro
*)request_dictionary
.lookup(nm
);
3833 mm
= rm
->to_macro();
3834 if (mm
&& mode
== DEFINE_APPEND
)
3838 if (calling
== CALLING_DISABLE_COMP
)
3839 mac
.append(COMPATIBLE_SAVE
);
3841 while (c
== ESCAPE_NEWLINE
) {
3842 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
3844 c
= get_copy(&n
, 1);
3846 if (bol
&& c
== '.') {
3847 const char *s
= term
.contents();
3849 // see if it matches term
3852 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
3854 if ((unsigned char)s
[0] == d
) {
3855 for (i
= 1; s
[i
] != 0; i
++) {
3857 if ((unsigned char)s
[i
] != d
)
3863 && ((i
== 2 && compatible_flag
)
3864 || (d
= get_copy(&n
)) == ' '
3865 || d
== '\n')) { // we found it
3870 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3873 request_dictionary
.define(nm
, mm
);
3875 if (calling
== CALLING_DISABLE_COMP
)
3876 mac
.append(COMPATIBLE_RESTORE
);
3879 if (term
!= dot_symbol
) {
3881 interpolate_macro(term
);
3887 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3889 for (int j
= 0; j
< i
; j
++)
3895 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3896 if (have_start_location
)
3897 error_with_file_and_line(start_filename
, start_lineno
,
3898 "end of file while defining macro `%1'",
3901 error("end of file while defining macro `%1'", nm
.contents());
3904 if (have_start_location
)
3905 error_with_file_and_line(start_filename
, start_lineno
,
3906 "end of file while ignoring input lines");
3908 error("end of file while ignoring input lines");
3913 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3920 c
= get_copy(&n
, 1);
3926 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
);
3929 void define_nocomp_macro()
3931 do_define_macro(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3934 void define_indirect_macro()
3936 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
);
3941 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
);
3944 void append_indirect_macro()
3946 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
);
3949 void append_nocomp_macro()
3951 do_define_macro(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3957 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
);
3964 symbol s
= get_name();
3967 request_dictionary
.remove(s
);
3974 symbol s1
= get_name(1);
3975 if (!s1
.is_null()) {
3976 symbol s2
= get_name(1);
3978 request_dictionary
.rename(s1
, s2
);
3985 symbol s1
= get_name(1);
3986 if (!s1
.is_null()) {
3987 symbol s2
= get_name(1);
3988 if (!s2
.is_null()) {
3989 if (!request_dictionary
.alias(s1
, s2
))
3990 warning(WARN_MAC
, "`%1' not defined", s2
.contents());
3998 symbol s
= get_name(1);
4000 request_or_macro
*p
= lookup_request(s
);
4001 macro
*m
= p
->to_macro();
4003 error("cannot chop request");
4004 else if (m
->length
== 0)
4005 error("cannot chop empty macro");
4012 void substring_macro()
4015 symbol s
= get_name(1);
4016 if (!s
.is_null() && get_integer(&start
)) {
4017 request_or_macro
*p
= lookup_request(s
);
4018 macro
*m
= p
->to_macro();
4020 error("cannot substring request");
4023 start
+= m
->length
- 1;
4027 if (!has_arg() || get_integer(&end
)) {
4029 end
+= m
->length
- 1;
4037 if (start
>= m
->length
|| end
< 0) {
4040 if (--(m
->p
->count
) <= 0)
4049 if (end
>= m
->length
)
4050 end
= m
->length
- 1;
4052 m
->length
= end
+ 1;
4054 string_iterator
iter(*m
);
4056 for (i
= 0; i
< start
; i
++)
4057 if (iter
.get(0) == EOF
)
4060 for (; i
<= end
; i
++) {
4062 int c
= iter
.get(&nd
);
4068 mac
.append((unsigned char)c
);
4082 if (ret
.is_null()) {
4092 else if (!tok
.space()) {
4093 error("bad string definition");
4104 while (c
!= '\n' && c
!= EOF
) {
4109 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4113 set_number_reg(ret
, len
);
4116 void asciify_macro()
4118 symbol s
= get_name(1);
4120 request_or_macro
*p
= lookup_request(s
);
4121 macro
*m
= p
->to_macro();
4123 error("cannot asciify request");
4126 string_iterator
iter(*m
);
4129 int c
= iter
.get(&nd
);
4143 void unformat_macro()
4145 symbol s
= get_name(1);
4147 request_or_macro
*p
= lookup_request(s
);
4148 macro
*m
= p
->to_macro();
4150 error("cannot unformat request");
4153 string_iterator
iter(*m
);
4156 int c
= iter
.get(&nd
);
4162 if (nd
->set_unformat_flag())
4172 static void interpolate_environment_variable(symbol nm
)
4174 const char *s
= getenv(nm
.contents());
4176 input_stack::push(make_temp_iterator(s
));
4179 void interpolate_number_reg(symbol nm
, int inc
)
4181 reg
*r
= lookup_number_reg(nm
);
4186 input_stack::push(make_temp_iterator(r
->get_string()));
4189 static void interpolate_number_format(symbol nm
)
4191 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4193 input_stack::push(make_temp_iterator(r
->get_format()));
4196 static int get_delim_number(units
*n
, int si
, int prev_value
)
4200 if (start
.delimiter(1)) {
4202 if (get_number(n
, si
, prev_value
)) {
4204 warning(WARN_DELIM
, "closing delimiter does not match");
4211 static int get_delim_number(units
*n
, int si
)
4215 if (start
.delimiter(1)) {
4217 if (get_number(n
, si
)) {
4219 warning(WARN_DELIM
, "closing delimiter does not match");
4226 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
4230 int start_level
= input_stack::get_level();
4231 if (!start
.delimiter(1))
4234 if (get_number(n
, si
)) {
4235 if (tok
.dummy() || tok
.transparent_dummy())
4237 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4238 *cp
= tok
.get_char(1);
4241 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4242 warning(WARN_DELIM
, "closing delimiter does not match");
4248 static int read_size(int *x
)
4258 else if (c
== '+') {
4269 // allow an increment either before or after the left parenthesis
4275 else if (c
== '+') {
4290 val
= val
*10 + (c
- '0');
4295 else if (csdigit(c
)) {
4297 if (!inc
&& c
!= '0' && c
< '4') {
4303 val
= val
*10 + (c
- '0');
4307 else if (!tok
.delimiter(1))
4313 ? get_number(&val
, 'z')
4314 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4316 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4317 if (start
.ch() == '[')
4318 error("missing `]'");
4320 error("missing closing delimiter");
4328 // special case -- \s[0] and \s0 means to revert to previous size
4335 *x
= curenv
->get_requested_point_size() + val
;
4338 *x
= curenv
->get_requested_point_size() - val
;
4345 "\\s request results in non-positive point size; set to 1");
4351 error("bad digit in point size");
4356 static symbol
get_delim_name()
4361 error("end of input at start of delimited name");
4364 if (start
.newline()) {
4365 error("can't delimit name with a newline");
4368 int start_level
= input_stack::get_level();
4369 char abuf
[ABUF_SIZE
];
4371 int buf_size
= ABUF_SIZE
;
4374 if (i
+ 1 > buf_size
) {
4376 buf
= new char[ABUF_SIZE
*2];
4377 memcpy(buf
, abuf
, buf_size
);
4378 buf_size
= ABUF_SIZE
*2;
4381 char *old_buf
= buf
;
4382 buf
= new char[buf_size
*2];
4383 memcpy(buf
, old_buf
, buf_size
);
4390 && (compatible_flag
|| input_stack::get_level() == start_level
))
4392 if ((buf
[i
] = tok
.ch()) == 0) {
4393 error("missing delimiter (got %1)", tok
.description());
4403 error("empty delimited name");
4418 static void do_register()
4422 if (!start
.delimiter(1))
4425 symbol nm
= get_long_name(1);
4430 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4432 if (!r
|| !r
->get_value(&prev_value
))
4435 if (!get_number(&val
, 'u', prev_value
))
4438 warning(WARN_DELIM
, "closing delimiter does not match");
4442 set_number_reg(nm
, val
);
4445 // this implements the \w escape sequence
4447 static void do_width()
4451 int start_level
= input_stack::get_level();
4452 environment
env(curenv
);
4453 environment
*oldenv
= curenv
;
4458 warning(WARN_DELIM
, "missing closing delimiter");
4461 if (tok
.newline()) {
4462 warning(WARN_DELIM
, "missing closing delimiter");
4463 input_stack::push(make_temp_iterator("\n"));
4467 && (compatible_flag
|| input_stack::get_level() == start_level
))
4472 units x
= env
.get_input_line_position().to_units();
4473 input_stack::push(make_temp_iterator(i_to_a(x
)));
4474 env
.width_registers();
4478 charinfo
*page_character
;
4480 void set_page_character()
4482 page_character
= get_optional_char();
4486 static const symbol
percent_symbol("%");
4488 void read_title_parts(node
**part
, hunits
*part_width
)
4491 if (tok
.newline() || tok
.eof())
4494 int start_level
= input_stack::get_level();
4496 for (int i
= 0; i
< 3; i
++) {
4497 while (!tok
.newline() && !tok
.eof()) {
4499 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
4503 if (page_character
!= 0 && tok
.get_char() == page_character
)
4504 interpolate_number_reg(percent_symbol
, 0);
4509 curenv
->wrap_up_tab();
4510 part_width
[i
] = curenv
->get_input_line_position();
4511 part
[i
] = curenv
->extract_output_line();
4513 while (!tok
.newline() && !tok
.eof())
4517 class non_interpreted_node
: public node
{
4520 non_interpreted_node(const macro
&);
4521 int interpret(macro
*);
4528 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
4532 int non_interpreted_node::same(node
*nd
)
4534 return mac
== ((non_interpreted_node
*)nd
)->mac
;
4537 const char *non_interpreted_node::type()
4539 return "non_interpreted_node";
4542 int non_interpreted_node::force_tprint()
4547 node
*non_interpreted_node::copy()
4549 return new non_interpreted_node(mac
);
4552 int non_interpreted_node::interpret(macro
*m
)
4554 string_iterator
si(mac
);
4568 static node
*do_non_interpreted()
4573 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4578 if (c
== EOF
|| c
== '\n') {
4579 error("missing \\?");
4582 return new non_interpreted_node(mac
);
4585 static void encode_char(macro
*mac
, char c
)
4588 if ((font::use_charnames_in_special
) && tok
.special()) {
4589 charinfo
*ci
= tok
.get_char(1);
4590 const char *s
= ci
->get_symbol()->contents();
4591 if (s
[0] != (char)0) {
4595 while (s
[i
] != (char)0) {
4603 else if (!(tok
.hyphen_indicator()
4605 || tok
.transparent_dummy()
4606 || tok
.zero_width_break()))
4607 error("%1 is invalid within \\X", tok
.description());
4610 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4612 * add escape escape sequence
4624 int start_level
= input_stack::get_level();
4627 tok
!= start
|| input_stack::get_level() != start_level
;
4630 warning(WARN_DELIM
, "missing closing delimiter");
4633 if (tok
.newline()) {
4634 input_stack::push(make_temp_iterator("\n"));
4635 warning(WARN_DELIM
, "missing closing delimiter");
4643 else if (tok
.leader())
4645 else if (tok
.backspace())
4649 encode_char(&mac
, c
);
4651 return new special_node(mac
);
4654 void output_request()
4656 if (!tok
.newline() && !tok
.eof()) {
4664 if (c
!= ' ' && c
!= '\t')
4667 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
4668 topdiv
->transparent_output(c
);
4669 topdiv
->transparent_output('\n');
4673 extern int image_no
; // from node.cc
4675 static node
*do_suppress(symbol nm
)
4677 if (nm
.is_null() || nm
.is_empty()) {
4678 error("expecting an argument to escape \\O");
4681 const char *s
= nm
.contents();
4684 if (begin_level
== 0)
4685 // suppress generation of glyphs
4686 return new suppress_node(0, 0);
4689 if (begin_level
== 0)
4690 // enable generation of glyphs
4691 return new suppress_node(1, 0);
4694 if (begin_level
== 0)
4695 return new suppress_node(1, 1);
4700 // say goodbye to all this code ?
4701 if ((begin_level
== 1) && (!is_html
)) {
4702 if (curdiv
== topdiv
) {
4703 if (topdiv
->before_first_page
) {
4705 if (!topdiv
->no_space_mode
)
4706 topdiv
->begin_page();
4708 else if (topdiv
->no_space_mode
)
4709 topdiv
->begin_page();
4711 push_page_ejector();
4712 topdiv
->begin_page();
4713 topdiv
->set_ejecting();
4717 push_page_ejector();
4720 if (!topdiv
->no_space_mode
)
4721 topdiv
->set_ejecting();
4725 // say goodbye to all this code?
4733 s
++; // move over '5'
4735 if (*s
== (char)0) {
4736 error("missing position and filename in \\O");
4739 if (!(position
== 'l'
4742 || position
== 'i')) {
4743 error("l, r, c, or i position expected (got %1 in \\O)", position
);
4746 s
++; // onto image name
4747 if (s
== (char *)0) {
4748 error("missing image name for \\O");
4752 if (begin_level
== 0)
4753 return new suppress_node(symbol(s
), position
, image_no
);
4757 error("`%1' is an invalid argument to \\O", *s
);
4762 void special_node::tprint(troff_output_file
*out
)
4765 string_iterator
iter(mac
);
4767 int c
= iter
.get(0);
4770 for (const char *s
= ::asciify(c
); *s
; s
++)
4771 tprint_char(out
, *s
);
4776 int get_file_line(const char **filename
, int *lineno
)
4778 return input_stack::get_location(0, filename
, lineno
);
4784 if (get_integer(&n
)) {
4785 const char *filename
= 0;
4787 symbol s
= get_long_name();
4788 filename
= s
.contents();
4790 (void)input_stack::set_location(filename
, n
-1);
4795 static int nroff_mode
= 0;
4797 static void nroff_request()
4803 static void troff_request()
4809 static void skip_alternative()
4812 // ensure that ``.if 0\{'' works as expected
4813 if (tok
.left_brace())
4817 c
= input_stack::get(0);
4820 if (c
== ESCAPE_LEFT_BRACE
)
4822 else if (c
== ESCAPE_RIGHT_BRACE
)
4824 else if (c
== escape_char
&& escape_char
> 0)
4825 switch(input_stack::get(0)) {
4833 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
4837 Note that the level can properly be < 0, eg
4843 So don't give an error message in this case.
4845 if (level
<= 0 && c
== '\n')
4851 static void begin_alternative()
4853 while (tok
.space() || tok
.left_brace())
4863 static int_stack if_else_stack
;
4870 while (tok
.ch() == '!') {
4875 unsigned char c
= tok
.ch();
4878 result
= !nroff_mode
;
4880 else if (c
== 'n') {
4882 result
= nroff_mode
;
4884 else if (c
== 'v') {
4888 else if (c
== 'o') {
4889 result
= (topdiv
->get_page_number() & 1);
4892 else if (c
== 'e') {
4893 result
= !(topdiv
->get_page_number() & 1);
4896 else if (c
== 'd' || c
== 'r') {
4898 symbol nm
= get_name(1);
4904 ? request_dictionary
.lookup(nm
) != 0
4905 : number_reg_dictionary
.lookup(nm
) != 0);
4907 else if (c
== 'm') {
4909 symbol nm
= get_long_name(1);
4914 result
= (nm
== default_symbol
4915 || color_dictionary
.lookup(nm
) != 0);
4917 else if (c
== 'c') {
4920 charinfo
*ci
= tok
.get_char(1);
4925 result
= character_exists(ci
, curenv
);
4928 else if (tok
.space())
4930 else if (tok
.delimiter()) {
4932 int delim_level
= input_stack::get_level();
4933 environment
env1(curenv
);
4934 environment
env2(curenv
);
4935 environment
*oldenv
= curenv
;
4937 for (int i
= 0; i
< 2; i
++) {
4940 if (tok
.newline() || tok
.eof()) {
4941 warning(WARN_DELIM
, "missing closing delimiter");
4947 && (compatible_flag
|| input_stack::get_level() == delim_level
))
4953 node
*n1
= env1
.extract_output_line();
4954 node
*n2
= env2
.extract_output_line();
4955 result
= same_node_list(n1
, n2
);
4956 delete_node_list(n1
);
4957 delete_node_list(n2
);
4963 if (!get_number(&n
, 'u')) {
4973 begin_alternative();
4979 void if_else_request()
4981 if_else_stack
.push(do_if_request());
4991 if (if_else_stack
.is_empty()) {
4992 warning(WARN_EL
, "unbalanced .el request");
4996 if (if_else_stack
.pop())
4999 begin_alternative();
5003 static int while_depth
= 0;
5004 static int while_break_flag
= 0;
5006 void while_request()
5011 mac
.append(new token_node(tok
));
5014 int c
= input_stack::get(&n
);
5030 if (c
== ESCAPE_LEFT_BRACE
)
5032 else if (c
== ESCAPE_RIGHT_BRACE
)
5034 else if (c
== escape_char
)
5037 if (c
== '\n' && level
<= 0)
5042 error("unbalanced \\{ \\}");
5045 input_stack::add_boundary();
5047 input_stack::push(new string_iterator(mac
, "while loop"));
5049 if (!do_if_request()) {
5050 while (input_stack::get(0) != EOF
)
5054 process_input_stack();
5055 if (while_break_flag
|| input_stack::is_return_boundary()) {
5056 while_break_flag
= 0;
5060 input_stack::remove_boundary();
5066 void while_break_request()
5069 error("no while loop");
5073 while_break_flag
= 1;
5074 while (input_stack::get(0) != EOF
)
5080 void while_continue_request()
5083 error("no while loop");
5087 while (input_stack::get(0) != EOF
)
5097 symbol nm
= get_long_name(1);
5101 while (!tok
.newline() && !tok
.eof())
5104 FILE *fp
= fopen(nm
.contents(), "r");
5106 input_stack::push(new file_iterator(fp
, nm
.contents()));
5108 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5113 // like .so but use popen()
5118 error(".pso request not allowed in safer mode");
5122 #ifdef POPEN_MISSING
5123 error("pipes not available on this system");
5125 #else /* not POPEN_MISSING */
5126 if (tok
.newline() || tok
.eof())
5127 error("missing command");
5130 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5133 char *buf
= new char[buf_size
];
5135 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5136 const char *s
= asciify(c
);
5137 int slen
= strlen(s
);
5138 if (buf_used
+ slen
+ 1> buf_size
) {
5139 char *old_buf
= buf
;
5140 int old_buf_size
= buf_size
;
5142 buf
= new char[buf_size
];
5143 memcpy(buf
, old_buf
, old_buf_size
);
5146 strcpy(buf
+ buf_used
, s
);
5149 buf
[buf_used
] = '\0';
5151 FILE *fp
= popen(buf
, POPEN_RT
);
5153 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5155 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5159 #endif /* not POPEN_MISSING */
5165 static int llx_reg_contents
= 0;
5166 static int lly_reg_contents
= 0;
5167 static int urx_reg_contents
= 0;
5168 static int ury_reg_contents
= 0;
5170 struct bounding_box
{
5171 int llx
, lly
, urx
, ury
;
5174 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5175 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5177 int parse_bounding_box(char *p
, bounding_box
*bb
)
5179 if (sscanf(p
, "%d %d %d %d",
5180 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5183 /* The Document Structuring Conventions say that the numbers
5184 should be integers. Unfortunately some broken applications
5186 double x1
, x2
, x3
, x4
;
5187 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5195 for (; *p
== ' ' || *p
== '\t'; p
++)
5197 if (strncmp(p
, "(atend)", 7) == 0) {
5202 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5206 // This version is taken from psrm.cc
5208 #define PS_LINE_MAX 255
5209 cset
white_space("\n\r \t");
5211 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5220 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5221 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5222 error("invalid input character code %1 in `%2'", int(c
), filename
);
5223 else if (i
< PS_LINE_MAX
)
5227 error("PostScript file `%1' is non-conforming "
5228 "because length of line exceeds 255", filename
);
5236 if (c
!= EOF
&& c
!= '\n')
5242 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5244 llx_reg_contents
= llx
;
5245 lly_reg_contents
= lly
;
5246 urx_reg_contents
= urx
;
5247 ury_reg_contents
= ury
;
5250 void do_ps_file(FILE *fp
, const char* filename
)
5254 char buf
[PS_LINE_MAX
];
5255 llx_reg_contents
= lly_reg_contents
=
5256 urx_reg_contents
= ury_reg_contents
= 0;
5257 if (!ps_get_line(buf
, fp
, filename
)) {
5258 error("`%1' is empty", filename
);
5261 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5262 error("`%1' is not conforming to the Document Structuring Conventions",
5266 while (ps_get_line(buf
, fp
, filename
) != 0) {
5267 if (buf
[0] != '%' || buf
[1] != '%'
5268 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5270 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5271 int res
= parse_bounding_box(buf
+ 14, &bb
);
5273 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5276 else if (res
== 2) {
5281 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5290 /* in the trailer, the last BoundingBox comment is significant */
5291 for (offset
= 512; !last_try
; offset
*= 2) {
5292 int had_trailer
= 0;
5294 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5296 if (fseek(fp
, 0L, 0) == -1)
5299 while (ps_get_line(buf
, fp
, filename
) != 0) {
5300 if (buf
[0] == '%' && buf
[1] == '%') {
5302 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5306 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5307 int res
= parse_bounding_box(buf
+ 14, &bb
);
5310 else if (res
== 2) {
5311 error("`(atend)' not allowed in trailer of `%1'", filename
);
5315 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5324 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5329 error("%%%%BoundingBox comment not found in `%1'", filename
);
5332 void ps_bbox_request()
5334 symbol nm
= get_long_name(1);
5338 while (!tok
.newline() && !tok
.eof())
5341 // PS files might contain non-printable characters, such as ^Z
5342 // and CRs not followed by an LF, so open them in binary mode.
5343 FILE *fp
= fopen(nm
.contents(), FOPEN_RB
);
5345 do_ps_file(fp
, nm
.contents());
5349 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5354 const char *asciify(int c
)
5357 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5358 buf
[1] = buf
[2] = '\0';
5360 case ESCAPE_QUESTION
:
5363 case ESCAPE_AMPERSAND
:
5366 case ESCAPE_RIGHT_PARENTHESIS
:
5369 case ESCAPE_UNDERSCORE
:
5375 case ESCAPE_CIRCUMFLEX
:
5378 case ESCAPE_LEFT_BRACE
:
5381 case ESCAPE_RIGHT_BRACE
:
5384 case ESCAPE_LEFT_QUOTE
:
5387 case ESCAPE_RIGHT_QUOTE
:
5405 case ESCAPE_PERCENT
:
5418 if (invalid_input_char(c
))
5427 const char *input_char_description(int c
)
5431 return "a newline character";
5433 return "a backspace character";
5435 return "a leader character";
5437 return "a tab character";
5439 return "a space character";
5443 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
5444 if (invalid_input_char(c
)) {
5445 const char *s
= asciify(c
);
5452 sprintf(buf
, "magic character code %d", c
);
5461 sprintf(buf
, "character code %d", c
);
5465 // .tm, .tm1, and .tmc
5467 void do_terminal(int newline
, int string_like
)
5469 if (!tok
.newline() && !tok
.eof()) {
5473 if (string_like
&& c
== '"') {
5477 if (c
!= ' ' && c
!= '\t')
5480 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5481 fputs(asciify(c
), stderr
);
5484 fputc('\n', stderr
);
5499 void terminal_continue()
5504 dictionary
stream_dictionary(20);
5506 void do_open(int append
)
5508 symbol stream
= get_name(1);
5509 if (!stream
.is_null()) {
5510 symbol filename
= get_long_name(1);
5511 if (!filename
.is_null()) {
5513 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
5515 error("can't open `%1' for %2: %3",
5516 filename
.contents(),
5517 append
? "appending" : "writing",
5519 fp
= (FILE *)stream_dictionary
.remove(stream
);
5522 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
5533 error(".open request not allowed in safer mode");
5540 void opena_request()
5543 error(".opena request not allowed in safer mode");
5550 void close_request()
5552 symbol stream
= get_name(1);
5553 if (!stream
.is_null()) {
5554 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
5556 error("no stream named `%1'", stream
.contents());
5563 // .write and .writec
5565 void do_write_request(int newline
)
5567 symbol stream
= get_name(1);
5568 if (stream
.is_null()) {
5572 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5574 error("no stream named `%1'", stream
.contents());
5579 while ((c
= get_copy(0)) == ' ')
5583 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5584 fputs(asciify(c
), fp
);
5591 void write_request()
5593 do_write_request(1);
5596 void write_request_continue()
5598 do_write_request(0);
5601 void write_macro_request()
5603 symbol stream
= get_name(1);
5604 if (stream
.is_null()) {
5608 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5610 error("no stream named `%1'", stream
.contents());
5614 symbol s
= get_name(1);
5619 request_or_macro
*p
= lookup_request(s
);
5620 macro
*m
= p
->to_macro();
5622 error("cannot write request");
5624 string_iterator
iter(*m
);
5626 int c
= iter
.get(0);
5629 fputs(asciify(c
), fp
);
5636 void warnscale_request()
5643 warn_scale
= (double)units_per_inch
;
5645 warn_scale
= (double)units_per_inch
/ 2.54;
5647 warn_scale
= (double)units_per_inch
/ 72.0;
5649 warn_scale
= (double)units_per_inch
/ 6.0;
5652 "invalid scaling indicator `%1', using `i' instead", c
);
5655 warn_scaling_indicator
= c
;
5660 void spreadwarn_request()
5663 if (has_arg() && get_hunits(&n
, 'm')) {
5666 hunits em
= curenv
->get_size();
5667 spread_limit
= (double)n
.to_units()
5668 / (em
.is_zero() ? hresolution
: em
.to_units());
5671 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
5672 // changing value; we mirror at
5673 // -0.5 to make zero a valid value
5677 static void init_charset_table()
5680 strcpy(buf
, "char");
5681 for (int i
= 0; i
< 256; i
++) {
5682 strcpy(buf
+ 4, i_to_a(i
));
5683 charset_table
[i
] = get_charinfo(symbol(buf
));
5684 charset_table
[i
]->set_ascii_code(i
);
5686 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
5688 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
5689 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
5690 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
5691 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
5692 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
5693 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
5694 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
5695 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
5696 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
5697 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
5698 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
5699 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
5700 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5701 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5702 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5703 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
5704 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
5705 page_character
= charset_table
['%'];
5708 static void init_hpf_code_table()
5710 for (int i
= 0; i
< 256; i
++)
5711 hpf_code_table
[i
] = i
;
5714 static void do_translate(int translate_transparent
, int translate_input
)
5717 while (!tok
.newline() && !tok
.eof()) {
5719 // This is a really bizarre troff feature.
5721 translate_space_to_dummy
= tok
.dummy();
5722 if (tok
.newline() || tok
.eof())
5727 charinfo
*ci1
= tok
.get_char(1);
5731 if (tok
.newline() || tok
.eof()) {
5732 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5733 translate_transparent
);
5737 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
5738 translate_transparent
);
5739 else if (tok
.stretchable_space())
5740 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
5741 translate_transparent
);
5742 else if (tok
.dummy())
5743 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
5744 translate_transparent
);
5745 else if (tok
.hyphen_indicator())
5746 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
5747 translate_transparent
);
5749 charinfo
*ci2
= tok
.get_char(1);
5753 ci1
->set_translation(0, translate_transparent
, translate_input
);
5755 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
5767 void translate_no_transparent()
5772 void translate_input()
5780 if (get_integer(&flags
))
5782 charinfo
*ci
= tok
.get_char(1);
5784 charinfo
*tem
= ci
->get_translation();
5787 ci
->set_flags(flags
);
5794 void hyphenation_code()
5797 while (!tok
.newline() && !tok
.eof()) {
5798 charinfo
*ci
= tok
.get_char(1);
5803 unsigned char c
= tok
.ch();
5805 error("hyphenation code must be ordinary character");
5809 error("hyphenation code cannot be digit");
5812 ci
->set_hyphenation_code(c
);
5813 if (ci
->get_translation()
5814 && ci
->get_translation()->get_translation_input())
5815 ci
->get_translation()->set_hyphenation_code(c
);
5822 void hyphenation_patterns_file_code()
5825 while (!tok
.newline() && !tok
.eof()) {
5827 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
5829 error("missing output hyphenation code");
5832 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
5833 hpf_code_table
[n1
] = n2
;
5837 error("output hyphenation code must be integer in the range 0..255");
5842 error("input hyphenation code must be integer in the range 0..255");
5849 charinfo
*token::get_char(int required
)
5851 if (type
== TOKEN_CHAR
)
5852 return charset_table
[c
];
5853 if (type
== TOKEN_SPECIAL
)
5854 return get_charinfo(nm
);
5855 if (type
== TOKEN_NUMBERED_CHAR
)
5856 return get_charinfo_by_number(val
);
5857 if (type
== TOKEN_ESCAPE
) {
5858 if (escape_char
!= 0)
5859 return charset_table
[escape_char
];
5861 error("`\\e' used while no current escape character");
5866 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
5867 warning(WARN_MISSING
, "missing normal or special character");
5869 error("normal or special character expected (got %1)", description());
5874 charinfo
*get_optional_char()
5878 charinfo
*ci
= tok
.get_char();
5880 check_missing_character();
5886 void check_missing_character()
5888 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
5889 error("normal or special character expected (got %1): "
5890 "treated as missing",
5896 int token::add_to_node_list(node
**pp
)
5903 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
5909 if (escape_char
!= 0)
5910 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
5912 case TOKEN_HYPHEN_INDICATOR
:
5913 *pp
= (*pp
)->add_discretionary_hyphen();
5915 case TOKEN_ITALIC_CORRECTION
:
5916 *pp
= (*pp
)->add_italic_correction(&w
);
5918 case TOKEN_LEFT_BRACE
:
5920 case TOKEN_MARK_INPUT
:
5921 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
5927 case TOKEN_NUMBERED_CHAR
:
5928 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
5930 case TOKEN_RIGHT_BRACE
:
5933 n
= new hmotion_node(curenv
->get_space_width());
5936 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
5938 case TOKEN_STRETCHABLE_SPACE
:
5939 n
= new unbreakable_space_node(curenv
->get_space_width());
5941 case TOKEN_TRANSPARENT_DUMMY
:
5942 n
= new transparent_dummy_node
;
5944 case TOKEN_ZERO_WIDTH_BREAK
:
5945 n
= new space_node(H0
);
5947 n
->is_escape_colon();
5959 void token::process()
5961 if (possibly_handle_first_page_transition())
5964 case TOKEN_BACKSPACE
:
5965 curenv
->add_node(new hmotion_node(-curenv
->get_space_width()));
5968 curenv
->add_char(charset_table
[c
]);
5971 curenv
->add_node(new dummy_node
);
5980 if (escape_char
!= 0)
5981 curenv
->add_char(charset_table
[escape_char
]);
5983 case TOKEN_BEGIN_TRAP
:
5984 case TOKEN_END_TRAP
:
5985 case TOKEN_PAGE_EJECTOR
:
5986 // these are all handled in process_input_stack()
5988 case TOKEN_HYPHEN_INDICATOR
:
5989 curenv
->add_hyphen_indicator();
5991 case TOKEN_INTERRUPT
:
5992 curenv
->interrupt();
5994 case TOKEN_ITALIC_CORRECTION
:
5995 curenv
->add_italic_correction();
5998 curenv
->handle_tab(1);
6000 case TOKEN_LEFT_BRACE
:
6002 case TOKEN_MARK_INPUT
:
6003 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6009 curenv
->add_node(nd
);
6012 case TOKEN_NUMBERED_CHAR
:
6013 curenv
->add_char(get_charinfo_by_number(val
));
6016 // handled in process_input_stack()
6018 case TOKEN_RIGHT_BRACE
:
6024 curenv
->add_char(get_charinfo(nm
));
6029 case TOKEN_STRETCHABLE_SPACE
:
6030 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width()));
6033 curenv
->handle_tab(0);
6035 case TOKEN_TRANSPARENT
:
6037 case TOKEN_TRANSPARENT_DUMMY
:
6038 curenv
->add_node(new transparent_dummy_node
);
6040 case TOKEN_ZERO_WIDTH_BREAK
:
6042 node
*tmp
= new space_node(H0
);
6043 tmp
->freeze_space();
6044 tmp
->is_escape_colon();
6045 curenv
->add_node(tmp
);
6053 class nargs_reg
: public reg
{
6055 const char *get_string();
6058 const char *nargs_reg::get_string()
6060 return i_to_a(input_stack::nargs());
6063 class lineno_reg
: public reg
{
6065 const char *get_string();
6068 const char *lineno_reg::get_string()
6072 if (!input_stack::get_location(0, &file
, &line
))
6074 return i_to_a(line
);
6077 class writable_lineno_reg
: public general_reg
{
6079 writable_lineno_reg();
6080 void set_value(units
);
6081 int get_value(units
*);
6084 writable_lineno_reg::writable_lineno_reg()
6088 int writable_lineno_reg::get_value(units
*res
)
6092 if (!input_stack::get_location(0, &file
, &line
))
6098 void writable_lineno_reg::set_value(units n
)
6100 input_stack::set_location(0, n
);
6103 class filename_reg
: public reg
{
6105 const char *get_string();
6108 const char *filename_reg::get_string()
6112 if (input_stack::get_location(0, &file
, &line
))
6118 class constant_reg
: public reg
{
6121 constant_reg(const char *);
6122 const char *get_string();
6125 constant_reg::constant_reg(const char *p
) : s(p
)
6129 const char *constant_reg::get_string()
6134 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6138 const char *constant_int_reg::get_string()
6143 void abort_request()
6148 else if (tok
.newline())
6151 while ((c
= get_copy(0)) == ' ')
6154 if (c
== EOF
|| c
== '\n')
6155 fputs("User Abort.", stderr
);
6157 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6158 fputs(asciify(c
), stderr
);
6160 fputc('\n', stderr
);
6161 cleanup_and_exit(1);
6167 char *s
= new char[len
];
6169 while ((c
= get_copy(0)) == ' ')
6172 while (c
!= '\n' && c
!= EOF
) {
6173 if (!invalid_input_char(c
)) {
6176 s
= new char[len
*2];
6177 memcpy(s
, tem
, len
);
6197 error(".pi request not allowed in safer mode");
6201 #ifdef POPEN_MISSING
6202 error("pipes not available on this system");
6204 #else /* not POPEN_MISSING */
6206 error("can't pipe: output already started");
6211 if ((pc
= read_string()) == 0)
6212 error("can't pipe to empty command");
6214 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6215 strcpy(s
, pipe_command
);
6218 a_delete pipe_command
;
6225 #endif /* not POPEN_MISSING */
6229 static int system_status
;
6231 void system_request()
6234 error(".sy request not allowed in safer mode");
6238 char *command
= read_string();
6240 error("empty command");
6242 system_status
= system(command
);
6250 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6251 handle_initial_request(COPY_FILE_REQUEST
);
6254 symbol filename
= get_long_name(1);
6255 while (!tok
.newline() && !tok
.eof())
6259 if (!filename
.is_null())
6260 curdiv
->copy_file(filename
.contents());
6268 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6269 handle_initial_request(VJUSTIFY_REQUEST
);
6272 symbol type
= get_long_name(1);
6273 if (!type
.is_null())
6274 curdiv
->vjustify(type
);
6280 void transparent_file()
6282 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6283 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6286 symbol filename
= get_long_name(1);
6287 while (!tok
.newline() && !tok
.eof())
6291 if (!filename
.is_null()) {
6293 FILE *fp
= fopen(filename
.contents(), "r");
6295 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6302 if (invalid_input_char(c
))
6303 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6305 curdiv
->transparent_output(c
);
6310 curdiv
->transparent_output('\n');
6322 page_range(int, int, page_range
*);
6323 int contains(int n
);
6326 page_range::page_range(int i
, int j
, page_range
*p
)
6327 : first(i
), last(j
), next(p
)
6331 int page_range::contains(int n
)
6333 return n
>= first
&& (last
<= 0 || n
<= last
);
6336 page_range
*output_page_list
= 0;
6338 int in_output_page_list(int n
)
6340 if (!output_page_list
)
6342 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
6348 static void parse_output_page_list(char *p
)
6354 else if (csdigit(*p
)) {
6357 i
= i
*10 + *p
++ - '0';
6358 while (csdigit(*p
));
6368 j
= j
*10 + *p
++ - '0';
6369 while (csdigit(*p
));
6375 last_page_number
= -1;
6376 else if (last_page_number
>= 0 && j
> last_page_number
)
6377 last_page_number
= j
;
6378 output_page_list
= new page_range(i
, j
, output_page_list
);
6384 error("bad output page list");
6385 output_page_list
= 0;
6389 static FILE *open_mac_file(const char *mac
, char **path
)
6391 // Try first FOOBAR.tmac, then tmac.FOOBAR
6392 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
6394 strcat(s1
, MACRO_POSTFIX
);
6395 FILE *fp
= mac_path
->open_file(s1
, path
);
6398 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
6399 strcpy(s2
, MACRO_PREFIX
);
6401 fp
= mac_path
->open_file(s2
, path
);
6407 static void process_macro_file(const char *mac
)
6410 FILE *fp
= open_mac_file(mac
, &path
);
6412 fatal("can't find macro file %1", mac
);
6413 const char *s
= symbol(path
).contents();
6415 input_stack::push(new file_iterator(fp
, s
));
6417 process_input_stack();
6420 static void process_startup_file(char *filename
)
6423 search_path
*orig_mac_path
= mac_path
;
6424 mac_path
= &config_macro_path
;
6425 FILE *fp
= mac_path
->open_file(filename
, &path
);
6427 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6430 process_input_stack();
6432 mac_path
= orig_mac_path
;
6437 symbol nm
= get_long_name(1);
6441 while (!tok
.newline() && !tok
.eof())
6444 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
6445 // .mso doesn't (and cannot) go through open_mac_file, so we
6446 // need to do it here manually: If we have tmac.FOOBAR, try
6447 // FOOBAR.tmac and vice versa
6449 const char *fn
= nm
.contents();
6450 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
6451 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
6452 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
6453 strcat(s
, MACRO_POSTFIX
);
6454 fp
= mac_path
->open_file(s
, &path
);
6458 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
6459 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
6460 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
6461 strcpy(s
, MACRO_PREFIX
);
6462 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
6463 fp
= mac_path
->open_file(s
, &path
);
6469 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6473 error("can't find macro file `%1'", nm
.contents());
6478 static void process_input_file(const char *name
)
6481 if (strcmp(name
, "-") == 0) {
6487 fp
= fopen(name
, "r");
6489 fatal("can't open `%1': %2", name
, strerror(errno
));
6491 input_stack::push(new file_iterator(fp
, name
));
6493 process_input_stack();
6496 // make sure the_input is empty before calling this
6498 static int evaluate_expression(const char *expr
, units
*res
)
6500 input_stack::push(make_temp_iterator(expr
));
6502 int success
= get_number(res
, 'u');
6503 while (input_stack::get(0) != EOF
)
6508 static void do_register_assignment(const char *s
)
6510 const char *p
= strchr(s
, '=');
6516 if (evaluate_expression(s
+ 1, &n
))
6517 set_number_reg(buf
, n
);
6520 char *buf
= new char[p
- s
+ 1];
6521 memcpy(buf
, s
, p
- s
);
6524 if (evaluate_expression(p
+ 1, &n
))
6525 set_number_reg(buf
, n
);
6530 static void set_string(const char *name
, const char *value
)
6532 macro
*m
= new macro
;
6533 for (const char *p
= value
; *p
; p
++)
6534 if (!invalid_input_char((unsigned char)*p
))
6536 request_dictionary
.define(name
, m
);
6539 static void do_string_assignment(const char *s
)
6541 const char *p
= strchr(s
, '=');
6546 set_string(buf
, s
+ 1);
6549 char *buf
= new char[p
- s
+ 1];
6550 memcpy(buf
, s
, p
- s
);
6552 set_string(buf
, p
+ 1);
6557 struct string_list
{
6560 string_list(const char *ss
) : s(ss
), next(0) {}
6564 static void prepend_string(const char *s
, string_list
**p
)
6566 string_list
*l
= new string_list(s
);
6572 static void add_string(const char *s
, string_list
**p
)
6576 *p
= new string_list(s
);
6579 void usage(FILE *stream
, const char *prog
)
6582 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6583 " -rcn -Tname -Fdir -Mdir [files...]\n",
6587 int main(int argc
, char **argv
)
6589 program_name
= argv
[0];
6590 static char stderr_buf
[BUFSIZ
];
6591 setbuf(stderr
, stderr_buf
);
6593 string_list
*macros
= 0;
6594 string_list
*register_assignments
= 0;
6595 string_list
*string_assignments
= 0;
6600 int no_rc
= 0; // don't process troffrc and troffrc-end
6601 int next_page_number
;
6603 hresolution
= vresolution
= 1;
6604 // restore $PATH if called from groff
6605 char* groff_path
= getenv("GROFF_PATH__");
6612 if (putenv(strsave(e
.contents())))
6613 fatal("putenv failed");
6615 static const struct option long_options
[] = {
6616 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
6617 { "version", no_argument
, 0, 'v' },
6620 while ((c
= getopt_long(argc
, argv
, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6626 printf("GNU troff (groff) version %s\n", Version_string
);
6633 is_html
= (strcmp(device
, "html") == 0);
6636 compatible_flag
= 1;
6639 disable_color_flag
= 1;
6642 macro_path
.command_line_dir(optarg
);
6643 safer_macro_path
.command_line_dir(optarg
);
6644 config_macro_path
.command_line_dir(optarg
);
6647 font::command_line_font_dir(optarg
);
6650 add_string(optarg
, ¯os
);
6659 enable_warning(optarg
);
6662 disable_warning(optarg
);
6671 ascii_output_flag
= 1;
6674 suppress_output_flag
= 1;
6677 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
6680 error("bad page number");
6683 parse_output_page_list(optarg
);
6686 if (*optarg
== '\0')
6687 error("`-d' requires non-empty argument");
6689 add_string(optarg
, &string_assignments
);
6692 if (*optarg
== '\0')
6693 error("`-r' requires non-empty argument");
6695 add_string(optarg
, ®ister_assignments
);
6698 default_family
= symbol(optarg
);
6704 // silently ignore these
6707 safer_flag
= 0; // unsafe behaviour
6709 case CHAR_MAX
+ 1: // --help
6710 usage(stdout
, argv
[0]);
6714 usage(stderr
, argv
[0]);
6716 break; // never reached
6721 mac_path
= ¯o_path
;
6722 set_string(".T", device
);
6723 init_charset_table();
6724 init_hpf_code_table();
6725 if (!font::load_desc())
6726 fatal("sorry, I can't continue");
6727 units_per_inch
= font::res
;
6728 hresolution
= font::hor
;
6729 vresolution
= font::vert
;
6730 sizescale
= font::sizescale
;
6731 tcommand_flag
= font::tcommand
;
6732 warn_scale
= (double)units_per_inch
;
6733 warn_scaling_indicator
= 'i';
6734 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
6735 default_family
= symbol(font::family
);
6736 font_size::init_size_table(font::sizes
);
6739 if (font::style_table
) {
6740 for (i
= 0; font::style_table
[i
]; i
++)
6741 mount_style(j
++, symbol(font::style_table
[i
]));
6743 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
6744 // In the DESC file a font name of 0 (zero) means leave this
6746 if (strcmp(font::font_name_table
[i
], "0") != 0)
6747 mount_font(j
, symbol(font::font_name_table
[i
]));
6748 curdiv
= topdiv
= new top_level_diversion
;
6750 topdiv
->set_next_page_number(next_page_number
);
6751 init_input_requests();
6752 init_env_requests();
6753 init_div_requests();
6755 init_column_requests();
6757 init_node_requests();
6758 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
6760 init_reg_requests();
6761 init_hyphen_requests();
6762 init_environments();
6763 while (string_assignments
) {
6764 do_string_assignment(string_assignments
->s
);
6765 string_list
*tem
= string_assignments
;
6766 string_assignments
= string_assignments
->next
;
6769 while (register_assignments
) {
6770 do_register_assignment(register_assignments
->s
);
6771 string_list
*tem
= register_assignments
;
6772 register_assignments
= register_assignments
->next
;
6776 process_startup_file(INITIAL_STARTUP_FILE
);
6778 process_macro_file(macros
->s
);
6779 string_list
*tem
= macros
;
6780 macros
= macros
->next
;
6784 process_startup_file(FINAL_STARTUP_FILE
);
6785 for (i
= optind
; i
< argc
; i
++)
6786 process_input_file(argv
[i
]);
6787 if (optind
>= argc
|| iflag
)
6788 process_input_file("-");
6790 return 0; // not reached
6796 if (has_arg() && get_integer(&n
)) {
6797 if (n
& ~WARN_TOTAL
) {
6798 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
6804 warning_mask
= WARN_TOTAL
;
6808 static void init_registers()
6810 #ifdef LONG_FOR_TIME_T
6812 #else /* not LONG_FOR_TIME_T */
6814 #endif /* not LONG_FOR_TIME_T */
6816 // Use struct here to work around misfeature in old versions of g++.
6817 struct tm
*tt
= localtime(&t
);
6818 set_number_reg("dw", int(tt
->tm_wday
+ 1));
6819 set_number_reg("dy", int(tt
->tm_mday
));
6820 set_number_reg("mo", int(tt
->tm_mon
+ 1));
6821 set_number_reg("year", int(1900 + tt
->tm_year
));
6822 set_number_reg("yr", int(tt
->tm_year
));
6823 set_number_reg("$$", getpid());
6824 number_reg_dictionary
.define(".A",
6825 new constant_reg(ascii_output_flag
6831 * registers associated with \O
6834 static int output_reg_minx_contents
= -1;
6835 static int output_reg_miny_contents
= -1;
6836 static int output_reg_maxx_contents
= -1;
6837 static int output_reg_maxy_contents
= -1;
6839 void check_output_limits(int x
, int y
)
6841 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
6842 output_reg_minx_contents
= x
;
6843 if (x
> output_reg_maxx_contents
)
6844 output_reg_maxx_contents
= x
;
6845 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
6846 output_reg_miny_contents
= y
;
6847 if (y
> output_reg_maxy_contents
)
6848 output_reg_maxy_contents
= y
;
6851 void reset_output_registers(int miny
)
6853 // fprintf(stderr, "reset_output_registers\n");
6854 output_reg_minx_contents
= -1;
6855 output_reg_miny_contents
= -1;
6856 output_reg_maxx_contents
= -1;
6857 output_reg_maxy_contents
= -1;
6860 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
6862 *minx
= output_reg_minx_contents
;
6863 *miny
= output_reg_miny_contents
;
6864 *maxx
= output_reg_maxx_contents
;
6865 *maxy
= output_reg_maxy_contents
;
6868 void init_input_requests()
6870 init_request("ab", abort_request
);
6871 init_request("als", alias_macro
);
6872 init_request("am", append_macro
);
6873 init_request("am1", append_nocomp_macro
);
6874 init_request("ami", append_indirect_macro
);
6875 init_request("as", append_string
);
6876 init_request("as1", append_nocomp_string
);
6877 init_request("asciify", asciify_macro
);
6878 init_request("backtrace", backtrace_request
);
6879 init_request("blm", blank_line_macro
);
6880 init_request("break", while_break_request
);
6881 init_request("cf", copy_file
);
6882 init_request("cflags", char_flags
);
6883 init_request("char", define_character
);
6884 init_request("chop", chop_macro
);
6885 init_request("close", close_request
);
6886 init_request("continue", while_continue_request
);
6887 init_request("cp", compatible
);
6888 init_request("de", define_macro
);
6889 init_request("de1", define_nocomp_macro
);
6890 init_request("defcolor", define_color
);
6891 init_request("dei", define_indirect_macro
);
6892 init_request("do", do_request
);
6893 init_request("ds", define_string
);
6894 init_request("ds1", define_nocomp_string
);
6895 init_request("ec", set_escape_char
);
6896 init_request("ecr", restore_escape_char
);
6897 init_request("ecs", save_escape_char
);
6898 init_request("el", else_request
);
6899 init_request("em", end_macro
);
6900 init_request("eo", escape_off
);
6901 init_request("ex", exit_request
);
6902 init_request("fchar", define_fallback_character
);
6903 #ifdef WIDOW_CONTROL
6904 init_request("fpl", flush_pending_lines
);
6905 #endif /* WIDOW_CONTROL */
6906 init_request("hcode", hyphenation_code
);
6907 init_request("hpfcode", hyphenation_patterns_file_code
);
6908 init_request("ie", if_else_request
);
6909 init_request("if", if_request
);
6910 init_request("ig", ignore
);
6911 init_request("length", length_macro
);
6912 init_request("lf", line_file
);
6913 init_request("mso", macro_source
);
6914 init_request("nop", nop_request
);
6915 init_request("nx", next_file
);
6916 init_request("open", open_request
);
6917 init_request("opena", opena_request
);
6918 init_request("output", output_request
);
6919 init_request("pc", set_page_character
);
6920 init_request("pi", pipe_output
);
6921 init_request("pm", print_macros
);
6922 init_request("psbb", ps_bbox_request
);
6923 #ifndef POPEN_MISSING
6924 init_request("pso", pipe_source
);
6925 #endif /* not POPEN_MISSING */
6926 init_request("rchar", remove_character
);
6927 init_request("rd", read_request
);
6928 init_request("return", return_macro_request
);
6929 init_request("rm", remove_macro
);
6930 init_request("rn", rename_macro
);
6931 init_request("shift", shift
);
6932 init_request("so", source
);
6933 init_request("spreadwarn", spreadwarn_request
);
6934 init_request("substring", substring_macro
);
6935 init_request("sy", system_request
);
6936 init_request("tm", terminal
);
6937 init_request("tm1", terminal1
);
6938 init_request("tmc", terminal_continue
);
6939 init_request("tr", translate
);
6940 init_request("trf", transparent_file
);
6941 init_request("trin", translate_input
);
6942 init_request("trnt", translate_no_transparent
);
6943 init_request("unformat", unformat_macro
);
6944 init_request("warn", warn_request
);
6945 init_request("while", while_request
);
6946 init_request("write", write_request
);
6947 init_request("writec", write_request_continue
);
6948 init_request("writem", write_macro_request
);
6949 init_request("nroff", nroff_request
);
6950 init_request("troff", troff_request
);
6952 init_request("vj", vjustify
);
6954 init_request("warnscale", warnscale_request
);
6955 number_reg_dictionary
.define(".$", new nargs_reg
);
6956 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
6957 number_reg_dictionary
.define(".F", new filename_reg
);
6958 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
6959 number_reg_dictionary
.define(".R", new constant_reg("10000"));
6960 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
6961 extern const char *revision
;
6962 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
6963 number_reg_dictionary
.define(".c", new lineno_reg
);
6964 number_reg_dictionary
.define(".g", new constant_reg("1"));
6965 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
6966 extern const char *major_version
;
6967 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
6968 extern const char *minor_version
;
6969 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
6970 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
6971 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
6972 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
6973 number_reg_dictionary
.define("opmaxx",
6974 new variable_reg(&output_reg_maxx_contents
));
6975 number_reg_dictionary
.define("opmaxy",
6976 new variable_reg(&output_reg_maxy_contents
));
6977 number_reg_dictionary
.define("opminx",
6978 new variable_reg(&output_reg_minx_contents
));
6979 number_reg_dictionary
.define("opminy",
6980 new variable_reg(&output_reg_miny_contents
));
6981 number_reg_dictionary
.define("slimit",
6982 new variable_reg(&input_stack::limit
));
6983 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
6984 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
6985 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
6988 object_dictionary
request_dictionary(501);
6990 void init_request(const char *s
, REQUEST_FUNCP f
)
6992 request_dictionary
.define(s
, new request(f
));
6995 static request_or_macro
*lookup_request(symbol nm
)
6997 assert(!nm
.is_null());
6998 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7000 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
7002 request_dictionary
.define(nm
, p
);
7007 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7009 // Don't interpret character definitions in compatible mode.
7010 int old_compatible_flag
= compatible_flag
;
7011 compatible_flag
= 0;
7012 int old_escape_char
= escape_char
;
7014 macro
*mac
= ci
->set_macro(0);
7016 environment
*oldenv
= curenv
;
7017 environment
env(envp
);
7019 curenv
->set_composite();
7020 token old_tok
= tok
;
7021 input_stack::add_boundary();
7022 string_iterator
*si
=
7023 new string_iterator(*mac
, "composite character", ci
->nm
);
7024 input_stack::push(si
);
7025 // we don't use process_input_stack, because we don't want to recognise
7031 if (tok
.newline()) {
7032 error("composite character mustn't contain newline");
7040 node
*n
= curenv
->extract_output_line();
7041 input_stack::remove_boundary();
7045 compatible_flag
= old_compatible_flag
;
7046 escape_char
= old_escape_char
;
7050 static node
*read_draw_node()
7054 if (!start
.delimiter(1)){
7057 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7062 error("missing argument");
7064 unsigned char type
= tok
.ch();
7067 hvpair
*point
= new hvpair
[maxpoints
];
7072 for (i
= 0; tok
!= start
; i
++) {
7073 if (i
== maxpoints
) {
7074 hvpair
*oldpoint
= point
;
7075 point
= new hvpair
[maxpoints
*2];
7076 for (int j
= 0; j
< maxpoints
; j
++)
7077 point
[j
] = oldpoint
[j
];
7081 if (!get_hunits(&point
[i
].h
,
7082 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7093 if (!get_vunits(&point
[i
].v
, 'v')) {
7099 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7104 if (npoints
!= 1 || no_last_v
) {
7105 error("two arguments needed for line");
7110 if (npoints
!= 1 || !no_last_v
) {
7111 error("one argument needed for circle");
7117 if (npoints
!= 1 || no_last_v
) {
7118 error("two arguments needed for ellipse");
7123 if (npoints
!= 2 || no_last_v
) {
7124 error("four arguments needed for arc");
7130 error("even number of arguments needed for spline");
7133 if (npoints
!= 1 || !no_last_v
) {
7134 error("one argument needed for gray shade");
7139 // silently pass it through
7142 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7143 curenv
->get_font_size());
7158 } warning_table
[] = {
7159 { "char", WARN_CHAR
},
7160 { "range", WARN_RANGE
},
7161 { "break", WARN_BREAK
},
7162 { "delim", WARN_DELIM
},
7164 { "scale", WARN_SCALE
},
7165 { "number", WARN_NUMBER
},
7166 { "syntax", WARN_SYNTAX
},
7167 { "tab", WARN_TAB
},
7168 { "right-brace", WARN_RIGHT_BRACE
},
7169 { "missing", WARN_MISSING
},
7170 { "input", WARN_INPUT
},
7171 { "escape", WARN_ESCAPE
},
7172 { "space", WARN_SPACE
},
7173 { "font", WARN_FONT
},
7175 { "mac", WARN_MAC
},
7176 { "reg", WARN_REG
},
7178 { "color", WARN_COLOR
},
7179 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7180 { "w", WARN_TOTAL
},
7181 { "default", DEFAULT_WARNING_MASK
},
7184 static int lookup_warning(const char *name
)
7186 for (unsigned int i
= 0;
7187 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7189 if (strcmp(name
, warning_table
[i
].name
) == 0)
7190 return warning_table
[i
].mask
;
7194 static void enable_warning(const char *name
)
7196 int mask
= lookup_warning(name
);
7198 warning_mask
|= mask
;
7200 error("unknown warning `%1'", name
);
7203 static void disable_warning(const char *name
)
7205 int mask
= lookup_warning(name
);
7207 warning_mask
&= ~mask
;
7209 error("unknown warning `%1'", name
);
7212 static void copy_mode_error(const char *format
,
7218 static const char prefix
[] = "(in ignored input) ";
7219 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7222 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7226 error(format
, arg1
, arg2
, arg3
);
7229 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7231 static void do_error(error_type type
,
7237 const char *filename
;
7239 if (inhibit_errors
&& type
< FATAL
)
7242 input_stack::backtrace();
7243 if (!get_file_line(&filename
, &lineno
))
7246 errprint("%1:%2: ", filename
, lineno
);
7247 else if (program_name
)
7248 fprintf(stderr
, "%s: ", program_name
);
7251 fputs("fatal error: ", stderr
);
7256 fputs("warning: ", stderr
);
7258 case OUTPUT_WARNING
:
7259 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7260 fprintf(stderr
, "warning [p %d, %.1f%c",
7261 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7262 if (topdiv
!= curdiv
) {
7263 double fromtop1
= curdiv
->get_vertical_position().to_units()
7265 fprintf(stderr
, ", div `%s', %.1f%c",
7266 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7268 fprintf(stderr
, "]: ");
7271 errprint(format
, arg1
, arg2
, arg3
);
7272 fputc('\n', stderr
);
7275 cleanup_and_exit(1);
7278 int warning(warning_type t
,
7284 if ((t
& warning_mask
) != 0) {
7285 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
7292 int output_warning(warning_type t
,
7298 if ((t
& warning_mask
) != 0) {
7299 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
7306 void error(const char *format
,
7311 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
7314 void fatal(const char *format
,
7319 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
7322 void fatal_with_file_and_line(const char *filename
, int lineno
,
7328 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
7329 errprint(format
, arg1
, arg2
, arg3
);
7330 fputc('\n', stderr
);
7332 cleanup_and_exit(1);
7335 void error_with_file_and_line(const char *filename
, int lineno
,
7341 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
7342 errprint(format
, arg1
, arg2
, arg3
);
7343 fputc('\n', stderr
);
7347 dictionary
charinfo_dictionary(501);
7349 charinfo
*get_charinfo(symbol nm
)
7351 void *p
= charinfo_dictionary
.lookup(nm
);
7353 return (charinfo
*)p
;
7354 charinfo
*cp
= new charinfo(nm
);
7355 (void)charinfo_dictionary
.lookup(nm
, cp
);
7359 int charinfo::next_index
= 0;
7361 charinfo::charinfo(symbol s
)
7362 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
7363 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7364 not_found(0), transparent_translate(1), translate_input(0),
7367 index
= next_index
++;
7370 void charinfo::set_hyphenation_code(unsigned char c
)
7372 hyphenation_code
= c
;
7375 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
7379 if (hyphenation_code
!= 0)
7380 ci
->set_hyphenation_code(hyphenation_code
);
7381 if (asciify_code
!= 0)
7382 ci
->set_asciify_code(asciify_code
);
7383 else if (ascii_code
!= 0)
7384 ci
->set_asciify_code(ascii_code
);
7385 ci
->set_translation_input();
7387 special_translation
= TRANSLATE_NONE
;
7388 transparent_translate
= tt
;
7391 void charinfo::set_special_translation(int c
, int tt
)
7393 special_translation
= c
;
7395 transparent_translate
= tt
;
7398 void charinfo::set_ascii_code(unsigned char c
)
7403 void charinfo::set_asciify_code(unsigned char c
)
7408 macro
*charinfo::set_macro(macro
*m
, int f
)
7416 void charinfo::set_number(int n
)
7422 int charinfo::get_number()
7424 assert(flags
& NUMBERED
);
7428 symbol
UNNAMED_SYMBOL("---");
7430 // For numbered characters not between 0 and 255, we make a symbol out
7431 // of the number and store them in this dictionary.
7433 dictionary
numbered_charinfo_dictionary(11);
7435 charinfo
*get_charinfo_by_number(int n
)
7437 static charinfo
*number_table
[256];
7439 if (n
>= 0 && n
< 256) {
7440 charinfo
*ci
= number_table
[n
];
7442 ci
= new charinfo(UNNAMED_SYMBOL
);
7444 number_table
[n
] = ci
;
7449 symbol
ns(i_to_a(n
));
7450 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
7452 ci
= new charinfo(UNNAMED_SYMBOL
);
7454 numbered_charinfo_dictionary
.lookup(ns
, ci
);
7460 int font::name_to_index(const char *nm
)
7464 ci
= charset_table
[nm
[0] & 0xff];
7465 else if (nm
[0] == '\\' && nm
[2] == 0)
7466 ci
= get_charinfo(symbol(nm
+ 1));
7468 ci
= get_charinfo(symbol(nm
));
7472 return ci
->get_index();
7475 int font::number_to_index(int n
)
7477 return get_charinfo_by_number(n
)->get_index();