2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include "dictionary.h"
33 #include "stringclass.h"
35 #include "macropath.h"
40 // Needed for getpid() and isatty()
45 #ifdef NEED_DECLARATION_PUTENV
47 int putenv(const char *);
49 #endif /* NEED_DECLARATION_PUTENV */
51 #define MACRO_PREFIX "tmac."
52 #define MACRO_POSTFIX ".tmac"
53 #define INITIAL_STARTUP_FILE "troffrc"
54 #define FINAL_STARTUP_FILE "troffrc-end"
55 #define DEFAULT_INPUT_STACK_LIMIT 1000
57 #ifndef DEFAULT_WARNING_MASK
58 // warnings that are enabled by default
59 #define DEFAULT_WARNING_MASK \
60 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
63 // initial size of buffer for reading names; expanded as necessary
66 extern "C" const char *Version_string
;
69 void init_column_requests();
72 static node
*read_draw_node();
73 static void read_color_draw_node(token
&);
74 void handle_first_page_transition();
75 static void push_token(const token
&);
80 void transparent_file();
81 void process_input_stack();
83 const char *program_name
= 0;
86 int color_flag
= 1; // colors are on by default
87 static int backtrace_flag
= 0;
89 char *pipe_command
= 0;
91 charinfo
*charset_table
[256];
92 unsigned char hpf_code_table
[256];
94 static int warning_mask
= DEFAULT_WARNING_MASK
;
95 static int inhibit_errors
= 0;
96 static int ignoring
= 0;
98 static void enable_warning(const char *);
99 static void disable_warning(const char *);
101 static int escape_char
= '\\';
102 static symbol end_macro_name
;
103 static symbol blank_line_macro_name
;
104 static int compatible_flag
= 0;
105 int ascii_output_flag
= 0;
106 int suppress_output_flag
= 0;
108 int begin_level
= 0; // number of nested .begin requests
110 int have_input
= 0; // whether \f, \F, \D'F...', \H, \m, \M,
111 // \R, \s, or \S has been processed in
113 int tcommand_flag
= 0;
114 int safer_flag
= 1; // safer by default
116 int have_string_arg
= 0; // whether we have \*[foo bar...]
118 double spread_limit
= -3.0 - 1.0; // negative means deactivated
121 char warn_scaling_indicator
;
123 search_path
*mac_path
= &safer_macro_path
;
125 static int get_copy(node
**, int = 0);
126 static void copy_mode_error(const char *,
127 const errarg
& = empty_errarg
,
128 const errarg
& = empty_errarg
,
129 const errarg
& = empty_errarg
);
131 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
132 static symbol
read_escape_name(read_mode mode
= NO_ARGS
);
133 static symbol
read_long_escape_name(read_mode mode
= NO_ARGS
);
134 static void interpolate_string(symbol
);
135 static void interpolate_string_with_args(symbol
);
136 static void interpolate_macro(symbol
);
137 static void interpolate_number_format(symbol
);
138 static void interpolate_environment_variable(symbol
);
140 static symbol
composite_glyph_name(symbol
);
141 static void interpolate_arg(symbol
);
142 static request_or_macro
*lookup_request(symbol
);
143 static int get_delim_number(units
*, int);
144 static int get_delim_number(units
*, int, units
);
145 static symbol
do_get_long_name(int, char);
146 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
147 static int read_size(int *);
148 static symbol
get_delim_name();
149 static void init_registers();
150 static void trapping_blank_line();
152 struct input_iterator
;
153 input_iterator
*make_temp_iterator(const char *);
154 const char *input_char_description(int);
157 void set_escape_char()
161 error("bad escape character");
165 escape_char
= tok
.ch();
178 static int saved_escape_char
= '\\';
180 void save_escape_char()
182 saved_escape_char
= escape_char
;
186 void restore_escape_char()
188 escape_char
= saved_escape_char
;
192 class input_iterator
{
195 virtual ~input_iterator() {}
197 friend class input_stack
;
199 const unsigned char *ptr
;
200 const unsigned char *eptr
;
201 input_iterator
*next
;
203 virtual int fill(node
**);
205 virtual int has_args() { return 0; }
206 virtual int nargs() { return 0; }
207 virtual input_iterator
*get_arg(int) { return 0; }
208 virtual int get_location(int, const char **, int *) { return 0; }
209 virtual void backtrace() {}
210 virtual int set_location(const char *, int) { return 0; }
211 virtual int next_file(FILE *, const char *) { return 0; }
212 virtual void shift(int) {}
213 virtual int is_boundary() {return 0; }
214 virtual int internal_level() { return 0; }
215 virtual int is_file() { return 0; }
216 virtual int is_macro() { return 0; }
217 virtual void save_compatible_flag(int) {}
218 virtual int get_compatible_flag() { return 0; }
221 input_iterator::input_iterator()
226 int input_iterator::fill(node
**)
231 int input_iterator::peek()
236 inline int input_iterator::get(node
**p
)
238 return ptr
< eptr
? *ptr
++ : fill(p
);
241 class input_boundary
: public input_iterator
{
243 int is_boundary() { return 1; }
246 class input_return_boundary
: public input_iterator
{
248 int is_boundary() { return 2; }
251 class file_iterator
: public input_iterator
{
254 const char *filename
;
258 enum { BUF_SIZE
= 512 };
259 unsigned char buf
[BUF_SIZE
];
262 file_iterator(FILE *, const char *, int = 0);
266 int get_location(int, const char **, int *);
268 int set_location(const char *, int);
269 int next_file(FILE *, const char *);
273 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
274 : fp(f
), lineno(1), filename(fn
), popened(po
),
275 newline_flag(0), seen_escape(0)
277 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
280 the_output
->put_filename(fn
);
284 file_iterator::~file_iterator()
289 void file_iterator::close()
293 #ifndef POPEN_MISSING
296 #endif /* not POPEN_MISSING */
301 int file_iterator::is_file()
306 int file_iterator::next_file(FILE *f
, const char *s
)
320 int file_iterator::fill(node
**)
325 unsigned char *p
= buf
;
327 unsigned char *e
= p
+ BUF_SIZE
;
332 if (invalid_input_char(c
))
333 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
341 seen_escape
= (c
== '\\');
354 int file_iterator::peek()
357 while (invalid_input_char(c
)) {
358 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
366 int file_iterator::get_location(int /*allow_macro*/,
367 const char **filenamep
, int *linenop
)
370 if (filename
!= 0 && strcmp(filename
, "-") == 0)
371 *filenamep
= "<standard input>";
373 *filenamep
= filename
;
377 void file_iterator::backtrace()
379 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
380 popened
? "process" : "file");
383 int file_iterator::set_location(const char *f
, int ln
)
389 the_output
->put_filename(f
);
395 input_iterator nil_iterator
;
399 static int get(node
**);
401 static void push(input_iterator
*);
402 static input_iterator
*get_arg(int);
404 static int get_location(int, const char **, int *);
405 static int set_location(const char *, int);
406 static void backtrace();
407 static void backtrace_all();
408 static void next_file(FILE *, const char *);
409 static void end_file();
410 static void shift(int n
);
411 static void add_boundary();
412 static void add_return_boundary();
413 static int is_return_boundary();
414 static void remove_boundary();
415 static int get_level();
417 static void pop_macro();
418 static void save_compatible_flag(int);
419 static int get_compatible_flag();
423 static input_iterator
*top
;
426 static int finish_get(node
**);
427 static int finish_peek();
430 input_iterator
*input_stack::top
= &nil_iterator
;
431 int input_stack::level
= 0;
432 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
434 inline int input_stack::get_level()
436 return level
+ top
->internal_level();
439 inline int input_stack::get(node
**np
)
441 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
447 int input_stack::finish_get(node
**np
)
450 int c
= top
->fill(np
);
451 if (c
!= EOF
|| top
->is_boundary())
453 if (top
== &nil_iterator
)
455 input_iterator
*tem
= top
;
459 if (top
->ptr
< top
->eptr
)
466 inline int input_stack::peek()
468 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
471 int input_stack::finish_peek()
475 if (c
!= EOF
|| top
->is_boundary())
477 if (top
== &nil_iterator
)
479 input_iterator
*tem
= top
;
483 if (top
->ptr
< top
->eptr
)
490 void input_stack::add_boundary()
492 push(new input_boundary
);
495 void input_stack::add_return_boundary()
497 push(new input_return_boundary
);
500 int input_stack::is_return_boundary()
502 return top
->is_boundary() == 2;
505 void input_stack::remove_boundary()
507 assert(top
->is_boundary());
508 input_iterator
*temp
= top
->next
;
514 void input_stack::push(input_iterator
*in
)
518 if (++level
> limit
&& limit
> 0)
519 fatal("input stack limit exceeded (probable infinite loop)");
524 input_iterator
*input_stack::get_arg(int i
)
527 for (p
= top
; p
!= 0; p
= p
->next
)
529 return p
->get_arg(i
);
533 void input_stack::shift(int n
)
535 for (input_iterator
*p
= top
; p
; p
= p
->next
)
542 int input_stack::nargs()
544 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
550 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
552 for (input_iterator
*p
= top
; p
; p
= p
->next
)
553 if (p
->get_location(allow_macro
, filenamep
, linenop
))
558 void input_stack::backtrace()
562 // only backtrace down to (not including) the topmost file
563 for (input_iterator
*p
= top
;
564 p
&& !p
->get_location(0, &f
, &n
);
569 void input_stack::backtrace_all()
571 for (input_iterator
*p
= top
; p
; p
= p
->next
)
575 int input_stack::set_location(const char *filename
, int lineno
)
577 for (input_iterator
*p
= top
; p
; p
= p
->next
)
578 if (p
->set_location(filename
, lineno
))
583 void input_stack::next_file(FILE *fp
, const char *s
)
586 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
587 if ((*pp
)->next_file(fp
, s
))
589 if (++level
> limit
&& limit
> 0)
590 fatal("input stack limit exceeded");
591 *pp
= new file_iterator(fp
, s
);
592 (*pp
)->next
= &nil_iterator
;
595 void input_stack::end_file()
597 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
598 if ((*pp
)->is_file()) {
599 input_iterator
*tem
= *pp
;
607 void input_stack::clear()
610 while (top
!= &nil_iterator
) {
611 if (top
->is_boundary())
613 input_iterator
*tem
= top
;
618 // Keep while_request happy.
619 for (; nboundaries
> 0; --nboundaries
)
620 add_return_boundary();
623 void input_stack::pop_macro()
628 if (top
->next
== &nil_iterator
)
630 if (top
->is_boundary())
632 is_macro
= top
->is_macro();
633 input_iterator
*tem
= top
;
638 // Keep while_request happy.
639 for (; nboundaries
> 0; --nboundaries
)
640 add_return_boundary();
643 inline void input_stack::save_compatible_flag(int f
)
645 top
->save_compatible_flag(f
);
648 inline int input_stack::get_compatible_flag()
650 return top
->get_compatible_flag();
653 void backtrace_request()
655 input_stack::backtrace_all();
662 symbol nm
= get_long_name();
663 while (!tok
.newline() && !tok
.eof())
666 input_stack::end_file();
669 FILE *fp
= fopen(nm
.contents(), "r");
671 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
673 input_stack::next_file(fp
, nm
.contents());
681 if (!has_arg() || !get_integer(&n
))
683 input_stack::shift(n
);
687 static int get_char_for_escape_name(int allow_space
= 0)
692 copy_mode_error("end of input in escape name");
695 if (!invalid_input_char(c
))
700 input_stack::push(make_temp_iterator("\n"));
703 if (c
== ' ' && allow_space
)
709 copy_mode_error("%1 is not allowed in an escape name",
710 input_char_description(c
));
716 static symbol
read_two_char_escape_name()
719 buf
[0] = get_char_for_escape_name();
720 if (buf
[0] != '\0') {
721 buf
[1] = get_char_for_escape_name();
730 static symbol
read_long_escape_name(read_mode mode
)
732 int start_level
= input_stack::get_level();
733 char abuf
[ABUF_SIZE
];
735 int buf_size
= ABUF_SIZE
;
740 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
747 if (mode
== WITH_ARGS
&& c
== ' ')
749 if (i
+ 2 > buf_size
) {
751 buf
= new char[ABUF_SIZE
*2];
752 memcpy(buf
, abuf
, buf_size
);
753 buf_size
= ABUF_SIZE
*2;
757 buf
= new char[buf_size
*2];
758 memcpy(buf
, old_buf
, buf_size
);
763 if (c
== ']' && input_stack::get_level() == start_level
)
772 if (mode
!= ALLOW_EMPTY
)
773 copy_mode_error("empty escape name");
785 static symbol
read_escape_name(read_mode mode
)
787 int c
= get_char_for_escape_name();
791 return read_two_char_escape_name();
792 if (c
== '[' && !compatible_flag
)
793 return read_long_escape_name(mode
);
800 static symbol
read_increment_and_escape_name(int *incp
)
802 int c
= get_char_for_escape_name();
809 return read_two_char_escape_name();
812 return read_escape_name();
815 return read_escape_name();
817 if (!compatible_flag
) {
819 return read_long_escape_name();
830 static int get_copy(node
**nd
, int defining
)
833 int c
= input_stack::get(nd
);
834 if (c
== ESCAPE_NEWLINE
) {
838 c
= input_stack::get(nd
);
839 } while (c
== ESCAPE_NEWLINE
);
841 if (c
!= escape_char
|| escape_char
<= 0)
843 c
= input_stack::peek();
848 (void)input_stack::get(0);
849 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
852 case '#': // Like \" but newline is ignored.
853 (void)input_stack::get(0);
854 while ((c
= input_stack::get(0)) != '\n')
860 (void)input_stack::get(0);
861 symbol s
= read_escape_name();
862 if (!(s
.is_null() || s
.is_empty()))
868 (void)input_stack::get(0);
869 symbol s
= read_escape_name(WITH_ARGS
);
870 if (!(s
.is_null() || s
.is_empty())) {
871 if (have_string_arg
) {
873 interpolate_string_with_args(s
);
876 interpolate_string(s
);
881 (void)input_stack::get(0);
884 (void)input_stack::get(0);
887 (void)input_stack::get(0);
891 (void)input_stack::get(0);
893 symbol s
= read_increment_and_escape_name(&inc
);
894 if (!(s
.is_null() || s
.is_empty()))
895 interpolate_number_reg(s
, inc
);
900 (void)input_stack::get(0);
901 symbol s
= read_escape_name();
902 if (!(s
.is_null() || s
.is_empty()))
903 interpolate_number_format(s
);
907 (void)input_stack::get(0);
911 (void)input_stack::get(0);
912 symbol s
= read_escape_name();
913 if (!(s
.is_null() || s
.is_empty()))
914 interpolate_environment_variable(s
);
918 (void)input_stack::get(0);
920 return ESCAPE_NEWLINE
;
923 (void)input_stack::get(0);
926 (void)input_stack::get(0);
929 (void)input_stack::get(0);
932 (void)input_stack::get(0);
935 (void)input_stack::get(0);
936 return ESCAPE_CIRCUMFLEX
;
938 (void)input_stack::get(0);
939 return ESCAPE_LEFT_BRACE
;
941 (void)input_stack::get(0);
942 return ESCAPE_RIGHT_BRACE
;
944 (void)input_stack::get(0);
945 return ESCAPE_LEFT_QUOTE
;
947 (void)input_stack::get(0);
948 return ESCAPE_RIGHT_QUOTE
;
950 (void)input_stack::get(0);
951 return ESCAPE_HYPHEN
;
953 (void)input_stack::get(0);
954 return ESCAPE_UNDERSCORE
;
956 (void)input_stack::get(0);
959 (void)input_stack::get(0);
962 (void)input_stack::get(0);
963 return ESCAPE_QUESTION
;
965 (void)input_stack::get(0);
966 return ESCAPE_AMPERSAND
;
968 (void)input_stack::get(0);
969 return ESCAPE_RIGHT_PARENTHESIS
;
971 (void)input_stack::get(0);
974 (void)input_stack::get(0);
975 return ESCAPE_PERCENT
;
977 if (c
== escape_char
) {
978 (void)input_stack::get(0);
987 class non_interpreted_char_node
: public node
{
990 non_interpreted_char_node(unsigned char);
992 int interpret(macro
*);
998 int non_interpreted_char_node::same(node
*nd
)
1000 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1003 const char *non_interpreted_char_node::type()
1005 return "non_interpreted_char_node";
1008 int non_interpreted_char_node::force_tprint()
1013 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1018 node
*non_interpreted_char_node::copy()
1020 return new non_interpreted_char_node(c
);
1023 int non_interpreted_char_node::interpret(macro
*mac
)
1029 static void do_width();
1030 static node
*do_non_interpreted();
1031 static node
*do_special();
1032 static node
*do_suppress(symbol nm
);
1033 static void do_register();
1035 dictionary
color_dictionary(501);
1036 static symbol
default_symbol("default");
1038 static color
*lookup_color(symbol nm
)
1040 assert(!nm
.is_null());
1041 if (nm
== default_symbol
)
1042 return &default_color
;
1043 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1045 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1049 void do_glyph_color(symbol nm
)
1054 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1056 color
*tem
= lookup_color(nm
);
1058 curenv
->set_glyph_color(tem
);
1060 (void)color_dictionary
.lookup(nm
, new color
);
1064 void do_fill_color(symbol nm
)
1069 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1071 color
*tem
= lookup_color(nm
);
1073 curenv
->set_fill_color(tem
);
1075 (void)color_dictionary
.lookup(nm
, new color
);
1079 static unsigned int get_color_element(const char *scheme
, const char *col
)
1082 if (!get_number(&val
, 'f')) {
1083 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1088 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1091 if (val
> color::MAX_COLOR_VAL
+1) {
1092 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1093 // we change 0x10000 to 0xffff
1094 return color::MAX_COLOR_VAL
;
1096 return (unsigned int)val
;
1099 static color
*read_rgb(char end
= 0)
1101 symbol component
= do_get_long_name(0, end
);
1102 if (component
.is_null()) {
1103 warning(WARN_COLOR
, "missing rgb color values");
1106 const char *s
= component
.contents();
1107 color
*col
= new color
;
1109 if (!col
->read_rgb(s
)) {
1110 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1117 input_stack::push(make_temp_iterator(" "));
1118 input_stack::push(make_temp_iterator(s
));
1120 unsigned int r
= get_color_element("rgb color", "red component");
1121 unsigned int g
= get_color_element("rgb color", "green component");
1122 unsigned int b
= get_color_element("rgb color", "blue component");
1123 col
->set_rgb(r
, g
, b
);
1128 static color
*read_cmy(char end
= 0)
1130 symbol component
= do_get_long_name(0, end
);
1131 if (component
.is_null()) {
1132 warning(WARN_COLOR
, "missing cmy color values");
1135 const char *s
= component
.contents();
1136 color
*col
= new color
;
1138 if (!col
->read_cmy(s
)) {
1139 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1146 input_stack::push(make_temp_iterator(" "));
1147 input_stack::push(make_temp_iterator(s
));
1149 unsigned int c
= get_color_element("cmy color", "cyan component");
1150 unsigned int m
= get_color_element("cmy color", "magenta component");
1151 unsigned int y
= get_color_element("cmy color", "yellow component");
1152 col
->set_cmy(c
, m
, y
);
1157 static color
*read_cmyk(char end
= 0)
1159 symbol component
= do_get_long_name(0, end
);
1160 if (component
.is_null()) {
1161 warning(WARN_COLOR
, "missing cmyk color values");
1164 const char *s
= component
.contents();
1165 color
*col
= new color
;
1167 if (!col
->read_cmyk(s
)) {
1168 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1175 input_stack::push(make_temp_iterator(" "));
1176 input_stack::push(make_temp_iterator(s
));
1178 unsigned int c
= get_color_element("cmyk color", "cyan component");
1179 unsigned int m
= get_color_element("cmyk color", "magenta component");
1180 unsigned int y
= get_color_element("cmyk color", "yellow component");
1181 unsigned int k
= get_color_element("cmyk color", "black component");
1182 col
->set_cmyk(c
, m
, y
, k
);
1187 static color
*read_gray(char end
= 0)
1189 symbol component
= do_get_long_name(0, end
);
1190 if (component
.is_null()) {
1191 warning(WARN_COLOR
, "missing gray values");
1194 const char *s
= component
.contents();
1195 color
*col
= new color
;
1197 if (!col
->read_gray(s
)) {
1198 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1205 input_stack::push(make_temp_iterator("\n"));
1206 input_stack::push(make_temp_iterator(s
));
1208 unsigned int g
= get_color_element("gray", "gray value");
1214 static void activate_color()
1217 if (has_arg() && get_integer(&n
))
1218 color_flag
= n
!= 0;
1224 static void define_color()
1226 symbol color_name
= get_long_name(1);
1227 if (color_name
.is_null()) {
1231 if (color_name
== default_symbol
) {
1232 warning(WARN_COLOR
, "default color can't be redefined");
1236 symbol style
= get_long_name(1);
1237 if (style
.is_null()) {
1242 if (strcmp(style
.contents(), "rgb") == 0)
1244 else if (strcmp(style
.contents(), "cmyk") == 0)
1246 else if (strcmp(style
.contents(), "gray") == 0)
1248 else if (strcmp(style
.contents(), "grey") == 0)
1250 else if (strcmp(style
.contents(), "cmy") == 0)
1254 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1260 (void)color_dictionary
.lookup(color_name
, col
);
1264 static node
*do_overstrike()
1267 overstrike_node
*on
= new overstrike_node
;
1268 int start_level
= input_stack::get_level();
1272 if (tok
.newline() || tok
.eof()) {
1273 warning(WARN_DELIM
, "missing closing delimiter");
1274 input_stack::push(make_temp_iterator("\n"));
1278 && (compatible_flag
|| input_stack::get_level() == start_level
))
1280 charinfo
*ci
= tok
.get_char(1);
1282 node
*n
= curenv
->make_char_node(ci
);
1290 static node
*do_bracket()
1293 bracket_node
*bn
= new bracket_node
;
1295 int start_level
= input_stack::get_level();
1299 warning(WARN_DELIM
, "missing closing delimiter");
1302 if (tok
.newline()) {
1303 warning(WARN_DELIM
, "missing closing delimiter");
1304 input_stack::push(make_temp_iterator("\n"));
1308 && (compatible_flag
|| input_stack::get_level() == start_level
))
1310 charinfo
*ci
= tok
.get_char(1);
1312 node
*n
= curenv
->make_char_node(ci
);
1320 static int do_name_test()
1324 int start_level
= input_stack::get_level();
1329 if (tok
.newline() || tok
.eof()) {
1330 warning(WARN_DELIM
, "missing closing delimiter");
1331 input_stack::push(make_temp_iterator("\n"));
1335 && (compatible_flag
|| input_stack::get_level() == start_level
))
1341 return some_char
&& !bad_char
;
1344 static int do_expr_test()
1348 int start_level
= input_stack::get_level();
1349 if (!start
.delimiter(1))
1352 // disable all warning and error messages temporarily
1353 int saved_warning_mask
= warning_mask
;
1354 int saved_inhibit_errors
= inhibit_errors
;
1358 int result
= get_number_rigidly(&dummy
, 'u');
1359 warning_mask
= saved_warning_mask
;
1360 inhibit_errors
= saved_inhibit_errors
;
1361 if (tok
== start
&& input_stack::get_level() == start_level
)
1363 // ignore everything up to the delimiter in case we aren't right there
1366 if (tok
.newline() || tok
.eof()) {
1367 warning(WARN_DELIM
, "missing closing delimiter");
1368 input_stack::push(make_temp_iterator("\n"));
1371 if (tok
== start
&& input_stack::get_level() == start_level
)
1378 static node
*do_zero_width()
1382 int start_level
= input_stack::get_level();
1383 environment
env(curenv
);
1384 environment
*oldenv
= curenv
;
1388 if (tok
.newline() || tok
.eof()) {
1389 error("missing closing delimiter");
1393 && (compatible_flag
|| input_stack::get_level() == start_level
))
1398 node
*rev
= env
.extract_output_line();
1406 return new zero_width_node(n
);
1411 // It's undesirable for \Z to change environments, because then
1412 // \n(.w won't work as expected.
1414 static node
*do_zero_width()
1416 node
*rev
= new dummy_node
;
1419 int start_level
= input_stack::get_level();
1422 if (tok
.newline() || tok
.eof()) {
1423 warning(WARN_DELIM
, "missing closing delimiter");
1424 input_stack::push(make_temp_iterator("\n"));
1428 && (compatible_flag
|| input_stack::get_level() == start_level
))
1430 if (!tok
.add_to_node_list(&rev
))
1431 error("invalid token in argument to \\Z");
1440 return new zero_width_node(n
);
1445 token_node
*node::get_token_node()
1450 class token_node
: public node
{
1453 token_node(const token
&t
);
1455 token_node
*get_token_node();
1461 token_node::token_node(const token
&t
) : tk(t
)
1465 node
*token_node::copy()
1467 return new token_node(tk
);
1470 token_node
*token_node::get_token_node()
1475 int token_node::same(node
*nd
)
1477 return tk
== ((token_node
*)nd
)->tk
;
1480 const char *token_node::type()
1482 return "token_node";
1485 int token_node::force_tprint()
1490 token::token() : nd(0), type(TOKEN_EMPTY
)
1499 token::token(const token
&t
)
1500 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1502 // Use two statements to work around bug in SGI C++.
1504 nd
= tem
? tem
->copy() : 0;
1507 void token::operator=(const token
&t
)
1511 // Use two statements to work around bug in SGI C++.
1513 nd
= tem
? tem
->copy() : 0;
1530 return !tok
.newline();
1533 void token::make_space()
1538 void token::make_newline()
1540 type
= TOKEN_NEWLINE
;
1552 int cc
= input_stack::get(&n
);
1553 if (cc
!= escape_char
|| escape_char
== 0) {
1556 case COMPATIBLE_SAVE
:
1557 input_stack::save_compatible_flag(compatible_flag
);
1558 compatible_flag
= 0;
1560 case COMPATIBLE_RESTORE
:
1561 compatible_flag
= input_stack::get_compatible_flag();
1566 case TRANSPARENT_FILE_REQUEST
:
1568 case COPY_FILE_REQUEST
:
1570 case VJUSTIFY_REQUEST
:
1572 type
= TOKEN_REQUEST
;
1576 type
= TOKEN_BEGIN_TRAP
;
1579 type
= TOKEN_END_TRAP
;
1581 case LAST_PAGE_EJECTOR
:
1582 seen_last_page_ejector
= 1;
1585 type
= TOKEN_PAGE_EJECTOR
;
1587 case ESCAPE_PERCENT
:
1589 type
= TOKEN_HYPHEN_INDICATOR
;
1593 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1597 type
= TOKEN_STRETCHABLE_SPACE
;
1601 type
= TOKEN_ZERO_WIDTH_BREAK
;
1605 type
= TOKEN_ESCAPE
;
1608 goto handle_escape_char
;
1612 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1613 curenv
->get_fill_color());
1615 case ESCAPE_CIRCUMFLEX
:
1618 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1619 curenv
->get_fill_color());
1621 case ESCAPE_NEWLINE
:
1624 case ESCAPE_LEFT_BRACE
:
1626 type
= TOKEN_LEFT_BRACE
;
1628 case ESCAPE_RIGHT_BRACE
:
1630 type
= TOKEN_RIGHT_BRACE
;
1632 case ESCAPE_LEFT_QUOTE
:
1634 type
= TOKEN_SPECIAL
;
1637 case ESCAPE_RIGHT_QUOTE
:
1639 type
= TOKEN_SPECIAL
;
1644 type
= TOKEN_SPECIAL
;
1647 case ESCAPE_UNDERSCORE
:
1649 type
= TOKEN_SPECIAL
;
1654 type
= TOKEN_INTERRUPT
;
1658 type
= TOKEN_TRANSPARENT
;
1660 case ESCAPE_QUESTION
:
1662 nd
= do_non_interpreted();
1668 case ESCAPE_AMPERSAND
:
1672 case ESCAPE_RIGHT_PARENTHESIS
:
1673 ESCAPE_RIGHT_PARENTHESIS
:
1674 type
= TOKEN_TRANSPARENT_DUMMY
;
1677 type
= TOKEN_BACKSPACE
;
1686 type
= TOKEN_NEWLINE
;
1689 type
= TOKEN_LEADER
;
1694 token_node
*tn
= n
->get_token_node();
1713 cc
= input_stack::get(0);
1716 nm
= read_two_char_escape_name();
1717 type
= TOKEN_SPECIAL
;
1721 error("end of input after escape character");
1724 goto ESCAPE_LEFT_QUOTE
;
1726 goto ESCAPE_RIGHT_QUOTE
;
1730 goto ESCAPE_UNDERSCORE
;
1732 goto ESCAPE_PERCENT
;
1736 nd
= new hmotion_node(curenv
->get_digit_width(),
1737 curenv
->get_fill_color());
1743 goto ESCAPE_CIRCUMFLEX
;
1745 type
= TOKEN_ITALIC_CORRECTION
;
1749 nd
= new left_italic_corrected_node
;
1752 goto ESCAPE_AMPERSAND
;
1754 goto ESCAPE_RIGHT_PARENTHESIS
;
1758 goto ESCAPE_QUESTION
;
1764 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1767 type
= TOKEN_NEWLINE
;
1771 case '#': // Like \" but newline is ignored.
1772 while ((cc
= input_stack::get(0)) != '\n')
1780 symbol s
= read_escape_name();
1781 if (!(s
.is_null() || s
.is_empty()))
1787 symbol s
= read_escape_name(WITH_ARGS
);
1788 if (!(s
.is_null() || s
.is_empty())) {
1789 if (have_string_arg
) {
1790 have_string_arg
= 0;
1791 interpolate_string_with_args(s
);
1794 interpolate_string(s
);
1799 nd
= new non_interpreted_char_node('\001');
1803 c
= '0' + do_name_test();
1811 c
= '0' + do_expr_test();
1817 nm
= get_delim_name();
1820 type
= TOKEN_SPECIAL
;
1824 nd
= new vmotion_node(curenv
->get_size() / 2,
1825 curenv
->get_fill_color());
1828 nd
= read_draw_node();
1836 goto handle_escape_char
;
1839 symbol s
= read_escape_name(ALLOW_EMPTY
);
1843 for (p
= s
.contents(); *p
!= '\0'; p
++)
1846 if (*p
|| s
.is_empty())
1847 curenv
->set_font(s
);
1849 curenv
->set_font(atoi(s
.contents()));
1850 if (!compatible_flag
)
1856 symbol s
= read_escape_name(ALLOW_EMPTY
);
1859 curenv
->set_family(s
);
1865 symbol s
= read_escape_name();
1866 if (!(s
.is_null() || s
.is_empty()))
1867 interpolate_number_format(s
);
1871 if (!get_delim_number(&x
, 'm'))
1874 nd
= new hmotion_node(x
, curenv
->get_fill_color());
1877 // don't take height increments relative to previous height if
1878 // in compatibility mode
1879 if (!compatible_flag
&& curenv
->get_char_height())
1881 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
1882 curenv
->set_char_height(x
);
1886 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1887 curenv
->set_char_height(x
);
1889 if (!compatible_flag
)
1893 nm
= read_escape_name();
1894 if (nm
.is_null() || nm
.is_empty())
1896 type
= TOKEN_MARK_INPUT
;
1902 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1905 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1907 node
*n
= curenv
->make_char_node(s
);
1909 nd
= new hline_node(x
, n
);
1911 nd
= new vline_node(x
, n
);
1915 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
1916 if (!compatible_flag
)
1920 do_fill_color(read_escape_name(ALLOW_EMPTY
));
1921 if (!compatible_flag
)
1927 symbol s
= read_increment_and_escape_name(&inc
);
1928 if (!(s
.is_null() || s
.is_empty()))
1929 interpolate_number_reg(s
, inc
);
1933 if (!get_delim_number(&val
, 0))
1935 type
= TOKEN_NUMBERED_CHAR
;
1938 nd
= do_overstrike();
1942 nd
= do_suppress(read_escape_name());
1948 type
= TOKEN_SPREAD
;
1952 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
1956 if (!compatible_flag
)
1961 curenv
->set_size(x
);
1962 if (!compatible_flag
)
1966 if (get_delim_number(&x
, 0))
1967 curenv
->set_char_slant(x
);
1968 if (!compatible_flag
)
1973 nd
= new non_interpreted_char_node('\t');
1977 nd
= new vmotion_node(-curenv
->get_size() / 2,
1978 curenv
->get_fill_color());
1981 if (!get_delim_number(&x
, 'v'))
1984 nd
= new vmotion_node(x
, curenv
->get_fill_color());
1988 symbol s
= read_escape_name();
1989 if (!(s
.is_null() || s
.is_empty()))
1990 interpolate_environment_variable(s
);
1997 if (!get_delim_number(&x
, 'v'))
2000 nd
= new extra_size_node(x
);
2010 symbol s
= read_escape_name();
2011 if (s
.is_null() || s
.is_empty())
2013 request_or_macro
*p
= lookup_request(s
);
2014 macro
*m
= p
->to_macro();
2016 error("can't transparently throughput a request");
2019 nd
= new special_node(*m
);
2026 if (type
== TOKEN_NODE
)
2027 nd
= new zero_width_node(nd
);
2029 charinfo
*ci
= get_char(1);
2032 node
*gn
= curenv
->make_char_node(ci
);
2035 nd
= new zero_width_node(gn
);
2041 nd
= do_zero_width();
2047 goto ESCAPE_LEFT_BRACE
;
2049 goto ESCAPE_RIGHT_BRACE
;
2053 if (!compatible_flag
) {
2054 symbol s
= read_long_escape_name(WITH_ARGS
);
2055 if (s
.is_null() || s
.is_empty())
2057 if (have_string_arg
) {
2058 have_string_arg
= 0;
2059 nm
= composite_glyph_name(s
);
2062 const char *gn
= check_unicode_name(s
.contents());
2064 const char *gn_decomposed
= decompose_unicode(gn
);
2066 gn
= &gn_decomposed
[1];
2067 const char *groff_gn
= unicode_to_glyph_name(gn
);
2069 nm
= symbol(groff_gn
);
2071 char *buf
= new char[strlen(gn
) + 1 + 1];
2079 nm
= symbol(s
.contents());
2081 type
= TOKEN_SPECIAL
;
2084 goto handle_normal_char
;
2086 if (cc
!= escape_char
&& cc
!= '.')
2087 warning(WARN_ESCAPE
, "escape character ignored before %1",
2088 input_char_description(cc
));
2089 goto handle_normal_char
;
2095 int token::operator==(const token
&t
)
2104 case TOKEN_NUMBERED_CHAR
:
2105 return val
== t
.val
;
2111 int token::operator!=(const token
&t
)
2113 return !(*this == t
);
2116 // is token a suitable delimiter (like ')?
2118 int token::delimiter(int err
)
2147 error("cannot use character `%1' as a starting delimiter", char(c
));
2154 case TOKEN_STRETCHABLE_SPACE
:
2155 case TOKEN_UNSTRETCHABLE_SPACE
:
2159 error("cannot use %1 as a starting delimiter", description());
2166 const char *token::description()
2170 case TOKEN_BACKSPACE
:
2171 return "a backspace character";
2182 case TOKEN_HYPHEN_INDICATOR
:
2184 case TOKEN_INTERRUPT
:
2186 case TOKEN_ITALIC_CORRECTION
:
2189 return "a leader character";
2190 case TOKEN_LEFT_BRACE
:
2192 case TOKEN_MARK_INPUT
:
2198 case TOKEN_NUMBERED_CHAR
:
2200 case TOKEN_RIGHT_BRACE
:
2205 return "a special character";
2208 case TOKEN_STRETCHABLE_SPACE
:
2210 case TOKEN_UNSTRETCHABLE_SPACE
:
2213 return "a tab character";
2214 case TOKEN_TRANSPARENT
:
2216 case TOKEN_TRANSPARENT_DUMMY
:
2218 case TOKEN_ZERO_WIDTH_BREAK
:
2221 return "end of input";
2225 return "a magic token";
2230 while (!tok
.newline())
2241 if (has_arg() && get_integer(&n
))
2242 compatible_flag
= n
!= 0;
2244 compatible_flag
= 1;
2248 static void empty_name_warning(int required
)
2250 if (tok
.newline() || tok
.eof()) {
2252 warning(WARN_MISSING
, "missing name");
2254 else if (tok
.right_brace() || tok
.tab()) {
2255 const char *start
= tok
.description();
2258 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2259 if (!tok
.newline() && !tok
.eof())
2260 error("%1 is not allowed before an argument", start
);
2262 warning(WARN_MISSING
, "missing name");
2265 error("name expected (got %1)", tok
.description());
2267 error("name expected (got %1): treated as missing", tok
.description());
2270 static void non_empty_name_warning()
2272 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2273 && !tok
.right_brace()
2274 // We don't want to give a warning for .el\{
2275 && !tok
.left_brace())
2276 error("%1 is not allowed in a name", tok
.description());
2279 symbol
get_name(int required
)
2281 if (compatible_flag
) {
2284 if ((buf
[0] = tok
.ch()) != 0) {
2286 if ((buf
[1] = tok
.ch()) != 0) {
2291 non_empty_name_warning();
2295 empty_name_warning(required
);
2300 return get_long_name(required
);
2303 symbol
get_long_name(int required
)
2305 return do_get_long_name(required
, 0);
2308 static symbol
do_get_long_name(int required
, char end
)
2312 char abuf
[ABUF_SIZE
];
2314 int buf_size
= ABUF_SIZE
;
2317 // If end != 0 we normally have to append a null byte
2318 if (i
+ 2 > buf_size
) {
2320 buf
= new char[ABUF_SIZE
*2];
2321 memcpy(buf
, abuf
, buf_size
);
2322 buf_size
= ABUF_SIZE
*2;
2325 char *old_buf
= buf
;
2326 buf
= new char[buf_size
*2];
2327 memcpy(buf
, old_buf
, buf_size
);
2332 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2338 empty_name_warning(required
);
2341 if (end
&& buf
[i
] == end
)
2344 non_empty_name_warning();
2357 topdiv
->set_last_page();
2358 if (!end_macro_name
.is_null()) {
2359 spring_trap(end_macro_name
);
2361 process_input_stack();
2363 curenv
->final_break();
2365 process_input_stack();
2367 if (topdiv
->get_page_length() > 0) {
2369 topdiv
->set_ejecting();
2370 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2371 input_stack::push(make_temp_iterator((char *)buf
));
2372 topdiv
->space(topdiv
->get_page_length(), 1);
2374 process_input_stack();
2375 seen_last_page_ejector
= 1; // should be set already
2376 topdiv
->set_ejecting();
2377 push_page_ejector();
2378 topdiv
->space(topdiv
->get_page_length(), 1);
2380 process_input_stack();
2382 // This will only happen if a trap-invoked macro starts a diversion,
2383 // or if vertical position traps have been disabled.
2384 cleanup_and_exit(0);
2387 // This implements .ex. The input stack must be cleared before calling
2392 input_stack::clear();
2399 void return_macro_request()
2401 input_stack::pop_macro();
2407 end_macro_name
= get_name();
2411 void blank_line_macro()
2413 blank_line_macro_name
= get_name();
2417 static void trapping_blank_line()
2419 if (!blank_line_macro_name
.is_null())
2420 spring_trap(blank_line_macro_name
);
2427 int old_compatible_flag
= compatible_flag
;
2428 compatible_flag
= 0;
2429 symbol nm
= get_name();
2433 interpolate_macro(nm
);
2434 compatible_flag
= old_compatible_flag
;
2437 inline int possibly_handle_first_page_transition()
2439 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2440 handle_first_page_transition();
2447 static int transparent_translate(int cc
)
2449 if (!invalid_input_char(cc
)) {
2450 charinfo
*ci
= charset_table
[cc
];
2451 switch (ci
->get_special_translation(1)) {
2452 case charinfo::TRANSLATE_SPACE
:
2454 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2455 return ESCAPE_TILDE
;
2456 case charinfo::TRANSLATE_DUMMY
:
2457 return ESCAPE_AMPERSAND
;
2458 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2459 return ESCAPE_PERCENT
;
2461 // This is really ugly.
2462 ci
= ci
->get_translation(1);
2464 int c
= ci
->get_ascii_code();
2467 error("can't translate %1 to special character `%2'"
2468 " in transparent throughput",
2469 input_char_description(cc
),
2477 struct int_stack_element
{
2479 int_stack_element
*next
;
2489 int_stack::int_stack()
2494 int_stack::~int_stack()
2497 int_stack_element
*temp
= top
;
2503 int int_stack::is_empty()
2508 void int_stack::push(int n
)
2510 int_stack_element
*p
= new int_stack_element
;
2516 int int_stack::pop()
2519 int_stack_element
*p
= top
;
2526 int node::reread(int *)
2531 int diverted_space_node::reread(int *bolp
)
2533 if (curenv
->get_fill())
2534 trapping_blank_line();
2541 int diverted_copy_file_node::reread(int *bolp
)
2543 curdiv
->copy_file(filename
.contents());
2548 int word_space_node::reread(int *)
2551 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2552 curenv
->space(w
->width
, w
->sentence_width
);
2559 int unbreakable_space_node::reread(int *)
2564 int hmotion_node::reread(int *)
2566 if (unformat
&& was_tab
) {
2567 curenv
->handle_tab(0);
2574 void process_input_stack()
2576 int_stack trap_bol_stack
;
2579 int suppress_next
= 0;
2581 case token::TOKEN_CHAR
:
2583 unsigned char ch
= tok
.c
;
2584 if (bol
&& !have_input
2585 && (ch
== curenv
->control_char
2586 || ch
== curenv
->no_break_control_char
)) {
2587 break_flag
= ch
== curenv
->control_char
;
2588 // skip tabs as well as spaces here
2591 } while (tok
.white_space());
2592 symbol nm
= get_name();
2596 interpolate_macro(nm
);
2600 if (possibly_handle_first_page_transition())
2604 curenv
->add_char(charset_table
[ch
]);
2606 if (tok
.type
!= token::TOKEN_CHAR
)
2616 case token::TOKEN_TRANSPARENT
:
2619 if (possibly_handle_first_page_transition())
2628 curdiv
->transparent_output(transparent_translate(cc
));
2630 curdiv
->transparent_output(n
);
2631 } while (cc
!= '\n' && cc
!= EOF
);
2633 curdiv
->transparent_output('\n');
2638 case token::TOKEN_NEWLINE
:
2640 if (bol
&& !have_input
2641 && !curenv
->get_prev_line_interrupted())
2642 trapping_blank_line();
2649 case token::TOKEN_REQUEST
:
2651 int request_code
= tok
.c
;
2653 switch (request_code
) {
2657 case COPY_FILE_REQUEST
:
2660 case TRANSPARENT_FILE_REQUEST
:
2664 case VJUSTIFY_REQUEST
:
2675 case token::TOKEN_SPACE
:
2677 if (possibly_handle_first_page_transition())
2679 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2681 // save space_width now so that it isn't changed by \f or \s
2682 // which we wouldn't notice here
2683 hunits space_width
= curenv
->get_space_width();
2685 nspaces
+= tok
.nspaces();
2687 } while (tok
.space());
2689 trapping_blank_line();
2693 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2694 curenv
->get_fill_color()));
2704 case token::TOKEN_EOF
:
2706 case token::TOKEN_NODE
:
2708 if (possibly_handle_first_page_transition())
2710 else if (tok
.nd
->reread(&bol
)) {
2715 curenv
->add_node(tok
.nd
);
2718 curenv
->possibly_break_line(1);
2722 case token::TOKEN_PAGE_EJECTOR
:
2724 continue_page_eject();
2725 // I think we just want to preserve bol.
2729 case token::TOKEN_BEGIN_TRAP
:
2731 trap_bol_stack
.push(bol
);
2735 case token::TOKEN_END_TRAP
:
2737 if (trap_bol_stack
.is_empty())
2738 error("spurious end trap token detected!");
2740 bol
= trap_bol_stack
.pop();
2742 /* I'm not totally happy about this. But I can't think of any other
2743 way to do it. Doing an output_pending_lines() whenever a
2744 TOKEN_END_TRAP is detected doesn't work: for example,
2757 a\%very\%very\%long\%word
2759 will print all but the first lines from the word immediately
2760 after the footer, rather than on the next page. */
2762 if (trap_bol_stack
.is_empty())
2763 curenv
->output_pending_lines();
2775 trap_sprung_flag
= 0;
2779 #ifdef WIDOW_CONTROL
2781 void flush_pending_lines()
2783 while (!tok
.newline() && !tok
.eof())
2785 curenv
->output_pending_lines();
2789 #endif /* WIDOW_CONTROL */
2791 request_or_macro::request_or_macro()
2795 macro
*request_or_macro::to_macro()
2800 request::request(REQUEST_FUNCP pp
) : p(pp
)
2804 void request::invoke(symbol
)
2810 enum { SIZE
= 128 };
2811 unsigned char s
[SIZE
];
2816 char_block::char_block()
2825 void append(unsigned char);
2826 void set(unsigned char, int);
2827 unsigned char get(int);
2834 friend class macro_header
;
2835 friend class string_iterator
;
2838 char_list::char_list()
2839 : ptr(0), len(0), head(0), tail(0)
2843 char_list::~char_list()
2846 char_block
*tem
= head
;
2852 int char_list::length()
2857 void char_list::append(unsigned char c
)
2860 head
= tail
= new char_block
;
2864 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2865 tail
->next
= new char_block
;
2874 void char_list::set(unsigned char c
, int offset
)
2876 assert(len
> offset
);
2877 // optimization for access at the end
2878 int boundary
= len
- len
% char_block::SIZE
;
2879 if (offset
>= boundary
) {
2880 *(tail
->s
+ offset
- boundary
) = c
;
2883 char_block
*tem
= head
;
2886 l
+= char_block::SIZE
;
2888 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
2895 unsigned char char_list::get(int offset
)
2897 assert(len
> offset
);
2898 // optimization for access at the end
2899 int boundary
= len
- len
% char_block::SIZE
;
2900 if (offset
>= boundary
)
2901 return *(tail
->s
+ offset
- boundary
);
2902 char_block
*tem
= head
;
2905 l
+= char_block::SIZE
;
2907 return *(tem
->s
+ offset
% char_block::SIZE
);
2918 void append(node
*);
2922 friend class macro_header
;
2923 friend class string_iterator
;
2926 void node_list::append(node
*n
)
2934 tail
= tail
->next
= n
;
2938 int node_list::length()
2941 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2946 node_list::node_list()
2951 node
*node_list::extract()
2958 node_list::~node_list()
2960 delete_node_list(head
);
2963 struct macro_header
{
2968 macro_header() { count
= 1; }
2969 macro_header
*copy(int);
2974 if (p
!= 0 && --(p
->count
) <= 0)
2980 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2989 macro::macro(const macro
&m
)
2990 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
2991 empty_macro(m
.empty_macro
)
2997 macro
¯o::operator=(const macro
&m
)
2999 // don't assign object
3002 if (p
!= 0 && --(p
->count
) <= 0)
3005 filename
= m
.filename
;
3008 empty_macro
= m
.empty_macro
;
3012 void macro::append(unsigned char c
)
3016 p
= new macro_header
;
3017 if (p
->cl
.length() != len
) {
3018 macro_header
*tem
= p
->copy(len
);
3019 if (--(p
->count
) <= 0)
3025 if (c
!= COMPATIBLE_SAVE
&& c
!= COMPATIBLE_RESTORE
)
3029 void macro::set(unsigned char c
, int offset
)
3033 p
->cl
.set(c
, offset
);
3036 unsigned char macro::get(int offset
)
3039 return p
->cl
.get(offset
);
3047 void macro::append_str(const char *s
)
3052 while (s
[i
] != (char)0) {
3059 void macro::append(node
*n
)
3063 p
= new macro_header
;
3064 if (p
->cl
.length() != len
) {
3065 macro_header
*tem
= p
->copy(len
);
3066 if (--(p
->count
) <= 0)
3076 void macro::append_unsigned(unsigned int i
)
3078 unsigned int j
= i
/ 10;
3081 append(((unsigned char)(((int)'0') + i
% 10)));
3084 void macro::append_int(int i
)
3090 append_unsigned((unsigned int)i
);
3093 void macro::print_size()
3095 errprint("%1", len
);
3098 // make a copy of the first n bytes
3100 macro_header
*macro_header::copy(int n
)
3102 macro_header
*p
= new macro_header
;
3103 char_block
*bp
= cl
.head
;
3104 unsigned char *ptr
= bp
->s
;
3107 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3114 p
->nl
.append(nd
->copy());
3123 object_dictionary_iterator
iter(request_dictionary
);
3124 request_or_macro
*rm
;
3126 while (iter
.get(&s
, (object
**)&rm
)) {
3127 assert(!s
.is_null());
3128 macro
*m
= rm
->to_macro();
3130 errprint("%1\t", s
.contents());
3139 class string_iterator
: public input_iterator
{
3141 const char *how_invoked
;
3145 int count
; // of characters remaining
3147 int saved_compatible_flag
;
3152 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3155 int get_location(int, const char **, int *);
3157 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3158 int get_compatible_flag() { return saved_compatible_flag
; }
3161 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3162 : mac(m
), how_invoked(p
),
3163 newline_flag(0), lineno(1), nm(s
)
3167 bp
= mac
.p
->cl
.head
;
3168 nd
= mac
.p
->nl
.head
;
3178 string_iterator::string_iterator()
3189 int string_iterator::fill(node
**np
)
3196 const unsigned char *p
= eptr
;
3197 if (p
>= bp
->s
+ char_block::SIZE
) {
3209 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3214 unsigned char c
= *p
;
3215 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3229 int string_iterator::peek()
3233 const unsigned char *p
= eptr
;
3234 if (p
>= bp
->s
+ char_block::SIZE
) {
3240 int string_iterator::get_location(int allow_macro
,
3241 const char **filep
, int *linep
)
3245 if (mac
.filename
== 0)
3247 *filep
= mac
.filename
;
3248 *linep
= mac
.lineno
+ lineno
- 1;
3252 void string_iterator::backtrace()
3255 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3258 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3260 errprint(": %1\n", how_invoked
);
3267 class temp_iterator
: public input_iterator
{
3268 unsigned char *base
;
3269 temp_iterator(const char *, int len
);
3272 friend input_iterator
*make_temp_iterator(const char *);
3278 temp_iterator::temp_iterator(const char *s
, int len
)
3280 base
= new unsigned char[len
];
3281 memcpy(base
, s
, len
);
3286 temp_iterator::~temp_iterator()
3291 class small_temp_iterator
: public input_iterator
{
3293 small_temp_iterator(const char *, int);
3294 ~small_temp_iterator();
3295 enum { BLOCK
= 16 };
3296 static small_temp_iterator
*free_list
;
3297 void *operator new(size_t);
3298 void operator delete(void *);
3300 unsigned char buf
[SIZE
];
3301 friend input_iterator
*make_temp_iterator(const char *);
3304 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3306 void *small_temp_iterator::operator new(size_t n
)
3308 assert(n
== sizeof(small_temp_iterator
));
3311 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3312 for (int i
= 0; i
< BLOCK
- 1; i
++)
3313 free_list
[i
].next
= free_list
+ i
+ 1;
3314 free_list
[BLOCK
-1].next
= 0;
3316 small_temp_iterator
*p
= free_list
;
3317 free_list
= (small_temp_iterator
*)(free_list
->next
);
3325 void small_temp_iterator::operator delete(void *p
)
3328 ((small_temp_iterator
*)p
)->next
= free_list
;
3329 free_list
= (small_temp_iterator
*)p
;
3333 small_temp_iterator::~small_temp_iterator()
3340 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3342 for (int i
= 0; i
< len
; i
++)
3348 input_iterator
*make_temp_iterator(const char *s
)
3351 return new small_temp_iterator(s
, 0);
3354 if (n
<= small_temp_iterator::SIZE
)
3355 return new small_temp_iterator(s
, n
);
3357 return new temp_iterator(s
, n
);
3361 // this is used when macros with arguments are interpolated
3366 arg_list(const macro
&);
3370 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3374 arg_list::~arg_list()
3378 class macro_iterator
: public string_iterator
{
3382 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3385 int has_args() { return 1; }
3386 input_iterator
*get_arg(int i
);
3387 int nargs() { return argc
; }
3388 void add_arg(const macro
&m
);
3390 int is_macro() { return 1; }
3393 input_iterator
*macro_iterator::get_arg(int i
)
3396 return make_temp_iterator(nm
.contents());
3397 if (i
> 0 && i
<= argc
) {
3399 for (int j
= 1; j
< i
; j
++) {
3403 return new string_iterator(p
->mac
);
3409 void macro_iterator::add_arg(const macro
&m
)
3412 for (p
= &args
; *p
; p
= &((*p
)->next
))
3414 *p
= new arg_list(m
);
3418 void macro_iterator::shift(int n
)
3420 while (n
> 0 && argc
> 0) {
3421 arg_list
*tem
= args
;
3429 // This gets used by eg .if '\?xxx\?''.
3431 int operator==(const macro
&m1
, const macro
&m2
)
3433 if (m1
.len
!= m2
.len
)
3435 string_iterator
iter1(m1
);
3436 string_iterator
iter2(m2
);
3440 int c1
= iter1
.get(&nd1
);
3443 int c2
= iter2
.get(&nd2
);
3455 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3465 static void interpolate_macro(symbol nm
)
3467 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3470 const char *s
= nm
.contents();
3471 if (strlen(s
) > 2) {
3472 request_or_macro
*r
;
3477 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3479 macro
*m
= r
->to_macro();
3480 if (!m
|| !m
->empty())
3481 warned
= warning(WARN_SPACE
,
3482 "macro `%1' not defined "
3483 "(probably missing space after `%2')",
3484 nm
.contents(), buf
);
3488 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3490 request_dictionary
.define(nm
, p
);
3501 static void decode_args(macro_iterator
*mi
)
3503 if (!tok
.newline() && !tok
.eof()) {
3505 int c
= get_copy(&n
);
3509 if (c
== '\n' || c
== EOF
)
3512 int quote_input_level
= 0;
3513 int done_tab_warning
= 0;
3515 quote_input_level
= input_stack::get_level();
3518 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3519 if (quote_input_level
> 0 && c
== '\"'
3521 || input_stack::get_level() == quote_input_level
)) {
3534 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3535 warning(WARN_TAB
, "tab character in unquoted macro argument");
3536 done_tab_warning
= 1;
3548 static void decode_string_args(macro_iterator
*mi
)
3551 int c
= get_copy(&n
);
3555 if (c
== '\n' || c
== EOF
) {
3556 error("missing `]'");
3562 int quote_input_level
= 0;
3563 int done_tab_warning
= 0;
3565 quote_input_level
= input_stack::get_level();
3568 while (c
!= EOF
&& c
!= '\n'
3569 && !(c
== ']' && quote_input_level
== 0)
3570 && !(c
== ' ' && quote_input_level
== 0)) {
3571 if (quote_input_level
> 0 && c
== '\"'
3572 && input_stack::get_level() == quote_input_level
) {
3585 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3586 warning(WARN_TAB
, "tab character in unquoted string argument");
3587 done_tab_warning
= 1;
3598 void macro::invoke(symbol nm
)
3600 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3602 input_stack::push(mi
);
3606 macro
*macro::to_macro()
3613 return empty_macro
== 1;
3616 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
3617 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
3621 macro_iterator::macro_iterator() : args(0), argc(0)
3625 macro_iterator::~macro_iterator()
3628 arg_list
*tem
= args
;
3634 dictionary
composite_dictionary(17);
3636 void composite_request()
3638 symbol from
= get_name(1);
3639 if (!from
.is_null()) {
3640 const char *from_gn
= glyph_name_to_unicode(from
.contents());
3642 from_gn
= check_unicode_name(from
.contents());
3644 error("invalid composite glyph name `%1'", from
.contents());
3649 const char *from_decomposed
= decompose_unicode(from_gn
);
3650 if (from_decomposed
)
3651 from_gn
= &from_decomposed
[1];
3652 symbol to
= get_name(1);
3654 composite_dictionary
.remove(symbol(from_gn
));
3656 const char *to_gn
= glyph_name_to_unicode(to
.contents());
3658 to_gn
= check_unicode_name(to
.contents());
3660 error("invalid composite glyph name `%1'", to
.contents());
3665 const char *to_decomposed
= decompose_unicode(to_gn
);
3667 to_gn
= &to_decomposed
[1];
3668 if (strcmp(from_gn
, to_gn
) == 0)
3669 composite_dictionary
.remove(symbol(from_gn
));
3671 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
3677 static symbol
composite_glyph_name(symbol nm
)
3679 macro_iterator
*mi
= new macro_iterator();
3680 decode_string_args(mi
);
3681 input_stack::push(mi
);
3682 const char *gn
= glyph_name_to_unicode(nm
.contents());
3684 gn
= check_unicode_name(nm
.contents());
3686 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
3687 return EMPTY_SYMBOL
;
3690 const char *gn_decomposed
= decompose_unicode(gn
);
3691 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
3693 int n
= input_stack::nargs();
3694 for (int i
= 1; i
<= n
; i
++) {
3696 input_iterator
*p
= input_stack::get_arg(i
);
3699 while ((c
= p
->get(0)) != EOF
)
3702 const char *u
= glyph_name_to_unicode(gl
.contents());
3704 u
= check_unicode_name(gl
.contents());
3706 error("invalid component `%1' in composite glyph name",
3708 return EMPTY_SYMBOL
;
3711 const char *decomposed
= decompose_unicode(u
);
3714 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
3715 if (mapped_composite
)
3716 u
= (const char *)mapped_composite
;
3720 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
3722 return symbol(groff_gn
);
3726 return symbol(gl
.contents());
3729 int trap_sprung_flag
= 0;
3730 int postpone_traps_flag
= 0;
3731 symbol postponed_trap
;
3733 void spring_trap(symbol nm
)
3735 assert(!nm
.is_null());
3736 trap_sprung_flag
= 1;
3737 if (postpone_traps_flag
) {
3738 postponed_trap
= nm
;
3741 static char buf
[2] = { BEGIN_TRAP
, 0 };
3742 static char buf2
[2] = { END_TRAP
, '\0' };
3743 input_stack::push(make_temp_iterator(buf2
));
3744 request_or_macro
*p
= lookup_request(nm
);
3745 macro
*m
= p
->to_macro();
3747 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3749 error("you can't invoke a request with a trap");
3750 input_stack::push(make_temp_iterator(buf
));
3753 void postpone_traps()
3755 postpone_traps_flag
= 1;
3758 int unpostpone_traps()
3760 postpone_traps_flag
= 0;
3761 if (!postponed_trap
.is_null()) {
3762 spring_trap(postponed_trap
);
3763 postponed_trap
= NULL_SYMBOL
;
3772 macro_iterator
*mi
= new macro_iterator
;
3773 int reading_from_terminal
= isatty(fileno(stdin
));
3775 if (!tok
.newline() && !tok
.eof()) {
3776 int c
= get_copy(0);
3779 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3780 if (!invalid_input_char(c
)) {
3781 if (reading_from_terminal
)
3792 if (reading_from_terminal
) {
3793 fputc(had_prompt
? ':' : '\a', stderr
);
3796 input_stack::push(mi
);
3800 while ((c
= getchar()) != EOF
) {
3801 if (invalid_input_char(c
))
3802 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
3815 if (reading_from_terminal
)
3817 input_stack::push(new string_iterator(mac
));
3821 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3822 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
, CALLING_DISABLE_COMP
};
3824 void do_define_string(define_mode mode
, calling_mode calling
)
3838 else if (!tok
.space()) {
3839 error("bad string definition");
3850 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3851 macro
*mm
= rm
? rm
->to_macro() : 0;
3852 if (mode
== DEFINE_APPEND
&& mm
)
3854 if (calling
== CALLING_DISABLE_COMP
)
3855 mac
.append(COMPATIBLE_SAVE
);
3856 while (c
!= '\n' && c
!= EOF
) {
3860 mac
.append((unsigned char)c
);
3865 request_dictionary
.define(nm
, mm
);
3867 if (calling
== CALLING_DISABLE_COMP
)
3868 mac
.append(COMPATIBLE_RESTORE
);
3873 void define_string()
3875 do_define_string(DEFINE_NORMAL
, CALLING_NORMAL
);
3878 void define_nocomp_string()
3880 do_define_string(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
3883 void append_string()
3885 do_define_string(DEFINE_APPEND
, CALLING_NORMAL
);
3888 void append_nocomp_string()
3890 do_define_string(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
3893 void do_define_character(char_mode mode
, const char *font_name
)
3898 charinfo
*ci
= tok
.get_char(1);
3904 string
s(font_name
);
3906 s
+= ci
->nm
.contents();
3908 ci
= get_charinfo(symbol(s
.contents()));
3915 else if (!tok
.space()) {
3916 error("bad character definition");
3922 while (c
== ' ' || c
== '\t')
3926 macro
*m
= new macro
;
3927 while (c
!= '\n' && c
!= EOF
) {
3931 m
->append((unsigned char)c
);
3934 m
= ci
->setx_macro(m
, mode
);
3940 void define_character()
3942 do_define_character(CHAR_NORMAL
);
3945 void define_fallback_character()
3947 do_define_character(CHAR_FALLBACK
);
3950 void define_special_character()
3952 do_define_character(CHAR_SPECIAL
);
3955 static void remove_character()
3958 while (!tok
.newline() && !tok
.eof()) {
3959 if (!tok
.space() && !tok
.tab()) {
3960 charinfo
*ci
= tok
.get_char(1);
3963 macro
*m
= ci
->set_macro(0);
3972 static void interpolate_string(symbol nm
)
3974 request_or_macro
*p
= lookup_request(nm
);
3975 macro
*m
= p
->to_macro();
3977 error("you can only invoke a string or macro using \\*");
3979 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3980 input_stack::push(si
);
3984 static void interpolate_string_with_args(symbol s
)
3986 request_or_macro
*p
= lookup_request(s
);
3987 macro
*m
= p
->to_macro();
3989 error("you can only invoke a string or macro using \\*");
3991 macro_iterator
*mi
= new macro_iterator(s
, *m
);
3992 decode_string_args(mi
);
3993 input_stack::push(mi
);
3997 /* This class is used for the implementation of \$@. It is used for
3998 each of the closing double quotes. It artificially increases the
3999 input level by 2, so that the closing double quote will appear to have
4000 the same input level as the opening quote. */
4002 class end_quote_iterator
: public input_iterator
{
4003 unsigned char buf
[1];
4005 end_quote_iterator();
4006 ~end_quote_iterator() { }
4007 int internal_level() { return 2; }
4010 end_quote_iterator::end_quote_iterator()
4017 static void interpolate_arg(symbol nm
)
4019 const char *s
= nm
.contents();
4020 if (!s
|| *s
== '\0')
4021 copy_mode_error("missing argument name");
4022 else if (s
[1] == 0 && csdigit(s
[0]))
4023 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4024 else if (s
[0] == '*' && s
[1] == '\0') {
4025 for (int i
= input_stack::nargs(); i
> 0; i
--) {
4026 input_stack::push(input_stack::get_arg(i
));
4028 input_stack::push(make_temp_iterator(" "));
4031 else if (s
[0] == '@' && s
[1] == '\0') {
4032 for (int i
= input_stack::nargs(); i
> 0; i
--) {
4033 input_stack::push(new end_quote_iterator
);
4034 input_stack::push(input_stack::get_arg(i
));
4035 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
4040 for (p
= s
; *p
&& csdigit(*p
); p
++)
4043 copy_mode_error("bad argument name `%1'", s
);
4045 input_stack::push(input_stack::get_arg(atoi(s
)));
4049 void handle_first_page_transition()
4052 topdiv
->begin_page();
4055 // We push back a token by wrapping it up in a token_node, and
4056 // wrapping that up in a string_iterator.
4058 static void push_token(const token
&t
)
4061 m
.append(new token_node(t
));
4062 input_stack::push(new string_iterator(m
));
4065 void push_page_ejector()
4067 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4068 input_stack::push(make_temp_iterator(buf
));
4071 void handle_initial_request(unsigned char code
)
4077 mac
.append(new token_node(tok
));
4078 input_stack::push(new string_iterator(mac
));
4079 input_stack::push(make_temp_iterator(buf
));
4080 topdiv
->begin_page();
4084 void handle_initial_title()
4086 handle_initial_request(TITLE_REQUEST
);
4089 // this should be local to define_macro, but cfront 1.2 doesn't support that
4090 static symbol
dot_symbol(".");
4092 void do_define_macro(define_mode mode
, calling_mode calling
)
4095 if (calling
== CALLING_INDIRECT
) {
4096 symbol temp1
= get_name(1);
4097 if (temp1
.is_null()) {
4101 symbol temp2
= get_name();
4102 input_stack::push(make_temp_iterator("\n"));
4103 if (!temp2
.is_null()) {
4104 interpolate_string(temp2
);
4105 input_stack::push(make_temp_iterator(" "));
4107 interpolate_string(temp1
);
4108 input_stack::push(make_temp_iterator(" "));
4111 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4118 term
= get_name(); // the request that terminates the definition
4121 while (!tok
.newline() && !tok
.eof())
4123 const char *start_filename
;
4125 int have_start_location
= input_stack::get_location(0, &start_filename
,
4128 // doing this here makes the line numbers come out right
4129 int c
= get_copy(&n
, 1);
4132 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4133 request_or_macro
*rm
=
4134 (request_or_macro
*)request_dictionary
.lookup(nm
);
4136 mm
= rm
->to_macro();
4137 if (mm
&& mode
== DEFINE_APPEND
)
4141 if (calling
== CALLING_DISABLE_COMP
)
4142 mac
.append(COMPATIBLE_SAVE
);
4144 while (c
== ESCAPE_NEWLINE
) {
4145 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4147 c
= get_copy(&n
, 1);
4149 if (bol
&& c
== '.') {
4150 const char *s
= term
.contents();
4152 // see if it matches term
4155 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4157 if ((unsigned char)s
[0] == d
) {
4158 for (i
= 1; s
[i
] != 0; i
++) {
4160 if ((unsigned char)s
[i
] != d
)
4166 && ((i
== 2 && compatible_flag
)
4167 || (d
= get_copy(&n
)) == ' '
4168 || d
== '\n')) { // we found it
4173 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4176 request_dictionary
.define(nm
, mm
);
4178 if (calling
== CALLING_DISABLE_COMP
)
4179 mac
.append(COMPATIBLE_RESTORE
);
4182 if (term
!= dot_symbol
) {
4184 interpolate_macro(term
);
4190 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4192 for (int j
= 0; j
< i
; j
++)
4198 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4199 if (have_start_location
)
4200 error_with_file_and_line(start_filename
, start_lineno
,
4201 "end of file while defining macro `%1'",
4204 error("end of file while defining macro `%1'", nm
.contents());
4207 if (have_start_location
)
4208 error_with_file_and_line(start_filename
, start_lineno
,
4209 "end of file while ignoring input lines");
4211 error("end of file while ignoring input lines");
4216 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4223 c
= get_copy(&n
, 1);
4229 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
);
4232 void define_nocomp_macro()
4234 do_define_macro(DEFINE_NORMAL
, CALLING_DISABLE_COMP
);
4237 void define_indirect_macro()
4239 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
);
4244 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
);
4247 void append_indirect_macro()
4249 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
);
4252 void append_nocomp_macro()
4254 do_define_macro(DEFINE_APPEND
, CALLING_DISABLE_COMP
);
4260 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
);
4267 symbol s
= get_name();
4270 request_dictionary
.remove(s
);
4277 symbol s1
= get_name(1);
4278 if (!s1
.is_null()) {
4279 symbol s2
= get_name(1);
4281 request_dictionary
.rename(s1
, s2
);
4288 symbol s1
= get_name(1);
4289 if (!s1
.is_null()) {
4290 symbol s2
= get_name(1);
4291 if (!s2
.is_null()) {
4292 if (!request_dictionary
.alias(s1
, s2
))
4293 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4301 symbol s
= get_name(1);
4303 request_or_macro
*p
= lookup_request(s
);
4304 macro
*m
= p
->to_macro();
4306 error("cannot chop request");
4307 else if (m
->empty())
4308 error("cannot chop empty macro");
4310 int have_restore
= 0;
4311 // we have to check for additional save/restore pairs which could be
4312 // there due to empty am1 requests.
4314 if (m
->get(m
->len
- 1) != COMPATIBLE_RESTORE
)
4318 if (m
->get(m
->len
- 1) != COMPATIBLE_SAVE
)
4326 error("cannot chop empty macro");
4329 m
->set(COMPATIBLE_RESTORE
, m
->len
- 1);
4338 void substring_request()
4340 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4341 symbol s
= get_name(1);
4342 if (!s
.is_null() && get_integer(&start
)) {
4343 request_or_macro
*p
= lookup_request(s
);
4344 macro
*m
= p
->to_macro();
4346 error("cannot apply `substring' on a request");
4349 if (!has_arg() || get_integer(&end
)) {
4350 int real_length
= 0; // 1, 2, ..., n
4351 string_iterator
iter1(*m
);
4352 for (int l
= 0; l
< m
->len
; l
++) {
4353 int c
= iter1
.get(0);
4354 if (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4361 start
+= real_length
;
4369 if (start
>= real_length
|| end
< 0) {
4371 "start and end index of substring out of range");
4374 if (--(m
->p
->count
) <= 0)
4383 "start index of substring out of range, set to 0");
4386 if (end
>= real_length
) {
4388 "end index of substring out of range, set to string length");
4389 end
= real_length
- 1;
4391 // now extract the substring
4392 string_iterator
iter(*m
);
4394 for (i
= 0; i
< start
; i
++) {
4395 int c
= iter
.get(0);
4396 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4402 for (; i
<= end
; i
++) {
4404 int c
= iter
.get(&nd
);
4405 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4412 mac
.append((unsigned char)c
);
4421 void length_request()
4425 if (ret
.is_null()) {
4435 else if (!tok
.space()) {
4436 error("bad string definition");
4447 while (c
!= '\n' && c
!= EOF
) {
4451 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4455 set_number_reg(ret
, len
);
4459 void asciify_macro()
4461 symbol s
= get_name(1);
4463 request_or_macro
*p
= lookup_request(s
);
4464 macro
*m
= p
->to_macro();
4466 error("cannot asciify request");
4469 string_iterator
iter(*m
);
4472 int c
= iter
.get(&nd
);
4486 void unformat_macro()
4488 symbol s
= get_name(1);
4490 request_or_macro
*p
= lookup_request(s
);
4491 macro
*m
= p
->to_macro();
4493 error("cannot unformat request");
4496 string_iterator
iter(*m
);
4499 int c
= iter
.get(&nd
);
4505 if (nd
->set_unformat_flag())
4515 static void interpolate_environment_variable(symbol nm
)
4517 const char *s
= getenv(nm
.contents());
4519 input_stack::push(make_temp_iterator(s
));
4522 void interpolate_number_reg(symbol nm
, int inc
)
4524 reg
*r
= lookup_number_reg(nm
);
4529 input_stack::push(make_temp_iterator(r
->get_string()));
4532 static void interpolate_number_format(symbol nm
)
4534 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4536 input_stack::push(make_temp_iterator(r
->get_format()));
4539 static int get_delim_number(units
*n
, int si
, int prev_value
)
4543 if (start
.delimiter(1)) {
4545 if (get_number(n
, si
, prev_value
)) {
4547 warning(WARN_DELIM
, "closing delimiter does not match");
4554 static int get_delim_number(units
*n
, int si
)
4558 if (start
.delimiter(1)) {
4560 if (get_number(n
, si
)) {
4562 warning(WARN_DELIM
, "closing delimiter does not match");
4569 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
4573 int start_level
= input_stack::get_level();
4574 if (!start
.delimiter(1))
4577 if (get_number(n
, si
)) {
4578 if (tok
.dummy() || tok
.transparent_dummy())
4580 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4581 *cp
= tok
.get_char(1);
4584 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4585 warning(WARN_DELIM
, "closing delimiter does not match");
4591 static int read_size(int *x
)
4601 else if (c
== '+') {
4612 // allow an increment either before or after the left parenthesis
4618 else if (c
== '+') {
4633 val
= val
*10 + (c
- '0');
4638 else if (csdigit(c
)) {
4640 if (!inc
&& c
!= '0' && c
< '4') {
4646 val
= val
*10 + (c
- '0');
4650 else if (!tok
.delimiter(1))
4656 ? get_number(&val
, 'z')
4657 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4659 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4660 if (start
.ch() == '[')
4661 error("missing `]'");
4663 error("missing closing delimiter");
4671 // special case -- \s[0] and \s0 means to revert to previous size
4678 *x
= curenv
->get_requested_point_size() + val
;
4681 *x
= curenv
->get_requested_point_size() - val
;
4688 "\\s request results in non-positive point size; set to 1");
4694 error("bad digit in point size");
4699 static symbol
get_delim_name()
4704 error("end of input at start of delimited name");
4707 if (start
.newline()) {
4708 error("can't delimit name with a newline");
4711 int start_level
= input_stack::get_level();
4712 char abuf
[ABUF_SIZE
];
4714 int buf_size
= ABUF_SIZE
;
4717 if (i
+ 1 > buf_size
) {
4719 buf
= new char[ABUF_SIZE
*2];
4720 memcpy(buf
, abuf
, buf_size
);
4721 buf_size
= ABUF_SIZE
*2;
4724 char *old_buf
= buf
;
4725 buf
= new char[buf_size
*2];
4726 memcpy(buf
, old_buf
, buf_size
);
4733 && (compatible_flag
|| input_stack::get_level() == start_level
))
4735 if ((buf
[i
] = tok
.ch()) == 0) {
4736 error("missing delimiter (got %1)", tok
.description());
4746 error("empty delimited name");
4761 static void do_register()
4765 if (!start
.delimiter(1))
4768 symbol nm
= get_long_name(1);
4773 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4775 if (!r
|| !r
->get_value(&prev_value
))
4778 if (!get_number(&val
, 'u', prev_value
))
4781 warning(WARN_DELIM
, "closing delimiter does not match");
4785 set_number_reg(nm
, val
);
4788 // this implements the \w escape sequence
4790 static void do_width()
4794 int start_level
= input_stack::get_level();
4795 environment
env(curenv
);
4796 environment
*oldenv
= curenv
;
4801 warning(WARN_DELIM
, "missing closing delimiter");
4804 if (tok
.newline()) {
4805 warning(WARN_DELIM
, "missing closing delimiter");
4806 input_stack::push(make_temp_iterator("\n"));
4810 && (compatible_flag
|| input_stack::get_level() == start_level
))
4815 units x
= env
.get_input_line_position().to_units();
4816 input_stack::push(make_temp_iterator(i_to_a(x
)));
4817 env
.width_registers();
4822 charinfo
*page_character
;
4824 void set_page_character()
4826 page_character
= get_optional_char();
4830 static const symbol
percent_symbol("%");
4832 void read_title_parts(node
**part
, hunits
*part_width
)
4835 if (tok
.newline() || tok
.eof())
4838 int start_level
= input_stack::get_level();
4840 for (int i
= 0; i
< 3; i
++) {
4841 while (!tok
.newline() && !tok
.eof()) {
4843 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
4847 if (page_character
!= 0 && tok
.get_char() == page_character
)
4848 interpolate_number_reg(percent_symbol
, 0);
4853 curenv
->wrap_up_tab();
4854 part_width
[i
] = curenv
->get_input_line_position();
4855 part
[i
] = curenv
->extract_output_line();
4857 while (!tok
.newline() && !tok
.eof())
4861 class non_interpreted_node
: public node
{
4864 non_interpreted_node(const macro
&);
4865 int interpret(macro
*);
4872 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
4876 int non_interpreted_node::same(node
*nd
)
4878 return mac
== ((non_interpreted_node
*)nd
)->mac
;
4881 const char *non_interpreted_node::type()
4883 return "non_interpreted_node";
4886 int non_interpreted_node::force_tprint()
4891 node
*non_interpreted_node::copy()
4893 return new non_interpreted_node(mac
);
4896 int non_interpreted_node::interpret(macro
*m
)
4898 string_iterator
si(mac
);
4912 static node
*do_non_interpreted()
4917 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4922 if (c
== EOF
|| c
== '\n') {
4923 error("missing \\?");
4926 return new non_interpreted_node(mac
);
4929 static void encode_char(macro
*mac
, char c
)
4932 if ((font::use_charnames_in_special
) && tok
.special()) {
4933 charinfo
*ci
= tok
.get_char(1);
4934 const char *s
= ci
->get_symbol()->contents();
4935 if (s
[0] != (char)0) {
4939 while (s
[i
] != (char)0) {
4947 else if (tok
.stretchable_space()
4948 || tok
.unstretchable_space())
4950 else if (!(tok
.hyphen_indicator()
4952 || tok
.transparent_dummy()
4953 || tok
.zero_width_break()))
4954 error("%1 is invalid within \\X", tok
.description());
4957 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4959 * add escape escape sequence
4971 int start_level
= input_stack::get_level();
4974 tok
!= start
|| input_stack::get_level() != start_level
;
4977 warning(WARN_DELIM
, "missing closing delimiter");
4980 if (tok
.newline()) {
4981 input_stack::push(make_temp_iterator("\n"));
4982 warning(WARN_DELIM
, "missing closing delimiter");
4990 else if (tok
.leader())
4992 else if (tok
.backspace())
4996 encode_char(&mac
, c
);
4998 return new special_node(mac
);
5001 void output_request()
5003 if (!tok
.newline() && !tok
.eof()) {
5011 if (c
!= ' ' && c
!= '\t')
5014 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5015 topdiv
->transparent_output(c
);
5016 topdiv
->transparent_output('\n');
5021 extern int image_no
; // from node.cc
5023 static node
*do_suppress(symbol nm
)
5025 if (nm
.is_null() || nm
.is_empty()) {
5026 error("expecting an argument to escape \\O");
5029 const char *s
= nm
.contents();
5032 if (begin_level
== 0)
5033 // suppress generation of glyphs
5034 return new suppress_node(0, 0);
5037 if (begin_level
== 0)
5038 // enable generation of glyphs
5039 return new suppress_node(1, 0);
5042 if (begin_level
== 0)
5043 return new suppress_node(1, 1);
5053 s
++; // move over '5'
5055 if (*s
== (char)0) {
5056 error("missing position and filename in \\O");
5059 if (!(position
== 'l'
5062 || position
== 'i')) {
5063 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5066 s
++; // onto image name
5067 if (s
== (char *)0) {
5068 error("missing image name for \\O");
5072 if (begin_level
== 0)
5073 return new suppress_node(symbol(s
), position
, image_no
);
5077 error("`%1' is an invalid argument to \\O", *s
);
5082 void special_node::tprint(troff_output_file
*out
)
5085 string_iterator
iter(mac
);
5087 int c
= iter
.get(0);
5090 for (const char *s
= ::asciify(c
); *s
; s
++)
5091 tprint_char(out
, *s
);
5096 int get_file_line(const char **filename
, int *lineno
)
5098 return input_stack::get_location(0, filename
, lineno
);
5104 if (get_integer(&n
)) {
5105 const char *filename
= 0;
5107 symbol s
= get_long_name();
5108 filename
= s
.contents();
5110 (void)input_stack::set_location(filename
, n
-1);
5115 static int nroff_mode
= 0;
5117 static void nroff_request()
5123 static void troff_request()
5129 static void skip_alternative()
5132 // ensure that ``.if 0\{'' works as expected
5133 if (tok
.left_brace())
5137 c
= input_stack::get(0);
5140 if (c
== ESCAPE_LEFT_BRACE
)
5142 else if (c
== ESCAPE_RIGHT_BRACE
)
5144 else if (c
== escape_char
&& escape_char
> 0)
5145 switch(input_stack::get(0)) {
5153 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5157 Note that the level can properly be < 0, eg
5163 So don't give an error message in this case.
5165 if (level
<= 0 && c
== '\n')
5171 static void begin_alternative()
5173 while (tok
.space() || tok
.left_brace())
5183 static int_stack if_else_stack
;
5190 while (tok
.ch() == '!') {
5195 unsigned char c
= tok
.ch();
5198 result
= !nroff_mode
;
5200 else if (c
== 'n') {
5202 result
= nroff_mode
;
5204 else if (c
== 'v') {
5208 else if (c
== 'o') {
5209 result
= (topdiv
->get_page_number() & 1);
5212 else if (c
== 'e') {
5213 result
= !(topdiv
->get_page_number() & 1);
5216 else if (c
== 'd' || c
== 'r') {
5218 symbol nm
= get_name(1);
5224 ? request_dictionary
.lookup(nm
) != 0
5225 : number_reg_dictionary
.lookup(nm
) != 0);
5227 else if (c
== 'm') {
5229 symbol nm
= get_long_name(1);
5234 result
= (nm
== default_symbol
5235 || color_dictionary
.lookup(nm
) != 0);
5237 else if (c
== 'c') {
5240 charinfo
*ci
= tok
.get_char(1);
5245 result
= character_exists(ci
, curenv
);
5248 else if (tok
.space())
5250 else if (tok
.delimiter()) {
5252 int delim_level
= input_stack::get_level();
5253 environment
env1(curenv
);
5254 environment
env2(curenv
);
5255 environment
*oldenv
= curenv
;
5257 for (int i
= 0; i
< 2; i
++) {
5260 if (tok
.newline() || tok
.eof()) {
5261 warning(WARN_DELIM
, "missing closing delimiter");
5267 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5273 node
*n1
= env1
.extract_output_line();
5274 node
*n2
= env2
.extract_output_line();
5275 result
= same_node_list(n1
, n2
);
5276 delete_node_list(n1
);
5277 delete_node_list(n2
);
5284 if (!get_number(&n
, 'u')) {
5294 begin_alternative();
5300 void if_else_request()
5302 if_else_stack
.push(do_if_request());
5312 if (if_else_stack
.is_empty()) {
5313 warning(WARN_EL
, "unbalanced .el request");
5317 if (if_else_stack
.pop())
5320 begin_alternative();
5324 static int while_depth
= 0;
5325 static int while_break_flag
= 0;
5327 void while_request()
5332 mac
.append(new token_node(tok
));
5335 int c
= input_stack::get(&n
);
5351 if (c
== ESCAPE_LEFT_BRACE
)
5353 else if (c
== ESCAPE_RIGHT_BRACE
)
5355 else if (c
== escape_char
)
5358 if (c
== '\n' && level
<= 0)
5363 error("unbalanced \\{ \\}");
5366 input_stack::add_boundary();
5368 input_stack::push(new string_iterator(mac
, "while loop"));
5370 if (!do_if_request()) {
5371 while (input_stack::get(0) != EOF
)
5375 process_input_stack();
5376 if (while_break_flag
|| input_stack::is_return_boundary()) {
5377 while_break_flag
= 0;
5381 input_stack::remove_boundary();
5387 void while_break_request()
5390 error("no while loop");
5394 while_break_flag
= 1;
5395 while (input_stack::get(0) != EOF
)
5401 void while_continue_request()
5404 error("no while loop");
5408 while (input_stack::get(0) != EOF
)
5418 symbol nm
= get_long_name(1);
5422 while (!tok
.newline() && !tok
.eof())
5425 FILE *fp
= fopen(nm
.contents(), "r");
5427 input_stack::push(new file_iterator(fp
, nm
.contents()));
5429 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5434 // like .so but use popen()
5439 error(".pso request not allowed in safer mode");
5443 #ifdef POPEN_MISSING
5444 error("pipes not available on this system");
5446 #else /* not POPEN_MISSING */
5447 if (tok
.newline() || tok
.eof())
5448 error("missing command");
5451 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5454 char *buf
= new char[buf_size
];
5456 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5457 const char *s
= asciify(c
);
5458 int slen
= strlen(s
);
5459 if (buf_used
+ slen
+ 1> buf_size
) {
5460 char *old_buf
= buf
;
5461 int old_buf_size
= buf_size
;
5463 buf
= new char[buf_size
];
5464 memcpy(buf
, old_buf
, old_buf_size
);
5467 strcpy(buf
+ buf_used
, s
);
5470 buf
[buf_used
] = '\0';
5472 FILE *fp
= popen(buf
, POPEN_RT
);
5474 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5476 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5480 #endif /* not POPEN_MISSING */
5486 static int llx_reg_contents
= 0;
5487 static int lly_reg_contents
= 0;
5488 static int urx_reg_contents
= 0;
5489 static int ury_reg_contents
= 0;
5491 struct bounding_box
{
5492 int llx
, lly
, urx
, ury
;
5495 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5496 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5498 int parse_bounding_box(char *p
, bounding_box
*bb
)
5500 if (sscanf(p
, "%d %d %d %d",
5501 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5504 /* The Document Structuring Conventions say that the numbers
5505 should be integers. Unfortunately some broken applications
5507 double x1
, x2
, x3
, x4
;
5508 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5516 for (; *p
== ' ' || *p
== '\t'; p
++)
5518 if (strncmp(p
, "(atend)", 7) == 0) {
5523 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5527 // This version is taken from psrm.cc
5529 #define PS_LINE_MAX 255
5530 cset
white_space("\n\r \t");
5532 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5541 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5542 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5543 error("invalid input character code %1 in `%2'", int(c
), filename
);
5544 else if (i
< PS_LINE_MAX
)
5548 error("PostScript file `%1' is non-conforming "
5549 "because length of line exceeds 255", filename
);
5557 if (c
!= EOF
&& c
!= '\n')
5563 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5565 llx_reg_contents
= llx
;
5566 lly_reg_contents
= lly
;
5567 urx_reg_contents
= urx
;
5568 ury_reg_contents
= ury
;
5571 void do_ps_file(FILE *fp
, const char* filename
)
5575 char buf
[PS_LINE_MAX
];
5576 llx_reg_contents
= lly_reg_contents
=
5577 urx_reg_contents
= ury_reg_contents
= 0;
5578 if (!ps_get_line(buf
, fp
, filename
)) {
5579 error("`%1' is empty", filename
);
5582 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5583 error("`%1' is not conforming to the Document Structuring Conventions",
5587 while (ps_get_line(buf
, fp
, filename
) != 0) {
5588 if (buf
[0] != '%' || buf
[1] != '%'
5589 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5591 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5592 int res
= parse_bounding_box(buf
+ 14, &bb
);
5594 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5597 else if (res
== 2) {
5602 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5611 /* in the trailer, the last BoundingBox comment is significant */
5612 for (offset
= 512; !last_try
; offset
*= 2) {
5613 int had_trailer
= 0;
5615 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5617 if (fseek(fp
, 0L, 0) == -1)
5620 while (ps_get_line(buf
, fp
, filename
) != 0) {
5621 if (buf
[0] == '%' && buf
[1] == '%') {
5623 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5627 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5628 int res
= parse_bounding_box(buf
+ 14, &bb
);
5631 else if (res
== 2) {
5632 error("`(atend)' not allowed in trailer of `%1'", filename
);
5636 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5645 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5650 error("%%%%BoundingBox comment not found in `%1'", filename
);
5653 void ps_bbox_request()
5655 symbol nm
= get_long_name(1);
5659 while (!tok
.newline() && !tok
.eof())
5662 // PS files might contain non-printable characters, such as ^Z
5663 // and CRs not followed by an LF, so open them in binary mode.
5664 FILE *fp
= fopen(nm
.contents(), FOPEN_RB
);
5666 do_ps_file(fp
, nm
.contents());
5670 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5675 const char *asciify(int c
)
5678 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5679 buf
[1] = buf
[2] = '\0';
5681 case ESCAPE_QUESTION
:
5684 case ESCAPE_AMPERSAND
:
5687 case ESCAPE_RIGHT_PARENTHESIS
:
5690 case ESCAPE_UNDERSCORE
:
5696 case ESCAPE_CIRCUMFLEX
:
5699 case ESCAPE_LEFT_BRACE
:
5702 case ESCAPE_RIGHT_BRACE
:
5705 case ESCAPE_LEFT_QUOTE
:
5708 case ESCAPE_RIGHT_QUOTE
:
5726 case ESCAPE_PERCENT
:
5738 case COMPATIBLE_SAVE
:
5739 case COMPATIBLE_RESTORE
:
5743 if (invalid_input_char(c
))
5752 const char *input_char_description(int c
)
5756 return "a newline character";
5758 return "a backspace character";
5760 return "a leader character";
5762 return "a tab character";
5764 return "a space character";
5768 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
5769 if (invalid_input_char(c
)) {
5770 const char *s
= asciify(c
);
5777 sprintf(buf
, "magic character code %d", c
);
5786 sprintf(buf
, "character code %d", c
);
5790 // .tm, .tm1, and .tmc
5792 void do_terminal(int newline
, int string_like
)
5794 if (!tok
.newline() && !tok
.eof()) {
5798 if (string_like
&& c
== '"') {
5802 if (c
!= ' ' && c
!= '\t')
5805 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5806 fputs(asciify(c
), stderr
);
5809 fputc('\n', stderr
);
5824 void terminal_continue()
5829 dictionary
stream_dictionary(20);
5831 void do_open(int append
)
5833 symbol stream
= get_name(1);
5834 if (!stream
.is_null()) {
5835 symbol filename
= get_long_name(1);
5836 if (!filename
.is_null()) {
5838 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
5840 error("can't open `%1' for %2: %3",
5841 filename
.contents(),
5842 append
? "appending" : "writing",
5844 fp
= (FILE *)stream_dictionary
.remove(stream
);
5847 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
5858 error(".open request not allowed in safer mode");
5865 void opena_request()
5868 error(".opena request not allowed in safer mode");
5875 void close_request()
5877 symbol stream
= get_name(1);
5878 if (!stream
.is_null()) {
5879 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
5881 error("no stream named `%1'", stream
.contents());
5888 // .write and .writec
5890 void do_write_request(int newline
)
5892 symbol stream
= get_name(1);
5893 if (stream
.is_null()) {
5897 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5899 error("no stream named `%1'", stream
.contents());
5904 while ((c
= get_copy(0)) == ' ')
5908 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5909 fputs(asciify(c
), fp
);
5916 void write_request()
5918 do_write_request(1);
5921 void write_request_continue()
5923 do_write_request(0);
5926 void write_macro_request()
5928 symbol stream
= get_name(1);
5929 if (stream
.is_null()) {
5933 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5935 error("no stream named `%1'", stream
.contents());
5939 symbol s
= get_name(1);
5944 request_or_macro
*p
= lookup_request(s
);
5945 macro
*m
= p
->to_macro();
5947 error("cannot write request");
5949 string_iterator
iter(*m
);
5951 int c
= iter
.get(0);
5954 fputs(asciify(c
), fp
);
5961 void warnscale_request()
5968 warn_scale
= (double)units_per_inch
;
5970 warn_scale
= (double)units_per_inch
/ 2.54;
5972 warn_scale
= (double)units_per_inch
/ 72.0;
5974 warn_scale
= (double)units_per_inch
/ 6.0;
5977 "invalid scaling indicator `%1', using `i' instead", c
);
5980 warn_scaling_indicator
= c
;
5985 void spreadwarn_request()
5988 if (has_arg() && get_hunits(&n
, 'm')) {
5991 hunits em
= curenv
->get_size();
5992 spread_limit
= (double)n
.to_units()
5993 / (em
.is_zero() ? hresolution
: em
.to_units());
5996 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
5997 // changing value; we mirror at
5998 // -0.5 to make zero a valid value
6002 static void init_charset_table()
6005 strcpy(buf
, "char");
6006 for (int i
= 0; i
< 256; i
++) {
6007 strcpy(buf
+ 4, i_to_a(i
));
6008 charset_table
[i
] = get_charinfo(symbol(buf
));
6009 charset_table
[i
]->set_ascii_code(i
);
6011 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6013 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6014 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6015 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6016 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6017 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6018 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6019 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6020 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6021 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6022 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6023 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6024 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6025 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6026 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6027 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6028 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6029 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6030 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6031 page_character
= charset_table
['%'];
6034 static void init_hpf_code_table()
6036 for (int i
= 0; i
< 256; i
++)
6037 hpf_code_table
[i
] = i
;
6040 static void do_translate(int translate_transparent
, int translate_input
)
6043 while (!tok
.newline() && !tok
.eof()) {
6045 // This is a really bizarre troff feature.
6047 translate_space_to_dummy
= tok
.dummy();
6048 if (tok
.newline() || tok
.eof())
6053 charinfo
*ci1
= tok
.get_char(1);
6057 if (tok
.newline() || tok
.eof()) {
6058 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6059 translate_transparent
);
6063 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6064 translate_transparent
);
6065 else if (tok
.stretchable_space())
6066 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6067 translate_transparent
);
6068 else if (tok
.dummy())
6069 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6070 translate_transparent
);
6071 else if (tok
.hyphen_indicator())
6072 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6073 translate_transparent
);
6075 charinfo
*ci2
= tok
.get_char(1);
6079 ci1
->set_translation(0, translate_transparent
, translate_input
);
6081 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6093 void translate_no_transparent()
6098 void translate_input()
6106 if (get_integer(&flags
))
6108 charinfo
*ci
= tok
.get_char(1);
6110 charinfo
*tem
= ci
->get_translation();
6113 ci
->set_flags(flags
);
6120 void hyphenation_code()
6123 while (!tok
.newline() && !tok
.eof()) {
6124 charinfo
*ci
= tok
.get_char(1);
6129 unsigned char c
= tok
.ch();
6131 error("hyphenation code must be ordinary character");
6135 error("hyphenation code cannot be digit");
6138 ci
->set_hyphenation_code(c
);
6139 if (ci
->get_translation()
6140 && ci
->get_translation()->get_translation_input())
6141 ci
->get_translation()->set_hyphenation_code(c
);
6148 void hyphenation_patterns_file_code()
6151 while (!tok
.newline() && !tok
.eof()) {
6153 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6155 error("missing output hyphenation code");
6158 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6159 hpf_code_table
[n1
] = n2
;
6163 error("output hyphenation code must be integer in the range 0..255");
6168 error("input hyphenation code must be integer in the range 0..255");
6175 charinfo
*token::get_char(int required
)
6177 if (type
== TOKEN_CHAR
)
6178 return charset_table
[c
];
6179 if (type
== TOKEN_SPECIAL
)
6180 return get_charinfo(nm
);
6181 if (type
== TOKEN_NUMBERED_CHAR
)
6182 return get_charinfo_by_number(val
);
6183 if (type
== TOKEN_ESCAPE
) {
6184 if (escape_char
!= 0)
6185 return charset_table
[escape_char
];
6187 error("`\\e' used while no current escape character");
6192 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6193 warning(WARN_MISSING
, "missing normal or special character");
6195 error("normal or special character expected (got %1)", description());
6200 charinfo
*get_optional_char()
6204 charinfo
*ci
= tok
.get_char();
6206 check_missing_character();
6212 void check_missing_character()
6214 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6215 error("normal or special character expected (got %1): "
6216 "treated as missing",
6222 int token::add_to_node_list(node
**pp
)
6229 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6235 if (escape_char
!= 0)
6236 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6238 case TOKEN_HYPHEN_INDICATOR
:
6239 *pp
= (*pp
)->add_discretionary_hyphen();
6241 case TOKEN_ITALIC_CORRECTION
:
6242 *pp
= (*pp
)->add_italic_correction(&w
);
6244 case TOKEN_LEFT_BRACE
:
6246 case TOKEN_MARK_INPUT
:
6247 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6253 case TOKEN_NUMBERED_CHAR
:
6254 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6256 case TOKEN_RIGHT_BRACE
:
6259 n
= new hmotion_node(curenv
->get_space_width(),
6260 curenv
->get_fill_color());
6263 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6265 case TOKEN_STRETCHABLE_SPACE
:
6266 n
= new unbreakable_space_node(curenv
->get_space_width(),
6267 curenv
->get_fill_color());
6269 case TOKEN_UNSTRETCHABLE_SPACE
:
6270 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6271 curenv
->get_fill_color());
6273 case TOKEN_TRANSPARENT_DUMMY
:
6274 n
= new transparent_dummy_node
;
6276 case TOKEN_ZERO_WIDTH_BREAK
:
6277 n
= new space_node(H0
, curenv
->get_fill_color());
6279 n
->is_escape_colon();
6291 void token::process()
6293 if (possibly_handle_first_page_transition())
6296 case TOKEN_BACKSPACE
:
6297 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6298 curenv
->get_fill_color()));
6301 curenv
->add_char(charset_table
[c
]);
6304 curenv
->add_node(new dummy_node
);
6313 if (escape_char
!= 0)
6314 curenv
->add_char(charset_table
[escape_char
]);
6316 case TOKEN_BEGIN_TRAP
:
6317 case TOKEN_END_TRAP
:
6318 case TOKEN_PAGE_EJECTOR
:
6319 // these are all handled in process_input_stack()
6321 case TOKEN_HYPHEN_INDICATOR
:
6322 curenv
->add_hyphen_indicator();
6324 case TOKEN_INTERRUPT
:
6325 curenv
->interrupt();
6327 case TOKEN_ITALIC_CORRECTION
:
6328 curenv
->add_italic_correction();
6331 curenv
->handle_tab(1);
6333 case TOKEN_LEFT_BRACE
:
6335 case TOKEN_MARK_INPUT
:
6336 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6342 curenv
->add_node(nd
);
6345 case TOKEN_NUMBERED_CHAR
:
6346 curenv
->add_char(get_charinfo_by_number(val
));
6349 // handled in process_input_stack()
6351 case TOKEN_RIGHT_BRACE
:
6357 curenv
->add_char(get_charinfo(nm
));
6362 case TOKEN_STRETCHABLE_SPACE
:
6363 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6364 curenv
->get_fill_color()));
6366 case TOKEN_UNSTRETCHABLE_SPACE
:
6367 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6368 curenv
->get_fill_color()));
6371 curenv
->handle_tab(0);
6373 case TOKEN_TRANSPARENT
:
6375 case TOKEN_TRANSPARENT_DUMMY
:
6376 curenv
->add_node(new transparent_dummy_node
);
6378 case TOKEN_ZERO_WIDTH_BREAK
:
6380 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6381 tmp
->freeze_space();
6382 tmp
->is_escape_colon();
6383 curenv
->add_node(tmp
);
6391 class nargs_reg
: public reg
{
6393 const char *get_string();
6396 const char *nargs_reg::get_string()
6398 return i_to_a(input_stack::nargs());
6401 class lineno_reg
: public reg
{
6403 const char *get_string();
6406 const char *lineno_reg::get_string()
6410 if (!input_stack::get_location(0, &file
, &line
))
6412 return i_to_a(line
);
6415 class writable_lineno_reg
: public general_reg
{
6417 writable_lineno_reg();
6418 void set_value(units
);
6419 int get_value(units
*);
6422 writable_lineno_reg::writable_lineno_reg()
6426 int writable_lineno_reg::get_value(units
*res
)
6430 if (!input_stack::get_location(0, &file
, &line
))
6436 void writable_lineno_reg::set_value(units n
)
6438 input_stack::set_location(0, n
);
6441 class filename_reg
: public reg
{
6443 const char *get_string();
6446 const char *filename_reg::get_string()
6450 if (input_stack::get_location(0, &file
, &line
))
6456 class constant_reg
: public reg
{
6459 constant_reg(const char *);
6460 const char *get_string();
6463 constant_reg::constant_reg(const char *p
) : s(p
)
6467 const char *constant_reg::get_string()
6472 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6476 const char *constant_int_reg::get_string()
6481 void abort_request()
6486 else if (tok
.newline())
6489 while ((c
= get_copy(0)) == ' ')
6492 if (c
== EOF
|| c
== '\n')
6493 fputs("User Abort.", stderr
);
6495 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6496 fputs(asciify(c
), stderr
);
6498 fputc('\n', stderr
);
6499 cleanup_and_exit(1);
6505 char *s
= new char[len
];
6507 while ((c
= get_copy(0)) == ' ')
6510 while (c
!= '\n' && c
!= EOF
) {
6511 if (!invalid_input_char(c
)) {
6514 s
= new char[len
*2];
6515 memcpy(s
, tem
, len
);
6535 error(".pi request not allowed in safer mode");
6539 #ifdef POPEN_MISSING
6540 error("pipes not available on this system");
6542 #else /* not POPEN_MISSING */
6544 error("can't pipe: output already started");
6549 if ((pc
= read_string()) == 0)
6550 error("can't pipe to empty command");
6552 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6553 strcpy(s
, pipe_command
);
6556 a_delete pipe_command
;
6563 #endif /* not POPEN_MISSING */
6567 static int system_status
;
6569 void system_request()
6572 error(".sy request not allowed in safer mode");
6576 char *command
= read_string();
6578 error("empty command");
6580 system_status
= system(command
);
6588 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6589 handle_initial_request(COPY_FILE_REQUEST
);
6592 symbol filename
= get_long_name(1);
6593 while (!tok
.newline() && !tok
.eof())
6597 if (!filename
.is_null())
6598 curdiv
->copy_file(filename
.contents());
6606 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6607 handle_initial_request(VJUSTIFY_REQUEST
);
6610 symbol type
= get_long_name(1);
6611 if (!type
.is_null())
6612 curdiv
->vjustify(type
);
6618 void transparent_file()
6620 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6621 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6624 symbol filename
= get_long_name(1);
6625 while (!tok
.newline() && !tok
.eof())
6629 if (!filename
.is_null()) {
6631 FILE *fp
= fopen(filename
.contents(), "r");
6633 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6640 if (invalid_input_char(c
))
6641 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6643 curdiv
->transparent_output(c
);
6648 curdiv
->transparent_output('\n');
6660 page_range(int, int, page_range
*);
6661 int contains(int n
);
6664 page_range::page_range(int i
, int j
, page_range
*p
)
6665 : first(i
), last(j
), next(p
)
6669 int page_range::contains(int n
)
6671 return n
>= first
&& (last
<= 0 || n
<= last
);
6674 page_range
*output_page_list
= 0;
6676 int in_output_page_list(int n
)
6678 if (!output_page_list
)
6680 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
6686 static void parse_output_page_list(char *p
)
6692 else if (csdigit(*p
)) {
6695 i
= i
*10 + *p
++ - '0';
6696 while (csdigit(*p
));
6706 j
= j
*10 + *p
++ - '0';
6707 while (csdigit(*p
));
6713 last_page_number
= -1;
6714 else if (last_page_number
>= 0 && j
> last_page_number
)
6715 last_page_number
= j
;
6716 output_page_list
= new page_range(i
, j
, output_page_list
);
6722 error("bad output page list");
6723 output_page_list
= 0;
6727 static FILE *open_mac_file(const char *mac
, char **path
)
6729 // Try first FOOBAR.tmac, then tmac.FOOBAR
6730 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
6732 strcat(s1
, MACRO_POSTFIX
);
6733 FILE *fp
= mac_path
->open_file(s1
, path
);
6736 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
6737 strcpy(s2
, MACRO_PREFIX
);
6739 fp
= mac_path
->open_file(s2
, path
);
6745 static void process_macro_file(const char *mac
)
6748 FILE *fp
= open_mac_file(mac
, &path
);
6750 fatal("can't find macro file %1", mac
);
6751 const char *s
= symbol(path
).contents();
6753 input_stack::push(new file_iterator(fp
, s
));
6755 process_input_stack();
6758 static void process_startup_file(char *filename
)
6761 search_path
*orig_mac_path
= mac_path
;
6762 mac_path
= &config_macro_path
;
6763 FILE *fp
= mac_path
->open_file(filename
, &path
);
6765 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6768 process_input_stack();
6770 mac_path
= orig_mac_path
;
6775 symbol nm
= get_long_name(1);
6779 while (!tok
.newline() && !tok
.eof())
6782 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
6783 // .mso doesn't (and cannot) go through open_mac_file, so we
6784 // need to do it here manually: If we have tmac.FOOBAR, try
6785 // FOOBAR.tmac and vice versa
6787 const char *fn
= nm
.contents();
6788 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
6789 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
6790 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
6791 strcat(s
, MACRO_POSTFIX
);
6792 fp
= mac_path
->open_file(s
, &path
);
6796 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
6797 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
6798 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
6799 strcpy(s
, MACRO_PREFIX
);
6800 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
6801 fp
= mac_path
->open_file(s
, &path
);
6807 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6811 error("can't find macro file `%1'", nm
.contents());
6816 static void process_input_file(const char *name
)
6819 if (strcmp(name
, "-") == 0) {
6825 fp
= fopen(name
, "r");
6827 fatal("can't open `%1': %2", name
, strerror(errno
));
6829 input_stack::push(new file_iterator(fp
, name
));
6831 process_input_stack();
6834 // make sure the_input is empty before calling this
6836 static int evaluate_expression(const char *expr
, units
*res
)
6838 input_stack::push(make_temp_iterator(expr
));
6840 int success
= get_number(res
, 'u');
6841 while (input_stack::get(0) != EOF
)
6846 static void do_register_assignment(const char *s
)
6848 const char *p
= strchr(s
, '=');
6854 if (evaluate_expression(s
+ 1, &n
))
6855 set_number_reg(buf
, n
);
6858 char *buf
= new char[p
- s
+ 1];
6859 memcpy(buf
, s
, p
- s
);
6862 if (evaluate_expression(p
+ 1, &n
))
6863 set_number_reg(buf
, n
);
6868 static void set_string(const char *name
, const char *value
)
6870 macro
*m
= new macro
;
6871 for (const char *p
= value
; *p
; p
++)
6872 if (!invalid_input_char((unsigned char)*p
))
6874 request_dictionary
.define(name
, m
);
6877 static void do_string_assignment(const char *s
)
6879 const char *p
= strchr(s
, '=');
6884 set_string(buf
, s
+ 1);
6887 char *buf
= new char[p
- s
+ 1];
6888 memcpy(buf
, s
, p
- s
);
6890 set_string(buf
, p
+ 1);
6895 struct string_list
{
6898 string_list(const char *ss
) : s(ss
), next(0) {}
6902 static void prepend_string(const char *s
, string_list
**p
)
6904 string_list
*l
= new string_list(s
);
6910 static void add_string(const char *s
, string_list
**p
)
6914 *p
= new string_list(s
);
6917 void usage(FILE *stream
, const char *prog
)
6920 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6921 " -rcn -Tname -Fdir -Mdir [files...]\n",
6925 int main(int argc
, char **argv
)
6927 program_name
= argv
[0];
6928 static char stderr_buf
[BUFSIZ
];
6929 setbuf(stderr
, stderr_buf
);
6931 string_list
*macros
= 0;
6932 string_list
*register_assignments
= 0;
6933 string_list
*string_assignments
= 0;
6938 int no_rc
= 0; // don't process troffrc and troffrc-end
6939 int next_page_number
;
6941 hresolution
= vresolution
= 1;
6942 // restore $PATH if called from groff
6943 char* groff_path
= getenv("GROFF_PATH__");
6950 if (putenv(strsave(e
.contents())))
6951 fatal("putenv failed");
6953 static const struct option long_options
[] = {
6954 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
6955 { "version", no_argument
, 0, 'v' },
6958 while ((c
= getopt_long(argc
, argv
, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6964 printf("GNU troff (groff) version %s\n", Version_string
);
6971 is_html
= (strcmp(device
, "html") == 0);
6974 compatible_flag
= 1;
6980 macro_path
.command_line_dir(optarg
);
6981 safer_macro_path
.command_line_dir(optarg
);
6982 config_macro_path
.command_line_dir(optarg
);
6985 font::command_line_font_dir(optarg
);
6988 add_string(optarg
, ¯os
);
6997 enable_warning(optarg
);
7000 disable_warning(optarg
);
7009 ascii_output_flag
= 1;
7012 suppress_output_flag
= 1;
7015 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7018 error("bad page number");
7021 parse_output_page_list(optarg
);
7024 if (*optarg
== '\0')
7025 error("`-d' requires non-empty argument");
7027 add_string(optarg
, &string_assignments
);
7030 if (*optarg
== '\0')
7031 error("`-r' requires non-empty argument");
7033 add_string(optarg
, ®ister_assignments
);
7036 default_family
= symbol(optarg
);
7042 // silently ignore these
7045 safer_flag
= 0; // unsafe behaviour
7047 case CHAR_MAX
+ 1: // --help
7048 usage(stdout
, argv
[0]);
7052 usage(stderr
, argv
[0]);
7054 break; // never reached
7059 mac_path
= ¯o_path
;
7060 set_string(".T", device
);
7061 init_charset_table();
7062 init_hpf_code_table();
7063 if (!font::load_desc())
7064 fatal("sorry, I can't continue");
7065 units_per_inch
= font::res
;
7066 hresolution
= font::hor
;
7067 vresolution
= font::vert
;
7068 sizescale
= font::sizescale
;
7069 tcommand_flag
= font::tcommand
;
7070 warn_scale
= (double)units_per_inch
;
7071 warn_scaling_indicator
= 'i';
7072 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7073 default_family
= symbol(font::family
);
7074 font_size::init_size_table(font::sizes
);
7077 if (font::style_table
) {
7078 for (i
= 0; font::style_table
[i
]; i
++)
7079 mount_style(j
++, symbol(font::style_table
[i
]));
7081 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7082 // In the DESC file a font name of 0 (zero) means leave this
7084 if (strcmp(font::font_name_table
[i
], "0") != 0)
7085 mount_font(j
, symbol(font::font_name_table
[i
]));
7086 curdiv
= topdiv
= new top_level_diversion
;
7088 topdiv
->set_next_page_number(next_page_number
);
7089 init_input_requests();
7090 init_env_requests();
7091 init_div_requests();
7093 init_column_requests();
7095 init_node_requests();
7096 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7098 init_reg_requests();
7099 init_hyphen_requests();
7100 init_environments();
7101 while (string_assignments
) {
7102 do_string_assignment(string_assignments
->s
);
7103 string_list
*tem
= string_assignments
;
7104 string_assignments
= string_assignments
->next
;
7107 while (register_assignments
) {
7108 do_register_assignment(register_assignments
->s
);
7109 string_list
*tem
= register_assignments
;
7110 register_assignments
= register_assignments
->next
;
7114 process_startup_file(INITIAL_STARTUP_FILE
);
7116 process_macro_file(macros
->s
);
7117 string_list
*tem
= macros
;
7118 macros
= macros
->next
;
7122 process_startup_file(FINAL_STARTUP_FILE
);
7123 for (i
= optind
; i
< argc
; i
++)
7124 process_input_file(argv
[i
]);
7125 if (optind
>= argc
|| iflag
)
7126 process_input_file("-");
7128 return 0; // not reached
7134 if (has_arg() && get_integer(&n
)) {
7135 if (n
& ~WARN_TOTAL
) {
7136 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7142 warning_mask
= WARN_TOTAL
;
7146 static void init_registers()
7148 #ifdef LONG_FOR_TIME_T
7150 #else /* not LONG_FOR_TIME_T */
7152 #endif /* not LONG_FOR_TIME_T */
7154 // Use struct here to work around misfeature in old versions of g++.
7155 struct tm
*tt
= localtime(&t
);
7156 set_number_reg("seconds", int(tt
->tm_sec
));
7157 set_number_reg("minutes", int(tt
->tm_min
));
7158 set_number_reg("hours", int(tt
->tm_hour
));
7159 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7160 set_number_reg("dy", int(tt
->tm_mday
));
7161 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7162 set_number_reg("year", int(1900 + tt
->tm_year
));
7163 set_number_reg("yr", int(tt
->tm_year
));
7164 set_number_reg("$$", getpid());
7165 number_reg_dictionary
.define(".A",
7166 new constant_reg(ascii_output_flag
7172 * registers associated with \O
7175 static int output_reg_minx_contents
= -1;
7176 static int output_reg_miny_contents
= -1;
7177 static int output_reg_maxx_contents
= -1;
7178 static int output_reg_maxy_contents
= -1;
7180 void check_output_limits(int x
, int y
)
7182 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7183 output_reg_minx_contents
= x
;
7184 if (x
> output_reg_maxx_contents
)
7185 output_reg_maxx_contents
= x
;
7186 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7187 output_reg_miny_contents
= y
;
7188 if (y
> output_reg_maxy_contents
)
7189 output_reg_maxy_contents
= y
;
7192 void reset_output_registers()
7194 output_reg_minx_contents
= -1;
7195 output_reg_miny_contents
= -1;
7196 output_reg_maxx_contents
= -1;
7197 output_reg_maxy_contents
= -1;
7200 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7202 *minx
= output_reg_minx_contents
;
7203 *miny
= output_reg_miny_contents
;
7204 *maxx
= output_reg_maxx_contents
;
7205 *maxy
= output_reg_maxy_contents
;
7208 void init_input_requests()
7210 init_request("ab", abort_request
);
7211 init_request("als", alias_macro
);
7212 init_request("am", append_macro
);
7213 init_request("am1", append_nocomp_macro
);
7214 init_request("ami", append_indirect_macro
);
7215 init_request("as", append_string
);
7216 init_request("as1", append_nocomp_string
);
7217 init_request("asciify", asciify_macro
);
7218 init_request("backtrace", backtrace_request
);
7219 init_request("blm", blank_line_macro
);
7220 init_request("break", while_break_request
);
7221 init_request("cf", copy_file
);
7222 init_request("cflags", char_flags
);
7223 init_request("char", define_character
);
7224 init_request("chop", chop_macro
);
7225 init_request("close", close_request
);
7226 init_request("color", activate_color
);
7227 init_request("composite", composite_request
);
7228 init_request("continue", while_continue_request
);
7229 init_request("cp", compatible
);
7230 init_request("de", define_macro
);
7231 init_request("de1", define_nocomp_macro
);
7232 init_request("defcolor", define_color
);
7233 init_request("dei", define_indirect_macro
);
7234 init_request("do", do_request
);
7235 init_request("ds", define_string
);
7236 init_request("ds1", define_nocomp_string
);
7237 init_request("ec", set_escape_char
);
7238 init_request("ecr", restore_escape_char
);
7239 init_request("ecs", save_escape_char
);
7240 init_request("el", else_request
);
7241 init_request("em", end_macro
);
7242 init_request("eo", escape_off
);
7243 init_request("ex", exit_request
);
7244 init_request("fchar", define_fallback_character
);
7245 #ifdef WIDOW_CONTROL
7246 init_request("fpl", flush_pending_lines
);
7247 #endif /* WIDOW_CONTROL */
7248 init_request("hcode", hyphenation_code
);
7249 init_request("hpfcode", hyphenation_patterns_file_code
);
7250 init_request("ie", if_else_request
);
7251 init_request("if", if_request
);
7252 init_request("ig", ignore
);
7253 init_request("length", length_request
);
7254 init_request("lf", line_file
);
7255 init_request("mso", macro_source
);
7256 init_request("nop", nop_request
);
7257 init_request("nroff", nroff_request
);
7258 init_request("nx", next_file
);
7259 init_request("open", open_request
);
7260 init_request("opena", opena_request
);
7261 init_request("output", output_request
);
7262 init_request("pc", set_page_character
);
7263 init_request("pi", pipe_output
);
7264 init_request("pm", print_macros
);
7265 init_request("psbb", ps_bbox_request
);
7266 #ifndef POPEN_MISSING
7267 init_request("pso", pipe_source
);
7268 #endif /* not POPEN_MISSING */
7269 init_request("rchar", remove_character
);
7270 init_request("rd", read_request
);
7271 init_request("return", return_macro_request
);
7272 init_request("rm", remove_macro
);
7273 init_request("rn", rename_macro
);
7274 init_request("schar", define_special_character
);
7275 init_request("shift", shift
);
7276 init_request("so", source
);
7277 init_request("spreadwarn", spreadwarn_request
);
7278 init_request("substring", substring_request
);
7279 init_request("sy", system_request
);
7280 init_request("tm", terminal
);
7281 init_request("tm1", terminal1
);
7282 init_request("tmc", terminal_continue
);
7283 init_request("tr", translate
);
7284 init_request("trf", transparent_file
);
7285 init_request("trin", translate_input
);
7286 init_request("trnt", translate_no_transparent
);
7287 init_request("troff", troff_request
);
7288 init_request("unformat", unformat_macro
);
7290 init_request("vj", vjustify
);
7292 init_request("warn", warn_request
);
7293 init_request("warnscale", warnscale_request
);
7294 init_request("while", while_request
);
7295 init_request("write", write_request
);
7296 init_request("writec", write_request_continue
);
7297 init_request("writem", write_macro_request
);
7298 number_reg_dictionary
.define(".$", new nargs_reg
);
7299 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7300 number_reg_dictionary
.define(".c", new lineno_reg
);
7301 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7302 number_reg_dictionary
.define(".F", new filename_reg
);
7303 number_reg_dictionary
.define(".g", new constant_reg("1"));
7304 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7305 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7306 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7307 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7308 extern const char *major_version
;
7309 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7310 extern const char *revision
;
7311 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7312 extern const char *minor_version
;
7313 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7314 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7315 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7316 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7317 number_reg_dictionary
.define("opmaxx",
7318 new variable_reg(&output_reg_maxx_contents
));
7319 number_reg_dictionary
.define("opmaxy",
7320 new variable_reg(&output_reg_maxy_contents
));
7321 number_reg_dictionary
.define("opminx",
7322 new variable_reg(&output_reg_minx_contents
));
7323 number_reg_dictionary
.define("opminy",
7324 new variable_reg(&output_reg_miny_contents
));
7325 number_reg_dictionary
.define("slimit",
7326 new variable_reg(&input_stack::limit
));
7327 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7328 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7329 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7332 object_dictionary
request_dictionary(501);
7334 void init_request(const char *s
, REQUEST_FUNCP f
)
7336 request_dictionary
.define(s
, new request(f
));
7339 static request_or_macro
*lookup_request(symbol nm
)
7341 assert(!nm
.is_null());
7342 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7344 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7346 request_dictionary
.define(nm
, p
);
7351 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7353 // Don't interpret character definitions in compatible mode.
7354 int old_compatible_flag
= compatible_flag
;
7355 compatible_flag
= 0;
7356 int old_escape_char
= escape_char
;
7358 macro
*mac
= ci
->set_macro(0);
7360 environment
*oldenv
= curenv
;
7361 environment
env(envp
);
7363 curenv
->set_composite();
7364 token old_tok
= tok
;
7365 input_stack::add_boundary();
7366 string_iterator
*si
=
7367 new string_iterator(*mac
, "composite character", ci
->nm
);
7368 input_stack::push(si
);
7369 // we don't use process_input_stack, because we don't want to recognise
7375 if (tok
.newline()) {
7376 error("composite character mustn't contain newline");
7384 node
*n
= curenv
->extract_output_line();
7385 input_stack::remove_boundary();
7389 compatible_flag
= old_compatible_flag
;
7390 escape_char
= old_escape_char
;
7395 static node
*read_draw_node()
7399 if (!start
.delimiter(1)){
7402 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7407 error("missing argument");
7409 unsigned char type
= tok
.ch();
7411 read_color_draw_node(start
);
7416 hvpair
*point
= new hvpair
[maxpoints
];
7421 for (i
= 0; tok
!= start
; i
++) {
7422 if (i
== maxpoints
) {
7423 hvpair
*oldpoint
= point
;
7424 point
= new hvpair
[maxpoints
*2];
7425 for (int j
= 0; j
< maxpoints
; j
++)
7426 point
[j
] = oldpoint
[j
];
7430 if (!get_hunits(&point
[i
].h
,
7431 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7442 if (!get_vunits(&point
[i
].v
, 'v')) {
7448 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7453 if (npoints
!= 1 || no_last_v
) {
7454 error("two arguments needed for line");
7459 if (npoints
!= 1 || !no_last_v
) {
7460 error("one argument needed for circle");
7466 if (npoints
!= 1 || no_last_v
) {
7467 error("two arguments needed for ellipse");
7472 if (npoints
!= 2 || no_last_v
) {
7473 error("four arguments needed for arc");
7479 error("even number of arguments needed for spline");
7482 if (npoints
!= 1 || !no_last_v
) {
7483 error("one argument needed for gray shade");
7488 // silently pass it through
7491 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7492 curenv
->get_font_size(),
7493 curenv
->get_glyph_color(),
7494 curenv
->get_fill_color());
7506 static void read_color_draw_node(token
&start
)
7510 error("missing color scheme");
7513 unsigned char scheme
= tok
.ch();
7516 char end
= start
.ch();
7519 col
= read_cmy(end
);
7522 col
= &default_color
;
7525 col
= read_gray(end
);
7528 col
= read_cmyk(end
);
7531 col
= read_rgb(end
);
7535 curenv
->set_fill_color(col
);
7536 while (tok
!= start
) {
7537 if (tok
.newline() || tok
.eof()) {
7538 warning(WARN_DELIM
, "missing closing delimiter");
7539 input_stack::push(make_temp_iterator("\n"));
7550 } warning_table
[] = {
7551 { "char", WARN_CHAR
},
7552 { "range", WARN_RANGE
},
7553 { "break", WARN_BREAK
},
7554 { "delim", WARN_DELIM
},
7556 { "scale", WARN_SCALE
},
7557 { "number", WARN_NUMBER
},
7558 { "syntax", WARN_SYNTAX
},
7559 { "tab", WARN_TAB
},
7560 { "right-brace", WARN_RIGHT_BRACE
},
7561 { "missing", WARN_MISSING
},
7562 { "input", WARN_INPUT
},
7563 { "escape", WARN_ESCAPE
},
7564 { "space", WARN_SPACE
},
7565 { "font", WARN_FONT
},
7567 { "mac", WARN_MAC
},
7568 { "reg", WARN_REG
},
7570 { "color", WARN_COLOR
},
7571 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7572 { "w", WARN_TOTAL
},
7573 { "default", DEFAULT_WARNING_MASK
},
7576 static int lookup_warning(const char *name
)
7578 for (unsigned int i
= 0;
7579 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7581 if (strcmp(name
, warning_table
[i
].name
) == 0)
7582 return warning_table
[i
].mask
;
7586 static void enable_warning(const char *name
)
7588 int mask
= lookup_warning(name
);
7590 warning_mask
|= mask
;
7592 error("unknown warning `%1'", name
);
7595 static void disable_warning(const char *name
)
7597 int mask
= lookup_warning(name
);
7599 warning_mask
&= ~mask
;
7601 error("unknown warning `%1'", name
);
7604 static void copy_mode_error(const char *format
,
7610 static const char prefix
[] = "(in ignored input) ";
7611 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7614 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7618 error(format
, arg1
, arg2
, arg3
);
7621 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7623 static void do_error(error_type type
,
7629 const char *filename
;
7631 if (inhibit_errors
&& type
< FATAL
)
7634 input_stack::backtrace();
7635 if (!get_file_line(&filename
, &lineno
))
7638 errprint("%1:%2: ", filename
, lineno
);
7639 else if (program_name
)
7640 fprintf(stderr
, "%s: ", program_name
);
7643 fputs("fatal error: ", stderr
);
7648 fputs("warning: ", stderr
);
7650 case OUTPUT_WARNING
:
7651 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7652 fprintf(stderr
, "warning [p %d, %.1f%c",
7653 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7654 if (topdiv
!= curdiv
) {
7655 double fromtop1
= curdiv
->get_vertical_position().to_units()
7657 fprintf(stderr
, ", div `%s', %.1f%c",
7658 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7660 fprintf(stderr
, "]: ");
7663 errprint(format
, arg1
, arg2
, arg3
);
7664 fputc('\n', stderr
);
7667 cleanup_and_exit(1);
7670 int warning(warning_type t
,
7676 if ((t
& warning_mask
) != 0) {
7677 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
7684 int output_warning(warning_type t
,
7690 if ((t
& warning_mask
) != 0) {
7691 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
7698 void error(const char *format
,
7703 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
7706 void fatal(const char *format
,
7711 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
7714 void fatal_with_file_and_line(const char *filename
, int lineno
,
7720 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
7721 errprint(format
, arg1
, arg2
, arg3
);
7722 fputc('\n', stderr
);
7724 cleanup_and_exit(1);
7727 void error_with_file_and_line(const char *filename
, int lineno
,
7733 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
7734 errprint(format
, arg1
, arg2
, arg3
);
7735 fputc('\n', stderr
);
7739 dictionary
charinfo_dictionary(501);
7741 charinfo
*get_charinfo(symbol nm
)
7743 void *p
= charinfo_dictionary
.lookup(nm
);
7745 return (charinfo
*)p
;
7746 charinfo
*cp
= new charinfo(nm
);
7747 (void)charinfo_dictionary
.lookup(nm
, cp
);
7751 int charinfo::next_index
= 0;
7753 charinfo::charinfo(symbol s
)
7754 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
7755 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7756 not_found(0), transparent_translate(1), translate_input(0),
7757 mode(CHAR_NORMAL
), nm(s
)
7759 index
= next_index
++;
7762 void charinfo::set_hyphenation_code(unsigned char c
)
7764 hyphenation_code
= c
;
7767 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
7771 if (hyphenation_code
!= 0)
7772 ci
->set_hyphenation_code(hyphenation_code
);
7773 if (asciify_code
!= 0)
7774 ci
->set_asciify_code(asciify_code
);
7775 else if (ascii_code
!= 0)
7776 ci
->set_asciify_code(ascii_code
);
7777 ci
->set_translation_input();
7779 special_translation
= TRANSLATE_NONE
;
7780 transparent_translate
= tt
;
7783 void charinfo::set_special_translation(int c
, int tt
)
7785 special_translation
= c
;
7787 transparent_translate
= tt
;
7790 void charinfo::set_ascii_code(unsigned char c
)
7795 void charinfo::set_asciify_code(unsigned char c
)
7800 macro
*charinfo::set_macro(macro
*m
)
7807 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
7815 void charinfo::set_number(int n
)
7821 int charinfo::get_number()
7823 assert(flags
& NUMBERED
);
7827 symbol
UNNAMED_SYMBOL("---");
7829 // For numbered characters not between 0 and 255, we make a symbol out
7830 // of the number and store them in this dictionary.
7832 dictionary
numbered_charinfo_dictionary(11);
7834 charinfo
*get_charinfo_by_number(int n
)
7836 static charinfo
*number_table
[256];
7838 if (n
>= 0 && n
< 256) {
7839 charinfo
*ci
= number_table
[n
];
7841 ci
= new charinfo(UNNAMED_SYMBOL
);
7843 number_table
[n
] = ci
;
7848 symbol
ns(i_to_a(n
));
7849 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
7851 ci
= new charinfo(UNNAMED_SYMBOL
);
7853 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
7859 int font::name_to_index(const char *nm
)
7863 ci
= charset_table
[nm
[0] & 0xff];
7864 else if (nm
[0] == '\\' && nm
[2] == 0)
7865 ci
= get_charinfo(symbol(nm
+ 1));
7867 ci
= get_charinfo(symbol(nm
));
7871 return ci
->get_index();
7874 int font::number_to_index(int n
)
7876 return get_charinfo_by_number(n
)->get_index();