2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include "dictionary.h"
33 #include "searchpath.h"
34 #include "macropath.h"
37 // Needed for getpid().
43 #else /* not ISATTY_MISSING */
48 #endif /* not isatty */
49 #endif /* not ISATTY_MISSING */
51 #define USAGE_EXIT_CODE 1
52 #define MACRO_PREFIX "tmac."
53 #define STARTUP_FILE "troffrc"
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
66 void init_column_requests();
69 static node
*read_draw_node();
70 void handle_first_page_transition();
71 static void push_token(const token
&);
77 void transparent_file();
79 const char *program_name
= 0;
82 static int backtrace_flag
= 0;
84 char *pipe_command
= 0;
86 charinfo
*charset_table
[256];
88 static int warning_mask
= DEFAULT_WARNING_MASK
;
89 static int inhibit_errors
= 0;
90 static int ignoring
= 0;
92 static void enable_warning(const char *);
93 static void disable_warning(const char *);
95 static int escape_char
= '\\';
96 static symbol end_macro_name
;
97 static symbol blank_line_macro_name
;
98 static int compatible_flag
= 0;
99 int ascii_output_flag
= 0;
100 int suppress_output_flag
= 0;
102 int tcommand_flag
= 0;
104 static int get_copy(node
**, int = 0);
105 static void copy_mode_error(const char *,
106 const errarg
& = empty_errarg
,
107 const errarg
& = empty_errarg
,
108 const errarg
& = empty_errarg
);
110 static symbol
read_escape_name();
111 static void interpolate_string(symbol
);
112 static void interpolate_macro(symbol
);
113 static void interpolate_number_format(symbol
);
114 static void interpolate_environment_variable(symbol
);
116 static void interpolate_arg(symbol
);
117 static request_or_macro
*lookup_request(symbol
);
118 static int get_delim_number(units
*, int);
119 static int get_delim_number(units
*, int, units
);
120 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
121 static int read_size(int *);
122 static symbol
get_delim_name();
123 static void init_registers();
124 static void trapping_blank_line();
126 struct input_iterator
;
127 input_iterator
*make_temp_iterator(const char *);
128 const char *input_char_description(int);
130 const int ESCAPE_QUESTION
= 015;
131 const int BEGIN_TRAP
= 016;
132 const int END_TRAP
= 017;
133 const int PAGE_EJECTOR
= 020;
134 const int ESCAPE_NEWLINE
= 021;
135 const int ESCAPE_AMPERSAND
= 022;
136 const int ESCAPE_UNDERSCORE
= 023;
137 const int ESCAPE_BAR
= 024;
138 const int ESCAPE_CIRCUMFLEX
= 025;
139 const int ESCAPE_LEFT_BRACE
= 026;
140 const int ESCAPE_RIGHT_BRACE
= 027;
141 const int ESCAPE_LEFT_QUOTE
= 030;
142 const int ESCAPE_RIGHT_QUOTE
= 031;
143 const int ESCAPE_HYPHEN
= 032;
144 const int ESCAPE_BANG
= 033;
145 const int ESCAPE_c
= 034;
146 const int ESCAPE_e
= 035;
147 const int ESCAPE_PERCENT
= 036;
148 const int ESCAPE_SPACE
= 037;
150 const int TITLE_REQUEST
= 0200;
151 const int COPY_FILE_REQUEST
= 0201;
152 const int TRANSPARENT_FILE_REQUEST
= 0202;
154 const int VJUSTIFY_REQUEST
= 0203;
156 const int ESCAPE_E
= 0204;
157 const int LAST_PAGE_EJECTOR
= 0205;
158 const int ESCAPE_RIGHT_PARENTHESIS
= 0206;
160 void set_escape_char()
164 error("bad escape character");
168 escape_char
= tok
.ch();
181 class input_iterator
{
184 virtual ~input_iterator();
186 friend class input_stack
;
188 const unsigned char *ptr
;
189 const unsigned char *eptr
;
190 input_iterator
*next
;
192 virtual int fill(node
**);
194 virtual int has_args() { return 0; }
195 virtual int nargs() { return 0; }
196 virtual input_iterator
*get_arg(int) { return NULL
; }
197 virtual int get_location(int, const char **, int *)
199 virtual void backtrace() {}
200 virtual int set_location(const char *, int)
202 virtual int next_file(FILE *, const char *) { return 0; }
203 virtual void shift(int) {}
204 virtual int is_boundary();
205 virtual int internal_level() { return 0; }
206 virtual int is_file() { return 0; }
209 input_iterator::input_iterator()
214 input_iterator::~input_iterator()
218 int input_iterator::fill(node
**)
223 int input_iterator::peek()
228 int input_iterator::is_boundary()
233 inline int input_iterator::get(node
**p
)
235 return ptr
< eptr
? *ptr
++ : fill(p
);
239 class input_boundary
: public input_iterator
{
241 int is_boundary() { return 1; }
244 class file_iterator
: public input_iterator
{
247 const char *filename
;
250 enum { BUF_SIZE
= 512 };
251 unsigned char buf
[BUF_SIZE
];
254 file_iterator(FILE *, const char *, int = 0);
258 int get_location(int, const char **, int *);
260 int set_location(const char *, int);
261 int next_file(FILE *, const char *);
265 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
266 : fp(f
), filename(fn
), lineno(1), newline_flag(0), popened(po
)
270 file_iterator::~file_iterator()
275 void file_iterator::close()
279 #ifndef POPEN_MISSING
282 #endif /* not POPEN_MISSING */
287 int file_iterator::is_file()
292 int file_iterator::next_file(FILE *f
, const char *s
)
305 int file_iterator::fill(node
**)
310 unsigned char *p
= buf
;
312 unsigned char *e
= p
+ BUF_SIZE
;
317 if (illegal_input_char(c
))
318 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
337 int file_iterator::peek()
340 while (illegal_input_char(c
)) {
341 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
349 int file_iterator::get_location(int /*allow_macro*/,
350 const char **filenamep
, int *linenop
)
353 if (filename
!= 0 && strcmp(filename
, "-") == 0)
354 *filenamep
= "<standard input>";
356 *filenamep
= filename
;
360 void file_iterator::backtrace()
362 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
363 popened
? "process" : "file");
366 int file_iterator::set_location(const char *f
, int ln
)
374 input_iterator nil_iterator
;
378 static int get(node
**);
380 static void push(input_iterator
*);
381 static input_iterator
*get_arg(int);
383 static int get_location(int, const char **, int *);
384 static int set_location(const char *, int);
385 static void backtrace();
386 static void backtrace_all();
387 static void next_file(FILE *, const char *);
388 static void end_file();
389 static void shift(int n
);
390 static void add_boundary();
391 static void remove_boundary();
392 static int get_level();
397 static input_iterator
*top
;
400 static int finish_get(node
**);
401 static int finish_peek();
404 input_iterator
*input_stack::top
= &nil_iterator
;
405 int input_stack::level
= 0;
406 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
408 inline int input_stack::get_level()
410 return level
+ top
->internal_level();
413 inline int input_stack::get(node
**np
)
415 return (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
418 int input_stack::finish_get(node
**np
)
421 int c
= top
->fill(np
);
422 if (c
!= EOF
|| top
->is_boundary())
424 if (top
== &nil_iterator
)
426 input_iterator
*tem
= top
;
430 if (top
->ptr
< top
->eptr
)
437 inline int input_stack::peek()
439 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
442 int input_stack::finish_peek()
446 if (c
!= EOF
|| top
->is_boundary())
448 if (top
== &nil_iterator
)
450 input_iterator
*tem
= top
;
454 if (top
->ptr
< top
->eptr
)
461 void input_stack::add_boundary()
463 push(new input_boundary
);
466 void input_stack::remove_boundary()
468 assert(top
->is_boundary());
469 input_iterator
*temp
= top
->next
;
475 void input_stack::push(input_iterator
*in
)
479 if (++level
> limit
&& limit
> 0)
480 fatal("input stack limit exceeded (probable infinite loop)");
485 input_iterator
*input_stack::get_arg(int i
)
488 for (p
= top
; p
!= NULL
; p
= p
->next
)
490 return p
->get_arg(i
);
494 void input_stack::shift(int n
)
496 for (input_iterator
*p
= top
; p
; p
= p
->next
)
503 int input_stack::nargs()
505 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
511 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
513 for (input_iterator
*p
= top
; p
; p
= p
->next
)
514 if (p
->get_location(allow_macro
, filenamep
, linenop
))
519 void input_stack::backtrace()
523 // only backtrace down to (not including) the topmost file
524 for (input_iterator
*p
= top
;
525 p
&& !p
->get_location(0, &f
, &n
);
530 void input_stack::backtrace_all()
532 for (input_iterator
*p
= top
; p
; p
= p
->next
)
536 int input_stack::set_location(const char *filename
, int lineno
)
538 for (input_iterator
*p
= top
; p
; p
= p
->next
)
539 if (p
->set_location(filename
, lineno
))
544 void input_stack::next_file(FILE *fp
, const char *s
)
547 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
548 if ((*pp
)->next_file(fp
, s
))
550 if (++level
> limit
&& limit
> 0)
551 fatal("input stack limit exceeded");
552 *pp
= new file_iterator(fp
, s
);
553 (*pp
)->next
= &nil_iterator
;
556 void input_stack::end_file()
558 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
559 if ((*pp
)->is_file()) {
560 input_iterator
*tem
= *pp
;
568 void input_stack::clear()
571 while (top
!= &nil_iterator
) {
572 if (top
->is_boundary())
574 input_iterator
*tem
= top
;
579 // Keep while_request happy.
580 for (; nboundaries
> 0; --nboundaries
)
584 void backtrace_request()
586 input_stack::backtrace_all();
593 symbol nm
= get_long_name(0);
594 while (!tok
.newline() && !tok
.eof())
597 input_stack::end_file();
600 FILE *fp
= fopen(nm
.contents(), "r");
602 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
604 input_stack::next_file(fp
, nm
.contents());
612 if (!has_arg() || !get_integer(&n
))
614 input_stack::shift(n
);
618 static int get_char_for_escape_name()
620 int c
= get_copy(NULL
);
623 copy_mode_error("end of input in escape name");
626 if (!illegal_input_char(c
))
631 input_stack::push(make_temp_iterator("\n"));
636 copy_mode_error("%1 is not allowed in an escape name",
637 input_char_description(c
));
643 static symbol
read_two_char_escape_name()
646 buf
[0] = get_char_for_escape_name();
647 if (buf
[0] != '\0') {
648 buf
[1] = get_char_for_escape_name();
657 static symbol
read_long_escape_name()
659 int start_level
= input_stack::get_level();
660 char abuf
[ABUF_SIZE
];
662 int buf_size
= ABUF_SIZE
;
665 int c
= get_char_for_escape_name();
671 if (i
+ 2 > buf_size
) {
673 buf
= new char [ABUF_SIZE
*2];
674 memcpy(buf
, abuf
, buf_size
);
675 buf_size
= ABUF_SIZE
*2;
679 buf
= new char[buf_size
*2];
680 memcpy(buf
, old_buf
, buf_size
);
685 if (c
== ']' && input_stack::get_level() == start_level
)
692 copy_mode_error("empty escape name");
704 static symbol
read_escape_name()
706 int c
= get_char_for_escape_name();
710 return read_two_char_escape_name();
711 if (c
== '[' && !compatible_flag
)
712 return read_long_escape_name();
719 static symbol
read_increment_and_escape_name(int *incp
)
721 int c
= get_char_for_escape_name();
728 return read_two_char_escape_name();
731 return read_escape_name();
734 return read_escape_name();
736 if (!compatible_flag
) {
738 return read_long_escape_name();
749 static int get_copy(node
**nd
, int defining
)
752 int c
= input_stack::get(nd
);
753 if (c
== ESCAPE_NEWLINE
) {
757 c
= input_stack::get(nd
);
758 } while (c
== ESCAPE_NEWLINE
);
760 if (c
!= escape_char
|| escape_char
<= 0)
762 c
= input_stack::peek();
767 (void)input_stack::get(NULL
);
768 while ((c
= input_stack::get(NULL
)) != '\n' && c
!= EOF
)
771 case '#': // Like \" but newline is ignored.
772 (void)input_stack::get(NULL
);
773 while ((c
= input_stack::get(NULL
)) != '\n')
779 (void)input_stack::get(NULL
);
780 symbol s
= read_escape_name();
787 (void)input_stack::get(NULL
);
788 symbol s
= read_escape_name();
790 interpolate_string(s
);
794 (void)input_stack::get(NULL
);
797 (void)input_stack::get(NULL
);
800 (void)input_stack::get(NULL
);
804 (void)input_stack::get(NULL
);
806 symbol s
= read_increment_and_escape_name(&inc
);
808 interpolate_number_reg(s
, inc
);
813 (void)input_stack::get(NULL
);
814 symbol s
= read_escape_name();
816 interpolate_number_format(s
);
820 (void)input_stack::get(NULL
);
824 (void)input_stack::get(NULL
);
825 symbol s
= read_escape_name();
827 interpolate_environment_variable(s
);
831 (void)input_stack::get(NULL
);
833 return ESCAPE_NEWLINE
;
836 (void)input_stack::get(NULL
);
839 (void)input_stack::get(NULL
);
842 (void)input_stack::get(NULL
);
843 return ESCAPE_CIRCUMFLEX
;
845 (void)input_stack::get(NULL
);
846 return ESCAPE_LEFT_BRACE
;
848 (void)input_stack::get(NULL
);
849 return ESCAPE_RIGHT_BRACE
;
851 (void)input_stack::get(NULL
);
852 return ESCAPE_LEFT_QUOTE
;
854 (void)input_stack::get(NULL
);
855 return ESCAPE_RIGHT_QUOTE
;
857 (void)input_stack::get(NULL
);
858 return ESCAPE_HYPHEN
;
860 (void)input_stack::get(NULL
);
861 return ESCAPE_UNDERSCORE
;
863 (void)input_stack::get(NULL
);
866 (void)input_stack::get(NULL
);
869 (void)input_stack::get(NULL
);
870 return ESCAPE_QUESTION
;
872 (void)input_stack::get(NULL
);
873 return ESCAPE_AMPERSAND
;
875 (void)input_stack::get(NULL
);
876 return ESCAPE_RIGHT_PARENTHESIS
;
878 (void)input_stack::get(NULL
);
881 (void)input_stack::get(NULL
);
882 return ESCAPE_PERCENT
;
884 if (c
== escape_char
) {
885 (void)input_stack::get(NULL
);
894 class non_interpreted_char_node
: public node
{
897 non_interpreted_char_node(unsigned char);
899 int interpret(macro
*);
904 int non_interpreted_char_node::same(node
*nd
)
906 return c
== ((non_interpreted_char_node
*)nd
)->c
;
909 const char *non_interpreted_char_node::type()
911 return "non_interpreted_char_node";
914 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
919 node
*non_interpreted_char_node::copy()
921 return new non_interpreted_char_node(c
);
924 int non_interpreted_char_node::interpret(macro
*mac
)
930 static void do_width();
931 static node
*do_non_interpreted();
932 static node
*do_special();
933 static void do_register();
935 static node
*do_overstrike()
938 overstrike_node
*on
= new overstrike_node
;
941 while (tok
!= start
) {
942 if (tok
.newline() || tok
.eof()) {
943 warning(WARN_DELIM
, "missing closing delimiter");
946 charinfo
*ci
= tok
.get_char(1);
948 node
*n
= curenv
->make_char_node(ci
);
957 static node
*do_bracket()
960 bracket_node
*bn
= new bracket_node
;
963 while (tok
!= start
) {
965 warning(WARN_DELIM
, "missing closing delimiter");
969 warning(WARN_DELIM
, "missing closing delimiter");
970 input_stack::push(make_temp_iterator("\n"));
973 charinfo
*ci
= tok
.get_char(1);
975 node
*n
= curenv
->make_char_node(ci
);
984 static int do_name_test()
988 int start_level
= input_stack::get_level();
993 if (tok
.newline() || tok
.eof()) {
994 warning(WARN_DELIM
, "missing closing delimiter");
998 && (compatible_flag
|| input_stack::get_level() == start_level
))
1004 return some_char
&& !bad_char
;
1008 static node
*do_zero_width()
1012 int start_level
= input_stack::get_level();
1013 environment
env(curenv
);
1014 environment
*oldenv
= curenv
;
1018 if (tok
.newline() || tok
.eof()) {
1019 error("missing closing delimiter");
1023 && (compatible_flag
|| input_stack::get_level() == start_level
))
1028 node
*rev
= env
.extract_output_line();
1036 return new zero_width_node(n
);
1041 // It's undesirable for \Z to change environments, because then
1042 // \n(.w won't work as expected.
1044 static node
*do_zero_width()
1046 node
*rev
= new dummy_node
;
1049 int start_level
= input_stack::get_level();
1052 if (tok
.newline() || tok
.eof()) {
1053 warning(WARN_DELIM
, "missing closing delimiter");
1057 && (compatible_flag
|| input_stack::get_level() == start_level
))
1059 if (!tok
.add_to_node_list(&rev
))
1060 error("illegal token in argument to \\Z");
1069 return new zero_width_node(n
);
1074 token_node
*node::get_token_node()
1079 class token_node
: public node
{
1082 token_node(const token
&t
);
1084 token_node
*get_token_node();
1089 token_node::token_node(const token
&t
) : tk(t
)
1093 node
*token_node::copy()
1095 return new token_node(tk
);
1098 token_node
*token_node::get_token_node()
1103 int token_node::same(node
*nd
)
1105 return tk
== ((token_node
*)nd
)->tk
;
1108 const char *token_node::type()
1110 return "token_node";
1113 token::token() : nd(0), type(TOKEN_EMPTY
)
1122 token::token(const token
&t
)
1123 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1125 // Use two statements to work around bug in SGI C++.
1127 nd
= tem
? tem
->copy() : 0;
1130 void token::operator=(const token
&t
)
1134 // Use two statements to work around bug in SGI C++.
1136 nd
= tem
? tem
->copy() : 0;
1153 return !tok
.newline();
1156 void token::make_space()
1161 void token::make_newline()
1163 type
= TOKEN_NEWLINE
;
1175 int cc
= input_stack::get(&n
);
1176 if (cc
!= escape_char
|| escape_char
== 0) {
1182 case TRANSPARENT_FILE_REQUEST
:
1184 case COPY_FILE_REQUEST
:
1186 case VJUSTIFY_REQUEST
:
1188 type
= TOKEN_REQUEST
;
1192 type
= TOKEN_BEGIN_TRAP
;
1195 type
= TOKEN_END_TRAP
;
1197 case LAST_PAGE_EJECTOR
:
1198 seen_last_page_ejector
= 1;
1201 type
= TOKEN_PAGE_EJECTOR
;
1203 case ESCAPE_PERCENT
:
1205 type
= TOKEN_HYPHEN_INDICATOR
;
1210 nd
= new space_char_hmotion_node(curenv
->get_space_width());
1214 type
= TOKEN_ESCAPE
;
1217 goto handle_escape_char
;
1221 nd
= new hmotion_node(curenv
->get_narrow_space_width());
1223 case ESCAPE_CIRCUMFLEX
:
1226 nd
= new hmotion_node(curenv
->get_half_narrow_space_width());
1228 case ESCAPE_NEWLINE
:
1230 case ESCAPE_LEFT_BRACE
:
1232 type
= TOKEN_LEFT_BRACE
;
1234 case ESCAPE_RIGHT_BRACE
:
1236 type
= TOKEN_RIGHT_BRACE
;
1238 case ESCAPE_LEFT_QUOTE
:
1240 type
= TOKEN_SPECIAL
;
1243 case ESCAPE_RIGHT_QUOTE
:
1245 type
= TOKEN_SPECIAL
;
1250 type
= TOKEN_SPECIAL
;
1253 case ESCAPE_UNDERSCORE
:
1255 type
= TOKEN_SPECIAL
;
1260 type
= TOKEN_INTERRUPT
;
1264 type
= TOKEN_TRANSPARENT
;
1266 case ESCAPE_QUESTION
:
1268 nd
= do_non_interpreted();
1274 case ESCAPE_AMPERSAND
:
1278 case ESCAPE_RIGHT_PARENTHESIS
:
1279 ESCAPE_RIGHT_PARENTHESIS
:
1281 nd
= new transparent_dummy_node
;
1284 type
= TOKEN_BACKSPACE
;
1293 type
= TOKEN_NEWLINE
;
1296 type
= TOKEN_LEADER
;
1301 token_node
*tn
= n
->get_token_node();
1320 cc
= input_stack::get(NULL
);
1323 nm
= read_two_char_escape_name();
1324 type
= TOKEN_SPECIAL
;
1328 error("end of input after escape character");
1331 goto ESCAPE_LEFT_QUOTE
;
1333 goto ESCAPE_RIGHT_QUOTE
;
1337 goto ESCAPE_UNDERSCORE
;
1339 goto ESCAPE_PERCENT
;
1343 nd
= new hmotion_node(curenv
->get_digit_width());
1349 goto ESCAPE_CIRCUMFLEX
;
1351 type
= TOKEN_ITALIC_CORRECTION
;
1355 nd
= new left_italic_corrected_node
;
1358 goto ESCAPE_AMPERSAND
;
1360 goto ESCAPE_RIGHT_PARENTHESIS
;
1364 goto ESCAPE_QUESTION
;
1366 nd
= new unbreakable_space_node(curenv
->get_space_width());
1370 while ((cc
= input_stack::get(NULL
)) != '\n' && cc
!= EOF
)
1373 type
= TOKEN_NEWLINE
;
1377 case '#': // Like \" but newline is ignored.
1378 while ((cc
= input_stack::get(NULL
)) != '\n')
1386 symbol nm
= read_escape_name();
1388 interpolate_arg(nm
);
1393 symbol nm
= read_escape_name();
1395 interpolate_string(nm
);
1399 nd
= new non_interpreted_char_node('\001');
1403 c
= '0' + do_name_test();
1413 nm
= get_delim_name();
1416 type
= TOKEN_SPECIAL
;
1420 nd
= new vmotion_node(curenv
->get_size()/2);
1423 nd
= read_draw_node();
1431 goto handle_escape_char
;
1434 symbol s
= read_escape_name();
1438 for (p
= s
.contents(); *p
!= '\0'; p
++)
1442 curenv
->set_font(s
);
1444 curenv
->set_font(atoi(s
.contents()));
1449 symbol s
= read_escape_name();
1451 interpolate_number_format(s
);
1455 if (!get_delim_number(&x
, 'm'))
1458 nd
= new hmotion_node(x
);
1461 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1462 curenv
->set_char_height(x
);
1465 nm
= read_escape_name();
1468 type
= TOKEN_MARK_INPUT
;
1474 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1477 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1479 node
*n
= curenv
->make_char_node(s
);
1481 nd
= new hline_node(x
, n
);
1483 nd
= new vline_node(x
, n
);
1489 symbol nm
= read_increment_and_escape_name(&inc
);
1491 interpolate_number_reg(nm
, inc
);
1495 if (!get_delim_number(&val
, 0))
1497 type
= TOKEN_NUMBERED_CHAR
;
1500 nd
= do_overstrike();
1504 type
= TOKEN_SPREAD
;
1508 nd
= new vmotion_node(-curenv
->get_size());
1515 curenv
->set_size(x
);
1518 if (get_delim_number(&x
, 0))
1519 curenv
->set_char_slant(x
);
1523 nd
= new non_interpreted_char_node('\t');
1527 nd
= new vmotion_node(-curenv
->get_size()/2);
1530 if (!get_delim_number(&x
, 'v'))
1533 nd
= new vmotion_node(x
);
1537 symbol nm
= read_escape_name();
1539 interpolate_environment_variable(nm
);
1546 if (!get_delim_number(&x
, 'v'))
1549 nd
= new extra_size_node(x
);
1559 symbol s
= read_escape_name();
1562 request_or_macro
*p
= lookup_request(s
);
1563 macro
*m
= p
->to_macro();
1565 error("can't transparently throughput a request");
1568 nd
= new special_node(*m
);
1575 if (type
== TOKEN_NODE
)
1576 nd
= new zero_width_node(nd
);
1578 charinfo
*ci
= get_char(1);
1581 node
*gn
= curenv
->make_char_node(ci
);
1584 nd
= new zero_width_node(gn
);
1590 nd
= do_zero_width();
1596 goto ESCAPE_LEFT_BRACE
;
1598 goto ESCAPE_RIGHT_BRACE
;
1602 if (!compatible_flag
) {
1603 nm
= read_long_escape_name();
1606 type
= TOKEN_SPECIAL
;
1609 goto handle_normal_char
;
1611 if (cc
!= escape_char
&& cc
!= '.')
1612 warning(WARN_ESCAPE
, "escape character ignored before %1",
1613 input_char_description(cc
));
1614 goto handle_normal_char
;
1620 int token::operator==(const token
&t
)
1629 case TOKEN_NUMBERED_CHAR
:
1630 return val
== t
.val
;
1636 int token::operator!=(const token
&t
)
1638 return !(*this == t
);
1641 // is token a suitable delimiter (like ')?
1643 int token::delimiter(int err
)
1672 error("cannot use character `%1' as a starting delimiter", char(c
));
1682 error("cannot use %1 as a starting delimiter", description());
1689 const char *token::description()
1693 case TOKEN_BACKSPACE
:
1694 return "a backspace character";
1705 case TOKEN_HYPHEN_INDICATOR
:
1707 case TOKEN_INTERRUPT
:
1709 case TOKEN_ITALIC_CORRECTION
:
1712 return "a leader character";
1713 case TOKEN_LEFT_BRACE
:
1715 case TOKEN_MARK_INPUT
:
1721 case TOKEN_NUMBERED_CHAR
:
1723 case TOKEN_RIGHT_BRACE
:
1728 return "a special character";
1732 return "a tab character";
1733 case TOKEN_TRANSPARENT
:
1736 return "end of input";
1740 return "a magic token";
1745 while (!tok
.newline())
1756 if (has_arg() && get_integer(&n
))
1757 compatible_flag
= n
!= 0;
1759 compatible_flag
= 1;
1763 static void empty_name_warning(int required
)
1765 if (tok
.newline() || tok
.eof()) {
1767 warning(WARN_MISSING
, "missing name");
1769 else if (tok
.right_brace() || tok
.tab()) {
1770 const char *start
= tok
.description();
1773 } while (tok
.space() || tok
.right_brace() || tok
.tab());
1774 if (!tok
.newline() && !tok
.eof())
1775 error("%1 is not allowed before an argument", start
);
1777 warning(WARN_MISSING
, "missing name");
1780 error("name expected (got %1)", tok
.description());
1782 error("name expected (got %1): treated as missing", tok
.description());
1785 static void non_empty_name_warning()
1787 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
1788 && !tok
.right_brace()
1789 // We don't want to give a warning for .el\{
1790 && !tok
.left_brace())
1791 error("%1 is not allowed in a name", tok
.description());
1794 symbol
get_name(int required
)
1796 if (compatible_flag
) {
1799 if ((buf
[0] = tok
.ch()) != 0) {
1801 if ((buf
[1] = tok
.ch()) != 0) {
1806 non_empty_name_warning();
1810 empty_name_warning(required
);
1815 return get_long_name(required
);
1818 symbol
get_long_name(int required
)
1822 char abuf
[ABUF_SIZE
];
1824 int buf_size
= ABUF_SIZE
;
1827 if (i
+ 1 > buf_size
) {
1829 buf
= new char [ABUF_SIZE
*2];
1830 memcpy(buf
, abuf
, buf_size
);
1831 buf_size
= ABUF_SIZE
*2;
1834 char *old_buf
= buf
;
1835 buf
= new char[buf_size
*2];
1836 memcpy(buf
, old_buf
, buf_size
);
1841 if ((buf
[i
] = tok
.ch()) == 0)
1847 empty_name_warning(required
);
1850 non_empty_name_warning();
1863 topdiv
->set_last_page();
1864 if (!end_macro_name
.is_null()) {
1865 spring_trap(end_macro_name
);
1867 process_input_stack();
1869 curenv
->final_break();
1871 process_input_stack();
1874 topdiv
->set_ejecting();
1875 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
1876 input_stack::push(make_temp_iterator((char *)buf
));
1877 topdiv
->space(topdiv
->get_page_length(), 1);
1879 process_input_stack();
1880 seen_last_page_ejector
= 1; // should be set already
1881 topdiv
->set_ejecting();
1882 push_page_ejector();
1883 topdiv
->space(topdiv
->get_page_length(), 1);
1885 process_input_stack();
1886 // This will only happen if a trap-invoked macro starts a diversion,
1887 // or if vertical position traps have been disabled.
1888 cleanup_and_exit(0);
1891 // This implements .ex. The input stack must be cleared before calling
1896 input_stack::clear();
1905 end_macro_name
= get_name();
1909 void blank_line_macro()
1911 blank_line_macro_name
= get_name();
1915 static void trapping_blank_line()
1917 if (!blank_line_macro_name
.is_null())
1918 spring_trap(blank_line_macro_name
);
1925 int saved_compatible_flag
= compatible_flag
;
1926 compatible_flag
= 0;
1927 symbol nm
= get_name();
1931 interpolate_macro(nm
);
1932 compatible_flag
= saved_compatible_flag
;
1935 inline int possibly_handle_first_page_transition()
1937 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
1938 handle_first_page_transition();
1945 static int transparent_translate(int cc
)
1947 if (!illegal_input_char(cc
)) {
1948 charinfo
*ci
= charset_table
[cc
];
1949 switch (ci
->get_special_translation(1)) {
1950 case charinfo::TRANSLATE_SPACE
:
1952 case charinfo::TRANSLATE_DUMMY
:
1953 return ESCAPE_AMPERSAND
;
1954 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
1955 return ESCAPE_PERCENT
;
1957 // This is realy ugly.
1958 ci
= ci
->get_translation(1);
1960 int c
= ci
->get_ascii_code();
1963 error("can't translate %1 to special character `%2'"
1964 " in transparent throughput",
1965 input_char_description(cc
),
1973 struct int_stack_element
{
1975 int_stack_element
*next
;
1985 int_stack::int_stack()
1990 int_stack::~int_stack()
1993 int_stack_element
*temp
= top
;
2000 int int_stack::is_empty()
2005 void int_stack::push(int n
)
2007 int_stack_element
*p
= new int_stack_element
;
2014 int int_stack::pop()
2017 int_stack_element
*p
= top
;
2024 int node::reread(int *)
2029 int diverted_space_node::reread(int *bolp
)
2031 if (curenv
->get_fill())
2032 trapping_blank_line();
2039 int diverted_copy_file_node::reread(int *bolp
)
2041 curdiv
->copy_file(filename
.contents());
2046 void process_input_stack()
2048 int_stack trap_bol_stack
;
2051 int suppress_next
= 0;
2053 case token::TOKEN_CHAR
:
2055 unsigned char ch
= tok
.c
;
2057 (ch
== curenv
->control_char
2058 || ch
== curenv
->no_break_control_char
)) {
2059 break_flag
= ch
== curenv
->control_char
;
2060 // skip tabs as well as spaces here
2063 } while (tok
.white_space());
2064 symbol nm
= get_name();
2068 interpolate_macro(nm
);
2072 if (possibly_handle_first_page_transition())
2076 curenv
->add_char(charset_table
[ch
]);
2078 if (tok
.type
!= token::TOKEN_CHAR
)
2088 case token::TOKEN_TRANSPARENT
:
2091 if (possibly_handle_first_page_transition())
2100 curdiv
->transparent_output(transparent_translate(cc
));
2102 curdiv
->transparent_output(n
);
2103 } while (cc
!= '\n' && cc
!= EOF
);
2105 curdiv
->transparent_output('\n');
2110 case token::TOKEN_NEWLINE
:
2112 if (bol
&& !curenv
->get_prev_line_interrupted())
2113 trapping_blank_line();
2120 case token::TOKEN_REQUEST
:
2122 int request_code
= tok
.c
;
2124 switch (request_code
) {
2128 case COPY_FILE_REQUEST
:
2131 case TRANSPARENT_FILE_REQUEST
:
2135 case VJUSTIFY_REQUEST
:
2146 case token::TOKEN_SPACE
:
2148 if (possibly_handle_first_page_transition())
2150 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2153 nspaces
+= tok
.nspaces();
2155 } while (tok
.space());
2157 trapping_blank_line();
2161 curenv
->add_node(new hmotion_node(curenv
->get_space_width()*nspaces
));
2171 case token::TOKEN_EOF
:
2173 case token::TOKEN_NODE
:
2175 if (possibly_handle_first_page_transition())
2177 else if (tok
.nd
->reread(&bol
)) {
2182 curenv
->add_node(tok
.nd
);
2188 case token::TOKEN_PAGE_EJECTOR
:
2190 continue_page_eject();
2191 // I think we just want to preserve bol.
2195 case token::TOKEN_BEGIN_TRAP
:
2197 trap_bol_stack
.push(bol
);
2201 case token::TOKEN_END_TRAP
:
2203 if (trap_bol_stack
.is_empty())
2204 error("spurious end trap token detected!");
2206 bol
= trap_bol_stack
.pop();
2208 /* I'm not totally happy about this. But I can't think of any other
2209 way to do it. Doing an output_pending_lines() whenever a
2210 TOKEN_END_TRAP is detected doesn't work: for example,
2223 a\%very\%very\%long\%word
2225 will print all but the first lines from the word immediately
2226 after the footer, rather than on the next page. */
2228 if (trap_bol_stack
.is_empty())
2229 curenv
->output_pending_lines();
2241 trap_sprung_flag
= 0;
2245 #ifdef WIDOW_CONTROL
2247 void flush_pending_lines()
2249 while (!tok
.newline() && !tok
.eof())
2251 curenv
->output_pending_lines();
2255 #endif /* WIDOW_CONTROL */
2257 request_or_macro::request_or_macro()
2261 macro
*request_or_macro::to_macro()
2266 request::request(REQUEST_FUNCP pp
) : p(pp
)
2270 void request::invoke(symbol
)
2276 enum { SIZE
= 128 };
2277 unsigned char s
[SIZE
];
2282 char_block::char_block()
2291 void append(unsigned char);
2298 friend class macro_header
;
2299 friend class string_iterator
;
2302 char_list::char_list()
2303 : head(0), tail(0), ptr(0), len(0)
2307 char_list::~char_list()
2310 char_block
*tem
= head
;
2316 int char_list::length()
2321 void char_list::append(unsigned char c
)
2324 head
= tail
= new char_block
;
2328 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2329 tail
->next
= new char_block
;
2344 void append(node
*);
2348 friend class macro_header
;
2349 friend class string_iterator
;
2352 void node_list::append(node
*n
)
2360 tail
= tail
->next
= n
;
2364 int node_list::length()
2367 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2372 node_list::node_list()
2377 node
*node_list::extract()
2385 node_list::~node_list()
2387 delete_node_list(head
);
2390 struct macro_header
{
2394 macro_header() { count
= 1; }
2395 macro_header
*copy(int);
2401 if (p
!= 0 && --(p
->count
) <= 0)
2407 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2415 macro::macro(const macro
&m
)
2416 : filename(m
.filename
), lineno(m
.lineno
), p(m
.p
), length(m
.length
)
2422 macro
¯o::operator=(const macro
&m
)
2424 // don't assign object
2427 if (p
!= 0 && --(p
->count
) <= 0)
2430 filename
= m
.filename
;
2436 void macro::append(unsigned char c
)
2440 p
= new macro_header
;
2441 if (p
->cl
.length() != length
) {
2442 macro_header
*tem
= p
->copy(length
);
2443 if (--(p
->count
) <= 0)
2451 void macro::append(node
*n
)
2455 p
= new macro_header
;
2456 if (p
->cl
.length() != length
) {
2457 macro_header
*tem
= p
->copy(length
);
2458 if (--(p
->count
) <= 0)
2467 void macro::print_size()
2469 errprint("%1", length
);
2472 // make a copy of the first n bytes
2474 macro_header
*macro_header::copy(int n
)
2476 macro_header
*p
= new macro_header
;
2477 char_block
*bp
= cl
.head
;
2478 unsigned char *ptr
= bp
->s
;
2481 if (ptr
>= bp
->s
+ char_block::SIZE
) {
2488 p
->nl
.append(nd
->copy());
2497 object_dictionary_iterator
iter(request_dictionary
);
2498 request_or_macro
*rm
;
2500 while (iter
.get(&s
, (object
**)&rm
)) {
2501 assert(!s
.is_null());
2502 macro
*m
= rm
->to_macro();
2504 errprint("%1\t", s
.contents());
2513 class string_iterator
: public input_iterator
{
2515 const char *how_invoked
;
2519 int count
; // of characters remaining
2525 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
2528 int get_location(int, const char **, int *);
2532 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
2533 : lineno(1), mac(m
), newline_flag(0), how_invoked(p
), nm(s
)
2537 bp
= mac
.p
->cl
.head
;
2538 nd
= mac
.p
->nl
.head
;
2548 string_iterator::string_iterator()
2559 int string_iterator::fill(node
**np
)
2566 const unsigned char *p
= eptr
;
2567 if (p
>= bp
->s
+ char_block::SIZE
) {
2579 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
2584 unsigned char c
= *p
;
2585 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
2599 int string_iterator::peek()
2603 const unsigned char *p
= eptr
;
2606 if (p
>= bp
->s
+ char_block::SIZE
) {
2612 int string_iterator::get_location(int allow_macro
,
2613 const char **filep
, int *linep
)
2617 if (mac
.filename
== 0)
2619 *filep
= mac
.filename
;
2620 *linep
= mac
.lineno
+ lineno
- 1;
2624 void string_iterator::backtrace()
2627 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
2630 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
2632 errprint(": %1\n", how_invoked
);
2639 class temp_iterator
: public input_iterator
{
2640 unsigned char *base
;
2641 temp_iterator(const char *, int len
);
2644 friend input_iterator
*make_temp_iterator(const char *);
2650 temp_iterator::temp_iterator(const char *s
, int len
)
2652 base
= new unsigned char[len
];
2653 memcpy(base
, s
, len
);
2658 temp_iterator::~temp_iterator()
2663 class small_temp_iterator
: public input_iterator
{
2665 small_temp_iterator(const char *, int);
2666 ~small_temp_iterator();
2667 enum { BLOCK
= 16 };
2668 static small_temp_iterator
*free_list
;
2669 void *operator new(size_t);
2670 void operator delete(void *);
2672 unsigned char buf
[SIZE
];
2673 friend input_iterator
*make_temp_iterator(const char *);
2676 small_temp_iterator
*small_temp_iterator::free_list
= 0;
2678 void *small_temp_iterator::operator new(size_t n
)
2680 assert(n
== sizeof(small_temp_iterator
));
2682 free_list
= (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
2683 for (int i
= 0; i
< BLOCK
- 1; i
++)
2684 free_list
[i
].next
= free_list
+ i
+ 1;
2685 free_list
[BLOCK
-1].next
= 0;
2687 small_temp_iterator
*p
= free_list
;
2688 free_list
= (small_temp_iterator
*)(free_list
->next
);
2696 void small_temp_iterator::operator delete(void *p
)
2699 ((small_temp_iterator
*)p
)->next
= free_list
;
2700 free_list
= (small_temp_iterator
*)p
;
2704 small_temp_iterator::~small_temp_iterator()
2712 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
2714 for (int i
= 0; i
< len
; i
++)
2720 input_iterator
*make_temp_iterator(const char *s
)
2723 return new small_temp_iterator(s
, 0);
2726 if (n
<= small_temp_iterator::SIZE
)
2727 return new small_temp_iterator(s
, n
);
2729 return new temp_iterator(s
, n
);
2733 // this is used when macros are interpolated using the .macro_name notation
2738 arg_list(const macro
&);
2742 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
2746 arg_list::~arg_list()
2750 class macro_iterator
: public string_iterator
{
2754 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
2757 int has_args() { return 1; }
2758 input_iterator
*get_arg(int i
);
2759 int nargs() { return argc
; }
2760 void add_arg(const macro
&m
);
2764 input_iterator
*macro_iterator::get_arg(int i
)
2767 return make_temp_iterator(nm
.contents());
2768 if (i
> 0 && i
<= argc
) {
2770 for (int j
= 1; j
< i
; j
++) {
2774 return new string_iterator(p
->mac
);
2780 void macro_iterator::add_arg(const macro
&m
)
2783 for (p
= &args
; *p
; p
= &((*p
)->next
))
2785 *p
= new arg_list(m
);
2789 void macro_iterator::shift(int n
)
2791 while (n
> 0 && argc
> 0) {
2792 arg_list
*tem
= args
;
2800 // This gets used by eg .if '\?xxx\?''.
2802 int operator==(const macro
&m1
, const macro
&m2
)
2804 if (m1
.length
!= m2
.length
)
2806 string_iterator
iter1(m1
);
2807 string_iterator
iter2(m2
);
2811 int c1
= iter1
.get(&nd1
);
2814 int c2
= iter2
.get(&nd2
);
2826 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
2836 static void interpolate_macro(symbol nm
)
2838 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
2841 const char *s
= nm
.contents();
2842 if (strlen(s
) > 2) {
2843 request_or_macro
*r
;
2848 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
2850 macro
*m
= r
->to_macro();
2851 if (!m
|| !m
->empty())
2852 warned
= warning(WARN_SPACE
,
2853 "`%1' not defined (probable missing space after `%2')",
2854 nm
.contents(), buf
);
2858 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
2860 request_dictionary
.define(nm
, p
);
2871 static void decode_args(macro_iterator
*mi
)
2873 if (!tok
.newline() && !tok
.eof()) {
2875 int c
= get_copy(&n
);
2879 if (c
== '\n' || c
== EOF
)
2882 int quote_input_level
= 0;
2883 int done_tab_warning
= 0;
2885 quote_input_level
= input_stack::get_level();
2888 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
2889 if (quote_input_level
> 0 && c
== '\"'
2891 || input_stack::get_level() == quote_input_level
)) {
2904 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
2905 warning(WARN_TAB
, "tab character in unquoted macro argument");
2906 done_tab_warning
= 1;
2918 void macro::invoke(symbol nm
)
2920 macro_iterator
*mi
= new macro_iterator(nm
, *this);
2922 input_stack::push(mi
);
2926 macro
*macro::to_macro()
2936 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
2937 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
2941 macro_iterator::macro_iterator() : args(0), argc(0)
2945 macro_iterator::~macro_iterator()
2948 arg_list
*tem
= args
;
2954 int trap_sprung_flag
= 0;
2955 int postpone_traps_flag
= 0;
2956 symbol postponed_trap
;
2958 void spring_trap(symbol nm
)
2960 assert(!nm
.is_null());
2961 trap_sprung_flag
= 1;
2962 if (postpone_traps_flag
) {
2963 postponed_trap
= nm
;
2966 static char buf
[2] = { BEGIN_TRAP
, 0 };
2967 static char buf2
[2] = { END_TRAP
, '\0' };
2968 input_stack::push(make_temp_iterator(buf2
));
2969 request_or_macro
*p
= lookup_request(nm
);
2970 macro
*m
= p
->to_macro();
2972 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
2974 error("you can't invoke a request with a trap");
2975 input_stack::push(make_temp_iterator(buf
));
2978 void postpone_traps()
2980 postpone_traps_flag
= 1;
2983 int unpostpone_traps()
2985 postpone_traps_flag
= 0;
2986 if (!postponed_trap
.is_null()) {
2987 spring_trap(postponed_trap
);
2988 postponed_trap
= NULL_SYMBOL
;
2997 macro_iterator
*mi
= new macro_iterator
;
2998 int reading_from_terminal
= isatty(fileno(stdin
));
3000 if (!tok
.newline() && !tok
.eof()) {
3001 int c
= get_copy(NULL
);
3004 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3005 if (!illegal_input_char(c
)) {
3006 if (reading_from_terminal
)
3017 if (reading_from_terminal
) {
3018 fputc(had_prompt
? ':' : '\007', stderr
);
3021 input_stack::push(mi
);
3025 while ((c
= getchar()) != EOF
) {
3026 if (illegal_input_char(c
))
3027 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
3040 if (reading_from_terminal
)
3042 input_stack::push(new string_iterator(mac
));
3046 void do_define_string(int append
)
3060 else if (!tok
.space()) {
3061 error("bad string definition");
3072 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3073 macro
*mm
= rm
? rm
->to_macro() : 0;
3076 while (c
!= '\n' && c
!= EOF
) {
3080 mac
.append((unsigned char)c
);
3085 request_dictionary
.define(nm
, mm
);
3091 void define_string()
3093 do_define_string(0);
3096 void append_string()
3098 do_define_string(1);
3101 void define_character()
3106 charinfo
*ci
= tok
.get_char(1);
3116 else if (!tok
.space()) {
3117 error("bad character definition");
3123 while (c
== ' ' || c
== '\t')
3127 macro
*m
= new macro
;
3128 while (c
!= '\n' && c
!= EOF
) {
3132 m
->append((unsigned char)c
);
3135 m
= ci
->set_macro(m
);
3142 static void remove_character()
3145 while (!tok
.newline() && !tok
.eof()) {
3146 if (!tok
.space() && !tok
.tab()) {
3147 charinfo
*ci
= tok
.get_char(1);
3150 macro
*m
= ci
->set_macro(0);
3159 static void interpolate_string(symbol nm
)
3161 request_or_macro
*p
= lookup_request(nm
);
3162 macro
*m
= p
->to_macro();
3164 error("you can only invoke a string using \\*");
3166 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3167 input_stack::push(si
);
3171 /* This class is used for the implementation of \$@. It is used for
3172 each of the closing double quotes. It artificially increases the
3173 input level by 2, so that the closing double quote will appear to have
3174 the same input level as the opening quote. */
3176 class end_quote_iterator
: public input_iterator
{
3177 unsigned char buf
[1];
3179 end_quote_iterator();
3180 ~end_quote_iterator() { }
3181 int internal_level() { return 2; }
3184 end_quote_iterator::end_quote_iterator()
3191 static void interpolate_arg(symbol nm
)
3193 const char *s
= nm
.contents();
3194 if (!s
|| *s
== '\0')
3195 copy_mode_error("missing argument name");
3196 else if (s
[1] == 0 && csdigit(s
[0]))
3197 input_stack::push(input_stack::get_arg(s
[0] - '0'));
3198 else if (s
[0] == '*' && s
[1] == '\0') {
3199 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3200 input_stack::push(input_stack::get_arg(i
));
3202 input_stack::push(make_temp_iterator(" "));
3205 else if (s
[0] == '@' && s
[1] == '\0') {
3206 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3207 input_stack::push(new end_quote_iterator
);
3208 input_stack::push(input_stack::get_arg(i
));
3209 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
3214 for (p
= s
; *p
&& csdigit(*p
); p
++)
3217 copy_mode_error("bad argument name `%1'", s
);
3219 input_stack::push(input_stack::get_arg(atoi(s
)));
3223 void handle_first_page_transition()
3226 topdiv
->begin_page();
3229 // We push back a token by wrapping it up in a token_node, and
3230 // wrapping that up in a string_iterator.
3232 static void push_token(const token
&t
)
3235 m
.append(new token_node(t
));
3236 input_stack::push(new string_iterator(m
));
3239 void push_page_ejector()
3241 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
3242 input_stack::push(make_temp_iterator(buf
));
3245 void handle_initial_request(unsigned char code
)
3251 mac
.append(new token_node(tok
));
3252 input_stack::push(new string_iterator(mac
));
3253 input_stack::push(make_temp_iterator(buf
));
3254 topdiv
->begin_page();
3258 void handle_initial_title()
3260 handle_initial_request(TITLE_REQUEST
);
3263 // this should be local to define_macro, but cfront 1.2 doesn't support that
3264 static symbol
dot_symbol(".");
3266 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3268 void do_define_macro(define_mode mode
)
3271 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3278 symbol term
= get_name(); // the request that terminates the definition
3281 while (!tok
.newline() && !tok
.eof())
3283 const char *start_filename
;
3285 int have_start_location
= input_stack::get_location(0, &start_filename
,
3288 // doing this here makes the line numbers come out right
3289 int c
= get_copy(&n
, 1);
3292 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3293 request_or_macro
*rm
=
3294 (request_or_macro
*)request_dictionary
.lookup(nm
);
3296 mm
= rm
->to_macro();
3297 if (mm
&& mode
== DEFINE_APPEND
)
3302 while (c
== ESCAPE_NEWLINE
) {
3303 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
3305 c
= get_copy(&n
, 1);
3307 if (bol
&& c
== '.') {
3308 const char *s
= term
.contents();
3310 // see if it matches term
3312 for (i
= 0; s
[i
] != 0; i
++) {
3314 if ((unsigned char)s
[i
] != d
)
3318 && ((i
== 2 && compatible_flag
)
3319 || (d
= get_copy(&n
)) == ' '
3320 || d
== '\n')) { // we found it
3325 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3328 request_dictionary
.define(nm
, mm
);
3332 if (term
!= dot_symbol
) {
3334 interpolate_macro(term
);
3340 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3342 for (int j
= 0; j
< i
; j
++)
3348 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3349 if (have_start_location
)
3350 error_with_file_and_line(start_filename
, start_lineno
,
3351 "end of file while defining macro `%1'",
3354 error("end of file while defining macro `%1'", nm
.contents());
3357 if (have_start_location
)
3358 error_with_file_and_line(start_filename
, start_lineno
,
3359 "end of file while ignoring input lines");
3361 error("end of file while ignoring input lines");
3366 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3373 c
= get_copy(&n
, 1);
3379 do_define_macro(DEFINE_NORMAL
);
3384 do_define_macro(DEFINE_APPEND
);
3390 do_define_macro(DEFINE_IGNORE
);
3397 symbol s
= get_name();
3400 request_dictionary
.remove(s
);
3407 symbol s1
= get_name(1);
3408 if (!s1
.is_null()) {
3409 symbol s2
= get_name(1);
3411 request_dictionary
.rename(s1
, s2
);
3418 symbol s1
= get_name(1);
3419 if (!s1
.is_null()) {
3420 symbol s2
= get_name(1);
3421 if (!s2
.is_null()) {
3422 if (!request_dictionary
.alias(s1
, s2
))
3423 warning(WARN_MAC
, "`%1' not defined", s2
.contents());
3431 symbol s
= get_name(1);
3433 request_or_macro
*p
= lookup_request(s
);
3434 macro
*m
= p
->to_macro();
3436 error("cannot chop request");
3437 else if (m
->length
== 0)
3438 error("cannot chop empty macro");
3445 void substring_macro()
3448 symbol s
= get_name(1);
3449 if (!s
.is_null() && get_integer(&start
)) {
3450 request_or_macro
*p
= lookup_request(s
);
3451 macro
*m
= p
->to_macro();
3453 error("cannot substring request");
3460 if (!has_arg() || get_integer(&end
)) {
3470 if (start
>= m
->length
|| start
== end
) {
3473 if (--(m
->p
->count
) <= 0)
3478 else if (start
== 0)
3481 string_iterator
iter(*m
);
3483 for (i
= 0; i
< start
; i
++)
3484 if (iter
.get(0) == EOF
)
3487 for (; i
< end
; i
++) {
3489 int c
= iter
.get(&nd
);
3495 mac
.append((unsigned char)c
);
3505 void asciify_macro()
3507 symbol s
= get_name(1);
3509 request_or_macro
*p
= lookup_request(s
);
3510 macro
*m
= p
->to_macro();
3512 error("cannot asciify request");
3515 string_iterator
iter(*m
);
3518 int c
= iter
.get(&nd
);
3532 static void interpolate_environment_variable(symbol nm
)
3534 const char *s
= getenv(nm
.contents());
3536 input_stack::push(make_temp_iterator(s
));
3539 void interpolate_number_reg(symbol nm
, int inc
)
3541 reg
*r
= lookup_number_reg(nm
);
3546 input_stack::push(make_temp_iterator(r
->get_string()));
3549 static void interpolate_number_format(symbol nm
)
3551 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
3553 input_stack::push(make_temp_iterator(r
->get_format()));
3556 static int get_delim_number(units
*n
, int si
, int prev_value
)
3560 if (start
.delimiter(1)) {
3562 if (get_number(n
, si
, prev_value
)) {
3564 warning(WARN_DELIM
, "closing delimiter does not match");
3571 static int get_delim_number(units
*n
, int si
)
3575 if (start
.delimiter(1)) {
3577 if (get_number(n
, si
)) {
3579 warning(WARN_DELIM
, "closing delimiter does not match");
3586 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
3590 if (start
.delimiter(1)) {
3592 if (get_number(n
, si
)) {
3596 *cp
= tok
.get_char(1);
3600 warning(WARN_DELIM
, "closing delimiter does not match");
3607 static int read_size(int *x
)
3617 else if (c
== '+') {
3628 // allow an increment either before or after the left parenthesis
3634 else if (c
== '+') {
3649 val
= val
*10 + (c
- '0');
3654 else if (csdigit(c
)) {
3656 if (!inc
&& c
!= '0' && c
< '4') {
3662 val
= val
*10 + (c
- '0');
3666 else if (!tok
.delimiter(1))
3672 ? get_number(&val
, 'z')
3673 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
3675 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
3676 if (start
.ch() == '[')
3677 error("missing `]'");
3679 error("missing closing delimiter");
3689 *x
= curenv
->get_requested_point_size() + val
;
3692 *x
= curenv
->get_requested_point_size() - val
;
3700 error("bad digit in point size");
3705 static symbol
get_delim_name()
3710 error("end of input at start of delimited name");
3713 if (start
.newline()) {
3714 error("can't delimit name with a newline");
3717 int start_level
= input_stack::get_level();
3718 char abuf
[ABUF_SIZE
];
3720 int buf_size
= ABUF_SIZE
;
3723 if (i
+ 1 > buf_size
) {
3725 buf
= new char [ABUF_SIZE
*2];
3726 memcpy(buf
, abuf
, buf_size
);
3727 buf_size
= ABUF_SIZE
*2;
3730 char *old_buf
= buf
;
3731 buf
= new char[buf_size
*2];
3732 memcpy(buf
, old_buf
, buf_size
);
3739 && (compatible_flag
|| input_stack::get_level() == start_level
))
3741 if ((buf
[i
] = tok
.ch()) == 0) {
3742 error("missing delimiter (got %1)", tok
.description());
3752 error("empty delimited name");
3768 static void do_register()
3772 if (!start
.delimiter(1))
3775 symbol nm
= get_long_name(1);
3780 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
3782 if (!r
|| !r
->get_value(&prev_value
))
3785 if (!get_number(&val
, 'u', prev_value
))
3788 warning(WARN_DELIM
, "closing delimiter does not match");
3792 set_number_reg(nm
, val
);
3795 // this implements the \w escape sequence
3797 static void do_width()
3801 int start_level
= input_stack::get_level();
3802 environment
env(curenv
);
3803 environment
*oldenv
= curenv
;
3808 warning(WARN_DELIM
, "missing closing delimiter");
3811 if (tok
.newline()) {
3812 warning(WARN_DELIM
, "missing closing delimiter");
3813 input_stack::push(make_temp_iterator("\n"));
3817 && (compatible_flag
|| input_stack::get_level() == start_level
))
3822 units x
= env
.get_input_line_position().to_units();
3823 input_stack::push(make_temp_iterator(itoa(x
)));
3824 env
.width_registers();
3828 charinfo
*page_character
;
3830 void set_page_character()
3832 page_character
= get_optional_char();
3836 static const symbol
percent_symbol("%");
3838 void read_title_parts(node
**part
, hunits
*part_width
)
3841 if (tok
.newline() || tok
.eof())
3844 int start_level
= input_stack::get_level();
3846 for (int i
= 0; i
< 3; i
++) {
3847 while (!tok
.newline() && !tok
.eof()) {
3849 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
3853 if (page_character
!= 0 && tok
.get_char() == page_character
)
3854 interpolate_number_reg(percent_symbol
, 0);
3859 curenv
->wrap_up_tab();
3860 part_width
[i
] = curenv
->get_input_line_position();
3861 part
[i
] = curenv
->extract_output_line();
3863 while (!tok
.newline() && !tok
.eof())
3867 class non_interpreted_node
: public node
{
3870 non_interpreted_node(const macro
&);
3871 int interpret(macro
*);
3877 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
3881 int non_interpreted_node::same(node
*nd
)
3883 return mac
== ((non_interpreted_node
*)nd
)->mac
;
3886 const char *non_interpreted_node::type()
3888 return "non_interpreted_node";
3891 node
*non_interpreted_node::copy()
3893 return new non_interpreted_node(mac
);
3896 int non_interpreted_node::interpret(macro
*m
)
3898 string_iterator
si(mac
);
3912 static node
*do_non_interpreted()
3917 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
3922 if (c
== EOF
|| c
== '\n') {
3923 error("missing \\?");
3926 return new non_interpreted_node(mac
);
3933 int start_level
= input_stack::get_level();
3936 tok
!= start
|| input_stack::get_level() != start_level
;
3939 warning(WARN_DELIM
, "missing closing delimiter");
3942 if (tok
.newline()) {
3943 input_stack::push(make_temp_iterator("\n"));
3944 warning(WARN_DELIM
, "missing closing delimiter");
3952 else if (tok
.leader())
3954 else if (tok
.backspace())
3959 error("%1 is illegal within \\X", tok
.description());
3963 return new special_node(mac
);
3966 void special_node::tprint(troff_output_file
*out
)
3969 string_iterator
iter(mac
);
3971 int c
= iter
.get(NULL
);
3974 for (const char *s
= ::asciify(c
); *s
; s
++)
3975 tprint_char(out
, *s
);
3980 int get_file_line(const char **filename
, int *lineno
)
3982 return input_stack::get_location(0, filename
, lineno
);
3988 if (get_integer(&n
)) {
3989 const char *filename
= 0;
3991 symbol s
= get_long_name();
3992 filename
= s
.contents();
3994 (void)input_stack::set_location(filename
, n
-1);
3999 static int nroff_mode
= 0;
4001 static void nroff_request()
4007 static void troff_request()
4013 static void skip_alternative()
4016 // ensure that ``.if 0\{'' works as expected
4017 if (tok
.left_brace())
4021 c
= input_stack::get(NULL
);
4024 if (c
== ESCAPE_LEFT_BRACE
)
4026 else if (c
== ESCAPE_RIGHT_BRACE
)
4028 else if (c
== escape_char
&& escape_char
> 0)
4029 switch(input_stack::get(NULL
)) {
4037 while ((c
= input_stack::get(NULL
)) != '\n' && c
!= EOF
)
4041 Note that the level can properly be < 0, eg
4047 So don't give an error message in this case.
4049 if (level
<= 0 && c
== '\n')
4055 static void begin_alternative()
4057 while (tok
.space() || tok
.left_brace())
4062 static int_stack if_else_stack
;
4069 while (tok
.ch() == '!') {
4074 unsigned char c
= tok
.ch();
4077 result
= !nroff_mode
;
4079 else if (c
== 'n') {
4081 result
= nroff_mode
;
4083 else if (c
== 'v') {
4087 else if (c
== 'o') {
4088 result
= (topdiv
->get_page_number() & 1);
4091 else if (c
== 'e') {
4092 result
= !(topdiv
->get_page_number() & 1);
4095 else if (c
== 'd' || c
== 'r') {
4097 symbol nm
= get_name(1);
4103 ? request_dictionary
.lookup(nm
) != 0
4104 : number_reg_dictionary
.lookup(nm
) != 0);
4106 else if (c
== 'c') {
4109 charinfo
*ci
= tok
.get_char(1);
4114 result
= character_exists(ci
, curenv
);
4117 else if (tok
.space())
4119 else if (tok
.delimiter()) {
4121 int delim_level
= input_stack::get_level();
4122 environment
env1(curenv
);
4123 environment
env2(curenv
);
4124 environment
*oldenv
= curenv
;
4126 for (int i
= 0; i
< 2; i
++) {
4129 if (tok
.newline() || tok
.eof()) {
4130 warning(WARN_DELIM
, "missing closing delimiter");
4136 && (compatible_flag
|| input_stack::get_level() == delim_level
))
4142 node
*n1
= env1
.extract_output_line();
4143 node
*n2
= env2
.extract_output_line();
4144 result
= same_node_list(n1
, n2
);
4145 delete_node_list(n1
);
4146 delete_node_list(n2
);
4152 if (!get_number(&n
, 'u')) {
4162 begin_alternative();
4168 void if_else_request()
4170 if_else_stack
.push(do_if_request());
4180 if (if_else_stack
.is_empty()) {
4181 warning(WARN_EL
, "unbalanced .el request");
4185 if (if_else_stack
.pop())
4188 begin_alternative();
4192 static int while_depth
= 0;
4193 static int while_break_flag
= 0;
4195 void while_request()
4200 mac
.append(new token_node(tok
));
4203 int c
= input_stack::get(&n
);
4219 if (c
== ESCAPE_LEFT_BRACE
)
4221 else if (c
== ESCAPE_RIGHT_BRACE
)
4223 else if (c
== escape_char
)
4226 if (c
== '\n' && level
<= 0)
4231 error("unbalanced \\{ \\}");
4234 input_stack::add_boundary();
4236 input_stack::push(new string_iterator(mac
, "while loop"));
4238 if (!do_if_request()) {
4239 while (input_stack::get(NULL
) != EOF
)
4243 process_input_stack();
4244 if (while_break_flag
) {
4245 while_break_flag
= 0;
4249 input_stack::remove_boundary();
4255 void while_break_request()
4258 error("no while loop");
4262 while_break_flag
= 1;
4263 while (input_stack::get(NULL
) != EOF
)
4269 void while_continue_request()
4272 error("no while loop");
4276 while (input_stack::get(NULL
) != EOF
)
4286 symbol nm
= get_long_name(1);
4290 while (!tok
.newline() && !tok
.eof())
4293 FILE *fp
= fopen(nm
.contents(), "r");
4295 input_stack::push(new file_iterator(fp
, nm
.contents()));
4297 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
4302 // like .so but use popen()
4306 #ifdef POPEN_MISSING
4307 error("pipes not available on this system");
4309 #else /* not POPEN_MISSING */
4310 if (tok
.newline() || tok
.eof())
4311 error("missing command");
4314 while ((c
= get_copy(NULL
)) == ' ' || c
== '\t')
4317 char *buf
= new char[buf_size
];
4319 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
)) {
4320 const char *s
= asciify(c
);
4321 int slen
= strlen(s
);
4322 if (buf_used
+ slen
+ 1> buf_size
) {
4323 char *old_buf
= buf
;
4324 int old_buf_size
= buf_size
;
4326 buf
= new char[buf_size
];
4327 memcpy(buf
, old_buf
, old_buf_size
);
4330 strcpy(buf
+ buf_used
, s
);
4333 buf
[buf_used
] = '\0';
4335 FILE *fp
= popen(buf
, "r");
4337 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
4339 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
4343 #endif /* not POPEN_MISSING */
4346 const char *asciify(int c
)
4349 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
4350 buf
[1] = buf
[2] = '\0';
4352 case ESCAPE_QUESTION
:
4355 case ESCAPE_AMPERSAND
:
4358 case ESCAPE_UNDERSCORE
:
4364 case ESCAPE_CIRCUMFLEX
:
4367 case ESCAPE_LEFT_BRACE
:
4370 case ESCAPE_RIGHT_BRACE
:
4373 case ESCAPE_LEFT_QUOTE
:
4376 case ESCAPE_RIGHT_QUOTE
:
4394 case ESCAPE_PERCENT
:
4401 if (illegal_input_char(c
))
4411 const char *input_char_description(int c
)
4415 return "a newline character";
4417 return "a backspace character";
4419 return "a leader character";
4421 return "a tab character ";
4423 return "a space character";
4427 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
4428 if (illegal_input_char(c
)) {
4429 const char *s
= asciify(c
);
4436 sprintf(buf
, "magic character code %d", c
);
4445 sprintf(buf
, "character code %d", c
);
4453 if (!tok
.newline() && !tok
.eof()) {
4455 while ((c
= get_copy(NULL
)) == ' ' || c
== '\t')
4457 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
4458 fputs(asciify(c
), stderr
);
4460 fputc('\n', stderr
);
4465 dictionary
stream_dictionary(20);
4467 void do_open(int append
)
4469 symbol stream
= get_name(1);
4470 if (!stream
.is_null()) {
4471 symbol filename
= get_long_name(1);
4472 if (!filename
.is_null()) {
4474 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
4476 error("can't open `%1' for %2: %3",
4477 filename
.contents(),
4478 append
? "appending" : "writing",
4480 fp
= (FILE *)stream_dictionary
.remove(stream
);
4483 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
4496 void opena_request()
4501 void close_request()
4503 symbol stream
= get_name(1);
4504 if (!stream
.is_null()) {
4505 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
4507 error("no stream named `%1'", stream
.contents());
4514 void write_request()
4516 symbol stream
= get_name(1);
4517 if (stream
.is_null()) {
4521 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
4523 error("no stream named `%1'", stream
.contents());
4528 while ((c
= get_copy(NULL
)) == ' ')
4532 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
4533 fputs(asciify(c
), fp
);
4539 void write_macro_request()
4541 symbol stream
= get_name(1);
4542 if (stream
.is_null()) {
4546 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
4548 error("no stream named `%1'", stream
.contents());
4552 symbol s
= get_name(1);
4557 request_or_macro
*p
= lookup_request(s
);
4558 macro
*m
= p
->to_macro();
4560 error("cannot write request");
4562 string_iterator
iter(*m
);
4564 int c
= iter
.get(0);
4567 fputs(asciify(c
), fp
);
4574 static void init_charset_table()
4577 strcpy(buf
, "char");
4578 for (int i
= 0; i
< 256; i
++) {
4579 strcpy(buf
+ 4, itoa(i
));
4580 charset_table
[i
] = get_charinfo(symbol(buf
));
4581 charset_table
[i
]->set_ascii_code(i
);
4583 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
4585 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
4586 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
4587 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
4588 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
4589 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
4590 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
4591 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
4592 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
4593 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
4594 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
4595 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
4596 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
4597 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4598 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4599 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4600 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4601 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
4602 page_character
= charset_table
['%'];
4606 void do_translate(int translate_transparent
)
4609 while (!tok
.newline() && !tok
.eof()) {
4611 // This is a really bizarre troff feature.
4613 translate_space_to_dummy
= tok
.dummy();
4614 if (tok
.newline() || tok
.eof())
4619 charinfo
*ci1
= tok
.get_char(1);
4623 if (tok
.newline() || tok
.eof()) {
4624 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
4625 translate_transparent
);
4629 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
4630 translate_transparent
);
4631 else if (tok
.dummy())
4632 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
4633 translate_transparent
);
4634 else if (tok
.hyphen_indicator())
4635 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
4636 translate_transparent
);
4638 charinfo
*ci2
= tok
.get_char(1);
4642 ci1
->set_translation(0, translate_transparent
);
4644 ci1
->set_translation(ci2
, translate_transparent
);
4656 void translate_no_transparent()
4664 if (get_integer(&flags
))
4666 charinfo
*ci
= tok
.get_char(1);
4668 charinfo
*tem
= ci
->get_translation();
4671 ci
->set_flags(flags
);
4678 void hyphenation_code()
4681 while (!tok
.newline() && !tok
.eof()) {
4682 charinfo
*ci
= tok
.get_char(1);
4687 unsigned char c
= tok
.ch();
4689 error("hyphenation code must be ordinary character");
4693 error("hyphenation code cannot be digit");
4696 ci
->set_hyphenation_code(c
);
4703 charinfo
*token::get_char(int required
)
4705 if (type
== TOKEN_CHAR
)
4706 return charset_table
[c
];
4707 if (type
== TOKEN_SPECIAL
)
4708 return get_charinfo(nm
);
4709 if (type
== TOKEN_NUMBERED_CHAR
)
4710 return get_charinfo_by_number(val
);
4711 if (type
== TOKEN_ESCAPE
) {
4712 if (escape_char
!= 0)
4713 return charset_table
[escape_char
];
4715 error("`\\e' used while no current escape character");
4720 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
4721 warning(WARN_MISSING
, "missing normal or special character");
4723 error("normal or special character expected (got %1)", description());
4728 charinfo
*get_optional_char()
4732 charinfo
*ci
= tok
.get_char();
4734 check_missing_character();
4740 void check_missing_character()
4742 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
4743 error("normal or special character expected (got %1): "
4744 "treated as missing",
4748 int token::add_to_node_list(node
**pp
)
4754 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
);
4760 if (escape_char
!= 0)
4761 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
);
4763 case TOKEN_HYPHEN_INDICATOR
:
4764 *pp
= (*pp
)->add_discretionary_hyphen();
4766 case TOKEN_ITALIC_CORRECTION
:
4767 *pp
= (*pp
)->add_italic_correction(&w
);
4769 case TOKEN_LEFT_BRACE
:
4771 case TOKEN_MARK_INPUT
:
4772 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
4778 case TOKEN_NUMBERED_CHAR
:
4779 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
);
4781 case TOKEN_RIGHT_BRACE
:
4784 n
= new hmotion_node(curenv
->get_space_width());
4787 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
);
4799 void token::process()
4801 if (possibly_handle_first_page_transition())
4804 case TOKEN_BACKSPACE
:
4805 curenv
->add_node(new hmotion_node(-curenv
->get_space_width()));
4808 curenv
->add_char(charset_table
[c
]);
4811 curenv
->add_node(new dummy_node
);
4820 if (escape_char
!= 0)
4821 curenv
->add_char(charset_table
[escape_char
]);
4823 case TOKEN_BEGIN_TRAP
:
4824 case TOKEN_END_TRAP
:
4825 case TOKEN_PAGE_EJECTOR
:
4826 // these are all handled in process_input_stack()
4828 case TOKEN_HYPHEN_INDICATOR
:
4829 curenv
->add_hyphen_indicator();
4831 case TOKEN_INTERRUPT
:
4832 curenv
->interrupt();
4834 case TOKEN_ITALIC_CORRECTION
:
4835 curenv
->add_italic_correction();
4838 curenv
->handle_tab(1);
4840 case TOKEN_LEFT_BRACE
:
4842 case TOKEN_MARK_INPUT
:
4843 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
4849 curenv
->add_node(nd
);
4852 case TOKEN_NUMBERED_CHAR
:
4853 curenv
->add_char(get_charinfo_by_number(val
));
4856 // handled in process_input_stack
4858 case TOKEN_RIGHT_BRACE
:
4864 curenv
->add_char(get_charinfo(nm
));
4870 curenv
->handle_tab(0);
4872 case TOKEN_TRANSPARENT
:
4879 class nargs_reg
: public reg
{
4881 const char *get_string();
4884 const char *nargs_reg::get_string()
4886 return itoa(input_stack::nargs());
4889 class lineno_reg
: public reg
{
4891 const char *get_string();
4894 const char *lineno_reg::get_string()
4898 if (!input_stack::get_location(0, &file
, &line
))
4904 class writable_lineno_reg
: public general_reg
{
4906 writable_lineno_reg();
4907 void set_value(units
);
4908 int get_value(units
*);
4911 writable_lineno_reg::writable_lineno_reg()
4915 int writable_lineno_reg::get_value(units
*res
)
4919 if (!input_stack::get_location(0, &file
, &line
))
4925 void writable_lineno_reg::set_value(units n
)
4927 input_stack::set_location(0, n
);
4930 class filename_reg
: public reg
{
4932 const char *get_string();
4935 const char *filename_reg::get_string()
4939 if (input_stack::get_location(0, &file
, &line
))
4946 class constant_reg
: public reg
{
4949 constant_reg(const char *);
4950 const char *get_string();
4953 constant_reg::constant_reg(const char *p
) : s(p
)
4957 const char *constant_reg::get_string()
4962 constant_int_reg::constant_int_reg(int *q
) : p(q
)
4966 const char *constant_int_reg::get_string()
4971 void abort_request()
4976 else if (tok
.newline())
4979 while ((c
= get_copy(0)) == ' ')
4982 if (c
== EOF
|| c
== '\n')
4983 fputs("User Abort.", stderr
);
4985 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
4986 fputs(asciify(c
), stderr
);
4988 fputc('\n', stderr
);
4989 cleanup_and_exit(1);
4995 char *s
= new char[len
];
4997 while ((c
= get_copy(0)) == ' ')
5000 while (c
!= '\n' && c
!= EOF
) {
5001 if (!illegal_input_char(c
)) {
5004 s
= new char[len
*2];
5005 memcpy(s
, tem
, len
);
5024 #ifdef POPEN_MISSING
5025 error("pipes not available on this system");
5027 #else /* not POPEN_MISSING */
5029 error("can't pipe: output already started");
5033 if ((pipe_command
= read_string()) == 0)
5034 error("can't pipe to empty command");
5036 #endif /* not POPEN_MISSING */
5039 static int system_status
;
5041 void system_request()
5043 char *command
= read_string();
5045 error("empty command");
5047 system_status
= system(command
);
5054 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5055 handle_initial_request(COPY_FILE_REQUEST
);
5058 symbol filename
= get_long_name(1);
5059 while (!tok
.newline() && !tok
.eof())
5063 if (!filename
.is_null())
5064 curdiv
->copy_file(filename
.contents());
5072 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5073 handle_initial_request(VJUSTIFY_REQUEST
);
5076 symbol type
= get_long_name(1);
5077 if (!type
.is_null())
5078 curdiv
->vjustify(type
);
5084 void transparent_file()
5086 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5087 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
5090 symbol filename
= get_long_name(1);
5091 while (!tok
.newline() && !tok
.eof())
5095 if (!filename
.is_null()) {
5097 FILE *fp
= fopen(filename
.contents(), "r");
5099 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
5106 if (illegal_input_char(c
))
5107 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
5109 curdiv
->transparent_output(c
);
5114 curdiv
->transparent_output('\n');
5126 page_range(int, int, page_range
*);
5127 int contains(int n
);
5130 page_range::page_range(int i
, int j
, page_range
*p
)
5131 : first(i
), last(j
), next(p
)
5135 int page_range::contains(int n
)
5137 return n
>= first
&& (last
<= 0 || n
<= last
);
5140 page_range
*output_page_list
= 0;
5142 int in_output_page_list(int n
)
5144 if (!output_page_list
)
5146 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
5152 static void parse_output_page_list(char *p
)
5158 else if (csdigit(*p
)) {
5161 i
= i
*10 + *p
++ - '0';
5162 while (csdigit(*p
));
5172 j
= j
*10 + *p
++ - '0';
5173 while (csdigit(*p
));
5179 last_page_number
= -1;
5180 else if (last_page_number
>= 0 && j
> last_page_number
)
5181 last_page_number
= j
;
5182 output_page_list
= new page_range(i
, j
, output_page_list
);
5188 error("bad output page list");
5189 output_page_list
= 0;
5193 static FILE *open_mac_file(const char *mac
, char **path
)
5195 char *s
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
5196 strcpy(s
, MACRO_PREFIX
);
5198 FILE *fp
= macro_path
.open_file(s
, path
);
5203 static void process_macro_file(const char *mac
)
5206 FILE *fp
= open_mac_file(mac
, &path
);
5208 fatal("can't find macro file %1", mac
);
5209 const char *s
= symbol(path
).contents();
5211 input_stack::push(new file_iterator(fp
, s
));
5213 process_input_stack();
5216 static void process_startup_file()
5219 FILE *fp
= macro_path
.open_file(STARTUP_FILE
, &path
);
5221 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
5224 process_input_stack();
5230 symbol nm
= get_long_name(1);
5234 while (!tok
.newline() && !tok
.eof())
5237 FILE *fp
= macro_path
.open_file(nm
.contents(), &path
);
5239 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
5243 error("can't find macro file `%1'", nm
.contents());
5248 static void process_input_file(const char *name
)
5251 if (strcmp(name
, "-") == 0) {
5257 fp
= fopen(name
, "r");
5259 fatal("can't open `%1': %2", name
, strerror(errno
));
5261 input_stack::push(new file_iterator(fp
, name
));
5263 process_input_stack();
5266 // make sure the_input is empty before calling this
5268 static int evaluate_expression(const char *expr
, units
*res
)
5270 input_stack::push(make_temp_iterator(expr
));
5272 int success
= get_number(res
, 'u');
5273 while (input_stack::get(NULL
) != EOF
)
5278 static void do_register_assignment(const char *s
)
5280 const char *p
= strchr(s
, '=');
5286 if (evaluate_expression(s
+ 1, &n
))
5287 set_number_reg(buf
, n
);
5290 char *buf
= new char[p
- s
+ 1];
5291 memcpy(buf
, s
, p
- s
);
5294 if (evaluate_expression(p
+ 1, &n
))
5295 set_number_reg(buf
, n
);
5300 static void set_string(const char *name
, const char *value
)
5302 macro
*m
= new macro
;
5303 for (const char *p
= value
; *p
; p
++)
5304 if (!illegal_input_char((unsigned char)*p
))
5306 request_dictionary
.define(name
, m
);
5310 static void do_string_assignment(const char *s
)
5312 const char *p
= strchr(s
, '=');
5317 set_string(buf
, s
+ 1);
5320 char *buf
= new char[p
- s
+ 1];
5321 memcpy(buf
, s
, p
- s
);
5323 set_string(buf
, p
+ 1);
5328 struct string_list
{
5331 string_list(const char *ss
) : s(ss
), next(0) {}
5334 static void prepend_string(const char *s
, string_list
**p
)
5336 string_list
*l
= new string_list(s
);
5341 static void add_string(const char *s
, string_list
**p
)
5345 *p
= new string_list(s
);
5348 void usage(const char *prog
)
5351 "usage: %1 -abivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
5352 " -rcn -Tname -Fdir -Mdir [files...]\n",
5354 exit(USAGE_EXIT_CODE
);
5357 int main(int argc
, char **argv
)
5359 program_name
= argv
[0];
5360 static char stderr_buf
[BUFSIZ
];
5361 setbuf(stderr
, stderr_buf
);
5363 string_list
*macros
= 0;
5364 string_list
*register_assignments
= 0;
5365 string_list
*string_assignments
= 0;
5370 int safer_flag
= 1; // safer by default
5371 int no_rc
= 0; // don't process troffrc
5372 int next_page_number
;
5374 hresolution
= vresolution
= 1;
5375 while ((c
= getopt(argc
, argv
, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"))
5380 extern const char *version_string
;
5381 fprintf(stderr
, "GNU troff version %s\n", version_string
);
5390 compatible_flag
= 1;
5393 macro_path
.command_line_dir(optarg
);
5396 font::command_line_font_dir(optarg
);
5399 add_string(optarg
, ¯os
);
5408 enable_warning(optarg
);
5411 disable_warning(optarg
);
5420 ascii_output_flag
= 1;
5423 suppress_output_flag
= 1;
5426 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
5429 error("bad page number");
5432 parse_output_page_list(optarg
);
5435 if (*optarg
== '\0')
5436 error("`-d' requires non-empty argument");
5438 add_string(optarg
, &string_assignments
);
5441 if (*optarg
== '\0')
5442 error("`-r' requires non-empty argument");
5444 add_string(optarg
, ®ister_assignments
);
5447 default_family
= symbol(optarg
);
5453 // silently ignore these
5456 safer_flag
= 0; // unsafe behaviour
5463 set_string(".T", device
);
5464 init_charset_table();
5465 if (!font::load_desc())
5466 fatal("sorry, I can't continue");
5467 units_per_inch
= font::res
;
5468 hresolution
= font::hor
;
5469 vresolution
= font::vert
;
5470 sizescale
= font::sizescale
;
5471 tcommand_flag
= font::tcommand
;
5472 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
5473 default_family
= symbol(font::family
);
5474 font_size::init_size_table(font::sizes
);
5477 if (font::style_table
) {
5478 for (i
= 0; font::style_table
[i
]; i
++)
5479 mount_style(j
++, symbol(font::style_table
[i
]));
5481 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
5482 // In the DESC file a font name of 0 (zero) means leave this
5484 if (strcmp(font::font_name_table
[i
], "0") != 0)
5485 mount_font(j
, symbol(font::font_name_table
[i
]));
5486 curdiv
= topdiv
= new top_level_diversion
;
5488 topdiv
->set_next_page_number(next_page_number
);
5489 init_input_requests();
5490 init_env_requests();
5491 init_div_requests();
5493 init_column_requests();
5495 init_node_requests();
5496 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
5498 init_reg_requests();
5499 init_hyphen_requests();
5500 init_environments();
5501 while (string_assignments
) {
5502 do_string_assignment(string_assignments
->s
);
5503 string_list
*tem
= string_assignments
;
5504 string_assignments
= string_assignments
->next
;
5507 while (register_assignments
) {
5508 do_register_assignment(register_assignments
->s
);
5509 string_list
*tem
= register_assignments
;
5510 register_assignments
= register_assignments
->next
;
5514 process_startup_file();
5516 prepend_string("safer", ¯os
);
5518 process_macro_file(macros
->s
);
5519 string_list
*tem
= macros
;
5520 macros
= macros
->next
;
5523 for (i
= optind
; i
< argc
; i
++)
5524 process_input_file(argv
[i
]);
5525 if (optind
>= argc
|| iflag
)
5526 process_input_file("-");
5528 return 0; // not reached
5534 if (has_arg() && get_integer(&n
)) {
5535 if (n
& ~WARN_TOTAL
) {
5536 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
5542 warning_mask
= WARN_TOTAL
;
5546 static void init_registers()
5548 #ifdef LONG_FOR_TIME_T
5550 #else /* not LONG_FOR_TIME_T */
5552 #endif /* not LONG_FOR_TIME_T */
5554 // Use struct here to work around misfeature in old versions of g++.
5555 struct tm
*tt
= localtime(&t
);
5556 set_number_reg("dw", int(tt
->tm_wday
+ 1));
5557 set_number_reg("dy", int(tt
->tm_mday
));
5558 set_number_reg("mo", int(tt
->tm_mon
+ 1));
5559 set_number_reg("yr", int(tt
->tm_year
));
5560 set_number_reg("$$", getpid());
5561 number_reg_dictionary
.define(".A",
5562 new constant_reg(ascii_output_flag
5567 void init_input_requests()
5569 init_request("ds", define_string
);
5570 init_request("as", append_string
);
5571 init_request("de", define_macro
);
5572 init_request("am", append_macro
);
5573 init_request("ig", ignore
);
5574 init_request("rm", remove_macro
);
5575 init_request("rn", rename_macro
);
5576 init_request("if", if_request
);
5577 init_request("ie", if_else_request
);
5578 init_request("el", else_request
);
5579 init_request("so", source
);
5580 init_request("nx", next_file
);
5581 init_request("pm", print_macros
);
5582 init_request("eo", escape_off
);
5583 init_request("ec", set_escape_char
);
5584 init_request("pc", set_page_character
);
5585 init_request("tm", terminal
);
5586 init_request("ex", exit_request
);
5587 init_request("em", end_macro
);
5588 init_request("blm", blank_line_macro
);
5589 init_request("tr", translate
);
5590 init_request("trnt", translate_no_transparent
);
5591 init_request("ab", abort_request
);
5592 init_request("pi", pipe_output
);
5593 init_request("cf", copy_file
);
5594 init_request("sy", system_request
);
5595 init_request("lf", line_file
);
5596 init_request("cflags", char_flags
);
5597 init_request("shift", shift
);
5598 init_request("rd", read_request
);
5599 init_request("cp", compatible
);
5600 init_request("char", define_character
);
5601 init_request("rchar", remove_character
);
5602 init_request("hcode", hyphenation_code
);
5603 init_request("while", while_request
);
5604 init_request("break", while_break_request
);
5605 init_request("continue", while_continue_request
);
5606 init_request("als", alias_macro
);
5607 init_request("backtrace", backtrace_request
);
5608 init_request("chop", chop_macro
);
5609 init_request("substring", substring_macro
);
5610 init_request("asciify", asciify_macro
);
5611 init_request("warn", warn_request
);
5612 init_request("open", open_request
);
5613 init_request("opena", opena_request
);
5614 init_request("close", close_request
);
5615 init_request("write", write_request
);
5616 init_request("writem", write_macro_request
);
5617 init_request("trf", transparent_file
);
5618 #ifdef WIDOW_CONTROL
5619 init_request("fpl", flush_pending_lines
);
5620 #endif /* WIDOW_CONTROL */
5621 init_request("nroff", nroff_request
);
5622 init_request("troff", troff_request
);
5624 init_request("vj", vjustify
);
5626 init_request("mso", macro_source
);
5627 init_request("do", do_request
);
5628 #ifndef POPEN_MISSING
5629 init_request("pso", pipe_source
);
5630 #endif /* not POPEN_MISSING */
5631 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
5632 number_reg_dictionary
.define("slimit",
5633 new variable_reg(&input_stack::limit
));
5634 number_reg_dictionary
.define(".$", new nargs_reg
);
5635 number_reg_dictionary
.define(".c", new lineno_reg
);
5636 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
5637 number_reg_dictionary
.define(".F", new filename_reg
);
5638 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
5639 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
5640 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
5641 number_reg_dictionary
.define(".R", new constant_reg("10000"));
5642 extern const char *major_version
;
5643 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
5644 extern const char *minor_version
;
5645 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
5646 number_reg_dictionary
.define(".g", new constant_reg("1"));
5647 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
5650 object_dictionary
request_dictionary(501);
5652 void init_request(const char *s
, REQUEST_FUNCP f
)
5654 request_dictionary
.define(s
, new request(f
));
5657 static request_or_macro
*lookup_request(symbol nm
)
5659 assert(!nm
.is_null());
5660 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
5662 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
5664 request_dictionary
.define(nm
, p
);
5670 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
5672 // Don't interpret character definitions in compatible mode.
5673 int old_compatible_flag
= compatible_flag
;
5674 compatible_flag
= 0;
5675 int old_escape_char
= escape_char
;
5677 macro
*mac
= ci
->set_macro(0);
5679 environment
*oldenv
= curenv
;
5680 environment
env(envp
);
5682 curenv
->set_composite();
5683 token old_tok
= tok
;
5684 input_stack::add_boundary();
5685 string_iterator
*si
= new string_iterator(*mac
, "composite character", ci
->nm
);
5686 input_stack::push(si
);
5687 // we don't use process_input_stack, because we don't want to recognise
5693 if (tok
.newline()) {
5694 error("composite character mustn't contain newline");
5702 node
*n
= curenv
->extract_output_line();
5703 input_stack::remove_boundary();
5707 compatible_flag
= old_compatible_flag
;
5708 escape_char
= old_escape_char
;
5712 static node
*read_draw_node()
5716 if (!start
.delimiter(1)){
5719 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
5724 error("missing argument");
5726 unsigned char type
= tok
.ch();
5729 hvpair
*point
= new hvpair
[maxpoints
];
5734 for (i
= 0; tok
!= start
; i
++) {
5735 if (i
== maxpoints
) {
5736 hvpair
*oldpoint
= point
;
5737 point
= new hvpair
[maxpoints
*2];
5738 for (int j
= 0; j
< maxpoints
; j
++)
5739 point
[j
] = oldpoint
[j
];
5743 if (!get_hunits(&point
[i
].h
,
5744 type
== 'f' || type
== 't' ? 'u' : 'm')) {
5755 if (!get_vunits(&point
[i
].v
, 'v')) {
5761 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
5766 if (npoints
!= 1 || no_last_v
) {
5767 error("two arguments needed for line");
5772 if (npoints
!= 1 || !no_last_v
) {
5773 error("one argument needed for circle");
5779 if (npoints
!= 1 || no_last_v
) {
5780 error("two arguments needed for ellipse");
5785 if (npoints
!= 2 || no_last_v
) {
5786 error("four arguments needed for arc");
5792 error("even number of arguments needed for spline");
5795 // silently pass it through
5798 draw_node
*dn
= new draw_node(type
, point
, npoints
,
5799 curenv
->get_font_size());
5814 } warning_table
[] = {
5815 { "char", WARN_CHAR
},
5816 { "range", WARN_RANGE
},
5817 { "break", WARN_BREAK
},
5818 { "delim", WARN_DELIM
},
5820 { "scale", WARN_SCALE
},
5821 { "number", WARN_NUMBER
},
5822 { "syntax", WARN_SYNTAX
},
5823 { "tab", WARN_TAB
},
5824 { "right-brace", WARN_RIGHT_BRACE
},
5825 { "missing", WARN_MISSING
},
5826 { "input", WARN_INPUT
},
5827 { "escape", WARN_ESCAPE
},
5828 { "space", WARN_SPACE
},
5829 { "font", WARN_FONT
},
5831 { "mac", WARN_MAC
},
5832 { "reg", WARN_REG
},
5834 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
5835 { "w", WARN_TOTAL
},
5836 { "default", DEFAULT_WARNING_MASK
},
5839 static int lookup_warning(const char *name
)
5842 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
5844 if (strcmp(name
, warning_table
[i
].name
) == 0)
5845 return warning_table
[i
].mask
;
5849 static void enable_warning(const char *name
)
5851 int mask
= lookup_warning(name
);
5853 warning_mask
|= mask
;
5855 error("unknown warning `%1'", name
);
5858 static void disable_warning(const char *name
)
5860 int mask
= lookup_warning(name
);
5862 warning_mask
&= ~mask
;
5864 error("unknown warning `%1'", name
);
5867 static void copy_mode_error(const char *format
,
5873 static const char prefix
[] = "(in ignored input) ";
5874 char *s
= new char[sizeof(prefix
) + strlen(format
)];
5877 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
5881 error(format
, arg1
, arg2
, arg3
);
5884 enum error_type
{ WARNING
, ERROR
, FATAL
};
5886 static void do_error(error_type type
,
5892 const char *filename
;
5894 if (inhibit_errors
&& type
< FATAL
)
5897 input_stack::backtrace();
5898 if (!get_file_line(&filename
, &lineno
))
5901 errprint("%1:%2: ", filename
, lineno
);
5902 else if (program_name
)
5903 fprintf(stderr
, "%s: ", program_name
);
5906 fputs("fatal error: ", stderr
);
5911 fputs("warning: ", stderr
);
5914 errprint(format
, arg1
, arg2
, arg3
);
5915 fputc('\n', stderr
);
5918 cleanup_and_exit(1);
5921 int warning(warning_type t
,
5927 if ((t
& warning_mask
) != 0) {
5928 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
5935 void error(const char *format
,
5940 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
5943 void fatal(const char *format
,
5948 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
5951 void fatal_with_file_and_line(const char *filename
, int lineno
,
5957 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
5958 errprint(format
, arg1
, arg2
, arg3
);
5959 fputc('\n', stderr
);
5961 cleanup_and_exit(1);
5964 void error_with_file_and_line(const char *filename
, int lineno
,
5970 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
5971 errprint(format
, arg1
, arg2
, arg3
);
5972 fputc('\n', stderr
);
5976 dictionary
charinfo_dictionary(501);
5978 charinfo
*get_charinfo(symbol nm
)
5980 void *p
= charinfo_dictionary
.lookup(nm
);
5982 return (charinfo
*)p
;
5983 charinfo
*cp
= new charinfo(nm
);
5984 (void)charinfo_dictionary
.lookup(nm
, cp
);
5988 int charinfo::next_index
= 0;
5990 charinfo::charinfo(symbol s
)
5991 : nm(s
), hyphenation_code(0), translation(0), flags(0), ascii_code(0),
5992 special_translation(TRANSLATE_NONE
), mac(0), not_found(0),
5993 transparent_translate(1)
5995 index
= next_index
++;
5998 void charinfo::set_hyphenation_code(unsigned char c
)
6000 hyphenation_code
= c
;
6003 void charinfo::set_translation(charinfo
*ci
, int tt
)
6006 special_translation
= TRANSLATE_NONE
;
6007 transparent_translate
= tt
;
6010 void charinfo::set_special_translation(int c
, int tt
)
6012 special_translation
= c
;
6014 transparent_translate
= tt
;
6017 void charinfo::set_ascii_code(unsigned char c
)
6022 macro
*charinfo::set_macro(macro
*m
)
6029 void charinfo::set_number(int n
)
6035 int charinfo::get_number()
6037 assert(flags
& NUMBERED
);
6041 symbol
UNNAMED_SYMBOL("---");
6043 // For numbered characters not between 0 and 255, we make a symbol out
6044 // of the number and store them in this dictionary.
6046 dictionary
numbered_charinfo_dictionary(11);
6048 charinfo
*get_charinfo_by_number(int n
)
6050 static charinfo
*number_table
[256];
6052 if (n
>= 0 && n
< 256) {
6053 charinfo
*ci
= number_table
[n
];
6055 ci
= new charinfo(UNNAMED_SYMBOL
);
6057 number_table
[n
] = ci
;
6063 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
6065 ci
= new charinfo(UNNAMED_SYMBOL
);
6067 numbered_charinfo_dictionary
.lookup(ns
, ci
);
6073 int font::name_to_index(const char *nm
)
6077 ci
= charset_table
[nm
[0] & 0xff];
6078 else if (nm
[0] == '\\' && nm
[2] == 0)
6079 ci
= get_charinfo(symbol(nm
+ 1));
6081 ci
= get_charinfo(symbol(nm
));
6085 return ci
->get_index();
6088 int font::number_to_index(int n
)
6090 return get_charinfo_by_number(n
)->get_index();