diff options
Diffstat (limited to 'contrib/groff/src/roff/troff')
-rw-r--r-- | contrib/groff/src/roff/troff/Makefile.sub | 20 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/TODO | 3 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/charinfo.h | 38 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/dictionary.cc | 2 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/div.cc | 23 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/div.h | 2 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/env.cc | 607 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/env.h | 33 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/input.cc | 1685 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/node.cc | 650 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/node.h | 51 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/number.cc | 9 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/request.h | 10 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/symbol.cc | 14 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/symbol.h | 9 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/token.h | 21 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/troff.h | 18 | ||||
-rw-r--r-- | contrib/groff/src/roff/troff/troff.man | 2595 |
18 files changed, 2691 insertions, 3099 deletions
diff --git a/contrib/groff/src/roff/troff/Makefile.sub b/contrib/groff/src/roff/troff/Makefile.sub index e883959..e889cdd 100644 --- a/contrib/groff/src/roff/troff/Makefile.sub +++ b/contrib/groff/src/roff/troff/Makefile.sub @@ -1,17 +1,17 @@ -PROG=troff +PROG=troff$(EXEEXT) MAN1=troff.n XLIBS=$(LIBGROFF) MLIB=$(LIBM) OBJS=\ - env.o \ - node.o \ - input.o \ - div.o \ - symbol.o \ - dictionary.o \ - reg.o \ - number.o \ - majorminor.o + env.$(OBJEXT) \ + node.$(OBJEXT) \ + input.$(OBJEXT) \ + div.$(OBJEXT) \ + symbol.$(OBJEXT) \ + dictionary.$(OBJEXT) \ + reg.$(OBJEXT) \ + number.$(OBJEXT) \ + majorminor.$(OBJEXT) CCSRCS=\ $(srcdir)/env.cc \ $(srcdir)/node.cc \ diff --git a/contrib/groff/src/roff/troff/TODO b/contrib/groff/src/roff/troff/TODO index 6660597..49aa0eb 100644 --- a/contrib/groff/src/roff/troff/TODO +++ b/contrib/groff/src/roff/troff/TODO @@ -83,9 +83,6 @@ Then is you ask for R at pointsize 16, groff will first look for R.display and then R. Probably necessary to be able to specify a separate unitwidth for each sizesuffix (eg. for X). -Variant of `.it' for which a line interrupted with \c counts as one -input line. - Make it possible to suppress hyphenation on a word-by-word basis. (Perhaps store hyphenation flags in tfont.) diff --git a/contrib/groff/src/roff/troff/charinfo.h b/contrib/groff/src/roff/troff/charinfo.h index a4ecd57..b907ae4 100644 --- a/contrib/groff/src/roff/troff/charinfo.h +++ b/contrib/groff/src/roff/troff/charinfo.h @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -30,9 +31,13 @@ class charinfo { unsigned char hyphenation_code; unsigned char flags; unsigned char ascii_code; + unsigned char asciify_code; char not_found; - char transparent_translate; // non-zero means translation applies to + char transparent_translate; // non-zero means translation applies // to transparent throughput + char translate_input; // non-zero means that asciify_code is + // active for .asciify (set by .trin) + char fallback; public: enum { ENDS_SENTENCE = 1, @@ -61,19 +66,24 @@ public: int transparent(); unsigned char get_hyphenation_code(); unsigned char get_ascii_code(); + unsigned char get_asciify_code(); void set_hyphenation_code(unsigned char); void set_ascii_code(unsigned char); + void set_asciify_code(unsigned char); + void set_translation_input(); + int get_translation_input(); charinfo *get_translation(int = 0); - void set_translation(charinfo *, int); + void set_translation(charinfo *, int, int); void set_flags(unsigned char); void set_special_translation(int, int); int get_special_translation(int = 0); - macro *set_macro(macro *); + macro *set_macro(macro *, int = 0); macro *get_macro(); int first_time_not_found(); void set_number(int); int get_number(); int numbered(); + int is_fallback(); symbol *get_symbol(); }; @@ -116,6 +126,11 @@ inline int charinfo::numbered() return flags & NUMBERED; } +inline int charinfo::is_fallback() +{ + return fallback; +} + inline charinfo *charinfo::get_translation(int transparent_throughput) { return (transparent_throughput && !transparent_translate @@ -133,6 +148,11 @@ inline unsigned char charinfo::get_ascii_code() return ascii_code; } +inline unsigned char charinfo::get_asciify_code() +{ + return (translate_input ? asciify_code : 0); +} + inline void charinfo::set_flags(unsigned char c) { flags = c; @@ -143,6 +163,16 @@ inline int charinfo::get_index() return index; } +inline void charinfo::set_translation_input() +{ + translate_input = 1; +} + +inline int charinfo::get_translation_input() +{ + return translate_input; +} + inline int charinfo::get_special_translation(int transparent_throughput) { return (transparent_throughput && !transparent_translate diff --git a/contrib/groff/src/roff/troff/dictionary.cc b/contrib/groff/src/roff/troff/dictionary.cc index bca3845..a70ebb0 100644 --- a/contrib/groff/src/roff/troff/dictionary.cc +++ b/contrib/groff/src/roff/troff/dictionary.cc @@ -138,7 +138,7 @@ int dictionary_iterator::get(symbol *sp, void **vp) } object_dictionary_iterator::object_dictionary_iterator(object_dictionary &od) - : di(od.d) +: di(od.d) { } diff --git a/contrib/groff/src/roff/troff/div.cc b/contrib/groff/src/roff/troff/div.cc index c885ca8..14c7399 100644 --- a/contrib/groff/src/roff/troff/div.cc +++ b/contrib/groff/src/roff/troff/div.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) @@ -410,7 +410,7 @@ void top_level_diversion::output(node *nd, int retain_size, void top_level_diversion::transparent_output(unsigned char c) { if (before_first_page && begin_page()) - // This can only happen with the transparent() request. + // This can only happen with the .output request. fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); const char *s = asciify(c); while (*s) @@ -467,7 +467,7 @@ void top_level_diversion::space(vunits n, int forced) } trap::trap(symbol s, vunits n, trap *p) - : next(p), position(n), nm(s) +: next(p), position(n), nm(s) { } @@ -641,7 +641,7 @@ void page_offset() n = topdiv->prev_page_offset; topdiv->prev_page_offset = topdiv->page_offset; topdiv->page_offset = n; - curenv->add_html_tag(".po", n.to_units()); + curenv->add_html_tag(0, ".po", n.to_units()); skip_line(); } @@ -760,7 +760,7 @@ void space_request() else // The line might have had line spacing that was truncated. truncated_space += n; - curenv->add_html_tag(".sp", n.to_units()); + curenv->add_html_tag(1, ".sp", n.to_units()); tok.next(); } @@ -769,7 +769,7 @@ void blank_line() curenv->do_break(); if (!trap_sprung_flag && !curdiv->no_space_mode) { curdiv->space(curenv->get_vertical_spacing()); - curenv->add_html_tag(".sp", 1); + curenv->add_html_tag(1, ".sp", 1); } else truncated_space += curenv->get_vertical_spacing(); } @@ -791,8 +791,13 @@ void need_space() void page_number() { int n; - if (has_arg() && get_integer(&n, topdiv->get_page_number())) - topdiv->set_next_page_number(n); + + // the ps4html register is set if we are using -Tps + // to generate images for html + reg *r = (reg *)number_reg_dictionary.lookup("ps4html"); + if (r == NULL) + if (has_arg() && get_integer(&n, topdiv->get_page_number())) + topdiv->set_next_page_number(n); skip_line(); } @@ -828,7 +833,7 @@ void flush_output() curenv->do_break(); if (the_output) the_output->flush(); - curenv->add_html_tag(".fl"); + curenv->add_html_tag(1, ".fl"); tok.next(); } diff --git a/contrib/groff/src/roff/troff/div.h b/contrib/groff/src/roff/troff/div.h index 3b726c3..31b9af3 100644 --- a/contrib/groff/src/roff/troff/div.h +++ b/contrib/groff/src/roff/troff/div.h @@ -152,5 +152,7 @@ void push_page_ejector(); void continue_page_eject(); void handle_first_page_transition(); void blank_line(); +void begin_page(); +void end_diversions(); extern void cleanup_and_exit(int); diff --git a/contrib/groff/src/roff/troff/env.cc b/contrib/groff/src/roff/troff/env.cc index c0743441..40c9e3c 100644 --- a/contrib/groff/src/roff/troff/env.cc +++ b/contrib/groff/src/roff/troff/env.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) @@ -31,6 +31,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "reg.h" #include "charinfo.h" #include "macropath.h" +#include "input.h" #include <math.h> symbol default_family("T"); @@ -299,7 +300,8 @@ node *environment::make_char_node(charinfo *ci) void environment::add_node(node *n) { - assert(n != 0); + if (n == 0) + return; if (current_tab || current_field) n->freeze_space(); if (interrupted) { @@ -392,7 +394,7 @@ void environment::space_newline() width_total += x; return; } - add_node(new word_space_node(x, w)); + add_node(new word_space_node(x, get_fill_color(), w)); possibly_break_line(0, spread_flag); spread_flag = 0; } @@ -426,6 +428,7 @@ void environment::space(hunits space_width, hunits sentence_space_width) return; } add_node(new word_space_node(x, + get_fill_color(), new width_list(space_width, sentence_space_width))); possibly_break_line(0, spread_flag); @@ -438,7 +441,7 @@ void environment::set_font(symbol nm) { if (interrupted) return; - if (nm == symbol("P")) { + if (nm == symbol("P") || nm.is_empty()) { if (family->make_definite(prev_fontno) < 0) return; int tem = fontno; @@ -479,7 +482,9 @@ void environment::set_font(int n) void environment::set_family(symbol fam) { - if (fam.is_null()) { + if (interrupted) + return; + if (fam.is_null() || fam.is_empty()) { if (prev_family->make_definite(fontno) < 0) return; font_family *tem = family; @@ -532,6 +537,42 @@ void environment::set_char_slant(int n) char_slant = n; } +color *environment::get_prev_glyph_color() +{ + return prev_glyph_color; +} + +color *environment::get_glyph_color() +{ + return glyph_color; +} + +color *environment::get_prev_fill_color() +{ + return prev_fill_color; +} + +color *environment::get_fill_color() +{ + return fill_color; +} + +void environment::set_glyph_color(color *c) +{ + if (interrupted) + return; + curenv->prev_glyph_color = curenv->glyph_color; + curenv->glyph_color = c; +} + +void environment::set_fill_color(color *c) +{ + if (interrupted) + return; + curenv->prev_fill_color = curenv->fill_color; + curenv->fill_color = c; +} + environment::environment(symbol nm) : dummy(0), prev_line_length((units_per_inch*13)/2), @@ -565,6 +606,7 @@ environment::environment(symbol nm) underline_lines(0), underline_spaces(0), input_trap_count(0), + continued_input_trap(0), line(0), prev_text_length(0), width_total(0), @@ -597,9 +639,12 @@ environment::environment(symbol nm) #ifdef WIDOW_CONTROL widow_control(0), #endif /* WIDOW_CONTROL */ - need_eol(0), ignore_next_eol(0), emitted_node(0), + glyph_color(&default_color), + prev_glyph_color(&default_color), + fill_color(&default_color), + prev_fill_color(&default_color), name(nm), control_char('.'), no_break_control_char('\''), @@ -651,6 +696,7 @@ environment::environment(const environment *e) underline_lines(0), underline_spaces(0), input_trap_count(0), + continued_input_trap(0), line(0), prev_text_length(e->prev_text_length), width_total(0), @@ -683,8 +729,12 @@ environment::environment(const environment *e) #ifdef WIDOW_CONTROL widow_control(e->widow_control), #endif /* WIDOW_CONTROL */ - need_eol(0), ignore_next_eol(0), + emitted_node(0), + glyph_color(e->glyph_color), + prev_glyph_color(e->prev_glyph_color), + fill_color(e->fill_color), + prev_fill_color(e->prev_fill_color), name(e->name), // so that eg `.if "\n[.ev]"0"' works control_char(e->control_char), no_break_control_char(e->no_break_control_char), @@ -725,6 +775,7 @@ void environment::copy(const environment *e) underline_lines = 0; underline_spaces = 0; input_trap_count = 0; + continued_input_trap = 0; prev_text_length = e->prev_text_length; width_total = 0; space_total = 0; @@ -765,6 +816,12 @@ void environment::copy(const environment *e) hyphenation_space = e->hyphenation_space; hyphenation_margin = e->hyphenation_margin; composite = 0; + ignore_next_eol = e->ignore_next_eol; + emitted_node = e->emitted_node; + glyph_color= e->glyph_color; + prev_glyph_color = e->prev_glyph_color; + fill_color = e->fill_color; + prev_fill_color = e->prev_fill_color; } environment::~environment() @@ -1107,13 +1164,54 @@ void point_size() if (n <= 0) n = 1; curenv->set_size(n); - curenv->add_html_tag(".ps", n); + curenv->add_html_tag(1, ".ps", n); } else curenv->set_size(0); skip_line(); } +void override_sizes() +{ + int n = 16; + int *sizes = new int[n]; + int i = 0; + char *buf = read_string(); + if (!buf) + return; + char *p = strtok(buf, " \t"); + for (;;) { + if (!p) + break; + int lower, upper; + switch (sscanf(p, "%d-%d", &lower, &upper)) { + case 1: + upper = lower; + // fall through + case 2: + if (lower <= upper && lower >= 0) + break; + // fall through + default: + warning(WARN_RANGE, "bad size range `%1'", p); + return; + } + if (i + 2 > n) { + int *old_sizes = sizes; + sizes = new int[n*2]; + memcpy(sizes, old_sizes, n*sizeof(int)); + n *= 2; + a_delete old_sizes; + } + sizes[i++] = lower; + if (lower == 0) + break; + sizes[i++] = upper; + p = strtok(0, " \t"); + } + font_size::init_size_table(sizes); +} + void space_size() { int n; @@ -1134,7 +1232,8 @@ void fill() if (break_flag) curenv->do_break(); curenv->fill = 1; - curenv->add_html_tag(".fi"); + curenv->add_html_tag(1, ".fi"); + curenv->add_html_tag(0, ".br"); tok.next(); } @@ -1144,11 +1243,10 @@ void no_fill() tok.next(); if (break_flag) curenv->do_break(); - curenv->fill = 0; - curenv->add_html_tag(".nf"); - curenv->ignore_next_eol = 1; - curenv->add_html_tag(".po", topdiv->get_page_offset().to_units()); + curenv->add_html_tag(1, ".nf"); + curenv->add_html_tag(0, ".br"); + curenv->add_html_tag(0, ".po", topdiv->get_page_offset().to_units()); tok.next(); } @@ -1165,7 +1263,7 @@ void center() curenv->do_break(); curenv->right_justify_lines = 0; curenv->center_lines = n; - curenv->add_html_tag(".ce", n); + curenv->add_html_tag(1, ".ce", n); tok.next(); } @@ -1182,7 +1280,7 @@ void right_justify() curenv->do_break(); curenv->center_lines = 0; curenv->right_justify_lines = n; - curenv->add_html_tag(".rj", n); + curenv->add_html_tag(1, ".rj", n); tok.next(); } @@ -1199,7 +1297,7 @@ void line_length() temp = curenv->prev_line_length; curenv->prev_line_length = curenv->line_length; curenv->line_length = temp; - curenv->add_html_tag(".ll", temp.to_units()); + curenv->add_html_tag(1, ".ll", temp.to_units()); skip_line(); } @@ -1286,7 +1384,8 @@ void indent() curenv->have_temporary_indent = 0; curenv->prev_indent = curenv->indent; curenv->indent = temp; - curenv->add_html_tag(".in", temp.to_units()); + if (break_flag) + curenv->add_html_tag(1, ".in", temp.to_units()); tok.next(); } @@ -1307,7 +1406,7 @@ void temporary_indent() if (!err) { curenv->temporary_indent = temp; curenv->have_temporary_indent = 1; - curenv->add_html_tag(".ti", temp.to_units()); + curenv->add_html_tag(1, ".ti", temp.to_units()); } tok.next(); } @@ -1562,6 +1661,11 @@ void environment::newline() if (x > H0) saved_indent += x/2; to_be_output = line; + if (is_html) { + node *n = make_html_tag("eol.ce"); + n->next = to_be_output; + to_be_output = n; + } to_be_output_width = width_total; line = 0; } @@ -1583,12 +1687,21 @@ void environment::newline() } input_line_start = line == 0 ? H0 : width_total; if (to_be_output) { + if (is_html && !fill) { + if (curdiv == topdiv) { + node *n = make_html_tag("eol"); + + n->next = to_be_output; + to_be_output = n; + } + } output_line(to_be_output, to_be_output_width); hyphen_line_count = 0; } if (input_trap_count > 0) { - if (--input_trap_count == 0) - spring_trap(input_trap); + if (!(continued_input_trap && prev_line_interrupted)) + if (--input_trap_count == 0) + spring_trap(input_trap); } } @@ -1598,7 +1711,7 @@ void environment::output_line(node *n, hunits width) if (margin_character_flags) { hunits d = line_length + margin_character_distance - saved_indent - width; if (d > 0) { - n = new hmotion_node(d, n); + n = new hmotion_node(d, get_fill_color(), n); width += d; } margin_character_flags &= ~MARGIN_CHARACTER_NEXT; @@ -1621,7 +1734,7 @@ void environment::output_line(node *n, hunits width) n = tem; } if (!saved_indent.is_zero()) - nn = new hmotion_node(saved_indent, nn); + nn = new hmotion_node(saved_indent, get_fill_color(), nn); width += saved_indent; if (no_number_count > 0) --no_number_count; @@ -1629,11 +1742,11 @@ void environment::output_line(node *n, hunits width) hunits w = (line_number_digit_width *(3+line_number_indent+number_text_separation)); if (next_line_number % line_number_multiple != 0) - nn = new hmotion_node(w, nn); + nn = new hmotion_node(w, get_fill_color(), nn); else { hunits x = w; - nn = new hmotion_node(number_text_separation*line_number_digit_width, - nn); + nn = new hmotion_node(number_text_separation * line_number_digit_width, + get_fill_color(), nn); x -= number_text_separation*line_number_digit_width; char buf[30]; sprintf(buf, "%3d", next_line_number); @@ -1646,7 +1759,7 @@ void environment::output_line(node *n, hunits width) gn->next = nn; nn = gn; } - nn = new hmotion_node(x, nn); + nn = new hmotion_node(x, get_fill_color(), nn); } width += w; ++next_line_number; @@ -1789,7 +1902,7 @@ breakpoint *environment::choose_breakpoint() } if (best_bp) { if (!best_bp_fits) - warning(WARN_BREAK, "can't break line"); + output_warning(WARN_BREAK, "can't break line"); return best_bp; } return 0; @@ -1875,6 +1988,14 @@ static void distribute_space(node *n, int nspaces, hunits desired_space, static int reverse = 0; if (force_reverse || reverse) n = node_list_reverse(n); + if (!force_reverse && nspaces > 0 && spread_limit >= 0 + && desired_space.to_units() > 0) { + hunits em = curenv->get_size(); + double Ems = (double)desired_space.to_units() / nspaces + / (em.is_zero() ? hresolution : em.to_units()); + if (Ems > spread_limit) + output_warning(WARN_BREAK, "spreading %1m per space", Ems); + } for (node *tem = n; tem; tem = tem->next) tem->spread_space(&nspaces, &desired_space); if (force_reverse || reverse) @@ -1909,6 +2030,9 @@ void environment::possibly_break_line(int start_here, int forced) case ADJUST_BOTH: if (bp->nspaces != 0) extra_space_width = target_text_length - bp->width; + else if (bp->width > 0 && target_text_length > 0 + && target_text_length > bp->width) + output_warning(WARN_BREAK, "cannot adjust line"); break; case ADJUST_CENTER: saved_indent += (target_text_length - bp->width)/2; @@ -2013,32 +2137,15 @@ void environment::final_break() } /* - * add_html_tag_eol - add an end of line tag if appropriate. - */ - -void environment::add_html_tag_eol() -{ - if (is_html) { - if (ignore_next_eol > 0) - ignore_next_eol--; - else if (need_eol > 0) { - need_eol--; - add_html_tag("eol"); - } - else if (!fill && emitted_node) { - add_html_tag("eol"); - emitted_node = 0; - } - } -} - -/* * add_html_tag - emits a special html-tag: to help post-grohtml understand * the key troff commands */ -void environment::add_html_tag(const char *name) +void environment::add_html_tag(int force, const char *name) { + if (!force && (curdiv != topdiv)) + return; + if (is_html) { /* * need to emit tag for post-grohtml @@ -2049,9 +2156,11 @@ void environment::add_html_tag(const char *name) macro *m = new macro; m->append_str("html-tag:"); for (const char *p = name; *p; p++) - if (!illegal_input_char((unsigned char)*p)) + if (!invalid_input_char((unsigned char)*p)) m->append(*p); - add_node(new special_node(*m)); + curdiv->output(new special_node(*m), 1, 0, 0, 0); + if (strcmp(name, ".nf") == 0) + curenv->ignore_next_eol = 1; } } @@ -2061,18 +2170,12 @@ void environment::add_html_tag(const char *name) * of i. */ -void environment::add_html_tag(const char *name, int i) +void environment::add_html_tag(int force, const char *name, int i) { - if (is_html) { - if (strcmp(name, ".ce") == 0) { - if (i == 0) - need_eol = 0; - else { - need_eol = i; - ignore_next_eol = 1; // since the .ce creates an eol - } - } + if (!force && (curdiv != topdiv)) + return; + if (is_html) { /* * need to emit tag for post-grohtml * but we check to see whether we can emit specials @@ -2082,13 +2185,12 @@ void environment::add_html_tag(const char *name, int i) macro *m = new macro; m->append_str("html-tag:"); for (const char *p = name; *p; p++) - if (!illegal_input_char((unsigned char)*p)) + if (!invalid_input_char((unsigned char)*p)) m->append(*p); m->append(' '); m->append_int(i); - // output_pending_lines(); - output(new special_node(*m), !fill, 0, 0, 0); - // output_pending_lines(); + node *n = new special_node(*m); + curdiv->output(n, 1, 0, 0, 0); } } @@ -2096,8 +2198,11 @@ void environment::add_html_tag(const char *name, int i) * add_html_tag_tabs - emits the tab settings for post-grohtml */ -void environment::add_html_tag_tabs() +void environment::add_html_tag_tabs(int force) { + if (!force && (curdiv != topdiv)) + return; + if (is_html) { /* * need to emit tag for post-grohtml @@ -2115,24 +2220,62 @@ void environment::add_html_tag_tabs() switch (t) { case TAB_LEFT: m->append_str(" L "); - m->append_int(d.to_units()); + m->append_int(l.to_units()); break; case TAB_CENTER: m->append_str(" C "); - m->append_int(d.to_units()); + m->append_int(l.to_units()); break; case TAB_RIGHT: m->append_str(" R "); - m->append_int(d.to_units()); + m->append_int(l.to_units()); break; case TAB_NONE: break; } } while ((t != TAB_NONE) && (l < get_line_length())); - output_pending_lines(); - output(new special_node(*m), !fill, 0, 0, 0); - output_pending_lines(); + curdiv->output(new special_node(*m), 1, 0, 0, 0); + } +} + +node *environment::make_html_tag(const char *name, int i) +{ + if (is_html) { + /* + * need to emit tag for post-grohtml + * but we check to see whether we can emit specials + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); + macro *m = new macro; + m->append_str("html-tag:"); + for (const char *p = name; *p; p++) + if (!invalid_input_char((unsigned char)*p)) + m->append(*p); + m->append(' '); + m->append_int(i); + return new special_node(*m); } + return 0; +} + +node *environment::make_html_tag(const char *name) +{ + if (is_html) { + /* + * need to emit tag for post-grohtml + * but we check to see whether we can emit specials + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); + macro *m = new macro; + m->append_str("html-tag:"); + for (const char *p = name; *p; p++) + if (!invalid_input_char((unsigned char)*p)) + m->append(*p); + return new special_node(*m); + } + return 0; } void environment::do_break(int spread) @@ -2144,7 +2287,8 @@ void environment::do_break(int spread) if (current_tab) wrap_up_tab(); if (line) { - line = new space_node(H0, line); // this is so that hyphenation works + // this is so that hyphenation works + line = new space_node(H0, get_fill_color(), line); space_total++; possibly_break_line(0, spread); } @@ -2191,7 +2335,7 @@ void do_break_request(int spread) tok.next(); if (break_flag) { curenv->do_break(spread); - curenv->add_html_tag(".br"); + curenv->add_html_tag(0, ".br"); } tok.next(); } @@ -2228,6 +2372,10 @@ void title() curenv->char_slant = env.char_slant; curenv->fontno = env.fontno; curenv->prev_fontno = env.prev_fontno; + curenv->glyph_color = env.glyph_color; + curenv->prev_glyph_color = env.prev_glyph_color; + curenv->fill_color = env.fill_color; + curenv->prev_fill_color = env.prev_fill_color; node *n = 0; node *p = part[2]; while (p != 0) { @@ -2239,7 +2387,7 @@ void title() hunits title_length(curenv->title_length); hunits f = title_length - part_width[1]; hunits f2 = f/2; - n = new hmotion_node(f2 - part_width[2], n); + n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n); p = part[1]; while (p != 0) { node *tem = p; @@ -2247,7 +2395,7 @@ void title() tem->next = n; n = tem; } - n = new hmotion_node(f - f2 - part_width[0], n); + n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n); p = part[0]; while (p != 0) { node *tem = p; @@ -2302,9 +2450,11 @@ void no_adjust() skip_line(); } -void input_trap() +void do_input_trap(int continued) { curenv->input_trap_count = 0; + if (continued) + curenv->continued_input_trap = 1; int n; if (has_arg() && get_integer(&n)) { if (n <= 0) @@ -2321,6 +2471,16 @@ void input_trap() skip_line(); } +void input_trap() +{ + do_input_trap(0); +} + +void input_trap_continued() +{ + do_input_trap(1); +} + /* tabs */ // must not be R or C or L or a legitimate part of a number expression @@ -2371,7 +2531,7 @@ tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t) } tab_stops::tab_stops(hunits distance, tab_type type) - : initial_list(0) +: initial_list(0) { repeated_list = new tab(distance, type); } @@ -2383,12 +2543,21 @@ tab_stops::~tab_stops() tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance) { + hunits nextpos; + + return distance_to_next_tab(curpos, distance, &nextpos); +} + +tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance, + hunits *nextpos) +{ hunits lastpos = 0; tab *tem; for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next) lastpos = tem->pos; if (tem) { *distance = tem->pos - curpos; + *nextpos = tem->pos; return tem->type; } if (repeated_list == 0) @@ -2399,6 +2568,7 @@ tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance) lastpos = tem->pos; if (tem) { *distance = tem->pos + base - curpos; + *nextpos = tem->pos + base; return tem->type; } assert(lastpos > 0); @@ -2476,7 +2646,7 @@ tab_stops::tab_stops() : initial_list(0), repeated_list(0) } tab_stops::tab_stops(const tab_stops &ts) - : initial_list(0), repeated_list(0) +: initial_list(0), repeated_list(0) { tab **p = &initial_list; tab *t = ts.initial_list; @@ -2573,7 +2743,7 @@ void set_tabs() } } curenv->tabs = tabs; - curenv->add_html_tag_tabs(); + curenv->add_html_tag_tabs(1); skip_line(); } @@ -2605,6 +2775,14 @@ tab_type environment::distance_to_next_tab(hunits *distance) : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance); } +tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos) +{ + return line_tabs + ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos) + : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance, + leftpos); +} + void field_characters() { field_delimiter_char = get_optional_char(); @@ -2683,7 +2861,7 @@ node *environment::make_tab_node(hunits d, node *next) leader_node = 0; } if (!leader_node) - return new hmotion_node(d, 1, 0, next); + return new hmotion_node(d, 1, 0, get_fill_color(), next); node *n = new hline_node(d, leader_node, next); leader_node = 0; return n; @@ -2692,31 +2870,34 @@ node *environment::make_tab_node(hunits d, node *next) void environment::handle_tab(int is_leader) { hunits d; + hunits abs; if (current_tab) wrap_up_tab(); charinfo *ci = is_leader ? leader_char : tab_char; delete leader_node; leader_node = ci ? make_char_node(ci) : 0; - tab_type t = distance_to_next_tab(&d); + tab_type t = distance_to_next_tab(&d, &abs); switch (t) { case TAB_NONE: return; case TAB_LEFT: - add_html_tag("tab left"); add_node(make_tab_node(d)); + add_node(make_html_tag("tab L", abs.to_units())); return; case TAB_RIGHT: + add_node(make_html_tag("tab R", abs.to_units())); + break; case TAB_CENTER: - add_html_tag("tab center"); - tab_width = 0; - tab_distance = d; - tab_contents = 0; - current_tab = t; - tab_field_spaces = 0; - return; + add_node(make_html_tag("tab C", abs.to_units())); + break; default: assert(0); } + tab_width = 0; + tab_distance = d; + tab_contents = 0; + current_tab = t; + tab_field_spaces = 0; } void environment::start_field() @@ -2792,13 +2973,13 @@ void environment::wrap_up_field() void environment::add_padding() { if (current_tab) { - tab_contents = new space_node(H0, tab_contents); + tab_contents = new space_node(H0, get_fill_color(), tab_contents); tab_field_spaces++; } else { if (line == 0) start_line(); - line = new space_node(H0, line); + line = new space_node(H0, get_fill_color(), line); field_spaces++; } } @@ -2921,6 +3102,12 @@ const char *environment::get_font_family_string() return family->nm.contents(); } +const char *environment::get_font_name_string() +{ + symbol f = get_font_name(fontno, this); + return f.contents(); +} + const char *environment::get_name_string() { return name.contents(); @@ -3001,12 +3188,14 @@ const char *environment::get_requested_point_size_string() void init_env_requests() { init_request("it", input_trap); + init_request("itc", input_trap_continued); init_request("ad", adjust); init_request("na", no_adjust); init_request("ev", environment_switch); init_request("evc", environment_copy); init_request("lt", title_length); init_request("ps", point_size); + init_request("sizes", override_sizes); init_request("ft", font_change); init_request("fam", family_change); init_request("ss", space_size); @@ -3070,6 +3259,7 @@ void init_env_requests() init_int_env_reg(".ss", get_space_size); init_int_env_reg(".sss", get_sentence_space_size); init_string_env_reg(".fam", get_font_family_string); + init_string_env_reg(".fn", get_font_name_string); init_string_env_reg(".ev", get_name_string); init_int_env_reg(".hy", get_hyphenation_flags); init_int_env_reg(".hlm", get_hyphen_line_max); @@ -3118,14 +3308,15 @@ class hyphen_trie : private trie { void do_match(int i, void *v); void do_delete(void *v); void insert_pattern(const char *pat, int patlen, int *num); + void insert_hyphenation(dictionary ex, const char *pat, int patlen); + int hpf_getc(FILE *f); public: hyphen_trie() {} ~hyphen_trie() {} void hyphenate(const char *word, int len, int *hyphens); - void read_patterns_file(const char *name); + void read_patterns_file(const char *name, int append, dictionary ex); }; - struct hyphenation_language { symbol name; dictionary exceptions; @@ -3150,7 +3341,8 @@ static void set_hyphenation_language() skip_line(); } -const int WORD_MAX = 1024; +const int WORD_MAX = 256; // we use unsigned char for offsets in + // hyphenation exceptions static void hyphen_word() { @@ -3189,7 +3381,7 @@ static void hyphen_word() pos[npos] = 0; buf[i] = 0; unsigned char *tem = new unsigned char[npos + 1]; - memcpy(tem, pos, npos+1); + memcpy(tem, pos, npos + 1); tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf), tem); if (tem) @@ -3290,6 +3482,33 @@ void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num) insert(pat, patlen, op); } +void hyphen_trie::insert_hyphenation(dictionary ex, const char *pat, + int patlen) +{ + char buf[WORD_MAX + 1]; + unsigned char pos[WORD_MAX + 2]; + int i = 0, j = 0; + int npos = 0; + while (j < patlen) { + unsigned char c = pat[j++]; + if (c == '-') { + if (i > 0 && (npos == 0 || pos[npos - 1] != i)) + pos[npos++] = i; + } + else + buf[i++] = hpf_code_table[c]; + } + if (i > 0) { + pos[npos] = 0; + buf[i] = 0; + unsigned char *tem = new unsigned char[npos + 1]; + memcpy(tem, pos, npos + 1); + tem = (unsigned char *)ex.lookup(symbol(buf), tem); + if (tem) + a_delete tem; + } +} + void hyphen_trie::hyphenate(const char *word, int len, int *hyphens) { int j; @@ -3324,10 +3543,78 @@ void hyphen_trie::do_delete(void *v) delete tem; } } - -void hyphen_trie::read_patterns_file(const char *name) + +/* We use very simple rules to parse TeX's hyphenation patterns. + + . `%' starts a comment even if preceded by `\'. + + . No support for digraphs and like `\$'. + + . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the + range 0-127) are recognized; other use of `^' causes an error. + + . No macro expansion. + + . We check for the expression `\patterns{...}' (possibly with + whitespace before and after the braces). Everything between the + braces is taken as hyphenation patterns. Consequently, `{' and `}' + are not allowed in patterns. + + . Similarly, `\hyphenation{...}' gives a list of hyphenation + exceptions. + + . `\endinput' is recognized also. + + . For backwards compatibility, if `\patterns' is missing, the + whole file is treated as a list of hyphenation patterns (only + recognizing `%' as the start of a comment. + +*/ + +int hyphen_trie::hpf_getc(FILE *f) +{ + int c = getc(f); + int c1; + int cc = 0; + if (c != '^') + return c; + c = getc(f); + if (c != '^') + goto fail; + c = getc(f); + c1 = getc(f); + if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) + && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) { + if (c >= '0' && c <= '9') + c -= '0'; + else + c = c - 'a' + 10; + if (c1 >= '0' && c1 <= '9') + c1 -= '0'; + else + c1 = c1 - 'a' + 10; + cc = c * 16 + c1; + } + else { + ungetc(c1, f); + if (c >= 0 && c <= 63) + cc = c + 64; + else if (c >= 64 && c <= 127) + cc = c - 64; + else + goto fail; + } + return cc; +fail: + error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file"); + return c; +} + +void hyphen_trie::read_patterns_file(const char *name, int append, + dictionary ex) { - clear(); + if (!append) + clear(); char buf[WORD_MAX]; int num[WORD_MAX+1]; errno = 0; @@ -3337,32 +3624,111 @@ void hyphen_trie::read_patterns_file(const char *name) error("can't find hyphenation patterns file `%1'", name); return; } - int c = getc(fp); + int c = hpf_getc(fp); + int have_patterns = 0; // we've seen \patterns + int final_pattern = 0; // 1 if we have a trailing closing brace + int have_hyphenation = 0; // we've seen \hyphenation + int final_hyphenation = 0; // 1 if we have a trailing closing brace + int have_keyword = 0; // we've seen either \patterns or \hyphenation + int traditional = 0; // don't handle \patterns for (;;) { for (;;) { - if (c == '%') { + if (c == '%') { // skip comments do { c = getc(fp); } while (c != EOF && c != '\n'); } if (c == EOF || !csspace(c)) break; - c = getc(fp); + c = hpf_getc(fp); + } + if (c == EOF) { + if (have_keyword || traditional) // we are done + break; + else { // rescan file in `traditional' mode + rewind(fp); + traditional = 1; + c = hpf_getc(fp); + continue; + } } - if (c == EOF) - break; int i = 0; num[0] = 0; - do { - if (csdigit(c)) - num[i] = c - '0'; - else { - buf[i++] = c; - num[i] = 0; + if (!(c == '{' || c == '}')) { // skip braces at line start + do { // scan patterns + if (csdigit(c)) + num[i] = c - '0'; + else { + buf[i++] = c; + num[i] = 0; + } + c = hpf_getc(fp); + } while (i < WORD_MAX && c != EOF && !csspace(c) + && c != '%' && c != '{' && c != '}'); + } + if (!traditional) { + if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) { + while (csspace(c)) + c = hpf_getc(fp); + if (c == '{') { + if (have_patterns || have_hyphenation) + error("`{' not allowed inside of \\patterns or \\hyphenation"); + else { + have_patterns = 1; + have_keyword = 1; + } + c = hpf_getc(fp); + continue; + } + } + else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) { + while (csspace(c)) + c = hpf_getc(fp); + if (c == '{') { + if (have_patterns || have_hyphenation) + error("`{' not allowed inside of \\patterns or \\hyphenation"); + else { + have_hyphenation = 1; + have_keyword = 1; + } + c = hpf_getc(fp); + continue; + } + } + else if (strstr(buf, "\\endinput")) { + if (have_patterns || have_hyphenation) + error("found \\endinput inside of %1 group", + have_patterns ? "\\patterns" : "\\hyphenation"); + break; + } + else if (c == '}') { + if (have_patterns) { + have_patterns = 0; + if (i > 0) + final_pattern = 1; + } + else if (have_hyphenation) { + have_hyphenation = 0; + if (i > 0) + final_hyphenation = 1; + } + c = hpf_getc(fp); } - c = getc(fp); - } while (i < WORD_MAX && c != EOF && !csspace(c) && c != '%'); - insert_pattern(buf, i, num); + else if (c == '{') // skipped if not starting \patterns + c = hpf_getc(fp); // or \hyphenation + } + if (i > 0) { + if (have_patterns || final_pattern || traditional) { + for (int j = 0; j < i; j++) + buf[j] = hpf_code_table[buf[j]]; + insert_pattern(buf, i, num); + final_pattern = 0; + } + else if (have_hyphenation || final_hyphenation) { + insert_hyphenation(ex, buf, i); + final_hyphenation = 0; + } + } } fclose(fp); a_delete path; @@ -3419,18 +3785,30 @@ void hyphenate(hyphen_list *h, unsigned flags) } } -static void hyphenation_patterns_file() +static void do_hyphenation_patterns_file(int append) { symbol name = get_long_name(1); if (!name.is_null()) { if (!current_language) error("no current hyphenation language"); else - current_language->patterns.read_patterns_file(name.contents()); + current_language->patterns.read_patterns_file( + name.contents(), append, + current_language->exceptions); } skip_line(); } +static void hyphenation_patterns_file() +{ + do_hyphenation_patterns_file(0); +} + +static void hyphenation_patterns_file_append() +{ + do_hyphenation_patterns_file(1); +} + class hyphenation_language_reg : public reg { public: const char *get_string(); @@ -3446,5 +3824,6 @@ void init_hyphen_requests() init_request("hw", hyphen_word); init_request("hla", set_hyphenation_language); init_request("hpf", hyphenation_patterns_file); + init_request("hpfa", hyphenation_patterns_file_append); number_reg_dictionary.define(".hla", new hyphenation_language_reg); } diff --git a/contrib/groff/src/roff/troff/env.h b/contrib/groff/src/roff/troff/env.h index 851a9a0..43a4c97 100644 --- a/contrib/groff/src/roff/troff/env.h +++ b/contrib/groff/src/roff/troff/env.h @@ -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) @@ -85,6 +85,7 @@ public: ~tab_stops(); void operator=(const tab_stops &); tab_type distance_to_next_tab(hunits pos, hunits *distance); + tab_type distance_to_next_tab(hunits curpos, hunits *distance, hunits *leftpos); void clear(); void add_tab(hunits pos, tab_type type, int repeated); const char *to_string(); @@ -140,6 +141,7 @@ class environment { int underline_spaces; symbol input_trap; int input_trap_count; + int continued_input_trap; node *line; // in reverse order hunits prev_text_length; hunits width_total; @@ -181,11 +183,15 @@ class environment { #ifdef WIDOW_CONTROL int widow_control; #endif /* WIDOW_CONTROL */ - int need_eol; int ignore_next_eol; int emitted_node; // have we emitted a node since the last html eol tag? + color *glyph_color; + color *prev_glyph_color; + color *fill_color; + color *prev_fill_color; tab_type distance_to_next_tab(hunits *); + tab_type distance_to_next_tab(hunits *distance, hunits *leftpos); void start_line(); void output_line(node *, hunits); void output(node *nd, int retain_size, vunits vs, vunits post_vs, @@ -261,6 +267,12 @@ public: int get_center_lines(); int get_right_justify_lines(); int get_prev_line_interrupted() { return prev_line_interrupted; } + color *get_fill_color(); + color *get_glyph_color(); + color *get_prev_glyph_color(); + color *get_prev_fill_color(); + void set_glyph_color(color *c); + void set_fill_color(color *c); node *make_char_node(charinfo *); node *extract_output_line(); void width_registers(); @@ -277,10 +289,11 @@ public: void possibly_break_line(int start_here = 0, int forced = 0); void do_break(int spread = 0); // .br void final_break(); - void add_html_tag_eol(); - void add_html_tag(const char *); - void add_html_tag(const char *, int); - void add_html_tag_tabs(); + void add_html_tag(int, const char *); + void add_html_tag(int, const char *, int); + void add_html_tag_tabs(int); + node *make_html_tag(const char *name, int i); + node *make_html_tag(const char *); void newline(); void handle_tab(int is_leader = 0); // do a tab or leader void add_node(node *); @@ -291,6 +304,7 @@ public: void space(hunits, hunits); void space_newline(); const char *get_font_family_string(); + const char *get_font_name_string(); const char *get_name_string(); const char *get_point_size_string(); const char *get_requested_point_size_string(); @@ -311,7 +325,7 @@ public: friend void indent(); friend void temporary_indent(); friend void do_underline(int); - friend void input_trap(); + friend void do_input_trap(int); friend void set_tabs(); friend void margin_character(); friend void no_number(); @@ -343,7 +357,12 @@ extern void push_env(int); void init_environments(); void read_hyphen_file(const char *name); +void title(); + +extern double spread_limit; extern int break_flag; extern symbol default_family; extern int translate_space_to_dummy; + +extern unsigned char hpf_code_table[]; 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; } diff --git a/contrib/groff/src/roff/troff/node.cc b/contrib/groff/src/roff/troff/node.cc index 2d2d799..650bf0e 100644 --- a/contrib/groff/src/roff/troff/node.cc +++ b/contrib/groff/src/roff/troff/node.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) @@ -19,11 +19,12 @@ You should have received a copy of the GNU General Public License along with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "troff.h" + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif -#include "troff.h" #include "symbol.h" #include "dictionary.h" #include "hvunits.h" @@ -35,6 +36,8 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "font.h" #include "reg.h" #include "input.h" +#include "div.h" +#include "geometry.h" #include "nonposix.h" @@ -60,8 +63,8 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ * debugging grohtml */ -static int image_no=0; -static int suppress_start_page=0; +int image_no = 0; +static int suppress_start_page = 0; #define STORE_WIDTH 1 @@ -147,6 +150,7 @@ public: int get_bold(hunits *); int is_special(); int is_style(); + friend symbol get_font_name(int, environment *); }; class tfont_spec { @@ -234,6 +238,14 @@ inline int font_info::is_style() return fm == 0; } +tfont *make_tfont(tfont_spec &spec) +{ + for (tfont *p = tfont::tfont_list; p; p = p->next) + if (*p == spec) + return p; + return new tfont(spec); +} + // this is the current_font, fontno is where we found the character, // presumably a special font @@ -378,6 +390,15 @@ symbol font_info::get_name() return internal_name; } +symbol get_font_name(int fontno, environment *env) +{ + symbol f = font_table[fontno]->get_name(); + if (font_table[fontno]->is_style()) { + return concat(env->get_family()->nm, f); + } + return f; +} + hunits font_info::get_space_width(font_size fs, int space_size) { if (is_constant_spaced == CONSTANT_SPACE_NONE) @@ -626,14 +647,6 @@ inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res) } } -tfont *make_tfont(tfont_spec &spec) -{ - for (tfont *p = tfont::tfont_list; p; p = p->next) - if (*p == spec) - return p; - return new tfont(spec); -} - tfont *tfont::tfont_list = 0; tfont::tfont(tfont_spec &spec) : tfont_spec(spec) @@ -658,7 +671,7 @@ class real_output_file : public output_file { int piped; #endif int printing; // decision via optional page list - int output_on; // .output 1 or .output 0 requests + int output_on; // \O[0] or \O[1] escape calls virtual void really_transparent_char(unsigned char) = 0; virtual void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width) = 0; @@ -727,6 +740,8 @@ class troff_output_file : public real_output_file { int current_slant; int current_height; tfont *current_tfont; + color *current_fill_color; + color *current_glyph_color; int current_font_number; symbol *font_position; int nfont_positions; @@ -739,6 +754,7 @@ class troff_output_file : public real_output_file { void put(char c); void put(unsigned char c); void put(int i); + void put(unsigned int i); void put(const char *s); void set_font(tfont *tf); void flush_tbuf(); @@ -746,13 +762,13 @@ public: troff_output_file(); ~troff_output_file(); void trailer(vunits page_length); - void put_char(charinfo *ci, tfont *tf); - void put_char_width(charinfo *ci, tfont *tf, hunits w, hunits k); + void put_char(charinfo *, tfont *, color *, color *); + void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits); void right(hunits); void down(vunits); void moveto(hunits, vunits); - void start_special(tfont *tf, int no_init_string = 0); - void start_special(int no_init_string = 0); + void start_special(tfont *, color *, color *, int = 0); + void start_special(); void special_char(unsigned char c); void end_special(); void word_marker(); @@ -763,9 +779,11 @@ public: void really_put_filename(const char *filename); void really_on(); void really_off(); - void draw(char, hvpair *, int, font_size); + void draw(char, hvpair *, int, font_size, color *, color *); void determine_line_limits (char code, hvpair *point, int npoints); void check_charinfo(tfont *tf, charinfo *ci); + void glyph_color(color *c); + void fill_color(color *c); int get_hpos() { return hpos; } int get_vpos() { return vpos; } }; @@ -796,29 +814,29 @@ inline void troff_output_file::put(int i) put_string(i_to_a(i), fp); } -void troff_output_file::start_special(tfont *tf, int no_init_string) +inline void troff_output_file::put(unsigned int i) { - flush_tbuf(); + put_string(ui_to_a(i), fp); +} - /* - * although this is extremely unlikely to have an effect on other devices - * this way is safer. Currently this is only needed for html. - */ - if (is_html && tf) { - if (tf != current_tfont) - set_font(tf); - } +void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol, + int no_init_string) +{ + flush_tbuf(); do_motion(); + if (tf != current_tfont) + set_font(tf); + glyph_color(gcol); + fill_color(fcol); if (!no_init_string) put("x X "); } -void troff_output_file::start_special(int no_init_string) +void troff_output_file::start_special() { flush_tbuf(); do_motion(); - if (!no_init_string) - put("x X "); + put("x X "); } void troff_output_file::special_char(unsigned char c) @@ -844,8 +862,7 @@ void troff_output_file::really_print_line(hunits x, vunits y, node *n, { moveto(x, y); while (n != 0) { - if (is_on() || (n->force_tprint())) - n->tprint(this); + n->tprint(this); n = n->next; } flush_tbuf(); @@ -864,7 +881,8 @@ void troff_output_file::really_print_line(hunits x, vunits y, node *n, inline void troff_output_file::word_marker() { flush_tbuf(); - put('w'); + if (is_on()) + put('w'); } inline void troff_output_file::right(hunits n) @@ -920,6 +938,11 @@ void troff_output_file::do_motion() void troff_output_file::flush_tbuf() { + if (!is_on()) { + tbuf_len = 0; + return; + } + if (tbuf_len == 0) return; if (tbuf_kern == 0) @@ -930,7 +953,7 @@ void troff_output_file::flush_tbuf() put(' '); } check_output_limits(hpos, vpos); - check_output_limits(hpos, vpos + current_size + current_height); + check_output_limits(hpos, vpos - current_size); for (int i = 0; i < tbuf_len; i++) put(tbuf[i]); @@ -940,29 +963,37 @@ void troff_output_file::flush_tbuf() void troff_output_file::check_charinfo(tfont *tf, charinfo *ci) { - int size = tf->get_size().to_scaled_points(); + if (!is_on()) + return; + int height = tf->get_char_height(ci).to_units(); int width = tf->get_width(ci).to_units() + tf->get_italic_correction(ci).to_units(); int depth = tf->get_char_depth(ci).to_units(); - check_output_limits(output_hpos, - output_vpos - height); - check_output_limits(output_hpos + width, - output_vpos + size + depth); + check_output_limits(output_hpos, output_vpos - height); + check_output_limits(output_hpos + width, output_vpos + depth); } -void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w, - hunits k) +void troff_output_file::put_char_width(charinfo *ci, tfont *tf, + color *gcol, color *fcol, + hunits w, hunits k) { + int kk = k.to_units(); + if (!is_on()) { + flush_tbuf(); + hpos += w.to_units() + kk; + return; + } if (tf != current_tfont) { flush_tbuf(); set_font(tf); } char c = ci->get_ascii_code(); - int kk = k.to_units(); if (c == '\0') { flush_tbuf(); do_motion(); + glyph_color(gcol); + fill_color(fcol); check_charinfo(tf, ci); if (ci->numbered()) { put('N'); @@ -983,6 +1014,7 @@ void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w, } else if (tcommand_flag) { if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos + && gcol == current_glyph_color && fcol == current_fill_color && kk == tbuf_kern && tbuf_len < TBUF_SIZE) { check_charinfo(tf, ci); @@ -993,6 +1025,8 @@ void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w, } flush_tbuf(); do_motion(); + glyph_color(gcol); + fill_color(fcol); check_charinfo(tf, ci); tbuf[tbuf_len++] = c; output_hpos += w.to_units() + kk; @@ -1004,7 +1038,9 @@ void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w, int n = hpos - output_hpos; check_charinfo(tf, ci); // check_output_limits(output_hpos, output_vpos); - if (vpos == output_vpos && n > 0 && n < 100 && !force_motion) { + if (vpos == output_vpos + && gcol == current_glyph_color && fcol == current_fill_color + && n > 0 && n < 100 && !force_motion) { put(char(n/10 + '0')); put(char(n%10 + '0')); put(c); @@ -1019,14 +1055,19 @@ void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w, } } -void troff_output_file::put_char(charinfo *ci, tfont *tf) +void troff_output_file::put_char(charinfo *ci, tfont *tf, + color *gcol, color *fcol) { flush_tbuf(); + if (!is_on()) + return; if (tf != current_tfont) set_font(tf); char c = ci->get_ascii_code(); if (c == '\0') { do_motion(); + glyph_color(gcol); + fill_color(fcol); if (ci->numbered()) { put('N'); put(ci->get_number()); @@ -1045,7 +1086,9 @@ void troff_output_file::put_char(charinfo *ci, tfont *tf) } else { int n = hpos - output_hpos; - if (vpos == output_vpos && n > 0 && n < 100) { + if (vpos == output_vpos + && gcol == current_glyph_color && fcol == current_fill_color + && n > 0 && n < 100) { put(char(n/10 + '0')); put(char(n%10 + '0')); put(c); @@ -1053,6 +1096,8 @@ void troff_output_file::put_char(charinfo *ci, tfont *tf) } else { do_motion(); + glyph_color(gcol); + fill_color(fcol); put('c'); put(c); } @@ -1115,12 +1160,112 @@ void troff_output_file::set_font(tfont *tf) current_tfont = tf; } +void troff_output_file::fill_color(color *col) +{ + if ((current_fill_color == col) || !color_flag) + return; + flush_tbuf(); + put("DF"); + unsigned int components[4]; + color_scheme cs; + cs = col->get_components(components); + switch (cs) { + case DEFAULT: + put('d'); + break; + case RGB: + put("r "); + put(Red); + put(' '); + put(Green); + put(' '); + put(Blue); + break; + case CMY: + put("c "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + break; + case CMYK: + put("k "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + put(' '); + put(Black); + break; + case GRAY: + put("g "); + put(Gray); + break; + } + put('\n'); + current_fill_color = col; +} + +void troff_output_file::glyph_color(color *col) +{ + if ((current_glyph_color == col) || !color_flag) + return; + flush_tbuf(); + put("m"); + unsigned int components[4]; + color_scheme cs; + cs = col->get_components(components); + switch (cs) { + case DEFAULT: + put('d'); + break; + case RGB: + put("r "); + put(Red); + put(' '); + put(Green); + put(' '); + put(Blue); + break; + case CMY: + put("c "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + break; + case CMYK: + put("k "); + put(Cyan); + put(' '); + put(Magenta); + put(' '); + put(Yellow); + put(' '); + put(Black); + break; + case GRAY: + put("g "); + put(Gray); + break; + } + put('\n'); + current_glyph_color = col; +} + // determine_line_limits - works out the smallest box which will contain // the entity, code, built from the point array. void troff_output_file::determine_line_limits(char code, hvpair *point, int npoints) { int i, x, y; + + if (!is_on()) + return; + switch (code) { case 'c': case 'C': @@ -1157,8 +1302,37 @@ void troff_output_file::determine_line_limits(char code, hvpair *point, check_output_limits(x, y); } break; + case 'a': + double c[2]; + int p[4]; + int minx, miny, maxx, maxy; + x = output_hpos; + y = output_vpos; + p[0] = point[0].h.to_units(); + p[1] = point[0].v.to_units(); + p[2] = point[1].h.to_units(); + p[3] = point[1].v.to_units(); + if (adjust_arc_center(p, c)) { + check_output_arc_limits(x, y, + p[0], p[1], p[2], p[3], + c[0], c[1], + &minx, &maxx, &miny, &maxy); + check_output_limits(minx, miny); + check_output_limits(maxx, maxy); + break; + } + // fall through + case 'l': + x = output_hpos; + y = output_vpos; + check_output_limits(x, y); + for (i = 0; i < npoints; i++) { + x += point[i].h.to_units(); + y += point[i].v.to_units(); + check_output_limits(x, y); + } + break; default: - // remember this doesn't work for arc.. yet x = output_hpos; y = output_vpos; for (i = 0; i < npoints; i++) { @@ -1170,34 +1344,37 @@ void troff_output_file::determine_line_limits(char code, hvpair *point, } void troff_output_file::draw(char code, hvpair *point, int npoints, - font_size fsize) + font_size fsize, color *gcol, color *fcol) { + int i; flush_tbuf(); do_motion(); - int size = fsize.to_scaled_points(); - if (current_size != size) { - put('s'); - put(size); - put('\n'); - current_size = size; - current_tfont = 0; - } - put('D'); - put(code); - int i; - if (code == 'c') { - put(' '); - put(point[0].h.to_units()); - } - else - for (i = 0; i < npoints; i++) { - put(' '); - put(point[i].h.to_units()); + glyph_color(gcol); + fill_color(fcol); + if (is_on()) { + int size = fsize.to_scaled_points(); + if (current_size != size) { + put('s'); + put(size); + put('\n'); + current_size = size; + current_tfont = 0; + } + put('D'); + put(code); + if (code == 'c') { put(' '); - put(point[i].v.to_units()); + put(point[0].h.to_units()); } - - determine_line_limits(code, point, npoints); + else + for (i = 0; i < npoints; i++) { + put(' '); + put(point[i].h.to_units()); + put(' '); + put(point[i].v.to_units()); + } + determine_line_limits(code, point, npoints); + } for (i = 0; i < npoints; i++) output_hpos += point[i].h.to_units(); @@ -1207,15 +1384,18 @@ void troff_output_file::draw(char code, hvpair *point, int npoints, output_vpos += point[i].v.to_units(); vpos = output_vpos; } - put('\n'); + if (is_on()) + put('\n'); } -void troff_output_file::really_on () +void troff_output_file::really_on() { flush_tbuf(); + force_motion = 1; + do_motion(); } -void troff_output_file::really_off () +void troff_output_file::really_off() { flush_tbuf(); } @@ -1412,7 +1592,7 @@ int real_output_file::is_printing() void real_output_file::begin_page(int pageno, vunits page_length) { printing = in_output_page_list(pageno); - if (printing && output_on) + if (printing) really_begin_page(pageno, page_length); } @@ -1454,9 +1634,8 @@ void real_output_file::really_put_filename(const char *filename) void real_output_file::on() { really_on(); - if (output_on == 0) { + if (output_on == 0) output_on = 1; - } } void real_output_file::off() @@ -1467,7 +1646,7 @@ void real_output_file::off() int real_output_file::is_on() { - return( output_on ); + return output_on; } void real_output_file::really_on() @@ -1562,14 +1741,16 @@ class glyph_node : public charinfo_node { static glyph_node *free_list; protected: tfont *tf; + color *gcol; + color *fcol; /* this is needed for grotty */ #ifdef STORE_WIDTH hunits wid; - glyph_node(charinfo *, tfont *, hunits, node * = 0); + glyph_node(charinfo *, tfont *, color *, color *, hunits, node * = 0); #endif public: void *operator new(size_t); void operator delete(void *); - glyph_node(charinfo *, tfont *, node * = 0); + glyph_node(charinfo *, tfont *, color *, color *, node * = 0); ~glyph_node() {} node *copy(); node *merge_glyph_node(glyph_node *); @@ -1584,6 +1765,8 @@ public: hunits skew(); hyphenation_type get_hyphenation_type(); tfont *get_tfont(); + color *get_glyph_color(); + color *get_fill_color(); void tprint(troff_output_file *); void zero_width_tprint(troff_output_file *); hyphen_list *get_hyphen_list(hyphen_list *ss = 0); @@ -1602,12 +1785,14 @@ class ligature_node : public glyph_node { node *n1; node *n2; #ifdef STORE_WIDTH - ligature_node(charinfo *, tfont *, hunits, node *gn1, node *gn2, node *x = 0); + ligature_node(charinfo *, tfont *, color *, color *, hunits, + node *, node *, node * = 0); #endif public: void *operator new(size_t); void operator delete(void *); - ligature_node(charinfo *, tfont *, node *gn1, node *gn2, node *x = 0); + ligature_node(charinfo *, tfont *, color *, color *, + node *, node *, node * = 0); ~ligature_node(); node *copy(); node *add_self(node *, hyphen_list **); @@ -1708,8 +1893,8 @@ void ligature_node::operator delete(void *p) delete[] (char *)p; } -glyph_node::glyph_node(charinfo *c, tfont *t, node *x) -: charinfo_node(c, x), tf(t) +glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc, node *x) +: charinfo_node(c, x), tf(t), gcol(gc), fcol(fc) { #ifdef STORE_WIDTH wid = tf->get_width(ci); @@ -1717,8 +1902,9 @@ glyph_node::glyph_node(charinfo *c, tfont *t, node *x) } #ifdef STORE_WIDTH -glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x) -: charinfo_node(c, x), tf(t), wid(w) +glyph_node::glyph_node(charinfo *c, tfont *t, + color *gc, color *fc, hunits w, node *x) +: charinfo_node(c, x), tf(t), gcol(gc), fcol(fc), wid(w) { } #endif @@ -1726,9 +1912,9 @@ glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x) node *glyph_node::copy() { #ifdef STORE_WIDTH - return new glyph_node(ci, tf, wid); + return new glyph_node(ci, tf, gcol, fcol, wid); #else - return new glyph_node(ci, tf); + return new glyph_node(ci, tf, gcol, fcol); #endif } @@ -1779,6 +1965,26 @@ tfont *glyph_node::get_tfont() return tf; } +color *node::get_glyph_color() +{ + return 0; +} + +color *glyph_node::get_glyph_color() +{ + return gcol; +} + +color *node::get_fill_color() +{ + return 0; +} + +color *glyph_node::get_fill_color() +{ + return fcol; +} + node *node::merge_glyph_node(glyph_node * /*gn*/) { return 0; @@ -1786,12 +1992,12 @@ node *node::merge_glyph_node(glyph_node * /*gn*/) node *glyph_node::merge_glyph_node(glyph_node *gn) { - if (tf == gn->tf) { + if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) { charinfo *lig; if ((lig = tf->get_lig(ci, gn->ci)) != 0) { node *next1 = next; next = 0; - return new ligature_node(lig, tf, this, gn, next1); + return new ligature_node(lig, tf, gcol, fcol, this, gn, next1); } hunits kern; if (tf->get_kern(ci, gn->ci, &kern)) { @@ -1860,16 +2066,16 @@ void glyph_node::ascii_print(ascii_output_file *ascii) ascii->outs(ci->nm.contents()); } -ligature_node::ligature_node(charinfo *c, tfont *t, +ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc, node *gn1, node *gn2, node *x) -: glyph_node(c, t, x), n1(gn1), n2(gn2) +: glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2) { } #ifdef STORE_WIDTH -ligature_node::ligature_node(charinfo *c, tfont *t, hunits w, - node *gn1, node *gn2, node *x) -: glyph_node(c, t, w, x), n1(gn1), n2(gn2) +ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc, + hunits w, node *gn1, node *gn2, node *x) +: glyph_node(c, t, gc, fc, w, x), n1(gn1), n2(gn2) { } #endif @@ -1883,9 +2089,9 @@ ligature_node::~ligature_node() node *ligature_node::copy() { #ifdef STORE_WIDTH - return new ligature_node(ci, tf, wid, n1->copy(), n2->copy()); + return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy()); #else - return new ligature_node(ci, tf, n1->copy(), n2->copy()); + return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy()); #endif } @@ -1986,10 +2192,12 @@ node *kern_pair_node::add_discretionary_hyphen() tfont *tf = n2->get_tfont(); if (tf) { if (tf->contains(soft_hyphen_char)) { + color *gcol = n2->get_glyph_color(); + color *fcol = n2->get_fill_color(); node *next1 = next; next = 0; node *n = copy(); - glyph_node *gn = new glyph_node(soft_hyphen_char, tf); + glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol); node *nn = n->merge_glyph_node(gn); if (nn == 0) { gn->next = n; @@ -2121,10 +2329,12 @@ node *node::add_discretionary_hyphen() if (!tf) return new hyphen_inhibitor_node(this); if (tf->contains(soft_hyphen_char)) { + color *gcol = get_glyph_color(); + color *fcol = get_fill_color(); node *next1 = next; next = 0; node *n = copy(); - glyph_node *gn = new glyph_node(soft_hyphen_char, tf); + glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol); node *n1 = n->merge_glyph_node(gn); if (n1 == 0) { gn->next = n; @@ -2362,8 +2572,9 @@ int italic_corrected_node::character_type() class break_char_node : public node { node *ch; char break_code; + color *col; public: - break_char_node(node *, int, node * = 0); + break_char_node(node *, int, color *, node * = 0); ~break_char_node(); node *copy(); hunits width(); @@ -2387,8 +2598,8 @@ public: int force_tprint(); }; -break_char_node::break_char_node(node *n, int c, node *x) -: node(x), ch(n), break_code(c) +break_char_node::break_char_node(node *n, int bc, color *c, node *x) +: node(x), ch(n), break_code(bc), col(c) { } @@ -2399,7 +2610,7 @@ break_char_node::~break_char_node() node *break_char_node::copy() { - return new break_char_node(ch->copy(), break_code); + return new break_char_node(ch->copy(), break_code, col); } hunits break_char_node::width() @@ -2431,13 +2642,13 @@ node *break_char_node::add_self(node *n, hyphen_list **p) { assert((*p)->hyphenation_code == 0); if ((*p)->breakable && (break_code & 1)) { - n = new space_node(H0, n); + n = new space_node(H0, col, n); n->freeze_space(); } next = n; n = this; if ((*p)->breakable && (break_code & 2)) { - n = new space_node(H0, n); + n = new space_node(H0, col, n); n->freeze_space(); } hyphen_list *pp = *p; @@ -2493,17 +2704,17 @@ node *vertical_size_node::copy() node *hmotion_node::copy() { - return new hmotion_node(n, was_tab, unformat); + return new hmotion_node(n, was_tab, unformat, col); } node *space_char_hmotion_node::copy() { - return new space_char_hmotion_node(n); + return new space_char_hmotion_node(n, col); } node *vmotion_node::copy() { - return new vmotion_node(n); + return new vmotion_node(n, col); } node *dummy_node::copy() @@ -2648,6 +2859,8 @@ node *bracket_node::copy() bracket_node *on = new bracket_node; node *last = 0; node *tem; + if (list) + list->last = 0; for (tem = list; tem; tem = tem->next) { if (tem->next) tem->next->last = tem; @@ -2711,13 +2924,13 @@ inline void space_node::operator delete(void *p) } #endif -space_node::space_node(hunits nn, node *p) -: node(p), n(nn), set(0), was_escape_colon(0) +space_node::space_node(hunits nn, color *c, node *p) +: node(p), n(nn), set(0), was_escape_colon(0), col(c) { } -space_node::space_node(hunits nn, int s, int flag, node *p) -: node(p), n(nn), set(s), was_escape_colon(flag) +space_node::space_node(hunits nn, int s, int flag, color *c, node *p) +: node(p), n(nn), set(s), was_escape_colon(flag), col(c) { } @@ -2729,7 +2942,7 @@ space_node::~space_node() node *space_node::copy() { - return new space_node(n, set, was_escape_colon); + return new space_node(n, set, was_escape_colon, col); } int space_node::force_tprint() @@ -3025,7 +3238,9 @@ void node::asciify(macro *m) void glyph_node::asciify(macro *m) { - unsigned char c = ci->get_ascii_code(); + unsigned char c = ci->get_asciify_code(); + if (c == 0) + c = ci->get_ascii_code(); if (c != 0) { m->append(c); delete this; @@ -3098,8 +3313,9 @@ void hmotion_node::asciify(macro *m) m->append(this); } -space_char_hmotion_node::space_char_hmotion_node(hunits i, node *next) -: hmotion_node(i, next) +space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c, + node *next) +: hmotion_node(i, c, next) { } @@ -3153,8 +3369,8 @@ int node::nbreaks() return 0; } -breakpoint *space_node::get_breakpoints(hunits width, int ns, breakpoint *rest, - int is_inner) +breakpoint *space_node::get_breakpoints(hunits width, int ns, + breakpoint *rest, int is_inner) { if (next->discardable()) return rest; @@ -3351,23 +3567,27 @@ special_node::special_node(const macro &m, int n) font_size fs = curenv->get_font_size(); int char_height = curenv->get_char_height(); int char_slant = curenv->get_char_slant(); - int fontno = curenv->get_font(); - tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, - fontno); + int fontno = env_definite_font(curenv); + tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno); if (curenv->is_composite()) tf = tf->get_plain(); + gcol = curenv->get_glyph_color(); + fcol = curenv->get_fill_color(); } -special_node::special_node(const macro &m, tfont *t, int n) -: mac(m), tf(t), no_init_string(n) +special_node::special_node(const macro &m, tfont *t, + color *gc, color *fc, int n) +: mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n) { } int special_node::same(node *n) { - return ((mac == ((special_node *)n)->mac) - && (tf == ((special_node *)n)->tf) - && (no_init_string == ((special_node *)n)->no_init_string)); + return mac == ((special_node *)n)->mac + && tf == ((special_node *)n)->tf + && gcol == ((special_node *)n)->gcol + && fcol == ((special_node *)n)->fcol + && no_init_string == ((special_node *)n)->no_init_string; } const char *special_node::type() @@ -3387,12 +3607,12 @@ int special_node::force_tprint() node *special_node::copy() { - return new special_node(mac, tf, no_init_string); + return new special_node(mac, tf, gcol, fcol, no_init_string); } void special_node::tprint_start(troff_output_file *out) { - out->start_special(get_tfont(), no_init_string); + out->start_special(tf, gcol, fcol, no_init_string); } void special_node::tprint_char(troff_output_file *out, unsigned char c) @@ -3413,18 +3633,20 @@ tfont *special_node::get_tfont() /* suppress_node */ suppress_node::suppress_node(int on_or_off, int issue_limits) -: is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0) +: is_on(on_or_off), emit_limits(issue_limits), + filename(0), position(0), image_id(0) { } -suppress_node::suppress_node(symbol f, char p) -: is_on(2), emit_limits(0), filename(f), position(p) +suppress_node::suppress_node(symbol f, char p, int id) +: is_on(2), emit_limits(0), filename(f), position(p), image_id(id) { } suppress_node::suppress_node(int issue_limits, int on_or_off, - symbol f, char p) -: is_on(on_or_off), emit_limits(issue_limits), filename(f), position(p) + symbol f, char p, int id) +: is_on(on_or_off), emit_limits(issue_limits), + filename(f), position(p), image_id(id) { } @@ -3433,8 +3655,8 @@ int suppress_node::same(node *n) return ((is_on == ((suppress_node *)n)->is_on) && (emit_limits == ((suppress_node *)n)->emit_limits) && (filename == ((suppress_node *)n)->filename) - && (position == ((suppress_node *)n)->position)); -; + && (position == ((suppress_node *)n)->position) + && (image_id == ((suppress_node *)n)->image_id)); } const char *suppress_node::type() @@ -3444,7 +3666,7 @@ const char *suppress_node::type() node *suppress_node::copy() { - return new suppress_node(emit_limits, is_on, filename, position); + return new suppress_node(emit_limits, is_on, filename, position, image_id); } int get_reg_int(const char *p) @@ -3483,6 +3705,7 @@ void suppress_node::put(troff_output_file *out, const char *s) static char last_position = 0; static const char *last_image_filename = 0; +static int last_image_id = 0; inline int min(int a, int b) { @@ -3508,22 +3731,23 @@ inline int min(int a, int b) void suppress_node::tprint(troff_output_file *out) { - int current_page = get_reg_int("%"); + int current_page = topdiv->get_page_number(); // firstly check to see whether this suppress node contains // an image filename & position. if (is_on == 2) { // remember position and filename last_position = position; - last_image_filename = filename.contents(); + last_image_filename = strdup(filename.contents()); + last_image_id = image_id; + // printf("start of image and page = %d\n", current_page); } else { // now check whether the suppress node requires us to issue limits. if (emit_limits) { char name[8192]; - image_no++; // remember that the filename will contain a %d in which the - // image_no is placed - sprintf(name, last_image_filename, image_no); + // last_image_id is placed + sprintf(name, last_image_filename, last_image_id); if (is_html) { switch (last_position) { case 'c': @@ -3551,14 +3775,18 @@ void suppress_node::tprint(troff_output_file *out) } else { // postscript (or other device) - if (current_page != suppress_start_page) + if (suppress_start_page > 0 && current_page != suppress_start_page) error("suppression limit registers span more than one page;\n" "image description %1 will be wrong", image_no); + // if (topdiv->get_page_number() != suppress_start_page) + // fprintf(stderr, "end of image and topdiv page = %d and suppress_start_page = %d\n", + // topdiv->get_page_number(), suppress_start_page); + // remember that the filename will contain a %d in which the // image_no is placed fprintf(stderr, "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n", - current_page, + topdiv->get_page_number(), get_reg_int("opminx"), get_reg_int("opminy"), get_reg_int("opmaxx"), get_reg_int("opmaxy"), // page offset + line length @@ -3568,12 +3796,13 @@ void suppress_node::tprint(troff_output_file *out) } } else { - if (is_on) + if (is_on) { out->on(); + // lastly we reset the output registers + reset_output_registers(out->get_vpos()); + } else out->off(); - // lastly we reset the output registers - reset_output_registers(out->get_vpos()); suppress_start_page = current_page; } } @@ -3670,7 +3899,9 @@ hyphenation_type composite_node::get_hyphenation_type() void composite_node::asciify(macro *m) { - unsigned char c = ci->get_ascii_code(); + unsigned char c = ci->get_asciify_code(); + if (c == 0) + c = ci->get_ascii_code(); if (c != 0) { m->append(c); delete this; @@ -3741,14 +3972,14 @@ width_list::width_list(width_list *w) { } -word_space_node::word_space_node(hunits d, width_list *w, node *x) -: space_node(d, x), orig_width(w), unformat(0) +word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x) +: space_node(d, c, x), orig_width(w), unformat(0) { } -word_space_node::word_space_node(hunits d, int s, width_list *w, +word_space_node::word_space_node(hunits d, int s, color *c, width_list *w, int flag, node *x) -: space_node(d, s, 0, x), orig_width(w), unformat(flag) +: space_node(d, s, 0, c, x), orig_width(w), unformat(flag) { } @@ -3774,7 +4005,7 @@ node *word_space_node::copy() w_new_curr = w_new_curr->next; w_old_curr = w_old_curr->next; } - return new word_space_node(n, set, w_new, unformat); + return new word_space_node(n, set, col, w_new, unformat); } int word_space_node::set_unformat_flag() @@ -3785,8 +4016,9 @@ int word_space_node::set_unformat_flag() void word_space_node::tprint(troff_output_file *out) { + out->fill_color(col); out->word_marker(); - space_node::tprint(out); + out->right(n); } int word_space_node::merge_space(hunits h, hunits sw, hunits ssw) @@ -3800,19 +4032,20 @@ int word_space_node::merge_space(hunits h, hunits sw, hunits ssw) return 1; } -unbreakable_space_node::unbreakable_space_node(hunits d, node *x) -: word_space_node(d, 0, x) +unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x) +: word_space_node(d, c, 0, x) { } -unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x) -: word_space_node(d, s, 0, 0, x) +unbreakable_space_node::unbreakable_space_node(hunits d, int s, + color *c, node *x) +: word_space_node(d, s, c, 0, 0, x) { } node *unbreakable_space_node::copy() { - return new unbreakable_space_node(n, set); + return new unbreakable_space_node(n, set, col); } int unbreakable_space_node::force_tprint() @@ -3845,8 +4078,9 @@ hvpair::hvpair() { } -draw_node::draw_node(char c, hvpair *p, int np, font_size s) -: npoints(np), sz(s), code(c) +draw_node::draw_node(char c, hvpair *p, int np, font_size s, + color *gc, color *fc) +: npoints(np), sz(s), gcol(gc), fcol(fc), code(c) { point = new hvpair[npoints]; for (int i = 0; i < npoints; i++) @@ -3856,7 +4090,8 @@ draw_node::draw_node(char c, hvpair *p, int np, font_size s) int draw_node::same(node *n) { draw_node *nd = (draw_node *)n; - if (code != nd->code || npoints != nd->npoints || sz != nd->sz) + if (code != nd->code || npoints != nd->npoints || sz != nd->sz + || gcol != nd->gcol || fcol != nd->fcol) return 0; for (int i = 0; i < npoints; i++) if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v) @@ -3900,12 +4135,12 @@ vunits draw_node::vertical_width() node *draw_node::copy() { - return new draw_node(code, point, npoints, sz); + return new draw_node(code, point, npoints, sz, gcol, fcol); } void draw_node::tprint(troff_output_file *out) { - out->draw(code, point, npoints, sz); + out->draw(code, point, npoints, sz, gcol, fcol); } /* tprint methods */ @@ -3914,7 +4149,7 @@ void glyph_node::tprint(troff_output_file *out) { tfont *ptf = tf->get_plain(); if (ptf == tf) - out->put_char_width(ci, ptf, width(), H0); + out->put_char_width(ci, ptf, gcol, fcol, width(), H0); else { hunits offset; int bold = tf->get_bold(&offset); @@ -3933,10 +4168,10 @@ void glyph_node::tprint(troff_output_file *out) else k = tf->get_track_kern(); if (bold) { - out->put_char(ci, ptf); + out->put_char(ci, ptf, gcol, fcol); out->right(offset); } - out->put_char_width(ci, ptf, w, k); + out->put_char_width(ci, ptf, gcol, fcol, w, k); } } @@ -3954,10 +4189,10 @@ void glyph_node::zero_width_tprint(troff_output_file *out) x = x/2; out->right(x); } - out->put_char(ci, ptf); + out->put_char(ci, ptf, gcol, fcol); if (bold) { out->right(offset); - out->put_char(ci, ptf); + out->put_char(ci, ptf, gcol, fcol); out->right(-offset); } if (cs) @@ -3995,20 +4230,23 @@ void hline_node::tprint(troff_output_file *out) hunits xx = x - w; hunits xx2 = xx/2; out->right(xx2); - n->tprint(out); + if (out->is_on()) + n->tprint(out); out->right(xx - xx2); } else { hunits rem = x - w*i; if (rem > H0) if (n->overlaps_horizontally()) { - n->tprint(out); + if (out->is_on()) + n->tprint(out); out->right(rem - w); } else out->right(rem); while (--i >= 0) - n->tprint(out); + if (out->is_on()) + n->tprint(out); } } @@ -4037,11 +4275,13 @@ void vline_node::tprint(troff_output_file *out) if (overlaps) { n->zero_width_tprint(out); out->down(-rem); - n->tprint(out); + if (out->is_on()) + n->tprint(out); out->down(-h); } else { - n->tprint(out); + if (out->is_on()) + n->tprint(out); out->down(-h - rem); } } @@ -4062,7 +4302,8 @@ void vline_node::tprint(troff_output_file *out) n->zero_width_tprint(out); out->down(h); } - n->tprint(out); + if (out->is_on()) + n->tprint(out); } } } @@ -4131,16 +4372,19 @@ void node::zero_width_tprint(troff_output_file *out) void space_node::tprint(troff_output_file *out) { + out->fill_color(col); out->right(n); } void hmotion_node::tprint(troff_output_file *out) { + out->fill_color(col); out->right(n); } void vmotion_node::tprint(troff_output_file *out) { + out->fill_color(col); out->down(n); } @@ -4226,6 +4470,8 @@ node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0) int fn = fontno; int found = font_table[fontno]->contains(s); if (!found) { + if (s->is_fallback()) + return make_composite_node(s, env); if (s->numbered()) { if (!no_error_message) warning(WARN_CHAR, "can't find numbered character %1", @@ -4284,16 +4530,20 @@ node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0) tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn); if (env->is_composite()) tf = tf->get_plain(); - return new glyph_node(s, tf); + color *gcol = env->get_glyph_color(); + color *fcol = env->get_fill_color(); + return new glyph_node(s, tf, gcol, fcol); } node *make_node(charinfo *ci, environment *env) { switch (ci->get_special_translation()) { case charinfo::TRANSLATE_SPACE: - return new space_char_hmotion_node(env->get_space_width()); + return new space_char_hmotion_node(env->get_space_width(), + env->get_fill_color()); case charinfo::TRANSLATE_STRETCHABLE_SPACE: - return new unbreakable_space_node(env->get_space_width()); + return new unbreakable_space_node(env->get_space_width(), + env->get_fill_color()); case charinfo::TRANSLATE_DUMMY: return new dummy_node; case charinfo::TRANSLATE_HYPHEN_INDICATOR: @@ -4304,7 +4554,7 @@ node *make_node(charinfo *ci, environment *env) if (tem) ci = tem; macro *mac = ci->get_macro(); - if (mac) + if (mac && !ci->is_fallback()) return make_composite_node(ci, env); else return make_glyph_node(ci, env); @@ -4333,11 +4583,14 @@ node *node::add_char(charinfo *ci, environment *env, node *res; switch (ci->get_special_translation()) { case charinfo::TRANSLATE_SPACE: - res = new space_char_hmotion_node(env->get_space_width(), this); + res = new space_char_hmotion_node(env->get_space_width(), + env->get_fill_color(), this); *widthp += res->width(); return res; case charinfo::TRANSLATE_STRETCHABLE_SPACE: - res = new unbreakable_space_node(env->get_space_width(), this); + res = new unbreakable_space_node(env->get_space_width(), + env->get_fill_color(), this); + res->freeze_space(); *widthp += res->width(); *spacep += res->nspaces(); return res; @@ -4350,7 +4603,7 @@ node *node::add_char(charinfo *ci, environment *env, if (tem) ci = tem; macro *mac = ci->get_macro(); - if (mac) { + if (mac && !ci->is_fallback()) { res = make_composite_node(ci, env); if (res) { res->next = this; @@ -4385,7 +4638,7 @@ node *node::add_char(charinfo *ci, environment *env, if (break_code) { node *next1 = res->next; res->next = 0; - res = new break_char_node(res, break_code, next1); + res = new break_char_node(res, break_code, env->get_fill_color(), next1); } return res; } @@ -4453,7 +4706,8 @@ int vertical_size_node::force_tprint() int hmotion_node::same(node *nd) { - return n == ((hmotion_node *)nd)->n; + return n == ((hmotion_node *)nd)->n + && col == ((hmotion_node *)nd)->col; } const char *hmotion_node::type() @@ -4488,7 +4742,8 @@ hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail) int space_char_hmotion_node::same(node *nd) { - return n == ((space_char_hmotion_node *)nd)->n; + return n == ((space_char_hmotion_node *)nd)->n + && col == ((space_char_hmotion_node *)nd)->col; } const char *space_char_hmotion_node::type() @@ -4517,7 +4772,8 @@ hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail) int vmotion_node::same(node *nd) { - return n == ((vmotion_node *)nd)->n; + return n == ((vmotion_node *)nd)->n + && col == ((vmotion_node *)nd)->col; } const char *vmotion_node::type() @@ -4841,7 +5097,10 @@ int composite_node::force_tprint() int glyph_node::same(node *nd) { - return ci == ((glyph_node *)nd)->ci && tf == ((glyph_node *)nd)->tf; + return ci == ((glyph_node *)nd)->ci + && tf == ((glyph_node *)nd)->tf + && gcol == ((glyph_node *)nd)->gcol + && fcol == ((glyph_node *)nd)->fcol; } const char *glyph_node::type() @@ -4907,8 +5166,9 @@ int dbreak_node::force_tprint() int break_char_node::same(node *nd) { - return (break_code == ((break_char_node *)nd)->break_code - && same_node(ch, ((break_char_node *)nd)->ch)); + return break_code == ((break_char_node *)nd)->break_code + && col == ((break_char_node *)nd)->col + && same_node(ch, ((break_char_node *)nd)->ch); } const char *break_char_node::type() @@ -4938,7 +5198,9 @@ int line_start_node::force_tprint() int space_node::same(node *nd) { - return n == ((space_node *)nd)->n && set == ((space_node *)nd)->set; + return n == ((space_node *)nd)->n + && set == ((space_node *)nd)->set + && col == ((space_node *)nd)->col; } const char *space_node::type() @@ -4948,8 +5210,9 @@ const char *space_node::type() int word_space_node::same(node *nd) { - return (n == ((word_space_node *)nd)->n - && set == ((word_space_node *)nd)->set); + return n == ((word_space_node *)nd)->n + && set == ((word_space_node *)nd)->set + && col == ((word_space_node *)nd)->col; } const char *word_space_node::type() @@ -4964,8 +5227,9 @@ int word_space_node::force_tprint() int unbreakable_space_node::same(node *nd) { - return (n == ((unbreakable_space_node *)nd)->n - && set == ((unbreakable_space_node *)nd)->set); + return n == ((unbreakable_space_node *)nd)->n + && set == ((unbreakable_space_node *)nd)->set + && col == ((unbreakable_space_node *)nd)->col; } const char *unbreakable_space_node::type() @@ -5345,7 +5609,7 @@ int symbol_fontno(symbol s) int is_good_fontno(int n) { - return n >= 0 && n < font_table_size && font_table[n] != NULL; + return n >= 0 && n < font_table_size && font_table[n] != 0; } int get_bold_fontno(int n) diff --git a/contrib/groff/src/roff/troff/node.h b/contrib/groff/src/roff/troff/node.h index a58afed..13295f4 100644 --- a/contrib/groff/src/roff/troff/node.h +++ b/contrib/groff/src/roff/troff/node.h @@ -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) @@ -97,6 +97,8 @@ struct node { virtual node *merge_glyph_node(glyph_node *); virtual tfont *get_tfont(); + virtual color *get_glyph_color(); + virtual color *get_fill_color(); virtual void tprint(troff_output_file *); virtual void zero_width_tprint(troff_output_file *); @@ -106,11 +108,13 @@ struct node { virtual const char *type() = 0; }; -inline node::node() : next(0), last(0) +inline node::node() +: next(0), last(0) { } -inline node::node(node *n) : next(n), last(0) +inline node::node(node *n) +: next(n), last(0) { } @@ -152,9 +156,10 @@ protected: hunits n; char set; char was_escape_colon; - space_node(hunits, int, int, node * = 0); + color *col; /* for grotty */ + space_node(hunits, int, int, color *, node * = 0); public: - space_node(hunits d, node *p = 0); + space_node(hunits, color *, node * = 0); #if 0 ~space_node(); void *operator new(size_t); @@ -192,9 +197,9 @@ class word_space_node : public space_node { protected: width_list *orig_width; unsigned char unformat; - word_space_node(hunits, int, width_list *, int, node * = 0); + word_space_node(hunits, int, color *, width_list *, int, node * = 0); public: - word_space_node(hunits, width_list *, node * = 0); + word_space_node(hunits, color *, width_list *, node * = 0); ~word_space_node(); node *copy(); int reread(int *); @@ -208,9 +213,9 @@ public: }; class unbreakable_space_node : public word_space_node { - unbreakable_space_node(hunits, int, node * = 0); + unbreakable_space_node(hunits, int, color *, node * = 0); public: - unbreakable_space_node(hunits, node * = 0); + unbreakable_space_node(hunits, color *, node * = 0); node *copy(); int reread(int *); int same(node *); @@ -279,11 +284,12 @@ protected: hunits n; unsigned char was_tab; unsigned char unformat; + color *col; /* for grotty */ public: - hmotion_node(hunits i, node *next = 0) - : node(next), n(i), was_tab(0), unformat(0) {} - hmotion_node(hunits i, int flag1, int flag2, node *next = 0) - : node(next), n(i), was_tab(flag1), unformat(flag2) {} + hmotion_node(hunits i, color *c, node *next = 0) + : node(next), n(i), was_tab(0), unformat(0), col(c) {} + hmotion_node(hunits i, int flag1, int flag2, color *c, node *next = 0) + : node(next), n(i), was_tab(flag1), unformat(flag2), col(c) {} node *copy(); int reread(int *); int set_unformat_flag(); @@ -301,7 +307,7 @@ public: class space_char_hmotion_node : public hmotion_node { public: - space_char_hmotion_node(hunits i, node *next = 0); + space_char_hmotion_node(hunits, color *, node * = 0); node *copy(); void ascii_print(ascii_output_file *); void asciify(macro *); @@ -315,8 +321,9 @@ public: class vmotion_node : public node { vunits n; + color *col; /* for grotty */ public: - vmotion_node(vunits i) : n(i) {} + vmotion_node(vunits i, color *c) : n(i), col(c) {} void tprint(troff_output_file *); node *copy(); vunits vertical_width(); @@ -458,13 +465,15 @@ public: class special_node : public node { macro mac; tfont *tf; + color *gcol; + color *fcol; int no_init_string; void tprint_start(troff_output_file *); void tprint_char(troff_output_file *, unsigned char); void tprint_end(troff_output_file *); public: special_node(const macro &, int = 0); - special_node(const macro &, tfont *, int = 0); + special_node(const macro &, tfont *, color *, color *, int = 0); node *copy(); void tprint(troff_output_file *); int same(node *); @@ -479,10 +488,11 @@ class suppress_node : public node { int emit_limits; // must we issue the extent of the area written out? symbol filename; char position; + int image_id; public: suppress_node(int, int); - suppress_node(symbol f, char p); - suppress_node(int, int, symbol f, char p); + suppress_node(symbol f, char p, int id); + suppress_node(int, int, symbol f, char p, int id); node *copy(); void tprint(troff_output_file *); hunits width(); @@ -502,10 +512,12 @@ struct hvpair { class draw_node : public node { int npoints; font_size sz; + color *gcol; + color *fcol; char code; hvpair *point; public: - draw_node(char, hvpair *, int, font_size); + draw_node(char, hvpair *, int, font_size, color *, color *); ~draw_node(); hunits width(); vunits vertical_width(); @@ -582,3 +594,4 @@ public: }; font_family *lookup_family(symbol); +symbol get_font_name(int, environment *); diff --git a/contrib/groff/src/roff/troff/number.cc b/contrib/groff/src/roff/troff/number.cc index 5393842..8fed342 100644 --- a/contrib/groff/src/roff/troff/number.cc +++ b/contrib/groff/src/roff/troff/number.cc @@ -1,5 +1,6 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002 + Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -234,7 +235,7 @@ static int start_number() enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' }; -#define SCALE_INDICATOR_CHARS "icPmnpuvMsz" +#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz" static int parse_term(units *v, int scale_indicator, int parenthesised, int rigid); @@ -320,6 +321,7 @@ static int parse_expr(units *v, int scale_indicator, break; case ':': *v = *v > 0 || v2 > 0; + break; case '+': if (v2 < 0) { if (*v < INT_MIN - v2) @@ -590,6 +592,9 @@ static int parse_term(units *v, int scale_indicator, if (divisor != 1) *v /= divisor; break; + case 'f': + *v = scale(*v, 65536, divisor); + break; case 'p': *v = scale(*v, units_per_inch, divisor*72); break; diff --git a/contrib/groff/src/roff/troff/request.h b/contrib/groff/src/roff/troff/request.h index 5654b83..0433ac1 100644 --- a/contrib/groff/src/roff/troff/request.h +++ b/contrib/groff/src/roff/troff/request.h @@ -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) @@ -48,7 +48,8 @@ class macro : public request_or_macro { macro_header *p; const char *filename; // where was it defined? int lineno; - int length; + int len; + int empty_macro; public: macro(); ~macro(); @@ -59,13 +60,16 @@ public: void append_unsigned(unsigned int i); void append_int(int i); void append_str(const char *); + void set(unsigned char, int); + unsigned char get(int); + int length(); void invoke(symbol); macro *to_macro(); void print_size(); int empty(); friend class string_iterator; friend void chop_macro(); - friend void substring_macro(); + friend void substring_request(); friend int operator==(const macro &, const macro &); }; diff --git a/contrib/groff/src/roff/troff/symbol.cc b/contrib/groff/src/roff/troff/symbol.cc index ce09e39..09f4c98 100644 --- a/contrib/groff/src/roff/troff/symbol.cc +++ b/contrib/groff/src/roff/troff/symbol.cc @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -29,6 +29,7 @@ char *symbol::block = 0; int symbol::block_size = 0; const symbol NULL_SYMBOL; +const symbol EMPTY_SYMBOL(""); #ifdef BLOCK_SIZE #undef BLOCK_SIZE @@ -38,9 +39,9 @@ const int BLOCK_SIZE = 1024; // the table will increase in size as necessary // the size will be chosen from the following array // add some more if you want -// I think it unlikely that we'll need more than a million symbols static const unsigned int table_sizes[] = { -101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009, 80021, 160001, 500009, 1000003, 0 + 101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009, 80021, + 160001, 500009, 1000003, 1500007, 2000003, 0 }; const double FULL_MAX = 0.3; // don't let the table get more than this full @@ -73,10 +74,14 @@ inline void unused(void *) { } symbol::symbol(const char *p, int how) { - if (p == 0 || *p == 0) { + if (p == 0) { s = 0; return; } + if (*p == 0) { + s = ""; + return; + } if (table == 0) { table_size = table_sizes[0]; table = (const char **)new char*[table_size]; @@ -147,4 +152,3 @@ symbol concat(symbol s1, symbol s2) a_delete buf; return res; } - diff --git a/contrib/groff/src/roff/troff/symbol.h b/contrib/groff/src/roff/troff/symbol.h index 88e0fff..5b7c9b1 100644 --- a/contrib/groff/src/roff/troff/symbol.h +++ b/contrib/groff/src/roff/troff/symbol.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1990, 1991, 1992, 2002 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. @@ -36,10 +36,12 @@ public: int operator !=(symbol) const; const char *contents() const; int is_null() const; + int is_empty() const; }; extern const symbol NULL_SYMBOL; +extern const symbol EMPTY_SYMBOL; inline symbol::symbol() : s(0) { @@ -70,4 +72,9 @@ inline int symbol::is_null() const return s == 0; } +inline int symbol::is_empty() const +{ + return s != 0 && *s == 0; +} + symbol concat(symbol, symbol); diff --git a/contrib/groff/src/roff/troff/token.h b/contrib/groff/src/roff/troff/token.h index b87a0b1..59f2aa2 100644 --- a/contrib/groff/src/roff/troff/token.h +++ b/contrib/groff/src/roff/troff/token.h @@ -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) @@ -51,14 +51,16 @@ class token { TOKEN_REQUEST, TOKEN_RIGHT_BRACE, TOKEN_SPACE, // ` ' -- ordinary space - TOKEN_SPECIAL, // a special character -- \' \` \- \(xx + TOKEN_SPECIAL, // a special character -- \' \` \- \(xx \[xxx] TOKEN_SPREAD, // \p -- break and spread output line TOKEN_STRETCHABLE_SPACE, // \~ + TOKEN_UNSTRETCHABLE_SPACE, // `\ ' TOKEN_TAB, // tab TOKEN_TRANSPARENT, // \! TOKEN_TRANSPARENT_DUMMY, // \) + TOKEN_ZERO_WIDTH_BREAK, // \: TOKEN_EOF // end of file - } type; + } type; public: token(); ~token(); @@ -71,6 +73,7 @@ public: int nspaces(); // 1 if space, 2 if double space, 0 otherwise int space(); // is the current token a space? int stretchable_space(); // is the current token a stretchable space? + int unstretchable_space(); // is the current token an unstretchable space? int white_space(); // is the current token space or tab? int special(); // is the current token a special character? int newline(); // is the current token a newline? @@ -85,6 +88,7 @@ public: int right_brace(); int page_ejector(); int hyphen_indicator(); + int zero_width_break(); int operator==(const token &); // need this for delimiters, and for conditions int operator!=(const token &); // ditto unsigned char ch(); @@ -103,6 +107,7 @@ extern token tok; // the current token extern symbol get_name(int required = 0); extern symbol get_long_name(int required = 0); extern charinfo *get_optional_char(); +extern char *read_string(); extern void check_missing_character(); extern void skip_line(); extern void handle_initial_title(); @@ -137,6 +142,11 @@ inline int token::stretchable_space() return type == TOKEN_STRETCHABLE_SPACE; } +inline int token::unstretchable_space() +{ + return type == TOKEN_UNSTRETCHABLE_SPACE; +} + inline int token::special() { return type == TOKEN_SPECIAL; @@ -215,4 +225,9 @@ inline int token::hyphen_indicator() return type == TOKEN_HYPHEN_INDICATOR; } +inline int token::zero_width_break() +{ + return type == TOKEN_ZERO_WIDTH_BREAK; +} + int has_arg(); diff --git a/contrib/groff/src/roff/troff/troff.h b/contrib/groff/src/roff/troff/troff.h index 254c626..80d9f0b 100644 --- a/contrib/groff/src/roff/troff/troff.h +++ b/contrib/groff/src/roff/troff/troff.h @@ -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) @@ -20,16 +20,16 @@ with groff; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <stdio.h> +#include "lib.h" + #include <ctype.h> -#include <string.h> #include <time.h> #include <stddef.h> #include <stdlib.h> #include <errno.h> -#include "lib.h" #include "assert.h" +#include "color.h" #include "device.h" #include "searchpath.h" @@ -43,6 +43,7 @@ extern units units_per_inch; extern int ascii_output_flag; extern int suppress_output_flag; +extern int color_flag; extern int is_html; extern int tcommand_flag; @@ -76,13 +77,18 @@ enum warning_type { WARN_ESCAPE = 0100000, WARN_SPACE = 0200000, WARN_FONT = 0400000, - WARN_IG = 01000000 + WARN_IG = 01000000, + WARN_COLOR = 02000000 // change WARN_TOTAL if you add more warning types }; -const int WARN_TOTAL = 01777777; +const int WARN_TOTAL = 02777777; int warning(warning_type, const char *, const errarg & = empty_errarg, const errarg & = empty_errarg, const errarg & = empty_errarg); +int output_warning(warning_type, const char *, + const errarg & = empty_errarg, + const errarg & = empty_errarg, + const errarg & = empty_errarg); diff --git a/contrib/groff/src/roff/troff/troff.man b/contrib/groff/src/roff/troff/troff.man index ac746dc..19bb624 100644 --- a/contrib/groff/src/roff/troff/troff.man +++ b/contrib/groff/src/roff/troff/troff.man @@ -1,25 +1,44 @@ +'\" t .ig -Copyright (C) 1989-2000, 2001 Free Software Foundation, Inc. +troff.man -Permission is granted to make and distribute verbatim copies of -this manual provided the copyright notice and this permission notice -are preserved on all copies. +Last update : 9 Jan 2002 -Permission is granted to copy and distribute modified versions of this -manual under the conditions for verbatim copying, provided that the -entire resulting derived work is distributed under the terms of a -permission notice identical to this one. +This file is part of groff, the GNU roff type-setting system. -Permission is granted to copy and distribute translations of this -manual into another language, under the above conditions for modified -versions, except that this permission notice may be included in -translations approved by the Free Software Foundation instead of in -the original English. +Copyright (C) 1989, 2000, 2001, 2002 Free Software Foundation, Inc. + +written by James Clark + +modified by Werner Lemberg <wl@gnu.org> + Bernd Warken <bwarken@mayn.de> + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 or +any later version published by the Free Software Foundation; with the +Invariant Sections being this .ig-section and AUTHOR, with no +Front-Cover Texts, and with no Back-Cover Texts. + +A copy of the Free Documentation License is included as a file called +FDL in the main directory of the groff source package. .. . -.\" define a string tx for the TeX logo -.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X -.el .ds tx TeX +. +.\" -------------------------------------------------------------------- +.\" Setup +.\" -------------------------------------------------------------------- +. +.mso www.tmac +. +.if n \{\ +. mso tty-char.tmac +. ftr CR R +. ftr CI I +. ftr CB B +.\} +. +.if '\*[.T]'dvi' \ +. ftr CB CW . .de TQ .br @@ -34,22 +53,19 @@ the original English. .el .TP "\\$1" .. . -.\" The BSD man macros can't handle " in arguments to font change macros, -.\" so use \(ts instead of ". -.tr \(ts" . +.\" -------------------------------------------------------------------- +.\" Title +.\" -------------------------------------------------------------------- . .TH @G@TROFF @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" -. -. .SH NAME +@g@troff \- the troff processor of the groff text formatting system . . -@g@troff \- format documents -. -. +.\" -------------------------------------------------------------------- .SH SYNOPSIS -. +.\" -------------------------------------------------------------------- . .nr a \n(.j .ad l @@ -61,90 +77,75 @@ the original English. .ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" .el .RB "[\ " "\\$1" "\ ]" .. -.OP \-abivzCERU -.OP \-w name -.OP \-W name +.OP \-abcivzCERU .OP \-d cs .OP \-f fam +.OP \-F dir .OP \-m name +.OP \-M dir .OP \-n num .OP \-o list .OP \-r cn .OP \-T name -.OP \-F dir -.OP \-M dir +.OP \-w name +.OP \-W name .RI "[\ " files\|.\|.\|. "\ ]" .br .ad \na -.PP -It is possible to have whitespace between a command line option and its -parameter. +.P +It is possible to have whitespace between a command line option and +its parameter. . . +.\" -------------------------------------------------------------------- .SH DESCRIPTION -. +.\" -------------------------------------------------------------------- . This manual page describes the GNU version of -.BR troff , -which is part of the groff document formatting system. -It is highly compatible with UNIX troff. -Usually it should be invoked using the groff command, which will -also run preprocessors and postprocessors in the appropriate -order and with the appropriate options. +.BR troff . +It is part of the groff document formatting system. . +It is functionally compatible with UNIX troff, but has many extensions, +see +.BR \%groff_diff (@MAN7EXT@). +Usually it should be invoked using the +.BR groff (@MAN1EXT@) +command which will also run preprocessors and postprocessors in the +appropriate order and with the appropriate options. . -.SH OPTIONS . +.\" -------------------------------------------------------------------- +.SH OPTIONS +.\" -------------------------------------------------------------------- . .TP \w'\-dname=s'u+2n .B \-a Generate an .SM ASCII approximation of the typeset output. +. .TP .B \-b -Print a backtrace with each warning or error message. This backtrace -should help track down the cause of the error. The line numbers given -in the backtrace may not always be correct: -.BR troff 's -idea of line numbers -gets confused by +Print a backtrace with each warning or error message. +. +This backtrace should help track down the cause of the error. +. +The line numbers given in the backtrace may not always be correct, for +.BR @g@troff 's +idea of line numbers gets confused by .B as or .B am requests. +. .TP -.B \-i -Read the standard input after all the named input files have been -processed. -.TP -.B \-v -Print the version number. -.TP -.BI \-w name -Enable warning -.IR name . -Available warnings are described in -the Warnings subsection below. -Multiple -.B \-w -options are allowed. -.TP -.BI \-W name -Inhibit warning -.IR name . -Multiple -.B \-W -options are allowed. -.TP -.B \-E -Inhibit all error messages. -.TP -.B \-z -Suppress formatted output. +.B \-c +Disable color output (always disabled in compatibility mode). +. .TP .B \-C Enable compatibility mode. +. .TP .BI \-d cs .TQ @@ -157,11 +158,42 @@ to be a string .IR s ; .I c must be a one letter name. +. +.TP +.B \-E +Inhibit all error messages of +.BR @g@troff . +Note that this doesn't affect messages output to standard error by macro +packages using the +.B tm +or +.B tm1 +requests. +. .TP .BI \-f fam Use .I fam as the default font family. +. +.TP +.BI \-F dir +Search in directory (or directory path) +.I dir +for subdirectories +.BI dev name +.RI ( name +is the name of the device) and there for the +.B DESC +file and font files. +.I dir +is scanned before all other font directories. +. +.TP +.B \-i +Read the standard input after all the named input files have been +processed. +. .TP .BI \-m name Read in the file @@ -169,36 +201,28 @@ Read in the file If it isn't found, try .BI tmac. name instead. +. It will be first searched for in directories given with the .B \-M -command line option, then in directories given -in the +command line option, then in directories given in the .B GROFF_TMAC_PATH environment variable, then in the current directory (only if in unsafe mode), the home directory, @SYSTEMMACRODIR@, @LOCALMACRODIR@, and @MACRODIR@. +. .TP -.B \-U -Unsafe mode. -This will enable the following requests: -.BR .open , -.BR .opena , -.BR .pso , -.BR .sy , -and -.BR .pi . -For security reasons, these potentially dangerous requests are disabled -otherwise. It will also add the current directory to the macro search path. -.TP -.B \-R -Don't load -.B troffrc -and -.BR troffrc-end . +.BI \-M dir +Search directory (or directory path) +.I dir +for macro files. +. +This is scanned before all other macro directories. +. .TP .BI \-n num Number the first page .IR num . +. .TP .BI \-o list Output only pages in @@ -218,8 +242,9 @@ means print every page up to .IB n \- means print every page from .IR n . -.B Troff +.B @g@troff will exit after printing the last page in the list. +. .TP .BI \-r cn .TQ @@ -234,2227 +259,339 @@ to must be a one character name; .I n can be any troff numeric expression. +. +.TP +.B \-R +Don't load +.B troffrc +and +.BR troffrc-end . +. .TP .BI \-T name Prepare output for device .IR name , rather than the default .BR @DEVICE@ . -.TP -.BI \-F dir -Search in directory (or directory path) -.I dir -for subdirectories -.BI dev name -.RI ( name -is the name of the device) and there for the -.B DESC -file and font files. -.I dir -is scanned before all other font directories. -.TP -.BI \-M dir -Search directory (or directory path) -.I dir -for macro files. -This is scanned before all other macro directories. -. -. -.SH USAGE -. -. -Only the features not in UNIX troff are described here. . -.SS Long names -. -The names of number registers, fonts, strings/macros/diversions, -special characters can be of any length. In escape sequences, where -you can use -.BI ( xx -for a two character name, you can use -.BI [ xxx ] -for a name of arbitrary length: -.TP -.BI \e[ xxx ] -Print the special character called -.IR xxx . -.TP -.BI \ef[ xxx ] -Set font -.IR xxx . .TP -.BI \e*[ xxx ] -Interpolate string -.IR xxx . -.TP -.BI \en[ xxx ] -Interpolate number register -.IR xxx . -. -.SS Fractional pointsizes -. -A -.I -scaled point -is equal to 1/sizescale -points, where -sizescale is specified in the -.B DESC -file (1 by default). -There is a new scale indicator -.B z -which has the effect of multiplying by sizescale. -Requests and escape sequences in troff -interpret arguments that represent a pointsize as being in units -of scaled points, but they evaluate each such argument -using a default scale indicator of -.BR z . -Arguments treated in this way are -the argument to the -.B ps -request, -the third argument to the -.B cs -request, -the second and fourth arguments to the -.B tkf -request, -the argument to the -.B \eH -escape sequence, -and those variants of the -.B \es -escape sequence that take a numeric expression as their argument. -.LP -For example, suppose sizescale is 1000; -then a scaled point will be equivalent to a millipoint; -the request -.B .ps 10.25 -is equivalent to -.B .ps 10.25z -and so sets the pointsize to 10250 scaled points, -which is equal to 10.25 points. -.LP -The number register -.B \en[.s] -returns the pointsize in points as decimal fraction. -There is also a new number register -.B \en[.ps] -that returns the pointsize in scaled points. -.LP -It would make no sense to use the -.B z -scale indicator in a numeric expression -whose default scale indicator was neither -.B u -nor -.BR z , -and so -.B troff -disallows this. -Similarly it would make no sense to use a scaling indicator -other than -.B z -or -.B u -in a numeric expression whose default scale indicator was -.BR z , -and so -.B troff -disallows this as well. -.LP -There is also new scale indicator -.B s -which multiplies by the number of units in a scaled point. -So, for example, -.B \en[.ps]s -is equal to -.BR 1m . -Be sure not to confuse the -.B s -and -.B z -scale indicators. -. -.SS Numeric expressions +.B \-U +Unsafe mode. . -.LP -Spaces are permitted in a number expression within parentheses. -.LP -.B M -indicates a scale of 100ths of an em. -.TP -.IB e1 >? e2 -The maximum of -.I e1 -and -.IR e2 . -.TP -.IB e1 <? e2 -The minimum of -.I e1 +This will enable the following requests: +.BR open , +.BR opena , +.BR pso , +.BR sy , and -.IR e2 . -.TP -.BI ( c ; e ) -Evaluate -.I e -using -.I c -as the default scaling indicator. -If -.I c -is missing, ignore scaling indicators in the evaluation of -.IR e . +.BR pi . +For security reasons, these potentially dangerous requests are disabled +otherwise. . -.SS New escape sequences +It will also add the current directory to the macro search path. . .TP -.BI \eA' anything ' -This expands to -.B 1 -or -.B 0 -according as -.I anything -is or is not acceptable as the name of a string, macro, diversion, -number register, environment or font. -It will return -.B 0 -if -.I anything -is empty. -This is useful if you want to lookup user input in some sort of -associative table. -.TP -.BI \eB' anything ' -This expands to -.B 1 -or -.B 0 -according as -.I anything -is or is not a valid numeric expression. -It will return -.B 0 -if -.I anything -is empty. -.TP -.BI \eC' xxx ' -Typeset character named -.IR xxx . -Normally it is more convenient to use -.BI \e[ xxx ]\fR. -But -.B \eC -has the advantage that it is compatible with recent versions of -.SM UNIX -and is available in compatibility mode. -.TP -.B \eE -This is equivalent to an escape character, -but it's not interpreted in copy-mode. -For example, strings to start and end superscripting could be defined -like this: -.RS -.IP -\&.ds { \ev'\-.3m'\es'\eEn[.s]*6u/10u' -.br -\&.ds } \es0\ev'.3m' -.LP -The use of -.B \eE -ensures that these definitions will work even if -.B \e*{ -gets interpreted in copy-mode -(for example, by being used in a macro argument). -.RE -.TP -.BI \eN' n ' -Typeset the character with code -.I n -in the current font. -.I n -can be any integer. -Most devices only have characters with codes between 0 and 255. -If the current font does not contain a character with that code, -special fonts will -.I not -be searched. -The -.B \eN -escape sequence can be conveniently used on conjunction with the -.B char -request: -.RS -.IP -.B -\&.char \e[phone] \ef(ZD\eN'37' -.RE -.IP -The code of each character is given in the fourth column in the font -description file after the -.B charset -command. -It is possible to include unnamed characters in the font description -file by using a name of -.BR \-\-\- ; -the -.B \eN -escape sequence is the only way to use these. -.TP -.BI \eR' name\ \(+-n ' -This has the same effect as -.RS -.IP -.BI .nr\ name\ \(+-n -.RE -.TP -.BI \es( nn -.TQ -.BI \es\(+-( nn -Set the point size to -.I nn -points; -.I nn -must be exactly two digits. -.TP -.BI \es[\(+- n ] -.TQ -.BI \es\(+-[ n ] -.TQ -.BI \es'\(+- n ' -.TQ -.BI \es\(+-' n ' -Set the point size to -.I n -scaled points; -.I n -is a numeric expression with a default scale indicator of -.BR z . -.TP -.BI \eV x -.TQ -.BI \eV( xx -.TQ -.BI \eV[ xxx ] -Interpolate the contents of the environment variable -.IR xxx , -as returned by -.BR getenv (3). -.B \eV -is interpreted in copy-mode. -.TP -.BI \eY x -.TQ -.BI \eY( xx -.TQ -.BI \eY[ xxx ] -This is approximately equivalent to -.BI \eX'\e*[ xxx ]'\fR. -However the contents of the string or macro -.I xxx -are not interpreted; -also it is permitted for -.I xxx -to have been defined as a macro and thus contain newlines -(it is not permitted for the argument to -.B \eX -to contain newlines). -The inclusion of newlines requires an extension to the UNIX troff output -format, and will confuse drivers that do not know about this -extension. -.TP -.BI \eZ' anything ' -Print anything and then restore the horizontal and vertical -position; -.I anything -may not contain tabs or leaders. -.TP -.B \e$0 -The name by which the current macro was invoked. -The -.B als -request can make a macro have more than one name. -.TP -.B \e$* -In a macro, the concatenation of all the arguments separated by spaces. -.TP -.B \e$@ -In a macro, the concatenation of all the arguments with each surrounded by -double quotes, and separated by spaces. -.TP -.BI \e$( nn -.TQ -.BI \e$[ nnn ] -In a macro, this gives the -.IR nn -th -or -.IR nnn -th -argument. -Macros can have an unlimited number of arguments. -.TP -.BI \e? anything \e? -When used in a diversion, this will transparently embed -.I anything -in the diversion. -.I anything -is read in copy mode. -When the diversion is reread, -.I anything -will be interpreted. -.I anything -may not contain newlines; use -.B \e!\& -if you want to embed newlines in a diversion. -The escape sequence -.B \e?\& -is also recognised in copy mode and turned into a single internal -code; it is this code that terminates -.IR anything . -Thus -.RS -.RS -.ft B -.nf -.ne 15 -\&.nr x 1 -\&.nf -\&.di d -\e?\e\e?\e\e\e\e?\e\e\e\e\e\e\e\enx\e\e\e\e?\e\e?\e? -\&.di -\&.nr x 2 -\&.di e -\&.d -\&.di -\&.nr x 3 -\&.di f -\&.e -\&.di -\&.nr x 4 -\&.f -.fi -.ft -.RE -.RE -.IP -will print -.BR 4 . -.TP -.B \e/ -This increases the width of the preceding character so that -the spacing between that character and the following character -will be correct if the following character is a roman character. -For example, if an italic f is immediately followed by a roman -right parenthesis, then in many fonts the top right portion of the f -will overlap the top left of the right parenthesis producing \fIf\fR)\fR, -which is ugly. -Inserting -.B \e/ -produces -.ie \n(.g \fIf\/\fR)\fR -.el \fIf\|\fR)\fR -and avoids this problem. -It is a good idea to use this escape sequence whenever an -italic character is immediately followed by a roman character without any -intervening space. -.TP -.B \e, -This modifies the spacing of the following character so that the spacing -between that character and the preceding character will correct if -the preceding character is a roman character. -For example, inserting -.B \e, -between the parenthesis and the f changes -\fR(\fIf\fR to -.ie \n(.g \fR(\,\fIf\fR. -.el \fR(\^\fIf\fR. -It is a good idea to use this escape sequence whenever a -roman character is immediately followed by an italic character without any -intervening space. -.TP -.B \e) -Like -.B \e& -except that it behaves like a character declared with the -.B cflags -request to be transparent for the purposes of end of sentence recognition. -.TP -.B \e~ -This produces an unbreakable space that stretches like a normal inter-word -space when a line is adjusted. -.TP -.B \e: -This causes the insertion of a zero-width break point. -It is equal to -.B \e% -but without insertion of a soft hyphen character. -.TP -.B \e# -Everything up to and including the next newline is ignored. -This is interpreted in copy mode. -This is like -.B \e" -except that -.B \e" -does not ignore the terminating newline. -. -.SS New requests +.B \-v +Print the version number. . .TP -.BI .aln\ xx\ yy -Create an alias -.I xx -for number register object named -.IR yy . -The new name and the old name will be exactly equivalent. -If -.I yy -is undefined, a warning of type -.B reg -will be generated, and the request will be ignored. -.TP -.BI .als\ xx\ yy -Create an alias -.I xx -for request, string, macro, or diversion object named -.IR yy . -The new name and the old name will be exactly equivalent (it is similar to a -hard rather than a soft link). -If -.I yy -is undefined, a warning of type -.B mac -will be generated, and the request will be ignored. -The -.BR de , -.BR am , -.BR di , -.BR da , -.BR ds , -and -.B as -requests only create a new object if the name of the macro, diversion -or string diversion is currently undefined or if it is defined to be a -request; normally they modify the value of an existing object. -.TP -.BI .am1\ xx\ yy -Similar to -.BR .am , -but compatibility mode is switched off during execution. -On entry, the current compatibility mode is saved and restored at exit. -.TP -.BI .asciify\ xx -This request `unformats' the diversion -.I xx -in such a way that -.SM ASCII -and space characters (and some escape sequences) that were formatted and -diverted into -.I xx -will be treated like ordinary input characters when -.I xx -is reread. -Useful for diversions in conjunction with the -.B .writem -request. -It can be also used for gross hacks; for example, this -.RS -.IP -.ne 7v+\n(.Vu -.ft B -.nf -\&.tr @. -\&.di x -\&@nr n 1 -\&.br -\&.di -\&.tr @@ -\&.asciify x -\&.x -.fi -.RE -.IP -will set register -.B n -to 1. -Note that glyph information (font, font size, etc.) is not preserved; use -.B .unformat -instead. -.TP -.B .backtrace -Print a backtrace of the input stack on stderr. -.TP -.BI .blm\ xx -Set the blank line macro to -.IR xx . -If there is a blank line macro, -it will be invoked when a blank line is encountered instead of the usual -troff behaviour. -.TP -.BI .box\ xx -.TQ -.BI .boxa\ xx -These requests are similar to the -.B di -and -.B da -requests with the exception that a partially filled line will not become -part of the diversion (i.e., the diversion always starts with a new line) -but restored after ending the diversion, discarding the partially filled -line which possibly comes from the diversion. -.TP -.B .break -Break out of a while loop. -See also the -.B while -and -.B continue -requests. -Be sure not to confuse this with the -.B br -request. -.TP -.B .brp -This is the same as -.BR \ep . -.TP -.BI .cflags\ n\ c1\ c2\|.\|.\|. -Characters -.IR c1 , -.IR c2 ,\|.\|.\|. -have properties determined by -.IR n , -which is ORed from the following: -.RS -.TP -1 -the character ends sentences -(initially characters -.B .?!\& -have this property); -.TP -2 -lines can be broken before the character -(initially no characters have this property); -a line will not be broken at a character with this property -unless the characters on each side both have non-zero -hyphenation codes. -.TP -4 -lines can be broken after the character -(initially characters -.B \-\e(hy\e(em -have this property); -a line will not be broken at a character with this property -unless the characters on each side both have non-zero -hyphenation codes. -.TP -8 -the character overlaps horizontally -(initially characters -.B \e(ul\e(rn\e(ru -have this property); -.TP -16 -the character overlaps vertically -(initially character -.B \e(br -has this property); -.TP -32 -an end of sentence character followed by any number of characters -with this property will be treated -as the end of a sentence if followed by a newline or two spaces; -in other words -the character is transparent for the purposes of end of sentence -recognition; -this is the same as having a zero space factor in \*(tx -(initially characters -.B \(ts')]*\e(dg\e(rq -have this property). -.RE -.TP -.BI .char\ c\ string -Define character -.I c -to be -.IR string . -Every time character -.I c -needs to be printed, -.I string -will be processed in a temporary environment and the result -will be wrapped up into a single object. -Compatibility mode will be turned off -and the escape character will be set to -.B \e -while -.I string -is being processed. -Any emboldening, constant spacing or track kerning will be applied -to this object rather than to individual characters in -.IR string . -A character defined by this request can be used just like -a normal character provided by the output device. -In particular other characters can be translated to it -with the -.B tr -request; -it can be made the leader character by the -.B lc -request; -repeated patterns can be drawn with the character using the -.B \el -and -.B \eL -escape sequences; -words containing the character can be hyphenated -correctly, if the -.B hcode -request is used to give the character a hyphenation code. -There is a special anti-recursion feature: -use of character within the character's definition -will be handled like normal characters not defined with -.BR char . -A character definition can be removed with the -.B rchar -request. -.TP -.BI .chop\ xx -Chop the last character off macro, string, or diversion -.IR xx . -This is useful for removing the newline from the end of diversions -that are to be interpolated as strings. -.TP -.BI .close\ stream -Close the stream named -.IR stream ; -.I stream -will no longer be an acceptable argument to the -.B write -request. -See the -.B open -request. -.TP -.B .continue -Finish the current iteration of a while loop. -See also the -.B while -and -.B break -requests. -.TP -.BI .cp\ n -If -.I n -is non-zero or missing, enable compatibility mode, otherwise -disable it. -In compatibility mode, long names are not recognised, and the -incompatibilities caused by long names do not arise. -.TP -.BI .dei\ xx\ yy -Define macro indirectly. -The following example -.RS -.IP -.ne 2v+\n(.Vu -.ft B -.nf -\&.ds xx aa -\&.ds yy bb -\&.dei xx yy -.fi -.RE -.IP -is equivalent to -.RS -.IP -.B -\&.de aa bb -.RE -.TP -.BI .de1\ xx\ yy -Similar to -.BR .de , -but compatibility mode is switched off during execution. -On entry, the current compatibility mode is saved and restored at exit. -.TP -.BI .do\ xxx -Interpret -.I .xxx -with compatibility mode disabled. -For example, -.RS -.IP -.B -\&.do fam T -.LP -would have the same effect as -.IP -.B -\&.fam T -.LP -except that it would work even if compatibility mode had been enabled. -Note that the previous compatibility mode is restored before any files -sourced by -.I xxx -are interpreted. -.RE -.TP -.B .ecs -Save current escape character. -.TP -.B .ecr -Restore escape character saved with -.BR ecs . -Without a previous call to -.BR ecs , -.RB ` \e ' -will be the new escape character. -.TP -.BI .evc\ xx -Copy the contents of environment -.I xx -to the current environment. -No pushing or popping of environents will be done. -.TP -.BI .fam\ xx -Set the current font family to -.IR xx . -The current font family is part of the current environment. -If -.I xx -is missing, switch back to previous font family. -See the description of the -.B sty -request for more information on font families. -.TP -.BI .fspecial\ f\ s1\ s2\|.\|.\|. -When the current font is -.IR f , -fonts -.IR s1 , -.IR s2 ,\|.\|.\|. -will be special, that is, they will searched for characters not in -the current font. -Any fonts specified in the -.B special -request will be searched after fonts specified in the -.B fspecial -request. -.TP -.BI .ftr\ f\ g -Translate font -.I f -to -.IR g . -Whenever a font named -.I f -is referred to in -.B \ef -escape sequence, -or in the -.BR ft , -.BR ul , -.BR bd , -.BR cs , -.BR tkf , -.BR special , -.BR fspecial , -.BR fp , -or -.BR sty -requests, -font -.I g -will be used. -If -.I g -is missing, -or equal to -.I f -then font -.I f -will not be translated. -.TP -.BI .hcode \ c1\ code1\ c2\ code2\|.\|.\|. -Set the hyphenation code of character -.I c1 -to -.I code1 -and that of -.I c2 -to -.IR code2 . -A hyphenation code must be a single input -character (not a special character) other than a digit or a space. -Initially each lower-case letter has a hyphenation code, which -is itself, and each upper-case letter has a hyphenation code -which is the lower case version of itself. -See also the -.B hpf -request. -.TP -.BI .hla\ lang -Set the current hyphenation language to -.IR lang . -Hyphenation exceptions specified with the -.B hw -request and hyphenation patterns specified with the -.B hpf -request are both associated with the current hyphenation language. -The -.B hla -request is usually invoked by the -.B troffrc -file. -.TP -.BI .hlm\ n -Set the maximum number of consecutive hyphenated lines to -.IR n . -If -.I n -is negative, there is no maximum. -The default value is \-1. -This value is associated with the current environment. -Only lines output from an environment count towards the maximum associated -with that environment. -Hyphens resulting from -.B \e% -are counted; explicit hyphens are not. -.TP -.BI .hpf\ file -Read hyphenation patterns from -.IR file ; -this will be searched for in the same way that -.IB name .tmac -is searched for when the -.BI \-m name -option is specified. -It should have the same format as the argument to -the \epatterns primitive in \*(tx; -the letters appearing in this file are interpreted as hyphenation -codes. -A -.B % -character in the patterns file introduces a comment that continues -to the end of the line. -The set of hyphenation patterns is associated with the current language -set by the -.B hla -request. -The -.B hpf -request -is usually invoked by the -.B troffrc -file. -.TP -.BI .hym\ n -Set the -.I hyphenation margin -to -.IR n : -when the current adjustment mode is not -.BR b , -the line will not be hyphenated if the line is no more than -.I n -short. -The default hyphenation margin is 0. -The default scaling indicator for this request is -.IR m . -The hyphenation margin is associated with the current environment. -The current hyphenation margin is available in the -.B \en[.hym] -register. -.TP -.BI .hys\ n -Set the -.I hyphenation space -to -.IR n : -when the current adjustment mode is -.B b -don't hyphenate the line if the line can be justified by adding no more than -.I n -extra space to each word space. -The default hyphenation space is 0. -The default scaling indicator for this request is -.BR m . -The hyphenation space is associated with the current environment. -The current hyphenation space is available in the -.B \en[.hys] -register. -.TP -.BI .kern\ n -If -.I n -is non-zero or missing, enable pairwise kerning, otherwise disable it. -.TP -.BI .length\ xx\ string -Compute the length of -.I string -and return it in the number register -.I xx -(which is not necessarily defined before). -.TP -.BI .linetabs\ n -If -.I n -is non-zero or missing, enable line-tabs mode, otherwise disable it (which -is the default). -In line-tabs mode, tab distances are computed relative to the (current) -output line. -Otherwise they are taken relative to the input line. -For example, the following -.RS -.IP -.ne 6v+\n(.Vu -.ft B -.nf -\&.ds x a\et\ec -\&.ds y b\et\ec -\&.ds z c -\&.ta 1i 3i -\e*x -\e*y -\e*z -.fi -.RE -.IP -yields -.RS -.IP -a b c -.RE -.IP -In line-tabs mode, the same code gives -.RS -.IP -a b c -.RE -.IP -Line-tabs mode is associated with the current environment; the read-only -number register -.B \\en[.linetabs] -is set to\~1 if in line-tabs mode, and 0 otherwise. -.TP -.BI .mso\ file -The same as the -.B so -request except that -.I file -is searched for in the same directories as macro files for the -the -.B \-m -command line option. -If the file name to be included -has the form -.IB name .tmac -and it isn't found, -.B mso -tries to include -.BI tmac. name -instead and vice versa. -.TP -.BI .nop \ anything -Execute -.IR anything . -This is similar to `.if\ 1'. -.TP -.B .nroff -Make the -.B n -built-in condition true -and the -.B t -built-in condition false. -This can be reversed using the -.B troff -request. -.TP -.BI .open\ stream\ filename -Open -.I filename -for writing and associate the stream named -.I stream -with it. -See also the -.B close -and -.B write -requests. -.TP -.BI .opena\ stream\ filename -Like -.BR open , -but if -.I filename -exists, append to it instead of truncating it. -.TP -.B .pnr -Print the names and contents of all currently defined number registers -on stderr. -.TP -.BI .psbb \ filename -Get the bounding box of a PostScript image -.IR filename . -This file must conform to Adobe's Document Structuring Conventions; the -command looks for a -.B %%BoundingBox -comment to extract the bounding box values. -After a successful call, the coordinates (in PostScript units) of the lower -left and upper right corner can be found in the registers -.BR \en[llx] , -.BR \en[lly] , -.BR \en[urx] , -and -.BR \en[ury] , -respectively. -If some error has occurred, the four registers are set to zero. -.TP -.BI .pso \ command -This behaves like the -.B so -request except that input comes from the standard output of -.IR command . -.TP -.B .ptr -Print the names and positions of all traps (not including input line -traps and diversion traps) on stderr. Empty slots in the page trap -list are printed as well, because they can affect the priority of -subsequently planted traps. -.TP -.BI .rchar\ c1\ c2\|.\|.\|. -Remove the definitions of characters -.IR c1 , -.IR c2 ,\|.\|.\|. -This undoes the effect of a -.B char -request. -.TP -.B .return -Within a macro, return immediately. -No effect otherwise. -.TP -.B .rj -.TQ -.BI .rj\ n -Right justify the next -.I n -input lines. -Without an argument right justify the next input line. -The number of lines to be right justified is available in the -.B \en[.rj] -register. -This implicitly does -.BR .ce\ 0 . -The -.B ce -request implicitly does -.BR .rj\ 0 . -.TP -.BI .rnn \ xx\ yy -Rename number register -.I xx -to -.IR yy . -.TP -.BI .shc\ c -Set the soft hyphen character to -.IR c . -If -.I c -is omitted, -the soft hyphen character will be set to the default -.BR \e(hy . -The soft hyphen character is the character which will be inserted -when a word is hyphenated at a line break. -If the soft hyphen character does not exist in the font of the character -immediately preceding a potential break point, -then the line will not be broken at that point. -Neither definitions (specified with the -.B char -request) -nor translations (specified with the -.B tr -request) -are considered when finding the soft hyphen character. -.TP -.BI .shift\ n -In a macro, shift the arguments by -.I n -positions: -argument -.I i -becomes argument -.IR i \- n ; -arguments 1 to -.I n -will no longer be available. -If -.I n -is missing, -arguments will be shifted by 1. -Shifting by negative amounts is currently undefined. -.TP -.BI .special\ s1\ s2\|.\|.\|. -Fonts -.IR s1 , -.IR s2 , -are special and will be searched for characters not in the -current font. -.TP -.BI .sty\ n\ f -Associate style -.I f -with font position -.IR n . -A font position can be associated either with a font or -with a style. -The current font is the index of a font position and so is also -either a font or a style. -When it is a style, the font that is actually used is the font the -name of which is the concatenation of the name of the current family -and the name of the current style. -For example, if the current font is 1 and font position 1 is -associated with style -.B R -and the current -font family is -.BR T , -then font -.BR TR -will be used. -If the current font is not a style, then the current family is ignored. -When the requests -.BR cs , -.BR bd , -.BR tkf , -.BR uf , -or -.B fspecial -are applied to a style, -then they will instead be applied to the member of the -current family corresponding to that style. -The default family can be set with the -.B \-f -option. -The styles command in the -.SM DESC -file controls which font positions -(if any) are initially associated with styles rather than fonts. -.TP -.BI .substring\ xx\ n1\ [ n2 ] -Replace the string in register -.I xx -with the substring defined by the indices -.I n1 -and -.IR n2 . -The first character in the string has index one. -If -.I n2 -is omitted, it is taken to be equal to the string's length. If the -index value -.I n1 -or -.I n2 -is negative or zero, it will be counted from the end of the string, -going backwards: The last character has index 0, the character before -the last character has index -1, etc. -.TP -.BI .tkf\ f\ s1\ n1\ s2\ n2 -Enable track kerning for font -.IR f . -When the current font is -.I f -the width of every character will be increased by an amount -between -.I n1 -and -.IR n2 ; -when the current point size is less than or equal to -.I s1 -the width will be increased by -.IR n1 ; -when it is greater than or equal to -.I s2 -the width will be increased by -.IR n2 ; -when the point size is greater than or equal to -.I s1 -and less than or equal to -.I s2 -the increase in width is a linear function of the point size. -.TP -.BI .tm1\ string -Similar to the -.B tm -request, -.I string -is read in copy mode and written on the standard error, but an initial -double quote in -.I string -is stripped off to allow initial blanks. -.TP -.BI .tmc\ string -Similar to -.BR tm1 -but without writing a final newline. -.TP -.BI .trf\ filename -Transparently output the contents of file -.IR filename . -Each line is output as it would be were it preceded by -.BR \e! ; -however, the lines are not subject to copy-mode interpretation. -If the file does not end with a newline, then a newline will -be added. -For example, you can define a macro -.I x -containing the contents of file -.IR f , -using -.RS -.IP -.BI .di\ x -.br -.BI .trf\ f -.br -.B .di -.LP -Unlike with the -.B cf -request, -the file cannot contain characters such as -.SM NUL -that are not legal troff input characters. -.RE -.TP -.B .trnt abcd -This is the same as the -.B tr -request except that the translations do not apply to text that is -transparently throughput into a diversion with -.BR \e! . -For example, -.RS -.IP -.nf -.ft B -\&.tr ab -\&.di x -\e!.tm a -\&.di -\&.x -.fi -.ft -.LP -will print -.BR b ; -if -.B trnt -is used instead of -.B tr -it will print -.BR a . -.RE -.TP -.B .troff -Make the -.B n -built-in condition false, -and the -.B t -built-in condition true. -This undoes the effect of the -.B nroff -request. -.TP -.BI .unformat\ xx -This request `unformats' the diversion -.IR xx . -Contrary to the -.B .asciify -request, which tries to convert formatted elements of the diversion back -to input tokens as much as possible, -.B .unformat -will only handle tabs and spaces between words (usually caused by spaces -or newlines in the input) specially. -The former are treated as if they were input tokens, and the latter are -stretchable again. -Note that the vertical size of lines is not preserved. -Glyph information (font, font size, space width, etc.) is retained. -Useful in conjunction with the -.B .box -and -.B .boxa -requests. -.TP -.BI .vpt\ n -Enable vertical position traps if -.I n -is non-zero, disable them otherwise. -Vertical position traps are traps set by the -.B wh -or -.B dt -requests. -Traps set by the -.B it -request are not vertical position traps. -The parameter that controls whether vertical position traps are enabled -is global. -Initially vertical position traps are enabled. -.TP -.BI .warn\ n -Control warnings. -.I n -is the sum of the numbers associated with each warning that is to be enabled; -all other warnings will be disabled. -The number associated with each warning is listed in the `Warnings' section. -For example, -.B .warn 0 -will disable all warnings, and -.B .warn 1 -will disable all warnings except that about missing characters. -If -.I n -is not given, -all warnings will be enabled. -.TP -.BI .while \ c\ anything -While condition -.I c -is true, accept -.I anything -as input; -.I c -can be any condition acceptable to an -.B if -request; -.I anything -can comprise multiple lines if the first line starts with -.B \e{ -and the last line ends with -.BR \e} . -See also the -.B break -and -.B continue -requests. -.TP -.BI .write\ stream\ anything -Write -.I anything -to the stream named -.IR stream . -.I stream -must previously have been the subject of an -.B open -request. -.I anything -is read in copy mode; -a leading -.B \(ts -will be stripped. -.TP -.BI .writem\ stream\ xx -Write the contents of the macro or string -.I xx -to the stream named -.IR stream . -.I stream -must previously have been the subject of an -.B open -request. -.I xx -is read in copy mode. +.BI \-w name +Enable warning +.IR name . +Available warnings are described in the section +.I WARNINGS +below. . -.SS Extended requests +For example, to enable all warnings, use +.B \-w +.BR all . +Multiple +.B \-w +options are allowed. . .TP -.BI .cf\ filename -When used in a diversion, this will embed in the diversion an object which, -when reread, will cause the contents of -.I filename -to be transparently copied through to the output. -In UNIX troff, the -contents of -.I filename -is immediately copied through to the output regardless of whether -there is a current diversion; this behaviour is so anomalous that it -must be considered a bug. -.TP -.BI .ev\ xx -If -.I xx -is not a number, this will switch to a named environment called -.IR xx . -The environment should be popped with a matching -.B ev -request without any arguments, just as for numbered environments. -There is no limit on the number of named environments; they will be -created the first time that they are referenced. -.TP -.BI .fp\ n\ f1\ f2 -The -.B fp -request has an optional third argument. -This argument gives the external name of the font, -which is used for finding the font description file. -The second argument gives the internal name of the font -which is used to refer to the font in troff after it has been mounted. -If there is no third argument then the internal name will be used -as the external name. -This feature allows you to use fonts with long names in compatibility mode. -.TP -.BI .ss\ m\ n -When two arguments are given to the -.B ss -request, the second argument gives the -.IR "sentence space size" . -If the second argument is not given, the sentence space size -will be the same as the word space size. -Like the word space size, the sentence space is in units of -one twelfth of the spacewidth parameter for the current font. -Initially both the word space size and the sentence -space size are 12. -Contrary to UNIX troff, GNU troff handles this request in nroff mode -also; a given value is then rounded down to the nearest multiple of\~12. -The sentence space size is used in two circumstances: -if the end of a sentence occurs at the end of a line in fill mode, then -both an inter-word space and a sentence space will be added; -if two spaces follow the end of a sentence in the middle of a line, -then the second space will be a sentence space. -Note that the behaviour of UNIX troff will be exactly -that exhibited by GNU troff if a second argument is never given to the -.B ss -request. -In GNU troff, as in UNIX troff, you should always -follow a sentence with either a newline or two spaces. -.TP -.BI .ta\ n1\ n2\|.\|.\|.nn \ T\ r1\ r2\|.\|.\|.\|rn -Set tabs at positions -.IR n1 , -.IR n2 ,\|.\|.\|.\|, -.I nn -and then set tabs at -.IR nn + r1 , -.IR nn + r2 ,\|.\|.\|.\|.\|, -.IR nn + rn -and then at -.IR nn + rn + r1 , -.IR nn + rn + r2 ,\|.\|.\|.\|, -.IR nn + rn + rn , -and so on. -For example, -.RS -.IP -.B -\&.ta T .5i -.LP -will set tabs every half an inch. -.RE -. -.SS New number registers +.BI \-W name +Inhibit warning +.IR name . +Multiple +.B \-W +options are allowed. . -The following read-only registers are available: -.TP -.B \en[.C] -1 if compatibility mode is in effect, 0 otherwise. -.TP -.B \en[.cdp] -The depth of the last character added to the current environment. -It is positive if the character extends below the baseline. -.TP -.B \en[.ce] -The number of lines remaining to be centered, as set by the -.B ce -request. -.TP -.B \en[.cht] -The height of the last character added to the current environment. -It is positive if the character extends above the baseline. -.TP -.B \en[.csk] -The skew of the last character added to the current environment. -The -.I skew -of a character is how far to the right of the center of a character -the center of an accent over that character should be placed. -.TP -.B \en[.ev] -The name or number of the current environment. -This is a string-valued register. -.TP -.B \en[.fam] -The current font family. -This is a string-valued register. -.TP -.B \en[.fp] -The number of the next free font position. -.TP -.B \en[.g] -Always 1. -Macros should use this to determine whether they are running -under GNU troff. -.TP -.B \en[.hla] -The current hyphenation language as set by the -.B hla -request. -.TP -.B \en[.hlc] -The number of immediately preceding consecutive hyphenated lines. -.TP -.B \en[.hlm] -The maximum allowed number of consecutive hyphenated lines, as set by the -.B hlm -request. -.TP -.B \en[.hy] -The current hyphenation flags (as set by the -.B hy -request). -.TP -.B \en[.hym] -The current hyphenation margin (as set by the -.B hym -request). -.TP -.B \en[.hys] -The current hyphenation space (as set by the -.B hys -request). -.TP -.B \en[.in] -The indent that applies to the current output line. -.TP -.B \en[.int] -Set to a positive value if last output line is interrupted (i.e., if it -contains -.IR \ec ). -.TP -.B \en[.kern] -.B 1 -if pairwise kerning is enabled, -.B 0 -otherwise. -.TP -.B \en[.lg] -The current ligature mode (as set by the -.B lg -request). -.TP -.B \en[.linetabs] -The current line-tabs mode (as set by the -.B linetabs -request). -.TP -.B \en[.ll] -The line length that applies to the current output line. -.TP -.B \en[.lt] -The title length as set by the -.B lt -request. -.TP -.B \en[.ne] -The amount of space that was needed in the last -.B ne -request that caused a trap to be sprung. -Useful in conjunction with the -.B \en[.trunc] -register. -.TP -.B \en[.ns] -.B 1 -if no-space mode is active, -.B 0 -otherwise. -.TP -.B \en[.pn] -The number of the next page: -either the value set by a -.B pn -request, or the number of the current page plus 1. -.TP -.B \en[.ps] -The current pointsize in scaled points. -.TP -.B \en[.psr] -The last-requested pointsize in scaled points. -.TP -.B \en[.rj] -The number of lines to be right-justified as set by the -.B rj -request. -.TP -.B \en[.sr] -The last requested pointsize in points as a decimal fraction. -This is a string-valued register. -.TP -.B \en[.tabs] -A string representation of the current tab settings suitable for use as -an argument to the -.B ta -request. -.TP -.B \en[.trunc] -The amount of vertical space truncated by the most recently sprung -vertical position trap, or, -if the trap was sprung by a -.B ne -request, -minus the amount of vertical motion produced by the -.B ne -request. -In other words, at the point a trap is sprung, it represents the difference -of what the vertical position would have been but for the trap, -and what the vertical position actually is. -Useful in conjunction with the -.B \en[.ne] -register. -.TP -.B \en[.ss] -.TQ -.B \en[.sss] -These give the values of the parameters set by the -first and second arguments of the -.B ss -request. -.TP -.B \en[.vpt] -1 if vertical position traps are enabled, 0 otherwise. -.TP -.B \en[.warn] -The sum of the numbers associated with each of the currently enabled -warnings. -The number associated with each warning is listed in the `Warnings' -subsection. -.TP -.B \en[.x] -The major version number. -For example, if the version number is -.B 1.03 -then -.B \en[.x] -will contain -.BR 1 . -.TP -.B \en[.y] -The minor version number. -For example, if the version number is -.B 1.03 -then -.B \en[.y] -will contain -.BR 03 . -.TP -.B \en[.Y] -The revision number of groff. -.TP -.B \en[llx] -.TQ -.B \en[lly] -.TQ -.B \en[urx] -.TQ -.B \en[ury] -These four registers are set by the -.B \&.psbb -request and contain the bounding box values (in PostScript units) of a given -PostScript image. -.LP -The following read/write registers are set by the -.B \ew -escape sequence: -.TP -.B \en[rst] -.TQ -.B \en[rsb] -Like the -.B st -and -.B sb -registers, but takes account of the heights and depths of characters. -.TP -.B \en[ssc] -The amount of horizontal space (possibly negative) that should -be added to the last character before a subscript. -.TP -.B \en[skw] -How far to right of the center of the last character -in the -.B \ew -argument, -the center of an accent from a roman font should be placed over that character. -.LP -Other available read/write number registers are: -.TP -.B \en[c.] -The current input line number. -.B \en[.c] -is a read-only alias to this register. .TP -.B \en[hp] -The current horizontal position at input line. -.TP -.B \en[systat] -The return value of the system() function executed by the last -.B sy -request. -.TP -.B \en[slimit] -If greater than 0, the maximum number of objects on the input stack. -If less than or equal to 0, there is no limit on the number of objects -on the input stack. With no limit, recursion can continue until -virtual memory is exhausted. -.TP -.B \en[year] -The current year. -Note that the traditional -.B troff -number register -.B \en[yr] -is the current year minus 1900. -. -.SS Miscellaneous +.B \-z +Suppress formatted output. . -.B @g@troff -predefines a single (read/write) string-based register, -.BR \e*(.T , -which contains the argument given to the -.B -T -command line option, namely the current output device (for example, -.I latin1 -or -.IR ascii ). -Note that this is not the same as the (read-only) number register -.B \en[.T] -which is defined to be\ 1 if -.B troff -is called with the -.B -T -command line option, and zero otherwise. This behaviour is different to -UNIX troff. -.LP -Fonts not listed in the -.SM DESC -file are automatically mounted on the next available font position -when they are referenced. -If a font is to be mounted explicitly with the -.B fp -request on an unused font position, -it should be mounted on the first unused font position, -which can be found in the -.B \en[.fp] -register; -although -.B troff -does not enforce this strictly, -it will not allow a font to be mounted at a position whose number is much -greater than that of any currently used position. -.LP -Interpolating a string does not hide existing macro arguments. -Thus in a macro, a more efficient way of doing -.IP -.BI . xx\ \e\e$@ -.LP -is -.IP -.BI \e\e*[ xx ]\e\e -.LP -If the font description file contains pairwise kerning information, -characters from that font will be kerned. -Kerning between two characters can be inhibited by placing a -.B \e& -between them. -.LP -In a string comparison in a condition, -characters that appear at different input levels -to the first delimiter character will not be recognised -as the second or third delimiters. -This applies also to the -.B tl -request. -In a -.B \ew -escape sequence, -a character that appears at a different input level to -the starting delimiter character will not be recognised -as the closing delimiter character. -When decoding a macro argument that is delimited -by double quotes, a character that appears at a different -input level to the starting delimiter character will not -be recognised as the closing delimiter character. -The implementation of -.B \e$@ -ensures that the double quotes surrounding an argument -will appear the same input level, which will be different -to the input level of the argument itself. -In a long escape name -.B ] -will not be recognized as a closing delimiter except -when it occurs at the same input level as the opening -.BR ] . -In compatibility mode, no attention is paid to the input-level. -.LP -There are some new types of condition: -.TP -.BI .if\ r xxx -True if there is a number register named -.IR xxx . -.TP -.BI .if\ d xxx -True if there is a string, macro, diversion, or request named -.IR xxx . -.TP -.BI .if\ c ch -True if there is a character -.IR ch -available; -.I ch -is either an -.SM ASCII -character -or a special character -.BI \e( xx -or -.BI \e[ xxx ]\fR; -the condition will also be true if -.I ch -has been defined by the -.B char -request. -.LP -The -.B tr -request can now map characters onto -.BR \e~ . . -.SS Warnings +.\" -------------------------------------------------------------------- +.SH WARNINGS +.\" -------------------------------------------------------------------- . The warnings that can be given by -.B troff +.B @g@troff are divided into the following categories. +. The name associated with each warning is used by the .B \-w and .B \-W -options; -the number is used by the +options; the number is used by the .B warn request, and by the .B .warn -register. -.nr x \w'\fBright-brace'+1n+\w'0000'u +register; it is always a power of 2 to allow bitwise composition. +. +.P +.TS +tab(@), center, box; +c c c | c c c +r rI lB | r rI lB. +Bit@Code@Warning@Bit@Code@Warning +_ +0@1@char@10@1024@reg +1@2@number@11@2048@tab +2@4@break@12@4096@right-brace +3@8@delim@13@8192@missing +4@16@el@14@16384@input +5@32@scale@15@32768@escape +6@64@range@16@65536@space +7@128@syntax@17@131072@font +8@256@di@18@262144@ig +9@512@mac@19@524288@color +.TE +. +.P +.nr x \w'\fBright-brace'+1n+\w'00000'u .ta \nxuR +. .TP \nxu+3n -.BR char \t1 -Non-existent characters. +.BR break "\t4" +In fill mode, lines which could not be broken so that their length was +less than the line length. +. This is enabled by default. +. .TP -.BR number \t2 -Invalid numeric expressions. +.BR char "\t1" +Non-existent characters. +. This is enabled by default. +. .TP -.BR break \t4 -In fill mode, lines which could not be broken so that their length was -less than the line length. -This is enabled by default. +.BR color "\t524288" +Color related warnings. +. .TP -.BR delim \t8 +.BR delim "\t8" Missing or mismatched closing delimiters. +. +.TP +.BR di "\t256" +Use of +.B di +or +.B da +without an argument when there is no current diversion. +. .TP -.BR el \t16 +.BR el "\t16" Use of the .B el request with no matching .B ie request. +. .TP -.BR scale \t32 -Meaningless scaling indicators. +.BR escape "\t32768" +Unrecognized escape sequences. +. +When an unrecognized escape sequence is encountered, the escape +character is ignored. +. .TP -.BR range \t64 -Out of range arguments. +.BR font "\t131072" +Non-existent fonts. +. +This is enabled by default. +. .TP -.BR syntax \t128 -Dubious syntax in numeric expressions. +.BR ig "\t262144" +Invalid escapes in text ignored with the +.B ig +request. +. +These are conditions that are errors when they do not occur in ignored +text. +. .TP -.BR di \t256 -Use of -.B di -or -.B da -without an argument when there is no current diversion. +.BR input "\t16384" +Invalid input characters. +. .TP -.BR mac \t512 +.BR mac "\t512" Use of undefined strings, macros and diversions. -When an undefined string, macro or diversion is used, -that string is automatically defined as empty. -So, in most cases, at most one warning will be given for -each name. +. +When an undefined string, macro or diversion is used, that string is +automatically defined as empty. +. +So, in most cases, at most one warning will be given for each name. +. .TP -.BR reg \t1024 -Use of undefined number registers. -When an undefined number register is used, -that register is automatically defined to have a value of 0. -a definition is automatically made with a value of 0. -So, in most cases, at most one warning will be given for -use of a particular name. +.BR missing "\t8192" +Requests that are missing non-optional arguments. +. .TP -.BR tab \t2048 -Inappropriate use of a tab character. -Either use of a tab character where a number was expected, -or use of tab character in an unquoted macro argument. +.BR number "\t2" +Invalid numeric expressions. +. +This is enabled by default. +. .TP -.BR right-brace \t4096 -Use of -.B \e} -where a number was expected. +.BR range "\t64" +Out of range arguments. +. .TP -.BR missing \t8192 -Requests that are missing non-optional arguments. +.BR reg "\t1024" +Use of undefined number registers. +. +When an undefined number register is used, that register is +automatically defined to have a value of\~0. +. +So, in most cases, at most one warning will be given for use of a +particular name. +. .TP -.BR input \t16384 -Illegal input characters. +.BR right-brace "\t4096" +Use of +.B \[rs]} +where a number was expected. +. .TP -.BR escape \t32768 -Unrecognized escape sequences. -When an unrecognized escape sequence is encountered, -the escape character is ignored. +.BR scale "\t32" +Meaningless scaling indicators. +. .TP -.BR space \t65536 +.BR space "\t65536" Missing space between a request or macro and its argument. -This warning will be given -when an undefined name longer than two characters is encountered, -and the first two characters of the name make a defined name. +. +This warning will be given when an undefined name longer than two +characters is encountered, and the first two characters of the name +make a defined name. +. The request or macro will not be invoked. +. When this warning is given, no macro is automatically defined. +. This is enabled by default. +. This warning will never occur in compatibility mode. +. .TP -.BR font \t131072 -Non-existent fonts. -This is enabled by default. +.BR syntax "\t128" +Dubious syntax in numeric expressions. +. .TP -.BR ig \t262144 -Illegal escapes in text ignored with the -.B ig -request. -These are conditions that are errors when they do not occur -in ignored text. -.LP +.BR tab "\t2048" +Inappropriate use of a tab character. +Either use of a tab character where a number was expected, or use of tab +character in an unquoted macro argument. +. +.P There are also names that can be used to refer to groups of warnings: +. .TP .B all All warnings except .BR di , -.B mac +.BR mac , and .BR reg . -It is intended that this covers all warnings -that are useful with traditional macro packages. +It is intended that this covers all warnings that are useful with +traditional macro packages. +. .TP .B w All warnings. . -.SS Incompatibilities -. -.LP -Long names cause some incompatibilities. -UNIX troff will interpret -.IP -.B -\&.dsabcd -.LP -as defining a string -.B ab -with contents -.BR cd . -Normally, GNU troff will interpret this as a call of a macro named -.BR dsabcd . -Also UNIX troff will interpret -.B \e*[ -or -.B \en[ -as references to a string or number register called -.BR [ . -In GNU troff, however, this will normally be interpreted as the start -of a long name. -In -.I compatibility mode -GNU troff will interpret these things in the traditional way. -In compatibility mode, however, long names are not recognised. -Compatibility mode can be turned on with the -.B \-C -command line option, and turned on or off with the -.B cp -request. -The number register -.B \en[.C] -is 1 if compatibility mode is on, 0 otherwise. -.LP -GNU troff -does not allow the use of the escape sequences -.BR \\e\e|\e^\e&\e}\e{\e (space) \e'\e`\e-\e_\e!\e%\ec -in names of strings, macros, diversions, number registers, -fonts or environments; UNIX troff does. -The -.B \eA -escape sequence may be helpful in avoiding use of these -escape sequences in names. -.LP -Fractional pointsizes cause one noteworthy incompatibility. -In UNIX troff the -.B ps -request ignores scale indicators and so -.IP -.B .ps\ 10u -.LP -will set the pointsize to 10 points, whereas in -GNU troff it will set the pointsize to 10 scaled points. -.LP -In GNU troff there is a fundamental difference between unformatted, -input characters, and formatted, output characters. -Everything that affects how an output character -will be output is stored with the character; once an output -character has been constructed it is unaffected by any subsequent -requests that are executed, including -.BR bd , -.BR cs , -.BR tkf , -.BR tr , -or -.B fp -requests. -Normally output characters are constructed from input -characters at the moment immediately before the character -is added to the current output line. -Macros, diversions and strings are all, in fact, the same type -of object; they contain lists of input characters and output -characters in any combination. -An output character does not behave like an input character -for the purposes of macro processing; it does not inherit any -of the special properties that the input character from which it -was constructed might have had. -For example, -.IP -.nf -.ft B -\&.di x -\e\e\e\e -\&.br -\&.di -\&.x -.ft -.fi -.LP -will print -.B \e\e -in GNU troff; -each pair of input -.BR \e s -is turned into one output -.B \e -and the resulting output -.BR \e s -are not interpreted as escape characters when they are reread. -UNIX troff would interpret them as escape characters -when they were reread and would end up printing one -.BR \e . -The correct way to obtain a printable -.B \e -is to use the -.B \ee -escape sequence: this will always print a single instance of the -current escape character, regardless of whether or not it is used in a -diversion; it will also work in both GNU troff and UNIX troff. -If you wish for some reason to store in a diversion an escape -sequence that will be interpreted when the diversion is reread, -you can either use the traditional -.B \e!\& -transparent output facility, or, if this is unsuitable, the new -.B \e?\& -escape sequence. -. . +.\" -------------------------------------------------------------------- .SH ENVIRONMENT -. +.\" -------------------------------------------------------------------- . .TP .SM .B GROFF_TMAC_PATH A colon separated list of directories in which to search for macro files. -.B troff -will scan directories given in -the +.B @g@troff +will scan directories given in the .B \-M -option before these, and in standard directories (current directory if in -unsafe mode, home directory, -.BR @LOCALMACRODIR@ , +option before these, and in standard directories (current directory if +in unsafe mode, home directory, .BR @SYSTEMMACRODIR@ , +.BR @LOCALMACRODIR@ , .BR @MACRODIR@ ) after these. +. .TP .SM .B GROFF_TYPESETTER Default device. +. .TP .SM .B GROFF_FONT_PATH A colon separated list of directories in which to search for the .BI dev name directory. -.B troff +.B @g@troff will scan directories given in the .B \-F option before these, and in standard directories -.RB ( @FONTPATH@ ) +.RB ( @LOCALFONTDIR@ , +.BR @FONTDIR@ , +.BR @LEGACYFONTDIR@ ) after these. . . +.\" -------------------------------------------------------------------- .SH FILES -. +.\" -------------------------------------------------------------------- . .Tp \w'@FONTDIR@/devname/DESC'u+3n .B @MACRODIR@/troffrc Initialization file (called before any other macro package). +. .TP .B @MACRODIR@/troffrc-end Initialization file (called after any other macro package). +. .TP .BI @MACRODIR@/ name .tmac .TQ .BI @MACRODIR@/tmac. name Macro files +. .TP .BI @FONTDIR@/dev name /DESC Device description file for device .IR name . +. .TP .BI @FONTDIR@/dev name / F Font file for font .I F of device .IR name . -.LP +.P Note that .B troffrc and .B troffrc-end -are neither searched in the current nor in the home directory by default for -security reasons (even if the +are neither searched in the current nor in the home directory by +default for security reasons (even if the .B \-U option is given). +. Use the .B \-M command line option or the @@ -2463,28 +600,86 @@ environment variable to add these directories to the search path if necessary. . . +.\" -------------------------------------------------------------------- +.SH AUTHOR +.\" -------------------------------------------------------------------- +. +Copyright (C) 1989, 2001, 2002 Free Software Foundation, Inc. +. +.P +This document is distributed under the terms of the FDL (GNU Free +Documentation License) version 1.1 or later. +. +You should have received a copy of the FDL on your system, it is also +available on-line at the +.URL http://www.gnu.org/copyleft/fdl.html "GNU copyleft site" . +This document was written by James Clark, with modifications from +.MTO wl@gnu.org "Werner Lemberg" +and +.MTO bwarken@mayn.de "Bernd Warken" +. +.P +This document is part of +.IR groff , +the GNU roff distribution. +. +. +.\" -------------------------------------------------------------------- .SH "SEE ALSO" +.\" -------------------------------------------------------------------- . +.TP +.BR groff (@MAN1EXT@) +The main program of the +.I groff +system, a wrapper around +.IR @g@troff . . +.TP .BR groff (@MAN7EXT@) --- This is a short but complete reference of all requests, registers, and -escapes. -.PP -.BR groff (@MAN1EXT@), -.BR @g@tbl (@MAN1EXT@), -.BR @g@pic (@MAN1EXT@), -.BR @g@eqn (@MAN1EXT@), -.BR @g@refer (@MAN1EXT@), -.BR @g@soelim (@MAN1EXT@), -.BR @g@grn (@MAN1EXT@), -.BR grops (@MAN1EXT@), -.BR grodvi (@MAN1EXT@), -.BR grotty (@MAN1EXT@), -.BR grohtml (@MAN1EXT@), -.BR grolj4 (@MAN1EXT@), -.BR groff_font (@MAN5EXT@), -.BR groff_out (@MAN5EXT@), -.BR groff_char (@MAN7EXT@) +A description of the +.I groff +language, including a short but complete reference of all predefined +requests, registers, and escapes of plain +.IR groff . +From the command line, this is called by +.RS +.IP +.B man 7 groff +.RE +. +.TP +.BR \%groff_diff (@MAN7EXT@) +The differences of the +.I groff +language and the +.I classical troff +language. +. +Currently, this is the most actual document of the +.I groff +system. +. +.TP +.BR roff (@MAN7EXT@) +An overview over +.I groff +and other +.I roff +systems, including pointers to further related documentation. +. +.P +The +.I groff info +.IR file , +cf.\& +.BR info (@MAN1EXT@), +presents all groff documentation within a single document. +. +. +.\" -------------------------------------------------------------------- +.\" Emacs variables +.\" -------------------------------------------------------------------- . .\" Local Variables: .\" mode: nroff |