2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
26 #include "dictionary.h"
28 #include "stringclass.h"
38 #include "macropath.h"
43 // Needed for getpid() and isatty()
48 #ifdef NEED_DECLARATION_PUTENV
50 int putenv(const char *);
52 #endif /* NEED_DECLARATION_PUTENV */
54 #define MACRO_PREFIX "tmac."
55 #define MACRO_POSTFIX ".tmac"
56 #define INITIAL_STARTUP_FILE "troffrc"
57 #define FINAL_STARTUP_FILE "troffrc-end"
58 #define DEFAULT_INPUT_STACK_LIMIT 1000
60 #ifndef DEFAULT_WARNING_MASK
61 // warnings that are enabled by default
62 #define DEFAULT_WARNING_MASK \
63 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
66 // initial size of buffer for reading names; expanded as necessary
69 extern "C" const char *program_name
;
70 extern "C" const char *Version_string
;
73 void init_column_requests();
76 static node
*read_draw_node();
77 static void read_color_draw_node(token
&);
78 static void push_token(const token
&);
83 void transparent_file();
87 int color_flag
= 1; // colors are on by default
88 static int backtrace_flag
= 0;
90 char *pipe_command
= 0;
92 charinfo
*charset_table
[256];
93 unsigned char hpf_code_table
[256];
95 static int warning_mask
= DEFAULT_WARNING_MASK
;
96 static int inhibit_errors
= 0;
97 static int ignoring
= 0;
99 static void enable_warning(const char *);
100 static void disable_warning(const char *);
102 static int escape_char
= '\\';
103 static symbol end_macro_name
;
104 static symbol blank_line_macro_name
;
105 static int compatible_flag
= 0;
106 int ascii_output_flag
= 0;
107 int suppress_output_flag
= 0;
109 int begin_level
= 0; // number of nested \O escapes
111 int have_input
= 0; // whether \f, \F, \D'F...', \H, \m, \M,
112 // \R, \s, or \S has been processed in
114 int old_have_input
= 0; // value of have_input right before \n
115 int tcommand_flag
= 0;
116 int unsafe_flag
= 0; // safer by default
118 int have_string_arg
= 0; // whether we have \*[foo bar...]
120 double spread_limit
= -3.0 - 1.0; // negative means deactivated
123 char warn_scaling_indicator
;
124 int debug_state
= 0; // turns on debugging of the html troff state
126 search_path
*mac_path
= &safer_macro_path
;
128 // Defaults to the current directory.
129 search_path
include_search_path(0, 0, 0, 1);
131 static int get_copy(node
**, int = 0, 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 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
138 static symbol
read_escape_name(read_mode
= NO_ARGS
);
139 static symbol
read_long_escape_name(read_mode
= NO_ARGS
);
140 static void interpolate_string(symbol
);
141 static void interpolate_string_with_args(symbol
);
142 static void interpolate_macro(symbol
, int = 0);
143 static void interpolate_number_format(symbol
);
144 static void interpolate_environment_variable(symbol
);
146 static symbol
composite_glyph_name(symbol
);
147 static void interpolate_arg(symbol
);
148 static request_or_macro
*lookup_request(symbol
);
149 static int get_delim_number(units
*, unsigned char);
150 static int get_delim_number(units
*, unsigned char, units
);
151 static symbol
do_get_long_name(int, char);
152 static int get_line_arg(units
*res
, unsigned char si
, charinfo
**cp
);
153 static int read_size(int *);
154 static symbol
get_delim_name();
155 static void init_registers();
156 static void trapping_blank_line();
158 class input_iterator
;
159 input_iterator
*make_temp_iterator(const char *);
160 const char *input_char_description(int);
162 void process_input_stack();
163 void chop_macro(); // declare to avoid friend name injection
166 void set_escape_char()
170 error("bad escape character");
174 escape_char
= tok
.ch();
187 static int saved_escape_char
= '\\';
189 void save_escape_char()
191 saved_escape_char
= escape_char
;
195 void restore_escape_char()
197 escape_char
= saved_escape_char
;
201 class input_iterator
{
204 input_iterator(int is_div
);
205 virtual ~input_iterator() {}
207 friend class input_stack
;
209 statem
*diversion_state
;
211 const unsigned char *ptr
;
212 const unsigned char *eptr
;
213 input_iterator
*next
;
215 virtual int fill(node
**);
217 virtual int has_args() { return 0; }
218 virtual int nargs() { return 0; }
219 virtual input_iterator
*get_arg(int) { return 0; }
220 virtual int space_follows_arg(int) { return 0; }
221 virtual int get_break_flag() { return 0; }
222 virtual int get_location(int, const char **, int *) { return 0; }
223 virtual void backtrace() {}
224 virtual int set_location(const char *, int) { return 0; }
225 virtual int next_file(FILE *, const char *) { return 0; }
226 virtual void shift(int) {}
227 virtual int is_boundary() {return 0; }
228 virtual int is_file() { return 0; }
229 virtual int is_macro() { return 0; }
230 virtual void save_compatible_flag(int) {}
231 virtual int get_compatible_flag() { return 0; }
234 input_iterator::input_iterator()
235 : is_diversion(0), ptr(0), eptr(0)
239 input_iterator::input_iterator(int is_div
)
240 : is_diversion(is_div
), ptr(0), eptr(0)
244 int input_iterator::fill(node
**)
249 int input_iterator::peek()
254 inline int input_iterator::get(node
**p
)
256 return ptr
< eptr
? *ptr
++ : fill(p
);
259 class input_boundary
: public input_iterator
{
261 int is_boundary() { return 1; }
264 class input_return_boundary
: public input_iterator
{
266 int is_boundary() { return 2; }
269 class file_iterator
: public input_iterator
{
272 const char *filename
;
276 enum { BUF_SIZE
= 512 };
277 unsigned char buf
[BUF_SIZE
];
280 file_iterator(FILE *, const char *, int = 0);
284 int get_location(int, const char **, int *);
286 int set_location(const char *, int);
287 int next_file(FILE *, const char *);
291 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
292 : fp(f
), lineno(1), filename(fn
), popened(po
),
293 newline_flag(0), seen_escape(0)
295 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
298 the_output
->put_filename(fn
, po
);
302 file_iterator::~file_iterator()
307 void file_iterator::close()
311 #ifndef POPEN_MISSING
314 #endif /* not POPEN_MISSING */
319 int file_iterator::is_file()
324 int file_iterator::next_file(FILE *f
, const char *s
)
338 int file_iterator::fill(node
**)
343 unsigned char *p
= buf
;
345 unsigned char *e
= p
+ BUF_SIZE
;
350 if (invalid_input_char(c
))
351 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
359 seen_escape
= (c
== '\\');
372 int file_iterator::peek()
375 while (invalid_input_char(c
)) {
376 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
384 int file_iterator::get_location(int /*allow_macro*/,
385 const char **filenamep
, int *linenop
)
388 if (filename
!= 0 && strcmp(filename
, "-") == 0)
389 *filenamep
= "<standard input>";
391 *filenamep
= filename
;
395 void file_iterator::backtrace()
397 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
398 popened
? "process" : "file");
401 int file_iterator::set_location(const char *f
, int ln
)
407 the_output
->put_filename(f
, 0);
413 input_iterator nil_iterator
;
417 static int get(node
**);
419 static void push(input_iterator
*);
420 static input_iterator
*get_arg(int);
421 static int space_follows_arg(int);
422 static int get_break_flag();
424 static int get_location(int, const char **, int *);
425 static int set_location(const char *, int);
426 static void backtrace();
427 static void backtrace_all();
428 static void next_file(FILE *, const char *);
429 static void end_file();
430 static void shift(int n
);
431 static void add_boundary();
432 static void add_return_boundary();
433 static int is_return_boundary();
434 static void remove_boundary();
435 static int get_level();
436 static int get_div_level();
437 static void increase_level();
438 static void decrease_level();
440 static void pop_macro();
441 static void save_compatible_flag(int);
442 static int get_compatible_flag();
443 static statem
*get_diversion_state();
444 static void check_end_diversion(input_iterator
*t
);
446 static int div_level
;
447 static statem
*diversion_state
;
449 static input_iterator
*top
;
451 static int finish_get(node
**);
452 static int finish_peek();
455 input_iterator
*input_stack::top
= &nil_iterator
;
456 int input_stack::level
= 0;
457 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
458 int input_stack::div_level
= 0;
459 statem
*input_stack::diversion_state
= NULL
;
463 inline int input_stack::get_level()
468 inline void input_stack::increase_level()
473 inline void input_stack::decrease_level()
478 inline int input_stack::get_div_level()
483 inline int input_stack::get(node
**np
)
485 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
487 old_have_input
= have_input
;
493 int input_stack::finish_get(node
**np
)
496 int c
= top
->fill(np
);
497 if (c
!= EOF
|| top
->is_boundary())
499 if (top
== &nil_iterator
)
501 input_iterator
*tem
= top
;
502 check_end_diversion(tem
);
503 #if defined(DEBUGGING)
505 if (tem
->is_diversion
)
507 "in diversion level = %d\n", input_stack::get_div_level());
512 if (top
->ptr
< top
->eptr
)
519 inline int input_stack::peek()
521 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
524 void input_stack::check_end_diversion(input_iterator
*t
)
526 if (t
->is_diversion
) {
528 diversion_state
= t
->diversion_state
;
532 int input_stack::finish_peek()
536 if (c
!= EOF
|| top
->is_boundary())
538 if (top
== &nil_iterator
)
540 input_iterator
*tem
= top
;
541 check_end_diversion(tem
);
545 if (top
->ptr
< top
->eptr
)
552 void input_stack::add_boundary()
554 push(new input_boundary
);
557 void input_stack::add_return_boundary()
559 push(new input_return_boundary
);
562 int input_stack::is_return_boundary()
564 return top
->is_boundary() == 2;
567 void input_stack::remove_boundary()
569 assert(top
->is_boundary());
570 input_iterator
*temp
= top
->next
;
571 check_end_diversion(top
);
578 void input_stack::push(input_iterator
*in
)
582 if (++level
> limit
&& limit
> 0)
583 fatal("input stack limit exceeded (probable infinite loop)");
586 if (top
->is_diversion
) {
588 in
->diversion_state
= diversion_state
;
589 diversion_state
= curenv
->construct_state(0);
590 #if defined(DEBUGGING)
592 curenv
->dump_troff_state();
597 #if defined(DEBUGGING)
599 if (top
->is_diversion
) {
601 "in diversion level = %d\n", input_stack::get_div_level());
607 statem
*get_diversion_state()
609 return input_stack::get_diversion_state();
612 statem
*input_stack::get_diversion_state()
614 if (diversion_state
== NULL
)
617 return new statem(diversion_state
);
620 input_iterator
*input_stack::get_arg(int i
)
623 for (p
= top
; p
!= 0; p
= p
->next
)
625 return p
->get_arg(i
);
629 int input_stack::space_follows_arg(int i
)
632 for (p
= top
; p
!= 0; p
= p
->next
)
634 return p
->space_follows_arg(i
);
638 int input_stack::get_break_flag()
640 return top
->get_break_flag();
643 void input_stack::shift(int n
)
645 for (input_iterator
*p
= top
; p
; p
= p
->next
)
652 int input_stack::nargs()
654 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
660 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
662 for (input_iterator
*p
= top
; p
; p
= p
->next
)
663 if (p
->get_location(allow_macro
, filenamep
, linenop
))
668 void input_stack::backtrace()
672 // only backtrace down to (not including) the topmost file
673 for (input_iterator
*p
= top
;
674 p
&& !p
->get_location(0, &f
, &n
);
679 void input_stack::backtrace_all()
681 for (input_iterator
*p
= top
; p
; p
= p
->next
)
685 int input_stack::set_location(const char *filename
, int lineno
)
687 for (input_iterator
*p
= top
; p
; p
= p
->next
)
688 if (p
->set_location(filename
, lineno
))
693 void input_stack::next_file(FILE *fp
, const char *s
)
696 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
697 if ((*pp
)->next_file(fp
, s
))
699 if (++level
> limit
&& limit
> 0)
700 fatal("input stack limit exceeded");
701 *pp
= new file_iterator(fp
, s
);
702 (*pp
)->next
= &nil_iterator
;
705 void input_stack::end_file()
707 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
708 if ((*pp
)->is_file()) {
709 input_iterator
*tem
= *pp
;
710 check_end_diversion(tem
);
718 void input_stack::clear()
721 while (top
!= &nil_iterator
) {
722 if (top
->is_boundary())
724 input_iterator
*tem
= top
;
725 check_end_diversion(tem
);
730 // Keep while_request happy.
731 for (; nboundaries
> 0; --nboundaries
)
732 add_return_boundary();
735 void input_stack::pop_macro()
740 if (top
->next
== &nil_iterator
)
742 if (top
->is_boundary())
744 is_macro
= top
->is_macro();
745 input_iterator
*tem
= top
;
746 check_end_diversion(tem
);
751 // Keep while_request happy.
752 for (; nboundaries
> 0; --nboundaries
)
753 add_return_boundary();
756 inline void input_stack::save_compatible_flag(int f
)
758 top
->save_compatible_flag(f
);
761 inline int input_stack::get_compatible_flag()
763 return top
->get_compatible_flag();
766 void backtrace_request()
768 input_stack::backtrace_all();
775 symbol nm
= get_long_name();
776 while (!tok
.newline() && !tok
.eof())
779 input_stack::end_file();
782 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
784 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
786 input_stack::next_file(fp
, nm
.contents());
794 if (!has_arg() || !get_integer(&n
))
796 input_stack::shift(n
);
800 static char get_char_for_escape_name(int allow_space
= 0)
802 int c
= get_copy(0, 0, 1);
805 copy_mode_error("end of input in escape name");
808 if (!invalid_input_char(c
))
813 input_stack::push(make_temp_iterator("\n"));
816 if (c
== ' ' && allow_space
)
822 copy_mode_error("%1 is not allowed in an escape name",
823 input_char_description(c
));
829 static symbol
read_two_char_escape_name()
832 buf
[0] = get_char_for_escape_name();
833 if (buf
[0] != '\0') {
834 buf
[1] = get_char_for_escape_name();
843 static symbol
read_long_escape_name(read_mode mode
)
845 int start_level
= input_stack::get_level();
846 char abuf
[ABUF_SIZE
];
848 int buf_size
= ABUF_SIZE
;
853 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
860 if (mode
== WITH_ARGS
&& c
== ' ')
862 if (i
+ 2 > buf_size
) {
864 buf
= new char[ABUF_SIZE
*2];
865 memcpy(buf
, abuf
, buf_size
);
866 buf_size
= ABUF_SIZE
*2;
870 buf
= new char[buf_size
*2];
871 memcpy(buf
, old_buf
, buf_size
);
876 if (c
== ']' && input_stack::get_level() == start_level
)
885 if (mode
!= ALLOW_EMPTY
)
886 copy_mode_error("empty escape name");
898 static symbol
read_escape_name(read_mode mode
)
900 char c
= get_char_for_escape_name();
904 return read_two_char_escape_name();
905 if (c
== '[' && !compatible_flag
)
906 return read_long_escape_name(mode
);
913 static symbol
read_increment_and_escape_name(int *incp
)
915 char c
= get_char_for_escape_name();
922 return read_two_char_escape_name();
925 return read_escape_name();
928 return read_escape_name();
930 if (!compatible_flag
) {
932 return read_long_escape_name();
943 static int get_copy(node
**nd
, int defining
, int handle_escape_E
)
946 int c
= input_stack::get(nd
);
947 if (c
== PUSH_GROFF_MODE
) {
948 input_stack::save_compatible_flag(compatible_flag
);
952 if (c
== PUSH_COMP_MODE
) {
953 input_stack::save_compatible_flag(compatible_flag
);
957 if (c
== POP_GROFFCOMP_MODE
) {
958 compatible_flag
= input_stack::get_compatible_flag();
961 if (c
== BEGIN_QUOTE
) {
962 input_stack::increase_level();
965 if (c
== END_QUOTE
) {
966 input_stack::decrease_level();
969 if (c
== DOUBLE_QUOTE
)
971 if (c
== ESCAPE_E
&& handle_escape_E
)
973 if (c
== ESCAPE_NEWLINE
) {
977 c
= input_stack::get(nd
);
978 } while (c
== ESCAPE_NEWLINE
);
980 if (c
!= escape_char
|| escape_char
<= 0)
983 c
= input_stack::peek();
988 (void)input_stack::get(0);
989 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
992 case '#': // Like \" but newline is ignored.
993 (void)input_stack::get(0);
994 while ((c
= input_stack::get(0)) != '\n')
1000 (void)input_stack::get(0);
1001 symbol s
= read_escape_name();
1002 if (!(s
.is_null() || s
.is_empty()))
1008 (void)input_stack::get(0);
1009 symbol s
= read_escape_name(WITH_ARGS
);
1010 if (!(s
.is_null() || s
.is_empty())) {
1011 if (have_string_arg
) {
1012 have_string_arg
= 0;
1013 interpolate_string_with_args(s
);
1016 interpolate_string(s
);
1021 (void)input_stack::get(0);
1024 (void)input_stack::get(0);
1027 (void)input_stack::get(0);
1028 if (handle_escape_E
)
1033 (void)input_stack::get(0);
1035 symbol s
= read_increment_and_escape_name(&inc
);
1036 if (!(s
.is_null() || s
.is_empty()))
1037 interpolate_number_reg(s
, inc
);
1042 (void)input_stack::get(0);
1043 symbol s
= read_escape_name();
1044 if (!(s
.is_null() || s
.is_empty()))
1045 interpolate_number_format(s
);
1049 (void)input_stack::get(0);
1053 (void)input_stack::get(0);
1054 symbol s
= read_escape_name();
1055 if (!(s
.is_null() || s
.is_empty()))
1056 interpolate_environment_variable(s
);
1060 (void)input_stack::get(0);
1062 return ESCAPE_NEWLINE
;
1065 (void)input_stack::get(0);
1066 return ESCAPE_SPACE
;
1068 (void)input_stack::get(0);
1069 return ESCAPE_TILDE
;
1071 (void)input_stack::get(0);
1072 return ESCAPE_COLON
;
1074 (void)input_stack::get(0);
1077 (void)input_stack::get(0);
1078 return ESCAPE_CIRCUMFLEX
;
1080 (void)input_stack::get(0);
1081 return ESCAPE_LEFT_BRACE
;
1083 (void)input_stack::get(0);
1084 return ESCAPE_RIGHT_BRACE
;
1086 (void)input_stack::get(0);
1087 return ESCAPE_LEFT_QUOTE
;
1089 (void)input_stack::get(0);
1090 return ESCAPE_RIGHT_QUOTE
;
1092 (void)input_stack::get(0);
1093 return ESCAPE_HYPHEN
;
1095 (void)input_stack::get(0);
1096 return ESCAPE_UNDERSCORE
;
1098 (void)input_stack::get(0);
1101 (void)input_stack::get(0);
1104 (void)input_stack::get(0);
1105 return ESCAPE_QUESTION
;
1107 (void)input_stack::get(0);
1108 return ESCAPE_AMPERSAND
;
1110 (void)input_stack::get(0);
1111 return ESCAPE_RIGHT_PARENTHESIS
;
1113 (void)input_stack::get(0);
1116 (void)input_stack::get(0);
1117 return ESCAPE_PERCENT
;
1119 if (c
== escape_char
) {
1120 (void)input_stack::get(0);
1129 class non_interpreted_char_node
: public node
{
1132 non_interpreted_char_node(unsigned char);
1134 int interpret(macro
*);
1141 int non_interpreted_char_node::same(node
*nd
)
1143 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1146 const char *non_interpreted_char_node::type()
1148 return "non_interpreted_char_node";
1151 int non_interpreted_char_node::force_tprint()
1156 int non_interpreted_char_node::is_tag()
1161 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1166 node
*non_interpreted_char_node::copy()
1168 return new non_interpreted_char_node(c
);
1171 int non_interpreted_char_node::interpret(macro
*mac
)
1177 static void do_width();
1178 static node
*do_non_interpreted();
1179 static node
*do_special();
1180 static node
*do_suppress(symbol nm
);
1181 static void do_register();
1183 dictionary
color_dictionary(501);
1185 static color
*lookup_color(symbol nm
)
1187 assert(!nm
.is_null());
1188 if (nm
== default_symbol
)
1189 return &default_color
;
1190 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1192 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1196 void do_glyph_color(symbol nm
)
1201 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1203 color
*tem
= lookup_color(nm
);
1205 curenv
->set_glyph_color(tem
);
1207 (void)color_dictionary
.lookup(nm
, new color(nm
));
1211 void do_fill_color(symbol nm
)
1216 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1218 color
*tem
= lookup_color(nm
);
1220 curenv
->set_fill_color(tem
);
1222 (void)color_dictionary
.lookup(nm
, new color(nm
));
1226 static unsigned int get_color_element(const char *scheme
, const char *col
)
1229 if (!get_number(&val
, 'f')) {
1230 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1235 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1238 if (val
> color::MAX_COLOR_VAL
+1) {
1239 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1240 // we change 0x10000 to 0xffff
1241 return color::MAX_COLOR_VAL
;
1243 return (unsigned int)val
;
1246 static color
*read_rgb(char end
= 0)
1248 symbol component
= do_get_long_name(0, end
);
1249 if (component
.is_null()) {
1250 warning(WARN_COLOR
, "missing rgb color values");
1253 const char *s
= component
.contents();
1254 color
*col
= new color
;
1256 if (!col
->read_rgb(s
)) {
1257 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1264 input_stack::push(make_temp_iterator(" "));
1265 input_stack::push(make_temp_iterator(s
));
1267 unsigned int r
= get_color_element("rgb color", "red component");
1268 unsigned int g
= get_color_element("rgb color", "green component");
1269 unsigned int b
= get_color_element("rgb color", "blue component");
1270 col
->set_rgb(r
, g
, b
);
1275 static color
*read_cmy(char end
= 0)
1277 symbol component
= do_get_long_name(0, end
);
1278 if (component
.is_null()) {
1279 warning(WARN_COLOR
, "missing cmy color values");
1282 const char *s
= component
.contents();
1283 color
*col
= new color
;
1285 if (!col
->read_cmy(s
)) {
1286 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1293 input_stack::push(make_temp_iterator(" "));
1294 input_stack::push(make_temp_iterator(s
));
1296 unsigned int c
= get_color_element("cmy color", "cyan component");
1297 unsigned int m
= get_color_element("cmy color", "magenta component");
1298 unsigned int y
= get_color_element("cmy color", "yellow component");
1299 col
->set_cmy(c
, m
, y
);
1304 static color
*read_cmyk(char end
= 0)
1306 symbol component
= do_get_long_name(0, end
);
1307 if (component
.is_null()) {
1308 warning(WARN_COLOR
, "missing cmyk color values");
1311 const char *s
= component
.contents();
1312 color
*col
= new color
;
1314 if (!col
->read_cmyk(s
)) {
1315 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1322 input_stack::push(make_temp_iterator(" "));
1323 input_stack::push(make_temp_iterator(s
));
1325 unsigned int c
= get_color_element("cmyk color", "cyan component");
1326 unsigned int m
= get_color_element("cmyk color", "magenta component");
1327 unsigned int y
= get_color_element("cmyk color", "yellow component");
1328 unsigned int k
= get_color_element("cmyk color", "black component");
1329 col
->set_cmyk(c
, m
, y
, k
);
1334 static color
*read_gray(char end
= 0)
1336 symbol component
= do_get_long_name(0, end
);
1337 if (component
.is_null()) {
1338 warning(WARN_COLOR
, "missing gray values");
1341 const char *s
= component
.contents();
1342 color
*col
= new color
;
1344 if (!col
->read_gray(s
)) {
1345 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1352 input_stack::push(make_temp_iterator("\n"));
1353 input_stack::push(make_temp_iterator(s
));
1355 unsigned int g
= get_color_element("gray", "gray value");
1361 static void activate_color()
1364 if (has_arg() && get_integer(&n
))
1365 color_flag
= n
!= 0;
1371 static void define_color()
1373 symbol color_name
= get_long_name(1);
1374 if (color_name
.is_null()) {
1378 if (color_name
== default_symbol
) {
1379 warning(WARN_COLOR
, "default color can't be redefined");
1383 symbol style
= get_long_name(1);
1384 if (style
.is_null()) {
1389 if (strcmp(style
.contents(), "rgb") == 0)
1391 else if (strcmp(style
.contents(), "cmyk") == 0)
1393 else if (strcmp(style
.contents(), "gray") == 0)
1395 else if (strcmp(style
.contents(), "grey") == 0)
1397 else if (strcmp(style
.contents(), "cmy") == 0)
1401 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1407 col
->nm
= color_name
;
1408 (void)color_dictionary
.lookup(color_name
, col
);
1413 static node
*do_overstrike()
1416 overstrike_node
*on
= new overstrike_node
;
1417 int start_level
= input_stack::get_level();
1421 if (tok
.newline() || tok
.eof()) {
1422 warning(WARN_DELIM
, "missing closing delimiter");
1423 input_stack::push(make_temp_iterator("\n"));
1427 && (compatible_flag
|| input_stack::get_level() == start_level
))
1429 charinfo
*ci
= tok
.get_char(1);
1431 node
*n
= curenv
->make_char_node(ci
);
1439 static node
*do_bracket()
1442 bracket_node
*bn
= new bracket_node
;
1444 int start_level
= input_stack::get_level();
1448 warning(WARN_DELIM
, "missing closing delimiter");
1451 if (tok
.newline()) {
1452 warning(WARN_DELIM
, "missing closing delimiter");
1453 input_stack::push(make_temp_iterator("\n"));
1457 && (compatible_flag
|| input_stack::get_level() == start_level
))
1459 charinfo
*ci
= tok
.get_char(1);
1461 node
*n
= curenv
->make_char_node(ci
);
1469 static int do_name_test()
1473 int start_level
= input_stack::get_level();
1478 if (tok
.newline() || tok
.eof()) {
1479 warning(WARN_DELIM
, "missing closing delimiter");
1480 input_stack::push(make_temp_iterator("\n"));
1484 && (compatible_flag
|| input_stack::get_level() == start_level
))
1490 return some_char
&& !bad_char
;
1493 static int do_expr_test()
1497 int start_level
= input_stack::get_level();
1498 if (!start
.delimiter(1))
1501 // disable all warning and error messages temporarily
1502 int saved_warning_mask
= warning_mask
;
1503 int saved_inhibit_errors
= inhibit_errors
;
1507 int result
= get_number_rigidly(&dummy
, 'u');
1508 warning_mask
= saved_warning_mask
;
1509 inhibit_errors
= saved_inhibit_errors
;
1510 if (tok
== start
&& input_stack::get_level() == start_level
)
1512 // ignore everything up to the delimiter in case we aren't right there
1515 if (tok
.newline() || tok
.eof()) {
1516 warning(WARN_DELIM
, "missing closing delimiter");
1517 input_stack::push(make_temp_iterator("\n"));
1520 if (tok
== start
&& input_stack::get_level() == start_level
)
1527 static node
*do_zero_width()
1531 int start_level
= input_stack::get_level();
1532 environment
env(curenv
);
1533 environment
*oldenv
= curenv
;
1537 if (tok
.newline() || tok
.eof()) {
1538 error("missing closing delimiter");
1542 && (compatible_flag
|| input_stack::get_level() == start_level
))
1547 node
*rev
= env
.extract_output_line();
1555 return new zero_width_node(n
);
1560 // It's undesirable for \Z to change environments, because then
1561 // \n(.w won't work as expected.
1563 static node
*do_zero_width()
1565 node
*rev
= new dummy_node
;
1568 int start_level
= input_stack::get_level();
1571 if (tok
.newline() || tok
.eof()) {
1572 warning(WARN_DELIM
, "missing closing delimiter");
1573 input_stack::push(make_temp_iterator("\n"));
1577 && (compatible_flag
|| input_stack::get_level() == start_level
))
1579 if (!tok
.add_to_node_list(&rev
))
1580 error("invalid token in argument to \\Z");
1589 return new zero_width_node(n
);
1594 token_node
*node::get_token_node()
1599 class token_node
: public node
{
1602 token_node(const token
&t
);
1604 token_node
*get_token_node();
1611 token_node::token_node(const token
&t
) : tk(t
)
1615 node
*token_node::copy()
1617 return new token_node(tk
);
1620 token_node
*token_node::get_token_node()
1625 int token_node::same(node
*nd
)
1627 return tk
== ((token_node
*)nd
)->tk
;
1630 const char *token_node::type()
1632 return "token_node";
1635 int token_node::force_tprint()
1640 int token_node::is_tag()
1645 token::token() : nd(0), type(TOKEN_EMPTY
)
1654 token::token(const token
&t
)
1655 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1657 // Use two statements to work around bug in SGI C++.
1659 nd
= tem
? tem
->copy() : 0;
1662 void token::operator=(const token
&t
)
1666 // Use two statements to work around bug in SGI C++.
1668 nd
= tem
? tem
->copy() : 0;
1685 return !tok
.newline();
1688 void token::make_space()
1693 void token::make_newline()
1695 type
= TOKEN_NEWLINE
;
1707 int cc
= input_stack::get(&n
);
1708 if (cc
!= escape_char
|| escape_char
== 0) {
1711 case PUSH_GROFF_MODE
:
1712 input_stack::save_compatible_flag(compatible_flag
);
1713 compatible_flag
= 0;
1715 case PUSH_COMP_MODE
:
1716 input_stack::save_compatible_flag(compatible_flag
);
1717 compatible_flag
= 1;
1719 case POP_GROFFCOMP_MODE
:
1720 compatible_flag
= input_stack::get_compatible_flag();
1723 input_stack::increase_level();
1726 input_stack::decrease_level();
1733 case TRANSPARENT_FILE_REQUEST
:
1735 case COPY_FILE_REQUEST
:
1737 case VJUSTIFY_REQUEST
:
1739 type
= TOKEN_REQUEST
;
1743 type
= TOKEN_BEGIN_TRAP
;
1746 type
= TOKEN_END_TRAP
;
1748 case LAST_PAGE_EJECTOR
:
1749 seen_last_page_ejector
= 1;
1752 type
= TOKEN_PAGE_EJECTOR
;
1754 case ESCAPE_PERCENT
:
1756 type
= TOKEN_HYPHEN_INDICATOR
;
1760 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1764 type
= TOKEN_STRETCHABLE_SPACE
;
1768 type
= TOKEN_ZERO_WIDTH_BREAK
;
1772 type
= TOKEN_ESCAPE
;
1775 goto handle_escape_char
;
1779 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1780 curenv
->get_fill_color());
1782 case ESCAPE_CIRCUMFLEX
:
1785 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1786 curenv
->get_fill_color());
1788 case ESCAPE_NEWLINE
:
1791 case ESCAPE_LEFT_BRACE
:
1793 type
= TOKEN_LEFT_BRACE
;
1795 case ESCAPE_RIGHT_BRACE
:
1797 type
= TOKEN_RIGHT_BRACE
;
1799 case ESCAPE_LEFT_QUOTE
:
1801 type
= TOKEN_SPECIAL
;
1804 case ESCAPE_RIGHT_QUOTE
:
1806 type
= TOKEN_SPECIAL
;
1811 type
= TOKEN_SPECIAL
;
1814 case ESCAPE_UNDERSCORE
:
1816 type
= TOKEN_SPECIAL
;
1821 type
= TOKEN_INTERRUPT
;
1825 type
= TOKEN_TRANSPARENT
;
1827 case ESCAPE_QUESTION
:
1829 nd
= do_non_interpreted();
1835 case ESCAPE_AMPERSAND
:
1839 case ESCAPE_RIGHT_PARENTHESIS
:
1840 ESCAPE_RIGHT_PARENTHESIS
:
1841 type
= TOKEN_TRANSPARENT_DUMMY
;
1844 type
= TOKEN_BACKSPACE
;
1853 type
= TOKEN_NEWLINE
;
1856 type
= TOKEN_LEADER
;
1861 token_node
*tn
= n
->get_token_node();
1880 cc
= input_stack::get(&n
);
1883 nm
= read_two_char_escape_name();
1884 type
= TOKEN_SPECIAL
;
1888 error("end of input after escape character");
1891 goto ESCAPE_LEFT_QUOTE
;
1893 goto ESCAPE_RIGHT_QUOTE
;
1897 goto ESCAPE_UNDERSCORE
;
1899 goto ESCAPE_PERCENT
;
1903 nd
= new hmotion_node(curenv
->get_digit_width(),
1904 curenv
->get_fill_color());
1910 goto ESCAPE_CIRCUMFLEX
;
1912 type
= TOKEN_ITALIC_CORRECTION
;
1916 nd
= new left_italic_corrected_node
;
1919 goto ESCAPE_AMPERSAND
;
1921 goto ESCAPE_RIGHT_PARENTHESIS
;
1925 goto ESCAPE_QUESTION
;
1931 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1934 type
= TOKEN_NEWLINE
;
1938 case '#': // Like \" but newline is ignored.
1939 while ((cc
= input_stack::get(0)) != '\n')
1947 symbol s
= read_escape_name();
1948 if (!(s
.is_null() || s
.is_empty()))
1954 symbol s
= read_escape_name(WITH_ARGS
);
1955 if (!(s
.is_null() || s
.is_empty())) {
1956 if (have_string_arg
) {
1957 have_string_arg
= 0;
1958 interpolate_string_with_args(s
);
1961 interpolate_string(s
);
1966 nd
= new non_interpreted_char_node('\001');
1970 c
= '0' + do_name_test();
1978 c
= '0' + do_expr_test();
1984 nm
= get_delim_name();
1987 type
= TOKEN_SPECIAL
;
1991 nd
= new vmotion_node(curenv
->get_size() / 2,
1992 curenv
->get_fill_color());
1995 nd
= read_draw_node();
2003 goto handle_escape_char
;
2006 symbol s
= read_escape_name(ALLOW_EMPTY
);
2010 for (p
= s
.contents(); *p
!= '\0'; p
++)
2013 if (*p
|| s
.is_empty())
2014 curenv
->set_font(s
);
2016 curenv
->set_font(atoi(s
.contents()));
2017 if (!compatible_flag
)
2023 symbol s
= read_escape_name(ALLOW_EMPTY
);
2026 curenv
->set_family(s
);
2032 symbol s
= read_escape_name();
2033 if (!(s
.is_null() || s
.is_empty()))
2034 interpolate_number_format(s
);
2038 if (!get_delim_number(&x
, 'm'))
2041 nd
= new hmotion_node(x
, curenv
->get_fill_color());
2044 // don't take height increments relative to previous height if
2045 // in compatibility mode
2046 if (!compatible_flag
&& curenv
->get_char_height()) {
2047 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
2048 curenv
->set_char_height(x
);
2051 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
2052 curenv
->set_char_height(x
);
2054 if (!compatible_flag
)
2058 nm
= read_escape_name();
2059 if (nm
.is_null() || nm
.is_empty())
2061 type
= TOKEN_MARK_INPUT
;
2067 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
2070 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
2072 node
*char_node
= curenv
->make_char_node(s
);
2074 nd
= new hline_node(x
, char_node
);
2076 nd
= new vline_node(x
, char_node
);
2080 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
2081 if (!compatible_flag
)
2085 do_fill_color(read_escape_name(ALLOW_EMPTY
));
2086 if (!compatible_flag
)
2092 symbol s
= read_increment_and_escape_name(&inc
);
2093 if (!(s
.is_null() || s
.is_empty()))
2094 interpolate_number_reg(s
, inc
);
2098 if (!get_delim_number(&val
, 0))
2101 warning(WARN_CHAR
, "invalid numbered character %1", val
);
2104 type
= TOKEN_NUMBERED_CHAR
;
2107 nd
= do_overstrike();
2111 nd
= do_suppress(read_escape_name());
2117 type
= TOKEN_SPREAD
;
2121 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
2125 if (!compatible_flag
)
2130 curenv
->set_size(x
);
2131 if (!compatible_flag
)
2135 if (get_delim_number(&x
, 0))
2136 curenv
->set_char_slant(x
);
2137 if (!compatible_flag
)
2142 nd
= new non_interpreted_char_node('\t');
2146 nd
= new vmotion_node(-curenv
->get_size() / 2,
2147 curenv
->get_fill_color());
2150 if (!get_delim_number(&x
, 'v'))
2153 nd
= new vmotion_node(x
, curenv
->get_fill_color());
2157 symbol s
= read_escape_name();
2158 if (!(s
.is_null() || s
.is_empty()))
2159 interpolate_environment_variable(s
);
2166 if (!get_delim_number(&x
, 'v'))
2169 nd
= new extra_size_node(x
);
2179 symbol s
= read_escape_name();
2180 if (s
.is_null() || s
.is_empty())
2182 request_or_macro
*p
= lookup_request(s
);
2183 macro
*m
= p
->to_macro();
2185 error("can't transparently throughput a request");
2188 nd
= new special_node(*m
);
2195 if (type
== TOKEN_NODE
)
2196 nd
= new zero_width_node(nd
);
2198 charinfo
*ci
= get_char(1);
2201 node
*gn
= curenv
->make_char_node(ci
);
2204 nd
= new zero_width_node(gn
);
2210 nd
= do_zero_width();
2216 goto ESCAPE_LEFT_BRACE
;
2218 goto ESCAPE_RIGHT_BRACE
;
2222 if (!compatible_flag
) {
2223 symbol s
= read_long_escape_name(WITH_ARGS
);
2224 if (s
.is_null() || s
.is_empty())
2226 if (have_string_arg
) {
2227 have_string_arg
= 0;
2228 nm
= composite_glyph_name(s
);
2231 const char *gn
= check_unicode_name(s
.contents());
2233 const char *gn_decomposed
= decompose_unicode(gn
);
2235 gn
= &gn_decomposed
[1];
2236 const char *groff_gn
= unicode_to_glyph_name(gn
);
2238 nm
= symbol(groff_gn
);
2240 char *buf
= new char[strlen(gn
) + 1 + 1];
2248 nm
= symbol(s
.contents());
2250 type
= TOKEN_SPECIAL
;
2253 goto handle_normal_char
;
2255 if (cc
!= escape_char
&& cc
!= '.')
2256 warning(WARN_ESCAPE
, "escape character ignored before %1",
2257 input_char_description(cc
));
2258 goto handle_normal_char
;
2264 int token::operator==(const token
&t
)
2273 case TOKEN_NUMBERED_CHAR
:
2274 return val
== t
.val
;
2280 int token::operator!=(const token
&t
)
2282 return !(*this == t
);
2285 // is token a suitable delimiter (like ')?
2287 int token::delimiter(int err
)
2316 error("cannot use character `%1' as a starting delimiter", char(c
));
2323 case TOKEN_STRETCHABLE_SPACE
:
2324 case TOKEN_UNSTRETCHABLE_SPACE
:
2328 error("cannot use %1 as a starting delimiter", description());
2335 const char *token::description()
2339 case TOKEN_BACKSPACE
:
2340 return "a backspace character";
2351 case TOKEN_HYPHEN_INDICATOR
:
2353 case TOKEN_INTERRUPT
:
2355 case TOKEN_ITALIC_CORRECTION
:
2358 return "a leader character";
2359 case TOKEN_LEFT_BRACE
:
2361 case TOKEN_MARK_INPUT
:
2367 case TOKEN_NUMBERED_CHAR
:
2369 case TOKEN_RIGHT_BRACE
:
2374 return "a special character";
2377 case TOKEN_STRETCHABLE_SPACE
:
2379 case TOKEN_UNSTRETCHABLE_SPACE
:
2382 return "a tab character";
2383 case TOKEN_TRANSPARENT
:
2385 case TOKEN_TRANSPARENT_DUMMY
:
2387 case TOKEN_ZERO_WIDTH_BREAK
:
2390 return "end of input";
2394 return "a magic token";
2399 while (!tok
.newline())
2410 if (has_arg() && get_integer(&n
))
2411 compatible_flag
= n
!= 0;
2413 compatible_flag
= 1;
2417 static void empty_name_warning(int required
)
2419 if (tok
.newline() || tok
.eof()) {
2421 warning(WARN_MISSING
, "missing name");
2423 else if (tok
.right_brace() || tok
.tab()) {
2424 const char *start
= tok
.description();
2427 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2428 if (!tok
.newline() && !tok
.eof())
2429 error("%1 is not allowed before an argument", start
);
2431 warning(WARN_MISSING
, "missing name");
2434 error("name expected (got %1)", tok
.description());
2436 error("name expected (got %1): treated as missing", tok
.description());
2439 static void non_empty_name_warning()
2441 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2442 && !tok
.right_brace()
2443 // We don't want to give a warning for .el\{
2444 && !tok
.left_brace())
2445 error("%1 is not allowed in a name", tok
.description());
2448 symbol
get_name(int required
)
2450 if (compatible_flag
) {
2453 if ((buf
[0] = tok
.ch()) != 0) {
2455 if ((buf
[1] = tok
.ch()) != 0) {
2460 non_empty_name_warning();
2464 empty_name_warning(required
);
2469 return get_long_name(required
);
2472 symbol
get_long_name(int required
)
2474 return do_get_long_name(required
, 0);
2477 static symbol
do_get_long_name(int required
, char end
)
2481 char abuf
[ABUF_SIZE
];
2483 int buf_size
= ABUF_SIZE
;
2486 // If end != 0 we normally have to append a null byte
2487 if (i
+ 2 > buf_size
) {
2489 buf
= new char[ABUF_SIZE
*2];
2490 memcpy(buf
, abuf
, buf_size
);
2491 buf_size
= ABUF_SIZE
*2;
2494 char *old_buf
= buf
;
2495 buf
= new char[buf_size
*2];
2496 memcpy(buf
, old_buf
, buf_size
);
2501 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2507 empty_name_warning(required
);
2510 if (end
&& buf
[i
] == end
)
2513 non_empty_name_warning();
2526 topdiv
->set_last_page();
2527 if (!end_macro_name
.is_null()) {
2528 spring_trap(end_macro_name
);
2530 process_input_stack();
2532 curenv
->final_break();
2534 process_input_stack();
2536 if (topdiv
->get_page_length() > 0) {
2538 topdiv
->set_ejecting();
2539 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2540 input_stack::push(make_temp_iterator((char *)buf
));
2541 topdiv
->space(topdiv
->get_page_length(), 1);
2543 process_input_stack();
2544 seen_last_page_ejector
= 1; // should be set already
2545 topdiv
->set_ejecting();
2546 push_page_ejector();
2547 topdiv
->space(topdiv
->get_page_length(), 1);
2549 process_input_stack();
2551 // This will only happen if a trap-invoked macro starts a diversion,
2552 // or if vertical position traps have been disabled.
2553 cleanup_and_exit(0);
2556 // This implements .ex. The input stack must be cleared before calling
2561 input_stack::clear();
2568 void return_macro_request()
2570 if (has_arg() && tok
.ch())
2571 input_stack::pop_macro();
2572 input_stack::pop_macro();
2578 end_macro_name
= get_name();
2582 void blank_line_macro()
2584 blank_line_macro_name
= get_name();
2588 static void trapping_blank_line()
2590 if (!blank_line_macro_name
.is_null())
2591 spring_trap(blank_line_macro_name
);
2598 int old_compatible_flag
= compatible_flag
;
2599 compatible_flag
= 0;
2600 symbol nm
= get_name();
2604 interpolate_macro(nm
, 1);
2605 compatible_flag
= old_compatible_flag
;
2606 request_or_macro
*p
= lookup_request(nm
);
2607 macro
*m
= p
->to_macro();
2612 inline int possibly_handle_first_page_transition()
2614 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2615 handle_first_page_transition();
2622 static int transparent_translate(int cc
)
2624 if (!invalid_input_char(cc
)) {
2625 charinfo
*ci
= charset_table
[cc
];
2626 switch (ci
->get_special_translation(1)) {
2627 case charinfo::TRANSLATE_SPACE
:
2629 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2630 return ESCAPE_TILDE
;
2631 case charinfo::TRANSLATE_DUMMY
:
2632 return ESCAPE_AMPERSAND
;
2633 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2634 return ESCAPE_PERCENT
;
2636 // This is really ugly.
2637 ci
= ci
->get_translation(1);
2639 int c
= ci
->get_ascii_code();
2642 error("can't translate %1 to special character `%2'"
2643 " in transparent throughput",
2644 input_char_description(cc
),
2652 struct int_stack_element
{
2654 int_stack_element
*next
;
2664 int_stack::int_stack()
2669 int_stack::~int_stack()
2672 int_stack_element
*temp
= top
;
2678 int int_stack::is_empty()
2683 void int_stack::push(int n
)
2685 int_stack_element
*p
= new int_stack_element
;
2691 int int_stack::pop()
2694 int_stack_element
*p
= top
;
2701 int node::reread(int *)
2706 int global_diverted_space
= 0;
2708 int diverted_space_node::reread(int *bolp
)
2710 global_diverted_space
= 1;
2711 if (curenv
->get_fill())
2712 trapping_blank_line();
2715 global_diverted_space
= 0;
2720 int diverted_copy_file_node::reread(int *bolp
)
2722 curdiv
->copy_file(filename
.contents());
2727 int word_space_node::reread(int *)
2730 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2731 curenv
->space(w
->width
, w
->sentence_width
);
2738 int unbreakable_space_node::reread(int *)
2743 int hmotion_node::reread(int *)
2745 if (unformat
&& was_tab
) {
2746 curenv
->handle_tab(0);
2753 void process_input_stack()
2755 int_stack trap_bol_stack
;
2758 int suppress_next
= 0;
2760 case token::TOKEN_CHAR
:
2762 unsigned char ch
= tok
.c
;
2763 if (bol
&& !have_input
2764 && (ch
== curenv
->control_char
2765 || ch
== curenv
->no_break_control_char
)) {
2766 break_flag
= ch
== curenv
->control_char
;
2767 // skip tabs as well as spaces here
2770 } while (tok
.white_space());
2771 symbol nm
= get_name();
2772 #if defined(DEBUGGING)
2774 if (! nm
.is_null()) {
2775 if (strcmp(nm
.contents(), "test") == 0) {
2776 fprintf(stderr
, "found it!\n");
2779 fprintf(stderr
, "interpreting [%s]", nm
.contents());
2780 if (strcmp(nm
.contents(), "di") == 0 && topdiv
!= curdiv
)
2781 fprintf(stderr
, " currently in diversion: %s",
2782 curdiv
->get_diversion_name());
2783 fprintf(stderr
, "\n");
2791 interpolate_macro(nm
);
2792 #if defined(DEBUGGING)
2794 fprintf(stderr
, "finished interpreting [%s] and environment state is\n", nm
.contents());
2795 curenv
->dump_troff_state();
2802 if (possibly_handle_first_page_transition())
2806 #if defined(DEBUGGING)
2808 fprintf(stderr
, "found [%c]\n", ch
); fflush(stderr
);
2811 curenv
->add_char(charset_table
[ch
]);
2813 if (tok
.type
!= token::TOKEN_CHAR
)
2823 case token::TOKEN_TRANSPARENT
:
2826 if (possibly_handle_first_page_transition())
2835 curdiv
->transparent_output(transparent_translate(cc
));
2837 curdiv
->transparent_output(n
);
2838 } while (cc
!= '\n' && cc
!= EOF
);
2840 curdiv
->transparent_output('\n');
2845 case token::TOKEN_NEWLINE
:
2847 if (bol
&& !old_have_input
2848 && !curenv
->get_prev_line_interrupted())
2849 trapping_blank_line();
2856 case token::TOKEN_REQUEST
:
2858 int request_code
= tok
.c
;
2860 switch (request_code
) {
2864 case COPY_FILE_REQUEST
:
2867 case TRANSPARENT_FILE_REQUEST
:
2871 case VJUSTIFY_REQUEST
:
2882 case token::TOKEN_SPACE
:
2884 if (possibly_handle_first_page_transition())
2886 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2888 // save space_width now so that it isn't changed by \f or \s
2889 // which we wouldn't notice here
2890 hunits space_width
= curenv
->get_space_width();
2892 nspaces
+= tok
.nspaces();
2894 } while (tok
.space());
2896 trapping_blank_line();
2900 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2901 curenv
->get_fill_color()));
2911 case token::TOKEN_EOF
:
2913 case token::TOKEN_NODE
:
2915 if (possibly_handle_first_page_transition())
2917 else if (tok
.nd
->reread(&bol
)) {
2922 curenv
->add_node(tok
.nd
);
2925 curenv
->possibly_break_line(1);
2929 case token::TOKEN_PAGE_EJECTOR
:
2931 continue_page_eject();
2932 // I think we just want to preserve bol.
2936 case token::TOKEN_BEGIN_TRAP
:
2938 trap_bol_stack
.push(bol
);
2943 case token::TOKEN_END_TRAP
:
2945 if (trap_bol_stack
.is_empty())
2946 error("spurious end trap token detected!");
2948 bol
= trap_bol_stack
.pop();
2951 /* I'm not totally happy about this. But I can't think of any other
2952 way to do it. Doing an output_pending_lines() whenever a
2953 TOKEN_END_TRAP is detected doesn't work: for example,
2966 a\%very\%very\%long\%word
2968 will print all but the first lines from the word immediately
2969 after the footer, rather than on the next page. */
2971 if (trap_bol_stack
.is_empty())
2972 curenv
->output_pending_lines();
2984 trap_sprung_flag
= 0;
2988 #ifdef WIDOW_CONTROL
2990 void flush_pending_lines()
2992 while (!tok
.newline() && !tok
.eof())
2994 curenv
->output_pending_lines();
2998 #endif /* WIDOW_CONTROL */
3000 request_or_macro::request_or_macro()
3004 macro
*request_or_macro::to_macro()
3009 request::request(REQUEST_FUNCP pp
) : p(pp
)
3013 void request::invoke(symbol
, int)
3019 enum { SIZE
= 128 };
3020 unsigned char s
[SIZE
];
3025 char_block::char_block()
3034 void append(unsigned char);
3035 void set(unsigned char, int);
3036 unsigned char get(int);
3043 friend class macro_header
;
3044 friend class string_iterator
;
3047 char_list::char_list()
3048 : ptr(0), len(0), head(0), tail(0)
3052 char_list::~char_list()
3055 char_block
*tem
= head
;
3061 int char_list::length()
3066 void char_list::append(unsigned char c
)
3069 head
= tail
= new char_block
;
3073 if (ptr
>= tail
->s
+ char_block::SIZE
) {
3074 tail
->next
= new char_block
;
3083 void char_list::set(unsigned char c
, int offset
)
3085 assert(len
> offset
);
3086 // optimization for access at the end
3087 int boundary
= len
- len
% char_block::SIZE
;
3088 if (offset
>= boundary
) {
3089 *(tail
->s
+ offset
- boundary
) = c
;
3092 char_block
*tem
= head
;
3095 l
+= char_block::SIZE
;
3097 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
3104 unsigned char char_list::get(int offset
)
3106 assert(len
> offset
);
3107 // optimization for access at the end
3108 int boundary
= len
- len
% char_block::SIZE
;
3109 if (offset
>= boundary
)
3110 return *(tail
->s
+ offset
- boundary
);
3111 char_block
*tem
= head
;
3114 l
+= char_block::SIZE
;
3116 return *(tem
->s
+ offset
% char_block::SIZE
);
3127 void append(node
*);
3131 friend class macro_header
;
3132 friend class string_iterator
;
3135 void node_list::append(node
*n
)
3143 tail
= tail
->next
= n
;
3147 int node_list::length()
3150 for (node
*n
= head
; n
!= 0; n
= n
->next
)
3155 node_list::node_list()
3160 node
*node_list::extract()
3167 node_list::~node_list()
3169 delete_node_list(head
);
3172 class macro_header
{
3177 macro_header() { count
= 1; }
3178 macro_header
*copy(int);
3183 if (p
!= 0 && --(p
->count
) <= 0)
3190 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3199 macro::macro(const macro
&m
)
3200 : filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
3201 empty_macro(m
.empty_macro
), is_a_diversion(m
.is_a_diversion
), p(m
.p
)
3207 macro::macro(int is_div
)
3208 : is_a_diversion(is_div
)
3210 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3219 int macro::is_diversion()
3221 return is_a_diversion
;
3224 macro
¯o::operator=(const macro
&m
)
3226 // don't assign object
3229 if (p
!= 0 && --(p
->count
) <= 0)
3232 filename
= m
.filename
;
3235 empty_macro
= m
.empty_macro
;
3236 is_a_diversion
= m
.is_a_diversion
;
3240 void macro::append(unsigned char c
)
3244 p
= new macro_header
;
3245 if (p
->cl
.length() != len
) {
3246 macro_header
*tem
= p
->copy(len
);
3247 if (--(p
->count
) <= 0)
3253 if (c
!= PUSH_GROFF_MODE
&& c
!= PUSH_COMP_MODE
&& c
!= POP_GROFFCOMP_MODE
)
3257 void macro::set(unsigned char c
, int offset
)
3261 p
->cl
.set(c
, offset
);
3264 unsigned char macro::get(int offset
)
3267 return p
->cl
.get(offset
);
3275 void macro::append_str(const char *s
)
3280 while (s
[i
] != (char)0) {
3287 void macro::append(node
*n
)
3291 p
= new macro_header
;
3292 if (p
->cl
.length() != len
) {
3293 macro_header
*tem
= p
->copy(len
);
3294 if (--(p
->count
) <= 0)
3304 void macro::append_unsigned(unsigned int i
)
3306 unsigned int j
= i
/ 10;
3309 append(((unsigned char)(((int)'0') + i
% 10)));
3312 void macro::append_int(int i
)
3318 append_unsigned((unsigned int)i
);
3321 void macro::print_size()
3323 errprint("%1", len
);
3326 // make a copy of the first n bytes
3328 macro_header
*macro_header::copy(int n
)
3330 macro_header
*p
= new macro_header
;
3331 char_block
*bp
= cl
.head
;
3332 unsigned char *ptr
= bp
->s
;
3335 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3339 unsigned char c
= *ptr
++;
3342 p
->nl
.append(nd
->copy());
3351 object_dictionary_iterator
iter(request_dictionary
);
3352 request_or_macro
*rm
;
3354 while (iter
.get(&s
, (object
**)&rm
)) {
3355 assert(!s
.is_null());
3356 macro
*m
= rm
->to_macro();
3358 errprint("%1\t", s
.contents());
3367 class string_iterator
: public input_iterator
{
3369 const char *how_invoked
;
3373 int count
; // of characters remaining
3375 int saved_compatible_flag
;
3376 int with_break
; // inherited from the caller
3381 string_iterator(const macro
&, const char * = 0, symbol
= NULL_SYMBOL
);
3384 int get_location(int, const char **, int *);
3386 int get_break_flag() { return with_break
; }
3387 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3388 int get_compatible_flag() { return saved_compatible_flag
; }
3392 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3393 : input_iterator(m
.is_a_diversion
), mac(m
), how_invoked(p
), newline_flag(0),
3398 bp
= mac
.p
->cl
.head
;
3399 nd
= mac
.p
->nl
.head
;
3407 with_break
= input_stack::get_break_flag();
3410 string_iterator::string_iterator()
3419 with_break
= input_stack::get_break_flag();
3422 int string_iterator::is_diversion()
3424 return mac
.is_diversion();
3427 int string_iterator::fill(node
**np
)
3434 const unsigned char *p
= eptr
;
3435 if (p
>= bp
->s
+ char_block::SIZE
) {
3443 (*np
)->div_nest_level
= input_stack::get_div_level();
3445 (*np
)->div_nest_level
= 0;
3452 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3457 unsigned char c
= *p
;
3458 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3472 int string_iterator::peek()
3476 const unsigned char *p
= eptr
;
3477 if (p
>= bp
->s
+ char_block::SIZE
) {
3483 int string_iterator::get_location(int allow_macro
,
3484 const char **filep
, int *linep
)
3488 if (mac
.filename
== 0)
3490 *filep
= mac
.filename
;
3491 *linep
= mac
.lineno
+ lineno
- 1;
3495 void string_iterator::backtrace()
3498 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3501 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3503 errprint(": %1\n", how_invoked
);
3510 class temp_iterator
: public input_iterator
{
3511 unsigned char *base
;
3512 temp_iterator(const char *, int len
);
3515 friend input_iterator
*make_temp_iterator(const char *);
3521 temp_iterator::temp_iterator(const char *s
, int len
)
3523 base
= new unsigned char[len
];
3524 memcpy(base
, s
, len
);
3529 temp_iterator::~temp_iterator()
3534 class small_temp_iterator
: public input_iterator
{
3536 small_temp_iterator(const char *, int);
3537 ~small_temp_iterator();
3538 enum { BLOCK
= 16 };
3539 static small_temp_iterator
*free_list
;
3540 void *operator new(size_t);
3541 void operator delete(void *);
3543 unsigned char buf
[SIZE
];
3544 friend input_iterator
*make_temp_iterator(const char *);
3547 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3549 void *small_temp_iterator::operator new(size_t n
)
3551 assert(n
== sizeof(small_temp_iterator
));
3554 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3555 for (int i
= 0; i
< BLOCK
- 1; i
++)
3556 free_list
[i
].next
= free_list
+ i
+ 1;
3557 free_list
[BLOCK
-1].next
= 0;
3559 small_temp_iterator
*p
= free_list
;
3560 free_list
= (small_temp_iterator
*)(free_list
->next
);
3568 void small_temp_iterator::operator delete(void *p
)
3571 ((small_temp_iterator
*)p
)->next
= free_list
;
3572 free_list
= (small_temp_iterator
*)p
;
3576 small_temp_iterator::~small_temp_iterator()
3583 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3585 for (int i
= 0; i
< len
; i
++)
3591 input_iterator
*make_temp_iterator(const char *s
)
3594 return new small_temp_iterator(s
, 0);
3597 if (n
<= small_temp_iterator::SIZE
)
3598 return new small_temp_iterator(s
, n
);
3600 return new temp_iterator(s
, n
);
3604 // this is used when macros with arguments are interpolated
3610 arg_list(const macro
&, int);
3614 arg_list::arg_list(const macro
&m
, int s
) : mac(m
), space_follows(s
), next(0)
3618 arg_list::~arg_list()
3622 class macro_iterator
: public string_iterator
{
3625 int with_break
; // whether called as .foo or 'foo
3627 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3630 int has_args() { return 1; }
3631 input_iterator
*get_arg(int);
3632 int space_follows_arg(int);
3633 int get_break_flag() { return with_break
; }
3634 int nargs() { return argc
; }
3635 void add_arg(const macro
&, int);
3637 int is_macro() { return 1; }
3641 input_iterator
*macro_iterator::get_arg(int i
)
3644 return make_temp_iterator(nm
.contents());
3645 if (i
> 0 && i
<= argc
) {
3647 for (int j
= 1; j
< i
; j
++) {
3651 return new string_iterator(p
->mac
);
3657 int macro_iterator::space_follows_arg(int i
)
3659 if (i
> 0 && i
<= argc
) {
3661 for (int j
= 1; j
< i
; j
++) {
3665 return p
->space_follows
;
3671 void macro_iterator::add_arg(const macro
&m
, int s
)
3674 for (p
= &args
; *p
; p
= &((*p
)->next
))
3676 *p
= new arg_list(m
, s
);
3680 void macro_iterator::shift(int n
)
3682 while (n
> 0 && argc
> 0) {
3683 arg_list
*tem
= args
;
3691 // This gets used by eg .if '\?xxx\?''.
3693 int operator==(const macro
&m1
, const macro
&m2
)
3695 if (m1
.len
!= m2
.len
)
3697 string_iterator
iter1(m1
);
3698 string_iterator
iter2(m2
);
3702 int c1
= iter1
.get(&nd1
);
3705 int c2
= iter2
.get(&nd2
);
3717 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3727 static void interpolate_macro(symbol nm
, int no_next
)
3729 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3732 const char *s
= nm
.contents();
3733 if (strlen(s
) > 2) {
3734 request_or_macro
*r
;
3739 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3741 macro
*m
= r
->to_macro();
3742 if (!m
|| !m
->empty())
3743 warned
= warning(WARN_SPACE
,
3744 "macro `%1' not defined "
3745 "(possibly missing space after `%2')",
3746 nm
.contents(), buf
);
3750 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3752 request_dictionary
.define(nm
, p
);
3756 p
->invoke(nm
, no_next
);
3763 static void decode_args(macro_iterator
*mi
)
3765 if (!tok
.newline() && !tok
.eof()) {
3767 int c
= get_copy(&n
);
3771 if (c
== '\n' || c
== EOF
)
3774 int quote_input_level
= 0;
3775 int done_tab_warning
= 0;
3776 arg
.append(compatible_flag
? PUSH_COMP_MODE
: PUSH_GROFF_MODE
);
3777 // we store discarded double quotes for \$^
3779 arg
.append(DOUBLE_QUOTE
);
3780 quote_input_level
= input_stack::get_level();
3783 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3784 if (quote_input_level
> 0 && c
== '"'
3786 || input_stack::get_level() == quote_input_level
)) {
3787 arg
.append(DOUBLE_QUOTE
);
3800 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3801 warning(WARN_TAB
, "tab character in unquoted macro argument");
3802 done_tab_warning
= 1;
3809 arg
.append(POP_GROFFCOMP_MODE
);
3810 mi
->add_arg(arg
, (c
== ' '));
3815 static void decode_string_args(macro_iterator
*mi
)
3818 int c
= get_copy(&n
);
3822 if (c
== '\n' || c
== EOF
) {
3823 error("missing `]'");
3829 int quote_input_level
= 0;
3830 int done_tab_warning
= 0;
3832 quote_input_level
= input_stack::get_level();
3835 while (c
!= EOF
&& c
!= '\n'
3836 && !(c
== ']' && quote_input_level
== 0)
3837 && !(c
== ' ' && quote_input_level
== 0)) {
3838 if (quote_input_level
> 0 && c
== '"'
3839 && input_stack::get_level() == quote_input_level
) {
3852 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3853 warning(WARN_TAB
, "tab character in unquoted string argument");
3854 done_tab_warning
= 1;
3861 mi
->add_arg(arg
, (c
== ' '));
3865 void macro::invoke(symbol nm
, int no_next
)
3867 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3869 input_stack::push(mi
);
3870 // we must delay tok.next() in case the function has been called by
3871 // do_request to assure proper handling of compatible_flag
3876 macro
*macro::to_macro()
3883 return empty_macro
== 1;
3886 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_called
)
3887 : string_iterator(m
, how_called
, s
), args(0), argc(0), with_break(break_flag
)
3891 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag
)
3895 macro_iterator::~macro_iterator()
3898 arg_list
*tem
= args
;
3904 dictionary
composite_dictionary(17);
3906 void composite_request()
3908 symbol from
= get_name(1);
3909 if (!from
.is_null()) {
3910 const char *from_gn
= glyph_name_to_unicode(from
.contents());
3912 from_gn
= check_unicode_name(from
.contents());
3914 error("invalid composite glyph name `%1'", from
.contents());
3919 const char *from_decomposed
= decompose_unicode(from_gn
);
3920 if (from_decomposed
)
3921 from_gn
= &from_decomposed
[1];
3922 symbol to
= get_name(1);
3924 composite_dictionary
.remove(symbol(from_gn
));
3926 const char *to_gn
= glyph_name_to_unicode(to
.contents());
3928 to_gn
= check_unicode_name(to
.contents());
3930 error("invalid composite glyph name `%1'", to
.contents());
3935 const char *to_decomposed
= decompose_unicode(to_gn
);
3937 to_gn
= &to_decomposed
[1];
3938 if (strcmp(from_gn
, to_gn
) == 0)
3939 composite_dictionary
.remove(symbol(from_gn
));
3941 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
3947 static symbol
composite_glyph_name(symbol nm
)
3949 macro_iterator
*mi
= new macro_iterator();
3950 decode_string_args(mi
);
3951 input_stack::push(mi
);
3952 const char *gn
= glyph_name_to_unicode(nm
.contents());
3954 gn
= check_unicode_name(nm
.contents());
3956 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
3957 return EMPTY_SYMBOL
;
3960 const char *gn_decomposed
= decompose_unicode(gn
);
3961 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
3963 int n
= input_stack::nargs();
3964 for (int i
= 1; i
<= n
; i
++) {
3966 input_iterator
*p
= input_stack::get_arg(i
);
3969 while ((c
= p
->get(0)) != EOF
)
3970 if (c
!= DOUBLE_QUOTE
)
3973 const char *u
= glyph_name_to_unicode(gl
.contents());
3975 u
= check_unicode_name(gl
.contents());
3977 error("invalid component `%1' in composite glyph name",
3979 return EMPTY_SYMBOL
;
3982 const char *decomposed
= decompose_unicode(u
);
3985 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
3986 if (mapped_composite
)
3987 u
= (const char *)mapped_composite
;
3991 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
3993 return symbol(groff_gn
);
3997 return symbol(gl
.contents());
4000 int trap_sprung_flag
= 0;
4001 int postpone_traps_flag
= 0;
4002 symbol postponed_trap
;
4004 void spring_trap(symbol nm
)
4006 assert(!nm
.is_null());
4007 trap_sprung_flag
= 1;
4008 if (postpone_traps_flag
) {
4009 postponed_trap
= nm
;
4012 static char buf
[2] = { BEGIN_TRAP
, 0 };
4013 static char buf2
[2] = { END_TRAP
, '\0' };
4014 input_stack::push(make_temp_iterator(buf2
));
4015 request_or_macro
*p
= lookup_request(nm
);
4016 macro
*m
= p
->to_macro();
4018 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
4020 error("you can't invoke a request with a trap");
4021 input_stack::push(make_temp_iterator(buf
));
4024 void postpone_traps()
4026 postpone_traps_flag
= 1;
4029 int unpostpone_traps()
4031 postpone_traps_flag
= 0;
4032 if (!postponed_trap
.is_null()) {
4033 spring_trap(postponed_trap
);
4034 postponed_trap
= NULL_SYMBOL
;
4043 macro_iterator
*mi
= new macro_iterator
;
4044 int reading_from_terminal
= isatty(fileno(stdin
));
4046 if (!tok
.newline() && !tok
.eof()) {
4047 int c
= get_copy(0);
4050 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
4051 if (!invalid_input_char(c
)) {
4052 if (reading_from_terminal
)
4063 if (reading_from_terminal
) {
4064 fputc(had_prompt
? ':' : '\a', stderr
);
4067 input_stack::push(mi
);
4071 while ((c
= getchar()) != EOF
) {
4072 if (invalid_input_char(c
))
4073 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
4086 if (reading_from_terminal
)
4088 input_stack::push(new string_iterator(mac
));
4092 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
4093 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
};
4094 enum comp_mode
{ COMP_IGNORE
, COMP_DISABLE
, COMP_ENABLE
};
4096 void do_define_string(define_mode mode
, comp_mode comp
)
4099 node
*n
= 0; // pacify compiler
4110 else if (!tok
.space()) {
4111 error("bad string definition");
4122 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
4123 macro
*mm
= rm
? rm
->to_macro() : 0;
4124 if (mode
== DEFINE_APPEND
&& mm
)
4126 if (comp
== COMP_DISABLE
)
4127 mac
.append(PUSH_GROFF_MODE
);
4128 else if (comp
== COMP_ENABLE
)
4129 mac
.append(PUSH_COMP_MODE
);
4130 while (c
!= '\n' && c
!= EOF
) {
4134 mac
.append((unsigned char)c
);
4139 request_dictionary
.define(nm
, mm
);
4141 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4142 mac
.append(POP_GROFFCOMP_MODE
);
4147 void define_string()
4149 do_define_string(DEFINE_NORMAL
,
4150 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4153 void define_nocomp_string()
4155 do_define_string(DEFINE_NORMAL
, COMP_DISABLE
);
4158 void append_string()
4160 do_define_string(DEFINE_APPEND
,
4161 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4164 void append_nocomp_string()
4166 do_define_string(DEFINE_APPEND
, COMP_DISABLE
);
4169 void do_define_character(char_mode mode
, const char *font_name
)
4171 node
*n
= 0; // pacify compiler
4174 charinfo
*ci
= tok
.get_char(1);
4180 string
s(font_name
);
4182 s
+= ci
->nm
.contents();
4184 ci
= get_charinfo(symbol(s
.contents()));
4191 else if (!tok
.space()) {
4192 error("bad character definition");
4198 while (c
== ' ' || c
== '\t')
4202 macro
*m
= new macro
;
4203 while (c
!= '\n' && c
!= EOF
) {
4207 m
->append((unsigned char)c
);
4210 m
= ci
->setx_macro(m
, mode
);
4216 void define_character()
4218 do_define_character(CHAR_NORMAL
);
4221 void define_fallback_character()
4223 do_define_character(CHAR_FALLBACK
);
4226 void define_special_character()
4228 do_define_character(CHAR_SPECIAL
);
4231 static void remove_character()
4234 while (!tok
.newline() && !tok
.eof()) {
4235 if (!tok
.space() && !tok
.tab()) {
4236 charinfo
*ci
= tok
.get_char(1);
4239 macro
*m
= ci
->set_macro(0);
4248 static void interpolate_string(symbol nm
)
4250 request_or_macro
*p
= lookup_request(nm
);
4251 macro
*m
= p
->to_macro();
4253 error("you can only invoke a string or macro using \\*");
4255 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
4256 input_stack::push(si
);
4260 static void interpolate_string_with_args(symbol s
)
4262 request_or_macro
*p
= lookup_request(s
);
4263 macro
*m
= p
->to_macro();
4265 error("you can only invoke a string or macro using \\*");
4267 macro_iterator
*mi
= new macro_iterator(s
, *m
);
4268 decode_string_args(mi
);
4269 input_stack::push(mi
);
4273 static void interpolate_arg(symbol nm
)
4275 const char *s
= nm
.contents();
4276 if (!s
|| *s
== '\0')
4277 copy_mode_error("missing argument name");
4278 else if (s
[1] == 0 && csdigit(s
[0]))
4279 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4280 else if (s
[0] == '*' && s
[1] == '\0') {
4281 int limit
= input_stack::nargs();
4283 for (int i
= 1; i
<= limit
; i
++) {
4284 input_iterator
*p
= input_stack::get_arg(i
);
4286 while ((c
= p
->get(0)) != EOF
)
4287 if (c
!= DOUBLE_QUOTE
)
4294 input_stack::push(make_temp_iterator(args
.contents()));
4297 else if (s
[0] == '@' && s
[1] == '\0') {
4298 int limit
= input_stack::nargs();
4300 for (int i
= 1; i
<= limit
; i
++) {
4302 args
+= char(BEGIN_QUOTE
);
4303 input_iterator
*p
= input_stack::get_arg(i
);
4305 while ((c
= p
->get(0)) != EOF
)
4306 if (c
!= DOUBLE_QUOTE
)
4308 args
+= char(END_QUOTE
);
4315 input_stack::push(make_temp_iterator(args
.contents()));
4318 else if (s
[0] == '^' && s
[1] == '\0') {
4319 int limit
= input_stack::nargs();
4321 int c
= input_stack::peek();
4322 for (int i
= 1; i
<= limit
; i
++) {
4323 input_iterator
*p
= input_stack::get_arg(i
);
4324 while ((c
= p
->get(0)) != EOF
) {
4325 if (c
== DOUBLE_QUOTE
)
4329 if (input_stack::space_follows_arg(i
))
4334 input_stack::push(make_temp_iterator(args
.contents()));
4339 for (p
= s
; *p
&& csdigit(*p
); p
++)
4342 copy_mode_error("bad argument name `%1'", s
);
4344 input_stack::push(input_stack::get_arg(atoi(s
)));
4348 void handle_first_page_transition()
4351 topdiv
->begin_page();
4354 // We push back a token by wrapping it up in a token_node, and
4355 // wrapping that up in a string_iterator.
4357 static void push_token(const token
&t
)
4360 m
.append(new token_node(t
));
4361 input_stack::push(new string_iterator(m
));
4364 void push_page_ejector()
4366 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4367 input_stack::push(make_temp_iterator(buf
));
4370 void handle_initial_request(unsigned char code
)
4376 mac
.append(new token_node(tok
));
4377 input_stack::push(new string_iterator(mac
));
4378 input_stack::push(make_temp_iterator(buf
));
4379 topdiv
->begin_page();
4383 void handle_initial_title()
4385 handle_initial_request(TITLE_REQUEST
);
4388 // this should be local to define_macro, but cfront 1.2 doesn't support that
4389 static symbol
dot_symbol(".");
4391 void do_define_macro(define_mode mode
, calling_mode calling
, comp_mode comp
)
4394 if (calling
== CALLING_INDIRECT
) {
4395 symbol temp1
= get_name(1);
4396 if (temp1
.is_null()) {
4400 symbol temp2
= get_name();
4401 input_stack::push(make_temp_iterator("\n"));
4402 if (!temp2
.is_null()) {
4403 interpolate_string(temp2
);
4404 input_stack::push(make_temp_iterator(" "));
4406 interpolate_string(temp1
);
4407 input_stack::push(make_temp_iterator(" "));
4410 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4417 term
= get_name(); // the request that terminates the definition
4420 while (!tok
.newline() && !tok
.eof())
4422 const char *start_filename
;
4424 int have_start_location
= input_stack::get_location(0, &start_filename
,
4427 // doing this here makes the line numbers come out right
4428 int c
= get_copy(&n
, 1);
4431 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4432 request_or_macro
*rm
=
4433 (request_or_macro
*)request_dictionary
.lookup(nm
);
4435 mm
= rm
->to_macro();
4436 if (mm
&& mode
== DEFINE_APPEND
)
4440 if (comp
== COMP_DISABLE
)
4441 mac
.append(PUSH_GROFF_MODE
);
4442 else if (comp
== COMP_ENABLE
)
4443 mac
.append(PUSH_COMP_MODE
);
4445 while (c
== ESCAPE_NEWLINE
) {
4446 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4448 c
= get_copy(&n
, 1);
4450 if (bol
&& c
== '.') {
4451 const char *s
= term
.contents();
4453 // see if it matches term
4456 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4458 if ((unsigned char)s
[0] == d
) {
4459 for (i
= 1; s
[i
] != 0; i
++) {
4461 if ((unsigned char)s
[i
] != d
)
4467 && ((i
== 2 && compatible_flag
)
4468 || (d
= get_copy(&n
)) == ' '
4469 || d
== '\n')) { // we found it
4474 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4477 request_dictionary
.define(nm
, mm
);
4479 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4480 mac
.append(POP_GROFFCOMP_MODE
);
4483 if (term
!= dot_symbol
) {
4485 interpolate_macro(term
);
4491 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4493 for (int j
= 0; j
< i
; j
++)
4499 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4500 if (have_start_location
)
4501 error_with_file_and_line(start_filename
, start_lineno
,
4502 "end of file while defining macro `%1'",
4505 error("end of file while defining macro `%1'", nm
.contents());
4508 if (have_start_location
)
4509 error_with_file_and_line(start_filename
, start_lineno
,
4510 "end of file while ignoring input lines");
4512 error("end of file while ignoring input lines");
4517 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4524 c
= get_copy(&n
, 1);
4530 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
,
4531 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4534 void define_nocomp_macro()
4536 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_DISABLE
);
4539 void define_indirect_macro()
4541 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
,
4542 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4545 void define_indirect_nocomp_macro()
4547 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_DISABLE
);
4552 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
,
4553 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4556 void append_nocomp_macro()
4558 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_DISABLE
);
4561 void append_indirect_macro()
4563 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
,
4564 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4567 void append_indirect_nocomp_macro()
4569 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_DISABLE
);
4575 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
, COMP_IGNORE
);
4582 symbol s
= get_name();
4585 request_dictionary
.remove(s
);
4592 symbol s1
= get_name(1);
4593 if (!s1
.is_null()) {
4594 symbol s2
= get_name(1);
4596 request_dictionary
.rename(s1
, s2
);
4603 symbol s1
= get_name(1);
4604 if (!s1
.is_null()) {
4605 symbol s2
= get_name(1);
4606 if (!s2
.is_null()) {
4607 if (!request_dictionary
.alias(s1
, s2
))
4608 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4616 symbol s
= get_name(1);
4618 request_or_macro
*p
= lookup_request(s
);
4619 macro
*m
= p
->to_macro();
4621 error("cannot chop request");
4622 else if (m
->empty())
4623 error("cannot chop empty macro");
4625 int have_restore
= 0;
4626 // we have to check for additional save/restore pairs which could be
4627 // there due to empty am1 requests.
4629 if (m
->get(m
->len
- 1) != POP_GROFFCOMP_MODE
)
4633 if (m
->get(m
->len
- 1) != PUSH_GROFF_MODE
4634 && m
->get(m
->len
- 1) != PUSH_COMP_MODE
)
4642 error("cannot chop empty macro");
4645 m
->set(POP_GROFFCOMP_MODE
, m
->len
- 1);
4654 void substring_request()
4656 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4657 symbol s
= get_name(1);
4658 if (!s
.is_null() && get_integer(&start
)) {
4659 request_or_macro
*p
= lookup_request(s
);
4660 macro
*m
= p
->to_macro();
4662 error("cannot apply `substring' on a request");
4665 if (!has_arg() || get_integer(&end
)) {
4666 int real_length
= 0; // 1, 2, ..., n
4667 string_iterator
iter1(*m
);
4668 for (int l
= 0; l
< m
->len
; l
++) {
4669 int c
= iter1
.get(0);
4670 if (c
== PUSH_GROFF_MODE
4671 || c
== PUSH_COMP_MODE
4672 || c
== POP_GROFFCOMP_MODE
)
4679 start
+= real_length
;
4687 if (start
>= real_length
|| end
< 0) {
4689 "start and end index of substring out of range");
4692 if (--(m
->p
->count
) <= 0)
4701 "start index of substring out of range, set to 0");
4704 if (end
>= real_length
) {
4706 "end index of substring out of range, set to string length");
4707 end
= real_length
- 1;
4709 // now extract the substring
4710 string_iterator
iter(*m
);
4712 for (i
= 0; i
< start
; i
++) {
4713 int c
= iter
.get(0);
4714 while (c
== PUSH_GROFF_MODE
4715 || c
== PUSH_COMP_MODE
4716 || c
== POP_GROFFCOMP_MODE
)
4722 for (; i
<= end
; i
++) {
4723 node
*nd
= 0; // pacify compiler
4724 int c
= iter
.get(&nd
);
4725 while (c
== PUSH_GROFF_MODE
4726 || c
== PUSH_COMP_MODE
4727 || c
== POP_GROFFCOMP_MODE
)
4734 mac
.append((unsigned char)c
);
4743 void length_request()
4747 if (ret
.is_null()) {
4757 else if (!tok
.space()) {
4758 error("bad string definition");
4769 while (c
!= '\n' && c
!= EOF
) {
4773 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4777 set_number_reg(ret
, len
);
4781 void asciify_macro()
4783 symbol s
= get_name(1);
4785 request_or_macro
*p
= lookup_request(s
);
4786 macro
*m
= p
->to_macro();
4788 error("cannot asciify request");
4791 string_iterator
iter(*m
);
4793 node
*nd
= 0; // pacify compiler
4794 int c
= iter
.get(&nd
);
4808 void unformat_macro()
4810 symbol s
= get_name(1);
4812 request_or_macro
*p
= lookup_request(s
);
4813 macro
*m
= p
->to_macro();
4815 error("cannot unformat request");
4818 string_iterator
iter(*m
);
4820 node
*nd
= 0; // pacify compiler
4821 int c
= iter
.get(&nd
);
4827 if (nd
->set_unformat_flag())
4837 static void interpolate_environment_variable(symbol nm
)
4839 const char *s
= getenv(nm
.contents());
4841 input_stack::push(make_temp_iterator(s
));
4844 void interpolate_number_reg(symbol nm
, int inc
)
4846 reg
*r
= lookup_number_reg(nm
);
4851 input_stack::push(make_temp_iterator(r
->get_string()));
4854 static void interpolate_number_format(symbol nm
)
4856 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4858 input_stack::push(make_temp_iterator(r
->get_format()));
4861 static int get_delim_number(units
*n
, unsigned char si
, int prev_value
)
4865 if (start
.delimiter(1)) {
4867 if (get_number(n
, si
, prev_value
)) {
4869 warning(WARN_DELIM
, "closing delimiter does not match");
4876 static int get_delim_number(units
*n
, unsigned char si
)
4880 if (start
.delimiter(1)) {
4882 if (get_number(n
, si
)) {
4884 warning(WARN_DELIM
, "closing delimiter does not match");
4891 static int get_line_arg(units
*n
, unsigned char si
, charinfo
**cp
)
4895 int start_level
= input_stack::get_level();
4896 if (!start
.delimiter(1))
4899 if (get_number(n
, si
)) {
4900 if (tok
.dummy() || tok
.transparent_dummy())
4902 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4903 *cp
= tok
.get_char(1);
4906 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4907 warning(WARN_DELIM
, "closing delimiter does not match");
4913 static int read_size(int *x
)
4923 else if (c
== '+') {
4928 int val
= 0; // pacify compiler
4934 // allow an increment either before or after the left parenthesis
4940 else if (c
== '+') {
4955 val
= val
*10 + (c
- '0');
4960 else if (csdigit(c
)) {
4962 if (!inc
&& c
!= '0' && c
< '4') {
4968 val
= val
*10 + (c
- '0');
4972 else if (!tok
.delimiter(1))
4978 if (!inc
&& (c
== '-' || c
== '+')) {
4979 inc
= c
== '+' ? 1 : -1;
4982 if (!get_number(&val
, 'z'))
4984 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4985 if (start
.ch() == '[')
4986 error("missing `]'");
4988 error("missing closing delimiter");
4996 // special case -- \s[0] and \s0 means to revert to previous size
5003 *x
= curenv
->get_requested_point_size() + val
;
5006 *x
= curenv
->get_requested_point_size() - val
;
5013 "\\s escape results in non-positive point size; set to 1");
5019 error("bad digit in point size");
5024 static symbol
get_delim_name()
5029 error("end of input at start of delimited name");
5032 if (start
.newline()) {
5033 error("can't delimit name with a newline");
5036 int start_level
= input_stack::get_level();
5037 char abuf
[ABUF_SIZE
];
5039 int buf_size
= ABUF_SIZE
;
5042 if (i
+ 1 > buf_size
) {
5044 buf
= new char[ABUF_SIZE
*2];
5045 memcpy(buf
, abuf
, buf_size
);
5046 buf_size
= ABUF_SIZE
*2;
5049 char *old_buf
= buf
;
5050 buf
= new char[buf_size
*2];
5051 memcpy(buf
, old_buf
, buf_size
);
5058 && (compatible_flag
|| input_stack::get_level() == start_level
))
5060 if ((buf
[i
] = tok
.ch()) == 0) {
5061 error("missing delimiter (got %1)", tok
.description());
5071 error("empty delimited name");
5086 static void do_register()
5090 if (!start
.delimiter(1))
5093 symbol nm
= get_long_name(1);
5098 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
5100 if (!r
|| !r
->get_value(&prev_value
))
5103 if (!get_number(&val
, 'u', prev_value
))
5106 warning(WARN_DELIM
, "closing delimiter does not match");
5110 set_number_reg(nm
, val
);
5113 // this implements the \w escape sequence
5115 static void do_width()
5119 int start_level
= input_stack::get_level();
5120 environment
env(curenv
);
5121 environment
*oldenv
= curenv
;
5126 warning(WARN_DELIM
, "missing closing delimiter");
5129 if (tok
.newline()) {
5130 warning(WARN_DELIM
, "missing closing delimiter");
5131 input_stack::push(make_temp_iterator("\n"));
5135 && (compatible_flag
|| input_stack::get_level() == start_level
))
5140 units x
= env
.get_input_line_position().to_units();
5141 input_stack::push(make_temp_iterator(i_to_a(x
)));
5142 env
.width_registers();
5147 charinfo
*page_character
;
5149 void set_page_character()
5151 page_character
= get_optional_char();
5155 static const symbol
percent_symbol("%");
5157 void read_title_parts(node
**part
, hunits
*part_width
)
5160 if (tok
.newline() || tok
.eof())
5163 int start_level
= input_stack::get_level();
5165 for (int i
= 0; i
< 3; i
++) {
5166 while (!tok
.newline() && !tok
.eof()) {
5168 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
5172 if (page_character
!= 0 && tok
.get_char() == page_character
)
5173 interpolate_number_reg(percent_symbol
, 0);
5178 curenv
->wrap_up_tab();
5179 part_width
[i
] = curenv
->get_input_line_position();
5180 part
[i
] = curenv
->extract_output_line();
5182 while (!tok
.newline() && !tok
.eof())
5186 class non_interpreted_node
: public node
{
5189 non_interpreted_node(const macro
&);
5190 int interpret(macro
*);
5192 int ends_sentence();
5199 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
5203 int non_interpreted_node::ends_sentence()
5208 int non_interpreted_node::same(node
*nd
)
5210 return mac
== ((non_interpreted_node
*)nd
)->mac
;
5213 const char *non_interpreted_node::type()
5215 return "non_interpreted_node";
5218 int non_interpreted_node::force_tprint()
5223 int non_interpreted_node::is_tag()
5228 node
*non_interpreted_node::copy()
5230 return new non_interpreted_node(mac
);
5233 int non_interpreted_node::interpret(macro
*m
)
5235 string_iterator
si(mac
);
5236 node
*n
= 0; // pacify compiler
5249 static node
*do_non_interpreted()
5254 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
5259 if (c
== EOF
|| c
== '\n') {
5260 error("missing \\?");
5263 return new non_interpreted_node(mac
);
5266 static void encode_char(macro
*mac
, char c
)
5269 if ((font::use_charnames_in_special
) && tok
.special()) {
5270 charinfo
*ci
= tok
.get_char(1);
5271 const char *s
= ci
->get_symbol()->contents();
5272 if (s
[0] != (char)0) {
5276 while (s
[i
] != (char)0) {
5283 else if (tok
.stretchable_space()
5284 || tok
.unstretchable_space())
5286 else if (!(tok
.hyphen_indicator()
5288 || tok
.transparent_dummy()
5289 || tok
.zero_width_break()))
5290 error("%1 is invalid within \\X", tok
.description());
5293 if ((font::use_charnames_in_special
) && (c
== '\\')) {
5295 * add escape escape sequence
5307 int start_level
= input_stack::get_level();
5310 tok
!= start
|| input_stack::get_level() != start_level
;
5313 warning(WARN_DELIM
, "missing closing delimiter");
5316 if (tok
.newline()) {
5317 input_stack::push(make_temp_iterator("\n"));
5318 warning(WARN_DELIM
, "missing closing delimiter");
5326 else if (tok
.leader())
5328 else if (tok
.backspace())
5332 encode_char(&mac
, c
);
5334 return new special_node(mac
);
5337 void device_request()
5339 if (!tok
.newline() && !tok
.eof()) {
5348 if (c
!= ' ' && c
!= '\t')
5351 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5353 curenv
->add_node(new special_node(mac
));
5358 void device_macro_request()
5360 symbol s
= get_name(1);
5361 if (!(s
.is_null() || s
.is_empty())) {
5362 request_or_macro
*p
= lookup_request(s
);
5363 macro
*m
= p
->to_macro();
5365 curenv
->add_node(new special_node(*m
));
5367 error("can't transparently throughput a request");
5372 void output_request()
5374 if (!tok
.newline() && !tok
.eof()) {
5382 if (c
!= ' ' && c
!= '\t')
5385 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5386 topdiv
->transparent_output(c
);
5387 topdiv
->transparent_output('\n');
5392 extern int image_no
; // from node.cpp
5394 static node
*do_suppress(symbol nm
)
5396 if (nm
.is_null() || nm
.is_empty()) {
5397 error("expecting an argument to escape \\O");
5400 const char *s
= nm
.contents();
5403 if (begin_level
== 0)
5404 // suppress generation of glyphs
5405 return new suppress_node(0, 0);
5408 if (begin_level
== 0)
5409 // enable generation of glyphs
5410 return new suppress_node(1, 0);
5413 if (begin_level
== 0)
5414 return new suppress_node(1, 1);
5424 s
++; // move over '5'
5426 if (*s
== (char)0) {
5427 error("missing position and filename in \\O");
5430 if (!(position
== 'l'
5433 || position
== 'i')) {
5434 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5437 s
++; // onto image name
5438 if (s
== (char *)0) {
5439 error("missing image name for \\O");
5443 if (begin_level
== 0)
5444 return new suppress_node(symbol(s
), position
, image_no
);
5448 error("`%1' is an invalid argument to \\O", *s
);
5453 void special_node::tprint(troff_output_file
*out
)
5456 string_iterator
iter(mac
);
5458 int c
= iter
.get(0);
5461 for (const char *s
= ::asciify(c
); *s
; s
++)
5462 tprint_char(out
, *s
);
5467 int get_file_line(const char **filename
, int *lineno
)
5469 return input_stack::get_location(0, filename
, lineno
);
5475 if (get_integer(&n
)) {
5476 const char *filename
= 0;
5478 symbol s
= get_long_name();
5479 filename
= s
.contents();
5481 (void)input_stack::set_location(filename
, n
-1);
5486 static int nroff_mode
= 0;
5488 static void nroff_request()
5494 static void troff_request()
5500 static void skip_alternative()
5503 // ensure that ``.if 0\{'' works as expected
5504 if (tok
.left_brace())
5508 c
= input_stack::get(0);
5511 if (c
== ESCAPE_LEFT_BRACE
)
5513 else if (c
== ESCAPE_RIGHT_BRACE
)
5515 else if (c
== escape_char
&& escape_char
> 0)
5516 switch(input_stack::get(0)) {
5524 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5528 Note that the level can properly be < 0, eg
5534 So don't give an error message in this case.
5536 if (level
<= 0 && c
== '\n')
5542 static void begin_alternative()
5544 while (tok
.space() || tok
.left_brace())
5554 static int_stack if_else_stack
;
5561 while (tok
.ch() == '!') {
5566 unsigned char c
= tok
.ch();
5569 result
= !nroff_mode
;
5571 else if (c
== 'n') {
5573 result
= nroff_mode
;
5575 else if (c
== 'v') {
5579 else if (c
== 'o') {
5580 result
= (topdiv
->get_page_number() & 1);
5583 else if (c
== 'e') {
5584 result
= !(topdiv
->get_page_number() & 1);
5587 else if (c
== 'd' || c
== 'r') {
5589 symbol nm
= get_name(1);
5595 ? request_dictionary
.lookup(nm
) != 0
5596 : number_reg_dictionary
.lookup(nm
) != 0);
5598 else if (c
== 'm') {
5600 symbol nm
= get_long_name(1);
5605 result
= (nm
== default_symbol
5606 || color_dictionary
.lookup(nm
) != 0);
5608 else if (c
== 'c') {
5611 charinfo
*ci
= tok
.get_char(1);
5616 result
= character_exists(ci
, curenv
);
5619 else if (c
== 'F') {
5621 symbol nm
= get_long_name(1);
5626 result
= check_font(curenv
->get_family()->nm
, nm
);
5628 else if (c
== 'S') {
5630 symbol nm
= get_long_name(1);
5635 result
= check_style(nm
);
5637 else if (tok
.space())
5639 else if (tok
.delimiter()) {
5641 int delim_level
= input_stack::get_level();
5642 environment
env1(curenv
);
5643 environment
env2(curenv
);
5644 environment
*oldenv
= curenv
;
5647 for (int i
= 0; i
< 2; i
++) {
5650 if (tok
.newline() || tok
.eof()) {
5651 warning(WARN_DELIM
, "missing closing delimiter");
5657 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5663 node
*n1
= env1
.extract_output_line();
5664 node
*n2
= env2
.extract_output_line();
5665 result
= same_node_list(n1
, n2
);
5666 delete_node_list(n1
);
5667 delete_node_list(n2
);
5675 if (!get_number(&n
, 'u')) {
5685 begin_alternative();
5691 void if_else_request()
5693 if_else_stack
.push(do_if_request());
5703 if (if_else_stack
.is_empty()) {
5704 warning(WARN_EL
, "unbalanced .el request");
5708 if (if_else_stack
.pop())
5711 begin_alternative();
5715 static int while_depth
= 0;
5716 static int while_break_flag
= 0;
5718 void while_request()
5723 mac
.append(new token_node(tok
));
5725 node
*n
= 0; // pacify compiler
5726 int c
= input_stack::get(&n
);
5742 if (c
== ESCAPE_LEFT_BRACE
)
5744 else if (c
== ESCAPE_RIGHT_BRACE
)
5746 else if (c
== escape_char
)
5749 if (c
== '\n' && level
<= 0)
5754 error("unbalanced \\{ \\}");
5757 input_stack::add_boundary();
5759 input_stack::push(new string_iterator(mac
, "while loop"));
5761 if (!do_if_request()) {
5762 while (input_stack::get(0) != EOF
)
5766 process_input_stack();
5767 if (while_break_flag
|| input_stack::is_return_boundary()) {
5768 while_break_flag
= 0;
5772 input_stack::remove_boundary();
5778 void while_break_request()
5781 error("no while loop");
5785 while_break_flag
= 1;
5786 while (input_stack::get(0) != EOF
)
5792 void while_continue_request()
5795 error("no while loop");
5799 while (input_stack::get(0) != EOF
)
5809 symbol nm
= get_long_name(1);
5813 while (!tok
.newline() && !tok
.eof())
5816 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
5818 input_stack::push(new file_iterator(fp
, nm
.contents()));
5820 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5825 // like .so but use popen()
5830 error(".pso request not allowed in safer mode");
5834 #ifdef POPEN_MISSING
5835 error("pipes not available on this system");
5837 #else /* not POPEN_MISSING */
5838 if (tok
.newline() || tok
.eof())
5839 error("missing command");
5842 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5845 char *buf
= new char[buf_size
];
5847 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5848 const char *s
= asciify(c
);
5849 int slen
= strlen(s
);
5850 if (buf_used
+ slen
+ 1> buf_size
) {
5851 char *old_buf
= buf
;
5852 int old_buf_size
= buf_size
;
5854 buf
= new char[buf_size
];
5855 memcpy(buf
, old_buf
, old_buf_size
);
5858 strcpy(buf
+ buf_used
, s
);
5861 buf
[buf_used
] = '\0';
5863 FILE *fp
= popen(buf
, POPEN_RT
);
5865 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5867 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5871 #endif /* not POPEN_MISSING */
5877 static int llx_reg_contents
= 0;
5878 static int lly_reg_contents
= 0;
5879 static int urx_reg_contents
= 0;
5880 static int ury_reg_contents
= 0;
5882 struct bounding_box
{
5883 int llx
, lly
, urx
, ury
;
5886 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5887 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5889 int parse_bounding_box(char *p
, bounding_box
*bb
)
5891 if (sscanf(p
, "%d %d %d %d",
5892 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5895 /* The Document Structuring Conventions say that the numbers
5896 should be integers. Unfortunately some broken applications
5898 double x1
, x2
, x3
, x4
;
5899 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5907 for (; *p
== ' ' || *p
== '\t'; p
++)
5909 if (strncmp(p
, "(atend)", 7) == 0) {
5914 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5918 // This version is taken from psrm.cpp
5920 #define PS_LINE_MAX 255
5921 cset
white_space("\n\r \t");
5923 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5932 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5933 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5934 error("invalid input character code %1 in `%2'", int(c
), filename
);
5935 else if (i
< PS_LINE_MAX
)
5939 error("PostScript file `%1' is non-conforming "
5940 "because length of line exceeds 255", filename
);
5948 if (c
!= EOF
&& c
!= '\n')
5954 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5956 llx_reg_contents
= llx
;
5957 lly_reg_contents
= lly
;
5958 urx_reg_contents
= urx
;
5959 ury_reg_contents
= ury
;
5962 void do_ps_file(FILE *fp
, const char* filename
)
5966 char buf
[PS_LINE_MAX
];
5967 llx_reg_contents
= lly_reg_contents
=
5968 urx_reg_contents
= ury_reg_contents
= 0;
5969 if (!ps_get_line(buf
, fp
, filename
)) {
5970 error("`%1' is empty", filename
);
5973 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5974 error("`%1' is not conforming to the Document Structuring Conventions",
5978 while (ps_get_line(buf
, fp
, filename
) != 0) {
5979 // in header comments, `%X' (`X' any printable character except
5980 // whitespace) is possible too
5981 if (buf
[0] == '%') {
5982 if (strncmp(buf
+ 1, "%EndComments", 12) == 0)
5984 if (white_space(buf
[1]))
5989 if (strncmp(buf
+ 1, "%BoundingBox:", 13) == 0) {
5990 int res
= parse_bounding_box(buf
+ 14, &bb
);
5992 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5995 else if (res
== 2) {
6000 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6009 // in the trailer, the last BoundingBox comment is significant
6010 for (offset
= 512; !last_try
; offset
*= 2) {
6011 int had_trailer
= 0;
6013 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
6015 if (fseek(fp
, 0L, 0) == -1)
6018 while (ps_get_line(buf
, fp
, filename
) != 0) {
6019 if (buf
[0] == '%' && buf
[1] == '%') {
6021 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
6025 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
6026 int res
= parse_bounding_box(buf
+ 14, &bb
);
6029 else if (res
== 2) {
6030 error("`(atend)' not allowed in trailer of `%1'", filename
);
6034 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6043 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
6048 error("%%%%BoundingBox comment not found in `%1'", filename
);
6051 void ps_bbox_request()
6053 symbol nm
= get_long_name(1);
6057 while (!tok
.newline() && !tok
.eof())
6060 // PS files might contain non-printable characters, such as ^Z
6061 // and CRs not followed by an LF, so open them in binary mode.
6062 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents(),
6065 do_ps_file(fp
, nm
.contents());
6069 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
6074 const char *asciify(int c
)
6077 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
6078 buf
[1] = buf
[2] = '\0';
6080 case ESCAPE_QUESTION
:
6083 case ESCAPE_AMPERSAND
:
6086 case ESCAPE_RIGHT_PARENTHESIS
:
6089 case ESCAPE_UNDERSCORE
:
6095 case ESCAPE_CIRCUMFLEX
:
6098 case ESCAPE_LEFT_BRACE
:
6101 case ESCAPE_RIGHT_BRACE
:
6104 case ESCAPE_LEFT_QUOTE
:
6107 case ESCAPE_RIGHT_QUOTE
:
6125 case ESCAPE_PERCENT
:
6137 case PUSH_GROFF_MODE
:
6138 case PUSH_COMP_MODE
:
6139 case POP_GROFFCOMP_MODE
:
6143 if (invalid_input_char(c
))
6152 const char *input_char_description(int c
)
6156 return "a newline character";
6158 return "a backspace character";
6160 return "a leader character";
6162 return "a tab character";
6164 return "a space character";
6168 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
6169 if (invalid_input_char(c
)) {
6170 const char *s
= asciify(c
);
6177 sprintf(buf
, "magic character code %d", c
);
6186 sprintf(buf
, "character code %d", c
);
6192 if (!tok
.newline() && !tok
.eof()) {
6201 if (c
!= ' ' && c
!= '\t')
6205 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6208 curenv
->add_node(new tag_node(s
, 0));
6215 if (!tok
.newline() && !tok
.eof()) {
6224 if (c
!= ' ' && c
!= '\t')
6228 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6231 curenv
->add_node(new tag_node(s
, 1));
6236 // .tm, .tm1, and .tmc
6238 void do_terminal(int newline
, int string_like
)
6240 if (!tok
.newline() && !tok
.eof()) {
6244 if (string_like
&& c
== '"') {
6248 if (c
!= ' ' && c
!= '\t')
6251 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6252 fputs(asciify(c
), stderr
);
6255 fputc('\n', stderr
);
6270 void terminal_continue()
6275 dictionary
stream_dictionary(20);
6277 void do_open(int append
)
6279 symbol stream
= get_name(1);
6280 if (!stream
.is_null()) {
6281 symbol filename
= get_long_name(1);
6282 if (!filename
.is_null()) {
6284 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
6286 error("can't open `%1' for %2: %3",
6287 filename
.contents(),
6288 append
? "appending" : "writing",
6290 fp
= (FILE *)stream_dictionary
.remove(stream
);
6293 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
6304 error(".open request not allowed in safer mode");
6311 void opena_request()
6314 error(".opena request not allowed in safer mode");
6321 void close_request()
6323 symbol stream
= get_name(1);
6324 if (!stream
.is_null()) {
6325 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
6327 error("no stream named `%1'", stream
.contents());
6334 // .write and .writec
6336 void do_write_request(int newline
)
6338 symbol stream
= get_name(1);
6339 if (stream
.is_null()) {
6343 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6345 error("no stream named `%1'", stream
.contents());
6350 while ((c
= get_copy(0)) == ' ')
6354 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6355 fputs(asciify(c
), fp
);
6362 void write_request()
6364 do_write_request(1);
6367 void write_request_continue()
6369 do_write_request(0);
6372 void write_macro_request()
6374 symbol stream
= get_name(1);
6375 if (stream
.is_null()) {
6379 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6381 error("no stream named `%1'", stream
.contents());
6385 symbol s
= get_name(1);
6390 request_or_macro
*p
= lookup_request(s
);
6391 macro
*m
= p
->to_macro();
6393 error("cannot write request");
6395 string_iterator
iter(*m
);
6397 int c
= iter
.get(0);
6400 fputs(asciify(c
), fp
);
6407 void warnscale_request()
6414 warn_scale
= (double)units_per_inch
;
6416 warn_scale
= (double)units_per_inch
/ 2.54;
6418 warn_scale
= (double)units_per_inch
/ 72.0;
6420 warn_scale
= (double)units_per_inch
/ 6.0;
6423 "invalid scaling indicator `%1', using `i' instead", c
);
6426 warn_scaling_indicator
= c
;
6431 void spreadwarn_request()
6434 if (has_arg() && get_hunits(&n
, 'm')) {
6437 hunits em
= curenv
->get_size();
6438 spread_limit
= (double)n
.to_units()
6439 / (em
.is_zero() ? hresolution
: em
.to_units());
6442 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
6443 // changing value; we mirror at
6444 // -0.5 to make zero a valid value
6448 static void init_charset_table()
6451 strcpy(buf
, "char");
6452 for (int i
= 0; i
< 256; i
++) {
6453 strcpy(buf
+ 4, i_to_a(i
));
6454 charset_table
[i
] = get_charinfo(symbol(buf
));
6455 charset_table
[i
]->set_ascii_code(i
);
6457 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6459 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6460 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6461 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6462 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6463 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6464 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6465 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6466 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6467 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6468 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6469 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6470 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6471 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER
);
6472 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6473 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6474 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6475 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6476 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6477 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6478 page_character
= charset_table
['%'];
6481 static void init_hpf_code_table()
6483 for (int i
= 0; i
< 256; i
++)
6484 hpf_code_table
[i
] = i
;
6487 static void do_translate(int translate_transparent
, int translate_input
)
6490 while (!tok
.newline() && !tok
.eof()) {
6492 // This is a really bizarre troff feature.
6494 translate_space_to_dummy
= tok
.dummy();
6495 if (tok
.newline() || tok
.eof())
6500 charinfo
*ci1
= tok
.get_char(1);
6504 if (tok
.newline() || tok
.eof()) {
6505 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6506 translate_transparent
);
6510 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6511 translate_transparent
);
6512 else if (tok
.stretchable_space())
6513 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6514 translate_transparent
);
6515 else if (tok
.dummy())
6516 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6517 translate_transparent
);
6518 else if (tok
.hyphen_indicator())
6519 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6520 translate_transparent
);
6522 charinfo
*ci2
= tok
.get_char(1);
6526 ci1
->set_translation(0, translate_transparent
, translate_input
);
6528 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6540 void translate_no_transparent()
6545 void translate_input()
6553 if (get_integer(&flags
))
6555 charinfo
*ci
= tok
.get_char(1);
6557 charinfo
*tem
= ci
->get_translation();
6560 ci
->set_flags(flags
);
6567 void hyphenation_code()
6570 while (!tok
.newline() && !tok
.eof()) {
6571 charinfo
*ci
= tok
.get_char(1);
6576 unsigned char c
= tok
.ch();
6578 error("hyphenation code must be ordinary character");
6582 error("hyphenation code cannot be digit");
6585 ci
->set_hyphenation_code(c
);
6586 if (ci
->get_translation()
6587 && ci
->get_translation()->get_translation_input())
6588 ci
->get_translation()->set_hyphenation_code(c
);
6595 void hyphenation_patterns_file_code()
6598 while (!tok
.newline() && !tok
.eof()) {
6600 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6602 error("missing output hyphenation code");
6605 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6606 hpf_code_table
[n1
] = n2
;
6610 error("output hyphenation code must be integer in the range 0..255");
6615 error("input hyphenation code must be integer in the range 0..255");
6622 charinfo
*token::get_char(int required
)
6624 if (type
== TOKEN_CHAR
)
6625 return charset_table
[c
];
6626 if (type
== TOKEN_SPECIAL
)
6627 return get_charinfo(nm
);
6628 if (type
== TOKEN_NUMBERED_CHAR
)
6629 return get_charinfo_by_number(val
);
6630 if (type
== TOKEN_ESCAPE
) {
6631 if (escape_char
!= 0)
6632 return charset_table
[escape_char
];
6634 error("`\\e' used while no current escape character");
6639 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6640 warning(WARN_MISSING
, "missing normal or special character");
6642 error("normal or special character expected (got %1)", description());
6647 charinfo
*get_optional_char()
6651 charinfo
*ci
= tok
.get_char();
6653 check_missing_character();
6659 void check_missing_character()
6661 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6662 error("normal or special character expected (got %1): "
6663 "treated as missing",
6669 int token::add_to_node_list(node
**pp
)
6676 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6682 if (escape_char
!= 0)
6683 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6685 case TOKEN_HYPHEN_INDICATOR
:
6686 *pp
= (*pp
)->add_discretionary_hyphen();
6688 case TOKEN_ITALIC_CORRECTION
:
6689 *pp
= (*pp
)->add_italic_correction(&w
);
6691 case TOKEN_LEFT_BRACE
:
6693 case TOKEN_MARK_INPUT
:
6694 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6700 case TOKEN_NUMBERED_CHAR
:
6701 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6703 case TOKEN_RIGHT_BRACE
:
6706 n
= new hmotion_node(curenv
->get_space_width(),
6707 curenv
->get_fill_color());
6710 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6712 case TOKEN_STRETCHABLE_SPACE
:
6713 n
= new unbreakable_space_node(curenv
->get_space_width(),
6714 curenv
->get_fill_color());
6716 case TOKEN_UNSTRETCHABLE_SPACE
:
6717 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6718 curenv
->get_fill_color());
6720 case TOKEN_TRANSPARENT_DUMMY
:
6721 n
= new transparent_dummy_node
;
6723 case TOKEN_ZERO_WIDTH_BREAK
:
6724 n
= new space_node(H0
, curenv
->get_fill_color());
6726 n
->is_escape_colon();
6738 void token::process()
6740 if (possibly_handle_first_page_transition())
6743 case TOKEN_BACKSPACE
:
6744 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6745 curenv
->get_fill_color()));
6748 curenv
->add_char(charset_table
[c
]);
6751 curenv
->add_node(new dummy_node
);
6760 if (escape_char
!= 0)
6761 curenv
->add_char(charset_table
[escape_char
]);
6763 case TOKEN_BEGIN_TRAP
:
6764 case TOKEN_END_TRAP
:
6765 case TOKEN_PAGE_EJECTOR
:
6766 // these are all handled in process_input_stack()
6768 case TOKEN_HYPHEN_INDICATOR
:
6769 curenv
->add_hyphen_indicator();
6771 case TOKEN_INTERRUPT
:
6772 curenv
->interrupt();
6774 case TOKEN_ITALIC_CORRECTION
:
6775 curenv
->add_italic_correction();
6778 curenv
->handle_tab(1);
6780 case TOKEN_LEFT_BRACE
:
6782 case TOKEN_MARK_INPUT
:
6783 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6789 curenv
->add_node(nd
);
6792 case TOKEN_NUMBERED_CHAR
:
6793 curenv
->add_char(get_charinfo_by_number(val
));
6796 // handled in process_input_stack()
6798 case TOKEN_RIGHT_BRACE
:
6804 curenv
->add_char(get_charinfo(nm
));
6809 case TOKEN_STRETCHABLE_SPACE
:
6810 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6811 curenv
->get_fill_color()));
6813 case TOKEN_UNSTRETCHABLE_SPACE
:
6814 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6815 curenv
->get_fill_color()));
6818 curenv
->handle_tab(0);
6820 case TOKEN_TRANSPARENT
:
6822 case TOKEN_TRANSPARENT_DUMMY
:
6823 curenv
->add_node(new transparent_dummy_node
);
6825 case TOKEN_ZERO_WIDTH_BREAK
:
6827 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6828 tmp
->freeze_space();
6829 tmp
->is_escape_colon();
6830 curenv
->add_node(tmp
);
6838 class nargs_reg
: public reg
{
6840 const char *get_string();
6843 const char *nargs_reg::get_string()
6845 return i_to_a(input_stack::nargs());
6848 class lineno_reg
: public reg
{
6850 const char *get_string();
6853 const char *lineno_reg::get_string()
6857 if (!input_stack::get_location(0, &file
, &line
))
6859 return i_to_a(line
);
6862 class writable_lineno_reg
: public general_reg
{
6864 writable_lineno_reg();
6865 void set_value(units
);
6866 int get_value(units
*);
6869 writable_lineno_reg::writable_lineno_reg()
6873 int writable_lineno_reg::get_value(units
*res
)
6877 if (!input_stack::get_location(0, &file
, &line
))
6883 void writable_lineno_reg::set_value(units n
)
6885 input_stack::set_location(0, n
);
6888 class filename_reg
: public reg
{
6890 const char *get_string();
6893 const char *filename_reg::get_string()
6897 if (input_stack::get_location(0, &file
, &line
))
6903 class break_flag_reg
: public reg
{
6905 const char *get_string();
6908 const char *break_flag_reg::get_string()
6910 return i_to_a(input_stack::get_break_flag());
6913 class constant_reg
: public reg
{
6916 constant_reg(const char *);
6917 const char *get_string();
6920 constant_reg::constant_reg(const char *p
) : s(p
)
6924 const char *constant_reg::get_string()
6929 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6933 const char *constant_int_reg::get_string()
6938 void abort_request()
6943 else if (tok
.newline())
6946 while ((c
= get_copy(0)) == ' ')
6949 if (c
== EOF
|| c
== '\n')
6950 fputs("User Abort.", stderr
);
6952 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6953 fputs(asciify(c
), stderr
);
6955 fputc('\n', stderr
);
6956 cleanup_and_exit(1);
6962 char *s
= new char[len
];
6964 while ((c
= get_copy(0)) == ' ')
6967 while (c
!= '\n' && c
!= EOF
) {
6968 if (!invalid_input_char(c
)) {
6971 s
= new char[len
*2];
6972 memcpy(s
, tem
, len
);
6992 error(".pi request not allowed in safer mode");
6996 #ifdef POPEN_MISSING
6997 error("pipes not available on this system");
6999 #else /* not POPEN_MISSING */
7001 error("can't pipe: output already started");
7006 if ((pc
= read_string()) == 0)
7007 error("can't pipe to empty command");
7009 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
7010 strcpy(s
, pipe_command
);
7013 a_delete pipe_command
;
7020 #endif /* not POPEN_MISSING */
7024 static int system_status
;
7026 void system_request()
7029 error(".sy request not allowed in safer mode");
7033 char *command
= read_string();
7035 error("empty command");
7037 system_status
= system(command
);
7045 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7046 handle_initial_request(COPY_FILE_REQUEST
);
7049 symbol filename
= get_long_name(1);
7050 while (!tok
.newline() && !tok
.eof())
7054 if (!filename
.is_null())
7055 curdiv
->copy_file(filename
.contents());
7063 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7064 handle_initial_request(VJUSTIFY_REQUEST
);
7067 symbol type
= get_long_name(1);
7068 if (!type
.is_null())
7069 curdiv
->vjustify(type
);
7075 void transparent_file()
7077 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7078 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
7081 symbol filename
= get_long_name(1);
7082 while (!tok
.newline() && !tok
.eof())
7086 if (!filename
.is_null()) {
7088 FILE *fp
= include_search_path
.open_file_cautious(filename
.contents());
7090 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
7097 if (invalid_input_char(c
))
7098 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
7100 curdiv
->transparent_output(c
);
7105 curdiv
->transparent_output('\n');
7117 page_range(int, int, page_range
*);
7118 int contains(int n
);
7121 page_range::page_range(int i
, int j
, page_range
*p
)
7122 : first(i
), last(j
), next(p
)
7126 int page_range::contains(int n
)
7128 return n
>= first
&& (last
<= 0 || n
<= last
);
7131 page_range
*output_page_list
= 0;
7133 int in_output_page_list(int n
)
7135 if (!output_page_list
)
7137 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
7143 static void parse_output_page_list(char *p
)
7149 else if (csdigit(*p
)) {
7152 i
= i
*10 + *p
++ - '0';
7153 while (csdigit(*p
));
7163 j
= j
*10 + *p
++ - '0';
7164 while (csdigit(*p
));
7170 last_page_number
= -1;
7171 else if (last_page_number
>= 0 && j
> last_page_number
)
7172 last_page_number
= j
;
7173 output_page_list
= new page_range(i
, j
, output_page_list
);
7179 error("bad output page list");
7180 output_page_list
= 0;
7184 static FILE *open_mac_file(const char *mac
, char **path
)
7186 // Try first FOOBAR.tmac, then tmac.FOOBAR
7187 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
7189 strcat(s1
, MACRO_POSTFIX
);
7190 FILE *fp
= mac_path
->open_file(s1
, path
);
7193 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
7194 strcpy(s2
, MACRO_PREFIX
);
7196 fp
= mac_path
->open_file(s2
, path
);
7202 static void process_macro_file(const char *mac
)
7205 FILE *fp
= open_mac_file(mac
, &path
);
7207 fatal("can't find macro file %1", mac
);
7208 const char *s
= symbol(path
).contents();
7210 input_stack::push(new file_iterator(fp
, s
));
7212 process_input_stack();
7215 static void process_startup_file(const char *filename
)
7218 search_path
*orig_mac_path
= mac_path
;
7219 mac_path
= &config_macro_path
;
7220 FILE *fp
= mac_path
->open_file(filename
, &path
);
7222 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
7225 process_input_stack();
7227 mac_path
= orig_mac_path
;
7232 symbol nm
= get_long_name(1);
7236 while (!tok
.newline() && !tok
.eof())
7239 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
7240 // .mso doesn't (and cannot) go through open_mac_file, so we
7241 // need to do it here manually: If we have tmac.FOOBAR, try
7242 // FOOBAR.tmac and vice versa
7244 const char *fn
= nm
.contents();
7245 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
7246 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
7247 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
7248 strcat(s
, MACRO_POSTFIX
);
7249 fp
= mac_path
->open_file(s
, &path
);
7253 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
7254 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
7255 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
7256 strcpy(s
, MACRO_PREFIX
);
7257 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
7258 fp
= mac_path
->open_file(s
, &path
);
7264 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
7268 error("can't find macro file `%1'", nm
.contents());
7273 static void process_input_file(const char *name
)
7276 if (strcmp(name
, "-") == 0) {
7282 fp
= include_search_path
.open_file_cautious(name
);
7284 fatal("can't open `%1': %2", name
, strerror(errno
));
7286 input_stack::push(new file_iterator(fp
, name
));
7288 process_input_stack();
7291 // make sure the_input is empty before calling this
7293 static int evaluate_expression(const char *expr
, units
*res
)
7295 input_stack::push(make_temp_iterator(expr
));
7297 int success
= get_number(res
, 'u');
7298 while (input_stack::get(0) != EOF
)
7303 static void do_register_assignment(const char *s
)
7305 const char *p
= strchr(s
, '=');
7311 if (evaluate_expression(s
+ 1, &n
))
7312 set_number_reg(buf
, n
);
7315 char *buf
= new char[p
- s
+ 1];
7316 memcpy(buf
, s
, p
- s
);
7319 if (evaluate_expression(p
+ 1, &n
))
7320 set_number_reg(buf
, n
);
7325 static void set_string(const char *name
, const char *value
)
7327 macro
*m
= new macro
;
7328 for (const char *p
= value
; *p
; p
++)
7329 if (!invalid_input_char((unsigned char)*p
))
7331 request_dictionary
.define(name
, m
);
7334 static void do_string_assignment(const char *s
)
7336 const char *p
= strchr(s
, '=');
7341 set_string(buf
, s
+ 1);
7344 char *buf
= new char[p
- s
+ 1];
7345 memcpy(buf
, s
, p
- s
);
7347 set_string(buf
, p
+ 1);
7352 struct string_list
{
7355 string_list(const char *ss
) : s(ss
), next(0) {}
7359 static void prepend_string(const char *s
, string_list
**p
)
7361 string_list
*l
= new string_list(s
);
7367 static void add_string(const char *s
, string_list
**p
)
7371 *p
= new string_list(s
);
7374 void usage(FILE *stream
, const char *prog
)
7377 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7378 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7382 int main(int argc
, char **argv
)
7384 program_name
= argv
[0];
7385 static char stderr_buf
[BUFSIZ
];
7386 setbuf(stderr
, stderr_buf
);
7388 string_list
*macros
= 0;
7389 string_list
*register_assignments
= 0;
7390 string_list
*string_assignments
= 0;
7395 int no_rc
= 0; // don't process troffrc and troffrc-end
7396 int next_page_number
= 0; // pacify compiler
7398 hresolution
= vresolution
= 1;
7399 // restore $PATH if called from groff
7400 char* groff_path
= getenv("GROFF_PATH__");
7407 if (putenv(strsave(e
.contents())))
7408 fatal("putenv failed");
7410 static const struct option long_options
[] = {
7411 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
7412 { "version", no_argument
, 0, 'v' },
7415 #if defined(DEBUGGING)
7416 #define DEBUG_OPTION "D"
7418 while ((c
= getopt_long(argc
, argv
,
7419 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7420 DEBUG_OPTION
, long_options
, 0))
7425 printf("GNU troff (groff) version %s\n", Version_string
);
7430 // Search path for .psbb files
7431 // and most other non-system input files.
7432 include_search_path
.command_line_dir(optarg
);
7437 is_html
= (strcmp(device
, "html") == 0);
7440 compatible_flag
= 1;
7446 macro_path
.command_line_dir(optarg
);
7447 safer_macro_path
.command_line_dir(optarg
);
7448 config_macro_path
.command_line_dir(optarg
);
7451 font::command_line_font_dir(optarg
);
7454 add_string(optarg
, ¯os
);
7463 enable_warning(optarg
);
7466 disable_warning(optarg
);
7475 ascii_output_flag
= 1;
7478 suppress_output_flag
= 1;
7481 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7484 error("bad page number");
7487 parse_output_page_list(optarg
);
7490 if (*optarg
== '\0')
7491 error("`-d' requires non-empty argument");
7493 add_string(optarg
, &string_assignments
);
7496 if (*optarg
== '\0')
7497 error("`-r' requires non-empty argument");
7499 add_string(optarg
, ®ister_assignments
);
7502 default_family
= symbol(optarg
);
7508 // silently ignore these
7511 unsafe_flag
= 1; // unsafe behaviour
7513 #if defined(DEBUGGING)
7518 case CHAR_MAX
+ 1: // --help
7519 usage(stdout
, argv
[0]);
7523 usage(stderr
, argv
[0]);
7525 break; // never reached
7530 mac_path
= ¯o_path
;
7531 set_string(".T", device
);
7532 init_charset_table();
7533 init_hpf_code_table();
7534 if (!font::load_desc())
7535 fatal("sorry, I can't continue");
7536 units_per_inch
= font::res
;
7537 hresolution
= font::hor
;
7538 vresolution
= font::vert
;
7539 sizescale
= font::sizescale
;
7540 tcommand_flag
= font::tcommand
;
7541 warn_scale
= (double)units_per_inch
;
7542 warn_scaling_indicator
= 'i';
7543 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7544 default_family
= symbol(font::family
);
7545 font_size::init_size_table(font::sizes
);
7548 if (font::style_table
) {
7549 for (i
= 0; font::style_table
[i
]; i
++)
7550 mount_style(j
++, symbol(font::style_table
[i
]));
7552 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7553 // In the DESC file a font name of 0 (zero) means leave this
7555 if (strcmp(font::font_name_table
[i
], "0") != 0)
7556 mount_font(j
, symbol(font::font_name_table
[i
]));
7557 curdiv
= topdiv
= new top_level_diversion
;
7559 topdiv
->set_next_page_number(next_page_number
);
7560 init_input_requests();
7561 init_env_requests();
7562 init_div_requests();
7564 init_column_requests();
7566 init_node_requests();
7567 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7569 init_reg_requests();
7570 init_hyphen_requests();
7571 init_environments();
7572 while (string_assignments
) {
7573 do_string_assignment(string_assignments
->s
);
7574 string_list
*tem
= string_assignments
;
7575 string_assignments
= string_assignments
->next
;
7578 while (register_assignments
) {
7579 do_register_assignment(register_assignments
->s
);
7580 string_list
*tem
= register_assignments
;
7581 register_assignments
= register_assignments
->next
;
7585 process_startup_file(INITIAL_STARTUP_FILE
);
7587 process_macro_file(macros
->s
);
7588 string_list
*tem
= macros
;
7589 macros
= macros
->next
;
7593 process_startup_file(FINAL_STARTUP_FILE
);
7594 for (i
= optind
; i
< argc
; i
++)
7595 process_input_file(argv
[i
]);
7596 if (optind
>= argc
|| iflag
)
7597 process_input_file("-");
7599 return 0; // not reached
7605 if (has_arg() && get_integer(&n
)) {
7606 if (n
& ~WARN_TOTAL
) {
7607 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7613 warning_mask
= WARN_TOTAL
;
7617 static void init_registers()
7619 #ifdef LONG_FOR_TIME_T
7621 #else /* not LONG_FOR_TIME_T */
7623 #endif /* not LONG_FOR_TIME_T */
7625 // Use struct here to work around misfeature in old versions of g++.
7626 struct tm
*tt
= localtime(&t
);
7627 set_number_reg("seconds", int(tt
->tm_sec
));
7628 set_number_reg("minutes", int(tt
->tm_min
));
7629 set_number_reg("hours", int(tt
->tm_hour
));
7630 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7631 set_number_reg("dy", int(tt
->tm_mday
));
7632 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7633 set_number_reg("year", int(1900 + tt
->tm_year
));
7634 set_number_reg("yr", int(tt
->tm_year
));
7635 set_number_reg("$$", getpid());
7636 number_reg_dictionary
.define(".A",
7637 new constant_reg(ascii_output_flag
7643 * registers associated with \O
7646 static int output_reg_minx_contents
= -1;
7647 static int output_reg_miny_contents
= -1;
7648 static int output_reg_maxx_contents
= -1;
7649 static int output_reg_maxy_contents
= -1;
7651 void check_output_limits(int x
, int y
)
7653 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7654 output_reg_minx_contents
= x
;
7655 if (x
> output_reg_maxx_contents
)
7656 output_reg_maxx_contents
= x
;
7657 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7658 output_reg_miny_contents
= y
;
7659 if (y
> output_reg_maxy_contents
)
7660 output_reg_maxy_contents
= y
;
7663 void reset_output_registers()
7665 output_reg_minx_contents
= -1;
7666 output_reg_miny_contents
= -1;
7667 output_reg_maxx_contents
= -1;
7668 output_reg_maxy_contents
= -1;
7671 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7673 *minx
= output_reg_minx_contents
;
7674 *miny
= output_reg_miny_contents
;
7675 *maxx
= output_reg_maxx_contents
;
7676 *maxy
= output_reg_maxy_contents
;
7679 void init_input_requests()
7681 init_request("ab", abort_request
);
7682 init_request("als", alias_macro
);
7683 init_request("am", append_macro
);
7684 init_request("am1", append_nocomp_macro
);
7685 init_request("ami", append_indirect_macro
);
7686 init_request("ami1", append_indirect_nocomp_macro
);
7687 init_request("as", append_string
);
7688 init_request("as1", append_nocomp_string
);
7689 init_request("asciify", asciify_macro
);
7690 init_request("backtrace", backtrace_request
);
7691 init_request("blm", blank_line_macro
);
7692 init_request("break", while_break_request
);
7693 init_request("cf", copy_file
);
7694 init_request("cflags", char_flags
);
7695 init_request("char", define_character
);
7696 init_request("chop", chop_macro
);
7697 init_request("close", close_request
);
7698 init_request("color", activate_color
);
7699 init_request("composite", composite_request
);
7700 init_request("continue", while_continue_request
);
7701 init_request("cp", compatible
);
7702 init_request("de", define_macro
);
7703 init_request("de1", define_nocomp_macro
);
7704 init_request("defcolor", define_color
);
7705 init_request("dei", define_indirect_macro
);
7706 init_request("dei1", define_indirect_nocomp_macro
);
7707 init_request("device", device_request
);
7708 init_request("devicem", device_macro_request
);
7709 init_request("do", do_request
);
7710 init_request("ds", define_string
);
7711 init_request("ds1", define_nocomp_string
);
7712 init_request("ec", set_escape_char
);
7713 init_request("ecr", restore_escape_char
);
7714 init_request("ecs", save_escape_char
);
7715 init_request("el", else_request
);
7716 init_request("em", end_macro
);
7717 init_request("eo", escape_off
);
7718 init_request("ex", exit_request
);
7719 init_request("fchar", define_fallback_character
);
7720 #ifdef WIDOW_CONTROL
7721 init_request("fpl", flush_pending_lines
);
7722 #endif /* WIDOW_CONTROL */
7723 init_request("hcode", hyphenation_code
);
7724 init_request("hpfcode", hyphenation_patterns_file_code
);
7725 init_request("ie", if_else_request
);
7726 init_request("if", if_request
);
7727 init_request("ig", ignore
);
7728 init_request("length", length_request
);
7729 init_request("lf", line_file
);
7730 init_request("mso", macro_source
);
7731 init_request("nop", nop_request
);
7732 init_request("nroff", nroff_request
);
7733 init_request("nx", next_file
);
7734 init_request("open", open_request
);
7735 init_request("opena", opena_request
);
7736 init_request("output", output_request
);
7737 init_request("pc", set_page_character
);
7738 init_request("pi", pipe_output
);
7739 init_request("pm", print_macros
);
7740 init_request("psbb", ps_bbox_request
);
7741 #ifndef POPEN_MISSING
7742 init_request("pso", pipe_source
);
7743 #endif /* not POPEN_MISSING */
7744 init_request("rchar", remove_character
);
7745 init_request("rd", read_request
);
7746 init_request("return", return_macro_request
);
7747 init_request("rm", remove_macro
);
7748 init_request("rn", rename_macro
);
7749 init_request("schar", define_special_character
);
7750 init_request("shift", shift
);
7751 init_request("so", source
);
7752 init_request("spreadwarn", spreadwarn_request
);
7753 init_request("substring", substring_request
);
7754 init_request("sy", system_request
);
7755 init_request("tag", tag
);
7756 init_request("taga", taga
);
7757 init_request("tm", terminal
);
7758 init_request("tm1", terminal1
);
7759 init_request("tmc", terminal_continue
);
7760 init_request("tr", translate
);
7761 init_request("trf", transparent_file
);
7762 init_request("trin", translate_input
);
7763 init_request("trnt", translate_no_transparent
);
7764 init_request("troff", troff_request
);
7765 init_request("unformat", unformat_macro
);
7767 init_request("vj", vjustify
);
7769 init_request("warn", warn_request
);
7770 init_request("warnscale", warnscale_request
);
7771 init_request("while", while_request
);
7772 init_request("write", write_request
);
7773 init_request("writec", write_request_continue
);
7774 init_request("writem", write_macro_request
);
7775 number_reg_dictionary
.define(".$", new nargs_reg
);
7776 number_reg_dictionary
.define(".br", new break_flag_reg
);
7777 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7778 number_reg_dictionary
.define(".O", new variable_reg(&begin_level
));
7779 number_reg_dictionary
.define(".c", new lineno_reg
);
7780 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7781 number_reg_dictionary
.define(".F", new filename_reg
);
7782 number_reg_dictionary
.define(".g", new constant_reg("1"));
7783 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7784 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7785 number_reg_dictionary
.define(".U", new constant_int_reg(&unsafe_flag
));
7786 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7787 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7788 extern const char *major_version
;
7789 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7790 extern const char *revision
;
7791 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7792 extern const char *minor_version
;
7793 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7794 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7795 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7796 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7797 number_reg_dictionary
.define("opmaxx",
7798 new variable_reg(&output_reg_maxx_contents
));
7799 number_reg_dictionary
.define("opmaxy",
7800 new variable_reg(&output_reg_maxy_contents
));
7801 number_reg_dictionary
.define("opminx",
7802 new variable_reg(&output_reg_minx_contents
));
7803 number_reg_dictionary
.define("opminy",
7804 new variable_reg(&output_reg_miny_contents
));
7805 number_reg_dictionary
.define("slimit",
7806 new variable_reg(&input_stack::limit
));
7807 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7808 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7809 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7812 object_dictionary
request_dictionary(501);
7814 void init_request(const char *s
, REQUEST_FUNCP f
)
7816 request_dictionary
.define(s
, new request(f
));
7819 static request_or_macro
*lookup_request(symbol nm
)
7821 assert(!nm
.is_null());
7822 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7824 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7826 request_dictionary
.define(nm
, p
);
7831 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7833 // Don't interpret character definitions in compatible mode.
7834 int old_compatible_flag
= compatible_flag
;
7835 compatible_flag
= 0;
7836 int old_escape_char
= escape_char
;
7838 macro
*mac
= ci
->set_macro(0);
7840 environment
*oldenv
= curenv
;
7841 environment
env(envp
);
7843 curenv
->set_composite();
7844 token old_tok
= tok
;
7845 input_stack::add_boundary();
7846 string_iterator
*si
=
7847 new string_iterator(*mac
, "composite character", ci
->nm
);
7848 input_stack::push(si
);
7849 // we don't use process_input_stack, because we don't want to recognise
7855 if (tok
.newline()) {
7856 error("composite character mustn't contain newline");
7864 node
*n
= curenv
->extract_output_line();
7865 input_stack::remove_boundary();
7869 compatible_flag
= old_compatible_flag
;
7870 escape_char
= old_escape_char
;
7875 static node
*read_draw_node()
7879 if (!start
.delimiter(1)){
7882 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7887 error("missing argument");
7889 unsigned char type
= tok
.ch();
7891 read_color_draw_node(start
);
7896 hvpair
*point
= new hvpair
[maxpoints
];
7901 for (i
= 0; tok
!= start
; i
++) {
7902 if (i
== maxpoints
) {
7903 hvpair
*oldpoint
= point
;
7904 point
= new hvpair
[maxpoints
*2];
7905 for (int j
= 0; j
< maxpoints
; j
++)
7906 point
[j
] = oldpoint
[j
];
7910 if (!get_hunits(&point
[i
].h
,
7911 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7922 if (!get_vunits(&point
[i
].v
, 'v')) {
7928 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7933 if (npoints
!= 1 || no_last_v
) {
7934 error("two arguments needed for line");
7939 if (npoints
!= 1 || !no_last_v
) {
7940 error("one argument needed for circle");
7946 if (npoints
!= 1 || no_last_v
) {
7947 error("two arguments needed for ellipse");
7952 if (npoints
!= 2 || no_last_v
) {
7953 error("four arguments needed for arc");
7959 error("even number of arguments needed for spline");
7962 if (npoints
!= 1 || !no_last_v
) {
7963 error("one argument needed for gray shade");
7968 // silently pass it through
7971 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7972 curenv
->get_font_size(),
7973 curenv
->get_glyph_color(),
7974 curenv
->get_fill_color());
7986 static void read_color_draw_node(token
&start
)
7990 error("missing color scheme");
7993 unsigned char scheme
= tok
.ch();
7996 char end
= start
.ch();
7999 col
= read_cmy(end
);
8002 col
= &default_color
;
8005 col
= read_gray(end
);
8008 col
= read_cmyk(end
);
8011 col
= read_rgb(end
);
8015 curenv
->set_fill_color(col
);
8016 while (tok
!= start
) {
8017 if (tok
.newline() || tok
.eof()) {
8018 warning(WARN_DELIM
, "missing closing delimiter");
8019 input_stack::push(make_temp_iterator("\n"));
8030 } warning_table
[] = {
8031 { "char", WARN_CHAR
},
8032 { "range", WARN_RANGE
},
8033 { "break", WARN_BREAK
},
8034 { "delim", WARN_DELIM
},
8036 { "scale", WARN_SCALE
},
8037 { "number", WARN_NUMBER
},
8038 { "syntax", WARN_SYNTAX
},
8039 { "tab", WARN_TAB
},
8040 { "right-brace", WARN_RIGHT_BRACE
},
8041 { "missing", WARN_MISSING
},
8042 { "input", WARN_INPUT
},
8043 { "escape", WARN_ESCAPE
},
8044 { "space", WARN_SPACE
},
8045 { "font", WARN_FONT
},
8047 { "mac", WARN_MAC
},
8048 { "reg", WARN_REG
},
8050 { "color", WARN_COLOR
},
8051 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
8052 { "w", WARN_TOTAL
},
8053 { "default", DEFAULT_WARNING_MASK
},
8056 static int lookup_warning(const char *name
)
8058 for (unsigned int i
= 0;
8059 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
8061 if (strcmp(name
, warning_table
[i
].name
) == 0)
8062 return warning_table
[i
].mask
;
8066 static void enable_warning(const char *name
)
8068 int mask
= lookup_warning(name
);
8070 warning_mask
|= mask
;
8072 error("unknown warning `%1'", name
);
8075 static void disable_warning(const char *name
)
8077 int mask
= lookup_warning(name
);
8079 warning_mask
&= ~mask
;
8081 error("unknown warning `%1'", name
);
8084 static void copy_mode_error(const char *format
,
8090 static const char prefix
[] = "(in ignored input) ";
8091 char *s
= new char[sizeof(prefix
) + strlen(format
)];
8094 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
8098 error(format
, arg1
, arg2
, arg3
);
8101 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
8103 static void do_error(error_type type
,
8109 const char *filename
;
8111 if (inhibit_errors
&& type
< FATAL
)
8114 input_stack::backtrace();
8115 if (!get_file_line(&filename
, &lineno
))
8118 errprint("%1:%2: ", filename
, lineno
);
8119 else if (program_name
)
8120 fprintf(stderr
, "%s: ", program_name
);
8123 fputs("fatal error: ", stderr
);
8128 fputs("warning: ", stderr
);
8130 case OUTPUT_WARNING
:
8131 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
8132 fprintf(stderr
, "warning [p %d, %.1f%c",
8133 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
8134 if (topdiv
!= curdiv
) {
8135 double fromtop1
= curdiv
->get_vertical_position().to_units()
8137 fprintf(stderr
, ", div `%s', %.1f%c",
8138 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
8140 fprintf(stderr
, "]: ");
8143 errprint(format
, arg1
, arg2
, arg3
);
8144 fputc('\n', stderr
);
8147 cleanup_and_exit(1);
8150 int warning(warning_type t
,
8156 if ((t
& warning_mask
) != 0) {
8157 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
8164 int output_warning(warning_type t
,
8170 if ((t
& warning_mask
) != 0) {
8171 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
8178 void error(const char *format
,
8183 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
8186 void fatal(const char *format
,
8191 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
8194 void fatal_with_file_and_line(const char *filename
, int lineno
,
8200 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
8201 errprint(format
, arg1
, arg2
, arg3
);
8202 fputc('\n', stderr
);
8204 cleanup_and_exit(1);
8207 void error_with_file_and_line(const char *filename
, int lineno
,
8213 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
8214 errprint(format
, arg1
, arg2
, arg3
);
8215 fputc('\n', stderr
);
8219 dictionary
charinfo_dictionary(501);
8221 charinfo
*get_charinfo(symbol nm
)
8223 void *p
= charinfo_dictionary
.lookup(nm
);
8225 return (charinfo
*)p
;
8226 charinfo
*cp
= new charinfo(nm
);
8227 (void)charinfo_dictionary
.lookup(nm
, cp
);
8231 int charinfo::next_index
= 0;
8233 charinfo::charinfo(symbol s
)
8234 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
8235 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8236 not_found(0), transparent_translate(1), translate_input(0),
8237 mode(CHAR_NORMAL
), nm(s
)
8239 index
= next_index
++;
8243 void charinfo::set_hyphenation_code(unsigned char c
)
8245 hyphenation_code
= c
;
8248 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
8252 if (hyphenation_code
!= 0)
8253 ci
->set_hyphenation_code(hyphenation_code
);
8254 if (asciify_code
!= 0)
8255 ci
->set_asciify_code(asciify_code
);
8256 else if (ascii_code
!= 0)
8257 ci
->set_asciify_code(ascii_code
);
8258 ci
->set_translation_input();
8260 special_translation
= TRANSLATE_NONE
;
8261 transparent_translate
= tt
;
8264 void charinfo::set_special_translation(int c
, int tt
)
8266 special_translation
= c
;
8268 transparent_translate
= tt
;
8271 void charinfo::set_ascii_code(unsigned char c
)
8276 void charinfo::set_asciify_code(unsigned char c
)
8281 macro
*charinfo::set_macro(macro
*m
)
8288 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
8296 void charinfo::set_number(int n
)
8302 int charinfo::get_number()
8304 assert(number
>= 0);
8308 symbol
UNNAMED_SYMBOL("---");
8310 // For numbered characters not between 0 and 255, we make a symbol out
8311 // of the number and store them in this dictionary.
8313 dictionary
numbered_charinfo_dictionary(11);
8315 charinfo
*get_charinfo_by_number(int n
)
8317 static charinfo
*number_table
[256];
8319 if (n
>= 0 && n
< 256) {
8320 charinfo
*ci
= number_table
[n
];
8322 ci
= new charinfo(UNNAMED_SYMBOL
);
8324 number_table
[n
] = ci
;
8329 symbol
ns(i_to_a(n
));
8330 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
8332 ci
= new charinfo(UNNAMED_SYMBOL
);
8334 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
8340 // This overrides the same function from libgroff; while reading font
8341 // definition files it puts single-letter glyph names into `charset_table'
8342 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8343 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8345 glyph
*name_to_glyph(const char *nm
)
8349 ci
= charset_table
[nm
[0] & 0xff];
8350 else if (nm
[0] == '\\' && nm
[2] == 0)
8351 ci
= get_charinfo(symbol(nm
+ 1));
8353 ci
= get_charinfo(symbol(nm
));
8354 return ci
->as_glyph();
8357 glyph
*number_to_glyph(int n
)
8359 return get_charinfo_by_number(n
)->as_glyph();
8362 const char *glyph_to_name(glyph
*g
)
8364 charinfo
*ci
= (charinfo
*)g
; // Every glyph is actually a charinfo.
8365 return (ci
->nm
!= UNNAMED_SYMBOL
? ci
->nm
.contents() : NULL
);