2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
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. */
23 #include "dictionary.h"
32 #include "stringclass.h"
34 #include "macropath.h"
39 // Needed for getpid() and isatty()
44 #ifdef NEED_DECLARATION_PUTENV
46 int putenv(const char *);
48 #endif /* NEED_DECLARATION_PUTENV */
50 #define MACRO_PREFIX "tmac."
51 #define MACRO_POSTFIX ".tmac"
52 #define INITIAL_STARTUP_FILE "troffrc"
53 #define FINAL_STARTUP_FILE "troffrc-end"
54 #define DEFAULT_INPUT_STACK_LIMIT 1000
56 #ifndef DEFAULT_WARNING_MASK
57 // warnings that are enabled by default
58 #define DEFAULT_WARNING_MASK \
59 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
62 // initial size of buffer for reading names; expanded as necessary
65 extern "C" const char *Version_string
;
68 void init_column_requests();
71 static node
*read_draw_node();
72 static void read_color_draw_node(token
&);
73 void handle_first_page_transition();
74 static void push_token(const token
&);
79 void transparent_file();
80 void process_input_stack();
82 const char *program_name
= 0;
85 int color_flag
= 1; // colors are on by default
86 static int backtrace_flag
= 0;
88 char *pipe_command
= 0;
90 charinfo
*charset_table
[256];
91 unsigned char hpf_code_table
[256];
93 static int warning_mask
= DEFAULT_WARNING_MASK
;
94 static int inhibit_errors
= 0;
95 static int ignoring
= 0;
97 static void enable_warning(const char *);
98 static void disable_warning(const char *);
100 static int escape_char
= '\\';
101 static symbol end_macro_name
;
102 static symbol blank_line_macro_name
;
103 static int compatible_flag
= 0;
104 int ascii_output_flag
= 0;
105 int suppress_output_flag
= 0;
107 int begin_level
= 0; // number of nested .begin requests
109 int have_input
= 0; // whether \f, \F, \D'F...', \H, \m, \M,
110 // \R, \s, or \S has been processed in
112 int old_have_input
= 0; // value of have_input right before \n
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 // Defaults to the current directory.
126 search_path
include_search_path(0, 0, 0, 1);
128 static int get_copy(node
**, int = 0);
129 static void copy_mode_error(const char *,
130 const errarg
& = empty_errarg
,
131 const errarg
& = empty_errarg
,
132 const errarg
& = empty_errarg
);
134 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
135 static symbol
read_escape_name(read_mode mode
= NO_ARGS
);
136 static symbol
read_long_escape_name(read_mode mode
= NO_ARGS
);
137 static void interpolate_string(symbol
);
138 static void interpolate_string_with_args(symbol
);
139 static void interpolate_macro(symbol
);
140 static void interpolate_number_format(symbol
);
141 static void interpolate_environment_variable(symbol
);
143 static symbol
composite_glyph_name(symbol
);
144 static void interpolate_arg(symbol
);
145 static request_or_macro
*lookup_request(symbol
);
146 static int get_delim_number(units
*, int);
147 static int get_delim_number(units
*, int, units
);
148 static symbol
do_get_long_name(int, char);
149 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
150 static int read_size(int *);
151 static symbol
get_delim_name();
152 static void init_registers();
153 static void trapping_blank_line();
155 struct input_iterator
;
156 input_iterator
*make_temp_iterator(const char *);
157 const char *input_char_description(int);
160 void set_escape_char()
164 error("bad escape character");
168 escape_char
= tok
.ch();
181 static int saved_escape_char
= '\\';
183 void save_escape_char()
185 saved_escape_char
= escape_char
;
189 void restore_escape_char()
191 escape_char
= saved_escape_char
;
195 class input_iterator
{
198 virtual ~input_iterator() {}
200 friend class input_stack
;
202 const unsigned char *ptr
;
203 const unsigned char *eptr
;
204 input_iterator
*next
;
206 virtual int fill(node
**);
208 virtual int has_args() { return 0; }
209 virtual int nargs() { return 0; }
210 virtual input_iterator
*get_arg(int) { return 0; }
211 virtual int get_location(int, const char **, int *) { return 0; }
212 virtual void backtrace() {}
213 virtual int set_location(const char *, int) { return 0; }
214 virtual int next_file(FILE *, const char *) { return 0; }
215 virtual void shift(int) {}
216 virtual int is_boundary() {return 0; }
217 virtual int internal_level() { return 0; }
218 virtual int is_file() { return 0; }
219 virtual int is_macro() { return 0; }
220 virtual void save_compatible_flag(int) {}
221 virtual int get_compatible_flag() { return 0; }
224 input_iterator::input_iterator()
229 int input_iterator::fill(node
**)
234 int input_iterator::peek()
239 inline int input_iterator::get(node
**p
)
241 return ptr
< eptr
? *ptr
++ : fill(p
);
244 class input_boundary
: public input_iterator
{
246 int is_boundary() { return 1; }
249 class input_return_boundary
: public input_iterator
{
251 int is_boundary() { return 2; }
254 class file_iterator
: public input_iterator
{
257 const char *filename
;
261 enum { BUF_SIZE
= 512 };
262 unsigned char buf
[BUF_SIZE
];
265 file_iterator(FILE *, const char *, int = 0);
269 int get_location(int, const char **, int *);
271 int set_location(const char *, int);
272 int next_file(FILE *, const char *);
276 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
277 : fp(f
), lineno(1), filename(fn
), popened(po
),
278 newline_flag(0), seen_escape(0)
280 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
283 the_output
->put_filename(fn
);
287 file_iterator::~file_iterator()
292 void file_iterator::close()
296 #ifndef POPEN_MISSING
299 #endif /* not POPEN_MISSING */
304 int file_iterator::is_file()
309 int file_iterator::next_file(FILE *f
, const char *s
)
323 int file_iterator::fill(node
**)
328 unsigned char *p
= buf
;
330 unsigned char *e
= p
+ BUF_SIZE
;
335 if (invalid_input_char(c
))
336 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
344 seen_escape
= (c
== '\\');
357 int file_iterator::peek()
360 while (invalid_input_char(c
)) {
361 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
369 int file_iterator::get_location(int /*allow_macro*/,
370 const char **filenamep
, int *linenop
)
373 if (filename
!= 0 && strcmp(filename
, "-") == 0)
374 *filenamep
= "<standard input>";
376 *filenamep
= filename
;
380 void file_iterator::backtrace()
382 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
383 popened
? "process" : "file");
386 int file_iterator::set_location(const char *f
, int ln
)
392 the_output
->put_filename(f
);
398 input_iterator nil_iterator
;
402 static int get(node
**);
404 static void push(input_iterator
*);
405 static input_iterator
*get_arg(int);
407 static int get_location(int, const char **, int *);
408 static int set_location(const char *, int);
409 static void backtrace();
410 static void backtrace_all();
411 static void next_file(FILE *, const char *);
412 static void end_file();
413 static void shift(int n
);
414 static void add_boundary();
415 static void add_return_boundary();
416 static int is_return_boundary();
417 static void remove_boundary();
418 static int get_level();
420 static void pop_macro();
421 static void save_compatible_flag(int);
422 static int get_compatible_flag();
426 static input_iterator
*top
;
429 static int finish_get(node
**);
430 static int finish_peek();
433 input_iterator
*input_stack::top
= &nil_iterator
;
434 int input_stack::level
= 0;
435 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
437 inline int input_stack::get_level()
439 return level
+ top
->internal_level();
442 inline int input_stack::get(node
**np
)
444 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
446 old_have_input
= have_input
;
452 int input_stack::finish_get(node
**np
)
455 int c
= top
->fill(np
);
456 if (c
!= EOF
|| top
->is_boundary())
458 if (top
== &nil_iterator
)
460 input_iterator
*tem
= top
;
464 if (top
->ptr
< top
->eptr
)
471 inline int input_stack::peek()
473 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
476 int input_stack::finish_peek()
480 if (c
!= EOF
|| top
->is_boundary())
482 if (top
== &nil_iterator
)
484 input_iterator
*tem
= top
;
488 if (top
->ptr
< top
->eptr
)
495 void input_stack::add_boundary()
497 push(new input_boundary
);
500 void input_stack::add_return_boundary()
502 push(new input_return_boundary
);
505 int input_stack::is_return_boundary()
507 return top
->is_boundary() == 2;
510 void input_stack::remove_boundary()
512 assert(top
->is_boundary());
513 input_iterator
*temp
= top
->next
;
519 void input_stack::push(input_iterator
*in
)
523 if (++level
> limit
&& limit
> 0)
524 fatal("input stack limit exceeded (probable infinite loop)");
529 input_iterator
*input_stack::get_arg(int i
)
532 for (p
= top
; p
!= 0; p
= p
->next
)
534 return p
->get_arg(i
);
538 void input_stack::shift(int n
)
540 for (input_iterator
*p
= top
; p
; p
= p
->next
)
547 int input_stack::nargs()
549 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
555 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
557 for (input_iterator
*p
= top
; p
; p
= p
->next
)
558 if (p
->get_location(allow_macro
, filenamep
, linenop
))
563 void input_stack::backtrace()
567 // only backtrace down to (not including) the topmost file
568 for (input_iterator
*p
= top
;
569 p
&& !p
->get_location(0, &f
, &n
);
574 void input_stack::backtrace_all()
576 for (input_iterator
*p
= top
; p
; p
= p
->next
)
580 int input_stack::set_location(const char *filename
, int lineno
)
582 for (input_iterator
*p
= top
; p
; p
= p
->next
)
583 if (p
->set_location(filename
, lineno
))
588 void input_stack::next_file(FILE *fp
, const char *s
)
591 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
592 if ((*pp
)->next_file(fp
, s
))
594 if (++level
> limit
&& limit
> 0)
595 fatal("input stack limit exceeded");
596 *pp
= new file_iterator(fp
, s
);
597 (*pp
)->next
= &nil_iterator
;
600 void input_stack::end_file()
602 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
603 if ((*pp
)->is_file()) {
604 input_iterator
*tem
= *pp
;
612 void input_stack::clear()
615 while (top
!= &nil_iterator
) {
616 if (top
->is_boundary())
618 input_iterator
*tem
= top
;
623 // Keep while_request happy.
624 for (; nboundaries
> 0; --nboundaries
)
625 add_return_boundary();
628 void input_stack::pop_macro()
633 if (top
->next
== &nil_iterator
)
635 if (top
->is_boundary())
637 is_macro
= top
->is_macro();
638 input_iterator
*tem
= top
;
643 // Keep while_request happy.
644 for (; nboundaries
> 0; --nboundaries
)
645 add_return_boundary();
648 inline void input_stack::save_compatible_flag(int f
)
650 top
->save_compatible_flag(f
);
653 inline int input_stack::get_compatible_flag()
655 return top
->get_compatible_flag();
658 void backtrace_request()
660 input_stack::backtrace_all();
667 symbol nm
= get_long_name();
668 while (!tok
.newline() && !tok
.eof())
671 input_stack::end_file();
674 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
676 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
678 input_stack::next_file(fp
, nm
.contents());
686 if (!has_arg() || !get_integer(&n
))
688 input_stack::shift(n
);
692 static int get_char_for_escape_name(int allow_space
= 0)
697 copy_mode_error("end of input in escape name");
700 if (!invalid_input_char(c
))
705 input_stack::push(make_temp_iterator("\n"));
708 if (c
== ' ' && allow_space
)
714 copy_mode_error("%1 is not allowed in an escape name",
715 input_char_description(c
));
721 static symbol
read_two_char_escape_name()
724 buf
[0] = get_char_for_escape_name();
725 if (buf
[0] != '\0') {
726 buf
[1] = get_char_for_escape_name();
735 static symbol
read_long_escape_name(read_mode mode
)
737 int start_level
= input_stack::get_level();
738 char abuf
[ABUF_SIZE
];
740 int buf_size
= ABUF_SIZE
;
745 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
752 if (mode
== WITH_ARGS
&& c
== ' ')
754 if (i
+ 2 > buf_size
) {
756 buf
= new char[ABUF_SIZE
*2];
757 memcpy(buf
, abuf
, buf_size
);
758 buf_size
= ABUF_SIZE
*2;
762 buf
= new char[buf_size
*2];
763 memcpy(buf
, old_buf
, buf_size
);
768 if (c
== ']' && input_stack::get_level() == start_level
)
777 if (mode
!= ALLOW_EMPTY
)
778 copy_mode_error("empty escape name");
790 static symbol
read_escape_name(read_mode mode
)
792 int c
= get_char_for_escape_name();
796 return read_two_char_escape_name();
797 if (c
== '[' && !compatible_flag
)
798 return read_long_escape_name(mode
);
805 static symbol
read_increment_and_escape_name(int *incp
)
807 int c
= get_char_for_escape_name();
814 return read_two_char_escape_name();
817 return read_escape_name();
820 return read_escape_name();
822 if (!compatible_flag
) {
824 return read_long_escape_name();
835 static int get_copy(node
**nd
, int defining
)
838 int c
= input_stack::get(nd
);
839 if (c
== ESCAPE_NEWLINE
) {
843 c
= input_stack::get(nd
);
844 } while (c
== ESCAPE_NEWLINE
);
846 if (c
!= escape_char
|| escape_char
<= 0)
848 c
= input_stack::peek();
853 (void)input_stack::get(0);
854 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
857 case '#': // Like \" but newline is ignored.
858 (void)input_stack::get(0);
859 while ((c
= input_stack::get(0)) != '\n')
865 (void)input_stack::get(0);
866 symbol s
= read_escape_name();
867 if (!(s
.is_null() || s
.is_empty()))
873 (void)input_stack::get(0);
874 symbol s
= read_escape_name(WITH_ARGS
);
875 if (!(s
.is_null() || s
.is_empty())) {
876 if (have_string_arg
) {
878 interpolate_string_with_args(s
);
881 interpolate_string(s
);
886 (void)input_stack::get(0);
889 (void)input_stack::get(0);
892 (void)input_stack::get(0);
896 (void)input_stack::get(0);
898 symbol s
= read_increment_and_escape_name(&inc
);
899 if (!(s
.is_null() || s
.is_empty()))
900 interpolate_number_reg(s
, inc
);
905 (void)input_stack::get(0);
906 symbol s
= read_escape_name();
907 if (!(s
.is_null() || s
.is_empty()))
908 interpolate_number_format(s
);
912 (void)input_stack::get(0);
916 (void)input_stack::get(0);
917 symbol s
= read_escape_name();
918 if (!(s
.is_null() || s
.is_empty()))
919 interpolate_environment_variable(s
);
923 (void)input_stack::get(0);
925 return ESCAPE_NEWLINE
;
928 (void)input_stack::get(0);
931 (void)input_stack::get(0);
934 (void)input_stack::get(0);
937 (void)input_stack::get(0);
940 (void)input_stack::get(0);
941 return ESCAPE_CIRCUMFLEX
;
943 (void)input_stack::get(0);
944 return ESCAPE_LEFT_BRACE
;
946 (void)input_stack::get(0);
947 return ESCAPE_RIGHT_BRACE
;
949 (void)input_stack::get(0);
950 return ESCAPE_LEFT_QUOTE
;
952 (void)input_stack::get(0);
953 return ESCAPE_RIGHT_QUOTE
;
955 (void)input_stack::get(0);
956 return ESCAPE_HYPHEN
;
958 (void)input_stack::get(0);
959 return ESCAPE_UNDERSCORE
;
961 (void)input_stack::get(0);
964 (void)input_stack::get(0);
967 (void)input_stack::get(0);
968 return ESCAPE_QUESTION
;
970 (void)input_stack::get(0);
971 return ESCAPE_AMPERSAND
;
973 (void)input_stack::get(0);
974 return ESCAPE_RIGHT_PARENTHESIS
;
976 (void)input_stack::get(0);
979 (void)input_stack::get(0);
980 return ESCAPE_PERCENT
;
982 if (c
== escape_char
) {
983 (void)input_stack::get(0);
992 class non_interpreted_char_node
: public node
{
995 non_interpreted_char_node(unsigned char);
997 int interpret(macro
*);
1003 int non_interpreted_char_node::same(node
*nd
)
1005 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1008 const char *non_interpreted_char_node::type()
1010 return "non_interpreted_char_node";
1013 int non_interpreted_char_node::force_tprint()
1018 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1023 node
*non_interpreted_char_node::copy()
1025 return new non_interpreted_char_node(c
);
1028 int non_interpreted_char_node::interpret(macro
*mac
)
1034 static void do_width();
1035 static node
*do_non_interpreted();
1036 static node
*do_special();
1037 static node
*do_suppress(symbol nm
);
1038 static void do_register();
1040 dictionary
color_dictionary(501);
1042 static color
*lookup_color(symbol nm
)
1044 assert(!nm
.is_null());
1045 if (nm
== default_symbol
)
1046 return &default_color
;
1047 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1049 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1053 void do_glyph_color(symbol nm
)
1058 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1060 color
*tem
= lookup_color(nm
);
1062 curenv
->set_glyph_color(tem
);
1064 (void)color_dictionary
.lookup(nm
, new color(nm
));
1068 void do_fill_color(symbol nm
)
1073 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1075 color
*tem
= lookup_color(nm
);
1077 curenv
->set_fill_color(tem
);
1079 (void)color_dictionary
.lookup(nm
, new color(nm
));
1083 static unsigned int get_color_element(const char *scheme
, const char *col
)
1086 if (!get_number(&val
, 'f')) {
1087 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1092 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1095 if (val
> color::MAX_COLOR_VAL
+1) {
1096 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1097 // we change 0x10000 to 0xffff
1098 return color::MAX_COLOR_VAL
;
1100 return (unsigned int)val
;
1103 static color
*read_rgb(char end
= 0)
1105 symbol component
= do_get_long_name(0, end
);
1106 if (component
.is_null()) {
1107 warning(WARN_COLOR
, "missing rgb color values");
1110 const char *s
= component
.contents();
1111 color
*col
= new color
;
1113 if (!col
->read_rgb(s
)) {
1114 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1121 input_stack::push(make_temp_iterator(" "));
1122 input_stack::push(make_temp_iterator(s
));
1124 unsigned int r
= get_color_element("rgb color", "red component");
1125 unsigned int g
= get_color_element("rgb color", "green component");
1126 unsigned int b
= get_color_element("rgb color", "blue component");
1127 col
->set_rgb(r
, g
, b
);
1132 static color
*read_cmy(char end
= 0)
1134 symbol component
= do_get_long_name(0, end
);
1135 if (component
.is_null()) {
1136 warning(WARN_COLOR
, "missing cmy color values");
1139 const char *s
= component
.contents();
1140 color
*col
= new color
;
1142 if (!col
->read_cmy(s
)) {
1143 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1150 input_stack::push(make_temp_iterator(" "));
1151 input_stack::push(make_temp_iterator(s
));
1153 unsigned int c
= get_color_element("cmy color", "cyan component");
1154 unsigned int m
= get_color_element("cmy color", "magenta component");
1155 unsigned int y
= get_color_element("cmy color", "yellow component");
1156 col
->set_cmy(c
, m
, y
);
1161 static color
*read_cmyk(char end
= 0)
1163 symbol component
= do_get_long_name(0, end
);
1164 if (component
.is_null()) {
1165 warning(WARN_COLOR
, "missing cmyk color values");
1168 const char *s
= component
.contents();
1169 color
*col
= new color
;
1171 if (!col
->read_cmyk(s
)) {
1172 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1179 input_stack::push(make_temp_iterator(" "));
1180 input_stack::push(make_temp_iterator(s
));
1182 unsigned int c
= get_color_element("cmyk color", "cyan component");
1183 unsigned int m
= get_color_element("cmyk color", "magenta component");
1184 unsigned int y
= get_color_element("cmyk color", "yellow component");
1185 unsigned int k
= get_color_element("cmyk color", "black component");
1186 col
->set_cmyk(c
, m
, y
, k
);
1191 static color
*read_gray(char end
= 0)
1193 symbol component
= do_get_long_name(0, end
);
1194 if (component
.is_null()) {
1195 warning(WARN_COLOR
, "missing gray values");
1198 const char *s
= component
.contents();
1199 color
*col
= new color
;
1201 if (!col
->read_gray(s
)) {
1202 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1209 input_stack::push(make_temp_iterator("\n"));
1210 input_stack::push(make_temp_iterator(s
));
1212 unsigned int g
= get_color_element("gray", "gray value");
1218 static void activate_color()
1221 if (has_arg() && get_integer(&n
))
1222 color_flag
= n
!= 0;
1228 static void define_color()
1230 symbol color_name
= get_long_name(1);
1231 if (color_name
.is_null()) {
1235 if (color_name
== default_symbol
) {
1236 warning(WARN_COLOR
, "default color can't be redefined");
1240 symbol style
= get_long_name(1);
1241 if (style
.is_null()) {
1246 if (strcmp(style
.contents(), "rgb") == 0)
1248 else if (strcmp(style
.contents(), "cmyk") == 0)
1250 else if (strcmp(style
.contents(), "gray") == 0)
1252 else if (strcmp(style
.contents(), "grey") == 0)
1254 else if (strcmp(style
.contents(), "cmy") == 0)
1258 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1264 col
->nm
= color_name
;
1265 (void)color_dictionary
.lookup(color_name
, col
);
1270 static node
*do_overstrike()
1273 overstrike_node
*on
= new overstrike_node
;
1274 int start_level
= input_stack::get_level();
1278 if (tok
.newline() || tok
.eof()) {
1279 warning(WARN_DELIM
, "missing closing delimiter");
1280 input_stack::push(make_temp_iterator("\n"));
1284 && (compatible_flag
|| input_stack::get_level() == start_level
))
1286 charinfo
*ci
= tok
.get_char(1);
1288 node
*n
= curenv
->make_char_node(ci
);
1296 static node
*do_bracket()
1299 bracket_node
*bn
= new bracket_node
;
1301 int start_level
= input_stack::get_level();
1305 warning(WARN_DELIM
, "missing closing delimiter");
1308 if (tok
.newline()) {
1309 warning(WARN_DELIM
, "missing closing delimiter");
1310 input_stack::push(make_temp_iterator("\n"));
1314 && (compatible_flag
|| input_stack::get_level() == start_level
))
1316 charinfo
*ci
= tok
.get_char(1);
1318 node
*n
= curenv
->make_char_node(ci
);
1326 static int do_name_test()
1330 int start_level
= input_stack::get_level();
1335 if (tok
.newline() || tok
.eof()) {
1336 warning(WARN_DELIM
, "missing closing delimiter");
1337 input_stack::push(make_temp_iterator("\n"));
1341 && (compatible_flag
|| input_stack::get_level() == start_level
))
1347 return some_char
&& !bad_char
;
1350 static int do_expr_test()
1354 int start_level
= input_stack::get_level();
1355 if (!start
.delimiter(1))
1358 // disable all warning and error messages temporarily
1359 int saved_warning_mask
= warning_mask
;
1360 int saved_inhibit_errors
= inhibit_errors
;
1364 int result
= get_number_rigidly(&dummy
, 'u');
1365 warning_mask
= saved_warning_mask
;
1366 inhibit_errors
= saved_inhibit_errors
;
1367 if (tok
== start
&& input_stack::get_level() == start_level
)
1369 // ignore everything up to the delimiter in case we aren't right there
1372 if (tok
.newline() || tok
.eof()) {
1373 warning(WARN_DELIM
, "missing closing delimiter");
1374 input_stack::push(make_temp_iterator("\n"));
1377 if (tok
== start
&& input_stack::get_level() == start_level
)
1384 static node
*do_zero_width()
1388 int start_level
= input_stack::get_level();
1389 environment
env(curenv
);
1390 environment
*oldenv
= curenv
;
1394 if (tok
.newline() || tok
.eof()) {
1395 error("missing closing delimiter");
1399 && (compatible_flag
|| input_stack::get_level() == start_level
))
1404 node
*rev
= env
.extract_output_line();
1412 return new zero_width_node(n
);
1417 // It's undesirable for \Z to change environments, because then
1418 // \n(.w won't work as expected.
1420 static node
*do_zero_width()
1422 node
*rev
= new dummy_node
;
1425 int start_level
= input_stack::get_level();
1428 if (tok
.newline() || tok
.eof()) {
1429 warning(WARN_DELIM
, "missing closing delimiter");
1430 input_stack::push(make_temp_iterator("\n"));
1434 && (compatible_flag
|| input_stack::get_level() == start_level
))
1436 if (!tok
.add_to_node_list(&rev
))
1437 error("invalid token in argument to \\Z");
1446 return new zero_width_node(n
);
1451 token_node
*node::get_token_node()
1456 class token_node
: public node
{
1459 token_node(const token
&t
);
1461 token_node
*get_token_node();
1467 token_node::token_node(const token
&t
) : tk(t
)
1471 node
*token_node::copy()
1473 return new token_node(tk
);
1476 token_node
*token_node::get_token_node()
1481 int token_node::same(node
*nd
)
1483 return tk
== ((token_node
*)nd
)->tk
;
1486 const char *token_node::type()
1488 return "token_node";
1491 int token_node::force_tprint()
1496 token::token() : nd(0), type(TOKEN_EMPTY
)
1505 token::token(const token
&t
)
1506 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1508 // Use two statements to work around bug in SGI C++.
1510 nd
= tem
? tem
->copy() : 0;
1513 void token::operator=(const token
&t
)
1517 // Use two statements to work around bug in SGI C++.
1519 nd
= tem
? tem
->copy() : 0;
1536 return !tok
.newline();
1539 void token::make_space()
1544 void token::make_newline()
1546 type
= TOKEN_NEWLINE
;
1558 int cc
= input_stack::get(&n
);
1559 if (cc
!= escape_char
|| escape_char
== 0) {
1562 case COMPATIBLE_SAVE
:
1563 input_stack::save_compatible_flag(compatible_flag
);
1564 compatible_flag
= 0;
1566 case COMPATIBLE_RESTORE
:
1567 compatible_flag
= input_stack::get_compatible_flag();
1572 case TRANSPARENT_FILE_REQUEST
:
1574 case COPY_FILE_REQUEST
:
1576 case VJUSTIFY_REQUEST
:
1578 type
= TOKEN_REQUEST
;
1582 type
= TOKEN_BEGIN_TRAP
;
1585 type
= TOKEN_END_TRAP
;
1587 case LAST_PAGE_EJECTOR
:
1588 seen_last_page_ejector
= 1;
1591 type
= TOKEN_PAGE_EJECTOR
;
1593 case ESCAPE_PERCENT
:
1595 type
= TOKEN_HYPHEN_INDICATOR
;
1599 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1603 type
= TOKEN_STRETCHABLE_SPACE
;
1607 type
= TOKEN_ZERO_WIDTH_BREAK
;
1611 type
= TOKEN_ESCAPE
;
1614 goto handle_escape_char
;
1618 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1619 curenv
->get_fill_color());
1621 case ESCAPE_CIRCUMFLEX
:
1624 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1625 curenv
->get_fill_color());
1627 case ESCAPE_NEWLINE
:
1630 case ESCAPE_LEFT_BRACE
:
1632 type
= TOKEN_LEFT_BRACE
;
1634 case ESCAPE_RIGHT_BRACE
:
1636 type
= TOKEN_RIGHT_BRACE
;
1638 case ESCAPE_LEFT_QUOTE
:
1640 type
= TOKEN_SPECIAL
;
1643 case ESCAPE_RIGHT_QUOTE
:
1645 type
= TOKEN_SPECIAL
;
1650 type
= TOKEN_SPECIAL
;
1653 case ESCAPE_UNDERSCORE
:
1655 type
= TOKEN_SPECIAL
;
1660 type
= TOKEN_INTERRUPT
;
1664 type
= TOKEN_TRANSPARENT
;
1666 case ESCAPE_QUESTION
:
1668 nd
= do_non_interpreted();
1674 case ESCAPE_AMPERSAND
:
1678 case ESCAPE_RIGHT_PARENTHESIS
:
1679 ESCAPE_RIGHT_PARENTHESIS
:
1680 type
= TOKEN_TRANSPARENT_DUMMY
;
1683 type
= TOKEN_BACKSPACE
;
1692 type
= TOKEN_NEWLINE
;
1695 type
= TOKEN_LEADER
;
1700 token_node
*tn
= n
->get_token_node();
1719 cc
= input_stack::get(&n
);
1722 nm
= read_two_char_escape_name();
1723 type
= TOKEN_SPECIAL
;
1727 error("end of input after escape character");
1730 goto ESCAPE_LEFT_QUOTE
;
1732 goto ESCAPE_RIGHT_QUOTE
;
1736 goto ESCAPE_UNDERSCORE
;
1738 goto ESCAPE_PERCENT
;
1742 nd
= new hmotion_node(curenv
->get_digit_width(),
1743 curenv
->get_fill_color());
1749 goto ESCAPE_CIRCUMFLEX
;
1751 type
= TOKEN_ITALIC_CORRECTION
;
1755 nd
= new left_italic_corrected_node
;
1758 goto ESCAPE_AMPERSAND
;
1760 goto ESCAPE_RIGHT_PARENTHESIS
;
1764 goto ESCAPE_QUESTION
;
1770 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1773 type
= TOKEN_NEWLINE
;
1777 case '#': // Like \" but newline is ignored.
1778 while ((cc
= input_stack::get(0)) != '\n')
1786 symbol s
= read_escape_name();
1787 if (!(s
.is_null() || s
.is_empty()))
1793 symbol s
= read_escape_name(WITH_ARGS
);
1794 if (!(s
.is_null() || s
.is_empty())) {
1795 if (have_string_arg
) {
1796 have_string_arg
= 0;
1797 interpolate_string_with_args(s
);
1800 interpolate_string(s
);
1805 nd
= new non_interpreted_char_node('\001');
1809 c
= '0' + do_name_test();
1817 c
= '0' + do_expr_test();
1823 nm
= get_delim_name();
1826 type
= TOKEN_SPECIAL
;
1830 nd
= new vmotion_node(curenv
->get_size() / 2,
1831 curenv
->get_fill_color());
1834 nd
= read_draw_node();
1842 goto handle_escape_char
;
1845 symbol s
= read_escape_name(ALLOW_EMPTY
);
1849 for (p
= s
.contents(); *p
!= '\0'; p
++)
1852 if (*p
|| s
.is_empty())
1853 curenv
->set_font(s
);
1855 curenv
->set_font(atoi(s
.contents()));
1856 if (!compatible_flag
)
1862 symbol s
= read_escape_name(ALLOW_EMPTY
);
1865 curenv
->set_family(s
);
1871 symbol s
= read_escape_name();
1872 if (!(s
.is_null() || s
.is_empty()))
1873 interpolate_number_format(s
);
1877 if (!get_delim_number(&x
, 'm'))
1880 nd
= new hmotion_node(x
, curenv
->get_fill_color());
1883 // don't take height increments relative to previous height if
1884 // in compatibility mode
1885 if (!compatible_flag
&& curenv
->get_char_height())
1887 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
1888 curenv
->set_char_height(x
);
1892 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1893 curenv
->set_char_height(x
);
1895 if (!compatible_flag
)
1899 nm
= read_escape_name();
1900 if (nm
.is_null() || nm
.is_empty())
1902 type
= TOKEN_MARK_INPUT
;
1908 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1911 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1913 node
*n
= curenv
->make_char_node(s
);
1915 nd
= new hline_node(x
, n
);
1917 nd
= new vline_node(x
, n
);
1921 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
1922 if (!compatible_flag
)
1926 do_fill_color(read_escape_name(ALLOW_EMPTY
));
1927 if (!compatible_flag
)
1933 symbol s
= read_increment_and_escape_name(&inc
);
1934 if (!(s
.is_null() || s
.is_empty()))
1935 interpolate_number_reg(s
, inc
);
1939 if (!get_delim_number(&val
, 0))
1941 type
= TOKEN_NUMBERED_CHAR
;
1944 nd
= do_overstrike();
1948 nd
= do_suppress(read_escape_name());
1954 type
= TOKEN_SPREAD
;
1958 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
1962 if (!compatible_flag
)
1967 curenv
->set_size(x
);
1968 if (!compatible_flag
)
1972 if (get_delim_number(&x
, 0))
1973 curenv
->set_char_slant(x
);
1974 if (!compatible_flag
)
1979 nd
= new non_interpreted_char_node('\t');
1983 nd
= new vmotion_node(-curenv
->get_size() / 2,
1984 curenv
->get_fill_color());
1987 if (!get_delim_number(&x
, 'v'))
1990 nd
= new vmotion_node(x
, curenv
->get_fill_color());
1994 symbol s
= read_escape_name();
1995 if (!(s
.is_null() || s
.is_empty()))
1996 interpolate_environment_variable(s
);
2003 if (!get_delim_number(&x
, 'v'))
2006 nd
= new extra_size_node(x
);
2016 symbol s
= read_escape_name();
2017 if (s
.is_null() || s
.is_empty())
2019 request_or_macro
*p
= lookup_request(s
);
2020 macro
*m
= p
->to_macro();
2022 error("can't transparently throughput a request");
2025 nd
= new special_node(*m
);
2032 if (type
== TOKEN_NODE
)
2033 nd
= new zero_width_node(nd
);
2035 charinfo
*ci
= get_char(1);
2038 node
*gn
= curenv
->make_char_node(ci
);
2041 nd
= new zero_width_node(gn
);
2047 nd
= do_zero_width();
2053 goto ESCAPE_LEFT_BRACE
;
2055 goto ESCAPE_RIGHT_BRACE
;
2059 if (!compatible_flag
) {
2060 symbol s
= read_long_escape_name(WITH_ARGS
);
2061 if (s
.is_null() || s
.is_empty())
2063 if (have_string_arg
) {
2064 have_string_arg
= 0;
2065 nm
= composite_glyph_name(s
);
2068 const char *gn
= check_unicode_name(s
.contents());
2070 const char *gn_decomposed
= decompose_unicode(gn
);
2072 gn
= &gn_decomposed
[1];
2073 const char *groff_gn
= unicode_to_glyph_name(gn
);
2075 nm
= symbol(groff_gn
);
2077 char *buf
= new char[strlen(gn
) + 1 + 1];
2085 nm
= symbol(s
.contents());
2087 type
= TOKEN_SPECIAL
;
2090 goto handle_normal_char
;
2092 if (cc
!= escape_char
&& cc
!= '.')
2093 warning(WARN_ESCAPE
, "escape character ignored before %1",
2094 input_char_description(cc
));
2095 goto handle_normal_char
;
2101 int token::operator==(const token
&t
)
2110 case TOKEN_NUMBERED_CHAR
:
2111 return val
== t
.val
;
2117 int token::operator!=(const token
&t
)
2119 return !(*this == t
);
2122 // is token a suitable delimiter (like ')?
2124 int token::delimiter(int err
)
2153 error("cannot use character `%1' as a starting delimiter", char(c
));
2160 case TOKEN_STRETCHABLE_SPACE
:
2161 case TOKEN_UNSTRETCHABLE_SPACE
:
2165 error("cannot use %1 as a starting delimiter", description());
2172 const char *token::description()
2176 case TOKEN_BACKSPACE
:
2177 return "a backspace character";
2188 case TOKEN_HYPHEN_INDICATOR
:
2190 case TOKEN_INTERRUPT
:
2192 case TOKEN_ITALIC_CORRECTION
:
2195 return "a leader character";
2196 case TOKEN_LEFT_BRACE
:
2198 case TOKEN_MARK_INPUT
:
2204 case TOKEN_NUMBERED_CHAR
:
2206 case TOKEN_RIGHT_BRACE
:
2211 return "a special character";
2214 case TOKEN_STRETCHABLE_SPACE
:
2216 case TOKEN_UNSTRETCHABLE_SPACE
:
2219 return "a tab character";
2220 case TOKEN_TRANSPARENT
:
2222 case TOKEN_TRANSPARENT_DUMMY
:
2224 case TOKEN_ZERO_WIDTH_BREAK
:
2227 return "end of input";
2231 return "a magic token";
2236 while (!tok
.newline())
2247 if (has_arg() && get_integer(&n
))
2248 compatible_flag
= n
!= 0;
2250 compatible_flag
= 1;
2254 static void empty_name_warning(int required
)
2256 if (tok
.newline() || tok
.eof()) {
2258 warning(WARN_MISSING
, "missing name");
2260 else if (tok
.right_brace() || tok
.tab()) {
2261 const char *start
= tok
.description();
2264 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2265 if (!tok
.newline() && !tok
.eof())
2266 error("%1 is not allowed before an argument", start
);
2268 warning(WARN_MISSING
, "missing name");
2271 error("name expected (got %1)", tok
.description());
2273 error("name expected (got %1): treated as missing", tok
.description());
2276 static void non_empty_name_warning()
2278 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2279 && !tok
.right_brace()
2280 // We don't want to give a warning for .el\{
2281 && !tok
.left_brace())
2282 error("%1 is not allowed in a name", tok
.description());
2285 symbol
get_name(int required
)
2287 if (compatible_flag
) {
2290 if ((buf
[0] = tok
.ch()) != 0) {
2292 if ((buf
[1] = tok
.ch()) != 0) {
2297 non_empty_name_warning();
2301 empty_name_warning(required
);
2306 return get_long_name(required
);
2309 symbol
get_long_name(int required
)
2311 return do_get_long_name(required
, 0);
2314 static symbol
do_get_long_name(int required
, char end
)
2318 char abuf
[ABUF_SIZE
];
2320 int buf_size
= ABUF_SIZE
;
2323 // If end != 0 we normally have to append a null byte
2324 if (i
+ 2 > buf_size
) {
2326 buf
= new char[ABUF_SIZE
*2];
2327 memcpy(buf
, abuf
, buf_size
);
2328 buf_size
= ABUF_SIZE
*2;
2331 char *old_buf
= buf
;
2332 buf
= new char[buf_size
*2];
2333 memcpy(buf
, old_buf
, buf_size
);
2338 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2344 empty_name_warning(required
);
2347 if (end
&& buf
[i
] == end
)
2350 non_empty_name_warning();
2363 topdiv
->set_last_page();
2364 if (!end_macro_name
.is_null()) {
2365 spring_trap(end_macro_name
);
2367 process_input_stack();
2369 curenv
->final_break();
2371 process_input_stack();
2373 if (topdiv
->get_page_length() > 0) {
2375 topdiv
->set_ejecting();
2376 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2377 input_stack::push(make_temp_iterator((char *)buf
));
2378 topdiv
->space(topdiv
->get_page_length(), 1);
2380 process_input_stack();
2381 seen_last_page_ejector
= 1; // should be set already
2382 topdiv
->set_ejecting();
2383 push_page_ejector();
2384 topdiv
->space(topdiv
->get_page_length(), 1);
2386 process_input_stack();
2388 // This will only happen if a trap-invoked macro starts a diversion,
2389 // or if vertical position traps have been disabled.
2390 cleanup_and_exit(0);
2393 // This implements .ex. The input stack must be cleared before calling
2398 input_stack::clear();
2405 void return_macro_request()
2407 if (has_arg() && tok
.ch())
2408 input_stack::pop_macro();
2409 input_stack::pop_macro();
2415 end_macro_name
= get_name();
2419 void blank_line_macro()
2421 blank_line_macro_name
= get_name();
2425 static void trapping_blank_line()
2427 if (!blank_line_macro_name
.is_null())
2428 spring_trap(blank_line_macro_name
);
2435 int old_compatible_flag
= compatible_flag
;
2436 compatible_flag
= 0;
2437 symbol nm
= get_name();
2441 interpolate_macro(nm
);
2442 compatible_flag
= old_compatible_flag
;
2445 inline int possibly_handle_first_page_transition()
2447 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2448 handle_first_page_transition();
2455 static int transparent_translate(int cc
)
2457 if (!invalid_input_char(cc
)) {
2458 charinfo
*ci
= charset_table
[cc
];
2459 switch (ci
->get_special_translation(1)) {
2460 case charinfo::TRANSLATE_SPACE
:
2462 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2463 return ESCAPE_TILDE
;
2464 case charinfo::TRANSLATE_DUMMY
:
2465 return ESCAPE_AMPERSAND
;
2466 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2467 return ESCAPE_PERCENT
;
2469 // This is really ugly.
2470 ci
= ci
->get_translation(1);
2472 int c
= ci
->get_ascii_code();
2475 error("can't translate %1 to special character `%2'"
2476 " in transparent throughput",
2477 input_char_description(cc
),
2485 struct int_stack_element
{
2487 int_stack_element
*next
;
2497 int_stack::int_stack()
2502 int_stack::~int_stack()
2505 int_stack_element
*temp
= top
;
2511 int int_stack::is_empty()
2516 void int_stack::push(int n
)
2518 int_stack_element
*p
= new int_stack_element
;
2524 int int_stack::pop()
2527 int_stack_element
*p
= top
;
2534 int node::reread(int *)
2539 int diverted_space_node::reread(int *bolp
)
2541 if (curenv
->get_fill())
2542 trapping_blank_line();
2549 int diverted_copy_file_node::reread(int *bolp
)
2551 curdiv
->copy_file(filename
.contents());
2556 int word_space_node::reread(int *)
2559 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2560 curenv
->space(w
->width
, w
->sentence_width
);
2567 int unbreakable_space_node::reread(int *)
2572 int hmotion_node::reread(int *)
2574 if (unformat
&& was_tab
) {
2575 curenv
->handle_tab(0);
2582 void process_input_stack()
2584 int_stack trap_bol_stack
;
2587 int suppress_next
= 0;
2589 case token::TOKEN_CHAR
:
2591 unsigned char ch
= tok
.c
;
2592 if (bol
&& !have_input
2593 && (ch
== curenv
->control_char
2594 || ch
== curenv
->no_break_control_char
)) {
2595 break_flag
= ch
== curenv
->control_char
;
2596 // skip tabs as well as spaces here
2599 } while (tok
.white_space());
2600 symbol nm
= get_name();
2604 interpolate_macro(nm
);
2608 if (possibly_handle_first_page_transition())
2612 curenv
->add_char(charset_table
[ch
]);
2614 if (tok
.type
!= token::TOKEN_CHAR
)
2624 case token::TOKEN_TRANSPARENT
:
2627 if (possibly_handle_first_page_transition())
2636 curdiv
->transparent_output(transparent_translate(cc
));
2638 curdiv
->transparent_output(n
);
2639 } while (cc
!= '\n' && cc
!= EOF
);
2641 curdiv
->transparent_output('\n');
2646 case token::TOKEN_NEWLINE
:
2648 if (bol
&& !old_have_input
2649 && !curenv
->get_prev_line_interrupted())
2650 trapping_blank_line();
2657 case token::TOKEN_REQUEST
:
2659 int request_code
= tok
.c
;
2661 switch (request_code
) {
2665 case COPY_FILE_REQUEST
:
2668 case TRANSPARENT_FILE_REQUEST
:
2672 case VJUSTIFY_REQUEST
:
2683 case token::TOKEN_SPACE
:
2685 if (possibly_handle_first_page_transition())
2687 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2689 // save space_width now so that it isn't changed by \f or \s
2690 // which we wouldn't notice here
2691 hunits space_width
= curenv
->get_space_width();
2693 nspaces
+= tok
.nspaces();
2695 } while (tok
.space());
2697 trapping_blank_line();
2701 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2702 curenv
->get_fill_color()));
2712 case token::TOKEN_EOF
:
2714 case token::TOKEN_NODE
:
2716 if (possibly_handle_first_page_transition())
2718 else if (tok
.nd
->reread(&bol
)) {
2723 curenv
->add_node(tok
.nd
);
2726 curenv
->possibly_break_line(1);
2730 case token::TOKEN_PAGE_EJECTOR
:
2732 continue_page_eject();
2733 // I think we just want to preserve bol.
2737 case token::TOKEN_BEGIN_TRAP
:
2739 trap_bol_stack
.push(bol
);
2744 case token::TOKEN_END_TRAP
:
2746 if (trap_bol_stack
.is_empty())
2747 error("spurious end trap token detected!");
2749 bol
= trap_bol_stack
.pop();
2752 /* I'm not totally happy about this. But I can't think of any other
2753 way to do it. Doing an output_pending_lines() whenever a
2754 TOKEN_END_TRAP is detected doesn't work: for example,
2767 a\%very\%very\%long\%word
2769 will print all but the first lines from the word immediately
2770 after the footer, rather than on the next page. */
2772 if (trap_bol_stack
.is_empty())
2773 curenv
->output_pending_lines();
2785 trap_sprung_flag
= 0;
2789 #ifdef WIDOW_CONTROL
2791 void flush_pending_lines()
2793 while (!tok
.newline() && !tok
.eof())
2795 curenv
->output_pending_lines();
2799 #endif /* WIDOW_CONTROL */
2801 request_or_macro::request_or_macro()
2805 macro
*request_or_macro::to_macro()
2810 request::request(REQUEST_FUNCP pp
) : p(pp
)
2814 void request::invoke(symbol
)
2820 enum { SIZE
= 128 };
2821 unsigned char s
[SIZE
];
2826 char_block::char_block()
2835 void append(unsigned char);
2836 void set(unsigned char, int);
2837 unsigned char get(int);
2844 friend class macro_header
;
2845 friend class string_iterator
;
2848 char_list::char_list()
2849 : ptr(0), len(0), head(0), tail(0)
2853 char_list::~char_list()
2856 char_block
*tem
= head
;
2862 int char_list::length()
2867 void char_list::append(unsigned char c
)
2870 head
= tail
= new char_block
;
2874 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2875 tail
->next
= new char_block
;
2884 void char_list::set(unsigned char c
, int offset
)
2886 assert(len
> offset
);
2887 // optimization for access at the end
2888 int boundary
= len
- len
% char_block::SIZE
;
2889 if (offset
>= boundary
) {
2890 *(tail
->s
+ offset
- boundary
) = c
;
2893 char_block
*tem
= head
;
2896 l
+= char_block::SIZE
;
2898 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
2905 unsigned char char_list::get(int offset
)
2907 assert(len
> offset
);
2908 // optimization for access at the end
2909 int boundary
= len
- len
% char_block::SIZE
;
2910 if (offset
>= boundary
)
2911 return *(tail
->s
+ offset
- boundary
);
2912 char_block
*tem
= head
;
2915 l
+= char_block::SIZE
;
2917 return *(tem
->s
+ offset
% char_block::SIZE
);
2928 void append(node
*);
2932 friend class macro_header
;
2933 friend class string_iterator
;
2936 void node_list::append(node
*n
)
2944 tail
= tail
->next
= n
;
2948 int node_list::length()
2951 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2956 node_list::node_list()
2961 node
*node_list::extract()
2968 node_list::~node_list()
2970 delete_node_list(head
);
2973 struct macro_header
{
2978 macro_header() { count
= 1; }
2979 macro_header
*copy(int);
2984 if (p
!= 0 && --(p
->count
) <= 0)
2990 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2999 macro::macro(const macro
&m
)
3000 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
3001 empty_macro(m
.empty_macro
)
3007 macro
¯o::operator=(const macro
&m
)
3009 // don't assign object
3012 if (p
!= 0 && --(p
->count
) <= 0)
3015 filename
= m
.filename
;
3018 empty_macro
= m
.empty_macro
;
3022 void macro::append(unsigned char c
)
3026 p
= new macro_header
;
3027 if (p
->cl
.length() != len
) {
3028 macro_header
*tem
= p
->copy(len
);
3029 if (--(p
->count
) <= 0)
3035 if (c
!= COMPATIBLE_SAVE
&& c
!= COMPATIBLE_RESTORE
)
3039 void macro::set(unsigned char c
, int offset
)
3043 p
->cl
.set(c
, offset
);
3046 unsigned char macro::get(int offset
)
3049 return p
->cl
.get(offset
);
3057 void macro::append_str(const char *s
)
3062 while (s
[i
] != (char)0) {
3069 void macro::append(node
*n
)
3073 p
= new macro_header
;
3074 if (p
->cl
.length() != len
) {
3075 macro_header
*tem
= p
->copy(len
);
3076 if (--(p
->count
) <= 0)
3086 void macro::append_unsigned(unsigned int i
)
3088 unsigned int j
= i
/ 10;
3091 append(((unsigned char)(((int)'0') + i
% 10)));
3094 void macro::append_int(int i
)
3100 append_unsigned((unsigned int)i
);
3103 void macro::print_size()
3105 errprint("%1", len
);
3108 // make a copy of the first n bytes
3110 macro_header
*macro_header::copy(int n
)
3112 macro_header
*p
= new macro_header
;
3113 char_block
*bp
= cl
.head
;
3114 unsigned char *ptr
= bp
->s
;
3117 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3124 p
->nl
.append(nd
->copy());
3133 object_dictionary_iterator
iter(request_dictionary
);
3134 request_or_macro
*rm
;
3136 while (iter
.get(&s
, (object
**)&rm
)) {
3137 assert(!s
.is_null());
3138 macro
*m
= rm
->to_macro();
3140 errprint("%1\t", s
.contents());
3149 class string_iterator
: public input_iterator
{
3151 const char *how_invoked
;
3155 int count
; // of characters remaining
3157 int saved_compatible_flag
;
3162 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
3165 int get_location(int, const char **, int *);
3167 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3168 int get_compatible_flag() { return saved_compatible_flag
; }
3171 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3172 : mac(m
), how_invoked(p
),
3173 newline_flag(0), lineno(1), nm(s
)
3177 bp
= mac
.p
->cl
.head
;
3178 nd
= mac
.p
->nl
.head
;
3188 string_iterator::string_iterator()
3199 int string_iterator::fill(node
**np
)
3206 const unsigned char *p
= eptr
;
3207 if (p
>= bp
->s
+ char_block::SIZE
) {
3219 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3224 unsigned char c
= *p
;
3225 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3239 int string_iterator::peek()
3243 const unsigned char *p
= eptr
;
3244 if (p
>= bp
->s
+ char_block::SIZE
) {
3250 int string_iterator::get_location(int allow_macro
,
3251 const char **filep
, int *linep
)
3255 if (mac
.filename
== 0)
3257 *filep
= mac
.filename
;
3258 *linep
= mac
.lineno
+ lineno
- 1;
3262 void string_iterator::backtrace()
3265 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3268 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3270 errprint(": %1\n", how_invoked
);
3277 class temp_iterator
: public input_iterator
{
3278 unsigned char *base
;
3279 temp_iterator(const char *, int len
);
3282 friend input_iterator
*make_temp_iterator(const char *);
3288 temp_iterator::temp_iterator(const char *s
, int len
)
3290 base
= new unsigned char[len
];
3291 memcpy(base
, s
, len
);
3296 temp_iterator::~temp_iterator()
3301 class small_temp_iterator
: public input_iterator
{
3303 small_temp_iterator(const char *, int);
3304 ~small_temp_iterator();
3305 enum { BLOCK
= 16 };
3306 static small_temp_iterator
*free_list
;
3307 void *operator new(size_t);
3308 void operator delete(void *);
3310 unsigned char buf
[SIZE
];
3311 friend input_iterator
*make_temp_iterator(const char *);
3314 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3316 void *small_temp_iterator::operator new(size_t n
)
3318 assert(n
== sizeof(small_temp_iterator
));
3321 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3322 for (int i
= 0; i
< BLOCK
- 1; i
++)
3323 free_list
[i
].next
= free_list
+ i
+ 1;
3324 free_list
[BLOCK
-1].next
= 0;
3326 small_temp_iterator
*p
= free_list
;
3327 free_list
= (small_temp_iterator
*)(free_list
->next
);
3335 void small_temp_iterator::operator delete(void *p
)
3338 ((small_temp_iterator
*)p
)->next
= free_list
;
3339 free_list
= (small_temp_iterator
*)p
;
3343 small_temp_iterator::~small_temp_iterator()
3350 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3352 for (int i
= 0; i
< len
; i
++)
3358 input_iterator
*make_temp_iterator(const char *s
)
3361 return new small_temp_iterator(s
, 0);
3364 if (n
<= small_temp_iterator::SIZE
)
3365 return new small_temp_iterator(s
, n
);
3367 return new temp_iterator(s
, n
);
3371 // this is used when macros with arguments are interpolated
3376 arg_list(const macro
&);
3380 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
3384 arg_list::~arg_list()
3388 class macro_iterator
: public string_iterator
{
3392 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
3395 int has_args() { return 1; }
3396 input_iterator
*get_arg(int i
);
3397 int nargs() { return argc
; }
3398 void add_arg(const macro
&m
);
3400 int is_macro() { return 1; }
3403 input_iterator
*macro_iterator::get_arg(int i
)
3406 return make_temp_iterator(nm
.contents());
3407 if (i
> 0 && i
<= argc
) {
3409 for (int j
= 1; j
< i
; j
++) {
3413 return new string_iterator(p
->mac
);
3419 void macro_iterator::add_arg(const macro
&m
)
3422 for (p
= &args
; *p
; p
= &((*p
)->next
))
3424 *p
= new arg_list(m
);
3428 void macro_iterator::shift(int n
)
3430 while (n
> 0 && argc
> 0) {
3431 arg_list
*tem
= args
;
3439 // This gets used by eg .if '\?xxx\?''.
3441 int operator==(const macro
&m1
, const macro
&m2
)
3443 if (m1
.len
!= m2
.len
)
3445 string_iterator
iter1(m1
);
3446 string_iterator
iter2(m2
);
3450 int c1
= iter1
.get(&nd1
);
3453 int c2
= iter2
.get(&nd2
);
3465 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3475 static void interpolate_macro(symbol nm
)
3477 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3480 const char *s
= nm
.contents();
3481 if (strlen(s
) > 2) {
3482 request_or_macro
*r
;
3487 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3489 macro
*m
= r
->to_macro();
3490 if (!m
|| !m
->empty())
3491 warned
= warning(WARN_SPACE
,
3492 "macro `%1' not defined "
3493 "(probably missing space after `%2')",
3494 nm
.contents(), buf
);
3498 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3500 request_dictionary
.define(nm
, p
);
3511 static void decode_args(macro_iterator
*mi
)
3513 if (!tok
.newline() && !tok
.eof()) {
3515 int c
= get_copy(&n
);
3519 if (c
== '\n' || c
== EOF
)
3522 int quote_input_level
= 0;
3523 int done_tab_warning
= 0;
3525 quote_input_level
= input_stack::get_level();
3528 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3529 if (quote_input_level
> 0 && c
== '\"'
3531 || input_stack::get_level() == quote_input_level
)) {
3544 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3545 warning(WARN_TAB
, "tab character in unquoted macro argument");
3546 done_tab_warning
= 1;
3558 static void decode_string_args(macro_iterator
*mi
)
3561 int c
= get_copy(&n
);
3565 if (c
== '\n' || c
== EOF
) {
3566 error("missing `]'");
3572 int quote_input_level
= 0;
3573 int done_tab_warning
= 0;
3575 quote_input_level
= input_stack::get_level();
3578 while (c
!= EOF
&& c
!= '\n'
3579 && !(c
== ']' && quote_input_level
== 0)
3580 && !(c
== ' ' && quote_input_level
== 0)) {
3581 if (quote_input_level
> 0 && c
== '\"'
3582 && input_stack::get_level() == quote_input_level
) {
3595 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3596 warning(WARN_TAB
, "tab character in unquoted string argument");
3597 done_tab_warning
= 1;
3608 void macro::invoke(symbol nm
)
3610 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3612 input_stack::push(mi
);
3616 macro
*macro::to_macro()
3623 return empty_macro
== 1;
3626 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
3627 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
3631 macro_iterator::macro_iterator() : args(0), argc(0)
3635 macro_iterator::~macro_iterator()
3638 arg_list
*tem
= args
;
3644 dictionary
composite_dictionary(17);
3646 void composite_request()
3648 symbol from
= get_name(1);
3649 if (!from
.is_null()) {
3650 const char *from_gn
= glyph_name_to_unicode(from
.contents());
3652 from_gn
= check_unicode_name(from
.contents());
3654 error("invalid composite glyph name `%1'", from
.contents());
3659 const char *from_decomposed
= decompose_unicode(from_gn
);
3660 if (from_decomposed
)
3661 from_gn
= &from_decomposed
[1];
3662 symbol to
= get_name(1);
3664 composite_dictionary
.remove(symbol(from_gn
));
3666 const char *to_gn
= glyph_name_to_unicode(to
.contents());
3668 to_gn
= check_unicode_name(to
.contents());
3670 error("invalid composite glyph name `%1'", to
.contents());
3675 const char *to_decomposed
= decompose_unicode(to_gn
);
3677 to_gn
= &to_decomposed
[1];
3678 if (strcmp(from_gn
, to_gn
) == 0)
3679 composite_dictionary
.remove(symbol(from_gn
));
3681 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
3687 static symbol
composite_glyph_name(symbol nm
)
3689 macro_iterator
*mi
= new macro_iterator();
3690 decode_string_args(mi
);
3691 input_stack::push(mi
);
3692 const char *gn
= glyph_name_to_unicode(nm
.contents());
3694 gn
= check_unicode_name(nm
.contents());
3696 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
3697 return EMPTY_SYMBOL
;
3700 const char *gn_decomposed
= decompose_unicode(gn
);
3701 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
3703 int n
= input_stack::nargs();
3704 for (int i
= 1; i
<= n
; i
++) {
3706 input_iterator
*p
= input_stack::get_arg(i
);
3709 while ((c
= p
->get(0)) != EOF
)
3712 const char *u
= glyph_name_to_unicode(gl
.contents());
3714 u
= check_unicode_name(gl
.contents());
3716 error("invalid component `%1' in composite glyph name",
3718 return EMPTY_SYMBOL
;
3721 const char *decomposed
= decompose_unicode(u
);
3724 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
3725 if (mapped_composite
)
3726 u
= (const char *)mapped_composite
;
3730 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
3732 return symbol(groff_gn
);
3736 return symbol(gl
.contents());
3739 int trap_sprung_flag
= 0;
3740 int postpone_traps_flag
= 0;
3741 symbol postponed_trap
;
3743 void spring_trap(symbol nm
)
3745 assert(!nm
.is_null());
3746 trap_sprung_flag
= 1;
3747 if (postpone_traps_flag
) {
3748 postponed_trap
= nm
;
3751 static char buf
[2] = { BEGIN_TRAP
, 0 };
3752 static char buf2
[2] = { END_TRAP
, '\0' };
3753 input_stack::push(make_temp_iterator(buf2
));
3754 request_or_macro
*p
= lookup_request(nm
);
3755 macro
*m
= p
->to_macro();
3757 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3759 error("you can't invoke a request with a trap");
3760 input_stack::push(make_temp_iterator(buf
));
3763 void postpone_traps()
3765 postpone_traps_flag
= 1;
3768 int unpostpone_traps()
3770 postpone_traps_flag
= 0;
3771 if (!postponed_trap
.is_null()) {
3772 spring_trap(postponed_trap
);
3773 postponed_trap
= NULL_SYMBOL
;
3782 macro_iterator
*mi
= new macro_iterator
;
3783 int reading_from_terminal
= isatty(fileno(stdin
));
3785 if (!tok
.newline() && !tok
.eof()) {
3786 int c
= get_copy(0);
3789 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3790 if (!invalid_input_char(c
)) {
3791 if (reading_from_terminal
)
3802 if (reading_from_terminal
) {
3803 fputc(had_prompt
? ':' : '\a', stderr
);
3806 input_stack::push(mi
);
3810 while ((c
= getchar()) != EOF
) {
3811 if (invalid_input_char(c
))
3812 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
3825 if (reading_from_terminal
)
3827 input_stack::push(new string_iterator(mac
));
3831 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3832 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
};
3833 enum comp_mode
{ COMP_IGNORE
, COMP_DISABLE
};
3835 void do_define_string(define_mode mode
, comp_mode comp
)
3849 else if (!tok
.space()) {
3850 error("bad string definition");
3861 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3862 macro
*mm
= rm
? rm
->to_macro() : 0;
3863 if (mode
== DEFINE_APPEND
&& mm
)
3865 if (comp
== COMP_DISABLE
)
3866 mac
.append(COMPATIBLE_SAVE
);
3867 while (c
!= '\n' && c
!= EOF
) {
3871 mac
.append((unsigned char)c
);
3876 request_dictionary
.define(nm
, mm
);
3878 if (comp
== COMP_DISABLE
)
3879 mac
.append(COMPATIBLE_RESTORE
);
3884 void define_string()
3886 do_define_string(DEFINE_NORMAL
, COMP_IGNORE
);
3889 void define_nocomp_string()
3891 do_define_string(DEFINE_NORMAL
, COMP_DISABLE
);
3894 void append_string()
3896 do_define_string(DEFINE_APPEND
, COMP_IGNORE
);
3899 void append_nocomp_string()
3901 do_define_string(DEFINE_APPEND
, COMP_DISABLE
);
3904 void do_define_character(char_mode mode
, const char *font_name
)
3909 charinfo
*ci
= tok
.get_char(1);
3915 string
s(font_name
);
3917 s
+= ci
->nm
.contents();
3919 ci
= get_charinfo(symbol(s
.contents()));
3926 else if (!tok
.space()) {
3927 error("bad character definition");
3933 while (c
== ' ' || c
== '\t')
3937 macro
*m
= new macro
;
3938 while (c
!= '\n' && c
!= EOF
) {
3942 m
->append((unsigned char)c
);
3945 m
= ci
->setx_macro(m
, mode
);
3951 void define_character()
3953 do_define_character(CHAR_NORMAL
);
3956 void define_fallback_character()
3958 do_define_character(CHAR_FALLBACK
);
3961 void define_special_character()
3963 do_define_character(CHAR_SPECIAL
);
3966 static void remove_character()
3969 while (!tok
.newline() && !tok
.eof()) {
3970 if (!tok
.space() && !tok
.tab()) {
3971 charinfo
*ci
= tok
.get_char(1);
3974 macro
*m
= ci
->set_macro(0);
3983 static void interpolate_string(symbol nm
)
3985 request_or_macro
*p
= lookup_request(nm
);
3986 macro
*m
= p
->to_macro();
3988 error("you can only invoke a string or macro using \\*");
3990 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3991 input_stack::push(si
);
3995 static void interpolate_string_with_args(symbol s
)
3997 request_or_macro
*p
= lookup_request(s
);
3998 macro
*m
= p
->to_macro();
4000 error("you can only invoke a string or macro using \\*");
4002 macro_iterator
*mi
= new macro_iterator(s
, *m
);
4003 decode_string_args(mi
);
4004 input_stack::push(mi
);
4008 /* This class is used for the implementation of \$@. It is used for
4009 each of the closing double quotes. It artificially increases the
4010 input level by 2, so that the closing double quote will appear to have
4011 the same input level as the opening quote. */
4013 class end_quote_iterator
: public input_iterator
{
4014 unsigned char buf
[1];
4016 end_quote_iterator();
4017 ~end_quote_iterator() { }
4018 int internal_level() { return 2; }
4021 end_quote_iterator::end_quote_iterator()
4028 static void interpolate_arg(symbol nm
)
4030 const char *s
= nm
.contents();
4031 if (!s
|| *s
== '\0')
4032 copy_mode_error("missing argument name");
4033 else if (s
[1] == 0 && csdigit(s
[0]))
4034 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4035 else if (s
[0] == '*' && s
[1] == '\0') {
4036 for (int i
= input_stack::nargs(); i
> 0; i
--) {
4037 input_stack::push(input_stack::get_arg(i
));
4039 input_stack::push(make_temp_iterator(" "));
4042 else if (s
[0] == '@' && s
[1] == '\0') {
4043 for (int i
= input_stack::nargs(); i
> 0; i
--) {
4044 input_stack::push(new end_quote_iterator
);
4045 input_stack::push(input_stack::get_arg(i
));
4046 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
4051 for (p
= s
; *p
&& csdigit(*p
); p
++)
4054 copy_mode_error("bad argument name `%1'", s
);
4056 input_stack::push(input_stack::get_arg(atoi(s
)));
4060 void handle_first_page_transition()
4063 topdiv
->begin_page();
4066 // We push back a token by wrapping it up in a token_node, and
4067 // wrapping that up in a string_iterator.
4069 static void push_token(const token
&t
)
4072 m
.append(new token_node(t
));
4073 input_stack::push(new string_iterator(m
));
4076 void push_page_ejector()
4078 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4079 input_stack::push(make_temp_iterator(buf
));
4082 void handle_initial_request(unsigned char code
)
4088 mac
.append(new token_node(tok
));
4089 input_stack::push(new string_iterator(mac
));
4090 input_stack::push(make_temp_iterator(buf
));
4091 topdiv
->begin_page();
4095 void handle_initial_title()
4097 handle_initial_request(TITLE_REQUEST
);
4100 // this should be local to define_macro, but cfront 1.2 doesn't support that
4101 static symbol
dot_symbol(".");
4103 void do_define_macro(define_mode mode
, calling_mode calling
, comp_mode comp
)
4106 if (calling
== CALLING_INDIRECT
) {
4107 symbol temp1
= get_name(1);
4108 if (temp1
.is_null()) {
4112 symbol temp2
= get_name();
4113 input_stack::push(make_temp_iterator("\n"));
4114 if (!temp2
.is_null()) {
4115 interpolate_string(temp2
);
4116 input_stack::push(make_temp_iterator(" "));
4118 interpolate_string(temp1
);
4119 input_stack::push(make_temp_iterator(" "));
4122 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4129 term
= get_name(); // the request that terminates the definition
4132 while (!tok
.newline() && !tok
.eof())
4134 const char *start_filename
;
4136 int have_start_location
= input_stack::get_location(0, &start_filename
,
4139 // doing this here makes the line numbers come out right
4140 int c
= get_copy(&n
, 1);
4143 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4144 request_or_macro
*rm
=
4145 (request_or_macro
*)request_dictionary
.lookup(nm
);
4147 mm
= rm
->to_macro();
4148 if (mm
&& mode
== DEFINE_APPEND
)
4152 if (comp
== COMP_DISABLE
)
4153 mac
.append(COMPATIBLE_SAVE
);
4155 while (c
== ESCAPE_NEWLINE
) {
4156 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4158 c
= get_copy(&n
, 1);
4160 if (bol
&& c
== '.') {
4161 const char *s
= term
.contents();
4163 // see if it matches term
4166 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4168 if ((unsigned char)s
[0] == d
) {
4169 for (i
= 1; s
[i
] != 0; i
++) {
4171 if ((unsigned char)s
[i
] != d
)
4177 && ((i
== 2 && compatible_flag
)
4178 || (d
= get_copy(&n
)) == ' '
4179 || d
== '\n')) { // we found it
4184 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4187 request_dictionary
.define(nm
, mm
);
4189 if (comp
== COMP_DISABLE
)
4190 mac
.append(COMPATIBLE_RESTORE
);
4193 if (term
!= dot_symbol
) {
4195 interpolate_macro(term
);
4201 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4203 for (int j
= 0; j
< i
; j
++)
4209 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4210 if (have_start_location
)
4211 error_with_file_and_line(start_filename
, start_lineno
,
4212 "end of file while defining macro `%1'",
4215 error("end of file while defining macro `%1'", nm
.contents());
4218 if (have_start_location
)
4219 error_with_file_and_line(start_filename
, start_lineno
,
4220 "end of file while ignoring input lines");
4222 error("end of file while ignoring input lines");
4227 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4234 c
= get_copy(&n
, 1);
4240 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_IGNORE
);
4243 void define_nocomp_macro()
4245 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_DISABLE
);
4248 void define_indirect_macro()
4250 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_IGNORE
);
4253 void define_indirect_nocomp_macro()
4255 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_DISABLE
);
4260 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_IGNORE
);
4263 void append_nocomp_macro()
4265 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_DISABLE
);
4268 void append_indirect_macro()
4270 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_IGNORE
);
4273 void append_indirect_nocomp_macro()
4275 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_DISABLE
);
4281 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
, COMP_IGNORE
);
4288 symbol s
= get_name();
4291 request_dictionary
.remove(s
);
4298 symbol s1
= get_name(1);
4299 if (!s1
.is_null()) {
4300 symbol s2
= get_name(1);
4302 request_dictionary
.rename(s1
, s2
);
4309 symbol s1
= get_name(1);
4310 if (!s1
.is_null()) {
4311 symbol s2
= get_name(1);
4312 if (!s2
.is_null()) {
4313 if (!request_dictionary
.alias(s1
, s2
))
4314 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4322 symbol s
= get_name(1);
4324 request_or_macro
*p
= lookup_request(s
);
4325 macro
*m
= p
->to_macro();
4327 error("cannot chop request");
4328 else if (m
->empty())
4329 error("cannot chop empty macro");
4331 int have_restore
= 0;
4332 // we have to check for additional save/restore pairs which could be
4333 // there due to empty am1 requests.
4335 if (m
->get(m
->len
- 1) != COMPATIBLE_RESTORE
)
4339 if (m
->get(m
->len
- 1) != COMPATIBLE_SAVE
)
4347 error("cannot chop empty macro");
4350 m
->set(COMPATIBLE_RESTORE
, m
->len
- 1);
4359 void substring_request()
4361 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4362 symbol s
= get_name(1);
4363 if (!s
.is_null() && get_integer(&start
)) {
4364 request_or_macro
*p
= lookup_request(s
);
4365 macro
*m
= p
->to_macro();
4367 error("cannot apply `substring' on a request");
4370 if (!has_arg() || get_integer(&end
)) {
4371 int real_length
= 0; // 1, 2, ..., n
4372 string_iterator
iter1(*m
);
4373 for (int l
= 0; l
< m
->len
; l
++) {
4374 int c
= iter1
.get(0);
4375 if (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4382 start
+= real_length
;
4390 if (start
>= real_length
|| end
< 0) {
4392 "start and end index of substring out of range");
4395 if (--(m
->p
->count
) <= 0)
4404 "start index of substring out of range, set to 0");
4407 if (end
>= real_length
) {
4409 "end index of substring out of range, set to string length");
4410 end
= real_length
- 1;
4412 // now extract the substring
4413 string_iterator
iter(*m
);
4415 for (i
= 0; i
< start
; i
++) {
4416 int c
= iter
.get(0);
4417 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4423 for (; i
<= end
; i
++) {
4425 int c
= iter
.get(&nd
);
4426 while (c
== COMPATIBLE_SAVE
|| c
== COMPATIBLE_RESTORE
)
4433 mac
.append((unsigned char)c
);
4442 void length_request()
4446 if (ret
.is_null()) {
4456 else if (!tok
.space()) {
4457 error("bad string definition");
4468 while (c
!= '\n' && c
!= EOF
) {
4472 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4476 set_number_reg(ret
, len
);
4480 void asciify_macro()
4482 symbol s
= get_name(1);
4484 request_or_macro
*p
= lookup_request(s
);
4485 macro
*m
= p
->to_macro();
4487 error("cannot asciify request");
4490 string_iterator
iter(*m
);
4493 int c
= iter
.get(&nd
);
4507 void unformat_macro()
4509 symbol s
= get_name(1);
4511 request_or_macro
*p
= lookup_request(s
);
4512 macro
*m
= p
->to_macro();
4514 error("cannot unformat request");
4517 string_iterator
iter(*m
);
4520 int c
= iter
.get(&nd
);
4526 if (nd
->set_unformat_flag())
4536 static void interpolate_environment_variable(symbol nm
)
4538 const char *s
= getenv(nm
.contents());
4540 input_stack::push(make_temp_iterator(s
));
4543 void interpolate_number_reg(symbol nm
, int inc
)
4545 reg
*r
= lookup_number_reg(nm
);
4550 input_stack::push(make_temp_iterator(r
->get_string()));
4553 static void interpolate_number_format(symbol nm
)
4555 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4557 input_stack::push(make_temp_iterator(r
->get_format()));
4560 static int get_delim_number(units
*n
, int si
, int prev_value
)
4564 if (start
.delimiter(1)) {
4566 if (get_number(n
, si
, prev_value
)) {
4568 warning(WARN_DELIM
, "closing delimiter does not match");
4575 static int get_delim_number(units
*n
, int si
)
4579 if (start
.delimiter(1)) {
4581 if (get_number(n
, si
)) {
4583 warning(WARN_DELIM
, "closing delimiter does not match");
4590 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
4594 int start_level
= input_stack::get_level();
4595 if (!start
.delimiter(1))
4598 if (get_number(n
, si
)) {
4599 if (tok
.dummy() || tok
.transparent_dummy())
4601 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
4602 *cp
= tok
.get_char(1);
4605 if (!(start
== tok
&& input_stack::get_level() == start_level
))
4606 warning(WARN_DELIM
, "closing delimiter does not match");
4612 static int read_size(int *x
)
4622 else if (c
== '+') {
4633 // allow an increment either before or after the left parenthesis
4639 else if (c
== '+') {
4654 val
= val
*10 + (c
- '0');
4659 else if (csdigit(c
)) {
4661 if (!inc
&& c
!= '0' && c
< '4') {
4667 val
= val
*10 + (c
- '0');
4671 else if (!tok
.delimiter(1))
4677 ? get_number(&val
, 'z')
4678 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
4680 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
4681 if (start
.ch() == '[')
4682 error("missing `]'");
4684 error("missing closing delimiter");
4692 // special case -- \s[0] and \s0 means to revert to previous size
4699 *x
= curenv
->get_requested_point_size() + val
;
4702 *x
= curenv
->get_requested_point_size() - val
;
4709 "\\s request results in non-positive point size; set to 1");
4715 error("bad digit in point size");
4720 static symbol
get_delim_name()
4725 error("end of input at start of delimited name");
4728 if (start
.newline()) {
4729 error("can't delimit name with a newline");
4732 int start_level
= input_stack::get_level();
4733 char abuf
[ABUF_SIZE
];
4735 int buf_size
= ABUF_SIZE
;
4738 if (i
+ 1 > buf_size
) {
4740 buf
= new char[ABUF_SIZE
*2];
4741 memcpy(buf
, abuf
, buf_size
);
4742 buf_size
= ABUF_SIZE
*2;
4745 char *old_buf
= buf
;
4746 buf
= new char[buf_size
*2];
4747 memcpy(buf
, old_buf
, buf_size
);
4754 && (compatible_flag
|| input_stack::get_level() == start_level
))
4756 if ((buf
[i
] = tok
.ch()) == 0) {
4757 error("missing delimiter (got %1)", tok
.description());
4767 error("empty delimited name");
4782 static void do_register()
4786 if (!start
.delimiter(1))
4789 symbol nm
= get_long_name(1);
4794 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4796 if (!r
|| !r
->get_value(&prev_value
))
4799 if (!get_number(&val
, 'u', prev_value
))
4802 warning(WARN_DELIM
, "closing delimiter does not match");
4806 set_number_reg(nm
, val
);
4809 // this implements the \w escape sequence
4811 static void do_width()
4815 int start_level
= input_stack::get_level();
4816 environment
env(curenv
);
4817 environment
*oldenv
= curenv
;
4822 warning(WARN_DELIM
, "missing closing delimiter");
4825 if (tok
.newline()) {
4826 warning(WARN_DELIM
, "missing closing delimiter");
4827 input_stack::push(make_temp_iterator("\n"));
4831 && (compatible_flag
|| input_stack::get_level() == start_level
))
4836 units x
= env
.get_input_line_position().to_units();
4837 input_stack::push(make_temp_iterator(i_to_a(x
)));
4838 env
.width_registers();
4843 charinfo
*page_character
;
4845 void set_page_character()
4847 page_character
= get_optional_char();
4851 static const symbol
percent_symbol("%");
4853 void read_title_parts(node
**part
, hunits
*part_width
)
4856 if (tok
.newline() || tok
.eof())
4859 int start_level
= input_stack::get_level();
4861 for (int i
= 0; i
< 3; i
++) {
4862 while (!tok
.newline() && !tok
.eof()) {
4864 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
4868 if (page_character
!= 0 && tok
.get_char() == page_character
)
4869 interpolate_number_reg(percent_symbol
, 0);
4874 curenv
->wrap_up_tab();
4875 part_width
[i
] = curenv
->get_input_line_position();
4876 part
[i
] = curenv
->extract_output_line();
4878 while (!tok
.newline() && !tok
.eof())
4882 class non_interpreted_node
: public node
{
4885 non_interpreted_node(const macro
&);
4886 int interpret(macro
*);
4888 int ends_sentence();
4894 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
4898 int non_interpreted_node::ends_sentence()
4903 int non_interpreted_node::same(node
*nd
)
4905 return mac
== ((non_interpreted_node
*)nd
)->mac
;
4908 const char *non_interpreted_node::type()
4910 return "non_interpreted_node";
4913 int non_interpreted_node::force_tprint()
4918 node
*non_interpreted_node::copy()
4920 return new non_interpreted_node(mac
);
4923 int non_interpreted_node::interpret(macro
*m
)
4925 string_iterator
si(mac
);
4939 static node
*do_non_interpreted()
4944 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4949 if (c
== EOF
|| c
== '\n') {
4950 error("missing \\?");
4953 return new non_interpreted_node(mac
);
4956 static void encode_char(macro
*mac
, char c
)
4959 if ((font::use_charnames_in_special
) && tok
.special()) {
4960 charinfo
*ci
= tok
.get_char(1);
4961 const char *s
= ci
->get_symbol()->contents();
4962 if (s
[0] != (char)0) {
4966 while (s
[i
] != (char)0) {
4974 else if (tok
.stretchable_space()
4975 || tok
.unstretchable_space())
4977 else if (!(tok
.hyphen_indicator()
4979 || tok
.transparent_dummy()
4980 || tok
.zero_width_break()))
4981 error("%1 is invalid within \\X", tok
.description());
4984 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4986 * add escape escape sequence
4998 int start_level
= input_stack::get_level();
5001 tok
!= start
|| input_stack::get_level() != start_level
;
5004 warning(WARN_DELIM
, "missing closing delimiter");
5007 if (tok
.newline()) {
5008 input_stack::push(make_temp_iterator("\n"));
5009 warning(WARN_DELIM
, "missing closing delimiter");
5017 else if (tok
.leader())
5019 else if (tok
.backspace())
5023 encode_char(&mac
, c
);
5025 return new special_node(mac
);
5028 void output_request()
5030 if (!tok
.newline() && !tok
.eof()) {
5038 if (c
!= ' ' && c
!= '\t')
5041 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5042 topdiv
->transparent_output(c
);
5043 topdiv
->transparent_output('\n');
5048 extern int image_no
; // from node.cpp
5050 static node
*do_suppress(symbol nm
)
5052 if (nm
.is_null() || nm
.is_empty()) {
5053 error("expecting an argument to escape \\O");
5056 const char *s
= nm
.contents();
5059 if (begin_level
== 0)
5060 // suppress generation of glyphs
5061 return new suppress_node(0, 0);
5064 if (begin_level
== 0)
5065 // enable generation of glyphs
5066 return new suppress_node(1, 0);
5069 if (begin_level
== 0)
5070 return new suppress_node(1, 1);
5080 s
++; // move over '5'
5082 if (*s
== (char)0) {
5083 error("missing position and filename in \\O");
5086 if (!(position
== 'l'
5089 || position
== 'i')) {
5090 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5093 s
++; // onto image name
5094 if (s
== (char *)0) {
5095 error("missing image name for \\O");
5099 if (begin_level
== 0)
5100 return new suppress_node(symbol(s
), position
, image_no
);
5104 error("`%1' is an invalid argument to \\O", *s
);
5109 void special_node::tprint(troff_output_file
*out
)
5112 string_iterator
iter(mac
);
5114 int c
= iter
.get(0);
5117 for (const char *s
= ::asciify(c
); *s
; s
++)
5118 tprint_char(out
, *s
);
5123 int get_file_line(const char **filename
, int *lineno
)
5125 return input_stack::get_location(0, filename
, lineno
);
5131 if (get_integer(&n
)) {
5132 const char *filename
= 0;
5134 symbol s
= get_long_name();
5135 filename
= s
.contents();
5137 (void)input_stack::set_location(filename
, n
-1);
5142 static int nroff_mode
= 0;
5144 static void nroff_request()
5150 static void troff_request()
5156 static void skip_alternative()
5159 // ensure that ``.if 0\{'' works as expected
5160 if (tok
.left_brace())
5164 c
= input_stack::get(0);
5167 if (c
== ESCAPE_LEFT_BRACE
)
5169 else if (c
== ESCAPE_RIGHT_BRACE
)
5171 else if (c
== escape_char
&& escape_char
> 0)
5172 switch(input_stack::get(0)) {
5180 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5184 Note that the level can properly be < 0, eg
5190 So don't give an error message in this case.
5192 if (level
<= 0 && c
== '\n')
5198 static void begin_alternative()
5200 while (tok
.space() || tok
.left_brace())
5210 static int_stack if_else_stack
;
5217 while (tok
.ch() == '!') {
5222 unsigned char c
= tok
.ch();
5225 result
= !nroff_mode
;
5227 else if (c
== 'n') {
5229 result
= nroff_mode
;
5231 else if (c
== 'v') {
5235 else if (c
== 'o') {
5236 result
= (topdiv
->get_page_number() & 1);
5239 else if (c
== 'e') {
5240 result
= !(topdiv
->get_page_number() & 1);
5243 else if (c
== 'd' || c
== 'r') {
5245 symbol nm
= get_name(1);
5251 ? request_dictionary
.lookup(nm
) != 0
5252 : number_reg_dictionary
.lookup(nm
) != 0);
5254 else if (c
== 'm') {
5256 symbol nm
= get_long_name(1);
5261 result
= (nm
== default_symbol
5262 || color_dictionary
.lookup(nm
) != 0);
5264 else if (c
== 'c') {
5267 charinfo
*ci
= tok
.get_char(1);
5272 result
= character_exists(ci
, curenv
);
5275 else if (tok
.space())
5277 else if (tok
.delimiter()) {
5279 int delim_level
= input_stack::get_level();
5280 environment
env1(curenv
);
5281 environment
env2(curenv
);
5282 environment
*oldenv
= curenv
;
5284 for (int i
= 0; i
< 2; i
++) {
5287 if (tok
.newline() || tok
.eof()) {
5288 warning(WARN_DELIM
, "missing closing delimiter");
5294 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5300 node
*n1
= env1
.extract_output_line();
5301 node
*n2
= env2
.extract_output_line();
5302 result
= same_node_list(n1
, n2
);
5303 delete_node_list(n1
);
5304 delete_node_list(n2
);
5311 if (!get_number(&n
, 'u')) {
5321 begin_alternative();
5327 void if_else_request()
5329 if_else_stack
.push(do_if_request());
5339 if (if_else_stack
.is_empty()) {
5340 warning(WARN_EL
, "unbalanced .el request");
5344 if (if_else_stack
.pop())
5347 begin_alternative();
5351 static int while_depth
= 0;
5352 static int while_break_flag
= 0;
5354 void while_request()
5359 mac
.append(new token_node(tok
));
5362 int c
= input_stack::get(&n
);
5378 if (c
== ESCAPE_LEFT_BRACE
)
5380 else if (c
== ESCAPE_RIGHT_BRACE
)
5382 else if (c
== escape_char
)
5385 if (c
== '\n' && level
<= 0)
5390 error("unbalanced \\{ \\}");
5393 input_stack::add_boundary();
5395 input_stack::push(new string_iterator(mac
, "while loop"));
5397 if (!do_if_request()) {
5398 while (input_stack::get(0) != EOF
)
5402 process_input_stack();
5403 if (while_break_flag
|| input_stack::is_return_boundary()) {
5404 while_break_flag
= 0;
5408 input_stack::remove_boundary();
5414 void while_break_request()
5417 error("no while loop");
5421 while_break_flag
= 1;
5422 while (input_stack::get(0) != EOF
)
5428 void while_continue_request()
5431 error("no while loop");
5435 while (input_stack::get(0) != EOF
)
5445 symbol nm
= get_long_name(1);
5449 while (!tok
.newline() && !tok
.eof())
5452 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents());
5454 input_stack::push(new file_iterator(fp
, nm
.contents()));
5456 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5461 // like .so but use popen()
5466 error(".pso request not allowed in safer mode");
5470 #ifdef POPEN_MISSING
5471 error("pipes not available on this system");
5473 #else /* not POPEN_MISSING */
5474 if (tok
.newline() || tok
.eof())
5475 error("missing command");
5478 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5481 char *buf
= new char[buf_size
];
5483 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5484 const char *s
= asciify(c
);
5485 int slen
= strlen(s
);
5486 if (buf_used
+ slen
+ 1> buf_size
) {
5487 char *old_buf
= buf
;
5488 int old_buf_size
= buf_size
;
5490 buf
= new char[buf_size
];
5491 memcpy(buf
, old_buf
, old_buf_size
);
5494 strcpy(buf
+ buf_used
, s
);
5497 buf
[buf_used
] = '\0';
5499 FILE *fp
= popen(buf
, POPEN_RT
);
5501 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
5503 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5507 #endif /* not POPEN_MISSING */
5513 static int llx_reg_contents
= 0;
5514 static int lly_reg_contents
= 0;
5515 static int urx_reg_contents
= 0;
5516 static int ury_reg_contents
= 0;
5518 struct bounding_box
{
5519 int llx
, lly
, urx
, ury
;
5522 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5523 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5525 int parse_bounding_box(char *p
, bounding_box
*bb
)
5527 if (sscanf(p
, "%d %d %d %d",
5528 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
5531 /* The Document Structuring Conventions say that the numbers
5532 should be integers. Unfortunately some broken applications
5534 double x1
, x2
, x3
, x4
;
5535 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
5543 for (; *p
== ' ' || *p
== '\t'; p
++)
5545 if (strncmp(p
, "(atend)", 7) == 0) {
5550 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
5554 // This version is taken from psrm.cpp
5556 #define PS_LINE_MAX 255
5557 cset
white_space("\n\r \t");
5559 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
5568 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
5569 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
5570 error("invalid input character code %1 in `%2'", int(c
), filename
);
5571 else if (i
< PS_LINE_MAX
)
5575 error("PostScript file `%1' is non-conforming "
5576 "because length of line exceeds 255", filename
);
5584 if (c
!= EOF
&& c
!= '\n')
5590 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
5592 llx_reg_contents
= llx
;
5593 lly_reg_contents
= lly
;
5594 urx_reg_contents
= urx
;
5595 ury_reg_contents
= ury
;
5598 void do_ps_file(FILE *fp
, const char* filename
)
5602 char buf
[PS_LINE_MAX
];
5603 llx_reg_contents
= lly_reg_contents
=
5604 urx_reg_contents
= ury_reg_contents
= 0;
5605 if (!ps_get_line(buf
, fp
, filename
)) {
5606 error("`%1' is empty", filename
);
5609 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
5610 error("`%1' is not conforming to the Document Structuring Conventions",
5614 while (ps_get_line(buf
, fp
, filename
) != 0) {
5615 if (buf
[0] != '%' || buf
[1] != '%'
5616 || strncmp(buf
+ 2, "EndComments", 11) == 0)
5618 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5619 int res
= parse_bounding_box(buf
+ 14, &bb
);
5621 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5624 else if (res
== 2) {
5629 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5638 /* in the trailer, the last BoundingBox comment is significant */
5639 for (offset
= 512; !last_try
; offset
*= 2) {
5640 int had_trailer
= 0;
5642 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
5644 if (fseek(fp
, 0L, 0) == -1)
5647 while (ps_get_line(buf
, fp
, filename
) != 0) {
5648 if (buf
[0] == '%' && buf
[1] == '%') {
5650 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
5654 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
5655 int res
= parse_bounding_box(buf
+ 14, &bb
);
5658 else if (res
== 2) {
5659 error("`(atend)' not allowed in trailer of `%1'", filename
);
5663 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5672 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
5677 error("%%%%BoundingBox comment not found in `%1'", filename
);
5680 void ps_bbox_request()
5682 symbol nm
= get_long_name(1);
5686 while (!tok
.newline() && !tok
.eof())
5689 // PS files might contain non-printable characters, such as ^Z
5690 // and CRs not followed by an LF, so open them in binary mode.
5691 FILE *fp
= include_search_path
.open_file_cautious(nm
.contents(),
5694 do_ps_file(fp
, nm
.contents());
5698 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5703 const char *asciify(int c
)
5706 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
5707 buf
[1] = buf
[2] = '\0';
5709 case ESCAPE_QUESTION
:
5712 case ESCAPE_AMPERSAND
:
5715 case ESCAPE_RIGHT_PARENTHESIS
:
5718 case ESCAPE_UNDERSCORE
:
5724 case ESCAPE_CIRCUMFLEX
:
5727 case ESCAPE_LEFT_BRACE
:
5730 case ESCAPE_RIGHT_BRACE
:
5733 case ESCAPE_LEFT_QUOTE
:
5736 case ESCAPE_RIGHT_QUOTE
:
5754 case ESCAPE_PERCENT
:
5766 case COMPATIBLE_SAVE
:
5767 case COMPATIBLE_RESTORE
:
5771 if (invalid_input_char(c
))
5780 const char *input_char_description(int c
)
5784 return "a newline character";
5786 return "a backspace character";
5788 return "a leader character";
5790 return "a tab character";
5792 return "a space character";
5796 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
5797 if (invalid_input_char(c
)) {
5798 const char *s
= asciify(c
);
5805 sprintf(buf
, "magic character code %d", c
);
5814 sprintf(buf
, "character code %d", c
);
5818 // .tm, .tm1, and .tmc
5820 void do_terminal(int newline
, int string_like
)
5822 if (!tok
.newline() && !tok
.eof()) {
5826 if (string_like
&& c
== '"') {
5830 if (c
!= ' ' && c
!= '\t')
5833 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5834 fputs(asciify(c
), stderr
);
5837 fputc('\n', stderr
);
5852 void terminal_continue()
5857 dictionary
stream_dictionary(20);
5859 void do_open(int append
)
5861 symbol stream
= get_name(1);
5862 if (!stream
.is_null()) {
5863 symbol filename
= get_long_name(1);
5864 if (!filename
.is_null()) {
5866 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
5868 error("can't open `%1' for %2: %3",
5869 filename
.contents(),
5870 append
? "appending" : "writing",
5872 fp
= (FILE *)stream_dictionary
.remove(stream
);
5875 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
5886 error(".open request not allowed in safer mode");
5893 void opena_request()
5896 error(".opena request not allowed in safer mode");
5903 void close_request()
5905 symbol stream
= get_name(1);
5906 if (!stream
.is_null()) {
5907 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
5909 error("no stream named `%1'", stream
.contents());
5916 // .write and .writec
5918 void do_write_request(int newline
)
5920 symbol stream
= get_name(1);
5921 if (stream
.is_null()) {
5925 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5927 error("no stream named `%1'", stream
.contents());
5932 while ((c
= get_copy(0)) == ' ')
5936 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5937 fputs(asciify(c
), fp
);
5944 void write_request()
5946 do_write_request(1);
5949 void write_request_continue()
5951 do_write_request(0);
5954 void write_macro_request()
5956 symbol stream
= get_name(1);
5957 if (stream
.is_null()) {
5961 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
5963 error("no stream named `%1'", stream
.contents());
5967 symbol s
= get_name(1);
5972 request_or_macro
*p
= lookup_request(s
);
5973 macro
*m
= p
->to_macro();
5975 error("cannot write request");
5977 string_iterator
iter(*m
);
5979 int c
= iter
.get(0);
5982 fputs(asciify(c
), fp
);
5989 void warnscale_request()
5996 warn_scale
= (double)units_per_inch
;
5998 warn_scale
= (double)units_per_inch
/ 2.54;
6000 warn_scale
= (double)units_per_inch
/ 72.0;
6002 warn_scale
= (double)units_per_inch
/ 6.0;
6005 "invalid scaling indicator `%1', using `i' instead", c
);
6008 warn_scaling_indicator
= c
;
6013 void spreadwarn_request()
6016 if (has_arg() && get_hunits(&n
, 'm')) {
6019 hunits em
= curenv
->get_size();
6020 spread_limit
= (double)n
.to_units()
6021 / (em
.is_zero() ? hresolution
: em
.to_units());
6024 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
6025 // changing value; we mirror at
6026 // -0.5 to make zero a valid value
6030 static void init_charset_table()
6033 strcpy(buf
, "char");
6034 for (int i
= 0; i
< 256; i
++) {
6035 strcpy(buf
+ 4, i_to_a(i
));
6036 charset_table
[i
] = get_charinfo(symbol(buf
));
6037 charset_table
[i
]->set_ascii_code(i
);
6039 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6041 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6042 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6043 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6044 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6045 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6046 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6047 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6048 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6049 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6050 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6051 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6052 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6053 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6054 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6055 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6056 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6057 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6058 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6059 page_character
= charset_table
['%'];
6062 static void init_hpf_code_table()
6064 for (int i
= 0; i
< 256; i
++)
6065 hpf_code_table
[i
] = i
;
6068 static void do_translate(int translate_transparent
, int translate_input
)
6071 while (!tok
.newline() && !tok
.eof()) {
6073 // This is a really bizarre troff feature.
6075 translate_space_to_dummy
= tok
.dummy();
6076 if (tok
.newline() || tok
.eof())
6081 charinfo
*ci1
= tok
.get_char(1);
6085 if (tok
.newline() || tok
.eof()) {
6086 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6087 translate_transparent
);
6091 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6092 translate_transparent
);
6093 else if (tok
.stretchable_space())
6094 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6095 translate_transparent
);
6096 else if (tok
.dummy())
6097 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6098 translate_transparent
);
6099 else if (tok
.hyphen_indicator())
6100 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6101 translate_transparent
);
6103 charinfo
*ci2
= tok
.get_char(1);
6107 ci1
->set_translation(0, translate_transparent
, translate_input
);
6109 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6121 void translate_no_transparent()
6126 void translate_input()
6134 if (get_integer(&flags
))
6136 charinfo
*ci
= tok
.get_char(1);
6138 charinfo
*tem
= ci
->get_translation();
6141 ci
->set_flags(flags
);
6148 void hyphenation_code()
6151 while (!tok
.newline() && !tok
.eof()) {
6152 charinfo
*ci
= tok
.get_char(1);
6157 unsigned char c
= tok
.ch();
6159 error("hyphenation code must be ordinary character");
6163 error("hyphenation code cannot be digit");
6166 ci
->set_hyphenation_code(c
);
6167 if (ci
->get_translation()
6168 && ci
->get_translation()->get_translation_input())
6169 ci
->get_translation()->set_hyphenation_code(c
);
6176 void hyphenation_patterns_file_code()
6179 while (!tok
.newline() && !tok
.eof()) {
6181 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6183 error("missing output hyphenation code");
6186 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6187 hpf_code_table
[n1
] = n2
;
6191 error("output hyphenation code must be integer in the range 0..255");
6196 error("input hyphenation code must be integer in the range 0..255");
6203 charinfo
*token::get_char(int required
)
6205 if (type
== TOKEN_CHAR
)
6206 return charset_table
[c
];
6207 if (type
== TOKEN_SPECIAL
)
6208 return get_charinfo(nm
);
6209 if (type
== TOKEN_NUMBERED_CHAR
)
6210 return get_charinfo_by_number(val
);
6211 if (type
== TOKEN_ESCAPE
) {
6212 if (escape_char
!= 0)
6213 return charset_table
[escape_char
];
6215 error("`\\e' used while no current escape character");
6220 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6221 warning(WARN_MISSING
, "missing normal or special character");
6223 error("normal or special character expected (got %1)", description());
6228 charinfo
*get_optional_char()
6232 charinfo
*ci
= tok
.get_char();
6234 check_missing_character();
6240 void check_missing_character()
6242 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6243 error("normal or special character expected (got %1): "
6244 "treated as missing",
6250 int token::add_to_node_list(node
**pp
)
6257 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6263 if (escape_char
!= 0)
6264 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6266 case TOKEN_HYPHEN_INDICATOR
:
6267 *pp
= (*pp
)->add_discretionary_hyphen();
6269 case TOKEN_ITALIC_CORRECTION
:
6270 *pp
= (*pp
)->add_italic_correction(&w
);
6272 case TOKEN_LEFT_BRACE
:
6274 case TOKEN_MARK_INPUT
:
6275 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6281 case TOKEN_NUMBERED_CHAR
:
6282 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6284 case TOKEN_RIGHT_BRACE
:
6287 n
= new hmotion_node(curenv
->get_space_width(),
6288 curenv
->get_fill_color());
6291 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6293 case TOKEN_STRETCHABLE_SPACE
:
6294 n
= new unbreakable_space_node(curenv
->get_space_width(),
6295 curenv
->get_fill_color());
6297 case TOKEN_UNSTRETCHABLE_SPACE
:
6298 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6299 curenv
->get_fill_color());
6301 case TOKEN_TRANSPARENT_DUMMY
:
6302 n
= new transparent_dummy_node
;
6304 case TOKEN_ZERO_WIDTH_BREAK
:
6305 n
= new space_node(H0
, curenv
->get_fill_color());
6307 n
->is_escape_colon();
6319 void token::process()
6321 if (possibly_handle_first_page_transition())
6324 case TOKEN_BACKSPACE
:
6325 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6326 curenv
->get_fill_color()));
6329 curenv
->add_char(charset_table
[c
]);
6332 curenv
->add_node(new dummy_node
);
6341 if (escape_char
!= 0)
6342 curenv
->add_char(charset_table
[escape_char
]);
6344 case TOKEN_BEGIN_TRAP
:
6345 case TOKEN_END_TRAP
:
6346 case TOKEN_PAGE_EJECTOR
:
6347 // these are all handled in process_input_stack()
6349 case TOKEN_HYPHEN_INDICATOR
:
6350 curenv
->add_hyphen_indicator();
6352 case TOKEN_INTERRUPT
:
6353 curenv
->interrupt();
6355 case TOKEN_ITALIC_CORRECTION
:
6356 curenv
->add_italic_correction();
6359 curenv
->handle_tab(1);
6361 case TOKEN_LEFT_BRACE
:
6363 case TOKEN_MARK_INPUT
:
6364 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6370 curenv
->add_node(nd
);
6373 case TOKEN_NUMBERED_CHAR
:
6374 curenv
->add_char(get_charinfo_by_number(val
));
6377 // handled in process_input_stack()
6379 case TOKEN_RIGHT_BRACE
:
6385 curenv
->add_char(get_charinfo(nm
));
6390 case TOKEN_STRETCHABLE_SPACE
:
6391 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6392 curenv
->get_fill_color()));
6394 case TOKEN_UNSTRETCHABLE_SPACE
:
6395 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6396 curenv
->get_fill_color()));
6399 curenv
->handle_tab(0);
6401 case TOKEN_TRANSPARENT
:
6403 case TOKEN_TRANSPARENT_DUMMY
:
6404 curenv
->add_node(new transparent_dummy_node
);
6406 case TOKEN_ZERO_WIDTH_BREAK
:
6408 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6409 tmp
->freeze_space();
6410 tmp
->is_escape_colon();
6411 curenv
->add_node(tmp
);
6419 class nargs_reg
: public reg
{
6421 const char *get_string();
6424 const char *nargs_reg::get_string()
6426 return i_to_a(input_stack::nargs());
6429 class lineno_reg
: public reg
{
6431 const char *get_string();
6434 const char *lineno_reg::get_string()
6438 if (!input_stack::get_location(0, &file
, &line
))
6440 return i_to_a(line
);
6443 class writable_lineno_reg
: public general_reg
{
6445 writable_lineno_reg();
6446 void set_value(units
);
6447 int get_value(units
*);
6450 writable_lineno_reg::writable_lineno_reg()
6454 int writable_lineno_reg::get_value(units
*res
)
6458 if (!input_stack::get_location(0, &file
, &line
))
6464 void writable_lineno_reg::set_value(units n
)
6466 input_stack::set_location(0, n
);
6469 class filename_reg
: public reg
{
6471 const char *get_string();
6474 const char *filename_reg::get_string()
6478 if (input_stack::get_location(0, &file
, &line
))
6484 class constant_reg
: public reg
{
6487 constant_reg(const char *);
6488 const char *get_string();
6491 constant_reg::constant_reg(const char *p
) : s(p
)
6495 const char *constant_reg::get_string()
6500 constant_int_reg::constant_int_reg(int *q
) : p(q
)
6504 const char *constant_int_reg::get_string()
6509 void abort_request()
6514 else if (tok
.newline())
6517 while ((c
= get_copy(0)) == ' ')
6520 if (c
== EOF
|| c
== '\n')
6521 fputs("User Abort.", stderr
);
6523 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6524 fputs(asciify(c
), stderr
);
6526 fputc('\n', stderr
);
6527 cleanup_and_exit(1);
6533 char *s
= new char[len
];
6535 while ((c
= get_copy(0)) == ' ')
6538 while (c
!= '\n' && c
!= EOF
) {
6539 if (!invalid_input_char(c
)) {
6542 s
= new char[len
*2];
6543 memcpy(s
, tem
, len
);
6563 error(".pi request not allowed in safer mode");
6567 #ifdef POPEN_MISSING
6568 error("pipes not available on this system");
6570 #else /* not POPEN_MISSING */
6572 error("can't pipe: output already started");
6577 if ((pc
= read_string()) == 0)
6578 error("can't pipe to empty command");
6580 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
6581 strcpy(s
, pipe_command
);
6584 a_delete pipe_command
;
6591 #endif /* not POPEN_MISSING */
6595 static int system_status
;
6597 void system_request()
6600 error(".sy request not allowed in safer mode");
6604 char *command
= read_string();
6606 error("empty command");
6608 system_status
= system(command
);
6616 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6617 handle_initial_request(COPY_FILE_REQUEST
);
6620 symbol filename
= get_long_name(1);
6621 while (!tok
.newline() && !tok
.eof())
6625 if (!filename
.is_null())
6626 curdiv
->copy_file(filename
.contents());
6634 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6635 handle_initial_request(VJUSTIFY_REQUEST
);
6638 symbol type
= get_long_name(1);
6639 if (!type
.is_null())
6640 curdiv
->vjustify(type
);
6646 void transparent_file()
6648 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
6649 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
6652 symbol filename
= get_long_name(1);
6653 while (!tok
.newline() && !tok
.eof())
6657 if (!filename
.is_null()) {
6659 FILE *fp
= include_search_path
.open_file_cautious(filename
.contents());
6661 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
6668 if (invalid_input_char(c
))
6669 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
6671 curdiv
->transparent_output(c
);
6676 curdiv
->transparent_output('\n');
6688 page_range(int, int, page_range
*);
6689 int contains(int n
);
6692 page_range::page_range(int i
, int j
, page_range
*p
)
6693 : first(i
), last(j
), next(p
)
6697 int page_range::contains(int n
)
6699 return n
>= first
&& (last
<= 0 || n
<= last
);
6702 page_range
*output_page_list
= 0;
6704 int in_output_page_list(int n
)
6706 if (!output_page_list
)
6708 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
6714 static void parse_output_page_list(char *p
)
6720 else if (csdigit(*p
)) {
6723 i
= i
*10 + *p
++ - '0';
6724 while (csdigit(*p
));
6734 j
= j
*10 + *p
++ - '0';
6735 while (csdigit(*p
));
6741 last_page_number
= -1;
6742 else if (last_page_number
>= 0 && j
> last_page_number
)
6743 last_page_number
= j
;
6744 output_page_list
= new page_range(i
, j
, output_page_list
);
6750 error("bad output page list");
6751 output_page_list
= 0;
6755 static FILE *open_mac_file(const char *mac
, char **path
)
6757 // Try first FOOBAR.tmac, then tmac.FOOBAR
6758 char *s1
= new char[strlen(mac
)+strlen(MACRO_POSTFIX
)+1];
6760 strcat(s1
, MACRO_POSTFIX
);
6761 FILE *fp
= mac_path
->open_file(s1
, path
);
6764 char *s2
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
6765 strcpy(s2
, MACRO_PREFIX
);
6767 fp
= mac_path
->open_file(s2
, path
);
6773 static void process_macro_file(const char *mac
)
6776 FILE *fp
= open_mac_file(mac
, &path
);
6778 fatal("can't find macro file %1", mac
);
6779 const char *s
= symbol(path
).contents();
6781 input_stack::push(new file_iterator(fp
, s
));
6783 process_input_stack();
6786 static void process_startup_file(const char *filename
)
6789 search_path
*orig_mac_path
= mac_path
;
6790 mac_path
= &config_macro_path
;
6791 FILE *fp
= mac_path
->open_file(filename
, &path
);
6793 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6796 process_input_stack();
6798 mac_path
= orig_mac_path
;
6803 symbol nm
= get_long_name(1);
6807 while (!tok
.newline() && !tok
.eof())
6810 FILE *fp
= mac_path
->open_file(nm
.contents(), &path
);
6811 // .mso doesn't (and cannot) go through open_mac_file, so we
6812 // need to do it here manually: If we have tmac.FOOBAR, try
6813 // FOOBAR.tmac and vice versa
6815 const char *fn
= nm
.contents();
6816 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
6817 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
6818 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
6819 strcat(s
, MACRO_POSTFIX
);
6820 fp
= mac_path
->open_file(s
, &path
);
6824 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
6825 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
6826 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
6827 strcpy(s
, MACRO_PREFIX
);
6828 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
6829 fp
= mac_path
->open_file(s
, &path
);
6835 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
6839 error("can't find macro file `%1'", nm
.contents());
6844 static void process_input_file(const char *name
)
6847 if (strcmp(name
, "-") == 0) {
6853 fp
= include_search_path
.open_file_cautious(name
);
6855 fatal("can't open `%1': %2", name
, strerror(errno
));
6857 input_stack::push(new file_iterator(fp
, name
));
6859 process_input_stack();
6862 // make sure the_input is empty before calling this
6864 static int evaluate_expression(const char *expr
, units
*res
)
6866 input_stack::push(make_temp_iterator(expr
));
6868 int success
= get_number(res
, 'u');
6869 while (input_stack::get(0) != EOF
)
6874 static void do_register_assignment(const char *s
)
6876 const char *p
= strchr(s
, '=');
6882 if (evaluate_expression(s
+ 1, &n
))
6883 set_number_reg(buf
, n
);
6886 char *buf
= new char[p
- s
+ 1];
6887 memcpy(buf
, s
, p
- s
);
6890 if (evaluate_expression(p
+ 1, &n
))
6891 set_number_reg(buf
, n
);
6896 static void set_string(const char *name
, const char *value
)
6898 macro
*m
= new macro
;
6899 for (const char *p
= value
; *p
; p
++)
6900 if (!invalid_input_char((unsigned char)*p
))
6902 request_dictionary
.define(name
, m
);
6905 static void do_string_assignment(const char *s
)
6907 const char *p
= strchr(s
, '=');
6912 set_string(buf
, s
+ 1);
6915 char *buf
= new char[p
- s
+ 1];
6916 memcpy(buf
, s
, p
- s
);
6918 set_string(buf
, p
+ 1);
6923 struct string_list
{
6926 string_list(const char *ss
) : s(ss
), next(0) {}
6930 static void prepend_string(const char *s
, string_list
**p
)
6932 string_list
*l
= new string_list(s
);
6938 static void add_string(const char *s
, string_list
**p
)
6942 *p
= new string_list(s
);
6945 void usage(FILE *stream
, const char *prog
)
6948 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6949 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
6953 int main(int argc
, char **argv
)
6955 program_name
= argv
[0];
6956 static char stderr_buf
[BUFSIZ
];
6957 setbuf(stderr
, stderr_buf
);
6959 string_list
*macros
= 0;
6960 string_list
*register_assignments
= 0;
6961 string_list
*string_assignments
= 0;
6966 int no_rc
= 0; // don't process troffrc and troffrc-end
6967 int next_page_number
;
6969 hresolution
= vresolution
= 1;
6970 // restore $PATH if called from groff
6971 char* groff_path
= getenv("GROFF_PATH__");
6978 if (putenv(strsave(e
.contents())))
6979 fatal("putenv failed");
6981 static const struct option long_options
[] = {
6982 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
6983 { "version", no_argument
, 0, 'v' },
6986 while ((c
= getopt_long(argc
, argv
, "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6992 printf("GNU troff (groff) version %s\n", Version_string
);
6997 // Search path for .psbb files
6998 // and most other non-system input files.
6999 include_search_path
.command_line_dir(optarg
);
7004 is_html
= (strcmp(device
, "html") == 0);
7007 compatible_flag
= 1;
7013 macro_path
.command_line_dir(optarg
);
7014 safer_macro_path
.command_line_dir(optarg
);
7015 config_macro_path
.command_line_dir(optarg
);
7018 font::command_line_font_dir(optarg
);
7021 add_string(optarg
, ¯os
);
7030 enable_warning(optarg
);
7033 disable_warning(optarg
);
7042 ascii_output_flag
= 1;
7045 suppress_output_flag
= 1;
7048 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7051 error("bad page number");
7054 parse_output_page_list(optarg
);
7057 if (*optarg
== '\0')
7058 error("`-d' requires non-empty argument");
7060 add_string(optarg
, &string_assignments
);
7063 if (*optarg
== '\0')
7064 error("`-r' requires non-empty argument");
7066 add_string(optarg
, ®ister_assignments
);
7069 default_family
= symbol(optarg
);
7075 // silently ignore these
7078 safer_flag
= 0; // unsafe behaviour
7080 case CHAR_MAX
+ 1: // --help
7081 usage(stdout
, argv
[0]);
7085 usage(stderr
, argv
[0]);
7087 break; // never reached
7092 mac_path
= ¯o_path
;
7093 set_string(".T", device
);
7094 init_charset_table();
7095 init_hpf_code_table();
7096 if (!font::load_desc())
7097 fatal("sorry, I can't continue");
7098 units_per_inch
= font::res
;
7099 hresolution
= font::hor
;
7100 vresolution
= font::vert
;
7101 sizescale
= font::sizescale
;
7102 tcommand_flag
= font::tcommand
;
7103 warn_scale
= (double)units_per_inch
;
7104 warn_scaling_indicator
= 'i';
7105 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7106 default_family
= symbol(font::family
);
7107 font_size::init_size_table(font::sizes
);
7110 if (font::style_table
) {
7111 for (i
= 0; font::style_table
[i
]; i
++)
7112 mount_style(j
++, symbol(font::style_table
[i
]));
7114 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7115 // In the DESC file a font name of 0 (zero) means leave this
7117 if (strcmp(font::font_name_table
[i
], "0") != 0)
7118 mount_font(j
, symbol(font::font_name_table
[i
]));
7119 curdiv
= topdiv
= new top_level_diversion
;
7121 topdiv
->set_next_page_number(next_page_number
);
7122 init_input_requests();
7123 init_env_requests();
7124 init_div_requests();
7126 init_column_requests();
7128 init_node_requests();
7129 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7131 init_reg_requests();
7132 init_hyphen_requests();
7133 init_environments();
7134 while (string_assignments
) {
7135 do_string_assignment(string_assignments
->s
);
7136 string_list
*tem
= string_assignments
;
7137 string_assignments
= string_assignments
->next
;
7140 while (register_assignments
) {
7141 do_register_assignment(register_assignments
->s
);
7142 string_list
*tem
= register_assignments
;
7143 register_assignments
= register_assignments
->next
;
7147 process_startup_file(INITIAL_STARTUP_FILE
);
7149 process_macro_file(macros
->s
);
7150 string_list
*tem
= macros
;
7151 macros
= macros
->next
;
7155 process_startup_file(FINAL_STARTUP_FILE
);
7156 for (i
= optind
; i
< argc
; i
++)
7157 process_input_file(argv
[i
]);
7158 if (optind
>= argc
|| iflag
)
7159 process_input_file("-");
7161 return 0; // not reached
7167 if (has_arg() && get_integer(&n
)) {
7168 if (n
& ~WARN_TOTAL
) {
7169 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7175 warning_mask
= WARN_TOTAL
;
7179 static void init_registers()
7181 #ifdef LONG_FOR_TIME_T
7183 #else /* not LONG_FOR_TIME_T */
7185 #endif /* not LONG_FOR_TIME_T */
7187 // Use struct here to work around misfeature in old versions of g++.
7188 struct tm
*tt
= localtime(&t
);
7189 set_number_reg("seconds", int(tt
->tm_sec
));
7190 set_number_reg("minutes", int(tt
->tm_min
));
7191 set_number_reg("hours", int(tt
->tm_hour
));
7192 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7193 set_number_reg("dy", int(tt
->tm_mday
));
7194 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7195 set_number_reg("year", int(1900 + tt
->tm_year
));
7196 set_number_reg("yr", int(tt
->tm_year
));
7197 set_number_reg("$$", getpid());
7198 number_reg_dictionary
.define(".A",
7199 new constant_reg(ascii_output_flag
7205 * registers associated with \O
7208 static int output_reg_minx_contents
= -1;
7209 static int output_reg_miny_contents
= -1;
7210 static int output_reg_maxx_contents
= -1;
7211 static int output_reg_maxy_contents
= -1;
7213 void check_output_limits(int x
, int y
)
7215 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7216 output_reg_minx_contents
= x
;
7217 if (x
> output_reg_maxx_contents
)
7218 output_reg_maxx_contents
= x
;
7219 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7220 output_reg_miny_contents
= y
;
7221 if (y
> output_reg_maxy_contents
)
7222 output_reg_maxy_contents
= y
;
7225 void reset_output_registers()
7227 output_reg_minx_contents
= -1;
7228 output_reg_miny_contents
= -1;
7229 output_reg_maxx_contents
= -1;
7230 output_reg_maxy_contents
= -1;
7233 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7235 *minx
= output_reg_minx_contents
;
7236 *miny
= output_reg_miny_contents
;
7237 *maxx
= output_reg_maxx_contents
;
7238 *maxy
= output_reg_maxy_contents
;
7241 void init_input_requests()
7243 init_request("ab", abort_request
);
7244 init_request("als", alias_macro
);
7245 init_request("am", append_macro
);
7246 init_request("am1", append_nocomp_macro
);
7247 init_request("ami", append_indirect_macro
);
7248 init_request("ami1", append_indirect_nocomp_macro
);
7249 init_request("as", append_string
);
7250 init_request("as1", append_nocomp_string
);
7251 init_request("asciify", asciify_macro
);
7252 init_request("backtrace", backtrace_request
);
7253 init_request("blm", blank_line_macro
);
7254 init_request("break", while_break_request
);
7255 init_request("cf", copy_file
);
7256 init_request("cflags", char_flags
);
7257 init_request("char", define_character
);
7258 init_request("chop", chop_macro
);
7259 init_request("close", close_request
);
7260 init_request("color", activate_color
);
7261 init_request("composite", composite_request
);
7262 init_request("continue", while_continue_request
);
7263 init_request("cp", compatible
);
7264 init_request("de", define_macro
);
7265 init_request("de1", define_nocomp_macro
);
7266 init_request("defcolor", define_color
);
7267 init_request("dei", define_indirect_macro
);
7268 init_request("dei1", define_indirect_nocomp_macro
);
7269 init_request("do", do_request
);
7270 init_request("ds", define_string
);
7271 init_request("ds1", define_nocomp_string
);
7272 init_request("ec", set_escape_char
);
7273 init_request("ecr", restore_escape_char
);
7274 init_request("ecs", save_escape_char
);
7275 init_request("el", else_request
);
7276 init_request("em", end_macro
);
7277 init_request("eo", escape_off
);
7278 init_request("ex", exit_request
);
7279 init_request("fchar", define_fallback_character
);
7280 #ifdef WIDOW_CONTROL
7281 init_request("fpl", flush_pending_lines
);
7282 #endif /* WIDOW_CONTROL */
7283 init_request("hcode", hyphenation_code
);
7284 init_request("hpfcode", hyphenation_patterns_file_code
);
7285 init_request("ie", if_else_request
);
7286 init_request("if", if_request
);
7287 init_request("ig", ignore
);
7288 init_request("length", length_request
);
7289 init_request("lf", line_file
);
7290 init_request("mso", macro_source
);
7291 init_request("nop", nop_request
);
7292 init_request("nroff", nroff_request
);
7293 init_request("nx", next_file
);
7294 init_request("open", open_request
);
7295 init_request("opena", opena_request
);
7296 init_request("output", output_request
);
7297 init_request("pc", set_page_character
);
7298 init_request("pi", pipe_output
);
7299 init_request("pm", print_macros
);
7300 init_request("psbb", ps_bbox_request
);
7301 #ifndef POPEN_MISSING
7302 init_request("pso", pipe_source
);
7303 #endif /* not POPEN_MISSING */
7304 init_request("rchar", remove_character
);
7305 init_request("rd", read_request
);
7306 init_request("return", return_macro_request
);
7307 init_request("rm", remove_macro
);
7308 init_request("rn", rename_macro
);
7309 init_request("schar", define_special_character
);
7310 init_request("shift", shift
);
7311 init_request("so", source
);
7312 init_request("spreadwarn", spreadwarn_request
);
7313 init_request("substring", substring_request
);
7314 init_request("sy", system_request
);
7315 init_request("tm", terminal
);
7316 init_request("tm1", terminal1
);
7317 init_request("tmc", terminal_continue
);
7318 init_request("tr", translate
);
7319 init_request("trf", transparent_file
);
7320 init_request("trin", translate_input
);
7321 init_request("trnt", translate_no_transparent
);
7322 init_request("troff", troff_request
);
7323 init_request("unformat", unformat_macro
);
7325 init_request("vj", vjustify
);
7327 init_request("warn", warn_request
);
7328 init_request("warnscale", warnscale_request
);
7329 init_request("while", while_request
);
7330 init_request("write", write_request
);
7331 init_request("writec", write_request_continue
);
7332 init_request("writem", write_macro_request
);
7333 number_reg_dictionary
.define(".$", new nargs_reg
);
7334 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7335 number_reg_dictionary
.define(".c", new lineno_reg
);
7336 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7337 number_reg_dictionary
.define(".F", new filename_reg
);
7338 number_reg_dictionary
.define(".g", new constant_reg("1"));
7339 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7340 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7341 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7342 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7343 extern const char *major_version
;
7344 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7345 extern const char *revision
;
7346 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7347 extern const char *minor_version
;
7348 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7349 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7350 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7351 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7352 number_reg_dictionary
.define("opmaxx",
7353 new variable_reg(&output_reg_maxx_contents
));
7354 number_reg_dictionary
.define("opmaxy",
7355 new variable_reg(&output_reg_maxy_contents
));
7356 number_reg_dictionary
.define("opminx",
7357 new variable_reg(&output_reg_minx_contents
));
7358 number_reg_dictionary
.define("opminy",
7359 new variable_reg(&output_reg_miny_contents
));
7360 number_reg_dictionary
.define("slimit",
7361 new variable_reg(&input_stack::limit
));
7362 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7363 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7364 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7367 object_dictionary
request_dictionary(501);
7369 void init_request(const char *s
, REQUEST_FUNCP f
)
7371 request_dictionary
.define(s
, new request(f
));
7374 static request_or_macro
*lookup_request(symbol nm
)
7376 assert(!nm
.is_null());
7377 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7379 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7381 request_dictionary
.define(nm
, p
);
7386 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7388 // Don't interpret character definitions in compatible mode.
7389 int old_compatible_flag
= compatible_flag
;
7390 compatible_flag
= 0;
7391 int old_escape_char
= escape_char
;
7393 macro
*mac
= ci
->set_macro(0);
7395 environment
*oldenv
= curenv
;
7396 environment
env(envp
);
7398 curenv
->set_composite();
7399 token old_tok
= tok
;
7400 input_stack::add_boundary();
7401 string_iterator
*si
=
7402 new string_iterator(*mac
, "composite character", ci
->nm
);
7403 input_stack::push(si
);
7404 // we don't use process_input_stack, because we don't want to recognise
7410 if (tok
.newline()) {
7411 error("composite character mustn't contain newline");
7419 node
*n
= curenv
->extract_output_line();
7420 input_stack::remove_boundary();
7424 compatible_flag
= old_compatible_flag
;
7425 escape_char
= old_escape_char
;
7430 static node
*read_draw_node()
7434 if (!start
.delimiter(1)){
7437 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
7442 error("missing argument");
7444 unsigned char type
= tok
.ch();
7446 read_color_draw_node(start
);
7451 hvpair
*point
= new hvpair
[maxpoints
];
7456 for (i
= 0; tok
!= start
; i
++) {
7457 if (i
== maxpoints
) {
7458 hvpair
*oldpoint
= point
;
7459 point
= new hvpair
[maxpoints
*2];
7460 for (int j
= 0; j
< maxpoints
; j
++)
7461 point
[j
] = oldpoint
[j
];
7465 if (!get_hunits(&point
[i
].h
,
7466 type
== 'f' || type
== 't' ? 'u' : 'm')) {
7477 if (!get_vunits(&point
[i
].v
, 'v')) {
7483 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
7488 if (npoints
!= 1 || no_last_v
) {
7489 error("two arguments needed for line");
7494 if (npoints
!= 1 || !no_last_v
) {
7495 error("one argument needed for circle");
7501 if (npoints
!= 1 || no_last_v
) {
7502 error("two arguments needed for ellipse");
7507 if (npoints
!= 2 || no_last_v
) {
7508 error("four arguments needed for arc");
7514 error("even number of arguments needed for spline");
7517 if (npoints
!= 1 || !no_last_v
) {
7518 error("one argument needed for gray shade");
7523 // silently pass it through
7526 draw_node
*dn
= new draw_node(type
, point
, npoints
,
7527 curenv
->get_font_size(),
7528 curenv
->get_glyph_color(),
7529 curenv
->get_fill_color());
7541 static void read_color_draw_node(token
&start
)
7545 error("missing color scheme");
7548 unsigned char scheme
= tok
.ch();
7551 char end
= start
.ch();
7554 col
= read_cmy(end
);
7557 col
= &default_color
;
7560 col
= read_gray(end
);
7563 col
= read_cmyk(end
);
7566 col
= read_rgb(end
);
7570 curenv
->set_fill_color(col
);
7571 while (tok
!= start
) {
7572 if (tok
.newline() || tok
.eof()) {
7573 warning(WARN_DELIM
, "missing closing delimiter");
7574 input_stack::push(make_temp_iterator("\n"));
7585 } warning_table
[] = {
7586 { "char", WARN_CHAR
},
7587 { "range", WARN_RANGE
},
7588 { "break", WARN_BREAK
},
7589 { "delim", WARN_DELIM
},
7591 { "scale", WARN_SCALE
},
7592 { "number", WARN_NUMBER
},
7593 { "syntax", WARN_SYNTAX
},
7594 { "tab", WARN_TAB
},
7595 { "right-brace", WARN_RIGHT_BRACE
},
7596 { "missing", WARN_MISSING
},
7597 { "input", WARN_INPUT
},
7598 { "escape", WARN_ESCAPE
},
7599 { "space", WARN_SPACE
},
7600 { "font", WARN_FONT
},
7602 { "mac", WARN_MAC
},
7603 { "reg", WARN_REG
},
7605 { "color", WARN_COLOR
},
7606 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
7607 { "w", WARN_TOTAL
},
7608 { "default", DEFAULT_WARNING_MASK
},
7611 static int lookup_warning(const char *name
)
7613 for (unsigned int i
= 0;
7614 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
7616 if (strcmp(name
, warning_table
[i
].name
) == 0)
7617 return warning_table
[i
].mask
;
7621 static void enable_warning(const char *name
)
7623 int mask
= lookup_warning(name
);
7625 warning_mask
|= mask
;
7627 error("unknown warning `%1'", name
);
7630 static void disable_warning(const char *name
)
7632 int mask
= lookup_warning(name
);
7634 warning_mask
&= ~mask
;
7636 error("unknown warning `%1'", name
);
7639 static void copy_mode_error(const char *format
,
7645 static const char prefix
[] = "(in ignored input) ";
7646 char *s
= new char[sizeof(prefix
) + strlen(format
)];
7649 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
7653 error(format
, arg1
, arg2
, arg3
);
7656 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
7658 static void do_error(error_type type
,
7664 const char *filename
;
7666 if (inhibit_errors
&& type
< FATAL
)
7669 input_stack::backtrace();
7670 if (!get_file_line(&filename
, &lineno
))
7673 errprint("%1:%2: ", filename
, lineno
);
7674 else if (program_name
)
7675 fprintf(stderr
, "%s: ", program_name
);
7678 fputs("fatal error: ", stderr
);
7683 fputs("warning: ", stderr
);
7685 case OUTPUT_WARNING
:
7686 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
7687 fprintf(stderr
, "warning [p %d, %.1f%c",
7688 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
7689 if (topdiv
!= curdiv
) {
7690 double fromtop1
= curdiv
->get_vertical_position().to_units()
7692 fprintf(stderr
, ", div `%s', %.1f%c",
7693 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
7695 fprintf(stderr
, "]: ");
7698 errprint(format
, arg1
, arg2
, arg3
);
7699 fputc('\n', stderr
);
7702 cleanup_and_exit(1);
7705 int warning(warning_type t
,
7711 if ((t
& warning_mask
) != 0) {
7712 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
7719 int output_warning(warning_type t
,
7725 if ((t
& warning_mask
) != 0) {
7726 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
7733 void error(const char *format
,
7738 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
7741 void fatal(const char *format
,
7746 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
7749 void fatal_with_file_and_line(const char *filename
, int lineno
,
7755 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
7756 errprint(format
, arg1
, arg2
, arg3
);
7757 fputc('\n', stderr
);
7759 cleanup_and_exit(1);
7762 void error_with_file_and_line(const char *filename
, int lineno
,
7768 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
7769 errprint(format
, arg1
, arg2
, arg3
);
7770 fputc('\n', stderr
);
7774 dictionary
charinfo_dictionary(501);
7776 charinfo
*get_charinfo(symbol nm
)
7778 void *p
= charinfo_dictionary
.lookup(nm
);
7780 return (charinfo
*)p
;
7781 charinfo
*cp
= new charinfo(nm
);
7782 (void)charinfo_dictionary
.lookup(nm
, cp
);
7786 int charinfo::next_index
= 0;
7788 charinfo::charinfo(symbol s
)
7789 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
7790 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7791 not_found(0), transparent_translate(1), translate_input(0),
7792 mode(CHAR_NORMAL
), nm(s
)
7794 index
= next_index
++;
7797 void charinfo::set_hyphenation_code(unsigned char c
)
7799 hyphenation_code
= c
;
7802 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
7806 if (hyphenation_code
!= 0)
7807 ci
->set_hyphenation_code(hyphenation_code
);
7808 if (asciify_code
!= 0)
7809 ci
->set_asciify_code(asciify_code
);
7810 else if (ascii_code
!= 0)
7811 ci
->set_asciify_code(ascii_code
);
7812 ci
->set_translation_input();
7814 special_translation
= TRANSLATE_NONE
;
7815 transparent_translate
= tt
;
7818 void charinfo::set_special_translation(int c
, int tt
)
7820 special_translation
= c
;
7822 transparent_translate
= tt
;
7825 void charinfo::set_ascii_code(unsigned char c
)
7830 void charinfo::set_asciify_code(unsigned char c
)
7835 macro
*charinfo::set_macro(macro
*m
)
7842 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
7850 void charinfo::set_number(int n
)
7856 int charinfo::get_number()
7858 assert(flags
& NUMBERED
);
7862 symbol
UNNAMED_SYMBOL("---");
7864 // For numbered characters not between 0 and 255, we make a symbol out
7865 // of the number and store them in this dictionary.
7867 dictionary
numbered_charinfo_dictionary(11);
7869 charinfo
*get_charinfo_by_number(int n
)
7871 static charinfo
*number_table
[256];
7873 if (n
>= 0 && n
< 256) {
7874 charinfo
*ci
= number_table
[n
];
7876 ci
= new charinfo(UNNAMED_SYMBOL
);
7878 number_table
[n
] = ci
;
7883 symbol
ns(i_to_a(n
));
7884 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
7886 ci
= new charinfo(UNNAMED_SYMBOL
);
7888 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
7894 int font::name_to_index(const char *nm
)
7898 ci
= charset_table
[nm
[0] & 0xff];
7899 else if (nm
[0] == '\\' && nm
[2] == 0)
7900 ci
= get_charinfo(symbol(nm
+ 1));
7902 ci
= get_charinfo(symbol(nm
));
7906 return ci
->get_index();
7909 int font::number_to_index(int n
)
7911 return get_charinfo_by_number(n
)->get_index();