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"
31 #include "file_case.h"
39 #include "macropath.h"
44 // Needed for getpid() and isatty()
49 #ifdef NEED_DECLARATION_PUTENV
51 int putenv(const char *);
53 #endif /* NEED_DECLARATION_PUTENV */
55 #define MACRO_PREFIX "tmac."
56 #define MACRO_POSTFIX ".tmac"
57 #define INITIAL_STARTUP_FILE "troffrc"
58 #define FINAL_STARTUP_FILE "troffrc-end"
59 #define DEFAULT_INPUT_STACK_LIMIT 1000
61 #ifndef DEFAULT_WARNING_MASK
62 // warnings that are enabled by default
63 #define DEFAULT_WARNING_MASK \
64 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
67 // initial size of buffer for reading names; expanded as necessary
70 extern "C" const char *program_name
;
71 extern "C" const char *Version_string
;
74 void init_column_requests();
77 static node
*read_draw_node();
78 static void read_color_draw_node(token
&);
79 static void push_token(const token
&);
84 void transparent_file();
88 int color_flag
= 1; // colors are on by default
89 static int backtrace_flag
= 0;
91 char *pipe_command
= 0;
93 charinfo
*charset_table
[256];
94 unsigned char hpf_code_table
[256];
96 static int warning_mask
= DEFAULT_WARNING_MASK
;
97 static int inhibit_errors
= 0;
98 static int ignoring
= 0;
100 static void enable_warning(const char *);
101 static void disable_warning(const char *);
103 static int escape_char
= '\\';
104 static symbol end_macro_name
;
105 static symbol blank_line_macro_name
;
106 static int compatible_flag
= 0;
107 int ascii_output_flag
= 0;
108 int suppress_output_flag
= 0;
110 int begin_level
= 0; // number of nested \O escapes
112 int have_input
= 0; // whether \f, \F, \D'F...', \H, \m, \M,
113 // \O[345], \R, \s, or \S has been processed
115 int old_have_input
= 0; // value of have_input right before \n
116 int tcommand_flag
= 0;
117 int unsafe_flag
= 0; // safer by default
119 int have_string_arg
= 0; // whether we have \*[foo bar...]
121 double spread_limit
= -3.0 - 1.0; // negative means deactivated
124 char warn_scaling_indicator
;
125 int debug_state
= 0; // turns on debugging of the html troff state
127 search_path
*mac_path
= &safer_macro_path
;
129 // Defaults to the current directory.
130 search_path
include_search_path(0, 0, 0, 1);
132 static int get_copy(node
**, int = 0, int = 0);
133 static void copy_mode_error(const char *,
134 const errarg
& = empty_errarg
,
135 const errarg
& = empty_errarg
,
136 const errarg
& = empty_errarg
);
138 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
139 static symbol
read_escape_name(read_mode
= NO_ARGS
);
140 static symbol
read_long_escape_name(read_mode
= NO_ARGS
);
141 static void interpolate_string(symbol
);
142 static void interpolate_string_with_args(symbol
);
143 static void interpolate_macro(symbol
, int = 0);
144 static void interpolate_number_format(symbol
);
145 static void interpolate_environment_variable(symbol
);
147 static symbol
composite_glyph_name(symbol
);
148 static void interpolate_arg(symbol
);
149 static request_or_macro
*lookup_request(symbol
);
150 static int get_delim_number(units
*, unsigned char);
151 static int get_delim_number(units
*, unsigned char, units
);
152 static symbol
do_get_long_name(int, char);
153 static int get_line_arg(units
*res
, unsigned char si
, charinfo
**cp
);
154 static int read_size(int *);
155 static symbol
get_delim_name();
156 static void init_registers();
157 static void trapping_blank_line();
159 class input_iterator
;
160 input_iterator
*make_temp_iterator(const char *);
161 const char *input_char_description(int);
163 void process_input_stack();
164 void chop_macro(); // declare to avoid friend name injection
167 void set_escape_char()
171 error("bad escape character");
175 escape_char
= tok
.ch();
188 static int saved_escape_char
= '\\';
190 void save_escape_char()
192 saved_escape_char
= escape_char
;
196 void restore_escape_char()
198 escape_char
= saved_escape_char
;
204 class input_iterator
{
207 input_iterator(int is_div
);
208 virtual ~input_iterator() {}
210 friend class input_stack
;
212 statem
*diversion_state
;
214 const unsigned char *ptr
;
215 const unsigned char *eptr
;
216 input_iterator
*next
;
218 virtual int fill(node
**);
220 virtual int has_args() { return 0; }
221 virtual int nargs() { return 0; }
222 virtual input_iterator
*get_arg(int) { return 0; }
223 virtual arg_list
*get_arg_list() { return 0; }
224 virtual symbol
get_macro_name() { return NULL_SYMBOL
; }
225 virtual int space_follows_arg(int) { return 0; }
226 virtual int get_break_flag() { return 0; }
227 virtual int get_location(int, const char **, int *) { return 0; }
228 virtual void backtrace() {}
229 virtual int set_location(const char *, int) { return 0; }
230 virtual int next_file(file_case
*, const char *) { return 0; }
231 virtual void shift(int) {}
232 virtual int is_boundary() {return 0; }
233 virtual int is_file() { return 0; }
234 virtual int is_macro() { return 0; }
235 virtual void save_compatible_flag(int) {}
236 virtual int get_compatible_flag() { return 0; }
239 input_iterator::input_iterator()
240 : is_diversion(0), ptr(0), eptr(0)
244 input_iterator::input_iterator(int is_div
)
245 : is_diversion(is_div
), ptr(0), eptr(0)
249 int input_iterator::fill(node
**)
254 int input_iterator::peek()
259 inline int input_iterator::get(node
**p
)
261 return ptr
< eptr
? *ptr
++ : fill(p
);
264 class input_boundary
: public input_iterator
{
266 int is_boundary() { return 1; }
269 class input_return_boundary
: public input_iterator
{
271 int is_boundary() { return 2; }
274 class file_iterator
: public input_iterator
{
277 const char *filename
;
280 enum { BUF_SIZE
= 512 };
281 unsigned char buf
[BUF_SIZE
];
284 file_iterator(file_case
*, const char *);
288 int get_location(int, const char **, int *);
290 int set_location(const char *, int);
291 int next_file(file_case
*, const char *);
295 file_iterator::file_iterator(file_case
*fcp
, const char *fn
)
296 : _fcp(fcp
), lineno(1), filename(fn
), newline_flag(0), seen_escape(0)
298 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
301 the_output
->put_filename(fn
, _fcp
->is_pipe());
305 file_iterator::~file_iterator()
311 void file_iterator::close()
319 int file_iterator::is_file()
324 int file_iterator::next_file(file_case
*fcp
, const char *s
)
337 int file_iterator::fill(node
**)
342 unsigned char *p
= buf
;
344 unsigned char *e
= p
+ BUF_SIZE
;
346 int c
= _fcp
->get_c();
349 if (invalid_input_char(c
))
350 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
358 seen_escape
= (c
== '\\');
371 int file_iterator::peek()
373 int c
= _fcp
->get_c();
374 while (invalid_input_char(c
)) {
375 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
383 int file_iterator::get_location(int /*allow_macro*/,
384 const char **filenamep
, int *linenop
)
387 if (filename
!= 0 && strcmp(filename
, "-") == 0)
388 *filenamep
= "<standard input>";
390 *filenamep
= filename
;
394 void file_iterator::backtrace()
396 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
397 _fcp
->is_pipe() ? "process" : "file");
400 int file_iterator::set_location(const char *f
, int ln
)
406 the_output
->put_filename(f
, 0);
412 input_iterator nil_iterator
;
416 static int get(node
**);
418 static void push(input_iterator
*);
419 static input_iterator
*get_arg(int);
420 static arg_list
*get_arg_list();
421 static symbol
get_macro_name();
422 static int space_follows_arg(int);
423 static int get_break_flag();
425 static int get_location(int, const char **, int *);
426 static int set_location(const char *, int);
427 static void backtrace();
428 static void backtrace_all();
429 static void next_file(file_case
*, const char *);
430 static void end_file();
431 static void shift(int n
);
432 static void add_boundary();
433 static void add_return_boundary();
434 static int is_return_boundary();
435 static void remove_boundary();
436 static int get_level();
437 static int get_div_level();
438 static void increase_level();
439 static void decrease_level();
441 static void pop_macro();
442 static void save_compatible_flag(int);
443 static int get_compatible_flag();
444 static statem
*get_diversion_state();
445 static void check_end_diversion(input_iterator
*t
);
447 static int div_level
;
448 static statem
*diversion_state
;
450 static input_iterator
*top
;
452 static int finish_get(node
**);
453 static int finish_peek();
456 input_iterator
*input_stack::top
= &nil_iterator
;
457 int input_stack::level
= 0;
458 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
459 int input_stack::div_level
= 0;
460 statem
*input_stack::diversion_state
= NULL
;
464 inline int input_stack::get_level()
469 inline void input_stack::increase_level()
474 inline void input_stack::decrease_level()
479 inline int input_stack::get_div_level()
484 inline int input_stack::get(node
**np
)
486 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
488 old_have_input
= have_input
;
494 int input_stack::finish_get(node
**np
)
497 int c
= top
->fill(np
);
498 if (c
!= EOF
|| top
->is_boundary())
500 if (top
== &nil_iterator
)
502 input_iterator
*tem
= top
;
503 check_end_diversion(tem
);
504 #if defined(DEBUGGING)
506 if (tem
->is_diversion
)
508 "in diversion level = %d\n", input_stack::get_div_level());
513 if (top
->ptr
< top
->eptr
)
520 inline int input_stack::peek()
522 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
525 void input_stack::check_end_diversion(input_iterator
*t
)
527 if (t
->is_diversion
) {
529 diversion_state
= t
->diversion_state
;
533 int input_stack::finish_peek()
537 if (c
!= EOF
|| top
->is_boundary())
539 if (top
== &nil_iterator
)
541 input_iterator
*tem
= top
;
542 check_end_diversion(tem
);
546 if (top
->ptr
< top
->eptr
)
553 void input_stack::add_boundary()
555 push(new input_boundary
);
558 void input_stack::add_return_boundary()
560 push(new input_return_boundary
);
563 int input_stack::is_return_boundary()
565 return top
->is_boundary() == 2;
568 void input_stack::remove_boundary()
570 assert(top
->is_boundary());
571 input_iterator
*temp
= top
->next
;
572 check_end_diversion(top
);
579 void input_stack::push(input_iterator
*in
)
583 if (++level
> limit
&& limit
> 0)
584 fatal("input stack limit exceeded (probable infinite loop)");
587 if (top
->is_diversion
) {
589 in
->diversion_state
= diversion_state
;
590 diversion_state
= curenv
->construct_state(0);
591 #if defined(DEBUGGING)
593 curenv
->dump_troff_state();
598 #if defined(DEBUGGING)
600 if (top
->is_diversion
) {
602 "in diversion level = %d\n", input_stack::get_div_level());
608 statem
*get_diversion_state()
610 return input_stack::get_diversion_state();
613 statem
*input_stack::get_diversion_state()
615 if (diversion_state
== NULL
)
618 return new statem(diversion_state
);
621 input_iterator
*input_stack::get_arg(int i
)
624 for (p
= top
; p
!= 0; p
= p
->next
)
626 return p
->get_arg(i
);
630 arg_list
*input_stack::get_arg_list()
633 for (p
= top
; p
!= 0; p
= p
->next
)
635 return p
->get_arg_list();
639 symbol
input_stack::get_macro_name()
642 for (p
= top
; p
!= 0; p
= p
->next
)
644 return p
->get_macro_name();
648 int input_stack::space_follows_arg(int i
)
651 for (p
= top
; p
!= 0; p
= p
->next
)
653 return p
->space_follows_arg(i
);
657 int input_stack::get_break_flag()
659 return top
->get_break_flag();
662 void input_stack::shift(int n
)
664 for (input_iterator
*p
= top
; p
; p
= p
->next
)
671 int input_stack::nargs()
673 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
679 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
681 for (input_iterator
*p
= top
; p
; p
= p
->next
)
682 if (p
->get_location(allow_macro
, filenamep
, linenop
))
687 void input_stack::backtrace()
691 // only backtrace down to (not including) the topmost file
692 for (input_iterator
*p
= top
;
693 p
&& !p
->get_location(0, &f
, &n
);
698 void input_stack::backtrace_all()
700 for (input_iterator
*p
= top
; p
; p
= p
->next
)
704 int input_stack::set_location(const char *filename
, int lineno
)
706 for (input_iterator
*p
= top
; p
; p
= p
->next
)
707 if (p
->set_location(filename
, lineno
))
712 void input_stack::next_file(file_case
*fcp
, const char *s
)
715 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
716 if ((*pp
)->next_file(fcp
, s
))
718 if (++level
> limit
&& limit
> 0)
719 fatal("input stack limit exceeded");
720 *pp
= new file_iterator(fcp
, s
);
721 (*pp
)->next
= &nil_iterator
;
724 void input_stack::end_file()
726 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
727 if ((*pp
)->is_file()) {
728 input_iterator
*tem
= *pp
;
729 check_end_diversion(tem
);
737 void input_stack::clear()
740 while (top
!= &nil_iterator
) {
741 if (top
->is_boundary())
743 input_iterator
*tem
= top
;
744 check_end_diversion(tem
);
749 // Keep while_request happy.
750 for (; nboundaries
> 0; --nboundaries
)
751 add_return_boundary();
754 void input_stack::pop_macro()
759 if (top
->next
== &nil_iterator
)
761 if (top
->is_boundary())
763 is_macro
= top
->is_macro();
764 input_iterator
*tem
= top
;
765 check_end_diversion(tem
);
770 // Keep while_request happy.
771 for (; nboundaries
> 0; --nboundaries
)
772 add_return_boundary();
775 inline void input_stack::save_compatible_flag(int f
)
777 top
->save_compatible_flag(f
);
780 inline int input_stack::get_compatible_flag()
782 return top
->get_compatible_flag();
785 void backtrace_request()
787 input_stack::backtrace_all();
794 symbol nm
= get_long_name();
795 while (!tok
.newline() && !tok
.eof())
798 input_stack::end_file();
800 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
803 input_stack::next_file(fcp
, nm
.contents());
805 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
813 if (!has_arg() || !get_integer(&n
))
815 input_stack::shift(n
);
819 static char get_char_for_escape_name(int allow_space
= 0)
821 int c
= get_copy(0, 0, 1);
824 copy_mode_error("end of input in escape name");
827 if (!invalid_input_char(c
))
832 input_stack::push(make_temp_iterator("\n"));
835 if (c
== ' ' && allow_space
)
841 copy_mode_error("%1 is not allowed in an escape name",
842 input_char_description(c
));
848 static symbol
read_two_char_escape_name()
851 buf
[0] = get_char_for_escape_name();
852 if (buf
[0] != '\0') {
853 buf
[1] = get_char_for_escape_name();
862 static symbol
read_long_escape_name(read_mode mode
)
864 int start_level
= input_stack::get_level();
865 char abuf
[ABUF_SIZE
];
867 int buf_size
= ABUF_SIZE
;
872 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
879 if (mode
== WITH_ARGS
&& c
== ' ')
881 if (i
+ 2 > buf_size
) {
883 buf
= new char[ABUF_SIZE
*2];
884 memcpy(buf
, abuf
, buf_size
);
885 buf_size
= ABUF_SIZE
*2;
889 buf
= new char[buf_size
*2];
890 memcpy(buf
, old_buf
, buf_size
);
895 if (c
== ']' && input_stack::get_level() == start_level
)
904 if (mode
!= ALLOW_EMPTY
)
905 copy_mode_error("empty escape name");
917 static symbol
read_escape_name(read_mode mode
)
919 char c
= get_char_for_escape_name();
923 return read_two_char_escape_name();
924 if (c
== '[' && !compatible_flag
)
925 return read_long_escape_name(mode
);
932 static symbol
read_increment_and_escape_name(int *incp
)
934 char c
= get_char_for_escape_name();
941 return read_two_char_escape_name();
944 return read_escape_name();
947 return read_escape_name();
949 if (!compatible_flag
) {
951 return read_long_escape_name();
962 static int get_copy(node
**nd
, int defining
, int handle_escape_E
)
965 int c
= input_stack::get(nd
);
966 if (c
== PUSH_GROFF_MODE
) {
967 input_stack::save_compatible_flag(compatible_flag
);
971 if (c
== PUSH_COMP_MODE
) {
972 input_stack::save_compatible_flag(compatible_flag
);
976 if (c
== POP_GROFFCOMP_MODE
) {
977 compatible_flag
= input_stack::get_compatible_flag();
980 if (c
== BEGIN_QUOTE
) {
981 input_stack::increase_level();
984 if (c
== END_QUOTE
) {
985 input_stack::decrease_level();
988 if (c
== DOUBLE_QUOTE
)
990 if (c
== ESCAPE_E
&& handle_escape_E
)
992 if (c
== ESCAPE_NEWLINE
) {
996 c
= input_stack::get(nd
);
997 } while (c
== ESCAPE_NEWLINE
);
999 if (c
!= escape_char
|| escape_char
<= 0)
1002 c
= input_stack::peek();
1007 (void)input_stack::get(0);
1008 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
1011 case '#': // Like \" but newline is ignored.
1012 (void)input_stack::get(0);
1013 while ((c
= input_stack::get(0)) != '\n')
1019 (void)input_stack::get(0);
1020 symbol s
= read_escape_name();
1021 if (!(s
.is_null() || s
.is_empty()))
1027 (void)input_stack::get(0);
1028 symbol s
= read_escape_name(WITH_ARGS
);
1029 if (!(s
.is_null() || s
.is_empty())) {
1030 if (have_string_arg
) {
1031 have_string_arg
= 0;
1032 interpolate_string_with_args(s
);
1035 interpolate_string(s
);
1040 (void)input_stack::get(0);
1043 (void)input_stack::get(0);
1046 (void)input_stack::get(0);
1047 if (handle_escape_E
)
1052 (void)input_stack::get(0);
1054 symbol s
= read_increment_and_escape_name(&inc
);
1055 if (!(s
.is_null() || s
.is_empty()))
1056 interpolate_number_reg(s
, inc
);
1061 (void)input_stack::get(0);
1062 symbol s
= read_escape_name();
1063 if (!(s
.is_null() || s
.is_empty()))
1064 interpolate_number_format(s
);
1068 (void)input_stack::get(0);
1072 (void)input_stack::get(0);
1073 symbol s
= read_escape_name();
1074 if (!(s
.is_null() || s
.is_empty()))
1075 interpolate_environment_variable(s
);
1079 (void)input_stack::get(0);
1081 return ESCAPE_NEWLINE
;
1084 (void)input_stack::get(0);
1085 return ESCAPE_SPACE
;
1087 (void)input_stack::get(0);
1088 return ESCAPE_TILDE
;
1090 (void)input_stack::get(0);
1091 return ESCAPE_COLON
;
1093 (void)input_stack::get(0);
1096 (void)input_stack::get(0);
1097 return ESCAPE_CIRCUMFLEX
;
1099 (void)input_stack::get(0);
1100 return ESCAPE_LEFT_BRACE
;
1102 (void)input_stack::get(0);
1103 return ESCAPE_RIGHT_BRACE
;
1105 (void)input_stack::get(0);
1106 return ESCAPE_LEFT_QUOTE
;
1108 (void)input_stack::get(0);
1109 return ESCAPE_RIGHT_QUOTE
;
1111 (void)input_stack::get(0);
1112 return ESCAPE_HYPHEN
;
1114 (void)input_stack::get(0);
1115 return ESCAPE_UNDERSCORE
;
1117 (void)input_stack::get(0);
1120 (void)input_stack::get(0);
1123 (void)input_stack::get(0);
1124 return ESCAPE_QUESTION
;
1126 (void)input_stack::get(0);
1127 return ESCAPE_AMPERSAND
;
1129 (void)input_stack::get(0);
1130 return ESCAPE_RIGHT_PARENTHESIS
;
1132 (void)input_stack::get(0);
1135 (void)input_stack::get(0);
1136 return ESCAPE_PERCENT
;
1138 if (c
== escape_char
) {
1139 (void)input_stack::get(0);
1148 class non_interpreted_char_node
: public node
{
1151 non_interpreted_char_node(unsigned char);
1153 int interpret(macro
*);
1160 int non_interpreted_char_node::same(node
*nd
)
1162 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1165 const char *non_interpreted_char_node::type()
1167 return "non_interpreted_char_node";
1170 int non_interpreted_char_node::force_tprint()
1175 int non_interpreted_char_node::is_tag()
1180 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1185 node
*non_interpreted_char_node::copy()
1187 return new non_interpreted_char_node(c
);
1190 int non_interpreted_char_node::interpret(macro
*mac
)
1196 static void do_width();
1197 static node
*do_non_interpreted();
1198 static node
*do_special();
1199 static node
*do_suppress(symbol nm
);
1200 static void do_register();
1202 dictionary
color_dictionary(501);
1204 static color
*lookup_color(symbol nm
)
1206 assert(!nm
.is_null());
1207 if (nm
== default_symbol
)
1208 return &default_color
;
1209 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1211 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1215 void do_glyph_color(symbol nm
)
1220 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1222 color
*tem
= lookup_color(nm
);
1224 curenv
->set_glyph_color(tem
);
1226 (void)color_dictionary
.lookup(nm
, new color(nm
));
1230 void do_fill_color(symbol nm
)
1235 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1237 color
*tem
= lookup_color(nm
);
1239 curenv
->set_fill_color(tem
);
1241 (void)color_dictionary
.lookup(nm
, new color(nm
));
1245 static unsigned int get_color_element(const char *scheme
, const char *col
)
1248 if (!get_number(&val
, 'f')) {
1249 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1254 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1257 if (val
> color::MAX_COLOR_VAL
+1) {
1258 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1259 // we change 0x10000 to 0xffff
1260 return color::MAX_COLOR_VAL
;
1262 return (unsigned int)val
;
1265 static color
*read_rgb(char end
= 0)
1267 symbol component
= do_get_long_name(0, end
);
1268 if (component
.is_null()) {
1269 warning(WARN_COLOR
, "missing rgb color values");
1272 const char *s
= component
.contents();
1273 color
*col
= new color
;
1275 if (!col
->read_rgb(s
)) {
1276 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1283 input_stack::push(make_temp_iterator(" "));
1284 input_stack::push(make_temp_iterator(s
));
1286 unsigned int r
= get_color_element("rgb color", "red component");
1287 unsigned int g
= get_color_element("rgb color", "green component");
1288 unsigned int b
= get_color_element("rgb color", "blue component");
1289 col
->set_rgb(r
, g
, b
);
1294 static color
*read_cmy(char end
= 0)
1296 symbol component
= do_get_long_name(0, end
);
1297 if (component
.is_null()) {
1298 warning(WARN_COLOR
, "missing cmy color values");
1301 const char *s
= component
.contents();
1302 color
*col
= new color
;
1304 if (!col
->read_cmy(s
)) {
1305 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1312 input_stack::push(make_temp_iterator(" "));
1313 input_stack::push(make_temp_iterator(s
));
1315 unsigned int c
= get_color_element("cmy color", "cyan component");
1316 unsigned int m
= get_color_element("cmy color", "magenta component");
1317 unsigned int y
= get_color_element("cmy color", "yellow component");
1318 col
->set_cmy(c
, m
, y
);
1323 static color
*read_cmyk(char end
= 0)
1325 symbol component
= do_get_long_name(0, end
);
1326 if (component
.is_null()) {
1327 warning(WARN_COLOR
, "missing cmyk color values");
1330 const char *s
= component
.contents();
1331 color
*col
= new color
;
1333 if (!col
->read_cmyk(s
)) {
1334 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1341 input_stack::push(make_temp_iterator(" "));
1342 input_stack::push(make_temp_iterator(s
));
1344 unsigned int c
= get_color_element("cmyk color", "cyan component");
1345 unsigned int m
= get_color_element("cmyk color", "magenta component");
1346 unsigned int y
= get_color_element("cmyk color", "yellow component");
1347 unsigned int k
= get_color_element("cmyk color", "black component");
1348 col
->set_cmyk(c
, m
, y
, k
);
1353 static color
*read_gray(char end
= 0)
1355 symbol component
= do_get_long_name(0, end
);
1356 if (component
.is_null()) {
1357 warning(WARN_COLOR
, "missing gray values");
1360 const char *s
= component
.contents();
1361 color
*col
= new color
;
1363 if (!col
->read_gray(s
)) {
1364 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1371 input_stack::push(make_temp_iterator("\n"));
1372 input_stack::push(make_temp_iterator(s
));
1374 unsigned int g
= get_color_element("gray", "gray value");
1380 static void activate_color()
1383 if (has_arg() && get_integer(&n
))
1384 color_flag
= n
!= 0;
1390 static void define_color()
1392 symbol color_name
= get_long_name(1);
1393 if (color_name
.is_null()) {
1397 if (color_name
== default_symbol
) {
1398 warning(WARN_COLOR
, "default color can't be redefined");
1402 symbol style
= get_long_name(1);
1403 if (style
.is_null()) {
1408 if (strcmp(style
.contents(), "rgb") == 0)
1410 else if (strcmp(style
.contents(), "cmyk") == 0)
1412 else if (strcmp(style
.contents(), "gray") == 0)
1414 else if (strcmp(style
.contents(), "grey") == 0)
1416 else if (strcmp(style
.contents(), "cmy") == 0)
1420 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1426 col
->nm
= color_name
;
1427 (void)color_dictionary
.lookup(color_name
, col
);
1432 static node
*do_overstrike()
1435 overstrike_node
*on
= new overstrike_node
;
1436 int start_level
= input_stack::get_level();
1440 if (tok
.newline() || tok
.eof()) {
1441 warning(WARN_DELIM
, "missing closing delimiter");
1442 input_stack::push(make_temp_iterator("\n"));
1446 && (compatible_flag
|| input_stack::get_level() == start_level
))
1448 charinfo
*ci
= tok
.get_char(1);
1450 node
*n
= curenv
->make_char_node(ci
);
1458 static node
*do_bracket()
1461 bracket_node
*bn
= new bracket_node
;
1463 int start_level
= input_stack::get_level();
1467 warning(WARN_DELIM
, "missing closing delimiter");
1470 if (tok
.newline()) {
1471 warning(WARN_DELIM
, "missing closing delimiter");
1472 input_stack::push(make_temp_iterator("\n"));
1476 && (compatible_flag
|| input_stack::get_level() == start_level
))
1478 charinfo
*ci
= tok
.get_char(1);
1480 node
*n
= curenv
->make_char_node(ci
);
1488 static int do_name_test()
1492 int start_level
= input_stack::get_level();
1497 if (tok
.newline() || tok
.eof()) {
1498 warning(WARN_DELIM
, "missing closing delimiter");
1499 input_stack::push(make_temp_iterator("\n"));
1503 && (compatible_flag
|| input_stack::get_level() == start_level
))
1509 return some_char
&& !bad_char
;
1512 static int do_expr_test()
1516 int start_level
= input_stack::get_level();
1517 if (!start
.delimiter(1))
1520 // disable all warning and error messages temporarily
1521 int saved_warning_mask
= warning_mask
;
1522 int saved_inhibit_errors
= inhibit_errors
;
1526 int result
= get_number_rigidly(&dummy
, 'u');
1527 warning_mask
= saved_warning_mask
;
1528 inhibit_errors
= saved_inhibit_errors
;
1529 if (tok
== start
&& input_stack::get_level() == start_level
)
1531 // ignore everything up to the delimiter in case we aren't right there
1534 if (tok
.newline() || tok
.eof()) {
1535 warning(WARN_DELIM
, "missing closing delimiter");
1536 input_stack::push(make_temp_iterator("\n"));
1539 if (tok
== start
&& input_stack::get_level() == start_level
)
1546 static node
*do_zero_width()
1550 int start_level
= input_stack::get_level();
1551 environment
env(curenv
);
1552 environment
*oldenv
= curenv
;
1556 if (tok
.newline() || tok
.eof()) {
1557 error("missing closing delimiter");
1561 && (compatible_flag
|| input_stack::get_level() == start_level
))
1566 node
*rev
= env
.extract_output_line();
1574 return new zero_width_node(n
);
1579 // It's undesirable for \Z to change environments, because then
1580 // \n(.w won't work as expected.
1582 static node
*do_zero_width()
1584 node
*rev
= new dummy_node
;
1587 int start_level
= input_stack::get_level();
1590 if (tok
.newline() || tok
.eof()) {
1591 warning(WARN_DELIM
, "missing closing delimiter");
1592 input_stack::push(make_temp_iterator("\n"));
1596 && (compatible_flag
|| input_stack::get_level() == start_level
))
1598 if (!tok
.add_to_node_list(&rev
))
1599 error("invalid token in argument to \\Z");
1608 return new zero_width_node(n
);
1613 token_node
*node::get_token_node()
1618 class token_node
: public node
{
1621 token_node(const token
&t
);
1623 token_node
*get_token_node();
1630 token_node::token_node(const token
&t
) : tk(t
)
1634 node
*token_node::copy()
1636 return new token_node(tk
);
1639 token_node
*token_node::get_token_node()
1644 int token_node::same(node
*nd
)
1646 return tk
== ((token_node
*)nd
)->tk
;
1649 const char *token_node::type()
1651 return "token_node";
1654 int token_node::force_tprint()
1659 int token_node::is_tag()
1664 token::token() : nd(0), type(TOKEN_EMPTY
)
1673 token::token(const token
&t
)
1674 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1676 // Use two statements to work around bug in SGI C++.
1678 nd
= tem
? tem
->copy() : 0;
1681 void token::operator=(const token
&t
)
1685 // Use two statements to work around bug in SGI C++.
1687 nd
= tem
? tem
->copy() : 0;
1704 return !tok
.newline();
1707 void token::make_space()
1712 void token::make_newline()
1714 type
= TOKEN_NEWLINE
;
1726 int cc
= input_stack::get(&n
);
1727 if (cc
!= escape_char
|| escape_char
== 0) {
1730 case PUSH_GROFF_MODE
:
1731 input_stack::save_compatible_flag(compatible_flag
);
1732 compatible_flag
= 0;
1734 case PUSH_COMP_MODE
:
1735 input_stack::save_compatible_flag(compatible_flag
);
1736 compatible_flag
= 1;
1738 case POP_GROFFCOMP_MODE
:
1739 compatible_flag
= input_stack::get_compatible_flag();
1742 input_stack::increase_level();
1745 input_stack::decrease_level();
1752 case TRANSPARENT_FILE_REQUEST
:
1754 case COPY_FILE_REQUEST
:
1756 case VJUSTIFY_REQUEST
:
1758 type
= TOKEN_REQUEST
;
1762 type
= TOKEN_BEGIN_TRAP
;
1765 type
= TOKEN_END_TRAP
;
1767 case LAST_PAGE_EJECTOR
:
1768 seen_last_page_ejector
= 1;
1771 type
= TOKEN_PAGE_EJECTOR
;
1773 case ESCAPE_PERCENT
:
1775 type
= TOKEN_HYPHEN_INDICATOR
;
1779 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1783 type
= TOKEN_STRETCHABLE_SPACE
;
1787 type
= TOKEN_ZERO_WIDTH_BREAK
;
1791 type
= TOKEN_ESCAPE
;
1794 goto handle_escape_char
;
1798 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1799 curenv
->get_fill_color());
1801 case ESCAPE_CIRCUMFLEX
:
1804 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1805 curenv
->get_fill_color());
1807 case ESCAPE_NEWLINE
:
1810 case ESCAPE_LEFT_BRACE
:
1812 type
= TOKEN_LEFT_BRACE
;
1814 case ESCAPE_RIGHT_BRACE
:
1816 type
= TOKEN_RIGHT_BRACE
;
1818 case ESCAPE_LEFT_QUOTE
:
1820 type
= TOKEN_SPECIAL
;
1823 case ESCAPE_RIGHT_QUOTE
:
1825 type
= TOKEN_SPECIAL
;
1830 type
= TOKEN_SPECIAL
;
1833 case ESCAPE_UNDERSCORE
:
1835 type
= TOKEN_SPECIAL
;
1840 type
= TOKEN_INTERRUPT
;
1844 type
= TOKEN_TRANSPARENT
;
1846 case ESCAPE_QUESTION
:
1848 nd
= do_non_interpreted();
1854 case ESCAPE_AMPERSAND
:
1858 case ESCAPE_RIGHT_PARENTHESIS
:
1859 ESCAPE_RIGHT_PARENTHESIS
:
1860 type
= TOKEN_TRANSPARENT_DUMMY
;
1863 type
= TOKEN_BACKSPACE
;
1872 type
= TOKEN_NEWLINE
;
1875 type
= TOKEN_LEADER
;
1880 token_node
*tn
= n
->get_token_node();
1899 cc
= input_stack::get(&n
);
1902 nm
= read_two_char_escape_name();
1903 type
= TOKEN_SPECIAL
;
1907 error("end of input after escape character");
1910 goto ESCAPE_LEFT_QUOTE
;
1912 goto ESCAPE_RIGHT_QUOTE
;
1916 goto ESCAPE_UNDERSCORE
;
1918 goto ESCAPE_PERCENT
;
1922 nd
= new hmotion_node(curenv
->get_digit_width(),
1923 curenv
->get_fill_color());
1929 goto ESCAPE_CIRCUMFLEX
;
1931 type
= TOKEN_ITALIC_CORRECTION
;
1935 nd
= new left_italic_corrected_node
;
1938 goto ESCAPE_AMPERSAND
;
1940 goto ESCAPE_RIGHT_PARENTHESIS
;
1944 goto ESCAPE_QUESTION
;
1950 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1953 type
= TOKEN_NEWLINE
;
1957 case '#': // Like \" but newline is ignored.
1958 while ((cc
= input_stack::get(0)) != '\n')
1966 symbol s
= read_escape_name();
1967 if (!(s
.is_null() || s
.is_empty()))
1973 symbol s
= read_escape_name(WITH_ARGS
);
1974 if (!(s
.is_null() || s
.is_empty())) {
1975 if (have_string_arg
) {
1976 have_string_arg
= 0;
1977 interpolate_string_with_args(s
);
1980 interpolate_string(s
);
1985 nd
= new non_interpreted_char_node('\001');
1989 c
= '0' + do_name_test();
1997 c
= '0' + do_expr_test();
2003 nm
= get_delim_name();
2006 type
= TOKEN_SPECIAL
;
2010 nd
= new vmotion_node(curenv
->get_size() / 2,
2011 curenv
->get_fill_color());
2014 nd
= read_draw_node();
2022 goto handle_escape_char
;
2025 symbol s
= read_escape_name(ALLOW_EMPTY
);
2029 for (p
= s
.contents(); *p
!= '\0'; p
++)
2032 if (*p
|| s
.is_empty())
2033 curenv
->set_font(s
);
2035 curenv
->set_font(atoi(s
.contents()));
2036 if (!compatible_flag
)
2042 symbol s
= read_escape_name(ALLOW_EMPTY
);
2045 curenv
->set_family(s
);
2051 symbol s
= read_escape_name();
2052 if (!(s
.is_null() || s
.is_empty()))
2053 interpolate_number_format(s
);
2057 if (!get_delim_number(&x
, 'm'))
2060 nd
= new hmotion_node(x
, curenv
->get_fill_color());
2063 // don't take height increments relative to previous height if
2064 // in compatibility mode
2065 if (!compatible_flag
&& curenv
->get_char_height()) {
2066 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
2067 curenv
->set_char_height(x
);
2070 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
2071 curenv
->set_char_height(x
);
2073 if (!compatible_flag
)
2077 nm
= read_escape_name();
2078 if (nm
.is_null() || nm
.is_empty())
2080 type
= TOKEN_MARK_INPUT
;
2086 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
2089 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
2091 node
*char_node
= curenv
->make_char_node(s
);
2093 nd
= new hline_node(x
, char_node
);
2095 nd
= new vline_node(x
, char_node
);
2099 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
2100 if (!compatible_flag
)
2104 do_fill_color(read_escape_name(ALLOW_EMPTY
));
2105 if (!compatible_flag
)
2111 symbol s
= read_increment_and_escape_name(&inc
);
2112 if (!(s
.is_null() || s
.is_empty()))
2113 interpolate_number_reg(s
, inc
);
2117 if (!get_delim_number(&val
, 0))
2120 warning(WARN_CHAR
, "invalid numbered character %1", val
);
2123 type
= TOKEN_NUMBERED_CHAR
;
2126 nd
= do_overstrike();
2130 nd
= do_suppress(read_escape_name());
2136 type
= TOKEN_SPREAD
;
2140 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
2144 if (!compatible_flag
)
2149 curenv
->set_size(x
);
2150 if (!compatible_flag
)
2154 if (get_delim_number(&x
, 0))
2155 curenv
->set_char_slant(x
);
2156 if (!compatible_flag
)
2161 nd
= new non_interpreted_char_node('\t');
2165 nd
= new vmotion_node(-curenv
->get_size() / 2,
2166 curenv
->get_fill_color());
2169 if (!get_delim_number(&x
, 'v'))
2172 nd
= new vmotion_node(x
, curenv
->get_fill_color());
2176 symbol s
= read_escape_name();
2177 if (!(s
.is_null() || s
.is_empty()))
2178 interpolate_environment_variable(s
);
2185 if (!get_delim_number(&x
, 'v'))
2188 nd
= new extra_size_node(x
);
2198 symbol s
= read_escape_name();
2199 if (s
.is_null() || s
.is_empty())
2201 request_or_macro
*p
= lookup_request(s
);
2202 macro
*m
= p
->to_macro();
2204 error("can't transparently throughput a request");
2207 nd
= new special_node(*m
);
2214 if (type
== TOKEN_NODE
)
2215 nd
= new zero_width_node(nd
);
2217 charinfo
*ci
= get_char(1);
2220 node
*gn
= curenv
->make_char_node(ci
);
2223 nd
= new zero_width_node(gn
);
2229 nd
= do_zero_width();
2235 goto ESCAPE_LEFT_BRACE
;
2237 goto ESCAPE_RIGHT_BRACE
;
2241 if (!compatible_flag
) {
2242 symbol s
= read_long_escape_name(WITH_ARGS
);
2243 if (s
.is_null() || s
.is_empty())
2245 if (have_string_arg
) {
2246 have_string_arg
= 0;
2247 nm
= composite_glyph_name(s
);
2250 const char *gn
= check_unicode_name(s
.contents());
2252 const char *gn_decomposed
= decompose_unicode(gn
);
2254 gn
= &gn_decomposed
[1];
2255 const char *groff_gn
= unicode_to_glyph_name(gn
);
2257 nm
= symbol(groff_gn
);
2259 char *buf
= new char[strlen(gn
) + 1 + 1];
2267 nm
= symbol(s
.contents());
2269 type
= TOKEN_SPECIAL
;
2272 goto handle_normal_char
;
2274 if (cc
!= escape_char
&& cc
!= '.')
2275 warning(WARN_ESCAPE
, "escape character ignored before %1",
2276 input_char_description(cc
));
2277 goto handle_normal_char
;
2283 int token::operator==(const token
&t
)
2292 case TOKEN_NUMBERED_CHAR
:
2293 return val
== t
.val
;
2299 int token::operator!=(const token
&t
)
2301 return !(*this == t
);
2304 // is token a suitable delimiter (like ')?
2306 int token::delimiter(int err
)
2335 error("cannot use character `%1' as a starting delimiter", char(c
));
2342 case TOKEN_STRETCHABLE_SPACE
:
2343 case TOKEN_UNSTRETCHABLE_SPACE
:
2347 error("cannot use %1 as a starting delimiter", description());
2354 const char *token::description()
2358 case TOKEN_BACKSPACE
:
2359 return "a backspace character";
2370 case TOKEN_HYPHEN_INDICATOR
:
2372 case TOKEN_INTERRUPT
:
2374 case TOKEN_ITALIC_CORRECTION
:
2377 return "a leader character";
2378 case TOKEN_LEFT_BRACE
:
2380 case TOKEN_MARK_INPUT
:
2386 case TOKEN_NUMBERED_CHAR
:
2388 case TOKEN_RIGHT_BRACE
:
2393 return "a special character";
2396 case TOKEN_STRETCHABLE_SPACE
:
2398 case TOKEN_UNSTRETCHABLE_SPACE
:
2401 return "a tab character";
2402 case TOKEN_TRANSPARENT
:
2404 case TOKEN_TRANSPARENT_DUMMY
:
2406 case TOKEN_ZERO_WIDTH_BREAK
:
2409 return "end of input";
2413 return "a magic token";
2418 while (!tok
.newline())
2429 if (has_arg() && get_integer(&n
))
2430 compatible_flag
= n
!= 0;
2432 compatible_flag
= 1;
2436 static void empty_name_warning(int required
)
2438 if (tok
.newline() || tok
.eof()) {
2440 warning(WARN_MISSING
, "missing name");
2442 else if (tok
.right_brace() || tok
.tab()) {
2443 const char *start
= tok
.description();
2446 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2447 if (!tok
.newline() && !tok
.eof())
2448 error("%1 is not allowed before an argument", start
);
2450 warning(WARN_MISSING
, "missing name");
2453 error("name expected (got %1)", tok
.description());
2455 error("name expected (got %1): treated as missing", tok
.description());
2458 static void non_empty_name_warning()
2460 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2461 && !tok
.right_brace()
2462 // We don't want to give a warning for .el\{
2463 && !tok
.left_brace())
2464 error("%1 is not allowed in a name", tok
.description());
2467 symbol
get_name(int required
)
2469 if (compatible_flag
) {
2472 if ((buf
[0] = tok
.ch()) != 0) {
2474 if ((buf
[1] = tok
.ch()) != 0) {
2479 non_empty_name_warning();
2483 empty_name_warning(required
);
2488 return get_long_name(required
);
2491 symbol
get_long_name(int required
)
2493 return do_get_long_name(required
, 0);
2496 static symbol
do_get_long_name(int required
, char end
)
2500 char abuf
[ABUF_SIZE
];
2502 int buf_size
= ABUF_SIZE
;
2505 // If end != 0 we normally have to append a null byte
2506 if (i
+ 2 > buf_size
) {
2508 buf
= new char[ABUF_SIZE
*2];
2509 memcpy(buf
, abuf
, buf_size
);
2510 buf_size
= ABUF_SIZE
*2;
2513 char *old_buf
= buf
;
2514 buf
= new char[buf_size
*2];
2515 memcpy(buf
, old_buf
, buf_size
);
2520 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2526 empty_name_warning(required
);
2529 if (end
&& buf
[i
] == end
)
2532 non_empty_name_warning();
2545 topdiv
->set_last_page();
2546 if (!end_macro_name
.is_null()) {
2547 spring_trap(end_macro_name
);
2549 process_input_stack();
2551 curenv
->final_break();
2553 process_input_stack();
2555 if (topdiv
->get_page_length() > 0) {
2557 topdiv
->set_ejecting();
2558 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2559 input_stack::push(make_temp_iterator((char *)buf
));
2560 topdiv
->space(topdiv
->get_page_length(), 1);
2562 process_input_stack();
2563 seen_last_page_ejector
= 1; // should be set already
2564 topdiv
->set_ejecting();
2565 push_page_ejector();
2566 topdiv
->space(topdiv
->get_page_length(), 1);
2568 process_input_stack();
2570 // This will only happen if a trap-invoked macro starts a diversion,
2571 // or if vertical position traps have been disabled.
2572 cleanup_and_exit(0);
2575 // This implements .ex. The input stack must be cleared before calling
2580 input_stack::clear();
2587 void return_macro_request()
2589 if (has_arg() && tok
.ch())
2590 input_stack::pop_macro();
2591 input_stack::pop_macro();
2597 end_macro_name
= get_name();
2601 void blank_line_macro()
2603 blank_line_macro_name
= get_name();
2607 static void trapping_blank_line()
2609 if (!blank_line_macro_name
.is_null())
2610 spring_trap(blank_line_macro_name
);
2617 int old_compatible_flag
= compatible_flag
;
2618 compatible_flag
= 0;
2619 symbol nm
= get_name();
2623 interpolate_macro(nm
, 1);
2624 compatible_flag
= old_compatible_flag
;
2625 request_or_macro
*p
= lookup_request(nm
);
2626 macro
*m
= p
->to_macro();
2631 inline int possibly_handle_first_page_transition()
2633 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2634 handle_first_page_transition();
2641 static int transparent_translate(int cc
)
2643 if (!invalid_input_char(cc
)) {
2644 charinfo
*ci
= charset_table
[cc
];
2645 switch (ci
->get_special_translation(1)) {
2646 case charinfo::TRANSLATE_SPACE
:
2648 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2649 return ESCAPE_TILDE
;
2650 case charinfo::TRANSLATE_DUMMY
:
2651 return ESCAPE_AMPERSAND
;
2652 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2653 return ESCAPE_PERCENT
;
2655 // This is really ugly.
2656 ci
= ci
->get_translation(1);
2658 int c
= ci
->get_ascii_code();
2661 error("can't translate %1 to special character `%2'"
2662 " in transparent throughput",
2663 input_char_description(cc
),
2671 struct int_stack_element
{
2673 int_stack_element
*next
;
2683 int_stack::int_stack()
2688 int_stack::~int_stack()
2691 int_stack_element
*temp
= top
;
2697 int int_stack::is_empty()
2702 void int_stack::push(int n
)
2704 int_stack_element
*p
= new int_stack_element
;
2710 int int_stack::pop()
2713 int_stack_element
*p
= top
;
2720 int node::reread(int *)
2725 int global_diverted_space
= 0;
2727 int diverted_space_node::reread(int *bolp
)
2729 global_diverted_space
= 1;
2730 if (curenv
->get_fill())
2731 trapping_blank_line();
2734 global_diverted_space
= 0;
2739 int diverted_copy_file_node::reread(int *bolp
)
2741 curdiv
->copy_file(filename
.contents());
2746 int word_space_node::reread(int *)
2749 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2750 curenv
->space(w
->width
, w
->sentence_width
);
2757 int unbreakable_space_node::reread(int *)
2762 int hmotion_node::reread(int *)
2764 if (unformat
&& was_tab
) {
2765 curenv
->handle_tab(0);
2772 void process_input_stack()
2774 int_stack trap_bol_stack
;
2777 int suppress_next
= 0;
2779 case token::TOKEN_CHAR
:
2781 unsigned char ch
= tok
.c
;
2782 if (bol
&& !have_input
2783 && (ch
== curenv
->control_char
2784 || ch
== curenv
->no_break_control_char
)) {
2785 break_flag
= ch
== curenv
->control_char
;
2786 // skip tabs as well as spaces here
2789 } while (tok
.white_space());
2790 symbol nm
= get_name();
2791 #if defined(DEBUGGING)
2793 if (! nm
.is_null()) {
2794 if (strcmp(nm
.contents(), "test") == 0) {
2795 fprintf(stderr
, "found it!\n");
2798 fprintf(stderr
, "interpreting [%s]", nm
.contents());
2799 if (strcmp(nm
.contents(), "di") == 0 && topdiv
!= curdiv
)
2800 fprintf(stderr
, " currently in diversion: %s",
2801 curdiv
->get_diversion_name());
2802 fprintf(stderr
, "\n");
2810 interpolate_macro(nm
);
2811 #if defined(DEBUGGING)
2813 fprintf(stderr
, "finished interpreting [%s] and environment state is\n", nm
.contents());
2814 curenv
->dump_troff_state();
2821 if (possibly_handle_first_page_transition())
2825 #if defined(DEBUGGING)
2827 fprintf(stderr
, "found [%c]\n", ch
); fflush(stderr
);
2830 curenv
->add_char(charset_table
[ch
]);
2832 if (tok
.type
!= token::TOKEN_CHAR
)
2842 case token::TOKEN_TRANSPARENT
:
2845 if (possibly_handle_first_page_transition())
2854 curdiv
->transparent_output(transparent_translate(cc
));
2856 curdiv
->transparent_output(n
);
2858 } while (cc
!= '\n' && cc
!= EOF
);
2860 curdiv
->transparent_output('\n');
2865 case token::TOKEN_NEWLINE
:
2867 if (bol
&& !old_have_input
2868 && !curenv
->get_prev_line_interrupted())
2869 trapping_blank_line();
2876 case token::TOKEN_REQUEST
:
2878 int request_code
= tok
.c
;
2880 switch (request_code
) {
2884 case COPY_FILE_REQUEST
:
2887 case TRANSPARENT_FILE_REQUEST
:
2891 case VJUSTIFY_REQUEST
:
2902 case token::TOKEN_SPACE
:
2904 if (possibly_handle_first_page_transition())
2906 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2908 // save space_width now so that it isn't changed by \f or \s
2909 // which we wouldn't notice here
2910 hunits space_width
= curenv
->get_space_width();
2912 nspaces
+= tok
.nspaces();
2914 } while (tok
.space());
2916 trapping_blank_line();
2920 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2921 curenv
->get_fill_color()));
2931 case token::TOKEN_EOF
:
2933 case token::TOKEN_NODE
:
2935 if (possibly_handle_first_page_transition())
2937 else if (tok
.nd
->reread(&bol
)) {
2942 curenv
->add_node(tok
.nd
);
2945 curenv
->possibly_break_line(1);
2949 case token::TOKEN_PAGE_EJECTOR
:
2951 continue_page_eject();
2952 // I think we just want to preserve bol.
2956 case token::TOKEN_BEGIN_TRAP
:
2958 trap_bol_stack
.push(bol
);
2963 case token::TOKEN_END_TRAP
:
2965 if (trap_bol_stack
.is_empty())
2966 error("spurious end trap token detected!");
2968 bol
= trap_bol_stack
.pop();
2971 /* I'm not totally happy about this. But I can't think of any other
2972 way to do it. Doing an output_pending_lines() whenever a
2973 TOKEN_END_TRAP is detected doesn't work: for example,
2986 a\%very\%very\%long\%word
2988 will print all but the first lines from the word immediately
2989 after the footer, rather than on the next page. */
2991 if (trap_bol_stack
.is_empty())
2992 curenv
->output_pending_lines();
3004 trap_sprung_flag
= 0;
3008 #ifdef WIDOW_CONTROL
3010 void flush_pending_lines()
3012 while (!tok
.newline() && !tok
.eof())
3014 curenv
->output_pending_lines();
3018 #endif /* WIDOW_CONTROL */
3020 request_or_macro::request_or_macro()
3024 macro
*request_or_macro::to_macro()
3029 request::request(REQUEST_FUNCP pp
) : p(pp
)
3033 void request::invoke(symbol
, int)
3039 enum { SIZE
= 128 };
3040 unsigned char s
[SIZE
];
3045 char_block::char_block()
3054 void append(unsigned char);
3055 void set(unsigned char, int);
3056 unsigned char get(int);
3063 friend class macro_header
;
3064 friend class string_iterator
;
3067 char_list::char_list()
3068 : ptr(0), len(0), head(0), tail(0)
3072 char_list::~char_list()
3075 char_block
*tem
= head
;
3081 int char_list::length()
3086 void char_list::append(unsigned char c
)
3089 head
= tail
= new char_block
;
3093 if (ptr
>= tail
->s
+ char_block::SIZE
) {
3094 tail
->next
= new char_block
;
3103 void char_list::set(unsigned char c
, int offset
)
3105 assert(len
> offset
);
3106 // optimization for access at the end
3107 int boundary
= len
- len
% char_block::SIZE
;
3108 if (offset
>= boundary
) {
3109 *(tail
->s
+ offset
- boundary
) = c
;
3112 char_block
*tem
= head
;
3115 l
+= char_block::SIZE
;
3117 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
3124 unsigned char char_list::get(int offset
)
3126 assert(len
> offset
);
3127 // optimization for access at the end
3128 int boundary
= len
- len
% char_block::SIZE
;
3129 if (offset
>= boundary
)
3130 return *(tail
->s
+ offset
- boundary
);
3131 char_block
*tem
= head
;
3134 l
+= char_block::SIZE
;
3136 return *(tem
->s
+ offset
% char_block::SIZE
);
3147 void append(node
*);
3151 friend class macro_header
;
3152 friend class string_iterator
;
3155 void node_list::append(node
*n
)
3163 tail
= tail
->next
= n
;
3167 int node_list::length()
3170 for (node
*n
= head
; n
!= 0; n
= n
->next
)
3175 node_list::node_list()
3180 node
*node_list::extract()
3187 node_list::~node_list()
3189 delete_node_list(head
);
3192 class macro_header
{
3197 macro_header() { count
= 1; }
3198 macro_header
*copy(int);
3203 if (p
!= 0 && --(p
->count
) <= 0)
3208 : is_a_diversion(0), is_a_string(1)
3210 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3219 macro::macro(const macro
&m
)
3220 : filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
3221 empty_macro(m
.empty_macro
), is_a_diversion(m
.is_a_diversion
),
3222 is_a_string(m
.is_a_string
), p(m
.p
)
3228 macro::macro(int is_div
)
3229 : is_a_diversion(is_div
)
3231 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3241 int macro::is_diversion()
3243 return is_a_diversion
;
3246 int macro::is_string()
3251 void macro::clear_string_flag()
3256 macro
¯o::operator=(const macro
&m
)
3258 // don't assign object
3261 if (p
!= 0 && --(p
->count
) <= 0)
3264 filename
= m
.filename
;
3267 empty_macro
= m
.empty_macro
;
3268 is_a_diversion
= m
.is_a_diversion
;
3269 is_a_string
= m
.is_a_string
;
3273 void macro::append(unsigned char c
)
3277 p
= new macro_header
;
3278 if (p
->cl
.length() != len
) {
3279 macro_header
*tem
= p
->copy(len
);
3280 if (--(p
->count
) <= 0)
3286 if (c
!= PUSH_GROFF_MODE
&& c
!= PUSH_COMP_MODE
&& c
!= POP_GROFFCOMP_MODE
)
3290 void macro::set(unsigned char c
, int offset
)
3294 p
->cl
.set(c
, offset
);
3297 unsigned char macro::get(int offset
)
3300 return p
->cl
.get(offset
);
3308 void macro::append_str(const char *s
)
3313 while (s
[i
] != (char)0) {
3320 void macro::append(node
*n
)
3324 p
= new macro_header
;
3325 if (p
->cl
.length() != len
) {
3326 macro_header
*tem
= p
->copy(len
);
3327 if (--(p
->count
) <= 0)
3337 void macro::append_unsigned(unsigned int i
)
3339 unsigned int j
= i
/ 10;
3342 append(((unsigned char)(((int)'0') + i
% 10)));
3345 void macro::append_int(int i
)
3351 append_unsigned((unsigned int)i
);
3354 void macro::print_size()
3356 errprint("%1", len
);
3359 // make a copy of the first n bytes
3361 macro_header
*macro_header::copy(int n
)
3363 macro_header
*p
= new macro_header
;
3364 char_block
*bp
= cl
.head
;
3365 unsigned char *ptr
= bp
->s
;
3368 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3372 unsigned char c
= *ptr
++;
3375 p
->nl
.append(nd
->copy());
3384 object_dictionary_iterator
iter(request_dictionary
);
3385 request_or_macro
*rm
;
3387 while (iter
.get(&s
, (object
**)&rm
)) {
3388 assert(!s
.is_null());
3389 macro
*m
= rm
->to_macro();
3391 errprint("%1\t", s
.contents());
3400 class string_iterator
: public input_iterator
{
3402 const char *how_invoked
;
3406 int count
; // of characters remaining
3408 int saved_compatible_flag
;
3409 int with_break
; // inherited from the caller
3414 string_iterator(const macro
&, const char * = 0, symbol
= NULL_SYMBOL
);
3417 int get_location(int, const char **, int *);
3419 int get_break_flag() { return with_break
; }
3420 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3421 int get_compatible_flag() { return saved_compatible_flag
; }
3425 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3426 : input_iterator(m
.is_a_diversion
), mac(m
), how_invoked(p
), newline_flag(0),
3431 bp
= mac
.p
->cl
.head
;
3432 nd
= mac
.p
->nl
.head
;
3440 with_break
= input_stack::get_break_flag();
3443 string_iterator::string_iterator()
3452 with_break
= input_stack::get_break_flag();
3455 int string_iterator::is_diversion()
3457 return mac
.is_diversion();
3460 int string_iterator::fill(node
**np
)
3467 const unsigned char *p
= eptr
;
3468 if (p
>= bp
->s
+ char_block::SIZE
) {
3476 (*np
)->div_nest_level
= input_stack::get_div_level();
3478 (*np
)->div_nest_level
= 0;
3485 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3490 unsigned char c
= *p
;
3491 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3505 int string_iterator::peek()
3509 const unsigned char *p
= eptr
;
3510 if (p
>= bp
->s
+ char_block::SIZE
) {
3516 int string_iterator::get_location(int allow_macro
,
3517 const char **filep
, int *linep
)
3521 if (mac
.filename
== 0)
3523 *filep
= mac
.filename
;
3524 *linep
= mac
.lineno
+ lineno
- 1;
3528 void string_iterator::backtrace()
3531 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3534 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3536 errprint(": %1\n", how_invoked
);
3543 class temp_iterator
: public input_iterator
{
3544 unsigned char *base
;
3545 temp_iterator(const char *, int len
);
3548 friend input_iterator
*make_temp_iterator(const char *);
3554 temp_iterator::temp_iterator(const char *s
, int len
)
3556 base
= new unsigned char[len
];
3557 memcpy(base
, s
, len
);
3562 temp_iterator::~temp_iterator()
3567 class small_temp_iterator
: public input_iterator
{
3569 small_temp_iterator(const char *, int);
3570 ~small_temp_iterator();
3571 enum { BLOCK
= 16 };
3572 static small_temp_iterator
*free_list
;
3573 void *operator new(size_t);
3574 void operator delete(void *);
3576 unsigned char buf
[SIZE
];
3577 friend input_iterator
*make_temp_iterator(const char *);
3580 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3582 void *small_temp_iterator::operator new(size_t n
)
3584 assert(n
== sizeof(small_temp_iterator
));
3587 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3588 for (int i
= 0; i
< BLOCK
- 1; i
++)
3589 free_list
[i
].next
= free_list
+ i
+ 1;
3590 free_list
[BLOCK
-1].next
= 0;
3592 small_temp_iterator
*p
= free_list
;
3593 free_list
= (small_temp_iterator
*)(free_list
->next
);
3601 void small_temp_iterator::operator delete(void *p
)
3604 ((small_temp_iterator
*)p
)->next
= free_list
;
3605 free_list
= (small_temp_iterator
*)p
;
3609 small_temp_iterator::~small_temp_iterator()
3616 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3618 for (int i
= 0; i
< len
; i
++)
3624 input_iterator
*make_temp_iterator(const char *s
)
3627 return new small_temp_iterator(s
, 0);
3630 if (n
<= small_temp_iterator::SIZE
)
3631 return new small_temp_iterator(s
, n
);
3633 return new temp_iterator(s
, n
);
3637 // this is used when macros with arguments are interpolated
3643 arg_list(const macro
&, int);
3644 arg_list(const arg_list
*);
3648 arg_list::arg_list(const macro
&m
, int s
) : mac(m
), space_follows(s
), next(0)
3652 arg_list::arg_list(const arg_list
*al
)
3656 space_follows
= al
->space_follows
;
3657 arg_list
**a
= &next
;
3658 arg_list
*p
= al
->next
;
3660 *a
= new arg_list(p
->mac
, p
->space_follows
);
3666 arg_list::~arg_list()
3670 class macro_iterator
: public string_iterator
{
3673 int with_break
; // whether called as .foo or 'foo
3675 macro_iterator(symbol
, macro
&, const char * = "macro", int = 0);
3678 int has_args() { return 1; }
3679 input_iterator
*get_arg(int);
3680 arg_list
*get_arg_list();
3681 symbol
get_macro_name();
3682 int space_follows_arg(int);
3683 int get_break_flag() { return with_break
; }
3684 int nargs() { return argc
; }
3685 void add_arg(const macro
&, int);
3687 int is_macro() { return 1; }
3691 input_iterator
*macro_iterator::get_arg(int i
)
3694 return make_temp_iterator(nm
.contents());
3695 if (i
> 0 && i
<= argc
) {
3697 for (int j
= 1; j
< i
; j
++) {
3701 return new string_iterator(p
->mac
);
3707 arg_list
*macro_iterator::get_arg_list()
3712 symbol
macro_iterator::get_macro_name()
3717 int macro_iterator::space_follows_arg(int i
)
3719 if (i
> 0 && i
<= argc
) {
3721 for (int j
= 1; j
< i
; j
++) {
3725 return p
->space_follows
;
3731 void macro_iterator::add_arg(const macro
&m
, int s
)
3734 for (p
= &args
; *p
; p
= &((*p
)->next
))
3736 *p
= new arg_list(m
, s
);
3740 void macro_iterator::shift(int n
)
3742 while (n
> 0 && argc
> 0) {
3743 arg_list
*tem
= args
;
3751 // This gets used by eg .if '\?xxx\?''.
3753 int operator==(const macro
&m1
, const macro
&m2
)
3755 if (m1
.len
!= m2
.len
)
3757 string_iterator
iter1(m1
);
3758 string_iterator
iter2(m2
);
3762 int c1
= iter1
.get(&nd1
);
3765 int c2
= iter2
.get(&nd2
);
3777 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3787 static void interpolate_macro(symbol nm
, int no_next
)
3789 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3792 const char *s
= nm
.contents();
3793 if (strlen(s
) > 2) {
3794 request_or_macro
*r
;
3799 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3801 macro
*m
= r
->to_macro();
3802 if (!m
|| !m
->empty())
3803 warned
= warning(WARN_SPACE
,
3804 "macro `%1' not defined "
3805 "(possibly missing space after `%2')",
3806 nm
.contents(), buf
);
3810 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3812 request_dictionary
.define(nm
, p
);
3816 p
->invoke(nm
, no_next
);
3823 static void decode_args(macro_iterator
*mi
)
3825 if (!tok
.newline() && !tok
.eof()) {
3827 int c
= get_copy(&n
);
3831 if (c
== '\n' || c
== EOF
)
3834 int quote_input_level
= 0;
3835 int done_tab_warning
= 0;
3836 arg
.append(compatible_flag
? PUSH_COMP_MODE
: PUSH_GROFF_MODE
);
3837 // we store discarded double quotes for \$^
3839 arg
.append(DOUBLE_QUOTE
);
3840 quote_input_level
= input_stack::get_level();
3843 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3844 if (quote_input_level
> 0 && c
== '"'
3846 || input_stack::get_level() == quote_input_level
)) {
3847 arg
.append(DOUBLE_QUOTE
);
3860 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3861 warning(WARN_TAB
, "tab character in unquoted macro argument");
3862 done_tab_warning
= 1;
3869 arg
.append(POP_GROFFCOMP_MODE
);
3870 mi
->add_arg(arg
, (c
== ' '));
3875 static void decode_string_args(macro_iterator
*mi
)
3878 int c
= get_copy(&n
);
3882 if (c
== '\n' || c
== EOF
) {
3883 error("missing `]'");
3889 int quote_input_level
= 0;
3890 int done_tab_warning
= 0;
3892 quote_input_level
= input_stack::get_level();
3895 while (c
!= EOF
&& c
!= '\n'
3896 && !(c
== ']' && quote_input_level
== 0)
3897 && !(c
== ' ' && quote_input_level
== 0)) {
3898 if (quote_input_level
> 0 && c
== '"'
3899 && input_stack::get_level() == quote_input_level
) {
3912 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3913 warning(WARN_TAB
, "tab character in unquoted string argument");
3914 done_tab_warning
= 1;
3921 mi
->add_arg(arg
, (c
== ' '));
3925 void macro::invoke(symbol nm
, int no_next
)
3927 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3929 input_stack::push(mi
);
3930 // we must delay tok.next() in case the function has been called by
3931 // do_request to assure proper handling of compatible_flag
3936 macro
*macro::to_macro()
3943 return empty_macro
== 1;
3946 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_called
,
3948 : string_iterator(m
, how_called
, s
), args(0), argc(0), with_break(break_flag
)
3951 arg_list
*al
= input_stack::get_arg_list();
3953 args
= new arg_list(al
);
3954 argc
= input_stack::nargs();
3959 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag
)
3963 macro_iterator::~macro_iterator()
3966 arg_list
*tem
= args
;
3972 dictionary
composite_dictionary(17);
3974 void composite_request()
3976 symbol from
= get_name(1);
3977 if (!from
.is_null()) {
3978 const char *from_gn
= glyph_name_to_unicode(from
.contents());
3980 from_gn
= check_unicode_name(from
.contents());
3982 error("invalid composite glyph name `%1'", from
.contents());
3987 const char *from_decomposed
= decompose_unicode(from_gn
);
3988 if (from_decomposed
)
3989 from_gn
= &from_decomposed
[1];
3990 symbol to
= get_name(1);
3992 composite_dictionary
.remove(symbol(from_gn
));
3994 const char *to_gn
= glyph_name_to_unicode(to
.contents());
3996 to_gn
= check_unicode_name(to
.contents());
3998 error("invalid composite glyph name `%1'", to
.contents());
4003 const char *to_decomposed
= decompose_unicode(to_gn
);
4005 to_gn
= &to_decomposed
[1];
4006 if (strcmp(from_gn
, to_gn
) == 0)
4007 composite_dictionary
.remove(symbol(from_gn
));
4009 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
4015 static symbol
composite_glyph_name(symbol nm
)
4017 macro_iterator
*mi
= new macro_iterator();
4018 decode_string_args(mi
);
4019 input_stack::push(mi
);
4020 const char *gn
= glyph_name_to_unicode(nm
.contents());
4022 gn
= check_unicode_name(nm
.contents());
4024 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
4025 return EMPTY_SYMBOL
;
4028 const char *gn_decomposed
= decompose_unicode(gn
);
4029 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
4031 int n
= input_stack::nargs();
4032 for (int i
= 1; i
<= n
; i
++) {
4034 input_iterator
*p
= input_stack::get_arg(i
);
4037 while ((c
= p
->get(0)) != EOF
)
4038 if (c
!= DOUBLE_QUOTE
)
4041 const char *u
= glyph_name_to_unicode(gl
.contents());
4043 u
= check_unicode_name(gl
.contents());
4045 error("invalid component `%1' in composite glyph name",
4047 return EMPTY_SYMBOL
;
4050 const char *decomposed
= decompose_unicode(u
);
4053 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
4054 if (mapped_composite
)
4055 u
= (const char *)mapped_composite
;
4059 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
4061 return symbol(groff_gn
);
4065 return symbol(gl
.contents());
4068 int trap_sprung_flag
= 0;
4069 int postpone_traps_flag
= 0;
4070 symbol postponed_trap
;
4072 void spring_trap(symbol nm
)
4074 assert(!nm
.is_null());
4075 trap_sprung_flag
= 1;
4076 if (postpone_traps_flag
) {
4077 postponed_trap
= nm
;
4080 static char buf
[2] = { BEGIN_TRAP
, 0 };
4081 static char buf2
[2] = { END_TRAP
, '\0' };
4082 input_stack::push(make_temp_iterator(buf2
));
4083 request_or_macro
*p
= lookup_request(nm
);
4084 macro
*m
= p
->to_macro();
4086 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
4088 error("you can't invoke a request with a trap");
4089 input_stack::push(make_temp_iterator(buf
));
4092 void postpone_traps()
4094 postpone_traps_flag
= 1;
4097 int unpostpone_traps()
4099 postpone_traps_flag
= 0;
4100 if (!postponed_trap
.is_null()) {
4101 spring_trap(postponed_trap
);
4102 postponed_trap
= NULL_SYMBOL
;
4111 macro_iterator
*mi
= new macro_iterator
;
4112 int reading_from_terminal
= isatty(fileno(stdin
));
4114 if (!tok
.newline() && !tok
.eof()) {
4115 int c
= get_copy(0);
4118 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
4119 if (!invalid_input_char(c
)) {
4120 if (reading_from_terminal
)
4131 if (reading_from_terminal
) {
4132 fputc(had_prompt
? ':' : '\a', stderr
);
4135 input_stack::push(mi
);
4139 while ((c
= getchar()) != EOF
) {
4140 if (invalid_input_char(c
))
4141 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
4154 if (reading_from_terminal
)
4156 input_stack::push(new string_iterator(mac
));
4160 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
4161 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
};
4162 enum comp_mode
{ COMP_IGNORE
, COMP_DISABLE
, COMP_ENABLE
};
4164 void do_define_string(define_mode mode
, comp_mode comp
)
4167 node
*n
= 0; // pacify compiler
4178 else if (!tok
.space()) {
4179 error("bad string definition");
4190 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
4191 macro
*mm
= rm
? rm
->to_macro() : 0;
4192 if (mode
== DEFINE_APPEND
&& mm
)
4194 if (comp
== COMP_DISABLE
)
4195 mac
.append(PUSH_GROFF_MODE
);
4196 else if (comp
== COMP_ENABLE
)
4197 mac
.append(PUSH_COMP_MODE
);
4198 while (c
!= '\n' && c
!= EOF
) {
4202 mac
.append((unsigned char)c
);
4205 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4206 mac
.append(POP_GROFFCOMP_MODE
);
4209 request_dictionary
.define(nm
, mm
);
4215 void define_string()
4217 do_define_string(DEFINE_NORMAL
,
4218 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4221 void define_nocomp_string()
4223 do_define_string(DEFINE_NORMAL
, COMP_DISABLE
);
4226 void append_string()
4228 do_define_string(DEFINE_APPEND
,
4229 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4232 void append_nocomp_string()
4234 do_define_string(DEFINE_APPEND
, COMP_DISABLE
);
4237 void do_define_character(char_mode mode
, const char *font_name
)
4239 node
*n
= 0; // pacify compiler
4242 charinfo
*ci
= tok
.get_char(1);
4248 string
s(font_name
);
4250 s
+= ci
->nm
.contents();
4252 ci
= get_charinfo(symbol(s
.contents()));
4259 else if (!tok
.space()) {
4260 error("bad character definition");
4266 while (c
== ' ' || c
== '\t')
4270 macro
*m
= new macro
;
4271 while (c
!= '\n' && c
!= EOF
) {
4275 m
->append((unsigned char)c
);
4278 m
= ci
->setx_macro(m
, mode
);
4284 void define_character()
4286 do_define_character(CHAR_NORMAL
);
4289 void define_fallback_character()
4291 do_define_character(CHAR_FALLBACK
);
4294 void define_special_character()
4296 do_define_character(CHAR_SPECIAL
);
4299 static void remove_character()
4302 while (!tok
.newline() && !tok
.eof()) {
4303 if (!tok
.space() && !tok
.tab()) {
4304 charinfo
*ci
= tok
.get_char(1);
4307 macro
*m
= ci
->set_macro(0);
4316 static void interpolate_string(symbol nm
)
4318 request_or_macro
*p
= lookup_request(nm
);
4319 macro
*m
= p
->to_macro();
4321 error("you can only invoke a string or macro using \\*");
4323 if (m
->is_string()) {
4324 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
4325 input_stack::push(si
);
4328 // if a macro is called as a string, \$0 doesn't get changed
4329 macro_iterator
*mi
= new macro_iterator(input_stack::get_macro_name(),
4331 input_stack::push(mi
);
4336 static void interpolate_string_with_args(symbol s
)
4338 request_or_macro
*p
= lookup_request(s
);
4339 macro
*m
= p
->to_macro();
4341 error("you can only invoke a string or macro using \\*");
4343 macro_iterator
*mi
= new macro_iterator(s
, *m
);
4344 decode_string_args(mi
);
4345 input_stack::push(mi
);
4349 static void interpolate_arg(symbol nm
)
4351 const char *s
= nm
.contents();
4352 if (!s
|| *s
== '\0')
4353 copy_mode_error("missing argument name");
4354 else if (s
[1] == 0 && csdigit(s
[0]))
4355 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4356 else if (s
[0] == '*' && s
[1] == '\0') {
4357 int limit
= input_stack::nargs();
4359 for (int i
= 1; i
<= limit
; i
++) {
4360 input_iterator
*p
= input_stack::get_arg(i
);
4362 while ((c
= p
->get(0)) != EOF
)
4363 if (c
!= DOUBLE_QUOTE
)
4370 input_stack::push(make_temp_iterator(args
.contents()));
4373 else if (s
[0] == '@' && s
[1] == '\0') {
4374 int limit
= input_stack::nargs();
4376 for (int i
= 1; i
<= limit
; i
++) {
4378 args
+= char(BEGIN_QUOTE
);
4379 input_iterator
*p
= input_stack::get_arg(i
);
4381 while ((c
= p
->get(0)) != EOF
)
4382 if (c
!= DOUBLE_QUOTE
)
4384 args
+= char(END_QUOTE
);
4391 input_stack::push(make_temp_iterator(args
.contents()));
4394 else if (s
[0] == '^' && s
[1] == '\0') {
4395 int limit
= input_stack::nargs();
4397 int c
= input_stack::peek();
4398 for (int i
= 1; i
<= limit
; i
++) {
4399 input_iterator
*p
= input_stack::get_arg(i
);
4400 while ((c
= p
->get(0)) != EOF
) {
4401 if (c
== DOUBLE_QUOTE
)
4405 if (input_stack::space_follows_arg(i
))
4410 input_stack::push(make_temp_iterator(args
.contents()));
4415 for (p
= s
; *p
&& csdigit(*p
); p
++)
4418 copy_mode_error("bad argument name `%1'", s
);
4420 input_stack::push(input_stack::get_arg(atoi(s
)));
4424 void handle_first_page_transition()
4427 topdiv
->begin_page();
4430 // We push back a token by wrapping it up in a token_node, and
4431 // wrapping that up in a string_iterator.
4433 static void push_token(const token
&t
)
4436 m
.append(new token_node(t
));
4437 input_stack::push(new string_iterator(m
));
4440 void push_page_ejector()
4442 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4443 input_stack::push(make_temp_iterator(buf
));
4446 void handle_initial_request(unsigned char code
)
4452 mac
.append(new token_node(tok
));
4453 input_stack::push(new string_iterator(mac
));
4454 input_stack::push(make_temp_iterator(buf
));
4455 topdiv
->begin_page();
4459 void handle_initial_title()
4461 handle_initial_request(TITLE_REQUEST
);
4464 // this should be local to define_macro, but cfront 1.2 doesn't support that
4465 static symbol
dot_symbol(".");
4467 void do_define_macro(define_mode mode
, calling_mode calling
, comp_mode comp
)
4470 if (calling
== CALLING_INDIRECT
) {
4471 symbol temp1
= get_name(1);
4472 if (temp1
.is_null()) {
4476 symbol temp2
= get_name();
4477 input_stack::push(make_temp_iterator("\n"));
4478 if (!temp2
.is_null()) {
4479 interpolate_string(temp2
);
4480 input_stack::push(make_temp_iterator(" "));
4482 interpolate_string(temp1
);
4483 input_stack::push(make_temp_iterator(" "));
4486 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4493 term
= get_name(); // the request that terminates the definition
4496 while (!tok
.newline() && !tok
.eof())
4498 const char *start_filename
;
4500 int have_start_location
= input_stack::get_location(0, &start_filename
,
4503 // doing this here makes the line numbers come out right
4504 int c
= get_copy(&n
, 1);
4507 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4508 request_or_macro
*rm
=
4509 (request_or_macro
*)request_dictionary
.lookup(nm
);
4511 mm
= rm
->to_macro();
4512 if (mm
&& mode
== DEFINE_APPEND
)
4516 if (comp
== COMP_DISABLE
)
4517 mac
.append(PUSH_GROFF_MODE
);
4518 else if (comp
== COMP_ENABLE
)
4519 mac
.append(PUSH_COMP_MODE
);
4522 mac
.clear_string_flag();
4523 while (c
== ESCAPE_NEWLINE
) {
4524 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4526 c
= get_copy(&n
, 1);
4528 if (bol
&& c
== '.') {
4529 const char *s
= term
.contents();
4531 // see if it matches term
4534 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4536 if ((unsigned char)s
[0] == d
) {
4537 for (i
= 1; s
[i
] != 0; i
++) {
4539 if ((unsigned char)s
[i
] != d
)
4545 && ((i
== 2 && compatible_flag
)
4546 || (d
= get_copy(&n
)) == ' '
4547 || d
== '\n')) { // we found it
4552 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4555 request_dictionary
.define(nm
, mm
);
4557 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4558 mac
.append(POP_GROFFCOMP_MODE
);
4561 if (term
!= dot_symbol
) {
4563 interpolate_macro(term
);
4569 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4571 for (int j
= 0; j
< i
; j
++)
4577 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4578 if (have_start_location
)
4579 error_with_file_and_line(start_filename
, start_lineno
,
4580 "end of file while defining macro `%1'",
4583 error("end of file while defining macro `%1'", nm
.contents());
4586 if (have_start_location
)
4587 error_with_file_and_line(start_filename
, start_lineno
,
4588 "end of file while ignoring input lines");
4590 error("end of file while ignoring input lines");
4595 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4602 c
= get_copy(&n
, 1);
4608 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
,
4609 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4612 void define_nocomp_macro()
4614 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_DISABLE
);
4617 void define_indirect_macro()
4619 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
,
4620 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4623 void define_indirect_nocomp_macro()
4625 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_DISABLE
);
4630 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
,
4631 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4634 void append_nocomp_macro()
4636 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_DISABLE
);
4639 void append_indirect_macro()
4641 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
,
4642 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4645 void append_indirect_nocomp_macro()
4647 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_DISABLE
);
4653 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
, COMP_IGNORE
);
4660 symbol s
= get_name();
4663 request_dictionary
.remove(s
);
4670 symbol s1
= get_name(1);
4671 if (!s1
.is_null()) {
4672 symbol s2
= get_name(1);
4674 request_dictionary
.rename(s1
, s2
);
4681 symbol s1
= get_name(1);
4682 if (!s1
.is_null()) {
4683 symbol s2
= get_name(1);
4684 if (!s2
.is_null()) {
4685 if (!request_dictionary
.alias(s1
, s2
))
4686 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4694 symbol s
= get_name(1);
4696 request_or_macro
*p
= lookup_request(s
);
4697 macro
*m
= p
->to_macro();
4699 error("cannot chop request");
4700 else if (m
->empty())
4701 error("cannot chop empty macro");
4703 int have_restore
= 0;
4704 // we have to check for additional save/restore pairs which could be
4705 // there due to empty am1 requests.
4707 if (m
->get(m
->len
- 1) != POP_GROFFCOMP_MODE
)
4711 if (m
->get(m
->len
- 1) != PUSH_GROFF_MODE
4712 && m
->get(m
->len
- 1) != PUSH_COMP_MODE
)
4720 error("cannot chop empty macro");
4723 m
->set(POP_GROFFCOMP_MODE
, m
->len
- 1);
4732 void substring_request()
4734 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4735 symbol s
= get_name(1);
4736 if (!s
.is_null() && get_integer(&start
)) {
4737 request_or_macro
*p
= lookup_request(s
);
4738 macro
*m
= p
->to_macro();
4740 error("cannot apply `substring' on a request");
4743 if (!has_arg() || get_integer(&end
)) {
4744 int real_length
= 0; // 1, 2, ..., n
4745 string_iterator
iter1(*m
);
4746 for (int l
= 0; l
< m
->len
; l
++) {
4747 int c
= iter1
.get(0);
4748 if (c
== PUSH_GROFF_MODE
4749 || c
== PUSH_COMP_MODE
4750 || c
== POP_GROFFCOMP_MODE
)
4757 start
+= real_length
;
4765 if (start
>= real_length
|| end
< 0) {
4767 "start and end index of substring out of range");
4770 if (--(m
->p
->count
) <= 0)
4779 "start index of substring out of range, set to 0");
4782 if (end
>= real_length
) {
4784 "end index of substring out of range, set to string length");
4785 end
= real_length
- 1;
4787 // now extract the substring
4788 string_iterator
iter(*m
);
4790 for (i
= 0; i
< start
; i
++) {
4791 int c
= iter
.get(0);
4792 while (c
== PUSH_GROFF_MODE
4793 || c
== PUSH_COMP_MODE
4794 || c
== POP_GROFFCOMP_MODE
)
4800 for (; i
<= end
; i
++) {
4801 node
*nd
= 0; // pacify compiler
4802 int c
= iter
.get(&nd
);
4803 while (c
== PUSH_GROFF_MODE
4804 || c
== PUSH_COMP_MODE
4805 || c
== POP_GROFFCOMP_MODE
)
4812 mac
.append((unsigned char)c
);
4821 void length_request()
4825 if (ret
.is_null()) {
4835 else if (!tok
.space()) {
4836 error("bad string definition");
4847 while (c
!= '\n' && c
!= EOF
) {
4851 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4855 set_number_reg(ret
, len
);
4859 void asciify_macro()
4861 symbol s
= get_name(1);
4863 request_or_macro
*p
= lookup_request(s
);
4864 macro
*m
= p
->to_macro();
4866 error("cannot asciify request");
4869 string_iterator
iter(*m
);
4871 node
*nd
= 0; // pacify compiler
4872 int c
= iter
.get(&nd
);
4886 void unformat_macro()
4888 symbol s
= get_name(1);
4890 request_or_macro
*p
= lookup_request(s
);
4891 macro
*m
= p
->to_macro();
4893 error("cannot unformat request");
4896 string_iterator
iter(*m
);
4898 node
*nd
= 0; // pacify compiler
4899 int c
= iter
.get(&nd
);
4905 if (nd
->set_unformat_flag())
4915 static void interpolate_environment_variable(symbol nm
)
4917 const char *s
= getenv(nm
.contents());
4919 input_stack::push(make_temp_iterator(s
));
4922 void interpolate_number_reg(symbol nm
, int inc
)
4924 reg
*r
= lookup_number_reg(nm
);
4929 input_stack::push(make_temp_iterator(r
->get_string()));
4932 static void interpolate_number_format(symbol nm
)
4934 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4936 input_stack::push(make_temp_iterator(r
->get_format()));
4939 static int get_delim_number(units
*n
, unsigned char si
, int prev_value
)
4943 if (start
.delimiter(1)) {
4945 if (get_number(n
, si
, prev_value
)) {
4947 warning(WARN_DELIM
, "closing delimiter does not match");
4954 static int get_delim_number(units
*n
, unsigned char si
)
4958 if (start
.delimiter(1)) {
4960 if (get_number(n
, si
)) {
4962 warning(WARN_DELIM
, "closing delimiter does not match");
4969 static int get_line_arg(units
*n
, unsigned char si
, charinfo
**cp
)
4973 int start_level
= input_stack::get_level();
4974 if (!start
.delimiter(1))
4977 if (get_number(n
, si
)) {
4978 if (tok
.dummy() || tok
.transparent_dummy())
4980 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4981 *cp
= tok
.get_char(1);
4984 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4985 warning(WARN_DELIM
, "closing delimiter does not match");
4991 static int read_size(int *x
)
5001 else if (c
== '+') {
5006 int val
= 0; // pacify compiler
5012 // allow an increment either before or after the left parenthesis
5018 else if (c
== '+') {
5033 val
= val
*10 + (c
- '0');
5038 else if (csdigit(c
)) {
5040 if (!inc
&& c
!= '0' && c
< '4') {
5046 val
= val
*10 + (c
- '0');
5050 else if (!tok
.delimiter(1))
5056 if (!inc
&& (c
== '-' || c
== '+')) {
5057 inc
= c
== '+' ? 1 : -1;
5060 if (!get_number(&val
, 'z'))
5062 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
5063 if (start
.ch() == '[')
5064 error("missing `]'");
5066 error("missing closing delimiter");
5074 // special case -- \s[0] and \s0 means to revert to previous size
5081 *x
= curenv
->get_requested_point_size() + val
;
5084 *x
= curenv
->get_requested_point_size() - val
;
5091 "\\s escape results in non-positive point size; set to 1");
5097 error("bad digit in point size");
5102 static symbol
get_delim_name()
5107 error("end of input at start of delimited name");
5110 if (start
.newline()) {
5111 error("can't delimit name with a newline");
5114 int start_level
= input_stack::get_level();
5115 char abuf
[ABUF_SIZE
];
5117 int buf_size
= ABUF_SIZE
;
5120 if (i
+ 1 > buf_size
) {
5122 buf
= new char[ABUF_SIZE
*2];
5123 memcpy(buf
, abuf
, buf_size
);
5124 buf_size
= ABUF_SIZE
*2;
5127 char *old_buf
= buf
;
5128 buf
= new char[buf_size
*2];
5129 memcpy(buf
, old_buf
, buf_size
);
5136 && (compatible_flag
|| input_stack::get_level() == start_level
))
5138 if ((buf
[i
] = tok
.ch()) == 0) {
5139 error("missing delimiter (got %1)", tok
.description());
5149 error("empty delimited name");
5164 static void do_register()
5168 if (!start
.delimiter(1))
5171 symbol nm
= get_long_name(1);
5176 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
5178 if (!r
|| !r
->get_value(&prev_value
))
5181 if (!get_number(&val
, 'u', prev_value
))
5184 warning(WARN_DELIM
, "closing delimiter does not match");
5188 set_number_reg(nm
, val
);
5191 // this implements the \w escape sequence
5193 static void do_width()
5197 int start_level
= input_stack::get_level();
5198 environment
env(curenv
);
5199 environment
*oldenv
= curenv
;
5204 warning(WARN_DELIM
, "missing closing delimiter");
5207 if (tok
.newline()) {
5208 warning(WARN_DELIM
, "missing closing delimiter");
5209 input_stack::push(make_temp_iterator("\n"));
5213 && (compatible_flag
|| input_stack::get_level() == start_level
))
5218 units x
= env
.get_input_line_position().to_units();
5219 input_stack::push(make_temp_iterator(i_to_a(x
)));
5220 env
.width_registers();
5225 charinfo
*page_character
;
5227 void set_page_character()
5229 page_character
= get_optional_char();
5233 static const symbol
percent_symbol("%");
5235 void read_title_parts(node
**part
, hunits
*part_width
)
5238 if (tok
.newline() || tok
.eof())
5241 int start_level
= input_stack::get_level();
5243 for (int i
= 0; i
< 3; i
++) {
5244 while (!tok
.newline() && !tok
.eof()) {
5246 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
5250 if (page_character
!= 0 && tok
.get_char() == page_character
)
5251 interpolate_number_reg(percent_symbol
, 0);
5256 curenv
->wrap_up_tab();
5257 part_width
[i
] = curenv
->get_input_line_position();
5258 part
[i
] = curenv
->extract_output_line();
5260 while (!tok
.newline() && !tok
.eof())
5264 class non_interpreted_node
: public node
{
5267 non_interpreted_node(const macro
&);
5268 int interpret(macro
*);
5270 int ends_sentence();
5277 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
5281 int non_interpreted_node::ends_sentence()
5286 int non_interpreted_node::same(node
*nd
)
5288 return mac
== ((non_interpreted_node
*)nd
)->mac
;
5291 const char *non_interpreted_node::type()
5293 return "non_interpreted_node";
5296 int non_interpreted_node::force_tprint()
5301 int non_interpreted_node::is_tag()
5306 node
*non_interpreted_node::copy()
5308 return new non_interpreted_node(mac
);
5311 int non_interpreted_node::interpret(macro
*m
)
5313 string_iterator
si(mac
);
5314 node
*n
= 0; // pacify compiler
5327 static node
*do_non_interpreted()
5332 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
5337 if (c
== EOF
|| c
== '\n') {
5338 error("missing \\?");
5341 return new non_interpreted_node(mac
);
5344 static void encode_char(macro
*mac
, char c
)
5347 if ((font::use_charnames_in_special
) && tok
.special()) {
5348 charinfo
*ci
= tok
.get_char(1);
5349 const char *s
= ci
->get_symbol()->contents();
5350 if (s
[0] != (char)0) {
5354 while (s
[i
] != (char)0) {
5361 else if (tok
.stretchable_space()
5362 || tok
.unstretchable_space())
5364 else if (!(tok
.hyphen_indicator()
5366 || tok
.transparent_dummy()
5367 || tok
.zero_width_break()))
5368 error("%1 is invalid within \\X", tok
.description());
5371 if ((font::use_charnames_in_special
) && (c
== '\\')) {
5373 * add escape escape sequence
5385 int start_level
= input_stack::get_level();
5388 tok
!= start
|| input_stack::get_level() != start_level
;
5391 warning(WARN_DELIM
, "missing closing delimiter");
5394 if (tok
.newline()) {
5395 input_stack::push(make_temp_iterator("\n"));
5396 warning(WARN_DELIM
, "missing closing delimiter");
5404 else if (tok
.leader())
5406 else if (tok
.backspace())
5410 encode_char(&mac
, c
);
5412 return new special_node(mac
);
5415 void device_request()
5417 if (!tok
.newline() && !tok
.eof()) {
5426 if (c
!= ' ' && c
!= '\t')
5429 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5431 curenv
->add_node(new special_node(mac
));
5436 void device_macro_request()
5438 symbol s
= get_name(1);
5439 if (!(s
.is_null() || s
.is_empty())) {
5440 request_or_macro
*p
= lookup_request(s
);
5441 macro
*m
= p
->to_macro();
5443 curenv
->add_node(new special_node(*m
));
5445 error("can't transparently throughput a request");
5450 void output_request()
5452 if (!tok
.newline() && !tok
.eof()) {
5460 if (c
!= ' ' && c
!= '\t')
5463 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5464 topdiv
->transparent_output(c
);
5465 topdiv
->transparent_output('\n');
5470 extern int image_no
; // from node.cpp
5472 static node
*do_suppress(symbol nm
)
5474 if (nm
.is_null() || nm
.is_empty()) {
5475 error("expecting an argument to escape \\O");
5478 const char *s
= nm
.contents();
5481 if (begin_level
== 0)
5482 // suppress generation of glyphs
5483 return new suppress_node(0, 0);
5486 if (begin_level
== 0)
5487 // enable generation of glyphs
5488 return new suppress_node(1, 0);
5491 if (begin_level
== 0)
5492 return new suppress_node(1, 1);
5504 s
++; // move over '5'
5506 if (*s
== (char)0) {
5507 error("missing position and filename in \\O");
5510 if (!(position
== 'l'
5513 || position
== 'i')) {
5514 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5517 s
++; // onto image name
5518 if (s
== (char *)0) {
5519 error("missing image name for \\O");
5523 if (begin_level
== 0)
5524 return new suppress_node(symbol(s
), position
, image_no
);
5530 error("`%1' is an invalid argument to \\O", *s
);
5535 void special_node::tprint(troff_output_file
*out
)
5538 string_iterator
iter(mac
);
5540 int c
= iter
.get(0);
5543 for (const char *s
= ::asciify(c
); *s
; s
++)
5544 tprint_char(out
, *s
);
5549 int get_file_line(const char **filename
, int *lineno
)
5551 return input_stack::get_location(0, filename
, lineno
);
5557 if (get_integer(&n
)) {
5558 const char *filename
= 0;
5560 symbol s
= get_long_name();
5561 filename
= s
.contents();
5563 (void)input_stack::set_location(filename
, n
-1);
5568 static int nroff_mode
= 0;
5570 static void nroff_request()
5576 static void troff_request()
5582 static void skip_alternative()
5585 // ensure that ``.if 0\{'' works as expected
5586 if (tok
.left_brace())
5590 c
= input_stack::get(0);
5593 if (c
== ESCAPE_LEFT_BRACE
)
5595 else if (c
== ESCAPE_RIGHT_BRACE
)
5597 else if (c
== escape_char
&& escape_char
> 0)
5598 switch(input_stack::get(0)) {
5606 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5610 Note that the level can properly be < 0, eg
5616 So don't give an error message in this case.
5618 if (level
<= 0 && c
== '\n')
5624 static void begin_alternative()
5626 while (tok
.space() || tok
.left_brace())
5636 static int_stack if_else_stack
;
5643 while (tok
.ch() == '!') {
5648 unsigned char c
= tok
.ch();
5651 result
= !nroff_mode
;
5653 else if (c
== 'n') {
5655 result
= nroff_mode
;
5657 else if (c
== 'v') {
5661 else if (c
== 'o') {
5662 result
= (topdiv
->get_page_number() & 1);
5665 else if (c
== 'e') {
5666 result
= !(topdiv
->get_page_number() & 1);
5669 else if (c
== 'd' || c
== 'r') {
5671 symbol nm
= get_name(1);
5677 ? request_dictionary
.lookup(nm
) != 0
5678 : number_reg_dictionary
.lookup(nm
) != 0);
5680 else if (c
== 'm') {
5682 symbol nm
= get_long_name(1);
5687 result
= (nm
== default_symbol
5688 || color_dictionary
.lookup(nm
) != 0);
5690 else if (c
== 'c') {
5693 charinfo
*ci
= tok
.get_char(1);
5698 result
= character_exists(ci
, curenv
);
5701 else if (c
== 'F') {
5703 symbol nm
= get_long_name(1);
5708 result
= check_font(curenv
->get_family()->nm
, nm
);
5710 else if (c
== 'S') {
5712 symbol nm
= get_long_name(1);
5717 result
= check_style(nm
);
5719 else if (tok
.space())
5721 else if (tok
.delimiter()) {
5723 int delim_level
= input_stack::get_level();
5724 environment
env1(curenv
);
5725 environment
env2(curenv
);
5726 environment
*oldenv
= curenv
;
5729 for (int i
= 0; i
< 2; i
++) {
5732 if (tok
.newline() || tok
.eof()) {
5733 warning(WARN_DELIM
, "missing closing delimiter");
5739 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5745 node
*n1
= env1
.extract_output_line();
5746 node
*n2
= env2
.extract_output_line();
5747 result
= same_node_list(n1
, n2
);
5748 delete_node_list(n1
);
5749 delete_node_list(n2
);
5757 if (!get_number(&n
, 'u')) {
5767 begin_alternative();
5773 void if_else_request()
5775 if_else_stack
.push(do_if_request());
5785 if (if_else_stack
.is_empty()) {
5786 warning(WARN_EL
, "unbalanced .el request");
5790 if (if_else_stack
.pop())
5793 begin_alternative();
5797 static int while_depth
= 0;
5798 static int while_break_flag
= 0;
5800 void while_request()
5805 mac
.append(new token_node(tok
));
5807 node
*n
= 0; // pacify compiler
5808 int c
= input_stack::get(&n
);
5824 if (c
== ESCAPE_LEFT_BRACE
)
5826 else if (c
== ESCAPE_RIGHT_BRACE
)
5828 else if (c
== escape_char
)
5831 if (c
== '\n' && level
<= 0)
5836 error("unbalanced \\{ \\}");
5839 input_stack::add_boundary();
5841 input_stack::push(new string_iterator(mac
, "while loop"));
5843 if (!do_if_request()) {
5844 while (input_stack::get(0) != EOF
)
5848 process_input_stack();
5849 if (while_break_flag
|| input_stack::is_return_boundary()) {
5850 while_break_flag
= 0;
5854 input_stack::remove_boundary();
5860 void while_break_request()
5863 error("no while loop");
5867 while_break_flag
= 1;
5868 while (input_stack::get(0) != EOF
)
5874 void while_continue_request()
5877 error("no while loop");
5881 while (input_stack::get(0) != EOF
)
5891 symbol nm
= get_long_name(1);
5895 while (!tok
.newline() && !tok
.eof())
5897 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
5900 input_stack::push(new file_iterator(fcp
, nm
.contents()));
5902 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5907 // like .so but use popen()
5912 error(".pso request not allowed in safer mode");
5916 #ifdef POPEN_MISSING
5917 error("pipes not available on this system");
5919 #else /* not POPEN_MISSING */
5920 if (tok
.newline() || tok
.eof())
5921 error("missing command");
5924 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5927 char *buf
= new char[buf_size
];
5929 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5930 const char *s
= asciify(c
);
5931 int slen
= strlen(s
);
5932 if (buf_used
+ slen
+ 1> buf_size
) {
5933 char *old_buf
= buf
;
5934 int old_buf_size
= buf_size
;
5936 buf
= new char[buf_size
];
5937 memcpy(buf
, old_buf
, old_buf_size
);
5940 strcpy(buf
+ buf_used
, s
);
5943 buf
[buf_used
] = '\0';
5945 FILE *fp
= popen(buf
, POPEN_RT
);
5947 input_stack::push(new file_iterator(
5948 new file_case(fp
, buf
, file_case::fc_pipe
| file_case::fc_take_path
),
5949 symbol(buf
).contents()));
5951 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5956 #endif /* not POPEN_MISSING */
5962 static int llx_reg_contents
= 0;
5963 static int lly_reg_contents
= 0;
5964 static int urx_reg_contents
= 0;
5965 static int ury_reg_contents
= 0;
5967 struct bounding_box
{
5968 int llx
, lly
, urx
, ury
;
5971 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5972 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5974 int parse_bounding_box(char *p
, bounding_box
*bb
)
5976 if (sscanf(p
, "%d %d %d %d",
5977 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5980 /* The Document Structuring Conventions say that the numbers
5981 should be integers. Unfortunately some broken applications
5983 double x1
, x2
, x3
, x4
;
5984 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5992 for (; *p
== ' ' || *p
== '\t'; p
++)
5994 if (strncmp(p
, "(atend)", 7) == 0) {
5999 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
6003 // This version is taken from psrm.cpp
6005 #define PS_LINE_MAX 255
6006 cset
white_space("\n\r \t");
6008 int ps_get_line(char *buf
, file_case
*fcp
, const char* filename
)
6010 int c
= fcp
->get_c();
6017 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
6018 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
6019 error("invalid input character code %1 in `%2'", int(c
), filename
);
6020 else if (i
< PS_LINE_MAX
)
6024 error("PostScript file `%1' is non-conforming "
6025 "because length of line exceeds 255", filename
);
6033 if (c
!= EOF
&& c
!= '\n')
6039 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
6041 llx_reg_contents
= llx
;
6042 lly_reg_contents
= lly
;
6043 urx_reg_contents
= urx
;
6044 ury_reg_contents
= ury
;
6047 void do_ps_file(file_case
*fcp
, const char* filename
)
6051 char buf
[PS_LINE_MAX
];
6052 llx_reg_contents
= lly_reg_contents
=
6053 urx_reg_contents
= ury_reg_contents
= 0;
6054 if (!ps_get_line(buf
, fcp
, filename
)) {
6055 error("`%1' is empty", filename
);
6058 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
6059 error("`%1' is not conforming to the Document Structuring Conventions",
6063 while (ps_get_line(buf
, fcp
, filename
) != 0) {
6064 // in header comments, `%X' (`X' any printable character except
6065 // whitespace) is possible too
6066 if (buf
[0] == '%') {
6067 if (strncmp(buf
+ 1, "%EndComments", 12) == 0)
6069 if (white_space(buf
[1]))
6074 if (strncmp(buf
+ 1, "%BoundingBox:", 13) == 0) {
6075 int res
= parse_bounding_box(buf
+ 14, &bb
);
6077 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
6080 else if (res
== 2) {
6085 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6094 // in the trailer, the last BoundingBox comment is significant
6095 for (offset
= 512; !last_try
; offset
*= 2) {
6096 int had_trailer
= 0;
6098 if (offset
> 32768 || fcp
->seek(-offset
, fcp
->seek_end
) == -1) {
6100 if (fcp
->seek(0L, fcp
->seek_set
) == -1)
6103 while (ps_get_line(buf
, fcp
, filename
) != 0) {
6104 if (buf
[0] == '%' && buf
[1] == '%') {
6106 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
6110 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
6111 int res
= parse_bounding_box(buf
+ 14, &bb
);
6114 else if (res
== 2) {
6115 error("`(atend)' not allowed in trailer of `%1'", filename
);
6119 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6128 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
6133 error("%%%%BoundingBox comment not found in `%1'", filename
);
6136 void ps_bbox_request()
6138 symbol nm
= get_long_name(1);
6142 while (!tok
.newline() && !tok
.eof())
6144 // PS files might contain non-printable characters, such as ^Z
6145 // and CRs not followed by an LF, so open them in binary mode.
6146 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
6147 fcp
->mux_need_seek
| fcp
->mux_need_binary
);
6149 do_ps_file(fcp
, nm
.contents());
6152 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
6157 const char *asciify(int c
)
6160 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
6161 buf
[1] = buf
[2] = '\0';
6163 case ESCAPE_QUESTION
:
6166 case ESCAPE_AMPERSAND
:
6169 case ESCAPE_RIGHT_PARENTHESIS
:
6172 case ESCAPE_UNDERSCORE
:
6178 case ESCAPE_CIRCUMFLEX
:
6181 case ESCAPE_LEFT_BRACE
:
6184 case ESCAPE_RIGHT_BRACE
:
6187 case ESCAPE_LEFT_QUOTE
:
6190 case ESCAPE_RIGHT_QUOTE
:
6208 case ESCAPE_PERCENT
:
6220 case PUSH_GROFF_MODE
:
6221 case PUSH_COMP_MODE
:
6222 case POP_GROFFCOMP_MODE
:
6226 if (invalid_input_char(c
))
6235 const char *input_char_description(int c
)
6239 return "a newline character";
6241 return "a backspace character";
6243 return "a leader character";
6245 return "a tab character";
6247 return "a space character";
6251 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
6252 if (invalid_input_char(c
)) {
6253 const char *s
= asciify(c
);
6260 sprintf(buf
, "magic character code %d", c
);
6269 sprintf(buf
, "character code %d", c
);
6275 if (!tok
.newline() && !tok
.eof()) {
6284 if (c
!= ' ' && c
!= '\t')
6288 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6291 curenv
->add_node(new tag_node(s
, 0));
6298 if (!tok
.newline() && !tok
.eof()) {
6307 if (c
!= ' ' && c
!= '\t')
6311 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6314 curenv
->add_node(new tag_node(s
, 1));
6319 // .tm, .tm1, and .tmc
6321 void do_terminal(int newline
, int string_like
)
6323 if (!tok
.newline() && !tok
.eof()) {
6327 if (string_like
&& c
== '"') {
6331 if (c
!= ' ' && c
!= '\t')
6334 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6335 fputs(asciify(c
), stderr
);
6338 fputc('\n', stderr
);
6353 void terminal_continue()
6358 dictionary
stream_dictionary(20);
6360 void do_open(int append
)
6362 symbol stream
= get_name(1);
6363 if (!stream
.is_null()) {
6364 symbol filename
= get_long_name(1);
6365 if (!filename
.is_null()) {
6367 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
6369 error("can't open `%1' for %2: %3",
6370 filename
.contents(),
6371 append
? "appending" : "writing",
6373 fp
= (FILE *)stream_dictionary
.remove(stream
);
6376 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
6387 error(".open request not allowed in safer mode");
6394 void opena_request()
6397 error(".opena request not allowed in safer mode");
6404 void close_request()
6406 symbol stream
= get_name(1);
6407 if (!stream
.is_null()) {
6408 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
6410 error("no stream named `%1'", stream
.contents());
6417 // .write and .writec
6419 void do_write_request(int newline
)
6421 symbol stream
= get_name(1);
6422 if (stream
.is_null()) {
6426 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6428 error("no stream named `%1'", stream
.contents());
6433 while ((c
= get_copy(0)) == ' ')
6437 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6438 fputs(asciify(c
), fp
);
6445 void write_request()
6447 do_write_request(1);
6450 void write_request_continue()
6452 do_write_request(0);
6455 void write_macro_request()
6457 symbol stream
= get_name(1);
6458 if (stream
.is_null()) {
6462 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6464 error("no stream named `%1'", stream
.contents());
6468 symbol s
= get_name(1);
6473 request_or_macro
*p
= lookup_request(s
);
6474 macro
*m
= p
->to_macro();
6476 error("cannot write request");
6478 string_iterator
iter(*m
);
6480 int c
= iter
.get(0);
6483 fputs(asciify(c
), fp
);
6490 void warnscale_request()
6497 warn_scale
= (double)units_per_inch
;
6499 warn_scale
= (double)units_per_inch
/ 2.54;
6501 warn_scale
= (double)units_per_inch
/ 72.0;
6503 warn_scale
= (double)units_per_inch
/ 6.0;
6506 "invalid scaling indicator `%1', using `i' instead", c
);
6509 warn_scaling_indicator
= c
;
6514 void spreadwarn_request()
6517 if (has_arg() && get_hunits(&n
, 'm')) {
6520 hunits em
= curenv
->get_size();
6521 spread_limit
= (double)n
.to_units()
6522 / (em
.is_zero() ? hresolution
: em
.to_units());
6525 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
6526 // changing value; we mirror at
6527 // -0.5 to make zero a valid value
6531 static void init_charset_table()
6534 strcpy(buf
, "char");
6535 for (int i
= 0; i
< 256; i
++) {
6536 strcpy(buf
+ 4, i_to_a(i
));
6537 charset_table
[i
] = get_charinfo(symbol(buf
));
6538 charset_table
[i
]->set_ascii_code(i
);
6540 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6542 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6543 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6544 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6545 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6546 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6547 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6548 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6549 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6550 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6551 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6552 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6553 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6554 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER
);
6555 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6556 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6557 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6558 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6559 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6560 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6561 page_character
= charset_table
['%'];
6564 static void init_hpf_code_table()
6566 for (int i
= 0; i
< 256; i
++)
6567 hpf_code_table
[i
] = i
;
6570 static void do_translate(int translate_transparent
, int translate_input
)
6573 while (!tok
.newline() && !tok
.eof()) {
6575 // This is a really bizarre troff feature.
6577 translate_space_to_dummy
= tok
.dummy();
6578 if (tok
.newline() || tok
.eof())
6583 charinfo
*ci1
= tok
.get_char(1);
6587 if (tok
.newline() || tok
.eof()) {
6588 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6589 translate_transparent
);
6593 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6594 translate_transparent
);
6595 else if (tok
.stretchable_space())
6596 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6597 translate_transparent
);
6598 else if (tok
.dummy())
6599 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6600 translate_transparent
);
6601 else if (tok
.hyphen_indicator())
6602 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6603 translate_transparent
);
6605 charinfo
*ci2
= tok
.get_char(1);
6609 ci1
->set_translation(0, translate_transparent
, translate_input
);
6611 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6623 void translate_no_transparent()
6628 void translate_input()
6636 if (get_integer(&flags
))
6638 charinfo
*ci
= tok
.get_char(1);
6640 charinfo
*tem
= ci
->get_translation();
6643 ci
->set_flags(flags
);
6650 void hyphenation_code()
6653 while (!tok
.newline() && !tok
.eof()) {
6654 charinfo
*ci
= tok
.get_char(1);
6659 unsigned char c
= tok
.ch();
6661 error("hyphenation code must be ordinary character");
6665 error("hyphenation code cannot be digit");
6668 ci
->set_hyphenation_code(c
);
6669 if (ci
->get_translation()
6670 && ci
->get_translation()->get_translation_input())
6671 ci
->get_translation()->set_hyphenation_code(c
);
6678 void hyphenation_patterns_file_code()
6681 while (!tok
.newline() && !tok
.eof()) {
6683 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6685 error("missing output hyphenation code");
6688 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6689 hpf_code_table
[n1
] = n2
;
6693 error("output hyphenation code must be integer in the range 0..255");
6698 error("input hyphenation code must be integer in the range 0..255");
6705 charinfo
*token::get_char(int required
)
6707 if (type
== TOKEN_CHAR
)
6708 return charset_table
[c
];
6709 if (type
== TOKEN_SPECIAL
)
6710 return get_charinfo(nm
);
6711 if (type
== TOKEN_NUMBERED_CHAR
)
6712 return get_charinfo_by_number(val
);
6713 if (type
== TOKEN_ESCAPE
) {
6714 if (escape_char
!= 0)
6715 return charset_table
[escape_char
];
6717 error("`\\e' used while no current escape character");
6722 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6723 warning(WARN_MISSING
, "missing normal or special character");
6725 error("normal or special character expected (got %1)", description());
6730 charinfo
*get_optional_char()
6734 charinfo
*ci
= tok
.get_char();
6736 check_missing_character();
6742 void check_missing_character()
6744 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6745 error("normal or special character expected (got %1): "
6746 "treated as missing",
6752 int token::add_to_node_list(node
**pp
)
6759 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6765 if (escape_char
!= 0)
6766 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6768 case TOKEN_HYPHEN_INDICATOR
:
6769 *pp
= (*pp
)->add_discretionary_hyphen();
6771 case TOKEN_ITALIC_CORRECTION
:
6772 *pp
= (*pp
)->add_italic_correction(&w
);
6774 case TOKEN_LEFT_BRACE
:
6776 case TOKEN_MARK_INPUT
:
6777 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6783 case TOKEN_NUMBERED_CHAR
:
6784 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6786 case TOKEN_RIGHT_BRACE
:
6789 n
= new hmotion_node(curenv
->get_space_width(),
6790 curenv
->get_fill_color());
6793 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6795 case TOKEN_STRETCHABLE_SPACE
:
6796 n
= new unbreakable_space_node(curenv
->get_space_width(),
6797 curenv
->get_fill_color());
6799 case TOKEN_UNSTRETCHABLE_SPACE
:
6800 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6801 curenv
->get_fill_color());
6803 case TOKEN_TRANSPARENT_DUMMY
:
6804 n
= new transparent_dummy_node
;
6806 case TOKEN_ZERO_WIDTH_BREAK
:
6807 n
= new space_node(H0
, curenv
->get_fill_color());
6809 n
->is_escape_colon();
6821 void token::process()
6823 if (possibly_handle_first_page_transition())
6826 case TOKEN_BACKSPACE
:
6827 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6828 curenv
->get_fill_color()));
6831 curenv
->add_char(charset_table
[c
]);
6834 curenv
->add_node(new dummy_node
);
6843 if (escape_char
!= 0)
6844 curenv
->add_char(charset_table
[escape_char
]);
6846 case TOKEN_BEGIN_TRAP
:
6847 case TOKEN_END_TRAP
:
6848 case TOKEN_PAGE_EJECTOR
:
6849 // these are all handled in process_input_stack()
6851 case TOKEN_HYPHEN_INDICATOR
:
6852 curenv
->add_hyphen_indicator();
6854 case TOKEN_INTERRUPT
:
6855 curenv
->interrupt();
6857 case TOKEN_ITALIC_CORRECTION
:
6858 curenv
->add_italic_correction();
6861 curenv
->handle_tab(1);
6863 case TOKEN_LEFT_BRACE
:
6865 case TOKEN_MARK_INPUT
:
6866 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6872 curenv
->add_node(nd
);
6875 case TOKEN_NUMBERED_CHAR
:
6876 curenv
->add_char(get_charinfo_by_number(val
));
6879 // handled in process_input_stack()
6881 case TOKEN_RIGHT_BRACE
:
6887 curenv
->add_char(get_charinfo(nm
));
6892 case TOKEN_STRETCHABLE_SPACE
:
6893 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6894 curenv
->get_fill_color()));
6896 case TOKEN_UNSTRETCHABLE_SPACE
:
6897 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6898 curenv
->get_fill_color()));
6901 curenv
->handle_tab(0);
6903 case TOKEN_TRANSPARENT
:
6905 case TOKEN_TRANSPARENT_DUMMY
:
6906 curenv
->add_node(new transparent_dummy_node
);
6908 case TOKEN_ZERO_WIDTH_BREAK
:
6910 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6911 tmp
->freeze_space();
6912 tmp
->is_escape_colon();
6913 curenv
->add_node(tmp
);
6921 class nargs_reg
: public reg
{
6923 const char *get_string();
6926 const char *nargs_reg::get_string()
6928 return i_to_a(input_stack::nargs());
6931 class lineno_reg
: public reg
{
6933 const char *get_string();
6936 const char *lineno_reg::get_string()
6940 if (!input_stack::get_location(0, &file
, &line
))
6942 return i_to_a(line
);
6945 class writable_lineno_reg
: public general_reg
{
6947 writable_lineno_reg();
6948 void set_value(units
);
6949 int get_value(units
*);
6952 writable_lineno_reg::writable_lineno_reg()
6956 int writable_lineno_reg::get_value(units
*res
)
6960 if (!input_stack::get_location(0, &file
, &line
))
6966 void writable_lineno_reg::set_value(units n
)
6968 input_stack::set_location(0, n
);
6971 class filename_reg
: public reg
{
6973 const char *get_string();
6976 const char *filename_reg::get_string()
6980 if (input_stack::get_location(0, &file
, &line
))
6986 class break_flag_reg
: public reg
{
6988 const char *get_string();
6991 const char *break_flag_reg::get_string()
6993 return i_to_a(input_stack::get_break_flag());
6996 class constant_reg
: public reg
{
6999 constant_reg(const char *);
7000 const char *get_string();
7003 constant_reg::constant_reg(const char *p
) : s(p
)
7007 const char *constant_reg::get_string()
7012 constant_int_reg::constant_int_reg(int *q
) : p(q
)
7016 const char *constant_int_reg::get_string()
7021 void abort_request()
7026 else if (tok
.newline())
7029 while ((c
= get_copy(0)) == ' ')
7032 if (c
== EOF
|| c
== '\n')
7033 fputs("User Abort.", stderr
);
7035 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
7036 fputs(asciify(c
), stderr
);
7038 fputc('\n', stderr
);
7039 cleanup_and_exit(1);
7045 char *s
= new char[len
];
7047 while ((c
= get_copy(0)) == ' ')
7050 while (c
!= '\n' && c
!= EOF
) {
7051 if (!invalid_input_char(c
)) {
7054 s
= new char[len
*2];
7055 memcpy(s
, tem
, len
);
7075 error(".pi request not allowed in safer mode");
7079 #ifdef POPEN_MISSING
7080 error("pipes not available on this system");
7082 #else /* not POPEN_MISSING */
7084 error("can't pipe: output already started");
7089 if ((pc
= read_string()) == 0)
7090 error("can't pipe to empty command");
7092 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
7093 strcpy(s
, pipe_command
);
7096 a_delete pipe_command
;
7103 #endif /* not POPEN_MISSING */
7107 static int system_status
;
7109 void system_request()
7112 error(".sy request not allowed in safer mode");
7116 char *command
= read_string();
7118 error("empty command");
7120 system_status
= system(command
);
7128 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7129 handle_initial_request(COPY_FILE_REQUEST
);
7132 symbol filename
= get_long_name(1);
7133 while (!tok
.newline() && !tok
.eof())
7137 if (!filename
.is_null())
7138 curdiv
->copy_file(filename
.contents());
7146 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7147 handle_initial_request(VJUSTIFY_REQUEST
);
7150 symbol type
= get_long_name(1);
7151 if (!type
.is_null())
7152 curdiv
->vjustify(type
);
7158 void transparent_file()
7160 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7161 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
7164 symbol filename
= get_long_name(1);
7165 while (!tok
.newline() && !tok
.eof())
7169 if (!filename
.is_null()) {
7170 file_case
*fcp
= include_search_path
7171 .open_file_cautious(filename
.contents());
7173 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
7177 int c
= fcp
->get_c();
7180 if (invalid_input_char(c
))
7181 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
7183 curdiv
->transparent_output(c
);
7188 curdiv
->transparent_output('\n');
7200 page_range(int, int, page_range
*);
7201 int contains(int n
);
7204 page_range::page_range(int i
, int j
, page_range
*p
)
7205 : first(i
), last(j
), next(p
)
7209 int page_range::contains(int n
)
7211 return n
>= first
&& (last
<= 0 || n
<= last
);
7214 page_range
*output_page_list
= 0;
7216 int in_output_page_list(int n
)
7218 if (!output_page_list
)
7220 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
7226 static void parse_output_page_list(char *p
)
7232 else if (csdigit(*p
)) {
7235 i
= i
*10 + *p
++ - '0';
7236 while (csdigit(*p
));
7246 j
= j
*10 + *p
++ - '0';
7247 while (csdigit(*p
));
7253 last_page_number
= -1;
7254 else if (last_page_number
>= 0 && j
> last_page_number
)
7255 last_page_number
= j
;
7256 output_page_list
= new page_range(i
, j
, output_page_list
);
7262 error("bad output page list");
7263 output_page_list
= 0;
7267 static file_case
*open_mac_file(const char *mac
)
7269 // Try first FOOBAR.tmac, then tmac.FOOBAR
7270 char *s
= new char[strlen(mac
) + strlen(MACRO_POSTFIX
) +1];
7272 strcat(s
, MACRO_POSTFIX
);
7275 if ((fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
)) == NULL
) {
7276 s
= new char[strlen(mac
) + strlen(MACRO_PREFIX
) +1];
7277 strcpy(s
, MACRO_PREFIX
);
7279 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7284 static void process_macro_file(const char *mac
)
7286 file_case
*fcp
= open_mac_file(mac
);
7288 fatal("can't find macro file %1", mac
);
7289 const char *s
= symbol(fcp
->path()).contents();
7290 input_stack::push(new file_iterator(fcp
, s
));
7292 process_input_stack();
7295 static void process_startup_file(const char *filename
)
7297 search_path
*orig_mac_path
= mac_path
;
7298 mac_path
= &config_macro_path
;
7300 if ((fcp
= mac_path
->open_file(filename
)) != NULL
) {
7301 input_stack::push(new file_iterator(fcp
, symbol(fcp
->path()).contents()));
7303 process_input_stack();
7305 mac_path
= orig_mac_path
;
7310 symbol nm
= get_long_name(1);
7314 while (!tok
.newline() && !tok
.eof())
7316 // .mso doesn't (and cannot) go through open_mac_file, so we
7317 // need to do it here manually: If we have tmac.FOOBAR, try
7318 // FOOBAR.tmac and vice versa
7320 if ((fcp
= mac_path
->open_file(nm
.contents())) == NULL
) {
7321 const char *fn
= nm
.contents();
7323 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
7324 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
7325 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
7326 strcat(s
, MACRO_POSTFIX
);
7327 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7331 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
7332 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
7333 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
7334 strcpy(s
, MACRO_PREFIX
);
7335 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
7336 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7342 input_stack::push(new file_iterator(fcp
, symbol(fcp
->path()).contents()));
7344 warning(WARN_FILE
, "can't find macro file `%1'", nm
.contents());
7349 static void process_input_file(const char *name
)
7352 if ((fcp
= include_search_path
.open_file_cautious(name
)) == NULL
) {
7353 assert(strcmp(name
, "-"));
7354 fatal("can't open `%1': %2", name
, strerror(errno
));
7356 input_stack::push(new file_iterator(fcp
, name
));
7358 process_input_stack();
7361 // make sure the_input is empty before calling this
7363 static int evaluate_expression(const char *expr
, units
*res
)
7365 input_stack::push(make_temp_iterator(expr
));
7367 int success
= get_number(res
, 'u');
7368 while (input_stack::get(0) != EOF
)
7373 static void do_register_assignment(const char *s
)
7375 const char *p
= strchr(s
, '=');
7381 if (evaluate_expression(s
+ 1, &n
))
7382 set_number_reg(buf
, n
);
7385 char *buf
= new char[p
- s
+ 1];
7386 memcpy(buf
, s
, p
- s
);
7389 if (evaluate_expression(p
+ 1, &n
))
7390 set_number_reg(buf
, n
);
7395 static void set_string(const char *name
, const char *value
)
7397 macro
*m
= new macro
;
7398 for (const char *p
= value
; *p
; p
++)
7399 if (!invalid_input_char((unsigned char)*p
))
7401 request_dictionary
.define(name
, m
);
7404 static void do_string_assignment(const char *s
)
7406 const char *p
= strchr(s
, '=');
7411 set_string(buf
, s
+ 1);
7414 char *buf
= new char[p
- s
+ 1];
7415 memcpy(buf
, s
, p
- s
);
7417 set_string(buf
, p
+ 1);
7422 struct string_list
{
7425 string_list(const char *ss
) : s(ss
), next(0) {}
7429 static void prepend_string(const char *s
, string_list
**p
)
7431 string_list
*l
= new string_list(s
);
7437 static void add_string(const char *s
, string_list
**p
)
7441 *p
= new string_list(s
);
7444 void usage(FILE *stream
, const char *prog
)
7447 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7448 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7452 int main(int argc
, char **argv
)
7454 program_name
= argv
[0];
7455 static char stderr_buf
[BUFSIZ
];
7456 setbuf(stderr
, stderr_buf
);
7458 string_list
*macros
= 0;
7459 string_list
*register_assignments
= 0;
7460 string_list
*string_assignments
= 0;
7465 int no_rc
= 0; // don't process troffrc and troffrc-end
7466 int next_page_number
= 0; // pacify compiler
7468 hresolution
= vresolution
= 1;
7469 // restore $PATH if called from groff
7470 char* groff_path
= getenv("GROFF_PATH__");
7477 if (putenv(strsave(e
.contents())))
7478 fatal("putenv failed");
7480 static const struct option long_options
[] = {
7481 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
7482 { "version", no_argument
, 0, 'v' },
7485 #if defined(DEBUGGING)
7486 #define DEBUG_OPTION "D"
7488 while ((c
= getopt_long(argc
, argv
,
7489 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7490 DEBUG_OPTION
, long_options
, 0))
7495 printf("GNU troff (groff) version %s\n", Version_string
);
7500 // Search path for .psbb files
7501 // and most other non-system input files.
7502 include_search_path
.command_line_dir(optarg
);
7507 is_html
= (strcmp(device
, "html") == 0);
7510 compatible_flag
= 1;
7516 macro_path
.command_line_dir(optarg
);
7517 safer_macro_path
.command_line_dir(optarg
);
7518 config_macro_path
.command_line_dir(optarg
);
7521 font::command_line_font_dir(optarg
);
7524 add_string(optarg
, ¯os
);
7533 enable_warning(optarg
);
7536 disable_warning(optarg
);
7545 ascii_output_flag
= 1;
7548 suppress_output_flag
= 1;
7551 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7554 error("bad page number");
7557 parse_output_page_list(optarg
);
7560 if (*optarg
== '\0')
7561 error("`-d' requires non-empty argument");
7563 add_string(optarg
, &string_assignments
);
7566 if (*optarg
== '\0')
7567 error("`-r' requires non-empty argument");
7569 add_string(optarg
, ®ister_assignments
);
7572 default_family
= symbol(optarg
);
7578 // silently ignore these
7581 unsafe_flag
= 1; // unsafe behaviour
7583 #if defined(DEBUGGING)
7588 case CHAR_MAX
+ 1: // --help
7589 usage(stdout
, argv
[0]);
7593 usage(stderr
, argv
[0]);
7595 break; // never reached
7600 mac_path
= ¯o_path
;
7601 set_string(".T", device
);
7602 init_charset_table();
7603 init_hpf_code_table();
7604 if (!font::load_desc())
7605 fatal("sorry, I can't continue");
7606 units_per_inch
= font::res
;
7607 hresolution
= font::hor
;
7608 vresolution
= font::vert
;
7609 sizescale
= font::sizescale
;
7610 tcommand_flag
= font::tcommand
;
7611 warn_scale
= (double)units_per_inch
;
7612 warn_scaling_indicator
= 'i';
7613 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7614 default_family
= symbol(font::family
);
7615 font_size::init_size_table(font::sizes
);
7618 if (font::style_table
) {
7619 for (i
= 0; font::style_table
[i
]; i
++)
7620 mount_style(j
++, symbol(font::style_table
[i
]));
7622 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7623 // In the DESC file a font name of 0 (zero) means leave this
7625 if (strcmp(font::font_name_table
[i
], "0") != 0)
7626 mount_font(j
, symbol(font::font_name_table
[i
]));
7627 curdiv
= topdiv
= new top_level_diversion
;
7629 topdiv
->set_next_page_number(next_page_number
);
7630 init_input_requests();
7631 init_env_requests();
7632 init_div_requests();
7634 init_column_requests();
7636 init_node_requests();
7637 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7639 init_reg_requests();
7640 init_hyphen_requests();
7641 init_environments();
7642 while (string_assignments
) {
7643 do_string_assignment(string_assignments
->s
);
7644 string_list
*tem
= string_assignments
;
7645 string_assignments
= string_assignments
->next
;
7648 while (register_assignments
) {
7649 do_register_assignment(register_assignments
->s
);
7650 string_list
*tem
= register_assignments
;
7651 register_assignments
= register_assignments
->next
;
7655 process_startup_file(INITIAL_STARTUP_FILE
);
7657 process_macro_file(macros
->s
);
7658 string_list
*tem
= macros
;
7659 macros
= macros
->next
;
7663 process_startup_file(FINAL_STARTUP_FILE
);
7664 for (i
= optind
; i
< argc
; i
++)
7665 process_input_file(argv
[i
]);
7666 if (optind
>= argc
|| iflag
)
7667 process_input_file("-");
7669 return 0; // not reached
7675 if (has_arg() && get_integer(&n
)) {
7676 if (n
& ~WARN_TOTAL
) {
7677 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7683 warning_mask
= WARN_TOTAL
;
7687 static void init_registers()
7689 #ifdef LONG_FOR_TIME_T
7691 #else /* not LONG_FOR_TIME_T */
7693 #endif /* not LONG_FOR_TIME_T */
7695 // Use struct here to work around misfeature in old versions of g++.
7696 struct tm
*tt
= localtime(&t
);
7697 set_number_reg("seconds", int(tt
->tm_sec
));
7698 set_number_reg("minutes", int(tt
->tm_min
));
7699 set_number_reg("hours", int(tt
->tm_hour
));
7700 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7701 set_number_reg("dy", int(tt
->tm_mday
));
7702 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7703 set_number_reg("year", int(1900 + tt
->tm_year
));
7704 set_number_reg("yr", int(tt
->tm_year
));
7705 set_number_reg("$$", getpid());
7706 number_reg_dictionary
.define(".A",
7707 new constant_reg(ascii_output_flag
7713 * registers associated with \O
7716 static int output_reg_minx_contents
= -1;
7717 static int output_reg_miny_contents
= -1;
7718 static int output_reg_maxx_contents
= -1;
7719 static int output_reg_maxy_contents
= -1;
7721 void check_output_limits(int x
, int y
)
7723 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7724 output_reg_minx_contents
= x
;
7725 if (x
> output_reg_maxx_contents
)
7726 output_reg_maxx_contents
= x
;
7727 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7728 output_reg_miny_contents
= y
;
7729 if (y
> output_reg_maxy_contents
)
7730 output_reg_maxy_contents
= y
;
7733 void reset_output_registers()
7735 output_reg_minx_contents
= -1;
7736 output_reg_miny_contents
= -1;
7737 output_reg_maxx_contents
= -1;
7738 output_reg_maxy_contents
= -1;
7741 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7743 *minx
= output_reg_minx_contents
;
7744 *miny
= output_reg_miny_contents
;
7745 *maxx
= output_reg_maxx_contents
;
7746 *maxy
= output_reg_maxy_contents
;
7749 void init_input_requests()
7751 init_request("ab", abort_request
);
7752 init_request("als", alias_macro
);
7753 init_request("am", append_macro
);
7754 init_request("am1", append_nocomp_macro
);
7755 init_request("ami", append_indirect_macro
);
7756 init_request("ami1", append_indirect_nocomp_macro
);
7757 init_request("as", append_string
);
7758 init_request("as1", append_nocomp_string
);
7759 init_request("asciify", asciify_macro
);
7760 init_request("backtrace", backtrace_request
);
7761 init_request("blm", blank_line_macro
);
7762 init_request("break", while_break_request
);
7763 init_request("cf", copy_file
);
7764 init_request("cflags", char_flags
);
7765 init_request("char", define_character
);
7766 init_request("chop", chop_macro
);
7767 init_request("close", close_request
);
7768 init_request("color", activate_color
);
7769 init_request("composite", composite_request
);
7770 init_request("continue", while_continue_request
);
7771 init_request("cp", compatible
);
7772 init_request("de", define_macro
);
7773 init_request("de1", define_nocomp_macro
);
7774 init_request("defcolor", define_color
);
7775 init_request("dei", define_indirect_macro
);
7776 init_request("dei1", define_indirect_nocomp_macro
);
7777 init_request("device", device_request
);
7778 init_request("devicem", device_macro_request
);
7779 init_request("do", do_request
);
7780 init_request("ds", define_string
);
7781 init_request("ds1", define_nocomp_string
);
7782 init_request("ec", set_escape_char
);
7783 init_request("ecr", restore_escape_char
);
7784 init_request("ecs", save_escape_char
);
7785 init_request("el", else_request
);
7786 init_request("em", end_macro
);
7787 init_request("eo", escape_off
);
7788 init_request("ex", exit_request
);
7789 init_request("fchar", define_fallback_character
);
7790 #ifdef WIDOW_CONTROL
7791 init_request("fpl", flush_pending_lines
);
7792 #endif /* WIDOW_CONTROL */
7793 init_request("hcode", hyphenation_code
);
7794 init_request("hpfcode", hyphenation_patterns_file_code
);
7795 init_request("ie", if_else_request
);
7796 init_request("if", if_request
);
7797 init_request("ig", ignore
);
7798 init_request("length", length_request
);
7799 init_request("lf", line_file
);
7800 init_request("mso", macro_source
);
7801 init_request("nop", nop_request
);
7802 init_request("nroff", nroff_request
);
7803 init_request("nx", next_file
);
7804 init_request("open", open_request
);
7805 init_request("opena", opena_request
);
7806 init_request("output", output_request
);
7807 init_request("pc", set_page_character
);
7808 init_request("pi", pipe_output
);
7809 init_request("pm", print_macros
);
7810 init_request("psbb", ps_bbox_request
);
7811 #ifndef POPEN_MISSING
7812 init_request("pso", pipe_source
);
7813 #endif /* not POPEN_MISSING */
7814 init_request("rchar", remove_character
);
7815 init_request("rd", read_request
);
7816 init_request("return", return_macro_request
);
7817 init_request("rm", remove_macro
);
7818 init_request("rn", rename_macro
);
7819 init_request("schar", define_special_character
);
7820 init_request("shift", shift
);
7821 init_request("so", source
);
7822 init_request("spreadwarn", spreadwarn_request
);
7823 init_request("substring", substring_request
);
7824 init_request("sy", system_request
);
7825 init_request("tag", tag
);
7826 init_request("taga", taga
);
7827 init_request("tm", terminal
);
7828 init_request("tm1", terminal1
);
7829 init_request("tmc", terminal_continue
);
7830 init_request("tr", translate
);
7831 init_request("trf", transparent_file
);
7832 init_request("trin", translate_input
);
7833 init_request("trnt", translate_no_transparent
);
7834 init_request("troff", troff_request
);
7835 init_request("unformat", unformat_macro
);
7837 init_request("vj", vjustify
);
7839 init_request("warn", warn_request
);
7840 init_request("warnscale", warnscale_request
);
7841 init_request("while", while_request
);
7842 init_request("write", write_request
);
7843 init_request("writec", write_request_continue
);
7844 init_request("writem", write_macro_request
);
7845 number_reg_dictionary
.define(".$", new nargs_reg
);
7846 number_reg_dictionary
.define(".br", new break_flag_reg
);
7847 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7848 number_reg_dictionary
.define(".O", new variable_reg(&begin_level
));
7849 number_reg_dictionary
.define(".c", new lineno_reg
);
7850 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7851 number_reg_dictionary
.define(".F", new filename_reg
);
7852 number_reg_dictionary
.define(".g", new constant_reg("1"));
7853 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7854 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7855 number_reg_dictionary
.define(".U", new constant_int_reg(&unsafe_flag
));
7856 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7857 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7858 extern const char *major_version
;
7859 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7860 extern const char *revision
;
7861 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7862 extern const char *minor_version
;
7863 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7864 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7865 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7866 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7867 number_reg_dictionary
.define("opmaxx",
7868 new variable_reg(&output_reg_maxx_contents
));
7869 number_reg_dictionary
.define("opmaxy",
7870 new variable_reg(&output_reg_maxy_contents
));
7871 number_reg_dictionary
.define("opminx",
7872 new variable_reg(&output_reg_minx_contents
));
7873 number_reg_dictionary
.define("opminy",
7874 new variable_reg(&output_reg_miny_contents
));
7875 number_reg_dictionary
.define("slimit",
7876 new variable_reg(&input_stack::limit
));
7877 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7878 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7879 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7882 object_dictionary
request_dictionary(501);
7884 void init_request(const char *s
, REQUEST_FUNCP f
)
7886 request_dictionary
.define(s
, new request(f
));
7889 static request_or_macro
*lookup_request(symbol nm
)
7891 assert(!nm
.is_null());
7892 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7894 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7896 request_dictionary
.define(nm
, p
);
7901 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7903 // Don't interpret character definitions in compatible mode.
7904 int old_compatible_flag
= compatible_flag
;
7905 compatible_flag
= 0;
7906 int old_escape_char
= escape_char
;
7908 macro
*mac
= ci
->set_macro(0);
7910 environment
*oldenv
= curenv
;
7911 environment
env(envp
);
7913 curenv
->set_composite();
7914 token old_tok
= tok
;
7915 input_stack::add_boundary();
7916 string_iterator
*si
=
7917 new string_iterator(*mac
, "composite character", ci
->nm
);
7918 input_stack::push(si
);
7919 // we don't use process_input_stack, because we don't want to recognise
7925 if (tok
.newline()) {
7926 error("composite character mustn't contain newline");
7934 node
*n
= curenv
->extract_output_line();
7935 input_stack::remove_boundary();
7939 compatible_flag
= old_compatible_flag
;
7940 escape_char
= old_escape_char
;
7945 static node
*read_draw_node()
7949 if (!start
.delimiter(1)){
7952 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7957 error("missing argument");
7959 unsigned char type
= tok
.ch();
7961 read_color_draw_node(start
);
7966 hvpair
*point
= new hvpair
[maxpoints
];
7971 for (i
= 0; tok
!= start
; i
++) {
7972 if (i
== maxpoints
) {
7973 hvpair
*oldpoint
= point
;
7974 point
= new hvpair
[maxpoints
*2];
7975 for (int j
= 0; j
< maxpoints
; j
++)
7976 point
[j
] = oldpoint
[j
];
7980 if (!get_hunits(&point
[i
].h
,
7981 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7992 if (!get_vunits(&point
[i
].v
, 'v')) {
7998 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
8003 if (npoints
!= 1 || no_last_v
) {
8004 error("two arguments needed for line");
8009 if (npoints
!= 1 || !no_last_v
) {
8010 error("one argument needed for circle");
8016 if (npoints
!= 1 || no_last_v
) {
8017 error("two arguments needed for ellipse");
8022 if (npoints
!= 2 || no_last_v
) {
8023 error("four arguments needed for arc");
8029 error("even number of arguments needed for spline");
8032 if (npoints
!= 1 || !no_last_v
) {
8033 error("one argument needed for gray shade");
8038 // silently pass it through
8041 draw_node
*dn
= new draw_node(type
, point
, npoints
,
8042 curenv
->get_font_size(),
8043 curenv
->get_glyph_color(),
8044 curenv
->get_fill_color());
8056 static void read_color_draw_node(token
&start
)
8060 error("missing color scheme");
8063 unsigned char scheme
= tok
.ch();
8066 char end
= start
.ch();
8069 col
= read_cmy(end
);
8072 col
= &default_color
;
8075 col
= read_gray(end
);
8078 col
= read_cmyk(end
);
8081 col
= read_rgb(end
);
8085 curenv
->set_fill_color(col
);
8086 while (tok
!= start
) {
8087 if (tok
.newline() || tok
.eof()) {
8088 warning(WARN_DELIM
, "missing closing delimiter");
8089 input_stack::push(make_temp_iterator("\n"));
8100 } warning_table
[] = {
8101 { "char", WARN_CHAR
},
8102 { "range", WARN_RANGE
},
8103 { "break", WARN_BREAK
},
8104 { "delim", WARN_DELIM
},
8106 { "scale", WARN_SCALE
},
8107 { "number", WARN_NUMBER
},
8108 { "syntax", WARN_SYNTAX
},
8109 { "tab", WARN_TAB
},
8110 { "right-brace", WARN_RIGHT_BRACE
},
8111 { "missing", WARN_MISSING
},
8112 { "input", WARN_INPUT
},
8113 { "escape", WARN_ESCAPE
},
8114 { "space", WARN_SPACE
},
8115 { "font", WARN_FONT
},
8117 { "mac", WARN_MAC
},
8118 { "reg", WARN_REG
},
8120 { "color", WARN_COLOR
},
8121 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
8122 { "w", WARN_TOTAL
},
8123 { "default", DEFAULT_WARNING_MASK
},
8126 static int lookup_warning(const char *name
)
8128 for (unsigned int i
= 0;
8129 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
8131 if (strcmp(name
, warning_table
[i
].name
) == 0)
8132 return warning_table
[i
].mask
;
8136 static void enable_warning(const char *name
)
8138 int mask
= lookup_warning(name
);
8140 warning_mask
|= mask
;
8142 error("unknown warning `%1'", name
);
8145 static void disable_warning(const char *name
)
8147 int mask
= lookup_warning(name
);
8149 warning_mask
&= ~mask
;
8151 error("unknown warning `%1'", name
);
8154 static void copy_mode_error(const char *format
,
8160 static const char prefix
[] = "(in ignored input) ";
8161 char *s
= new char[sizeof(prefix
) + strlen(format
)];
8164 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
8168 error(format
, arg1
, arg2
, arg3
);
8171 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
8173 static void do_error(error_type type
,
8179 const char *filename
;
8181 if (inhibit_errors
&& type
< FATAL
)
8184 input_stack::backtrace();
8185 if (!get_file_line(&filename
, &lineno
))
8188 errprint("%1:%2: ", filename
, lineno
);
8189 else if (program_name
)
8190 fprintf(stderr
, "%s: ", program_name
);
8193 fputs("fatal error: ", stderr
);
8198 fputs("warning: ", stderr
);
8200 case OUTPUT_WARNING
:
8201 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
8202 fprintf(stderr
, "warning [p %d, %.1f%c",
8203 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
8204 if (topdiv
!= curdiv
) {
8205 double fromtop1
= curdiv
->get_vertical_position().to_units()
8207 fprintf(stderr
, ", div `%s', %.1f%c",
8208 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
8210 fprintf(stderr
, "]: ");
8213 errprint(format
, arg1
, arg2
, arg3
);
8214 fputc('\n', stderr
);
8217 cleanup_and_exit(1);
8220 int warning(warning_type t
,
8226 if ((t
& warning_mask
) != 0) {
8227 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
8234 int output_warning(warning_type t
,
8240 if ((t
& warning_mask
) != 0) {
8241 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
8248 void error(const char *format
,
8253 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
8256 void fatal(const char *format
,
8261 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
8264 void fatal_with_file_and_line(const char *filename
, int lineno
,
8270 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
8271 errprint(format
, arg1
, arg2
, arg3
);
8272 fputc('\n', stderr
);
8274 cleanup_and_exit(1);
8277 void error_with_file_and_line(const char *filename
, int lineno
,
8283 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
8284 errprint(format
, arg1
, arg2
, arg3
);
8285 fputc('\n', stderr
);
8289 dictionary
charinfo_dictionary(501);
8291 charinfo
*get_charinfo(symbol nm
)
8293 void *p
= charinfo_dictionary
.lookup(nm
);
8295 return (charinfo
*)p
;
8296 charinfo
*cp
= new charinfo(nm
);
8297 (void)charinfo_dictionary
.lookup(nm
, cp
);
8301 int charinfo::next_index
= 0;
8303 charinfo::charinfo(symbol s
)
8304 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
8305 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8306 not_found(0), transparent_translate(1), translate_input(0),
8307 mode(CHAR_NORMAL
), nm(s
)
8309 index
= next_index
++;
8313 void charinfo::set_hyphenation_code(unsigned char c
)
8315 hyphenation_code
= c
;
8318 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
8322 if (hyphenation_code
!= 0)
8323 ci
->set_hyphenation_code(hyphenation_code
);
8324 if (asciify_code
!= 0)
8325 ci
->set_asciify_code(asciify_code
);
8326 else if (ascii_code
!= 0)
8327 ci
->set_asciify_code(ascii_code
);
8328 ci
->set_translation_input();
8330 special_translation
= TRANSLATE_NONE
;
8331 transparent_translate
= tt
;
8334 void charinfo::set_special_translation(int c
, int tt
)
8336 special_translation
= c
;
8338 transparent_translate
= tt
;
8341 void charinfo::set_ascii_code(unsigned char c
)
8346 void charinfo::set_asciify_code(unsigned char c
)
8351 macro
*charinfo::set_macro(macro
*m
)
8358 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
8366 void charinfo::set_number(int n
)
8372 int charinfo::get_number()
8374 assert(number
>= 0);
8378 symbol
UNNAMED_SYMBOL("---");
8380 // For numbered characters not between 0 and 255, we make a symbol out
8381 // of the number and store them in this dictionary.
8383 dictionary
numbered_charinfo_dictionary(11);
8385 charinfo
*get_charinfo_by_number(int n
)
8387 static charinfo
*number_table
[256];
8389 if (n
>= 0 && n
< 256) {
8390 charinfo
*ci
= number_table
[n
];
8392 ci
= new charinfo(UNNAMED_SYMBOL
);
8394 number_table
[n
] = ci
;
8399 symbol
ns(i_to_a(n
));
8400 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
8402 ci
= new charinfo(UNNAMED_SYMBOL
);
8404 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
8410 // This overrides the same function from libgroff; while reading font
8411 // definition files it puts single-letter glyph names into `charset_table'
8412 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8413 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8415 glyph
*name_to_glyph(const char *nm
)
8419 ci
= charset_table
[nm
[0] & 0xff];
8420 else if (nm
[0] == '\\' && nm
[2] == 0)
8421 ci
= get_charinfo(symbol(nm
+ 1));
8423 ci
= get_charinfo(symbol(nm
));
8424 return ci
->as_glyph();
8427 glyph
*number_to_glyph(int n
)
8429 return get_charinfo_by_number(n
)->as_glyph();
8432 const char *glyph_to_name(glyph
*g
)
8434 charinfo
*ci
= (charinfo
*)g
; // Every glyph is actually a charinfo.
8435 return (ci
->nm
!= UNNAMED_SYMBOL
? ci
->nm
.contents() : NULL
);