summaryrefslogtreecommitdiffstats
path: root/contrib/groff/src/roff/troff
diff options
context:
space:
mode:
authorru <ru@FreeBSD.org>2003-05-01 13:09:50 +0000
committerru <ru@FreeBSD.org>2003-05-01 13:09:50 +0000
commitc96557721be60d942f4d486b9ea7f9b7cbb034cc (patch)
tree5bb520ef39570cf7c612b59697308ed396ca84e0 /contrib/groff/src/roff/troff
parentf78d5fa81a0b603b2741c98c8f48ce8245a18a4c (diff)
downloadFreeBSD-src-c96557721be60d942f4d486b9ea7f9b7cbb034cc.zip
FreeBSD-src-c96557721be60d942f4d486b9ea7f9b7cbb034cc.tar.gz
Virgin import of FSF groff v1.19
Diffstat (limited to 'contrib/groff/src/roff/troff')
-rw-r--r--contrib/groff/src/roff/troff/Makefile.sub45
-rw-r--r--contrib/groff/src/roff/troff/charinfo.h21
-rw-r--r--contrib/groff/src/roff/troff/column.cpp732
-rw-r--r--contrib/groff/src/roff/troff/dictionary.cpp212
-rw-r--r--contrib/groff/src/roff/troff/div.cpp1182
-rw-r--r--contrib/groff/src/roff/troff/div.h5
-rw-r--r--contrib/groff/src/roff/troff/env.cpp3818
-rw-r--r--contrib/groff/src/roff/troff/glyphuni.cpp503
-rw-r--r--contrib/groff/src/roff/troff/input.cpp7879
-rw-r--r--contrib/groff/src/roff/troff/node.cpp5993
-rw-r--r--contrib/groff/src/roff/troff/node.h16
-rw-r--r--contrib/groff/src/roff/troff/number.cpp697
-rw-r--r--contrib/groff/src/roff/troff/reg.cpp474
-rw-r--r--contrib/groff/src/roff/troff/reg.h4
-rw-r--r--contrib/groff/src/roff/troff/symbol.cpp154
-rw-r--r--contrib/groff/src/roff/troff/token.h9
-rw-r--r--contrib/groff/src/roff/troff/troff.man7
-rw-r--r--contrib/groff/src/roff/troff/unicode.cpp67
-rw-r--r--contrib/groff/src/roff/troff/unicode.h26
-rw-r--r--contrib/groff/src/roff/troff/uniglyph.cpp503
-rw-r--r--contrib/groff/src/roff/troff/uniuni.cpp1994
21 files changed, 24306 insertions, 35 deletions
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 <math.h>
+
+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,
+ &current_language->exceptions);
+ }
+ skip_line();
+}
+
+static void hyphenation_patterns_file()
+{
+ do_hyphenation_patterns_file(0);
+}
+
+static void hyphenation_patterns_file_append()
+{
+ do_hyphenation_patterns_file(1);
+}
+
+class hyphenation_language_reg : public reg {
+public:
+ const char *get_string();
+};
+
+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 <wl@gnu.org>
+
+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 = "<standard input>";
+ 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 = &top; *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 = &top; *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 &macro::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, &macros);
+ 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, &register_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 = &macro_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 <unistd.h>
+#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 <sys/wait.h>
+
+#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("<beginning of page>\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(&not_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(), &not_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 <wl@gnu.org>
+
+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 <wl@gnu.org>
+
+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 <wl@gnu.org>
+
+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 <wl@gnu.org>
+
+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;
+}
OpenPOWER on IntegriCloud