From c96557721be60d942f4d486b9ea7f9b7cbb034cc Mon Sep 17 00:00:00 2001 From: ru Date: Thu, 1 May 2003 13:09:50 +0000 Subject: Virgin import of FSF groff v1.19 --- contrib/groff/src/roff/troff/Makefile.sub | 45 +- contrib/groff/src/roff/troff/charinfo.h | 21 +- contrib/groff/src/roff/troff/column.cpp | 732 +++ contrib/groff/src/roff/troff/dictionary.cpp | 212 + contrib/groff/src/roff/troff/div.cpp | 1182 ++++ contrib/groff/src/roff/troff/div.h | 5 +- contrib/groff/src/roff/troff/env.cpp | 3818 +++++++++++++ contrib/groff/src/roff/troff/glyphuni.cpp | 503 ++ contrib/groff/src/roff/troff/input.cpp | 7879 +++++++++++++++++++++++++++ contrib/groff/src/roff/troff/node.cpp | 5993 ++++++++++++++++++++ contrib/groff/src/roff/troff/node.h | 16 +- contrib/groff/src/roff/troff/number.cpp | 697 +++ contrib/groff/src/roff/troff/reg.cpp | 474 ++ contrib/groff/src/roff/troff/reg.h | 4 +- contrib/groff/src/roff/troff/symbol.cpp | 154 + contrib/groff/src/roff/troff/token.h | 9 + contrib/groff/src/roff/troff/troff.man | 7 +- contrib/groff/src/roff/troff/unicode.cpp | 67 + contrib/groff/src/roff/troff/unicode.h | 26 + contrib/groff/src/roff/troff/uniglyph.cpp | 503 ++ contrib/groff/src/roff/troff/uniuni.cpp | 1994 +++++++ 21 files changed, 24306 insertions(+), 35 deletions(-) create mode 100644 contrib/groff/src/roff/troff/column.cpp create mode 100644 contrib/groff/src/roff/troff/dictionary.cpp create mode 100644 contrib/groff/src/roff/troff/div.cpp create mode 100644 contrib/groff/src/roff/troff/env.cpp create mode 100644 contrib/groff/src/roff/troff/glyphuni.cpp create mode 100644 contrib/groff/src/roff/troff/input.cpp create mode 100644 contrib/groff/src/roff/troff/node.cpp create mode 100644 contrib/groff/src/roff/troff/number.cpp create mode 100644 contrib/groff/src/roff/troff/reg.cpp create mode 100644 contrib/groff/src/roff/troff/symbol.cpp create mode 100644 contrib/groff/src/roff/troff/unicode.cpp create mode 100644 contrib/groff/src/roff/troff/unicode.h create mode 100644 contrib/groff/src/roff/troff/uniglyph.cpp create mode 100644 contrib/groff/src/roff/troff/uniuni.cpp (limited to 'contrib/groff/src/roff/troff') diff --git a/contrib/groff/src/roff/troff/Makefile.sub b/contrib/groff/src/roff/troff/Makefile.sub index e889cdd..7fa8024 100644 --- a/contrib/groff/src/roff/troff/Makefile.sub +++ b/contrib/groff/src/roff/troff/Makefile.sub @@ -3,25 +3,33 @@ MAN1=troff.n XLIBS=$(LIBGROFF) MLIB=$(LIBM) OBJS=\ + dictionary.$(OBJEXT) \ + div.$(OBJEXT) \ env.$(OBJEXT) \ - node.$(OBJEXT) \ + glyphuni.$(OBJEXT) \ input.$(OBJEXT) \ - div.$(OBJEXT) \ - symbol.$(OBJEXT) \ - dictionary.$(OBJEXT) \ - reg.$(OBJEXT) \ + majorminor.$(OBJEXT) \ + node.$(OBJEXT) \ number.$(OBJEXT) \ - majorminor.$(OBJEXT) + reg.$(OBJEXT) \ + symbol.$(OBJEXT) \ + unicode.$(OBJEXT) \ + uniglyph.$(OBJEXT) \ + uniuni.$(OBJEXT) CCSRCS=\ - $(srcdir)/env.cc \ - $(srcdir)/node.cc \ - $(srcdir)/input.cc \ - $(srcdir)/div.cc \ - $(srcdir)/symbol.cc \ - $(srcdir)/dictionary.cc \ - $(srcdir)/reg.cc \ - $(srcdir)/number.cc \ - majorminor.cc + $(srcdir)/dictionary.cpp \ + $(srcdir)/div.cpp \ + $(srcdir)/env.cpp \ + $(srcdir)/glyphuni.cpp \ + $(srcdir)/input.cpp \ + majorminor.cpp \ + $(srcdir)/node.cpp \ + $(srcdir)/number.cpp \ + $(srcdir)/reg.cpp \ + $(srcdir)/symbol.cpp \ + $(srcdir)/unicode.cpp \ + $(srcdir)/uniglyph.cpp \ + $(srcdir)/uniuni.cpp HDRS=\ $(srcdir)/charinfo.h \ $(srcdir)/dictionary.h \ @@ -34,11 +42,12 @@ HDRS=\ $(srcdir)/request.h \ $(srcdir)/symbol.h \ $(srcdir)/token.h \ - $(srcdir)/troff.h -GENSRCS=majorminor.cc + $(srcdir)/troff.h \ + $(srcdir)/unicode.h +GENSRCS=majorminor.cpp NAMEPREFIX=$(g) -majorminor.cc: $(top_srcdir)/VERSION $(top_srcdir)/REVISION +majorminor.cpp: $(top_srcdir)/VERSION $(top_srcdir)/REVISION @echo Making $@ @-rm -f $@ @echo const char \*major_version = \ diff --git a/contrib/groff/src/roff/troff/charinfo.h b/contrib/groff/src/roff/troff/charinfo.h index b907ae4..4123fba 100644 --- a/contrib/groff/src/roff/troff/charinfo.h +++ b/contrib/groff/src/roff/troff/charinfo.h @@ -37,7 +37,7 @@ class charinfo { // to transparent throughput char translate_input; // non-zero means that asciify_code is // active for .asciify (set by .trin) - char fallback; + char_mode mode; public: enum { ENDS_SENTENCE = 1, @@ -47,7 +47,7 @@ public: OVERLAPS_VERTICALLY = 16, TRANSPARENT = 32, NUMBERED = 64 - }; + }; enum { TRANSLATE_NONE, TRANSLATE_SPACE, @@ -77,13 +77,16 @@ public: void set_flags(unsigned char); void set_special_translation(int, int); int get_special_translation(int = 0); - macro *set_macro(macro *, int = 0); + macro *set_macro(macro *); + macro *setx_macro(macro *, char_mode); macro *get_macro(); int first_time_not_found(); void set_number(int); int get_number(); int numbered(); + int is_normal(); int is_fallback(); + int is_special(); symbol *get_symbol(); }; @@ -126,9 +129,19 @@ inline int charinfo::numbered() return flags & NUMBERED; } +inline int charinfo::is_normal() +{ + return mode == CHAR_NORMAL; +} + inline int charinfo::is_fallback() { - return fallback; + return mode == CHAR_FALLBACK; +} + +inline int charinfo::is_special() +{ + return mode == CHAR_SPECIAL; } inline charinfo *charinfo::get_translation(int transparent_throughput) diff --git a/contrib/groff/src/roff/troff/column.cpp b/contrib/groff/src/roff/troff/column.cpp new file mode 100644 index 0000000..8d6a6eb --- /dev/null +++ b/contrib/groff/src/roff/troff/column.cpp @@ -0,0 +1,732 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + +#ifdef COLUMN + +#include "troff.h" +#include "symbol.h" +#include "dictionary.h" +#include "hvunits.h" +#include "env.h" +#include "request.h" +#include "node.h" +#include "token.h" +#include "div.h" +#include "reg.h" +#include "stringclass.h" + +void output_file::vjustify(vunits, symbol) +{ + // do nothing +} + +struct justification_spec; +struct output_line; + +class column : public output_file { +private: + output_file *out; + vunits bottom; + output_line *col; + output_line **tail; + void add_output_line(output_line *); + void begin_page(int pageno, vunits page_length); + void flush(); + void print_line(hunits, vunits, node *, vunits, vunits); + void vjustify(vunits, symbol); + void transparent_char(unsigned char c); + void copy_file(hunits, vunits, const char *); + int is_printing(); + void check_bottom(); +public: + column(); + ~column(); + void start(); + void output(); + void justify(const justification_spec &); + void trim(); + void reset(); + vunits get_bottom(); + vunits get_last_extra_space(); + int is_active() { return out != 0; } +}; + +column *the_column = 0; + +struct transparent_output_line; +struct vjustify_output_line; + +class output_line { + output_line *next; +public: + output_line(); + virtual ~output_line(); + virtual void output(output_file *, vunits); + virtual transparent_output_line *as_transparent_output_line(); + virtual vjustify_output_line *as_vjustify_output_line(); + virtual vunits distance(); + virtual vunits height(); + virtual void reset(); + virtual vunits extra_space(); // post line + friend class column; + friend class justification_spec; +}; + +class position_output_line : public output_line { + vunits dist; +public: + position_output_line(vunits); + vunits distance(); +}; + +class node_output_line : public position_output_line { + node *nd; + hunits page_offset; + vunits before; + vunits after; +public: + node_output_line(vunits, node *, hunits, vunits, vunits); + ~node_output_line(); + void output(output_file *, vunits); + vunits height(); + vunits extra_space(); +}; + +class vjustify_output_line : public position_output_line { + vunits current; + symbol typ; +public: + vjustify_output_line(vunits dist, symbol); + vunits height(); + vjustify_output_line *as_vjustify_output_line(); + void vary(vunits amount); + void reset(); + symbol type(); +}; + +inline symbol vjustify_output_line::type() +{ + return typ; +} + +class copy_file_output_line : public position_output_line { + symbol filename; + hunits hpos; +public: + copy_file_output_line(vunits, const char *, hunits); + void output(output_file *, vunits); +}; + +class transparent_output_line : public output_line { + string buf; +public: + transparent_output_line(); + void output(output_file *, vunits); + void append_char(unsigned char c); + transparent_output_line *as_transparent_output_line(); +}; + +output_line::output_line() : next(0) +{ +} + +output_line::~output_line() +{ +} + +void output_line::reset() +{ +} + +transparent_output_line *output_line::as_transparent_output_line() +{ + return 0; +} + +vjustify_output_line *output_line::as_vjustify_output_line() +{ + return 0; +} + +void output_line::output(output_file *, vunits) +{ +} + +vunits output_line::distance() +{ + return V0; +} + +vunits output_line::height() +{ + return V0; +} + +vunits output_line::extra_space() +{ + return V0; +} + +position_output_line::position_output_line(vunits d) +: dist(d) +{ +} + +vunits position_output_line::distance() +{ + return dist; +} + +node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a) +: position_output_line(d), nd(n), page_offset(po), before(b), after(a) +{ +} + +node_output_line::~node_output_line() +{ + delete_node_list(nd); +} + +void node_output_line::output(output_file *out, vunits pos) +{ + out->print_line(page_offset, pos, nd, before, after); + nd = 0; +} + +vunits node_output_line::height() +{ + return after; +} + +vunits node_output_line::extra_space() +{ + return after; +} + +vjustify_output_line::vjustify_output_line(vunits d, symbol t) +: position_output_line(d), typ(t) +{ +} + +void vjustify_output_line::reset() +{ + current = V0; +} + +vunits vjustify_output_line::height() +{ + return current; +} + +vjustify_output_line *vjustify_output_line::as_vjustify_output_line() +{ + return this; +} + +inline void vjustify_output_line::vary(vunits amount) +{ + current += amount; +} + +transparent_output_line::transparent_output_line() +{ +} + +transparent_output_line *transparent_output_line::as_transparent_output_line() +{ + return this; +} + +void transparent_output_line::append_char(unsigned char c) +{ + assert(c != 0); + buf += c; +} + +void transparent_output_line::output(output_file *out, vunits) +{ + int len = buf.length(); + for (int i = 0; i < len; i++) + out->transparent_char(buf[i]); +} + +copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h) +: position_output_line(d), hpos(h), filename(f) +{ +} + +void copy_file_output_line::output(output_file *out, vunits pos) +{ + out->copy_file(hpos, pos, filename.contents()); +} + +column::column() +: bottom(V0), col(0), tail(&col), out(0) +{ +} + +column::~column() +{ + assert(out != 0); + error("automatically outputting column before exiting"); + output(); + delete the_output; +} + +void column::start() +{ + assert(out == 0); + if (!the_output) + init_output(); + assert(the_output != 0); + out = the_output; + the_output = this; +} + +void column::begin_page(int pageno, vunits page_length) +{ + assert(out != 0); + if (col) { + error("automatically outputting column before beginning next page"); + output(); + the_output->begin_page(pageno, page_length); + } + else + out->begin_page(pageno, page_length); + +} + +void column::flush() +{ + assert(out != 0); + out->flush(); +} + +int column::is_printing() +{ + assert(out != 0); + return out->is_printing(); +} + +vunits column::get_bottom() +{ + return bottom; +} + +void column::add_output_line(output_line *ln) +{ + *tail = ln; + bottom += ln->distance(); + bottom += ln->height(); + ln->next = 0; + tail = &(*tail)->next; +} + +void column::print_line(hunits page_offset, vunits pos, node *nd, + vunits before, vunits after) +{ + assert(out != 0); + add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after)); +} + +void column::vjustify(vunits pos, symbol typ) +{ + assert(out != 0); + add_output_line(new vjustify_output_line(pos - bottom, typ)); +} + +void column::transparent_char(unsigned char c) +{ + assert(out != 0); + transparent_output_line *tl = 0; + if (*tail) + tl = (*tail)->as_transparent_output_line(); + if (!tl) { + tl = new transparent_output_line; + add_output_line(tl); + } + tl->append_char(c); +} + +void column::copy_file(hunits page_offset, vunits pos, const char *filename) +{ + assert(out != 0); + add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset)); +} + +void column::trim() +{ + output_line **spp = 0; + for (output_line **pp = &col; *pp; pp = &(*pp)->next) + if ((*pp)->as_vjustify_output_line() == 0) + spp = 0; + else if (!spp) + spp = pp; + if (spp) { + output_line *ln = *spp; + *spp = 0; + tail = spp; + while (ln) { + output_line *tem = ln->next; + bottom -= ln->distance(); + bottom -= ln->height(); + delete ln; + ln = tem; + } + } +} + +void column::reset() +{ + bottom = V0; + for (output_line *ln = col; ln; ln = ln->next) { + bottom += ln->distance(); + ln->reset(); + bottom += ln->height(); + } +} + +void column::check_bottom() +{ + vunits b; + for (output_line *ln = col; ln; ln = ln->next) { + b += ln->distance(); + b += ln->height(); + } + assert(b == bottom); +} + +void column::output() +{ + assert(out != 0); + vunits vpos(V0); + output_line *ln = col; + while (ln) { + vpos += ln->distance(); + ln->output(out, vpos); + vpos += ln->height(); + output_line *tem = ln->next; + delete ln; + ln = tem; + } + tail = &col; + bottom = V0; + col = 0; + the_output = out; + out = 0; +} + +vunits column::get_last_extra_space() +{ + if (!col) + return V0; + for (output_line *p = col; p->next; p = p->next) + ; + return p->extra_space(); +} + +class justification_spec { + vunits height; + symbol *type; + vunits *amount; + int n; + int maxn; +public: + justification_spec(vunits); + ~justification_spec(); + void append(symbol t, vunits v); + void justify(output_line *, vunits *bottomp) const; +}; + +justification_spec::justification_spec(vunits h) +: height(h), n(0), maxn(10) +{ + type = new symbol[maxn]; + amount = new vunits[maxn]; +} + +justification_spec::~justification_spec() +{ + a_delete type; + a_delete amount; +} + +void justification_spec::append(symbol t, vunits v) +{ + if (v <= V0) { + if (v < V0) + warning(WARN_RANGE, + "maximum space for vertical justification must not be negative"); + else + warning(WARN_RANGE, + "maximum space for vertical justification must not be zero"); + return; + } + if (n >= maxn) { + maxn *= 2; + symbol *old_type = type; + type = new symbol[maxn]; + int i; + for (i = 0; i < n; i++) + type[i] = old_type[i]; + a_delete old_type; + vunits *old_amount = amount; + amount = new vunits[maxn]; + for (i = 0; i < n; i++) + amount[i] = old_amount[i]; + a_delete old_amount; + } + assert(n < maxn); + type[n] = t; + amount[n] = v; + n++; +} + +void justification_spec::justify(output_line *col, vunits *bottomp) const +{ + if (*bottomp >= height) + return; + vunits total; + output_line *p; + for (p = col; p; p = p->next) { + vjustify_output_line *sp = p->as_vjustify_output_line(); + if (sp) { + symbol t = sp->type(); + for (int i = 0; i < n; i++) { + if (t == type[i]) + total += amount[i]; + } + } + } + vunits gap = height - *bottomp; + for (p = col; p; p = p->next) { + vjustify_output_line *sp = p->as_vjustify_output_line(); + if (sp) { + symbol t = sp->type(); + for (int i = 0; i < n; i++) { + if (t == type[i]) { + if (total <= gap) { + sp->vary(amount[i]); + gap -= amount[i]; + } + else { + // gap < total + vunits v = scale(amount[i], gap, total); + sp->vary(v); + gap -= v; + } + total -= amount[i]; + } + } + } + } + assert(total == V0); + *bottomp = height - gap; +} + +void column::justify(const justification_spec &js) +{ + check_bottom(); + js.justify(col, &bottom); + check_bottom(); +} + +void column_justify() +{ + vunits height; + if (!the_column->is_active()) + error("can't justify column - column not active"); + else if (get_vunits(&height, 'v')) { + justification_spec js(height); + symbol nm = get_long_name(1); + if (!nm.is_null()) { + vunits v; + if (get_vunits(&v, 'v')) { + js.append(nm, v); + int err = 0; + while (has_arg()) { + nm = get_long_name(1); + if (nm.is_null()) { + err = 1; + break; + } + if (!get_vunits(&v, 'v')) { + err = 1; + break; + } + js.append(nm, v); + } + if (!err) + the_column->justify(js); + } + } + } + skip_line(); +} + +void column_start() +{ + if (the_column->is_active()) + error("can't start column - column already active"); + else + the_column->start(); + skip_line(); +} + +void column_output() +{ + if (!the_column->is_active()) + error("can't output column - column not active"); + else + the_column->output(); + skip_line(); +} + +void column_trim() +{ + if (!the_column->is_active()) + error("can't trim column - column not active"); + else + the_column->trim(); + skip_line(); +} + +void column_reset() +{ + if (!the_column->is_active()) + error("can't reset column - column not active"); + else + the_column->reset(); + skip_line(); +} + +class column_bottom_reg : public reg { +public: + const char *get_string(); +}; + +const char *column_bottom_reg::get_string() +{ + return i_to_a(the_column->get_bottom().to_units()); +} + +class column_extra_space_reg : public reg { +public: + const char *get_string(); +}; + +const char *column_extra_space_reg::get_string() +{ + return i_to_a(the_column->get_last_extra_space().to_units()); +} + +class column_active_reg : public reg { +public: + const char *get_string(); +}; + +const char *column_active_reg::get_string() +{ + return the_column->is_active() ? "1" : "0"; +} + +static int no_vjustify_mode = 0; + +class vjustify_node : public node { + symbol typ; +public: + vjustify_node(symbol); + int reread(int *); + const char *type(); + int same(node *); + node *copy(); +}; + +vjustify_node::vjustify_node(symbol t) +: typ(t) +{ +} + +node *vjustify_node::copy() +{ + return new vjustify_node(typ); +} + +const char *vjustify_node::type() +{ + return "vjustify_node"; +} + +int vjustify_node::same(node *nd) +{ + return typ == ((vjustify_node *)nd)->typ; +} + +int vjustify_node::reread(int *bolp) +{ + curdiv->vjustify(typ); + *bolp = 1; + return 1; +} + +void macro_diversion::vjustify(symbol type) +{ + if (!no_vjustify_mode) + mac->append(new vjustify_node(type)); +} + +void top_level_diversion::vjustify(symbol type) +{ + if (no_space_mode || no_vjustify_mode) + return; + assert(first_page_begun); // I'm not sure about this. + the_output->vjustify(vertical_position, type); +} + +void no_vjustify() +{ + skip_line(); + no_vjustify_mode = 1; +} + +void restore_vjustify() +{ + skip_line(); + no_vjustify_mode = 0; +} + +void init_column_requests() +{ + the_column = new column; + init_request("cols", column_start); + init_request("colo", column_output); + init_request("colj", column_justify); + init_request("colr", column_reset); + init_request("colt", column_trim); + init_request("nvj", no_vjustify); + init_request("rvj", restore_vjustify); + number_reg_dictionary.define(".colb", new column_bottom_reg); + number_reg_dictionary.define(".colx", new column_extra_space_reg); + number_reg_dictionary.define(".cola", new column_active_reg); + number_reg_dictionary.define(".nvj", + new constant_int_reg(&no_vjustify_mode)); +} + +#endif /* COLUMN */ diff --git a/contrib/groff/src/roff/troff/dictionary.cpp b/contrib/groff/src/roff/troff/dictionary.cpp new file mode 100644 index 0000000..a70ebb0 --- /dev/null +++ b/contrib/groff/src/roff/troff/dictionary.cpp @@ -0,0 +1,212 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" +#include "dictionary.h" + +// is `p' a good size for a hash table + +static int is_good_size(unsigned int p) +{ + const unsigned int SMALL = 10; + unsigned int i; + for (i = 2; i <= p/2; i++) + if (p % i == 0) + return 0; + for (i = 0x100; i != 0; i <<= 8) + if (i % p <= SMALL || i % p > p - SMALL) + return 0; + return 1; +} + +dictionary::dictionary(int n) : size(n), used(0), threshold(0.5), factor(1.5) +{ + table = new association[n]; +} + +// see Knuth, Sorting and Searching, p518, Algorithm L +// we can't use double-hashing because we want a remove function + +void *dictionary::lookup(symbol s, void *v) +{ + int i; + for (i = int(s.hash() % size); + table[i].v != 0; + i == 0 ? i = size - 1: --i) + if (s == table[i].s) { + if (v != 0) { + void *temp = table[i].v; + table[i].v = v; + return temp; + } + else + return table[i].v; + } + if (v == 0) + return 0; + ++used; + table[i].v = v; + table[i].s = s; + if ((double)used/(double)size >= threshold || used + 1 >= size) { + int old_size = size; + size = int(size*factor); + while (!is_good_size(size)) + ++size; + association *old_table = table; + table = new association[size]; + used = 0; + for (i = 0; i < old_size; i++) + if (old_table[i].v != 0) + (void)lookup(old_table[i].s, old_table[i].v); + a_delete old_table; + } + return 0; +} + +void *dictionary::lookup(const char *p) +{ + symbol s(p, MUST_ALREADY_EXIST); + if (s.is_null()) + return 0; + else + return lookup(s); +} + +// see Knuth, Sorting and Searching, p527, Algorithm R + +void *dictionary::remove(symbol s) +{ + // this relies on the fact that we are using linear probing + int i; + for (i = int(s.hash() % size); + table[i].v != 0 && s != table[i].s; + i == 0 ? i = size - 1: --i) + ; + void *p = table[i].v; + while (table[i].v != 0) { + table[i].v = 0; + int j = i; + int r; + do { + --i; + if (i < 0) + i = size - 1; + if (table[i].v == 0) + break; + r = int(table[i].s.hash() % size); + } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r)); + table[j] = table[i]; + } + if (p != 0) + --used; + return p; +} + +dictionary_iterator::dictionary_iterator(dictionary &d) : dict(&d), i(0) +{ +} + +int dictionary_iterator::get(symbol *sp, void **vp) +{ + for (; i < dict->size; i++) + if (dict->table[i].v) { + *sp = dict->table[i].s; + *vp = dict->table[i].v; + i++; + return 1; + } + return 0; +} + +object_dictionary_iterator::object_dictionary_iterator(object_dictionary &od) +: di(od.d) +{ +} + +object::object() : rcount(0) +{ +} + +object::~object() +{ +} + +void object::add_reference() +{ + rcount += 1; +} + +void object::remove_reference() +{ + if (--rcount == 0) + delete this; +} + +object_dictionary::object_dictionary(int n) : d(n) +{ +} + +object *object_dictionary::lookup(symbol nm) +{ + return (object *)d.lookup(nm); +} + +void object_dictionary::define(symbol nm, object *obj) +{ + obj->add_reference(); + obj = (object *)d.lookup(nm, obj); + if (obj) + obj->remove_reference(); +} + +void object_dictionary::rename(symbol oldnm, symbol newnm) +{ + object *obj = (object *)d.remove(oldnm); + if (obj) { + obj = (object *)d.lookup(newnm, obj); + if (obj) + obj->remove_reference(); + } +} + +void object_dictionary::remove(symbol nm) +{ + object *obj = (object *)d.remove(nm); + if (obj) + obj->remove_reference(); +} + +// Return non-zero if oldnm was defined. + +int object_dictionary::alias(symbol newnm, symbol oldnm) +{ + object *obj = (object *)d.lookup(oldnm); + if (obj) { + obj->add_reference(); + obj = (object *)d.lookup(newnm, obj); + if (obj) + obj->remove_reference(); + return 1; + } + return 0; +} + diff --git a/contrib/groff/src/roff/troff/div.cpp b/contrib/groff/src/roff/troff/div.cpp new file mode 100644 index 0000000..1bbbe45 --- /dev/null +++ b/contrib/groff/src/roff/troff/div.cpp @@ -0,0 +1,1182 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002 + Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + + +// diversions + +#include "troff.h" +#include "symbol.h" +#include "dictionary.h" +#include "hvunits.h" +#include "env.h" +#include "request.h" +#include "node.h" +#include "token.h" +#include "div.h" +#include "reg.h" + +int exit_started = 0; // the exit process has started +int done_end_macro = 0; // the end macro (if any) has finished +int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie +int last_page_number = 0; // if > 0, the number of the last page + // specified with -o +static int began_page_in_end_macro = 0; // a new page was begun during the end macro + +static int last_post_line_extra_space = 0; // needed for \n(.a +static int nl_reg_contents = -1; +static int dl_reg_contents = 0; +static int dn_reg_contents = 0; +static int vertical_position_traps_flag = 1; +static vunits truncated_space; +static vunits needed_space; + +diversion::diversion(symbol s) +: prev(0), nm(s), vertical_position(V0), high_water_mark(V0), + no_space_mode(0), marked_place(V0) +{ +} + +struct vertical_size { + vunits pre_extra, post_extra, pre, post; + vertical_size(vunits vs, vunits post_vs); +}; + +vertical_size::vertical_size(vunits vs, vunits post_vs) +: pre_extra(V0), post_extra(V0), pre(vs), post(post_vs) +{ +} + +void node::set_vertical_size(vertical_size *) +{ +} + +void extra_size_node::set_vertical_size(vertical_size *v) +{ + if (n < V0) { + if (-n > v->pre_extra) + v->pre_extra = -n; + } + else if (n > v->post_extra) + v->post_extra = n; +} + +void vertical_size_node::set_vertical_size(vertical_size *v) +{ + if (n < V0) + v->pre = -n; + else + v->post = n; +} + +top_level_diversion *topdiv; + +diversion *curdiv; + +void do_divert(int append, int boxing) +{ + tok.skip(); + symbol nm = get_name(); + if (nm.is_null()) { + if (curdiv->prev) { + if (boxing) { + curenv->line = curdiv->saved_line; + curenv->width_total = curdiv->saved_width_total; + curenv->space_total = curdiv->saved_space_total; + curenv->saved_indent = curdiv->saved_saved_indent; + curenv->target_text_length = curdiv->saved_target_text_length; + curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted; + } + diversion *temp = curdiv; + curdiv = curdiv->prev; + delete temp; + } + else + warning(WARN_DI, "diversion stack underflow"); + } + else { + macro_diversion *md = new macro_diversion(nm, append); + md->prev = curdiv; + curdiv = md; + if (boxing) { + curdiv->saved_line = curenv->line; + curdiv->saved_width_total = curenv->width_total; + curdiv->saved_space_total = curenv->space_total; + curdiv->saved_saved_indent = curenv->saved_indent; + curdiv->saved_target_text_length = curenv->target_text_length; + curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted; + curenv->line = 0; + curenv->start_line(); + } + } + skip_line(); +} + +void divert() +{ + do_divert(0, 0); +} + +void divert_append() +{ + do_divert(1, 0); +} + +void box() +{ + do_divert(0, 1); +} + +void box_append() +{ + do_divert(1, 1); +} + +void diversion::need(vunits n) +{ + vunits d = distance_to_next_trap(); + if (d < n) { + truncated_space = -d; + needed_space = n; + space(d, 1); + } +} + +macro_diversion::macro_diversion(symbol s, int append) +: diversion(s), max_width(H0) +{ +#if 0 + if (append) { + /* We don't allow recursive appends eg: + + .da a + .a + .di + + This causes an infinite loop in troff anyway. + This is because the user could do + + .as a foo + + in the diversion, and this would mess things up royally, + since there would be two things appending to the same + macro_header. + To make it work, we would have to copy the _contents_ + of the macro into which we were diverting; this doesn't + strike me as worthwhile. + However, + + .di a + .a + .a + .di + + will work and will make `a' contain two copies of what it contained + before; in troff, `a' would contain nothing. */ + request_or_macro *rm + = (request_or_macro *)request_dictionary.remove(s); + if (!rm || (mac = rm->to_macro()) == 0) + mac = new macro; + } + else + mac = new macro; +#endif + // We can now catch the situation described above by comparing + // the length of the charlist in the macro_header with the length + // stored in the macro. When we detect this, we copy the contents. + mac = new macro; + if (append) { + request_or_macro *rm + = (request_or_macro *)request_dictionary.lookup(s); + if (rm) { + macro *m = rm->to_macro(); + if (m) + *mac = *m; + } + } +} + +macro_diversion::~macro_diversion() +{ + request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); + macro *m = rm ? rm->to_macro() : 0; + if (m) { + *m = *mac; + delete mac; + } + else + request_dictionary.define(nm, mac); + mac = 0; + dl_reg_contents = max_width.to_units(); + dn_reg_contents = vertical_position.to_units(); +} + +vunits macro_diversion::distance_to_next_trap() +{ + if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position) + return diversion_trap_pos - vertical_position; + else + // Substract vresolution so that vunits::vunits does not overflow. + return vunits(INT_MAX - vresolution); +} + +void macro_diversion::transparent_output(unsigned char c) +{ + mac->append(c); +} + +void macro_diversion::transparent_output(node *n) +{ + mac->append(n); +} + +void macro_diversion::output(node *nd, int retain_size, + vunits vs, vunits post_vs, hunits width) +{ + no_space_mode = 0; + vertical_size v(vs, post_vs); + while (nd != 0) { + nd->set_vertical_size(&v); + node *temp = nd; + nd = nd->next; + if (temp->interpret(mac)) { + delete temp; + } + else { +#if 1 + temp->freeze_space(); +#endif + mac->append(temp); + } + } + last_post_line_extra_space = v.post_extra.to_units(); + if (!retain_size) { + v.pre = vs; + v.post = post_vs; + } + if (width > max_width) + max_width = width; + vunits x = v.pre + v.pre_extra + v.post + v.post_extra; + if (vertical_position_traps_flag + && !diversion_trap.is_null() && diversion_trap_pos > vertical_position + && diversion_trap_pos <= vertical_position + x) { + vunits trunc = vertical_position + x - diversion_trap_pos; + if (trunc > v.post) + trunc = v.post; + v.post -= trunc; + x -= trunc; + truncated_space = trunc; + spring_trap(diversion_trap); + } + mac->append(new vertical_size_node(-v.pre)); + mac->append(new vertical_size_node(v.post)); + mac->append('\n'); + vertical_position += x; + if (vertical_position - v.post > high_water_mark) + high_water_mark = vertical_position - v.post; +} + +void macro_diversion::space(vunits n, int) +{ + if (vertical_position_traps_flag + && !diversion_trap.is_null() && diversion_trap_pos > vertical_position + && diversion_trap_pos <= vertical_position + n) { + truncated_space = vertical_position + n - diversion_trap_pos; + n = diversion_trap_pos - vertical_position; + spring_trap(diversion_trap); + } + else if (n + vertical_position < V0) + n = -vertical_position; + mac->append(new diverted_space_node(n)); + vertical_position += n; +} + +void macro_diversion::copy_file(const char *filename) +{ + mac->append(new diverted_copy_file_node(filename)); +} + +top_level_diversion::top_level_diversion() +: page_number(0), page_count(0), last_page_count(-1), + page_length(units_per_inch*11), + prev_page_offset(units_per_inch), page_offset(units_per_inch), + page_trap_list(0), have_next_page_number(0), + ejecting_page(0), before_first_page(1) +{ +} + +// find the next trap after pos + +trap *top_level_diversion::find_next_trap(vunits *next_trap_pos) +{ + trap *next_trap = 0; + for (trap *pt = page_trap_list; pt != 0; pt = pt->next) + if (!pt->nm.is_null()) { + if (pt->position >= V0) { + if (pt->position > vertical_position + && pt->position < page_length + && (next_trap == 0 || pt->position < *next_trap_pos)) { + next_trap = pt; + *next_trap_pos = pt->position; + } + } + else { + vunits pos = pt->position; + pos += page_length; + if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) { + next_trap = pt; + *next_trap_pos = pos; + } + } + } + return next_trap; +} + +vunits top_level_diversion::distance_to_next_trap() +{ + vunits d; + if (!find_next_trap(&d)) + return page_length - vertical_position; + else + return d - vertical_position; +} + +void top_level_diversion::output(node *nd, int retain_size, + vunits vs, vunits post_vs, hunits width) +{ + no_space_mode = 0; + vunits next_trap_pos; + trap *next_trap = find_next_trap(&next_trap_pos); + if (before_first_page && begin_page()) + fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); + vertical_size v(vs, post_vs); + for (node *tem = nd; tem != 0; tem = tem->next) + tem->set_vertical_size(&v); + last_post_line_extra_space = v.post_extra.to_units(); + if (!retain_size) { + v.pre = vs; + v.post = post_vs; + } + vertical_position += v.pre; + vertical_position += v.pre_extra; + the_output->print_line(page_offset, vertical_position, nd, + v.pre + v.pre_extra, v.post_extra, width); + vertical_position += v.post_extra; + if (vertical_position > high_water_mark) + high_water_mark = vertical_position; + if (vertical_position_traps_flag && vertical_position >= page_length) + begin_page(); + else if (vertical_position_traps_flag + && next_trap != 0 && vertical_position >= next_trap_pos) { + nl_reg_contents = vertical_position.to_units(); + truncated_space = v.post; + spring_trap(next_trap->nm); + } + else if (v.post > V0) { + vertical_position += v.post; + if (vertical_position_traps_flag + && next_trap != 0 && vertical_position >= next_trap_pos) { + truncated_space = vertical_position - next_trap_pos; + vertical_position = next_trap_pos; + nl_reg_contents = vertical_position.to_units(); + spring_trap(next_trap->nm); + } + else if (vertical_position_traps_flag && vertical_position >= page_length) + begin_page(); + else + nl_reg_contents = vertical_position.to_units(); + } + else + nl_reg_contents = vertical_position.to_units(); +} + +void top_level_diversion::transparent_output(unsigned char c) +{ + if (before_first_page && begin_page()) + // 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) + the_output->transparent_char(*s++); +} + +void top_level_diversion::transparent_output(node * /*n*/) +{ + error("can't transparently output node at top level"); +} + +void top_level_diversion::copy_file(const char *filename) +{ + if (before_first_page && begin_page()) + fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); + the_output->copy_file(page_offset, vertical_position, filename); +} + +void top_level_diversion::space(vunits n, int forced) +{ + if (no_space_mode) { + if (!forced) + return; + else + no_space_mode = 0; + } + if (before_first_page) { + begin_page(n); + return; + } + vunits next_trap_pos; + trap *next_trap = find_next_trap(&next_trap_pos); + vunits y = vertical_position + n; + if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) { + vertical_position = next_trap_pos; + nl_reg_contents = vertical_position.to_units(); + truncated_space = y - vertical_position; + spring_trap(next_trap->nm); + } + else if (y < V0) { + vertical_position = V0; + nl_reg_contents = vertical_position.to_units(); + } + else if (vertical_position_traps_flag && y >= page_length && n >= V0) + begin_page(y - page_length); + else { + vertical_position = y; + nl_reg_contents = vertical_position.to_units(); + } +} + +trap::trap(symbol s, vunits n, trap *p) +: next(p), position(n), nm(s) +{ +} + +void top_level_diversion::add_trap(symbol nm, vunits pos) +{ + trap *first_free_slot = 0; + trap **p; + for (p = &page_trap_list; *p; p = &(*p)->next) { + if ((*p)->nm.is_null()) { + if (first_free_slot == 0) + first_free_slot = *p; + } + else if ((*p)->position == pos) { + (*p)->nm = nm; + return; + } + } + if (first_free_slot) { + first_free_slot->nm = nm; + first_free_slot->position = pos; + } + else + *p = new trap(nm, pos, 0); +} + +void top_level_diversion::remove_trap(symbol nm) +{ + for (trap *p = page_trap_list; p; p = p->next) + if (p->nm == nm) { + p->nm = NULL_SYMBOL; + return; + } +} + +void top_level_diversion::remove_trap_at(vunits pos) +{ + for (trap *p = page_trap_list; p; p = p->next) + if (p->position == pos) { + p->nm = NULL_SYMBOL; + return; + } +} + +void top_level_diversion::change_trap(symbol nm, vunits pos) +{ + for (trap *p = page_trap_list; p; p = p->next) + if (p->nm == nm) { + p->position = pos; + return; + } +} + +void top_level_diversion::print_traps() +{ + for (trap *p = page_trap_list; p; p = p->next) + if (p->nm.is_null()) + fprintf(stderr, " empty\n"); + else + fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units()); + fflush(stderr); +} + +void end_diversions() +{ + while (curdiv != topdiv) { + error("automatically ending diversion `%1' on exit", + curdiv->nm.contents()); + diversion *tem = curdiv; + curdiv = curdiv->prev; + delete tem; + } +} + +void cleanup_and_exit(int exit_code) +{ + if (the_output) { + the_output->trailer(topdiv->get_page_length()); + delete the_output; + } + exit(exit_code); +} + +// Returns non-zero if it sprung a top-of-page trap. +// The optional parameter is for the .trunc register. +int top_level_diversion::begin_page(vunits n) +{ + if (exit_started) { + if (page_count == last_page_count + ? curenv->is_empty() + : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro))) + cleanup_and_exit(0); + if (!done_end_macro) + began_page_in_end_macro = 1; + } + if (last_page_number > 0 && page_number == last_page_number) + cleanup_and_exit(0); + if (!the_output) + init_output(); + ++page_count; + if (have_next_page_number) { + page_number = next_page_number; + have_next_page_number = 0; + } + else if (before_first_page == 1) + page_number = 1; + else + page_number++; + // spring the top of page trap if there is one + vunits next_trap_pos; + vertical_position = -vresolution; + trap *next_trap = find_next_trap(&next_trap_pos); + vertical_position = V0; + high_water_mark = V0; + ejecting_page = 0; + // If before_first_page was 2, then the top of page transition was undone + // using eg .nr nl 0-1. See nl_reg::set_value. + if (before_first_page != 2) + the_output->begin_page(page_number, page_length); + before_first_page = 0; + nl_reg_contents = vertical_position.to_units(); + if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) { + truncated_space = n; + spring_trap(next_trap->nm); + return 1; + } + else + return 0; +} + +void continue_page_eject() +{ + if (topdiv->get_ejecting()) { + if (curdiv != topdiv) + error("can't continue page ejection because of current diversion"); + else if (!vertical_position_traps_flag) + error("can't continue page ejection because vertical position traps disabled"); + else { + push_page_ejector(); + topdiv->space(topdiv->get_page_length(), 1); + } + } +} + +void top_level_diversion::set_next_page_number(int n) +{ + next_page_number= n; + have_next_page_number = 1; +} + +int top_level_diversion::get_next_page_number() +{ + return have_next_page_number ? next_page_number : page_number + 1; +} + +void top_level_diversion::set_page_length(vunits n) +{ + page_length = n; +} + +diversion::~diversion() +{ +} + +void page_offset() +{ + hunits n; + // The troff manual says that the default scaling indicator is v, + // but it is in fact m: v wouldn't make sense for a horizontally + // oriented request. + if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset)) + n = topdiv->prev_page_offset; + topdiv->prev_page_offset = topdiv->page_offset; + topdiv->page_offset = n; + curenv->add_html_tag(0, ".po", n.to_units()); + skip_line(); +} + +void page_length() +{ + vunits n; + if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length())) + topdiv->set_page_length(n); + else + topdiv->set_page_length(11*units_per_inch); + skip_line(); +} + +void when_request() +{ + vunits n; + if (get_vunits(&n, 'v')) { + symbol s = get_name(); + if (s.is_null()) + topdiv->remove_trap_at(n); + else + topdiv->add_trap(s, n); + } + skip_line(); +} + +void begin_page() +{ + int got_arg = 0; + int n; + if (has_arg() && get_integer(&n, topdiv->get_page_number())) + got_arg = 1; + while (!tok.newline() && !tok.eof()) + tok.next(); + if (curdiv == topdiv) { + if (topdiv->before_first_page) { + if (!break_flag) { + if (got_arg) + topdiv->set_next_page_number(n); + if (got_arg || !topdiv->no_space_mode) + topdiv->begin_page(); + } + else if (topdiv->no_space_mode && !got_arg) + topdiv->begin_page(); + else { + /* Given this + + .wh 0 x + .de x + .tm \\n% + .. + .bp 3 + + troff prints + + 1 + 3 + + This code makes groff do the same. */ + + push_page_ejector(); + topdiv->begin_page(); + if (got_arg) + topdiv->set_next_page_number(n); + topdiv->set_ejecting(); + } + } + else { + push_page_ejector(); + if (break_flag) + curenv->do_break(); + if (got_arg) + topdiv->set_next_page_number(n); + if (!(topdiv->no_space_mode && !got_arg)) + topdiv->set_ejecting(); + } + } + tok.next(); +} + +void no_space() +{ + curdiv->no_space_mode = 1; + skip_line(); +} + +void restore_spacing() +{ + curdiv->no_space_mode = 0; + skip_line(); +} + +/* It is necessary to generate a break before before reading the argument, +because otherwise arguments using | will be wrong. But if we just +generate a break as usual, then the line forced out may spring a trap +and thus push a macro onto the input stack before we have had a chance +to read the argument to the sp request. We resolve this dilemma by +setting, before generating the break, a flag which will postpone the +actual pushing of the macro associated with the trap sprung by the +outputting of the line forced out by the break till after we have read +the argument to the request. If the break did cause a trap to be +sprung, then we don't actually do the space. */ + +void space_request() +{ + postpone_traps(); + if (break_flag) + curenv->do_break(); + vunits n; + if (!has_arg() || !get_vunits(&n, 'v')) + n = curenv->get_vertical_spacing(); + while (!tok.newline() && !tok.eof()) + tok.next(); + if (!unpostpone_traps() && !curdiv->no_space_mode) + curdiv->space(n); + else + // The line might have had line spacing that was truncated. + truncated_space += n; + curenv->add_html_tag(1, ".sp", n.to_units()); + tok.next(); +} + +void blank_line() +{ + curenv->do_break(); + if (!trap_sprung_flag && !curdiv->no_space_mode) { + curdiv->space(curenv->get_vertical_spacing()); + curenv->add_html_tag(1, ".sp", 1); + } + else + truncated_space += curenv->get_vertical_spacing(); +} + +/* need_space might spring a trap and so we must be careful that the +BEGIN_TRAP token is not skipped over. */ + +void need_space() +{ + vunits n; + if (!has_arg() || !get_vunits(&n, 'v')) + n = curenv->get_vertical_spacing(); + while (!tok.newline() && !tok.eof()) + tok.next(); + curdiv->need(n); + tok.next(); +} + +void page_number() +{ + int 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(); +} + +vunits saved_space; + +void save_vertical_space() +{ + vunits x; + if (!has_arg() || !get_vunits(&x, 'v')) + x = curenv->get_vertical_spacing(); + if (curdiv->distance_to_next_trap() > x) + curdiv->space(x, 1); + else + saved_space = x; + skip_line(); +} + +void output_saved_vertical_space() +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + if (saved_space > V0) + curdiv->space(saved_space, 1); + saved_space = V0; + tok.next(); +} + +void flush_output() +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + if (the_output) + the_output->flush(); + curenv->add_html_tag(1, ".fl"); + tok.next(); +} + +void macro_diversion::set_diversion_trap(symbol s, vunits n) +{ + diversion_trap = s; + diversion_trap_pos = n; +} + +void macro_diversion::clear_diversion_trap() +{ + diversion_trap = NULL_SYMBOL; +} + +void top_level_diversion::set_diversion_trap(symbol, vunits) +{ + error("can't set diversion trap when no current diversion"); +} + +void top_level_diversion::clear_diversion_trap() +{ + error("can't set diversion trap when no current diversion"); +} + +void diversion_trap() +{ + vunits n; + if (has_arg() && get_vunits(&n, 'v')) { + symbol s = get_name(); + if (!s.is_null()) + curdiv->set_diversion_trap(s, n); + else + curdiv->clear_diversion_trap(); + } + else + curdiv->clear_diversion_trap(); + skip_line(); +} + +void change_trap() +{ + symbol s = get_name(1); + if (!s.is_null()) { + vunits x; + if (has_arg() && get_vunits(&x, 'v')) + topdiv->change_trap(s, x); + else + topdiv->remove_trap(s); + } + skip_line(); +} + +void print_traps() +{ + topdiv->print_traps(); + skip_line(); +} + +void mark() +{ + symbol s = get_name(); + if (s.is_null()) + curdiv->marked_place = curdiv->get_vertical_position(); + else if (curdiv == topdiv) + set_number_reg(s, nl_reg_contents); + else + set_number_reg(s, curdiv->get_vertical_position().to_units()); + skip_line(); +} + +// This is truly bizarre. It is documented in the SQ manual. + +void return_request() +{ + vunits dist = curdiv->marked_place - curdiv->get_vertical_position(); + if (has_arg()) { + if (tok.ch() == '-') { + tok.next(); + vunits x; + if (get_vunits(&x, 'v')) + dist = -x; + } + else { + vunits x; + if (get_vunits(&x, 'v')) + dist = x >= V0 ? x - curdiv->get_vertical_position() : V0; + } + } + if (dist < V0) + curdiv->space(dist); + skip_line(); +} + +void vertical_position_traps() +{ + int n; + if (has_arg() && get_integer(&n)) + vertical_position_traps_flag = (n != 0); + else + vertical_position_traps_flag = 1; + skip_line(); +} + +class page_offset_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int page_offset_reg::get_value(units *res) +{ + *res = topdiv->get_page_offset().to_units(); + return 1; +} + +const char *page_offset_reg::get_string() +{ + return i_to_a(topdiv->get_page_offset().to_units()); +} + +class page_length_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int page_length_reg::get_value(units *res) +{ + *res = topdiv->get_page_length().to_units(); + return 1; +} + +const char *page_length_reg::get_string() +{ + return i_to_a(topdiv->get_page_length().to_units()); +} + +class vertical_position_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int vertical_position_reg::get_value(units *res) +{ + if (curdiv == topdiv && topdiv->before_first_page) + *res = -1; + else + *res = curdiv->get_vertical_position().to_units(); + return 1; +} + +const char *vertical_position_reg::get_string() +{ + if (curdiv == topdiv && topdiv->before_first_page) + return "-1"; + else + return i_to_a(curdiv->get_vertical_position().to_units()); +} + +class high_water_mark_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int high_water_mark_reg::get_value(units *res) +{ + *res = curdiv->get_high_water_mark().to_units(); + return 1; +} + +const char *high_water_mark_reg::get_string() +{ + return i_to_a(curdiv->get_high_water_mark().to_units()); +} + +class distance_to_next_trap_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int distance_to_next_trap_reg::get_value(units *res) +{ + *res = curdiv->distance_to_next_trap().to_units(); + return 1; +} + +const char *distance_to_next_trap_reg::get_string() +{ + return i_to_a(curdiv->distance_to_next_trap().to_units()); +} + +class diversion_name_reg : public reg { +public: + const char *get_string(); +}; + +const char *diversion_name_reg::get_string() +{ + return curdiv->get_diversion_name(); +} + +class page_number_reg : public general_reg { +public: + page_number_reg(); + int get_value(units *); + void set_value(units); +}; + +page_number_reg::page_number_reg() +{ +} + +void page_number_reg::set_value(units n) +{ + topdiv->set_page_number(n); +} + +int page_number_reg::get_value(units *res) +{ + *res = topdiv->get_page_number(); + return 1; +} + +class next_page_number_reg : public reg { +public: + const char *get_string(); +}; + +const char *next_page_number_reg::get_string() +{ + return i_to_a(topdiv->get_next_page_number()); +} + +class page_ejecting_reg : public reg { +public: + const char *get_string(); +}; + +const char *page_ejecting_reg::get_string() +{ + return i_to_a(topdiv->get_ejecting()); +} + +class constant_vunits_reg : public reg { + vunits *p; +public: + constant_vunits_reg(vunits *); + const char *get_string(); +}; + +constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q) +{ +} + +const char *constant_vunits_reg::get_string() +{ + return i_to_a(p->to_units()); +} + +class nl_reg : public variable_reg { +public: + nl_reg(); + void set_value(units); +}; + +nl_reg::nl_reg() : variable_reg(&nl_reg_contents) +{ +} + +void nl_reg::set_value(units n) +{ + variable_reg::set_value(n); + // Setting nl to a negative value when the vertical position in + // the top-level diversion is 0 undoes the top of page transition, + // so that the header macro will be called as if the top of page + // transition hasn't happened. This is used by Larry Wall's + // wrapman program. Setting before_first_page to 2 rather than 1, + // tells top_level_diversion::begin_page not to call + // output_file::begin_page again. + if (n < 0 && topdiv->get_vertical_position() == V0) + topdiv->before_first_page = 2; +} + +class no_space_mode_reg : public reg { +public: + int get_value(units *); + const char *get_string(); +}; + +int no_space_mode_reg::get_value(units *val) +{ + *val = curdiv->no_space_mode; + return 1; +} + +const char *no_space_mode_reg::get_string() +{ + return curdiv->no_space_mode ? "1" : "0"; +} + +void init_div_requests() +{ + init_request("box", box); + init_request("boxa", box_append); + init_request("bp", begin_page); + init_request("ch", change_trap); + init_request("da", divert_append); + init_request("di", divert); + init_request("dt", diversion_trap); + init_request("fl", flush_output); + init_request("mk", mark); + init_request("ne", need_space); + init_request("ns", no_space); + init_request("os", output_saved_vertical_space); + init_request("pl", page_length); + init_request("pn", page_number); + init_request("po", page_offset); + init_request("ptr", print_traps); + init_request("rs", restore_spacing); + init_request("rt", return_request); + init_request("sp", space_request); + init_request("sv", save_vertical_space); + init_request("vpt", vertical_position_traps); + init_request("wh", when_request); + number_reg_dictionary.define(".a", + new constant_int_reg(&last_post_line_extra_space)); + number_reg_dictionary.define(".d", new vertical_position_reg); + number_reg_dictionary.define(".h", new high_water_mark_reg); + number_reg_dictionary.define(".ne", + new constant_vunits_reg(&needed_space)); + number_reg_dictionary.define(".ns", new no_space_mode_reg); + number_reg_dictionary.define(".o", new page_offset_reg); + number_reg_dictionary.define(".p", new page_length_reg); + number_reg_dictionary.define(".pe", new page_ejecting_reg); + number_reg_dictionary.define(".pn", new next_page_number_reg); + number_reg_dictionary.define(".t", new distance_to_next_trap_reg); + number_reg_dictionary.define(".trunc", + new constant_vunits_reg(&truncated_space)); + number_reg_dictionary.define(".vpt", + new constant_int_reg(&vertical_position_traps_flag)); + number_reg_dictionary.define(".z", new diversion_name_reg); + number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents)); + number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents)); + number_reg_dictionary.define("nl", new nl_reg); + number_reg_dictionary.define("%", new page_number_reg); +} diff --git a/contrib/groff/src/roff/troff/div.h b/contrib/groff/src/roff/troff/div.h index 31b9af3..bde41a8 100644 --- a/contrib/groff/src/roff/troff/div.h +++ b/contrib/groff/src/roff/troff/div.h @@ -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. @@ -123,7 +124,7 @@ public: int get_page_number() { return page_number; } int get_next_page_number(); void set_page_number(int n) { page_number = n; } - int begin_page(); + int begin_page(vunits = V0); void set_next_page_number(int); void set_page_length(vunits); void copy_file(const char *filename); diff --git a/contrib/groff/src/roff/troff/env.cpp b/contrib/groff/src/roff/troff/env.cpp new file mode 100644 index 0000000..264b441 --- /dev/null +++ b/contrib/groff/src/roff/troff/env.cpp @@ -0,0 +1,3818 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" +#include "dictionary.h" +#include "hvunits.h" +#include "env.h" +#include "request.h" +#include "node.h" +#include "token.h" +#include "div.h" +#include "reg.h" +#include "charinfo.h" +#include "macropath.h" +#include "input.h" +#include + +symbol default_family("T"); + +enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 }; + +enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 }; + +struct env_list { + environment *env; + env_list *next; + env_list(environment *e, env_list *p) : env(e), next(p) {} +}; + +env_list *env_stack; +const int NENVIRONMENTS = 10; +environment *env_table[NENVIRONMENTS]; +dictionary env_dictionary(10); +environment *curenv; +static int next_line_number = 0; + +charinfo *field_delimiter_char; +charinfo *padding_indicator_char; + +int translate_space_to_dummy = 0; + +class pending_output_line { + node *nd; + int no_fill; + vunits vs; + vunits post_vs; + hunits width; +#ifdef WIDOW_CONTROL + int last_line; // Is it the last line of the paragraph? +#endif /* WIDOW_CONTROL */ +public: + pending_output_line *next; + + pending_output_line(node *, int, vunits, vunits, hunits, + pending_output_line * = 0); + ~pending_output_line(); + int output(); + +#ifdef WIDOW_CONTROL + friend void environment::mark_last_line(); + friend void environment::output(node *, int, vunits, vunits, hunits); +#endif /* WIDOW_CONTROL */ +}; + +pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv, + hunits w, pending_output_line *p) +: nd(n), no_fill(nf), vs(v), post_vs(pv), width(w), +#ifdef WIDOW_CONTROL + last_line(0), +#endif /* WIDOW_CONTROL */ + next(p) +{ +} + +pending_output_line::~pending_output_line() +{ + delete_node_list(nd); +} + +int pending_output_line::output() +{ + if (trap_sprung_flag) + return 0; +#ifdef WIDOW_CONTROL + if (next && next->last_line && !no_fill) { + curdiv->need(vs + post_vs + vunits(vresolution)); + if (trap_sprung_flag) { + next->last_line = 0; // Try to avoid infinite loops. + return 0; + } + } +#endif + curdiv->output(nd, no_fill, vs, post_vs, width); + nd = 0; + return 1; +} + +void environment::output(node *nd, int no_fill, vunits vs, vunits post_vs, + hunits width) +{ +#ifdef WIDOW_CONTROL + while (pending_lines) { + if (widow_control && !pending_lines->no_fill && !pending_lines->next) + break; + if (!pending_lines->output()) + break; + pending_output_line *tem = pending_lines; + pending_lines = pending_lines->next; + delete tem; + } +#else /* WIDOW_CONTROL */ + output_pending_lines(); +#endif /* WIDOW_CONTROL */ + if (!trap_sprung_flag && !pending_lines +#ifdef WIDOW_CONTROL + && (!widow_control || no_fill) +#endif /* WIDOW_CONTROL */ + ) { + curdiv->output(nd, no_fill, vs, post_vs, width); + emitted_node = 1; + } else { + pending_output_line **p; + for (p = &pending_lines; *p; p = &(*p)->next) + ; + *p = new pending_output_line(nd, no_fill, vs, post_vs, width); + } +} + +// a line from .tl goes at the head of the queue + +void environment::output_title(node *nd, int no_fill, vunits vs, + vunits post_vs, hunits width) +{ + if (!trap_sprung_flag) + curdiv->output(nd, no_fill, vs, post_vs, width); + else + pending_lines = new pending_output_line(nd, no_fill, vs, post_vs, width, + pending_lines); +} + +void environment::output_pending_lines() +{ + while (pending_lines && pending_lines->output()) { + pending_output_line *tem = pending_lines; + pending_lines = pending_lines->next; + delete tem; + } +} + +#ifdef WIDOW_CONTROL + +void environment::mark_last_line() +{ + if (!widow_control || !pending_lines) + return; + for (pending_output_line *p = pending_lines; p->next; p = p->next) + ; + if (!p->no_fill) + p->last_line = 1; +} + +void widow_control_request() +{ + int n; + if (has_arg() && get_integer(&n)) + curenv->widow_control = n != 0; + else + curenv->widow_control = 1; + skip_line(); +} + +#endif /* WIDOW_CONTROL */ + +/* font_size functions */ + +size_range *font_size::size_table = 0; +int font_size::nranges = 0; + +extern "C" { + +int compare_ranges(const void *p1, const void *p2) +{ + return ((size_range *)p1)->min - ((size_range *)p2)->min; +} + +} + +void font_size::init_size_table(int *sizes) +{ + nranges = 0; + while (sizes[nranges*2] != 0) + nranges++; + assert(nranges > 0); + size_table = new size_range[nranges]; + for (int i = 0; i < nranges; i++) { + size_table[i].min = sizes[i*2]; + size_table[i].max = sizes[i*2 + 1]; + } + qsort(size_table, nranges, sizeof(size_range), compare_ranges); +} + +font_size::font_size(int sp) +{ + for (int i = 0; i < nranges; i++) { + if (sp < size_table[i].min) { + if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max) + p = size_table[i - 1].max; + else + p = size_table[i].min; + return; + } + if (sp <= size_table[i].max) { + p = sp; + return; + } + } + p = size_table[nranges - 1].max; +} + +int font_size::to_units() +{ + return scale(p, units_per_inch, sizescale*72); +} + +// we can't do this in a static constructor because various dictionaries +// have to get initialized first + +void init_environments() +{ + curenv = env_table[0] = new environment("0"); +} + +void tab_character() +{ + curenv->tab_char = get_optional_char(); + skip_line(); +} + +void leader_character() +{ + curenv->leader_char = get_optional_char(); + skip_line(); +} + +void environment::add_char(charinfo *ci) +{ + int s; + if (interrupted) + ; + // don't allow fields in dummy environments + else if (ci == field_delimiter_char && !dummy) { + if (current_field) + wrap_up_field(); + else + start_field(); + } + else if (current_field && ci == padding_indicator_char) + add_padding(); + else if (current_tab) { + if (tab_contents == 0) + tab_contents = new line_start_node; + if (ci != hyphen_indicator_char) + tab_contents = tab_contents->add_char(ci, this, &tab_width, &s); + else + tab_contents = tab_contents->add_discretionary_hyphen(); + } + else { + if (line == 0) + start_line(); + if (ci != hyphen_indicator_char) + line = line->add_char(ci, this, &width_total, &space_total); + else + line = line->add_discretionary_hyphen(); + } +} + +node *environment::make_char_node(charinfo *ci) +{ + return make_node(ci, this); +} + +void environment::add_node(node *n) +{ + if (n == 0) + return; + if (current_tab || current_field) + n->freeze_space(); + if (interrupted) { + delete n; + } + else if (current_tab) { + n->next = tab_contents; + tab_contents = n; + tab_width += n->width(); + } + else { + if (line == 0) { + if (discarding && n->discardable()) { + // XXX possibly: input_line_start -= n->width(); + delete n; + return; + } + start_line(); + } + width_total += n->width(); + space_total += n->nspaces(); + n->next = line; + line = n; + } +} + + +void environment::add_hyphen_indicator() +{ + if (current_tab || interrupted || current_field + || hyphen_indicator_char != 0) + return; + if (line == 0) + start_line(); + line = line->add_discretionary_hyphen(); +} + +int environment::get_hyphenation_flags() +{ + return hyphenation_flags; +} + +int environment::get_hyphen_line_max() +{ + return hyphen_line_max; +} + +int environment::get_hyphen_line_count() +{ + return hyphen_line_count; +} + +int environment::get_center_lines() +{ + return center_lines; +} + +int environment::get_right_justify_lines() +{ + return right_justify_lines; +} + +void environment::add_italic_correction() +{ + if (current_tab) { + if (tab_contents) + tab_contents = tab_contents->add_italic_correction(&tab_width); + } + else if (line) + line = line->add_italic_correction(&width_total); +} + +void environment::space_newline() +{ + assert(!current_tab && !current_field); + if (interrupted) + return; + hunits x = H0; + hunits sw = env_space_width(this); + hunits ssw = env_sentence_space_width(this); + if (!translate_space_to_dummy) { + x = sw; + if (node_list_ends_sentence(line) == 1) + x += ssw; + } + width_list *w = new width_list(sw, ssw); + if (node_list_ends_sentence(line) == 1) + w->next = new width_list(sw, ssw); + if (line != 0 && line->merge_space(x, sw, ssw)) { + width_total += x; + return; + } + add_node(new word_space_node(x, get_fill_color(), w)); + possibly_break_line(0, spread_flag); + spread_flag = 0; +} + +void environment::space() +{ + space(env_space_width(this), env_sentence_space_width(this)); +} + +void environment::space(hunits space_width, hunits sentence_space_width) +{ + if (interrupted) + return; + if (current_field && padding_indicator_char == 0) { + add_padding(); + return; + } + hunits x = translate_space_to_dummy ? H0 : space_width; + node *p = current_tab ? tab_contents : line; + hunits *tp = current_tab ? &tab_width : &width_total; + if (p && p->nspaces() == 1 && p->width() == x + && node_list_ends_sentence(p->next) == 1) { + hunits xx = translate_space_to_dummy ? H0 : sentence_space_width; + if (p->merge_space(xx, space_width, sentence_space_width)) { + *tp += xx; + return; + } + } + if (p && p->merge_space(x, space_width, sentence_space_width)) { + *tp += x; + 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); + spread_flag = 0; +} + +node *do_underline_special(int); + +void environment::set_font(symbol nm) +{ + if (interrupted) + return; + if (nm == symbol("P") || nm.is_empty()) { + if (family->make_definite(prev_fontno) < 0) + return; + int tem = fontno; + fontno = prev_fontno; + prev_fontno = tem; + } + else { + prev_fontno = fontno; + int n = symbol_fontno(nm); + if (n < 0) { + n = next_available_font_position(); + if (!mount_font(n, nm)) + return; + } + if (family->make_definite(n) < 0) + return; + fontno = n; + } + if (underline_spaces && fontno != prev_fontno) { + if (fontno == get_underline_fontno()) + add_node(do_underline_special(1)); + if (prev_fontno == get_underline_fontno()) + add_node(do_underline_special(0)); + } +} + +void environment::set_font(int n) +{ + if (interrupted) + return; + if (is_good_fontno(n)) { + prev_fontno = fontno; + fontno = n; + } + else + warning(WARN_FONT, "bad font number"); +} + +void environment::set_family(symbol fam) +{ + if (interrupted) + return; + if (fam.is_null() || fam.is_empty()) { + if (prev_family->make_definite(fontno) < 0) + return; + font_family *tem = family; + family = prev_family; + prev_family = tem; + } + else { + font_family *f = lookup_family(fam); + if (f->make_definite(fontno) < 0) + return; + prev_family = family; + family = f; + } +} + +void environment::set_size(int n) +{ + if (interrupted) + return; + if (n == 0) { + font_size temp = prev_size; + prev_size = size; + size = temp; + int temp2 = prev_requested_size; + prev_requested_size = requested_size; + requested_size = temp2; + } + else { + prev_size = size; + size = font_size(n); + prev_requested_size = requested_size; + requested_size = n; + } +} + +void environment::set_char_height(int n) +{ + if (interrupted) + return; + if (n == requested_size || n <= 0) + char_height = 0; + else + char_height = n; +} + +void environment::set_char_slant(int n) +{ + if (interrupted) + return; + 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), + line_length((units_per_inch*13)/2), + prev_title_length((units_per_inch*13)/2), + title_length((units_per_inch*13)/2), + prev_size(sizescale*10), + size(sizescale*10), + requested_size(sizescale*10), + prev_requested_size(sizescale*10), + char_height(0), + char_slant(0), + space_size(12), + sentence_space_size(12), + adjust_mode(ADJUST_BOTH), + fill(1), + interrupted(0), + prev_line_interrupted(0), + center_lines(0), + right_justify_lines(0), + prev_vertical_spacing(points_to_units(12)), + vertical_spacing(points_to_units(12)), + prev_post_vertical_spacing(0), + post_vertical_spacing(0), + prev_line_spacing(1), + line_spacing(1), + prev_indent(0), + indent(0), + temporary_indent(0), + have_temporary_indent(0), + underline_lines(0), + underline_spaces(0), + input_trap_count(0), + continued_input_trap(0), + line(0), + prev_text_length(0), + width_total(0), + space_total(0), + input_line_start(0), + tabs(units_per_inch/2, TAB_LEFT), + line_tabs(0), + current_tab(TAB_NONE), + leader_node(0), + tab_char(0), + leader_char(charset_table['.']), + current_field(0), + discarding(0), + spread_flag(0), + margin_character_flags(0), + margin_character_node(0), + margin_character_distance(points_to_units(10)), + numbering_nodes(0), + number_text_separation(1), + line_number_indent(0), + line_number_multiple(1), + no_number_count(0), + hyphenation_flags(1), + hyphen_line_count(0), + hyphen_line_max(-1), + hyphenation_space(H0), + hyphenation_margin(H0), + composite(0), + pending_lines(0), +#ifdef WIDOW_CONTROL + widow_control(0), +#endif /* WIDOW_CONTROL */ + 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('\''), + hyphen_indicator_char(0) +{ + prev_family = family = lookup_family(default_family); + prev_fontno = fontno = 1; + if (!is_good_fontno(1)) + fatal("font number 1 not a valid font"); + if (family->make_definite(1) < 0) + fatal("invalid default family `%1'", default_family.contents()); + prev_fontno = fontno; +} + +environment::environment(const environment *e) +: dummy(1), + prev_line_length(e->prev_line_length), + line_length(e->line_length), + prev_title_length(e->prev_title_length), + title_length(e->title_length), + prev_size(e->prev_size), + size(e->size), + requested_size(e->requested_size), + prev_requested_size(e->prev_requested_size), + char_height(e->char_height), + char_slant(e->char_slant), + prev_fontno(e->prev_fontno), + fontno(e->fontno), + prev_family(e->prev_family), + family(e->family), + space_size(e->space_size), + sentence_space_size(e->sentence_space_size), + adjust_mode(e->adjust_mode), + fill(e->fill), + interrupted(0), + prev_line_interrupted(0), + center_lines(0), + right_justify_lines(0), + prev_vertical_spacing(e->prev_vertical_spacing), + vertical_spacing(e->vertical_spacing), + prev_post_vertical_spacing(e->prev_post_vertical_spacing), + post_vertical_spacing(e->post_vertical_spacing), + prev_line_spacing(e->prev_line_spacing), + line_spacing(e->line_spacing), + prev_indent(e->prev_indent), + indent(e->indent), + temporary_indent(0), + have_temporary_indent(0), + 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), + space_total(0), + input_line_start(0), + tabs(e->tabs), + line_tabs(e->line_tabs), + current_tab(TAB_NONE), + leader_node(0), + tab_char(e->tab_char), + leader_char(e->leader_char), + current_field(0), + discarding(0), + spread_flag(0), + margin_character_flags(e->margin_character_flags), + margin_character_node(e->margin_character_node), + margin_character_distance(e->margin_character_distance), + numbering_nodes(0), + number_text_separation(e->number_text_separation), + line_number_indent(e->line_number_indent), + line_number_multiple(e->line_number_multiple), + no_number_count(e->no_number_count), + hyphenation_flags(e->hyphenation_flags), + hyphen_line_count(0), + hyphen_line_max(e->hyphen_line_max), + hyphenation_space(e->hyphenation_space), + hyphenation_margin(e->hyphenation_margin), + composite(0), + pending_lines(0), +#ifdef WIDOW_CONTROL + widow_control(e->widow_control), +#endif /* WIDOW_CONTROL */ + 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), + hyphen_indicator_char(e->hyphen_indicator_char) +{ +} + +void environment::copy(const environment *e) +{ + prev_line_length = e->prev_line_length; + line_length = e->line_length; + prev_title_length = e->prev_title_length; + title_length = e->title_length; + prev_size = e->prev_size; + size = e->size; + prev_requested_size = e->prev_requested_size; + requested_size = e->requested_size; + char_height = e->char_height; + char_slant = e->char_slant; + space_size = e->space_size; + sentence_space_size = e->sentence_space_size; + adjust_mode = e->adjust_mode; + fill = e->fill; + interrupted = 0; + prev_line_interrupted = 0; + center_lines = 0; + right_justify_lines = 0; + prev_vertical_spacing = e->prev_vertical_spacing; + vertical_spacing = e->vertical_spacing; + prev_post_vertical_spacing = e->prev_post_vertical_spacing, + post_vertical_spacing = e->post_vertical_spacing, + prev_line_spacing = e->prev_line_spacing; + line_spacing = e->line_spacing; + prev_indent = e->prev_indent; + indent = e->indent; + have_temporary_indent = 0; + temporary_indent = 0; + 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; + input_line_start = 0; + control_char = e->control_char; + no_break_control_char = e->no_break_control_char; + hyphen_indicator_char = e->hyphen_indicator_char; + spread_flag = 0; + line = 0; + pending_lines = 0; + discarding = 0; + tabs = e->tabs; + line_tabs = e->line_tabs; + current_tab = TAB_NONE; + current_field = 0; + margin_character_flags = e->margin_character_flags; + margin_character_node = e->margin_character_node; + margin_character_distance = e->margin_character_distance; + numbering_nodes = 0; + number_text_separation = e->number_text_separation; + line_number_multiple = e->line_number_multiple; + line_number_indent = e->line_number_indent; + no_number_count = e->no_number_count; + tab_char = e->tab_char; + leader_char = e->leader_char; + hyphenation_flags = e->hyphenation_flags; + fontno = e->fontno; + prev_fontno = e->prev_fontno; + dummy = e->dummy; + family = e->family; + prev_family = e->prev_family; + leader_node = 0; +#ifdef WIDOW_CONTROL + widow_control = e->widow_control; +#endif /* WIDOW_CONTROL */ + hyphen_line_max = e->hyphen_line_max; + hyphen_line_count = 0; + 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() +{ + delete leader_node; + delete_node_list(line); + delete_node_list(numbering_nodes); +} + +hunits environment::get_input_line_position() +{ + hunits n; + if (line == 0) + n = -input_line_start; + else + n = width_total - input_line_start; + if (current_tab) + n += tab_width; + return n; +} + +void environment::set_input_line_position(hunits n) +{ + input_line_start = line == 0 ? -n : width_total - n; + if (current_tab) + input_line_start += tab_width; +} + +hunits environment::get_line_length() +{ + return line_length; +} + +hunits environment::get_saved_line_length() +{ + if (line) + return target_text_length + saved_indent; + else + return line_length; +} + +vunits environment::get_vertical_spacing() +{ + return vertical_spacing; +} + +vunits environment::get_post_vertical_spacing() +{ + return post_vertical_spacing; +} + +int environment::get_line_spacing() +{ + return line_spacing; +} + +vunits environment::total_post_vertical_spacing() +{ + vunits tem(post_vertical_spacing); + if (line_spacing > 1) + tem += (line_spacing - 1)*vertical_spacing; + return tem; +} + +int environment::get_bold() +{ + return get_bold_fontno(fontno); +} + +hunits environment::get_digit_width() +{ + return env_digit_width(this); +} + +int environment::get_adjust_mode() +{ + return adjust_mode; +} + +int environment::get_fill() +{ + return fill; +} + +hunits environment::get_indent() +{ + return indent; +} + +hunits environment::get_saved_indent() +{ + if (line) + return saved_indent; + else if (have_temporary_indent) + return temporary_indent; + else + return indent; +} + +hunits environment::get_temporary_indent() +{ + return temporary_indent; +} + +hunits environment::get_title_length() +{ + return title_length; +} + +node *environment::get_prev_char() +{ + for (node *n = current_tab ? tab_contents : line; n; n = n->next) { + node *last = n->last_char_node(); + if (last) + return last; + } + return 0; +} + +hunits environment::get_prev_char_width() +{ + node *last = get_prev_char(); + if (!last) + return H0; + return last->width(); +} + +hunits environment::get_prev_char_skew() +{ + node *last = get_prev_char(); + if (!last) + return H0; + return last->skew(); +} + +vunits environment::get_prev_char_height() +{ + node *last = get_prev_char(); + if (!last) + return V0; + vunits min, max; + last->vertical_extent(&min, &max); + return -min; +} + +vunits environment::get_prev_char_depth() +{ + node *last = get_prev_char(); + if (!last) + return V0; + vunits min, max; + last->vertical_extent(&min, &max); + return max; +} + +hunits environment::get_text_length() +{ + hunits n = line == 0 ? H0 : width_total; + if (current_tab) + n += tab_width; + return n; +} + +hunits environment::get_prev_text_length() +{ + return prev_text_length; +} + + +static int sb_reg_contents = 0; +static int st_reg_contents = 0; +static int ct_reg_contents = 0; +static int rsb_reg_contents = 0; +static int rst_reg_contents = 0; +static int skw_reg_contents = 0; +static int ssc_reg_contents = 0; + +void environment::width_registers() +{ + // this is used to implement \w; it sets the st, sb, ct registers + vunits min = 0, max = 0, cur = 0; + int character_type = 0; + ssc_reg_contents = line ? line->subscript_correction().to_units() : 0; + skw_reg_contents = line ? line->skew().to_units() : 0; + line = reverse_node_list(line); + vunits real_min = V0; + vunits real_max = V0; + vunits v1, v2; + for (node *tem = line; tem; tem = tem->next) { + tem->vertical_extent(&v1, &v2); + v1 += cur; + if (v1 < real_min) + real_min = v1; + v2 += cur; + if (v2 > real_max) + real_max = v2; + if ((cur += tem->vertical_width()) < min) + min = cur; + else if (cur > max) + max = cur; + character_type |= tem->character_type(); + } + line = reverse_node_list(line); + st_reg_contents = -min.to_units(); + sb_reg_contents = -max.to_units(); + rst_reg_contents = -real_min.to_units(); + rsb_reg_contents = -real_max.to_units(); + ct_reg_contents = character_type; +} + +node *environment::extract_output_line() +{ + if (current_tab) + wrap_up_tab(); + node *n = line; + line = 0; + return n; +} + +/* environment related requests */ + +void environment_switch() +{ + int pop = 0; // 1 means pop, 2 means pop but no error message on underflow + if (curenv->is_dummy()) + error("can't switch environments when current environment is dummy"); + else if (!has_arg()) + pop = 1; + else { + symbol nm; + if (!tok.delimiter()) { + // It looks like a number. + int n; + if (get_integer(&n)) { + if (n >= 0 && n < NENVIRONMENTS) { + env_stack = new env_list(curenv, env_stack); + if (env_table[n] == 0) + env_table[n] = new environment(i_to_a(n)); + curenv = env_table[n]; + } + else + nm = i_to_a(n); + } + else + pop = 2; + } + else { + nm = get_long_name(1); + if (nm.is_null()) + pop = 2; + } + if (!nm.is_null()) { + environment *e = (environment *)env_dictionary.lookup(nm); + if (!e) { + e = new environment(nm); + (void)env_dictionary.lookup(nm, e); + } + env_stack = new env_list(curenv, env_stack); + curenv = e; + } + } + if (pop) { + if (env_stack == 0) { + if (pop == 1) + error("environment stack underflow"); + } + else { + curenv = env_stack->env; + env_list *tem = env_stack; + env_stack = env_stack->next; + delete tem; + } + } + skip_line(); +} + +void environment_copy() +{ + symbol nm; + environment *e=0; + tok.skip(); + if (!tok.delimiter()) { + // It looks like a number. + int n; + if (get_integer(&n)) { + if (n >= 0 && n < NENVIRONMENTS) + e = env_table[n]; + else + nm = i_to_a(n); + } + } + else + nm = get_long_name(1); + if (!e && !nm.is_null()) + e = (environment *)env_dictionary.lookup(nm); + if (e == 0) { + error("No environment to copy from"); + return; + } + else + curenv->copy(e); + skip_line(); +} + +static symbol P_symbol("P"); + +void font_change() +{ + symbol s = get_name(); + int is_number = 1; + if (s.is_null() || s == P_symbol) { + s = P_symbol; + is_number = 0; + } + else { + for (const char *p = s.contents(); p != 0 && *p != 0; p++) + if (!csdigit(*p)) { + is_number = 0; + break; + } + } + if (is_number) + curenv->set_font(atoi(s.contents())); + else + curenv->set_font(s); + skip_line(); +} + +void family_change() +{ + symbol s = get_name(); + curenv->set_family(s); + skip_line(); +} + +void point_size() +{ + int n; + if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) { + if (n <= 0) + n = 1; + curenv->set_size(n); + curenv->add_html_tag(0, ".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; + if (get_integer(&n)) { + curenv->space_size = n; + if (has_arg() && get_integer(&n)) + curenv->sentence_space_size = n; + else + curenv->sentence_space_size = curenv->space_size; + } + skip_line(); +} + +void fill() +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + curenv->fill = 1; + curenv->add_html_tag(1, ".fi"); + curenv->add_html_tag(0, ".br"); + tok.next(); +} + +void no_fill() +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + curenv->fill = 0; + 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(); +} + +void center() +{ + int n; + if (!has_arg() || !get_integer(&n)) + n = 1; + else if (n < 0) + n = 0; + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + curenv->right_justify_lines = 0; + curenv->center_lines = n; + curenv->add_html_tag(1, ".ce", n); + tok.next(); +} + +void right_justify() +{ + int n; + if (!has_arg() || !get_integer(&n)) + n = 1; + else if (n < 0) + n = 0; + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + curenv->center_lines = 0; + curenv->right_justify_lines = n; + curenv->add_html_tag(1, ".rj", n); + tok.next(); +} + +void line_length() +{ + hunits temp; + if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) { + if (temp < H0) { + warning(WARN_RANGE, "bad line length %1u", temp.to_units()); + temp = H0; + } + } + else + temp = curenv->prev_line_length; + curenv->prev_line_length = curenv->line_length; + curenv->line_length = temp; + curenv->add_html_tag(1, ".ll", temp.to_units()); + skip_line(); +} + +void title_length() +{ + hunits temp; + if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) { + if (temp < H0) { + warning(WARN_RANGE, "bad title length %1u", temp.to_units()); + temp = H0; + } + } + else + temp = curenv->prev_title_length; + curenv->prev_title_length = curenv->title_length; + curenv->title_length = temp; + skip_line(); +} + +void vertical_spacing() +{ + vunits temp; + if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) { + if (temp < V0) { + warning(WARN_RANGE, "vertical spacing must not be negative"); + temp = vresolution; + } + } + else + temp = curenv->prev_vertical_spacing; + curenv->prev_vertical_spacing = curenv->vertical_spacing; + curenv->vertical_spacing = temp; + skip_line(); +} + +void post_vertical_spacing() +{ + vunits temp; + if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) { + if (temp < V0) { + warning(WARN_RANGE, + "post vertical spacing must be greater than or equal to 0"); + temp = V0; + } + } + else + temp = curenv->prev_post_vertical_spacing; + curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing; + curenv->post_vertical_spacing = temp; + skip_line(); +} + +void line_spacing() +{ + int temp; + if (has_arg() && get_integer(&temp)) { + if (temp < 1) { + warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp); + temp = 1; + } + } + else + temp = curenv->prev_line_spacing; + curenv->prev_line_spacing = curenv->line_spacing; + curenv->line_spacing = temp; + skip_line(); +} + +void indent() +{ + hunits temp; + if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) { + if (temp < H0) { + warning(WARN_RANGE, "indent cannot be negative"); + temp = H0; + } + } + else + temp = curenv->prev_indent; + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + curenv->have_temporary_indent = 0; + curenv->prev_indent = curenv->indent; + curenv->indent = temp; + if (break_flag) + curenv->add_html_tag(1, ".in", temp.to_units()); + tok.next(); +} + +void temporary_indent() +{ + int err = 0; + hunits temp; + if (!get_hunits(&temp, 'm', curenv->get_indent())) + err = 1; + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + if (temp < H0) { + warning(WARN_RANGE, "total indent cannot be negative"); + temp = H0; + } + if (!err) { + curenv->temporary_indent = temp; + curenv->have_temporary_indent = 1; + curenv->add_html_tag(1, ".ti", temp.to_units()); + } + tok.next(); +} + +node *do_underline_special(int underline_spaces) +{ + macro m; + m.append_str("x u "); + m.append(underline_spaces + '0'); + return new special_node(m, 1); +} + +void do_underline(int underline_spaces) +{ + int n; + if (!has_arg() || !get_integer(&n)) + n = 1; + if (n <= 0) { + if (curenv->underline_lines > 0) { + curenv->prev_fontno = curenv->fontno; + curenv->fontno = curenv->pre_underline_fontno; + if (underline_spaces) { + curenv->underline_spaces = 0; + curenv->add_node(do_underline_special(0)); + } + } + curenv->underline_lines = 0; + } + else { + curenv->underline_lines = n; + curenv->pre_underline_fontno = curenv->fontno; + curenv->fontno = get_underline_fontno(); + if (underline_spaces) { + curenv->underline_spaces = 1; + curenv->add_node(do_underline_special(1)); + } + } + skip_line(); +} + +void continuous_underline() +{ + do_underline(1); +} + +void underline() +{ + do_underline(0); +} + +void control_char() +{ + curenv->control_char = '.'; + if (has_arg()) { + if (tok.ch() == 0) + error("bad control character"); + else + curenv->control_char = tok.ch(); + } + skip_line(); +} + +void no_break_control_char() +{ + curenv->no_break_control_char = '\''; + if (has_arg()) { + if (tok.ch() == 0) + error("bad control character"); + else + curenv->no_break_control_char = tok.ch(); + } + skip_line(); +} + +void margin_character() +{ + while (tok.space()) + tok.next(); + charinfo *ci = tok.get_char(); + if (ci) { + // Call tok.next() only after making the node so that + // .mc \s+9\(br\s0 works. + node *nd = curenv->make_char_node(ci); + tok.next(); + if (nd) { + delete curenv->margin_character_node; + curenv->margin_character_node = nd; + curenv->margin_character_flags = (MARGIN_CHARACTER_ON + |MARGIN_CHARACTER_NEXT); + hunits d; + if (has_arg() && get_hunits(&d, 'm')) + curenv->margin_character_distance = d; + } + } + else { + check_missing_character(); + curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON; + if (curenv->margin_character_flags == 0) { + delete curenv->margin_character_node; + curenv->margin_character_node = 0; + } + } + skip_line(); +} + +void number_lines() +{ + delete_node_list(curenv->numbering_nodes); + curenv->numbering_nodes = 0; + if (has_arg()) { + node *nd = 0; + for (int i = '9'; i >= '0'; i--) { + node *tem = make_node(charset_table[i], curenv); + if (!tem) { + skip_line(); + return; + } + tem->next = nd; + nd = tem; + } + curenv->numbering_nodes = nd; + curenv->line_number_digit_width = env_digit_width(curenv); + int n; + if (!tok.delimiter()) { + if (get_integer(&n, next_line_number)) { + next_line_number = n; + if (next_line_number < 0) { + warning(WARN_RANGE, "negative line number"); + next_line_number = 0; + } + } + } + else + while (!tok.space() && !tok.newline() && !tok.eof()) + tok.next(); + if (has_arg()) { + if (!tok.delimiter()) { + if (get_integer(&n)) { + if (n <= 0) { + warning(WARN_RANGE, "negative or zero line number multiple"); + } + else + curenv->line_number_multiple = n; + } + } + else + while (!tok.space() && !tok.newline() && !tok.eof()) + tok.next(); + if (has_arg()) { + if (!tok.delimiter()) { + if (get_integer(&n)) + curenv->number_text_separation = n; + } + else + while (!tok.space() && !tok.newline() && !tok.eof()) + tok.next(); + if (has_arg() && !tok.delimiter() && get_integer(&n)) + curenv->line_number_indent = n; + } + } + } + skip_line(); +} + +void no_number() +{ + int n; + if (has_arg() && get_integer(&n)) + curenv->no_number_count = n > 0 ? n : 0; + else + curenv->no_number_count = 1; + skip_line(); +} + +void no_hyphenate() +{ + curenv->hyphenation_flags = 0; + skip_line(); +} + +void hyphenate_request() +{ + int n; + if (has_arg() && get_integer(&n)) + curenv->hyphenation_flags = n; + else + curenv->hyphenation_flags = 1; + skip_line(); +} + +void hyphen_char() +{ + curenv->hyphen_indicator_char = get_optional_char(); + skip_line(); +} + +void hyphen_line_max_request() +{ + int n; + if (has_arg() && get_integer(&n)) + curenv->hyphen_line_max = n; + else + curenv->hyphen_line_max = -1; + skip_line(); +} + +void environment::interrupt() +{ + if (!dummy) { + add_node(new transparent_dummy_node); + interrupted = 1; + } +} + +void environment::newline() +{ + if (underline_lines > 0) { + if (--underline_lines == 0) { + prev_fontno = fontno; + fontno = pre_underline_fontno; + if (underline_spaces) { + underline_spaces = 0; + add_node(do_underline_special(0)); + } + } + } + if (current_field) + wrap_up_field(); + if (current_tab) + wrap_up_tab(); + // strip trailing spaces + while (line != 0 && line->discardable()) { + width_total -= line->width(); + space_total -= line->nspaces(); + node *tem = line; + line = line->next; + delete tem; + } + node *to_be_output = 0; + hunits to_be_output_width; + prev_line_interrupted = 0; + if (dummy) + space_newline(); + else if (interrupted) { + interrupted = 0; + // see environment::final_break + prev_line_interrupted = exit_started ? 2 : 1; + } + else if (center_lines > 0) { + --center_lines; + hunits x = target_text_length - width_total; + 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; + } + else if (right_justify_lines > 0) { + --right_justify_lines; + hunits x = target_text_length - width_total; + if (x > H0) + saved_indent += x; + to_be_output = line; + to_be_output_width = width_total; + line = 0; + } + else if (fill) + space_newline(); + else { + to_be_output = line; + to_be_output_width = width_total; + line = 0; + } + 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 (!(continued_input_trap && prev_line_interrupted)) + if (--input_trap_count == 0) + spring_trap(input_trap); + } +} + +void environment::output_line(node *n, hunits width) +{ + prev_text_length = width; + if (margin_character_flags) { + hunits d = line_length + margin_character_distance - saved_indent - width; + if (d > 0) { + n = new hmotion_node(d, get_fill_color(), n); + width += d; + } + margin_character_flags &= ~MARGIN_CHARACTER_NEXT; + node *tem; + if (!margin_character_flags) { + tem = margin_character_node; + margin_character_node = 0; + } + else + tem = margin_character_node->copy(); + tem->next = n; + n = tem; + width += tem->width(); + } + node *nn = 0; + while (n != 0) { + node *tem = n->next; + n->next = nn; + nn = n; + n = tem; + } + if (!saved_indent.is_zero()) + nn = new hmotion_node(saved_indent, get_fill_color(), nn); + width += saved_indent; + if (no_number_count > 0) + --no_number_count; + else if (numbering_nodes) { + 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, get_fill_color(), nn); + else { + hunits x = w; + 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); + for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) { + node *gn = numbering_nodes; + for (int count = *p - '0'; count > 0; count--) + gn = gn->next; + gn = gn->copy(); + x -= gn->width(); + gn->next = nn; + nn = gn; + } + nn = new hmotion_node(x, get_fill_color(), nn); + } + width += w; + ++next_line_number; + } + output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width); +} + +void environment::start_line() +{ + assert(line == 0); + discarding = 0; + line = new line_start_node; + if (have_temporary_indent) { + saved_indent = temporary_indent; + have_temporary_indent = 0; + } + else + saved_indent = indent; + target_text_length = line_length - saved_indent; + width_total = H0; + space_total = 0; +} + +hunits environment::get_hyphenation_space() +{ + return hyphenation_space; +} + +void hyphenation_space_request() +{ + hunits n; + if (get_hunits(&n, 'm')) { + if (n < H0) { + warning(WARN_RANGE, "hyphenation space cannot be negative"); + n = H0; + } + curenv->hyphenation_space = n; + } + skip_line(); +} + +hunits environment::get_hyphenation_margin() +{ + return hyphenation_margin; +} + +void hyphenation_margin_request() +{ + hunits n; + if (get_hunits(&n, 'm')) { + if (n < H0) { + warning(WARN_RANGE, "hyphenation margin cannot be negative"); + n = H0; + } + curenv->hyphenation_margin = n; + } + skip_line(); +} + +breakpoint *environment::choose_breakpoint() +{ + hunits x = width_total; + int s = space_total; + node *n = line; + breakpoint *best_bp = 0; // the best breakpoint so far + int best_bp_fits = 0; + while (n != 0) { + x -= n->width(); + s -= n->nspaces(); + breakpoint *bp = n->get_breakpoints(x, s); + while (bp != 0) { + if (bp->width <= target_text_length) { + if (!bp->hyphenated) { + breakpoint *tem = bp->next; + bp->next = 0; + while (tem != 0) { + breakpoint *tem1 = tem; + tem = tem->next; + delete tem1; + } + if (best_bp_fits + // Decide whether to use the hyphenated breakpoint. + && (hyphen_line_max < 0 + // Only choose the hyphenated breakpoint if it would not + // exceed the maximum number of consecutive hyphenated + // lines. + || hyphen_line_count + 1 <= hyphen_line_max) + && !(adjust_mode == ADJUST_BOTH + // Don't choose the hyphenated breakpoint if the line + // can be justified by adding no more than + // hyphenation_space to any word space. + ? (bp->nspaces > 0 + && (((target_text_length - bp->width + + (bp->nspaces - 1)*hresolution)/bp->nspaces) + <= hyphenation_space)) + // Don't choose the hyphenated breakpoint if the line + // is no more than hyphenation_margin short. + : target_text_length - bp->width <= hyphenation_margin)) { + delete bp; + return best_bp; + } + if (best_bp) + delete best_bp; + return bp; + } + else { + if ((adjust_mode == ADJUST_BOTH + ? hyphenation_space == H0 + : hyphenation_margin == H0) + && (hyphen_line_max < 0 + || hyphen_line_count + 1 <= hyphen_line_max)) { + // No need to consider a non-hyphenated breakpoint. + if (best_bp) + delete best_bp; + breakpoint *tem = bp->next; + bp->next = 0; + while (tem != 0) { + breakpoint *tem1 = tem; + tem = tem->next; + delete tem1; + } + return bp; + } + // It fits but it's hyphenated. + if (!best_bp_fits) { + if (best_bp) + delete best_bp; + best_bp = bp; + bp = bp->next; + best_bp_fits = 1; + } + else { + breakpoint *tem = bp; + bp = bp->next; + delete tem; + } + } + } + else { + if (best_bp) + delete best_bp; + best_bp = bp; + bp = bp->next; + } + } + n = n->next; + } + if (best_bp) { + if (!best_bp_fits) + output_warning(WARN_BREAK, "can't break line"); + return best_bp; + } + return 0; +} + +void environment::hyphenate_line(int start_here) +{ + assert(line != 0); + hyphenation_type prev_type = line->get_hyphenation_type(); + node **startp; + if (start_here) + startp = &line; + else + for (startp = &line->next; *startp != 0; startp = &(*startp)->next) { + hyphenation_type this_type = (*startp)->get_hyphenation_type(); + if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE) + break; + prev_type = this_type; + } + if (*startp == 0) + return; + node *tem = *startp; + do { + tem = tem->next; + } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE); + int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT); + node *end = tem; + hyphen_list *sl = 0; + tem = *startp; + node *forward = 0; + int i = 0; + while (tem != end) { + sl = tem->get_hyphen_list(sl, &i); + node *tem1 = tem; + tem = tem->next; + tem1->next = forward; + forward = tem1; + } + if (!inhibit) { + // this is for characters like hyphen and emdash + int prev_code = 0; + for (hyphen_list *h = sl; h; h = h->next) { + h->breakable = (prev_code != 0 + && h->next != 0 + && h->next->hyphenation_code != 0); + prev_code = h->hyphenation_code; + } + } + if (hyphenation_flags != 0 + && !inhibit + // this may not be right if we have extra space on this line + && !((hyphenation_flags & HYPHEN_LAST_LINE) + && (curdiv->distance_to_next_trap() + <= vertical_spacing + total_post_vertical_spacing())) + && i >= 4) + hyphenate(sl, hyphenation_flags); + while (forward != 0) { + node *tem1 = forward; + forward = forward->next; + tem1->next = 0; + tem = tem1->add_self(tem, &sl); + } + *startp = tem; +} + +static node *node_list_reverse(node *n) +{ + node *res = 0; + while (n) { + node *tem = n; + n = n->next; + tem->next = res; + res = tem; + } + return res; +} + +static void distribute_space(node *n, int nspaces, hunits desired_space, + int force_reverse = 0) +{ + 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) + (void)node_list_reverse(n); + if (!force_reverse) + reverse = !reverse; + assert(desired_space.is_zero() && nspaces == 0); +} + +void environment::possibly_break_line(int start_here, int forced) +{ + if (!fill || current_tab || current_field || dummy) + return; + while (line != 0 + && (forced + // When a macro follows a paragraph in fill mode, the + // current line should not be empty. + || (width_total - line->width()) > target_text_length)) { + hyphenate_line(start_here); + breakpoint *bp = choose_breakpoint(); + if (bp == 0) + // we'll find one eventually + return; + node *pre, *post; + node **ndp = &line; + while (*ndp != bp->nd) + ndp = &(*ndp)->next; + bp->nd->split(bp->index, &pre, &post); + *ndp = post; + hunits extra_space_width = H0; + switch(adjust_mode) { + 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; + break; + case ADJUST_RIGHT: + saved_indent += target_text_length - bp->width; + break; + } + distribute_space(pre, bp->nspaces, extra_space_width); + hunits output_width = bp->width + extra_space_width; + input_line_start -= output_width; + if (bp->hyphenated) + hyphen_line_count++; + else + hyphen_line_count = 0; + delete bp; + space_total = 0; + width_total = 0; + node *first_non_discardable = 0; + node *tem; + for (tem = line; tem != 0; tem = tem->next) + if (!tem->discardable()) + first_non_discardable = tem; + node *to_be_discarded; + if (first_non_discardable) { + to_be_discarded = first_non_discardable->next; + first_non_discardable->next = 0; + for (tem = line; tem != 0; tem = tem->next) { + width_total += tem->width(); + space_total += tem->nspaces(); + } + discarding = 0; + } + else { + discarding = 1; + to_be_discarded = line; + line = 0; + } + // Do output_line() here so that line will be 0 iff the + // the environment will be empty. + output_line(pre, output_width); + while (to_be_discarded != 0) { + tem = to_be_discarded; + to_be_discarded = to_be_discarded->next; + input_line_start -= tem->width(); + delete tem; + } + if (line != 0) { + if (have_temporary_indent) { + saved_indent = temporary_indent; + have_temporary_indent = 0; + } + else + saved_indent = indent; + target_text_length = line_length - saved_indent; + } + } +} + +/* +Do the break at the end of input after the end macro (if any). + +Unix troff behaves as follows: if the last line is + +foo bar\c + +it will output foo on the current page, and bar on the next page; +if the last line is + +foo\c + +or + +foo bar + +everything will be output on the current page. This behaviour must be +considered a bug. + +The problem is that some macro packages rely on this. For example, +the ATK macros have an end macro that emits \c if it needs to print a +table of contents but doesn't do a 'bp in the end macro; instead the +'bp is done in the bottom of page trap. This works with Unix troff, +provided that the current environment is not empty at the end of the +input file. + +The following will make macro packages that do that sort of thing work +even if the current environment is empty at the end of the input file. +If the last input line used \c and this line occurred in the end macro, +then we'll force everything out on the current page, but we'll make +sure that the environment isn't empty so that we won't exit at the +bottom of this page. +*/ + +void environment::final_break() +{ + if (prev_line_interrupted == 2) { + do_break(); + add_node(new transparent_dummy_node); + } + else + do_break(); +} + +/* + * add_html_tag - emits a special html-tag: to help post-grohtml understand + * the key troff commands + */ + +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 + * 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); + curdiv->output(new special_node(*m), 1, 0, 0, 0); + if (strcmp(name, ".nf") == 0) + curenv->ignore_next_eol = 1; + } +} + +/* + * add_html_tag - emits a special html-tag: to help post-grohtml understand + * the key troff commands, it appends a string representation + * of i. + */ + +void environment::add_html_tag(int force, const char *name, int i) +{ + 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 + */ + 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); + node *n = new special_node(*m); + curdiv->output(n, 1, 0, 0, 0); + } +} + +/* + * add_html_tag_tabs - emits the tab settings for post-grohtml + */ + +void environment::add_html_tag_tabs(int force) +{ + 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 + */ + if (curdiv == topdiv && topdiv->before_first_page) + topdiv->begin_page(); + macro *m = new macro; + hunits d, l; + enum tab_type t; + m->append_str("html-tag:.ta "); + do { + t = curenv->tabs.distance_to_next_tab(l, &d); + l += d; + switch (t) { + case TAB_LEFT: + m->append_str(" L "); + m->append_int(l.to_units()); + break; + case TAB_CENTER: + m->append_str(" C "); + m->append_int(l.to_units()); + break; + case TAB_RIGHT: + m->append_str(" R "); + m->append_int(l.to_units()); + break; + case TAB_NONE: + break; + } + } while ((t != TAB_NONE) && (l < get_line_length())); + 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) +{ + if (curdiv == topdiv && topdiv->before_first_page) { + topdiv->begin_page(); + return; + } + if (current_tab) + wrap_up_tab(); + if (line) { + // this is so that hyphenation works + line = new space_node(H0, get_fill_color(), line); + space_total++; + possibly_break_line(0, spread); + } + while (line != 0 && line->discardable()) { + width_total -= line->width(); + space_total -= line->nspaces(); + node *tem = line; + line = line->next; + delete tem; + } + discarding = 0; + input_line_start = H0; + if (line != 0) { + if (fill) { + switch (adjust_mode) { + case ADJUST_CENTER: + saved_indent += (target_text_length - width_total)/2; + break; + case ADJUST_RIGHT: + saved_indent += target_text_length - width_total; + break; + } + } + node *tem = line; + line = 0; + output_line(tem, width_total); + hyphen_line_count = 0; + } + prev_line_interrupted = 0; +#ifdef WIDOW_CONTROL + mark_last_line(); + output_pending_lines(); +#endif /* WIDOW_CONTROL */ +} + +int environment::is_empty() +{ + return !current_tab && line == 0 && pending_lines == 0; +} + +void do_break_request(int spread) +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) { + curenv->do_break(spread); + curenv->add_html_tag(0, ".br"); + } + tok.next(); +} + +void break_request() +{ + do_break_request(0); +} + +void break_spread_request() +{ + do_break_request(1); +} + +void title() +{ + if (curdiv == topdiv && topdiv->before_first_page) { + handle_initial_title(); + return; + } + node *part[3]; + hunits part_width[3]; + part[0] = part[1] = part[2] = 0; + environment env(curenv); + environment *oldenv = curenv; + curenv = &env; + read_title_parts(part, part_width); + curenv = oldenv; + curenv->size = env.size; + curenv->prev_size = env.prev_size; + curenv->requested_size = env.requested_size; + curenv->prev_requested_size = env.prev_requested_size; + curenv->char_height = env.char_height; + 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) { + node *tem = p; + p = p->next; + tem->next = n; + n = tem; + } + 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], curenv->get_fill_color(), n); + p = part[1]; + while (p != 0) { + node *tem = p; + p = p->next; + tem->next = n; + n = tem; + } + n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n); + p = part[0]; + while (p != 0) { + node *tem = p; + p = p->next; + tem->next = n; + n = tem; + } + curenv->output_title(n, !curenv->fill, curenv->vertical_spacing, + curenv->total_post_vertical_spacing(), title_length); + curenv->hyphen_line_count = 0; + tok.next(); +} + +void adjust() +{ + curenv->adjust_mode |= 1; + if (has_arg()) { + switch (tok.ch()) { + case 'l': + curenv->adjust_mode = ADJUST_LEFT; + break; + case 'r': + curenv->adjust_mode = ADJUST_RIGHT; + break; + case 'c': + curenv->adjust_mode = ADJUST_CENTER; + break; + case 'b': + case 'n': + curenv->adjust_mode = ADJUST_BOTH; + break; + default: + int n; + if (get_integer(&n)) { + if (n < 0) + warning(WARN_RANGE, "negative adjustment mode"); + else if (n > 5) { + curenv->adjust_mode = 5; + warning(WARN_RANGE, "adjustment mode `%1' out of range", n); + } + else + curenv->adjust_mode = n; + } + } + } + skip_line(); +} + +void no_adjust() +{ + curenv->adjust_mode &= ~1; + skip_line(); +} + +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) + warning(WARN_RANGE, + "number of lines for input trap must be greater than zero"); + else { + symbol s = get_name(1); + if (!s.is_null()) { + curenv->input_trap_count = n; + curenv->input_trap = s; + } + } + } + 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 +const char TAB_REPEAT_CHAR = 'T'; + +struct tab { + tab *next; + hunits pos; + tab_type type; + tab(hunits, tab_type); + enum { BLOCK = 1024 }; + static tab *free_list; + void *operator new(size_t); + void operator delete(void *); +}; + +tab *tab::free_list = 0; + +void *tab::operator new(size_t n) +{ + assert(n == sizeof(tab)); + if (!free_list) { + free_list = (tab *)new char[sizeof(tab)*BLOCK]; + for (int i = 0; i < BLOCK - 1; i++) + free_list[i].next = free_list + i + 1; + free_list[BLOCK-1].next = 0; + } + tab *p = free_list; + free_list = (tab *)(free_list->next); + p->next = 0; + return p; +} + +#ifdef __GNUG__ +/* cfront can't cope with this. */ +inline +#endif +void tab::operator delete(void *p) +{ + if (p) { + ((tab *)p)->next = free_list; + free_list = (tab *)p; + } +} + +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) +{ + repeated_list = new tab(distance, type); +} + +tab_stops::~tab_stops() +{ + clear(); +} + +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) + return TAB_NONE; + hunits base = lastpos; + for (;;) { + for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next) + lastpos = tem->pos; + if (tem) { + *distance = tem->pos + base - curpos; + *nextpos = tem->pos + base; + return tem->type; + } + assert(lastpos > 0); + base += lastpos; + } + return TAB_NONE; +} + +const char *tab_stops::to_string() +{ + static char *buf = 0; + static int buf_size = 0; + // figure out a maximum on the amount of space we can need + int count = 0; + tab *p; + for (p = initial_list; p; p = p->next) + ++count; + for (p = repeated_list; p; p = p->next) + ++count; + // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0' + int need = count*12 + 3; + if (buf == 0 || need > buf_size) { + if (buf) + a_delete buf; + buf_size = need; + buf = new char[buf_size]; + } + char *ptr = buf; + for (p = initial_list; p; p = p->next) { + strcpy(ptr, i_to_a(p->pos.to_units())); + ptr = strchr(ptr, '\0'); + *ptr++ = 'u'; + *ptr = '\0'; + switch (p->type) { + case TAB_LEFT: + break; + case TAB_RIGHT: + *ptr++ = 'R'; + break; + case TAB_CENTER: + *ptr++ = 'C'; + break; + case TAB_NONE: + default: + assert(0); + } + } + if (repeated_list) + *ptr++ = TAB_REPEAT_CHAR; + for (p = repeated_list; p; p = p->next) { + strcpy(ptr, i_to_a(p->pos.to_units())); + ptr = strchr(ptr, '\0'); + *ptr++ = 'u'; + *ptr = '\0'; + switch (p->type) { + case TAB_LEFT: + break; + case TAB_RIGHT: + *ptr++ = 'R'; + break; + case TAB_CENTER: + *ptr++ = 'C'; + break; + case TAB_NONE: + default: + assert(0); + } + } + *ptr++ = '\0'; + return buf; +} + +tab_stops::tab_stops() : initial_list(0), repeated_list(0) +{ +} + +tab_stops::tab_stops(const tab_stops &ts) +: initial_list(0), repeated_list(0) +{ + tab **p = &initial_list; + tab *t = ts.initial_list; + while (t) { + *p = new tab(t->pos, t->type); + t = t->next; + p = &(*p)->next; + } + p = &repeated_list; + t = ts.repeated_list; + while (t) { + *p = new tab(t->pos, t->type); + t = t->next; + p = &(*p)->next; + } +} + +void tab_stops::clear() +{ + while (initial_list) { + tab *tem = initial_list; + initial_list = initial_list->next; + delete tem; + } + while (repeated_list) { + tab *tem = repeated_list; + repeated_list = repeated_list->next; + delete tem; + } +} + +void tab_stops::add_tab(hunits pos, tab_type type, int repeated) +{ + tab **p; + for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next) + ; + *p = new tab(pos, type); +} + + +void tab_stops::operator=(const tab_stops &ts) +{ + clear(); + tab **p = &initial_list; + tab *t = ts.initial_list; + while (t) { + *p = new tab(t->pos, t->type); + t = t->next; + p = &(*p)->next; + } + p = &repeated_list; + t = ts.repeated_list; + while (t) { + *p = new tab(t->pos, t->type); + t = t->next; + p = &(*p)->next; + } +} + +void set_tabs() +{ + hunits pos; + hunits prev_pos = 0; + int first = 1; + int repeated = 0; + tab_stops tabs; + while (has_arg()) { + if (tok.ch() == TAB_REPEAT_CHAR) { + tok.next(); + repeated = 1; + prev_pos = 0; + } + if (!get_hunits(&pos, 'm', prev_pos)) + break; + tab_type type = TAB_LEFT; + if (tok.ch() == 'C') { + tok.next(); + type = TAB_CENTER; + } + else if (tok.ch() == 'R') { + tok.next(); + type = TAB_RIGHT; + } + else if (tok.ch() == 'L') { + tok.next(); + } + if (pos <= prev_pos && !first) + warning(WARN_RANGE, + "positions of tab stops must be strictly increasing"); + else { + tabs.add_tab(pos, type, repeated); + prev_pos = pos; + first = 0; + } + } + curenv->tabs = tabs; + curenv->add_html_tag_tabs(1); + skip_line(); +} + +const char *environment::get_tabs() +{ + return tabs.to_string(); +} + +tab_type environment::distance_to_next_tab(hunits *distance) +{ + return line_tabs + ? curenv->tabs.distance_to_next_tab(get_text_length(), 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(); + if (field_delimiter_char) + padding_indicator_char = get_optional_char(); + else + padding_indicator_char = 0; + skip_line(); +} + +void line_tabs_request() +{ + int n; + if (has_arg() && get_integer(&n)) + curenv->line_tabs = n != 0; + else + curenv->line_tabs = 1; + skip_line(); +} + +int environment::get_line_tabs() +{ + return line_tabs; +} + +void environment::wrap_up_tab() +{ + if (!current_tab) + return; + if (line == 0) + start_line(); + hunits tab_amount; + switch (current_tab) { + case TAB_RIGHT: + tab_amount = tab_distance - tab_width; + line = make_tab_node(tab_amount, line); + break; + case TAB_CENTER: + tab_amount = tab_distance - tab_width/2; + line = make_tab_node(tab_amount, line); + break; + case TAB_NONE: + case TAB_LEFT: + default: + assert(0); + } + width_total += tab_amount; + width_total += tab_width; + if (current_field) { + if (tab_precedes_field) { + pre_field_width += tab_amount; + tab_precedes_field = 0; + } + field_distance -= tab_amount; + field_spaces += tab_field_spaces; + } + if (tab_contents != 0) { + node *tem; + for (tem = tab_contents; tem->next != 0; tem = tem->next) + ; + tem->next = line; + line = tab_contents; + } + tab_field_spaces = 0; + tab_contents = 0; + tab_width = H0; + tab_distance = H0; + current_tab = TAB_NONE; +} + +node *environment::make_tab_node(hunits d, node *next) +{ + if (leader_node != 0 && d < 0) { + error("motion generated by leader cannot be negative"); + delete leader_node; + leader_node = 0; + } + if (!leader_node) + 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; +} + +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, &abs); + switch (t) { + case TAB_NONE: + return; + case 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_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() +{ + assert(!current_field); + hunits d; + if (distance_to_next_tab(&d) != TAB_NONE) { + pre_field_width = get_text_length(); + field_distance = d; + current_field = 1; + field_spaces = 0; + tab_field_spaces = 0; + for (node *p = line; p; p = p->next) + if (p->nspaces()) { + p->freeze_space(); + space_total--; + } + tab_precedes_field = current_tab != TAB_NONE; + } + else + error("zero field width"); +} + +void environment::wrap_up_field() +{ + if (!current_tab && field_spaces == 0) + add_padding(); + hunits padding = field_distance - (get_text_length() - pre_field_width); + if (current_tab && tab_field_spaces != 0) { + hunits tab_padding = scale(padding, + tab_field_spaces, + field_spaces + tab_field_spaces); + padding -= tab_padding; + distribute_space(tab_contents, tab_field_spaces, tab_padding, 1); + tab_field_spaces = 0; + tab_width += tab_padding; + } + if (field_spaces != 0) { + distribute_space(line, field_spaces, padding, 1); + width_total += padding; + if (current_tab) { + // the start of the tab has been moved to the right by padding, so + tab_distance -= padding; + if (tab_distance <= H0) { + // use the next tab stop instead + current_tab = tabs.distance_to_next_tab(get_input_line_position() + - tab_width, + &tab_distance); + if (current_tab == TAB_NONE || current_tab == TAB_LEFT) { + width_total += tab_width; + if (current_tab == TAB_LEFT) { + line = make_tab_node(tab_distance, line); + width_total += tab_distance; + current_tab = TAB_NONE; + } + if (tab_contents != 0) { + node *tem; + for (tem = tab_contents; tem->next != 0; tem = tem->next) + ; + tem->next = line; + line = tab_contents; + tab_contents = 0; + } + tab_width = H0; + tab_distance = H0; + } + } + } + } + current_field = 0; +} + +void environment::add_padding() +{ + if (current_tab) { + 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, get_fill_color(), line); + field_spaces++; + } +} + +typedef int (environment::*INT_FUNCP)(); +typedef vunits (environment::*VUNITS_FUNCP)(); +typedef hunits (environment::*HUNITS_FUNCP)(); +typedef const char *(environment::*STRING_FUNCP)(); + +class int_env_reg : public reg { + INT_FUNCP func; + public: + int_env_reg(INT_FUNCP); + const char *get_string(); + int get_value(units *val); +}; + +class vunits_env_reg : public reg { + VUNITS_FUNCP func; + public: + vunits_env_reg(VUNITS_FUNCP f); + const char *get_string(); + int get_value(units *val); +}; + + +class hunits_env_reg : public reg { + HUNITS_FUNCP func; + public: + hunits_env_reg(HUNITS_FUNCP f); + const char *get_string(); + int get_value(units *val); +}; + +class string_env_reg : public reg { + STRING_FUNCP func; +public: + string_env_reg(STRING_FUNCP); + const char *get_string(); +}; + +int_env_reg::int_env_reg(INT_FUNCP f) : func(f) +{ +} + +int int_env_reg::get_value(units *val) +{ + *val = (curenv->*func)(); + return 1; +} + +const char *int_env_reg::get_string() +{ + return i_to_a((curenv->*func)()); +} + +vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f) +{ +} + +int vunits_env_reg::get_value(units *val) +{ + *val = (curenv->*func)().to_units(); + return 1; +} + +const char *vunits_env_reg::get_string() +{ + return i_to_a((curenv->*func)().to_units()); +} + +hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f) +{ +} + +int hunits_env_reg::get_value(units *val) +{ + *val = (curenv->*func)().to_units(); + return 1; +} + +const char *hunits_env_reg::get_string() +{ + return i_to_a((curenv->*func)().to_units()); +} + +string_env_reg::string_env_reg(STRING_FUNCP f) : func(f) +{ +} + +const char *string_env_reg::get_string() +{ + return (curenv->*func)(); +} + +class horizontal_place_reg : public general_reg { +public: + horizontal_place_reg(); + int get_value(units *); + void set_value(units); +}; + +horizontal_place_reg::horizontal_place_reg() +{ +} + +int horizontal_place_reg::get_value(units *res) +{ + *res = curenv->get_input_line_position().to_units(); + return 1; +} + +void horizontal_place_reg::set_value(units n) +{ + curenv->set_input_line_position(hunits(n)); +} + +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(); +} + +// Convert a quantity in scaled points to ascii decimal fraction. + +const char *sptoa(int sp) +{ + assert(sp > 0); + assert(sizescale > 0); + if (sizescale == 1) + return i_to_a(sp); + if (sp % sizescale == 0) + return i_to_a(sp/sizescale); + // See if 1/sizescale is exactly representable as a decimal fraction, + // ie its only prime factors are 2 and 5. + int n = sizescale; + int power2 = 0; + while ((n & 1) == 0) { + n >>= 1; + power2++; + } + int power5 = 0; + while ((n % 5) == 0) { + n /= 5; + power5++; + } + if (n == 1) { + int decimal_point = power5 > power2 ? power5 : power2; + if (decimal_point <= 10) { + int factor = 1; + int t; + for (t = decimal_point - power2; --t >= 0;) + factor *= 2; + for (t = decimal_point - power5; --t >= 0;) + factor *= 5; + if (factor == 1 || sp <= INT_MAX/factor) + return if_to_a(sp*factor, decimal_point); + } + } + double s = double(sp)/double(sizescale); + double factor = 10.0; + double val = s; + int decimal_point = 0; + do { + double v = ceil(s*factor); + if (v > INT_MAX) + break; + val = v; + factor *= 10.0; + } while (++decimal_point < 10); + return if_to_a(int(val), decimal_point); +} + +const char *environment::get_point_size_string() +{ + return sptoa(curenv->get_point_size()); +} + +const char *environment::get_requested_point_size_string() +{ + return sptoa(curenv->get_requested_point_size()); +} + +#define init_int_env_reg(name, func) \ + number_reg_dictionary.define(name, new int_env_reg(&environment::func)) + +#define init_vunits_env_reg(name, func) \ + number_reg_dictionary.define(name, new vunits_env_reg(&environment::func)) + +#define init_hunits_env_reg(name, func) \ + number_reg_dictionary.define(name, new hunits_env_reg(&environment::func)) + +#define init_string_env_reg(name, func) \ + number_reg_dictionary.define(name, new string_env_reg(&environment::func)) + +void init_env_requests() +{ + init_request("ad", adjust); + init_request("br", break_request); + init_request("brp", break_spread_request); + init_request("c2", no_break_control_char); + init_request("cc", control_char); + init_request("ce", center); + init_request("cu", continuous_underline); + init_request("ev", environment_switch); + init_request("evc", environment_copy); + init_request("fam", family_change); + init_request("fc", field_characters); + init_request("fi", fill); + init_request("ft", font_change); + init_request("hc", hyphen_char); + init_request("hlm", hyphen_line_max_request); + init_request("hy", hyphenate_request); + init_request("hym", hyphenation_margin_request); + init_request("hys", hyphenation_space_request); + init_request("in", indent); + init_request("it", input_trap); + init_request("itc", input_trap_continued); + init_request("lc", leader_character); + init_request("linetabs", line_tabs_request); + init_request("ll", line_length); + init_request("ls", line_spacing); + init_request("lt", title_length); + init_request("mc", margin_character); + init_request("na", no_adjust); + init_request("nf", no_fill); + init_request("nh", no_hyphenate); + init_request("nm", number_lines); + init_request("nn", no_number); + init_request("ps", point_size); + init_request("pvs", post_vertical_spacing); + init_request("rj", right_justify); + init_request("sizes", override_sizes); + init_request("ss", space_size); + init_request("ta", set_tabs); + init_request("ti", temporary_indent); + init_request("tc", tab_character); + init_request("tl", title); + init_request("ul", underline); + init_request("vs", vertical_spacing); +#ifdef WIDOW_CONTROL + init_request("wdc", widow_control_request); +#endif /* WIDOW_CONTROL */ + init_int_env_reg(".b", get_bold); + init_vunits_env_reg(".cdp", get_prev_char_depth); + init_int_env_reg(".ce", get_center_lines); + init_vunits_env_reg(".cht", get_prev_char_height); + init_hunits_env_reg(".csk", get_prev_char_skew); + init_string_env_reg(".ev", get_name_string); + init_int_env_reg(".f", get_font); + init_string_env_reg(".fam", get_font_family_string); + init_string_env_reg(".fn", get_font_name_string); + init_int_env_reg(".height", get_char_height); + init_int_env_reg(".hlc", get_hyphen_line_count); + init_int_env_reg(".hlm", get_hyphen_line_max); + init_int_env_reg(".hy", get_hyphenation_flags); + init_hunits_env_reg(".hym", get_hyphenation_margin); + init_hunits_env_reg(".hys", get_hyphenation_space); + init_hunits_env_reg(".i", get_indent); + init_hunits_env_reg(".in", get_saved_indent); + init_int_env_reg(".int", get_prev_line_interrupted); + init_int_env_reg(".linetabs", get_line_tabs); + init_hunits_env_reg(".lt", get_title_length); + init_int_env_reg(".j", get_adjust_mode); + init_hunits_env_reg(".k", get_text_length); + init_int_env_reg(".L", get_line_spacing); + init_hunits_env_reg(".l", get_line_length); + init_hunits_env_reg(".ll", get_saved_line_length); + init_hunits_env_reg(".n", get_prev_text_length); + init_int_env_reg(".ps", get_point_size); + init_int_env_reg(".psr", get_requested_point_size); + init_vunits_env_reg(".pvs", get_post_vertical_spacing); + init_int_env_reg(".rj", get_right_justify_lines); + init_string_env_reg(".s", get_point_size_string); + init_int_env_reg(".slant", get_char_slant); + init_int_env_reg(".ss", get_space_size); + init_int_env_reg(".sss", get_sentence_space_size); + init_string_env_reg(".sr", get_requested_point_size_string); + init_string_env_reg(".tabs", get_tabs); + init_int_env_reg(".u", get_fill); + init_vunits_env_reg(".v", get_vertical_spacing); + init_hunits_env_reg(".w", get_prev_char_width); + number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents)); + number_reg_dictionary.define("hp", new horizontal_place_reg); + number_reg_dictionary.define("ln", new variable_reg(&next_line_number)); + number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents)); + number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents)); + number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents)); + number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents)); + number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents)); + number_reg_dictionary.define("st", new variable_reg(&st_reg_contents)); +} + +// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation. + +struct trie_node; + +class trie { + trie_node *tp; + virtual void do_match(int len, void *val) = 0; + virtual void do_delete(void *) = 0; + void delete_trie_node(trie_node *); +public: + trie() : tp(0) {} + virtual ~trie(); // virtual to shut up g++ + void insert(const char *, int, void *); + // find calls do_match for each match it finds + void find(const char *pat, int patlen); + void clear(); +}; + +class hyphen_trie : private trie { + int *h; + 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, int append, dictionary *ex); +}; + +struct hyphenation_language { + symbol name; + dictionary exceptions; + hyphen_trie patterns; + hyphenation_language(symbol nm) : name(nm), exceptions(501) {} + ~hyphenation_language() { } +}; + +dictionary language_dictionary(5); +hyphenation_language *current_language = 0; + +static void set_hyphenation_language() +{ + symbol nm = get_name(1); + if (!nm.is_null()) { + current_language = (hyphenation_language *)language_dictionary.lookup(nm); + if (!current_language) { + current_language = new hyphenation_language(nm); + (void)language_dictionary.lookup(nm, (void *)current_language); + } + } + skip_line(); +} + +const int WORD_MAX = 256; // we use unsigned char for offsets in + // hyphenation exceptions + +static void hyphen_word() +{ + if (!current_language) { + error("no current hyphenation language"); + skip_line(); + return; + } + char buf[WORD_MAX + 1]; + unsigned char pos[WORD_MAX + 2]; + for (;;) { + tok.skip(); + if (tok.newline() || tok.eof()) + break; + int i = 0; + int npos = 0; + while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) { + charinfo *ci = tok.get_char(1); + if (ci == 0) { + skip_line(); + return; + } + tok.next(); + if (ci->get_ascii_code() == '-') { + if (i > 0 && (npos == 0 || pos[npos - 1] != i)) + pos[npos++] = i; + } + else { + int c = ci->get_hyphenation_code(); + if (c == 0) + break; + buf[i++] = 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 *)current_language->exceptions.lookup(symbol(buf), + tem); + if (tem) + a_delete tem; + } + } + skip_line(); +} + +struct trie_node { + char c; + trie_node *down; + trie_node *right; + void *val; + trie_node(char, trie_node *); +}; + +trie_node::trie_node(char ch, trie_node *p) +: c(ch), down(0), right(p), val(0) +{ +} + +trie::~trie() +{ + clear(); +} + +void trie::clear() +{ + delete_trie_node(tp); + tp = 0; +} + + +void trie::delete_trie_node(trie_node *p) +{ + if (p) { + delete_trie_node(p->down); + delete_trie_node(p->right); + if (p->val) + do_delete(p->val); + delete p; + } +} + +void trie::insert(const char *pat, int patlen, void *val) +{ + trie_node **p = &tp; + assert(patlen > 0 && pat != 0); + for (;;) { + while (*p != 0 && (*p)->c < pat[0]) + p = &((*p)->right); + if (*p == 0 || (*p)->c != pat[0]) + *p = new trie_node(pat[0], *p); + if (--patlen == 0) { + (*p)->val = val; + break; + } + ++pat; + p = &((*p)->down); + } +} + +void trie::find(const char *pat, int patlen) +{ + trie_node *p = tp; + for (int i = 0; p != 0 && i < patlen; i++) { + while (p != 0 && p->c < pat[i]) + p = p->right; + if (p != 0 && p->c == pat[i]) { + if (p->val != 0) + do_match(i+1, p->val); + p = p->down; + } + else + break; + } +} + +struct operation { + operation *next; + short distance; + short num; + operation(int, int, operation *); +}; + +operation::operation(int i, int j, operation *op) +: next(op), distance(j), num(i) +{ +} + +void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num) +{ + operation *op = 0; + for (int i = 0; i < patlen+1; i++) + if (num[i] != 0) + op = new operation(num[i], patlen - i, op); + 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; + for (j = 0; j < len + 1; j++) + hyphens[j] = 0; + for (j = 0; j < len - 1; j++) { + h = hyphens + j; + find(word + j, len - j); + } +} + +inline int max(int m, int n) +{ + return m > n ? m : n; +} + +void hyphen_trie::do_match(int i, void *v) +{ + operation *op = (operation *)v; + while (op != 0) { + h[i - op->distance] = max(h[i - op->distance], op->num); + op = op->next; + } +} + +void hyphen_trie::do_delete(void *v) +{ + operation *op = (operation *)v; + while (op) { + operation *tem = op; + op = tem->next; + delete tem; + } +} + +/* 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) +{ + if (!append) + clear(); + char buf[WORD_MAX]; + for (int i = 0; i < WORD_MAX; i++) + buf[i] = 0; + int num[WORD_MAX+1]; + errno = 0; + char *path = 0; + FILE *fp = mac_path->open_file(name, &path); + if (fp == 0) { + error("can't find hyphenation patterns file `%1'", name); + return; + } + 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 == '%') { // skip comments + do { + c = getc(fp); + } while (c != EOF && c != '\n'); + } + if (c == EOF || !csspace(c)) + break; + 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; + } + } + int i = 0; + num[0] = 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); + } + 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[(unsigned char)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; + return; +} + +void hyphenate(hyphen_list *h, unsigned flags) +{ + if (!current_language) + return; + while (h) { + while (h && h->hyphenation_code == 0) + h = h->next; + int len = 0; + char hbuf[WORD_MAX+2]; + char *buf = hbuf + 1; + hyphen_list *tem; + for (tem = h; tem && len < WORD_MAX; tem = tem->next) { + if (tem->hyphenation_code != 0) + buf[len++] = tem->hyphenation_code; + else + break; + } + hyphen_list *nexth = tem; + if (len > 2) { + buf[len] = 0; + unsigned char *pos + = (unsigned char *)current_language->exceptions.lookup(buf); + if (pos != 0) { + int j = 0; + int i = 1; + for (tem = h; tem != 0; tem = tem->next, i++) + if (pos[j] == i) { + tem->hyphen = 1; + j++; + } + } + else { + hbuf[0] = hbuf[len+1] = '.'; + int num[WORD_MAX+3]; + current_language->patterns.hyphenate(hbuf, len+2, num); + int i; + num[2] = 0; + if (flags & 8) + num[3] = 0; + if (flags & 4) + --len; + for (i = 2, tem = h; i < len && tem; tem = tem->next, i++) + if (num[i] & 1) + tem->hyphen = 1; + } + } + h = nexth; + } +} + +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(), append, + ¤t_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(); +}; + +const char *hyphenation_language_reg::get_string() +{ + return current_language ? current_language->name.contents() : ""; +} + +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/glyphuni.cpp b/contrib/groff/src/roff/troff/glyphuni.cpp new file mode 100644 index 0000000..7e242ce --- /dev/null +++ b/contrib/groff/src/roff/troff/glyphuni.cpp @@ -0,0 +1,503 @@ +// -*- C++ -*- +/* Copyright (C) 2002, 2003 + Free Software Foundation, Inc. + Written by Werner Lemberg + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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 "lib.h" +#include "stringclass.h" +#include "ptable.h" + +#include "unicode.h" + +struct glyph_to_unicode { + char *value; +}; + +declare_ptable(glyph_to_unicode) +implement_ptable(glyph_to_unicode) + +PTABLE(glyph_to_unicode) glyph_to_unicode_table; + +struct { + const char *key; + const char *value; +} glyph_to_unicode_list[] = { + { "!", "0021" }, + { "\"", "0022" }, + { "dq", "0022" }, + { "#", "0023" }, + { "sh", "0023" }, + { "$", "0024" }, + { "Do", "0024" }, + { "%", "0025" }, + { "&", "0026" }, + { "aq", "0027" }, + { "(", "0028" }, + { ")", "0029" }, + { "*", "002A" }, + { "+", "002B" }, + { "pl", "002B" }, + { ",", "002C" }, + { ".", "002E" }, + { "/", "002F" }, + { "sl", "002F" }, + { "0", "0030" }, + { "1", "0031" }, + { "2", "0032" }, + { "3", "0033" }, + { "4", "0034" }, + { "5", "0035" }, + { "6", "0036" }, + { "7", "0037" }, + { "8", "0038" }, + { "9", "0039" }, + { ":", "003A" }, + { ";", "003B" }, + { "<", "003C" }, + { "=", "003D" }, + { "eq", "003D" }, + { ">", "003E" }, + { "?", "003F" }, + { "@", "0040" }, + { "at", "0040" }, + { "A", "0041" }, + { "B", "0042" }, + { "C", "0043" }, + { "D", "0044" }, + { "E", "0045" }, + { "F", "0046" }, + { "G", "0047" }, + { "H", "0048" }, + { "I", "0049" }, + { "J", "004A" }, + { "K", "004B" }, + { "L", "004C" }, + { "M", "004D" }, + { "N", "004E" }, + { "O", "004F" }, + { "P", "0050" }, + { "Q", "0051" }, + { "R", "0052" }, + { "S", "0053" }, + { "T", "0054" }, + { "U", "0055" }, + { "V", "0056" }, + { "W", "0057" }, + { "X", "0058" }, + { "Y", "0059" }, + { "Z", "005A" }, +//{ "[", "005B" }, + { "lB", "005B" }, +//{ "\\", "005C" }, + { "rs", "005C" }, +//{ "]", "005D" }, + { "rB", "005D" }, + { "a^", "005E" }, + { "^", "005E" }, + { "ha", "005E" }, + { "_", "005F" }, + { "ru", "005F" }, + { "ul", "005F" }, +//{ "\\`", "0060" }, + { "ga", "0060" }, + { "a", "0061" }, + { "b", "0062" }, + { "c", "0063" }, + { "d", "0064" }, + { "e", "0065" }, + { "f", "0066" }, + { "ff", "0066_0066" }, + { "Fi", "0066_0066_0069" }, + { "Fl", "0066_0066_006C" }, + { "fi", "0066_0069" }, + { "fl", "0066_006C" }, + { "g", "0067" }, + { "h", "0068" }, + { "i", "0069" }, + { "j", "006A" }, + { "k", "006B" }, + { "l", "006C" }, + { "m", "006D" }, + { "n", "006E" }, + { "o", "006F" }, + { "p", "0070" }, + { "q", "0071" }, + { "r", "0072" }, + { "s", "0073" }, + { "t", "0074" }, + { "u", "0075" }, + { "v", "0076" }, + { "w", "0077" }, + { "x", "0078" }, + { "y", "0079" }, + { "z", "007A" }, + { "lC", "007B" }, + { "{", "007B" }, + { "ba", "007C" }, + { "or", "007C" }, + { "|", "007C" }, + { "rC", "007D" }, + { "}", "007D" }, + { "a~", "007E" }, + { "~", "007E" }, + { "ti", "007E" }, + { "r!", "00A1" }, + { "ct", "00A2" }, + { "Po", "00A3" }, + { "Cs", "00A4" }, + { "Ye", "00A5" }, + { "bb", "00A6" }, + { "sc", "00A7" }, + { "ad", "00A8" }, + { "co", "00A9" }, + { "Of", "00AA" }, + { "Fo", "00AB" }, + { "no", "00AC" }, + { "tno", "00AC" }, + { "shc", "00AD" }, + { "rg", "00AE" }, + { "a-", "00AF" }, + { "de", "00B0" }, + { "+-", "00B1" }, + { "t+-", "00B1" }, + { "S2", "00B2" }, + { "S3", "00B3" }, + { "aa", "00B4" }, +//{ "\\'", "00B4" }, + { "mc", "00B5" }, + { "ps", "00B6" }, + { "pc", "00B7" }, + { "ac", "00B8" }, + { "S1", "00B9" }, + { "Om", "00BA" }, + { "Fc", "00BB" }, + { "14", "00BC" }, + { "12", "00BD" }, + { "34", "00BE" }, + { "r?", "00BF" }, + { "`A", "00C0" }, + { "'A", "00C1" }, + { "^A", "00C2" }, + { "~A", "00C3" }, + { ":A", "00C4" }, + { "oA", "00C5" }, + { "AE", "00C6" }, + { ",C", "00C7" }, + { "`E", "00C8" }, + { "'E", "00C9" }, + { "^E", "00CA" }, + { ":E", "00CB" }, + { "`I", "00CC" }, + { "'I", "00CD" }, + { "^I", "00CE" }, + { ":I", "00CF" }, + { "-D", "00D0" }, + { "~N", "00D1" }, + { "`O", "00D2" }, + { "'O", "00D3" }, + { "^O", "00D4" }, + { "~O", "00D5" }, + { ":O", "00D6" }, + { "mu", "00D7" }, + { "tmu", "00D7" }, + { "/O", "00D8" }, + { "`U", "00D9" }, + { "'U", "00DA" }, + { "^U", "00DB" }, + { ":U", "00DC" }, + { "'Y", "00DD" }, + { "TP", "00DE" }, + { "ss", "00DF" }, + { "`a", "00E0" }, + { "'a", "00E1" }, + { "^a", "00E2" }, + { "~a", "00E3" }, + { ":a", "00E4" }, + { "oa", "00E5" }, + { "ae", "00E6" }, + { ",c", "00E7" }, + { "`e", "00E8" }, + { "'e", "00E9" }, + { "^e", "00EA" }, + { ":e", "00EB" }, + { "`i", "00EC" }, + { "'i", "00ED" }, + { "^i", "00EE" }, + { ":i", "00EF" }, + { "Sd", "00F0" }, + { "~n", "00F1" }, + { "`o", "00F2" }, + { "'o", "00F3" }, + { "^o", "00F4" }, + { "~o", "00F5" }, + { ":o", "00F6" }, + { "di", "00F7" }, + { "tdi", "00F7" }, + { "/o", "00F8" }, + { "`u", "00F9" }, + { "'u", "00FA" }, + { "^u", "00FB" }, + { ":u", "00FC" }, + { "'y", "00FD" }, + { "Tp", "00FE" }, + { ":y", "00FF" }, + { "'C", "0106" }, + { "'c", "0107" }, + { ".i", "0131" }, + { "IJ", "0132" }, + { "ij", "0133" }, + { "/L", "0141" }, + { "/l", "0142" }, + { "OE", "0152" }, + { "oe", "0153" }, + { "vS", "0160" }, + { "vs", "0161" }, + { ":Y", "0178" }, + { "vZ", "017D" }, + { "vz", "017E" }, + { "Fn", "0192" }, + { "ah", "02C7" }, + { "ab", "02D8" }, + { "a.", "02D9" }, + { "ao", "02DA" }, + { "ho", "02DB" }, + { "a\"", "02DD" }, + { "*A", "0391" }, + { "*B", "0392" }, + { "*G", "0393" }, + { "*D", "0394" }, + { "*E", "0395" }, + { "*Z", "0396" }, + { "*Y", "0397" }, + { "*H", "0398" }, + { "*I", "0399" }, + { "*K", "039A" }, + { "*L", "039B" }, + { "*M", "039C" }, + { "*N", "039D" }, + { "*C", "039E" }, + { "*O", "039F" }, + { "*P", "03A0" }, + { "*R", "03A1" }, + { "*S", "03A3" }, + { "*T", "03A4" }, + { "*U", "03A5" }, + { "*F", "03A6" }, + { "*X", "03A7" }, + { "*Q", "03A8" }, + { "*W", "03A9" }, + { "*a", "03B1" }, + { "*b", "03B2" }, + { "*g", "03B3" }, + { "*d", "03B4" }, + { "*e", "03B5" }, + { "*z", "03B6" }, + { "*y", "03B7" }, + { "*h", "03B8" }, + { "*i", "03B9" }, + { "*k", "03BA" }, + { "*l", "03BB" }, + { "*m", "03BC" }, + { "*n", "03BD" }, + { "*c", "03BE" }, + { "*o", "03BF" }, + { "*p", "03C0" }, + { "*r", "03C1" }, + { "ts", "03C2" }, + { "*s", "03C3" }, + { "*t", "03C4" }, + { "*u", "03C5" }, + { "*f", "03C6" }, + { "*x", "03C7" }, + { "*q", "03C8" }, + { "*w", "03C9" }, + { "+h", "03D1" }, + { "+f", "03D5" }, + { "+p", "03D6" }, + { "+e", "03F5" }, + { "-", "2010" }, + { "hy", "2010" }, + { "en", "2013" }, + { "em", "2014" }, + { "`", "2018" }, + { "oq", "2018" }, + { "'", "2019" }, + { "cq", "2019" }, + { "bq", "201A" }, + { "lq", "201C" }, + { "rq", "201D" }, + { "Bq", "201E" }, + { "dg", "2020" }, + { "dd", "2021" }, + { "bu", "2022" }, + { "%0", "2030" }, + { "fm", "2032" }, + { "sd", "2033" }, + { "fo", "2039" }, + { "fc", "203A" }, + { "rn", "203E" }, + { "f/", "2044" }, + { "eu", "20AC" }, + { "Eu", "20AC" }, + { "-h", "210F" }, + { "hbar", "210F" }, + { "Im", "2111" }, + { "wp", "2118" }, + { "Re", "211C" }, + { "tm", "2122" }, + { "Ah", "2135" }, + { "18", "215B" }, + { "38", "215C" }, + { "58", "215D" }, + { "78", "215E" }, + { "<-", "2190" }, + { "ua", "2191" }, + { "->", "2192" }, + { "da", "2193" }, + { "<>", "2194" }, + { "va", "2195" }, + { "CR", "21B5" }, + { "lA", "21D0" }, + { "uA", "21D1" }, + { "rA", "21D2" }, + { "dA", "21D3" }, + { "hA", "21D4" }, + { "vA", "21D5" }, + { "fa", "2200" }, + { "pd", "2202" }, + { "te", "2203" }, + { "es", "2205" }, + { "gr", "2207" }, + { "mo", "2208" }, + { "nm", "2209" }, + { "st", "220B" }, + { "product", "220F" }, + { "sum", "2211" }, +//{ "\\-", "2212" }, + { "mi", "2212" }, + { "-+", "2213" }, + { "**", "2217" }, + { "sr", "221A" }, + { "pt", "221D" }, + { "if", "221E" }, + { "/_", "2220" }, + { "AN", "2227" }, + { "OR", "2228" }, + { "ca", "2229" }, + { "cu", "222A" }, + { "is", "222B" }, + { "integral", "222B" }, + { "tf", "2234" }, + { "3d", "2234" }, + { "ap", "223C" }, + { "|=", "2243" }, + { "=~", "2245" }, + { "~~", "2248" }, + { "~=", "2248" }, + { "!=", "2260" }, + { "==", "2261" }, + { "ne", "2262" }, + { "<=", "2264" }, + { ">=", "2265" }, + { ">>", "226A" }, + { "<<", "226B" }, + { "sb", "2282" }, + { "sp", "2283" }, + { "nb", "2284" }, + { "nc", "2285" }, + { "ib", "2286" }, + { "ip", "2287" }, + { "c+", "2295" }, + { "c*", "2297" }, + { "pp", "22A5" }, + { "md", "22C5" }, + { "lc", "2308" }, + { "rc", "2309" }, + { "lf", "230A" }, + { "rf", "230B" }, + { "parenlefttp", "239B" }, + { "parenleftex", "239C" }, + { "parenleftbt", "239D" }, + { "parenrighttp", "239E" }, + { "parenrightex", "239F" }, + { "parenrightbt", "23A0" }, + { "bracketlefttp", "23A1" }, + { "bracketleftex", "23A2" }, + { "bracketleftbt", "23A3" }, + { "bracketrighttp", "23A4" }, + { "bracketrightex", "23A5" }, + { "bracketrightbt", "23A6" }, + { "lt", "23A7" }, + { "bracelefttp", "23A7" }, + { "lk", "23A8" }, + { "braceleftmid", "23A8" }, + { "lb", "23A9" }, + { "braceleftbt", "23A9" }, + { "bv", "23AA" }, + { "braceex", "23AA" }, + { "braceleftex", "23AA" }, + { "bracerightex", "23AA" }, + { "rt", "23AB" }, + { "bracerighttp", "23AB" }, + { "rk", "23AC" }, + { "bracerightmid", "23AC" }, + { "rb", "23AD" }, + { "bracerightbt", "23AD" }, + { "an", "23AF" }, + { "br", "2502" }, + { "rk", "251D" }, + { "lk", "2525" }, + { "lt", "256D" }, + { "rt", "256E" }, + { "rb", "256F" }, + { "lb", "2570" }, + { "sq", "25A1" }, + { "lz", "25CA" }, + { "ci", "25CB" }, + { "lh", "261C" }, + { "rh", "261E" }, + { "SP", "2660" }, + { "CL", "2663" }, + { "HE", "2665" }, + { "DI", "2666" }, + { "OK", "2713" }, + { "la", "27E8" }, + { "ra", "27E9" }, +}; + +// global constructor +static struct glyph_to_unicode_init { + glyph_to_unicode_init(); +} _glyph_to_unicode_init; + +glyph_to_unicode_init::glyph_to_unicode_init() { + for (unsigned int i = 0; + i < sizeof(glyph_to_unicode_list)/sizeof(glyph_to_unicode_list[0]); + i++) { + glyph_to_unicode *gtu = new glyph_to_unicode[1]; + gtu->value = (char *)glyph_to_unicode_list[i].value; + glyph_to_unicode_table.define(glyph_to_unicode_list[i].key, gtu); + } +} + +const char *glyph_name_to_unicode(const char *s) +{ + glyph_to_unicode *result = glyph_to_unicode_table.lookup(s); + return result ? result->value : 0; +} diff --git a/contrib/groff/src/roff/troff/input.cpp b/contrib/groff/src/roff/troff/input.cpp new file mode 100644 index 0000000..55178a2 --- /dev/null +++ b/contrib/groff/src/roff/troff/input.cpp @@ -0,0 +1,7879 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" +#include "dictionary.h" +#include "hvunits.h" +#include "env.h" +#include "request.h" +#include "node.h" +#include "reg.h" +#include "token.h" +#include "div.h" +#include "charinfo.h" +#include "stringclass.h" +#include "font.h" +#include "macropath.h" +#include "defs.h" +#include "input.h" +#include "unicode.h" + +// Needed for getpid() and isatty() +#include "posix.h" + +#include "nonposix.h" + +#ifdef NEED_DECLARATION_PUTENV +extern "C" { + int putenv(const char *); +} +#endif /* NEED_DECLARATION_PUTENV */ + +#define MACRO_PREFIX "tmac." +#define MACRO_POSTFIX ".tmac" +#define INITIAL_STARTUP_FILE "troffrc" +#define FINAL_STARTUP_FILE "troffrc-end" +#define DEFAULT_INPUT_STACK_LIMIT 1000 + +#ifndef DEFAULT_WARNING_MASK +// warnings that are enabled by default +#define DEFAULT_WARNING_MASK \ + (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT) +#endif + +// initial size of buffer for reading names; expanded as necessary +#define ABUF_SIZE 16 + +extern "C" const char *Version_string; + +#ifdef COLUMN +void init_column_requests(); +#endif /* COLUMN */ + +static node *read_draw_node(); +static void read_color_draw_node(token &); +void handle_first_page_transition(); +static void push_token(const token &); +void copy_file(); +#ifdef COLUMN +void vjustify(); +#endif /* COLUMN */ +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; +static int ignoring = 0; + +static void enable_warning(const char *); +static void disable_warning(const char *); + +static int escape_char = '\\'; +static symbol end_macro_name; +static symbol blank_line_macro_name; +static int compatible_flag = 0; +int ascii_output_flag = 0; +int suppress_output_flag = 0; +int is_html = 0; +int begin_level = 0; // number of nested .begin requests + +int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M, + // \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); +static void copy_mode_error(const char *, + const errarg & = empty_errarg, + const errarg & = empty_errarg, + const errarg & = empty_errarg); + +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); + +static symbol composite_glyph_name(symbol); +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 do_get_long_name(int, char); +static int get_line_arg(units *res, int si, charinfo **cp); +static int read_size(int *); +static symbol get_delim_name(); +static void init_registers(); +static void trapping_blank_line(); + +struct input_iterator; +input_iterator *make_temp_iterator(const char *); +const char *input_char_description(int); + + +void set_escape_char() +{ + if (has_arg()) { + if (tok.ch() == 0) { + error("bad escape character"); + escape_char = '\\'; + } + else + escape_char = tok.ch(); + } + else + escape_char = '\\'; + skip_line(); +} + +void escape_off() +{ + escape_char = 0; + skip_line(); +} + +static int saved_escape_char = '\\'; + +void save_escape_char() +{ + saved_escape_char = escape_char; + skip_line(); +} + +void restore_escape_char() +{ + escape_char = saved_escape_char; + skip_line(); +} + +class input_iterator { +public: + input_iterator(); + virtual ~input_iterator() {} + int get(node **); + friend class input_stack; +protected: + const unsigned char *ptr; + const unsigned char *eptr; + input_iterator *next; +private: + virtual int fill(node **); + virtual int peek(); + virtual int has_args() { return 0; } + virtual int nargs() { 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 next_file(FILE *, const char *) { return 0; } + virtual void shift(int) {} + virtual int is_boundary() {return 0; } + virtual int internal_level() { return 0; } + virtual int is_file() { return 0; } + virtual int is_macro() { return 0; } + virtual void save_compatible_flag(int) {} + virtual int get_compatible_flag() { return 0; } +}; + +input_iterator::input_iterator() +: ptr(0), eptr(0) +{ +} + +int input_iterator::fill(node **) +{ + return EOF; +} + +int input_iterator::peek() +{ + return EOF; +} + +inline int input_iterator::get(node **p) +{ + return ptr < eptr ? *ptr++ : fill(p); +} + +class input_boundary : public input_iterator { +public: + int is_boundary() { return 1; } +}; + +class input_return_boundary : public input_iterator { +public: + int is_boundary() { return 2; } +}; + +class file_iterator : public input_iterator { + FILE *fp; + int lineno; + const char *filename; + int popened; + int newline_flag; + int seen_escape; + enum { BUF_SIZE = 512 }; + unsigned char buf[BUF_SIZE]; + void close(); +public: + file_iterator(FILE *, const char *, int = 0); + ~file_iterator(); + int fill(node **); + int peek(); + int get_location(int, const char **, int *); + void backtrace(); + int set_location(const char *, int); + int next_file(FILE *, const char *); + int is_file(); +}; + +file_iterator::file_iterator(FILE *f, const char *fn, int po) +: 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) + init_output(); + the_output->put_filename(fn); + } +} + +file_iterator::~file_iterator() +{ + close(); +} + +void file_iterator::close() +{ + if (fp == stdin) + clearerr(stdin); +#ifndef POPEN_MISSING + else if (popened) + pclose(fp); +#endif /* not POPEN_MISSING */ + else + fclose(fp); +} + +int file_iterator::is_file() +{ + return 1; +} + +int file_iterator::next_file(FILE *f, const char *s) +{ + close(); + filename = s; + fp = f; + lineno = 1; + newline_flag = 0; + seen_escape = 0; + popened = 0; + ptr = 0; + eptr = 0; + return 1; +} + +int file_iterator::fill(node **) +{ + if (newline_flag) + lineno++; + newline_flag = 0; + unsigned char *p = buf; + ptr = p; + unsigned char *e = p + BUF_SIZE; + while (p < e) { + int c = getc(fp); + if (c == EOF) + break; + 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) { + eptr = p; + return *ptr++; + } + else { + eptr = p; + return EOF; + } +} + +int file_iterator::peek() +{ + int c = getc(fp); + while (invalid_input_char(c)) { + warning(WARN_INPUT, "invalid input character code %1", int(c)); + c = getc(fp); + } + if (c != EOF) + ungetc(c, fp); + return c; +} + +int file_iterator::get_location(int /*allow_macro*/, + const char **filenamep, int *linenop) +{ + *linenop = lineno; + if (filename != 0 && strcmp(filename, "-") == 0) + *filenamep = ""; + else + *filenamep = filename; + return 1; +} + +void file_iterator::backtrace() +{ + errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno, + popened ? "process" : "file"); +} + +int file_iterator::set_location(const char *f, int ln) +{ + if (f) { + filename = f; + if (!the_output) + init_output(); + the_output->put_filename(f); + } + lineno = ln; + return 1; +} + +input_iterator nil_iterator; + +class input_stack { +public: + static int get(node **); + static int peek(); + static void push(input_iterator *); + static input_iterator *get_arg(int); + static int nargs(); + static int get_location(int, const char **, int *); + static int set_location(const char *, int); + static void backtrace(); + static void backtrace_all(); + static void next_file(FILE *, const char *); + static void end_file(); + static void shift(int n); + static void add_boundary(); + static void add_return_boundary(); + static int is_return_boundary(); + static void remove_boundary(); + static int get_level(); + static void clear(); + static void pop_macro(); + static void save_compatible_flag(int); + static int get_compatible_flag(); + + static int limit; +private: + static input_iterator *top; + static int level; + + static int finish_get(node **); + static int finish_peek(); +}; + +input_iterator *input_stack::top = &nil_iterator; +int input_stack::level = 0; +int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT; + +inline int input_stack::get_level() +{ + return level + top->internal_level(); +} + +inline int input_stack::get(node **np) +{ + int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np); + if (res == '\n') + have_input = 0; + return res; +} + +int input_stack::finish_get(node **np) +{ + for (;;) { + int c = top->fill(np); + if (c != EOF || top->is_boundary()) + return c; + if (top == &nil_iterator) + break; + input_iterator *tem = top; + top = top->next; + level--; + delete tem; + if (top->ptr < top->eptr) + return *top->ptr++; + } + assert(level == 0); + return EOF; +} + +inline int input_stack::peek() +{ + return (top->ptr < top->eptr) ? *top->ptr : finish_peek(); +} + +int input_stack::finish_peek() +{ + for (;;) { + int c = top->peek(); + if (c != EOF || top->is_boundary()) + return c; + if (top == &nil_iterator) + break; + input_iterator *tem = top; + top = top->next; + level--; + delete tem; + if (top->ptr < top->eptr) + return *top->ptr; + } + assert(level == 0); + return EOF; +} + +void input_stack::add_boundary() +{ + push(new input_boundary); +} + +void input_stack::add_return_boundary() +{ + push(new input_return_boundary); +} + +int input_stack::is_return_boundary() +{ + return top->is_boundary() == 2; +} + +void input_stack::remove_boundary() +{ + assert(top->is_boundary()); + input_iterator *temp = top->next; + delete top; + top = temp; + level--; +} + +void input_stack::push(input_iterator *in) +{ + if (in == 0) + return; + if (++level > limit && limit > 0) + fatal("input stack limit exceeded (probable infinite loop)"); + in->next = top; + top = in; +} + +input_iterator *input_stack::get_arg(int i) +{ + input_iterator *p; + for (p = top; p != 0; p = p->next) + if (p->has_args()) + return p->get_arg(i); + return 0; +} + +void input_stack::shift(int n) +{ + for (input_iterator *p = top; p; p = p->next) + if (p->has_args()) { + p->shift(n); + return; + } +} + +int input_stack::nargs() +{ + for (input_iterator *p =top; p != 0; p = p->next) + if (p->has_args()) + return p->nargs(); + return 0; +} + +int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop) +{ + for (input_iterator *p = top; p; p = p->next) + if (p->get_location(allow_macro, filenamep, linenop)) + return 1; + return 0; +} + +void input_stack::backtrace() +{ + const char *f; + int n; + // only backtrace down to (not including) the topmost file + for (input_iterator *p = top; + p && !p->get_location(0, &f, &n); + p = p->next) + p->backtrace(); +} + +void input_stack::backtrace_all() +{ + for (input_iterator *p = top; p; p = p->next) + p->backtrace(); +} + +int input_stack::set_location(const char *filename, int lineno) +{ + for (input_iterator *p = top; p; p = p->next) + if (p->set_location(filename, lineno)) + return 1; + return 0; +} + +void input_stack::next_file(FILE *fp, const char *s) +{ + input_iterator **pp; + for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) + if ((*pp)->next_file(fp, s)) + return; + if (++level > limit && limit > 0) + fatal("input stack limit exceeded"); + *pp = new file_iterator(fp, s); + (*pp)->next = &nil_iterator; +} + +void input_stack::end_file() +{ + for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next) + if ((*pp)->is_file()) { + input_iterator *tem = *pp; + *pp = (*pp)->next; + delete tem; + level--; + return; + } +} + +void input_stack::clear() +{ + int nboundaries = 0; + while (top != &nil_iterator) { + if (top->is_boundary()) + nboundaries++; + input_iterator *tem = top; + top = top->next; + level--; + delete tem; + } + // Keep while_request happy. + for (; nboundaries > 0; --nboundaries) + add_return_boundary(); +} + +void input_stack::pop_macro() +{ + int nboundaries = 0; + int is_macro = 0; + do { + if (top->next == &nil_iterator) + break; + if (top->is_boundary()) + nboundaries++; + is_macro = top->is_macro(); + input_iterator *tem = top; + top = top->next; + level--; + delete tem; + } while (!is_macro); + // Keep while_request happy. + for (; nboundaries > 0; --nboundaries) + add_return_boundary(); +} + +inline void input_stack::save_compatible_flag(int f) +{ + top->save_compatible_flag(f); +} + +inline int input_stack::get_compatible_flag() +{ + return top->get_compatible_flag(); +} + +void backtrace_request() +{ + input_stack::backtrace_all(); + fflush(stderr); + skip_line(); +} + +void next_file() +{ + symbol nm = get_long_name(); + while (!tok.newline() && !tok.eof()) + tok.next(); + if (nm.is_null()) + input_stack::end_file(); + else { + errno = 0; + FILE *fp = fopen(nm.contents(), "r"); + if (!fp) + error("can't open `%1': %2", nm.contents(), strerror(errno)); + else + input_stack::next_file(fp, nm.contents()); + } + tok.next(); +} + +void shift() +{ + int n; + if (!has_arg() || !get_integer(&n)) + n = 1; + input_stack::shift(n); + skip_line(); +} + +static int get_char_for_escape_name(int allow_space = 0) +{ + int c = get_copy(0); + switch (c) { + case EOF: + copy_mode_error("end of input in escape name"); + return '\0'; + default: + 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': + copy_mode_error("%1 is not allowed in an escape name", + input_char_description(c)); + return '\0'; + } + return c; +} + +static symbol read_two_char_escape_name() +{ + char buf[3]; + buf[0] = get_char_for_escape_name(); + if (buf[0] != '\0') { + buf[1] = get_char_for_escape_name(); + if (buf[1] == '\0') + buf[0] = 0; + else + buf[2] = 0; + } + return symbol(buf); +} + +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 (;;) { + 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]; + 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; + } + } + if (c == ']' && input_stack::get_level() == start_level) + break; + buf[i++] = c; + } + buf[i] = 0; + if (c == ' ') + have_string_arg = 1; + if (buf == abuf) { + if (i == 0) { + if (mode != ALLOW_EMPTY) + copy_mode_error("empty escape name"); + return EMPTY_SYMBOL; + } + return symbol(abuf); + } + else { + symbol s(buf); + a_delete buf; + return s; + } +} + +static symbol read_escape_name(read_mode mode) +{ + int c = get_char_for_escape_name(); + if (c == 0) + return NULL_SYMBOL; + if (c == '(') + return read_two_char_escape_name(); + if (c == '[' && !compatible_flag) + return read_long_escape_name(mode); + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + return symbol(buf); +} + +static symbol read_increment_and_escape_name(int *incp) +{ + int c = get_char_for_escape_name(); + switch (c) { + case 0: + *incp = 0; + return NULL_SYMBOL; + case '(': + *incp = 0; + return read_two_char_escape_name(); + case '+': + *incp = 1; + return read_escape_name(); + case '-': + *incp = -1; + return read_escape_name(); + case '[': + if (!compatible_flag) { + *incp = 0; + return read_long_escape_name(); + } + break; + } + *incp = 0; + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + return symbol(buf); +} + +static int get_copy(node **nd, int defining) +{ + for (;;) { + int c = input_stack::get(nd); + if (c == ESCAPE_NEWLINE) { + if (defining) + return c; + do { + c = input_stack::get(nd); + } while (c == ESCAPE_NEWLINE); + } + if (c != escape_char || escape_char <= 0) + return c; + c = input_stack::peek(); + switch(c) { + case 0: + return escape_char; + case '"': + (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(0); + while ((c = input_stack::get(0)) != '\n') + if (c == EOF) + return EOF; + break; + case '$': + { + (void)input_stack::get(0); + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_arg(s); + break; + } + case '*': + { + (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(0); + return '\001'; + case 'e': + (void)input_stack::get(0); + return ESCAPE_e; + case 'E': + (void)input_stack::get(0); + return ESCAPE_E; + case 'n': + { + (void)input_stack::get(0); + int inc; + symbol s = read_increment_and_escape_name(&inc); + if (!(s.is_null() || s.is_empty())) + interpolate_number_reg(s, inc); + break; + } + case 'g': + { + (void)input_stack::get(0); + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_number_format(s); + break; + } + case 't': + (void)input_stack::get(0); + return '\t'; + case 'V': + { + (void)input_stack::get(0); + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_environment_variable(s); + break; + } + case '\n': + (void)input_stack::get(0); + if (defining) + return ESCAPE_NEWLINE; + break; + case ' ': + (void)input_stack::get(0); + return ESCAPE_SPACE; + case '~': + (void)input_stack::get(0); + return ESCAPE_TILDE; + case ':': + (void)input_stack::get(0); + return ESCAPE_COLON; + case '|': + (void)input_stack::get(0); + return ESCAPE_BAR; + case '^': + (void)input_stack::get(0); + return ESCAPE_CIRCUMFLEX; + case '{': + (void)input_stack::get(0); + return ESCAPE_LEFT_BRACE; + case '}': + (void)input_stack::get(0); + return ESCAPE_RIGHT_BRACE; + case '`': + (void)input_stack::get(0); + return ESCAPE_LEFT_QUOTE; + case '\'': + (void)input_stack::get(0); + return ESCAPE_RIGHT_QUOTE; + case '-': + (void)input_stack::get(0); + return ESCAPE_HYPHEN; + case '_': + (void)input_stack::get(0); + return ESCAPE_UNDERSCORE; + case 'c': + (void)input_stack::get(0); + return ESCAPE_c; + case '!': + (void)input_stack::get(0); + return ESCAPE_BANG; + case '?': + (void)input_stack::get(0); + return ESCAPE_QUESTION; + case '&': + (void)input_stack::get(0); + return ESCAPE_AMPERSAND; + case ')': + (void)input_stack::get(0); + return ESCAPE_RIGHT_PARENTHESIS; + case '.': + (void)input_stack::get(0); + return c; + case '%': + (void)input_stack::get(0); + return ESCAPE_PERCENT; + default: + if (c == escape_char) { + (void)input_stack::get(0); + return c; + } + else + return escape_char; + } + } +} + +class non_interpreted_char_node : public node { + unsigned char c; +public: + non_interpreted_char_node(unsigned char); + node *copy(); + int interpret(macro *); + int same(node *); + const char *type(); + int force_tprint(); +}; + +int non_interpreted_char_node::same(node *nd) +{ + return c == ((non_interpreted_char_node *)nd)->c; +} + +const char *non_interpreted_char_node::type() +{ + return "non_interpreted_char_node"; +} + +int non_interpreted_char_node::force_tprint() +{ + return 0; +} + +non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n) +{ + assert(n != 0); +} + +node *non_interpreted_char_node::copy() +{ + return new non_interpreted_char_node(c); +} + +int non_interpreted_char_node::interpret(macro *mac) +{ + mac->append(c); + return 1; +} + +static void do_width(); +static node *do_non_interpreted(); +static node *do_special(); +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, "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(char end = 0) +{ + symbol component = do_get_long_name(0, end); + 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 { + if (!end) + 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(char end = 0) +{ + symbol component = do_get_long_name(0, end); + 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 { + if (!end) + 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(char end = 0) +{ + symbol component = do_get_long_name(0, end); + 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 { + if (!end) + 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(char end = 0) +{ + symbol component = do_get_long_name(0, end); + 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 { + if (!end) + 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(); + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + 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) + on->overstrike(n); + } + } + return on; +} + +static node *do_bracket() +{ + token start; + bracket_node *bn = new bracket_node; + start.next(); + int start_level = input_stack::get_level(); + for (;;) { + tok.next(); + if (tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + break; + } + if (tok.newline()) { + warning(WARN_DELIM, "missing closing delimiter"); + 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); + } + } + return bn; +} + +static int do_name_test() +{ + token start; + start.next(); + int start_level = input_stack::get_level(); + int bad_char = 0; + int some_char = 0; + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + input_stack::push(make_temp_iterator("\n")); + break; + } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; + if (!tok.ch()) + bad_char = 1; + some_char = 1; + } + return some_char && !bad_char; +} + +static int do_expr_test() +{ + token start; + start.next(); + int start_level = input_stack::get_level(); + if (!start.delimiter(1)) + return 0; + tok.next(); + // disable all warning and error messages temporarily + int saved_warning_mask = warning_mask; + int saved_inhibit_errors = inhibit_errors; + warning_mask = 0; + inhibit_errors = 1; + int dummy; + int result = get_number_rigidly(&dummy, 'u'); + warning_mask = saved_warning_mask; + inhibit_errors = saved_inhibit_errors; + if (tok == start && input_stack::get_level() == start_level) + return result; + // ignore everything up to the delimiter in case we aren't right there + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + input_stack::push(make_temp_iterator("\n")); + break; + } + if (tok == start && input_stack::get_level() == start_level) + break; + } + return 0; +} + +#if 0 +static node *do_zero_width() +{ + token start; + start.next(); + int start_level = input_stack::get_level(); + environment env(curenv); + environment *oldenv = curenv; + curenv = &env; + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + error("missing closing delimiter"); + break; + } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; + tok.process(); + } + curenv = oldenv; + node *rev = env.extract_output_line(); + node *n = 0; + while (rev) { + node *tem = rev; + rev = rev->next; + tem->next = n; + n = tem; + } + return new zero_width_node(n); +} + +#else + +// It's undesirable for \Z to change environments, because then +// \n(.w won't work as expected. + +static node *do_zero_width() +{ + node *rev = new dummy_node; + token start; + start.next(); + int start_level = input_stack::get_level(); + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + input_stack::push(make_temp_iterator("\n")); + break; + } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; + if (!tok.add_to_node_list(&rev)) + error("invalid token in argument to \\Z"); + } + node *n = 0; + while (rev) { + node *tem = rev; + rev = rev->next; + tem->next = n; + n = tem; + } + return new zero_width_node(n); +} + +#endif + +token_node *node::get_token_node() +{ + return 0; +} + +class token_node : public node { +public: + token tk; + token_node(const token &t); + node *copy(); + token_node *get_token_node(); + int same(node *); + const char *type(); + int force_tprint(); +}; + +token_node::token_node(const token &t) : tk(t) +{ +} + +node *token_node::copy() +{ + return new token_node(tk); +} + +token_node *token_node::get_token_node() +{ + return this; +} + +int token_node::same(node *nd) +{ + return tk == ((token_node *)nd)->tk; +} + +const char *token_node::type() +{ + return "token_node"; +} + +int token_node::force_tprint() +{ + return 0; +} + +token::token() : nd(0), type(TOKEN_EMPTY) +{ +} + +token::~token() +{ + delete nd; +} + +token::token(const token &t) +: nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type) +{ + // Use two statements to work around bug in SGI C++. + node *tem = t.nd; + nd = tem ? tem->copy() : 0; +} + +void token::operator=(const token &t) +{ + delete nd; + nm = t.nm; + // Use two statements to work around bug in SGI C++. + node *tem = t.nd; + nd = tem ? tem->copy() : 0; + c = t.c; + val = t.val; + dim = t.dim; + type = t.type; +} + +void token::skip() +{ + while (space()) + next(); +} + +int has_arg() +{ + while (tok.space()) + tok.next(); + return !tok.newline(); +} + +void token::make_space() +{ + type = TOKEN_SPACE; +} + +void token::make_newline() +{ + type = TOKEN_NEWLINE; +} + +void token::next() +{ + if (nd) { + delete nd; + nd = 0; + } + units x; + for (;;) { + node *n; + int cc = input_stack::get(&n); + if (cc != escape_char || escape_char == 0) { + handle_normal_char: + switch(cc) { + case COMPATIBLE_SAVE: + input_stack::save_compatible_flag(compatible_flag); + compatible_flag = 0; + continue; + case COMPATIBLE_RESTORE: + compatible_flag = input_stack::get_compatible_flag(); + continue; + case EOF: + type = TOKEN_EOF; + return; + case TRANSPARENT_FILE_REQUEST: + case TITLE_REQUEST: + case COPY_FILE_REQUEST: +#ifdef COLUMN + case VJUSTIFY_REQUEST: +#endif /* COLUMN */ + type = TOKEN_REQUEST; + c = cc; + return; + case BEGIN_TRAP: + type = TOKEN_BEGIN_TRAP; + return; + case END_TRAP: + type = TOKEN_END_TRAP; + return; + case LAST_PAGE_EJECTOR: + seen_last_page_ejector = 1; + // fall through + case PAGE_EJECTOR: + type = TOKEN_PAGE_EJECTOR; + return; + case ESCAPE_PERCENT: + ESCAPE_PERCENT: + type = TOKEN_HYPHEN_INDICATOR; + return; + case ESCAPE_SPACE: + ESCAPE_SPACE: + type = TOKEN_UNSTRETCHABLE_SPACE; + return; + case ESCAPE_TILDE: + ESCAPE_TILDE: + type = TOKEN_STRETCHABLE_SPACE; + return; + case ESCAPE_COLON: + ESCAPE_COLON: + type = TOKEN_ZERO_WIDTH_BREAK; + return; + case ESCAPE_e: + ESCAPE_e: + type = TOKEN_ESCAPE; + return; + case ESCAPE_E: + goto handle_escape_char; + case ESCAPE_BAR: + ESCAPE_BAR: + type = TOKEN_NODE; + 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(), + curenv->get_fill_color()); + return; + case ESCAPE_NEWLINE: + have_input = 0; + break; + case ESCAPE_LEFT_BRACE: + ESCAPE_LEFT_BRACE: + type = TOKEN_LEFT_BRACE; + return; + case ESCAPE_RIGHT_BRACE: + ESCAPE_RIGHT_BRACE: + type = TOKEN_RIGHT_BRACE; + return; + case ESCAPE_LEFT_QUOTE: + ESCAPE_LEFT_QUOTE: + type = TOKEN_SPECIAL; + nm = symbol("ga"); + return; + case ESCAPE_RIGHT_QUOTE: + ESCAPE_RIGHT_QUOTE: + type = TOKEN_SPECIAL; + nm = symbol("aa"); + return; + case ESCAPE_HYPHEN: + ESCAPE_HYPHEN: + type = TOKEN_SPECIAL; + nm = symbol("-"); + return; + case ESCAPE_UNDERSCORE: + ESCAPE_UNDERSCORE: + type = TOKEN_SPECIAL; + nm = symbol("ul"); + return; + case ESCAPE_c: + ESCAPE_c: + type = TOKEN_INTERRUPT; + return; + case ESCAPE_BANG: + ESCAPE_BANG: + type = TOKEN_TRANSPARENT; + return; + case ESCAPE_QUESTION: + ESCAPE_QUESTION: + nd = do_non_interpreted(); + if (nd) { + type = TOKEN_NODE; + return; + } + break; + case ESCAPE_AMPERSAND: + ESCAPE_AMPERSAND: + type = TOKEN_DUMMY; + return; + case ESCAPE_RIGHT_PARENTHESIS: + ESCAPE_RIGHT_PARENTHESIS: + type = TOKEN_TRANSPARENT_DUMMY; + return; + case '\b': + type = TOKEN_BACKSPACE; + return; + case ' ': + type = TOKEN_SPACE; + return; + case '\t': + type = TOKEN_TAB; + return; + case '\n': + type = TOKEN_NEWLINE; + return; + case '\001': + type = TOKEN_LEADER; + return; + case 0: + { + assert(n != 0); + token_node *tn = n->get_token_node(); + if (tn) { + *this = tn->tk; + delete tn; + } + else { + nd = n; + type = TOKEN_NODE; + } + } + return; + default: + type = TOKEN_CHAR; + c = cc; + return; + } + } + else { + handle_escape_char: + cc = input_stack::get(0); + switch(cc) { + case '(': + nm = read_two_char_escape_name(); + type = TOKEN_SPECIAL; + return; + case EOF: + type = TOKEN_EOF; + error("end of input after escape character"); + return; + case '`': + goto ESCAPE_LEFT_QUOTE; + case '\'': + goto ESCAPE_RIGHT_QUOTE; + case '-': + goto ESCAPE_HYPHEN; + case '_': + goto ESCAPE_UNDERSCORE; + case '%': + goto ESCAPE_PERCENT; + case ' ': + goto ESCAPE_SPACE; + case '0': + nd = new hmotion_node(curenv->get_digit_width(), + curenv->get_fill_color()); + type = TOKEN_NODE; + return; + case '|': + goto ESCAPE_BAR; + case '^': + goto ESCAPE_CIRCUMFLEX; + case '/': + type = TOKEN_ITALIC_CORRECTION; + return; + case ',': + type = TOKEN_NODE; + nd = new left_italic_corrected_node; + return; + case '&': + goto ESCAPE_AMPERSAND; + case ')': + goto ESCAPE_RIGHT_PARENTHESIS; + case '!': + goto ESCAPE_BANG; + case '?': + goto ESCAPE_QUESTION; + case '~': + goto ESCAPE_TILDE; + case ':': + goto ESCAPE_COLON; + case '"': + while ((cc = input_stack::get(0)) != '\n' && cc != EOF) + ; + if (cc == '\n') + type = TOKEN_NEWLINE; + else + type = TOKEN_EOF; + return; + case '#': // Like \" but newline is ignored. + while ((cc = input_stack::get(0)) != '\n') + if (cc == EOF) { + type = TOKEN_EOF; + return; + } + break; + case '$': + { + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_arg(s); + break; + } + case '*': + { + 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': + nd = new non_interpreted_char_node('\001'); + type = TOKEN_NODE; + return; + case 'A': + c = '0' + do_name_test(); + type = TOKEN_CHAR; + return; + case 'b': + nd = do_bracket(); + type = TOKEN_NODE; + return; + case 'B': + c = '0' + do_expr_test(); + type = TOKEN_CHAR; + return; + case 'c': + goto ESCAPE_c; + case 'C': + nm = get_delim_name(); + if (nm.is_null()) + break; + type = TOKEN_SPECIAL; + return; + case 'd': + type = TOKEN_NODE; + nd = new vmotion_node(curenv->get_size() / 2, + curenv->get_fill_color()); + return; + case 'D': + nd = read_draw_node(); + if (!nd) + break; + type = TOKEN_NODE; + return; + case 'e': + goto ESCAPE_e; + case 'E': + goto handle_escape_char; + case 'f': + { + 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 || 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); + have_input = 1; + break; + } + case 'g': + { + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_number_format(s); + break; + } + case 'h': + if (!get_delim_number(&x, 'm')) + break; + type = TOKEN_NODE; + nd = new hmotion_node(x, curenv->get_fill_color()); + return; + case 'H': + // 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() || nm.is_empty()) + break; + type = TOKEN_MARK_INPUT; + return; + case 'l': + case 'L': + { + charinfo *s = 0; + if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s)) + break; + if (s == 0) + s = get_charinfo(cc == 'l' ? "ru" : "br"); + type = TOKEN_NODE; + node *n = curenv->make_char_node(s); + if (cc == 'l') + nd = new hline_node(x, n); + else + 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 s = read_increment_and_escape_name(&inc); + if (!(s.is_null() || s.is_empty())) + interpolate_number_reg(s, inc); + break; + } + case 'N': + if (!get_delim_number(&val, 0)) + break; + type = TOKEN_NUMBERED_CHAR; + return; + case 'o': + nd = do_overstrike(); + type = TOKEN_NODE; + return; + case 'O': + nd = do_suppress(read_escape_name()); + if (!nd) + break; + type = TOKEN_NODE; + return; + case 'p': + type = TOKEN_SPREAD; + return; + case 'r': + type = TOKEN_NODE; + 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; + nd = new non_interpreted_char_node('\t'); + return; + case 'u': + type = TOKEN_NODE; + 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, curenv->get_fill_color()); + return; + case 'V': + { + symbol s = read_escape_name(); + if (!(s.is_null() || s.is_empty())) + interpolate_environment_variable(s); + break; + } + case 'w': + do_width(); + break; + case 'x': + if (!get_delim_number(&x, 'v')) + break; + type = TOKEN_NODE; + nd = new extra_size_node(x); + return; + case 'X': + nd = do_special(); + if (!nd) + break; + type = TOKEN_NODE; + return; + case 'Y': + { + symbol s = read_escape_name(); + if (s.is_null() || s.is_empty()) + break; + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) { + error("can't transparently throughput a request"); + break; + } + nd = new special_node(*m); + type = TOKEN_NODE; + return; + } + case 'z': + { + next(); + if (type == TOKEN_NODE) + nd = new zero_width_node(nd); + else { + charinfo *ci = get_char(1); + if (ci == 0) + break; + node *gn = curenv->make_char_node(ci); + if (gn == 0) + break; + nd = new zero_width_node(gn); + type = TOKEN_NODE; + } + return; + } + case 'Z': + nd = do_zero_width(); + if (nd == 0) + break; + type = TOKEN_NODE; + return; + case '{': + goto ESCAPE_LEFT_BRACE; + case '}': + goto ESCAPE_RIGHT_BRACE; + case '\n': + break; + case '[': + if (!compatible_flag) { + symbol s = read_long_escape_name(WITH_ARGS); + if (s.is_null() || s.is_empty()) + break; + if (have_string_arg) { + have_string_arg = 0; + nm = composite_glyph_name(s); + } + else { + const char *gn = check_unicode_name(s.contents()); + if (gn) { + const char *gn_decomposed = decompose_unicode(gn); + if (gn_decomposed) + gn = &gn_decomposed[1]; + const char *groff_gn = unicode_to_glyph_name(gn); + if (groff_gn) + nm = symbol(groff_gn); + else { + char *buf = new char[strlen(gn) + 1 + 1]; + strcpy(buf, "u"); + strcat(buf, gn); + nm = symbol(buf); + a_delete buf; + } + } + else + nm = symbol(s.contents()); + } + type = TOKEN_SPECIAL; + return; + } + goto handle_normal_char; + default: + if (cc != escape_char && cc != '.') + warning(WARN_ESCAPE, "escape character ignored before %1", + input_char_description(cc)); + goto handle_normal_char; + } + } + } +} + +int token::operator==(const token &t) +{ + if (type != t.type) + return 0; + switch(type) { + case TOKEN_CHAR: + return c == t.c; + case TOKEN_SPECIAL: + return nm == t.nm; + case TOKEN_NUMBERED_CHAR: + return val == t.val; + default: + return 1; + } +} + +int token::operator!=(const token &t) +{ + return !(*this == t); +} + +// is token a suitable delimiter (like ')? + +int token::delimiter(int err) +{ + switch(type) { + case TOKEN_CHAR: + switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case '/': + case '*': + case '%': + case '<': + case '>': + case '=': + case '&': + case ':': + case '(': + case ')': + case '.': + if (err) + error("cannot use character `%1' as a starting delimiter", char(c)); + return 0; + default: + return 1; + } + case TOKEN_NODE: + case TOKEN_SPACE: + case TOKEN_STRETCHABLE_SPACE: + case TOKEN_UNSTRETCHABLE_SPACE: + case TOKEN_TAB: + case TOKEN_NEWLINE: + if (err) + error("cannot use %1 as a starting delimiter", description()); + return 0; + default: + return 1; + } +} + +const char *token::description() +{ + static char buf[4]; + switch (type) { + case TOKEN_BACKSPACE: + return "a backspace character"; + case TOKEN_CHAR: + buf[0] = '`'; + buf[1] = c; + buf[2] = '\''; + buf[3] = '\0'; + return buf; + case TOKEN_DUMMY: + return "`\\&'"; + case TOKEN_ESCAPE: + return "`\\e'"; + case TOKEN_HYPHEN_INDICATOR: + return "`\\%'"; + case TOKEN_INTERRUPT: + return "`\\c'"; + case TOKEN_ITALIC_CORRECTION: + return "`\\/'"; + case TOKEN_LEADER: + return "a leader character"; + case TOKEN_LEFT_BRACE: + return "`\\{'"; + case TOKEN_MARK_INPUT: + return "`\\k'"; + case TOKEN_NEWLINE: + return "newline"; + case TOKEN_NODE: + return "a node"; + case TOKEN_NUMBERED_CHAR: + return "`\\N'"; + case TOKEN_RIGHT_BRACE: + return "`\\}'"; + case TOKEN_SPACE: + return "a space"; + case TOKEN_SPECIAL: + return "a special character"; + case TOKEN_SPREAD: + 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: + break; + } + return "a magic token"; +} + +void skip_line() +{ + while (!tok.newline()) + if (tok.eof()) + return; + else + tok.next(); + tok.next(); +} + +void compatible() +{ + int n; + if (has_arg() && get_integer(&n)) + compatible_flag = n != 0; + else + compatible_flag = 1; + skip_line(); +} + +static void empty_name_warning(int required) +{ + if (tok.newline() || tok.eof()) { + if (required) + warning(WARN_MISSING, "missing name"); + } + else if (tok.right_brace() || tok.tab()) { + const char *start = tok.description(); + do { + tok.next(); + } while (tok.space() || tok.right_brace() || tok.tab()); + if (!tok.newline() && !tok.eof()) + error("%1 is not allowed before an argument", start); + else if (required) + warning(WARN_MISSING, "missing name"); + } + else if (required) + error("name expected (got %1)", tok.description()); + else + error("name expected (got %1): treated as missing", tok.description()); +} + +static void non_empty_name_warning() +{ + if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab() + && !tok.right_brace() + // We don't want to give a warning for .el\{ + && !tok.left_brace()) + error("%1 is not allowed in a name", tok.description()); +} + +symbol get_name(int required) +{ + if (compatible_flag) { + char buf[3]; + tok.skip(); + if ((buf[0] = tok.ch()) != 0) { + tok.next(); + if ((buf[1] = tok.ch()) != 0) { + buf[2] = 0; + tok.make_space(); + } + else + non_empty_name_warning(); + return symbol(buf); + } + else { + empty_name_warning(required); + return NULL_SYMBOL; + } + } + else + return get_long_name(required); +} + +symbol get_long_name(int required) +{ + return do_get_long_name(required, 0); +} + +static symbol do_get_long_name(int required, char end) +{ + while (tok.space()) + tok.next(); + char abuf[ABUF_SIZE]; + char *buf = abuf; + int buf_size = ABUF_SIZE; + int i = 0; + for (;;) { + // If end != 0 we normally have to append a null byte + if (i + 2 > 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; + } + } + if ((buf[i] = tok.ch()) == 0 || buf[i] == end) + break; + i++; + tok.next(); + } + if (i == 0) { + empty_name_warning(required); + return NULL_SYMBOL; + } + if (end && buf[i] == end) + buf[i+1] = '\0'; + else + non_empty_name_warning(); + if (buf == abuf) + return symbol(buf); + else { + symbol s(buf); + a_delete buf; + return s; + } +} + +void exit_troff() +{ + exit_started = 1; + topdiv->set_last_page(); + if (!end_macro_name.is_null()) { + spring_trap(end_macro_name); + tok.next(); + process_input_stack(); + } + curenv->final_break(); + tok.next(); + process_input_stack(); + end_diversions(); + 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); +} + +// This implements .ex. The input stack must be cleared before calling +// exit_troff(). + +void exit_request() +{ + input_stack::clear(); + if (exit_started) + tok.next(); + else + exit_troff(); +} + +void return_macro_request() +{ + input_stack::pop_macro(); + tok.next(); +} + +void end_macro() +{ + end_macro_name = get_name(); + skip_line(); +} + +void blank_line_macro() +{ + blank_line_macro_name = get_name(); + skip_line(); +} + +static void trapping_blank_line() +{ + if (!blank_line_macro_name.is_null()) + spring_trap(blank_line_macro_name); + else + blank_line(); +} + +void do_request() +{ + int old_compatible_flag = compatible_flag; + compatible_flag = 0; + symbol nm = get_name(); + if (nm.is_null()) + skip_line(); + else + interpolate_macro(nm); + compatible_flag = old_compatible_flag; +} + +inline int possibly_handle_first_page_transition() +{ + if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) { + handle_first_page_transition(); + return 1; + } + else + return 0; +} + +static int transparent_translate(int cc) +{ + if (!invalid_input_char(cc)) { + charinfo *ci = charset_table[cc]; + switch (ci->get_special_translation(1)) { + case charinfo::TRANSLATE_SPACE: + return ' '; + case charinfo::TRANSLATE_STRETCHABLE_SPACE: + return ESCAPE_TILDE; + case charinfo::TRANSLATE_DUMMY: + return ESCAPE_AMPERSAND; + case charinfo::TRANSLATE_HYPHEN_INDICATOR: + return ESCAPE_PERCENT; + } + // This is really ugly. + ci = ci->get_translation(1); + if (ci) { + int c = ci->get_ascii_code(); + if (c != '\0') + return c; + error("can't translate %1 to special character `%2'" + " in transparent throughput", + input_char_description(cc), + ci->nm.contents()); + } + } + return cc; +} + +class int_stack { + struct int_stack_element { + int n; + int_stack_element *next; + } *top; +public: + int_stack(); + ~int_stack(); + void push(int); + int is_empty(); + int pop(); +}; + +int_stack::int_stack() +{ + top = 0; +} + +int_stack::~int_stack() +{ + while (top != 0) { + int_stack_element *temp = top; + top = top->next; + delete temp; + } +} + +int int_stack::is_empty() +{ + return top == 0; +} + +void int_stack::push(int n) +{ + int_stack_element *p = new int_stack_element; + p->next = top; + p->n = n; + top = p; +} + +int int_stack::pop() +{ + assert(top != 0); + int_stack_element *p = top; + top = top->next; + int n = p->n; + delete p; + return n; +} + +int node::reread(int *) +{ + return 0; +} + +int diverted_space_node::reread(int *bolp) +{ + if (curenv->get_fill()) + trapping_blank_line(); + else + curdiv->space(n); + *bolp = 1; + return 1; +} + +int diverted_copy_file_node::reread(int *bolp) +{ + curdiv->copy_file(filename.contents()); + *bolp = 1; + return 1; +} + +int word_space_node::reread(int *) +{ + if (unformat) { + for (width_list *w = orig_width; w; w = w->next) + curenv->space(w->width, w->sentence_width); + unformat = 0; + return 1; + } + return 0; +} + +int unbreakable_space_node::reread(int *) +{ + return 0; +} + +int hmotion_node::reread(int *) +{ + if (unformat && was_tab) { + curenv->handle_tab(0); + unformat = 0; + return 1; + } + return 0; +} + +void process_input_stack() +{ + int_stack trap_bol_stack; + int bol = 1; + for (;;) { + int suppress_next = 0; + switch (tok.type) { + case token::TOKEN_CHAR: + { + unsigned char ch = tok.c; + 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 { + tok.next(); + } while (tok.white_space()); + symbol nm = get_name(); + if (nm.is_null()) + skip_line(); + else + interpolate_macro(nm); + suppress_next = 1; + } + else { + if (possibly_handle_first_page_transition()) + ; + else { + for (;;) { + curenv->add_char(charset_table[ch]); + tok.next(); + if (tok.type != token::TOKEN_CHAR) + break; + ch = tok.c; + } + suppress_next = 1; + bol = 0; + } + } + break; + } + case token::TOKEN_TRANSPARENT: + { + if (bol) { + if (possibly_handle_first_page_transition()) + ; + else { + int cc; + do { + node *n; + cc = get_copy(&n); + if (cc != EOF) + if (cc != '\0') + curdiv->transparent_output(transparent_translate(cc)); + else + curdiv->transparent_output(n); + } while (cc != '\n' && cc != EOF); + if (cc == EOF) + curdiv->transparent_output('\n'); + } + } + break; + } + case token::TOKEN_NEWLINE: + { + if (bol && !have_input + && !curenv->get_prev_line_interrupted()) + trapping_blank_line(); + else { + curenv->newline(); + bol = 1; + } + break; + } + case token::TOKEN_REQUEST: + { + int request_code = tok.c; + tok.next(); + switch (request_code) { + case TITLE_REQUEST: + title(); + break; + case COPY_FILE_REQUEST: + copy_file(); + break; + case TRANSPARENT_FILE_REQUEST: + transparent_file(); + break; +#ifdef COLUMN + case VJUSTIFY_REQUEST: + vjustify(); + break; +#endif /* COLUMN */ + default: + assert(0); + break; + } + suppress_next = 1; + break; + } + case token::TOKEN_SPACE: + { + if (possibly_handle_first_page_transition()) + ; + 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(); + } while (tok.space()); + if (tok.newline()) + trapping_blank_line(); + else { + push_token(tok); + curenv->do_break(); + curenv->add_node(new hmotion_node(space_width * nspaces, + curenv->get_fill_color())); + bol = 0; + } + } + else { + curenv->space(); + bol = 0; + } + break; + } + case token::TOKEN_EOF: + return; + case token::TOKEN_NODE: + { + if (possibly_handle_first_page_transition()) + ; + else if (tok.nd->reread(&bol)) { + delete tok.nd; + tok.nd = 0; + } + else { + curenv->add_node(tok.nd); + tok.nd = 0; + bol = 0; + curenv->possibly_break_line(1); + } + break; + } + case token::TOKEN_PAGE_EJECTOR: + { + continue_page_eject(); + // I think we just want to preserve bol. + // bol = 1; + break; + } + case token::TOKEN_BEGIN_TRAP: + { + trap_bol_stack.push(bol); + bol = 1; + have_input = 0; + break; + } + case token::TOKEN_END_TRAP: + { + if (trap_bol_stack.is_empty()) + error("spurious end trap token detected!"); + else + bol = trap_bol_stack.pop(); + have_input = 0; + + /* I'm not totally happy about this. But I can't think of any other + 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 -.5i y + .de y + .tl ''-%-'' + .. + .br + .ll .5i + .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. */ + + if (trap_bol_stack.is_empty()) + curenv->output_pending_lines(); + break; + } + default: + { + bol = 0; + tok.process(); + break; + } + } + if (!suppress_next) + tok.next(); + trap_sprung_flag = 0; + } +} + +#ifdef WIDOW_CONTROL + +void flush_pending_lines() +{ + while (!tok.newline() && !tok.eof()) + tok.next(); + curenv->output_pending_lines(); + tok.next(); +} + +#endif /* WIDOW_CONTROL */ + +request_or_macro::request_or_macro() +{ +} + +macro *request_or_macro::to_macro() +{ + return 0; +} + +request::request(REQUEST_FUNCP pp) : p(pp) +{ +} + +void request::invoke(symbol) +{ + (*p)(); +} + +struct char_block { + enum { SIZE = 128 }; + unsigned char s[SIZE]; + char_block *next; + char_block(); +}; + +char_block::char_block() +: next(0) +{ +} + +class char_list { +public: + char_list(); + ~char_list(); + void append(unsigned char); + void set(unsigned char, int); + unsigned char get(int); + int length(); +private: + unsigned char *ptr; + int len; + char_block *head; + char_block *tail; + friend class macro_header; + friend class string_iterator; +}; + +char_list::char_list() +: ptr(0), len(0), head(0), tail(0) +{ +} + +char_list::~char_list() +{ + while (head != 0) { + char_block *tem = head; + head = head->next; + delete tem; + } +} + +int char_list::length() +{ + return len; +} + +void char_list::append(unsigned char c) +{ + if (tail == 0) { + head = tail = new char_block; + ptr = tail->s; + } + else { + if (ptr >= tail->s + char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + ptr = tail->s; + } + } + *ptr++ = 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; +public: + node_list(); + ~node_list(); + void append(node *); + int length(); + node *extract(); + + friend class macro_header; + friend class string_iterator; +}; + +void node_list::append(node *n) +{ + if (head == 0) { + n->next = 0; + head = tail = n; + } + else { + n->next = 0; + tail = tail->next = n; + } +} + +int node_list::length() +{ + int total = 0; + for (node *n = head; n != 0; n = n->next) + ++total; + return total; +} + +node_list::node_list() +{ + head = tail = 0; +} + +node *node_list::extract() +{ + node *temp = head; + head = tail = 0; + return temp; +} + +node_list::~node_list() +{ + delete_node_list(head); +} + +struct macro_header { +public: + int count; + char_list cl; + node_list nl; + macro_header() { count = 1; } + macro_header *copy(int); +}; + +macro::~macro() +{ + if (p != 0 && --(p->count) <= 0) + delete p; +} + +macro::macro() +{ + if (!input_stack::get_location(1, &filename, &lineno)) { + filename = 0; + lineno = 0; + } + len = 0; + empty_macro = 1; + p = 0; +} + +macro::macro(const macro &m) +: p(m.p), filename(m.filename), lineno(m.lineno), len(m.len), + empty_macro(m.empty_macro) +{ + if (p != 0) + p->count++; +} + +macro ¯o::operator=(const macro &m) +{ + // don't assign object + if (m.p != 0) + m.p->count++; + if (p != 0 && --(p->count) <= 0) + delete p; + p = m.p; + filename = m.filename; + lineno = m.lineno; + len = m.len; + empty_macro = m.empty_macro; + return *this; +} + +void macro::append(unsigned char c) +{ + assert(c != 0); + if (p == 0) + p = new macro_header; + if (p->cl.length() != len) { + macro_header *tem = p->copy(len); + if (--(p->count) <= 0) + delete p; + p = tem; + } + p->cl.append(c); + ++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) +{ + int i = 0; + + if (s) { + while (s[i] != (char)0) { + append(s[i]); + i++; + } + } +} + +void macro::append(node *n) +{ + assert(n != 0); + if (p == 0) + p = new macro_header; + 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); + ++len; + empty_macro = 0; +} + +void macro::append_unsigned(unsigned int i) +{ + unsigned int j = i / 10; + if (j != 0) + append_unsigned(j); + append(((unsigned char)(((int)'0') + i % 10))); +} + +void macro::append_int(int i) +{ + if (i < 0) { + append('-'); + i = -i; + } + append_unsigned((unsigned int)i); +} + +void macro::print_size() +{ + errprint("%1", len); +} + +// make a copy of the first n bytes + +macro_header *macro_header::copy(int n) +{ + macro_header *p = new macro_header; + char_block *bp = cl.head; + unsigned char *ptr = bp->s; + node *nd = nl.head; + while (--n >= 0) { + if (ptr >= bp->s + char_block::SIZE) { + bp = bp->next; + ptr = bp->s; + } + int c = *ptr++; + p->cl.append(c); + if (c == 0) { + p->nl.append(nd->copy()); + nd = nd->next; + } + } + return p; +} + +void print_macros() +{ + object_dictionary_iterator iter(request_dictionary); + request_or_macro *rm; + symbol s; + while (iter.get(&s, (object **)&rm)) { + assert(!s.is_null()); + macro *m = rm->to_macro(); + if (m) { + errprint("%1\t", s.contents()); + m->print_size(); + errprint("\n"); + } + } + fflush(stderr); + skip_line(); +} + +class string_iterator : public input_iterator { + macro mac; + const char *how_invoked; + int newline_flag; + int lineno; + char_block *bp; + int count; // of characters remaining + node *nd; + int saved_compatible_flag; +protected: + symbol nm; + string_iterator(); +public: + string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL); + int fill(node **); + int peek(); + int get_location(int, const char **, int *); + void backtrace(); + void save_compatible_flag(int f) { saved_compatible_flag = f; } + int get_compatible_flag() { return saved_compatible_flag; } +}; + +string_iterator::string_iterator(const macro &m, const char *p, symbol s) +: mac(m), how_invoked(p), + newline_flag(0), lineno(1), nm(s) +{ + count = mac.len; + if (count != 0) { + bp = mac.p->cl.head; + nd = mac.p->nl.head; + ptr = eptr = bp->s; + } + else { + bp = 0; + nd = 0; + ptr = eptr = 0; + } +} + +string_iterator::string_iterator() +{ + bp = 0; + nd = 0; + ptr = eptr = 0; + newline_flag = 0; + how_invoked = 0; + lineno = 1; + count = 0; +} + +int string_iterator::fill(node **np) +{ + if (newline_flag) + lineno++; + newline_flag = 0; + if (count <= 0) + return EOF; + const unsigned char *p = eptr; + if (p >= bp->s + char_block::SIZE) { + bp = bp->next; + p = bp->s; + } + if (*p == '\0') { + if (np) + *np = nd->copy(); + nd = nd->next; + eptr = ptr = p + 1; + count--; + return 0; + } + const unsigned char *e = bp->s + char_block::SIZE; + if (e - p > count) + e = p + count; + ptr = p; + while (p < e) { + unsigned char c = *p; + if (c == '\n' || c == ESCAPE_NEWLINE) { + newline_flag = 1; + p++; + break; + } + if (c == '\0') + break; + p++; + } + eptr = p; + count -= p - ptr; + return *ptr++; +} + +int string_iterator::peek() +{ + if (count <= 0) + return EOF; + const unsigned char *p = eptr; + if (p >= bp->s + char_block::SIZE) { + p = bp->next->s; + } + return *p; +} + +int string_iterator::get_location(int allow_macro, + const char **filep, int *linep) +{ + if (!allow_macro) + return 0; + if (mac.filename == 0) + return 0; + *filep = mac.filename; + *linep = mac.lineno + lineno - 1; + return 1; +} + +void string_iterator::backtrace() +{ + if (mac.filename) { + errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1); + if (how_invoked) { + if (!nm.is_null()) + errprint(": %1 `%2'\n", how_invoked, nm.contents()); + else + errprint(": %1\n", how_invoked); + } + else + errprint("\n"); + } +} + +class temp_iterator : public input_iterator { + unsigned char *base; + temp_iterator(const char *, int len); +public: + ~temp_iterator(); + friend input_iterator *make_temp_iterator(const char *); +}; + +#ifdef __GNUG__ +inline +#endif +temp_iterator::temp_iterator(const char *s, int len) +{ + base = new unsigned char[len]; + memcpy(base, s, len); + ptr = base; + eptr = base + len; +} + +temp_iterator::~temp_iterator() +{ + a_delete base; +} + +class small_temp_iterator : public input_iterator { +private: + small_temp_iterator(const char *, int); + ~small_temp_iterator(); + enum { BLOCK = 16 }; + static small_temp_iterator *free_list; + void *operator new(size_t); + void operator delete(void *); + enum { SIZE = 12 }; + unsigned char buf[SIZE]; + friend input_iterator *make_temp_iterator(const char *); +}; + +small_temp_iterator *small_temp_iterator::free_list = 0; + +void *small_temp_iterator::operator new(size_t n) +{ + assert(n == sizeof(small_temp_iterator)); + if (!free_list) { + free_list = + (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK]; + for (int i = 0; i < BLOCK - 1; i++) + free_list[i].next = free_list + i + 1; + free_list[BLOCK-1].next = 0; + } + small_temp_iterator *p = free_list; + free_list = (small_temp_iterator *)(free_list->next); + p->next = 0; + return p; +} + +#ifdef __GNUG__ +inline +#endif +void small_temp_iterator::operator delete(void *p) +{ + if (p) { + ((small_temp_iterator *)p)->next = free_list; + free_list = (small_temp_iterator *)p; + } +} + +small_temp_iterator::~small_temp_iterator() +{ +} + +#ifdef __GNUG__ +inline +#endif +small_temp_iterator::small_temp_iterator(const char *s, int len) +{ + for (int i = 0; i < len; i++) + buf[i] = s[i]; + ptr = buf; + eptr = buf + len; +} + +input_iterator *make_temp_iterator(const char *s) +{ + if (s == 0) + return new small_temp_iterator(s, 0); + else { + int n = strlen(s); + if (n <= small_temp_iterator::SIZE) + return new small_temp_iterator(s, n); + else + return new temp_iterator(s, n); + } +} + +// this is used when macros with arguments are interpolated + +struct arg_list { + macro mac; + arg_list *next; + arg_list(const macro &); + ~arg_list(); +}; + +arg_list::arg_list(const macro &m) : mac(m), next(0) +{ +} + +arg_list::~arg_list() +{ +} + +class macro_iterator : public string_iterator { + arg_list *args; + int argc; +public: + macro_iterator(symbol, macro &, const char *how_invoked = "macro"); + macro_iterator(); + ~macro_iterator(); + int has_args() { return 1; } + input_iterator *get_arg(int i); + int nargs() { return argc; } + void add_arg(const macro &m); + void shift(int n); + int is_macro() { return 1; } +}; + +input_iterator *macro_iterator::get_arg(int i) +{ + if (i == 0) + return make_temp_iterator(nm.contents()); + if (i > 0 && i <= argc) { + arg_list *p = args; + for (int j = 1; j < i; j++) { + assert(p != 0); + p = p->next; + } + return new string_iterator(p->mac); + } + else + return 0; +} + +void macro_iterator::add_arg(const macro &m) +{ + arg_list **p; + for (p = &args; *p; p = &((*p)->next)) + ; + *p = new arg_list(m); + ++argc; +} + +void macro_iterator::shift(int n) +{ + while (n > 0 && argc > 0) { + arg_list *tem = args; + args = args->next; + delete tem; + --argc; + --n; + } +} + +// This gets used by eg .if '\?xxx\?''. + +int operator==(const macro &m1, const macro &m2) +{ + if (m1.len != m2.len) + return 0; + string_iterator iter1(m1); + string_iterator iter2(m2); + int n = m1.len; + while (--n >= 0) { + node *nd1 = 0; + int c1 = iter1.get(&nd1); + assert(c1 != EOF); + node *nd2 = 0; + int c2 = iter2.get(&nd2); + assert(c2 != EOF); + if (c1 != c2) { + if (c1 == 0) + delete nd1; + else if (c2 == 0) + delete nd2; + return 0; + } + if (c1 == 0) { + assert(nd1 != 0); + assert(nd2 != 0); + int are_same = nd1->type() == nd2->type() && nd1->same(nd2); + delete nd1; + delete nd2; + if (!are_same) + return 0; + } + } + return 1; +} + +static void interpolate_macro(symbol nm) +{ + request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); + if (p == 0) { + int warned = 0; + const char *s = nm.contents(); + if (strlen(s) > 2) { + request_or_macro *r; + char buf[3]; + buf[0] = s[0]; + buf[1] = s[1]; + buf[2] = '\0'; + r = (request_or_macro *)request_dictionary.lookup(symbol(buf)); + if (r) { + macro *m = r->to_macro(); + if (!m || !m->empty()) + warned = warning(WARN_SPACE, + "macro `%1' not defined " + "(probably missing space after `%2')", + nm.contents(), buf); + } + } + if (!warned) { + warning(WARN_MAC, "macro `%1' not defined", nm.contents()); + p = new macro; + request_dictionary.define(nm, p); + } + } + if (p) + p->invoke(nm); + else { + skip_line(); + return; + } +} + +static void decode_args(macro_iterator *mi) +{ + if (!tok.newline() && !tok.eof()) { + node *n; + int c = get_copy(&n); + for (;;) { + while (c == ' ') + c = get_copy(&n); + if (c == '\n' || c == EOF) + 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)) { + if (quote_input_level > 0 && c == '\"' + && (compatible_flag + || 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 macro argument"); + done_tab_warning = 1; + } + arg.append(c); + } + c = get_copy(&n); + } + } + mi->add_arg(arg); + } + } +} + +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); + decode_args(mi); + input_stack::push(mi); + tok.next(); +} + +macro *macro::to_macro() +{ + return this; +} + +int macro::empty() +{ + return empty_macro == 1; +} + +macro_iterator::macro_iterator(symbol s, macro &m, const char *how_invoked) +: string_iterator(m, how_invoked, s), args(0), argc(0) +{ +} + +macro_iterator::macro_iterator() : args(0), argc(0) +{ +} + +macro_iterator::~macro_iterator() +{ + while (args != 0) { + arg_list *tem = args; + args = args->next; + delete tem; + } +} + +dictionary composite_dictionary(17); + +void composite_request() +{ + symbol from = get_name(1); + if (!from.is_null()) { + const char *from_gn = glyph_name_to_unicode(from.contents()); + if (!from_gn) { + from_gn = check_unicode_name(from.contents()); + if (!from_gn) { + error("invalid composite glyph name `%1'", from.contents()); + skip_line(); + return; + } + } + const char *from_decomposed = decompose_unicode(from_gn); + if (from_decomposed) + from_gn = &from_decomposed[1]; + symbol to = get_name(1); + if (to.is_null()) + composite_dictionary.remove(symbol(from_gn)); + else { + const char *to_gn = glyph_name_to_unicode(to.contents()); + if (!to_gn) { + to_gn = check_unicode_name(to.contents()); + if (!to_gn) { + error("invalid composite glyph name `%1'", to.contents()); + skip_line(); + return; + } + } + const char *to_decomposed = decompose_unicode(to_gn); + if (to_decomposed) + to_gn = &to_decomposed[1]; + if (strcmp(from_gn, to_gn) == 0) + composite_dictionary.remove(symbol(from_gn)); + else + (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn); + } + } + skip_line(); +} + +static symbol composite_glyph_name(symbol nm) +{ + macro_iterator *mi = new macro_iterator(); + decode_string_args(mi); + input_stack::push(mi); + const char *gn = glyph_name_to_unicode(nm.contents()); + if (!gn) { + gn = check_unicode_name(nm.contents()); + if (!gn) { + error("invalid base glyph `%1' in composite glyph name", nm.contents()); + return EMPTY_SYMBOL; + } + } + const char *gn_decomposed = decompose_unicode(gn); + string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn); + string gl; + int n = input_stack::nargs(); + for (int i = 1; i <= n; i++) { + glyph_name += '_'; + input_iterator *p = input_stack::get_arg(i); + gl.clear(); + int c; + while ((c = p->get(0)) != EOF) + gl += c; + gl += '\0'; + const char *u = glyph_name_to_unicode(gl.contents()); + if (!u) { + u = check_unicode_name(gl.contents()); + if (!u) { + error("invalid component `%1' in composite glyph name", + gl.contents()); + return EMPTY_SYMBOL; + } + } + const char *decomposed = decompose_unicode(u); + if (decomposed) + u = &decomposed[1]; + void *mapped_composite = composite_dictionary.lookup(symbol(u)); + if (mapped_composite) + u = (const char *)mapped_composite; + glyph_name += u; + } + glyph_name += '\0'; + const char *groff_gn = unicode_to_glyph_name(glyph_name.contents()); + if (groff_gn) + return symbol(groff_gn); + gl.clear(); + gl += 'u'; + gl += glyph_name; + return symbol(gl.contents()); +} + +int trap_sprung_flag = 0; +int postpone_traps_flag = 0; +symbol postponed_trap; + +void spring_trap(symbol nm) +{ + assert(!nm.is_null()); + trap_sprung_flag = 1; + if (postpone_traps_flag) { + postponed_trap = nm; + return; + } + static char buf[2] = { BEGIN_TRAP, 0 }; + static char buf2[2] = { END_TRAP, '\0' }; + input_stack::push(make_temp_iterator(buf2)); + request_or_macro *p = lookup_request(nm); + macro *m = p->to_macro(); + if (m) + input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro")); + else + error("you can't invoke a request with a trap"); + input_stack::push(make_temp_iterator(buf)); +} + +void postpone_traps() +{ + postpone_traps_flag = 1; +} + +int unpostpone_traps() +{ + postpone_traps_flag = 0; + if (!postponed_trap.is_null()) { + spring_trap(postponed_trap); + postponed_trap = NULL_SYMBOL; + return 1; + } + else + return 0; +} + +void read_request() +{ + macro_iterator *mi = new macro_iterator; + int reading_from_terminal = isatty(fileno(stdin)); + int had_prompt = 0; + if (!tok.newline() && !tok.eof()) { + int c = get_copy(0); + while (c == ' ') + c = get_copy(0); + while (c != EOF && c != '\n' && c != ' ') { + if (!invalid_input_char(c)) { + if (reading_from_terminal) + fputc(c, stderr); + had_prompt = 1; + } + c = get_copy(0); + } + if (c == ' ') { + tok.make_space(); + decode_args(mi); + } + } + if (reading_from_terminal) { + fputc(had_prompt ? ':' : '\a', stderr); + fflush(stderr); + } + input_stack::push(mi); + macro mac; + int nl = 0; + int c; + while ((c = getchar()) != EOF) { + if (invalid_input_char(c)) + warning(WARN_INPUT, "invalid input character code %1", int(c)); + else { + if (c == '\n') { + if (nl) + break; + else + nl = 1; + } + else + nl = 0; + mac.append(c); + } + } + if (reading_from_terminal) + clearerr(stdin); + input_stack::push(new string_iterator(mac)); + tok.next(); +} + +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; + int c; + nm = get_name(1); + if (nm.is_null()) { + skip_line(); + return; + } + if (tok.newline()) + c = '\n'; + else if (tok.tab()) + c = '\t'; + else if (!tok.space()) { + error("bad string definition"); + skip_line(); + return; + } + else + c = get_copy(&n); + while (c == ' ') + c = get_copy(&n); + if (c == '"') + c = get_copy(&n); + macro mac; + request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); + macro *mm = rm ? rm->to_macro() : 0; + 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); + else + mac.append((unsigned char)c); + c = get_copy(&n); + } + if (!mm) { + 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(DEFINE_NORMAL, CALLING_NORMAL); +} + +void define_nocomp_string() +{ + do_define_string(DEFINE_NORMAL, CALLING_DISABLE_COMP); +} + +void append_string() +{ + do_define_string(DEFINE_APPEND, CALLING_NORMAL); +} + +void append_nocomp_string() +{ + do_define_string(DEFINE_APPEND, CALLING_DISABLE_COMP); +} + +void do_define_character(char_mode mode, const char *font_name) +{ + node *n; + int c; + tok.skip(); + charinfo *ci = tok.get_char(1); + if (ci == 0) { + skip_line(); + return; + } + if (font_name) { + string s(font_name); + s += ' '; + s += ci->nm.contents(); + s += '\0'; + ci = get_charinfo(symbol(s.contents())); + } + tok.next(); + if (tok.newline()) + c = '\n'; + else if (tok.tab()) + c = '\t'; + else if (!tok.space()) { + error("bad character definition"); + skip_line(); + return; + } + else + c = get_copy(&n); + while (c == ' ' || c == '\t') + c = get_copy(&n); + if (c == '"') + c = get_copy(&n); + macro *m = new macro; + while (c != '\n' && c != EOF) { + if (c == 0) + m->append(n); + else + m->append((unsigned char)c); + c = get_copy(&n); + } + m = ci->setx_macro(m, mode); + if (m) + delete m; + tok.next(); +} + +void define_character() +{ + do_define_character(CHAR_NORMAL); +} + +void define_fallback_character() +{ + do_define_character(CHAR_FALLBACK); +} + +void define_special_character() +{ + do_define_character(CHAR_SPECIAL); +} + +static void remove_character() +{ + tok.skip(); + while (!tok.newline() && !tok.eof()) { + if (!tok.space() && !tok.tab()) { + charinfo *ci = tok.get_char(1); + if (!ci) + break; + macro *m = ci->set_macro(0); + if (m) + delete m; + } + tok.next(); + } + skip_line(); +} + +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 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 +the same input level as the opening quote. */ + +class end_quote_iterator : public input_iterator { + unsigned char buf[1]; +public: + end_quote_iterator(); + ~end_quote_iterator() { } + int internal_level() { return 2; } +}; + +end_quote_iterator::end_quote_iterator() +{ + buf[0] = '"'; + ptr = buf; + eptr = buf + 1; +} + +static void interpolate_arg(symbol nm) +{ + const char *s = nm.contents(); + if (!s || *s == '\0') + copy_mode_error("missing argument name"); + else if (s[1] == 0 && csdigit(s[0])) + input_stack::push(input_stack::get_arg(s[0] - '0')); + else if (s[0] == '*' && s[1] == '\0') { + for (int i = input_stack::nargs(); i > 0; i--) { + input_stack::push(input_stack::get_arg(i)); + if (i != 1) + input_stack::push(make_temp_iterator(" ")); + } + } + else if (s[0] == '@' && s[1] == '\0') { + for (int i = input_stack::nargs(); i > 0; i--) { + input_stack::push(new end_quote_iterator); + input_stack::push(input_stack::get_arg(i)); + input_stack::push(make_temp_iterator(i == 1 ? "\"" : " \"")); + } + } + else { + const char *p; + for (p = s; *p && csdigit(*p); p++) + ; + if (*p) + copy_mode_error("bad argument name `%1'", s); + else + input_stack::push(input_stack::get_arg(atoi(s))); + } +} + +void handle_first_page_transition() +{ + push_token(tok); + topdiv->begin_page(); +} + +// We push back a token by wrapping it up in a token_node, and +// wrapping that up in a string_iterator. + +static void push_token(const token &t) +{ + macro m; + m.append(new token_node(t)); + input_stack::push(new string_iterator(m)); +} + +void push_page_ejector() +{ + static char buf[2] = { PAGE_EJECTOR, '\0' }; + input_stack::push(make_temp_iterator(buf)); +} + +void handle_initial_request(unsigned char code) +{ + char buf[2]; + buf[0] = code; + buf[1] = '\0'; + macro mac; + mac.append(new token_node(tok)); + input_stack::push(new string_iterator(mac)); + input_stack::push(make_temp_iterator(buf)); + topdiv->begin_page(); + tok.next(); +} + +void handle_initial_title() +{ + handle_initial_request(TITLE_REQUEST); +} + +// this should be local to define_macro, but cfront 1.2 doesn't support that +static symbol dot_symbol("."); + +void do_define_macro(define_mode mode, calling_mode calling) +{ + symbol nm, term; + if (calling == CALLING_INDIRECT) { + symbol temp1 = get_name(1); + if (temp1.is_null()) { + skip_line(); + return; + } + symbol temp2 = get_name(); + input_stack::push(make_temp_iterator("\n")); + if (!temp2.is_null()) { + interpolate_string(temp2); + input_stack::push(make_temp_iterator(" ")); + } + interpolate_string(temp1); + input_stack::push(make_temp_iterator(" ")); + tok.next(); + } + if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { + nm = get_name(1); + if (nm.is_null()) { + skip_line(); + return; + } + } + term = get_name(); // the request that terminates the definition + if (term.is_null()) + term = dot_symbol; + while (!tok.newline() && !tok.eof()) + tok.next(); + const char *start_filename; + int start_lineno; + int have_start_location = input_stack::get_location(0, &start_filename, + &start_lineno); + node *n; + // doing this here makes the line numbers come out right + int c = get_copy(&n, 1); + macro mac; + macro *mm = 0; + if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { + request_or_macro *rm = + (request_or_macro *)request_dictionary.lookup(nm); + if (rm) + mm = rm->to_macro(); + if (mm && mode == DEFINE_APPEND) + mac = *mm; + } + int bol = 1; + if (calling == CALLING_DISABLE_COMP) + mac.append(COMPATIBLE_SAVE); + for (;;) { + while (c == ESCAPE_NEWLINE) { + if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) + mac.append(c); + c = get_copy(&n, 1); + } + if (bol && c == '.') { + const char *s = term.contents(); + int d = 0; + // see if it matches term + 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) + || (d = get_copy(&n)) == ' ' + || d == '\n')) { // we found it + if (d == '\n') + tok.make_newline(); + else + tok.make_space(); + if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { + if (!mm) { + mm = new macro; + request_dictionary.define(nm, mm); + } + if (calling == CALLING_DISABLE_COMP) + mac.append(COMPATIBLE_RESTORE); + *mm = mac; + } + if (term != dot_symbol) { + ignoring = 0; + interpolate_macro(term); + } + else + skip_line(); + return; + } + if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) { + mac.append(c); + for (int j = 0; j < i; j++) + mac.append(s[j]); + } + c = d; + } + if (c == EOF) { + if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { + if (have_start_location) + error_with_file_and_line(start_filename, start_lineno, + "end of file while defining macro `%1'", + nm.contents()); + else + error("end of file while defining macro `%1'", nm.contents()); + } + else { + if (have_start_location) + error_with_file_and_line(start_filename, start_lineno, + "end of file while ignoring input lines"); + else + error("end of file while ignoring input lines"); + } + tok.next(); + return; + } + if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) { + if (c == 0) + mac.append(n); + else + mac.append(c); + } + bol = (c == '\n'); + c = get_copy(&n, 1); + } +} + +void define_macro() +{ + do_define_macro(DEFINE_NORMAL, CALLING_NORMAL); +} + +void define_nocomp_macro() +{ + do_define_macro(DEFINE_NORMAL, CALLING_DISABLE_COMP); +} + +void define_indirect_macro() +{ + do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT); +} + +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); +} + +void ignore() +{ + ignoring = 1; + do_define_macro(DEFINE_IGNORE, CALLING_NORMAL); + ignoring = 0; +} + +void remove_macro() +{ + for (;;) { + symbol s = get_name(); + if (s.is_null()) + break; + request_dictionary.remove(s); + } + skip_line(); +} + +void rename_macro() +{ + symbol s1 = get_name(1); + if (!s1.is_null()) { + symbol s2 = get_name(1); + if (!s2.is_null()) + request_dictionary.rename(s1, s2); + } + skip_line(); +} + +void alias_macro() +{ + symbol s1 = get_name(1); + if (!s1.is_null()) { + symbol s2 = get_name(1); + if (!s2.is_null()) { + if (!request_dictionary.alias(s1, s2)) + warning(WARN_MAC, "macro `%1' not defined", s2.contents()); + } + } + skip_line(); +} + +void chop_macro() +{ + symbol s = get_name(1); + if (!s.is_null()) { + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) + error("cannot chop request"); + else if (m->empty()) + error("cannot chop empty macro"); + 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_request() +{ + 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 apply `substring' on a request"); + else { + int end = -1; + if (!has_arg() || get_integer(&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 >= 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; + } + 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_request() +{ + symbol ret; + ret = get_name(1); + if (ret.is_null()) { + skip_line(); + return; + } + int c; + node *n; + if (tok.newline()) + c = '\n'; + else if (tok.tab()) + c = '\t'; + else if (!tok.space()) { + error("bad string definition"); + skip_line(); + return; + } + else + c = get_copy(&n); + while (c == ' ') + c = get_copy(&n); + if (c == '"') + c = get_copy(&n); + int len = 0; + while (c != '\n' && c != EOF) { + ++len; + c = get_copy(&n); + } + 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() +{ + symbol s = get_name(1); + if (!s.is_null()) { + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) + error("cannot asciify request"); + else { + macro am; + string_iterator iter(*m); + for (;;) { + node *nd; + int c = iter.get(&nd); + if (c == EOF) + break; + if (c != 0) + am.append(c); + else + nd->asciify(&am); + } + *m = am; + } + } + skip_line(); +} + +void unformat_macro() +{ + symbol s = get_name(1); + if (!s.is_null()) { + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) + error("cannot unformat request"); + else { + macro am; + string_iterator iter(*m); + for (;;) { + node *nd; + int c = iter.get(&nd); + if (c == EOF) + break; + if (c != 0) + am.append(c); + else { + if (nd->set_unformat_flag()) + am.append(nd); + } + } + *m = am; + } + } + skip_line(); +} + +static void interpolate_environment_variable(symbol nm) +{ + const char *s = getenv(nm.contents()); + if (s && *s) + input_stack::push(make_temp_iterator(s)); +} + +void interpolate_number_reg(symbol nm, int inc) +{ + reg *r = lookup_number_reg(nm); + if (inc < 0) + r->decrement(); + else if (inc > 0) + r->increment(); + input_stack::push(make_temp_iterator(r->get_string())); +} + +static void interpolate_number_format(symbol nm) +{ + reg *r = (reg *)number_reg_dictionary.lookup(nm); + if (r) + input_stack::push(make_temp_iterator(r->get_format())); +} + +static int get_delim_number(units *n, int si, int prev_value) +{ + token start; + start.next(); + if (start.delimiter(1)) { + tok.next(); + if (get_number(n, si, prev_value)) { + if (start != tok) + warning(WARN_DELIM, "closing delimiter does not match"); + return 1; + } + } + return 0; +} + +static int get_delim_number(units *n, int si) +{ + token start; + start.next(); + if (start.delimiter(1)) { + tok.next(); + if (get_number(n, si)) { + if (start != tok) + warning(WARN_DELIM, "closing delimiter does not match"); + return 1; + } + } + return 0; +} + +static int get_line_arg(units *n, int si, charinfo **cp) +{ + token start; + start.next(); + 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; +} + +static int read_size(int *x) +{ + tok.next(); + int c = tok.ch(); + int inc = 0; + if (c == '-') { + inc = -1; + tok.next(); + c = tok.ch(); + } + else if (c == '+') { + inc = 1; + tok.next(); + c = tok.ch(); + } + int val; + int bad = 0; + if (c == '(') { + tok.next(); + c = tok.ch(); + if (!inc) { + // allow an increment either before or after the left parenthesis + if (c == '-') { + inc = -1; + tok.next(); + c = tok.ch(); + } + else if (c == '+') { + inc = 1; + tok.next(); + c = tok.ch(); + } + } + if (!csdigit(c)) + bad = 1; + else { + val = c - '0'; + tok.next(); + c = tok.ch(); + if (!csdigit(c)) + bad = 1; + else { + val = val*10 + (c - '0'); + val *= sizescale; + } + } + } + else if (csdigit(c)) { + val = c - '0'; + if (!inc && c != '0' && c < '4') { + tok.next(); + c = tok.ch(); + if (!csdigit(c)) + bad = 1; + else + val = val*10 + (c - '0'); + } + val *= sizescale; + } + else if (!tok.delimiter(1)) + return 0; + else { + token start(tok); + tok.next(); + if (!(inc + ? get_number(&val, 'z') + : get_number(&val, 'z', curenv->get_requested_point_size()))) + return 0; + if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) { + if (start.ch() == '[') + error("missing `]'"); + else + error("missing closing delimiter"); + return 0; + } + } + if (!bad) { + switch (inc) { + case 0: + if (val == 0) { + // special case -- \s[0] and \s0 means to revert to previous size + *x = 0; + return 1; + } + *x = val; + break; + case 1: + *x = curenv->get_requested_point_size() + val; + break; + case -1: + *x = curenv->get_requested_point_size() - val; + break; + default: + assert(0); + } + if (*x <= 0) { + warning(WARN_RANGE, + "\\s request results in non-positive point size; set to 1"); + *x = 1; + } + return 1; + } + else { + error("bad digit in point size"); + return 0; + } +} + +static symbol get_delim_name() +{ + token start; + start.next(); + if (start.eof()) { + error("end of input at start of delimited name"); + return NULL_SYMBOL; + } + if (start.newline()) { + error("can't delimit 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 == start + && (compatible_flag || 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 name"); + return NULL_SYMBOL; + } + else + return symbol(buf); + } + else { + symbol s(buf); + a_delete buf; + return s; + } +} + +// Implement \R + +static void do_register() +{ + token start; + start.next(); + if (!start.delimiter(1)) + return; + tok.next(); + symbol nm = get_long_name(1); + if (nm.is_null()) + return; + while (tok.space()) + tok.next(); + reg *r = (reg *)number_reg_dictionary.lookup(nm); + int prev_value; + if (!r || !r->get_value(&prev_value)) + prev_value = 0; + int val; + if (!get_number(&val, 'u', prev_value)) + return; + if (start != tok) + warning(WARN_DELIM, "closing delimiter does not match"); + if (r) + r->set_value(val); + else + set_number_reg(nm, val); +} + +// this implements the \w escape sequence + +static void do_width() +{ + token start; + start.next(); + int start_level = input_stack::get_level(); + environment env(curenv); + environment *oldenv = curenv; + curenv = &env; + for (;;) { + tok.next(); + if (tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + break; + } + if (tok.newline()) { + warning(WARN_DELIM, "missing closing delimiter"); + input_stack::push(make_temp_iterator("\n")); + break; + } + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) + break; + tok.process(); + } + env.wrap_up_tab(); + units x = env.get_input_line_position().to_units(); + input_stack::push(make_temp_iterator(i_to_a(x))); + env.width_registers(); + curenv = oldenv; + have_input = 0; +} + +charinfo *page_character; + +void set_page_character() +{ + page_character = get_optional_char(); + skip_line(); +} + +static const symbol percent_symbol("%"); + +void read_title_parts(node **part, hunits *part_width) +{ + tok.skip(); + if (tok.newline() || tok.eof()) + return; + token start(tok); + int start_level = input_stack::get_level(); + tok.next(); + for (int i = 0; i < 3; i++) { + while (!tok.newline() && !tok.eof()) { + if (tok == start + && (compatible_flag || input_stack::get_level() == start_level)) { + tok.next(); + break; + } + if (page_character != 0 && tok.get_char() == page_character) + interpolate_number_reg(percent_symbol, 0); + else + tok.process(); + tok.next(); + } + curenv->wrap_up_tab(); + part_width[i] = curenv->get_input_line_position(); + part[i] = curenv->extract_output_line(); + } + while (!tok.newline() && !tok.eof()) + tok.next(); +} + +class non_interpreted_node : public node { + macro mac; +public: + non_interpreted_node(const macro &); + int interpret(macro *); + node *copy(); + int same(node *); + const char *type(); + int force_tprint(); +}; + +non_interpreted_node::non_interpreted_node(const macro &m) : mac(m) +{ +} + +int non_interpreted_node::same(node *nd) +{ + return mac == ((non_interpreted_node *)nd)->mac; +} + +const char *non_interpreted_node::type() +{ + return "non_interpreted_node"; +} + +int non_interpreted_node::force_tprint() +{ + return 0; +} + +node *non_interpreted_node::copy() +{ + return new non_interpreted_node(mac); +} + +int non_interpreted_node::interpret(macro *m) +{ + string_iterator si(mac); + node *n; + for (;;) { + int c = si.get(&n); + if (c == EOF) + break; + if (c == 0) + m->append(n); + else + m->append(c); + } + return 1; +} + +static node *do_non_interpreted() +{ + node *n; + int c; + macro mac; + while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n') + if (c == 0) + mac.append(n); + else + mac.append(c); + if (c == EOF || c == '\n') { + error("missing \\?"); + return 0; + } + return new non_interpreted_node(mac); +} + +static void encode_char(macro *mac, char c) +{ + if (c == '\0') { + if ((font::use_charnames_in_special) && tok.special()) { + charinfo *ci = tok.get_char(1); + const char *s = ci->get_symbol()->contents(); + if (s[0] != (char)0) { + mac->append('\\'); + mac->append('('); + int i = 0; + while (s[i] != (char)0) { + mac->append(s[i]); + i++; + } + mac->append('\\'); + mac->append(')'); + } + } + 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 == '\\')) { + /* + * add escape escape sequence + */ + mac->append(c); + } + mac->append(c); + } +} + +node *do_special() +{ + token start; + start.next(); + int start_level = input_stack::get_level(); + macro mac; + for (tok.next(); + tok != start || input_stack::get_level() != start_level; + tok.next()) { + if (tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + return 0; + } + if (tok.newline()) { + input_stack::push(make_temp_iterator("\n")); + warning(WARN_DELIM, "missing closing delimiter"); + break; + } + unsigned char c; + if (tok.space()) + c = ' '; + else if (tok.tab()) + c = '\t'; + else if (tok.leader()) + c = '\001'; + else if (tok.backspace()) + c = '\b'; + else + c = tok.ch(); + encode_char(&mac, c); + } + return new special_node(mac); +} + +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(); +} + +extern int image_no; // from node.cpp + +static node *do_suppress(symbol nm) +{ + if (nm.is_null() || nm.is_empty()) { + error("expecting an argument to escape \\O"); + return 0; + } + const char *s = nm.contents(); + switch (*s) { + case '0': + if (begin_level == 0) + // suppress generation of glyphs + return new suppress_node(0, 0); + break; + case '1': + if (begin_level == 0) + // enable generation of glyphs + return new suppress_node(1, 0); + break; + case '2': + if (begin_level == 0) + return new suppress_node(1, 1); + break; + case '3': + begin_level++; + break; + case '4': + begin_level--; + break; + 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); + } + break; + default: + error("`%1' is an invalid argument to \\O", *s); + } + return 0; +} + +void special_node::tprint(troff_output_file *out) +{ + tprint_start(out); + string_iterator iter(mac); + for (;;) { + int c = iter.get(0); + if (c == EOF) + break; + for (const char *s = ::asciify(c); *s; s++) + tprint_char(out, *s); + } + tprint_end(out); +} + +int get_file_line(const char **filename, int *lineno) +{ + return input_stack::get_location(0, filename, lineno); +} + +void line_file() +{ + int n; + if (get_integer(&n)) { + const char *filename = 0; + if (has_arg()) { + symbol s = get_long_name(); + filename = s.contents(); + } + (void)input_stack::set_location(filename, n-1); + } + skip_line(); +} + +static int nroff_mode = 0; + +static void nroff_request() +{ + nroff_mode = 1; + skip_line(); +} + +static void troff_request() +{ + nroff_mode = 0; + skip_line(); +} + +static void skip_alternative() +{ + int level = 0; + // ensure that ``.if 0\{'' works as expected + if (tok.left_brace()) + level++; + int c; + for (;;) { + c = input_stack::get(0); + if (c == EOF) + break; + if (c == ESCAPE_LEFT_BRACE) + ++level; + else if (c == ESCAPE_RIGHT_BRACE) + --level; + else if (c == escape_char && escape_char > 0) + switch(input_stack::get(0)) { + case '{': + ++level; + break; + case '}': + --level; + break; + case '"': + while ((c = input_stack::get(0)) != '\n' && c != EOF) + ; + } + /* + Note that the level can properly be < 0, eg + + .if 1 \{\ + .if 0 \{\ + .\}\} + + So don't give an error message in this case. + */ + if (level <= 0 && c == '\n') + break; + } + tok.next(); +} + +static void begin_alternative() +{ + while (tok.space() || tok.left_brace()) + tok.next(); +} + +void nop_request() +{ + while (tok.space()) + tok.next(); +} + +static int_stack if_else_stack; + +int do_if_request() +{ + int invert = 0; + while (tok.space()) + tok.next(); + while (tok.ch() == '!') { + tok.next(); + invert = !invert; + } + int result; + unsigned char c = tok.ch(); + if (c == 't') { + tok.next(); + result = !nroff_mode; + } + else if (c == 'n') { + tok.next(); + result = nroff_mode; + } + else if (c == 'v') { + tok.next(); + result = 0; + } + else if (c == 'o') { + result = (topdiv->get_page_number() & 1); + tok.next(); + } + else if (c == 'e') { + result = !(topdiv->get_page_number() & 1); + tok.next(); + } + else if (c == 'd' || c == 'r') { + tok.next(); + symbol nm = get_name(1); + if (nm.is_null()) { + skip_alternative(); + return 0; + } + result = (c == 'd' + ? 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(); + charinfo *ci = tok.get_char(1); + if (ci == 0) { + skip_alternative(); + return 0; + } + result = character_exists(ci, curenv); + tok.next(); + } + else if (tok.space()) + result = 0; + else if (tok.delimiter()) { + token delim = tok; + int delim_level = input_stack::get_level(); + environment env1(curenv); + environment env2(curenv); + environment *oldenv = curenv; + curenv = &env1; + for (int i = 0; i < 2; i++) { + for (;;) { + tok.next(); + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + tok.next(); + curenv = oldenv; + return 0; + } + if (tok == delim + && (compatible_flag || input_stack::get_level() == delim_level)) + break; + tok.process(); + } + curenv = &env2; + } + node *n1 = env1.extract_output_line(); + node *n2 = env2.extract_output_line(); + result = same_node_list(n1, n2); + delete_node_list(n1); + delete_node_list(n2); + curenv = oldenv; + have_input = 0; + tok.next(); + } + else { + units n; + if (!get_number(&n, 'u')) { + skip_alternative(); + return 0; + } + else + result = n > 0; + } + if (invert) + result = !result; + if (result) + begin_alternative(); + else + skip_alternative(); + return result; +} + +void if_else_request() +{ + if_else_stack.push(do_if_request()); +} + +void if_request() +{ + do_if_request(); +} + +void else_request() +{ + if (if_else_stack.is_empty()) { + warning(WARN_EL, "unbalanced .el request"); + skip_alternative(); + } + else { + if (if_else_stack.pop()) + skip_alternative(); + else + begin_alternative(); + } +} + +static int while_depth = 0; +static int while_break_flag = 0; + +void while_request() +{ + macro mac; + int escaped = 0; + int level = 0; + mac.append(new token_node(tok)); + for (;;) { + node *n; + int c = input_stack::get(&n); + if (c == EOF) + break; + if (c == 0) { + escaped = 0; + mac.append(n); + } + else if (escaped) { + if (c == '{') + level += 1; + else if (c == '}') + level -= 1; + escaped = 0; + mac.append(c); + } + else { + if (c == ESCAPE_LEFT_BRACE) + level += 1; + else if (c == ESCAPE_RIGHT_BRACE) + level -= 1; + else if (c == escape_char) + escaped = 1; + mac.append(c); + if (c == '\n' && level <= 0) + break; + } + } + if (level != 0) + error("unbalanced \\{ \\}"); + else { + while_depth++; + input_stack::add_boundary(); + for (;;) { + input_stack::push(new string_iterator(mac, "while loop")); + tok.next(); + if (!do_if_request()) { + while (input_stack::get(0) != EOF) + ; + break; + } + process_input_stack(); + if (while_break_flag || input_stack::is_return_boundary()) { + while_break_flag = 0; + break; + } + } + input_stack::remove_boundary(); + while_depth--; + } + tok.next(); +} + +void while_break_request() +{ + if (!while_depth) { + error("no while loop"); + skip_line(); + } + else { + while_break_flag = 1; + while (input_stack::get(0) != EOF) + ; + tok.next(); + } +} + +void while_continue_request() +{ + if (!while_depth) { + error("no while loop"); + skip_line(); + } + else { + while (input_stack::get(0) != EOF) + ; + tok.next(); + } +} + +// .so + +void source() +{ + symbol nm = get_long_name(1); + if (nm.is_null()) + skip_line(); + else { + while (!tok.newline() && !tok.eof()) + tok.next(); + errno = 0; + FILE *fp = fopen(nm.contents(), "r"); + if (fp) + input_stack::push(new file_iterator(fp, nm.contents())); + else + error("can't open `%1': %2", nm.contents(), strerror(errno)); + tok.next(); + } +} + +// like .so but use popen() + +void pipe_source() +{ + if (safer_flag) { + error(".pso request not allowed in safer mode"); + skip_line(); + } + else { +#ifdef POPEN_MISSING + error("pipes not available on this system"); + skip_line(); +#else /* not POPEN_MISSING */ + if (tok.newline() || tok.eof()) + error("missing command"); + else { + int c; + 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(0)) { + const char *s = asciify(c); + int slen = strlen(s); + if (buf_used + slen + 1> buf_size) { + char *old_buf = buf; + int old_buf_size = buf_size; + buf_size *= 2; + buf = new char[buf_size]; + memcpy(buf, old_buf, old_buf_size); + a_delete old_buf; + } + strcpy(buf + buf_used, s); + buf_used += slen; + } + buf[buf_used] = '\0'; + errno = 0; + FILE *fp = popen(buf, POPEN_RT); + if (fp) + input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1)); + else + error("can't open pipe to process `%1': %2", buf, strerror(errno)); + a_delete buf; + } + tok.next(); +#endif /* not POPEN_MISSING */ + } +} + +// .psbb + +static int llx_reg_contents = 0; +static int lly_reg_contents = 0; +static int urx_reg_contents = 0; +static int ury_reg_contents = 0; + +struct bounding_box { + int llx, lly, urx, ury; +}; + +/* Parse the argument to a %%BoundingBox comment. Return 1 if it +contains 4 numbers, 2 if it contains (atend), 0 otherwise. */ + +int parse_bounding_box(char *p, bounding_box *bb) +{ + if (sscanf(p, "%d %d %d %d", + &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4) + return 1; + else { + /* The Document Structuring Conventions say that the numbers + should be integers. Unfortunately some broken applications + get this wrong. */ + double x1, x2, x3, x4; + if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) { + bb->llx = (int)x1; + bb->lly = (int)x2; + bb->urx = (int)x3; + bb->ury = (int)x4; + return 1; + } + else { + for (; *p == ' ' || *p == '\t'; p++) + ; + if (strncmp(p, "(atend)", 7) == 0) { + return 2; + } + } + } + bb->llx = bb->lly = bb->urx = bb->ury = 0; + return 0; +} + +// This version is taken from psrm.cpp + +#define PS_LINE_MAX 255 +cset white_space("\n\r \t"); + +int ps_get_line(char *buf, FILE *fp, const char* filename) +{ + int c = getc(fp); + if (c == EOF) { + buf[0] = '\0'; + return 0; + } + int i = 0; + int err = 0; + while (c != '\r' && c != '\n' && c != EOF) { + if ((c < 0x1b && !white_space(c)) || c == 0x7f) + error("invalid input character code %1 in `%2'", int(c), filename); + else if (i < PS_LINE_MAX) + buf[i++] = c; + else if (!err) { + err = 1; + error("PostScript file `%1' is non-conforming " + "because length of line exceeds 255", filename); + } + c = getc(fp); + } + buf[i++] = '\n'; + buf[i] = '\0'; + if (c == '\r') { + c = getc(fp); + if (c != EOF && c != '\n') + ungetc(c, fp); + } + return 1; +} + +inline void assign_registers(int llx, int lly, int urx, int ury) +{ + llx_reg_contents = llx; + lly_reg_contents = lly; + urx_reg_contents = urx; + ury_reg_contents = ury; +} + +void do_ps_file(FILE *fp, const char* filename) +{ + bounding_box bb; + int bb_at_end = 0; + char buf[PS_LINE_MAX]; + llx_reg_contents = lly_reg_contents = + urx_reg_contents = ury_reg_contents = 0; + if (!ps_get_line(buf, fp, filename)) { + error("`%1' is empty", filename); + return; + } + if (strncmp("%!PS-Adobe-", buf, 11) != 0) { + error("`%1' is not conforming to the Document Structuring Conventions", + filename); + return; + } + while (ps_get_line(buf, fp, filename) != 0) { + if (buf[0] != '%' || buf[1] != '%' + || strncmp(buf + 2, "EndComments", 11) == 0) + break; + if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { + int res = parse_bounding_box(buf + 14, &bb); + if (res == 1) { + assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); + return; + } + else if (res == 2) { + bb_at_end = 1; + break; + } + else { + error("the arguments to the %%%%BoundingBox comment in `%1' are bad", + filename); + return; + } + } + } + if (bb_at_end) { + long offset; + int last_try = 0; + /* in the trailer, the last BoundingBox comment is significant */ + for (offset = 512; !last_try; offset *= 2) { + int had_trailer = 0; + int got_bb = 0; + if (offset > 32768 || fseek(fp, -offset, 2) == -1) { + last_try = 1; + if (fseek(fp, 0L, 0) == -1) + break; + } + while (ps_get_line(buf, fp, filename) != 0) { + if (buf[0] == '%' && buf[1] == '%') { + if (!had_trailer) { + if (strncmp(buf + 2, "Trailer", 7) == 0) + had_trailer = 1; + } + else { + if (strncmp(buf + 2, "BoundingBox:", 12) == 0) { + int res = parse_bounding_box(buf + 14, &bb); + if (res == 1) + got_bb = 1; + else if (res == 2) { + error("`(atend)' not allowed in trailer of `%1'", filename); + return; + } + else { + error("the arguments to the %%%%BoundingBox comment in `%1' are bad", + filename); + return; + } + } + } + } + } + if (got_bb) { + assign_registers(bb.llx, bb.lly, bb.urx, bb.ury); + return; + } + } + } + error("%%%%BoundingBox comment not found in `%1'", filename); +} + +void ps_bbox_request() +{ + symbol nm = get_long_name(1); + if (nm.is_null()) + skip_line(); + else { + while (!tok.newline() && !tok.eof()) + tok.next(); + errno = 0; + // PS files might contain non-printable characters, such as ^Z + // and CRs not followed by an LF, so open them in binary mode. + FILE *fp = fopen(nm.contents(), FOPEN_RB); + if (fp) { + do_ps_file(fp, nm.contents()); + fclose(fp); + } + else + error("can't open `%1': %2", nm.contents(), strerror(errno)); + tok.next(); + } +} + +const char *asciify(int c) +{ + static char buf[3]; + buf[0] = escape_char == '\0' ? '\\' : escape_char; + buf[1] = buf[2] = '\0'; + switch (c) { + case ESCAPE_QUESTION: + buf[1] = '?'; + break; + case ESCAPE_AMPERSAND: + buf[1] = '&'; + break; + case ESCAPE_RIGHT_PARENTHESIS: + buf[1] = ')'; + break; + case ESCAPE_UNDERSCORE: + buf[1] = '_'; + break; + case ESCAPE_BAR: + buf[1] = '|'; + break; + case ESCAPE_CIRCUMFLEX: + buf[1] = '^'; + break; + case ESCAPE_LEFT_BRACE: + buf[1] = '{'; + break; + case ESCAPE_RIGHT_BRACE: + buf[1] = '}'; + break; + case ESCAPE_LEFT_QUOTE: + buf[1] = '`'; + break; + case ESCAPE_RIGHT_QUOTE: + buf[1] = '\''; + break; + case ESCAPE_HYPHEN: + buf[1] = '-'; + break; + case ESCAPE_BANG: + buf[1] = '!'; + break; + case ESCAPE_c: + buf[1] = 'c'; + break; + case ESCAPE_e: + buf[1] = 'e'; + break; + case ESCAPE_E: + buf[1] = 'E'; + break; + case ESCAPE_PERCENT: + buf[1] = '%'; + break; + case ESCAPE_SPACE: + buf[1] = ' '; + break; + case ESCAPE_TILDE: + buf[1] = '~'; + break; + case ESCAPE_COLON: + buf[1] = ':'; + break; + case COMPATIBLE_SAVE: + case COMPATIBLE_RESTORE: + buf[0] = '\0'; + break; + default: + if (invalid_input_char(c)) + buf[0] = '\0'; + else + buf[0] = c; + break; + } + return buf; +} + +const char *input_char_description(int c) +{ + switch (c) { + case '\n': + return "a newline character"; + case '\b': + return "a backspace character"; + case '\001': + return "a leader character"; + case '\t': + return "a tab character"; + case ' ': + return "a space character"; + case '\0': + return "a node"; + } + static char buf[sizeof("magic character code ") + 1 + INT_DIGITS]; + if (invalid_input_char(c)) { + const char *s = asciify(c); + if (*s) { + buf[0] = '`'; + strcpy(buf + 1, s); + strcat(buf, "'"); + return buf; + } + sprintf(buf, "magic character code %d", c); + return buf; + } + if (csprint(c)) { + buf[0] = '`'; + buf[1] = c; + buf[2] = '\''; + return buf; + } + sprintf(buf, "character code %d", c); + return buf; +} + +// .tm, .tm1, and .tmc + +void do_terminal(int newline, int string_like) +{ + if (!tok.newline() && !tok.eof()) { + int c; + for (;;) { + c = get_copy(0); + if (string_like && c == '"') { + c = get_copy(0); + break; + } + if (c != ' ' && c != '\t') + break; + } + for (; c != '\n' && c != EOF; c = get_copy(0)) + fputs(asciify(c), stderr); + } + if (newline) + fputc('\n', stderr); + fflush(stderr); + tok.next(); +} + +void terminal() +{ + do_terminal(1, 0); +} + +void terminal1() +{ + do_terminal(1, 1); +} + +void terminal_continue() +{ + do_terminal(0, 1); +} + +dictionary stream_dictionary(20); + +void do_open(int append) +{ + symbol stream = get_name(1); + if (!stream.is_null()) { + symbol filename = get_long_name(1); + if (!filename.is_null()) { + errno = 0; + FILE *fp = fopen(filename.contents(), append ? "a" : "w"); + if (!fp) { + error("can't open `%1' for %2: %3", + filename.contents(), + append ? "appending" : "writing", + strerror(errno)); + fp = (FILE *)stream_dictionary.remove(stream); + } + else + fp = (FILE *)stream_dictionary.lookup(stream, fp); + if (fp) + fclose(fp); + } + } + skip_line(); +} + +void open_request() +{ + if (safer_flag) { + error(".open request not allowed in safer mode"); + skip_line(); + } + else + do_open(0); +} + +void opena_request() +{ + if (safer_flag) { + error(".opena request not allowed in safer mode"); + skip_line(); + } + else + do_open(1); +} + +void close_request() +{ + symbol stream = get_name(1); + if (!stream.is_null()) { + FILE *fp = (FILE *)stream_dictionary.remove(stream); + if (!fp) + error("no stream named `%1'", stream.contents()); + else + fclose(fp); + } + skip_line(); +} + +// .write and .writec + +void do_write_request(int newline) +{ + symbol stream = get_name(1); + if (stream.is_null()) { + skip_line(); + return; + } + FILE *fp = (FILE *)stream_dictionary.lookup(stream); + if (!fp) { + error("no stream named `%1'", stream.contents()); + skip_line(); + return; + } + int c; + while ((c = get_copy(0)) == ' ') + ; + if (c == '"') + c = get_copy(0); + for (; c != '\n' && c != EOF; c = get_copy(0)) + fputs(asciify(c), 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); + if (stream.is_null()) { + skip_line(); + return; + } + FILE *fp = (FILE *)stream_dictionary.lookup(stream); + if (!fp) { + error("no stream named `%1'", stream.contents()); + skip_line(); + return; + } + symbol s = get_name(1); + if (s.is_null()) { + skip_line(); + return; + } + request_or_macro *p = lookup_request(s); + macro *m = p->to_macro(); + if (!m) + error("cannot write request"); + else { + string_iterator iter(*m); + for (;;) { + int c = iter.get(0); + if (c == EOF) + break; + fputs(asciify(c), fp); + } + fflush(fp); + } + 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]; + strcpy(buf, "char"); + for (int i = 0; i < 256; i++) { + strcpy(buf + 4, i_to_a(i)); + charset_table[i] = get_charinfo(symbol(buf)); + charset_table[i]->set_ascii_code(i); + if (csalpha(i)) + charset_table[i]->set_hyphenation_code(cmlower(i)); + } + charset_table['.']->set_flags(charinfo::ENDS_SENTENCE); + charset_table['?']->set_flags(charinfo::ENDS_SENTENCE); + charset_table['!']->set_flags(charinfo::ENDS_SENTENCE); + charset_table['-']->set_flags(charinfo::BREAK_AFTER); + charset_table['"']->set_flags(charinfo::TRANSPARENT); + charset_table['\'']->set_flags(charinfo::TRANSPARENT); + charset_table[')']->set_flags(charinfo::TRANSPARENT); + charset_table[']']->set_flags(charinfo::TRANSPARENT); + charset_table['*']->set_flags(charinfo::TRANSPARENT); + get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT); + get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT); + get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER); + get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); + get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); + get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); + get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); + get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY); + get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY); + page_character = charset_table['%']; +} + +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()) { + if (tok.space()) { + // This is a really bizarre troff feature. + tok.next(); + translate_space_to_dummy = tok.dummy(); + if (tok.newline() || tok.eof()) + break; + tok.next(); + continue; + } + charinfo *ci1 = tok.get_char(1); + if (ci1 == 0) + break; + tok.next(); + if (tok.newline() || tok.eof()) { + ci1->set_special_translation(charinfo::TRANSLATE_SPACE, + translate_transparent); + break; + } + if (tok.space()) + ci1->set_special_translation(charinfo::TRANSLATE_SPACE, + translate_transparent); + else if (tok.stretchable_space()) + ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE, + translate_transparent); + else if (tok.dummy()) + ci1->set_special_translation(charinfo::TRANSLATE_DUMMY, + translate_transparent); + else if (tok.hyphen_indicator()) + ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR, + translate_transparent); + else { + charinfo *ci2 = tok.get_char(1); + if (ci2 == 0) + break; + if (ci1 == ci2) + ci1->set_translation(0, translate_transparent, translate_input); + else + ci1->set_translation(ci2, translate_transparent, translate_input); + } + tok.next(); + } + skip_line(); +} + +void translate() +{ + do_translate(1, 0); +} + +void translate_no_transparent() +{ + do_translate(0, 0); +} + +void translate_input() +{ + do_translate(1, 1); +} + +void char_flags() +{ + int flags; + if (get_integer(&flags)) + while (has_arg()) { + charinfo *ci = tok.get_char(1); + if (ci) { + charinfo *tem = ci->get_translation(); + if (tem) + ci = tem; + ci->set_flags(flags); + } + tok.next(); + } + skip_line(); +} + +void hyphenation_code() +{ + tok.skip(); + while (!tok.newline() && !tok.eof()) { + charinfo *ci = tok.get_char(1); + if (ci == 0) + break; + tok.next(); + tok.skip(); + unsigned char c = tok.ch(); + if (c == 0) { + error("hyphenation code must be ordinary character"); + break; + } + if (csdigit(c)) { + error("hyphenation code cannot be digit"); + 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) + return charset_table[c]; + if (type == TOKEN_SPECIAL) + return get_charinfo(nm); + if (type == TOKEN_NUMBERED_CHAR) + return get_charinfo_by_number(val); + if (type == TOKEN_ESCAPE) { + if (escape_char != 0) + return charset_table[escape_char]; + else { + error("`\\e' used while no current escape character"); + return 0; + } + } + if (required) { + if (type == TOKEN_EOF || type == TOKEN_NEWLINE) + warning(WARN_MISSING, "missing normal or special character"); + else + error("normal or special character expected (got %1)", description()); + } + return 0; +} + +charinfo *get_optional_char() +{ + while (tok.space()) + tok.next(); + charinfo *ci = tok.get_char(); + if (!ci) + check_missing_character(); + else + tok.next(); + return ci; +} + +void check_missing_character() +{ + if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab()) + error("normal or special character expected (got %1): " + "treated as missing", + tok.description()); +} + +// this is for \Z + +int token::add_to_node_list(node **pp) +{ + hunits w; + int s; + node *n = 0; + switch (type) { + case TOKEN_CHAR: + *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s); + break; + case TOKEN_DUMMY: + n = new dummy_node; + break; + case TOKEN_ESCAPE: + if (escape_char != 0) + *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s); + break; + case TOKEN_HYPHEN_INDICATOR: + *pp = (*pp)->add_discretionary_hyphen(); + break; + case TOKEN_ITALIC_CORRECTION: + *pp = (*pp)->add_italic_correction(&w); + break; + case TOKEN_LEFT_BRACE: + break; + case TOKEN_MARK_INPUT: + set_number_reg(nm, curenv->get_input_line_position().to_units()); + break; + case TOKEN_NODE: + n = nd; + nd = 0; + break; + case TOKEN_NUMBERED_CHAR: + *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s); + break; + case TOKEN_RIGHT_BRACE: + break; + case TOKEN_SPACE: + 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(), + 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; + } + if (n) { + n->next = *pp; + *pp = n; + } + return 1; +} + +void token::process() +{ + if (possibly_handle_first_page_transition()) + return; + switch (type) { + case TOKEN_BACKSPACE: + curenv->add_node(new hmotion_node(-curenv->get_space_width(), + curenv->get_fill_color())); + break; + case TOKEN_CHAR: + curenv->add_char(charset_table[c]); + break; + case TOKEN_DUMMY: + curenv->add_node(new dummy_node); + break; + case TOKEN_EMPTY: + assert(0); + break; + case TOKEN_EOF: + assert(0); + break; + case TOKEN_ESCAPE: + if (escape_char != 0) + curenv->add_char(charset_table[escape_char]); + break; + case TOKEN_BEGIN_TRAP: + case TOKEN_END_TRAP: + case TOKEN_PAGE_EJECTOR: + // these are all handled in process_input_stack() + break; + case TOKEN_HYPHEN_INDICATOR: + curenv->add_hyphen_indicator(); + break; + case TOKEN_INTERRUPT: + curenv->interrupt(); + break; + case TOKEN_ITALIC_CORRECTION: + curenv->add_italic_correction(); + break; + case TOKEN_LEADER: + curenv->handle_tab(1); + break; + case TOKEN_LEFT_BRACE: + break; + case TOKEN_MARK_INPUT: + set_number_reg(nm, curenv->get_input_line_position().to_units()); + break; + case TOKEN_NEWLINE: + curenv->newline(); + break; + case TOKEN_NODE: + curenv->add_node(nd); + nd = 0; + break; + case TOKEN_NUMBERED_CHAR: + curenv->add_char(get_charinfo_by_number(val)); + break; + case TOKEN_REQUEST: + // handled in process_input_stack() + break; + case TOKEN_RIGHT_BRACE: + break; + case TOKEN_SPACE: + curenv->space(); + break; + case TOKEN_SPECIAL: + curenv->add_char(get_charinfo(nm)); + break; + case TOKEN_SPREAD: + curenv->spread(); + break; + case TOKEN_STRETCHABLE_SPACE: + 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); + break; + case TOKEN_TRANSPARENT: + break; + 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); + } +} + +class nargs_reg : public reg { +public: + const char *get_string(); +}; + +const char *nargs_reg::get_string() +{ + return i_to_a(input_stack::nargs()); +} + +class lineno_reg : public reg { +public: + const char *get_string(); +}; + +const char *lineno_reg::get_string() +{ + int line; + const char *file; + if (!input_stack::get_location(0, &file, &line)) + line = 0; + return i_to_a(line); +} + +class writable_lineno_reg : public general_reg { +public: + writable_lineno_reg(); + void set_value(units); + int get_value(units *); +}; + +writable_lineno_reg::writable_lineno_reg() +{ +} + +int writable_lineno_reg::get_value(units *res) +{ + int line; + const char *file; + if (!input_stack::get_location(0, &file, &line)) + return 0; + *res = line; + return 1; +} + +void writable_lineno_reg::set_value(units n) +{ + input_stack::set_location(0, n); +} + +class filename_reg : public reg { +public: + const char *get_string(); +}; + +const char *filename_reg::get_string() +{ + int line; + const char *file; + if (input_stack::get_location(0, &file, &line)) + return file; + else + return 0; +} + +class constant_reg : public reg { + const char *s; +public: + constant_reg(const char *); + const char *get_string(); +}; + +constant_reg::constant_reg(const char *p) : s(p) +{ +} + +const char *constant_reg::get_string() +{ + return s; +} + +constant_int_reg::constant_int_reg(int *q) : p(q) +{ +} + +const char *constant_int_reg::get_string() +{ + return i_to_a(*p); +} + +void abort_request() +{ + int c; + if (tok.eof()) + c = EOF; + else if (tok.newline()) + c = '\n'; + else { + while ((c = get_copy(0)) == ' ') + ; + } + if (c == EOF || c == '\n') + fputs("User Abort.", stderr); + else { + for (; c != '\n' && c != EOF; c = get_copy(0)) + fputs(asciify(c), stderr); + } + fputc('\n', stderr); + cleanup_and_exit(1); +} + +char *read_string() +{ + int len = 256; + char *s = new char[len]; + int c; + while ((c = get_copy(0)) == ' ') + ; + int i = 0; + while (c != '\n' && c != EOF) { + if (!invalid_input_char(c)) { + if (i + 2 > len) { + char *tem = s; + s = new char[len*2]; + memcpy(s, tem, len); + len *= 2; + a_delete tem; + } + s[i++] = c; + } + c = get_copy(0); + } + s[i] = '\0'; + tok.next(); + if (i == 0) { + a_delete s; + return 0; + } + return s; +} + +void pipe_output() +{ + if (safer_flag) { + error(".pi request not allowed in safer mode"); + skip_line(); + } + else { +#ifdef POPEN_MISSING + error("pipes not available on this system"); + skip_line(); +#else /* not POPEN_MISSING */ + if (the_output) { + error("can't pipe: output already started"); + skip_line(); + } + else { + 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 */ + } +} + +static int system_status; + +void system_request() +{ + if (safer_flag) { + error(".sy request not allowed in safer mode"); + skip_line(); + } + else { + char *command = read_string(); + if (!command) + error("empty command"); + else { + system_status = system(command); + a_delete command; + } + } +} + +void copy_file() +{ + if (curdiv == topdiv && topdiv->before_first_page) { + handle_initial_request(COPY_FILE_REQUEST); + return; + } + symbol filename = get_long_name(1); + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + if (!filename.is_null()) + curdiv->copy_file(filename.contents()); + tok.next(); +} + +#ifdef COLUMN + +void vjustify() +{ + if (curdiv == topdiv && topdiv->before_first_page) { + handle_initial_request(VJUSTIFY_REQUEST); + return; + } + symbol type = get_long_name(1); + if (!type.is_null()) + curdiv->vjustify(type); + skip_line(); +} + +#endif /* COLUMN */ + +void transparent_file() +{ + if (curdiv == topdiv && topdiv->before_first_page) { + handle_initial_request(TRANSPARENT_FILE_REQUEST); + return; + } + symbol filename = get_long_name(1); + while (!tok.newline() && !tok.eof()) + tok.next(); + if (break_flag) + curenv->do_break(); + if (!filename.is_null()) { + errno = 0; + FILE *fp = fopen(filename.contents(), "r"); + if (!fp) + error("can't open `%1': %2", filename.contents(), strerror(errno)); + else { + int bol = 1; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + if (invalid_input_char(c)) + warning(WARN_INPUT, "invalid input character code %1", int(c)); + else { + curdiv->transparent_output(c); + bol = c == '\n'; + } + } + if (!bol) + curdiv->transparent_output('\n'); + fclose(fp); + } + } + tok.next(); +} + +class page_range { + int first; + int last; +public: + page_range *next; + page_range(int, int, page_range *); + int contains(int n); +}; + +page_range::page_range(int i, int j, page_range *p) +: first(i), last(j), next(p) +{ +} + +int page_range::contains(int n) +{ + return n >= first && (last <= 0 || n <= last); +} + +page_range *output_page_list = 0; + +int in_output_page_list(int n) +{ + if (!output_page_list) + return 1; + for (page_range *p = output_page_list; p; p = p->next) + if (p->contains(n)) + return 1; + return 0; +} + +static void parse_output_page_list(char *p) +{ + for (;;) { + int i; + if (*p == '-') + i = 1; + else if (csdigit(*p)) { + i = 0; + do + i = i*10 + *p++ - '0'; + while (csdigit(*p)); + } + else + break; + int j; + if (*p == '-') { + p++; + j = 0; + if (csdigit(*p)) { + do + j = j*10 + *p++ - '0'; + while (csdigit(*p)); + } + } + else + j = i; + if (j == 0) + last_page_number = -1; + else if (last_page_number >= 0 && j > last_page_number) + last_page_number = j; + output_page_list = new page_range(i, j, output_page_list); + if (*p != ',') + break; + ++p; + } + if (*p != '\0') { + error("bad output page list"); + output_page_list = 0; + } +} + +static FILE *open_mac_file(const char *mac, char **path) +{ + // Try first FOOBAR.tmac, then tmac.FOOBAR + char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1]; + strcpy(s1, mac); + strcat(s1, MACRO_POSTFIX); + FILE *fp = mac_path->open_file(s1, path); + a_delete s1; + if (!fp) { + char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1]; + strcpy(s2, MACRO_PREFIX); + strcat(s2, mac); + fp = mac_path->open_file(s2, path); + a_delete s2; + } + return fp; +} + +static void process_macro_file(const char *mac) +{ + char *path; + FILE *fp = open_mac_file(mac, &path); + if (!fp) + fatal("can't find macro file %1", mac); + const char *s = symbol(path).contents(); + a_delete path; + input_stack::push(new file_iterator(fp, s)); + tok.next(); + process_input_stack(); +} + +static void process_startup_file(const char *filename) +{ + char *path; + search_path *orig_mac_path = mac_path; + mac_path = &config_macro_path; + FILE *fp = mac_path->open_file(filename, &path); + if (fp) { + input_stack::push(new file_iterator(fp, symbol(path).contents())); + a_delete path; + tok.next(); + process_input_stack(); + } + mac_path = orig_mac_path; +} + +void macro_source() +{ + symbol nm = get_long_name(1); + if (nm.is_null()) + skip_line(); + else { + while (!tok.newline() && !tok.eof()) + tok.next(); + char *path; + FILE *fp = mac_path->open_file(nm.contents(), &path); + // .mso doesn't (and cannot) go through open_mac_file, so we + // need to do it here manually: If we have tmac.FOOBAR, try + // FOOBAR.tmac and vice versa + if (!fp) { + const char *fn = nm.contents(); + if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) { + char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)]; + strcpy(s, fn + sizeof(MACRO_PREFIX) - 1); + strcat(s, MACRO_POSTFIX); + fp = mac_path->open_file(s, &path); + a_delete s; + } + if (!fp) { + if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1, + MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) { + char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)]; + strcpy(s, MACRO_PREFIX); + strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1); + fp = mac_path->open_file(s, &path); + a_delete s; + } + } + } + if (fp) { + input_stack::push(new file_iterator(fp, symbol(path).contents())); + a_delete path; + } + else + error("can't find macro file `%1'", nm.contents()); + tok.next(); + } +} + +static void process_input_file(const char *name) +{ + FILE *fp; + if (strcmp(name, "-") == 0) { + clearerr(stdin); + fp = stdin; + } + else { + errno = 0; + fp = fopen(name, "r"); + if (!fp) + fatal("can't open `%1': %2", name, strerror(errno)); + } + input_stack::push(new file_iterator(fp, name)); + tok.next(); + process_input_stack(); +} + +// make sure the_input is empty before calling this + +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(0) != EOF) + ; + return success; +} + +static void do_register_assignment(const char *s) +{ + const char *p = strchr(s, '='); + if (!p) { + char buf[2]; + buf[0] = s[0]; + buf[1] = 0; + units n; + if (evaluate_expression(s + 1, &n)) + set_number_reg(buf, n); + } + else { + char *buf = new char[p - s + 1]; + memcpy(buf, s, p - s); + buf[p - s] = 0; + units n; + if (evaluate_expression(p + 1, &n)) + set_number_reg(buf, n); + a_delete buf; + } +} + +static void set_string(const char *name, const char *value) +{ + macro *m = new macro; + for (const char *p = value; *p; p++) + if (!invalid_input_char((unsigned char)*p)) + m->append(*p); + request_dictionary.define(name, m); +} + +static void do_string_assignment(const char *s) +{ + const char *p = strchr(s, '='); + if (!p) { + char buf[2]; + buf[0] = s[0]; + buf[1] = 0; + set_string(buf, s + 1); + } + else { + char *buf = new char[p - s + 1]; + memcpy(buf, s, p - s); + buf[p - s] = 0; + set_string(buf, p + 1); + a_delete buf; + } +} + +struct string_list { + const char *s; + string_list *next; + string_list(const char *ss) : s(ss), next(0) {} +}; + +#if 0 +static void prepend_string(const char *s, string_list **p) +{ + string_list *l = new string_list(s); + l->next = *p; + *p = l; +} +#endif + +static void add_string(const char *s, string_list **p) +{ + while (*p) + p = &((*p)->next); + *p = new string_list(s); +} + +void usage(FILE *stream, const char *prog) +{ + fprintf(stream, +"usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n" +" -rcn -Tname -Fdir -Mdir [files...]\n", + prog); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + string_list *macros = 0; + string_list *register_assignments = 0; + string_list *string_assignments = 0; + int iflag = 0; + int tflag = 0; + int fflag = 0; + int nflag = 0; + int no_rc = 0; // don't process troffrc and troffrc-end + int next_page_number; + opterr = 0; + hresolution = vresolution = 1; + // restore $PATH if called from groff + char* groff_path = getenv("GROFF_PATH__"); + if (groff_path) { + string e = "PATH"; + e += '='; + if (*groff_path) + e += groff_path; + e += '\0'; + if (putenv(strsave(e.contents()))) + fatal("putenv failed"); + } + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { 0, 0, 0, 0 } + }; + 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': + { + printf("GNU troff (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'T': + device = optarg; + tflag = 1; + is_html = (strcmp(device, "html") == 0); + break; + case 'C': + compatible_flag = 1; + // fall through + case 'c': + color_flag = 0; + break; + case 'M': + macro_path.command_line_dir(optarg); + safer_macro_path.command_line_dir(optarg); + config_macro_path.command_line_dir(optarg); + break; + case 'F': + font::command_line_font_dir(optarg); + break; + case 'm': + add_string(optarg, ¯os); + break; + case 'E': + inhibit_errors = 1; + break; + case 'R': + no_rc = 1; + break; + case 'w': + enable_warning(optarg); + break; + case 'W': + disable_warning(optarg); + break; + case 'i': + iflag = 1; + break; + case 'b': + backtrace_flag = 1; + break; + case 'a': + ascii_output_flag = 1; + break; + case 'z': + suppress_output_flag = 1; + break; + case 'n': + if (sscanf(optarg, "%d", &next_page_number) == 1) + nflag++; + else + error("bad page number"); + break; + case 'o': + parse_output_page_list(optarg); + break; + case 'd': + if (*optarg == '\0') + error("`-d' requires non-empty argument"); + else + add_string(optarg, &string_assignments); + break; + case 'r': + if (*optarg == '\0') + error("`-r' requires non-empty argument"); + else + add_string(optarg, ®ister_assignments); + break; + case 'f': + default_family = symbol(optarg); + fflag = 1; + break; + case 'q': + case 's': + case 't': + // silently ignore these + break; + case 'U': + safer_flag = 0; // unsafe behaviour + break; + case CHAR_MAX + 1: // --help + usage(stdout, argv[0]); + exit(0); + break; + case '?': + usage(stderr, argv[0]); + exit(1); + break; // never reached + default: + assert(0); + } + if (!safer_flag) + 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; + hresolution = font::hor; + 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); + int i; + int j = 1; + if (font::style_table) { + for (i = 0; font::style_table[i]; i++) + mount_style(j++, symbol(font::style_table[i])); + } + for (i = 0; font::font_name_table[i]; i++, j++) + // In the DESC file a font name of 0 (zero) means leave this + // position empty. + if (strcmp(font::font_name_table[i], "0") != 0) + mount_font(j, symbol(font::font_name_table[i])); + curdiv = topdiv = new top_level_diversion; + if (nflag) + topdiv->set_next_page_number(next_page_number); + init_input_requests(); + init_env_requests(); + init_div_requests(); +#ifdef COLUMN + init_column_requests(); +#endif /* COLUMN */ + init_node_requests(); + number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0")); + init_registers(); + init_reg_requests(); + init_hyphen_requests(); + init_environments(); + while (string_assignments) { + do_string_assignment(string_assignments->s); + string_list *tem = string_assignments; + string_assignments = string_assignments->next; + delete tem; + } + while (register_assignments) { + do_register_assignment(register_assignments->s); + string_list *tem = register_assignments; + register_assignments = register_assignments->next; + delete tem; + } + if (!no_rc) + process_startup_file(INITIAL_STARTUP_FILE); + while (macros) { + process_macro_file(macros->s); + string_list *tem = macros; + macros = macros->next; + delete tem; + } + if (!no_rc) + process_startup_file(FINAL_STARTUP_FILE); + for (i = optind; i < argc; i++) + process_input_file(argv[i]); + if (optind >= argc || iflag) + process_input_file("-"); + exit_troff(); + return 0; // not reached +} + +void warn_request() +{ + int n; + if (has_arg() && get_integer(&n)) { + if (n & ~WARN_TOTAL) { + warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL); + n &= WARN_TOTAL; + } + warning_mask = n; + } + else + warning_mask = WARN_TOTAL; + skip_line(); +} + +static void init_registers() +{ +#ifdef LONG_FOR_TIME_T + long +#else /* not LONG_FOR_TIME_T */ + time_t +#endif /* not LONG_FOR_TIME_T */ + 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)); + set_number_reg("year", int(1900 + tt->tm_year)); + set_number_reg("yr", int(tt->tm_year)); + set_number_reg("$$", getpid()); + number_reg_dictionary.define(".A", + new constant_reg(ascii_output_flag + ? "1" + : "0")); +} + +/* + * registers associated with \O + */ + +static int output_reg_minx_contents = -1; +static int output_reg_miny_contents = -1; +static int output_reg_maxx_contents = -1; +static int output_reg_maxy_contents = -1; + +void check_output_limits(int x, int y) +{ + if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents)) + output_reg_minx_contents = x; + if (x > output_reg_maxx_contents) + output_reg_maxx_contents = x; + if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents)) + output_reg_miny_contents = y; + if (y > output_reg_maxy_contents) + output_reg_maxy_contents = y; +} + +void reset_output_registers() +{ + output_reg_minx_contents = -1; + output_reg_miny_contents = -1; + output_reg_maxx_contents = -1; + output_reg_maxy_contents = -1; +} + +void get_output_registers(int *minx, int *miny, int *maxx, int *maxy) +{ + *minx = output_reg_minx_contents; + *miny = output_reg_miny_contents; + *maxx = output_reg_maxx_contents; + *maxy = output_reg_maxy_contents; +} + +void init_input_requests() +{ + 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("composite", composite_request); + init_request("continue", while_continue_request); + init_request("cp", compatible); + init_request("de", define_macro); + init_request("de1", define_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("length", length_request); + init_request("lf", line_file); + init_request("mso", macro_source); + init_request("nop", nop_request); + init_request("nroff", nroff_request); + init_request("nx", next_file); + 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("schar", define_special_character); + 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("tr", translate); + init_request("trf", transparent_file); + init_request("trin", translate_input); + init_request("trnt", translate_no_transparent); + init_request("troff", troff_request); + init_request("unformat", unformat_macro); +#ifdef COLUMN + init_request("vj", vjustify); +#endif /* COLUMN */ + init_request("warn", warn_request); + init_request("warnscale", warnscale_request); + init_request("while", while_request); + init_request("write", write_request); + init_request("writec", write_request_continue); + init_request("writem", write_macro_request); + number_reg_dictionary.define(".$", new nargs_reg); + number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag)); + number_reg_dictionary.define(".c", new lineno_reg); + number_reg_dictionary.define(".color", new constant_int_reg(&color_flag)); + number_reg_dictionary.define(".F", new filename_reg); + number_reg_dictionary.define(".g", new constant_reg("1")); + number_reg_dictionary.define(".H", new constant_int_reg(&hresolution)); + number_reg_dictionary.define(".R", new constant_reg("10000")); + number_reg_dictionary.define(".V", new constant_int_reg(&vresolution)); + 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 *revision; + number_reg_dictionary.define(".Y", new constant_reg(revision)); + 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("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); + +void init_request(const char *s, REQUEST_FUNCP f) +{ + request_dictionary.define(s, new request(f)); +} + +static request_or_macro *lookup_request(symbol nm) +{ + assert(!nm.is_null()); + request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm); + if (p == 0) { + warning(WARN_MAC, "macro `%1' not defined", nm.contents()); + p = new macro; + request_dictionary.define(nm, p); + } + return p; +} + +node *charinfo_to_node_list(charinfo *ci, const environment *envp) +{ + // Don't interpret character definitions in compatible mode. + int old_compatible_flag = compatible_flag; + compatible_flag = 0; + int old_escape_char = escape_char; + escape_char = '\\'; + macro *mac = ci->set_macro(0); + assert(mac != 0); + environment *oldenv = curenv; + environment env(envp); + curenv = &env; + curenv->set_composite(); + token old_tok = tok; + input_stack::add_boundary(); + string_iterator *si = + new string_iterator(*mac, "composite character", ci->nm); + input_stack::push(si); + // we don't use process_input_stack, because we don't want to recognise + // requests + for (;;) { + tok.next(); + if (tok.eof()) + break; + if (tok.newline()) { + error("composite character mustn't contain newline"); + while (!tok.eof()) + tok.next(); + break; + } + else + tok.process(); + } + node *n = curenv->extract_output_line(); + input_stack::remove_boundary(); + ci->set_macro(mac); + tok = old_tok; + curenv = oldenv; + compatible_flag = old_compatible_flag; + escape_char = old_escape_char; + have_input = 0; + return n; +} + +static node *read_draw_node() +{ + token start; + start.next(); + if (!start.delimiter(1)){ + do { + tok.next(); + } while (tok != start && !tok.newline() && !tok.eof()); + } + else { + tok.next(); + if (tok == start) + error("missing argument"); + else { + unsigned char type = tok.ch(); + if (type == 'F') { + read_color_draw_node(start); + return 0; + } + tok.next(); + int maxpoints = 10; + hvpair *point = new hvpair[maxpoints]; + int npoints = 0; + int no_last_v = 0; + int err = 0; + int i; + for (i = 0; tok != start; i++) { + if (i == maxpoints) { + hvpair *oldpoint = point; + point = new hvpair[maxpoints*2]; + for (int j = 0; j < maxpoints; j++) + point[j] = oldpoint[j]; + maxpoints *= 2; + a_delete oldpoint; + } + if (!get_hunits(&point[i].h, + type == 'f' || type == 't' ? 'u' : 'm')) { + err = 1; + break; + } + ++npoints; + tok.skip(); + point[i].v = V0; + if (tok == start) { + no_last_v = 1; + break; + } + if (!get_vunits(&point[i].v, 'v')) { + err = 1; + break; + } + tok.skip(); + } + while (tok != start && !tok.newline() && !tok.eof()) + tok.next(); + if (!err) { + switch (type) { + case 'l': + if (npoints != 1 || no_last_v) { + error("two arguments needed for line"); + npoints = 1; + } + break; + case 'c': + if (npoints != 1 || !no_last_v) { + error("one argument needed for circle"); + npoints = 1; + point[0].v = V0; + } + break; + case 'e': + if (npoints != 1 || no_last_v) { + error("two arguments needed for ellipse"); + npoints = 1; + } + break; + case 'a': + if (npoints != 2 || no_last_v) { + error("four arguments needed for arc"); + npoints = 2; + } + break; + case '~': + 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_glyph_color(), + curenv->get_fill_color()); + a_delete point; + return dn; + } + else { + a_delete point; + } + } + } + return 0; +} + +static void read_color_draw_node(token &start) +{ + tok.next(); + if (tok == start) { + error("missing color scheme"); + return; + } + unsigned char scheme = tok.ch(); + tok.next(); + color *col; + char end = start.ch(); + switch (scheme) { + case 'c': + col = read_cmy(end); + break; + case 'd': + col = &default_color; + break; + case 'g': + col = read_gray(end); + break; + case 'k': + col = read_cmyk(end); + break; + case 'r': + col = read_rgb(end); + break; + } + if (col) + curenv->set_fill_color(col); + while (tok != start) { + if (tok.newline() || tok.eof()) { + warning(WARN_DELIM, "missing closing delimiter"); + input_stack::push(make_temp_iterator("\n")); + break; + } + tok.next(); + } + have_input = 1; +} + +static struct { + const char *name; + int mask; +} warning_table[] = { + { "char", WARN_CHAR }, + { "range", WARN_RANGE }, + { "break", WARN_BREAK }, + { "delim", WARN_DELIM }, + { "el", WARN_EL }, + { "scale", WARN_SCALE }, + { "number", WARN_NUMBER }, + { "syntax", WARN_SYNTAX }, + { "tab", WARN_TAB }, + { "right-brace", WARN_RIGHT_BRACE }, + { "missing", WARN_MISSING }, + { "input", WARN_INPUT }, + { "escape", WARN_ESCAPE }, + { "space", WARN_SPACE }, + { "font", WARN_FONT }, + { "di", WARN_DI }, + { "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 }, +}; + +static int lookup_warning(const char *name) +{ + for (unsigned int i = 0; + i < sizeof(warning_table)/sizeof(warning_table[0]); + i++) + if (strcmp(name, warning_table[i].name) == 0) + return warning_table[i].mask; + return 0; +} + +static void enable_warning(const char *name) +{ + int mask = lookup_warning(name); + if (mask) + warning_mask |= mask; + else + error("unknown warning `%1'", name); +} + +static void disable_warning(const char *name) +{ + int mask = lookup_warning(name); + if (mask) + warning_mask &= ~mask; + else + error("unknown warning `%1'", name); +} + +static void copy_mode_error(const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + if (ignoring) { + static const char prefix[] = "(in ignored input) "; + char *s = new char[sizeof(prefix) + strlen(format)]; + strcpy(s, prefix); + strcat(s, format); + warning(WARN_IG, s, arg1, arg2, arg3); + a_delete s; + } + else + error(format, arg1, arg2, arg3); +} + +enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL }; + +static void do_error(error_type type, + const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + const char *filename; + int lineno; + if (inhibit_errors && type < FATAL) + return; + if (backtrace_flag) + input_stack::backtrace(); + if (!get_file_line(&filename, &lineno)) + filename = 0; + if (filename) + errprint("%1:%2: ", filename, lineno); + else if (program_name) + fprintf(stderr, "%s: ", program_name); + switch (type) { + case FATAL: + fputs("fatal error: ", stderr); + break; + case ERROR: + break; + 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); + fflush(stderr); + if (type == FATAL) + cleanup_and_exit(1); +} + +int warning(warning_type t, + const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + if ((t & warning_mask) != 0) { + do_error(WARNING, format, arg1, arg2, arg3); + return 1; + } + else + 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, + const errarg &arg3) +{ + do_error(ERROR, format, arg1, arg2, arg3); +} + +void fatal(const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + do_error(FATAL, format, arg1, arg2, arg3); +} + +void fatal_with_file_and_line(const char *filename, int lineno, + const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + fprintf(stderr, "%s:%d: fatal error: ", filename, lineno); + errprint(format, arg1, arg2, arg3); + fputc('\n', stderr); + fflush(stderr); + cleanup_and_exit(1); +} + +void error_with_file_and_line(const char *filename, int lineno, + const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + fprintf(stderr, "%s:%d: error: ", filename, lineno); + errprint(format, arg1, arg2, arg3); + fputc('\n', stderr); + fflush(stderr); +} + +dictionary charinfo_dictionary(501); + +charinfo *get_charinfo(symbol nm) +{ + void *p = charinfo_dictionary.lookup(nm); + if (p != 0) + return (charinfo *)p; + charinfo *cp = new charinfo(nm); + (void)charinfo_dictionary.lookup(nm, cp); + return cp; +} + +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), asciify_code(0), + not_found(0), transparent_translate(1), translate_input(0), + mode(CHAR_NORMAL), nm(s) +{ + index = next_index++; +} + +void charinfo::set_hyphenation_code(unsigned char c) +{ + hyphenation_code = c; +} + +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; +} + +void charinfo::set_special_translation(int c, int tt) +{ + special_translation = c; + translation = 0; + transparent_translate = tt; +} + +void charinfo::set_ascii_code(unsigned char c) +{ + ascii_code = c; +} + +void charinfo::set_asciify_code(unsigned char c) +{ + asciify_code = c; +} + +macro *charinfo::set_macro(macro *m) +{ + macro *tem = mac; + mac = m; + return tem; +} + +macro *charinfo::setx_macro(macro *m, char_mode cm) +{ + macro *tem = mac; + mac = m; + mode = cm; + return tem; +} + +void charinfo::set_number(int n) +{ + number = n; + flags |= NUMBERED; +} + +int charinfo::get_number() +{ + assert(flags & NUMBERED); + return number; +} + +symbol UNNAMED_SYMBOL("---"); + +// For numbered characters not between 0 and 255, we make a symbol out +// of the number and store them in this dictionary. + +dictionary numbered_charinfo_dictionary(11); + +charinfo *get_charinfo_by_number(int n) +{ + static charinfo *number_table[256]; + + if (n >= 0 && n < 256) { + charinfo *ci = number_table[n]; + if (!ci) { + ci = new charinfo(UNNAMED_SYMBOL); + ci->set_number(n); + number_table[n] = ci; + } + return ci; + } + else { + symbol ns(i_to_a(n)); + charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns); + if (!ci) { + ci = new charinfo(UNNAMED_SYMBOL); + ci->set_number(n); + (void)numbered_charinfo_dictionary.lookup(ns, ci); + } + return ci; + } +} + +int font::name_to_index(const char *nm) +{ + charinfo *ci; + if (nm[1] == 0) + ci = charset_table[nm[0] & 0xff]; + else if (nm[0] == '\\' && nm[2] == 0) + ci = get_charinfo(symbol(nm + 1)); + else + ci = get_charinfo(symbol(nm)); + if (ci == 0) + return -1; + else + return ci->get_index(); +} + +int font::number_to_index(int n) +{ + return get_charinfo_by_number(n)->get_index(); +} diff --git a/contrib/groff/src/roff/troff/node.cpp b/contrib/groff/src/roff/troff/node.cpp new file mode 100644 index 0000000..73776b1 --- /dev/null +++ b/contrib/groff/src/roff/troff/node.cpp @@ -0,0 +1,5993 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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 +#endif + +#include "symbol.h" +#include "dictionary.h" +#include "hvunits.h" +#include "env.h" +#include "request.h" +#include "node.h" +#include "token.h" +#include "charinfo.h" +#include "font.h" +#include "reg.h" +#include "input.h" +#include "div.h" +#include "geometry.h" +#include "stringclass.h" + +#include "nonposix.h" + +#ifdef _POSIX_VERSION + +#include + +#else /* not _POSIX_VERSION */ + +/* traditional Unix */ + +#define WIFEXITED(s) (((s) & 0377) == 0) +#define WEXITSTATUS(s) (((s) >> 8) & 0377) +#define WTERMSIG(s) ((s) & 0177) +#define WIFSTOPPED(s) (((s) & 0377) == 0177) +#define WSTOPSIG(s) (((s) >> 8) & 0377) +#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177)) + +#endif /* not _POSIX_VERSION */ + +/* + * how many boundaries of images have been written? Useful for + * debugging grohtml + */ + +int image_no = 0; +static int suppress_start_page = 0; + +#define STORE_WIDTH 1 + +symbol HYPHEN_SYMBOL("hy"); + +// Character used when a hyphen is inserted at a line break. +static charinfo *soft_hyphen_char; + +enum constant_space_type { + CONSTANT_SPACE_NONE, + CONSTANT_SPACE_RELATIVE, + CONSTANT_SPACE_ABSOLUTE + }; + +struct special_font_list { + int n; + special_font_list *next; +}; + +special_font_list *global_special_fonts; +static int global_ligature_mode = 1; +static int global_kern_mode = 1; + +class track_kerning_function { + int non_zero; + units min_size; + hunits min_amount; + units max_size; + hunits max_amount; +public: + track_kerning_function(); + track_kerning_function(units, hunits, units, hunits); + int operator==(const track_kerning_function &); + int operator!=(const track_kerning_function &); + hunits compute(int point_size); +}; + +// embolden fontno when this is the current font + +struct conditional_bold { + conditional_bold *next; + int fontno; + hunits offset; + conditional_bold(int, hunits, conditional_bold * = 0); +}; + +struct tfont; + +class font_info { + tfont *last_tfont; + int number; + font_size last_size; + int last_height; + int last_slant; + symbol internal_name; + symbol external_name; + font *fm; + char is_bold; + hunits bold_offset; + track_kerning_function track_kern; + constant_space_type is_constant_spaced; + units constant_space; + int last_ligature_mode; + int last_kern_mode; + conditional_bold *cond_bold_list; + void flush(); +public: + special_font_list *sf; + font_info(symbol nm, int n, symbol enm, font *f); + int contains(charinfo *); + void set_bold(hunits); + void unbold(); + void set_conditional_bold(int, hunits); + void conditional_unbold(int); + void set_track_kern(track_kerning_function &); + void set_constant_space(constant_space_type, units = 0); + int is_named(symbol); + symbol get_name(); + tfont *get_tfont(font_size, int, int, int); + hunits get_space_width(font_size, int); + hunits get_narrow_space_width(font_size); + hunits get_half_narrow_space_width(font_size); + int get_bold(hunits *); + int is_special(); + int is_style(); + friend symbol get_font_name(int, environment *); +}; + +class tfont_spec { +protected: + symbol name; + int input_position; + font *fm; + font_size size; + char is_bold; + char is_constant_spaced; + int ligature_mode; + int kern_mode; + hunits bold_offset; + hunits track_kern; // add this to the width + hunits constant_space_width; + int height; + int slant; +public: + tfont_spec(symbol nm, int pos, font *, font_size, int, int); + tfont_spec(const tfont_spec &spec) { *this = spec; } + tfont_spec plain(); + int operator==(const tfont_spec &); + friend tfont *font_info::get_tfont(font_size fs, int, int, int); +}; + +class tfont : public tfont_spec { + static tfont *tfont_list; + tfont *next; + tfont *plain_version; +public: + tfont(tfont_spec &); + int contains(charinfo *); + hunits get_width(charinfo *c); + int get_bold(hunits *); + int get_constant_space(hunits *); + hunits get_track_kern(); + tfont *get_plain(); + font_size get_size(); + symbol get_name(); + charinfo *get_lig(charinfo *c1, charinfo *c2); + int get_kern(charinfo *c1, charinfo *c2, hunits *res); + int get_input_position(); + int get_character_type(charinfo *); + int get_height(); + int get_slant(); + vunits get_char_height(charinfo *); + vunits get_char_depth(charinfo *); + hunits get_char_skew(charinfo *); + hunits get_italic_correction(charinfo *); + hunits get_left_italic_correction(charinfo *); + hunits get_subscript_correction(charinfo *); + friend tfont *make_tfont(tfont_spec &); +}; + +inline int env_definite_font(environment *env) +{ + return env->get_family()->make_definite(env->get_font()); +} + +/* font_info functions */ + +static font_info **font_table = 0; +static int font_table_size = 0; + +font_info::font_info(symbol nm, int n, symbol enm, font *f) +: last_tfont(0), number(n), last_size(0), + internal_name(nm), external_name(enm), fm(f), + is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1), + last_kern_mode(1), cond_bold_list(0), sf(0) +{ +} + +inline int font_info::contains(charinfo *ci) +{ + return fm != 0 && fm->contains(ci->get_index()); +} + +inline int font_info::is_special() +{ + return fm != 0 && fm->is_special(); +} + +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 + +tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno) +{ + if (last_tfont == 0 || fs != last_size + || height != last_height || slant != last_slant + || global_ligature_mode != last_ligature_mode + || global_kern_mode != last_kern_mode + || fontno != number) { + font_info *f = font_table[fontno]; + tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant); + for (conditional_bold *p = cond_bold_list; p; p = p->next) + if (p->fontno == fontno) { + spec.is_bold = 1; + spec.bold_offset = p->offset; + break; + } + if (!spec.is_bold && is_bold) { + spec.is_bold = 1; + spec.bold_offset = bold_offset; + } + spec.track_kern = track_kern.compute(fs.to_scaled_points()); + spec.ligature_mode = global_ligature_mode; + spec.kern_mode = global_kern_mode; + switch (is_constant_spaced) { + case CONSTANT_SPACE_NONE: + break; + case CONSTANT_SPACE_ABSOLUTE: + spec.is_constant_spaced = 1; + spec.constant_space_width = constant_space; + break; + case CONSTANT_SPACE_RELATIVE: + spec.is_constant_spaced = 1; + spec.constant_space_width + = scale(constant_space*fs.to_scaled_points(), + units_per_inch, + 36*72*sizescale); + break; + default: + assert(0); + } + if (fontno != number) + return make_tfont(spec); + last_tfont = make_tfont(spec); + last_size = fs; + last_height = height; + last_slant = slant; + last_ligature_mode = global_ligature_mode; + last_kern_mode = global_kern_mode; + } + return last_tfont; +} + +int font_info::get_bold(hunits *res) +{ + if (is_bold) { + *res = bold_offset; + return 1; + } + else + return 0; +} + +void font_info::unbold() +{ + if (is_bold) { + is_bold = 0; + flush(); + } +} + +void font_info::set_bold(hunits offset) +{ + if (!is_bold || offset != bold_offset) { + is_bold = 1; + bold_offset = offset; + flush(); + } +} + +void font_info::set_conditional_bold(int fontno, hunits offset) +{ + for (conditional_bold *p = cond_bold_list; p; p = p->next) + if (p->fontno == fontno) { + if (offset != p->offset) { + p->offset = offset; + flush(); + } + return; + } + cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list); +} + +conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x) +: next(x), fontno(f), offset(h) +{ +} + +void font_info::conditional_unbold(int fontno) +{ + for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next) + if ((*p)->fontno == fontno) { + conditional_bold *tem = *p; + *p = (*p)->next; + delete tem; + flush(); + return; + } +} + +void font_info::set_constant_space(constant_space_type type, units x) +{ + if (type != is_constant_spaced + || (type != CONSTANT_SPACE_NONE && x != constant_space)) { + flush(); + is_constant_spaced = type; + constant_space = x; + } +} + +void font_info::set_track_kern(track_kerning_function &tk) +{ + if (track_kern != tk) { + track_kern = tk; + flush(); + } +} + +void font_info::flush() +{ + last_tfont = 0; +} + +int font_info::is_named(symbol s) +{ + return internal_name == s; +} + +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) + return scale(hunits(fm->get_space_width(fs.to_scaled_points())), + space_size, 12); + else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE) + return constant_space; + else + return scale(constant_space*fs.to_scaled_points(), + units_per_inch, 36*72*sizescale); +} + +hunits font_info::get_narrow_space_width(font_size fs) +{ + charinfo *ci = get_charinfo(symbol("|")); + if (fm->contains(ci->get_index())) + return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points())); + else + return hunits(fs.to_units()/6); +} + +hunits font_info::get_half_narrow_space_width(font_size fs) +{ + charinfo *ci = get_charinfo(symbol("^")); + if (fm->contains(ci->get_index())) + return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points())); + else + return hunits(fs.to_units()/12); +} + +/* tfont */ + +tfont_spec::tfont_spec(symbol nm, int n, font *f, + font_size s, int h, int sl) +: name(nm), input_position(n), fm(f), size(s), + is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1), + height(h), slant(sl) +{ + if (height == size.to_scaled_points()) + height = 0; +} + +int tfont_spec::operator==(const tfont_spec &spec) +{ + if (fm == spec.fm + && size == spec.size + && input_position == spec.input_position + && name == spec.name + && height == spec.height + && slant == spec.slant + && (is_bold + ? (spec.is_bold && bold_offset == spec.bold_offset) + : !spec.is_bold) + && track_kern == spec.track_kern + && (is_constant_spaced + ? (spec.is_constant_spaced + && constant_space_width == spec.constant_space_width) + : !spec.is_constant_spaced) + && ligature_mode == spec.ligature_mode + && kern_mode == spec.kern_mode) + return 1; + else + return 0; +} + +tfont_spec tfont_spec::plain() +{ + return tfont_spec(name, input_position, fm, size, height, slant); +} + +hunits tfont::get_width(charinfo *c) +{ + if (is_constant_spaced) + return constant_space_width; + else if (is_bold) + return (hunits(fm->get_width(c->get_index(), size.to_scaled_points())) + + track_kern + bold_offset); + else + return (hunits(fm->get_width(c->get_index(), size.to_scaled_points())) + + track_kern); +} + +vunits tfont::get_char_height(charinfo *c) +{ + vunits v = fm->get_height(c->get_index(), size.to_scaled_points()); + if (height != 0 && height != size.to_scaled_points()) + return scale(v, height, size.to_scaled_points()); + else + return v; +} + +vunits tfont::get_char_depth(charinfo *c) +{ + vunits v = fm->get_depth(c->get_index(), size.to_scaled_points()); + if (height != 0 && height != size.to_scaled_points()) + return scale(v, height, size.to_scaled_points()); + else + return v; +} + +hunits tfont::get_char_skew(charinfo *c) +{ + return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant)); +} + +hunits tfont::get_italic_correction(charinfo *c) +{ + return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points())); +} + +hunits tfont::get_left_italic_correction(charinfo *c) +{ + return hunits(fm->get_left_italic_correction(c->get_index(), + size.to_scaled_points())); +} + +hunits tfont::get_subscript_correction(charinfo *c) +{ + return hunits(fm->get_subscript_correction(c->get_index(), + size.to_scaled_points())); +} + +inline int tfont::get_input_position() +{ + return input_position; +} + +inline int tfont::contains(charinfo *ci) +{ + return fm->contains(ci->get_index()); +} + +inline int tfont::get_character_type(charinfo *ci) +{ + return fm->get_character_type(ci->get_index()); +} + +inline int tfont::get_bold(hunits *res) +{ + if (is_bold) { + *res = bold_offset; + return 1; + } + else + return 0; +} + +inline int tfont::get_constant_space(hunits *res) +{ + if (is_constant_spaced) { + *res = constant_space_width; + return 1; + } + else + return 0; +} + +inline hunits tfont::get_track_kern() +{ + return track_kern; +} + +inline tfont *tfont::get_plain() +{ + return plain_version; +} + +inline font_size tfont::get_size() +{ + return size; +} + +inline symbol tfont::get_name() +{ + return name; +} + +inline int tfont::get_height() +{ + return height; +} + +inline int tfont::get_slant() +{ + return slant; +} + +symbol SYMBOL_ff("ff"); +symbol SYMBOL_fi("fi"); +symbol SYMBOL_fl("fl"); +symbol SYMBOL_Fi("Fi"); +symbol SYMBOL_Fl("Fl"); + +charinfo *tfont::get_lig(charinfo *c1, charinfo *c2) +{ + if (ligature_mode == 0) + return 0; + charinfo *ci = 0; + if (c1->get_ascii_code() == 'f') { + switch (c2->get_ascii_code()) { + case 'f': + if (fm->has_ligature(font::LIG_ff)) + ci = get_charinfo(SYMBOL_ff); + break; + case 'i': + if (fm->has_ligature(font::LIG_fi)) + ci = get_charinfo(SYMBOL_fi); + break; + case 'l': + if (fm->has_ligature(font::LIG_fl)) + ci = get_charinfo(SYMBOL_fl); + break; + } + } + else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) { + switch (c2->get_ascii_code()) { + case 'i': + if (fm->has_ligature(font::LIG_ffi)) + ci = get_charinfo(SYMBOL_Fi); + break; + case 'l': + if (fm->has_ligature(font::LIG_ffl)) + ci = get_charinfo(SYMBOL_Fl); + break; + } + } + if (ci != 0 && fm->contains(ci->get_index())) + return ci; + return 0; +} + +inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res) +{ + if (kern_mode == 0) + return 0; + else { + int n = fm->get_kern(c1->get_index(), + c2->get_index(), + size.to_scaled_points()); + if (n) { + *res = hunits(n); + return 1; + } + else + return 0; + } +} + +tfont *tfont::tfont_list = 0; + +tfont::tfont(tfont_spec &spec) : tfont_spec(spec) +{ + next = tfont_list; + tfont_list = this; + tfont_spec plain_spec = plain(); + tfont *p; + for (p = tfont_list; p; p = p->next) + if (*p == plain_spec) { + plain_version = p; + break; + } + if (!p) + plain_version = new tfont(plain_spec); +} + +/* output_file */ + +class real_output_file : public output_file { +#ifndef POPEN_MISSING + int piped; +#endif + int printing; // decision via optional page list + 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; + virtual void really_begin_page(int pageno, vunits page_length) = 0; + virtual void really_copy_file(hunits x, vunits y, const char *filename); + virtual void really_put_filename(const char *filename); + virtual void really_on(); + virtual void really_off(); +protected: + FILE *fp; +public: + real_output_file(); + ~real_output_file(); + void flush(); + void transparent_char(unsigned char); + void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width); + void begin_page(int pageno, vunits page_length); + void put_filename(const char *filename); + void on(); + void off(); + int is_on(); + int is_printing(); + void copy_file(hunits x, vunits y, const char *filename); +}; + +class suppress_output_file : public real_output_file { +public: + suppress_output_file(); + void really_transparent_char(unsigned char); + void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width); + void really_begin_page(int pageno, vunits page_length); +}; + +class ascii_output_file : public real_output_file { +public: + ascii_output_file(); + void really_transparent_char(unsigned char); + void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width); + void really_begin_page(int pageno, vunits page_length); + void outc(unsigned char c); + void outs(const char *s); +}; + +void ascii_output_file::outc(unsigned char c) +{ + fputc(c, fp); +} + +void ascii_output_file::outs(const char *s) +{ + fputc('<', fp); + if (s) + fputs(s, fp); + fputc('>', fp); +} + +struct hvpair; + +class troff_output_file : public real_output_file { + units hpos; + units vpos; + units output_vpos; + units output_hpos; + int force_motion; + int current_size; + 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; + enum { TBUF_SIZE = 256 }; + char tbuf[TBUF_SIZE]; + int tbuf_len; + int tbuf_kern; + int begun_page; + void do_motion(); + 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(); +public: + troff_output_file(); + ~troff_output_file(); + void trailer(vunits page_length); + 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 *, color *, color *, int = 0); + void start_special(); + void special_char(unsigned char c); + void end_special(); + void word_marker(); + void really_transparent_char(unsigned char c); + void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width); + void really_begin_page(int pageno, vunits page_length); + void really_copy_file(hunits x, vunits y, const char *filename); + void really_put_filename(const char *filename); + void really_on(); + void really_off(); + 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; } + friend void space_char_hmotion_node::tprint(troff_output_file *); + friend void unbreakable_space_node::tprint(troff_output_file *); +}; + +static void put_string(const char *s, FILE *fp) +{ + for (; *s != '\0'; ++s) + putc(*s, fp); +} + +inline void troff_output_file::put(char c) +{ + putc(c, fp); +} + +inline void troff_output_file::put(unsigned char c) +{ + putc(c, fp); +} + +inline void troff_output_file::put(const char *s) +{ + put_string(s, fp); +} + +inline void troff_output_file::put(int i) +{ + put_string(i_to_a(i), fp); +} + +inline void troff_output_file::put(unsigned int i) +{ + put_string(ui_to_a(i), fp); +} + +void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol, + int no_init_string) +{ + set_font(tf); + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + if (!no_init_string) + put("x X "); +} + +void troff_output_file::start_special() +{ + flush_tbuf(); + do_motion(); + put("x X "); +} + +void troff_output_file::special_char(unsigned char c) +{ + put(c); + if (c == '\n') + put('+'); +} + +void troff_output_file::end_special() +{ + put('\n'); +} + +inline void troff_output_file::moveto(hunits h, vunits v) +{ + hpos = h.to_units(); + vpos = v.to_units(); +} + +void troff_output_file::really_print_line(hunits x, vunits y, node *n, + vunits before, vunits after, hunits) +{ + moveto(x, y); + while (n != 0) { + n->tprint(this); + n = n->next; + } + flush_tbuf(); + // This ensures that transparent throughput will have a more predictable + // position. + do_motion(); + force_motion = 1; + hpos = 0; + put('n'); + put(before.to_units()); + put(' '); + put(after.to_units()); + put('\n'); +} + +inline void troff_output_file::word_marker() +{ + flush_tbuf(); + if (is_on()) + put('w'); +} + +inline void troff_output_file::right(hunits n) +{ + hpos += n.to_units(); +} + +inline void troff_output_file::down(vunits n) +{ + vpos += n.to_units(); +} + +void troff_output_file::do_motion() +{ + if (force_motion) { + put('V'); + put(vpos); + put('\n'); + put('H'); + put(hpos); + put('\n'); + } + else { + if (hpos != output_hpos) { + units n = hpos - output_hpos; + if (n > 0 && n < hpos) { + put('h'); + put(n); + } + else { + put('H'); + put(hpos); + } + put('\n'); + } + if (vpos != output_vpos) { + units n = vpos - output_vpos; + if (n > 0 && n < vpos) { + put('v'); + put(n); + } + else { + put('V'); + put(vpos); + } + put('\n'); + } + } + output_vpos = vpos; + output_hpos = hpos; + force_motion = 0; +} + +void troff_output_file::flush_tbuf() +{ + if (!is_on()) { + tbuf_len = 0; + return; + } + + if (tbuf_len == 0) + return; + if (tbuf_kern == 0) + put('t'); + else { + put('u'); + put(tbuf_kern); + put(' '); + } + check_output_limits(hpos, vpos); + check_output_limits(hpos, vpos - current_size); + + for (int i = 0; i < tbuf_len; i++) + put(tbuf[i]); + put('\n'); + tbuf_len = 0; +} + +void troff_output_file::check_charinfo(tfont *tf, charinfo *ci) +{ + 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 + depth); +} + +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; + } + set_font(tf); + char c = ci->get_ascii_code(); + if (c == '\0') { + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + check_charinfo(tf, ci); + if (ci->numbered()) { + put('N'); + put(ci->get_number()); + } + else { + put('C'); + const char *s = ci->nm.contents(); + if (s[1] == 0) { + put('\\'); + put(s[0]); + } + else + put(s); + } + put('\n'); + hpos += w.to_units() + kk; + } + else if (tcommand_flag) { + if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos + && (!gcol || gcol == current_glyph_color) + && (!fcol || fcol == current_fill_color) + && kk == tbuf_kern + && tbuf_len < TBUF_SIZE) { + check_charinfo(tf, ci); + tbuf[tbuf_len++] = c; + output_hpos += w.to_units() + kk; + hpos = output_hpos; + return; + } + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + check_charinfo(tf, ci); + tbuf[tbuf_len++] = c; + output_hpos += w.to_units() + kk; + tbuf_kern = kk; + hpos = output_hpos; + } + else { + // flush_tbuf(); + int n = hpos - output_hpos; + check_charinfo(tf, ci); + // check_output_limits(output_hpos, output_vpos); + if (vpos == output_vpos + && (!gcol || gcol == current_glyph_color) + && (!fcol || fcol == current_fill_color) + && n > 0 && n < 100 && !force_motion) { + put(char(n/10 + '0')); + put(char(n%10 + '0')); + put(c); + output_hpos = hpos; + } + else { + glyph_color(gcol); + fill_color(fcol); + do_motion(); + put('c'); + put(c); + } + hpos += w.to_units() + kk; + } +} + +void troff_output_file::put_char(charinfo *ci, tfont *tf, + color *gcol, color *fcol) +{ + flush_tbuf(); + if (!is_on()) + return; + set_font(tf); + char c = ci->get_ascii_code(); + if (c == '\0') { + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + if (ci->numbered()) { + put('N'); + put(ci->get_number()); + } + else { + put('C'); + const char *s = ci->nm.contents(); + if (s[1] == 0) { + put('\\'); + put(s[0]); + } + else + put(s); + } + put('\n'); + } + else { + int n = hpos - output_hpos; + if (vpos == output_vpos + && (!gcol || gcol == current_glyph_color) + && (!fcol || fcol == current_fill_color) + && n > 0 && n < 100) { + put(char(n/10 + '0')); + put(char(n%10 + '0')); + put(c); + output_hpos = hpos; + } + else { + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + put('c'); + put(c); + } + } +} + +// set_font calls `flush_tbuf' if necessary. + +void troff_output_file::set_font(tfont *tf) +{ + if (current_tfont == tf) + return; + flush_tbuf(); + int n = tf->get_input_position(); + symbol nm = tf->get_name(); + if (n >= nfont_positions || font_position[n] != nm) { + put("x font "); + put(n); + put(' '); + put(nm.contents()); + put('\n'); + if (n >= nfont_positions) { + int old_nfont_positions = nfont_positions; + symbol *old_font_position = font_position; + nfont_positions *= 3; + nfont_positions /= 2; + if (nfont_positions <= n) + nfont_positions = n + 10; + font_position = new symbol[nfont_positions]; + memcpy(font_position, old_font_position, + old_nfont_positions*sizeof(symbol)); + a_delete old_font_position; + } + font_position[n] = nm; + } + if (current_font_number != n) { + put('f'); + put(n); + put('\n'); + current_font_number = n; + } + int size = tf->get_size().to_scaled_points(); + if (current_size != size) { + put('s'); + put(size); + put('\n'); + current_size = size; + } + int slant = tf->get_slant(); + if (current_slant != slant) { + put("x Slant "); + put(slant); + put('\n'); + current_slant = slant; + } + int height = tf->get_height(); + if (current_height != height) { + put("x Height "); + put(height == 0 ? current_size : height); + put('\n'); + current_height = height; + } + current_tfont = tf; +} + +// fill_color calls `flush_tbuf' and `do_motion' if necessary. + +void troff_output_file::fill_color(color *col) +{ + if (!col || current_fill_color == col) + return; + current_fill_color = col; + if (!color_flag) + return; + flush_tbuf(); + do_motion(); + 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'); +} + +// glyph_color calls `flush_tbuf' and `do_motion' if necessary. + +void troff_output_file::glyph_color(color *col) +{ + if (!col || current_glyph_color == col) + return; + current_glyph_color = col; + if (!color_flag) + return; + flush_tbuf(); + // grotty doesn't like a color command if the vertical position is zero. + do_motion(); + 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'); +} + +// 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': + // only the h field is used when defining a circle + check_output_limits(output_hpos, + output_vpos - point[0].h.to_units()/2); + check_output_limits(output_hpos + point[0].h.to_units(), + output_vpos + point[0].h.to_units()/2); + break; + case 'E': + case 'e': + check_output_limits(output_hpos, + output_vpos - point[0].v.to_units()/2); + check_output_limits(output_hpos + point[0].h.to_units(), + output_vpos + point[0].v.to_units()/2); + break; + case 'P': + case 'p': + 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; + case 't': + x = output_hpos; + y = output_vpos; + for (i = 0; i < npoints; i++) { + x += point[i].h.to_units(); + y += point[i].v.to_units(); + 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: + x = output_hpos; + y = output_vpos; + for (i = 0; i < npoints; i++) { + x += point[i].h.to_units(); + y += point[i].v.to_units(); + check_output_limits(x, y); + } + } +} + +void troff_output_file::draw(char code, hvpair *point, int npoints, + font_size fsize, color *gcol, color *fcol) +{ + int i; + glyph_color(gcol); + fill_color(fcol); + flush_tbuf(); + do_motion(); + 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[0].h.to_units()); + } + 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(); + hpos = output_hpos; + if (code != 'e') { + for (i = 0; i < npoints; i++) + output_vpos += point[i].v.to_units(); + vpos = output_vpos; + } + if (is_on()) + put('\n'); +} + +void troff_output_file::really_on() +{ + flush_tbuf(); + force_motion = 1; + do_motion(); +} + +void troff_output_file::really_off() +{ + flush_tbuf(); +} + +void troff_output_file::really_put_filename(const char *filename) +{ + flush_tbuf(); + put("F "); + put(filename); + put('\n'); +} + +void troff_output_file::really_begin_page(int pageno, vunits page_length) +{ + flush_tbuf(); + if (begun_page) { + if (page_length > V0) { + put('V'); + put(page_length.to_units()); + put('\n'); + } + } + else + begun_page = 1; + current_tfont = 0; + current_font_number = -1; + current_size = 0; + // current_height = 0; + // current_slant = 0; + hpos = 0; + vpos = 0; + output_hpos = 0; + output_vpos = 0; + force_motion = 1; + for (int i = 0; i < nfont_positions; i++) + font_position[i] = NULL_SYMBOL; + put('p'); + put(pageno); + put('\n'); +} + +void troff_output_file::really_copy_file(hunits x, vunits y, const char *filename) +{ + moveto(x, y); + flush_tbuf(); + do_motion(); + errno = 0; + FILE *ifp = fopen(filename, "r"); + if (ifp == 0) + error("can't open `%1': %2", filename, strerror(errno)); + else { + int c; + while ((c = getc(ifp)) != EOF) + put(char(c)); + fclose(ifp); + } + force_motion = 1; + current_size = 0; + current_tfont = 0; + current_font_number = -1; + for (int i = 0; i < nfont_positions; i++) + font_position[i] = NULL_SYMBOL; +} + +void troff_output_file::really_transparent_char(unsigned char c) +{ + put(c); +} + +troff_output_file::~troff_output_file() +{ + a_delete font_position; +} + +void troff_output_file::trailer(vunits page_length) +{ + flush_tbuf(); + if (page_length > V0) { + put("x trailer\n"); + put('V'); + put(page_length.to_units()); + put('\n'); + } + put("x stop\n"); +} + +troff_output_file::troff_output_file() +: current_slant(0), current_height(0), current_fill_color(0), + current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0) +{ + font_position = new symbol[nfont_positions]; + put("x T "); + put(device); + put('\n'); + put("x res "); + put(units_per_inch); + put(' '); + put(hresolution); + put(' '); + put(vresolution); + put('\n'); + put("x init\n"); +} + +/* output_file */ + +output_file *the_output = 0; + +output_file::output_file() +{ +} + +output_file::~output_file() +{ +} + +void output_file::trailer(vunits) +{ +} + +void output_file::put_filename(const char *) +{ +} + +void output_file::on() +{ +} + +void output_file::off() +{ +} + +real_output_file::real_output_file() +: printing(0), output_on(1) +{ +#ifndef POPEN_MISSING + if (pipe_command) { + if ((fp = popen(pipe_command, POPEN_WT)) != 0) { + piped = 1; + return; + } + error("pipe open failed: %1", strerror(errno)); + } + piped = 0; +#endif /* not POPEN_MISSING */ + fp = stdout; +} + +real_output_file::~real_output_file() +{ + if (!fp) + return; + // To avoid looping, set fp to 0 before calling fatal(). + if (ferror(fp) || fflush(fp) < 0) { + fp = 0; + fatal("error writing output file"); + } +#ifndef POPEN_MISSING + if (piped) { + int result = pclose(fp); + fp = 0; + if (result < 0) + fatal("pclose failed"); + if (!WIFEXITED(result)) + error("output process `%1' got fatal signal %2", + pipe_command, + WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result)); + else { + int exit_status = WEXITSTATUS(result); + if (exit_status != 0) + error("output process `%1' exited with status %2", + pipe_command, exit_status); + } + } + else +#endif /* not POPEN MISSING */ + if (fclose(fp) < 0) { + fp = 0; + fatal("error closing output file"); + } +} + +void real_output_file::flush() +{ + if (fflush(fp) < 0) + fatal("error writing output file"); +} + +int real_output_file::is_printing() +{ + return printing; +} + +void real_output_file::begin_page(int pageno, vunits page_length) +{ + printing = in_output_page_list(pageno); + if (printing) + really_begin_page(pageno, page_length); +} + +void real_output_file::copy_file(hunits x, vunits y, const char *filename) +{ + if (printing && output_on) + really_copy_file(x, y, filename); + check_output_limits(x.to_units(), y.to_units()); +} + +void real_output_file::transparent_char(unsigned char c) +{ + if (printing && output_on) + really_transparent_char(c); +} + +void real_output_file::print_line(hunits x, vunits y, node *n, + vunits before, vunits after, hunits width) +{ + if (printing) + really_print_line(x, y, n, before, after, width); + delete_node_list(n); +} + +void real_output_file::really_copy_file(hunits, vunits, const char *) +{ + // do nothing +} + +void real_output_file::put_filename(const char *filename) +{ + really_put_filename(filename); +} + +void real_output_file::really_put_filename(const char *) +{ +} + +void real_output_file::on() +{ + really_on(); + if (output_on == 0) + output_on = 1; +} + +void real_output_file::off() +{ + really_off(); + output_on = 0; +} + +int real_output_file::is_on() +{ + return output_on; +} + +void real_output_file::really_on() +{ +} + +void real_output_file::really_off() +{ +} + +/* ascii_output_file */ + +void ascii_output_file::really_transparent_char(unsigned char c) +{ + putc(c, fp); +} + +void ascii_output_file::really_print_line(hunits, vunits, node *n, + vunits, vunits, hunits) +{ + while (n != 0) { + n->ascii_print(this); + n = n->next; + } + fputc('\n', fp); +} + +void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/) +{ + fputs("\n", fp); +} + +ascii_output_file::ascii_output_file() +{ +} + +/* suppress_output_file */ + +suppress_output_file::suppress_output_file() +{ +} + +void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits) +{ +} + +void suppress_output_file::really_begin_page(int, vunits) +{ +} + +void suppress_output_file::really_transparent_char(unsigned char) +{ +} + +/* glyphs, ligatures, kerns, discretionary breaks */ + +class charinfo_node : public node { +protected: + charinfo *ci; +public: + charinfo_node(charinfo *, node * = 0); + int ends_sentence(); + int overlaps_vertically(); + int overlaps_horizontally(); +}; + +charinfo_node::charinfo_node(charinfo *c, node *x) +: node(x), ci(c) +{ +} + +int charinfo_node::ends_sentence() +{ + if (ci->ends_sentence()) + return 1; + else if (ci->transparent()) + return 2; + else + return 0; +} + +int charinfo_node::overlaps_horizontally() +{ + return ci->overlaps_horizontally(); +} + +int charinfo_node::overlaps_vertically() +{ + return ci->overlaps_vertically(); +} + +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 *, color *, color *, hunits, node * = 0); +#endif +public: + void *operator new(size_t); + void operator delete(void *); + glyph_node(charinfo *, tfont *, color *, color *, node * = 0); + ~glyph_node() {} + node *copy(); + node *merge_glyph_node(glyph_node *); + node *merge_self(node *); + hunits width(); + node *last_char_node(); + units size(); + void vertical_extent(vunits *, vunits *); + hunits subscript_correction(); + hunits italic_correction(); + hunits left_italic_correction(); + 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 *, int *); + node *add_self(node *, hyphen_list **); + void ascii_print(ascii_output_file *); + void asciify(macro *); + int character_type(); + int same(node *); + const char *type(); + int force_tprint(); +}; + +glyph_node *glyph_node::free_list = 0; + +class ligature_node : public glyph_node { + node *n1; + node *n2; +#ifdef STORE_WIDTH + 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 *, color *, color *, + node *, node *, node * = 0); + ~ligature_node(); + node *copy(); + node *add_self(node *, hyphen_list **); + hyphen_list *get_hyphen_list(hyphen_list *, int *); + void ascii_print(ascii_output_file *); + void asciify(macro *); + int same(node *); + const char *type(); + int force_tprint(); +}; + +class kern_pair_node : public node { + hunits amount; + node *n1; + node *n2; +public: + kern_pair_node(hunits n, node *first, node *second, node *x = 0); + ~kern_pair_node(); + node *copy(); + node *merge_glyph_node(glyph_node *); + node *add_self(node *, hyphen_list **); + hyphen_list *get_hyphen_list(hyphen_list *, int *); + node *add_discretionary_hyphen(); + hunits width(); + node *last_char_node(); + hunits italic_correction(); + hunits subscript_correction(); + void tprint(troff_output_file *); + hyphenation_type get_hyphenation_type(); + int ends_sentence(); + void ascii_print(ascii_output_file *); + void asciify(macro *); + int same(node *); + const char *type(); + int force_tprint(); + void vertical_extent(vunits *, vunits *); +}; + +class dbreak_node : public node { + node *none; + node *pre; + node *post; +public: + dbreak_node(node *n, node *p, node *x = 0); + ~dbreak_node(); + node *copy(); + node *merge_glyph_node(glyph_node *); + node *add_discretionary_hyphen(); + hunits width(); + node *last_char_node(); + hunits italic_correction(); + hunits subscript_correction(); + void tprint(troff_output_file *); + breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0, + int is_inner = 0); + int nbreaks(); + int ends_sentence(); + void split(int, node **, node **); + hyphenation_type get_hyphenation_type(); + void ascii_print(ascii_output_file *); + void asciify(macro *); + int same(node *); + const char *type(); + int force_tprint(); +}; + +void *glyph_node::operator new(size_t n) +{ + assert(n == sizeof(glyph_node)); + if (!free_list) { + const int BLOCK = 1024; + free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK]; + for (int i = 0; i < BLOCK - 1; i++) + free_list[i].next = free_list + i + 1; + free_list[BLOCK-1].next = 0; + } + glyph_node *p = free_list; + free_list = (glyph_node *)(free_list->next); + p->next = 0; + return p; +} + +void *ligature_node::operator new(size_t n) +{ + return new char[n]; +} + +void glyph_node::operator delete(void *p) +{ + if (p) { + ((glyph_node *)p)->next = free_list; + free_list = (glyph_node *)p; + } +} + +void ligature_node::operator delete(void *p) +{ + delete[] (char *)p; +} + +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); +#endif +} + +#ifdef STORE_WIDTH +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 + +node *glyph_node::copy() +{ +#ifdef STORE_WIDTH + return new glyph_node(ci, tf, gcol, fcol, wid); +#else + return new glyph_node(ci, tf, gcol, fcol); +#endif +} + +node *glyph_node::merge_self(node *nd) +{ + return nd->merge_glyph_node(this); +} + +int glyph_node::character_type() +{ + return tf->get_character_type(ci); +} + +node *glyph_node::add_self(node *n, hyphen_list **p) +{ + assert(ci->get_hyphenation_code() == (*p)->hyphenation_code); + next = 0; + node *nn; + if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) { + next = n; + nn = this; + } + if ((*p)->hyphen) + nn = nn->add_discretionary_hyphen(); + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return nn; +} + +units glyph_node::size() +{ + return tf->get_size().to_units(); +} + +hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count) +{ + (*count)++; + return new hyphen_list(ci->get_hyphenation_code(), tail); +} + +tfont *node::get_tfont() +{ + return 0; +} + +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 *) +{ + return 0; +} + +node *glyph_node::merge_glyph_node(glyph_node *gn) +{ + 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, gcol, fcol, this, gn, next1); + } + hunits kern; + if (tf->get_kern(ci, gn->ci, &kern)) { + node *next1 = next; + next = 0; + return new kern_pair_node(kern, this, gn, next1); + } + } + return 0; +} + +#ifdef STORE_WIDTH +inline +#endif +hunits glyph_node::width() +{ +#ifdef STORE_WIDTH + return wid; +#else + return tf->get_width(ci); +#endif +} + +node *glyph_node::last_char_node() +{ + return this; +} + +void glyph_node::vertical_extent(vunits *min, vunits *max) +{ + *min = -tf->get_char_height(ci); + *max = tf->get_char_depth(ci); +} + +hunits glyph_node::skew() +{ + return tf->get_char_skew(ci); +} + +hunits glyph_node::subscript_correction() +{ + return tf->get_subscript_correction(ci); +} + +hunits glyph_node::italic_correction() +{ + return tf->get_italic_correction(ci); +} + +hunits glyph_node::left_italic_correction() +{ + return tf->get_left_italic_correction(ci); +} + +hyphenation_type glyph_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +void glyph_node::ascii_print(ascii_output_file *ascii) +{ + unsigned char c = ci->get_ascii_code(); + if (c != 0) + ascii->outc(c); + else + ascii->outs(ci->nm.contents()); +} + +ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc, + node *gn1, node *gn2, node *x) +: glyph_node(c, t, gc, fc, x), n1(gn1), n2(gn2) +{ +} + +#ifdef STORE_WIDTH +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 + +ligature_node::~ligature_node() +{ + delete n1; + delete n2; +} + +node *ligature_node::copy() +{ +#ifdef STORE_WIDTH + return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy()); +#else + return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy()); +#endif +} + +void ligature_node::ascii_print(ascii_output_file *ascii) +{ + n1->ascii_print(ascii); + n2->ascii_print(ascii); +} + +hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count) +{ + hyphen_list *hl = n2->get_hyphen_list(tail, count); + return n1->get_hyphen_list(hl, count); +} + +node *ligature_node::add_self(node *n, hyphen_list **p) +{ + n = n1->add_self(n, p); + n = n2->add_self(n, p); + n1 = n2 = 0; + delete this; + return n; +} + +kern_pair_node::kern_pair_node(hunits n, node *first, node *second, node *x) +: node(x), amount(n), n1(first), n2(second) +{ +} + +dbreak_node::dbreak_node(node *n, node *p, node *x) +: node(x), none(n), pre(p), post(0) +{ +} + +node *dbreak_node::merge_glyph_node(glyph_node *gn) +{ + glyph_node *gn2 = (glyph_node *)gn->copy(); + node *new_none = none ? none->merge_glyph_node(gn) : 0; + node *new_post = post ? post->merge_glyph_node(gn2) : 0; + if (new_none == 0 && new_post == 0) { + delete gn2; + return 0; + } + if (new_none != 0) + none = new_none; + else { + gn->next = none; + none = gn; + } + if (new_post != 0) + post = new_post; + else { + gn2->next = post; + post = gn2; + } + return this; +} + +node *kern_pair_node::merge_glyph_node(glyph_node *gn) +{ + node *nd = n2->merge_glyph_node(gn); + if (nd == 0) + return 0; + n2 = nd; + nd = n2->merge_self(n1); + if (nd) { + nd->next = next; + n1 = 0; + n2 = 0; + delete this; + return nd; + } + return this; +} + +hunits kern_pair_node::italic_correction() +{ + return n2->italic_correction(); +} + +hunits kern_pair_node::subscript_correction() +{ + return n2->subscript_correction(); +} + +void kern_pair_node::vertical_extent(vunits *min, vunits *max) +{ + n1->vertical_extent(min, max); + vunits min2, max2; + n2->vertical_extent(&min2, &max2); + if (min2 < *min) + *min = min2; + if (max2 > *max) + *max = max2; +} + +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, gcol, fcol); + node *nn = n->merge_glyph_node(gn); + if (nn == 0) { + gn->next = n; + nn = gn; + } + return new dbreak_node(this, nn, next1); + } + } + return this; +} + +kern_pair_node::~kern_pair_node() +{ + if (n1 != 0) + delete n1; + if (n2 != 0) + delete n2; +} + +dbreak_node::~dbreak_node() +{ + delete_node_list(pre); + delete_node_list(post); + delete_node_list(none); +} + +node *kern_pair_node::copy() +{ + return new kern_pair_node(amount, n1->copy(), n2->copy()); +} + +node *copy_node_list(node *n) +{ + node *p = 0; + while (n != 0) { + node *nn = n->copy(); + nn->next = p; + p = nn; + n = n->next; + } + while (p != 0) { + node *pp = p->next; + p->next = n; + n = p; + p = pp; + } + return n; +} + +void delete_node_list(node *n) +{ + while (n != 0) { + node *tem = n; + n = n->next; + delete tem; + } +} + +node *dbreak_node::copy() +{ + dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre)); + p->post = copy_node_list(post); + return p; +} + +hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *) +{ + return tail; +} + +hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count) +{ + hyphen_list *hl = n2->get_hyphen_list(tail, count); + return n1->get_hyphen_list(hl, count); +} + +class hyphen_inhibitor_node : public node { +public: + hyphen_inhibitor_node(node *nd = 0); + node *copy(); + int same(node *); + const char *type(); + int force_tprint(); + hyphenation_type get_hyphenation_type(); +}; + +hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd) +{ +} + +node *hyphen_inhibitor_node::copy() +{ + return new hyphen_inhibitor_node; +} + +int hyphen_inhibitor_node::same(node *) +{ + return 1; +} + +const char *hyphen_inhibitor_node::type() +{ + return "hyphen_inhibitor_node"; +} + +int hyphen_inhibitor_node::force_tprint() +{ + return 0; +} + +hyphenation_type hyphen_inhibitor_node::get_hyphenation_type() +{ + return HYPHEN_INHIBIT; +} + +/* add_discretionary_hyphen methods */ + +node *dbreak_node::add_discretionary_hyphen() +{ + if (post) + post = post->add_discretionary_hyphen(); + if (none) + none = none->add_discretionary_hyphen(); + return this; +} + +node *node::add_discretionary_hyphen() +{ + tfont *tf = get_tfont(); + 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, gcol, fcol); + node *n1 = n->merge_glyph_node(gn); + if (n1 == 0) { + gn->next = n; + n1 = gn; + } + return new dbreak_node(this, n1, next1); + } + return this; +} + +node *node::merge_self(node *) +{ + return 0; +} + +node *node::add_self(node *n, hyphen_list ** /*p*/) +{ + next = n; + return this; +} + +node *kern_pair_node::add_self(node *n, hyphen_list **p) +{ + n = n1->add_self(n, p); + n = n2->add_self(n, p); + n1 = n2 = 0; + delete this; + return n; +} + +hunits node::width() +{ + return H0; +} + +node *node::last_char_node() +{ + return 0; +} + +int node::force_tprint() +{ + return 0; +} + +hunits hmotion_node::width() +{ + return n; +} + +units node::size() +{ + return points_to_units(10); +} + +hunits kern_pair_node::width() +{ + return n1->width() + n2->width() + amount; +} + +node *kern_pair_node::last_char_node() +{ + node *nd = n2->last_char_node(); + if (nd) + return nd; + return n1->last_char_node(); +} + +hunits dbreak_node::width() +{ + hunits x = H0; + for (node *n = none; n != 0; n = n->next) + x += n->width(); + return x; +} + +node *dbreak_node::last_char_node() +{ + for (node *n = none; n; n = n->next) { + node *last = n->last_char_node(); + if (last) + return last; + } + return 0; +} + +hunits dbreak_node::italic_correction() +{ + return none ? none->italic_correction() : H0; +} + +hunits dbreak_node::subscript_correction() +{ + return none ? none->subscript_correction() : H0; +} + +class italic_corrected_node : public node { + node *n; + hunits x; +public: + italic_corrected_node(node *, hunits, node * = 0); + ~italic_corrected_node(); + node *copy(); + void ascii_print(ascii_output_file *); + void asciify(macro *); + hunits width(); + node *last_char_node(); + void vertical_extent(vunits *, vunits *); + int ends_sentence(); + int overlaps_horizontally(); + int overlaps_vertically(); + int same(node *); + hyphenation_type get_hyphenation_type(); + tfont *get_tfont(); + hyphen_list *get_hyphen_list(hyphen_list *, int *); + int character_type(); + void tprint(troff_output_file *); + hunits subscript_correction(); + hunits skew(); + node *add_self(node *, hyphen_list **); + const char *type(); + int force_tprint(); +}; + +node *node::add_italic_correction(hunits *width) +{ + hunits ic = italic_correction(); + if (ic.is_zero()) + return this; + else { + node *next1 = next; + next = 0; + *width += ic; + return new italic_corrected_node(this, ic, next1); + } +} + +italic_corrected_node::italic_corrected_node(node *nn, hunits xx, node *p) +: node(p), n(nn), x(xx) +{ + assert(n != 0); +} + +italic_corrected_node::~italic_corrected_node() +{ + delete n; +} + +node *italic_corrected_node::copy() +{ + return new italic_corrected_node(n->copy(), x); +} + +hunits italic_corrected_node::width() +{ + return n->width() + x; +} + +void italic_corrected_node::vertical_extent(vunits *min, vunits *max) +{ + n->vertical_extent(min, max); +} + +void italic_corrected_node::tprint(troff_output_file *out) +{ + n->tprint(out); + out->right(x); +} + +hunits italic_corrected_node::skew() +{ + return n->skew() - x/2; +} + +hunits italic_corrected_node::subscript_correction() +{ + return n->subscript_correction() - x; +} + +void italic_corrected_node::ascii_print(ascii_output_file *out) +{ + n->ascii_print(out); +} + +int italic_corrected_node::ends_sentence() +{ + return n->ends_sentence(); +} + +int italic_corrected_node::overlaps_horizontally() +{ + return n->overlaps_horizontally(); +} + +int italic_corrected_node::overlaps_vertically() +{ + return n->overlaps_vertically(); +} + +node *italic_corrected_node::last_char_node() +{ + return n->last_char_node(); +} + +tfont *italic_corrected_node::get_tfont() +{ + return n->get_tfont(); +} + +hyphenation_type italic_corrected_node::get_hyphenation_type() +{ + return n->get_hyphenation_type(); +} + +node *italic_corrected_node::add_self(node *nd, hyphen_list **p) +{ + nd = n->add_self(nd, p); + hunits not_interested; + nd = nd->add_italic_correction(¬_interested); + n = 0; + delete this; + return nd; +} + +hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail, + int *count) +{ + return n->get_hyphen_list(tail, count); +} + +int italic_corrected_node::character_type() +{ + return n->character_type(); +} + +class break_char_node : public node { + node *ch; + char break_code; + color *col; +public: + break_char_node(node *, int, color *, node * = 0); + ~break_char_node(); + node *copy(); + hunits width(); + vunits vertical_width(); + node *last_char_node(); + int character_type(); + int ends_sentence(); + node *add_self(node *, hyphen_list **); + hyphen_list *get_hyphen_list(hyphen_list *, int *); + void tprint(troff_output_file *); + void zero_width_tprint(troff_output_file *); + void ascii_print(ascii_output_file *); + void asciify(macro *); + hyphenation_type get_hyphenation_type(); + int overlaps_vertically(); + int overlaps_horizontally(); + units size(); + tfont *get_tfont(); + int same(node *); + const char *type(); + int force_tprint(); +}; + +break_char_node::break_char_node(node *n, int bc, color *c, node *x) +: node(x), ch(n), break_code(bc), col(c) +{ +} + +break_char_node::~break_char_node() +{ + delete ch; +} + +node *break_char_node::copy() +{ + return new break_char_node(ch->copy(), break_code, col); +} + +hunits break_char_node::width() +{ + return ch->width(); +} + +vunits break_char_node::vertical_width() +{ + return ch->vertical_width(); +} + +node *break_char_node::last_char_node() +{ + return ch->last_char_node(); +} + +int break_char_node::character_type() +{ + return ch->character_type(); +} + +int break_char_node::ends_sentence() +{ + return ch->ends_sentence(); +} + +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, col, n); + n->freeze_space(); + } + next = n; + n = this; + if ((*p)->breakable && (break_code & 2)) { + n = new space_node(H0, col, n); + n->freeze_space(); + } + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return n; +} + +hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *) +{ + return new hyphen_list(0, tail); +} + +hyphenation_type break_char_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +void break_char_node::ascii_print(ascii_output_file *ascii) +{ + ch->ascii_print(ascii); +} + +int break_char_node::overlaps_vertically() +{ + return ch->overlaps_vertically(); +} + +int break_char_node::overlaps_horizontally() +{ + return ch->overlaps_horizontally(); +} + +units break_char_node::size() +{ + return ch->size(); +} + +tfont *break_char_node::get_tfont() +{ + return ch->get_tfont(); +} + +node *extra_size_node::copy() +{ + return new extra_size_node(n); +} + +node *vertical_size_node::copy() +{ + return new vertical_size_node(n); +} + +node *hmotion_node::copy() +{ + return new hmotion_node(n, was_tab, unformat, col); +} + +node *space_char_hmotion_node::copy() +{ + return new space_char_hmotion_node(n, col); +} + +node *vmotion_node::copy() +{ + return new vmotion_node(n, col); +} + +node *dummy_node::copy() +{ + return new dummy_node; +} + +node *transparent_dummy_node::copy() +{ + return new transparent_dummy_node; +} + +hline_node::~hline_node() +{ + if (n) + delete n; +} + +node *hline_node::copy() +{ + return new hline_node(x, n ? n->copy() : 0); +} + +hunits hline_node::width() +{ + return x < H0 ? H0 : x; +} + +vline_node::~vline_node() +{ + if (n) + delete n; +} + +node *vline_node::copy() +{ + return new vline_node(x, n ? n->copy() : 0); +} + +hunits vline_node::width() +{ + return n == 0 ? H0 : n->width(); +} + +zero_width_node::zero_width_node(node *nd) : n(nd) +{ +} + +zero_width_node::~zero_width_node() +{ + delete_node_list(n); +} + +node *zero_width_node::copy() +{ + return new zero_width_node(copy_node_list(n)); +} + +int node_list_character_type(node *p) +{ + int t = 0; + for (; p; p = p->next) + t |= p->character_type(); + return t; +} + +int zero_width_node::character_type() +{ + return node_list_character_type(n); +} + +void node_list_vertical_extent(node *p, vunits *min, vunits *max) +{ + *min = V0; + *max = V0; + vunits cur_vpos = V0; + vunits v1, v2; + for (; p; p = p->next) { + p->vertical_extent(&v1, &v2); + v1 += cur_vpos; + if (v1 < *min) + *min = v1; + v2 += cur_vpos; + if (v2 > *max) + *max = v2; + cur_vpos += p->vertical_width(); + } +} + +void zero_width_node::vertical_extent(vunits *min, vunits *max) +{ + node_list_vertical_extent(n, min, max); +} + +overstrike_node::overstrike_node() : list(0), max_width(H0) +{ +} + +overstrike_node::~overstrike_node() +{ + delete_node_list(list); +} + +node *overstrike_node::copy() +{ + overstrike_node *on = new overstrike_node; + for (node *tem = list; tem; tem = tem->next) + on->overstrike(tem->copy()); + return on; +} + +void overstrike_node::overstrike(node *n) +{ + if (n == 0) + return; + hunits w = n->width(); + if (w > max_width) + max_width = w; + node **p; + for (p = &list; *p; p = &(*p)->next) + ; + n->next = 0; + *p = n; +} + +hunits overstrike_node::width() +{ + return max_width; +} + +bracket_node::bracket_node() : list(0), max_width(H0) +{ +} + +bracket_node::~bracket_node() +{ + delete_node_list(list); +} + +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; + last = tem; + } + for (tem = last; tem; tem = tem->last) + on->bracket(tem->copy()); + return on; +} + +void bracket_node::bracket(node *n) +{ + if (n == 0) + return; + hunits w = n->width(); + if (w > max_width) + max_width = w; + n->next = list; + list = n; +} + +hunits bracket_node::width() +{ + return max_width; +} + +int node::nspaces() +{ + return 0; +} + +int node::merge_space(hunits, hunits, hunits) +{ + return 0; +} + +#if 0 +space_node *space_node::free_list = 0; + +void *space_node::operator new(size_t n) +{ + assert(n == sizeof(space_node)); + if (!free_list) { + free_list = (space_node *)new char[sizeof(space_node)*BLOCK]; + for (int i = 0; i < BLOCK - 1; i++) + free_list[i].next = free_list + i + 1; + free_list[BLOCK-1].next = 0; + } + space_node *p = free_list; + free_list = (space_node *)(free_list->next); + p->next = 0; + return p; +} + +inline void space_node::operator delete(void *p) +{ + if (p) { + ((space_node *)p)->next = free_list; + free_list = (space_node *)p; + } +} +#endif + +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, color *c, node *p) +: node(p), n(nn), set(s), was_escape_colon(flag), col(c) +{ +} + +#if 0 +space_node::~space_node() +{ +} +#endif + +node *space_node::copy() +{ + return new space_node(n, set, was_escape_colon, col); +} + +int space_node::force_tprint() +{ + return 0; +} + +int space_node::nspaces() +{ + return set ? 0 : 1; +} + +int space_node::merge_space(hunits h, hunits, hunits) +{ + n += h; + return 1; +} + +hunits space_node::width() +{ + return n; +} + +void node::spread_space(int*, hunits*) +{ +} + +void space_node::spread_space(int *nspaces, hunits *desired_space) +{ + if (!set) { + assert(*nspaces > 0); + if (*nspaces == 1) { + n += *desired_space; + *desired_space = H0; + } + else { + hunits extra = *desired_space / *nspaces; + *desired_space -= extra; + n += extra; + } + *nspaces -= 1; + set = 1; + } +} + +void node::freeze_space() +{ +} + +void space_node::freeze_space() +{ + set = 1; +} + +void node::is_escape_colon() +{ +} + +void space_node::is_escape_colon() +{ + was_escape_colon = 1; +} + +diverted_space_node::diverted_space_node(vunits d, node *p) +: node(p), n(d) +{ +} + +node *diverted_space_node::copy() +{ + return new diverted_space_node(n); +} + +diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p) +: node(p), filename(s) +{ +} + +node *diverted_copy_file_node::copy() +{ + return new diverted_copy_file_node(filename); +} + +int node::ends_sentence() +{ + return 0; +} + +int kern_pair_node::ends_sentence() +{ + switch (n2->ends_sentence()) { + case 0: + return 0; + case 1: + return 1; + case 2: + break; + default: + assert(0); + } + return n1->ends_sentence(); +} + +int node_list_ends_sentence(node *n) +{ + for (; n != 0; n = n->next) + switch (n->ends_sentence()) { + case 0: + return 0; + case 1: + return 1; + case 2: + break; + default: + assert(0); + } + return 2; +} + +int dbreak_node::ends_sentence() +{ + return node_list_ends_sentence(none); +} + +int node::overlaps_horizontally() +{ + return 0; +} + +int node::overlaps_vertically() +{ + return 0; +} + +int node::discardable() +{ + return 0; +} + +int space_node::discardable() +{ + return set ? 0 : 1; +} + +vunits node::vertical_width() +{ + return V0; +} + +vunits vline_node::vertical_width() +{ + return x; +} + +vunits vmotion_node::vertical_width() +{ + return n; +} + +int node::set_unformat_flag() +{ + return 1; +} + +int node::character_type() +{ + return 0; +} + +hunits node::subscript_correction() +{ + return H0; +} + +hunits node::italic_correction() +{ + return H0; +} + +hunits node::left_italic_correction() +{ + return H0; +} + +hunits node::skew() +{ + return H0; +} + +/* vertical_extent methods */ + +void node::vertical_extent(vunits *min, vunits *max) +{ + vunits v = vertical_width(); + if (v < V0) { + *min = v; + *max = V0; + } + else { + *max = v; + *min = V0; + } +} + +void vline_node::vertical_extent(vunits *min, vunits *max) +{ + if (n == 0) + node::vertical_extent(min, max); + else { + vunits cmin, cmax; + n->vertical_extent(&cmin, &cmax); + vunits h = n->size(); + if (x < V0) { + if (-x < h) { + *min = x; + *max = V0; + } + else { + // we print the first character and then move up, so + *max = cmax; + // we print the last character and then move up h + *min = cmin + h; + if (*min > V0) + *min = V0; + *min += x; + } + } + else { + if (x < h) { + *max = x; + *min = V0; + } + else { + // we move down by h and then print the first character, so + *min = cmin + h; + if (*min > V0) + *min = V0; + *max = x + cmax; + } + } + } +} + +/* ascii_print methods */ + +static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n) +{ + if (n == 0) + return; + ascii_print_reverse_node_list(ascii, n->next); + n->ascii_print(ascii); +} + +void dbreak_node::ascii_print(ascii_output_file *ascii) +{ + ascii_print_reverse_node_list(ascii, none); +} + +void kern_pair_node::ascii_print(ascii_output_file *ascii) +{ + n1->ascii_print(ascii); + n2->ascii_print(ascii); +} + +void node::ascii_print(ascii_output_file *) +{ +} + +void space_node::ascii_print(ascii_output_file *ascii) +{ + if (!n.is_zero()) + ascii->outc(' '); +} + +void hmotion_node::ascii_print(ascii_output_file *ascii) +{ + // this is pretty arbitrary + if (n >= points_to_units(2)) + ascii->outc(' '); +} + +void space_char_hmotion_node::ascii_print(ascii_output_file *ascii) +{ + ascii->outc(' '); +} + +/* asciify methods */ + +void node::asciify(macro *m) +{ + m->append(this); +} + +void glyph_node::asciify(macro *m) +{ + unsigned char c = ci->get_asciify_code(); + if (c == 0) + c = ci->get_ascii_code(); + if (c != 0) { + m->append(c); + delete this; + } + else + m->append(this); +} + +void kern_pair_node::asciify(macro *m) +{ + n1->asciify(m); + n2->asciify(m); + n1 = n2 = 0; + delete this; +} + +static void asciify_reverse_node_list(macro *m, node *n) +{ + if (n == 0) + return; + asciify_reverse_node_list(m, n->next); + n->asciify(m); +} + +void dbreak_node::asciify(macro *m) +{ + asciify_reverse_node_list(m, none); + none = 0; + delete this; +} + +void ligature_node::asciify(macro *m) +{ + n1->asciify(m); + n2->asciify(m); + n1 = n2 = 0; + delete this; +} + +void break_char_node::asciify(macro *m) +{ + ch->asciify(m); + ch = 0; + delete this; +} + +void italic_corrected_node::asciify(macro *m) +{ + n->asciify(m); + n = 0; + delete this; +} + +void left_italic_corrected_node::asciify(macro *m) +{ + if (n) { + n->asciify(m); + n = 0; + } + delete this; +} + +void hmotion_node::asciify(macro *m) +{ + if (was_tab) { + m->append('\t'); + delete this; + } + else + m->append(this); +} + +space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c, + node *next) +: hmotion_node(i, c, next) +{ +} + +void space_char_hmotion_node::asciify(macro *m) +{ + m->append(ESCAPE_SPACE); + delete this; +} + +void space_node::asciify(macro *m) +{ + if (was_escape_colon) { + m->append(ESCAPE_COLON); + delete this; + } + else + m->append(this); +} + +void word_space_node::asciify(macro *m) +{ + for (width_list *w = orig_width; w; w = w->next) + m->append(' '); + delete this; +} + +void unbreakable_space_node::asciify(macro *m) +{ + m->append(ESCAPE_TILDE); + delete this; +} + +void line_start_node::asciify(macro *) +{ + delete this; +} + +void vertical_size_node::asciify(macro *) +{ + delete this; +} + +breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/, + breakpoint *rest, int /*is_inner*/) +{ + return rest; +} + +int node::nbreaks() +{ + return 0; +} + +breakpoint *space_node::get_breakpoints(hunits width, int ns, + breakpoint *rest, int is_inner) +{ + if (next->discardable()) + return rest; + breakpoint *bp = new breakpoint; + bp->next = rest; + bp->width = width; + bp->nspaces = ns; + bp->hyphenated = 0; + if (is_inner) { + assert(rest != 0); + bp->index = rest->index + 1; + bp->nd = rest->nd; + } + else { + bp->nd = this; + bp->index = 0; + } + return bp; +} + +int space_node::nbreaks() +{ + if (next->discardable()) + return 0; + else + return 1; +} + +static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp, + int ns, breakpoint *rest) +{ + if (p != 0) { + rest = p->get_breakpoints(*widthp, + ns, + node_list_get_breakpoints(p->next, widthp, ns, + rest), + 1); + *widthp += p->width(); + } + return rest; +} + +breakpoint *dbreak_node::get_breakpoints(hunits width, int ns, + breakpoint *rest, int is_inner) +{ + breakpoint *bp = new breakpoint; + bp->next = rest; + bp->width = width; + for (node *tem = pre; tem != 0; tem = tem->next) + bp->width += tem->width(); + bp->nspaces = ns; + bp->hyphenated = 1; + if (is_inner) { + assert(rest != 0); + bp->index = rest->index + 1; + bp->nd = rest->nd; + } + else { + bp->nd = this; + bp->index = 0; + } + return node_list_get_breakpoints(none, &width, ns, bp); +} + +int dbreak_node::nbreaks() +{ + int i = 1; + for (node *tem = none; tem != 0; tem = tem->next) + i += tem->nbreaks(); + return i; +} + +void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/) +{ + assert(0); +} + +void space_node::split(int where, node **pre, node **post) +{ + assert(where == 0); + *pre = next; + *post = 0; + delete this; +} + +static void node_list_split(node *p, int *wherep, node **prep, node **postp) +{ + if (p == 0) + return; + int nb = p->nbreaks(); + node_list_split(p->next, wherep, prep, postp); + if (*wherep < 0) { + p->next = *postp; + *postp = p; + } + else if (*wherep < nb) { + p->next = *prep; + p->split(*wherep, prep, postp); + } + else { + p->next = *prep; + *prep = p; + } + *wherep -= nb; +} + +void dbreak_node::split(int where, node **prep, node **postp) +{ + assert(where >= 0); + if (where == 0) { + *postp = post; + post = 0; + if (pre == 0) + *prep = next; + else { + node *tem; + for (tem = pre; tem->next != 0; tem = tem->next) + ; + tem->next = next; + *prep = pre; + } + pre = 0; + delete this; + } + else { + *prep = next; + where -= 1; + node_list_split(none, &where, prep, postp); + none = 0; + delete this; + } +} + +hyphenation_type node::get_hyphenation_type() +{ + return HYPHEN_BOUNDARY; +} + +hyphenation_type dbreak_node::get_hyphenation_type() +{ + return HYPHEN_INHIBIT; +} + +hyphenation_type kern_pair_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type dummy_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type transparent_dummy_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type hmotion_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type space_char_hmotion_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type overstrike_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +hyphenation_type space_node::get_hyphenation_type() +{ + if (was_escape_colon) + return HYPHEN_MIDDLE; + return HYPHEN_BOUNDARY; +} + +hyphenation_type unbreakable_space_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +int node::interpret(macro *) +{ + return 0; +} + +special_node::special_node(const macro &m, int n) +: mac(m), no_init_string(n) +{ + font_size fs = curenv->get_font_size(); + int char_height = curenv->get_char_height(); + int char_slant = curenv->get_char_slant(); + 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, + 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 + && gcol == ((special_node *)n)->gcol + && fcol == ((special_node *)n)->fcol + && no_init_string == ((special_node *)n)->no_init_string; +} + +const char *special_node::type() +{ + return "special_node"; +} + +int special_node::ends_sentence() +{ + return 2; +} + +int special_node::force_tprint() +{ + return 0; +} + +node *special_node::copy() +{ + return new special_node(mac, tf, gcol, fcol, no_init_string); +} + +void special_node::tprint_start(troff_output_file *out) +{ + out->start_special(tf, gcol, fcol, no_init_string); +} + +void special_node::tprint_char(troff_output_file *out, unsigned char c) +{ + out->special_char(c); +} + +void special_node::tprint_end(troff_output_file *out) +{ + out->end_special(); +} + +tfont *special_node::get_tfont() +{ + return tf; +} + +/* 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), image_id(0) +{ +} + +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, int id) +: is_on(on_or_off), emit_limits(issue_limits), + filename(f), position(p), image_id(id) +{ +} + +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) + && (image_id == ((suppress_node *)n)->image_id)); +} + +const char *suppress_node::type() +{ + return "suppress_node"; +} + +node *suppress_node::copy() +{ + return new suppress_node(emit_limits, is_on, filename, position, image_id); +} + +int get_reg_int(const char *p) +{ + reg *r = (reg *)number_reg_dictionary.lookup(p); + units prev_value; + if (r && (r->get_value(&prev_value))) + return (int)prev_value; + else + warning(WARN_REG, "number register `%1' not defined", p); + return 0; +} + +const char *get_reg_str(const char *p) +{ + reg *r = (reg *)number_reg_dictionary.lookup(p); + if (r) + return r->get_string(); + else + warning(WARN_REG, "register `%1' not defined", p); + return 0; +} + +void suppress_node::put(troff_output_file *out, const char *s) +{ + int i = 0; + while (s[i] != (char)0) { + out->special_char(s[i]); + i++; + } +} + +/* + * We need to remember the start of the image and its name. + */ + +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) +{ + return a < b ? a : b; +} + +/* + * tprint - if (is_on == 2) + * remember current position (l, r, c, i) and filename + * else + * if (emit_limits) + * if (html) + * emit image tag + * else + * emit postscript bounds for image + * else + * if (suppress boolean differs from current state) + * alter state + * reset registers + * record current page + * set low water mark. + */ + +void suppress_node::tprint(troff_output_file *out) +{ + 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; + const char *tem = last_image_filename; + last_image_filename = strsave(filename.contents()); + if (tem) + a_delete(tem); + 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]; + // remember that the filename will contain a %d in which the + // last_image_id is placed + sprintf(name, last_image_filename, last_image_id); + if (is_html) { + switch (last_position) { + case 'c': + out->start_special(); + put(out, "html-tag:.centered-image"); + break; + case 'r': + out->start_special(); + put(out, "html-tag:.right-image"); + break; + case 'l': + out->start_special(); + put(out, "html-tag:.left-image"); + break; + case 'i': + ; + default: + ; + } + out->end_special(); + out->start_special(); + put(out, "html-tag:.auto-image "); + put(out, name); + out->end_special(); + } + else { + // postscript (or other device) + 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", + topdiv->get_page_number(), + get_reg_int("opminx"), get_reg_int("opminy"), + get_reg_int("opmaxx"), get_reg_int("opmaxy"), + // page offset + line length + get_reg_int(".o") + get_reg_int(".l"), + name, hresolution, vresolution, get_reg_str(".F")); + fflush(stderr); + } + } + else { + if (is_on) { + out->on(); + // lastly we reset the output registers + reset_output_registers(); + } + else + out->off(); + suppress_start_page = current_page; + } + } +} + +int suppress_node::force_tprint() +{ + return is_on; +} + +hunits suppress_node::width() +{ + return H0; +} + +/* composite_node */ + +class composite_node : public charinfo_node { + node *n; + tfont *tf; +public: + composite_node(node *, charinfo *, tfont *, node * = 0); + ~composite_node(); + node *copy(); + hunits width(); + node *last_char_node(); + units size(); + void tprint(troff_output_file *); + hyphenation_type get_hyphenation_type(); + void ascii_print(ascii_output_file *); + void asciify(macro *); + hyphen_list *get_hyphen_list(hyphen_list *, int *); + node *add_self(node *, hyphen_list **); + tfont *get_tfont(); + int same(node *); + const char *type(); + int force_tprint(); + void vertical_extent(vunits *, vunits *); + vunits vertical_width(); +}; + +composite_node::composite_node(node *p, charinfo *c, tfont *t, node *x) +: charinfo_node(c, x), n(p), tf(t) +{ +} + +composite_node::~composite_node() +{ + delete_node_list(n); +} + +node *composite_node::copy() +{ + return new composite_node(copy_node_list(n), ci, tf); +} + +hunits composite_node::width() +{ + hunits x; + if (tf->get_constant_space(&x)) + return x; + x = H0; + for (node *tem = n; tem; tem = tem->next) + x += tem->width(); + hunits offset; + if (tf->get_bold(&offset)) + x += offset; + x += tf->get_track_kern(); + return x; +} + +node *composite_node::last_char_node() +{ + return this; +} + +vunits composite_node::vertical_width() +{ + vunits v = V0; + for (node *tem = n; tem; tem = tem->next) + v += tem->vertical_width(); + return v; +} + +units composite_node::size() +{ + return tf->get_size().to_units(); +} + +hyphenation_type composite_node::get_hyphenation_type() +{ + return HYPHEN_MIDDLE; +} + +void composite_node::asciify(macro *m) +{ + unsigned char c = ci->get_asciify_code(); + if (c == 0) + c = ci->get_ascii_code(); + if (c != 0) { + m->append(c); + delete this; + } + else + m->append(this); +} + +void composite_node::ascii_print(ascii_output_file *ascii) +{ + unsigned char c = ci->get_ascii_code(); + if (c != 0) + ascii->outc(c); + else + ascii->outs(ci->nm.contents()); + +} + +hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count) +{ + (*count)++; + return new hyphen_list(ci->get_hyphenation_code(), tail); +} + +node *composite_node::add_self(node *nn, hyphen_list **p) +{ + assert(ci->get_hyphenation_code() == (*p)->hyphenation_code); + next = nn; + nn = this; + if ((*p)->hyphen) + nn = nn->add_discretionary_hyphen(); + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return nn; +} + +tfont *composite_node::get_tfont() +{ + return tf; +} + +node *reverse_node_list(node *n) +{ + node *r = 0; + while (n) { + node *tem = n; + n = n->next; + tem->next = r; + r = tem; + } + return r; +} + +void composite_node::vertical_extent(vunits *min, vunits *max) +{ + n = reverse_node_list(n); + node_list_vertical_extent(n, min, max); + n = reverse_node_list(n); +} + +width_list::width_list(hunits w, hunits s) +: width(w), sentence_width(s), next(0) +{ +} + +width_list::width_list(width_list *w) +: width(w->width), sentence_width(w->sentence_width), next(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, color *c, width_list *w, + int flag, node *x) +: space_node(d, s, 0, c, x), orig_width(w), unformat(flag) +{ +} + +word_space_node::~word_space_node() +{ + width_list *w = orig_width; + while (w != 0) { + width_list *tmp = w; + w = w->next; + delete tmp; + } +} + +node *word_space_node::copy() +{ + assert(orig_width != 0); + width_list *w_old_curr = orig_width; + width_list *w_new_curr = new width_list(w_old_curr); + width_list *w_new = w_new_curr; + w_old_curr = w_old_curr->next; + while (w_old_curr != 0) { + w_new_curr->next = new width_list(w_old_curr); + w_new_curr = w_new_curr->next; + w_old_curr = w_old_curr->next; + } + return new word_space_node(n, set, col, w_new, unformat); +} + +int word_space_node::set_unformat_flag() +{ + unformat = 1; + return 1; +} + +void word_space_node::tprint(troff_output_file *out) +{ + out->fill_color(col); + out->word_marker(); + out->right(n); +} + +int word_space_node::merge_space(hunits h, hunits sw, hunits ssw) +{ + n += h; + assert(orig_width != 0); + width_list *w = orig_width; + for (; w->next; w = w->next) + ; + w->next = new width_list(sw, ssw); + return 1; +} + +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, + 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, col); +} + +int unbreakable_space_node::force_tprint() +{ + return 0; +} + +breakpoint *unbreakable_space_node::get_breakpoints(hunits, int, + breakpoint *rest, int) +{ + return rest; +} + +int unbreakable_space_node::nbreaks() +{ + return 0; +} + +void unbreakable_space_node::split(int, node **, node **) +{ + assert(0); +} + +int unbreakable_space_node::merge_space(hunits, hunits, hunits) +{ + return 0; +} + +hvpair::hvpair() +{ +} + +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++) + point[i] = p[i]; +} + +int draw_node::same(node *n) +{ + draw_node *nd = (draw_node *)n; + 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) + return 0; + return 1; +} + +const char *draw_node::type() +{ + return "draw_node"; +} + +int draw_node::force_tprint() +{ + return 0; +} + +draw_node::~draw_node() +{ + if (point) + a_delete point; +} + +hunits draw_node::width() +{ + hunits x = H0; + for (int i = 0; i < npoints; i++) + x += point[i].h; + return x; +} + +vunits draw_node::vertical_width() +{ + if (code == 'e') + return V0; + vunits x = V0; + for (int i = 0; i < npoints; i++) + x += point[i].v; + return x; +} + +node *draw_node::copy() +{ + return new draw_node(code, point, npoints, sz, gcol, fcol); +} + +void draw_node::tprint(troff_output_file *out) +{ + out->draw(code, point, npoints, sz, gcol, fcol); +} + +/* tprint methods */ + +void glyph_node::tprint(troff_output_file *out) +{ + tfont *ptf = tf->get_plain(); + if (ptf == tf) + out->put_char_width(ci, ptf, gcol, fcol, width(), H0); + else { + hunits offset; + int bold = tf->get_bold(&offset); + hunits w = ptf->get_width(ci); + hunits k = H0; + hunits x; + int cs = tf->get_constant_space(&x); + if (cs) { + x -= w; + if (bold) + x -= offset; + hunits x2 = x/2; + out->right(x2); + k = x - x2; + } + else + k = tf->get_track_kern(); + if (bold) { + out->put_char(ci, ptf, gcol, fcol); + out->right(offset); + } + out->put_char_width(ci, ptf, gcol, fcol, w, k); + } +} + +void glyph_node::zero_width_tprint(troff_output_file *out) +{ + tfont *ptf = tf->get_plain(); + hunits offset; + int bold = tf->get_bold(&offset); + hunits x; + int cs = tf->get_constant_space(&x); + if (cs) { + x -= ptf->get_width(ci); + if (bold) + x -= offset; + x = x/2; + out->right(x); + } + out->put_char(ci, ptf, gcol, fcol); + if (bold) { + out->right(offset); + out->put_char(ci, ptf, gcol, fcol); + out->right(-offset); + } + if (cs) + out->right(-x); +} + +void break_char_node::tprint(troff_output_file *t) +{ + ch->tprint(t); +} + +void break_char_node::zero_width_tprint(troff_output_file *t) +{ + ch->zero_width_tprint(t); +} + +void hline_node::tprint(troff_output_file *out) +{ + if (x < H0) { + out->right(x); + x = -x; + } + if (n == 0) { + out->right(x); + return; + } + hunits w = n->width(); + if (w <= H0) { + error("horizontal line drawing character must have positive width"); + out->right(x); + return; + } + int i = int(x/w); + if (i == 0) { + hunits xx = x - w; + hunits xx2 = xx/2; + out->right(xx2); + if (out->is_on()) + n->tprint(out); + out->right(xx - xx2); + } + else { + hunits rem = x - w*i; + if (rem > H0) + if (n->overlaps_horizontally()) { + if (out->is_on()) + n->tprint(out); + out->right(rem - w); + } + else + out->right(rem); + while (--i >= 0) + if (out->is_on()) + n->tprint(out); + } +} + +void vline_node::tprint(troff_output_file *out) +{ + if (n == 0) { + out->down(x); + return; + } + vunits h = n->size(); + int overlaps = n->overlaps_vertically(); + vunits y = x; + if (y < V0) { + y = -y; + int i = y / h; + vunits rem = y - i*h; + if (i == 0) { + out->right(n->width()); + out->down(-rem); + } + else { + while (--i > 0) { + n->zero_width_tprint(out); + out->down(-h); + } + if (overlaps) { + n->zero_width_tprint(out); + out->down(-rem); + if (out->is_on()) + n->tprint(out); + out->down(-h); + } + else { + if (out->is_on()) + n->tprint(out); + out->down(-h - rem); + } + } + } + else { + int i = y / h; + vunits rem = y - i*h; + if (i == 0) { + out->down(rem); + out->right(n->width()); + } + else { + out->down(h); + if (overlaps) + n->zero_width_tprint(out); + out->down(rem); + while (--i > 0) { + n->zero_width_tprint(out); + out->down(h); + } + if (out->is_on()) + n->tprint(out); + } + } +} + +void zero_width_node::tprint(troff_output_file *out) +{ + if (!n) + return; + if (!n->next) { + n->zero_width_tprint(out); + return; + } + int hpos = out->get_hpos(); + int vpos = out->get_vpos(); + node *tem = n; + while (tem) { + tem->tprint(out); + tem = tem->next; + } + out->moveto(hpos, vpos); +} + +void overstrike_node::tprint(troff_output_file *out) +{ + hunits pos = H0; + for (node *tem = list; tem; tem = tem->next) { + hunits x = (max_width - tem->width())/2; + out->right(x - pos); + pos = x; + tem->zero_width_tprint(out); + } + out->right(max_width - pos); +} + +void bracket_node::tprint(troff_output_file *out) +{ + if (list == 0) + return; + int npieces = 0; + node *tem; + for (tem = list; tem; tem = tem->next) + ++npieces; + vunits h = list->size(); + vunits totalh = h*npieces; + vunits y = (totalh - h)/2; + out->down(y); + for (tem = list; tem; tem = tem->next) { + tem->zero_width_tprint(out); + out->down(-h); + } + out->right(max_width); + out->down(totalh - y); +} + +void node::tprint(troff_output_file *) +{ +} + +void node::zero_width_tprint(troff_output_file *out) +{ + int hpos = out->get_hpos(); + int vpos = out->get_vpos(); + tprint(out); + out->moveto(hpos, vpos); +} + +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 space_char_hmotion_node::tprint(troff_output_file *out) +{ + out->fill_color(col); + if (is_html) { + // we emit the space width as a negative glyph index + out->flush_tbuf(); + out->do_motion(); + out->put('N'); + out->put(-n.to_units()); + out->put('\n'); + } + out->right(n); +} + +void vmotion_node::tprint(troff_output_file *out) +{ + out->fill_color(col); + out->down(n); +} + +void kern_pair_node::tprint(troff_output_file *out) +{ + n1->tprint(out); + out->right(amount); + n2->tprint(out); +} + +static void tprint_reverse_node_list(troff_output_file *out, node *n) +{ + if (n == 0) + return; + tprint_reverse_node_list(out, n->next); + n->tprint(out); +} + +void dbreak_node::tprint(troff_output_file *out) +{ + tprint_reverse_node_list(out, none); +} + +void composite_node::tprint(troff_output_file *out) +{ + hunits bold_offset; + int is_bold = tf->get_bold(&bold_offset); + hunits track_kern = tf->get_track_kern(); + hunits constant_space; + int is_constant_spaced = tf->get_constant_space(&constant_space); + hunits x = H0; + if (is_constant_spaced) { + x = constant_space; + for (node *tem = n; tem; tem = tem->next) + x -= tem->width(); + if (is_bold) + x -= bold_offset; + hunits x2 = x/2; + out->right(x2); + x -= x2; + } + if (is_bold) { + int hpos = out->get_hpos(); + int vpos = out->get_vpos(); + tprint_reverse_node_list(out, n); + out->moveto(hpos, vpos); + out->right(bold_offset); + } + tprint_reverse_node_list(out, n); + if (is_constant_spaced) + out->right(x); + else + out->right(track_kern); +} + +node *make_composite_node(charinfo *s, environment *env) +{ + int fontno = env_definite_font(env); + if (fontno < 0) { + error("no current font"); + return 0; + } + assert(fontno < font_table_size && font_table[fontno] != 0); + node *n = charinfo_to_node_list(s, env); + font_size fs = env->get_font_size(); + int char_height = env->get_char_height(); + int char_slant = env->get_char_slant(); + tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, + fontno); + if (env->is_composite()) + tf = tf->get_plain(); + return new composite_node(n, s, tf); +} + +node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0) +{ + int fontno = env_definite_font(env); + if (fontno < 0) { + error("no current font"); + return 0; + } + assert(fontno < font_table_size && font_table[fontno] != 0); + int fn = fontno; + int found = font_table[fontno]->contains(s); + if (!found) { + macro *mac = s->get_macro(); + if (mac && 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", + s->get_number()); + return 0; + } + special_font_list *sf = font_table[fontno]->sf; + while (sf != 0 && !found) { + fn = sf->n; + if (font_table[fn]) + found = font_table[fn]->contains(s); + sf = sf->next; + } + if (!found) { + symbol f = font_table[fontno]->get_name(); + string gl(f.contents()); + gl += ' '; + gl += s->nm.contents(); + gl += '\0'; + charinfo *ci = get_charinfo(symbol(gl.contents())); + if (ci && ci->get_macro()) + return make_composite_node(ci, env); + } + if (!found) { + sf = global_special_fonts; + while (sf != 0 && !found) { + fn = sf->n; + if (font_table[fn]) + found = font_table[fn]->contains(s); + sf = sf->next; + } + } + if (!found) + if (mac && s->is_special()) + return make_composite_node(s, env); + if (!found) { + for (fn = 0; fn < font_table_size; fn++) + if (font_table[fn] + && font_table[fn]->is_special() + && font_table[fn]->contains(s)) { + found = 1; + break; + } + } + if (!found) { + if (!no_error_message && s->first_time_not_found()) { + unsigned char input_code = s->get_ascii_code(); + if (input_code != 0) { + if (csgraph(input_code)) + warning(WARN_CHAR, "can't find character `%1'", input_code); + else + warning(WARN_CHAR, "can't find character with input code %1", + int(input_code)); + } + else if (s->nm.contents()) + warning(WARN_CHAR, "can't find special character `%1'", + s->nm.contents()); + } + return 0; + } + } + font_size fs = env->get_font_size(); + int char_height = env->get_char_height(); + int char_slant = env->get_char_slant(); + tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn); + if (env->is_composite()) + tf = tf->get_plain(); + 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(), + env->get_fill_color()); + case charinfo::TRANSLATE_STRETCHABLE_SPACE: + 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: + error("translation to \\% ignored in this context"); + break; + } + charinfo *tem = ci->get_translation(); + if (tem) + ci = tem; + macro *mac = ci->get_macro(); + if (mac && ci->is_normal()) + return make_composite_node(ci, env); + else + return make_glyph_node(ci, env); +} + +int character_exists(charinfo *ci, environment *env) +{ + if (ci->get_special_translation() != charinfo::TRANSLATE_NONE) + return 1; + charinfo *tem = ci->get_translation(); + if (tem) + ci = tem; + if (ci->get_macro()) + return 1; + node *nd = make_glyph_node(ci, env, 1); + if (nd) { + delete nd; + return 1; + } + return 0; +} + +node *node::add_char(charinfo *ci, environment *env, + hunits *widthp, int *spacep) +{ + node *res; + switch (ci->get_special_translation()) { + case charinfo::TRANSLATE_SPACE: + 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(), + env->get_fill_color(), this); + res->freeze_space(); + *widthp += res->width(); + *spacep += res->nspaces(); + return res; + case charinfo::TRANSLATE_DUMMY: + return new dummy_node(this); + case charinfo::TRANSLATE_HYPHEN_INDICATOR: + return add_discretionary_hyphen(); + } + charinfo *tem = ci->get_translation(); + if (tem) + ci = tem; + macro *mac = ci->get_macro(); + if (mac && ci->is_normal()) { + res = make_composite_node(ci, env); + if (res) { + res->next = this; + *widthp += res->width(); + } + else + return this; + } + else { + node *gn = make_glyph_node(ci, env); + if (gn == 0) + return this; + else { + hunits old_width = width(); + node *p = gn->merge_self(this); + if (p == 0) { + *widthp += gn->width(); + gn->next = this; + res = gn; + } + else { + *widthp += p->width() - old_width; + res = p; + } + } + } + int break_code = 0; + if (ci->can_break_before()) + break_code = 1; + if (ci->can_break_after()) + break_code |= 2; + if (break_code) { + node *next1 = res->next; + res->next = 0; + res = new break_char_node(res, break_code, env->get_fill_color(), next1); + } + return res; +} + +#ifdef __GNUG__ +inline +#endif +int same_node(node *n1, node *n2) +{ + if (n1 != 0) { + if (n2 != 0) + return n1->type() == n2->type() && n1->same(n2); + else + return 0; + } + else + return n2 == 0; +} + +int same_node_list(node *n1, node *n2) +{ + while (n1 && n2) { + if (n1->type() != n2->type() || !n1->same(n2)) + return 0; + n1 = n1->next; + n2 = n2->next; + } + return !n1 && !n2; +} + +int extra_size_node::same(node *nd) +{ + return n == ((extra_size_node *)nd)->n; +} + +const char *extra_size_node::type() +{ + return "extra_size_node"; +} + +int extra_size_node::force_tprint() +{ + return 0; +} + +int vertical_size_node::same(node *nd) +{ + return n == ((vertical_size_node *)nd)->n; +} + +const char *vertical_size_node::type() +{ + return "vertical_size_node"; +} + +int vertical_size_node::set_unformat_flag() +{ + return 0; +} + +int vertical_size_node::force_tprint() +{ + return 0; +} + +int hmotion_node::same(node *nd) +{ + return n == ((hmotion_node *)nd)->n + && col == ((hmotion_node *)nd)->col; +} + +const char *hmotion_node::type() +{ + return "hmotion_node"; +} + +int hmotion_node::set_unformat_flag() +{ + unformat = 1; + return 1; +} + +int hmotion_node::force_tprint() +{ + return 0; +} + +node *hmotion_node::add_self(node *n, hyphen_list **p) +{ + next = n; + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return this; +} + +hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *) +{ + return new hyphen_list(0, tail); +} + +int space_char_hmotion_node::same(node *nd) +{ + return n == ((space_char_hmotion_node *)nd)->n + && col == ((space_char_hmotion_node *)nd)->col; +} + +const char *space_char_hmotion_node::type() +{ + return "space_char_hmotion_node"; +} + +int space_char_hmotion_node::force_tprint() +{ + return 0; +} + +node *space_char_hmotion_node::add_self(node *n, hyphen_list **p) +{ + next = n; + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return this; +} + +hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail, + int *) +{ + return new hyphen_list(0, tail); +} + +int vmotion_node::same(node *nd) +{ + return n == ((vmotion_node *)nd)->n + && col == ((vmotion_node *)nd)->col; +} + +const char *vmotion_node::type() +{ + return "vmotion_node"; +} + +int vmotion_node::force_tprint() +{ + return 0; +} + +int hline_node::same(node *nd) +{ + return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n); +} + +const char *hline_node::type() +{ + return "hline_node"; +} + +int hline_node::force_tprint() +{ + return 0; +} + +int vline_node::same(node *nd) +{ + return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n); +} + +const char *vline_node::type() +{ + return "vline_node"; +} + +int vline_node::force_tprint() +{ + return 0; +} + +int dummy_node::same(node * /*nd*/) +{ + return 1; +} + +const char *dummy_node::type() +{ + return "dummy_node"; +} + +int dummy_node::force_tprint() +{ + return 0; +} + +int transparent_dummy_node::same(node * /*nd*/) +{ + return 1; +} + +const char *transparent_dummy_node::type() +{ + return "transparent_dummy_node"; +} + +int transparent_dummy_node::force_tprint() +{ + return 0; +} + +int transparent_dummy_node::ends_sentence() +{ + return 2; +} + +int zero_width_node::same(node *nd) +{ + return same_node_list(n, ((zero_width_node *)nd)->n); +} + +const char *zero_width_node::type() +{ + return "zero_width_node"; +} + +int zero_width_node::force_tprint() +{ + return 0; +} + +int italic_corrected_node::same(node *nd) +{ + return (x == ((italic_corrected_node *)nd)->x + && same_node(n, ((italic_corrected_node *)nd)->n)); +} + +const char *italic_corrected_node::type() +{ + return "italic_corrected_node"; +} + +int italic_corrected_node::force_tprint() +{ + return 0; +} + +left_italic_corrected_node::left_italic_corrected_node(node *x) +: node(x), n(0) +{ +} + +left_italic_corrected_node::~left_italic_corrected_node() +{ + delete n; +} + +node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn) +{ + if (n == 0) { + hunits lic = gn->left_italic_correction(); + if (!lic.is_zero()) { + x = lic; + n = gn; + return this; + } + } + else { + node *nd = n->merge_glyph_node(gn); + if (nd) { + n = nd; + x = n->left_italic_correction(); + return this; + } + } + return 0; +} + +node *left_italic_corrected_node::copy() +{ + left_italic_corrected_node *nd = new left_italic_corrected_node; + if (n) { + nd->n = n->copy(); + nd->x = x; + } + return nd; +} + +void left_italic_corrected_node::tprint(troff_output_file *out) +{ + if (n) { + out->right(x); + n->tprint(out); + } +} + +const char *left_italic_corrected_node::type() +{ + return "left_italic_corrected_node"; +} + +int left_italic_corrected_node::force_tprint() +{ + return 0; +} + +int left_italic_corrected_node::same(node *nd) +{ + return (x == ((left_italic_corrected_node *)nd)->x + && same_node(n, ((left_italic_corrected_node *)nd)->n)); +} + +void left_italic_corrected_node::ascii_print(ascii_output_file *out) +{ + if (n) + n->ascii_print(out); +} + +hunits left_italic_corrected_node::width() +{ + return n ? n->width() + x : H0; +} + +void left_italic_corrected_node::vertical_extent(vunits *min, vunits *max) +{ + if (n) + n->vertical_extent(min, max); + else + node::vertical_extent(min, max); +} + +hunits left_italic_corrected_node::skew() +{ + return n ? n->skew() + x/2 : H0; +} + +hunits left_italic_corrected_node::subscript_correction() +{ + return n ? n->subscript_correction() : H0; +} + +hunits left_italic_corrected_node::italic_correction() +{ + return n ? n->italic_correction() : H0; +} + +int left_italic_corrected_node::ends_sentence() +{ + return n ? n->ends_sentence() : 0; +} + +int left_italic_corrected_node::overlaps_horizontally() +{ + return n ? n->overlaps_horizontally() : 0; +} + +int left_italic_corrected_node::overlaps_vertically() +{ + return n ? n->overlaps_vertically() : 0; +} + +node *left_italic_corrected_node::last_char_node() +{ + return n ? n->last_char_node() : 0; +} + +tfont *left_italic_corrected_node::get_tfont() +{ + return n ? n->get_tfont() : 0; +} + +hyphenation_type left_italic_corrected_node::get_hyphenation_type() +{ + if (n) + return n->get_hyphenation_type(); + else + return HYPHEN_MIDDLE; +} + +hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail, + int *count) +{ + return n ? n->get_hyphen_list(tail, count) : tail; +} + +node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p) +{ + if (n) { + nd = new left_italic_corrected_node(nd); + nd = n->add_self(nd, p); + n = 0; + delete this; + } + return nd; +} + +int left_italic_corrected_node::character_type() +{ + return n ? n->character_type() : 0; +} + +int overstrike_node::same(node *nd) +{ + return same_node_list(list, ((overstrike_node *)nd)->list); +} + +const char *overstrike_node::type() +{ + return "overstrike_node"; +} + +int overstrike_node::force_tprint() +{ + return 0; +} + +node *overstrike_node::add_self(node *n, hyphen_list **p) +{ + next = n; + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return this; +} + +hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *) +{ + return new hyphen_list(0, tail); +} + +int bracket_node::same(node *nd) +{ + return same_node_list(list, ((bracket_node *)nd)->list); +} + +const char *bracket_node::type() +{ + return "bracket_node"; +} + +int bracket_node::force_tprint() +{ + return 0; +} + +int composite_node::same(node *nd) +{ + return ci == ((composite_node *)nd)->ci + && same_node_list(n, ((composite_node *)nd)->n); +} + +const char *composite_node::type() +{ + return "composite_node"; +} + +int composite_node::force_tprint() +{ + return 0; +} + +int glyph_node::same(node *nd) +{ + 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() +{ + return "glyph_node"; +} + +int glyph_node::force_tprint() +{ + return 0; +} + +int ligature_node::same(node *nd) +{ + return (same_node(n1, ((ligature_node *)nd)->n1) + && same_node(n2, ((ligature_node *)nd)->n2) + && glyph_node::same(nd)); +} + +const char *ligature_node::type() +{ + return "ligature_node"; +} + +int ligature_node::force_tprint() +{ + return 0; +} + +int kern_pair_node::same(node *nd) +{ + return (amount == ((kern_pair_node *)nd)->amount + && same_node(n1, ((kern_pair_node *)nd)->n1) + && same_node(n2, ((kern_pair_node *)nd)->n2)); +} + +const char *kern_pair_node::type() +{ + return "kern_pair_node"; +} + +int kern_pair_node::force_tprint() +{ + return 0; +} + +int dbreak_node::same(node *nd) +{ + return (same_node_list(none, ((dbreak_node *)nd)->none) + && same_node_list(pre, ((dbreak_node *)nd)->pre) + && same_node_list(post, ((dbreak_node *)nd)->post)); +} + +const char *dbreak_node::type() +{ + return "dbreak_node"; +} + +int dbreak_node::force_tprint() +{ + return 0; +} + +int break_char_node::same(node *nd) +{ + 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() +{ + return "break_char_node"; +} + +int break_char_node::force_tprint() +{ + return 0; +} + +int line_start_node::same(node * /*nd*/) +{ + return 1; +} + +const char *line_start_node::type() +{ + return "line_start_node"; +} + +int line_start_node::force_tprint() +{ + return 0; +} + +int space_node::same(node *nd) +{ + return n == ((space_node *)nd)->n + && set == ((space_node *)nd)->set + && col == ((space_node *)nd)->col; +} + +const char *space_node::type() +{ + return "space_node"; +} + +int word_space_node::same(node *nd) +{ + 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() +{ + return "word_space_node"; +} + +int word_space_node::force_tprint() +{ + return 0; +} + +void unbreakable_space_node::tprint(troff_output_file *out) +{ + out->fill_color(col); + if (is_html) { + // we emit the space width as a negative glyph index + out->flush_tbuf(); + out->do_motion(); + out->put('N'); + out->put(-n.to_units()); + out->put('\n'); + } + out->right(n); +} + +int unbreakable_space_node::same(node *nd) +{ + 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() +{ + return "unbreakable_space_node"; +} + +node *unbreakable_space_node::add_self(node *n, hyphen_list **p) +{ + next = n; + hyphen_list *pp = *p; + *p = (*p)->next; + delete pp; + return this; +} + +hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *) +{ + return new hyphen_list(0, tail); +} + +int diverted_space_node::same(node *nd) +{ + return n == ((diverted_space_node *)nd)->n; +} + +const char *diverted_space_node::type() +{ + return "diverted_space_node"; +} + +int diverted_space_node::force_tprint() +{ + return 0; +} + +int diverted_copy_file_node::same(node *nd) +{ + return filename == ((diverted_copy_file_node *)nd)->filename; +} + +const char *diverted_copy_file_node::type() +{ + return "diverted_copy_file_node"; +} + +int diverted_copy_file_node::force_tprint() +{ + return 0; +} + +// Grow the font_table so that its size is > n. + +static void grow_font_table(int n) +{ + assert(n >= font_table_size); + font_info **old_font_table = font_table; + int old_font_table_size = font_table_size; + font_table_size = font_table_size ? (font_table_size*3)/2 : 10; + if (font_table_size <= n) + font_table_size = n + 10; + font_table = new font_info *[font_table_size]; + if (old_font_table_size) + memcpy(font_table, old_font_table, + old_font_table_size*sizeof(font_info *)); + a_delete old_font_table; + for (int i = old_font_table_size; i < font_table_size; i++) + font_table[i] = 0; +} + +dictionary font_translation_dictionary(17); + +static symbol get_font_translation(symbol nm) +{ + void *p = font_translation_dictionary.lookup(nm); + return p ? symbol((char *)p) : nm; +} + +dictionary font_dictionary(50); + +static int mount_font_no_translate(int n, symbol name, symbol external_name) +{ + assert(n >= 0); + // We store the address of this char in font_dictionary to indicate + // that we've previously tried to mount the font and failed. + static char a_char; + font *fm = 0; + void *p = font_dictionary.lookup(external_name); + if (p == 0) { + int not_found; + fm = font::load_font(external_name.contents(), ¬_found); + if (!fm) { + if (not_found) + warning(WARN_FONT, "can't find font `%1'", external_name.contents()); + (void)font_dictionary.lookup(external_name, &a_char); + return 0; + } + (void)font_dictionary.lookup(name, fm); + } + else if (p == &a_char) { +#if 0 + error("invalid font `%1'", external_name.contents()); +#endif + return 0; + } + else + fm = (font*)p; + if (n >= font_table_size) { + if (n - font_table_size > 1000) { + error("font position too much larger than first unused position"); + return 0; + } + grow_font_table(n); + } + else if (font_table[n] != 0) + delete font_table[n]; + font_table[n] = new font_info(name, n, external_name, fm); + font_family::invalidate_fontno(n); + return 1; +} + +int mount_font(int n, symbol name, symbol external_name) +{ + assert(n >= 0); + name = get_font_translation(name); + if (external_name.is_null()) + external_name = name; + else + external_name = get_font_translation(external_name); + return mount_font_no_translate(n, name, external_name); +} + +void mount_style(int n, symbol name) +{ + assert(n >= 0); + if (n >= font_table_size) { + if (n - font_table_size > 1000) { + error("font position too much larger than first unused position"); + return; + } + grow_font_table(n); + } + else if (font_table[n] != 0) + delete font_table[n]; + font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0); + font_family::invalidate_fontno(n); +} + +/* global functions */ + +void font_translate() +{ + symbol from = get_name(1); + if (!from.is_null()) { + symbol to = get_name(); + if (to.is_null() || from == to) + font_translation_dictionary.remove(from); + else + (void)font_translation_dictionary.lookup(from, (void *)to.contents()); + } + skip_line(); +} + +void font_position() +{ + int n; + if (get_integer(&n)) { + if (n < 0) + error("negative font position"); + else { + symbol internal_name = get_name(1); + if (!internal_name.is_null()) { + symbol external_name = get_long_name(); + mount_font(n, internal_name, external_name); // ignore error + } + } + } + skip_line(); +} + +font_family::font_family(symbol s) +: map_size(10), nm(s) +{ + map = new int[map_size]; + for (int i = 0; i < map_size; i++) + map[i] = -1; +} + +font_family::~font_family() +{ + a_delete map; +} + +int font_family::make_definite(int i) +{ + if (i >= 0) { + if (i < map_size && map[i] >= 0) + return map[i]; + else { + if (i < font_table_size && font_table[i] != 0) { + if (i >= map_size) { + int old_map_size = map_size; + int *old_map = map; + map_size *= 3; + map_size /= 2; + if (i >= map_size) + map_size = i + 10; + map = new int[map_size]; + memcpy(map, old_map, old_map_size*sizeof(int)); + a_delete old_map; + for (int j = old_map_size; j < map_size; j++) + map[j] = -1; + } + if (font_table[i]->is_style()) { + symbol sty = font_table[i]->get_name(); + symbol f = concat(nm, sty); + int n; + // don't use symbol_fontno, because that might return a style + // and because we don't want to translate the name + for (n = 0; n < font_table_size; n++) + if (font_table[n] != 0 && font_table[n]->is_named(f) + && !font_table[n]->is_style()) + break; + if (n >= font_table_size) { + n = next_available_font_position(); + if (!mount_font_no_translate(n, f, f)) + return -1; + } + return map[i] = n; + } + else + return map[i] = i; + } + else + return -1; + } + } + else + return -1; +} + +dictionary family_dictionary(5); + +font_family *lookup_family(symbol nm) +{ + font_family *f = (font_family *)family_dictionary.lookup(nm); + if (!f) { + f = new font_family(nm); + (void)family_dictionary.lookup(nm, f); + } + return f; +} + +void font_family::invalidate_fontno(int n) +{ + assert(n >= 0 && n < font_table_size); + dictionary_iterator iter(family_dictionary); + symbol nm; + font_family *fam; + while (iter.get(&nm, (void **)&fam)) { + int map_size = fam->map_size; + if (n < map_size) + fam->map[n] = -1; + for (int i = 0; i < map_size; i++) + if (fam->map[i] == n) + fam->map[i] = -1; + } +} + +void style() +{ + int n; + if (get_integer(&n)) { + if (n < 0) + error("negative font position"); + else { + symbol internal_name = get_name(1); + if (!internal_name.is_null()) + mount_style(n, internal_name); + } + } + skip_line(); +} + +static int get_fontno() +{ + int n; + tok.skip(); + if (tok.delimiter()) { + symbol s = get_name(1); + if (!s.is_null()) { + n = symbol_fontno(s); + if (n < 0) { + n = next_available_font_position(); + if (!mount_font(n, s)) + return -1; + } + return curenv->get_family()->make_definite(n); + } + } + else if (get_integer(&n)) { + if (n < 0 || n >= font_table_size || font_table[n] == 0) + error("bad font number"); + else + return curenv->get_family()->make_definite(n); + } + return -1; +} + +static int underline_fontno = 2; + +void underline_font() +{ + int n = get_fontno(); + if (n >= 0) + underline_fontno = n; + skip_line(); +} + +int get_underline_fontno() +{ + return underline_fontno; +} + +void define_font_special_character() +{ + int n = get_fontno(); + if (n < 0) { + skip_line(); + return; + } + symbol f = font_table[n]->get_name(); + do_define_character(CHAR_FONT_SPECIAL, f.contents()); +} + +void remove_font_special_character() +{ + int n = get_fontno(); + if (n < 0) { + skip_line(); + return; + } + symbol f = font_table[n]->get_name(); + while (!tok.newline() && !tok.eof()) { + if (!tok.space() && !tok.tab()) { + charinfo *s = tok.get_char(1); + string gl(f.contents()); + gl += ' '; + gl += s->nm.contents(); + gl += '\0'; + charinfo *ci = get_charinfo(symbol(gl.contents())); + if (!ci) + break; + macro *m = ci->set_macro(0); + if (m) + delete m; + } + tok.next(); + } + skip_line(); +} + +static void read_special_fonts(special_font_list **sp) +{ + special_font_list *s = *sp; + *sp = 0; + while (s != 0) { + special_font_list *tem = s; + s = s->next; + delete tem; + } + special_font_list **p = sp; + while (has_arg()) { + int i = get_fontno(); + if (i >= 0) { + special_font_list *tem = new special_font_list; + tem->n = i; + tem->next = 0; + *p = tem; + p = &(tem->next); + } + } +} + +void font_special_request() +{ + int n = get_fontno(); + if (n >= 0) + read_special_fonts(&font_table[n]->sf); + skip_line(); +} + +void special_request() +{ + read_special_fonts(&global_special_fonts); + skip_line(); +} + +int next_available_font_position() +{ + int i; + for (i = 1; i < font_table_size && font_table[i] != 0; i++) + ; + return i; +} + +int symbol_fontno(symbol s) +{ + s = get_font_translation(s); + for (int i = 0; i < font_table_size; i++) + if (font_table[i] != 0 && font_table[i]->is_named(s)) + return i; + return -1; +} + +int is_good_fontno(int n) +{ + return n >= 0 && n < font_table_size && font_table[n] != 0; +} + +int get_bold_fontno(int n) +{ + if (n >= 0 && n < font_table_size && font_table[n] != 0) { + hunits offset; + if (font_table[n]->get_bold(&offset)) + return offset.to_units() + 1; + else + return 0; + } + else + return 0; +} + +hunits env_digit_width(environment *env) +{ + node *n = make_glyph_node(charset_table['0'], env); + if (n) { + hunits x = n->width(); + delete n; + return x; + } + else + return H0; +} + +hunits env_space_width(environment *env) +{ + int fn = env_definite_font(env); + font_size fs = env->get_font_size(); + if (fn < 0 || fn >= font_table_size || font_table[fn] == 0) + return scale(fs.to_units()/3, env->get_space_size(), 12); + else + return font_table[fn]->get_space_width(fs, env->get_space_size()); +} + +hunits env_sentence_space_width(environment *env) +{ + int fn = env_definite_font(env); + font_size fs = env->get_font_size(); + if (fn < 0 || fn >= font_table_size || font_table[fn] == 0) + return scale(fs.to_units()/3, env->get_sentence_space_size(), 12); + else + return font_table[fn]->get_space_width(fs, env->get_sentence_space_size()); +} + +hunits env_half_narrow_space_width(environment *env) +{ + int fn = env_definite_font(env); + font_size fs = env->get_font_size(); + if (fn < 0 || fn >= font_table_size || font_table[fn] == 0) + return 0; + else + return font_table[fn]->get_half_narrow_space_width(fs); +} + +hunits env_narrow_space_width(environment *env) +{ + int fn = env_definite_font(env); + font_size fs = env->get_font_size(); + if (fn < 0 || fn >= font_table_size || font_table[fn] == 0) + return 0; + else + return font_table[fn]->get_narrow_space_width(fs); +} + +void bold_font() +{ + int n = get_fontno(); + if (n >= 0) { + if (has_arg()) { + if (tok.delimiter()) { + int f = get_fontno(); + if (f >= 0) { + units offset; + if (has_arg() && get_number(&offset, 'u') && offset >= 1) + font_table[f]->set_conditional_bold(n, hunits(offset - 1)); + else + font_table[f]->conditional_unbold(n); + } + } + else { + units offset; + if (get_number(&offset, 'u') && offset >= 1) + font_table[n]->set_bold(hunits(offset - 1)); + else + font_table[n]->unbold(); + } + } + else + font_table[n]->unbold(); + } + skip_line(); +} + +track_kerning_function::track_kerning_function() : non_zero(0) +{ +} + +track_kerning_function::track_kerning_function(int min_s, hunits min_a, + int max_s, hunits max_a) +: non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s), + max_amount(max_a) +{ +} + +int track_kerning_function::operator==(const track_kerning_function &tk) +{ + if (non_zero) + return (tk.non_zero + && min_size == tk.min_size + && min_amount == tk.min_amount + && max_size == tk.max_size + && max_amount == tk.max_amount); + else + return !tk.non_zero; +} + +int track_kerning_function::operator!=(const track_kerning_function &tk) +{ + if (non_zero) + return (!tk.non_zero + || min_size != tk.min_size + || min_amount != tk.min_amount + || max_size != tk.max_size + || max_amount != tk.max_amount); + else + return tk.non_zero; +} + +hunits track_kerning_function::compute(int size) +{ + if (non_zero) { + if (max_size <= min_size) + return min_amount; + else if (size <= min_size) + return min_amount; + else if (size >= max_size) + return max_amount; + else + return (scale(max_amount, size - min_size, max_size - min_size) + + scale(min_amount, max_size - size, max_size - min_size)); + } + else + return H0; +} + +void track_kern() +{ + int n = get_fontno(); + if (n >= 0) { + int min_s, max_s; + hunits min_a, max_a; + if (has_arg() + && get_number(&min_s, 'z') + && get_hunits(&min_a, 'p') + && get_number(&max_s, 'z') + && get_hunits(&max_a, 'p')) { + track_kerning_function tk(min_s, min_a, max_s, max_a); + font_table[n]->set_track_kern(tk); + } + else { + track_kerning_function tk; + font_table[n]->set_track_kern(tk); + } + } + skip_line(); +} + +void constant_space() +{ + int n = get_fontno(); + if (n >= 0) { + int x, y; + if (!has_arg() || !get_integer(&x)) + font_table[n]->set_constant_space(CONSTANT_SPACE_NONE); + else { + if (!has_arg() || !get_number(&y, 'z')) + font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x); + else + font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE, + scale(y*x, + units_per_inch, + 36*72*sizescale)); + } + } + skip_line(); +} + +void ligature() +{ + int lig; + if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2) + global_ligature_mode = lig; + else + global_ligature_mode = 1; + skip_line(); +} + +void kern_request() +{ + int k; + if (has_arg() && get_integer(&k)) + global_kern_mode = k != 0; + else + global_kern_mode = 1; + skip_line(); +} + +void set_soft_hyphen_char() +{ + soft_hyphen_char = get_optional_char(); + if (!soft_hyphen_char) + soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL); + skip_line(); +} + +void init_output() +{ + if (suppress_output_flag) + the_output = new suppress_output_file; + else if (ascii_output_flag) + the_output = new ascii_output_file; + else + the_output = new troff_output_file; +} + +class next_available_font_position_reg : public reg { +public: + const char *get_string(); +}; + +const char *next_available_font_position_reg::get_string() +{ + return i_to_a(next_available_font_position()); +} + +class printing_reg : public reg { +public: + const char *get_string(); +}; + +const char *printing_reg::get_string() +{ + if (the_output) + return the_output->is_printing() ? "1" : "0"; + else + return "0"; +} + +void init_node_requests() +{ + init_request("bd", bold_font); + init_request("cs", constant_space); + init_request("fp", font_position); + init_request("fschar", define_font_special_character); + init_request("fspecial", font_special_request); + init_request("ftr", font_translate); + init_request("kern", kern_request); + init_request("lg", ligature); + init_request("rfschar", remove_font_special_character); + init_request("shc", set_soft_hyphen_char); + init_request("special", special_request); + init_request("sty", style); + init_request("tkf", track_kern); + init_request("uf", underline_font); + number_reg_dictionary.define(".fp", new next_available_font_position_reg); + number_reg_dictionary.define(".kern", + new constant_int_reg(&global_kern_mode)); + number_reg_dictionary.define(".lg", + new constant_int_reg(&global_ligature_mode)); + number_reg_dictionary.define(".P", new printing_reg); + soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL); +} diff --git a/contrib/groff/src/roff/troff/node.h b/contrib/groff/src/roff/troff/node.h index 13295f4..54f8822 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, 2002 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -75,7 +75,7 @@ struct node { virtual node *merge_self(node *); virtual node *add_discretionary_hyphen(); virtual node *add_self(node *, hyphen_list **); - virtual hyphen_list *get_hyphen_list(hyphen_list *s = 0); + virtual hyphen_list *get_hyphen_list(hyphen_list *, int *); virtual void ascii_print(ascii_output_file *); virtual void asciify(macro *); virtual int discardable(); @@ -218,6 +218,7 @@ public: unbreakable_space_node(hunits, color *, node * = 0); node *copy(); int reread(int *); + void tprint(troff_output_file *); int same(node *); void asciify(macro *); const char *type(); @@ -228,7 +229,7 @@ public: void split(int, node **, node **); int merge_space(hunits, hunits, hunits); node *add_self(node *, hyphen_list **); - hyphen_list *get_hyphen_list(hyphen_list *ss = 0); + hyphen_list *get_hyphen_list(hyphen_list *, int *); hyphenation_type get_hyphenation_type(); }; @@ -301,7 +302,7 @@ public: const char *type(); int force_tprint(); node *add_self(node *, hyphen_list **); - hyphen_list *get_hyphen_list(hyphen_list *ss = 0); + hyphen_list *get_hyphen_list(hyphen_list *, int *); hyphenation_type get_hyphenation_type(); }; @@ -311,11 +312,12 @@ public: node *copy(); void ascii_print(ascii_output_file *); void asciify(macro *); + void tprint(troff_output_file *); int same(node *); const char *type(); int force_tprint(); node *add_self(node *, hyphen_list **); - hyphen_list *get_hyphen_list(hyphen_list *ss = 0); + hyphen_list *get_hyphen_list(hyphen_list *, int *); hyphenation_type get_hyphenation_type(); }; @@ -424,7 +426,7 @@ public: hunits skew(); hunits italic_correction(); hunits subscript_correction(); - hyphen_list *get_hyphen_list(hyphen_list *ss = 0); + hyphen_list *get_hyphen_list(hyphen_list *, int *); node *add_self(node *, hyphen_list **); node *merge_glyph_node(glyph_node *); }; @@ -443,7 +445,7 @@ public: const char *type(); int force_tprint(); node *add_self(node *, hyphen_list **); - hyphen_list *get_hyphen_list(hyphen_list *ss = 0); + hyphen_list *get_hyphen_list(hyphen_list *, int *); hyphenation_type get_hyphenation_type(); }; diff --git a/contrib/groff/src/roff/troff/number.cpp b/contrib/groff/src/roff/troff/number.cpp new file mode 100644 index 0000000..8fed342 --- /dev/null +++ b/contrib/groff/src/roff/troff/number.cpp @@ -0,0 +1,697 @@ +// -*- C++ -*- +/* 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. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" +#include "hvunits.h" +#include "env.h" +#include "token.h" +#include "div.h" + +vunits V0; +hunits H0; + +int hresolution = 1; +int vresolution = 1; +int units_per_inch; +int sizescale; + +static int parse_expr(units *v, int scale_indicator, + int parenthesised, int rigid = 0); +static int start_number(); + +int get_vunits(vunits *res, unsigned char si) +{ + if (!start_number()) + return 0; + units x; + if (parse_expr(&x, si, 0)) { + *res = vunits(x); + return 1; + } + else + return 0; +} + +int get_hunits(hunits *res, unsigned char si) +{ + if (!start_number()) + return 0; + units x; + if (parse_expr(&x, si, 0)) { + *res = hunits(x); + return 1; + } + else + return 0; +} + +// for \B + +int get_number_rigidly(units *res, unsigned char si) +{ + if (!start_number()) + return 0; + units x; + if (parse_expr(&x, si, 0, 1)) { + *res = x; + return 1; + } + else + return 0; +} + +int get_number(units *res, unsigned char si) +{ + if (!start_number()) + return 0; + units x; + if (parse_expr(&x, si, 0)) { + *res = x; + return 1; + } + else + return 0; +} + +int get_integer(int *res) +{ + if (!start_number()) + return 0; + units x; + if (parse_expr(&x, 0, 0)) { + *res = x; + return 1; + } + else + return 0; +} + +enum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT }; + +static incr_number_result get_incr_number(units *res, unsigned char); + +int get_vunits(vunits *res, unsigned char si, vunits prev_value) +{ + units v; + switch (get_incr_number(&v, si)) { + case BAD: + return 0; + case ABSOLUTE: + *res = v; + break; + case INCREMENT: + *res = prev_value + v; + break; + case DECREMENT: + *res = prev_value - v; + break; + default: + assert(0); + } + return 1; +} + +int get_hunits(hunits *res, unsigned char si, hunits prev_value) +{ + units v; + switch (get_incr_number(&v, si)) { + case BAD: + return 0; + case ABSOLUTE: + *res = v; + break; + case INCREMENT: + *res = prev_value + v; + break; + case DECREMENT: + *res = prev_value - v; + break; + default: + assert(0); + } + return 1; +} + +int get_number(units *res, unsigned char si, units prev_value) +{ + units v; + switch (get_incr_number(&v, si)) { + case BAD: + return 0; + case ABSOLUTE: + *res = v; + break; + case INCREMENT: + *res = prev_value + v; + break; + case DECREMENT: + *res = prev_value - v; + break; + default: + assert(0); + } + return 1; +} + +int get_integer(int *res, int prev_value) +{ + units v; + switch (get_incr_number(&v, 0)) { + case BAD: + return 0; + case ABSOLUTE: + *res = v; + break; + case INCREMENT: + *res = prev_value + int(v); + break; + case DECREMENT: + *res = prev_value - int(v); + break; + default: + assert(0); + } + return 1; +} + + +static incr_number_result get_incr_number(units *res, unsigned char si) +{ + if (!start_number()) + return BAD; + incr_number_result result = ABSOLUTE; + if (tok.ch() == '+') { + tok.next(); + result = INCREMENT; + } + else if (tok.ch() == '-') { + tok.next(); + result = DECREMENT; + } + if (parse_expr(res, si, 0)) + return result; + else + return BAD; +} + +static int start_number() +{ + while (tok.space()) + tok.next(); + if (tok.newline()) { + warning(WARN_MISSING, "missing number"); + return 0; + } + if (tok.tab()) { + warning(WARN_TAB, "tab character where number expected"); + return 0; + } + if (tok.right_brace()) { + warning(WARN_RIGHT_BRACE, "`\\}' where number expected"); + return 0; + } + return 1; +} + +enum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' }; + +#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz" + +static int parse_term(units *v, int scale_indicator, + int parenthesised, int rigid); + +static int parse_expr(units *v, int scale_indicator, + int parenthesised, int rigid) +{ + int result = parse_term(v, scale_indicator, parenthesised, rigid); + while (result) { + if (parenthesised) + tok.skip(); + int op = tok.ch(); + switch (op) { + case '+': + case '-': + case '/': + case '*': + case '%': + case ':': + case '&': + tok.next(); + break; + case '>': + tok.next(); + if (tok.ch() == '=') { + tok.next(); + op = OP_GEQ; + } + else if (tok.ch() == '?') { + tok.next(); + op = OP_MAX; + } + break; + case '<': + tok.next(); + if (tok.ch() == '=') { + tok.next(); + op = OP_LEQ; + } + else if (tok.ch() == '?') { + tok.next(); + op = OP_MIN; + } + break; + case '=': + tok.next(); + if (tok.ch() == '=') + tok.next(); + break; + default: + return result; + } + units v2; + if (!parse_term(&v2, scale_indicator, parenthesised, rigid)) + return 0; + int overflow = 0; + switch (op) { + case '<': + *v = *v < v2; + break; + case '>': + *v = *v > v2; + break; + case OP_LEQ: + *v = *v <= v2; + break; + case OP_GEQ: + *v = *v >= v2; + break; + case OP_MIN: + if (*v > v2) + *v = v2; + break; + case OP_MAX: + if (*v < v2) + *v = v2; + break; + case '=': + *v = *v == v2; + break; + case '&': + *v = *v > 0 && v2 > 0; + break; + case ':': + *v = *v > 0 || v2 > 0; + break; + case '+': + if (v2 < 0) { + if (*v < INT_MIN - v2) + overflow = 1; + } + else if (v2 > 0) { + if (*v > INT_MAX - v2) + overflow = 1; + } + if (overflow) { + error("addition overflow"); + return 0; + } + *v += v2; + break; + case '-': + if (v2 < 0) { + if (*v > INT_MAX + v2) + overflow = 1; + } + else if (v2 > 0) { + if (*v < INT_MIN + v2) + overflow = 1; + } + if (overflow) { + error("subtraction overflow"); + return 0; + } + *v -= v2; + break; + case '*': + if (v2 < 0) { + if (*v > 0) { + if (*v > -(unsigned)INT_MIN / -(unsigned)v2) + overflow = 1; + } + else if (-(unsigned)*v > INT_MAX / -(unsigned)v2) + overflow = 1; + } + else if (v2 > 0) { + if (*v > 0) { + if (*v > INT_MAX / v2) + overflow = 1; + } + else if (-(unsigned)*v > -(unsigned)INT_MIN / v2) + overflow = 1; + } + if (overflow) { + error("multiplication overflow"); + return 0; + } + *v *= v2; + break; + case '/': + if (v2 == 0) { + error("division by zero"); + return 0; + } + *v /= v2; + break; + case '%': + if (v2 == 0) { + error("modulus by zero"); + return 0; + } + *v %= v2; + break; + default: + assert(0); + } + } + return result; +} + +static int parse_term(units *v, int scale_indicator, + int parenthesised, int rigid) +{ + int negative = 0; + for (;;) + if (parenthesised && tok.space()) + tok.next(); + else if (tok.ch() == '+') + tok.next(); + else if (tok.ch() == '-') { + tok.next(); + negative = !negative; + } + else + break; + unsigned char c = tok.ch(); + switch (c) { + case '|': + // | is not restricted to the outermost level + // tbl uses this + tok.next(); + if (!parse_term(v, scale_indicator, parenthesised, rigid)) + return 0; + int tem; + tem = (scale_indicator == 'v' + ? curdiv->get_vertical_position().to_units() + : curenv->get_input_line_position().to_units()); + if (tem >= 0) { + if (*v < INT_MIN + tem) { + error("numeric overflow"); + return 0; + } + } + else { + if (*v > INT_MAX + tem) { + error("numeric overflow"); + return 0; + } + } + *v -= tem; + if (negative) { + if (*v == INT_MIN) { + error("numeric overflow"); + return 0; + } + *v = -*v; + } + return 1; + case '(': + tok.next(); + c = tok.ch(); + if (c == ')') { + if (rigid) + return 0; + warning(WARN_SYNTAX, "empty parentheses"); + tok.next(); + *v = 0; + return 1; + } + else if (c != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) { + tok.next(); + if (tok.ch() == ';') { + tok.next(); + scale_indicator = c; + } + else { + error("expected `;' after scale-indicator (got %1)", + tok.description()); + return 0; + } + } + else if (c == ';') { + scale_indicator = 0; + tok.next(); + } + if (!parse_expr(v, scale_indicator, 1, rigid)) + return 0; + tok.skip(); + if (tok.ch() != ')') { + if (rigid) + return 0; + warning(WARN_SYNTAX, "missing `)' (got %1)", tok.description()); + } + else + tok.next(); + if (negative) { + if (*v == INT_MIN) { + error("numeric overflow"); + return 0; + } + *v = -*v; + } + return 1; + case '.': + *v = 0; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *v = 0; + do { + if (*v > INT_MAX/10) { + error("numeric overflow"); + return 0; + } + *v *= 10; + if (*v > INT_MAX - (int(c) - '0')) { + error("numeric overflow"); + return 0; + } + *v += c - '0'; + tok.next(); + c = tok.ch(); + } while (csdigit(c)); + break; + case '/': + case '*': + case '%': + case ':': + case '&': + case '>': + case '<': + case '=': + warning(WARN_SYNTAX, "empty left operand"); + *v = 0; + return rigid ? 0 : 1; + default: + warning(WARN_NUMBER, "numeric expression expected (got %1)", + tok.description()); + return 0; + } + int divisor = 1; + if (tok.ch() == '.') { + tok.next(); + for (;;) { + c = tok.ch(); + if (!csdigit(c)) + break; + // we may multiply the divisor by 254 later on + if (divisor <= INT_MAX/2540 && *v <= (INT_MAX - 9)/10) { + *v *= 10; + *v += c - '0'; + divisor *= 10; + } + tok.next(); + } + } + int si = scale_indicator; + int do_next = 0; + if ((c = tok.ch()) != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) { + switch (scale_indicator) { + case 'z': + if (c != 'u' && c != 'z') { + warning(WARN_SCALE, + "only `z' and `u' scale indicators valid in this context"); + break; + } + si = c; + break; + case 0: + warning(WARN_SCALE, "scale indicator invalid in this context"); + break; + case 'u': + si = c; + break; + default: + if (c == 'z') { + warning(WARN_SCALE, "`z' scale indicator invalid in this context"); + break; + } + si = c; + break; + } + // Don't do tok.next() here because the next token might be \s, which + // would affect the interpretation of m. + do_next = 1; + } + switch (si) { + case 'i': + *v = scale(*v, units_per_inch, divisor); + break; + case 'c': + *v = scale(*v, units_per_inch*100, divisor*254); + break; + case 0: + case 'u': + 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; + case 'P': + *v = scale(*v, units_per_inch, divisor*6); + break; + case 'm': + { + // Convert to hunits so that with -Tascii `m' behaves as in nroff. + hunits em = curenv->get_size(); + *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor); + } + break; + case 'M': + { + hunits em = curenv->get_size(); + *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100); + } + break; + case 'n': + { + // Convert to hunits so that with -Tascii `n' behaves as in nroff. + hunits en = curenv->get_size()/2; + *v = scale(*v, en.is_zero() ? hresolution : en.to_units(), divisor); + } + break; + case 'v': + *v = scale(*v, curenv->get_vertical_spacing().to_units(), divisor); + break; + case 's': + while (divisor > INT_MAX/(sizescale*72)) { + divisor /= 10; + *v /= 10; + } + *v = scale(*v, units_per_inch, divisor*sizescale*72); + break; + case 'z': + *v = scale(*v, sizescale, divisor); + break; + default: + assert(0); + } + if (do_next) + tok.next(); + if (negative) { + if (*v == INT_MIN) { + error("numeric overflow"); + return 0; + } + *v = -*v; + } + return 1; +} + +units scale(units n, units x, units y) +{ + assert(x >= 0 && y > 0); + if (x == 0) + return 0; + if (n >= 0) { + if (n <= INT_MAX/x) + return (n*x)/y; + } + else { + if (-(unsigned)n <= -(unsigned)INT_MIN/x) + return (n*x)/y; + } + double res = n*double(x)/double(y); + if (res > INT_MAX) { + error("numeric overflow"); + return INT_MAX; + } + else if (res < INT_MIN) { + error("numeric overflow"); + return INT_MIN; + } + return int(res); +} + +vunits::vunits(units x) +{ + // don't depend on the rounding direction for division of negative integers + if (vresolution == 1) + n = x; + else + n = (x < 0 + ? -((-x + vresolution/2 - 1)/vresolution) + : (x + vresolution/2 - 1)/vresolution); +} + +hunits::hunits(units x) +{ + // don't depend on the rounding direction for division of negative integers + if (hresolution == 1) + n = x; + else + n = (x < 0 + ? -((-x + hresolution/2 - 1)/hresolution) + : (x + hresolution/2 - 1)/hresolution); +} diff --git a/contrib/groff/src/roff/troff/reg.cpp b/contrib/groff/src/roff/troff/reg.cpp new file mode 100644 index 0000000..8ac20c9 --- /dev/null +++ b/contrib/groff/src/roff/troff/reg.cpp @@ -0,0 +1,474 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" +#include "dictionary.h" +#include "token.h" +#include "request.h" +#include "reg.h" + +object_dictionary number_reg_dictionary(101); + +int reg::get_value(units * /*d*/) +{ + return 0; +} + +void reg::increment() +{ + error("can't increment read-only register"); +} + +void reg::decrement() +{ + error("can't decrement read-only register"); +} + +void reg::set_increment(units /*n*/) +{ + error("can't auto increment read-only register"); +} + +void reg::alter_format(char /*f*/, int /*w*/) +{ + error("can't alter format of read-only register"); +} + +const char *reg::get_format() +{ + return "0"; +} + +void reg::set_value(units /*n*/) +{ + error("can't write read-only register"); +} + +general_reg::general_reg() : format('1'), width(0), inc(0) +{ +} + +static char uppercase_array[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', +}; + +static char lowercase_array[] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', +}; + +static const char *number_value_to_ascii(int value, char format, int width) +{ + static char buf[128]; // must be at least 21 + switch(format) { + case '1': + if (width <= 0) + return i_to_a(value); + else if (width > int(sizeof(buf) - 2)) + sprintf(buf, "%.*d", int(sizeof(buf) - 2), int(value)); + else + sprintf(buf, "%.*d", width, int(value)); + break; + case 'i': + case 'I': + { + char *p = buf; + // troff uses z and w to represent 10000 and 5000 in Roman + // numerals; I can find no historical basis for this usage + const char *s = format == 'i' ? "zwmdclxvi" : "ZWMDCLXVI"; + int n = int(value); + if (n >= 40000 || n <= -40000) { + error("magnitude of `%1' too big for i or I format", n); + return i_to_a(n); + } + if (n == 0) { + *p++ = '0'; + *p = 0; + break; + } + if (n < 0) { + *p++ = '-'; + n = -n; + } + while (n >= 10000) { + *p++ = s[0]; + n -= 10000; + } + for (int i = 1000; i > 0; i /= 10, s += 2) { + int m = n/i; + n -= m*i; + switch (m) { + case 3: + *p++ = s[2]; + /* falls through */ + case 2: + *p++ = s[2]; + /* falls through */ + case 1: + *p++ = s[2]; + break; + case 4: + *p++ = s[2]; + *p++ = s[1]; + break; + case 8: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 7: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 6: + *p++ = s[1]; + *p++ = s[2]; + break; + case 5: + *p++ = s[1]; + break; + case 9: + *p++ = s[2]; + *p++ = s[0]; + } + } + *p = 0; + break; + } + case 'a': + case 'A': + { + int n = value; + char *p = buf; + if (n == 0) { + *p++ = '0'; + *p = 0; + } + else { + if (n < 0) { + n = -n; + *p++ = '-'; + } + // this is a bit tricky + while (n > 0) { + int d = n % 26; + if (d == 0) + d = 26; + n -= d; + n /= 26; + *p++ = format == 'a' ? lowercase_array[d - 1] : + uppercase_array[d - 1]; + } + *p-- = 0; + char *q = buf[0] == '-' ? buf + 1 : buf; + while (q < p) { + char temp = *q; + *q = *p; + *p = temp; + --p; + ++q; + } + } + break; + } + default: + assert(0); + break; + } + return buf; +} + +const char *general_reg::get_string() +{ + units n; + if (!get_value(&n)) + return ""; + return number_value_to_ascii(n, format, width); +} + + +void general_reg::increment() +{ + int n; + if (get_value(&n)) + set_value(n + inc); +} + +void general_reg::decrement() +{ + int n; + if (get_value(&n)) + set_value(n - inc); +} + +void general_reg::set_increment(units n) +{ + inc = n; +} + +void general_reg::alter_format(char f, int w) +{ + format = f; + width = w; +} + +static const char *number_format_to_ascii(char format, int width) +{ + static char buf[24]; + if (format == '1') { + if (width > 0) { + int n = width; + if (n > int(sizeof(buf)) - 1) + n = int(sizeof(buf)) - 1; + sprintf(buf, "%.*d", n, 0); + return buf; + } + else + return "0"; + } + else { + buf[0] = format; + buf[1] = '\0'; + return buf; + } +} + +const char *general_reg::get_format() +{ + return number_format_to_ascii(format, width); +} + +class number_reg : public general_reg { + units value; +public: + number_reg(); + int get_value(units *); + void set_value(units); +}; + +number_reg::number_reg() : value(0) +{ +} + +int number_reg::get_value(units *res) +{ + *res = value; + return 1; +} + +void number_reg::set_value(units n) +{ + value = n; +} + +variable_reg::variable_reg(units *p) : ptr(p) +{ +} + +void variable_reg::set_value(units n) +{ + *ptr = n; +} + +int variable_reg::get_value(units *res) +{ + *res = *ptr; + return 1; +} + +void define_number_reg() +{ + symbol nm = get_name(1); + if (nm.is_null()) { + skip_line(); + return; + } + reg *r = (reg *)number_reg_dictionary.lookup(nm); + units v; + units prev_value; + if (!r || !r->get_value(&prev_value)) + prev_value = 0; + if (get_number(&v, 'u', prev_value)) { + if (r == 0) { + r = new number_reg; + number_reg_dictionary.define(nm, r); + } + r->set_value(v); + if (tok.space() && has_arg() && get_number(&v, 'u')) + r->set_increment(v); + } + skip_line(); +} + +#if 0 +void inline_define_reg() +{ + token start; + start.next(); + if (!start.delimiter(1)) + return; + tok.next(); + symbol nm = get_name(1); + if (nm.is_null()) + return; + reg *r = (reg *)number_reg_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + number_reg_dictionary.define(nm, r); + } + units v; + units prev_value; + if (!r->get_value(&prev_value)) + prev_value = 0; + if (get_number(&v, 'u', prev_value)) { + r->set_value(v); + if (start != tok) { + if (get_number(&v, 'u')) { + r->set_increment(v); + if (start != tok) + warning(WARN_DELIM, "closing delimiter does not match"); + } + } + } +} +#endif + +void set_number_reg(symbol nm, units n) +{ + reg *r = (reg *)number_reg_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + number_reg_dictionary.define(nm, r); + } + r->set_value(n); +} + +reg *lookup_number_reg(symbol nm) +{ + reg *r = (reg *)number_reg_dictionary.lookup(nm); + if (r == 0) { + warning(WARN_REG, "number register `%1' not defined", nm.contents()); + r = new number_reg; + number_reg_dictionary.define(nm, r); + } + return r; +} + +void alter_format() +{ + symbol nm = get_name(1); + if (nm.is_null()) { + skip_line(); + return; + } + reg *r = (reg *)number_reg_dictionary.lookup(nm); + if (r == 0) { + r = new number_reg; + number_reg_dictionary.define(nm, r); + } + tok.skip(); + char c = tok.ch(); + if (csdigit(c)) { + int n = 0; + do { + ++n; + tok.next(); + } while (csdigit(tok.ch())); + r->alter_format('1', n); + } + else if (c == 'i' || c == 'I' || c == 'a' || c == 'A') + r->alter_format(c); + else if (tok.newline() || tok.eof()) + warning(WARN_MISSING, "missing number register format"); + else + error("bad number register format (got %1)", tok.description()); + skip_line(); +} + +void remove_reg() +{ + for (;;) { + symbol s = get_name(); + if (s.is_null()) + break; + number_reg_dictionary.remove(s); + } + skip_line(); +} + +void alias_reg() +{ + symbol s1 = get_name(1); + if (!s1.is_null()) { + symbol s2 = get_name(1); + if (!s2.is_null()) { + if (!number_reg_dictionary.alias(s1, s2)) + warning(WARN_REG, "number register `%1' not defined", s2.contents()); + } + } + skip_line(); +} + +void rename_reg() +{ + symbol s1 = get_name(1); + if (!s1.is_null()) { + symbol s2 = get_name(1); + if (!s2.is_null()) + number_reg_dictionary.rename(s1, s2); + } + skip_line(); +} + +void print_number_regs() +{ + object_dictionary_iterator iter(number_reg_dictionary); + reg *r; + symbol s; + while (iter.get(&s, (object **)&r)) { + assert(!s.is_null()); + errprint("%1\t", s.contents()); + const char *p = r->get_string(); + if (p) + errprint(p); + errprint("\n"); + } + fflush(stderr); + skip_line(); +} + +void init_reg_requests() +{ + init_request("rr", remove_reg); + init_request("nr", define_number_reg); + init_request("af", alter_format); + init_request("aln", alias_reg); + init_request("rnn", rename_reg); + init_request("pnr", print_number_regs); +} diff --git a/contrib/groff/src/roff/troff/reg.h b/contrib/groff/src/roff/troff/reg.h index 8d403d4..950be4f 100644 --- a/contrib/groff/src/roff/troff/reg.h +++ b/contrib/groff/src/roff/troff/reg.h @@ -1,5 +1,5 @@ // -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2003 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -68,7 +68,7 @@ public: extern object_dictionary number_reg_dictionary; extern void set_number_reg(symbol nm, units n); extern void check_output_limits(int x, int y); -extern void reset_output_registers (int miny); +extern void reset_output_registers(); reg *lookup_number_reg(symbol); #if 0 diff --git a/contrib/groff/src/roff/troff/symbol.cpp b/contrib/groff/src/roff/troff/symbol.cpp new file mode 100644 index 0000000..09f4c98 --- /dev/null +++ b/contrib/groff/src/roff/troff/symbol.cpp @@ -0,0 +1,154 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2002 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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" +#include "symbol.h" + +const char **symbol::table = 0; +int symbol::table_used = 0; +int symbol::table_size = 0; +char *symbol::block = 0; +int symbol::block_size = 0; + +const symbol NULL_SYMBOL; +const symbol EMPTY_SYMBOL(""); + +#ifdef BLOCK_SIZE +#undef BLOCK_SIZE +#endif + +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 +static const unsigned int table_sizes[] = { + 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 + +static unsigned int hash_string(const char *p) +{ + // compute a hash code; this assumes 32-bit unsigned ints + // see p436 of Compilers by Aho, Sethi & Ullman + // give special treatment to two-character names + unsigned int hc = 0, g; + if (*p != 0) { + hc = *p++; + if (*p != 0) { + hc <<= 7; + hc += *p++; + for (; *p != 0; p++) { + hc <<= 4; + hc += *p; + if ((g = (hc & 0xf0000000)) == 0) { + hc ^= g >> 24; + hc ^= g; + } + } + } + } + return hc; +} + +// Tell compiler that a variable is intentionally unused. +inline void unused(void *) { } + +symbol::symbol(const char *p, int how) +{ + 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]; + for (int i = 0; i < table_size; i++) + table[i] = 0; + table_used = 0; + } + unsigned int hc = hash_string(p); + const char **pp; + for (pp = table + hc % table_size; + *pp != 0; + (pp == table ? pp = table + table_size - 1 : --pp)) + if (strcmp(p, *pp) == 0) { + s = *pp; + return; + } + if (how == MUST_ALREADY_EXIST) { + s = 0; + return; + } + if (table_used >= table_size - 1 || table_used >= table_size*FULL_MAX) { + const char **old_table = table; + unsigned int old_table_size = table_size; + int i; + for (i = 1; table_sizes[i] <= old_table_size; i++) + if (table_sizes[i] == 0) + fatal("too many symbols"); + table_size = table_sizes[i]; + table_used = 0; + table = (const char **)new char*[table_size]; + for (i = 0; i < table_size; i++) + table[i] = 0; + for (pp = old_table + old_table_size - 1; + pp >= old_table; + --pp) { + symbol temp(*pp, 1); /* insert it into the new table */ + unused(&temp); + } + a_delete old_table; + for (pp = table + hc % table_size; + *pp != 0; + (pp == table ? pp = table + table_size - 1 : --pp)) + ; + } + ++table_used; + if (how == DONT_STORE) { + s = *pp = p; + } + else { + int len = strlen(p)+1; + if (block == 0 || block_size < len) { + block_size = len > BLOCK_SIZE ? len : BLOCK_SIZE; + block = new char [block_size]; + } + (void)strcpy(block, p); + s = *pp = block; + block += len; + block_size -= len; + } +} + +symbol concat(symbol s1, symbol s2) +{ + char *buf = new char [strlen(s1.contents()) + strlen(s2.contents()) + 1]; + strcpy(buf, s1.contents()); + strcat(buf, s2.contents()); + symbol res(buf); + a_delete buf; + return res; +} diff --git a/contrib/groff/src/roff/troff/token.h b/contrib/groff/src/roff/troff/token.h index 59f2aa2..9f5b069 100644 --- a/contrib/groff/src/roff/troff/token.h +++ b/contrib/groff/src/roff/troff/token.h @@ -112,6 +112,15 @@ extern void check_missing_character(); extern void skip_line(); extern void handle_initial_title(); +enum char_mode { + CHAR_NORMAL, + CHAR_FALLBACK, + CHAR_FONT_SPECIAL, + CHAR_SPECIAL +}; + +extern void do_define_character(char_mode, const char * = 0); + struct hunits; extern void read_title_parts(node **part, hunits *part_width); diff --git a/contrib/groff/src/roff/troff/troff.man b/contrib/groff/src/roff/troff/troff.man index 19bb624..dce81dd 100644 --- a/contrib/groff/src/roff/troff/troff.man +++ b/contrib/groff/src/roff/troff/troff.man @@ -2,7 +2,7 @@ .ig troff.man -Last update : 9 Jan 2002 +Last update : 09 Dec 2002 This file is part of groff, the GNU roff type-setting system. @@ -272,7 +272,10 @@ and Prepare output for device .IR name , rather than the default -.BR @DEVICE@ . +.BR @DEVICE@ ; +see +.BR groff (@MAN1EXT@) +for a more detailed description. . .TP .B \-U diff --git a/contrib/groff/src/roff/troff/unicode.cpp b/contrib/groff/src/roff/troff/unicode.cpp new file mode 100644 index 0000000..2117463 --- /dev/null +++ b/contrib/groff/src/roff/troff/unicode.cpp @@ -0,0 +1,67 @@ +// -*- C++ -*- +/* Copyright (C) 2002 + Free Software Foundation, Inc. + Written by Werner Lemberg + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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 "lib.h" +#include "cset.h" +#include "stringclass.h" + +#include "unicode.h" + +const char *check_unicode_name(const char *u) +{ + if (*u != 'u') + return 0; + const char *p = ++u; + for (;;) { + int val = 0; + const char *start = p; + for (;;) { + // only uppercase hex digits allowed + if (!csxdigit(*p)) + return 0; + if (csdigit(*p)) + val = val*0x10 + (*p-'0'); + else if (csupper(*p)) + val = val*0x10 + (*p-'A'+10); + else + return 0; + // biggest Unicode value is U+10FFFF + if (val > 0x10FFFF) + return 0; + p++; + if (*p == '\0' || *p == '_') + break; + } + // surrogates not allowed + if ((val >= 0xD800 && val <= 0xDBFF) || (val >= 0xDC00 && val <= 0xDFFF)) + return 0; + if (val > 0xFFFF) { + if (*start == '0') // no leading zeros allowed if > 0xFFFF + return 0; + } + else if (p - start != 4) // otherwise, check for exactly 4 hex digits + return 0; + if (*p == '\0') + break; + p++; + } + return u; +} diff --git a/contrib/groff/src/roff/troff/unicode.h b/contrib/groff/src/roff/troff/unicode.h new file mode 100644 index 0000000..846a70e --- /dev/null +++ b/contrib/groff/src/roff/troff/unicode.h @@ -0,0 +1,26 @@ +// -*- C++ -*- +/* Copyright (C) 2002 + Free Software Foundation, Inc. + Written by Werner Lemberg + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + +extern const char *glyph_name_to_unicode(const char *); +extern const char *unicode_to_glyph_name(const char *); +extern const char *decompose_unicode(const char *); + +extern const char *check_unicode_name(const char *); diff --git a/contrib/groff/src/roff/troff/uniglyph.cpp b/contrib/groff/src/roff/troff/uniglyph.cpp new file mode 100644 index 0000000..3428605 --- /dev/null +++ b/contrib/groff/src/roff/troff/uniglyph.cpp @@ -0,0 +1,503 @@ +// -*- C++ -*- +/* Copyright (C) 2002, 2003 + Free Software Foundation, Inc. + Written by Werner Lemberg + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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 "lib.h" +#include "stringclass.h" +#include "ptable.h" + +#include "unicode.h" + +struct unicode_to_glyph { + char *value; +}; + +declare_ptable(unicode_to_glyph) +implement_ptable(unicode_to_glyph) + +PTABLE(unicode_to_glyph) unicode_to_glyph_table; + +struct { + const char *key; + const char *value; +} unicode_to_glyph_list[] = { + { "0021", "!" }, +//{ "0022", "\"" }, + { "0022", "dq" }, +//{ "0023", "#" }, + { "0023", "sh" }, +//{ "0024", "$" }, + { "0024", "Do" }, + { "0025", "%" }, + { "0026", "&" }, + { "0027", "aq" }, + { "0028", "(" }, + { "0029", ")" }, + { "002A", "*" }, +//{ "002B", "+" }, + { "002B", "pl" }, + { "002C", "," }, + { "002E", "." }, +//{ "002F", "/" }, + { "002F", "sl" }, + { "0030", "0" }, + { "0031", "1" }, + { "0032", "2" }, + { "0033", "3" }, + { "0034", "4" }, + { "0035", "5" }, + { "0036", "6" }, + { "0037", "7" }, + { "0038", "8" }, + { "0039", "9" }, + { "003A", ":" }, + { "003B", ";" }, + { "003C", "<" }, +//{ "003D", "=" }, + { "003D", "eq" }, + { "003D_0338", "!=" }, + { "003E", ">" }, + { "003F", "?" }, +//{ "0040", "@" }, + { "0040", "at" }, + { "0041", "A" }, + { "0041_0300", "`A" }, + { "0041_0301", "'A" }, + { "0041_0302", "^A" }, + { "0041_0303", "~A" }, + { "0041_0308", ":A" }, + { "0041_030A", "oA" }, + { "0042", "B" }, + { "0043", "C" }, + { "0043_0301", "'C" }, + { "0043_0327", ",C" }, + { "0044", "D" }, + { "0045", "E" }, + { "0045_0300", "`E" }, + { "0045_0301", "'E" }, + { "0045_0302", "^E" }, + { "0045_0308", ":E" }, + { "0046", "F" }, + { "0047", "G" }, + { "0048", "H" }, + { "0049", "I" }, + { "0049_0300", "`I" }, + { "0049_0301", "'I" }, + { "0049_0302", "^I" }, + { "0049_0308", ":I" }, + { "004A", "J" }, + { "004B", "K" }, + { "004C", "L" }, + { "004D", "M" }, + { "004E", "N" }, + { "004E_0303", "~N" }, + { "004F", "O" }, + { "004F_0300", "`O" }, + { "004F_0301", "'O" }, + { "004F_0302", "^O" }, + { "004F_0303", "~O" }, + { "004F_0308", ":O" }, + { "0050", "P" }, + { "0051", "Q" }, + { "0052", "R" }, + { "0053", "S" }, + { "0053_030C", "vS" }, + { "0054", "T" }, + { "0055", "U" }, + { "0055_0300", "`U" }, + { "0055_0301", "'U" }, + { "0055_0302", "^U" }, + { "0055_0308", ":U" }, + { "0056", "V" }, + { "0057", "W" }, + { "0058", "X" }, + { "0059", "Y" }, + { "0059_0301", "'Y" }, + { "0059_0308", ":Y" }, + { "005A", "Z" }, + { "005A_030C", "vZ" }, + { "005B", "lB" }, +//{ "005B", "[" }, + { "005C", "rs" }, +//{ "005C", "\\" }, + { "005D", "rB" }, +//{ "005D", "]" }, +//{ "005E", "^" }, +//{ "005E", "a^" }, + { "005E", "ha" }, +//{ "005F", "_" }, +//{ "005F", "ru" }, + { "005F", "ul" }, + { "0060", "ga" }, +//{ "0060", "\\`" }, + { "0061", "a" }, + { "0061_0300", "`a" }, + { "0061_0301", "'a" }, + { "0061_0302", "^a" }, + { "0061_0303", "~a" }, + { "0061_0308", ":a" }, + { "0061_030A", "oa" }, + { "0062", "b" }, + { "0063", "c" }, + { "0063_0301", "'c" }, + { "0063_0327", ",c" }, + { "0064", "d" }, + { "0065", "e" }, + { "0065_0300", "`e" }, + { "0065_0301", "'e" }, + { "0065_0302", "^e" }, + { "0065_0308", ":e" }, + { "0066", "f" }, + { "0066_0066", "ff" }, + { "0066_0066_0069", "Fi" }, + { "0066_0066_006C", "Fl" }, + { "0066_0069", "fi" }, + { "0066_006C", "fl" }, + { "0067", "g" }, + { "0068", "h" }, + { "0069", "i" }, + { "0069_0300", "`i" }, + { "0069_0301", "'i" }, + { "0069_0302", "^i" }, + { "0069_0308", ":i" }, + { "006A", "j" }, + { "006B", "k" }, + { "006C", "l" }, + { "006D", "m" }, + { "006E", "n" }, + { "006E_0303", "~n" }, + { "006F", "o" }, + { "006F_0300", "`o" }, + { "006F_0301", "'o" }, + { "006F_0302", "^o" }, + { "006F_0303", "~o" }, + { "006F_0308", ":o" }, + { "0070", "p" }, + { "0071", "q" }, + { "0072", "r" }, + { "0073", "s" }, + { "0073_030C", "vs" }, + { "0074", "t" }, + { "0075", "u" }, + { "0075_0300", "`u" }, + { "0075_0301", "'u" }, + { "0075_0302", "^u" }, + { "0075_0308", ":u" }, + { "0076", "v" }, + { "0077", "w" }, + { "0078", "x" }, + { "0079", "y" }, + { "0079_0301", "'y" }, + { "0079_0308", ":y" }, + { "007A", "z" }, + { "007A_030C", "vz" }, + { "007B", "lC" }, +//{ "007B", "{" }, + { "007C", "ba" }, +//{ "007C", "or" }, +//{ "007C", "|" }, + { "007D", "rC" }, +//{ "007D", "}" }, +//{ "007E", "a~" }, + { "007E", "ti" }, +//{ "007E", "~" }, + { "00A1", "r!" }, + { "00A2", "ct" }, + { "00A3", "Po" }, + { "00A4", "Cs" }, + { "00A5", "Ye" }, + { "00A6", "bb" }, + { "00A7", "sc" }, + { "00A8", "ad" }, + { "00A9", "co" }, + { "00AA", "Of" }, + { "00AB", "Fo" }, + { "00AC", "no" }, +//{ "00AC", "tno" }, + { "00AD", "shc" }, + { "00AE", "rg" }, + { "00AF", "a-" }, + { "00B0", "de" }, + { "00B1", "+-" }, +//{ "00B1", "t+-" }, + { "00B2", "S2" }, + { "00B3", "S3" }, + { "00B4", "aa" }, +//{ "00B4", "\\'" }, + { "00B5", "mc" }, + { "00B6", "ps" }, + { "00B7", "pc" }, + { "00B8", "ac" }, + { "00B9", "S1" }, + { "00BA", "Om" }, + { "00BB", "Fc" }, + { "00BC", "14" }, + { "00BD", "12" }, + { "00BE", "34" }, + { "00BF", "r?" }, + { "00C6", "AE" }, + { "00D0", "-D" }, + { "00D7", "mu" }, +//{ "00D7", "tmu" }, + { "00D8", "/O" }, + { "00DE", "TP" }, + { "00DF", "ss" }, + { "00E6", "ae" }, + { "00F0", "Sd" }, + { "00F7", "di" }, +//{ "00F7", "tdi" }, + { "00F8", "/o" }, + { "00FE", "Tp" }, + { "0131", ".i" }, + { "0132", "IJ" }, + { "0133", "ij" }, + { "0141", "/L" }, + { "0142", "/l" }, + { "0152", "OE" }, + { "0153", "oe" }, + { "0192", "Fn" }, + { "02C7", "ah" }, + { "02D8", "ab" }, + { "02D9", "a." }, + { "02DA", "ao" }, + { "02DB", "ho" }, + { "02DD", "a\"" }, + { "0391", "*A" }, + { "0392", "*B" }, + { "0393", "*G" }, + { "0394", "*D" }, + { "0395", "*E" }, + { "0396", "*Z" }, + { "0397", "*Y" }, + { "0398", "*H" }, + { "0399", "*I" }, + { "039A", "*K" }, + { "039B", "*L" }, + { "039C", "*M" }, + { "039D", "*N" }, + { "039E", "*C" }, + { "039F", "*O" }, + { "03A0", "*P" }, + { "03A1", "*R" }, + { "03A3", "*S" }, + { "03A4", "*T" }, + { "03A5", "*U" }, + { "03A6", "*F" }, + { "03A7", "*X" }, + { "03A8", "*Q" }, + { "03A9", "*W" }, + { "03B1", "*a" }, + { "03B2", "*b" }, + { "03B3", "*g" }, + { "03B4", "*d" }, + { "03B5", "*e" }, + { "03B6", "*z" }, + { "03B7", "*y" }, + { "03B8", "*h" }, + { "03B9", "*i" }, + { "03BA", "*k" }, + { "03BB", "*l" }, + { "03BC", "*m" }, + { "03BD", "*n" }, + { "03BE", "*c" }, + { "03BF", "*o" }, + { "03C0", "*p" }, + { "03C1", "*r" }, + { "03C2", "ts" }, + { "03C3", "*s" }, + { "03C4", "*t" }, + { "03C5", "*u" }, + { "03C6", "*f" }, + { "03C7", "*x" }, + { "03C8", "*q" }, + { "03C9", "*w" }, + { "03D1", "+h" }, + { "03D5", "+f" }, + { "03D6", "+p" }, + { "03F5", "+e" }, +//{ "2010", "-" }, + { "2010", "hy" }, + { "2013", "en" }, + { "2014", "em" }, +//{ "2018", "`" }, + { "2018", "oq" }, +//{ "2019", "'" }, + { "2019", "cq" }, + { "201A", "bq" }, + { "201C", "lq" }, + { "201D", "rq" }, + { "201E", "Bq" }, + { "2020", "dg" }, + { "2021", "dd" }, + { "2022", "bu" }, + { "2030", "%0" }, + { "2032", "fm" }, + { "2033", "sd" }, + { "2039", "fo" }, + { "203A", "fc" }, + { "203E", "rn" }, + { "2044", "f/" }, + { "20AC", "Eu" }, +//{ "20AC", "eu" }, + { "210F", "-h" }, +//{ "210F", "hbar" }, + { "2111", "Im" }, + { "2118", "wp" }, + { "211C", "Re" }, + { "2122", "tm" }, + { "2135", "Ah" }, + { "215B", "18" }, + { "215C", "38" }, + { "215D", "58" }, + { "215E", "78" }, + { "2190", "<-" }, + { "2191", "ua" }, + { "2192", "->" }, + { "2193", "da" }, + { "2194", "<>" }, + { "2195", "va" }, + { "21B5", "CR" }, + { "21D0", "lA" }, + { "21D1", "uA" }, + { "21D2", "rA" }, + { "21D3", "dA" }, + { "21D4", "hA" }, + { "21D5", "vA" }, + { "2200", "fa" }, + { "2202", "pd" }, + { "2203", "te" }, + { "2205", "es" }, + { "2207", "gr" }, + { "2208", "mo" }, + { "2208_0338", "nm" }, + { "220B", "st" }, + { "220F", "product" }, + { "2211", "sum" }, + { "2212", "mi" }, +//{ "2212", "\\-" }, + { "2213", "-+" }, + { "2217", "**" }, + { "221A", "sr" }, + { "221D", "pt" }, + { "221E", "if" }, + { "2220", "/_" }, + { "2227", "AN" }, + { "2228", "OR" }, + { "2229", "ca" }, + { "222A", "cu" }, + { "222B", "is" }, +//{ "222B", "integral" }, +//{ "2234", "3d" }, + { "2234", "tf" }, + { "223C", "ap" }, + { "2243", "|=" }, + { "2245", "=~" }, +//{ "2248", "~=" }, + { "2248", "~~" }, + { "2261", "==" }, + { "2261_0338", "ne" }, + { "2264", "<=" }, + { "2265", ">=" }, + { "226A", ">>" }, + { "226B", "<<" }, + { "2282", "sb" }, + { "2282_0338", "nb" }, + { "2283", "sp" }, + { "2283_0338", "nc" }, + { "2286", "ib" }, + { "2287", "ip" }, + { "2295", "c+" }, + { "2297", "c*" }, + { "22A5", "pp" }, + { "22C5", "md" }, + { "2308", "lc" }, + { "2309", "rc" }, + { "230A", "lf" }, + { "230B", "rf" }, + { "239B", "parenlefttp" }, + { "239C", "parenleftex" }, + { "239D", "parenleftbt" }, + { "239E", "parenrighttp" }, + { "239F", "parenrightex" }, + { "23A0", "parenrightbt" }, +//{ "23A1", "bracketlefttp" }, + { "23A2", "bracketleftex" }, +//{ "23A3", "bracketleftbt" }, +//{ "23A4", "bracketrighttp" }, + { "23A5", "bracketrightex" }, +//{ "23A6", "bracketrightbt" }, + { "23A7", "lt" }, +//{ "23A7", "bracelefttp" }, + { "23A8", "lk" }, +//{ "23A8", "braceleftmid" }, + { "23A9", "lb" }, +//{ "23A9", "braceleftbt" }, + { "23AA", "bv" }, +//{ "23AA", "braceex" }, +//{ "23AA", "braceleftex" }, +//{ "23AA", "bracerightex" }, + { "23AB", "rt" }, +//{ "23AB", "bracerighttp" }, + { "23AC", "rk" }, +//{ "23AC", "bracerightmid" }, + { "23AD", "rb" }, +//{ "23AD", "bracerightbt" }, + { "23AF", "an" }, + { "2502", "br" }, + { "251D", "rk" }, + { "2525", "lk" }, + { "256D", "lt" }, + { "256E", "rt" }, + { "256F", "rb" }, + { "2570", "lb" }, + { "25A1", "sq" }, + { "25CA", "lz" }, + { "25CB", "ci" }, + { "261C", "lh" }, + { "261E", "rh" }, + { "2660", "SP" }, + { "2663", "CL" }, + { "2665", "HE" }, + { "2666", "DI" }, + { "2713", "OK" }, + { "27E8", "la" }, + { "27E9", "ra" }, +}; + +// global constructor +static struct unicode_to_glyph_init { + unicode_to_glyph_init(); +} _unicode_to_glyph_init; + +unicode_to_glyph_init::unicode_to_glyph_init() { + for (unsigned int i = 0; + i < sizeof(unicode_to_glyph_list)/sizeof(unicode_to_glyph_list[0]); + i++) { + unicode_to_glyph *utg = new unicode_to_glyph[1]; + utg->value = (char *)unicode_to_glyph_list[i].value; + unicode_to_glyph_table.define(unicode_to_glyph_list[i].key, utg); + } +} + +const char *unicode_to_glyph_name(const char *s) +{ + unicode_to_glyph *result = unicode_to_glyph_table.lookup(s); + return result ? result->value : 0; +} diff --git a/contrib/groff/src/roff/troff/uniuni.cpp b/contrib/groff/src/roff/troff/uniuni.cpp new file mode 100644 index 0000000..3f4baa4 --- /dev/null +++ b/contrib/groff/src/roff/troff/uniuni.cpp @@ -0,0 +1,1994 @@ +// -*- C++ -*- +/* Copyright (C) 2002, 2003 + Free Software Foundation, Inc. + Written by Werner Lemberg + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +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. */ + +// This code has been algorithmically derived from the file +// UnicodeData-3.2.0.txt, available from unicode.org. + +#include "lib.h" +#include "stringclass.h" +#include "ptable.h" + +#include "unicode.h" + +struct unicode_decompose { + char *value; +}; + +declare_ptable(unicode_decompose) +implement_ptable(unicode_decompose) + +PTABLE(unicode_decompose) unicode_decompose_table; + +// the first digit in the composite string gives the number of composites + +struct { + const char *key; + const char *value; +} unicode_decompose_list[] = { + { "00C0", "20041_0300" }, + { "00C1", "20041_0301" }, + { "00C2", "20041_0302" }, + { "00C3", "20041_0303" }, + { "00C4", "20041_0308" }, + { "00C5", "20041_030A" }, + { "00C7", "20043_0327" }, + { "00C8", "20045_0300" }, + { "00C9", "20045_0301" }, + { "00CA", "20045_0302" }, + { "00CB", "20045_0308" }, + { "00CC", "20049_0300" }, + { "00CD", "20049_0301" }, + { "00CE", "20049_0302" }, + { "00CF", "20049_0308" }, + { "00D1", "2004E_0303" }, + { "00D2", "2004F_0300" }, + { "00D3", "2004F_0301" }, + { "00D4", "2004F_0302" }, + { "00D5", "2004F_0303" }, + { "00D6", "2004F_0308" }, + { "00D9", "20055_0300" }, + { "00DA", "20055_0301" }, + { "00DB", "20055_0302" }, + { "00DC", "20055_0308" }, + { "00DD", "20059_0301" }, + { "00E0", "20061_0300" }, + { "00E1", "20061_0301" }, + { "00E2", "20061_0302" }, + { "00E3", "20061_0303" }, + { "00E4", "20061_0308" }, + { "00E5", "20061_030A" }, + { "00E7", "20063_0327" }, + { "00E8", "20065_0300" }, + { "00E9", "20065_0301" }, + { "00EA", "20065_0302" }, + { "00EB", "20065_0308" }, + { "00EC", "20069_0300" }, + { "00ED", "20069_0301" }, + { "00EE", "20069_0302" }, + { "00EF", "20069_0308" }, + { "00F1", "2006E_0303" }, + { "00F2", "2006F_0300" }, + { "00F3", "2006F_0301" }, + { "00F4", "2006F_0302" }, + { "00F5", "2006F_0303" }, + { "00F6", "2006F_0308" }, + { "00F9", "20075_0300" }, + { "00FA", "20075_0301" }, + { "00FB", "20075_0302" }, + { "00FC", "20075_0308" }, + { "00FD", "20079_0301" }, + { "00FF", "20079_0308" }, + { "0100", "20041_0304" }, + { "0101", "20061_0304" }, + { "0102", "20041_0306" }, + { "0103", "20061_0306" }, + { "0104", "20041_0328" }, + { "0105", "20061_0328" }, + { "0106", "20043_0301" }, + { "0107", "20063_0301" }, + { "0108", "20043_0302" }, + { "0109", "20063_0302" }, + { "010A", "20043_0307" }, + { "010B", "20063_0307" }, + { "010C", "20043_030C" }, + { "010D", "20063_030C" }, + { "010E", "20044_030C" }, + { "010F", "20064_030C" }, + { "0112", "20045_0304" }, + { "0113", "20065_0304" }, + { "0114", "20045_0306" }, + { "0115", "20065_0306" }, + { "0116", "20045_0307" }, + { "0117", "20065_0307" }, + { "0118", "20045_0328" }, + { "0119", "20065_0328" }, + { "011A", "20045_030C" }, + { "011B", "20065_030C" }, + { "011C", "20047_0302" }, + { "011D", "20067_0302" }, + { "011E", "20047_0306" }, + { "011F", "20067_0306" }, + { "0120", "20047_0307" }, + { "0121", "20067_0307" }, + { "0122", "20047_0327" }, + { "0123", "20067_0327" }, + { "0124", "20048_0302" }, + { "0125", "20068_0302" }, + { "0128", "20049_0303" }, + { "0129", "20069_0303" }, + { "012A", "20049_0304" }, + { "012B", "20069_0304" }, + { "012C", "20049_0306" }, + { "012D", "20069_0306" }, + { "012E", "20049_0328" }, + { "012F", "20069_0328" }, + { "0130", "20049_0307" }, + { "0134", "2004A_0302" }, + { "0135", "2006A_0302" }, + { "0136", "2004B_0327" }, + { "0137", "2006B_0327" }, + { "0139", "2004C_0301" }, + { "013A", "2006C_0301" }, + { "013B", "2004C_0327" }, + { "013C", "2006C_0327" }, + { "013D", "2004C_030C" }, + { "013E", "2006C_030C" }, + { "0143", "2004E_0301" }, + { "0144", "2006E_0301" }, + { "0145", "2004E_0327" }, + { "0146", "2006E_0327" }, + { "0147", "2004E_030C" }, + { "0148", "2006E_030C" }, + { "014C", "2004F_0304" }, + { "014D", "2006F_0304" }, + { "014E", "2004F_0306" }, + { "014F", "2006F_0306" }, + { "0150", "2004F_030B" }, + { "0151", "2006F_030B" }, + { "0154", "20052_0301" }, + { "0155", "20072_0301" }, + { "0156", "20052_0327" }, + { "0157", "20072_0327" }, + { "0158", "20052_030C" }, + { "0159", "20072_030C" }, + { "015A", "20053_0301" }, + { "015B", "20073_0301" }, + { "015C", "20053_0302" }, + { "015D", "20073_0302" }, + { "015E", "20053_0327" }, + { "015F", "20073_0327" }, + { "0160", "20053_030C" }, + { "0161", "20073_030C" }, + { "0162", "20054_0327" }, + { "0163", "20074_0327" }, + { "0164", "20054_030C" }, + { "0165", "20074_030C" }, + { "0168", "20055_0303" }, + { "0169", "20075_0303" }, + { "016A", "20055_0304" }, + { "016B", "20075_0304" }, + { "016C", "20055_0306" }, + { "016D", "20075_0306" }, + { "016E", "20055_030A" }, + { "016F", "20075_030A" }, + { "0170", "20055_030B" }, + { "0171", "20075_030B" }, + { "0172", "20055_0328" }, + { "0173", "20075_0328" }, + { "0174", "20057_0302" }, + { "0175", "20077_0302" }, + { "0176", "20059_0302" }, + { "0177", "20079_0302" }, + { "0178", "20059_0308" }, + { "0179", "2005A_0301" }, + { "017A", "2007A_0301" }, + { "017B", "2005A_0307" }, + { "017C", "2007A_0307" }, + { "017D", "2005A_030C" }, + { "017E", "2007A_030C" }, + { "01A0", "2004F_031B" }, + { "01A1", "2006F_031B" }, + { "01AF", "20055_031B" }, + { "01B0", "20075_031B" }, + { "01CD", "20041_030C" }, + { "01CE", "20061_030C" }, + { "01CF", "20049_030C" }, + { "01D0", "20069_030C" }, + { "01D1", "2004F_030C" }, + { "01D2", "2006F_030C" }, + { "01D3", "20055_030C" }, + { "01D4", "20075_030C" }, + { "01D5", "30055_0308_0304" }, + { "01D6", "30075_0308_0304" }, + { "01D7", "30055_0308_0301" }, + { "01D8", "30075_0308_0301" }, + { "01D9", "30055_0308_030C" }, + { "01DA", "30075_0308_030C" }, + { "01DB", "30055_0308_0300" }, + { "01DC", "30075_0308_0300" }, + { "01DE", "30041_0308_0304" }, + { "01DF", "30061_0308_0304" }, + { "01E0", "30041_0307_0304" }, + { "01E1", "30061_0307_0304" }, + { "01E2", "200C6_0304" }, + { "01E3", "200E6_0304" }, + { "01E6", "20047_030C" }, + { "01E7", "20067_030C" }, + { "01E8", "2004B_030C" }, + { "01E9", "2006B_030C" }, + { "01EA", "2004F_0328" }, + { "01EB", "2006F_0328" }, + { "01EC", "3004F_0328_0304" }, + { "01ED", "3006F_0328_0304" }, + { "01EE", "201B7_030C" }, + { "01EF", "20292_030C" }, + { "01F0", "2006A_030C" }, + { "01F4", "20047_0301" }, + { "01F5", "20067_0301" }, + { "01F8", "2004E_0300" }, + { "01F9", "2006E_0300" }, + { "01FA", "30041_030A_0301" }, + { "01FB", "30061_030A_0301" }, + { "01FC", "200C6_0301" }, + { "01FD", "200E6_0301" }, + { "01FE", "200D8_0301" }, + { "01FF", "200F8_0301" }, + { "0200", "20041_030F" }, + { "0201", "20061_030F" }, + { "0202", "20041_0311" }, + { "0203", "20061_0311" }, + { "0204", "20045_030F" }, + { "0205", "20065_030F" }, + { "0206", "20045_0311" }, + { "0207", "20065_0311" }, + { "0208", "20049_030F" }, + { "0209", "20069_030F" }, + { "020A", "20049_0311" }, + { "020B", "20069_0311" }, + { "020C", "2004F_030F" }, + { "020D", "2006F_030F" }, + { "020E", "2004F_0311" }, + { "020F", "2006F_0311" }, + { "0210", "20052_030F" }, + { "0211", "20072_030F" }, + { "0212", "20052_0311" }, + { "0213", "20072_0311" }, + { "0214", "20055_030F" }, + { "0215", "20075_030F" }, + { "0216", "20055_0311" }, + { "0217", "20075_0311" }, + { "0218", "20053_0326" }, + { "0219", "20073_0326" }, + { "021A", "20054_0326" }, + { "021B", "20074_0326" }, + { "021E", "20048_030C" }, + { "021F", "20068_030C" }, + { "0226", "20041_0307" }, + { "0227", "20061_0307" }, + { "0228", "20045_0327" }, + { "0229", "20065_0327" }, + { "022A", "3004F_0308_0304" }, + { "022B", "3006F_0308_0304" }, + { "022C", "3004F_0303_0304" }, + { "022D", "3006F_0303_0304" }, + { "022E", "2004F_0307" }, + { "022F", "2006F_0307" }, + { "0230", "3004F_0307_0304" }, + { "0231", "3006F_0307_0304" }, + { "0232", "20059_0304" }, + { "0233", "20079_0304" }, + { "0340", "10300" }, + { "0341", "10301" }, + { "0343", "10313" }, + { "0344", "20308_0301" }, + { "0374", "102B9" }, + { "037E", "1003B" }, + { "0385", "200A8_0301" }, + { "0386", "20391_0301" }, + { "0387", "100B7" }, + { "0388", "20395_0301" }, + { "0389", "20397_0301" }, + { "038A", "20399_0301" }, + { "038C", "2039F_0301" }, + { "038E", "203A5_0301" }, + { "038F", "203A9_0301" }, + { "0390", "303B9_0308_0301" }, + { "03AA", "20399_0308" }, + { "03AB", "203A5_0308" }, + { "03AC", "203B1_0301" }, + { "03AD", "203B5_0301" }, + { "03AE", "203B7_0301" }, + { "03AF", "203B9_0301" }, + { "03B0", "303C5_0308_0301" }, + { "03CA", "203B9_0308" }, + { "03CB", "203C5_0308" }, + { "03CC", "203BF_0301" }, + { "03CD", "203C5_0301" }, + { "03CE", "203C9_0301" }, + { "03D3", "203D2_0301" }, + { "03D4", "203D2_0308" }, + { "0400", "20415_0300" }, + { "0401", "20415_0308" }, + { "0403", "20413_0301" }, + { "0407", "20406_0308" }, + { "040C", "2041A_0301" }, + { "040D", "20418_0300" }, + { "040E", "20423_0306" }, + { "0419", "20418_0306" }, + { "0439", "20438_0306" }, + { "0450", "20435_0300" }, + { "0451", "20435_0308" }, + { "0453", "20433_0301" }, + { "0457", "20456_0308" }, + { "045C", "2043A_0301" }, + { "045D", "20438_0300" }, + { "045E", "20443_0306" }, + { "0476", "20474_030F" }, + { "0477", "20475_030F" }, + { "04C1", "20416_0306" }, + { "04C2", "20436_0306" }, + { "04D0", "20410_0306" }, + { "04D1", "20430_0306" }, + { "04D2", "20410_0308" }, + { "04D3", "20430_0308" }, + { "04D6", "20415_0306" }, + { "04D7", "20435_0306" }, + { "04DA", "204D8_0308" }, + { "04DB", "204D9_0308" }, + { "04DC", "20416_0308" }, + { "04DD", "20436_0308" }, + { "04DE", "20417_0308" }, + { "04DF", "20437_0308" }, + { "04E2", "20418_0304" }, + { "04E3", "20438_0304" }, + { "04E4", "20418_0308" }, + { "04E5", "20438_0308" }, + { "04E6", "2041E_0308" }, + { "04E7", "2043E_0308" }, + { "04EA", "204E8_0308" }, + { "04EB", "204E9_0308" }, + { "04EC", "2042D_0308" }, + { "04ED", "2044D_0308" }, + { "04EE", "20423_0304" }, + { "04EF", "20443_0304" }, + { "04F0", "20423_0308" }, + { "04F1", "20443_0308" }, + { "04F2", "20423_030B" }, + { "04F3", "20443_030B" }, + { "04F4", "20427_0308" }, + { "04F5", "20447_0308" }, + { "04F8", "2042B_0308" }, + { "04F9", "2044B_0308" }, + { "0622", "20627_0653" }, + { "0623", "20627_0654" }, + { "0624", "20648_0654" }, + { "0625", "20627_0655" }, + { "0626", "2064A_0654" }, + { "06C0", "206D5_0654" }, + { "06C2", "206C1_0654" }, + { "06D3", "206D2_0654" }, + { "0929", "20928_093C" }, + { "0931", "20930_093C" }, + { "0934", "20933_093C" }, + { "0958", "20915_093C" }, + { "0959", "20916_093C" }, + { "095A", "20917_093C" }, + { "095B", "2091C_093C" }, + { "095C", "20921_093C" }, + { "095D", "20922_093C" }, + { "095E", "2092B_093C" }, + { "095F", "2092F_093C" }, + { "09CB", "209C7_09BE" }, + { "09CC", "209C7_09D7" }, + { "09DC", "209A1_09BC" }, + { "09DD", "209A2_09BC" }, + { "09DF", "209AF_09BC" }, + { "0A33", "20A32_0A3C" }, + { "0A36", "20A38_0A3C" }, + { "0A59", "20A16_0A3C" }, + { "0A5A", "20A17_0A3C" }, + { "0A5B", "20A1C_0A3C" }, + { "0A5E", "20A2B_0A3C" }, + { "0B48", "20B47_0B56" }, + { "0B4B", "20B47_0B3E" }, + { "0B4C", "20B47_0B57" }, + { "0B5C", "20B21_0B3C" }, + { "0B5D", "20B22_0B3C" }, + { "0B94", "20B92_0BD7" }, + { "0BCA", "20BC6_0BBE" }, + { "0BCB", "20BC7_0BBE" }, + { "0BCC", "20BC6_0BD7" }, + { "0C48", "20C46_0C56" }, + { "0CC0", "20CBF_0CD5" }, + { "0CC7", "20CC6_0CD5" }, + { "0CC8", "20CC6_0CD6" }, + { "0CCA", "20CC6_0CC2" }, + { "0CCB", "30CC6_0CC2_0CD5" }, + { "0D4A", "20D46_0D3E" }, + { "0D4B", "20D47_0D3E" }, + { "0D4C", "20D46_0D57" }, + { "0DDA", "20DD9_0DCA" }, + { "0DDC", "20DD9_0DCF" }, + { "0DDD", "30DD9_0DCF_0DCA" }, + { "0DDE", "20DD9_0DDF" }, + { "0F43", "20F42_0FB7" }, + { "0F4D", "20F4C_0FB7" }, + { "0F52", "20F51_0FB7" }, + { "0F57", "20F56_0FB7" }, + { "0F5C", "20F5B_0FB7" }, + { "0F69", "20F40_0FB5" }, + { "0F73", "20F71_0F72" }, + { "0F75", "20F71_0F74" }, + { "0F76", "20FB2_0F80" }, + { "0F78", "20FB3_0F80" }, + { "0F81", "20F71_0F80" }, + { "0F93", "20F92_0FB7" }, + { "0F9D", "20F9C_0FB7" }, + { "0FA2", "20FA1_0FB7" }, + { "0FA7", "20FA6_0FB7" }, + { "0FAC", "20FAB_0FB7" }, + { "0FB9", "20F90_0FB5" }, + { "1026", "21025_102E" }, + { "1E00", "20041_0325" }, + { "1E01", "20061_0325" }, + { "1E02", "20042_0307" }, + { "1E03", "20062_0307" }, + { "1E04", "20042_0323" }, + { "1E05", "20062_0323" }, + { "1E06", "20042_0331" }, + { "1E07", "20062_0331" }, + { "1E08", "30043_0327_0301" }, + { "1E09", "30063_0327_0301" }, + { "1E0A", "20044_0307" }, + { "1E0B", "20064_0307" }, + { "1E0C", "20044_0323" }, + { "1E0D", "20064_0323" }, + { "1E0E", "20044_0331" }, + { "1E0F", "20064_0331" }, + { "1E10", "20044_0327" }, + { "1E11", "20064_0327" }, + { "1E12", "20044_032D" }, + { "1E13", "20064_032D" }, + { "1E14", "30045_0304_0300" }, + { "1E15", "30065_0304_0300" }, + { "1E16", "30045_0304_0301" }, + { "1E17", "30065_0304_0301" }, + { "1E18", "20045_032D" }, + { "1E19", "20065_032D" }, + { "1E1A", "20045_0330" }, + { "1E1B", "20065_0330" }, + { "1E1C", "30045_0327_0306" }, + { "1E1D", "30065_0327_0306" }, + { "1E1E", "20046_0307" }, + { "1E1F", "20066_0307" }, + { "1E20", "20047_0304" }, + { "1E21", "20067_0304" }, + { "1E22", "20048_0307" }, + { "1E23", "20068_0307" }, + { "1E24", "20048_0323" }, + { "1E25", "20068_0323" }, + { "1E26", "20048_0308" }, + { "1E27", "20068_0308" }, + { "1E28", "20048_0327" }, + { "1E29", "20068_0327" }, + { "1E2A", "20048_032E" }, + { "1E2B", "20068_032E" }, + { "1E2C", "20049_0330" }, + { "1E2D", "20069_0330" }, + { "1E2E", "30049_0308_0301" }, + { "1E2F", "30069_0308_0301" }, + { "1E30", "2004B_0301" }, + { "1E31", "2006B_0301" }, + { "1E32", "2004B_0323" }, + { "1E33", "2006B_0323" }, + { "1E34", "2004B_0331" }, + { "1E35", "2006B_0331" }, + { "1E36", "2004C_0323" }, + { "1E37", "2006C_0323" }, + { "1E38", "3004C_0323_0304" }, + { "1E39", "3006C_0323_0304" }, + { "1E3A", "2004C_0331" }, + { "1E3B", "2006C_0331" }, + { "1E3C", "2004C_032D" }, + { "1E3D", "2006C_032D" }, + { "1E3E", "2004D_0301" }, + { "1E3F", "2006D_0301" }, + { "1E40", "2004D_0307" }, + { "1E41", "2006D_0307" }, + { "1E42", "2004D_0323" }, + { "1E43", "2006D_0323" }, + { "1E44", "2004E_0307" }, + { "1E45", "2006E_0307" }, + { "1E46", "2004E_0323" }, + { "1E47", "2006E_0323" }, + { "1E48", "2004E_0331" }, + { "1E49", "2006E_0331" }, + { "1E4A", "2004E_032D" }, + { "1E4B", "2006E_032D" }, + { "1E4C", "3004F_0303_0301" }, + { "1E4D", "3006F_0303_0301" }, + { "1E4E", "3004F_0303_0308" }, + { "1E4F", "3006F_0303_0308" }, + { "1E50", "3004F_0304_0300" }, + { "1E51", "3006F_0304_0300" }, + { "1E52", "3004F_0304_0301" }, + { "1E53", "3006F_0304_0301" }, + { "1E54", "20050_0301" }, + { "1E55", "20070_0301" }, + { "1E56", "20050_0307" }, + { "1E57", "20070_0307" }, + { "1E58", "20052_0307" }, + { "1E59", "20072_0307" }, + { "1E5A", "20052_0323" }, + { "1E5B", "20072_0323" }, + { "1E5C", "30052_0323_0304" }, + { "1E5D", "30072_0323_0304" }, + { "1E5E", "20052_0331" }, + { "1E5F", "20072_0331" }, + { "1E60", "20053_0307" }, + { "1E61", "20073_0307" }, + { "1E62", "20053_0323" }, + { "1E63", "20073_0323" }, + { "1E64", "30053_0301_0307" }, + { "1E65", "30073_0301_0307" }, + { "1E66", "30053_030C_0307" }, + { "1E67", "30073_030C_0307" }, + { "1E68", "30053_0323_0307" }, + { "1E69", "30073_0323_0307" }, + { "1E6A", "20054_0307" }, + { "1E6B", "20074_0307" }, + { "1E6C", "20054_0323" }, + { "1E6D", "20074_0323" }, + { "1E6E", "20054_0331" }, + { "1E6F", "20074_0331" }, + { "1E70", "20054_032D" }, + { "1E71", "20074_032D" }, + { "1E72", "20055_0324" }, + { "1E73", "20075_0324" }, + { "1E74", "20055_0330" }, + { "1E75", "20075_0330" }, + { "1E76", "20055_032D" }, + { "1E77", "20075_032D" }, + { "1E78", "30055_0303_0301" }, + { "1E79", "30075_0303_0301" }, + { "1E7A", "30055_0304_0308" }, + { "1E7B", "30075_0304_0308" }, + { "1E7C", "20056_0303" }, + { "1E7D", "20076_0303" }, + { "1E7E", "20056_0323" }, + { "1E7F", "20076_0323" }, + { "1E80", "20057_0300" }, + { "1E81", "20077_0300" }, + { "1E82", "20057_0301" }, + { "1E83", "20077_0301" }, + { "1E84", "20057_0308" }, + { "1E85", "20077_0308" }, + { "1E86", "20057_0307" }, + { "1E87", "20077_0307" }, + { "1E88", "20057_0323" }, + { "1E89", "20077_0323" }, + { "1E8A", "20058_0307" }, + { "1E8B", "20078_0307" }, + { "1E8C", "20058_0308" }, + { "1E8D", "20078_0308" }, + { "1E8E", "20059_0307" }, + { "1E8F", "20079_0307" }, + { "1E90", "2005A_0302" }, + { "1E91", "2007A_0302" }, + { "1E92", "2005A_0323" }, + { "1E93", "2007A_0323" }, + { "1E94", "2005A_0331" }, + { "1E95", "2007A_0331" }, + { "1E96", "20068_0331" }, + { "1E97", "20074_0308" }, + { "1E98", "20077_030A" }, + { "1E99", "20079_030A" }, + { "1E9B", "2017F_0307" }, + { "1EA0", "20041_0323" }, + { "1EA1", "20061_0323" }, + { "1EA2", "20041_0309" }, + { "1EA3", "20061_0309" }, + { "1EA4", "30041_0302_0301" }, + { "1EA5", "30061_0302_0301" }, + { "1EA6", "30041_0302_0300" }, + { "1EA7", "30061_0302_0300" }, + { "1EA8", "30041_0302_0309" }, + { "1EA9", "30061_0302_0309" }, + { "1EAA", "30041_0302_0303" }, + { "1EAB", "30061_0302_0303" }, + { "1EAC", "30041_0323_0302" }, + { "1EAD", "30061_0323_0302" }, + { "1EAE", "30041_0306_0301" }, + { "1EAF", "30061_0306_0301" }, + { "1EB0", "30041_0306_0300" }, + { "1EB1", "30061_0306_0300" }, + { "1EB2", "30041_0306_0309" }, + { "1EB3", "30061_0306_0309" }, + { "1EB4", "30041_0306_0303" }, + { "1EB5", "30061_0306_0303" }, + { "1EB6", "30041_0323_0306" }, + { "1EB7", "30061_0323_0306" }, + { "1EB8", "20045_0323" }, + { "1EB9", "20065_0323" }, + { "1EBA", "20045_0309" }, + { "1EBB", "20065_0309" }, + { "1EBC", "20045_0303" }, + { "1EBD", "20065_0303" }, + { "1EBE", "30045_0302_0301" }, + { "1EBF", "30065_0302_0301" }, + { "1EC0", "30045_0302_0300" }, + { "1EC1", "30065_0302_0300" }, + { "1EC2", "30045_0302_0309" }, + { "1EC3", "30065_0302_0309" }, + { "1EC4", "30045_0302_0303" }, + { "1EC5", "30065_0302_0303" }, + { "1EC6", "30045_0323_0302" }, + { "1EC7", "30065_0323_0302" }, + { "1EC8", "20049_0309" }, + { "1EC9", "20069_0309" }, + { "1ECA", "20049_0323" }, + { "1ECB", "20069_0323" }, + { "1ECC", "2004F_0323" }, + { "1ECD", "2006F_0323" }, + { "1ECE", "2004F_0309" }, + { "1ECF", "2006F_0309" }, + { "1ED0", "3004F_0302_0301" }, + { "1ED1", "3006F_0302_0301" }, + { "1ED2", "3004F_0302_0300" }, + { "1ED3", "3006F_0302_0300" }, + { "1ED4", "3004F_0302_0309" }, + { "1ED5", "3006F_0302_0309" }, + { "1ED6", "3004F_0302_0303" }, + { "1ED7", "3006F_0302_0303" }, + { "1ED8", "3004F_0323_0302" }, + { "1ED9", "3006F_0323_0302" }, + { "1EDA", "3004F_031B_0301" }, + { "1EDB", "3006F_031B_0301" }, + { "1EDC", "3004F_031B_0300" }, + { "1EDD", "3006F_031B_0300" }, + { "1EDE", "3004F_031B_0309" }, + { "1EDF", "3006F_031B_0309" }, + { "1EE0", "3004F_031B_0303" }, + { "1EE1", "3006F_031B_0303" }, + { "1EE2", "3004F_031B_0323" }, + { "1EE3", "3006F_031B_0323" }, + { "1EE4", "20055_0323" }, + { "1EE5", "20075_0323" }, + { "1EE6", "20055_0309" }, + { "1EE7", "20075_0309" }, + { "1EE8", "30055_031B_0301" }, + { "1EE9", "30075_031B_0301" }, + { "1EEA", "30055_031B_0300" }, + { "1EEB", "30075_031B_0300" }, + { "1EEC", "30055_031B_0309" }, + { "1EED", "30075_031B_0309" }, + { "1EEE", "30055_031B_0303" }, + { "1EEF", "30075_031B_0303" }, + { "1EF0", "30055_031B_0323" }, + { "1EF1", "30075_031B_0323" }, + { "1EF2", "20059_0300" }, + { "1EF3", "20079_0300" }, + { "1EF4", "20059_0323" }, + { "1EF5", "20079_0323" }, + { "1EF6", "20059_0309" }, + { "1EF7", "20079_0309" }, + { "1EF8", "20059_0303" }, + { "1EF9", "20079_0303" }, + { "1F00", "203B1_0313" }, + { "1F01", "203B1_0314" }, + { "1F02", "303B1_0313_0300" }, + { "1F03", "303B1_0314_0300" }, + { "1F04", "303B1_0313_0301" }, + { "1F05", "303B1_0314_0301" }, + { "1F06", "303B1_0313_0342" }, + { "1F07", "303B1_0314_0342" }, + { "1F08", "20391_0313" }, + { "1F09", "20391_0314" }, + { "1F0A", "30391_0313_0300" }, + { "1F0B", "30391_0314_0300" }, + { "1F0C", "30391_0313_0301" }, + { "1F0D", "30391_0314_0301" }, + { "1F0E", "30391_0313_0342" }, + { "1F0F", "30391_0314_0342" }, + { "1F10", "203B5_0313" }, + { "1F11", "203B5_0314" }, + { "1F12", "303B5_0313_0300" }, + { "1F13", "303B5_0314_0300" }, + { "1F14", "303B5_0313_0301" }, + { "1F15", "303B5_0314_0301" }, + { "1F18", "20395_0313" }, + { "1F19", "20395_0314" }, + { "1F1A", "30395_0313_0300" }, + { "1F1B", "30395_0314_0300" }, + { "1F1C", "30395_0313_0301" }, + { "1F1D", "30395_0314_0301" }, + { "1F20", "203B7_0313" }, + { "1F21", "203B7_0314" }, + { "1F22", "303B7_0313_0300" }, + { "1F23", "303B7_0314_0300" }, + { "1F24", "303B7_0313_0301" }, + { "1F25", "303B7_0314_0301" }, + { "1F26", "303B7_0313_0342" }, + { "1F27", "303B7_0314_0342" }, + { "1F28", "20397_0313" }, + { "1F29", "20397_0314" }, + { "1F2A", "30397_0313_0300" }, + { "1F2B", "30397_0314_0300" }, + { "1F2C", "30397_0313_0301" }, + { "1F2D", "30397_0314_0301" }, + { "1F2E", "30397_0313_0342" }, + { "1F2F", "30397_0314_0342" }, + { "1F30", "203B9_0313" }, + { "1F31", "203B9_0314" }, + { "1F32", "303B9_0313_0300" }, + { "1F33", "303B9_0314_0300" }, + { "1F34", "303B9_0313_0301" }, + { "1F35", "303B9_0314_0301" }, + { "1F36", "303B9_0313_0342" }, + { "1F37", "303B9_0314_0342" }, + { "1F38", "20399_0313" }, + { "1F39", "20399_0314" }, + { "1F3A", "30399_0313_0300" }, + { "1F3B", "30399_0314_0300" }, + { "1F3C", "30399_0313_0301" }, + { "1F3D", "30399_0314_0301" }, + { "1F3E", "30399_0313_0342" }, + { "1F3F", "30399_0314_0342" }, + { "1F40", "203BF_0313" }, + { "1F41", "203BF_0314" }, + { "1F42", "303BF_0313_0300" }, + { "1F43", "303BF_0314_0300" }, + { "1F44", "303BF_0313_0301" }, + { "1F45", "303BF_0314_0301" }, + { "1F48", "2039F_0313" }, + { "1F49", "2039F_0314" }, + { "1F4A", "3039F_0313_0300" }, + { "1F4B", "3039F_0314_0300" }, + { "1F4C", "3039F_0313_0301" }, + { "1F4D", "3039F_0314_0301" }, + { "1F50", "203C5_0313" }, + { "1F51", "203C5_0314" }, + { "1F52", "303C5_0313_0300" }, + { "1F53", "303C5_0314_0300" }, + { "1F54", "303C5_0313_0301" }, + { "1F55", "303C5_0314_0301" }, + { "1F56", "303C5_0313_0342" }, + { "1F57", "303C5_0314_0342" }, + { "1F59", "203A5_0314" }, + { "1F5B", "303A5_0314_0300" }, + { "1F5D", "303A5_0314_0301" }, + { "1F5F", "303A5_0314_0342" }, + { "1F60", "203C9_0313" }, + { "1F61", "203C9_0314" }, + { "1F62", "303C9_0313_0300" }, + { "1F63", "303C9_0314_0300" }, + { "1F64", "303C9_0313_0301" }, + { "1F65", "303C9_0314_0301" }, + { "1F66", "303C9_0313_0342" }, + { "1F67", "303C9_0314_0342" }, + { "1F68", "203A9_0313" }, + { "1F69", "203A9_0314" }, + { "1F6A", "303A9_0313_0300" }, + { "1F6B", "303A9_0314_0300" }, + { "1F6C", "303A9_0313_0301" }, + { "1F6D", "303A9_0314_0301" }, + { "1F6E", "303A9_0313_0342" }, + { "1F6F", "303A9_0314_0342" }, + { "1F70", "203B1_0300" }, + { "1F71", "203B1_0301" }, + { "1F72", "203B5_0300" }, + { "1F73", "203B5_0301" }, + { "1F74", "203B7_0300" }, + { "1F75", "203B7_0301" }, + { "1F76", "203B9_0300" }, + { "1F77", "203B9_0301" }, + { "1F78", "203BF_0300" }, + { "1F79", "203BF_0301" }, + { "1F7A", "203C5_0300" }, + { "1F7B", "203C5_0301" }, + { "1F7C", "203C9_0300" }, + { "1F7D", "203C9_0301" }, + { "1F80", "303B1_0313_0345" }, + { "1F81", "303B1_0314_0345" }, + { "1F82", "403B1_0313_0300_0345" }, + { "1F83", "403B1_0314_0300_0345" }, + { "1F84", "403B1_0313_0301_0345" }, + { "1F85", "403B1_0314_0301_0345" }, + { "1F86", "403B1_0313_0342_0345" }, + { "1F87", "403B1_0314_0342_0345" }, + { "1F88", "30391_0313_0345" }, + { "1F89", "30391_0314_0345" }, + { "1F8A", "40391_0313_0300_0345" }, + { "1F8B", "40391_0314_0300_0345" }, + { "1F8C", "40391_0313_0301_0345" }, + { "1F8D", "40391_0314_0301_0345" }, + { "1F8E", "40391_0313_0342_0345" }, + { "1F8F", "40391_0314_0342_0345" }, + { "1F90", "303B7_0313_0345" }, + { "1F91", "303B7_0314_0345" }, + { "1F92", "403B7_0313_0300_0345" }, + { "1F93", "403B7_0314_0300_0345" }, + { "1F94", "403B7_0313_0301_0345" }, + { "1F95", "403B7_0314_0301_0345" }, + { "1F96", "403B7_0313_0342_0345" }, + { "1F97", "403B7_0314_0342_0345" }, + { "1F98", "30397_0313_0345" }, + { "1F99", "30397_0314_0345" }, + { "1F9A", "40397_0313_0300_0345" }, + { "1F9B", "40397_0314_0300_0345" }, + { "1F9C", "40397_0313_0301_0345" }, + { "1F9D", "40397_0314_0301_0345" }, + { "1F9E", "40397_0313_0342_0345" }, + { "1F9F", "40397_0314_0342_0345" }, + { "1FA0", "303C9_0313_0345" }, + { "1FA1", "303C9_0314_0345" }, + { "1FA2", "403C9_0313_0300_0345" }, + { "1FA3", "403C9_0314_0300_0345" }, + { "1FA4", "403C9_0313_0301_0345" }, + { "1FA5", "403C9_0314_0301_0345" }, + { "1FA6", "403C9_0313_0342_0345" }, + { "1FA7", "403C9_0314_0342_0345" }, + { "1FA8", "303A9_0313_0345" }, + { "1FA9", "303A9_0314_0345" }, + { "1FAA", "403A9_0313_0300_0345" }, + { "1FAB", "403A9_0314_0300_0345" }, + { "1FAC", "403A9_0313_0301_0345" }, + { "1FAD", "403A9_0314_0301_0345" }, + { "1FAE", "403A9_0313_0342_0345" }, + { "1FAF", "403A9_0314_0342_0345" }, + { "1FB0", "203B1_0306" }, + { "1FB1", "203B1_0304" }, + { "1FB2", "303B1_0300_0345" }, + { "1FB3", "203B1_0345" }, + { "1FB4", "303B1_0301_0345" }, + { "1FB6", "203B1_0342" }, + { "1FB7", "303B1_0342_0345" }, + { "1FB8", "20391_0306" }, + { "1FB9", "20391_0304" }, + { "1FBA", "20391_0300" }, + { "1FBB", "20391_0301" }, + { "1FBC", "20391_0345" }, + { "1FBE", "103B9" }, + { "1FC1", "200A8_0342" }, + { "1FC2", "303B7_0300_0345" }, + { "1FC3", "203B7_0345" }, + { "1FC4", "303B7_0301_0345" }, + { "1FC6", "203B7_0342" }, + { "1FC7", "303B7_0342_0345" }, + { "1FC8", "20395_0300" }, + { "1FC9", "20395_0301" }, + { "1FCA", "20397_0300" }, + { "1FCB", "20397_0301" }, + { "1FCC", "20397_0345" }, + { "1FCD", "21FBF_0300" }, + { "1FCE", "21FBF_0301" }, + { "1FCF", "21FBF_0342" }, + { "1FD0", "203B9_0306" }, + { "1FD1", "203B9_0304" }, + { "1FD2", "303B9_0308_0300" }, + { "1FD3", "303B9_0308_0301" }, + { "1FD6", "203B9_0342" }, + { "1FD7", "303B9_0308_0342" }, + { "1FD8", "20399_0306" }, + { "1FD9", "20399_0304" }, + { "1FDA", "20399_0300" }, + { "1FDB", "20399_0301" }, + { "1FDD", "21FFE_0300" }, + { "1FDE", "21FFE_0301" }, + { "1FDF", "21FFE_0342" }, + { "1FE0", "203C5_0306" }, + { "1FE1", "203C5_0304" }, + { "1FE2", "303C5_0308_0300" }, + { "1FE3", "303C5_0308_0301" }, + { "1FE4", "203C1_0313" }, + { "1FE5", "203C1_0314" }, + { "1FE6", "203C5_0342" }, + { "1FE7", "303C5_0308_0342" }, + { "1FE8", "203A5_0306" }, + { "1FE9", "203A5_0304" }, + { "1FEA", "203A5_0300" }, + { "1FEB", "203A5_0301" }, + { "1FEC", "203A1_0314" }, + { "1FED", "200A8_0300" }, + { "1FEE", "200A8_0301" }, + { "1FEF", "10060" }, + { "1FF2", "303C9_0300_0345" }, + { "1FF3", "203C9_0345" }, + { "1FF4", "303C9_0301_0345" }, + { "1FF6", "203C9_0342" }, + { "1FF7", "303C9_0342_0345" }, + { "1FF8", "2039F_0300" }, + { "1FF9", "2039F_0301" }, + { "1FFA", "203A9_0300" }, + { "1FFB", "203A9_0301" }, + { "1FFC", "203A9_0345" }, + { "1FFD", "100B4" }, + { "2000", "12002" }, + { "2001", "12003" }, + { "2126", "103A9" }, + { "212A", "1004B" }, + { "212B", "20041_030A" }, + { "219A", "22190_0338" }, + { "219B", "22192_0338" }, + { "21AE", "22194_0338" }, + { "21CD", "221D0_0338" }, + { "21CE", "221D4_0338" }, + { "21CF", "221D2_0338" }, + { "2204", "22203_0338" }, + { "2209", "22208_0338" }, + { "220C", "2220B_0338" }, + { "2224", "22223_0338" }, + { "2226", "22225_0338" }, + { "2241", "2223C_0338" }, + { "2244", "22243_0338" }, + { "2247", "22245_0338" }, + { "2249", "22248_0338" }, + { "2260", "2003D_0338" }, + { "2262", "22261_0338" }, + { "226D", "2224D_0338" }, + { "226E", "2003C_0338" }, + { "226F", "2003E_0338" }, + { "2270", "22264_0338" }, + { "2271", "22265_0338" }, + { "2274", "22272_0338" }, + { "2275", "22273_0338" }, + { "2278", "22276_0338" }, + { "2279", "22277_0338" }, + { "2280", "2227A_0338" }, + { "2281", "2227B_0338" }, + { "2284", "22282_0338" }, + { "2285", "22283_0338" }, + { "2288", "22286_0338" }, + { "2289", "22287_0338" }, + { "22AC", "222A2_0338" }, + { "22AD", "222A8_0338" }, + { "22AE", "222A9_0338" }, + { "22AF", "222AB_0338" }, + { "22E0", "2227C_0338" }, + { "22E1", "2227D_0338" }, + { "22E2", "22291_0338" }, + { "22E3", "22292_0338" }, + { "22EA", "222B2_0338" }, + { "22EB", "222B3_0338" }, + { "22EC", "222B4_0338" }, + { "22ED", "222B5_0338" }, + { "2329", "13008" }, + { "232A", "13009" }, + { "2ADC", "22ADD_0338" }, + { "304C", "2304B_3099" }, + { "304E", "2304D_3099" }, + { "3050", "2304F_3099" }, + { "3052", "23051_3099" }, + { "3054", "23053_3099" }, + { "3056", "23055_3099" }, + { "3058", "23057_3099" }, + { "305A", "23059_3099" }, + { "305C", "2305B_3099" }, + { "305E", "2305D_3099" }, + { "3060", "2305F_3099" }, + { "3062", "23061_3099" }, + { "3065", "23064_3099" }, + { "3067", "23066_3099" }, + { "3069", "23068_3099" }, + { "3070", "2306F_3099" }, + { "3071", "2306F_309A" }, + { "3073", "23072_3099" }, + { "3074", "23072_309A" }, + { "3076", "23075_3099" }, + { "3077", "23075_309A" }, + { "3079", "23078_3099" }, + { "307A", "23078_309A" }, + { "307C", "2307B_3099" }, + { "307D", "2307B_309A" }, + { "3094", "23046_3099" }, + { "309E", "2309D_3099" }, + { "30AC", "230AB_3099" }, + { "30AE", "230AD_3099" }, + { "30B0", "230AF_3099" }, + { "30B2", "230B1_3099" }, + { "30B4", "230B3_3099" }, + { "30B6", "230B5_3099" }, + { "30B8", "230B7_3099" }, + { "30BA", "230B9_3099" }, + { "30BC", "230BB_3099" }, + { "30BE", "230BD_3099" }, + { "30C0", "230BF_3099" }, + { "30C2", "230C1_3099" }, + { "30C5", "230C4_3099" }, + { "30C7", "230C6_3099" }, + { "30C9", "230C8_3099" }, + { "30D0", "230CF_3099" }, + { "30D1", "230CF_309A" }, + { "30D3", "230D2_3099" }, + { "30D4", "230D2_309A" }, + { "30D6", "230D5_3099" }, + { "30D7", "230D5_309A" }, + { "30D9", "230D8_3099" }, + { "30DA", "230D8_309A" }, + { "30DC", "230DB_3099" }, + { "30DD", "230DB_309A" }, + { "30F4", "230A6_3099" }, + { "30F7", "230EF_3099" }, + { "30F8", "230F0_3099" }, + { "30F9", "230F1_3099" }, + { "30FA", "230F2_3099" }, + { "30FE", "230FD_3099" }, + { "F900", "18C48" }, + { "F901", "166F4" }, + { "F902", "18ECA" }, + { "F903", "18CC8" }, + { "F904", "16ED1" }, + { "F905", "14E32" }, + { "F906", "153E5" }, + { "F907", "19F9C" }, + { "F908", "19F9C" }, + { "F909", "15951" }, + { "F90A", "191D1" }, + { "F90B", "15587" }, + { "F90C", "15948" }, + { "F90D", "161F6" }, + { "F90E", "17669" }, + { "F90F", "17F85" }, + { "F910", "1863F" }, + { "F911", "187BA" }, + { "F912", "188F8" }, + { "F913", "1908F" }, + { "F914", "16A02" }, + { "F915", "16D1B" }, + { "F916", "170D9" }, + { "F917", "173DE" }, + { "F918", "1843D" }, + { "F919", "1916A" }, + { "F91A", "199F1" }, + { "F91B", "14E82" }, + { "F91C", "15375" }, + { "F91D", "16B04" }, + { "F91E", "1721B" }, + { "F91F", "1862D" }, + { "F920", "19E1E" }, + { "F921", "15D50" }, + { "F922", "16FEB" }, + { "F923", "185CD" }, + { "F924", "18964" }, + { "F925", "162C9" }, + { "F926", "181D8" }, + { "F927", "1881F" }, + { "F928", "15ECA" }, + { "F929", "16717" }, + { "F92A", "16D6A" }, + { "F92B", "172FC" }, + { "F92C", "190CE" }, + { "F92D", "14F86" }, + { "F92E", "151B7" }, + { "F92F", "152DE" }, + { "F930", "164C4" }, + { "F931", "16AD3" }, + { "F932", "17210" }, + { "F933", "176E7" }, + { "F934", "18001" }, + { "F935", "18606" }, + { "F936", "1865C" }, + { "F937", "18DEF" }, + { "F938", "19732" }, + { "F939", "19B6F" }, + { "F93A", "19DFA" }, + { "F93B", "1788C" }, + { "F93C", "1797F" }, + { "F93D", "17DA0" }, + { "F93E", "183C9" }, + { "F93F", "19304" }, + { "F940", "19E7F" }, + { "F941", "18AD6" }, + { "F942", "158DF" }, + { "F943", "15F04" }, + { "F944", "17C60" }, + { "F945", "1807E" }, + { "F946", "17262" }, + { "F947", "178CA" }, + { "F948", "18CC2" }, + { "F949", "196F7" }, + { "F94A", "158D8" }, + { "F94B", "15C62" }, + { "F94C", "16A13" }, + { "F94D", "16DDA" }, + { "F94E", "16F0F" }, + { "F94F", "17D2F" }, + { "F950", "17E37" }, + { "F951", "1964B" }, + { "F952", "152D2" }, + { "F953", "1808B" }, + { "F954", "151DC" }, + { "F955", "151CC" }, + { "F956", "17A1C" }, + { "F957", "17DBE" }, + { "F958", "183F1" }, + { "F959", "19675" }, + { "F95A", "18B80" }, + { "F95B", "162CF" }, + { "F95C", "16A02" }, + { "F95D", "18AFE" }, + { "F95E", "14E39" }, + { "F95F", "15BE7" }, + { "F960", "16012" }, + { "F961", "17387" }, + { "F962", "17570" }, + { "F963", "15317" }, + { "F964", "178FB" }, + { "F965", "14FBF" }, + { "F966", "15FA9" }, + { "F967", "14E0D" }, + { "F968", "16CCC" }, + { "F969", "16578" }, + { "F96A", "17D22" }, + { "F96B", "153C3" }, + { "F96C", "1585E" }, + { "F96D", "17701" }, + { "F96E", "18449" }, + { "F96F", "18AAA" }, + { "F970", "16BBA" }, + { "F971", "18FB0" }, + { "F972", "16C88" }, + { "F973", "162FE" }, + { "F974", "182E5" }, + { "F975", "163A0" }, + { "F976", "17565" }, + { "F977", "14EAE" }, + { "F978", "15169" }, + { "F979", "151C9" }, + { "F97A", "16881" }, + { "F97B", "17CE7" }, + { "F97C", "1826F" }, + { "F97D", "18AD2" }, + { "F97E", "191CF" }, + { "F97F", "152F5" }, + { "F980", "15442" }, + { "F981", "15973" }, + { "F982", "15EEC" }, + { "F983", "165C5" }, + { "F984", "16FFE" }, + { "F985", "1792A" }, + { "F986", "195AD" }, + { "F987", "19A6A" }, + { "F988", "19E97" }, + { "F989", "19ECE" }, + { "F98A", "1529B" }, + { "F98B", "166C6" }, + { "F98C", "16B77" }, + { "F98D", "18F62" }, + { "F98E", "15E74" }, + { "F98F", "16190" }, + { "F990", "16200" }, + { "F991", "1649A" }, + { "F992", "16F23" }, + { "F993", "17149" }, + { "F994", "17489" }, + { "F995", "179CA" }, + { "F996", "17DF4" }, + { "F997", "1806F" }, + { "F998", "18F26" }, + { "F999", "184EE" }, + { "F99A", "19023" }, + { "F99B", "1934A" }, + { "F99C", "15217" }, + { "F99D", "152A3" }, + { "F99E", "154BD" }, + { "F99F", "170C8" }, + { "F9A0", "188C2" }, + { "F9A1", "18AAA" }, + { "F9A2", "15EC9" }, + { "F9A3", "15FF5" }, + { "F9A4", "1637B" }, + { "F9A5", "16BAE" }, + { "F9A6", "17C3E" }, + { "F9A7", "17375" }, + { "F9A8", "14EE4" }, + { "F9A9", "156F9" }, + { "F9AA", "15BE7" }, + { "F9AB", "15DBA" }, + { "F9AC", "1601C" }, + { "F9AD", "173B2" }, + { "F9AE", "17469" }, + { "F9AF", "17F9A" }, + { "F9B0", "18046" }, + { "F9B1", "19234" }, + { "F9B2", "196F6" }, + { "F9B3", "19748" }, + { "F9B4", "19818" }, + { "F9B5", "14F8B" }, + { "F9B6", "179AE" }, + { "F9B7", "191B4" }, + { "F9B8", "196B8" }, + { "F9B9", "160E1" }, + { "F9BA", "14E86" }, + { "F9BB", "150DA" }, + { "F9BC", "15BEE" }, + { "F9BD", "15C3F" }, + { "F9BE", "16599" }, + { "F9BF", "16A02" }, + { "F9C0", "171CE" }, + { "F9C1", "17642" }, + { "F9C2", "184FC" }, + { "F9C3", "1907C" }, + { "F9C4", "19F8D" }, + { "F9C5", "16688" }, + { "F9C6", "1962E" }, + { "F9C7", "15289" }, + { "F9C8", "1677B" }, + { "F9C9", "167F3" }, + { "F9CA", "16D41" }, + { "F9CB", "16E9C" }, + { "F9CC", "17409" }, + { "F9CD", "17559" }, + { "F9CE", "1786B" }, + { "F9CF", "17D10" }, + { "F9D0", "1985E" }, + { "F9D1", "1516D" }, + { "F9D2", "1622E" }, + { "F9D3", "19678" }, + { "F9D4", "1502B" }, + { "F9D5", "15D19" }, + { "F9D6", "16DEA" }, + { "F9D7", "18F2A" }, + { "F9D8", "15F8B" }, + { "F9D9", "16144" }, + { "F9DA", "16817" }, + { "F9DB", "17387" }, + { "F9DC", "19686" }, + { "F9DD", "15229" }, + { "F9DE", "1540F" }, + { "F9DF", "15C65" }, + { "F9E0", "16613" }, + { "F9E1", "1674E" }, + { "F9E2", "168A8" }, + { "F9E3", "16CE5" }, + { "F9E4", "17406" }, + { "F9E5", "175E2" }, + { "F9E6", "17F79" }, + { "F9E7", "188CF" }, + { "F9E8", "188E1" }, + { "F9E9", "191CC" }, + { "F9EA", "196E2" }, + { "F9EB", "1533F" }, + { "F9EC", "16EBA" }, + { "F9ED", "1541D" }, + { "F9EE", "171D0" }, + { "F9EF", "17498" }, + { "F9F0", "185FA" }, + { "F9F1", "196A3" }, + { "F9F2", "19C57" }, + { "F9F3", "19E9F" }, + { "F9F4", "16797" }, + { "F9F5", "16DCB" }, + { "F9F6", "181E8" }, + { "F9F7", "17ACB" }, + { "F9F8", "17B20" }, + { "F9F9", "17C92" }, + { "F9FA", "172C0" }, + { "F9FB", "17099" }, + { "F9FC", "18B58" }, + { "F9FD", "14EC0" }, + { "F9FE", "18336" }, + { "F9FF", "1523A" }, + { "FA00", "15207" }, + { "FA01", "15EA6" }, + { "FA02", "162D3" }, + { "FA03", "17CD6" }, + { "FA04", "15B85" }, + { "FA05", "16D1E" }, + { "FA06", "166B4" }, + { "FA07", "18F3B" }, + { "FA08", "1884C" }, + { "FA09", "1964D" }, + { "FA0A", "1898B" }, + { "FA0B", "15ED3" }, + { "FA0C", "15140" }, + { "FA0D", "155C0" }, + { "FA10", "1585A" }, + { "FA12", "16674" }, + { "FA15", "151DE" }, + { "FA16", "1732A" }, + { "FA17", "176CA" }, + { "FA18", "1793C" }, + { "FA19", "1795E" }, + { "FA1A", "17965" }, + { "FA1B", "1798F" }, + { "FA1C", "19756" }, + { "FA1D", "17CBE" }, + { "FA1E", "17FBD" }, + { "FA20", "18612" }, + { "FA22", "18AF8" }, + { "FA25", "19038" }, + { "FA26", "190FD" }, + { "FA2A", "198EF" }, + { "FA2B", "198FC" }, + { "FA2C", "19928" }, + { "FA2D", "19DB4" }, + { "FA30", "14FAE" }, + { "FA31", "150E7" }, + { "FA32", "1514D" }, + { "FA33", "152C9" }, + { "FA34", "152E4" }, + { "FA35", "15351" }, + { "FA36", "1559D" }, + { "FA37", "15606" }, + { "FA38", "15668" }, + { "FA39", "15840" }, + { "FA3A", "158A8" }, + { "FA3B", "15C64" }, + { "FA3C", "15C6E" }, + { "FA3D", "16094" }, + { "FA3E", "16168" }, + { "FA3F", "1618E" }, + { "FA40", "161F2" }, + { "FA41", "1654F" }, + { "FA42", "165E2" }, + { "FA43", "16691" }, + { "FA44", "16885" }, + { "FA45", "16D77" }, + { "FA46", "16E1A" }, + { "FA47", "16F22" }, + { "FA48", "1716E" }, + { "FA49", "1722B" }, + { "FA4A", "17422" }, + { "FA4B", "17891" }, + { "FA4C", "1793E" }, + { "FA4D", "17949" }, + { "FA4E", "17948" }, + { "FA4F", "17950" }, + { "FA50", "17956" }, + { "FA51", "1795D" }, + { "FA52", "1798D" }, + { "FA53", "1798E" }, + { "FA54", "17A40" }, + { "FA55", "17A81" }, + { "FA56", "17BC0" }, + { "FA57", "17DF4" }, + { "FA58", "17E09" }, + { "FA59", "17E41" }, + { "FA5A", "17F72" }, + { "FA5B", "18005" }, + { "FA5C", "181ED" }, + { "FA5D", "18279" }, + { "FA5E", "18279" }, + { "FA5F", "18457" }, + { "FA60", "18910" }, + { "FA61", "18996" }, + { "FA62", "18B01" }, + { "FA63", "18B39" }, + { "FA64", "18CD3" }, + { "FA65", "18D08" }, + { "FA66", "18FB6" }, + { "FA67", "19038" }, + { "FA68", "196E3" }, + { "FA69", "197FF" }, + { "FA6A", "1983B" }, + { "FB1D", "205D9_05B4" }, + { "FB1F", "205F2_05B7" }, + { "FB2A", "205E9_05C1" }, + { "FB2B", "205E9_05C2" }, + { "FB2C", "305E9_05BC_05C1" }, + { "FB2D", "305E9_05BC_05C2" }, + { "FB2E", "205D0_05B7" }, + { "FB2F", "205D0_05B8" }, + { "FB30", "205D0_05BC" }, + { "FB31", "205D1_05BC" }, + { "FB32", "205D2_05BC" }, + { "FB33", "205D3_05BC" }, + { "FB34", "205D4_05BC" }, + { "FB35", "205D5_05BC" }, + { "FB36", "205D6_05BC" }, + { "FB38", "205D8_05BC" }, + { "FB39", "205D9_05BC" }, + { "FB3A", "205DA_05BC" }, + { "FB3B", "205DB_05BC" }, + { "FB3C", "205DC_05BC" }, + { "FB3E", "205DE_05BC" }, + { "FB40", "205E0_05BC" }, + { "FB41", "205E1_05BC" }, + { "FB43", "205E3_05BC" }, + { "FB44", "205E4_05BC" }, + { "FB46", "205E6_05BC" }, + { "FB47", "205E7_05BC" }, + { "FB48", "205E8_05BC" }, + { "FB49", "205E9_05BC" }, + { "FB4A", "205EA_05BC" }, + { "FB4B", "205D5_05B9" }, + { "FB4C", "205D1_05BF" }, + { "FB4D", "205DB_05BF" }, + { "FB4E", "205E4_05BF" }, + { "1D15E", "21D157_1D165" }, + { "1D15F", "21D158_1D165" }, + { "1D160", "31D158_1D165_1D16E" }, + { "1D161", "31D158_1D165_1D16F" }, + { "1D162", "31D158_1D165_1D170" }, + { "1D163", "31D158_1D165_1D171" }, + { "1D164", "31D158_1D165_1D172" }, + { "1D1BB", "21D1B9_1D165" }, + { "1D1BC", "21D1BA_1D165" }, + { "1D1BD", "31D1B9_1D165_1D16E" }, + { "1D1BE", "31D1BA_1D165_1D16E" }, + { "1D1BF", "31D1B9_1D165_1D16F" }, + { "1D1C0", "31D1BA_1D165_1D16F" }, + { "2F800", "14E3D" }, + { "2F801", "14E38" }, + { "2F802", "14E41" }, + { "2F803", "120122" }, + { "2F804", "14F60" }, + { "2F805", "14FAE" }, + { "2F806", "14FBB" }, + { "2F807", "15002" }, + { "2F808", "1507A" }, + { "2F809", "15099" }, + { "2F80A", "150E7" }, + { "2F80B", "150CF" }, + { "2F80C", "1349E" }, + { "2F80D", "12063A" }, + { "2F80E", "1514D" }, + { "2F80F", "15154" }, + { "2F810", "15164" }, + { "2F811", "15177" }, + { "2F812", "12051C" }, + { "2F813", "134B9" }, + { "2F814", "15167" }, + { "2F815", "1518D" }, + { "2F816", "12054B" }, + { "2F817", "15197" }, + { "2F818", "151A4" }, + { "2F819", "14ECC" }, + { "2F81A", "151AC" }, + { "2F81B", "151B5" }, + { "2F81C", "1291DF" }, + { "2F81D", "151F5" }, + { "2F81E", "15203" }, + { "2F81F", "134DF" }, + { "2F820", "1523B" }, + { "2F821", "15246" }, + { "2F822", "15272" }, + { "2F823", "15277" }, + { "2F824", "13515" }, + { "2F825", "152C7" }, + { "2F826", "152C9" }, + { "2F827", "152E4" }, + { "2F828", "152FA" }, + { "2F829", "15305" }, + { "2F82A", "15306" }, + { "2F82B", "15317" }, + { "2F82C", "15349" }, + { "2F82D", "15351" }, + { "2F82E", "1535A" }, + { "2F82F", "15373" }, + { "2F830", "1537D" }, + { "2F831", "1537F" }, + { "2F832", "1537F" }, + { "2F833", "1537F" }, + { "2F834", "120A2C" }, + { "2F835", "17070" }, + { "2F836", "153CA" }, + { "2F837", "153DF" }, + { "2F838", "120B63" }, + { "2F839", "153EB" }, + { "2F83A", "153F1" }, + { "2F83B", "15406" }, + { "2F83C", "1549E" }, + { "2F83D", "15438" }, + { "2F83E", "15448" }, + { "2F83F", "15468" }, + { "2F840", "154A2" }, + { "2F841", "154F6" }, + { "2F842", "15510" }, + { "2F843", "15553" }, + { "2F844", "15563" }, + { "2F845", "15584" }, + { "2F846", "15584" }, + { "2F847", "15599" }, + { "2F848", "155AB" }, + { "2F849", "155B3" }, + { "2F84A", "155C2" }, + { "2F84B", "15716" }, + { "2F84C", "15606" }, + { "2F84D", "15717" }, + { "2F84E", "15651" }, + { "2F84F", "15674" }, + { "2F850", "15207" }, + { "2F851", "158EE" }, + { "2F852", "157CE" }, + { "2F853", "157F4" }, + { "2F854", "1580D" }, + { "2F855", "1578B" }, + { "2F856", "15832" }, + { "2F857", "15831" }, + { "2F858", "158AC" }, + { "2F859", "1214E4" }, + { "2F85A", "158F2" }, + { "2F85B", "158F7" }, + { "2F85C", "15906" }, + { "2F85D", "1591A" }, + { "2F85E", "15922" }, + { "2F85F", "15962" }, + { "2F860", "1216A8" }, + { "2F861", "1216EA" }, + { "2F862", "159EC" }, + { "2F863", "15A1B" }, + { "2F864", "15A27" }, + { "2F865", "159D8" }, + { "2F866", "15A66" }, + { "2F867", "136EE" }, + { "2F868", "12136A" }, + { "2F869", "15B08" }, + { "2F86A", "15B3E" }, + { "2F86B", "15B3E" }, + { "2F86C", "1219C8" }, + { "2F86D", "15BC3" }, + { "2F86E", "15BD8" }, + { "2F86F", "15BE7" }, + { "2F870", "15BF3" }, + { "2F871", "121B18" }, + { "2F872", "15BFF" }, + { "2F873", "15C06" }, + { "2F874", "15F33" }, + { "2F875", "15C22" }, + { "2F876", "13781" }, + { "2F877", "15C60" }, + { "2F878", "15C6E" }, + { "2F879", "15CC0" }, + { "2F87A", "15C8D" }, + { "2F87B", "121DE4" }, + { "2F87C", "15D43" }, + { "2F87D", "121DE6" }, + { "2F87E", "15D6E" }, + { "2F87F", "15D6B" }, + { "2F880", "15D7C" }, + { "2F881", "15DE1" }, + { "2F882", "15DE2" }, + { "2F883", "1382F" }, + { "2F884", "15DFD" }, + { "2F885", "15E28" }, + { "2F886", "15E3D" }, + { "2F887", "15E69" }, + { "2F888", "13862" }, + { "2F889", "122183" }, + { "2F88A", "1387C" }, + { "2F88B", "15EB0" }, + { "2F88C", "15EB3" }, + { "2F88D", "15EB6" }, + { "2F88E", "15ECA" }, + { "2F88F", "12A392" }, + { "2F890", "15EFE" }, + { "2F891", "122331" }, + { "2F892", "122331" }, + { "2F893", "18201" }, + { "2F894", "15F22" }, + { "2F895", "15F22" }, + { "2F896", "138C7" }, + { "2F897", "1232B8" }, + { "2F898", "1261DA" }, + { "2F899", "15F62" }, + { "2F89A", "15F6B" }, + { "2F89B", "138E3" }, + { "2F89C", "15F9A" }, + { "2F89D", "15FCD" }, + { "2F89E", "15FD7" }, + { "2F89F", "15FF9" }, + { "2F8A0", "16081" }, + { "2F8A1", "1393A" }, + { "2F8A2", "1391C" }, + { "2F8A3", "16094" }, + { "2F8A4", "1226D4" }, + { "2F8A5", "160C7" }, + { "2F8A6", "16148" }, + { "2F8A7", "1614C" }, + { "2F8A8", "1614E" }, + { "2F8A9", "1614C" }, + { "2F8AA", "1617A" }, + { "2F8AB", "1618E" }, + { "2F8AC", "161B2" }, + { "2F8AD", "161A4" }, + { "2F8AE", "161AF" }, + { "2F8AF", "161DE" }, + { "2F8B0", "161F2" }, + { "2F8B1", "161F6" }, + { "2F8B2", "16210" }, + { "2F8B3", "1621B" }, + { "2F8B4", "1625D" }, + { "2F8B5", "162B1" }, + { "2F8B6", "162D4" }, + { "2F8B7", "16350" }, + { "2F8B8", "122B0C" }, + { "2F8B9", "1633D" }, + { "2F8BA", "162FC" }, + { "2F8BB", "16368" }, + { "2F8BC", "16383" }, + { "2F8BD", "163E4" }, + { "2F8BE", "122BF1" }, + { "2F8BF", "16422" }, + { "2F8C0", "163C5" }, + { "2F8C1", "163A9" }, + { "2F8C2", "13A2E" }, + { "2F8C3", "16469" }, + { "2F8C4", "1647E" }, + { "2F8C5", "1649D" }, + { "2F8C6", "16477" }, + { "2F8C7", "13A6C" }, + { "2F8C8", "1654F" }, + { "2F8C9", "1656C" }, + { "2F8CA", "12300A" }, + { "2F8CB", "165E3" }, + { "2F8CC", "166F8" }, + { "2F8CD", "16649" }, + { "2F8CE", "13B19" }, + { "2F8CF", "16691" }, + { "2F8D0", "13B08" }, + { "2F8D1", "13AE4" }, + { "2F8D2", "15192" }, + { "2F8D3", "15195" }, + { "2F8D4", "16700" }, + { "2F8D5", "1669C" }, + { "2F8D6", "180AD" }, + { "2F8D7", "143D9" }, + { "2F8D8", "16717" }, + { "2F8D9", "1671B" }, + { "2F8DA", "16721" }, + { "2F8DB", "1675E" }, + { "2F8DC", "16753" }, + { "2F8DD", "1233C3" }, + { "2F8DE", "13B49" }, + { "2F8DF", "167FA" }, + { "2F8E0", "16785" }, + { "2F8E1", "16852" }, + { "2F8E2", "16885" }, + { "2F8E3", "12346D" }, + { "2F8E4", "1688E" }, + { "2F8E5", "1681F" }, + { "2F8E6", "16914" }, + { "2F8E7", "13B9D" }, + { "2F8E8", "16942" }, + { "2F8E9", "169A3" }, + { "2F8EA", "169EA" }, + { "2F8EB", "16AA8" }, + { "2F8EC", "1236A3" }, + { "2F8ED", "16ADB" }, + { "2F8EE", "13C18" }, + { "2F8EF", "16B21" }, + { "2F8F0", "1238A7" }, + { "2F8F1", "16B54" }, + { "2F8F2", "13C4E" }, + { "2F8F3", "16B72" }, + { "2F8F4", "16B9F" }, + { "2F8F5", "16BBA" }, + { "2F8F6", "16BBB" }, + { "2F8F7", "123A8D" }, + { "2F8F8", "121D0B" }, + { "2F8F9", "123AFA" }, + { "2F8FA", "16C4E" }, + { "2F8FB", "123CBC" }, + { "2F8FC", "16CBF" }, + { "2F8FD", "16CCD" }, + { "2F8FE", "16C67" }, + { "2F8FF", "16D16" }, + { "2F900", "16D3E" }, + { "2F901", "16D77" }, + { "2F902", "16D41" }, + { "2F903", "16D69" }, + { "2F904", "16D78" }, + { "2F905", "16D85" }, + { "2F906", "123D1E" }, + { "2F907", "16D34" }, + { "2F908", "16E2F" }, + { "2F909", "16E6E" }, + { "2F90A", "13D33" }, + { "2F90B", "16ECB" }, + { "2F90C", "16EC7" }, + { "2F90D", "123ED1" }, + { "2F90E", "16DF9" }, + { "2F90F", "16F6E" }, + { "2F910", "123F5E" }, + { "2F911", "123F8E" }, + { "2F912", "16FC6" }, + { "2F913", "17039" }, + { "2F914", "1701E" }, + { "2F915", "1701B" }, + { "2F916", "13D96" }, + { "2F917", "1704A" }, + { "2F918", "1707D" }, + { "2F919", "17077" }, + { "2F91A", "170AD" }, + { "2F91B", "120525" }, + { "2F91C", "17145" }, + { "2F91D", "124263" }, + { "2F91E", "1719C" }, + { "2F91F", "143AB" }, + { "2F920", "17228" }, + { "2F921", "17235" }, + { "2F922", "17250" }, + { "2F923", "124608" }, + { "2F924", "17280" }, + { "2F925", "17295" }, + { "2F926", "124735" }, + { "2F927", "124814" }, + { "2F928", "1737A" }, + { "2F929", "1738B" }, + { "2F92A", "13EAC" }, + { "2F92B", "173A5" }, + { "2F92C", "13EB8" }, + { "2F92D", "13EB8" }, + { "2F92E", "17447" }, + { "2F92F", "1745C" }, + { "2F930", "17471" }, + { "2F931", "17485" }, + { "2F932", "174CA" }, + { "2F933", "13F1B" }, + { "2F934", "17524" }, + { "2F935", "124C36" }, + { "2F936", "1753E" }, + { "2F937", "124C92" }, + { "2F938", "17570" }, + { "2F939", "12219F" }, + { "2F93A", "17610" }, + { "2F93B", "124FA1" }, + { "2F93C", "124FB8" }, + { "2F93D", "125044" }, + { "2F93E", "13FFC" }, + { "2F93F", "14008" }, + { "2F940", "176F4" }, + { "2F941", "1250F3" }, + { "2F942", "1250F2" }, + { "2F943", "125119" }, + { "2F944", "125133" }, + { "2F945", "1771E" }, + { "2F946", "1771F" }, + { "2F947", "1771F" }, + { "2F948", "1774A" }, + { "2F949", "14039" }, + { "2F94A", "1778B" }, + { "2F94B", "14046" }, + { "2F94C", "14096" }, + { "2F94D", "12541D" }, + { "2F94E", "1784E" }, + { "2F94F", "1788C" }, + { "2F950", "178CC" }, + { "2F951", "140E3" }, + { "2F952", "125626" }, + { "2F953", "17956" }, + { "2F954", "12569A" }, + { "2F955", "1256C5" }, + { "2F956", "1798F" }, + { "2F957", "179EB" }, + { "2F958", "1412F" }, + { "2F959", "17A40" }, + { "2F95A", "17A4A" }, + { "2F95B", "17A4F" }, + { "2F95C", "12597C" }, + { "2F95D", "125AA7" }, + { "2F95E", "125AA7" }, + { "2F95F", "17AAE" }, + { "2F960", "14202" }, + { "2F961", "125BAB" }, + { "2F962", "17BC6" }, + { "2F963", "17BC9" }, + { "2F964", "14227" }, + { "2F965", "125C80" }, + { "2F966", "17CD2" }, + { "2F967", "142A0" }, + { "2F968", "17CE8" }, + { "2F969", "17CE3" }, + { "2F96A", "17D00" }, + { "2F96B", "125F86" }, + { "2F96C", "17D63" }, + { "2F96D", "14301" }, + { "2F96E", "17DC7" }, + { "2F96F", "17E02" }, + { "2F970", "17E45" }, + { "2F971", "14334" }, + { "2F972", "126228" }, + { "2F973", "126247" }, + { "2F974", "14359" }, + { "2F975", "1262D9" }, + { "2F976", "17F7A" }, + { "2F977", "12633E" }, + { "2F978", "17F95" }, + { "2F979", "17FFA" }, + { "2F97A", "18005" }, + { "2F97B", "1264DA" }, + { "2F97C", "126523" }, + { "2F97D", "18060" }, + { "2F97E", "1265A8" }, + { "2F97F", "18070" }, + { "2F980", "12335F" }, + { "2F981", "143D5" }, + { "2F982", "180B2" }, + { "2F983", "18103" }, + { "2F984", "1440B" }, + { "2F985", "1813E" }, + { "2F986", "15AB5" }, + { "2F987", "1267A7" }, + { "2F988", "1267B5" }, + { "2F989", "123393" }, + { "2F98A", "12339C" }, + { "2F98B", "18201" }, + { "2F98C", "18204" }, + { "2F98D", "18F9E" }, + { "2F98E", "1446B" }, + { "2F98F", "18291" }, + { "2F990", "1828B" }, + { "2F991", "1829D" }, + { "2F992", "152B3" }, + { "2F993", "182B1" }, + { "2F994", "182B3" }, + { "2F995", "182BD" }, + { "2F996", "182E6" }, + { "2F997", "126B3C" }, + { "2F998", "182E5" }, + { "2F999", "1831D" }, + { "2F99A", "18363" }, + { "2F99B", "183AD" }, + { "2F99C", "18323" }, + { "2F99D", "183BD" }, + { "2F99E", "183E7" }, + { "2F99F", "18457" }, + { "2F9A0", "18353" }, + { "2F9A1", "183CA" }, + { "2F9A2", "183CC" }, + { "2F9A3", "183DC" }, + { "2F9A4", "126C36" }, + { "2F9A5", "126D6B" }, + { "2F9A6", "126CD5" }, + { "2F9A7", "1452B" }, + { "2F9A8", "184F1" }, + { "2F9A9", "184F3" }, + { "2F9AA", "18516" }, + { "2F9AB", "1273CA" }, + { "2F9AC", "18564" }, + { "2F9AD", "126F2C" }, + { "2F9AE", "1455D" }, + { "2F9AF", "14561" }, + { "2F9B0", "126FB1" }, + { "2F9B1", "1270D2" }, + { "2F9B2", "1456B" }, + { "2F9B3", "18650" }, + { "2F9B4", "1865C" }, + { "2F9B5", "18667" }, + { "2F9B6", "18669" }, + { "2F9B7", "186A9" }, + { "2F9B8", "18688" }, + { "2F9B9", "1870E" }, + { "2F9BA", "186E2" }, + { "2F9BB", "18779" }, + { "2F9BC", "18728" }, + { "2F9BD", "1876B" }, + { "2F9BE", "18786" }, + { "2F9BF", "14D57" }, + { "2F9C0", "187E1" }, + { "2F9C1", "18801" }, + { "2F9C2", "145F9" }, + { "2F9C3", "18860" }, + { "2F9C4", "18863" }, + { "2F9C5", "127667" }, + { "2F9C6", "188D7" }, + { "2F9C7", "188DE" }, + { "2F9C8", "14635" }, + { "2F9C9", "188FA" }, + { "2F9CA", "134BB" }, + { "2F9CB", "1278AE" }, + { "2F9CC", "127966" }, + { "2F9CD", "146BE" }, + { "2F9CE", "146C7" }, + { "2F9CF", "18AA0" }, + { "2F9D0", "18AED" }, + { "2F9D1", "18B8A" }, + { "2F9D2", "18C55" }, + { "2F9D3", "127CA8" }, + { "2F9D4", "18CAB" }, + { "2F9D5", "18CC1" }, + { "2F9D6", "18D1B" }, + { "2F9D7", "18D77" }, + { "2F9D8", "127F2F" }, + { "2F9D9", "120804" }, + { "2F9DA", "18DCB" }, + { "2F9DB", "18DBC" }, + { "2F9DC", "18DF0" }, + { "2F9DD", "1208DE" }, + { "2F9DE", "18ED4" }, + { "2F9DF", "18F38" }, + { "2F9E0", "1285D2" }, + { "2F9E1", "1285ED" }, + { "2F9E2", "19094" }, + { "2F9E3", "190F1" }, + { "2F9E4", "19111" }, + { "2F9E5", "12872E" }, + { "2F9E6", "1911B" }, + { "2F9E7", "19238" }, + { "2F9E8", "192D7" }, + { "2F9E9", "192D8" }, + { "2F9EA", "1927C" }, + { "2F9EB", "193F9" }, + { "2F9EC", "19415" }, + { "2F9ED", "128BFA" }, + { "2F9EE", "1958B" }, + { "2F9EF", "14995" }, + { "2F9F0", "195B7" }, + { "2F9F1", "128D77" }, + { "2F9F2", "149E6" }, + { "2F9F3", "196C3" }, + { "2F9F4", "15DB2" }, + { "2F9F5", "19723" }, + { "2F9F6", "129145" }, + { "2F9F7", "12921A" }, + { "2F9F8", "14A6E" }, + { "2F9F9", "14A76" }, + { "2F9FA", "197E0" }, + { "2F9FB", "12940A" }, + { "2F9FC", "14AB2" }, + { "2F9FD", "129496" }, + { "2F9FE", "1980B" }, + { "2F9FF", "1980B" }, + { "2FA00", "19829" }, + { "2FA01", "1295B6" }, + { "2FA02", "198E2" }, + { "2FA03", "14B33" }, + { "2FA04", "19929" }, + { "2FA05", "199A7" }, + { "2FA06", "199C2" }, + { "2FA07", "199FE" }, + { "2FA08", "14BCE" }, + { "2FA09", "129B30" }, + { "2FA0A", "19B12" }, + { "2FA0B", "19C40" }, + { "2FA0C", "19CFD" }, + { "2FA0D", "14CCE" }, + { "2FA0E", "14CED" }, + { "2FA0F", "19D67" }, + { "2FA10", "12A0CE" }, + { "2FA11", "14CF8" }, + { "2FA12", "12A105" }, + { "2FA13", "12A20E" }, + { "2FA14", "12A291" }, + { "2FA15", "19EBB" }, + { "2FA16", "14D56" }, + { "2FA17", "19EF9" }, + { "2FA18", "19EFE" }, + { "2FA19", "19F05" }, + { "2FA1A", "19F0F" }, + { "2FA1B", "19F16" }, + { "2FA1C", "19F3B" }, + { "2FA1D", "12A600" }, +}; + +// global constructor + +static struct unicode_decompose_init { + unicode_decompose_init(); +} _unicode_decompose_init; + +unicode_decompose_init::unicode_decompose_init() { + for (unsigned int i = 0; + i < sizeof(unicode_decompose_list)/sizeof(unicode_decompose_list[0]); + i++) { + unicode_decompose *dec = new unicode_decompose[1]; + dec->value = (char *)unicode_decompose_list[i].value; + unicode_decompose_table.define(unicode_decompose_list[i].key, dec); + } +} + +const char *decompose_unicode(const char *s) +{ + unicode_decompose *result = unicode_decompose_table.lookup(s); + return result ? result->value : 0; +} -- cgit v1.1