diff options
Diffstat (limited to 'contrib/groff/src/roff/troff/input.cc')
-rw-r--r-- | contrib/groff/src/roff/troff/input.cc | 1685 |
1 files changed, 1166 insertions, 519 deletions
diff --git a/contrib/groff/src/roff/troff/input.cc b/contrib/groff/src/roff/troff/input.cc index 54aaa3f..7a90e4b 100644 --- a/contrib/groff/src/roff/troff/input.cc +++ b/contrib/groff/src/roff/troff/input.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -36,7 +36,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "defs.h" #include "input.h" -// Needed for getpid(). +// Needed for getpid() and isatty() #include "posix.h" #include "nonposix.h" @@ -47,17 +47,6 @@ extern "C" { } #endif /* NEED_DECLARATION_PUTENV */ -#ifdef ISATTY_MISSING -#undef isatty -#define isatty(n) (1) -#else /* not ISATTY_MISSING */ -#ifndef isatty -extern "C" { - int isatty(int); -} -#endif /* not isatty */ -#endif /* not ISATTY_MISSING */ - #define MACRO_PREFIX "tmac." #define MACRO_POSTFIX ".tmac" #define INITIAL_STARTUP_FILE "troffrc" @@ -86,17 +75,19 @@ void copy_file(); #ifdef COLUMN void vjustify(); #endif /* COLUMN */ -void transparent(); void transparent_file(); +void process_input_stack(); const char *program_name = 0; token tok; int break_flag = 0; +int color_flag = 1; // colors are on by default static int backtrace_flag = 0; #ifndef POPEN_MISSING char *pipe_command = 0; #endif charinfo *charset_table[256]; +unsigned char hpf_code_table[256]; static int warning_mask = DEFAULT_WARNING_MASK; static int inhibit_errors = 0; @@ -114,9 +105,18 @@ int suppress_output_flag = 0; int is_html = 0; int begin_level = 0; // number of nested .begin requests +int have_input = 0; // whether \f, \H, \R, \s, or \S has + // been processed in token::next() int tcommand_flag = 0; int safer_flag = 1; // safer by default +int have_string_arg = 0; // whether we have \*[foo bar...] + +double spread_limit = -3.0 - 1.0; // negative means deactivated + +double warn_scale; +char warn_scaling_indicator; + search_path *mac_path = &safer_macro_path; static int get_copy(node**, int = 0); @@ -125,8 +125,11 @@ static void copy_mode_error(const char *, const errarg & = empty_errarg, const errarg & = empty_errarg); -static symbol read_escape_name(); +enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS }; +static symbol read_escape_name(read_mode mode = NO_ARGS); +static symbol read_long_escape_name(read_mode mode = NO_ARGS); static void interpolate_string(symbol); +static void interpolate_string_with_args(symbol); static void interpolate_macro(symbol); static void interpolate_number_format(symbol); static void interpolate_environment_variable(symbol); @@ -135,7 +138,6 @@ static void interpolate_arg(symbol); static request_or_macro *lookup_request(symbol); static int get_delim_number(units *, int); static int get_delim_number(units *, int, units); -static symbol get_delim_file_name(); static int get_line_arg(units *res, int si, charinfo **cp); static int read_size(int *); static symbol get_delim_name(); @@ -185,7 +187,7 @@ void restore_escape_char() class input_iterator { public: input_iterator(); - virtual ~input_iterator(); + virtual ~input_iterator() {} int get(node **); friend class input_stack; protected: @@ -197,15 +199,13 @@ private: virtual int peek(); virtual int has_args() { return 0; } virtual int nargs() { return 0; } - virtual input_iterator *get_arg(int) { return NULL; } - virtual int get_location(int, const char **, int *) - { return 0; } + virtual input_iterator *get_arg(int) { return 0; } + virtual int get_location(int, const char **, int *) { return 0; } virtual void backtrace() {} - virtual int set_location(const char *, int) - { return 0; } + virtual int set_location(const char *, int) { return 0; } virtual int next_file(FILE *, const char *) { return 0; } virtual void shift(int) {} - virtual int is_boundary() { return 0; } + virtual int is_boundary() {return 0; } virtual int internal_level() { return 0; } virtual int is_file() { return 0; } virtual int is_macro() { return 0; } @@ -218,10 +218,6 @@ input_iterator::input_iterator() { } -input_iterator::~input_iterator() -{ -} - int input_iterator::fill(node **) { return EOF; @@ -253,6 +249,7 @@ class file_iterator : public input_iterator { const char *filename; int popened; int newline_flag; + int seen_escape; enum { BUF_SIZE = 512 }; unsigned char buf[BUF_SIZE]; void close(); @@ -269,7 +266,8 @@ public: }; file_iterator::file_iterator(FILE *f, const char *fn, int po) -: fp(f), lineno(1), filename(fn), popened(po), newline_flag(0) +: fp(f), lineno(1), filename(fn), popened(po), + newline_flag(0), seen_escape(0) { if ((font::use_charnames_in_special) && (fn != 0)) { if (!the_output) @@ -307,6 +305,7 @@ int file_iterator::next_file(FILE *f, const char *s) fp = f; lineno = 1; newline_flag = 0; + seen_escape = 0; popened = 0; ptr = 0; eptr = 0; @@ -315,10 +314,8 @@ int file_iterator::next_file(FILE *f, const char *s) int file_iterator::fill(node **) { - if (newline_flag) { - curenv->add_html_tag_eol(); + if (newline_flag) lineno++; - } newline_flag = 0; unsigned char *p = buf; ptr = p; @@ -327,14 +324,16 @@ int file_iterator::fill(node **) int c = getc(fp); if (c == EOF) break; - if (illegal_input_char(c)) - warning(WARN_INPUT, "illegal input character code %1", int(c)); + if (invalid_input_char(c)) + warning(WARN_INPUT, "invalid input character code %1", int(c)); else { *p++ = c; if (c == '\n') { + seen_escape = 0; newline_flag = 1; break; } + seen_escape = (c == '\\'); } } if (p > buf) { @@ -350,8 +349,8 @@ int file_iterator::fill(node **) int file_iterator::peek() { int c = getc(fp); - while (illegal_input_char(c)) { - warning(WARN_INPUT, "illegal input character code %1", int(c)); + while (invalid_input_char(c)) { + warning(WARN_INPUT, "invalid input character code %1", int(c)); c = getc(fp); } if (c != EOF) @@ -517,7 +516,7 @@ void input_stack::push(input_iterator *in) input_iterator *input_stack::get_arg(int i) { input_iterator *p; - for (p = top; p != NULL; p = p->next) + for (p = top; p != 0; p = p->next) if (p->has_args()) return p->get_arg(i); return 0; @@ -677,21 +676,25 @@ void shift() skip_line(); } -static int get_char_for_escape_name() +static int get_char_for_escape_name(int allow_space = 0) { - int c = get_copy(NULL); + int c = get_copy(0); switch (c) { case EOF: copy_mode_error("end of input in escape name"); return '\0'; default: - if (!illegal_input_char(c)) + if (!invalid_input_char(c)) break; // fall through case '\n': if (c == '\n') input_stack::push(make_temp_iterator("\n")); + // fall through case ' ': + if (c == ' ' && allow_space) + break; + // fall through case '\t': case '\001': case '\b': @@ -716,20 +719,25 @@ static symbol read_two_char_escape_name() return symbol(buf); } -static symbol read_long_escape_name() +static symbol read_long_escape_name(read_mode mode) { int start_level = input_stack::get_level(); char abuf[ABUF_SIZE]; char *buf = abuf; int buf_size = ABUF_SIZE; int i = 0; + int c; + int have_char = 0; for (;;) { - int c = get_char_for_escape_name(); + c = get_char_for_escape_name(have_char && mode == WITH_ARGS); if (c == 0) { if (buf != abuf) a_delete buf; return NULL_SYMBOL; } + have_char = 1; + if (mode == WITH_ARGS && c == ' ') + break; if (i + 2 > buf_size) { if (buf == abuf) { buf = new char[ABUF_SIZE*2]; @@ -749,10 +757,13 @@ static symbol read_long_escape_name() buf[i++] = c; } buf[i] = 0; + if (c == ' ') + have_string_arg = 1; if (buf == abuf) { if (i == 0) { - copy_mode_error("empty escape name"); - return NULL_SYMBOL; + if (mode != ALLOW_EMPTY) + copy_mode_error("empty escape name"); + return EMPTY_SYMBOL; } return symbol(abuf); } @@ -763,7 +774,7 @@ static symbol read_long_escape_name() } } -static symbol read_escape_name() +static symbol read_escape_name(read_mode mode) { int c = get_char_for_escape_name(); if (c == 0) @@ -771,7 +782,7 @@ static symbol read_escape_name() if (c == '(') return read_two_char_escape_name(); if (c == '[' && !compatible_flag) - return read_long_escape_name(); + return read_long_escape_name(mode); char buf[2]; buf[0] = c; buf[1] = '\0'; @@ -826,131 +837,137 @@ static int get_copy(node **nd, int defining) case 0: return escape_char; case '"': - (void)input_stack::get(NULL); - while ((c = input_stack::get(NULL)) != '\n' && c != EOF) + (void)input_stack::get(0); + while ((c = input_stack::get(0)) != '\n' && c != EOF) ; return c; case '#': // Like \" but newline is ignored. - (void)input_stack::get(NULL); - while ((c = input_stack::get(NULL)) != '\n') + (void)input_stack::get(0); + while ((c = input_stack::get(0)) != '\n') if (c == EOF) return EOF; break; case '$': { - (void)input_stack::get(NULL); + (void)input_stack::get(0); symbol s = read_escape_name(); - if (!s.is_null()) + if (!(s.is_null() || s.is_empty())) interpolate_arg(s); break; } case '*': { - (void)input_stack::get(NULL); - symbol s = read_escape_name(); - if (!s.is_null()) - interpolate_string(s); + (void)input_stack::get(0); + symbol s = read_escape_name(WITH_ARGS); + if (!(s.is_null() || s.is_empty())) { + if (have_string_arg) { + have_string_arg = 0; + interpolate_string_with_args(s); + } + else + interpolate_string(s); + } break; } case 'a': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return '\001'; case 'e': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_e; case 'E': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_E; case 'n': { - (void)input_stack::get(NULL); + (void)input_stack::get(0); int inc; symbol s = read_increment_and_escape_name(&inc); - if (!s.is_null()) + if (!(s.is_null() || s.is_empty())) interpolate_number_reg(s, inc); break; } case 'g': { - (void)input_stack::get(NULL); - symbol s = read_escape_name(); - if (!s.is_null()) + (void)input_stack::get(0); + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) interpolate_number_format(s); - break; + break; } case 't': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return '\t'; case 'V': { - (void)input_stack::get(NULL); + (void)input_stack::get(0); symbol s = read_escape_name(); - if (!s.is_null()) + if (!(s.is_null() || s.is_empty())) interpolate_environment_variable(s); break; } case '\n': - (void)input_stack::get(NULL); + (void)input_stack::get(0); if (defining) return ESCAPE_NEWLINE; break; case ' ': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_SPACE; case '~': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_TILDE; case ':': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_COLON; case '|': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_BAR; case '^': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_CIRCUMFLEX; case '{': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_LEFT_BRACE; case '}': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_RIGHT_BRACE; case '`': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_LEFT_QUOTE; case '\'': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_RIGHT_QUOTE; case '-': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_HYPHEN; case '_': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_UNDERSCORE; case 'c': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_c; case '!': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_BANG; case '?': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_QUESTION; case '&': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_AMPERSAND; case ')': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_RIGHT_PARENTHESIS; case '.': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return c; case '%': - (void)input_stack::get(NULL); + (void)input_stack::get(0); return ESCAPE_PERCENT; default: if (c == escape_char) { - (void)input_stack::get(NULL); + (void)input_stack::get(0); return c; } else @@ -1004,27 +1021,255 @@ int non_interpreted_char_node::interpret(macro *mac) static void do_width(); static node *do_non_interpreted(); static node *do_special(); -static node *do_suppress(); +static node *do_suppress(symbol nm); static void do_register(); +dictionary color_dictionary(501); +static symbol default_symbol("default"); + +static color *lookup_color(symbol nm) +{ + assert(!nm.is_null()); + if (nm == default_symbol) + return &default_color; + color *c = (color *)color_dictionary.lookup(nm); + if (c == 0) + warning(WARN_COLOR, "`%1' not defined", nm.contents()); + return c; +} + +void do_glyph_color(symbol nm) +{ + if (nm.is_null()) + return; + if (nm.is_empty()) + curenv->set_glyph_color(curenv->get_prev_glyph_color()); + else { + color *tem = lookup_color(nm); + if (tem) + curenv->set_glyph_color(tem); + else + (void)color_dictionary.lookup(nm, new color); + } +} + +void do_fill_color(symbol nm) +{ + if (nm.is_null()) + return; + if (nm.is_empty()) + curenv->set_fill_color(curenv->get_prev_fill_color()); + else { + color *tem = lookup_color(nm); + if (tem) + curenv->set_fill_color(tem); + else + (void)color_dictionary.lookup(nm, new color); + } +} + +static unsigned int get_color_element(const char *scheme, const char *col) +{ + units val; + if (!get_number(&val, 'f')) { + warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme); + tok.next(); + return 0; + } + if (val < 0) { + warning(WARN_RANGE, "%1 cannot be negative: set to 0", col); + return 0; + } + if (val > color::MAX_COLOR_VAL+1) { + warning(WARN_RANGE, "%1 cannot be greater than 1", col); + // we change 0x10000 to 0xffff + return color::MAX_COLOR_VAL; + } + return (unsigned int)val; +} + +static color *read_rgb() +{ + symbol component = get_long_name(0); + if (component.is_null()) { + warning(WARN_COLOR, "missing rgb color values"); + return 0; + } + const char *s = component.contents(); + color *col = new color; + if (*s == '#') { + if (!col->read_rgb(s)) { + warning(WARN_COLOR, "expecting rgb color definition not `%1'", s); + delete col; + return 0; + } + } + else { + input_stack::push(make_temp_iterator(" ")); + input_stack::push(make_temp_iterator(s)); + tok.next(); + unsigned int r = get_color_element("rgb color", "red component"); + unsigned int g = get_color_element("rgb color", "green component"); + unsigned int b = get_color_element("rgb color", "blue component"); + col->set_rgb(r, g, b); + } + return col; +} + +static color *read_cmy() +{ + symbol component = get_long_name(0); + if (component.is_null()) { + warning(WARN_COLOR, "missing cmy color values"); + return 0; + } + const char *s = component.contents(); + color *col = new color; + if (*s == '#') { + if (!col->read_cmy(s)) { + warning(WARN_COLOR, "expecting cmy color definition not `%1'", s); + delete col; + return 0; + } + } + else { + input_stack::push(make_temp_iterator(" ")); + input_stack::push(make_temp_iterator(s)); + tok.next(); + unsigned int c = get_color_element("cmy color", "cyan component"); + unsigned int m = get_color_element("cmy color", "magenta component"); + unsigned int y = get_color_element("cmy color", "yellow component"); + col->set_cmy(c, m, y); + } + return col; +} + +static color *read_cmyk() +{ + symbol component = get_long_name(0); + if (component.is_null()) { + warning(WARN_COLOR, "missing cmyk color values"); + return 0; + } + const char *s = component.contents(); + color *col = new color; + if (*s == '#') { + if (!col->read_cmyk(s)) { + warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s); + delete col; + return 0; + } + } + else { + input_stack::push(make_temp_iterator(" ")); + input_stack::push(make_temp_iterator(s)); + tok.next(); + unsigned int c = get_color_element("cmyk color", "cyan component"); + unsigned int m = get_color_element("cmyk color", "magenta component"); + unsigned int y = get_color_element("cmyk color", "yellow component"); + unsigned int k = get_color_element("cmyk color", "black component"); + col->set_cmyk(c, m, y, k); + } + return col; +} + +static color *read_gray() +{ + symbol component = get_long_name(0); + if (component.is_null()) { + warning(WARN_COLOR, "missing gray values"); + return 0; + } + const char *s = component.contents(); + color *col = new color; + if (*s == '#') { + if (!col->read_gray(s)) { + warning(WARN_COLOR, "`expecting a gray definition not `%1'", s); + delete col; + return 0; + } + } + else { + input_stack::push(make_temp_iterator("\n")); + input_stack::push(make_temp_iterator(s)); + tok.next(); + unsigned int g = get_color_element("gray", "gray value"); + col->set_gray(g); + } + return col; +} + +static void activate_color() +{ + int n; + if (has_arg() && get_integer(&n)) + color_flag = n != 0; + else + color_flag = 1; + skip_line(); +} + +static void define_color() +{ + symbol color_name = get_long_name(1); + if (color_name.is_null()) { + skip_line(); + return; + } + if (color_name == default_symbol) { + warning(WARN_COLOR, "default color can't be redefined"); + skip_line(); + return; + } + symbol style = get_long_name(1); + if (style.is_null()) { + skip_line(); + return; + } + color *col; + if (strcmp(style.contents(), "rgb") == 0) + col = read_rgb(); + else if (strcmp(style.contents(), "cmyk") == 0) + col = read_cmyk(); + else if (strcmp(style.contents(), "gray") == 0) + col = read_gray(); + else if (strcmp(style.contents(), "grey") == 0) + col = read_gray(); + else if (strcmp(style.contents(), "cmy") == 0) + col = read_cmy(); + else { + warning(WARN_COLOR, + "unknown color space `%1'; use rgb, cmyk, gray or cmy", + style.contents()); + skip_line(); + return; + } + if (col) + (void)color_dictionary.lookup(color_name, col); + skip_line(); +} + static node *do_overstrike() { token start; overstrike_node *on = new overstrike_node; + int start_level = input_stack::get_level(); start.next(); - tok.next(); - while (tok != start) { + for (;;) { + tok.next(); if (tok.newline() || tok.eof()) { warning(WARN_DELIM, "missing closing delimiter"); break; } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; charinfo *ci = tok.get_char(1); if (ci) { node *n = curenv->make_char_node(ci); if (n) on->overstrike(n); } - tok.next(); } return on; } @@ -1034,8 +1279,9 @@ static node *do_bracket() token start; bracket_node *bn = new bracket_node; start.next(); - tok.next(); - while (tok != start) { + int start_level = input_stack::get_level(); + for (;;) { + tok.next(); if (tok.eof()) { warning(WARN_DELIM, "missing closing delimiter"); break; @@ -1045,13 +1291,15 @@ static node *do_bracket() input_stack::push(make_temp_iterator("\n")); break; } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; charinfo *ci = tok.get_char(1); if (ci) { node *n = curenv->make_char_node(ci); if (n) bn->bracket(n); } - tok.next(); } return bn; } @@ -1164,7 +1412,7 @@ static node *do_zero_width() && (compatible_flag || input_stack::get_level() == start_level)) break; if (!tok.add_to_node_list(&rev)) - error("illegal token in argument to \\Z"); + error("invalid token in argument to \\Z"); } node *n = 0; while (rev) { @@ -1326,8 +1574,7 @@ void token::next() return; case ESCAPE_SPACE: ESCAPE_SPACE: - type = TOKEN_NODE; - nd = new space_char_hmotion_node(curenv->get_space_width()); + type = TOKEN_UNSTRETCHABLE_SPACE; return; case ESCAPE_TILDE: ESCAPE_TILDE: @@ -1335,10 +1582,7 @@ void token::next() return; case ESCAPE_COLON: ESCAPE_COLON: - type = TOKEN_NODE; - nd = new space_node(H0); - nd->freeze_space(); - nd->is_escape_colon(); + type = TOKEN_ZERO_WIDTH_BREAK; return; case ESCAPE_e: ESCAPE_e: @@ -1349,12 +1593,14 @@ void token::next() case ESCAPE_BAR: ESCAPE_BAR: type = TOKEN_NODE; - nd = new hmotion_node(curenv->get_narrow_space_width()); + nd = new hmotion_node(curenv->get_narrow_space_width(), + curenv->get_fill_color()); return; case ESCAPE_CIRCUMFLEX: ESCAPE_CIRCUMFLEX: type = TOKEN_NODE; - nd = new hmotion_node(curenv->get_half_narrow_space_width()); + nd = new hmotion_node(curenv->get_half_narrow_space_width(), + curenv->get_fill_color()); return; case ESCAPE_NEWLINE: break; @@ -1447,7 +1693,7 @@ void token::next() } else { handle_escape_char: - cc = input_stack::get(NULL); + cc = input_stack::get(0); switch(cc) { case '(': nm = read_two_char_escape_name(); @@ -1470,7 +1716,8 @@ void token::next() case ' ': goto ESCAPE_SPACE; case '0': - nd = new hmotion_node(curenv->get_digit_width()); + nd = new hmotion_node(curenv->get_digit_width(), + curenv->get_fill_color()); type = TOKEN_NODE; return; case '|': @@ -1497,7 +1744,7 @@ void token::next() case ':': goto ESCAPE_COLON; case '"': - while ((cc = input_stack::get(NULL)) != '\n' && cc != EOF) + while ((cc = input_stack::get(0)) != '\n' && cc != EOF) ; if (cc == '\n') type = TOKEN_NEWLINE; @@ -1505,7 +1752,7 @@ void token::next() type = TOKEN_EOF; return; case '#': // Like \" but newline is ignored. - while ((cc = input_stack::get(NULL)) != '\n') + while ((cc = input_stack::get(0)) != '\n') if (cc == EOF) { type = TOKEN_EOF; return; @@ -1514,15 +1761,21 @@ void token::next() case '$': { symbol nm = read_escape_name(); - if (!nm.is_null()) + if (!(nm.is_null() || nm.is_empty())) interpolate_arg(nm); break; } case '*': { - symbol nm = read_escape_name(); - if (!nm.is_null()) - interpolate_string(nm); + symbol nm = read_escape_name(WITH_ARGS); + if (!(nm.is_null() || nm.is_empty())) { + if (have_string_arg) { + have_string_arg = 0; + interpolate_string_with_args(nm); + } + else + interpolate_string(nm); + } break; } case 'a': @@ -1551,7 +1804,8 @@ void token::next() return; case 'd': type = TOKEN_NODE; - nd = new vmotion_node(curenv->get_size()/2); + nd = new vmotion_node(curenv->get_size() / 2, + curenv->get_fill_color()); return; case 'D': nd = read_draw_node(); @@ -1565,23 +1819,33 @@ void token::next() goto handle_escape_char; case 'f': { - symbol s = read_escape_name(); + symbol s = read_escape_name(ALLOW_EMPTY); if (s.is_null()) break; const char *p; for (p = s.contents(); *p != '\0'; p++) if (!csdigit(*p)) break; - if (*p) + if (*p || s.is_empty()) curenv->set_font(s); else curenv->set_font(atoi(s.contents())); + if (!compatible_flag) + have_input = 1; + break; + } + case 'F': + { + symbol s = read_escape_name(ALLOW_EMPTY); + if (s.is_null()) + break; + curenv->set_family(s); break; } case 'g': { symbol s = read_escape_name(); - if (!s.is_null()) + if (!(s.is_null() || s.is_empty())) interpolate_number_format(s); break; } @@ -1589,15 +1853,27 @@ void token::next() if (!get_delim_number(&x, 'm')) break; type = TOKEN_NODE; - nd = new hmotion_node(x); + nd = new hmotion_node(x, curenv->get_fill_color()); return; case 'H': - if (get_delim_number(&x, 'z', curenv->get_requested_point_size())) - curenv->set_char_height(x); + // don't take height increments relative to previous height if + // in compatibility mode + if (!compatible_flag && curenv->get_char_height()) + { + if (get_delim_number(&x, 'z', curenv->get_char_height())) + curenv->set_char_height(x); + } + else + { + if (get_delim_number(&x, 'z', curenv->get_requested_point_size())) + curenv->set_char_height(x); + } + if (!compatible_flag) + have_input = 1; break; case 'k': nm = read_escape_name(); - if (nm.is_null()) + if (nm.is_null() || nm.is_empty()) break; type = TOKEN_MARK_INPUT; return; @@ -1617,11 +1893,21 @@ void token::next() nd = new vline_node(x, n); return; } + case 'm': + do_glyph_color(read_escape_name(ALLOW_EMPTY)); + if (!compatible_flag) + have_input = 1; + break; + case 'M': + do_fill_color(read_escape_name(ALLOW_EMPTY)); + if (!compatible_flag) + have_input = 1; + break; case 'n': { int inc; symbol nm = read_increment_and_escape_name(&inc); - if (!nm.is_null()) + if (!(nm.is_null() || nm.is_empty())) interpolate_number_reg(nm, inc); break; } @@ -1635,7 +1921,7 @@ void token::next() type = TOKEN_NODE; return; case 'O': - nd = do_suppress(); + nd = do_suppress(read_escape_name()); if (!nd) break; type = TOKEN_NODE; @@ -1645,18 +1931,24 @@ void token::next() return; case 'r': type = TOKEN_NODE; - nd = new vmotion_node(-curenv->get_size()); + nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color()); return; case 'R': do_register(); + if (!compatible_flag) + have_input = 1; break; case 's': if (read_size(&x)) curenv->set_size(x); + if (!compatible_flag) + have_input = 1; break; case 'S': if (get_delim_number(&x, 0)) curenv->set_char_slant(x); + if (!compatible_flag) + have_input = 1; break; case 't': type = TOKEN_NODE; @@ -1664,18 +1956,19 @@ void token::next() return; case 'u': type = TOKEN_NODE; - nd = new vmotion_node(-curenv->get_size()/2); + nd = new vmotion_node(-curenv->get_size() / 2, + curenv->get_fill_color()); return; case 'v': if (!get_delim_number(&x, 'v')) break; type = TOKEN_NODE; - nd = new vmotion_node(x); + nd = new vmotion_node(x, curenv->get_fill_color()); return; case 'V': - { + { symbol nm = read_escape_name(); - if (!nm.is_null()) + if (!(nm.is_null() || nm.is_empty())) interpolate_environment_variable(nm); break; } @@ -1697,7 +1990,7 @@ void token::next() case 'Y': { symbol s = read_escape_name(); - if (s.is_null()) + if (s.is_null() || s.is_empty()) break; request_or_macro *p = lookup_request(s); macro *m = p->to_macro(); @@ -1712,9 +2005,9 @@ void token::next() case 'z': { next(); - if (type == TOKEN_NODE) - nd = new zero_width_node(nd); - else { + if (type == TOKEN_NODE) + nd = new zero_width_node(nd); + else { charinfo *ci = get_char(1); if (ci == 0) break; @@ -1723,7 +2016,7 @@ void token::next() break; nd = new zero_width_node(gn); type = TOKEN_NODE; - } + } return; } case 'Z': @@ -1741,7 +2034,7 @@ void token::next() case '[': if (!compatible_flag) { nm = read_long_escape_name(); - if (nm.is_null()) + if (nm.is_null() || nm.is_empty()) break; type = TOKEN_SPECIAL; return; @@ -1817,6 +2110,7 @@ int token::delimiter(int err) case TOKEN_NODE: case TOKEN_SPACE: case TOKEN_STRETCHABLE_SPACE: + case TOKEN_UNSTRETCHABLE_SPACE: case TOKEN_TAB: case TOKEN_NEWLINE: if (err) @@ -1871,12 +2165,16 @@ const char *token::description() return "`\\p'"; case TOKEN_STRETCHABLE_SPACE: return "`\\~'"; + case TOKEN_UNSTRETCHABLE_SPACE: + return "`\\ '"; case TOKEN_TAB: return "a tab character"; case TOKEN_TRANSPARENT: return "`\\!'"; case TOKEN_TRANSPARENT_DUMMY: return "`\\)'"; + case TOKEN_ZERO_WIDTH_BREAK: + return "`\\:'"; case TOKEN_EOF: return "end of input"; default: @@ -2015,19 +2313,21 @@ void exit_troff() tok.next(); process_input_stack(); end_diversions(); - done_end_macro = 1; - topdiv->set_ejecting(); - static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' }; - input_stack::push(make_temp_iterator((char *)buf)); - topdiv->space(topdiv->get_page_length(), 1); - tok.next(); - process_input_stack(); - seen_last_page_ejector = 1; // should be set already - topdiv->set_ejecting(); - push_page_ejector(); - topdiv->space(topdiv->get_page_length(), 1); - tok.next(); - process_input_stack(); + if (topdiv->get_page_length() > 0) { + done_end_macro = 1; + topdiv->set_ejecting(); + static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' }; + input_stack::push(make_temp_iterator((char *)buf)); + topdiv->space(topdiv->get_page_length(), 1); + tok.next(); + process_input_stack(); + seen_last_page_ejector = 1; // should be set already + topdiv->set_ejecting(); + push_page_ejector(); + topdiv->space(topdiv->get_page_length(), 1); + tok.next(); + process_input_stack(); + } // This will only happen if a trap-invoked macro starts a diversion, // or if vertical position traps have been disabled. cleanup_and_exit(0); @@ -2095,7 +2395,7 @@ inline int possibly_handle_first_page_transition() static int transparent_translate(int cc) { - if (!illegal_input_char(cc)) { + if (!invalid_input_char(cc)) { charinfo *ci = charset_table[cc]; switch (ci->get_special_translation(1)) { case charinfo::TRANSLATE_SPACE: @@ -2230,9 +2530,9 @@ void process_input_stack() case token::TOKEN_CHAR: { unsigned char ch = tok.c; - if (bol && - (ch == curenv->control_char - || ch == curenv->no_break_control_char)) { + if (bol && !have_input + && (ch == curenv->control_char + || ch == curenv->no_break_control_char)) { break_flag = ch == curenv->control_char; // skip tabs as well as spaces here do { @@ -2244,6 +2544,7 @@ void process_input_stack() else interpolate_macro(nm); suppress_next = 1; + have_input = 0; } else { if (possibly_handle_first_page_transition()) @@ -2286,11 +2587,13 @@ void process_input_stack() } case token::TOKEN_NEWLINE: { - if (bol && !curenv->get_prev_line_interrupted()) + if (bol && !have_input + && !curenv->get_prev_line_interrupted()) trapping_blank_line(); else { curenv->newline(); bol = 1; + have_input = 0; } break; } @@ -2318,6 +2621,7 @@ void process_input_stack() break; } suppress_next = 1; + have_input = 0; break; } case token::TOKEN_SPACE: @@ -2326,6 +2630,9 @@ void process_input_stack() ; else if (bol && !curenv->get_prev_line_interrupted()) { int nspaces = 0; + // save space_width now so that it isn't changed by \f or \s + // which we wouldn't notice here + hunits space_width = curenv->get_space_width(); do { nspaces += tok.nspaces(); tok.next(); @@ -2335,8 +2642,8 @@ void process_input_stack() else { push_token(tok); curenv->do_break(); - curenv->add_node(new hmotion_node(curenv->get_space_width() - * nspaces)); + curenv->add_node(new hmotion_node(space_width * nspaces, + curenv->get_fill_color())); bol = 0; } } @@ -2375,6 +2682,7 @@ void process_input_stack() { trap_bol_stack.push(bol); bol = 1; + have_input = 0; break; } case token::TOKEN_END_TRAP: @@ -2388,9 +2696,9 @@ void process_input_stack() way to do it. Doing an output_pending_lines() whenever a TOKEN_END_TRAP is detected doesn't work: for example, - .wh -1i x - .de x - 'bp + .wh -1i x + .de x + 'bp .. .wh -.5i y .de y @@ -2401,8 +2709,8 @@ void process_input_stack() .sp |\n(.pu-1i-.5v a\%very\%very\%long\%word - will print all but the first lines from the word immediately - after the footer, rather than on the next page. */ + will print all but the first lines from the word immediately + after the footer, rather than on the next page. */ if (trap_bol_stack.is_empty()) curenv->output_pending_lines(); @@ -2468,6 +2776,8 @@ public: char_list(); ~char_list(); void append(unsigned char); + void set(unsigned char, int); + unsigned char get(int); int length(); private: unsigned char *ptr; @@ -2514,6 +2824,44 @@ void char_list::append(unsigned char c) len++; } +void char_list::set(unsigned char c, int offset) +{ + assert(len > offset); + // optimization for access at the end + int boundary = len - len % char_block::SIZE; + if (offset >= boundary) { + *(tail->s + offset - boundary) = c; + return; + } + char_block *tem = head; + int l = 0; + for (;;) { + l += char_block::SIZE; + if (l > offset) { + *(tem->s + offset % char_block::SIZE) = c; + return; + } + tem = tem->next; + } +} + +unsigned char char_list::get(int offset) +{ + assert(len > offset); + // optimization for access at the end + int boundary = len - len % char_block::SIZE; + if (offset >= boundary) + return *(tail->s + offset - boundary); + char_block *tem = head; + int l = 0; + for (;;) { + l += char_block::SIZE; + if (l > offset) + return *(tem->s + offset % char_block::SIZE); + tem = tem->next; + } +} + class node_list { node *head; node *tail; @@ -2586,12 +2934,14 @@ macro::macro() filename = 0; lineno = 0; } - length = 0; + len = 0; + empty_macro = 1; p = 0; } macro::macro(const macro &m) -: p(m.p), filename(m.filename), lineno(m.lineno), length(m.length) +: p(m.p), filename(m.filename), lineno(m.lineno), len(m.len), + empty_macro(m.empty_macro) { if (p != 0) p->count++; @@ -2607,7 +2957,8 @@ macro ¯o::operator=(const macro &m) p = m.p; filename = m.filename; lineno = m.lineno; - length = m.length; + len = m.len; + empty_macro = m.empty_macro; return *this; } @@ -2616,14 +2967,34 @@ void macro::append(unsigned char c) assert(c != 0); if (p == 0) p = new macro_header; - if (p->cl.length() != length) { - macro_header *tem = p->copy(length); + if (p->cl.length() != len) { + macro_header *tem = p->copy(len); if (--(p->count) <= 0) delete p; p = tem; } p->cl.append(c); - ++length; + ++len; + if (c != COMPATIBLE_SAVE && c != COMPATIBLE_RESTORE) + empty_macro = 0; +} + +void macro::set(unsigned char c, int offset) +{ + assert(p != 0); + assert(c != 0); + p->cl.set(c, offset); +} + +unsigned char macro::get(int offset) +{ + assert(p != 0); + return p->cl.get(offset); +} + +int macro::length() +{ + return len; } void macro::append_str(const char *s) @@ -2643,15 +3014,16 @@ void macro::append(node *n) assert(n != 0); if (p == 0) p = new macro_header; - if (p->cl.length() != length) { - macro_header *tem = p->copy(length); + if (p->cl.length() != len) { + macro_header *tem = p->copy(len); if (--(p->count) <= 0) delete p; p = tem; } p->cl.append(0); p->nl.append(n); - ++length; + ++len; + empty_macro = 0; } void macro::append_unsigned(unsigned int i) @@ -2673,7 +3045,7 @@ void macro::append_int(int i) void macro::print_size() { - errprint("%1", length); + errprint("%1", len); } // make a copy of the first n bytes @@ -2740,9 +3112,10 @@ public: }; string_iterator::string_iterator(const macro &m, const char *p, symbol s) -: mac(m), how_invoked(p), newline_flag(0), lineno(1), nm(s) +: mac(m), how_invoked(p), + newline_flag(0), lineno(1), nm(s) { - count = mac.length; + count = mac.len; if (count != 0) { bp = mac.p->cl.head; nd = mac.p->nl.head; @@ -2811,8 +3184,6 @@ int string_iterator::peek() if (count <= 0) return EOF; const unsigned char *p = eptr; - if (count <= 0) - return EOF; if (p >= bp->s + char_block::SIZE) { p = bp->next->s; } @@ -2940,7 +3311,7 @@ input_iterator *make_temp_iterator(const char *s) } } -// this is used when macros are interpolated using the .macro_name notation +// this is used when macros with arguments are interpolated struct arg_list { macro mac; @@ -3012,11 +3383,11 @@ void macro_iterator::shift(int n) int operator==(const macro &m1, const macro &m2) { - if (m1.length != m2.length) + if (m1.len != m2.len) return 0; string_iterator iter1(m1); string_iterator iter2(m2); - int n = m1.length; + int n = m1.len; while (--n >= 0) { node *nd1 = 0; int c1 = iter1.get(&nd1); @@ -3084,7 +3455,7 @@ static void decode_args(macro_iterator *mi) if (!tok.newline() && !tok.eof()) { node *n; int c = get_copy(&n); - for (;;) { + for (;;) { while (c == ' ') c = get_copy(&n); if (c == '\n' || c == EOF) @@ -3126,6 +3497,56 @@ static void decode_args(macro_iterator *mi) } } +static void decode_string_args(macro_iterator *mi) +{ + node *n; + int c = get_copy(&n); + for (;;) { + while (c == ' ') + c = get_copy(&n); + if (c == '\n' || c == EOF) { + error("missing `]'"); + break; + } + if (c == ']') + break; + macro arg; + int quote_input_level = 0; + int done_tab_warning = 0; + if (c == '\"') { + quote_input_level = input_stack::get_level(); + c = get_copy(&n); + } + while (c != EOF && c != '\n' + && !(c == ']' && quote_input_level == 0) + && !(c == ' ' && quote_input_level == 0)) { + if (quote_input_level > 0 && c == '\"' + && input_stack::get_level() == quote_input_level) { + c = get_copy(&n); + if (c == '"') { + arg.append(c); + c = get_copy(&n); + } + else + break; + } + else { + if (c == 0) + arg.append(n); + else { + if (c == '\t' && quote_input_level == 0 && !done_tab_warning) { + warning(WARN_TAB, "tab character in unquoted string argument"); + done_tab_warning = 1; + } + arg.append(c); + } + c = get_copy(&n); + } + } + mi->add_arg(arg); + } +} + void macro::invoke(symbol nm) { macro_iterator *mi = new macro_iterator(nm, *this); @@ -3141,7 +3562,7 @@ macro *macro::to_macro() int macro::empty() { - return length == 0; + return empty_macro == 1; } macro_iterator::macro_iterator(symbol s, macro &m, const char *how_invoked) @@ -3209,16 +3630,16 @@ void read_request() int reading_from_terminal = isatty(fileno(stdin)); int had_prompt = 0; if (!tok.newline() && !tok.eof()) { - int c = get_copy(NULL); + int c = get_copy(0); while (c == ' ') - c = get_copy(NULL); + c = get_copy(0); while (c != EOF && c != '\n' && c != ' ') { - if (!illegal_input_char(c)) { + if (!invalid_input_char(c)) { if (reading_from_terminal) fputc(c, stderr); had_prompt = 1; } - c = get_copy(NULL); + c = get_copy(0); } if (c == ' ') { tok.make_space(); @@ -3234,8 +3655,8 @@ void read_request() int nl = 0; int c; while ((c = getchar()) != EOF) { - if (illegal_input_char(c)) - warning(WARN_INPUT, "illegal input character code %1", int(c)); + if (invalid_input_char(c)) + warning(WARN_INPUT, "invalid input character code %1", int(c)); else { if (c == '\n') { if (nl) @@ -3254,7 +3675,10 @@ void read_request() tok.next(); } -void do_define_string(int append) +enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE }; +enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT, CALLING_DISABLE_COMP }; + +void do_define_string(define_mode mode, calling_mode calling) { symbol nm; node *n; @@ -3282,8 +3706,10 @@ void do_define_string(int append) macro mac; request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); macro *mm = rm ? rm->to_macro() : 0; - if (append && mm) + if (mode == DEFINE_APPEND && mm) mac = *mm; + if (calling == CALLING_DISABLE_COMP) + mac.append(COMPATIBLE_SAVE); while (c != '\n' && c != EOF) { if (c == 0) mac.append(n); @@ -3295,21 +3721,33 @@ void do_define_string(int append) mm = new macro; request_dictionary.define(nm, mm); } + if (calling == CALLING_DISABLE_COMP) + mac.append(COMPATIBLE_RESTORE); *mm = mac; tok.next(); } void define_string() { - do_define_string(0); + do_define_string(DEFINE_NORMAL, CALLING_NORMAL); +} + +void define_nocomp_string() +{ + do_define_string(DEFINE_NORMAL, CALLING_DISABLE_COMP); } void append_string() { - do_define_string(1); + do_define_string(DEFINE_APPEND, CALLING_NORMAL); } -void define_character() +void append_nocomp_string() +{ + do_define_string(DEFINE_APPEND, CALLING_DISABLE_COMP); +} + +void do_define_character(int fallback) { node *n; int c; @@ -3343,12 +3781,22 @@ void define_character() m->append((unsigned char)c); c = get_copy(&n); } - m = ci->set_macro(m); + m = ci->set_macro(m, fallback); if (m) delete m; tok.next(); } +void define_character() +{ + do_define_character(0); +} + +void define_fallback_character() +{ + do_define_character(1); +} + static void remove_character() { tok.skip(); @@ -3371,13 +3819,26 @@ static void interpolate_string(symbol nm) request_or_macro *p = lookup_request(nm); macro *m = p->to_macro(); if (!m) - error("you can only invoke a string using \\*"); + error("you can only invoke a string or macro using \\*"); else { string_iterator *si = new string_iterator(*m, "string", nm); input_stack::push(si); } } +static void interpolate_string_with_args(symbol s) +{ + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) + error("you can only invoke a string or macro using \\*"); + else { + macro_iterator *mi = new macro_iterator(s, *m); + decode_string_args(mi); + input_stack::push(mi); + } +} + /* This class is used for the implementation of \$@. It is used for each of the closing double quotes. It artificially increases the input level by 2, so that the closing double quote will appear to have @@ -3473,9 +3934,6 @@ void handle_initial_title() // this should be local to define_macro, but cfront 1.2 doesn't support that static symbol dot_symbol("."); -enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE }; -enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT, CALLING_DISABLE_COMP }; - void do_define_macro(define_mode mode, calling_mode calling) { symbol nm, term; @@ -3537,11 +3995,17 @@ void do_define_macro(define_mode mode, calling_mode calling) const char *s = term.contents(); int d = 0; // see if it matches term - int i; - for (i = 0; s[i] != 0; i++) { - d = get_copy(&n); - if ((unsigned char)s[i] != d) - break; + int i = 0; + if (s[0] != 0) { + while ((d = get_copy(&n)) == ' ' || d == '\t') + ; + if ((unsigned char)s[0] == d) { + for (i = 1; s[i] != 0; i++) { + d = get_copy(&n); + if ((unsigned char)s[i] != d) + break; + } + } } if (s[i] == 0 && ((i == 2 && compatible_flag) @@ -3625,6 +4089,11 @@ void append_macro() do_define_macro(DEFINE_APPEND, CALLING_NORMAL); } +void append_indirect_macro() +{ + do_define_macro(DEFINE_APPEND, CALLING_INDIRECT); +} + void append_nocomp_macro() { do_define_macro(DEFINE_APPEND, CALLING_DISABLE_COMP); @@ -3680,75 +4149,121 @@ void chop_macro() macro *m = p->to_macro(); if (!m) error("cannot chop request"); - else if (m->length == 0) + else if (m->empty()) error("cannot chop empty macro"); - else - m->length -= 1; + else { + int have_restore = 0; + // we have to check for additional save/restore pairs which could be + // there due to empty am1 requests. + for (;;) { + if (m->get(m->len - 1) != COMPATIBLE_RESTORE) + break; + have_restore = 1; + m->len -= 1; + if (m->get(m->len - 1) != COMPATIBLE_SAVE) + break; + have_restore = 0; + m->len -= 1; + if (m->len == 0) + break; + } + if (m->len == 0) + error("cannot chop empty macro"); + else { + if (have_restore) + m->set(COMPATIBLE_RESTORE, m->len - 1); + else + m->len -= 1; + } + } } skip_line(); } -void substring_macro() +void substring_request() { - int start; + int start; // 0, 1, ..., n-1 or -1, -2, ... symbol s = get_name(1); if (!s.is_null() && get_integer(&start)) { request_or_macro *p = lookup_request(s); macro *m = p->to_macro(); if (!m) - error("cannot substring request"); + error("cannot apply `substring' on a request"); else { - if (start <= 0) - start += m->length; - else - start--; - int end = 0; + int end = -1; if (!has_arg() || get_integer(&end)) { - if (end <= 0) - end += m->length; - else - end--; + int real_length = 0; // 1, 2, ..., n + string_iterator iter1(*m); + for (int l = 0; l < m->len; l++) { + int c = iter1.get(0); + if (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE) + continue; + if (c == EOF) + break; + real_length++; + } + if (start < 0) + start += real_length; + if (end < 0) + end += real_length; if (start > end) { int tem = start; start = end; end = tem; } - if (start >= m->length || start == end) { - m->length = 0; + if (start >= real_length || end < 0) { + warning(WARN_RANGE, + "start and end index of substring out of range"); + m->len = 0; if (m->p) { if (--(m->p->count) <= 0) delete m->p; m->p = 0; } + skip_line(); + return; } - else if (start == 0) - m->length = end; - else { - string_iterator iter(*m); - int i; - for (i = 0; i < start; i++) - if (iter.get(0) == EOF) - break; - macro mac; - for (; i < end; i++) { - node *nd; - int c = iter.get(&nd); - if (c == EOF) - break; - if (c == 0) - mac.append(nd); - else - mac.append((unsigned char)c); - } - *m = mac; + if (start < 0) { + warning(WARN_RANGE, + "start index of substring out of range, set to 0"); + start = 0; + } + if (end >= real_length) { + warning(WARN_RANGE, + "end index of substring out of range, set to string length"); + end = real_length - 1; } + // now extract the substring + string_iterator iter(*m); + int i; + for (i = 0; i < start; i++) { + int c = iter.get(0); + while (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE) + c = iter.get(0); + if (c == EOF) + break; + } + macro mac; + for (; i <= end; i++) { + node *nd; + int c = iter.get(&nd); + while (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE) + c = iter.get(0); + if (c == EOF) + break; + if (c == 0) + mac.append(nd); + else + mac.append((unsigned char)c); + } + *m = mac; } } } skip_line(); } -void length_macro() +void length_request() { symbol ret; ret = get_name(1); @@ -3778,12 +4293,12 @@ void length_macro() ++len; c = get_copy(&n); } - tok.next(); reg *r = (reg*)number_reg_dictionary.lookup(ret); if (r) r->set_value(len); else set_number_reg(ret, len); + tok.next(); } void asciify_macro() @@ -3900,19 +4415,20 @@ static int get_line_arg(units *n, int si, charinfo **cp) { token start; start.next(); - if (start.delimiter(1)) { - tok.next(); - if (get_number(n, si)) { - if (tok.dummy() || tok.transparent_dummy()) - tok.next(); - if (start != tok) { - *cp = tok.get_char(1); - tok.next(); - } - if (start != tok) - warning(WARN_DELIM, "closing delimiter does not match"); - return 1; + int start_level = input_stack::get_level(); + if (!start.delimiter(1)) + return 0; + tok.next(); + if (get_number(n, si)) { + if (tok.dummy() || tok.transparent_dummy()) + tok.next(); + if (!(start == tok && input_stack::get_level() == start_level)) { + *cp = tok.get_char(1); + tok.next(); } + if (!(start == tok && input_stack::get_level() == start_level)) + warning(WARN_DELIM, "closing delimiter does not match"); + return 1; } return 0; } @@ -4085,65 +4601,6 @@ static symbol get_delim_name() } } -static symbol get_delim_file_name() -{ - token start; - start.next(); - if (start.eof()) { - error("end of input at start of delimited file name"); - return NULL_SYMBOL; - } - if (start.newline()) { - error("can't delimit file name with a newline"); - return NULL_SYMBOL; - } - int start_level = input_stack::get_level(); - char abuf[ABUF_SIZE]; - char *buf = abuf; - int buf_size = ABUF_SIZE; - int i = 0; - for (;;) { - if (i + 1 > buf_size) { - if (buf == abuf) { - buf = new char[ABUF_SIZE*2]; - memcpy(buf, abuf, buf_size); - buf_size = ABUF_SIZE*2; - } - else { - char *old_buf = buf; - buf = new char[buf_size*2]; - memcpy(buf, old_buf, buf_size); - buf_size *= 2; - a_delete old_buf; - } - } - tok.next(); - if (tok.ch() == ']' && input_stack::get_level() == start_level) - break; - if ((buf[i] = tok.ch()) == 0) { - error("missing delimiter (got %1)", tok.description()); - if (buf != abuf) - a_delete buf; - return NULL_SYMBOL; - } - i++; - } - buf[i] = '\0'; - if (buf == abuf) { - if (i == 0) { - error("empty delimited file name"); - return NULL_SYMBOL; - } - else - return symbol(buf); - } - else { - symbol s(buf); - a_delete buf; - return s; - } -} - // Implement \R static void do_register() @@ -4331,9 +4788,14 @@ static void encode_char(macro *mac, char c) mac->append(')'); } } - else { - error("%1 is illegal within \\X", tok.description()); - } + else if (tok.stretchable_space() + || tok.unstretchable_space()) + mac->append(' '); + else if (!(tok.hyphen_indicator() + || tok.dummy() + || tok.transparent_dummy() + || tok.zero_width_break())) + error("%1 is invalid within \\X", tok.description()); } else { if ((font::use_charnames_in_special) && (c == '\\')) { @@ -4380,29 +4842,48 @@ node *do_special() return new special_node(mac); } -node *do_suppress() +void output_request() { + if (!tok.newline() && !tok.eof()) { + int c; + for (;;) { + c = get_copy(0); + if (c == '"') { + c = get_copy(0); + break; + } + if (c != ' ' && c != '\t') + break; + } + for (; c != '\n' && c != EOF; c = get_copy(0)) + topdiv->transparent_output(c); + topdiv->transparent_output('\n'); + } tok.next(); - int c = tok.ch(); - if (c != '[') { - error("argument(s) of \\O must be enclosed in brackets (got %1)", - char(c)); +} + +extern int image_no; // from node.cc + +static node *do_suppress(symbol nm) +{ + if (nm.is_null() || nm.is_empty()) { + error("expecting an argument to escape \\O"); return 0; } - tok.next(); - c = tok.ch(); - tok.next(); - switch (c) { + const char *s = nm.contents(); + switch (*s) { case '0': - if (begin_level == 1) + if (begin_level == 0) + // suppress generation of glyphs return new suppress_node(0, 0); break; case '1': - if (begin_level == 1) + if (begin_level == 0) + // enable generation of glyphs return new suppress_node(1, 0); break; case '2': - if (begin_level == 1) + if (begin_level == 0) return new suppress_node(1, 1); break; case '3': @@ -4411,20 +4892,33 @@ node *do_suppress() case '4': begin_level--; break; - case '5': { - symbol filename = get_delim_file_name(); - tok.next(); - if (filename.is_null()) { - error("missing filename as second argument to \\O"); - return 0; + case '5': + { + s++; // move over '5' + char position = *s; + if (*s == (char)0) { + error("missing position and filename in \\O"); + return 0; + } + if (!(position == 'l' + || position == 'r' + || position == 'c' + || position == 'i')) { + error("l, r, c, or i position expected (got %1 in \\O)", position); + return 0; + } + s++; // onto image name + if (s == (char *)0) { + error("missing image name for \\O"); + return 0; + } + image_no++; + if (begin_level == 0) + return new suppress_node(symbol(s), position, image_no); } - if (begin_level == 1) - return new suppress_node(filename, 'i'); - return 0; break; - } default: - error("`%1' is an invalid argument to \\O", char(c)); + error("`%1' is an invalid argument to \\O", *s); } return 0; } @@ -4434,7 +4928,7 @@ void special_node::tprint(troff_output_file *out) tprint_start(out); string_iterator iter(mac); for (;;) { - int c = iter.get(NULL); + int c = iter.get(0); if (c == EOF) break; for (const char *s = ::asciify(c); *s; s++) @@ -4484,7 +4978,7 @@ static void skip_alternative() level++; int c; for (;;) { - c = input_stack::get(NULL); + c = input_stack::get(0); if (c == EOF) break; if (c == ESCAPE_LEFT_BRACE) @@ -4492,7 +4986,7 @@ static void skip_alternative() else if (c == ESCAPE_RIGHT_BRACE) --level; else if (c == escape_char && escape_char > 0) - switch(input_stack::get(NULL)) { + switch(input_stack::get(0)) { case '{': ++level; break; @@ -4500,7 +4994,7 @@ static void skip_alternative() --level; break; case '"': - while ((c = input_stack::get(NULL)) != '\n' && c != EOF) + while ((c = input_stack::get(0)) != '\n' && c != EOF) ; } /* @@ -4574,6 +5068,16 @@ int do_if_request() ? request_dictionary.lookup(nm) != 0 : number_reg_dictionary.lookup(nm) != 0); } + else if (c == 'm') { + tok.next(); + symbol nm = get_long_name(1); + if (nm.is_null()) { + skip_alternative(); + return 0; + } + result = (nm == default_symbol + || color_dictionary.lookup(nm) != 0); + } else if (c == 'c') { tok.next(); tok.skip(); @@ -4660,65 +5164,6 @@ void else_request() } } -/* - * begin - if this is the outermost html_begin request then execute the - * rest of the line, else skip line - */ - -void begin() -{ - begin_level++; - if (begin_level == 1) - begin_alternative(); - else - skip_alternative(); -} - -/* - * end - if this is the outermost html_end request then execute the - * rest of the line, else skip line - */ - -void end() -{ - begin_level--; - if (begin_level == 0) - begin_alternative(); - else - skip_alternative(); - if (begin_level < 0) - begin_level = 0; -} - -/* - * image - implements the directive `.image {l|r|c|i} filename' - * which places the filename into a node which is later - * written out - * - * . either as a special in the form of an image tag for -Thtml - * . or as an image region definition for all other devices - * - */ - -void image() -{ - if (has_arg()) { - char position = tok.ch(); - if (!(position == 'l' - || position == 'r' - || position == 'c' - || position == 'i')) { - error("l, r, c, or i expected (got %1)", tok.description()); - position = 'c'; - } - tok.next(); - symbol filename = get_long_name(1); - if (!filename.is_null()) - curenv->add_node(new suppress_node(filename, position)); - } - skip_line(); -} - static int while_depth = 0; static int while_break_flag = 0; @@ -4766,7 +5211,7 @@ void while_request() input_stack::push(new string_iterator(mac, "while loop")); tok.next(); if (!do_if_request()) { - while (input_stack::get(NULL) != EOF) + while (input_stack::get(0) != EOF) ; break; } @@ -4790,7 +5235,7 @@ void while_break_request() } else { while_break_flag = 1; - while (input_stack::get(NULL) != EOF) + while (input_stack::get(0) != EOF) ; tok.next(); } @@ -4803,7 +5248,7 @@ void while_continue_request() skip_line(); } else { - while (input_stack::get(NULL) != EOF) + while (input_stack::get(0) != EOF) ; tok.next(); } @@ -4846,12 +5291,12 @@ void pipe_source() error("missing command"); else { int c; - while ((c = get_copy(NULL)) == ' ' || c == '\t') + while ((c = get_copy(0)) == ' ' || c == '\t') ; int buf_size = 24; char *buf = new char[buf_size]; int buf_used = 0; - for (; c != '\n' && c != EOF; c = get_copy(NULL)) { + for (; c != '\n' && c != EOF; c = get_copy(0)) { const char *s = asciify(c); int slen = strlen(s); if (buf_used + slen + 1> buf_size) { @@ -4938,7 +5383,7 @@ int ps_get_line(char *buf, FILE *fp, const char* filename) int err = 0; while (c != '\r' && c != '\n' && c != EOF) { if ((c < 0x1b && !white_space(c)) || c == 0x7f) - error("illegal input character code %1 in `%2'", int(c), filename); + error("invalid input character code %1 in `%2'", int(c), filename); else if (i < PS_LINE_MAX) buf[i++] = c; else if (!err) { @@ -4991,14 +5436,15 @@ void do_ps_file(FILE *fp, const char* filename) if (res == 1) { assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); return; - } else if (res == 2) { + } + else if (res == 2) { bb_at_end = 1; break; } else { error("the arguments to the %%%%BoundingBox comment in `%1' are bad", filename); - return; + return; } } } @@ -5132,8 +5578,12 @@ const char *asciify(int c) case ESCAPE_COLON: buf[1] = ':'; break; + case COMPATIBLE_SAVE: + case COMPATIBLE_RESTORE: + buf[0] = '\0'; + break; default: - if (illegal_input_char(c)) + if (invalid_input_char(c)) buf[0] = '\0'; else buf[0] = c; @@ -5159,7 +5609,7 @@ const char *input_char_description(int c) return "a node"; } static char buf[sizeof("magic character code ") + 1 + INT_DIGITS]; - if (illegal_input_char(c)) { + if (invalid_input_char(c)) { const char *s = asciify(c); if (*s) { buf[0] = '`'; @@ -5187,15 +5637,15 @@ void do_terminal(int newline, int string_like) if (!tok.newline() && !tok.eof()) { int c; for (;;) { - c = get_copy(NULL); + c = get_copy(0); if (string_like && c == '"') { - c = get_copy(NULL); + c = get_copy(0); break; } if (c != ' ' && c != '\t') break; } - for (; c != '\n' && c != EOF; c = get_copy(NULL)) + for (; c != '\n' && c != EOF; c = get_copy(0)) fputs(asciify(c), stderr); } if (newline) @@ -5278,7 +5728,9 @@ void close_request() skip_line(); } -void write_request() +// .write and .writec + +void do_write_request(int newline) { symbol stream = get_name(1); if (stream.is_null()) { @@ -5292,17 +5744,28 @@ void write_request() return; } int c; - while ((c = get_copy(NULL)) == ' ') + while ((c = get_copy(0)) == ' ') ; if (c == '"') - c = get_copy(NULL); - for (; c != '\n' && c != EOF; c = get_copy(NULL)) + c = get_copy(0); + for (; c != '\n' && c != EOF; c = get_copy(0)) fputs(asciify(c), fp); - fputc('\n', fp); + if (newline) + fputc('\n', fp); fflush(fp); tok.next(); } +void write_request() +{ + do_write_request(1); +} + +void write_request_continue() +{ + do_write_request(0); +} + void write_macro_request() { symbol stream = get_name(1); @@ -5338,6 +5801,47 @@ void write_macro_request() skip_line(); } +void warnscale_request() +{ + if (has_arg()) { + char c = tok.ch(); + if (c == 'u') + warn_scale = 1.0; + else if (c == 'i') + warn_scale = (double)units_per_inch; + else if (c == 'c') + warn_scale = (double)units_per_inch / 2.54; + else if (c == 'p') + warn_scale = (double)units_per_inch / 72.0; + else if (c == 'P') + warn_scale = (double)units_per_inch / 6.0; + else { + warning(WARN_SCALE, + "invalid scaling indicator `%1', using `i' instead", c); + c = 'i'; + } + warn_scaling_indicator = c; + } + skip_line(); +} + +void spreadwarn_request() +{ + hunits n; + if (has_arg() && get_hunits(&n, 'm')) { + if (n < 0) + n = 0; + hunits em = curenv->get_size(); + spread_limit = (double)n.to_units() + / (em.is_zero() ? hresolution : em.to_units()); + } + else + spread_limit = -spread_limit - 1; // no arg toggles on/off without + // changing value; we mirror at + // -0.5 to make zero a valid value + skip_line(); +} + static void init_charset_table() { char buf[16]; @@ -5369,7 +5873,13 @@ static void init_charset_table() page_character = charset_table['%']; } -static void do_translate(int translate_transparent) +static void init_hpf_code_table() +{ + for (int i = 0; i < 256; i++) + hpf_code_table[i] = i; +} + +static void do_translate(int translate_transparent, int translate_input) { tok.skip(); while (!tok.newline() && !tok.eof()) { @@ -5408,9 +5918,9 @@ static void do_translate(int translate_transparent) if (ci2 == 0) break; if (ci1 == ci2) - ci1->set_translation(0, translate_transparent); + ci1->set_translation(0, translate_transparent, translate_input); else - ci1->set_translation(ci2, translate_transparent); + ci1->set_translation(ci2, translate_transparent, translate_input); } tok.next(); } @@ -5419,12 +5929,17 @@ static void do_translate(int translate_transparent) void translate() { - do_translate(1); + do_translate(1, 0); } void translate_no_transparent() { - do_translate(0); + do_translate(0, 0); +} + +void translate_input() +{ + do_translate(1, 1); } void char_flags() @@ -5463,12 +5978,42 @@ void hyphenation_code() break; } ci->set_hyphenation_code(c); + if (ci->get_translation() + && ci->get_translation()->get_translation_input()) + ci->get_translation()->set_hyphenation_code(c); tok.next(); tok.skip(); } skip_line(); } +void hyphenation_patterns_file_code() +{ + tok.skip(); + while (!tok.newline() && !tok.eof()) { + int n1, n2; + if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) { + if (!has_arg()) { + error("missing output hyphenation code"); + break; + } + if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) { + hpf_code_table[n1] = n2; + tok.skip(); + } + else { + error("output hyphenation code must be integer in the range 0..255"); + break; + } + } + else { + error("input hyphenation code must be integer in the range 0..255"); + break; + } + } + skip_line(); +} + charinfo *token::get_char(int required) { if (type == TOKEN_CHAR) @@ -5553,17 +6098,28 @@ int token::add_to_node_list(node **pp) case TOKEN_RIGHT_BRACE: break; case TOKEN_SPACE: - n = new hmotion_node(curenv->get_space_width()); + n = new hmotion_node(curenv->get_space_width(), + curenv->get_fill_color()); break; case TOKEN_SPECIAL: *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s); break; case TOKEN_STRETCHABLE_SPACE: - n = new unbreakable_space_node(curenv->get_space_width()); + n = new unbreakable_space_node(curenv->get_space_width(), + curenv->get_fill_color()); + break; + case TOKEN_UNSTRETCHABLE_SPACE: + n = new space_char_hmotion_node(curenv->get_space_width(), + curenv->get_fill_color()); break; case TOKEN_TRANSPARENT_DUMMY: n = new transparent_dummy_node; break; + case TOKEN_ZERO_WIDTH_BREAK: + n = new space_node(H0, curenv->get_fill_color()); + n->freeze_space(); + n->is_escape_colon(); + break; default: return 0; } @@ -5580,7 +6136,8 @@ void token::process() return; switch (type) { case TOKEN_BACKSPACE: - curenv->add_node(new hmotion_node(-curenv->get_space_width())); + curenv->add_node(new hmotion_node(-curenv->get_space_width(), + curenv->get_fill_color())); break; case TOKEN_CHAR: curenv->add_char(charset_table[c]); @@ -5631,7 +6188,7 @@ void token::process() curenv->add_char(get_charinfo_by_number(val)); break; case TOKEN_REQUEST: - // handled in process_input_stack + // handled in process_input_stack() break; case TOKEN_RIGHT_BRACE: break; @@ -5645,7 +6202,12 @@ void token::process() curenv->spread(); break; case TOKEN_STRETCHABLE_SPACE: - curenv->add_node(new unbreakable_space_node(curenv->get_space_width())); + curenv->add_node(new unbreakable_space_node(curenv->get_space_width(), + curenv->get_fill_color())); + break; + case TOKEN_UNSTRETCHABLE_SPACE: + curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(), + curenv->get_fill_color())); break; case TOKEN_TAB: curenv->handle_tab(0); @@ -5655,6 +6217,14 @@ void token::process() case TOKEN_TRANSPARENT_DUMMY: curenv->add_node(new transparent_dummy_node); break; + case TOKEN_ZERO_WIDTH_BREAK: + { + node *tmp = new space_node(H0, curenv->get_fill_color()); + tmp->freeze_space(); + tmp->is_escape_colon(); + curenv->add_node(tmp); + break; + } default: assert(0); } @@ -5764,7 +6334,7 @@ void abort_request() if (c == EOF || c == '\n') fputs("User Abort.", stderr); else { - for (; c != '\n' && c != EOF; c = get_copy(NULL)) + for (; c != '\n' && c != EOF; c = get_copy(0)) fputs(asciify(c), stderr); } fputc('\n', stderr); @@ -5780,7 +6350,7 @@ char *read_string() ; int i = 0; while (c != '\n' && c != EOF) { - if (!illegal_input_char(c)) { + if (!invalid_input_char(c)) { if (i + 2 > len) { char *tem = s; s = new char[len*2]; @@ -5817,8 +6387,20 @@ void pipe_output() skip_line(); } else { - if ((pipe_command = read_string()) == 0) + char *pc; + if ((pc = read_string()) == 0) error("can't pipe to empty command"); + if (pipe_command) { + char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1]; + strcpy(s, pipe_command); + strcat(s, "|"); + strcat(s, pc); + a_delete pipe_command; + a_delete pc; + pipe_command = s; + } + else + pipe_command = pc; } #endif /* not POPEN_MISSING */ } @@ -5897,8 +6479,8 @@ void transparent_file() int c = getc(fp); if (c == EOF) break; - if (illegal_input_char(c)) - warning(WARN_INPUT, "illegal input character code %1", int(c)); + if (invalid_input_char(c)) + warning(WARN_INPUT, "invalid input character code %1", int(c)); else { curdiv->transparent_output(c); bol = c == '\n'; @@ -6098,7 +6680,7 @@ static int evaluate_expression(const char *expr, units *res) input_stack::push(make_temp_iterator(expr)); tok.next(); int success = get_number(res, 'u'); - while (input_stack::get(NULL) != EOF) + while (input_stack::get(0) != EOF) ; return success; } @@ -6129,7 +6711,7 @@ static void set_string(const char *name, const char *value) { macro *m = new macro; for (const char *p = value; *p; p++) - if (!illegal_input_char((unsigned char)*p)) + if (!invalid_input_char((unsigned char)*p)) m->append(*p); request_dictionary.define(name, m); } @@ -6177,7 +6759,7 @@ static void add_string(const char *s, string_list **p) void usage(FILE *stream, const char *prog) { fprintf(stream, -"usage: %s -abivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" +"usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" " -rcn -Tname -Fdir -Mdir [files...]\n", prog); } @@ -6213,10 +6795,10 @@ int main(int argc, char **argv) static const struct option long_options[] = { { "help", no_argument, 0, CHAR_MAX + 1 }, { "version", no_argument, 0, 'v' }, - { NULL, 0, 0, 0 } + { 0, 0, 0, 0 } }; - while ((c = getopt_long(argc, argv, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU", - long_options, NULL)) + while ((c = getopt_long(argc, argv, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU", + long_options, 0)) != EOF) switch(c) { case 'v': @@ -6232,6 +6814,9 @@ int main(int argc, char **argv) break; case 'C': compatible_flag = 1; + // fall through + case 'c': + color_flag = 0; break; case 'M': macro_path.command_line_dir(optarg); @@ -6316,6 +6901,7 @@ int main(int argc, char **argv) mac_path = ¯o_path; set_string(".T", device); init_charset_table(); + init_hpf_code_table(); if (!font::load_desc()) fatal("sorry, I can't continue"); units_per_inch = font::res; @@ -6323,6 +6909,8 @@ int main(int argc, char **argv) vresolution = font::vert; sizescale = font::sizescale; tcommand_flag = font::tcommand; + warn_scale = (double)units_per_inch; + warn_scaling_indicator = 'i'; if (!fflag && font::family != 0 && *font::family != '\0') default_family = symbol(font::family); font_size::init_size_table(font::sizes); @@ -6347,7 +6935,6 @@ int main(int argc, char **argv) init_column_requests(); #endif /* COLUMN */ init_node_requests(); - init_markup_requests(); number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); init_registers(); init_reg_requests(); @@ -6408,6 +6995,9 @@ static void init_registers() t = time(0); // Use struct here to work around misfeature in old versions of g++. struct tm *tt = localtime(&t); + set_number_reg("seconds", int(tt->tm_sec)); + set_number_reg("minutes", int(tt->tm_min)); + set_number_reg("hours", int(tt->tm_hour)); set_number_reg("dw", int(tt->tm_wday + 1)); set_number_reg("dy", int(tt->tm_mday)); set_number_reg("mo", int(tt->tm_mon + 1)); @@ -6458,120 +7048,126 @@ void get_output_registers(int *minx, int *miny, int *maxx, int *maxy) *maxy = output_reg_maxy_contents; } -void init_markup_requests() -{ - init_request("begin", begin); - init_request("end", end); - init_request("image", image); -} - void init_input_requests() { - init_request("ds", define_string); + init_request("ab", abort_request); + init_request("als", alias_macro); + init_request("am", append_macro); + init_request("am1", append_nocomp_macro); + init_request("ami", append_indirect_macro); init_request("as", append_string); + init_request("as1", append_nocomp_string); + init_request("asciify", asciify_macro); + init_request("backtrace", backtrace_request); + init_request("blm", blank_line_macro); + init_request("break", while_break_request); + init_request("cf", copy_file); + init_request("cflags", char_flags); + init_request("char", define_character); + init_request("chop", chop_macro); + init_request("close", close_request); + init_request("color", activate_color); + init_request("continue", while_continue_request); + init_request("cp", compatible); init_request("de", define_macro); - init_request("dei", define_indirect_macro); init_request("de1", define_nocomp_macro); - init_request("am", append_macro); - init_request("am1", append_nocomp_macro); + init_request("defcolor", define_color); + init_request("dei", define_indirect_macro); + init_request("do", do_request); + init_request("ds", define_string); + init_request("ds1", define_nocomp_string); + init_request("ec", set_escape_char); + init_request("ecr", restore_escape_char); + init_request("ecs", save_escape_char); + init_request("el", else_request); + init_request("em", end_macro); + init_request("eo", escape_off); + init_request("ex", exit_request); + init_request("fchar", define_fallback_character); +#ifdef WIDOW_CONTROL + init_request("fpl", flush_pending_lines); +#endif /* WIDOW_CONTROL */ + init_request("hcode", hyphenation_code); + init_request("hpfcode", hyphenation_patterns_file_code); + init_request("ie", if_else_request); + init_request("if", if_request); init_request("ig", ignore); - init_request("rm", remove_macro); - init_request("rn", rename_macro); + init_request("length", length_request); + init_request("lf", line_file); + init_request("mso", macro_source); init_request("nop", nop_request); - init_request("if", if_request); - init_request("ie", if_else_request); - init_request("el", else_request); - init_request("so", source); init_request("nx", next_file); - init_request("pm", print_macros); - init_request("eo", escape_off); - init_request("ec", set_escape_char); - init_request("ecs", save_escape_char); - init_request("ecr", restore_escape_char); + init_request("open", open_request); + init_request("opena", opena_request); + init_request("output", output_request); init_request("pc", set_page_character); + init_request("pi", pipe_output); + init_request("pm", print_macros); + init_request("psbb", ps_bbox_request); +#ifndef POPEN_MISSING + init_request("pso", pipe_source); +#endif /* not POPEN_MISSING */ + init_request("rchar", remove_character); + init_request("rd", read_request); + init_request("return", return_macro_request); + init_request("rm", remove_macro); + init_request("rn", rename_macro); + init_request("shift", shift); + init_request("so", source); + init_request("spreadwarn", spreadwarn_request); + init_request("substring", substring_request); + init_request("sy", system_request); init_request("tm", terminal); init_request("tm1", terminal1); init_request("tmc", terminal_continue); - init_request("ex", exit_request); - init_request("return", return_macro_request); - init_request("em", end_macro); - init_request("blm", blank_line_macro); init_request("tr", translate); + init_request("trf", transparent_file); + init_request("trin", translate_input); init_request("trnt", translate_no_transparent); - init_request("ab", abort_request); - init_request("pi", pipe_output); - init_request("cf", copy_file); - init_request("sy", system_request); - init_request("lf", line_file); - init_request("cflags", char_flags); - init_request("shift", shift); - init_request("rd", read_request); - init_request("cp", compatible); - init_request("char", define_character); - init_request("rchar", remove_character); - init_request("hcode", hyphenation_code); - init_request("while", while_request); - init_request("break", while_break_request); - init_request("continue", while_continue_request); - init_request("als", alias_macro); - init_request("backtrace", backtrace_request); - init_request("chop", chop_macro); - init_request("substring", substring_macro); - init_request("length", length_macro); - init_request("asciify", asciify_macro); init_request("unformat", unformat_macro); init_request("warn", warn_request); - init_request("open", open_request); - init_request("opena", opena_request); - init_request("close", close_request); + init_request("while", while_request); init_request("write", write_request); + init_request("writec", write_request_continue); init_request("writem", write_macro_request); - init_request("trf", transparent_file); -#ifdef WIDOW_CONTROL - init_request("fpl", flush_pending_lines); -#endif /* WIDOW_CONTROL */ init_request("nroff", nroff_request); init_request("troff", troff_request); #ifdef COLUMN init_request("vj", vjustify); #endif /* COLUMN */ - init_request("mso", macro_source); - init_request("do", do_request); -#ifndef POPEN_MISSING - init_request("pso", pipe_source); -#endif /* not POPEN_MISSING */ - init_request("psbb", ps_bbox_request); - number_reg_dictionary.define("systat", new variable_reg(&system_status)); - number_reg_dictionary.define("slimit", - new variable_reg(&input_stack::limit)); + init_request("warnscale", warnscale_request); number_reg_dictionary.define(".$", new nargs_reg); - number_reg_dictionary.define(".c", new lineno_reg); - number_reg_dictionary.define("c.", new writable_lineno_reg); - number_reg_dictionary.define(".F", new filename_reg); number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag)); + number_reg_dictionary.define(".F", new filename_reg); number_reg_dictionary.define(".H", new constant_int_reg(&hresolution)); - number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); number_reg_dictionary.define(".R", new constant_reg("10000")); - extern const char *major_version; - number_reg_dictionary.define(".x", new constant_reg(major_version)); - extern const char *minor_version; - number_reg_dictionary.define(".y", new constant_reg(minor_version)); + number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); extern const char *revision; number_reg_dictionary.define(".Y", new constant_reg(revision)); + number_reg_dictionary.define(".c", new lineno_reg); number_reg_dictionary.define(".g", new constant_reg("1")); + number_reg_dictionary.define(".color", new constant_int_reg(&color_flag)); number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask)); + extern const char *major_version; + number_reg_dictionary.define(".x", new constant_reg(major_version)); + extern const char *minor_version; + number_reg_dictionary.define(".y", new constant_reg(minor_version)); + number_reg_dictionary.define("c.", new writable_lineno_reg); number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents)); number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents)); - number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents)); - number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents)); - number_reg_dictionary.define("opminx", - new variable_reg(&output_reg_minx_contents)); - number_reg_dictionary.define("opminy", - new variable_reg(&output_reg_miny_contents)); number_reg_dictionary.define("opmaxx", new variable_reg(&output_reg_maxx_contents)); number_reg_dictionary.define("opmaxy", new variable_reg(&output_reg_maxy_contents)); + number_reg_dictionary.define("opminx", + new variable_reg(&output_reg_minx_contents)); + number_reg_dictionary.define("opminy", + new variable_reg(&output_reg_miny_contents)); + number_reg_dictionary.define("slimit", + new variable_reg(&input_stack::limit)); + number_reg_dictionary.define("systat", new variable_reg(&system_status)); + number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents)); + number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents)); } object_dictionary request_dictionary(501); @@ -6718,12 +7314,20 @@ static node *read_draw_node() if (no_last_v) error("even number of arguments needed for spline"); break; + case 'f': + if (npoints != 1 || !no_last_v) { + error("one argument needed for gray shade"); + npoints = 1; + point[0].v = V0; + } default: // silently pass it through break; } draw_node *dn = new draw_node(type, point, npoints, - curenv->get_font_size()); + curenv->get_font_size(), + curenv->get_glyph_color(), + curenv->get_fill_color()); a_delete point; return dn; } @@ -6758,6 +7362,7 @@ static struct { { "mac", WARN_MAC }, { "reg", WARN_REG }, { "ig", WARN_IG }, + { "color", WARN_COLOR }, { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) }, { "w", WARN_TOTAL }, { "default", DEFAULT_WARNING_MASK }, @@ -6808,7 +7413,7 @@ static void copy_mode_error(const char *format, error(format, arg1, arg2, arg3); } -enum error_type { WARNING, ERROR, FATAL }; +enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL }; static void do_error(error_type type, const char *format, @@ -6837,6 +7442,18 @@ static void do_error(error_type type, case WARNING: fputs("warning: ", stderr); break; + case OUTPUT_WARNING: + double fromtop = topdiv->get_vertical_position().to_units() / warn_scale; + fprintf(stderr, "warning [p %d, %.1f%c", + topdiv->get_page_number(), fromtop, warn_scaling_indicator); + if (topdiv != curdiv) { + double fromtop1 = curdiv->get_vertical_position().to_units() + / warn_scale; + fprintf(stderr, ", div `%s', %.1f%c", + curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator); + } + fprintf(stderr, "]: "); + break; } errprint(format, arg1, arg2, arg3); fputc('\n', stderr); @@ -6859,6 +7476,20 @@ int warning(warning_type t, return 0; } +int output_warning(warning_type t, + const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + if ((t & warning_mask) != 0) { + do_error(OUTPUT_WARNING, format, arg1, arg2, arg3); + return 1; + } + else + return 0; +} + void error(const char *format, const errarg &arg1, const errarg &arg2, @@ -6916,8 +7547,9 @@ int charinfo::next_index = 0; charinfo::charinfo(symbol s) : translation(0), mac(0), special_translation(TRANSLATE_NONE), - hyphenation_code(0), flags(0), ascii_code(0), not_found(0), - transparent_translate(1), nm(s) + hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0), + not_found(0), transparent_translate(1), translate_input(0), + fallback(0), nm(s) { index = next_index++; } @@ -6927,9 +7559,18 @@ void charinfo::set_hyphenation_code(unsigned char c) hyphenation_code = c; } -void charinfo::set_translation(charinfo *ci, int tt) +void charinfo::set_translation(charinfo *ci, int tt, int ti) { translation = ci; + if (ci && ti) { + if (hyphenation_code != 0) + ci->set_hyphenation_code(hyphenation_code); + if (asciify_code != 0) + ci->set_asciify_code(asciify_code); + else if (ascii_code != 0) + ci->set_asciify_code(ascii_code); + ci->set_translation_input(); + } special_translation = TRANSLATE_NONE; transparent_translate = tt; } @@ -6946,10 +7587,16 @@ void charinfo::set_ascii_code(unsigned char c) ascii_code = c; } -macro *charinfo::set_macro(macro *m) +void charinfo::set_asciify_code(unsigned char c) +{ + asciify_code = c; +} + +macro *charinfo::set_macro(macro *m, int f) { macro *tem = mac; mac = m; + fallback = f; return tem; } |