4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
27 #include "dictionary.h"
29 #include "stringclass.h"
38 #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 // \R, \s, or \S has been processed in
115 int old_have_input
= 0; // value of have_input right before \n
116 int tcommand_flag
= 0;
117 int safer_flag
= 1; // 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);
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 mode
= NO_ARGS
);
140 static symbol
read_long_escape_name(read_mode mode
= NO_ARGS
);
141 static void interpolate_string(symbol
);
142 static void interpolate_string_with_args(symbol
);
143 static void interpolate_macro(symbol
);
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
;
202 class input_iterator
{
205 input_iterator(int is_div
);
206 virtual ~input_iterator() {}
208 friend class input_stack
;
210 statem
*diversion_state
;
212 const unsigned char *ptr
;
213 const unsigned char *eptr
;
214 input_iterator
*next
;
216 virtual int fill(node
**);
218 virtual int has_args() { return 0; }
219 virtual int nargs() { return 0; }
220 virtual input_iterator
*get_arg(int) { return 0; }
221 virtual int get_location(int, const char **, int *) { return 0; }
222 virtual void backtrace() {}
223 virtual int set_location(const char *, int) { return 0; }
224 virtual int next_file(FILE *, const char *) { return 0; }
225 virtual void shift(int) {}
226 virtual int is_boundary() {return 0; }
227 virtual int is_file() { return 0; }
228 virtual int is_macro() { return 0; }
229 virtual void save_compatible_flag(int) {}
230 virtual int get_compatible_flag() { return 0; }
233 input_iterator::input_iterator()
234 : is_diversion(0), ptr(0), eptr(0)
238 input_iterator::input_iterator(int is_div
)
239 : is_diversion(is_div
), ptr(0), eptr(0)
243 int input_iterator::fill(node
**)
248 int input_iterator::peek()
253 inline int input_iterator::get(node
**p
)
255 return ptr
< eptr
? *ptr
++ : fill(p
);
258 class input_boundary
: public input_iterator
{
260 int is_boundary() { return 1; }
263 class input_return_boundary
: public input_iterator
{
265 int is_boundary() { return 2; }
268 class file_iterator
: public input_iterator
{
271 const char *filename
;
275 enum { BUF_SIZE
= 512 };
276 unsigned char buf
[BUF_SIZE
];
279 file_iterator(FILE *, const char *, int = 0);
283 int get_location(int, const char **, int *);
285 int set_location(const char *, int);
286 int next_file(FILE *, const char *);
290 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
291 : fp(f
), lineno(1), filename(fn
), popened(po
),
292 newline_flag(0), seen_escape(0)
294 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
297 the_output
->put_filename(fn
);
301 file_iterator::~file_iterator()
306 void file_iterator::close()
310 #ifndef POPEN_MISSING
313 #endif /* not POPEN_MISSING */
318 int file_iterator::is_file()
323 int file_iterator::next_file(FILE *f
, const char *s
)
337 int file_iterator::fill(node
**)
342 unsigned char *p
= buf
;
344 unsigned char *e
= p
+ BUF_SIZE
;
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()
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 popened
? "process" : "file");
400 int file_iterator::set_location(const char *f
, int ln
)
406 the_output
->put_filename(f
);
412 input_iterator nil_iterator
;
416 static int get(node
**);
418 static void push(input_iterator
*);
419 static input_iterator
*get_arg(int);
421 static int get_location(int, const char **, int *);
422 static int set_location(const char *, int);
423 static void backtrace();
424 static void backtrace_all();
425 static void next_file(FILE *, const char *);
426 static void end_file();
427 static void shift(int n
);
428 static void add_boundary();
429 static void add_return_boundary();
430 static int is_return_boundary();
431 static void remove_boundary();
432 static int get_level();
433 static int get_div_level();
434 static void increase_level();
435 static void decrease_level();
437 static void pop_macro();
438 static void save_compatible_flag(int);
439 static int get_compatible_flag();
440 static statem
*get_diversion_state();
441 static void check_end_diversion(input_iterator
*t
);
443 static int div_level
;
444 static statem
*diversion_state
;
446 static input_iterator
*top
;
448 static int finish_get(node
**);
449 static int finish_peek();
452 input_iterator
*input_stack::top
= &nil_iterator
;
453 int input_stack::level
= 0;
454 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
455 int input_stack::div_level
= 0;
456 statem
*input_stack::diversion_state
= NULL
;
460 inline int input_stack::get_level()
465 inline void input_stack::increase_level()
470 inline void input_stack::decrease_level()
475 inline int input_stack::get_div_level()
480 inline int input_stack::get(node
**np
)
482 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
484 old_have_input
= have_input
;
490 int input_stack::finish_get(node
**np
)
493 int c
= top
->fill(np
);
494 if (c
!= EOF
|| top
->is_boundary())
496 if (top
== &nil_iterator
)
498 input_iterator
*tem
= top
;
499 check_end_diversion(tem
);
500 #if defined(DEBUGGING)
502 if (tem
->is_diversion
)
504 "in diversion level = %d\n", input_stack::get_div_level());
509 if (top
->ptr
< top
->eptr
)
516 inline int input_stack::peek()
518 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
521 void input_stack::check_end_diversion(input_iterator
*t
)
523 if (t
->is_diversion
) {
525 diversion_state
= t
->diversion_state
;
529 int input_stack::finish_peek()
533 if (c
!= EOF
|| top
->is_boundary())
535 if (top
== &nil_iterator
)
537 input_iterator
*tem
= top
;
538 check_end_diversion(tem
);
542 if (top
->ptr
< top
->eptr
)
549 void input_stack::add_boundary()
551 push(new input_boundary
);
554 void input_stack::add_return_boundary()
556 push(new input_return_boundary
);
559 int input_stack::is_return_boundary()
561 return top
->is_boundary() == 2;
564 void input_stack::remove_boundary()
566 assert(top
->is_boundary());
567 input_iterator
*temp
= top
->next
;
568 check_end_diversion(top
);
575 void input_stack::push(input_iterator
*in
)
579 if (++level
> limit
&& limit
> 0)
580 fatal("input stack limit exceeded (probable infinite loop)");
583 if (top
->is_diversion
) {
585 in
->diversion_state
= diversion_state
;
586 diversion_state
= curenv
->construct_state(0);
587 #if defined(DEBUGGING)
589 curenv
->dump_troff_state();
594 #if defined(DEBUGGING)
596 if (top
->is_diversion
) {
598 "in diversion level = %d\n", input_stack::get_div_level());
604 statem
*get_diversion_state()
606 return input_stack::get_diversion_state();
609 statem
*input_stack::get_diversion_state()
611 if (diversion_state
== NULL
)
614 return new statem(diversion_state
);
617 input_iterator
*input_stack::get_arg(int i
)
620 for (p
= top
; p
!= 0; p
= p
->next
)
622 return p
->get_arg(i
);
626 void input_stack::shift(int n
)
628 for (input_iterator
*p
= top
; p
; p
= p
->next
)
635 int input_stack::nargs()
637 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
643 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
645 for (input_iterator
*p
= top
; p
; p
= p
->next
)
646 if (p
->get_location(allow_macro
, filenamep
, linenop
))
651 void input_stack::backtrace()
655 // only backtrace down to (not including) the topmost file
656 for (input_iterator
*p
= top
;
657 p
&& !p
->get_location(0, &f
, &n
);
662 void input_stack::backtrace_all()
664 for (input_iterator
*p
= top
; p
; p
= p
->next
)
668 int input_stack::set_location(const char *filename
, int lineno
)
670 for (input_iterator
*p
= top
; p
; p
= p
->next
)
671 if (p
->set_location(filename
, lineno
))
676 void input_stack::next_file(FILE *fp
, const char *s
)
679 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
680 if ((*pp
)->next_file(fp
, s
))
682 if (++level
> limit
&& limit
> 0)
683 fatal("input stack limit exceeded");
684 *pp
= new file_iterator(fp
, s
);
685 (*pp
)->next
= &nil_iterator
;
688 void input_stack::end_file()
690 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
691 if ((*pp
)->is_file()) {
692 input_iterator
*tem
= *pp
;
693 check_end_diversion(tem
);
701 void input_stack::clear()
704 while (top
!= &nil_iterator
) {
705 if (top
->is_boundary())
707 input_iterator
*tem
= top
;
708 check_end_diversion(tem
);
713 // Keep while_request happy.
714 for (; nboundaries
> 0; --nboundaries
)
715 add_return_boundary();
718 void input_stack::pop_macro()
723 if (top
->next
== &nil_iterator
)
725 if (top
->is_boundary())
727 is_macro
= top
->is_macro();
728 input_iterator
*tem
= top
;
729 check_end_diversion(tem
);
734 // Keep while_request happy.
735 for (; nboundaries
> 0; --nboundaries
)
736 add_return_boundary();
739 inline void input_stack::save_compatible_flag(int f
)
741 top
->save_compatible_flag(f
);
744 inline int input_stack::get_compatible_flag()
746 return top
->get_compatible_flag();
749 void backtrace_request()
751 input_stack::backtrace_all();
758 symbol nm
= get_long_name();
759 while (!tok
.newline() && !tok
.eof())
762 input_stack::end_file();
765 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
767 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
769 input_stack::next_file(fp
, nm
.contents());
777 if (!has_arg() || !get_integer(&n
))
779 input_stack::shift(n
);
783 static char get_char_for_escape_name(int allow_space
= 0)
788 copy_mode_error("end of input in escape name");
791 if (!invalid_input_char(c
))
796 input_stack::push(make_temp_iterator("\n"));
799 if (c
== ' ' && allow_space
)
805 copy_mode_error("%1 is not allowed in an escape name",
806 input_char_description(c
));
812 static symbol
read_two_char_escape_name()
815 buf
[0] = get_char_for_escape_name();
816 if (buf
[0] != '\0') {
817 buf
[1] = get_char_for_escape_name();
826 static symbol
read_long_escape_name(read_mode mode
)
828 int start_level
= input_stack::get_level();
829 char abuf
[ABUF_SIZE
];
831 int buf_size
= ABUF_SIZE
;
836 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
843 if (mode
== WITH_ARGS
&& c
== ' ')
845 if (i
+ 2 > buf_size
) {
847 buf
= new char[ABUF_SIZE
*2];
848 memcpy(buf
, abuf
, buf_size
);
849 buf_size
= ABUF_SIZE
*2;
853 buf
= new char[buf_size
*2];
854 memcpy(buf
, old_buf
, buf_size
);
859 if (c
== ']' && input_stack::get_level() == start_level
)
868 if (mode
!= ALLOW_EMPTY
)
869 copy_mode_error("empty escape name");
881 static symbol
read_escape_name(read_mode mode
)
883 char c
= get_char_for_escape_name();
887 return read_two_char_escape_name();
888 if (c
== '[' && !compatible_flag
)
889 return read_long_escape_name(mode
);
896 static symbol
read_increment_and_escape_name(int *incp
)
898 char c
= get_char_for_escape_name();
905 return read_two_char_escape_name();
908 return read_escape_name();
911 return read_escape_name();
913 if (!compatible_flag
) {
915 return read_long_escape_name();
926 static int get_copy(node
**nd
, int defining
)
929 int c
= input_stack::get(nd
);
930 if (c
== PUSH_GROFF_MODE
) {
931 input_stack::save_compatible_flag(compatible_flag
);
935 if (c
== PUSH_COMP_MODE
) {
936 input_stack::save_compatible_flag(compatible_flag
);
940 if (c
== POP_GROFFCOMP_MODE
) {
941 compatible_flag
= input_stack::get_compatible_flag();
944 if (c
== BEGIN_QUOTE
) {
945 input_stack::increase_level();
948 if (c
== END_QUOTE
) {
949 input_stack::decrease_level();
952 if (c
== ESCAPE_NEWLINE
) {
956 c
= input_stack::get(nd
);
957 } while (c
== ESCAPE_NEWLINE
);
959 if (c
!= escape_char
|| escape_char
<= 0)
961 c
= input_stack::peek();
966 (void)input_stack::get(0);
967 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
970 case '#': // Like \" but newline is ignored.
971 (void)input_stack::get(0);
972 while ((c
= input_stack::get(0)) != '\n')
978 (void)input_stack::get(0);
979 symbol s
= read_escape_name();
980 if (!(s
.is_null() || s
.is_empty()))
986 (void)input_stack::get(0);
987 symbol s
= read_escape_name(WITH_ARGS
);
988 if (!(s
.is_null() || s
.is_empty())) {
989 if (have_string_arg
) {
991 interpolate_string_with_args(s
);
994 interpolate_string(s
);
999 (void)input_stack::get(0);
1002 (void)input_stack::get(0);
1005 (void)input_stack::get(0);
1009 (void)input_stack::get(0);
1011 symbol s
= read_increment_and_escape_name(&inc
);
1012 if (!(s
.is_null() || s
.is_empty()))
1013 interpolate_number_reg(s
, inc
);
1018 (void)input_stack::get(0);
1019 symbol s
= read_escape_name();
1020 if (!(s
.is_null() || s
.is_empty()))
1021 interpolate_number_format(s
);
1025 (void)input_stack::get(0);
1029 (void)input_stack::get(0);
1030 symbol s
= read_escape_name();
1031 if (!(s
.is_null() || s
.is_empty()))
1032 interpolate_environment_variable(s
);
1036 (void)input_stack::get(0);
1038 return ESCAPE_NEWLINE
;
1041 (void)input_stack::get(0);
1042 return ESCAPE_SPACE
;
1044 (void)input_stack::get(0);
1045 return ESCAPE_TILDE
;
1047 (void)input_stack::get(0);
1048 return ESCAPE_COLON
;
1050 (void)input_stack::get(0);
1053 (void)input_stack::get(0);
1054 return ESCAPE_CIRCUMFLEX
;
1056 (void)input_stack::get(0);
1057 return ESCAPE_LEFT_BRACE
;
1059 (void)input_stack::get(0);
1060 return ESCAPE_RIGHT_BRACE
;
1062 (void)input_stack::get(0);
1063 return ESCAPE_LEFT_QUOTE
;
1065 (void)input_stack::get(0);
1066 return ESCAPE_RIGHT_QUOTE
;
1068 (void)input_stack::get(0);
1069 return ESCAPE_HYPHEN
;
1071 (void)input_stack::get(0);
1072 return ESCAPE_UNDERSCORE
;
1074 (void)input_stack::get(0);
1077 (void)input_stack::get(0);
1080 (void)input_stack::get(0);
1081 return ESCAPE_QUESTION
;
1083 (void)input_stack::get(0);
1084 return ESCAPE_AMPERSAND
;
1086 (void)input_stack::get(0);
1087 return ESCAPE_RIGHT_PARENTHESIS
;
1089 (void)input_stack::get(0);
1092 (void)input_stack::get(0);
1093 return ESCAPE_PERCENT
;
1095 if (c
== escape_char
) {
1096 (void)input_stack::get(0);
1105 class non_interpreted_char_node
: public node
{
1108 non_interpreted_char_node(unsigned char);
1110 int interpret(macro
*);
1117 int non_interpreted_char_node::same(node
*nd
)
1119 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1122 const char *non_interpreted_char_node::type()
1124 return "non_interpreted_char_node";
1127 int non_interpreted_char_node::force_tprint()
1132 int non_interpreted_char_node::is_tag()
1137 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1142 node
*non_interpreted_char_node::copy()
1144 return new non_interpreted_char_node(c
);
1147 int non_interpreted_char_node::interpret(macro
*mac
)
1153 static void do_width();
1154 static node
*do_non_interpreted();
1155 static node
*do_special();
1156 static node
*do_suppress(symbol nm
);
1157 static void do_register();
1159 dictionary
color_dictionary(501);
1161 static color
*lookup_color(symbol nm
)
1163 assert(!nm
.is_null());
1164 if (nm
== default_symbol
)
1165 return &default_color
;
1166 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1168 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1172 void do_glyph_color(symbol nm
)
1177 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1179 color
*tem
= lookup_color(nm
);
1181 curenv
->set_glyph_color(tem
);
1183 (void)color_dictionary
.lookup(nm
, new color(nm
));
1187 void do_fill_color(symbol nm
)
1192 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1194 color
*tem
= lookup_color(nm
);
1196 curenv
->set_fill_color(tem
);
1198 (void)color_dictionary
.lookup(nm
, new color(nm
));
1202 static unsigned int get_color_element(const char *scheme
, const char *col
)
1205 if (!get_number(&val
, 'f')) {
1206 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1211 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1214 if (val
> color::MAX_COLOR_VAL
+1) {
1215 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1216 // we change 0x10000 to 0xffff
1217 return color::MAX_COLOR_VAL
;
1219 return (unsigned int)val
;
1222 static color
*read_rgb(char end
= 0)
1224 symbol component
= do_get_long_name(0, end
);
1225 if (component
.is_null()) {
1226 warning(WARN_COLOR
, "missing rgb color values");
1229 const char *s
= component
.contents();
1230 color
*col
= new color
;
1232 if (!col
->read_rgb(s
)) {
1233 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1240 input_stack::push(make_temp_iterator(" "));
1241 input_stack::push(make_temp_iterator(s
));
1243 unsigned int r
= get_color_element("rgb color", "red component");
1244 unsigned int g
= get_color_element("rgb color", "green component");
1245 unsigned int b
= get_color_element("rgb color", "blue component");
1246 col
->set_rgb(r
, g
, b
);
1251 static color
*read_cmy(char end
= 0)
1253 symbol component
= do_get_long_name(0, end
);
1254 if (component
.is_null()) {
1255 warning(WARN_COLOR
, "missing cmy color values");
1258 const char *s
= component
.contents();
1259 color
*col
= new color
;
1261 if (!col
->read_cmy(s
)) {
1262 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1269 input_stack::push(make_temp_iterator(" "));
1270 input_stack::push(make_temp_iterator(s
));
1272 unsigned int c
= get_color_element("cmy color", "cyan component");
1273 unsigned int m
= get_color_element("cmy color", "magenta component");
1274 unsigned int y
= get_color_element("cmy color", "yellow component");
1275 col
->set_cmy(c
, m
, y
);
1280 static color
*read_cmyk(char end
= 0)
1282 symbol component
= do_get_long_name(0, end
);
1283 if (component
.is_null()) {
1284 warning(WARN_COLOR
, "missing cmyk color values");
1287 const char *s
= component
.contents();
1288 color
*col
= new color
;
1290 if (!col
->read_cmyk(s
)) {
1291 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1298 input_stack::push(make_temp_iterator(" "));
1299 input_stack::push(make_temp_iterator(s
));
1301 unsigned int c
= get_color_element("cmyk color", "cyan component");
1302 unsigned int m
= get_color_element("cmyk color", "magenta component");
1303 unsigned int y
= get_color_element("cmyk color", "yellow component");
1304 unsigned int k
= get_color_element("cmyk color", "black component");
1305 col
->set_cmyk(c
, m
, y
, k
);
1310 static color
*read_gray(char end
= 0)
1312 symbol component
= do_get_long_name(0, end
);
1313 if (component
.is_null()) {
1314 warning(WARN_COLOR
, "missing gray values");
1317 const char *s
= component
.contents();
1318 color
*col
= new color
;
1320 if (!col
->read_gray(s
)) {
1321 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1328 input_stack::push(make_temp_iterator("\n"));
1329 input_stack::push(make_temp_iterator(s
));
1331 unsigned int g
= get_color_element("gray", "gray value");
1337 static void activate_color()
1340 if (has_arg() && get_integer(&n
))
1341 color_flag
= n
!= 0;
1347 static void define_color()
1349 symbol color_name
= get_long_name(1);
1350 if (color_name
.is_null()) {
1354 if (color_name
== default_symbol
) {
1355 warning(WARN_COLOR
, "default color can't be redefined");
1359 symbol style
= get_long_name(1);
1360 if (style
.is_null()) {
1365 if (strcmp(style
.contents(), "rgb") == 0)
1367 else if (strcmp(style
.contents(), "cmyk") == 0)
1369 else if (strcmp(style
.contents(), "gray") == 0)
1371 else if (strcmp(style
.contents(), "grey") == 0)
1373 else if (strcmp(style
.contents(), "cmy") == 0)
1377 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1383 col
->nm
= color_name
;
1384 (void)color_dictionary
.lookup(color_name
, col
);
1389 static node
*do_overstrike()
1392 overstrike_node
*on
= new overstrike_node
;
1393 int start_level
= input_stack::get_level();
1397 if (tok
.newline() || tok
.eof()) {
1398 warning(WARN_DELIM
, "missing closing delimiter");
1399 input_stack::push(make_temp_iterator("\n"));
1403 && (compatible_flag
|| input_stack::get_level() == start_level
))
1405 charinfo
*ci
= tok
.get_char(1);
1407 node
*n
= curenv
->make_char_node(ci
);
1415 static node
*do_bracket()
1418 bracket_node
*bn
= new bracket_node
;
1420 int start_level
= input_stack::get_level();
1424 warning(WARN_DELIM
, "missing closing delimiter");
1427 if (tok
.newline()) {
1428 warning(WARN_DELIM
, "missing closing delimiter");
1429 input_stack::push(make_temp_iterator("\n"));
1433 && (compatible_flag
|| input_stack::get_level() == start_level
))
1435 charinfo
*ci
= tok
.get_char(1);
1437 node
*n
= curenv
->make_char_node(ci
);
1445 static int do_name_test()
1449 int start_level
= input_stack::get_level();
1454 if (tok
.newline() || tok
.eof()) {
1455 warning(WARN_DELIM
, "missing closing delimiter");
1456 input_stack::push(make_temp_iterator("\n"));
1460 && (compatible_flag
|| input_stack::get_level() == start_level
))
1466 return some_char
&& !bad_char
;
1469 static int do_expr_test()
1473 int start_level
= input_stack::get_level();
1474 if (!start
.delimiter(1))
1477 // disable all warning and error messages temporarily
1478 int saved_warning_mask
= warning_mask
;
1479 int saved_inhibit_errors
= inhibit_errors
;
1483 int result
= get_number_rigidly(&dummy
, 'u');
1484 warning_mask
= saved_warning_mask
;
1485 inhibit_errors
= saved_inhibit_errors
;
1486 if (tok
== start
&& input_stack::get_level() == start_level
)
1488 // ignore everything up to the delimiter in case we aren't right there
1491 if (tok
.newline() || tok
.eof()) {
1492 warning(WARN_DELIM
, "missing closing delimiter");
1493 input_stack::push(make_temp_iterator("\n"));
1496 if (tok
== start
&& input_stack::get_level() == start_level
)
1503 static node
*do_zero_width()
1507 int start_level
= input_stack::get_level();
1508 environment
env(curenv
);
1509 environment
*oldenv
= curenv
;
1513 if (tok
.newline() || tok
.eof()) {
1514 error("missing closing delimiter");
1518 && (compatible_flag
|| input_stack::get_level() == start_level
))
1523 node
*rev
= env
.extract_output_line();
1531 return new zero_width_node(n
);
1536 // It's undesirable for \Z to change environments, because then
1537 // \n(.w won't work as expected.
1539 static node
*do_zero_width()
1541 node
*rev
= new dummy_node
;
1544 int start_level
= input_stack::get_level();
1547 if (tok
.newline() || tok
.eof()) {
1548 warning(WARN_DELIM
, "missing closing delimiter");
1549 input_stack::push(make_temp_iterator("\n"));
1553 && (compatible_flag
|| input_stack::get_level() == start_level
))
1555 if (!tok
.add_to_node_list(&rev
))
1556 error("invalid token in argument to \\Z");
1565 return new zero_width_node(n
);
1570 token_node
*node::get_token_node()
1575 class token_node
: public node
{
1578 token_node(const token
&t
);
1580 token_node
*get_token_node();
1587 token_node::token_node(const token
&t
) : tk(t
)
1591 node
*token_node::copy()
1593 return new token_node(tk
);
1596 token_node
*token_node::get_token_node()
1601 int token_node::same(node
*nd
)
1603 return tk
== ((token_node
*)nd
)->tk
;
1606 const char *token_node::type()
1608 return "token_node";
1611 int token_node::force_tprint()
1616 int token_node::is_tag()
1621 token::token() : nd(0), type(TOKEN_EMPTY
)
1630 token::token(const token
&t
)
1631 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1633 // Use two statements to work around bug in SGI C++.
1635 nd
= tem
? tem
->copy() : 0;
1638 void token::operator=(const token
&t
)
1642 // Use two statements to work around bug in SGI C++.
1644 nd
= tem
? tem
->copy() : 0;
1661 return !tok
.newline();
1664 void token::make_space()
1669 void token::make_newline()
1671 type
= TOKEN_NEWLINE
;
1683 int cc
= input_stack::get(&n
);
1684 if (cc
!= escape_char
|| escape_char
== 0) {
1687 case PUSH_GROFF_MODE
:
1688 input_stack::save_compatible_flag(compatible_flag
);
1689 compatible_flag
= 0;
1691 case PUSH_COMP_MODE
:
1692 input_stack::save_compatible_flag(compatible_flag
);
1693 compatible_flag
= 1;
1695 case POP_GROFFCOMP_MODE
:
1696 compatible_flag
= input_stack::get_compatible_flag();
1699 input_stack::increase_level();
1702 input_stack::decrease_level();
1707 case TRANSPARENT_FILE_REQUEST
:
1709 case COPY_FILE_REQUEST
:
1711 case VJUSTIFY_REQUEST
:
1713 type
= TOKEN_REQUEST
;
1717 type
= TOKEN_BEGIN_TRAP
;
1720 type
= TOKEN_END_TRAP
;
1722 case LAST_PAGE_EJECTOR
:
1723 seen_last_page_ejector
= 1;
1726 type
= TOKEN_PAGE_EJECTOR
;
1728 case ESCAPE_PERCENT
:
1730 type
= TOKEN_HYPHEN_INDICATOR
;
1734 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1738 type
= TOKEN_STRETCHABLE_SPACE
;
1742 type
= TOKEN_ZERO_WIDTH_BREAK
;
1746 type
= TOKEN_ESCAPE
;
1749 goto handle_escape_char
;
1753 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1754 curenv
->get_fill_color());
1756 case ESCAPE_CIRCUMFLEX
:
1759 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1760 curenv
->get_fill_color());
1762 case ESCAPE_NEWLINE
:
1765 case ESCAPE_LEFT_BRACE
:
1767 type
= TOKEN_LEFT_BRACE
;
1769 case ESCAPE_RIGHT_BRACE
:
1771 type
= TOKEN_RIGHT_BRACE
;
1773 case ESCAPE_LEFT_QUOTE
:
1775 type
= TOKEN_SPECIAL
;
1778 case ESCAPE_RIGHT_QUOTE
:
1780 type
= TOKEN_SPECIAL
;
1785 type
= TOKEN_SPECIAL
;
1788 case ESCAPE_UNDERSCORE
:
1790 type
= TOKEN_SPECIAL
;
1795 type
= TOKEN_INTERRUPT
;
1799 type
= TOKEN_TRANSPARENT
;
1801 case ESCAPE_QUESTION
:
1803 nd
= do_non_interpreted();
1809 case ESCAPE_AMPERSAND
:
1813 case ESCAPE_RIGHT_PARENTHESIS
:
1814 ESCAPE_RIGHT_PARENTHESIS
:
1815 type
= TOKEN_TRANSPARENT_DUMMY
;
1818 type
= TOKEN_BACKSPACE
;
1827 type
= TOKEN_NEWLINE
;
1830 type
= TOKEN_LEADER
;
1835 token_node
*tn
= n
->get_token_node();
1854 cc
= input_stack::get(&n
);
1857 nm
= read_two_char_escape_name();
1858 type
= TOKEN_SPECIAL
;
1862 error("end of input after escape character");
1865 goto ESCAPE_LEFT_QUOTE
;
1867 goto ESCAPE_RIGHT_QUOTE
;
1871 goto ESCAPE_UNDERSCORE
;
1873 goto ESCAPE_PERCENT
;
1877 nd
= new hmotion_node(curenv
->get_digit_width(),
1878 curenv
->get_fill_color());
1884 goto ESCAPE_CIRCUMFLEX
;
1886 type
= TOKEN_ITALIC_CORRECTION
;
1890 nd
= new left_italic_corrected_node
;
1893 goto ESCAPE_AMPERSAND
;
1895 goto ESCAPE_RIGHT_PARENTHESIS
;
1899 goto ESCAPE_QUESTION
;
1905 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1908 type
= TOKEN_NEWLINE
;
1912 case '#': // Like \" but newline is ignored.
1913 while ((cc
= input_stack::get(0)) != '\n')
1921 symbol s
= read_escape_name();
1922 if (!(s
.is_null() || s
.is_empty()))
1928 symbol s
= read_escape_name(WITH_ARGS
);
1929 if (!(s
.is_null() || s
.is_empty())) {
1930 if (have_string_arg
) {
1931 have_string_arg
= 0;
1932 interpolate_string_with_args(s
);
1935 interpolate_string(s
);
1940 nd
= new non_interpreted_char_node('\001');
1944 c
= '0' + do_name_test();
1952 c
= '0' + do_expr_test();
1958 nm
= get_delim_name();
1961 type
= TOKEN_SPECIAL
;
1965 nd
= new vmotion_node(curenv
->get_size() / 2,
1966 curenv
->get_fill_color());
1969 nd
= read_draw_node();
1977 goto handle_escape_char
;
1980 symbol s
= read_escape_name(ALLOW_EMPTY
);
1984 for (p
= s
.contents(); *p
!= '\0'; p
++)
1987 if (*p
|| s
.is_empty())
1988 curenv
->set_font(s
);
1990 curenv
->set_font(atoi(s
.contents()));
1991 if (!compatible_flag
)
1997 symbol s
= read_escape_name(ALLOW_EMPTY
);
2000 curenv
->set_family(s
);
2006 symbol s
= read_escape_name();
2007 if (!(s
.is_null() || s
.is_empty()))
2008 interpolate_number_format(s
);
2012 if (!get_delim_number(&x
, 'm'))
2015 nd
= new hmotion_node(x
, curenv
->get_fill_color());
2018 // don't take height increments relative to previous height if
2019 // in compatibility mode
2020 if (!compatible_flag
&& curenv
->get_char_height())
2022 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
2023 curenv
->set_char_height(x
);
2027 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
2028 curenv
->set_char_height(x
);
2030 if (!compatible_flag
)
2034 nm
= read_escape_name();
2035 if (nm
.is_null() || nm
.is_empty())
2037 type
= TOKEN_MARK_INPUT
;
2043 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
2046 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
2048 node
*char_node
= curenv
->make_char_node(s
);
2050 nd
= new hline_node(x
, char_node
);
2052 nd
= new vline_node(x
, char_node
);
2056 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
2057 if (!compatible_flag
)
2061 do_fill_color(read_escape_name(ALLOW_EMPTY
));
2062 if (!compatible_flag
)
2068 symbol s
= read_increment_and_escape_name(&inc
);
2069 if (!(s
.is_null() || s
.is_empty()))
2070 interpolate_number_reg(s
, inc
);
2074 if (!get_delim_number(&val
, 0))
2076 type
= TOKEN_NUMBERED_CHAR
;
2079 nd
= do_overstrike();
2083 nd
= do_suppress(read_escape_name());
2089 type
= TOKEN_SPREAD
;
2093 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
2097 if (!compatible_flag
)
2102 curenv
->set_size(x
);
2103 if (!compatible_flag
)
2107 if (get_delim_number(&x
, 0))
2108 curenv
->set_char_slant(x
);
2109 if (!compatible_flag
)
2114 nd
= new non_interpreted_char_node('\t');
2118 nd
= new vmotion_node(-curenv
->get_size() / 2,
2119 curenv
->get_fill_color());
2122 if (!get_delim_number(&x
, 'v'))
2125 nd
= new vmotion_node(x
, curenv
->get_fill_color());
2129 symbol s
= read_escape_name();
2130 if (!(s
.is_null() || s
.is_empty()))
2131 interpolate_environment_variable(s
);
2138 if (!get_delim_number(&x
, 'v'))
2141 nd
= new extra_size_node(x
);
2151 symbol s
= read_escape_name();
2152 if (s
.is_null() || s
.is_empty())
2154 request_or_macro
*p
= lookup_request(s
);
2155 macro
*m
= p
->to_macro();
2157 error("can't transparently throughput a request");
2160 nd
= new special_node(*m
);
2167 if (type
== TOKEN_NODE
)
2168 nd
= new zero_width_node(nd
);
2170 charinfo
*ci
= get_char(1);
2173 node
*gn
= curenv
->make_char_node(ci
);
2176 nd
= new zero_width_node(gn
);
2182 nd
= do_zero_width();
2188 goto ESCAPE_LEFT_BRACE
;
2190 goto ESCAPE_RIGHT_BRACE
;
2194 if (!compatible_flag
) {
2195 symbol s
= read_long_escape_name(WITH_ARGS
);
2196 if (s
.is_null() || s
.is_empty())
2198 if (have_string_arg
) {
2199 have_string_arg
= 0;
2200 nm
= composite_glyph_name(s
);
2203 const char *gn
= check_unicode_name(s
.contents());
2205 const char *gn_decomposed
= decompose_unicode(gn
);
2207 gn
= &gn_decomposed
[1];
2208 const char *groff_gn
= unicode_to_glyph_name(gn
);
2210 nm
= symbol(groff_gn
);
2212 char *buf
= new char[strlen(gn
) + 1 + 1];
2220 nm
= symbol(s
.contents());
2222 type
= TOKEN_SPECIAL
;
2225 goto handle_normal_char
;
2227 if (cc
!= escape_char
&& cc
!= '.')
2228 warning(WARN_ESCAPE
, "escape character ignored before %1",
2229 input_char_description(cc
));
2230 goto handle_normal_char
;
2236 int token::operator==(const token
&t
)
2245 case TOKEN_NUMBERED_CHAR
:
2246 return val
== t
.val
;
2252 int token::operator!=(const token
&t
)
2254 return !(*this == t
);
2257 // is token a suitable delimiter (like ')?
2259 int token::delimiter(int err
)
2288 error("cannot use character `%1' as a starting delimiter", char(c
));
2295 case TOKEN_STRETCHABLE_SPACE
:
2296 case TOKEN_UNSTRETCHABLE_SPACE
:
2300 error("cannot use %1 as a starting delimiter", description());
2307 const char *token::description()
2311 case TOKEN_BACKSPACE
:
2312 return "a backspace character";
2323 case TOKEN_HYPHEN_INDICATOR
:
2325 case TOKEN_INTERRUPT
:
2327 case TOKEN_ITALIC_CORRECTION
:
2330 return "a leader character";
2331 case TOKEN_LEFT_BRACE
:
2333 case TOKEN_MARK_INPUT
:
2339 case TOKEN_NUMBERED_CHAR
:
2341 case TOKEN_RIGHT_BRACE
:
2346 return "a special character";
2349 case TOKEN_STRETCHABLE_SPACE
:
2351 case TOKEN_UNSTRETCHABLE_SPACE
:
2354 return "a tab character";
2355 case TOKEN_TRANSPARENT
:
2357 case TOKEN_TRANSPARENT_DUMMY
:
2359 case TOKEN_ZERO_WIDTH_BREAK
:
2362 return "end of input";
2366 return "a magic token";
2371 while (!tok
.newline())
2382 if (has_arg() && get_integer(&n
))
2383 compatible_flag
= n
!= 0;
2385 compatible_flag
= 1;
2389 static void empty_name_warning(int required
)
2391 if (tok
.newline() || tok
.eof()) {
2393 warning(WARN_MISSING
, "missing name");
2395 else if (tok
.right_brace() || tok
.tab()) {
2396 const char *start
= tok
.description();
2399 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2400 if (!tok
.newline() && !tok
.eof())
2401 error("%1 is not allowed before an argument", start
);
2403 warning(WARN_MISSING
, "missing name");
2406 error("name expected (got %1)", tok
.description());
2408 error("name expected (got %1): treated as missing", tok
.description());
2411 static void non_empty_name_warning()
2413 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2414 && !tok
.right_brace()
2415 // We don't want to give a warning for .el\{
2416 && !tok
.left_brace())
2417 error("%1 is not allowed in a name", tok
.description());
2420 symbol
get_name(int required
)
2422 if (compatible_flag
) {
2425 if ((buf
[0] = tok
.ch()) != 0) {
2427 if ((buf
[1] = tok
.ch()) != 0) {
2432 non_empty_name_warning();
2436 empty_name_warning(required
);
2441 return get_long_name(required
);
2444 symbol
get_long_name(int required
)
2446 return do_get_long_name(required
, 0);
2449 static symbol
do_get_long_name(int required
, char end
)
2453 char abuf
[ABUF_SIZE
];
2455 int buf_size
= ABUF_SIZE
;
2458 // If end != 0 we normally have to append a null byte
2459 if (i
+ 2 > buf_size
) {
2461 buf
= new char[ABUF_SIZE
*2];
2462 memcpy(buf
, abuf
, buf_size
);
2463 buf_size
= ABUF_SIZE
*2;
2466 char *old_buf
= buf
;
2467 buf
= new char[buf_size
*2];
2468 memcpy(buf
, old_buf
, buf_size
);
2473 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2479 empty_name_warning(required
);
2482 if (end
&& buf
[i
] == end
)
2485 non_empty_name_warning();
2498 topdiv
->set_last_page();
2499 if (!end_macro_name
.is_null()) {
2500 spring_trap(end_macro_name
);
2502 process_input_stack();
2504 curenv
->final_break();
2506 process_input_stack();
2508 if (topdiv
->get_page_length() > 0) {
2510 topdiv
->set_ejecting();
2511 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2512 input_stack::push(make_temp_iterator((char *)buf
));
2513 topdiv
->space(topdiv
->get_page_length(), 1);
2515 process_input_stack();
2516 seen_last_page_ejector
= 1; // should be set already
2517 topdiv
->set_ejecting();
2518 push_page_ejector();
2519 topdiv
->space(topdiv
->get_page_length(), 1);
2521 process_input_stack();
2523 // This will only happen if a trap-invoked macro starts a diversion,
2524 // or if vertical position traps have been disabled.
2525 cleanup_and_exit(0);
2528 // This implements .ex. The input stack must be cleared before calling
2533 input_stack::clear();
2540 void return_macro_request()
2542 if (has_arg() && tok
.ch())
2543 input_stack::pop_macro();
2544 input_stack::pop_macro();
2550 end_macro_name
= get_name();
2554 void blank_line_macro()
2556 blank_line_macro_name
= get_name();
2560 static void trapping_blank_line()
2562 if (!blank_line_macro_name
.is_null())
2563 spring_trap(blank_line_macro_name
);
2570 int old_compatible_flag
= compatible_flag
;
2571 compatible_flag
= 0;
2572 symbol nm
= get_name();
2576 interpolate_macro(nm
);
2577 compatible_flag
= old_compatible_flag
;
2580 inline int possibly_handle_first_page_transition()
2582 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2583 handle_first_page_transition();
2590 static int transparent_translate(int cc
)
2592 if (!invalid_input_char(cc
)) {
2593 charinfo
*ci
= charset_table
[cc
];
2594 switch (ci
->get_special_translation(1)) {
2595 case charinfo::TRANSLATE_SPACE
:
2597 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2598 return ESCAPE_TILDE
;
2599 case charinfo::TRANSLATE_DUMMY
:
2600 return ESCAPE_AMPERSAND
;
2601 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2602 return ESCAPE_PERCENT
;
2604 // This is really ugly.
2605 ci
= ci
->get_translation(1);
2607 int c
= ci
->get_ascii_code();
2610 error("can't translate %1 to special character `%2'"
2611 " in transparent throughput",
2612 input_char_description(cc
),
2620 struct int_stack_element
{
2622 int_stack_element
*next
;
2632 int_stack::int_stack()
2637 int_stack::~int_stack()
2640 int_stack_element
*temp
= top
;
2646 int int_stack::is_empty()
2651 void int_stack::push(int n
)
2653 int_stack_element
*p
= new int_stack_element
;
2659 int int_stack::pop()
2662 int_stack_element
*p
= top
;
2669 int node::reread(int *)
2674 int global_diverted_space
= 0;
2676 int diverted_space_node::reread(int *bolp
)
2678 global_diverted_space
= 1;
2679 if (curenv
->get_fill())
2680 trapping_blank_line();
2683 global_diverted_space
= 0;
2688 int diverted_copy_file_node::reread(int *bolp
)
2690 curdiv
->copy_file(filename
.contents());
2695 int word_space_node::reread(int *)
2698 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2699 curenv
->space(w
->width
, w
->sentence_width
);
2706 int unbreakable_space_node::reread(int *)
2711 int hmotion_node::reread(int *)
2713 if (unformat
&& was_tab
) {
2714 curenv
->handle_tab(0);
2721 void process_input_stack()
2723 int_stack trap_bol_stack
;
2726 int suppress_next
= 0;
2728 case token::TOKEN_CHAR
:
2730 unsigned char ch
= tok
.c
;
2731 if (bol
&& !have_input
2732 && (ch
== curenv
->control_char
2733 || ch
== curenv
->no_break_control_char
)) {
2734 break_flag
= ch
== curenv
->control_char
;
2735 // skip tabs as well as spaces here
2738 } while (tok
.white_space());
2739 symbol nm
= get_name();
2740 #if defined(DEBUGGING)
2742 if (! nm
.is_null()) {
2743 if (strcmp(nm
.contents(), "test") == 0) {
2744 fprintf(stderr
, "found it!\n");
2747 fprintf(stderr
, "interpreting [%s]", nm
.contents());
2748 if (strcmp(nm
.contents(), "di") == 0 && topdiv
!= curdiv
)
2749 fprintf(stderr
, " currently in diversion: %s",
2750 curdiv
->get_diversion_name());
2751 fprintf(stderr
, "\n");
2759 interpolate_macro(nm
);
2760 #if defined(DEBUGGING)
2762 fprintf(stderr
, "finished interpreting [%s] and environment state is\n", nm
.contents());
2763 curenv
->dump_troff_state();
2770 if (possibly_handle_first_page_transition())
2774 #if defined(DEBUGGING)
2776 fprintf(stderr
, "found [%c]\n", ch
); fflush(stderr
);
2779 curenv
->add_char(charset_table
[ch
]);
2781 if (tok
.type
!= token::TOKEN_CHAR
)
2791 case token::TOKEN_TRANSPARENT
:
2794 if (possibly_handle_first_page_transition())
2803 curdiv
->transparent_output(transparent_translate(cc
));
2805 curdiv
->transparent_output(n
);
2806 } while (cc
!= '\n' && cc
!= EOF
);
2808 curdiv
->transparent_output('\n');
2813 case token::TOKEN_NEWLINE
:
2815 if (bol
&& !old_have_input
2816 && !curenv
->get_prev_line_interrupted())
2817 trapping_blank_line();
2824 case token::TOKEN_REQUEST
:
2826 int request_code
= tok
.c
;
2828 switch (request_code
) {
2832 case COPY_FILE_REQUEST
:
2835 case TRANSPARENT_FILE_REQUEST
:
2839 case VJUSTIFY_REQUEST
:
2850 case token::TOKEN_SPACE
:
2852 if (possibly_handle_first_page_transition())
2854 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2856 // save space_width now so that it isn't changed by \f or \s
2857 // which we wouldn't notice here
2858 hunits space_width
= curenv
->get_space_width();
2860 nspaces
+= tok
.nspaces();
2862 } while (tok
.space());
2864 trapping_blank_line();
2868 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2869 curenv
->get_fill_color()));
2879 case token::TOKEN_EOF
:
2881 case token::TOKEN_NODE
:
2883 if (possibly_handle_first_page_transition())
2885 else if (tok
.nd
->reread(&bol
)) {
2890 curenv
->add_node(tok
.nd
);
2893 curenv
->possibly_break_line(1);
2897 case token::TOKEN_PAGE_EJECTOR
:
2899 continue_page_eject();
2900 // I think we just want to preserve bol.
2904 case token::TOKEN_BEGIN_TRAP
:
2906 trap_bol_stack
.push(bol
);
2911 case token::TOKEN_END_TRAP
:
2913 if (trap_bol_stack
.is_empty())
2914 error("spurious end trap token detected!");
2916 bol
= trap_bol_stack
.pop();
2919 /* I'm not totally happy about this. But I can't think of any other
2920 way to do it. Doing an output_pending_lines() whenever a
2921 TOKEN_END_TRAP is detected doesn't work: for example,
2934 a\%very\%very\%long\%word
2936 will print all but the first lines from the word immediately
2937 after the footer, rather than on the next page. */
2939 if (trap_bol_stack
.is_empty())
2940 curenv
->output_pending_lines();
2952 trap_sprung_flag
= 0;
2956 #ifdef WIDOW_CONTROL
2958 void flush_pending_lines()
2960 while (!tok
.newline() && !tok
.eof())
2962 curenv
->output_pending_lines();
2966 #endif /* WIDOW_CONTROL */
2968 request_or_macro::request_or_macro()
2972 macro
*request_or_macro::to_macro()
2977 request::request(REQUEST_FUNCP pp
) : p(pp
)
2981 void request::invoke(symbol
)
2987 enum { SIZE
= 128 };
2988 unsigned char s
[SIZE
];
2993 char_block::char_block()
3002 void append(unsigned char);
3003 void set(unsigned char, int);
3004 unsigned char get(int);
3011 friend class macro_header
;
3012 friend class string_iterator
;
3015 char_list::char_list()
3016 : ptr(0), len(0), head(0), tail(0)
3020 char_list::~char_list()
3023 char_block
*tem
= head
;
3029 int char_list::length()
3034 void char_list::append(unsigned char c
)
3037 head
= tail
= new char_block
;
3041 if (ptr
>= tail
->s
+ char_block::SIZE
) {
3042 tail
->next
= new char_block
;
3051 void char_list::set(unsigned char c
, int offset
)
3053 assert(len
> offset
);
3054 // optimization for access at the end
3055 int boundary
= len
- len
% char_block::SIZE
;
3056 if (offset
>= boundary
) {
3057 *(tail
->s
+ offset
- boundary
) = c
;
3060 char_block
*tem
= head
;
3063 l
+= char_block::SIZE
;
3065 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
3072 unsigned char char_list::get(int offset
)
3074 assert(len
> offset
);
3075 // optimization for access at the end
3076 int boundary
= len
- len
% char_block::SIZE
;
3077 if (offset
>= boundary
)
3078 return *(tail
->s
+ offset
- boundary
);
3079 char_block
*tem
= head
;
3082 l
+= char_block::SIZE
;
3084 return *(tem
->s
+ offset
% char_block::SIZE
);
3095 void append(node
*);
3099 friend class macro_header
;
3100 friend class string_iterator
;
3103 void node_list::append(node
*n
)
3111 tail
= tail
->next
= n
;
3115 int node_list::length()
3118 for (node
*n
= head
; n
!= 0; n
= n
->next
)
3123 node_list::node_list()
3128 node
*node_list::extract()
3135 node_list::~node_list()
3137 delete_node_list(head
);
3140 class macro_header
{
3145 macro_header() { count
= 1; }
3146 macro_header
*copy(int);
3151 if (p
!= 0 && --(p
->count
) <= 0)
3158 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3167 macro::macro(const macro
&m
)
3168 : filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
3169 empty_macro(m
.empty_macro
), is_a_diversion(m
.is_a_diversion
), p(m
.p
)
3175 macro::macro(int is_div
)
3176 : is_a_diversion(is_div
)
3178 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3187 int macro::is_diversion()
3189 return is_a_diversion
;
3192 macro
¯o::operator=(const macro
&m
)
3194 // don't assign object
3197 if (p
!= 0 && --(p
->count
) <= 0)
3200 filename
= m
.filename
;
3203 empty_macro
= m
.empty_macro
;
3204 is_a_diversion
= m
.is_a_diversion
;
3208 void macro::append(unsigned char c
)
3212 p
= new macro_header
;
3213 if (p
->cl
.length() != len
) {
3214 macro_header
*tem
= p
->copy(len
);
3215 if (--(p
->count
) <= 0)
3221 if (c
!= PUSH_GROFF_MODE
&& c
!= PUSH_COMP_MODE
&& c
!= POP_GROFFCOMP_MODE
)
3225 void macro::set(unsigned char c
, int offset
)
3229 p
->cl
.set(c
, offset
);
3232 unsigned char macro::get(int offset
)
3235 return p
->cl
.get(offset
);
3243 void macro::append_str(const char *s
)
3248 while (s
[i
] != (char)0) {
3255 void macro::append(node
*n
)
3259 p
= new macro_header
;
3260 if (p
->cl
.length() != len
) {
3261 macro_header
*tem
= p
->copy(len
);
3262 if (--(p
->count
) <= 0)
3272 void macro::append_unsigned(unsigned int i
)
3274 unsigned int j
= i
/ 10;
3277 append(((unsigned char)(((int)'0') + i
% 10)));
3280 void macro::append_int(int i
)
3286 append_unsigned((unsigned int)i
);
3289 void macro::print_size()
3291 errprint("%1", len
);
3294 // make a copy of the first n bytes
3296 macro_header
*macro_header::copy(int n
)
3298 macro_header
*p
= new macro_header
;
3299 char_block
*bp
= cl
.head
;
3300 unsigned char *ptr
= bp
->s
;
3303 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3307 unsigned char c
= *ptr
++;
3310 p
->nl
.append(nd
->copy());
3319 object_dictionary_iterator
iter(request_dictionary
);
3320 request_or_macro
*rm
;
3322 while (iter
.get(&s
, (object
**)&rm
)) {
3323 assert(!s
.is_null());
3324 macro
*m
= rm
->to_macro();
3326 errprint("%1\t", s
.contents());
3335 class string_iterator
: public input_iterator
{
3337 const char *how_invoked
;
3341 int count
; // of characters remaining
3343 int saved_compatible_flag
;
3348 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3351 int get_location(int, const char **, int *);
3353 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3354 int get_compatible_flag() { return saved_compatible_flag
; }
3358 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3359 : input_iterator(m
.is_a_diversion
), mac(m
), how_invoked(p
), newline_flag(0),
3364 bp
= mac
.p
->cl
.head
;
3365 nd
= mac
.p
->nl
.head
;
3375 string_iterator::string_iterator()
3386 int string_iterator::is_diversion()
3388 return mac
.is_diversion();
3391 int string_iterator::fill(node
**np
)
3398 const unsigned char *p
= eptr
;
3399 if (p
>= bp
->s
+ char_block::SIZE
) {
3407 (*np
)->div_nest_level
= input_stack::get_div_level();
3409 (*np
)->div_nest_level
= 0;
3416 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3421 unsigned char c
= *p
;
3422 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3436 int string_iterator::peek()
3440 const unsigned char *p
= eptr
;
3441 if (p
>= bp
->s
+ char_block::SIZE
) {
3447 int string_iterator::get_location(int allow_macro
,
3448 const char **filep
, int *linep
)
3452 if (mac
.filename
== 0)
3454 *filep
= mac
.filename
;
3455 *linep
= mac
.lineno
+ lineno
- 1;
3459 void string_iterator::backtrace()
3462 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3465 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3467 errprint(": %1\n", how_invoked
);
3474 class temp_iterator
: public input_iterator
{
3475 unsigned char *base
;
3476 temp_iterator(const char *, int len
);
3479 friend input_iterator
*make_temp_iterator(const char *);
3485 temp_iterator::temp_iterator(const char *s
, int len
)
3487 base
= new unsigned char[len
];
3488 memcpy(base
, s
, len
);
3493 temp_iterator::~temp_iterator()
3498 class small_temp_iterator
: public input_iterator
{
3500 small_temp_iterator(const char *, int);
3501 ~small_temp_iterator();
3502 enum { BLOCK
= 16 };
3503 static small_temp_iterator
*free_list
;
3504 void *operator new(size_t);
3505 void operator delete(void *);
3507 unsigned char buf
[SIZE
];
3508 friend input_iterator
*make_temp_iterator(const char *);
3511 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3513 void *small_temp_iterator::operator new(size_t n
)
3515 assert(n
== sizeof(small_temp_iterator
));
3518 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3519 for (int i
= 0; i
< BLOCK
- 1; i
++)
3520 free_list
[i
].next
= free_list
+ i
+ 1;
3521 free_list
[BLOCK
-1].next
= 0;
3523 small_temp_iterator
*p
= free_list
;
3524 free_list
= (small_temp_iterator
*)(free_list
->next
);
3532 void small_temp_iterator::operator delete(void *p
)
3535 ((small_temp_iterator
*)p
)->next
= free_list
;
3536 free_list
= (small_temp_iterator
*)p
;
3540 small_temp_iterator::~small_temp_iterator()
3547 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3549 for (int i
= 0; i
< len
; i
++)
3555 input_iterator
*make_temp_iterator(const char *s
)
3558 return new small_temp_iterator(s
, 0);
3561 if (n
<= small_temp_iterator::SIZE
)
3562 return new small_temp_iterator(s
, n
);
3564 return new temp_iterator(s
, n
);
3568 // this is used when macros with arguments are interpolated
3573 arg_list(const macro
&);
3577 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3581 arg_list::~arg_list()
3585 class macro_iterator
: public string_iterator
{
3589 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3592 int has_args() { return 1; }
3593 input_iterator
*get_arg(int i
);
3594 int nargs() { return argc
; }
3595 void add_arg(const macro
&m
);
3597 int is_macro() { return 1; }
3601 input_iterator
*macro_iterator::get_arg(int i
)
3604 return make_temp_iterator(nm
.contents());
3605 if (i
> 0 && i
<= argc
) {
3607 for (int j
= 1; j
< i
; j
++) {
3611 return new string_iterator(p
->mac
);
3617 void macro_iterator::add_arg(const macro
&m
)
3620 for (p
= &args
; *p
; p
= &((*p
)->next
))
3622 *p
= new arg_list(m
);
3626 void macro_iterator::shift(int n
)
3628 while (n
> 0 && argc
> 0) {
3629 arg_list
*tem
= args
;
3637 // This gets used by eg .if '\?xxx\?''.
3639 int operator==(const macro
&m1
, const macro
&m2
)
3641 if (m1
.len
!= m2
.len
)
3643 string_iterator
iter1(m1
);
3644 string_iterator
iter2(m2
);
3648 int c1
= iter1
.get(&nd1
);
3651 int c2
= iter2
.get(&nd2
);
3663 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3673 static void interpolate_macro(symbol nm
)
3675 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3678 const char *s
= nm
.contents();
3679 if (strlen(s
) > 2) {
3680 request_or_macro
*r
;
3685 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3687 macro
*m
= r
->to_macro();
3688 if (!m
|| !m
->empty())
3689 warned
= warning(WARN_SPACE
,
3690 "macro `%1' not defined "
3691 "(probably missing space after `%2')",
3692 nm
.contents(), buf
);
3696 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3698 request_dictionary
.define(nm
, p
);
3709 static void decode_args(macro_iterator
*mi
)
3711 if (!tok
.newline() && !tok
.eof()) {
3713 int c
= get_copy(&n
);
3717 if (c
== '\n' || c
== EOF
)
3720 int quote_input_level
= 0;
3721 int done_tab_warning
= 0;
3723 quote_input_level
= input_stack::get_level();
3726 arg
.append(compatible_flag
? PUSH_COMP_MODE
: PUSH_GROFF_MODE
);
3727 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3728 if (quote_input_level
> 0 && c
== '"'
3730 || input_stack::get_level() == quote_input_level
)) {
3743 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3744 warning(WARN_TAB
, "tab character in unquoted macro argument");
3745 done_tab_warning
= 1;
3752 arg
.append(POP_GROFFCOMP_MODE
);
3758 static void decode_string_args(macro_iterator
*mi
)
3761 int c
= get_copy(&n
);
3765 if (c
== '\n' || c
== EOF
) {
3766 error("missing `]'");
3772 int quote_input_level
= 0;
3773 int done_tab_warning
= 0;
3775 quote_input_level
= input_stack::get_level();
3778 while (c
!= EOF
&& c
!= '\n'
3779 && !(c
== ']' && quote_input_level
== 0)
3780 && !(c
== ' ' && quote_input_level
== 0)) {
3781 if (quote_input_level
> 0 && c
== '"'
3782 && input_stack::get_level() == quote_input_level
) {
3795 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3796 warning(WARN_TAB
, "tab character in unquoted string argument");
3797 done_tab_warning
= 1;
3808 void macro::invoke(symbol nm
)
3810 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3812 input_stack::push(mi
);
3816 macro
*macro::to_macro()
3823 return empty_macro
== 1;
3826 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_called
)
3827 : string_iterator(m
, how_called
, s
), args(0), argc(0)
3831 macro_iterator::macro_iterator() : args(0), argc(0)
3835 macro_iterator::~macro_iterator()
3838 arg_list
*tem
= args
;
3844 dictionary
composite_dictionary(17);
3846 void composite_request()
3848 symbol from
= get_name(1);
3849 if (!from
.is_null()) {
3850 const char *from_gn
= glyph_name_to_unicode(from
.contents());
3852 from_gn
= check_unicode_name(from
.contents());
3854 error("invalid composite glyph name `%1'", from
.contents());
3859 const char *from_decomposed
= decompose_unicode(from_gn
);
3860 if (from_decomposed
)
3861 from_gn
= &from_decomposed
[1];
3862 symbol to
= get_name(1);
3864 composite_dictionary
.remove(symbol(from_gn
));
3866 const char *to_gn
= glyph_name_to_unicode(to
.contents());
3868 to_gn
= check_unicode_name(to
.contents());
3870 error("invalid composite glyph name `%1'", to
.contents());
3875 const char *to_decomposed
= decompose_unicode(to_gn
);
3877 to_gn
= &to_decomposed
[1];
3878 if (strcmp(from_gn
, to_gn
) == 0)
3879 composite_dictionary
.remove(symbol(from_gn
));
3881 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
3887 static symbol
composite_glyph_name(symbol nm
)
3889 macro_iterator
*mi
= new macro_iterator();
3890 decode_string_args(mi
);
3891 input_stack::push(mi
);
3892 const char *gn
= glyph_name_to_unicode(nm
.contents());
3894 gn
= check_unicode_name(nm
.contents());
3896 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
3897 return EMPTY_SYMBOL
;
3900 const char *gn_decomposed
= decompose_unicode(gn
);
3901 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
3903 int n
= input_stack::nargs();
3904 for (int i
= 1; i
<= n
; i
++) {
3906 input_iterator
*p
= input_stack::get_arg(i
);
3909 while ((c
= p
->get(0)) != EOF
)
3912 const char *u
= glyph_name_to_unicode(gl
.contents());
3914 u
= check_unicode_name(gl
.contents());
3916 error("invalid component `%1' in composite glyph name",
3918 return EMPTY_SYMBOL
;
3921 const char *decomposed
= decompose_unicode(u
);
3924 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
3925 if (mapped_composite
)
3926 u
= (const char *)mapped_composite
;
3930 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
3932 return symbol(groff_gn
);
3936 return symbol(gl
.contents());
3939 int trap_sprung_flag
= 0;
3940 int postpone_traps_flag
= 0;
3941 symbol postponed_trap
;
3943 void spring_trap(symbol nm
)
3945 assert(!nm
.is_null());
3946 trap_sprung_flag
= 1;
3947 if (postpone_traps_flag
) {
3948 postponed_trap
= nm
;
3951 static char buf
[2] = { BEGIN_TRAP
, 0 };
3952 static char buf2
[2] = { END_TRAP
, '\0' };
3953 input_stack::push(make_temp_iterator(buf2
));
3954 request_or_macro
*p
= lookup_request(nm
);
3955 macro
*m
= p
->to_macro();
3957 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3959 error("you can't invoke a request with a trap");
3960 input_stack::push(make_temp_iterator(buf
));
3963 void postpone_traps()
3965 postpone_traps_flag
= 1;
3968 int unpostpone_traps()
3970 postpone_traps_flag
= 0;
3971 if (!postponed_trap
.is_null()) {
3972 spring_trap(postponed_trap
);
3973 postponed_trap
= NULL_SYMBOL
;
3982 macro_iterator
*mi
= new macro_iterator
;
3983 int reading_from_terminal
= isatty(fileno(stdin
));
3985 if (!tok
.newline() && !tok
.eof()) {
3986 int c
= get_copy(0);
3989 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3990 if (!invalid_input_char(c
)) {
3991 if (reading_from_terminal
)
4002 if (reading_from_terminal
) {
4003 fputc(had_prompt
? ':' : '\a', stderr
);
4006 input_stack::push(mi
);
4010 while ((c
= getchar()) != EOF
) {
4011 if (invalid_input_char(c
))
4012 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
4025 if (reading_from_terminal
)
4027 input_stack::push(new string_iterator(mac
));
4031 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
4032 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
};
4033 enum comp_mode
{ COMP_IGNORE
, COMP_DISABLE
, COMP_ENABLE
};
4035 void do_define_string(define_mode mode
, comp_mode comp
)
4038 node
*n
= 0; // pacify compiler
4049 else if (!tok
.space()) {
4050 error("bad string definition");
4061 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
4062 macro
*mm
= rm
? rm
->to_macro() : 0;
4063 if (mode
== DEFINE_APPEND
&& mm
)
4065 if (comp
== COMP_DISABLE
)
4066 mac
.append(PUSH_GROFF_MODE
);
4067 else if (comp
== COMP_ENABLE
)
4068 mac
.append(PUSH_COMP_MODE
);
4069 while (c
!= '\n' && c
!= EOF
) {
4073 mac
.append((unsigned char)c
);
4078 request_dictionary
.define(nm
, mm
);
4080 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4081 mac
.append(POP_GROFFCOMP_MODE
);
4086 void define_string()
4088 do_define_string(DEFINE_NORMAL
,
4089 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4092 void define_nocomp_string()
4094 do_define_string(DEFINE_NORMAL
, COMP_DISABLE
);
4097 void append_string()
4099 do_define_string(DEFINE_APPEND
,
4100 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4103 void append_nocomp_string()
4105 do_define_string(DEFINE_APPEND
, COMP_DISABLE
);
4108 void do_define_character(char_mode mode
, const char *font_name
)
4110 node
*n
= 0; // pacify compiler
4113 charinfo
*ci
= tok
.get_char(1);
4119 string
s(font_name
);
4121 s
+= ci
->nm
.contents();
4123 ci
= get_charinfo(symbol(s
.contents()));
4130 else if (!tok
.space()) {
4131 error("bad character definition");
4137 while (c
== ' ' || c
== '\t')
4141 macro
*m
= new macro
;
4142 while (c
!= '\n' && c
!= EOF
) {
4146 m
->append((unsigned char)c
);
4149 m
= ci
->setx_macro(m
, mode
);
4155 void define_character()
4157 do_define_character(CHAR_NORMAL
);
4160 void define_fallback_character()
4162 do_define_character(CHAR_FALLBACK
);
4165 void define_special_character()
4167 do_define_character(CHAR_SPECIAL
);
4170 static void remove_character()
4173 while (!tok
.newline() && !tok
.eof()) {
4174 if (!tok
.space() && !tok
.tab()) {
4175 charinfo
*ci
= tok
.get_char(1);
4178 macro
*m
= ci
->set_macro(0);
4187 static void interpolate_string(symbol nm
)
4189 request_or_macro
*p
= lookup_request(nm
);
4190 macro
*m
= p
->to_macro();
4192 error("you can only invoke a string or macro using \\*");
4194 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
4195 input_stack::push(si
);
4199 static void interpolate_string_with_args(symbol s
)
4201 request_or_macro
*p
= lookup_request(s
);
4202 macro
*m
= p
->to_macro();
4204 error("you can only invoke a string or macro using \\*");
4206 macro_iterator
*mi
= new macro_iterator(s
, *m
);
4207 decode_string_args(mi
);
4208 input_stack::push(mi
);
4212 static void interpolate_arg(symbol nm
)
4214 const char *s
= nm
.contents();
4215 if (!s
|| *s
== '\0')
4216 copy_mode_error("missing argument name");
4217 else if (s
[1] == 0 && csdigit(s
[0]))
4218 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4219 else if (s
[0] == '*' && s
[1] == '\0') {
4220 int limit
= input_stack::nargs();
4222 for (int i
= 1; i
<= limit
; i
++) {
4223 input_iterator
*p
= input_stack::get_arg(i
);
4225 while ((c
= p
->get(0)) != EOF
)
4232 input_stack::push(make_temp_iterator(args
.contents()));
4235 else if (s
[0] == '@' && s
[1] == '\0') {
4236 int limit
= input_stack::nargs();
4238 for (int i
= 1; i
<= limit
; i
++) {
4240 args
+= BEGIN_QUOTE
;
4241 input_iterator
*p
= input_stack::get_arg(i
);
4243 while ((c
= p
->get(0)) != EOF
)
4252 input_stack::push(make_temp_iterator(args
.contents()));
4257 for (p
= s
; *p
&& csdigit(*p
); p
++)
4260 copy_mode_error("bad argument name `%1'", s
);
4262 input_stack::push(input_stack::get_arg(atoi(s
)));
4266 void handle_first_page_transition()
4269 topdiv
->begin_page();
4272 // We push back a token by wrapping it up in a token_node, and
4273 // wrapping that up in a string_iterator.
4275 static void push_token(const token
&t
)
4278 m
.append(new token_node(t
));
4279 input_stack::push(new string_iterator(m
));
4282 void push_page_ejector()
4284 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4285 input_stack::push(make_temp_iterator(buf
));
4288 void handle_initial_request(unsigned char code
)
4294 mac
.append(new token_node(tok
));
4295 input_stack::push(new string_iterator(mac
));
4296 input_stack::push(make_temp_iterator(buf
));
4297 topdiv
->begin_page();
4301 void handle_initial_title()
4303 handle_initial_request(TITLE_REQUEST
);
4306 // this should be local to define_macro, but cfront 1.2 doesn't support that
4307 static symbol
dot_symbol(".");
4309 void do_define_macro(define_mode mode
, calling_mode calling
, comp_mode comp
)
4312 if (calling
== CALLING_INDIRECT
) {
4313 symbol temp1
= get_name(1);
4314 if (temp1
.is_null()) {
4318 symbol temp2
= get_name();
4319 input_stack::push(make_temp_iterator("\n"));
4320 if (!temp2
.is_null()) {
4321 interpolate_string(temp2
);
4322 input_stack::push(make_temp_iterator(" "));
4324 interpolate_string(temp1
);
4325 input_stack::push(make_temp_iterator(" "));
4328 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4335 term
= get_name(); // the request that terminates the definition
4338 while (!tok
.newline() && !tok
.eof())
4340 const char *start_filename
;
4342 int have_start_location
= input_stack::get_location(0, &start_filename
,
4345 // doing this here makes the line numbers come out right
4346 int c
= get_copy(&n
, 1);
4349 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4350 request_or_macro
*rm
=
4351 (request_or_macro
*)request_dictionary
.lookup(nm
);
4353 mm
= rm
->to_macro();
4354 if (mm
&& mode
== DEFINE_APPEND
)
4358 if (comp
== COMP_DISABLE
)
4359 mac
.append(PUSH_GROFF_MODE
);
4360 else if (comp
== COMP_ENABLE
)
4361 mac
.append(PUSH_COMP_MODE
);
4363 while (c
== ESCAPE_NEWLINE
) {
4364 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4366 c
= get_copy(&n
, 1);
4368 if (bol
&& c
== '.') {
4369 const char *s
= term
.contents();
4371 // see if it matches term
4374 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4376 if ((unsigned char)s
[0] == d
) {
4377 for (i
= 1; s
[i
] != 0; i
++) {
4379 if ((unsigned char)s
[i
] != d
)
4385 && ((i
== 2 && compatible_flag
)
4386 || (d
= get_copy(&n
)) == ' '
4387 || d
== '\n')) { // we found it
4392 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4395 request_dictionary
.define(nm
, mm
);
4397 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4398 mac
.append(POP_GROFFCOMP_MODE
);
4401 if (term
!= dot_symbol
) {
4403 interpolate_macro(term
);
4409 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4411 for (int j
= 0; j
< i
; j
++)
4417 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4418 if (have_start_location
)
4419 error_with_file_and_line(start_filename
, start_lineno
,
4420 "end of file while defining macro `%1'",
4423 error("end of file while defining macro `%1'", nm
.contents());
4426 if (have_start_location
)
4427 error_with_file_and_line(start_filename
, start_lineno
,
4428 "end of file while ignoring input lines");
4430 error("end of file while ignoring input lines");
4435 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4442 c
= get_copy(&n
, 1);
4448 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
,
4449 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4452 void define_nocomp_macro()
4454 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_DISABLE
);
4457 void define_indirect_macro()
4459 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
,
4460 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4463 void define_indirect_nocomp_macro()
4465 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_DISABLE
);
4470 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
,
4471 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4474 void append_nocomp_macro()
4476 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_DISABLE
);
4479 void append_indirect_macro()
4481 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
,
4482 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4485 void append_indirect_nocomp_macro()
4487 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_DISABLE
);
4493 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
, COMP_IGNORE
);
4500 symbol s
= get_name();
4503 request_dictionary
.remove(s
);
4510 symbol s1
= get_name(1);
4511 if (!s1
.is_null()) {
4512 symbol s2
= get_name(1);
4514 request_dictionary
.rename(s1
, s2
);
4521 symbol s1
= get_name(1);
4522 if (!s1
.is_null()) {
4523 symbol s2
= get_name(1);
4524 if (!s2
.is_null()) {
4525 if (!request_dictionary
.alias(s1
, s2
))
4526 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4534 symbol s
= get_name(1);
4536 request_or_macro
*p
= lookup_request(s
);
4537 macro
*m
= p
->to_macro();
4539 error("cannot chop request");
4540 else if (m
->empty())
4541 error("cannot chop empty macro");
4543 int have_restore
= 0;
4544 // we have to check for additional save/restore pairs which could be
4545 // there due to empty am1 requests.
4547 if (m
->get(m
->len
- 1) != POP_GROFFCOMP_MODE
)
4551 if (m
->get(m
->len
- 1) != PUSH_GROFF_MODE
4552 && m
->get(m
->len
- 1) != PUSH_COMP_MODE
)
4560 error("cannot chop empty macro");
4563 m
->set(POP_GROFFCOMP_MODE
, m
->len
- 1);
4572 void substring_request()
4574 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4575 symbol s
= get_name(1);
4576 if (!s
.is_null() && get_integer(&start
)) {
4577 request_or_macro
*p
= lookup_request(s
);
4578 macro
*m
= p
->to_macro();
4580 error("cannot apply `substring' on a request");
4583 if (!has_arg() || get_integer(&end
)) {
4584 int real_length
= 0; // 1, 2, ..., n
4585 string_iterator
iter1(*m
);
4586 for (int l
= 0; l
< m
->len
; l
++) {
4587 int c
= iter1
.get(0);
4588 if (c
== PUSH_GROFF_MODE
4589 || c
== PUSH_COMP_MODE
4590 || c
== POP_GROFFCOMP_MODE
)
4597 start
+= real_length
;
4605 if (start
>= real_length
|| end
< 0) {
4607 "start and end index of substring out of range");
4610 if (--(m
->p
->count
) <= 0)
4619 "start index of substring out of range, set to 0");
4622 if (end
>= real_length
) {
4624 "end index of substring out of range, set to string length");
4625 end
= real_length
- 1;
4627 // now extract the substring
4628 string_iterator
iter(*m
);
4630 for (i
= 0; i
< start
; i
++) {
4631 int c
= iter
.get(0);
4632 while (c
== PUSH_GROFF_MODE
4633 || c
== PUSH_COMP_MODE
4634 || c
== POP_GROFFCOMP_MODE
)
4640 for (; i
<= end
; i
++) {
4641 node
*nd
= 0; // pacify compiler
4642 int c
= iter
.get(&nd
);
4643 while (c
== PUSH_GROFF_MODE
4644 || c
== PUSH_COMP_MODE
4645 || c
== POP_GROFFCOMP_MODE
)
4652 mac
.append((unsigned char)c
);
4661 void length_request()
4665 if (ret
.is_null()) {
4675 else if (!tok
.space()) {
4676 error("bad string definition");
4687 while (c
!= '\n' && c
!= EOF
) {
4691 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4695 set_number_reg(ret
, len
);
4699 void asciify_macro()
4701 symbol s
= get_name(1);
4703 request_or_macro
*p
= lookup_request(s
);
4704 macro
*m
= p
->to_macro();
4706 error("cannot asciify request");
4709 string_iterator
iter(*m
);
4711 node
*nd
= 0; // pacify compiler
4712 int c
= iter
.get(&nd
);
4726 void unformat_macro()
4728 symbol s
= get_name(1);
4730 request_or_macro
*p
= lookup_request(s
);
4731 macro
*m
= p
->to_macro();
4733 error("cannot unformat request");
4736 string_iterator
iter(*m
);
4738 node
*nd
= 0; // pacify compiler
4739 int c
= iter
.get(&nd
);
4745 if (nd
->set_unformat_flag())
4755 static void interpolate_environment_variable(symbol nm
)
4757 const char *s
= getenv(nm
.contents());
4759 input_stack::push(make_temp_iterator(s
));
4762 void interpolate_number_reg(symbol nm
, int inc
)
4764 reg
*r
= lookup_number_reg(nm
);
4769 input_stack::push(make_temp_iterator(r
->get_string()));
4772 static void interpolate_number_format(symbol nm
)
4774 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4776 input_stack::push(make_temp_iterator(r
->get_format()));
4779 static int get_delim_number(units
*n
, unsigned char si
, int prev_value
)
4783 if (start
.delimiter(1)) {
4785 if (get_number(n
, si
, prev_value
)) {
4787 warning(WARN_DELIM
, "closing delimiter does not match");
4794 static int get_delim_number(units
*n
, unsigned char si
)
4798 if (start
.delimiter(1)) {
4800 if (get_number(n
, si
)) {
4802 warning(WARN_DELIM
, "closing delimiter does not match");
4809 static int get_line_arg(units
*n
, unsigned char si
, charinfo
**cp
)
4813 int start_level
= input_stack::get_level();
4814 if (!start
.delimiter(1))
4817 if (get_number(n
, si
)) {
4818 if (tok
.dummy() || tok
.transparent_dummy())
4820 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4821 *cp
= tok
.get_char(1);
4824 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4825 warning(WARN_DELIM
, "closing delimiter does not match");
4831 static int read_size(int *x
)
4841 else if (c
== '+') {
4846 int val
= 0; // pacify compiler
4852 // allow an increment either before or after the left parenthesis
4858 else if (c
== '+') {
4873 val
= val
*10 + (c
- '0');
4878 else if (csdigit(c
)) {
4880 if (!inc
&& c
!= '0' && c
< '4') {
4886 val
= val
*10 + (c
- '0');
4890 else if (!tok
.delimiter(1))
4896 ? get_number(&val
, 'z')
4897 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4899 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4900 if (start
.ch() == '[')
4901 error("missing `]'");
4903 error("missing closing delimiter");
4911 // special case -- \s[0] and \s0 means to revert to previous size
4918 *x
= curenv
->get_requested_point_size() + val
;
4921 *x
= curenv
->get_requested_point_size() - val
;
4928 "\\s request results in non-positive point size; set to 1");
4934 error("bad digit in point size");
4939 static symbol
get_delim_name()
4944 error("end of input at start of delimited name");
4947 if (start
.newline()) {
4948 error("can't delimit name with a newline");
4951 int start_level
= input_stack::get_level();
4952 char abuf
[ABUF_SIZE
];
4954 int buf_size
= ABUF_SIZE
;
4957 if (i
+ 1 > buf_size
) {
4959 buf
= new char[ABUF_SIZE
*2];
4960 memcpy(buf
, abuf
, buf_size
);
4961 buf_size
= ABUF_SIZE
*2;
4964 char *old_buf
= buf
;
4965 buf
= new char[buf_size
*2];
4966 memcpy(buf
, old_buf
, buf_size
);
4973 && (compatible_flag
|| input_stack::get_level() == start_level
))
4975 if ((buf
[i
] = tok
.ch()) == 0) {
4976 error("missing delimiter (got %1)", tok
.description());
4986 error("empty delimited name");
5001 static void do_register()
5005 if (!start
.delimiter(1))
5008 symbol nm
= get_long_name(1);
5013 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
5015 if (!r
|| !r
->get_value(&prev_value
))
5018 if (!get_number(&val
, 'u', prev_value
))
5021 warning(WARN_DELIM
, "closing delimiter does not match");
5025 set_number_reg(nm
, val
);
5028 // this implements the \w escape sequence
5030 static void do_width()
5034 int start_level
= input_stack::get_level();
5035 environment
env(curenv
);
5036 environment
*oldenv
= curenv
;
5041 warning(WARN_DELIM
, "missing closing delimiter");
5044 if (tok
.newline()) {
5045 warning(WARN_DELIM
, "missing closing delimiter");
5046 input_stack::push(make_temp_iterator("\n"));
5050 && (compatible_flag
|| input_stack::get_level() == start_level
))
5055 units x
= env
.get_input_line_position().to_units();
5056 input_stack::push(make_temp_iterator(i_to_a(x
)));
5057 env
.width_registers();
5062 charinfo
*page_character
;
5064 void set_page_character()
5066 page_character
= get_optional_char();
5070 static const symbol
percent_symbol("%");
5072 void read_title_parts(node
**part
, hunits
*part_width
)
5075 if (tok
.newline() || tok
.eof())
5078 int start_level
= input_stack::get_level();
5080 for (int i
= 0; i
< 3; i
++) {
5081 while (!tok
.newline() && !tok
.eof()) {
5083 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
5087 if (page_character
!= 0 && tok
.get_char() == page_character
)
5088 interpolate_number_reg(percent_symbol
, 0);
5093 curenv
->wrap_up_tab();
5094 part_width
[i
] = curenv
->get_input_line_position();
5095 part
[i
] = curenv
->extract_output_line();
5097 while (!tok
.newline() && !tok
.eof())
5101 class non_interpreted_node
: public node
{
5104 non_interpreted_node(const macro
&);
5105 int interpret(macro
*);
5107 int ends_sentence();
5114 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
5118 int non_interpreted_node::ends_sentence()
5123 int non_interpreted_node::same(node
*nd
)
5125 return mac
== ((non_interpreted_node
*)nd
)->mac
;
5128 const char *non_interpreted_node::type()
5130 return "non_interpreted_node";
5133 int non_interpreted_node::force_tprint()
5138 int non_interpreted_node::is_tag()
5143 node
*non_interpreted_node::copy()
5145 return new non_interpreted_node(mac
);
5148 int non_interpreted_node::interpret(macro
*m
)
5150 string_iterator
si(mac
);
5151 node
*n
= 0; // pacify compiler
5164 static node
*do_non_interpreted()
5169 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
5174 if (c
== EOF
|| c
== '\n') {
5175 error("missing \\?");
5178 return new non_interpreted_node(mac
);
5181 static void encode_char(macro
*mac
, char c
)
5184 if ((font::use_charnames_in_special
) && tok
.special()) {
5185 charinfo
*ci
= tok
.get_char(1);
5186 const char *s
= ci
->get_symbol()->contents();
5187 if (s
[0] != (char)0) {
5191 while (s
[i
] != (char)0) {
5199 else if (tok
.stretchable_space()
5200 || tok
.unstretchable_space())
5202 else if (!(tok
.hyphen_indicator()
5204 || tok
.transparent_dummy()
5205 || tok
.zero_width_break()))
5206 error("%1 is invalid within \\X", tok
.description());
5209 if ((font::use_charnames_in_special
) && (c
== '\\')) {
5211 * add escape escape sequence
5223 int start_level
= input_stack::get_level();
5226 tok
!= start
|| input_stack::get_level() != start_level
;
5229 warning(WARN_DELIM
, "missing closing delimiter");
5232 if (tok
.newline()) {
5233 input_stack::push(make_temp_iterator("\n"));
5234 warning(WARN_DELIM
, "missing closing delimiter");
5242 else if (tok
.leader())
5244 else if (tok
.backspace())
5248 encode_char(&mac
, c
);
5250 return new special_node(mac
);
5253 void output_request()
5255 if (!tok
.newline() && !tok
.eof()) {
5263 if (c
!= ' ' && c
!= '\t')
5266 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5267 topdiv
->transparent_output(c
);
5268 topdiv
->transparent_output('\n');
5273 extern int image_no
; // from node.cpp
5275 static node
*do_suppress(symbol nm
)
5277 if (nm
.is_null() || nm
.is_empty()) {
5278 error("expecting an argument to escape \\O");
5281 const char *s
= nm
.contents();
5284 if (begin_level
== 0)
5285 // suppress generation of glyphs
5286 return new suppress_node(0, 0);
5289 if (begin_level
== 0)
5290 // enable generation of glyphs
5291 return new suppress_node(1, 0);
5294 if (begin_level
== 0)
5295 return new suppress_node(1, 1);
5305 s
++; // move over '5'
5307 if (*s
== (char)0) {
5308 error("missing position and filename in \\O");
5311 if (!(position
== 'l'
5314 || position
== 'i')) {
5315 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5318 s
++; // onto image name
5319 if (s
== (char *)0) {
5320 error("missing image name for \\O");
5324 if (begin_level
== 0)
5325 return new suppress_node(symbol(s
), position
, image_no
);
5329 error("`%1' is an invalid argument to \\O", *s
);
5334 void special_node::tprint(troff_output_file
*out
)
5337 string_iterator
iter(mac
);
5339 int c
= iter
.get(0);
5342 for (const char *s
= ::asciify(c
); *s
; s
++)
5343 tprint_char(out
, *s
);
5348 int get_file_line(const char **filename
, int *lineno
)
5350 return input_stack::get_location(0, filename
, lineno
);
5356 if (get_integer(&n
)) {
5357 const char *filename
= 0;
5359 symbol s
= get_long_name();
5360 filename
= s
.contents();
5362 (void)input_stack::set_location(filename
, n
-1);
5367 static int nroff_mode
= 0;
5369 static void nroff_request()
5375 static void troff_request()
5381 static void skip_alternative()
5384 // ensure that ``.if 0\{'' works as expected
5385 if (tok
.left_brace())
5389 c
= input_stack::get(0);
5392 if (c
== ESCAPE_LEFT_BRACE
)
5394 else if (c
== ESCAPE_RIGHT_BRACE
)
5396 else if (c
== escape_char
&& escape_char
> 0)
5397 switch(input_stack::get(0)) {
5405 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5409 Note that the level can properly be < 0, eg
5415 So don't give an error message in this case.
5417 if (level
<= 0 && c
== '\n')
5423 static void begin_alternative()
5425 while (tok
.space() || tok
.left_brace())
5435 static int_stack if_else_stack
;
5442 while (tok
.ch() == '!') {
5447 unsigned char c
= tok
.ch();
5450 result
= !nroff_mode
;
5452 else if (c
== 'n') {
5454 result
= nroff_mode
;
5456 else if (c
== 'v') {
5460 else if (c
== 'o') {
5461 result
= (topdiv
->get_page_number() & 1);
5464 else if (c
== 'e') {
5465 result
= !(topdiv
->get_page_number() & 1);
5468 else if (c
== 'd' || c
== 'r') {
5470 symbol nm
= get_name(1);
5476 ? request_dictionary
.lookup(nm
) != 0
5477 : number_reg_dictionary
.lookup(nm
) != 0);
5479 else if (c
== 'm') {
5481 symbol nm
= get_long_name(1);
5486 result
= (nm
== default_symbol
5487 || color_dictionary
.lookup(nm
) != 0);
5489 else if (c
== 'c') {
5492 charinfo
*ci
= tok
.get_char(1);
5497 result
= character_exists(ci
, curenv
);
5500 else if (c
== 'F') {
5502 symbol nm
= get_long_name(1);
5507 result
= check_font(curenv
->get_family()->nm
, nm
);
5509 else if (c
== 'S') {
5511 symbol nm
= get_long_name(1);
5516 result
= check_style(nm
);
5518 else if (tok
.space())
5520 else if (tok
.delimiter()) {
5522 int delim_level
= input_stack::get_level();
5523 environment
env1(curenv
);
5524 environment
env2(curenv
);
5525 environment
*oldenv
= curenv
;
5528 for (int i
= 0; i
< 2; i
++) {
5531 if (tok
.newline() || tok
.eof()) {
5532 warning(WARN_DELIM
, "missing closing delimiter");
5538 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5544 node
*n1
= env1
.extract_output_line();
5545 node
*n2
= env2
.extract_output_line();
5546 result
= same_node_list(n1
, n2
);
5547 delete_node_list(n1
);
5548 delete_node_list(n2
);
5556 if (!get_number(&n
, 'u')) {
5566 begin_alternative();
5572 void if_else_request()
5574 if_else_stack
.push(do_if_request());
5584 if (if_else_stack
.is_empty()) {
5585 warning(WARN_EL
, "unbalanced .el request");
5589 if (if_else_stack
.pop())
5592 begin_alternative();
5596 static int while_depth
= 0;
5597 static int while_break_flag
= 0;
5599 void while_request()
5604 mac
.append(new token_node(tok
));
5606 node
*n
= 0; // pacify compiler
5607 int c
= input_stack::get(&n
);
5623 if (c
== ESCAPE_LEFT_BRACE
)
5625 else if (c
== ESCAPE_RIGHT_BRACE
)
5627 else if (c
== escape_char
)
5630 if (c
== '\n' && level
<= 0)
5635 error("unbalanced \\{ \\}");
5638 input_stack::add_boundary();
5640 input_stack::push(new string_iterator(mac
, "while loop"));
5642 if (!do_if_request()) {
5643 while (input_stack::get(0) != EOF
)
5647 process_input_stack();
5648 if (while_break_flag
|| input_stack::is_return_boundary()) {
5649 while_break_flag
= 0;
5653 input_stack::remove_boundary();
5659 void while_break_request()
5662 error("no while loop");
5666 while_break_flag
= 1;
5667 while (input_stack::get(0) != EOF
)
5673 void while_continue_request()
5676 error("no while loop");
5680 while (input_stack::get(0) != EOF
)
5690 symbol nm
= get_long_name(1);
5694 while (!tok
.newline() && !tok
.eof())
5697 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
5699 input_stack::push(new file_iterator(fp
, nm
.contents()));
5701 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5706 // like .so but use popen()
5711 error(".pso request not allowed in safer mode");
5715 #ifdef POPEN_MISSING
5716 error("pipes not available on this system");
5718 #else /* not POPEN_MISSING */
5719 if (tok
.newline() || tok
.eof())
5720 error("missing command");
5723 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5726 char *buf
= new char[buf_size
];
5728 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5729 const char *s
= asciify(c
);
5730 int slen
= strlen(s
);
5731 if (buf_used
+ slen
+ 1> buf_size
) {
5732 char *old_buf
= buf
;
5733 int old_buf_size
= buf_size
;
5735 buf
= new char[buf_size
];
5736 memcpy(buf
, old_buf
, old_buf_size
);
5739 strcpy(buf
+ buf_used
, s
);
5742 buf
[buf_used
] = '\0';
5744 FILE *fp
= popen(buf
, POPEN_RT
);
5746 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5748 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5752 #endif /* not POPEN_MISSING */
5758 static int llx_reg_contents
= 0;
5759 static int lly_reg_contents
= 0;
5760 static int urx_reg_contents
= 0;
5761 static int ury_reg_contents
= 0;
5763 struct bounding_box
{
5764 int llx
, lly
, urx
, ury
;
5767 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5768 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5770 int parse_bounding_box(char *p
, bounding_box
*bb
)
5772 if (sscanf(p
, "%d %d %d %d",
5773 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5776 /* The Document Structuring Conventions say that the numbers
5777 should be integers. Unfortunately some broken applications
5779 double x1
, x2
, x3
, x4
;
5780 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5788 for (; *p
== ' ' || *p
== '\t'; p
++)
5790 if (strncmp(p
, "(atend)", 7) == 0) {
5795 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5799 // This version is taken from psrm.cpp
5801 #define PS_LINE_MAX 255
5802 cset
white_space("\n\r \t");
5804 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5813 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5814 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5815 error("invalid input character code %1 in `%2'", int(c
), filename
);
5816 else if (i
< PS_LINE_MAX
)
5820 error("PostScript file `%1' is non-conforming "
5821 "because length of line exceeds 255", filename
);
5829 if (c
!= EOF
&& c
!= '\n')
5835 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5837 llx_reg_contents
= llx
;
5838 lly_reg_contents
= lly
;
5839 urx_reg_contents
= urx
;
5840 ury_reg_contents
= ury
;
5843 void do_ps_file(FILE *fp
, const char* filename
)
5847 char buf
[PS_LINE_MAX
];
5848 llx_reg_contents
= lly_reg_contents
=
5849 urx_reg_contents
= ury_reg_contents
= 0;
5850 if (!ps_get_line(buf
, fp
, filename
)) {
5851 error("`%1' is empty", filename
);
5854 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5855 error("`%1' is not conforming to the Document Structuring Conventions",
5859 while (ps_get_line(buf
, fp
, filename
) != 0) {
5860 if (buf
[0] != '%' || buf
[1] != '%'
5861 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5863 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5864 int res
= parse_bounding_box(buf
+ 14, &bb
);
5866 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5869 else if (res
== 2) {
5874 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5883 /* in the trailer, the last BoundingBox comment is significant */
5884 for (offset
= 512; !last_try
; offset
*= 2) {
5885 int had_trailer
= 0;
5887 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5889 if (fseek(fp
, 0L, 0) == -1)
5892 while (ps_get_line(buf
, fp
, filename
) != 0) {
5893 if (buf
[0] == '%' && buf
[1] == '%') {
5895 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5899 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5900 int res
= parse_bounding_box(buf
+ 14, &bb
);
5903 else if (res
== 2) {
5904 error("`(atend)' not allowed in trailer of `%1'", filename
);
5908 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5917 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5922 error("%%%%BoundingBox comment not found in `%1'", filename
);
5925 void ps_bbox_request()
5927 symbol nm
= get_long_name(1);
5931 while (!tok
.newline() && !tok
.eof())
5934 // PS files might contain non-printable characters, such as ^Z
5935 // and CRs not followed by an LF, so open them in binary mode.
5936 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents(),
5939 do_ps_file(fp
, nm
.contents());
5943 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5948 const char *asciify(int c
)
5951 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5952 buf
[1] = buf
[2] = '\0';
5954 case ESCAPE_QUESTION
:
5957 case ESCAPE_AMPERSAND
:
5960 case ESCAPE_RIGHT_PARENTHESIS
:
5963 case ESCAPE_UNDERSCORE
:
5969 case ESCAPE_CIRCUMFLEX
:
5972 case ESCAPE_LEFT_BRACE
:
5975 case ESCAPE_RIGHT_BRACE
:
5978 case ESCAPE_LEFT_QUOTE
:
5981 case ESCAPE_RIGHT_QUOTE
:
5999 case ESCAPE_PERCENT
:
6011 case PUSH_GROFF_MODE
:
6012 case PUSH_COMP_MODE
:
6013 case POP_GROFFCOMP_MODE
:
6017 if (invalid_input_char(c
))
6026 const char *input_char_description(int c
)
6030 return "a newline character";
6032 return "a backspace character";
6034 return "a leader character";
6036 return "a tab character";
6038 return "a space character";
6042 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
6043 if (invalid_input_char(c
)) {
6044 const char *s
= asciify(c
);
6051 sprintf(buf
, "magic character code %d", c
);
6060 sprintf(buf
, "character code %d", c
);
6066 if (!tok
.newline() && !tok
.eof()) {
6075 if (c
!= ' ' && c
!= '\t')
6079 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6082 curenv
->add_node(new tag_node(s
, 0));
6089 if (!tok
.newline() && !tok
.eof()) {
6098 if (c
!= ' ' && c
!= '\t')
6102 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6105 curenv
->add_node(new tag_node(s
, 1));
6110 // .tm, .tm1, and .tmc
6112 void do_terminal(int newline
, int string_like
)
6114 if (!tok
.newline() && !tok
.eof()) {
6118 if (string_like
&& c
== '"') {
6122 if (c
!= ' ' && c
!= '\t')
6125 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6126 fputs(asciify(c
), stderr
);
6129 fputc('\n', stderr
);
6144 void terminal_continue()
6149 dictionary
stream_dictionary(20);
6151 void do_open(int append
)
6153 symbol stream
= get_name(1);
6154 if (!stream
.is_null()) {
6155 symbol filename
= get_long_name(1);
6156 if (!filename
.is_null()) {
6158 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
6160 error("can't open `%1' for %2: %3",
6161 filename
.contents(),
6162 append
? "appending" : "writing",
6164 fp
= (FILE *)stream_dictionary
.remove(stream
);
6167 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
6178 error(".open request not allowed in safer mode");
6185 void opena_request()
6188 error(".opena request not allowed in safer mode");
6195 void close_request()
6197 symbol stream
= get_name(1);
6198 if (!stream
.is_null()) {
6199 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
6201 error("no stream named `%1'", stream
.contents());
6208 // .write and .writec
6210 void do_write_request(int newline
)
6212 symbol stream
= get_name(1);
6213 if (stream
.is_null()) {
6217 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6219 error("no stream named `%1'", stream
.contents());
6224 while ((c
= get_copy(0)) == ' ')
6228 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6229 fputs(asciify(c
), fp
);
6236 void write_request()
6238 do_write_request(1);
6241 void write_request_continue()
6243 do_write_request(0);
6246 void write_macro_request()
6248 symbol stream
= get_name(1);
6249 if (stream
.is_null()) {
6253 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6255 error("no stream named `%1'", stream
.contents());
6259 symbol s
= get_name(1);
6264 request_or_macro
*p
= lookup_request(s
);
6265 macro
*m
= p
->to_macro();
6267 error("cannot write request");
6269 string_iterator
iter(*m
);
6271 int c
= iter
.get(0);
6274 fputs(asciify(c
), fp
);
6281 void warnscale_request()
6288 warn_scale
= (double)units_per_inch
;
6290 warn_scale
= (double)units_per_inch
/ 2.54;
6292 warn_scale
= (double)units_per_inch
/ 72.0;
6294 warn_scale
= (double)units_per_inch
/ 6.0;
6297 "invalid scaling indicator `%1', using `i' instead", c
);
6300 warn_scaling_indicator
= c
;
6305 void spreadwarn_request()
6308 if (has_arg() && get_hunits(&n
, 'm')) {
6311 hunits em
= curenv
->get_size();
6312 spread_limit
= (double)n
.to_units()
6313 / (em
.is_zero() ? hresolution
: em
.to_units());
6316 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
6317 // changing value; we mirror at
6318 // -0.5 to make zero a valid value
6322 static void init_charset_table()
6325 strcpy(buf
, "char");
6326 for (int i
= 0; i
< 256; i
++) {
6327 strcpy(buf
+ 4, i_to_a(i
));
6328 charset_table
[i
] = get_charinfo(symbol(buf
));
6329 charset_table
[i
]->set_ascii_code(i
);
6331 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6333 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6334 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6335 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6336 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6337 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6338 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6339 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6340 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6341 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6342 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6343 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6344 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6345 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6346 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6347 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6348 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6349 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6350 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6351 page_character
= charset_table
['%'];
6354 static void init_hpf_code_table()
6356 for (int i
= 0; i
< 256; i
++)
6357 hpf_code_table
[i
] = i
;
6360 static void do_translate(int translate_transparent
, int translate_input
)
6363 while (!tok
.newline() && !tok
.eof()) {
6365 // This is a really bizarre troff feature.
6367 translate_space_to_dummy
= tok
.dummy();
6368 if (tok
.newline() || tok
.eof())
6373 charinfo
*ci1
= tok
.get_char(1);
6377 if (tok
.newline() || tok
.eof()) {
6378 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6379 translate_transparent
);
6383 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6384 translate_transparent
);
6385 else if (tok
.stretchable_space())
6386 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6387 translate_transparent
);
6388 else if (tok
.dummy())
6389 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6390 translate_transparent
);
6391 else if (tok
.hyphen_indicator())
6392 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6393 translate_transparent
);
6395 charinfo
*ci2
= tok
.get_char(1);
6399 ci1
->set_translation(0, translate_transparent
, translate_input
);
6401 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6413 void translate_no_transparent()
6418 void translate_input()
6426 if (get_integer(&flags
))
6428 charinfo
*ci
= tok
.get_char(1);
6430 charinfo
*tem
= ci
->get_translation();
6433 ci
->set_flags(flags
);
6440 void hyphenation_code()
6443 while (!tok
.newline() && !tok
.eof()) {
6444 charinfo
*ci
= tok
.get_char(1);
6449 unsigned char c
= tok
.ch();
6451 error("hyphenation code must be ordinary character");
6455 error("hyphenation code cannot be digit");
6458 ci
->set_hyphenation_code(c
);
6459 if (ci
->get_translation()
6460 && ci
->get_translation()->get_translation_input())
6461 ci
->get_translation()->set_hyphenation_code(c
);
6468 void hyphenation_patterns_file_code()
6471 while (!tok
.newline() && !tok
.eof()) {
6473 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6475 error("missing output hyphenation code");
6478 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6479 hpf_code_table
[n1
] = n2
;
6483 error("output hyphenation code must be integer in the range 0..255");
6488 error("input hyphenation code must be integer in the range 0..255");
6495 charinfo
*token::get_char(int required
)
6497 if (type
== TOKEN_CHAR
)
6498 return charset_table
[c
];
6499 if (type
== TOKEN_SPECIAL
)
6500 return get_charinfo(nm
);
6501 if (type
== TOKEN_NUMBERED_CHAR
)
6502 return get_charinfo_by_number(val
);
6503 if (type
== TOKEN_ESCAPE
) {
6504 if (escape_char
!= 0)
6505 return charset_table
[escape_char
];
6507 error("`\\e' used while no current escape character");
6512 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6513 warning(WARN_MISSING
, "missing normal or special character");
6515 error("normal or special character expected (got %1)", description());
6520 charinfo
*get_optional_char()
6524 charinfo
*ci
= tok
.get_char();
6526 check_missing_character();
6532 void check_missing_character()
6534 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6535 error("normal or special character expected (got %1): "
6536 "treated as missing",
6542 int token::add_to_node_list(node
**pp
)
6549 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6555 if (escape_char
!= 0)
6556 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6558 case TOKEN_HYPHEN_INDICATOR
:
6559 *pp
= (*pp
)->add_discretionary_hyphen();
6561 case TOKEN_ITALIC_CORRECTION
:
6562 *pp
= (*pp
)->add_italic_correction(&w
);
6564 case TOKEN_LEFT_BRACE
:
6566 case TOKEN_MARK_INPUT
:
6567 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6573 case TOKEN_NUMBERED_CHAR
:
6574 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6576 case TOKEN_RIGHT_BRACE
:
6579 n
= new hmotion_node(curenv
->get_space_width(),
6580 curenv
->get_fill_color());
6583 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6585 case TOKEN_STRETCHABLE_SPACE
:
6586 n
= new unbreakable_space_node(curenv
->get_space_width(),
6587 curenv
->get_fill_color());
6589 case TOKEN_UNSTRETCHABLE_SPACE
:
6590 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6591 curenv
->get_fill_color());
6593 case TOKEN_TRANSPARENT_DUMMY
:
6594 n
= new transparent_dummy_node
;
6596 case TOKEN_ZERO_WIDTH_BREAK
:
6597 n
= new space_node(H0
, curenv
->get_fill_color());
6599 n
->is_escape_colon();
6611 void token::process()
6613 if (possibly_handle_first_page_transition())
6616 case TOKEN_BACKSPACE
:
6617 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6618 curenv
->get_fill_color()));
6621 curenv
->add_char(charset_table
[c
]);
6624 curenv
->add_node(new dummy_node
);
6633 if (escape_char
!= 0)
6634 curenv
->add_char(charset_table
[escape_char
]);
6636 case TOKEN_BEGIN_TRAP
:
6637 case TOKEN_END_TRAP
:
6638 case TOKEN_PAGE_EJECTOR
:
6639 // these are all handled in process_input_stack()
6641 case TOKEN_HYPHEN_INDICATOR
:
6642 curenv
->add_hyphen_indicator();
6644 case TOKEN_INTERRUPT
:
6645 curenv
->interrupt();
6647 case TOKEN_ITALIC_CORRECTION
:
6648 curenv
->add_italic_correction();
6651 curenv
->handle_tab(1);
6653 case TOKEN_LEFT_BRACE
:
6655 case TOKEN_MARK_INPUT
:
6656 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6662 curenv
->add_node(nd
);
6665 case TOKEN_NUMBERED_CHAR
:
6666 curenv
->add_char(get_charinfo_by_number(val
));
6669 // handled in process_input_stack()
6671 case TOKEN_RIGHT_BRACE
:
6677 curenv
->add_char(get_charinfo(nm
));
6682 case TOKEN_STRETCHABLE_SPACE
:
6683 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6684 curenv
->get_fill_color()));
6686 case TOKEN_UNSTRETCHABLE_SPACE
:
6687 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6688 curenv
->get_fill_color()));
6691 curenv
->handle_tab(0);
6693 case TOKEN_TRANSPARENT
:
6695 case TOKEN_TRANSPARENT_DUMMY
:
6696 curenv
->add_node(new transparent_dummy_node
);
6698 case TOKEN_ZERO_WIDTH_BREAK
:
6700 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6701 tmp
->freeze_space();
6702 tmp
->is_escape_colon();
6703 curenv
->add_node(tmp
);
6711 class nargs_reg
: public reg
{
6713 const char *get_string();
6716 const char *nargs_reg::get_string()
6718 return i_to_a(input_stack::nargs());
6721 class lineno_reg
: public reg
{
6723 const char *get_string();
6726 const char *lineno_reg::get_string()
6730 if (!input_stack::get_location(0, &file
, &line
))
6732 return i_to_a(line
);
6735 class writable_lineno_reg
: public general_reg
{
6737 writable_lineno_reg();
6738 void set_value(units
);
6739 int get_value(units
*);
6742 writable_lineno_reg::writable_lineno_reg()
6746 int writable_lineno_reg::get_value(units
*res
)
6750 if (!input_stack::get_location(0, &file
, &line
))
6756 void writable_lineno_reg::set_value(units n
)
6758 input_stack::set_location(0, n
);
6761 class filename_reg
: public reg
{
6763 const char *get_string();
6766 const char *filename_reg::get_string()
6770 if (input_stack::get_location(0, &file
, &line
))
6776 class constant_reg
: public reg
{
6779 constant_reg(const char *);
6780 const char *get_string();
6783 constant_reg::constant_reg(const char *p
) : s(p
)
6787 const char *constant_reg::get_string()
6792 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6796 const char *constant_int_reg::get_string()
6801 void abort_request()
6806 else if (tok
.newline())
6809 while ((c
= get_copy(0)) == ' ')
6812 if (c
== EOF
|| c
== '\n')
6813 fputs("User Abort.", stderr
);
6815 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6816 fputs(asciify(c
), stderr
);
6818 fputc('\n', stderr
);
6819 cleanup_and_exit(1);
6825 char *s
= new char[len
];
6827 while ((c
= get_copy(0)) == ' ')
6830 while (c
!= '\n' && c
!= EOF
) {
6831 if (!invalid_input_char(c
)) {
6834 s
= new char[len
*2];
6835 memcpy(s
, tem
, len
);
6855 error(".pi request not allowed in safer mode");
6859 #ifdef POPEN_MISSING
6860 error("pipes not available on this system");
6862 #else /* not POPEN_MISSING */
6864 error("can't pipe: output already started");
6869 if ((pc
= read_string()) == 0)
6870 error("can't pipe to empty command");
6872 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6873 strcpy(s
, pipe_command
);
6876 a_delete pipe_command
;
6883 #endif /* not POPEN_MISSING */
6887 static int system_status
;
6889 void system_request()
6892 error(".sy request not allowed in safer mode");
6896 char *command
= read_string();
6898 error("empty command");
6900 system_status
= system(command
);
6908 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6909 handle_initial_request(COPY_FILE_REQUEST
);
6912 symbol filename
= get_long_name(1);
6913 while (!tok
.newline() && !tok
.eof())
6917 if (!filename
.is_null())
6918 curdiv
->copy_file(filename
.contents());
6926 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6927 handle_initial_request(VJUSTIFY_REQUEST
);
6930 symbol type
= get_long_name(1);
6931 if (!type
.is_null())
6932 curdiv
->vjustify(type
);
6938 void transparent_file()
6940 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6941 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6944 symbol filename
= get_long_name(1);
6945 while (!tok
.newline() && !tok
.eof())
6949 if (!filename
.is_null()) {
6951 FILE *fp
= include_search_path
.open_file_cautious(filename
.contents());
6953 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6960 if (invalid_input_char(c
))
6961 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6963 curdiv
->transparent_output(c
);
6968 curdiv
->transparent_output('\n');
6980 page_range(int, int, page_range
*);
6981 int contains(int n
);
6984 page_range::page_range(int i
, int j
, page_range
*p
)
6985 : first(i
), last(j
), next(p
)
6989 int page_range::contains(int n
)
6991 return n
>= first
&& (last
<= 0 || n
<= last
);
6994 page_range
*output_page_list
= 0;
6996 int in_output_page_list(int n
)
6998 if (!output_page_list
)
7000 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
7006 static void parse_output_page_list(char *p
)
7012 else if (csdigit(*p
)) {
7015 i
= i
*10 + *p
++ - '0';
7016 while (csdigit(*p
));
7026 j
= j
*10 + *p
++ - '0';
7027 while (csdigit(*p
));
7033 last_page_number
= -1;
7034 else if (last_page_number
>= 0 && j
> last_page_number
)
7035 last_page_number
= j
;
7036 output_page_list
= new page_range(i
, j
, output_page_list
);
7042 error("bad output page list");
7043 output_page_list
= 0;
7047 static FILE *open_mac_file(const char *mac
, char **path
)
7049 // Try first FOOBAR.tmac, then tmac.FOOBAR
7050 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
7052 strcat(s1
, MACRO_POSTFIX
);
7053 FILE *fp
= mac_path
->open_file(s1
, path
);
7056 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
7057 strcpy(s2
, MACRO_PREFIX
);
7059 fp
= mac_path
->open_file(s2
, path
);
7065 static void process_macro_file(const char *mac
)
7068 FILE *fp
= open_mac_file(mac
, &path
);
7070 fatal("can't find macro file %1", mac
);
7071 const char *s
= symbol(path
).contents();
7073 input_stack::push(new file_iterator(fp
, s
));
7075 process_input_stack();
7078 static void process_startup_file(const char *filename
)
7081 search_path
*orig_mac_path
= mac_path
;
7082 mac_path
= &config_macro_path
;
7083 FILE *fp
= mac_path
->open_file(filename
, &path
);
7085 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
7088 process_input_stack();
7090 mac_path
= orig_mac_path
;
7095 symbol nm
= get_long_name(1);
7099 while (!tok
.newline() && !tok
.eof())
7102 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
7103 // .mso doesn't (and cannot) go through open_mac_file, so we
7104 // need to do it here manually: If we have tmac.FOOBAR, try
7105 // FOOBAR.tmac and vice versa
7107 const char *fn
= nm
.contents();
7108 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
7109 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
7110 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
7111 strcat(s
, MACRO_POSTFIX
);
7112 fp
= mac_path
->open_file(s
, &path
);
7116 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
7117 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
7118 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
7119 strcpy(s
, MACRO_PREFIX
);
7120 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
7121 fp
= mac_path
->open_file(s
, &path
);
7127 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
7131 error("can't find macro file `%1'", nm
.contents());
7136 static void process_input_file(const char *name
)
7139 if (strcmp(name
, "-") == 0) {
7145 fp
= include_search_path
.open_file_cautious(name
);
7147 fatal("can't open `%1': %2", name
, strerror(errno
));
7149 input_stack::push(new file_iterator(fp
, name
));
7151 process_input_stack();
7154 // make sure the_input is empty before calling this
7156 static int evaluate_expression(const char *expr
, units
*res
)
7158 input_stack::push(make_temp_iterator(expr
));
7160 int success
= get_number(res
, 'u');
7161 while (input_stack::get(0) != EOF
)
7166 static void do_register_assignment(const char *s
)
7168 const char *p
= strchr(s
, '=');
7174 if (evaluate_expression(s
+ 1, &n
))
7175 set_number_reg(buf
, n
);
7178 char *buf
= new char[p
- s
+ 1];
7179 memcpy(buf
, s
, p
- s
);
7182 if (evaluate_expression(p
+ 1, &n
))
7183 set_number_reg(buf
, n
);
7188 static void set_string(const char *name
, const char *value
)
7190 macro
*m
= new macro
;
7191 for (const char *p
= value
; *p
; p
++)
7192 if (!invalid_input_char((unsigned char)*p
))
7194 request_dictionary
.define(name
, m
);
7197 static void do_string_assignment(const char *s
)
7199 const char *p
= strchr(s
, '=');
7204 set_string(buf
, s
+ 1);
7207 char *buf
= new char[p
- s
+ 1];
7208 memcpy(buf
, s
, p
- s
);
7210 set_string(buf
, p
+ 1);
7215 struct string_list
{
7218 string_list(const char *ss
) : s(ss
), next(0) {}
7222 static void prepend_string(const char *s
, string_list
**p
)
7224 string_list
*l
= new string_list(s
);
7230 static void add_string(const char *s
, string_list
**p
)
7234 *p
= new string_list(s
);
7237 void usage(FILE *stream
, const char *prog
)
7240 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7241 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7245 int main(int argc
, char **argv
)
7247 program_name
= argv
[0];
7248 static char stderr_buf
[BUFSIZ
];
7249 setbuf(stderr
, stderr_buf
);
7251 string_list
*macros
= 0;
7252 string_list
*register_assignments
= 0;
7253 string_list
*string_assignments
= 0;
7258 int no_rc
= 0; // don't process troffrc and troffrc-end
7259 int next_page_number
= 0; // pacify compiler
7261 hresolution
= vresolution
= 1;
7262 // restore $PATH if called from groff
7263 char* groff_path
= getenv("GROFF_PATH__");
7270 if (putenv(strsave(e
.contents())))
7271 fatal("putenv failed");
7273 static const struct option long_options
[] = {
7274 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
7275 { "version", no_argument
, 0, 'v' },
7278 #if defined(DEBUGGING)
7279 #define DEBUG_OPTION "D"
7281 while ((c
= getopt_long(argc
, argv
,
7282 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7283 DEBUG_OPTION
, long_options
, 0))
7288 printf("GNU troff (groff) version %s\n", Version_string
);
7293 // Search path for .psbb files
7294 // and most other non-system input files.
7295 include_search_path
.command_line_dir(optarg
);
7300 is_html
= (strcmp(device
, "html") == 0);
7303 compatible_flag
= 1;
7309 macro_path
.command_line_dir(optarg
);
7310 safer_macro_path
.command_line_dir(optarg
);
7311 config_macro_path
.command_line_dir(optarg
);
7314 font::command_line_font_dir(optarg
);
7317 add_string(optarg
, ¯os
);
7326 enable_warning(optarg
);
7329 disable_warning(optarg
);
7338 ascii_output_flag
= 1;
7341 suppress_output_flag
= 1;
7344 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7347 error("bad page number");
7350 parse_output_page_list(optarg
);
7353 if (*optarg
== '\0')
7354 error("`-d' requires non-empty argument");
7356 add_string(optarg
, &string_assignments
);
7359 if (*optarg
== '\0')
7360 error("`-r' requires non-empty argument");
7362 add_string(optarg
, ®ister_assignments
);
7365 default_family
= symbol(optarg
);
7371 // silently ignore these
7374 safer_flag
= 0; // unsafe behaviour
7376 #if defined(DEBUGGING)
7381 case CHAR_MAX
+ 1: // --help
7382 usage(stdout
, argv
[0]);
7386 usage(stderr
, argv
[0]);
7388 break; // never reached
7393 mac_path
= ¯o_path
;
7394 set_string(".T", device
);
7395 init_charset_table();
7396 init_hpf_code_table();
7397 if (!font::load_desc())
7398 fatal("sorry, I can't continue");
7399 units_per_inch
= font::res
;
7400 hresolution
= font::hor
;
7401 vresolution
= font::vert
;
7402 sizescale
= font::sizescale
;
7403 tcommand_flag
= font::tcommand
;
7404 warn_scale
= (double)units_per_inch
;
7405 warn_scaling_indicator
= 'i';
7406 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7407 default_family
= symbol(font::family
);
7408 font_size::init_size_table(font::sizes
);
7411 if (font::style_table
) {
7412 for (i
= 0; font::style_table
[i
]; i
++)
7413 mount_style(j
++, symbol(font::style_table
[i
]));
7415 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7416 // In the DESC file a font name of 0 (zero) means leave this
7418 if (strcmp(font::font_name_table
[i
], "0") != 0)
7419 mount_font(j
, symbol(font::font_name_table
[i
]));
7420 curdiv
= topdiv
= new top_level_diversion
;
7422 topdiv
->set_next_page_number(next_page_number
);
7423 init_input_requests();
7424 init_env_requests();
7425 init_div_requests();
7427 init_column_requests();
7429 init_node_requests();
7430 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7432 init_reg_requests();
7433 init_hyphen_requests();
7434 init_environments();
7435 while (string_assignments
) {
7436 do_string_assignment(string_assignments
->s
);
7437 string_list
*tem
= string_assignments
;
7438 string_assignments
= string_assignments
->next
;
7441 while (register_assignments
) {
7442 do_register_assignment(register_assignments
->s
);
7443 string_list
*tem
= register_assignments
;
7444 register_assignments
= register_assignments
->next
;
7448 process_startup_file(INITIAL_STARTUP_FILE
);
7450 process_macro_file(macros
->s
);
7451 string_list
*tem
= macros
;
7452 macros
= macros
->next
;
7456 process_startup_file(FINAL_STARTUP_FILE
);
7457 for (i
= optind
; i
< argc
; i
++)
7458 process_input_file(argv
[i
]);
7459 if (optind
>= argc
|| iflag
)
7460 process_input_file("-");
7462 return 0; // not reached
7468 if (has_arg() && get_integer(&n
)) {
7469 if (n
& ~WARN_TOTAL
) {
7470 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7476 warning_mask
= WARN_TOTAL
;
7480 static void init_registers()
7482 #ifdef LONG_FOR_TIME_T
7484 #else /* not LONG_FOR_TIME_T */
7486 #endif /* not LONG_FOR_TIME_T */
7488 // Use struct here to work around misfeature in old versions of g++.
7489 struct tm
*tt
= localtime(&t
);
7490 set_number_reg("seconds", int(tt
->tm_sec
));
7491 set_number_reg("minutes", int(tt
->tm_min
));
7492 set_number_reg("hours", int(tt
->tm_hour
));
7493 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7494 set_number_reg("dy", int(tt
->tm_mday
));
7495 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7496 set_number_reg("year", int(1900 + tt
->tm_year
));
7497 set_number_reg("yr", int(tt
->tm_year
));
7498 set_number_reg("$$", getpid());
7499 number_reg_dictionary
.define(".A",
7500 new constant_reg(ascii_output_flag
7506 * registers associated with \O
7509 static int output_reg_minx_contents
= -1;
7510 static int output_reg_miny_contents
= -1;
7511 static int output_reg_maxx_contents
= -1;
7512 static int output_reg_maxy_contents
= -1;
7514 void check_output_limits(int x
, int y
)
7516 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7517 output_reg_minx_contents
= x
;
7518 if (x
> output_reg_maxx_contents
)
7519 output_reg_maxx_contents
= x
;
7520 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7521 output_reg_miny_contents
= y
;
7522 if (y
> output_reg_maxy_contents
)
7523 output_reg_maxy_contents
= y
;
7526 void reset_output_registers()
7528 output_reg_minx_contents
= -1;
7529 output_reg_miny_contents
= -1;
7530 output_reg_maxx_contents
= -1;
7531 output_reg_maxy_contents
= -1;
7534 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7536 *minx
= output_reg_minx_contents
;
7537 *miny
= output_reg_miny_contents
;
7538 *maxx
= output_reg_maxx_contents
;
7539 *maxy
= output_reg_maxy_contents
;
7542 void init_input_requests()
7544 init_request("ab", abort_request
);
7545 init_request("als", alias_macro
);
7546 init_request("am", append_macro
);
7547 init_request("am1", append_nocomp_macro
);
7548 init_request("ami", append_indirect_macro
);
7549 init_request("ami1", append_indirect_nocomp_macro
);
7550 init_request("as", append_string
);
7551 init_request("as1", append_nocomp_string
);
7552 init_request("asciify", asciify_macro
);
7553 init_request("backtrace", backtrace_request
);
7554 init_request("blm", blank_line_macro
);
7555 init_request("break", while_break_request
);
7556 init_request("cf", copy_file
);
7557 init_request("cflags", char_flags
);
7558 init_request("char", define_character
);
7559 init_request("chop", chop_macro
);
7560 init_request("close", close_request
);
7561 init_request("color", activate_color
);
7562 init_request("composite", composite_request
);
7563 init_request("continue", while_continue_request
);
7564 init_request("cp", compatible
);
7565 init_request("de", define_macro
);
7566 init_request("de1", define_nocomp_macro
);
7567 init_request("defcolor", define_color
);
7568 init_request("dei", define_indirect_macro
);
7569 init_request("dei1", define_indirect_nocomp_macro
);
7570 init_request("do", do_request
);
7571 init_request("ds", define_string
);
7572 init_request("ds1", define_nocomp_string
);
7573 init_request("ec", set_escape_char
);
7574 init_request("ecr", restore_escape_char
);
7575 init_request("ecs", save_escape_char
);
7576 init_request("el", else_request
);
7577 init_request("em", end_macro
);
7578 init_request("eo", escape_off
);
7579 init_request("ex", exit_request
);
7580 init_request("fchar", define_fallback_character
);
7581 #ifdef WIDOW_CONTROL
7582 init_request("fpl", flush_pending_lines
);
7583 #endif /* WIDOW_CONTROL */
7584 init_request("hcode", hyphenation_code
);
7585 init_request("hpfcode", hyphenation_patterns_file_code
);
7586 init_request("ie", if_else_request
);
7587 init_request("if", if_request
);
7588 init_request("ig", ignore
);
7589 init_request("length", length_request
);
7590 init_request("lf", line_file
);
7591 init_request("mso", macro_source
);
7592 init_request("nop", nop_request
);
7593 init_request("nroff", nroff_request
);
7594 init_request("nx", next_file
);
7595 init_request("open", open_request
);
7596 init_request("opena", opena_request
);
7597 init_request("output", output_request
);
7598 init_request("pc", set_page_character
);
7599 init_request("pi", pipe_output
);
7600 init_request("pm", print_macros
);
7601 init_request("psbb", ps_bbox_request
);
7602 #ifndef POPEN_MISSING
7603 init_request("pso", pipe_source
);
7604 #endif /* not POPEN_MISSING */
7605 init_request("rchar", remove_character
);
7606 init_request("rd", read_request
);
7607 init_request("return", return_macro_request
);
7608 init_request("rm", remove_macro
);
7609 init_request("rn", rename_macro
);
7610 init_request("schar", define_special_character
);
7611 init_request("shift", shift
);
7612 init_request("so", source
);
7613 init_request("spreadwarn", spreadwarn_request
);
7614 init_request("substring", substring_request
);
7615 init_request("sy", system_request
);
7616 init_request("tag", tag
);
7617 init_request("taga", taga
);
7618 init_request("tm", terminal
);
7619 init_request("tm1", terminal1
);
7620 init_request("tmc", terminal_continue
);
7621 init_request("tr", translate
);
7622 init_request("trf", transparent_file
);
7623 init_request("trin", translate_input
);
7624 init_request("trnt", translate_no_transparent
);
7625 init_request("troff", troff_request
);
7626 init_request("unformat", unformat_macro
);
7628 init_request("vj", vjustify
);
7630 init_request("warn", warn_request
);
7631 init_request("warnscale", warnscale_request
);
7632 init_request("while", while_request
);
7633 init_request("write", write_request
);
7634 init_request("writec", write_request_continue
);
7635 init_request("writem", write_macro_request
);
7636 number_reg_dictionary
.define(".$", new nargs_reg
);
7637 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7638 number_reg_dictionary
.define(".c", new lineno_reg
);
7639 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7640 number_reg_dictionary
.define(".F", new filename_reg
);
7641 number_reg_dictionary
.define(".g", new constant_reg("1"));
7642 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7643 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7644 number_reg_dictionary
.define(".U", new constant_int_reg(&safer_flag
));
7645 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7646 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7647 extern const char *major_version
;
7648 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7649 extern const char *revision
;
7650 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7651 extern const char *minor_version
;
7652 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7653 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7654 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7655 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7656 number_reg_dictionary
.define("opmaxx",
7657 new variable_reg(&output_reg_maxx_contents
));
7658 number_reg_dictionary
.define("opmaxy",
7659 new variable_reg(&output_reg_maxy_contents
));
7660 number_reg_dictionary
.define("opminx",
7661 new variable_reg(&output_reg_minx_contents
));
7662 number_reg_dictionary
.define("opminy",
7663 new variable_reg(&output_reg_miny_contents
));
7664 number_reg_dictionary
.define("slimit",
7665 new variable_reg(&input_stack::limit
));
7666 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7667 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7668 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7671 object_dictionary
request_dictionary(501);
7673 void init_request(const char *s
, REQUEST_FUNCP f
)
7675 request_dictionary
.define(s
, new request(f
));
7678 static request_or_macro
*lookup_request(symbol nm
)
7680 assert(!nm
.is_null());
7681 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7683 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7685 request_dictionary
.define(nm
, p
);
7690 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7692 // Don't interpret character definitions in compatible mode.
7693 int old_compatible_flag
= compatible_flag
;
7694 compatible_flag
= 0;
7695 int old_escape_char
= escape_char
;
7697 macro
*mac
= ci
->set_macro(0);
7699 environment
*oldenv
= curenv
;
7700 environment
env(envp
);
7702 curenv
->set_composite();
7703 token old_tok
= tok
;
7704 input_stack::add_boundary();
7705 string_iterator
*si
=
7706 new string_iterator(*mac
, "composite character", ci
->nm
);
7707 input_stack::push(si
);
7708 // we don't use process_input_stack, because we don't want to recognise
7714 if (tok
.newline()) {
7715 error("composite character mustn't contain newline");
7723 node
*n
= curenv
->extract_output_line();
7724 input_stack::remove_boundary();
7728 compatible_flag
= old_compatible_flag
;
7729 escape_char
= old_escape_char
;
7734 static node
*read_draw_node()
7738 if (!start
.delimiter(1)){
7741 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7746 error("missing argument");
7748 unsigned char type
= tok
.ch();
7750 read_color_draw_node(start
);
7755 hvpair
*point
= new hvpair
[maxpoints
];
7760 for (i
= 0; tok
!= start
; i
++) {
7761 if (i
== maxpoints
) {
7762 hvpair
*oldpoint
= point
;
7763 point
= new hvpair
[maxpoints
*2];
7764 for (int j
= 0; j
< maxpoints
; j
++)
7765 point
[j
] = oldpoint
[j
];
7769 if (!get_hunits(&point
[i
].h
,
7770 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7781 if (!get_vunits(&point
[i
].v
, 'v')) {
7787 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7792 if (npoints
!= 1 || no_last_v
) {
7793 error("two arguments needed for line");
7798 if (npoints
!= 1 || !no_last_v
) {
7799 error("one argument needed for circle");
7805 if (npoints
!= 1 || no_last_v
) {
7806 error("two arguments needed for ellipse");
7811 if (npoints
!= 2 || no_last_v
) {
7812 error("four arguments needed for arc");
7818 error("even number of arguments needed for spline");
7821 if (npoints
!= 1 || !no_last_v
) {
7822 error("one argument needed for gray shade");
7827 // silently pass it through
7830 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7831 curenv
->get_font_size(),
7832 curenv
->get_glyph_color(),
7833 curenv
->get_fill_color());
7845 static void read_color_draw_node(token
&start
)
7849 error("missing color scheme");
7852 unsigned char scheme
= tok
.ch();
7855 char end
= start
.ch();
7858 col
= read_cmy(end
);
7861 col
= &default_color
;
7864 col
= read_gray(end
);
7867 col
= read_cmyk(end
);
7870 col
= read_rgb(end
);
7874 curenv
->set_fill_color(col
);
7875 while (tok
!= start
) {
7876 if (tok
.newline() || tok
.eof()) {
7877 warning(WARN_DELIM
, "missing closing delimiter");
7878 input_stack::push(make_temp_iterator("\n"));
7889 } warning_table
[] = {
7890 { "char", WARN_CHAR
},
7891 { "range", WARN_RANGE
},
7892 { "break", WARN_BREAK
},
7893 { "delim", WARN_DELIM
},
7895 { "scale", WARN_SCALE
},
7896 { "number", WARN_NUMBER
},
7897 { "syntax", WARN_SYNTAX
},
7898 { "tab", WARN_TAB
},
7899 { "right-brace", WARN_RIGHT_BRACE
},
7900 { "missing", WARN_MISSING
},
7901 { "input", WARN_INPUT
},
7902 { "escape", WARN_ESCAPE
},
7903 { "space", WARN_SPACE
},
7904 { "font", WARN_FONT
},
7906 { "mac", WARN_MAC
},
7907 { "reg", WARN_REG
},
7909 { "color", WARN_COLOR
},
7910 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7911 { "w", WARN_TOTAL
},
7912 { "default", DEFAULT_WARNING_MASK
},
7915 static int lookup_warning(const char *name
)
7917 for (unsigned int i
= 0;
7918 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7920 if (strcmp(name
, warning_table
[i
].name
) == 0)
7921 return warning_table
[i
].mask
;
7925 static void enable_warning(const char *name
)
7927 int mask
= lookup_warning(name
);
7929 warning_mask
|= mask
;
7931 error("unknown warning `%1'", name
);
7934 static void disable_warning(const char *name
)
7936 int mask
= lookup_warning(name
);
7938 warning_mask
&= ~mask
;
7940 error("unknown warning `%1'", name
);
7943 static void copy_mode_error(const char *format
,
7949 static const char prefix
[] = "(in ignored input) ";
7950 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7953 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7957 error(format
, arg1
, arg2
, arg3
);
7960 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7962 static void do_error(error_type type
,
7968 const char *filename
;
7970 if (inhibit_errors
&& type
< FATAL
)
7973 input_stack::backtrace();
7974 if (!get_file_line(&filename
, &lineno
))
7977 errprint("%1:%2: ", filename
, lineno
);
7978 else if (program_name
)
7979 fprintf(stderr
, "%s: ", program_name
);
7982 fputs("fatal error: ", stderr
);
7987 fputs("warning: ", stderr
);
7989 case OUTPUT_WARNING
:
7990 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7991 fprintf(stderr
, "warning [p %d, %.1f%c",
7992 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7993 if (topdiv
!= curdiv
) {
7994 double fromtop1
= curdiv
->get_vertical_position().to_units()
7996 fprintf(stderr
, ", div `%s', %.1f%c",
7997 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7999 fprintf(stderr
, "]: ");
8002 errprint(format
, arg1
, arg2
, arg3
);
8003 fputc('\n', stderr
);
8006 cleanup_and_exit(1);
8009 int warning(warning_type t
,
8015 if ((t
& warning_mask
) != 0) {
8016 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
8023 int output_warning(warning_type t
,
8029 if ((t
& warning_mask
) != 0) {
8030 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
8037 void error(const char *format
,
8042 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
8045 void fatal(const char *format
,
8050 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
8053 void fatal_with_file_and_line(const char *filename
, int lineno
,
8059 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
8060 errprint(format
, arg1
, arg2
, arg3
);
8061 fputc('\n', stderr
);
8063 cleanup_and_exit(1);
8066 void error_with_file_and_line(const char *filename
, int lineno
,
8072 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
8073 errprint(format
, arg1
, arg2
, arg3
);
8074 fputc('\n', stderr
);
8078 dictionary
charinfo_dictionary(501);
8080 charinfo
*get_charinfo(symbol nm
)
8082 void *p
= charinfo_dictionary
.lookup(nm
);
8084 return (charinfo
*)p
;
8085 charinfo
*cp
= new charinfo(nm
);
8086 (void)charinfo_dictionary
.lookup(nm
, cp
);
8090 int charinfo::next_index
= 0;
8092 charinfo::charinfo(symbol s
)
8093 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
8094 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8095 not_found(0), transparent_translate(1), translate_input(0),
8096 mode(CHAR_NORMAL
), nm(s
)
8098 index
= next_index
++;
8101 void charinfo::set_hyphenation_code(unsigned char c
)
8103 hyphenation_code
= c
;
8106 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
8110 if (hyphenation_code
!= 0)
8111 ci
->set_hyphenation_code(hyphenation_code
);
8112 if (asciify_code
!= 0)
8113 ci
->set_asciify_code(asciify_code
);
8114 else if (ascii_code
!= 0)
8115 ci
->set_asciify_code(ascii_code
);
8116 ci
->set_translation_input();
8118 special_translation
= TRANSLATE_NONE
;
8119 transparent_translate
= tt
;
8122 void charinfo::set_special_translation(int c
, int tt
)
8124 special_translation
= c
;
8126 transparent_translate
= tt
;
8129 void charinfo::set_ascii_code(unsigned char c
)
8134 void charinfo::set_asciify_code(unsigned char c
)
8139 macro
*charinfo::set_macro(macro
*m
)
8146 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
8154 void charinfo::set_number(int n
)
8160 int charinfo::get_number()
8162 assert(flags
& NUMBERED
);
8166 symbol
UNNAMED_SYMBOL("---");
8168 // For numbered characters not between 0 and 255, we make a symbol out
8169 // of the number and store them in this dictionary.
8171 dictionary
numbered_charinfo_dictionary(11);
8173 charinfo
*get_charinfo_by_number(int n
)
8175 static charinfo
*number_table
[256];
8177 if (n
>= 0 && n
< 256) {
8178 charinfo
*ci
= number_table
[n
];
8180 ci
= new charinfo(UNNAMED_SYMBOL
);
8182 number_table
[n
] = ci
;
8187 symbol
ns(i_to_a(n
));
8188 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
8190 ci
= new charinfo(UNNAMED_SYMBOL
);
8192 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
8198 int font::name_to_index(const char *nm
)
8202 ci
= charset_table
[nm
[0] & 0xff];
8203 else if (nm
[0] == '\\' && nm
[2] == 0)
8204 ci
= get_charinfo(symbol(nm
+ 1));
8206 ci
= get_charinfo(symbol(nm
));
8210 return ci
->get_index();
8213 int font::number_to_index(int n
)
8215 return get_charinfo_by_number(n
)->get_index();