aboutsummaryrefslogtreecommitdiff
path: root/contrib/groff/src
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/groff/src')
-rw-r--r--contrib/groff/src/devices/grodvi/Makefile.sub6
-rw-r--r--contrib/groff/src/devices/grodvi/dvi.cc912
-rw-r--r--contrib/groff/src/devices/grodvi/grodvi.man174
-rw-r--r--contrib/groff/src/devices/grohtml/Makefile.sub16
-rw-r--r--contrib/groff/src/devices/grohtml/grohtml.man137
-rw-r--r--contrib/groff/src/devices/grohtml/html-chars.h27
-rw-r--r--contrib/groff/src/devices/grohtml/html-text.cc829
-rw-r--r--contrib/groff/src/devices/grohtml/html-text.h109
-rw-r--r--contrib/groff/src/devices/grohtml/html.h96
-rw-r--r--contrib/groff/src/devices/grohtml/output.cc335
-rw-r--r--contrib/groff/src/devices/grohtml/post-html.cc2933
-rw-r--r--contrib/groff/src/devices/grolbp/Makefile.sub6
-rw-r--r--contrib/groff/src/devices/grolbp/charset.h69
-rw-r--r--contrib/groff/src/devices/grolbp/grolbp.man357
-rw-r--r--contrib/groff/src/devices/grolbp/lbp.cc765
-rw-r--r--contrib/groff/src/devices/grolbp/lbp.h511
-rw-r--r--contrib/groff/src/devices/grolj4/Makefile.sub6
-rw-r--r--contrib/groff/src/devices/grolj4/grolj4.man144
-rw-r--r--contrib/groff/src/devices/grolj4/lj4.cc710
-rw-r--r--contrib/groff/src/devices/grops/Makefile.sub12
-rw-r--r--contrib/groff/src/devices/grops/TODO29
-rw-r--r--contrib/groff/src/devices/grops/grops.man861
-rw-r--r--contrib/groff/src/devices/grops/ps.cc1570
-rw-r--r--contrib/groff/src/devices/grops/ps.h122
-rw-r--r--contrib/groff/src/devices/grops/psfig.diff106
-rw-r--r--contrib/groff/src/devices/grops/psrm.cc1118
-rw-r--r--contrib/groff/src/devices/grotty/Makefile.sub6
-rw-r--r--contrib/groff/src/devices/grotty/TODO3
-rw-r--r--contrib/groff/src/devices/grotty/tty.cc509
-rw-r--r--contrib/groff/src/include/Makefile.sub42
-rw-r--r--contrib/groff/src/include/assert.h39
-rw-r--r--contrib/groff/src/include/cmap.h56
-rw-r--r--contrib/groff/src/include/cset.h75
-rw-r--r--contrib/groff/src/include/device.h21
-rw-r--r--contrib/groff/src/include/driver.h35
-rw-r--r--contrib/groff/src/include/errarg.h46
-rw-r--r--contrib/groff/src/include/error.h58
-rw-r--r--contrib/groff/src/include/font.h116
-rw-r--r--contrib/groff/src/include/getopt.h169
-rw-r--r--contrib/groff/src/include/groff-getopt.h68
-rw-r--r--contrib/groff/src/include/html-strings.h31
-rw-r--r--contrib/groff/src/include/htmlindicate.h59
-rw-r--r--contrib/groff/src/include/index.h42
-rw-r--r--contrib/groff/src/include/lib.h133
-rw-r--r--contrib/groff/src/include/macropath.h23
-rw-r--r--contrib/groff/src/include/nonposix.h136
-rw-r--r--contrib/groff/src/include/posix.h51
-rw-r--r--contrib/groff/src/include/printer.h77
-rw-r--r--contrib/groff/src/include/ptable.h168
-rw-r--r--contrib/groff/src/include/refid.h35
-rw-r--r--contrib/groff/src/include/search.h96
-rw-r--r--contrib/groff/src/include/searchpath.h30
-rw-r--r--contrib/groff/src/include/stringclass.h195
-rw-r--r--contrib/groff/src/libs/libbib/Makefile.sub14
-rw-r--r--contrib/groff/src/libs/libbib/common.cc38
-rw-r--r--contrib/groff/src/libs/libbib/index.cc641
-rw-r--r--contrib/groff/src/libs/libbib/linear.cc503
-rw-r--r--contrib/groff/src/libs/libbib/map.c71
-rw-r--r--contrib/groff/src/libs/libbib/search.cc132
-rw-r--r--contrib/groff/src/libs/libdriver/Makefile.sub7
-rw-r--r--contrib/groff/src/libs/libdriver/input.cc504
-rw-r--r--contrib/groff/src/libs/libdriver/printer.cc271
-rw-r--r--contrib/groff/src/libs/libgroff/Makefile.sub84
-rw-r--r--contrib/groff/src/libs/libgroff/assert.cc34
-rw-r--r--contrib/groff/src/libs/libgroff/change_lf.cc37
-rw-r--r--contrib/groff/src/libs/libgroff/device.cc36
-rw-r--r--contrib/groff/src/libs/libgroff/errarg.cc118
-rw-r--r--contrib/groff/src/libs/libgroff/error.cc137
-rw-r--r--contrib/groff/src/libs/libgroff/fatal.cc27
-rw-r--r--contrib/groff/src/libs/libgroff/filename.cc1
-rw-r--r--contrib/groff/src/libs/libgroff/fmod.c28
-rw-r--r--contrib/groff/src/libs/libgroff/font.cc938
-rw-r--r--contrib/groff/src/libs/libgroff/fontfile.cc66
-rw-r--r--contrib/groff/src/libs/libgroff/getcwd.c54
-rw-r--r--contrib/groff/src/libs/libgroff/getopt.c1055
-rw-r--r--contrib/groff/src/libs/libgroff/getopt1.c188
-rw-r--r--contrib/groff/src/libs/libgroff/htmlindicate.cc97
-rw-r--r--contrib/groff/src/libs/libgroff/iftoa.c65
-rw-r--r--contrib/groff/src/libs/libgroff/itoa.c43
-rw-r--r--contrib/groff/src/libs/libgroff/lf.cc62
-rw-r--r--contrib/groff/src/libs/libgroff/lineno.cc1
-rw-r--r--contrib/groff/src/libs/libgroff/macropath.cc30
-rw-r--r--contrib/groff/src/libs/libgroff/matherr.c45
-rw-r--r--contrib/groff/src/libs/libgroff/nametoindex.cc118
-rw-r--r--contrib/groff/src/libs/libgroff/new.cc68
-rw-r--r--contrib/groff/src/libs/libgroff/prime.cc26
-rw-r--r--contrib/groff/src/libs/libgroff/progname.cc1
-rw-r--r--contrib/groff/src/libs/libgroff/ptable.cc52
-rw-r--r--contrib/groff/src/libs/libgroff/putenv.c95
-rw-r--r--contrib/groff/src/libs/libgroff/searchpath.cc132
-rw-r--r--contrib/groff/src/libs/libgroff/strerror.c41
-rw-r--r--contrib/groff/src/libs/libgroff/string.cc311
-rw-r--r--contrib/groff/src/libs/libgroff/strsave.cc31
-rw-r--r--contrib/groff/src/libs/libgroff/strtol.c128
-rw-r--r--contrib/groff/src/libs/libgroff/tmpfile.cc186
-rw-r--r--contrib/groff/src/preproc/eqn/Makefile.sub59
-rw-r--r--contrib/groff/src/preproc/eqn/TODO49
-rw-r--r--contrib/groff/src/preproc/eqn/box.cc611
-rw-r--r--contrib/groff/src/preproc/eqn/box.h277
-rw-r--r--contrib/groff/src/preproc/eqn/delim.cc381
-rw-r--r--contrib/groff/src/preproc/eqn/eqn.cc1277
-rw-r--r--contrib/groff/src/preproc/eqn/eqn.h51
-rw-r--r--contrib/groff/src/preproc/eqn/eqn.man882
-rw-r--r--contrib/groff/src/preproc/eqn/eqn.y331
-rw-r--r--contrib/groff/src/preproc/eqn/eqn_tab.h67
-rw-r--r--contrib/groff/src/preproc/eqn/lex.cc1165
-rw-r--r--contrib/groff/src/preproc/eqn/limit.cc195
-rw-r--r--contrib/groff/src/preproc/eqn/list.cc237
-rw-r--r--contrib/groff/src/preproc/eqn/main.cc419
-rw-r--r--contrib/groff/src/preproc/eqn/mark.cc121
-rw-r--r--contrib/groff/src/preproc/eqn/neqn.man21
-rw-r--r--contrib/groff/src/preproc/eqn/other.cc601
-rw-r--r--contrib/groff/src/preproc/eqn/over.cc196
-rw-r--r--contrib/groff/src/preproc/eqn/pbox.h141
-rw-r--r--contrib/groff/src/preproc/eqn/pile.cc293
-rw-r--r--contrib/groff/src/preproc/eqn/script.cc221
-rw-r--r--contrib/groff/src/preproc/eqn/special.cc115
-rw-r--r--contrib/groff/src/preproc/eqn/sqrt.cc179
-rw-r--r--contrib/groff/src/preproc/eqn/text.cc528
-rw-r--r--contrib/groff/src/preproc/grn/Makefile.sub17
-rw-r--r--contrib/groff/src/preproc/grn/README60
-rw-r--r--contrib/groff/src/preproc/grn/gprint.h84
-rw-r--r--contrib/groff/src/preproc/grn/grn.man636
-rw-r--r--contrib/groff/src/preproc/grn/hdb.cc326
-rw-r--r--contrib/groff/src/preproc/grn/hgraph.cc1043
-rw-r--r--contrib/groff/src/preproc/grn/hpoint.cc49
-rw-r--r--contrib/groff/src/preproc/grn/main.cc905
-rw-r--r--contrib/groff/src/preproc/html/Makefile.sub7
-rw-r--r--contrib/groff/src/preproc/html/pre-html.cc1160
-rw-r--r--contrib/groff/src/preproc/html/pre-html.h37
-rw-r--r--contrib/groff/src/preproc/html/pushbackbuffer.cc337
-rw-r--r--contrib/groff/src/preproc/html/pushbackbuffer.h54
-rw-r--r--contrib/groff/src/preproc/pic/Makefile.sub31
-rw-r--r--contrib/groff/src/preproc/pic/TODO37
-rw-r--r--contrib/groff/src/preproc/pic/common.cc497
-rw-r--r--contrib/groff/src/preproc/pic/common.h70
-rw-r--r--contrib/groff/src/preproc/pic/lex.cc1940
-rw-r--r--contrib/groff/src/preproc/pic/main.cc635
-rw-r--r--contrib/groff/src/preproc/pic/object.cc1833
-rw-r--r--contrib/groff/src/preproc/pic/object.h217
-rw-r--r--contrib/groff/src/preproc/pic/output.h79
-rw-r--r--contrib/groff/src/preproc/pic/pic.cc5216
-rw-r--r--contrib/groff/src/preproc/pic/pic.h104
-rw-r--r--contrib/groff/src/preproc/pic/pic.man883
-rw-r--r--contrib/groff/src/preproc/pic/pic.y1812
-rw-r--r--contrib/groff/src/preproc/pic/pic_tab.h131
-rw-r--r--contrib/groff/src/preproc/pic/position.h47
-rw-r--r--contrib/groff/src/preproc/pic/tex.cc412
-rw-r--r--contrib/groff/src/preproc/pic/text.h28
-rw-r--r--contrib/groff/src/preproc/pic/troff.cc504
-rw-r--r--contrib/groff/src/preproc/refer/Makefile.sub23
-rw-r--r--contrib/groff/src/preproc/refer/TODO124
-rw-r--r--contrib/groff/src/preproc/refer/command.cc807
-rw-r--r--contrib/groff/src/preproc/refer/command.h36
-rw-r--r--contrib/groff/src/preproc/refer/label.cc1602
-rw-r--r--contrib/groff/src/preproc/refer/label.y1177
-rw-r--r--contrib/groff/src/preproc/refer/ref.cc1160
-rw-r--r--contrib/groff/src/preproc/refer/ref.h120
-rw-r--r--contrib/groff/src/preproc/refer/refer.cc1234
-rw-r--r--contrib/groff/src/preproc/refer/refer.h78
-rw-r--r--contrib/groff/src/preproc/refer/refer.man1302
-rw-r--r--contrib/groff/src/preproc/refer/token.cc378
-rw-r--r--contrib/groff/src/preproc/refer/token.h88
-rw-r--r--contrib/groff/src/preproc/soelim/Makefile.sub6
-rw-r--r--contrib/groff/src/preproc/soelim/TODO1
-rw-r--r--contrib/groff/src/preproc/soelim/soelim.cc347
-rw-r--r--contrib/groff/src/preproc/soelim/soelim.man85
-rw-r--r--contrib/groff/src/preproc/tbl/Makefile.sub12
-rw-r--r--contrib/groff/src/preproc/tbl/main.cc1528
-rw-r--r--contrib/groff/src/preproc/tbl/table.cc2778
-rw-r--r--contrib/groff/src/preproc/tbl/table.h152
-rw-r--r--contrib/groff/src/preproc/tbl/tbl.man178
-rw-r--r--contrib/groff/src/roff/groff/Makefile.sub8
-rw-r--r--contrib/groff/src/roff/groff/groff.cc731
-rw-r--r--contrib/groff/src/roff/groff/pipeline.c411
-rw-r--r--contrib/groff/src/roff/groff/pipeline.h30
-rw-r--r--contrib/groff/src/roff/grog/Makefile.sub26
-rw-r--r--contrib/groff/src/roff/grog/grog.man86
-rw-r--r--contrib/groff/src/roff/grog/grog.pl183
-rw-r--r--contrib/groff/src/roff/grog/grog.sh91
-rw-r--r--contrib/groff/src/roff/nroff/Makefile.sub20
-rw-r--r--contrib/groff/src/roff/troff/Makefile.sub48
-rw-r--r--contrib/groff/src/roff/troff/TODO134
-rw-r--r--contrib/groff/src/roff/troff/charinfo.h171
-rw-r--r--contrib/groff/src/roff/troff/column.cc732
-rw-r--r--contrib/groff/src/roff/troff/dictionary.cc212
-rw-r--r--contrib/groff/src/roff/troff/dictionary.h92
-rw-r--r--contrib/groff/src/roff/troff/div.cc1161
-rw-r--r--contrib/groff/src/roff/troff/div.h156
-rw-r--r--contrib/groff/src/roff/troff/env.cc3439
-rw-r--r--contrib/groff/src/roff/troff/env.h350
-rw-r--r--contrib/groff/src/roff/troff/hvunits.h340
-rw-r--r--contrib/groff/src/roff/troff/input.cc6891
-rw-r--r--contrib/groff/src/roff/troff/input.h92
-rw-r--r--contrib/groff/src/roff/troff/node.cc5621
-rw-r--r--contrib/groff/src/roff/troff/node.h584
-rw-r--r--contrib/groff/src/roff/troff/number.cc692
-rw-r--r--contrib/groff/src/roff/troff/reg.cc473
-rw-r--r--contrib/groff/src/roff/troff/reg.h76
-rw-r--r--contrib/groff/src/roff/troff/request.h84
-rw-r--r--contrib/groff/src/roff/troff/symbol.cc150
-rw-r--r--contrib/groff/src/roff/troff/symbol.h73
-rw-r--r--contrib/groff/src/roff/troff/token.h218
-rw-r--r--contrib/groff/src/roff/troff/troff.h88
-rw-r--r--contrib/groff/src/roff/troff/troff.man2462
-rw-r--r--contrib/groff/src/utils/addftinfo/Makefile.sub11
-rw-r--r--contrib/groff/src/utils/addftinfo/addftinfo.cc218
-rw-r--r--contrib/groff/src/utils/addftinfo/addftinfo.man107
-rw-r--r--contrib/groff/src/utils/addftinfo/guess.cc490
-rw-r--r--contrib/groff/src/utils/addftinfo/guess.h44
-rw-r--r--contrib/groff/src/utils/afmtodit/Makefile.sub23
-rw-r--r--contrib/groff/src/utils/afmtodit/afmtodit.man225
-rw-r--r--contrib/groff/src/utils/afmtodit/afmtodit.pl331
-rw-r--r--contrib/groff/src/utils/hpftodit/Makefile.sub6
-rw-r--r--contrib/groff/src/utils/hpftodit/hpftodit.cc827
-rw-r--r--contrib/groff/src/utils/hpftodit/hpftodit.man155
-rw-r--r--contrib/groff/src/utils/indxbib/Makefile.sub31
-rw-r--r--contrib/groff/src/utils/indxbib/dirnamemax.c49
-rw-r--r--contrib/groff/src/utils/indxbib/eign133
-rw-r--r--contrib/groff/src/utils/indxbib/indxbib.cc805
-rw-r--r--contrib/groff/src/utils/indxbib/indxbib.man207
-rw-r--r--contrib/groff/src/utils/indxbib/signal.c63
-rw-r--r--contrib/groff/src/utils/lkbib/Makefile.sub6
-rw-r--r--contrib/groff/src/utils/lkbib/lkbib.cc137
-rw-r--r--contrib/groff/src/utils/lkbib/lkbib.man110
-rw-r--r--contrib/groff/src/utils/lookbib/Makefile.sub7
-rw-r--r--contrib/groff/src/utils/lookbib/lookbib.cc140
-rw-r--r--contrib/groff/src/utils/lookbib/lookbib.man78
-rw-r--r--contrib/groff/src/utils/pfbtops/Makefile.sub6
-rw-r--r--contrib/groff/src/utils/pfbtops/pfbtops.c131
-rw-r--r--contrib/groff/src/utils/pfbtops/pfbtops.man44
-rw-r--r--contrib/groff/src/utils/tfmtodit/Makefile.sub6
-rw-r--r--contrib/groff/src/utils/tfmtodit/tfmtodit.cc874
-rw-r--r--contrib/groff/src/utils/tfmtodit/tfmtodit.man175
-rw-r--r--contrib/groff/src/xditview/ChangeLog413
-rw-r--r--contrib/groff/src/xditview/DESC9
-rw-r--r--contrib/groff/src/xditview/Dvi.c573
-rw-r--r--contrib/groff/src/xditview/Dvi.h46
-rw-r--r--contrib/groff/src/xditview/DviChar.c664
-rw-r--r--contrib/groff/src/xditview/DviChar.h37
-rw-r--r--contrib/groff/src/xditview/DviP.h233
-rw-r--r--contrib/groff/src/xditview/FontMap17
-rw-r--r--contrib/groff/src/xditview/GXditview-ad.h52
-rw-r--r--contrib/groff/src/xditview/GXditview.ad57
-rw-r--r--contrib/groff/src/xditview/INSTALL20
-rw-r--r--contrib/groff/src/xditview/Imakefile.in104
-rw-r--r--contrib/groff/src/xditview/Menu.h46
-rw-r--r--contrib/groff/src/xditview/README14
-rw-r--r--contrib/groff/src/xditview/TODO17
-rw-r--r--contrib/groff/src/xditview/XFontName.c256
-rw-r--r--contrib/groff/src/xditview/XFontName.h45
-rw-r--r--contrib/groff/src/xditview/ad2c62
-rw-r--r--contrib/groff/src/xditview/device.c600
-rw-r--r--contrib/groff/src/xditview/device.h21
-rw-r--r--contrib/groff/src/xditview/draw.c721
-rw-r--r--contrib/groff/src/xditview/font.c471
-rw-r--r--contrib/groff/src/xditview/gray1.bm4
-rw-r--r--contrib/groff/src/xditview/gray2.bm4
-rw-r--r--contrib/groff/src/xditview/gray3.bm4
-rw-r--r--contrib/groff/src/xditview/gray4.bm4
-rw-r--r--contrib/groff/src/xditview/gray5.bm4
-rw-r--r--contrib/groff/src/xditview/gray6.bm4
-rw-r--r--contrib/groff/src/xditview/gray7.bm4
-rw-r--r--contrib/groff/src/xditview/gray8.bm4
-rw-r--r--contrib/groff/src/xditview/gxditview.man246
-rw-r--r--contrib/groff/src/xditview/lex.c103
-rw-r--r--contrib/groff/src/xditview/page.c88
-rw-r--r--contrib/groff/src/xditview/parse.c335
-rw-r--r--contrib/groff/src/xditview/xdit.bm14
-rw-r--r--contrib/groff/src/xditview/xdit_mask.bm14
-rw-r--r--contrib/groff/src/xditview/xditview.c594
-rw-r--r--contrib/groff/src/xditview/xtotroff.c311
272 files changed, 99198 insertions, 0 deletions
diff --git a/contrib/groff/src/devices/grodvi/Makefile.sub b/contrib/groff/src/devices/grodvi/Makefile.sub
new file mode 100644
index 000000000000..0e5d32cabbd5
--- /dev/null
+++ b/contrib/groff/src/devices/grodvi/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=grodvi
+MAN1=grodvi.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=dvi.o
+CCSRCS=$(srcdir)/dvi.cc
diff --git a/contrib/groff/src/devices/grodvi/dvi.cc b/contrib/groff/src/devices/grodvi/dvi.cc
new file mode 100644
index 000000000000..74422f804e24
--- /dev/null
+++ b/contrib/groff/src/devices/grodvi/dvi.cc
@@ -0,0 +1,912 @@
+// -*- 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 "driver.h"
+#include "nonposix.h"
+
+#define DEFAULT_LINEWIDTH 40
+static int linewidth = DEFAULT_LINEWIDTH;
+
+static int draw_flag = 1;
+
+/* These values were chosen because:
+
+(MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
+
+and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
+
+The width in the groff font file is the product of MULTIPLIER and the
+width in the tfm file. */
+
+#define RES 57816
+#define RES_7227 (RES/7227)
+#define UNITWIDTH 131072
+#define SIZESCALE 100
+#define MULTIPLIER 1
+
+#define FILL_MAX 1000
+
+class dvi_font : public font {
+ dvi_font(const char *);
+public:
+ int checksum;
+ int design_size;
+ ~dvi_font();
+ void handle_unknown_font_command(const char *command, const char *arg,
+ const char *filename, int lineno);
+ static dvi_font *load_dvi_font(const char *);
+};
+
+dvi_font *dvi_font::load_dvi_font(const char *s)
+{
+ dvi_font *f = new dvi_font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+dvi_font::dvi_font(const char *nm)
+: font(nm), checksum(0), design_size(0)
+{
+}
+
+dvi_font::~dvi_font()
+{
+}
+
+void dvi_font::handle_unknown_font_command(const char *command,
+ const char *arg,
+ const char *filename, int lineno)
+{
+ char *ptr;
+ if (strcmp(command, "checksum") == 0) {
+ if (arg == 0)
+ fatal_with_file_and_line(filename, lineno,
+ "`checksum' command requires an argument");
+ checksum = int(strtol(arg, &ptr, 10));
+ if (checksum == 0 && ptr == arg) {
+ fatal_with_file_and_line(filename, lineno, "bad checksum");
+ }
+ }
+ else if (strcmp(command, "designsize") == 0) {
+ if (arg == 0)
+ fatal_with_file_and_line(filename, lineno,
+ "`designsize' command requires an argument");
+ design_size = int(strtol(arg, &ptr, 10));
+ if (design_size == 0 && ptr == arg) {
+ fatal_with_file_and_line(filename, lineno, "bad design size");
+ }
+ }
+}
+
+#define FONTS_MAX 256
+
+struct output_font {
+ dvi_font *f;
+ int point_size;
+ output_font() : f(0) { }
+};
+
+class dvi_printer : public printer {
+ FILE *fp;
+ int max_drift;
+ int byte_count;
+ int last_bop;
+ int page_count;
+ int cur_h;
+ int cur_v;
+ int end_h;
+ int max_h;
+ int max_v;
+ output_font output_font_table[FONTS_MAX];
+ font *cur_font;
+ int cur_point_size;
+ int pushed;
+ int pushed_h;
+ int pushed_v;
+ int have_pushed;
+ void preamble();
+ void postamble();
+ void define_font(int);
+ void set_font(int);
+ void possibly_begin_line();
+protected:
+ enum {
+ id_byte = 2,
+ set1 = 128,
+ put1 = 133,
+ put_rule = 137,
+ bop = 139,
+ eop = 140,
+ push = 141,
+ pop = 142,
+ right1 = 143,
+ down1 = 157,
+ fnt_num_0 = 171,
+ fnt1 = 235,
+ xxx1 = 239,
+ fnt_def1 = 243,
+ pre = 247,
+ post = 248,
+ post_post = 249,
+ filler = 223
+ };
+ int line_thickness;
+
+ void out1(int);
+ void out2(int);
+ void out3(int);
+ void out4(int);
+ void moveto(int, int);
+ void out_string(const char *);
+ void out_signed(unsigned char, int);
+ void out_unsigned(unsigned char, int);
+ void do_special(const char *);
+public:
+ dvi_printer();
+ ~dvi_printer();
+ font *make_font(const char *);
+ void begin_page(int);
+ void end_page(int);
+ void set_char(int, font *, const environment *, int w, const char *name);
+ void special(char *arg, const environment *env, char type);
+ void end_of_line();
+ void draw(int code, int *p, int np, const environment *env);
+};
+
+
+class draw_dvi_printer : public dvi_printer {
+ int output_pen_size;
+ int fill;
+ void set_line_thickness(const environment *);
+ void fill_next();
+public:
+ draw_dvi_printer();
+ ~draw_dvi_printer();
+ void draw(int code, int *p, int np, const environment *env);
+ void end_page(int);
+};
+
+dvi_printer::dvi_printer()
+: fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
+ cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
+{
+ if (font::res != RES)
+ fatal("resolution must be %1", RES);
+ if (font::unitwidth != UNITWIDTH)
+ fatal("unitwidth must be %1", UNITWIDTH);
+ if (font::hor != 1)
+ fatal("hor must be equal to 1");
+ if (font::vert != 1)
+ fatal("vert must be equal to 1");
+ if (font::sizescale != SIZESCALE)
+ fatal("sizescale must be equal to %1", SIZESCALE);
+ max_drift = font::res/1000; // this is fairly arbitrary
+ preamble();
+}
+
+dvi_printer::~dvi_printer()
+{
+ postamble();
+}
+
+
+draw_dvi_printer::draw_dvi_printer()
+: output_pen_size(-1), fill(FILL_MAX)
+{
+}
+
+draw_dvi_printer::~draw_dvi_printer()
+{
+}
+
+
+void dvi_printer::out1(int n)
+{
+ byte_count += 1;
+ putc(n & 0xff, fp);
+}
+
+void dvi_printer::out2(int n)
+{
+ byte_count += 2;
+ putc((n >> 8) & 0xff, fp);
+ putc(n & 0xff, fp);
+}
+
+void dvi_printer::out3(int n)
+{
+ byte_count += 3;
+ putc((n >> 16) & 0xff, fp);
+ putc((n >> 8) & 0xff, fp);
+ putc(n & 0xff, fp);
+}
+
+void dvi_printer::out4(int n)
+{
+ byte_count += 4;
+ putc((n >> 24) & 0xff, fp);
+ putc((n >> 16) & 0xff, fp);
+ putc((n >> 8) & 0xff, fp);
+ putc(n & 0xff, fp);
+}
+
+void dvi_printer::out_string(const char *s)
+{
+ out1(strlen(s));
+ while (*s != 0)
+ out1(*s++);
+}
+
+
+void dvi_printer::end_of_line()
+{
+ if (pushed) {
+ out1(pop);
+ pushed = 0;
+ cur_h = pushed_h;
+ cur_v = pushed_v;
+ }
+}
+
+void dvi_printer::possibly_begin_line()
+{
+ if (!pushed) {
+ have_pushed = pushed = 1;
+ pushed_h = cur_h;
+ pushed_v = cur_v;
+ out1(push);
+ }
+}
+
+int scale(int x, int z)
+{
+ int sw;
+ int a, b, c, d;
+ int alpha, beta;
+ alpha = 16*z; beta = 16;
+ while (z >= 040000000L) {
+ z /= 2; beta /= 2;
+ }
+ d = x & 255;
+ c = (x >> 8) & 255;
+ b = (x >> 16) & 255;
+ a = (x >> 24) & 255;
+ sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
+ if (a == 255)
+ sw -= alpha;
+ else
+ assert(a == 0);
+ return sw;
+}
+
+
+void dvi_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
+{
+ int code = f->get_code(index);
+ if (env->size != cur_point_size || f != cur_font) {
+ cur_font = f;
+ cur_point_size = env->size;
+ int i;
+ for (i = 0;; i++) {
+ if (i >= FONTS_MAX) {
+ fatal("too many output fonts required");
+ }
+ if (output_font_table[i].f == 0) {
+ output_font_table[i].f = (dvi_font *)cur_font;
+ output_font_table[i].point_size = cur_point_size;
+ define_font(i);
+ }
+ if (output_font_table[i].f == cur_font
+ && output_font_table[i].point_size == cur_point_size)
+ break;
+ }
+ set_font(i);
+ }
+ int distance = env->hpos - cur_h;
+ if (env->hpos != end_h && distance != 0) {
+ out_signed(right1, distance);
+ cur_h = env->hpos;
+ }
+ else if (distance > max_drift) {
+ out_signed(right1, distance - max_drift);
+ cur_h = env->hpos - max_drift;
+ }
+ else if (distance < -max_drift) {
+ out_signed(right1, distance + max_drift);
+ cur_h = env->hpos + max_drift;
+ }
+ if (env->vpos != cur_v) {
+ out_signed(down1, env->vpos - cur_v);
+ cur_v = env->vpos;
+ }
+ possibly_begin_line();
+ end_h = env->hpos + w;
+ cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER,
+ cur_point_size*RES_7227);
+ if (cur_h > max_h)
+ max_h = cur_h;
+ if (cur_v > max_v)
+ max_v = cur_v;
+ if (code >= 0 && code <= 127)
+ out1(code);
+ else
+ out_unsigned(set1, code);
+}
+
+void dvi_printer::define_font(int i)
+{
+ out_unsigned(fnt_def1, i);
+ dvi_font *f = output_font_table[i].f;
+ out4(f->checksum);
+ out4(output_font_table[i].point_size*RES_7227);
+ out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
+ const char *nm = f->get_internal_name();
+ out1(0);
+ out_string(nm);
+}
+
+void dvi_printer::set_font(int i)
+{
+ if (i >= 0 && i <= 63)
+ out1(fnt_num_0 + i);
+ else
+ out_unsigned(fnt1, i);
+}
+
+void dvi_printer::out_signed(unsigned char base, int param)
+{
+ if (-128 <= param && param < 128) {
+ out1(base);
+ out1(param);
+ }
+ else if (-32768 <= param && param < 32768) {
+ out1(base+1);
+ out2(param);
+ }
+ else if (-(1 << 23) <= param && param < (1 << 23)) {
+ out1(base+2);
+ out3(param);
+ }
+ else {
+ out1(base+3);
+ out4(param);
+ }
+}
+
+void dvi_printer::out_unsigned(unsigned char base, int param)
+{
+ if (param >= 0) {
+ if (param < 256) {
+ out1(base);
+ out1(param);
+ }
+ else if (param < 65536) {
+ out1(base+1);
+ out2(param);
+ }
+ else if (param < (1 << 24)) {
+ out1(base+2);
+ out3(param);
+ }
+ else {
+ out1(base+3);
+ out4(param);
+ }
+ }
+ else {
+ out1(base+3);
+ out4(param);
+ }
+}
+
+void dvi_printer::preamble()
+{
+ out1(pre);
+ out1(id_byte);
+ out4(254000);
+ out4(font::res);
+ out4(1000);
+ out1(0);
+}
+
+void dvi_printer::postamble()
+{
+ int tem = byte_count;
+ out1(post);
+ out4(last_bop);
+ out4(254000);
+ out4(font::res);
+ out4(1000);
+ out4(max_v);
+ out4(max_h);
+ out2(have_pushed); // stack depth
+ out2(page_count);
+ int i;
+ for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
+ define_font(i);
+ out1(post_post);
+ out4(tem);
+ out1(id_byte);
+ for (i = 0; i < 4 || byte_count % 4 != 0; i++)
+ out1(filler);
+}
+
+void dvi_printer::begin_page(int i)
+{
+ page_count++;
+ int tem = byte_count;
+ out1(bop);
+ out4(i);
+ for (int j = 1; j < 10; j++)
+ out4(0);
+ out4(last_bop);
+ last_bop = tem;
+ // By convention position (0,0) in a dvi file is placed at (1in, 1in).
+ cur_h = font::res;
+ cur_v = font::res;
+ end_h = 0;
+}
+
+void dvi_printer::end_page(int)
+{
+ if (pushed)
+ end_of_line();
+ out1(eop);
+ cur_font = 0;
+}
+
+void draw_dvi_printer::end_page(int len)
+{
+ dvi_printer::end_page(len);
+ output_pen_size = -1;
+}
+
+void dvi_printer::do_special(const char *s)
+{
+ int len = strlen(s);
+ if (len == 0)
+ return;
+ possibly_begin_line();
+ out_unsigned(xxx1, len);
+ while (*s)
+ out1(*s++);
+}
+
+void dvi_printer::special(char *arg, const environment *env, char type)
+{
+ if (type != 'p')
+ return;
+ moveto(env->hpos, env->vpos);
+ do_special(arg);
+}
+
+void dvi_printer::moveto(int h, int v)
+{
+ if (h != cur_h) {
+ out_signed(right1, h - cur_h);
+ cur_h = h;
+ if (cur_h > max_h)
+ max_h = cur_h;
+ }
+ if (v != cur_v) {
+ out_signed(down1, v - cur_v);
+ cur_v = v;
+ if (cur_v > max_v)
+ max_v = cur_v;
+ }
+ end_h = 0;
+}
+
+void dvi_printer::draw(int code, int *p, int np, const environment *env)
+{
+ if (code == 'l') {
+ int x, y;
+ int height = 0, width;
+ int thickness;
+ if (line_thickness < 0)
+ thickness = env->size*RES_7227*linewidth/1000;
+ else if (line_thickness > 0)
+ thickness = line_thickness;
+ else
+ thickness = 1;
+ if (np != 2) {
+ error("2 arguments required for line");
+ }
+ else if (p[0] == 0) {
+ // vertical rule
+ if (p[1] > 0) {
+ x = env->hpos - thickness/2;
+ y = env->vpos + p[1] + thickness/2;
+ height = p[1] + thickness;
+ width = thickness;
+ }
+ else if (p[1] < 0) {
+ x = env->hpos - thickness/2;
+ y = env->vpos + thickness/2;
+ height = thickness - p[1];
+ width = thickness;
+ }
+ }
+ else if (p[1] == 0) {
+ if (p[0] > 0) {
+ x = env->hpos - thickness/2;
+ y = env->vpos + thickness/2;
+ height = thickness;
+ width = p[0] + thickness;
+ }
+ else if (p[0] < 0) {
+ x = env->hpos - p[0] - thickness/2;
+ y = env->vpos + thickness/2;
+ height = thickness;
+ width = thickness - p[0];
+ }
+ }
+ if (height != 0) {
+ moveto(x, y);
+ out1(put_rule);
+ out4(height);
+ out4(width);
+ }
+ }
+ else if (code == 't') {
+ if (np == 0) {
+ line_thickness = -1;
+ }
+ else {
+ // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2)
+ error("0 or 1 argument required for thickness");
+ else
+ line_thickness = p[0];
+ }
+ }
+ else if (code == 'R') {
+ if (np != 2)
+ error("2 arguments required for rule");
+ else if (p[0] != 0 || p[1] != 0) {
+ int dh = p[0];
+ int dv = p[1];
+ int oh = env->hpos;
+ int ov = env->vpos;
+ if (dv > 0) {
+ ov += dv;
+ dv = -dv;
+ }
+ if (dh < 0) {
+ oh += dh;
+ dh = -dh;
+ }
+ moveto(oh, ov);
+ out1(put_rule);
+ out4(-dv);
+ out4(dh);
+ }
+ }
+}
+
+// XXX Will this overflow?
+
+inline int milliinches(int n)
+{
+ return (n*1000 + font::res/2)/font::res;
+}
+
+void draw_dvi_printer::set_line_thickness(const environment *env)
+{
+ int desired_pen_size
+ = milliinches(line_thickness < 0
+ // Will this overflow?
+ ? env->size*RES_7227*linewidth/1000
+ : line_thickness);
+ if (desired_pen_size != output_pen_size) {
+ char buf[256];
+ sprintf(buf, "pn %d", desired_pen_size);
+ do_special(buf);
+ output_pen_size = desired_pen_size;
+ }
+}
+
+void draw_dvi_printer::fill_next()
+{
+ char buf[256];
+ sprintf(buf, "sh %.3f", double(fill)/FILL_MAX);
+ do_special(buf);
+}
+
+void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
+{
+ char buf[1024];
+ int fill_flag = 0;
+ switch (code) {
+ case 'C':
+ fill_flag = 1;
+ // fall through
+ case 'c':
+ // troff adds an extra argument to C
+ if (np != 1 && !(code == 'C' && np == 2)) {
+ error("1 argument required for circle");
+ break;
+ }
+ moveto(env->hpos+p[0]/2, env->vpos);
+ if (fill_flag)
+ fill_next();
+ else
+ set_line_thickness(env);
+ int rad;
+ rad = milliinches(p[0]/2);
+ sprintf(buf, "%s 0 0 %d %d 0 6.28319",
+ (fill_flag ? "ia" : "ar"),
+ rad,
+ rad);
+ do_special(buf);
+ break;
+ case 'l':
+ if (np != 2) {
+ error("2 arguments required for line");
+ break;
+ }
+ moveto(env->hpos, env->vpos);
+ set_line_thickness(env);
+ do_special("pa 0 0");
+ sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
+ do_special(buf);
+ do_special("fp");
+ break;
+ case 'E':
+ fill_flag = 1;
+ // fall through
+ case 'e':
+ if (np != 2) {
+ error("2 arguments required for ellipse");
+ break;
+ }
+ moveto(env->hpos+p[0]/2, env->vpos);
+ if (fill_flag)
+ fill_next();
+ sprintf(buf, "%s 0 0 %d %d 0 6.28319",
+ (fill_flag ? "ia" : "ar"),
+ milliinches(p[0]/2),
+ milliinches(p[1]/2));
+ do_special(buf);
+ break;
+ case 'P':
+ fill_flag = 1;
+ // fall through
+ case 'p':
+ {
+ if (np & 1) {
+ error("even number of arguments required for polygon");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for polygon");
+ break;
+ }
+ moveto(env->hpos, env->vpos);
+ if (fill_flag)
+ fill_next();
+ else
+ set_line_thickness(env);
+ do_special("pa 0 0");
+ int h = 0, v = 0;
+ for (int i = 0; i < np; i += 2) {
+ h += p[i];
+ v += p[i+1];
+ sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
+ do_special(buf);
+ }
+ do_special("pa 0 0");
+ do_special(fill_flag ? "ip" : "fp");
+ break;
+ }
+ case '~':
+ {
+ if (np & 1) {
+ error("even number of arguments required for spline");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for spline");
+ break;
+ }
+ moveto(env->hpos, env->vpos);
+ set_line_thickness(env);
+ do_special("pa 0 0");
+ int h = 0, v = 0;
+ for (int i = 0; i < np; i += 2) {
+ h += p[i];
+ v += p[i+1];
+ sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
+ do_special(buf);
+ }
+ do_special("sp");
+ break;
+ }
+ case 'a':
+ {
+ if (np != 4) {
+ error("4 arguments required for arc");
+ break;
+ }
+ set_line_thickness(env);
+ double c[2];
+ if (adjust_arc_center(p, c)) {
+ int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
+ moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
+ sprintf(buf, "ar 0 0 %d %d %f %f",
+ rad,
+ rad,
+ atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]),
+ atan2(-c[1], -c[0]));
+ do_special(buf);
+ }
+ else {
+ moveto(env->hpos, env->vpos);
+ do_special("pa 0 0");
+ sprintf(buf,
+ "pa %d %d",
+ milliinches(p[0] + p[2]),
+ milliinches(p[1] + p[3]));
+ do_special(buf);
+ do_special("fp");
+ }
+ break;
+ }
+ case 't':
+ {
+ if (np == 0) {
+ line_thickness = -1;
+ }
+ else {
+ // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2) {
+ error("0 or 1 argument required for thickness");
+ break;
+ }
+ line_thickness = p[0];
+ }
+ break;
+ }
+ case 'f':
+ {
+ if (np != 1 && np != 2) {
+ error("1 argument required for fill");
+ break;
+ }
+ fill = p[0];
+ if (fill < 0 || fill > FILL_MAX)
+ fill = FILL_MAX;
+ break;
+ }
+ case 'R':
+ {
+ if (np != 2) {
+ error("2 arguments required for rule");
+ break;
+ }
+ int dh = p[0];
+ if (dh == 0)
+ break;
+ int dv = p[1];
+ if (dv == 0)
+ break;
+ int oh = env->hpos;
+ int ov = env->vpos;
+ if (dv > 0) {
+ ov += dv;
+ dv = -dv;
+ }
+ if (dh < 0) {
+ oh += dh;
+ dh = -dh;
+ }
+ moveto(oh, ov);
+ out1(put_rule);
+ out4(-dv);
+ out4(dh);
+ break;
+ }
+ default:
+ error("unrecognised drawing command `%1'", char(code));
+ break;
+ }
+}
+
+font *dvi_printer::make_font(const char *nm)
+{
+ return dvi_font::load_dvi_font(nm);
+}
+
+printer *make_printer()
+{
+ if (draw_flag)
+ return new draw_dvi_printer;
+ else
+ return new dvi_printer;
+}
+
+static void usage(FILE *stream);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, "F:vw:d", long_options, NULL)) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU grodvi (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'w':
+ if (sscanf(optarg, "%d", &linewidth) != 1
+ || linewidth < 0 || linewidth > 1000) {
+ error("bad line width");
+ linewidth = DEFAULT_LINEWIDTH;
+ }
+ break;
+ case 'd':
+ draw_flag = 0;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+#ifdef SET_BINARY
+ SET_BINARY(fileno(stdout));
+#endif
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
+ program_name);
+}
diff --git a/contrib/groff/src/devices/grodvi/grodvi.man b/contrib/groff/src/devices/grodvi/grodvi.man
new file mode 100644
index 000000000000..7aa4431609b1
--- /dev/null
+++ b/contrib/groff/src/devices/grodvi/grodvi.man
@@ -0,0 +1,174 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH GRODVI @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grodvi \- convert groff output to TeX dvi format
+.SH SYNOPSIS
+.B grodvi
+[
+.B \-dv
+] [
+.BI \-w n
+] [
+.BI \-F dir
+] [
+.IR files \|.\|.\|.
+]
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B grodvi
+is a driver for
+.B groff
+that produces \*(tx dvi format.
+Normally it should be run by
+.BR groff\ \-Tdvi .
+This will run
+.BR @g@troff\ \-Tdvi ;
+it will also input the macros
+.BR @MACRODIR@/dvi.tmac ;
+if the input is being preprocessed with
+.B @g@eqn
+it will also input
+.BR @FONTDIR@/devdvi/eqnchar .
+.LP
+The dvi file generated by
+.B grodvi
+can be printed by any correctly-written dvi driver.
+The troff drawing primitives are implemented
+using the tpic version 2 specials.
+If the driver does not support these, the
+.B \eD
+commands will not produce any output.
+.LP
+There is an additional drawing command available:
+.TP
+.BI \eD'R\ dh\ dv '
+Draw a rule (solid black rectangle), with one corner
+at the current position, and the diagonally opposite corner
+at the current position
+.RI +( dh , dv ).
+Afterwards the current position will be at the opposite corner. This
+produces a rule in the dvi file and so can be printed even with a
+driver that does not support the tpic specials unlike the other
+.B \eD
+commands.
+.LP
+The groff command
+.BI \eX' anything '
+is translated into the same command in the dvi file as would be
+produced by
+.BI \especial{ anything }
+in \*(tx;
+.I anything may not contain a newline.
+.LP
+Font files for
+.B grodvi
+can be created from tfm files using
+.BR tfmtodit (@MAN1EXT@).
+The font description file should contain the following
+additional commands:
+.Tp \w'\fBinternalname'u+2n
+.BI internalname\ name
+The name of the tfm file (without the
+.B .tfm
+extension) is
+.IR name .
+.TP
+.BI checksum\ n
+The checksum in the tfm file is
+.IR n .
+.TP
+.BI designsize\ n
+The designsize in the tfm file is
+.IR n .
+.LP
+These are automatically generated by
+.B tfmtodit.
+.LP
+In
+.B troff
+the
+.B \eN
+escape sequence can be used to access characters by their position
+in the corresponding tfm file;
+all characters in the tfm file can be accessed this way.
+.SH OPTIONS
+.TP
+.B \-d
+Do not use tpic specials to implement drawing commands.
+Horizontal and vertical lines will be implemented by rules.
+Other drawing commands will be ignored.
+.TP
+.B \-v
+Print the version number.
+.TP
+.BI \-w n
+Set the default line thickness to
+.I n
+thousandths of an em.
+.TP
+.BI \-F dir
+Prepend directory
+.IB dir /devdvi
+to the search path for font and device description files.
+.SH FILES
+.TP
+.B @FONTDIR@/devdvi/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devdvi/ F
+Font description file for font
+.IR F .
+.TP
+.B @MACRODIR@/dvi.tmac
+Macros for use with
+.BR grodvi .
+.SH BUGS
+Dvi files produced by
+.B grodvi
+use a different resolution (57816 units per inch) to those produced by
+\*(tx.
+Incorrectly written drivers which assume the resolution used by \*(tx,
+rather than using the resolution specified in the dvi file will not
+work with
+.BR grodvi .
+.LP
+When using the
+.B \-d
+option with boxed tables,
+vertical and horizontal lines can sometimes protrude by one pixel.
+This is a consequence of the way \*(tx requires that the heights
+and widths of rules be rounded.
+.SH "SEE ALSO"
+.BR tfmtodit (@MAN1EXT@),
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/devices/grohtml/Makefile.sub b/contrib/groff/src/devices/grohtml/Makefile.sub
new file mode 100644
index 000000000000..2c3a55a5d40e
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/Makefile.sub
@@ -0,0 +1,16 @@
+PROG=post-grohtml
+MAN1=grohtml.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ post-html.o \
+ html-text.o \
+ output.o
+CCSRCS=\
+ $(srcdir)/post-html.cc \
+ $(srcdir)/html-text.cc \
+ $(srcdir)/output.cc
+HDRS=\
+ $(srcdir)/html.h \
+ $(srcdir)/html-chars.h \
+ $(srcdir)/html-text.h
diff --git a/contrib/groff/src/devices/grohtml/grohtml.man b/contrib/groff/src/devices/grohtml/grohtml.man
new file mode 100644
index 000000000000..8796d8f8cbc6
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/grohtml.man
@@ -0,0 +1,137 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1999-2000, 2001 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH GROHTML @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grohtml \- html driver for groff
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBgrohtml 'u
+.ti \niu
+.B grohtml
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-v?lrn
+.OP \-F dir
+.OP \-i resolution
+.OP \-o image vertical offset
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.SH DESCRIPTION
+.B grohtml
+translates the output of GNU
+.B troff
+to html.
+Users should always invoke
+.B grohtml
+via the groff command with a
+.B \-Thtml
+option.
+If no files are given,
+.B grohtml
+will read the standard input.
+A filename of
+.B \-
+will also cause
+.B grohtml
+to read the standard input.
+Html output is written to the standard output.
+When
+.B grohtml
+is run by
+.B groff
+options can be passed to
+.B grohtml
+using
+.BR groff 's
+.B \-P
+option.
+.SH OPTIONS
+.TP
+.B \-v
+Displays the version.
+.TP
+.B \-?
+Emits a usage synopsis.
+.TP
+.B -l
+Turns off the production of automatic section links at the top of the document.
+.TP
+.B -r
+Turns off the automatic header and footer line (html rule).
+.TP
+.B -n
+Generate simple heading anchors whenever a section/number heading is found.
+Without the option the anchor value is the textual heading. This can cause problems
+when a heading contains a `?' on some brousers (netscape).
+This flag is automatically turned on if a heading contains an image.
+.TP
+.BI \-F dir
+Prepend directory
+.IB dir /dev name
+to the search path for font and device description files;
+.I name
+is the name of the device, usually
+.BR html .
+.TP
+.BI \-i resolution
+select the resolution for all images.
+By default this is 80 pixels per inch.
+Example: -i100 indicates 100 pixels per inch.
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-?
+Display usage.
+.SH USAGE
+There are styles called
+.BR R ,
+.BR I ,
+.BR B ,
+and
+.B BI
+mounted at font positions 1 to 4.
+.SH DEPENDENCIES
+.B grohtml
+is dependent upon the png utilities and gs.
+Images are generated whenever a table, picture, equation or line is
+encountered.
+.SH BUGS
+.B Grohtml
+has been completely redesigned and rewritten.
+It is still alpha code.
+.SH "SEE ALSO"
+.BR afmtodit (@MAN1EXT@),
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR psbb (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/devices/grohtml/html-chars.h b/contrib/groff/src/devices/grohtml/html-chars.h
new file mode 100644
index 000000000000..f58f8dcc20a8
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/html-chars.h
@@ -0,0 +1,27 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc
+ * but it owes a huge amount of ideas and raw code from
+ * James Clark (jjc@jclark.com) grops/ps.cc.
+ *
+ * html-chars.h
+ *
+ * provides a diacritical character combination table for html
+ */
+
+
+
+struct diacritical_desc {
+ char *mark;
+ char *second_troff_char;
+ char translation;
+};
+
+
+static struct diacritical_desc diacritical_table[] = {
+ { "ad" , "aeiouyAEIOU" , ':' , }, /* */
+ { "ac" , "cC" , ',' , }, /* cedilla */
+ { "aa" , "aeiouyAEIOU" , '\'' , }, /* acute */
+ { NULL , NULL , (char)0, },
+};
diff --git a/contrib/groff/src/devices/grohtml/html-text.cc b/contrib/groff/src/devices/grohtml/html-text.cc
new file mode 100644
index 000000000000..0b63aa0a1b9d
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/html-text.cc
@@ -0,0 +1,829 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc
+ *
+ * html-text.cc
+ *
+ * provide a troff like state machine interface which
+ * generates html text.
+ */
+
+/*
+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 "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#if !defined(TRUE)
+# define TRUE (1==1)
+#endif
+#if !defined(FALSE)
+# define FALSE (1==0)
+#endif
+
+
+#include "html-text.h"
+
+
+html_text::html_text (simple_output *op) :
+ stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE),
+ current_indentation(-1), pageoffset(-1), linelength(-1)
+{
+}
+
+html_text::~html_text ()
+{
+ flush_text();
+}
+
+/*
+ * end_tag - shuts down the tag.
+ */
+
+void html_text::end_tag (tag_definition *t)
+{
+ switch (t->type) {
+
+ case I_TAG: out->put_string("</i>"); break;
+ case B_TAG: out->put_string("</b>"); break;
+ case P_TAG: out->put_string("</p>").nl().enable_newlines(FALSE); break;
+ case SUB_TAG: out->put_string("</sub>"); break;
+ case SUP_TAG: out->put_string("</sup>"); break;
+ case TT_TAG: out->put_string("</tt>"); break;
+ case PRE_TAG: out->put_string("</pre>");
+ if (! is_present(TABLE_TAG)) {
+ out->nl();
+ out->enable_newlines(TRUE);
+ }
+ break;
+ case SMALL_TAG: out->put_string("</small>"); break;
+ case BIG_TAG: out->put_string("</big>"); break;
+ case TABLE_TAG: issue_table_end(); break;
+
+ default:
+ error("unrecognised tag");
+ }
+}
+
+/*
+ * issue_tag - writes out an html tag with argument.
+ */
+
+void html_text::issue_tag (char *tagname, char *arg)
+{
+ if ((arg == 0) || (strlen(arg) == 0)) {
+ out->put_string(tagname);
+ out->put_string(">");
+ } else {
+ out->put_string(tagname);
+ out->put_string(" ");
+ out->put_string(arg);
+ out->put_string(">");
+ }
+}
+
+/*
+ * start_tag - starts a tag.
+ */
+
+void html_text::start_tag (tag_definition *t)
+{
+ switch (t->type) {
+
+ case I_TAG: issue_tag("<i", t->arg1); break;
+ case B_TAG: issue_tag("<b", t->arg1); break;
+ case P_TAG: issue_tag("\n<p", t->arg1);
+ out->enable_newlines(TRUE); break;
+ case SUB_TAG: issue_tag("<sub", t->arg1); break;
+ case SUP_TAG: issue_tag("<sup", t->arg1); break;
+ case TT_TAG: issue_tag("<tt", t->arg1); break;
+ case PRE_TAG: out->nl(); issue_tag("<pre", t->arg1);
+ out->enable_newlines(FALSE); break;
+ case SMALL_TAG: issue_tag("<small", t->arg1); break;
+ case BIG_TAG: issue_tag("<big", t->arg1); break;
+ case TABLE_TAG: issue_table_begin(t); break;
+ case BREAK_TAG: break;
+
+ default:
+ error("unrecognised tag");
+ }
+}
+
+int html_text::table_is_void (tag_definition *t)
+{
+ if (linelength > 0) {
+ return( current_indentation*100/linelength <= 0 );
+ } else {
+ return( FALSE );
+ }
+}
+
+void html_text::issue_table_begin (tag_definition *t)
+{
+ if (linelength > 0) {
+ int width=current_indentation*100/linelength;
+
+ if (width > 0) {
+ out->put_string("<table width=\"100%\" border=0 rules=\"none\" frame=\"void\"\n cols=\"2\" cellspacing=\"0\" cellpadding=\"0\">").nl();
+ out->put_string("<tr valign=\"top\" align=\"left\">").nl();
+ if ((t->arg1 == 0) || (strcmp(t->arg1, "") == 0))
+ out->put_string("<td width=\"").put_number(width).put_string("%\"></td>");
+ else {
+ out->put_string("<td width=\"").put_number(width).put_string("%\">").nl();
+ out->put_string(t->arg1).put_string("</td>");
+ t->arg1[0] = (char)0;
+ }
+ out->put_string("<td width=\"").put_number(100-width).put_string("%\">").nl();
+ }
+ }
+}
+
+void html_text::issue_table_end (void)
+{
+ out->put_string("</td></table>").nl();
+ out->enable_newlines(TRUE);
+}
+
+/*
+ * flush_text - flushes html tags which are outstanding on the html stack.
+ */
+
+void html_text::flush_text (void)
+{
+ int notext=TRUE;
+ tag_definition *p=stackptr;
+
+ while (stackptr != 0) {
+ notext = (notext && (! stackptr->text_emitted));
+ if (! notext) {
+ end_tag(stackptr);
+ }
+ p = stackptr;
+ stackptr = stackptr->next;
+ free(p);
+ }
+ lastptr = NULL;
+}
+
+/*
+ * is_present - returns TRUE if tag is already present on the stack.
+ */
+
+int html_text::is_present (HTML_TAG t)
+{
+ tag_definition *p=stackptr;
+
+ while (p != NULL) {
+ if (t == p->type) {
+ return( TRUE );
+ }
+ p = p->next;
+ }
+ return( FALSE );
+}
+
+/*
+ * push_para - adds a new entry onto the html paragraph stack.
+ */
+
+void html_text::push_para (HTML_TAG t, char *arg)
+{
+ tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition));
+
+ p->type = t;
+ p->arg1 = arg;
+ p->text_emitted = FALSE;
+
+ /*
+ * if t is a P_TAG or TABLE_TAG or PRE_TAG make sure it goes on the end of the stack.
+ * But we insist that a TABLE_TAG is always after a PRE_TAG
+ * and that a P_TAG is always after a TABLE_TAG
+ */
+
+ if (((t == P_TAG) || (t == PRE_TAG) || (t == TABLE_TAG)) &&
+ (lastptr != NULL)) {
+ if (((lastptr->type == TABLE_TAG) && (t == PRE_TAG)) ||
+ ((lastptr->type == P_TAG) && (t == TABLE_TAG))) {
+ /*
+ * insert p before the lastptr
+ */
+ if (stackptr == lastptr) {
+ /*
+ * only on element of the stack
+ */
+ p->next = stackptr;
+ stackptr = p;
+ } else {
+ /*
+ * more than one element is on the stack
+ */
+ tag_definition *q = stackptr;
+
+ while (q->next != lastptr) {
+ q = q->next;
+ }
+ q->next = p;
+ p->next = lastptr;
+ }
+ } else {
+ /*
+ * store, p, at the end
+ */
+ lastptr->next = p;
+ lastptr = p;
+ p->next = NULL;
+ }
+ } else {
+ p->next = stackptr;
+ if (stackptr == NULL)
+ lastptr = p;
+ stackptr = p;
+ }
+}
+
+/*
+ * do_indent - remember the indent parameters and if
+ * indent is > pageoff and indent has changed
+ * then we start a html table to implement the indentation.
+ */
+
+void html_text::do_indent (char *arg, int indent, int pageoff, int linelen)
+{
+ if ((current_indentation != -1) &&
+ (pageoffset+current_indentation != indent+pageoff)) {
+ /*
+ * actual indentation of text has changed, we need to put
+ * a table tag onto the stack.
+ */
+ do_table(arg);
+ }
+ current_indentation = indent;
+ pageoffset = pageoff;
+ linelength = linelen;
+}
+
+void html_text::do_table (char *arg)
+{
+ int in_pre = is_in_pre();
+ // char *para_type = done_para();
+ done_pre();
+ shutdown(TABLE_TAG); // shutdown a previous table, if present
+ remove_break();
+ if (in_pre) {
+ do_pre();
+ }
+ // do_para(para_type);
+ push_para(TABLE_TAG, arg);
+}
+
+/*
+ * done_table - terminates a possibly existing table.
+ */
+
+void html_text::done_table (void)
+{
+ shutdown(TABLE_TAG);
+ space_emitted = TRUE;
+}
+
+/*
+ * do_italic - changes to italic
+ */
+
+void html_text::do_italic (void)
+{
+ done_bold();
+ done_tt();
+ if (! is_present(I_TAG)) {
+ push_para(I_TAG, "");
+ }
+}
+
+/*
+ * do_bold - changes to bold.
+ */
+
+void html_text::do_bold (void)
+{
+ done_italic();
+ done_tt();
+ if (! is_present(B_TAG)) {
+ push_para(B_TAG, "");
+ }
+}
+
+/*
+ * do_tt - changes to teletype.
+ */
+
+void html_text::do_tt (void)
+{
+ done_bold();
+ done_italic();
+ if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG))) {
+ push_para(TT_TAG, "");
+ }
+}
+
+/*
+ * do_pre - changes to preformated text.
+ */
+
+void html_text::do_pre (void)
+{
+ done_bold();
+ done_italic();
+ done_tt();
+ char *type = done_para();
+ if (! is_present(PRE_TAG)) {
+ push_para(PRE_TAG, "");
+ }
+}
+
+/*
+ * is_in_pre - returns TRUE if we are currently within a preformatted
+ * <pre> block.
+ */
+
+int html_text::is_in_pre (void)
+{
+ return( is_present(PRE_TAG) );
+}
+
+/*
+ * is_in_table - returns TRUE if we are currently within a table.
+ */
+
+int html_text::is_in_table (void)
+{
+ return( is_present(TABLE_TAG) );
+}
+
+/*
+ * shutdown - shuts down an html tag.
+ */
+
+char *html_text::shutdown (HTML_TAG t)
+{
+ char *arg=NULL;
+
+ if (is_present(t)) {
+ tag_definition *p =stackptr;
+ tag_definition *temp =NULL;
+ int notext =TRUE;
+
+ while ((stackptr != NULL) && (stackptr->type != t)) {
+ notext = (notext && (! stackptr->text_emitted));
+ if (! notext) {
+ end_tag(stackptr);
+ }
+
+ /*
+ * pop tag
+ */
+ p = stackptr;
+ stackptr = stackptr->next;
+ if (stackptr == NULL)
+ lastptr = NULL;
+
+ /*
+ * push tag onto temp stack
+ */
+ p->next = temp;
+ temp = p;
+ }
+
+ /*
+ * and examine stackptr
+ */
+ if ((stackptr != NULL) && (stackptr->type == t)) {
+ if (stackptr->text_emitted) {
+ end_tag(stackptr);
+ }
+ if (t == P_TAG) {
+ arg = stackptr->arg1;
+ }
+ p = stackptr;
+ stackptr = stackptr->next;
+ if (stackptr == NULL)
+ lastptr = NULL;
+ free(p);
+ }
+
+ /*
+ * and restore unaffected tags
+ */
+ while (temp != NULL) {
+ push_para(temp->type, temp->arg1);
+ p = temp;
+ temp = temp->next;
+ free(p);
+ }
+ }
+ return( arg );
+}
+
+/*
+ * done_bold - shuts downs a bold tag.
+ */
+
+void html_text::done_bold (void)
+{
+ shutdown(B_TAG);
+}
+
+/*
+ * done_italic - shuts downs an italic tag.
+ */
+
+void html_text::done_italic (void)
+{
+ shutdown(I_TAG);
+}
+
+/*
+ * done_sup - shuts downs a sup tag.
+ */
+
+void html_text::done_sup (void)
+{
+ shutdown(SUP_TAG);
+}
+
+/*
+ * done_sub - shuts downs a sub tag.
+ */
+
+void html_text::done_sub (void)
+{
+ shutdown(SUB_TAG);
+}
+
+/*
+ * done_tt - shuts downs a tt tag.
+ */
+
+void html_text::done_tt (void)
+{
+ shutdown(TT_TAG);
+}
+
+/*
+ * done_pre - shuts downs a pre tag.
+ */
+
+void html_text::done_pre (void)
+{
+ shutdown(PRE_TAG);
+}
+
+/*
+ * done_small - shuts downs a small tag.
+ */
+
+void html_text::done_small (void)
+{
+ shutdown(SMALL_TAG);
+}
+
+/*
+ * done_big - shuts downs a big tag.
+ */
+
+void html_text::done_big (void)
+{
+ shutdown(BIG_TAG);
+}
+
+/*
+ * check_emit_text - ensures that all previous tags have been emitted (in order)
+ * before the text is written.
+ */
+
+void html_text::check_emit_text (tag_definition *t)
+{
+ if ((t != NULL) && (! t->text_emitted)) {
+ /*
+ * we peep and see whether there is a <p> before the <table>
+ * in which case we skip the <p>
+ */
+ if (t->type == TABLE_TAG) {
+ if (table_is_void(t)) {
+ tag_definition *n = t->next;
+ remove_def(t);
+ check_emit_text(n);
+ } else {
+ /*
+ * a table which will be emitted, is there a <p> succeeding it?
+ */
+ if ((t->next != NULL) &&
+ (t->next->type == P_TAG) &&
+ ((t->next->arg1 == 0) || strcmp(t->next->arg1, "") == 0)) {
+ /*
+ * yes skip the <p>
+ */
+ check_emit_text(t->next->next);
+ } else {
+ check_emit_text(t->next);
+ }
+ t->text_emitted = TRUE;
+ start_tag(t);
+ }
+ } else {
+ check_emit_text(t->next);
+ t->text_emitted = TRUE;
+ start_tag(t);
+ }
+ }
+}
+
+/*
+ * do_emittext - tells the class that text was written during the current tag.
+ */
+
+void html_text::do_emittext (char *s, int length)
+{
+ if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
+ do_para("");
+
+ if (is_present(BREAK_TAG)) {
+ int text = remove_break();
+ check_emit_text(stackptr);
+ if (text) {
+ if (is_present(PRE_TAG)) {
+ out->nl();
+ } else {
+ out->put_string("<br>").nl();
+ }
+ }
+ } else {
+ check_emit_text(stackptr);
+ }
+ out->put_string(s, length);
+ space_emitted = FALSE;
+}
+
+/*
+ * do_para- starts a new paragraph
+ */
+
+void html_text::do_para (char *arg)
+{
+ done_pre();
+ if (! is_present(P_TAG)) {
+ remove_sub_sup();
+ if ((arg != 0) && (strcmp(arg, "") != 0)) {
+ remove_tag(TABLE_TAG);
+ }
+ push_para(P_TAG, arg);
+ space_emitted = TRUE;
+ }
+}
+
+/*
+ * done_para - shuts down a paragraph tag.
+ */
+
+char *html_text::done_para (void)
+{
+ space_emitted = TRUE;
+ return( shutdown(P_TAG) );
+}
+
+/*
+ * do_space - issues an end of paragraph
+ */
+
+void html_text::do_space (void)
+{
+ if (is_in_pre()) {
+ do_emittext("", 0);
+ } else {
+ do_para(done_para());
+ }
+ space_emitted = TRUE;
+}
+
+/*
+ * do_break - issue a break tag.
+ */
+
+void html_text::do_break (void)
+{
+ if (! is_present(PRE_TAG)) {
+ if (emitted_text()) {
+ if (! is_present(BREAK_TAG)) {
+ push_para(BREAK_TAG, "");
+ }
+ }
+ }
+ space_emitted = TRUE;
+}
+
+/*
+ * do_newline - issue a newline providing that we are inside a <pre> tag.
+ */
+
+void html_text::do_newline (void)
+{
+ if (is_present(PRE_TAG)) {
+ do_emittext("\n", 1);
+ space_emitted = TRUE;
+ }
+}
+
+/*
+ * emitted_text - returns FALSE if white space has just been written.
+ */
+
+int html_text::emitted_text (void)
+{
+ return( ! space_emitted);
+}
+
+/*
+ * emit_space - writes a space providing that text was written beforehand.
+ */
+
+int html_text::emit_space (void)
+{
+ if (space_emitted) {
+ if (is_present(PRE_TAG)) {
+ do_emittext(" ", 1);
+ }
+ } else {
+ out->space_or_newline();
+ space_emitted = TRUE;
+ }
+}
+
+/*
+ * remove_def - removes a definition, t, from the stack.
+ */
+
+void html_text::remove_def (tag_definition *t)
+{
+ tag_definition *p = stackptr;
+ tag_definition *l = 0;
+ tag_definition *q = 0;
+
+ while ((p != 0) && (p != t)) {
+ l = p;
+ p = p->next;
+ }
+ if ((p != 0) && (p == t)) {
+ if (p == stackptr) {
+ stackptr = stackptr->next;
+ if (stackptr == NULL)
+ lastptr = NULL;
+ q = stackptr;
+ } else if (l == 0) {
+ error("stack list pointers are wrong");
+ } else {
+ l->next = p->next;
+ q = p->next;
+ if (l->next == NULL)
+ lastptr = l;
+ }
+ free(p);
+ }
+}
+
+/*
+ * remove_tag - removes a tag from the stack.
+ */
+
+void html_text::remove_tag (HTML_TAG tag)
+{
+ tag_definition *p = stackptr;
+
+ while ((p != 0) && (p->type != tag)) {
+ p = p->next;
+ }
+ if ((p != 0) && (p->type == tag))
+ remove_def(p);
+}
+
+/*
+ * remove_sub_sup - removes a sub or sup tag, should either exist on the stack.
+ */
+
+void html_text::remove_sub_sup (void)
+{
+ if (is_present(SUB_TAG)) {
+ remove_tag(SUB_TAG);
+ }
+ if (is_present(SUP_TAG)) {
+ remove_tag(SUP_TAG);
+ }
+ if (is_present(PRE_TAG)) {
+ remove_tag(PRE_TAG);
+ }
+}
+
+/*
+ * remove_break - break tags are not balanced thus remove it once it has been emitted.
+ * It returns TRUE if text was emitted before the <br> was issued.
+ */
+
+int html_text::remove_break (void)
+{
+ tag_definition *p = stackptr;
+ tag_definition *l = 0;
+ tag_definition *q = 0;
+
+ while ((p != 0) && (p->type != BREAK_TAG)) {
+ l = p;
+ p = p->next;
+ }
+ if ((p != 0) && (p->type == BREAK_TAG)) {
+ if (p == stackptr) {
+ stackptr = stackptr->next;
+ if (stackptr == NULL)
+ lastptr = NULL;
+ q = stackptr;
+ } else if (l == 0) {
+ error("stack list pointers are wrong");
+ } else {
+ l->next = p->next;
+ q = p->next;
+ if (l->next == NULL)
+ lastptr = l;
+ }
+ free(p);
+ }
+ /*
+ * now determine whether text was issued before <br>
+ */
+ while (q != 0) {
+ if (q->text_emitted) {
+ return( TRUE );
+ } else {
+ q = q->next;
+ }
+ }
+ return( FALSE );
+}
+
+/*
+ * do_small - potentially inserts a <small> tag into the html stream.
+ * However we check for a <big> tag, if present then we terminate it.
+ * Otherwise a <small> tag is inserted.
+ */
+
+void html_text::do_small (void)
+{
+ if (is_present(BIG_TAG)) {
+ done_big();
+ } else {
+ push_para(SMALL_TAG, "");
+ }
+}
+
+/*
+ * do_big - is the mirror image of do_small.
+ */
+
+void html_text::do_big (void)
+{
+ if (is_present(SMALL_TAG)) {
+ done_small();
+ } else {
+ push_para(BIG_TAG, "");
+ }
+}
+
+/*
+ * do_sup - save a superscript tag on the stack of tags.
+ */
+
+void html_text::do_sup (void)
+{
+ push_para(SUP_TAG, "");
+}
+
+/*
+ * do_sub - save a subscript tag on the stack of tags.
+ */
+
+void html_text::do_sub (void)
+{
+ push_para(SUB_TAG, "");
+}
+
diff --git a/contrib/groff/src/devices/grohtml/html-text.h b/contrib/groff/src/devices/grohtml/html-text.h
new file mode 100644
index 000000000000..c8ab2ac57ccf
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/html-text.h
@@ -0,0 +1,109 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc
+ *
+ * html-text.h
+ *
+ * provides a state machine interface which generates html text.
+ */
+
+/*
+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 "html.h"
+
+/*
+ * html tags
+ */
+
+typedef enum {I_TAG, B_TAG, P_TAG, SUB_TAG, SUP_TAG, TT_TAG,
+ PRE_TAG, SMALL_TAG, BIG_TAG, BREAK_TAG, TABLE_TAG} HTML_TAG;
+
+typedef struct tag_definition {
+ HTML_TAG type;
+ char *arg1;
+ int text_emitted;
+ tag_definition *next;
+} tag_definition ;
+
+/*
+ * the state of the current paragraph.
+ * It allows post-html.cc to request font changes, paragraph start/end
+ * and emits balanced tags with a small amount of peephole optimization.
+ */
+
+class html_text {
+public:
+ html_text (simple_output *op);
+ ~html_text (void);
+ void flush_text (void);
+ void do_emittext (char *s, int length);
+ void do_italic (void);
+ void do_bold (void);
+ void do_roman (void);
+ void do_tt (void);
+ void do_pre (void);
+ void do_small (void);
+ void do_big (void);
+ void do_para (char *arg1);
+ void do_sup (void);
+ void do_sub (void);
+ void do_space (void);
+ void do_break (void);
+ void do_newline (void);
+ void do_table (char *arg);
+ void done_bold (void);
+ void done_italic (void);
+ char *done_para (void);
+ void done_sup (void);
+ void done_sub (void);
+ void done_tt (void);
+ void done_pre (void);
+ void done_small (void);
+ void done_big (void);
+ void do_indent (char *arg, int indent, int pageoff, int linelen);
+ int emitted_text (void);
+ int emit_space (void);
+ int is_in_pre (void);
+ void remove_tag (HTML_TAG tag);
+ void remove_sub_sup (void);
+ void done_table (void);
+ int is_in_table (void);
+
+private:
+ tag_definition *stackptr; /* the current paragraph state */
+ tag_definition *lastptr; /* the end of the stack */
+ simple_output *out;
+ int space_emitted;
+ int current_indentation; /* current .in value */
+ int pageoffset; /* .po value */
+ int linelength; /* current line length */
+
+ int is_present (HTML_TAG t);
+ void end_tag (tag_definition *t);
+ void start_tag (tag_definition *t);
+ void push_para (HTML_TAG t, char *arg);
+ char *shutdown (HTML_TAG t);
+ void check_emit_text (tag_definition *t);
+ int remove_break (void);
+ void issue_tag (char *tagname, char *arg);
+ void issue_table_begin (tag_definition *t);
+ void issue_table_end (void);
+ int table_is_void (tag_definition *t);
+ void remove_def (tag_definition *t);
+};
diff --git a/contrib/groff/src/devices/grohtml/html.h b/contrib/groff/src/devices/grohtml/html.h
new file mode 100644
index 000000000000..69b6e35eafc2
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/html.h
@@ -0,0 +1,96 @@
+// -*- C++ -*-
+/* Copyright (C) 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. */
+
+#if !defined(HTML_H)
+# define HTML_H
+# undef DEBUGGING
+// # define DEBUGGING
+
+/*
+ * class and structure needed to buffer words
+ */
+
+struct word {
+ char *s;
+ word *next;
+
+ word (const char *w, int n);
+ ~word ();
+};
+
+class word_list {
+public:
+ word_list ();
+ int flush (FILE *f);
+ void add_word (const char *s, int n);
+ int get_length (void);
+
+private:
+ int length;
+ word *head;
+ word *tail;
+};
+
+class simple_output {
+public:
+ simple_output(FILE *, int max_line_length);
+ simple_output &put_string(const char *, int);
+ simple_output &put_string(const char *s);
+ simple_output &put_troffps_char (const char *s);
+ simple_output &put_translated_string(const char *s);
+ simple_output &put_number(int);
+ simple_output &put_float(double);
+ simple_output &put_symbol(const char *);
+ simple_output &put_literal_symbol(const char *);
+ simple_output &set_fixed_point(int);
+ simple_output &simple_comment(const char *);
+ simple_output &begin_comment(const char *);
+ simple_output &comment_arg(const char *);
+ simple_output &end_comment();
+ simple_output &set_file(FILE *);
+ simple_output &include_file(FILE *);
+ simple_output &copy_file(FILE *);
+ simple_output &end_line();
+ simple_output &put_raw_char(char);
+ simple_output &special(const char *);
+ simple_output &enable_newlines(int);
+ simple_output &check_newline(int n);
+ simple_output &nl(void);
+ simple_output &space_or_newline (void);
+ simple_output &begin_tag (void);
+ FILE *get_file();
+private:
+ FILE *fp;
+ int max_line_length; // not including newline
+ int col;
+ int fixed_point;
+ int newlines; // can we issue newlines automatically?
+ word_list last_word;
+
+ void flush_last_word (void);
+ int check_space (const char *s, int n);
+};
+
+inline FILE *simple_output::get_file()
+{
+ return fp;
+}
+
+#endif
diff --git a/contrib/groff/src/devices/grohtml/output.cc b/contrib/groff/src/devices/grohtml/output.cc
new file mode 100644
index 000000000000..4c72bba7d35d
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/output.cc
@@ -0,0 +1,335 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc
+ * but it owes a huge amount of ideas and raw code from
+ * James Clark (jjc@jclark.com) grops/ps.cc.
+ *
+ * output.cc
+ *
+ * provide the simple low level output routines needed by html.cc
+ */
+
+/*
+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 "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#include <time.h>
+#include "html.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !defined(TRUE)
+# define TRUE (1==1)
+#endif
+#if !defined(FALSE)
+# define FALSE (1==0)
+#endif
+
+/*
+ * word - initialise a word and set next to NULL
+ */
+
+word::word (const char *w, int n)
+ : next(0)
+{
+ s = (char *)malloc(n+1);
+ strncpy(s, w, n);
+ s[n] = (char)0;
+}
+
+/*
+ * destroy word and the string copy.
+ */
+
+word::~word ()
+{
+ free(s);
+}
+
+/*
+ * word_list - create an empty word list.
+ */
+
+word_list::word_list ()
+ : head(0), tail(0), length(0)
+{
+}
+
+/*
+ * flush - flush a word list to a FILE, f, and return the
+ * length of the buffered string.
+ */
+
+int word_list::flush (FILE *f)
+{
+ word *t;
+ int len=length;
+
+ while (head != 0) {
+ t = head;
+ head = head->next;
+ fputs(t->s, f);
+ delete t;
+ }
+ head = 0;
+ tail = 0;
+ length = 0;
+#if defined(DEBUGGING)
+ fflush(f); // just for testing
+#endif
+ return( len );
+}
+
+/*
+ * add_word - adds a word to the outstanding word list.
+ */
+
+void word_list::add_word (const char *s, int n)
+{
+ if (head == 0) {
+ head = new word(s, n);
+ tail = head;
+ } else {
+ tail->next = new word(s, n);
+ tail = tail->next;
+ }
+ length += n;
+}
+
+/*
+ * get_length - returns the number of characters buffered
+ */
+
+int word_list::get_length (void)
+{
+ return( length );
+}
+
+/*
+ * the classes and methods for simple_output manipulation
+ */
+
+simple_output::simple_output(FILE *f, int n)
+: fp(f), max_line_length(n), col(0), fixed_point(0), newlines(0)
+{
+}
+
+simple_output &simple_output::set_file(FILE *f)
+{
+ if (fp)
+ fflush(fp);
+ fp = f;
+ return *this;
+}
+
+simple_output &simple_output::copy_file(FILE *infp)
+{
+ int c;
+ while ((c = getc(infp)) != EOF)
+ putc(c, fp);
+ return *this;
+}
+
+simple_output &simple_output::end_line()
+{
+ flush_last_word();
+ if (col != 0) {
+ putc('\n', fp);
+ col = 0;
+ }
+ return *this;
+}
+
+simple_output &simple_output::special(const char *s)
+{
+ return *this;
+}
+
+simple_output &simple_output::simple_comment(const char *s)
+{
+ flush_last_word();
+ if (col != 0)
+ putc('\n', fp);
+ fputs("<!-- ", fp);
+ fputs(s, fp);
+ fputs(" -->\n", fp);
+ col = 0;
+ return *this;
+}
+
+simple_output &simple_output::begin_comment(const char *s)
+{
+ flush_last_word();
+ if (col != 0)
+ putc('\n', fp);
+ col = 0;
+ put_string("<!--");
+ space_or_newline();
+ last_word.add_word(s, strlen(s));
+ return *this;
+}
+
+simple_output &simple_output::end_comment()
+{
+ flush_last_word();
+ space_or_newline();
+ put_string("-->").nl();
+ return *this;
+}
+
+/*
+ * check_newline - checks to see whether we are able to issue
+ * a newline and that one is needed.
+ */
+
+simple_output &simple_output::check_newline(int n)
+{
+ if ((col + n + last_word.get_length() + 1 > max_line_length) && (newlines)) {
+ fputc('\n', fp);
+ col = last_word.flush(fp);
+ }
+}
+
+/*
+ * space_or_newline - will emit a newline or a space later on
+ * depending upon the current column.
+ */
+
+simple_output &simple_output::space_or_newline (void)
+{
+#if defined(DEBUGGING)
+ fflush(fp); // just for testing
+#endif
+ if ((col + last_word.get_length() + 1 > max_line_length) && (newlines)) {
+ fputc('\n', fp);
+ if (last_word.get_length() > 0) {
+ col = last_word.flush(fp);
+ } else {
+ col = 0;
+ }
+ } else {
+ if (last_word.get_length() != 0) {
+ if (col > 0) {
+ fputc(' ', fp);
+ col++;
+ }
+ col += last_word.flush(fp);
+ }
+ }
+}
+
+/*
+ * nl - writes a newline providing that we
+ * are not in the first column.
+ */
+
+simple_output &simple_output::nl (void)
+{
+ space_or_newline();
+ col += last_word.flush(fp);
+ if (col != 0) {
+ fputc('\n', fp);
+ col = 0;
+ }
+ return *this ;
+}
+
+simple_output &simple_output::set_fixed_point(int n)
+{
+ assert(n >= 0 && n <= 10);
+ fixed_point = n;
+ return *this;
+}
+
+simple_output &simple_output::put_raw_char(char c)
+{
+ col += last_word.flush(fp);
+ putc(c, fp);
+ col++;
+ return *this;
+}
+
+simple_output &simple_output::put_string(const char *s, int n)
+{
+ last_word.add_word(s, n);
+ return *this;
+}
+
+simple_output &simple_output::put_string(const char *s)
+{
+ last_word.add_word(s, strlen(s));
+ return *this;
+}
+
+simple_output &simple_output::put_number(int n)
+{
+ char buf[1 + INT_DIGITS + 1];
+ sprintf(buf, "%d", n);
+ put_string(buf);
+ return *this;
+}
+
+simple_output &simple_output::put_float(double d)
+{
+ char buf[128];
+
+ sprintf(buf, "%.4f", d);
+ put_string(buf);
+ return *this;
+}
+
+simple_output &simple_output::enable_newlines (int auto_newlines)
+{
+ check_newline(0);
+ newlines = auto_newlines;
+ check_newline(0);
+}
+
+/*
+ * flush_last_word - flushes the last word and adjusts the
+ * col position. It will insert a newline
+ * before the last word if allowed and if
+ * necessary.
+ */
+
+void simple_output::flush_last_word (void)
+{
+ int len=last_word.get_length();
+
+ if (len > 0) {
+ if (newlines) {
+ if (col + len + 1 > max_line_length) {
+ fputs("\n", fp);
+ col = 0;
+ } else {
+ fputs(" ", fp);
+ col++;
+ }
+ len += last_word.flush(fp);
+ } else {
+ fputs(" ", fp);
+ col++;
+ col += last_word.flush(fp);
+ }
+ }
+}
diff --git a/contrib/groff/src/devices/grohtml/post-html.cc b/contrib/groff/src/devices/grohtml/post-html.cc
new file mode 100644
index 000000000000..0237bfc10aa3
--- /dev/null
+++ b/contrib/groff/src/devices/grohtml/post-html.cc
@@ -0,0 +1,2933 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc
+ * but it owes a huge amount of ideas and raw code from
+ * James Clark (jjc@jclark.com) grops/ps.cc.
+ */
+
+/*
+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 "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "html.h"
+#include "html-chars.h"
+#include "html-text.h"
+
+#include <time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#if !defined(TRUE)
+# define TRUE (1==1)
+#endif
+#if !defined(FALSE)
+# define FALSE (1==0)
+#endif
+
+#define MAX_STRING_LENGTH 4096
+#define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
+#define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
+#define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
+#define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
+#define ANCHOR_TEMPLATE "heading%d" /* if simple anchor is set we use this */
+#define UNICODE_DESC_START 0x80 /* all character entities above this are */
+ /* either encoded by their glyph names or if */
+ /* there is no name then we use &#nnn; */
+#define INDENTATION /* #undef INDENTATION to remove .in handling */
+
+typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
+
+/*
+ * prototypes
+ */
+
+void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single);
+char *get_html_translation (font *f, const char *name);
+
+
+static int auto_links = TRUE; /* by default we enable automatic links at */
+ /* top of the document. */
+static int auto_rule = TRUE; /* by default we enable an automatic rule */
+ /* at the top and bottom of the document */
+static int simple_anchors = FALSE; /* default to anchors with heading text */
+
+
+/*
+ * start with a few favorites
+ */
+
+void stop () {}
+
+static int min (int a, int b)
+{
+ if (a < b) {
+ return( a );
+ } else {
+ return( b );
+ }
+}
+
+static int max (int a, int b)
+{
+ if (a > b) {
+ return( a );
+ } else {
+ return( b );
+ }
+}
+
+/*
+ * is_subsection - returns TRUE if a1..a2 is within b1..b2
+ */
+
+static int is_subsection (int a1, int a2, int b1, int b2)
+{
+ // easier to see whether this is not the case
+ return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) );
+}
+
+/*
+ * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
+ */
+
+static int is_intersection (int a1, int a2, int b1, int b2)
+{
+ // again easier to prove NOT outside limits
+ return( ! ((a1 > b2) || (a2 < b1)) );
+}
+
+/*
+ * is_digit - returns TRUE if character, ch, is a digit.
+ */
+
+static int is_digit (char ch)
+{
+ return( (ch >= '0') && (ch <= '9') );
+}
+
+/*
+ * the classes and methods for maintaining a list of files.
+ */
+
+struct file {
+ FILE *fp;
+ file *next;
+
+ file (FILE *f);
+};
+
+/*
+ * file - initialize all fields to NULL
+ */
+
+file::file (FILE *f)
+ : fp(f), next(0)
+{
+}
+
+class files {
+public:
+ files ();
+ FILE *get_file (void);
+ void start_of_list (void);
+ void move_next (void);
+ void add_new_file (FILE *f);
+private:
+ file *head;
+ file *tail;
+ file *ptr;
+};
+
+/*
+ * files - create an empty list of files.
+ */
+
+files::files ()
+ : head(0), tail(0), ptr(0)
+{
+}
+
+/*
+ * get_file - returns the FILE associated with ptr.
+ */
+
+FILE *files::get_file (void)
+{
+ if (ptr) {
+ return( ptr->fp );
+ } else {
+ return( 0 );
+ }
+}
+
+/*
+ * start_of_list - reset the ptr to the start of the list.
+ */
+
+void files::start_of_list (void)
+{
+ ptr = head;
+}
+
+/*
+ * move_next - moves the ptr to the next element on the list.
+ */
+
+void files::move_next (void)
+{
+ if (ptr != 0)
+ ptr = ptr->next;
+}
+
+/*
+ * add_new_file - adds a new file, f, to the list.
+ */
+
+void files::add_new_file (FILE *f)
+{
+ if (head == 0) {
+ head = new file(f);
+ tail = head;
+ } else {
+ tail->next = new file(f);
+ tail = tail->next;
+ }
+ ptr = tail;
+}
+
+/*
+ * the class and methods for styles
+ */
+
+struct style {
+ font *f;
+ int point_size;
+ int font_no;
+ int height;
+ int slant;
+ style ();
+ style (font *, int, int, int, int);
+ int operator == (const style &) const;
+ int operator != (const style &) const;
+};
+
+style::style()
+ : f(0)
+{
+}
+
+style::style(font *p, int sz, int h, int sl, int no)
+ : f(p), point_size(sz), font_no(no), height(h), slant(sl)
+{
+}
+
+int style::operator==(const style &s) const
+{
+ return (f == s.f && point_size == s.point_size
+ && height == s.height && slant == s.slant);
+}
+
+int style::operator!=(const style &s) const
+{
+ return !(*this == s);
+}
+
+/*
+ * the class and methods for retaining ascii text
+ */
+
+struct char_block {
+ enum { SIZE = 256 };
+ char buffer[SIZE];
+ int used;
+ char_block *next;
+
+ char_block();
+};
+
+char_block::char_block()
+: used(0), next(0)
+{
+}
+
+class char_buffer {
+public:
+ char_buffer();
+ ~char_buffer();
+ char *add_string(char *, unsigned int);
+private:
+ char_block *head;
+ char_block *tail;
+};
+
+char_buffer::char_buffer()
+: head(0), tail(0)
+{
+}
+
+char_buffer::~char_buffer()
+{
+ while (head != 0) {
+ char_block *temp = head;
+ head = head->next;
+ delete temp;
+ }
+}
+
+char *char_buffer::add_string (char *s, unsigned int length)
+{
+ int i=0;
+ unsigned int old_used;
+
+ if (tail == 0) {
+ tail = new char_block;
+ head = tail;
+ } else {
+ if (tail->used + length+1 > char_block::SIZE) {
+ tail->next = new char_block;
+ tail = tail->next;
+ }
+ }
+ // at this point we have a tail which is ready for the string.
+ if (tail->used + length+1 > char_block::SIZE) {
+ fatal("need to increase char_block::SIZE");
+ }
+
+ old_used = tail->used;
+ do {
+ tail->buffer[tail->used] = s[i];
+ tail->used++;
+ i++;
+ length--;
+ } while (length>0);
+
+ // add terminating nul character
+
+ tail->buffer[tail->used] = '\0';
+ tail->used++;
+
+ // and return start of new string
+
+ return( &tail->buffer[old_used] );
+}
+
+/*
+ * the classes and methods for maintaining glyph positions.
+ */
+
+class text_glob {
+public:
+ text_glob (style *s, char *string, unsigned int length,
+ int min_vertical , int min_horizontal,
+ int max_vertical , int max_horizontal,
+ int is_html , int is_troff_command,
+ int is_auto_image,
+ int is_a_line , int thickness);
+ text_glob (void);
+ ~text_glob (void);
+ int is_a_line (void);
+ int is_a_tag (void);
+ int is_raw (void);
+ int is_eol (void);
+ int is_auto_img (void);
+ int is_br (void);
+
+ style text_style;
+ char *text_string;
+ unsigned int text_length;
+ int minv, maxv, minh, maxh;
+ int is_raw_command; // should the text be sent directly to the device?
+ int is_tag; // is this a .br, .sp, .tl etc
+ int is_line; // is the command a <line>?
+ int is_img_auto; // image created by eqn delim
+ int thickness; // the thickness of a line
+};
+
+text_glob::text_glob (style *s, char *string, unsigned int length,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal,
+ int is_html, int is_troff_command,
+ int is_auto_image,
+ int is_a_line, int line_thickness)
+ : text_style(*s), text_string(string), text_length(length),
+ minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
+ is_raw_command(is_html), is_tag(is_troff_command), is_img_auto(is_auto_image),
+ is_line(is_a_line), thickness(line_thickness)
+{
+}
+
+text_glob::text_glob ()
+ : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
+ is_raw_command(FALSE), is_tag(FALSE), is_line(FALSE), thickness(0)
+{
+}
+
+text_glob::~text_glob ()
+{
+}
+
+/*
+ * is_a_line - returns TRUE if glob should be converted into an <hr>
+ */
+
+int text_glob::is_a_line (void)
+{
+ return( is_line );
+}
+
+/*
+ * is_a_tag - returns TRUE if glob contains a troff directive.
+ */
+
+int text_glob::is_a_tag (void)
+{
+ return( is_tag );
+}
+
+/*
+ * is_eol - returns TRUE if glob contains the tag eol
+ */
+
+int text_glob::is_eol (void)
+{
+ return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) );
+}
+
+/*
+ * is_raw - returns TRUE if glob contains raw html.
+ */
+
+int text_glob::is_raw (void)
+{
+ return( is_raw_command );
+}
+
+/*
+ * is_auto_img - returns TRUE if the glob contains an automatically
+ * generated image.
+ */
+
+int text_glob::is_auto_img (void)
+{
+ return( is_img_auto );
+}
+
+/*
+ * is_br - returns TRUE if the glob is a tag containing a .br
+ */
+
+int text_glob::is_br (void)
+{
+ return( is_a_tag() && (strcmp("html-tag:.br", text_string) == 0) );
+}
+
+/*
+ * the class and methods used to construct ordered double linked lists.
+ * In a previous implementation we used templates via #include "ordered-list.h",
+ * but this does assume that all C++ compilers can handle this feature. Pragmatically
+ * it is safer to assume this is not the case.
+ */
+
+struct element_list {
+ element_list *right;
+ element_list *left;
+ text_glob *datum;
+ int lineno;
+ int minv, maxv, minh, maxh;
+
+ element_list (text_glob *d,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ element_list ();
+};
+
+element_list::element_list ()
+ : right(0), left(0), datum(0), lineno(0), minv(-1), maxv(-1), minh(-1), maxh(-1)
+{
+}
+
+/*
+ * element_list - create a list element assigning the datum and region parameters.
+ */
+
+element_list::element_list (text_glob *in,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+ : right(0), left(0), datum(in), lineno(line_number),
+ minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
+{
+}
+
+class list {
+public:
+ list ();
+ ~list ();
+ int is_less (element_list *a, element_list *b);
+ void add (text_glob *in,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void sub_move_right (void);
+ void move_right (void);
+ void move_left (void);
+ int is_empty (void);
+ int is_equal_to_tail (void);
+ int is_equal_to_head (void);
+ void start_from_head (void);
+ void start_from_tail (void);
+ text_glob *move_right_get_data (void);
+ text_glob *move_left_get_data (void);
+ text_glob *get_data (void);
+private:
+ element_list *head;
+ element_list *tail;
+ element_list *ptr;
+};
+
+/*
+ * list - construct an empty list.
+ */
+
+list::list ()
+ : head(0), tail(0), ptr(0)
+{
+}
+
+/*
+ * ~list - destroy a complete list.
+ */
+
+list::~list()
+{
+ element_list *temp=head;
+
+ do {
+ temp = head;
+ if (temp != 0) {
+ head = head->right;
+ delete temp;
+ }
+ } while ((head != 0) && (head != tail));
+}
+
+/*
+ * is_less - returns TRUE if a is left of b if on the same line or
+ * if a is higher up the page than b.
+ */
+
+int list::is_less (element_list *a, element_list *b)
+{
+ // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
+ if (a->lineno < b->lineno) {
+ return( TRUE );
+ } else if (a->lineno > b->lineno) {
+ return( FALSE );
+ } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
+ return( a->minh < b->minh );
+ } else {
+ return( a->maxv < b->maxv );
+ }
+}
+
+/*
+ * add - adds a datum to the list in the order specified by the region position.
+ */
+
+void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
+{
+ // create a new list element with datum and position fields initialized
+ element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
+ element_list *last;
+
+ if (head == 0) {
+ head = t;
+ tail = t;
+ t->left = t;
+ t->right = t;
+ } else {
+ last = tail;
+
+ while ((last != head) && (is_less(t, last))) {
+ last = last->left;
+ }
+
+ if (is_less(t, last)) {
+ t->right = last;
+ last->left->right = t;
+ t->left = last->left;
+ last->left = t;
+ // now check for a new head
+ if (last == head) {
+ head = t;
+ }
+ } else {
+ // add t beyond last
+ t->right = last->right;
+ t->left = last;
+ last->right->left = t;
+ last->right = t;
+ // now check for a new tail
+ if (last == tail) {
+ tail = t;
+ }
+ }
+ }
+}
+
+/*
+ * sub_move_right - removes the element which is currently pointed to by ptr
+ * from the list and moves ptr to the right.
+ */
+
+void list::sub_move_right (void)
+{
+ element_list *t=ptr->right;
+
+ if (head == tail) {
+ head = 0;
+ if (tail != 0) {
+ delete tail;
+ }
+ tail = 0;
+ ptr = 0;
+ } else {
+ if (head == ptr) {
+ head = head->right;
+ }
+ if (tail == ptr) {
+ tail = tail->left;
+ }
+ ptr->left->right = ptr->right;
+ ptr->right->left = ptr->left;
+ ptr=t;
+ }
+}
+
+/*
+ * start_from_head - assigns ptr to the head.
+ */
+
+void list::start_from_head (void)
+{
+ ptr = head;
+}
+
+/*
+ * start_from_tail - assigns ptr to the tail.
+ */
+
+void list::start_from_tail (void)
+{
+ ptr = tail;
+}
+
+/*
+ * is_empty - returns TRUE if the list has no elements.
+ */
+
+int list::is_empty (void)
+{
+ return( head == 0 );
+}
+
+/*
+ * is_equal_to_tail - returns TRUE if the ptr equals the tail.
+ */
+
+int list::is_equal_to_tail (void)
+{
+ return( ptr == tail );
+}
+
+/*
+ * is_equal_to_head - returns TRUE if the ptr equals the head.
+ */
+
+int list::is_equal_to_head (void)
+{
+ return( ptr == head );
+}
+
+/*
+ * move_left - moves the ptr left.
+ */
+
+void list::move_left (void)
+{
+ ptr = ptr->left;
+}
+
+/*
+ * move_right - moves the ptr right.
+ */
+
+void list::move_right (void)
+{
+ ptr = ptr->right;
+}
+
+/*
+ * get_datum - returns the datum referenced via ptr.
+ */
+
+text_glob* list::get_data (void)
+{
+ return( ptr->datum );
+}
+
+/*
+ * move_right_get_data - returns the datum referenced via ptr and moves
+ * ptr right.
+ */
+
+text_glob* list::move_right_get_data (void)
+{
+ ptr = ptr->right;
+ if (ptr == head) {
+ return( 0 );
+ } else {
+ return( ptr->datum );
+ }
+}
+
+/*
+ * move_left_get_data - returns the datum referenced via ptr and moves
+ * ptr right.
+ */
+
+text_glob* list::move_left_get_data (void)
+{
+ ptr = ptr->left;
+ if (ptr == tail) {
+ return( 0 );
+ } else {
+ return( ptr->datum );
+ }
+}
+
+/*
+ * page class and methods
+ */
+
+class page {
+public:
+ page (void);
+ void add (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void add_html (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void add_tag (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void add_line (style *s,
+ int line_number,
+ int x1, int y1, int x2, int y2,
+ int thickness);
+ void dump_page (void); // debugging method
+
+ // and the data
+
+ list glyphs; // position of glyphs and specials on page
+ char_buffer buffer; // all characters for this page
+};
+
+page::page()
+{
+}
+
+void page::add (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+{
+ if (length > 0) {
+ text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
+ min_vertical, min_horizontal, max_vertical, max_horizontal,
+ FALSE, FALSE, FALSE, FALSE, 0);
+ glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
+ }
+}
+
+/*
+ * add_html - add a raw html command, for example mailto, line, background, image etc.
+ */
+
+void page::add_html (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+{
+ if (length > 0) {
+ text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
+ min_vertical, min_horizontal, max_vertical, max_horizontal,
+ TRUE, FALSE, FALSE, FALSE, 0);
+ glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
+ }
+}
+
+/*
+ * add_tag - adds a troff tag, for example: .tl .sp .br
+ */
+
+void page::add_tag (style *s, char *string, unsigned int length,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+{
+ if (length > 0) {
+ text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
+ min_vertical, min_horizontal, max_vertical, max_horizontal,
+ FALSE, TRUE,
+ (strncmp(string, "html-tag:.auto-image", 20) == 0),
+ FALSE, 0);
+ glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
+ }
+}
+
+/*
+ * add_line - adds the <line> primitive providing that y1==y2
+ */
+
+void page::add_line (style *s,
+ int line_number,
+ int x1, int y1, int x2, int y2,
+ int thickness)
+{
+ if (y1 == y2) {
+ text_glob *g = new text_glob(s, "", 0,
+ min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2),
+ FALSE, TRUE, FALSE, FALSE, thickness);
+ glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2));
+ }
+}
+
+/*
+ * dump_page - dump the page contents for debugging purposes.
+ */
+
+void page::dump_page(void)
+{
+ text_glob *g;
+
+ printf("\n\ndebugging start\n");
+ glyphs.start_from_head();
+ do {
+ g = glyphs.get_data();
+ printf("%s ", g->text_string);
+ glyphs.move_right();
+ } while (! glyphs.is_equal_to_head());
+ printf("\ndebugging end\n\n");
+}
+
+/*
+ * font classes and methods
+ */
+
+class html_font : public font {
+ html_font(const char *);
+public:
+ int encoding_index;
+ char *encoding;
+ char *reencoded_name;
+ ~html_font();
+ static html_font *load_html_font(const char *);
+};
+
+html_font *html_font::load_html_font(const char *s)
+{
+ html_font *f = new html_font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+html_font::html_font(const char *nm)
+: font(nm)
+{
+}
+
+html_font::~html_font()
+{
+}
+
+/*
+ * a simple class to contain the header to this document
+ */
+
+class title_desc {
+public:
+ title_desc ();
+ ~title_desc ();
+
+ int has_been_written;
+ int has_been_found;
+ char text[MAX_STRING_LENGTH];
+};
+
+
+title_desc::title_desc ()
+ : has_been_written(FALSE), has_been_found(FALSE)
+{
+}
+
+title_desc::~title_desc ()
+{
+}
+
+class header_desc {
+public:
+ header_desc ();
+ ~header_desc ();
+
+ int no_of_headings; // how many headings have we found?
+ char_buffer headings; // all the headings used in the document
+ list headers; // list of headers built from .NH and .SH
+ int header_level; // current header level
+ int written_header; // have we written the header yet?
+ char header_buffer[MAX_STRING_LENGTH]; // current header text
+
+ void write_headings (FILE *f, int force);
+};
+
+header_desc::header_desc ()
+ : no_of_headings(0), header_level(2), written_header(0)
+{
+}
+
+header_desc::~header_desc ()
+{
+}
+
+/*
+ * write_headings - emits a list of links for the headings in this document
+ */
+
+void header_desc::write_headings (FILE *f, int force)
+{
+ text_glob *g;
+
+ if (auto_links || force) {
+ if (! headers.is_empty()) {
+ int h=1;
+
+ headers.start_from_head();
+ do {
+ g = headers.get_data();
+ fputs("<a href=\"#", f);
+ if (simple_anchors)
+ fprintf(f, ANCHOR_TEMPLATE, h);
+ else
+ fputs(g->text_string, f);
+ h++;
+ fputs("\">", f);
+ fputs(g->text_string, f);
+ fputs("</a><br>\n", f);
+ headers.move_right();
+ } while (! headers.is_equal_to_head());
+ fputs("\n", f);
+ }
+ }
+}
+
+class html_printer : public printer {
+ files file_list;
+ simple_output html;
+ int res;
+ int space_char_index;
+ int no_of_printed_pages;
+ int paper_length;
+ enum { SBUF_SIZE = 8192 };
+ char sbuf[SBUF_SIZE];
+ int sbuf_len;
+ int sbuf_start_hpos;
+ int sbuf_vpos;
+ int sbuf_end_hpos;
+ int sbuf_kern;
+ style sbuf_style;
+ style output_style;
+ int output_hpos;
+ int output_vpos;
+ int output_vpos_max;
+ int output_draw_point_size;
+ int line_thickness;
+ int output_line_thickness;
+ unsigned char output_space_code;
+ string defs;
+ char *inside_font_style;
+ int page_number;
+ title_desc title;
+ title_desc indent; // use title class to remember $1 of .ip
+ header_desc header;
+ int header_indent;
+ int supress_sub_sup;
+ int cutoff_heading;
+ page *page_contents;
+ html_text *current_paragraph;
+ int end_center;
+ int end_tempindent;
+ TAG_ALIGNMENT next_tag;
+ int fill_on;
+ int linelength;
+ int pageoffset;
+ int indentation;
+ int prev_indent;
+ int pointsize;
+ int vertical_spacing;
+ int line_number;
+
+ void flush_sbuf ();
+ void set_style (const style &);
+ void set_space_code (unsigned char c);
+ void do_exec (char *, const environment *);
+ void do_import (char *, const environment *);
+ void do_def (char *, const environment *);
+ void do_mdef (char *, const environment *);
+ void do_file (char *, const environment *);
+ void set_line_thickness (const environment *);
+ void terminate_current_font (void);
+ void flush_font (void);
+ void add_char_to_sbuf (unsigned char code);
+ void add_to_sbuf (unsigned char code, const char *name);
+ void write_title (int in_head);
+ void determine_diacritical_mark (const char *name, const environment *env);
+ int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w);
+ char *remove_last_char_from_sbuf ();
+ int seen_backwards_escape (char *s, int l);
+ void flush_page (void);
+ void troff_tag (text_glob *g);
+ void flush_globs (void);
+ void emit_line (text_glob *g);
+ void emit_raw (text_glob *g);
+ void translate_to_html (text_glob *g);
+ void determine_space (text_glob *g);
+ void start_font (const char *name);
+ void end_font (const char *name);
+ int is_font_courier (font *f);
+ int is_courier_until_eol (void);
+ void start_size (int from, int to);
+ void do_font (text_glob *g);
+ void do_center (char *arg);
+ void do_break (void);
+ void do_eol (void);
+ void do_title (void);
+ void do_fill (int on);
+ void do_heading (char *arg);
+ void write_header (void);
+ void determine_header_level (int level);
+ void do_linelength (char *arg);
+ void do_pageoffset (char *arg);
+ void do_indentation (char *arg);
+ void do_tempindent (char *arg);
+ void do_indentedparagraph (void);
+ void do_verticalspacing (char *arg);
+ void do_pointsize (char *arg);
+ void do_centered_image (void);
+ void do_left_image (void);
+ void do_right_image (void);
+ void do_auto_image (text_glob *g, const char *filename);
+ void do_links (void);
+ void do_flush (void);
+ int is_in_middle (int left, int right);
+ void do_sup_or_sub (text_glob *g);
+ int start_subscript (text_glob *g);
+ int end_subscript (text_glob *g);
+ int start_superscript (text_glob *g);
+ int end_superscript (text_glob *g);
+
+ // ADD HERE
+
+public:
+ html_printer ();
+ ~html_printer ();
+ void set_char (int i, font *f, const environment *env, int w, const char *name);
+ void draw (int code, int *p, int np, const environment *env);
+ void begin_page (int);
+ void end_page (int);
+ void special (char *arg, const environment *env, char type);
+ font *make_font (const char *);
+ void end_of_line ();
+};
+
+printer *make_printer()
+{
+ return new html_printer;
+}
+
+static void usage(FILE *stream);
+
+void html_printer::set_style(const style &sty)
+{
+ const char *fontname = sty.f->get_name();
+ if (fontname == 0)
+ fatal("no internalname specified for font");
+
+#if 0
+ change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
+#endif
+}
+
+void html_printer::end_of_line()
+{
+ flush_sbuf();
+ line_number++;
+}
+
+/*
+ * emit_line - writes out a horizontal rule.
+ */
+
+void html_printer::emit_line (text_glob *g)
+{
+ // --fixme-- needs to know the length in percentage
+ html.put_string("<hr>");
+}
+
+/*
+ * emit_raw - writes the raw html information directly to the device.
+ */
+
+void html_printer::emit_raw (text_glob *g)
+{
+ do_font(g);
+ if (next_tag == INLINE) {
+ determine_space(g);
+ current_paragraph->do_emittext(g->text_string, g->text_length);
+ } else {
+ int in_table=current_paragraph->is_in_table();
+
+ current_paragraph->done_para();
+ switch (next_tag) {
+
+ case CENTERED:
+ current_paragraph->do_para("align=center");
+ break;
+ case LEFT:
+ current_paragraph->do_para("align=left");
+ break;
+ case RIGHT:
+ current_paragraph->do_para("align=right");
+ break;
+ default:
+ fatal("unknown enumeration");
+ }
+ current_paragraph->do_emittext(g->text_string, g->text_length);
+ current_paragraph->done_para();
+ next_tag = INLINE;
+ supress_sub_sup = TRUE;
+#if defined(INDENTATION)
+ if (in_table) {
+ stop();
+ current_paragraph->do_indent(NULL, 0, pageoffset, linelength);
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+#endif
+ }
+}
+
+/*
+ * do_center - handle the .ce commands from troff.
+ */
+
+void html_printer::do_center (char *arg)
+{
+ int n = atoi(arg);
+
+ current_paragraph->do_break();
+ current_paragraph->done_para();
+ supress_sub_sup = TRUE;
+
+ if (n > 0) {
+ current_paragraph->do_para("align=center");
+ end_center += n;
+ } else {
+ end_center = 0;
+ }
+}
+
+/*
+ * do_centered_image - set a flag such that the next html-tag is
+ * placed inside a centered paragraph.
+ */
+
+void html_printer::do_centered_image (void)
+{
+ next_tag = CENTERED;
+}
+
+/*
+ * do_right_image - set a flag such that the next html-tag is
+ * placed inside a right aligned paragraph.
+ */
+
+void html_printer::do_right_image (void)
+{
+ next_tag = RIGHT;
+}
+
+/*
+ * do_left_image - set a flag such that the next html-tag is
+ * placed inside a left aligned paragraph.
+ */
+
+void html_printer::do_left_image (void)
+{
+ next_tag = LEFT;
+}
+
+/*
+ * exists - returns TRUE if filename exists.
+ */
+
+static int exists (const char *filename)
+{
+ FILE *fp = fopen(filename, "r");
+
+ if (fp == 0) {
+ return( FALSE );
+ } else {
+ fclose(fp);
+ return( TRUE );
+ }
+}
+
+/*
+ * generate_img_src - returns a html image tag for the filename
+ * providing that the image exists.
+ */
+
+static char *generate_img_src (const char *filename)
+{
+ static char buffer[MAX_STRING_LENGTH];
+
+ while (filename && (filename[0] == ' ')) {
+ filename++;
+ }
+ if (exists(filename)) {
+ strcpy(buffer, "<img src=\"");
+ strncat(buffer, filename, MAX_STRING_LENGTH-strlen("<img src=\"")-1);
+ if (strlen(buffer) < MAX_STRING_LENGTH-3) {
+ strncat(buffer, "\">", 3);
+ }
+ return( (char *)&buffer );
+ } else {
+ return( 0 );
+ }
+}
+
+/*
+ * do_auto_image - tests whether the image, indicated by filename,
+ * is present, if so then it emits an html image tag.
+ * An image tag may be passed through from pic, eqn
+ * but the corresponding image might not be created.
+ * Consider .EQ delim $$ .EN or an empty .PS .PE.
+ */
+
+void html_printer::do_auto_image (text_glob *g, const char *filename)
+{
+ char *buffer = generate_img_src(filename);
+
+ if (buffer) {
+ /*
+ * utilize emit_raw by creating a new text_glob.
+ */
+ text_glob h = *g;
+
+ h.text_string = buffer;
+ h.text_length = strlen(buffer);
+ emit_raw(&h);
+ } else {
+ next_tag = INLINE;
+ }
+}
+
+/*
+ * do_title - handle the .tl commands from troff.
+ */
+
+void html_printer::do_title (void)
+{
+ text_glob *t;
+ int removed_from_head;
+ char buf[MAX_STRING_LENGTH];
+
+ if (page_number == 1) {
+ int found_title_start = FALSE;
+ if (! page_contents->glyphs.is_empty()) {
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ do {
+ t = page_contents->glyphs.get_data();
+ removed_from_head = FALSE;
+ if (t->is_auto_img()) {
+ char *img=generate_img_src((char *)(t->text_string + 20));
+
+ if (img) {
+ if (found_title_start) {
+ strcat(title.text, " ");
+ }
+ found_title_start = TRUE;
+ title.has_been_found = TRUE;
+ strcat(title.text, img);
+ }
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ removed_from_head = ((!page_contents->glyphs.is_empty()) &&
+ (page_contents->glyphs.is_equal_to_head()));
+ } else if (t->is_raw_command) {
+ /* skip raw commands
+ */
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ } else if (t->is_eol()) {
+ /* end of title found
+ */
+ title.has_been_found = TRUE;
+ return;
+ } else if (t->is_a_tag()) {
+ /* end of title found, but move back so that we read this tag and process it
+ */
+ page_contents->glyphs.move_left(); /* move backwards to last word */
+ title.has_been_found = TRUE;
+ return;
+ } else if (found_title_start) {
+ strcat(title.text, " ");
+ str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
+ strcat(title.text, buf);
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ removed_from_head = ((!page_contents->glyphs.is_empty()) &&
+ (page_contents->glyphs.is_equal_to_head()));
+ } else {
+ str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
+ strcpy((char *)title.text, buf);
+ found_title_start = TRUE;
+ title.has_been_found = TRUE;
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ removed_from_head = ((!page_contents->glyphs.is_empty()) &&
+ (page_contents->glyphs.is_equal_to_head()));
+ }
+ } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
+ }
+ // page_contents->glyphs.move_left(); /* move backwards to last word */
+ }
+}
+
+void html_printer::write_header (void)
+{
+ if (strlen(header.header_buffer) > 0) {
+ if (header.header_level > 7) {
+ header.header_level = 7;
+ }
+
+ // firstly we must terminate any font and type faces
+ current_paragraph->done_para();
+ current_paragraph->done_table();
+ supress_sub_sup = TRUE;
+
+ if (cutoff_heading+2 > header.header_level) {
+ // now we save the header so we can issue a list of links
+ header.no_of_headings++;
+ style st;
+
+ text_glob *h=new text_glob(&st,
+ header.headings.add_string(header.header_buffer, strlen(header.header_buffer)),
+ strlen(header.header_buffer),
+ header.no_of_headings, header.header_level,
+ header.no_of_headings, header.header_level,
+ FALSE, FALSE, FALSE, FALSE, FALSE);
+ header.headers.add(h,
+ header.no_of_headings,
+ header.no_of_headings, header.no_of_headings,
+ header.no_of_headings, header.no_of_headings); // and add this header to the header list
+
+ // lastly we generate a tag
+
+ html.nl().put_string("<a name=\"");
+ if (simple_anchors) {
+ char buffer[MAX_LINE_LENGTH];
+
+ sprintf(buffer, ANCHOR_TEMPLATE, header.no_of_headings);
+ html.put_string(buffer);
+ } else {
+ html.put_string(header.header_buffer);
+ }
+ html.put_string("\"></a>").nl();
+ }
+
+ // and now we issue the real header
+ html.put_string("<h");
+ html.put_number(header.header_level);
+ html.put_string(">");
+ html.put_string(header.header_buffer);
+ html.put_string("</h");
+ html.put_number(header.header_level);
+ html.put_string(">").nl();
+
+ current_paragraph->do_para("");
+ }
+}
+
+void html_printer::determine_header_level (int level)
+{
+ if (level == 0) {
+ int i;
+ int l=strlen(header.header_buffer);
+
+ for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) {
+ if (header.header_buffer[i] == '.') {
+ level++;
+ }
+ }
+ }
+ header.header_level = level+1;
+}
+
+/*
+ * do_heading - handle the .SH and .NH and equivalent commands from troff.
+ */
+
+void html_printer::do_heading (char *arg)
+{
+ text_glob *g;
+ text_glob *l = 0;
+ char buf[MAX_STRING_LENGTH];
+ int level=atoi(arg);
+
+ strcpy(header.header_buffer, "");
+ page_contents->glyphs.move_right();
+ if (! page_contents->glyphs.is_equal_to_head()) {
+ g = page_contents->glyphs.get_data();
+ do {
+ if (g->is_auto_img()) {
+ char *img=generate_img_src((char *)(g->text_string + 20));
+
+ if (img) {
+ simple_anchors = TRUE; // we cannot use full heading anchors with images
+ if (l != 0) {
+ strcat(header.header_buffer, " ");
+ }
+ l = g;
+ strcat(header.header_buffer, img);
+ }
+ } else if (! (g->is_a_line() || g->is_a_tag() || g->is_raw())) {
+ /*
+ * we ignore raw commands when constructing a heading
+ */
+ if (l != 0) {
+ strcat(header.header_buffer, " ");
+ }
+ l = g;
+ str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE);
+ strcat(header.header_buffer, (char *)buf);
+ }
+ page_contents->glyphs.move_right();
+ g = page_contents->glyphs.get_data();
+ } while ((! page_contents->glyphs.is_equal_to_head()) &&
+ (! g->is_br()));
+ }
+
+ determine_header_level(level);
+ write_header();
+
+ // finally set the output to neutral for after the header
+ g = page_contents->glyphs.get_data();
+ page_contents->glyphs.move_left(); // so that next time we use old g
+}
+
+/*
+ * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
+ */
+
+int html_printer::is_courier_until_eol (void)
+{
+ text_glob *orig = page_contents->glyphs.get_data();
+ int result = TRUE;
+ text_glob *g;
+
+ if (! page_contents->glyphs.is_equal_to_tail()) {
+ page_contents->glyphs.move_right();
+ do {
+ g = page_contents->glyphs.get_data();
+ if (! is_font_courier(g->text_style.f)) {
+ result = FALSE;
+ }
+ page_contents->glyphs.move_right();
+ } while ((result) &&
+ (! page_contents->glyphs.is_equal_to_head()) &&
+ (! g->is_eol()));
+
+ /*
+ * now restore our previous position.
+ */
+ while (page_contents->glyphs.get_data() != orig) {
+ page_contents->glyphs.move_left();
+ }
+ }
+ return( result );
+}
+
+/*
+ * do_linelength - handle the .ll command from troff.
+ */
+
+void html_printer::do_linelength (char *arg)
+{
+#if defined(INDENTATION)
+ if (fill_on) {
+ linelength = atoi(arg);
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+#endif
+}
+
+/*
+ * do_pageoffset - handle the .po command from troff.
+ */
+
+void html_printer::do_pageoffset (char *arg)
+{
+#if defined(INDENTATION)
+ pageoffset = atoi(arg);
+ if (fill_on) {
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+#endif
+}
+
+/*
+ * do_indentation - handle the .in command from troff.
+ */
+
+void html_printer::do_indentation (char *arg)
+{
+#if defined(INDENTATION)
+ if (fill_on) {
+ indentation = atoi(arg);
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+#endif
+}
+
+/*
+ * do_tempindent - handle the .ti command from troff.
+ */
+
+void html_printer::do_tempindent (char *arg)
+{
+#if defined(INDENTATION)
+ if (fill_on) {
+ end_tempindent = 1;
+ prev_indent = indentation;
+ indentation = atoi(arg);
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+#endif
+}
+
+/*
+ * do_indentedparagraph - handle the .ip tag, this buffers the next line
+ * and passes this to text-text as the left hand
+ * column table entry.
+ */
+
+void html_printer::do_indentedparagraph (void)
+{
+#if defined(INDENTATION)
+ text_glob *t;
+ int removed_from_head;
+ char buf[MAX_STRING_LENGTH];
+ int found_indent_start = FALSE;
+
+ indent.has_been_found = FALSE;
+ indent.text[0] = (char)0;
+
+ if (! page_contents->glyphs.is_empty()) {
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ do {
+ t = page_contents->glyphs.get_data();
+ removed_from_head = FALSE;
+ if (t->is_auto_img()) {
+ char *img=generate_img_src((char *)(t->text_string + 20));
+
+ if (img) {
+ if (found_indent_start) {
+ strcat(indent.text, " ");
+ }
+ found_indent_start = TRUE;
+ strcat(indent.text, img);
+ }
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ } else if (t->is_raw_command) {
+ /* skip raw commands
+ */
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ } else if (t->is_a_tag() && (strncmp(t->text_string, "html-tag:.br", 12) == 0)) {
+ /* end of indented para found, but move back so that we read this tag and process it
+ */
+ page_contents->glyphs.move_left(); /* move backwards to last word */
+ indent.has_been_found = TRUE;
+ return;
+ } else if (t->is_a_tag()) {
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ } else if (found_indent_start) {
+ strcat(indent.text, " ");
+ str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
+ strcat(indent.text, buf);
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ removed_from_head = ((!page_contents->glyphs.is_empty()) &&
+ (page_contents->glyphs.is_equal_to_head()));
+ } else {
+ str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
+ strcpy((char *)indent.text, buf);
+ found_indent_start = TRUE;
+ indent.has_been_found = TRUE;
+ page_contents->glyphs.sub_move_right(); /* move onto next word */
+ removed_from_head = ((!page_contents->glyphs.is_empty()) &&
+ (page_contents->glyphs.is_equal_to_head()));
+ }
+ } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
+ }
+ // page_contents->glyphs.move_left(); /* move backwards to last word */
+#endif
+}
+
+/*
+ * do_verticalspacing - handle the .vs command from troff.
+ */
+
+void html_printer::do_verticalspacing (char *arg)
+{
+ vertical_spacing = atoi(arg);
+}
+
+/*
+ * do_pointsize - handle the .ps command from troff.
+ */
+
+void html_printer::do_pointsize (char *arg)
+{
+ pointsize = atoi(arg);
+}
+
+/*
+ * do_fill - records whether troff has requested that text be filled.
+ */
+
+void html_printer::do_fill (int on)
+{
+ current_paragraph->do_break();
+ output_hpos = indentation+pageoffset;
+ supress_sub_sup = TRUE;
+
+ if (fill_on != on) {
+ if (on) {
+ current_paragraph->done_pre();
+ } else {
+ current_paragraph->do_pre();
+ }
+ }
+ fill_on = on;
+}
+
+/*
+ * do_eol - handle the end of line
+ */
+
+void html_printer::do_eol (void)
+{
+ if (! fill_on) {
+ current_paragraph->do_newline();
+ current_paragraph->do_break();
+ }
+ output_hpos = indentation+pageoffset;
+ if (end_center > 0) {
+ if (end_center > 1) {
+ current_paragraph->do_break();
+ }
+ end_center--;
+ if (end_center == 0) {
+ current_paragraph->done_para();
+ supress_sub_sup = TRUE;
+ }
+ }
+}
+
+/*
+ * do_flush - flushes all output and tags.
+ */
+
+void html_printer::do_flush (void)
+{
+ current_paragraph->done_para();
+ current_paragraph->done_table();
+}
+
+/*
+ * do_links - moves onto a new temporary file and sets auto_links to FALSE.
+ */
+
+void html_printer::do_links (void)
+{
+ current_paragraph->done_para();
+ current_paragraph->done_table();
+ auto_links = FALSE; /* from now on only emit under user request */
+#if !defined(DEBUGGING)
+ file_list.add_new_file(xtmpfile());
+ html.set_file(file_list.get_file());
+#endif
+}
+
+/*
+ * do_break - handles the ".br" request and also
+ * undoes an outstanding ".ti" command.
+ */
+
+void html_printer::do_break (void)
+{
+ current_paragraph->do_break();
+#if defined(INDENTATION)
+ if (end_tempindent > 0) {
+ end_tempindent--;
+ if (end_tempindent == 0) {
+ indentation = prev_indent;
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+ }
+ }
+#endif
+ output_hpos = indentation+pageoffset;
+ supress_sub_sup = TRUE;
+}
+
+/*
+ * troff_tag - processes the troff tag and manipulates the troff state machine.
+ */
+
+void html_printer::troff_tag (text_glob *g)
+{
+ /*
+ * firstly skip over html-tag:
+ */
+ char *t=(char *)g->text_string+9;
+
+ if (g->is_eol()) {
+ do_eol();
+ } else if (strncmp(t, ".sp", 3) == 0) {
+ current_paragraph->do_space();
+ supress_sub_sup = TRUE;
+ } else if (strncmp(t, ".br", 3) == 0) {
+ do_break();
+ } else if (strcmp(t, ".centered-image") == 0) {
+ do_centered_image();
+ } else if (strcmp(t, ".right-image") == 0) {
+ do_right_image();
+ } else if (strcmp(t, ".left-image") == 0) {
+ do_left_image();
+ } else if (strncmp(t, ".auto-image", 11) == 0) {
+ char *a = (char *)t+11;
+ do_auto_image(g, a);
+ } else if (strncmp(t, ".ce", 3) == 0) {
+ char *a = (char *)t+3;
+ supress_sub_sup = TRUE;
+ do_center(a);
+ } else if (strncmp(t, ".tl", 3) == 0) {
+ supress_sub_sup = TRUE;
+ do_title();
+ } else if (strncmp(t, ".fi", 3) == 0) {
+ do_fill(TRUE);
+ } else if (strncmp(t, ".nf", 3) == 0) {
+ do_fill(FALSE);
+ } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
+ char *a = (char *)t+3;
+ do_heading(a);
+ } else if (strncmp(t, ".ll", 3) == 0) {
+ char *a = (char *)t+3;
+ do_linelength(a);
+ } else if (strncmp(t, ".po", 3) == 0) {
+ char *a = (char *)t+3;
+ do_pageoffset(a);
+ } else if (strncmp(t, ".in", 3) == 0) {
+ char *a = (char *)t+3;
+ do_indentation(a);
+ } else if (strncmp(t, ".ti", 3) == 0) {
+ char *a = (char *)t+3;
+ do_tempindent(a);
+ } else if (strncmp(t, ".vs", 3) == 0) {
+ char *a = (char *)t+3;
+ do_verticalspacing(a);
+ } else if (strncmp(t, ".ip", 3) == 0) {
+ do_indentedparagraph();
+ } else if (strcmp(t, ".links") == 0) {
+ do_links();
+ }
+}
+
+/*
+ * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
+ */
+
+int html_printer::is_in_middle (int left, int right)
+{
+ return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE );
+}
+
+/*
+ * flush_globs - runs through the text glob list and emits html.
+ */
+
+void html_printer::flush_globs (void)
+{
+ text_glob *g;
+
+ if (! page_contents->glyphs.is_empty()) {
+ page_contents->glyphs.start_from_head();
+ do {
+ g = page_contents->glyphs.get_data();
+
+ if (strcmp(g->text_string, "XXXXXXX") == 0) {
+ stop();
+ }
+
+ if (g->is_raw()) {
+ emit_raw(g);
+ } else if (g->is_a_tag()) {
+ troff_tag(g);
+ } else if (g->is_a_line()) {
+ emit_line(g);
+ } else {
+ translate_to_html(g);
+ }
+ /*
+ * after processing the title (and removing it) the glyph list might be empty
+ */
+ if (! page_contents->glyphs.is_empty()) {
+ page_contents->glyphs.move_right();
+ }
+ } while (! page_contents->glyphs.is_equal_to_head());
+ }
+}
+
+void html_printer::flush_page (void)
+{
+ supress_sub_sup = TRUE;
+ flush_sbuf();
+ // page_contents->dump_page();
+ flush_globs();
+ current_paragraph->done_para();
+ current_paragraph->done_table();
+
+ // move onto a new page
+ delete page_contents;
+ page_contents = new page;
+}
+
+/*
+ * determine_space - works out whether we need to write a space.
+ * If last glyth is ajoining then no space emitted.
+ */
+
+void html_printer::determine_space (text_glob *g)
+{
+ if (current_paragraph->is_in_pre()) {
+ int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
+ /*
+ * .nf has been specified
+ */
+ while (output_hpos < g->minh) {
+ output_hpos += space_width;
+ current_paragraph->emit_space();
+ }
+ } else {
+ if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
+ current_paragraph->emit_space();
+ }
+ }
+}
+
+/*
+ * is_font_courier - returns TRUE if the font, f, is courier.
+ */
+
+int html_printer::is_font_courier (font *f)
+{
+ if (f != 0) {
+ const char *fontname = f->get_name();
+
+ return( (fontname != 0) && (fontname[0] == 'C') );
+ }
+ return( FALSE );
+}
+
+/*
+ * end_font - shuts down the font corresponding to fontname.
+ */
+
+void html_printer::end_font (const char *fontname)
+{
+ if (strcmp(fontname, "B") == 0) {
+ current_paragraph->done_bold();
+ } else if (strcmp(fontname, "I") == 0) {
+ current_paragraph->done_italic();
+ } else if (strcmp(fontname, "BI") == 0) {
+ current_paragraph->done_bold();
+ current_paragraph->done_italic();
+ } else if (strcmp(fontname, "CR") == 0) {
+ current_paragraph->done_tt();
+ current_paragraph->done_pre();
+ }
+}
+
+/*
+ * start_font - starts the font corresponding to name.
+ */
+
+void html_printer::start_font (const char *fontname)
+{
+ if (strcmp(fontname, "R") == 0) {
+ current_paragraph->done_bold();
+ current_paragraph->done_italic();
+ current_paragraph->done_tt();
+ } else if (strcmp(fontname, "B") == 0) {
+ current_paragraph->do_bold();
+ } else if (strcmp(fontname, "I") == 0) {
+ current_paragraph->do_italic();
+ } else if (strcmp(fontname, "BI") == 0) {
+ current_paragraph->do_bold();
+ current_paragraph->do_italic();
+ } else if (strcmp(fontname, "CR") == 0) {
+ if ((! fill_on) && (is_courier_until_eol())) {
+ current_paragraph->do_pre();
+ }
+ current_paragraph->do_tt();
+ }
+}
+
+/*
+ * start_size - from is old font size, to is the new font size.
+ * The html increase <big> and <small> decrease alters the
+ * font size by 20%. We try and map these onto glyph sizes.
+ */
+
+void html_printer::start_size (int from, int to)
+{
+ if (from < to) {
+ while (from < to) {
+ current_paragraph->do_big();
+ from += SIZE_INCREMENT;
+ }
+ } else if (from > to) {
+ while (from > to) {
+ current_paragraph->do_small();
+ from -= SIZE_INCREMENT;
+ }
+ }
+}
+
+/*
+ * do_font - checks to see whether we need to alter the html font.
+ */
+
+void html_printer::do_font (text_glob *g)
+{
+ /*
+ * check if the output_style.point_size has not been set yet
+ * this allow users to place .ps at the top of their troff files
+ * and grohtml can then treat the .ps value as the base font size (3)
+ */
+ if (output_style.point_size == -1) {
+ output_style.point_size = pointsize;
+ }
+
+ if (g->text_style.f != output_style.f) {
+ if (output_style.f != 0) {
+ end_font(output_style.f->get_name());
+ }
+ output_style.f = g->text_style.f;
+ if (output_style.f != 0) {
+ start_font(output_style.f->get_name());
+ }
+ }
+ if (output_style.point_size != g->text_style.point_size) {
+ do_sup_or_sub(g);
+ if ((output_style.point_size > 0) &&
+ (g->text_style.point_size > 0)) {
+ start_size(output_style.point_size, g->text_style.point_size);
+ }
+ if (g->text_style.point_size > 0) {
+ output_style.point_size = g->text_style.point_size;
+ }
+ }
+}
+
+/*
+ * start_subscript - returns TRUE if, g, looks like a subscript start.
+ */
+
+int html_printer::start_subscript (text_glob *g)
+{
+ int r = font::res;
+ int height = output_style.point_size*r/72;
+
+ return( (output_style.point_size != 0) &&
+ (output_vpos < g->minv) &&
+ (output_vpos-height > g->maxv) &&
+ (output_style.point_size > g->text_style.point_size) );
+}
+
+/*
+ * start_superscript - returns TRUE if, g, looks like a superscript start.
+ */
+
+int html_printer::start_superscript (text_glob *g)
+{
+ int r = font::res;
+ int height = output_style.point_size*r/72;
+
+ return( (output_style.point_size != 0) &&
+ (output_vpos > g->minv) &&
+ (output_vpos-height < g->maxv) &&
+ (output_style.point_size > g->text_style.point_size) );
+}
+
+/*
+ * end_subscript - returns TRUE if, g, looks like the end of a subscript.
+ */
+
+int html_printer::end_subscript (text_glob *g)
+{
+ int r = font::res;
+ int height = output_style.point_size*r/72;
+
+ return( (output_style.point_size != 0) &&
+ (g->minv < output_vpos) &&
+ (output_vpos-height > g->maxv) &&
+ (output_style.point_size < g->text_style.point_size) );
+}
+
+/*
+ * end_superscript - returns TRUE if, g, looks like the end of a superscript.
+ */
+
+int html_printer::end_superscript (text_glob *g)
+{
+ int r = font::res;
+ int height = output_style.point_size*r/72;
+
+ return( (output_style.point_size != 0) &&
+ (g->minv > output_vpos) &&
+ (output_vpos-height < g->maxv) &&
+ (output_style.point_size < g->text_style.point_size) );
+}
+
+/*
+ * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
+ * start/end and it calls the services of html-text to issue the
+ * appropriate tags.
+ */
+
+void html_printer::do_sup_or_sub (text_glob *g)
+{
+ if (! supress_sub_sup) {
+ if (start_subscript(g)) {
+ current_paragraph->do_sub();
+ } else if (start_superscript(g)) {
+ current_paragraph->do_sup();
+ } else if (end_subscript(g)) {
+ current_paragraph->done_sub();
+ } else if (end_superscript(g)) {
+ current_paragraph->done_sup();
+ }
+ }
+}
+
+/*
+ * translate_to_html - translates a textual string into html text
+ */
+
+void html_printer::translate_to_html (text_glob *g)
+{
+ char buf[MAX_STRING_LENGTH];
+
+ do_font(g);
+ determine_space(g);
+ str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH,
+ g->text_string, g->text_length, TRUE);
+ current_paragraph->do_emittext(buf, strlen(buf));
+ output_vpos = g->minv;
+ output_hpos = g->maxh;
+ output_vpos_max = g->maxv;
+ supress_sub_sup = FALSE;
+}
+
+/*
+ * flush_sbuf - flushes the current sbuf into the list of glyphs.
+ */
+
+void html_printer::flush_sbuf()
+{
+ if (sbuf_len > 0) {
+ int r=font::res; // resolution of the device
+ set_style(sbuf_style);
+
+ page_contents->add(&sbuf_style, sbuf, sbuf_len,
+ line_number,
+ sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
+ sbuf_vpos , sbuf_end_hpos);
+
+ output_hpos = sbuf_end_hpos;
+ output_vpos = sbuf_vpos;
+ sbuf_len = 0;
+ }
+}
+
+void html_printer::set_line_thickness(const environment *env)
+{
+ line_thickness = env->size;
+}
+
+void html_printer::draw(int code, int *p, int np, const environment *env)
+{
+ switch (code) {
+
+ case 'l':
+ if (np == 2) {
+ page_contents->add_line(&sbuf_style,
+ line_number,
+ env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
+ } else {
+ error("2 arguments required for line");
+ }
+ break;
+ case 't':
+ {
+ if (np == 0) {
+ line_thickness = -1;
+ } else {
+ // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2) {
+ error("0 or 1 argument required for thickness");
+ break;
+ }
+ line_thickness = p[0];
+ }
+ break;
+ }
+
+ case 'P':
+ // fall through
+ case 'p':
+ {
+#if 0
+ if (np & 1) {
+ error("even number of arguments required for polygon");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for polygon");
+ break;
+ }
+ // firstly lets add our current position to polygon
+ int oh=env->hpos;
+ int ov=env->vpos;
+ int i=0;
+
+ while (i<np) {
+ p[i+0] += oh;
+ p[i+1] += ov;
+ oh = p[i+0];
+ ov = p[i+1];
+ i += 2;
+ }
+ // now store polygon in page
+ page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill);
+#endif
+ }
+ break;
+ case 'E':
+ // fall through
+ case 'e':
+#if 0
+ if (np != 2) {
+ error("2 arguments required for ellipse");
+ break;
+ }
+ page_contents->add_line(code,
+ env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2,
+ env->size, fill);
+#endif
+ break;
+ case 'C':
+ // fill circle
+
+ case 'c':
+ {
+#if 0
+ // troff adds an extra argument to C
+ if (np != 1 && !(code == 'C' && np == 2)) {
+ error("1 argument required for circle");
+ break;
+ }
+ page_contents->add_line(code,
+ env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2,
+ env->size, fill);
+#endif
+ }
+ break;
+ case 'a':
+ {
+#if 0
+ if (np == 4) {
+ double c[2];
+
+ if (adjust_arc_center(p, c)) {
+ page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill);
+ } else {
+ // a straignt line
+ page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill);
+ }
+ } else {
+ error("4 arguments required for arc");
+ }
+#endif
+ }
+ break;
+ case '~':
+ {
+#if 0
+ if (np & 1) {
+ error("even number of arguments required for spline");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for spline");
+ break;
+ }
+ // firstly lets add our current position to spline
+ int oh=env->hpos;
+ int ov=env->vpos;
+ int i=0;
+
+ while (i<np) {
+ p[i+0] += oh;
+ p[i+1] += ov;
+ oh = p[i+0];
+ ov = p[i+1];
+ i += 2;
+ }
+ page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill);
+#endif
+ }
+ break;
+ case 'f':
+ {
+#if 0
+ if (np != 1 && np != 2) {
+ error("1 argument required for fill");
+ break;
+ }
+ fill = p[0];
+ if (fill < 0 || fill > FILL_MAX) {
+ // This means fill with the current color.
+ fill = FILL_MAX + 1;
+ }
+#endif
+ break;
+ }
+
+ default:
+ error("unrecognised drawing command `%1'", char(code));
+ break;
+ }
+}
+
+html_printer::html_printer()
+: html(0, MAX_LINE_LENGTH),
+ no_of_printed_pages(0),
+ sbuf_len(0),
+ output_hpos(-1),
+ output_vpos(-1),
+ output_vpos_max(-1),
+ line_thickness(-1),
+ inside_font_style(0),
+ page_number(0),
+ header_indent(-1),
+ supress_sub_sup(TRUE),
+ cutoff_heading(100),
+ end_center(0),
+ end_tempindent(0),
+ next_tag(INLINE),
+ fill_on(TRUE),
+ pageoffset(0),
+ indentation(0),
+ prev_indent(0),
+ linelength(0),
+ line_number(0)
+{
+#if defined(DEBUGGING)
+ file_list.add_new_file(stdout);
+#else
+ file_list.add_new_file(xtmpfile());
+#endif
+ html.set_file(file_list.get_file());
+ if (font::hor != 24)
+ fatal("horizontal resolution must be 24");
+ if (font::vert != 40)
+ fatal("vertical resolution must be 40");
+#if 0
+ // should be sorted html..
+ if (font::res % (font::sizescale*72) != 0)
+ fatal("res must be a multiple of 72*sizescale");
+#endif
+ int r = font::res;
+ int point = 0;
+ while (r % 10 == 0) {
+ r /= 10;
+ point++;
+ }
+ res = r;
+ html.set_fixed_point(point);
+ space_char_index = font::name_to_index("space");
+ paper_length = font::paperlength;
+ linelength = font::res*13/2;
+ if (paper_length == 0)
+ paper_length = 11*font::res;
+
+ page_contents = new page();
+}
+
+/*
+ * add_char_to_sbuf - adds a single character to the sbuf.
+ */
+
+void html_printer::add_char_to_sbuf (unsigned char code)
+{
+ if (sbuf_len < SBUF_SIZE) {
+ sbuf[sbuf_len] = code;
+ sbuf_len++;
+ } else {
+ fatal("need to increase SBUF_SIZE");
+ }
+}
+
+/*
+ * add_to_sbuf - adds character code or name to the sbuf.
+ */
+
+void html_printer::add_to_sbuf (unsigned char code, const char *name)
+{
+ if (name == 0) {
+ add_char_to_sbuf(code);
+ } else {
+ if (sbuf_style.f != NULL) {
+ char *html_glyph = get_html_translation(sbuf_style.f, name);
+
+ if (html_glyph == NULL) {
+ add_char_to_sbuf(code);
+ } else {
+ int l = strlen(html_glyph);
+ int i;
+
+ // Escape the name, so that "&" doesn't get expanded to "&amp;"
+ // later during translate_to_html.
+ add_char_to_sbuf('\\'); add_char_to_sbuf('(');
+
+ for (i=0; i<l; i++) {
+ add_char_to_sbuf(html_glyph[i]);
+ }
+ add_char_to_sbuf('\\'); add_char_to_sbuf(')');
+ }
+ }
+ }
+}
+
+int html_printer::sbuf_continuation (unsigned char code, const char *name,
+ const environment *env, int w)
+{
+ if (sbuf_end_hpos == env->hpos) {
+ add_to_sbuf(code, name);
+ sbuf_end_hpos += w + sbuf_kern;
+ return( TRUE );
+ } else {
+ if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) &&
+ ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
+ /*
+ * lets see whether a space is needed or not
+ */
+ int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
+
+ if (env->hpos-sbuf_end_hpos < space_width/2) {
+ add_to_sbuf(code, name);
+ sbuf_end_hpos = env->hpos + w;
+ return( TRUE );
+ }
+ }
+ }
+ return( FALSE );
+}
+
+/*
+ * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s
+ */
+
+int html_printer::seen_backwards_escape (char *s, int l)
+{
+ /*
+ * this is tricky so it is broken into components for clarity
+ * (we let the compiler put in all back into a complex expression)
+ */
+ if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) {
+ /*
+ * ok seen '\(' but we must now check for '\\('
+ */
+ if ((l>1) && (sbuf[l-2] == '\\')) {
+ /*
+ * escaped the escape
+ */
+ return( FALSE );
+ } else {
+ return( TRUE );
+ }
+ } else {
+ return( FALSE );
+ }
+}
+
+/*
+ * reverse - return reversed string.
+ */
+
+char *reverse (char *s)
+{
+ int i=0;
+ int j=strlen(s)-1;
+ char t;
+
+ while (i<j) {
+ t = s[i];
+ s[i] = s[j];
+ s[j] = t;
+ i++;
+ j--;
+ }
+ return( s );
+}
+
+/*
+ * remove_last_char_from_sbuf - removes the last character from sbuf.
+ */
+
+char *html_printer::remove_last_char_from_sbuf ()
+{
+ int l=sbuf_len;
+ static char last[MAX_STRING_LENGTH];
+
+ if (l>0) {
+ l--;
+ if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) {
+ /*
+ * found terminating escape
+ */
+ int i=0;
+
+ l -= 2;
+ while ((l>0) && (! seen_backwards_escape(sbuf, l))) {
+ if (sbuf[l] == '\\') {
+ if (sbuf[l-1] == '\\') {
+ last[i] = sbuf[l];
+ i++;
+ l--;
+ }
+ } else {
+ last[i] = sbuf[l];
+ i++;
+ }
+ l--;
+ }
+ last[i] = (char)0;
+ sbuf_len = l;
+ if (seen_backwards_escape(sbuf, l)) {
+ sbuf_len--;
+ }
+ return( reverse(last) );
+ } else {
+ if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) {
+ l -= 2;
+ sbuf_len = l;
+ return( "\\" );
+ } else {
+ sbuf_len--;
+ last[0] = sbuf[sbuf_len];
+ last[1] = (char)0;
+ return( last );
+ }
+ }
+ } else {
+ return( NULL );
+ }
+}
+
+/*
+ * get_html_translation - given the position of the character and its name
+ * return the device encoding for such character.
+ */
+
+char *get_html_translation (font *f, const char *name)
+{
+ int index;
+
+ if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) {
+ return( NULL );
+ } else {
+ index = f->name_to_index((char *)name);
+ if (index == 0) {
+ error("character `%s' not found", name);
+ return( NULL );
+ } else {
+ if (f->contains(index)) {
+ return( (char *)f->get_special_device_encoding(index) );
+ } else {
+ return( NULL );
+ }
+ }
+ }
+}
+
+/*
+ * to_unicode - returns a unicode translation of char, ch.
+ */
+
+static char *to_unicode (unsigned char ch)
+{
+ static char buf[20];
+
+ sprintf(buf, "&#%u;", (unsigned int)ch);
+ return( buf );
+}
+
+/*
+ * char_translate_to_html - convert a single non escaped character
+ * into the appropriate html character.
+ */
+
+int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single)
+{
+ if (and_single) {
+ int t, l;
+ char *translation;
+ char name[2];
+
+ name[0] = ch;
+ name[1] = (char)0;
+ translation = get_html_translation(f, name);
+ if ((translation == NULL) && (ch >= UNICODE_DESC_START)) {
+ translation = to_unicode(ch);
+ }
+ if (translation) {
+ l = strlen(translation);
+ t = max(0, min(l, buflen-b));
+ strncpy(&buf[b], translation, t);
+ b += t;
+ } else {
+ if (b<buflen) {
+ buf[b] = ch;
+ b++;
+ }
+ }
+ } else {
+ /*
+ * do not attempt to encode single characters
+ */
+ if (b<buflen) {
+ buf[b] = ch;
+ b++;
+ }
+ }
+ return( b );
+}
+
+/*
+ * str_translate_to_html - converts a string, str, into html text. It places
+ * the output input buffer, buf. It truncates string, str, if
+ * there is not enough space in buf.
+ * It looks up the html character encoding of single characters
+ * if, and_single, is TRUE. Characters such as < > & etc.
+ */
+
+void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single)
+{
+ char *translation;
+ int e;
+ char escaped_char[MAX_STRING_LENGTH];
+ int l;
+ int i=0;
+ int b=0;
+ int t=0;
+
+#if 0
+ if (strcmp(str, "``@,;:\\\\()[]''") == 0) {
+ stop();
+ }
+#endif
+ while (str[i] != (char)0) {
+ if ((str[i]=='\\') && (i+1<len)) {
+ i++; // skip the leading backslash
+ if (str[i] == '(') {
+ // start of escape
+ i++;
+ e = 0;
+ while ((str[i] != (char)0) &&
+ (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) {
+ if (str[i] == '\\') {
+ i++;
+ }
+ escaped_char[e] = str[i];
+ e++;
+ i++;
+ }
+ if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) {
+ i += 2;
+ }
+ escaped_char[e] = (char)0;
+ if (e > 0) {
+ translation = get_html_translation(f, escaped_char);
+ if (translation) {
+ l = strlen(translation);
+ t = max(0, min(l, buflen-b));
+ strncpy(&buf[b], translation, t);
+ b += t;
+ } else {
+ int index=f->name_to_index(escaped_char);
+
+ if (f->contains(index) && (index != 0)) {
+ buf[b] = f->get_code(index);
+ b++;
+ }
+ }
+ }
+ } else {
+ b = char_translate_to_html(f, buf, buflen, str[i], b, and_single);
+ i++;
+ }
+ } else {
+ b = char_translate_to_html(f, buf, buflen, str[i], b, and_single);
+ i++;
+ }
+ }
+ buf[min(b, buflen)] = (char)0;
+}
+
+/*
+ * set_char - adds a character into the sbuf if it is a continuation with the previous
+ * word otherwise flush the current sbuf and add character anew.
+ */
+
+void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
+{
+ unsigned char code = f->get_code(i);
+
+#if 0
+ if (code == ' ') {
+ stop();
+ }
+#endif
+ style sty(f, env->size, env->height, env->slant, env->fontno);
+ if (sty.slant != 0) {
+ if (sty.slant > 80 || sty.slant < -80) {
+ error("silly slant `%1' degrees", sty.slant);
+ sty.slant = 0;
+ }
+ }
+ if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) &&
+ (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) {
+ return;
+ } else {
+ flush_sbuf();
+ sbuf_len = 0;
+ add_to_sbuf(code, name);
+ sbuf_end_hpos = env->hpos + w;
+ sbuf_start_hpos = env->hpos;
+ sbuf_vpos = env->vpos;
+ sbuf_style = sty;
+ sbuf_kern = 0;
+ }
+}
+
+/*
+ * write_title - writes the title to this document
+ */
+
+void html_printer::write_title (int in_head)
+{
+ if (title.has_been_found) {
+ if (in_head) {
+ html.put_string("<title>");
+ html.put_string(title.text);
+ html.put_string("</title>").nl().nl();
+ } else {
+ title.has_been_written = TRUE;
+ html.put_string("<h1 align=center>");
+ html.put_string(title.text);
+ html.put_string("</h1>").nl().nl();
+ }
+ } else if (in_head) {
+ // place empty title tags to help conform to `tidy'
+ html.put_string("<title></title>").nl();
+ }
+}
+
+/*
+ * write_rule - emits a html rule tag, if the auto_rule boolean is true.
+ */
+
+static void write_rule (void)
+{
+ if (auto_rule)
+ fputs("<hr>\n", stdout);
+}
+
+void html_printer::begin_page(int n)
+{
+ page_number = n;
+#if defined(DEBUGGING)
+ html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
+#endif
+ no_of_printed_pages++;
+
+ output_style.f = 0;
+ output_style.point_size= -1;
+ output_space_code = 32;
+ output_draw_point_size = -1;
+ output_line_thickness = -1;
+ output_hpos = -1;
+ output_vpos = -1;
+ output_vpos_max = -1;
+ current_paragraph = new html_text(&html);
+#if defined(INDENTATION)
+ current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength);
+#endif
+ current_paragraph->do_para("");
+}
+
+void html_printer::end_page(int)
+{
+ flush_sbuf();
+ flush_page();
+}
+
+font *html_printer::make_font(const char *nm)
+{
+ return html_font::load_html_font(nm);
+}
+
+html_printer::~html_printer()
+{
+ current_paragraph->flush_text();
+ html.end_line();
+ html.set_file(stdout);
+ /*
+ * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying
+ * the dtd, so for the moment I'll leave this commented out.
+ * If requested we could always emit it if a command line switch
+ * was present.
+ *
+ * fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\">\n", stdout);
+ */
+ fputs("<html>\n", stdout);
+ fputs("<head>\n", stdout);
+ fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
+ fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
+ write_title(TRUE);
+ fputs("</head>\n", stdout);
+ fputs("<body>\n\n", stdout);
+ write_title(FALSE);
+ header.write_headings(stdout, FALSE);
+ write_rule();
+ {
+ extern const char *Version_string;
+ html.begin_comment("Creator : ")
+ .put_string("groff ")
+ .put_string("version ")
+ .put_string(Version_string)
+ .end_comment();
+ }
+ {
+#ifdef LONG_FOR_TIME_T
+ long
+#else
+ time_t
+#endif
+ t = time(0);
+ html.begin_comment("CreationDate: ")
+ .put_string(ctime(&t), strlen(ctime(&t))-1)
+ .end_comment();
+ }
+#if defined(DEBUGGING)
+ html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
+#endif
+ html.end_line();
+ html.end_line();
+ /*
+ * now run through the file list copying each temporary file in turn and emitting the links.
+ */
+ file_list.start_of_list();
+ while (file_list.get_file() != 0) {
+ if (fseek(file_list.get_file(), 0L, 0) < 0)
+ fatal("fseek on temporary file failed");
+ html.copy_file(file_list.get_file());
+ fclose(file_list.get_file());
+ file_list.move_next();
+ if (file_list.get_file() != 0)
+ header.write_headings(stdout, TRUE);
+ }
+ write_rule();
+ fputs("</body>\n", stdout);
+ fputs("</html>\n", stdout);
+}
+
+/*
+ * special - handle all x X requests from troff. For post-html they allow users
+ * to pass raw html commands, turn auto linked headings off/on and
+ * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
+ */
+
+void html_printer::special(char *s, const environment *env, char type)
+{
+ if (type != 'p')
+ return;
+ if (s != 0) {
+ flush_sbuf();
+ if (env->fontno >= 0) {
+ style sty(get_font_from_index(env->fontno), env->size, env->height, env->slant, env->fontno);
+ sbuf_style = sty;
+ }
+
+ if (strncmp(s, "html:", 5) == 0) {
+ int r=font::res; /* resolution of the device */
+ char buf[MAX_STRING_LENGTH];
+ font *f=sbuf_style.f;
+
+ if (f == NULL) {
+ int found=FALSE;
+
+ f = font::load_font("TR", &found);
+ }
+ str_translate_to_html(f, buf, MAX_STRING_LENGTH,
+ &s[5], strlen(s)-5, FALSE);
+
+ /*
+ * need to pass rest of string through to html output during flush
+ */
+ page_contents->add_html(&sbuf_style, buf, strlen(buf),
+ line_number,
+ env->vpos-env->size*r/72, env->hpos,
+ env->vpos , env->hpos);
+
+ /*
+ * assume that the html command has no width, if it does then hopefully troff
+ * will have fudged this in a macro by requesting that the formatting move right by
+ * the appropriate width.
+ */
+ } else if (strncmp(s, "index:", 6) == 0) {
+ cutoff_heading = atoi(&s[6]);
+ } else if (strncmp(s, "html-tag:", 9) == 0) {
+ int r=font::res; /* resolution of the device */
+
+ page_contents->add_tag(&sbuf_style, s, strlen(s),
+ line_number,
+ env->vpos-env->size*r/72, env->hpos,
+ env->vpos , env->hpos);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, "o:i:F:vd?lrn", long_options, NULL))
+ != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU post-grohtml (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'l':
+ auto_links = FALSE;
+ break;
+ case 'r':
+ auto_rule = FALSE;
+ break;
+ case 'o':
+ /* handled by pre-html */
+ break;
+ case 'i':
+ /* handled by pre-html */
+ break;
+ case 'n':
+ simple_anchors = TRUE;
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc) {
+ do_file("-");
+ } else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-vld?n] [-F dir] [files ...]\n",
+ program_name);
+}
diff --git a/contrib/groff/src/devices/grolbp/Makefile.sub b/contrib/groff/src/devices/grolbp/Makefile.sub
new file mode 100644
index 000000000000..d60008b297e9
--- /dev/null
+++ b/contrib/groff/src/devices/grolbp/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=grolbp
+MAN1=grolbp.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=lbp.o
+CCSRCS=$(srcdir)/lbp.cc
diff --git a/contrib/groff/src/devices/grolbp/charset.h b/contrib/groff/src/devices/grolbp/charset.h
new file mode 100644
index 000000000000..adc76f4282d0
--- /dev/null
+++ b/contrib/groff/src/devices/grolbp/charset.h
@@ -0,0 +1,69 @@
+// Definition of the WP54 character set
+
+char symset[] = {
+0x57,0x50,0x35,0x34,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61,
+0x72,0x64,0x65,0x2d,0x42,0x6f,0x6f,0x6b,0x00,0x41,0x76,
+0x61,0x6e,0x74,0x47,0x61,0x72,0x64,0x65,0x2d,0x44,0x65,
+0x6d,0x69,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61,0x72,
+0x64,0x65,0x2d,0x42,0x6f,0x6f,0x6b,0x4f,0x62,0x6c,0x69,
+0x71,0x75,0x65,0x00,0x41,0x76,0x61,0x6e,0x74,0x47,0x61,
+0x72,0x64,0x65,0x2d,0x44,0x65,0x6d,0x69,0x4f,0x62,0x6c,
+0x69,0x71,0x75,0x65,0x00,0x42,0x6f,0x6f,0x6b,0x6d,0x61,
+0x6e,0x2d,0x4c,0x69,0x67,0x68,0x74,0x00,0x42,0x6f,0x6f,
+0x6b,0x6d,0x61,0x6e,0x2d,0x44,0x65,0x6d,0x69,0x00,0x42,
+0x6f,0x6f,0x6b,0x6d,0x61,0x6e,0x2d,0x4c,0x69,0x67,0x68,
+0x74,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x42,0x6f,0x6f,
+0x6b,0x6d,0x61,0x6e,0x2d,0x44,0x65,0x6d,0x69,0x49,0x74,
+0x61,0x6c,0x69,0x63,0x00,0x43,0x65,0x6e,0x74,0x75,0x72,
+0x79,0x53,0x63,0x68,0x6c,0x62,0x6b,0x2d,0x52,0x6f,0x6d,
+0x61,0x6e,0x00,0x43,0x65,0x6e,0x74,0x75,0x72,0x79,0x53,
+0x63,0x68,0x6c,0x62,0x6b,0x2d,0x42,0x6f,0x6c,0x64,0x00,
+0x43,0x65,0x6e,0x74,0x75,0x72,0x79,0x53,0x63,0x68,0x6c,
+0x62,0x6b,0x2d,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x43,
+0x65,0x6e,0x74,0x75,0x72,0x79,0x53,0x63,0x68,0x6c,0x62,
+0x6b,0x2d,0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c,0x69,
+0x63,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x52,0x6f,0x6d,
+0x61,0x6e,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x42,0x6f,
+0x6c,0x64,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,0x49,0x74,
+0x61,0x6c,0x69,0x63,0x00,0x44,0x75,0x74,0x63,0x68,0x2d,
+0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,
+0x53,0x77,0x69,0x73,0x73,0x00,0x53,0x77,0x69,0x73,0x73,
+0x2d,0x42,0x6f,0x6c,0x64,0x00,0x53,0x77,0x69,0x73,0x73,
+0x2d,0x4f,0x62,0x6c,0x69,0x71,0x75,0x65,0x00,0x53,0x77,
+0x69,0x73,0x73,0x2d,0x42,0x6f,0x6c,0x64,0x4f,0x62,0x6c,
+0x69,0x71,0x75,0x65,0x00,0x53,0x77,0x69,0x73,0x73,0x2d,
+0x4e,0x61,0x72,0x72,0x6f,0x77,0x00,0x53,0x77,0x69,0x73,
+0x73,0x2d,0x4e,0x61,0x72,0x72,0x6f,0x77,0x2d,0x42,0x6f,
+0x6c,0x64,0x00,0x53,0x77,0x69,0x73,0x73,0x2d,0x4e,0x61,
+0x72,0x72,0x6f,0x77,0x2d,0x4f,0x62,0x6c,0x69,0x71,0x75,
+0x65,0x00,0x53,0x77,0x69,0x73,0x73,0x2d,0x4e,0x61,0x72,
+0x72,0x6f,0x77,0x2d,0x42,0x6f,0x6c,0x64,0x4f,0x62,0x6c,
+0x69,0x71,0x75,0x65,0x00,0x5a,0x61,0x70,0x66,0x43,0x61,
+0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x2d,
+0x52,0x6f,0x6d,0x61,0x6e,0x00,0x5a,0x61,0x70,0x66,0x43,
+0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63,
+0x2d,0x42,0x6f,0x6c,0x64,0x00,0x5a,0x61,0x70,0x66,0x43,
+0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,0x69,0x63,
+0x2d,0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x5a,0x61,0x70,
+0x66,0x43,0x61,0x6c,0x6c,0x69,0x67,0x72,0x61,0x70,0x68,
+0x69,0x63,0x2d,0x42,0x6f,0x6c,0x64,0x49,0x74,0x61,0x6c,
+0x69,0x63,0x00,0x5a,0x61,0x70,0x66,0x43,0x68,0x61,0x6e,
+0x63,0x65,0x72,0x79,0x2d,0x4d,0x65,0x64,0x69,0x75,0x6d,
+0x49,0x74,0x61,0x6c,0x69,0x63,0x00,0x00,0x09,0x00,0x0A,
+0x00,0x0B,0x00,0x0E,0x00,0x14,0x00,0x17,0x00,0x18,0x00,
+0x1F,0x00,0x20,0x00,0x36,0x00,0x37,0x00,0x38,0x00,0x45,0x00,
+0x47,0x00,0x48,0x00,0x80,0x00,0x82,0x00,0x83,0x00,0x84,
+0x00,0x85,0x00,0x87,0x00,0x8B,0x00,0x8C,0x00,0x8D,0x00,0x8E,
+0x00,0x8F,0x00,0x90,0x00,0x91,0x00,0x92,0x00,0x95,0x00,0x96,
+0x00,0x97,0x00,0x98,0x00,0x99,0x00,0x9C,0x00,0x9E,0x00,
+0x9F,0x00,0xA0,0x00,0xA1,0x00,0xA2,0x00,0xA3,0x00,0xCB,0x00,
+0xCC,0x00,0xCD,0x00,0xCE,0x00,0xD1,0x00,0xD3,0x00,0xD4,
+0x00,0xD5,0x00,0xD6,0x00,0xFA,0x00,0xFB,0x00,0xFC,0x00,0xFD,
+0x00,0xCF,0x00,0x26,0x00,0x7E,0x03,0x05,0x00,0xA5,0x00,
+0xA6,0x00,0xA8,0x00,0xAA,0x00,0xAD,0x00,0xAE,0x00,0xAF,0x00,
+0xB0,0x00,0xB1,0x00,0xB2,0x00,0xB3,0x00,0xB5,0x00,0xB6,0x00,
+0xB8,0x00,0xB9,0x00,0xBA,0x00,0xBB,0x00,0xBC,0x00,0xBE,
+0x00,0xBF,0x00,0xC0,0x00,0xC1,0x00,0xC6,0x00,0xDC,0x00,0xEB,
+0x00,0xEC,0x00,0xF2,0x00,0xF3,0x00,0x15,0x00,0x16,0x00,
+0x86
+};
diff --git a/contrib/groff/src/devices/grolbp/grolbp.man b/contrib/groff/src/devices/grolbp/grolbp.man
new file mode 100644
index 000000000000..d567c1aa8143
--- /dev/null
+++ b/contrib/groff/src/devices/grolbp/grolbp.man
@@ -0,0 +1,357 @@
+'\" t
+.\" The above line should force the use of tbl as a preprocessor
+.\" vim: set syntax=nroff :
+.\" The above line should set vim into nroff mode
+.ig
+Copyright (C) 1994-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
+Modified by Francisco Andrés Verdú <pandres@dragonet.es> for the grolbp
+program.
+..
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH GROLBP @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grolbp \- groff driver for Canon CAPSL printers (LBP-4 and LBP-8 series laser printers).
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBgrolbp 'u
+.ti \niu
+.B grolpb
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-l
+.OP \-\-landscape
+.OP \-v
+.OP \-\-version
+.OP \-c n
+.OP \-\-copies= numcopies
+.OP \-p paper_size
+.OP \-\-papersize= paper_size
+.OP \-o orientation
+.OP \-\-orientation= orientation
+.OP \-F dir
+.OP \-\-fontdir= dir
+.OP \-h
+.OP \-\-help
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.SH DESCRIPTION
+.B grolbp
+is a driver for
+.B groff
+that produces output in CAPSL and VDM format suitable for Canon LBP\-4 and
+LBP\-8 printers.
+.LP
+For compatibility with grolj4 there is an additional drawing command
+available:
+.TP
+.BI \eD'R\ dh\ dv '
+Draw a rule (i.e.\ a solid black rectangle), with one corner at the current
+position, and the diagonally opposite corner at the current position
+.RI +( dh , dv ).
+.SH OPTIONS
+Note that there can be whitespace between a one-letter option and its
+argument; on the other hand, there must be whitespace and/or an equal sign
+(`=') between a long-name option and its argument.
+.TP
+.BI \-c numcopies
+.TQ
+.BI \-\-copies= numcopies
+Print
+.I numcopies
+copies of each page.
+.TP
+.B \-l
+.TQ
+.B \-\-landscape
+Print the document with a landscape orientation.
+.TP
+.BI \-p paper_size
+.TQ
+.BI \-\-papersize= paper_size
+Set the paper size to
+.IR paper_size ,
+which must be a valid paper size description as indicated in the section
+.BR "PAPER SIZES" .
+.TP
+.BI \-o orientation
+.TQ
+.BI \-\-orientation= orientation
+Print the document with
+.I orientation
+orientation, which must be `portrait' or `landscape'.
+.TP
+.B \-v
+.TQ
+.B \-\-version
+Print the version number.
+.TP
+.BI \-F dir
+.TQ
+.BI \-\-fontdir= dir
+Prepend directory
+.IB dir /devlbp
+to the search path for font and device description files.
+.TP
+.B \-h
+.TQ
+.B \-\-help
+Print a short help text.
+.SH TYPEFACES
+The driver supports the Dutch, Swiss and Swiss-Narrow scalable typefaces,
+each one in the Regular, Bold, Italic and Bold-Italic styles.
+Additionally, the Courier and Elite monospaced typefaces at the sizes 8 and
+12 points (for Courier) resp. 8 and 10 points (for Elite) are supported,
+each one in the Regular, Bold and Italic styles.
+.PP
+The following chart summarizes the font names you can use to access these
+fonts:
+.PP
+.TS
+tab(|) allbox center;
+c c c c c
+ab c c c c
+.
+Typeface | Regular | Bold | Italic | Bold-Italic
+Dutch | TR | TB | TI | TBI
+Swiss | HR | HB | HI | HBI
+Swiss Narrow | HNR | HNB | HNI | HNBI
+Courier | CR | CB | CI |
+Elite | ER | EB | EI |
+.TE
+.PP
+.SH PAPER SIZES
+The paper size can be set in the
+.B DESC
+file or with command line options to
+.BR grolbp .
+If the paper size is specified both ways, the command line options take
+precedence over the contents of the
+.B DESC
+file (this applies to the page orientation too).
+.PP
+To set the paper size in the
+.B DESC
+file, insert in that file a line containing
+.B papersize
+.IR desired_papersize ,
+where
+.I desired_papersize
+is:
+.IP \(bu 4
+One of the recognized paper sizes: `a4', `letter', `legal' or `executive'.
+.IP \(bu 4
+A custom defined paper size, as described in the
+.B CUSTOM PAPER SIZES
+subsection below.
+.IP \(bu 4
+The name of a file (e.g.
+.IR /etc/papersize )
+whose first line must be the desired paper size in one of the above formats.
+.PP
+If there are various papersize lines in the
+.B DESC
+file, only the first valid one is used.
+.PP
+To set the paper size in the command line, add
+.sp 1
+.in +2m
+.BI \-p \ desired_papersize
+.in -2m
+.sp 1
+or
+.sp 1
+.in +2m
+.BI \-\-papersize= desired_papersize
+.in -2m
+.sp 1
+to the other
+.B grolbp
+options, where
+.B desired_papersize
+is in the same format as in the
+.B DESC
+file.
+.PP
+Paper sizes are case insensitive (i.e., `A4' is the same as `a4').
+.PP
+If no paper size is specified in the
+.B DESC
+file or the command line, a default size of A4 is used.
+.TP
+.SH CUSTOM PAPER SIZES
+Custom defined paper sizes are in the form
+.BI cust length x width
+where
+.I length
+and
+.I width
+are the dimensions of the paper you want to to use, specified in printer
+units (1/300 of an inch).
+For instance, to print in a postcard sized paper which is two inches long
+and four inches wide you can insert a line containing
+.sp 1
+.in +2m
+.B papersize cust600x1200
+.in -2m
+.sp 1
+at the beginning of the
+.B DESC
+file.
+.SH PAGE ORIENTATION
+As with the page size, the orientation of the printed page
+.RB ( portrait
+or
+.BR landscape )
+can be set in the
+.B DESC
+file or with command line options.
+It is also case insensitive.
+.PP
+To set the orientation in the
+.B DESC
+file, insert a line with the following content:
+.sp 1
+.in +2m
+.B orientation
+.RB [ portrait | landscape ]
+.in -2m
+.sp 1
+As with paper sizes, only the first valid orientation command in the
+.B DESC
+file is used.
+.PP
+To set the page orientation with command line options you can use the
+.B \-o
+or
+.B \-\-orientation
+option with the same parameters
+.RB ( portrait
+or
+.BR landscape )
+as in the
+.B DESC
+file.
+Or you can use the
+.B \-l
+option to force the pages to be printed in landscape.
+.SH FONT FILE FORMAT
+In addition to the usual commands described in
+.BR groff_font (@MAN5EXT@),
+.B grolbp
+provides the command
+.I lbpname
+which sets the font name sent to the printer when requesting this font.
+The syntax of this command is:
+.sp 1
+.in +2m
+.B lbpname
+.I printer_font_name
+.in -2m
+.IP \(bu
+For bitmapped fonts,
+.I printer_font_name
+has the form
+.sp 1
+.in +2m
+.RI N\(la base_fontname \(ra\(la font_style \(ra
+.in -2m
+.sp 1
+.I base_fontname
+is the font name as it appears in the printers font listings without the
+first letter, up to (but not including) the font size.
+.I font_style
+can be one of the letters
+.BR R ,
+.BR I ,
+or
+.BR B ,
+indicating the font styles Roman, Italic and Bold respectively.
+.IP
+For instance, if the printer's
+.I font listing A
+shows font `Nelite12I.ISO_USA', the corresponding entry in the font
+description file is
+.sp 1
+.in +2m
+.B lbpname NeliteI
+.in -2m
+.IP
+Note that you may need to modify
+.B grolbp
+to add support for new bitmapped fonts, since the available font names and
+font sizes of bitmapped fonts (as documented above) are hard-coded into the
+program.
+.IP \(bu
+For scalable fonts,
+.I printer_font_name
+is identical to the font name as it appears in the printer's
+.IR "font listing A" .
+.IP
+For instance, to select the `Swiss' font in bold style, which appears in
+the printer's
+.I font listing A
+as `Swiss-Bold', the required
+.B lbpname
+command line is
+.sp 1
+.in +2m
+.B lbpname Swiss-Bold
+.in -2m
+.sp 1
+.PP
+The argument of
+.B lbpname
+is case sensitive.
+.SH FILES
+.TP
+.B @FONTDIR@/devlbp/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devlbp/ F
+Font description file for font
+.IR F .
+.TP
+.B @MACRODIR@/lbp.tmac
+Macros for use with
+.BR grolbp .
+.SH SEE ALSO
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
+.\"
+.\" Local Variables:
+.\" mode: nroff
+.\" End:
diff --git a/contrib/groff/src/devices/grolbp/lbp.cc b/contrib/groff/src/devices/grolbp/lbp.cc
new file mode 100644
index 000000000000..69196af464db
--- /dev/null
+++ b/contrib/groff/src/devices/grolbp/lbp.cc
@@ -0,0 +1,765 @@
+// -*- C++ -*-
+/* Copyright (C) 1994, 2000, 2001 Free Software Foundation, Inc.
+ Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas
+ taken from the other groff drivers.
+
+
+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. */
+
+/*
+TODO
+
+ - Add X command to include bitmaps
+*/
+#define _GNU_SOURCE
+
+#include "driver.h"
+#include "lbp.h"
+#include "charset.h"
+
+#include "nonposix.h"
+
+static short int papersize = -1, // papersize
+ orientation = -1 , // orientation
+ paperlength = 0, // Custom Paper size
+ paperwidth = 0,
+ ncopies = 1; // Number of copies
+
+class lbp_font : public font {
+public:
+ ~lbp_font();
+ void handle_unknown_font_command(const char *command, const char *arg,
+ const char *filename, int lineno);
+ static lbp_font *load_lbp_font(const char *);
+ char *lbpname;
+ char is_scalable;
+private:
+ lbp_font(const char *);
+};
+
+class lbp_printer : public printer {
+public:
+ lbp_printer();
+ ~lbp_printer();
+ void set_char(int, font *, const environment *, int, const char *name);
+ void draw(int code, int *p, int np, const environment *env);
+ void begin_page(int);
+ void end_page(int page_length);
+ font *make_font(const char *);
+ void end_of_line();
+private:
+ void set_line_thickness(int size, int dot = 0);
+ void vdmstart();
+ void vdmflush(); // the name vdmend was already used in lbp.h
+ void setfillmode(int mode);
+ void polygon( int hpos,int vpos,int np,int *p);
+ char *font_name(const lbp_font *f, const int siz);
+
+ int fill_pattern;
+ int fill_mode;
+ int cur_hpos;
+ int cur_vpos;
+ lbp_font *cur_font;
+ int cur_size;
+ unsigned short cur_symbol_set;
+ int line_thickness;
+};
+
+// Compatibility section.
+//
+// Here we define some functions not present in some of the targets
+// platforms
+#ifndef HAVE_STRSEP
+// Solaris 8 doesn't have the strsep function
+static char *strsep(char **pcadena, const char *delim)
+{
+ char *p;
+
+ p = strtok(*pcadena,delim);
+ *pcadena = strtok(NULL,delim);
+ return p;
+
+};
+#endif
+
+#ifndef HAVE_STRDUP
+// Ditto with OS/390 and strdup
+static char *strdup(const char *s)
+{
+ char *result;
+
+ result = (char *)malloc(strlen(s)+1);
+ if (result != NULL) strcpy(result,s);
+ return result;
+
+}; // strdup
+
+#endif
+lbp_font::lbp_font(const char *nm)
+: font(nm)
+{
+}
+
+lbp_font::~lbp_font()
+{
+}
+
+lbp_font *lbp_font::load_lbp_font(const char *s)
+{
+ lbp_font *f = new lbp_font(s);
+ f->lbpname = NULL;
+ f->is_scalable = 1; // Default is that fonts are scalable
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+
+void lbp_font::handle_unknown_font_command(const char *command,
+ const char *arg,
+ const char *filename, int lineno)
+{
+ if (strcmp(command, "lbpname") == 0) {
+ if (arg == 0)
+ fatal_with_file_and_line(filename, lineno,
+ "`%1' command requires an argument",
+ command);
+ this->lbpname = new char[strlen(arg)+1];
+ strcpy(this->lbpname,arg);
+ // We Recongnize bitmaped fonts by the first character of it's name
+ if (arg[0] == 'N') this->is_scalable = 0;
+ // fprintf(stderr,"Loading font \"%s\" \n",arg);
+ }; // if (strcmp(command, "lbpname")
+ // fprintf(stderr,"Loading font %s \"%s\" in %s at %d\n",command,arg,filename,lineno);
+};
+
+static void wp54charset()
+{
+ int i;
+
+ lbpputs("\033[714;100;29;0;32;120.}");
+ for (i = 0; i < sizeof(symset) ; i++) lbpputc(symset[i]);
+ lbpputs("\033[100;0 D");
+ return ;
+};
+
+lbp_printer::lbp_printer()
+: fill_pattern(1),
+ fill_mode(0),
+ cur_hpos(-1),
+ cur_font(0),
+ cur_size(0),
+ cur_symbol_set(0),
+ line_thickness(-1)
+{
+#ifdef SET_BINARY
+ SET_BINARY(fileno(stdout));
+#endif
+ lbpinit(stdout);
+ lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
+ wp54charset(); // Define the new symbol set
+ lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
+ // Paper size handling
+ if (orientation < 0) orientation = 0;// Default orientation is portrait
+ if (papersize < 0) papersize = 14; // Default paper size is A4
+ if (papersize < 80) // standard paper
+ lbpprintf("\033[%dp",(papersize | orientation));
+ else // Custom paper
+ lbpprintf("\033[%d;%d;%dp",(papersize | orientation),\
+ paperlength,paperwidth);
+
+ // Number of copies
+ lbpprintf("\033[%dv\n",ncopies);
+
+ lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
+ lbpmoveabs(0,0);
+ lbpputs("\033[0t\033[2t");
+ lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
+ // Secondary symbol set IBMR1
+ cur_symbol_set = 0;
+};
+
+lbp_printer::~lbp_printer()
+{
+ lbpputs("\033P1y\033\\");
+ lbpputs("\033c\033<");
+}
+
+void lbp_printer::begin_page(int)
+{
+}
+
+void lbp_printer::end_page(int)
+{
+ if (vdminited()) vdmflush();
+ lbpputc('\f');
+ cur_hpos = -1;
+}
+
+void lbp_printer::end_of_line()
+{
+ cur_hpos = -1; // force absolute motion
+}
+
+char *lbp_printer::font_name(const lbp_font *f, const int siz)
+{
+ static char bfont_name[255] ; // The resulting font name
+ char type, // Italic, Roman, Bold
+ ori, // Normal or Rotated
+ *nam; // The font name without other data.
+// nam[strlen(f->lbpname)-2]; // The font name without other data.
+ int cpi; // The font size in characters per inch
+ // (Bitmaped fonts are monospaced).
+
+
+ /* Bitmap font selection is ugly in this printer, so don't expect
+ this function to be elegant. */
+
+ bfont_name[0] = 0x00;
+ if (orientation) // Landscape
+ ori = 'R';
+ else // Portrait
+ ori = 'N';
+ type = f->lbpname[strlen(f->lbpname)-1];
+ nam = new char[strlen(f->lbpname)-2];
+ strncpy(nam,&(f->lbpname[1]),strlen(f->lbpname)-2);
+ nam[strlen(f->lbpname)-2] = 0x00;
+ // fprintf(stderr,"Bitmap font '%s' %d %c %c \n",nam,siz,type,ori);
+ /* Since these fonts are avaiable only at certain sizes,
+ 10 and 17 cpi for courier, 12 and 17 cpi for elite,
+ we adjust the resulting size. */
+ cpi = 17;
+ // Fortunately there were only two bitmaped fonts shiped with the printer.
+ if (!strcasecmp(nam,"courier"))
+ { // Courier font
+ if (siz >= 12) cpi = 10;
+ else cpi = 17;
+ };
+ if (!strcasecmp(nam,"elite"))
+ { // Elite font
+ if (siz >= 10) cpi = 12;
+ else cpi = 17;
+ };
+
+ // Now that we have all the data, let's generate the font name.
+ if ((type != 'B') && (type != 'I')) // Roman font
+ sprintf(bfont_name,"%c%s%d",ori,nam,cpi);
+ else
+ sprintf(bfont_name,"%c%s%d%c",ori,nam,cpi,type);
+
+ return bfont_name;
+
+}; // lbp_printer::font_name
+
+void lbp_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
+{
+ int code = f->get_code(index);
+
+ unsigned char ch = code & 0xff;
+ unsigned short symbol_set = code >> 8;
+ if (f != cur_font) {
+ lbp_font *psf = (lbp_font *)f;
+ // fprintf(stderr,"Loading font %s \"%d\" \n",psf->lbpname,env->size);
+ if (psf->is_scalable)
+ { // Scalable font selection is different from bitmaped
+ lbpprintf("\033Pz%s.IBML\033\\\033[%d C",psf->lbpname,\
+ (int)((env->size*300)/72));
+ } else
+ { // Bitmaped font
+ lbpprintf("\033Pz%s.IBML\033\\\n",font_name(psf,env->size));
+ };
+ lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
+ cur_size = env->size;
+ cur_font = psf;
+ cur_symbol_set = 0;
+ }
+ if (symbol_set != cur_symbol_set) {
+ if ( cur_symbol_set == 3 ) {
+ // if current symbol set is Symbol we must restore the font
+ lbpprintf("\033Pz%s.IBML\033\\\033[%d C",cur_font->lbpname,\
+ (int)((env->size*300)/72));
+ }; // if ( cur_symbol_set == 3 )
+ switch (symbol_set) {
+ case 0: lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
+ break;
+ case 1: lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
+ break;
+ case 2: lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
+ break;
+ case 3: lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",\
+ (int)((env->size*300)/72));
+ lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
+ break;
+ case 4: lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
+ break;
+ }; // switch (symbol_set)
+
+// if (symbol_set == 1) lbpputs("\033(d"); // Select wp54 symbol set
+// else lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
+ cur_symbol_set = symbol_set;
+ }
+ if (env->size != cur_size) {
+
+ if (!cur_font->is_scalable)
+ lbpprintf("\033Pz%s.IBML\033\\\n",font_name(cur_font,env->size));
+ else
+ lbpprintf("\033[%d C",(int)((env->size*300)/72));
+ cur_size = env->size;
+ }
+ if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos))
+ {
+ // lbpmoveabs(env->hpos - ((5*300)/16),env->vpos );
+ lbpmoveabs(env->hpos - 64,env->vpos - 64 );
+ cur_vpos = env->vpos;
+ cur_hpos = env->hpos;
+ };
+ if ((ch & 0x7F) < 32) lbpputs("\033[1.v");
+ lbpputc(ch);
+ cur_hpos += w;
+};
+
+void
+lbp_printer::vdmstart()
+{
+ FILE *f;
+ static int changed_origin = 0;
+
+ errno = 0;
+ f = tmpfile();
+ // f = fopen("/tmp/gtmp","w+");
+ if (f == NULL) perror("Openinig temp file");
+ vdminit(f);
+ if (!changed_origin) { // we should change the origin only one time
+ changed_origin = 1;
+ vdmorigin(-63,0);
+ };
+ vdmlinewidth(line_thickness);
+
+};
+
+void
+lbp_printer::vdmflush()
+{
+ char buffer[1024];
+ int bytes_read = 1;
+
+ vdmend();
+ fflush(lbpoutput);
+ /* lets copy the vdm code to the output */
+ rewind(vdmoutput);
+ do
+ {
+ bytes_read = fread(buffer,1,sizeof(buffer),vdmoutput);
+ bytes_read = fwrite(buffer,1,bytes_read,lbpoutput);
+ } while ( bytes_read == sizeof(buffer));
+
+ fclose(vdmoutput); // This will also delete the file,
+ // since it is created by tmpfile()
+ vdmoutput = NULL;
+
+}; // lbp_printer::vdmflush
+
+inline void
+lbp_printer::setfillmode(int mode)
+{
+ if (mode != fill_mode) {
+ if (mode != 1) vdmsetfillmode(mode,1,0);
+ else vdmsetfillmode(mode,1,1); // To get black we must use white
+ // inverted
+ fill_mode = mode;
+ };
+}; // setfillmode
+
+inline void
+lbp_printer::polygon( int hpos,int vpos,int np,int *p)
+{
+ //int points[np+2],i;
+ int *points,i;
+
+ points = new int[np+2];
+ points[0] = hpos;
+ points[1] = vpos;
+/* fprintf(stderr,"Poligon (%d,%d) ", points[0],points[1]);*/
+ for (i = 0; i < np; i++) points[i+2] = p[i];
+/* for (i = 0; i < np; i++) fprintf(stderr," %d ",p[i]);
+ fprintf(stderr,"\n"); */
+ vdmpolygon((np /2) + 1,points);
+};
+
+void lbp_printer::draw(int code, int *p, int np, const environment *env)
+{
+ switch (code) {
+ case 't':
+ if (np == 0) line_thickness = 1;
+ else { // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2) {
+ error("0 or 1 argument required for thickness");
+ break;
+ } // if (np != ...
+ if (p[0] == 0) line_thickness = 1;
+ if (p[0] < 0) // Default = 1 point
+ line_thickness = (int)(env->size*30/72);
+ line_thickness = (int)((abs(p[0])*env->size)/10);
+ if ((line_thickness > 16 ) && (!vdminited()))
+ { /* for greater thickness we must use VDM */
+ vdmstart();
+ /* vdmlinewidth(line_thickness); already done in
+ * vdmstart() */
+ };
+ if (vdminited()) vdmlinewidth(line_thickness);
+ // fprintf(stderr,"\nthickness: %d == %d, size %d\n",
+ // p[0],line_thickness,env->size );
+ } // else
+ break;
+
+ case 'l': // Line
+ if (np != 2) {
+ error("2 arguments required for line");
+ break;
+ };
+ if (!vdminited()) vdmstart();
+ vdmline(env->hpos,env->vpos,p[0],p[1]);
+ /*fprintf(stderr,"\nline: %d,%d - %d,%d thickness %d == %d\n",\
+ env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],\
+ env->vpos -64 + p[1],env->size, line_thickness);*/
+ break;
+ case 'R': // Rule
+ if (np != 2) {
+ error("2 arguments required for Rule");
+ break;
+ }
+ if (vdminited()) {
+ setfillmode(fill_pattern); // Solid Rule
+ vdmrectangle(env->hpos,env->vpos,p[0],p[1]);
+ }
+ else {
+ lbpruleabs(env->hpos - 64,env->vpos -64 , p[0], p[1]);
+ cur_vpos = p[1];
+ cur_hpos = p[0];
+ };
+ fprintf(stderr,"\nrule: thickness %d == %d\n", env->size, line_thickness);
+ break;
+ case 'P': // Filled Polygon
+ if (!vdminited()) vdmstart();
+ setfillmode(fill_pattern);
+ polygon(env->hpos,env->vpos,np,p);
+ break;
+ case 'p': // Empty Polygon
+ if (!vdminited()) vdmstart();
+ setfillmode(0);
+ polygon(env->hpos,env->vpos,np,p);
+ break;
+ case 'C': // Filled Circle
+ if (!vdminited()) vdmstart();
+ // fprintf(stderr,"Circle (%d,%d) Fill %d\n",env->hpos,env->vpos,fill_pattern);
+ setfillmode(fill_pattern);
+ vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
+ break;
+ case 'c': // Empty Circle
+ if (!vdminited()) vdmstart();
+ setfillmode(0);
+ vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
+ break;
+ case 'E': // Filled Ellipse
+ if (!vdminited()) vdmstart();
+ setfillmode(fill_pattern);
+ vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
+ break;
+ case 'e': // Empty Ellipse
+ if (!vdminited()) vdmstart();
+ setfillmode(0);
+ vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
+ break;
+ case 'a': // Arc
+ if (!vdminited()) vdmstart();
+ setfillmode(0);
+ // VDM draws arcs clockwise and pic counterclockwise
+ // We must compensate for that, exchanging the starting and
+ // ending points
+ vdmvarc(env->hpos + p[0],env->vpos+p[1],\
+ int(sqrt( double((p[0]*p[0])+(p[1]*p[1])))),\
+ p[2],p[3],\
+ (-p[0]),(-p[1]),1,2);
+ break;
+ case '~': // Spline
+ if (!vdminited()) vdmstart();
+ setfillmode(0);
+ vdmspline(np/2,env->hpos,env->vpos,p);
+ break;
+ case 'f':
+ if (np != 1 && np != 2) {
+ error("1 argument required for fill");
+ break;
+ };
+ // fprintf(stderr,"Fill %d\n",p[0]);
+ if ((p[0] == 1) || (p[0] >= 1000)) { // Black
+ fill_pattern = 1;
+ break;
+ }; // if (p[0] == 1)
+ if (p[0] == 0) { // White
+ fill_pattern = 0;
+ break;
+ };
+ if ((p[0] > 1) && (p[0] < 1000))
+ {
+ if (p[0] >= 990) fill_pattern = -23;
+ else if (p[0] >= 700) fill_pattern = -28;
+ else if (p[0] >= 500) fill_pattern = -27;
+ else if (p[0] >= 400) fill_pattern = -26;
+ else if (p[0] >= 300) fill_pattern = -25;
+ else if (p[0] >= 200) fill_pattern = -22;
+ else if (p[0] >= 100) fill_pattern = -24;
+ else fill_pattern = -21;
+ }; // if (p[0] >= 0 && p[0] <= 1000)
+ break;
+ default:
+ error("unrecognised drawing command `%1'", char(code));
+ break;
+ }; // switch (code)
+ return ;
+};
+
+font *lbp_printer::make_font(const char *nm)
+{
+ return lbp_font::load_lbp_font(nm);
+}
+
+
+
+printer *make_printer()
+{
+ return new lbp_printer;
+}
+
+static struct
+{
+ const char *name;
+ int code;
+} papersizes[] =
+{{ "A4", 14 },
+{ "letter", 30 },
+{ "legal", 32 },
+{ "executive", 40 },
+};
+
+
+static int set_papersize(const char *papersize)
+{
+ int i;
+
+ // First test for a standard (i.e. supported directly by the printer)
+ // papersize
+ for (i = 0 ; i < sizeof(papersizes)/sizeof(papersizes[0]); i++)
+ {
+ if (strcasecmp(papersizes[i].name,papersize) == 0)
+ return papersizes[i].code;
+ };
+
+ // Now test for a custom papersize
+ if (strncasecmp("cust",papersize,4) == 0)
+ {
+ char *p ,
+ *p1,
+ *papsize;
+
+ p = papsize = strdup(&papersize[4]);
+ if (papsize == NULL) return -1;
+ p1 = strsep(&p,"x");
+ if (p == NULL)
+ { // let's test for an uppercase x
+ p = papsize ;
+ p1 = strsep(&p,"X");
+ if (p == NULL) { free(papsize); return -1;};
+ }; // if (p1 == NULL)
+ paperlength = atoi(p1);
+ if (paperlength == 0) { free(papsize); return -1;};
+ paperwidth = atoi(p);
+ if (paperwidth == 0) { free(papsize); return -1;};
+ free(papsize);
+ return 82;
+ }; // if (strcnasecmp("cust",papersize,4) == 0)
+
+ return -1;
+};
+
+static int handle_papersize_command(const char *arg)
+{
+ int n = set_papersize(arg);
+
+ if (n < 0)
+ { // If is not a standard nor custom paper size
+ // let's see if it's a file (i.e /etc/papersize )
+ FILE *f = fopen(arg,"r");
+ if (f != NULL)
+ { // the file exists and is readable
+ char psize[255],*p;
+ fgets(psize,254,f);
+ fclose(f);
+ // set_papersize doesn't like the trailing \n
+ p = psize; while (*p) p++;
+ if (*(--p) == '\n') *p = 0x00;
+
+ n = set_papersize(psize);
+ }; // if (f != NULL)
+ }; // if (n < 0)
+
+ return n;
+}; // handle_papersize_command
+
+
+static void handle_unknown_desc_command(const char *command, const char *arg,
+ const char *filename, int lineno)
+{
+ // papersize command
+ if (strcasecmp(command, "papersize") == 0) {
+ // We give priority to command line options
+ if (papersize > 0) return;
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`papersize' command requires an argument");
+ else
+ {
+ int n = handle_papersize_command(arg);
+ if (n < 0)
+ error_with_file_and_line(filename, lineno,
+ "unknown paper size `%1'", arg);
+ else
+ papersize = n;
+
+ }; // if (arg == 0) ... else ...
+ }; // if (strcasecmp(command, "papersize")
+
+ // orientation command
+ if (strcasecmp(command, "orientation") == 0) {
+ // We give priority to command line options
+ if (orientation > 0) return;
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`papersize' command requires an argument");
+ else {
+ if (strcasecmp(arg,"portrait") == 0) orientation = 0;
+ else { if (strcasecmp(arg,"landscape") == 0) orientation = 1;
+ else error_with_file_and_line(filename, lineno,
+ "`orientation' command requires an argument");
+ };
+ }; // if (arg == 0) ... else ...
+ }; // if (strcasecmp(command, "orientation") == 0)
+};
+
+static struct option long_options[] = {
+ { "orientation", required_argument, NULL, 'o' },
+ { "version", no_argument, NULL, 'v' },
+ { "copies", required_argument, NULL, 'c' },
+ { "landscape", no_argument, NULL, 'l' },
+ { "papersize", required_argument, NULL, 'p' },
+ { "fontdir", required_argument, NULL, 'F' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or] "\
+ " [files ...]\n"\
+ " -o --orientation=[portrait|landscape]\n"\
+ " -v --version\n"\
+ " -c --copies=numcopies\n"\
+ " -l --landscape\n"\
+ " -p --papersize=paper_size\n"\
+ " -F --fontdir=dir\n"\
+ " -h --help\n",
+ program_name);
+}; // usage
+
+int main(int argc, char **argv)
+{
+ if (program_name == NULL) program_name = strdup(argv[0]);
+
+ font::set_unknown_desc_command_handler(handle_unknown_desc_command);
+ // command line parsing
+ int c = 0;
+ int option_index = 0;
+
+ while (c >= 0 )
+ {
+ c = getopt_long (argc, argv, "F:p:lvo:c:h",\
+ long_options, &option_index);
+ switch (c) {
+ case 'F' : font::command_line_font_dir(optarg);
+ break;
+ case 'p' : {
+ int n = handle_papersize_command(optarg);
+ if (n < 0)
+ error("unknown paper size `%1'", optarg);
+ else
+ papersize = n;
+ break;
+ };
+ case 'l' : orientation = 1;
+ break;
+ case 'v' : {
+ extern const char *Version_string;
+ printf("GNU grolbp (groff) version %s\n",
+ Version_string);
+ exit(0);
+ break;
+ };
+ case 'o' : {
+ if (strcasecmp(optarg,"portrait") == 0)
+ orientation = 0;
+ else {
+ if (strcasecmp(optarg,"landscape") == 0)
+ orientation = 1;
+ else
+ error("unknown orientation '%1'", optarg);
+ };
+ break;
+ };
+ case 'c' : {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if ((n <= 0) && (ptr == optarg))
+ error("argument for -c must be a positive integer");
+ else if (n <= 0 || n > 32767)
+ error("out of range argument for -c");
+ else
+ ncopies = unsigned(n);
+ break;
+ }
+ case 'h' : usage(stdout);
+ exit(0);
+ break;
+ case '?' : usage(stderr);
+ exit(1);
+ break;
+
+ }; // switch (c)
+ }; // while (c > 0 )
+
+ if (optind >= argc)
+ do_file("-");
+
+ while (optind < argc) {
+ do_file(argv[optind++]);
+ };
+
+ lbpputs("\033c\033<");
+ return 0;
+};
diff --git a/contrib/groff/src/devices/grolbp/lbp.h b/contrib/groff/src/devices/grolbp/lbp.h
new file mode 100644
index 000000000000..6a11b198a8ad
--- /dev/null
+++ b/contrib/groff/src/devices/grolbp/lbp.h
@@ -0,0 +1,511 @@
+// -*- C -*-
+/* Copyright (C) 1994, 2000 Free Software Foundation, Inc.
+ Written by Francisco Andrés Verdú <pandres@dragonet.es>
+
+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 file contains a set of utility functions to use canon CAPSL printers
+ * (lbp-4 and lbp-8 series printers) */
+
+#ifndef LBP_H
+#define LBP_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static FILE *lbpoutput = NULL;
+static FILE *vdmoutput = NULL;
+
+static inline void
+lbpinit(FILE *outfile)
+{
+ lbpoutput = outfile;
+};
+
+
+static inline void
+lbpprintf(char *format, ... )
+{ /* Taken from cjet */
+ va_list stuff;
+
+ va_start(stuff, format);
+ vfprintf(lbpoutput, format, stuff);
+ va_end(stuff);
+};
+
+static inline void
+lbpputs(char *data)
+{
+ fputs(data,lbpoutput);
+};
+
+static inline void
+lbpputc(char c)
+{
+ fputc(c,lbpoutput);
+};
+
+
+static inline void
+lbpsavestatus(int index )
+{
+ fprintf(lbpoutput,"\033[%d%%y",index);
+};
+
+static inline void
+lbprestorestatus(int index )
+{
+ fprintf(lbpoutput,"\033[%d%cz",index ,'%');
+};
+
+static inline void
+lbpsavepos(int index)
+{
+ fprintf(lbpoutput,"\033[1;%d;0x",index);
+};
+
+static inline void
+lbprestorepos(int index)
+{
+ fprintf(lbpoutput,"\033[0;%d;0x",index);
+};
+
+static inline void
+lbprestoreposx(int index)
+{
+ fprintf(lbpoutput,"\033[0;%d;1x",index);
+};
+
+static inline void
+lbpmoverel(int despl, char direction)
+{
+ fprintf(lbpoutput,"\033[%d%c",despl,direction);
+};
+
+static inline void
+lbplinerel(int width,int despl,char direction )
+{
+ fprintf(lbpoutput,"\033[%d;0;9{\033[%d%c\033[9}",width,despl,direction);
+};
+
+static inline void
+lbpmoveabs(int x, int y)
+{
+ fprintf(lbpoutput,"\033[%d;%df",y,x);
+};
+
+static inline void
+lbplineto(int x,int y, int width )
+{
+ fprintf(lbpoutput,"\033[%d;0;9{",width);
+ lbpmoveabs(x,y);
+ fprintf(lbpoutput,"\033[9}\n");
+};
+
+static inline void
+lbpruleabs(int x, int y, int hsize, int vsize)
+{
+ lbpmoveabs(x,y);
+ fprintf(lbpoutput,"\033[0;9;000s");
+ lbpmoveabs(x+hsize,y+vsize);
+ fprintf(lbpoutput,"\033[9r");
+};
+
+static inline void vdmprintf(char *format, ... );
+
+static inline char *
+vdmnum(int num,char *result)
+{
+ char b1,b2,b3;
+ char *p = result;
+ int nm;
+
+ nm = abs(num);
+ /* First byte 1024 - 32768 */
+ b1 = ((nm >> 10) & 0x3F);
+ if (b1) *p++ = b1 | 0x40;
+
+ /* Second Byte 16 - 1024 */
+ b2 = ((nm >> 4) & 0x3F);
+ if ( b1 || b2) *p++= b2 | 0x40;
+
+ /* Third byte 0 - 15 */
+ b3 = ((nm & 0x0F) | 32);
+ if (num >= 0) b3 |= 16;
+ *p++ = b3;
+ *p = 0x00; /* End of the resulting string */
+ return result;
+};
+
+static inline void
+vdmorigin(int newx, int newy)
+{
+ char nx[4],ny[4];
+
+ vdmprintf("}\"%s%s\x1e",vdmnum(newx,nx),vdmnum(newy,ny));
+}; /* vdmorigin */
+
+
+static inline FILE *
+vdminit(FILE *vdmfile)
+{
+ char scale[4],size[4],lineend[4];
+
+/* vdmoutput = tmpfile();*/
+ vdmoutput = vdmfile;
+ /* Initialize the VDM mode */
+ vdmprintf("\033[0&}#GROLBP\x1e!0%s%s\x1e$\x1e}F%s\x1e",\
+ vdmnum(-3,scale),vdmnum(1,size),vdmnum(1,lineend));
+ return vdmoutput;
+
+};
+
+static inline void
+vdmend()
+{
+ vdmprintf("}p\x1e");
+};
+
+static inline void
+vdmprintf(char *format, ... )
+{ /* Taken from cjet */
+ va_list stuff;
+
+ if (vdmoutput == NULL) vdminit(tmpfile());
+ va_start(stuff, format);
+ vfprintf(vdmoutput, format, stuff);
+ va_end(stuff);
+};
+
+static inline void
+vdmsetfillmode(int pattern,int perimeter, int inverted)
+{
+ char patt[4],perim[4],
+ rot[4], /* rotation */
+ espejo[4], /* espejo */
+ inv[4]; /* Inverted */
+
+ vdmprintf("I%s%s%s%s%s\x1e",vdmnum(pattern,patt),\
+ vdmnum(perimeter,perim),vdmnum(0,rot),
+ vdmnum(0,espejo),vdmnum(inverted,inv));
+};
+
+static inline void
+vdmcircle(int centerx, int centery, int radius)
+{
+ char x[4],y[4],rad[4];
+
+ vdmprintf("5%s%s%s\x1e",vdmnum(centerx,x),vdmnum(centery,y),\
+ vdmnum(radius,rad));
+};
+
+static inline void
+vdmaarc(int centerx, int centery, int radius,int startangle,int angle,int style,int arcopen)
+{
+ char x[4],y[4],rad[4],stx[4],sty[4],styl[4],op[4];
+
+ vdmprintf("}6%s%s%s%s%s%s%s\x1e",vdmnum(arcopen,op),\
+ vdmnum(centerx,x),vdmnum(centery,y),\
+ vdmnum(radius,rad),vdmnum(startangle,stx),vdmnum(angle,sty),\
+ vdmnum(style,styl));
+};
+
+static inline void
+vdmvarc(int centerx, int centery,int radius, int startx, int starty, int endx, int endy,\
+ int style,int arcopen)
+{
+ char x[4],y[4],rad[4],stx[4],sty[4],enx[4],eny[4],styl[4],op[4];
+
+ vdmprintf("}6%s%s%s%s%s%s%s%s\x1e",vdmnum(arcopen,op),\
+ vdmnum(centerx,x),vdmnum(centery,y),\
+ vdmnum(radius,rad),vdmnum(startx,stx),vdmnum(starty,sty),\
+ vdmnum(endx,enx),vdmnum(endy,eny),vdmnum(style,styl));
+};
+
+static inline void
+vdmellipse(int centerx, int centery, int radiusx, int radiusy,int rotation)
+{
+ char x[4],y[4],radx[4],rady[4],rotat[4];
+
+ vdmprintf("}7%s%s%s%s%s\x1e\n",vdmnum(centerx,x),vdmnum(centery,y),\
+ vdmnum(radiusx,radx),vdmnum(radiusy,rady),\
+ vdmnum(rotation,rotat));
+};
+
+static inline void
+vdmsetlinetype(int lintype)
+{
+ char ltyp[4], expfact[4];
+
+ vdmprintf("E1%s%s\x1e",vdmnum(lintype,ltyp),vdmnum(1,expfact));
+
+};
+
+static inline void
+vdmsetlinestyle(int lintype, int pattern,int unionstyle)
+{
+ char patt[4],ltip[4],
+ rot[4], /* rotation */
+ espejo[4], /* espejo */
+ in[4]; /* Inverted */
+
+ vdmprintf("}G%s%s%s%s%s\x1e",vdmnum(lintype,ltip),\
+ vdmnum(pattern,patt),vdmnum(0,rot),
+ vdmnum(0,espejo),vdmnum(0,in));
+ vdmprintf("}F%s",vdmnum(unionstyle,rot));
+};
+
+static inline void
+vdmlinewidth(int width)
+{
+ char wh[4];
+
+ vdmprintf("F1%s\x1e",vdmnum(width,wh));
+};
+
+static inline void
+vdmrectangle(int origx, int origy,int dstx, int dsty)
+{
+ char xcoord[4],ycoord[4],sdstx[4],sdsty[4];
+
+ vdmprintf("}:%s%s%s%s\x1e\n",vdmnum(origx,xcoord),vdmnum(dstx,sdstx),\
+ vdmnum(origy,ycoord),vdmnum(dsty,sdsty));
+}; /* polyline */
+
+static inline void
+vdmpolyline(int numpoints, int *points)
+{
+ int i,*p = points;
+ char xcoord[4],ycoord[4];
+
+ if (numpoints < 2) return;
+ vdmprintf("1%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord));
+ p += 2;
+ for (i = 1; i < numpoints ; i++) {
+ vdmprintf("%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord));
+ p += 2;
+ }; /* for */
+ vdmprintf("\x1e\n");
+}; /* polyline */
+
+static inline void
+vdmpolygon(int numpoints, int *points)
+{
+ int i,*p = points;
+ char xcoord[4],ycoord[4];
+
+ if (numpoints < 2) return;
+ vdmprintf("2%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord));
+ p += 2;
+ for (i = 1; i < numpoints ; i++) {
+ vdmprintf("%s%s",vdmnum(*p,xcoord),vdmnum(*(p+1),ycoord));
+ p += 2;
+ }; /* for */
+ vdmprintf("\x1e\n");
+
+}; /* vdmpolygon */
+
+
+/************************************************************************
+ * Highter level auxiliary functions *
+ ************************************************************************/
+static inline int
+vdminited()
+{
+ return (vdmoutput != NULL);
+}; /* vdminited */
+
+
+static inline void
+vdmline(int startx, int starty, int sizex, int sizey)
+{
+ int points[4];
+
+ points[0] = startx;
+ points[1] = starty;
+ points[2] = sizex;
+ points[3] = sizey;
+
+ vdmpolyline(2,points);
+
+};
+
+/*#define THRESHOLD .05 */ /* inch */
+#define THRESHOLD 1 /* points (1/300 inch) */
+static inline void
+splinerel(double px,double py,int flush)
+{
+ static int lx = 0 ,ly = 0;
+ static float pend = 0.0;
+ static int dy = 0, despx = 0, despy = 0, sigpend = 0;
+ int dxnew ,dynew, sg;
+ char xcoord[4],ycoord[4];
+ float npend ;
+
+ if (flush == -1) {lx = (int)px; ly = (int)py; return;};
+
+ if (flush == 0) {
+ dxnew = (int)px -lx;
+ dynew = (int)py -ly;
+ if ((dxnew == 0) && (dynew == 0)) return;
+ sg = (dxnew < 0)? -1 : 0;
+/* fprintf(stderr,"s (%d,%d) (%d,%d)\n",dxnew,dynew,despx,despy);*/
+ if (dynew == 0) {
+ despx = dxnew;
+ if ((sg == sigpend) && (dy == 0)){
+ return;
+ };
+ dy = 0;
+ }
+ else {
+ dy = 1;
+ npend = (1.0*dxnew)/dynew;
+ if (( npend == pend) && (sigpend == sg))
+ { despy = dynew; despx = dxnew; return; }
+ else
+ { sigpend = sg;
+ pend = npend;
+ }; /* else (( npend == pend) && ... */
+ }; /* else (if (dynew == 0)) */
+ }; /* if (!flush ) */
+
+ /* if we've changed direction we must draw the line */
+/* fprintf(stderr," (%d) %.2f,%.2f\n",flush,(float)px,(float)py);*/
+ if ((despx != 0) || (despy != 0)) vdmprintf("%s%s",vdmnum(despx,xcoord),\
+ vdmnum(despy,ycoord));
+ /*if ((despx != 0) || (despy != 0)) fprintf(stderr,"2
+ *%d,%d\n",despx,despy);*/
+ if (flush) {
+ dxnew = dy = despx = despy = 0;
+ return;
+ }; /* if (flush) */
+ dxnew -= despx;
+ dynew -= despy;
+ if ((dxnew != 0) || (dynew != 0)) vdmprintf("%s%s",vdmnum(dxnew,xcoord),\
+ vdmnum(dynew,ycoord));
+
+/* if ((dxnew != 0) || (dynew != 0)) fprintf(stderr,"3
+ * %d,%d\n",dxnew,dynew);*/
+ lx = (int)px; ly = (int)py;
+ dxnew = dy = despx = despy = 0;
+
+}; /* splinerel */
+
+/**********************************************************************
+ * The following code to draw splines is adapted from the transfig package
+ */
+static void
+quadratic_spline(double a1,double b1, double a2, double b2, \
+ double a3, double b3, double a4, double b4)
+{
+ double x1, y1, x4, y4;
+ double xmid, ymid;
+
+ x1 = a1; y1 = b1;
+ x4 = a4; y4 = b4;
+ xmid = (a2 + a3)/2.0;
+ ymid = (b2 + b3)/2.0;
+ if ((fabs(x1 - xmid) < THRESHOLD) && (fabs(y1 - ymid) < THRESHOLD)) {
+ splinerel(xmid,ymid,0);
+/* fprintf(tfp, "PA%.4f,%.4f;\n", xmid, ymid);*/
+ }
+ else {
+ quadratic_spline(x1, y1, ((x1+a2)/2.0), ((y1+b2)/2.0),
+ ((3.0*a2+a3)/4.0), ((3.0*b2+b3)/4.0), xmid, ymid);
+ }
+
+ if ((fabs(xmid - x4) < THRESHOLD) && (fabs(ymid - y4) < THRESHOLD)) {
+ splinerel(x4,y4,0);
+/* fprintf(tfp, "PA%.4f,%.4f;\n", x4, y4);*/
+ }
+ else {
+ quadratic_spline(xmid, ymid, ((a2+3.0*a3)/4.0), ((b2+3.0*b3)/4.0),
+ ((a3+x4)/2.0), ((b3+y4)/2.0), x4, y4);
+ };
+}; /* quadratic_spline */
+
+#define XCOORD(i) numbers[(2*i)]
+#define YCOORD(i) numbers[(2*i)+1]
+static void
+vdmspline(int numpoints, int ox,int oy, int *numbers)
+{
+ double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
+ double x1, y1, x2, y2;
+ char xcoord[4],ycoord[4];
+ int i;
+
+ /*p = s->points;
+ x1 = p->x/ppi;*/
+ x1 = ox;
+ y1 = oy;
+/* p = p->next;
+ x2 = p->x/ppi;
+ y2 = p->y/ppi;*/
+ x2 = ox + XCOORD(0);
+ y2 = oy + YCOORD(0);
+ cx1 = (x1 + x2)/2.0;
+ cy1 = (y1 + y2)/2.0;
+ cx2 = (x1 + 3.0*x2)/4.0;
+ cy2 = (y1 + 3.0*y2)/4.0;
+
+/* fprintf(stderr,"Spline %d (%d,%d)\n",numpoints,(int)x1,(int)y1);*/
+ vdmprintf("1%s%s",vdmnum((int)x1,xcoord),vdmnum((int)y1,ycoord));
+ splinerel(x1,y1,-1);
+ splinerel(cx1,cy1,0);
+/* fprintf(tfp, "PA%.4f,%.4f;PD%.4f,%.4f;\n",
+ x1, y1, cx1, cy1);*/
+
+ /*for (p = p->next; p != NULL; p = p->next) {*/
+ for (i = 1; i < (numpoints); i++) {
+ x1 = x2;
+ y1 = y2;
+/* x2 = p->x/ppi;
+ y2 = p->y/ppi;*/
+ x2 = x1 + XCOORD(i);
+ y2 = y1 + YCOORD(i);
+ cx3 = (3.0*x1 + x2)/4.0;
+ cy3 = (3.0*y1 + y2)/4.0;
+ cx4 = (x1 + x2)/2.0;
+ cy4 = (y1 + y2)/2.0;
+ /* fprintf(stderr,"Point (%d,%d) - (%d,%d)\n",(int)x1,(int)(y1),(int)x2,(int)y2);*/
+ quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
+ cx1 = cx4;
+ cy1 = cy4;
+ cx2 = (x1 + 3.0*x2)/4.0;
+ cy2 = (y1 + 3.0*y2)/4.0;
+ }
+ x1 = x2;
+ y1 = y2;
+/* p = s->points->next;
+ x2 = p->x/ppi;
+ y2 = p->y/ppi;*/
+ x2 = ox + XCOORD(0);
+ y2 = oy + YCOORD(0);
+ cx3 = (3.0*x1 + x2)/4.0;
+ cy3 = (3.0*y1 + y2)/4.0;
+ cx4 = (x1 + x2)/2.0;
+ cy4 = (y1 + y2)/2.0;
+ splinerel(x1,y1,0);
+ splinerel(x1,y1,1);
+ /*vdmprintf("%s%s",vdmnum((int)(x1-lx),xcoord),\
+ vdmnum((int)(y1-ly),ycoord));*/
+ vdmprintf("\x1e\n");
+/* fprintf(tfp, "PA%.4f,%.4f;PU;\n", x1, y1);*/
+
+
+}; /* vdmspline */
+
+
+#endif
diff --git a/contrib/groff/src/devices/grolj4/Makefile.sub b/contrib/groff/src/devices/grolj4/Makefile.sub
new file mode 100644
index 000000000000..bbb0cff0dc5a
--- /dev/null
+++ b/contrib/groff/src/devices/grolj4/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=grolj4
+MAN1=grolj4.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=lj4.o
+CCSRCS=$(srcdir)/lj4.cc
diff --git a/contrib/groff/src/devices/grolj4/grolj4.man b/contrib/groff/src/devices/grolj4/grolj4.man
new file mode 100644
index 000000000000..891d7dc99e83
--- /dev/null
+++ b/contrib/groff/src/devices/grolj4/grolj4.man
@@ -0,0 +1,144 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1994-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH GROLJ4 @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grolj4 \- groff driver for HP Laserjet 4 family
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBgrolj4 'u
+.ti \niu
+.B grolj4
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-lv
+.OP \-d \fR[\fPn\fR]\fP
+.OP \-c n
+.OP \-p paper_size
+.OP \-w n
+.OP \-F dir
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B grolj4
+is a driver for
+.B groff
+that produces output in PCL5 format suitable for an HP Laserjet 4 printer.
+.LP
+There is an additional drawing command available:
+.TP
+.BI \eD'R\ dh\ dv '
+Draw a rule (solid black rectangle), with one corner
+at the current position, and the diagonally opposite corner
+at the current position
+.RI +( dh , dv ).
+Afterwards the current position will be at the opposite corner. This
+generates a PCL fill rectangle command, and so will work on
+printers that do not support HPGL/2 unlike the other
+.B \eD
+commands.
+.SH OPTIONS
+.TP
+.BI \-c n
+Print
+.I n
+copies of each page.
+.TP
+.B \-l
+Print the document with a landscape orientation.
+.TP
+.BI "\-d [" n ]
+Use duplex mode
+.IR n :
+1\ is long-side binding; 2\ is short-side binding;
+default is\ 1.
+.TP
+.BI \-p size
+Set the paper size to
+.IR size ,
+which must be one of
+letter, legal, executive, a4, com10, monarch, c5, b5, dl.
+.TP
+.B \-v
+Print the version number.
+.TP
+.BI \-w n
+Set the default line thickness to
+.I n
+thousandths of an em.
+.TP
+.BI \-F dir
+Prepend directory
+.IB dir /devlj4
+to the search path for font and device description files.
+.LP
+The following four commands are available additionally in the
+.B DESC
+file:
+.TP
+.BI pclweight \ N
+The integer value
+.I N
+must be in the range -7 to +7; default is 0.
+.TP
+.BI pclstyle \ N
+The integer value
+.I N
+must be in the range 0 to 32767; default is 0.
+.TP
+.BI pclproportional \ N
+A boolean flag which can be either 0 or 1; default is 0.
+.TP
+.BI pcltypeface \ N
+The integer value
+.I N
+must be in the range 0 to 65535; default is 0.
+.SH FILES
+.TP
+.B @FONTDIR@/devlj4/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devlj4/ F
+Font description file for font
+.IR F .
+.TP
+.B @MACRODIR@/lj4.tmac
+Macros for use with
+.BR grolj4 .
+.SH BUGS
+Small dots.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/devices/grolj4/lj4.cc b/contrib/groff/src/devices/grolj4/lj4.cc
new file mode 100644
index 000000000000..9fbc6afa6e05
--- /dev/null
+++ b/contrib/groff/src/devices/grolj4/lj4.cc
@@ -0,0 +1,710 @@
+// -*- C++ -*-
+/* Copyright (C) 1994, 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. */
+
+/*
+TODO
+
+option to use beziers for circle/ellipse/arc
+option to use lines for spline (for LJ3)
+left/top offset registration
+output bin selection option
+paper source option
+output non-integer parameters using fixed point numbers
+X command to insert contents of file
+X command to specify inline escape sequence (how to specify unprintable chars?)
+X command to include bitmap graphics
+*/
+
+#include "driver.h"
+#include "nonposix.h"
+
+static struct {
+ const char *name;
+ int code;
+ // at 300dpi
+ int x_offset_portrait;
+ int x_offset_landscape;
+} paper_table[] = {
+ { "letter", 2, 75, 60 },
+ { "legal", 3, 75, 60 },
+ { "executive", 1, 75, 60 },
+ { "a4", 26, 71, 59 },
+ { "com10", 81, 75, 60 },
+ { "monarch", 80, 75, 60 },
+ { "c5", 91, 71, 59 },
+ { "b5", 100, 71, 59 },
+ { "dl", 90, 71, 59 },
+};
+
+static int paper_size = -1;
+static int landscape_flag = 0;
+static int duplex_flag = 0;
+
+// An upper limit on the paper size in centipoints,
+// used for setting HPGL picture frame.
+#define MAX_PAPER_WIDTH (12*720)
+#define MAX_PAPER_HEIGHT (17*720)
+
+// Dotted lines that are thinner than this don't work right.
+#define MIN_DOT_PEN_WIDTH .351
+
+#ifndef DEFAULT_LINE_WIDTH_FACTOR
+// in ems/1000
+#define DEFAULT_LINE_WIDTH_FACTOR 40
+#endif
+
+const int DEFAULT_HPGL_UNITS = 1016;
+int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
+unsigned ncopies = 0; // 0 means don't send ncopies command
+
+class lj4_font : public font {
+public:
+ ~lj4_font();
+ void handle_unknown_font_command(const char *command, const char *arg,
+ const char *filename, int lineno);
+ static lj4_font *load_lj4_font(const char *);
+ int weight;
+ int style;
+ int proportional;
+ int typeface;
+private:
+ lj4_font(const char *);
+};
+
+lj4_font::lj4_font(const char *nm)
+: font(nm), weight(0), style(0), proportional(0), typeface(0)
+{
+}
+
+lj4_font::~lj4_font()
+{
+}
+
+lj4_font *lj4_font::load_lj4_font(const char *s)
+{
+ lj4_font *f = new lj4_font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+static struct {
+ const char *s;
+ int lj4_font::*ptr;
+ int min;
+ int max;
+} command_table[] = {
+ { "pclweight", &lj4_font::weight, -7, 7 },
+ { "pclstyle", &lj4_font::style, 0, 32767 },
+ { "pclproportional", &lj4_font::proportional, 0, 1 },
+ { "pcltypeface", &lj4_font::typeface, 0, 65535 },
+};
+
+void lj4_font::handle_unknown_font_command(const char *command,
+ const char *arg,
+ const char *filename, int lineno)
+{
+ for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++) {
+ if (strcmp(command, command_table[i].s) == 0) {
+ if (arg == 0)
+ fatal_with_file_and_line(filename, lineno,
+ "`%1' command requires an argument",
+ command);
+ char *ptr;
+ long n = strtol(arg, &ptr, 10);
+ if (n == 0 && ptr == arg)
+ fatal_with_file_and_line(filename, lineno,
+ "`%1' command requires numeric argument",
+ command);
+ if (n < command_table[i].min) {
+ error_with_file_and_line(filename, lineno,
+ "argument for `%1' command must not be less than %2",
+ command, command_table[i].min);
+ n = command_table[i].min;
+ }
+ else if (n > command_table[i].max) {
+ error_with_file_and_line(filename, lineno,
+ "argument for `%1' command must not be greater than %2",
+ command, command_table[i].max);
+ n = command_table[i].max;
+ }
+ this->*command_table[i].ptr = int(n);
+ break;
+ }
+ }
+}
+
+class lj4_printer : public printer {
+public:
+ lj4_printer();
+ ~lj4_printer();
+ void set_char(int, font *, const environment *, int, const char *name);
+ void draw(int code, int *p, int np, const environment *env);
+ void begin_page(int);
+ void end_page(int page_length);
+ font *make_font(const char *);
+ void end_of_line();
+private:
+ void set_line_thickness(int size, int dot = 0);
+ void hpgl_init();
+ void hpgl_start();
+ void hpgl_end();
+ int moveto(int hpos, int vpos);
+ int moveto1(int hpos, int vpos);
+
+ int cur_hpos;
+ int cur_vpos;
+ lj4_font *cur_font;
+ int cur_size;
+ unsigned short cur_symbol_set;
+ int x_offset;
+ int line_thickness;
+ double pen_width;
+ double hpgl_scale;
+ int hpgl_inited;
+};
+
+inline
+int lj4_printer::moveto(int hpos, int vpos)
+{
+ if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
+ return moveto1(hpos, vpos);
+ else
+ return 1;
+}
+
+inline
+void lj4_printer::hpgl_start()
+{
+ fputs("\033%1B", stdout);
+}
+
+inline
+void lj4_printer::hpgl_end()
+{
+ fputs(";\033%0A", stdout);
+}
+
+lj4_printer::lj4_printer()
+: cur_hpos(-1),
+ cur_font(0),
+ cur_size(0),
+ cur_symbol_set(0),
+ line_thickness(-1),
+ pen_width(-1.0),
+ hpgl_inited(0)
+{
+ if (7200 % font::res != 0)
+ fatal("invalid resolution %1: resolution must be a factor of 7200",
+ font::res);
+ fputs("\033E", stdout); // reset
+ if (font::res != 300)
+ printf("\033&u%dD", font::res); // unit of measure
+ if (ncopies > 0)
+ printf("\033&l%uX", ncopies);
+ if (paper_size < 0)
+ paper_size = 0; // default to letter
+ printf("\033&l%dA" // paper size
+ "\033&l%dO" // orientation
+ "\033&l0E", // no top margin
+ paper_table[paper_size].code,
+ landscape_flag != 0);
+ if (landscape_flag)
+ x_offset = paper_table[paper_size].x_offset_landscape;
+ else
+ x_offset = paper_table[paper_size].x_offset_portrait;
+ x_offset = (x_offset * font::res) / 300;
+ if (duplex_flag)
+ printf("\033&l%dS", duplex_flag);
+}
+
+lj4_printer::~lj4_printer()
+{
+ fputs("\033E", stdout);
+}
+
+void lj4_printer::begin_page(int)
+{
+}
+
+void lj4_printer::end_page(int)
+{
+ putchar('\f');
+ cur_hpos = -1;
+}
+
+void lj4_printer::end_of_line()
+{
+ cur_hpos = -1; // force absolute motion
+}
+
+inline
+int is_unprintable(unsigned char c)
+{
+ return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
+}
+
+void lj4_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
+{
+ int code = f->get_code(index);
+
+ unsigned char ch = code & 0xff;
+ unsigned short symbol_set = code >> 8;
+ if (symbol_set != cur_symbol_set) {
+ printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
+ cur_symbol_set = symbol_set;
+ }
+ if (f != cur_font) {
+ lj4_font *psf = (lj4_font *)f;
+ // FIXME only output those that are needed
+ printf("\033(s%dp%ds%db%dT",
+ psf->proportional,
+ psf->style,
+ psf->weight,
+ psf->typeface);
+ if (!psf->proportional || !cur_font || !cur_font->proportional)
+ cur_size = 0;
+ cur_font = psf;
+ }
+ if (env->size != cur_size) {
+ if (cur_font->proportional) {
+ static const char *quarters[] = { "", ".25", ".5", ".75" };
+ printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
+ }
+ else {
+ double pitch = double(font::res)/w;
+ // PCL uses the next largest pitch, so round it down.
+ pitch = floor(pitch*100.0)/100.0;
+ printf("\033(s%.2fH", pitch);
+ }
+ cur_size = env->size;
+ }
+ if (!moveto(env->hpos, env->vpos))
+ return;
+ if (is_unprintable(ch))
+ fputs("\033&p1X", stdout);
+ putchar(ch);
+ cur_hpos += w;
+}
+
+int lj4_printer::moveto1(int hpos, int vpos)
+{
+ if (hpos < x_offset || vpos < 0)
+ return 0;
+ fputs("\033*p", stdout);
+ if (cur_hpos < 0)
+ printf("%dx%dY", hpos - x_offset, vpos);
+ else {
+ if (cur_hpos != hpos)
+ printf("%s%d%c", hpos > cur_hpos ? "+" : "",
+ hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
+ if (cur_vpos != vpos)
+ printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
+ }
+ cur_hpos = hpos;
+ cur_vpos = vpos;
+ return 1;
+}
+
+void lj4_printer::draw(int code, int *p, int np, const environment *env)
+{
+ switch (code) {
+ case 'R':
+ {
+ if (np != 2) {
+ error("2 arguments required for rule");
+ break;
+ }
+ int hpos = env->hpos;
+ int vpos = env->vpos;
+ int hsize = p[0];
+ int vsize = p[1];
+ if (hsize < 0) {
+ hpos += hsize;
+ hsize = -hsize;
+ }
+ if (vsize < 0) {
+ vpos += vsize;
+ vsize = -vsize;
+ }
+ if (!moveto(hpos, vpos))
+ return;
+ printf("\033*c%da%db0P", hsize, vsize);
+ break;
+ }
+ case 'l':
+ if (np != 2) {
+ error("2 arguments required for line");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos, env->vpos))
+ return;
+ hpgl_start();
+ set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
+ printf("PD%d,%d", p[0], p[1]);
+ hpgl_end();
+ break;
+ case 'p':
+ case 'P':
+ {
+ if (np & 1) {
+ error("even number of arguments required for polygon");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for polygon");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos, env->vpos))
+ return;
+ hpgl_start();
+ if (code == 'p')
+ set_line_thickness(env->size);
+ printf("PMPD%d", p[0]);
+ for (int i = 1; i < np; i++)
+ printf(",%d", p[i]);
+ printf("PM2%cP", code == 'p' ? 'E' : 'F');
+ hpgl_end();
+ break;
+ }
+ case '~':
+ {
+ if (np & 1) {
+ error("even number of arguments required for spline");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for spline");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos, env->vpos))
+ return;
+ hpgl_start();
+ set_line_thickness(env->size);
+ printf("PD%d,%d", p[0]/2, p[1]/2);
+ const int tnum = 2;
+ const int tden = 3;
+ if (np > 2) {
+ fputs("BR", stdout);
+ for (int i = 0; i < np - 2; i += 2) {
+ if (i != 0)
+ putchar(',');
+ printf("%d,%d,%d,%d,%d,%d",
+ (p[i]*tnum)/(2*tden),
+ (p[i + 1]*tnum)/(2*tden),
+ p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
+ p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
+ (p[i] - p[i]/2) + p[i + 2]/2,
+ (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
+ }
+ }
+ printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
+ hpgl_end();
+ break;
+ }
+ case 'c':
+ case 'C':
+ // troff adds an extra argument to C
+ if (np != 1 && !(code == 'C' && np == 2)) {
+ error("1 argument required for circle");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos + p[0]/2, env->vpos))
+ return;
+ hpgl_start();
+ if (code == 'c') {
+ set_line_thickness(env->size);
+ printf("CI%d", p[0]/2);
+ }
+ else
+ printf("WG%d,0,360", p[0]/2);
+ hpgl_end();
+ break;
+ case 'e':
+ case 'E':
+ if (np != 2) {
+ error("2 arguments required for ellipse");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos + p[0]/2, env->vpos))
+ return;
+ hpgl_start();
+ printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
+ if (code == 'e') {
+ set_line_thickness(env->size);
+ printf("CI%d", p[1]/2);
+ }
+ else
+ printf("WG%d,0,360", p[1]/2);
+ printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
+ hpgl_end();
+ break;
+ case 'a':
+ {
+ if (np != 4) {
+ error("4 arguments required for arc");
+ break;
+ }
+ hpgl_init();
+ if (!moveto(env->hpos, env->vpos))
+ return;
+ hpgl_start();
+ set_line_thickness(env->size);
+ double c[2];
+ if (adjust_arc_center(p, c)) {
+ double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
+ - atan2(-c[1], -c[0]))
+ * 180.0/PI);
+ if (sweep > 0.0)
+ sweep -= 360.0;
+ printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
+ }
+ else
+ printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
+ hpgl_end();
+ }
+ break;
+ case 'f':
+ if (np != 1 && np != 2) {
+ error("1 argument required for fill");
+ break;
+ }
+ hpgl_init();
+ hpgl_start();
+ if (p[0] >= 0 && p[0] <= 1000)
+ printf("FT10,%d", p[0]/10);
+ hpgl_end();
+ break;
+ case 't':
+ {
+ if (np == 0) {
+ line_thickness = -1;
+ }
+ else {
+ // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2) {
+ error("0 or 1 argument required for thickness");
+ break;
+ }
+ line_thickness = p[0];
+ }
+ break;
+ }
+ default:
+ error("unrecognised drawing command `%1'", char(code));
+ break;
+ }
+}
+
+void lj4_printer::hpgl_init()
+{
+ if (hpgl_inited)
+ return;
+ hpgl_inited = 1;
+ hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
+ printf("\033&f0S" // push position
+ "\033*p0x0Y" // move to 0,0
+ "\033*c%dx%dy0T" // establish picture frame
+ "\033%%1B" // switch to HPGL
+ "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
+ "LA1,4,2,4" // round line ends and joins
+ "PR" // relative plotting
+ "TR0" // opaque
+ ";\033%%1A" // back to PCL
+ "\033&f1S", // pop position
+ MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
+ hpgl_scale, hpgl_scale);
+}
+
+void lj4_printer::set_line_thickness(int size, int dot)
+{
+ double pw;
+ if (line_thickness < 0)
+ pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
+ else
+ pw = line_thickness*25.4/font::res;
+ if (dot && pw < MIN_DOT_PEN_WIDTH)
+ pw = MIN_DOT_PEN_WIDTH;
+ if (pw != pen_width) {
+ printf("PW%f", pw);
+ pen_width = pw;
+ }
+}
+
+font *lj4_printer::make_font(const char *nm)
+{
+ return lj4_font::load_lj4_font(nm);
+}
+
+printer *make_printer()
+{
+ return new lj4_printer;
+}
+
+static
+int lookup_paper_size(const char *s)
+{
+ for (int i = 0; i < sizeof(paper_table)/sizeof(paper_table[0]); i++) {
+ // FIXME Perhaps allow unique prefix.
+ if (strcasecmp(s, paper_table[i].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static
+void handle_unknown_desc_command(const char *command, const char *arg,
+ const char *filename, int lineno)
+{
+ if (strcmp(command, "papersize") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`papersize' command requires an argument");
+ else if (paper_size < 0) {
+ int n = lookup_paper_size(arg);
+ if (n < 0)
+ error_with_file_and_line(filename, lineno,
+ "unknown paper size `%1'", arg);
+ else
+ paper_size = n;
+ }
+ }
+}
+
+static void usage(FILE *stream);
+
+extern "C" int optopt, optind;
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ font::set_unknown_desc_command_handler(handle_unknown_desc_command);
+ int c;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, ":F:p:d:lvw:c:", long_options, NULL))
+ != EOF)
+ switch(c) {
+ case 'l':
+ landscape_flag = 1;
+ break;
+ case ':':
+ if (optopt == 'd') {
+ fprintf(stderr, "duplex assumed to be long-side\n");
+ duplex_flag = 1;
+ } else
+ fprintf(stderr, "option -%c requires an operand\n", optopt);
+ fflush(stderr);
+ break;
+ case 'd':
+ if (!isdigit(*optarg)) // this ugly hack prevents -d without
+ optind--; // args from messing up the arg list
+ duplex_flag = atoi(optarg);
+ if (duplex_flag != 1 && duplex_flag != 2) {
+ fprintf(stderr, "odd value for duplex; assumed to be long-side\n");
+ duplex_flag = 1;
+ }
+ break;
+ case 'p':
+ {
+ int n = lookup_paper_size(optarg);
+ if (n < 0)
+ error("unknown paper size `%1'", optarg);
+ else
+ paper_size = n;
+ break;
+ }
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU grolj4 (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'c':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg)
+ error("argument for -c must be a positive integer");
+ else if (n <= 0 || n > 32767)
+ error("out of range argument for -c");
+ else
+ ncopies = unsigned(n);
+ break;
+ }
+ case 'w':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg)
+ error("argument for -w must be a non-negative integer");
+ else if (n < 0 || n > INT_MAX)
+ error("out of range argument for -w");
+ else
+ line_width_factor = int(n);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+#ifdef SET_BINARY
+ SET_BINARY(fileno(stdout));
+#endif
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n"
+ " [-w n] [-F dir] [files ...]\n",
+ program_name);
+}
diff --git a/contrib/groff/src/devices/grops/Makefile.sub b/contrib/groff/src/devices/grops/Makefile.sub
new file mode 100644
index 000000000000..4182527d514f
--- /dev/null
+++ b/contrib/groff/src/devices/grops/Makefile.sub
@@ -0,0 +1,12 @@
+PROG=grops
+MAN1=grops.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ ps.o \
+ psrm.o
+CCSRCS=\
+ $(srcdir)/ps.cc \
+ $(srcdir)/psrm.cc
+HDRS=\
+ $(srcdir)/ps.h
diff --git a/contrib/groff/src/devices/grops/TODO b/contrib/groff/src/devices/grops/TODO
new file mode 100644
index 000000000000..da67973a2feb
--- /dev/null
+++ b/contrib/groff/src/devices/grops/TODO
@@ -0,0 +1,29 @@
+Read PFB files directly.
+
+Generate %%DocumentMedia comment.
+
+Generate %%For comment.
+
+Generate %%Title comment.
+
+For efficiency it might be better to have the printer interface have
+support for the t and u commands.
+
+Angles in arc command: don't generate more digits after the decimal
+point than are necessary.
+
+Possibly generate BoundingBox comment.
+
+Per font composite character mechanism (sufficient for fractions).
+
+Consider whether we ought to do rounding of graphical objects other
+than lines. What's the point?
+
+Error messages should refer to output page number.
+
+Search for downloadable fonts using their PostScript names if not
+found in download file.
+
+Separate path for searching for downloadable font files.
+
+Clip to the BoundingBox when importing documents.
diff --git a/contrib/groff/src/devices/grops/grops.man b/contrib/groff/src/devices/grops/grops.man
new file mode 100644
index 000000000000..1dac72594602
--- /dev/null
+++ b/contrib/groff/src/devices/grops/grops.man
@@ -0,0 +1,861 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH GROPS @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grops \- PostScript driver for groff
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBgrops 'u
+.ti \niu
+.B grops
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-glmv
+.OP \-b n
+.OP \-c n
+.OP \-w n
+.OP \-F dir
+.OP \-P prologue
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B grops
+translates the output of GNU
+.B troff
+to PostScript.
+Normally
+.B grops
+should be invoked by using the groff command
+with a
+.B \-Tps
+option.
+.if '@DEVICE@'ps' (Actually, this is the default for groff.)
+If no files are given,
+.B grops
+will read the standard input.
+A filename of
+.B \-
+will also cause
+.B grops
+to read the standard input.
+PostScript output is written to the standard output.
+When
+.B grops
+is run by
+.B groff
+options can be passed to
+.B grops
+using the
+.B groff
+.B \-P
+option.
+.SH OPTIONS
+.TP
+.BI \-b n
+Workaround broken spoolers and previewers.
+Normally
+.B grops
+produces output that conforms
+the Document Structuring Conventions version 3.0.
+Unfortunately some spoolers and previewers can't handle such output.
+The value of
+.I n
+controls what
+.B grops
+does to its output acceptable to such programs.
+A value of 0 will cause grops not to employ any workarounds.
+Add 1 if no
+.B %%BeginDocumentSetup
+and
+.B %%EndDocumentSetup
+comments should be generated;
+this is needed for early versions of TranScript that get confused by
+anything between the
+.B %%EndProlog
+comment and the first
+.B %%Page
+comment.
+Add 2 if lines in included files beginning with
+.B %!
+should be stripped out; this is needed for Sun's pageview previewer.
+Add 4 if
+.BR %%Page ,
+.BR %%Trailer
+and
+.B %%EndProlog
+comments should be
+stripped out of included files; this is needed for spoolers that
+don't understand the
+.B %%BeginDocument
+and
+.B %%EndDocument
+comments.
+Add 8 if the first line of the PostScript output should be
+.B %!PS-Adobe-2.0
+rather than
+.BR %!PS-Adobe-3.0 ;
+this is needed when using Sun's Newsprint with a printer that requires
+page reversal.
+The default value can be specified by a
+.RS
+.IP
+.BI broken\ n
+.LP
+command in the DESC file.
+Otherwise the default value is 0.
+.RE
+.TP
+.BI \-c n
+Print
+.I n
+copies of each page.
+.TP
+.BI \-g
+Guess the page length.
+This generates PostScript code that guesses the page length.
+The guess will be correct only if the imageable area is vertically
+centered on the page.
+This option allows you to generate documents that can be printed
+both on letter (8.5\(mu11) paper and on A4 paper without change.
+.TP
+.B \-l
+Print the document in landscape format.
+.TP
+.B \-m
+Turn manual feed on for the document.
+.TP
+.BI \-F dir
+Prepend directory
+.IB dir /dev name
+to the search path for prologue, font, and device description files;
+.I name
+is the name of the device, usually
+.BR ps .
+.TP
+.BI \-P prologue-file
+Use the file
+.I prologue-file
+(in the font path) as the prologue instead of the default prologue file
+.BR prologue .
+This option overrides the environment variable
+.SM GROPS_PROLOGUE.
+.TP
+.BI \-w n
+Lines should be drawn using a thickness of
+.I n
+thousandths of an em.
+.TP
+.B \-v
+Print the version number.
+.SH USAGE
+There are styles called
+.BR R ,
+.BR I ,
+.BR B ,
+and
+.B BI
+mounted at font positions 1 to 4.
+The fonts are grouped into families
+.BR A ,
+.BR BM ,
+.BR C ,
+.BR H ,
+.BR HN ,
+.BR N ,
+.B P
+and
+.B T
+having members in each of these styles:
+.de FT
+.if '\\*(.T'ps' .ft \\$1
+..
+.TP
+.B AR
+.FT AR
+AvantGarde-Book
+.FT
+.TP
+.B AI
+.FT AI
+AvantGarde-BookOblique
+.FT
+.TP
+.B AB
+.FT AB
+AvantGarde-Demi
+.FT
+.TP
+.B ABI
+.FT ABI
+AvantGarde-DemiOblique
+.FT
+.TP
+.B BMR
+.FT BMR
+Bookman-Light
+.FT
+.TP
+.B BMI
+.FT BMI
+Bookman-LightItalic
+.FT
+.TP
+.B BMB
+.FT BMB
+Bookman-Demi
+.FT
+.TP
+.B BMBI
+.FT BMBI
+Bookman-DemiItalic
+.FT
+.TP
+.B CR
+.FT CR
+Courier
+.FT
+.TP
+.B CI
+.FT CI
+Courier-Oblique
+.FT
+.TP
+.B CB
+.FT CB
+Courier-Bold
+.FT
+.TP
+.B CBI
+.FT CBI
+Courier-BoldOblique
+.FT
+.TP
+.B HR
+.FT HR
+Helvetica
+.FT
+.TP
+.B HI
+.FT HI
+Helvetica-Oblique
+.FT
+.TP
+.B HB
+.FT HB
+Helvetica-Bold
+.FT
+.TP
+.B HBI
+.FT HBI
+Helvetica-BoldOblique
+.FT
+.TP
+.B HNR
+.FT HNR
+Helvetica-Narrow
+.FT
+.TP
+.B HNI
+.FT HNI
+Helvetica-Narrow-Oblique
+.FT
+.TP
+.B HNB
+.FT HNB
+Helvetica-Narrow-Bold
+.FT
+.TP
+.B HNBI
+.FT HNBI
+Helvetica-Narrow-BoldOblique
+.FT
+.TP
+.B NR
+.FT NR
+NewCenturySchlbk-Roman
+.FT
+.TP
+.B NI
+.FT NI
+NewCenturySchlbk-Italic
+.FT
+.TP
+.B NB
+.FT NB
+NewCenturySchlbk-Bold
+.FT
+.TP
+.B NBI
+.FT NBI
+NewCenturySchlbk-BoldItalic
+.FT
+.TP
+.B PR
+.FT PR
+Palatino-Roman
+.FT
+.TP
+.B PI
+.FT PI
+Palatino-Italic
+.FT
+.TP
+.B PB
+.FT PB
+Palatino-Bold
+.FT
+.TP
+.B PBI
+.FT PBI
+Palatino-BoldItalic
+.FT
+.TP
+.B TR
+.FT TR
+Times-Roman
+.FT
+.TP
+.B TI
+.FT TI
+Times-Italic
+.FT
+.TP
+.B TB
+.FT TB
+Times-Bold
+.FT
+.TP
+.B TBI
+.FT TBI
+Times-BoldItalic
+.FT
+.LP
+There is also the following font which is not a member of a family:
+.TP
+.B ZCMI
+.FT ZCMI
+ZapfChancery-MediumItalic
+.FT
+.LP
+There are also some special fonts called
+.B SS
+and
+.BR S .
+Zapf Dingbats is available as
+.BR ZD
+and a reversed version of ZapfDingbats (with symbols pointing in the opposite
+direction) is available as
+.BR ZDR ;
+most characters in these fonts are unnamed and must be accessed using
+.BR \eN .
+.LP
+.B grops
+understands various X commands produced using the
+.B \eX
+escape sequence;
+.B grops
+will only interpret commands that begin with a
+.B ps:
+tag.
+.TP
+.BI \eX'ps:\ exec\ code '
+This executes the arbitrary PostScript commands in
+.IR code .
+The PostScript currentpoint will be set to the position of the
+.B \eX
+command before executing
+.IR code .
+The origin will be at the top left corner of the page,
+and y coordinates will increase down the page.
+A procedure
+.B u
+will be defined that converts groff units
+to the coordinate system in effect.
+For example,
+.RS
+.IP
+.B
+\&.nr x 1i
+.br
+.B
+\eX'ps: exec \enx u 0 rlineto stroke'
+.br
+.RE
+.IP
+will draw a horizontal line one inch long.
+.I code
+may make changes to the graphics state,
+but any changes will persist only to the
+end of the page.
+A dictionary containing the definitions specified by the
+.B def
+and
+.B mdef
+will be on top of the dictionary stack.
+If your code adds definitions to this dictionary,
+you should allocate space for them using
+.BI \eX'ps\ mdef \ n '\fR.
+Any definitions will persist only until the end of the page.
+If you use the
+.B \eY
+escape sequence with an argument that names a macro,
+.I code
+can extend over multiple lines.
+For example,
+.RS
+.IP
+.nf
+.ft B
+\&.nr x 1i
+\&.de y
+\&ps: exec
+\&\enx u 0 rlineto
+\&stroke
+\&..
+\&\eYy
+.fi
+.ft R
+.LP
+is another way to draw a horizontal line one inch long.
+.RE
+.TP
+.BI \eX'ps:\ file\ name '
+This is the same as the
+.B exec
+command except that the PostScript code is read from file
+.IR name .
+.TP
+.BI \eX'ps:\ def\ code '
+Place a PostScript definition contained in
+.I code
+in the prologue.
+There should be at most one definition per
+.B \eX
+command.
+Long definitions can be split over several
+.B \eX
+commands;
+all the
+.I code
+arguments are simply joined together separated by newlines.
+The definitions are placed in a dictionary which is automatically
+pushed on the dictionary stack when an
+.B exec
+command is executed.
+If you use the
+.B \eY
+escape sequence with an argument that names a macro,
+.I code
+can extend over multiple lines.
+.TP
+.BI \eX'ps:\ mdef\ n\ code '
+Like
+.BR def ,
+except that
+.I code
+may contain up to
+.I n
+definitions.
+.B grops
+needs to know how many definitions
+.I code
+contains
+so that it can create an appropriately sized PostScript dictionary
+to contain them.
+.TP
+.BI \eX'ps:\ import\ file\ llx\ lly\ urx\ ury\ width\ \fR[\fP\ height\ \fR]\fP '
+Import a PostScript graphic from
+.IR file .
+The arguments
+.IR llx ,
+.IR lly ,
+.IR urx ,
+and
+.I ury
+give the bounding box of the graphic in the default PostScript
+coordinate system; they should all be integers;
+.I llx
+and
+.I lly
+are the x and y coordinates of the lower left
+corner of the graphic;
+.I urx
+and
+.I ury
+are the x and y coordinates of the upper right corner of the graphic;
+.I width
+and
+.I height
+are integers that give the desired width and height in groff
+units of the graphic.
+The graphic will be scaled so that it has this width and height
+and translated so that the lower left corner of the graphic is
+located at the position associated with
+.B \eX
+command.
+If the height argument is omitted it will be scaled uniformly in the
+x and y directions so that it has the specified width.
+Note that the contents of the
+.B \eX
+command are not interpreted by
+.BR troff ;
+so vertical space for the graphic is not automatically added,
+and the
+.I width
+and
+.I height
+arguments are not allowed to have attached scaling indicators.
+If the PostScript file complies with the Adobe Document Structuring
+Conventions and contains a
+.B %%BoundingBox
+comment, then the bounding box can be automatically
+extracted from within groff by using the
+.B psbb
+request.
+.RS
+.LP
+The
+.B \-mps
+macros (which are automatically loaded when
+.B grops
+is run by the groff command) include a
+.B PSPIC
+macro which allows a picture to be easily imported.
+This has the format
+.IP
+\&\fB.PSPIC\fP [ \fB\-L\fP | \fB-R\fP | \fB\-I\fP \fIn\fP ]\ \"
+\fI\|file\fP [ \fIwidth\fP [ \fIheight\fP ]]
+.LP
+.I file
+is the name of the file containing the illustration;
+.I width
+and
+.I height
+give the desired width and height of the graphic.
+The
+.I width
+and
+.I height
+arguments may have scaling indicators attached;
+the default scaling indicator is
+.BR i .
+This macro will scale the graphic uniformly
+in the x and y directions so that it is no more than
+.I width
+wide
+and
+.I height
+high.
+By default, the graphic will be horizontally centered.
+The
+.BI \-L
+and
+.BI \-R
+cause the graphic to be left-aligned and right-aligned
+respectively.
+The
+.B \-I
+option causes the graphic to be indented by
+.IR n .
+.RE
+.TP
+.B \eX'ps:\ invis'
+.br
+.ns
+.TP
+.B \eX'ps:\ endinvis'
+No output will be generated for text and drawing commands
+that are bracketed with these
+.B \eX
+commands.
+These commands are intended for use when output from
+.B troff
+will be previewed before being processed with
+.BR grops ;
+if the previewer is unable to display certain characters
+or other constructs, then other substitute characters or constructs
+can be used for previewing by bracketing them with these
+.B \eX
+commands.
+.RS
+.LP
+For example,
+.B gxditview
+is not able to display a proper
+.B \e(em
+character because the standard X11 fonts do not provide it;
+this problem can be overcome by executing the following
+request
+.IP
+.ft B
+.nf
+\&.char \e(em \eX'ps: invis'\e
+\eZ'\ev'-.25m'\eh'.05m'\eD'l .9m 0'\eh'.05m''\e
+\eX'ps: endinvis'\e(em
+.ft
+.fi
+.LP
+In this case,
+.B gxditview
+will be unable to display the
+.B \e(em
+character and will draw the line,
+whereas
+.B grops
+will print the
+.B \e(em
+character
+and ignore the line.
+.RE
+.LP
+The input to
+.B grops
+must be in the format output by
+.BR @g@troff (@MAN1EXT@).
+This is described in
+.BR groff_out (@MAN5EXT@).
+In addition the device and font description files for the device used
+must meet certain requirements.
+The device and font description files supplied for
+.B ps
+device meet all these requirements.
+.BR afmtodit (@MAN1EXT@)
+can be used to create font files from AFM files.
+The resolution must be an integer multiple of 72 times the
+.BR sizescale .
+The
+.B ps
+device uses a resolution of 72000 and a sizescale of 1000.
+The device description file should contain a command
+.IP
+.BI paperlength\ n
+.LP
+which says that output should be generated which is suitable for
+printing on a page whose length is
+.I n
+machine units.
+Each font description file must contain a command
+.IP
+.BI internalname\ psname
+.LP
+which says that the PostScript name of the font is
+.IR psname .
+It may also contain a command
+.IP
+.BI encoding\ enc_file
+.LP
+which says that
+the PostScript font should be reencoded using the encoding described in
+.IR enc_file ;
+this file should consist of a sequence of lines of the form:
+.IP
+.I
+pschar code
+.LP
+where
+.I pschar
+is the PostScript name of the character,
+and
+.I code
+is its position in the encoding expressed as a decimal integer.
+The code for each character given in the font file must correspond
+to the code for the character in encoding file, or to the code in the default
+encoding for the font if the PostScript font is not to be reencoded.
+This code can be used with the
+.B \eN
+escape sequence in
+.B troff
+to select the character,
+even if the character does not have a groff name.
+Every character in the font file must exist in the PostScript font, and
+the widths given in the font file must match the widths used
+in the PostScript font.
+.B grops
+will assume that a character with a groff name of
+.B space
+is blank (makes no marks on the page);
+it can make use of such a character to generate more efficient and
+compact PostScript output.
+.LP
+.B grops
+can automatically include the downloadable fonts necessary
+to print the document.
+Any downloadable fonts which should, when required, be included by
+.B grops
+must be listed in the file
+.BR @FONTDIR@/devps/download ;
+this should consist of lines of the form
+.IP
+.I
+font filename
+.LP
+where
+.I font
+is the PostScript name of the font,
+and
+.I filename
+is the name of the file containing the font;
+lines beginning with
+.B #
+and blank lines are ignored;
+fields may be separated by tabs or spaces;
+.I filename
+will be searched for using the same mechanism that is used
+for groff font metric files.
+The
+.B download
+file itself will also be searched for using this mechanism.
+.LP
+If the file containing a downloadable font or imported document
+conforms to the Adobe Document Structuring Conventions,
+then
+.B grops
+will interpret any comments in the files sufficiently to ensure that its
+own output is conforming.
+It will also supply any needed font resources that are listed in the
+.B download
+file
+as well as any needed file resources.
+It is also able to handle inter-resource dependencies.
+For example, suppose that you have a downloadable font called Garamond,
+and also a downloadable font called Garamond-Outline
+which depends on Garamond
+(typically it would be defined to copy Garamond's font dictionary,
+and change the PaintType),
+then it is necessary for Garamond to be appear before Garamond-Outline
+in the PostScript document.
+.B grops
+will handle this automatically
+provided that the downloadable font file for Garamond-Outline
+indicates its dependence on Garamond by means of
+the Document Structuring Conventions,
+for example by beginning with the following lines
+.IP
+.B
+%!PS-Adobe-3.0 Resource-Font
+.br
+.B
+%%DocumentNeededResources: font Garamond
+.br
+.B
+%%EndComments
+.br
+.B
+%%IncludeResource: font Garamond
+.LP
+In this case both Garamond and Garamond-Outline would need to be listed
+in the
+.B download
+file.
+A downloadable font should not include its own name in a
+.B %%DocumentSuppliedResources
+comment.
+.LP
+.B grops
+will not interpret
+.B %%DocumentFonts
+comments.
+The
+.BR %%DocumentNeededResources ,
+.BR %%DocumentSuppliedResources ,
+.BR %%IncludeResource ,
+.BR %%BeginResource
+and
+.BR %%EndResource
+comments
+(or possibly the old
+.BR %%DocumentNeededFonts ,
+.BR %%DocumentSuppliedFonts ,
+.BR %%IncludeFont ,
+.BR %%BeginFont
+and
+.BR %%EndFont
+comments)
+should be used.
+.SH ENVIRONMENT
+.TP
+.SM
+.B GROPS_PROLOGUE
+If this is set to
+.IR foo ,
+then
+.B grops
+will use the file
+.I foo
+(in the font path) instead of the default prologue file
+.BR prologue .
+The option
+.B \-P
+overrides this environment variable.
+.SH FILES
+.Tp \w'\fB@FONTDIR@/devps/download'u+2n
+.B @FONTDIR@/devps/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devps/ F
+Font description file for font
+.IR F .
+.TP
+.B @FONTDIR@/devps/download
+List of downloadable fonts.
+.TP
+.B @FONTDIR@/devps/text.enc
+Encoding used for text fonts.
+.TP
+.B @MACRODIR@/ps.tmac
+Macros for use with
+.BR grops ;
+automatically loaded by
+.BR troffrc
+.TP
+.B @MACRODIR@/pspic.tmac
+Definition of
+.B PSPIC
+macro,
+automatically loaded by
+.BR ps.tmac .
+.TP
+.B @MACRODIR@/psold.tmac
+Macros to disable use of characters not present in older
+PostScript printers (e.g. `eth' or `thorn').
+.TP
+.BI /tmp/grops XXXXXX
+Temporary file.
+.SH "SEE ALSO"
+.BR afmtodit (@MAN1EXT@),
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR psbb (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/devices/grops/ps.cc b/contrib/groff/src/devices/grops/ps.cc
new file mode 100644
index 000000000000..98b1a910607e
--- /dev/null
+++ b/contrib/groff/src/devices/grops/ps.cc
@@ -0,0 +1,1570 @@
+// -*- 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 "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "nonposix.h"
+
+#include "ps.h"
+#include <time.h>
+
+#ifdef NEED_DECLARATION_PUTENV
+extern "C" {
+ int putenv(const char *);
+}
+#endif /* NEED_DECLARATION_PUTENV */
+
+static int landscape_flag = 0;
+static int manual_feed_flag = 0;
+static int ncopies = 1;
+static int linewidth = -1;
+// Non-zero means generate PostScript code that guesses the paper
+// length using the imageable area.
+static int guess_flag = 0;
+
+// Non-zero if -b was specified on the command line.
+static int bflag = 0;
+unsigned broken_flags = 0;
+
+#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
+#define MAX_LINE_LENGTH 72
+#define FILL_MAX 1000
+
+const char *const dict_name = "grops";
+const char *const defs_dict_name = "DEFS";
+const int DEFS_DICT_SPARE = 50;
+
+double degrees(double r)
+{
+ return r*180.0/PI;
+}
+
+double radians(double d)
+{
+ return d*PI/180.0;
+}
+
+inline double transform_fill(int fill)
+{
+ return 1 - fill/double(FILL_MAX);
+}
+
+// This is used for testing whether a character should be output in the
+// PostScript file using \nnn, so we really want the character to be
+// less than 0200.
+
+inline int is_ascii(char c)
+{
+ return (unsigned char)c < 0200;
+}
+
+ps_output::ps_output(FILE *f, int n)
+: fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
+{
+}
+
+ps_output &ps_output::set_file(FILE *f)
+{
+ fp = f;
+ col = 0;
+ return *this;
+}
+
+ps_output &ps_output::copy_file(FILE *infp)
+{
+ int c;
+ while ((c = getc(infp)) != EOF)
+ putc(c, fp);
+ return *this;
+}
+
+ps_output &ps_output::end_line()
+{
+ if (col != 0) {
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ }
+ return *this;
+}
+
+ps_output &ps_output::special(const char *s)
+{
+ if (s == 0 || *s == '\0')
+ return *this;
+ if (col != 0) {
+ putc('\n', fp);
+ col = 0;
+ }
+ fputs(s, fp);
+ if (strchr(s, '\0')[-1] != '\n')
+ putc('\n', fp);
+ need_space = 0;
+ return *this;
+}
+
+ps_output &ps_output::simple_comment(const char *s)
+{
+ if (col != 0)
+ putc('\n', fp);
+ putc('%', fp);
+ putc('%', fp);
+ fputs(s, fp);
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ return *this;
+}
+
+ps_output &ps_output::begin_comment(const char *s)
+{
+ if (col != 0)
+ putc('\n', fp);
+ putc('%', fp);
+ putc('%', fp);
+ fputs(s, fp);
+ col = 2 + strlen(s);
+ return *this;
+}
+
+ps_output &ps_output::end_comment()
+{
+ if (col != 0) {
+ putc('\n', fp);
+ col = 0;
+ }
+ need_space = 0;
+ return *this;
+}
+
+ps_output &ps_output::comment_arg(const char *s)
+{
+ int len = strlen(s);
+ if (col + len + 1 > max_line_length) {
+ putc('\n', fp);
+ fputs("%%+", fp);
+ col = 3;
+ }
+ putc(' ', fp);
+ fputs(s, fp);
+ col += len + 1;
+ return *this;
+}
+
+ps_output &ps_output::set_fixed_point(int n)
+{
+ assert(n >= 0 && n <= 10);
+ fixed_point = n;
+ return *this;
+}
+
+ps_output &ps_output::put_delimiter(char c)
+{
+ if (col + 1 > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ putc(c, fp);
+ col++;
+ need_space = 0;
+ return *this;
+}
+
+ps_output &ps_output::put_string(const char *s, int n)
+{
+ int len = 0;
+ int i;
+ for (i = 0; i < n; i++) {
+ char c = s[i];
+ if (is_ascii(c) && csprint(c)) {
+ if (c == '(' || c == ')' || c == '\\')
+ len += 2;
+ else
+ len += 1;
+ }
+ else
+ len += 4;
+ }
+ if (len > n*2) {
+ if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ if (col + 1 > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ putc('<', fp);
+ col++;
+ for (i = 0; i < n; i++) {
+ if (col + 2 > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ fprintf(fp, "%02x", s[i] & 0377);
+ col += 2;
+ }
+ putc('>', fp);
+ col++;
+ }
+ else {
+ if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ if (col + 2 > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ putc('(', fp);
+ col++;
+ for (i = 0; i < n; i++) {
+ char c = s[i];
+ if (is_ascii(c) && csprint(c)) {
+ if (c == '(' || c == ')' || c == '\\')
+ len = 2;
+ else
+ len = 1;
+ }
+ else
+ len = 4;
+ if (col + len + 1 > max_line_length) {
+ putc('\\', fp);
+ putc('\n', fp);
+ col = 0;
+ }
+ switch (len) {
+ case 1:
+ putc(c, fp);
+ break;
+ case 2:
+ putc('\\', fp);
+ putc(c, fp);
+ break;
+ case 4:
+ fprintf(fp, "\\%03o", c & 0377);
+ break;
+ default:
+ assert(0);
+ }
+ col += len;
+ }
+ putc(')', fp);
+ col++;
+ }
+ need_space = 0;
+ return *this;
+}
+
+ps_output &ps_output::put_number(int n)
+{
+ char buf[1 + INT_DIGITS + 1];
+ sprintf(buf, "%d", n);
+ int len = strlen(buf);
+ if (col > 0 && col + len + need_space > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ }
+ if (need_space) {
+ putc(' ', fp);
+ col++;
+ }
+ fputs(buf, fp);
+ col += len;
+ need_space = 1;
+ return *this;
+}
+
+ps_output &ps_output::put_fix_number(int i)
+{
+ const char *p = if_to_a(i, fixed_point);
+ int len = strlen(p);
+ if (col > 0 && col + len + need_space > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ }
+ if (need_space) {
+ putc(' ', fp);
+ col++;
+ }
+ fputs(p, fp);
+ col += len;
+ need_space = 1;
+ return *this;
+}
+
+ps_output &ps_output::put_float(double d)
+{
+ char buf[128];
+ sprintf(buf, "%.4f", d);
+ int len = strlen(buf);
+ if (col > 0 && col + len + need_space > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ }
+ if (need_space) {
+ putc(' ', fp);
+ col++;
+ }
+ fputs(buf, fp);
+ col += len;
+ need_space = 1;
+ return *this;
+}
+
+ps_output &ps_output::put_symbol(const char *s)
+{
+ int len = strlen(s);
+ if (col > 0 && col + len + need_space > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ need_space = 0;
+ }
+ if (need_space) {
+ putc(' ', fp);
+ col++;
+ }
+ fputs(s, fp);
+ col += len;
+ need_space = 1;
+ return *this;
+}
+
+ps_output &ps_output::put_literal_symbol(const char *s)
+{
+ int len = strlen(s);
+ if (col > 0 && col + len + 1 > max_line_length) {
+ putc('\n', fp);
+ col = 0;
+ }
+ putc('/', fp);
+ fputs(s, fp);
+ col += len + 1;
+ need_space = 1;
+ return *this;
+}
+
+class ps_font : public font {
+ ps_font(const char *);
+public:
+ int encoding_index;
+ char *encoding;
+ char *reencoded_name;
+ ~ps_font();
+ void handle_unknown_font_command(const char *command, const char *arg,
+ const char *filename, int lineno);
+ static ps_font *load_ps_font(const char *);
+};
+
+ps_font *ps_font::load_ps_font(const char *s)
+{
+ ps_font *f = new ps_font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+ps_font::ps_font(const char *nm)
+: font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
+{
+}
+
+ps_font::~ps_font()
+{
+ a_delete encoding;
+ a_delete reencoded_name;
+}
+
+void ps_font::handle_unknown_font_command(const char *command, const char *arg,
+ const char *filename, int lineno)
+{
+ if (strcmp(command, "encoding") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`encoding' command requires an argument");
+ else
+ encoding = strsave(arg);
+ }
+}
+
+static void handle_unknown_desc_command(const char *command, const char *arg,
+ const char *filename, int lineno)
+{
+ if (strcmp(command, "broken") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`broken' command requires an argument");
+ else if (!bflag)
+ broken_flags = atoi(arg);
+ }
+}
+
+struct style {
+ font *f;
+ int point_size;
+ int height;
+ int slant;
+ style();
+ style(font *, int, int, int);
+ int operator==(const style &) const;
+ int operator!=(const style &) const;
+};
+
+style::style() : f(0)
+{
+}
+
+style::style(font *p, int sz, int h, int sl)
+: f(p), point_size(sz), height(h), slant(sl)
+{
+}
+
+int style::operator==(const style &s) const
+{
+ return (f == s.f && point_size == s.point_size
+ && height == s.height && slant == s.slant);
+}
+
+int style::operator!=(const style &s) const
+{
+ return !(*this == s);
+}
+
+class ps_printer : public printer {
+ FILE *tempfp;
+ ps_output out;
+ int res;
+ int space_char_index;
+ int pages_output;
+ int paper_length;
+ int equalise_spaces;
+ enum { SBUF_SIZE = 256 };
+ char sbuf[SBUF_SIZE];
+ int sbuf_len;
+ int sbuf_start_hpos;
+ int sbuf_vpos;
+ int sbuf_end_hpos;
+ int sbuf_space_width;
+ int sbuf_space_count;
+ int sbuf_space_diff_count;
+ int sbuf_space_code;
+ int sbuf_kern;
+ style sbuf_style;
+ style output_style;
+ int output_hpos;
+ int output_vpos;
+ int output_draw_point_size;
+ int line_thickness;
+ int output_line_thickness;
+ int fill;
+ unsigned char output_space_code;
+ enum { MAX_DEFINED_STYLES = 50 };
+ style defined_styles[MAX_DEFINED_STYLES];
+ int ndefined_styles;
+ int next_encoding_index;
+ string defs;
+ int ndefs;
+ resource_manager rm;
+ int invis_count;
+
+ void flush_sbuf();
+ void set_style(const style &);
+ void set_space_code(unsigned char c);
+ int set_encoding_index(ps_font *);
+ void do_exec(char *, const environment *);
+ void do_import(char *, const environment *);
+ void do_def(char *, const environment *);
+ void do_mdef(char *, const environment *);
+ void do_file(char *, const environment *);
+ void do_invis(char *, const environment *);
+ void do_endinvis(char *, const environment *);
+ void set_line_thickness(const environment *);
+ void fill_path();
+ void encode_fonts();
+ void define_encoding(const char *, int);
+ void reencode_font(ps_font *);
+public:
+ ps_printer();
+ ~ps_printer();
+ void set_char(int i, font *f, const environment *env, int w, const char *name);
+ void draw(int code, int *p, int np, const environment *env);
+ void begin_page(int);
+ void end_page(int);
+ void special(char *arg, const environment *env, char type);
+ font *make_font(const char *);
+ void end_of_line();
+};
+
+ps_printer::ps_printer()
+: out(0, MAX_LINE_LENGTH),
+ pages_output(0),
+ sbuf_len(0),
+ output_hpos(-1),
+ output_vpos(-1),
+ line_thickness(-1),
+ fill(FILL_MAX + 1),
+ ndefined_styles(0),
+ next_encoding_index(0),
+ ndefs(0),
+ invis_count(0)
+{
+ tempfp = xtmpfile();
+ out.set_file(tempfp);
+ if (linewidth < 0)
+ linewidth = DEFAULT_LINEWIDTH;
+ if (font::hor != 1)
+ fatal("horizontal resolution must be 1");
+ if (font::vert != 1)
+ fatal("vertical resolution must be 1");
+ if (font::res % (font::sizescale*72) != 0)
+ fatal("res must be a multiple of 72*sizescale");
+ int r = font::res;
+ int point = 0;
+ while (r % 10 == 0) {
+ r /= 10;
+ point++;
+ }
+ res = r;
+ out.set_fixed_point(point);
+ space_char_index = font::name_to_index("space");
+ paper_length = font::paperlength;
+ if (paper_length == 0)
+ paper_length = 11*font::res;
+ equalise_spaces = font::res >= 72000;
+}
+
+int ps_printer::set_encoding_index(ps_font *f)
+{
+ if (f->encoding_index >= 0)
+ return f->encoding_index;
+ for (font_pointer_list *p = font_list; p; p = p->next)
+ if (p->p != f) {
+ char *encoding = ((ps_font *)p->p)->encoding;
+ int encoding_index = ((ps_font *)p->p)->encoding_index;
+ if (encoding != 0 && encoding_index >= 0
+ && strcmp(f->encoding, encoding) == 0) {
+ return f->encoding_index = encoding_index;
+ }
+ }
+ return f->encoding_index = next_encoding_index++;
+}
+
+void ps_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
+{
+ if (i == space_char_index || invis_count > 0)
+ return;
+ unsigned char code = f->get_code(i);
+ style sty(f, env->size, env->height, env->slant);
+ if (sty.slant != 0) {
+ if (sty.slant > 80 || sty.slant < -80) {
+ error("silly slant `%1' degrees", sty.slant);
+ sty.slant = 0;
+ }
+ }
+ if (sbuf_len > 0) {
+ if (sbuf_len < SBUF_SIZE
+ && sty == sbuf_style
+ && sbuf_vpos == env->vpos) {
+ if (sbuf_end_hpos == env->hpos) {
+ sbuf[sbuf_len++] = code;
+ sbuf_end_hpos += w + sbuf_kern;
+ return;
+ }
+ if (sbuf_len == 1 && sbuf_kern == 0) {
+ sbuf_kern = env->hpos - sbuf_end_hpos;
+ sbuf_end_hpos = env->hpos + sbuf_kern + w;
+ sbuf[sbuf_len++] = code;
+ return;
+ }
+ /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
+ starting a new string. */
+ if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
+ && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
+ if (sbuf_space_code < 0) {
+ if (f->contains(space_char_index)) {
+ sbuf_space_code = f->get_code(space_char_index);
+ sbuf_space_width = env->hpos - sbuf_end_hpos;
+ sbuf_end_hpos = env->hpos + w + sbuf_kern;
+ sbuf[sbuf_len++] = sbuf_space_code;
+ sbuf[sbuf_len++] = code;
+ sbuf_space_count++;
+ return;
+ }
+ }
+ else {
+ int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
+ if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
+ sbuf_end_hpos = env->hpos + w + sbuf_kern;
+ sbuf[sbuf_len++] = sbuf_space_code;
+ sbuf[sbuf_len++] = code;
+ sbuf_space_count++;
+ if (diff == 1)
+ sbuf_space_diff_count++;
+ else if (diff == -1)
+ sbuf_space_diff_count--;
+ return;
+ }
+ }
+ }
+ }
+ flush_sbuf();
+ }
+ sbuf_len = 1;
+ sbuf[0] = code;
+ sbuf_end_hpos = env->hpos + w;
+ sbuf_start_hpos = env->hpos;
+ sbuf_vpos = env->vpos;
+ sbuf_style = sty;
+ sbuf_space_code = -1;
+ sbuf_space_width = 0;
+ sbuf_space_count = sbuf_space_diff_count = 0;
+ sbuf_kern = 0;
+}
+
+static char *make_encoding_name(int encoding_index)
+{
+ static char buf[3 + INT_DIGITS + 1];
+ sprintf(buf, "ENC%d", encoding_index);
+ return buf;
+}
+
+const char *const WS = " \t\n\r";
+
+void ps_printer::define_encoding(const char *encoding, int encoding_index)
+{
+ char *vec[256];
+ int i;
+ for (i = 0; i < 256; i++)
+ vec[i] = 0;
+ char *path;
+ FILE *fp = font::open_file(encoding, &path);
+ if (fp == 0)
+ fatal("can't open encoding file `%1'", encoding);
+ int lineno = 1;
+ char buf[256];
+ while (fgets(buf, 512, fp) != 0) {
+ char *p = buf;
+ while (csspace(*p))
+ p++;
+ if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
+ char *q = strtok(0, WS);
+ int n;
+ if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
+ fatal_with_file_and_line(path, lineno, "bad second field");
+ vec[n] = new char[strlen(p) + 1];
+ strcpy(vec[n], p);
+ }
+ lineno++;
+ }
+ a_delete path;
+ out.put_literal_symbol(make_encoding_name(encoding_index));
+ out.put_delimiter('[');
+ for (i = 0; i < 256; i++) {
+ if (vec[i] == 0)
+ out.put_literal_symbol(".notdef");
+ else {
+ out.put_literal_symbol(vec[i]);
+ a_delete vec[i];
+ }
+ }
+ out.put_delimiter(']').put_symbol("def");
+}
+
+void ps_printer::reencode_font(ps_font *f)
+{
+ out.put_literal_symbol(f->reencoded_name)
+ .put_symbol(make_encoding_name(f->encoding_index))
+ .put_literal_symbol(f->get_internal_name())
+ .put_symbol("RE");
+}
+
+void ps_printer::encode_fonts()
+{
+ if (next_encoding_index == 0)
+ return;
+ char *done_encoding = new char[next_encoding_index];
+ for (int i = 0; i < next_encoding_index; i++)
+ done_encoding[i] = 0;
+ for (font_pointer_list *f = font_list; f; f = f->next) {
+ int encoding_index = ((ps_font *)f->p)->encoding_index;
+ if (encoding_index >= 0) {
+ assert(encoding_index < next_encoding_index);
+ if (!done_encoding[encoding_index]) {
+ done_encoding[encoding_index] = 1;
+ define_encoding(((ps_font *)f->p)->encoding, encoding_index);
+ }
+ reencode_font((ps_font *)f->p);
+ }
+ }
+ a_delete done_encoding;
+}
+
+void ps_printer::set_style(const style &sty)
+{
+ char buf[1 + INT_DIGITS + 1];
+ for (int i = 0; i < ndefined_styles; i++)
+ if (sty == defined_styles[i]) {
+ sprintf(buf, "F%d", i);
+ out.put_symbol(buf);
+ return;
+ }
+ if (ndefined_styles >= MAX_DEFINED_STYLES)
+ ndefined_styles = 0;
+ sprintf(buf, "F%d", ndefined_styles);
+ out.put_literal_symbol(buf);
+ const char *psname = sty.f->get_internal_name();
+ if (psname == 0)
+ fatal("no internalname specified for font `%1'", sty.f->get_name());
+ char *encoding = ((ps_font *)sty.f)->encoding;
+ if (encoding != 0) {
+ char *s = ((ps_font *)sty.f)->reencoded_name;
+ if (s == 0) {
+ int ei = set_encoding_index((ps_font *)sty.f);
+ char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
+ sprintf(tem, "%s@%d", psname, ei);
+ psname = tem;
+ ((ps_font *)sty.f)->reencoded_name = tem;
+ }
+ else
+ psname = s;
+ }
+ out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
+ if (sty.height != 0 || sty.slant != 0) {
+ int h = sty.height == 0 ? sty.point_size : sty.height;
+ h *= font::res/(72*font::sizescale);
+ int c = int(h*tan(radians(sty.slant)) + .5);
+ out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
+ .put_symbol("MF");
+ }
+ else {
+ out.put_literal_symbol(psname).put_symbol("SF");
+ }
+ defined_styles[ndefined_styles++] = sty;
+}
+
+void ps_printer::set_space_code(unsigned char c)
+{
+ out.put_literal_symbol("SC").put_number(c).put_symbol("def");
+}
+
+void ps_printer::end_of_line()
+{
+ flush_sbuf();
+ // this ensures that we do an absolute motion to the beginning of a line
+ output_vpos = output_hpos = -1;
+}
+
+void ps_printer::flush_sbuf()
+{
+ enum {
+ NONE,
+ RELATIVE_H,
+ RELATIVE_V,
+ RELATIVE_HV,
+ ABSOLUTE
+ } motion = NONE;
+ int space_flag = 0;
+ if (sbuf_len == 0)
+ return;
+ if (output_style != sbuf_style) {
+ set_style(sbuf_style);
+ output_style = sbuf_style;
+ }
+ int extra_space = 0;
+ if (output_hpos < 0 || output_vpos < 0)
+ motion = ABSOLUTE;
+ else {
+ if (output_hpos != sbuf_start_hpos)
+ motion = RELATIVE_H;
+ if (output_vpos != sbuf_vpos) {
+ if (motion != NONE)
+ motion = RELATIVE_HV;
+ else
+ motion = RELATIVE_V;
+ }
+ }
+ if (sbuf_space_code >= 0) {
+ int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
+ if (w + sbuf_kern != sbuf_space_width) {
+ if (sbuf_space_code != output_space_code) {
+ set_space_code(sbuf_space_code);
+ output_space_code = sbuf_space_code;
+ }
+ space_flag = 1;
+ extra_space = sbuf_space_width - w - sbuf_kern;
+ if (sbuf_space_diff_count > sbuf_space_count/2)
+ extra_space++;
+ else if (sbuf_space_diff_count < -(sbuf_space_count/2))
+ extra_space--;
+ }
+ }
+ if (space_flag)
+ out.put_fix_number(extra_space);
+ if (sbuf_kern != 0)
+ out.put_fix_number(sbuf_kern);
+ out.put_string(sbuf, sbuf_len);
+ char command_array[] = {'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L',
+ 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T'};
+ char sym[2];
+ sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
+ sym[1] = '\0';
+ switch (motion) {
+ case NONE:
+ break;
+ case ABSOLUTE:
+ out.put_fix_number(sbuf_start_hpos)
+ .put_fix_number(sbuf_vpos);
+ break;
+ case RELATIVE_H:
+ out.put_fix_number(sbuf_start_hpos - output_hpos);
+ break;
+ case RELATIVE_V:
+ out.put_fix_number(sbuf_vpos - output_vpos);
+ break;
+ case RELATIVE_HV:
+ out.put_fix_number(sbuf_start_hpos - output_hpos)
+ .put_fix_number(sbuf_vpos - output_vpos);
+ break;
+ default:
+ assert(0);
+ }
+ out.put_symbol(sym);
+ output_hpos = sbuf_end_hpos;
+ output_vpos = sbuf_vpos;
+ sbuf_len = 0;
+}
+
+
+void ps_printer::set_line_thickness(const environment *env)
+{
+ if (line_thickness < 0) {
+ if (output_draw_point_size != env->size) {
+ // we ought to check for overflow here
+ int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
+ out.put_fix_number(lw).put_symbol("LW");
+ output_draw_point_size = env->size;
+ output_line_thickness = -1;
+ }
+ }
+ else {
+ if (output_line_thickness != line_thickness) {
+ out.put_fix_number(line_thickness).put_symbol("LW");
+ output_line_thickness = line_thickness;
+ output_draw_point_size = -1;
+ }
+ }
+}
+
+void ps_printer::fill_path()
+{
+ if (fill > FILL_MAX)
+ out.put_symbol("BL");
+ else
+ out.put_float(transform_fill(fill)).put_symbol("FL");
+}
+
+void ps_printer::draw(int code, int *p, int np, const environment *env)
+{
+ if (invis_count > 0)
+ return;
+ int fill_flag = 0;
+ switch (code) {
+ case 'C':
+ fill_flag = 1;
+ // fall through
+ case 'c':
+ // troff adds an extra argument to C
+ if (np != 1 && !(code == 'C' && np == 2)) {
+ error("1 argument required for circle");
+ break;
+ }
+ out.put_fix_number(env->hpos + p[0]/2)
+ .put_fix_number(env->vpos)
+ .put_fix_number(p[0]/2)
+ .put_symbol("DC");
+ if (fill_flag) {
+ fill_path();
+ }
+ else {
+ set_line_thickness(env);
+ out.put_symbol("ST");
+ }
+ break;
+ case 'l':
+ if (np != 2) {
+ error("2 arguments required for line");
+ break;
+ }
+ set_line_thickness(env);
+ out.put_fix_number(p[0] + env->hpos)
+ .put_fix_number(p[1] + env->vpos)
+ .put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("DL");
+ break;
+ case 'E':
+ fill_flag = 1;
+ // fall through
+ case 'e':
+ if (np != 2) {
+ error("2 arguments required for ellipse");
+ break;
+ }
+ out.put_fix_number(p[0])
+ .put_fix_number(p[1])
+ .put_fix_number(env->hpos + p[0]/2)
+ .put_fix_number(env->vpos)
+ .put_symbol("DE");
+ if (fill_flag) {
+ fill_path();
+ }
+ else {
+ set_line_thickness(env);
+ out.put_symbol("ST");
+ }
+ break;
+ case 'P':
+ fill_flag = 1;
+ // fall through
+ case 'p':
+ {
+ if (np & 1) {
+ error("even number of arguments required for polygon");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for polygon");
+ break;
+ }
+ out.put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("MT");
+ for (int i = 0; i < np; i += 2)
+ out.put_fix_number(p[i])
+ .put_fix_number(p[i+1])
+ .put_symbol("RL");
+ out.put_symbol("CL");
+ if (fill_flag) {
+ fill_path();
+ }
+ else {
+ set_line_thickness(env);
+ out.put_symbol("ST");
+ }
+ break;
+ }
+ case '~':
+ {
+ if (np & 1) {
+ error("even number of arguments required for spline");
+ break;
+ }
+ if (np == 0) {
+ error("no arguments for spline");
+ break;
+ }
+ out.put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("MT");
+ out.put_fix_number(p[0]/2)
+ .put_fix_number(p[1]/2)
+ .put_symbol("RL");
+ /* tnum/tden should be between 0 and 1; the closer it is to 1
+ the tighter the curve will be to the guiding lines; 2/3
+ is the standard value */
+ const int tnum = 2;
+ const int tden = 3;
+ for (int i = 0; i < np - 2; i += 2) {
+ out.put_fix_number((p[i]*tnum)/(2*tden))
+ .put_fix_number((p[i + 1]*tnum)/(2*tden))
+ .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
+ .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
+ .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
+ .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
+ .put_symbol("RC");
+ }
+ out.put_fix_number(p[np - 2] - p[np - 2]/2)
+ .put_fix_number(p[np - 1] - p[np - 1]/2)
+ .put_symbol("RL");
+ set_line_thickness(env);
+ out.put_symbol("ST");
+ }
+ break;
+ case 'a':
+ {
+ if (np != 4) {
+ error("4 arguments required for arc");
+ break;
+ }
+ set_line_thickness(env);
+ double c[2];
+ if (adjust_arc_center(p, c))
+ out.put_fix_number(env->hpos + int(c[0]))
+ .put_fix_number(env->vpos + int(c[1]))
+ .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
+ .put_float(degrees(atan2(-c[1], -c[0])))
+ .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
+ .put_symbol("DA");
+ else
+ out.put_fix_number(p[0] + p[2] + env->hpos)
+ .put_fix_number(p[1] + p[3] + env->vpos)
+ .put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("DL");
+ }
+ break;
+ case 't':
+ {
+ if (np == 0) {
+ line_thickness = -1;
+ }
+ else {
+ // troff gratuitously adds an extra 0
+ if (np != 1 && np != 2) {
+ error("0 or 1 argument required for thickness");
+ break;
+ }
+ line_thickness = p[0];
+ }
+ break;
+ }
+ case 'f':
+ {
+ if (np != 1 && np != 2) {
+ error("1 argument required for fill");
+ break;
+ }
+ fill = p[0];
+ if (fill < 0 || fill > FILL_MAX) {
+ // This means fill with the current color.
+ fill = FILL_MAX + 1;
+ }
+ break;
+ }
+ default:
+ error("unrecognised drawing command `%1'", char(code));
+ break;
+ }
+
+ output_hpos = output_vpos = -1;
+}
+
+
+void ps_printer::begin_page(int n)
+{
+ out.begin_comment("Page:").comment_arg(i_to_a(n));
+ out.comment_arg(i_to_a(++pages_output)).end_comment();
+ output_style.f = 0;
+ output_space_code = 32;
+ output_draw_point_size = -1;
+ output_line_thickness = -1;
+ output_hpos = output_vpos = -1;
+ ndefined_styles = 0;
+ out.simple_comment("BeginPageSetup");
+ out.put_symbol("BP");
+ out.simple_comment("EndPageSetup");
+}
+
+void ps_printer::end_page(int)
+{
+ flush_sbuf();
+ out.put_symbol("EP");
+ if (invis_count != 0) {
+ error("missing `endinvis' command");
+ invis_count = 0;
+ }
+}
+
+font *ps_printer::make_font(const char *nm)
+{
+ return ps_font::load_ps_font(nm);
+}
+
+ps_printer::~ps_printer()
+{
+ out.simple_comment("Trailer");
+ out.put_symbol("end");
+ out.simple_comment("EOF");
+ if (fseek(tempfp, 0L, 0) < 0)
+ fatal("fseek on temporary file failed");
+ fputs("%!PS-Adobe-", stdout);
+ fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
+ putchar('\n');
+ out.set_file(stdout);
+ {
+ extern const char *Version_string;
+ out.begin_comment("Creator:")
+ .comment_arg("groff")
+ .comment_arg("version")
+ .comment_arg(Version_string)
+ .end_comment();
+ }
+ {
+ fputs("%%CreationDate: ", out.get_file());
+#ifdef LONG_FOR_TIME_T
+ long
+#else
+ time_t
+#endif
+ t = time(0);
+ fputs(ctime(&t), out.get_file());
+ }
+ for (font_pointer_list *f = font_list; f; f = f->next) {
+ ps_font *psf = (ps_font *)(f->p);
+ rm.need_font(psf->get_internal_name());
+ }
+ rm.print_header_comments(out);
+ out.begin_comment("Pages:").comment_arg(i_to_a(pages_output)).end_comment();
+ out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
+#if 0
+ fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
+ font::paperwidth*72.0/font::res,
+ paper_length*72.0/font::res);
+#endif
+ out.begin_comment("Orientation:")
+ .comment_arg(landscape_flag ? "Landscape" : "Portrait")
+ .end_comment();
+ if (ncopies != 1) {
+ out.end_line();
+ fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
+ }
+ out.simple_comment("EndComments");
+ out.simple_comment("BeginProlog");
+ rm.output_prolog(out);
+ if (!(broken_flags & NO_SETUP_SECTION)) {
+ out.simple_comment("EndProlog");
+ out.simple_comment("BeginSetup");
+ }
+ rm.document_setup(out);
+ out.put_symbol(dict_name).put_symbol("begin");
+ if (ndefs > 0)
+ ndefs += DEFS_DICT_SPARE;
+ out.put_literal_symbol(defs_dict_name)
+ .put_number(ndefs + 1)
+ .put_symbol("dict")
+ .put_symbol("def");
+ out.put_symbol(defs_dict_name)
+ .put_symbol("begin");
+ out.put_literal_symbol("u")
+ .put_delimiter('{')
+ .put_fix_number(1)
+ .put_symbol("mul")
+ .put_delimiter('}')
+ .put_symbol("bind")
+ .put_symbol("def");
+ defs += '\0';
+ out.special(defs.contents());
+ out.put_symbol("end");
+ if (ncopies != 1)
+ out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
+ out.put_literal_symbol("RES").put_number(res).put_symbol("def");
+ out.put_literal_symbol("PL");
+ if (guess_flag)
+ out.put_symbol("PLG");
+ else
+ out.put_fix_number(paper_length);
+ out.put_symbol("def");
+ out.put_literal_symbol("LS")
+ .put_symbol(landscape_flag ? "true" : "false")
+ .put_symbol("def");
+ if (manual_feed_flag) {
+ out.begin_comment("BeginFeature:")
+ .comment_arg("*ManualFeed")
+ .comment_arg("True")
+ .end_comment()
+ .put_symbol("MANUAL")
+ .simple_comment("EndFeature");
+ }
+ encode_fonts();
+ out.simple_comment((broken_flags & NO_SETUP_SECTION)
+ ? "EndProlog"
+ : "EndSetup");
+ out.end_line();
+ out.copy_file(tempfp);
+ fclose(tempfp);
+}
+
+void ps_printer::special(char *arg, const environment *env, char type)
+{
+ if (type != 'p')
+ return;
+ typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
+ static struct {
+ const char *name;
+ SPECIAL_PROCP proc;
+ } proc_table[] = {
+ { "exec", &ps_printer::do_exec },
+ { "def", &ps_printer::do_def },
+ { "mdef", &ps_printer::do_mdef },
+ { "import", &ps_printer::do_import },
+ { "file", &ps_printer::do_file },
+ { "invis", &ps_printer::do_invis },
+ { "endinvis", &ps_printer::do_endinvis },
+ };
+ char *p;
+ for (p = arg; *p == ' ' || *p == '\n'; p++)
+ ;
+ char *tag = p;
+ for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
+ ;
+ if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
+ error("X command without `ps:' tag ignored");
+ return;
+ }
+ p++;
+ for (; *p == ' ' || *p == '\n'; p++)
+ ;
+ char *command = p;
+ for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
+ ;
+ if (*command == '\0') {
+ error("X command without `ps:' tag ignored");
+ return;
+ }
+ for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
+ if (strncmp(command, proc_table[i].name, p - command) == 0) {
+ (this->*(proc_table[i].proc))(p, env);
+ return;
+ }
+ error("X command `%1' not recognised", command);
+}
+
+// A conforming PostScript document must not have lines longer
+// than 255 characters (excluding line termination characters).
+
+static int check_line_lengths(const char *p)
+{
+ for (;;) {
+ const char *end = strchr(p, '\n');
+ if (end == 0)
+ end = strchr(p, '\0');
+ if (end - p > 255)
+ return 0;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ return 1;
+}
+
+void ps_printer::do_exec(char *arg, const environment *env)
+{
+ flush_sbuf();
+ while (csspace(*arg))
+ arg++;
+ if (*arg == '\0') {
+ error("missing argument to X exec command");
+ return;
+ }
+ if (!check_line_lengths(arg)) {
+ error("lines in X exec command must not be more than 255 characters long");
+ return;
+ }
+ out.put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("EBEGIN")
+ .special(arg)
+ .put_symbol("EEND");
+ output_hpos = output_vpos = -1;
+ output_style.f = 0;
+ output_draw_point_size = -1;
+ output_line_thickness = -1;
+ ndefined_styles = 0;
+ if (!ndefs)
+ ndefs = 1;
+}
+
+void ps_printer::do_file(char *arg, const environment *env)
+{
+ flush_sbuf();
+ while (csspace(*arg))
+ arg++;
+ if (*arg == '\0') {
+ error("missing argument to X file command");
+ return;
+ }
+ const char *filename = arg;
+ do {
+ ++arg;
+ } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
+ out.put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("EBEGIN");
+ rm.import_file(filename, out);
+ out.put_symbol("EEND");
+ output_hpos = output_vpos = -1;
+ output_style.f = 0;
+ output_draw_point_size = -1;
+ output_line_thickness = -1;
+ ndefined_styles = 0;
+ if (!ndefs)
+ ndefs = 1;
+}
+
+void ps_printer::do_def(char *arg, const environment *)
+{
+ flush_sbuf();
+ while (csspace(*arg))
+ arg++;
+ if (!check_line_lengths(arg)) {
+ error("lines in X def command must not be more than 255 characters long");
+ return;
+ }
+ defs += arg;
+ if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
+ defs += '\n';
+ ndefs++;
+}
+
+// Like def, but the first argument says how many definitions it contains.
+
+void ps_printer::do_mdef(char *arg, const environment *)
+{
+ flush_sbuf();
+ char *p;
+ int n = (int)strtol(arg, &p, 10);
+ if (n == 0 && p == arg) {
+ error("first argument to X mdef must be an integer");
+ return;
+ }
+ if (n < 0) {
+ error("out of range argument `%1' to X mdef command", int(n));
+ return;
+ }
+ arg = p;
+ while (csspace(*arg))
+ arg++;
+ if (!check_line_lengths(arg)) {
+ error("lines in X mdef command must not be more than 255 characters long");
+ return;
+ }
+ defs += arg;
+ if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
+ defs += '\n';
+ ndefs += n;
+}
+
+void ps_printer::do_import(char *arg, const environment *env)
+{
+ flush_sbuf();
+ while (*arg == ' ' || *arg == '\n')
+ arg++;
+ char *p;
+ for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
+ ;
+ if (*p != '\0')
+ *p++ = '\0';
+ int parms[6];
+ int nparms = 0;
+ while (nparms < 6) {
+ char *end;
+ long n = strtol(p, &end, 10);
+ if (n == 0 && end == p)
+ break;
+ parms[nparms++] = int(n);
+ p = end;
+ }
+ if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
+ error("scaling indicators not allowed in arguments for X import command");
+ return;
+ }
+ while (*p == ' ' || *p == '\n')
+ p++;
+ if (nparms < 5) {
+ if (*p == '\0')
+ error("too few arguments for X import command");
+ else
+ error("invalid argument `%1' for X import command", p);
+ return;
+ }
+ if (*p != '\0') {
+ error("superflous argument `%1' for X import command", p);
+ return;
+ }
+ int llx = parms[0];
+ int lly = parms[1];
+ int urx = parms[2];
+ int ury = parms[3];
+ int desired_width = parms[4];
+ int desired_height = parms[5];
+ if (desired_width <= 0) {
+ error("bad width argument `%1' for X import command: must be > 0",
+ desired_width);
+ return;
+ }
+ if (nparms == 6 && desired_height <= 0) {
+ error("bad height argument `%1' for X import command: must be > 0",
+ desired_height);
+ return;
+ }
+ if (llx == urx) {
+ error("llx and urx arguments for X import command must not be equal");
+ return;
+ }
+ if (lly == ury) {
+ error("lly and ury arguments for X import command must not be equal");
+ return;
+ }
+ if (nparms == 5) {
+ int old_wid = urx - llx;
+ int old_ht = ury - lly;
+ if (old_wid < 0)
+ old_wid = -old_wid;
+ if (old_ht < 0)
+ old_ht = -old_ht;
+ desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
+ }
+ if (env->vpos - desired_height < 0)
+ warning("top of imported graphic is above the top of the page");
+ out.put_number(llx)
+ .put_number(lly)
+ .put_fix_number(desired_width)
+ .put_number(urx - llx)
+ .put_fix_number(-desired_height)
+ .put_number(ury - lly)
+ .put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("PBEGIN");
+ rm.import_file(arg, out);
+ // do this here just in case application defines PEND
+ out.put_symbol("end");
+ out.put_symbol("PEND");
+}
+
+void ps_printer::do_invis(char *, const environment *)
+{
+ invis_count++;
+}
+
+void ps_printer::do_endinvis(char *, const environment *)
+{
+ if (invis_count == 0)
+ error("unbalanced `endinvis' command");
+ else
+ --invis_count;
+}
+
+printer *make_printer()
+{
+ return new ps_printer;
+}
+
+static void usage(FILE *stream);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ string env;
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, "F:P:glmc:w:vb:", long_options, NULL))
+ != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU grops (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'c':
+ if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
+ error("bad number of copies `%s'", optarg);
+ ncopies = 1;
+ }
+ break;
+ case 'g':
+ guess_flag = 1;
+ break;
+ case 'l':
+ landscape_flag = 1;
+ break;
+ case 'm':
+ manual_feed_flag = 1;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'P':
+ env = "GROPS_PROLOGUE";
+ env += '=';
+ env += optarg;
+ env += '\0';
+ if (putenv(strsave(env.contents())))
+ fatal("putenv failed");
+ break;
+ case 'w':
+ if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
+ error("bad linewidth `%1'", optarg);
+ linewidth = -1;
+ }
+ break;
+ case 'b':
+ // XXX check this
+ broken_flags = atoi(optarg);
+ bflag = 1;
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ font::set_unknown_desc_command_handler(handle_unknown_desc_command);
+#ifdef SET_BINARY
+ SET_BINARY(fileno(stdout));
+#endif
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-glmv] [-b n] [-c n] [-w n] [-P prologue] [-F dir] [files ...]\n",
+ program_name);
+}
diff --git a/contrib/groff/src/devices/grops/ps.h b/contrib/groff/src/devices/grops/ps.h
new file mode 100644
index 000000000000..6e78597d1276
--- /dev/null
+++ b/contrib/groff/src/devices/grops/ps.h
@@ -0,0 +1,122 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+class ps_output {
+public:
+ ps_output(FILE *, int max_line_length);
+ ps_output &put_string(const char *, int);
+ ps_output &put_number(int);
+ ps_output &put_fix_number(int);
+ ps_output &put_float(double);
+ ps_output &put_symbol(const char *);
+ ps_output &put_literal_symbol(const char *);
+ ps_output &set_fixed_point(int);
+ ps_output &simple_comment(const char *);
+ ps_output &begin_comment(const char *);
+ ps_output &comment_arg(const char *);
+ ps_output &end_comment();
+ ps_output &set_file(FILE *);
+ ps_output &include_file(FILE *);
+ ps_output &copy_file(FILE *);
+ ps_output &end_line();
+ ps_output &put_delimiter(char);
+ ps_output &special(const char *);
+ FILE *get_file();
+private:
+ FILE *fp;
+ int col;
+ int max_line_length; // not including newline
+ int need_space;
+ int fixed_point;
+};
+
+inline FILE *ps_output::get_file()
+{
+ return fp;
+}
+
+enum resource_type {
+ RESOURCE_FONT,
+ RESOURCE_PROCSET,
+ RESOURCE_FILE,
+ RESOURCE_ENCODING,
+ RESOURCE_FORM,
+ RESOURCE_PATTERN
+ };
+
+struct resource;
+
+extern string an_empty_string;
+
+class resource_manager {
+public:
+ resource_manager();
+ ~resource_manager();
+ void import_file(const char *filename, ps_output &);
+ void need_font(const char *name);
+ void print_header_comments(ps_output &);
+ void document_setup(ps_output &);
+ void output_prolog(ps_output &);
+private:
+ unsigned extensions;
+ unsigned language_level;
+ resource *procset_resource;
+ resource *resource_list;
+ resource *lookup_resource(resource_type type, string &name,
+ string &version = an_empty_string,
+ unsigned revision = 0);
+ resource *lookup_font(const char *name);
+ void read_download_file();
+ void supply_resource(resource *r, int rank, FILE *outfp,
+ int is_document = 0);
+ void process_file(int rank, FILE *fp, const char *filename, FILE *outfp);
+ resource *read_file_arg(const char **);
+ resource *read_procset_arg(const char **);
+ resource *read_font_arg(const char **);
+ resource *read_resource_arg(const char **);
+ void print_resources_comment(unsigned flag, FILE *outfp);
+ void print_extensions_comment(FILE *outfp);
+ void print_language_level_comment(FILE *outfp);
+ int do_begin_resource(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_include_resource(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_document(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_include_document(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_procset(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_include_procset(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_font(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_include_font(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_file(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_include_file(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int change_to_end_resource(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_preview(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_data(const char *ptr, int rank, FILE *fp, FILE *outfp);
+ int do_begin_binary(const char *ptr, int rank, FILE *fp, FILE *outfp);
+};
+
+extern unsigned broken_flags;
+
+// broken_flags is ored from these
+
+enum {
+ NO_SETUP_SECTION = 01,
+ STRIP_PERCENT_BANG = 02,
+ STRIP_STRUCTURE_COMMENTS = 04,
+ USE_PS_ADOBE_2_0 = 010
+};
diff --git a/contrib/groff/src/devices/grops/psfig.diff b/contrib/groff/src/devices/grops/psfig.diff
new file mode 100644
index 000000000000..5be080ddd933
--- /dev/null
+++ b/contrib/groff/src/devices/grops/psfig.diff
@@ -0,0 +1,106 @@
+These are patches to makes psfig work with groff. They apply to the
+version of psfig in comp.sources.unix/Volume11. After applying them,
+psfig should be recompiled with -DGROFF. The resulting psfig will
+work only with groff, so you might want to install it under a
+different name. The output of this psfig must be processed using the
+macros in the file ../tmac/tmac.psfig. These will automatically add
+the necessary PostScript code to the prologue output by grops. Use of
+the `global' feature in psfig will result in non-conformant PostScript
+which will fail if processed by a page reversal program. Note that
+psfig is unsupported by me (I'm not interested in hearing about psfig
+problems.) For new documents, I recommend using the PostScript
+inclusion features provided by grops.
+
+James Clark
+jjc@jclark.com
+
+*** cmds.c.~1~ Thu Feb 14 16:09:45 1991
+--- cmds.c Mon Mar 4 12:49:26 1991
+***************
+*** 245,253 ****
+--- 245,261 ----
+ (void) sprintf(x, "%.2fp", fx);
+ (void) sprintf(y, "%.2fp", fy);
+ } else if (!*x) {
++ #ifndef GROFF
+ (void) sprintf(x,"(%.2fp*%s/%.2fp)", fx, y, fy);
++ #else /* GROFF */
++ (void) sprintf(x,"(%.0fu*%s/%.0fu)", fx, y, fy);
++ #endif /* GROFF */
+ } else if (!*y) {
++ #ifndef GROFF
+ (void) sprintf(y,"(%.2fp*%s/%.2fp)", fy, x, fx);
++ #else /* GROFF */
++ (void) sprintf(y,"(%.0fu*%s/%.0fu)", fy, x, fx);
++ #endif /* GROFF */
+ }
+
+ /*
+*** troff.c.~1~ Thu Feb 14 16:09:48 1991
+--- troff.c Mon Mar 4 12:48:46 1991
+***************
+*** 26,32 ****
+--- 26,36 ----
+ }
+
+
++ #ifndef GROFF
+ char incl_file_s[] = "\\X'f%s'";
++ #else /* GROFF */
++ char incl_file_s[] = "\\X'ps: file %s'";
++ #endif /* GROFF */
+ includeFile(filenm)
+ char *filenm; {
+ printf(incl_file_s, filenm);
+***************
+*** 40,52 ****
+--- 44,64 ----
+ error("buffer overflow");
+ }
+
++ #ifndef GROFF
+ char endfig_s[] = "\\X'pendFig'";
++ #else /* GROFF */
++ char endfig_s[] = "\\X'ps: exec psfigend'";
++ #endif /* GROFF */
+ endfig() {
+ printf(endfig_s);
+ }
+
+ char startfig_s[] =
++ #ifndef GROFF
+ "\\X'p\\w@\\h@%s@@'\\X'p\\w@\\h@%s@@'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'pstartFig'";
++ #else /* GROFF */
++ "\\X'ps: exec \\w@\\h@%s@@ \\w@\\h@%s@@ %.2f %.2f %.2f %.2f psfigstart'";
++ #endif /* GROFF */
+
+ startfig(x, y, llx, lly, urx, ury)
+ char *x, *y;
+***************
+*** 57,63 ****
+--- 69,79 ----
+ }
+
+ emitDoClip() {
++ #ifndef GROFF
+ printf("\\X'pdoclip'");
++ #else /* GROFF */
++ printf("\\X'ps: exec psfigclip'");
++ #endif /* GROFF */
+ }
+
+ flushX()
+***************
+*** 116,122 ****
+--- 132,142 ----
+
+ #define isWhite(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n')
+
++ #ifndef GROFF
+ char literal_s[] = "\\X'p%s'";
++ #else /* GROFF */
++ char literal_s[] = "\\X'ps: exec %s'";
++ #endif /* GROFF */
+ emitLiteral(text)
+ char *text; {
+ static char litbuf[BUFSZ];
diff --git a/contrib/groff/src/devices/grops/psrm.cc b/contrib/groff/src/devices/grops/psrm.cc
new file mode 100644
index 000000000000..b816c6b20cde
--- /dev/null
+++ b/contrib/groff/src/devices/grops/psrm.cc
@@ -0,0 +1,1118 @@
+// -*- 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. */
+
+#include "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#include "ps.h"
+
+#ifdef NEED_DECLARATION_PUTENV
+extern "C" {
+ int putenv(const char *);
+}
+#endif /* NEED_DECLARATION_PUTENV */
+
+#define GROPS_PROLOGUE "prologue"
+
+static void print_ps_string(const string &s, FILE *outfp);
+
+cset white_space("\n\r \t");
+string an_empty_string;
+
+const char *extension_table[] = {
+ "DPS",
+ "CMYK",
+ "Composite",
+ "FileSystem",
+};
+
+const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
+
+const char *resource_table[] = {
+ "font",
+ "procset",
+ "file",
+ "encoding",
+ "form",
+ "pattern",
+};
+
+const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
+
+static int read_uint_arg(const char **pp, unsigned *res)
+{
+ while (white_space(**pp))
+ *pp += 1;
+ if (**pp == '\0') {
+ error("missing argument");
+ return 0;
+ }
+ const char *start = *pp;
+ // XXX use strtoul
+ long n = strtol(start, (char **)pp, 10);
+ if (n == 0 && *pp == start) {
+ error("not an integer");
+ return 0;
+ }
+ if (n < 0) {
+ error("argument must not be negative");
+ return 0;
+ }
+ *res = unsigned(n);
+ return 1;
+}
+
+struct resource {
+ resource *next;
+ resource_type type;
+ string name;
+ enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
+ unsigned flags;
+ string version;
+ unsigned revision;
+ char *filename;
+ int rank;
+ resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
+ ~resource();
+ void print_type_and_name(FILE *outfp);
+};
+
+resource::resource(resource_type t, string &n, string &v, unsigned r)
+: next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
+{
+ name.move(n);
+ version.move(v);
+ if (type == RESOURCE_FILE) {
+ if (name.search('\0') >= 0)
+ error("filename contains a character with code 0");
+ filename = name.extract();
+ }
+}
+
+resource::~resource()
+{
+ a_delete filename;
+}
+
+void resource::print_type_and_name(FILE *outfp)
+{
+ fputs(resource_table[type], outfp);
+ putc(' ', outfp);
+ print_ps_string(name, outfp);
+ if (type == RESOURCE_PROCSET) {
+ putc(' ', outfp);
+ print_ps_string(version, outfp);
+ fprintf(outfp, " %u", revision);
+ }
+}
+
+resource_manager::resource_manager()
+: extensions(0), language_level(0), resource_list(0)
+{
+ read_download_file();
+ string procset_name("grops");
+ extern const char *version_string;
+ extern const char *revision_string;
+ unsigned revision_uint;
+ if ( !read_uint_arg( &revision_string, &revision_uint) )
+ revision_uint = 0;
+ string procset_version(version_string);
+ procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
+ procset_version, revision_uint);
+ procset_resource->flags |= resource::SUPPLIED;
+}
+
+resource_manager::~resource_manager()
+{
+ while (resource_list) {
+ resource *tem = resource_list;
+ resource_list = resource_list->next;
+ delete tem;
+ }
+}
+
+resource *resource_manager::lookup_resource(resource_type type,
+ string &name,
+ string &version,
+ unsigned revision)
+{
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->type == type
+ && r->name == name
+ && r->version == version
+ && r->revision == revision)
+ return r;
+ r = new resource(type, name, version, revision);
+ r->next = resource_list;
+ resource_list = r;
+ return r;
+}
+
+// Just a specialized version of lookup_resource().
+
+resource *resource_manager::lookup_font(const char *name)
+{
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT
+ && strlen(name) == r->name.length()
+ && memcmp(name, r->name.contents(), r->name.length()) == 0)
+ return r;
+ string s(name);
+ r = new resource(RESOURCE_FONT, s);
+ r->next = resource_list;
+ resource_list = r;
+ return r;
+}
+
+void resource_manager::need_font(const char *name)
+{
+ lookup_font(name)->flags |= resource::FONT_NEEDED;
+}
+
+typedef resource *Presource; // Work around g++ bug.
+
+void resource_manager::document_setup(ps_output &out)
+{
+ int nranks = 0;
+ resource *r;
+ for (r = resource_list; r; r = r->next)
+ if (r->rank >= nranks)
+ nranks = r->rank + 1;
+ if (nranks > 0) {
+ // Sort resource_list in reverse order of rank.
+ Presource *head = new Presource[nranks + 1];
+ Presource **tail = new Presource *[nranks + 1];
+ int i;
+ for (i = 0; i < nranks + 1; i++) {
+ head[i] = 0;
+ tail[i] = &head[i];
+ }
+ for (r = resource_list; r; r = r->next) {
+ i = r->rank < 0 ? 0 : r->rank + 1;
+ *tail[i] = r;
+ tail[i] = &(*tail[i])->next;
+ }
+ resource_list = 0;
+ for (i = 0; i < nranks + 1; i++)
+ if (head[i]) {
+ *tail[i] = resource_list;
+ resource_list = head[i];
+ }
+ a_delete head;
+ a_delete tail;
+ // check it
+ for (r = resource_list; r; r = r->next)
+ if (r->next)
+ assert(r->rank >= r->next->rank);
+ for (r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT && r->rank >= 0)
+ supply_resource(r, -1, out.get_file());
+ }
+}
+
+void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
+{
+ int continued = 0;
+ for (resource *r = resource_list; r; r = r->next)
+ if (r->flags & flag) {
+ if (continued)
+ fputs("%%+ ", outfp);
+ else {
+ fputs(flag == resource::NEEDED
+ ? "%%DocumentNeededResources: "
+ : "%%DocumentSuppliedResources: ",
+ outfp);
+ continued = 1;
+ }
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+}
+
+void resource_manager::print_header_comments(ps_output &out)
+{
+ for (resource *r = resource_list; r; r = r->next)
+ if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
+ supply_resource(r, 0, 0);
+ print_resources_comment(resource::NEEDED, out.get_file());
+ print_resources_comment(resource::SUPPLIED, out.get_file());
+ print_language_level_comment(out.get_file());
+ print_extensions_comment(out.get_file());
+}
+
+void resource_manager::output_prolog(ps_output &out)
+{
+ FILE *outfp = out.get_file();
+ out.end_line();
+ char *path;
+ if (!getenv("GROPS_PROLOGUE")) {
+ string e = "GROPS_PROLOGUE";
+ e += '=';
+ e += GROPS_PROLOGUE;
+ e += '\0';
+ if (putenv(strsave(e.contents())))
+ fatal("putenv failed");
+ }
+ char *prologue = getenv("GROPS_PROLOGUE");
+ FILE *fp = font::open_file(prologue, &path);
+ if (!fp)
+ fatal("can't find `%1'", prologue);
+ fputs("%%BeginResource: ", outfp);
+ procset_resource->print_type_and_name(outfp);
+ putc('\n', outfp);
+ process_file(-1, fp, path, outfp);
+ fclose(fp);
+ a_delete path;
+ fputs("%%EndResource\n", outfp);
+}
+
+void resource_manager::import_file(const char *filename, ps_output &out)
+{
+ out.end_line();
+ string name(filename);
+ resource *r = lookup_resource(RESOURCE_FILE, name);
+ supply_resource(r, -1, out.get_file(), 1);
+}
+
+void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
+ int is_document)
+{
+ if (r->flags & resource::BUSY) {
+ r->name += '\0';
+ fatal("loop detected in dependency graph for %1 `%2'",
+ resource_table[r->type],
+ r->name.contents());
+ }
+ r->flags |= resource::BUSY;
+ if (rank > r->rank)
+ r->rank = rank;
+ char *path;
+ FILE *fp = 0;
+ if (r->filename != 0) {
+ if (r->type == RESOURCE_FONT) {
+ fp = font::open_file(r->filename, &path);
+ if (!fp) {
+ error("can't find `%1'", r->filename);
+ a_delete r->filename;
+ r->filename = 0;
+ }
+ }
+ else {
+ errno = 0;
+ fp = fopen(r->filename, "r");
+ if (!fp) {
+ error("can't open `%1': %2", r->filename, strerror(errno));
+ a_delete r->filename;
+ r->filename = 0;
+ }
+ else
+ path = r->filename;
+ }
+ }
+ if (fp) {
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document) {
+ fputs("%%BeginDocument: ", outfp);
+ print_ps_string(r->name, outfp);
+ putc('\n', outfp);
+ }
+ else {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ process_file(rank, fp, path, outfp);
+ fclose(fp);
+ if (r->type == RESOURCE_FONT)
+ a_delete path;
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document)
+ fputs("%%EndDocument\n", outfp);
+ else
+ fputs("%%EndResource\n", outfp);
+ }
+ r->flags |= resource::SUPPLIED;
+ }
+ else {
+ if (outfp) {
+ if (r->type == RESOURCE_FILE && is_document) {
+ fputs("%%IncludeDocument: ", outfp);
+ print_ps_string(r->name, outfp);
+ putc('\n', outfp);
+ }
+ else {
+ fputs("%%IncludeResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ r->flags |= resource::NEEDED;
+ }
+ r->flags &= ~resource::BUSY;
+}
+
+
+#define PS_LINE_MAX 255
+#define PS_MAGIC "%!PS-Adobe-"
+
+static int ps_get_line(char *buf, FILE *fp)
+{
+ int c = getc(fp);
+ if (c == EOF) {
+ buf[0] = '\0';
+ return 0;
+ }
+ current_lineno++;
+ int i = 0;
+ int err = 0;
+ while (c != '\r' && c != '\n' && c != EOF) {
+ if ((c < 0x1b && !white_space(c)) || c == 0x7f)
+ error("illegal input character code %1", int(c));
+ else if (i < PS_LINE_MAX)
+ buf[i++] = c;
+ else if (!err) {
+ err = 1;
+ error("PostScript file non-conforming "
+ "because length of line exceeds 255");
+ }
+ 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;
+}
+
+static int read_text_arg(const char **pp, string &res)
+{
+ res.clear();
+ while (white_space(**pp))
+ *pp += 1;
+ if (**pp == '\0') {
+ error("missing argument");
+ return 0;
+ }
+ if (**pp != '(') {
+ for (; **pp != '\0' && !white_space(**pp); *pp += 1)
+ res += **pp;
+ return 1;
+ }
+ *pp += 1;
+ res.clear();
+ int level = 0;
+ for (;;) {
+ if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
+ error("missing ')'");
+ return 0;
+ }
+ if (**pp == ')') {
+ if (level == 0) {
+ *pp += 1;
+ break;
+ }
+ res += **pp;
+ level--;
+ }
+ else if (**pp == '(') {
+ level++;
+ res += **pp;
+ }
+ else if (**pp == '\\') {
+ *pp += 1;
+ switch (**pp) {
+ case 'n':
+ res += '\n';
+ break;
+ case 'r':
+ res += '\n';
+ break;
+ case 't':
+ res += '\t';
+ break;
+ case 'b':
+ res += '\b';
+ break;
+ case 'f':
+ res += '\f';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int val = **pp - '0';
+ if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
+ *pp += 1;
+ val = val*8 + (**pp - '0');
+ if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
+ *pp += 1;
+ val = val*8 + (**pp - '0');
+ }
+ }
+ }
+ break;
+ default:
+ res += **pp;
+ break;
+ }
+ }
+ else
+ res += **pp;
+ *pp += 1;
+ }
+ return 1;
+}
+
+resource *resource_manager::read_file_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(RESOURCE_FILE, arg);
+}
+
+resource *resource_manager::read_font_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(RESOURCE_FONT, arg);
+}
+
+resource *resource_manager::read_procset_arg(const char **ptr)
+{
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ string version;
+ if (!read_text_arg(ptr, version))
+ return 0;
+ unsigned revision;
+ if (!read_uint_arg(ptr, &revision))
+ return 0;
+ return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
+}
+
+resource *resource_manager::read_resource_arg(const char **ptr)
+{
+ while (white_space(**ptr))
+ *ptr += 1;
+ const char *name = *ptr;
+ while (**ptr != '\0' && !white_space(**ptr))
+ *ptr += 1;
+ if (name == *ptr) {
+ error("missing resource type");
+ return 0;
+ }
+ int ri;
+ for (ri = 0; ri < NRESOURCES; ri++)
+ if (strlen(resource_table[ri]) == *ptr - name
+ && memcmp(resource_table[ri], name, *ptr - name) == 0)
+ break;
+ if (ri >= NRESOURCES) {
+ error("unknown resource type");
+ return 0;
+ }
+ if (ri == RESOURCE_PROCSET)
+ return read_procset_arg(ptr);
+ string arg;
+ if (!read_text_arg(ptr, arg))
+ return 0;
+ return lookup_resource(resource_type(ri), arg);
+}
+
+static const char *matches_comment(const char *buf, const char *comment)
+{
+ if (buf[0] != '%' || buf[1] != '%')
+ return 0;
+ for (buf += 2; *comment; comment++, buf++)
+ if (*buf != *comment)
+ return 0;
+ if (comment[-1] == ':')
+ return buf;
+ if (*buf == '\0' || white_space(*buf))
+ return buf;
+ return 0;
+}
+
+// Return 1 if the line should be copied out.
+
+int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
+ FILE *)
+{
+ resource *r = read_resource_arg(&ptr);
+ if (r)
+ r->flags |= resource::SUPPLIED;
+ return 1;
+}
+
+int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_resource_arg(&ptr);
+ if (r) {
+ if (r->type == RESOURCE_FONT) {
+ if (rank >= 0)
+ supply_resource(r, rank + 1, outfp);
+ else
+ r->flags |= resource::FONT_NEEDED;
+ }
+ else
+ supply_resource(r, rank, outfp);
+ }
+ return 0;
+}
+
+int resource_manager::do_begin_document(const char *ptr, int, FILE *,
+ FILE *)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ r->flags |= resource::SUPPLIED;
+ return 1;
+}
+
+int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp, 1);
+ return 0;
+}
+
+int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_procset_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_procset_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_file(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_file_arg(&ptr);
+ if (r)
+ supply_resource(r, rank, outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_font(const char *ptr, int, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_font_arg(&ptr);
+ if (r) {
+ r->flags |= resource::SUPPLIED;
+ if (outfp) {
+ fputs("%%BeginResource: ", outfp);
+ r->print_type_and_name(outfp);
+ putc('\n', outfp);
+ }
+ }
+ return 0;
+}
+
+int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
+ FILE *outfp)
+{
+ resource *r = read_font_arg(&ptr);
+ if (r) {
+ if (rank >= 0)
+ supply_resource(r, rank + 1, outfp);
+ else
+ r->flags |= resource::FONT_NEEDED;
+ }
+ return 0;
+}
+
+int resource_manager::change_to_end_resource(const char *, int, FILE *,
+ FILE *outfp)
+{
+ if (outfp)
+ fputs("%%EndResource\n", outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
+{
+ char buf[PS_LINE_MAX + 2];
+ do {
+ if (!ps_get_line(buf, fp)) {
+ error("end of file in preview section");
+ break;
+ }
+ } while (!matches_comment(buf, "EndPreview"));
+ return 0;
+}
+
+int read_one_of(const char **ptr, const char **s, int n)
+{
+ while (white_space(**ptr))
+ *ptr += 1;
+ if (**ptr == '\0')
+ return -1;
+ const char *start = *ptr;
+ do {
+ ++(*ptr);
+ } while (**ptr != '\0' && !white_space(**ptr));
+ for (int i = 0; i < n; i++)
+ if (strlen(s[i]) == *ptr - start
+ && memcmp(s[i], start, *ptr - start) == 0)
+ return i;
+ return -1;
+}
+
+int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
+ FILE *outfp)
+{
+ while (white_space(*ptr))
+ ptr++;
+ const char *start = ptr;
+ unsigned numberof;
+ if (!read_uint_arg(&ptr, &numberof))
+ return 0;
+ static const char *types[] = { "Binary", "Hex", "ASCII" };
+ const int Binary = 0;
+ int type = 0;
+ static const char *units[] = { "Bytes", "Lines" };
+ const int Bytes = 0;
+ int unit = Bytes;
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr != '\0') {
+ type = read_one_of(&ptr, types, 3);
+ if (type < 0) {
+ error("bad data type");
+ return 0;
+ }
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr != '\0') {
+ unit = read_one_of(&ptr, units, 2);
+ if (unit < 0) {
+ error("expected `Bytes' or `Lines'");
+ return 0;
+ }
+ }
+ }
+ if (type != Binary)
+ return 1;
+ if (outfp) {
+ fputs("%%BeginData: ", outfp);
+ fputs(start, outfp);
+ }
+ if (numberof > 0) {
+ unsigned bytecount = 0;
+ unsigned linecount = 0;
+ do {
+ int c = getc(fp);
+ if (c == EOF) {
+ error("end of file within data section");
+ return 0;
+ }
+ if (outfp)
+ putc(c, outfp);
+ bytecount++;
+ if (c == '\r') {
+ int cc = getc(fp);
+ if (cc != '\n') {
+ linecount++;
+ current_lineno++;
+ }
+ if (cc != EOF)
+ ungetc(c, fp);
+ }
+ else if (c == '\n') {
+ linecount++;
+ current_lineno++;
+ }
+ } while ((unit == Bytes ? bytecount : linecount) < numberof);
+ }
+ char buf[PS_LINE_MAX + 2];
+ if (!ps_get_line(buf, fp)) {
+ error("missing %%%%EndData line");
+ return 0;
+ }
+ if (!matches_comment(buf, "EndData"))
+ error("bad %%%%EndData line");
+ if (outfp)
+ fputs(buf, outfp);
+ return 0;
+}
+
+int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
+ FILE *outfp)
+{
+ if (!outfp)
+ return 0;
+ unsigned count;
+ if (!read_uint_arg(&ptr, &count))
+ return 0;
+ if (outfp)
+ fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
+ while (count != 0) {
+ int c = getc(fp);
+ if (c == EOF) {
+ error("end of file within binary section");
+ return 0;
+ }
+ if (outfp)
+ putc(c, outfp);
+ --count;
+ if (c == '\r') {
+ int cc = getc(fp);
+ if (cc != '\n')
+ current_lineno++;
+ if (cc != EOF)
+ ungetc(c, fp);
+ }
+ else if (c == '\n')
+ current_lineno++;
+ }
+ char buf[PS_LINE_MAX + 2];
+ if (!ps_get_line(buf, fp)) {
+ error("missing %%%%EndBinary line");
+ return 0;
+ }
+ if (!matches_comment(buf, "EndBinary")) {
+ error("bad %%%%EndBinary line");
+ if (outfp)
+ fputs(buf, outfp);
+ }
+ else if (outfp)
+ fputs("%%EndData\n", outfp);
+ return 0;
+}
+
+static unsigned parse_extensions(const char *ptr)
+{
+ unsigned flags = 0;
+ for (;;) {
+ while (white_space(*ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ const char *name = ptr;
+ do {
+ ++ptr;
+ } while (*ptr != '\0' && !white_space(*ptr));
+ int i;
+ for (i = 0; i < NEXTENSIONS; i++)
+ if (strlen(extension_table[i]) == ptr - name
+ && memcmp(extension_table[i], name, ptr - name) == 0) {
+ flags |= (1 << i);
+ break;
+ }
+ if (i >= NEXTENSIONS) {
+ string s(name, ptr - name);
+ s += '\0';
+ error("unknown extension `%1'", s.contents());
+ }
+ }
+ return flags;
+}
+
+// XXX if it has not been surrounded with {Begin,End}Document need to strip
+// out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
+
+// XXX Perhaps the decision whether to use BeginDocument or
+// BeginResource: file should be postponed till we have seen
+// the first line of the file.
+
+void resource_manager::process_file(int rank, FILE *fp, const char *filename,
+ FILE *outfp)
+{
+ // If none of these comments appear in the header section, and we are
+ // just analyzing the file (ie outfp is 0), then we can return immediately.
+ static const char *header_comment_table[] = {
+ "DocumentNeededResources:",
+ "DocumentSuppliedResources:",
+ "DocumentNeededFonts:",
+ "DocumentSuppliedFonts:",
+ "DocumentNeededProcSets:",
+ "DocumentSuppliedProcSets:",
+ "DocumentNeededFiles:",
+ "DocumentSuppliedFiles:",
+ };
+
+ const int NHEADER_COMMENTS = (sizeof(header_comment_table)
+ / sizeof(header_comment_table[0]));
+ struct comment_info {
+ const char *name;
+ int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
+ };
+
+ static comment_info comment_table[] = {
+ { "BeginResource:", &resource_manager::do_begin_resource },
+ { "IncludeResource:", &resource_manager::do_include_resource },
+ { "BeginDocument:", &resource_manager::do_begin_document },
+ { "IncludeDocument:", &resource_manager::do_include_document },
+ { "BeginProcSet:", &resource_manager::do_begin_procset },
+ { "IncludeProcSet:", &resource_manager::do_include_procset },
+ { "BeginFont:", &resource_manager::do_begin_font },
+ { "IncludeFont:", &resource_manager::do_include_font },
+ { "BeginFile:", &resource_manager::do_begin_file },
+ { "IncludeFile:", &resource_manager::do_include_file },
+ { "EndProcSet", &resource_manager::change_to_end_resource },
+ { "EndFont", &resource_manager::change_to_end_resource },
+ { "EndFile", &resource_manager::change_to_end_resource },
+ { "BeginPreview:", &resource_manager::do_begin_preview },
+ { "BeginData:", &resource_manager::do_begin_data },
+ { "BeginBinary:", &resource_manager::do_begin_binary },
+ };
+
+ const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
+ char buf[PS_LINE_MAX + 2];
+ int saved_lineno = current_lineno;
+ const char *saved_filename = current_filename;
+ current_filename = filename;
+ current_lineno = 0;
+ if (!ps_get_line(buf, fp)) {
+ current_filename = saved_filename;
+ current_lineno = saved_lineno;
+ return;
+ }
+ if (strlen(buf) < sizeof(PS_MAGIC) - 1
+ || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
+ if (outfp) {
+ do {
+ if (!(broken_flags & STRIP_PERCENT_BANG)
+ || buf[0] != '%' || buf[1] != '!')
+ fputs(buf, outfp);
+ } while (ps_get_line(buf, fp));
+ }
+ }
+ else {
+ if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
+ fputs(buf, outfp);
+ int in_header = 1;
+ int interesting = 0;
+ int had_extensions_comment = 0;
+ int had_language_level_comment = 0;
+ for (;;) {
+ if (!ps_get_line(buf, fp))
+ break;
+ int copy_this_line = 1;
+ if (buf[0] == '%') {
+ if (buf[1] == '%') {
+ const char *ptr;
+ int i;
+ for (i = 0; i < NCOMMENTS; i++)
+ if ((ptr = matches_comment(buf, comment_table[i].name))) {
+ copy_this_line
+ = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
+ break;
+ }
+ if (i >= NCOMMENTS && in_header) {
+ if ((ptr = matches_comment(buf, "EndComments")))
+ in_header = 0;
+ else if (!had_extensions_comment
+ && (ptr = matches_comment(buf, "Extensions:"))) {
+ extensions |= parse_extensions(ptr);
+ // XXX handle possibility that next line is %%+
+ had_extensions_comment = 1;
+ }
+ else if (!had_language_level_comment
+ && (ptr = matches_comment(buf, "LanguageLevel:"))) {
+ unsigned ll;
+ if (read_uint_arg(&ptr, &ll) && ll > language_level)
+ language_level = ll;
+ had_language_level_comment = 1;
+ }
+ else {
+ for (i = 0; i < NHEADER_COMMENTS; i++)
+ if (matches_comment(buf, header_comment_table[i])) {
+ interesting = 1;
+ break;
+ }
+ }
+ }
+ if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
+ && (matches_comment(buf, "EndProlog")
+ || matches_comment(buf, "Page:")
+ || matches_comment(buf, "Trailer")))
+ copy_this_line = 0;
+ }
+ else if (buf[1] == '!') {
+ if (broken_flags & STRIP_PERCENT_BANG)
+ copy_this_line = 0;
+ }
+ }
+ else
+ in_header = 0;
+ if (!outfp && !in_header && !interesting)
+ break;
+ if (copy_this_line && outfp)
+ fputs(buf, outfp);
+ }
+ }
+ current_filename = saved_filename;
+ current_lineno = saved_lineno;
+}
+
+void resource_manager::read_download_file()
+{
+ char *path = 0;
+ FILE *fp = font::open_file("download", &path);
+ if (!fp)
+ fatal("can't find `download'");
+ char buf[512];
+ int lineno = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ lineno++;
+ char *p = strtok(buf, " \t\r\n");
+ if (p == 0 || *p == '#')
+ continue;
+ char *q = strtok(0, " \t\r\n");
+ if (!q)
+ fatal_with_file_and_line(path, lineno, "missing filename");
+ lookup_font(p)->filename = strsave(q);
+ }
+ a_delete path;
+ fclose(fp);
+}
+
+// XXX Can we share some code with ps_output::put_string()?
+
+static void print_ps_string(const string &s, FILE *outfp)
+{
+ int len = s.length();
+ const char *str = s.contents();
+ int funny = 0;
+ if (str[0] == '(')
+ funny = 1;
+ else {
+ for (int i = 0; i < len; i++)
+ if (str[i] <= 040 || str[i] > 0176) {
+ funny = 1;
+ break;
+ }
+ }
+ if (!funny) {
+ put_string(s, outfp);
+ return;
+ }
+ int level = 0;
+ int i;
+ for (i = 0; i < len; i++)
+ if (str[i] == '(')
+ level++;
+ else if (str[i] == ')' && --level < 0)
+ break;
+ putc('(', outfp);
+ for (i = 0; i < len; i++)
+ switch (str[i]) {
+ case '(':
+ case ')':
+ if (level != 0)
+ putc('\\', outfp);
+ putc(str[i], outfp);
+ break;
+ case '\\':
+ fputs("\\\\", outfp);
+ break;
+ case '\n':
+ fputs("\\n", outfp);
+ break;
+ case '\r':
+ fputs("\\r", outfp);
+ break;
+ case '\t':
+ fputs("\\t", outfp);
+ break;
+ case '\b':
+ fputs("\\b", outfp);
+ break;
+ case '\f':
+ fputs("\\f", outfp);
+ break;
+ default:
+ if (str[i] < 040 || str[i] > 0176)
+ fprintf(outfp, "\\%03o", str[i] & 0377);
+ else
+ putc(str[i], outfp);
+ break;
+ }
+ putc(')', outfp);
+}
+
+void resource_manager::print_extensions_comment(FILE *outfp)
+{
+ if (extensions) {
+ fputs("%%Extensions:", outfp);
+ for (int i = 0; i < NEXTENSIONS; i++)
+ if (extensions & (1 << i)) {
+ putc(' ', outfp);
+ fputs(extension_table[i], outfp);
+ }
+ putc('\n', outfp);
+ }
+}
+
+void resource_manager::print_language_level_comment(FILE *outfp)
+{
+ if (language_level)
+ fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
+}
+
diff --git a/contrib/groff/src/devices/grotty/Makefile.sub b/contrib/groff/src/devices/grotty/Makefile.sub
new file mode 100644
index 000000000000..91d3908c00fe
--- /dev/null
+++ b/contrib/groff/src/devices/grotty/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=grotty
+MAN1=grotty.n
+XLIBS=$(LIBDRIVER) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=tty.o
+CCSRCS=$(srcdir)/tty.cc
diff --git a/contrib/groff/src/devices/grotty/TODO b/contrib/groff/src/devices/grotty/TODO
new file mode 100644
index 000000000000..3f23dc35d56b
--- /dev/null
+++ b/contrib/groff/src/devices/grotty/TODO
@@ -0,0 +1,3 @@
+Document font and device description file usage of grotty.
+
+With -h avoid using a tab when a single space will do.
diff --git a/contrib/groff/src/devices/grotty/tty.cc b/contrib/groff/src/devices/grotty/tty.cc
new file mode 100644
index 000000000000..a8ee06532075
--- /dev/null
+++ b/contrib/groff/src/devices/grotty/tty.cc
@@ -0,0 +1,509 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 "driver.h"
+#include "device.h"
+
+#ifndef SHRT_MIN
+#define SHRT_MIN (-32768)
+#endif
+
+#ifndef SHRT_MAX
+#define SHRT_MAX 32767
+#endif
+
+#define TAB_WIDTH 8
+
+static int horizontal_tab_flag = 0;
+static int form_feed_flag = 0;
+static int bold_flag = 1;
+static int underline_flag = 1;
+static int overstrike_flag = 1;
+static int draw_flag = 1;
+
+enum {
+ UNDERLINE_MODE = 0x01,
+ BOLD_MODE = 0x02,
+ VDRAW_MODE = 0x04,
+ HDRAW_MODE = 0x08,
+ CU_MODE = 0x10
+};
+
+// Mode to use for bold-underlining.
+static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
+
+class tty_font : public font {
+ tty_font(const char *);
+ unsigned char mode;
+public:
+ ~tty_font();
+ unsigned char get_mode() { return mode; }
+#if 0
+ void handle_x_command(int argc, const char **argv);
+#endif
+ static tty_font *load_tty_font(const char *);
+};
+
+tty_font *tty_font::load_tty_font(const char *s)
+{
+ tty_font *f = new tty_font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ const char *num = f->get_internal_name();
+ long n;
+ if (num != 0 && (n = strtol(num, 0, 0)) != 0)
+ f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE));
+ if (!underline_flag)
+ f->mode &= ~UNDERLINE_MODE;
+ if (!bold_flag)
+ f->mode &= ~BOLD_MODE;
+ if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
+ f->mode = (f->mode & ~(BOLD_MODE|UNDERLINE_MODE)) | bold_underline_mode;
+ return f;
+}
+
+tty_font::tty_font(const char *nm)
+: font(nm), mode(0)
+{
+}
+
+tty_font::~tty_font()
+{
+}
+
+#if 0
+void tty_font::handle_x_command(int argc, const char **argv)
+{
+ if (argc >= 1 && strcmp(argv[0], "bold") == 0)
+ mode |= BOLD_MODE;
+ else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
+ mode |= UNDERLINE_MODE;
+}
+#endif
+
+class glyph {
+ static glyph *free_list;
+public:
+ glyph *next;
+ short hpos;
+ unsigned int code;
+ unsigned char mode;
+ void *operator new(size_t);
+ void operator delete(void *);
+ inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
+ inline int order() { return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE); }
+};
+
+glyph *glyph::free_list = 0;
+
+void *glyph::operator new(size_t)
+{
+ if (!free_list) {
+ const int BLOCK = 1024;
+ free_list = (glyph *)new char[sizeof(glyph) * BLOCK];
+ for (int i = 0; i < BLOCK - 1; i++)
+ free_list[i].next = free_list + i + 1;
+ free_list[BLOCK - 1].next = 0;
+ }
+ glyph *p = free_list;
+ free_list = free_list->next;
+ p->next = 0;
+ return p;
+}
+
+void glyph::operator delete(void *p)
+{
+ if (p) {
+ ((glyph *)p)->next = free_list;
+ free_list = (glyph *)p;
+ }
+}
+
+class tty_printer : public printer {
+ int is_utf8;
+ glyph **lines;
+ int nlines;
+ int cached_v;
+ int cached_vpos;
+ void add_char(unsigned int, int, int, unsigned char);
+public:
+ tty_printer(const char *device);
+ ~tty_printer();
+ void set_char(int, font *, const environment *, int, const char *name);
+ void draw(int code, int *p, int np, const environment *env);
+ void special(char *arg, const environment *env, char type);
+ void put_char(unsigned int);
+ void begin_page(int) { }
+ void end_page(int page_length);
+ font *make_font(const char *);
+};
+
+tty_printer::tty_printer(const char *device) : cached_v(0)
+{
+ is_utf8 = !strcmp(device, "utf8");
+ nlines = 66;
+ lines = new glyph *[nlines];
+ for (int i = 0; i < nlines; i++)
+ lines[i] = 0;
+}
+
+tty_printer::~tty_printer()
+{
+ a_delete lines;
+}
+
+void tty_printer::set_char(int i, font *f, const environment *env,
+ int w, const char *name)
+{
+ if (w != font::hor)
+ fatal("width of character not equal to horizontal resolution");
+ add_char(f->get_code(i), env->hpos, env->vpos, ((tty_font *)f)->get_mode());
+}
+
+void tty_printer::add_char(unsigned int c, int h, int v, unsigned char mode)
+{
+#if 0
+ // This is too expensive.
+ if (h % font::hor != 0)
+ fatal("horizontal position not a multiple of horizontal resolution");
+#endif
+ int hpos = h / font::hor;
+ if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
+ error("character with ridiculous horizontal position discarded");
+ return;
+ }
+ int vpos;
+ if (v == cached_v && cached_v != 0)
+ vpos = cached_vpos;
+ else {
+ if (v % font::vert != 0)
+ fatal("vertical position not a multiple of vertical resolution");
+ vpos = v / font::vert;
+ if (vpos > nlines) {
+ glyph **old_lines = lines;
+ lines = new glyph *[vpos + 1];
+ memcpy(lines, old_lines, nlines * sizeof(glyph *));
+ for (int i = nlines; i <= vpos; i++)
+ lines[i] = 0;
+ a_delete old_lines;
+ nlines = vpos + 1;
+ }
+ // Note that the first output line corresponds to groff
+ // position font::vert.
+ if (vpos <= 0) {
+ error("character above first line discarded");
+ return;
+ }
+ cached_v = v;
+ cached_vpos = vpos;
+ }
+ glyph *g = new glyph;
+ g->hpos = hpos;
+ g->code = c;
+ g->mode = mode;
+
+ // The list will be reversed later. After reversal, it must be in
+ // increasing order of hpos, with CU specials before HDRAW characters
+ // before VDRAW characters before normal characters at each hpos, and
+ // otherwise in order of occurrence.
+
+ glyph **pp;
+ for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
+ if ((*pp)->hpos < hpos
+ || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
+ break;
+ g->next = *pp;
+ *pp = g;
+}
+
+void tty_printer::special(char *arg, const environment *env, char type)
+{
+ if (type == 'u')
+ add_char(*arg - '0', env->hpos, env->vpos, CU_MODE);
+}
+
+void tty_printer::draw(int code, int *p, int np, const environment *env)
+{
+ if (code != 'l' || !draw_flag)
+ return;
+ if (np != 2) {
+ error("2 arguments required for line");
+ return;
+ }
+ if (p[0] == 0) {
+ // vertical line
+ int v = env->vpos;
+ int len = p[1];
+ if (len < 0) {
+ v += len;
+ len = -len;
+ }
+ while (len >= 0) {
+ add_char('|', env->hpos, v, VDRAW_MODE);
+ len -= font::vert;
+ v += font::vert;
+ }
+ }
+ if (p[1] == 0) {
+ // horizontal line
+ int h = env->hpos;
+ int len = p[0];
+ if (len < 0) {
+ h += len;
+ len = -len;
+ }
+ while (len >= 0) {
+ add_char('-', h, env->vpos, HDRAW_MODE);
+ len -= font::hor;
+ h += font::hor;
+ }
+ }
+}
+
+void tty_printer::put_char(unsigned int wc)
+{
+ if (is_utf8 && wc >= 0x80) {
+ char buf[6 + 1];
+ int count;
+ char *p = buf;
+ if (wc < 0x800)
+ count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
+ else if (wc < 0x10000)
+ count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
+ else if (wc < 0x200000)
+ count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
+ else if (wc < 0x4000000)
+ count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
+ else if (wc <= 0x7fffffff)
+ count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
+ else
+ return;
+ do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
+ while (count > 0);
+ *++p = '\0';
+ fputs(buf, stdout);
+ }
+ else {
+ putchar(wc);
+ }
+}
+
+int cu_flag = 0;
+
+void tty_printer::end_page(int page_length)
+{
+ if (page_length % font::vert != 0)
+ error("vertical position at end of page not multiple of vertical resolution");
+ int lines_per_page = page_length / font::vert;
+ int last_line;
+ for (last_line = nlines; last_line > 0; last_line--)
+ if (lines[last_line - 1])
+ break;
+#if 0
+ if (last_line > lines_per_page) {
+ error("characters past last line discarded");
+ do {
+ --last_line;
+ while (lines[last_line]) {
+ glyph *tem = lines[last_line];
+ lines[last_line] = tem->next;
+ delete tem;
+ }
+ } while (last_line > lines_per_page);
+ }
+#endif
+ for (int i = 0; i < last_line; i++) {
+ glyph *p = lines[i];
+ lines[i] = 0;
+ glyph *g = 0;
+ while (p) {
+ glyph *tem = p->next;
+ p->next = g;
+ g = p;
+ p = tem;
+ }
+ int hpos = 0;
+ glyph *nextp;
+ for (p = g; p; delete p, p = nextp) {
+ nextp = p->next;
+ if (p->mode & CU_MODE) {
+ cu_flag = p->code;
+ continue;
+ }
+ if (nextp && p->hpos == nextp->hpos) {
+ if (p->draw_mode() == HDRAW_MODE &&
+ nextp->draw_mode() == VDRAW_MODE) {
+ nextp->code = '+';
+ continue;
+ }
+ if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
+ nextp->code = p->code;
+ continue;
+ }
+ if (!overstrike_flag)
+ continue;
+ }
+ if (hpos > p->hpos) {
+ do {
+ putchar('\b');
+ hpos--;
+ } while (hpos > p->hpos);
+ }
+ else {
+ if (horizontal_tab_flag) {
+ for (;;) {
+ int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
+ if (next_tab_pos > p->hpos)
+ break;
+ if (cu_flag) {
+ putchar('_');
+ putchar('\b');
+ }
+ putchar('\t');
+ hpos = next_tab_pos;
+ }
+ }
+ for (; hpos < p->hpos; hpos++) {
+ if (cu_flag) {
+ putchar('_');
+ putchar('\b');
+ }
+ putchar(' ');
+ }
+ }
+ assert(hpos == p->hpos);
+ if (p->mode & UNDERLINE_MODE) {
+ putchar('_');
+ putchar('\b');
+ }
+ if (p->mode & BOLD_MODE) {
+ put_char(p->code);
+ putchar('\b');
+ }
+ put_char(p->code);
+ hpos++;
+ }
+ putchar('\n');
+ }
+ if (form_feed_flag) {
+ if (last_line < lines_per_page)
+ putchar('\f');
+ }
+ else {
+ for (; last_line < lines_per_page; last_line++)
+ putchar('\n');
+ }
+}
+
+font *tty_printer::make_font(const char *nm)
+{
+ return tty_font::load_tty_font(nm);
+}
+
+printer *make_printer()
+{
+ return new tty_printer(device);
+}
+
+static void usage(FILE *stream);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, "F:vhfbuoBUd", long_options, NULL))
+ != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU grotty (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'b':
+ // Do not embolden by overstriking.
+ bold_flag = 0;
+ break;
+ case 'u':
+ // Do not underline.
+ underline_flag = 0;
+ break;
+ case 'o':
+ // Do not overstrike (other than emboldening and underlining).
+ overstrike_flag = 0;
+ break;
+ case 'B':
+ // Do bold-underlining as bold.
+ bold_underline_mode = BOLD_MODE;
+ break;
+ case 'U':
+ // Do bold-underlining as underlining.
+ bold_underline_mode = UNDERLINE_MODE;
+ break;
+ case 'h':
+ // Use horizontal tabs.
+ horizontal_tab_flag = 1;
+ break;
+ case 'f':
+ form_feed_flag = 1;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'd':
+ // Ignore \D commands.
+ draw_flag = 0;
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-hfvbuodBU] [-F dir] [files ...]\n",
+ program_name);
+}
diff --git a/contrib/groff/src/include/Makefile.sub b/contrib/groff/src/include/Makefile.sub
new file mode 100644
index 000000000000..cee00d31a314
--- /dev/null
+++ b/contrib/groff/src/include/Makefile.sub
@@ -0,0 +1,42 @@
+HDRS=\
+ assert.h \
+ cmap.h \
+ cset.h \
+ device.h \
+ driver.h \
+ errarg.h \
+ error.h \
+ font.h \
+ getopt.h \
+ groff-getopt.h \
+ htmlindicate.h \
+ index.h \
+ lib.h \
+ macropath.h \
+ nonposix.h \
+ posix.h \
+ printer.h \
+ ptable.h \
+ refid.h \
+ search.h \
+ searchpath.h \
+ stringclass.h
+GENHDRS=defs.h
+CLEANADD=$(GENHDRS)
+
+all depend: $(GENHDRS)
+
+defs.h: FORCE
+ @$(SHELL) $(top_srcdir)/gendef.sh defs.h \
+ "PROG_PREFIX=\"$(g)\"" \
+ "DEVICE=\"$(DEVICE)\"" \
+ "BINPATH=\"$(bindir)\"" \
+ "FONTPATH=\"$(fontpath)\"" \
+ "MACROPATH=\"$(tmacpath)\"" \
+ "INDEX_SUFFIX=\"$(indexext)\"" \
+ "COMMON_WORDS_FILE=\"$(common_words_file)\"" \
+ "DEFAULT_INDEX_DIR=\"$(indexdir)\"" \
+ "DEFAULT_INDEX_NAME=\"$(indexname)\"" \
+ "DEFAULT_INDEX=\"$(indexdir)/$(indexname)\""
+
+FORCE:
diff --git a/contrib/groff/src/include/assert.h b/contrib/groff/src/include/assert.h
new file mode 100644
index 000000000000..18d9c2623eda
--- /dev/null
+++ b/contrib/groff/src/include/assert.h
@@ -0,0 +1,39 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+#ifndef ASSERT_H
+#define ASSERT_H
+
+void assertion_failed(int, const char *);
+
+inline void do_assert(int expr, int line, const char *file)
+{
+ if (!expr)
+ assertion_failed(line, file);
+}
+#endif /* ASSERT_H */
+
+#undef assert
+
+#ifdef NDEBUG
+#define assert(ignore) /* as nothing */
+#else
+#define assert(expr) do_assert(expr, __LINE__, __FILE__)
+#endif
diff --git a/contrib/groff/src/include/cmap.h b/contrib/groff/src/include/cmap.h
new file mode 100644
index 000000000000..1537d4627d7b
--- /dev/null
+++ b/contrib/groff/src/include/cmap.h
@@ -0,0 +1,56 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+
+enum cmap_builtin { CMAP_BUILTIN };
+
+class cmap {
+public:
+ cmap();
+ cmap(cmap_builtin);
+ int operator()(unsigned char) const;
+ unsigned char &operator[](unsigned char);
+
+ friend class cmap_init;
+private:
+ unsigned char v[UCHAR_MAX+1];
+};
+
+inline int cmap::operator()(unsigned char c) const
+{
+ return v[c];
+}
+
+inline unsigned char &cmap::operator[](unsigned char c)
+{
+ return v[c];
+}
+
+extern cmap cmlower;
+extern cmap cmupper;
+
+static class cmap_init {
+ static int initialised;
+public:
+ cmap_init();
+} _cmap_init;
diff --git a/contrib/groff/src/include/cset.h b/contrib/groff/src/include/cset.h
new file mode 100644
index 000000000000..b3a1a976139e
--- /dev/null
+++ b/contrib/groff/src/include/cset.h
@@ -0,0 +1,75 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 HAVE_CC_LIMITS_H
+#include <limits.h>
+#else /* not HAVE_CC_LIMITS_H */
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+#endif /* not HAVE_CC_LIMITS_H */
+
+enum cset_builtin { CSET_BUILTIN };
+
+class cset {
+public:
+ cset();
+ cset(cset_builtin);
+ cset(const char *);
+ cset(const unsigned char *);
+ int operator()(unsigned char) const;
+
+ cset &operator|=(const cset &);
+ cset &operator|=(unsigned char);
+
+ friend class cset_init;
+private:
+ char v[UCHAR_MAX+1];
+ void clear();
+};
+
+inline int cset::operator()(unsigned char c) const
+{
+ return v[c];
+}
+
+inline cset &cset::operator|=(unsigned char c)
+{
+ v[c] = 1;
+ return *this;
+}
+
+extern cset csalpha;
+extern cset csupper;
+extern cset cslower;
+extern cset csdigit;
+extern cset csxdigit;
+extern cset csspace;
+extern cset cspunct;
+extern cset csalnum;
+extern cset csprint;
+extern cset csgraph;
+extern cset cscntrl;
+
+static class cset_init {
+ static int initialised;
+public:
+ cset_init();
+} _cset_init;
diff --git a/contrib/groff/src/include/device.h b/contrib/groff/src/include/device.h
new file mode 100644
index 000000000000..341af8d2a70f
--- /dev/null
+++ b/contrib/groff/src/include/device.h
@@ -0,0 +1,21 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+extern const char *device;
diff --git a/contrib/groff/src/include/driver.h b/contrib/groff/src/include/driver.h
new file mode 100644
index 000000000000..97eb89125d02
--- /dev/null
+++ b/contrib/groff/src/include/driver.h
@@ -0,0 +1,35 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <math.h>
+#include "errarg.h"
+#include "error.h"
+#include "font.h"
+#include "printer.h"
+#include "lib.h"
+
+void do_file(const char *);
+extern printer *pr;
diff --git a/contrib/groff/src/include/errarg.h b/contrib/groff/src/include/errarg.h
new file mode 100644
index 000000000000..0c7957c85ce0
--- /dev/null
+++ b/contrib/groff/src/include/errarg.h
@@ -0,0 +1,46 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+class errarg {
+ enum { EMPTY, STRING, CHAR, INTEGER, DOUBLE } type;
+ union {
+ const char *s;
+ int n;
+ char c;
+ double d;
+ };
+ public:
+ errarg();
+ errarg(const char *);
+ errarg(char);
+ errarg(unsigned char);
+ errarg(int);
+ errarg(double);
+ int empty() const;
+ void print() const;
+};
+
+extern errarg empty_errarg;
+
+extern void errprint(const char *,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
diff --git a/contrib/groff/src/include/error.h b/contrib/groff/src/include/error.h
new file mode 100644
index 000000000000..d26e2c70facd
--- /dev/null
+++ b/contrib/groff/src/include/error.h
@@ -0,0 +1,58 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+extern void fatal_with_file_and_line(const char *filename, int lineno,
+ const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+extern void error_with_file_and_line(const char *filename, int lineno,
+ const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+extern void warning_with_file_and_line(const char *filename, int lineno,
+ const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+extern void fatal(const char *,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+extern void error(const char *,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+extern void warning(const char *,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+
+extern const char *program_name;
+extern int current_lineno;
+extern const char *current_filename;
+
diff --git a/contrib/groff/src/include/font.h b/contrib/groff/src/include/font.h
new file mode 100644
index 000000000000..099f97bea180
--- /dev/null
+++ b/contrib/groff/src/include/font.h
@@ -0,0 +1,116 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+typedef void (*FONT_COMMAND_HANDLER)(const char *, const char *,
+ const char *, int);
+
+struct font_kern_list;
+struct font_char_metric;
+struct font_widths_cache;
+
+class font {
+public:
+ enum {
+ LIG_ff = 1,
+ LIG_fi = 2,
+ LIG_fl = 4,
+ LIG_ffi = 8,
+ LIG_ffl = 16
+ };
+
+ virtual ~font();
+ int contains(int index);
+ int is_special();
+ int get_width(int index, int point_size);
+ int get_height(int index, int point_size);
+ int get_depth(int index, int point_size);
+ int get_space_width(int point_size);
+ int get_character_type(int index);
+ int get_kern(int index1, int index2, int point_size);
+ int get_skew(int index, int point_size, int slant);
+ int has_ligature(int);
+ int get_italic_correction(int index, int point_size);
+ int get_left_italic_correction(int index, int point_size);
+ int get_subscript_correction(int index, int point_size);
+ int get_code(int i);
+ const char *get_special_device_encoding(int index);
+ const char *get_name();
+ const char *get_internal_name();
+
+ static font *load_font(const char *, int *not_found = 0);
+ static void command_line_font_dir(const char *path);
+ static FILE *open_file(const char *name, char **pathp);
+ static int load_desc();
+ static int name_to_index(const char *);
+ static int number_to_index(int);
+ static FONT_COMMAND_HANDLER
+ set_unknown_desc_command_handler(FONT_COMMAND_HANDLER);
+
+ static int res;
+ static int hor;
+ static int vert;
+ static int unitwidth;
+ static int paperwidth;
+ static int paperlength;
+ static int biggestfont;
+ static int spare2;
+ static int sizescale;
+ static int tcommand;
+ static int pass_filenames;
+ static int use_charnames_in_special;
+
+ static const char **font_name_table;
+ static const char **style_table;
+ static const char *family;
+ static int *sizes;
+private:
+ unsigned ligatures;
+ font_kern_list **kern_hash_table;
+ int space_width;
+ short *ch_index;
+ int nindices;
+ font_char_metric *ch;
+ int ch_used;
+ int ch_size;
+ int special;
+ char *name;
+ char *internalname;
+ double slant;
+ font_widths_cache *widths_cache;
+ static FONT_COMMAND_HANDLER unknown_desc_command_handler;
+
+ enum { KERN_HASH_TABLE_SIZE = 503 };
+
+ void add_entry(int index, const font_char_metric &);
+ void copy_entry(int new_index, int old_index);
+ void add_kern(int index1, int index2, int amount);
+ static int hash_kern(int i1, int i2);
+ void alloc_ch_index(int);
+ void extend_ch();
+ void compact();
+
+ static int scale(int w, int pointsize);
+ virtual void handle_unknown_font_command(const char *command,
+ const char *arg,
+ const char *file, int lineno);
+protected:
+ font(const char *);
+ int load(int *not_found = 0);
+};
diff --git a/contrib/groff/src/include/getopt.h b/contrib/groff/src/include/getopt.h
new file mode 100644
index 000000000000..b0147e9d2ac3
--- /dev/null
+++ b/contrib/groff/src/include/getopt.h
@@ -0,0 +1,169 @@
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+# if defined __STDC__ && __STDC__
+ const char *name;
+# else
+ char *name;
+# endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+#endif /* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+#if defined __STDC__ && __STDC__
+# ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int __argc, char *const *__argv, const char *__shortopts);
+# else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+# endif /* __GNU_LIBRARY__ */
+
+# ifndef __need_getopt
+extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int __argc, char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int __argc, char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only);
+# endif
+#else /* not __STDC__ */
+extern int getopt ();
+# ifndef __need_getopt
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+# endif
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations. */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/contrib/groff/src/include/groff-getopt.h b/contrib/groff/src/include/groff-getopt.h
new file mode 100644
index 000000000000..1807fc7ccfb9
--- /dev/null
+++ b/contrib/groff/src/include/groff-getopt.h
@@ -0,0 +1,68 @@
+// -*- C++ -*-
+/* Copyright (C) 2000 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 file has to be included from within lib.h instead of getopt.h
+ to avoid problems with picky C++ compilers.
+*/
+
+#ifndef _GROFF_GETOPT_H
+#define _GROFF_GETOPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+struct option
+{
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+extern int getopt(int __argc,
+ char *const *__argv,
+ const char *__shortopts);
+extern int getopt_long(int __argc,
+ char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind);
+extern int getopt_long_only(int __argc,
+ char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GROFF_GETOPT_H */
diff --git a/contrib/groff/src/include/html-strings.h b/contrib/groff/src/include/html-strings.h
new file mode 100644
index 000000000000..710e8d7e816c
--- /dev/null
+++ b/contrib/groff/src/include/html-strings.h
@@ -0,0 +1,31 @@
+// -*- C++ -*-
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk).
+
+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. */
+
+/*
+ * defines the image tags issued by the pre-processors (tbl, pic, eqn)
+ * and later detected by pre-html.cc
+ */
+
+#define HTML_IMAGE_INLINE_BEGIN "\\O[HTML-IMAGE-INLINE-BEGIN]"
+#define HTML_IMAGE_INLINE_END "\\O[HTML-IMAGE-INLINE-END]"
+#define HTML_IMAGE_CENTERED ".HTML-IMAGE"
+#define HTML_IMAGE_RIGHT ".HTML-IMAGE-RIGHT"
+#define HTML_IMAGE_LEFT ".HTML-IMAGE-LEFT"
+#define HTML_IMAGE_END ".HTML-IMAGE-END"
diff --git a/contrib/groff/src/include/htmlindicate.h b/contrib/groff/src/include/htmlindicate.h
new file mode 100644
index 000000000000..4915ba8a2b15
--- /dev/null
+++ b/contrib/groff/src/include/htmlindicate.h
@@ -0,0 +1,59 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley <gaius@glam.ac.uk>
+
+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. */
+
+#ifndef HTMLINDICATE_H
+#define HTMLINDICATE_H
+
+/*
+ * graphic_start - emit a html graphic start indicator, but only
+ * if one has not already been issued.
+ *
+ * The boolean, is_inline, should be:
+ *
+ * FALSE if this is called via EQ, TS, PS, and
+ * TRUE if issued via delim $$ $ x over y $ etc.
+ */
+extern void graphic_start(int is_inline);
+
+/*
+ * graphic_end - emit a html graphic end indicator, but only
+ * if a corresponding matching graphic-start has
+ * been issued.
+ *
+ */
+extern void graphic_end();
+
+/*
+ * html_begin_suppress - suppresses output for the html device
+ * and resets the min/max registers for -Tps
+ *
+ * The boolean, is_inline, should be:
+ *
+ * FALSE if this is called via EQ, TS, PS, and
+ * TRUE if issued via delim $$ $ x over y $ etc.
+ */
+extern void html_begin_suppress(int is_inline);
+
+/*
+ * html_end_suppress - end the suppression of output.
+ */
+extern void html_end_suppress(int is_inline);
+
+#endif
diff --git a/contrib/groff/src/include/index.h b/contrib/groff/src/include/index.h
new file mode 100644
index 000000000000..7e6081381c2e
--- /dev/null
+++ b/contrib/groff/src/include/index.h
@@ -0,0 +1,42 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+#define INDEX_MAGIC 0x23021964
+#define INDEX_VERSION 1
+
+struct index_header {
+ int magic;
+ int version;
+ int tags_size;
+ int table_size;
+ int lists_size;
+ int strings_size;
+ int truncate;
+ int shortest;
+ int common;
+};
+
+struct tag {
+ int filename_index;
+ int start;
+ int length;
+};
+
+unsigned hash(const char *s, int len);
diff --git a/contrib/groff/src/include/lib.h b/contrib/groff/src/include/lib.h
new file mode 100644
index 000000000000..ad416e0a9883
--- /dev/null
+++ b/contrib/groff/src/include/lib.h
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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. */
+
+extern "C" {
+#ifndef strerror
+ char *strerror(int);
+#endif
+ const char *i_to_a(int);
+ const char *if_to_a(int, int);
+}
+
+/* stdio.h on IRIX and OSF/1 include getopt.h */
+
+#if !(defined(__sgi) || (defined(__osf__) && defined(__alpha)))
+#include <groff-getopt.h>
+#endif
+
+char *strsave(const char *s);
+int is_prime(unsigned);
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+FILE *xtmpfile(char **namep=0, char *postfix=0, int do_unlink=1);
+char *xtmptemplate(char *extension=0);
+
+#ifdef NEED_DECLARATION_POPEN
+
+extern "C" { FILE *popen(const char *, const char *); }
+
+#endif /* NEED_DECLARATION_POPEN */
+
+#ifdef NEED_DECLARATION_PCLOSE
+
+extern "C" { int pclose (FILE *); }
+
+#endif /* NEED_DECLARATION_PCLOSE */
+
+int interpret_lf_args(const char *p);
+
+extern char illegal_char_table[];
+
+inline int illegal_input_char(int c)
+{
+ return c >= 0 && illegal_char_table[c];
+}
+
+#if !defined(_AIX) && !defined(sinix) && !defined(__sinix__)
+#ifdef HAVE_STRNCASECMP
+#ifdef NEED_DECLARATION_STRNCASECMP
+extern "C" {
+ // SunOS's string.h fails to declare this.
+ int strncasecmp(const char *, const char *, int);
+}
+#endif /* NEED_DECLARATION_STRNCASECMP */
+#endif /* HAVE_STRNCASECMP */
+#endif /* !_AIX && !sinix && !__sinix__ */
+
+#ifndef HAVE_STRCASECMP
+#define strcasecmp(a,b) strcmp((a),(b))
+#endif
+
+#ifndef HAVE_STRNCASECMP
+#define strncasecmp(a,b,c) strncmp((a),(b),(c))
+#endif
+
+#ifdef HAVE_CC_LIMITS_H
+#include <limits.h>
+#else /* not HAVE_CC_LIMITS_H */
+#define INT_MAX 2147483647
+#endif /* not HAVE_CC_LIMITS_H */
+
+/* It's not safe to rely on people getting INT_MIN right (ie signed). */
+
+#ifdef INT_MIN
+#undef INT_MIN
+#endif
+
+#ifdef CFRONT_ANSI_BUG
+
+/* This works around a bug in cfront 2.0 used with ANSI C compilers. */
+
+#define INT_MIN ((long)(-INT_MAX-1))
+
+#else /* not CFRONT_ANSI_BUG */
+
+#define INT_MIN (-INT_MAX-1)
+
+#endif /* not CFRONT_ANSI_BUG */
+
+/* Maximum number of digits in the decimal representation of an int
+(not including the -). */
+
+#define INT_DIGITS 10
+
+#ifdef PI
+#undef PI
+#endif
+
+const double PI = 3.14159265358979323846;
+
+/* ad_delete deletes an array of objects with destructors;
+a_delete deletes an array of objects without destructors */
+
+#ifdef ARRAY_DELETE_NEEDS_SIZE
+/* for 2.0 systems */
+#define ad_delete(size) delete [size]
+#define a_delete delete
+#else /* not ARRAY_DELETE_NEEDS_SIZE */
+/* for ARM systems */
+#define ad_delete(size) delete []
+#define a_delete delete []
+#endif /* not ARRAY_DELETE_NEEDS_SIZE */
diff --git a/contrib/groff/src/include/macropath.h b/contrib/groff/src/include/macropath.h
new file mode 100644
index 000000000000..b4a2bd0cadaa
--- /dev/null
+++ b/contrib/groff/src/include/macropath.h
@@ -0,0 +1,23 @@
+// -*- 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. */
+
+extern search_path macro_path;
+extern search_path safer_macro_path;
+extern search_path config_macro_path;
diff --git a/contrib/groff/src/include/nonposix.h b/contrib/groff/src/include/nonposix.h
new file mode 100644
index 000000000000..51449832ae35
--- /dev/null
+++ b/contrib/groff/src/include/nonposix.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ Written by Eli Zaretskii (eliz@is.elta.co.il)
+
+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 header file compartmentalize all idiosyncrasies of non-Posix
+ systems, such as MS-DOS, MS-Windows, etc. */
+
+#if defined _MSC_VER
+# ifndef _WIN32
+# define _WIN32
+# endif
+# define setmode(f,m) _setmode(f,m)
+#endif
+
+#if defined(__MSDOS__) || (defined(_WIN32) && !defined(__CYGWIN32__))
+
+/* Binary I/O nuisances. Note: "setmode" is right for DJGPP and
+ Borland; Windows compilers might need _setmode or some such. */
+# include <fcntl.h>
+# include <io.h>
+# ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# endif
+# define SET_BINARY(f) do {if (!isatty(f)) setmode(f,O_BINARY);} while(0)
+# define FOPEN_RB "rb"
+# define FOPEN_WB "wb"
+# define FOPEN_RWB "wb+"
+# ifdef _MSC_VER
+# define POPEN_RT "rt"
+# define POPEN_WT "wt"
+# define popen(c,m) _popen(c,m)
+# define pclose(p) _pclose(p)
+# define getpid() (1)
+# endif
+# ifndef O_BINARY
+# ifdef _O_BINARY
+# define O_BINARY (_O_BINARY)
+# endif
+# endif
+
+/* The system shell. Groff assumes a Unixy shell, but non-Posix
+ systems don't have standard places where it lives, and might not
+ have it installed to begin with. We want to give them some leeway. */
+# define BSHELL (system_shell_name())
+# define BSHELL_DASH_C (system_shell_dash_c())
+# define IS_BSHELL(s) (is_system_shell(s))
+
+/* The separator for directories in PATH and other environment
+ variables. */
+# define PATH_SEP ";"
+
+/* Characters that separate directories in a path name. */
+# define DIR_SEPS "/\\:"
+
+/* How to tell if the argument is an absolute file name. */
+# define IS_ABSOLUTE(f) \
+ ((f)[0] == '/' || (f)[0] == '\\' || (f)[0] && (f)[1] == ':')
+
+/* The executable extension. */
+# define EXE_EXT ".exe"
+
+/* The system null device. */
+# define NULL_DEV "NUL"
+
+/* Prototypes. */
+# ifdef __cplusplus
+ extern "C" {
+# endif
+ const char * system_shell_name(void);
+ const char * system_shell_dash_c(void);
+ int is_system_shell(const char *);
+# ifdef __cplusplus
+ }
+# endif
+
+#endif
+
+/* Defaults, for Posix systems. */
+
+#ifndef FOPEN_RB
+# define FOPEN_RB "r"
+#endif
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef FOPEN_RWB
+# define FOPEN_RWB "w+"
+#endif
+#ifndef POPEN_RT
+# define POPEN_RT "r"
+#endif
+#ifndef POPEN_WT
+# define POPEN_WT "w"
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+#ifndef BSHELL
+# define BSHELL "/bin/sh"
+#endif
+#ifndef BSHELL_DASH_C
+# define BSHELL_DASH_C "-c"
+#endif
+#ifndef IS_BSHELL
+# define IS_BSHELL(s) ((s) && strcmp(s,BSHELL) == 0)
+#endif
+#ifndef PATH_SEP
+# define PATH_SEP ":"
+#endif
+#ifndef DIR_SEPS
+# define DIR_SEPS "/"
+#endif
+#ifndef IS_ABSOLUTE
+# define IS_ABSOLUTE(f) ((f)[0] == '/')
+#endif
+#ifndef EXE_EXT
+# define EXE_EXT ""
+#endif
+#ifndef NULL_DEV
+# define NULL_DEV "/dev/null"
+#endif
diff --git a/contrib/groff/src/include/posix.h b/contrib/groff/src/include/posix.h
new file mode 100644
index 000000000000..1b7d5cdc3424
--- /dev/null
+++ b/contrib/groff/src/include/posix.h
@@ -0,0 +1,51 @@
+// -*- C++ -*-
+/* Copyright (C) 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. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_CC_OSFCN_H
+#include <osfcn.h>
+#else
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#endif
+
+#ifndef S_IRGRP
+#define S_IRGRP 0040
+#endif
+
+#ifndef S_IROTH
+#define S_IROTH 0004
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
diff --git a/contrib/groff/src/include/printer.h b/contrib/groff/src/include/printer.h
new file mode 100644
index 000000000000..beae4d9ab915
--- /dev/null
+++ b/contrib/groff/src/include/printer.h
@@ -0,0 +1,77 @@
+// -*- 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. */
+
+struct environment {
+ int fontno;
+ int size;
+ int hpos;
+ int vpos;
+ int height;
+ int slant;
+};
+
+struct font;
+
+struct font_pointer_list {
+ font *p;
+ font_pointer_list *next;
+
+ font_pointer_list(font *, font_pointer_list *);
+};
+
+class printer {
+public:
+ printer();
+ virtual ~printer();
+ void load_font(int i, const char *name);
+ void set_ascii_char(unsigned char c, const environment *env,
+ int *widthp = 0);
+ void set_special_char(const char *nm, const environment *env,
+ int *widthp = 0);
+ void set_numbered_char(int n, const environment *env, int *widthp = 0);
+ int set_char_and_width(const char *nm, const environment *env,
+ int *widthp, font **f);
+ font *get_font_from_index(int fontno);
+ virtual void draw(int code, int *p, int np, const environment *env);
+ virtual void begin_page(int) = 0;
+ virtual void end_page(int page_length) = 0;
+ virtual font *make_font(const char *nm);
+ virtual void end_of_line();
+ virtual void special(char *arg, const environment *env, char type = 'p');
+ static int adjust_arc_center(const int *, double *);
+protected:
+ font_pointer_list *font_list;
+
+ // information about named characters
+ int is_char_named;
+ int is_named_set;
+ char named_command;
+ const char *named_char_s;
+ int named_char_n;
+
+private:
+ font **font_table;
+ int nfonts;
+ font *find_font(const char *);
+ virtual void set_char(int index, font *f, const environment *env,
+ int w, const char *name) = 0;
+};
+
+printer *make_printer();
diff --git a/contrib/groff/src/include/ptable.h b/contrib/groff/src/include/ptable.h
new file mode 100644
index 000000000000..dc56add0f6c7
--- /dev/null
+++ b/contrib/groff/src/include/ptable.h
@@ -0,0 +1,168 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <assert.h>
+#include <string.h>
+
+#ifdef TRADITIONAL_CPP
+#define name2(a,b) a/**/b
+#else /* not TRADITIONAL_CPP */
+#define name2(a,b) name2x(a,b)
+#define name2x(a,b) a ## b
+#endif /* not TRADITIONAL_CPP */
+
+#define PTABLE(T) name2(T,_ptable)
+#define PASSOC(T) name2(T,_passoc)
+#define PTABLE_ITERATOR(T) name2(T,_ptable_iterator)
+
+extern unsigned next_ptable_size(unsigned);
+extern unsigned long hash_string(const char *);
+
+#define declare_ptable(T) \
+ \
+struct PASSOC(T) { \
+ char *key; \
+ T *val; \
+ PASSOC(T)(); \
+}; \
+ \
+struct PTABLE(T); \
+ \
+class PTABLE_ITERATOR(T) { \
+ PTABLE(T) *p; \
+ unsigned i; \
+public: \
+ PTABLE_ITERATOR(T)(PTABLE(T) *); \
+ int next(const char **, T **); \
+}; \
+ \
+class PTABLE(T) { \
+ PASSOC(T) *v; \
+ unsigned size; \
+ unsigned used; \
+ enum { FULL_NUM = 2, FULL_DEN = 3, INITIAL_SIZE = 17 }; \
+public: \
+ PTABLE(T)(); \
+ ~PTABLE(T)(); \
+ void define(const char *, T *); \
+ T *lookup(const char *); \
+ friend class PTABLE_ITERATOR(T); \
+};
+
+
+#define implement_ptable(T) \
+ \
+PASSOC(T)::PASSOC(T)() \
+: key(0), val(0) \
+{ \
+} \
+ \
+PTABLE(T)::PTABLE(T)() \
+{ \
+ v = new PASSOC(T)[size = INITIAL_SIZE]; \
+ used = 0; \
+} \
+ \
+PTABLE(T)::~PTABLE(T)() \
+{ \
+ for (unsigned i = 0; i < size; i++) { \
+ a_delete v[i].key; \
+ delete v[i].val; \
+ } \
+ a_delete v; \
+} \
+ \
+void PTABLE(T)::define(const char *key, T *val) \
+{ \
+ assert(key != 0); \
+ unsigned long h = hash_string(key); \
+ unsigned n; \
+ for (n = unsigned(h % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ if (strcmp(v[n].key, key) == 0) { \
+ delete v[n].val; \
+ v[n].val = val; \
+ return; \
+ } \
+ if (val == 0) \
+ return; \
+ if (used*FULL_DEN >= size*FULL_NUM) { \
+ PASSOC(T) *oldv = v; \
+ unsigned old_size = size; \
+ size = next_ptable_size(size); \
+ v = new PASSOC(T)[size]; \
+ for (unsigned i = 0; i < old_size; i++) \
+ if (oldv[i].key != 0) { \
+ if (oldv[i].val == 0) \
+ a_delete oldv[i].key; \
+ else { \
+ unsigned j; \
+ for (j = unsigned(hash_string(oldv[i].key) % size); \
+ v[j].key != 0; \
+ j = (j == 0 ? size - 1 : j - 1)) \
+ ; \
+ v[j].key = oldv[i].key; \
+ v[j].val = oldv[i].val; \
+ } \
+ } \
+ for (n = unsigned(h % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ ; \
+ a_delete oldv; \
+ } \
+ char *temp = new char[strlen(key)+1]; \
+ strcpy(temp, key); \
+ v[n].key = temp; \
+ v[n].val = val; \
+ used++; \
+} \
+ \
+T *PTABLE(T)::lookup(const char *key) \
+{ \
+ assert(key != 0); \
+ for (unsigned n = unsigned(hash_string(key) % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ if (strcmp(v[n].key, key) == 0) \
+ return v[n].val; \
+ return 0; \
+} \
+ \
+PTABLE_ITERATOR(T)::PTABLE_ITERATOR(T)(PTABLE(T) *t) \
+: p(t), i(0) \
+{ \
+} \
+ \
+int PTABLE_ITERATOR(T)::next(const char **keyp, T **valp) \
+{ \
+ unsigned size = p->size; \
+ PASSOC(T) *v = p->v; \
+ for (; i < size; i++) \
+ if (v[i].key != 0) { \
+ *keyp = v[i].key; \
+ *valp = v[i].val; \
+ i++; \
+ return 1; \
+ } \
+ return 0; \
+}
+
diff --git a/contrib/groff/src/include/refid.h b/contrib/groff/src/include/refid.h
new file mode 100644
index 000000000000..605427e9af9b
--- /dev/null
+++ b/contrib/groff/src/include/refid.h
@@ -0,0 +1,35 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+class reference_id {
+ int filename_id;
+ int pos;
+public:
+ reference_id() : filename_id(-1) { }
+ reference_id(int fid, int off) : filename_id(fid), pos(off) { }
+ unsigned hash() const { return (filename_id << 4) + pos; }
+ int is_null() const { return filename_id < 0; }
+ friend inline int operator==(const reference_id &, const reference_id &);
+};
+
+inline int operator==(const reference_id &r1, const reference_id &r2)
+{
+ return r1.filename_id == r2.filename_id && r1.pos == r2.pos;
+}
diff --git a/contrib/groff/src/include/search.h b/contrib/groff/src/include/search.h
new file mode 100644
index 000000000000..260410e2372b
--- /dev/null
+++ b/contrib/groff/src/include/search.h
@@ -0,0 +1,96 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct search_item;
+struct search_item_iterator;
+
+class search_list {
+public:
+ search_list();
+ ~search_list();
+ void add_file(const char *fn, int silent = 0);
+ int nfiles() const;
+private:
+ search_item *list;
+ int niterators;
+ int next_fid;
+ friend class search_list_iterator;
+};
+
+struct bmpattern;
+
+class linear_searcher {
+ const char *ignore_fields;
+ int truncate_len;
+ bmpattern **keys;
+ int nkeys;
+ const char *search_and_check(const bmpattern *key, const char *buf,
+ const char *bufend, const char **start = 0)
+ const;
+ int check_match(const char *buf, const char *bufend, const char *match,
+ int matchlen, const char **cont, const char **start)
+ const;
+public:
+ linear_searcher(const char *query, int query_len,
+ const char *ign, int trunc);
+ ~linear_searcher();
+ int search(const char *buf, const char *bufend,
+ const char **startp, int *lengthp) const;
+};
+
+class search_list_iterator {
+ search_list *list;
+ search_item *ptr;
+ search_item_iterator *iter;
+ char *query;
+ linear_searcher searcher;
+public:
+ search_list_iterator(search_list *, const char *query);
+ ~search_list_iterator();
+ int next(const char **, int *, reference_id * = 0);
+};
+
+class search_item {
+protected:
+ char *name;
+ int filename_id;
+public:
+ search_item *next;
+ search_item(const char *nm, int fid);
+ virtual search_item_iterator *make_search_item_iterator(const char *) = 0;
+ virtual ~search_item();
+ int is_named(const char *) const;
+ virtual int next_filename_id() const;
+};
+
+class search_item_iterator {
+ char shut_g_plus_plus_up;
+public:
+ virtual ~search_item_iterator();
+ virtual int next(const linear_searcher &, const char **ptr, int *lenp,
+ reference_id *) = 0;
+};
+
+search_item *make_index_search_item(const char *filename, int fid);
+search_item *make_linear_search_item(int fd, const char *filename, int fid);
+
+extern int linear_truncate_len;
+extern const char *linear_ignore_fields;
+extern int verify_flag;
diff --git a/contrib/groff/src/include/searchpath.h b/contrib/groff/src/include/searchpath.h
new file mode 100644
index 000000000000..4d89a8d04f1d
--- /dev/null
+++ b/contrib/groff/src/include/searchpath.h
@@ -0,0 +1,30 @@
+// -*- 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. */
+
+class search_path {
+ char *dirs;
+ unsigned init_len;
+public:
+ search_path(const char *envvar, const char *standard,
+ int add_home, int add_current);
+ ~search_path();
+ void command_line_dir(const char *);
+ FILE *open_file(const char *, char **);
+};
diff --git a/contrib/groff/src/include/stringclass.h b/contrib/groff/src/include/stringclass.h
new file mode 100644
index 000000000000..be3a044fb0b0
--- /dev/null
+++ b/contrib/groff/src/include/stringclass.h
@@ -0,0 +1,195 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+// Ensure that the first declaration of functions that are later
+// declared as inline declares them as inline.
+
+class string;
+
+inline string operator+(const string &, const string &);
+inline string operator+(const string &, const char *);
+inline string operator+(const char *, const string &);
+inline string operator+(const string &, char);
+inline string operator+(char, const string &);
+inline int operator==(const string &, const string &);
+inline int operator!=(const string &, const string &);
+
+class string {
+public:
+ string();
+ string(const string &);
+ string(const char *);
+ string(const char *, int);
+ string(char);
+
+ ~string();
+
+ string &operator=(const string &);
+ string &operator=(const char *);
+ string &operator=(char);
+
+ string &operator+=(const string &);
+ string &operator+=(const char *);
+ string &operator+=(char);
+ void append(const char *, int);
+
+ int length() const;
+ int empty() const;
+ int operator*() const;
+
+ string substring(int i, int n) const;
+
+ char &operator[](int);
+ char operator[](int) const;
+
+ void set_length(int i);
+ const char *contents() const;
+ int search(char) const;
+ char *extract() const;
+ void clear();
+ void move(string &);
+
+ friend string operator+(const string &, const string &);
+ friend string operator+(const string &, const char *);
+ friend string operator+(const char *, const string &);
+ friend string operator+(const string &, char);
+ friend string operator+(char, const string &);
+
+ friend int operator==(const string &, const string &);
+ friend int operator!=(const string &, const string &);
+ friend int operator<=(const string &, const string &);
+ friend int operator<(const string &, const string &);
+ friend int operator>=(const string &, const string &);
+ friend int operator>(const string &, const string &);
+
+private:
+ char *ptr;
+ int len;
+ int sz;
+
+ string(const char *, int, const char *, int); // for use by operator+
+ void grow1();
+};
+
+
+inline char &string::operator[](int i)
+{
+ assert(i >= 0 && i < len);
+ return ptr[i];
+}
+
+inline char string::operator[](int i) const
+{
+ assert(i >= 0 && i < len);
+ return ptr[i];
+}
+
+inline int string::length() const
+{
+ return len;
+}
+
+inline int string::empty() const
+{
+ return len == 0;
+}
+
+inline int string::operator*() const
+{
+ return len;
+}
+
+inline const char *string::contents() const
+{
+ return ptr;
+}
+
+inline string operator+(const string &s1, const string &s2)
+{
+ return string(s1.ptr, s1.len, s2.ptr, s2.len);
+}
+
+inline string operator+(const string &s1, const char *s2)
+{
+#ifdef __GNUG__
+ if (s2 == 0)
+ return s1;
+ else
+ return string(s1.ptr, s1.len, s2, strlen(s2));
+#else
+ return s2 == 0 ? s1 : string(s1.ptr, s1.len, s2, strlen(s2));
+#endif
+}
+
+inline string operator+(const char *s1, const string &s2)
+{
+#ifdef __GNUG__
+ if (s1 == 0)
+ return s2;
+ else
+ return string(s1, strlen(s1), s2.ptr, s2.len);
+#else
+ return s1 == 0 ? s2 : string(s1, strlen(s1), s2.ptr, s2.len);
+#endif
+}
+
+inline string operator+(const string &s, char c)
+{
+ return string(s.ptr, s.len, &c, 1);
+}
+
+inline string operator+(char c, const string &s)
+{
+ return string(&c, 1, s.ptr, s.len);
+}
+
+inline int operator==(const string &s1, const string &s2)
+{
+ return (s1.len == s2.len
+ && (s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) == 0));
+}
+
+inline int operator!=(const string &s1, const string &s2)
+{
+ return (s1.len != s2.len
+ || (s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) != 0));
+}
+
+inline string string::substring(int i, int n) const
+{
+ assert(i >= 0 && i + n <= len);
+ return string(ptr + i, n);
+}
+
+inline string &string::operator+=(char c)
+{
+ if (len >= sz)
+ grow1();
+ ptr[len++] = c;
+ return *this;
+}
+
+void put_string(const string &, FILE *);
+
+string as_string(int);
diff --git a/contrib/groff/src/libs/libbib/Makefile.sub b/contrib/groff/src/libs/libbib/Makefile.sub
new file mode 100644
index 000000000000..482f01abcf66
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/Makefile.sub
@@ -0,0 +1,14 @@
+LIB=bib
+OBJS=\
+ common.o \
+ index.o \
+ linear.o \
+ search.o \
+ map.o
+CCSRCS=\
+ $(srcdir)/common.cc \
+ $(srcdir)/index.cc \
+ $(srcdir)/linear.cc \
+ $(srcdir)/search.cc
+CSRCS=\
+ $(srcdir)/map.c
diff --git a/contrib/groff/src/libs/libbib/common.cc b/contrib/groff/src/libs/libbib/common.cc
new file mode 100644
index 000000000000..4b2bcca23e21
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/common.cc
@@ -0,0 +1,38 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+unsigned hash(const char *s, int len)
+{
+#if 0
+ unsigned h = 0, g;
+ while (*s != '\0') {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+#endif
+ unsigned h = 0;
+ while (--len >= 0)
+ h = *s++ + 65587*h;
+ return h;
+}
+
diff --git a/contrib/groff/src/libs/libbib/index.cc b/contrib/groff/src/libs/libbib/index.cc
new file mode 100644
index 000000000000..55737717dcbe
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/index.cc
@@ -0,0 +1,641 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "lib.h"
+#include "cset.h"
+#include "cmap.h"
+#include "errarg.h"
+#include "error.h"
+
+#include "refid.h"
+#include "search.h"
+#include "index.h"
+#include "defs.h"
+
+#include "nonposix.h"
+
+// Interface to mmap.
+extern "C" {
+ void *mapread(int fd, int len);
+ int unmap(void *, int len);
+}
+
+#if 0
+const
+#endif
+int minus_one = -1;
+
+int verify_flag = 0;
+
+struct word_list;
+
+class index_search_item : public search_item {
+ search_item *out_of_date_files;
+ index_header header;
+ char *buffer;
+ void *map_addr;
+ int map_len;
+ tag *tags;
+ int *table;
+ int *lists;
+ char *pool;
+ char *key_buffer;
+ char *filename_buffer;
+ int filename_buflen;
+ char **common_words_table;
+ int common_words_table_size;
+ const char *ignore_fields;
+ time_t mtime;
+
+ const char *do_verify();
+ const int *search1(const char **pp, const char *end);
+ const int *search(const char *ptr, int length, int **temp_listp);
+ const char *munge_filename(const char *);
+ void read_common_words_file();
+ void add_out_of_date_file(int fd, const char *filename, int fid);
+public:
+ index_search_item(const char *, int);
+ ~index_search_item();
+ int load(int fd);
+ search_item_iterator *make_search_item_iterator(const char *);
+ int verify();
+ void check_files();
+ int next_filename_id() const;
+ friend class index_search_item_iterator;
+};
+
+class index_search_item_iterator : public search_item_iterator {
+ index_search_item *indx;
+ search_item_iterator *out_of_date_files_iter;
+ search_item *next_out_of_date_file;
+ const int *found_list;
+ int *temp_list;
+ char *buf;
+ int buflen;
+ linear_searcher searcher;
+ char *query;
+ int get_tag(int tagno, const linear_searcher &, const char **, int *,
+ reference_id *);
+public:
+ index_search_item_iterator(index_search_item *, const char *);
+ ~index_search_item_iterator();
+ int next(const linear_searcher &, const char **, int *, reference_id *);
+};
+
+
+index_search_item::index_search_item(const char *filename, int fid)
+: search_item(filename, fid), out_of_date_files(0), buffer(0), map_addr(0),
+ map_len(0), key_buffer(0), filename_buffer(0), filename_buflen(0),
+ common_words_table(0)
+{
+}
+
+index_search_item::~index_search_item()
+{
+ if (buffer)
+ free(buffer);
+ if (map_addr) {
+ if (unmap(map_addr, map_len) < 0)
+ error("unmap: %1", strerror(errno));
+ }
+ while (out_of_date_files) {
+ search_item *tem = out_of_date_files;
+ out_of_date_files = out_of_date_files->next;
+ delete tem;
+ }
+ a_delete filename_buffer;
+ a_delete key_buffer;
+ if (common_words_table) {
+ for (int i = 0; i < common_words_table_size; i++)
+ a_delete common_words_table[i];
+ a_delete common_words_table;
+ }
+}
+
+class file_closer {
+ int *fdp;
+public:
+ file_closer(int &fd) : fdp(&fd) { }
+ ~file_closer() { close(*fdp); }
+};
+
+// Tell the compiler that a variable is intentionally unused.
+inline void unused(void *) { }
+
+int index_search_item::load(int fd)
+{
+ file_closer fd_closer(fd); // close fd on return
+ unused(&fd_closer);
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ error("can't fstat `%1': %2", name, strerror(errno));
+ return 0;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ error("`%1' is not a regular file", name);
+ return 0;
+ }
+ mtime = sb.st_mtime;
+ int size = int(sb.st_size);
+ char *addr;
+ map_addr = mapread(fd, size);
+ if (map_addr) {
+ addr = (char *)map_addr;
+ map_len = size;
+ }
+ else {
+ addr = buffer = (char *)malloc(size);
+ if (buffer == 0) {
+ error("can't allocate buffer for `%1'", name);
+ return 0;
+ }
+ char *ptr = buffer;
+ int bytes_to_read = size;
+ while (bytes_to_read > 0) {
+ int nread = read(fd, ptr, bytes_to_read);
+ if (nread == 0) {
+ error("unexpected EOF on `%1'", name);
+ return 0;
+ }
+ if (nread < 0) {
+ error("read error on `%1': %2", name, strerror(errno));
+ return 0;
+ }
+ bytes_to_read -= nread;
+ ptr += nread;
+ }
+ }
+ header = *(index_header *)addr;
+ if (header.magic != INDEX_MAGIC) {
+ error("`%1' is not an index file: wrong magic number", name);
+ return 0;
+ }
+ if (header.version != INDEX_VERSION) {
+ error("version number in `%1' is wrong: was %2, should be %3",
+ name, header.version, INDEX_VERSION);
+ return 0;
+ }
+ int sz = (header.tags_size * sizeof(tag)
+ + header.lists_size * sizeof(int)
+ + header.table_size * sizeof(int)
+ + header.strings_size
+ + sizeof(header));
+ if (sz != size) {
+ error("size of `%1' is wrong: was %2, should be %3",
+ name, size, sz);
+ return 0;
+ }
+ tags = (tag *)(addr + sizeof(header));
+ lists = (int *)(tags + header.tags_size);
+ table = (int *)(lists + header.lists_size);
+ pool = (char *)(table + header.table_size);
+ ignore_fields = strchr(strchr(pool, '\0') + 1, '\0') + 1;
+ key_buffer = new char[header.truncate];
+ read_common_words_file();
+ return 1;
+}
+
+const char *index_search_item::do_verify()
+{
+ if (tags == 0)
+ return "not loaded";
+ if (lists[header.lists_size - 1] >= 0)
+ return "last list element not negative";
+ int i;
+ for (i = 0; i < header.table_size; i++) {
+ int li = table[i];
+ if (li >= header.lists_size)
+ return "bad list index";
+ if (li >= 0) {
+ for (int *ptr = lists + li; *ptr >= 0; ptr++) {
+ if (*ptr >= header.tags_size)
+ return "bad tag index";
+ if (*ptr >= ptr[1] && ptr[1] >= 0)
+ return "list not ordered";
+ }
+ }
+ }
+ for (i = 0; i < header.tags_size; i++) {
+ if (tags[i].filename_index >= header.strings_size)
+ return "bad index in tags";
+ if (tags[i].length < 0)
+ return "bad length in tags";
+ if (tags[i].start < 0)
+ return "bad start in tags";
+ }
+ if (pool[header.strings_size - 1] != '\0')
+ return "last character in pool not nul";
+ return 0;
+}
+
+int index_search_item::verify()
+{
+ const char *reason = do_verify();
+ if (!reason)
+ return 1;
+ error("`%1' is bad: %2", name, reason);
+ return 0;
+}
+
+int index_search_item::next_filename_id() const
+{
+ return filename_id + header.strings_size + 1;
+}
+
+search_item_iterator *index_search_item::make_search_item_iterator(
+ const char *query)
+{
+ return new index_search_item_iterator(this, query);
+}
+
+search_item *make_index_search_item(const char *filename, int fid)
+{
+ char *index_filename = new char[strlen(filename) + sizeof(INDEX_SUFFIX)];
+ strcpy(index_filename, filename);
+ strcat(index_filename, INDEX_SUFFIX);
+ int fd = open(index_filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return 0;
+ index_search_item *item = new index_search_item(index_filename, fid);
+ a_delete index_filename;
+ if (!item->load(fd)) {
+ close(fd);
+ delete item;
+ return 0;
+ }
+ else if (verify_flag && !item->verify()) {
+ delete item;
+ return 0;
+ }
+ else {
+ item->check_files();
+ return item;
+ }
+}
+
+
+index_search_item_iterator::index_search_item_iterator(index_search_item *ind,
+ const char *q)
+: indx(ind), out_of_date_files_iter(0), next_out_of_date_file(0), temp_list(0),
+ buf(0), buflen(0),
+ searcher(q, strlen(q), ind->ignore_fields, ind->header.truncate),
+ query(strsave(q))
+{
+ found_list = indx->search(q, strlen(q), &temp_list);
+ if (!found_list) {
+ found_list = &minus_one;
+ warning("all keys would have been discarded in constructing index `%1'",
+ indx->name);
+ }
+}
+
+index_search_item_iterator::~index_search_item_iterator()
+{
+ a_delete temp_list;
+ a_delete buf;
+ a_delete query;
+ delete out_of_date_files_iter;
+}
+
+int index_search_item_iterator::next(const linear_searcher &,
+ const char **pp, int *lenp,
+ reference_id *ridp)
+{
+ if (found_list) {
+ for (;;) {
+ int tagno = *found_list;
+ if (tagno == -1)
+ break;
+ found_list++;
+ if (get_tag(tagno, searcher, pp, lenp, ridp))
+ return 1;
+ }
+ found_list = 0;
+ next_out_of_date_file = indx->out_of_date_files;
+ }
+ while (next_out_of_date_file) {
+ if (out_of_date_files_iter == 0)
+ out_of_date_files_iter
+ = next_out_of_date_file->make_search_item_iterator(query);
+ if (out_of_date_files_iter->next(searcher, pp, lenp, ridp))
+ return 1;
+ delete out_of_date_files_iter;
+ out_of_date_files_iter = 0;
+ next_out_of_date_file = next_out_of_date_file->next;
+ }
+ return 0;
+}
+
+int index_search_item_iterator::get_tag(int tagno,
+ const linear_searcher &searcher,
+ const char **pp, int *lenp,
+ reference_id *ridp)
+{
+ if (tagno < 0 || tagno >= indx->header.tags_size) {
+ error("bad tag number");
+ return 0;
+ }
+ tag *tp = indx->tags + tagno;
+ const char *filename = indx->munge_filename(indx->pool + tp->filename_index);
+ int fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ error("can't fstat: %1", strerror(errno));
+ close(fd);
+ return 0;
+ }
+ time_t mtime = sb.st_mtime;
+ if (mtime > indx->mtime) {
+ indx->add_out_of_date_file(fd, filename,
+ indx->filename_id + tp->filename_index);
+ return 0;
+ }
+ int res = 0;
+ FILE *fp = fdopen(fd, FOPEN_RB);
+ if (!fp) {
+ error("fdopen failed");
+ close(fd);
+ return 0;
+ }
+ if (tp->start != 0 && fseek(fp, long(tp->start), 0) < 0)
+ error("can't seek on `%1': %2", filename, strerror(errno));
+ else {
+ int length = tp->length;
+ int err = 0;
+ if (length == 0) {
+ struct stat sb;
+ if (fstat(fileno(fp), &sb) < 0) {
+ error("can't stat `%1': %2", filename, strerror(errno));
+ err = 1;
+ }
+ else if (!S_ISREG(sb.st_mode)) {
+ error("`%1' is not a regular file", filename);
+ err = 1;
+ }
+ else
+ length = int(sb.st_size);
+ }
+ if (!err) {
+ if (length + 2 > buflen) {
+ a_delete buf;
+ buflen = length + 2;
+ buf = new char[buflen];
+ }
+ if (fread(buf + 1, 1, length, fp) != length)
+ error("fread on `%1' failed: %2", filename, strerror(errno));
+ else {
+ buf[0] = '\n';
+ // Remove the CR characters from CRLF pairs.
+ int sidx = 1, didx = 1;
+ for ( ; sidx < length + 1; sidx++, didx++)
+ {
+ if (buf[sidx] == '\r')
+ {
+ if (buf[++sidx] != '\n')
+ buf[didx++] = '\r';
+ else
+ length--;
+ }
+ if (sidx != didx)
+ buf[didx] = buf[sidx];
+ }
+ buf[length + 1] = '\n';
+ res = searcher.search(buf + 1, buf + 2 + length, pp, lenp);
+ if (res && ridp)
+ *ridp = reference_id(indx->filename_id + tp->filename_index,
+ tp->start);
+ }
+ }
+ }
+ fclose(fp);
+ return res;
+}
+
+const char *index_search_item::munge_filename(const char *filename)
+{
+ if (IS_ABSOLUTE(filename))
+ return filename;
+ const char *cwd = pool;
+ int need_slash = (cwd[0] != 0
+ && strchr(DIR_SEPS, strchr(cwd, '\0')[-1]) == 0);
+ int len = strlen(cwd) + strlen(filename) + need_slash + 1;
+ if (len > filename_buflen) {
+ a_delete filename_buffer;
+ filename_buflen = len;
+ filename_buffer = new char[len];
+ }
+ strcpy(filename_buffer, cwd);
+ if (need_slash)
+ strcat(filename_buffer, "/");
+ strcat(filename_buffer, filename);
+ return filename_buffer;
+}
+
+const int *index_search_item::search1(const char **pp, const char *end)
+{
+ while (*pp < end && !csalnum(**pp))
+ *pp += 1;
+ if (*pp >= end)
+ return 0;
+ const char *start = *pp;
+ while (*pp < end && csalnum(**pp))
+ *pp += 1;
+ int len = *pp - start;
+ if (len < header.shortest)
+ return 0;
+ if (len > header.truncate)
+ len = header.truncate;
+ int is_number = 1;
+ for (int i = 0; i < len; i++)
+ if (csdigit(start[i]))
+ key_buffer[i] = start[i];
+ else {
+ key_buffer[i] = cmlower(start[i]);
+ is_number = 0;
+ }
+ if (is_number && !(len == 4 && start[0] == '1' && start[1] == '9'))
+ return 0;
+ unsigned hc = hash(key_buffer, len);
+ if (common_words_table) {
+ for (int h = hc % common_words_table_size;
+ common_words_table[h];
+ --h) {
+ if (strlen(common_words_table[h]) == len
+ && memcmp(common_words_table[h], key_buffer, len) == 0)
+ return 0;
+ if (h == 0)
+ h = common_words_table_size;
+ }
+ }
+ int li = table[int(hc % header.table_size)];
+ return li < 0 ? &minus_one : lists + li;
+}
+
+static void merge(int *result, const int *s1, const int *s2)
+{
+ for (; *s1 >= 0; s1++) {
+ while (*s2 >= 0 && *s2 < *s1)
+ s2++;
+ if (*s2 == *s1)
+ *result++ = *s2;
+ }
+ *result++ = -1;
+}
+
+const int *index_search_item::search(const char *ptr, int length,
+ int **temp_listp)
+{
+ const char *end = ptr + length;
+ if (*temp_listp) {
+ a_delete *temp_listp;
+ *temp_listp = 0;
+ }
+ const int *first_list = 0;
+ while (ptr < end && (first_list = search1(&ptr, end)) == 0)
+ ;
+ if (!first_list)
+ return 0;
+ if (*first_list < 0)
+ return first_list;
+ const int *second_list = 0;
+ while (ptr < end && (second_list = search1(&ptr, end)) == 0)
+ ;
+ if (!second_list)
+ return first_list;
+ if (*second_list < 0)
+ return second_list;
+ const int *p;
+ for (p = first_list; *p >= 0; p++)
+ ;
+ int len = p - first_list;
+ for (p = second_list; *p >= 0; p++)
+ ;
+ if (p - second_list < len)
+ len = p - second_list;
+ int *matches = new int[len + 1];
+ merge(matches, first_list, second_list);
+ while (ptr < end) {
+ const int *list = search1(&ptr, end);
+ if (list != 0) {
+ if (*list < 0) {
+ a_delete matches;
+ return list;
+ }
+ merge(matches, matches, list);
+ if (*matches < 0) {
+ a_delete matches;
+ return &minus_one;
+ }
+ }
+ }
+ *temp_listp = matches;
+ return matches;
+}
+
+void index_search_item::read_common_words_file()
+{
+ if (header.common <= 0)
+ return;
+ const char *common_words_file = munge_filename(strchr(pool, '\0') + 1);
+ errno = 0;
+ FILE *fp = fopen(common_words_file, "r");
+ if (!fp) {
+ error("can't open `%1': %2", common_words_file, strerror(errno));
+ return;
+ }
+ common_words_table_size = 2*header.common + 1;
+ while (!is_prime(common_words_table_size))
+ common_words_table_size++;
+ common_words_table = new char *[common_words_table_size];
+ for (int i = 0; i < common_words_table_size; i++)
+ common_words_table[i] = 0;
+ int count = 0;
+ int key_len = 0;
+ for (;;) {
+ int c = getc(fp);
+ while (c != EOF && !csalnum(c))
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ do {
+ if (key_len < header.truncate)
+ key_buffer[key_len++] = cmlower(c);
+ c = getc(fp);
+ } while (c != EOF && csalnum(c));
+ if (key_len >= header.shortest) {
+ int h = hash(key_buffer, key_len) % common_words_table_size;
+ while (common_words_table[h]) {
+ if (h == 0)
+ h = common_words_table_size;
+ --h;
+ }
+ common_words_table[h] = new char[key_len + 1];
+ memcpy(common_words_table[h], key_buffer, key_len);
+ common_words_table[h][key_len] = '\0';
+ }
+ if (++count >= header.common)
+ break;
+ key_len = 0;
+ if (c == EOF)
+ break;
+ }
+ fclose(fp);
+}
+
+void index_search_item::add_out_of_date_file(int fd, const char *filename,
+ int fid)
+{
+ search_item **pp;
+ for (pp = &out_of_date_files; *pp; pp = &(*pp)->next)
+ if ((*pp)->is_named(filename))
+ return;
+ *pp = make_linear_search_item(fd, filename, fid);
+ warning("`%1' modified since `%2' created", filename, name);
+}
+
+void index_search_item::check_files()
+{
+ const char *pool_end = pool + header.strings_size;
+ for (const char *ptr = strchr(ignore_fields, '\0') + 1;
+ ptr < pool_end;
+ ptr = strchr(ptr, '\0') + 1) {
+ const char *path = munge_filename(ptr);
+ struct stat sb;
+ if (stat(path, &sb) < 0)
+ error("can't stat `%1': %2", path, strerror(errno));
+ else if (sb.st_mtime > mtime) {
+ int fd = open(path, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ error("can't open `%1': %2", path, strerror(errno));
+ else
+ add_out_of_date_file(fd, path, filename_id + (ptr - pool));
+ }
+ }
+}
diff --git a/contrib/groff/src/libs/libbib/linear.cc b/contrib/groff/src/libs/libbib/linear.cc
new file mode 100644
index 000000000000..a8c2a553735d
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/linear.cc
@@ -0,0 +1,503 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "cmap.h"
+#include "nonposix.h"
+
+#include "refid.h"
+#include "search.h"
+
+class file_buffer {
+ char *buffer;
+ char *bufend;
+public:
+ file_buffer();
+ ~file_buffer();
+ int load(int fd, const char *filename);
+ const char *get_start() const;
+ const char *get_end() const;
+};
+
+typedef unsigned char uchar;
+
+static uchar map[256];
+static uchar inv_map[256][3];
+
+struct map_init {
+ map_init();
+};
+
+static map_init the_map_init;
+
+map_init::map_init()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ map[i] = csalnum(i) ? cmlower(i) : '\0';
+ for (i = 0; i < 256; i++) {
+ if (cslower(i)) {
+ inv_map[i][0] = i;
+ inv_map[i][1] = cmupper(i);
+ inv_map[i][2] = '\0';
+ }
+ else if (csdigit(i)) {
+ inv_map[i][0] = i;
+ inv_map[i][1] = 0;
+ }
+ else
+ inv_map[i][0] = '\0';
+ }
+}
+
+
+class bmpattern {
+ char *pat;
+ int len;
+ int delta[256];
+public:
+ bmpattern(const char *pattern, int pattern_length);
+ ~bmpattern();
+ const char *search(const char *p, const char *end) const;
+ int length() const;
+};
+
+bmpattern::bmpattern(const char *pattern, int pattern_length)
+: len(pattern_length)
+{
+ pat = new char[len];
+ int i;
+ for (i = 0; i < len; i++)
+ pat[i] = map[uchar(pattern[i])];
+ for (i = 0; i < 256; i++)
+ delta[i] = len;
+ for (i = 0; i < len; i++)
+ for (const unsigned char *inv = inv_map[uchar(pat[i])]; *inv; inv++)
+ delta[*inv] = len - i - 1;
+}
+
+const char *bmpattern::search(const char *buf, const char *end) const
+{
+ int buflen = end - buf;
+ if (len > buflen)
+ return 0;
+ const char *strend;
+ if (buflen > len*4)
+ strend = end - len*4;
+ else
+ strend = buf;
+ const char *k = buf + len - 1;
+ const int *del = delta;
+ const char *pattern = pat;
+ for (;;) {
+ while (k < strend) {
+ int t = del[uchar(*k)];
+ if (!t)
+ break;
+ k += t;
+ k += del[uchar(*k)];
+ k += del[uchar(*k)];
+ }
+ while (k < end && del[uchar(*k)] != 0)
+ k++;
+ if (k == end)
+ break;
+ int j = len - 1;
+ const char *s = k;
+ for (;;) {
+ if (j == 0)
+ return s;
+ if (map[uchar(*--s)] != uchar(pattern[--j]))
+ break;
+ }
+ k++;
+ }
+ return 0;
+}
+
+bmpattern::~bmpattern()
+{
+ a_delete pat;
+}
+
+inline int bmpattern::length() const
+{
+ return len;
+}
+
+
+static const char *find_end(const char *bufend, const char *p);
+
+const char *linear_searcher::search_and_check(const bmpattern *key,
+ const char *buf, const char *bufend, const char **start) const
+{
+ assert(buf[-1] == '\n');
+ assert(bufend[-1] == '\n');
+ const char *ptr = buf;
+ for (;;) {
+ const char *found = key->search(ptr, bufend);
+ if (!found)
+ break;
+ if (check_match(buf, bufend, found, key->length(), &ptr, start))
+ return found;
+ }
+ return 0;
+}
+
+static const char *skip_field(const char *end, const char *p)
+{
+ for (;;)
+ if (*p++ == '\n') {
+ if (p == end || *p == '%')
+ break;
+ const char *q;
+ for (q = p; *q == ' ' || *q == '\t'; q++)
+ ;
+ if (*q == '\n')
+ break;
+ p = q + 1;
+ }
+ return p;
+}
+
+static const char *find_end(const char *bufend, const char *p)
+{
+ for (;;)
+ if (*p++ == '\n') {
+ if (p == bufend)
+ break;
+ const char *q;
+ for (q = p; *q == ' ' || *q == '\t'; q++)
+ ;
+ if (*q == '\n')
+ break;
+ p = q + 1;
+ }
+ return p;
+}
+
+
+int linear_searcher::check_match(const char *buf, const char *bufend,
+ const char *match, int matchlen,
+ const char **cont, const char **start) const
+{
+ *cont = match + 1;
+ // The user is required to supply only the first truncate_len characters
+ // of the key. If truncate_len <= 0, he must supply all the key.
+ if ((truncate_len <= 0 || matchlen < truncate_len)
+ && map[uchar(match[matchlen])] != '\0')
+ return 0;
+
+ // The character before the match must not be an alphanumeric
+ // character (unless the alphanumeric character follows one or two
+ // percent characters at the beginning of the line), nor must it be
+ // a percent character at the beginning of a line, nor a percent
+ // character following a percent character at the beginning of a
+ // line.
+
+ switch (match - buf) {
+ case 0:
+ break;
+ case 1:
+ if (match[-1] == '%' || map[uchar(match[-1])] != '\0')
+ return 0;
+ break;
+ case 2:
+ if (map[uchar(match[-1])] != '\0' && match[-2] != '%')
+ return 0;
+ if (match[-1] == '%'
+ && (match[-2] == '\n' || match[-2] == '%'))
+ return 0;
+ break;
+ default:
+ if (map[uchar(match[-1])] != '\0'
+ && !(match[-2] == '%'
+ && (match[-3] == '\n'
+ || (match[-3] == '%' && match[-4] == '\n'))))
+ return 0;
+ if (match[-1] == '%'
+ && (match[-2] == '\n'
+ || (match[-2] == '%' && match[-3] == '\n')))
+ return 0;
+ }
+
+ const char *p = match;
+ int had_percent = 0;
+ for (;;) {
+ if (*p == '\n') {
+ if (!had_percent && p[1] == '%') {
+ if (p[2] != '\0' && strchr(ignore_fields, p[2]) != 0) {
+ *cont = skip_field(bufend, match + matchlen);
+ return 0;
+ }
+ if (!start)
+ break;
+ had_percent = 1;
+ }
+ if (p <= buf) {
+ if (start)
+ *start = p + 1;
+ return 1;
+ }
+ const char *q;
+ for (q = p - 1; *q == ' ' || *q == '\t'; q--)
+ ;
+ if (*q == '\n') {
+ if (start)
+ *start = p + 1;
+ break;
+ }
+ p = q;
+ }
+ p--;
+ }
+ return 1;
+}
+
+file_buffer::file_buffer()
+: buffer(0), bufend(0)
+{
+}
+
+file_buffer::~file_buffer()
+{
+ a_delete buffer;
+}
+
+const char *file_buffer::get_start() const
+{
+ return buffer ? buffer + 4 : 0;
+}
+
+const char *file_buffer::get_end() const
+{
+ return bufend;
+}
+
+int file_buffer::load(int fd, const char *filename)
+{
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ error("can't fstat `%1': %2", filename, strerror(errno));
+ else if (!S_ISREG(sb.st_mode))
+ error("`%1' is not a regular file", filename);
+ else {
+ // We need one character extra at the beginning for an additional newline
+ // used as a sentinel. We get 4 instead so that the read buffer will be
+ // word-aligned. This seems to make the read slightly faster. We also
+ // need one character at the end also for an additional newline used as a
+ // sentinel.
+ int size = int(sb.st_size);
+ buffer = new char[size + 4 + 1];
+ int nread = read(fd, buffer + 4, size);
+ if (nread < 0)
+ error("error reading `%1': %2", filename, strerror(errno));
+ else if (nread != size)
+ error("size of `%1' decreased", filename);
+ else {
+ char c;
+ nread = read(fd, &c, 1);
+ if (nread != 0)
+ error("size of `%1' increased", filename);
+ else if (memchr(buffer + 4, '\0', size < 1024 ? size : 1024) != 0)
+ error("database `%1' is a binary file", filename);
+ else {
+ close(fd);
+ buffer[3] = '\n';
+ int sidx = 4, didx = 4;
+ for ( ; sidx < size + 4; sidx++, didx++)
+ {
+ if (buffer[sidx] == '\r')
+ {
+ if (buffer[++sidx] != '\n')
+ buffer[didx++] = '\r';
+ else
+ size--;
+ }
+ if (sidx != didx)
+ buffer[didx] = buffer[sidx];
+ }
+ bufend = buffer + 4 + size;
+ if (bufend[-1] != '\n')
+ *bufend++ = '\n';
+ return 1;
+ }
+ }
+ a_delete buffer;
+ buffer = 0;
+ }
+ close(fd);
+ return 0;
+}
+
+linear_searcher::linear_searcher(const char *query, int query_len,
+ const char *ign, int trunc)
+: ignore_fields(ign), truncate_len(trunc), keys(0), nkeys(0)
+{
+ const char *query_end = query + query_len;
+ int nk = 0;
+ const char *p;
+ for (p = query; p < query_end; p++)
+ if (map[uchar(*p)] != '\0'
+ && (p[1] == '\0' || map[uchar(p[1])] == '\0'))
+ nk++;
+ if (nk == 0)
+ return;
+ keys = new bmpattern*[nk];
+ p = query;
+ for (;;) {
+ while (p < query_end && map[uchar(*p)] == '\0')
+ p++;
+ if (p == query_end)
+ break;
+ const char *start = p;
+ while (p < query_end && map[uchar(*p)] != '\0')
+ p++;
+ keys[nkeys++] = new bmpattern(start, p - start);
+ }
+ assert(nkeys <= nk);
+ if (nkeys == 0) {
+ a_delete keys;
+ keys = 0;
+ }
+}
+
+linear_searcher::~linear_searcher()
+{
+ for (int i = 0; i < nkeys; i++)
+ delete keys[i];
+ a_delete keys;
+}
+
+int linear_searcher::search(const char *buffer, const char *bufend,
+ const char **startp, int *lengthp) const
+{
+ assert(bufend - buffer > 0);
+ assert(buffer[-1] == '\n');
+ assert(bufend[-1] == '\n');
+ if (nkeys == 0)
+ return 0;
+ for (;;) {
+ const char *refstart;
+ const char *found = search_and_check(keys[0], buffer, bufend, &refstart);
+ if (!found)
+ break;
+ const char *refend = find_end(bufend, found + keys[0]->length());
+ int i;
+ for (i = 1; i < nkeys; i++)
+ if (!search_and_check(keys[i], refstart, refend))
+ break;
+ if (i >= nkeys) {
+ *startp = refstart;
+ *lengthp = refend - refstart;
+ return 1;
+ }
+ buffer = refend;
+ }
+ return 0;
+}
+
+class linear_search_item : public search_item {
+ file_buffer fbuf;
+public:
+ linear_search_item(const char *filename, int fid);
+ ~linear_search_item();
+ int load(int fd);
+ search_item_iterator *make_search_item_iterator(const char *);
+ friend class linear_search_item_iterator;
+};
+
+class linear_search_item_iterator : public search_item_iterator {
+ linear_search_item *lsi;
+ int pos;
+public:
+ linear_search_item_iterator(linear_search_item *, const char *query);
+ ~linear_search_item_iterator();
+ int next(const linear_searcher &, const char **ptr, int *lenp,
+ reference_id *ridp);
+};
+
+search_item *make_linear_search_item(int fd, const char *filename, int fid)
+{
+ linear_search_item *item = new linear_search_item(filename, fid);
+ if (!item->load(fd)) {
+ delete item;
+ return 0;
+ }
+ else
+ return item;
+}
+
+linear_search_item::linear_search_item(const char *filename, int fid)
+: search_item(filename, fid)
+{
+}
+
+linear_search_item::~linear_search_item()
+{
+}
+
+int linear_search_item::load(int fd)
+{
+ return fbuf.load(fd, name);
+}
+
+search_item_iterator *linear_search_item::make_search_item_iterator(
+ const char *query)
+{
+ return new linear_search_item_iterator(this, query);
+}
+
+linear_search_item_iterator::linear_search_item_iterator(
+ linear_search_item *p, const char *)
+: lsi(p), pos(0)
+{
+}
+
+linear_search_item_iterator::~linear_search_item_iterator()
+{
+}
+
+int linear_search_item_iterator::next(const linear_searcher &searcher,
+ const char **startp, int *lengthp,
+ reference_id *ridp)
+{
+ const char *bufstart = lsi->fbuf.get_start();
+ const char *bufend = lsi->fbuf.get_end();
+ const char *ptr = bufstart + pos;
+ if (ptr < bufend && searcher.search(ptr, bufend, startp, lengthp)) {
+ pos = *startp + *lengthp - bufstart;
+ if (ridp)
+ *ridp = reference_id(lsi->filename_id, *startp - bufstart);
+ return 1;
+ }
+ else
+ return 0;
+}
diff --git a/contrib/groff/src/libs/libbib/map.c b/contrib/groff/src/libs/libbib/map.c
new file mode 100644
index 000000000000..ee5d0087ef1a
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/map.c
@@ -0,0 +1,71 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 HAVE_MMAP
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+/* The Net-2 man pages says that a MAP_FILE flag is required. */
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+char *mapread(fd, nbytes)
+ int fd;
+ int nbytes;
+{
+ char *p = (char *)mmap((caddr_t)0, (size_t)nbytes, PROT_READ,
+ MAP_FILE|MAP_PRIVATE, fd, (off_t)0);
+ if (p == (char *)-1)
+ return 0;
+ /* mmap() shouldn't return 0 since MAP_FIXED wasn't specified. */
+ if (p == 0)
+ abort();
+ return p;
+}
+
+int unmap(p, len)
+ char *p;
+ int len;
+{
+ return munmap((caddr_t)p, len);
+}
+
+#else /* not HAVE_MMAP */
+
+#include <errno.h>
+
+char *mapread(fd, nbytes)
+ int fd;
+ int nbytes;
+{
+ errno = ENODEV;
+ return 0;
+}
+
+int unmap(p, len)
+ char *p;
+ int len;
+{
+ errno = EINVAL;
+ return -1;
+}
+
+#endif /* not HAVE_MMAP */
diff --git a/contrib/groff/src/libs/libbib/search.cc b/contrib/groff/src/libs/libbib/search.cc
new file mode 100644
index 000000000000..1e027c609711
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/search.cc
@@ -0,0 +1,132 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "nonposix.h"
+
+#include "refid.h"
+#include "search.h"
+
+int linear_truncate_len = 6;
+const char *linear_ignore_fields = "XYZ";
+
+search_list::search_list()
+: list(0), niterators(0), next_fid(1)
+{
+}
+
+search_list::~search_list()
+{
+ assert(niterators == 0);
+ while (list) {
+ search_item *tem = list->next;
+ delete list;
+ list = tem;
+ }
+}
+
+void search_list::add_file(const char *filename, int silent)
+{
+ search_item *p = make_index_search_item(filename, next_fid);
+ if (!p) {
+ int fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ if (!silent)
+ error("can't open `%1': %2", filename, strerror(errno));
+ }
+ else
+ p = make_linear_search_item(fd, filename, next_fid);
+ }
+ if (p) {
+ search_item **pp;
+ for (pp = &list; *pp; pp = &(*pp)->next)
+ ;
+ *pp = p;
+ next_fid = p->next_filename_id();
+ }
+}
+
+int search_list::nfiles() const
+{
+ int n = 0;
+ for (search_item *ptr = list; ptr; ptr = ptr->next)
+ n++;
+ return n;
+}
+
+search_list_iterator::search_list_iterator(search_list *p, const char *q)
+: list(p), ptr(p->list), iter(0), query(strsave(q)),
+ searcher(q, strlen(q), linear_ignore_fields, linear_truncate_len)
+{
+ list->niterators += 1;
+}
+
+search_list_iterator::~search_list_iterator()
+{
+ list->niterators -= 1;
+ a_delete query;
+ delete iter;
+}
+
+int search_list_iterator::next(const char **pp, int *lenp, reference_id *ridp)
+{
+ while (ptr) {
+ if (iter == 0)
+ iter = ptr->make_search_item_iterator(query);
+ if (iter->next(searcher, pp, lenp, ridp))
+ return 1;
+ delete iter;
+ iter = 0;
+ ptr = ptr->next;
+ }
+ return 0;
+}
+
+search_item::search_item(const char *nm, int fid)
+: name(strsave(nm)), filename_id(fid), next(0)
+{
+}
+
+search_item::~search_item()
+{
+ a_delete name;
+}
+
+int search_item::is_named(const char *nm) const
+{
+ return strcmp(name, nm) == 0;
+}
+
+int search_item::next_filename_id() const
+{
+ return filename_id + 1;
+}
+
+search_item_iterator::~search_item_iterator()
+{
+}
diff --git a/contrib/groff/src/libs/libdriver/Makefile.sub b/contrib/groff/src/libs/libdriver/Makefile.sub
new file mode 100644
index 000000000000..d50f060bd917
--- /dev/null
+++ b/contrib/groff/src/libs/libdriver/Makefile.sub
@@ -0,0 +1,7 @@
+LIB=driver
+OBJS=\
+ input.o \
+ printer.o
+CCSRCS=\
+ $(srcdir)/input.cc \
+ $(srcdir)/printer.cc
diff --git a/contrib/groff/src/libs/libdriver/input.cc b/contrib/groff/src/libs/libdriver/input.cc
new file mode 100644
index 000000000000..e19841c1f778
--- /dev/null
+++ b/contrib/groff/src/libs/libdriver/input.cc
@@ -0,0 +1,504 @@
+// -*- 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 "driver.h"
+#include "device.h"
+#include "cset.h"
+
+const char *current_filename=0;
+int current_lineno;
+const char *device = 0;
+FILE *current_file;
+
+int get_integer(); // don't read the newline
+int possibly_get_integer(int *);
+char *get_string(int is_long = 0);
+void skip_line();
+
+struct environment_list {
+ environment env;
+ environment_list *next;
+
+ environment_list(const environment &, environment_list *);
+};
+
+environment_list::environment_list(const environment &e, environment_list *p)
+: env(e), next(p)
+{
+}
+
+inline int get_char()
+{
+ return getc(current_file);
+}
+
+/*
+ * remember_filename - is needed as get_string might overwrite the
+ * filename eventually.
+ */
+
+void remember_filename(const char *filename)
+{
+ if (current_filename != 0) {
+ free((char *)current_filename);
+ }
+ if (strcmp(filename, "-") == 0) {
+ filename = "<standard input>";
+ }
+ current_filename = (const char *)malloc(strlen(filename) + 1);
+ if (current_filename == 0) {
+ fatal("can't malloc space for filename");
+ }
+ strcpy((char *)current_filename, (char *)filename);
+}
+
+void do_file(const char *filename)
+{
+ int npages = 0;
+ if (filename[0] == '-' && filename[1] == '\0') {
+ remember_filename(filename);
+ current_file = stdin;
+ }
+ else {
+ errno = 0;
+ current_file = fopen(filename, "r");
+ if (current_file == 0) {
+ error("can't open `%1'", filename);
+ return;
+ }
+ remember_filename(filename);
+ }
+ environment env;
+ env.vpos = -1;
+ env.hpos = -1;
+ env.fontno = -1;
+ env.height = 0;
+ env.slant = 0;
+ environment_list *env_list = 0;
+ current_lineno = 1;
+ int command;
+ char *s;
+ command = get_char();
+ if (command == EOF)
+ return;
+ if (command != 'x')
+ fatal("the first command must be `x T'");
+ s = get_string();
+ if (s[0] != 'T')
+ fatal("the first command must be `x T'");
+ char *dev = get_string();
+ if (pr == 0) {
+ device = strsave(dev);
+ if (!font::load_desc())
+ fatal("sorry, I can't continue");
+ }
+ else {
+ if (device == 0 || strcmp(device, dev) != 0)
+ fatal("all files must use the same device");
+ }
+ skip_line();
+ env.size = 10*font::sizescale;
+ command = get_char();
+ if (command != 'x')
+ fatal("the second command must be `x res'");
+ s = get_string();
+ if (s[0] != 'r')
+ fatal("the second command must be `x res'");
+ int n = get_integer();
+ if (n != font::res)
+ fatal("resolution does not match");
+ n = get_integer();
+ if (n != font::hor)
+ fatal("horizontal resolution does not match");
+ n = get_integer();
+ if (n != font::vert)
+ fatal("vertical resolution does not match");
+ skip_line();
+ command = get_char();
+ if (command != 'x')
+ fatal("the third command must be `x init'");
+ s = get_string();
+ if (s[0] != 'i')
+ fatal("the third command must be `x init'");
+ skip_line();
+ if (pr == 0)
+ pr = make_printer();
+ while ((command = get_char()) != EOF) {
+ switch (command) {
+ case 's':
+ env.size = get_integer();
+ if (env.height == env.size)
+ env.height = 0;
+ break;
+ case 'f':
+ env.fontno = get_integer();
+ break;
+ case 'F':
+ remember_filename(get_string());
+ break;
+ case 'C':
+ {
+ if (npages == 0)
+ fatal("`C' command illegal before first `p' command");
+ char *s = get_string();
+ pr->set_special_char(s, &env);
+ }
+ break;
+ case 'N':
+ {
+ if (npages == 0)
+ fatal("`N' command illegal before first `p' command");
+ pr->set_numbered_char(get_integer(), &env);
+ }
+ break;
+ case 'H':
+ env.hpos = get_integer();
+ break;
+ case 'h':
+ env.hpos += get_integer();
+ break;
+ case 'V':
+ env.vpos = get_integer();
+ break;
+ case 'v':
+ env.vpos += get_integer();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int c = get_char();
+ if (!csdigit(c))
+ fatal("digit expected");
+ env.hpos += (command - '0')*10 + (c - '0');
+ }
+ // fall through
+ case 'c':
+ {
+ if (npages == 0)
+ fatal("`c' command illegal before first `p' command");
+ int c = get_char();
+ if (c == EOF)
+ fatal("missing argument to `c' command");
+ pr->set_ascii_char(c, &env);
+ }
+ break;
+ case 'n':
+ if (npages == 0)
+ fatal("`n' command illegal before first `p' command");
+ pr->end_of_line();
+ (void)get_integer();
+ (void)get_integer();
+ break;
+ case 'w':
+ case ' ':
+ break;
+ case '\n':
+ current_lineno++;
+ break;
+ case 'p':
+ if (npages)
+ pr->end_page(env.vpos);
+ npages++;
+ pr->begin_page(get_integer());
+ env.vpos = 0;
+ break;
+ case '{':
+ env_list = new environment_list(env, env_list);
+ break;
+ case '}':
+ if (!env_list) {
+ fatal("can't pop");
+ }
+ else {
+ env = env_list->env;
+ environment_list *tem = env_list;
+ env_list = env_list->next;
+ delete tem;
+ }
+ break;
+ case 'u':
+ {
+ if (npages == 0)
+ fatal("`u' command illegal before first `p' command");
+ int kern = get_integer();
+ int c = get_char();
+ while (c == ' ')
+ c = get_char();
+ while (c != EOF) {
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ int w;
+ pr->set_ascii_char(c, &env, &w);
+ env.hpos += w + kern;
+ c = get_char();
+ if (c == ' ')
+ break;
+ }
+ }
+ break;
+ case 't':
+ {
+ if (npages == 0)
+ fatal("`t' command illegal before first `p' command");
+ int c;
+ while ((c = get_char()) != EOF && c != ' ') {
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ int w;
+ pr->set_ascii_char(c, &env, &w);
+ env.hpos += w;
+ }
+ }
+ break;
+ case '#':
+ skip_line();
+ break;
+ case 'D':
+ {
+ if (npages == 0)
+ fatal("`D' command illegal before first `p' command");
+ int c;
+ while ((c = get_char()) == ' ')
+ ;
+ int n;
+ int *p = 0;
+ int szp = 0;
+ int np;
+ for (np = 0; possibly_get_integer(&n); np++) {
+ if (np >= szp) {
+ if (szp == 0) {
+ szp = 16;
+ p = new int[szp];
+ }
+ else {
+ int *oldp = p;
+ p = new int[szp*2];
+ memcpy(p, oldp, szp*sizeof(int));
+ szp *= 2;
+ a_delete oldp;
+ }
+ }
+ p[np] = n;
+ }
+ pr->draw(c, p, np, &env);
+ if (c == 'e') {
+ if (np > 0)
+ env.hpos += p[0];
+ }
+ else if (c == 'f' || c == 't')
+ ;
+ else {
+ int i;
+ for (i = 0; i < np/2; i++) {
+ env.hpos += p[i*2];
+ env.vpos += p[i*2 + 1];
+ }
+ // there might be an odd number of characters
+ if (i*2 < np)
+ env.hpos += p[i*2];
+ }
+ a_delete p;
+ skip_line();
+ }
+ break;
+ case 'x':
+ {
+ char *s = get_string();
+ int suppress_skip = 0;
+ switch (s[0]) {
+ case 'i':
+ error("duplicate `x init' command");
+ break;
+ case 'T':
+ error("duplicate `x T' command");
+ break;
+ case 'r':
+ error("duplicate `x res' command");
+ break;
+ case 'p':
+ break;
+ case 's':
+ break;
+ case 't':
+ break;
+ case 'f':
+ {
+ int n = get_integer();
+ char *name = get_string();
+ pr->load_font(n, name);
+ }
+ break;
+ case 'H':
+ env.height = get_integer();
+ if (env.height == env.size)
+ env.height = 0;
+ break;
+ case 'S':
+ env.slant = get_integer();
+ break;
+ case 'X':
+ if (npages == 0)
+ fatal("`x X' command illegal before first `p' command");
+ pr->special(get_string(1), &env);
+ suppress_skip = 1;
+ break;
+ case 'u':
+ // .cu
+ pr->special(get_string(), &env, 'u');
+ break;
+ default:
+ error("unrecognised x command `%1'", s);
+ }
+ if (!suppress_skip)
+ skip_line();
+ }
+ break;
+ default:
+ error("unrecognised command code %1", int(command));
+ skip_line();
+ break;
+ }
+ }
+ if (npages)
+ pr->end_page(env.vpos);
+}
+
+int get_integer()
+{
+ int c = get_char();
+ while (c == ' ')
+ c = get_char();
+ int neg = 0;
+ if (c == '-') {
+ neg = 1;
+ c = get_char();
+ }
+ if (!csdigit(c))
+ fatal("integer expected");
+ int total = 0;
+ do {
+ total = total*10;
+ if (neg)
+ total -= c - '0';
+ else
+ total += c - '0';
+ c = get_char();
+ } while (csdigit(c));
+ if (c != EOF)
+ ungetc(c, current_file);
+ return total;
+}
+
+int possibly_get_integer(int *res)
+{
+ int c = get_char();
+ while (c == ' ')
+ c = get_char();
+ int neg = 0;
+ if (c == '-') {
+ neg = 1;
+ c = get_char();
+ }
+ if (!csdigit(c)) {
+ if (c != EOF)
+ ungetc(c, current_file);
+ return 0;
+ }
+ int total = 0;
+ do {
+ total = total*10;
+ if (neg)
+ total -= c - '0';
+ else
+ total += c - '0';
+ c = get_char();
+ } while (csdigit(c));
+ if (c != EOF)
+ ungetc(c, current_file);
+ *res = total;
+ return 1;
+}
+
+
+char *get_string(int is_long)
+{
+ static char *buf;
+ static int buf_size;
+ int c = get_char();
+ while (c == ' ')
+ c = get_char();
+ for (int i = 0;; i++) {
+ if (i >= buf_size) {
+ if (buf_size == 0) {
+ buf_size = 16;
+ buf = new char[buf_size];
+ }
+ else {
+ char *old_buf = buf;
+ int old_size = buf_size;
+ buf_size *= 2;
+ buf = new char[buf_size];
+ memcpy(buf, old_buf, old_size);
+ a_delete old_buf;
+ }
+ }
+ if ((!is_long && (c == ' ' || c == '\n')) || c == EOF) {
+ buf[i] = '\0';
+ break;
+ }
+ if (is_long && c == '\n') {
+ current_lineno++;
+ c = get_char();
+ if (c == '+')
+ c = '\n';
+ else {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ buf[i] = c;
+ c = get_char();
+ }
+ if (c != EOF)
+ ungetc(c, current_file);
+ return buf;
+}
+
+void skip_line()
+{
+ int c;
+ while ((c = get_char()) != EOF)
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+}
diff --git a/contrib/groff/src/libs/libdriver/printer.cc b/contrib/groff/src/libs/libdriver/printer.cc
new file mode 100644
index 000000000000..4d66f7ba2307
--- /dev/null
+++ b/contrib/groff/src/libs/libdriver/printer.cc
@@ -0,0 +1,271 @@
+// -*- 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 "driver.h"
+
+printer *pr = 0;
+
+font_pointer_list::font_pointer_list(font *f, font_pointer_list *fp)
+: p(f), next(fp)
+{
+}
+
+printer::printer()
+: font_list(0), font_table(0), nfonts(0)
+{
+}
+
+printer::~printer()
+{
+ a_delete font_table;
+ while (font_list) {
+ font_pointer_list *tem = font_list;
+ font_list = font_list->next;
+ delete tem->p;
+ delete tem;
+ }
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+}
+
+void printer::load_font(int n, const char *nm)
+{
+ assert(n >= 0);
+ if (n >= nfonts) {
+ if (nfonts == 0) {
+ nfonts = 10;
+ if (nfonts <= n)
+ nfonts = n + 1;
+ font_table = new font *[nfonts];
+ for (int i = 0; i < nfonts; i++)
+ font_table[i] = 0;
+ }
+ else {
+ font **old_font_table = font_table;
+ int old_nfonts = nfonts;
+ nfonts *= 2;
+ if (n >= nfonts)
+ nfonts = n + 1;
+ font_table = new font *[nfonts];
+ int i;
+ for (i = 0; i < old_nfonts; i++)
+ font_table[i] = old_font_table[i];
+ for (i = old_nfonts; i < nfonts; i++)
+ font_table[i] = 0;
+ a_delete old_font_table;
+ }
+ }
+ font *f = find_font(nm);
+ font_table[n] = f;
+}
+
+font *printer::find_font(const char *nm)
+{
+ for (font_pointer_list *p = font_list; p; p = p->next)
+ if (strcmp(p->p->get_name(), nm) == 0)
+ return p->p;
+ font *f = make_font(nm);
+ if (!f)
+ fatal("sorry, I can't continue");
+ font_list = new font_pointer_list(f, font_list);
+ return f;
+}
+
+font *printer::make_font(const char *nm)
+{
+ return font::load_font(nm);
+}
+
+void printer::end_of_line()
+{
+}
+
+void printer::special(char *, const environment *, char)
+{
+}
+
+void printer::draw(int, int *, int, const environment *)
+{
+}
+
+void printer::set_ascii_char(unsigned char c, const environment *env,
+ int *widthp)
+{
+ char buf[2];
+ int w;
+ font *f;
+
+ buf[0] = c;
+ buf[1] = '\0';
+
+ int i = set_char_and_width(buf, env, &w, &f);
+ set_char(i, f, env, w, 0);
+ if (widthp) {
+ *widthp = w;
+ }
+}
+
+void printer::set_special_char(const char *nm, const environment *env,
+ int *widthp)
+{
+ font *f;
+ int w;
+ int i = set_char_and_width(nm, env, &w, &f);
+ if (i != -1) {
+ set_char(i, f, env, w, nm);
+ if (widthp) {
+ *widthp = w;
+ }
+ }
+}
+
+int printer::set_char_and_width(const char *nm, const environment *env,
+ int *widthp, font **f)
+{
+ int i = font::name_to_index(nm);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("bad font position `%1'", fn);
+ return(-1);
+ }
+ *f = font_table[fn];
+ if (*f == 0) {
+ error("no font mounted at `%1'", fn);
+ return(-1);
+ }
+ if (!(*f)->contains(i)) {
+ if (nm[0] != '\0' && nm[1] == '\0')
+ error("font `%1' does not contain ascii character `%2'",
+ (*f)->get_name(),
+ nm[0]);
+ else
+ error("font `%1' does not contain special character `%2'",
+ (*f)->get_name(),
+ nm);
+ return(-1);
+ }
+ int w = (*f)->get_width(i, env->size);
+ if (widthp)
+ *widthp = w;
+ return( i );
+}
+
+void printer::set_numbered_char(int num, const environment *env, int *widthp)
+{
+ int i = font::number_to_index(num);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("bad font position `%1'", fn);
+ return;
+ }
+ font *f = font_table[fn];
+ if (f == 0) {
+ error("no font mounted at `%1'", fn);
+ return;
+ }
+ if (!f->contains(i)) {
+ error("font `%1' does not contain numbered character %2",
+ f->get_name(),
+ num);
+ return;
+ }
+ int w = f->get_width(i, env->size);
+ if (widthp)
+ *widthp = w;
+ set_char(i, f, env, w, 0);
+}
+
+font *printer::get_font_from_index(int fontno)
+{
+ if ((fontno >= 0) && (fontno < nfonts))
+ return(font_table[fontno]);
+ else
+ return(0);
+}
+
+// This utility function adjusts the specified center of the
+// arc so that it is equidistant between the specified start
+// and end points. (p[0], p[1]) is a vector from the current
+// point to the center; (p[2], p[3]) is a vector from the
+// center to the end point. If the center can be adjusted,
+// a vector from the current point to the adjusted center is
+// stored in c[0], c[1] and 1 is returned. Otherwise 0 is
+// returned.
+
+#if 1
+int printer::adjust_arc_center(const int *p, double *c)
+{
+ // We move the center along a line parallel to the line between
+ // the specified start point and end point so that the center
+ // is equidistant between the start and end point.
+ // It can be proved (using Lagrange multipliers) that this will
+ // give the point nearest to the specified center that is equidistant
+ // between the start and end point.
+
+ double x = p[0] + p[2]; // (x, y) is the end point
+ double y = p[1] + p[3];
+ double n = x*x + y*y;
+ if (n != 0) {
+ c[0]= double(p[0]);
+ c[1] = double(p[1]);
+ double k = .5 - (c[0]*x + c[1]*y)/n;
+ c[0] += k*x;
+ c[1] += k*y;
+ return 1;
+ }
+ else
+ return 0;
+}
+#else
+int printer::adjust_arc_center(const int *p, double *c)
+{
+ int x = p[0] + p[2]; // (x, y) is the end point
+ int y = p[1] + p[3];
+ // Start at the current point; go in the direction of the specified
+ // center point until we reach a point that is equidistant between
+ // the specified starting point and the specified end point. Place
+ // the center of the arc there.
+ double n = p[0]*double(x) + p[1]*double(y);
+ if (n > 0) {
+ double k = (double(x)*x + double(y)*y)/(2.0*n);
+ // (cx, cy) is our chosen center
+ c[0] = k*p[0];
+ c[1] = k*p[1];
+ return 1;
+ }
+ else {
+ // We would never reach such a point. So instead start at the
+ // specified end point of the arc. Go towards the specified
+ // center point until we reach a point that is equidistant between
+ // the specified start point and specified end point. Place
+ // the center of the arc there.
+ n = p[2]*double(x) + p[3]*double(y);
+ if (n > 0) {
+ double k = 1 - (double(x)*x + double(y)*y)/(2.0*n);
+ // (c[0], c[1]) is our chosen center
+ c[0] = p[0] + k*p[2];
+ c[1] = p[1] + k*p[3];
+ return 1;
+ }
+ else
+ return 0;
+ }
+}
+#endif
diff --git a/contrib/groff/src/libs/libgroff/Makefile.sub b/contrib/groff/src/libs/libgroff/Makefile.sub
new file mode 100644
index 000000000000..5ce0691af5a8
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/Makefile.sub
@@ -0,0 +1,84 @@
+LIB=groff
+OBJS=\
+ assert.o \
+ change_lf.o \
+ cmap.o \
+ cset.o \
+ device.o \
+ errarg.o \
+ error.o \
+ fatal.o \
+ filename.o \
+ font.o \
+ fontfile.o \
+ getopt.o \
+ getopt1.o \
+ htmlindicate.o \
+ illegal.o \
+ lf.o \
+ lineno.o \
+ macropath.o \
+ nametoindex.o \
+ new.o \
+ prime.o \
+ progname.o \
+ ptable.o \
+ searchpath.o \
+ string.o \
+ strsave.o \
+ tmpfile.o \
+ iftoa.o \
+ itoa.o \
+ matherr.o \
+ version.o \
+ $(LIBOBJS)
+CCSRCS=\
+ $(srcdir)/assert.cc \
+ $(srcdir)/change_lf.cc \
+ $(srcdir)/cmap.cc \
+ $(srcdir)/cset.cc \
+ $(srcdir)/device.cc \
+ $(srcdir)/errarg.cc \
+ $(srcdir)/error.cc \
+ $(srcdir)/fatal.cc \
+ $(srcdir)/filename.cc \
+ $(srcdir)/font.cc \
+ $(srcdir)/fontfile.cc \
+ $(srcdir)/htmlindicate.cc \
+ $(srcdir)/illegal.cc \
+ $(srcdir)/lf.cc \
+ $(srcdir)/lineno.cc \
+ $(srcdir)/macropath.cc \
+ $(srcdir)/nametoindex.cc \
+ $(srcdir)/new.cc \
+ $(srcdir)/prime.cc \
+ $(srcdir)/progname.cc \
+ $(srcdir)/ptable.cc \
+ $(srcdir)/searchpath.cc \
+ $(srcdir)/string.cc \
+ $(srcdir)/strsave.cc \
+ $(srcdir)/tmpfile.cc \
+ version.cc
+CSRCS=\
+ $(srcdir)/fmod.c \
+ $(srcdir)/getcwd.c \
+ $(srcdir)/getopt.c \
+ $(srcdir)/getopt1.c \
+ $(srcdir)/iftoa.c \
+ $(srcdir)/itoa.c \
+ $(srcdir)/matherr.c \
+ $(srcdir)/putenv.c \
+ $(srcdir)/strerror.c \
+ $(srcdir)/strtol.c
+GENSRCS=\
+ version.cc
+
+version=`cat $(top_srcdir)/VERSION`
+revision=`cat $(top_srcdir)/REVISION`
+
+version.cc: $(top_srcdir)/VERSION $(top_srcdir)/REVISION
+ @echo Making version.cc
+ @echo const char \*version_string = \"$(version)\"\; >$@
+ @echo const char \*revision_string = \"$(revision)\"\; >>$@
+ @echo const char \*Version_string = \"$(version).$(revision)\"\; | \
+ sed -e 's/\.0\"/\"/' >>$@
diff --git a/contrib/groff/src/libs/libgroff/assert.cc b/contrib/groff/src/libs/libgroff/assert.cc
new file mode 100644
index 000000000000..89742e32cbb1
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/assert.cc
@@ -0,0 +1,34 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <stdlib.h>
+#include "assert.h"
+
+extern const char *program_name;
+
+void assertion_failed(int lineno, const char *filename)
+{
+ if (program_name != 0)
+ fprintf(stderr, "%s: ", program_name);
+ fprintf(stderr, "Failed assertion at line %d, file `%s'.\n",
+ lineno, filename);
+ fflush(stderr);
+ abort();
+}
diff --git a/contrib/groff/src/libs/libgroff/change_lf.cc b/contrib/groff/src/libs/libgroff/change_lf.cc
new file mode 100644
index 000000000000..2e44af1d5749
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/change_lf.cc
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <string.h>
+
+extern char *strsave(const char *);
+
+extern const char *current_filename;
+extern int current_lineno;
+
+void change_filename(const char *f)
+{
+ if (current_filename != 0 && strcmp(current_filename, f) == 0)
+ return;
+ current_filename = strsave(f);
+}
+
+void change_lineno(int ln)
+{
+ current_lineno = ln;
+}
diff --git a/contrib/groff/src/libs/libgroff/device.cc b/contrib/groff/src/libs/libgroff/device.cc
new file mode 100644
index 000000000000..7efbfef25a2d
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/device.cc
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdlib.h>
+#include "device.h"
+#include "defs.h"
+
+const char *device = DEVICE;
+
+struct device_init {
+ device_init();
+} _device_init;
+
+device_init::device_init()
+{
+ char *tem = getenv("GROFF_TYPESETTER");
+ if (tem)
+ device = tem;
+}
diff --git a/contrib/groff/src/libs/libgroff/errarg.cc b/contrib/groff/src/libs/libgroff/errarg.cc
new file mode 100644
index 000000000000..f8075ea71a49
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/errarg.cc
@@ -0,0 +1,118 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include "assert.h"
+#include "errarg.h"
+
+errarg::errarg(const char *p) : type(STRING)
+{
+ s = p ? p : "(null)";
+}
+
+errarg::errarg() : type(EMPTY)
+{
+}
+
+errarg::errarg(unsigned char cc) : type(CHAR)
+{
+ c = cc;
+}
+
+errarg::errarg(int nn) : type(INTEGER)
+{
+ n = nn;
+}
+
+errarg::errarg(char cc) : type(CHAR)
+{
+ c = cc;
+}
+
+errarg::errarg(double dd) : type(DOUBLE)
+{
+ d = dd;
+}
+
+int errarg::empty() const
+{
+ return type == EMPTY;
+}
+
+extern "C" {
+ const char *i_to_a(int);
+}
+
+void errarg::print() const
+{
+ switch (type) {
+ case INTEGER:
+ fputs(i_to_a(n), stderr);
+ break;
+ case CHAR:
+ putc(c, stderr);
+ break;
+ case STRING:
+ fputs(s, stderr);
+ break;
+ case DOUBLE:
+ fprintf(stderr, "%g", d);
+ break;
+ case EMPTY:
+ break;
+ }
+}
+
+errarg empty_errarg;
+
+void errprint(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ assert(format != 0);
+ char c;
+ while ((c = *format++) != '\0') {
+ if (c == '%') {
+ c = *format++;
+ switch(c) {
+ case '%':
+ fputc('%', stderr);
+ break;
+ case '1':
+ assert(!arg1.empty());
+ arg1.print();
+ break;
+ case '2':
+ assert(!arg2.empty());
+ arg2.print();
+ break;
+ case '3':
+ assert(!arg3.empty());
+ arg3.print();
+ break;
+ default:
+ assert(0);
+ }
+ }
+ else
+ putc(c, stderr);
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/error.cc b/contrib/groff/src/libs/libgroff/error.cc
new file mode 100644
index 000000000000..53fd629845fc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/error.cc
@@ -0,0 +1,137 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "errarg.h"
+#include "error.h"
+
+extern void fatal_error_exit();
+
+enum error_type { WARNING, ERROR, FATAL };
+
+static void do_error_with_file_and_line(const char *filename, int lineno,
+ error_type type,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ int need_space = 0;
+ if (program_name) {
+ fprintf(stderr, "%s:", program_name);
+ need_space = 1;
+ }
+ if (lineno >= 0 && filename != 0) {
+ if (strcmp(filename, "-") == 0)
+ filename = "<standard input>";
+ fprintf(stderr, "%s:%d:", filename, lineno);
+ need_space = 1;
+ }
+ switch (type) {
+ case FATAL:
+ fputs("fatal error:", stderr);
+ need_space = 1;
+ break;
+ case ERROR:
+ break;
+ case WARNING:
+ fputs("warning:", stderr);
+ need_space = 1;
+ break;
+ }
+ if (need_space)
+ fputc(' ', stderr);
+ errprint(format, arg1, arg2, arg3);
+ fputc('\n', stderr);
+ fflush(stderr);
+ if (type == FATAL)
+ fatal_error_exit();
+}
+
+
+static void do_error(error_type type,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(current_filename, current_lineno,
+ type, format, arg1, arg2, arg3);
+}
+
+
+void error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(ERROR, format, arg1, arg2, arg3);
+}
+
+void warning(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(WARNING, 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 error_with_file_and_line(const char *filename,
+ int lineno,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(filename, lineno,
+ ERROR, format, arg1, arg2, arg3);
+}
+
+void warning_with_file_and_line(const char *filename,
+ int lineno,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(filename, lineno,
+ WARNING, 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)
+{
+ do_error_with_file_and_line(filename, lineno,
+ FATAL, format, arg1, arg2, arg3);
+}
diff --git a/contrib/groff/src/libs/libgroff/fatal.cc b/contrib/groff/src/libs/libgroff/fatal.cc
new file mode 100644
index 000000000000..42560dcaf5e7
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/fatal.cc
@@ -0,0 +1,27 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdlib.h>
+
+#define FATAL_ERROR_EXIT_CODE 3
+
+void fatal_error_exit()
+{
+ exit(FATAL_ERROR_EXIT_CODE);
+}
diff --git a/contrib/groff/src/libs/libgroff/filename.cc b/contrib/groff/src/libs/libgroff/filename.cc
new file mode 100644
index 000000000000..1cbaa93dddfc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/filename.cc
@@ -0,0 +1 @@
+const char *current_filename = 0;
diff --git a/contrib/groff/src/libs/libgroff/fmod.c b/contrib/groff/src/libs/libgroff/fmod.c
new file mode 100644
index 000000000000..818f946edc04
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/fmod.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <math.h>
+
+double fmod(x, y)
+ double x, y;
+{
+ double quot = x/y;
+ return x - (quot < 0.0 ? ceil(quot) : floor(quot)) * y;
+}
+
diff --git a/contrib/groff/src/libs/libgroff/font.cc b/contrib/groff/src/libs/libgroff/font.cc
new file mode 100644
index 000000000000..6cdd6479b84c
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/font.cc
@@ -0,0 +1,938 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "font.h"
+#include "lib.h"
+
+const char *const WS = " \t\n\r";
+
+struct font_char_metric {
+ char type;
+ int code;
+ int width;
+ int height;
+ int depth;
+ int pre_math_space;
+ int italic_correction;
+ int subscript_correction;
+ char *special_device_coding;
+};
+
+struct font_kern_list {
+ int i1;
+ int i2;
+ int amount;
+ font_kern_list *next;
+
+ font_kern_list(int, int, int, font_kern_list * = 0);
+};
+
+struct font_widths_cache {
+ font_widths_cache *next;
+ int point_size;
+ int *width;
+
+ font_widths_cache(int, int, font_widths_cache * = 0);
+ ~font_widths_cache();
+};
+
+/* text_file */
+
+struct text_file {
+ FILE *fp;
+ char *path;
+ int lineno;
+ int size;
+ int skip_comments;
+ char *buf;
+ text_file(FILE *fp, char *p);
+ ~text_file();
+ int next();
+ void error(const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+};
+
+text_file::text_file(FILE *p, char *s)
+: fp(p), path(s), lineno(0), size(0), skip_comments(1), buf(0)
+{
+}
+
+text_file::~text_file()
+{
+ a_delete buf;
+ a_delete path;
+ if (fp)
+ fclose(fp);
+}
+
+
+int text_file::next()
+{
+ if (fp == 0)
+ return 0;
+ if (buf == 0) {
+ buf = new char [128];
+ size = 128;
+ }
+ for (;;) {
+ int i = 0;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ if (illegal_input_char(c))
+ error("illegal input character code `%1'", int(c));
+ else {
+ if (i + 1 >= size) {
+ char *old_buf = buf;
+ buf = new char[size*2];
+ memcpy(buf, old_buf, size);
+ a_delete old_buf;
+ size *= 2;
+ }
+ buf[i++] = c;
+ if (c == '\n')
+ break;
+ }
+ }
+ if (i == 0)
+ break;
+ buf[i] = '\0';
+ lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr != 0 && (!skip_comments || *ptr != '#'))
+ return 1;
+ }
+ return 0;
+}
+
+void text_file::error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
+}
+
+
+/* font functions */
+
+font::font(const char *s)
+: ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
+ ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
+{
+ name = new char[strlen(s) + 1];
+ strcpy(name, s);
+ internalname = 0;
+ slant = 0.0;
+ // load(); // for testing
+}
+
+font::~font()
+{
+ a_delete ch;
+ a_delete ch_index;
+ if (kern_hash_table) {
+ for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
+ font_kern_list *kerns = kern_hash_table[i];
+ while (kerns) {
+ font_kern_list *tem = kerns;
+ kerns = kerns->next;
+ delete tem;
+ }
+ }
+ a_delete kern_hash_table;
+ }
+ a_delete name;
+ a_delete internalname;
+ while (widths_cache) {
+ font_widths_cache *tem = widths_cache;
+ widths_cache = widths_cache->next;
+ delete tem;
+ }
+}
+
+static int scale_round(int n, int x, int y)
+{
+ assert(x >= 0 && y > 0);
+ int y2 = y/2;
+ if (x == 0)
+ return 0;
+ if (n >= 0) {
+ if (n <= (INT_MAX - y2)/x)
+ return (n*x + y2)/y;
+ return int(n*double(x)/double(y) + .5);
+ }
+ else {
+ if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
+ return (n*x - y2)/y;
+ return int(n*double(x)/double(y) - .5);
+ }
+}
+
+inline int font::scale(int w, int sz)
+{
+ return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
+}
+
+int font::get_skew(int c, int point_size, int sl)
+{
+ int h = get_height(c, point_size);
+ return int(h*tan((slant+sl)*PI/180.0) + .5);
+}
+
+int font::contains(int c)
+{
+ return c >= 0 && c < nindices && ch_index[c] >= 0;
+}
+
+int font::is_special()
+{
+ return special;
+}
+
+font_widths_cache::font_widths_cache(int ps, int ch_size,
+ font_widths_cache *p)
+: next(p), point_size(ps)
+{
+ width = new int[ch_size];
+ for (int i = 0; i < ch_size; i++)
+ width[i] = -1;
+}
+
+font_widths_cache::~font_widths_cache()
+{
+ a_delete width;
+}
+
+int font::get_width(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices);
+ int i = ch_index[c];
+ assert(i >= 0);
+
+ if (point_size == unitwidth)
+ return ch[i].width;
+
+ if (!widths_cache)
+ widths_cache = new font_widths_cache(point_size, ch_size);
+ else if (widths_cache->point_size != point_size) {
+ font_widths_cache **p;
+ for (p = &widths_cache; *p; p = &(*p)->next)
+ if ((*p)->point_size == point_size)
+ break;
+ if (*p) {
+ font_widths_cache *tem = *p;
+ *p = (*p)->next;
+ tem->next = widths_cache;
+ widths_cache = tem;
+ }
+ else
+ widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
+ }
+ int &w = widths_cache->width[i];
+ if (w < 0)
+ w = scale(ch[i].width, point_size);
+ return w;
+}
+
+int font::get_height(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].height, point_size);
+}
+
+int font::get_depth(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].depth, point_size);
+}
+
+int font::get_italic_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].italic_correction, point_size);
+}
+
+int font::get_left_italic_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].pre_math_space, point_size);
+}
+
+int font::get_subscript_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].subscript_correction, point_size);
+}
+
+int font::get_space_width(int point_size)
+{
+ return scale(space_width, point_size);
+}
+
+font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
+ : i1(c1), i2(c2), amount(n), next(p)
+{
+}
+
+inline int font::hash_kern(int i1, int i2)
+{
+ int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
+ return n < 0 ? -n : n;
+}
+
+void font::add_kern(int i1, int i2, int amount)
+{
+ if (!kern_hash_table) {
+ kern_hash_table = new font_kern_list *[KERN_HASH_TABLE_SIZE];
+ for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
+ kern_hash_table[i] = 0;
+ }
+ font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
+ *p = new font_kern_list(i1, i2, amount, *p);
+}
+
+int font::get_kern(int i1, int i2, int point_size)
+{
+ if (kern_hash_table) {
+ for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
+ if (i1 == p->i1 && i2 == p->i2)
+ return scale(p->amount, point_size);
+ }
+ return 0;
+}
+
+int font::has_ligature(int mask)
+{
+ return mask & ligatures;
+}
+
+int font::get_character_type(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return ch[ch_index[c]].type;
+}
+
+int font::get_code(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return ch[ch_index[c]].code;
+}
+
+const char *font::get_name()
+{
+ return name;
+}
+
+const char *font::get_internal_name()
+{
+ return internalname;
+}
+
+const char *font::get_special_device_encoding(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return( ch[ch_index[c]].special_device_coding );
+}
+
+void font::alloc_ch_index(int index)
+{
+ if (nindices == 0) {
+ nindices = 128;
+ if (index >= nindices)
+ nindices = index + 10;
+ ch_index = new short[nindices];
+ for (int i = 0; i < nindices; i++)
+ ch_index[i] = -1;
+ }
+ else {
+ int old_nindices = nindices;
+ nindices *= 2;
+ if (index >= nindices)
+ nindices = index + 10;
+ short *old_ch_index = ch_index;
+ ch_index = new short[nindices];
+ memcpy(ch_index, old_ch_index, sizeof(short)*old_nindices);
+ for (int i = old_nindices; i < nindices; i++)
+ ch_index[i] = -1;
+ a_delete old_ch_index;
+ }
+}
+
+void font::extend_ch()
+{
+ if (ch == 0)
+ ch = new font_char_metric[ch_size = 16];
+ else {
+ int old_ch_size = ch_size;
+ ch_size *= 2;
+ font_char_metric *old_ch = ch;
+ ch = new font_char_metric[ch_size];
+ memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
+ a_delete old_ch;
+ }
+}
+
+void font::compact()
+{
+ int i;
+ for (i = nindices - 1; i >= 0; i--)
+ if (ch_index[i] >= 0)
+ break;
+ i++;
+ if (i < nindices) {
+ short *old_ch_index = ch_index;
+ ch_index = new short[i];
+ memcpy(ch_index, old_ch_index, i*sizeof(short));
+ a_delete old_ch_index;
+ nindices = i;
+ }
+ if (ch_used < ch_size) {
+ font_char_metric *old_ch = ch;
+ ch = new font_char_metric[ch_used];
+ memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
+ a_delete old_ch;
+ ch_size = ch_used;
+ }
+}
+
+void font::add_entry(int index, const font_char_metric &metric)
+{
+ assert(index >= 0);
+ if (index >= nindices)
+ alloc_ch_index(index);
+ assert(index < nindices);
+ if (ch_used + 1 >= ch_size)
+ extend_ch();
+ assert(ch_used + 1 < ch_size);
+ ch_index[index] = ch_used;
+ ch[ch_used++] = metric;
+}
+
+void font::copy_entry(int new_index, int old_index)
+{
+ assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
+ if (new_index >= nindices)
+ alloc_ch_index(new_index);
+ ch_index[new_index] = ch_index[old_index];
+}
+
+font *font::load_font(const char *s, int *not_found)
+{
+ font *f = new font(s);
+ if (!f->load(not_found)) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+static char *trim_arg(char *p)
+{
+ if (!p)
+ return 0;
+ while (csspace(*p))
+ p++;
+ char *q = strchr(p, '\0');
+ while (q > p && csspace(q[-1]))
+ q--;
+ *q = '\0';
+ return p;
+}
+
+// If the font can't be found, then if not_found is non-NULL, it will be set
+// to 1 otherwise a message will be printed.
+
+int font::load(int *not_found)
+{
+ char *path;
+ FILE *fp;
+ if ((fp = open_file(name, &path)) == NULL) {
+ if (not_found)
+ *not_found = 1;
+ else
+ error("can't find font file `%1'", name);
+ return 0;
+ }
+ text_file t(fp, path);
+ t.skip_comments = 1;
+ char *p;
+ for (;;) {
+ if (!t.next()) {
+ t.error("missing charset command");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ if (strcmp(p, "name") == 0) {
+ }
+ else if (strcmp(p, "spacewidth") == 0) {
+ p = strtok(0, WS);
+ int n;
+ if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
+ t.error("bad argument for spacewidth command");
+ return 0;
+ }
+ space_width = n;
+ }
+ else if (strcmp(p, "slant") == 0) {
+ p = strtok(0, WS);
+ double n;
+ if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
+ t.error("bad argument for slant command", p);
+ return 0;
+ }
+ slant = n;
+ }
+ else if (strcmp(p, "ligatures") == 0) {
+ for (;;) {
+ p = strtok(0, WS);
+ if (p == 0 || strcmp(p, "0") == 0)
+ break;
+ if (strcmp(p, "ff") == 0)
+ ligatures |= LIG_ff;
+ else if (strcmp(p, "fi") == 0)
+ ligatures |= LIG_fi;
+ else if (strcmp(p, "fl") == 0)
+ ligatures |= LIG_fl;
+ else if (strcmp(p, "ffi") == 0)
+ ligatures |= LIG_ffi;
+ else if (strcmp(p, "ffl") == 0)
+ ligatures |= LIG_ffl;
+ else {
+ t.error("unrecognised ligature `%1'", p);
+ return 0;
+ }
+ }
+ }
+ else if (strcmp(p, "internalname") == 0) {
+ p = strtok(0, WS);
+ if (!p) {
+ t.error("`internalname command requires argument");
+ return 0;
+ }
+ internalname = new char[strlen(p) + 1];
+ strcpy(internalname, p);
+ }
+ else if (strcmp(p, "special") == 0) {
+ special = 1;
+ }
+ else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
+ char *command = p;
+ p = strtok(0, "\n");
+ handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
+ }
+ else
+ break;
+ }
+ char *command = p;
+ int had_charset = 0;
+ t.skip_comments = 0;
+ while (command) {
+ if (strcmp(command, "kernpairs") == 0) {
+ for (;;) {
+ if (!t.next()) {
+ command = 0;
+ break;
+ }
+ char *c1 = strtok(t.buf, WS);
+ if (c1 == 0)
+ continue;
+ char *c2 = strtok(0, WS);
+ if (c2 == 0) {
+ command = c1;
+ break;
+ }
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing kern amount");
+ return 0;
+ }
+ int n;
+ if (sscanf(p, "%d", &n) != 1) {
+ t.error("bad kern amount `%1'", p);
+ return 0;
+ }
+ int i1 = name_to_index(c1);
+ if (i1 < 0) {
+ t.error("illegal character `%1'", c1);
+ return 0;
+ }
+ int i2 = name_to_index(c2);
+ if (i2 < 0) {
+ t.error("illegal character `%1'", c2);
+ return 0;
+ }
+ add_kern(i1, i2, n);
+ }
+ }
+ else if (strcmp(command, "charset") == 0) {
+ had_charset = 1;
+ int last_index = -1;
+ for (;;) {
+ if (!t.next()) {
+ command = 0;
+ break;
+ }
+ char *nm = strtok(t.buf, WS);
+ if (nm == 0)
+ continue; // I dont think this should happen
+ p = strtok(0, WS);
+ if (p == 0) {
+ command = nm;
+ break;
+ }
+ if (p[0] == '"') {
+ if (last_index == -1) {
+ t.error("first charset entry is duplicate");
+ return 0;
+ }
+ if (strcmp(nm, "---") == 0) {
+ t.error("unnamed character cannot be duplicate");
+ return 0;
+ }
+ int index = name_to_index(nm);
+ if (index < 0) {
+ t.error("illegal character `%1'", nm);
+ return 0;
+ }
+ copy_entry(index, last_index);
+ }
+ else {
+ font_char_metric metric;
+ metric.height = 0;
+ metric.depth = 0;
+ metric.pre_math_space = 0;
+ metric.italic_correction = 0;
+ metric.subscript_correction = 0;
+ int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
+ &metric.width, &metric.height, &metric.depth,
+ &metric.italic_correction,
+ &metric.pre_math_space,
+ &metric.subscript_correction);
+ if (nparms < 1) {
+ t.error("bad width for `%1'", nm);
+ return 0;
+ }
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing character type for `%1'", nm);
+ return 0;
+ }
+ int type;
+ if (sscanf(p, "%d", &type) != 1) {
+ t.error("bad character type for `%1'", nm);
+ return 0;
+ }
+ if (type < 0 || type > 255) {
+ t.error("character code `%1' out of range", type);
+ return 0;
+ }
+ metric.type = type;
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing code for `%1'", nm);
+ return 0;
+ }
+ char *ptr;
+ metric.code = (int)strtol(p, &ptr, 0);
+ if (metric.code == 0 && ptr == p) {
+ t.error("bad code `%1' for character `%2'", p, nm);
+ return 0;
+ }
+
+ p = strtok(0, WS);
+ if ((p == NULL) || (strcmp(p, "--") == 0)) {
+ metric.special_device_coding = NULL;
+ } else {
+ char *name=(char *)malloc(strlen(p)+1);
+
+ if (name == NULL) {
+ fatal("malloc failed while reading character encoding");
+ }
+ strcpy(name, p);
+ metric.special_device_coding = name;
+ }
+
+ if (strcmp(nm, "---") == 0) {
+ last_index = number_to_index(metric.code);
+ add_entry(last_index, metric);
+ }
+ else {
+ last_index = name_to_index(nm);
+ if (last_index < 0) {
+ t.error("illegal character `%1'", nm);
+ return 0;
+ }
+ add_entry(last_index, metric);
+ copy_entry(number_to_index(metric.code), last_index);
+ }
+ }
+ }
+ if (last_index == -1) {
+ t.error("I didn't seem to find any characters");
+ return 0;
+ }
+ }
+ else {
+ t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
+ return 0;
+ }
+ }
+ if (!had_charset) {
+ t.error("missing charset command");
+ return 0;
+ }
+ if (space_width == 0)
+ space_width = scale_round(unitwidth, res, 72*3*sizescale);
+ compact();
+ return 1;
+}
+
+static struct {
+ const char *command;
+ int *ptr;
+} table[] = {
+ { "res", &font::res },
+ { "hor", &font::hor },
+ { "vert", &font::vert },
+ { "unitwidth", &font::unitwidth },
+ { "paperwidth", &font::paperwidth },
+ { "paperlength", &font::paperlength },
+ { "spare1", &font::biggestfont },
+ { "biggestfont", &font::biggestfont },
+ { "spare2", &font::spare2 },
+ { "sizescale", &font::sizescale }
+ };
+
+
+int font::load_desc()
+{
+ int nfonts = 0;
+ FILE *fp;
+ char *path;
+ if ((fp = open_file("DESC", &path)) == 0) {
+ error("can't find `DESC' file");
+ return 0;
+ }
+ text_file t(fp, path);
+ t.skip_comments = 1;
+ res = 0;
+ while (t.next()) {
+ char *p = strtok(t.buf, WS);
+ int found = 0;
+ int idx;
+ for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
+ if (strcmp(table[idx].command, p) == 0)
+ found = 1;
+ if (found) {
+ char *q = strtok(0, WS);
+ if (!q) {
+ t.error("missing value for command `%1'", p);
+ return 0;
+ }
+ //int *ptr = &(this->*(table[idx-1].ptr));
+ int *ptr = table[idx-1].ptr;
+ if (sscanf(q, "%d", ptr) != 1) {
+ t.error("bad number `%1'", q);
+ return 0;
+ }
+ }
+ else if (strcmp("tcommand", p) == 0) {
+ tcommand = 1;
+ }
+ else if (strcmp("pass_filenames", p) == 0) {
+ pass_filenames = 1;
+ }
+ else if (strcmp("use_charnames_in_special", p) == 0) {
+ use_charnames_in_special = 1;
+ }
+ else if (strcmp("family", p) == 0) {
+ p = strtok(0, WS);
+ if (!p) {
+ t.error("family command requires an argument");
+ return 0;
+ }
+ char *tem = new char[strlen(p)+1];
+ strcpy(tem, p);
+ family = tem;
+ }
+ else if (strcmp("fonts", p) == 0) {
+ p = strtok(0, WS);
+ if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
+ t.error("bad number of fonts `%1'", p);
+ return 0;
+ }
+ font_name_table = (const char **)new char *[nfonts+1];
+ for (int i = 0; i < nfonts; i++) {
+ p = strtok(0, WS);
+ while (p == 0) {
+ if (!t.next()) {
+ t.error("end of file while reading list of fonts");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ }
+ char *temp = new char[strlen(p)+1];
+ strcpy(temp, p);
+ font_name_table[i] = temp;
+ }
+ p = strtok(0, WS);
+ if (p != 0) {
+ t.error("font count does not match number of fonts");
+ return 0;
+ }
+ font_name_table[nfonts] = 0;
+ }
+ else if (strcmp("sizes", p) == 0) {
+ int n = 16;
+ sizes = new int[n];
+ int i = 0;
+ for (;;) {
+ p = strtok(0, WS);
+ while (p == 0) {
+ if (!t.next()) {
+ t.error("list of sizes must be terminated by `0'");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ }
+ 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:
+ t.error("bad size range `%1'", p);
+ return 0;
+ }
+ 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;
+ }
+ if (i == 1) {
+ t.error("must have some sizes");
+ return 0;
+ }
+ }
+ else if (strcmp("styles", p) == 0) {
+ int style_table_size = 5;
+ style_table = (const char **)new char *[style_table_size];
+ int j;
+ for (j = 0; j < style_table_size; j++)
+ style_table[j] = 0;
+ int i = 0;
+ for (;;) {
+ p = strtok(0, WS);
+ if (p == 0)
+ break;
+ // leave room for terminating 0
+ if (i + 1 >= style_table_size) {
+ const char **old_style_table = style_table;
+ style_table_size *= 2;
+ style_table = (const char **)new char*[style_table_size];
+ for (j = 0; j < i; j++)
+ style_table[j] = old_style_table[j];
+ for (; j < style_table_size; j++)
+ style_table[j] = 0;
+ a_delete old_style_table;
+ }
+ char *tem = new char[strlen(p) + 1];
+ strcpy(tem, p);
+ style_table[i++] = tem;
+ }
+ }
+ else if (strcmp("charset", p) == 0)
+ break;
+ else if (unknown_desc_command_handler) {
+ char *command = p;
+ p = strtok(0, "\n");
+ (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
+ }
+ }
+ if (res == 0) {
+ t.error("missing `res' command");
+ return 0;
+ }
+ if (unitwidth == 0) {
+ t.error("missing `unitwidth' command");
+ return 0;
+ }
+ if (font_name_table == 0) {
+ t.error("missing `fonts' command");
+ return 0;
+ }
+ if (sizes == 0) {
+ t.error("missing `sizes' command");
+ return 0;
+ }
+ if (sizescale < 1) {
+ t.error("bad `sizescale' value");
+ return 0;
+ }
+ if (hor < 1) {
+ t.error("bad `hor' value");
+ return 0;
+ }
+ if (vert < 1) {
+ t.error("bad `vert' value");
+ return 0;
+ }
+ return 1;
+}
+
+void font::handle_unknown_font_command(const char *, const char *,
+ const char *, int)
+{
+}
+
+FONT_COMMAND_HANDLER
+font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
+{
+ FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
+ unknown_desc_command_handler = func;
+ return prev;
+}
+
diff --git a/contrib/groff/src/libs/libgroff/fontfile.cc b/contrib/groff/src/libs/libgroff/fontfile.cc
new file mode 100644
index 000000000000..cc1ad2c4268a
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/fontfile.cc
@@ -0,0 +1,66 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "font.h"
+#include "lib.h"
+#include "searchpath.h"
+#include "device.h"
+#include "defs.h"
+
+const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
+
+static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
+
+int font::res = 0;
+int font::hor = 1;
+int font::vert = 1;
+int font::unitwidth = 0;
+int font::paperwidth = 0;
+int font::paperlength = 0;
+int font::biggestfont = 0;
+int font::spare2 = 0;
+int font::sizescale = 1;
+int font::tcommand = 0;
+int font::pass_filenames = 0;
+int font::use_charnames_in_special = 0;
+const char **font::font_name_table = 0;
+int *font::sizes = 0;
+const char *font::family = 0;
+const char **font::style_table = 0;
+FONT_COMMAND_HANDLER font::unknown_desc_command_handler = 0;
+
+void font::command_line_font_dir(const char *dir)
+{
+ font_path.command_line_dir(dir);
+}
+
+FILE *font::open_file(const char *name, char **pathp)
+{
+ char *filename = new char[strlen(name) + strlen(device) + 5];
+ sprintf(filename, "dev%s/%s", device, name);
+ FILE *fp = font_path.open_file(filename, pathp);
+ a_delete filename;
+ return fp;
+}
diff --git a/contrib/groff/src/libs/libgroff/getcwd.c b/contrib/groff/src/libs/libgroff/getcwd.c
new file mode 100644
index 000000000000..7a769ffc53f2
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/getcwd.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 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. */
+
+/* Partial emulation of getcwd in terms of getwd. */
+
+#include <sys/param.h>
+#include <string.h>
+#include <errno.h>
+
+char *getwd();
+
+char *getcwd(buf, size)
+ char *buf;
+ int size; /* POSIX says this should be size_t */
+{
+ if (size <= 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else {
+ char mybuf[MAXPATHLEN];
+ int saved_errno = errno;
+
+ errno = 0;
+ if (!getwd(mybuf)) {
+ if (errno == 0)
+ ; /* what to do? */
+ return 0;
+ }
+ errno = saved_errno;
+ if (strlen(mybuf) + 1 > size) {
+ errno = ERANGE;
+ return 0;
+ }
+ strcpy(buf, mybuf);
+ return buf;
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/getopt.c b/contrib/groff/src/libs/libgroff/getopt.c
new file mode 100644
index 000000000000..4744e43390ac
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/getopt.c
@@ -0,0 +1,1055 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000
+ Free Software Foundation, Inc.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+# ifndef const
+# define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+# define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+# include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+# ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+# else
+# define _(msgid) (msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+# include <string.h>
+# define my_index strchr
+#else
+
+# if HAVE_STRING_H
+# include <string.h>
+# else
+# include <strings.h>
+# endif
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+# ifdef text_set_element
+text_set_element (__libc_subinit, store_args_and_env);
+# endif /* text_set_element */
+
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#ifdef _LIBC
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int print_errors = opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/contrib/groff/src/libs/libgroff/getopt1.c b/contrib/groff/src/libs/libgroff/getopt1.c
new file mode 100644
index 000000000000..3d264f2db4d5
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/getopt1.c
@@ -0,0 +1,188 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/contrib/groff/src/libs/libgroff/htmlindicate.cc b/contrib/groff/src/libs/libgroff/htmlindicate.cc
new file mode 100644
index 000000000000..2e6a5d7de6ae
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/htmlindicate.cc
@@ -0,0 +1,97 @@
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk)
+
+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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nonposix.h"
+#include "stringclass.h"
+#include "html-strings.h"
+
+/*
+ * This file contains a very simple set of routines shared by
+ * tbl, pic, eqn which help the html device driver to make
+ * sensible formatting choices. Currently it simply indicates
+ * to pre-html when an image is about to be created this is then
+ * passes to pre-html.
+ * Pre-html runs troff twice, once with -Thtml and once with -Tps.
+ * troff -Thtml device driver emits a <src='image'.png> tag
+ * and the postscript device driver works out the min/max limits
+ * of the graphic region. These region limits are read by pre-html
+ * and an image is generated via troff -Tps -> gs -> png
+ */
+
+static int is_in_graphic_start = 0;
+static int is_inline_image = 0;
+
+/*
+ * html_begin_suppress - emit a start of image tag which will be seen
+ * by pre-html.
+ */
+void html_begin_suppress(int is_inline)
+{
+ if (is_inline)
+ put_string(HTML_IMAGE_INLINE_BEGIN, stdout);
+ else {
+ put_string(HTML_IMAGE_CENTERED, stdout);
+ put_string("\n", stdout);
+ }
+}
+
+/*
+ * html_end_suppress - emit an end of image tag which will be seen
+ * by pre-html.
+ */
+void html_end_suppress(int is_inline)
+{
+ if (is_inline)
+ put_string(HTML_IMAGE_INLINE_END, stdout);
+ else {
+ put_string(HTML_IMAGE_END, stdout);
+ put_string("\n", stdout);
+ }
+}
+
+/*
+ * graphic_start - The boolean, is_inline, should be:
+ *
+ * FALSE if this is called via EQ, TS, PS, and
+ * TRUE if issued via delim $$ $ x over y $ etc.
+ */
+void graphic_start(int is_inline)
+{
+ if (!is_in_graphic_start) {
+ html_begin_suppress(is_inline);
+ is_inline_image = is_inline;
+ is_in_graphic_start = 1;
+ }
+}
+
+/*
+ * graphic_end - tell troff that the image region is ending.
+ */
+
+void graphic_end()
+{
+ if (is_in_graphic_start) {
+ html_end_suppress(is_inline_image);
+ is_in_graphic_start = 0;
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/iftoa.c b/contrib/groff/src/libs/libgroff/iftoa.c
new file mode 100644
index 000000000000..29a3d894cb4a
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/iftoa.c
@@ -0,0 +1,65 @@
+/* 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. */
+
+#define INT_DIGITS 19 /* enough for 64-bit integer */
+
+char *if_to_a(i, decimal_point)
+ int i, decimal_point;
+{
+ /* room for a -, INT_DIGITS digits, a decimal point, and a terminating '\0' */
+ static char buf[INT_DIGITS + 3];
+ char *p = buf + INT_DIGITS + 2;
+ int point = 0;
+ buf[INT_DIGITS + 2] = '\0';
+ /* assert(decimal_point <= INT_DIGITS); */
+ if (i >= 0) {
+ do {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ if (++point == decimal_point)
+ *--p = '.';
+ } while (i != 0 || point < decimal_point);
+ }
+ else { /* i < 0 */
+ do {
+ *--p = '0' - (i % 10);
+ i /= 10;
+ if (++point == decimal_point)
+ *--p = '.';
+ } while (i != 0 || point < decimal_point);
+ *--p = '-';
+ }
+ if (decimal_point > 0) {
+ char *q;
+ /* there must be a dot, so this will terminate */
+ for (q = buf + INT_DIGITS + 2; q[-1] == '0'; --q)
+ ;
+ if (q[-1] == '.') {
+ if (q - 1 == p) {
+ q[-1] = '0';
+ q[0] = '\0';
+ }
+ else
+ q[-1] = '\0';
+ }
+ else
+ *q = '\0';
+ }
+ return p;
+}
diff --git a/contrib/groff/src/libs/libgroff/itoa.c b/contrib/groff/src/libs/libgroff/itoa.c
new file mode 100644
index 000000000000..72826b748f7e
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/itoa.c
@@ -0,0 +1,43 @@
+/* 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. */
+
+#define INT_DIGITS 19 /* enough for 64 bit integer */
+
+char *i_to_a(i)
+ int i;
+{
+ /* Room for INT_DIGITS digits, - and '\0' */
+ static char buf[INT_DIGITS + 2];
+ char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */
+ if (i >= 0) {
+ do {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ } while (i != 0);
+ return p;
+ }
+ else { /* i < 0 */
+ do {
+ *--p = '0' - (i % 10);
+ i /= 10;
+ } while (i != 0);
+ *--p = '-';
+ }
+ return p;
+}
diff --git a/contrib/groff/src/libs/libgroff/lf.cc b/contrib/groff/src/libs/libgroff/lf.cc
new file mode 100644
index 000000000000..34272c7d03d0
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/lf.cc
@@ -0,0 +1,62 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <string.h>
+#include <ctype.h>
+#include "cset.h"
+#include "stringclass.h"
+
+extern void change_filename(const char *);
+extern void change_lineno(int);
+
+int interpret_lf_args(const char *p)
+{
+ while (*p == ' ')
+ p++;
+ if (!csdigit(*p))
+ return 0;
+ int ln = 0;
+ do {
+ ln *= 10;
+ ln += *p++ - '0';
+ } while (csdigit(*p));
+ if (*p != ' ' && *p != '\n' && *p != '\0')
+ return 0;
+ while (*p == ' ')
+ p++;
+ if (*p == '\0' || *p == '\n') {
+ change_lineno(ln);
+ return 1;
+ }
+ const char *q;
+ for (q = p;
+ *q != '\0' && *q != ' ' && *q != '\n' && *q != '\\';
+ q++)
+ ;
+ string tem(p, q - p);
+ while (*q == ' ')
+ q++;
+ if (*q != '\n' && *q != '\0')
+ return 0;
+ tem += '\0';
+ change_filename(tem.contents());
+ change_lineno(ln);
+ return 1;
+}
diff --git a/contrib/groff/src/libs/libgroff/lineno.cc b/contrib/groff/src/libs/libgroff/lineno.cc
new file mode 100644
index 000000000000..f7138dba0210
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/lineno.cc
@@ -0,0 +1 @@
+int current_lineno = 0;
diff --git a/contrib/groff/src/libs/libgroff/macropath.cc b/contrib/groff/src/libs/libgroff/macropath.cc
new file mode 100644
index 000000000000..03c04cbafd79
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/macropath.cc
@@ -0,0 +1,30 @@
+// -*- 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. */
+
+#include "lib.h"
+#include "searchpath.h"
+#include "macropath.h"
+#include "defs.h"
+
+#define MACROPATH_ENVVAR "GROFF_TMAC_PATH"
+
+search_path macro_path(MACROPATH_ENVVAR, MACROPATH, 1, 1);
+search_path safer_macro_path(MACROPATH_ENVVAR, MACROPATH, 1, 0);
+search_path config_macro_path(MACROPATH_ENVVAR, MACROPATH, 0, 0);
diff --git a/contrib/groff/src/libs/libgroff/matherr.c b/contrib/groff/src/libs/libgroff/matherr.c
new file mode 100644
index 000000000000..b0097b8b5559
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/matherr.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <math.h>
+#include <errno.h>
+
+#ifdef HAVE_STRUCT_EXCEPTION
+#ifdef TLOSS
+
+int matherr(exc)
+struct exception *exc;
+{
+ switch (exc->type) {
+ case SING:
+ case DOMAIN:
+ errno = EDOM;
+ break;
+ case OVERFLOW:
+ case UNDERFLOW:
+ case TLOSS:
+ case PLOSS:
+ errno = ERANGE;
+ break;
+ }
+ return 1;
+}
+
+#endif /* TLOSS */
+#endif /* HAVE_STRUCT_EXCEPTION */
diff --git a/contrib/groff/src/libs/libgroff/nametoindex.cc b/contrib/groff/src/libs/libgroff/nametoindex.cc
new file mode 100644
index 000000000000..578ff349d443
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/nametoindex.cc
@@ -0,0 +1,118 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "font.h"
+#include "ptable.h"
+
+declare_ptable(int)
+implement_ptable(int)
+
+class character_indexer {
+public:
+ character_indexer();
+ ~character_indexer();
+ int ascii_char_index(unsigned char);
+ int named_char_index(const char *);
+ int numbered_char_index(int);
+private:
+ enum { NSMALL = 256 };
+ int next_index;
+ int ascii_index[256];
+ int small_number_index[NSMALL];
+ PTABLE(int) table;
+ int lookup_char(const char *, int);
+};
+
+character_indexer::character_indexer()
+: next_index(0)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ ascii_index[i] = -1;
+ for (i = 0; i < NSMALL; i++)
+ small_number_index[i] = -1;
+}
+
+character_indexer::~character_indexer()
+{
+}
+
+int character_indexer::ascii_char_index(unsigned char c)
+{
+ if (ascii_index[c] < 0)
+ ascii_index[c] = next_index++;
+ return ascii_index[c];
+}
+
+int character_indexer::numbered_char_index(int n)
+{
+ if (n >= 0 && n < NSMALL) {
+ if (small_number_index[n] < 0)
+ small_number_index[n] = next_index++;
+ return small_number_index[n];
+ }
+ // Not the most efficient possible implementation.
+ char buf[INT_DIGITS + 3];
+ buf[0] = ' ';
+ strcpy(buf + 1, i_to_a(n));
+ return named_char_index(buf);
+}
+
+int character_indexer::named_char_index(const char *s)
+{
+ int *np = table.lookup(s);
+ if (!np) {
+ np = new int;
+ *np = next_index++;
+ table.define(s, np);
+ }
+ return *np;
+}
+
+static character_indexer indexer;
+
+int font::number_to_index(int n)
+{
+ return indexer.numbered_char_index(n);
+}
+
+int font::name_to_index(const char *s)
+{
+ assert(s != 0 && s[0] != '\0' && s[0] != ' ');
+ if (s[1] == '\0')
+ return indexer.ascii_char_index(s[0]);
+ /* char128 and \200 are synonyms */
+ if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
+ char *res;
+ long n = strtol(s + 4, &res, 10);
+ if (res != s + 4 && *res == '\0' && n >= 0 && n < 256)
+ return indexer.ascii_char_index((unsigned char)n);
+ }
+ return indexer.named_char_index(s);
+}
+
diff --git a/contrib/groff/src/libs/libgroff/new.cc b/contrib/groff/src/libs/libgroff/new.cc
new file mode 100644
index 000000000000..8d98591a64af
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/new.cc
@@ -0,0 +1,68 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "posix.h"
+#include "nonposix.h"
+
+extern const char *program_name;
+
+static void ewrite(const char *s)
+{
+ write(2, s, strlen(s));
+}
+
+void *operator new(size_t size)
+{
+ // Avoid relying on the behaviour of malloc(0).
+ if (size == 0)
+ size++;
+#ifdef COOKIE_BUG
+ char *p = (char *)malloc(unsigned(size + 8));
+#else /* not COOKIE_BUG */
+ char *p = (char *)malloc(unsigned(size));
+#endif /* not COOKIE_BUG */
+ if (p == 0) {
+ if (program_name) {
+ ewrite(program_name);
+ ewrite(": ");
+ }
+ ewrite("out of memory\n");
+ _exit(-1);
+ }
+#ifdef COOKIE_BUG
+ ((unsigned *)p)[1] = 0;
+ return p + 8;
+#else /* not COOKIE_BUG */
+ return p;
+#endif /* not COOKIE_BUG */
+}
+
+#ifdef COOKIE_BUG
+
+void operator delete(void *p)
+{
+ if (p)
+ free((void *)((char *)p - 8));
+}
+
+#endif /* COOKIE_BUG */
diff --git a/contrib/groff/src/libs/libgroff/prime.cc b/contrib/groff/src/libs/libgroff/prime.cc
new file mode 100644
index 000000000000..f0b1eadcc68b
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/prime.cc
@@ -0,0 +1,26 @@
+#include <math.h>
+
+int is_prime(unsigned n)
+{
+ if (n <= 3)
+ return 1;
+ if (!(n & 1))
+ return 0;
+ if (n % 3 == 0)
+ return 0;
+ unsigned lim = unsigned(sqrt((double)n));
+ unsigned d = 5;
+ for (;;) {
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return 0;
+ d += 2;
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return 0;
+ d += 4;
+ }
+ return 1;
+}
diff --git a/contrib/groff/src/libs/libgroff/progname.cc b/contrib/groff/src/libs/libgroff/progname.cc
new file mode 100644
index 000000000000..a70e3419c2be
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/progname.cc
@@ -0,0 +1 @@
+const char *program_name = 0;
diff --git a/contrib/groff/src/libs/libgroff/ptable.cc b/contrib/groff/src/libs/libgroff/ptable.cc
new file mode 100644
index 000000000000..76735c2426de
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/ptable.cc
@@ -0,0 +1,52 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "ptable.h"
+#include "errarg.h"
+#include "error.h"
+
+unsigned long hash_string(const char *s)
+{
+ assert(s != 0);
+ unsigned long h = 0, g;
+ while (*s != 0) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ return h;
+}
+
+static const unsigned table_sizes[] = {
+101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009,
+80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009,
+16000057, 32000011, 64000031, 128000003, 0
+};
+
+unsigned next_ptable_size(unsigned n)
+{
+ const unsigned *p;
+ for (p = table_sizes; *p <= n; p++)
+ if (*p == 0)
+ fatal("cannot expand table");
+ return *p;
+}
diff --git a/contrib/groff/src/libs/libgroff/putenv.c b/contrib/groff/src/libs/libgroff/putenv.c
new file mode 100644
index 000000000000..c1ca671ae89f
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/putenv.c
@@ -0,0 +1,95 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* Hacked slightly by jjc@jclark.com for groff. */
+
+#include <string.h>
+
+#ifdef __STDC__
+#include <stddef.h>
+typedef void *PTR;
+typedef size_t SIZE_T;
+#else /* not __STDC__ */
+typedef char *PTR;
+typedef int SIZE_T;
+#endif /* not __STDC__ */
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else /* not HAVE_STDLIB_H */
+PTR malloc();
+#endif /* not HAVE_STDLIB_H */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+extern char **environ;
+
+/* Put STRING, which is of the form "NAME=VALUE", in the environment. */
+
+int putenv(const char *string)
+{
+ char *name_end = strchr(string, '=');
+ SIZE_T size;
+ char **ep;
+
+ if (name_end == NULL)
+ {
+ /* Remove the variable from the environment. */
+ size = strlen(string);
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp(*ep, string, size) && (*ep)[size] == '=')
+ {
+ while (ep[1] != NULL)
+ {
+ ep[0] = ep[1];
+ ++ep;
+ }
+ *ep = NULL;
+ return 0;
+ }
+ }
+
+ size = 0;
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp(*ep, string, name_end - string)
+ && (*ep)[name_end - string] == '=')
+ break;
+ else
+ ++size;
+
+ if (*ep == NULL)
+ {
+ static char **last_environ = NULL;
+ char **new_environ = (char **) malloc((size + 2) * sizeof(char *));
+ if (new_environ == NULL)
+ return -1;
+ (void) memcpy((PTR) new_environ, (PTR) environ, size * sizeof(char *));
+ new_environ[size] = (char *) string;
+ new_environ[size + 1] = NULL;
+ if (last_environ != NULL)
+ free((PTR) last_environ);
+ last_environ = new_environ;
+ environ = new_environ;
+ }
+ else
+ *ep = (char *) string;
+
+ return 0;
+}
diff --git a/contrib/groff/src/libs/libgroff/searchpath.cc b/contrib/groff/src/libs/libgroff/searchpath.cc
new file mode 100644
index 000000000000..f4e2b90d3151
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/searchpath.cc
@@ -0,0 +1,132 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "lib.h"
+#include "searchpath.h"
+#include "nonposix.h"
+
+search_path::search_path(const char *envvar, const char *standard,
+ int add_home, int add_current)
+{
+ char *home = 0;
+ if (add_home)
+ home = getenv("HOME");
+ char *e = 0;
+ if (envvar)
+ e = getenv(envvar);
+ dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
+ + (add_current ? 1 + 1 : 0)
+ + ((home && *home) ? strlen(home) + 1 : 0)
+ + ((standard && *standard) ? strlen(standard) : 0)
+ + 1];
+ *dirs = '\0';
+ if (e && *e) {
+ strcat(dirs, e);
+ strcat(dirs, PATH_SEP);
+ }
+ if (add_current) {
+ strcat(dirs, ".");
+ strcat(dirs, PATH_SEP);
+ }
+ if (home && *home) {
+ strcat(dirs, home);
+ strcat(dirs, PATH_SEP);
+ }
+ if (standard && *standard)
+ strcat(dirs, standard);
+ init_len = strlen(dirs);
+}
+
+search_path::~search_path()
+{
+ // dirs is always allocated
+ a_delete dirs;
+}
+
+void search_path::command_line_dir(const char *s)
+{
+ char *old = dirs;
+ unsigned old_len = strlen(old);
+ unsigned slen = strlen(s);
+ dirs = new char[old_len + 1 + slen + 1];
+ memcpy(dirs, old, old_len - init_len);
+ char *p = dirs;
+ p += old_len - init_len;
+ if (init_len == 0)
+ *p++ = PATH_SEP[0];
+ memcpy(p, s, slen);
+ p += slen;
+ if (init_len > 0) {
+ *p++ = PATH_SEP[0];
+ memcpy(p, old + old_len - init_len, init_len);
+ p += init_len;
+ }
+ *p++ = '\0';
+ a_delete old;
+}
+
+FILE *search_path::open_file(const char *name, char **pathp)
+{
+ assert(name != 0);
+ if (IS_ABSOLUTE(name) || *dirs == '\0') {
+ FILE *fp = fopen(name, "r");
+ if (fp) {
+ if (pathp)
+ *pathp = strsave(name);
+ return fp;
+ }
+ else
+ return 0;
+ }
+ unsigned namelen = strlen(name);
+ char *p = dirs;
+ for (;;) {
+ char *end = strchr(p, PATH_SEP[0]);
+ if (!end)
+ end = strchr(p, '\0');
+ int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
+ char *path = new char[(end - p) + need_slash + namelen + 1];
+ memcpy(path, p, end - p);
+ if (need_slash)
+ path[end - p] = '/';
+ strcpy(path + (end - p) + need_slash, name);
+#if 0
+ fprintf(stderr, "trying `%s'\n", path);
+#endif
+ FILE *fp = fopen(path, "r");
+ if (fp) {
+ if (pathp)
+ *pathp = path;
+ else
+ a_delete path;
+ return fp;
+ }
+ a_delete path;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ return 0;
+}
diff --git a/contrib/groff/src/libs/libgroff/strerror.c b/contrib/groff/src/libs/libgroff/strerror.c
new file mode 100644
index 000000000000..69089f13235f
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strerror.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+
+#define INT_DIGITS 19 /* enough for 64 bit integer */
+
+#ifndef HAVE_SYS_NERR
+extern int sys_nerr;
+#endif
+#ifndef HAVE_SYS_ERRLIST
+extern char *sys_errlist[];
+#endif
+
+char *strerror(n)
+ int n;
+{
+ static char buf[sizeof("Error ") + 1 + INT_DIGITS];
+ if (n >= 0 && n < sys_nerr && sys_errlist[n] != 0)
+ return sys_errlist[n];
+ else {
+ sprintf(buf, "Error %d", n);
+ return buf;
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/string.cc b/contrib/groff/src/libs/libgroff/string.cc
new file mode 100644
index 000000000000..4bcd4cccb047
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/string.cc
@@ -0,0 +1,311 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include "stringclass.h"
+#include "lib.h"
+
+static char *salloc(int len, int *sizep);
+static void sfree(char *ptr, int size);
+static char *sfree_alloc(char *ptr, int size, int len, int *sizep);
+static char *srealloc(char *ptr, int size, int oldlen, int newlen, int *sizep);
+
+static char *salloc(int len, int *sizep)
+{
+ if (len == 0) {
+ *sizep = 0;
+ return 0;
+ }
+ else
+ return new char[*sizep = len*2];
+}
+
+static void sfree(char *ptr, int)
+{
+ a_delete ptr;
+}
+
+static char *sfree_alloc(char *ptr, int oldsz, int len, int *sizep)
+{
+ if (oldsz >= len) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ a_delete ptr;
+ if (len == 0) {
+ *sizep = 0;
+ return 0;
+ }
+ else
+ return new char[*sizep = len*2];
+}
+
+static char *srealloc(char *ptr, int oldsz, int oldlen, int newlen, int *sizep)
+{
+ if (oldsz >= newlen) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ if (newlen == 0) {
+ a_delete ptr;
+ *sizep = 0;
+ return 0;
+ }
+ else {
+ char *p = new char[*sizep = newlen*2];
+ if (oldlen < newlen && oldlen != 0)
+ memcpy(p, ptr, oldlen);
+ a_delete ptr;
+ return p;
+ }
+}
+
+string::string() : ptr(0), len(0), sz(0)
+{
+}
+
+string::string(const char *p, int n) : len(n)
+{
+ assert(n >= 0);
+ ptr = salloc(n, &sz);
+ if (n != 0)
+ memcpy(ptr, p, n);
+}
+
+string::string(const char *p)
+{
+ if (p == 0) {
+ len = 0;
+ ptr = 0;
+ sz = 0;
+ }
+ else {
+ len = strlen(p);
+ ptr = salloc(len, &sz);
+ memcpy(ptr, p, len);
+ }
+}
+
+string::string(char c) : len(1)
+{
+ ptr = salloc(1, &sz);
+ *ptr = c;
+}
+
+string::string(const string &s) : len(s.len)
+{
+ ptr = salloc(len, &sz);
+ if (len != 0)
+ memcpy(ptr, s.ptr, len);
+}
+
+string::~string()
+{
+ sfree(ptr, sz);
+}
+
+string &string::operator=(const string &s)
+{
+ ptr = sfree_alloc(ptr, sz, s.len, &sz);
+ len = s.len;
+ if (len != 0)
+ memcpy(ptr, s.ptr, len);
+ return *this;
+}
+
+string &string::operator=(const char *p)
+{
+ if (p == 0) {
+ sfree(ptr, len);
+ len = 0;
+ ptr = 0;
+ sz = 0;
+ }
+ else {
+ int slen = strlen(p);
+ ptr = sfree_alloc(ptr, sz, slen, &sz);
+ len = slen;
+ memcpy(ptr, p, len);
+ }
+ return *this;
+}
+
+string &string::operator=(char c)
+{
+ ptr = sfree_alloc(ptr, sz, 1, &sz);
+ len = 1;
+ *ptr = c;
+ return *this;
+}
+
+void string::move(string &s)
+{
+ sfree(ptr, sz);
+ ptr = s.ptr;
+ len = s.len;
+ sz = s.sz;
+ s.ptr = 0;
+ s.len = 0;
+ s.sz = 0;
+}
+
+void string::grow1()
+{
+ ptr = srealloc(ptr, sz, len, len + 1, &sz);
+}
+
+string &string::operator+=(const char *p)
+{
+ if (p != 0) {
+ int n = strlen(p);
+ int newlen = len + n;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, p, n);
+ len = newlen;
+ }
+ return *this;
+}
+
+string &string::operator+=(const string &s)
+{
+ if (s.len != 0) {
+ int newlen = len + s.len;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, s.ptr, s.len);
+ len = newlen;
+ }
+ return *this;
+}
+
+void string::append(const char *p, int n)
+{
+ if (n > 0) {
+ int newlen = len + n;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, p, n);
+ len = newlen;
+ }
+}
+
+string::string(const char *s1, int n1, const char *s2, int n2)
+{
+ assert(n1 >= 0 && n2 >= 0);
+ len = n1 + n2;
+ if (len == 0) {
+ sz = 0;
+ ptr = 0;
+ }
+ else {
+ ptr = salloc(len, &sz);
+ if (n1 == 0)
+ memcpy(ptr, s2, n2);
+ else {
+ memcpy(ptr, s1, n1);
+ if (n2 != 0)
+ memcpy(ptr + n1, s2, n2);
+ }
+ }
+}
+
+int operator<=(const string &s1, const string &s2)
+{
+ return (s1.len <= s2.len
+ ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
+ : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
+}
+
+int operator<(const string &s1, const string &s2)
+{
+ return (s1.len < s2.len
+ ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
+ : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
+}
+
+int operator>=(const string &s1, const string &s2)
+{
+ return (s1.len >= s2.len
+ ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
+ : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
+}
+
+int operator>(const string &s1, const string &s2)
+{
+ return (s1.len > s2.len
+ ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
+ : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
+}
+
+void string::set_length(int i)
+{
+ assert(i >= 0);
+ if (i > sz)
+ ptr = srealloc(ptr, sz, len, i, &sz);
+ len = i;
+}
+
+void string::clear()
+{
+ len = 0;
+}
+
+int string::search(char c) const
+{
+ char *p = ptr ? (char *)memchr(ptr, c, len) : NULL;
+ return p ? p - ptr : -1;
+}
+
+// we silently strip nuls
+
+char *string::extract() const
+{
+ char *p = ptr;
+ int n = len;
+ int nnuls = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ if (p[i] == '\0')
+ nnuls++;
+ char *q = new char[n + 1 - nnuls];
+ char *r = q;
+ for (i = 0; i < n; i++)
+ if (p[i] != '\0')
+ *r++ = p[i];
+ q[n] = '\0';
+ return q;
+}
+
+void put_string(const string &s, FILE *fp)
+{
+ int len = s.length();
+ const char *ptr = s.contents();
+ for (int i = 0; i < len; i++)
+ putc(ptr[i], fp);
+}
+
+string as_string(int i)
+{
+ static char buf[INT_DIGITS + 2];
+ sprintf(buf, "%d", i);
+ return string(buf);
+}
+
diff --git a/contrib/groff/src/libs/libgroff/strsave.cc b/contrib/groff/src/libs/libgroff/strsave.cc
new file mode 100644
index 000000000000..dfd2b6f40743
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strsave.cc
@@ -0,0 +1,31 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <string.h>
+
+char *strsave(const char *s)
+{
+ if (s == 0)
+ return 0;
+ char *p = new char[strlen(s) + 1];
+ strcpy(p, s);
+ return p;
+}
+
diff --git a/contrib/groff/src/libs/libgroff/strtol.c b/contrib/groff/src/libs/libgroff/strtol.c
new file mode 100644
index 000000000000..61ce70e98775
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strtol.c
@@ -0,0 +1,128 @@
+/* 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. */
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#ifndef LONG_MIN
+#define LONG_MIN (-LONG_MAX-1)
+#endif
+
+#ifdef isascii
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+long strtol(str, ptr, base)
+ char *str, **ptr;
+ int base;
+{
+ char *start = str;
+ int neg = 0;
+ long val;
+ char *p;
+ static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ while (ISASCII((unsigned char)*str) && isspace((unsigned char)*str))
+ str++;
+
+ if (*str == '-') {
+ neg = 1;
+ str++;
+ }
+ if (base == 0) {
+ if (*str == '0') {
+ if (str[1] == 'x' || str[1] == 'X') {
+ str += 2;
+ base = 16;
+ }
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ }
+ if (base < 2 || base > 36)
+ base = 10;
+ else if (base == 16 && *str == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+
+ p = strchr(digits, (ISASCII((unsigned char)*str)
+ && isupper((unsigned char)*str)
+ ? tolower((unsigned char)*str)
+ : *str));
+ if (p == 0 || (val = (p - digits)) >= base) {
+ if (base == 16 && str > start && (str[-1] == 'x' || str[-1] == 'X')) {
+ if (ptr)
+ *ptr = str - 1;
+ }
+ else {
+ if (ptr)
+ *ptr = start;
+ errno = ERANGE;
+ }
+ return 0;
+ }
+ if (neg)
+ val = -val;
+
+ while (*++str != '\0') {
+ int n;
+
+ p = strchr(digits, (ISASCII((unsigned char)*str)
+ && isupper((unsigned char)*str)
+ ? tolower((unsigned char)*str) : *str));
+ if (p == 0)
+ break;
+ n = p - digits;
+ if (n >= base)
+ break;
+ if (neg) {
+ if (-(unsigned long)val > (-(unsigned long)LONG_MIN - n)/base) {
+ val = LONG_MIN;
+ errno = ERANGE;
+ }
+ else
+ val = val*base - n;
+ }
+ else {
+ if (val > (LONG_MAX - n)/base) {
+ val = LONG_MAX;
+ errno = ERANGE;
+ }
+ else
+ val = val*base + n;
+ }
+ }
+
+ if (ptr)
+ *ptr = str;
+
+ return val;
+}
diff --git a/contrib/groff/src/libs/libgroff/tmpfile.cc b/contrib/groff/src/libs/libgroff/tmpfile.cc
new file mode 100644
index 000000000000..a6c20101667f
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/tmpfile.cc
@@ -0,0 +1,186 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "posix.h"
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "nonposix.h"
+
+extern "C" {
+ // Solaris 2.5.1 has these functions,
+ // but its stdlib.h fails to declare them.
+ char *mktemp(char *);
+ int mkstemp(char *);
+}
+
+// If this is set, create temporary files there
+#define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
+// otherwise if this is set, create temporary files there
+#define TMPDIR_ENVVAR "TMPDIR"
+// otherwise if P_tmpdir is defined, create temporary files there
+#ifdef P_tmpdir
+# define DEFAULT_TMPDIR P_tmpdir
+#else
+// otherwise create temporary files here.
+# define DEFAULT_TMPDIR "/tmp"
+#endif
+// Use this as the prefix for temporary filenames.
+#define TMPFILE_PREFIX "groff"
+
+/*
+ * Generate a temporary name template with a postfix
+ * immediately after the TMPFILE_PREFIX.
+ * It uses the groff preferences for a temporary directory.
+ * Note that no file name is either created or opened,
+ * only the *template* is returned.
+ */
+
+char *xtmptemplate(char *postfix)
+{
+ const char *dir = getenv(GROFF_TMPDIR_ENVVAR);
+ int postlen = 0;
+
+ if (postfix)
+ postlen = strlen(postfix);
+
+ if (!dir) {
+ dir = getenv(TMPDIR_ENVVAR);
+ if (!dir)
+ dir = DEFAULT_TMPDIR;
+ }
+
+ size_t dir_len = strlen(dir);
+ const char *dir_end = dir + dir_len - 1;
+ int needs_slash = strchr(DIR_SEPS, *dir_end) == NULL;
+ char *templ = new char[strlen(dir) + needs_slash
+ + sizeof(TMPFILE_PREFIX) - 1 + 6 + 1 + postlen];
+ strcpy(templ, dir);
+ if (needs_slash)
+ strcat(templ, "/");
+ strcat(templ, TMPFILE_PREFIX);
+ if (postlen > 0)
+ strcat(templ, postfix);
+ strcat(templ, "XXXXXX");
+
+ return( templ );
+}
+
+// The trick with unlinking the temporary file while it is still in
+// use is not portable, it will fail on MS-DOS and most MS-Windows
+// filesystems. So it cannot be used on non-Posix systems.
+// Instead, we maintain a list of files to be deleted on exit, and
+// register an atexit function that will remove them all in one go.
+// This should be portable to all platforms.
+
+static struct xtmpfile_list {
+ struct xtmpfile_list *next;
+ char fname[1];
+} *xtmpfiles_to_delete;
+
+static void remove_tmp_files()
+{
+ struct xtmpfile_list *p = xtmpfiles_to_delete;
+
+ while (p)
+ {
+ if (unlink(p->fname) < 0)
+ error("cannot unlink `%1': %2", p->fname, strerror(errno));
+ struct xtmpfile_list *old = p;
+ p = p->next;
+ free(old);
+ }
+}
+
+static void add_tmp_file(const char *name)
+{
+ if (xtmpfiles_to_delete == NULL)
+ atexit(remove_tmp_files);
+
+ struct xtmpfile_list *p
+ = (struct xtmpfile_list *)malloc(sizeof(struct xtmpfile_list)
+ + strlen (name));
+ if (p == NULL)
+ {
+ error("cannot unlink `%1': %2", name, strerror(errno));
+ return;
+ }
+ p->next = xtmpfiles_to_delete;
+ strcpy(p->fname, name);
+ xtmpfiles_to_delete = p;
+}
+
+// Open a temporary file and with fatal error on failure.
+
+#ifndef _MSC_VER
+
+FILE *xtmpfile(char **namep, char *postfix, int do_unlink)
+{
+ char *templ = xtmptemplate(postfix);
+
+#ifdef HAVE_MKSTEMP
+ errno = 0;
+ int fd = mkstemp(templ);
+ if (fd < 0)
+ fatal("cannot create temporary file: %1", strerror(errno));
+ errno = 0;
+ FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O
+ if (!fp)
+ fatal("fdopen: %1", strerror(errno));
+#else /* not HAVE_MKSTEMP */
+ if (!mktemp(templ) || !templ[0])
+ fatal("cannot create file name for temporary file");
+ errno = 0;
+ FILE *fp = fopen(templ, FOPEN_RWB);
+ if (!fp)
+ fatal("cannot open `%1': %2", templ, strerror(errno));
+#endif /* not HAVE_MKSTEMP */
+ if (do_unlink)
+ add_tmp_file(templ);
+ if ((namep != 0) && ((*namep) != 0)) {
+ *namep = templ;
+ } else {
+ a_delete templ;
+ }
+ return fp;
+}
+
+#else
+
+// FIXME: does MSVC have mktemp or mkstemp? If so, it should now
+// use the version above, as it no longer removes an open file.
+// The version below will NOT work with grohtml, since grohtml
+// wants to know the name of the file opened by xtmpfile!!
+
+// If you're not running Unix, the following will do:
+FILE *xtmpfile(char **namep, char *postfix, int do_unlink)
+{
+ FILE *fp = tmpfile();
+ if (!fp)
+ fatal("couldn't create temporary file");
+ return fp;
+}
+
+#endif /* _MSC_VER */
diff --git a/contrib/groff/src/preproc/eqn/Makefile.sub b/contrib/groff/src/preproc/eqn/Makefile.sub
new file mode 100644
index 000000000000..20421e1c4958
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/Makefile.sub
@@ -0,0 +1,59 @@
+PROG=eqn
+MAN1=eqn.n neqn.n
+XLIBS=$(LIBGROFF)
+OBJS=\
+ eqn.o \
+ main.o \
+ lex.o \
+ box.o \
+ limit.o \
+ list.o \
+ over.o \
+ text.o \
+ script.o \
+ mark.o \
+ other.o \
+ delim.o \
+ sqrt.o \
+ pile.o \
+ special.o
+CCSRCS=\
+ $(srcdir)/main.cc \
+ $(srcdir)/lex.cc \
+ $(srcdir)/box.cc \
+ $(srcdir)/limit.cc \
+ $(srcdir)/list.cc \
+ $(srcdir)/over.cc \
+ $(srcdir)/text.cc \
+ $(srcdir)/script.cc \
+ $(srcdir)/mark.cc \
+ $(srcdir)/other.cc \
+ $(srcdir)/delim.cc \
+ $(srcdir)/sqrt.cc \
+ $(srcdir)/pile.cc \
+ $(srcdir)/special.cc
+HDRS=\
+ $(srcdir)/box.h \
+ $(srcdir)/eqn.h \
+ $(srcdir)/pbox.h
+GRAM=$(srcdir)/eqn.y
+YTABC=$(srcdir)/eqn.cc
+YTABH=$(srcdir)/eqn_tab.h
+NAMEPREFIX=$(g)
+CLEANADD=neqn
+
+all: neqn
+
+neqn: neqn.sh
+ -rm -f $@
+ sed -e 's/@g@/$(g)/g' \
+ -e 's|@BINDIR@|$(bindir)|g' \
+ -e $(SH_SCRIPT_SED_CMD) $(srcdir)/neqn.sh >$@
+ chmod +x $@
+
+install_data: neqn
+ -rm -f $(bindir)/$(NAMEPREFIX)neqn
+ $(INSTALL_SCRIPT) neqn $(bindir)/$(NAMEPREFIX)neqn
+
+uninstall_sub:
+ -rm -f $(bindir)/$(NAMEPREFIX)neqn
diff --git a/contrib/groff/src/preproc/eqn/TODO b/contrib/groff/src/preproc/eqn/TODO
new file mode 100644
index 000000000000..210d0ab06e4d
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/TODO
@@ -0,0 +1,49 @@
+Use the same size increases for sum prod int as eqn does.
+
+Perhaps chartype should be renamed.
+
+TeX makes {sub,super}script on a single character with an accent
+into an accent onto the (character with the script). Should we do this?
+
+Implement mark and lineups within scripts, matrices and piles, and accents.
+(Why would this be useful?)
+
+Perhaps push hmotions down through lists to avoid upsetting spacing
+adjustments.
+
+Possibly generate .lf commands during compute_metrics phase.
+
+Consider whether there should be extra space at the side of piles.
+
+Provide scriptstyle displaystyle etc.
+
+Provide a nicer matrix syntax, eg
+matrix ccc {
+a then b then c above
+e then f then g above
+h then i then k
+}
+
+Perhaps generate syntax error messages using the style of gpic.
+
+Wide accents.
+
+More use of \Z.
+
+Extensible square roots.
+
+Vphantom
+
+Smash.
+
+Provide a variant of vec that extends over the length of the accentee.
+
+Support vertical arrow delimiters.
+
+Make the following work:
+.EQ
+delim @@
+.EN
+.EQ @<-@
+some equation
+.EN
diff --git a/contrib/groff/src/preproc/eqn/box.cc b/contrib/groff/src/preproc/eqn/box.cc
new file mode 100644
index 000000000000..4e61b5dc3b74
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/box.cc
@@ -0,0 +1,611 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+const char *current_roman_font;
+
+char *gfont = 0;
+char *grfont = 0;
+char *gbfont = 0;
+int gsize = 0;
+
+int script_size_reduction = -1; // negative means reduce by a percentage
+
+int positive_space = -1;
+int negative_space = -1;
+
+int minimum_size = 5;
+
+int fat_offset = 4;
+int body_height = 85;
+int body_depth = 35;
+
+int over_hang = 0;
+int accent_width = 31;
+int delimiter_factor = 900;
+int delimiter_shortfall = 50;
+
+int null_delimiter_space = 12;
+int script_space = 5;
+int thin_space = 17;
+int medium_space = 22;
+int thick_space = 28;
+
+int num1 = 70;
+int num2 = 40;
+// we don't use num3, because we don't have \atop
+int denom1 = 70;
+int denom2 = 36;
+int axis_height = 26; // in 100ths of an em
+int sup1 = 42;
+int sup2 = 37;
+int sup3 = 28;
+int default_rule_thickness = 4;
+int sub1 = 20;
+int sub2 = 23;
+int sup_drop = 38;
+int sub_drop = 5;
+int x_height = 45;
+int big_op_spacing1 = 11;
+int big_op_spacing2 = 17;
+int big_op_spacing3 = 20;
+int big_op_spacing4 = 60;
+int big_op_spacing5 = 10;
+
+// These are for piles and matrices.
+
+int baseline_sep = 140; // = num1 + denom1
+int shift_down = 26; // = axis_height
+int column_sep = 100; // = em space
+int matrix_side_sep = 17; // = thin space
+
+int nroff = 0; // should we grok ndefine or tdefine?
+
+struct {
+ const char *name;
+ int *ptr;
+} param_table[] = {
+ { "fat_offset", &fat_offset },
+ { "over_hang", &over_hang },
+ { "accent_width", &accent_width },
+ { "delimiter_factor", &delimiter_factor },
+ { "delimiter_shortfall", &delimiter_shortfall },
+ { "null_delimiter_space", &null_delimiter_space },
+ { "script_space", &script_space },
+ { "thin_space", &thin_space },
+ { "medium_space", &medium_space },
+ { "thick_space", &thick_space },
+ { "num1", &num1 },
+ { "num2", &num2 },
+ { "denom1", &denom1 },
+ { "denom2", &denom2 },
+ { "axis_height", &axis_height },
+ { "sup1", &sup1 },
+ { "sup2", &sup2 },
+ { "sup3", &sup3 },
+ { "default_rule_thickness", &default_rule_thickness },
+ { "sub1", &sub1 },
+ { "sub2", &sub2 },
+ { "sup_drop", &sup_drop },
+ { "sub_drop", &sub_drop },
+ { "x_height", &x_height },
+ { "big_op_spacing1", &big_op_spacing1 },
+ { "big_op_spacing2", &big_op_spacing2 },
+ { "big_op_spacing3", &big_op_spacing3 },
+ { "big_op_spacing4", &big_op_spacing4 },
+ { "big_op_spacing5", &big_op_spacing5 },
+ { "minimum_size", &minimum_size },
+ { "baseline_sep", &baseline_sep },
+ { "shift_down", &shift_down },
+ { "column_sep", &column_sep },
+ { "matrix_side_sep", &matrix_side_sep },
+ { "draw_lines", &draw_flag },
+ { "body_height", &body_height },
+ { "body_depth", &body_depth },
+ { "nroff", &nroff },
+ { 0, 0 }
+};
+
+void set_param(const char *name, int value)
+{
+ for (int i = 0; param_table[i].name != 0; i++)
+ if (strcmp(param_table[i].name, name) == 0) {
+ *param_table[i].ptr = value;
+ return;
+ }
+ error("unrecognised parameter `%1'", name);
+}
+
+int script_style(int style)
+{
+ return style > SCRIPT_STYLE ? style - 2 : style;
+}
+
+int cramped_style(int style)
+{
+ return (style & 1) ? style - 1 : style;
+}
+
+void set_space(int n)
+{
+ if (n < 0)
+ negative_space = -n;
+ else
+ positive_space = n;
+}
+
+// Return 0 if the specified size is bad.
+// The caller is responsible for giving the error message.
+
+int set_gsize(const char *s)
+{
+ const char *p = (*s == '+' || *s == '-') ? s + 1 : s;
+ char *end;
+ long n = strtol(p, &end, 10);
+ if (n <= 0 || *end != '\0' || n > INT_MAX)
+ return 0;
+ if (p > s) {
+ if (!gsize)
+ gsize = 10;
+ if (*s == '+') {
+ if (gsize > INT_MAX - n)
+ return 0;
+ gsize += int(n);
+ }
+ else {
+ if (gsize - n <= 0)
+ return 0;
+ gsize -= int(n);
+ }
+ }
+ else
+ gsize = int(n);
+ return 1;
+}
+
+void set_script_reduction(int n)
+{
+ script_size_reduction = n;
+}
+
+const char *get_gfont()
+{
+ return gfont ? gfont : "I";
+}
+
+const char *get_grfont()
+{
+ return grfont ? grfont : "R";
+}
+
+const char *get_gbfont()
+{
+ return gbfont ? gbfont : "B";
+}
+
+void set_gfont(const char *s)
+{
+ a_delete gfont;
+ gfont = strsave(s);
+}
+
+void set_grfont(const char *s)
+{
+ a_delete grfont;
+ grfont = strsave(s);
+}
+
+void set_gbfont(const char *s)
+{
+ a_delete gbfont;
+ gbfont = strsave(s);
+}
+
+// this must be precisely 2 characters in length
+#define COMPATIBLE_REG "0C"
+
+void start_string()
+{
+ printf(".nr " COMPATIBLE_REG " \\n(.C\n");
+ printf(".cp 0\n");
+ printf(".ds " LINE_STRING "\n");
+}
+
+void output_string()
+{
+ printf("\\*[" LINE_STRING "]\n");
+}
+
+void restore_compatibility()
+{
+ printf(".cp \\n(" COMPATIBLE_REG "\n");
+}
+
+void do_text(const char *s)
+{
+ printf(".eo\n");
+ printf(".as " LINE_STRING " \"%s\n", s);
+ printf(".ec\n");
+}
+
+void set_minimum_size(int n)
+{
+ minimum_size = n;
+}
+
+void set_script_size()
+{
+ if (minimum_size < 0)
+ minimum_size = 0;
+ if (script_size_reduction >= 0)
+ printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size);
+ else
+ printf(".ps (u;\\n[.s]*7+5/10>?%d)*1z\n", minimum_size);
+}
+
+int box::next_uid = 0;
+
+box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++)
+{
+}
+
+box::~box()
+{
+}
+
+void box::top_level()
+{
+ // debug_print();
+ // putc('\n', stderr);
+ box *b = this;
+ printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
+ printf(".ft\n");
+ printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n");
+ printf(".ft %s\n", get_gfont());
+ printf(".nr " SAVED_SIZE_REG " \\n[.s]z\n");
+ if (gsize > 0) {
+ char buf[INT_DIGITS + 1];
+ sprintf(buf, "%d", gsize);
+ b = new size_box(strsave(buf), b);
+ }
+ current_roman_font = get_grfont();
+ // This catches tabs used within \Z (which aren't allowed).
+ b->check_tabs(0);
+ int r = b->compute_metrics(DISPLAY_STYLE);
+ printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n");
+ printf(".ft \\n[" SAVED_FONT_REG "]\n");
+ printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r);
+ if (r == FOUND_MARK) {
+ printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n");
+ printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid);
+ }
+ else if (r == FOUND_LINEUP)
+ printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
+ SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n");
+ else
+ assert(r == FOUND_NOTHING);
+ // The problem here is that the argument to \f is read in copy mode,
+ // so we cannot use \E there; so we hide it in a string instead.
+ // Another problem is that if we use \R directly, then the space will
+ // prevent it working in a macro argument.
+ printf(".ds " SAVE_FONT_STRING " "
+ "\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'"
+ "\\fP"
+ "\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'"
+ "\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.s]z'"
+ "\\s0"
+ "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.s]z'"
+ "\n"
+ ".ds " RESTORE_FONT_STRING " "
+ "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]"
+ "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]"
+ "\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'"
+ "\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'"
+ "\n");
+ printf(".as " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]");
+ printf("\\f[%s]", get_gfont());
+ printf("\\s'\\En[" SAVED_SIZE_REG "]u'");
+ current_roman_font = get_grfont();
+ b->output();
+ printf("\\E*[" RESTORE_FONT_STRING "]\n");
+ if (r == FOUND_LINEUP)
+ printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n["
+ MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n["
+ WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n",
+ b->uid);
+ b->extra_space();
+ if (!inline_flag)
+ printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n["
+ DEPTH_FORMAT "]u-%dM>?0)\n",
+ b->uid, body_height, b->uid, body_depth);
+ delete b;
+ next_uid = 0;
+}
+
+// gpic defines this register so as to make geqn not produce `\x's
+#define EQN_NO_EXTRA_SPACE_REG "0x"
+
+void box::extra_space()
+{
+ printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
+ ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
+ if (positive_space >= 0 || negative_space >= 0) {
+ if (positive_space > 0)
+ printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
+ ".as " LINE_STRING " \\x'-%dM'\n", positive_space);
+ if (negative_space > 0)
+ printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
+ ".as " LINE_STRING " \\x'%dM'\n", negative_space);
+ positive_space = negative_space = -1;
+ }
+ else {
+ printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
+ ".if \\n[" HEIGHT_FORMAT "]>%dM .as " LINE_STRING
+ " \\x'-(\\n[" HEIGHT_FORMAT
+ "]u-%dM)'\n",
+ uid, body_height, uid, body_height);
+ printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] "
+ ".if \\n[" DEPTH_FORMAT "]>%dM .as " LINE_STRING
+ " \\x'\\n[" DEPTH_FORMAT
+ "]u-%dM'\n",
+ uid, body_depth, uid, body_depth);
+ }
+}
+
+int box::compute_metrics(int)
+{
+ printf(".nr " WIDTH_FORMAT " 0\n", uid);
+ printf(".nr " HEIGHT_FORMAT " 0\n", uid);
+ printf(".nr " DEPTH_FORMAT " 0\n", uid);
+ return FOUND_NOTHING;
+}
+
+void box::compute_subscript_kern()
+{
+ printf(".nr " SUB_KERN_FORMAT " 0\n", uid);
+}
+
+void box::compute_skew()
+{
+ printf(".nr " SKEW_FORMAT " 0\n", uid);
+}
+
+void box::output()
+{
+}
+
+void box::check_tabs(int)
+{
+}
+
+int box::is_char()
+{
+ return 0;
+}
+
+int box::left_is_italic()
+{
+ return 0;
+}
+
+int box::right_is_italic()
+{
+ return 0;
+}
+
+void box::hint(unsigned)
+{
+}
+
+void box::handle_char_type(int, int)
+{
+}
+
+
+box_list::box_list(box *pp)
+{
+ p = new box*[10];
+ for (int i = 0; i < 10; i++)
+ p[i] = 0;
+ maxlen = 10;
+ len = 1;
+ p[0] = pp;
+}
+
+void box_list::append(box *pp)
+{
+ if (len + 1 > maxlen) {
+ box **oldp = p;
+ maxlen *= 2;
+ p = new box*[maxlen];
+ memcpy(p, oldp, sizeof(box*)*len);
+ a_delete oldp;
+ }
+ p[len++] = pp;
+}
+
+box_list::~box_list()
+{
+ for (int i = 0; i < len; i++)
+ delete p[i];
+ a_delete p;
+}
+
+void box_list::list_check_tabs(int level)
+{
+ for (int i = 0; i < len; i++)
+ p[i]->check_tabs(level);
+}
+
+
+pointer_box::pointer_box(box *pp) : p(pp)
+{
+ spacing_type = p->spacing_type;
+}
+
+pointer_box::~pointer_box()
+{
+ delete p;
+}
+
+int pointer_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void pointer_box::compute_subscript_kern()
+{
+ p->compute_subscript_kern();
+ printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid);
+}
+
+void pointer_box::compute_skew()
+{
+ p->compute_skew();
+ printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n",
+ uid, p->uid);
+}
+
+void pointer_box::check_tabs(int level)
+{
+ p->check_tabs(level);
+}
+
+int simple_box::compute_metrics(int)
+{
+ printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid);
+ output();
+ printf(DELIMITER_CHAR "\n");
+ printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid);
+ printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid);
+ printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid);
+ printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid);
+ return FOUND_NOTHING;
+}
+
+void simple_box::compute_subscript_kern()
+{
+ // do nothing, we already computed it in do_metrics
+}
+
+void simple_box::compute_skew()
+{
+ // do nothing, we already computed it in do_metrics
+}
+
+int box::is_simple()
+{
+ return 0;
+}
+
+int simple_box::is_simple()
+{
+ return 1;
+}
+
+quoted_text_box::quoted_text_box(char *s) : text(s)
+{
+}
+
+quoted_text_box::~quoted_text_box()
+{
+ a_delete text;
+}
+
+void quoted_text_box::output()
+{
+ if (text)
+ fputs(text, stdout);
+}
+
+tab_box::tab_box() : disabled(0)
+{
+}
+
+// We treat a tab_box as having width 0 for width computations.
+
+void tab_box::output()
+{
+ if (!disabled)
+ printf("\\t");
+}
+
+void tab_box::check_tabs(int level)
+{
+ if (level > 0) {
+ error("tabs allowed only at outermost level");
+ disabled = 1;
+ }
+}
+
+space_box::space_box()
+{
+ spacing_type = SUPPRESS_TYPE;
+}
+
+void space_box::output()
+{
+ printf("\\h'%dM'", thick_space);
+}
+
+half_space_box::half_space_box()
+{
+ spacing_type = SUPPRESS_TYPE;
+}
+
+void half_space_box::output()
+{
+ printf("\\h'%dM'", thin_space);
+}
+
+void box_list::list_debug_print(const char *sep)
+{
+ p[0]->debug_print();
+ for (int i = 1; i < len; i++) {
+ fprintf(stderr, "%s", sep);
+ p[i]->debug_print();
+ }
+}
+
+void quoted_text_box::debug_print()
+{
+ fprintf(stderr, "\"%s\"", (text ? text : ""));
+}
+
+void half_space_box::debug_print()
+{
+ fprintf(stderr, "^");
+}
+
+void space_box::debug_print()
+{
+ fprintf(stderr, "~");
+}
+
+void tab_box::debug_print()
+{
+ fprintf(stderr, "<tab>");
+}
diff --git a/contrib/groff/src/preproc/eqn/box.h b/contrib/groff/src/preproc/eqn/box.h
new file mode 100644
index 000000000000..01bfe96a4ac3
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/box.h
@@ -0,0 +1,277 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct list_box;
+
+class box {
+private:
+ static int next_uid;
+public:
+ int spacing_type;
+ const int uid;
+ box();
+ virtual void debug_print() = 0;
+ virtual ~box();
+ void top_level();
+ virtual int compute_metrics(int);
+ virtual void compute_subscript_kern();
+ virtual void compute_skew();
+ virtual void output();
+ void extra_space();
+ virtual list_box *to_list_box();
+ virtual int is_simple();
+ virtual int is_char();
+ virtual int left_is_italic();
+ virtual int right_is_italic();
+ virtual void handle_char_type(int, int);
+ enum { FOUND_NOTHING = 0, FOUND_MARK = 1, FOUND_LINEUP = 2 };
+ void set_spacing_type(char *type);
+ virtual void hint(unsigned);
+ virtual void check_tabs(int);
+};
+
+class box_list {
+private:
+ int maxlen;
+public:
+ box **p;
+ int len;
+
+ box_list(box *);
+ ~box_list();
+ void append(box *);
+ void list_check_tabs(int);
+ void list_debug_print(const char *sep);
+ friend class list_box;
+};
+
+class list_box : public box {
+ int is_script;
+ box_list list;
+ int sty;
+public:
+ list_box(box *);
+ void debug_print();
+ int compute_metrics(int);
+ void compute_subscript_kern();
+ void output();
+ void check_tabs(int);
+ void append(box *);
+ list_box *to_list_box();
+ void handle_char_type(int, int);
+ void compute_sublist_width(int n);
+ friend box *make_script_box(box *, box *, box *);
+ friend box *make_mark_box(box *);
+ friend box *make_lineup_box(box *);
+};
+
+enum alignment { LEFT_ALIGN, RIGHT_ALIGN, CENTER_ALIGN };
+
+class column : public box_list {
+ alignment align;
+ int space;
+public:
+ column(box *);
+ void set_alignment(alignment);
+ void set_space(int);
+ void debug_print(const char *);
+
+ friend class matrix_box;
+ friend class pile_box;
+};
+
+class pile_box : public box {
+ column col;
+public:
+ pile_box(box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+ void check_tabs(int);
+ void set_alignment(alignment a) { col.set_alignment(a); }
+ void set_space(int n) { col.set_space(n); }
+ void append(box *p) { col.append(p); }
+};
+
+class matrix_box : public box {
+private:
+ int len;
+ int maxlen;
+ column **p;
+public:
+ matrix_box(column *);
+ ~matrix_box();
+ void append(column *);
+ int compute_metrics(int);
+ void output();
+ void check_tabs(int);
+ void debug_print();
+};
+
+class pointer_box : public box {
+protected:
+ box *p;
+public:
+ pointer_box(box *);
+ ~pointer_box();
+ int compute_metrics(int);
+ void compute_subscript_kern();
+ void compute_skew();
+ void debug_print() = 0;
+ void check_tabs(int);
+};
+
+class vcenter_box : public pointer_box {
+public:
+ vcenter_box(box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+class simple_box : public box {
+public:
+ int compute_metrics(int);
+ void compute_subscript_kern();
+ void compute_skew();
+ void output() = 0;
+ void debug_print() = 0;
+ int is_simple();
+};
+
+class quoted_text_box : public simple_box {
+ char *text;
+public:
+ quoted_text_box(char *);
+ ~quoted_text_box();
+ void debug_print();
+ void output();
+};
+
+class half_space_box : public simple_box {
+public:
+ half_space_box();
+ void output();
+ void debug_print();
+};
+
+class space_box : public simple_box {
+public:
+ space_box();
+ void output();
+ void debug_print();
+};
+
+class tab_box : public box {
+ int disabled;
+public:
+ tab_box();
+ void output();
+ void debug_print();
+ void check_tabs(int);
+};
+
+class size_box : public pointer_box {
+private:
+ char *size;
+public:
+ size_box(char *, box *);
+ ~size_box();
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+class font_box : public pointer_box {
+private:
+ char *f;
+public:
+ font_box(char *, box *);
+ ~font_box();
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+class fat_box : public pointer_box {
+public:
+ fat_box(box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+class vmotion_box : public pointer_box {
+private:
+ int n; // up is >= 0
+public:
+ vmotion_box(int, box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+class hmotion_box : public pointer_box {
+ int n;
+public:
+ hmotion_box(int, box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+box *split_text(char *);
+box *make_script_box(box *, box *, box *);
+box *make_mark_box(box *);
+box *make_lineup_box(box *);
+box *make_delim_box(char *, box *, char *);
+box *make_sqrt_box(box *);
+box *make_prime_box(box *);
+box *make_over_box(box *, box *);
+box *make_small_over_box(box *, box *);
+box *make_limit_box(box *, box *, box *);
+box *make_accent_box(box *, box *);
+box *make_uaccent_box(box *, box *);
+box *make_overline_box(box *);
+box *make_underline_box(box *);
+box *make_special_box(char *, box *);
+
+void set_space(int);
+int set_gsize(const char *);
+void set_gfont(const char *);
+void set_grfont(const char *);
+void set_gbfont(const char *);
+const char *get_gfont();
+const char *get_grfont();
+const char *get_gbfont();
+void start_string();
+void output_string();
+void do_text(const char *);
+void restore_compatibility();
+void set_script_reduction(int n);
+void set_minimum_size(int n);
+void set_param(const char *name, int value);
+
+void set_char_type(const char *type, char *ch);
+
+void init_char_table();
+void init_extensible();
+void define_extensible(const char *name, const char *ext, const char *top = 0,
+ const char *mid = 0, const char *bot = 0);
diff --git a/contrib/groff/src/preproc/eqn/delim.cc b/contrib/groff/src/preproc/eqn/delim.cc
new file mode 100644
index 000000000000..29deded38343
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/delim.cc
@@ -0,0 +1,381 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 };
+
+// Small must be none-zero and must exist in each device.
+// Small will be put in the roman font, others are assumed to be
+// on the special font (so no font change will be necessary.)
+
+struct delimiter {
+ const char *name;
+ int flags;
+ const char *small;
+ const char *chain_format;
+ const char *ext;
+ const char *top;
+ const char *mid;
+ const char *bot;
+} delim_table[] = {
+ {
+ "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]",
+ "\\[parenleftex]",
+ "\\[parenlefttp]",
+ 0,
+ "\\[parenleftbt]",
+ },
+ {
+ ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]",
+ "\\[parenrightex]",
+ "\\[parenrighttp]",
+ 0,
+ "\\[parenrightbt]",
+ },
+ {
+ "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]",
+ "\\[bracketleftex]",
+ "\\[bracketlefttp]",
+ 0,
+ "\\[bracketleftbt]",
+ },
+ {
+ "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]",
+ "\\[bracketrightex]",
+ "\\[bracketrighttp]",
+ 0,
+ "\\[bracketrightbt]",
+ },
+ {
+ "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]",
+ "\\[braceleftex]",
+ "\\[bracelefttp]",
+ "\\[braceleftmid]",
+ "\\[braceleftbt]",
+ },
+ {
+ "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]",
+ "\\[bracerightex]",
+ "\\[bracerighttp]",
+ "\\[bracerightmid]",
+ "\\[bracerightbt]",
+ },
+ {
+ "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
+ "\\[barex]",
+ },
+ {
+ "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]",
+ "\\[bracketleftex]",
+ 0,
+ 0,
+ "\\[bracketleftbt]",
+ },
+ {
+ "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]",
+ "\\[bracketrightex]",
+ 0,
+ 0,
+ "\\[bracketrightbt]",
+ },
+ {
+ "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]",
+ "\\[bracketleftex]",
+ "\\[bracketlefttp]",
+ },
+ {
+ "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]",
+ "\\[bracketrightex]",
+ "\\[bracketrighttp]",
+ },
+ {
+ "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
+ "\\[bardblex]",
+ },
+ {
+ "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]",
+ },
+ {
+ ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]",
+ },
+ {
+ "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]",
+ "\\[arrowvertex]",
+ "\\[arrowverttp]",
+ },
+ {
+ "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]",
+ "\\[arrowvertex]",
+ 0,
+ 0,
+ "\\[arrowvertbt]",
+ },
+ {
+ "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]",
+ "\\[arrowvertex]",
+ "\\[arrowverttp]",
+ 0,
+ "\\[arrowvertbt]",
+ },
+};
+
+const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0]));
+
+class delim_box : public box {
+private:
+ char *left;
+ char *right;
+ box *p;
+public:
+ delim_box(char *, box *, char *);
+ ~delim_box();
+ int compute_metrics(int);
+ void output();
+ void check_tabs(int);
+ void debug_print();
+};
+
+box *make_delim_box(char *l, box *pp, char *r)
+{
+ if (l != 0 && *l == '\0') {
+ a_delete l;
+ l = 0;
+ }
+ if (r != 0 && *r == '\0') {
+ a_delete r;
+ r = 0;
+ }
+ return new delim_box(l, pp, r);
+}
+
+delim_box::delim_box(char *l, box *pp, char *r)
+: left(l), right(r), p(pp)
+{
+}
+
+delim_box::~delim_box()
+{
+ a_delete left;
+ a_delete right;
+ delete p;
+}
+
+static void build_extensible(const char *ext, const char *top, const char *mid,
+ const char *bot)
+{
+ assert(ext != 0);
+ printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
+ ext);
+ printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n");
+ printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n");
+ if (top) {
+ printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
+ ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
+ top);
+ printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n");
+ printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n");
+ }
+ if (mid) {
+ printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
+ ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
+ mid);
+ printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n");
+ printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n");
+ }
+ if (bot) {
+ printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
+ ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
+ bot);
+ printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n");
+ printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n");
+ }
+ printf(".nr " TOTAL_HEIGHT_REG " 0");
+ if (top)
+ printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]");
+ if (bot)
+ printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]");
+ if (mid)
+ printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]");
+ printf("\n");
+ // determine how many extensible characters we need
+ printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]");
+ if (mid)
+ printf("/2");
+ printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n["
+ EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n");
+
+ printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n["
+ EXT_DEPTH_REG "]*\\n[" TEMP_REG "]");
+ if (mid)
+ printf("*2");
+ printf(")\n");
+ printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
+ "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n",
+ axis_height);
+ if (top)
+ printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'"
+ "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
+ "\\v'\\n[" TOP_DEPTH_REG "]u'\n",
+ top);
+
+ // this macro appends $2 copies of $3 to string $1
+ printf(".de " REPEAT_APPEND_STRING_MACRO "\n"
+ ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n"
+ "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n"
+ ".\\}\n"
+ "..\n");
+
+ printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] "
+ "\\v'\\n[" EXT_HEIGHT_REG "]u'"
+ "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
+ "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
+ ext);
+
+ if (mid) {
+ printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'"
+ "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
+ "\\v'\\n[" MID_DEPTH_REG "]u'\n",
+ mid);
+ printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING
+ " \\n[" TEMP_REG "] "
+ "\\v'\\n[" EXT_HEIGHT_REG "]u'"
+ "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
+ "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
+ ext);
+ }
+ if (bot)
+ printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'"
+ "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
+ "\\v'\\n[" BOT_DEPTH_REG "]u'\n",
+ bot);
+ printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n");
+}
+
+static void define_extensible_string(char *delim, int uid,
+ left_or_right_t left_or_right)
+{
+ printf(".ds " DELIM_STRING "\n");
+ delimiter *d = delim_table;
+ int delim_len = strlen(delim);
+ int i;
+ for (i = 0; i < DELIM_TABLE_SIZE; i++, d++)
+ if (strncmp(delim, d->name, delim_len) == 0
+ && (left_or_right & d->flags) != 0)
+ break;
+ if (i >= DELIM_TABLE_SIZE) {
+ error("there is no `%1' delimiter", delim);
+ printf(".nr " DELIM_WIDTH_REG " 0\n");
+ return;
+ }
+
+ printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
+ ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
+ "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
+ ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n"
+ ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
+ "\\{",
+ current_roman_font, d->small, axis_height,
+ current_roman_font, d->small);
+
+ char buf[256];
+ sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]");
+ printf(".nr " INDEX_REG " 0\n"
+ ".de " TEMP_MACRO "\n"
+ ".ie c%s \\{\\\n"
+ ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n"
+ ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
+ "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n"
+ ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n"
+ ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
+ "\\{.nr " INDEX_REG " +1\n"
+ "." TEMP_MACRO "\n"
+ ".\\}\\}\n"
+ ".el .nr " INDEX_REG " 0-1\n"
+ "..\n"
+ "." TEMP_MACRO "\n",
+ buf, buf, axis_height, buf);
+ if (d->ext) {
+ printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext);
+ build_extensible(d->ext, d->top, d->mid, d->bot);
+ printf(".\\}\\}\n");
+ }
+ printf(".\\}\n");
+ printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n");
+ printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
+ ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n",
+ uid, uid, axis_height);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
+ ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n",
+ uid, uid, axis_height);
+}
+
+int delim_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM"
+ ">?(\\n[" DEPTH_FORMAT "]+%dM)\n",
+ p->uid, axis_height, p->uid, axis_height);
+ printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500"
+ ">?(\\n[" DELTA_REG "]*2-%dM)\n",
+ delimiter_factor, delimiter_shortfall);
+ if (left) {
+ define_extensible_string(left, uid, LEFT_DELIM);
+ printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n",
+ uid);
+ if (r)
+ printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n");
+ }
+ if (right) {
+ define_extensible_string(right, uid, RIGHT_DELIM);
+ printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n",
+ uid);
+ }
+ return r;
+}
+
+void delim_box::output()
+{
+ if (left)
+ printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid);
+ p->output();
+ if (right)
+ printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid);
+}
+
+void delim_box::check_tabs(int level)
+{
+ p->check_tabs(level);
+}
+
+void delim_box::debug_print()
+{
+ fprintf(stderr, "left \"%s\" { ", left ? left : "");
+ p->debug_print();
+ fprintf(stderr, " }");
+ if (right)
+ fprintf(stderr, " right \"%s\"", right);
+}
+
diff --git a/contrib/groff/src/preproc/eqn/eqn.cc b/contrib/groff/src/preproc/eqn/eqn.cc
new file mode 100644
index 000000000000..1fdda611201a
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/eqn.cc
@@ -0,0 +1,1277 @@
+#if defined(__STDC__) || defined(__cplusplus)
+#define YYCONST const
+#define YYPARAMS(x) x
+#define YYDEFUN(name, arglist, args) name(args)
+#define YYAND ,
+#define YYPTR void *
+#else
+#define YYCONST
+#define YYPARAMS(x) ()
+#define YYDEFUN(name, arglist, args) name arglist args;
+#define YYAND ;
+#define YYPTR char *
+#endif
+#ifndef lint
+YYCONST static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley +Cygnus.28) 01/20/91";
+#endif
+#define YYBYACC 1
+#ifndef YYDONT_INCLUDE_STDIO
+#include <stdio.h>
+#endif
+#ifdef __cplusplus
+#include <stdlib.h> /* for malloc/realloc/free */
+#endif
+#line 20 "eqn.y"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "lib.h"
+#include "box.h"
+extern int non_empty_flag;
+char *strsave(const char *);
+int yylex();
+void yyerror(const char *);
+#line 32 "eqn.y"
+typedef union {
+ char *str;
+ box *b;
+ pile_box *pb;
+ matrix_box *mb;
+ int n;
+ column *col;
+} YYSTYPE;
+#line 45 "y.tab.c"
+#define OVER 257
+#define SMALLOVER 258
+#define SQRT 259
+#define SUB 260
+#define SUP 261
+#define LPILE 262
+#define RPILE 263
+#define CPILE 264
+#define PILE 265
+#define LEFT 266
+#define RIGHT 267
+#define TO 268
+#define FROM 269
+#define SIZE 270
+#define FONT 271
+#define ROMAN 272
+#define BOLD 273
+#define ITALIC 274
+#define FAT 275
+#define ACCENT 276
+#define BAR 277
+#define UNDER 278
+#define ABOVE 279
+#define TEXT 280
+#define QUOTED_TEXT 281
+#define FWD 282
+#define BACK 283
+#define DOWN 284
+#define UP 285
+#define MATRIX 286
+#define COL 287
+#define LCOL 288
+#define RCOL 289
+#define CCOL 290
+#define MARK 291
+#define LINEUP 292
+#define TYPE 293
+#define VCENTER 294
+#define PRIME 295
+#define SPLIT 296
+#define NOSPLIT 297
+#define UACCENT 298
+#define SPECIAL 299
+#define SPACE 300
+#define GFONT 301
+#define GSIZE 302
+#define DEFINE 303
+#define NDEFINE 304
+#define TDEFINE 305
+#define SDEFINE 306
+#define UNDEF 307
+#define IFDEF 308
+#define INCLUDE 309
+#define DELIM 310
+#define CHARTYPE 311
+#define SET 312
+#define GRFONT 313
+#define GBFONT 314
+#define YYERRCODE 256
+static YYCONST short yylhs[] = { -1,
+ 0, 0, 6, 6, 1, 1, 1, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 4, 4, 7, 7,
+ 7, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 8, 11, 11, 12, 12, 13,
+ 13, 16, 16, 15, 15, 14, 14, 14, 14, 9,
+ 9, 10, 10, 10,
+};
+static YYCONST short yylen[] = { 2,
+ 0, 1, 1, 2, 1, 2, 2, 1, 3, 3,
+ 5, 5, 1, 2, 3, 3, 1, 3, 1, 3,
+ 5, 1, 1, 2, 2, 1, 1, 1, 3, 2,
+ 2, 2, 2, 4, 5, 3, 2, 2, 2, 3,
+ 3, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+ 3, 3, 2, 3, 1, 1, 3, 3, 4, 1,
+ 2, 1, 3, 3, 4, 2, 2, 2, 2, 1,
+ 1, 1, 1, 1,
+};
+static YYCONST short yydefred[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 22, 23, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 26, 27, 28, 0,
+ 0, 3, 5, 0, 13, 0, 0, 17, 14, 70,
+ 71, 0, 0, 55, 31, 32, 33, 30, 73, 74,
+ 72, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 6, 7, 0, 0, 24, 25, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 37, 38,
+ 39, 0, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,
+ 0, 29, 15, 16, 9, 0, 0, 20, 18, 40,
+ 41, 0, 58, 0, 0, 0, 0, 66, 67, 68,
+ 69, 34, 61, 0, 0, 0, 0, 59, 35, 0,
+ 0, 0, 11, 12, 21, 0, 64, 0, 0, 65,
+};
+static YYCONST short yydgoto[] = { 31,
+ 32, 33, 34, 35, 36, 84, 38, 43, 44, 52,
+ 85, 45, 98, 99, 118, 131,
+};
+static YYCONST short yysindex[] = { 1488,
+ 1527, -120, -120, -120, -120, -123, -249, -249, 1566, 1566,
+ 1566, 1566, 0, 0, -249, -249, -249, -249, -115, 1488,
+ 1488, -249, 1566, -256, -251, -249, 0, 0, 0, 1488,
+ 0, 0, 0, -221, 0, -233, 1488, 0, 0, 0,
+ 0, 1488, -85, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1488, 1566, 1566, -195, -195, -195, -195, 1566, 1566,
+ 1566, 1566, -272, 0, 0, 1566, -195, 0, 0, 1566,
+ 1402, 1527, 1527, 1527, 1527, 1566, 1566, 1566, 0, 0,
+ 0, 1566, 0, 1488, -113, 1488, 1444, -195, -195, -195,
+ -195, -195, -195, -117, -117, -117, -117, -118, 0, -195,
+ -195, 0, 0, 0, 0, -167, -189, 0, 0, 0,
+ 0, 1488, 0, -106, -123, 1488, -83, 0, 0, 0,
+ 0, 0, 0, 1527, 1527, 1566, 1488, 0, 0, 1488,
+ -105, 1488, 0, 0, 0, 1488, 0, -104, 1488, 0,
+};
+static YYCONST short yyrindex[] = { 41,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 1220, 46, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 85, 128, 363, 406, 0, 0,
+ 0, 0, 0, 0, 0, 0, 449, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -103, 0, 0, 185, 492, 727, 770,
+ 813, 856, 1091, 0, 0, 0, 0, 0, 0, 1134,
+ 1177, 0, 0, 0, 0, 42, 1220, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -102, 0, 0, -101,
+ 0, 0, 0, 0, 0, 0, 0, 0, -99, 0,
+};
+static YYCONST short yygindex[] = { 0,
+ -7, -69, 3, -66, 458, 9, -26, 52, 27, -63,
+ -32, 54, 0, -35, 2, -59,
+};
+#define YYTABLESIZE 1865
+static YYCONST short yytable[] = { 49,
+ 8, 50, 42, 39, 105, 116, 122, 63, 37, 8,
+ 109, 113, 64, 65, 94, 95, 96, 97, 128, 137,
+ 140, 56, 57, 62, 68, 63, 76, 77, 69, 83,
+ 40, 41, 51, 53, 54, 72, 73, 86, 71, 132,
+ 1, 10, 78, 79, 80, 2, 74, 75, 66, 108,
+ 10, 129, 70, 114, 133, 134, 46, 47, 48, 135,
+ 87, 81, 123, 83, 82, 0, 59, 60, 61, 62,
+ 76, 126, 138, 0, 103, 104, 83, 106, 0, 83,
+ 78, 79, 80, 0, 42, 0, 78, 79, 80, 72,
+ 73, 0, 0, 42, 8, 0, 119, 120, 121, 81,
+ 124, 125, 82, 0, 0, 81, 0, 0, 82, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 83,
+ 127, 0, 83, 8, 130, 8, 8, 43, 0, 0,
+ 0, 83, 0, 0, 0, 10, 43, 0, 0, 0,
+ 130, 51, 0, 0, 139, 117, 117, 117, 117, 0,
+ 0, 0, 0, 0, 0, 0, 40, 41, 0, 40,
+ 41, 0, 40, 41, 10, 112, 10, 10, 94, 95,
+ 96, 97, 112, 136, 136, 56, 57, 62, 42, 63,
+ 0, 0, 0, 0, 36, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 42, 0, 42,
+ 42, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 43, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 43, 0, 43, 43, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 0, 0, 0,
+ 0, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 36,
+ 0, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 0, 0,
+ 0, 0, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 0, 0, 44, 42, 42, 42, 42, 42, 42, 42,
+ 42, 44, 0, 0, 0, 42, 42, 42, 42, 0,
+ 42, 42, 0, 42, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 0, 0, 45, 43, 43, 43, 43,
+ 43, 43, 43, 43, 45, 0, 0, 0, 43, 43,
+ 43, 43, 0, 43, 43, 0, 43, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 36, 0, 36, 36, 0, 0, 53, 0,
+ 0, 0, 36, 36, 0, 0, 44, 53, 0, 0,
+ 36, 36, 36, 36, 0, 0, 55, 56, 57, 58,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 36,
+ 67, 0, 36, 0, 0, 44, 0, 44, 44, 0,
+ 0, 47, 0, 0, 0, 0, 0, 0, 0, 45,
+ 47, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 88, 89, 0, 0, 0, 0, 90, 91, 92, 93,
+ 0, 0, 0, 100, 0, 0, 0, 101, 45, 0,
+ 45, 45, 0, 107, 0, 110, 0, 0, 0, 111,
+ 0, 0, 53, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 53, 0, 53, 53, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 47, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 47, 0, 47, 47, 0, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 0, 0,
+ 0, 44, 44, 44, 44, 44, 44, 44, 44, 0,
+ 0, 0, 0, 44, 44, 44, 44, 0, 44, 44,
+ 0, 44, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 0, 0, 0, 45, 45, 45, 45, 45, 45,
+ 45, 45, 0, 0, 0, 0, 45, 45, 45, 45,
+ 0, 45, 45, 0, 45, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 0, 0, 46, 53, 53, 53,
+ 53, 53, 53, 53, 53, 46, 0, 0, 0, 53,
+ 53, 53, 53, 0, 53, 53, 0, 53, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 0, 0, 48,
+ 47, 47, 47, 47, 47, 47, 47, 47, 48, 0,
+ 0, 0, 47, 47, 47, 47, 0, 47, 47, 0,
+ 47, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 49, 0, 0, 0, 0, 0, 0, 0,
+ 46, 49, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 46,
+ 0, 46, 46, 0, 0, 51, 0, 0, 0, 0,
+ 0, 0, 0, 48, 51, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 48, 0, 48, 48, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 49, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 0, 49, 49, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 51, 0,
+ 51, 51, 0, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 0, 0, 0, 46, 46, 46, 46, 46,
+ 46, 46, 46, 0, 0, 0, 0, 46, 46, 46,
+ 46, 0, 46, 46, 0, 46, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 0, 0, 0, 48, 48,
+ 48, 48, 48, 48, 48, 48, 0, 0, 0, 0,
+ 48, 48, 48, 48, 0, 48, 48, 0, 48, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 0, 0,
+ 50, 49, 49, 49, 49, 49, 49, 49, 49, 50,
+ 0, 0, 0, 49, 49, 49, 49, 0, 49, 49,
+ 0, 49, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 0, 0, 52, 51, 51, 51, 51, 51, 51,
+ 51, 51, 52, 0, 0, 0, 51, 51, 51, 51,
+ 0, 51, 51, 0, 51, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 54, 0, 0, 0,
+ 0, 0, 0, 0, 50, 54, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 50, 0, 50, 50, 0, 0, 19,
+ 0, 0, 0, 0, 0, 0, 0, 52, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 52, 0, 52, 52,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
+ 0, 54, 54, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 19, 0, 19, 19, 0, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 0, 0, 0, 50,
+ 50, 50, 50, 50, 50, 50, 50, 0, 0, 0,
+ 0, 50, 50, 50, 50, 0, 50, 50, 0, 50,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 0,
+ 29, 0, 52, 52, 52, 52, 52, 52, 52, 52,
+ 0, 0, 0, 0, 52, 52, 52, 52, 0, 52,
+ 52, 0, 52, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 29, 0, 0, 54, 54, 54, 54, 54,
+ 54, 54, 54, 0, 0, 0, 0, 54, 54, 54,
+ 54, 0, 54, 54, 0, 54, 19, 19, 19, 0,
+ 0, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 27, 29, 0, 19, 19,
+ 19, 19, 19, 19, 19, 19, 0, 0, 0, 0,
+ 19, 19, 19, 19, 0, 19, 19, 0, 19, 0,
+ 0, 0, 0, 0, 30, 0, 102, 28, 0, 0,
+ 0, 0, 0, 0, 0, 29, 0, 27, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 30, 0, 0, 28,
+ 0, 0, 0, 0, 29, 0, 0, 0, 0, 0,
+ 0, 27, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 30, 0, 0, 28, 0, 0, 0, 0, 0, 0,
+ 27, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,
+ 0, 0, 28, 0, 0, 0, 0, 0, 0, 27,
+ 1, 0, 0, 2, 3, 4, 5, 6, 0, 0,
+ 0, 7, 8, 9, 10, 11, 12, 0, 0, 0,
+ 0, 13, 14, 15, 16, 17, 18, 19, 30, 0,
+ 0, 28, 20, 21, 22, 23, 0, 24, 25, 0,
+ 26, 0, 1, 0, 0, 2, 3, 4, 5, 6,
+ 115, 0, 0, 7, 8, 9, 10, 11, 12, 0,
+ 0, 0, 0, 13, 14, 15, 16, 17, 18, 19,
+ 0, 0, 0, 0, 20, 21, 22, 23, 0, 24,
+ 25, 0, 26, 0, 0, 0, 1, 0, 0, 2,
+ 3, 4, 5, 6, 0, 0, 0, 7, 8, 9,
+ 10, 11, 12, 0, 0, 0, 0, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 20, 21,
+ 22, 23, 0, 24, 25, 1, 26, 0, 2, 3,
+ 4, 5, 6, 0, 0, 0, 7, 8, 9, 10,
+ 11, 12, 0, 0, 0, 0, 13, 14, 15, 16,
+ 17, 18, 19, 0, 0, 0, 0, 0, 0, 22,
+ 23, 0, 24, 25, 0, 26, 0, 2, 3, 4,
+ 5, 6, 0, 0, 0, 7, 8, 9, 10, 11,
+ 12, 0, 0, 0, 0, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, 0, 0, 0, 0, 22, 23,
+ 0, 24, 25, 0, 26,
+};
+static YYCONST short yycheck[] = { 123,
+ 0, 125, 123, 1, 74, 123, 125, 123, 0, 9,
+ 77, 125, 20, 21, 287, 288, 289, 290, 125, 125,
+ 125, 125, 125, 125, 281, 125, 260, 261, 280, 37,
+ 280, 281, 6, 7, 8, 257, 258, 123, 30, 123,
+ 0, 0, 276, 277, 278, 0, 268, 269, 22, 76,
+ 9, 115, 26, 86, 124, 125, 3, 4, 5, 126,
+ 52, 295, 98, 71, 298, -1, 15, 16, 17, 18,
+ 260, 261, 132, -1, 72, 73, 84, 75, -1, 87,
+ 276, 277, 278, -1, 0, -1, 276, 277, 278, 257,
+ 258, -1, -1, 9, 94, -1, 95, 96, 97, 295,
+ 268, 269, 298, -1, -1, 295, -1, -1, 298, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 127,
+ 112, -1, 130, 123, 116, 125, 126, 0, -1, -1,
+ -1, 139, -1, -1, -1, 94, 9, -1, -1, -1,
+ 132, 115, -1, -1, 136, 94, 95, 96, 97, -1,
+ -1, -1, -1, -1, -1, -1, 280, 281, -1, 280,
+ 281, -1, 280, 281, 123, 279, 125, 126, 287, 288,
+ 289, 290, 279, 279, 279, 279, 279, 279, 94, 279,
+ -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 123, -1, 125,
+ 126, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 94, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 123, -1, 125, 126, -1, -1, -1, -1, -1, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, -1, -1,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, -1, -1, -1,
+ -1, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 125,
+ -1, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+ -1, -1, 0, 279, 280, 281, 282, 283, 284, 285,
+ 286, 9, -1, -1, -1, 291, 292, 293, 294, -1,
+ 296, 297, -1, 299, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, -1, -1, 0, 279, 280, 281, 282,
+ 283, 284, 285, 286, 9, -1, -1, -1, 291, 292,
+ 293, 294, -1, 296, 297, -1, 299, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, -1, 260, 261, -1, -1, 0, -1,
+ -1, -1, 268, 269, -1, -1, 94, 9, -1, -1,
+ 276, 277, 278, 279, -1, -1, 9, 10, 11, 12,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 295,
+ 23, -1, 298, -1, -1, 123, -1, 125, 126, -1,
+ -1, 0, -1, -1, -1, -1, -1, -1, -1, 94,
+ 9, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 53, 54, -1, -1, -1, -1, 59, 60, 61, 62,
+ -1, -1, -1, 66, -1, -1, -1, 70, 123, -1,
+ 125, 126, -1, 76, -1, 78, -1, -1, -1, 82,
+ -1, -1, 94, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 123, -1, 125, 126, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 94, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 123, -1, 125, 126, -1, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, -1, -1,
+ -1, 279, 280, 281, 282, 283, 284, 285, 286, -1,
+ -1, -1, -1, 291, 292, 293, 294, -1, 296, 297,
+ -1, 299, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, -1, -1, -1, 279, 280, 281, 282, 283, 284,
+ 285, 286, -1, -1, -1, -1, 291, 292, 293, 294,
+ -1, 296, 297, -1, 299, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, -1, -1, 0, 279, 280, 281,
+ 282, 283, 284, 285, 286, 9, -1, -1, -1, 291,
+ 292, 293, 294, -1, 296, 297, -1, 299, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, -1, -1, 0,
+ 279, 280, 281, 282, 283, 284, 285, 286, 9, -1,
+ -1, -1, 291, 292, 293, 294, -1, 296, 297, -1,
+ 299, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 0, -1, -1, -1, -1, -1, -1, -1,
+ 94, 9, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 123,
+ -1, 125, 126, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, 94, 9, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 123, -1, 125, 126, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 94, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 123, -1, 125, 126, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 94,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 123, -1,
+ 125, 126, -1, 257, 258, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, 268, 269, 270, 271, 272, 273,
+ 274, 275, -1, -1, -1, 279, 280, 281, 282, 283,
+ 284, 285, 286, -1, -1, -1, -1, 291, 292, 293,
+ 294, -1, 296, 297, -1, 299, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 275, -1, -1, -1, 279, 280,
+ 281, 282, 283, 284, 285, 286, -1, -1, -1, -1,
+ 291, 292, 293, 294, -1, 296, 297, -1, 299, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, -1, -1,
+ 0, 279, 280, 281, 282, 283, 284, 285, 286, 9,
+ -1, -1, -1, 291, 292, 293, 294, -1, 296, 297,
+ -1, 299, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, -1, -1, 0, 279, 280, 281, 282, 283, 284,
+ 285, 286, 9, -1, -1, -1, 291, 292, 293, 294,
+ -1, 296, 297, -1, 299, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 0, -1, -1, -1,
+ -1, -1, -1, -1, 94, 9, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 123, -1, 125, 126, -1, -1, 0,
+ -1, -1, -1, -1, -1, -1, -1, 94, 9, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 123, -1, 125, 126,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 94, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 123,
+ -1, 125, 126, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 94, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 123, -1, 125, 126, -1, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, -1, -1, -1, 279,
+ 280, 281, 282, 283, 284, 285, 286, -1, -1, -1,
+ -1, 291, 292, 293, 294, -1, 296, 297, -1, 299,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, 273, 274, 275, -1,
+ 9, -1, 279, 280, 281, 282, 283, 284, 285, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, 296,
+ 297, -1, 299, 257, 258, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, 268, 269, 270, 271, 272, 273,
+ 274, 275, 9, -1, -1, 279, 280, 281, 282, 283,
+ 284, 285, 286, -1, -1, -1, -1, 291, 292, 293,
+ 294, -1, 296, 297, -1, 299, 257, 258, 259, -1,
+ -1, 262, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 275, 94, 9, -1, 279, 280,
+ 281, 282, 283, 284, 285, 286, -1, -1, -1, -1,
+ 291, 292, 293, 294, -1, 296, 297, -1, 299, -1,
+ -1, -1, -1, -1, 123, -1, 125, 126, -1, -1,
+ -1, -1, -1, -1, -1, 9, -1, 94, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 123, -1, -1, 126,
+ -1, -1, -1, -1, 9, -1, -1, -1, -1, -1,
+ -1, 94, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 123, -1, -1, 126, -1, -1, -1, -1, -1, -1,
+ 94, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 123,
+ -1, -1, 126, -1, -1, -1, -1, -1, -1, 94,
+ 259, -1, -1, 262, 263, 264, 265, 266, -1, -1,
+ -1, 270, 271, 272, 273, 274, 275, -1, -1, -1,
+ -1, 280, 281, 282, 283, 284, 285, 286, 123, -1,
+ -1, 126, 291, 292, 293, 294, -1, 296, 297, -1,
+ 299, -1, 259, -1, -1, 262, 263, 264, 265, 266,
+ 267, -1, -1, 270, 271, 272, 273, 274, 275, -1,
+ -1, -1, -1, 280, 281, 282, 283, 284, 285, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, 296,
+ 297, -1, 299, -1, -1, -1, 259, -1, -1, 262,
+ 263, 264, 265, 266, -1, -1, -1, 270, 271, 272,
+ 273, 274, 275, -1, -1, -1, -1, 280, 281, 282,
+ 283, 284, 285, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, 296, 297, 259, 299, -1, 262, 263,
+ 264, 265, 266, -1, -1, -1, 270, 271, 272, 273,
+ 274, 275, -1, -1, -1, -1, 280, 281, 282, 283,
+ 284, 285, 286, -1, -1, -1, -1, -1, -1, 293,
+ 294, -1, 296, 297, -1, 299, -1, 262, 263, 264,
+ 265, 266, -1, -1, -1, 270, 271, 272, 273, 274,
+ 275, -1, -1, -1, -1, 280, 281, 282, 283, 284,
+ 285, 286, -1, -1, -1, -1, -1, -1, 293, 294,
+ -1, 296, 297, -1, 299,
+};
+#define YYFINAL 31
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 314
+#if YYDEBUG
+static YYCONST char *YYCONST yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,"'\\t'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'","'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"OVER",
+"SMALLOVER","SQRT","SUB","SUP","LPILE","RPILE","CPILE","PILE","LEFT","RIGHT",
+"TO","FROM","SIZE","FONT","ROMAN","BOLD","ITALIC","FAT","ACCENT","BAR","UNDER",
+"ABOVE","TEXT","QUOTED_TEXT","FWD","BACK","DOWN","UP","MATRIX","COL","LCOL",
+"RCOL","CCOL","MARK","LINEUP","TYPE","VCENTER","PRIME","SPLIT","NOSPLIT",
+"UACCENT","SPECIAL","SPACE","GFONT","GSIZE","DEFINE","NDEFINE","TDEFINE",
+"SDEFINE","UNDEF","IFDEF","INCLUDE","DELIM","CHARTYPE","SET","GRFONT","GBFONT",
+};
+static YYCONST char *YYCONST yyrule[] = {
+"$accept : top",
+"top :",
+"top : equation",
+"equation : mark",
+"equation : equation mark",
+"mark : from_to",
+"mark : MARK mark",
+"mark : LINEUP mark",
+"from_to : sqrt_over",
+"from_to : sqrt_over TO from_to",
+"from_to : sqrt_over FROM sqrt_over",
+"from_to : sqrt_over FROM sqrt_over TO from_to",
+"from_to : sqrt_over FROM sqrt_over FROM from_to",
+"sqrt_over : script",
+"sqrt_over : SQRT sqrt_over",
+"sqrt_over : sqrt_over OVER sqrt_over",
+"sqrt_over : sqrt_over SMALLOVER sqrt_over",
+"script : nonsup",
+"script : simple SUP script",
+"nonsup : simple",
+"nonsup : simple SUB nonsup",
+"nonsup : simple SUB simple SUP script",
+"simple : TEXT",
+"simple : QUOTED_TEXT",
+"simple : SPLIT QUOTED_TEXT",
+"simple : NOSPLIT TEXT",
+"simple : '^'",
+"simple : '~'",
+"simple : '\\t'",
+"simple : '{' equation '}'",
+"simple : PILE pile_arg",
+"simple : LPILE pile_arg",
+"simple : RPILE pile_arg",
+"simple : CPILE pile_arg",
+"simple : MATRIX '{' column_list '}'",
+"simple : LEFT delim equation RIGHT delim",
+"simple : LEFT delim equation",
+"simple : simple BAR",
+"simple : simple UNDER",
+"simple : simple PRIME",
+"simple : simple ACCENT simple",
+"simple : simple UACCENT simple",
+"simple : ROMAN simple",
+"simple : BOLD simple",
+"simple : ITALIC simple",
+"simple : FAT simple",
+"simple : FONT text simple",
+"simple : SIZE text simple",
+"simple : FWD number simple",
+"simple : BACK number simple",
+"simple : UP number simple",
+"simple : DOWN number simple",
+"simple : TYPE text simple",
+"simple : VCENTER simple",
+"simple : SPECIAL text simple",
+"number : text",
+"pile_element_list : equation",
+"pile_element_list : pile_element_list ABOVE equation",
+"pile_arg : '{' pile_element_list '}'",
+"pile_arg : number '{' pile_element_list '}'",
+"column_list : column",
+"column_list : column_list column",
+"column_element_list : equation",
+"column_element_list : column_element_list ABOVE equation",
+"column_arg : '{' column_element_list '}'",
+"column_arg : number '{' column_element_list '}'",
+"column : COL column_arg",
+"column : LCOL column_arg",
+"column : RCOL column_arg",
+"column : CCOL column_arg",
+"text : TEXT",
+"text : QUOTED_TEXT",
+"delim : text",
+"delim : '{'",
+"delim : '}'",
+};
+#endif
+#define YYLEX yylex()
+#define YYEMPTY -1
+#define yyclearin (yychar=(YYEMPTY))
+#define yyerrok (yyerrflag=0)
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 10000
+#define YYMAXDEPTH 10000
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+YYSTYPE yyval;
+YYSTYPE yylval;
+static short *yyss;
+static YYSTYPE *yyvs;
+static int yystacksize;
+static int yygrow ();
+static YYPTR yymalloc YYPARAMS((unsigned));
+static YYPTR yyrealloc YYPARAMS((YYPTR, unsigned));
+#define yyfree(x) free(x)
+#define YYABORT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+
+#if YYDEBUG
+#ifdef __cplusplus
+extern "C" char *getenv();
+#else
+extern char *getenv();
+#endif
+#endif
+
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+ register YYSTYPE *yyvsp;
+ register short *yyssp;
+ short *yysse;
+#if YYDEBUG
+ register YYCONST char *yys;
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ if (yyss == 0)
+ {
+ yyss = (short *) yymalloc (YYINITDEPTH * sizeof (short));
+ if (yyss == 0)
+ goto yyabort;
+ yyvs = (YYSTYPE *) yymalloc (YYINITDEPTH * sizeof (YYSTYPE));
+ if (yyvs == 0)
+ {
+ yyfree (yyss);
+ goto yyabort;
+ }
+ yystacksize = YYINITDEPTH;
+ }
+ yysse = yyss + yystacksize - 1;
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n", yystate,
+ yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, shifting to state %d\n",
+ yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yysse)
+ {
+ /* FIXME: Rework so there's only one of these. */
+ int depth = yyssp - yyss;
+ if (yygrow () != 0)
+ goto yyoverflow;
+ yysse = yyss + yystacksize - 1;
+ yyssp = yyss + depth;
+ yyvsp = yyvs + depth;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, error recovery shifting\
+ to state %d\n", *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yysse)
+ {
+ int depth = yyssp - yyss;
+ if (yygrow () != 0)
+ goto yyoverflow;
+ yysse = yyss + yystacksize - 1;
+ yyssp = yyss + depth;
+ yyvsp = yyvs + depth;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: error recovery discarding state %d\n",
+ *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, error recovery discards token %d (%s)\n",
+ yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, reducing by rule %d (%s)\n",
+ yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 2:
+#line 126 "eqn.y"
+{ yyvsp[0].b->top_level(); non_empty_flag = 1; }
+break;
+case 3:
+#line 131 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 4:
+#line 133 "eqn.y"
+{
+ list_box *lb = yyvsp[-1].b->to_list_box();
+ if (!lb)
+ lb = new list_box(yyvsp[-1].b);
+ lb->append(yyvsp[0].b);
+ yyval.b = lb;
+ }
+break;
+case 5:
+#line 144 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 6:
+#line 146 "eqn.y"
+{ yyval.b = make_mark_box(yyvsp[0].b); }
+break;
+case 7:
+#line 148 "eqn.y"
+{ yyval.b = make_lineup_box(yyvsp[0].b); }
+break;
+case 8:
+#line 153 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 9:
+#line 155 "eqn.y"
+{ yyval.b = make_limit_box(yyvsp[-2].b, 0, yyvsp[0].b); }
+break;
+case 10:
+#line 157 "eqn.y"
+{ yyval.b = make_limit_box(yyvsp[-2].b, yyvsp[0].b, 0); }
+break;
+case 11:
+#line 159 "eqn.y"
+{ yyval.b = make_limit_box(yyvsp[-4].b, yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 12:
+#line 161 "eqn.y"
+{ yyval.b = make_limit_box(yyvsp[-4].b, make_limit_box(yyvsp[-2].b, yyvsp[0].b, 0), 0); }
+break;
+case 13:
+#line 166 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 14:
+#line 168 "eqn.y"
+{ yyval.b = make_sqrt_box(yyvsp[0].b); }
+break;
+case 15:
+#line 170 "eqn.y"
+{ yyval.b = make_over_box(yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 16:
+#line 172 "eqn.y"
+{ yyval.b = make_small_over_box(yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 17:
+#line 177 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 18:
+#line 179 "eqn.y"
+{ yyval.b = make_script_box(yyvsp[-2].b, 0, yyvsp[0].b); }
+break;
+case 19:
+#line 184 "eqn.y"
+{ yyval.b = yyvsp[0].b; }
+break;
+case 20:
+#line 186 "eqn.y"
+{ yyval.b = make_script_box(yyvsp[-2].b, yyvsp[0].b, 0); }
+break;
+case 21:
+#line 188 "eqn.y"
+{ yyval.b = make_script_box(yyvsp[-4].b, yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 22:
+#line 193 "eqn.y"
+{ yyval.b = split_text(yyvsp[0].str); }
+break;
+case 23:
+#line 195 "eqn.y"
+{ yyval.b = new quoted_text_box(yyvsp[0].str); }
+break;
+case 24:
+#line 197 "eqn.y"
+{ yyval.b = split_text(yyvsp[0].str); }
+break;
+case 25:
+#line 199 "eqn.y"
+{ yyval.b = new quoted_text_box(yyvsp[0].str); }
+break;
+case 26:
+#line 201 "eqn.y"
+{ yyval.b = new half_space_box; }
+break;
+case 27:
+#line 203 "eqn.y"
+{ yyval.b = new space_box; }
+break;
+case 28:
+#line 205 "eqn.y"
+{ yyval.b = new tab_box; }
+break;
+case 29:
+#line 207 "eqn.y"
+{ yyval.b = yyvsp[-1].b; }
+break;
+case 30:
+#line 209 "eqn.y"
+{ yyvsp[0].pb->set_alignment(CENTER_ALIGN); yyval.b = yyvsp[0].pb; }
+break;
+case 31:
+#line 211 "eqn.y"
+{ yyvsp[0].pb->set_alignment(LEFT_ALIGN); yyval.b = yyvsp[0].pb; }
+break;
+case 32:
+#line 213 "eqn.y"
+{ yyvsp[0].pb->set_alignment(RIGHT_ALIGN); yyval.b = yyvsp[0].pb; }
+break;
+case 33:
+#line 215 "eqn.y"
+{ yyvsp[0].pb->set_alignment(CENTER_ALIGN); yyval.b = yyvsp[0].pb; }
+break;
+case 34:
+#line 217 "eqn.y"
+{ yyval.b = yyvsp[-1].mb; }
+break;
+case 35:
+#line 219 "eqn.y"
+{ yyval.b = make_delim_box(yyvsp[-3].str, yyvsp[-2].b, yyvsp[0].str); }
+break;
+case 36:
+#line 221 "eqn.y"
+{ yyval.b = make_delim_box(yyvsp[-1].str, yyvsp[0].b, 0); }
+break;
+case 37:
+#line 223 "eqn.y"
+{ yyval.b = make_overline_box(yyvsp[-1].b); }
+break;
+case 38:
+#line 225 "eqn.y"
+{ yyval.b = make_underline_box(yyvsp[-1].b); }
+break;
+case 39:
+#line 227 "eqn.y"
+{ yyval.b = make_prime_box(yyvsp[-1].b); }
+break;
+case 40:
+#line 229 "eqn.y"
+{ yyval.b = make_accent_box(yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 41:
+#line 231 "eqn.y"
+{ yyval.b = make_uaccent_box(yyvsp[-2].b, yyvsp[0].b); }
+break;
+case 42:
+#line 233 "eqn.y"
+{ yyval.b = new font_box(strsave(get_grfont()), yyvsp[0].b); }
+break;
+case 43:
+#line 235 "eqn.y"
+{ yyval.b = new font_box(strsave(get_gbfont()), yyvsp[0].b); }
+break;
+case 44:
+#line 237 "eqn.y"
+{ yyval.b = new font_box(strsave(get_gfont()), yyvsp[0].b); }
+break;
+case 45:
+#line 239 "eqn.y"
+{ yyval.b = new fat_box(yyvsp[0].b); }
+break;
+case 46:
+#line 241 "eqn.y"
+{ yyval.b = new font_box(yyvsp[-1].str, yyvsp[0].b); }
+break;
+case 47:
+#line 243 "eqn.y"
+{ yyval.b = new size_box(yyvsp[-1].str, yyvsp[0].b); }
+break;
+case 48:
+#line 245 "eqn.y"
+{ yyval.b = new hmotion_box(yyvsp[-1].n, yyvsp[0].b); }
+break;
+case 49:
+#line 247 "eqn.y"
+{ yyval.b = new hmotion_box(-yyvsp[-1].n, yyvsp[0].b); }
+break;
+case 50:
+#line 249 "eqn.y"
+{ yyval.b = new vmotion_box(yyvsp[-1].n, yyvsp[0].b); }
+break;
+case 51:
+#line 251 "eqn.y"
+{ yyval.b = new vmotion_box(-yyvsp[-1].n, yyvsp[0].b); }
+break;
+case 52:
+#line 253 "eqn.y"
+{ yyvsp[0].b->set_spacing_type(yyvsp[-1].str); yyval.b = yyvsp[0].b; }
+break;
+case 53:
+#line 255 "eqn.y"
+{ yyval.b = new vcenter_box(yyvsp[0].b); }
+break;
+case 54:
+#line 257 "eqn.y"
+{ yyval.b = make_special_box(yyvsp[-1].str, yyvsp[0].b); }
+break;
+case 55:
+#line 262 "eqn.y"
+{
+ int n;
+ if (sscanf(yyvsp[0].str, "%d", &n) == 1)
+ yyval.n = n;
+ a_delete yyvsp[0].str;
+ }
+break;
+case 56:
+#line 272 "eqn.y"
+{ yyval.pb = new pile_box(yyvsp[0].b); }
+break;
+case 57:
+#line 274 "eqn.y"
+{ yyvsp[-2].pb->append(yyvsp[0].b); yyval.pb = yyvsp[-2].pb; }
+break;
+case 58:
+#line 279 "eqn.y"
+{ yyval.pb = yyvsp[-1].pb; }
+break;
+case 59:
+#line 281 "eqn.y"
+{ yyvsp[-1].pb->set_space(yyvsp[-3].n); yyval.pb = yyvsp[-1].pb; }
+break;
+case 60:
+#line 286 "eqn.y"
+{ yyval.mb = new matrix_box(yyvsp[0].col); }
+break;
+case 61:
+#line 288 "eqn.y"
+{ yyvsp[-1].mb->append(yyvsp[0].col); yyval.mb = yyvsp[-1].mb; }
+break;
+case 62:
+#line 293 "eqn.y"
+{ yyval.col = new column(yyvsp[0].b); }
+break;
+case 63:
+#line 295 "eqn.y"
+{ yyvsp[-2].col->append(yyvsp[0].b); yyval.col = yyvsp[-2].col; }
+break;
+case 64:
+#line 300 "eqn.y"
+{ yyval.col = yyvsp[-1].col; }
+break;
+case 65:
+#line 302 "eqn.y"
+{ yyvsp[-1].col->set_space(yyvsp[-3].n); yyval.col = yyvsp[-1].col; }
+break;
+case 66:
+#line 307 "eqn.y"
+{ yyvsp[0].col->set_alignment(CENTER_ALIGN); yyval.col = yyvsp[0].col; }
+break;
+case 67:
+#line 309 "eqn.y"
+{ yyvsp[0].col->set_alignment(LEFT_ALIGN); yyval.col = yyvsp[0].col; }
+break;
+case 68:
+#line 311 "eqn.y"
+{ yyvsp[0].col->set_alignment(RIGHT_ALIGN); yyval.col = yyvsp[0].col; }
+break;
+case 69:
+#line 313 "eqn.y"
+{ yyvsp[0].col->set_alignment(CENTER_ALIGN); yyval.col = yyvsp[0].col; }
+break;
+case 70:
+#line 317 "eqn.y"
+{ yyval.str = yyvsp[0].str; }
+break;
+case 71:
+#line 319 "eqn.y"
+{ yyval.str = yyvsp[0].str; }
+break;
+case 72:
+#line 324 "eqn.y"
+{ yyval.str = yyvsp[0].str; }
+break;
+case 73:
+#line 326 "eqn.y"
+{ yyval.str = strsave("{"); }
+break;
+case 74:
+#line 328 "eqn.y"
+{ yyval.str = strsave("}"); }
+break;
+#line 1168 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state 0 to\
+ state %d\n", YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n",
+ YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state %d \
+to state %d\n", *yyssp, yystate);
+#endif
+ if (yyssp >= yysse)
+ {
+ int depth = yyssp - yyss;
+ if (yygrow () != 0)
+ goto yyoverflow;
+ yysse = yyss + yystacksize - 1;
+ yyssp = yyss + depth;
+ yyvsp = yyvs + depth;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
+
+static int
+yygrow ()
+{
+ int old_stacksize = yystacksize;
+ short *new_yyss;
+ YYSTYPE *new_yyvs;
+
+ if (yystacksize >= YYMAXDEPTH)
+ return (1);
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: growing stack size from %d to %d\n",
+ old_stacksize, yystacksize);
+#endif
+ new_yyss = (short *) yyrealloc (yyss, yystacksize * sizeof (short));
+ if (new_yyss == 0)
+ return (1);
+ new_yyvs = (YYSTYPE *) yyrealloc (yyvs, yystacksize * sizeof (YYSTYPE));
+ if (new_yyvs == 0)
+ {
+ yyfree (new_yyss);
+ return (1);
+ }
+ yyss = new_yyss;
+ yyvs = new_yyvs;
+ return (0);
+}
+
+static YYPTR
+YYDEFUN (yymalloc, (bytes), unsigned bytes)
+{
+ YYPTR ptr = (YYPTR) malloc (bytes);
+ if (ptr != 0) return (ptr);
+ yyerror ("yyparse: memory exhausted");
+ return (0);
+}
+
+static YYPTR
+YYDEFUN (yyrealloc, (old, bytes), YYPTR old YYAND unsigned bytes)
+{
+ YYPTR ptr = (YYPTR) realloc (old, bytes);
+ if (ptr != 0) return (ptr);
+ yyerror ("yyparse: memory exhausted");
+ return (0);
+}
diff --git a/contrib/groff/src/preproc/eqn/eqn.h b/contrib/groff/src/preproc/eqn/eqn.h
new file mode 100644
index 000000000000..70b19271cbc1
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/eqn.h
@@ -0,0 +1,51 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "cset.h"
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+
+#include "box.h"
+
+extern char start_delim;
+extern char end_delim;
+extern int non_empty_flag;
+extern int inline_flag;
+extern int draw_flag;
+extern int one_size_reduction_flag;
+extern int compatible_flag;
+extern int nroff;
+
+void init_lex(const char *str, const char *filename, int lineno);
+void lex_error(const char *message,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+void init_table(const char *device);
+
+// prefix for all registers, strings, macros
+#define PREFIX "0"
diff --git a/contrib/groff/src/preproc/eqn/eqn.man b/contrib/groff/src/preproc/eqn/eqn.man
new file mode 100644
index 000000000000..381d97de33ab
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/eqn.man
@@ -0,0 +1,882 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.ie \n(.V<\n(.v .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.\" The BSD man macros can't handle " in arguments to font change macros,
+.\" so use \(ts instead of ".
+.tr \(ts"
+.TH @G@EQN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@eqn \- format equations for troff
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fB@g@eqn 'u
+.ti \niu
+.B @g@eqn
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-rvCNR
+.OP \-d cc
+.OP \-T name
+.OP \-M dir
+.OP \-f F
+.OP \-s n
+.OP \-p n
+.OP \-m n
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+This manual page describes the GNU version of
+.BR eqn ,
+which is part of the groff document formatting system.
+.B eqn
+compiles descriptions of equations embedded within
+.B troff
+input files into commands that are understood by
+.BR troff .
+Normally, it should be invoked using the
+.B \-e
+option of
+.BR groff .
+The syntax is quite compatible with Unix eqn.
+The output of GNU eqn cannot be processed with Unix troff;
+it must be processed with GNU troff.
+If no files are given on the command line, the standard input
+will be read.
+A filename of
+.B \-
+will cause the standard input to be read.
+.LP
+.B eqn
+searches for the file
+.B eqnrc
+in the directories given with the
+.B \-M
+option first, then in
+.BR @SYSTEMMACRODIR@ ,
+.BR @LOCALMACRODIR@ ,
+and finally in the standard macro directory
+.BR @MACRODIR@ .
+If it exists, eqn will process it before the other input files.
+The
+.B \-R
+option prevents this.
+.LP
+GNU eqn does not provide the functionality of neqn:
+it does not support low-resolution, typewriter-like devices
+(although it may work adequately for very simple input).
+.SH OPTIONS
+.TP
+.B \-C
+Recognize
+.B .EQ
+and
+.B .EN
+even when followed by a character other than space or newline.
+.TP
+.B \-N
+Don't allow newlines within delimiters.
+This option allows
+.B eqn
+to recover better from missing closing delimiters.
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-r
+Only one size reduction.
+.TP
+.BI \-m n
+The minimum point-size is
+.IR n .
+eqn will not reduce the size of subscripts or superscripts to
+a smaller size than
+.IR n .
+.TP
+.BI \-T name
+The output is for device
+.IR name .
+The only effect of this is to define a macro
+.I name
+with a value of
+.BR 1 .
+Typically
+.B eqnrc
+will use this to provide definitions appropriate for the output device.
+The default output device is
+.BR @DEVICE@ .
+.TP
+.BI \-M dir
+Search
+.I dir
+for
+.B eqnrc
+before the default directories.
+.TP
+.B \-R
+Don't load
+.BR eqnrc .
+.TP
+.BI \-f F
+This is equivalent to a
+.BI gfont\ F
+command.
+.TP
+.BI \-s n
+This is equivalent to a
+.BI gsize\ n
+command.
+This option is deprecated.
+eqn will normally set equations at whatever the current point size
+is when the equation is encountered.
+.TP
+.BI \-p n
+This says that subscripts and superscripts should be
+.I n
+points smaller than the surrounding text.
+This option is deprecated.
+Normally eqn makes sets subscripts and superscripts at 70%
+of the size of the surrounding text.
+.SH USAGE
+Only the differences between GNU eqn and Unix eqn are described here.
+.LP
+Most of the new features of GNU eqn
+are based on \*(tx.
+There are some references to the differences between \*(tx and GNU eqn below;
+these may safely be ignored if you do not know \*(tx.
+.SS Automatic spacing
+.LP
+.B eqn
+gives each component of an equation a type, and adjusts the spacing
+between components using that type.
+Possible types are:
+.TP \w'punctuation'u+2n
+ordinary
+an ordinary character such as 1 or
+.IR x ;
+.TP
+operator
+a large operator such as
+.ds Su \s+5\(*S\s0
+.if \n(.g .if !c\(*S .ds Su the summation operator
+\*(Su;
+.TP
+binary
+a binary operator such as +;
+.TP
+relation
+a relation such as =;
+.TP
+opening
+a opening bracket such as (;
+.TP
+closing
+a closing bracket such as );
+.TP
+punctuation
+a punctuation character such as ,;
+.TP
+inner
+a subformula contained within brackets;
+.TP
+suppress
+spacing that suppresses automatic spacing adjustment.
+.LP
+Components of an equation get a type in one of two ways.
+.TP
+.BI type\ t\ e
+This yields an equation component that contains
+.I e
+but that has type
+.IR t ,
+where
+.I t
+is one of the types mentioned above.
+For example,
+.B times
+is defined as
+.RS
+.IP
+.B
+type "binary" \e(mu
+.RE
+.IP
+The name of the type doesn't have to be quoted, but quoting protects
+from macro expansion.
+.TP
+.BI chartype\ t\ text
+Unquoted groups of characters are split up into individual characters,
+and the type of each character is looked up;
+this changes the type that is stored for each character;
+it says that the characters in
+.I text
+from now on have type
+.IR t .
+For example,
+.RS
+.IP
+.B
+chartype "punctuation" .,;:
+.RE
+.IP
+would make the characters
+.B .,;:
+have type punctuation
+whenever they subsequently appeared in an equation.
+The type
+.I t
+can also be
+.B letter
+or
+.BR digit ;
+in these cases
+.B chartype
+changes the font type of the characters.
+See the Fonts subsection.
+.SS New primitives
+.TP
+.IB e1\ smallover\ e2
+This is similar to
+.BR over ;
+.B smallover
+reduces the size of
+.I e1
+and
+.IR e2 ;
+it also puts less vertical space between
+.I e1
+or
+.I e2
+and the fraction bar.
+The
+.B over
+primitive corresponds to the \*(tx
+.B \eover
+primitive in display styles;
+.B smallover
+corresponds to
+.B \eover
+in non-display styles.
+.TP
+.BI vcenter\ e
+This vertically centers
+.I e
+about the math axis.
+The math axis is the vertical position about which characters
+such as + and - are centered; also it is the vertical position
+used for the bar of fractions.
+For example,
+.B sum
+is defined as
+.RS
+.IP
+.B
+{ type "operator" vcenter size +5 \e(*S }
+.RE
+.TP
+.IB e1\ accent\ e2
+This sets
+.I e2
+as an accent over
+.IR e1 .
+.I e2
+is assumed to be at the correct height for a lowercase letter;
+.I e2
+will be moved down according if
+.I e1
+is taller or shorter than a lowercase letter.
+For example,
+.B hat
+is defined as
+.RS
+.IP
+.B
+accent { "^" }
+.RE
+.IP
+.BR dotdot ,
+.BR dot ,
+.BR tilde ,
+.B vec
+and
+.B dyad
+are also defined using the
+.B accent
+primitive.
+.TP
+.IB e1\ uaccent\ e2
+This sets
+.I e2
+as an accent under
+.IR e1 .
+.I e2
+is assumed to be at the correct height for a character without a descender;
+.I e2
+will be moved down if
+.I e1
+has a descender.
+.B utilde
+is pre-defined using
+.B uaccent
+as a tilde accent below the baseline.
+.TP
+.BI split\ \(ts text \(ts
+This has the same effect as simply
+.RS
+.IP
+.I text
+.RE
+.IP
+but
+.I text
+is not subject to macro expansion because it is quoted;
+.I text
+will be split up and the spacing between individual characters
+will be adjusted.
+.TP
+.BI nosplit\ text
+This has the same effect as
+.RS
+.IP
+.BI \(ts text \(ts
+.RE
+.IP
+but because
+.I text
+is not quoted it will be subject to macro expansion;
+.I text
+will not be split up
+and the spacing between individual characters will not be adjusted.
+.TP
+.IB e\ opprime
+This is a variant of
+.B prime
+that acts as an operator on
+.IR e .
+It produces a different result from
+.B prime
+in a case such as
+.BR A\ opprime\ sub\ 1 :
+with
+.B opprime
+the
+.B 1
+will be tucked under the prime as a subscript to the
+.B A
+(as is conventional in mathematical typesetting),
+whereas with
+.B prime
+the
+.B 1
+will be a subscript to the prime character.
+The precedence of
+.B opprime
+is the same as that of
+.B bar
+and
+.BR under ,
+which is higher than that of everything except
+.B accent
+and
+.BR uaccent .
+In unquoted text a
+.B '
+that is not the first character will be treated like
+.BR opprime .
+.TP
+.BI special\ text\ e
+This constructs a new object from
+.I e
+using a
+.BR @g@troff (@MAN1EXT@)
+macro named
+.IR text .
+When the macro is called,
+the string
+.B 0s
+will contain the output for
+.IR e ,
+and the number registers
+.BR 0w ,
+.BR 0h ,
+.BR 0d ,
+.BR 0skern
+and
+.BR 0skew
+will contain the width, height, depth, subscript kern, and skew of
+.IR e .
+(The
+.I "subscript kern"
+of an object says how much a subscript on that object should be tucked in;
+the
+.I skew
+of an object says how far to the right of the center of the object an
+accent over the object should be placed.)
+The macro must modify
+.B 0s
+so that it will output the desired result with its origin at the current
+point, and increase the current horizontal position by the width
+of the object.
+The number registers must also be modified so that they correspond to the
+result.
+.RS
+.LP
+For example, suppose you wanted a construct that `cancels' an expression
+by drawing a diagonal line through it.
+.IP
+.nf
+.ft B
+.ne 6+\n(.Vu
+\&.EQ
+define cancel 'special Ca'
+\&.EN
+\&.de Ca
+\&.ds 0s \eZ'\e\e*(0s'\ev'\e\en(0du'\eD'l \e\en(0wu -\e\en(0hu-\e\en(0du'\ev'\e\en(0hu'
+\&..
+.ft
+.fi
+.LP
+Then you could cancel an expression
+.I e
+with
+.BI cancel\ {\ e\ }
+.LP
+Here's a more complicated construct that draws a box round an expression:
+.IP
+.nf
+.ft B
+.ne 11+\n(.Vu
+\&.EQ
+define box 'special Bx'
+\&.EN
+\&.de Bx
+\&.ds 0s \eZ'\eh'1n'\e\e*(0s'\e
+\eZ'\ev'\e\en(0du+1n'\eD'l \e\en(0wu+2n 0'\eD'l 0 -\e\en(0hu-\e\en(0du-2n'\e
+\eD'l -\e\en(0wu-2n 0'\eD'l 0 \e\en(0hu+\e\en(0du+2n''\eh'\e\en(0wu+2n'
+\&.nr 0w +2n
+\&.nr 0d +1n
+\&.nr 0h +1n
+\&..
+.ft
+.fi
+.RE
+.SS Customization
+The appearance of equations is controlled by
+a large number of parameters. These can be set using
+the
+.B set
+command.
+.TP
+.BI set\ p\ n
+This sets parameter
+.I p
+to value
+.I n ;
+.I n
+is an integer.
+For example,
+.RS
+.IP
+.B
+set x_height 45
+.RE
+.IP
+says that
+.B eqn
+should assume an x height of 0.45 ems.
+.RS
+.LP
+Possible parameters are as follows.
+Values are in units of hundredths of an em unless otherwise stated.
+These descriptions are intended to be expository rather than
+definitive.
+.TP \w'\fBdefault_rule_thickness'u+2n
+.B minimum_size
+.B eqn
+will not set anything at a smaller point-size than this.
+The value is in points.
+.TP
+.B fat_offset
+The
+.B fat
+primitive emboldens an equation
+by overprinting two copies of the equation
+horizontally offset by this amount.
+.TP
+.B over_hang
+A fraction bar will be longer by twice this amount than
+the maximum of the widths of the numerator and denominator;
+in other words, it will overhang the numerator and
+denominator by at least this amount.
+.TP
+.B accent_width
+When
+.B bar
+or
+.B under
+is applied to a single character,
+the line will be this long.
+Normally,
+.B bar
+or
+.B under
+produces a line whose length is the width of the object to which it applies;
+in the case of a single character,
+this tends to produce a line that looks too long.
+.TP
+.B delimiter_factor
+Extensible delimiters produced with the
+.B left
+and
+.B right
+primitives will have a combined height and depth of at least this many
+thousandths of twice the maximum amount by which the sub-equation that
+the delimiters enclose extends away from the axis.
+.TP
+.B delimiter_shortfall
+Extensible delimiters produced with the
+.B left
+and
+.B right
+primitives will have a combined height and depth
+not less than the difference of
+twice the maximum amount by which the sub-equation that
+the delimiters enclose extends away from the axis
+and this amount.
+.TP
+.B null_delimiter_space
+This much horizontal space is inserted
+on each side of a fraction.
+.TP
+.B script_space
+The width of subscripts and superscripts is increased by this amount.
+.TP
+.B thin_space
+This amount of space is automatically inserted after punctuation
+characters.
+.TP
+.B medium_space
+This amount of space is automatically inserted on either side
+of binary operators.
+.TP
+.B thick_space
+This amount of space is automatically inserted on either side of
+relations.
+.TP
+.B x_height
+The height of lowercase letters without ascenders such as x.
+.TP
+.B axis_height
+The height above the baseline of the center of characters
+such as \(pl and \(mi.
+It is important that this value is correct for the font
+you are using.
+.TP
+.B default_rule_thickness
+This should set to the thickness of the
+.B \e(ru
+character, or the thickness of horizontal lines produced with the
+.B \eD
+escape sequence.
+.TP
+.B num1
+The
+.B over
+command will shift up the numerator by at least this amount.
+.TP
+.B num2
+The
+.B smallover
+command will shift up the numerator by at least this amount.
+.TP
+.B denom1
+The
+.B over
+command will shift down the denominator by at least this amount.
+.TP
+.B denom2
+The
+.B smallover
+command will shift down the denominator by at least this amount.
+.TP
+.B sup1
+Normally superscripts will be shifted up by at least this amount.
+.TP
+.B sup2
+Superscripts within superscripts or upper limits
+or numerators of
+.B smallover
+fractions
+will be shifted up by at least this amount.
+This is usually less than sup1.
+.TP
+.B sup3
+Superscripts within denominators or square roots
+or subscripts or lower limits will be shifted up by at least
+this amount.
+This is usually less than sup2.
+.TP
+.B sub1
+Subscripts will normally be shifted down by at least this amount.
+.TP
+.B sub2
+When there is both a subscript and a superscript, the subscript
+will be shifted down by at least this amount.
+.TP
+.B sup_drop
+The baseline of a superscript will be no more
+than this much amount below the top of the object on
+which the superscript is set.
+.TP
+.B sub_drop
+The baseline of a subscript will be at least this much below
+the bottom of the object on which the subscript is set.
+.TP
+.B big_op_spacing1
+The baseline of an upper limit will be at least this
+much above the top of the object on which the limit is set.
+.TP
+.B big_op_spacing2
+The baseline of a lower limit will be at least this
+much below the bottom of the object on which the limit is set.
+.TP
+.B big_op_spacing3
+The bottom of an upper limit will be at least this much above the
+top of the object on which the limit is set.
+.TP
+.B big_op_spacing4
+The top of a lower limit will be at least this much below
+the bottom of the object on which the limit is set.
+.TP
+.B big_op_spacing5
+This much vertical space will be added above and below limits.
+.TP
+.B baseline_sep
+The baselines of the rows in a pile or matrix will normally be
+this far apart.
+In most cases this should be equal to the sum of
+.B num1
+and
+.BR denom1 .
+.TP
+.B shift_down
+The midpoint between the top baseline and the bottom baseline
+in a matrix or pile will be shifted down by this much from the axis.
+In most cases this should be equal to
+.BR axis_height .
+.TP
+.B column_sep
+This much space will be added between columns in a matrix.
+.TP
+.B matrix_side_sep
+This much space will be added at each side of a matrix.
+.TP
+.B draw_lines
+If this is non-zero, lines will be drawn using the
+.B \eD
+escape sequence, rather than with the
+.B \el
+escape sequence and the
+.B \e(ru
+character.
+.TP
+.B body_height
+The amount by which the height of the equation exceeds this
+will be added as extra space before the line containing the equation
+(using
+.BR \ex .)
+The default value is 85.
+.TP
+.B body_depth
+The amount by which the depth of the equation exceeds this
+will be added as extra space after the line containing the equation
+(using
+.BR \ex .)
+The default value is 35.
+.TP
+.B nroff
+If this is non-zero,
+then
+.B ndefine
+will behave like
+.B define
+and
+.B tdefine
+will be ignored,
+otherwise
+.B tdefine
+will behave like
+.B define
+and
+.B ndefine
+will be ignored.
+The default value is 0
+(This is typically changed to 1 by the
+.B eqnrc
+file for the
+.BR ascii ,
+.BR latin1 ,
+.BR utf8 ,
+and
+.B cp1047
+devices.)
+.LP
+A more precise description of the role of many of these
+parameters can be found in Appendix H of
+.IR The\ \*(txbook .
+.RE
+.SS Macros
+Macros can take arguments.
+In a macro body,
+.BI $ n
+where
+.I n
+is between 1 and 9,
+will be replaced by the
+.IR n-th
+argument if the macro is called with arguments;
+if there are fewer than
+.I n
+arguments, it will be replaced by nothing.
+A word containing a left parenthesis where the part of the word
+before the left parenthesis has been defined using the
+.B define
+command
+will be recognized as a macro call with arguments;
+characters following the left parenthesis
+up to a matching right parenthesis will be treated as comma-separated
+arguments;
+commas inside nested parentheses do not terminate an argument.
+.TP
+.BI sdefine\ name\ X\ anything\ X
+This is like the
+.B define
+command, but
+.I name
+will not be recognized if called with arguments.
+.TP
+.BI include\ \(ts file \(ts
+Include the contents of
+.IR file .
+Lines of
+.I file
+beginning with
+.B .EQ
+or
+.B .EN
+will be ignored.
+.TP
+.BI ifdef\ name\ X\ anything\ X
+If
+.I name
+has been defined by
+.B define
+(or has been automatically defined because
+.I name
+is the output device)
+process
+.IR anything ;
+otherwise ignore
+.IR anything .
+.I X
+can be any character not appearing in
+.IR anything .
+.SS Fonts
+.B eqn
+normally uses at least two fonts to set an equation:
+an italic font for letters,
+and a roman font for everything else.
+The existing
+.B gfont
+command
+changes the font that is used as the italic font.
+By default this is
+.BR I .
+The font that is used as the roman font can be changed
+using the new
+.B grfont
+command.
+.TP
+.BI grfont\ f
+Set the roman font to
+.IR f .
+.LP
+The
+.B italic
+primitive uses the current italic font set by
+.BR gfont ;
+the
+.B roman
+primitive uses the current roman font set by
+.BR grfont .
+There is also a new
+.B gbfont
+command, which changes the font used by the
+.B bold
+primitive.
+If you only use the
+.BR roman ,
+.B italic
+and
+.B bold
+primitives to changes fonts within an equation,
+you can change all the fonts used by your equations
+just by using
+.BR gfont ,
+.B grfont
+and
+.B gbfont
+commands.
+.LP
+You can control which characters are treated as letters
+(and therefore set in italics) by using the
+.B chartype
+command described above.
+A type of
+.B letter
+will cause a character to be set in italic type.
+A type of
+.B digit
+will cause a character to be set in roman type.
+.SH FILES
+.Tp \w'\fB@MACRODIR@/eqnrc'u+2n
+.B @MACRODIR@/eqnrc
+Initialization file.
+.SH BUGS
+Inline equations will be set at the point size that is current at the
+beginning of the input line.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.I The\ \*(txbook
diff --git a/contrib/groff/src/preproc/eqn/eqn.y b/contrib/groff/src/preproc/eqn/eqn.y
new file mode 100644
index 000000000000..833a0f01072a
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/eqn.y
@@ -0,0 +1,331 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "lib.h"
+#include "box.h"
+extern int non_empty_flag;
+char *strsave(const char *);
+int yylex();
+void yyerror(const char *);
+%}
+
+%union {
+ char *str;
+ box *b;
+ pile_box *pb;
+ matrix_box *mb;
+ int n;
+ column *col;
+}
+
+%token OVER
+%token SMALLOVER
+%token SQRT
+%token SUB
+%token SUP
+%token LPILE
+%token RPILE
+%token CPILE
+%token PILE
+%token LEFT
+%token RIGHT
+%token TO
+%token FROM
+%token SIZE
+%token FONT
+%token ROMAN
+%token BOLD
+%token ITALIC
+%token FAT
+%token ACCENT
+%token BAR
+%token UNDER
+%token ABOVE
+%token <str> TEXT
+%token <str> QUOTED_TEXT
+%token FWD
+%token BACK
+%token DOWN
+%token UP
+%token MATRIX
+%token COL
+%token LCOL
+%token RCOL
+%token CCOL
+%token MARK
+%token LINEUP
+%token TYPE
+%token VCENTER
+%token PRIME
+%token SPLIT
+%token NOSPLIT
+%token UACCENT
+%token SPECIAL
+
+/* these are handled in the lexer */
+%token SPACE
+%token GFONT
+%token GSIZE
+%token DEFINE
+%token NDEFINE
+%token TDEFINE
+%token SDEFINE
+%token UNDEF
+%token IFDEF
+%token INCLUDE
+%token DELIM
+%token CHARTYPE
+%token SET
+%token GRFONT
+%token GBFONT
+
+/* The original eqn manual says that `left' is right associative. It's lying.
+Consider `left ( ~ left ( ~ right ) right )'. */
+
+%right LEFT
+%left RIGHT
+%right LPILE RPILE CPILE PILE TEXT QUOTED_TEXT MATRIX MARK LINEUP '^' '~' '\t' '{' SPLIT NOSPLIT
+%right FROM TO
+%left SQRT OVER SMALLOVER
+%right SUB SUP
+%right ROMAN BOLD ITALIC FAT FONT SIZE FWD BACK DOWN UP TYPE VCENTER SPECIAL
+%right BAR UNDER PRIME
+%left ACCENT UACCENT
+
+%type <b> mark from_to sqrt_over script simple equation nonsup
+%type <n> number
+%type <str> text delim
+%type <pb> pile_element_list pile_arg
+%type <mb> column_list
+%type <col> column column_arg column_element_list
+
+%%
+top:
+ /* empty */
+ | equation
+ { $1->top_level(); non_empty_flag = 1; }
+ ;
+
+equation:
+ mark
+ { $$ = $1; }
+ | equation mark
+ {
+ list_box *lb = $1->to_list_box();
+ if (!lb)
+ lb = new list_box($1);
+ lb->append($2);
+ $$ = lb;
+ }
+ ;
+
+mark:
+ from_to
+ { $$ = $1; }
+ | MARK mark
+ { $$ = make_mark_box($2); }
+ | LINEUP mark
+ { $$ = make_lineup_box($2); }
+ ;
+
+from_to:
+ sqrt_over %prec FROM
+ { $$ = $1; }
+ | sqrt_over TO from_to
+ { $$ = make_limit_box($1, 0, $3); }
+ | sqrt_over FROM sqrt_over
+ { $$ = make_limit_box($1, $3, 0); }
+ | sqrt_over FROM sqrt_over TO from_to
+ { $$ = make_limit_box($1, $3, $5); }
+ | sqrt_over FROM sqrt_over FROM from_to
+ { $$ = make_limit_box($1, make_limit_box($3, $5, 0), 0); }
+ ;
+
+sqrt_over:
+ script
+ { $$ = $1; }
+ | SQRT sqrt_over
+ { $$ = make_sqrt_box($2); }
+ | sqrt_over OVER sqrt_over
+ { $$ = make_over_box($1, $3); }
+ | sqrt_over SMALLOVER sqrt_over
+ { $$ = make_small_over_box($1, $3); }
+ ;
+
+script:
+ nonsup
+ { $$ = $1; }
+ | simple SUP script
+ { $$ = make_script_box($1, 0, $3); }
+ ;
+
+nonsup:
+ simple %prec SUP
+ { $$ = $1; }
+ | simple SUB nonsup
+ { $$ = make_script_box($1, $3, 0); }
+ | simple SUB simple SUP script
+ { $$ = make_script_box($1, $3, $5); }
+ ;
+
+simple:
+ TEXT
+ { $$ = split_text($1); }
+ | QUOTED_TEXT
+ { $$ = new quoted_text_box($1); }
+ | SPLIT QUOTED_TEXT
+ { $$ = split_text($2); }
+ | NOSPLIT TEXT
+ { $$ = new quoted_text_box($2); }
+ | '^'
+ { $$ = new half_space_box; }
+ | '~'
+ { $$ = new space_box; }
+ | '\t'
+ { $$ = new tab_box; }
+ | '{' equation '}'
+ { $$ = $2; }
+ | PILE pile_arg
+ { $2->set_alignment(CENTER_ALIGN); $$ = $2; }
+ | LPILE pile_arg
+ { $2->set_alignment(LEFT_ALIGN); $$ = $2; }
+ | RPILE pile_arg
+ { $2->set_alignment(RIGHT_ALIGN); $$ = $2; }
+ | CPILE pile_arg
+ { $2->set_alignment(CENTER_ALIGN); $$ = $2; }
+ | MATRIX '{' column_list '}'
+ { $$ = $3; }
+ | LEFT delim equation RIGHT delim
+ { $$ = make_delim_box($2, $3, $5); }
+ | LEFT delim equation
+ { $$ = make_delim_box($2, $3, 0); }
+ | simple BAR
+ { $$ = make_overline_box($1); }
+ | simple UNDER
+ { $$ = make_underline_box($1); }
+ | simple PRIME
+ { $$ = make_prime_box($1); }
+ | simple ACCENT simple
+ { $$ = make_accent_box($1, $3); }
+ | simple UACCENT simple
+ { $$ = make_uaccent_box($1, $3); }
+ | ROMAN simple
+ { $$ = new font_box(strsave(get_grfont()), $2); }
+ | BOLD simple
+ { $$ = new font_box(strsave(get_gbfont()), $2); }
+ | ITALIC simple
+ { $$ = new font_box(strsave(get_gfont()), $2); }
+ | FAT simple
+ { $$ = new fat_box($2); }
+ | FONT text simple
+ { $$ = new font_box($2, $3); }
+ | SIZE text simple
+ { $$ = new size_box($2, $3); }
+ | FWD number simple
+ { $$ = new hmotion_box($2, $3); }
+ | BACK number simple
+ { $$ = new hmotion_box(-$2, $3); }
+ | UP number simple
+ { $$ = new vmotion_box($2, $3); }
+ | DOWN number simple
+ { $$ = new vmotion_box(-$2, $3); }
+ | TYPE text simple
+ { $3->set_spacing_type($2); $$ = $3; }
+ | VCENTER simple
+ { $$ = new vcenter_box($2); }
+ | SPECIAL text simple
+ { $$ = make_special_box($2, $3); }
+ ;
+
+number:
+ text
+ {
+ int n;
+ if (sscanf($1, "%d", &n) == 1)
+ $$ = n;
+ a_delete $1;
+ }
+ ;
+
+pile_element_list:
+ equation
+ { $$ = new pile_box($1); }
+ | pile_element_list ABOVE equation
+ { $1->append($3); $$ = $1; }
+ ;
+
+pile_arg:
+ '{' pile_element_list '}'
+ { $$ = $2; }
+ | number '{' pile_element_list '}'
+ { $3->set_space($1); $$ = $3; }
+ ;
+
+column_list:
+ column
+ { $$ = new matrix_box($1); }
+ | column_list column
+ { $1->append($2); $$ = $1; }
+ ;
+
+column_element_list:
+ equation
+ { $$ = new column($1); }
+ | column_element_list ABOVE equation
+ { $1->append($3); $$ = $1; }
+ ;
+
+column_arg:
+ '{' column_element_list '}'
+ { $$ = $2; }
+ | number '{' column_element_list '}'
+ { $3->set_space($1); $$ = $3; }
+ ;
+
+column:
+ COL column_arg
+ { $2->set_alignment(CENTER_ALIGN); $$ = $2; }
+ | LCOL column_arg
+ { $2->set_alignment(LEFT_ALIGN); $$ = $2; }
+ | RCOL column_arg
+ { $2->set_alignment(RIGHT_ALIGN); $$ = $2; }
+ | CCOL column_arg
+ { $2->set_alignment(CENTER_ALIGN); $$ = $2; }
+ ;
+
+text: TEXT
+ { $$ = $1; }
+ | QUOTED_TEXT
+ { $$ = $1; }
+ ;
+
+delim:
+ text
+ { $$ = $1; }
+ | '{'
+ { $$ = strsave("{"); }
+ | '}'
+ { $$ = strsave("}"); }
+ ;
+
+%%
diff --git a/contrib/groff/src/preproc/eqn/eqn_tab.h b/contrib/groff/src/preproc/eqn/eqn_tab.h
new file mode 100644
index 000000000000..9a8b3cb22595
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/eqn_tab.h
@@ -0,0 +1,67 @@
+#define OVER 257
+#define SMALLOVER 258
+#define SQRT 259
+#define SUB 260
+#define SUP 261
+#define LPILE 262
+#define RPILE 263
+#define CPILE 264
+#define PILE 265
+#define LEFT 266
+#define RIGHT 267
+#define TO 268
+#define FROM 269
+#define SIZE 270
+#define FONT 271
+#define ROMAN 272
+#define BOLD 273
+#define ITALIC 274
+#define FAT 275
+#define ACCENT 276
+#define BAR 277
+#define UNDER 278
+#define ABOVE 279
+#define TEXT 280
+#define QUOTED_TEXT 281
+#define FWD 282
+#define BACK 283
+#define DOWN 284
+#define UP 285
+#define MATRIX 286
+#define COL 287
+#define LCOL 288
+#define RCOL 289
+#define CCOL 290
+#define MARK 291
+#define LINEUP 292
+#define TYPE 293
+#define VCENTER 294
+#define PRIME 295
+#define SPLIT 296
+#define NOSPLIT 297
+#define UACCENT 298
+#define SPECIAL 299
+#define SPACE 300
+#define GFONT 301
+#define GSIZE 302
+#define DEFINE 303
+#define NDEFINE 304
+#define TDEFINE 305
+#define SDEFINE 306
+#define UNDEF 307
+#define IFDEF 308
+#define INCLUDE 309
+#define DELIM 310
+#define CHARTYPE 311
+#define SET 312
+#define GRFONT 313
+#define GBFONT 314
+typedef union {
+ char *str;
+ box *b;
+ pile_box *pb;
+ matrix_box *mb;
+ int n;
+ column *col;
+} YYSTYPE;
+extern YYSTYPE yylval;
diff --git a/contrib/groff/src/preproc/eqn/lex.cc b/contrib/groff/src/preproc/eqn/lex.cc
new file mode 100644
index 000000000000..25faec2bcecf
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/lex.cc
@@ -0,0 +1,1165 @@
+// -*- 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. */
+
+#include "eqn.h"
+#include "eqn_tab.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+struct definition {
+ char is_macro;
+ char is_simple;
+ union {
+ int tok;
+ char *contents;
+ };
+ definition();
+ ~definition();
+};
+
+definition::definition() : is_macro(1), is_simple(0)
+{
+ contents = 0;
+}
+
+definition::~definition()
+{
+ if (is_macro)
+ a_delete contents;
+}
+
+declare_ptable(definition)
+implement_ptable(definition)
+
+PTABLE(definition) macro_table;
+
+static struct {
+ const char *name;
+ int token;
+} token_table[] = {
+ { "over", OVER },
+ { "smallover", SMALLOVER },
+ { "sqrt", SQRT },
+ { "sub", SUB },
+ { "sup", SUP },
+ { "lpile", LPILE },
+ { "rpile", RPILE },
+ { "cpile", CPILE },
+ { "pile", PILE },
+ { "left", LEFT },
+ { "right", RIGHT },
+ { "to", TO },
+ { "from", FROM },
+ { "size", SIZE },
+ { "font", FONT },
+ { "roman", ROMAN },
+ { "bold", BOLD },
+ { "italic", ITALIC },
+ { "fat", FAT },
+ { "bar", BAR },
+ { "under", UNDER },
+ { "accent", ACCENT },
+ { "uaccent", UACCENT },
+ { "above", ABOVE },
+ { "fwd", FWD },
+ { "back", BACK },
+ { "down", DOWN },
+ { "up", UP },
+ { "matrix", MATRIX },
+ { "col", COL },
+ { "lcol", LCOL },
+ { "rcol", RCOL },
+ { "ccol", CCOL },
+ { "mark", MARK },
+ { "lineup", LINEUP },
+ { "space", SPACE },
+ { "gfont", GFONT },
+ { "gsize", GSIZE },
+ { "define", DEFINE },
+ { "sdefine", SDEFINE },
+ { "ndefine", NDEFINE },
+ { "tdefine", TDEFINE },
+ { "undef", UNDEF },
+ { "ifdef", IFDEF },
+ { "include", INCLUDE },
+ { "copy", INCLUDE },
+ { "delim", DELIM },
+ { "chartype", CHARTYPE },
+ { "type", TYPE },
+ { "vcenter", VCENTER },
+ { "set", SET },
+ { "opprime", PRIME },
+ { "grfont", GRFONT },
+ { "gbfont", GBFONT },
+ { "split", SPLIT },
+ { "nosplit", NOSPLIT },
+ { "special", SPECIAL },
+};
+
+static struct {
+ const char *name;
+ const char *def;
+} def_table[] = {
+ { "ALPHA", "\\(*A" },
+ { "BETA", "\\(*B" },
+ { "CHI", "\\(*X" },
+ { "DELTA", "\\(*D" },
+ { "EPSILON", "\\(*E" },
+ { "ETA", "\\(*Y" },
+ { "GAMMA", "\\(*G" },
+ { "IOTA", "\\(*I" },
+ { "KAPPA", "\\(*K" },
+ { "LAMBDA", "\\(*L" },
+ { "MU", "\\(*M" },
+ { "NU", "\\(*N" },
+ { "OMEGA", "\\(*W" },
+ { "OMICRON", "\\(*O" },
+ { "PHI", "\\(*F" },
+ { "PI", "\\(*P" },
+ { "PSI", "\\(*Q" },
+ { "RHO", "\\(*R" },
+ { "SIGMA", "\\(*S" },
+ { "TAU", "\\(*T" },
+ { "THETA", "\\(*H" },
+ { "UPSILON", "\\(*U" },
+ { "XI", "\\(*C" },
+ { "ZETA", "\\(*Z" },
+ { "Alpha", "\\(*A" },
+ { "Beta", "\\(*B" },
+ { "Chi", "\\(*X" },
+ { "Delta", "\\(*D" },
+ { "Epsilon", "\\(*E" },
+ { "Eta", "\\(*Y" },
+ { "Gamma", "\\(*G" },
+ { "Iota", "\\(*I" },
+ { "Kappa", "\\(*K" },
+ { "Lambda", "\\(*L" },
+ { "Mu", "\\(*M" },
+ { "Nu", "\\(*N" },
+ { "Omega", "\\(*W" },
+ { "Omicron", "\\(*O" },
+ { "Phi", "\\(*F" },
+ { "Pi", "\\(*P" },
+ { "Psi", "\\(*Q" },
+ { "Rho", "\\(*R" },
+ { "Sigma", "\\(*S" },
+ { "Tau", "\\(*T" },
+ { "Theta", "\\(*H" },
+ { "Upsilon", "\\(*U" },
+ { "Xi", "\\(*C" },
+ { "Zeta", "\\(*Z" },
+ { "alpha", "\\(*a" },
+ { "beta", "\\(*b" },
+ { "chi", "\\(*x" },
+ { "delta", "\\(*d" },
+ { "epsilon", "\\(*e" },
+ { "eta", "\\(*y" },
+ { "gamma", "\\(*g" },
+ { "iota", "\\(*i" },
+ { "kappa", "\\(*k" },
+ { "lambda", "\\(*l" },
+ { "mu", "\\(*m" },
+ { "nu", "\\(*n" },
+ { "omega", "\\(*w" },
+ { "omicron", "\\(*o" },
+ { "phi", "\\(*f" },
+ { "pi", "\\(*p" },
+ { "psi", "\\(*q" },
+ { "rho", "\\(*r" },
+ { "sigma", "\\(*s" },
+ { "tau", "\\(*t" },
+ { "theta", "\\(*h" },
+ { "upsilon", "\\(*u" },
+ { "xi", "\\(*c" },
+ { "zeta", "\\(*z" },
+ { "max", "{type \"operator\" roman \"max\"}" },
+ { "min", "{type \"operator\" roman \"min\"}" },
+ { "lim", "{type \"operator\" roman \"lim\"}" },
+ { "sin", "{type \"operator\" roman \"sin\"}" },
+ { "cos", "{type \"operator\" roman \"cos\"}" },
+ { "tan", "{type \"operator\" roman \"tan\"}" },
+ { "sinh", "{type \"operator\" roman \"sinh\"}" },
+ { "cosh", "{type \"operator\" roman \"cosh\"}" },
+ { "tanh", "{type \"operator\" roman \"tanh\"}" },
+ { "arc", "{type \"operator\" roman \"arc\"}" },
+ { "log", "{type \"operator\" roman \"log\"}" },
+ { "ln", "{type \"operator\" roman \"ln\"}" },
+ { "exp", "{type \"operator\" roman \"exp\"}" },
+ { "Re", "{type \"operator\" roman \"Re\"}" },
+ { "Im", "{type \"operator\" roman \"Im\"}" },
+ { "det", "{type \"operator\" roman \"det\"}" },
+ { "and", "{roman \"and\"}" },
+ { "if", "{roman \"if\"}" },
+ { "for", "{roman \"for\"}" },
+ { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
+ { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
+ { "int", "{type \"operator\" vcenter size +8 \\(is}" },
+ { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
+ { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
+ { "times", "type \"binary\" \\(mu" },
+ { "ldots", "type \"inner\" { . . . }" },
+ { "inf", "\\(if" },
+ { "partial", "\\(pd" },
+ { "nothing", "\"\"" },
+ { "half", "{1 smallover 2}" },
+ { "hat_def", "roman \"^\"" },
+ { "hat", "accent { hat_def }" },
+ { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" },
+ { "dot", "accent { dot_def }" },
+ { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" },
+ { "dotdot", "accent { dotdot_def }" },
+ { "tilde_def", "\"~\"" },
+ { "tilde", "accent { tilde_def }" },
+ { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" },
+ { "utilde", "uaccent { utilde_def }" },
+ { "vec_def", "up 52 size -5 \\(->" },
+ { "vec", "accent { vec_def }" },
+ { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" },
+ { "dyad", "accent { dyad_def }" },
+ { "==", "type \"relation\" \\(==" },
+ { "!=", "type \"relation\" \\(!=" },
+ { "+-", "type \"binary\" \\(+-" },
+ { "->", "type \"relation\" \\(->" },
+ { "<-", "type \"relation\" \\(<-" },
+ { "<<", "{ < back 20 < }" },
+ { ">>", "{ > back 20 > }" },
+ { "...", "type \"inner\" vcenter { . . . }" },
+ { "prime", "'" },
+ { "approx", "type \"relation\" \"\\(~=\"" },
+ { "grad", "\\(gr" },
+ { "del", "\\(gr" },
+ { "cdot", "type \"binary\" vcenter ." },
+ { "dollar", "$" },
+};
+
+void init_table(const char *device)
+{
+ int i;
+ for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
+ definition *def = new definition;
+ def->is_macro = 0;
+ def->tok = token_table[i].token;
+ macro_table.define(token_table[i].name, def);
+ }
+ for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) {
+ definition *def = new definition;
+ def->is_macro = 1;
+ def->contents = strsave(def_table[i].def);
+ def->is_simple = 1;
+ macro_table.define(def_table[i].name, def);
+ }
+ definition *def = new definition;
+ def->is_macro = 1;
+ def->contents = strsave("1");
+ macro_table.define(device, def);
+}
+
+class input {
+ input *next;
+public:
+ input(input *p);
+ virtual ~input();
+ virtual int get() = 0;
+ virtual int peek() = 0;
+ virtual int get_location(char **, int *);
+
+ friend int get_char();
+ friend int peek_char();
+ friend int get_location(char **, int *);
+ friend void init_lex(const char *str, const char *filename, int lineno);
+};
+
+class file_input : public input {
+ FILE *fp;
+ char *filename;
+ int lineno;
+ string line;
+ const char *ptr;
+ int read_line();
+public:
+ file_input(FILE *, const char *, input *);
+ ~file_input();
+ int get();
+ int peek();
+ int get_location(char **, int *);
+};
+
+
+class macro_input : public input {
+ char *s;
+ char *p;
+public:
+ macro_input(const char *, input *);
+ ~macro_input();
+ int get();
+ int peek();
+};
+
+class top_input : public macro_input {
+ char *filename;
+ int lineno;
+ public:
+ top_input(const char *, const char *, int, input *);
+ ~top_input();
+ int get();
+ int get_location(char **, int *);
+};
+
+class argument_macro_input: public input {
+ char *s;
+ char *p;
+ char *ap;
+ int argc;
+ char *argv[9];
+public:
+ argument_macro_input(const char *, int, char **, input *);
+ ~argument_macro_input();
+ int get();
+ int peek();
+};
+
+input::input(input *x) : next(x)
+{
+}
+
+input::~input()
+{
+}
+
+int input::get_location(char **, int *)
+{
+ return 0;
+}
+
+file_input::file_input(FILE *f, const char *fn, input *p)
+: input(p), lineno(0), ptr("")
+{
+ fp = f;
+ filename = strsave(fn);
+}
+
+file_input::~file_input()
+{
+ a_delete filename;
+ fclose(fp);
+}
+
+int file_input::read_line()
+{
+ for (;;) {
+ line.clear();
+ lineno++;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ else if (illegal_input_char(c))
+ lex_error("illegal input character code %1", c);
+ else {
+ line += char(c);
+ if (c == '\n')
+ break;
+ }
+ }
+ if (line.length() == 0)
+ return 0;
+ if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
+ && (line[2] == 'Q' || line[2] == 'N')
+ && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
+ || compatible_flag))) {
+ line += '\0';
+ ptr = line.contents();
+ return 1;
+ }
+ }
+}
+
+int file_input::get()
+{
+ if (*ptr != '\0' || read_line())
+ return *ptr++ & 0377;
+ else
+ return EOF;
+}
+
+int file_input::peek()
+{
+ if (*ptr != '\0' || read_line())
+ return *ptr;
+ else
+ return EOF;
+}
+
+int file_input::get_location(char **fnp, int *lnp)
+{
+ *fnp = filename;
+ *lnp = lineno;
+ return 1;
+}
+
+macro_input::macro_input(const char *str, input *x) : input(x)
+{
+ p = s = strsave(str);
+}
+
+macro_input::~macro_input()
+{
+ a_delete s;
+}
+
+int macro_input::get()
+{
+ if (p == 0 || *p == '\0')
+ return EOF;
+ else
+ return *p++ & 0377;
+}
+
+int macro_input::peek()
+{
+ if (p == 0 || *p == '\0')
+ return EOF;
+ else
+ return *p & 0377;
+}
+
+top_input::top_input(const char *str, const char *fn, int ln, input *x)
+: macro_input(str, x), lineno(ln)
+{
+ filename = strsave(fn);
+}
+
+top_input::~top_input()
+{
+ a_delete filename;
+}
+
+int top_input::get()
+{
+ int c = macro_input::get();
+ if (c == '\n')
+ lineno++;
+ return c;
+}
+
+int top_input::get_location(char **fnp, int *lnp)
+{
+ *fnp = filename;
+ *lnp = lineno;
+ return 1;
+}
+
+// Character representing $1. Must be illegal input character.
+#define ARG1 14
+
+argument_macro_input::argument_macro_input(const char *body, int ac,
+ char **av, input *x)
+: input(x), ap(0), argc(ac)
+{
+ int i;
+ for (i = 0; i < argc; i++)
+ argv[i] = av[i];
+ p = s = strsave(body);
+ int j = 0;
+ for (i = 0; s[i] != '\0'; i++)
+ if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
+ if (s[i+1] != '0')
+ s[j++] = ARG1 + s[++i] - '1';
+ }
+ else
+ s[j++] = s[i];
+ s[j] = '\0';
+}
+
+
+argument_macro_input::~argument_macro_input()
+{
+ for (int i = 0; i < argc; i++)
+ a_delete argv[i];
+ a_delete s;
+}
+
+int argument_macro_input::get()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return *ap++ & 0377;
+ ap = 0;
+ }
+ if (p == 0)
+ return EOF;
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
+ ap = argv[i];
+ return *ap++ & 0377;
+ }
+ }
+ if (*p == '\0')
+ return EOF;
+ return *p++ & 0377;
+}
+
+int argument_macro_input::peek()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return *ap & 0377;
+ ap = 0;
+ }
+ if (p == 0)
+ return EOF;
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
+ ap = argv[i];
+ return *ap & 0377;
+ }
+ }
+ if (*p == '\0')
+ return EOF;
+ return *p & 0377;
+}
+
+static input *current_input = 0;
+
+/* we insert a newline between input from different levels */
+
+int get_char()
+{
+ if (current_input == 0)
+ return EOF;
+ else {
+ int c = current_input->get();
+ if (c != EOF)
+ return c;
+ else {
+ input *tem = current_input;
+ current_input = current_input->next;
+ delete tem;
+ return '\n';
+ }
+ }
+}
+
+int peek_char()
+{
+ if (current_input == 0)
+ return EOF;
+ else {
+ int c = current_input->peek();
+ if (c != EOF)
+ return c;
+ else
+ return '\n';
+ }
+}
+
+int get_location(char **fnp, int *lnp)
+{
+ for (input *p = current_input; p; p = p->next)
+ if (p->get_location(fnp, lnp))
+ return 1;
+ return 0;
+}
+
+string token_buffer;
+const int NCONTEXT = 4;
+string context_ring[NCONTEXT];
+int context_index = 0;
+
+void flush_context()
+{
+ for (int i = 0; i < NCONTEXT; i++)
+ context_ring[i] = "";
+ context_index = 0;
+}
+
+void show_context()
+{
+ int i = context_index;
+ fputs(" context is\n\t", stderr);
+ for (;;) {
+ int j = (i + 1) % NCONTEXT;
+ if (j == context_index) {
+ fputs(">>> ", stderr);
+ put_string(context_ring[i], stderr);
+ fputs(" <<<", stderr);
+ break;
+ }
+ else if (context_ring[i].length() > 0) {
+ put_string(context_ring[i], stderr);
+ putc(' ', stderr);
+ }
+ i = j;
+ }
+ putc('\n', stderr);
+}
+
+void add_context(const string &s)
+{
+ context_ring[context_index] = s;
+ context_index = (context_index + 1) % NCONTEXT;
+}
+
+void add_context(char c)
+{
+ context_ring[context_index] = c;
+ context_index = (context_index + 1) % NCONTEXT;
+}
+
+void add_quoted_context(const string &s)
+{
+ string &r = context_ring[context_index];
+ r = '"';
+ for (int i = 0; i < s.length(); i++)
+ if (s[i] == '"')
+ r += "\\\"";
+ else
+ r += s[i];
+ r += '"';
+ context_index = (context_index + 1) % NCONTEXT;
+}
+
+void init_lex(const char *str, const char *filename, int lineno)
+{
+ while (current_input != 0) {
+ input *tem = current_input;
+ current_input = current_input->next;
+ delete tem;
+ }
+ current_input = new top_input(str, filename, lineno, 0);
+ flush_context();
+}
+
+
+void get_delimited_text()
+{
+ char *filename;
+ int lineno;
+ int got_location = get_location(&filename, &lineno);
+ int start = get_char();
+ while (start == ' ' || start == '\t' || start == '\n')
+ start = get_char();
+ token_buffer.clear();
+ if (start == EOF) {
+ if (got_location)
+ error_with_file_and_line(filename, lineno,
+ "end of input while defining macro");
+ else
+ error("end of input while defining macro");
+ return;
+ }
+ for (;;) {
+ int c = get_char();
+ if (c == EOF) {
+ if (got_location)
+ error_with_file_and_line(filename, lineno,
+ "end of input while defining macro");
+ else
+ error("end of input while defining macro");
+ add_context(start + token_buffer);
+ return;
+ }
+ if (c == start)
+ break;
+ token_buffer += char(c);
+ }
+ add_context(start + token_buffer + start);
+}
+
+void interpolate_macro_with_args(const char *body)
+{
+ char *argv[9];
+ int argc = 0;
+ int i;
+ for (i = 0; i < 9; i++)
+ argv[i] = 0;
+ int level = 0;
+ int c;
+ do {
+ token_buffer.clear();
+ for (;;) {
+ c = get_char();
+ if (c == EOF) {
+ lex_error("end of input while scanning macro arguments");
+ break;
+ }
+ if (level == 0 && (c == ',' || c == ')')) {
+ if (token_buffer.length() > 0) {
+ token_buffer += '\0';
+ argv[argc] = strsave(token_buffer.contents());
+ }
+ // for `foo()', argc = 0
+ if (argc > 0 || c != ')' || i > 0)
+ argc++;
+ break;
+ }
+ token_buffer += char(c);
+ if (c == '(')
+ level++;
+ else if (c == ')')
+ level--;
+ }
+ } while (c != ')' && c != EOF);
+ current_input = new argument_macro_input(body, argc, argv, current_input);
+}
+
+/* If lookup flag is non-zero the token will be looked up to see
+if it is macro. If it's 1, it will looked up to see if it's a token.
+*/
+
+int get_token(int lookup_flag = 0)
+{
+ for (;;) {
+ int c = get_char();
+ while (c == ' ' || c == '\n')
+ c = get_char();
+ switch (c) {
+ case EOF:
+ {
+ add_context("end of input");
+ }
+ return 0;
+ case '"':
+ {
+ int quoted = 0;
+ token_buffer.clear();
+ for (;;) {
+ c = get_char();
+ if (c == EOF) {
+ lex_error("missing \"");
+ break;
+ }
+ else if (c == '\n') {
+ lex_error("newline before end of quoted text");
+ break;
+ }
+ else if (c == '"') {
+ if (!quoted)
+ break;
+ token_buffer[token_buffer.length() - 1] = '"';
+ quoted = 0;
+ }
+ else {
+ token_buffer += c;
+ quoted = quoted ? 0 : c == '\\';
+ }
+ }
+ }
+ add_quoted_context(token_buffer);
+ return QUOTED_TEXT;
+ case '{':
+ case '}':
+ case '^':
+ case '~':
+ case '\t':
+ add_context(c);
+ return c;
+ default:
+ {
+ int break_flag = 0;
+ int quoted = 0;
+ token_buffer.clear();
+ if (c == '\\')
+ quoted = 1;
+ else
+ token_buffer += c;
+ int done = 0;
+ while (!done) {
+ c = peek_char();
+ if (!quoted && lookup_flag != 0 && c == '(') {
+ token_buffer += '\0';
+ definition *def = macro_table.lookup(token_buffer.contents());
+ if (def && def->is_macro && !def->is_simple) {
+ (void)get_char(); // skip initial '('
+ interpolate_macro_with_args(def->contents);
+ break_flag = 1;
+ break;
+ }
+ token_buffer.set_length(token_buffer.length() - 1);
+ }
+ if (quoted) {
+ quoted = 0;
+ switch (c) {
+ case EOF:
+ lex_error("`\\' ignored at end of equation");
+ done = 1;
+ break;
+ case '\n':
+ lex_error("`\\' ignored because followed by newline");
+ done = 1;
+ break;
+ case '\t':
+ lex_error("`\\' ignored because followed by tab");
+ done = 1;
+ break;
+ case '"':
+ (void)get_char();
+ token_buffer += '"';
+ break;
+ default:
+ (void)get_char();
+ token_buffer += '\\';
+ token_buffer += c;
+ break;
+ }
+ }
+ else {
+ switch (c) {
+ case EOF:
+ case '{':
+ case '}':
+ case '^':
+ case '~':
+ case '"':
+ case ' ':
+ case '\t':
+ case '\n':
+ done = 1;
+ break;
+ case '\\':
+ (void)get_char();
+ quoted = 1;
+ break;
+ default:
+ (void)get_char();
+ token_buffer += char(c);
+ break;
+ }
+ }
+ }
+ if (break_flag || token_buffer.length() == 0)
+ break;
+ if (lookup_flag != 0) {
+ token_buffer += '\0';
+ definition *def = macro_table.lookup(token_buffer.contents());
+ token_buffer.set_length(token_buffer.length() - 1);
+ if (def) {
+ if (def->is_macro) {
+ current_input = new macro_input(def->contents, current_input);
+ break;
+ }
+ else if (lookup_flag == 1) {
+ add_context(token_buffer);
+ return def->tok;
+ }
+ }
+ }
+ add_context(token_buffer);
+ return TEXT;
+ }
+ }
+ }
+}
+
+void do_include()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad filename for include");
+ return;
+ }
+ token_buffer += '\0';
+ const char *filename = token_buffer.contents();
+ errno = 0;
+ FILE *fp = fopen(filename, "r");
+ if (fp == 0) {
+ lex_error("can't open included file `%1'", filename);
+ return;
+ }
+ current_input = new file_input(fp, filename, current_input);
+}
+
+void ignore_definition()
+{
+ int t = get_token();
+ if (t != TEXT) {
+ lex_error("bad definition");
+ return;
+ }
+ get_delimited_text();
+}
+
+void do_definition(int is_simple)
+{
+ int t = get_token();
+ if (t != TEXT) {
+ lex_error("bad definition");
+ return;
+ }
+ token_buffer += '\0';
+ const char *name = token_buffer.contents();
+ definition *def = macro_table.lookup(name);
+ if (def == 0) {
+ def = new definition;
+ macro_table.define(name, def);
+ }
+ else if (def->is_macro) {
+ a_delete def->contents;
+ }
+ get_delimited_text();
+ token_buffer += '\0';
+ def->is_macro = 1;
+ def->contents = strsave(token_buffer.contents());
+ def->is_simple = is_simple;
+}
+
+void do_undef()
+{
+ int t = get_token();
+ if (t != TEXT) {
+ lex_error("bad undef command");
+ return;
+ }
+ token_buffer += '\0';
+ macro_table.define(token_buffer.contents(), 0);
+}
+
+void do_gsize()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad argument to gsize command");
+ return;
+ }
+ token_buffer += '\0';
+ if (!set_gsize(token_buffer.contents()))
+ lex_error("invalid size `%1'", token_buffer.contents());
+}
+
+void do_gfont()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad argument to gfont command");
+ return;
+ }
+ token_buffer += '\0';
+ set_gfont(token_buffer.contents());
+}
+
+void do_grfont()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad argument to grfont command");
+ return;
+ }
+ token_buffer += '\0';
+ set_grfont(token_buffer.contents());
+}
+
+void do_gbfont()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad argument to gbfont command");
+ return;
+ }
+ token_buffer += '\0';
+ set_gbfont(token_buffer.contents());
+}
+
+void do_space()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad argument to space command");
+ return;
+ }
+ token_buffer += '\0';
+ char *ptr;
+ long n = strtol(token_buffer.contents(), &ptr, 10);
+ if (n == 0 && ptr == token_buffer.contents())
+ lex_error("bad argument `%1' to space command", token_buffer.contents());
+ else
+ set_space(int(n));
+}
+
+void do_ifdef()
+{
+ int t = get_token();
+ if (t != TEXT) {
+ lex_error("bad ifdef");
+ return;
+ }
+ token_buffer += '\0';
+ definition *def = macro_table.lookup(token_buffer.contents());
+ int result = def && def->is_macro && !def->is_simple;
+ get_delimited_text();
+ if (result) {
+ token_buffer += '\0';
+ current_input = new macro_input(token_buffer.contents(), current_input);
+ }
+}
+
+void do_delim()
+{
+ int c = get_char();
+ while (c == ' ' || c == '\n')
+ c = get_char();
+ int d;
+ if (c == EOF || (d = get_char()) == EOF)
+ lex_error("end of file while reading argument to `delim'");
+ else {
+ if (c == 'o' && d == 'f' && peek_char() == 'f') {
+ (void)get_char();
+ start_delim = end_delim = '\0';
+ }
+ else {
+ start_delim = c;
+ end_delim = d;
+ }
+ }
+}
+
+void do_chartype()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad chartype");
+ return;
+ }
+ token_buffer += '\0';
+ string type = token_buffer;
+ t = get_token();
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad chartype");
+ return;
+ }
+ token_buffer += '\0';
+ set_char_type(type.contents(), strsave(token_buffer.contents()));
+}
+
+void do_set()
+{
+ int t = get_token(2);
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad set");
+ return;
+ }
+ token_buffer += '\0';
+ string param = token_buffer;
+ t = get_token();
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad set");
+ return;
+ }
+ token_buffer += '\0';
+ int n;
+ if (sscanf(&token_buffer[0], "%d", &n) != 1) {
+ lex_error("bad number `%1'", token_buffer.contents());
+ return;
+ }
+ set_param(param.contents(), n);
+}
+
+int yylex()
+{
+ for (;;) {
+ int tk = get_token(1);
+ switch(tk) {
+ case UNDEF:
+ do_undef();
+ break;
+ case SDEFINE:
+ do_definition(1);
+ break;
+ case DEFINE:
+ do_definition(0);
+ break;
+ case TDEFINE:
+ if (!nroff)
+ do_definition(0);
+ else
+ ignore_definition();
+ break;
+ case NDEFINE:
+ if (nroff)
+ do_definition(0);
+ else
+ ignore_definition();
+ break;
+ case GSIZE:
+ do_gsize();
+ break;
+ case GFONT:
+ do_gfont();
+ break;
+ case GRFONT:
+ do_grfont();
+ break;
+ case GBFONT:
+ do_gbfont();
+ break;
+ case SPACE:
+ do_space();
+ break;
+ case INCLUDE:
+ do_include();
+ break;
+ case IFDEF:
+ do_ifdef();
+ break;
+ case DELIM:
+ do_delim();
+ break;
+ case CHARTYPE:
+ do_chartype();
+ break;
+ case SET:
+ do_set();
+ break;
+ case QUOTED_TEXT:
+ case TEXT:
+ token_buffer += '\0';
+ yylval.str = strsave(token_buffer.contents());
+ // fall through
+ default:
+ return tk;
+ }
+ }
+}
+
+void lex_error(const char *message,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ char *filename;
+ int lineno;
+ if (!get_location(&filename, &lineno))
+ error(message, arg1, arg2, arg3);
+ else
+ error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
+}
+
+void yyerror(const char *s)
+{
+ char *filename;
+ int lineno;
+ if (!get_location(&filename, &lineno))
+ error(s);
+ else
+ error_with_file_and_line(filename, lineno, s);
+ show_context();
+}
+
diff --git a/contrib/groff/src/preproc/eqn/limit.cc b/contrib/groff/src/preproc/eqn/limit.cc
new file mode 100644
index 000000000000..046885d2a580
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/limit.cc
@@ -0,0 +1,195 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+class limit_box : public box {
+private:
+ box *p;
+ box *from;
+ box *to;
+public:
+ limit_box(box *, box *, box *);
+ ~limit_box();
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+ void check_tabs(int);
+};
+
+box *make_limit_box(box *pp, box *qq, box *rr)
+{
+ return new limit_box(pp, qq, rr);
+}
+
+limit_box::limit_box(box *pp, box *qq, box *rr)
+: p(pp), from(qq), to(rr)
+{
+ spacing_type = p->spacing_type;
+}
+
+limit_box::~limit_box()
+{
+ delete p;
+ delete from;
+ delete to;
+}
+
+int limit_box::compute_metrics(int style)
+{
+ printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid);
+ if (!(style <= SCRIPT_STYLE && one_size_reduction_flag))
+ set_script_size();
+ printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid);
+ int res = 0;
+ int mark_uid = -1;
+ if (from != 0) {
+ res = from->compute_metrics(cramped_style(script_style(style)));
+ if (res)
+ mark_uid = from->uid;
+ }
+ if (to != 0) {
+ int r = to->compute_metrics(script_style(style));
+ if (res && r)
+ error("multiple marks and lineups");
+ else {
+ mark_uid = to->uid;
+ res = r;
+ }
+ }
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ int r = p->compute_metrics(style);
+ p->compute_subscript_kern();
+ if (res && r)
+ error("multiple marks and lineups");
+ else {
+ mark_uid = p->uid;
+ res = r;
+ }
+ printf(".nr " LEFT_WIDTH_FORMAT " "
+ "0\\n[" WIDTH_FORMAT "]",
+ uid, p->uid);
+ if (from != 0)
+ printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])",
+ p->uid, from->uid);
+ if (to != 0)
+ printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])",
+ p->uid, to->uid);
+ printf("/2\n");
+ printf(".nr " WIDTH_FORMAT " "
+ "0\\n[" WIDTH_FORMAT "]",
+ uid, p->uid);
+ if (from != 0)
+ printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])",
+ p->uid, from->uid);
+ if (to != 0)
+ printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])",
+ p->uid, to->uid);
+ printf("/2+\\n[" LEFT_WIDTH_FORMAT "]\n", uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid);
+ if (to != 0)
+ printf(">?\\n[" WIDTH_FORMAT "]", to->uid);
+ if (from != 0)
+ printf(">?\\n[" WIDTH_FORMAT "]", from->uid);
+ printf("\n");
+ if (res)
+ printf(".nr " MARK_REG " +(\\n[" LEFT_WIDTH_FORMAT "]"
+ "-(\\n[" WIDTH_FORMAT "]/2))\n",
+ uid, mark_uid);
+ if (to != 0) {
+ printf(".nr " SUP_RAISE_FORMAT " %dM+\\n[" DEPTH_FORMAT
+ "]>?%dM+\\n[" HEIGHT_FORMAT "]\n",
+ uid, big_op_spacing1, to->uid, big_op_spacing3, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n["
+ HEIGHT_FORMAT "]+%dM\n",
+ uid, uid, to->uid, big_op_spacing5);
+ }
+ else
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ if (from != 0) {
+ printf(".nr " SUB_LOWER_FORMAT " %dM+\\n[" HEIGHT_FORMAT
+ "]>?%dM+\\n[" DEPTH_FORMAT "]\n",
+ uid, big_op_spacing2, from->uid, big_op_spacing4, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n["
+ DEPTH_FORMAT "]+%dM\n",
+ uid, uid, from->uid, big_op_spacing5);
+ }
+ else
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return res;
+}
+
+void limit_box::output()
+{
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+ if (to != 0) {
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u"
+ "+(-\\n[" WIDTH_FORMAT "]u+\\n[" SUB_KERN_FORMAT "]u/2u)'",
+ uid, to->uid, p->uid);
+ to->output();
+ printf(DELIMITER_CHAR);
+ }
+ if (from != 0) {
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u"
+ "+(-\\n[" SUB_KERN_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, p->uid, from->uid);
+ from->output();
+ printf(DELIMITER_CHAR);
+ }
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u"
+ "-(\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, p->uid);
+ p->output();
+ printf(DELIMITER_CHAR);
+ printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
+}
+
+void limit_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " }");
+ if (from) {
+ fprintf(stderr, " from { ");
+ from->debug_print();
+ fprintf(stderr, " }");
+ }
+ if (to) {
+ fprintf(stderr, " to { ");
+ to->debug_print();
+ fprintf(stderr, " }");
+ }
+}
+
+void limit_box::check_tabs(int level)
+{
+ if (to)
+ to->check_tabs(level + 1);
+ if (from)
+ from->check_tabs(level + 1);
+ p->check_tabs(level + 1);
+}
diff --git a/contrib/groff/src/preproc/eqn/list.cc b/contrib/groff/src/preproc/eqn/list.cc
new file mode 100644
index 000000000000..1118fa1b1660
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/list.cc
@@ -0,0 +1,237 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+list_box *box::to_list_box()
+{
+ return 0;
+}
+
+list_box *list_box::to_list_box()
+{
+ return this;
+}
+
+void list_box::append(box *pp)
+{
+ list_box *q = pp->to_list_box();
+ if (q == 0)
+ list.append(pp);
+ else {
+ for (int i = 0; i < q->list.len; i++) {
+ list.append(q->list.p[i]);
+ q->list.p[i] = 0;
+ }
+ q->list.len = 0;
+ delete q;
+ }
+}
+
+list_box::list_box(box *pp) : list(pp), sty(-1)
+{
+ list_box *q = pp->to_list_box();
+ if (q != 0) {
+ // flatten it
+ list.p[0] = q->list.p[0];
+ for (int i = 1; i < q->list.len; i++) {
+ list.append(q->list.p[i]);
+ q->list.p[i] = 0;
+ }
+ q->list.len = 0;
+ delete q;
+ }
+}
+
+static int compute_spacing(int is_script, int left, int right)
+{
+ if (left == SUPPRESS_TYPE || right == SUPPRESS_TYPE)
+ return 0;
+ if (left == PUNCTUATION_TYPE)
+ return is_script ? 0 : thin_space;
+ if (left == OPENING_TYPE || right == CLOSING_TYPE)
+ return 0;
+ if (right == BINARY_TYPE || left == BINARY_TYPE)
+ return is_script ? 0 : medium_space;
+ if (right == RELATION_TYPE) {
+ if (left == RELATION_TYPE)
+ return 0;
+ else
+ return is_script ? 0 : thick_space;
+ }
+ if (left == RELATION_TYPE)
+ return is_script ? 0 : thick_space;
+ if (right == OPERATOR_TYPE)
+ return thin_space;
+ if (left == INNER_TYPE || right == INNER_TYPE)
+ return is_script ? 0 : thin_space;
+ if (left == OPERATOR_TYPE && right == ORDINARY_TYPE)
+ return thin_space;
+ return 0;
+}
+
+int list_box::compute_metrics(int style)
+{
+ sty = style;
+ int i;
+ for (i = 0; i < list.len; i++) {
+ int t = list.p[i]->spacing_type;
+ // 5
+ if (t == BINARY_TYPE) {
+ int prevt;
+ if (i == 0
+ || (prevt = list.p[i-1]->spacing_type) == BINARY_TYPE
+ || prevt == OPERATOR_TYPE
+ || prevt == RELATION_TYPE
+ || prevt == OPENING_TYPE
+ || prevt == PUNCTUATION_TYPE)
+ list.p[i]->spacing_type = ORDINARY_TYPE;
+ }
+ // 7
+ else if ((t == RELATION_TYPE || t == CLOSING_TYPE
+ || t == PUNCTUATION_TYPE)
+ && i > 0 && list.p[i-1]->spacing_type == BINARY_TYPE)
+ list.p[i-1]->spacing_type = ORDINARY_TYPE;
+ }
+ for (i = 0; i < list.len; i++) {
+ unsigned flags = 0;
+ if (i - 1 >= 0 && list.p[i - 1]->right_is_italic())
+ flags |= HINT_PREV_IS_ITALIC;
+ if (i + 1 < list.len && list.p[i + 1]->left_is_italic())
+ flags |= HINT_NEXT_IS_ITALIC;
+ if (flags)
+ list.p[i]->hint(flags);
+ }
+ is_script = (style <= SCRIPT_STYLE);
+ int total_spacing = 0;
+ for (i = 1; i < list.len; i++)
+ total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type,
+ list.p[i]->spacing_type);
+ int res = 0;
+ for (i = 0; i < list.len; i++)
+ if (!list.p[i]->is_simple()) {
+ int r = list.p[i]->compute_metrics(style);
+ if (r) {
+ if (res)
+ error("multiple marks and lineups");
+ else {
+ compute_sublist_width(i);
+ printf(".nr " MARK_REG " +\\n[" TEMP_REG"]\n");
+ res = r;
+ }
+ }
+ }
+ printf(".nr " WIDTH_FORMAT " %dM", uid, total_spacing);
+ for (i = 0; i < list.len; i++)
+ if (!list.p[i]->is_simple())
+ printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid);
+ printf("\n");
+ printf(".nr " HEIGHT_FORMAT " 0", uid);
+ for (i = 0; i < list.len; i++)
+ if (!list.p[i]->is_simple())
+ printf(">?\\n[" HEIGHT_FORMAT "]", list.p[i]->uid);
+ printf("\n");
+ printf(".nr " DEPTH_FORMAT " 0", uid);
+ for (i = 0; i < list.len; i++)
+ if (!list.p[i]->is_simple())
+ printf(">?\\n[" DEPTH_FORMAT "]", list.p[i]->uid);
+ printf("\n");
+ int have_simple = 0;
+ for (i = 0; i < list.len && !have_simple; i++)
+ have_simple = list.p[i]->is_simple();
+ if (have_simple) {
+ printf(".nr " WIDTH_FORMAT " +\\w" DELIMITER_CHAR, uid);
+ for (i = 0; i < list.len; i++)
+ if (list.p[i]->is_simple())
+ list.p[i]->output();
+ printf(DELIMITER_CHAR "\n");
+ printf(".nr " HEIGHT_FORMAT " \\n[rst]>?\\n[" HEIGHT_FORMAT "]\n",
+ uid, uid);
+ printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?\\n[" DEPTH_FORMAT "]\n",
+ uid, uid);
+ }
+ return res;
+}
+
+void list_box::compute_sublist_width(int n)
+{
+ int total_spacing = 0;
+ int i;
+ for (i = 1; i < n + 1 && i < list.len; i++)
+ total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type,
+ list.p[i]->spacing_type);
+ printf(".nr " TEMP_REG " %dM", total_spacing);
+ for (i = 0; i < n; i++)
+ if (!list.p[i]->is_simple())
+ printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid);
+ int have_simple = 0;
+ for (i = 0; i < n && !have_simple; i++)
+ have_simple = list.p[i]->is_simple();
+ if (have_simple) {
+ printf("+\\w" DELIMITER_CHAR);
+ for (i = 0; i < n; i++)
+ if (list.p[i]->is_simple())
+ list.p[i]->output();
+ printf(DELIMITER_CHAR);
+ }
+ printf("\n");
+}
+
+void list_box::compute_subscript_kern()
+{
+ // We can only call compute_subscript_kern if we have called
+ // compute_metrics first.
+ if (list.p[list.len-1]->is_simple())
+ list.p[list.len-1]->compute_metrics(sty);
+ list.p[list.len-1]->compute_subscript_kern();
+ printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n",
+ uid, list.p[list.len-1]->uid);
+}
+
+void list_box::output()
+{
+ for (int i = 0; i < list.len; i++) {
+ if (i > 0) {
+ int n = compute_spacing(is_script,
+ list.p[i-1]->spacing_type,
+ list.p[i]->spacing_type);
+ if (n > 0)
+ printf("\\h'%dM'", n);
+ }
+ list.p[i]->output();
+ }
+}
+
+void list_box::handle_char_type(int st, int ft)
+{
+ for (int i = 0; i < list.len; i++)
+ list.p[i]->handle_char_type(st, ft);
+}
+
+void list_box::debug_print()
+{
+ list.list_debug_print(" ");
+}
+
+void list_box::check_tabs(int level)
+{
+ list.list_check_tabs(level);
+}
diff --git a/contrib/groff/src/preproc/eqn/main.cc b/contrib/groff/src/preproc/eqn/main.cc
new file mode 100644
index 000000000000..6dc03f0b0543
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/main.cc
@@ -0,0 +1,419 @@
+// -*- 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 "eqn.h"
+#include "stringclass.h"
+#include "device.h"
+#include "searchpath.h"
+#include "macropath.h"
+#include "htmlindicate.h"
+#include "pbox.h"
+
+#define STARTUP_FILE "eqnrc"
+
+extern int yyparse();
+
+static char *delim_search (char *, int);
+static int inline_equation (FILE *, string &, string &);
+
+char start_delim = '\0';
+char end_delim = '\0';
+int non_empty_flag;
+int inline_flag;
+int draw_flag = 0;
+int one_size_reduction_flag = 0;
+int compatible_flag = 0;
+int no_newline_in_delim_flag = 0;
+int html = 0;
+// if we encounter a region marked as an image then we
+// do not mark up inline equations.
+int suppress_html = 0;
+
+
+int read_line(FILE *fp, string *p)
+{
+ p->clear();
+ int c = -1;
+ while ((c = getc(fp)) != EOF) {
+ if (!illegal_input_char(c))
+ *p += char(c);
+ else
+ error("illegal input character code `%1'", c);
+ if (c == '\n')
+ break;
+ }
+ current_lineno++;
+ return p->length() > 0;
+}
+
+void do_file(FILE *fp, const char *filename)
+{
+ string linebuf;
+ string str;
+ printf(".lf 1 %s\n", filename);
+ current_filename = filename;
+ current_lineno = 0;
+ while (read_line(fp, &linebuf)) {
+ if (linebuf.length() >= 4
+ && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f'
+ && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
+ put_string(linebuf, stdout);
+ linebuf += '\0';
+ if (interpret_lf_args(linebuf.contents() + 3))
+ current_lineno--;
+ }
+ else if (linebuf.length() >= 12
+ && linebuf[0] == '.' && linebuf[1] == 'H' && linebuf[2] == 'T'
+ && linebuf[3] == 'M' && linebuf[4] == 'L' && linebuf[5] == '-'
+ && linebuf[6] == 'I' && linebuf[7] == 'M' && linebuf[8] == 'A'
+ && linebuf[9] == 'G' && linebuf[10] == 'E'
+ && linebuf[11] == '\n') {
+ put_string(linebuf, stdout);
+ suppress_html++;
+ }
+ else if (linebuf.length() >= 16
+ && linebuf[0] == '.' && linebuf[1] == 'H' && linebuf[2] == 'T'
+ && linebuf[3] == 'M' && linebuf[4] == 'L' && linebuf[5] == '-'
+ && linebuf[6] == 'I' && linebuf[7] == 'M' && linebuf[8] == 'A'
+ && linebuf[9] == 'G' && linebuf[10] == 'E' && linebuf[11] == '-'
+ && linebuf[12] == 'E' && linebuf[13] == 'N' && linebuf[14] == 'D'
+ && linebuf[15] == '\n') {
+ put_string(linebuf, stdout);
+ suppress_html--;
+ }
+ else if (linebuf.length() >= 4
+ && linebuf[0] == '.'
+ && linebuf[1] == 'E'
+ && linebuf[2] == 'Q'
+ && (linebuf[3] == ' ' || linebuf[3] == '\n'
+ || compatible_flag)) {
+ if (html && (suppress_html == 0))
+ graphic_start(0);
+ put_string(linebuf, stdout);
+ int start_lineno = current_lineno + 1;
+ str.clear();
+ for (;;) {
+ if (!read_line(fp, &linebuf))
+ fatal("end of file before .EN");
+ if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') {
+ if (linebuf[2] == 'N'
+ && (linebuf.length() == 3 || linebuf[3] == ' '
+ || linebuf[3] == '\n' || compatible_flag))
+ break;
+ else if (linebuf[2] == 'Q' && linebuf.length() > 3
+ && (linebuf[3] == ' ' || linebuf[3] == '\n'
+ || compatible_flag))
+ fatal("nested .EQ");
+ }
+ str += linebuf;
+ }
+ str += '\0';
+ start_string();
+ init_lex(str.contents(), current_filename, start_lineno);
+ non_empty_flag = 0;
+ inline_flag = 0;
+ yyparse();
+ if (non_empty_flag) {
+ printf(".lf %d\n", current_lineno - 1);
+ output_string();
+ }
+ restore_compatibility();
+ printf(".lf %d\n", current_lineno);
+ put_string(linebuf, stdout);
+ if (html && (suppress_html == 0))
+ graphic_end();
+ }
+ else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
+ && inline_equation(fp, linebuf, str))
+ ;
+ else
+ put_string(linebuf, stdout);
+ }
+ current_filename = 0;
+ current_lineno = 0;
+}
+
+// Handle an inline equation. Return 1 if it was an inline equation,
+// otherwise.
+static int inline_equation(FILE *fp, string &linebuf, string &str)
+{
+ linebuf += '\0';
+ char *ptr = &linebuf[0];
+ char *start = delim_search(ptr, start_delim);
+ if (!start) {
+ // It wasn't a delimiter after all.
+ linebuf.set_length(linebuf.length() - 1); // strip the '\0'
+ return 0;
+ }
+ start_string();
+ inline_flag = 1;
+ for (;;) {
+ if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) {
+ error("missing `%1'", end_delim);
+ char *nl = strchr(start + 1, '\n');
+ if (nl != 0)
+ *nl = '\0';
+ do_text(ptr);
+ break;
+ }
+ int start_lineno = current_lineno;
+ *start = '\0';
+ do_text(ptr);
+ ptr = start + 1;
+ str.clear();
+ for (;;) {
+ char *end = strchr(ptr, end_delim);
+ if (end != 0) {
+ *end = '\0';
+ str += ptr;
+ ptr = end + 1;
+ break;
+ }
+ str += ptr;
+ if (!read_line(fp, &linebuf))
+ fatal("end of file before `%1'", end_delim);
+ linebuf += '\0';
+ ptr = &linebuf[0];
+ }
+ str += '\0';
+ if (html && (suppress_html == 0)) {
+ printf(".as %s ", LINE_STRING);
+ graphic_start(1);
+ printf("\n");
+ }
+ init_lex(str.contents(), current_filename, start_lineno);
+ yyparse();
+ if (html && (suppress_html == 0)) {
+ printf(".as %s ", LINE_STRING);
+ graphic_end();
+ printf("\n");
+ }
+ start = delim_search(ptr, start_delim);
+ if (start == 0) {
+ char *nl = strchr(ptr, '\n');
+ if (nl != 0)
+ *nl = '\0';
+ do_text(ptr);
+ break;
+ }
+ }
+ printf(".lf %d\n", current_lineno);
+ output_string();
+ restore_compatibility();
+ printf(".lf %d\n", current_lineno + 1);
+ return 1;
+}
+
+/* Search for delim. Skip over number register and string names etc. */
+
+static char *delim_search(char *ptr, int delim)
+{
+ while (*ptr) {
+ if (*ptr == delim)
+ return ptr;
+ if (*ptr++ == '\\') {
+ switch (*ptr) {
+ case 'n':
+ case '*':
+ case 'f':
+ case 'g':
+ case 'k':
+ switch (*++ptr) {
+ case '\0':
+ case '\\':
+ break;
+ case '(':
+ if (*++ptr != '\\' && *ptr != '\0'
+ && *++ptr != '\\' && *ptr != '\0')
+ ptr++;
+ break;
+ case '[':
+ while (*++ptr != '\0')
+ if (*ptr == ']') {
+ ptr++;
+ break;
+ }
+ break;
+ default:
+ ptr++;
+ break;
+ }
+ break;
+ case '\\':
+ case '\0':
+ break;
+ default:
+ ptr++;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
+ program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ int load_startup_file = 1;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options,
+ NULL))
+ != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'R': // don't load eqnrc
+ load_startup_file = 0;
+ break;
+ case 'M':
+ config_macro_path.command_line_dir(optarg);
+ break;
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU eqn (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'd':
+ if (optarg[0] == '\0' || optarg[1] == '\0')
+ error("-d requires two character argument");
+ else if (illegal_input_char(optarg[0]))
+ error("bad delimiter `%1'", optarg[0]);
+ else if (illegal_input_char(optarg[1]))
+ error("bad delimiter `%1'", optarg[1]);
+ else {
+ start_delim = optarg[0];
+ end_delim = optarg[1];
+ }
+ break;
+ case 'f':
+ set_gfont(optarg);
+ break;
+ case 'T':
+ device = optarg;
+ if (strcmp(device, "ps:html") == 0) {
+ device = "ps";
+ html = 1;
+ }
+ break;
+ case 's':
+ if (!set_gsize(optarg))
+ error("invalid size `%1'", optarg);
+ break;
+ case 'p':
+ {
+ int n;
+ if (sscanf(optarg, "%d", &n) == 1)
+ set_script_reduction(n);
+ else
+ error("bad size `%1'", optarg);
+ }
+ break;
+ case 'm':
+ {
+ int n;
+ if (sscanf(optarg, "%d", &n) == 1)
+ set_minimum_size(n);
+ else
+ error("bad size `%1'", optarg);
+ }
+ break;
+ case 'r':
+ one_size_reduction_flag = 1;
+ break;
+ case 'D':
+ warning("-D option is obsolete: use `set draw_lines 1' instead");
+ draw_flag = 1;
+ break;
+ case 'N':
+ no_newline_in_delim_flag = 1;
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ init_table(device);
+ init_char_table();
+ printf(".if !'\\*(.T'%s' "
+ ".if !'\\*(.T'html' " // the html device uses `-Tps' to render
+ // equations as images
+ ".tm warning: %s should have been given a `-T\\*(.T' option\n",
+ device, program_name);
+ printf(".if '\\*(.T'html' "
+ ".if !'%s'ps' "
+ ".tm warning: %s should have been given a `-Tps' option\n",
+ device, program_name);
+ printf(".if '\\*(.T'html' "
+ ".if !'%s'ps' "
+ ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n",
+ device);
+ if (load_startup_file) {
+ char *path;
+ FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path);
+ if (fp) {
+ do_file(fp, path);
+ fclose(fp);
+ a_delete path;
+ }
+ }
+ if (optind >= argc)
+ do_file(stdin, "-");
+ else
+ for (int i = optind; i < argc; i++)
+ if (strcmp(argv[i], "-") == 0)
+ do_file(stdin, "-");
+ else {
+ errno = 0;
+ FILE *fp = fopen(argv[i], "r");
+ if (!fp)
+ fatal("can't open `%1': %2", argv[i], strerror(errno));
+ else {
+ do_file(fp, argv[i]);
+ fclose(fp);
+ }
+ }
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ return 0;
+}
diff --git a/contrib/groff/src/preproc/eqn/mark.cc b/contrib/groff/src/preproc/eqn/mark.cc
new file mode 100644
index 000000000000..99d1b75f2f78
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/mark.cc
@@ -0,0 +1,121 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+class mark_box : public pointer_box {
+public:
+ mark_box(box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+// we push down marks so that they don't interfere with spacing
+
+box *make_mark_box(box *p)
+{
+ list_box *b = p->to_list_box();
+ if (b != 0) {
+ b->list.p[0] = make_mark_box(b->list.p[0]);
+ return b;
+ }
+ else
+ return new mark_box(p);
+}
+
+mark_box::mark_box(box *pp) : pointer_box(pp)
+{
+}
+
+void mark_box::output()
+{
+ p->output();
+}
+
+int mark_box::compute_metrics(int style)
+{
+ int res = p->compute_metrics(style);
+ if (res)
+ error("multiple marks and lineups");
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " MARK_REG " 0\n");
+ return FOUND_MARK;
+}
+
+void mark_box::debug_print()
+{
+ fprintf(stderr, "mark { ");
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+
+class lineup_box : public pointer_box {
+public:
+ lineup_box(box *);
+ void output();
+ int compute_metrics(int style);
+ void debug_print();
+};
+
+// we push down lineups so that they don't interfere with spacing
+
+box *make_lineup_box(box *p)
+{
+ list_box *b = p->to_list_box();
+ if (b != 0) {
+ b->list.p[0] = make_lineup_box(b->list.p[0]);
+ return b;
+ }
+ else
+ return new lineup_box(p);
+}
+
+lineup_box::lineup_box(box *pp) : pointer_box(pp)
+{
+}
+
+void lineup_box::output()
+{
+ p->output();
+}
+
+int lineup_box::compute_metrics(int style)
+{
+ int res = p->compute_metrics(style);
+ if (res)
+ error("multiple marks and lineups");
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " MARK_REG " 0\n");
+ return FOUND_LINEUP;
+}
+
+void lineup_box::debug_print()
+{
+ fprintf(stderr, "lineup { ");
+ p->debug_print();
+ fprintf(stderr, " }");
+}
diff --git a/contrib/groff/src/preproc/eqn/neqn.man b/contrib/groff/src/preproc/eqn/neqn.man
new file mode 100644
index 000000000000..bca7dc2ed912
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/neqn.man
@@ -0,0 +1,21 @@
+.TH @G@NEQN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@neqn \- format equations for ascii output
+.SH SYNOPSIS
+.B @g@neqn
+[@g@eqn options]
+.SH DESCRIPTION
+The
+.B @g@neqn
+program is actually just a shell script which invokes the
+.BR @g@eqn (@MAN1EXT@)
+command with the
+.B ascii
+output device.
+.LP
+Note that
+.B @g@eqn
+does not support low-resolution, typewriter-like devices (although it may
+work adequately for very simple input).
+.SH "SEE ALSO"
+.BR @g@eqn (@MAN1EXT@)
diff --git a/contrib/groff/src/preproc/eqn/other.cc b/contrib/groff/src/preproc/eqn/other.cc
new file mode 100644
index 000000000000..eb9e50a2bc57
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/other.cc
@@ -0,0 +1,601 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+class accent_box : public pointer_box {
+private:
+ box *ab;
+public:
+ accent_box(box *, box *);
+ ~accent_box();
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+ void check_tabs(int);
+};
+
+box *make_accent_box(box *p, box *q)
+{
+ return new accent_box(p, q);
+}
+
+accent_box::accent_box(box *pp, box *qq) : pointer_box(pp), ab(qq)
+{
+}
+
+accent_box::~accent_box()
+{
+ delete ab;
+}
+
+#if 0
+int accent_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ p->compute_skew();
+ ab->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n",
+ uid, p->uid, x_height);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
+ SUP_RAISE_FORMAT "]\n",
+ uid, ab->uid, uid);
+ return r;
+}
+
+void accent_box::output()
+{
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n["
+ SKEW_FORMAT "]u'",
+ p->uid, ab->uid, p->uid);
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ ab->output();
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", ab->uid);
+ printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n["
+ SKEW_FORMAT "]u)'",
+ p->uid, ab->uid, p->uid);
+ p->output();
+}
+#endif
+
+/* This version copes with the possibility of an accent's being wider
+than its accentee. LEFT_WIDTH_FORMAT gives the distance from the
+left edge of the resulting box to the middle of the accentee's box.*/
+
+int accent_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ p->compute_skew();
+ ab->compute_metrics(style);
+ printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
+ ">?(\\n[" WIDTH_FORMAT "]/2-\\n[" SKEW_FORMAT "])\n",
+ uid, p->uid, ab->uid, p->uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
+ ">?(\\n[" WIDTH_FORMAT "]/2+\\n[" SKEW_FORMAT "])"
+ "+\\n[" LEFT_WIDTH_FORMAT "]\n",
+ uid, p->uid, ab->uid, p->uid, uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n",
+ uid, p->uid, x_height);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
+ SUP_RAISE_FORMAT "]\n",
+ uid, ab->uid, uid);
+ if (r)
+ printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]"
+ "-(\\n[" WIDTH_FORMAT "]/2)'\n",
+ uid, p->uid);
+ return r;
+}
+
+void accent_box::output()
+{
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u+\\n[" SKEW_FORMAT "]u"
+ "-(\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, p->uid, ab->uid);
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ ab->output();
+ printf(DELIMITER_CHAR);
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, p->uid);
+ p->output();
+ printf(DELIMITER_CHAR);
+ printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
+}
+
+void accent_box::check_tabs(int level)
+{
+ ab->check_tabs(level + 1);
+ p->check_tabs(level + 1);
+}
+
+void accent_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " } accent { ");
+ ab->debug_print();
+ fprintf(stderr, " }");
+}
+
+class overline_char_box : public simple_box {
+public:
+ overline_char_box();
+ void output();
+ void debug_print();
+};
+
+overline_char_box::overline_char_box()
+{
+}
+
+void overline_char_box::output()
+{
+ printf("\\v'-%dM/2u-%dM'", 7*default_rule_thickness, x_height);
+ printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"),
+ accent_width);
+ printf("\\v'%dM/2u+%dM'", 7*default_rule_thickness, x_height);
+}
+
+void overline_char_box::debug_print()
+{
+ fprintf(stderr, "<overline char>");
+}
+
+class overline_box : public pointer_box {
+public:
+ overline_box(box *);
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+};
+
+box *make_overline_box(box *p)
+{
+ if (p->is_char())
+ return new accent_box(p, new overline_char_box);
+ else
+ return new overline_box(p);
+}
+
+overline_box::overline_box(box *pp) : pointer_box(pp)
+{
+}
+
+int overline_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(cramped_style(style));
+ // 9
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+%dM\n",
+ uid, p->uid, default_rule_thickness*5);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void overline_box::output()
+{
+ // 9
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'-\\n[" HEIGHT_FORMAT "]u-(%dM/2u)'",
+ p->uid, 7*default_rule_thickness);
+ if (draw_flag)
+ printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid);
+ else
+ printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid);
+ printf(DELIMITER_CHAR);
+ p->output();
+}
+
+void overline_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " } bar");
+}
+
+class uaccent_box : public pointer_box {
+ box *ab;
+public:
+ uaccent_box(box *, box *);
+ ~uaccent_box();
+ int compute_metrics(int);
+ void output();
+ void compute_subscript_kern();
+ void check_tabs(int);
+ void debug_print();
+};
+
+box *make_uaccent_box(box *p, box *q)
+{
+ return new uaccent_box(p, q);
+}
+
+uaccent_box::uaccent_box(box *pp, box *qq)
+: pointer_box(pp), ab(qq)
+{
+}
+
+uaccent_box::~uaccent_box()
+{
+ delete ab;
+}
+
+int uaccent_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ ab->compute_metrics(style);
+ printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
+ ">?(\\n[" WIDTH_FORMAT "]/2)\n",
+ uid, p->uid, ab->uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2"
+ ">?(\\n[" WIDTH_FORMAT "]/2)"
+ "+\\n[" LEFT_WIDTH_FORMAT "]\n",
+ uid, p->uid, ab->uid, uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
+ "+\\n[" DEPTH_FORMAT "]\n",
+ uid, p->uid, ab->uid);
+ if (r)
+ printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]"
+ "-(\\n[" WIDTH_FORMAT "]/2)'\n",
+ uid, p->uid);
+ return r;
+}
+
+void uaccent_box::output()
+{
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, ab->uid);
+ printf("\\v'\\n[" DEPTH_FORMAT "]u'", p->uid);
+ ab->output();
+ printf(DELIMITER_CHAR);
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, p->uid);
+ p->output();
+ printf(DELIMITER_CHAR);
+ printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
+}
+
+void uaccent_box::check_tabs(int level)
+{
+ ab->check_tabs(level + 1);
+ p->check_tabs(level + 1);
+}
+
+void uaccent_box::compute_subscript_kern()
+{
+ box::compute_subscript_kern(); // want 0 subscript kern
+}
+
+void uaccent_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " } uaccent { ");
+ ab->debug_print();
+ fprintf(stderr, " }");
+}
+
+class underline_char_box : public simple_box {
+public:
+ underline_char_box();
+ void output();
+ void debug_print();
+};
+
+underline_char_box::underline_char_box()
+{
+}
+
+void underline_char_box::output()
+{
+ printf("\\v'%dM/2u'", 7*default_rule_thickness);
+ printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"),
+ accent_width);
+ printf("\\v'-%dM/2u'", 7*default_rule_thickness);
+}
+
+void underline_char_box::debug_print()
+{
+ fprintf(stderr, "<underline char>");
+}
+
+
+class underline_box : public pointer_box {
+public:
+ underline_box(box *);
+ int compute_metrics(int);
+ void output();
+ void compute_subscript_kern();
+ void debug_print();
+};
+
+box *make_underline_box(box *p)
+{
+ if (p->is_char())
+ return new uaccent_box(p, new underline_char_box);
+ else
+ return new underline_box(p);
+}
+
+underline_box::underline_box(box *pp) : pointer_box(pp)
+{
+}
+
+int underline_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ // 10
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n",
+ uid, p->uid, default_rule_thickness*5);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void underline_box::output()
+{
+ // 10
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'\\n[" DEPTH_FORMAT "]u+(%dM/2u)'",
+ p->uid, 7*default_rule_thickness);
+ if (draw_flag)
+ printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid);
+ else
+ printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid);
+ printf(DELIMITER_CHAR);
+ p->output();
+}
+
+// we want an underline box to have 0 subscript kern
+
+void underline_box::compute_subscript_kern()
+{
+ box::compute_subscript_kern();
+}
+
+void underline_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " } under");
+}
+
+size_box::size_box(char *s, box *pp) : pointer_box(pp), size(s)
+{
+}
+
+int size_box::compute_metrics(int style)
+{
+ printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid);
+ printf(".ps %s\n", size);
+ printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid);
+ int r = p->compute_metrics(style);
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void size_box::output()
+{
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+ p->output();
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+}
+
+size_box::~size_box()
+{
+ a_delete size;
+}
+
+void size_box::debug_print()
+{
+ fprintf(stderr, "size %s { ", size);
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+
+font_box::font_box(char *s, box *pp) : pointer_box(pp), f(s)
+{
+}
+
+font_box::~font_box()
+{
+ a_delete f;
+}
+
+int font_box::compute_metrics(int style)
+{
+ const char *old_roman_font = current_roman_font;
+ current_roman_font = f;
+ printf(".nr " FONT_FORMAT " \\n[.f]\n", uid);
+ printf(".ft %s\n", f);
+ int r = p->compute_metrics(style);
+ current_roman_font = old_roman_font;
+ printf(".ft \\n[" FONT_FORMAT "]\n", uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void font_box::output()
+{
+ printf("\\f[%s]", f);
+ const char *old_roman_font = current_roman_font;
+ current_roman_font = f;
+ p->output();
+ current_roman_font = old_roman_font;
+ printf("\\f[\\n[" FONT_FORMAT "]]", uid);
+}
+
+void font_box::debug_print()
+{
+ fprintf(stderr, "font %s { ", f);
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+fat_box::fat_box(box *pp) : pointer_box(pp)
+{
+}
+
+int fat_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n",
+ uid, p->uid, fat_offset);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ return r;
+}
+
+void fat_box::output()
+{
+ p->output();
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p->uid);
+ printf("\\h'%dM'", fat_offset);
+ p->output();
+}
+
+
+void fat_box::debug_print()
+{
+ fprintf(stderr, "fat { ");
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+
+vmotion_box::vmotion_box(int i, box *pp) : pointer_box(pp), n(i)
+{
+}
+
+int vmotion_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ if (n > 0) {
+ printf(".nr " HEIGHT_FORMAT " %dM+\\n[" HEIGHT_FORMAT "]\n",
+ uid, n, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ }
+ else {
+ printf(".nr " DEPTH_FORMAT " %dM+\\n[" DEPTH_FORMAT "]>?0\n",
+ uid, -n, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n",
+ uid, p->uid);
+ }
+ return r;
+}
+
+void vmotion_box::output()
+{
+ printf("\\v'%dM'", -n);
+ p->output();
+ printf("\\v'%dM'", n);
+}
+
+void vmotion_box::debug_print()
+{
+ if (n >= 0)
+ fprintf(stderr, "up %d { ", n);
+ else
+ fprintf(stderr, "down %d { ", -n);
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+hmotion_box::hmotion_box(int i, box *pp) : pointer_box(pp), n(i)
+{
+}
+
+int hmotion_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n",
+ uid, p->uid, n);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ if (r)
+ printf(".nr " MARK_REG " +%dM\n", n);
+ return r;
+}
+
+void hmotion_box::output()
+{
+ printf("\\h'%dM'", n);
+ p->output();
+}
+
+void hmotion_box::debug_print()
+{
+ if (n >= 0)
+ fprintf(stderr, "fwd %d { ", n);
+ else
+ fprintf(stderr, "back %d { ", -n);
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+vcenter_box::vcenter_box(box *pp) : pointer_box(pp)
+{
+}
+
+int vcenter_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" DEPTH_FORMAT "]-\\n["
+ HEIGHT_FORMAT "]/2+%dM\n",
+ uid, p->uid, p->uid, axis_height);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n["
+ SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]-\\n["
+ SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid);
+
+ return r;
+}
+
+void vcenter_box::output()
+{
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ p->output();
+ printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
+}
+
+void vcenter_box::debug_print()
+{
+ fprintf(stderr, "vcenter { ");
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
diff --git a/contrib/groff/src/preproc/eqn/over.cc b/contrib/groff/src/preproc/eqn/over.cc
new file mode 100644
index 000000000000..06b032129fd7
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/over.cc
@@ -0,0 +1,196 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+class over_box : public box {
+private:
+ int reduce_size;
+ box *num;
+ box *den;
+public:
+ over_box(int small, box *, box *);
+ ~over_box();
+ void debug_print();
+ int compute_metrics(int);
+ void output();
+ void check_tabs(int);
+};
+
+box *make_over_box(box *pp, box *qq)
+{
+ return new over_box(0, pp, qq);
+}
+
+box *make_small_over_box(box *pp, box *qq)
+{
+ return new over_box(1, pp, qq);
+}
+
+over_box::over_box(int is_small, box *pp, box *qq)
+: reduce_size(is_small), num(pp), den(qq)
+{
+ spacing_type = INNER_TYPE;
+}
+
+over_box::~over_box()
+{
+ delete num;
+ delete den;
+}
+
+int over_box::compute_metrics(int style)
+{
+ if (reduce_size) {
+ style = script_style(style);
+ printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid);
+ set_script_size();
+ printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid);
+ }
+ int mark_uid;
+ int res = num->compute_metrics(style);
+ if (res)
+ mark_uid = num->uid;
+ int r = den->compute_metrics(cramped_style(style));
+ if (r && res)
+ error("multiple marks and lineups");
+ else {
+ mark_uid = den->uid;
+ res = r;
+ }
+ if (reduce_size)
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ printf(".nr " WIDTH_FORMAT " (\\n[" WIDTH_FORMAT "]>?\\n[" WIDTH_FORMAT "]",
+ uid, num->uid, den->uid);
+ // allow for \(ru being wider than both the numerator and denominator
+ if (!draw_flag)
+ fputs(">?\\w" DELIMITER_CHAR "\\(ru" DELIMITER_CHAR, stdout);
+ printf(")+%dM\n", null_delimiter_space*2 + over_hang*2);
+ // 15b
+ printf(".nr " SUP_RAISE_FORMAT " %dM\n",
+ uid, (reduce_size ? num2 : num1));
+ printf(".nr " SUB_LOWER_FORMAT " %dM\n",
+ uid, (reduce_size ? denom2 : denom1));
+
+ // 15d
+ printf(".nr " SUP_RAISE_FORMAT " +(\\n[" DEPTH_FORMAT
+ "]-\\n[" SUP_RAISE_FORMAT "]+%dM+(%dM/2)+%dM)>?0\n",
+ uid, num->uid, uid, axis_height, default_rule_thickness,
+ default_rule_thickness*(reduce_size ? 1 : 3));
+ printf(".nr " SUB_LOWER_FORMAT " +(\\n[" HEIGHT_FORMAT
+ "]-\\n[" SUB_LOWER_FORMAT "]-%dM+(%dM/2)+%dM)>?0\n",
+ uid, den->uid, uid, axis_height, default_rule_thickness,
+ default_rule_thickness*(reduce_size ? 1 : 3));
+
+
+ printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n["
+ HEIGHT_FORMAT "]\n",
+ uid, uid, num->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n["
+ DEPTH_FORMAT "]\n",
+ uid, uid, den->uid);
+ if (res)
+ printf(".nr " MARK_REG " +(\\n[" WIDTH_FORMAT "]-\\n["
+ WIDTH_FORMAT "]/2)\n", uid, mark_uid);
+ return res;
+}
+
+#define USE_Z
+
+void over_box::output()
+{
+ if (reduce_size)
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+#ifdef USE_Z
+ printf("\\Z" DELIMITER_CHAR);
+#endif
+ // move up to the numerator baseline
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ // move across so that it's centered
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'",
+ uid, num->uid);
+
+ // print the numerator
+ num->output();
+
+#ifdef USE_Z
+ printf(DELIMITER_CHAR);
+#else
+ // back again
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", num->uid);
+ printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, num->uid);
+ // down again
+ printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
+#endif
+#ifdef USE_Z
+ printf("\\Z" DELIMITER_CHAR);
+#endif
+ // move down to the denominator baseline
+ printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid);
+
+ // move across so that it's centered
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'",
+ uid, den->uid);
+
+ // print the the denominator
+ den->output();
+
+#ifdef USE_Z
+ printf(DELIMITER_CHAR);
+#else
+ // back again
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", den->uid);
+ printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'",
+ uid, den->uid);
+ // up again
+ printf("\\v'-\\n[" SUB_LOWER_FORMAT "]u'", uid);
+#endif
+ if (reduce_size)
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+ // draw the line
+ printf("\\h'%dM'", null_delimiter_space);
+ printf("\\v'-%dM'", axis_height);
+ fputs(draw_flag ? "\\D'l" : "\\l'", stdout);
+ printf("\\n[" WIDTH_FORMAT "]u-%dM",
+ uid, 2*null_delimiter_space);
+ fputs(draw_flag ? " 0'" : "\\&\\(ru'", stdout);
+ printf("\\v'%dM'", axis_height);
+ printf("\\h'%dM'", null_delimiter_space);
+}
+
+void over_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ num->debug_print();
+ if (reduce_size)
+ fprintf(stderr, " } smallover { ");
+ else
+ fprintf(stderr, " } over { ");
+ den->debug_print();
+ fprintf(stderr, " }");
+}
+
+void over_box::check_tabs(int level)
+{
+ num->check_tabs(level + 1);
+ den->check_tabs(level + 1);
+}
diff --git a/contrib/groff/src/preproc/eqn/pbox.h b/contrib/groff/src/preproc/eqn/pbox.h
new file mode 100644
index 000000000000..d1f16ac486ae
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/pbox.h
@@ -0,0 +1,141 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+extern int fat_offset;
+
+extern int over_hang;
+extern int accent_width;
+
+extern int delimiter_factor;
+extern int delimiter_shortfall;
+
+extern int null_delimiter_space;
+extern int script_space;
+extern int thin_space;
+extern int medium_space;
+extern int thick_space;
+
+extern int num1;
+extern int num2;
+// we don't use num3, because we don't have \atop
+extern int denom1;
+extern int denom2;
+extern int axis_height;
+extern int sup1;
+extern int sup2;
+extern int sup3;
+extern int default_rule_thickness;
+extern int sub1;
+extern int sub2;
+extern int sup_drop;
+extern int sub_drop;
+extern int x_height;
+extern int big_op_spacing1;
+extern int big_op_spacing2;
+extern int big_op_spacing3;
+extern int big_op_spacing4;
+extern int big_op_spacing5;
+
+extern int baseline_sep;
+extern int shift_down;
+extern int column_sep;
+extern int matrix_side_sep;
+
+// ms.eqn relies on this!
+
+#define LINE_STRING "10"
+#define MARK_OR_LINEUP_FLAG_REG "MK"
+
+#define WIDTH_FORMAT PREFIX "w%d"
+#define HEIGHT_FORMAT PREFIX "h%d"
+#define DEPTH_FORMAT PREFIX "d%d"
+#define TOTAL_FORMAT PREFIX "t%d"
+#define SIZE_FORMAT PREFIX "z%d"
+#define SMALL_SIZE_FORMAT PREFIX "Z%d"
+#define SUP_RAISE_FORMAT PREFIX "p%d"
+#define SUB_LOWER_FORMAT PREFIX "b%d"
+#define SUB_KERN_FORMAT PREFIX "k%d"
+#define FONT_FORMAT PREFIX "f%d"
+#define SKEW_FORMAT PREFIX "s%d"
+#define LEFT_WIDTH_FORMAT PREFIX "lw%d"
+#define LEFT_DELIM_STRING_FORMAT PREFIX "l%d"
+#define RIGHT_DELIM_STRING_FORMAT PREFIX "r%d"
+#define SQRT_STRING_FORMAT PREFIX "sqr%d"
+#define SQRT_WIDTH_FORMAT PREFIX "sq%d"
+#define BASELINE_SEP_FORMAT PREFIX "bs%d"
+// this needs two parameters, the uid and the column index
+#define COLUMN_WIDTH_FORMAT PREFIX "cw%d,%d"
+
+#define BAR_STRING PREFIX "sqb"
+#define TEMP_REG PREFIX "temp"
+#define MARK_REG PREFIX "mark"
+#define MARK_WIDTH_REG PREFIX "mwidth"
+#define SAVED_MARK_REG PREFIX "smark"
+#define MAX_SIZE_REG PREFIX "mxsz"
+#define REPEAT_APPEND_STRING_MACRO PREFIX "ras"
+#define TOP_HEIGHT_REG PREFIX "th"
+#define TOP_DEPTH_REG PREFIX "td"
+#define MID_HEIGHT_REG PREFIX "mh"
+#define MID_DEPTH_REG PREFIX "md"
+#define BOT_HEIGHT_REG PREFIX "bh"
+#define BOT_DEPTH_REG PREFIX "bd"
+#define EXT_HEIGHT_REG PREFIX "eh"
+#define EXT_DEPTH_REG PREFIX "ed"
+#define TOTAL_HEIGHT_REG PREFIX "tot"
+#define DELTA_REG PREFIX "delta"
+#define DELIM_STRING PREFIX "delim"
+#define DELIM_WIDTH_REG PREFIX "dwidth"
+#define SAVED_FONT_REG PREFIX "sfont"
+#define SAVED_PREV_FONT_REG PREFIX "spfont"
+#define SAVED_INLINE_FONT_REG PREFIX "sifont"
+#define SAVED_INLINE_PREV_FONT_REG PREFIX "sipfont"
+#define SAVED_SIZE_REG PREFIX "ssize"
+#define SAVED_INLINE_SIZE_REG PREFIX "sisize"
+#define SAVED_INLINE_PREV_SIZE_REG PREFIX "sipsize"
+#define SAVE_FONT_STRING PREFIX "sfont"
+#define RESTORE_FONT_STRING PREFIX "rfont"
+#define INDEX_REG PREFIX "i"
+#define TEMP_MACRO PREFIX "tempmac"
+
+#define DELIMITER_CHAR "\\(EQ"
+
+const int CRAMPED_SCRIPT_STYLE = 0;
+const int SCRIPT_STYLE = 1;
+const int CRAMPED_DISPLAY_STYLE = 2;
+const int DISPLAY_STYLE = 3;
+
+extern int script_style(int);
+extern int cramped_style(int);
+
+const int ORDINARY_TYPE = 0;
+const int OPERATOR_TYPE = 1;
+const int BINARY_TYPE = 2;
+const int RELATION_TYPE = 3;
+const int OPENING_TYPE = 4;
+const int CLOSING_TYPE = 5;
+const int PUNCTUATION_TYPE = 6;
+const int INNER_TYPE = 7;
+const int SUPPRESS_TYPE = 8;
+
+void set_script_size();
+
+enum { HINT_PREV_IS_ITALIC = 01, HINT_NEXT_IS_ITALIC = 02 };
+
+extern const char *current_roman_font;
diff --git a/contrib/groff/src/preproc/eqn/pile.cc b/contrib/groff/src/preproc/eqn/pile.cc
new file mode 100644
index 000000000000..0df5241f0fee
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/pile.cc
@@ -0,0 +1,293 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+// piles and matrices
+
+#include "eqn.h"
+#include "pbox.h"
+
+// SUP_RAISE_FORMAT gives the first baseline
+// BASELINE_SEP_FORMAT gives the separation between baselines
+
+int pile_box::compute_metrics(int style)
+{
+ int i;
+ for (i = 0; i < col.len; i++)
+ col.p[i]->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0", uid);
+ for (i = 0; i < col.len; i++)
+ printf(">?\\n[" WIDTH_FORMAT "]", col.p[i]->uid);
+ printf("\n");
+ printf(".nr " BASELINE_SEP_FORMAT " %dM",
+ uid, baseline_sep+col.space);
+ for (i = 1; i < col.len; i++)
+ printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)",
+ col.p[i-1]->uid, col.p[i]->uid, default_rule_thickness*5);
+ // round it so that it's a multiple of the vertical resolution
+ printf("/\\n(.V+(\\n(.V/2)*\\n(.V\n");
+
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2"
+ "+%dM\n",
+ uid, uid, col.len-1, axis_height - shift_down);
+ printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n["
+ HEIGHT_FORMAT "]\n",
+ uid, uid, col.p[0]->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d+\\n["
+ DEPTH_FORMAT "]-\\n[" SUP_RAISE_FORMAT "]\n",
+ uid, uid, col.len-1, col.p[col.len-1]->uid, uid);
+ return FOUND_NOTHING;
+}
+
+void pile_box::output()
+{
+ int i;
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ for (i = 0; i < col.len; i++) {
+ switch (col.align) {
+ case LEFT_ALIGN:
+ break;
+ case CENTER_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'",
+ uid, col.p[i]->uid);
+ break;
+ case RIGHT_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'",
+ uid, col.p[i]->uid);
+ break;
+ default:
+ assert(0);
+ }
+ col.p[i]->output();
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", col.p[i]->uid);
+ switch (col.align) {
+ case LEFT_ALIGN:
+ break;
+ case CENTER_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'",
+ col.p[i]->uid, uid);
+ break;
+ case RIGHT_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'",
+ col.p[i]->uid, uid);
+ break;
+ default:
+ assert(0);
+ }
+ if (i != col.len - 1)
+ printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid);
+ }
+ printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", col.len - 1, uid);
+ printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
+}
+
+pile_box::pile_box(box *pp) : col(pp)
+{
+}
+
+void pile_box::check_tabs(int level)
+{
+ col.list_check_tabs(level);
+}
+
+void pile_box::debug_print()
+{
+ col.debug_print("pile");
+}
+
+int matrix_box::compute_metrics(int style)
+{
+ int i, j;
+ int maxlen = 0;
+ int space = 0;
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < p[i]->len; j++)
+ p[i]->p[j]->compute_metrics(style);
+ if (p[i]->len > maxlen)
+ maxlen = p[i]->len;
+ if (p[i]->space > space)
+ space = p[i]->space;
+ }
+ for (i = 0; i < len; i++) {
+ printf(".nr " COLUMN_WIDTH_FORMAT " 0", uid, i);
+ for (j = 0; j < p[i]->len; j++)
+ printf(">?\\n[" WIDTH_FORMAT "]", p[i]->p[j]->uid);
+ printf("\n");
+ }
+ printf(".nr " WIDTH_FORMAT " %dM",
+ uid, column_sep*(len-1)+2*matrix_side_sep);
+ for (i = 0; i < len; i++)
+ printf("+\\n[" COLUMN_WIDTH_FORMAT "]", uid, i);
+ printf("\n");
+ printf(".nr " BASELINE_SEP_FORMAT " %dM",
+ uid, baseline_sep+space);
+ for (i = 0; i < len; i++)
+ for (j = 1; j < p[i]->len; j++)
+ printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)",
+ p[i]->p[j-1]->uid, p[i]->p[j]->uid, default_rule_thickness*5);
+ // round it so that it's a multiple of the vertical resolution
+ printf("/\\n(.V+(\\n(.V/2)*\\n(.V\n");
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2"
+ "+%dM\n",
+ uid, uid, maxlen-1, axis_height - shift_down);
+ printf(".nr " HEIGHT_FORMAT " 0\\n[" SUP_RAISE_FORMAT "]+(0",
+ uid, uid);
+ for (i = 0; i < len; i++)
+ printf(">?\\n[" HEIGHT_FORMAT "]", p[i]->p[0]->uid);
+ printf(")>?0\n");
+ printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d-\\n["
+ SUP_RAISE_FORMAT "]+(0",
+ uid, uid, maxlen-1, uid);
+ for (i = 0; i < len; i++)
+ if (p[i]->len == maxlen)
+ printf(">?\\n[" DEPTH_FORMAT "]", p[i]->p[maxlen-1]->uid);
+ printf(")>?0\n");
+ return FOUND_NOTHING;
+}
+
+void matrix_box::output()
+{
+ printf("\\h'%dM'", matrix_side_sep);
+ for (int i = 0; i < len; i++) {
+ int j;
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ for (j = 0; j < p[i]->len; j++) {
+ switch (p[i]->align) {
+ case LEFT_ALIGN:
+ break;
+ case CENTER_ALIGN:
+ printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'",
+ uid, i, p[i]->p[j]->uid);
+ break;
+ case RIGHT_ALIGN:
+ printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'",
+ uid, i, p[i]->p[j]->uid);
+ break;
+ default:
+ assert(0);
+ }
+ p[i]->p[j]->output();
+ printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p[i]->p[j]->uid);
+ switch (p[i]->align) {
+ case LEFT_ALIGN:
+ break;
+ case CENTER_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u/2u'",
+ p[i]->p[j]->uid, uid, i);
+ break;
+ case RIGHT_ALIGN:
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u'",
+ p[i]->p[j]->uid, uid, i);
+ break;
+ default:
+ assert(0);
+ }
+ if (j != p[i]->len - 1)
+ printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid);
+ }
+ printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", p[i]->len - 1, uid);
+ printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u'", uid, i);
+ if (i != len - 1)
+ printf("\\h'%dM'", column_sep);
+ }
+ printf("\\h'%dM'", matrix_side_sep);
+}
+
+matrix_box::matrix_box(column *pp)
+{
+ p = new column*[10];
+ for (int i = 0; i < 10; i++)
+ p[i] = 0;
+ maxlen = 10;
+ len = 1;
+ p[0] = pp;
+}
+
+matrix_box::~matrix_box()
+{
+ for (int i = 0; i < len; i++)
+ delete p[i];
+ a_delete p;
+}
+
+void matrix_box::append(column *pp)
+{
+ if (len + 1 > maxlen) {
+ column **oldp = p;
+ maxlen *= 2;
+ p = new column*[maxlen];
+ memcpy(p, oldp, sizeof(column*)*len);
+ a_delete oldp;
+ }
+ p[len++] = pp;
+}
+
+void matrix_box::check_tabs(int level)
+{
+ for (int i = 0; i < len; i++)
+ p[i]->list_check_tabs(level);
+}
+
+void matrix_box::debug_print()
+{
+ fprintf(stderr, "matrix { ");
+ p[0]->debug_print("col");
+ for (int i = 1; i < len; i++) {
+ fprintf(stderr, " ");
+ p[i]->debug_print("col");
+ }
+ fprintf(stderr, " }");
+}
+
+column::column(box *pp) : box_list(pp), align(CENTER_ALIGN), space(0)
+{
+}
+
+void column::set_alignment(alignment a)
+{
+ align = a;
+}
+
+void column::set_space(int n)
+{
+ space = n;
+}
+
+void column::debug_print(const char *s)
+{
+ char c = '\0'; // shut up -Wall
+ switch (align) {
+ case LEFT_ALIGN:
+ c = 'l';
+ break;
+ case RIGHT_ALIGN:
+ c = 'r';
+ break;
+ case CENTER_ALIGN:
+ c = 'c';
+ break;
+ default:
+ assert(0);
+ }
+ fprintf(stderr, "%c%s %d { ", c, s, space);
+ list_debug_print(" above ");
+ fprintf(stderr, " }");
+}
+
diff --git a/contrib/groff/src/preproc/eqn/script.cc b/contrib/groff/src/preproc/eqn/script.cc
new file mode 100644
index 000000000000..7c2e6c258807
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/script.cc
@@ -0,0 +1,221 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+class script_box : public pointer_box {
+private:
+ box *sub;
+ box *sup;
+public:
+ script_box(box *, box *, box *);
+ ~script_box();
+ int compute_metrics(int);
+ void output();
+ void debug_print();
+ int left_is_italic();
+ void hint(unsigned);
+ void check_tabs(int);
+};
+
+/* The idea is that the script should attach to the rightmost box
+of a list. For example, given `2x sup 3', the superscript should
+attach to `x' rather than `2x'. */
+
+box *make_script_box(box *nuc, box *sub, box *sup)
+{
+ list_box *b = nuc->to_list_box();
+ if (b != 0) {
+ b->list.p[b->list.len-1] = make_script_box(b->list.p[b->list.len - 1],
+ sub,
+ sup);
+ return b;
+ }
+ else
+ return new script_box(nuc, sub, sup);
+}
+
+script_box::script_box(box *pp, box *qq, box *rr)
+: pointer_box(pp), sub(qq), sup(rr)
+{
+}
+
+script_box::~script_box()
+{
+ delete sub;
+ delete sup;
+}
+
+int script_box::left_is_italic()
+{
+ return p->left_is_italic();
+}
+
+int script_box::compute_metrics(int style)
+{
+ int res = p->compute_metrics(style);
+ p->compute_subscript_kern();
+ printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid);
+ if (!(style <= SCRIPT_STYLE && one_size_reduction_flag))
+ set_script_size();
+ printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid);
+ if (sub != 0)
+ sub->compute_metrics(cramped_style(script_style(style)));
+ if (sup != 0)
+ sup->compute_metrics(script_style(style));
+ // 18a
+ if (p->is_char()) {
+ printf(".nr " SUP_RAISE_FORMAT " 0\n", uid);
+ printf(".nr " SUB_LOWER_FORMAT " 0\n", uid);
+ }
+ else {
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n",
+ uid, p->uid, sup_drop);
+ printf(".nr " SUB_LOWER_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n",
+ uid, p->uid, sub_drop);
+ }
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ if (sup == 0) {
+ assert(sub != 0);
+ // 18b
+ printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM>?(\\n["
+ HEIGHT_FORMAT "]-(%dM*4/5))\n",
+ uid, uid, sub1, sub->uid, x_height);
+ }
+ else {
+ // sup != 0
+ // 18c
+ int p;
+ if (style == DISPLAY_STYLE)
+ p = sup1;
+ else if (style & 1) // not cramped
+ p = sup2;
+ else
+ p = sup3;
+ printf(".nr " SUP_RAISE_FORMAT " \\n[" SUP_RAISE_FORMAT
+ "]>?%dM>?(\\n[" DEPTH_FORMAT "]+(%dM/4))\n",
+ uid, uid, p, sup->uid, x_height);
+ // 18d
+ if (sub != 0) {
+ printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM\n",
+ uid, uid, sub2);
+ // 18e
+ printf(".nr " TEMP_REG " \\n[" DEPTH_FORMAT "]-\\n["
+ SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "]-\\n["
+ SUB_LOWER_FORMAT "]+(4*%dM)\n",
+ sup->uid, uid, sub->uid, uid, default_rule_thickness);
+ printf(".if \\n[" TEMP_REG "] \\{");
+ printf(".nr " SUB_LOWER_FORMAT " +\\n[" TEMP_REG "]\n", uid);
+ printf(".nr " TEMP_REG " (%dM*4/5)-\\n[" SUP_RAISE_FORMAT
+ "]+\\n[" DEPTH_FORMAT "]>?0\n",
+ x_height, uid, sup->uid);
+ printf(".nr " SUP_RAISE_FORMAT " +\\n[" TEMP_REG "]\n", uid);
+ printf(".nr " SUB_LOWER_FORMAT " -\\n[" TEMP_REG "]\n", uid);
+ printf(".\\}\n");
+ }
+ }
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid);
+ if (sub != 0 && sup != 0)
+ printf("+((\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]>?\\n["
+ WIDTH_FORMAT "])+%dM)>?0\n",
+ sub->uid, p->uid, sup->uid, script_space);
+ else if (sub != 0)
+ printf("+(\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]+%dM)>?0\n",
+ sub->uid, p->uid, script_space);
+ else if (sup != 0)
+ printf("+(\\n[" WIDTH_FORMAT "]+%dM)>?0\n", sup->uid, script_space);
+ else
+ printf("\n");
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]",
+ uid, p->uid);
+ if (sup != 0)
+ printf(">?(\\n[" SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "])",
+ uid, sup->uid);
+ if (sub != 0)
+ printf(">?(-\\n[" SUB_LOWER_FORMAT "]+\\n[" HEIGHT_FORMAT "])",
+ uid, sub->uid);
+ printf("\n");
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]",
+ uid, p->uid);
+ if (sub != 0)
+ printf(">?(\\n[" SUB_LOWER_FORMAT "]+\\n[" DEPTH_FORMAT "])",
+ uid, sub->uid);
+ if (sup != 0)
+ printf(">?(-\\n[" SUP_RAISE_FORMAT "]+\\n[" DEPTH_FORMAT "])",
+ uid, sup->uid);
+ printf("\n");
+ return res;
+}
+
+void script_box::output()
+{
+ p->output();
+ if (sup != 0) {
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+ sup->output();
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+ printf(DELIMITER_CHAR);
+ }
+ if (sub != 0) {
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid);
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+ printf("\\h'-\\n[" SUB_KERN_FORMAT "]u'", p->uid);
+ sub->output();
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+ printf(DELIMITER_CHAR);
+ }
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'",
+ uid, p->uid);
+}
+
+void script_box::hint(unsigned flags)
+{
+ p->hint(flags & ~HINT_NEXT_IS_ITALIC);
+}
+
+void script_box::debug_print()
+{
+ fprintf(stderr, "{ ");
+ p->debug_print();
+ fprintf(stderr, " }");
+ if (sub) {
+ fprintf(stderr, " sub { ");
+ sub->debug_print();
+ fprintf(stderr, " }");
+ }
+ if (sup) {
+ fprintf(stderr, " sup { ");
+ sup->debug_print();
+ fprintf(stderr, " }");
+ }
+}
+
+void script_box::check_tabs(int level)
+{
+ if (sup)
+ sup->check_tabs(level + 1);
+ if (sub)
+ sub->check_tabs(level + 1);
+ p->check_tabs(level);
+}
diff --git a/contrib/groff/src/preproc/eqn/special.cc b/contrib/groff/src/preproc/eqn/special.cc
new file mode 100644
index 000000000000..310261ae4c59
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/special.cc
@@ -0,0 +1,115 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+#define STRING_FORMAT PREFIX "str%d"
+
+#define SPECIAL_STRING "0s"
+#define SPECIAL_WIDTH_REG "0w"
+#define SPECIAL_HEIGHT_REG "0h"
+#define SPECIAL_DEPTH_REG "0d"
+#define SPECIAL_SUB_KERN_REG "0skern"
+#define SPECIAL_SKEW_REG "0skew"
+
+/*
+For example:
+
+.de Cl
+.ds 0s \Z'\\*[0s]'\v'\\n(0du'\D'l \\n(0wu -\\n(0hu-\\n(0du'\v'\\n(0hu'
+..
+.EQ
+define cancel 'special Cl'
+.EN
+*/
+
+
+class special_box : public pointer_box {
+ char *macro_name;
+public:
+ special_box(char *, box *);
+ ~special_box();
+ int compute_metrics(int);
+ void compute_subscript_kern();
+ void compute_skew();
+ void output();
+ void debug_print();
+};
+
+box *make_special_box(char *s, box *p)
+{
+ return new special_box(s, p);
+}
+
+special_box::special_box(char *s, box *pp) : pointer_box(pp), macro_name(s)
+{
+}
+
+special_box::~special_box()
+{
+ a_delete macro_name;
+}
+
+int special_box::compute_metrics(int style)
+{
+ int r = p->compute_metrics(style);
+ p->compute_subscript_kern();
+ p->compute_skew();
+ printf(".ds " SPECIAL_STRING " \"");
+ p->output();
+ printf("\n");
+ printf(".nr " SPECIAL_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", p->uid);
+ printf(".nr " SPECIAL_HEIGHT_REG " \\n[" HEIGHT_FORMAT "]\n", p->uid);
+ printf(".nr " SPECIAL_DEPTH_REG " \\n[" DEPTH_FORMAT "]\n", p->uid);
+ printf(".nr " SPECIAL_SUB_KERN_REG " \\n[" SUB_KERN_FORMAT "]\n", p->uid);
+ printf(".nr " SPECIAL_SKEW_REG " 0\\n[" SKEW_FORMAT "]\n", p->uid);
+ printf(".%s\n", macro_name);
+ printf(".rn " SPECIAL_STRING " " STRING_FORMAT "\n", uid);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" SPECIAL_WIDTH_REG "]\n", uid);
+ printf(".nr " HEIGHT_FORMAT " 0>?\\n[" SPECIAL_HEIGHT_REG "]\n", uid);
+ printf(".nr " DEPTH_FORMAT " 0>?\\n[" SPECIAL_DEPTH_REG "]\n", uid);
+ printf(".nr " SUB_KERN_FORMAT " 0>?\\n[" SPECIAL_SUB_KERN_REG "]\n", uid);
+ printf(".nr " SKEW_FORMAT " 0\\n[" SPECIAL_SKEW_REG "]\n", uid);
+ // User will have to change MARK_REG if appropriate.
+ return r;
+}
+
+void special_box::compute_subscript_kern()
+{
+ // Already computed in compute_metrics(), so do nothing.
+}
+
+void special_box::compute_skew()
+{
+ // Already computed in compute_metrics(), so do nothing.
+}
+
+void special_box::output()
+{
+ printf("\\*[" STRING_FORMAT "]", uid);
+}
+
+void special_box::debug_print()
+{
+ fprintf(stderr, "special %s { ", macro_name);
+ p->debug_print();
+ fprintf(stderr, " }");
+}
diff --git a/contrib/groff/src/preproc/eqn/sqrt.cc b/contrib/groff/src/preproc/eqn/sqrt.cc
new file mode 100644
index 000000000000..6109ffeaf67c
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/sqrt.cc
@@ -0,0 +1,179 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+
+
+class sqrt_box : public pointer_box {
+public:
+ sqrt_box(box *);
+ int compute_metrics(int style);
+ void output();
+ void debug_print();
+ void check_tabs(int);
+};
+
+box *make_sqrt_box(box *pp)
+{
+ return new sqrt_box(pp);
+}
+
+sqrt_box::sqrt_box(box *pp) : pointer_box(pp)
+{
+}
+
+#define SQRT_CHAR "\\(sr"
+#define RADICAL_EXTENSION_CHAR "\\[radicalex]"
+
+#define SQRT_CHAIN "\\[sr\\\\n[" INDEX_REG "]]"
+#define BAR_CHAIN "\\[radicalex\\\\n[" INDEX_REG "]]"
+
+int sqrt_box::compute_metrics(int style)
+{
+ // 11
+ int r = p->compute_metrics(cramped_style(style));
+ printf(".nr " TEMP_REG " \\n[" HEIGHT_FORMAT "]+\\n[" DEPTH_FORMAT
+ "]+%dM+(%dM/4)\n",
+ p->uid, p->uid, default_rule_thickness,
+ (style > SCRIPT_STYLE ? x_height : default_rule_thickness));
+ printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid);
+ printf(".ds " SQRT_STRING_FORMAT " " SQRT_CHAR "\n", uid);
+ printf(".ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n");
+ printf(".nr " SQRT_WIDTH_FORMAT
+ " 0\\w" DELIMITER_CHAR SQRT_CHAR DELIMITER_CHAR "\n",
+ uid);
+ printf(".if \\n[rst]-\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{",
+ default_rule_thickness);
+
+ printf(".nr " INDEX_REG " 0\n"
+ ".de " TEMP_MACRO "\n"
+ ".ie c" SQRT_CHAIN " \\{"
+ ".ds " SQRT_STRING_FORMAT " " SQRT_CHAIN "\n"
+ ".ie c" BAR_CHAIN " .ds " BAR_STRING " " BAR_CHAIN "\n"
+ ".el .ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n"
+ ".nr " SQRT_WIDTH_FORMAT
+ " 0\\w" DELIMITER_CHAR SQRT_CHAIN DELIMITER_CHAR "\n"
+ ".if \\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{"
+ ".nr " INDEX_REG " +1\n"
+ "." TEMP_MACRO "\n"
+ ".\\}\\}\n"
+ ".el .nr " INDEX_REG " 0-1\n"
+ "..\n"
+ "." TEMP_MACRO "\n",
+ uid, uid, default_rule_thickness);
+
+ printf(".if \\n[" INDEX_REG "]<0 \\{");
+
+ // Determine the maximum point size
+ printf(".ps 1000\n");
+ printf(".nr " MAX_SIZE_REG " \\n[.s]\n");
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ // We define a macro that will increase the current point size
+ // until we get a radical sign that's tall enough or we reach
+ // the maximum point size.
+ printf(".de " TEMP_MACRO "\n"
+ ".nr " SQRT_WIDTH_FORMAT
+ " 0\\w" DELIMITER_CHAR "\\*[" SQRT_STRING_FORMAT "]" DELIMITER_CHAR "\n"
+ ".if \\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "]"
+ "&(\\\\n[.s]<\\n[" MAX_SIZE_REG "]) \\{"
+ ".ps +1\n"
+ "." TEMP_MACRO "\n"
+ ".\\}\n"
+ "..\n"
+ "." TEMP_MACRO "\n",
+ uid, uid, default_rule_thickness);
+
+ printf(".\\}\\}\n");
+
+ printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid);
+ // set TEMP_REG to the amount by which the radical sign is too big
+ printf(".nr " TEMP_REG " \\n[rst]-\\n[rsb]-%dM-\\n[" TEMP_REG "]\n",
+ default_rule_thickness);
+ // If TEMP_REG is negative, the bottom of the radical sign should
+ // be -TEMP_REG above the bottom of p. If it's positive, the bottom
+ // of the radical sign should be TEMP_REG/2 below the bottom of p.
+ // This calculates the amount by which the baseline of the radical
+ // should be raised.
+ printf(".nr " SUP_RAISE_FORMAT " (-\\n[" TEMP_REG "]>?(-\\n[" TEMP_REG "]/2))"
+ "-\\n[rsb]-\\n[" DEPTH_FORMAT "]\n", uid, p->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
+ ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n",
+ uid, p->uid, uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
+ ">?(-\\n[" SUP_RAISE_FORMAT "]-\\n[rsb])\n",
+ uid, p->uid, uid);
+ // Do this last, so we don't lose height and depth information on
+ // the radical sign.
+ // Remember that the width of the bar might be greater than the width of p.
+
+ printf(".nr " TEMP_REG " "
+ "\\n[" WIDTH_FORMAT "]"
+ ">?\\w" DELIMITER_CHAR "\\*[" BAR_STRING "]" DELIMITER_CHAR "\n",
+ p->uid);
+ printf(".as " SQRT_STRING_FORMAT " "
+ "\\l'\\n[" TEMP_REG "]u\\&\\*[" BAR_STRING "]'\n",
+ uid);
+ printf(".nr " WIDTH_FORMAT " \\n[" TEMP_REG "]"
+ "+\\n[" SQRT_WIDTH_FORMAT "]\n",
+ uid, uid);
+
+ if (r)
+ printf(".nr " MARK_REG " +\\n[" SQRT_WIDTH_FORMAT "]\n", uid);
+ // the top of the bar might be higher than the top of the radical sign
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
+ ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n",
+ uid, p->uid, uid);
+ // put a bit of extra space above the bar
+ printf(".nr " HEIGHT_FORMAT " +%dM\n", uid, default_rule_thickness);
+ printf(".ps \\n[" SIZE_FORMAT "]\n", uid);
+ return r;
+}
+
+void sqrt_box::output()
+{
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid);
+ printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid);
+ printf("\\*[" SQRT_STRING_FORMAT "]", uid);
+ printf("\\s[\\n[" SIZE_FORMAT "]]", uid);
+ printf(DELIMITER_CHAR);
+
+ printf("\\Z" DELIMITER_CHAR);
+ printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u"
+ "+\\n[" SQRT_WIDTH_FORMAT "]u/2u'",
+ uid, p->uid, uid);
+ p->output();
+ printf(DELIMITER_CHAR);
+
+ printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid);
+}
+
+void sqrt_box::debug_print()
+{
+ fprintf(stderr, "sqrt { ");
+ p->debug_print();
+ fprintf(stderr, " }");
+}
+
+void sqrt_box::check_tabs(int level)
+{
+ p->check_tabs(level + 1);
+}
diff --git a/contrib/groff/src/preproc/eqn/text.cc b/contrib/groff/src/preproc/eqn/text.cc
new file mode 100644
index 000000000000..b0f1700c221d
--- /dev/null
+++ b/contrib/groff/src/preproc/eqn/text.cc
@@ -0,0 +1,528 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "eqn.h"
+#include "pbox.h"
+#include "ptable.h"
+
+class char_box : public simple_box {
+ unsigned char c;
+ char next_is_italic;
+ char prev_is_italic;
+public:
+ char_box(unsigned char);
+ void debug_print();
+ void output();
+ int is_char();
+ int left_is_italic();
+ int right_is_italic();
+ void hint(unsigned);
+ void handle_char_type(int, int);
+};
+
+class special_char_box : public simple_box {
+ char *s;
+public:
+ special_char_box(const char *);
+ ~special_char_box();
+ void output();
+ void debug_print();
+ int is_char();
+ void handle_char_type(int, int);
+};
+
+const char *spacing_type_table[] = {
+ "ordinary",
+ "operator",
+ "binary",
+ "relation",
+ "opening",
+ "closing",
+ "punctuation",
+ "inner",
+ "suppress",
+ 0,
+};
+
+const int DIGIT_TYPE = 0;
+const int LETTER_TYPE = 1;
+
+const char *font_type_table[] = {
+ "digit",
+ "letter",
+ 0,
+};
+
+struct char_info {
+ int spacing_type;
+ int font_type;
+ char_info();
+};
+
+char_info::char_info()
+: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE)
+{
+}
+
+static char_info char_table[256];
+
+declare_ptable(char_info)
+implement_ptable(char_info)
+
+PTABLE(char_info) special_char_table;
+
+static int get_special_char_spacing_type(const char *ch)
+{
+ char_info *p = special_char_table.lookup(ch);
+ return p ? p->spacing_type : ORDINARY_TYPE;
+}
+
+static int get_special_char_font_type(const char *ch)
+{
+ char_info *p = special_char_table.lookup(ch);
+ return p ? p->font_type : DIGIT_TYPE;
+}
+
+static void set_special_char_type(const char *ch, int st, int ft)
+{
+ char_info *p = special_char_table.lookup(ch);
+ if (!p) {
+ p = new char_info;
+ special_char_table.define(ch, p);
+ }
+ if (st >= 0)
+ p->spacing_type = st;
+ if (ft >= 0)
+ p->font_type = ft;
+}
+
+void init_char_table()
+{
+ set_special_char_type("pl", 2, -1); // binary
+ set_special_char_type("mi", 2, -1);
+ set_special_char_type("eq", 3, -1); // relation
+ set_special_char_type("<=", 3, -1);
+ set_special_char_type(">=", 3, -1);
+ char_table['}'].spacing_type = 5; // closing
+ char_table[')'].spacing_type = 5;
+ char_table[']'].spacing_type = 5;
+ char_table['{'].spacing_type = 4; // opening
+ char_table['('].spacing_type = 4;
+ char_table['['].spacing_type = 4;
+ char_table[','].spacing_type = 6; // punctuation
+ char_table[';'].spacing_type = 6;
+ char_table[':'].spacing_type = 6;
+ char_table['.'].spacing_type = 6;
+ char_table['>'].spacing_type = 3;
+ char_table['<'].spacing_type = 3;
+ char_table['*'].spacing_type = 2; // binary
+ for (int i = 0; i < 256; i++)
+ if (csalpha(i))
+ char_table[i].font_type = LETTER_TYPE;
+}
+
+static int lookup_spacing_type(const char *type)
+{
+ for (int i = 0; spacing_type_table[i] != 0; i++)
+ if (strcmp(spacing_type_table[i], type) == 0)
+ return i;
+ return -1;
+}
+
+static int lookup_font_type(const char *type)
+{
+ for (int i = 0; font_type_table[i] != 0; i++)
+ if (strcmp(font_type_table[i], type) == 0)
+ return i;
+ return -1;
+}
+
+void box::set_spacing_type(char *type)
+{
+ int t = lookup_spacing_type(type);
+ if (t < 0)
+ error("unrecognised type `%1'", type);
+ else
+ spacing_type = t;
+ a_delete type;
+}
+
+char_box::char_box(unsigned char cc)
+: c(cc), next_is_italic(0), prev_is_italic(0)
+{
+ spacing_type = char_table[c].spacing_type;
+}
+
+void char_box::hint(unsigned flags)
+{
+ if (flags & HINT_PREV_IS_ITALIC)
+ prev_is_italic = 1;
+ if (flags & HINT_NEXT_IS_ITALIC)
+ next_is_italic = 1;
+}
+
+void char_box::output()
+{
+ int font_type = char_table[c].font_type;
+ if (font_type != LETTER_TYPE)
+ printf("\\f[%s]", current_roman_font);
+ if (!prev_is_italic)
+ fputs("\\,", stdout);
+ if (c == '\\')
+ fputs("\\e", stdout);
+ else
+ putchar(c);
+ if (!next_is_italic)
+ fputs("\\/", stdout);
+ else
+ fputs("\\&", stdout); // suppress ligaturing and kerning
+ if (font_type != LETTER_TYPE)
+ fputs("\\fP", stdout);
+}
+
+int char_box::left_is_italic()
+{
+ int font_type = char_table[c].font_type;
+ return font_type == LETTER_TYPE;
+}
+
+int char_box::right_is_italic()
+{
+ int font_type = char_table[c].font_type;
+ return font_type == LETTER_TYPE;
+}
+
+int char_box::is_char()
+{
+ return 1;
+}
+
+void char_box::debug_print()
+{
+ if (c == '\\') {
+ putc('\\', stderr);
+ putc('\\', stderr);
+ }
+ else
+ putc(c, stderr);
+}
+
+special_char_box::special_char_box(const char *t)
+{
+ s = strsave(t);
+ spacing_type = get_special_char_spacing_type(s);
+}
+
+special_char_box::~special_char_box()
+{
+ a_delete s;
+}
+
+void special_char_box::output()
+{
+ int font_type = get_special_char_font_type(s);
+ if (font_type != LETTER_TYPE)
+ printf("\\f[%s]", current_roman_font);
+ printf("\\,\\[%s]\\/", s);
+ if (font_type != LETTER_TYPE)
+ printf("\\fP");
+}
+
+int special_char_box::is_char()
+{
+ return 1;
+}
+
+void special_char_box::debug_print()
+{
+ fprintf(stderr, "\\[%s]", s);
+}
+
+
+void char_box::handle_char_type(int st, int ft)
+{
+ if (st >= 0)
+ char_table[c].spacing_type = st;
+ if (ft >= 0)
+ char_table[c].font_type = ft;
+}
+
+void special_char_box::handle_char_type(int st, int ft)
+{
+ set_special_char_type(s, st, ft);
+}
+
+void set_char_type(const char *type, char *ch)
+{
+ assert(ch != 0);
+ int st = lookup_spacing_type(type);
+ int ft = lookup_font_type(type);
+ if (st < 0 && ft < 0) {
+ error("bad character type `%1'", type);
+ a_delete ch;
+ return;
+ }
+ box *b = split_text(ch);
+ b->handle_char_type(st, ft);
+ delete b;
+}
+
+/* We give primes special treatment so that in ``x' sub 2'', the ``2''
+will be tucked under the prime */
+
+class prime_box : public pointer_box {
+ box *pb;
+public:
+ prime_box(box *);
+ ~prime_box();
+ int compute_metrics(int style);
+ void output();
+ void compute_subscript_kern();
+ void debug_print();
+ void handle_char_type(int, int);
+};
+
+box *make_prime_box(box *pp)
+{
+ return new prime_box(pp);
+}
+
+prime_box::prime_box(box *pp) : pointer_box(pp)
+{
+ pb = new special_char_box("fm");
+}
+
+prime_box::~prime_box()
+{
+ delete pb;
+}
+
+int prime_box::compute_metrics(int style)
+{
+ int res = p->compute_metrics(style);
+ pb->compute_metrics(style);
+ printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]"
+ "+\\n[" WIDTH_FORMAT "]\n",
+ uid, p->uid, pb->uid);
+ printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
+ ">?\\n[" HEIGHT_FORMAT "]\n",
+ uid, p->uid, pb->uid);
+ printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
+ ">?\\n[" DEPTH_FORMAT "]\n",
+ uid, p->uid, pb->uid);
+ return res;
+}
+
+void prime_box::compute_subscript_kern()
+{
+ p->compute_subscript_kern();
+ printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
+ "+\\n[" SUB_KERN_FORMAT "]>?0\n",
+ uid, pb->uid, p->uid);
+}
+
+void prime_box::output()
+{
+ p->output();
+ pb->output();
+}
+
+void prime_box::handle_char_type(int st, int ft)
+{
+ p->handle_char_type(st, ft);
+ pb->handle_char_type(st, ft);
+}
+
+void prime_box::debug_print()
+{
+ p->debug_print();
+ putc('\'', stderr);
+}
+
+box *split_text(char *text)
+{
+ list_box *lb = 0;
+ box *fb = 0;
+ char *s = text;
+ while (*s != '\0') {
+ char c = *s++;
+ box *b = 0;
+ switch (c) {
+ case '+':
+ b = new special_char_box("pl");
+ break;
+ case '-':
+ b = new special_char_box("mi");
+ break;
+ case '=':
+ b = new special_char_box("eq");
+ break;
+ case '\'':
+ b = new special_char_box("fm");
+ break;
+ case '<':
+ if (*s == '=') {
+ b = new special_char_box("<=");
+ s++;
+ break;
+ }
+ goto normal_char;
+ case '>':
+ if (*s == '=') {
+ b = new special_char_box(">=");
+ s++;
+ break;
+ }
+ goto normal_char;
+ case '\\':
+ if (*s == '\0') {
+ lex_error("bad escape");
+ break;
+ }
+ c = *s++;
+ switch (c) {
+ case '(':
+ {
+ char buf[3];
+ if (*s != '\0') {
+ buf[0] = *s++;
+ if (*s != '\0') {
+ buf[1] = *s++;
+ buf[2] = '\0';
+ b = new special_char_box(buf);
+ }
+ else {
+ lex_error("bad escape");
+ }
+ }
+ else {
+ lex_error("bad escape");
+ }
+ }
+ break;
+ case '[':
+ {
+ char *ch = s;
+ while (*s != ']' && *s != '\0')
+ s++;
+ if (*s == '\0')
+ lex_error("bad escape");
+ else {
+ *s++ = '\0';
+ b = new special_char_box(ch);
+ }
+ }
+ break;
+ case 'f':
+ case 'g':
+ case 'k':
+ case 'n':
+ case '*':
+ {
+ char *escape_start = s - 2;
+ switch (*s) {
+ case '(':
+ if (*++s != '\0')
+ ++s;
+ break;
+ case '[':
+ for (++s; *s != '\0' && *s != ']'; s++)
+ ;
+ break;
+ }
+ if (*s == '\0')
+ lex_error("bad escape");
+ else {
+ ++s;
+ char *buf = new char[s - escape_start + 1];
+ memcpy(buf, escape_start, s - escape_start);
+ buf[s - escape_start] = '\0';
+ b = new quoted_text_box(buf);
+ }
+ }
+ break;
+ case '-':
+ case '_':
+ {
+ char buf[2];
+ buf[0] = c;
+ buf[1] = '\0';
+ b = new special_char_box(buf);
+ }
+ break;
+ case '`':
+ b = new special_char_box("ga");
+ break;
+ case '\'':
+ b = new special_char_box("aa");
+ break;
+ case 'e':
+ case '\\':
+ b = new char_box('\\');
+ break;
+ case '^':
+ case '|':
+ case '0':
+ {
+ char buf[3];
+ buf[0] = '\\';
+ buf[1] = c;
+ buf[2] = '\0';
+ b = new quoted_text_box(strsave(buf));
+ break;
+ }
+ default:
+ lex_error("unquoted escape");
+ b = new quoted_text_box(strsave(s - 2));
+ s = strchr(s, '\0');
+ break;
+ }
+ break;
+ default:
+ normal_char:
+ b = new char_box(c);
+ break;
+ }
+ while (*s == '\'') {
+ if (b == 0)
+ b = new quoted_text_box(0);
+ b = new prime_box(b);
+ s++;
+ }
+ if (b != 0) {
+ if (lb != 0)
+ lb->append(b);
+ else if (fb != 0) {
+ lb = new list_box(fb);
+ lb->append(b);
+ }
+ else
+ fb = b;
+ }
+ }
+ delete text;
+ if (lb != 0)
+ return lb;
+ else if (fb != 0)
+ return fb;
+ else
+ return new quoted_text_box(0);
+}
+
diff --git a/contrib/groff/src/preproc/grn/Makefile.sub b/contrib/groff/src/preproc/grn/Makefile.sub
new file mode 100644
index 000000000000..ffa0ad209cc4
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/Makefile.sub
@@ -0,0 +1,17 @@
+PROG=grn
+MAN1=grn.n
+MLIB=$(LIBM)
+XLIBS=$(LIBGROFF)
+OBJS=\
+ hdb.o \
+ hpoint.o \
+ hgraph.o \
+ main.o
+CCSRCS=\
+ $(srcdir)/hdb.cc \
+ $(srcdir)/hpoint.cc \
+ $(srcdir)/hgraph.cc \
+ $(srcdir)/main.cc
+HDRS=\
+ $(srcdir)/gprint.h
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/grn/README b/contrib/groff/src/preproc/grn/README
new file mode 100644
index 000000000000..b5b9fc9abd83
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/README
@@ -0,0 +1,60 @@
+This is grn from the Berkeley ditroff distribution. It has no
+AT&T code and is therefore freely distributable.
+
+Tim Theisen <tim@cs.wisc.edu>
+
+=====================================================================
+
+This is the modified code for the groff. It uses the different
+devxxx format that is ascii rather than binary as in the
+Berkeley distribution. Since groff does not have the \Ds option
+for line drawing (dotted, dashed, etc.), this version includes
+the routines for drawing curves and arcs, so it does not use the
+\D~, \Da nor \Dc. Although also included in here is a routine
+for drawing the optional gremlin style curves, it is not used
+because the gremlin editor uses the conventional spline
+algorithm. The Berkeley grn has the choice of different
+stipples. Here, only different shades of gray will be painted
+depending on the gremlin file. It is possible to upgrade this at
+a later time. (Daniel Senderowicz <daniel@synchrods.com> 12/28/99)
+
+=====================================================================
+
+It has been further modified by Werner Lemberg <wl@gnu.org> to fit
+better into the groff package.
+
+ . Replaced Makefile with Makefile.sub.
+
+ . Removed dev.h since it is unused.
+
+ . Renamed grn.1 to grn.man; this man page has been extensively
+ revised.
+
+ . Used error() and fatal() from libgroff for all source files.
+
+ . Renamed *.c to *.cc; updates as needed for C++ (prototypes, proper
+ casts, standard header files etc). Heavy formatting.
+
+ . main.cc:
+
+ Using groff's default values instead of DEVDIR, DEFAULTDEV, PRINTER,
+ TYPESETTER, and GREMLIB.
+
+ `res' is now an integer.
+
+ Added `-C' command flag (for compatibility mode) as with other
+ preprocessors.
+
+ Added `-F' and `-v' option (similar to troff).
+
+ Renamed `-L' option to `-M' for consistence.
+
+ Removed `-P' option.
+
+ Using font::load_desc() for scanning DESC files.
+
+ Removed SYSV-specific code.
+
+ Using macro_path.open_file() for getting gremlin graphic files.
+
+ Added usage().
diff --git a/contrib/groff/src/preproc/grn/gprint.h b/contrib/groff/src/preproc/grn/gprint.h
new file mode 100644
index 000000000000..b25305b3ca03
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/gprint.h
@@ -0,0 +1,84 @@
+/* Last non-groff version: gprint.h 1.1 84/10/08
+ *
+ * This file contains standard definitions used by the gprint program.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+
+#define xorn(x,y) (x)
+ /* was 512 */
+#define yorn(x,y) (511 - (y)) /* switch direction for */
+ /* y-coordinates */
+
+#define STYLES 6
+#define SIZES 4
+#define FONTS 4
+#define SOLID -1
+#define DOTTED 004 /* 014 */
+#define DASHED 020 /* 034 */
+#define DOTDASHED 024 /* 054 */
+#define LONGDASHED 074
+
+#define DEFTHICK -1 /* default thicknes */
+#define DEFSTYLE SOLID /* default line style */
+
+#define TRUE 1
+#define FALSE 0
+
+#define nullelt -1
+#define nullpt -1
+#define nullun NULL
+
+#define BOTLEFT 0
+#define BOTRIGHT 1
+#define CENTCENT 2
+#define VECTOR 3
+#define ARC 4
+#define CURVE 5
+#define POLYGON 6
+#define TOPLEFT 10
+#define TOPCENT 11
+#define TOPRIGHT 12
+#define CENTLEFT 13
+#define CENTRIGHT 14
+#define BOTCENT 15
+#define TEXT(t) ( (t <= CENTCENT) || (t >= TOPLEFT) )
+
+/* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * The above (TEXT) test is dependent on the relative values of the
+ * constants and will have to change if these values change or if new
+ * commands are added with value greater than BOTCENT
+ */
+
+#define NUSER 4
+#define NFONTS 4
+#define NBRUSHES 6
+#define NSIZES 4
+#define NJUSTS 9
+#define NSTIPPLES 16
+
+#define ADD 1
+#define DELETE 2
+#define MOD 3
+
+typedef struct point {
+ float x, y;
+ struct point *nextpt;
+} POINT;
+
+typedef struct elmt {
+ int type, brushf, size, textlength;
+ char *textpt;
+ POINT *ptlist;
+ struct elmt *nextelt, *setnext;
+} ELT;
+
+#define DBNextElt(elt) (elt->nextelt)
+#define DBNextofSet(elt) (elt->setnext)
+#define DBNullelt(elt) (elt == NULL)
+#define Nullpoint(pt) ((pt) == (POINT *) NULL)
+#define PTNextPoint(pt) (pt->nextpt)
+
+/* EOF */
diff --git a/contrib/groff/src/preproc/grn/grn.man b/contrib/groff/src/preproc/grn/grn.man
new file mode 100644
index 000000000000..f2613da1b27f
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/grn.man
@@ -0,0 +1,636 @@
+.ig \"-*- nroff -*-
+Copyright (C) 2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH @G@GRN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@grn \- groff preprocessor for gremlin files
+.SH SYNOPSIS
+.BR @g@grn
+[
+.B \-Cv
+]
+[
+.BI \-T dev
+]
+[
+.BI \-M dir
+]
+[
+.BI \-F dir
+]
+[
+.IR file\.\.\.
+]
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.I @g@grn
+is a preprocessor for including
+.I gremlin
+pictures in
+.I groff
+input.
+.I @g@grn
+writes to standard output, processing only input lines between two that
+start with
+.B .GS
+and
+.BR .GE.
+Those lines must contain
+.I @g@grn
+commands (see below).
+These commands request a
+.I gremlin
+file, and the picture in that file is
+converted and placed in the
+.I @g@troff
+input stream.
+The
+.B .GS
+request may be followed by a C, L, or R to center, left, or right
+justify the whole
+.I gremlin
+picture (default justification is center).
+If no
+.I file
+is mentioned, the standard input is read.
+At the end of the picture, the position on the page is the bottom of the
+.I gremlin
+picture.
+If the
+.I @g@grn
+entry is ended with
+.B .GF
+instead of
+.BR .GE ,
+the position is left at the top of the picture.
+.PP
+Please note that currently only the \-me macro package has support for
+.BR .GS ,
+.BR .GE ,
+and
+.BR .GF .
+.PP
+The following command-line options are understood:
+.TP
+.BI \-T dev
+Prepare output for printer
+.IR dev .
+The default device is
+.BR @DEVICE@ .
+See
+.BR groff (@MAN1EXT@)
+for acceptable devices.
+.TP
+.BI \-M dir
+Prepend
+.I dir
+to the default search path for
+.I gremlin
+files.
+The default path is (in that order) the current directory, the home
+directory,
+.BR @SYSTEMMACRODIR@ ,
+.BR @LOCALMACRODIR@ ,
+and
+.BR @MACRODIR@ .
+.TP
+.BI \-F dir
+Search
+.I dir
+for subdirectories
+.BI dev name
+.RI ( name
+is the name of the device) for the
+.B DESC
+file before the normal
+.BR @FONTDIR@ .
+.TP
+.B \-C
+Recognize
+.B .GS
+and
+.B .GE
+(resp.
+.BR .GF )
+even when followed by a character other than space or newline.
+.\".TP
+.\".B \-s
+.\"This switch causes the picture to be traversed twice:
+.\"The first time, only the interiors of filled polygons (as borderless
+.\"polygons) are printed.
+.\"The second time, the outline is printed as a series of line segments.
+.\"This way, postprocessors that overwrite rather than merge picture elements
+.\"(such as Postscript) can still have text and graphics on a shaded
+.\"background.
+.TP
+.B \-v
+Print the version number.
+.SH GRN COMMANDS
+Each input line between
+.B .GS
+and
+.B .GE
+may have one
+.I @g@grn
+command.
+Commands consist of one or two strings separated by white space, the first
+string being the command and the second its operand.
+Commands may be upper or lower case and abbreviated down to one character.
+.PP
+Commands that affect a picture's environment (those listed before
+.BR default ,
+see below) are only in effect for the current picture:
+The environment is reinitialized to the defaults at the start of the next
+picture.
+The commands are as follows:
+.TP
+.BI 1\ N
+.TQ
+.BI 2\ N
+.TQ
+.BI 3\ N
+.TQ
+.BI 4\ N
+Set
+.IR gremlin 's
+text size number 1 (2, 3, or 4) to
+.I N
+points.
+The default is 12 (resp. 16, 24, and 36).
+.TP
+.BI roman\ f
+.TQ
+.BI italics\ f
+.TQ
+.BI bold\ f
+.TQ
+.BI special\ f
+Set the roman (italics, bold, or special) font to
+.IR @g@troff 's
+font
+.I f
+(either a name or number).
+The default is R (resp. I, B, and S).
+.TP
+.BI l\ f
+.TQ
+.BI stipple\ f
+Set the stipple font to
+.IR @g@troff 's
+stipple font
+.I f
+(name or number).
+The command
+.B stipple
+may be abbreviated down as far as `st' (to avoid
+confusion with
+.BR special ).
+There is
+.I no
+default for stipples (unless one is set by the default command), and it is
+illegal to include a
+.I gremlin
+picture with polygons without specifying a
+stipple font.
+.TP
+.BI x\ N
+.TQ
+.BI scale\ N
+Magnify the picture (in addition to any default magnification) by
+.IR N ,
+a floating point number larger than zero.
+The command
+.B scale
+may be abbreviated down to `sc'.
+.TP
+.BI narrow\ N
+.TQ
+.BI medium\ N
+.TQ
+.BI thick\ N
+Set the thickness of
+.IR gremlin 's
+narrow (resp. medium and thick) lines to
+.I N
+times 0.15pt (this value can be changed at compile time).
+The default is 1.0 (resp. 3.0 and 5.0), which corresponds to 0.15pt
+(resp. 0.45pt and 0.75pt).
+A thickness value of zero selects the smallest available line thickness.
+Negative values cause the line thickness to be proportional to the current
+point size.
+.TP
+.BI pointscale\ <off/on>
+Scale text to match the picture.
+Gremlin text is usually printed in the point size specified with the
+commands
+.BR 1 ,\ 2 ,\ 3 ,\ or\ 4
+regardless of any scaling factors in the picture.
+Setting
+.B pointscale
+will cause the point sizes to scale with the picture (within
+.IR @g@troff 's
+limitations, of course).
+An operand of anything but
+.I off
+will turn text scaling on.
+.TP
+.B default
+Reset the picture environment defaults to the settings in the current
+picture.
+This is meant to be used as a global parameter setting mechanism at the
+beginning of the
+.I @g@troff
+input file, but can be used at any time to reset the
+default settings.
+.TP
+.BI width\ N
+Forces the picture to be
+.I N
+inches wide.
+This overrides any scaling factors present in the same picture.
+.RB ` width
+.IR 0 '
+is ignored.
+.TP
+.BI height\ N
+Forces picture to be
+.I N
+inches high, overriding other scaling factors.
+If both `width' and `height' are specified the tighter constraint will
+determine the scale of the picture.
+.B Height
+and
+.B width
+commands are not saved with a
+.B default
+command.
+They will, however, affect point size scaling if that option is set.
+.TP
+.BI file\ name
+Get picture from
+.I gremlin
+file
+.I name
+located the current directory (or in the library directory; see the
+.B \-M
+option above).
+If two
+.B file
+commands are given, the second one overrides the first.
+If
+.I name
+doesn't exist, an error message is reported and processing continues from
+the
+.B .GE
+line.
+.SH NOTES ABOUT GROFF
+Since
+.I @g@grn
+is a preprocessor, it doesn't know about current indents, point sizes,
+margins, number registers, etc.
+Consequently, no
+.I @g@troff
+input can be placed between the
+.B .GS
+and
+.B .GE
+requests.
+However,
+.I gremlin
+text is now processed by
+.IR @g@troff ,
+so anything legal in a single line of
+.I @g@troff
+input is legal in a line of
+.I gremlin
+text (barring `.' directives at the beginning of a line).
+Thus, it is possible to have equations within a
+.I gremlin
+figure by including in the
+.I gremlin
+file
+.I eqn
+expressions enclosed by previously defined delimiters (e.g.
+.IR $$ ).
+.PP
+When using
+.I @g@grn
+along with other preprocessors, it is best to run
+.I tbl
+before
+.IR @g@grn ,
+.IR pic ,
+and/or
+.I ideal
+to avoid overworking
+.IR tbl .
+.I Eqn
+should always be run last.
+.PP
+A picture is considered an entity, but that doesn't stop
+.I @g@troff
+from trying to break it up if it falls off the end of a page.
+Placing the picture between `keeps' in \-me macros will ensure proper
+placement.
+.PP
+.I @g@grn
+uses
+.IR @g@troff 's
+number registers
+.B g1
+through
+.B g9
+and sets registers
+.B g1
+and
+.B g2
+to the width and height of the
+.I gremlin
+figure (in device units) before entering the
+.B .GS
+request (this is for those who want to rewrite these macros).
+.SH GREMLIN FILE FORMAT
+There exist two distinct
+.I gremlin
+file formats, the original format from the
+.I AED
+graphic terminal version, and the
+.I SUN
+or
+.I X11
+version.
+An extension to the
+.IR SUN / X11
+version allowing reference points with negative coordinates is
+.B not
+compatible with the
+.I AED
+version.
+As long as a
+.I gremlin
+file does not contain negative coordinates, either format will be read
+correctly by either version of
+.I gremlin
+or
+.IR @g@grn .
+The other difference to the
+.IR SUN / X11
+format is the use of names for picture objects (e.g., POLYGON, CURVE)
+instead of numbers.
+Files representing the same picture are shown in Table 1 in each format.
+.sp
+.DS
+.TS
+center, tab(@);
+l lw(0.1i) l.
+sungremlinfile@@gremlinfile
+0 240.00 128.00@@0 240.00 128.00
+CENTCENT@@2
+240.00 128.00@@240.00 128.00
+185.00 120.00@@185.00 120.00
+240.00 120.00@@240.00 120.00
+296.00 120.00@@296.00 120.00
+*@@-1.00 -1.00
+2 3@@2 3
+10 A Triangle@@10 A Triangle
+POLYGON@@6
+224.00 416.00@@224.00 416.00
+96.00 160.00@@96.00 160.00
+384.00 160.00@@384.00 160.00
+*@@-1.00 -1.00
+5 1@@5 1
+0@@0
+-1@@-1
+.T&
+css.
+.sp
+Table 1. File examples
+.TE
+.DE
+.sp
+.IP \(bu
+The first line of each
+.I gremlin
+file contains either the string
+.B gremlinfile
+.RI ( AED
+version) or
+.B sungremlinfile
+.RI ( SUN / X11 )
+.IP \(bu
+The second line of the file contains an orientation, and
+.B x
+and
+.B y
+values for a positioning point, separated by spaces.
+The orientation, either
+.B 0
+or
+.BR 1 ,
+is ignored by the
+.IR SUN / X11
+version.
+.B 0
+means that
+.I gremlin
+will display things in horizontal format (drawing area wider than it is
+tall, with menu across top).
+.B 1
+means that
+.I gremlin
+will display things in vertical format (drawing area taller than it is wide,
+with menu on left side).
+.B x
+and
+.B y
+are floating point values giving a positioning point to be used when this
+file is read into another file.
+The stuff on this line really isn't all that important; a value of ``1 0.00
+0.00'' is suggested.
+.IP \(bu
+The rest of the file consists of zero or more element specifications.
+After the last element specification is a line containing the string ``-1''.
+.SH ELEMENT SPECIFICATIONS
+.IP \(bu
+The first line of each element contains a single decimal number giving the
+type of the element
+.RI ( AED
+version) or its ASCII name
+.RI ( SUN / X11
+version).
+See Table 2.
+.sp
+.DS
+.TS
+center, tab(@);
+css
+ccc
+nll.
+\fIgremlin\fP File Format \(mi Object Type Specification
+.sp
+\fIAED\fP Number@\fISUN\fP/\fIX11\fP Name@Description
+0@BOTLEFT@bottom-left-justified text
+1@BOTRIGHT@bottom-right-justified text
+2@CENTCENT@center-justified text
+3@VECTOR@vector
+4@ARC@arc
+5@CURVE@curve
+6@POLYGON@polygon
+10@TOPLEFT@top-left-justified text
+11@TOPCENT@top-center-justified text
+12@TOPRIGHT@top-right-justified text
+13@CENTLEFT@left-center-justified text
+14@CENTRIGHT@right-center-justified text
+15@BOTCENT@bottom-center-justified text
+.T&
+css.
+.sp
+Table 2.
+Type Specifications in \fIgremlin\fP Files
+.TE
+.DE
+.sp
+.IP \(bu
+After the object type comes a variable number of lines, each specifying a
+point used to display the element.
+Each line contains an x-coordinate and a y-coordinate in floating point
+format, separated by spaces.
+The list of points is terminated by a line containing the string ``-1.0
+-1.0''
+.RI ( AED
+version) or a single asterisk, ``*''
+.RI ( SUN / X11
+version).
+.IP \(bu
+After the points comes a line containing two decimal values, giving the
+brush and size for the element.
+The brush determines the style in which things are drawn.
+For vectors, arcs, and curves there are six legal brush values:
+.sp
+.DS
+.TS
+center, tab(@);
+ncw(0.1i)l.
+1 \(mi@@thin dotted lines
+2 \(mi@@thin dot-dashed lines
+3 \(mi@@thick solid lines
+4 \(mi@@thin dashed lines
+5 \(mi@@thin solid lines
+6 \(mi@@medium solid lines
+.TE
+.DE
+.sp
+For polygons, one more value, 0, is legal.
+It specifies a polygon with an invisible border.
+For text, the brush selects a font as follows:
+.sp
+.DS
+.TS
+center, tab(@);
+ncw(0.1i)l.
+1 \(mi@@roman (R font in groff)
+2 \(mi@@italics (I font in groff)
+3 \(mi@@bold (B font in groff)
+4 \(mi@@special (S font in groff)
+.TE
+.DE
+.sp
+If you're using
+.I @g@grn
+to run your pictures through
+.IR groff ,
+the font is really just a starting font:
+The text string can contain formatting sequences like
+``\\fI''
+or
+``\\d''
+which may change the font (as well as do many other things).
+For text, the size field is a decimal value between 1 and 4.
+It selects the size of the font in which the text will be drawn.
+For polygons, this size field is interpreted as a stipple number to fill the
+polygon with.
+The number is used to index into a stipple font at print time.
+.IP \(bu
+The last line of each element contains a decimal number and a string of
+characters, separated by a single space.
+The number is a count of the number of characters in the string.
+This information is only used for text elements, and contains the text
+string.
+There can be spaces inside the text.
+For arcs, curves, and vectors, this line of the element contains the string
+``0''.
+.SH NOTES ON COORDINATES
+.I gremlin
+was designed for
+.IR AED s,
+and its coordinates reflect the
+.I AED
+coordinate space.
+For vertical pictures, x-values range 116 to 511, and y-values from 0 to
+483.
+For horizontal pictures, x-values range from 0 to 511 and y-values range
+from 0 to 367.
+Although you needn't absolutely stick to this range, you'll get best results
+if you at least stay in this vicinity.
+Also, point lists are terminated by a point of (-1, -1), so you shouldn't
+ever use negative coordinates.
+.I gremlin
+writes out coordinates using format ``%f1.2''; it's probably a good idea to
+use the same format if you want to modify the
+.I @g@grn
+code.
+.SH NOTES ON SUN/X11 COORDINATES
+There is no longer a restriction on the range of coordinates used to create
+objects in the
+.IR SUN / X11
+version of
+.IR gremlin .
+However, files with negative coordinates
+.B will
+cause problems if displayed on the
+.IR AED .
+.SH FILES
+.Tp \w'@FONTDIR@/devname/DESC'u+3n
+.BI @FONTDIR@/dev name /DESC
+Device description file for device
+.IR name .
+.SH SEE ALSO
+.BR gremlin (1),
+.BR groff (@MAN1EXT@),
+.BR @g@pic (@MAN1EXT@),
+.BR ideal (1)
+.SH HISTORY
+.PP
+David Slattengren and Barry Roitblat wrote the original Berkeley
+.IR @g@grn .
+.PP
+Daniel Senderowicz and Werner Lemberg modified it for
+.IR groff .
diff --git a/contrib/groff/src/preproc/grn/hdb.cc b/contrib/groff/src/preproc/grn/hdb.cc
new file mode 100644
index 000000000000..fd5bb48a679c
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/hdb.cc
@@ -0,0 +1,326 @@
+/* Last non-groff version: hdb.c 1.8 (Berkeley) 84/10/20
+ *
+ * Copyright -C- 1982 Barry S. Roitblat
+ *
+ * This file contains database routines for the hard copy programs of the
+ * gremlin picture editor.
+ */
+
+#include "gprint.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "errarg.h"
+#include "error.h"
+
+#define MAXSTRING 128
+
+/* imports from main.cc */
+
+extern int linenum; /* current line number in input file */
+extern char gremlinfile[]; /* name of file currently reading */
+extern int SUNFILE; /* TRUE if SUN gremlin file */
+extern void savebounds(float x, float y);
+
+/* imports from hpoint.cc */
+
+extern POINT *PTInit();
+extern POINT *PTMakePoint(float x, float y, POINT ** pplist);
+
+
+int DBGetType(register char *s);
+
+
+/*
+ * This routine returns a pointer to an initialized database element which
+ * would be the only element in an empty list.
+ */
+ELT *
+DBInit()
+{
+ return ((ELT *) NULL);
+} /* end DBInit */
+
+
+/*
+ * This routine creates a new element with the specified attributes and
+ * links it into database.
+ */
+ELT *
+DBCreateElt(int type,
+ POINT * pointlist,
+ int brush,
+ int size,
+ char *text,
+ ELT **db)
+{
+ register ELT *temp;
+
+ temp = (ELT *) malloc(sizeof(ELT));
+ temp->nextelt = *db;
+ temp->type = type;
+ temp->ptlist = pointlist;
+ temp->brushf = brush;
+ temp->size = size;
+ temp->textpt = text;
+ *db = temp;
+ return (temp);
+} /* end CreateElt */
+
+
+/*
+ * This routine reads the specified file into a database and returns a
+ * pointer to that database.
+ */
+ELT *
+DBRead(register FILE *file)
+{
+ register int i;
+ register int done; /* flag for input exhausted */
+ register float nx; /* x holder so x is not set before orienting */
+ int type; /* element type */
+ ELT *elist; /* pointer to the file's elements */
+ POINT *plist; /* pointer for reading in points */
+ char string[MAXSTRING], *txt;
+ float x, y; /* x and y are read in point coords */
+ int len, brush, size;
+ int lastpoint;
+
+ SUNFILE = FALSE;
+ elist = DBInit();
+ (void) fscanf(file, "%s\n", string);
+ if (strcmp(string, "gremlinfile")) {
+ if (strcmp(string, "sungremlinfile")) {
+ error("`%1' is not a gremlin file", gremlinfile);
+ return (elist);
+ }
+ SUNFILE = TRUE;
+ }
+
+ (void) fscanf(file, "%d%f%f\n", &size, &x, &y);
+ /* ignore orientation and file positioning point */
+
+ done = FALSE;
+ while (!done) {
+ /* if (fscanf(file,"%s\n", string) == EOF) */
+ /* I changed the scanf format because the element */
+ /* can have two words (e.g. CURVE SPLINE) */
+ if (fscanf(file, "\n%[^\n]\n", string) == EOF) {
+ error("`%1', error in file format", gremlinfile);
+ return (elist);
+ }
+
+ type = DBGetType(string); /* interpret element type */
+ if (type < 0) { /* no more data */
+ done = TRUE;
+ (void) fclose(file);
+ } else {
+#ifdef UW_FASTSCAN
+ (void) xscanf(file, &x, &y); /* always one point */
+#else
+ (void) fscanf(file, "%f%f\n", &x, &y); /* always one point */
+#endif /* UW_FASTSCAN */
+ plist = PTInit(); /* NULL point list */
+
+ /*
+ * Files created on the SUN have point lists terminated by a line
+ * containing only an asterik ('*'). Files created on the AED have
+ * point lists terminated by the coordinate pair (-1.00 -1.00).
+ */
+ if (TEXT(type)) { /* read only first point for TEXT elements */
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+
+#ifdef UW_FASTSCAN
+ while (xscanf(file, &x, &y));
+#else
+ lastpoint = FALSE;
+ do {
+ fgets(string, MAXSTRING, file);
+ if (string[0] == '*') { /* SUN gremlin file */
+ lastpoint = TRUE;
+ } else {
+ (void) sscanf(string, "%f%f", &x, &y);
+ if ((x == -1.00 && y == -1.00) && (!SUNFILE))
+ lastpoint = TRUE;
+ }
+ } while (!lastpoint);
+#endif /* UW_FASTSCAN */
+ } else { /* not TEXT element */
+#ifdef UW_FASTSCAN
+ do {
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+ } while (xscanf(file, &x, &y));
+#else
+ lastpoint = FALSE;
+ while (!lastpoint) {
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+
+ fgets(string, MAXSTRING, file);
+ if (string[0] == '*') { /* SUN gremlin file */
+ lastpoint = TRUE;
+ } else {
+ (void) sscanf(string, "%f%f", &x, &y);
+ if ((x == -1.00 && y == -1.00) && (!SUNFILE))
+ lastpoint = TRUE;
+ }
+ }
+#endif /* UW_FASTSCAN */
+ }
+ (void) fscanf(file, "%d%d\n", &brush, &size);
+ (void) fscanf(file, "%d", &len); /* text length */
+ (void) getc(file); /* eat blank */
+ txt = (char *) malloc((unsigned) len + 1);
+ for (i = 0; i < len; ++i) { /* read text */
+ txt[i] = getc(file);
+ }
+ txt[len] = '\0';
+ (void) DBCreateElt(type, plist, brush, size, txt, &elist);
+ } /* end else */
+ } /* end while not done */ ;
+ return (elist);
+} /* end DBRead */
+
+
+/*
+ * Interpret element type in string s.
+ * Old file format consisted of integer element types.
+ * New file format has literal names for element types.
+ */
+int
+DBGetType(register char *s)
+{
+ if (isdigit(s[0]) || (s[0] == '-')) /* old element format or EOF */
+ return (atoi(s));
+
+ switch (s[0]) {
+ case 'P':
+ return (POLYGON);
+ case 'V':
+ return (VECTOR);
+ case 'A':
+ return (ARC);
+ case 'C':
+ if (s[1] == 'U')
+ return (CURVE);
+ switch (s[4]) {
+ case 'L':
+ return (CENTLEFT);
+ case 'C':
+ return (CENTCENT);
+ case 'R':
+ return (CENTRIGHT);
+ default:
+ fatal("unknown element type");
+ }
+ case 'B':
+ switch (s[3]) {
+ case 'L':
+ return (BOTLEFT);
+ case 'C':
+ return (BOTCENT);
+ case 'R':
+ return (BOTRIGHT);
+ default:
+ fatal("unknown element type");
+ }
+ case 'T':
+ switch (s[3]) {
+ case 'L':
+ return (TOPLEFT);
+ case 'C':
+ return (TOPCENT);
+ case 'R':
+ return (TOPRIGHT);
+ default:
+ fatal("unknown element type");
+ }
+ default:
+ fatal("unknown element type");
+ }
+
+ return 0; /* never reached */
+}
+
+#ifdef UW_FASTSCAN
+/*
+ * Optimization hack added by solomon@crys.wisc.edu, 12/2/86.
+ * A huge fraction of the time was spent reading floating point numbers from
+ * the input file, but the numbers always have the format 'ddd.dd'. Thus
+ * the following special-purpose version of fscanf.
+ *
+ * xscanf(f,xp,yp) does roughly what fscanf(f,"%f%f",xp,yp) does except:
+ * -the next piece of input must be of the form
+ * <space>* <digit>*'.'<digit>* <space>* <digit>*'.'<digit>*
+ * -xscanf eats the character following the second number
+ * -xscanf returns 0 for "end-of-data" indication, 1 otherwise, where
+ * end-of-data is signalled by a '*' [in which case the rest of the
+ * line is gobbled], or by '-1.00 -1.00' [but only if !SUNFILE].
+ */
+int
+xscanf(FILE *f,
+ float *xp,
+ float *yp)
+{
+ register int c, i, j, m, frac;
+ int iscale = 1, jscale = 1; /* x = i/scale, y=j/jscale */
+
+ while ((c = getc(f)) == ' ');
+ if (c == '*') {
+ while ((c = getc(f)) != '\n');
+ return 0;
+ }
+ i = m = frac = 0;
+ while (isdigit(c) || c == '.' || c == '-') {
+ if (c == '-') {
+ m++;
+ c = getc(f);
+ continue;
+ }
+ if (c == '.')
+ frac = 1;
+ else {
+ if (frac)
+ iscale *= 10;
+ i = 10 * i + c - '0';
+ }
+ c = getc(f);
+ }
+ if (m)
+ i = -i;
+ *xp = (double) i / (double) iscale;
+
+ while ((c = getc(f)) == ' ');
+ j = m = frac = 0;
+ while (isdigit(c) || c == '.' || c == '-') {
+ if (c == '-') {
+ m++;
+ c = getc(f);
+ continue;
+ }
+ if (c == '.')
+ frac = 1;
+ else {
+ if (frac)
+ jscale *= 10;
+ j = 10 * j + c - '0';
+ }
+ c = getc(f);
+ }
+ if (m)
+ j = -j;
+ *yp = (double) j / (double) jscale;
+ return (SUNFILE || i != -iscale || j != -jscale);
+}
+#endif /* UW_FASTSCAN */
+
+/* EOF */
diff --git a/contrib/groff/src/preproc/grn/hgraph.cc b/contrib/groff/src/preproc/grn/hgraph.cc
new file mode 100644
index 000000000000..79637209774e
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/hgraph.cc
@@ -0,0 +1,1043 @@
+/* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27
+ *
+ * This file contains the graphics routines for converting gremlin pictures
+ * to troff input.
+ */
+
+#include "gprint.h"
+
+#ifdef NEED_DECLARATION_HYPOT
+extern "C" {
+ double hypot(double, double);
+}
+#endif /* NEED_DECLARATION_HYPOT */
+
+#define MAXVECT 40
+#define MAXPOINTS 200
+#define LINELENGTH 1
+#define PointsPerInterval 64
+#define pi 3.14159265358979324
+#define twopi (2.0 * pi)
+#define len(a, b) hypot((double)(b.x-a.x), (double)(b.y-a.y))
+
+
+extern int dotshifter; /* for the length of dotted curves */
+
+extern int style[]; /* line and character styles */
+extern double thick[];
+extern char *tfont[];
+extern int tsize[];
+extern int stipple_index[]; /* stipple font index for stipples 0 - 16 */
+extern char *stipple; /* stipple type (cf or ug) */
+
+
+extern double troffscale; /* imports from main.c */
+extern double linethickness;
+extern int linmod;
+extern int lastx;
+extern int lasty;
+extern int lastyline;
+extern int ytop;
+extern int ybottom;
+extern int xleft;
+extern int xright;
+extern enum {
+ OUTLINE, FILL, BOTH
+} polyfill;
+
+extern double adj1;
+extern double adj2;
+extern double adj3;
+extern double adj4;
+extern int res;
+
+void HGSetFont(int font, int size);
+void HGPutText(int justify, POINT pnt, register char *string);
+void HGSetBrush(int mode);
+void tmove2(int px, int py);
+void doarc(POINT cp, POINT sp, int angle);
+void tmove(POINT * ptr);
+void cr();
+void drawwig(POINT * ptr);
+void HGtline(int x1, int y1);
+void dx(double x);
+void dy(double y);
+void HGArc(register int cx, register int cy, int px, int py, int angle);
+void picurve(register int *x, register int *y, int npts);
+void Paramaterize(int x[], int y[], float h[], int n);
+void PeriodicSpline(float h[], int z[],
+ float dz[], float d2z[], float d3z[],
+ int npoints);
+void NaturalEndSpline(float h[], int z[],
+ float dz[], float d2z[], float d3z[],
+ int npoints);
+
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: HGPrintElt (element_pointer, baseline)
+ |
+ | Results: Examines a picture element and calls the appropriate
+ | routine(s) to print them according to their type. After the
+ | picture is drawn, current position is (lastx, lasty).
+ *----------------------------------------------------------------------------*/
+
+void
+HGPrintElt(ELT *element,
+ int baseline)
+{
+ register POINT *p1;
+ register POINT *p2;
+ register int length;
+ register int graylevel;
+
+ if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) {
+ /* p1 always has first point */
+ if (TEXT(element->type)) {
+ HGSetFont(element->brushf, element->size);
+ switch (element->size) {
+ case 1:
+ p1->y += adj1;
+ break;
+ case 2:
+ p1->y += adj2;
+ break;
+ case 3:
+ p1->y += adj3;
+ break;
+ case 4:
+ p1->y += adj4;
+ break;
+ default:
+ break;
+ }
+ HGPutText(element->type, *p1, element->textpt);
+ } else {
+ if (element->brushf) /* if there is a brush, the */
+ HGSetBrush(element->brushf); /* graphics need it set */
+
+ switch (element->type) {
+
+ case ARC:
+ p2 = PTNextPoint(p1);
+ tmove(p2);
+ doarc(*p1, *p2, element->size);
+ cr();
+ break;
+
+ case CURVE:
+ length = 0; /* keep track of line length */
+ drawwig(p1);
+ cr();
+ break;
+
+ case VECTOR:
+ length = 0; /* keep track of line length so */
+ tmove(p1); /* single lines don't get long */
+ while (!Nullpoint((p1 = PTNextPoint(p1)))) {
+ HGtline((int) (p1->x * troffscale),
+ (int) (p1->y * troffscale));
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+ cr();
+ break;
+
+ case POLYGON:
+ {
+ /* brushf = style of outline; size = color of fill:
+ * on first pass (polyfill=FILL), do the interior using 'P'
+ * unless size=0
+ * on second pass (polyfill=OUTLINE), do the outline using a series
+ * of vectors. It might make more sense to use \D'p ...',
+ * but there is no uniform way to specify a 'fill character'
+ * that prints as 'no fill' on all output devices (and
+ * stipple fonts).
+ * If polyfill=BOTH, just use the \D'p ...' command.
+ */
+ float firstx = p1->x;
+ float firsty = p1->y;
+
+ length = 0; /* keep track of line length so */
+ /* single lines don't get long */
+
+ if (polyfill == FILL || polyfill == BOTH) {
+ /* do the interior */
+ char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P';
+
+ /* include outline, if there is one and */
+ /* the -p flag was set */
+
+ /* switch based on what gremlin gives */
+ switch (element->size) {
+ case 1:
+ graylevel = 1;
+ break;
+ case 3:
+ graylevel = 2;
+ break;
+ case 12:
+ graylevel = 3;
+ break;
+ case 14:
+ graylevel = 4;
+ break;
+ case 16:
+ graylevel = 5;
+ break;
+ case 19:
+ graylevel = 6;
+ break;
+ case 21:
+ graylevel = 7;
+ break;
+ case 23:
+ graylevel = 8;
+ break;
+ default: /* who's giving something else? */
+ graylevel = NSTIPPLES;
+ break;
+ }
+ /* int graylevel = element->size; */
+
+ if (graylevel < 0)
+ break;
+ if (graylevel > NSTIPPLES)
+ graylevel = NSTIPPLES;
+ printf("\\h'-%du'\\D'f %du'",
+ stipple_index[graylevel],
+ stipple_index[graylevel]);
+ cr();
+ tmove(p1);
+ printf("\\D'%c", command);
+
+ while (!Nullpoint((PTNextPoint(p1)))) {
+ p1 = PTNextPoint(p1);
+ dx((double) p1->x);
+ dy((double) p1->y);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+
+ /* close polygon if not done so by user */
+ if ((firstx != p1->x) || (firsty != p1->y)) {
+ dx((double) firstx);
+ dy((double) firsty);
+ }
+ putchar('\'');
+ cr();
+ break;
+ }
+ /* else polyfill == OUTLINE; only draw the outline */
+ if (!(element->brushf))
+ break;
+ length = 0; /* keep track of line length */
+ tmove(p1);
+
+ while (!Nullpoint((PTNextPoint(p1)))) {
+ p1 = PTNextPoint(p1);
+ HGtline((int) (p1->x * troffscale),
+ (int) (p1->y * troffscale));
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+
+ /* close polygon if not done so by user */
+ if ((firstx != p1->x) || (firsty != p1->y)) {
+ HGtline((int) (firstx * troffscale),
+ (int) (firsty * troffscale));
+ }
+ cr();
+ break;
+ } /* end case POLYGON */
+ } /* end switch */
+ } /* end else Text */
+ } /* end if */
+} /* end PrintElt */
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: HGPutText (justification, position_point, string)
+ |
+ | Results: Given the justification, a point to position with, and a
+ | string to put, HGPutText first sends the string into a
+ | diversion, moves to the positioning point, then outputs
+ | local vertical and horizontal motions as needed to justify
+ | the text. After all motions are done, the diversion is
+ | printed out.
+ *----------------------------------------------------------------------------*/
+
+void
+HGPutText(int justify,
+ POINT pnt,
+ register char *string)
+{
+ int savelasty = lasty; /* vertical motion for text is to be */
+ /* ignored. Save current y here */
+
+ printf(".nr g8 \\n(.d\n"); /* save current vertical position. */
+ printf(".ds g9 \""); /* define string containing the text. */
+ while (*string) { /* put out the string */
+ if (*string == '\\' &&
+ *(string + 1) == '\\') { /* one character at a */
+ printf("\\\\\\"); /* time replacing // */
+ string++; /* by //// to prevent */
+ } /* interpretation at */
+ printf("%c", *(string++)); /* printout time */
+ }
+ printf("\n");
+
+ tmove(&pnt); /* move to positioning point */
+
+ switch (justify) {
+ /* local vertical motions */
+ /* (the numbers here are used to be somewhat compatible with gprint) */
+ case CENTLEFT:
+ case CENTCENT:
+ case CENTRIGHT:
+ printf("\\v'0.85n'"); /* down half */
+ break;
+
+ case TOPLEFT:
+ case TOPCENT:
+ case TOPRIGHT:
+ printf("\\v'1.7n'"); /* down whole */
+ }
+
+ switch (justify) {
+ /* local horizontal motions */
+ case BOTCENT:
+ case CENTCENT:
+ case TOPCENT:
+ printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */
+ break;
+
+ case BOTRIGHT:
+ case CENTRIGHT:
+ case TOPRIGHT:
+ printf("\\h'-\\w'\\*(g9'u'"); /* back whole */
+ }
+
+ printf("\\&\\*(g9\n"); /* now print the text. */
+ printf(".sp |\\n(g8u\n"); /* restore vertical position */
+ lasty = savelasty; /* vertical position restored to where it */
+ lastx = xleft; /* was before text, also horizontal is at */
+ /* left */
+} /* end HGPutText */
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: doarc (center_point, start_point, angle)
+ |
+ | Results: Produces either drawarc command or a drawcircle command
+ | depending on the angle needed to draw through.
+ *----------------------------------------------------------------------------*/
+
+void
+doarc(POINT cp,
+ POINT sp,
+ int angle)
+{
+ if (angle) /* arc with angle */
+ HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
+ (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle);
+ else /* a full circle (angle == 0) */
+ HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
+ (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0);
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: HGSetFont (font_number, Point_size)
+ |
+ | Results: ALWAYS outputs a .ft and .ps directive to troff. This is
+ | done because someone may change stuff inside a text string.
+ | Changes thickness back to default thickness. Default
+ | thickness depends on font and pointsize.
+ *----------------------------------------------------------------------------*/
+
+void
+HGSetFont(int font,
+ int size)
+{
+ printf(".ft %s\n"
+ ".ps %d\n", tfont[font - 1], tsize[size - 1]);
+ linethickness = DEFTHICK;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: HGSetBrush (line_mode)
+ |
+ | Results: Generates the troff commands to set up the line width and
+ | style of subsequent lines. Does nothing if no change is
+ | needed.
+ |
+ | Side Efct: Sets `linmode' and `linethicknes'.
+ *----------------------------------------------------------------------------*/
+
+void
+HGSetBrush(int mode)
+{
+ register int printed = 0;
+
+ if (linmod != style[--mode]) {
+ /* Groff doesn't understand \Ds, so we take it out */
+ /* printf ("\\D's %du'", linmod = style[mode]); */
+ linmod = style[mode];
+ printed = 1;
+ }
+ if (linethickness != thick[mode]) {
+ linethickness = thick[mode];
+ printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness);
+ printed = 1;
+ }
+ if (printed)
+ cr();
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: dx (x_destination)
+ |
+ | Results: Scales and outputs a number for delta x (with a leading
+ | space) given `lastx' and x_destination.
+ |
+ | Side Efct: Resets `lastx' to x_destination.
+ *----------------------------------------------------------------------------*/
+
+void
+dx(double x)
+{
+ register int ix = (int) (x * troffscale);
+
+ printf(" %du", ix - lastx);
+ lastx = ix;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: dy (y_destination)
+ |
+ | Results: Scales and outputs a number for delta y (with a leading
+ | space) given `lastyline' and y_destination.
+ |
+ | Side Efct: Resets `lastyline' to y_destination. Since `line' vertical
+ | motions don't affect `page' ones, `lasty' isn't updated.
+ *----------------------------------------------------------------------------*/
+
+void
+dy(double y)
+{
+ register int iy = (int) (y * troffscale);
+
+ printf(" %du", iy - lastyline);
+ lastyline = iy;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: tmove2 (px, py)
+ |
+ | Results: Produces horizontal and vertical moves for troff given the
+ | pair of points to move to and knowing the current position.
+ | Also puts out a horizontal move to start the line. This is
+ | a variation without the .sp command.
+ *----------------------------------------------------------------------------*/
+
+void
+tmove2(int px,
+ int py)
+{
+ register int dx;
+ register int dy;
+
+ if ((dy = py - lasty)) {
+ printf("\\v'%du'", dy);
+ }
+ lastyline = lasty = py; /* lasty is always set to current */
+ if ((dx = px - lastx)) {
+ printf("\\h'%du'", dx);
+ lastx = px;
+ }
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: tmove (point_pointer)
+ |
+ | Results: Produces horizontal and vertical moves for troff given the
+ | pointer of a point to move to and knowing the current
+ | position. Also puts out a horizontal move to start the
+ | line.
+ *----------------------------------------------------------------------------*/
+
+void
+tmove(POINT * ptr)
+{
+ register int ix = (int) (ptr->x * troffscale);
+ register int iy = (int) (ptr->y * troffscale);
+ register int dx;
+ register int dy;
+
+ if ((dy = iy - lasty)) {
+ printf(".sp %du\n", dy);
+ }
+ lastyline = lasty = iy; /* lasty is always set to current */
+ if ((dx = ix - lastx)) {
+ printf("\\h'%du'", dx);
+ lastx = ix;
+ }
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: cr ( )
+ |
+ | Results: Ends off an input line. `.sp -1' is also added to counteract
+ | the vertical move done at the end of text lines.
+ |
+ | Side Efct: Sets `lastx' to `xleft' for troff's return to left margin.
+ *----------------------------------------------------------------------------*/
+
+void
+cr()
+{
+ printf("\n.sp -1\n");
+ lastx = xleft;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: line ( )
+ |
+ | Results: Draws a single solid line to (x,y).
+ *----------------------------------------------------------------------------*/
+
+void
+line(int px,
+ int py)
+{
+ printf("\\D'l");
+ printf(" %du", px - lastx);
+ printf(" %du'", py - lastyline);
+ lastx = px;
+ lastyline = lasty = py;
+}
+
+
+/*----------------------------------------------------------------------------
+ | Routine: drawwig (ptr)
+ |
+ | Results: The point sequence found in the structure pointed by ptr is
+ | placed in integer arrays for further manipulation by the
+ | existing routing. With the proper parameters, HGCurve is
+ | called.
+ *----------------------------------------------------------------------------*/
+
+void
+drawwig(POINT * ptr)
+{
+ register int npts; /* point list index */
+ int x[MAXPOINTS], y[MAXPOINTS]; /* point list */
+
+ for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) {
+ x[npts] = (int) (ptr->x * troffscale);
+ y[npts] = (int) (ptr->y * troffscale);
+ }
+ if (--npts) {
+ /* HGCurve(&x[0], &y[0], npts); */ /*Gremlin looks different, so... */
+ picurve(&x[0], &y[0], npts);
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle)
+ |
+ | Results: This routine plots an arc centered about (cx, cy) counter
+ | clockwise starting from the point (px, py) through `angle'
+ | degrees. If angle is 0, a full circle is drawn. It does so
+ | by creating a draw-path around the arc whose density of
+ | points depends on the size of the arc.
+ *----------------------------------------------------------------------------*/
+
+void
+HGArc(register int cx,
+ register int cy,
+ int px,
+ int py,
+ int angle)
+{
+ double xs, ys, resolution, fullcircle;
+ int m;
+ register int mask;
+ register int extent;
+ register int nx;
+ register int ny;
+ register int length;
+ register double epsilon;
+
+ xs = px - cx;
+ ys = py - cy;
+
+ length = 0;
+
+ resolution = (1.0 + hypot(xs, ys) / res) * PointsPerInterval;
+ /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */
+ (void) frexp(resolution, &m); /* A bit more elegant than log10 */
+ for (mask = 1; mask < m; mask = mask << 1);
+ mask -= 1;
+
+ epsilon = 1.0 / resolution;
+ fullcircle = (2.0 * pi) * resolution;
+ if (angle == 0)
+ extent = (int) fullcircle;
+ else
+ extent = (int) (angle * fullcircle / 360.0);
+
+ HGtline(px, py);
+ while (--extent >= 0) {
+ xs += epsilon * ys;
+ nx = cx + (int) (xs + 0.5);
+ ys -= epsilon * xs;
+ ny = cy + (int) (ys + 0.5);
+ if (!(extent & mask)) {
+ HGtline(nx, ny); /* put out a point on circle */
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ }
+ } /* end for */
+} /* end HGArc */
+
+
+/*----------------------------------------------------------------------------
+ | Routine: picurve (xpoints, ypoints, num_of_points)
+ |
+ | Results: Draws a curve delimited by (not through) the line segments
+ | traced by (xpoints, ypoints) point list. This is the `Pic'
+ | style curve.
+ *----------------------------------------------------------------------------*/
+
+void
+picurve(register int *x,
+ register int *y,
+ int npts)
+{
+ register int nseg; /* effective resolution for each curve */
+ register int xp; /* current point (and temporary) */
+ register int yp;
+ int pxp, pyp; /* previous point (to make lines from) */
+ int i; /* inner curve segment traverser */
+ int length = 0;
+ double w; /* position factor */
+ double t1, t2, t3; /* calculation temps */
+
+ if (x[1] == x[npts] && y[1] == y[npts]) {
+ x[0] = x[npts - 1]; /* if the lines' ends meet, make */
+ y[0] = y[npts - 1]; /* sure the curve meets */
+ x[npts + 1] = x[2];
+ y[npts + 1] = y[2];
+ } else { /* otherwise, make the ends of the */
+ x[0] = x[1]; /* curve touch the ending points of */
+ y[0] = y[1]; /* the line segments */
+ x[npts + 1] = x[npts];
+ y[npts + 1] = y[npts];
+ }
+
+ pxp = (x[0] + x[1]) / 2; /* make the last point pointers */
+ pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */
+ tmove2(pxp, pyp);
+
+ for (; npts--; x++, y++) { /* traverse the line segments */
+ xp = x[0] - x[1];
+ yp = y[0] - y[1];
+ nseg = (int) hypot((double) xp, (double) yp);
+ xp = x[1] - x[2];
+ yp = y[1] - y[2];
+ /* `nseg' is the number of line */
+ /* segments that will be drawn for */
+ /* each curve segment. */
+ nseg = (int) ((double) (nseg + (int) hypot((double) xp, (double) yp)) /
+ res * PointsPerInterval);
+
+ for (i = 1; i < nseg; i++) {
+ w = (double) i / (double) nseg;
+ t1 = w * w;
+ t3 = t1 + 1.0 - (w + w);
+ t2 = 2.0 - (t3 + t1);
+ xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
+ yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
+
+ HGtline(xp, yp);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ | Routine: HGCurve(xpoints, ypoints, num_points)
+ |
+ | Results: This routine generates a smooth curve through a set of
+ | points. The method used is the parametric spline curve on
+ | unit knot mesh described in `Spline Curve Techniques' by
+ | Patrick Baudelaire, Robert Flegal, and Robert Sproull --
+ | Xerox Parc.
+ *----------------------------------------------------------------------------*/
+
+void
+HGCurve(int *x,
+ int *y,
+ int numpoints)
+{
+ float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
+ float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
+ float t, t2, t3;
+ register int j;
+ register int k;
+ register int nx;
+ register int ny;
+ int lx, ly;
+ int length = 0;
+
+ lx = x[1];
+ ly = y[1];
+ tmove2(lx, ly);
+
+ /*
+ * Solve for derivatives of the curve at each point separately for x and y
+ * (parametric).
+ */
+ Paramaterize(x, y, h, numpoints);
+
+ /* closed curve */
+ if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
+ PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
+ PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
+ } else {
+ NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
+ NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
+ }
+
+ /*
+ * generate the curve using the above information and PointsPerInterval
+ * vectors between each specified knot.
+ */
+
+ for (j = 1; j < numpoints; ++j) {
+ if ((x[j] == x[j + 1]) && (y[j] == y[j + 1]))
+ continue;
+ for (k = 0; k <= PointsPerInterval; ++k) {
+ t = (float) k *h[j] / (float) PointsPerInterval;
+ t2 = t * t;
+ t3 = t * t * t;
+ nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6);
+ ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6);
+ HGtline(nx, ny);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end for k */
+ } /* end for j */
+} /* end HGCurve */
+
+
+/*----------------------------------------------------------------------------
+ | Routine: Paramaterize (xpoints, ypoints, hparams, num_points)
+ |
+ | Results: This routine calculates parameteric values for use in
+ | calculating curves. The parametric values are returned
+ | in the array h. The values are an approximation of
+ | cumulative arc lengths of the curve (uses cord length).
+ | For additional information, see paper cited below.
+ *----------------------------------------------------------------------------*/
+
+void
+Paramaterize(int x[],
+ int y[],
+ float h[],
+ int n)
+{
+ register int dx;
+ register int dy;
+ register int i;
+ register int j;
+ float u[MAXPOINTS];
+
+ for (i = 1; i <= n; ++i) {
+ u[i] = 0;
+ for (j = 1; j < i; j++) {
+ dx = x[j + 1] - x[j];
+ dy = y[j + 1] - y[j];
+ /* Here was overflowing, so I changed it. */
+ /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */
+ u[i] += hypot((double) dx, (double) dy);
+ }
+ }
+ for (i = 1; i < n; ++i)
+ h[i] = u[i + 1] - u[i];
+} /* end Paramaterize */
+
+
+/*----------------------------------------------------------------------------
+ | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints)
+ |
+ | Results: This routine solves for the cubic polynomial to fit a spline
+ | curve to the the points specified by the list of values.
+ | The Curve generated is periodic. The algorithms for this
+ | curve are from the `Spline Curve Techniques' paper cited
+ | above.
+ *----------------------------------------------------------------------------*/
+
+void
+PeriodicSpline(float h[], /* paramaterization */
+ int z[], /* point list */
+ float dz[], /* to return the 1st derivative */
+ float d2z[], /* 2nd derivative */
+ float d3z[], /* 3rd derivative */
+ int npoints) /* number of valid points */
+{
+ float d[MAXPOINTS];
+ float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
+ float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
+ int i;
+
+ /* step 1 */
+ for (i = 1; i < npoints; ++i) {
+ deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
+ }
+ h[0] = h[npoints - 1];
+ deltaz[0] = deltaz[npoints - 1];
+
+ /* step 2 */
+ for (i = 1; i < npoints - 1; ++i) {
+ d[i] = deltaz[i + 1] - deltaz[i];
+ }
+ d[0] = deltaz[1] - deltaz[0];
+
+ /* step 3a */
+ a[1] = 2 * (h[0] + h[1]);
+ b[1] = d[0];
+ c[1] = h[0];
+ for (i = 2; i < npoints - 1; ++i) {
+ a[i] = 2 * (h[i - 1] + h[i]) -
+ pow((double) h[i - 1], (double) 2.0) / a[i - 1];
+ b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1];
+ c[i] = -h[i - 1] * c[i - 1] / a[i - 1];
+ }
+
+ /* step 3b */
+ r[npoints - 1] = 1;
+ s[npoints - 1] = 0;
+ for (i = npoints - 2; i > 0; --i) {
+ r[i] = -(h[i] * r[i + 1] + c[i]) / a[i];
+ s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
+ }
+
+ /* step 4 */
+ d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1]
+ - h[npoints - 1] * s[npoints - 2])
+ / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2]
+ + 2 * (h[npoints - 2] + h[0]));
+ for (i = 1; i < npoints - 1; ++i) {
+ d2z[i] = r[i] * d2z[npoints - 1] + s[i];
+ }
+ d2z[npoints] = d2z[1];
+
+ /* step 5 */
+ for (i = 1; i < npoints; ++i) {
+ dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
+ d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
+ }
+} /* end PeriodicSpline */
+
+
+/*----------------------------------------------------------------------------
+ | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
+ |
+ | Results: This routine solves for the cubic polynomial to fit a spline
+ | curve the the points specified by the list of values. The
+ | alogrithms for this curve are from the `Spline Curve
+ | Techniques' paper cited above.
+ *----------------------------------------------------------------------------*/
+
+void
+NaturalEndSpline(float h[], /* parameterization */
+ int z[], /* Point list */
+ float dz[], /* to return the 1st derivative */
+ float d2z[], /* 2nd derivative */
+ float d3z[], /* 3rd derivative */
+ int npoints) /* number of valid points */
+{
+ float d[MAXPOINTS];
+ float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
+ int i;
+
+ /* step 1 */
+ for (i = 1; i < npoints; ++i) {
+ deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
+ }
+ deltaz[0] = deltaz[npoints - 1];
+
+ /* step 2 */
+ for (i = 1; i < npoints - 1; ++i) {
+ d[i] = deltaz[i + 1] - deltaz[i];
+ }
+ d[0] = deltaz[1] - deltaz[0];
+
+ /* step 3 */
+ a[0] = 2 * (h[2] + h[1]);
+ b[0] = d[1];
+ for (i = 1; i < npoints - 2; ++i) {
+ a[i] = 2 * (h[i + 1] + h[i + 2]) -
+ pow((double) h[i + 1], (double) 2.0) / a[i - 1];
+ b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1];
+ }
+
+ /* step 4 */
+ d2z[npoints] = d2z[1] = 0;
+ for (i = npoints - 1; i > 1; --i) {
+ d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
+ }
+
+ /* step 5 */
+ for (i = 1; i < npoints; ++i) {
+ dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
+ d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
+ }
+} /* end NaturalEndSpline */
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: change (x_position, y_position, visible_flag)
+ |
+ | Results: As HGtline passes from the invisible to visible (or vice
+ | versa) portion of a line, change is called to either draw
+ | the line, or initialize the beginning of the next one.
+ | Change calls line to draw segments if visible_flag is set
+ | (which means we're leaving a visible area).
+ *----------------------------------------------------------------------------*/
+
+void
+change(register int x,
+ register int y,
+ register int vis)
+{
+ static int length = 0;
+
+ if (vis) { /* leaving a visible area, draw it. */
+ line(x, y);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } else { /* otherwise, we're entering one, remember */
+ /* beginning */
+ tmove2(x, y);
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ | Routine: HGtline (xstart, ystart, xend, yend)
+ |
+ | Results: Draws a line from current position to (x1,y1) using line(x1,
+ | y1) to place individual segments of dotted or dashed lines.
+ *----------------------------------------------------------------------------*/
+
+void
+HGtline(int x1,
+ int y1)
+{
+ register int x0 = lastx;
+ register int y0 = lasty;
+ register int dx;
+ register int dy;
+ register int oldcoord;
+ register int res1;
+ register int visible;
+ register int res2;
+ register int xinc;
+ register int yinc;
+ register int dotcounter;
+
+ if (linmod == SOLID) {
+ line(x1, y1);
+ return;
+ }
+
+ /* for handling different resolutions */
+ dotcounter = linmod << dotshifter;
+
+ xinc = 1;
+ yinc = 1;
+ if ((dx = x1 - x0) < 0) {
+ xinc = -xinc;
+ dx = -dx;
+ }
+ if ((dy = y1 - y0) < 0) {
+ yinc = -yinc;
+ dy = -dy;
+ }
+ res1 = 0;
+ res2 = 0;
+ visible = 0;
+ if (dx >= dy) {
+ oldcoord = y0;
+ while (x0 != x1) {
+ if ((x0 & dotcounter) && !visible) {
+ change(x0, y0, 0);
+ visible = 1;
+ } else if (visible && !(x0 & dotcounter)) {
+ change(x0 - xinc, oldcoord, 1);
+ visible = 0;
+ }
+ if (res1 > res2) {
+ oldcoord = y0;
+ res2 += dx - res1;
+ res1 = 0;
+ y0 += yinc;
+ }
+ res1 += dy;
+ x0 += xinc;
+ }
+ } else {
+ oldcoord = x0;
+ while (y0 != y1) {
+ if ((y0 & dotcounter) && !visible) {
+ change(x0, y0, 0);
+ visible = 1;
+ } else if (visible && !(y0 & dotcounter)) {
+ change(oldcoord, y0 - yinc, 1);
+ visible = 0;
+ }
+ if (res1 > res2) {
+ oldcoord = x0;
+ res2 += dy - res1;
+ res1 = 0;
+ x0 += xinc;
+ }
+ res1 += dx;
+ y0 += yinc;
+ }
+ }
+ if (visible)
+ change(x1, y1, 1);
+ else
+ change(x1, y1, 0);
+}
+
+/* EOF */
diff --git a/contrib/groff/src/preproc/grn/hpoint.cc b/contrib/groff/src/preproc/grn/hpoint.cc
new file mode 100644
index 000000000000..f4e1ca827755
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/hpoint.cc
@@ -0,0 +1,49 @@
+/* Last non-groff version: hpoint.c 1.1 84/10/08 */
+
+/*
+ * This file contains routines for manipulating the point data structures
+ * for the gremlin picture editor.
+ */
+
+#include <stdlib.h>
+#include "gprint.h"
+
+
+/*
+ * Return pointer to empty point list.
+ */
+POINT *
+PTInit()
+{
+ return ((POINT *) NULL);
+}
+
+
+/*
+ * This routine creates a new point with coordinates x and y and links it
+ * into the pointlist.
+ */
+POINT *
+PTMakePoint(float x,
+ float y,
+ POINT **pplist)
+{
+ register POINT *point;
+
+ if (Nullpoint(point = *pplist)) { /* empty list */
+ *pplist = (POINT *) malloc(sizeof(POINT));
+ point = *pplist;
+ } else {
+ while (!Nullpoint(point->nextpt))
+ point = point->nextpt;
+ point->nextpt = (POINT *) malloc(sizeof(POINT));
+ point = point->nextpt;
+ }
+
+ point->x = x;
+ point->y = y;
+ point->nextpt = PTInit();
+ return (point);
+} /* end PTMakePoint */
+
+/* EOF */
diff --git a/contrib/groff/src/preproc/grn/main.cc b/contrib/groff/src/preproc/grn/main.cc
new file mode 100644
index 000000000000..92e64c6ae77b
--- /dev/null
+++ b/contrib/groff/src/preproc/grn/main.cc
@@ -0,0 +1,905 @@
+/* Last non-groff version: main.cc 1.23 (Berkeley) 85/08/05
+ *
+ * Adapted to GNU troff by Daniel Senderowicz 99/12/29.
+ *
+ * Further refinements by Werner Lemberg 00/02/20.
+ *
+ *
+ * This file contains the main and file system dependent routines for
+ * processing gremlin files into troff input. The program watches input go
+ * by to standard output, only interpreting things between .GS and .GE
+ * lines. Default values (font, size, scale, thickness) may be overridden
+ * with a `default' command and are further overridden by commands in the
+ * input.
+ *
+ * Inside the GS and GE, commands are accepted to reconfigure the picture.
+ * At most one command may reside on each line, and each command is followed
+ * by a parameter separated by white space. The commands are as follows,
+ * and may be abbreviated down to one character (with exception of `scale'
+ * and `stipple' down to "sc" and "st") and may be upper or lower case.
+ *
+ * default - Make all settings in the current
+ * .GS/.GE the global defaults. Height,
+ * width and file are NOT saved.
+ * 1, 2, 3, 4 - Set size 1, 2, 3, or 4 (followed by an
+ * integer point size).
+ * roman, italics, bold, special - Set gremlin's fonts to any other troff
+ * font (one or two characters).
+ * stipple, l - Use a stipple font for polygons. Arg
+ * is troff font name. No Default. Can
+ * use only one stipple font per picture.
+ * (See below for stipple font index.)
+ * scale, x - Scale is IN ADDITION to the global
+ * scale factor from the default.
+ * pointscale - Turn on scaling point sizes to match
+ * `scale' commands. (Optional operand
+ * `off' to turn it off.)
+ * narrow, medium, thick - Set widths of lines.
+ * file - Set the file name to read the gremlin
+ * picture from. If the file isn't in
+ * the current directory, the gremlin
+ * library is tried.
+ * width, height - These two commands override any
+ * scaling factor that is in effect, and
+ * forces the picture to fit into either
+ * the height or width specified,
+ * whichever makes the picture smaller.
+ * The operand for these two commands is
+ * a floating-point number in units of
+ * inches.
+ * l<nn> (integer <nn>) - Set association between stipple <nn>
+ * and a stipple `character'. <nn> must
+ * be in the range 0 to NSTIPPLES (16)
+ * inclusive. The integer operand is an
+ * index in the stipple font selected.
+ * Valid cf (cifplot) indices are 1-32
+ * (although 24 is not defined), valid ug
+ * (unigrafix) indices are 1-14, and
+ * valid gs (gray scale) indices are
+ * 0-16. Nonetheless, any number between
+ * 0 and 255 is accepted since new
+ * stipple fonts may be added. An
+ * integer operand is required.
+ *
+ * Troff number registers used: g1 through g9. g1 is the width of the
+ * picture, and g2 is the height. g3, and g4, save information, g8 and g9
+ * are used for text processing and g5-g7 are reserved.
+ */
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gprint.h"
+
+#include "device.h"
+#include "font.h"
+#include "searchpath.h"
+#include "macropath.h"
+
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "defs.h"
+
+/* database imports */
+
+extern void HGPrintElt(ELT *element, int baseline);
+extern ELT *DBInit();
+extern ELT *DBRead(register FILE *file);
+extern POINT *PTInit();
+extern POINT *PTMakePoint(float x, float y, POINT **pplist);
+
+
+#define SUN_SCALEFACTOR 0.70
+
+/* #define DEFSTIPPLE "gs" */
+#define DEFSTIPPLE "cf"
+
+#define MAXINLINE 100 /* input line length */
+
+#define SCREENtoINCH 0.02 /* scaling factor, screen to inches */
+
+#define BIG 999999999999.0 /* unweildly large floating number */
+
+
+static char sccsid[] = "@(#) (Berkeley) 8/5/85, 12/28/99";
+
+int res; /* the printer's resolution goes here */
+
+int dotshifter; /* for the length of dotted curves */
+
+double linethickness; /* brush styles */
+int linmod;
+int lastx; /* point registers for printing elements */
+int lasty;
+int lastyline; /* A line's vertical position is NOT the */
+ /* same after that line is over, so for a */
+ /* line of drawing commands, vertical */
+ /* spacing is kept in lastyline */
+
+/* These are the default fonts, sizes, line styles, */
+/* and thicknesses. They can be modified from a */
+/* `default' command and are reset each time the */
+/* start of a picture (.GS) is found. */
+
+char *deffont[] =
+{"R", "I", "B", "S"};
+int defsize[] =
+{10, 16, 24, 36};
+/* #define BASE_THICKNESS 1.0 */
+#define BASE_THICKNESS 0.15
+double defthick[STYLES] =
+{1 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 5 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 3 * BASE_THICKNESS};
+
+/* int cf_stipple_index[NSTIPPLES + 1] = */
+/* {0, 1, 3, 12, 14, 16, 19, 21, 23}; */
+/* a logarithmic scale looks better than a linear one for the gray shades */
+/* */
+/* int other_stipple_index[NSTIPPLES + 1] = */
+/* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; */
+
+int cf_stipple_index[NSTIPPLES + 1] =
+{0, 18, 32, 56, 100, 178, 316, 562, 1000}; /* only 1-8 used */
+int other_stipple_index[NSTIPPLES + 1] =
+{0, 62, 125, 187, 250, 312, 375, 437, 500,
+ 562, 625, 687, 750, 812, 875, 937, 1000};
+
+/* int *defstipple_index = other_stipple_index; */
+int *defstipple_index = cf_stipple_index;
+
+int style[STYLES] =
+{DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID};
+double scale = 1.0; /* no scaling, default */
+int defpoint = 0; /* flag for pointsize scaling */
+char *defstipple = (char *) 0;
+enum {
+ OUTLINE, FILL, BOTH
+} polyfill;
+
+/* flag to controll filling of polygons */
+
+double adj1 = 0.0;
+double adj2 = 0.0;
+double adj3 = 0.0;
+double adj4 = 0.0;
+
+double thick[STYLES]; /* thicknesses set by defaults, then by */
+ /* commands */
+char *tfont[FONTS]; /* fonts originally set to deffont values, */
+ /* then */
+int tsize[SIZES]; /* optionally changed by commands inside */
+ /* grn */
+int stipple_index[NSTIPPLES + 1]; /* stipple font file indices */
+char *stipple;
+
+double xscale; /* scaling factor from individual pictures */
+double troffscale; /* scaling factor at output time */
+
+double width; /* user-request maximum width for picture */
+ /* (in inches) */
+double height; /* user-request height */
+int pointscale; /* flag for pointsize scaling */
+int setdefault; /* flag for a .GS/.GE to remember all */
+ /* settings */
+int sflag; /* -s flag: sort order (do polyfill first) */
+
+double toppoint; /* remember the picture */
+double bottompoint; /* bounds in these variables */
+double leftpoint;
+double rightpoint;
+
+int ytop; /* these are integer versions of the above */
+int ybottom; /* so not to convert each time they're used */
+int xleft;
+int xright;
+
+int linenum = 0; /* line number of input file */
+char inputline[MAXINLINE]; /* spot to filter through the file */
+char *c1 = inputline; /* c1, c2, and c3 will be used to */
+char *c2 = inputline + 1; /* hunt for lines that begin with */
+char *c3 = inputline + 2; /* ".GS" by looking individually */
+char *c4 = inputline + 3; /* needed for compatibility mode */
+char GScommand[MAXINLINE]; /* put user's ".GS" command line here */
+char gremlinfile[MAXINLINE]; /* filename to use for a picture */
+int SUNFILE = FALSE; /* TRUE if SUN gremlin file */
+int compatibility_flag = FALSE; /* TRUE if in compatibility mode */
+
+
+void getres();
+char *doinput(FILE *fp);
+void conv(register FILE *fp, int baseline);
+void savestate();
+int has_polygon(register ELT *elist);
+void interpret(char *line);
+
+
+void
+usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [ -vCs ] [ -M dir ] [ -F dir ] [ -T dev ] [ file ]\n",
+ program_name);
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: main (argument_count, argument_pointer)
+ |
+ | Results: Parses the command line, accumulating input file names, then
+ | reads the inputs, passing it directly to output until a `.GS'
+ | line is read. Main then passes control to `conv' to do the
+ | gremlin file conversions.
+ *----------------------------------------------------------------------------*/
+
+int
+main(int argc,
+ char **argv)
+{
+ program_name = argv[0];
+ register FILE *fp;
+ register int k;
+ register char c;
+ register int gfil = 0;
+ char *file[50];
+ char *operand(int *argcp, char ***argvp);
+
+ while (--argc) {
+ if (**++argv != '-')
+ file[gfil++] = *argv;
+ else
+ switch (c = (*argv)[1]) {
+
+ case 0:
+ file[gfil++] = NULL;
+ break;
+
+ case 'C': /* compatibility mode */
+ compatibility_flag = TRUE;
+ break;
+
+ case 'F': /* font path to find DESC */
+ font::command_line_font_dir(operand(&argc, &argv));
+ break;
+
+ case 'T': /* final output typesetter name */
+ device = operand(&argc, &argv);
+ break;
+
+ case 'M': /* set library directory */
+ macro_path.command_line_dir(operand(&argc, &argv));
+ break;
+
+ case 's': /* preserve order of elements */
+ sflag = 1;
+ break;
+
+ case '-':
+ if (strcmp(*argv,"--version")==0) {
+ case 'v':
+ extern const char *Version_string;
+ printf("GNU grn (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ if (strcmp(*argv,"--help")==0) {
+ case '?':
+ usage(stdout);
+ exit(0);
+ break;
+ }
+ // fallthrough
+ default:
+ error("unknown switch: %1", c);
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ getres(); /* set the resolution for an output device */
+
+ if (gfil == 0) { /* no filename, use standard input */
+ file[0] = NULL;
+ gfil++;
+ }
+
+ for (k = 0; k < gfil; k++) {
+ if (file[k] != NULL) {
+ if ((fp = fopen(file[k], "r")) == NULL)
+ fatal("can't open %1", file[k]);
+ } else
+ fp = stdin;
+
+ while (doinput(fp) != NULL) {
+ if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
+ if (compatibility_flag ||
+ *c4 == '\n' || *c4 == ' ' || *c4 == '\0')
+ conv(fp, linenum);
+ else
+ fputs(inputline, stdout);
+ } else
+ fputs(inputline, stdout);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: char * operand (& argc, & argv)
+ |
+ | Results: Returns address of the operand given with a command-line
+ | option. It uses either `-Xoperand' or `-X operand', whichever
+ | is present. The program is terminated if no option is
+ | present.
+ |
+ | Side Efct: argc and argv are updated as necessary.
+ *----------------------------------------------------------------------------*/
+
+char *
+operand(int *argcp,
+ char ***argvp)
+{
+ if ((**argvp)[2])
+ return (**argvp + 2); /* operand immediately follows */
+ if ((--*argcp) <= 0) { /* no operand */
+ error("command-line option operand missing.");
+ exit(8);
+ }
+ return (*(++(*argvp))); /* operand is next word */
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: getres ()
+ |
+ | Results: Sets `res' to the resolution of the output device.
+ *----------------------------------------------------------------------------*/
+
+void
+getres()
+{
+ int linepiece;
+
+ if (!font::load_desc())
+ fatal("sorry, I can't continue");
+
+ res = font::res;
+
+ /* Correct the brush thicknesses based on res */
+ /* if (res >= 256) {
+ defthick[0] = res >> 8;
+ defthick[1] = res >> 8;
+ defthick[2] = res >> 4;
+ defthick[3] = res >> 8;
+ defthick[4] = res >> 8;
+ defthick[5] = res >> 6;
+ } */
+
+ linepiece = res >> 9;
+ for (dotshifter = 0; linepiece; dotshifter++)
+ linepiece = linepiece >> 1;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: char * doinput (file_pointer)
+ |
+ | Results: A line of input is read into `inputline'.
+ |
+ | Side Efct: "linenum" is incremented.
+ |
+ | Bugs: Lines longer than MAXINLINE are NOT checked, except for
+ | updating `linenum'.
+ *----------------------------------------------------------------------------*/
+
+char *
+doinput(FILE *fp)
+{
+ char *k;
+
+ if ((k = fgets(inputline, MAXINLINE, fp)) == NULL)
+ return k;
+ if (strchr(inputline, '\n')) /* ++ only if it's a complete line */
+ linenum++;
+ return (char *) !NULL;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: initpic ( )
+ |
+ | Results: Sets all parameters to the normal defaults, possibly
+ | overridden by a setdefault command. Initialize the picture
+ | variables, and output the startup commands to troff to begin
+ | the picture.
+ *----------------------------------------------------------------------------*/
+
+void
+initpic()
+{
+ register int i;
+
+ for (i = 0; i < STYLES; i++) { /* line thickness defaults */
+ thick[i] = defthick[i];
+ }
+ for (i = 0; i < FONTS; i++) { /* font name defaults */
+ tfont[i] = deffont[i];
+ }
+ for (i = 0; i < SIZES; i++) { /* font size defaults */
+ tsize[i] = defsize[i];
+ }
+ for (i = 0; i <= NSTIPPLES; i++) { /* stipple font file default indices */
+ stipple_index[i] = defstipple_index[i];
+ }
+ stipple = defstipple;
+
+ gremlinfile[0] = 0; /* filename is `null' */
+ setdefault = 0; /* this is not the default settings (yet) */
+
+ toppoint = BIG; /* set the picture bounds out */
+ bottompoint = -BIG; /* of range so they'll be set */
+ leftpoint = BIG; /* by `savebounds' on input */
+ rightpoint = -BIG;
+
+ pointscale = defpoint; /* flag for scaling point sizes default */
+ xscale = scale; /* default scale of individual pictures */
+ width = 0.0; /* size specifications input by user */
+ height = 0.0;
+
+ linethickness = DEFTHICK; /* brush styles */
+ linmod = DEFSTYLE;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: conv (file_pointer, starting_line)
+ |
+ | Results: At this point, we just passed a `.GS' line in the input
+ | file. conv reads the input and calls `interpret' to process
+ | commands, gathering up information until a `.GE' line is
+ | found. It then calls `HGPrint' to do the translation of the
+ | gremlin file to troff commands.
+ *----------------------------------------------------------------------------*/
+
+void
+conv(register FILE *fp,
+ int baseline)
+{
+ register FILE *gfp = NULL; /* input file pointer */
+ register int done = 0; /* flag to remember if finished */
+ register ELT *e; /* current element pointer */
+ ELT *PICTURE; /* whole picture data base pointer */
+ double temp; /* temporary calculating area */
+ /* POINT ptr; */ /* coordinates of a point to pass to `mov' */
+ /* routine */
+ int flyback; /* flag `want to end up at the top of the */
+ /* picture?' */
+ int compat; /* test character after .GE or .GF */
+
+
+ initpic(); /* set defaults, ranges, etc. */
+ strcpy(GScommand, inputline); /* save `.GS' line for later */
+
+ do {
+ done = (doinput(fp) == NULL); /* test for EOF */
+ flyback = (*c3 == 'F'); /* and .GE or .GF */
+ compat = (compatibility_flag ||
+ *c4 == '\n' || *c4 == ' ' || *c4 == '\0');
+ done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback) &&
+ compat);
+
+ if (done) {
+ if (setdefault)
+ savestate();
+
+ if (!gremlinfile[0]) {
+ if (!setdefault)
+ error("at line %1: no picture filename.\n", baseline);
+ return;
+ }
+ char *path;
+ gfp = macro_path.open_file(gremlinfile, &path);
+ if (!gfp)
+ return;
+ PICTURE = DBRead(gfp); /* read picture file */
+ fclose(gfp);
+ a_delete path;
+ if (DBNullelt(PICTURE))
+ return; /* If a request is made to make the */
+ /* picture fit into a specific area, */
+ /* set the scale to do that. */
+
+ if (stipple == (char *) NULL) /* if user forgot stipple */
+ if (has_polygon(PICTURE)) /* and picture has a polygon */
+ stipple = DEFSTIPPLE; /* then set the default */
+
+ if ((temp = bottompoint - toppoint) < 0.1)
+ temp = 0.1;
+ temp = (height != 0.0) ? height / (temp * SCREENtoINCH) : BIG;
+ if ((troffscale = rightpoint - leftpoint) < 0.1)
+ troffscale = 0.1;
+ troffscale = (width != 0.0) ?
+ width / (troffscale * SCREENtoINCH) : BIG;
+ if (temp == BIG && troffscale == BIG)
+ troffscale = xscale;
+ else {
+ if (temp < troffscale)
+ troffscale = temp;
+ } /* here, troffscale is the */
+ /* picture's scaling factor */
+ if (pointscale) {
+ register int i; /* do pointscaling here, when */
+ /* scale is known, before output */
+ for (i = 0; i < SIZES; i++)
+ tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5);
+ }
+
+ /* change to device units */
+ troffscale *= SCREENtoINCH * res; /* from screen units */
+
+ ytop = (int) (toppoint * troffscale); /* calculate integer */
+ ybottom = (int) (bottompoint * troffscale); /* versions of the */
+ xleft = (int) (leftpoint * troffscale); /* picture limits */
+ xright = (int) (rightpoint * troffscale);
+
+ /* save stuff in number registers, */
+ /* register g1 = picture width and */
+ /* register g2 = picture height, */
+ /* set vertical spacing, no fill, */
+ /* and break (to make sure picture */
+ /* starts on left), and put out the */
+ /* user's `.GS' line. */
+ printf(".br\n"
+ ".nr g1 %du\n"
+ ".nr g2 %du\n"
+ "%s"
+ ".nr g3 \\n(.f\n"
+ ".nr g4 \\n(.s\n"
+ "\\0\n"
+ ".sp -1\n",
+ xright - xleft, ybottom - ytop, GScommand);
+
+ if (stipple) /* stipple requested for this picture */
+ printf(".st %s\n", stipple);
+ lastx = xleft; /* note where we are (upper left */
+ lastyline = lasty = ytop; /* corner of the picture) */
+
+ /* Just dump everything in the order it appears.
+ *
+ * If -s command-line option, traverse picture twice: First time,
+ * print only the interiors of filled polygons (as borderless
+ * polygons). Second time, print the outline as series of line
+ * segments. This way, postprocessors that overwrite rather than
+ * merge picture elements (such as Postscript) can still have text and
+ * graphics on a shaded background.
+ */
+ /* if (sflag) */
+ if (!sflag) { /* changing the default for filled polygons */
+ e = PICTURE;
+ polyfill = FILL;
+ while (!DBNullelt(e)) {
+ printf(".mk\n");
+ if (e->type == POLYGON)
+ HGPrintElt(e, baseline);
+ printf(".rt\n");
+ lastx = xleft;
+ lastyline = lasty = ytop;
+ e = DBNextElt(e);
+ }
+ }
+ e = PICTURE;
+
+ /* polyfill = !sflag ? BOTH : OUTLINE; */
+ polyfill = sflag ? BOTH : OUTLINE; /* changing the default */
+ while (!DBNullelt(e)) {
+ printf(".mk\n");
+ HGPrintElt(e, baseline);
+ printf(".rt\n");
+ lastx = xleft;
+ lastyline = lasty = ytop;
+ e = DBNextElt(e);
+ }
+
+ /* decide where to end picture */
+
+ /* I changed everything here. I always use the combination .mk and */
+ /* .rt so once finished I just space down the heigth of the picture */
+ /* that is \n(g2u */
+ if (flyback) { /* end picture at upper left */
+ /* ptr.x = leftpoint;
+ ptr.y = toppoint; */
+ } else { /* end picture at lower left */
+ /* ptr.x = leftpoint;
+ ptr.y = bottompoint; */
+ printf(".sp \\n(g2u\n");
+ }
+
+ /* tmove(&ptr); */ /* restore default line parameters */
+
+ /* restore everything to the way it was before the .GS, then put */
+ /* out the `.GE' line from user */
+
+ /* printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE); */
+ /* groff doesn't understand the \Ds command */
+
+ printf("\\D't %du'\n", DEFTHICK);
+ if (flyback) /* make sure we end up at top of */
+ printf(".sp -1\n"); /* picture if `flying back' */
+ if (stipple) /* restore stipple to previous */
+ printf(".st\n");
+ printf(".br\n"
+ ".ft \\n(g3\n"
+ ".ps \\n(g4\n"
+ "%s", inputline);
+ } else
+ interpret(inputline); /* take commands from the input file */
+ } while (!done);
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: savestate ( )
+ |
+ | Results: all the current scaling / font size / font name / thickness
+ | / pointscale settings are saved to be the defaults. Scaled
+ | point sizes are NOT saved. The scaling is done each time a
+ | new picture is started.
+ |
+ | Side Efct: scale, and def* are modified.
+ *----------------------------------------------------------------------------*/
+
+void
+savestate()
+{
+ register int i;
+
+ for (i = 0; i < STYLES; i++) /* line thickness defaults */
+ defthick[i] = thick[i];
+ for (i = 0; i < FONTS; i++) /* font name defaults */
+ deffont[i] = tfont[i];
+ for (i = 0; i < SIZES; i++) /* font size defaults */
+ defsize[i] = tsize[i];
+ for (i = 0; i <= NSTIPPLES; i++) /* stipple font file default indices */
+ defstipple_index[i] = stipple_index[i];
+
+ defstipple = stipple; /* if stipple has been set, it's remembered */
+ scale *= xscale; /* default scale of individual pictures */
+ defpoint = pointscale; /* flag for scaling pointsizes from x factors */
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: savebounds (x_coordinate, y_coordinate)
+ |
+ | Results: Keeps track of the maximum and minimum extent of a picture
+ | in the global variables: left-, right-, top- and
+ | bottompoint. `savebounds' assumes that the points have been
+ | oriented to the correct direction. No scaling has taken
+ | place, though.
+ *----------------------------------------------------------------------------*/
+
+void
+savebounds(float x,
+ float y)
+{
+ if (x < leftpoint)
+ leftpoint = x;
+ if (x > rightpoint)
+ rightpoint = x;
+ if (y < toppoint)
+ toppoint = y;
+ if (y > bottompoint)
+ bottompoint = y;
+}
+
+
+/*----------------------------------------------------------------------------*
+ | Routine: interpret (character_string)
+ |
+ | Results: Commands are taken from the input string and performed.
+ | Commands are separated by the endofline, and are of the
+ | format:
+ | string1 string2
+ |
+ | where string1 is the command and string2 is the argument.
+ |
+ | Side Efct: Font and size strings, plus the gremlin file name and the
+ | width and height variables are set by this routine.
+ *----------------------------------------------------------------------------*/
+
+void
+interpret(char *line)
+{
+ char str1[MAXINLINE];
+ char str2[MAXINLINE];
+ register char *chr;
+ register int i;
+ double par;
+
+ str2[0] = '\0';
+ sscanf(line, "%80s%80s", &str1[0], &str2[0]);
+ for (chr = &str1[0]; *chr; chr++) /* convert command to */
+ if (isupper(*chr))
+ *chr = tolower(*chr); /* lower case */
+
+ switch (str1[0]) {
+
+ case '1':
+ case '2': /* font sizes */
+ case '3':
+ case '4':
+ i = atoi(str2);
+ if (i > 0 && i < 1000)
+ tsize[str1[0] - '1'] = i;
+ else
+ error("bad font size value at line %1", linenum);
+ break;
+
+ case 'r': /* roman */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[0] = (char *) malloc(strlen(str2) + 1);
+ strcpy(tfont[0], str2);
+ break;
+
+ case 'i': /* italics */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[1] = (char *) malloc(strlen(str2) + 1);
+ strcpy(tfont[1], str2);
+ break;
+
+ case 'b': /* bold */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[2] = (char *) malloc(strlen(str2) + 1);
+ strcpy(tfont[2], str2);
+ break;
+
+ case 's': /* special */
+ if (str1[1] == 'c')
+ goto scalecommand; /* or scale */
+
+ if (str2[0] < '0') {
+ nofont:
+ error("no fontname specified in line %1", linenum);
+ break;
+ }
+ if (str1[1] == 't')
+ goto stipplecommand; /* or stipple */
+
+ tfont[3] = (char *) malloc(strlen(str2) + 1);
+ strcpy(tfont[3], str2);
+ break;
+
+ case 'l': /* l */
+ if (isdigit(str1[1])) { /* set stipple index */
+ int index = atoi(str1 + 1), val;
+
+ if (index < 0 || index > NSTIPPLES) {
+ error("bad stipple number %1 at line %2", index, linenum);
+ break;
+ }
+ if (!defstipple_index)
+ defstipple_index = other_stipple_index;
+ val = atoi(str2);
+ if (val >= 0 && val < 256)
+ stipple_index[index] = val;
+ else
+ error("bad stipple index value at line %1", linenum);
+ break;
+ }
+
+ stipplecommand: /* set stipple name */
+ stipple = (char *) malloc(strlen(str2) + 1);
+ strcpy(stipple, str2);
+ /* if its a `known' font (currently only `cf'), set indicies */
+ if (strcmp(stipple, "cf") == 0)
+ defstipple_index = cf_stipple_index;
+ else
+ defstipple_index = other_stipple_index;
+ for (i = 0; i <= NSTIPPLES; i++)
+ stipple_index[i] = defstipple_index[i];
+ break;
+
+ case 'a': /* text adjust */
+ par = atof(str2);
+ switch (str1[1]) {
+ case '1':
+ adj1 = par;
+ break;
+ case '2':
+ adj2 = par;
+ break;
+ case '3':
+ adj3 = par;
+ break;
+ case '4':
+ adj4 = par;
+ break;
+ default:
+ error("bad adjust command at line %1", linenum);
+ break;
+ }
+ break;
+
+ case 't': /* thick */
+ thick[2] = defthick[0] * atof(str2);
+ break;
+
+ case 'm': /* medium */
+ thick[5] = defthick[0] * atof(str2);
+ break;
+
+ case 'n': /* narrow */
+ thick[0] = thick[1] = thick[3] = thick[4] =
+ defthick[0] * atof(str2);
+ break;
+
+ case 'x': /* x */
+ scalecommand: /* scale */
+ par = atof(str2);
+ if (par > 0.0)
+ xscale *= par;
+ else
+ error("illegal scale value on line %1", linenum);
+ break;
+
+ case 'f': /* file */
+ strcpy(gremlinfile, str2);
+ break;
+
+ case 'w': /* width */
+ width = atof(str2);
+ if (width < 0.0)
+ width = -width;
+ break;
+
+ case 'h': /* height */
+ height = atof(str2);
+ if (height < 0.0)
+ height = -height;
+ break;
+
+ case 'd': /* defaults */
+ setdefault = 1;
+ break;
+
+ case 'p': /* pointscale */
+ if (strcmp("off", str2))
+ pointscale = 1;
+ else
+ pointscale = 0;
+ break;
+
+ default:
+ error("unknown command `%1' on line %2", str1, linenum);
+ exit(8);
+ break;
+ };
+}
+
+
+/*
+ * return TRUE if picture contains a polygon
+ * otherwise FALSE
+ */
+
+int
+has_polygon(register ELT *elist)
+{
+ while (!DBNullelt(elist)) {
+ if (elist->type == POLYGON)
+ return (1);
+ elist = DBNextElt(elist);
+ }
+
+ return (0);
+}
+
+/* EOF */
diff --git a/contrib/groff/src/preproc/html/Makefile.sub b/contrib/groff/src/preproc/html/Makefile.sub
new file mode 100644
index 000000000000..9d5045a97b31
--- /dev/null
+++ b/contrib/groff/src/preproc/html/Makefile.sub
@@ -0,0 +1,7 @@
+PROG=pre-grohtml
+# MAN1=pre-grohtml.n
+MAN1=
+XLIBS=$(LIBGROFF)
+OBJS=pre-html.o pushbackbuffer.o
+CCSRCS=$(srcdir)/pre-html.cc $(srcdir)/pushbackbuffer.cc
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/html/pre-html.cc b/contrib/groff/src/preproc/html/pre-html.cc
new file mode 100644
index 000000000000..8357dd6e4603
--- /dev/null
+++ b/contrib/groff/src/preproc/html/pre-html.cc
@@ -0,0 +1,1160 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk).
+
+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. */
+
+#define PREHTMLC
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "posix.h"
+#include "defs.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _POSIX_VERSION
+#include <sys/wait.h>
+#define PID_T pid_t
+#else /* not _POSIX_VERSION */
+#define PID_T int
+#endif /* not _POSIX_VERSION */
+
+extern char *strerror();
+
+#include "pre-html.h"
+#include "pushbackbuffer.h"
+#include "html-strings.h"
+
+#define POSTSCRIPTRES 72000 // maybe there is a better way to find this? --fixme--
+#define DEFAULT_IMAGE_RES 80 // 80 pixels per inch resolution
+#define DEFAULT_VERTICAL_OFFSET 45 // DEFAULT_VERTICAL_OFFSET/72 of an inch
+#define IMAGE_BOARDER_PIXELS 0
+#define MAX_WIDTH 8 // inches
+#define INLINE_LEADER_CHAR '\\'
+
+#define TRANSPARENT "-background \"#FFF\" -transparent \"#FFF\""
+
+#if 0
+# define DEBUGGING
+# define DEBUG_HTML
+#endif
+
+#if !defined(TRUE)
+# define TRUE (1==1)
+#endif
+#if !defined(FALSE)
+# define FALSE (1==0)
+#endif
+
+void stop() {}
+
+typedef enum {CENTERED, LEFT, RIGHT, INLINE} IMAGE_ALIGNMENT;
+
+static int stdoutfd = 1; // output file descriptor - normally 1 but might move
+ // -1 means closed
+static int copyofstdoutfd =-1; // a copy of stdout, so we can restore stdout when
+ // writing to post-html
+static char *psFileName = 0; // name of postscript file
+static char *regionFileName = 0; // name of file containing all image regions
+static char *imagePageStem = 0; // stem of all files containing page images
+static char *image_device = "pnmraw";
+static int image_res = DEFAULT_IMAGE_RES;
+static int vertical_offset= DEFAULT_VERTICAL_OFFSET;
+static char *image_template = 0; // image template filename
+static int troff_arg = 0; // troff arg index
+static char *command_prefix = 0; // optional prefix for some installations.
+static char *troff_command = 0;
+#if defined(DEBUGGING)
+static int debug = FALSE;
+static char *troffFileName = 0; // output of pre-html output which is sent to troff -Tps
+static char *htmlFileName = 0; // output of pre-html output which is sent to troff -Thtml
+#endif
+
+
+/*
+ * Images are generated via postscript, gs and the pnm utilities.
+ */
+
+#define IMAGE_DEVICE "-Tps"
+
+/*
+ * prototypes
+ */
+static int do_file(const char *filename);
+
+/*
+ * sys_fatal - writes a fatal error message. Taken from src/roff/groff/pipeline.c
+ */
+
+void sys_fatal (const char *s)
+{
+ fprintf(stderr, "%s: %s: %s", program_name, s, strerror(errno));
+}
+
+/*
+ * the class and methods for retaining ascii text
+ */
+
+struct char_block {
+ enum { SIZE = 256 };
+ char buffer[SIZE];
+ int used;
+ char_block *next;
+
+ char_block();
+};
+
+/*
+ * char_block - constructor, sets the, used, and, next, fields to zero.
+ */
+
+char_block::char_block()
+: used(0), next(0)
+{
+}
+
+class char_buffer {
+public:
+ char_buffer();
+ ~char_buffer();
+ int read_file(FILE *fp);
+ int do_html(int argc, char *argv[]);
+ int do_image(int argc, char *argv[]);
+ void write_file_html(void);
+ void write_file_troff(void);
+ void write_upto_newline (char_block **t, int *i, int is_html);
+ int can_see(char_block **t, int *i, char *string);
+ int skip_spaces(char_block **t, int *i);
+ void skip_to_newline(char_block **t, int *i);
+private:
+ char_block *head;
+ char_block *tail;
+};
+
+/*
+ * char_buffer - constructor
+ */
+
+char_buffer::char_buffer()
+: head(0), tail(0)
+{
+}
+
+/*
+ * char_buffer - deconstructor, throws aways the whole buffer list.
+ */
+
+char_buffer::~char_buffer()
+{
+ while (head != 0) {
+ char_block *temp = head;
+ head = head->next;
+ delete temp;
+ }
+}
+
+/*
+ * read_file - read in a complete file, fp, placing the contents inside char_blocks.
+ */
+
+int char_buffer::read_file (FILE *fp)
+{
+ int i=0;
+ unsigned int old_used;
+ int n;
+
+ while (! feof(fp)) {
+ if (tail == 0) {
+ tail = new char_block;
+ head = tail;
+ } else {
+ if (tail->used == char_block::SIZE) {
+ tail->next = new char_block;
+ tail = tail->next;
+ }
+ }
+ // at this point we have a tail which is ready for the next SIZE bytes of the file
+
+ n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
+ if (n <= 0) {
+ // error
+ return( 0 );
+ } else {
+ tail->used += n*sizeof(char);
+ }
+ }
+ return( 1 );
+}
+
+/*
+ * writeNbytes - writes n bytes to stdout.
+ */
+
+static void writeNbytes (char *s, int l)
+{
+ int n=0;
+ int r;
+
+ while (n<l) {
+ r = write(stdoutfd, s, l-n);
+ if (r<0) {
+ sys_fatal("write");
+ }
+ n += r;
+ s += r;
+ }
+}
+
+/*
+ * writeString - writes a string to stdout.
+ */
+
+static void writeString (char *s)
+{
+ writeNbytes(s, strlen(s));
+}
+
+/*
+ * makeFileName - creates the image filename template.
+ */
+
+void makeFileName ()
+{
+ char buffer[8192];
+
+ sprintf(buffer, "grohtml-%d", (int)getpid());
+ strcat(buffer, "-%d");
+ image_template = (char *)malloc(strlen(buffer)+1);
+ strcpy(image_template, buffer);
+}
+
+/*
+ * write_end_image - ends the image. It writes out the image extents if we are using -Tps.
+ */
+
+static void write_end_image (int is_html)
+{
+ if (is_html) {
+ /*
+ * emit image name and enable output
+ */
+ writeString("\\O2\\O1\\O4\n");
+ } else {
+ /*
+ * postscript, therefore emit image boundaries
+ */
+ writeString("\\O2\\O4\n");
+ }
+}
+
+/*
+ * write_start_image - writes the troff which will:
+ *
+ * (i) disable html output for the following image
+ * (ii) reset the max/min x/y registers during postscript
+ * rendering.
+ */
+
+static void write_start_image (IMAGE_ALIGNMENT pos, int is_html)
+{
+ if (pos == INLINE) {
+ writeString("\\O3\\O5'");
+ writeString(image_template); writeString(".png'");
+ } else {
+ writeString(".begin \\{\\\n");
+ switch (pos) {
+
+ case LEFT:
+ writeString(". image l ");
+ break;
+ case RIGHT:
+ writeString(". image r ");
+ break;
+ case CENTERED:
+ default:
+ writeString(". image c ");
+ }
+ writeString(image_template); writeString(".png\n");
+ if (! is_html) {
+ writeString(".bp\n");
+ writeString(".tl ''''\n");
+ }
+ writeString("\\}\n");
+ }
+ if (is_html) {
+ writeString("\\O0\n");
+ } else {
+ // reset min/max registers
+ writeString("\\O0\\O1\n");
+ }
+}
+
+/*
+ * write_upto_newline - writes the contents of the buffer until a newline is seen.
+ * It checks for HTML_IMAGE_INLINE_BEGIN and HTML_IMAGE_INLINE_END
+ * and if they are present it processes them.
+ */
+
+void char_buffer::write_upto_newline (char_block **t, int *i, int is_html)
+{
+ int j=*i;
+
+ if (*t) {
+ while ((j < (*t)->used) && ((*t)->buffer[j] != '\n') &&
+ ((*t)->buffer[j] != INLINE_LEADER_CHAR)) {
+ j++;
+ }
+ if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) {
+ j++;
+ }
+ writeNbytes((*t)->buffer+(*i), j-(*i));
+ if ((*t)->buffer[j] == INLINE_LEADER_CHAR) {
+ if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
+ write_start_image(INLINE, is_html);
+ else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
+ write_end_image(is_html);
+ else {
+ if (j < (*t)->used) {
+ *i = j;
+ j++;
+ writeNbytes((*t)->buffer+(*i), j-(*i));
+ }
+ }
+ }
+ if (j == (*t)->used) {
+ *i = 0;
+ if ((*t)->buffer[j-1] == '\n') {
+ *t = (*t)->next;
+ } else {
+ *t = (*t)->next;
+ write_upto_newline(t, i, is_html);
+ }
+ } else {
+ // newline was seen
+ *i = j;
+ }
+ }
+}
+
+/*
+ * can_see - returns TRUE if we can see string in t->buffer[i] onwards
+ */
+
+int char_buffer::can_see (char_block **t, int *i, char *string)
+{
+ int j = 0;
+ int l = strlen(string);
+ int k = *i;
+ char_block *s = *t;
+
+ while (s) {
+ while ((k<s->used) && (j<l) && (s->buffer[k] == string[j])) {
+ j++;
+ k++;
+ }
+ if (j == l) {
+ *i = k;
+ *t = s;
+ return( TRUE );
+ } else if ((k<s->used) && (s->buffer[k] != string[j])) {
+ return( FALSE );
+ }
+ s = s->next;
+ k = 0;
+ }
+ return( FALSE );
+}
+
+/*
+ * skip_spaces - returns TRUE if we have not run out of data.
+ * It also consumes spaces.
+ */
+
+int char_buffer::skip_spaces(char_block **t, int *i)
+{
+ char_block *s = *t;
+ int k = *i;
+
+ while (s) {
+ while ((k<s->used) && (isspace(s->buffer[k]))) {
+ k++;
+ }
+ if (k == s->used) {
+ k = 0;
+ s = s->next;
+ } else {
+ *i = k;
+ return( TRUE );
+ }
+ }
+ return( FALSE );
+}
+
+/*
+ * skip_to_newline - skips all characters until a newline is seen.
+ * The newline is also consumed.
+ */
+
+void char_buffer::skip_to_newline (char_block **t, int *i)
+{
+ int j=*i;
+
+ if (*t) {
+ while ((j < (*t)->used) && ((*t)->buffer[j] != '\n')) {
+ j++;
+ }
+ if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) {
+ j++;
+ }
+ if (j == (*t)->used) {
+ *i = 0;
+ if ((*t)->buffer[j-1] == '\n') {
+ *t = (*t)->next;
+ } else {
+ *t = (*t)->next;
+ skip_to_newline(t, i);
+ }
+ } else {
+ // newline was seen
+ *i = j;
+ }
+ }
+}
+
+/*
+ * write_file_troff - writes the buffer to stdout (troff).
+ */
+
+void char_buffer::write_file_troff (void)
+{
+ char_block *t=head;
+ int r;
+ int i=0;
+
+ if (t != 0) {
+ do {
+ /*
+ * remember to check the shortest string last
+ */
+ if (can_see(&t, &i, HTML_IMAGE_END)) {
+ write_end_image(FALSE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_LEFT)) {
+ write_start_image(LEFT, FALSE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_RIGHT)) {
+ write_start_image(RIGHT, FALSE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_CENTERED)) {
+ write_start_image(CENTERED, FALSE);
+ skip_to_newline(&t, &i);
+ } else {
+ write_upto_newline(&t, &i, FALSE);
+ }
+ } while (t != 0);
+ }
+ if (close(stdoutfd) < 0)
+ sys_fatal("close");
+
+ // now we grab fd=1 so that the next pipe cannot use fd=1
+ if (stdoutfd == 1) {
+ if (dup(2) != stdoutfd) {
+ sys_fatal("dup failed to use fd=1");
+ }
+ }
+}
+
+/*
+ * the image class remembers the position of all images in the postscript file
+ * and assigns names for each image.
+ */
+
+struct imageItem {
+ imageItem *next;
+ int X1;
+ int Y1;
+ int X2;
+ int Y2;
+ char *imageName;
+ int resolution;
+ int maxx;
+ int pageNo;
+
+ imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name);
+ ~imageItem ();
+};
+
+/*
+ * imageItem - constructor
+ */
+
+imageItem::imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name)
+{
+ X1 = x1;
+ Y1 = y1;
+ X2 = x2;
+ Y2 = y2;
+ pageNo = page;
+ resolution = res;
+ maxx = max_width;
+ imageName = name;
+ next = 0;
+}
+
+/*
+ * imageItem - deconstructor
+ */
+
+imageItem::~imageItem ()
+{
+}
+
+/*
+ * imageList - class containing a list of imageItems.
+ */
+
+class imageList {
+private:
+ imageItem *head;
+ imageItem *tail;
+ int count;
+public:
+ imageList();
+ ~imageList();
+ void add(int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name);
+};
+
+/*
+ * imageList - constructor.
+ */
+
+imageList::imageList ()
+ : head(0), tail(0), count(0)
+{
+}
+
+/*
+ * imageList - deconstructor.
+ */
+
+imageList::~imageList ()
+{
+ while (head != 0) {
+ imageItem *i = head;
+ head = head->next;
+ delete i;
+ }
+}
+
+/*
+ * createAllPages - creates a set of images, one per page.
+ */
+
+static void createAllPages (void)
+{
+ char buffer[4096];
+
+ sprintf(buffer,
+ "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -sOutputFile=%s%%d %s - > /dev/null 2>&1 \n",
+ image_device,
+ image_res,
+ imagePageStem,
+ psFileName);
+#if defined(DEBUGGING)
+ fwrite(buffer, sizeof(char), strlen(buffer), stderr);
+ fflush(stderr);
+#endif
+ system(buffer);
+}
+
+/*
+ * removeAllPages - removes all page images.
+ */
+
+static void removeAllPages (void)
+{
+#if !defined(DEBUGGING)
+ char buffer[4096];
+ int i=1;
+
+ do {
+ sprintf(buffer, "%s%d", imagePageStem, i);
+ i++;
+ } while (remove(buffer) == 0);
+#endif
+}
+
+/*
+ * abs - returns the absolute value.
+ */
+
+int abs (int x)
+{
+ if (x < 0) {
+ return( -x );
+ } else {
+ return( x );
+ }
+}
+
+/*
+ * min - returns the minimum of two numbers.
+ */
+
+int min (int x, int y)
+{
+ if (x < y) {
+ return( x );
+ } else {
+ return( y );
+ }
+}
+
+/*
+ * max - returns the maximum of two numbers.
+ */
+
+int max (int x, int y)
+{
+ if (x > y) {
+ return( x );
+ } else {
+ return( y );
+ }
+}
+
+/*
+ * createImage - generates a minimal png file from the set of page images.
+ */
+
+static void createImage (imageItem *i)
+{
+ if (i->X1 != -1) {
+ char buffer[4096];
+ int x1 = max(min(i->X1, i->X2)*image_res/POSTSCRIPTRES-1*IMAGE_BOARDER_PIXELS, 0);
+ int y1 = max((image_res*vertical_offset/72)+min(i->Y1, i->Y2)*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS, 0);
+ int x2 = max(i->X1, i->X2)*image_res/POSTSCRIPTRES+1*IMAGE_BOARDER_PIXELS;
+ int y2 = (image_res*vertical_offset/72)+max(i->Y1, i->Y2)*image_res/POSTSCRIPTRES+1*IMAGE_BOARDER_PIXELS;
+
+ sprintf(buffer,
+ "pnmcut %d %d %d %d < %s%d | pnmtopng %s > %s \n",
+ x1, y1, x2-x1+1, y2-y1+1,
+ imagePageStem,
+ i->pageNo,
+ TRANSPARENT,
+ i->imageName);
+#if defined(DEBUGGING)
+ fprintf(stderr, buffer);
+#endif
+ system(buffer);
+#if defined(DEBUGGING)
+ } else {
+ fprintf(stderr, "ignoring image as x1 coord is -1\n");
+ fflush(stderr);
+#endif
+ }
+}
+
+/*
+ * add - an image description to the imageList.
+ */
+
+void imageList::add (int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name)
+{
+ imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
+
+ if (head == 0) {
+ head = i;
+ tail = i;
+ } else {
+ tail->next = i;
+ tail = i;
+ }
+ createImage(i);
+}
+
+static imageList listOfImages; // list of images defined by the region file.
+
+/*
+ * write_file_html - writes the buffer to stdout (troff).
+ * It writes out the file replacing template image names with
+ * actual image names.
+ */
+
+void char_buffer::write_file_html (void)
+{
+ char_block *t =head;
+ char *name;
+ int i=0;
+
+ if (t != 0) {
+ stop();
+ do {
+ /*
+ * remember to check the shortest string last
+ */
+ if (can_see(&t, &i, HTML_IMAGE_END)) {
+ write_end_image(TRUE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_LEFT)) {
+ write_start_image(LEFT, TRUE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_RIGHT)) {
+ write_start_image(RIGHT, TRUE);
+ skip_to_newline(&t, &i);
+ } else if (can_see(&t, &i, HTML_IMAGE_CENTERED)) {
+ stop();
+ write_start_image(CENTERED, TRUE);
+ skip_to_newline(&t, &i);
+ } else {
+ write_upto_newline(&t, &i, TRUE);
+ }
+ } while (t != 0);
+ }
+ if (close(stdoutfd) < 0)
+ sys_fatal("close");
+
+ // now we grab fd=1 so that the next pipe cannot use fd=1
+ if (stdoutfd == 1) {
+ if (dup(2) != stdoutfd) {
+ sys_fatal("dup failed to use fd=1");
+ }
+ }
+}
+
+/*
+ * generateImages - parses the region file and generates images
+ * from the postscript file. The region file
+ * contains the x1,y1 x2,y2 extents of each
+ * image.
+ */
+
+static void generateImages (char *regionFileName)
+{
+ pushBackBuffer *f=new pushBackBuffer(regionFileName);
+ char ch;
+
+ while (f->putPB(f->getPB()) != eof) {
+ if (f->isString("grohtml-info:page")) {
+ int page = f->readInt();
+ int x1 = f->readInt();
+ int y1 = f->readInt();
+ int x2 = f->readInt();
+ int y2 = f->readInt();
+ int maxx = max(f->readInt(), MAX_WIDTH*image_res);
+ char *name = f->readString();
+ int res = POSTSCRIPTRES; // --fixme-- prefer (f->readInt()) providing that troff can discover the value
+ listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
+ while ((f->putPB(f->getPB()) != '\n') &&
+ (f->putPB(f->getPB()) != eof)) {
+ ch = f->getPB();
+ }
+ if (f->putPB(f->getPB()) == '\n') {
+ ch = f->getPB();
+ }
+ } else {
+ /*
+ * write any error messages out to the user
+ */
+ fputc(f->getPB(), stderr);
+ }
+ }
+}
+
+/*
+ * replaceFd - replace a file descriptor, was, with, willbe.
+ */
+
+static void replaceFd (int was, int willbe)
+{
+ int dupres;
+
+ if (was != willbe) {
+ if (close(was)<0) {
+ sys_fatal("close");
+ }
+ dupres = dup(willbe);
+ if (dupres != was) {
+ sys_fatal("dup");
+ fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres);
+ if (willbe == 1) {
+ fprintf(stderr, "likely that stdout should be opened before %d\n", was);
+ }
+ exit(1);
+ }
+ if (close(willbe) < 0) {
+ sys_fatal("close");
+ }
+ }
+}
+
+/*
+ * waitForChild - waits for child, pid, to exit.
+ */
+
+static void waitForChild (PID_T pid)
+{
+ PID_T waitpd;
+ int status;
+
+ waitpd = wait(&status);
+ if (waitpd != pid)
+ sys_fatal("wait");
+}
+
+/*
+ * alterDeviceTo - if toImage is set then the arg list is altered to include
+ * IMAGE_DEVICE and we invoke groff rather than troff.
+ * else
+ * set -Thtml and troff
+ */
+
+static void alterDeviceTo (int argc, char *argv[], int toImage)
+{
+ int i=0;
+
+ if (toImage) {
+ while (i < argc) {
+ if (strcmp(argv[i], "-Thtml") == 0) {
+ argv[i] = IMAGE_DEVICE;
+ }
+ i++;
+ }
+ argv[troff_arg] = "groff"; /* rather than troff */
+ } else {
+ while (i < argc) {
+ if (strcmp(argv[i], IMAGE_DEVICE) == 0) {
+ argv[i] = "-Thtml";
+ }
+ i++;
+ }
+ argv[troff_arg] = troff_command; /* use troff */
+ }
+}
+
+/*
+ * do_html - sets the troff number htmlflip and
+ * writes out the buffer to troff -Thtml
+ */
+
+int char_buffer::do_html(int argc, char *argv[])
+{
+ int pdes[2];
+ PID_T pid;
+
+ if (pipe(pdes) < 0)
+ sys_fatal("pipe");
+
+ alterDeviceTo(argc, argv, 0);
+ argv += troff_arg; // skip all arguments up to troff/groff
+ argc -= troff_arg;
+
+#if defined(DEBUG_HTML)
+ write_file_html();
+ writeString("--------------- troff --------------------------\n");
+ write_file_troff();
+#else
+ pid = fork();
+ if (pid < 0)
+ sys_fatal("fork");
+
+ if (pid == 0) {
+ // child
+ replaceFd(0, pdes[0]);
+ // close end we are not using
+ if (close(pdes[1])<0)
+ sys_fatal("close");
+ replaceFd(1, copyofstdoutfd); // and restore stdout
+
+ execvp(argv[0], argv);
+ error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0);
+ fflush(stderr); /* just in case error() doesn't */
+ exit(1);
+ } else {
+ // parent
+
+#if defined(DEBUGGING)
+ /*
+ * slight security risk so only enabled if compiled with defined(DEBUGGING)
+ */
+ if (debug) {
+ replaceFd(1, creat(htmlFileName, S_IWUSR|S_IRUSR));
+ write_file_html();
+ }
+#endif
+ replaceFd(1, pdes[1]);
+ // close end we are not using
+ if (close(pdes[0])<0)
+ sys_fatal("close");
+
+ write_file_html();
+ waitForChild(pid);
+ }
+#endif
+ return( 0 );
+}
+
+/*
+ * addps4html - appends -rps4html=1 onto the command list for troff.
+ */
+
+char **addps4html (int argc, char *argv[])
+{
+ char **new_argv = (char **)malloc((argc+2)*sizeof(char *));
+ int i=0;
+
+ while (i<argc) {
+ new_argv[i] = argv[i];
+ i++;
+ }
+ new_argv[argc] = "-rps4html=1";
+ argc++;
+ new_argv[argc] = NULL;
+ return( new_argv );
+}
+
+/*
+ * do_image - writes out the buffer to troff -Tps
+ */
+
+int char_buffer::do_image(int argc, char *argv[])
+{
+ PID_T pid;
+ int pdes[2];
+
+ if (pipe(pdes) < 0)
+ sys_fatal("pipe");
+
+ alterDeviceTo(argc, argv, 1);
+ argv += troff_arg; // skip all arguments up to troff/groff
+ argc -= troff_arg;
+ argv = addps4html(argc, argv);
+ argc++;
+
+ pid = fork();
+ if (pid == 0) {
+ // child
+
+ int psFd = creat(psFileName, S_IWUSR|S_IRUSR);
+ int regionFd = creat(regionFileName, S_IWUSR|S_IRUSR);
+
+ replaceFd(1, psFd);
+ replaceFd(0, pdes[0]);
+ replaceFd(2, regionFd);
+
+ // close end we are not using
+ if (close(pdes[1])<0)
+ sys_fatal("close");
+
+ execvp(argv[0], argv);
+ error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0);
+ fflush(stderr); /* just in case error() doesn't */
+ exit(1);
+ } else {
+ // parent
+
+#if defined(DEBUGGING)
+ /*
+ * slight security risk so only enabled if compiled with defined(DEBUGGING)
+ */
+ if (debug) {
+ replaceFd(1, creat(troffFileName, S_IWUSR|S_IRUSR));
+ write_file_troff();
+ }
+#endif
+ replaceFd(1, pdes[1]);
+ write_file_troff();
+ waitForChild(pid);
+ }
+ return( 0 );
+}
+
+static char_buffer inputFile;
+
+
+/*
+ * usage - emit usage arguments.
+ */
+
+void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s troffname [-P-o vertical_image_offset] [-P-i image_resolution] [troff flags] [files]\n", program_name);
+ fprintf(stream, " vertical_image_offset (default %d/72 of an inch)\n", vertical_offset);
+ fprintf(stream, " image_resolution (default %d) pixels per inch\n", image_res);
+}
+
+/*
+ * scanArguments - scans for -P-i and -P-o arguments.
+ */
+
+int scanArguments (int argc, char **argv)
+{
+ int i=1;
+
+ while (i<argc) {
+ if (strncmp(argv[i], "-i", 2) == 0) {
+ image_res = atoi((char *)(argv[i]+2));
+ } else if (strncmp(argv[i], "-o", 2) == 0) {
+ vertical_offset = atoi((char *)(argv[i]+2));
+ } else if ((strcmp(argv[i], "-v") == 0)
+ || (strcmp(argv[i], "--version") == 0)) {
+ extern const char *Version_string;
+ printf("GNU pre-grohtml (groff) version %s\n", Version_string);
+ exit(0);
+ } else if ((strcmp(argv[i], "-h") == 0)
+ || (strcmp(argv[i], "--help") == 0)
+ || (strcmp(argv[i], "-?") == 0)) {
+ usage(stdout);
+ exit(0);
+ } else if (strcmp(argv[i], "troff") == 0) {
+ /* remember troff argument number */
+ troff_arg = i;
+#if defined(DEBUGGING)
+ } else if (strcmp(argv[i], "-d") == 0) {
+ debug = TRUE;
+#endif
+ } else if (argv[i][0] != '-') {
+ return( i );
+ }
+ i++;
+ }
+ return( argc );
+}
+
+/*
+ * makeTempFiles - name the temporary files
+ */
+
+static void makeTempFiles (void)
+{
+#if defined(DEBUGGING)
+ psFileName = "/tmp/prehtml-ps";
+ regionFileName = "/tmp/prehtml-region";
+ imagePageStem = "/tmp/prehtml-page";
+ troffFileName = "/tmp/prehtml-troff";
+ htmlFileName = "/tmp/prehtml-html";
+#else
+ psFileName = mktemp(xtmptemplate("-ps-"));
+ regionFileName = mktemp(xtmptemplate("-regions-"));
+ imagePageStem = mktemp(xtmptemplate("-page-"));
+#endif
+}
+
+/*
+ * removeTempFiles - remove the temporary files
+ */
+
+static void removeTempFiles (void)
+{
+#if !defined(DEBUGGING)
+ remove(psFileName);
+ remove(regionFileName);
+#endif
+}
+
+/*
+ * findPrefix - finds the optional prefix to the groff utilities.
+ * It also builds the 'troff' executable name.
+ */
+
+static void findPrefix (void)
+{
+ command_prefix = getenv("GROFF_COMMAND_PREFIX");
+ if (!command_prefix)
+ command_prefix = PROG_PREFIX;
+ troff_command = (char *)malloc(strlen("troff")+strlen(command_prefix)+1);
+ strcpy(troff_command, command_prefix);
+ strcat(troff_command, "troff");
+}
+
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int i;
+ int found=0;
+ int ok=1;
+
+ findPrefix();
+ makeFileName();
+ i = scanArguments(argc, argv);
+ while (i < argc) {
+ if (argv[i][0] != '-') {
+ /* found source file */
+ ok = do_file(argv[i]);
+ if (! ok) {
+ return( 0 );
+ }
+ found = 1;
+ }
+ i++;
+ }
+
+ copyofstdoutfd=dup(stdoutfd);
+
+ if (! found) {
+ do_file("-");
+ }
+ makeTempFiles();
+ ok = inputFile.do_image(argc, argv);
+ if (ok == 0) {
+ createAllPages();
+ generateImages(regionFileName);
+ ok = inputFile.do_html(argc, argv);
+ removeAllPages();
+ }
+ removeTempFiles();
+ return ok;
+}
+
+static int do_file(const char *filename)
+{
+ FILE *fp;
+
+ current_filename = filename;
+ if (strcmp(filename, "-") == 0) {
+ fp = stdin;
+ } else {
+ fp = fopen(filename, "r");
+ if (fp == 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ }
+
+ if (inputFile.read_file(fp)) {
+ }
+
+ if (fp != stdin)
+ fclose(fp);
+ current_filename = 0;
+ return 1;
+}
diff --git a/contrib/groff/src/preproc/html/pre-html.h b/contrib/groff/src/preproc/html/pre-html.h
new file mode 100644
index 000000000000..f9a590c17b7e
--- /dev/null
+++ b/contrib/groff/src/preproc/html/pre-html.h
@@ -0,0 +1,37 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk).
+
+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. */
+
+/*
+ * defines functions implemented within pre-html.c
+ */
+
+#if !defined(PREHTMLH)
+# define PREHTMLH
+# if defined(PREHTMLC)
+# define EXTERN
+# else
+# define EXTERN extern
+# endif
+
+
+extern void sys_fatal (const char *s);
+
+#undef EXTERN
+#endif
diff --git a/contrib/groff/src/preproc/html/pushbackbuffer.cc b/contrib/groff/src/preproc/html/pushbackbuffer.cc
new file mode 100644
index 000000000000..1d380f4da942
--- /dev/null
+++ b/contrib/groff/src/preproc/html/pushbackbuffer.cc
@@ -0,0 +1,337 @@
+// -*- C++ -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk).
+
+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 <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "posix.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "pushbackbuffer.h"
+#include "pre-html.h"
+
+#if !defined(TRUE)
+# define TRUE (1==1)
+#endif
+
+#if !defined(FALSE)
+# define FALSE (1==0)
+#endif
+
+# define ERROR(X) (fprintf(stderr, "%s:%d error %s\n", __FILE__, __LINE__, X) && \
+ (fflush(stderr)) && localexit(1))
+
+
+#define MAXPUSHBACKSTACK 4096 /* maximum number of character that can be pushed back */
+
+
+/*
+ * constructor for pushBackBuffer
+ */
+
+pushBackBuffer::pushBackBuffer (char *filename)
+{
+ charStack = (char *)malloc(MAXPUSHBACKSTACK);
+ if (charStack == 0) {
+ sys_fatal("malloc");
+ }
+ stackPtr = 0; /* index to push back stack */
+ debug = 0;
+ verbose = 0;
+ eofFound = FALSE;
+ lineNo = 1;
+ if (strcmp(filename, "") != 0) {
+ stdIn = dup(0);
+ close(0);
+ if (open(filename, O_RDONLY) != 0) {
+ sys_fatal("when trying to open file");
+ } else {
+ fileName = filename;
+ }
+ }
+}
+
+pushBackBuffer::~pushBackBuffer ()
+{
+ int old;
+
+ if (charStack != 0) {
+ free(charStack);
+ }
+ close(0);
+ /* restore stdin in file descriptor 0 */
+ old = dup(stdIn);
+ close(stdIn);
+}
+
+/*
+ * localexit - wraps exit with a return code to aid the ERROR macro.
+ */
+
+int localexit (int i)
+{
+ exit(i);
+ return( 1 );
+}
+
+/*
+ * getPB - returns a character, possibly a pushed back character.
+ */
+
+char pushBackBuffer::getPB (void)
+{
+ if (stackPtr>0) {
+ stackPtr--;
+ return( charStack[stackPtr] );
+ } else {
+ char ch;
+
+ if (read(0, &ch, 1) == 1) {
+ if (verbose) {
+ printf("%c", ch);
+ }
+ if (ch == '\n') {
+ lineNo++;
+ }
+ return( ch );
+ } else {
+ eofFound = TRUE;
+ return( eof );
+ }
+ }
+}
+
+/*
+ * putPB - pushes a character onto the push back stack.
+ * The same character is returned.
+ */
+
+char pushBackBuffer::putPB (char ch)
+{
+ if (stackPtr<MAXPUSHBACKSTACK) {
+ charStack[stackPtr] = ch ;
+ stackPtr++;
+ } else {
+ ERROR("max push back stack exceeded, increase MAXPUSHBACKSTACK constant");
+ }
+ return( ch );
+}
+
+/*
+ * isWhite - returns TRUE if a white character is found. This character is NOT consumed.
+ */
+
+static int isWhite (char ch)
+{
+ return( (ch==' ') || (ch == '\t') || (ch == '\n') );
+}
+
+/*
+ * skipToNewline - skips characters until a newline is seen.
+ */
+
+void pushBackBuffer::skipToNewline (void)
+{
+ char ch;
+
+ while ((putPB(getPB()) != '\n') && (! eofFound)) {
+ ch = getPB();
+ }
+}
+
+/*
+ * skipUntilToken - skips until a token is seen
+ */
+
+void pushBackBuffer::skipUntilToken (void)
+{
+ char ch;
+
+ while ((isWhite(putPB(getPB())) || (putPB(getPB()) == '#')) && (! eofFound)) {
+ ch = getPB();
+ if (ch == '#') {
+ skipToNewline();
+ }
+ }
+}
+
+/*
+ * isString - returns TRUE if the string, s, matches the pushed back string.
+ * if TRUE is returned then this string is consumed, otherwise it is
+ * left alone.
+ */
+
+int pushBackBuffer::isString (char *s)
+{
+ int length=strlen(s);
+ int i=0;
+ int j;
+
+ while ((i<length) && (putPB(getPB())==s[i])) {
+ if (getPB() != s[i]) {
+ ERROR("assert failed");
+ }
+ i++;
+ }
+ if (i==length) {
+ return( TRUE );
+ } else {
+ i--;
+ while (i>=0) {
+ if (putPB(s[i]) != s[i]) {
+ ERROR("assert failed");
+ }
+ i--;
+ }
+ }
+ return( FALSE );
+}
+
+/*
+ * isDigit - returns TRUE if the character, ch, is a digit.
+ */
+
+static int isDigit (char ch)
+{
+ return( ((ch>='0') && (ch<='9')) );
+}
+
+/*
+ * isHexDigit - returns TRUE if the character, ch, is a hex digit.
+ */
+
+static int isHexDigit (char ch)
+{
+ return( (isDigit(ch)) || ((ch>='a') && (ch<='f')) );
+}
+
+/*
+ * readInt - returns an integer from the input stream.
+ */
+
+int pushBackBuffer::readInt (void)
+{
+ int c =0;
+ int i =0;
+ int s =1;
+ char ch=getPB();
+
+ while (isWhite(ch)) {
+ ch=getPB();
+ }
+ // now read integer
+
+ if (ch == '-') {
+ s = -1;
+ ch = getPB();
+ }
+ while (isDigit(ch)) {
+ i *= 10;
+ if ((ch>='0') && (ch<='9')) {
+ i += (int)(ch-'0');
+ }
+ ch = getPB();
+ c++;
+ }
+ if (ch != putPB(ch)) {
+ ERROR("assert failed");
+ }
+ return( i*s );
+}
+
+/*
+ * convertToFloat - converts integers, a and b into a.b
+ */
+
+static float convertToFloat (int a, int b)
+{
+ int c=10;
+ float f;
+
+ while (b>c) {
+ c *= 10;
+ }
+ f = ((float)a) + (((float)b)/((float)c));
+ return( f );
+}
+
+/*
+ * readNumber - returns a float representing the word just read.
+ */
+
+float pushBackBuffer::readNumber (void)
+{
+ int integer;
+ int fraction;
+ char ch;
+ float f;
+
+ integer = readInt();
+ if (putPB(getPB()) == '.') {
+ ch = getPB();
+ fraction = readInt();
+ f = convertToFloat(integer, fraction);
+ return( f );
+ } else {
+ return( (float)integer );
+ }
+}
+
+/*
+ * readString - reads a string terminated by white space
+ * and returns a malloced area of memory containing
+ * a copy of the characters.
+ */
+
+char *pushBackBuffer::readString (void)
+{
+ char buffer[MAXPUSHBACKSTACK];
+ char *string = 0;
+ int i=0;
+ char ch=getPB();
+
+ while (isWhite(ch)) {
+ ch=getPB();
+ }
+ while ((i < MAXPUSHBACKSTACK) && (! isWhite(ch)) && (! eofFound)) {
+ buffer[i] = ch;
+ i++;
+ ch = getPB();
+ }
+ if (i < MAXPUSHBACKSTACK) {
+ buffer[i] = (char)0;
+ string = (char *)malloc(strlen(buffer)+1);
+ strcpy(string, buffer);
+ }
+ return( string );
+}
diff --git a/contrib/groff/src/preproc/html/pushbackbuffer.h b/contrib/groff/src/preproc/html/pushbackbuffer.h
new file mode 100644
index 000000000000..93cb3f1a4e82
--- /dev/null
+++ b/contrib/groff/src/preproc/html/pushbackbuffer.h
@@ -0,0 +1,54 @@
+// -*- C -*-
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk).
+
+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. */
+
+
+#define eof (char)-1
+
+
+/*
+ * defines the class and methods implemented within pushbackbuffer.cc
+ */
+
+class pushBackBuffer
+{
+ private:
+ char *charStack;
+ int stackPtr; /* index to push back stack */
+ int debug;
+ int verbose;
+ int eofFound;
+ char *fileName;
+ int lineNo;
+ int stdIn;
+
+ public:
+ pushBackBuffer (char *);
+ ~ pushBackBuffer ();
+ char getPB (void);
+ char putPB (char ch);
+ void skipUntilToken (void);
+ void skipToNewline (void);
+ float readNumber (void);
+ int readInt (void);
+ char *readString (void);
+ int isString (char *string);
+};
+
+
diff --git a/contrib/groff/src/preproc/pic/Makefile.sub b/contrib/groff/src/preproc/pic/Makefile.sub
new file mode 100644
index 000000000000..f1e292731c14
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/Makefile.sub
@@ -0,0 +1,31 @@
+PROG=pic
+MAN1=pic.n
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ pic.o \
+ lex.o \
+ main.o \
+ object.o \
+ common.o \
+ troff.o \
+ tex.o
+ # fig.o
+CCSRCS=\
+ $(srcdir)/lex.cc \
+ $(srcdir)/main.cc \
+ $(srcdir)/object.cc \
+ $(srcdir)/common.cc \
+ $(srcdir)/troff.cc \
+ $(srcdir)/tex.cc
+HDRS=\
+ $(srcdir)/common.h \
+ $(srcdir)/object.h \
+ $(srcdir)/output.h \
+ $(srcdir)/pic.h \
+ $(srcdir)/position.h \
+ $(srcdir)/text.h
+GRAM=$(srcdir)/pic.y
+YTABC=$(srcdir)/pic.cc
+YTABH=$(srcdir)/pic_tab.h
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/pic/TODO b/contrib/groff/src/preproc/pic/TODO
new file mode 100644
index 000000000000..2346b575e1de
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/TODO
@@ -0,0 +1,37 @@
+Dotted and dashed ellipses.
+
+In troff mode, dotted and dashed splines.
+
+Make DELIMITED have type lstr; this would allow us to give better
+error messages for problems within the body of for and if constructs.
+
+In troff mode without -x, fake \D't' with .ps commands.
+
+Perhaps an option to set command char.
+
+Add an output class for dumb line printers. It wouldn't be pretty but
+it would be better than nothing. Integrate it with texinfo. Useful
+for groff -Tascii as well.
+
+Option to allow better positioning of arrowheads on arcs.
+
+Perhaps add PostScript output mode.
+
+Change the interface to the output class so that output devices have
+the opportunity to handle arrowheads themselves.
+
+Consider whether the line thickness should scale.
+
+Consider whether the test in a for loop should be fuzzy (as it
+apparently is in grap).
+
+Possibly change fillval so that zero is black.
+
+Provide a way of getting text blocks (positioned with `.in' rather
+than \h), into pic. Should be possible to use block of diverted text
+in pic. Possibly something similar to T{ and T} in tbl.
+
+Option to provide macro backtraces.
+
+Have a path that is searched by `copy' statement. Set by environment
+variable or command line option.
diff --git a/contrib/groff/src/preproc/pic/common.cc b/contrib/groff/src/preproc/pic/common.cc
new file mode 100644
index 000000000000..e83ef3122583
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/common.cc
@@ -0,0 +1,497 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "pic.h"
+#include "common.h"
+
+// output a dashed circle as a series of arcs
+
+void common_output::dashed_circle(const position &cent, double rad,
+ const line_type &lt)
+{
+ assert(lt.type == line_type::dashed);
+ line_type slt = lt;
+ slt.type = line_type::solid;
+ double dash_angle = lt.dash_width/rad;
+ int ndashes;
+ double gap_angle;
+ if (dash_angle >= M_PI/4.0) {
+ if (dash_angle < M_PI/2.0) {
+ gap_angle = M_PI/2.0 - dash_angle;
+ ndashes = 4;
+ }
+ else if (dash_angle < M_PI) {
+ gap_angle = M_PI - dash_angle;
+ ndashes = 2;
+ }
+ else {
+ circle(cent, rad, slt, -1.0);
+ return;
+ }
+ }
+ else {
+ ndashes = 4*int(ceil(M_PI/(4.0*dash_angle)));
+ gap_angle = (M_PI*2.0)/ndashes - dash_angle;
+ }
+ for (int i = 0; i < ndashes; i++) {
+ double start_angle = i*(dash_angle+gap_angle) - dash_angle/2.0;
+ solid_arc(cent, rad, start_angle, start_angle + dash_angle, lt);
+ }
+}
+
+// output a dotted circle as a series of dots
+
+void common_output::dotted_circle(const position &cent, double rad,
+ const line_type &lt)
+{
+ assert(lt.type == line_type::dotted);
+ double gap_angle = lt.dash_width/rad;
+ int ndots;
+ if (gap_angle >= M_PI/2.0) {
+ // always have at least 2 dots
+ gap_angle = M_PI;
+ ndots = 2;
+ }
+ else {
+ ndots = 4*int(M_PI/(2.0*gap_angle));
+ gap_angle = (M_PI*2.0)/ndots;
+ }
+ double ang = 0.0;
+ for (int i = 0; i < ndots; i++, ang += gap_angle)
+ dot(cent + position(cos(ang), sin(ang))*rad, lt);
+}
+
+// return non-zero iff we can compute a center
+
+int compute_arc_center(const position &start, const position &cent,
+ const position &end, position *result)
+{
+ // This finds the point along the vector from start to cent that
+ // is equidistant between start and end.
+ distance c = cent - start;
+ distance e = end - start;
+ double n = c*e;
+ if (n == 0.0)
+ return 0;
+ *result = start + c*((e*e)/(2.0*n));
+ return 1;
+}
+
+// output a dashed arc as a series of arcs
+
+void common_output::dashed_arc(const position &start, const position &cent,
+ const position &end, const line_type &lt)
+{
+ assert(lt.type == line_type::dashed);
+ position c;
+ if (!compute_arc_center(start, cent, end, &c)) {
+ line(start, &end, 1, lt);
+ return;
+ }
+ distance start_offset = start - c;
+ distance end_offset = end - c;
+ double start_angle = atan2(start_offset.y, start_offset.x);
+ double end_angle = atan2(end_offset.y, end_offset.x);
+ double rad = hypot(c - start);
+ double dash_angle = lt.dash_width/rad;
+ double total_angle = end_angle - start_angle;
+ while (total_angle < 0)
+ total_angle += M_PI + M_PI;
+ if (total_angle <= dash_angle*2.0) {
+ solid_arc(cent, rad, start_angle, end_angle, lt);
+ return;
+ }
+ int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5);
+ double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
+ for (int i = 0; i <= ndashes; i++)
+ solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
+ start_angle + i*dash_and_gap_angle + dash_angle, lt);
+}
+
+// output a dotted arc as a series of dots
+
+void common_output::dotted_arc(const position &start, const position &cent,
+ const position &end, const line_type &lt)
+{
+ assert(lt.type == line_type::dotted);
+ position c;
+ if (!compute_arc_center(start, cent, end, &c)) {
+ line(start, &end, 1, lt);
+ return;
+ }
+ distance start_offset = start - c;
+ distance end_offset = end - c;
+ double start_angle = atan2(start_offset.y, start_offset.x);
+ double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
+ while (total_angle < 0)
+ total_angle += M_PI + M_PI;
+ double rad = hypot(c - start);
+ int ndots = int(total_angle/(lt.dash_width/rad) + .5);
+ if (ndots == 0)
+ dot(start, lt);
+ else {
+ for (int i = 0; i <= ndots; i++) {
+ double a = start_angle + (total_angle*i)/ndots;
+ dot(cent + position(cos(a), sin(a))*rad, lt);
+ }
+ }
+}
+
+void common_output::solid_arc(const position &cent, double rad,
+ double start_angle, double end_angle,
+ const line_type &lt)
+{
+ line_type slt = lt;
+ slt.type = line_type::solid;
+ arc(cent + position(cos(start_angle), sin(start_angle))*rad,
+ cent,
+ cent + position(cos(end_angle), sin(end_angle))*rad,
+ slt);
+}
+
+
+void common_output::rounded_box(const position &cent, const distance &dim,
+ double rad, const line_type &lt, double fill)
+{
+ if (fill >= 0.0)
+ filled_rounded_box(cent, dim, rad, fill);
+ switch (lt.type) {
+ case line_type::invisible:
+ break;
+ case line_type::dashed:
+ dashed_rounded_box(cent, dim, rad, lt);
+ break;
+ case line_type::dotted:
+ dotted_rounded_box(cent, dim, rad, lt);
+ break;
+ case line_type::solid:
+ solid_rounded_box(cent, dim, rad, lt);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+
+void common_output::dashed_rounded_box(const position &cent,
+ const distance &dim, double rad,
+ const line_type &lt)
+{
+ line_type slt = lt;
+ slt.type = line_type::solid;
+
+ double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
+ int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5);
+ double hor_gap_width = (n_hor_dashes != 0
+ ? hor_length/n_hor_dashes - lt.dash_width
+ : 0.0);
+
+ double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
+ int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5);
+ double vert_gap_width = (n_vert_dashes != 0
+ ? vert_length/n_vert_dashes - lt.dash_width
+ : 0.0);
+ // Note that each corner arc has to be split into two for dashing,
+ // because one part is dashed using vert_gap_width, and the other
+ // using hor_gap_width.
+ double offset = lt.dash_width/2.0;
+ dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
+ -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
+ dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
+ cent + position(dim.x/2.0, dim.y/2.0 - rad),
+ slt, lt.dash_width, vert_gap_width, &offset);
+ dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
+ 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
+
+ offset = lt.dash_width/2.0;
+ dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
+ M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
+ dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
+ cent + position(-dim.x/2.0 + rad, dim.y/2.0),
+ slt, lt.dash_width, hor_gap_width, &offset);
+ dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
+ M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
+
+ offset = lt.dash_width/2.0;
+ dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
+ 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
+ dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
+ cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
+ slt, lt.dash_width, vert_gap_width, &offset);
+ dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
+ M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
+
+ offset = lt.dash_width/2.0;
+ dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
+ 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
+ dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
+ cent + position(dim.x/2.0 - rad, -dim.y/2.0),
+ slt, lt.dash_width, hor_gap_width, &offset);
+ dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
+ 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
+}
+
+// Used by dashed_rounded_box.
+
+void common_output::dash_arc(const position &cent, double rad,
+ double start_angle, double end_angle,
+ const line_type &lt,
+ double dash_width, double gap_width,
+ double *offsetp)
+{
+ double length = (end_angle - start_angle)*rad;
+ double pos = 0.0;
+ for (;;) {
+ if (*offsetp >= dash_width) {
+ double rem = dash_width + gap_width - *offsetp;
+ if (pos + rem > length) {
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ pos += rem;
+ *offsetp = 0.0;
+ }
+ }
+ else {
+ double rem = dash_width - *offsetp;
+ if (pos + rem > length) {
+ solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ solid_arc(cent, rad, start_angle + pos/rad,
+ start_angle + (pos + rem)/rad, lt);
+ pos += rem;
+ *offsetp = dash_width;
+ }
+ }
+ }
+}
+
+// Used by dashed_rounded_box.
+
+void common_output::dash_line(const position &start, const position &end,
+ const line_type &lt,
+ double dash_width, double gap_width,
+ double *offsetp)
+{
+ distance dist = end - start;
+ double length = hypot(dist);
+ if (length == 0.0)
+ return;
+ double pos = 0.0;
+ for (;;) {
+ if (*offsetp >= dash_width) {
+ double rem = dash_width + gap_width - *offsetp;
+ if (pos + rem > length) {
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ pos += rem;
+ *offsetp = 0.0;
+ }
+ }
+ else {
+ double rem = dash_width - *offsetp;
+ if (pos + rem > length) {
+ line(start + dist*(pos/length), &end, 1, lt);
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ position p(start + dist*((pos + rem)/length));
+ line(start + dist*(pos/length), &p, 1, lt);
+ pos += rem;
+ *offsetp = dash_width;
+ }
+ }
+ }
+}
+
+void common_output::dotted_rounded_box(const position &cent,
+ const distance &dim, double rad,
+ const line_type &lt)
+{
+ line_type slt = lt;
+ slt.type = line_type::solid;
+
+ double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
+ int n_hor_dots = int(hor_length/lt.dash_width + .5);
+ double hor_gap_width = (n_hor_dots != 0
+ ? hor_length/n_hor_dots
+ : lt.dash_width);
+
+ double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
+ int n_vert_dots = int(vert_length/lt.dash_width + .5);
+ double vert_gap_width = (n_vert_dots != 0
+ ? vert_length/n_vert_dots
+ : lt.dash_width);
+ double epsilon = lt.dash_width/(rad*100.0);
+
+ double offset = 0.0;
+ dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
+ -M_PI/4.0, 0, slt, vert_gap_width, &offset);
+ dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
+ cent + position(dim.x/2.0, dim.y/2.0 - rad),
+ slt, vert_gap_width, &offset);
+ dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
+ 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
+
+ offset = 0.0;
+ dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
+ M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
+ dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
+ cent + position(-dim.x/2.0 + rad, dim.y/2.0),
+ slt, hor_gap_width, &offset);
+ dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
+ M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
+
+ offset = 0.0;
+ dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
+ 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
+ dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
+ cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
+ slt, vert_gap_width, &offset);
+ dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
+ M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
+
+ offset = 0.0;
+ dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
+ 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
+ dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
+ cent + position(dim.x/2.0 - rad, -dim.y/2.0),
+ slt, hor_gap_width, &offset);
+ dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
+ 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
+}
+
+// Used by dotted_rounded_box.
+
+void common_output::dot_arc(const position &cent, double rad,
+ double start_angle, double end_angle,
+ const line_type &lt, double gap_width,
+ double *offsetp)
+{
+ double length = (end_angle - start_angle)*rad;
+ double pos = 0.0;
+ for (;;) {
+ if (*offsetp == 0.0) {
+ double ang = start_angle + pos/rad;
+ dot(cent + position(cos(ang), sin(ang))*rad, lt);
+ }
+ double rem = gap_width - *offsetp;
+ if (pos + rem > length) {
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ pos += rem;
+ *offsetp = 0.0;
+ }
+ }
+}
+
+// Used by dotted_rounded_box.
+
+void common_output::dot_line(const position &start, const position &end,
+ const line_type &lt, double gap_width,
+ double *offsetp)
+{
+ distance dist = end - start;
+ double length = hypot(dist);
+ if (length == 0.0)
+ return;
+ double pos = 0.0;
+ for (;;) {
+ if (*offsetp == 0.0)
+ dot(start + dist*(pos/length), lt);
+ double rem = gap_width - *offsetp;
+ if (pos + rem > length) {
+ *offsetp += length - pos;
+ break;
+ }
+ else {
+ pos += rem;
+ *offsetp = 0.0;
+ }
+ }
+}
+
+
+void common_output::solid_rounded_box(const position &cent,
+ const distance &dim, double rad,
+ const line_type &lt)
+{
+ position tem = cent - dim/2.0;
+ arc(tem + position(0.0, rad),
+ tem + position(rad, rad),
+ tem + position(rad, 0.0),
+ lt);
+ tem = cent + position(-dim.x/2.0, dim.y/2.0);
+ arc(tem + position(rad, 0.0),
+ tem + position(rad, -rad),
+ tem + position(0.0, -rad),
+ lt);
+ tem = cent + dim/2.0;
+ arc(tem + position(0.0, -rad),
+ tem + position(-rad, -rad),
+ tem + position(-rad, 0.0),
+ lt);
+ tem = cent + position(dim.x/2.0, -dim.y/2.0);
+ arc(tem + position(-rad, 0.0),
+ tem + position(-rad, rad),
+ tem + position(0.0, rad),
+ lt);
+ position end;
+ end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
+ line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
+ end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
+ line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
+ end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
+ line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
+ end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
+ line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
+}
+
+void common_output::filled_rounded_box(const position &cent,
+ const distance &dim, double rad,
+ double fill)
+{
+ line_type ilt;
+ ilt.type = line_type::invisible;
+ circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
+ circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
+ circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
+ circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
+ position vec[4];
+ vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
+ vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
+ vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
+ vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
+ polygon(vec, 4, ilt, fill);
+ vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
+ vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
+ vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
+ vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
+ polygon(vec, 4, ilt, fill);
+}
diff --git a/contrib/groff/src/preproc/pic/common.h b/contrib/groff/src/preproc/pic/common.h
new file mode 100644
index 000000000000..25a6e10c689c
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/common.h
@@ -0,0 +1,70 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+class common_output : public output {
+private:
+ void dash_line(const position &start, const position &end,
+ const line_type &lt, double dash_width, double gap_width,
+ double *offsetp);
+ void dash_arc(const position &cent, double rad,
+ double start_angle, double end_angle, const line_type &lt,
+ double dash_width, double gap_width, double *offsetp);
+ void dot_line(const position &start, const position &end,
+ const line_type &lt, double gap_width, double *offsetp);
+ void dot_arc(const position &cent, double rad,
+ double start_angle, double end_angle, const line_type &lt,
+ double gap_width, double *offsetp);
+protected:
+ virtual void dot(const position &, const line_type &) = 0;
+ void dashed_circle(const position &, double rad, const line_type &);
+ void dotted_circle(const position &, double rad, const line_type &);
+ void dashed_arc(const position &, const position &, const position &,
+ const line_type &);
+ void dotted_arc(const position &, const position &, const position &,
+ const line_type &);
+ virtual void solid_arc(const position &cent, double rad, double start_angle,
+ double end_angle, const line_type &lt);
+ void dashed_rounded_box(const position &, const distance &, double,
+ const line_type &);
+ void dotted_rounded_box(const position &, const distance &, double,
+ const line_type &);
+ void solid_rounded_box(const position &, const distance &, double,
+ const line_type &);
+ void filled_rounded_box(const position &, const distance &, double, double);
+public:
+ void start_picture(double sc, const position &ll, const position &ur) = 0;
+ void finish_picture() = 0;
+ void circle(const position &, double rad, const line_type &, double) = 0;
+ void text(const position &, text_piece *, int, double) = 0;
+ void line(const position &, const position *, int n, const line_type &) = 0;
+ void polygon(const position *, int n, const line_type &, double) = 0;
+ void spline(const position &, const position *, int n,
+ const line_type &) = 0;
+ void arc(const position &, const position &, const position &,
+ const line_type &) = 0;
+ void ellipse(const position &, const distance &,
+ const line_type &, double) = 0;
+ void rounded_box(const position &, const distance &, double,
+ const line_type &, double);
+};
+
+int compute_arc_center(const position &start, const position &cent,
+ const position &end, position *result);
+
diff --git a/contrib/groff/src/preproc/pic/lex.cc b/contrib/groff/src/preproc/pic/lex.cc
new file mode 100644
index 000000000000..5b6d439a0440
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/lex.cc
@@ -0,0 +1,1940 @@
+// -*- 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. */
+
+#include "pic.h"
+#include "ptable.h"
+#include "object.h"
+#include "pic_tab.h"
+
+declare_ptable(char)
+implement_ptable(char)
+
+PTABLE(char) macro_table;
+
+class macro_input : public input {
+ char *s;
+ char *p;
+public:
+ macro_input(const char *);
+ ~macro_input();
+ int get();
+ int peek();
+};
+
+class argument_macro_input : public input {
+ char *s;
+ char *p;
+ char *ap;
+ int argc;
+ char *argv[9];
+public:
+ argument_macro_input(const char *, int, char **);
+ ~argument_macro_input();
+ int get();
+ int peek();
+};
+
+input::input() : next(0)
+{
+}
+
+input::~input()
+{
+}
+
+int input::get_location(const char **, int *)
+{
+ return 0;
+}
+
+file_input::file_input(FILE *f, const char *fn)
+: fp(f), filename(fn), lineno(0), ptr("")
+{
+}
+
+file_input::~file_input()
+{
+ fclose(fp);
+}
+
+int file_input::read_line()
+{
+ for (;;) {
+ line.clear();
+ lineno++;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ else if (illegal_input_char(c))
+ lex_error("illegal input character code %1", c);
+ else {
+ line += char(c);
+ if (c == '\n')
+ break;
+ }
+ }
+ if (line.length() == 0)
+ return 0;
+ if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
+ && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
+ && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
+ || compatible_flag))) {
+ line += '\0';
+ ptr = line.contents();
+ return 1;
+ }
+ }
+}
+
+int file_input::get()
+{
+ if (*ptr != '\0' || read_line())
+ return (unsigned char)*ptr++;
+ else
+ return EOF;
+}
+
+int file_input::peek()
+{
+ if (*ptr != '\0' || read_line())
+ return (unsigned char)*ptr;
+ else
+ return EOF;
+}
+
+int file_input::get_location(const char **fnp, int *lnp)
+{
+ *fnp = filename;
+ *lnp = lineno;
+ return 1;
+}
+
+macro_input::macro_input(const char *str)
+{
+ p = s = strsave(str);
+}
+
+macro_input::~macro_input()
+{
+ a_delete s;
+}
+
+int macro_input::get()
+{
+ if (p == 0 || *p == '\0')
+ return EOF;
+ else
+ return (unsigned char)*p++;
+}
+
+int macro_input::peek()
+{
+ if (p == 0 || *p == '\0')
+ return EOF;
+ else
+ return (unsigned char)*p;
+}
+
+// Character representing $1. Must be illegal input character.
+#define ARG1 14
+
+char *process_body(const char *body)
+{
+ char *s = strsave(body);
+ int j = 0;
+ for (int i = 0; s[i] != '\0'; i++)
+ if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
+ if (s[i+1] != '0')
+ s[j++] = ARG1 + s[++i] - '1';
+ }
+ else
+ s[j++] = s[i];
+ s[j] = '\0';
+ return s;
+}
+
+
+argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
+: ap(0), argc(ac)
+{
+ for (int i = 0; i < argc; i++)
+ argv[i] = av[i];
+ p = s = process_body(body);
+}
+
+
+argument_macro_input::~argument_macro_input()
+{
+ for (int i = 0; i < argc; i++)
+ a_delete argv[i];
+ a_delete s;
+}
+
+int argument_macro_input::get()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return (unsigned char)*ap++;
+ ap = 0;
+ }
+ if (p == 0)
+ return EOF;
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
+ ap = argv[i];
+ return (unsigned char)*ap++;
+ }
+ }
+ if (*p == '\0')
+ return EOF;
+ return (unsigned char)*p++;
+}
+
+int argument_macro_input::peek()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return (unsigned char)*ap;
+ ap = 0;
+ }
+ if (p == 0)
+ return EOF;
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
+ ap = argv[i];
+ return (unsigned char)*ap;
+ }
+ }
+ if (*p == '\0')
+ return EOF;
+ return (unsigned char)*p;
+}
+
+class input_stack {
+ static input *current_input;
+ static int bol_flag;
+public:
+ static void push(input *);
+ static void clear();
+ static int get_char();
+ static int peek_char();
+ static int get_location(const char **fnp, int *lnp);
+ static void push_back(unsigned char c, int was_bol = 0);
+ static int bol();
+};
+
+input *input_stack::current_input = 0;
+int input_stack::bol_flag = 0;
+
+inline int input_stack::bol()
+{
+ return bol_flag;
+}
+
+void input_stack::clear()
+{
+ while (current_input != 0) {
+ input *tem = current_input;
+ current_input = current_input->next;
+ delete tem;
+ }
+ bol_flag = 1;
+}
+
+void input_stack::push(input *in)
+{
+ in->next = current_input;
+ current_input = in;
+}
+
+void lex_init(input *top)
+{
+ input_stack::clear();
+ input_stack::push(top);
+}
+
+void lex_cleanup()
+{
+ while (input_stack::get_char() != EOF)
+ ;
+}
+
+int input_stack::get_char()
+{
+ while (current_input != 0) {
+ int c = current_input->get();
+ if (c != EOF) {
+ bol_flag = c == '\n';
+ return c;
+ }
+ // don't pop the top-level input off the stack
+ if (current_input->next == 0)
+ return EOF;
+ input *tem = current_input;
+ current_input = current_input->next;
+ delete tem;
+ }
+ return EOF;
+}
+
+int input_stack::peek_char()
+{
+ while (current_input != 0) {
+ int c = current_input->peek();
+ if (c != EOF)
+ return c;
+ if (current_input->next == 0)
+ return EOF;
+ input *tem = current_input;
+ current_input = current_input->next;
+ delete tem;
+ }
+ return EOF;
+}
+
+class char_input : public input {
+ int c;
+public:
+ char_input(int);
+ int get();
+ int peek();
+};
+
+char_input::char_input(int n) : c((unsigned char)n)
+{
+}
+
+int char_input::get()
+{
+ int n = c;
+ c = EOF;
+ return n;
+}
+
+int char_input::peek()
+{
+ return c;
+}
+
+void input_stack::push_back(unsigned char c, int was_bol)
+{
+ push(new char_input(c));
+ bol_flag = was_bol;
+}
+
+int input_stack::get_location(const char **fnp, int *lnp)
+{
+ for (input *p = current_input; p; p = p->next)
+ if (p->get_location(fnp, lnp))
+ return 1;
+ return 0;
+}
+
+string context_buffer;
+
+string token_buffer;
+double token_double;
+int token_int;
+
+void interpolate_macro_with_args(const char *body)
+{
+ char *argv[9];
+ int argc = 0;
+ int i;
+ for (i = 0; i < 9; i++)
+ argv[i] = 0;
+ int level = 0;
+ int c;
+ enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
+ do {
+ token_buffer.clear();
+ for (;;) {
+ c = input_stack::get_char();
+ if (c == EOF) {
+ lex_error("end of input while scanning macro arguments");
+ break;
+ }
+ if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
+ if (token_buffer.length() > 0) {
+ token_buffer += '\0';
+ argv[argc] = strsave(token_buffer.contents());
+ }
+ // for `foo()', argc = 0
+ if (argc > 0 || c != ')' || i > 0)
+ argc++;
+ break;
+ }
+ token_buffer += char(c);
+ switch (state) {
+ case NORMAL:
+ if (c == '"')
+ state = IN_STRING;
+ else if (c == '(')
+ level++;
+ else if (c == ')')
+ level--;
+ break;
+ case IN_STRING:
+ if (c == '"')
+ state = NORMAL;
+ else if (c == '\\')
+ state = IN_STRING_QUOTED;
+ break;
+ case IN_STRING_QUOTED:
+ state = IN_STRING;
+ break;
+ }
+ }
+ } while (c != ')' && c != EOF);
+ input_stack::push(new argument_macro_input(body, argc, argv));
+}
+
+static int docmp(const char *s1, int n1, const char *s2, int n2)
+{
+ if (n1 < n2) {
+ int r = memcmp(s1, s2, n1);
+ return r ? r : -1;
+ }
+ else if (n1 > n2) {
+ int r = memcmp(s1, s2, n2);
+ return r ? r : 1;
+ }
+ else
+ return memcmp(s1, s2, n1);
+}
+
+int lookup_keyword(const char *str, int len)
+{
+ static struct keyword {
+ const char *name;
+ int token;
+ } table[] = {
+ { "Here", HERE },
+ { "above", ABOVE },
+ { "aligned", ALIGNED },
+ { "and", AND },
+ { "arc", ARC },
+ { "arrow", ARROW },
+ { "at", AT },
+ { "atan2", ATAN2 },
+ { "below", BELOW },
+ { "between", BETWEEN },
+ { "bottom", BOTTOM },
+ { "box", BOX },
+ { "by", BY },
+ { "ccw", CCW },
+ { "center", CENTER },
+ { "chop", CHOP },
+ { "circle", CIRCLE },
+ { "command", COMMAND },
+ { "copy", COPY },
+ { "cos", COS },
+ { "cw", CW },
+ { "dashed", DASHED },
+ { "define", DEFINE },
+ { "diam", DIAMETER },
+ { "diameter", DIAMETER },
+ { "do", DO },
+ { "dotted", DOTTED },
+ { "down", DOWN },
+ { "ellipse", ELLIPSE },
+ { "else", ELSE },
+ { "end", END },
+ { "exp", EXP },
+ { "fill", FILL },
+ { "filled", FILL },
+ { "for", FOR },
+ { "from", FROM },
+ { "height", HEIGHT },
+ { "ht", HEIGHT },
+ { "if", IF },
+ { "int", INT },
+ { "invis", INVISIBLE },
+ { "invisible", INVISIBLE },
+ { "last", LAST },
+ { "left", LEFT },
+ { "line", LINE },
+ { "ljust", LJUST },
+ { "log", LOG },
+ { "lower", LOWER },
+ { "max", K_MAX },
+ { "min", K_MIN },
+ { "move", MOVE },
+ { "of", OF },
+ { "plot", PLOT },
+ { "print", PRINT },
+ { "rad", RADIUS },
+ { "radius", RADIUS },
+ { "rand", RAND },
+ { "reset", RESET },
+ { "right", RIGHT },
+ { "rjust", RJUST },
+ { "same", SAME },
+ { "sh", SH },
+ { "sin", SIN },
+ { "solid", SOLID },
+ { "spline", SPLINE },
+ { "sprintf", SPRINTF },
+ { "sqrt", SQRT },
+ { "srand", SRAND },
+ { "start", START },
+ { "the", THE },
+ { "then", THEN },
+ { "thick", THICKNESS },
+ { "thickness", THICKNESS },
+ { "thru", THRU },
+ { "to", TO },
+ { "top", TOP },
+ { "undef", UNDEF },
+ { "until", UNTIL },
+ { "up", UP },
+ { "upper", UPPER },
+ { "way", WAY },
+ { "wid", WIDTH },
+ { "width", WIDTH },
+ { "with", WITH },
+ };
+
+ const keyword *start = table;
+ const keyword *end = table + sizeof(table)/sizeof(table[0]);
+ while (start < end) {
+ // start <= target < end
+ const keyword *mid = start + (end - start)/2;
+
+ int cmp = docmp(str, len, mid->name, strlen(mid->name));
+ if (cmp == 0)
+ return mid->token;
+ if (cmp < 0)
+ end = mid;
+ else
+ start = mid + 1;
+ }
+ return 0;
+}
+
+int get_token_after_dot(int c)
+{
+ // get_token deals with the case where c is a digit
+ switch (c) {
+ case 'h':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ context_buffer = ".ht";
+ return DOT_HT;
+ }
+ else if (c == 'e') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'i') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'g') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'h') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ context_buffer = ".height";
+ return DOT_HT;
+ }
+ input_stack::push_back('h');
+ }
+ input_stack::push_back('g');
+ }
+ input_stack::push_back('i');
+ }
+ input_stack::push_back('e');
+ }
+ input_stack::push_back('h');
+ return '.';
+ case 'x':
+ input_stack::get_char();
+ context_buffer = ".x";
+ return DOT_X;
+ case 'y':
+ input_stack::get_char();
+ context_buffer = ".y";
+ return DOT_Y;
+ case 'c':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'e') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'n') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'e') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'r') {
+ input_stack::get_char();
+ context_buffer = ".center";
+ return DOT_C;
+ }
+ input_stack::push_back('e');
+ }
+ input_stack::push_back('t');
+ }
+ input_stack::push_back('n');
+ }
+ input_stack::push_back('e');
+ }
+ context_buffer = ".c";
+ return DOT_C;
+ case 'n':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'e') {
+ input_stack::get_char();
+ context_buffer = ".ne";
+ return DOT_NE;
+ }
+ else if (c == 'w') {
+ input_stack::get_char();
+ context_buffer = ".nw";
+ return DOT_NW;
+ }
+ else {
+ context_buffer = ".n";
+ return DOT_N;
+ }
+ break;
+ case 'e':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'n') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'd') {
+ input_stack::get_char();
+ context_buffer = ".end";
+ return DOT_END;
+ }
+ input_stack::push_back('n');
+ context_buffer = ".e";
+ return DOT_E;
+ }
+ context_buffer = ".e";
+ return DOT_E;
+ case 'w':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'i') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'd') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'h') {
+ input_stack::get_char();
+ context_buffer = ".width";
+ return DOT_WID;
+ }
+ input_stack::push_back('t');
+ }
+ context_buffer = ".wid";
+ return DOT_WID;
+ }
+ input_stack::push_back('i');
+ }
+ context_buffer = ".w";
+ return DOT_W;
+ case 's':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'e') {
+ input_stack::get_char();
+ context_buffer = ".se";
+ return DOT_SE;
+ }
+ else if (c == 'w') {
+ input_stack::get_char();
+ context_buffer = ".sw";
+ return DOT_SW;
+ }
+ else {
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'a') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'r') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ context_buffer = ".start";
+ return DOT_START;
+ }
+ input_stack::push_back('r');
+ }
+ input_stack::push_back('a');
+ }
+ input_stack::push_back('t');
+ }
+ context_buffer = ".s";
+ return DOT_S;
+ }
+ break;
+ case 't':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'o') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'p') {
+ input_stack::get_char();
+ context_buffer = ".top";
+ return DOT_N;
+ }
+ input_stack::push_back('o');
+ }
+ context_buffer = ".t";
+ return DOT_N;
+ case 'l':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'e') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'f') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ context_buffer = ".left";
+ return DOT_W;
+ }
+ input_stack::push_back('f');
+ }
+ input_stack::push_back('e');
+ }
+ context_buffer = ".l";
+ return DOT_W;
+ case 'r':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'a') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'd') {
+ input_stack::get_char();
+ context_buffer = ".rad";
+ return DOT_RAD;
+ }
+ input_stack::push_back('a');
+ }
+ else if (c == 'i') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'g') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'h') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ context_buffer = ".right";
+ return DOT_E;
+ }
+ input_stack::push_back('h');
+ }
+ input_stack::push_back('g');
+ }
+ input_stack::push_back('i');
+ }
+ context_buffer = ".r";
+ return DOT_E;
+ case 'b':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'o') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'o') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'm') {
+ input_stack::get_char();
+ context_buffer = ".bottom";
+ return DOT_S;
+ }
+ input_stack::push_back('o');
+ }
+ input_stack::push_back('t');
+ }
+ context_buffer = ".bot";
+ return DOT_S;
+ }
+ input_stack::push_back('o');
+ }
+ context_buffer = ".b";
+ return DOT_S;
+ default:
+ context_buffer = '.';
+ return '.';
+ }
+}
+
+int get_token(int lookup_flag)
+{
+ context_buffer.clear();
+ for (;;) {
+ int n = 0;
+ int bol = input_stack::bol();
+ int c = input_stack::get_char();
+ if (bol && c == command_char) {
+ token_buffer.clear();
+ token_buffer += c;
+ // the newline is not part of the token
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == EOF || c == '\n')
+ break;
+ input_stack::get_char();
+ token_buffer += char(c);
+ }
+ context_buffer = token_buffer;
+ return COMMAND_LINE;
+ }
+ switch (c) {
+ case EOF:
+ return EOF;
+ case ' ':
+ case '\t':
+ break;
+ case '\\':
+ {
+ int d = input_stack::peek_char();
+ if (d != '\n') {
+ context_buffer = '\\';
+ return '\\';
+ }
+ input_stack::get_char();
+ break;
+ }
+ case '#':
+ do {
+ c = input_stack::get_char();
+ } while (c != '\n' && c != EOF);
+ if (c == '\n')
+ context_buffer = '\n';
+ return c;
+ case '"':
+ context_buffer = '"';
+ token_buffer.clear();
+ for (;;) {
+ c = input_stack::get_char();
+ if (c == '\\') {
+ context_buffer += '\\';
+ c = input_stack::peek_char();
+ if (c == '"') {
+ input_stack::get_char();
+ token_buffer += '"';
+ context_buffer += '"';
+ }
+ else
+ token_buffer += '\\';
+ }
+ else if (c == '\n') {
+ error("newline in string");
+ break;
+ }
+ else if (c == EOF) {
+ error("missing `\"'");
+ break;
+ }
+ else if (c == '"') {
+ context_buffer += '"';
+ break;
+ }
+ else {
+ context_buffer += char(c);
+ token_buffer += char(c);
+ }
+ }
+ return TEXT;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int overflow = 0;
+ n = 0;
+ for (;;) {
+ if (n > (INT_MAX - 9)/10) {
+ overflow = 1;
+ break;
+ }
+ n *= 10;
+ n += c - '0';
+ context_buffer += char(c);
+ c = input_stack::peek_char();
+ if (c == EOF || !csdigit(c))
+ break;
+ c = input_stack::get_char();
+ }
+ token_double = n;
+ if (overflow) {
+ for (;;) {
+ token_double *= 10.0;
+ token_double += c - '0';
+ context_buffer += char(c);
+ c = input_stack::peek_char();
+ if (c == EOF || !csdigit(c))
+ break;
+ c = input_stack::get_char();
+ }
+ // if somebody asks for 1000000000000th, we will silently
+ // give them INT_MAXth
+ double temp = token_double; // work around gas 1.34/sparc bug
+ if (token_double > INT_MAX)
+ n = INT_MAX;
+ else
+ n = int(temp);
+ }
+ }
+ switch (c) {
+ case 'i':
+ case 'I':
+ context_buffer += char(c);
+ input_stack::get_char();
+ return NUMBER;
+ case '.':
+ {
+ context_buffer += '.';
+ input_stack::get_char();
+ got_dot:
+ double factor = 1.0;
+ for (;;) {
+ c = input_stack::peek_char();
+ if (!c == EOF || !csdigit(c))
+ break;
+ input_stack::get_char();
+ context_buffer += char(c);
+ factor /= 10.0;
+ if (c != '0')
+ token_double += factor*(c - '0');
+ }
+ if (c != 'e' && c != 'E') {
+ if (c == 'i' || c == 'I') {
+ context_buffer += char(c);
+ input_stack::get_char();
+ }
+ return NUMBER;
+ }
+ }
+ // fall through
+ case 'e':
+ case 'E':
+ {
+ int echar = c;
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ int sign = '+';
+ if (c == '+' || c == '-') {
+ sign = c;
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == EOF || !csdigit(c)) {
+ input_stack::push_back(sign);
+ input_stack::push_back(echar);
+ return NUMBER;
+ }
+ context_buffer += char(echar);
+ context_buffer += char(sign);
+ }
+ else {
+ if (c == EOF || !csdigit(c)) {
+ input_stack::push_back(echar);
+ return NUMBER;
+ }
+ context_buffer += char(echar);
+ }
+ input_stack::get_char();
+ context_buffer += char(c);
+ n = c - '0';
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == EOF || !csdigit(c))
+ break;
+ input_stack::get_char();
+ context_buffer += char(c);
+ n = n*10 + (c - '0');
+ }
+ if (sign == '-')
+ n = -n;
+ if (c == 'i' || c == 'I') {
+ context_buffer += char(c);
+ input_stack::get_char();
+ }
+ token_double *= pow(10.0, n);
+ return NUMBER;
+ }
+ case 'n':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'd') {
+ input_stack::get_char();
+ token_int = n;
+ context_buffer += "nd";
+ return ORDINAL;
+ }
+ input_stack::push_back('n');
+ return NUMBER;
+ case 'r':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'd') {
+ input_stack::get_char();
+ token_int = n;
+ context_buffer += "rd";
+ return ORDINAL;
+ }
+ input_stack::push_back('r');
+ return NUMBER;
+ case 't':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'h') {
+ input_stack::get_char();
+ token_int = n;
+ context_buffer += "th";
+ return ORDINAL;
+ }
+ input_stack::push_back('t');
+ return NUMBER;
+ case 's':
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ token_int = n;
+ context_buffer += "st";
+ return ORDINAL;
+ }
+ input_stack::push_back('s');
+ return NUMBER;
+ default:
+ return NUMBER;
+ }
+ break;
+ case '\'':
+ {
+ c = input_stack::peek_char();
+ if (c == 't') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == 'h') {
+ input_stack::get_char();
+ context_buffer = "'th";
+ return TH;
+ }
+ else
+ input_stack::push_back('t');
+ }
+ context_buffer = "'";
+ return '\'';
+ }
+ case '.':
+ {
+ c = input_stack::peek_char();
+ if (c != EOF && csdigit(c)) {
+ n = 0;
+ token_double = 0.0;
+ context_buffer = '.';
+ goto got_dot;
+ }
+ return get_token_after_dot(c);
+ }
+ case '<':
+ c = input_stack::peek_char();
+ if (c == '-') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ if (c == '>') {
+ input_stack::get_char();
+ context_buffer = "<->";
+ return DOUBLE_ARROW_HEAD;
+ }
+ context_buffer = "<-";
+ return LEFT_ARROW_HEAD;
+ }
+ else if (c == '=') {
+ input_stack::get_char();
+ context_buffer = "<=";
+ return LESSEQUAL;
+ }
+ context_buffer = "<";
+ return '<';
+ case '-':
+ c = input_stack::peek_char();
+ if (c == '>') {
+ input_stack::get_char();
+ context_buffer = "->";
+ return RIGHT_ARROW_HEAD;
+ }
+ context_buffer = "-";
+ return '-';
+ case '!':
+ c = input_stack::peek_char();
+ if (c == '=') {
+ input_stack::get_char();
+ context_buffer = "!=";
+ return NOTEQUAL;
+ }
+ context_buffer = "!";
+ return '!';
+ case '>':
+ c = input_stack::peek_char();
+ if (c == '=') {
+ input_stack::get_char();
+ context_buffer = ">=";
+ return GREATEREQUAL;
+ }
+ context_buffer = ">";
+ return '>';
+ case '=':
+ c = input_stack::peek_char();
+ if (c == '=') {
+ input_stack::get_char();
+ context_buffer = "==";
+ return EQUALEQUAL;
+ }
+ context_buffer = "=";
+ return '=';
+ case '&':
+ c = input_stack::peek_char();
+ if (c == '&') {
+ input_stack::get_char();
+ context_buffer = "&&";
+ return ANDAND;
+ }
+ context_buffer = "&";
+ return '&';
+ case '|':
+ c = input_stack::peek_char();
+ if (c == '|') {
+ input_stack::get_char();
+ context_buffer = "||";
+ return OROR;
+ }
+ context_buffer = "|";
+ return '|';
+ default:
+ if (c != EOF && csalpha(c)) {
+ token_buffer.clear();
+ token_buffer = c;
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == EOF || (!csalnum(c) && c != '_'))
+ break;
+ input_stack::get_char();
+ token_buffer += char(c);
+ }
+ int tok = lookup_keyword(token_buffer.contents(),
+ token_buffer.length());
+ if (tok != 0) {
+ context_buffer = token_buffer;
+ return tok;
+ }
+ char *def = 0;
+ if (lookup_flag) {
+ token_buffer += '\0';
+ def = macro_table.lookup(token_buffer.contents());
+ token_buffer.set_length(token_buffer.length() - 1);
+ if (def) {
+ if (c == '(') {
+ input_stack::get_char();
+ interpolate_macro_with_args(def);
+ }
+ else
+ input_stack::push(new macro_input(def));
+ }
+ }
+ if (!def) {
+ context_buffer = token_buffer;
+ if (csupper(token_buffer[0]))
+ return LABEL;
+ else
+ return VARIABLE;
+ }
+ }
+ else {
+ context_buffer = char(c);
+ return (unsigned char)c;
+ }
+ break;
+ }
+ }
+}
+
+int get_delimited()
+{
+ token_buffer.clear();
+ int c = input_stack::get_char();
+ while (c == ' ' || c == '\t' || c == '\n')
+ c = input_stack::get_char();
+ if (c == EOF) {
+ lex_error("missing delimiter");
+ return 0;
+ }
+ context_buffer = char(c);
+ int had_newline = 0;
+ int start = c;
+ int level = 0;
+ enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
+ for (;;) {
+ c = input_stack::get_char();
+ if (c == EOF) {
+ lex_error("missing closing delimiter");
+ return 0;
+ }
+ if (c == '\n')
+ had_newline = 1;
+ else if (!had_newline)
+ context_buffer += char(c);
+ switch (state) {
+ case NORMAL:
+ if (start == '{') {
+ if (c == '{') {
+ level++;
+ break;
+ }
+ if (c == '}') {
+ if (--level < 0)
+ state = DELIM_END;
+ break;
+ }
+ }
+ else {
+ if (c == start) {
+ state = DELIM_END;
+ break;
+ }
+ }
+ if (c == '"')
+ state = IN_STRING;
+ break;
+ case IN_STRING_QUOTED:
+ if (c == '\n')
+ state = NORMAL;
+ else
+ state = IN_STRING;
+ break;
+ case IN_STRING:
+ if (c == '"' || c == '\n')
+ state = NORMAL;
+ else if (c == '\\')
+ state = IN_STRING_QUOTED;
+ break;
+ case DELIM_END:
+ // This case it just to shut cfront 2.0 up.
+ default:
+ assert(0);
+ }
+ if (state == DELIM_END)
+ break;
+ token_buffer += c;
+ }
+ return 1;
+}
+
+void do_define()
+{
+ int t = get_token(0); // do not expand what we are defining
+ if (t != VARIABLE && t != LABEL) {
+ lex_error("can only define variable or placename");
+ return;
+ }
+ token_buffer += '\0';
+ string nm = token_buffer;
+ const char *name = nm.contents();
+ if (!get_delimited())
+ return;
+ token_buffer += '\0';
+ macro_table.define(name, strsave(token_buffer.contents()));
+}
+
+void do_undef()
+{
+ int t = get_token(0); // do not expand what we are undefining
+ if (t != VARIABLE && t != LABEL) {
+ lex_error("can only define variable or placename");
+ return;
+ }
+ token_buffer += '\0';
+ macro_table.define(token_buffer.contents(), 0);
+}
+
+
+class for_input : public input {
+ char *var;
+ char *body;
+ double to;
+ int by_is_multiplicative;
+ double by;
+ const char *p;
+ int done_newline;
+public:
+ for_input(char *, double, int, double, char *);
+ ~for_input();
+ int get();
+ int peek();
+};
+
+for_input::for_input(char *vr, double t, int bim, double b, char *bd)
+: var(vr), body(bd), to(t), by_is_multiplicative(bim), by(b), p(body),
+ done_newline(0)
+{
+}
+
+for_input::~for_input()
+{
+ a_delete var;
+ a_delete body;
+}
+
+int for_input::get()
+{
+ if (p == 0)
+ return EOF;
+ for (;;) {
+ if (*p != '\0')
+ return (unsigned char)*p++;
+ if (!done_newline) {
+ done_newline = 1;
+ return '\n';
+ }
+ double val;
+ if (!lookup_variable(var, &val)) {
+ lex_error("body of `for' terminated enclosing block");
+ return EOF;
+ }
+ if (by_is_multiplicative)
+ val *= by;
+ else
+ val += by;
+ define_variable(var, val);
+ if (val > to) {
+ p = 0;
+ return EOF;
+ }
+ p = body;
+ done_newline = 0;
+ }
+}
+
+int for_input::peek()
+{
+ if (p == 0)
+ return EOF;
+ if (*p != '\0')
+ return (unsigned char)*p;
+ if (!done_newline)
+ return '\n';
+ double val;
+ if (!lookup_variable(var, &val))
+ return EOF;
+ if (by_is_multiplicative) {
+ if (val * by > to)
+ return EOF;
+ }
+ else {
+ if (val + by > to)
+ return EOF;
+ }
+ if (*body == '\0')
+ return EOF;
+ return (unsigned char)*body;
+}
+
+void do_for(char *var, double from, double to, int by_is_multiplicative,
+ double by, char *body)
+{
+ define_variable(var, from);
+ if (from <= to)
+ input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
+}
+
+
+void do_copy(const char *filename)
+{
+ errno = 0;
+ FILE *fp = fopen(filename, "r");
+ if (fp == 0) {
+ lex_error("can't open `%1': %2", filename, strerror(errno));
+ return;
+ }
+ input_stack::push(new file_input(fp, filename));
+}
+
+class copy_thru_input : public input {
+ int done;
+ char *body;
+ char *until;
+ const char *p;
+ const char *ap;
+ int argv[9];
+ int argc;
+ string line;
+ int get_line();
+ virtual int inget() = 0;
+public:
+ copy_thru_input(const char *b, const char *u);
+ ~copy_thru_input();
+ int get();
+ int peek();
+};
+
+class copy_file_thru_input : public copy_thru_input {
+ input *in;
+public:
+ copy_file_thru_input(input *, const char *b, const char *u);
+ ~copy_file_thru_input();
+ int inget();
+};
+
+copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
+ const char *u)
+: copy_thru_input(b, u), in(i)
+{
+}
+
+copy_file_thru_input::~copy_file_thru_input()
+{
+ delete in;
+}
+
+int copy_file_thru_input::inget()
+{
+ if (!in)
+ return EOF;
+ else
+ return in->get();
+}
+
+class copy_rest_thru_input : public copy_thru_input {
+public:
+ copy_rest_thru_input(const char *, const char *u);
+ int inget();
+};
+
+copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
+: copy_thru_input(b, u)
+{
+}
+
+int copy_rest_thru_input::inget()
+{
+ while (next != 0) {
+ int c = next->get();
+ if (c != EOF)
+ return c;
+ if (next->next == 0)
+ return EOF;
+ input *tem = next;
+ next = next->next;
+ delete tem;
+ }
+ return EOF;
+
+}
+
+copy_thru_input::copy_thru_input(const char *b, const char *u)
+: done(0)
+{
+ ap = 0;
+ body = process_body(b);
+ p = 0;
+ until = strsave(u);
+}
+
+
+copy_thru_input::~copy_thru_input()
+{
+ a_delete body;
+ a_delete until;
+}
+
+int copy_thru_input::get()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return (unsigned char)*ap++;
+ ap = 0;
+ }
+ for (;;) {
+ if (p == 0) {
+ if (!get_line())
+ break;
+ p = body;
+ }
+ if (*p == '\0') {
+ p = 0;
+ return '\n';
+ }
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && line[argv[i]] != '\0') {
+ ap = line.contents() + argv[i];
+ return (unsigned char)*ap++;
+ }
+ }
+ if (*p != '\0')
+ return (unsigned char)*p++;
+ }
+ return EOF;
+}
+
+int copy_thru_input::peek()
+{
+ if (ap) {
+ if (*ap != '\0')
+ return (unsigned char)*ap;
+ ap = 0;
+ }
+ for (;;) {
+ if (p == 0) {
+ if (!get_line())
+ break;
+ p = body;
+ }
+ if (*p == '\0')
+ return '\n';
+ while (*p >= ARG1 && *p <= ARG1 + 8) {
+ int i = *p++ - ARG1;
+ if (i < argc && line[argv[i]] != '\0') {
+ ap = line.contents() + argv[i];
+ return (unsigned char)*ap;
+ }
+ }
+ if (*p != '\0')
+ return (unsigned char)*p;
+ }
+ return EOF;
+}
+
+int copy_thru_input::get_line()
+{
+ if (done)
+ return 0;
+ line.clear();
+ argc = 0;
+ int c = inget();
+ for (;;) {
+ while (c == ' ')
+ c = inget();
+ if (c == EOF || c == '\n')
+ break;
+ if (argc == 9) {
+ do {
+ c = inget();
+ } while (c != '\n' && c != EOF);
+ break;
+ }
+ argv[argc++] = line.length();
+ do {
+ line += char(c);
+ c = inget();
+ } while (c != ' ' && c != '\n');
+ line += '\0';
+ }
+ if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
+ done = 1;
+ return 0;
+ }
+ return argc > 0 || c == '\n';
+}
+
+class simple_file_input : public input {
+ const char *filename;
+ int lineno;
+ FILE *fp;
+public:
+ simple_file_input(FILE *, const char *);
+ ~simple_file_input();
+ int get();
+ int peek();
+ int get_location(const char **, int *);
+};
+
+simple_file_input::simple_file_input(FILE *p, const char *s)
+: filename(s), lineno(1), fp(p)
+{
+}
+
+simple_file_input::~simple_file_input()
+{
+ // don't delete the filename
+ fclose(fp);
+}
+
+int simple_file_input::get()
+{
+ int c = getc(fp);
+ while (illegal_input_char(c)) {
+ error("illegal input character code %1", c);
+ c = getc(fp);
+ }
+ if (c == '\n')
+ lineno++;
+ return c;
+}
+
+int simple_file_input::peek()
+{
+ int c = getc(fp);
+ while (illegal_input_char(c)) {
+ error("illegal input character code %1", c);
+ c = getc(fp);
+ }
+ if (c != EOF)
+ ungetc(c, fp);
+ return c;
+}
+
+int simple_file_input::get_location(const char **fnp, int *lnp)
+{
+ *fnp = filename;
+ *lnp = lineno;
+ return 1;
+}
+
+
+void copy_file_thru(const char *filename, const char *body, const char *until)
+{
+ errno = 0;
+ FILE *fp = fopen(filename, "r");
+ if (fp == 0) {
+ lex_error("can't open `%1': %2", filename, strerror(errno));
+ return;
+ }
+ input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
+ body, until);
+ input_stack::push(in);
+}
+
+void copy_rest_thru(const char *body, const char *until)
+{
+ input_stack::push(new copy_rest_thru_input(body, until));
+}
+
+void push_body(const char *s)
+{
+ input_stack::push(new char_input('\n'));
+ input_stack::push(new macro_input(s));
+}
+
+int delim_flag = 0;
+
+char *get_thru_arg()
+{
+ int c = input_stack::peek_char();
+ while (c == ' ') {
+ input_stack::get_char();
+ c = input_stack::peek_char();
+ }
+ if (c != EOF && csalpha(c)) {
+ // looks like a macro
+ input_stack::get_char();
+ token_buffer = c;
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == EOF || (!csalnum(c) && c != '_'))
+ break;
+ input_stack::get_char();
+ token_buffer += char(c);
+ }
+ context_buffer = token_buffer;
+ token_buffer += '\0';
+ char *def = macro_table.lookup(token_buffer.contents());
+ if (def)
+ return strsave(def);
+ // I guess it wasn't a macro after all; so push the macro name back.
+ // -2 because we added a '\0'
+ for (int i = token_buffer.length() - 2; i >= 0; i--)
+ input_stack::push_back(token_buffer[i]);
+ }
+ if (get_delimited()) {
+ token_buffer += '\0';
+ return strsave(token_buffer.contents());
+ }
+ else
+ return 0;
+}
+
+int lookahead_token = -1;
+string old_context_buffer;
+
+void do_lookahead()
+{
+ if (lookahead_token == -1) {
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ }
+}
+
+int yylex()
+{
+ if (delim_flag) {
+ assert(lookahead_token == -1);
+ if (delim_flag == 2) {
+ if ((yylval.str = get_thru_arg()) != 0)
+ return DELIMITED;
+ else
+ return 0;
+ }
+ else {
+ if (get_delimited()) {
+ token_buffer += '\0';
+ yylval.str = strsave(token_buffer.contents());
+ return DELIMITED;
+ }
+ else
+ return 0;
+ }
+ }
+ for (;;) {
+ int t;
+ if (lookahead_token >= 0) {
+ t = lookahead_token;
+ lookahead_token = -1;
+ }
+ else
+ t = get_token(1);
+ switch (t) {
+ case '\n':
+ return ';';
+ case EOF:
+ return 0;
+ case DEFINE:
+ do_define();
+ break;
+ case UNDEF:
+ do_undef();
+ break;
+ case ORDINAL:
+ yylval.n = token_int;
+ return t;
+ case NUMBER:
+ yylval.x = token_double;
+ return t;
+ case COMMAND_LINE:
+ case TEXT:
+ token_buffer += '\0';
+ if (!input_stack::get_location(&yylval.lstr.filename,
+ &yylval.lstr.lineno)) {
+ yylval.lstr.filename = 0;
+ yylval.lstr.lineno = -1;
+ }
+ yylval.lstr.str = strsave(token_buffer.contents());
+ return t;
+ case LABEL:
+ case VARIABLE:
+ token_buffer += '\0';
+ yylval.str = strsave(token_buffer.contents());
+ return t;
+ case LEFT:
+ // change LEFT to LEFT_CORNER when followed by OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token == OF)
+ return LEFT_CORNER;
+ else
+ return t;
+ case RIGHT:
+ // change RIGHT to RIGHT_CORNER when followed by OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token == OF)
+ return RIGHT_CORNER;
+ else
+ return t;
+ case UPPER:
+ // recognise UPPER only before LEFT or RIGHT
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != LEFT && lookahead_token != RIGHT) {
+ yylval.str = strsave("upper");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case LOWER:
+ // recognise LOWER only before LEFT or RIGHT
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != LEFT && lookahead_token != RIGHT) {
+ yylval.str = strsave("lower");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case TOP:
+ // recognise TOP only before OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != OF) {
+ yylval.str = strsave("top");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case BOTTOM:
+ // recognise BOTTOM only before OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != OF) {
+ yylval.str = strsave("bottom");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case CENTER:
+ // recognise CENTER only before OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != OF) {
+ yylval.str = strsave("center");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case START:
+ // recognise START only before OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != OF) {
+ yylval.str = strsave("start");
+ return VARIABLE;
+ }
+ else
+ return t;
+ case END:
+ // recognise END only before OF
+ old_context_buffer = context_buffer;
+ lookahead_token = get_token(1);
+ if (lookahead_token != OF) {
+ yylval.str = strsave("end");
+ return VARIABLE;
+ }
+ else
+ return t;
+ default:
+ return t;
+ }
+ }
+}
+
+void lex_error(const char *message,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ const char *filename;
+ int lineno;
+ if (!input_stack::get_location(&filename, &lineno))
+ error(message, arg1, arg2, arg3);
+ else
+ error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
+}
+
+void lex_warning(const char *message,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ const char *filename;
+ int lineno;
+ if (!input_stack::get_location(&filename, &lineno))
+ warning(message, arg1, arg2, arg3);
+ else
+ warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
+}
+
+void yyerror(const char *s)
+{
+ const char *filename;
+ int lineno;
+ const char *context = 0;
+ if (lookahead_token == -1) {
+ if (context_buffer.length() > 0) {
+ context_buffer += '\0';
+ context = context_buffer.contents();
+ }
+ }
+ else {
+ if (old_context_buffer.length() > 0) {
+ old_context_buffer += '\0';
+ context = old_context_buffer.contents();
+ }
+ }
+ if (!input_stack::get_location(&filename, &lineno)) {
+ if (context) {
+ if (context[0] == '\n' && context[1] == '\0')
+ error("%1 before newline", s);
+ else
+ error("%1 before `%2'", s, context);
+ }
+ else
+ error("%1 at end of picture", s);
+ }
+ else {
+ if (context) {
+ if (context[0] == '\n' && context[1] == '\0')
+ error_with_file_and_line(filename, lineno, "%1 before newline", s);
+ else
+ error_with_file_and_line(filename, lineno, "%1 before `%2'",
+ s, context);
+ }
+ else
+ error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
+ }
+}
+
diff --git a/contrib/groff/src/preproc/pic/main.cc b/contrib/groff/src/preproc/pic/main.cc
new file mode 100644
index 000000000000..87d2b9300074
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/main.cc
@@ -0,0 +1,635 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 "pic.h"
+
+extern int yyparse();
+
+output *out;
+
+int flyback_flag;
+int zero_length_line_flag = 0;
+// Non-zero means we're using a groff driver.
+int driver_extension_flag = 1;
+int compatible_flag = 0;
+int safer_flag = 1;
+int command_char = '.'; // the character that introduces lines
+ // that should be passed through tranparently
+static int lf_flag = 1; // non-zero if we should attempt to understand
+ // lines beginning with `.lf'
+
+// Non-zero means a parse error was encountered.
+static int had_parse_error = 0;
+
+void do_file(const char *filename);
+
+class top_input : public input {
+ FILE *fp;
+ int bol;
+ int eof;
+ int push_back[3];
+ int start_lineno;
+public:
+ top_input(FILE *);
+ int get();
+ int peek();
+ int get_location(const char **, int *);
+};
+
+top_input::top_input(FILE *p) : fp(p), bol(1), eof(0)
+{
+ push_back[0] = push_back[1] = push_back[2] = EOF;
+ start_lineno = current_lineno;
+}
+
+int top_input::get()
+{
+ if (eof)
+ return EOF;
+ if (push_back[2] != EOF) {
+ int c = push_back[2];
+ push_back[2] = EOF;
+ return c;
+ }
+ else if (push_back[1] != EOF) {
+ int c = push_back[1];
+ push_back[1] = EOF;
+ return c;
+ }
+ else if (push_back[0] != EOF) {
+ int c = push_back[0];
+ push_back[0] = EOF;
+ return c;
+ }
+ int c = getc(fp);
+ while (illegal_input_char(c)) {
+ error("illegal input character code %1", int(c));
+ c = getc(fp);
+ bol = 0;
+ }
+ if (bol && c == '.') {
+ c = getc(fp);
+ if (c == 'P') {
+ c = getc(fp);
+ if (c == 'F' || c == 'E') {
+ int d = getc(fp);
+ if (d != EOF)
+ ungetc(d, fp);
+ if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
+ eof = 1;
+ flyback_flag = c == 'F';
+ return EOF;
+ }
+ push_back[0] = c;
+ push_back[1] = 'P';
+ return '.';
+ }
+ if (c == 'S') {
+ c = getc(fp);
+ if (c != EOF)
+ ungetc(c, fp);
+ if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
+ error("nested .PS");
+ eof = 1;
+ return EOF;
+ }
+ push_back[0] = 'S';
+ push_back[1] = 'P';
+ return '.';
+ }
+ if (c != EOF)
+ ungetc(c, fp);
+ push_back[0] = 'P';
+ return '.';
+ }
+ else {
+ if (c != EOF)
+ ungetc(c, fp);
+ return '.';
+ }
+ }
+ if (c == '\n') {
+ bol = 1;
+ current_lineno++;
+ return '\n';
+ }
+ bol = 0;
+ if (c == EOF) {
+ eof = 1;
+ error("end of file before .PE or .PF");
+ error_with_file_and_line(current_filename, start_lineno - 1,
+ ".PS was here");
+ }
+ return c;
+}
+
+int top_input::peek()
+{
+ if (eof)
+ return EOF;
+ if (push_back[2] != EOF)
+ return push_back[2];
+ if (push_back[1] != EOF)
+ return push_back[1];
+ if (push_back[0] != EOF)
+ return push_back[0];
+ int c = getc(fp);
+ while (illegal_input_char(c)) {
+ error("illegal input character code %1", int(c));
+ c = getc(fp);
+ bol = 0;
+ }
+ if (bol && c == '.') {
+ c = getc(fp);
+ if (c == 'P') {
+ c = getc(fp);
+ if (c == 'F' || c == 'E') {
+ int d = getc(fp);
+ if (d != EOF)
+ ungetc(d, fp);
+ if (d == EOF || d == ' ' || d == '\n' || compatible_flag) {
+ eof = 1;
+ flyback_flag = c == 'F';
+ return EOF;
+ }
+ push_back[0] = c;
+ push_back[1] = 'P';
+ push_back[2] = '.';
+ return '.';
+ }
+ if (c == 'S') {
+ c = getc(fp);
+ if (c != EOF)
+ ungetc(c, fp);
+ if (c == EOF || c == ' ' || c == '\n' || compatible_flag) {
+ error("nested .PS");
+ eof = 1;
+ return EOF;
+ }
+ push_back[0] = 'S';
+ push_back[1] = 'P';
+ push_back[2] = '.';
+ return '.';
+ }
+ if (c != EOF)
+ ungetc(c, fp);
+ push_back[0] = 'P';
+ push_back[1] = '.';
+ return '.';
+ }
+ else {
+ if (c != EOF)
+ ungetc(c, fp);
+ push_back[0] = '.';
+ return '.';
+ }
+ }
+ if (c != EOF)
+ ungetc(c, fp);
+ if (c == '\n')
+ return '\n';
+ return c;
+}
+
+int top_input::get_location(const char **filenamep, int *linenop)
+{
+ *filenamep = current_filename;
+ *linenop = current_lineno;
+ return 1;
+}
+
+void do_picture(FILE *fp)
+{
+ flyback_flag = 0;
+ int c;
+ while ((c = getc(fp)) == ' ')
+ ;
+ if (c == '<') {
+ string filename;
+ while ((c = getc(fp)) == ' ')
+ ;
+ while (c != EOF && c != ' ' && c != '\n') {
+ filename += char(c);
+ c = getc(fp);
+ }
+ if (c == ' ') {
+ do {
+ c = getc(fp);
+ } while (c != EOF && c != '\n');
+ }
+ if (c == '\n')
+ current_lineno++;
+ if (filename.length() == 0)
+ error("missing filename after `<'");
+ else {
+ filename += '\0';
+ const char *old_filename = current_filename;
+ int old_lineno = current_lineno;
+ // filenames must be permanent
+ do_file(strsave(filename.contents()));
+ current_filename = old_filename;
+ current_lineno = old_lineno;
+ }
+ out->set_location(current_filename, current_lineno);
+ }
+ else {
+ out->set_location(current_filename, current_lineno);
+ string start_line;
+ while (c != EOF) {
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ start_line += c;
+ c = getc(fp);
+ }
+ if (c == EOF)
+ return;
+ start_line += '\0';
+ double wid, ht;
+ switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) {
+ case 1:
+ ht = 0.0;
+ break;
+ case 2:
+ break;
+ default:
+ ht = wid = 0.0;
+ break;
+ }
+ out->set_desired_width_height(wid, ht);
+ out->set_args(start_line.contents());
+ lex_init(new top_input(fp));
+ if (yyparse()) {
+ had_parse_error = 1;
+ lex_error("giving up on this picture");
+ }
+ parse_cleanup();
+ lex_cleanup();
+
+ // skip the rest of the .PF/.PE line
+ while ((c = getc(fp)) != EOF && c != '\n')
+ ;
+ if (c == '\n')
+ current_lineno++;
+ out->set_location(current_filename, current_lineno);
+ }
+}
+
+void do_file(const char *filename)
+{
+ FILE *fp;
+ if (strcmp(filename, "-") == 0)
+ fp = stdin;
+ else {
+ errno = 0;
+ fp = fopen(filename, "r");
+ if (fp == 0)
+ fatal("can't open `%1': %2", filename, strerror(errno));
+ }
+ out->set_location(filename, 1);
+ current_filename = filename;
+ current_lineno = 1;
+ enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ switch (state) {
+ case START:
+ if (c == '.')
+ state = HAD_DOT;
+ else {
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case MIDDLE:
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ break;
+ case HAD_DOT:
+ if (c == 'P')
+ state = HAD_P;
+ else if (lf_flag && c == 'l')
+ state = HAD_l;
+ else {
+ putchar('.');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_P:
+ if (c == 'S')
+ state = HAD_PS;
+ else {
+ putchar('.');
+ putchar('P');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_PS:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ ungetc(c, fp);
+ do_picture(fp);
+ state = START;
+ }
+ else {
+ fputs(".PS", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ case HAD_l:
+ if (c == 'f')
+ state = HAD_lf;
+ else {
+ putchar('.');
+ putchar('l');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_lf:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ string line;
+ while (c != EOF) {
+ line += c;
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ c = getc(fp);
+ }
+ line += '\0';
+ interpret_lf_args(line.contents());
+ printf(".lf%s", line.contents());
+ state = START;
+ }
+ else {
+ fputs(".lf", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+ switch (state) {
+ case START:
+ break;
+ case MIDDLE:
+ putchar('\n');
+ break;
+ case HAD_DOT:
+ fputs(".\n", stdout);
+ break;
+ case HAD_P:
+ fputs(".P\n", stdout);
+ break;
+ case HAD_PS:
+ fputs(".PS\n", stdout);
+ break;
+ case HAD_l:
+ fputs(".l\n", stdout);
+ break;
+ case HAD_lf:
+ fputs(".lf\n", stdout);
+ break;
+ }
+ if (fp != stdin)
+ fclose(fp);
+}
+
+#ifdef FIG_SUPPORT
+void do_whole_file(const char *filename)
+{
+ // Do not set current_filename.
+ FILE *fp;
+ if (strcmp(filename, "-") == 0)
+ fp = stdin;
+ else {
+ errno = 0;
+ fp = fopen(filename, "r");
+ if (fp == 0)
+ fatal("can't open `%1': %2", filename, strerror(errno));
+ }
+ lex_init(new file_input(fp, filename));
+ if (yyparse())
+ had_parse_error = 1;
+ parse_cleanup();
+ lex_cleanup();
+}
+#endif
+
+void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name);
+#ifdef TEX_SUPPORT
+ fprintf(stream, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
+#endif
+#ifdef FIG_SUPPORT
+ fprintf(stream, " %s -f [ -v ] [ filename ]\n", program_name);
+#endif
+}
+
+#ifdef __MSDOS__
+static char *fix_program_name(char *arg, char *dflt)
+{
+ if (!arg)
+ return dflt;
+ char *prog = strchr(arg, '\0');
+ for (;;) {
+ if (prog == arg)
+ break;
+ --prog;
+ if (strchr("\\/:", *prog)) {
+ prog++;
+ break;
+ }
+ }
+ char *ext = strchr(prog, '.');
+ if (ext)
+ *ext = '\0';
+ for (char *p = prog; *p; p++)
+ if ('A' <= *p && *p <= 'Z')
+ *p = 'a' + (*p - 'A');
+ return prog;
+}
+#endif /* __MSDOS__ */
+
+int main(int argc, char **argv)
+{
+#ifdef __MSDOS__
+ argv[0] = fix_program_name(argv[0], "pic");
+#endif /* __MSDOS__ */
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+#ifdef TEX_SUPPORT
+ int tex_flag = 0;
+ int tpic_flag = 0;
+#endif
+#ifdef FIG_SUPPORT
+ int whole_file_flag = 0;
+ int fig_flag = 0;
+#endif
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'D':
+ case 'T':
+ break;
+ case 'S':
+ safer_flag = 1;
+ break;
+ case 'U':
+ safer_flag = 0;
+ break;
+ case 'f':
+#ifdef FIG_SUPPORT
+ whole_file_flag++;
+ fig_flag++;
+#else
+ fatal("fig support not included");
+#endif
+ break;
+ case 'n':
+ driver_extension_flag = 0;
+ break;
+ case 'p':
+ case 'x':
+ warning("-%1 option is obsolete", char(opt));
+ break;
+ case 't':
+#ifdef TEX_SUPPORT
+ tex_flag++;
+#else
+ fatal("TeX support not included");
+#endif
+ break;
+ case 'c':
+#ifdef TEX_SUPPORT
+ tpic_flag++;
+#else
+ fatal("TeX support not included");
+#endif
+ break;
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU pic (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'z':
+ // zero length lines will be printed as dots
+ zero_length_line_flag++;
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ parse_init();
+#ifdef TEX_SUPPORT
+ if (tpic_flag) {
+ out = make_tpic_output();
+ lf_flag = 0;
+ }
+ else if (tex_flag) {
+ out = make_tex_output();
+ command_char = '\\';
+ lf_flag = 0;
+ }
+ else
+#endif
+#ifdef FIG_SUPPORT
+ if (fig_flag)
+ out = make_fig_output();
+ else
+#endif
+ out = make_troff_output();
+#ifdef FIG_SUPPORT
+ if (whole_file_flag) {
+ if (optind >= argc)
+ do_whole_file("-");
+ else if (argc - optind > 1) {
+ usage(stderr);
+ exit(1);
+ } else
+ do_whole_file(argv[optind]);
+ }
+ else {
+#endif
+ if (optind >= argc)
+ do_file("-");
+ else
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+#ifdef FIG_SUPPORT
+ }
+#endif
+ delete out;
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ return had_parse_error;
+}
+
diff --git a/contrib/groff/src/preproc/pic/object.cc b/contrib/groff/src/preproc/pic/object.cc
new file mode 100644
index 000000000000..6b346330e4e2
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/object.cc
@@ -0,0 +1,1833 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "pic.h"
+#include "ptable.h"
+#include "object.h"
+
+void print_object_list(object *);
+
+line_type::line_type()
+: type(solid), thickness(1.0)
+{
+}
+
+output::output() : args(0), desired_height(0.0), desired_width(0.0)
+{
+}
+
+output::~output()
+{
+ a_delete args;
+}
+
+void output::set_desired_width_height(double wid, double ht)
+{
+ desired_width = wid;
+ desired_height = ht;
+}
+
+void output::set_args(const char *s)
+{
+ a_delete args;
+ if (s == 0 || *s == '\0')
+ args = 0;
+ else
+ args = strsave(s);
+}
+
+void output::command(const char *, const char *, int)
+{
+}
+
+void output::set_location(const char *, int)
+{
+}
+
+int output::supports_filled_polygons()
+{
+ return 0;
+}
+
+void output::begin_block(const position &, const position &)
+{
+}
+
+void output::end_block()
+{
+}
+
+double output::compute_scale(double sc, const position &ll, const position &ur)
+{
+ distance dim = ur - ll;
+ if (desired_width != 0.0 || desired_height != 0.0) {
+ sc = 0.0;
+ if (desired_width != 0.0) {
+ if (dim.x == 0.0)
+ error("width specified for picture with zero width");
+ else
+ sc = dim.x/desired_width;
+ }
+ if (desired_height != 0.0) {
+ if (dim.y == 0.0)
+ error("height specified for picture with zero height");
+ else {
+ double tem = dim.y/desired_height;
+ if (tem > sc)
+ sc = tem;
+ }
+ }
+ return sc == 0.0 ? 1.0 : sc;
+ }
+ else {
+ if (sc <= 0.0)
+ sc = 1.0;
+ distance sdim = dim/sc;
+ double max_width = 0.0;
+ lookup_variable("maxpswid", &max_width);
+ double max_height = 0.0;
+ lookup_variable("maxpsht", &max_height);
+ if ((max_width > 0.0 && sdim.x > max_width)
+ || (max_height > 0.0 && sdim.y > max_height)) {
+ double xscale = dim.x/max_width;
+ double yscale = dim.y/max_height;
+ return xscale > yscale ? xscale : yscale;
+ }
+ else
+ return sc;
+ }
+}
+
+position::position(const place &pl)
+{
+ if (pl.obj != 0) {
+ // Use two statements to work around bug in SGI C++.
+ object *tem = pl.obj;
+ *this = tem->origin();
+ }
+ else {
+ x = pl.x;
+ y = pl.y;
+ }
+}
+
+position::position() : x(0.0), y(0.0)
+{
+}
+
+position::position(double a, double b) : x(a), y(b)
+{
+}
+
+
+int operator==(const position &a, const position &b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+int operator!=(const position &a, const position &b)
+{
+ return a.x != b.x || a.y != b.y;
+}
+
+position &position::operator+=(const position &a)
+{
+ x += a.x;
+ y += a.y;
+ return *this;
+}
+
+position &position::operator-=(const position &a)
+{
+ x -= a.x;
+ y -= a.y;
+ return *this;
+}
+
+position &position::operator*=(double a)
+{
+ x *= a;
+ y *= a;
+ return *this;
+}
+
+position &position::operator/=(double a)
+{
+ x /= a;
+ y /= a;
+ return *this;
+}
+
+position operator-(const position &a)
+{
+ return position(-a.x, -a.y);
+}
+
+position operator+(const position &a, const position &b)
+{
+ return position(a.x + b.x, a.y + b.y);
+}
+
+position operator-(const position &a, const position &b)
+{
+ return position(a.x - b.x, a.y - b.y);
+}
+
+position operator/(const position &a, double n)
+{
+ return position(a.x/n, a.y/n);
+}
+
+position operator*(const position &a, double n)
+{
+ return position(a.x*n, a.y*n);
+}
+
+// dot product
+
+double operator*(const position &a, const position &b)
+{
+ return a.x*b.x + a.y*b.y;
+}
+
+double hypot(const position &a)
+{
+ return hypot(a.x, a.y);
+}
+
+struct arrow_head_type {
+ double height;
+ double width;
+ int solid;
+};
+
+void draw_arrow(const position &pos, const distance &dir,
+ const arrow_head_type &aht, const line_type &lt)
+{
+ double hyp = hypot(dir);
+ if (hyp == 0.0) {
+ error("cannot draw arrow on object with zero length");
+ return;
+ }
+ position base = -dir;
+ base *= aht.height/hyp;
+ position n(dir.y, -dir.x);
+ n *= aht.width/(hyp*2.0);
+ line_type slt = lt;
+ slt.type = line_type::solid;
+ if (aht.solid && out->supports_filled_polygons()) {
+ position v[3];
+ v[0] = pos;
+ v[1] = pos + base + n;
+ v[2] = pos + base - n;
+ // A value > 1 means fill with the current color.
+ out->polygon(v, 3, slt, 2.0);
+ }
+ else {
+ position v[2];
+ v[0] = pos;
+ v[1] = pos + base + n;
+ out->line(pos + base - n, v, 2, slt);
+ }
+}
+
+object::object() : prev(0), next(0)
+{
+}
+
+object::~object()
+{
+}
+
+void object::move_by(const position &)
+{
+}
+
+void object::print()
+{
+}
+
+void object::print_text()
+{
+}
+
+int object::blank()
+{
+ return 0;
+}
+
+struct bounding_box {
+ int blank;
+ position ll;
+ position ur;
+
+ bounding_box();
+ void encompass(const position &);
+};
+
+bounding_box::bounding_box()
+: blank(1)
+{
+}
+
+void bounding_box::encompass(const position &pos)
+{
+ if (blank) {
+ ll = pos;
+ ur = pos;
+ blank = 0;
+ }
+ else {
+ if (pos.x < ll.x)
+ ll.x = pos.x;
+ if (pos.y < ll.y)
+ ll.y = pos.y;
+ if (pos.x > ur.x)
+ ur.x = pos.x;
+ if (pos.y > ur.y)
+ ur.y = pos.y;
+ }
+}
+
+void object::update_bounding_box(bounding_box *)
+{
+}
+
+position object::origin()
+{
+ return position(0.0,0.0);
+}
+
+position object::north()
+{
+ return origin();
+}
+
+position object::south()
+{
+ return origin();
+}
+
+position object::east()
+{
+ return origin();
+}
+
+position object::west()
+{
+ return origin();
+}
+
+position object::north_east()
+{
+ return origin();
+}
+
+position object::north_west()
+{
+ return origin();
+}
+
+position object::south_east()
+{
+ return origin();
+}
+
+position object::south_west()
+{
+ return origin();
+}
+
+position object::start()
+{
+ return origin();
+}
+
+position object::end()
+{
+ return origin();
+}
+
+position object::center()
+{
+ return origin();
+}
+
+double object::width()
+{
+ return 0.0;
+}
+
+double object::radius()
+{
+ return 0.0;
+}
+
+double object::height()
+{
+ return 0.0;
+}
+
+place *object::find_label(const char *)
+{
+ return 0;
+}
+
+segment::segment(const position &a, int n, segment *p)
+: is_absolute(n), pos(a), next(p)
+{
+}
+
+text_item::text_item(char *t, const char *fn, int ln)
+: next(0), text(t), filename(fn), lineno(ln)
+{
+ adj.h = CENTER_ADJUST;
+ adj.v = NONE_ADJUST;
+}
+
+text_item::~text_item()
+{
+ a_delete text;
+}
+
+object_spec::object_spec(object_type t) : type(t)
+{
+ flags = 0;
+ tbl = 0;
+ segment_list = 0;
+ segment_width = segment_height = 0.0;
+ segment_is_absolute = 0;
+ text = 0;
+ with = 0;
+ dir = RIGHT_DIRECTION;
+}
+
+object_spec::~object_spec()
+{
+ delete tbl;
+ while (segment_list != 0) {
+ segment *tem = segment_list;
+ segment_list = segment_list->next;
+ delete tem;
+ }
+ object *p = oblist.head;
+ while (p != 0) {
+ object *tem = p;
+ p = p->next;
+ delete tem;
+ }
+ while (text != 0) {
+ text_item *tem = text;
+ text = text->next;
+ delete tem;
+ }
+ delete with;
+}
+
+class command_object : public object {
+ char *s;
+ const char *filename;
+ int lineno;
+public:
+ command_object(char *, const char *, int);
+ ~command_object();
+ object_type type() { return OTHER_OBJECT; }
+ void print();
+};
+
+command_object::command_object(char *p, const char *fn, int ln)
+: s(p), filename(fn), lineno(ln)
+{
+}
+
+command_object::~command_object()
+{
+ a_delete s;
+}
+
+void command_object::print()
+{
+ out->command(s, filename, lineno);
+}
+
+object *make_command_object(char *s, const char *fn, int ln)
+{
+ return new command_object(s, fn, ln);
+}
+
+class mark_object : public object {
+public:
+ mark_object();
+ object_type type();
+};
+
+object *make_mark_object()
+{
+ return new mark_object();
+}
+
+mark_object::mark_object()
+{
+}
+
+object_type mark_object::type()
+{
+ return MARK_OBJECT;
+}
+
+object_list::object_list() : head(0), tail(0)
+{
+}
+
+void object_list::append(object *obj)
+{
+ if (tail == 0) {
+ obj->next = obj->prev = 0;
+ head = tail = obj;
+ }
+ else {
+ obj->prev = tail;
+ obj->next = 0;
+ tail->next = obj;
+ tail = obj;
+ }
+}
+
+void object_list::wrap_up_block(object_list *ol)
+{
+ object *p;
+ for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
+ ;
+ assert(p != 0);
+ ol->head = p->next;
+ if (ol->head) {
+ ol->tail = tail;
+ ol->head->prev = 0;
+ }
+ else
+ ol->tail = 0;
+ tail = p->prev;
+ if (tail)
+ tail->next = 0;
+ else
+ head = 0;
+ delete p;
+}
+
+text_piece::text_piece()
+: text(0), filename(0), lineno(-1)
+{
+ adj.h = CENTER_ADJUST;
+ adj.v = NONE_ADJUST;
+}
+
+text_piece::~text_piece()
+{
+ a_delete text;
+}
+
+class graphic_object : public object {
+ int ntext;
+ text_piece *text;
+ int aligned;
+protected:
+ line_type lt;
+public:
+ graphic_object();
+ ~graphic_object();
+ object_type type() = 0;
+ void print_text();
+ void add_text(text_item *, int);
+ void set_dotted(double);
+ void set_dashed(double);
+ void set_thickness(double);
+ void set_invisible();
+ virtual void set_fill(double);
+};
+
+graphic_object::graphic_object() : ntext(0), text(0), aligned(0)
+{
+}
+
+void graphic_object::set_dotted(double wid)
+{
+ lt.type = line_type::dotted;
+ lt.dash_width = wid;
+}
+
+void graphic_object::set_dashed(double wid)
+{
+ lt.type = line_type::dashed;
+ lt.dash_width = wid;
+}
+
+void graphic_object::set_thickness(double th)
+{
+ lt.thickness = th;
+}
+
+void graphic_object::set_fill(double)
+{
+}
+
+void graphic_object::set_invisible()
+{
+ lt.type = line_type::invisible;
+}
+
+void graphic_object::add_text(text_item *t, int a)
+{
+ aligned = a;
+ int len = 0;
+ text_item *p;
+ for (p = t; p; p = p->next)
+ len++;
+ if (len == 0)
+ text = 0;
+ else {
+ text = new text_piece[len];
+ for (p = t, len = 0; p; p = p->next, len++) {
+ text[len].text = p->text;
+ p->text = 0;
+ text[len].adj = p->adj;
+ text[len].filename = p->filename;
+ text[len].lineno = p->lineno;
+ }
+ }
+ ntext = len;
+}
+
+void graphic_object::print_text()
+{
+ double angle = 0.0;
+ if (aligned) {
+ position d(end() - start());
+ if (d.x != 0.0 || d.y != 0.0)
+ angle = atan2(d.y, d.x);
+ }
+ if (text != 0)
+ out->text(center(), text, ntext, angle);
+}
+
+graphic_object::~graphic_object()
+{
+ if (text)
+ ad_delete(ntext) text;
+}
+
+class rectangle_object : public graphic_object {
+protected:
+ position cent;
+ position dim;
+public:
+ rectangle_object(const position &);
+ double width() { return dim.x; }
+ double height() { return dim.y; }
+ position origin() { return cent; }
+ position center() { return cent; }
+ position north() { return position(cent.x, cent.y + dim.y/2.0); }
+ position south() { return position(cent.x, cent.y - dim.y/2.0); }
+ position east() { return position(cent.x + dim.x/2.0, cent.y); }
+ position west() { return position(cent.x - dim.x/2.0, cent.y); }
+ position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
+ position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
+ position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
+ position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
+ object_type type() = 0;
+ void update_bounding_box(bounding_box *);
+ void move_by(const position &);
+};
+
+rectangle_object::rectangle_object(const position &d)
+: dim(d)
+{
+}
+
+void rectangle_object::update_bounding_box(bounding_box *p)
+{
+ p->encompass(cent - dim/2.0);
+ p->encompass(cent + dim/2.0);
+}
+
+void rectangle_object::move_by(const position &a)
+{
+ cent += a;
+}
+
+class closed_object : public rectangle_object {
+public:
+ closed_object(const position &);
+ object_type type() = 0;
+ void set_fill(double);
+protected:
+ double fill; // < 0 if not filled
+};
+
+closed_object::closed_object(const position &pos)
+: rectangle_object(pos), fill(-1.0)
+{
+}
+
+void closed_object::set_fill(double f)
+{
+ assert(f >= 0.0);
+ fill = f;
+}
+
+
+class box_object : public closed_object {
+ double xrad;
+ double yrad;
+public:
+ box_object(const position &, double);
+ object_type type() { return BOX_OBJECT; }
+ void print();
+ position north_east();
+ position north_west();
+ position south_east();
+ position south_west();
+};
+
+box_object::box_object(const position &pos, double r)
+: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
+{
+}
+
+const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
+
+position box_object::north_east()
+{
+ return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
+ cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
+}
+
+position box_object::north_west()
+{
+ return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
+ cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
+}
+
+position box_object::south_east()
+{
+ return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
+ cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
+}
+
+position box_object::south_west()
+{
+ return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
+ cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
+}
+
+void box_object::print()
+{
+ if (lt.type == line_type::invisible && fill < 0.0)
+ return;
+ if (xrad == 0.0) {
+ distance dim2 = dim/2.0;
+ position vec[4];
+ vec[0] = cent + position(dim2.x, -dim2.y);
+ vec[1] = cent + position(dim2.x, dim2.y);
+ vec[2] = cent + position(-dim2.x, dim2.y);
+ vec[3] = cent + position(-dim2.x, -dim2.y);
+ out->polygon(vec, 4, lt, fill);
+ }
+ else {
+ distance abs_dim(fabs(dim.x), fabs(dim.y));
+ out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
+ }
+}
+
+graphic_object *object_spec::make_box(position *curpos, direction *dirp)
+{
+ static double last_box_height;
+ static double last_box_width;
+ static double last_box_radius;
+ static int have_last_box = 0;
+ if (!(flags & HAS_HEIGHT)) {
+ if ((flags & IS_SAME) && have_last_box)
+ height = last_box_height;
+ else
+ lookup_variable("boxht", &height);
+ }
+ if (!(flags & HAS_WIDTH)) {
+ if ((flags & IS_SAME) && have_last_box)
+ width = last_box_width;
+ else
+ lookup_variable("boxwid", &width);
+ }
+ if (!(flags & HAS_RADIUS)) {
+ if ((flags & IS_SAME) && have_last_box)
+ radius = last_box_radius;
+ else
+ lookup_variable("boxrad", &radius);
+ }
+ last_box_width = width;
+ last_box_height = height;
+ last_box_radius = radius;
+ have_last_box = 1;
+ radius = fabs(radius);
+ if (radius*2.0 > fabs(width))
+ radius = fabs(width/2.0);
+ if (radius*2.0 > fabs(height))
+ radius = fabs(height/2.0);
+ box_object *p = new box_object(position(width, height), radius);
+ if (!position_rectangle(p, curpos, dirp)) {
+ delete p;
+ p = 0;
+ }
+ return p;
+}
+
+// return non-zero for success
+
+int object_spec::position_rectangle(rectangle_object *p,
+ position *curpos, direction *dirp)
+{
+ position pos;
+ dir = *dirp; // ignore any direction in attribute list
+ position motion;
+ switch (dir) {
+ case UP_DIRECTION:
+ motion.y = p->height()/2.0;
+ break;
+ case DOWN_DIRECTION:
+ motion.y = -p->height()/2.0;
+ break;
+ case LEFT_DIRECTION:
+ motion.x = -p->width()/2.0;
+ break;
+ case RIGHT_DIRECTION:
+ motion.x = p->width()/2.0;
+ break;
+ default:
+ assert(0);
+ }
+ if (flags & HAS_AT) {
+ pos = at;
+ if (flags & HAS_WITH) {
+ place offset;
+ place here;
+ here.obj = p;
+ if (!with->follow(here, &offset))
+ return 0;
+ pos -= offset;
+ }
+ }
+ else {
+ pos = *curpos;
+ pos += motion;
+ }
+ p->move_by(pos);
+ pos += motion;
+ *curpos = pos;
+ return 1;
+}
+
+class block_object : public rectangle_object {
+ object_list oblist;
+ PTABLE(place) *tbl;
+public:
+ block_object(const position &, const object_list &ol, PTABLE(place) *t);
+ ~block_object();
+ place *find_label(const char *);
+ object_type type();
+ void move_by(const position &);
+ void print();
+};
+
+block_object::block_object(const position &d, const object_list &ol,
+ PTABLE(place) *t)
+: rectangle_object(d), oblist(ol), tbl(t)
+{
+}
+
+block_object::~block_object()
+{
+ delete tbl;
+ object *p = oblist.head;
+ while (p != 0) {
+ object *tem = p;
+ p = p->next;
+ delete tem;
+ }
+}
+
+void block_object::print()
+{
+ out->begin_block(south_west(), north_east());
+ print_object_list(oblist.head);
+ out->end_block();
+}
+
+static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
+{
+ // Adjust all the labels that aren't attached to objects.
+ PTABLE_ITERATOR(place) iter(tbl);
+ const char *key;
+ place *pl;
+ while (iter.next(&key, &pl))
+ if (key && csupper(key[0]) && pl->obj == 0) {
+ pl->x += a.x;
+ pl->y += a.y;
+ }
+}
+
+void block_object::move_by(const position &a)
+{
+ cent += a;
+ for (object *p = oblist.head; p; p = p->next)
+ p->move_by(a);
+ adjust_objectless_places(tbl, a);
+}
+
+
+place *block_object::find_label(const char *name)
+{
+ return tbl->lookup(name);
+}
+
+object_type block_object::type()
+{
+ return BLOCK_OBJECT;
+}
+
+graphic_object *object_spec::make_block(position *curpos, direction *dirp)
+{
+ bounding_box bb;
+ for (object *p = oblist.head; p; p = p->next)
+ p->update_bounding_box(&bb);
+ position dim;
+ if (!bb.blank) {
+ position m = -(bb.ll + bb.ur)/2.0;
+ for (object *p = oblist.head; p; p = p->next)
+ p->move_by(m);
+ adjust_objectless_places(tbl, m);
+ dim = bb.ur - bb.ll;
+ }
+ if (flags & HAS_WIDTH)
+ dim.x = width;
+ if (flags & HAS_HEIGHT)
+ dim.y = height;
+ block_object *block = new block_object(dim, oblist, tbl);
+ if (!position_rectangle(block, curpos, dirp)) {
+ delete block;
+ block = 0;
+ }
+ tbl = 0;
+ oblist.head = oblist.tail = 0;
+ return block;
+}
+
+class text_object : public rectangle_object {
+public:
+ text_object(const position &);
+ object_type type() { return TEXT_OBJECT; }
+};
+
+text_object::text_object(const position &d)
+: rectangle_object(d)
+{
+}
+
+graphic_object *object_spec::make_text(position *curpos, direction *dirp)
+{
+ if (!(flags & HAS_HEIGHT)) {
+ lookup_variable("textht", &height);
+ int nitems = 0;
+ for (text_item *t = text; t; t = t->next)
+ nitems++;
+ height *= nitems;
+ }
+ if (!(flags & HAS_WIDTH))
+ lookup_variable("textwid", &width);
+ text_object *p = new text_object(position(width, height));
+ if (!position_rectangle(p, curpos, dirp)) {
+ delete p;
+ p = 0;
+ }
+ return p;
+}
+
+
+class ellipse_object : public closed_object {
+public:
+ ellipse_object(const position &);
+ position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
+ cent.y + dim.y/(M_SQRT2*2.0)); }
+ position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
+ cent.y + dim.y/(M_SQRT2*2.0)); }
+ position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
+ cent.y - dim.y/(M_SQRT2*2.0)); }
+ position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
+ cent.y - dim.y/(M_SQRT2*2.0)); }
+ double radius() { return dim.x/2.0; }
+ object_type type() { return ELLIPSE_OBJECT; }
+ void print();
+};
+
+ellipse_object::ellipse_object(const position &d)
+: closed_object(d)
+{
+}
+
+void ellipse_object::print()
+{
+ if (lt.type == line_type::invisible && fill < 0.0)
+ return;
+ out->ellipse(cent, dim, lt, fill);
+}
+
+graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
+{
+ static double last_ellipse_height;
+ static double last_ellipse_width;
+ static int have_last_ellipse = 0;
+ if (!(flags & HAS_HEIGHT)) {
+ if ((flags & IS_SAME) && have_last_ellipse)
+ height = last_ellipse_height;
+ else
+ lookup_variable("ellipseht", &height);
+ }
+ if (!(flags & HAS_WIDTH)) {
+ if ((flags & IS_SAME) && have_last_ellipse)
+ width = last_ellipse_width;
+ else
+ lookup_variable("ellipsewid", &width);
+ }
+ last_ellipse_width = width;
+ last_ellipse_height = height;
+ have_last_ellipse = 1;
+ ellipse_object *p = new ellipse_object(position(width, height));
+ if (!position_rectangle(p, curpos, dirp)) {
+ delete p;
+ return 0;
+ }
+ return p;
+}
+
+class circle_object : public ellipse_object {
+public:
+ circle_object(double);
+ object_type type() { return CIRCLE_OBJECT; }
+ void print();
+};
+
+circle_object::circle_object(double diam)
+: ellipse_object(position(diam, diam))
+{
+}
+
+void circle_object::print()
+{
+ if (lt.type == line_type::invisible && fill < 0.0)
+ return;
+ out->circle(cent, dim.x/2.0, lt, fill);
+}
+
+graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
+{
+ static double last_circle_radius;
+ static int have_last_circle = 0;
+ if (!(flags & HAS_RADIUS)) {
+ if ((flags & IS_SAME) && have_last_circle)
+ radius = last_circle_radius;
+ else
+ lookup_variable("circlerad", &radius);
+ }
+ last_circle_radius = radius;
+ have_last_circle = 1;
+ circle_object *p = new circle_object(radius*2.0);
+ if (!position_rectangle(p, curpos, dirp)) {
+ delete p;
+ return 0;
+ }
+ return p;
+}
+
+class move_object : public graphic_object {
+ position strt;
+ position en;
+public:
+ move_object(const position &s, const position &e);
+ position origin() { return en; }
+ object_type type() { return MOVE_OBJECT; }
+ void update_bounding_box(bounding_box *);
+ void move_by(const position &);
+};
+
+move_object::move_object(const position &s, const position &e)
+: strt(s), en(e)
+{
+}
+
+void move_object::update_bounding_box(bounding_box *p)
+{
+ p->encompass(strt);
+ p->encompass(en);
+}
+
+void move_object::move_by(const position &a)
+{
+ strt += a;
+ en += a;
+}
+
+graphic_object *object_spec::make_move(position *curpos, direction *dirp)
+{
+ static position last_move;
+ static int have_last_move = 0;
+ *dirp = dir;
+ // No need to look at at since `at' attribute sets `from' attribute.
+ position startpos = (flags & HAS_FROM) ? from : *curpos;
+ if (!(flags & HAS_SEGMENT)) {
+ if ((flags && IS_SAME) && have_last_move)
+ segment_pos = last_move;
+ else {
+ switch (dir) {
+ case UP_DIRECTION:
+ segment_pos.y = segment_height;
+ break;
+ case DOWN_DIRECTION:
+ segment_pos.y = -segment_height;
+ break;
+ case LEFT_DIRECTION:
+ segment_pos.x = -segment_width;
+ break;
+ case RIGHT_DIRECTION:
+ segment_pos.x = segment_width;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
+ // Reverse the segment_list so that it's in forward order.
+ segment *old = segment_list;
+ segment_list = 0;
+ while (old != 0) {
+ segment *tem = old->next;
+ old->next = segment_list;
+ segment_list = old;
+ old = tem;
+ }
+ // Compute the end position.
+ position endpos = startpos;
+ for (segment *s = segment_list; s; s = s->next)
+ if (s->is_absolute)
+ endpos = s->pos;
+ else
+ endpos += s->pos;
+ have_last_move = 1;
+ last_move = endpos - startpos;
+ move_object *p = new move_object(startpos, endpos);
+ *curpos = endpos;
+ return p;
+}
+
+class linear_object : public graphic_object {
+protected:
+ char arrow_at_start;
+ char arrow_at_end;
+ arrow_head_type aht;
+ position strt;
+ position en;
+public:
+ linear_object(const position &s, const position &e);
+ position start() { return strt; }
+ position end() { return en; }
+ void move_by(const position &);
+ void update_bounding_box(bounding_box *) = 0;
+ object_type type() = 0;
+ void add_arrows(int at_start, int at_end, const arrow_head_type &);
+};
+
+class line_object : public linear_object {
+protected:
+ position *v;
+ int n;
+public:
+ line_object(const position &s, const position &e, position *, int);
+ ~line_object();
+ position origin() { return strt; }
+ position center() { return (strt + en)/2.0; }
+ position north() { return (en.y - strt.y) > 0 ? en : strt; }
+ position south() { return (en.y - strt.y) < 0 ? en : strt; }
+ position east() { return (en.x - strt.x) > 0 ? en : strt; }
+ position west() { return (en.x - strt.x) < 0 ? en : strt; }
+ object_type type() { return LINE_OBJECT; }
+ void update_bounding_box(bounding_box *);
+ void print();
+ void move_by(const position &);
+};
+
+class arrow_object : public line_object {
+public:
+ arrow_object(const position &, const position &, position *, int);
+ object_type type() { return ARROW_OBJECT; }
+};
+
+class spline_object : public line_object {
+public:
+ spline_object(const position &, const position &, position *, int);
+ object_type type() { return SPLINE_OBJECT; }
+ void print();
+ void update_bounding_box(bounding_box *);
+};
+
+linear_object::linear_object(const position &s, const position &e)
+: arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
+{
+}
+
+void linear_object::move_by(const position &a)
+{
+ strt += a;
+ en += a;
+}
+
+void linear_object::add_arrows(int at_start, int at_end,
+ const arrow_head_type &a)
+{
+ arrow_at_start = at_start;
+ arrow_at_end = at_end;
+ aht = a;
+}
+
+line_object::line_object(const position &s, const position &e,
+ position *p, int i)
+: linear_object(s, e), v(p), n(i)
+{
+}
+
+void line_object::print()
+{
+ if (lt.type == line_type::invisible)
+ return;
+ out->line(strt, v, n, lt);
+ if (arrow_at_start)
+ draw_arrow(strt, strt-v[0], aht, lt);
+ if (arrow_at_end)
+ draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
+}
+
+void line_object::update_bounding_box(bounding_box *p)
+{
+ p->encompass(strt);
+ for (int i = 0; i < n; i++)
+ p->encompass(v[i]);
+}
+
+void line_object::move_by(const position &pos)
+{
+ linear_object::move_by(pos);
+ for (int i = 0; i < n; i++)
+ v[i] += pos;
+}
+
+void spline_object::update_bounding_box(bounding_box *p)
+{
+ p->encompass(strt);
+ p->encompass(en);
+ /*
+
+ If
+
+ p1 = q1/2 + q2/2
+ p2 = q1/6 + q2*5/6
+ p3 = q2*5/6 + q3/6
+ p4 = q2/2 + q3/2
+ [ the points for the Bezier cubic ]
+
+ and
+
+ t = .5
+
+ then
+
+ (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
+ [ the equation for the Bezier cubic ]
+
+ = .125*q1 + .75*q2 + .125*q3
+
+ */
+ for (int i = 1; i < n; i++)
+ p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
+}
+
+arrow_object::arrow_object(const position &s, const position &e,
+ position *p, int i)
+: line_object(s, e, p, i)
+{
+}
+
+spline_object::spline_object(const position &s, const position &e,
+ position *p, int i)
+: line_object(s, e, p, i)
+{
+}
+
+void spline_object::print()
+{
+ if (lt.type == line_type::invisible)
+ return;
+ out->spline(strt, v, n, lt);
+ if (arrow_at_start)
+ draw_arrow(strt, strt-v[0], aht, lt);
+ if (arrow_at_end)
+ draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
+}
+
+line_object::~line_object()
+{
+ a_delete v;
+}
+
+linear_object *object_spec::make_line(position *curpos, direction *dirp)
+{
+ static position last_line;
+ static int have_last_line = 0;
+ *dirp = dir;
+ // No need to look at at since `at' attribute sets `from' attribute.
+ position startpos = (flags & HAS_FROM) ? from : *curpos;
+ if (!(flags & HAS_SEGMENT)) {
+ if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
+ && have_last_line)
+ segment_pos = last_line;
+ else
+ switch (dir) {
+ case UP_DIRECTION:
+ segment_pos.y = segment_height;
+ break;
+ case DOWN_DIRECTION:
+ segment_pos.y = -segment_height;
+ break;
+ case LEFT_DIRECTION:
+ segment_pos.x = -segment_width;
+ break;
+ case RIGHT_DIRECTION:
+ segment_pos.x = segment_width;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
+ // reverse the segment_list so that it's in forward order
+ segment *old = segment_list;
+ segment_list = 0;
+ while (old != 0) {
+ segment *tem = old->next;
+ old->next = segment_list;
+ segment_list = old;
+ old = tem;
+ }
+ // Absolutise all movements
+ position endpos = startpos;
+ int nsegments = 0;
+ segment *s;
+ for (s = segment_list; s; s = s->next, nsegments++)
+ if (s->is_absolute)
+ endpos = s->pos;
+ else {
+ endpos += s->pos;
+ s->pos = endpos;
+ s->is_absolute = 1; // to avoid confusion
+ }
+ // handle chop
+ line_object *p = 0;
+ position *v = new position[nsegments];
+ int i = 0;
+ for (s = segment_list; s; s = s->next, i++)
+ v[i] = s->pos;
+ if (flags & IS_DEFAULT_CHOPPED) {
+ lookup_variable("circlerad", &start_chop);
+ end_chop = start_chop;
+ flags |= IS_CHOPPED;
+ }
+ if (flags & IS_CHOPPED) {
+ position start_chop_vec, end_chop_vec;
+ if (start_chop != 0.0) {
+ start_chop_vec = v[0] - startpos;
+ start_chop_vec *= start_chop / hypot(start_chop_vec);
+ }
+ if (end_chop != 0.0) {
+ end_chop_vec = (v[nsegments - 1]
+ - (nsegments > 1 ? v[nsegments - 2] : startpos));
+ end_chop_vec *= end_chop / hypot(end_chop_vec);
+ }
+ startpos += start_chop_vec;
+ v[nsegments - 1] -= end_chop_vec;
+ endpos -= end_chop_vec;
+ }
+ switch (type) {
+ case SPLINE_OBJECT:
+ p = new spline_object(startpos, endpos, v, nsegments);
+ break;
+ case ARROW_OBJECT:
+ p = new arrow_object(startpos, endpos, v, nsegments);
+ break;
+ case LINE_OBJECT:
+ p = new line_object(startpos, endpos, v, nsegments);
+ break;
+ default:
+ assert(0);
+ }
+ have_last_line = 1;
+ last_line = endpos - startpos;
+ *curpos = endpos;
+ return p;
+}
+
+class arc_object : public linear_object {
+ int clockwise;
+ position cent;
+ double rad;
+public:
+ arc_object(int, const position &, const position &, const position &);
+ position origin() { return cent; }
+ position center() { return cent; }
+ double radius() { return rad; }
+ position north();
+ position south();
+ position east();
+ position west();
+ position north_east();
+ position north_west();
+ position south_east();
+ position south_west();
+ void update_bounding_box(bounding_box *);
+ object_type type() { return ARC_OBJECT; }
+ void print();
+ void move_by(const position &pos);
+};
+
+arc_object::arc_object(int cw, const position &s, const position &e,
+ const position &c)
+: linear_object(s, e), clockwise(cw), cent(c)
+{
+ rad = hypot(c - s);
+}
+
+void arc_object::move_by(const position &pos)
+{
+ linear_object::move_by(pos);
+ cent += pos;
+}
+
+// we get arc corners from the corresponding circle
+
+position arc_object::north()
+{
+ position result(cent);
+ result.y += rad;
+ return result;
+}
+
+position arc_object::south()
+{
+ position result(cent);
+ result.y -= rad;
+ return result;
+}
+
+position arc_object::east()
+{
+ position result(cent);
+ result.x += rad;
+ return result;
+}
+
+position arc_object::west()
+{
+ position result(cent);
+ result.x -= rad;
+ return result;
+}
+
+position arc_object::north_east()
+{
+ position result(cent);
+ result.x += rad/M_SQRT2;
+ result.y += rad/M_SQRT2;
+ return result;
+}
+
+position arc_object::north_west()
+{
+ position result(cent);
+ result.x -= rad/M_SQRT2;
+ result.y += rad/M_SQRT2;
+ return result;
+}
+
+position arc_object::south_east()
+{
+ position result(cent);
+ result.x += rad/M_SQRT2;
+ result.y -= rad/M_SQRT2;
+ return result;
+}
+
+position arc_object::south_west()
+{
+ position result(cent);
+ result.x -= rad/M_SQRT2;
+ result.y -= rad/M_SQRT2;
+ return result;
+}
+
+
+void arc_object::print()
+{
+ if (lt.type == line_type::invisible)
+ return;
+ if (clockwise)
+ out->arc(en, cent, strt, lt);
+ else
+ out->arc(strt, cent, en, lt);
+ if (arrow_at_start) {
+ position c = cent - strt;
+ draw_arrow(strt,
+ (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
+ aht, lt);
+ }
+ if (arrow_at_end) {
+ position e = en - cent;
+ draw_arrow(en,
+ (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
+ aht, lt);
+ }
+}
+
+inline double max(double a, double b)
+{
+ return a > b ? a : b;
+}
+
+void arc_object::update_bounding_box(bounding_box *p)
+{
+ p->encompass(strt);
+ p->encompass(en);
+ position start_offset = strt - cent;
+ if (start_offset.x == 0.0 && start_offset.y == 0.0)
+ return;
+ position end_offset = en - cent;
+ if (end_offset.x == 0.0 && end_offset.y == 0.0)
+ return;
+ double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
+ double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
+ if (clockwise) {
+ double temp = start_quad;
+ start_quad = end_quad;
+ end_quad = temp;
+ }
+ if (start_quad < 0.0)
+ start_quad += 4.0;
+ while (end_quad <= start_quad)
+ end_quad += 4.0;
+ double radius = max(hypot(start_offset), hypot(end_offset));
+ for (int q = int(start_quad) + 1; q < end_quad; q++) {
+ position offset;
+ switch (q % 4) {
+ case 0:
+ offset.x = radius;
+ break;
+ case 1:
+ offset.y = radius;
+ break;
+ case 2:
+ offset.x = -radius;
+ break;
+ case 3:
+ offset.y = -radius;
+ break;
+ }
+ p->encompass(cent + offset);
+ }
+}
+
+// We ignore the with attribute. The at attribute always refers to the center.
+
+linear_object *object_spec::make_arc(position *curpos, direction *dirp)
+{
+ *dirp = dir;
+ int cw = (flags & IS_CLOCKWISE) != 0;
+ // compute the start
+ position startpos;
+ if (flags & HAS_FROM)
+ startpos = from;
+ else
+ startpos = *curpos;
+ if (!(flags & HAS_RADIUS))
+ lookup_variable("arcrad", &radius);
+ // compute the end
+ position endpos;
+ if (flags & HAS_TO)
+ endpos = to;
+ else {
+ position m(radius, radius);
+ // Adjust the signs.
+ if (cw) {
+ if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
+ m.x = -m.x;
+ if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
+ m.y = -m.y;
+ *dirp = direction((dir + 3) % 4);
+ }
+ else {
+ if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
+ m.x = -m.x;
+ if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
+ m.y = -m.y;
+ *dirp = direction((dir + 1) % 4);
+ }
+ endpos = startpos + m;
+ }
+ // compute the center
+ position centerpos;
+ if (flags & HAS_AT)
+ centerpos = at;
+ else if (startpos == endpos)
+ centerpos = startpos;
+ else {
+ position h = (endpos - startpos)/2.0;
+ double d = hypot(h);
+ if (radius <= 0)
+ radius = .25;
+ // make the radius big enough
+ while (radius < d)
+ radius *= 2.0;
+ double alpha = acos(d/radius);
+ double theta = atan2(h.y, h.x);
+ if (cw)
+ theta -= alpha;
+ else
+ theta += alpha;
+ centerpos = position(cos(theta), sin(theta))*radius + startpos;
+ }
+ arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
+ *curpos = endpos;
+ return p;
+}
+
+graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
+{
+ linear_object *obj;
+ if (type == ARC_OBJECT)
+ obj = make_arc(curpos, dirp);
+ else
+ obj = make_line(curpos, dirp);
+ if (type == ARROW_OBJECT
+ && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
+ flags |= HAS_RIGHT_ARROW_HEAD;
+ if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
+ arrow_head_type a;
+ int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
+ int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
+ if (flags & HAS_HEIGHT)
+ a.height = height;
+ else
+ lookup_variable("arrowht", &a.height);
+ if (flags & HAS_WIDTH)
+ a.width = width;
+ else
+ lookup_variable("arrowwid", &a.width);
+ double solid;
+ lookup_variable("arrowhead", &solid);
+ a.solid = solid != 0.0;
+ obj->add_arrows(at_start, at_end, a);
+ }
+ return obj;
+}
+
+object *object_spec::make_object(position *curpos, direction *dirp)
+{
+ graphic_object *obj = 0;
+ switch (type) {
+ case BLOCK_OBJECT:
+ obj = make_block(curpos, dirp);
+ break;
+ case BOX_OBJECT:
+ obj = make_box(curpos, dirp);
+ break;
+ case TEXT_OBJECT:
+ obj = make_text(curpos, dirp);
+ break;
+ case ELLIPSE_OBJECT:
+ obj = make_ellipse(curpos, dirp);
+ break;
+ case CIRCLE_OBJECT:
+ obj = make_circle(curpos, dirp);
+ break;
+ case MOVE_OBJECT:
+ obj = make_move(curpos, dirp);
+ break;
+ case ARC_OBJECT:
+ case LINE_OBJECT:
+ case SPLINE_OBJECT:
+ case ARROW_OBJECT:
+ obj = make_linear(curpos, dirp);
+ break;
+ case MARK_OBJECT:
+ case OTHER_OBJECT:
+ default:
+ assert(0);
+ break;
+ }
+ if (obj) {
+ if (flags & IS_INVISIBLE)
+ obj->set_invisible();
+ if (text != 0)
+ obj->add_text(text, (flags & IS_ALIGNED) != 0);
+ if (flags & IS_DOTTED)
+ obj->set_dotted(dash_width);
+ else if (flags & IS_DASHED)
+ obj->set_dashed(dash_width);
+ double th;
+ if (flags & HAS_THICKNESS)
+ th = thickness;
+ else
+ lookup_variable("linethick", &th);
+ obj->set_thickness(th);
+ if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) {
+ if (flags & IS_DEFAULT_FILLED)
+ lookup_variable("fillval", &fill);
+ if (fill < 0.0)
+ error("bad fill value %1", fill);
+ else
+ obj->set_fill(fill);
+ }
+ }
+ return obj;
+}
+
+struct string_list {
+ string_list *next;
+ char *str;
+ string_list(char *);
+ ~string_list();
+};
+
+string_list::string_list(char *s)
+: next(0), str(s)
+{
+}
+
+string_list::~string_list()
+{
+ a_delete str;
+}
+
+/* A path is used to hold the argument to the with attribute. For example,
+`.nw' or `.A.s' or `.A'. The major operation on a path is to take a
+place and follow the path through the place to place within the place.
+Note that `.A.B.C.sw' will work. */
+
+path::path(corner c)
+: crn(c), label_list(0), ypath(0)
+{
+}
+
+path::path(char *l, corner c)
+: crn(c), ypath(0)
+{
+ label_list = new string_list(l);
+}
+
+path::~path()
+{
+ while (label_list) {
+ string_list *tem = label_list;
+ label_list = label_list->next;
+ delete tem;
+ }
+ delete ypath;
+}
+
+void path::append(corner c)
+{
+ assert(crn == 0);
+ crn = c;
+}
+
+void path::append(char *s)
+{
+ string_list **p;
+ for (p = &label_list; *p; p = &(*p)->next)
+ ;
+ *p = new string_list(s);
+}
+
+void path::set_ypath(path *p)
+{
+ ypath = p;
+}
+
+// return non-zero for success
+
+int path::follow(const place &pl, place *result) const
+{
+ const place *p = &pl;
+ for (string_list *lb = label_list; lb; lb = lb->next)
+ if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
+ lex_error("object does not contain a place `%1'", lb->str);
+ return 0;
+ }
+ if (crn == 0 || p->obj == 0)
+ *result = *p;
+ else {
+ position pos = ((p->obj)->*(crn))();
+ result->x = pos.x;
+ result->y = pos.y;
+ result->obj = 0;
+ }
+ if (ypath) {
+ place tem;
+ if (!ypath->follow(pl, &tem))
+ return 0;
+ result->y = tem.y;
+ if (result->obj != tem.obj)
+ result->obj = 0;
+ }
+ return 1;
+}
+
+void print_object_list(object *p)
+{
+ for (; p; p = p->next) {
+ p->print();
+ p->print_text();
+ }
+}
+
+void print_picture(object *obj)
+{
+ bounding_box bb;
+ for (object *p = obj; p; p = p->next)
+ p->update_bounding_box(&bb);
+ double scale;
+ lookup_variable("scale", &scale);
+ out->start_picture(scale, bb.ll, bb.ur);
+ print_object_list(obj);
+ out->finish_picture();
+}
+
diff --git a/contrib/groff/src/preproc/pic/object.h b/contrib/groff/src/preproc/pic/object.h
new file mode 100644
index 000000000000..2748e81e8465
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/object.h
@@ -0,0 +1,217 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct place;
+
+enum object_type {
+ OTHER_OBJECT,
+ BOX_OBJECT,
+ CIRCLE_OBJECT,
+ ELLIPSE_OBJECT,
+ ARC_OBJECT,
+ SPLINE_OBJECT,
+ LINE_OBJECT,
+ ARROW_OBJECT,
+ MOVE_OBJECT,
+ TEXT_OBJECT,
+ BLOCK_OBJECT,
+ MARK_OBJECT
+ };
+
+struct bounding_box;
+
+struct object {
+ object *prev;
+ object *next;
+ object();
+ virtual ~object();
+ virtual position origin();
+ virtual double width();
+ virtual double radius();
+ virtual double height();
+ virtual position north();
+ virtual position south();
+ virtual position east();
+ virtual position west();
+ virtual position north_east();
+ virtual position north_west();
+ virtual position south_east();
+ virtual position south_west();
+ virtual position start();
+ virtual position end();
+ virtual position center();
+ virtual place *find_label(const char *);
+ virtual void move_by(const position &);
+ virtual int blank();
+ virtual void update_bounding_box(bounding_box *);
+ virtual object_type type() = 0;
+ virtual void print();
+ virtual void print_text();
+};
+
+typedef position (object::*corner)();
+
+struct place {
+ object *obj;
+ double x, y;
+};
+
+struct string_list;
+
+class path {
+ corner crn;
+ string_list *label_list;
+ path *ypath;
+public:
+ path(corner = 0);
+ path(char *, corner = 0);
+ ~path();
+ void append(corner);
+ void append(char *);
+ void set_ypath(path *);
+ int follow(const place &, place *) const;
+};
+
+struct object_list {
+ object *head;
+ object *tail;
+ object_list();
+ void append(object *);
+ void wrap_up_block(object_list *);
+};
+
+declare_ptable(place)
+
+// these go counterclockwise
+enum direction {
+ RIGHT_DIRECTION,
+ UP_DIRECTION,
+ LEFT_DIRECTION,
+ DOWN_DIRECTION
+ };
+
+struct graphics_state {
+ double x, y;
+ direction dir;
+};
+
+struct saved_state : public graphics_state {
+ saved_state *prev;
+ PTABLE(place) *tbl;
+};
+
+
+struct text_item {
+ text_item *next;
+ char *text;
+ adjustment adj;
+ const char *filename;
+ int lineno;
+
+ text_item(char *, const char *, int);
+ ~text_item();
+};
+
+const unsigned long IS_DOTTED = 01;
+const unsigned long IS_DASHED = 02;
+const unsigned long IS_CLOCKWISE = 04;
+const unsigned long IS_INVISIBLE = 020;
+const unsigned long HAS_LEFT_ARROW_HEAD = 040;
+const unsigned long HAS_RIGHT_ARROW_HEAD = 0100;
+const unsigned long HAS_SEGMENT = 0200;
+const unsigned long IS_SAME = 0400;
+const unsigned long HAS_FROM = 01000;
+const unsigned long HAS_AT = 02000;
+const unsigned long HAS_WITH = 04000;
+const unsigned long HAS_HEIGHT = 010000;
+const unsigned long HAS_WIDTH = 020000;
+const unsigned long HAS_RADIUS = 040000;
+const unsigned long HAS_TO = 0100000;
+const unsigned long IS_CHOPPED = 0200000;
+const unsigned long IS_DEFAULT_CHOPPED = 0400000;
+const unsigned long HAS_THICKNESS = 01000000;
+const unsigned long IS_FILLED = 02000000;
+const unsigned long IS_DEFAULT_FILLED = 04000000;
+const unsigned long IS_ALIGNED = 010000000;
+
+struct segment {
+ int is_absolute;
+ position pos;
+ segment *next;
+ segment(const position &, int, segment *);
+};
+
+struct rectangle_object;
+struct graphic_object;
+struct linear_object;
+
+struct object_spec {
+ unsigned long flags;
+ object_type type;
+ object_list oblist;
+ PTABLE(place) *tbl;
+ double dash_width;
+ position from;
+ position to;
+ position at;
+ position by;
+ path *with;
+ text_item *text;
+ double height;
+ double radius;
+ double width;
+ double segment_width;
+ double segment_height;
+ double start_chop;
+ double end_chop;
+ double thickness;
+ double fill;
+ direction dir;
+ segment *segment_list;
+ position segment_pos;
+ int segment_is_absolute;
+
+ object_spec(object_type);
+ ~object_spec();
+ object *make_object(position *, direction *);
+ graphic_object *make_box(position *, direction *);
+ graphic_object *make_block(position *, direction *);
+ graphic_object *make_text(position *, direction *);
+ graphic_object *make_ellipse(position *, direction *);
+ graphic_object *make_circle(position *, direction *);
+ linear_object *make_line(position *, direction *);
+ linear_object *make_arc(position *, direction *);
+ graphic_object *make_linear(position *, direction *);
+ graphic_object *make_move(position *, direction *);
+ int position_rectangle(rectangle_object *p, position *curpos,
+ direction *dirp);
+};
+
+
+object *make_object(object_spec *, position *, direction *);
+
+object *make_mark_object();
+object *make_command_object(char *, const char *, int);
+
+int lookup_variable(const char *name, double *val);
+void define_variable(const char *name, double val);
+
+void print_picture(object *);
+
diff --git a/contrib/groff/src/preproc/pic/output.h b/contrib/groff/src/preproc/pic/output.h
new file mode 100644
index 000000000000..ac490db463e3
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/output.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct line_type {
+ enum { invisible, solid, dotted, dashed } type;
+ double dash_width;
+ double thickness; // the thickness is in points
+
+ line_type();
+};
+
+
+class output {
+protected:
+ char *args;
+ double desired_height; // zero if no height specified
+ double desired_width; // zero if no depth specified
+ double compute_scale(double, const position &, const position &);
+public:
+ output();
+ virtual ~output();
+ void set_desired_width_height(double wid, double ht);
+ void set_args(const char *);
+ virtual void start_picture(double sc, const position &ll, const position &ur) = 0;
+ virtual void finish_picture() = 0;
+ virtual void circle(const position &, double rad,
+ const line_type &, double) = 0;
+ virtual void text(const position &, text_piece *, int, double) = 0;
+ virtual void line(const position &, const position *, int n,
+ const line_type &) = 0;
+ virtual void polygon(const position *, int n,
+ const line_type &, double) = 0;
+ virtual void spline(const position &, const position *, int n,
+ const line_type &) = 0;
+ virtual void arc(const position &, const position &, const position &,
+ const line_type &) = 0;
+ virtual void ellipse(const position &, const distance &,
+ const line_type &, double) = 0;
+ virtual void rounded_box(const position &, const distance &, double,
+ const line_type &, double) = 0;
+ virtual void command(const char *, const char *, int);
+ virtual void set_location(const char *, int);
+ virtual int supports_filled_polygons();
+ virtual void begin_block(const position &ll, const position &ur);
+ virtual void end_block();
+};
+
+extern output *out;
+
+/* #define FIG_SUPPORT 1 */
+#define TEX_SUPPORT 1
+
+output *make_troff_output();
+
+#ifdef TEX_SUPPORT
+output *make_tex_output();
+output *make_tpic_output();
+#endif /* TEX_SUPPORT */
+
+#ifdef FIG_SUPPORT
+output *make_fig_output();
+#endif /* FIG_SUPPORT */
diff --git a/contrib/groff/src/preproc/pic/pic.cc b/contrib/groff/src/preproc/pic/pic.cc
new file mode 100644
index 000000000000..f6d97bb2d202
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/pic.cc
@@ -0,0 +1,5216 @@
+#ifndef lint
+/*static char yysccsid[] = "from: @(#)yaccpar 1.9 (Berkeley) 02/21/93";*/
+static char yyrcsid[] = "$Id: pic.cc,v 1.3 2000/11/14 20:40:28 wlemb Exp $";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#line 20 "/home/cjk/groff/src/preproc/pic/pic.y"
+#include "pic.h"
+#include "ptable.h"
+#include "object.h"
+
+extern int delim_flag;
+extern void do_copy(const char *);
+extern void copy_rest_thru(const char *, const char *);
+extern void copy_file_thru(const char *, const char *, const char *);
+extern void push_body(const char *);
+extern void do_for(char *var, double from, double to,
+ int by_is_multiplicative, double by, char *body);
+extern void do_lookahead();
+
+#ifndef HAVE_FMOD
+extern "C" {
+ double fmod(double, double);
+}
+#endif
+
+#undef rand
+#undef srand
+extern "C" {
+ int rand();
+#ifdef RET_TYPE_SRAND_IS_VOID
+ void srand(unsigned int);
+#else
+ int srand(unsigned int);
+#endif
+}
+
+/* Maximum number of characters produced by printf("%g") */
+#define GDIGITS 14
+
+int yylex();
+void yyerror(const char *);
+
+void reset(const char *nm);
+void reset_all();
+
+place *lookup_label(const char *);
+void define_label(const char *label, const place *pl);
+
+direction current_direction;
+position current_position;
+
+implement_ptable(place)
+
+PTABLE(place) top_table;
+
+PTABLE(place) *current_table = &top_table;
+saved_state *current_saved_state = 0;
+
+object_list olist;
+
+const char *ordinal_postfix(int n);
+const char *object_type_name(object_type type);
+char *format_number(const char *form, double n);
+char *do_sprintf(const char *form, const double *v, int nv);
+
+#line 82 "/home/cjk/groff/src/preproc/pic/pic.y"
+typedef union {
+ char *str;
+ int n;
+ double x;
+ struct { double x, y; } pair;
+ struct { double x; char *body; } if_data;
+ struct { char *str; const char *filename; int lineno; } lstr;
+ struct { double *v; int nv; int maxv; } dv;
+ struct { double val; int is_multiplicative; } by;
+ place pl;
+ object *obj;
+ corner crn;
+ path *pth;
+ object_spec *spec;
+ saved_state *pstate;
+ graphics_state state;
+ object_type obtype;
+} YYSTYPE;
+#line 92 "y.tab.c"
+#define LABEL 257
+#define VARIABLE 258
+#define NUMBER 259
+#define TEXT 260
+#define COMMAND_LINE 261
+#define DELIMITED 262
+#define ORDINAL 263
+#define TH 264
+#define LEFT_ARROW_HEAD 265
+#define RIGHT_ARROW_HEAD 266
+#define DOUBLE_ARROW_HEAD 267
+#define LAST 268
+#define UP 269
+#define DOWN 270
+#define LEFT 271
+#define RIGHT 272
+#define BOX 273
+#define CIRCLE 274
+#define ELLIPSE 275
+#define ARC 276
+#define LINE 277
+#define ARROW 278
+#define MOVE 279
+#define SPLINE 280
+#define HEIGHT 281
+#define RADIUS 282
+#define WIDTH 283
+#define DIAMETER 284
+#define FROM 285
+#define TO 286
+#define AT 287
+#define WITH 288
+#define BY 289
+#define THEN 290
+#define SOLID 291
+#define DOTTED 292
+#define DASHED 293
+#define CHOP 294
+#define SAME 295
+#define INVISIBLE 296
+#define LJUST 297
+#define RJUST 298
+#define ABOVE 299
+#define BELOW 300
+#define OF 301
+#define THE 302
+#define WAY 303
+#define BETWEEN 304
+#define AND 305
+#define HERE 306
+#define DOT_N 307
+#define DOT_E 308
+#define DOT_W 309
+#define DOT_S 310
+#define DOT_NE 311
+#define DOT_SE 312
+#define DOT_NW 313
+#define DOT_SW 314
+#define DOT_C 315
+#define DOT_START 316
+#define DOT_END 317
+#define DOT_X 318
+#define DOT_Y 319
+#define DOT_HT 320
+#define DOT_WID 321
+#define DOT_RAD 322
+#define SIN 323
+#define COS 324
+#define ATAN2 325
+#define LOG 326
+#define EXP 327
+#define SQRT 328
+#define K_MAX 329
+#define K_MIN 330
+#define INT 331
+#define RAND 332
+#define SRAND 333
+#define COPY 334
+#define THRU 335
+#define TOP 336
+#define BOTTOM 337
+#define UPPER 338
+#define LOWER 339
+#define SH 340
+#define PRINT 341
+#define CW 342
+#define CCW 343
+#define FOR 344
+#define DO 345
+#define IF 346
+#define ELSE 347
+#define ANDAND 348
+#define OROR 349
+#define NOTEQUAL 350
+#define EQUALEQUAL 351
+#define LESSEQUAL 352
+#define GREATEREQUAL 353
+#define LEFT_CORNER 354
+#define RIGHT_CORNER 355
+#define CENTER 356
+#define END 357
+#define START 358
+#define RESET 359
+#define UNTIL 360
+#define PLOT 361
+#define THICKNESS 362
+#define FILL 363
+#define ALIGNED 364
+#define SPRINTF 365
+#define COMMAND 366
+#define DEFINE 367
+#define UNDEF 368
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 0, 16, 17, 17, 28, 28, 29, 29, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 31, 30,
+ 30, 32, 33, 30, 34, 35, 30, 36, 30, 30,
+ 37, 30, 30, 30, 38, 38, 38, 26, 26, 27,
+ 27, 27, 39, 7, 23, 23, 2, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
+ 15, 15, 15, 15, 40, 42, 15, 15, 41, 41,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 43, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 25, 25, 24, 24, 19,
+ 19, 6, 6, 6, 6, 6, 6, 44, 44, 5,
+ 5, 13, 13, 13, 13, 13, 14, 14, 14, 22,
+ 22, 21, 21, 8, 8, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 11, 11, 12, 12, 12, 10,
+ 10, 10, 10, 10, 10, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+short yylen[] = { 2,
+ 1, 1, 3, 1, 3, 0, 1, 1, 2, 3,
+ 4, 1, 1, 1, 1, 1, 2, 2, 0, 3,
+ 2, 0, 0, 7, 0, 0, 6, 0, 10, 1,
+ 0, 4, 1, 1, 2, 2, 3, 1, 2, 1,
+ 1, 1, 0, 5, 0, 2, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 0, 2, 3,
+ 1, 4, 4, 4, 0, 0, 6, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 3, 0, 4, 3, 3, 3, 3, 2, 2, 3,
+ 2, 3, 2, 3, 2, 3, 3, 3, 3, 3,
+ 3, 2, 2, 2, 3, 2, 3, 2, 3, 2,
+ 3, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 3, 2, 1, 5, 0, 3, 1,
+ 1, 1, 3, 3, 5, 5, 6, 1, 4, 3,
+ 3, 1, 2, 2, 3, 1, 1, 1, 3, 1,
+ 3, 1, 2, 2, 2, 1, 1, 1, 1, 1,
+ 1, 1, 2, 1, 2, 3, 1, 1, 2, 1,
+ 5, 4, 3, 3, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 1, 1, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 2, 3, 4, 4,
+ 6, 4, 4, 4, 6, 6, 4, 4, 3, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 2,
+};
+short yydefred[] = { 0,
+ 8, 0, 2, 0, 0, 0, 0, 126, 16, 12,
+ 13, 14, 15, 71, 72, 73, 74, 75, 76, 77,
+ 78, 0, 19, 0, 0, 0, 0, 0, 0, 0,
+ 65, 82, 0, 4, 0, 0, 79, 68, 0, 9,
+ 0, 0, 0, 0, 25, 0, 147, 204, 205, 150,
+ 152, 189, 190, 146, 176, 177, 178, 179, 180, 181,
+ 182, 183, 184, 185, 186, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 187, 188, 0, 0,
+ 195, 196, 201, 203, 202, 0, 0, 0, 0, 0,
+ 132, 130, 148, 0, 0, 0, 0, 0, 0, 41,
+ 0, 38, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 0, 0, 0, 0, 0, 31, 3, 0, 114,
+ 115, 116, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 102, 103, 0, 0, 0,
+ 112, 113, 120, 121, 122, 123, 117, 118, 0, 0,
+ 125, 0, 119, 36, 0, 0, 10, 0, 22, 0,
+ 20, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 191, 193, 197, 199, 192, 194, 198, 200,
+ 0, 0, 0, 0, 0, 0, 0, 0, 138, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 206, 207, 208,
+ 209, 210, 0, 143, 0, 0, 164, 156, 157, 158,
+ 159, 160, 161, 162, 0, 155, 153, 154, 39, 0,
+ 0, 57, 0, 0, 0, 43, 0, 0, 0, 0,
+ 81, 128, 0, 0, 0, 0, 5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 167, 100, 0, 170, 0, 0,
+ 101, 0, 0, 0, 0, 0, 37, 0, 0, 0,
+ 0, 0, 0, 62, 0, 11, 0, 26, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 229, 0, 0,
+ 218, 141, 0, 151, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 149, 133, 134, 163, 0, 0, 53,
+ 0, 0, 0, 0, 0, 51, 0, 0, 50, 49,
+ 0, 66, 83, 32, 175, 0, 0, 0, 0, 165,
+ 0, 169, 0, 0, 23, 0, 219, 220, 0, 222,
+ 223, 224, 0, 0, 227, 228, 230, 0, 0, 0,
+ 0, 0, 44, 0, 127, 0, 0, 174, 173, 0,
+ 166, 0, 0, 27, 0, 0, 0, 135, 139, 0,
+ 0, 0, 0, 70, 67, 172, 0, 24, 46, 221,
+ 225, 226, 137, 0, 0, 171, 0, 0, 28, 0,
+ 0, 29,
+};
+short yydgoto[] = { 2,
+ 106, 182, 108, 405, 91, 92, 33, 93, 94, 266,
+ 267, 268, 109, 96, 34, 3, 35, 36, 97, 226,
+ 98, 99, 384, 341, 110, 101, 102, 244, 5, 38,
+ 46, 287, 382, 160, 356, 411, 246, 39, 334, 115,
+ 395, 376, 116, 205,
+};
+short yysindex[] = { 25,
+ 0, 0, 0,11784, 59, -47, -5, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -250, 0,10817, -218,10934, -197,11387, 90,10817,
+ 0, 0, -214, 0, 25,10516, 0, 0, -36, 0,
+ 25,10934, 75, -192, 0, -124, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 106, 107, 110, 111, 113,
+ 120, 122, 124, 143, 146, 153, 0, 0, -213, -41,
+ 0, 0, 0, 0, 0,11061,10934,11387,11387, 635,
+ 0, 0, 0, -61, -64, 2542, 44, 834, 356, 0,
+10817, 0, 133,10934,10934, 1378, -91, -294, -64, -279,
+ 0, -28, -52,10817, 25, 25, 0, 0,11406, 0,
+ 0, 0,11694,11694,11694,11694,11387,11387,11387,11387,
+11505,11505,11505, 3348,11609, 0, 0,11694,11694,11694,
+ 0, 0, 0, 0, 0, 0, 0, 0,11387,11694,
+ 0, 1491, 0, 0, -33,10189, 0,10934, 0, -46,
+ 0,10934,10934,10934,10934,10934,10934,10934,10934,10934,
+10633,10934, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1524, 194, 195, -23, -26, 150, 150, -56, 0,11387,
+11387,11387,11387,11387,11387,11387,11505,11387,11387,11387,
+11387,11387,11387,11387,11505, -29, 213, 0, 0, 0,
+ 0, 0, -7, 0,11609,11609, 0, 0, 0, 0,
+ 0, 0, 0, 0, 167, 0, 0, 0, 0,11387,
+ 150, 0,10934,10934,11387, 0,10934,10934, -230, -230,
+ 0, 0, 139,11784, 177, 11, 0, 1491, 1491, 1491,
+ 1491, 1491, 1491, 1491, 1491, 635, 44, 44, 44, 2101,
+ 432, 834, 2101, 17, 0, 0, 2435, 0,11178, 623,
+ 0, 1491, 1491, 1491, 1491, 1491, 0, -47, -5, 0,
+ 0, 0, -64, 0, 44, 0, 18, 0, 240, 246,
+ 244, 248, 249, 250, 252, 253, 257, 0, 260, 262,
+ 0, 0,11505, 0, 1, 1953, 1821, 158, 158, 4,
+ 4, 1491, -19, 48, 4, 200, 200, 150, 150, 150,
+ 150, -42, 213, 0, 0, 0, 0, 1044, 1953, 0,
+ 1924, -43, 4, 46, 1953, 0, 1924, -43, 0, 0,
+ 19, 0, 0, 0, 0, 834, 2101, 2101, 266, 0,
+ 49, 0, 1079, 195, 0, -49, 0, 0,10934, 0,
+ 0, 0,10934,10934, 0, 0, 0, 33, 8,11505,
+11505,11387, 0,11387, 0,11784, 2101, 0, 0, 2101,
+ 0, -49, 55, 0, 275, 277, 290, 0, 0, 7,
+ 44, 1484, 1491, 0, 0, 0, 293, 0, 0, 0,
+ 0, 0, 0,11283, -10, 0,11387, 1491, 0, 1491,
+ 74, 0,
+};
+short yyrindex[] = { 204,
+ 0, 0, 0, 338, 94, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 36, 0, 0, 0,
+ 0, 0, 399, 0, 27, 412, 0, 0, 443, 0,
+10384, 0, 0, 454, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 8824,
+ 0, 0, 0, 0, 4444, 8610, 9125, 0, 0, 0,
+ 467, 0, 0, 0, 0, 954, 0, 970, 0, 0,
+ 0,10077, 0, 487,11820,11820, 0, 0, 31, 0,
+ 0, 0, 9459, 9500, 9245, 9351, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9612, 9720, 9761,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9868,
+ 0, 4996, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 299, 0, 114, 0, 0, 456, 566, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3115, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 224, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5297, 5406, 5707,
+ 5816, 6117, 6226, 6527, 6636, 0, 6937, 7046, 7347, 0,
+ 0, 0, 0, 0, 0, 0, 8697, 0, 0, 0,
+ 0, 7456, 7757, 7866, 8167, 8276, 0, 9870, 1967, 3642,
+ 4085, 184, 233, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4001, 4110, 3224, 3558, 2229,
+ 2338, 4887, 9004, 0, 2672, 1786, 1895, 900, 1009, 1343,
+ 1452, 0, 3667, 0, 0, 0, 0, 0, 29, 0,
+ 38, 182, 2781, 0, 96, 0, 468, 578, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 299, 0, 0, 495, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 530, 0, 0, 0, 0,
+ 0, 495, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4553, -4, 39, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -2, 0, -1,
+ 0, 0,
+};
+short yygindex[] = { 0,
+ -24, 480, -89, 0, -18, 189, 0, 0, -48, 0,
+ 0, 354, 3172, -87, -114, -3, 0, 0, 1313, -74,
+ 0, 0, -21, 0, 9, 326, -57, 2, 324, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+};
+#define YYTABLESIZE 12186
+short yytable[] = { 90,
+ 215, 4, 216, 112, 247, 90, 207, 155, 203, 44,
+ 41, 152, 37, 201, 199, 232, 200, 203, 202, 215,
+ 303, 216, 201, 199, 228, 200, 6, 202, 237, 8,
+ 7, 235, 100, 198, 87, 34, 118, 238, 100, 103,
+ 203, 284, 156, 229, 153, 201, 199, 214, 200, 215,
+ 202, 216, 43, 237, 238, 42, 229, 173, 174, 375,
+ 111, 181, 374, 186, 187, 204, 87, 183, 403, 237,
+ 239, 240, 237, 388, 204, 215, 90, 216, 238, 129,
+ 231, 238, 129, 1, 45, 265, 215, 237, 216, 90,
+ 215, 370, 216, 7, 34, 52, 238, 204, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 256, 256, 100,
+ 270, 243, 245, 272, 273, 274, 271, 40, 323, 6,
+ 241, 237, 100, 7, 275, 276, 7, 37, 34, 113,
+ 238, 256, 117, 7, 29, 158, 52, 161, 7, 52,
+ 175, 176, 159, 330, 332, 162, 163, 336, 338, 164,
+ 165, 6, 166, 237, 52, 7, 132, 132, 132, 167,
+ 34, 168, 238, 169, 37, 306, 307, 308, 309, 310,
+ 311, 312, 313, 315, 316, 317, 318, 319, 320, 321,
+ 256, 56, 170, 63, 7, 171, 347, 348, 52, 7,
+ 270, 270, 172, 230, 203, 47, 325, 326, 236, 201,
+ 199, 50, 200, 6, 202, 328, 51, 242, 329, 331,
+ 333, 265, 335, 337, 265, 288, 7, 235, 352, 198,
+ 52, 154, 56, 239, 277, 56, 130, 47, 130, 177,
+ 178, 8, 64, 50, 301, 302, 203, 304, 51, 206,
+ 56, 201, 63, 204, 353, 305, 202, 339, 340, 324,
+ 354, 204, 37, 208, 209, 210, 211, 212, 213, 327,
+ 239, 394, 371, 342, 239, 239, 239, 239, 239, 343,
+ 239, 377, 344, 350, 56, 131, 63, 131, 256, 355,
+ 357, 188, 239, 239, 189, 239, 358, 359, 360, 361,
+ 362, 64, 237, 204, 6, 363, 364, 365, 265, 265,
+ 366, 238, 367, 369, 237, 381, 56, 373, 63, 380,
+ 383, 389, 179, 180, 399, 400, 239, 401, 237, 190,
+ 191, 192, 193, 194, 195, 64, 6, 238, 265, 237,
+ 402, 265, 237, 406, 409, 412, 29, 1, 238, 47,
+ 58, 238, 59, 60, 282, 256, 256, 392, 239, 393,
+ 7, 7, 7, 7, 7, 114, 7, 64, 119, 52,
+ 398, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 0, 0, 237, 237, 0, 408,
+ 0, 0, 410, 0, 37, 52, 238, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 30, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 61, 0, 0, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 0, 7,
+ 7, 7, 7, 7, 7, 0, 0, 7, 0, 7,
+ 0, 0, 33, 52, 52, 56, 225, 7, 7, 7,
+ 7, 7, 7, 21, 7, 217, 0, 30, 7, 7,
+ 6, 6, 0, 6, 6, 0, 18, 55, 0, 0,
+ 61, 56, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 0, 0, 17, 239, 217, 0,
+ 0, 30, 217, 0, 45, 217, 217, 217, 217, 217,
+ 217, 33, 217, 0, 61, 107, 0, 0, 55, 194,
+ 195, 55, 21, 239, 217, 217, 0, 217, 0, 0,
+ 0, 157, 225, 30, 239, 18, 55, 239, 0, 69,
+ 56, 0, 0, 0, 0, 33, 61, 6, 0, 0,
+ 0, 0, 0, 6, 6, 17, 21, 6, 217, 6,
+ 0, 217, 0, 45, 0, 0, 0, 0, 0, 18,
+ 55, 0, 6, 0, 6, 239, 185, 33, 6, 6,
+ 0, 239, 239, 239, 239, 239, 239, 54, 21, 17,
+ 217, 0, 0, 0, 0, 0, 0, 45, 69, 0,
+ 0, 18, 55, 0, 0, 0, 0, 0, 239, 0,
+ 0, 0, 239, 0, 0, 239, 239, 239, 239, 239,
+ 239, 17, 239, 345, 0, 217, 349, 0, 54, 45,
+ 0, 54, 69, 227, 239, 239, 0, 239, 218, 219,
+ 220, 221, 222, 223, 0, 224, 54, 286, 0, 0,
+ 0, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 299, 300, 0, 0, 69, 0, 0, 0, 239, 203,
+ 0, 239, 0, 0, 201, 199, 196, 200, 0, 202,
+ 54, 203, 0, 0, 0, 0, 201, 199, 196, 200,
+ 0, 202, 235, 0, 198, 0, 0, 0, 0, 0,
+ 239, 217, 0, 0, 197, 0, 198, 0, 0, 346,
+ 378, 379, 54, 0, 218, 219, 220, 221, 222, 223,
+ 0, 224, 217, 217, 217, 217, 204, 0, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217, 204, 0,
+ 396, 55, 0, 397, 0, 0, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 55, 0, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 0, 0, 0, 0, 0, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217, 217, 0,
+ 0, 217, 217, 217, 217, 0, 0, 217, 217, 0,
+ 217, 0, 0, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 0, 0, 55, 217, 217, 217,
+ 217, 0, 239, 239, 239, 239, 0, 0, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239, 385, 0,
+ 0, 54, 386, 387, 0, 0, 239, 239, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 54, 0, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 0, 0, 0, 0, 0, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239, 239, 213,
+ 0, 239, 239, 239, 239, 0, 0, 239, 239, 0,
+ 239, 0, 0, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 225, 0, 54, 239, 239, 239,
+ 239, 0, 213, 0, 0, 188, 213, 0, 189, 213,
+ 213, 213, 213, 213, 213, 0, 213, 0, 0, 0,
+ 0, 0, 0, 47, 0, 0, 0, 0, 213, 213,
+ 0, 213, 0, 0, 0, 0, 0, 0, 0, 48,
+ 190, 191, 192, 193, 194, 195, 0, 0, 0, 0,
+ 0, 0, 190, 191, 192, 193, 194, 195, 0, 0,
+ 0, 0, 213, 0, 47, 213, 0, 47, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 214, 0,
+ 48, 0, 47, 48, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 213, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 214, 0, 0, 0, 214, 47, 0, 214, 214,
+ 214, 214, 214, 214, 0, 214, 0, 0, 0, 0,
+ 0, 0, 48, 0, 0, 0, 0, 214, 214, 0,
+ 214, 0, 0, 0, 0, 0, 0, 0, 47, 0,
+ 203, 0, 0, 0, 0, 201, 199, 0, 200, 0,
+ 202, 0, 0, 217, 48, 0, 0, 0, 0, 0,
+ 0, 214, 0, 235, 214, 198, 218, 219, 220, 221,
+ 222, 223, 0, 224, 0, 203, 0, 0, 0, 0,
+ 201, 199, 196, 200, 0, 202, 0, 0, 0, 0,
+ 0, 0, 0, 214, 0, 0, 0, 204, 235, 0,
+ 198, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 213, 213, 213, 213,
+ 0, 0, 213, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 204, 0, 0, 0, 0, 0, 0, 0,
+ 213, 213, 213, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 213, 213, 213, 213, 213, 213, 213, 213,
+ 213, 0, 0, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 213, 213, 213, 213, 213, 47, 0, 0,
+ 0, 0, 213, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 213, 48, 0, 213, 213, 213, 213, 0,
+ 0, 213, 213, 47, 213, 0, 0, 213, 213, 213,
+ 213, 213, 213, 213, 213, 213, 213, 213, 0, 48,
+ 0, 213, 213, 213, 213, 214, 214, 214, 214, 0,
+ 0, 214, 214, 214, 214, 214, 214, 214, 214, 214,
+ 214, 0, 0, 0, 0, 0, 0, 0, 0, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214, 214, 214,
+ 0, 0, 214, 214, 214, 214, 214, 214, 214, 214,
+ 214, 214, 214, 214, 214, 214, 0, 0, 0, 372,
+ 0, 214, 214, 214, 214, 214, 214, 214, 214, 214,
+ 214, 214, 215, 0, 214, 214, 214, 214, 0, 0,
+ 214, 214, 0, 214, 0, 0, 214, 214, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 0, 0, 0,
+ 214, 214, 214, 214, 0, 215, 0, 0, 0, 215,
+ 0, 0, 215, 215, 215, 215, 215, 215, 0, 215,
+ 0, 190, 191, 192, 193, 194, 195, 0, 184, 0,
+ 0, 215, 215, 0, 215, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 203, 0, 0, 0, 0, 201,
+ 199, 0, 200, 0, 202, 0, 233, 234, 192, 193,
+ 194, 195, 0, 0, 0, 215, 0, 235, 215, 198,
+ 0, 0, 0, 257, 258, 259, 0, 0, 0, 0,
+ 0, 216, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 215, 285, 0,
+ 0, 204, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 216, 0, 0, 0, 216, 0,
+ 0, 216, 216, 216, 216, 216, 216, 0, 216, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 314,
+ 216, 216, 0, 216, 0, 0, 0, 322, 0, 0,
+ 203, 0, 0, 0, 0, 201, 199, 203, 200, 0,
+ 202, 0, 201, 199, 0, 200, 0, 202, 0, 0,
+ 0, 0, 0, 235, 216, 198, 0, 216, 0, 0,
+ 235, 0, 198, 0, 0, 0, 0, 0, 0, 0,
+ 203, 0, 0, 0, 0, 201, 199, 196, 200, 0,
+ 202, 0, 0, 0, 0, 0, 216, 204, 0, 0,
+ 0, 0, 0, 197, 204, 198, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 215,
+ 215, 215, 215, 0, 0, 215, 215, 215, 215, 215,
+ 215, 215, 215, 215, 215, 368, 0, 204, 0, 0,
+ 0, 0, 0, 215, 215, 215, 215, 215, 215, 215,
+ 215, 215, 215, 215, 215, 215, 215, 215, 215, 215,
+ 215, 215, 215, 215, 0, 0, 215, 215, 215, 215,
+ 215, 215, 215, 215, 215, 215, 215, 215, 215, 215,
+ 0, 0, 0, 0, 0, 215, 215, 215, 215, 215,
+ 215, 215, 215, 215, 215, 215, 0, 0, 215, 215,
+ 215, 215, 390, 391, 215, 215, 0, 215, 0, 0,
+ 215, 215, 215, 215, 215, 215, 215, 215, 215, 215,
+ 215, 0, 0, 0, 215, 215, 215, 215, 216, 216,
+ 216, 216, 0, 0, 216, 216, 216, 216, 216, 216,
+ 216, 216, 216, 216, 0, 233, 234, 192, 193, 194,
+ 195, 0, 216, 216, 216, 216, 216, 216, 216, 216,
+ 216, 216, 216, 216, 216, 216, 216, 216, 216, 216,
+ 216, 216, 216, 0, 0, 216, 216, 216, 216, 216,
+ 216, 216, 216, 216, 216, 216, 216, 216, 216, 0,
+ 0, 0, 404, 0, 216, 216, 216, 216, 216, 216,
+ 216, 216, 216, 216, 216, 211, 0, 216, 216, 216,
+ 216, 0, 0, 216, 216, 0, 216, 0, 0, 216,
+ 216, 216, 216, 216, 216, 216, 216, 216, 216, 216,
+ 0, 0, 0, 216, 216, 216, 216, 0, 211, 0,
+ 0, 0, 0, 0, 188, 211, 211, 189, 211, 211,
+ 211, 190, 191, 192, 193, 194, 195, 0, 190, 191,
+ 192, 193, 194, 195, 211, 211, 0, 211, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 203, 0, 0,
+ 0, 0, 201, 199, 0, 200, 0, 202, 0, 0,
+ 0, 233, 234, 192, 193, 194, 195, 0, 211, 0,
+ 235, 211, 198, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 212, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 211, 0, 0, 0, 204, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 212, 0, 0,
+ 0, 0, 0, 0, 212, 212, 0, 212, 212, 212,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 212, 212, 0, 212, 0, 0, 0,
+ 203, 0, 0, 0, 0, 201, 199, 0, 200, 0,
+ 202, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 235, 0, 198, 0, 212, 0, 203,
+ 212, 0, 0, 0, 201, 199, 0, 200, 0, 202,
+ 0, 0, 0, 204, 0, 0, 0, 0, 204, 204,
+ 204, 204, 235, 204, 198, 0, 0, 204, 0, 212,
+ 0, 0, 0, 0, 0, 0, 204, 0, 204, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 211, 211, 211, 211, 204, 0, 211, 211,
+ 211, 211, 211, 211, 211, 211, 211, 211, 0, 0,
+ 204, 0, 0, 0, 0, 0, 211, 211, 211, 211,
+ 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
+ 211, 211, 211, 211, 211, 211, 211, 0, 0, 211,
+ 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
+ 211, 211, 211, 0, 0, 0, 0, 0, 211, 211,
+ 211, 211, 211, 211, 211, 211, 211, 211, 211, 0,
+ 0, 211, 211, 211, 211, 0, 0, 211, 211, 0,
+ 211, 0, 0, 211, 211, 211, 211, 211, 211, 211,
+ 211, 211, 211, 211, 0, 0, 264, 211, 211, 211,
+ 211, 212, 212, 212, 212, 0, 0, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 0, 190, 0,
+ 192, 193, 194, 195, 0, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 0, 0, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 0, 0, 0, 0, 0, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212, 232, 0,
+ 212, 212, 212, 212, 0, 0, 212, 212, 0, 212,
+ 0, 0, 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 0, 0, 0, 212, 212, 212, 212,
+ 0, 232, 0, 0, 0, 0, 0, 204, 232, 232,
+ 204, 233, 232, 192, 193, 194, 195, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 232, 232, 0,
+ 232, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 192, 193, 194, 195, 0, 0, 0, 0,
+ 0, 0, 0, 0, 204, 204, 204, 204, 204, 204,
+ 0, 232, 0, 0, 232, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 234, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 232, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 234, 52, 53, 0, 0, 0, 0, 234, 234, 0,
+ 0, 234, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 234, 234, 0, 234,
+ 0, 0, 0, 0, 0, 0, 0, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 234, 0, 0, 234, 0, 0, 77, 78, 79, 80,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 82, 83, 84, 85, 0,
+ 0, 0, 234, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 351, 0, 0, 0, 0, 232, 232, 232, 232, 0,
+ 0, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 0, 0, 0, 0, 0, 0, 0, 0, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 0, 0, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 0, 0, 0, 0,
+ 0, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 0, 0, 232, 232, 232, 232, 0, 0,
+ 232, 232, 0, 232, 0, 0, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 213, 0, 0,
+ 232, 232, 232, 232, 234, 234, 234, 234, 0, 0,
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 0, 0, 0, 0, 0, 0, 0, 0, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 0,
+ 0, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 0, 0, 0, 0, 0,
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 233, 0, 234, 234, 234, 234, 0, 0, 234,
+ 234, 0, 234, 0, 0, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 0, 0, 0, 234,
+ 234, 234, 234, 0, 233, 52, 53, 0, 0, 0,
+ 0, 233, 233, 0, 0, 233, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 233, 233, 0, 233, 0, 0, 0, 0, 0, 0,
+ 0, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 233, 0, 0, 233, 0, 0,
+ 77, 78, 79, 80, 0, 0, 0, 0, 0, 0,
+ 231, 0, 0, 0, 0, 0, 0, 0, 81, 82,
+ 83, 84, 85, 0, 0, 0, 233, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 231, 0, 0, 0, 0, 0, 0,
+ 231, 231, 0, 0, 231, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 231,
+ 231, 0, 231, 0, 0, 0, 0, 0, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 231, 0, 0, 231, 77, 78, 79,
+ 80, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 81, 82, 83, 84, 85,
+ 0, 0, 0, 0, 0, 231, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 233, 233,
+ 233, 233, 0, 0, 233, 233, 233, 233, 233, 233,
+ 233, 233, 233, 233, 0, 0, 0, 0, 0, 0,
+ 0, 0, 233, 233, 233, 233, 233, 233, 233, 233,
+ 233, 233, 233, 233, 233, 233, 233, 233, 233, 233,
+ 233, 233, 233, 0, 0, 233, 233, 233, 233, 233,
+ 233, 233, 233, 233, 233, 233, 233, 233, 233, 0,
+ 0, 0, 0, 0, 233, 233, 233, 233, 233, 233,
+ 233, 233, 233, 233, 233, 0, 0, 233, 233, 233,
+ 233, 0, 0, 233, 233, 0, 233, 0, 0, 233,
+ 233, 233, 233, 233, 233, 233, 233, 233, 233, 233,
+ 0, 0, 0, 233, 233, 233, 233, 231, 231, 231,
+ 231, 0, 0, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 231, 0, 0, 0, 0, 0, 0, 0,
+ 0, 231, 231, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 231, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 0, 0, 231, 231, 231, 231, 231, 231,
+ 231, 231, 231, 231, 231, 231, 231, 231, 0, 0,
+ 0, 0, 0, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 231, 231, 144, 0, 231, 231, 231, 231,
+ 0, 0, 231, 231, 0, 231, 0, 0, 231, 231,
+ 231, 231, 231, 231, 231, 231, 231, 231, 231, 0,
+ 0, 0, 231, 231, 231, 231, 0, 144, 0, 0,
+ 0, 0, 0, 0, 144, 144, 0, 144, 144, 144,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 144, 0, 0, 144, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 95, 0, 0, 0, 0,
+ 0, 95, 0, 0, 0, 0, 0, 144, 0, 0,
+ 144, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 236, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 144,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 236, 95, 0, 0,
+ 0, 0, 0, 236, 236, 0, 0, 236, 0, 0,
+ 0, 0, 95, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 236, 0, 0, 95, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 95, 95, 95, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 236, 0, 0, 236,
+ 0, 0, 0, 0, 0, 0, 0, 283, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 236, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 95, 0,
+ 0, 144, 144, 144, 144, 0, 95, 144, 0, 144,
+ 144, 144, 144, 144, 144, 144, 144, 263, 0, 0,
+ 0, 0, 0, 264, 0, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 0, 0, 0, 0, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144, 0, 0,
+ 144, 144, 144, 144, 0, 0, 144, 144, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 144, 144,
+ 144, 144, 144, 0, 95, 0, 144, 144, 144, 144,
+ 236, 236, 236, 236, 0, 0, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 0, 0, 0, 0,
+ 0, 0, 0, 0, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 0, 0, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 95, 95, 0, 0, 0, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 235, 0, 236,
+ 236, 236, 236, 0, 0, 236, 236, 0, 236, 0,
+ 0, 236, 236, 236, 236, 0, 0, 236, 236, 236,
+ 236, 236, 0, 0, 0, 236, 236, 236, 236, 0,
+ 235, 0, 0, 0, 0, 0, 0, 235, 235, 0,
+ 0, 235, 0, 0, 260, 0, 0, 0, 0, 0,
+ 261, 0, 0, 0, 0, 262, 235, 0, 52, 53,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+ 235, 0, 0, 235, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 0, 145, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 235, 77, 78, 79, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 145,
+ 14, 81, 82, 83, 84, 85, 145, 145, 0, 145,
+ 145, 145, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 145, 0, 0, 145, 0,
+ 0, 0, 0, 0, 14, 0, 0, 189, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 145,
+ 0, 0, 145, 0, 0, 0, 14, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 145, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 235, 235, 235, 235, 0, 0,
+ 235, 235, 235, 235, 235, 235, 235, 235, 235, 235,
+ 0, 0, 0, 0, 0, 0, 0, 0, 235, 235,
+ 235, 235, 235, 235, 235, 235, 235, 235, 235, 235,
+ 235, 235, 235, 235, 235, 235, 235, 235, 235, 0,
+ 0, 235, 235, 235, 235, 235, 235, 235, 235, 235,
+ 235, 235, 235, 235, 235, 0, 0, 0, 0, 0,
+ 235, 235, 235, 235, 235, 235, 235, 235, 235, 235,
+ 235, 0, 0, 235, 235, 235, 235, 0, 189, 235,
+ 235, 0, 235, 0, 189, 235, 235, 235, 235, 189,
+ 0, 235, 235, 235, 235, 235, 0, 0, 0, 235,
+ 235, 235, 235, 145, 145, 145, 145, 0, 0, 145,
+ 0, 145, 145, 145, 145, 145, 145, 145, 145, 0,
+ 0, 0, 189, 0, 0, 0, 0, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 0, 0, 0,
+ 0, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 237, 0, 145, 145, 145, 145, 0, 0, 145, 145,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 145, 145, 145, 145, 145, 0, 0, 0, 145, 145,
+ 145, 145, 0, 237, 0, 0, 0, 0, 0, 0,
+ 237, 237, 0, 0, 237, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 237,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 237, 0, 0, 237, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 238,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 237, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 238, 15, 0, 0, 0, 0, 0, 238,
+ 238, 0, 0, 238, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 238, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 0, 0,
+ 190, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 238, 0, 0, 238, 0, 0, 0, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 238, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 237, 237, 237,
+ 237, 0, 0, 237, 237, 237, 237, 237, 237, 237,
+ 237, 237, 237, 0, 0, 0, 0, 0, 0, 0,
+ 0, 237, 237, 237, 237, 237, 237, 237, 237, 237,
+ 237, 237, 237, 237, 237, 237, 237, 237, 237, 237,
+ 237, 237, 0, 0, 237, 237, 237, 237, 237, 237,
+ 237, 237, 237, 237, 237, 237, 237, 237, 0, 0,
+ 0, 0, 0, 237, 237, 237, 237, 237, 237, 237,
+ 237, 237, 237, 237, 0, 0, 237, 237, 237, 237,
+ 0, 190, 237, 237, 0, 237, 0, 190, 237, 237,
+ 0, 0, 190, 0, 237, 237, 237, 237, 237, 0,
+ 0, 0, 237, 237, 237, 237, 238, 238, 238, 238,
+ 0, 0, 238, 238, 238, 238, 238, 238, 238, 238,
+ 238, 238, 0, 0, 0, 190, 0, 0, 0, 0,
+ 238, 238, 238, 238, 238, 238, 238, 238, 238, 238,
+ 238, 238, 238, 238, 238, 238, 238, 238, 238, 238,
+ 238, 0, 0, 238, 238, 238, 238, 238, 238, 238,
+ 238, 238, 238, 238, 238, 238, 238, 0, 0, 0,
+ 0, 0, 238, 238, 238, 238, 238, 238, 238, 238,
+ 238, 238, 238, 131, 0, 238, 238, 238, 238, 0,
+ 0, 238, 238, 0, 238, 0, 0, 0, 238, 0,
+ 0, 0, 0, 238, 238, 238, 238, 238, 0, 0,
+ 0, 238, 238, 238, 238, 0, 131, 0, 0, 0,
+ 0, 0, 0, 131, 131, 0, 131, 131, 131, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 131, 0, 0, 131, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 131, 0, 0, 131,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 136, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 131, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 136, 0, 0, 0, 0,
+ 0, 0, 136, 136, 0, 0, 136, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 136, 0, 0, 136, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 136, 0, 0, 136, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 136, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 131, 131, 131, 131, 0, 0, 131, 0, 131, 131,
+ 131, 131, 131, 131, 131, 131, 0, 0, 0, 0,
+ 0, 0, 0, 0, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 0, 0, 0, 0, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 0, 0, 0, 0, 0, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 0, 0, 131,
+ 131, 131, 131, 0, 0, 131, 131, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 131, 131, 131,
+ 131, 131, 0, 0, 0, 131, 131, 131, 131, 136,
+ 136, 136, 136, 0, 0, 136, 0, 136, 136, 136,
+ 136, 136, 136, 136, 136, 0, 0, 0, 0, 0,
+ 0, 0, 0, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 0, 0, 0, 0, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 0, 0, 0, 0, 0, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 140, 0, 136, 136,
+ 136, 136, 0, 0, 136, 136, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 136, 136, 136, 136,
+ 136, 0, 0, 0, 136, 136, 136, 136, 0, 140,
+ 0, 0, 0, 0, 0, 0, 140, 140, 0, 0,
+ 140, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 140, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 140,
+ 0, 0, 140, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 88, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 140, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 88, 0,
+ 0, 0, 0, 0, 0, 88, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 88, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 88, 0,
+ 0, 88, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 140, 140, 140, 140, 0, 0, 140,
+ 0, 140, 140, 140, 140, 140, 140, 140, 140, 0,
+ 0, 0, 0, 0, 0, 0, 0, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 0, 0, 0,
+ 0, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 0, 0, 0, 0, 0, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 0, 0, 140, 140, 140, 140, 0, 0, 140, 140,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 140, 140, 140, 140, 140, 0, 0, 0, 140, 140,
+ 140, 140, 88, 88, 88, 88, 0, 0, 88, 0,
+ 88, 88, 88, 88, 88, 88, 88, 88, 0, 0,
+ 0, 0, 0, 0, 0, 0, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 90, 0, 0, 0,
+ 0, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 0, 0, 0, 0, 0, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 90,
+ 0, 88, 88, 88, 88, 0, 90, 88, 88, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 88,
+ 88, 88, 88, 88, 0, 90, 0, 88, 88, 88,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 90,
+ 0, 0, 90, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 92, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 90, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 92, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 92, 0,
+ 0, 92, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 90, 90, 90, 90, 0, 0, 90,
+ 0, 90, 90, 90, 90, 90, 90, 90, 90, 0,
+ 0, 0, 0, 0, 0, 0, 0, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 0, 0, 0,
+ 0, 0, 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 0, 0, 0, 0, 0, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 0, 0, 90, 90, 90, 90, 0, 0, 90, 90,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 90, 90, 90, 90, 90, 0, 0, 0, 90, 90,
+ 90, 90, 92, 92, 92, 92, 0, 0, 92, 0,
+ 92, 92, 92, 92, 92, 92, 92, 92, 0, 0,
+ 0, 0, 0, 0, 0, 0, 92, 92, 92, 92,
+ 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
+ 92, 92, 92, 92, 92, 92, 96, 0, 0, 0,
+ 0, 92, 92, 92, 92, 92, 92, 92, 92, 92,
+ 92, 92, 92, 0, 0, 0, 0, 0, 92, 92,
+ 92, 92, 92, 92, 92, 92, 92, 92, 92, 96,
+ 0, 92, 92, 92, 92, 0, 96, 92, 92, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 92,
+ 92, 92, 92, 92, 0, 96, 0, 92, 92, 92,
+ 92, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,
+ 0, 0, 96, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 94, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 96, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,
+ 0, 0, 0, 0, 0, 94, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 94, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,
+ 0, 94, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 94, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 96, 96, 96, 96, 0, 0, 96,
+ 0, 96, 96, 96, 96, 96, 96, 96, 96, 0,
+ 0, 0, 0, 0, 0, 0, 0, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 0, 0, 0,
+ 0, 0, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 0, 0, 0, 0, 0, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 0, 0, 96, 96, 96, 96, 0, 0, 96, 96,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 96, 96, 96, 96, 96, 0, 0, 0, 96, 96,
+ 96, 96, 94, 94, 94, 94, 0, 0, 94, 0,
+ 94, 94, 94, 94, 94, 94, 94, 94, 0, 0,
+ 0, 0, 0, 0, 0, 0, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 84, 0, 0, 0,
+ 0, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 0, 0, 0, 0, 0, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 84,
+ 0, 94, 94, 94, 94, 0, 84, 94, 94, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 94,
+ 94, 94, 94, 94, 0, 84, 0, 94, 94, 94,
+ 94, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,
+ 0, 0, 84, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 84, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 85, 0,
+ 0, 0, 0, 0, 0, 85, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 85, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 85, 0,
+ 0, 85, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 84, 84, 84, 0, 0, 84,
+ 0, 84, 84, 84, 84, 84, 84, 84, 84, 0,
+ 0, 0, 0, 0, 0, 0, 0, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 0, 0, 0,
+ 0, 0, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 0, 0, 0, 0, 0, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 0, 0, 84, 84, 84, 84, 0, 0, 84, 84,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 84, 84, 84, 84, 84, 0, 0, 0, 84, 84,
+ 84, 84, 85, 85, 85, 85, 0, 0, 85, 0,
+ 85, 85, 85, 85, 85, 85, 85, 85, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 86, 0, 0, 0,
+ 0, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 0, 0, 0, 0, 0, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 86,
+ 0, 85, 85, 85, 85, 0, 86, 85, 85, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 85,
+ 85, 85, 85, 85, 0, 86, 0, 85, 85, 85,
+ 85, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 86,
+ 0, 0, 86, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 87, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 86, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 87, 0,
+ 0, 0, 0, 0, 0, 87, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 87, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 87, 0,
+ 0, 87, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 87, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 86, 86, 86, 86, 0, 0, 86,
+ 0, 86, 86, 86, 86, 86, 86, 86, 86, 0,
+ 0, 0, 0, 0, 0, 0, 0, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 0, 0, 0,
+ 0, 0, 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 0, 0, 0, 0, 0, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 0, 0, 86, 86, 86, 86, 0, 0, 86, 86,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 86, 86, 86, 86, 86, 0, 0, 0, 86, 86,
+ 86, 86, 87, 87, 87, 87, 0, 0, 87, 0,
+ 87, 87, 87, 87, 87, 87, 87, 87, 0, 0,
+ 0, 0, 0, 0, 0, 0, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 97, 0, 0, 0,
+ 0, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 0, 0, 0, 0, 0, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 97,
+ 0, 87, 87, 87, 87, 0, 97, 87, 87, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 87,
+ 87, 87, 87, 87, 0, 97, 0, 87, 87, 87,
+ 87, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,
+ 0, 0, 97, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 98, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 97, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 98, 0,
+ 0, 0, 0, 0, 0, 98, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 98, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 98, 0,
+ 0, 98, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 98, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 97, 97, 97, 97, 0, 0, 97,
+ 0, 97, 97, 97, 97, 97, 97, 97, 97, 0,
+ 0, 0, 0, 0, 0, 0, 0, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 0, 0, 0,
+ 0, 0, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 0, 0, 0, 0, 0, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 0, 0, 97, 97, 97, 97, 0, 0, 97, 97,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 97, 97, 97, 97, 97, 0, 0, 0, 97, 97,
+ 97, 97, 98, 98, 98, 98, 0, 0, 98, 0,
+ 98, 98, 98, 98, 98, 98, 98, 98, 0, 0,
+ 0, 0, 0, 0, 0, 0, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 99, 0, 0, 0,
+ 0, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 0, 0, 0, 0, 0, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98, 98, 99,
+ 0, 98, 98, 98, 98, 0, 99, 98, 98, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 98,
+ 98, 98, 98, 98, 0, 99, 0, 98, 98, 98,
+ 98, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 99,
+ 0, 0, 99, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 105, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 99, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 105, 0,
+ 0, 0, 0, 0, 0, 105, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 105, 0,
+ 0, 105, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 105, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 99, 99, 99, 0, 0, 99,
+ 0, 99, 99, 99, 99, 99, 99, 99, 99, 0,
+ 0, 0, 0, 0, 0, 0, 0, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 0, 0, 0,
+ 0, 0, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 0, 0, 0, 0, 0, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 0, 0, 99, 99, 99, 99, 0, 0, 99, 99,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 99, 99, 99, 99, 99, 0, 0, 0, 99, 99,
+ 99, 99, 105, 105, 105, 105, 0, 0, 105, 0,
+ 105, 105, 105, 105, 105, 105, 105, 105, 0, 0,
+ 0, 0, 0, 0, 0, 0, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 107, 0, 0, 0,
+ 0, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 0, 0, 0, 0, 0, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 107,
+ 0, 105, 105, 105, 105, 0, 107, 105, 105, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 105,
+ 105, 105, 105, 105, 0, 107, 0, 105, 105, 105,
+ 105, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 107,
+ 0, 0, 107, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 111, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 107, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 111, 0,
+ 0, 0, 0, 0, 0, 111, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 111, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 111, 0,
+ 0, 111, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 111, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 107, 107, 107, 107, 0, 0, 107,
+ 0, 107, 107, 107, 107, 107, 107, 107, 107, 0,
+ 0, 0, 0, 0, 0, 0, 0, 107, 107, 107,
+ 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 107, 107, 107, 107, 107, 107, 0, 0, 0,
+ 0, 0, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 107, 107, 107, 0, 0, 0, 0, 0, 107,
+ 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 0, 0, 107, 107, 107, 107, 0, 0, 107, 107,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 107, 107, 107, 107, 107, 0, 0, 0, 107, 107,
+ 107, 107, 111, 111, 111, 111, 0, 0, 111, 0,
+ 111, 111, 111, 111, 111, 111, 111, 111, 0, 0,
+ 0, 0, 0, 0, 0, 0, 111, 111, 111, 111,
+ 111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
+ 111, 111, 111, 111, 111, 111, 124, 0, 0, 0,
+ 0, 111, 111, 111, 111, 111, 111, 111, 111, 111,
+ 111, 111, 111, 0, 0, 0, 0, 0, 111, 111,
+ 111, 111, 111, 111, 111, 111, 111, 111, 111, 124,
+ 0, 111, 111, 111, 111, 0, 124, 111, 111, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 111,
+ 111, 111, 111, 111, 0, 124, 0, 111, 111, 111,
+ 111, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 124,
+ 0, 0, 124, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 109, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 124, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 109, 0,
+ 0, 0, 0, 0, 0, 109, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 109, 0,
+ 0, 109, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 109, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 124, 124, 124, 124, 0, 0, 124,
+ 0, 124, 124, 124, 124, 124, 124, 124, 124, 0,
+ 0, 0, 0, 0, 0, 0, 0, 124, 124, 124,
+ 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+ 124, 124, 124, 124, 124, 124, 124, 0, 0, 0,
+ 0, 0, 124, 124, 124, 124, 124, 124, 124, 124,
+ 124, 124, 124, 124, 0, 0, 0, 0, 0, 124,
+ 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+ 0, 0, 124, 124, 124, 124, 0, 0, 124, 124,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 124, 124, 124, 124, 124, 0, 0, 0, 124, 124,
+ 124, 124, 109, 109, 109, 109, 0, 0, 109, 0,
+ 109, 109, 109, 109, 109, 109, 109, 109, 0, 0,
+ 0, 0, 0, 0, 0, 0, 109, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 0, 0, 0, 0,
+ 0, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 0, 0, 0, 0, 0, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 109, 109, 142,
+ 0, 109, 109, 109, 109, 0, 0, 109, 109, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 109,
+ 109, 109, 109, 109, 0, 0, 0, 109, 109, 109,
+ 109, 0, 142, 0, 0, 0, 0, 0, 0, 142,
+ 142, 0, 142, 142, 142, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 142, 0,
+ 0, 142, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 168, 0, 0, 0,
+ 0, 0, 142, 0, 0, 142, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 168,
+ 0, 0, 0, 0, 142, 0, 168, 168, 0, 0,
+ 168, 168, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 168, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 168,
+ 0, 0, 168, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 168, 0, 40, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 40, 0, 0, 0,
+ 0, 0, 0, 40, 0, 0, 142, 142, 142, 142,
+ 0, 0, 142, 0, 142, 142, 142, 142, 142, 142,
+ 142, 142, 40, 0, 0, 0, 0, 0, 0, 0,
+ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142,
+ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142,
+ 0, 0, 0, 0, 142, 142, 40, 0, 0, 40,
+ 0, 0, 0, 0, 0, 0, 0, 142, 142, 142,
+ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142,
+ 142, 142, 142, 0, 0, 0, 0, 0, 40, 0,
+ 0, 142, 142, 168, 168, 168, 168, 0, 0, 168,
+ 0, 168, 168, 168, 168, 168, 168, 0, 0, 0,
+ 0, 142, 142, 142, 142, 0, 0, 168, 168, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 168, 168, 168, 168, 168, 168, 168, 0, 0, 0,
+ 0, 0, 168, 231, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 0, 0, 0, 0, 0, 0, 231, 0, 168, 168,
+ 0, 0, 0, 231, 231, 0, 0, 231, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 168, 168,
+ 168, 168, 231, 231, 0, 231, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 40, 40, 40, 40, 0, 0, 40, 0, 0, 0,
+ 0, 40, 0, 0, 40, 40, 231, 0, 0, 231,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 42, 0, 0, 0, 231, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 0, 0, 0, 0, 0, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 42, 0, 40,
+ 40, 40, 40, 0, 42, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 40, 40, 40,
+ 40, 40, 0, 42, 0, 0, 0, 0, 40, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 42, 0, 0,
+ 42, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 95, 0, 0, 0, 0, 42,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 231, 231, 231, 231, 0, 0, 231, 0, 0, 0,
+ 0, 231, 0, 0, 231, 231, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 95, 0, 0, 0, 0, 0, 231,
+ 231, 231, 231, 231, 231, 231, 231, 231, 231, 231,
+ 231, 0, 0, 0, 0, 0, 231, 231, 231, 231,
+ 231, 231, 231, 231, 231, 231, 231, 95, 0, 231,
+ 231, 231, 231, 0, 0, 0, 0, 0, 0, 0,
+ 93, 231, 231, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 0, 0, 0, 0, 0, 0, 231, 95,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 42, 42, 42, 42, 0, 0, 42, 0, 0,
+ 0, 0, 42, 0, 0, 42, 42, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 93,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 0, 93, 0, 0, 0, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 89, 0,
+ 42, 42, 42, 42, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 93, 0, 0, 42, 42,
+ 42, 42, 42, 0, 0, 0, 0, 0, 0, 42,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 91,
+ 0, 0, 0, 0, 95, 0, 0, 0, 0, 95,
+ 95, 95, 0, 95, 95, 95, 95, 89, 0, 0,
+ 0, 0, 0, 0, 0, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 95, 189, 0, 0, 0, 0,
+ 0, 89, 0, 0, 0, 0, 0, 0, 91, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 89, 0, 0, 95, 95, 0, 0,
+ 0, 0, 91, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 95, 95, 95, 95,
+ 93, 104, 0, 0, 0, 93, 93, 93, 0, 93,
+ 93, 93, 93, 0, 91, 0, 0, 0, 0, 0,
+ 0, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 190, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 93, 93, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 104, 0, 0, 0, 0, 0,
+ 0, 0, 93, 93, 93, 93, 0, 0, 89, 106,
+ 0, 0, 0, 89, 89, 89, 0, 89, 89, 89,
+ 89, 0, 0, 0, 0, 0, 104, 0, 0, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 91,
+ 110, 0, 0, 0, 91, 91, 91, 0, 91, 91,
+ 91, 91, 0, 0, 0, 0, 0, 0, 106, 0,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 89, 89, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 106, 0, 0, 0, 0, 0, 0, 110,
+ 89, 89, 89, 89, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 91, 91, 0, 106, 0, 0, 0, 0, 0,
+ 0, 0, 0, 110, 0, 0, 0, 0, 0, 0,
+ 0, 91, 91, 91, 91, 0, 0, 108, 0, 147,
+ 0, 104, 0, 0, 0, 0, 104, 104, 104, 0,
+ 104, 104, 104, 104, 0, 110, 0, 0, 0, 0,
+ 0, 0, 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 147, 0, 147, 147, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 108, 0, 147, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 104, 104, 0, 0, 0, 0, 0,
+ 108, 0, 147, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 104, 104, 104, 104, 0, 0, 106,
+ 0, 0, 0, 0, 106, 106, 106, 0, 106, 106,
+ 106, 106, 108, 0, 147, 0, 0, 0, 0, 0,
+ 106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+ 106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+ 110, 0, 0, 0, 0, 110, 110, 110, 0, 110,
+ 110, 110, 110, 0, 0, 0, 0, 0, 0, 0,
+ 0, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 106, 106, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 80, 0, 0, 0,
+ 0, 106, 106, 106, 106, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 110, 110, 0, 0, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0, 80, 0, 0, 0,
+ 0, 0, 110, 110, 110, 110, 0, 108, 0, 0,
+ 0, 0, 108, 108, 108, 80, 108, 108, 108, 108,
+ 147, 147, 0, 0, 0, 0, 0, 0, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108, 0, 80,
+ 0, 0, 80, 0, 0, 0, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 80, 0, 0, 0, 147, 147, 147, 147, 108,
+ 108, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 89, 0, 147, 147, 147, 147, 147, 86, 108,
+ 108, 108, 108, 88, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 32,
+ 0, 0, 0, 0, 87, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 80, 80, 80, 0, 0, 0, 80,
+ 0, 80, 80, 80, 80, 80, 80, 80, 80, 0,
+ 0, 0, 0, 0, 0, 0, 0, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 0, 0, 0,
+ 0, 0, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 0, 0, 0, 0, 0, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 0, 0, 80, 80, 80, 80, 6, 0, 80, 80,
+ 0, 0, 0, 6, 0, 0, 0, 0, 6, 0,
+ 80, 80, 80, 80, 80, 0, 0, 0, 80, 80,
+ 80, 0, 0, 0, 0, 278, 279, 49, 8, 9,
+ 0, 50, 0, 0, 0, 0, 51, 10, 11, 280,
+ 281, 14, 15, 16, 17, 18, 19, 20, 21, 0,
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 6, 0, 0, 0,
+ 0, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 22, 0, 77, 78, 79, 80, 23, 24,
+ 0, 0, 25, 0, 26, 0, 0, 0, 0, 0,
+ 0, 0, 81, 82, 83, 84, 85, 27, 89, 28,
+ 0, 0, 0, 29, 30, 104, 0, 0, 0, 0,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 87, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 6, 6, 6, 6, 0, 6, 0, 0, 0,
+ 0, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 0, 105, 0, 0, 0, 0,
+ 0, 0, 104, 298, 0, 0, 0, 88, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 0, 0, 0, 0, 0, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 0, 6,
+ 6, 6, 6, 6, 6, 0, 0, 6, 87, 6,
+ 0, 0, 0, 0, 0, 0, 0, 6, 6, 6,
+ 6, 6, 6, 0, 6, 0, 0, 0, 6, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 47, 48, 49, 8, 0, 0, 50, 0,
+ 120, 121, 122, 51, 123, 124, 125, 126, 0, 0,
+ 0, 0, 0, 0, 0, 0, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 0, 0, 0, 0,
+ 0, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 0, 0, 0, 0, 0, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 89,
+ 0, 77, 78, 79, 80, 0, 86, 147, 148, 0,
+ 0, 88, 0, 0, 0, 0, 0, 0, 0, 81,
+ 82, 83, 84, 85, 0, 0, 0, 149, 150, 151,
+ 29, 0, 0, 0, 0, 0, 0, 0, 0, 47,
+ 48, 49, 8, 0, 0, 50, 0, 0, 0, 0,
+ 51, 0, 0, 52, 53, 0, 0, 0, 0, 0,
+ 0, 0, 87, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 0, 0, 0, 0, 0, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 105, 0, 77, 78,
+ 79, 80, 0, 104, 0, 0, 0, 0, 88, 0,
+ 0, 0, 0, 0, 0, 0, 81, 82, 83, 84,
+ 85, 0, 0, 0, 0, 0, 0, 29, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 87,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 47, 48, 49, 8, 0, 0, 50,
+ 0, 0, 0, 0, 51, 0, 0, 52, 53, 0,
+ 0, 0, 0, 105, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 0, 88, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 0, 0, 0, 0, 0, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 0, 0, 77, 78, 79, 80, 87, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 81, 82, 83, 84, 85, 0, 0, 0, 0, 0,
+ 0, 29, 0, 0, 0, 0, 0, 0, 0, 0,
+ 47, 48, 49, 8, 0, 0, 50, 0, 0, 0,
+ 0, 51, 0, 0, 52, 53, 0, 0, 0, 0,
+ 105, 0, 0, 0, 0, 0, 0, 269, 0, 0,
+ 0, 0, 88, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 0, 0, 0, 0, 0, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 0, 0, 77,
+ 78, 79, 80, 87, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 81, 82, 83,
+ 84, 85, 0, 0, 0, 0, 0, 0, 29, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 89, 0, 47, 48, 49,
+ 8, 0, 104, 50, 407, 0, 0, 88, 51, 0,
+ 0, 52, 53, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 87, 0,
+ 0, 0, 0, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 0, 0, 77, 78, 79, 80,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 82, 83, 84, 85, 89,
+ 0, 0, 0, 0, 0, 29, 104, 0, 0, 0,
+ 0, 88, 0, 0, 47, 48, 49, 8, 0, 0,
+ 50, 0, 0, 0, 0, 51, 0, 0, 52, 53,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 40, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 87, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 0, 32, 0, 0, 0,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 0, 0, 77, 78, 79, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 31, 0,
+ 0, 81, 82, 83, 84, 85, 0, 89, 0, 47,
+ 48, 49, 29, 0, 86, 50, 0, 0, 0, 88,
+ 51, 0, 0, 52, 53, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 87, 0, 0, 0, 0, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 0, 0, 77, 78,
+ 79, 80, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 81, 82, 83, 84,
+ 85, 89, 0, 47, 48, 49, 0, 0, 269, 50,
+ 0, 0, 0, 88, 51, 0, 0, 52, 53, 0,
+ 0, 0, 6, 7, 0, 8, 9, 0, 0, 0,
+ 0, 0, 0, 0, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 0, 0, 0, 0,
+ 0, 0, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 87, 0, 0, 0, 0, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 0, 0, 77, 78, 79, 80, 89, 0, 0, 0,
+ 0, 0, 0, 104, 0, 0, 0, 0, 88, 22,
+ 81, 82, 83, 84, 85, 23, 24, 0, 0, 25,
+ 0, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 47, 48, 49, 27, 0, 28, 50, 0, 0,
+ 29, 30, 51, 0, 0, 52, 53, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 87,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 0, 0, 0, 0, 0, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 0, 0,
+ 77, 78, 79, 80, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 81, 82,
+ 83, 84, 85, 0, 0, 47, 48, 49, 0, 0,
+ 0, 50, 0, 0, 32, 0, 51, 0, 0, 52,
+ 53, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 31, 0, 0, 0,
+ 6, 0, 0, 0, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 0, 0, 0, 0,
+ 0, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 6, 0, 77, 78, 79, 80, 0, 0,
+ 47, 48, 49, 0, 0, 0, 50, 0, 0, 0,
+ 0, 51, 81, 82, 83, 84, 85, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 0, 0, 0, 0, 0, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 0, 0, 77,
+ 78, 79, 80, 0, 0, 0, 0, 0, 0, 0,
+ 6, 7, 0, 8, 9, 0, 0, 81, 82, 83,
+ 84, 85, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 6, 6, 0, 6,
+ 6, 0, 0, 0, 0, 0, 0, 0, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 22, 0, 0,
+ 0, 0, 0, 23, 24, 0, 0, 25, 0, 26,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 27, 0, 28, 0, 0, 0, 29, 30,
+ 0, 0, 0, 6, 0, 0, 0, 0, 0, 6,
+ 6, 0, 0, 6, 0, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 6, 0,
+ 6, 0, 0, 0, 6, 6,
+};
+short yycheck[] = { 24,
+ 43, 0, 45, 28, 119, 30, 94, 44, 37, 260,
+ 58, 36, 4, 42, 43, 105, 45, 37, 47, 43,
+ 44, 45, 42, 43, 99, 45, 0, 47, 0, 260,
+ 0, 60, 24, 62, 96, 0, 35, 0, 30, 258,
+ 37, 156, 41, 101, 36, 42, 43, 96, 45, 43,
+ 47, 45, 58, 348, 349, 61, 114, 271, 272, 41,
+ 258, 86, 44, 88, 89, 94, 96, 86, 62, 41,
+ 350, 351, 44, 41, 94, 43, 101, 45, 41, 41,
+ 105, 44, 44, 59, 335, 134, 43, 59, 45, 114,
+ 43, 44, 45, 0, 59, 0, 59, 94, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 101,
+ 135, 115, 116, 138, 139, 140, 135, 59, 206, 93,
+ 112, 93, 114, 93, 149, 150, 33, 119, 93, 40,
+ 93, 156, 347, 40, 365, 61, 41, 262, 45, 44,
+ 354, 355, 335, 233, 234, 40, 40, 237, 238, 40,
+ 40, 125, 40, 125, 59, 125, 43, 44, 45, 40,
+ 125, 40, 125, 40, 156, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 0, 40, 0, 91, 40, 261, 262, 93, 96,
+ 215, 216, 40, 61, 37, 257, 215, 216, 290, 42,
+ 43, 263, 45, 0, 47, 230, 268, 260, 233, 234,
+ 235, 260, 237, 238, 263, 262, 123, 60, 267, 62,
+ 125, 258, 41, 0, 258, 44, 43, 257, 45, 271,
+ 272, 260, 0, 263, 41, 41, 37, 264, 268, 301,
+ 59, 42, 59, 94, 269, 302, 47, 239, 240, 257,
+ 269, 94, 244, 318, 319, 320, 321, 322, 46, 93,
+ 37, 376, 305, 125, 41, 42, 43, 44, 45, 93,
+ 47, 346, 262, 257, 93, 43, 93, 45, 303, 262,
+ 41, 301, 59, 60, 304, 62, 41, 44, 41, 41,
+ 41, 59, 264, 94, 91, 44, 44, 41, 347, 348,
+ 41, 264, 41, 303, 348, 257, 125, 262, 125, 44,
+ 360, 304, 354, 355, 260, 41, 93, 41, 290, 348,
+ 349, 350, 351, 352, 353, 93, 123, 290, 377, 301,
+ 41, 380, 304, 41, 345, 262, 365, 0, 301, 41,
+ 345, 304, 345, 345, 156, 370, 371, 372, 125, 374,
+ 257, 258, 259, 260, 261, 30, 263, 125, 35, 264,
+ 382, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, -1, -1, 348, 349, -1, 404,
+ -1, -1, 407, -1, 376, 290, 349, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 0, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 0, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 334, -1, 336,
+ 337, 338, 339, 340, 341, -1, -1, 344, -1, 346,
+ -1, -1, 0, 348, 349, 264, 91, 354, 355, 356,
+ 357, 358, 359, 0, 361, 0, -1, 59, 365, 366,
+ 257, 258, -1, 260, 261, -1, 0, 0, -1, -1,
+ 59, 290, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, -1, -1, 0, 264, 33, -1,
+ -1, 93, 37, -1, 0, 40, 41, 42, 43, 44,
+ 45, 59, 47, -1, 93, 26, -1, -1, 41, 352,
+ 353, 44, 59, 290, 59, 60, -1, 62, -1, -1,
+ -1, 42, 91, 125, 301, 59, 59, 304, -1, 0,
+ 349, -1, -1, -1, -1, 93, 125, 334, -1, -1,
+ -1, -1, -1, 340, 341, 59, 93, 344, 93, 346,
+ -1, 96, -1, 59, -1, -1, -1, -1, -1, 93,
+ 93, -1, 359, -1, 361, 0, 87, 125, 365, 366,
+ -1, 348, 349, 350, 351, 352, 353, 0, 125, 93,
+ 125, -1, -1, -1, -1, -1, -1, 93, 59, -1,
+ -1, 125, 125, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, 37, -1, -1, 40, 41, 42, 43, 44,
+ 45, 125, 47, 260, -1, 260, 263, -1, 41, 125,
+ -1, 44, 93, 268, 59, 60, -1, 62, 273, 274,
+ 275, 276, 277, 278, -1, 280, 59, 158, -1, -1,
+ -1, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 172, -1, -1, 125, -1, -1, -1, 93, 37,
+ -1, 96, -1, -1, 42, 43, 44, 45, -1, 47,
+ 93, 37, -1, -1, -1, -1, 42, 43, 44, 45,
+ -1, 47, 60, -1, 62, -1, -1, -1, -1, -1,
+ 125, 260, -1, -1, 60, -1, 62, -1, -1, 268,
+ 347, 348, 125, -1, 273, 274, 275, 276, 277, 278,
+ -1, 280, 257, 258, 259, 260, 94, -1, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 94, -1,
+ 377, 264, -1, 380, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 290, -1, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, -1,
+ -1, 336, 337, 338, 339, -1, -1, 342, 343, -1,
+ 345, -1, -1, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, -1, -1, 349, 362, 363, 364,
+ 365, -1, 257, 258, 259, 260, -1, -1, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 359, -1,
+ -1, 264, 363, 364, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 290, -1, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 0,
+ -1, 336, 337, 338, 339, -1, -1, 342, 343, -1,
+ 345, -1, -1, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 91, -1, 349, 362, 363, 364,
+ 365, -1, 33, -1, -1, 301, 37, -1, 304, 40,
+ 41, 42, 43, 44, 45, -1, 47, -1, -1, -1,
+ -1, -1, -1, 0, -1, -1, -1, -1, 59, 60,
+ -1, 62, -1, -1, -1, -1, -1, -1, -1, 0,
+ 348, 349, 350, 351, 352, 353, -1, -1, -1, -1,
+ -1, -1, 348, 349, 350, 351, 352, 353, -1, -1,
+ -1, -1, 93, -1, 41, 96, -1, 44, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 0, -1,
+ 41, -1, 59, 44, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 125, -1, -1, -1, 59, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 33, -1, -1, -1, 37, 93, -1, 40, 41,
+ 42, 43, 44, 45, -1, 47, -1, -1, -1, -1,
+ -1, -1, 93, -1, -1, -1, -1, 59, 60, -1,
+ 62, -1, -1, -1, -1, -1, -1, -1, 125, -1,
+ 37, -1, -1, -1, -1, 42, 43, -1, 45, -1,
+ 47, -1, -1, 260, 125, -1, -1, -1, -1, -1,
+ -1, 93, -1, 60, 96, 62, 273, 274, 275, 276,
+ 277, 278, -1, 280, -1, 37, -1, -1, -1, -1,
+ 42, 43, 44, 45, -1, 47, -1, -1, -1, -1,
+ -1, -1, -1, 125, -1, -1, -1, 94, 60, -1,
+ 62, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 257, 258, 259, 260,
+ -1, -1, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 94, -1, -1, -1, -1, -1, -1, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 301, -1, -1, 304, 305, 306, 307, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, 264, -1, -1,
+ -1, -1, 323, 324, 325, 326, 327, 328, 329, 330,
+ 331, 332, 333, 264, -1, 336, 337, 338, 339, -1,
+ -1, 342, 343, 290, 345, -1, -1, 348, 349, 350,
+ 351, 352, 353, 354, 355, 356, 357, 358, -1, 290,
+ -1, 362, 363, 364, 365, 257, 258, 259, 260, -1,
+ -1, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, -1, -1, -1, -1, -1, -1, -1, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
+ -1, -1, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, -1, -1, -1, 286,
+ -1, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, 0, -1, 336, 337, 338, 339, -1, -1,
+ 342, 343, -1, 345, -1, -1, 348, 349, 350, 351,
+ 352, 353, 354, 355, 356, 357, 358, -1, -1, -1,
+ 362, 363, 364, 365, -1, 33, -1, -1, -1, 37,
+ -1, -1, 40, 41, 42, 43, 44, 45, -1, 47,
+ -1, 348, 349, 350, 351, 352, 353, -1, 86, -1,
+ -1, 59, 60, -1, 62, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 37, -1, -1, -1, -1, 42,
+ 43, -1, 45, -1, 47, -1, 348, 349, 350, 351,
+ 352, 353, -1, -1, -1, 93, -1, 60, 96, 62,
+ -1, -1, -1, 131, 132, 133, -1, -1, -1, -1,
+ -1, 0, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 125, 156, -1,
+ -1, 94, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 33, -1, -1, -1, 37, -1,
+ -1, 40, 41, 42, 43, 44, 45, -1, 47, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 197,
+ 59, 60, -1, 62, -1, -1, -1, 205, -1, -1,
+ 37, -1, -1, -1, -1, 42, 43, 37, 45, -1,
+ 47, -1, 42, 43, -1, 45, -1, 47, -1, -1,
+ -1, -1, -1, 60, 93, 62, -1, 96, -1, -1,
+ 60, -1, 62, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, -1, -1, -1, 42, 43, 44, 45, -1,
+ 47, -1, -1, -1, -1, -1, 125, 94, -1, -1,
+ -1, -1, -1, 60, 94, 62, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, 260, -1, -1, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 303, -1, 94, -1, -1,
+ -1, -1, -1, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, -1, -1, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ -1, -1, -1, -1, -1, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, -1, -1, 336, 337,
+ 338, 339, 370, 371, 342, 343, -1, 345, -1, -1,
+ 348, 349, 350, 351, 352, 353, 354, 355, 356, 357,
+ 358, -1, -1, -1, 362, 363, 364, 365, 257, 258,
+ 259, 260, -1, -1, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, -1, 348, 349, 350, 351, 352,
+ 353, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, -1, -1, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, -1, 289, -1, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, 0, -1, 336, 337, 338,
+ 339, -1, -1, 342, 343, -1, 345, -1, -1, 348,
+ 349, 350, 351, 352, 353, 354, 355, 356, 357, 358,
+ -1, -1, -1, 362, 363, 364, 365, -1, 33, -1,
+ -1, -1, -1, -1, 301, 40, 41, 304, 43, 44,
+ 45, 348, 349, 350, 351, 352, 353, -1, 348, 349,
+ 350, 351, 352, 353, 59, 60, -1, 62, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 37, -1, -1,
+ -1, -1, 42, 43, -1, 45, -1, 47, -1, -1,
+ -1, 348, 349, 350, 351, 352, 353, -1, 93, -1,
+ 60, 96, 62, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, 94, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 33, -1, -1,
+ -1, -1, -1, -1, 40, 41, -1, 43, 44, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 59, 60, -1, 62, -1, -1, -1,
+ 37, -1, -1, -1, -1, 42, 43, -1, 45, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 60, -1, 62, -1, 93, -1, 37,
+ 96, -1, -1, -1, 42, 43, -1, 45, -1, 47,
+ -1, -1, -1, 37, -1, -1, -1, -1, 42, 43,
+ 44, 45, 60, 47, 62, -1, -1, 94, -1, 125,
+ -1, -1, -1, -1, -1, -1, 60, -1, 62, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 257, 258, 259, 260, 94, -1, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ 94, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, -1, -1, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, -1,
+ -1, 336, 337, 338, 339, -1, -1, 342, 343, -1,
+ 345, -1, -1, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, -1, -1, 46, 362, 363, 364,
+ 365, 257, 258, 259, 260, -1, -1, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, -1, 348, -1,
+ 350, 351, 352, 353, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 301, -1, -1, 304, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, -1, -1, -1, -1, 323, 324, 325,
+ 326, 327, 328, 329, 330, 331, 332, 333, 0, -1,
+ 336, 337, 338, 339, -1, -1, 342, 343, -1, 345,
+ -1, -1, 348, 349, 350, 351, 352, 353, 354, 355,
+ 356, 357, 358, -1, -1, -1, 362, 363, 364, 365,
+ -1, 33, -1, -1, -1, -1, -1, 301, 40, 41,
+ 304, 348, 44, 350, 351, 352, 353, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 59, 60, -1,
+ 62, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 350, 351, 352, 353, -1, -1, -1, -1,
+ -1, -1, -1, -1, 348, 349, 350, 351, 352, 353,
+ -1, 93, -1, -1, 96, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 125, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 33, 271, 272, -1, -1, -1, -1, 40, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 59, 60, -1, 62,
+ -1, -1, -1, -1, -1, -1, -1, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 93, -1, -1, 96, -1, -1, 336, 337, 338, 339,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 354, 355, 356, 357, 358, -1,
+ -1, -1, 125, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 46, -1, -1, -1, -1, 257, 258, 259, 260, -1,
+ -1, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, -1, -1, -1, -1, -1, -1, -1, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
+ -1, -1, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, -1, -1, -1, -1,
+ -1, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, -1, -1, 336, 337, 338, 339, -1, -1,
+ 342, 343, -1, 345, -1, -1, 348, 349, 350, 351,
+ 352, 353, 354, 355, 356, 357, 358, 46, -1, -1,
+ 362, 363, 364, 365, 257, 258, 259, 260, -1, -1,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ -1, -1, -1, -1, -1, -1, -1, -1, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, -1,
+ -1, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, -1, -1, -1, -1,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 0, -1, 336, 337, 338, 339, -1, -1, 342,
+ 343, -1, 345, -1, -1, 348, 349, 350, 351, 352,
+ 353, 354, 355, 356, 357, 358, -1, -1, -1, 362,
+ 363, 364, 365, -1, 33, 271, 272, -1, -1, -1,
+ -1, 40, 41, -1, -1, 44, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 59, 60, -1, 62, -1, -1, -1, -1, -1, -1,
+ -1, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 93, -1, -1, 96, -1, -1,
+ 336, 337, 338, 339, -1, -1, -1, -1, -1, -1,
+ 0, -1, -1, -1, -1, -1, -1, -1, 354, 355,
+ 356, 357, 358, -1, -1, -1, 125, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 33, -1, -1, -1, -1, -1, -1,
+ 40, 41, -1, -1, 44, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ 60, -1, 62, -1, -1, -1, -1, -1, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, -1, -1, 96, 336, 337, 338,
+ 339, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 354, 355, 356, 357, 358,
+ -1, -1, -1, -1, -1, 125, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 257, 258,
+ 259, 260, -1, -1, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, -1, -1, -1, -1, -1, -1,
+ -1, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, -1, -1, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, -1, -1, -1, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, -1, -1, 336, 337, 338,
+ 339, -1, -1, 342, 343, -1, 345, -1, -1, 348,
+ 349, 350, 351, 352, 353, 354, 355, 356, 357, 358,
+ -1, -1, -1, 362, 363, 364, 365, 257, 258, 259,
+ 260, -1, -1, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, -1, -1, -1, -1, -1, -1, -1,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, -1, -1, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, -1, -1,
+ -1, -1, -1, 323, 324, 325, 326, 327, 328, 329,
+ 330, 331, 332, 333, 0, -1, 336, 337, 338, 339,
+ -1, -1, 342, 343, -1, 345, -1, -1, 348, 349,
+ 350, 351, 352, 353, 354, 355, 356, 357, 358, -1,
+ -1, -1, 362, 363, 364, 365, -1, 33, -1, -1,
+ -1, -1, -1, -1, 40, 41, -1, 43, 44, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 59, -1, -1, 62, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 24, -1, -1, -1, -1,
+ -1, 30, -1, -1, -1, -1, -1, 93, -1, -1,
+ 96, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 125,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 33, 86, -1, -1,
+ -1, -1, -1, 40, 41, -1, -1, 44, -1, -1,
+ -1, -1, 101, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 59, -1, -1, 114, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 131, 132, 133, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, 156, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 125, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 197, -1,
+ -1, 257, 258, 259, 260, -1, 205, 263, -1, 265,
+ 266, 267, 268, 269, 270, 271, 272, 40, -1, -1,
+ -1, -1, -1, 46, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, -1, -1, -1, -1, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 326, 327, 328, 329, 330, 331, 332, 333, -1, -1,
+ 336, 337, 338, 339, -1, -1, 342, 343, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 354, 355,
+ 356, 357, 358, -1, 303, -1, 362, 363, 364, 365,
+ 257, 258, 259, 260, -1, -1, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, -1, -1, -1, -1,
+ -1, -1, -1, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, -1, -1, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 370, 371, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 0, -1, 336,
+ 337, 338, 339, -1, -1, 342, 343, -1, 345, -1,
+ -1, 348, 349, 350, 351, -1, -1, 354, 355, 356,
+ 357, 358, -1, -1, -1, 362, 363, 364, 365, -1,
+ 33, -1, -1, -1, -1, -1, -1, 40, 41, -1,
+ -1, 44, -1, -1, 257, -1, -1, -1, -1, -1,
+ 263, -1, -1, -1, -1, 268, 59, -1, 271, 272,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 0, -1, -1, -1, -1, -1, -1, -1, -1,
+ 93, -1, -1, 96, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, 0, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 125, 336, 337, 338, 339, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 33,
+ 59, 354, 355, 356, 357, 358, 40, 41, -1, 43,
+ 44, 45, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 59, -1, -1, 62, -1,
+ -1, -1, -1, -1, 93, -1, -1, 96, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, 125, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 257, 258, 259, 260, -1, -1,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ -1, -1, -1, -1, -1, -1, -1, -1, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, -1,
+ -1, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, -1, -1, -1, -1,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, -1, -1, 336, 337, 338, 339, -1, 257, 342,
+ 343, -1, 345, -1, 263, 348, 349, 350, 351, 268,
+ -1, 354, 355, 356, 357, 358, -1, -1, -1, 362,
+ 363, 364, 365, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, 301, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, 305, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, 318, 319, 320, 321, 322, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ 0, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, -1, 33, -1, -1, -1, -1, -1, -1,
+ 40, 41, -1, -1, 44, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, -1, -1, 96, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 125, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 33, 59, -1, -1, -1, -1, -1, 40,
+ 41, -1, -1, 44, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 59, -1,
+ -1, -1, -1, -1, -1, -1, -1, 93, -1, -1,
+ 96, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 93, -1, -1, 96, -1, -1, -1, 125,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 125, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 257, 258, 259,
+ 260, -1, -1, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, -1, -1, -1, -1, -1, -1, -1,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, -1, -1, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, -1, -1,
+ -1, -1, -1, 323, 324, 325, 326, 327, 328, 329,
+ 330, 331, 332, 333, -1, -1, 336, 337, 338, 339,
+ -1, 257, 342, 343, -1, 345, -1, 263, 348, 349,
+ -1, -1, 268, -1, 354, 355, 356, 357, 358, -1,
+ -1, -1, 362, 363, 364, 365, 257, 258, 259, 260,
+ -1, -1, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, -1, -1, -1, 301, -1, -1, -1, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 301, -1, -1, 304, 305, 306, 307, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, -1, -1, -1,
+ -1, -1, 323, 324, 325, 326, 327, 328, 329, 330,
+ 331, 332, 333, 0, -1, 336, 337, 338, 339, -1,
+ -1, 342, 343, -1, 345, -1, -1, -1, 349, -1,
+ -1, -1, -1, 354, 355, 356, 357, 358, -1, -1,
+ -1, 362, 363, 364, 365, -1, 33, -1, -1, -1,
+ -1, -1, -1, 40, 41, -1, 43, 44, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 59, -1, -1, 62, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 0, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 125, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 33, -1, -1, -1, -1,
+ -1, -1, 40, 41, -1, -1, 44, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 59, -1, -1, 62, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 93, -1, -1, 96, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 125, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, -1, -1, 263, -1, 265, 266,
+ 267, 268, 269, 270, 271, 272, -1, -1, -1, -1,
+ -1, -1, -1, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, -1, -1, -1, -1, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, -1, -1, 336,
+ 337, 338, 339, -1, -1, 342, 343, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 354, 355, 356,
+ 357, 358, -1, -1, -1, 362, 363, 364, 365, 257,
+ 258, 259, 260, -1, -1, 263, -1, 265, 266, 267,
+ 268, 269, 270, 271, 272, -1, -1, -1, -1, -1,
+ -1, -1, -1, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, -1, -1, -1, -1, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ -1, -1, -1, -1, -1, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 0, -1, 336, 337,
+ 338, 339, -1, -1, 342, 343, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 354, 355, 356, 357,
+ 358, -1, -1, -1, 362, 363, 364, 365, -1, 33,
+ -1, -1, -1, -1, -1, -1, 40, 41, -1, -1,
+ 44, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 59, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, 305, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 0, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, 59, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 125, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, -1, -1, 342, 343,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, 365, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, -1, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 0,
+ -1, 336, 337, 338, 339, -1, -1, 342, 343, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, -1, -1, 362, 363, 364,
+ 365, -1, 33, -1, -1, -1, -1, -1, -1, 40,
+ 41, -1, 43, 44, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 59, -1,
+ -1, 62, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 0, -1, -1, -1,
+ -1, -1, 93, -1, -1, 96, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 33,
+ -1, -1, -1, -1, 125, -1, 40, 41, -1, -1,
+ 44, 45, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 59, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 33, -1, -1, -1,
+ -1, -1, -1, 40, -1, -1, 257, 258, 259, 260,
+ -1, -1, 263, -1, 265, 266, 267, 268, 269, 270,
+ 271, 272, 59, -1, -1, -1, -1, -1, -1, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ -1, -1, -1, -1, 305, 306, 93, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, 318, 319, 320,
+ 321, 322, 323, 324, 325, 326, 327, 328, 329, 330,
+ 331, 332, 333, -1, -1, -1, -1, -1, 125, -1,
+ -1, 342, 343, 257, 258, 259, 260, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, -1, -1, -1,
+ -1, 362, 363, 364, 365, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, -1, -1, -1, -1, 33, -1, 342, 343,
+ -1, -1, -1, 40, 41, -1, -1, 44, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 362, 363,
+ 364, 365, 59, 60, -1, 62, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, -1, -1, 263, -1, -1, -1,
+ -1, 268, -1, -1, 271, 272, 93, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, -1, 125, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 33, -1, 336,
+ 337, 338, 339, -1, 40, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 354, 355, 356,
+ 357, 358, -1, 59, -1, -1, -1, -1, 365, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 93, -1, -1,
+ 96, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, -1, -1, 125,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, -1, -1, 263, -1, -1, -1,
+ -1, 268, -1, -1, 271, 272, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 59, -1, -1, -1, -1, -1, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 93, -1, 336,
+ 337, 338, 339, -1, -1, -1, -1, -1, -1, -1,
+ 0, 348, 349, 350, 351, 352, 353, 354, 355, 356,
+ 357, 358, -1, -1, -1, -1, -1, -1, 365, 125,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, 259, 260, -1, -1, 263, -1, -1,
+ -1, -1, 268, -1, -1, 271, 272, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, 93, -1, -1, -1, 323, 324, 325,
+ 326, 327, 328, 329, 330, 331, 332, 333, 0, -1,
+ 336, 337, 338, 339, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 125, -1, -1, 354, 355,
+ 356, 357, 358, -1, -1, -1, -1, -1, -1, 365,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, -1, 260, -1, -1, -1, -1, 265,
+ 266, 267, -1, 269, 270, 271, 272, 59, -1, -1,
+ -1, -1, -1, -1, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 301, -1, -1, -1, -1,
+ -1, 93, -1, -1, -1, -1, -1, -1, 59, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 125, -1, -1, 342, 343, -1, -1,
+ -1, -1, 93, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 362, 363, 364, 365,
+ 260, 0, -1, -1, -1, 265, 266, 267, -1, 269,
+ 270, 271, 272, -1, 125, -1, -1, -1, -1, -1,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 59, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 342, 343, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 93, -1, -1, -1, -1, -1,
+ -1, -1, 362, 363, 364, 365, -1, -1, 260, 0,
+ -1, -1, -1, 265, 266, 267, -1, 269, 270, 271,
+ 272, -1, -1, -1, -1, -1, 125, -1, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 260,
+ 0, -1, -1, -1, 265, 266, 267, -1, 269, 270,
+ 271, 272, -1, -1, -1, -1, -1, -1, 59, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 342, 343, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 93, -1, -1, -1, -1, -1, -1, 59,
+ 362, 363, 364, 365, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 342, 343, -1, 125, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, -1, -1, -1, -1, -1, -1,
+ -1, 362, 363, 364, 365, -1, -1, 0, -1, 0,
+ -1, 260, -1, -1, -1, -1, 265, 266, 267, -1,
+ 269, 270, 271, 272, -1, 125, -1, -1, -1, -1,
+ -1, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 43, -1, 45, 46, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 59, -1, 59, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 342, 343, -1, -1, -1, -1, -1,
+ 93, -1, 93, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 362, 363, 364, 365, -1, -1, 260,
+ -1, -1, -1, -1, 265, 266, 267, -1, 269, 270,
+ 271, 272, 125, -1, 125, -1, -1, -1, -1, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 260, -1, -1, -1, -1, 265, 266, 267, -1, 269,
+ 270, 271, 272, -1, -1, -1, -1, -1, -1, -1,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 342, 343, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 0, -1, -1, -1,
+ -1, 362, 363, 364, 365, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 342, 343, -1, -1, -1, -1, -1, 33,
+ -1, -1, -1, -1, -1, -1, 40, -1, -1, -1,
+ -1, -1, 362, 363, 364, 365, -1, 260, -1, -1,
+ -1, -1, 265, 266, 267, 59, 269, 270, 271, 272,
+ 271, 272, -1, -1, -1, -1, -1, -1, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, -1, 93,
+ -1, -1, 96, -1, -1, -1, 307, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, 318, 319, 320,
+ 321, 322, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 125, -1, -1, -1, 336, 337, 338, 339, 342,
+ 343, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 33, -1, 354, 355, 356, 357, 358, 40, 362,
+ 363, 364, 365, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 91,
+ -1, -1, -1, -1, 96, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 123, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, -1, -1, -1, 263,
+ -1, 265, 266, 267, 268, 269, 270, 271, 272, -1,
+ -1, -1, -1, -1, -1, -1, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, 33, -1, 342, 343,
+ -1, -1, -1, 40, -1, -1, -1, -1, 45, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, 362, 363,
+ 364, -1, -1, -1, -1, 257, 258, 259, 260, 261,
+ -1, 263, -1, -1, -1, -1, 268, 269, 270, 271,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, -1,
+ -1, -1, -1, -1, 91, -1, -1, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 123, -1, -1, -1,
+ -1, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, 334, -1, 336, 337, 338, 339, 340, 341,
+ -1, -1, 344, -1, 346, -1, -1, -1, -1, -1,
+ -1, -1, 354, 355, 356, 357, 358, 359, 33, 361,
+ -1, -1, -1, 365, 366, 40, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, 261, -1, 263, -1, -1, -1,
+ -1, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, -1, 33, -1, -1, -1, -1,
+ -1, -1, 40, 41, -1, -1, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 334, -1, 336,
+ 337, 338, 339, 340, 341, -1, -1, 344, 96, 346,
+ -1, -1, -1, -1, -1, -1, -1, 354, 355, 356,
+ 357, 358, 359, -1, 361, -1, -1, -1, 365, 366,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 257, 258, 259, 260, -1, -1, 263, -1,
+ 265, 266, 267, 268, 269, 270, 271, 272, -1, -1,
+ -1, -1, -1, -1, -1, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, -1, -1, -1, -1,
+ -1, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, -1, -1, -1, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 33,
+ -1, 336, 337, 338, 339, -1, 40, 342, 343, -1,
+ -1, 45, -1, -1, -1, -1, -1, -1, -1, 354,
+ 355, 356, 357, 358, -1, -1, -1, 362, 363, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, 260, -1, -1, 263, -1, -1, -1, -1,
+ 268, -1, -1, 271, 272, -1, -1, -1, -1, -1,
+ -1, -1, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ -1, -1, -1, -1, -1, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 33, -1, 336, 337,
+ 338, 339, -1, 40, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, 354, 355, 356, 357,
+ 358, -1, -1, -1, -1, -1, -1, 365, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, 263,
+ -1, -1, -1, -1, 268, -1, -1, 271, 272, -1,
+ -1, -1, -1, 33, -1, -1, -1, -1, -1, -1,
+ 40, -1, -1, -1, -1, 45, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, 96, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 354, 355, 356, 357, 358, -1, -1, -1, -1, -1,
+ -1, 365, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, -1, -1, 263, -1, -1, -1,
+ -1, 268, -1, -1, 271, 272, -1, -1, -1, -1,
+ 33, -1, -1, -1, -1, -1, -1, 40, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, -1, -1, 336,
+ 337, 338, 339, 96, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 354, 355, 356,
+ 357, 358, -1, -1, -1, -1, -1, -1, 365, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 33, -1, 257, 258, 259,
+ 260, -1, 40, 263, 42, -1, -1, 45, 268, -1,
+ -1, 271, 272, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 96, -1,
+ -1, -1, -1, 323, 324, 325, 326, 327, 328, 329,
+ 330, 331, 332, 333, -1, -1, 336, 337, 338, 339,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 354, 355, 356, 357, 358, 33,
+ -1, -1, -1, -1, -1, 365, 40, -1, -1, -1,
+ -1, 45, -1, -1, 257, 258, 259, 260, -1, -1,
+ 263, -1, -1, -1, -1, 268, -1, -1, 271, 272,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 96, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, 91, -1, -1, -1,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, -1, -1, 336, 337, 338, 339, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 123, -1,
+ -1, 354, 355, 356, 357, 358, -1, 33, -1, 257,
+ 258, 259, 365, -1, 40, 263, -1, -1, -1, 45,
+ 268, -1, -1, 271, 272, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 96, -1, -1, -1, -1, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, -1, -1, 336, 337,
+ 338, 339, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 354, 355, 356, 357,
+ 358, 33, -1, 257, 258, 259, -1, -1, 40, 263,
+ -1, -1, -1, 45, 268, -1, -1, 271, 272, -1,
+ -1, -1, 257, 258, -1, 260, 261, -1, -1, -1,
+ -1, -1, -1, -1, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, -1, -1, -1, -1,
+ -1, -1, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, 96, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ -1, -1, 336, 337, 338, 339, 33, -1, -1, -1,
+ -1, -1, -1, 40, -1, -1, -1, -1, 45, 334,
+ 354, 355, 356, 357, 358, 340, 341, -1, -1, 344,
+ -1, 346, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, 259, 359, -1, 361, 263, -1, -1,
+ 365, 366, 268, -1, -1, 271, 272, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, -1, -1, -1, -1, 323, 324, 325,
+ 326, 327, 328, 329, 330, 331, 332, 333, -1, -1,
+ 336, 337, 338, 339, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 354, 355,
+ 356, 357, 358, -1, -1, 257, 258, 259, -1, -1,
+ -1, 263, -1, -1, 91, -1, 268, -1, -1, 271,
+ 272, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 123, -1, -1, -1,
+ 91, -1, -1, -1, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, -1, -1, -1, -1,
+ -1, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, 123, -1, 336, 337, 338, 339, -1, -1,
+ 257, 258, 259, -1, -1, -1, 263, -1, -1, -1,
+ -1, 268, 354, 355, 356, 357, 358, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, -1, -1, 336,
+ 337, 338, 339, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, -1, 260, 261, -1, -1, 354, 355, 356,
+ 357, 358, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 257, 258, -1, 260,
+ 261, -1, -1, -1, -1, -1, -1, -1, 269, 270,
+ 271, 272, 273, 274, 275, 276, 277, 278, 279, 280,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 334, -1, -1,
+ -1, -1, -1, 340, 341, -1, -1, 344, -1, 346,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 359, -1, 361, -1, -1, -1, 365, 366,
+ -1, -1, -1, 334, -1, -1, -1, -1, -1, 340,
+ 341, -1, -1, 344, -1, 346, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 359, -1,
+ 361, -1, -1, -1, 365, 366,
+};
+#define YYFINAL 2
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 368
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'!'",0,0,0,"'%'",0,0,"'('","')'","'*'","'+'","','","'-'","'.'","'/'",0,0,0,0,0,
+0,0,0,0,0,"':'","';'","'<'","'='","'>'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0,"'`'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"LABEL","VARIABLE","NUMBER",
+"TEXT","COMMAND_LINE","DELIMITED","ORDINAL","TH","LEFT_ARROW_HEAD",
+"RIGHT_ARROW_HEAD","DOUBLE_ARROW_HEAD","LAST","UP","DOWN","LEFT","RIGHT","BOX",
+"CIRCLE","ELLIPSE","ARC","LINE","ARROW","MOVE","SPLINE","HEIGHT","RADIUS",
+"WIDTH","DIAMETER","FROM","TO","AT","WITH","BY","THEN","SOLID","DOTTED",
+"DASHED","CHOP","SAME","INVISIBLE","LJUST","RJUST","ABOVE","BELOW","OF","THE",
+"WAY","BETWEEN","AND","HERE","DOT_N","DOT_E","DOT_W","DOT_S","DOT_NE","DOT_SE",
+"DOT_NW","DOT_SW","DOT_C","DOT_START","DOT_END","DOT_X","DOT_Y","DOT_HT",
+"DOT_WID","DOT_RAD","SIN","COS","ATAN2","LOG","EXP","SQRT","K_MAX","K_MIN",
+"INT","RAND","SRAND","COPY","THRU","TOP","BOTTOM","UPPER","LOWER","SH","PRINT",
+"CW","CCW","FOR","DO","IF","ELSE","ANDAND","OROR","NOTEQUAL","EQUALEQUAL",
+"LESSEQUAL","GREATEREQUAL","LEFT_CORNER","RIGHT_CORNER","CENTER","END","START",
+"RESET","UNTIL","PLOT","THICKNESS","FILL","ALIGNED","SPRINTF","COMMAND",
+"DEFINE","UNDEF",
+};
+char *yyrule[] = {
+"$accept : top",
+"top : optional_separator",
+"top : element_list",
+"element_list : optional_separator middle_element_list optional_separator",
+"middle_element_list : element",
+"middle_element_list : middle_element_list separator element",
+"optional_separator :",
+"optional_separator : separator",
+"separator : ';'",
+"separator : separator ';'",
+"placeless_element : VARIABLE '=' any_expr",
+"placeless_element : VARIABLE ':' '=' any_expr",
+"placeless_element : UP",
+"placeless_element : DOWN",
+"placeless_element : LEFT",
+"placeless_element : RIGHT",
+"placeless_element : COMMAND_LINE",
+"placeless_element : COMMAND print_args",
+"placeless_element : PRINT print_args",
+"$$1 :",
+"placeless_element : SH $$1 DELIMITED",
+"placeless_element : COPY TEXT",
+"$$2 :",
+"$$3 :",
+"placeless_element : COPY TEXT THRU $$2 DELIMITED $$3 until",
+"$$4 :",
+"$$5 :",
+"placeless_element : COPY THRU $$4 DELIMITED $$5 until",
+"$$6 :",
+"placeless_element : FOR VARIABLE '=' expr TO expr optional_by DO $$6 DELIMITED",
+"placeless_element : simple_if",
+"$$7 :",
+"placeless_element : simple_if ELSE $$7 DELIMITED",
+"placeless_element : reset_variables",
+"placeless_element : RESET",
+"reset_variables : RESET VARIABLE",
+"reset_variables : reset_variables VARIABLE",
+"reset_variables : reset_variables ',' VARIABLE",
+"print_args : print_arg",
+"print_args : print_args print_arg",
+"print_arg : expr",
+"print_arg : text",
+"print_arg : position",
+"$$8 :",
+"simple_if : IF any_expr THEN $$8 DELIMITED",
+"until :",
+"until : UNTIL TEXT",
+"any_expr : expr",
+"any_expr : text_expr",
+"text_expr : text EQUALEQUAL text",
+"text_expr : text NOTEQUAL text",
+"text_expr : text_expr ANDAND text_expr",
+"text_expr : text_expr ANDAND expr",
+"text_expr : expr ANDAND text_expr",
+"text_expr : text_expr OROR text_expr",
+"text_expr : text_expr OROR expr",
+"text_expr : expr OROR text_expr",
+"text_expr : '!' text_expr",
+"optional_by :",
+"optional_by : BY expr",
+"optional_by : BY '*' expr",
+"element : object_spec",
+"element : LABEL ':' optional_separator element",
+"element : LABEL ':' optional_separator position_not_place",
+"element : LABEL ':' optional_separator place",
+"$$9 :",
+"$$10 :",
+"element : '{' $$9 element_list '}' $$10 optional_element",
+"element : placeless_element",
+"optional_element :",
+"optional_element : element",
+"object_spec : BOX",
+"object_spec : CIRCLE",
+"object_spec : ELLIPSE",
+"object_spec : ARC",
+"object_spec : LINE",
+"object_spec : ARROW",
+"object_spec : MOVE",
+"object_spec : SPLINE",
+"object_spec : text",
+"object_spec : PLOT expr",
+"object_spec : PLOT expr text",
+"$$11 :",
+"object_spec : '[' $$11 element_list ']'",
+"object_spec : object_spec HEIGHT expr",
+"object_spec : object_spec RADIUS expr",
+"object_spec : object_spec WIDTH expr",
+"object_spec : object_spec DIAMETER expr",
+"object_spec : object_spec expr",
+"object_spec : object_spec UP",
+"object_spec : object_spec UP expr",
+"object_spec : object_spec DOWN",
+"object_spec : object_spec DOWN expr",
+"object_spec : object_spec RIGHT",
+"object_spec : object_spec RIGHT expr",
+"object_spec : object_spec LEFT",
+"object_spec : object_spec LEFT expr",
+"object_spec : object_spec FROM position",
+"object_spec : object_spec TO position",
+"object_spec : object_spec AT position",
+"object_spec : object_spec WITH path",
+"object_spec : object_spec BY expr_pair",
+"object_spec : object_spec THEN",
+"object_spec : object_spec SOLID",
+"object_spec : object_spec DOTTED",
+"object_spec : object_spec DOTTED expr",
+"object_spec : object_spec DASHED",
+"object_spec : object_spec DASHED expr",
+"object_spec : object_spec FILL",
+"object_spec : object_spec FILL expr",
+"object_spec : object_spec CHOP",
+"object_spec : object_spec CHOP expr",
+"object_spec : object_spec SAME",
+"object_spec : object_spec INVISIBLE",
+"object_spec : object_spec LEFT_ARROW_HEAD",
+"object_spec : object_spec RIGHT_ARROW_HEAD",
+"object_spec : object_spec DOUBLE_ARROW_HEAD",
+"object_spec : object_spec CW",
+"object_spec : object_spec CCW",
+"object_spec : object_spec text",
+"object_spec : object_spec LJUST",
+"object_spec : object_spec RJUST",
+"object_spec : object_spec ABOVE",
+"object_spec : object_spec BELOW",
+"object_spec : object_spec THICKNESS expr",
+"object_spec : object_spec ALIGNED",
+"text : TEXT",
+"text : SPRINTF '(' TEXT sprintf_args ')'",
+"sprintf_args :",
+"sprintf_args : sprintf_args ',' expr",
+"position : position_not_place",
+"position : place",
+"position_not_place : expr_pair",
+"position_not_place : position '+' expr_pair",
+"position_not_place : position '-' expr_pair",
+"position_not_place : '(' position ',' position ')'",
+"position_not_place : expr between position AND position",
+"position_not_place : expr '<' position ',' position '>'",
+"between : BETWEEN",
+"between : OF THE WAY BETWEEN",
+"expr_pair : expr ',' expr",
+"expr_pair : '(' expr_pair ')'",
+"place : label",
+"place : label corner",
+"place : corner label",
+"place : corner OF label",
+"place : HERE",
+"label : LABEL",
+"label : nth_primitive",
+"label : label '.' LABEL",
+"ordinal : ORDINAL",
+"ordinal : '`' any_expr TH",
+"optional_ordinal_last : LAST",
+"optional_ordinal_last : ordinal LAST",
+"nth_primitive : ordinal object_type",
+"nth_primitive : optional_ordinal_last object_type",
+"object_type : BOX",
+"object_type : CIRCLE",
+"object_type : ELLIPSE",
+"object_type : ARC",
+"object_type : LINE",
+"object_type : ARROW",
+"object_type : SPLINE",
+"object_type : '[' ']'",
+"object_type : TEXT",
+"label_path : '.' LABEL",
+"label_path : label_path '.' LABEL",
+"relative_path : corner",
+"relative_path : label_path",
+"relative_path : label_path corner",
+"path : relative_path",
+"path : '(' relative_path ',' relative_path ')'",
+"path : ORDINAL LAST object_type relative_path",
+"path : LAST object_type relative_path",
+"path : ORDINAL object_type relative_path",
+"path : LABEL relative_path",
+"corner : DOT_N",
+"corner : DOT_E",
+"corner : DOT_W",
+"corner : DOT_S",
+"corner : DOT_NE",
+"corner : DOT_SE",
+"corner : DOT_NW",
+"corner : DOT_SW",
+"corner : DOT_C",
+"corner : DOT_START",
+"corner : DOT_END",
+"corner : TOP",
+"corner : BOTTOM",
+"corner : LEFT",
+"corner : RIGHT",
+"corner : UPPER LEFT",
+"corner : LOWER LEFT",
+"corner : UPPER RIGHT",
+"corner : LOWER RIGHT",
+"corner : LEFT_CORNER",
+"corner : RIGHT_CORNER",
+"corner : UPPER LEFT_CORNER",
+"corner : LOWER LEFT_CORNER",
+"corner : UPPER RIGHT_CORNER",
+"corner : LOWER RIGHT_CORNER",
+"corner : CENTER",
+"corner : START",
+"corner : END",
+"expr : VARIABLE",
+"expr : NUMBER",
+"expr : place DOT_X",
+"expr : place DOT_Y",
+"expr : place DOT_HT",
+"expr : place DOT_WID",
+"expr : place DOT_RAD",
+"expr : expr '+' expr",
+"expr : expr '-' expr",
+"expr : expr '*' expr",
+"expr : expr '/' expr",
+"expr : expr '%' expr",
+"expr : expr '^' expr",
+"expr : '-' expr",
+"expr : '(' any_expr ')'",
+"expr : SIN '(' any_expr ')'",
+"expr : COS '(' any_expr ')'",
+"expr : ATAN2 '(' any_expr ',' any_expr ')'",
+"expr : LOG '(' any_expr ')'",
+"expr : EXP '(' any_expr ')'",
+"expr : SQRT '(' any_expr ')'",
+"expr : K_MAX '(' any_expr ',' any_expr ')'",
+"expr : K_MIN '(' any_expr ',' any_expr ')'",
+"expr : INT '(' any_expr ')'",
+"expr : RAND '(' any_expr ')'",
+"expr : RAND '(' ')'",
+"expr : SRAND '(' any_expr ')'",
+"expr : expr '<' expr",
+"expr : expr LESSEQUAL expr",
+"expr : expr '>' expr",
+"expr : expr GREATEREQUAL expr",
+"expr : expr EQUALEQUAL expr",
+"expr : expr NOTEQUAL expr",
+"expr : expr ANDAND expr",
+"expr : expr OROR expr",
+"expr : '!' expr",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 1546 "/home/cjk/groff/src/preproc/pic/pic.y"
+
+/* bison defines const to be empty unless __STDC__ is defined, which it
+isn't under cfront */
+
+#ifdef const
+#undef const
+#endif
+
+static struct {
+ const char *name;
+ double val;
+ int scaled; // non-zero if val should be multiplied by scale
+} defaults_table[] = {
+ { "arcrad", .25, 1 },
+ { "arrowht", .1, 1 },
+ { "arrowwid", .05, 1 },
+ { "circlerad", .25, 1 },
+ { "boxht", .5, 1 },
+ { "boxwid", .75, 1 },
+ { "boxrad", 0.0, 1 },
+ { "dashwid", .05, 1 },
+ { "ellipseht", .5, 1 },
+ { "ellipsewid", .75, 1 },
+ { "moveht", .5, 1 },
+ { "movewid", .5, 1 },
+ { "lineht", .5, 1 },
+ { "linewid", .5, 1 },
+ { "textht", 0.0, 1 },
+ { "textwid", 0.0, 1 },
+ { "scale", 1.0, 0 },
+ { "linethick", -1.0, 0 }, // in points
+ { "fillval", .5, 0 },
+ { "arrowhead", 1.0, 0 },
+ { "maxpswid", 8.5, 0 },
+ { "maxpsht", 11.0, 0 },
+};
+
+place *lookup_label(const char *label)
+{
+ saved_state *state = current_saved_state;
+ PTABLE(place) *tbl = current_table;
+ for (;;) {
+ place *pl = tbl->lookup(label);
+ if (pl)
+ return pl;
+ if (!state)
+ return 0;
+ tbl = state->tbl;
+ state = state->prev;
+ }
+}
+
+void define_label(const char *label, const place *pl)
+{
+ place *p = new place;
+ *p = *pl;
+ current_table->define(label, p);
+}
+
+int lookup_variable(const char *name, double *val)
+{
+ place *pl = lookup_label(name);
+ if (pl) {
+ *val = pl->x;
+ return 1;
+ }
+ return 0;
+}
+
+void define_variable(const char *name, double val)
+{
+ place *p = new place;
+ p->obj = 0;
+ p->x = val;
+ p->y = 0.0;
+ current_table->define(name, p);
+ if (strcmp(name, "scale") == 0) {
+ // When the scale changes, reset all scaled pre-defined variables to
+ // their default values.
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (defaults_table[i].scaled)
+ define_variable(defaults_table[i].name, val*defaults_table[i].val);
+ }
+}
+
+// called once only (not once per parse)
+
+void parse_init()
+{
+ current_direction = RIGHT_DIRECTION;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+ // This resets everything to its default value.
+ reset_all();
+}
+
+void reset(const char *nm)
+{
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (strcmp(nm, defaults_table[i].name) == 0) {
+ double val = defaults_table[i].val;
+ if (defaults_table[i].scaled) {
+ double scale;
+ lookup_variable("scale", &scale);
+ val *= scale;
+ }
+ define_variable(defaults_table[i].name, val);
+ return;
+ }
+ lex_error("`%1' is not a predefined variable", nm);
+}
+
+void reset_all()
+{
+ // We only have to explicitly reset the pre-defined variables that
+ // aren't scaled because `scale' is not scaled, and changing the
+ // value of `scale' will reset all the pre-defined variables that
+ // are scaled.
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (!defaults_table[i].scaled)
+ define_variable(defaults_table[i].name, defaults_table[i].val);
+}
+
+// called after each parse
+
+void parse_cleanup()
+{
+ while (current_saved_state != 0) {
+ delete current_table;
+ current_table = current_saved_state->tbl;
+ saved_state *tem = current_saved_state;
+ current_saved_state = current_saved_state->prev;
+ delete tem;
+ }
+ assert(current_table == &top_table);
+ PTABLE_ITERATOR(place) iter(current_table);
+ const char *key;
+ place *pl;
+ while (iter.next(&key, &pl))
+ if (pl->obj != 0) {
+ position pos = pl->obj->origin();
+ pl->obj = 0;
+ pl->x = pos.x;
+ pl->y = pos.y;
+ }
+ while (olist.head != 0) {
+ object *tem = olist.head;
+ olist.head = olist.head->next;
+ delete tem;
+ }
+ olist.tail = 0;
+ current_direction = RIGHT_DIRECTION;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+}
+
+const char *ordinal_postfix(int n)
+{
+ if (n < 10 || n > 20)
+ switch (n % 10) {
+ case 1:
+ return "st";
+ case 2:
+ return "nd";
+ case 3:
+ return "rd";
+ }
+ return "th";
+}
+
+const char *object_type_name(object_type type)
+{
+ switch (type) {
+ case BOX_OBJECT:
+ return "box";
+ case CIRCLE_OBJECT:
+ return "circle";
+ case ELLIPSE_OBJECT:
+ return "ellipse";
+ case ARC_OBJECT:
+ return "arc";
+ case SPLINE_OBJECT:
+ return "spline";
+ case LINE_OBJECT:
+ return "line";
+ case ARROW_OBJECT:
+ return "arrow";
+ case MOVE_OBJECT:
+ return "move";
+ case TEXT_OBJECT:
+ return "\"\"";
+ case BLOCK_OBJECT:
+ return "[]";
+ case OTHER_OBJECT:
+ case MARK_OBJECT:
+ default:
+ break;
+ }
+ return "object";
+}
+
+static char sprintf_buf[1024];
+
+char *format_number(const char *form, double n)
+{
+ if (form == 0)
+ form = "%g";
+ else {
+ // this is a fairly feeble attempt at validation of the format
+ int nspecs = 0;
+ for (const char *p = form; *p != '\0'; p++)
+ if (*p == '%') {
+ if (p[1] == '%')
+ p++;
+ else
+ nspecs++;
+ }
+ if (nspecs > 1) {
+ lex_error("bad format `%1'", form);
+ return strsave(form);
+ }
+ }
+ sprintf(sprintf_buf, form, n);
+ return strsave(sprintf_buf);
+}
+
+char *do_sprintf(const char *form, const double *v, int nv)
+{
+ string result;
+ int i = 0;
+ string one_format;
+ while (*form) {
+ if (*form == '%') {
+ one_format += *form++;
+ for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
+ one_format += *form;
+ if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
+ lex_error("bad sprintf format");
+ result += one_format;
+ result += form;
+ break;
+ }
+ if (*form == '%') {
+ one_format += *form++;
+ one_format += '\0';
+ sprintf(sprintf_buf, one_format.contents());
+ }
+ else {
+ if (i >= nv) {
+ lex_error("too few arguments to sprintf");
+ result += one_format;
+ result += form;
+ break;
+ }
+ one_format += *form++;
+ one_format += '\0';
+ sprintf(sprintf_buf, one_format.contents(), v[i++]);
+ }
+ one_format.clear();
+ result += sprintf_buf;
+ }
+ else
+ result += *form++;
+ }
+ result += '\0';
+ return strsave(result.contents());
+}
+#line 3409 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+#if defined(__STDC__)
+yyparse(void)
+#else
+yyparse()
+#endif
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 2:
+#line 283 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (olist.head)
+ print_picture(olist.head);
+ }
+break;
+case 3:
+#line 292 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pl = yyvsp[-1].pl; }
+break;
+case 4:
+#line 297 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pl = yyvsp[0].pl; }
+break;
+case 5:
+#line 299 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pl = yyvsp[-2].pl; }
+break;
+case 10:
+#line 314 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ define_variable(yyvsp[-2].str, yyvsp[0].x);
+ a_delete yyvsp[-2].str;
+ }
+break;
+case 11:
+#line 319 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ place *p = lookup_label(yyvsp[-3].str);
+ if (!p) {
+ lex_error("variable `%1' not defined", yyvsp[-3].str);
+ YYABORT;
+ }
+ p->obj = 0;
+ p->x = yyvsp[0].x;
+ p->y = 0.0;
+ a_delete yyvsp[-3].str;
+ }
+break;
+case 12:
+#line 331 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ current_direction = UP_DIRECTION; }
+break;
+case 13:
+#line 333 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ current_direction = DOWN_DIRECTION; }
+break;
+case 14:
+#line 335 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ current_direction = LEFT_DIRECTION; }
+break;
+case 15:
+#line 337 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ current_direction = RIGHT_DIRECTION; }
+break;
+case 16:
+#line 339 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ olist.append(make_command_object(yyvsp[0].lstr.str, yyvsp[0].lstr.filename,
+ yyvsp[0].lstr.lineno));
+ }
+break;
+case 17:
+#line 344 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ olist.append(make_command_object(yyvsp[0].lstr.str, yyvsp[0].lstr.filename,
+ yyvsp[0].lstr.lineno));
+ }
+break;
+case 18:
+#line 349 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ fprintf(stderr, "%s\n", yyvsp[0].lstr.str);
+ a_delete yyvsp[0].lstr.str;
+ fflush(stderr);
+ }
+break;
+case 19:
+#line 355 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 1; }
+break;
+case 20:
+#line 357 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ delim_flag = 0;
+ if (safer_flag)
+ lex_error("unsafe to run command `%1'", yyvsp[0].str);
+ else
+ system(yyvsp[0].str);
+ a_delete yyvsp[0].str;
+ }
+break;
+case 21:
+#line 366 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ do_copy(yyvsp[0].lstr.str);
+ /* do not delete the filename*/
+ }
+break;
+case 22:
+#line 373 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 2; }
+break;
+case 23:
+#line 375 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 0; }
+break;
+case 24:
+#line 377 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ copy_file_thru(yyvsp[-5].lstr.str, yyvsp[-2].str, yyvsp[0].str);
+ /* do not delete the filename*/
+ a_delete yyvsp[-2].str;
+ a_delete yyvsp[0].str;
+ }
+break;
+case 25:
+#line 386 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 2; }
+break;
+case 26:
+#line 388 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 0; }
+break;
+case 27:
+#line 390 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ copy_rest_thru(yyvsp[-2].str, yyvsp[0].str);
+ a_delete yyvsp[-2].str;
+ a_delete yyvsp[0].str;
+ }
+break;
+case 28:
+#line 398 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 1; }
+break;
+case 29:
+#line 400 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ delim_flag = 0;
+ if (yychar < 0)
+ do_lookahead();
+ do_for(yyvsp[-8].str, yyvsp[-6].x, yyvsp[-4].x, yyvsp[-3].by.is_multiplicative, yyvsp[-3].by.val, yyvsp[0].str);
+ }
+break;
+case 30:
+#line 407 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ if (yyvsp[0].if_data.x != 0.0)
+ push_body(yyvsp[0].if_data.body);
+ a_delete yyvsp[0].if_data.body;
+ }
+break;
+case 31:
+#line 415 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 1; }
+break;
+case 32:
+#line 417 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ delim_flag = 0;
+ if (yychar < 0)
+ do_lookahead();
+ if (yyvsp[-3].if_data.x != 0.0)
+ push_body(yyvsp[-3].if_data.body);
+ else
+ push_body(yyvsp[0].str);
+ a_delete yyvsp[-3].if_data.body;
+ a_delete yyvsp[0].str;
+ }
+break;
+case 34:
+#line 430 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ define_variable("scale", 1.0); }
+break;
+case 35:
+#line 435 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ reset(yyvsp[0].str); a_delete yyvsp[0].str; }
+break;
+case 36:
+#line 437 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ reset(yyvsp[0].str); a_delete yyvsp[0].str; }
+break;
+case 37:
+#line 439 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ reset(yyvsp[0].str); a_delete yyvsp[0].str; }
+break;
+case 38:
+#line 444 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.lstr = yyvsp[0].lstr; }
+break;
+case 39:
+#line 446 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.lstr.str = new char[strlen(yyvsp[-1].lstr.str) + strlen(yyvsp[0].lstr.str) + 1];
+ strcpy(yyval.lstr.str, yyvsp[-1].lstr.str);
+ strcat(yyval.lstr.str, yyvsp[0].lstr.str);
+ a_delete yyvsp[-1].lstr.str;
+ a_delete yyvsp[0].lstr.str;
+ if (yyvsp[-1].lstr.filename) {
+ yyval.lstr.filename = yyvsp[-1].lstr.filename;
+ yyval.lstr.lineno = yyvsp[-1].lstr.lineno;
+ }
+ else if (yyvsp[0].lstr.filename) {
+ yyval.lstr.filename = yyvsp[0].lstr.filename;
+ yyval.lstr.lineno = yyvsp[0].lstr.lineno;
+ }
+ }
+break;
+case 40:
+#line 465 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.lstr.str = new char[GDIGITS + 1];
+ sprintf(yyval.lstr.str, "%g", yyvsp[0].x);
+ yyval.lstr.filename = 0;
+ yyval.lstr.lineno = 0;
+ }
+break;
+case 41:
+#line 472 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.lstr = yyvsp[0].lstr; }
+break;
+case 42:
+#line 474 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.lstr.str = new char[GDIGITS + 2 + GDIGITS + 1];
+ sprintf(yyval.lstr.str, "%g, %g", yyvsp[0].pair.x, yyvsp[0].pair.y);
+ yyval.lstr.filename = 0;
+ yyval.lstr.lineno = 0;
+ }
+break;
+case 43:
+#line 483 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 1; }
+break;
+case 44:
+#line 485 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ delim_flag = 0; yyval.if_data.x = yyvsp[-3].x; yyval.if_data.body = yyvsp[0].str; }
+break;
+case 45:
+#line 490 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.str = 0; }
+break;
+case 46:
+#line 492 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.str = yyvsp[0].lstr.str; }
+break;
+case 47:
+#line 497 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[0].x; }
+break;
+case 48:
+#line 499 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[0].x; }
+break;
+case 49:
+#line 504 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) == 0;
+ a_delete yyvsp[-2].lstr.str;
+ a_delete yyvsp[0].lstr.str;
+ }
+break;
+case 50:
+#line 510 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) != 0;
+ a_delete yyvsp[-2].lstr.str;
+ a_delete yyvsp[0].lstr.str;
+ }
+break;
+case 51:
+#line 516 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); }
+break;
+case 52:
+#line 518 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); }
+break;
+case 53:
+#line 520 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); }
+break;
+case 54:
+#line 522 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); }
+break;
+case 55:
+#line 524 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); }
+break;
+case 56:
+#line 526 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); }
+break;
+case 57:
+#line 528 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[0].x == 0.0); }
+break;
+case 58:
+#line 534 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.by.val = 1.0; yyval.by.is_multiplicative = 0; }
+break;
+case 59:
+#line 536 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 0; }
+break;
+case 60:
+#line 538 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 1; }
+break;
+case 61:
+#line 543 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl.obj = yyvsp[0].spec->make_object(&current_position,
+ &current_direction);
+ if (yyval.pl.obj == 0)
+ YYABORT;
+ delete yyvsp[0].spec;
+ if (yyval.pl.obj)
+ olist.append(yyval.pl.obj);
+ else {
+ yyval.pl.x = current_position.x;
+ yyval.pl.y = current_position.y;
+ }
+ }
+break;
+case 62:
+#line 557 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pl = yyvsp[0].pl; define_label(yyvsp[-3].str, & yyval.pl); a_delete yyvsp[-3].str; }
+break;
+case 63:
+#line 559 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl.obj = 0;
+ yyval.pl.x = yyvsp[0].pair.x;
+ yyval.pl.y = yyvsp[0].pair.y;
+ define_label(yyvsp[-3].str, & yyval.pl);
+ a_delete yyvsp[-3].str;
+ }
+break;
+case 64:
+#line 567 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl = yyvsp[0].pl;
+ define_label(yyvsp[-3].str, & yyval.pl);
+ a_delete yyvsp[-3].str;
+ }
+break;
+case 65:
+#line 573 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.state.x = current_position.x;
+ yyval.state.y = current_position.y;
+ yyval.state.dir = current_direction;
+ }
+break;
+case 66:
+#line 579 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ current_position.x = yyvsp[-2].state.x;
+ current_position.y = yyvsp[-2].state.y;
+ current_direction = yyvsp[-2].state.dir;
+ }
+break;
+case 67:
+#line 585 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl = yyvsp[-3].pl;
+ }
+break;
+case 68:
+#line 589 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl.obj = 0;
+ yyval.pl.x = current_position.x;
+ yyval.pl.y = current_position.y;
+ }
+break;
+case 69:
+#line 598 "/home/cjk/groff/src/preproc/pic/pic.y"
+{}
+break;
+case 70:
+#line 600 "/home/cjk/groff/src/preproc/pic/pic.y"
+{}
+break;
+case 71:
+#line 605 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(BOX_OBJECT);
+ }
+break;
+case 72:
+#line 609 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(CIRCLE_OBJECT);
+ }
+break;
+case 73:
+#line 613 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(ELLIPSE_OBJECT);
+ }
+break;
+case 74:
+#line 617 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(ARC_OBJECT);
+ yyval.spec->dir = current_direction;
+ }
+break;
+case 75:
+#line 622 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(LINE_OBJECT);
+ lookup_variable("lineht", & yyval.spec->segment_height);
+ lookup_variable("linewid", & yyval.spec->segment_width);
+ yyval.spec->dir = current_direction;
+ }
+break;
+case 76:
+#line 629 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(ARROW_OBJECT);
+ lookup_variable("lineht", & yyval.spec->segment_height);
+ lookup_variable("linewid", & yyval.spec->segment_width);
+ yyval.spec->dir = current_direction;
+ }
+break;
+case 77:
+#line 636 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(MOVE_OBJECT);
+ lookup_variable("moveht", & yyval.spec->segment_height);
+ lookup_variable("movewid", & yyval.spec->segment_width);
+ yyval.spec->dir = current_direction;
+ }
+break;
+case 78:
+#line 643 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(SPLINE_OBJECT);
+ lookup_variable("lineht", & yyval.spec->segment_height);
+ lookup_variable("linewid", & yyval.spec->segment_width);
+ yyval.spec->dir = current_direction;
+ }
+break;
+case 79:
+#line 650 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(TEXT_OBJECT);
+ yyval.spec->text = new text_item(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno);
+ }
+break;
+case 80:
+#line 655 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(TEXT_OBJECT);
+ yyval.spec->text = new text_item(format_number(0, yyvsp[0].x), 0, -1);
+ }
+break;
+case 81:
+#line 660 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = new object_spec(TEXT_OBJECT);
+ yyval.spec->text = new text_item(format_number(yyvsp[0].lstr.str, yyvsp[-1].x),
+ yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno);
+ a_delete yyvsp[0].lstr.str;
+ }
+break;
+case 82:
+#line 667 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ saved_state *p = new saved_state;
+ yyval.pstate = p;
+ p->x = current_position.x;
+ p->y = current_position.y;
+ p->dir = current_direction;
+ p->tbl = current_table;
+ p->prev = current_saved_state;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+ current_table = new PTABLE(place);
+ current_saved_state = p;
+ olist.append(make_mark_object());
+ }
+break;
+case 83:
+#line 682 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ current_position.x = yyvsp[-2].pstate->x;
+ current_position.y = yyvsp[-2].pstate->y;
+ current_direction = yyvsp[-2].pstate->dir;
+ yyval.spec = new object_spec(BLOCK_OBJECT);
+ olist.wrap_up_block(& yyval.spec->oblist);
+ yyval.spec->tbl = current_table;
+ current_table = yyvsp[-2].pstate->tbl;
+ current_saved_state = yyvsp[-2].pstate->prev;
+ delete yyvsp[-2].pstate;
+ }
+break;
+case 84:
+#line 694 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->height = yyvsp[0].x;
+ yyval.spec->flags |= HAS_HEIGHT;
+ }
+break;
+case 85:
+#line 700 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->radius = yyvsp[0].x;
+ yyval.spec->flags |= HAS_RADIUS;
+ }
+break;
+case 86:
+#line 706 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->width = yyvsp[0].x;
+ yyval.spec->flags |= HAS_WIDTH;
+ }
+break;
+case 87:
+#line 712 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->radius = yyvsp[0].x/2.0;
+ yyval.spec->flags |= HAS_RADIUS;
+ }
+break;
+case 88:
+#line 718 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= HAS_SEGMENT;
+ switch (yyval.spec->dir) {
+ case UP_DIRECTION:
+ yyval.spec->segment_pos.y += yyvsp[0].x;
+ break;
+ case DOWN_DIRECTION:
+ yyval.spec->segment_pos.y -= yyvsp[0].x;
+ break;
+ case RIGHT_DIRECTION:
+ yyval.spec->segment_pos.x += yyvsp[0].x;
+ break;
+ case LEFT_DIRECTION:
+ yyval.spec->segment_pos.x -= yyvsp[0].x;
+ break;
+ }
+ }
+break;
+case 89:
+#line 737 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->dir = UP_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.y += yyval.spec->segment_height;
+ }
+break;
+case 90:
+#line 744 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->dir = UP_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.y += yyvsp[0].x;
+ }
+break;
+case 91:
+#line 751 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->dir = DOWN_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.y -= yyval.spec->segment_height;
+ }
+break;
+case 92:
+#line 758 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->dir = DOWN_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.y -= yyvsp[0].x;
+ }
+break;
+case 93:
+#line 765 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->dir = RIGHT_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x += yyval.spec->segment_width;
+ }
+break;
+case 94:
+#line 772 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->dir = RIGHT_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x += yyvsp[0].x;
+ }
+break;
+case 95:
+#line 779 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->dir = LEFT_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x -= yyval.spec->segment_width;
+ }
+break;
+case 96:
+#line 786 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->dir = LEFT_DIRECTION;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x -= yyvsp[0].x;
+ }
+break;
+case 97:
+#line 793 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_FROM;
+ yyval.spec->from.x = yyvsp[0].pair.x;
+ yyval.spec->from.y = yyvsp[0].pair.y;
+ }
+break;
+case 98:
+#line 800 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ if (yyval.spec->flags & HAS_SEGMENT)
+ yyval.spec->segment_list = new segment(yyval.spec->segment_pos,
+ yyval.spec->segment_is_absolute,
+ yyval.spec->segment_list);
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x = yyvsp[0].pair.x;
+ yyval.spec->segment_pos.y = yyvsp[0].pair.y;
+ yyval.spec->segment_is_absolute = 1;
+ yyval.spec->flags |= HAS_TO;
+ yyval.spec->to.x = yyvsp[0].pair.x;
+ yyval.spec->to.y = yyvsp[0].pair.y;
+ }
+break;
+case 99:
+#line 815 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_AT;
+ yyval.spec->at.x = yyvsp[0].pair.x;
+ yyval.spec->at.y = yyvsp[0].pair.y;
+ if (yyval.spec->type != ARC_OBJECT) {
+ yyval.spec->flags |= HAS_FROM;
+ yyval.spec->from.x = yyvsp[0].pair.x;
+ yyval.spec->from.y = yyvsp[0].pair.y;
+ }
+ }
+break;
+case 100:
+#line 827 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_WITH;
+ yyval.spec->with = yyvsp[0].pth;
+ }
+break;
+case 101:
+#line 833 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_SEGMENT;
+ yyval.spec->segment_pos.x += yyvsp[0].pair.x;
+ yyval.spec->segment_pos.y += yyvsp[0].pair.y;
+ }
+break;
+case 102:
+#line 840 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->flags & HAS_SEGMENT) {
+ yyval.spec->segment_list = new segment(yyval.spec->segment_pos,
+ yyval.spec->segment_is_absolute,
+ yyval.spec->segment_list);
+ yyval.spec->flags &= ~HAS_SEGMENT;
+ yyval.spec->segment_pos.x = yyval.spec->segment_pos.y = 0.0;
+ yyval.spec->segment_is_absolute = 0;
+ }
+ }
+break;
+case 103:
+#line 852 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec; /* nothing*/
+ }
+break;
+case 104:
+#line 856 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DOTTED;
+ lookup_variable("dashwid", & yyval.spec->dash_width);
+ }
+break;
+case 105:
+#line 862 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_DOTTED;
+ yyval.spec->dash_width = yyvsp[0].x;
+ }
+break;
+case 106:
+#line 868 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DASHED;
+ lookup_variable("dashwid", & yyval.spec->dash_width);
+ }
+break;
+case 107:
+#line 874 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_DASHED;
+ yyval.spec->dash_width = yyvsp[0].x;
+ }
+break;
+case 108:
+#line 880 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DEFAULT_FILLED;
+ }
+break;
+case 109:
+#line 885 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_FILLED;
+ yyval.spec->fill = yyvsp[0].x;
+ }
+break;
+case 110:
+#line 891 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ /* line chop chop means line chop 0 chop 0*/
+ if (yyval.spec->flags & IS_DEFAULT_CHOPPED) {
+ yyval.spec->flags |= IS_CHOPPED;
+ yyval.spec->flags &= ~IS_DEFAULT_CHOPPED;
+ yyval.spec->start_chop = yyval.spec->end_chop = 0.0;
+ }
+ else if (yyval.spec->flags & IS_CHOPPED) {
+ yyval.spec->end_chop = 0.0;
+ }
+ else {
+ yyval.spec->flags |= IS_DEFAULT_CHOPPED;
+ }
+ }
+break;
+case 111:
+#line 907 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ if (yyval.spec->flags & IS_DEFAULT_CHOPPED) {
+ yyval.spec->flags |= IS_CHOPPED;
+ yyval.spec->flags &= ~IS_DEFAULT_CHOPPED;
+ yyval.spec->start_chop = 0.0;
+ yyval.spec->end_chop = yyvsp[0].x;
+ }
+ else if (yyval.spec->flags & IS_CHOPPED) {
+ yyval.spec->end_chop = yyvsp[0].x;
+ }
+ else {
+ yyval.spec->start_chop = yyval.spec->end_chop = yyvsp[0].x;
+ yyval.spec->flags |= IS_CHOPPED;
+ }
+ }
+break;
+case 112:
+#line 924 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_SAME;
+ }
+break;
+case 113:
+#line 929 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_INVISIBLE;
+ }
+break;
+case 114:
+#line 934 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= HAS_LEFT_ARROW_HEAD;
+ }
+break;
+case 115:
+#line 939 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= HAS_RIGHT_ARROW_HEAD;
+ }
+break;
+case 116:
+#line 944 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
+ }
+break;
+case 117:
+#line 949 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_CLOCKWISE;
+ }
+break;
+case 118:
+#line 954 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags &= ~IS_CLOCKWISE;
+ }
+break;
+case 119:
+#line 959 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ text_item **p;
+ for (p = & yyval.spec->text; *p; p = &(*p)->next)
+ ;
+ *p = new text_item(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno);
+ }
+break;
+case 120:
+#line 967 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ text_item *p;
+ for (p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.h = LEFT_ADJUST;
+ }
+ }
+break;
+case 121:
+#line 977 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ text_item *p;
+ for (p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.h = RIGHT_ADJUST;
+ }
+ }
+break;
+case 122:
+#line 987 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ text_item *p;
+ for (p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.v = ABOVE_ADJUST;
+ }
+ }
+break;
+case 123:
+#line 997 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ text_item *p;
+ for (p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.v = BELOW_ADJUST;
+ }
+ }
+break;
+case 124:
+#line 1007 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_THICKNESS;
+ yyval.spec->thickness = yyvsp[0].x;
+ }
+break;
+case 125:
+#line 1013 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_ALIGNED;
+ }
+break;
+case 126:
+#line 1021 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.lstr = yyvsp[0].lstr;
+ }
+break;
+case 127:
+#line 1025 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.lstr.filename = yyvsp[-2].lstr.filename;
+ yyval.lstr.lineno = yyvsp[-2].lstr.lineno;
+ yyval.lstr.str = do_sprintf(yyvsp[-2].lstr.str, yyvsp[-1].dv.v, yyvsp[-1].dv.nv);
+ a_delete yyvsp[-1].dv.v;
+ a_delete yyvsp[-2].lstr.str;
+ }
+break;
+case 128:
+#line 1036 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.dv.v = 0;
+ yyval.dv.nv = 0;
+ yyval.dv.maxv = 0;
+ }
+break;
+case 129:
+#line 1042 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.dv = yyvsp[-2].dv;
+ if (yyval.dv.nv >= yyval.dv.maxv) {
+ if (yyval.dv.nv == 0) {
+ yyval.dv.v = new double[4];
+ yyval.dv.maxv = 4;
+ }
+ else {
+ double *oldv = yyval.dv.v;
+ yyval.dv.maxv *= 2;
+ yyval.dv.v = new double[yyval.dv.maxv];
+ memcpy(yyval.dv.v, oldv, yyval.dv.nv*sizeof(double));
+ a_delete oldv;
+ }
+ }
+ yyval.dv.v[yyval.dv.nv] = yyvsp[0].x;
+ yyval.dv.nv += 1;
+ }
+break;
+case 130:
+#line 1064 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pair = yyvsp[0].pair; }
+break;
+case 131:
+#line 1066 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ position pos = yyvsp[0].pl;
+ yyval.pair.x = pos.x;
+ yyval.pair.y = pos.y;
+ }
+break;
+case 132:
+#line 1075 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pair = yyvsp[0].pair; }
+break;
+case 133:
+#line 1077 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pair.x = yyvsp[-2].pair.x + yyvsp[0].pair.x;
+ yyval.pair.y = yyvsp[-2].pair.y + yyvsp[0].pair.y;
+ }
+break;
+case 134:
+#line 1082 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pair.x = yyvsp[-2].pair.x - yyvsp[0].pair.x;
+ yyval.pair.y = yyvsp[-2].pair.y - yyvsp[0].pair.y;
+ }
+break;
+case 135:
+#line 1087 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pair.x = yyvsp[-3].pair.x;
+ yyval.pair.y = yyvsp[-1].pair.y;
+ }
+break;
+case 136:
+#line 1092 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pair.x = (1.0 - yyvsp[-4].x)*yyvsp[-2].pair.x + yyvsp[-4].x*yyvsp[0].pair.x;
+ yyval.pair.y = (1.0 - yyvsp[-4].x)*yyvsp[-2].pair.y + yyvsp[-4].x*yyvsp[0].pair.y;
+ }
+break;
+case 137:
+#line 1097 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pair.x = (1.0 - yyvsp[-5].x)*yyvsp[-3].pair.x + yyvsp[-5].x*yyvsp[-1].pair.x;
+ yyval.pair.y = (1.0 - yyvsp[-5].x)*yyvsp[-3].pair.y + yyvsp[-5].x*yyvsp[-1].pair.y;
+ }
+break;
+case 140:
+#line 1110 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pair.x = yyvsp[-2].x; yyval.pair.y = yyvsp[0].x; }
+break;
+case 141:
+#line 1112 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pair = yyvsp[-1].pair; }
+break;
+case 142:
+#line 1117 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.pl = yyvsp[0].pl; }
+break;
+case 143:
+#line 1119 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ path pth(yyvsp[0].crn);
+ if (!pth.follow(yyvsp[-1].pl, & yyval.pl))
+ YYABORT;
+ }
+break;
+case 144:
+#line 1125 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ path pth(yyvsp[-1].crn);
+ if (!pth.follow(yyvsp[0].pl, & yyval.pl))
+ YYABORT;
+ }
+break;
+case 145:
+#line 1131 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ path pth(yyvsp[-2].crn);
+ if (!pth.follow(yyvsp[0].pl, & yyval.pl))
+ YYABORT;
+ }
+break;
+case 146:
+#line 1137 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl.x = current_position.x;
+ yyval.pl.y = current_position.y;
+ yyval.pl.obj = 0;
+ }
+break;
+case 147:
+#line 1146 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ place *p = lookup_label(yyvsp[0].str);
+ if (!p) {
+ lex_error("there is no place `%1'", yyvsp[0].str);
+ YYABORT;
+ }
+ yyval.pl = *p;
+ a_delete yyvsp[0].str;
+ }
+break;
+case 148:
+#line 1156 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pl.obj = yyvsp[0].obj;
+ }
+break;
+case 149:
+#line 1160 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ path pth(yyvsp[0].str);
+ if (!pth.follow(yyvsp[-2].pl, & yyval.pl))
+ YYABORT;
+ }
+break;
+case 150:
+#line 1169 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.n = yyvsp[0].n; }
+break;
+case 151:
+#line 1171 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ /* XXX Check for overflow (and non-integers?).*/
+ yyval.n = (int)yyvsp[-1].x;
+ }
+break;
+case 152:
+#line 1179 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.n = 1; }
+break;
+case 153:
+#line 1181 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.n = yyvsp[-1].n; }
+break;
+case 154:
+#line 1186 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ int count = 0;
+ object *p;
+ for (p = olist.head; p != 0; p = p->next)
+ if (p->type() == yyvsp[0].obtype && ++count == yyvsp[-1].n) {
+ yyval.obj = p;
+ break;
+ }
+ if (p == 0) {
+ lex_error("there is no %1%2 %3", yyvsp[-1].n, ordinal_postfix(yyvsp[-1].n),
+ object_type_name(yyvsp[0].obtype));
+ YYABORT;
+ }
+ }
+break;
+case 155:
+#line 1201 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ int count = 0;
+ object *p;
+ for (p = olist.tail; p != 0; p = p->prev)
+ if (p->type() == yyvsp[0].obtype && ++count == yyvsp[-1].n) {
+ yyval.obj = p;
+ break;
+ }
+ if (p == 0) {
+ lex_error("there is no %1%2 last %3", yyvsp[-1].n,
+ ordinal_postfix(yyvsp[-1].n), object_type_name(yyvsp[0].obtype));
+ YYABORT;
+ }
+ }
+break;
+case 156:
+#line 1219 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = BOX_OBJECT; }
+break;
+case 157:
+#line 1221 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = CIRCLE_OBJECT; }
+break;
+case 158:
+#line 1223 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = ELLIPSE_OBJECT; }
+break;
+case 159:
+#line 1225 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = ARC_OBJECT; }
+break;
+case 160:
+#line 1227 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = LINE_OBJECT; }
+break;
+case 161:
+#line 1229 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = ARROW_OBJECT; }
+break;
+case 162:
+#line 1231 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = SPLINE_OBJECT; }
+break;
+case 163:
+#line 1233 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = BLOCK_OBJECT; }
+break;
+case 164:
+#line 1235 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.obtype = TEXT_OBJECT; }
+break;
+case 165:
+#line 1240 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = new path(yyvsp[0].str);
+ }
+break;
+case 166:
+#line 1244 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = yyvsp[-2].pth;
+ yyval.pth->append(yyvsp[0].str);
+ }
+break;
+case 167:
+#line 1252 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = new path(yyvsp[0].crn);
+ }
+break;
+case 168:
+#line 1259 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 169:
+#line 1263 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = yyvsp[-1].pth;
+ yyval.pth->append(yyvsp[0].crn);
+ }
+break;
+case 170:
+#line 1271 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 171:
+#line 1275 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ yyval.pth = yyvsp[-3].pth;
+ yyval.pth->set_ypath(yyvsp[-1].pth);
+ }
+break;
+case 172:
+#line 1281 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ lex_warning("`%1%2 last %3' in `with' argument ignored",
+ yyvsp[-3].n, ordinal_postfix(yyvsp[-3].n), object_type_name(yyvsp[-1].obtype));
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 173:
+#line 1287 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ lex_warning("`last %1' in `with' argument ignored",
+ object_type_name(yyvsp[-1].obtype));
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 174:
+#line 1293 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ lex_warning("`%1%2 %3' in `with' argument ignored",
+ yyvsp[-2].n, ordinal_postfix(yyvsp[-2].n), object_type_name(yyvsp[-1].obtype));
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 175:
+#line 1299 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ lex_warning("initial `%1' in `with' argument ignored", yyvsp[-1].str);
+ a_delete yyvsp[-1].str;
+ yyval.pth = yyvsp[0].pth;
+ }
+break;
+case 176:
+#line 1308 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north; }
+break;
+case 177:
+#line 1310 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::east; }
+break;
+case 178:
+#line 1312 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::west; }
+break;
+case 179:
+#line 1314 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south; }
+break;
+case 180:
+#line 1316 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_east; }
+break;
+case 181:
+#line 1318 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object:: south_east; }
+break;
+case 182:
+#line 1320 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_west; }
+break;
+case 183:
+#line 1322 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south_west; }
+break;
+case 184:
+#line 1324 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::center; }
+break;
+case 185:
+#line 1326 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::start; }
+break;
+case 186:
+#line 1328 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::end; }
+break;
+case 187:
+#line 1330 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north; }
+break;
+case 188:
+#line 1332 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south; }
+break;
+case 189:
+#line 1334 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::west; }
+break;
+case 190:
+#line 1336 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::east; }
+break;
+case 191:
+#line 1338 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_west; }
+break;
+case 192:
+#line 1340 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south_west; }
+break;
+case 193:
+#line 1342 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_east; }
+break;
+case 194:
+#line 1344 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south_east; }
+break;
+case 195:
+#line 1346 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::west; }
+break;
+case 196:
+#line 1348 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::east; }
+break;
+case 197:
+#line 1350 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_west; }
+break;
+case 198:
+#line 1352 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south_west; }
+break;
+case 199:
+#line 1354 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::north_east; }
+break;
+case 200:
+#line 1356 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::south_east; }
+break;
+case 201:
+#line 1358 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::center; }
+break;
+case 202:
+#line 1360 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::start; }
+break;
+case 203:
+#line 1362 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.crn = &object::end; }
+break;
+case 204:
+#line 1367 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (!lookup_variable(yyvsp[0].str, & yyval.x)) {
+ lex_error("there is no variable `%1'", yyvsp[0].str);
+ YYABORT;
+ }
+ a_delete yyvsp[0].str;
+ }
+break;
+case 205:
+#line 1375 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[0].x; }
+break;
+case 206:
+#line 1377 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->origin().x;
+ else
+ yyval.x = yyvsp[-1].pl.x;
+ }
+break;
+case 207:
+#line 1384 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->origin().y;
+ else
+ yyval.x = yyvsp[-1].pl.y;
+ }
+break;
+case 208:
+#line 1391 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->height();
+ else
+ yyval.x = 0.0;
+ }
+break;
+case 209:
+#line 1398 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->width();
+ else
+ yyval.x = 0.0;
+ }
+break;
+case 210:
+#line 1405 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->radius();
+ else
+ yyval.x = 0.0;
+ }
+break;
+case 211:
+#line 1412 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-2].x + yyvsp[0].x; }
+break;
+case 212:
+#line 1414 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-2].x - yyvsp[0].x; }
+break;
+case 213:
+#line 1416 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-2].x * yyvsp[0].x; }
+break;
+case 214:
+#line 1418 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[0].x == 0.0) {
+ lex_error("division by zero");
+ YYABORT;
+ }
+ yyval.x = yyvsp[-2].x/yyvsp[0].x;
+ }
+break;
+case 215:
+#line 1426 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ if (yyvsp[0].x == 0.0) {
+ lex_error("modulus by zero");
+ YYABORT;
+ }
+ yyval.x = fmod(yyvsp[-2].x, yyvsp[0].x);
+ }
+break;
+case 216:
+#line 1434 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = pow(yyvsp[-2].x, yyvsp[0].x);
+ if (errno == EDOM) {
+ lex_error("arguments to `^' operator out of domain");
+ YYABORT;
+ }
+ if (errno == ERANGE) {
+ lex_error("result of `^' operator out of range");
+ YYABORT;
+ }
+ }
+break;
+case 217:
+#line 1447 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = -yyvsp[0].x; }
+break;
+case 218:
+#line 1449 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-1].x; }
+break;
+case 219:
+#line 1451 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = sin(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("sin result out of range");
+ YYABORT;
+ }
+ }
+break;
+case 220:
+#line 1460 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = cos(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("cos result out of range");
+ YYABORT;
+ }
+ }
+break;
+case 221:
+#line 1469 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = atan2(yyvsp[-3].x, yyvsp[-1].x);
+ if (errno == EDOM) {
+ lex_error("atan2 argument out of domain");
+ YYABORT;
+ }
+ if (errno == ERANGE) {
+ lex_error("atan2 result out of range");
+ YYABORT;
+ }
+ }
+break;
+case 222:
+#line 1482 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = log10(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("log result out of range");
+ YYABORT;
+ }
+ }
+break;
+case 223:
+#line 1491 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = pow(10.0, yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("exp result out of range");
+ YYABORT;
+ }
+ }
+break;
+case 224:
+#line 1500 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ errno = 0;
+ yyval.x = sqrt(yyvsp[-1].x);
+ if (errno == EDOM) {
+ lex_error("sqrt argument out of domain");
+ YYABORT;
+ }
+ }
+break;
+case 225:
+#line 1509 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-3].x > yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; }
+break;
+case 226:
+#line 1511 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = yyvsp[-3].x < yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; }
+break;
+case 227:
+#line 1513 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = floor(yyvsp[-1].x); }
+break;
+case 228:
+#line 1515 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*yyvsp[-1].x); }
+break;
+case 229:
+#line 1517 "/home/cjk/groff/src/preproc/pic/pic.y"
+{
+ /* return a random number in the range [0,1) */
+ /* portable, but not very random */
+ yyval.x = (rand() & 0x7fff) / double(0x8000);
+ }
+break;
+case 230:
+#line 1523 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = 0; srand((unsigned int)yyvsp[-1].x); }
+break;
+case 231:
+#line 1525 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x < yyvsp[0].x); }
+break;
+case 232:
+#line 1527 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x <= yyvsp[0].x); }
+break;
+case 233:
+#line 1529 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x > yyvsp[0].x); }
+break;
+case 234:
+#line 1531 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x >= yyvsp[0].x); }
+break;
+case 235:
+#line 1533 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x == yyvsp[0].x); }
+break;
+case 236:
+#line 1535 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != yyvsp[0].x); }
+break;
+case 237:
+#line 1537 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); }
+break;
+case 238:
+#line 1539 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); }
+break;
+case 239:
+#line 1541 "/home/cjk/groff/src/preproc/pic/pic.y"
+{ yyval.x = (yyvsp[0].x == 0.0); }
+break;
+#line 5161 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/contrib/groff/src/preproc/pic/pic.h b/contrib/groff/src/preproc/pic/pic.h
new file mode 100644
index 000000000000..36c36d1b8a00
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/pic.h
@@ -0,0 +1,104 @@
+// -*- 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. */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef NEED_DECLARATION_HYPOT
+extern "C" {
+ double hypot(double, double);
+}
+#endif /* NEED_DECLARATION_HYPOT */
+
+#include "assert.h"
+#include "cset.h"
+#include "lib.h"
+#include "stringclass.h"
+#include "errarg.h"
+#include "error.h"
+#include "position.h"
+#include "text.h"
+#include "output.h"
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+class input {
+ input *next;
+public:
+ input();
+ virtual ~input();
+ virtual int get() = 0;
+ virtual int peek() = 0;
+ virtual int get_location(const char **, int *);
+ friend class input_stack;
+ friend class copy_rest_thru_input;
+};
+
+class file_input : public input {
+ FILE *fp;
+ const char *filename;
+ int lineno;
+ string line;
+ const char *ptr;
+ int read_line();
+public:
+ file_input(FILE *, const char *);
+ ~file_input();
+ int get();
+ int peek();
+ int get_location(const char **, int *);
+};
+
+void lex_init(input *);
+int get_location(char **, int *);
+
+void do_copy(const char *file);
+void parse_init();
+void parse_cleanup();
+
+void lex_error(const char *message,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+void lex_warning(const char *message,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+void lex_cleanup();
+
+extern int flyback_flag;
+extern int command_char;
+// zero_length_line_flag is non-zero if zero-length lines are drawn
+// as dots by the output device
+extern int zero_length_line_flag;
+extern int driver_extension_flag;
+extern int compatible_flag;
+extern int safer_flag;
diff --git a/contrib/groff/src/preproc/pic/pic.man b/contrib/groff/src/preproc/pic/pic.man
new file mode 100644
index 000000000000..e18702171187
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/pic.man
@@ -0,0 +1,883 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.ie \n(.g .ds ic \/
+.el .ds ic \^
+.\" The BSD man macros can't handle " in arguments to font change macros,
+.\" so use \(ts instead of ".
+.tr \(ts"
+.TH @G@PIC @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@pic \- compile pictures for troff or TeX
+.SH SYNOPSIS
+.B @g@pic
+[
+.B \-nvCSU
+]
+[
+.I filename
+\&.\|.\|.
+]
+.br
+.B @g@pic
+.B \-t
+[
+.B \-cvzCSU
+]
+[
+.I filename
+\&.\|.\|.
+]
+.SH DESCRIPTION
+This manual page describes the GNU version of
+.BR pic ,
+which is part of the groff document formatting system.
+.B pic
+compiles descriptions of pictures embedded within
+.B troff
+or \*(tx input files into commands that are understood by \*(tx or
+.BR troff .
+Each picture starts with a line beginning with
+.B .PS
+and ends with a line beginning with
+.BR .PE .
+Anything outside of
+.B .PS
+and
+.B .PE
+is passed through without change.
+.LP
+It is the user's responsibility to provide appropriate definitions of the
+.B PS
+and
+.B PE
+macros.
+When the macro package being used does not supply such definitions
+(for example, old versions of \-ms),
+appropriate definitions can be obtained with
+.BR \-mpic :
+these will center each picture.
+.SH OPTIONS
+Options that do not take arguments may be grouped behind a single
+.BR \- .
+The special option
+.B \-\^\-
+can be used to mark the end of the options.
+A filename of
+.B \-
+refers to the standard input.
+.TP
+.B \-C
+Recognize
+.B .PS
+and
+.B .PE
+even when followed by a character other than space or newline.
+.TP
+.B \-S
+Safer mode; do not execute
+.B sh
+commands.
+This can be useful when operating on untrustworthy input.
+(enabled by default)
+.TP
+.B \-U
+Unsafe mode; revert the default option
+.BR \-S .
+.TP
+.B \-n
+Don't use the groff extensions to the troff drawing commands.
+You should use this if you are using a postprocessor that doesn't support
+these extensions.
+The extensions are described in
+.BR groff_out (@MAN5EXT@).
+The
+.B \-n
+option also causes
+.B pic
+not to use zero-length lines to draw dots in troff mode.
+.TP
+.B \-t
+\*(tx mode.
+.TP
+.B \-c
+Be more compatible with
+.BR tpic .
+Implies
+.BR \-t .
+Lines beginning with
+.B \e
+are not passed through transparently.
+Lines beginning with
+.B .
+are passed through with the initial
+.B .
+changed to
+.BR \e .
+A line beginning with
+.B .ps
+is given special treatment:
+it takes an optional integer argument specifying
+the line thickness (pen size) in milliinches;
+a missing argument restores the previous line thickness;
+the default line thickness is 8 milliinches.
+The line thickness thus specified takes effect only
+when a non-negative line thickness has not been
+specified by use of the
+.B thickness
+attribute or by setting the
+.B linethick
+variable.
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-z
+In \*(tx mode draw dots using zero-length lines.
+.LP
+The following options supported by other versions of
+.B pic
+are ignored:
+.TP
+.B \-D
+Draw all lines using the \eD escape sequence.
+.B pic
+always does this.
+.TP
+.BI \-T \ dev
+Generate output for the
+.B troff
+device
+.IR dev .
+This is unnecessary because the
+.B troff
+output generated by
+.B pic
+is device-independent.
+.SH USAGE
+This section describes only the differences between GNU
+.B pic
+and the original version of
+.BR pic .
+Many of these differences also apply to newer versions of Unix
+.BR pic .
+.SS \*(tx mode
+.LP
+\*(tx mode is enabled by the
+.B \-t
+option.
+In \*(tx mode,
+.B pic
+will define a vbox called
+.B \egraph
+for each picture.
+You must yourself print that vbox using, for example, the command
+.RS
+.LP
+.B
+\ecenterline{\ebox\egraph}
+.RE
+.LP
+Actually, since the vbox has a height of zero this will produce
+slightly more vertical space above the picture than below it;
+.RS
+.LP
+.B
+\ecenterline{\eraise 1em\ebox\egraph}
+.RE
+.LP
+would avoid this.
+.LP
+You must use a \*(tx driver that supports the
+.B tpic
+specials, version 2.
+.LP
+Lines beginning with
+.B \e
+are passed through transparently; a
+.B %
+is added to the end of the line to avoid unwanted spaces.
+You can safely use this feature to change fonts or to
+change the value of
+.BR \ebaselineskip .
+Anything else may well produce undesirable results; use at your own risk.
+Lines beginning with a period are not given any special treatment.
+.SS Commands
+.TP
+\fBfor\fR \fIvariable\fR \fB=\fR \fIexpr1\fR \fBto\fR \fIexpr2\fR \
+[\fBby\fR [\fB*\fR]\fIexpr3\fR] \fBdo\fR \fIX\fR \fIbody\fR \fIX\fR
+Set
+.I variable
+to
+.IR expr1 .
+While the value of
+.I variable
+is less than or equal to
+.IR expr2 ,
+do
+.I body
+and increment
+.I variable
+by
+.IR expr3 ;
+if
+.B by
+is not given, increment
+.I variable
+by 1.
+If
+.I expr3
+is prefixed by
+.B *
+then
+.I variable
+will instead be multiplied by
+.IR expr3 .
+.I X
+can be any character not occurring in
+.IR body .
+.TP
+\fBif\fR \fIexpr\fR \fBthen\fR \fIX\fR \fIif-true\fR \fIX\fR \
+[\fBelse\fR \fIY\fR \fIif-false\fR \fIY\fR]
+Evaluate
+.IR expr ;
+if it is non-zero then do
+.IR if-true ,
+otherwise do
+.IR if-false .
+.I X
+can be any character not occurring in
+.IR if-true .
+.I Y
+can be any character not occurring in
+.IR if-false .
+.TP
+\fBprint\fR \fIarg\fR\|.\|.\|.
+Concatenate the arguments and print as a line on stderr.
+Each
+.I arg
+must be an expression, a position, or text.
+This is useful for debugging.
+.TP
+\fBcommand\fR \fIarg\fR\|.\|.\|.
+Concatenate the arguments
+and pass them through as a line to troff or\*(tx.
+Each
+.I arg
+must be an expression, a position, or text.
+This has a similar effect to a line beginning with
+.B .
+or
+.BR \e ,
+but allows the values of variables to be passed through.
+.TP
+\fBsh\fR \fIX\fR \fIcommand\fR \fIX\fR
+Pass
+.I command
+to a shell.
+.I X
+can be any character not occurring in
+.IR command .
+.TP
+\fBcopy\fR \fB"\fIfilename\fB"\fR
+Include
+.I filename
+at this point in the file.
+.TP
+\fBcopy\fR [\fB"\fIfilename\fB"\fR] \fBthru\fR \fIX\fR \fIbody\fR \fIX\fR \
+[\fBuntil\fR \fB"\fIword\*(ic\fB"\fR]
+.ns
+.TP
+\fBcopy\fR [\fB"\fIfilename\fB"\fR] \fBthru\fR \fImacro\fR \
+[\fBuntil\fR \fB"\fIword\*(ic\fB"\fR]
+This construct does
+.I body
+once for each line of
+.IR filename ;
+the line is split into blank-delimited words,
+and occurrences of
+.BI $ i
+in
+.IR body ,
+for
+.I i
+between 1 and 9,
+are replaced by the
+.IR i -th
+word of the line.
+If
+.I filename
+is not given, lines are taken from the current input up to
+.BR .PE .
+If an
+.B until
+clause is specified,
+lines will be read only until a line the first word of which is
+.IR word ;
+that line will then be discarded.
+.I X
+can be any character not occurring in
+.IR body .
+For example,
+.RS
+.IP
+.ft B
+.nf
+\&.PS
+copy thru % circle at ($1,$2) % until "END"
+1 2
+3 4
+5 6
+END
+box
+\&.PE
+.ft
+.fi
+.RE
+.IP
+is equivalent to
+.RS
+.IP
+.ft B
+.nf
+\&.PS
+circle at (1,2)
+circle at (3,4)
+circle at (5,6)
+box
+\&.PE
+.ft
+.fi
+.RE
+.IP
+The commands to be performed for each line can also be taken
+from a macro defined earlier by giving the name of the macro
+as the argument to
+.BR thru .
+.LP
+.B reset
+.br
+.ns
+.TP
+\fBreset\fI variable1\fB,\fI variable2 .\^.\^.
+Reset pre-defined variables
+.IR variable1 ,
+.I variable2
+\&.\^.\^. to their default values.
+If no arguments are given, reset all pre-defined variables
+to their default values.
+Note that assigning a value to
+.B scale
+also causes all pre-defined variables that control dimensions
+to be reset to their default values times the new value of scale.
+.TP
+\fBplot\fR \fIexpr\fR [\fB"\fItext\*(ic\fB"\fR]
+This is a text object which is constructed by using
+.I text
+as a format string for sprintf
+with an argument of
+.IR expr .
+If
+.I text
+is omitted a format string of
+.B "\(ts%g\(ts"
+is used.
+Attributes can be specified in the same way as for a normal text
+object.
+Be very careful that you specify an appropriate format string;
+.B pic
+does only very limited checking of the string.
+This is deprecated in favour of
+.BR sprintf .
+.TP
+.IB variable := expr
+This is similar to
+.B =
+except
+.I variable
+must already be defined,
+and the value of
+.I variable
+will be changed only in the innermost block in which it is defined.
+(By contrast,
+.B =
+defines the variable in the current block if it is not already defined there,
+and then changes the value in the current block.)
+.LP
+Arguments of the form
+.IP
+.IR X\ anything\ X
+.LP
+are also allowed to be of the form
+.IP
+.BI {\ anything\ }
+.LP
+In this case
+.I anything
+can contain balanced occurrences of
+.B {
+and
+.BR } .
+Strings may contain
+.I X
+or imbalanced occurrences of
+.B {
+and
+.BR } .
+.SS Expressions
+The syntax for expressions has been significantly extended:
+.LP
+.IB x\ ^\ y
+(exponentiation)
+.br
+.BI sin( x )
+.br
+.BI cos( x )
+.br
+.BI atan2( y , \ x )
+.br
+.BI log( x )
+(base 10)
+.br
+.BI exp( x )
+(base 10, ie 10\v'-.4m'\fIx\*(ic\fR\v'.4m')
+.br
+.BI sqrt( x )
+.br
+.BI int( x )
+.br
+.B rand()
+(return a random number between 0 and 1)
+.br
+.BI rand( x )
+(return a random number between 1 and
+.IR x ;
+deprecated)
+.br
+.BI srand( x )
+(set the random number seed)
+.br
+.BI max( e1 , \ e2 )
+.br
+.BI min( e1 , \ e2 )
+.br
+.BI ! e
+.br
+\fIe1\fB && \fIe2\fR
+.br
+\fIe1\fB || \fIe2\fR
+.br
+\fIe1\fB == \fIe2\fR
+.br
+\fIe1\fB != \fIe2\fR
+.br
+\fIe1\fB >= \fIe2\fR
+.br
+\fIe1\fB > \fIe2\fR
+.br
+\fIe1\fB <= \fIe2\fR
+.br
+\fIe1\fB < \fIe2\fR
+.br
+\fB"\fIstr1\*(ic\fB" == "\fIstr2\*(ic\fB"\fR
+.br
+\fB"\fIstr1\*(ic\fB" != "\fIstr2\*(ic\fB"\fR
+.br
+.LP
+String comparison expressions must be parenthesised in some contexts
+to avoid ambiguity.
+.SS Other Changes
+.LP
+A bare expression,
+.IR expr ,
+is acceptable as an attribute;
+it is equivalent to
+.IR dir\ expr ,
+where
+.I dir
+is the current direction.
+For example
+.IP
+.B line 2i
+.LP
+means draw a line 2 inches long in the current direction.
+.LP
+The maximum width and height of the picture are taken from the variables
+.B maxpswid
+and
+.BR maxpsht .
+Initially these have values 8.5 and 11.
+.LP
+Scientific notation is allowed for numbers.
+For example
+.RS
+.B
+x = 5e\-2
+.RE
+.LP
+Text attributes can be compounded.
+For example,
+.RS
+.B
+"foo" above ljust
+.RE
+is legal.
+.LP
+There is no limit to the depth to which blocks can be examined.
+For example,
+.RS
+.B
+[A: [B: [C: box ]]] with .A.B.C.sw at 1,2
+.br
+.B
+circle at last [\^].A.B.C
+.RE
+is acceptable.
+.LP
+Arcs now have compass points
+determined by the circle of which the arc is a part.
+.LP
+Circles and arcs can be dotted or dashed.
+In \*(tx mode splines can be dotted or dashed.
+.LP
+Boxes can have rounded corners.
+The
+.B rad
+attribute specifies the radius of the quarter-circles at each corner.
+If no
+.B rad
+or
+.B diam
+attribute is given, a radius of
+.B boxrad
+is used.
+Initially,
+.B boxrad
+has a value of 0.
+A box with rounded corners can be dotted or dashed.
+.LP
+The
+.B .PS
+line can have a second argument specifying a maximum height for
+the picture.
+If the width of zero is specified the width will be ignored in computing
+the scaling factor for the picture.
+Note that GNU
+.B pic
+will always scale a picture by the same amount vertically as horizontally.
+This is different from the
+.SM DWB
+2.0
+.B pic
+which may scale a picture by a different amount vertically than
+horizontally if a height is specified.
+.LP
+Each text object has an invisible box associated with it.
+The compass points of a text object are determined by this box.
+The implicit motion associated with the object is also determined
+by this box.
+The dimensions of this box are taken from the width and height attributes;
+if the width attribute is not supplied then the width will be taken to be
+.BR textwid ;
+if the height attribute is not supplied then the height will be taken to be
+the number of text strings associated with the object
+times
+.BR textht .
+Initially
+.B textwid
+and
+.B textht
+have a value of 0.
+.LP
+In places where a quoted text string can be used,
+an expression of the form
+.IP
+.BI sprintf(\(ts format \(ts,\ arg ,\fR.\|.\|.\fB)
+.LP
+can also be used;
+this will produce the arguments formatted according to
+.IR format ,
+which should be a string as described in
+.BR printf (3)
+appropriate for the number of arguments supplied,
+using only the
+.BR e ,
+.BR f ,
+.B g
+or
+.B %
+format characters.
+.LP
+The thickness of the lines used to draw objects is controlled by the
+.B linethick
+variable.
+This gives the thickness of lines in points.
+A negative value means use the default thickness:
+in \*(tx output mode, this means use a thickness of 8 milliinches;
+in \*(tx output mode with the
+.B -c
+option, this means use the line thickness specified by
+.B .ps
+lines;
+in troff output mode, this means use a thickness proportional
+to the pointsize.
+A zero value means draw the thinnest possible line supported by
+the output device.
+Initially it has a value of -1.
+There is also a
+.BR thick [ ness ]
+attribute.
+For example,
+.RS
+.LP
+.B circle thickness 1.5
+.RE
+.LP
+would draw a circle using a line with a thickness of 1.5 points.
+The thickness of lines is not affected by the
+value of the
+.B scale
+variable, nor by the width or height given in the
+.B .PS
+line.
+.LP
+Boxes (including boxes with rounded corners),
+circles and ellipses can be filled by giving then an attribute of
+.BR fill [ ed ].
+This takes an optional argument of an expression with a value between
+0 and 1; 0 will fill it with white, 1 with black, values in between
+with a proportionally gray shade.
+A value greater than 1 can also be used:
+this means fill with the
+shade of gray that is currently being used for text and lines.
+Normally this will be black, but output devices may provide
+a mechanism for changing this.
+Without an argument, then the value of the variable
+.B fillval
+will be used.
+Initially this has a value of 0.5.
+The invisible attribute does not affect the filling of objects.
+Any text associated with a filled object will be added after the
+object has been filled, so that the text will not be obscured
+by the filling.
+.LP
+Arrow heads will be drawn as solid triangles if the variable
+.B arrowhead
+is non-zero and either \*(tx mode is enabled or
+the
+.B \-x
+option has been given.
+Initially
+.B arrowhead
+has a value of 1.
+.LP
+The troff output of
+.B pic
+is device-independent.
+The
+.B \-T
+option is therefore redundant.
+All numbers are taken to be in inches; numbers are never interpreted
+to be in troff machine units.
+.LP
+Objects can have an
+.B aligned
+attribute.
+This will only work when the postprocessor is
+.BR grops .
+Any text associated with an object having the
+.B aligned
+attribute will be rotated about the center of the object
+so that it is aligned in the direction from the start point
+to the end point of the object.
+Note that this attribute will have no effect for objects whose start and
+end points are coincident.
+.LP
+In places where
+.IB n th
+is allowed
+.BI ` expr 'th
+is also allowed.
+Note that
+.B 'th
+is a single token: no space is allowed between the
+.B '
+and the
+.BR th .
+For example,
+.IP
+.B
+.nf
+for i = 1 to 4 do {
+ line from `i'th box.nw to `i+1'th box.se
+}
+.fi
+.SH CONVERSION
+To obtain a stand-alone picture from a
+.B pic
+file, enclose your
+.B pic
+code with
+.B .PS
+and
+.B .PE
+requests;
+.B roff
+configuration commands may be added at the beginning of the file, but no
+.B roff
+text.
+.LP
+It is necessary to feed this file into
+.B groff
+without adding any page information, so you must check which
+.B .PS
+and
+.B .PE
+requests are actually called.
+For example, the mm macro package adds a page number, which is very
+annoying.
+At the moment, calling standard
+.B groff
+without any macro package works.
+Alternatively, you can define your own requests, e.g. to do nothing:
+.LP
+.RS
+.nf
+.ft B
+\&.de PS
+\&..
+\&.de PE
+\&..
+.ft
+.fi
+.RE
+.LP
+.B groff
+itself does not provide direct conversion into other graphics file
+formats.
+But there are lots of possibilities if you first transform your picture
+into PostScript\*R format using the
+.B groff
+option
+.BR -Tps .
+Since this
+.IR ps -file
+lacks BoundingBox information it is not very useful by itself, but it
+may be fed into other conversion programs, usually named
+.BI ps2 other
+or
+.BI psto other
+or the like.
+Moreover, the PostScript interpreter
+.B ghostscript
+.RB ( gs )
+has built-in graphics conversion devices that are called with the option
+.LP
+.RS
+.BI "gs -sDEVICE=" <devname>
+.RE
+.LP
+Call
+.RS
+.B gs --help
+.RE
+.LP
+for a list of the available devices.
+.LP
+As the Encapsulated PostScript File Format
+.B EPS
+is getting more and more important, and the conversion wasn't regarded
+trivial in the past you might be interested to know that there is a
+conversion tool named
+.B ps2eps
+which does the right job.
+It is much better than the tool
+.B ps2epsi
+packaged with
+.BR gs .
+.LP
+For bitmapped graphic formats, you should use
+.BR pstopnm ;
+the resulting (intermediate)
+.B PNM
+file can be then converted to virtually any graphics format using the tools
+of the
+.B netpbm
+package .
+.SH FILES
+.Tp \w'\fB@MACRODIR@/pic.tmac'u+3n
+.B
+@MACRODIR@/pic.tmac
+Example definitions of the
+.B PS
+and
+.B PE
+macros.
+.SH "SEE ALSO"
+.BR @g@troff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR tex (1),
+.BR gs (1),
+.BR ps2eps (1),
+.BR pstopnm (1),
+.BR ps2epsi (1),
+.BR pnm (5)
+.LP
+Tpic: Pic for \*(tx
+.LP
+Brian W. Kernighan,
+PIC \(em A Graphics Language for Typesetting (User Manual).
+AT&T Bell Laboratories, Computing Science Technical Report No.\ 116
+<URL:http://cm.bell-labs.com/cm/cs/cstr/116.ps.gz>
+(revised May, 1991).
+.LP
+.B ps2eps
+is available from CTAN mirrors, e.g.
+.br
+<ftp://ftp.dante.de/tex-archive/support/ps2eps/>
+.LP
+W. Richard Stevens - Turning PIC Into HTML
+.br
+<http://www.kohala.com/start/troff/pic2html.html>
+.LP
+W. Richard Stevens - Examples of picMacros
+.br
+<http://www.kohala.com/start/troff/pic.examples.ps>
+.SH BUGS
+.LP
+Input characters that are illegal for
+.B groff
+(ie those with
+.SM ASCII
+code 0 or between 013 and 037 octal or between 0200 and 0237 octal)
+are rejected even in \*(tx mode.
+.LP
+The interpretation of
+.B fillval
+is incompatible with the pic in 10th edition Unix,
+which interprets 0 as black and 1 as white.
+.LP
+PostScript\*R is a registered trademark of Adobe Systems Incorporation.
diff --git a/contrib/groff/src/preproc/pic/pic.y b/contrib/groff/src/preproc/pic/pic.y
new file mode 100644
index 000000000000..38b960a6615e
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/pic.y
@@ -0,0 +1,1812 @@
+/* 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. */
+%{
+#include "pic.h"
+#include "ptable.h"
+#include "object.h"
+
+extern int delim_flag;
+extern void do_copy(const char *);
+extern void copy_rest_thru(const char *, const char *);
+extern void copy_file_thru(const char *, const char *, const char *);
+extern void push_body(const char *);
+extern void do_for(char *var, double from, double to,
+ int by_is_multiplicative, double by, char *body);
+extern void do_lookahead();
+
+#ifndef HAVE_FMOD
+extern "C" {
+ double fmod(double, double);
+}
+#endif
+
+#undef rand
+#undef srand
+extern "C" {
+ int rand();
+#ifdef RET_TYPE_SRAND_IS_VOID
+ void srand(unsigned int);
+#else
+ int srand(unsigned int);
+#endif
+}
+
+/* Maximum number of characters produced by printf("%g") */
+#define GDIGITS 14
+
+int yylex();
+void yyerror(const char *);
+
+void reset(const char *nm);
+void reset_all();
+
+place *lookup_label(const char *);
+void define_label(const char *label, const place *pl);
+
+direction current_direction;
+position current_position;
+
+implement_ptable(place)
+
+PTABLE(place) top_table;
+
+PTABLE(place) *current_table = &top_table;
+saved_state *current_saved_state = 0;
+
+object_list olist;
+
+const char *ordinal_postfix(int n);
+const char *object_type_name(object_type type);
+char *format_number(const char *form, double n);
+char *do_sprintf(const char *form, const double *v, int nv);
+
+%}
+
+
+%union {
+ char *str;
+ int n;
+ double x;
+ struct { double x, y; } pair;
+ struct { double x; char *body; } if_data;
+ struct { char *str; const char *filename; int lineno; } lstr;
+ struct { double *v; int nv; int maxv; } dv;
+ struct { double val; int is_multiplicative; } by;
+ place pl;
+ object *obj;
+ corner crn;
+ path *pth;
+ object_spec *spec;
+ saved_state *pstate;
+ graphics_state state;
+ object_type obtype;
+}
+
+%token <str> LABEL
+%token <str> VARIABLE
+%token <x> NUMBER
+%token <lstr> TEXT
+%token <lstr> COMMAND_LINE
+%token <str> DELIMITED
+%token <n> ORDINAL
+%token TH
+%token LEFT_ARROW_HEAD
+%token RIGHT_ARROW_HEAD
+%token DOUBLE_ARROW_HEAD
+%token LAST
+%token UP
+%token DOWN
+%token LEFT
+%token RIGHT
+%token BOX
+%token CIRCLE
+%token ELLIPSE
+%token ARC
+%token LINE
+%token ARROW
+%token MOVE
+%token SPLINE
+%token HEIGHT
+%token RADIUS
+%token WIDTH
+%token DIAMETER
+%token UP
+%token DOWN
+%token RIGHT
+%token LEFT
+%token FROM
+%token TO
+%token AT
+%token WITH
+%token BY
+%token THEN
+%token SOLID
+%token DOTTED
+%token DASHED
+%token CHOP
+%token SAME
+%token INVISIBLE
+%token LJUST
+%token RJUST
+%token ABOVE
+%token BELOW
+%token OF
+%token THE
+%token WAY
+%token BETWEEN
+%token AND
+%token HERE
+%token DOT_N
+%token DOT_E
+%token DOT_W
+%token DOT_S
+%token DOT_NE
+%token DOT_SE
+%token DOT_NW
+%token DOT_SW
+%token DOT_C
+%token DOT_START
+%token DOT_END
+%token DOT_X
+%token DOT_Y
+%token DOT_HT
+%token DOT_WID
+%token DOT_RAD
+%token SIN
+%token COS
+%token ATAN2
+%token LOG
+%token EXP
+%token SQRT
+%token K_MAX
+%token K_MIN
+%token INT
+%token RAND
+%token SRAND
+%token COPY
+%token THRU
+%token TOP
+%token BOTTOM
+%token UPPER
+%token LOWER
+%token SH
+%token PRINT
+%token CW
+%token CCW
+%token FOR
+%token DO
+%token IF
+%token ELSE
+%token ANDAND
+%token OROR
+%token NOTEQUAL
+%token EQUALEQUAL
+%token LESSEQUAL
+%token GREATEREQUAL
+%token LEFT_CORNER
+%token RIGHT_CORNER
+%token CENTER
+%token END
+%token START
+%token RESET
+%token UNTIL
+%token PLOT
+%token THICKNESS
+%token FILL
+%token ALIGNED
+%token SPRINTF
+%token COMMAND
+
+%token DEFINE
+%token UNDEF
+
+/* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
+%left PLOT
+%left TEXT SPRINTF
+
+/* give text adjustments higher precedence than TEXT, so that
+box "foo" above ljust == box ("foo" above ljust)
+*/
+
+%left LJUST RJUST ABOVE BELOW
+
+%left LEFT RIGHT
+/* Give attributes that take an optional expression a higher
+precedence than left and right, so that eg `line chop left'
+parses properly. */
+%left CHOP SOLID DASHED DOTTED UP DOWN FILL
+%left LABEL
+
+%left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
+%left ORDINAL HERE '`'
+
+/* these need to be lower than '-' */
+%left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
+
+/* these must have higher precedence than CHOP so that `label %prec CHOP'
+works */
+%left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
+%left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
+%left UPPER LOWER CENTER START END
+
+%left ','
+%left OROR
+%left ANDAND
+%left EQUALEQUAL NOTEQUAL
+%left '<' '>' LESSEQUAL GREATEREQUAL
+
+%left BETWEEN OF
+%left AND
+
+%left '+' '-'
+%left '*' '/' '%'
+%right '!'
+%right '^'
+
+%type <x> expr any_expr text_expr
+%type <by> optional_by
+%type <pair> expr_pair position_not_place
+%type <if_data> simple_if
+%type <obj> nth_primitive
+%type <crn> corner
+%type <pth> path label_path relative_path
+%type <pl> place label element element_list middle_element_list
+%type <spec> object_spec
+%type <pair> position
+%type <obtype> object_type
+%type <n> optional_ordinal_last ordinal
+%type <str> until
+%type <dv> sprintf_args
+%type <lstr> text print_args print_arg
+
+%%
+
+top:
+ optional_separator
+ | element_list
+ {
+ if (olist.head)
+ print_picture(olist.head);
+ }
+ ;
+
+
+element_list:
+ optional_separator middle_element_list optional_separator
+ { $$ = $2; }
+ ;
+
+middle_element_list:
+ element
+ { $$ = $1; }
+ | middle_element_list separator element
+ { $$ = $1; }
+ ;
+
+optional_separator:
+ /* empty */
+ | separator
+ ;
+
+separator:
+ ';'
+ | separator ';'
+ ;
+
+placeless_element:
+ VARIABLE '=' any_expr
+ {
+ define_variable($1, $3);
+ a_delete $1;
+ }
+ | VARIABLE ':' '=' any_expr
+ {
+ place *p = lookup_label($1);
+ if (!p) {
+ lex_error("variable `%1' not defined", $1);
+ YYABORT;
+ }
+ p->obj = 0;
+ p->x = $4;
+ p->y = 0.0;
+ a_delete $1;
+ }
+ | UP
+ { current_direction = UP_DIRECTION; }
+ | DOWN
+ { current_direction = DOWN_DIRECTION; }
+ | LEFT
+ { current_direction = LEFT_DIRECTION; }
+ | RIGHT
+ { current_direction = RIGHT_DIRECTION; }
+ | COMMAND_LINE
+ {
+ olist.append(make_command_object($1.str, $1.filename,
+ $1.lineno));
+ }
+ | COMMAND print_args
+ {
+ olist.append(make_command_object($2.str, $2.filename,
+ $2.lineno));
+ }
+ | PRINT print_args
+ {
+ fprintf(stderr, "%s\n", $2.str);
+ a_delete $2.str;
+ fflush(stderr);
+ }
+ | SH
+ { delim_flag = 1; }
+ DELIMITED
+ {
+ delim_flag = 0;
+ if (safer_flag)
+ lex_error("unsafe to run command `%1'", $3);
+ else
+ system($3);
+ a_delete $3;
+ }
+ | COPY TEXT
+ {
+ if (yychar < 0)
+ do_lookahead();
+ do_copy($2.str);
+ // do not delete the filename
+ }
+ | COPY TEXT THRU
+ { delim_flag = 2; }
+ DELIMITED
+ { delim_flag = 0; }
+ until
+ {
+ if (yychar < 0)
+ do_lookahead();
+ copy_file_thru($2.str, $5, $7);
+ // do not delete the filename
+ a_delete $5;
+ a_delete $7;
+ }
+ | COPY THRU
+ { delim_flag = 2; }
+ DELIMITED
+ { delim_flag = 0; }
+ until
+ {
+ if (yychar < 0)
+ do_lookahead();
+ copy_rest_thru($4, $6);
+ a_delete $4;
+ a_delete $6;
+ }
+ | FOR VARIABLE '=' expr TO expr optional_by DO
+ { delim_flag = 1; }
+ DELIMITED
+ {
+ delim_flag = 0;
+ if (yychar < 0)
+ do_lookahead();
+ do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10);
+ }
+ | simple_if
+ {
+ if (yychar < 0)
+ do_lookahead();
+ if ($1.x != 0.0)
+ push_body($1.body);
+ a_delete $1.body;
+ }
+ | simple_if ELSE
+ { delim_flag = 1; }
+ DELIMITED
+ {
+ delim_flag = 0;
+ if (yychar < 0)
+ do_lookahead();
+ if ($1.x != 0.0)
+ push_body($1.body);
+ else
+ push_body($4);
+ a_delete $1.body;
+ a_delete $4;
+ }
+ | reset_variables
+ | RESET
+ { define_variable("scale", 1.0); }
+ ;
+
+reset_variables:
+ RESET VARIABLE
+ { reset($2); a_delete $2; }
+ | reset_variables VARIABLE
+ { reset($2); a_delete $2; }
+ | reset_variables ',' VARIABLE
+ { reset($3); a_delete $3; }
+ ;
+
+print_args:
+ print_arg
+ { $$ = $1; }
+ | print_args print_arg
+ {
+ $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
+ strcpy($$.str, $1.str);
+ strcat($$.str, $2.str);
+ a_delete $1.str;
+ a_delete $2.str;
+ if ($1.filename) {
+ $$.filename = $1.filename;
+ $$.lineno = $1.lineno;
+ }
+ else if ($2.filename) {
+ $$.filename = $2.filename;
+ $$.lineno = $2.lineno;
+ }
+ }
+ ;
+
+print_arg:
+ expr %prec ','
+ {
+ $$.str = new char[GDIGITS + 1];
+ sprintf($$.str, "%g", $1);
+ $$.filename = 0;
+ $$.lineno = 0;
+ }
+ | text
+ { $$ = $1; }
+ | position %prec ','
+ {
+ $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
+ sprintf($$.str, "%g, %g", $1.x, $1.y);
+ $$.filename = 0;
+ $$.lineno = 0;
+ }
+
+simple_if:
+ IF any_expr THEN
+ { delim_flag = 1; }
+ DELIMITED
+ { delim_flag = 0; $$.x = $2; $$.body = $5; }
+ ;
+
+until:
+ /* empty */
+ { $$ = 0; }
+ | UNTIL TEXT
+ { $$ = $2.str; }
+ ;
+
+any_expr:
+ expr
+ { $$ = $1; }
+ | text_expr
+ { $$ = $1; }
+ ;
+
+text_expr:
+ text EQUALEQUAL text
+ {
+ $$ = strcmp($1.str, $3.str) == 0;
+ a_delete $1.str;
+ a_delete $3.str;
+ }
+ | text NOTEQUAL text
+ {
+ $$ = strcmp($1.str, $3.str) != 0;
+ a_delete $1.str;
+ a_delete $3.str;
+ }
+ | text_expr ANDAND text_expr
+ { $$ = ($1 != 0.0 && $3 != 0.0); }
+ | text_expr ANDAND expr
+ { $$ = ($1 != 0.0 && $3 != 0.0); }
+ | expr ANDAND text_expr
+ { $$ = ($1 != 0.0 && $3 != 0.0); }
+ | text_expr OROR text_expr
+ { $$ = ($1 != 0.0 || $3 != 0.0); }
+ | text_expr OROR expr
+ { $$ = ($1 != 0.0 || $3 != 0.0); }
+ | expr OROR text_expr
+ { $$ = ($1 != 0.0 || $3 != 0.0); }
+ | '!' text_expr
+ { $$ = ($2 == 0.0); }
+ ;
+
+
+optional_by:
+ /* empty */
+ { $$.val = 1.0; $$.is_multiplicative = 0; }
+ | BY expr
+ { $$.val = $2; $$.is_multiplicative = 0; }
+ | BY '*' expr
+ { $$.val = $3; $$.is_multiplicative = 1; }
+ ;
+
+element:
+ object_spec
+ {
+ $$.obj = $1->make_object(&current_position,
+ &current_direction);
+ if ($$.obj == 0)
+ YYABORT;
+ delete $1;
+ if ($$.obj)
+ olist.append($$.obj);
+ else {
+ $$.x = current_position.x;
+ $$.y = current_position.y;
+ }
+ }
+ | LABEL ':' optional_separator element
+ { $$ = $4; define_label($1, & $$); a_delete $1; }
+ | LABEL ':' optional_separator position_not_place
+ {
+ $$.obj = 0;
+ $$.x = $4.x;
+ $$.y = $4.y;
+ define_label($1, & $$);
+ a_delete $1;
+ }
+ | LABEL ':' optional_separator place
+ {
+ $$ = $4;
+ define_label($1, & $$);
+ a_delete $1;
+ }
+ | '{'
+ {
+ $<state>$.x = current_position.x;
+ $<state>$.y = current_position.y;
+ $<state>$.dir = current_direction;
+ }
+ element_list '}'
+ {
+ current_position.x = $<state>2.x;
+ current_position.y = $<state>2.y;
+ current_direction = $<state>2.dir;
+ }
+ optional_element
+ {
+ $$ = $3;
+ }
+ | placeless_element
+ {
+ $$.obj = 0;
+ $$.x = current_position.x;
+ $$.y = current_position.y;
+ }
+ ;
+
+optional_element:
+ /* empty */
+ {}
+ | element
+ {}
+ ;
+
+object_spec:
+ BOX
+ {
+ $$ = new object_spec(BOX_OBJECT);
+ }
+ | CIRCLE
+ {
+ $$ = new object_spec(CIRCLE_OBJECT);
+ }
+ | ELLIPSE
+ {
+ $$ = new object_spec(ELLIPSE_OBJECT);
+ }
+ | ARC
+ {
+ $$ = new object_spec(ARC_OBJECT);
+ $$->dir = current_direction;
+ }
+ | LINE
+ {
+ $$ = new object_spec(LINE_OBJECT);
+ lookup_variable("lineht", & $$->segment_height);
+ lookup_variable("linewid", & $$->segment_width);
+ $$->dir = current_direction;
+ }
+ | ARROW
+ {
+ $$ = new object_spec(ARROW_OBJECT);
+ lookup_variable("lineht", & $$->segment_height);
+ lookup_variable("linewid", & $$->segment_width);
+ $$->dir = current_direction;
+ }
+ | MOVE
+ {
+ $$ = new object_spec(MOVE_OBJECT);
+ lookup_variable("moveht", & $$->segment_height);
+ lookup_variable("movewid", & $$->segment_width);
+ $$->dir = current_direction;
+ }
+ | SPLINE
+ {
+ $$ = new object_spec(SPLINE_OBJECT);
+ lookup_variable("lineht", & $$->segment_height);
+ lookup_variable("linewid", & $$->segment_width);
+ $$->dir = current_direction;
+ }
+ | text %prec TEXT
+ {
+ $$ = new object_spec(TEXT_OBJECT);
+ $$->text = new text_item($1.str, $1.filename, $1.lineno);
+ }
+ | PLOT expr
+ {
+ $$ = new object_spec(TEXT_OBJECT);
+ $$->text = new text_item(format_number(0, $2), 0, -1);
+ }
+ | PLOT expr text
+ {
+ $$ = new object_spec(TEXT_OBJECT);
+ $$->text = new text_item(format_number($3.str, $2),
+ $3.filename, $3.lineno);
+ a_delete $3.str;
+ }
+ | '['
+ {
+ saved_state *p = new saved_state;
+ $<pstate>$ = p;
+ p->x = current_position.x;
+ p->y = current_position.y;
+ p->dir = current_direction;
+ p->tbl = current_table;
+ p->prev = current_saved_state;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+ current_table = new PTABLE(place);
+ current_saved_state = p;
+ olist.append(make_mark_object());
+ }
+ element_list ']'
+ {
+ current_position.x = $<pstate>2->x;
+ current_position.y = $<pstate>2->y;
+ current_direction = $<pstate>2->dir;
+ $$ = new object_spec(BLOCK_OBJECT);
+ olist.wrap_up_block(& $$->oblist);
+ $$->tbl = current_table;
+ current_table = $<pstate>2->tbl;
+ current_saved_state = $<pstate>2->prev;
+ delete $<pstate>2;
+ }
+ | object_spec HEIGHT expr
+ {
+ $$ = $1;
+ $$->height = $3;
+ $$->flags |= HAS_HEIGHT;
+ }
+ | object_spec RADIUS expr
+ {
+ $$ = $1;
+ $$->radius = $3;
+ $$->flags |= HAS_RADIUS;
+ }
+ | object_spec WIDTH expr
+ {
+ $$ = $1;
+ $$->width = $3;
+ $$->flags |= HAS_WIDTH;
+ }
+ | object_spec DIAMETER expr
+ {
+ $$ = $1;
+ $$->radius = $3/2.0;
+ $$->flags |= HAS_RADIUS;
+ }
+ | object_spec expr %prec HEIGHT
+ {
+ $$ = $1;
+ $$->flags |= HAS_SEGMENT;
+ switch ($$->dir) {
+ case UP_DIRECTION:
+ $$->segment_pos.y += $2;
+ break;
+ case DOWN_DIRECTION:
+ $$->segment_pos.y -= $2;
+ break;
+ case RIGHT_DIRECTION:
+ $$->segment_pos.x += $2;
+ break;
+ case LEFT_DIRECTION:
+ $$->segment_pos.x -= $2;
+ break;
+ }
+ }
+ | object_spec UP
+ {
+ $$ = $1;
+ $$->dir = UP_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.y += $$->segment_height;
+ }
+ | object_spec UP expr
+ {
+ $$ = $1;
+ $$->dir = UP_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.y += $3;
+ }
+ | object_spec DOWN
+ {
+ $$ = $1;
+ $$->dir = DOWN_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.y -= $$->segment_height;
+ }
+ | object_spec DOWN expr
+ {
+ $$ = $1;
+ $$->dir = DOWN_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.y -= $3;
+ }
+ | object_spec RIGHT
+ {
+ $$ = $1;
+ $$->dir = RIGHT_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x += $$->segment_width;
+ }
+ | object_spec RIGHT expr
+ {
+ $$ = $1;
+ $$->dir = RIGHT_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x += $3;
+ }
+ | object_spec LEFT
+ {
+ $$ = $1;
+ $$->dir = LEFT_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x -= $$->segment_width;
+ }
+ | object_spec LEFT expr
+ {
+ $$ = $1;
+ $$->dir = LEFT_DIRECTION;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x -= $3;
+ }
+ | object_spec FROM position
+ {
+ $$ = $1;
+ $$->flags |= HAS_FROM;
+ $$->from.x = $3.x;
+ $$->from.y = $3.y;
+ }
+ | object_spec TO position
+ {
+ $$ = $1;
+ if ($$->flags & HAS_SEGMENT)
+ $$->segment_list = new segment($$->segment_pos,
+ $$->segment_is_absolute,
+ $$->segment_list);
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x = $3.x;
+ $$->segment_pos.y = $3.y;
+ $$->segment_is_absolute = 1;
+ $$->flags |= HAS_TO;
+ $$->to.x = $3.x;
+ $$->to.y = $3.y;
+ }
+ | object_spec AT position
+ {
+ $$ = $1;
+ $$->flags |= HAS_AT;
+ $$->at.x = $3.x;
+ $$->at.y = $3.y;
+ if ($$->type != ARC_OBJECT) {
+ $$->flags |= HAS_FROM;
+ $$->from.x = $3.x;
+ $$->from.y = $3.y;
+ }
+ }
+ | object_spec WITH path
+ {
+ $$ = $1;
+ $$->flags |= HAS_WITH;
+ $$->with = $3;
+ }
+ | object_spec BY expr_pair
+ {
+ $$ = $1;
+ $$->flags |= HAS_SEGMENT;
+ $$->segment_pos.x += $3.x;
+ $$->segment_pos.y += $3.y;
+ }
+ | object_spec THEN
+ {
+ $$ = $1;
+ if ($$->flags & HAS_SEGMENT) {
+ $$->segment_list = new segment($$->segment_pos,
+ $$->segment_is_absolute,
+ $$->segment_list);
+ $$->flags &= ~HAS_SEGMENT;
+ $$->segment_pos.x = $$->segment_pos.y = 0.0;
+ $$->segment_is_absolute = 0;
+ }
+ }
+ | object_spec SOLID
+ {
+ $$ = $1; // nothing
+ }
+ | object_spec DOTTED
+ {
+ $$ = $1;
+ $$->flags |= IS_DOTTED;
+ lookup_variable("dashwid", & $$->dash_width);
+ }
+ | object_spec DOTTED expr
+ {
+ $$ = $1;
+ $$->flags |= IS_DOTTED;
+ $$->dash_width = $3;
+ }
+ | object_spec DASHED
+ {
+ $$ = $1;
+ $$->flags |= IS_DASHED;
+ lookup_variable("dashwid", & $$->dash_width);
+ }
+ | object_spec DASHED expr
+ {
+ $$ = $1;
+ $$->flags |= IS_DASHED;
+ $$->dash_width = $3;
+ }
+ | object_spec FILL
+ {
+ $$ = $1;
+ $$->flags |= IS_DEFAULT_FILLED;
+ }
+ | object_spec FILL expr
+ {
+ $$ = $1;
+ $$->flags |= IS_FILLED;
+ $$->fill = $3;
+ }
+ | object_spec CHOP
+ {
+ $$ = $1;
+ // line chop chop means line chop 0 chop 0
+ if ($$->flags & IS_DEFAULT_CHOPPED) {
+ $$->flags |= IS_CHOPPED;
+ $$->flags &= ~IS_DEFAULT_CHOPPED;
+ $$->start_chop = $$->end_chop = 0.0;
+ }
+ else if ($$->flags & IS_CHOPPED) {
+ $$->end_chop = 0.0;
+ }
+ else {
+ $$->flags |= IS_DEFAULT_CHOPPED;
+ }
+ }
+ | object_spec CHOP expr
+ {
+ $$ = $1;
+ if ($$->flags & IS_DEFAULT_CHOPPED) {
+ $$->flags |= IS_CHOPPED;
+ $$->flags &= ~IS_DEFAULT_CHOPPED;
+ $$->start_chop = 0.0;
+ $$->end_chop = $3;
+ }
+ else if ($$->flags & IS_CHOPPED) {
+ $$->end_chop = $3;
+ }
+ else {
+ $$->start_chop = $$->end_chop = $3;
+ $$->flags |= IS_CHOPPED;
+ }
+ }
+ | object_spec SAME
+ {
+ $$ = $1;
+ $$->flags |= IS_SAME;
+ }
+ | object_spec INVISIBLE
+ {
+ $$ = $1;
+ $$->flags |= IS_INVISIBLE;
+ }
+ | object_spec LEFT_ARROW_HEAD
+ {
+ $$ = $1;
+ $$->flags |= HAS_LEFT_ARROW_HEAD;
+ }
+ | object_spec RIGHT_ARROW_HEAD
+ {
+ $$ = $1;
+ $$->flags |= HAS_RIGHT_ARROW_HEAD;
+ }
+ | object_spec DOUBLE_ARROW_HEAD
+ {
+ $$ = $1;
+ $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
+ }
+ | object_spec CW
+ {
+ $$ = $1;
+ $$->flags |= IS_CLOCKWISE;
+ }
+ | object_spec CCW
+ {
+ $$ = $1;
+ $$->flags &= ~IS_CLOCKWISE;
+ }
+ | object_spec text %prec TEXT
+ {
+ $$ = $1;
+ text_item **p;
+ for (p = & $$->text; *p; p = &(*p)->next)
+ ;
+ *p = new text_item($2.str, $2.filename, $2.lineno);
+ }
+ | object_spec LJUST
+ {
+ $$ = $1;
+ if ($$->text) {
+ text_item *p;
+ for (p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.h = LEFT_ADJUST;
+ }
+ }
+ | object_spec RJUST
+ {
+ $$ = $1;
+ if ($$->text) {
+ text_item *p;
+ for (p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.h = RIGHT_ADJUST;
+ }
+ }
+ | object_spec ABOVE
+ {
+ $$ = $1;
+ if ($$->text) {
+ text_item *p;
+ for (p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.v = ABOVE_ADJUST;
+ }
+ }
+ | object_spec BELOW
+ {
+ $$ = $1;
+ if ($$->text) {
+ text_item *p;
+ for (p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.v = BELOW_ADJUST;
+ }
+ }
+ | object_spec THICKNESS expr
+ {
+ $$ = $1;
+ $$->flags |= HAS_THICKNESS;
+ $$->thickness = $3;
+ }
+ | object_spec ALIGNED
+ {
+ $$ = $1;
+ $$->flags |= IS_ALIGNED;
+ }
+ ;
+
+text:
+ TEXT
+ {
+ $$ = $1;
+ }
+ | SPRINTF '(' TEXT sprintf_args ')'
+ {
+ $$.filename = $3.filename;
+ $$.lineno = $3.lineno;
+ $$.str = do_sprintf($3.str, $4.v, $4.nv);
+ a_delete $4.v;
+ a_delete $3.str;
+ }
+ ;
+
+sprintf_args:
+ /* empty */
+ {
+ $$.v = 0;
+ $$.nv = 0;
+ $$.maxv = 0;
+ }
+ | sprintf_args ',' expr
+ {
+ $$ = $1;
+ if ($$.nv >= $$.maxv) {
+ if ($$.nv == 0) {
+ $$.v = new double[4];
+ $$.maxv = 4;
+ }
+ else {
+ double *oldv = $$.v;
+ $$.maxv *= 2;
+ $$.v = new double[$$.maxv];
+ memcpy($$.v, oldv, $$.nv*sizeof(double));
+ a_delete oldv;
+ }
+ }
+ $$.v[$$.nv] = $3;
+ $$.nv += 1;
+ }
+ ;
+
+position:
+ position_not_place
+ { $$ = $1; }
+ | place
+ {
+ position pos = $1;
+ $$.x = pos.x;
+ $$.y = pos.y;
+ }
+ ;
+
+position_not_place:
+ expr_pair
+ { $$ = $1; }
+ | position '+' expr_pair
+ {
+ $$.x = $1.x + $3.x;
+ $$.y = $1.y + $3.y;
+ }
+ | position '-' expr_pair
+ {
+ $$.x = $1.x - $3.x;
+ $$.y = $1.y - $3.y;
+ }
+ | '(' position ',' position ')'
+ {
+ $$.x = $2.x;
+ $$.y = $4.y;
+ }
+ | expr between position AND position
+ {
+ $$.x = (1.0 - $1)*$3.x + $1*$5.x;
+ $$.y = (1.0 - $1)*$3.y + $1*$5.y;
+ }
+ | expr '<' position ',' position '>'
+ {
+ $$.x = (1.0 - $1)*$3.x + $1*$5.x;
+ $$.y = (1.0 - $1)*$3.y + $1*$5.y;
+ }
+ ;
+
+between:
+ BETWEEN
+ | OF THE WAY BETWEEN
+ ;
+
+expr_pair:
+ expr ',' expr
+ { $$.x = $1; $$.y = $3; }
+ | '(' expr_pair ')'
+ { $$ = $2; }
+ ;
+
+place:
+ label %prec CHOP /* line at A left == line (at A) left */
+ { $$ = $1; }
+ | label corner
+ {
+ path pth($2);
+ if (!pth.follow($1, & $$))
+ YYABORT;
+ }
+ | corner label
+ {
+ path pth($1);
+ if (!pth.follow($2, & $$))
+ YYABORT;
+ }
+ | corner OF label
+ {
+ path pth($1);
+ if (!pth.follow($3, & $$))
+ YYABORT;
+ }
+ | HERE
+ {
+ $$.x = current_position.x;
+ $$.y = current_position.y;
+ $$.obj = 0;
+ }
+ ;
+
+label:
+ LABEL
+ {
+ place *p = lookup_label($1);
+ if (!p) {
+ lex_error("there is no place `%1'", $1);
+ YYABORT;
+ }
+ $$ = *p;
+ a_delete $1;
+ }
+ | nth_primitive
+ {
+ $$.obj = $1;
+ }
+ | label '.' LABEL
+ {
+ path pth($3);
+ if (!pth.follow($1, & $$))
+ YYABORT;
+ }
+ ;
+
+ordinal:
+ ORDINAL
+ { $$ = $1; }
+ | '`' any_expr TH
+ {
+ // XXX Check for overflow (and non-integers?).
+ $$ = (int)$2;
+ }
+ ;
+
+optional_ordinal_last:
+ LAST
+ { $$ = 1; }
+ | ordinal LAST
+ { $$ = $1; }
+ ;
+
+nth_primitive:
+ ordinal object_type
+ {
+ int count = 0;
+ object *p;
+ for (p = olist.head; p != 0; p = p->next)
+ if (p->type() == $2 && ++count == $1) {
+ $$ = p;
+ break;
+ }
+ if (p == 0) {
+ lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
+ object_type_name($2));
+ YYABORT;
+ }
+ }
+ | optional_ordinal_last object_type
+ {
+ int count = 0;
+ object *p;
+ for (p = olist.tail; p != 0; p = p->prev)
+ if (p->type() == $2 && ++count == $1) {
+ $$ = p;
+ break;
+ }
+ if (p == 0) {
+ lex_error("there is no %1%2 last %3", $1,
+ ordinal_postfix($1), object_type_name($2));
+ YYABORT;
+ }
+ }
+ ;
+
+object_type:
+ BOX
+ { $$ = BOX_OBJECT; }
+ | CIRCLE
+ { $$ = CIRCLE_OBJECT; }
+ | ELLIPSE
+ { $$ = ELLIPSE_OBJECT; }
+ | ARC
+ { $$ = ARC_OBJECT; }
+ | LINE
+ { $$ = LINE_OBJECT; }
+ | ARROW
+ { $$ = ARROW_OBJECT; }
+ | SPLINE
+ { $$ = SPLINE_OBJECT; }
+ | '[' ']'
+ { $$ = BLOCK_OBJECT; }
+ | TEXT
+ { $$ = TEXT_OBJECT; }
+ ;
+
+label_path:
+ '.' LABEL
+ {
+ $$ = new path($2);
+ }
+ | label_path '.' LABEL
+ {
+ $$ = $1;
+ $$->append($3);
+ }
+ ;
+
+relative_path:
+ corner
+ {
+ $$ = new path($1);
+ }
+ /* give this a lower precedence than LEFT and RIGHT so that
+ [A: box] with .A left == [A: box] with (.A left) */
+
+ | label_path %prec TEXT
+ {
+ $$ = $1;
+ }
+ | label_path corner
+ {
+ $$ = $1;
+ $$->append($2);
+ }
+ ;
+
+path:
+ relative_path
+ {
+ $$ = $1;
+ }
+ | '(' relative_path ',' relative_path ')'
+ {
+ $$ = $2;
+ $$->set_ypath($4);
+ }
+ /* The rest of these rules are a compatibility sop. */
+ | ORDINAL LAST object_type relative_path
+ {
+ lex_warning("`%1%2 last %3' in `with' argument ignored",
+ $1, ordinal_postfix($1), object_type_name($3));
+ $$ = $4;
+ }
+ | LAST object_type relative_path
+ {
+ lex_warning("`last %1' in `with' argument ignored",
+ object_type_name($2));
+ $$ = $3;
+ }
+ | ORDINAL object_type relative_path
+ {
+ lex_warning("`%1%2 %3' in `with' argument ignored",
+ $1, ordinal_postfix($1), object_type_name($2));
+ $$ = $3;
+ }
+ | LABEL relative_path
+ {
+ lex_warning("initial `%1' in `with' argument ignored", $1);
+ a_delete $1;
+ $$ = $2;
+ }
+ ;
+
+corner:
+ DOT_N
+ { $$ = &object::north; }
+ | DOT_E
+ { $$ = &object::east; }
+ | DOT_W
+ { $$ = &object::west; }
+ | DOT_S
+ { $$ = &object::south; }
+ | DOT_NE
+ { $$ = &object::north_east; }
+ | DOT_SE
+ { $$ = &object:: south_east; }
+ | DOT_NW
+ { $$ = &object::north_west; }
+ | DOT_SW
+ { $$ = &object::south_west; }
+ | DOT_C
+ { $$ = &object::center; }
+ | DOT_START
+ { $$ = &object::start; }
+ | DOT_END
+ { $$ = &object::end; }
+ | TOP
+ { $$ = &object::north; }
+ | BOTTOM
+ { $$ = &object::south; }
+ | LEFT
+ { $$ = &object::west; }
+ | RIGHT
+ { $$ = &object::east; }
+ | UPPER LEFT
+ { $$ = &object::north_west; }
+ | LOWER LEFT
+ { $$ = &object::south_west; }
+ | UPPER RIGHT
+ { $$ = &object::north_east; }
+ | LOWER RIGHT
+ { $$ = &object::south_east; }
+ | LEFT_CORNER
+ { $$ = &object::west; }
+ | RIGHT_CORNER
+ { $$ = &object::east; }
+ | UPPER LEFT_CORNER
+ { $$ = &object::north_west; }
+ | LOWER LEFT_CORNER
+ { $$ = &object::south_west; }
+ | UPPER RIGHT_CORNER
+ { $$ = &object::north_east; }
+ | LOWER RIGHT_CORNER
+ { $$ = &object::south_east; }
+ | CENTER
+ { $$ = &object::center; }
+ | START
+ { $$ = &object::start; }
+ | END
+ { $$ = &object::end; }
+ ;
+
+expr:
+ VARIABLE
+ {
+ if (!lookup_variable($1, & $$)) {
+ lex_error("there is no variable `%1'", $1);
+ YYABORT;
+ }
+ a_delete $1;
+ }
+ | NUMBER
+ { $$ = $1; }
+ | place DOT_X
+ {
+ if ($1.obj != 0)
+ $$ = $1.obj->origin().x;
+ else
+ $$ = $1.x;
+ }
+ | place DOT_Y
+ {
+ if ($1.obj != 0)
+ $$ = $1.obj->origin().y;
+ else
+ $$ = $1.y;
+ }
+ | place DOT_HT
+ {
+ if ($1.obj != 0)
+ $$ = $1.obj->height();
+ else
+ $$ = 0.0;
+ }
+ | place DOT_WID
+ {
+ if ($1.obj != 0)
+ $$ = $1.obj->width();
+ else
+ $$ = 0.0;
+ }
+ | place DOT_RAD
+ {
+ if ($1.obj != 0)
+ $$ = $1.obj->radius();
+ else
+ $$ = 0.0;
+ }
+ | expr '+' expr
+ { $$ = $1 + $3; }
+ | expr '-' expr
+ { $$ = $1 - $3; }
+ | expr '*' expr
+ { $$ = $1 * $3; }
+ | expr '/' expr
+ {
+ if ($3 == 0.0) {
+ lex_error("division by zero");
+ YYABORT;
+ }
+ $$ = $1/$3;
+ }
+ | expr '%' expr
+ {
+ if ($3 == 0.0) {
+ lex_error("modulus by zero");
+ YYABORT;
+ }
+ $$ = fmod($1, $3);
+ }
+ | expr '^' expr
+ {
+ errno = 0;
+ $$ = pow($1, $3);
+ if (errno == EDOM) {
+ lex_error("arguments to `^' operator out of domain");
+ YYABORT;
+ }
+ if (errno == ERANGE) {
+ lex_error("result of `^' operator out of range");
+ YYABORT;
+ }
+ }
+ | '-' expr %prec '!'
+ { $$ = -$2; }
+ | '(' any_expr ')'
+ { $$ = $2; }
+ | SIN '(' any_expr ')'
+ {
+ errno = 0;
+ $$ = sin($3);
+ if (errno == ERANGE) {
+ lex_error("sin result out of range");
+ YYABORT;
+ }
+ }
+ | COS '(' any_expr ')'
+ {
+ errno = 0;
+ $$ = cos($3);
+ if (errno == ERANGE) {
+ lex_error("cos result out of range");
+ YYABORT;
+ }
+ }
+ | ATAN2 '(' any_expr ',' any_expr ')'
+ {
+ errno = 0;
+ $$ = atan2($3, $5);
+ if (errno == EDOM) {
+ lex_error("atan2 argument out of domain");
+ YYABORT;
+ }
+ if (errno == ERANGE) {
+ lex_error("atan2 result out of range");
+ YYABORT;
+ }
+ }
+ | LOG '(' any_expr ')'
+ {
+ errno = 0;
+ $$ = log10($3);
+ if (errno == ERANGE) {
+ lex_error("log result out of range");
+ YYABORT;
+ }
+ }
+ | EXP '(' any_expr ')'
+ {
+ errno = 0;
+ $$ = pow(10.0, $3);
+ if (errno == ERANGE) {
+ lex_error("exp result out of range");
+ YYABORT;
+ }
+ }
+ | SQRT '(' any_expr ')'
+ {
+ errno = 0;
+ $$ = sqrt($3);
+ if (errno == EDOM) {
+ lex_error("sqrt argument out of domain");
+ YYABORT;
+ }
+ }
+ | K_MAX '(' any_expr ',' any_expr ')'
+ { $$ = $3 > $5 ? $3 : $5; }
+ | K_MIN '(' any_expr ',' any_expr ')'
+ { $$ = $3 < $5 ? $3 : $5; }
+ | INT '(' any_expr ')'
+ { $$ = floor($3); }
+ | RAND '(' any_expr ')'
+ { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
+ | RAND '(' ')'
+ {
+ /* return a random number in the range [0,1) */
+ /* portable, but not very random */
+ $$ = (rand() & 0x7fff) / double(0x8000);
+ }
+ | SRAND '(' any_expr ')'
+ { $$ = 0; srand((unsigned int)$3); }
+ | expr '<' expr
+ { $$ = ($1 < $3); }
+ | expr LESSEQUAL expr
+ { $$ = ($1 <= $3); }
+ | expr '>' expr
+ { $$ = ($1 > $3); }
+ | expr GREATEREQUAL expr
+ { $$ = ($1 >= $3); }
+ | expr EQUALEQUAL expr
+ { $$ = ($1 == $3); }
+ | expr NOTEQUAL expr
+ { $$ = ($1 != $3); }
+ | expr ANDAND expr
+ { $$ = ($1 != 0.0 && $3 != 0.0); }
+ | expr OROR expr
+ { $$ = ($1 != 0.0 || $3 != 0.0); }
+ | '!' expr
+ { $$ = ($2 == 0.0); }
+
+ ;
+
+%%
+
+/* bison defines const to be empty unless __STDC__ is defined, which it
+isn't under cfront */
+
+#ifdef const
+#undef const
+#endif
+
+static struct {
+ const char *name;
+ double val;
+ int scaled; // non-zero if val should be multiplied by scale
+} defaults_table[] = {
+ { "arcrad", .25, 1 },
+ { "arrowht", .1, 1 },
+ { "arrowwid", .05, 1 },
+ { "circlerad", .25, 1 },
+ { "boxht", .5, 1 },
+ { "boxwid", .75, 1 },
+ { "boxrad", 0.0, 1 },
+ { "dashwid", .05, 1 },
+ { "ellipseht", .5, 1 },
+ { "ellipsewid", .75, 1 },
+ { "moveht", .5, 1 },
+ { "movewid", .5, 1 },
+ { "lineht", .5, 1 },
+ { "linewid", .5, 1 },
+ { "textht", 0.0, 1 },
+ { "textwid", 0.0, 1 },
+ { "scale", 1.0, 0 },
+ { "linethick", -1.0, 0 }, // in points
+ { "fillval", .5, 0 },
+ { "arrowhead", 1.0, 0 },
+ { "maxpswid", 8.5, 0 },
+ { "maxpsht", 11.0, 0 },
+};
+
+place *lookup_label(const char *label)
+{
+ saved_state *state = current_saved_state;
+ PTABLE(place) *tbl = current_table;
+ for (;;) {
+ place *pl = tbl->lookup(label);
+ if (pl)
+ return pl;
+ if (!state)
+ return 0;
+ tbl = state->tbl;
+ state = state->prev;
+ }
+}
+
+void define_label(const char *label, const place *pl)
+{
+ place *p = new place;
+ *p = *pl;
+ current_table->define(label, p);
+}
+
+int lookup_variable(const char *name, double *val)
+{
+ place *pl = lookup_label(name);
+ if (pl) {
+ *val = pl->x;
+ return 1;
+ }
+ return 0;
+}
+
+void define_variable(const char *name, double val)
+{
+ place *p = new place;
+ p->obj = 0;
+ p->x = val;
+ p->y = 0.0;
+ current_table->define(name, p);
+ if (strcmp(name, "scale") == 0) {
+ // When the scale changes, reset all scaled pre-defined variables to
+ // their default values.
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (defaults_table[i].scaled)
+ define_variable(defaults_table[i].name, val*defaults_table[i].val);
+ }
+}
+
+// called once only (not once per parse)
+
+void parse_init()
+{
+ current_direction = RIGHT_DIRECTION;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+ // This resets everything to its default value.
+ reset_all();
+}
+
+void reset(const char *nm)
+{
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (strcmp(nm, defaults_table[i].name) == 0) {
+ double val = defaults_table[i].val;
+ if (defaults_table[i].scaled) {
+ double scale;
+ lookup_variable("scale", &scale);
+ val *= scale;
+ }
+ define_variable(defaults_table[i].name, val);
+ return;
+ }
+ lex_error("`%1' is not a predefined variable", nm);
+}
+
+void reset_all()
+{
+ // We only have to explicitly reset the pre-defined variables that
+ // aren't scaled because `scale' is not scaled, and changing the
+ // value of `scale' will reset all the pre-defined variables that
+ // are scaled.
+ for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
+ if (!defaults_table[i].scaled)
+ define_variable(defaults_table[i].name, defaults_table[i].val);
+}
+
+// called after each parse
+
+void parse_cleanup()
+{
+ while (current_saved_state != 0) {
+ delete current_table;
+ current_table = current_saved_state->tbl;
+ saved_state *tem = current_saved_state;
+ current_saved_state = current_saved_state->prev;
+ delete tem;
+ }
+ assert(current_table == &top_table);
+ PTABLE_ITERATOR(place) iter(current_table);
+ const char *key;
+ place *pl;
+ while (iter.next(&key, &pl))
+ if (pl->obj != 0) {
+ position pos = pl->obj->origin();
+ pl->obj = 0;
+ pl->x = pos.x;
+ pl->y = pos.y;
+ }
+ while (olist.head != 0) {
+ object *tem = olist.head;
+ olist.head = olist.head->next;
+ delete tem;
+ }
+ olist.tail = 0;
+ current_direction = RIGHT_DIRECTION;
+ current_position.x = 0.0;
+ current_position.y = 0.0;
+}
+
+const char *ordinal_postfix(int n)
+{
+ if (n < 10 || n > 20)
+ switch (n % 10) {
+ case 1:
+ return "st";
+ case 2:
+ return "nd";
+ case 3:
+ return "rd";
+ }
+ return "th";
+}
+
+const char *object_type_name(object_type type)
+{
+ switch (type) {
+ case BOX_OBJECT:
+ return "box";
+ case CIRCLE_OBJECT:
+ return "circle";
+ case ELLIPSE_OBJECT:
+ return "ellipse";
+ case ARC_OBJECT:
+ return "arc";
+ case SPLINE_OBJECT:
+ return "spline";
+ case LINE_OBJECT:
+ return "line";
+ case ARROW_OBJECT:
+ return "arrow";
+ case MOVE_OBJECT:
+ return "move";
+ case TEXT_OBJECT:
+ return "\"\"";
+ case BLOCK_OBJECT:
+ return "[]";
+ case OTHER_OBJECT:
+ case MARK_OBJECT:
+ default:
+ break;
+ }
+ return "object";
+}
+
+static char sprintf_buf[1024];
+
+char *format_number(const char *form, double n)
+{
+ if (form == 0)
+ form = "%g";
+ else {
+ // this is a fairly feeble attempt at validation of the format
+ int nspecs = 0;
+ for (const char *p = form; *p != '\0'; p++)
+ if (*p == '%') {
+ if (p[1] == '%')
+ p++;
+ else
+ nspecs++;
+ }
+ if (nspecs > 1) {
+ lex_error("bad format `%1'", form);
+ return strsave(form);
+ }
+ }
+ sprintf(sprintf_buf, form, n);
+ return strsave(sprintf_buf);
+}
+
+char *do_sprintf(const char *form, const double *v, int nv)
+{
+ string result;
+ int i = 0;
+ string one_format;
+ while (*form) {
+ if (*form == '%') {
+ one_format += *form++;
+ for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
+ one_format += *form;
+ if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
+ lex_error("bad sprintf format");
+ result += one_format;
+ result += form;
+ break;
+ }
+ if (*form == '%') {
+ one_format += *form++;
+ one_format += '\0';
+ sprintf(sprintf_buf, one_format.contents());
+ }
+ else {
+ if (i >= nv) {
+ lex_error("too few arguments to sprintf");
+ result += one_format;
+ result += form;
+ break;
+ }
+ one_format += *form++;
+ one_format += '\0';
+ sprintf(sprintf_buf, one_format.contents(), v[i++]);
+ }
+ one_format.clear();
+ result += sprintf_buf;
+ }
+ else
+ result += *form++;
+ }
+ result += '\0';
+ return strsave(result.contents());
+}
diff --git a/contrib/groff/src/preproc/pic/pic_tab.h b/contrib/groff/src/preproc/pic/pic_tab.h
new file mode 100644
index 000000000000..17658829a672
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/pic_tab.h
@@ -0,0 +1,131 @@
+#define LABEL 257
+#define VARIABLE 258
+#define NUMBER 259
+#define TEXT 260
+#define COMMAND_LINE 261
+#define DELIMITED 262
+#define ORDINAL 263
+#define TH 264
+#define LEFT_ARROW_HEAD 265
+#define RIGHT_ARROW_HEAD 266
+#define DOUBLE_ARROW_HEAD 267
+#define LAST 268
+#define UP 269
+#define DOWN 270
+#define LEFT 271
+#define RIGHT 272
+#define BOX 273
+#define CIRCLE 274
+#define ELLIPSE 275
+#define ARC 276
+#define LINE 277
+#define ARROW 278
+#define MOVE 279
+#define SPLINE 280
+#define HEIGHT 281
+#define RADIUS 282
+#define WIDTH 283
+#define DIAMETER 284
+#define FROM 285
+#define TO 286
+#define AT 287
+#define WITH 288
+#define BY 289
+#define THEN 290
+#define SOLID 291
+#define DOTTED 292
+#define DASHED 293
+#define CHOP 294
+#define SAME 295
+#define INVISIBLE 296
+#define LJUST 297
+#define RJUST 298
+#define ABOVE 299
+#define BELOW 300
+#define OF 301
+#define THE 302
+#define WAY 303
+#define BETWEEN 304
+#define AND 305
+#define HERE 306
+#define DOT_N 307
+#define DOT_E 308
+#define DOT_W 309
+#define DOT_S 310
+#define DOT_NE 311
+#define DOT_SE 312
+#define DOT_NW 313
+#define DOT_SW 314
+#define DOT_C 315
+#define DOT_START 316
+#define DOT_END 317
+#define DOT_X 318
+#define DOT_Y 319
+#define DOT_HT 320
+#define DOT_WID 321
+#define DOT_RAD 322
+#define SIN 323
+#define COS 324
+#define ATAN2 325
+#define LOG 326
+#define EXP 327
+#define SQRT 328
+#define K_MAX 329
+#define K_MIN 330
+#define INT 331
+#define RAND 332
+#define SRAND 333
+#define COPY 334
+#define THRU 335
+#define TOP 336
+#define BOTTOM 337
+#define UPPER 338
+#define LOWER 339
+#define SH 340
+#define PRINT 341
+#define CW 342
+#define CCW 343
+#define FOR 344
+#define DO 345
+#define IF 346
+#define ELSE 347
+#define ANDAND 348
+#define OROR 349
+#define NOTEQUAL 350
+#define EQUALEQUAL 351
+#define LESSEQUAL 352
+#define GREATEREQUAL 353
+#define LEFT_CORNER 354
+#define RIGHT_CORNER 355
+#define CENTER 356
+#define END 357
+#define START 358
+#define RESET 359
+#define UNTIL 360
+#define PLOT 361
+#define THICKNESS 362
+#define FILL 363
+#define ALIGNED 364
+#define SPRINTF 365
+#define COMMAND 366
+#define DEFINE 367
+#define UNDEF 368
+typedef union {
+ char *str;
+ int n;
+ double x;
+ struct { double x, y; } pair;
+ struct { double x; char *body; } if_data;
+ struct { char *str; const char *filename; int lineno; } lstr;
+ struct { double *v; int nv; int maxv; } dv;
+ struct { double val; int is_multiplicative; } by;
+ place pl;
+ object *obj;
+ corner crn;
+ path *pth;
+ object_spec *spec;
+ saved_state *pstate;
+ graphics_state state;
+ object_type obtype;
+} YYSTYPE;
+extern YYSTYPE yylval;
diff --git a/contrib/groff/src/preproc/pic/position.h b/contrib/groff/src/preproc/pic/position.h
new file mode 100644
index 000000000000..ab7d5469c006
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/position.h
@@ -0,0 +1,47 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct place;
+struct position {
+ double x;
+ double y;
+ position(double, double );
+ position();
+ position(const place &);
+ position &operator+=(const position &);
+ position &operator-=(const position &);
+ position &operator*=(double);
+ position &operator/=(double);
+};
+
+position operator-(const position &);
+position operator+(const position &, const position &);
+position operator-(const position &, const position &);
+position operator/(const position &, double);
+position operator*(const position &, double);
+// dot product
+double operator*(const position &, const position &);
+int operator==(const position &, const position &);
+int operator!=(const position &, const position &);
+
+double hypot(const position &a);
+
+typedef position distance;
+
diff --git a/contrib/groff/src/preproc/pic/tex.cc b/contrib/groff/src/preproc/pic/tex.cc
new file mode 100644
index 000000000000..2a91b621c62d
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/tex.cc
@@ -0,0 +1,412 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "pic.h"
+
+#ifdef TEX_SUPPORT
+
+#include "common.h"
+
+class tex_output : public common_output {
+public:
+ tex_output();
+ ~tex_output();
+ void start_picture(double, const position &ll, const position &ur);
+ void finish_picture();
+ void text(const position &, text_piece *, int, double);
+ void line(const position &, const position *, int n,
+ const line_type &);
+ void polygon(const position *, int n,
+ const line_type &, double);
+ void spline(const position &, const position *, int n,
+ const line_type &);
+ void arc(const position &, const position &, const position &,
+ const line_type &);
+ void circle(const position &, double rad, const line_type &, double);
+ void ellipse(const position &, const distance &, const line_type &, double);
+ void command(const char *, const char *, int);
+ int supports_filled_polygons();
+private:
+ position upper_left;
+ double height;
+ double width;
+ double scale;
+ double pen_size;
+
+ void point(const position &);
+ void dot(const position &, const line_type &);
+ void solid_arc(const position &cent, double rad, double start_angle,
+ double end_angle, const line_type &lt);
+ position transform(const position &);
+protected:
+ virtual void set_pen_size(double ps);
+};
+
+// convert inches to milliinches
+
+inline int milliinches(double x)
+{
+ return int(x*1000.0 + .5);
+}
+
+inline position tex_output::transform(const position &pos)
+{
+ return position((pos.x - upper_left.x)/scale,
+ (upper_left.y - pos.y)/scale);
+}
+
+output *make_tex_output()
+{
+ return new tex_output;
+}
+
+tex_output::tex_output()
+{
+}
+
+tex_output::~tex_output()
+{
+}
+
+const int DEFAULT_PEN_SIZE = 8;
+
+void tex_output::set_pen_size(double ps)
+{
+ if (ps < 0.0)
+ ps = -1.0;
+ if (ps != pen_size) {
+ pen_size = ps;
+ printf(" \\special{pn %d}%%\n",
+ ps < 0.0 ? DEFAULT_PEN_SIZE : int(ps*(1000.0/72.0) + .5));
+ }
+}
+
+void tex_output::start_picture(double sc, const position &ll,
+ const position &ur)
+{
+ upper_left.x = ll.x;
+ upper_left.y = ur.y;
+ scale = compute_scale(sc, ll, ur);
+ height = (ur.y - ll.y)/scale;
+ width = (ur.x - ll.x)/scale;
+ /* the point of \vskip 0pt is to ensure that the vtop gets
+ a height of 0 rather than the height of the hbox; this
+ might be non-zero if text from text attributes lies outside pic's
+ idea of the bounding box of the picture. */
+ fputs("\\expandafter\\ifx\\csname graph\\endcsname\\relax \\csname newbox\\endcsname\\graph\\fi\n"
+ "\\expandafter\\ifx\\csname graphtemp\\endcsname\\relax \\csname newdimen\\endcsname\\graphtemp\\fi\n"
+ "\\setbox\\graph=\\vtop{\\vskip 0pt\\hbox{%\n",
+ stdout);
+ pen_size = -2.0;
+}
+
+void tex_output::finish_picture()
+{
+ printf(" \\hbox{\\vrule depth%.3fin width0pt height 0pt}%%\n"
+ " \\kern %.3fin\n"
+ " }%%\n"
+ "}%%\n",
+ height, width);
+}
+
+void tex_output::text(const position &center, text_piece *v, int n, double)
+{
+ position c = transform(center);
+ for (int i = 0; i < n; i++)
+ if (v[i].text != 0 && *v[i].text != '\0') {
+ int j = 2*i - n + 1;
+ if (v[i].adj.v == ABOVE_ADJUST)
+ j--;
+ else if (v[i].adj.v == BELOW_ADJUST)
+ j++;
+ if (j == 0) {
+ printf(" \\graphtemp=.5ex\\advance\\graphtemp by %.3fin\n", c.y);
+ }
+ else {
+ printf(" \\graphtemp=\\baselineskip"
+ "\\multiply\\graphtemp by %d"
+ "\\divide\\graphtemp by 2\n"
+ " \\advance\\graphtemp by .5ex"
+ "\\advance\\graphtemp by %.3fin\n",
+ j, c.y);
+ }
+ printf(" \\rlap{\\kern %.3fin\\lower\\graphtemp", c.x);
+ fputs("\\hbox to 0pt{", stdout);
+ if (v[i].adj.h != LEFT_ADJUST)
+ fputs("\\hss ", stdout);
+ fputs(v[i].text, stdout);
+ if (v[i].adj.h != RIGHT_ADJUST)
+ fputs("\\hss", stdout);
+ fputs("}}%\n", stdout);
+ }
+}
+
+void tex_output::point(const position &pos)
+{
+ position p = transform(pos);
+ printf(" \\special{pa %d %d}%%\n", milliinches(p.x), milliinches(p.y));
+}
+
+void tex_output::line(const position &start, const position *v, int n,
+ const line_type &lt)
+{
+ set_pen_size(lt.thickness);
+ point(start);
+ for (int i = 0; i < n; i++)
+ point(v[i]);
+ fputs(" \\special{", stdout);
+ switch(lt.type) {
+ case line_type::invisible:
+ fputs("ip", stdout);
+ break;
+ case line_type::solid:
+ fputs("fp", stdout);
+ break;
+ case line_type::dotted:
+ printf("dt %.3f", lt.dash_width/scale);
+ break;
+ case line_type::dashed:
+ printf("da %.3f", lt.dash_width/scale);
+ break;
+ }
+ fputs("}%\n", stdout);
+}
+
+void tex_output::polygon(const position *v, int n,
+ const line_type &lt, double fill)
+{
+ if (fill >= 0.0) {
+ if (fill > 1.0)
+ fill = 1.0;
+ printf(" \\special{sh %.3f}%%\n", fill);
+ }
+ line(v[n-1], v, n, lt);
+}
+
+void tex_output::spline(const position &start, const position *v, int n,
+ const line_type &lt)
+{
+ if (lt.type == line_type::invisible)
+ return;
+ set_pen_size(lt.thickness);
+ point(start);
+ for (int i = 0; i < n; i++)
+ point(v[i]);
+ fputs(" \\special{sp", stdout);
+ switch(lt.type) {
+ case line_type::solid:
+ break;
+ case line_type::dotted:
+ printf(" %.3f", -lt.dash_width/scale);
+ break;
+ case line_type::dashed:
+ printf(" %.3f", lt.dash_width/scale);
+ break;
+ case line_type::invisible:
+ assert(0);
+ }
+ fputs("}%\n", stdout);
+}
+
+void tex_output::solid_arc(const position &cent, double rad,
+ double start_angle, double end_angle,
+ const line_type &lt)
+{
+ set_pen_size(lt.thickness);
+ position c = transform(cent);
+ printf(" \\special{ar %d %d %d %d %f %f}%%\n",
+ milliinches(c.x),
+ milliinches(c.y),
+ milliinches(rad/scale),
+ milliinches(rad/scale),
+ -end_angle,
+ (-end_angle > -start_angle) ? (double)M_PI * 2 - start_angle
+ : -start_angle);
+}
+
+void tex_output::arc(const position &start, const position &cent,
+ const position &end, const line_type &lt)
+{
+ switch (lt.type) {
+ case line_type::invisible:
+ break;
+ case line_type::dashed:
+ dashed_arc(start, cent, end, lt);
+ break;
+ case line_type::dotted:
+ dotted_arc(start, cent, end, lt);
+ break;
+ case line_type::solid:
+ {
+ position c;
+ if (!compute_arc_center(start, cent, end, &c)) {
+ line(start, &end, 1, lt);
+ break;
+ }
+ solid_arc(c,
+ hypot(cent - start),
+ atan2(start.y - c.y, start.x - c.x),
+ atan2(end.y - c.y, end.x - c.x),
+ lt);
+ break;
+ }
+ }
+}
+
+void tex_output::circle(const position &cent, double rad,
+ const line_type &lt, double fill)
+{
+ if (fill >= 0.0 && lt.type != line_type::solid) {
+ if (fill > 1.0)
+ fill = 1.0;
+ line_type ilt;
+ ilt.type = line_type::invisible;
+ ellipse(cent, position(rad*2.0, rad*2.0), ilt, fill);
+ }
+ switch (lt.type) {
+ case line_type::dashed:
+ dashed_circle(cent, rad, lt);
+ break;
+ case line_type::invisible:
+ break;
+ case line_type::solid:
+ ellipse(cent, position(rad*2.0,rad*2.0), lt, fill);
+ break;
+ case line_type::dotted:
+ dotted_circle(cent, rad, lt);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void tex_output::ellipse(const position &cent, const distance &dim,
+ const line_type &lt, double fill)
+{
+ if (lt.type == line_type::invisible) {
+ if (fill < 0.0)
+ return;
+ }
+ else
+ set_pen_size(lt.thickness);
+ if (fill >= 0.0) {
+ if (fill > 1.0)
+ fill = 1.0;
+ printf(" \\special{sh %.3f}%%\n", fill);
+ }
+ position c = transform(cent);
+ printf(" \\special{%s %d %d %d %d 0 6.28319}%%\n",
+ (lt.type == line_type::invisible ? "ia" : "ar"),
+ milliinches(c.x),
+ milliinches(c.y),
+ milliinches(dim.x/(2.0*scale)),
+ milliinches(dim.y/(2.0*scale)));
+}
+
+void tex_output::command(const char *s, const char *, int)
+{
+ fputs(s, stdout);
+ putchar('%'); // avoid unwanted spaces
+ putchar('\n');
+}
+
+int tex_output::supports_filled_polygons()
+{
+ return 1;
+}
+
+void tex_output::dot(const position &pos, const line_type &lt)
+{
+ if (zero_length_line_flag) {
+ line_type slt = lt;
+ slt.type = line_type::solid;
+ line(pos, &pos, 1, slt);
+ }
+ else {
+ int dot_rad = int(lt.thickness*(1000.0/(72.0*2)) + .5);
+ if (dot_rad == 0)
+ dot_rad = 1;
+ position p = transform(pos);
+ printf(" \\special{sh 1}%%\n"
+ " \\special{ia %d %d %d %d 0 6.28319}%%\n",
+ milliinches(p.x), milliinches(p.y), dot_rad, dot_rad);
+ }
+}
+
+class tpic_output : public tex_output {
+public:
+ tpic_output();
+ void command(const char *, const char *, int);
+private:
+ void set_pen_size(double ps);
+ int default_pen_size;
+ int prev_default_pen_size;
+};
+
+tpic_output::tpic_output()
+: default_pen_size(DEFAULT_PEN_SIZE), prev_default_pen_size(DEFAULT_PEN_SIZE)
+{
+}
+
+void tpic_output::command(const char *s, const char *filename, int lineno)
+{
+ assert(s[0] == '.');
+ if (s[1] == 'p' && s[2] == 's' && (s[3] == '\0' || !csalpha(s[3]))) {
+ const char *p = s + 3;
+ while (csspace(*p))
+ p++;
+ if (*p == '\0') {
+ int temp = default_pen_size;
+ default_pen_size = prev_default_pen_size;
+ prev_default_pen_size = temp;
+ }
+ else {
+ char *ptr;
+ int temp = (int)strtol(p, &ptr, 10);
+ if (temp == 0 && ptr == p)
+ error_with_file_and_line(filename, lineno,
+ "argument to `.ps' not an integer");
+ else if (temp < 0)
+ error_with_file_and_line(filename, lineno,
+ "negative pen size");
+ else {
+ prev_default_pen_size = default_pen_size;
+ default_pen_size = temp;
+ }
+ }
+ }
+ else
+ printf("\\%s%%\n", s + 1);
+}
+
+void tpic_output::set_pen_size(double ps)
+{
+ if (ps < 0.0)
+ printf(" \\special{pn %d}%%\n", default_pen_size);
+ else
+ tex_output::set_pen_size(ps);
+}
+
+output *make_tpic_output()
+{
+ return new tpic_output;
+}
+
+#endif
diff --git a/contrib/groff/src/preproc/pic/text.h b/contrib/groff/src/preproc/pic/text.h
new file mode 100644
index 000000000000..f9d34875075f
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/text.h
@@ -0,0 +1,28 @@
+// -*- C++ -*-
+
+enum hadjustment {
+ CENTER_ADJUST,
+ LEFT_ADJUST,
+ RIGHT_ADJUST
+ };
+
+enum vadjustment {
+ NONE_ADJUST,
+ ABOVE_ADJUST,
+ BELOW_ADJUST
+ };
+
+struct adjustment {
+ hadjustment h;
+ vadjustment v;
+};
+
+struct text_piece {
+ char *text;
+ adjustment adj;
+ const char *filename;
+ int lineno;
+
+ text_piece();
+ ~text_piece();
+};
diff --git a/contrib/groff/src/preproc/pic/troff.cc b/contrib/groff/src/preproc/pic/troff.cc
new file mode 100644
index 000000000000..62fe540ae3a6
--- /dev/null
+++ b/contrib/groff/src/preproc/pic/troff.cc
@@ -0,0 +1,504 @@
+// -*- 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 "pic.h"
+#include "common.h"
+#include "htmlindicate.h"
+
+
+const double RELATIVE_THICKNESS = -1.0;
+const double BAD_THICKNESS = -2.0;
+
+class simple_output : public common_output {
+ virtual void simple_line(const position &, const position &) = 0;
+ virtual void simple_spline(const position &, const position *, int n) = 0;
+ virtual void simple_arc(const position &, const position &,
+ const position &) = 0;
+ virtual void simple_circle(int, const position &, double rad) = 0;
+ virtual void simple_ellipse(int, const position &, const distance &) = 0;
+ virtual void simple_polygon(int, const position *, int) = 0;
+ virtual void line_thickness(double) = 0;
+ virtual void set_fill(double) = 0;
+ void dot(const position &, const line_type &) = 0;
+public:
+ void start_picture(double sc, const position &ll, const position &ur) = 0;
+ void finish_picture() = 0;
+ void text(const position &, text_piece *, int, double) = 0;
+ void line(const position &, const position *, int n,
+ const line_type &);
+ void polygon(const position *, int n,
+ const line_type &, double);
+ void spline(const position &, const position *, int n,
+ const line_type &);
+ void arc(const position &, const position &, const position &,
+ const line_type &);
+ void circle(const position &, double rad, const line_type &, double);
+ void ellipse(const position &, const distance &, const line_type &, double);
+ int supports_filled_polygons();
+};
+
+int simple_output::supports_filled_polygons()
+{
+ return driver_extension_flag != 0;
+}
+
+void simple_output::arc(const position &start, const position &cent,
+ const position &end, const line_type &lt)
+{
+ switch (lt.type) {
+ case line_type::solid:
+ line_thickness(lt.thickness);
+ simple_arc(start, cent, end);
+ break;
+ case line_type::invisible:
+ break;
+ case line_type::dashed:
+ dashed_arc(start, cent, end, lt);
+ break;
+ case line_type::dotted:
+ dotted_arc(start, cent, end, lt);
+ break;
+ }
+}
+
+void simple_output::line(const position &start, const position *v, int n,
+ const line_type &lt)
+{
+ position pos = start;
+ line_thickness(lt.thickness);
+ for (int i = 0; i < n; i++) {
+ switch (lt.type) {
+ case line_type::solid:
+ simple_line(pos, v[i]);
+ break;
+ case line_type::dotted:
+ {
+ distance vec(v[i] - pos);
+ double dist = hypot(vec);
+ int ndots = int(dist/lt.dash_width + .5);
+ if (ndots == 0)
+ dot(pos, lt);
+ else {
+ vec /= double(ndots);
+ for (int j = 0; j <= ndots; j++)
+ dot(pos + vec*j, lt);
+ }
+ }
+ break;
+ case line_type::dashed:
+ {
+ distance vec(v[i] - pos);
+ double dist = hypot(vec);
+ if (dist <= lt.dash_width*2.0)
+ simple_line(pos, v[i]);
+ else {
+ int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
+ distance dash_vec = vec*(lt.dash_width/dist);
+ double dash_gap = (dist - lt.dash_width)/ndashes;
+ distance dash_gap_vec = vec*(dash_gap/dist);
+ for (int j = 0; j <= ndashes; j++) {
+ position s(pos + dash_gap_vec*j);
+ simple_line(s, s + dash_vec);
+ }
+ }
+ }
+ break;
+ case line_type::invisible:
+ break;
+ default:
+ assert(0);
+ }
+ pos = v[i];
+ }
+}
+
+void simple_output::spline(const position &start, const position *v, int n,
+ const line_type &lt)
+{
+ line_thickness(lt.thickness);
+ simple_spline(start, v, n);
+}
+
+void simple_output::polygon(const position *v, int n,
+ const line_type &lt, double fill)
+{
+ if (driver_extension_flag) {
+ if (fill >= 0.0) {
+ set_fill(fill);
+ simple_polygon(1, v, n);
+ }
+ }
+ if (lt.type == line_type::solid && driver_extension_flag) {
+ line_thickness(lt.thickness);
+ simple_polygon(0, v, n);
+ }
+ else if (lt.type != line_type::invisible) {
+ line_thickness(lt.thickness);
+ line(v[n - 1], v, n, lt);
+ }
+}
+
+void simple_output::circle(const position &cent, double rad,
+ const line_type &lt, double fill)
+{
+ if (driver_extension_flag && fill >= 0.0) {
+ set_fill(fill);
+ simple_circle(1, cent, rad);
+ }
+ line_thickness(lt.thickness);
+ switch (lt.type) {
+ case line_type::invisible:
+ break;
+ case line_type::dashed:
+ dashed_circle(cent, rad, lt);
+ break;
+ case line_type::dotted:
+ dotted_circle(cent, rad, lt);
+ break;
+ case line_type::solid:
+ simple_circle(0, cent, rad);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void simple_output::ellipse(const position &cent, const distance &dim,
+ const line_type &lt, double fill)
+{
+ if (driver_extension_flag && fill >= 0.0) {
+ set_fill(fill);
+ simple_ellipse(1, cent, dim);
+ }
+ if (lt.type != line_type::invisible)
+ line_thickness(lt.thickness);
+ switch (lt.type) {
+ case line_type::invisible:
+ break;
+ case line_type::dotted:
+ case line_type::dashed:
+ case line_type::solid:
+ simple_ellipse(0, cent, dim);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+#define FILL_MAX 1000
+
+class troff_output : public simple_output {
+ const char *last_filename;
+ position upper_left;
+ double height;
+ double scale;
+ double last_line_thickness;
+ double last_fill;
+public:
+ troff_output();
+ ~troff_output();
+ void start_picture(double, const position &ll, const position &ur);
+ void finish_picture();
+ void text(const position &, text_piece *, int, double);
+ void dot(const position &, const line_type &);
+ void command(const char *, const char *, int);
+ void set_location(const char *, int);
+ void simple_line(const position &, const position &);
+ void simple_spline(const position &, const position *, int n);
+ void simple_arc(const position &, const position &, const position &);
+ void simple_circle(int, const position &, double rad);
+ void simple_ellipse(int, const position &, const distance &);
+ void simple_polygon(int, const position *, int);
+ void line_thickness(double p);
+ void set_fill(double);
+ position transform(const position &);
+};
+
+output *make_troff_output()
+{
+ return new troff_output;
+}
+
+troff_output::troff_output()
+: last_filename(0), last_line_thickness(BAD_THICKNESS), last_fill(-1.0)
+{
+}
+
+troff_output::~troff_output()
+{
+}
+
+inline position troff_output::transform(const position &pos)
+{
+ return position((pos.x - upper_left.x)/scale,
+ (upper_left.y - pos.y)/scale);
+}
+
+#define FILL_REG "00"
+
+// If this register > 0, then pic will generate \X'ps: ...' commands
+// if the aligned attribute is used.
+#define GROPS_REG "0p"
+
+// If this register is defined, geqn won't produce `\x's.
+#define EQN_NO_EXTRA_SPACE_REG "0x"
+
+void troff_output::start_picture(double sc,
+ const position &ll, const position &ur)
+{
+ upper_left.x = ll.x;
+ upper_left.y = ur.y;
+ scale = compute_scale(sc, ll, ur);
+ height = (ur.y - ll.y)/scale;
+ double width = (ur.x - ll.x)/scale;
+ graphic_start(0);
+ printf(".PS %.3fi %.3fi", height, width);
+ if (args)
+ printf(" %s\n", args);
+ else
+ putchar('\n');
+ printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
+ printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
+ printf(".nr " FILL_REG " \\n(.u\n.nf\n");
+ printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
+ // This guarantees that if the picture is used in a diversion it will
+ // have the right width.
+ printf("\\h'%.3fi'\n.sp -1\n", width);
+}
+
+void troff_output::finish_picture()
+{
+ line_thickness(BAD_THICKNESS);
+ last_fill = -1.0; // force it to be reset for each picture
+ if (!flyback_flag)
+ printf(".sp %.3fi+1\n", height);
+ printf(".if \\n(" FILL_REG " .fi\n");
+ printf(".br\n");
+ printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
+ // this is a little gross
+ set_location(current_filename, current_lineno);
+ fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
+ graphic_end();
+}
+
+void troff_output::command(const char *s,
+ const char *filename, int lineno)
+{
+ if (filename != 0)
+ set_location(filename, lineno);
+ fputs(s, stdout);
+ putchar('\n');
+}
+
+void troff_output::simple_circle(int filled, const position &cent, double rad)
+{
+ position c = transform(cent);
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\D'%c%.3fi'"
+ "\n.sp -1\n",
+ c.x - rad/scale,
+ c.y,
+ (filled ? 'C' : 'c'),
+ rad*2.0/scale);
+}
+
+void troff_output::simple_ellipse(int filled, const position &cent,
+ const distance &dim)
+{
+ position c = transform(cent);
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\D'%c%.3fi %.3fi'"
+ "\n.sp -1\n",
+ c.x - dim.x/(2.0*scale),
+ c.y,
+ (filled ? 'E' : 'e'),
+ dim.x/scale, dim.y/scale);
+}
+
+void troff_output::simple_arc(const position &start, const distance &cent,
+ const distance &end)
+{
+ position s = transform(start);
+ position c = transform(cent);
+ distance cv = c - s;
+ distance ev = transform(end) - c;
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\D'a%.3fi %.3fi %.3fi %.3fi'"
+ "\n.sp -1\n",
+ s.x, s.y, cv.x, cv.y, ev.x, ev.y);
+}
+
+void troff_output::simple_line(const position &start, const position &end)
+{
+ position s = transform(start);
+ distance ev = transform(end) - s;
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\D'l%.3fi %.3fi'"
+ "\n.sp -1\n",
+ s.x, s.y, ev.x, ev.y);
+}
+
+void troff_output::simple_spline(const position &start,
+ const position *v, int n)
+{
+ position pos = transform(start);
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'",
+ pos.x, pos.y);
+ fputs("\\D'~", stdout);
+ for (int i = 0; i < n; i++) {
+ position temp = transform(v[i]);
+ distance d = temp - pos;
+ pos = temp;
+ if (i != 0)
+ putchar(' ');
+ printf("%.3fi %.3fi", d.x, d.y);
+ }
+ printf("'\n.sp -1\n");
+}
+
+// a solid polygon
+
+void troff_output::simple_polygon(int filled, const position *v, int n)
+{
+ position pos = transform(v[0]);
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'",
+ pos.x, pos.y);
+ printf("\\D'%c", (filled ? 'P' : 'p'));
+ for (int i = 1; i < n; i++) {
+ position temp = transform(v[i]);
+ distance d = temp - pos;
+ pos = temp;
+ if (i != 1)
+ putchar(' ');
+ printf("%.3fi %.3fi", d.x, d.y);
+ }
+ printf("'\n.sp -1\n");
+}
+
+const double TEXT_AXIS = 0.22; // in ems
+
+static const char *choose_delimiter(const char *text)
+{
+ if (strchr(text, '\'') == 0)
+ return "'";
+ else
+ return "\\(ts";
+}
+
+void troff_output::text(const position &center, text_piece *v, int n,
+ double ang)
+{
+ line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
+ int rotate_flag = 0;
+ if (driver_extension_flag && ang != 0.0) {
+ rotate_flag = 1;
+ position c = transform(center);
+ printf(".if \\n(" GROPS_REG " \\{\\\n"
+ "\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
+ "\n.sp -1\n"
+ ".\\}\n",
+ c.x, c.y, -ang*180.0/M_PI);
+ }
+ for (int i = 0; i < n; i++)
+ if (v[i].text != 0 && *v[i].text != '\0') {
+ position c = transform(center);
+ if (v[i].filename != 0)
+ set_location(v[i].filename, v[i].lineno);
+ printf("\\h'%.3fi", c.x);
+ const char *delim = choose_delimiter(v[i].text);
+ if (v[i].adj.h == RIGHT_ADJUST)
+ printf("-\\w%s%s%su", delim, v[i].text, delim);
+ else if (v[i].adj.h != LEFT_ADJUST)
+ printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
+ putchar('\'');
+ printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
+ c.y,
+ n - 1,
+ i,
+ TEXT_AXIS);
+ if (v[i].adj.v == ABOVE_ADJUST)
+ printf("-.5v");
+ else if (v[i].adj.v == BELOW_ADJUST)
+ printf("+.5v");
+ putchar('\'');
+ fputs(v[i].text, stdout);
+ fputs("\n.sp -1\n", stdout);
+ }
+ if (rotate_flag)
+ printf(".if '\\*(.T'ps' \\{\\\n"
+ "\\X'ps: exec grestore'\n.sp -1\n"
+ ".\\}\n");
+}
+
+void troff_output::line_thickness(double p)
+{
+ if (p < 0.0)
+ p = RELATIVE_THICKNESS;
+ if (driver_extension_flag && p != last_line_thickness) {
+ printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
+ last_line_thickness = p;
+ }
+}
+
+void troff_output::set_fill(double f)
+{
+ if (driver_extension_flag && f != last_fill) {
+ printf("\\D'f %du'\\h'%du'\n.sp -1\n", int(f*FILL_MAX), -int(f*FILL_MAX));
+ last_fill = f;
+ }
+}
+
+const double DOT_AXIS = .044;
+
+void troff_output::dot(const position &cent, const line_type &lt)
+{
+ if (driver_extension_flag) {
+ line_thickness(lt.thickness);
+ simple_line(cent, cent);
+ }
+ else {
+ position c = transform(cent);
+ printf("\\h'%.3fi-(\\w'.'u/2u)'"
+ "\\v'%.3fi+%.2fm'"
+ ".\n.sp -1\n",
+ c.x,
+ c.y,
+ DOT_AXIS);
+ }
+}
+
+void troff_output::set_location(const char *s, int n)
+{
+ if (last_filename != 0 && strcmp(s, last_filename) == 0)
+ printf(".lf %d\n", n);
+ else {
+ printf(".lf %d %s\n", n, s);
+ last_filename = s;
+ }
+}
diff --git a/contrib/groff/src/preproc/refer/Makefile.sub b/contrib/groff/src/preproc/refer/Makefile.sub
new file mode 100644
index 000000000000..1631b5e36e05
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/Makefile.sub
@@ -0,0 +1,23 @@
+PROG=refer
+MAN1=refer.n
+XLIBS=$(LIBBIB) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ command.o \
+ label.o \
+ ref.o \
+ refer.o \
+ token.o
+CCSRCS=\
+ $(srcdir)/command.cc \
+ $(srcdir)/ref.cc \
+ $(srcdir)/refer.cc \
+ $(srcdir)/token.cc
+HDRS=\
+ $(srcdir)/refer.h \
+ $(srcdir)/token.h \
+ $(srcdir)/command.h \
+ $(srcdir)/ref.h
+GRAM=$(srcdir)/label.y
+YTABC=$(srcdir)/label.cc
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/refer/TODO b/contrib/groff/src/preproc/refer/TODO
new file mode 100644
index 000000000000..5bbd9bff1e8c
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/TODO
@@ -0,0 +1,124 @@
+inline references
+
+Some sort of macro/subroutine that can cover several references.
+
+move-punctuation should ignore multiple punctuation characters.
+
+Make the index files machine independent.
+
+Allow search keys to be negated (with !) to indicate that the
+reference should not contain the key. Ignore negated keys during
+indexed searching.
+
+Provide an option with lkbib and lookbib that prints the location
+(filename, position) of each reference. Need to map filename_id's
+back to filenames.
+
+Rename join-authors to join-fields. Have a separate label-join-fields
+command used by @ and #.
+
+Have some sort of quantifier: eg $.n#A means execute `$.n' for each
+instance of an A field, setting $ to that field, and then join the
+results using the join-authors command.
+
+no-text-in-bracket command which says not to allow post_text and
+pre_text when the [] flags has been given. Useful for superscripted
+footnotes.
+
+Make it possible to translate - to \(en in page ranges.
+
+Trim eign a bit.
+
+In indexed searching discard all numeric keys except dates.
+
+Allow `\ ' to separate article from first word.
+
+%also
+
+Option automatically to supply [] flags in every reference.
+
+See if we can avoid requiring a comma before jr. and so on
+in find_last_name().
+
+Cache sortified authors in authors string during tentative evaluation of
+label specification.
+
+Possibly don't allow * and % expressions in the first part of ?:, | or
+& expressions.
+
+Handle better the case where <> occurs inside functions and in the
+first operand of ~. Or perhaps implement <> using some magic character
+in the string.
+
+Should special treatment be given to lines beginning with . in
+references? (Unix refer seems to treat them like `%').
+
+Add global flag to control whether all files should be stat-ed after
+loading, and whether they should be stat-ed before each search.
+Perhaps make this dependent on the number of files there are.
+
+Option to truncate keys to truncate_len in linear searching.
+
+Allow multiple -f options in indxbib.
+
+In indxbib, possibly store common words rather than common words
+filename. In this case store only words that are actually present in
+the file.
+
+Perhaps we should put out an obnoxious copyright message when lookbib
+starts up.
+
+Provide an option that writes a file containing just the references
+actually used. Useful if you want to distribute a document.
+
+Have a magic token such that
+%A <sort stuff><magic token><print stuff>
+will print as though it were
+%A <print stuff>
+but sort as though it were
+%A <sort stuff>
+Do we need this if we can specify author alternatives for sorting?
+No, provided we have separate alternatives for @.
+
+In consider_authors when last names are ambiguous we might be able to
+use just the first name and not Jr. bit. Or we might be able to
+abbreviate the author.
+
+It ought to be possible to specify an alternative field to sort on
+instead of date. (ie if there's a field giving the type of document --
+these references should sort after any years)
+
+Provide a way to execute a command using a command-line option.
+
+Option to set the label-spec as a command-line option (-L).
+
+Command to to specify which fields can occur multiple times:
+multiple AE
+
+Command to specify how various fields sort:
+aort-as-name A
+sort-as-date D
+sort-as-title T
+sort-as-other O
+
+Command to specify which fields are author fields:
+# if we don't have A use field Q
+author-fields AQ
+
+Commands to set properties of tokens.
+sortify-token \(ae ae
+uppercase-token \[ae] \[AE]
+
+Command to set the names of months:
+months january february march april may ...
+
+Perhaps provide some sort of macro capability:
+# perhaps a macro capability
+defmacro foo
+annotation-field $1
+endef
+
+Command to control strings used in capitalization
+capitalize-start \s+2
+capitalize-end \s-2
+(perhaps make these arguments to the capitalize command.)
diff --git a/contrib/groff/src/preproc/refer/command.cc b/contrib/groff/src/preproc/refer/command.cc
new file mode 100644
index 000000000000..004189eeb03a
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/command.cc
@@ -0,0 +1,807 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "refer.h"
+#include "refid.h"
+#include "search.h"
+#include "command.h"
+
+cset cs_field_name = csalpha;
+
+class input_item {
+ input_item *next;
+ char *filename;
+ int first_lineno;
+ string buffer;
+ const char *ptr;
+ const char *end;
+public:
+ input_item(string &, const char *, int = 1);
+ ~input_item();
+ int get_char();
+ int peek_char();
+ void skip_char();
+ int get_location(const char **, int *);
+
+ friend class input_stack;
+};
+
+input_item::input_item(string &s, const char *fn, int ln)
+: filename(strsave(fn)), first_lineno(ln)
+{
+ buffer.move(s);
+ ptr = buffer.contents();
+ end = ptr + buffer.length();
+}
+
+input_item::~input_item()
+{
+ a_delete filename;
+}
+
+inline int input_item::peek_char()
+{
+ if (ptr >= end)
+ return EOF;
+ else
+ return (unsigned char)*ptr;
+}
+
+inline int input_item::get_char()
+{
+ if (ptr >= end)
+ return EOF;
+ else
+ return (unsigned char)*ptr++;
+}
+
+inline void input_item::skip_char()
+{
+ ptr++;
+}
+
+int input_item::get_location(const char **filenamep, int *linenop)
+{
+ *filenamep = filename;
+ if (ptr == buffer.contents())
+ *linenop = first_lineno;
+ else {
+ int ln = first_lineno;
+ const char *e = ptr - 1;
+ for (const char *p = buffer.contents(); p < e; p++)
+ if (*p == '\n')
+ ln++;
+ *linenop = ln;
+ }
+ return 1;
+}
+
+class input_stack {
+ static input_item *top;
+public:
+ static void init();
+ static int get_char();
+ static int peek_char();
+ static void skip_char() { top->skip_char(); }
+ static void push_file(const char *);
+ static void push_string(string &, const char *, int);
+ static void error(const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+};
+
+input_item *input_stack::top = 0;
+
+void input_stack::init()
+{
+ while (top) {
+ input_item *tem = top;
+ top = top->next;
+ delete tem;
+ }
+}
+
+int input_stack::get_char()
+{
+ while (top) {
+ int c = top->get_char();
+ if (c >= 0)
+ return c;
+ input_item *tem = top;
+ top = top->next;
+ delete tem;
+ }
+ return -1;
+}
+
+int input_stack::peek_char()
+{
+ while (top) {
+ int c = top->peek_char();
+ if (c >= 0)
+ return c;
+ input_item *tem = top;
+ top = top->next;
+ delete tem;
+ }
+ return -1;
+}
+
+void input_stack::push_file(const char *fn)
+{
+ FILE *fp;
+ if (strcmp(fn, "-") == 0) {
+ fp = stdin;
+ fn = "<standard input>";
+ }
+ else {
+ errno = 0;
+ fp = fopen(fn, "r");
+ if (fp == 0) {
+ error("can't open `%1': %2", fn, strerror(errno));
+ return;
+ }
+ }
+ string buf;
+ int bol = 1;
+ int lineno = 1;
+ for (;;) {
+ int c = getc(fp);
+ if (bol && c == '.') {
+ // replace lines beginning with .R1 or .R2 with a blank line
+ c = getc(fp);
+ if (c == 'R') {
+ c = getc(fp);
+ if (c == '1' || c == '2') {
+ int cc = c;
+ c = getc(fp);
+ if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
+ while (c != '\n' && c != EOF)
+ c = getc(fp);
+ }
+ else {
+ buf += '.';
+ buf += 'R';
+ buf += cc;
+ }
+ }
+ else {
+ buf += '.';
+ buf += 'R';
+ }
+ }
+ else
+ buf += '.';
+ }
+ if (c == EOF)
+ break;
+ if (illegal_input_char(c))
+ error_with_file_and_line(fn, lineno,
+ "illegal input character code %1", int(c));
+ else {
+ buf += c;
+ if (c == '\n') {
+ bol = 1;
+ lineno++;
+ }
+ else
+ bol = 0;
+ }
+ }
+ if (fp != stdin)
+ fclose(fp);
+ if (buf.length() > 0 && buf[buf.length() - 1] != '\n')
+ buf += '\n';
+ input_item *it = new input_item(buf, fn);
+ it->next = top;
+ top = it;
+}
+
+void input_stack::push_string(string &s, const char *filename, int lineno)
+{
+ input_item *it = new input_item(s, filename, lineno);
+ it->next = top;
+ top = it;
+}
+
+void input_stack::error(const char *format, const errarg &arg1,
+ const errarg &arg2, const errarg &arg3)
+{
+ const char *filename;
+ int lineno;
+ for (input_item *it = top; it; it = it->next)
+ if (it->get_location(&filename, &lineno)) {
+ error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3);
+ return;
+ }
+ ::error(format, arg1, arg2, arg3);
+}
+
+void command_error(const char *format, const errarg &arg1,
+ const errarg &arg2, const errarg &arg3)
+{
+ input_stack::error(format, arg1, arg2, arg3);
+}
+
+// # not recognized in ""
+// \<newline> is recognized in ""
+// # does not conceal newline
+// if missing closing quote, word extends to end of line
+// no special treatment of \ other than before newline
+// \<newline> not recognized after #
+// ; allowed as alternative to newline
+// ; not recognized in ""
+// don't clear word_buffer; just append on
+// return -1 for EOF, 0 for newline, 1 for word
+
+int get_word(string &word_buffer)
+{
+ int c = input_stack::get_char();
+ for (;;) {
+ if (c == '#') {
+ do {
+ c = input_stack::get_char();
+ } while (c != '\n' && c != EOF);
+ break;
+ }
+ if (c == '\\' && input_stack::peek_char() == '\n')
+ input_stack::skip_char();
+ else if (c != ' ' && c != '\t')
+ break;
+ c = input_stack::get_char();
+ }
+ if (c == EOF)
+ return -1;
+ if (c == '\n' || c == ';')
+ return 0;
+ if (c == '"') {
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == EOF || c == '\n')
+ break;
+ input_stack::skip_char();
+ if (c == '"') {
+ int d = input_stack::peek_char();
+ if (d == '"')
+ input_stack::skip_char();
+ else
+ break;
+ }
+ else if (c == '\\') {
+ int d = input_stack::peek_char();
+ if (d == '\n')
+ input_stack::skip_char();
+ else
+ word_buffer += '\\';
+ }
+ else
+ word_buffer += c;
+ }
+ return 1;
+ }
+ word_buffer += c;
+ for (;;) {
+ c = input_stack::peek_char();
+ if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';')
+ break;
+ input_stack::skip_char();
+ if (c == '\\') {
+ int d = input_stack::peek_char();
+ if (d == '\n')
+ input_stack::skip_char();
+ else
+ word_buffer += '\\';
+ }
+ else
+ word_buffer += c;
+ }
+ return 1;
+}
+
+union argument {
+ const char *s;
+ int n;
+};
+
+// This is for debugging.
+
+static void echo_command(int argc, argument *argv)
+{
+ for (int i = 0; i < argc; i++)
+ fprintf(stderr, "%s\n", argv[i].s);
+}
+
+static void include_command(int argc, argument *argv)
+{
+ assert(argc == 1);
+ input_stack::push_file(argv[0].s);
+}
+
+static void capitalize_command(int argc, argument *argv)
+{
+ if (argc > 0)
+ capitalize_fields = argv[0].s;
+ else
+ capitalize_fields.clear();
+}
+
+static void accumulate_command(int, argument *)
+{
+ accumulate = 1;
+}
+
+static void no_accumulate_command(int, argument *)
+{
+ accumulate = 0;
+}
+
+static void move_punctuation_command(int, argument *)
+{
+ move_punctuation = 1;
+}
+
+static void no_move_punctuation_command(int, argument *)
+{
+ move_punctuation = 0;
+}
+
+static void sort_command(int argc, argument *argv)
+{
+ if (argc == 0)
+ sort_fields = "AD";
+ else
+ sort_fields = argv[0].s;
+ accumulate = 1;
+}
+
+static void no_sort_command(int, argument *)
+{
+ sort_fields.clear();
+}
+
+static void articles_command(int argc, argument *argv)
+{
+ articles.clear();
+ int i;
+ for (i = 0; i < argc; i++) {
+ articles += argv[i].s;
+ articles += '\0';
+ }
+ int len = articles.length();
+ for (i = 0; i < len; i++)
+ articles[i] = cmlower(articles[i]);
+}
+
+static void database_command(int argc, argument *argv)
+{
+ for (int i = 0; i < argc; i++)
+ database_list.add_file(argv[i].s);
+}
+
+static void default_database_command(int, argument *)
+{
+ search_default = 1;
+}
+
+static void no_default_database_command(int, argument *)
+{
+ search_default = 0;
+}
+
+static void bibliography_command(int argc, argument *argv)
+{
+ const char *saved_filename = current_filename;
+ int saved_lineno = current_lineno;
+ int saved_label_in_text = label_in_text;
+ label_in_text = 0;
+ if (!accumulate)
+ fputs(".]<\n", stdout);
+ for (int i = 0; i < argc; i++)
+ do_bib(argv[i].s);
+ if (accumulate)
+ output_references();
+ else
+ fputs(".]>\n", stdout);
+ current_filename = saved_filename;
+ current_lineno = saved_lineno;
+ label_in_text = saved_label_in_text;
+}
+
+static void annotate_command(int argc, argument *argv)
+{
+ if (argc > 0)
+ annotation_field = argv[0].s[0];
+ else
+ annotation_field = 'X';
+ if (argc == 2)
+ annotation_macro = argv[1].s;
+ else
+ annotation_macro = "AP";
+}
+
+static void no_annotate_command(int, argument *)
+{
+ annotation_macro.clear();
+ annotation_field = -1;
+}
+
+static void reverse_command(int, argument *argv)
+{
+ reverse_fields = argv[0].s;
+}
+
+static void no_reverse_command(int, argument *)
+{
+ reverse_fields.clear();
+}
+
+static void abbreviate_command(int argc, argument *argv)
+{
+ abbreviate_fields = argv[0].s;
+ period_before_initial = argc > 1 ? argv[1].s : ". ";
+ period_before_last_name = argc > 2 ? argv[2].s : ". ";
+ period_before_other = argc > 3 ? argv[3].s : ". ";
+ period_before_hyphen = argc > 4 ? argv[4].s : ".";
+}
+
+static void no_abbreviate_command(int, argument *)
+{
+ abbreviate_fields.clear();
+}
+
+string search_ignore_fields;
+
+static void search_ignore_command(int argc, argument *argv)
+{
+ if (argc > 0)
+ search_ignore_fields = argv[0].s;
+ else
+ search_ignore_fields = "XYZ";
+ search_ignore_fields += '\0';
+ linear_ignore_fields = search_ignore_fields.contents();
+}
+
+static void no_search_ignore_command(int, argument *)
+{
+ linear_ignore_fields = "";
+}
+
+static void search_truncate_command(int argc, argument *argv)
+{
+ if (argc > 0)
+ linear_truncate_len = argv[0].n;
+ else
+ linear_truncate_len = 6;
+}
+
+static void no_search_truncate_command(int, argument *)
+{
+ linear_truncate_len = -1;
+}
+
+static void discard_command(int argc, argument *argv)
+{
+ if (argc == 0)
+ discard_fields = "XYZ";
+ else
+ discard_fields = argv[0].s;
+ accumulate = 1;
+}
+
+static void no_discard_command(int, argument *)
+{
+ discard_fields.clear();
+}
+
+static void label_command(int, argument *argv)
+{
+ set_label_spec(argv[0].s);
+}
+
+static void abbreviate_label_ranges_command(int argc, argument *argv)
+{
+ abbreviate_label_ranges = 1;
+ label_range_indicator = argc > 0 ? argv[0].s : "-";
+}
+
+static void no_abbreviate_label_ranges_command(int, argument *)
+{
+ abbreviate_label_ranges = 0;
+}
+
+static void label_in_reference_command(int, argument *)
+{
+ label_in_reference = 1;
+}
+
+static void no_label_in_reference_command(int, argument *)
+{
+ label_in_reference = 0;
+}
+
+static void label_in_text_command(int, argument *)
+{
+ label_in_text = 1;
+}
+
+static void no_label_in_text_command(int, argument *)
+{
+ label_in_text = 0;
+}
+
+static void sort_adjacent_labels_command(int, argument *)
+{
+ sort_adjacent_labels = 1;
+}
+
+static void no_sort_adjacent_labels_command(int, argument *)
+{
+ sort_adjacent_labels = 0;
+}
+
+static void date_as_label_command(int argc, argument *argv)
+{
+ if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*"))
+ date_as_label = 1;
+}
+
+static void no_date_as_label_command(int, argument *)
+{
+ date_as_label = 0;
+}
+
+static void short_label_command(int, argument *argv)
+{
+ if (set_short_label_spec(argv[0].s))
+ short_label_flag = 1;
+}
+
+static void no_short_label_command(int, argument *)
+{
+ short_label_flag = 0;
+}
+
+static void compatible_command(int, argument *)
+{
+ compatible_flag = 1;
+}
+
+static void no_compatible_command(int, argument *)
+{
+ compatible_flag = 0;
+}
+
+static void join_authors_command(int argc, argument *argv)
+{
+ join_authors_exactly_two = argv[0].s;
+ join_authors_default = argc > 1 ? argv[1].s : argv[0].s;
+ join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s;
+}
+
+static void bracket_label_command(int, argument *argv)
+{
+ pre_label = argv[0].s;
+ post_label = argv[1].s;
+ sep_label = argv[2].s;
+}
+
+static void separate_label_second_parts_command(int, argument *argv)
+{
+ separate_label_second_parts = argv[0].s;
+}
+
+static void et_al_command(int argc, argument *argv)
+{
+ et_al = argv[0].s;
+ et_al_min_elide = argv[1].n;
+ if (et_al_min_elide < 1)
+ et_al_min_elide = 1;
+ et_al_min_total = argc >= 3 ? argv[2].n : 0;
+}
+
+static void no_et_al_command(int, argument *)
+{
+ et_al.clear();
+ et_al_min_elide = 0;
+}
+
+typedef void (*command_t)(int, argument *);
+
+/* arg_types is a string describing the numbers and types of arguments.
+s means a string, i means an integer, f is a list of fields, F is
+a single field,
+? means that the previous argument is optional, * means that the
+previous argument can occur any number of times. */
+
+struct {
+ const char *name;
+ command_t func;
+ const char *arg_types;
+} command_table[] = {
+ { "include", include_command, "s" },
+ { "echo", echo_command, "s*" },
+ { "capitalize", capitalize_command, "f?" },
+ { "accumulate", accumulate_command, "" },
+ { "no-accumulate", no_accumulate_command, "" },
+ { "move-punctuation", move_punctuation_command, "" },
+ { "no-move-punctuation", no_move_punctuation_command, "" },
+ { "sort", sort_command, "s?" },
+ { "no-sort", no_sort_command, "" },
+ { "articles", articles_command, "s*" },
+ { "database", database_command, "ss*" },
+ { "default-database", default_database_command, "" },
+ { "no-default-database", no_default_database_command, "" },
+ { "bibliography", bibliography_command, "ss*" },
+ { "annotate", annotate_command, "F?s?" },
+ { "no-annotate", no_annotate_command, "" },
+ { "reverse", reverse_command, "s" },
+ { "no-reverse", no_reverse_command, "" },
+ { "abbreviate", abbreviate_command, "ss?s?s?s?" },
+ { "no-abbreviate", no_abbreviate_command, "" },
+ { "search-ignore", search_ignore_command, "f?" },
+ { "no-search-ignore", no_search_ignore_command, "" },
+ { "search-truncate", search_truncate_command, "i?" },
+ { "no-search-truncate", no_search_truncate_command, "" },
+ { "discard", discard_command, "f?" },
+ { "no-discard", no_discard_command, "" },
+ { "label", label_command, "s" },
+ { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" },
+ { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" },
+ { "label-in-reference", label_in_reference_command, "" },
+ { "no-label-in-reference", no_label_in_reference_command, "" },
+ { "label-in-text", label_in_text_command, "" },
+ { "no-label-in-text", no_label_in_text_command, "" },
+ { "sort-adjacent-labels", sort_adjacent_labels_command, "" },
+ { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" },
+ { "date-as-label", date_as_label_command, "s?" },
+ { "no-date-as-label", no_date_as_label_command, "" },
+ { "short-label", short_label_command, "s" },
+ { "no-short-label", no_short_label_command, "" },
+ { "compatible", compatible_command, "" },
+ { "no-compatible", no_compatible_command, "" },
+ { "join-authors", join_authors_command, "sss?" },
+ { "bracket-label", bracket_label_command, "sss" },
+ { "separate-label-second-parts", separate_label_second_parts_command, "s" },
+ { "et-al", et_al_command, "sii?" },
+ { "no-et-al", no_et_al_command, "" },
+};
+
+static int check_args(const char *types, const char *name,
+ int argc, argument *argv)
+{
+ int argno = 0;
+ while (*types) {
+ if (argc == 0) {
+ if (types[1] == '?')
+ break;
+ else if (types[1] == '*') {
+ assert(types[2] == '\0');
+ break;
+ }
+ else {
+ input_stack::error("missing argument for command `%1'", name);
+ return 0;
+ }
+ }
+ switch (*types) {
+ case 's':
+ break;
+ case 'i':
+ {
+ char *ptr;
+ long n = strtol(argv->s, &ptr, 10);
+ if ((n == 0 && ptr == argv->s)
+ || *ptr != '\0') {
+ input_stack::error("argument %1 for command `%2' must be an integer",
+ argno + 1, name);
+ return 0;
+ }
+ argv->n = (int)n;
+ break;
+ }
+ case 'f':
+ {
+ for (const char *ptr = argv->s; *ptr != '\0'; ptr++)
+ if (!cs_field_name(*ptr)) {
+ input_stack::error("argument %1 for command `%2' must be a list of fields",
+ argno + 1, name);
+ return 0;
+ }
+ break;
+ }
+ case 'F':
+ if (argv->s[0] == '\0' || argv->s[1] != '\0'
+ || !cs_field_name(argv->s[0])) {
+ input_stack::error("argument %1 for command `%2' must be a field name",
+ argno + 1, name);
+ return 0;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ if (types[1] == '?')
+ types += 2;
+ else if (types[1] != '*')
+ types += 1;
+ --argc;
+ ++argv;
+ ++argno;
+ }
+ if (argc > 0) {
+ input_stack::error("too many arguments for command `%1'", name);
+ return 0;
+ }
+ return 1;
+}
+
+static void execute_command(const char *name, int argc, argument *argv)
+{
+ for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++)
+ if (strcmp(name, command_table[i].name) == 0) {
+ if (check_args(command_table[i].arg_types, name, argc, argv))
+ (*command_table[i].func)(argc, argv);
+ return;
+ }
+ input_stack::error("unknown command `%1'", name);
+}
+
+static void command_loop()
+{
+ string command;
+ for (;;) {
+ command.clear();
+ int res = get_word(command);
+ if (res != 1) {
+ if (res == 0)
+ continue;
+ break;
+ }
+ int argc = 0;
+ command += '\0';
+ while ((res = get_word(command)) == 1) {
+ argc++;
+ command += '\0';
+ }
+ argument *argv = new argument[argc];
+ const char *ptr = command.contents();
+ for (int i = 0; i < argc; i++)
+ argv[i].s = ptr = strchr(ptr, '\0') + 1;
+ execute_command(command.contents(), argc, argv);
+ a_delete argv;
+ if (res == -1)
+ break;
+ }
+}
+
+void process_commands(const char *file)
+{
+ input_stack::init();
+ input_stack::push_file(file);
+ command_loop();
+}
+
+void process_commands(string &s, const char *file, int lineno)
+{
+ input_stack::init();
+ input_stack::push_string(s, file, lineno);
+ command_loop();
+}
diff --git a/contrib/groff/src/preproc/refer/command.h b/contrib/groff/src/preproc/refer/command.h
new file mode 100644
index 000000000000..c7085db6927b
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/command.h
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+void process_commands(const char *file);
+void process_commands(string &s, const char *file, int lineno);
+
+extern int accumulate;
+extern int move_punctuation;
+extern int search_default;
+extern search_list database_list;
+extern int label_in_text;
+extern int label_in_reference;
+extern int sort_adjacent_labels;
+extern string pre_label;
+extern string post_label;
+extern string sep_label;
+
+extern void do_bib(const char *);
+extern void output_references();
diff --git a/contrib/groff/src/preproc/refer/label.cc b/contrib/groff/src/preproc/refer/label.cc
new file mode 100644
index 000000000000..c6dc07cd4817
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/label.cc
@@ -0,0 +1,1602 @@
+#ifndef lint
+/*static char yysccsid[] = "from: @(#)yaccpar 1.9 (Berkeley) 02/21/93";*/
+static char yyrcsid[] = "$Id: label.cc,v 1.2 2000/02/28 11:02:12 wlemb Exp $";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#line 22 "label.y"
+
+#include "refer.h"
+#include "refid.h"
+#include "ref.h"
+#include "token.h"
+
+int yylex();
+void yyerror(const char *);
+int yyparse();
+
+static const char *format_serial(char c, int n);
+
+struct label_info {
+ int start;
+ int length;
+ int count;
+ int total;
+ label_info(const string &);
+};
+
+label_info *lookup_label(const string &label);
+
+struct expression {
+ enum {
+ /* Does the tentative label depend on the reference?*/
+ CONTAINS_VARIABLE = 01,
+ CONTAINS_STAR = 02,
+ CONTAINS_FORMAT = 04,
+ CONTAINS_AT = 010
+ };
+ virtual ~expression() { }
+ virtual void evaluate(int, const reference &, string &,
+ substring_position &) = 0;
+ virtual unsigned analyze() { return 0; }
+};
+
+class at_expr : public expression {
+public:
+ at_expr() { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_VARIABLE|CONTAINS_AT; }
+};
+
+class format_expr : public expression {
+ char type;
+ int width;
+ int first_number;
+public:
+ format_expr(char c, int w = 0, int f = 1)
+ : type(c), width(w), first_number(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_FORMAT; }
+};
+
+class field_expr : public expression {
+ int number;
+ char name;
+public:
+ field_expr(char nm, int num) : number(num), name(nm) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_VARIABLE; }
+};
+
+class literal_expr : public expression {
+ string s;
+public:
+ literal_expr(const char *ptr, int len) : s(ptr, len) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class unary_expr : public expression {
+protected:
+ expression *expr;
+public:
+ unary_expr(expression *e) : expr(e) { }
+ ~unary_expr() { delete expr; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() { return expr ? expr->analyze() : 0; }
+};
+
+/* This caches the analysis of an expression.*/
+
+class analyzed_expr : public unary_expr {
+ unsigned flags;
+public:
+ analyzed_expr(expression *);
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return flags; }
+};
+
+class star_expr : public unary_expr {
+public:
+ star_expr(expression *e) : unary_expr(e) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() {
+ return ((expr ? (expr->analyze() & ~CONTAINS_VARIABLE) : 0)
+ | CONTAINS_STAR);
+ }
+};
+
+typedef void map_func(const char *, const char *, string &);
+
+class map_expr : public unary_expr {
+ map_func *func;
+public:
+ map_expr(expression *e, map_func *f) : unary_expr(e), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+typedef const char *extractor_func(const char *, const char *, const char **);
+
+class extractor_expr : public unary_expr {
+ int part;
+ extractor_func *func;
+public:
+ enum { BEFORE = +1, MATCH = 0, AFTER = -1 };
+ extractor_expr(expression *e, extractor_func *f, int pt)
+ : unary_expr(e), part(pt), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class truncate_expr : public unary_expr {
+ int n;
+public:
+ truncate_expr(expression *e, int i) : unary_expr(e), n(i) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class separator_expr : public unary_expr {
+public:
+ separator_expr(expression *e) : unary_expr(e) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class binary_expr : public expression {
+protected:
+ expression *expr1;
+ expression *expr2;
+public:
+ binary_expr(expression *e1, expression *e2) : expr1(e1), expr2(e2) { }
+ ~binary_expr() { delete expr1; delete expr2; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() {
+ return (expr1 ? expr1->analyze() : 0) | (expr2 ? expr2->analyze() : 0);
+ }
+};
+
+class alternative_expr : public binary_expr {
+public:
+ alternative_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class list_expr : public binary_expr {
+public:
+ list_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class substitute_expr : public binary_expr {
+public:
+ substitute_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class ternary_expr : public expression {
+protected:
+ expression *expr1;
+ expression *expr2;
+ expression *expr3;
+public:
+ ternary_expr(expression *e1, expression *e2, expression *e3)
+ : expr1(e1), expr2(e2), expr3(e3) { }
+ ~ternary_expr() { delete expr1; delete expr2; delete expr3; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() {
+ return ((expr1 ? expr1->analyze() : 0)
+ | (expr2 ? expr2->analyze() : 0)
+ | (expr3 ? expr3->analyze() : 0));
+ }
+};
+
+class conditional_expr : public ternary_expr {
+public:
+ conditional_expr(expression *e1, expression *e2, expression *e3)
+ : ternary_expr(e1, e2, e3) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+static expression *parsed_label = 0;
+static expression *parsed_date_label = 0;
+static expression *parsed_short_label = 0;
+
+static expression *parse_result;
+
+string literals;
+
+#line 221 "label.y"
+typedef union {
+ int num;
+ expression *expr;
+ struct { int ndigits; int val; } dig;
+ struct { int start; int len; } str;
+} YYSTYPE;
+#line 218 "y.tab.c"
+#define TOKEN_LETTER 257
+#define TOKEN_LITERAL 258
+#define TOKEN_DIGIT 259
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 1, 1, 6, 6, 2, 2, 2, 3, 3,
+ 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 9, 9, 7, 7, 8, 8,
+ 10, 10, 10,
+};
+short yylen[] = { 2,
+ 1, 1, 5, 0, 1, 1, 3, 3, 1, 2,
+ 1, 3, 1, 1, 1, 2, 2, 2, 5, 3,
+ 3, 2, 3, 3, 0, 1, 1, 2, 1, 2,
+ 0, 1, 1,
+};
+short yydefred[] = { 0,
+ 0, 14, 13, 0, 0, 0, 0, 5, 0, 0,
+ 0, 0, 1, 27, 0, 17, 29, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 22, 0, 28,
+ 30, 23, 24, 0, 0, 0, 32, 33, 0, 0,
+ 0, 0, 0, 0, 3, 0, 19,
+};
+short yydgoto[] = { 7,
+ 8, 9, 10, 11, 12, 13, 15, 18, 47, 39,
+};
+short yysindex[] = { -32,
+ -257, 0, 0, -240, -32, -32, 0, 0, -18, -32,
+ -36, -114, 0, 0, -246, 0, 0, -241, -14, -39,
+ -32, -32, -32, -114, -21, -257, -257, 0, -32, 0,
+ 0, 0, 0, -25, -32, -32, 0, 0, -223, -246,
+ -246, -36, -32, -257, 0, -246, 0,
+};
+short yyrindex[] = { 35,
+ 1, 0, 0, 0, -5, -4, 0, 0, 14, 208,
+ 159, 224, 0, 0, 11, 0, 0, 40, 0, 0,
+ 2, 0, 0, 253, -220, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 263, 281, 0, 0, 0, 50,
+ 105, 214, 0, 115, 0, 149, 0,
+};
+short yygindex[] = { 0,
+ 19, 0, 7, 37, -10, 10, -23, 0, 0, 0,
+};
+#define YYTABLESIZE 511
+short yytable[] = { 24,
+ 15, 14, 40, 41, 4, 28, 26, 5, 27, 25,
+ 16, 29, 30, 2, 19, 20, 16, 31, 17, 23,
+ 46, 37, 33, 38, 24, 24, 32, 6, 35, 36,
+ 34, 3, 43, 44, 4, 4, 31, 15, 15, 18,
+ 15, 15, 15, 15, 21, 15, 15, 16, 16, 20,
+ 16, 16, 16, 16, 2, 16, 16, 4, 15, 4,
+ 15, 45, 15, 15, 15, 42, 0, 0, 16, 0,
+ 16, 2, 16, 16, 16, 2, 18, 18, 0, 18,
+ 18, 18, 18, 0, 18, 18, 20, 20, 0, 20,
+ 20, 20, 20, 0, 20, 20, 0, 18, 0, 18,
+ 0, 18, 18, 18, 21, 22, 0, 20, 0, 20,
+ 0, 20, 20, 20, 25, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0, 15, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0, 16, 0, 0, 0,
+ 0, 21, 21, 0, 21, 21, 21, 21, 26, 21,
+ 21, 25, 25, 0, 25, 25, 25, 25, 11, 25,
+ 25, 0, 21, 18, 21, 18, 21, 21, 21, 0,
+ 0, 0, 25, 20, 25, 20, 25, 25, 25, 0,
+ 0, 0, 0, 0, 0, 26, 26, 0, 26, 26,
+ 26, 26, 0, 26, 26, 11, 11, 0, 11, 11,
+ 0, 0, 0, 0, 0, 0, 26, 6, 26, 0,
+ 26, 26, 26, 12, 0, 0, 11, 0, 11, 0,
+ 11, 11, 11, 9, 1, 2, 0, 0, 21, 0,
+ 21, 0, 0, 0, 0, 0, 0, 0, 25, 0,
+ 25, 0, 0, 0, 0, 6, 0, 0, 6, 0,
+ 12, 12, 10, 12, 12, 0, 0, 15, 15, 0,
+ 9, 9, 7, 9, 9, 6, 0, 16, 16, 6,
+ 6, 12, 26, 12, 26, 12, 12, 12, 0, 0,
+ 8, 9, 11, 9, 11, 9, 9, 9, 0, 10,
+ 10, 0, 10, 10, 0, 0, 18, 18, 0, 0,
+ 7, 0, 0, 7, 0, 0, 20, 20, 0, 0,
+ 10, 0, 10, 0, 10, 10, 10, 0, 8, 0,
+ 7, 8, 0, 0, 7, 7, 0, 0, 0, 0,
+ 0, 6, 0, 0, 0, 0, 0, 12, 8, 12,
+ 0, 0, 8, 8, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 21, 0, 0, 0, 0, 0, 0, 0,
+ 0, 25, 25, 0, 0, 0, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 26, 26, 0, 0, 0,
+ 0, 0, 0, 0, 0, 11, 11, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 12, 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 9, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
+ 10,
+};
+short yycheck[] = { 10,
+ 0, 259, 26, 27, 37, 42, 43, 40, 45, 46,
+ 0, 126, 259, 0, 5, 6, 257, 259, 259, 38,
+ 44, 43, 62, 45, 35, 36, 41, 60, 22, 23,
+ 21, 64, 58, 257, 0, 41, 257, 37, 38, 0,
+ 40, 41, 42, 43, 63, 45, 46, 37, 38, 0,
+ 40, 41, 42, 43, 41, 45, 46, 62, 58, 58,
+ 60, 43, 62, 63, 64, 29, -1, -1, 58, -1,
+ 60, 58, 62, 63, 64, 62, 37, 38, -1, 40,
+ 41, 42, 43, -1, 45, 46, 37, 38, -1, 40,
+ 41, 42, 43, -1, 45, 46, -1, 58, -1, 60,
+ -1, 62, 63, 64, 0, 124, -1, 58, -1, 60,
+ -1, 62, 63, 64, 0, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 124, -1, 126, -1, -1, -1,
+ -1, -1, -1, -1, 124, -1, 126, -1, -1, -1,
+ -1, 37, 38, -1, 40, 41, 42, 43, 0, 45,
+ 46, 37, 38, -1, 40, 41, 42, 43, 0, 45,
+ 46, -1, 58, 124, 60, 126, 62, 63, 64, -1,
+ -1, -1, 58, 124, 60, 126, 62, 63, 64, -1,
+ -1, -1, -1, -1, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, 45, 46, 37, 38, -1, 40, 41,
+ -1, -1, -1, -1, -1, -1, 58, 0, 60, -1,
+ 62, 63, 64, 0, -1, -1, 58, -1, 60, -1,
+ 62, 63, 64, 0, 257, 258, -1, -1, 124, -1,
+ 126, -1, -1, -1, -1, -1, -1, -1, 124, -1,
+ 126, -1, -1, -1, -1, 38, -1, -1, 41, -1,
+ 37, 38, 0, 40, 41, -1, -1, 257, 258, -1,
+ 37, 38, 0, 40, 41, 58, -1, 257, 258, 62,
+ 63, 58, 124, 60, 126, 62, 63, 64, -1, -1,
+ 0, 58, 124, 60, 126, 62, 63, 64, -1, 37,
+ 38, -1, 40, 41, -1, -1, 257, 258, -1, -1,
+ 38, -1, -1, 41, -1, -1, 257, 258, -1, -1,
+ 58, -1, 60, -1, 62, 63, 64, -1, 38, -1,
+ 58, 41, -1, -1, 62, 63, -1, -1, -1, -1,
+ -1, 124, -1, -1, -1, -1, -1, 124, 58, 126,
+ -1, -1, 62, 63, -1, -1, -1, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, -1, -1, -1, 124, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 124, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 124, 257, 258, -1, -1, -1,
+ -1, -1, -1, -1, -1, 257, 258, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258,
+};
+#define YYFINAL 7
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 259
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'%'","'&'",0,"'('","')'","'*'","'+'",0,"'-'","'.'",0,0,0,0,0,0,0,0,0,0,0,
+"':'",0,"'<'",0,"'>'","'?'","'@'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'|'",0,
+"'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,"TOKEN_LETTER","TOKEN_LITERAL","TOKEN_DIGIT",
+};
+char *yyrule[] = {
+"$accept : expr",
+"expr : optional_conditional",
+"conditional : alternative",
+"conditional : alternative '?' optional_conditional ':' conditional",
+"optional_conditional :",
+"optional_conditional : conditional",
+"alternative : list",
+"alternative : alternative '|' list",
+"alternative : alternative '&' list",
+"list : substitute",
+"list : list substitute",
+"substitute : string",
+"substitute : substitute '~' string",
+"string : '@'",
+"string : TOKEN_LITERAL",
+"string : TOKEN_LETTER",
+"string : TOKEN_LETTER number",
+"string : '%' TOKEN_LETTER",
+"string : '%' digits",
+"string : string '.' flag TOKEN_LETTER optional_number",
+"string : string '+' number",
+"string : string '-' number",
+"string : string '*'",
+"string : '(' optional_conditional ')'",
+"string : '<' optional_conditional '>'",
+"optional_number :",
+"optional_number : number",
+"number : TOKEN_DIGIT",
+"number : number TOKEN_DIGIT",
+"digits : TOKEN_DIGIT",
+"digits : digits TOKEN_DIGIT",
+"flag :",
+"flag : '+'",
+"flag : '-'",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 397 "label.y"
+
+/* bison defines const to be empty unless __STDC__ is defined, which it
+isn't under cfront */
+
+#ifdef const
+#undef const
+#endif
+
+const char *spec_ptr;
+const char *spec_end;
+const char *spec_cur;
+
+int yylex()
+{
+ while (spec_ptr < spec_end && csspace(*spec_ptr))
+ spec_ptr++;
+ spec_cur = spec_ptr;
+ if (spec_ptr >= spec_end)
+ return 0;
+ unsigned char c = *spec_ptr++;
+ if (csalpha(c)) {
+ yylval.num = c;
+ return TOKEN_LETTER;
+ }
+ if (csdigit(c)) {
+ yylval.num = c - '0';
+ return TOKEN_DIGIT;
+ }
+ if (c == '\'') {
+ yylval.str.start = literals.length();
+ for (; spec_ptr < spec_end; spec_ptr++) {
+ if (*spec_ptr == '\'') {
+ if (++spec_ptr < spec_end && *spec_ptr == '\'')
+ literals += '\'';
+ else {
+ yylval.str.len = literals.length() - yylval.str.start;
+ return TOKEN_LITERAL;
+ }
+ }
+ else
+ literals += *spec_ptr;
+ }
+ yylval.str.len = literals.length() - yylval.str.start;
+ return TOKEN_LITERAL;
+ }
+ return c;
+}
+
+int set_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_label;
+ parsed_label = parse_result;
+ return 1;
+}
+
+int set_date_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_date_label;
+ parsed_date_label = parse_result;
+ return 1;
+}
+
+int set_short_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_short_label;
+ parsed_short_label = parse_result;
+ return 1;
+}
+
+void yyerror(const char *message)
+{
+ if (spec_cur < spec_end)
+ command_error("label specification %1 before `%2'", message, spec_cur);
+ else
+ command_error("label specification %1 at end of string",
+ message, spec_cur);
+}
+
+void at_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (tentative)
+ ref.canonicalize_authors(result);
+ else {
+ const char *end, *start = ref.get_authors(&end);
+ if (start)
+ result.append(start, end - start);
+ }
+}
+
+void format_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (tentative)
+ return;
+ const label_info *lp = ref.get_label_ptr();
+ int num = lp == 0 ? ref.get_number() : lp->count;
+ if (type != '0')
+ result += format_serial(type, num + 1);
+ else {
+ const char *ptr = i_to_a(num + first_number);
+ int pad = width - strlen(ptr);
+ while (--pad >= 0)
+ result += '0';
+ result += ptr;
+ }
+}
+
+static const char *format_serial(char c, int n)
+{
+ assert(n > 0);
+ static char buf[128]; // more than enough.
+ switch (c) {
+ 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 = c == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
+ if (n >= 40000)
+ return i_to_a(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':
+ {
+ char *p = buf;
+ // this is derived from troff/reg.c
+ while (n > 0) {
+ int d = n % 26;
+ if (d == 0)
+ d = 26;
+ n -= d;
+ n /= 26;
+ *p++ = c + d - 1; // ASCII dependent
+ }
+ *p-- = 0;
+ // Reverse it.
+ char *q = buf;
+ while (q < p) {
+ char temp = *q;
+ *q = *p;
+ *p = temp;
+ --p;
+ ++q;
+ }
+ break;
+ }
+ default:
+ assert(0);
+ }
+ return buf;
+}
+
+void field_expr::evaluate(int, const reference &ref,
+ string &result, substring_position &)
+{
+ const char *end;
+ const char *start = ref.get_field(name, &end);
+ if (start) {
+ start = nth_field(number, start, &end);
+ if (start)
+ result.append(start, end - start);
+ }
+}
+
+void literal_expr::evaluate(int, const reference &,
+ string &result, substring_position &)
+{
+ result += s;
+}
+
+analyzed_expr::analyzed_expr(expression *e)
+: unary_expr(e), flags(e ? e->analyze() : 0)
+{
+}
+
+void analyzed_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ if (expr)
+ expr->evaluate(tentative, ref, result, pos);
+}
+
+void star_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ const label_info *lp = ref.get_label_ptr();
+ if (!tentative
+ && (lp == 0 || lp->total > 1)
+ && expr)
+ expr->evaluate(tentative, ref, result, pos);
+}
+
+void separator_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ int is_first = pos.start < 0;
+ if (expr)
+ expr->evaluate(tentative, ref, result, pos);
+ if (is_first) {
+ pos.start = start_length;
+ pos.length = result.length() - start_length;
+ }
+}
+
+void map_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ (*func)(temp.contents(), temp.contents() + temp.length(), result);
+ }
+}
+
+void extractor_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ const char *end, *start = (*func)(temp.contents(),
+ temp.contents() + temp.length(),
+ &end);
+ switch (part) {
+ case BEFORE:
+ if (start)
+ result.append(temp.contents(), start - temp.contents());
+ else
+ result += temp;
+ break;
+ case MATCH:
+ if (start)
+ result.append(start, end - start);
+ break;
+ case AFTER:
+ if (start)
+ result.append(end, temp.contents() + temp.length() - end);
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
+
+static void first_part(int len, const char *ptr, const char *end,
+ string &result)
+{
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ const token_info *ti = lookup_token(token_start, ptr);
+ int counts = ti->sortify_non_empty(token_start, ptr);
+ if (counts && --len < 0)
+ break;
+ if (counts || ti->is_accent())
+ result.append(token_start, ptr - token_start);
+ }
+}
+
+static void last_part(int len, const char *ptr, const char *end,
+ string &result)
+{
+ const char *start = ptr;
+ int count = 0;
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ const token_info *ti = lookup_token(token_start, ptr);
+ if (ti->sortify_non_empty(token_start, ptr))
+ count++;
+ }
+ ptr = start;
+ int skip = count - len;
+ if (skip > 0) {
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ assert(0);
+ const token_info *ti = lookup_token(token_start, ptr);
+ if (ti->sortify_non_empty(token_start, ptr) && --skip < 0) {
+ ptr = token_start;
+ break;
+ }
+ }
+ }
+ first_part(len, ptr, end, result);
+}
+
+void truncate_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ const char *start = temp.contents();
+ const char *end = start + temp.length();
+ if (n > 0)
+ first_part(n, start, end, result);
+ else if (n < 0)
+ last_part(-n, start, end, result);
+ }
+}
+
+void alternative_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (result.length() == start_length && expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+}
+
+void list_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+}
+
+void substitute_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (result.length() > start_length && result[result.length() - 1] == '-') {
+ // ought to see if pos covers the -
+ result.set_length(result.length() - 1);
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+ }
+}
+
+void conditional_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ string temp;
+ substring_position temp_pos;
+ if (expr1)
+ expr1->evaluate(tentative, ref, temp, temp_pos);
+ if (temp.length() > 0) {
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+ }
+ else {
+ if (expr3)
+ expr3->evaluate(tentative, ref, result, pos);
+ }
+}
+
+void reference::pre_compute_label()
+{
+ if (parsed_label != 0
+ && (parsed_label->analyze() & expression::CONTAINS_VARIABLE)) {
+ label.clear();
+ substring_position temp_pos;
+ parsed_label->evaluate(1, *this, label, temp_pos);
+ label_ptr = lookup_label(label);
+ }
+}
+
+void reference::compute_label()
+{
+ label.clear();
+ if (parsed_label)
+ parsed_label->evaluate(0, *this, label, separator_pos);
+ if (short_label_flag && parsed_short_label)
+ parsed_short_label->evaluate(0, *this, short_label, short_separator_pos);
+ if (date_as_label) {
+ string new_date;
+ if (parsed_date_label) {
+ substring_position temp_pos;
+ parsed_date_label->evaluate(0, *this, new_date, temp_pos);
+ }
+ set_date(new_date);
+ }
+ if (label_ptr)
+ label_ptr->count += 1;
+}
+
+void reference::immediate_compute_label()
+{
+ if (label_ptr)
+ label_ptr->total = 2; // force use of disambiguator
+ compute_label();
+}
+
+int reference::merge_labels(reference **v, int n, label_type type,
+ string &result)
+{
+ if (abbreviate_label_ranges)
+ return merge_labels_by_number(v, n, type, result);
+ else
+ return merge_labels_by_parts(v, n, type, result);
+}
+
+int reference::merge_labels_by_number(reference **v, int n, label_type type,
+ string &result)
+{
+ if (n <= 1)
+ return 0;
+ int num = get_number();
+ // Only merge three or more labels.
+ if (v[0]->get_number() != num + 1
+ || v[1]->get_number() != num + 2)
+ return 0;
+ int i;
+ for (i = 2; i < n; i++)
+ if (v[i]->get_number() != num + i + 1)
+ break;
+ result = get_label(type);
+ result += label_range_indicator;
+ result += v[i - 1]->get_label(type);
+ return i;
+}
+
+const substring_position &reference::get_separator_pos(label_type type) const
+{
+ if (type == SHORT_LABEL && short_label_flag)
+ return short_separator_pos;
+ else
+ return separator_pos;
+}
+
+const string &reference::get_label(label_type type) const
+{
+ if (type == SHORT_LABEL && short_label_flag)
+ return short_label;
+ else
+ return label;
+}
+
+int reference::merge_labels_by_parts(reference **v, int n, label_type type,
+ string &result)
+{
+ if (n <= 0)
+ return 0;
+ const string &lb = get_label(type);
+ const substring_position &sp = get_separator_pos(type);
+ if (sp.start < 0
+ || sp.start != v[0]->get_separator_pos(type).start
+ || memcmp(lb.contents(), v[0]->get_label(type).contents(),
+ sp.start) != 0)
+ return 0;
+ result = lb;
+ int i = 0;
+ do {
+ result += separate_label_second_parts;
+ const substring_position &s = v[i]->get_separator_pos(type);
+ int sep_end_pos = s.start + s.length;
+ result.append(v[i]->get_label(type).contents() + sep_end_pos,
+ v[i]->get_label(type).length() - sep_end_pos);
+ } while (++i < n
+ && sp.start == v[i]->get_separator_pos(type).start
+ && memcmp(lb.contents(), v[i]->get_label(type).contents(),
+ sp.start) == 0);
+ return i;
+}
+
+string label_pool;
+
+label_info::label_info(const string &s)
+: start(label_pool.length()), length(s.length()), count(0), total(1)
+{
+ label_pool += s;
+}
+
+static label_info **label_table = 0;
+static int label_table_size = 0;
+static int label_table_used = 0;
+
+label_info *lookup_label(const string &label)
+{
+ if (label_table == 0) {
+ label_table = new label_info *[17];
+ label_table_size = 17;
+ for (int i = 0; i < 17; i++)
+ label_table[i] = 0;
+ }
+ unsigned h = hash_string(label.contents(), label.length()) % label_table_size;
+ label_info **ptr;
+ for (ptr = label_table + h;
+ *ptr != 0;
+ (ptr == label_table)
+ ? (ptr = label_table + label_table_size - 1)
+ : ptr--)
+ if ((*ptr)->length == label.length()
+ && memcmp(label_pool.contents() + (*ptr)->start, label.contents(),
+ label.length()) == 0) {
+ (*ptr)->total += 1;
+ return *ptr;
+ }
+ label_info *result = *ptr = new label_info(label);
+ if (++label_table_used * 2 > label_table_size) {
+ // Rehash the table.
+ label_info **old_table = label_table;
+ int old_size = label_table_size;
+ label_table_size = next_size(label_table_size);
+ label_table = new label_info *[label_table_size];
+ int i;
+ for (i = 0; i < label_table_size; i++)
+ label_table[i] = 0;
+ for (i = 0; i < old_size; i++)
+ if (old_table[i]) {
+ unsigned h = hash_string(label_pool.contents() + old_table[i]->start,
+ old_table[i]->length);
+ label_info **p;
+ for (p = label_table + (h % label_table_size);
+ *p != 0;
+ (p == label_table)
+ ? (p = label_table + label_table_size - 1)
+ : --p)
+ ;
+ *p = old_table[i];
+ }
+ a_delete old_table;
+ }
+ return result;
+}
+
+void clear_labels()
+{
+ for (int i = 0; i < label_table_size; i++) {
+ delete label_table[i];
+ label_table[i] = 0;
+ }
+ label_table_used = 0;
+ label_pool.clear();
+}
+
+static void consider_authors(reference **start, reference **end, int i);
+
+void compute_labels(reference **v, int n)
+{
+ if (parsed_label
+ && (parsed_label->analyze() & expression::CONTAINS_AT)
+ && sort_fields.length() >= 2
+ && sort_fields[0] == 'A'
+ && sort_fields[1] == '+')
+ consider_authors(v, v + n, 0);
+ for (int i = 0; i < n; i++)
+ v[i]->compute_label();
+}
+
+
+/* A reference with a list of authors <A0,A1,...,AN> _needs_ author i
+where 0 <= i <= N if there exists a reference with a list of authors
+<B0,B1,...,BM> such that <A0,A1,...,AN> != <B0,B1,...,BM> and M >= i
+and Aj = Bj for 0 <= j < i. In this case if we can't say ``A0,
+A1,...,A(i-1) et al'' because this would match both <A0,A1,...,AN> and
+<B0,B1,...,BM>. If a reference needs author i we only have to call
+need_author(j) for some j >= i such that the reference also needs
+author j. */
+
+/* This function handles 2 tasks:
+determine which authors are needed (cannot be elided with et al.);
+determine which authors can have only last names in the labels.
+
+References >= start and < end have the same first i author names.
+Also they're sorted by A+. */
+
+static void consider_authors(reference **start, reference **end, int i)
+{
+ if (start >= end)
+ return;
+ reference **p = start;
+ if (i >= (*p)->get_nauthors()) {
+ for (++p; p < end && i >= (*p)->get_nauthors(); p++)
+ ;
+ if (p < end && i > 0) {
+ // If we have an author list <A B C> and an author list <A B C D>,
+ // then both lists need C.
+ for (reference **q = start; q < end; q++)
+ (*q)->need_author(i - 1);
+ }
+ start = p;
+ }
+ while (p < end) {
+ reference **last_name_start = p;
+ reference **name_start = p;
+ for (++p;
+ p < end && i < (*p)->get_nauthors()
+ && same_author_last_name(**last_name_start, **p, i);
+ p++) {
+ if (!same_author_name(**name_start, **p, i)) {
+ consider_authors(name_start, p, i + 1);
+ name_start = p;
+ }
+ }
+ consider_authors(name_start, p, i + 1);
+ if (last_name_start == name_start) {
+ for (reference **q = last_name_start; q < p; q++)
+ (*q)->set_last_name_unambiguous(i);
+ }
+ // If we have an author list <A B C D> and <A B C E>, then the lists
+ // need author D and E respectively.
+ if (name_start > start || p < end) {
+ for (reference **q = last_name_start; q < p; q++)
+ (*q)->need_author(i);
+ }
+ }
+}
+
+int same_author_last_name(const reference &r1, const reference &r2, int n)
+{
+ const char *ae1;
+ const char *as1 = r1.get_sort_field(0, n, 0, &ae1);
+ assert(as1 != 0);
+ const char *ae2;
+ const char *as2 = r2.get_sort_field(0, n, 0, &ae2);
+ assert(as2 != 0);
+ return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
+}
+
+int same_author_name(const reference &r1, const reference &r2, int n)
+{
+ const char *ae1;
+ const char *as1 = r1.get_sort_field(0, n, -1, &ae1);
+ assert(as1 != 0);
+ const char *ae2;
+ const char *as2 = r2.get_sort_field(0, n, -1, &ae2);
+ assert(as2 != 0);
+ return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
+}
+
+
+void int_set::set(int i)
+{
+ assert(i >= 0);
+ int bytei = i >> 3;
+ if (bytei >= v.length()) {
+ int old_length = v.length();
+ v.set_length(bytei + 1);
+ for (int j = old_length; j <= bytei; j++)
+ v[j] = 0;
+ }
+ v[bytei] |= 1 << (i & 7);
+}
+
+int int_set::get(int i) const
+{
+ assert(i >= 0);
+ int bytei = i >> 3;
+ return bytei >= v.length() ? 0 : (v[bytei] & (1 << (i & 7))) != 0;
+}
+
+void reference::set_last_name_unambiguous(int i)
+{
+ last_name_unambiguous.set(i);
+}
+
+void reference::need_author(int n)
+{
+ if (n > last_needed_author)
+ last_needed_author = n;
+}
+
+const char *reference::get_authors(const char **end) const
+{
+ if (!computed_authors) {
+ ((reference *)this)->computed_authors = 1;
+ string &result = ((reference *)this)->authors;
+ int na = get_nauthors();
+ result.clear();
+ for (int i = 0; i < na; i++) {
+ if (last_name_unambiguous.get(i)) {
+ const char *e, *start = get_author_last_name(i, &e);
+ assert(start != 0);
+ result.append(start, e - start);
+ }
+ else {
+ const char *e, *start = get_author(i, &e);
+ assert(start != 0);
+ result.append(start, e - start);
+ }
+ if (i == last_needed_author
+ && et_al.length() > 0
+ && et_al_min_elide > 0
+ && last_needed_author + et_al_min_elide < na
+ && na >= et_al_min_total) {
+ result += et_al;
+ break;
+ }
+ if (i < na - 1) {
+ if (na == 2)
+ result += join_authors_exactly_two;
+ else if (i < na - 2)
+ result += join_authors_default;
+ else
+ result += join_authors_last_two;
+ }
+ }
+ }
+ const char *start = authors.contents();
+ *end = start + authors.length();
+ return start;
+}
+
+int reference::get_nauthors() const
+{
+ if (nauthors < 0) {
+ const char *dummy;
+ int na;
+ for (na = 0; get_author(na, &dummy) != 0; na++)
+ ;
+ ((reference *)this)->nauthors = na;
+ }
+ return nauthors;
+}
+#line 1228 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+#if defined(__STDC__)
+yyparse(void)
+#else
+yyparse()
+#endif
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 1:
+#line 250 "label.y"
+{ parse_result = (yyvsp[0].expr ? new analyzed_expr(yyvsp[0].expr) : 0); }
+break;
+case 2:
+#line 255 "label.y"
+{ yyval.expr = yyvsp[0].expr; }
+break;
+case 3:
+#line 257 "label.y"
+{ yyval.expr = new conditional_expr(yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); }
+break;
+case 4:
+#line 262 "label.y"
+{ yyval.expr = 0; }
+break;
+case 5:
+#line 264 "label.y"
+{ yyval.expr = yyvsp[0].expr; }
+break;
+case 6:
+#line 269 "label.y"
+{ yyval.expr = yyvsp[0].expr; }
+break;
+case 7:
+#line 271 "label.y"
+{ yyval.expr = new alternative_expr(yyvsp[-2].expr, yyvsp[0].expr); }
+break;
+case 8:
+#line 273 "label.y"
+{ yyval.expr = new conditional_expr(yyvsp[-2].expr, yyvsp[0].expr, 0); }
+break;
+case 9:
+#line 278 "label.y"
+{ yyval.expr = yyvsp[0].expr; }
+break;
+case 10:
+#line 280 "label.y"
+{ yyval.expr = new list_expr(yyvsp[-1].expr, yyvsp[0].expr); }
+break;
+case 11:
+#line 285 "label.y"
+{ yyval.expr = yyvsp[0].expr; }
+break;
+case 12:
+#line 287 "label.y"
+{ yyval.expr = new substitute_expr(yyvsp[-2].expr, yyvsp[0].expr); }
+break;
+case 13:
+#line 292 "label.y"
+{ yyval.expr = new at_expr; }
+break;
+case 14:
+#line 294 "label.y"
+{
+ yyval.expr = new literal_expr(literals.contents() + yyvsp[0].str.start,
+ yyvsp[0].str.len);
+ }
+break;
+case 15:
+#line 299 "label.y"
+{ yyval.expr = new field_expr(yyvsp[0].num, 0); }
+break;
+case 16:
+#line 301 "label.y"
+{ yyval.expr = new field_expr(yyvsp[-1].num, yyvsp[0].num - 1); }
+break;
+case 17:
+#line 303 "label.y"
+{
+ switch (yyvsp[0].num) {
+ case 'I':
+ case 'i':
+ case 'A':
+ case 'a':
+ yyval.expr = new format_expr(yyvsp[0].num);
+ break;
+ default:
+ command_error("unrecognized format `%1'", char(yyvsp[0].num));
+ yyval.expr = new format_expr('a');
+ break;
+ }
+ }
+break;
+case 18:
+#line 319 "label.y"
+{
+ yyval.expr = new format_expr('0', yyvsp[0].dig.ndigits, yyvsp[0].dig.val);
+ }
+break;
+case 19:
+#line 323 "label.y"
+{
+ switch (yyvsp[-1].num) {
+ case 'l':
+ yyval.expr = new map_expr(yyvsp[-4].expr, lowercase);
+ break;
+ case 'u':
+ yyval.expr = new map_expr(yyvsp[-4].expr, uppercase);
+ break;
+ case 'c':
+ yyval.expr = new map_expr(yyvsp[-4].expr, capitalize);
+ break;
+ case 'r':
+ yyval.expr = new map_expr(yyvsp[-4].expr, reverse_name);
+ break;
+ case 'a':
+ yyval.expr = new map_expr(yyvsp[-4].expr, abbreviate_name);
+ break;
+ case 'y':
+ yyval.expr = new extractor_expr(yyvsp[-4].expr, find_year, yyvsp[-2].num);
+ break;
+ case 'n':
+ yyval.expr = new extractor_expr(yyvsp[-4].expr, find_last_name, yyvsp[-2].num);
+ break;
+ default:
+ yyval.expr = yyvsp[-4].expr;
+ command_error("unknown function `%1'", char(yyvsp[-1].num));
+ break;
+ }
+ }
+break;
+case 20:
+#line 354 "label.y"
+{ yyval.expr = new truncate_expr(yyvsp[-2].expr, yyvsp[0].num); }
+break;
+case 21:
+#line 356 "label.y"
+{ yyval.expr = new truncate_expr(yyvsp[-2].expr, -yyvsp[0].num); }
+break;
+case 22:
+#line 358 "label.y"
+{ yyval.expr = new star_expr(yyvsp[-1].expr); }
+break;
+case 23:
+#line 360 "label.y"
+{ yyval.expr = yyvsp[-1].expr; }
+break;
+case 24:
+#line 362 "label.y"
+{ yyval.expr = new separator_expr(yyvsp[-1].expr); }
+break;
+case 25:
+#line 367 "label.y"
+{ yyval.num = -1; }
+break;
+case 26:
+#line 369 "label.y"
+{ yyval.num = yyvsp[0].num; }
+break;
+case 27:
+#line 374 "label.y"
+{ yyval.num = yyvsp[0].num; }
+break;
+case 28:
+#line 376 "label.y"
+{ yyval.num = yyvsp[-1].num*10 + yyvsp[0].num; }
+break;
+case 29:
+#line 381 "label.y"
+{ yyval.dig.ndigits = 1; yyval.dig.val = yyvsp[0].num; }
+break;
+case 30:
+#line 383 "label.y"
+{ yyval.dig.ndigits = yyvsp[-1].dig.ndigits + 1; yyval.dig.val = yyvsp[-1].dig.val*10 + yyvsp[0].num; }
+break;
+case 31:
+#line 389 "label.y"
+{ yyval.num = 0; }
+break;
+case 32:
+#line 391 "label.y"
+{ yyval.num = 1; }
+break;
+case 33:
+#line 393 "label.y"
+{ yyval.num = -1; }
+break;
+#line 1547 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/contrib/groff/src/preproc/refer/label.y b/contrib/groff/src/preproc/refer/label.y
new file mode 100644
index 000000000000..2648b98552dc
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/label.y
@@ -0,0 +1,1177 @@
+/* -*- 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. */
+
+%{
+
+#include "refer.h"
+#include "refid.h"
+#include "ref.h"
+#include "token.h"
+
+int yylex();
+void yyerror(const char *);
+int yyparse();
+
+static const char *format_serial(char c, int n);
+
+struct label_info {
+ int start;
+ int length;
+ int count;
+ int total;
+ label_info(const string &);
+};
+
+label_info *lookup_label(const string &label);
+
+struct expression {
+ enum {
+ // Does the tentative label depend on the reference?
+ CONTAINS_VARIABLE = 01,
+ CONTAINS_STAR = 02,
+ CONTAINS_FORMAT = 04,
+ CONTAINS_AT = 010
+ };
+ virtual ~expression() { }
+ virtual void evaluate(int, const reference &, string &,
+ substring_position &) = 0;
+ virtual unsigned analyze() { return 0; }
+};
+
+class at_expr : public expression {
+public:
+ at_expr() { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_VARIABLE|CONTAINS_AT; }
+};
+
+class format_expr : public expression {
+ char type;
+ int width;
+ int first_number;
+public:
+ format_expr(char c, int w = 0, int f = 1)
+ : type(c), width(w), first_number(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_FORMAT; }
+};
+
+class field_expr : public expression {
+ int number;
+ char name;
+public:
+ field_expr(char nm, int num) : number(num), name(nm) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return CONTAINS_VARIABLE; }
+};
+
+class literal_expr : public expression {
+ string s;
+public:
+ literal_expr(const char *ptr, int len) : s(ptr, len) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class unary_expr : public expression {
+protected:
+ expression *expr;
+public:
+ unary_expr(expression *e) : expr(e) { }
+ ~unary_expr() { delete expr; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() { return expr ? expr->analyze() : 0; }
+};
+
+// This caches the analysis of an expression.
+
+class analyzed_expr : public unary_expr {
+ unsigned flags;
+public:
+ analyzed_expr(expression *);
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() { return flags; }
+};
+
+class star_expr : public unary_expr {
+public:
+ star_expr(expression *e) : unary_expr(e) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+ unsigned analyze() {
+ return ((expr ? (expr->analyze() & ~CONTAINS_VARIABLE) : 0)
+ | CONTAINS_STAR);
+ }
+};
+
+typedef void map_func(const char *, const char *, string &);
+
+class map_expr : public unary_expr {
+ map_func *func;
+public:
+ map_expr(expression *e, map_func *f) : unary_expr(e), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+typedef const char *extractor_func(const char *, const char *, const char **);
+
+class extractor_expr : public unary_expr {
+ int part;
+ extractor_func *func;
+public:
+ enum { BEFORE = +1, MATCH = 0, AFTER = -1 };
+ extractor_expr(expression *e, extractor_func *f, int pt)
+ : unary_expr(e), part(pt), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class truncate_expr : public unary_expr {
+ int n;
+public:
+ truncate_expr(expression *e, int i) : unary_expr(e), n(i) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class separator_expr : public unary_expr {
+public:
+ separator_expr(expression *e) : unary_expr(e) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class binary_expr : public expression {
+protected:
+ expression *expr1;
+ expression *expr2;
+public:
+ binary_expr(expression *e1, expression *e2) : expr1(e1), expr2(e2) { }
+ ~binary_expr() { delete expr1; delete expr2; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() {
+ return (expr1 ? expr1->analyze() : 0) | (expr2 ? expr2->analyze() : 0);
+ }
+};
+
+class alternative_expr : public binary_expr {
+public:
+ alternative_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class list_expr : public binary_expr {
+public:
+ list_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class substitute_expr : public binary_expr {
+public:
+ substitute_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class ternary_expr : public expression {
+protected:
+ expression *expr1;
+ expression *expr2;
+ expression *expr3;
+public:
+ ternary_expr(expression *e1, expression *e2, expression *e3)
+ : expr1(e1), expr2(e2), expr3(e3) { }
+ ~ternary_expr() { delete expr1; delete expr2; delete expr3; }
+ void evaluate(int, const reference &, string &, substring_position &) = 0;
+ unsigned analyze() {
+ return ((expr1 ? expr1->analyze() : 0)
+ | (expr2 ? expr2->analyze() : 0)
+ | (expr3 ? expr3->analyze() : 0));
+ }
+};
+
+class conditional_expr : public ternary_expr {
+public:
+ conditional_expr(expression *e1, expression *e2, expression *e3)
+ : ternary_expr(e1, e2, e3) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+static expression *parsed_label = 0;
+static expression *parsed_date_label = 0;
+static expression *parsed_short_label = 0;
+
+static expression *parse_result;
+
+string literals;
+
+%}
+
+%union {
+ int num;
+ expression *expr;
+ struct { int ndigits; int val; } dig;
+ struct { int start; int len; } str;
+}
+
+/* uppercase or lowercase letter */
+%token <num> TOKEN_LETTER
+/* literal characters */
+%token <str> TOKEN_LITERAL
+/* digit */
+%token <num> TOKEN_DIGIT
+
+%type <expr> conditional
+%type <expr> alternative
+%type <expr> list
+%type <expr> string
+%type <expr> substitute
+%type <expr> optional_conditional
+%type <num> number
+%type <dig> digits
+%type <num> optional_number
+%type <num> flag
+
+%%
+
+expr:
+ optional_conditional
+ { parse_result = ($1 ? new analyzed_expr($1) : 0); }
+ ;
+
+conditional:
+ alternative
+ { $$ = $1; }
+ | alternative '?' optional_conditional ':' conditional
+ { $$ = new conditional_expr($1, $3, $5); }
+ ;
+
+optional_conditional:
+ /* empty */
+ { $$ = 0; }
+ | conditional
+ { $$ = $1; }
+ ;
+
+alternative:
+ list
+ { $$ = $1; }
+ | alternative '|' list
+ { $$ = new alternative_expr($1, $3); }
+ | alternative '&' list
+ { $$ = new conditional_expr($1, $3, 0); }
+ ;
+
+list:
+ substitute
+ { $$ = $1; }
+ | list substitute
+ { $$ = new list_expr($1, $2); }
+ ;
+
+substitute:
+ string
+ { $$ = $1; }
+ | substitute '~' string
+ { $$ = new substitute_expr($1, $3); }
+ ;
+
+string:
+ '@'
+ { $$ = new at_expr; }
+ | TOKEN_LITERAL
+ {
+ $$ = new literal_expr(literals.contents() + $1.start,
+ $1.len);
+ }
+ | TOKEN_LETTER
+ { $$ = new field_expr($1, 0); }
+ | TOKEN_LETTER number
+ { $$ = new field_expr($1, $2 - 1); }
+ | '%' TOKEN_LETTER
+ {
+ switch ($2) {
+ case 'I':
+ case 'i':
+ case 'A':
+ case 'a':
+ $$ = new format_expr($2);
+ break;
+ default:
+ command_error("unrecognized format `%1'", char($2));
+ $$ = new format_expr('a');
+ break;
+ }
+ }
+
+ | '%' digits
+ {
+ $$ = new format_expr('0', $2.ndigits, $2.val);
+ }
+ | string '.' flag TOKEN_LETTER optional_number
+ {
+ switch ($4) {
+ case 'l':
+ $$ = new map_expr($1, lowercase);
+ break;
+ case 'u':
+ $$ = new map_expr($1, uppercase);
+ break;
+ case 'c':
+ $$ = new map_expr($1, capitalize);
+ break;
+ case 'r':
+ $$ = new map_expr($1, reverse_name);
+ break;
+ case 'a':
+ $$ = new map_expr($1, abbreviate_name);
+ break;
+ case 'y':
+ $$ = new extractor_expr($1, find_year, $3);
+ break;
+ case 'n':
+ $$ = new extractor_expr($1, find_last_name, $3);
+ break;
+ default:
+ $$ = $1;
+ command_error("unknown function `%1'", char($4));
+ break;
+ }
+ }
+
+ | string '+' number
+ { $$ = new truncate_expr($1, $3); }
+ | string '-' number
+ { $$ = new truncate_expr($1, -$3); }
+ | string '*'
+ { $$ = new star_expr($1); }
+ | '(' optional_conditional ')'
+ { $$ = $2; }
+ | '<' optional_conditional '>'
+ { $$ = new separator_expr($2); }
+ ;
+
+optional_number:
+ /* empty */
+ { $$ = -1; }
+ | number
+ { $$ = $1; }
+ ;
+
+number:
+ TOKEN_DIGIT
+ { $$ = $1; }
+ | number TOKEN_DIGIT
+ { $$ = $1*10 + $2; }
+ ;
+
+digits:
+ TOKEN_DIGIT
+ { $$.ndigits = 1; $$.val = $1; }
+ | digits TOKEN_DIGIT
+ { $$.ndigits = $1.ndigits + 1; $$.val = $1.val*10 + $2; }
+ ;
+
+
+flag:
+ /* empty */
+ { $$ = 0; }
+ | '+'
+ { $$ = 1; }
+ | '-'
+ { $$ = -1; }
+ ;
+
+%%
+
+/* bison defines const to be empty unless __STDC__ is defined, which it
+isn't under cfront */
+
+#ifdef const
+#undef const
+#endif
+
+const char *spec_ptr;
+const char *spec_end;
+const char *spec_cur;
+
+int yylex()
+{
+ while (spec_ptr < spec_end && csspace(*spec_ptr))
+ spec_ptr++;
+ spec_cur = spec_ptr;
+ if (spec_ptr >= spec_end)
+ return 0;
+ unsigned char c = *spec_ptr++;
+ if (csalpha(c)) {
+ yylval.num = c;
+ return TOKEN_LETTER;
+ }
+ if (csdigit(c)) {
+ yylval.num = c - '0';
+ return TOKEN_DIGIT;
+ }
+ if (c == '\'') {
+ yylval.str.start = literals.length();
+ for (; spec_ptr < spec_end; spec_ptr++) {
+ if (*spec_ptr == '\'') {
+ if (++spec_ptr < spec_end && *spec_ptr == '\'')
+ literals += '\'';
+ else {
+ yylval.str.len = literals.length() - yylval.str.start;
+ return TOKEN_LITERAL;
+ }
+ }
+ else
+ literals += *spec_ptr;
+ }
+ yylval.str.len = literals.length() - yylval.str.start;
+ return TOKEN_LITERAL;
+ }
+ return c;
+}
+
+int set_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_label;
+ parsed_label = parse_result;
+ return 1;
+}
+
+int set_date_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_date_label;
+ parsed_date_label = parse_result;
+ return 1;
+}
+
+int set_short_label_spec(const char *label_spec)
+{
+ spec_cur = spec_ptr = label_spec;
+ spec_end = strchr(label_spec, '\0');
+ literals.clear();
+ if (yyparse())
+ return 0;
+ delete parsed_short_label;
+ parsed_short_label = parse_result;
+ return 1;
+}
+
+void yyerror(const char *message)
+{
+ if (spec_cur < spec_end)
+ command_error("label specification %1 before `%2'", message, spec_cur);
+ else
+ command_error("label specification %1 at end of string",
+ message, spec_cur);
+}
+
+void at_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (tentative)
+ ref.canonicalize_authors(result);
+ else {
+ const char *end, *start = ref.get_authors(&end);
+ if (start)
+ result.append(start, end - start);
+ }
+}
+
+void format_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (tentative)
+ return;
+ const label_info *lp = ref.get_label_ptr();
+ int num = lp == 0 ? ref.get_number() : lp->count;
+ if (type != '0')
+ result += format_serial(type, num + 1);
+ else {
+ const char *ptr = i_to_a(num + first_number);
+ int pad = width - strlen(ptr);
+ while (--pad >= 0)
+ result += '0';
+ result += ptr;
+ }
+}
+
+static const char *format_serial(char c, int n)
+{
+ assert(n > 0);
+ static char buf[128]; // more than enough.
+ switch (c) {
+ 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 = c == 'i' ? "zwmdclxvi" : "ZWMDCLXVI";
+ if (n >= 40000)
+ return i_to_a(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':
+ {
+ char *p = buf;
+ // this is derived from troff/reg.c
+ while (n > 0) {
+ int d = n % 26;
+ if (d == 0)
+ d = 26;
+ n -= d;
+ n /= 26;
+ *p++ = c + d - 1; // ASCII dependent
+ }
+ *p-- = 0;
+ // Reverse it.
+ char *q = buf;
+ while (q < p) {
+ char temp = *q;
+ *q = *p;
+ *p = temp;
+ --p;
+ ++q;
+ }
+ break;
+ }
+ default:
+ assert(0);
+ }
+ return buf;
+}
+
+void field_expr::evaluate(int, const reference &ref,
+ string &result, substring_position &)
+{
+ const char *end;
+ const char *start = ref.get_field(name, &end);
+ if (start) {
+ start = nth_field(number, start, &end);
+ if (start)
+ result.append(start, end - start);
+ }
+}
+
+void literal_expr::evaluate(int, const reference &,
+ string &result, substring_position &)
+{
+ result += s;
+}
+
+analyzed_expr::analyzed_expr(expression *e)
+: unary_expr(e), flags(e ? e->analyze() : 0)
+{
+}
+
+void analyzed_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ if (expr)
+ expr->evaluate(tentative, ref, result, pos);
+}
+
+void star_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ const label_info *lp = ref.get_label_ptr();
+ if (!tentative
+ && (lp == 0 || lp->total > 1)
+ && expr)
+ expr->evaluate(tentative, ref, result, pos);
+}
+
+void separator_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ int is_first = pos.start < 0;
+ if (expr)
+ expr->evaluate(tentative, ref, result, pos);
+ if (is_first) {
+ pos.start = start_length;
+ pos.length = result.length() - start_length;
+ }
+}
+
+void map_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ (*func)(temp.contents(), temp.contents() + temp.length(), result);
+ }
+}
+
+void extractor_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ const char *end, *start = (*func)(temp.contents(),
+ temp.contents() + temp.length(),
+ &end);
+ switch (part) {
+ case BEFORE:
+ if (start)
+ result.append(temp.contents(), start - temp.contents());
+ else
+ result += temp;
+ break;
+ case MATCH:
+ if (start)
+ result.append(start, end - start);
+ break;
+ case AFTER:
+ if (start)
+ result.append(end, temp.contents() + temp.length() - end);
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
+
+static void first_part(int len, const char *ptr, const char *end,
+ string &result)
+{
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ const token_info *ti = lookup_token(token_start, ptr);
+ int counts = ti->sortify_non_empty(token_start, ptr);
+ if (counts && --len < 0)
+ break;
+ if (counts || ti->is_accent())
+ result.append(token_start, ptr - token_start);
+ }
+}
+
+static void last_part(int len, const char *ptr, const char *end,
+ string &result)
+{
+ const char *start = ptr;
+ int count = 0;
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ const token_info *ti = lookup_token(token_start, ptr);
+ if (ti->sortify_non_empty(token_start, ptr))
+ count++;
+ }
+ ptr = start;
+ int skip = count - len;
+ if (skip > 0) {
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ assert(0);
+ const token_info *ti = lookup_token(token_start, ptr);
+ if (ti->sortify_non_empty(token_start, ptr) && --skip < 0) {
+ ptr = token_start;
+ break;
+ }
+ }
+ }
+ first_part(len, ptr, end, result);
+}
+
+void truncate_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &)
+{
+ if (expr) {
+ string temp;
+ substring_position temp_pos;
+ expr->evaluate(tentative, ref, temp, temp_pos);
+ const char *start = temp.contents();
+ const char *end = start + temp.length();
+ if (n > 0)
+ first_part(n, start, end, result);
+ else if (n < 0)
+ last_part(-n, start, end, result);
+ }
+}
+
+void alternative_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (result.length() == start_length && expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+}
+
+void list_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+}
+
+void substitute_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ int start_length = result.length();
+ if (expr1)
+ expr1->evaluate(tentative, ref, result, pos);
+ if (result.length() > start_length && result[result.length() - 1] == '-') {
+ // ought to see if pos covers the -
+ result.set_length(result.length() - 1);
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+ }
+}
+
+void conditional_expr::evaluate(int tentative, const reference &ref,
+ string &result, substring_position &pos)
+{
+ string temp;
+ substring_position temp_pos;
+ if (expr1)
+ expr1->evaluate(tentative, ref, temp, temp_pos);
+ if (temp.length() > 0) {
+ if (expr2)
+ expr2->evaluate(tentative, ref, result, pos);
+ }
+ else {
+ if (expr3)
+ expr3->evaluate(tentative, ref, result, pos);
+ }
+}
+
+void reference::pre_compute_label()
+{
+ if (parsed_label != 0
+ && (parsed_label->analyze() & expression::CONTAINS_VARIABLE)) {
+ label.clear();
+ substring_position temp_pos;
+ parsed_label->evaluate(1, *this, label, temp_pos);
+ label_ptr = lookup_label(label);
+ }
+}
+
+void reference::compute_label()
+{
+ label.clear();
+ if (parsed_label)
+ parsed_label->evaluate(0, *this, label, separator_pos);
+ if (short_label_flag && parsed_short_label)
+ parsed_short_label->evaluate(0, *this, short_label, short_separator_pos);
+ if (date_as_label) {
+ string new_date;
+ if (parsed_date_label) {
+ substring_position temp_pos;
+ parsed_date_label->evaluate(0, *this, new_date, temp_pos);
+ }
+ set_date(new_date);
+ }
+ if (label_ptr)
+ label_ptr->count += 1;
+}
+
+void reference::immediate_compute_label()
+{
+ if (label_ptr)
+ label_ptr->total = 2; // force use of disambiguator
+ compute_label();
+}
+
+int reference::merge_labels(reference **v, int n, label_type type,
+ string &result)
+{
+ if (abbreviate_label_ranges)
+ return merge_labels_by_number(v, n, type, result);
+ else
+ return merge_labels_by_parts(v, n, type, result);
+}
+
+int reference::merge_labels_by_number(reference **v, int n, label_type type,
+ string &result)
+{
+ if (n <= 1)
+ return 0;
+ int num = get_number();
+ // Only merge three or more labels.
+ if (v[0]->get_number() != num + 1
+ || v[1]->get_number() != num + 2)
+ return 0;
+ int i;
+ for (i = 2; i < n; i++)
+ if (v[i]->get_number() != num + i + 1)
+ break;
+ result = get_label(type);
+ result += label_range_indicator;
+ result += v[i - 1]->get_label(type);
+ return i;
+}
+
+const substring_position &reference::get_separator_pos(label_type type) const
+{
+ if (type == SHORT_LABEL && short_label_flag)
+ return short_separator_pos;
+ else
+ return separator_pos;
+}
+
+const string &reference::get_label(label_type type) const
+{
+ if (type == SHORT_LABEL && short_label_flag)
+ return short_label;
+ else
+ return label;
+}
+
+int reference::merge_labels_by_parts(reference **v, int n, label_type type,
+ string &result)
+{
+ if (n <= 0)
+ return 0;
+ const string &lb = get_label(type);
+ const substring_position &sp = get_separator_pos(type);
+ if (sp.start < 0
+ || sp.start != v[0]->get_separator_pos(type).start
+ || memcmp(lb.contents(), v[0]->get_label(type).contents(),
+ sp.start) != 0)
+ return 0;
+ result = lb;
+ int i = 0;
+ do {
+ result += separate_label_second_parts;
+ const substring_position &s = v[i]->get_separator_pos(type);
+ int sep_end_pos = s.start + s.length;
+ result.append(v[i]->get_label(type).contents() + sep_end_pos,
+ v[i]->get_label(type).length() - sep_end_pos);
+ } while (++i < n
+ && sp.start == v[i]->get_separator_pos(type).start
+ && memcmp(lb.contents(), v[i]->get_label(type).contents(),
+ sp.start) == 0);
+ return i;
+}
+
+string label_pool;
+
+label_info::label_info(const string &s)
+: start(label_pool.length()), length(s.length()), count(0), total(1)
+{
+ label_pool += s;
+}
+
+static label_info **label_table = 0;
+static int label_table_size = 0;
+static int label_table_used = 0;
+
+label_info *lookup_label(const string &label)
+{
+ if (label_table == 0) {
+ label_table = new label_info *[17];
+ label_table_size = 17;
+ for (int i = 0; i < 17; i++)
+ label_table[i] = 0;
+ }
+ unsigned h = hash_string(label.contents(), label.length()) % label_table_size;
+ label_info **ptr;
+ for (ptr = label_table + h;
+ *ptr != 0;
+ (ptr == label_table)
+ ? (ptr = label_table + label_table_size - 1)
+ : ptr--)
+ if ((*ptr)->length == label.length()
+ && memcmp(label_pool.contents() + (*ptr)->start, label.contents(),
+ label.length()) == 0) {
+ (*ptr)->total += 1;
+ return *ptr;
+ }
+ label_info *result = *ptr = new label_info(label);
+ if (++label_table_used * 2 > label_table_size) {
+ // Rehash the table.
+ label_info **old_table = label_table;
+ int old_size = label_table_size;
+ label_table_size = next_size(label_table_size);
+ label_table = new label_info *[label_table_size];
+ int i;
+ for (i = 0; i < label_table_size; i++)
+ label_table[i] = 0;
+ for (i = 0; i < old_size; i++)
+ if (old_table[i]) {
+ unsigned h = hash_string(label_pool.contents() + old_table[i]->start,
+ old_table[i]->length);
+ label_info **p;
+ for (p = label_table + (h % label_table_size);
+ *p != 0;
+ (p == label_table)
+ ? (p = label_table + label_table_size - 1)
+ : --p)
+ ;
+ *p = old_table[i];
+ }
+ a_delete old_table;
+ }
+ return result;
+}
+
+void clear_labels()
+{
+ for (int i = 0; i < label_table_size; i++) {
+ delete label_table[i];
+ label_table[i] = 0;
+ }
+ label_table_used = 0;
+ label_pool.clear();
+}
+
+static void consider_authors(reference **start, reference **end, int i);
+
+void compute_labels(reference **v, int n)
+{
+ if (parsed_label
+ && (parsed_label->analyze() & expression::CONTAINS_AT)
+ && sort_fields.length() >= 2
+ && sort_fields[0] == 'A'
+ && sort_fields[1] == '+')
+ consider_authors(v, v + n, 0);
+ for (int i = 0; i < n; i++)
+ v[i]->compute_label();
+}
+
+
+/* A reference with a list of authors <A0,A1,...,AN> _needs_ author i
+where 0 <= i <= N if there exists a reference with a list of authors
+<B0,B1,...,BM> such that <A0,A1,...,AN> != <B0,B1,...,BM> and M >= i
+and Aj = Bj for 0 <= j < i. In this case if we can't say ``A0,
+A1,...,A(i-1) et al'' because this would match both <A0,A1,...,AN> and
+<B0,B1,...,BM>. If a reference needs author i we only have to call
+need_author(j) for some j >= i such that the reference also needs
+author j. */
+
+/* This function handles 2 tasks:
+determine which authors are needed (cannot be elided with et al.);
+determine which authors can have only last names in the labels.
+
+References >= start and < end have the same first i author names.
+Also they're sorted by A+. */
+
+static void consider_authors(reference **start, reference **end, int i)
+{
+ if (start >= end)
+ return;
+ reference **p = start;
+ if (i >= (*p)->get_nauthors()) {
+ for (++p; p < end && i >= (*p)->get_nauthors(); p++)
+ ;
+ if (p < end && i > 0) {
+ // If we have an author list <A B C> and an author list <A B C D>,
+ // then both lists need C.
+ for (reference **q = start; q < end; q++)
+ (*q)->need_author(i - 1);
+ }
+ start = p;
+ }
+ while (p < end) {
+ reference **last_name_start = p;
+ reference **name_start = p;
+ for (++p;
+ p < end && i < (*p)->get_nauthors()
+ && same_author_last_name(**last_name_start, **p, i);
+ p++) {
+ if (!same_author_name(**name_start, **p, i)) {
+ consider_authors(name_start, p, i + 1);
+ name_start = p;
+ }
+ }
+ consider_authors(name_start, p, i + 1);
+ if (last_name_start == name_start) {
+ for (reference **q = last_name_start; q < p; q++)
+ (*q)->set_last_name_unambiguous(i);
+ }
+ // If we have an author list <A B C D> and <A B C E>, then the lists
+ // need author D and E respectively.
+ if (name_start > start || p < end) {
+ for (reference **q = last_name_start; q < p; q++)
+ (*q)->need_author(i);
+ }
+ }
+}
+
+int same_author_last_name(const reference &r1, const reference &r2, int n)
+{
+ const char *ae1;
+ const char *as1 = r1.get_sort_field(0, n, 0, &ae1);
+ assert(as1 != 0);
+ const char *ae2;
+ const char *as2 = r2.get_sort_field(0, n, 0, &ae2);
+ assert(as2 != 0);
+ return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
+}
+
+int same_author_name(const reference &r1, const reference &r2, int n)
+{
+ const char *ae1;
+ const char *as1 = r1.get_sort_field(0, n, -1, &ae1);
+ assert(as1 != 0);
+ const char *ae2;
+ const char *as2 = r2.get_sort_field(0, n, -1, &ae2);
+ assert(as2 != 0);
+ return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0;
+}
+
+
+void int_set::set(int i)
+{
+ assert(i >= 0);
+ int bytei = i >> 3;
+ if (bytei >= v.length()) {
+ int old_length = v.length();
+ v.set_length(bytei + 1);
+ for (int j = old_length; j <= bytei; j++)
+ v[j] = 0;
+ }
+ v[bytei] |= 1 << (i & 7);
+}
+
+int int_set::get(int i) const
+{
+ assert(i >= 0);
+ int bytei = i >> 3;
+ return bytei >= v.length() ? 0 : (v[bytei] & (1 << (i & 7))) != 0;
+}
+
+void reference::set_last_name_unambiguous(int i)
+{
+ last_name_unambiguous.set(i);
+}
+
+void reference::need_author(int n)
+{
+ if (n > last_needed_author)
+ last_needed_author = n;
+}
+
+const char *reference::get_authors(const char **end) const
+{
+ if (!computed_authors) {
+ ((reference *)this)->computed_authors = 1;
+ string &result = ((reference *)this)->authors;
+ int na = get_nauthors();
+ result.clear();
+ for (int i = 0; i < na; i++) {
+ if (last_name_unambiguous.get(i)) {
+ const char *e, *start = get_author_last_name(i, &e);
+ assert(start != 0);
+ result.append(start, e - start);
+ }
+ else {
+ const char *e, *start = get_author(i, &e);
+ assert(start != 0);
+ result.append(start, e - start);
+ }
+ if (i == last_needed_author
+ && et_al.length() > 0
+ && et_al_min_elide > 0
+ && last_needed_author + et_al_min_elide < na
+ && na >= et_al_min_total) {
+ result += et_al;
+ break;
+ }
+ if (i < na - 1) {
+ if (na == 2)
+ result += join_authors_exactly_two;
+ else if (i < na - 2)
+ result += join_authors_default;
+ else
+ result += join_authors_last_two;
+ }
+ }
+ }
+ const char *start = authors.contents();
+ *end = start + authors.length();
+ return start;
+}
+
+int reference::get_nauthors() const
+{
+ if (nauthors < 0) {
+ const char *dummy;
+ int na;
+ for (na = 0; get_author(na, &dummy) != 0; na++)
+ ;
+ ((reference *)this)->nauthors = na;
+ }
+ return nauthors;
+}
diff --git a/contrib/groff/src/preproc/refer/ref.cc b/contrib/groff/src/preproc/refer/ref.cc
new file mode 100644
index 000000000000..c3517b194598
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/ref.cc
@@ -0,0 +1,1160 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "refer.h"
+#include "refid.h"
+#include "ref.h"
+#include "token.h"
+
+static const char *find_day(const char *, const char *, const char **);
+static int find_month(const char *start, const char *end);
+static void abbreviate_names(string &);
+
+#define DEFAULT_ARTICLES "the\000a\000an"
+
+string articles(DEFAULT_ARTICLES, sizeof(DEFAULT_ARTICLES));
+
+// Multiple occurrences of fields are separated by FIELD_SEPARATOR.
+const char FIELD_SEPARATOR = '\0';
+
+const char MULTI_FIELD_NAMES[] = "AE";
+const char *AUTHOR_FIELDS = "AQ";
+
+enum { OTHER, JOURNAL_ARTICLE, BOOK, ARTICLE_IN_BOOK, TECH_REPORT, BELL_TM };
+
+const char *reference_types[] = {
+ "other",
+ "journal-article",
+ "book",
+ "article-in-book",
+ "tech-report",
+ "bell-tm",
+};
+
+static string temp_fields[256];
+
+reference::reference(const char *start, int len, reference_id *ridp)
+: h(0), merged(0), no(-1), field(0), nfields(0), label_ptr(0),
+ computed_authors(0), last_needed_author(-1), nauthors(-1)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ field_index[i] = NULL_FIELD_INDEX;
+ if (ridp)
+ rid = *ridp;
+ if (start == 0)
+ return;
+ if (len <= 0)
+ return;
+ const char *end = start + len;
+ const char *ptr = start;
+ assert(*ptr == '%');
+ while (ptr < end) {
+ if (ptr + 1 < end && ptr[1] != '\0'
+ && ((ptr[1] != '%' && ptr[1] == annotation_field)
+ || (ptr + 2 < end && ptr[1] == '%' && ptr[2] != '\0'
+ && discard_fields.search(ptr[2]) < 0))) {
+ if (ptr[1] == '%')
+ ptr++;
+ string &f = temp_fields[(unsigned char)ptr[1]];
+ ptr += 2;
+ while (ptr < end && csspace(*ptr))
+ ptr++;
+ for (;;) {
+ for (;;) {
+ if (ptr >= end) {
+ f += '\n';
+ break;
+ }
+ f += *ptr;
+ if (*ptr++ == '\n')
+ break;
+ }
+ if (ptr >= end || *ptr == '%')
+ break;
+ }
+ }
+ else if (ptr + 1 < end && ptr[1] != '\0' && ptr[1] != '%'
+ && discard_fields.search(ptr[1]) < 0) {
+ string &f = temp_fields[(unsigned char)ptr[1]];
+ if (f.length() > 0) {
+ if (strchr(MULTI_FIELD_NAMES, ptr[1]) != 0)
+ f += FIELD_SEPARATOR;
+ else
+ f.clear();
+ }
+ ptr += 2;
+ if (ptr < end) {
+ if (*ptr == ' ')
+ ptr++;
+ for (;;) {
+ const char *p = ptr;
+ while (ptr < end && *ptr != '\n')
+ ptr++;
+ // strip trailing white space
+ const char *q = ptr;
+ while (q > p && q[-1] != '\n' && csspace(q[-1]))
+ q--;
+ while (p < q)
+ f += *p++;
+ if (ptr >= end)
+ break;
+ ptr++;
+ if (ptr >= end)
+ break;
+ if (*ptr == '%')
+ break;
+ f += ' ';
+ }
+ }
+ }
+ else {
+ // skip this field
+ for (;;) {
+ while (ptr < end && *ptr++ != '\n')
+ ;
+ if (ptr >= end || *ptr == '%')
+ break;
+ }
+ }
+ }
+ for (i = 0; i < 256; i++)
+ if (temp_fields[i].length() > 0)
+ nfields++;
+ field = new string[nfields];
+ int j = 0;
+ for (i = 0; i < 256; i++)
+ if (temp_fields[i].length() > 0) {
+ field[j].move(temp_fields[i]);
+ if (abbreviate_fields.search(i) >= 0)
+ abbreviate_names(field[j]);
+ field_index[i] = j;
+ j++;
+ }
+}
+
+reference::~reference()
+{
+ if (nfields > 0)
+ ad_delete(nfields) field;
+}
+
+// ref is the inline, this is the database ref
+
+void reference::merge(reference &ref)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (field_index[i] != NULL_FIELD_INDEX)
+ temp_fields[i].move(field[field_index[i]]);
+ for (i = 0; i < 256; i++)
+ if (ref.field_index[i] != NULL_FIELD_INDEX)
+ temp_fields[i].move(ref.field[ref.field_index[i]]);
+ for (i = 0; i < 256; i++)
+ field_index[i] = NULL_FIELD_INDEX;
+ int old_nfields = nfields;
+ nfields = 0;
+ for (i = 0; i < 256; i++)
+ if (temp_fields[i].length() > 0)
+ nfields++;
+ if (nfields != old_nfields) {
+ if (old_nfields > 0)
+ ad_delete(old_nfields) field;
+ field = new string[nfields];
+ }
+ int j = 0;
+ for (i = 0; i < 256; i++)
+ if (temp_fields[i].length() > 0) {
+ field[j].move(temp_fields[i]);
+ field_index[i] = j;
+ j++;
+ }
+ merged = 1;
+}
+
+void reference::insert_field(unsigned char c, string &s)
+{
+ assert(s.length() > 0);
+ if (field_index[c] != NULL_FIELD_INDEX) {
+ field[field_index[c]].move(s);
+ return;
+ }
+ assert(field_index[c] == NULL_FIELD_INDEX);
+ string *old_field = field;
+ field = new string[nfields + 1];
+ int pos = 0;
+ int i;
+ for (i = 0; i < int(c); i++)
+ if (field_index[i] != NULL_FIELD_INDEX)
+ pos++;
+ for (i = 0; i < pos; i++)
+ field[i].move(old_field[i]);
+ field[pos].move(s);
+ for (i = pos; i < nfields; i++)
+ field[i + 1].move(old_field[i]);
+ if (nfields > 0)
+ ad_delete(nfields) old_field;
+ nfields++;
+ field_index[c] = pos;
+ for (i = c + 1; i < 256; i++)
+ if (field_index[i] != NULL_FIELD_INDEX)
+ field_index[i] += 1;
+}
+
+void reference::delete_field(unsigned char c)
+{
+ if (field_index[c] == NULL_FIELD_INDEX)
+ return;
+ string *old_field = field;
+ field = new string[nfields - 1];
+ int i;
+ for (i = 0; i < int(field_index[c]); i++)
+ field[i].move(old_field[i]);
+ for (i = field_index[c]; i < nfields - 1; i++)
+ field[i].move(old_field[i + 1]);
+ if (nfields > 0)
+ ad_delete(nfields) old_field;
+ nfields--;
+ field_index[c] = NULL_FIELD_INDEX;
+ for (i = c + 1; i < 256; i++)
+ if (field_index[i] != NULL_FIELD_INDEX)
+ field_index[i] -= 1;
+}
+
+void reference::compute_hash_code()
+{
+ if (!rid.is_null())
+ h = rid.hash();
+ else {
+ h = 0;
+ for (int i = 0; i < nfields; i++)
+ if (field[i].length() > 0) {
+ h <<= 4;
+ h ^= hash_string(field[i].contents(), field[i].length());
+ }
+ }
+}
+
+void reference::set_number(int n)
+{
+ no = n;
+}
+
+const char SORT_SEP = '\001';
+const char SORT_SUB_SEP = '\002';
+const char SORT_SUB_SUB_SEP = '\003';
+
+// sep specifies additional word separators
+
+void sortify_words(const char *s, const char *end, const char *sep,
+ string &result)
+{
+ int non_empty = 0;
+ int need_separator = 0;
+ for (;;) {
+ const char *token_start = s;
+ if (!get_token(&s, end))
+ break;
+ if ((s - token_start == 1
+ && (*token_start == ' '
+ || *token_start == '\n'
+ || (sep && *token_start != '\0'
+ && strchr(sep, *token_start) != 0)))
+ || (s - token_start == 2
+ && token_start[0] == '\\' && token_start[1] == ' ')) {
+ if (non_empty)
+ need_separator = 1;
+ }
+ else {
+ const token_info *ti = lookup_token(token_start, s);
+ if (ti->sortify_non_empty(token_start, s)) {
+ if (need_separator) {
+ result += ' ';
+ need_separator = 0;
+ }
+ ti->sortify(token_start, s, result);
+ non_empty = 1;
+ }
+ }
+ }
+}
+
+void sortify_word(const char *s, const char *end, string &result)
+{
+ for (;;) {
+ const char *token_start = s;
+ if (!get_token(&s, end))
+ break;
+ const token_info *ti = lookup_token(token_start, s);
+ ti->sortify(token_start, s, result);
+ }
+}
+
+void sortify_other(const char *s, int len, string &key)
+{
+ sortify_words(s, s + len, 0, key);
+}
+
+void sortify_title(const char *s, int len, string &key)
+{
+ const char *end = s + len;
+ for (; s < end && (*s == ' ' || *s == '\n'); s++)
+ ;
+ const char *ptr = s;
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ if (ptr - token_start == 1
+ && (*token_start == ' ' || *token_start == '\n'))
+ break;
+ }
+ if (ptr < end) {
+ int first_word_len = ptr - s - 1;
+ const char *ae = articles.contents() + articles.length();
+ for (const char *a = articles.contents();
+ a < ae;
+ a = strchr(a, '\0') + 1)
+ if (first_word_len == strlen(a)) {
+ int j;
+ for (j = 0; j < first_word_len; j++)
+ if (a[j] != cmlower(s[j]))
+ break;
+ if (j >= first_word_len) {
+ s = ptr;
+ for (; s < end && (*s == ' ' || *s == '\n'); s++)
+ ;
+ break;
+ }
+ }
+ }
+ sortify_words(s, end, 0, key);
+}
+
+void sortify_name(const char *s, int len, string &key)
+{
+ const char *last_name_end;
+ const char *last_name = find_last_name(s, s + len, &last_name_end);
+ sortify_word(last_name, last_name_end, key);
+ key += SORT_SUB_SUB_SEP;
+ if (last_name > s)
+ sortify_words(s, last_name, ".", key);
+ key += SORT_SUB_SUB_SEP;
+ if (last_name_end < s + len)
+ sortify_words(last_name_end, s + len, ".,", key);
+}
+
+void sortify_date(const char *s, int len, string &key)
+{
+ const char *year_end;
+ const char *year_start = find_year(s, s + len, &year_end);
+ if (!year_start) {
+ // Things without years are often `forthcoming', so it makes sense
+ // that they sort after things with explicit years.
+ key += 'A';
+ sortify_words(s, s + len, 0, key);
+ return;
+ }
+ int n = year_end - year_start;
+ while (n < 4) {
+ key += '0';
+ n++;
+ }
+ while (year_start < year_end)
+ key += *year_start++;
+ int m = find_month(s, s + len);
+ if (m < 0)
+ return;
+ key += 'A' + m;
+ const char *day_end;
+ const char *day_start = find_day(s, s + len, &day_end);
+ if (!day_start)
+ return;
+ if (day_end - day_start == 1)
+ key += '0';
+ while (day_start < day_end)
+ key += *day_start++;
+}
+
+// SORT_{SUB,SUB_SUB}_SEP can creep in from use of @ in label specification.
+
+void sortify_label(const char *s, int len, string &key)
+{
+ const char *end = s + len;
+ for (;;) {
+ const char *ptr;
+ for (ptr = s;
+ ptr < end && *ptr != SORT_SUB_SEP && *ptr != SORT_SUB_SUB_SEP;
+ ptr++)
+ ;
+ if (ptr > s)
+ sortify_words(s, ptr, 0, key);
+ s = ptr;
+ if (s >= end)
+ break;
+ key += *s++;
+ }
+}
+
+void reference::compute_sort_key()
+{
+ if (sort_fields.length() == 0)
+ return;
+ sort_fields += '\0';
+ const char *sf = sort_fields.contents();
+ while (*sf != '\0') {
+ if (sf > sort_fields)
+ sort_key += SORT_SEP;
+ char f = *sf++;
+ int n = 1;
+ if (*sf == '+') {
+ n = INT_MAX;
+ sf++;
+ }
+ else if (csdigit(*sf)) {
+ char *ptr;
+ long l = strtol(sf, &ptr, 10);
+ if (l == 0 && ptr == sf)
+ ;
+ else {
+ sf = ptr;
+ if (l < 0) {
+ n = 1;
+ }
+ else {
+ n = int(l);
+ }
+ }
+ }
+ if (f == '.')
+ sortify_label(label.contents(), label.length(), sort_key);
+ else if (f == AUTHOR_FIELDS[0])
+ sortify_authors(n, sort_key);
+ else
+ sortify_field(f, n, sort_key);
+ }
+ sort_fields.set_length(sort_fields.length() - 1);
+}
+
+void reference::sortify_authors(int n, string &result) const
+{
+ for (const char *p = AUTHOR_FIELDS; *p != '\0'; p++)
+ if (contains_field(*p)) {
+ sortify_field(*p, n, result);
+ return;
+ }
+ sortify_field(AUTHOR_FIELDS[0], n, result);
+}
+
+void reference::canonicalize_authors(string &result) const
+{
+ int len = result.length();
+ sortify_authors(INT_MAX, result);
+ if (result.length() > len)
+ result += SORT_SUB_SEP;
+}
+
+void reference::sortify_field(unsigned char f, int n, string &result) const
+{
+ typedef void (*sortify_t)(const char *, int, string &);
+ sortify_t sortifier = sortify_other;
+ switch (f) {
+ case 'A':
+ case 'E':
+ sortifier = sortify_name;
+ break;
+ case 'D':
+ sortifier = sortify_date;
+ break;
+ case 'B':
+ case 'J':
+ case 'T':
+ sortifier = sortify_title;
+ break;
+ }
+ int fi = field_index[(unsigned char)f];
+ if (fi != NULL_FIELD_INDEX) {
+ string &str = field[fi];
+ const char *start = str.contents();
+ const char *end = start + str.length();
+ for (int i = 0; i < n && start < end; i++) {
+ const char *p = start;
+ while (start < end && *start != FIELD_SEPARATOR)
+ start++;
+ if (i > 0)
+ result += SORT_SUB_SEP;
+ (*sortifier)(p, start - p, result);
+ if (start < end)
+ start++;
+ }
+ }
+}
+
+int compare_reference(const reference &r1, const reference &r2)
+{
+ assert(r1.no >= 0);
+ assert(r2.no >= 0);
+ const char *s1 = r1.sort_key.contents();
+ int n1 = r1.sort_key.length();
+ const char *s2 = r2.sort_key.contents();
+ int n2 = r2.sort_key.length();
+ for (; n1 > 0 && n2 > 0; --n1, --n2, ++s1, ++s2)
+ if (*s1 != *s2)
+ return (int)(unsigned char)*s1 - (int)(unsigned char)*s2;
+ if (n2 > 0)
+ return -1;
+ if (n1 > 0)
+ return 1;
+ return r1.no - r2.no;
+}
+
+int same_reference(const reference &r1, const reference &r2)
+{
+ if (!r1.rid.is_null() && r1.rid == r2.rid)
+ return 1;
+ if (r1.h != r2.h)
+ return 0;
+ if (r1.nfields != r2.nfields)
+ return 0;
+ int i = 0;
+ for (i = 0; i < 256; i++)
+ if (r1.field_index != r2.field_index)
+ return 0;
+ for (i = 0; i < r1.nfields; i++)
+ if (r1.field[i] != r2.field[i])
+ return 0;
+ return 1;
+}
+
+const char *find_last_name(const char *start, const char *end,
+ const char **endp)
+{
+ const char *ptr = start;
+ const char *last_word = start;
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ if (ptr - token_start == 1) {
+ if (*token_start == ',') {
+ *endp = token_start;
+ return last_word;
+ }
+ else if (*token_start == ' ' || *token_start == '\n') {
+ if (ptr < end && *ptr != ' ' && *ptr != '\n')
+ last_word = ptr;
+ }
+ }
+ }
+ *endp = end;
+ return last_word;
+}
+
+void abbreviate_name(const char *ptr, const char *end, string &result)
+{
+ const char *last_name_end;
+ const char *last_name_start = find_last_name(ptr, end, &last_name_end);
+ int need_period = 0;
+ for (;;) {
+ const char *token_start = ptr;
+ if (!get_token(&ptr, last_name_start))
+ break;
+ const token_info *ti = lookup_token(token_start, ptr);
+ if (need_period) {
+ if ((ptr - token_start == 1 && *token_start == ' ')
+ || (ptr - token_start == 2 && token_start[0] == '\\'
+ && token_start[1] == ' '))
+ continue;
+ if (ti->is_upper())
+ result += period_before_initial;
+ else
+ result += period_before_other;
+ need_period = 0;
+ }
+ result.append(token_start, ptr - token_start);
+ if (ti->is_upper()) {
+ const char *lower_ptr = ptr;
+ int first_token = 1;
+ for (;;) {
+ token_start = ptr;
+ if (!get_token(&ptr, last_name_start))
+ break;
+ if ((ptr - token_start == 1 && *token_start == ' ')
+ || (ptr - token_start == 2 && token_start[0] == '\\'
+ && token_start[1] == ' '))
+ break;
+ ti = lookup_token(token_start, ptr);
+ if (ti->is_hyphen()) {
+ const char *ptr1 = ptr;
+ if (get_token(&ptr1, last_name_start)) {
+ ti = lookup_token(ptr, ptr1);
+ if (ti->is_upper()) {
+ result += period_before_hyphen;
+ result.append(token_start, ptr1 - token_start);
+ ptr = ptr1;
+ }
+ }
+ }
+ else if (ti->is_upper()) {
+ // MacDougal -> MacD.
+ result.append(lower_ptr, ptr - lower_ptr);
+ lower_ptr = ptr;
+ first_token = 1;
+ }
+ else if (first_token && ti->is_accent()) {
+ result.append(token_start, ptr - token_start);
+ lower_ptr = ptr;
+ }
+ first_token = 0;
+ }
+ need_period = 1;
+ }
+ }
+ if (need_period)
+ result += period_before_last_name;
+ result.append(last_name_start, end - last_name_start);
+}
+
+static void abbreviate_names(string &result)
+{
+ string str;
+ str.move(result);
+ const char *ptr = str.contents();
+ const char *end = ptr + str.length();
+ while (ptr < end) {
+ const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr);
+ if (name_end == 0)
+ name_end = end;
+ abbreviate_name(ptr, name_end, result);
+ if (name_end >= end)
+ break;
+ ptr = name_end + 1;
+ result += FIELD_SEPARATOR;
+ }
+}
+
+void reverse_name(const char *ptr, const char *name_end, string &result)
+{
+ const char *last_name_end;
+ const char *last_name_start = find_last_name(ptr, name_end, &last_name_end);
+ result.append(last_name_start, last_name_end - last_name_start);
+ while (last_name_start > ptr
+ && (last_name_start[-1] == ' ' || last_name_start[-1] == '\n'))
+ last_name_start--;
+ if (last_name_start > ptr) {
+ result += ", ";
+ result.append(ptr, last_name_start - ptr);
+ }
+ if (last_name_end < name_end)
+ result.append(last_name_end, name_end - last_name_end);
+}
+
+void reverse_names(string &result, int n)
+{
+ if (n <= 0)
+ return;
+ string str;
+ str.move(result);
+ const char *ptr = str.contents();
+ const char *end = ptr + str.length();
+ while (ptr < end) {
+ if (--n < 0) {
+ result.append(ptr, end - ptr);
+ break;
+ }
+ const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr);
+ if (name_end == 0)
+ name_end = end;
+ reverse_name(ptr, name_end, result);
+ if (name_end >= end)
+ break;
+ ptr = name_end + 1;
+ result += FIELD_SEPARATOR;
+ }
+}
+
+// Return number of field separators.
+
+int join_fields(string &f)
+{
+ const char *ptr = f.contents();
+ int len = f.length();
+ int nfield_seps = 0;
+ int j;
+ for (j = 0; j < len; j++)
+ if (ptr[j] == FIELD_SEPARATOR)
+ nfield_seps++;
+ if (nfield_seps == 0)
+ return 0;
+ string temp;
+ int field_seps_left = nfield_seps;
+ for (j = 0; j < len; j++) {
+ if (ptr[j] == FIELD_SEPARATOR) {
+ if (nfield_seps == 1)
+ temp += join_authors_exactly_two;
+ else if (--field_seps_left == 0)
+ temp += join_authors_last_two;
+ else
+ temp += join_authors_default;
+ }
+ else
+ temp += ptr[j];
+ }
+ f = temp;
+ return nfield_seps;
+}
+
+void uppercase(const char *start, const char *end, string &result)
+{
+ for (;;) {
+ const char *token_start = start;
+ if (!get_token(&start, end))
+ break;
+ const token_info *ti = lookup_token(token_start, start);
+ ti->upper_case(token_start, start, result);
+ }
+}
+
+void lowercase(const char *start, const char *end, string &result)
+{
+ for (;;) {
+ const char *token_start = start;
+ if (!get_token(&start, end))
+ break;
+ const token_info *ti = lookup_token(token_start, start);
+ ti->lower_case(token_start, start, result);
+ }
+}
+
+void capitalize(const char *ptr, const char *end, string &result)
+{
+ int in_small_point_size = 0;
+ for (;;) {
+ const char *start = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ const token_info *ti = lookup_token(start, ptr);
+ const char *char_end = ptr;
+ int is_lower = ti->is_lower();
+ if ((is_lower || ti->is_upper()) && get_token(&ptr, end)) {
+ const token_info *ti2 = lookup_token(char_end, ptr);
+ if (!ti2->is_accent())
+ ptr = char_end;
+ }
+ if (is_lower) {
+ if (!in_small_point_size) {
+ result += "\\s-2";
+ in_small_point_size = 1;
+ }
+ ti->upper_case(start, char_end, result);
+ result.append(char_end, ptr - char_end);
+ }
+ else {
+ if (in_small_point_size) {
+ result += "\\s+2";
+ in_small_point_size = 0;
+ }
+ result.append(start, ptr - start);
+ }
+ }
+ if (in_small_point_size)
+ result += "\\s+2";
+}
+
+void capitalize_field(string &str)
+{
+ string temp;
+ capitalize(str.contents(), str.contents() + str.length(), temp);
+ str.move(temp);
+}
+
+int is_terminated(const char *ptr, const char *end)
+{
+ const char *last_token = end;
+ for (;;) {
+ const char *p = ptr;
+ if (!get_token(&ptr, end))
+ break;
+ last_token = p;
+ }
+ return end - last_token == 1
+ && (*last_token == '.' || *last_token == '!' || *last_token == '?');
+}
+
+void reference::output(FILE *fp)
+{
+ fputs(".]-\n", fp);
+ for (int i = 0; i < 256; i++)
+ if (field_index[i] != NULL_FIELD_INDEX && i != annotation_field) {
+ string &f = field[field_index[i]];
+ if (!csdigit(i)) {
+ int j = reverse_fields.search(i);
+ if (j >= 0) {
+ int n;
+ int len = reverse_fields.length();
+ if (++j < len && csdigit(reverse_fields[j])) {
+ n = reverse_fields[j] - '0';
+ for (++j; j < len && csdigit(reverse_fields[j]); j++)
+ // should check for overflow
+ n = n*10 + reverse_fields[j] - '0';
+ }
+ else
+ n = INT_MAX;
+ reverse_names(f, n);
+ }
+ }
+ int is_multiple = join_fields(f) > 0;
+ if (capitalize_fields.search(i) >= 0)
+ capitalize_field(f);
+ if (memchr(f.contents(), '\n', f.length()) == 0) {
+ fprintf(fp, ".ds [%c ", i);
+ if (f[0] == ' ' || f[0] == '\\' || f[0] == '"')
+ putc('"', fp);
+ put_string(f, fp);
+ putc('\n', fp);
+ }
+ else {
+ fprintf(fp, ".de [%c\n", i);
+ put_string(f, fp);
+ fputs("..\n", fp);
+ }
+ if (i == 'P') {
+ int multiple_pages = 0;
+ const char *s = f.contents();
+ const char *end = f.contents() + f.length();
+ for (;;) {
+ const char *token_start = s;
+ if (!get_token(&s, end))
+ break;
+ const token_info *ti = lookup_token(token_start, s);
+ if (ti->is_hyphen() || ti->is_range_sep()) {
+ multiple_pages = 1;
+ break;
+ }
+ }
+ fprintf(fp, ".nr [P %d\n", multiple_pages);
+ }
+ else if (i == 'E')
+ fprintf(fp, ".nr [E %d\n", is_multiple);
+ }
+ for (const char *p = "TAO"; *p; p++) {
+ int fi = field_index[(unsigned char)*p];
+ if (fi != NULL_FIELD_INDEX) {
+ string &f = field[fi];
+ fprintf(fp, ".nr [%c %d\n", *p,
+ is_terminated(f.contents(), f.contents() + f.length()));
+ }
+ }
+ int t = classify();
+ fprintf(fp, ".][ %d %s\n", t, reference_types[t]);
+ if (annotation_macro.length() > 0 && annotation_field >= 0
+ && field_index[annotation_field] != NULL_FIELD_INDEX) {
+ putc('.', fp);
+ put_string(annotation_macro, fp);
+ putc('\n', fp);
+ put_string(field[field_index[annotation_field]], fp);
+ }
+}
+
+void reference::print_sort_key_comment(FILE *fp)
+{
+ fputs(".\\\"", fp);
+ put_string(sort_key, fp);
+ putc('\n', fp);
+}
+
+const char *find_year(const char *start, const char *end, const char **endp)
+{
+ for (;;) {
+ while (start < end && !csdigit(*start))
+ start++;
+ const char *ptr = start;
+ if (start == end)
+ break;
+ while (ptr < end && csdigit(*ptr))
+ ptr++;
+ if (ptr - start == 4 || ptr - start == 3
+ || (ptr - start == 2
+ && (start[0] >= '4' || (start[0] == '3' && start[1] >= '2')))) {
+ *endp = ptr;
+ return start;
+ }
+ start = ptr;
+ }
+ return 0;
+}
+
+static const char *find_day(const char *start, const char *end,
+ const char **endp)
+{
+ for (;;) {
+ while (start < end && !csdigit(*start))
+ start++;
+ const char *ptr = start;
+ if (start == end)
+ break;
+ while (ptr < end && csdigit(*ptr))
+ ptr++;
+ if ((ptr - start == 1 && start[0] != '0')
+ || (ptr - start == 2 &&
+ (start[0] == '1'
+ || start[0] == '2'
+ || (start[0] == '3' && start[1] <= '1')
+ || (start[0] == '0' && start[1] != '0')))) {
+ *endp = ptr;
+ return start;
+ }
+ start = ptr;
+ }
+ return 0;
+}
+
+static int find_month(const char *start, const char *end)
+{
+ static const char *months[] = {
+ "january",
+ "february",
+ "march",
+ "april",
+ "may",
+ "june",
+ "july",
+ "august",
+ "september",
+ "october",
+ "november",
+ "december",
+ };
+ for (;;) {
+ while (start < end && !csalpha(*start))
+ start++;
+ const char *ptr = start;
+ if (start == end)
+ break;
+ while (ptr < end && csalpha(*ptr))
+ ptr++;
+ if (ptr - start >= 3) {
+ for (int i = 0; i < sizeof(months)/sizeof(months[0]); i++) {
+ const char *q = months[i];
+ const char *p = start;
+ for (; p < ptr; p++, q++)
+ if (cmlower(*p) != *q)
+ break;
+ if (p >= ptr)
+ return i;
+ }
+ }
+ start = ptr;
+ }
+ return -1;
+}
+
+int reference::contains_field(char c) const
+{
+ return field_index[(unsigned char)c] != NULL_FIELD_INDEX;
+}
+
+int reference::classify()
+{
+ if (contains_field('J'))
+ return JOURNAL_ARTICLE;
+ if (contains_field('B'))
+ return ARTICLE_IN_BOOK;
+ if (contains_field('G'))
+ return TECH_REPORT;
+ if (contains_field('R'))
+ return TECH_REPORT;
+ if (contains_field('I'))
+ return BOOK;
+ if (contains_field('M'))
+ return BELL_TM;
+ return OTHER;
+}
+
+const char *reference::get_year(const char **endp) const
+{
+ if (field_index['D'] != NULL_FIELD_INDEX) {
+ string &date = field[field_index['D']];
+ const char *start = date.contents();
+ const char *end = start + date.length();
+ return find_year(start, end, endp);
+ }
+ else
+ return 0;
+}
+
+const char *reference::get_field(unsigned char c, const char **endp) const
+{
+ if (field_index[c] != NULL_FIELD_INDEX) {
+ string &f = field[field_index[c]];
+ const char *start = f.contents();
+ *endp = start + f.length();
+ return start;
+ }
+ else
+ return 0;
+}
+
+const char *reference::get_date(const char **endp) const
+{
+ return get_field('D', endp);
+}
+
+const char *nth_field(int i, const char *start, const char **endp)
+{
+ while (--i >= 0) {
+ start = (char *)memchr(start, FIELD_SEPARATOR, *endp - start);
+ if (!start)
+ return 0;
+ start++;
+ }
+ const char *e = (char *)memchr(start, FIELD_SEPARATOR, *endp - start);
+ if (e)
+ *endp = e;
+ return start;
+}
+
+const char *reference::get_author(int i, const char **endp) const
+{
+ for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) {
+ const char *start = get_field(*f, endp);
+ if (start) {
+ if (strchr(MULTI_FIELD_NAMES, *f) != 0)
+ return nth_field(i, start, endp);
+ else if (i == 0)
+ return start;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+const char *reference::get_author_last_name(int i, const char **endp) const
+{
+ for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) {
+ const char *start = get_field(*f, endp);
+ if (start) {
+ if (strchr(MULTI_FIELD_NAMES, *f) != 0) {
+ start = nth_field(i, start, endp);
+ if (!start)
+ return 0;
+ }
+ if (*f == 'A')
+ return find_last_name(start, *endp, endp);
+ else
+ return start;
+ }
+ }
+ return 0;
+}
+
+void reference::set_date(string &d)
+{
+ if (d.length() == 0)
+ delete_field('D');
+ else
+ insert_field('D', d);
+}
+
+int same_year(const reference &r1, const reference &r2)
+{
+ const char *ye1;
+ const char *ys1 = r1.get_year(&ye1);
+ const char *ye2;
+ const char *ys2 = r2.get_year(&ye2);
+ if (ys1 == 0) {
+ if (ys2 == 0)
+ return same_date(r1, r2);
+ else
+ return 0;
+ }
+ else if (ys2 == 0)
+ return 0;
+ else if (ye1 - ys1 != ye2 - ys2)
+ return 0;
+ else
+ return memcmp(ys1, ys2, ye1 - ys1) == 0;
+}
+
+int same_date(const reference &r1, const reference &r2)
+{
+ const char *e1;
+ const char *s1 = r1.get_date(&e1);
+ const char *e2;
+ const char *s2 = r2.get_date(&e2);
+ if (s1 == 0)
+ return s2 == 0;
+ else if (s2 == 0)
+ return 0;
+ else if (e1 - s1 != e2 - s2)
+ return 0;
+ else
+ return memcmp(s1, s2, e1 - s1) == 0;
+}
+
+const char *reference::get_sort_field(int i, int si, int ssi,
+ const char **endp) const
+{
+ const char *start = sort_key.contents();
+ const char *end = start + sort_key.length();
+ if (i < 0) {
+ *endp = end;
+ return start;
+ }
+ while (--i >= 0) {
+ start = (char *)memchr(start, SORT_SEP, end - start);
+ if (!start)
+ return 0;
+ start++;
+ }
+ const char *e = (char *)memchr(start, SORT_SEP, end - start);
+ if (e)
+ end = e;
+ if (si < 0) {
+ *endp = end;
+ return start;
+ }
+ while (--si >= 0) {
+ start = (char *)memchr(start, SORT_SUB_SEP, end - start);
+ if (!start)
+ return 0;
+ start++;
+ }
+ e = (char *)memchr(start, SORT_SUB_SEP, end - start);
+ if (e)
+ end = e;
+ if (ssi < 0) {
+ *endp = end;
+ return start;
+ }
+ while (--ssi >= 0) {
+ start = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start);
+ if (!start)
+ return 0;
+ start++;
+ }
+ e = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start);
+ if (e)
+ end = e;
+ *endp = end;
+ return start;
+}
+
diff --git a/contrib/groff/src/preproc/refer/ref.h b/contrib/groff/src/preproc/refer/ref.h
new file mode 100644
index 000000000000..13a984a4c727
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/ref.h
@@ -0,0 +1,120 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct label_info;
+
+enum label_type { NORMAL_LABEL, SHORT_LABEL };
+const int N_LABEL_TYPES = 2;
+
+struct substring_position {
+ int start;
+ int length;
+ substring_position() : start(-1) { }
+};
+
+class int_set {
+ string v;
+public:
+ int_set() { }
+ void set(int i);
+ int get(int i) const;
+};
+
+class reference {
+private:
+ unsigned h;
+ reference_id rid;
+ int merged;
+ string sort_key;
+ int no;
+ string *field;
+ int nfields;
+ unsigned char field_index[256];
+ enum { NULL_FIELD_INDEX = 255 };
+ string label;
+ substring_position separator_pos;
+ string short_label;
+ substring_position short_separator_pos;
+ label_info *label_ptr;
+ string authors;
+ int computed_authors;
+ int last_needed_author;
+ int nauthors;
+ int_set last_name_unambiguous;
+
+ int contains_field(char) const;
+ void insert_field(unsigned char, string &s);
+ void delete_field(unsigned char);
+ void set_date(string &);
+ const char *get_sort_field(int i, int si, int ssi, const char **endp) const;
+ int merge_labels_by_parts(reference **, int, label_type, string &);
+ int merge_labels_by_number(reference **, int, label_type, string &);
+public:
+ reference(const char * = 0, int = -1, reference_id * = 0);
+ ~reference();
+ void output(FILE *);
+ void print_sort_key_comment(FILE *);
+ void set_number(int);
+ int get_number() const { return no; }
+ unsigned hash() const { return h; }
+ const string &get_label(label_type type) const;
+ const substring_position &get_separator_pos(label_type) const;
+ int is_merged() const { return merged; }
+ void compute_sort_key();
+ void compute_hash_code();
+ void pre_compute_label();
+ void compute_label();
+ void immediate_compute_label();
+ int classify();
+ void merge(reference &);
+ int merge_labels(reference **, int, label_type, string &);
+ int get_nauthors() const;
+ void need_author(int);
+ void set_last_name_unambiguous(int);
+ void sortify_authors(int, string &) const;
+ void canonicalize_authors(string &) const;
+ void sortify_field(unsigned char, int, string &) const;
+ const char *get_author(int, const char **) const;
+ const char *get_author_last_name(int, const char **) const;
+ const char *get_date(const char **) const;
+ const char *get_year(const char **) const;
+ const char *get_field(unsigned char, const char **) const;
+ const label_info *get_label_ptr() const { return label_ptr; }
+ const char *get_authors(const char **) const;
+ // for sorting
+ friend int compare_reference(const reference &r1, const reference &r2);
+ // for merging
+ friend int same_reference(const reference &, const reference &);
+ friend int same_year(const reference &, const reference &);
+ friend int same_date(const reference &, const reference &);
+ friend int same_author_last_name(const reference &, const reference &, int);
+ friend int same_author_name(const reference &, const reference &, int);
+};
+
+const char *find_year(const char *, const char *, const char **);
+const char *find_last_name(const char *, const char *, const char **);
+
+const char *nth_field(int i, const char *start, const char **endp);
+
+void capitalize(const char *ptr, const char *end, string &result);
+void reverse_name(const char *ptr, const char *end, string &result);
+void uppercase(const char *ptr, const char *end, string &result);
+void lowercase(const char *ptr, const char *end, string &result);
+void abbreviate_name(const char *ptr, const char *end, string &result);
diff --git a/contrib/groff/src/preproc/refer/refer.cc b/contrib/groff/src/preproc/refer/refer.cc
new file mode 100644
index 000000000000..b6cefc5859cf
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/refer.cc
@@ -0,0 +1,1234 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 "refer.h"
+#include "refid.h"
+#include "ref.h"
+#include "token.h"
+#include "search.h"
+#include "command.h"
+
+const char PRE_LABEL_MARKER = '\013';
+const char POST_LABEL_MARKER = '\014';
+const char LABEL_MARKER = '\015'; // label_type is added on
+
+#define FORCE_LEFT_BRACKET 04
+#define FORCE_RIGHT_BRACKET 010
+
+static FILE *outfp = stdout;
+
+string capitalize_fields;
+string reverse_fields;
+string abbreviate_fields;
+string period_before_last_name = ". ";
+string period_before_initial = ".";
+string period_before_hyphen = "";
+string period_before_other = ". ";
+string sort_fields;
+int annotation_field = -1;
+string annotation_macro;
+string discard_fields = "XYZ";
+string pre_label = "\\*([.";
+string post_label = "\\*(.]";
+string sep_label = ", ";
+int accumulate = 0;
+int move_punctuation = 0;
+int abbreviate_label_ranges = 0;
+string label_range_indicator;
+int label_in_text = 1;
+int label_in_reference = 1;
+int date_as_label = 0;
+int sort_adjacent_labels = 0;
+// Join exactly two authors with this.
+string join_authors_exactly_two = " and ";
+// When there are more than two authors join the last two with this.
+string join_authors_last_two = ", and ";
+// Otherwise join authors with this.
+string join_authors_default = ", ";
+string separate_label_second_parts = ", ";
+// Use this string to represent that there are other authors.
+string et_al = " et al";
+// Use et al only if it can replace at least this many authors.
+int et_al_min_elide = 2;
+// Use et al only if the total number of authors is at least this.
+int et_al_min_total = 3;
+
+
+int compatible_flag = 0;
+
+int short_label_flag = 0;
+
+static int recognize_R1_R2 = 1;
+
+search_list database_list;
+int search_default = 1;
+static int default_database_loaded = 0;
+
+static reference **citation = 0;
+static int ncitations = 0;
+static int citation_max = 0;
+
+static reference **reference_hash_table = 0;
+static int hash_table_size;
+static int nreferences = 0;
+
+static int need_syncing = 0;
+string pending_line;
+string pending_lf_lines;
+
+static void output_pending_line();
+static unsigned immediately_handle_reference(const string &);
+static void immediately_output_references();
+static unsigned store_reference(const string &);
+static void divert_to_temporary_file();
+static reference *make_reference(const string &, unsigned *);
+static void usage(FILE *stream);
+static void do_file(const char *);
+static void split_punct(string &line, string &punct);
+static void output_citation_group(reference **v, int n, label_type, FILE *fp);
+static void possibly_load_default_database();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ outfp = stdout;
+ int finished_options = 0;
+ int bib_flag = 0;
+ int done_spec = 0;
+
+ for (--argc, ++argv;
+ !finished_options && argc > 0 && argv[0][0] == '-'
+ && argv[0][1] != '\0';
+ argv++, argc--) {
+ const char *opt = argv[0] + 1;
+ while (opt != 0 && *opt != '\0') {
+ switch (*opt) {
+ case 'C':
+ compatible_flag = 1;
+ opt++;
+ break;
+ case 'B':
+ bib_flag = 1;
+ label_in_reference = 0;
+ label_in_text = 0;
+ ++opt;
+ if (*opt == '\0') {
+ annotation_field = 'X';
+ annotation_macro = "AP";
+ }
+ else if (csalnum(opt[0]) && opt[1] == '.' && opt[2] != '\0') {
+ annotation_field = opt[0];
+ annotation_macro = opt + 2;
+ }
+ opt = 0;
+ break;
+ case 'P':
+ move_punctuation = 1;
+ opt++;
+ break;
+ case 'R':
+ recognize_R1_R2 = 0;
+ opt++;
+ break;
+ case 'S':
+ // Not a very useful spec.
+ set_label_spec("(A.n|Q)', '(D.y|D)");
+ done_spec = 1;
+ pre_label = " (";
+ post_label = ")";
+ sep_label = "; ";
+ opt++;
+ break;
+ case 'V':
+ verify_flag = 1;
+ opt++;
+ break;
+ case 'f':
+ {
+ const char *num = 0;
+ if (*++opt == '\0') {
+ if (argc > 1) {
+ num = *++argv;
+ --argc;
+ }
+ else {
+ error("option `f' requires an argument");
+ usage(stderr);
+ exit(1);
+ }
+ }
+ else {
+ num = opt;
+ opt = 0;
+ }
+ const char *ptr;
+ for (ptr = num; *ptr; ptr++)
+ if (!csdigit(*ptr)) {
+ error("bad character `%1' in argument to -f option", *ptr);
+ break;
+ }
+ if (*ptr == '\0') {
+ string spec;
+ spec = '%';
+ spec += num;
+ spec += '\0';
+ set_label_spec(spec.contents());
+ done_spec = 1;
+ }
+ break;
+ }
+ case 'b':
+ label_in_text = 0;
+ label_in_reference = 0;
+ opt++;
+ break;
+ case 'e':
+ accumulate = 1;
+ opt++;
+ break;
+ case 'c':
+ capitalize_fields = ++opt;
+ opt = 0;
+ break;
+ case 'k':
+ {
+ char buf[5];
+ if (csalpha(*++opt))
+ buf[0] = *opt++;
+ else {
+ if (*opt != '\0')
+ error("bad field name `%1'", *opt++);
+ buf[0] = 'L';
+ }
+ buf[1] = '~';
+ buf[2] = '%';
+ buf[3] = 'a';
+ buf[4] = '\0';
+ set_label_spec(buf);
+ done_spec = 1;
+ }
+ break;
+ case 'a':
+ {
+ const char *ptr;
+ for (ptr = ++opt; *ptr; ptr++)
+ if (!csdigit(*ptr)) {
+ error("argument to `a' option not a number");
+ break;
+ }
+ if (*ptr == '\0') {
+ reverse_fields = 'A';
+ reverse_fields += opt;
+ }
+ opt = 0;
+ }
+ break;
+ case 'i':
+ linear_ignore_fields = ++opt;
+ opt = 0;
+ break;
+ case 'l':
+ {
+ char buf[INT_DIGITS*2 + 11]; // A.n+2D.y-3%a
+ strcpy(buf, "A.n");
+ if (*++opt != '\0' && *opt != ',') {
+ char *ptr;
+ long n = strtol(opt, &ptr, 10);
+ if (n == 0 && ptr == opt) {
+ error("bad integer `%1' in `l' option", opt);
+ opt = 0;
+ break;
+ }
+ if (n < 0)
+ n = 0;
+ opt = ptr;
+ sprintf(strchr(buf, '\0'), "+%ld", n);
+ }
+ strcat(buf, "D.y");
+ if (*opt == ',')
+ opt++;
+ if (*opt != '\0') {
+ char *ptr;
+ long n = strtol(opt, &ptr, 10);
+ if (n == 0 && ptr == opt) {
+ error("bad integer `%1' in `l' option", opt);
+ opt = 0;
+ break;
+ }
+ if (n < 0)
+ n = 0;
+ sprintf(strchr(buf, '\0'), "-%ld", n);
+ opt = ptr;
+ if (*opt != '\0')
+ error("argument to `l' option not of form `m,n'");
+ }
+ strcat(buf, "%a");
+ if (!set_label_spec(buf))
+ assert(0);
+ done_spec = 1;
+ }
+ break;
+ case 'n':
+ search_default = 0;
+ opt++;
+ break;
+ case 'p':
+ {
+ const char *filename = 0;
+ if (*++opt == '\0') {
+ if (argc > 1) {
+ filename = *++argv;
+ argc--;
+ }
+ else {
+ error("option `p' requires an argument");
+ usage(stderr);
+ exit(1);
+ }
+ }
+ else {
+ filename = opt;
+ opt = 0;
+ }
+ database_list.add_file(filename);
+ }
+ break;
+ case 's':
+ if (*++opt == '\0')
+ sort_fields = "AD";
+ else {
+ sort_fields = opt;
+ opt = 0;
+ }
+ accumulate = 1;
+ break;
+ case 't':
+ {
+ char *ptr;
+ long n = strtol(opt, &ptr, 10);
+ if (n == 0 && ptr == opt) {
+ error("bad integer `%1' in `t' option", opt);
+ opt = 0;
+ break;
+ }
+ if (n < 1)
+ n = 1;
+ linear_truncate_len = int(n);
+ opt = ptr;
+ break;
+ }
+ case '-':
+ if (opt[1] == '\0') {
+ finished_options = 1;
+ opt++;
+ break;
+ }
+ if (strcmp(opt,"-version")==0) {
+ case 'v':
+ extern const char *Version_string;
+ printf("GNU refer (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ if (strcmp(opt,"-help")==0) {
+ usage(stdout);
+ exit(0);
+ break;
+ }
+ // fall through
+ default:
+ error("unrecognized option `%1'", *opt);
+ usage(stderr);
+ exit(1);
+ break;
+ }
+ }
+ }
+ if (!done_spec)
+ set_label_spec("%1");
+ if (argc <= 0) {
+ if (bib_flag)
+ do_bib("-");
+ else
+ do_file("-");
+ }
+ else {
+ for (int i = 0; i < argc; i++) {
+ if (bib_flag)
+ do_bib(argv[i]);
+ else
+ do_file(argv[i]);
+ }
+ }
+ if (accumulate)
+ output_references();
+ if (fflush(stdout) < 0)
+ fatal("output error");
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-benvCPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N] [-p file]\n"
+" [-sXYZ] [-tN] [-BL.M] [files ...]\n",
+ program_name);
+}
+
+static void possibly_load_default_database()
+{
+ if (search_default && !default_database_loaded) {
+ char *filename = getenv("REFER");
+ if (filename)
+ database_list.add_file(filename);
+ else
+ database_list.add_file(DEFAULT_INDEX, 1);
+ default_database_loaded = 1;
+ }
+}
+
+static int is_list(const string &str)
+{
+ const char *start = str.contents();
+ const char *end = start + str.length();
+ while (end > start && csspace(end[-1]))
+ end--;
+ while (start < end && csspace(*start))
+ start++;
+ return end - start == 6 && memcmp(start, "$LIST$", 6) == 0;
+}
+
+static void do_file(const char *filename)
+{
+ FILE *fp;
+ if (strcmp(filename, "-") == 0) {
+ fp = stdin;
+ }
+ else {
+ errno = 0;
+ fp = fopen(filename, "r");
+ if (fp == 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return;
+ }
+ }
+ current_filename = filename;
+ fprintf(outfp, ".lf 1 %s\n", filename);
+ string line;
+ current_lineno = 0;
+ for (;;) {
+ line.clear();
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF) {
+ if (line.length() > 0)
+ line += '\n';
+ break;
+ }
+ if (illegal_input_char(c))
+ error("illegal input character code %1", c);
+ else {
+ line += c;
+ if (c == '\n')
+ break;
+ }
+ }
+ int len = line.length();
+ if (len == 0)
+ break;
+ current_lineno++;
+ if (len >= 2 && line[0] == '.' && line[1] == '[') {
+ int start_lineno = current_lineno;
+ int start_of_line = 1;
+ string str;
+ string post;
+ string pre(line.contents() + 2, line.length() - 3);
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF) {
+ error_with_file_and_line(current_filename, start_lineno,
+ "missing `.]' line");
+ break;
+ }
+ if (start_of_line)
+ current_lineno++;
+ if (start_of_line && c == '.') {
+ int d = getc(fp);
+ if (d == ']') {
+ while ((d = getc(fp)) != '\n' && d != EOF) {
+ if (illegal_input_char(d))
+ error("illegal input character code %1", d);
+ else
+ post += d;
+ }
+ break;
+ }
+ if (d != EOF)
+ ungetc(d, fp);
+ }
+ if (illegal_input_char(c))
+ error("illegal input character code %1", c);
+ else
+ str += c;
+ start_of_line = (c == '\n');
+ }
+ if (is_list(str)) {
+ output_pending_line();
+ if (accumulate)
+ output_references();
+ else
+ error("found `$LIST$' but not accumulating references");
+ }
+ else {
+ unsigned flags = (accumulate
+ ? store_reference(str)
+ : immediately_handle_reference(str));
+ if (label_in_text) {
+ if (accumulate && outfp == stdout)
+ divert_to_temporary_file();
+ if (pending_line.length() == 0) {
+ warning("can't attach citation to previous line");
+ }
+ else
+ pending_line.set_length(pending_line.length() - 1);
+ string punct;
+ if (move_punctuation)
+ split_punct(pending_line, punct);
+ int have_text = pre.length() > 0 || post.length() > 0;
+ label_type lt = label_type(flags & ~(FORCE_LEFT_BRACKET
+ |FORCE_RIGHT_BRACKET));
+ if ((flags & FORCE_LEFT_BRACKET) || !have_text)
+ pending_line += PRE_LABEL_MARKER;
+ pending_line += pre;
+ char lm = LABEL_MARKER + (int)lt;
+ pending_line += lm;
+ pending_line += post;
+ if ((flags & FORCE_RIGHT_BRACKET) || !have_text)
+ pending_line += POST_LABEL_MARKER;
+ pending_line += punct;
+ pending_line += '\n';
+ }
+ }
+ need_syncing = 1;
+ }
+ else if (len >= 4
+ && line[0] == '.' && line[1] == 'l' && line[2] == 'f'
+ && (compatible_flag || line[3] == '\n' || line[3] == ' ')) {
+ pending_lf_lines += line;
+ line += '\0';
+ if (interpret_lf_args(line.contents() + 3))
+ current_lineno--;
+ }
+ else if (recognize_R1_R2
+ && len >= 4
+ && line[0] == '.' && line[1] == 'R' && line[2] == '1'
+ && (compatible_flag || line[3] == '\n' || line[3] == ' ')) {
+ line.clear();
+ int start_of_line = 1;
+ int start_lineno = current_lineno;
+ for (;;) {
+ int c = getc(fp);
+ if (c != EOF && start_of_line)
+ current_lineno++;
+ if (start_of_line && c == '.') {
+ c = getc(fp);
+ if (c == 'R') {
+ c = getc(fp);
+ if (c == '2') {
+ c = getc(fp);
+ if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
+ while (c != EOF && c != '\n')
+ c = getc(fp);
+ break;
+ }
+ else {
+ line += '.';
+ line += 'R';
+ line += '2';
+ }
+ }
+ else {
+ line += '.';
+ line += 'R';
+ }
+ }
+ else
+ line += '.';
+ }
+ if (c == EOF) {
+ error_with_file_and_line(current_filename, start_lineno,
+ "missing `.R2' line");
+ break;
+ }
+ if (illegal_input_char(c))
+ error("illegal input character code %1", int(c));
+ else {
+ line += c;
+ start_of_line = c == '\n';
+ }
+ }
+ output_pending_line();
+ if (accumulate)
+ output_references();
+ else
+ nreferences = 0;
+ process_commands(line, current_filename, start_lineno + 1);
+ need_syncing = 1;
+ }
+ else {
+ output_pending_line();
+ pending_line = line;
+ }
+ }
+ need_syncing = 0;
+ output_pending_line();
+ if (fp != stdin)
+ fclose(fp);
+}
+
+class label_processing_state {
+ enum {
+ NORMAL,
+ PENDING_LABEL,
+ PENDING_LABEL_POST,
+ PENDING_LABEL_POST_PRE,
+ PENDING_POST
+ } state;
+ label_type type; // type of pending labels
+ int count; // number of pending labels
+ reference **rptr; // pointer to next reference
+ int rcount; // number of references left
+ FILE *fp;
+ int handle_pending(int c);
+public:
+ label_processing_state(reference **, int, FILE *);
+ ~label_processing_state();
+ void process(int c);
+};
+
+static void output_pending_line()
+{
+ if (label_in_text && !accumulate && ncitations > 0) {
+ label_processing_state state(citation, ncitations, outfp);
+ int len = pending_line.length();
+ for (int i = 0; i < len; i++)
+ state.process((unsigned char)(pending_line[i]));
+ }
+ else
+ put_string(pending_line, outfp);
+ pending_line.clear();
+ if (pending_lf_lines.length() > 0) {
+ put_string(pending_lf_lines, outfp);
+ pending_lf_lines.clear();
+ }
+ if (!accumulate)
+ immediately_output_references();
+ if (need_syncing) {
+ fprintf(outfp, ".lf %d %s\n", current_lineno, current_filename);
+ need_syncing = 0;
+ }
+}
+
+static void split_punct(string &line, string &punct)
+{
+ const char *start = line.contents();
+ const char *end = start + line.length();
+ const char *ptr = start;
+ const char *last_token_start = 0;
+ for (;;) {
+ if (ptr >= end)
+ break;
+ last_token_start = ptr;
+ if (*ptr == PRE_LABEL_MARKER || *ptr == POST_LABEL_MARKER
+ || (*ptr >= LABEL_MARKER && *ptr < LABEL_MARKER + N_LABEL_TYPES))
+ ptr++;
+ else if (!get_token(&ptr, end))
+ break;
+ }
+ if (last_token_start) {
+ const token_info *ti = lookup_token(last_token_start, end);
+ if (ti->is_punct()) {
+ punct.append(last_token_start, end - last_token_start);
+ line.set_length(last_token_start - start);
+ }
+ }
+}
+
+static void divert_to_temporary_file()
+{
+ outfp = xtmpfile();
+}
+
+static void store_citation(reference *ref)
+{
+ if (ncitations >= citation_max) {
+ if (citation == 0)
+ citation = new reference*[citation_max = 100];
+ else {
+ reference **old_citation = citation;
+ citation_max *= 2;
+ citation = new reference *[citation_max];
+ memcpy(citation, old_citation, ncitations*sizeof(reference *));
+ a_delete old_citation;
+ }
+ }
+ citation[ncitations++] = ref;
+}
+
+static unsigned store_reference(const string &str)
+{
+ if (reference_hash_table == 0) {
+ reference_hash_table = new reference *[17];
+ hash_table_size = 17;
+ for (int i = 0; i < hash_table_size; i++)
+ reference_hash_table[i] = 0;
+ }
+ unsigned flags;
+ reference *ref = make_reference(str, &flags);
+ ref->compute_hash_code();
+ unsigned h = ref->hash();
+ reference **ptr;
+ for (ptr = reference_hash_table + (h % hash_table_size);
+ *ptr != 0;
+ ((ptr == reference_hash_table)
+ ? (ptr = reference_hash_table + hash_table_size - 1)
+ : --ptr))
+ if (same_reference(**ptr, *ref))
+ break;
+ if (*ptr != 0) {
+ if (ref->is_merged())
+ warning("fields ignored because reference already used");
+ delete ref;
+ ref = *ptr;
+ }
+ else {
+ *ptr = ref;
+ ref->set_number(nreferences);
+ nreferences++;
+ ref->pre_compute_label();
+ ref->compute_sort_key();
+ if (nreferences*2 >= hash_table_size) {
+ // Rehash it.
+ reference **old_table = reference_hash_table;
+ int old_size = hash_table_size;
+ hash_table_size = next_size(hash_table_size);
+ reference_hash_table = new reference*[hash_table_size];
+ int i;
+ for (i = 0; i < hash_table_size; i++)
+ reference_hash_table[i] = 0;
+ for (i = 0; i < old_size; i++)
+ if (old_table[i]) {
+ reference **p;
+ for (p = (reference_hash_table
+ + (old_table[i]->hash() % hash_table_size));
+ *p;
+ ((p == reference_hash_table)
+ ? (p = reference_hash_table + hash_table_size - 1)
+ : --p))
+ ;
+ *p = old_table[i];
+ }
+ a_delete old_table;
+ }
+ }
+ if (label_in_text)
+ store_citation(ref);
+ return flags;
+}
+
+unsigned immediately_handle_reference(const string &str)
+{
+ unsigned flags;
+ reference *ref = make_reference(str, &flags);
+ ref->set_number(nreferences);
+ if (label_in_text || label_in_reference) {
+ ref->pre_compute_label();
+ ref->immediate_compute_label();
+ }
+ nreferences++;
+ store_citation(ref);
+ return flags;
+}
+
+static void immediately_output_references()
+{
+ for (int i = 0; i < ncitations; i++) {
+ reference *ref = citation[i];
+ if (label_in_reference) {
+ fputs(".ds [F ", outfp);
+ const string &label = ref->get_label(NORMAL_LABEL);
+ if (label.length() > 0
+ && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
+ putc('"', outfp);
+ put_string(label, outfp);
+ putc('\n', outfp);
+ }
+ ref->output(outfp);
+ delete ref;
+ }
+ ncitations = 0;
+}
+
+static void output_citation_group(reference **v, int n, label_type type,
+ FILE *fp)
+{
+ if (sort_adjacent_labels) {
+ // Do an insertion sort. Usually n will be very small.
+ for (int i = 1; i < n; i++) {
+ int num = v[i]->get_number();
+ reference *temp = v[i];
+ int j;
+ for (j = i - 1; j >= 0 && v[j]->get_number() > num; j--)
+ v[j + 1] = v[j];
+ v[j + 1] = temp;
+ }
+ }
+ // This messes up if !accumulate.
+ if (accumulate && n > 1) {
+ // remove duplicates
+ int j = 1;
+ for (int i = 1; i < n; i++)
+ if (v[i]->get_label(type) != v[i - 1]->get_label(type))
+ v[j++] = v[i];
+ n = j;
+ }
+ string merged_label;
+ for (int i = 0; i < n; i++) {
+ int nmerged = v[i]->merge_labels(v + i + 1, n - i - 1, type, merged_label);
+ if (nmerged > 0) {
+ put_string(merged_label, fp);
+ i += nmerged;
+ }
+ else
+ put_string(v[i]->get_label(type), fp);
+ if (i < n - 1)
+ put_string(sep_label, fp);
+ }
+}
+
+
+label_processing_state::label_processing_state(reference **p, int n, FILE *f)
+: state(NORMAL), count(0), rptr(p), rcount(n), fp(f)
+{
+}
+
+label_processing_state::~label_processing_state()
+{
+ int handled = handle_pending(EOF);
+ assert(!handled);
+ assert(rcount == 0);
+}
+
+int label_processing_state::handle_pending(int c)
+{
+ switch (state) {
+ case NORMAL:
+ break;
+ case PENDING_LABEL:
+ if (c == POST_LABEL_MARKER) {
+ state = PENDING_LABEL_POST;
+ return 1;
+ }
+ else {
+ output_citation_group(rptr, count, type, fp);
+ rptr += count ;
+ rcount -= count;
+ state = NORMAL;
+ }
+ break;
+ case PENDING_LABEL_POST:
+ if (c == PRE_LABEL_MARKER) {
+ state = PENDING_LABEL_POST_PRE;
+ return 1;
+ }
+ else {
+ output_citation_group(rptr, count, type, fp);
+ rptr += count;
+ rcount -= count;
+ put_string(post_label, fp);
+ state = NORMAL;
+ }
+ break;
+ case PENDING_LABEL_POST_PRE:
+ if (c >= LABEL_MARKER
+ && c < LABEL_MARKER + N_LABEL_TYPES
+ && c - LABEL_MARKER == type) {
+ count += 1;
+ state = PENDING_LABEL;
+ return 1;
+ }
+ else {
+ output_citation_group(rptr, count, type, fp);
+ rptr += count;
+ rcount -= count;
+ put_string(sep_label, fp);
+ state = NORMAL;
+ }
+ break;
+ case PENDING_POST:
+ if (c == PRE_LABEL_MARKER) {
+ put_string(sep_label, fp);
+ state = NORMAL;
+ return 1;
+ }
+ else {
+ put_string(post_label, fp);
+ state = NORMAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+void label_processing_state::process(int c)
+{
+ if (handle_pending(c))
+ return;
+ assert(state == NORMAL);
+ switch (c) {
+ case PRE_LABEL_MARKER:
+ put_string(pre_label, fp);
+ state = NORMAL;
+ break;
+ case POST_LABEL_MARKER:
+ state = PENDING_POST;
+ break;
+ case LABEL_MARKER:
+ case LABEL_MARKER + 1:
+ count = 1;
+ state = PENDING_LABEL;
+ type = label_type(c - LABEL_MARKER);
+ break;
+ default:
+ state = NORMAL;
+ putc(c, fp);
+ break;
+ }
+}
+
+extern "C" {
+
+int rcompare(const void *p1, const void *p2)
+{
+ return compare_reference(**(reference **)p1, **(reference **)p2);
+}
+
+}
+
+void output_references()
+{
+ assert(accumulate);
+ if (nreferences > 0) {
+ int j = 0;
+ int i;
+ for (i = 0; i < hash_table_size; i++)
+ if (reference_hash_table[i] != 0)
+ reference_hash_table[j++] = reference_hash_table[i];
+ assert(j == nreferences);
+ for (; j < hash_table_size; j++)
+ reference_hash_table[j] = 0;
+ qsort(reference_hash_table, nreferences, sizeof(reference*), rcompare);
+ for (i = 0; i < nreferences; i++)
+ reference_hash_table[i]->set_number(i);
+ compute_labels(reference_hash_table, nreferences);
+ }
+ if (outfp != stdout) {
+ rewind(outfp);
+ {
+ label_processing_state state(citation, ncitations, stdout);
+ int c;
+ while ((c = getc(outfp)) != EOF)
+ state.process(c);
+ }
+ ncitations = 0;
+ fclose(outfp);
+ outfp = stdout;
+ }
+ if (nreferences > 0) {
+ fputs(".]<\n", outfp);
+ for (int i = 0; i < nreferences; i++) {
+ if (sort_fields.length() > 0)
+ reference_hash_table[i]->print_sort_key_comment(outfp);
+ if (label_in_reference) {
+ fputs(".ds [F ", outfp);
+ const string &label = reference_hash_table[i]->get_label(NORMAL_LABEL);
+ if (label.length() > 0
+ && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
+ putc('"', outfp);
+ put_string(label, outfp);
+ putc('\n', outfp);
+ }
+ reference_hash_table[i]->output(outfp);
+ delete reference_hash_table[i];
+ reference_hash_table[i] = 0;
+ }
+ fputs(".]>\n", outfp);
+ nreferences = 0;
+ }
+ clear_labels();
+}
+
+static reference *find_reference(const char *query, int query_len)
+{
+ // This is so that error messages look better.
+ while (query_len > 0 && csspace(query[query_len - 1]))
+ query_len--;
+ string str;
+ for (int i = 0; i < query_len; i++)
+ str += query[i] == '\n' ? ' ' : query[i];
+ str += '\0';
+ possibly_load_default_database();
+ search_list_iterator iter(&database_list, str.contents());
+ reference_id rid;
+ const char *start;
+ int len;
+ if (!iter.next(&start, &len, &rid)) {
+ error("no matches for `%1'", str.contents());
+ return 0;
+ }
+ const char *end = start + len;
+ while (start < end) {
+ if (*start == '%')
+ break;
+ while (start < end && *start++ != '\n')
+ ;
+ }
+ if (start >= end) {
+ error("found a reference for `%1' but it didn't contain any fields",
+ str.contents());
+ return 0;
+ }
+ reference *result = new reference(start, end - start, &rid);
+ if (iter.next(&start, &len, &rid))
+ warning("multiple matches for `%1'", str.contents());
+ return result;
+}
+
+static reference *make_reference(const string &str, unsigned *flagsp)
+{
+ const char *start = str.contents();
+ const char *end = start + str.length();
+ const char *ptr = start;
+ while (ptr < end) {
+ if (*ptr == '%')
+ break;
+ while (ptr < end && *ptr++ != '\n')
+ ;
+ }
+ *flagsp = 0;
+ for (; start < ptr; start++) {
+ if (*start == '#')
+ *flagsp = (SHORT_LABEL | (*flagsp & (FORCE_RIGHT_BRACKET
+ | FORCE_LEFT_BRACKET)));
+ else if (*start == '[')
+ *flagsp |= FORCE_LEFT_BRACKET;
+ else if (*start == ']')
+ *flagsp |= FORCE_RIGHT_BRACKET;
+ else if (!csspace(*start))
+ break;
+ }
+ if (start >= end) {
+ error("empty reference");
+ return new reference;
+ }
+ reference *database_ref = 0;
+ if (start < ptr)
+ database_ref = find_reference(start, ptr - start);
+ reference *inline_ref = 0;
+ if (ptr < end)
+ inline_ref = new reference(ptr, end - ptr);
+ if (inline_ref) {
+ if (database_ref) {
+ database_ref->merge(*inline_ref);
+ delete inline_ref;
+ return database_ref;
+ }
+ else
+ return inline_ref;
+ }
+ else if (database_ref)
+ return database_ref;
+ else
+ return new reference;
+}
+
+static void do_ref(const string &str)
+{
+ if (accumulate)
+ (void)store_reference(str);
+ else {
+ (void)immediately_handle_reference(str);
+ immediately_output_references();
+ }
+}
+
+static void trim_blanks(string &str)
+{
+ const char *start = str.contents();
+ const char *end = start + str.length();
+ while (end > start && end[-1] != '\n' && csspace(end[-1]))
+ --end;
+ str.set_length(end - start);
+}
+
+void do_bib(const char *filename)
+{
+ FILE *fp;
+ if (strcmp(filename, "-") == 0)
+ fp = stdin;
+ else {
+ errno = 0;
+ fp = fopen(filename, "r");
+ if (fp == 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return;
+ }
+ current_filename = filename;
+ }
+ enum {
+ START, MIDDLE, BODY, BODY_START, BODY_BLANK, BODY_DOT
+ } state = START;
+ string body;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ if (illegal_input_char(c)) {
+ error("illegal input character code %1", c);
+ continue;
+ }
+ switch (state) {
+ case START:
+ if (c == '%') {
+ body = c;
+ state = BODY;
+ }
+ else if (c != '\n')
+ state = MIDDLE;
+ break;
+ case MIDDLE:
+ if (c == '\n')
+ state = START;
+ break;
+ case BODY:
+ body += c;
+ if (c == '\n')
+ state = BODY_START;
+ break;
+ case BODY_START:
+ if (c == '\n') {
+ do_ref(body);
+ state = START;
+ }
+ else if (c == '.')
+ state = BODY_DOT;
+ else if (csspace(c)) {
+ state = BODY_BLANK;
+ body += c;
+ }
+ else {
+ body += c;
+ state = BODY;
+ }
+ break;
+ case BODY_BLANK:
+ if (c == '\n') {
+ trim_blanks(body);
+ do_ref(body);
+ state = START;
+ }
+ else if (csspace(c))
+ body += c;
+ else {
+ body += c;
+ state = BODY;
+ }
+ break;
+ case BODY_DOT:
+ if (c == ']') {
+ do_ref(body);
+ state = MIDDLE;
+ }
+ else {
+ body += '.';
+ body += c;
+ state = c == '\n' ? BODY_START : BODY;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ if (c == '\n')
+ current_lineno++;
+ }
+ switch (state) {
+ case START:
+ case MIDDLE:
+ break;
+ case BODY:
+ body += '\n';
+ do_ref(body);
+ break;
+ case BODY_DOT:
+ case BODY_START:
+ do_ref(body);
+ break;
+ case BODY_BLANK:
+ trim_blanks(body);
+ do_ref(body);
+ break;
+ }
+ fclose(fp);
+}
+
+// from the Dragon Book
+
+unsigned hash_string(const char *s, int len)
+{
+ const char *end = s + len;
+ unsigned h = 0, g;
+ while (s < end) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ return h;
+}
+
+int next_size(int n)
+{
+ static const int table_sizes[] = {
+ 101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009,
+ 80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009,
+ 16000057, 32000011, 64000031, 128000003, 0
+ };
+
+ const int *p;
+ for (p = table_sizes; *p <= n && *p != 0; p++)
+ ;
+ assert(*p != 0);
+ return *p;
+}
+
diff --git a/contrib/groff/src/preproc/refer/refer.h b/contrib/groff/src/preproc/refer/refer.h
new file mode 100644
index 000000000000..f0ab3cd70957
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/refer.h
@@ -0,0 +1,78 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "cmap.h"
+
+#include "defs.h"
+
+unsigned hash_string(const char *, int);
+int next_size(int);
+
+extern string capitalize_fields;
+extern string reverse_fields;
+extern string abbreviate_fields;
+extern string period_before_last_name;
+extern string period_before_initial;
+extern string period_before_hyphen;
+extern string period_before_other;
+extern string sort_fields;
+extern int annotation_field;
+extern string annotation_macro;
+extern string discard_fields;
+extern string articles;
+extern int abbreviate_label_ranges;
+extern string label_range_indicator;
+extern int date_as_label;
+extern string join_authors_exactly_two;
+extern string join_authors_last_two;
+extern string join_authors_default;
+extern string separate_label_second_parts;
+extern string et_al;
+extern int et_al_min_elide;
+extern int et_al_min_total;
+
+extern int compatible_flag;
+
+extern int set_label_spec(const char *);
+extern int set_date_label_spec(const char *);
+extern int set_short_label_spec(const char *);
+
+extern int short_label_flag;
+
+void clear_labels();
+void command_error(const char *,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+
+struct reference;
+
+void compute_labels(reference **, int);
diff --git a/contrib/groff/src/preproc/refer/refer.man b/contrib/groff/src/preproc/refer/refer.man
new file mode 100644
index 000000000000..13708cf0e568
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/refer.man
@@ -0,0 +1,1302 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.\" The BSD man macros can't handle " in arguments to font change macros,
+.\" so use \(ts instead of ".
+.tr \(ts"
+.TH @G@REFER @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@refer \- preprocess bibliographic references for groff
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fB@g@refer 'u
+.ti \niu
+.B @g@refer
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-benvCPRS
+.OP \-a n
+.OP \-c fields
+.OP \-f n
+.OP \-i fields
+.OP \-k field
+.OP \-l m,n
+.OP \-p filename
+.OP \-s fields
+.OP \-t n
+.OP \-B field.macro
+.RI [\ filename \|.\|.\|.\ ]
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+This file documents the GNU version of
+.BR refer ,
+which is part of the groff document formatting system.
+.B refer
+copies the contents of
+.IR filename \|.\|.\|.
+to the standard output,
+except that lines between
+.B .[
+and
+.B .]
+are interpreted as citations,
+and lines between
+.B .R1
+and
+.B .R2
+are interpreted as commands about how citations are to be processed.
+.LP
+Each citation specifies a reference.
+The citation can specify a reference that is contained in
+a bibliographic database by giving a set of keywords
+that only that reference contains.
+Alternatively it can specify a reference by supplying a database
+record in the citation.
+A combination of these alternatives is also possible.
+.LP
+For each citation,
+.B refer
+can produce a mark in the text.
+This mark consists of some label which can be separated from
+the text and from other labels in various ways.
+For each reference it also outputs
+.B groff
+commands that can be used by a macro package to produce a formatted
+reference for each citation.
+The output of
+.B refer
+must therefore be processed using a suitable macro package.
+The
+.B \-ms
+and
+.B \-me
+macros are both suitable.
+The commands to format a citation's reference can be output immediately after
+the citation,
+or the references may be accumulated,
+and the commands output at some later point.
+If the references are accumulated, then multiple citations of the same
+reference will produce a single formatted reference.
+.LP
+The interpretation of lines between
+.B .R1
+and
+.B .R2
+as commands is a new feature of GNU refer.
+Documents making use of this feature can still be processed by
+Unix refer just by adding the lines
+.RS
+.LP
+.nf
+.ft B
+\&.de R1
+\&.ig R2
+\&..
+.ft
+.fi
+.RE
+to the beginning of the document.
+This will cause
+.B troff
+to ignore everything between
+.B .R1
+and
+.BR .R2 .
+The effect of some commands can also be achieved by options.
+These options are supported mainly for compatibility with Unix refer.
+It is usually more convenient to use commands.
+.LP
+.B refer
+generates
+.B .lf
+lines so that filenames and line numbers in messages produced
+by commands that read
+.B refer
+output will be correct;
+it also interprets lines beginning with
+.B .lf
+so that filenames and line numbers in the messages and
+.B .lf
+lines that it produces will be accurate even if the input has been
+preprocessed by a command such as
+.BR @g@soelim (@MAN1EXT@).
+.SH OPTIONS
+.LP
+Most options are equivalent to commands
+(for a description of these commands see the
+.B Commands
+subsection):
+.TP
+.B \-b
+.B
+no-label-in-text; no-label-in-reference
+.TP
+.B \-e
+.B accumulate
+.TP
+.B \-n
+.B no-default-database
+.TP
+.B \-C
+.B compatible
+.TP
+.B \-P
+.B move-punctuation
+.TP
+.B \-S
+.B
+label "(A.n|Q) ', ' (D.y|D)"; bracket-label " (" ) "; "
+.TP
+.BI \-a n
+.B reverse
+.BI A n
+.TP
+.BI \-c fields
+.B capitalize
+.I fields
+.TP
+.BI \-f n
+.B label
+.BI % n
+.TP
+.BI \-i fields
+.B search-ignore
+.I fields
+.TP
+.B \-k
+.B label
+.B L\(ti%a
+.TP
+.BI \-k field
+.B label
+.IB field \(ti%a
+.TP
+.B \-l
+.B label
+.BI A.nD.y%a
+.TP
+.BI \-l m
+.B label
+.BI A.n+ m D.y%a
+.TP
+.BI \-l, n
+.B label
+.BI A.nD.y\- n %a
+.TP
+.BI \-l m , n
+.B label
+.BI A.n+ m D.y\- n %a
+.TP
+.BI \-p filename
+.B database
+.I filename
+.TP
+.BI \-s spec
+.B sort
+.I spec
+.TP
+.BI \-t n
+.B search-truncate
+.I n
+.LP
+These options are equivalent to the following commands with the
+addition that the filenames specified on the command line are
+processed as if they were arguments to the
+.B bibliography
+command instead of in the normal way:
+.TP
+.B \-B
+.B
+annotate X AP; no-label-in-reference
+.TP
+.BI \-B field . macro
+.B annotate
+.I field
+.IB macro ;
+.B no-label-in-reference
+.LP
+The following options have no equivalent commands:
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-R
+Don't recognize lines beginning with
+.BR .R1 / .R2 .
+.SH USAGE
+.SS Bibliographic databases
+The bibliographic database is a text file consisting of records
+separated by one or more blank lines.
+Within each record fields start with a
+.B %
+at the beginning of a line.
+Each field has a one character name that immediately follows the
+.BR % .
+It is best to use only upper and lower case letters for the names
+of fields.
+The name of the field should be followed by exactly one space,
+and then by the contents of the field.
+Empty fields are ignored.
+The conventional meaning of each field is as follows:
+.TP
+.B A
+The name of an author.
+If the name contains a title such as
+.B Jr.
+at the end,
+it should be separated from the last name by a comma.
+There can be multiple occurrences of the
+.B A
+field.
+The order is significant.
+It is a good idea always to supply an
+.B A
+field or a
+.B Q
+field.
+.TP
+.B B
+For an article that is part of a book, the title of the book
+.TP
+.B C
+The place (city) of publication.
+.TP
+.B D
+The date of publication.
+The year should be specified in full.
+If the month is specified, the name rather than the number of the month
+should be used, but only the first three letters are required.
+It is a good idea always to supply a
+.B D
+field;
+if the date is unknown, a value such as
+.B in press
+or
+.B unknown
+can be used.
+.TP
+.B E
+For an article that is part of a book, the name of an editor of the book.
+Where the work has editors and no authors,
+the names of the editors should be given as
+.B A
+fields and
+.B ,\ (ed)
+or
+.B ,\ (eds)
+should be appended to the last author.
+.TP
+.B G
+US Government ordering number.
+.TP
+.B I
+The publisher (issuer).
+.TP
+.B J
+For an article in a journal, the name of the journal.
+.TP
+.B K
+Keywords to be used for searching.
+.TP
+.B L
+Label.
+.TP
+.B N
+Journal issue number.
+.TP
+.B O
+Other information.
+This is usually printed at the end of the reference.
+.TP
+.B P
+Page number.
+A range of pages can be specified as
+.IB m \- n\fR.
+.TP
+.B Q
+The name of the author, if the author is not a person.
+This will only be used if there are no
+.B A
+fields.
+There can only be one
+.B Q
+field.
+.TP
+.B R
+Technical report number.
+.TP
+.B S
+Series name.
+.TP
+.B T
+Title.
+For an article in a book or journal,
+this should be the title of the article.
+.TP
+.B V
+Volume number of the journal or book.
+.TP
+.B X
+Annotation.
+.LP
+For all fields except
+.B A
+and
+.BR E ,
+if there is more than one occurrence of a particular field in a record,
+only the last such field will be used.
+.LP
+If accent strings are used, they should follow the character to be accented.
+This means that the
+.B AM
+macro must be used with the
+.B \-ms
+macros.
+Accent strings should not be quoted:
+use one
+.B \e
+rather than two.
+.SS Citations
+The format of a citation is
+.RS
+.BI .[ opening-text
+.br
+.I
+flags keywords
+.br
+.I fields
+.br
+.BI .] closing-text
+.RE
+.LP
+The
+.IR opening-text ,
+.IR closing-text
+and
+.I flags
+components are optional.
+Only one of the
+.I keywords
+and
+.I fields
+components need be specified.
+.LP
+The
+.I keywords
+component says to search the bibliographic databases for a reference
+that contains all the words in
+.IR keywords .
+It is an error if more than one reference if found.
+.LP
+The
+.I fields
+components specifies additional fields to replace or supplement
+those specified in the reference.
+When references are being accumulated and the
+.I keywords
+component is non-empty,
+then additional fields should be specified only on the first
+occasion that a particular reference is cited,
+and will apply to all citations of that reference.
+.LP
+The
+.I opening-text
+and
+.I closing-text
+component specifies strings to be used to bracket the label instead
+of the strings specified in the
+.B bracket-label
+command.
+If either of these components is non-empty,
+the strings specified in the
+.B bracket-label
+command will not be used;
+this behaviour can be altered using the
+.B [
+and
+.B ]
+flags.
+Note that leading and trailing spaces are significant for these components.
+.LP
+The
+.I flags
+component is a list of
+non-alphanumeric characters each of which modifies the treatment
+of this particular citation.
+Unix refer will treat these flags as part of the keywords and
+so will ignore them since they are non-alphanumeric.
+The following flags are currently recognized:
+.TP
+.B #
+This says to use the label specified by the
+.B short-label
+command,
+instead of that specified by the
+.B label
+command.
+If no short label has been specified, the normal label will be used.
+Typically the short label is used with author-date labels
+and consists of only the date and possibly a disambiguating letter;
+the
+.B #
+is supposed to be suggestive of a numeric type of label.
+.TP
+.B [
+Precede
+.I opening-text
+with the first string specified in the
+.B bracket-label
+command.
+.TP
+.B ]
+Follow
+.I closing-text
+with the second string specified in the
+.B bracket-label
+command.
+.LP
+One advantages of using the
+.B [
+and
+.B ]
+flags rather than including the brackets in
+.I opening-text
+and
+.I closing-text
+is that
+you can change the style of bracket used in the document just by changing the
+.B bracket-label
+command.
+Another advantage is that sorting and merging of citations
+will not necessarily be inhibited if the flags are used.
+.LP
+If a label is to be inserted into the text,
+it will be attached to the line preceding the
+.B .[
+line.
+If there is no such line, then an extra line will be inserted before the
+.B .[
+line and a warning will be given.
+.LP
+There is no special notation for making a citation to multiple references.
+Just use a sequence of citations, one for each reference.
+Don't put anything between the citations.
+The labels for all the citations will be attached to the line preceding
+the first citation.
+The labels may also be sorted or merged.
+See the description of the
+.B <>
+label expression, and of the
+.B sort-adjacent-labels
+and
+.B abbreviate-label-ranges
+command.
+A label will not be merged if its citation has a non-empty
+.I opening-text
+or
+.IR closing-text .
+However, the labels for a citation using the
+.B ]
+flag and without any
+.I closing-text
+immediately followed by a citation using the
+.B [
+flag and without any
+.I opening-text
+may be sorted and merged
+even though the first citation's
+.I opening-text
+or the second citation's
+.I closing-text
+is non-empty.
+(If you wish to prevent this just make the first citation's
+.I closing-text
+.BR \e& .)
+.SS Commands
+Commands are contained between lines starting with
+.B .R1
+and
+.BR .R2 .
+Recognition of these lines can be prevented by the
+.B \-R
+option.
+When a
+.B .R1
+line is recognized any accumulated references are flushed out.
+Neither
+.B .R1
+nor
+.B .R2
+lines,
+nor anything between them
+is output.
+.LP
+Commands are separated by newlines or
+.BR ; s.
+.B #
+introduces a comment that extends to the end of the line
+(but does not conceal the newline).
+Each command is broken up into words.
+Words are separated by spaces or tabs.
+A word that begins with
+.B \(ts
+extends to the next
+.B \(ts
+that is not followed by another
+.BR \(ts .
+If there is no such
+.B \(ts
+the word extends to the end of the line.
+Pairs of
+.B \(ts
+in a word beginning with
+.B \(ts
+collapse to a single
+.BR \(ts .
+Neither
+.B #
+nor
+.B ;
+are recognized inside
+.BR \(ts s.
+A line can be continued by ending it with
+.BR \e ;
+this works everywhere except after a
+.BR # .
+.LP
+.ds n \fR*
+Each command
+.I name
+that is marked with \*n has an associated negative command
+.BI no- name
+that undoes the effect of
+.IR name .
+For example, the
+.B no-sort
+command specifies that references should not be sorted.
+The negative commands take no arguments.
+.LP
+In the following description each argument must be a single word;
+.I field
+is used for a single upper or lower case letter naming a field;
+.I fields
+is used for a sequence of such letters;
+.I m
+and
+.I n
+are used for a non-negative numbers;
+.I string
+is used for an arbitrary string;
+.I filename
+is used for the name of a file.
+.Tp \w'\fBabbreviate-label-ranges'u+2n
+.BI abbreviate\*n\ fields\ string1\ string2\ string3\ string4
+Abbreviate the first names of
+.IR fields .
+An initial letter will be separated from another initial letter by
+.IR string1 ,
+from the last name by
+.IR string2 ,
+and from anything else
+(such as a
+.B von
+or
+.BR de )
+by
+.IR string3 .
+These default to a period followed by a space.
+In a hyphenated first name,
+the initial of the first part of the name will be separated from the hyphen by
+.IR string4 ;
+this defaults to a period.
+No attempt is made to handle any ambiguities that might
+result from abbreviation.
+Names are abbreviated before sorting and before
+label construction.
+.TP
+.BI abbreviate-label-ranges\*n\ string
+Three or more adjacent labels that refer to consecutive references
+will be abbreviated to a label consisting
+of the first label, followed by
+.I string
+followed by the last label.
+This is mainly useful with numeric labels.
+If
+.I string
+is omitted it defaults to
+.BR \- .
+.TP
+.B accumulate\*n
+Accumulate references instead of writing out each reference
+as it is encountered.
+Accumulated references will be written out whenever a reference
+of the form
+.RS
+.IP
+.B .[
+.br
+.B $LIST$
+.br
+.B .]
+.LP
+is encountered,
+after all input files hve been processed,
+and whenever
+.B .R1
+line is recognized.
+.RE
+.TP
+.BI annotate\*n\ field\ string
+.I field
+is an annotation;
+print it at the end of the reference as a paragraph preceded by the line
+.RS
+.IP
+.BI . string
+.LP
+If
+.I macro
+is omitted it will default to
+.BR AP ;
+if
+.I field
+is also omitted it will default to
+.BR X .
+Only one field can be an annotation.
+.RE
+.TP
+.BI articles\ string \fR\|.\|.\|.
+.IR string \|.\|.\|.
+are definite or indefinite articles, and should be ignored at the beginning of
+.B T
+fields when sorting.
+Initially,
+.BR the ,
+.B a
+and
+.B an
+are recognized as articles.
+.TP
+.BI bibliography\ filename \fR\|.\|.\|.
+Write out all the references contained in the bibliographic databases
+.IR filename \|.\|.\|.
+.TP
+.BI bracket-label\ string1\ string2\ string3
+In the text, bracket each label
+with
+.I string1
+and
+.IR string2 .
+An occurrence of
+.I string2
+immediately followed by
+.I string1
+will be turned into
+.IR string3 .
+The default behaviour is
+.RS
+.IP
+.B
+bracket-label \e*([. \e*(.] ", "
+.RE
+.TP
+.BI capitalize\ fields
+Convert
+.I fields
+to caps and small caps.
+.TP
+.B compatible\*n
+Recognize
+.B .R1
+and
+.B .R2
+even when followed by a character other than space or newline.
+.TP
+.BI database\ filename \fR\|.\|.\|.
+Search the bibliographic databases
+.IR filename \|.\|.\|.
+For each
+.I filename
+if an index
+.IB filename @INDEX_SUFFIX@
+created by
+.BR @g@indxbib (@MAN1EXT@)
+exists, then it will be searched instead;
+each index can cover multiple databases.
+.TP
+.BI date-as-label\*n\ string
+.I string
+is a label expression that specifies a string with which to replace the
+.B D
+field after constructing the label.
+See the
+.B "Label expressions"
+subsection for a description of label expressions.
+This command is useful if you do not want explicit labels in the
+reference list, but instead want to handle any necessary
+disambiguation by qualifying the date in some way.
+The label used in the text would typically be some combination of the
+author and date.
+In most cases you should also use the
+.B no-label-in-reference
+command.
+For example,
+.RS
+.IP
+.B
+date-as-label D.+yD.y%a*D.-y
+.LP
+would attach a disambiguating letter to the year part of the
+.B D
+field in the reference.
+.RE
+.TP
+.B default-database\*n
+The default database should be searched.
+This is the default behaviour, so the negative version of
+this command is more useful.
+refer determines whether the default database should be searched
+on the first occasion that it needs to do a search.
+Thus a
+.B no-default-database
+command must be given before then,
+in order to be effective.
+.TP
+.BI discard\*n\ fields
+When the reference is read,
+.I fields
+should be discarded;
+no string definitions for
+.I fields
+will be output.
+Initially,
+.I fields
+are
+.BR XYZ .
+.TP
+.BI et-al\*n\ string\ m\ n
+Control use of
+.B
+et al
+in the evaluation of
+.B @
+expressions in label expressions.
+If the number of authors needed to make the author sequence
+unambiguous is
+.I u
+and the total number of authors is
+.I t
+then the last
+.IR t \|\-\| u
+authors will be replaced by
+.I string
+provided that
+.IR t \|\-\| u
+is not less than
+.I m
+and
+.I t
+is not less than
+.IR n .
+The default behaviour is
+.RS
+.IP
+.B
+et-al " et al" 2 3
+.RE
+.TP
+.BI include\ filename
+Include
+.I filename
+and interpret the contents as commands.
+.TP
+.BI join-authors\ string1\ string2\ string3
+This says how authors should be joined together.
+When there are exactly two authors, they will be joined with
+.IR string1 .
+When there are more than two authors, all but the last two will
+be joined with
+.IR string2 ,
+and the last two authors will be joined with
+.IR string3 .
+If
+.I string3
+is omitted,
+it will default to
+.IR string1 ;
+if
+.I string2
+is also omitted it will also default to
+.IR string1 .
+For example,
+.RS
+.IP
+.B
+join-authors " and " ", " ", and "
+.LP
+will restore the default method for joining authors.
+.RE
+.TP
+.B label-in-reference\*n
+When outputting the reference,
+define the string
+.B [F
+to be the reference's label.
+This is the default behaviour; so the negative version
+of this command is more useful.
+.TP
+.B label-in-text\*n
+For each reference output a label in the text.
+The label will be separated from the surrounding text as described in the
+.B bracket-label
+command.
+This is the default behaviour; so the negative version
+of this command is more useful.
+.TP
+.BI label\ string
+.I string
+is a label expression describing how to label each reference.
+.TP
+.BI separate-label-second-parts\ string
+When merging two-part labels, separate the second part of the second
+label from the first label with
+.IR string .
+See the description of the
+.B <>
+label expression.
+.TP
+.B move-punctuation\*n
+In the text, move any punctuation at the end of line past the label.
+It is usually a good idea to give this command unless you are using
+superscripted numbers as labels.
+.TP
+.BI reverse\*n\ string
+Reverse the fields whose names
+are in
+.IR string .
+Each field name can be followed by a number which says
+how many such fields should be reversed.
+If no number is given for a field, all such fields will be reversed.
+.TP
+.BI search-ignore\*n\ fields
+While searching for keys in databases for which no index exists,
+ignore the contents of
+.IR fields .
+Initially, fields
+.B XYZ
+are ignored.
+.TP
+.BI search-truncate\*n\ n
+Only require the first
+.I n
+characters of keys to be given.
+In effect when searching for a given key
+words in the database are truncated to the maximum of
+.I n
+and the length of the key.
+Initially
+.I n
+is 6.
+.TP
+.BI short-label\*n\ string
+.I string
+is a label expression that specifies an alternative (usually shorter)
+style of label.
+This is used when the
+.B #
+flag is given in the citation.
+When using author-date style labels, the identity of the author
+or authors is sometimes clear from the context, and so it
+may be desirable to omit the author or authors from the label.
+The
+.B short-label
+command will typically be used to specify a label containing just
+a date and possibly a disambiguating letter.
+.TP
+.BI sort\*n\ string
+Sort references according to
+.BR string .
+References will automatically be accumulated.
+.I string
+should be a list of field names, each followed by a number,
+indicating how many fields with the name should be used for sorting.
+.B +
+can be used to indicate that all the fields with the name should be used.
+Also
+.B .
+can be used to indicate the references should be sorted using the
+(tentative) label.
+(The
+.B
+Label expressions
+subsection describes the concept of a tentative label.)
+.TP
+.B sort-adjacent-labels\*n
+Sort labels that are adjacent in the text according to their
+position in the reference list.
+This command should usually be given if the
+.B abbreviate-label-ranges
+command has been given,
+or if the label expression contains a
+.B <>
+expression.
+This will have no effect unless references are being accumulated.
+.SS Label expressions
+.LP
+Label expressions can be evaluated both normally and tentatively.
+The result of normal evaluation is used for output.
+The result of tentative evaluation, called the
+.I
+tentative label,
+is used to gather the information
+that normal evaluation needs to disambiguate the label.
+Label expressions specified by the
+.B date-as-label
+and
+.B short-label
+commands are not evaluated tentatively.
+Normal and tentative evaluation are the same for all types
+of expression other than
+.BR @ ,
+.BR * ,
+and
+.B %
+expressions.
+The description below applies to normal evaluation,
+except where otherwise specified.
+.TP
+.I field
+.TQ
+.I field\ n
+The
+.IR n -th
+part of
+.IR field .
+If
+.I n
+is omitted, it defaults to 1.
+.TP
+.BI ' string '
+The characters in
+.I string
+literally.
+.TP
+.B @
+All the authors joined as specified by the
+.B join-authors
+command.
+The whole of each author's name will be used.
+However, if the references are sorted by author
+(that is the sort specification starts with
+.BR A+ ),
+then authors' last names will be used instead, provided that this does
+not introduce ambiguity,
+and also an initial subsequence of the authors may be used
+instead of all the authors, again provided that this does not
+introduce ambiguity.
+The use of only the last name for the
+.IR i -th
+author of some reference
+is considered to be ambiguous if
+there is some other reference,
+such that the first
+.IR i \|-\|1
+authors of the references are the same,
+the
+.IR i -th
+authors are not the same,
+but the
+.IR i -th
+authors' last names are the same.
+A proper initial subsequence of the sequence
+of authors for some reference is considered to be ambiguous if there is
+a reference with some other sequence of authors which also has
+that subsequence as a proper initial subsequence.
+When an initial subsequence of authors is used, the remaining
+authors are replaced by the string specified by the
+.B et-al
+command;
+this command may also specify additional requirements that must be
+met before an initial subsequence can be used.
+.B @
+tentatively evaluates to a canonical representation of the authors,
+such that authors that compare equally for sorting purpose
+will have the same representation.
+.TP
+.BI % n
+.TQ
+.B %a
+.TQ
+.B %A
+.TQ
+.B %i
+.TQ
+.B %I
+The serial number of the reference formatted according to the character
+following the
+.BR % .
+The serial number of a reference is 1 plus the number of earlier references
+with same tentative label as this reference.
+These expressions tentatively evaluate to an empty string.
+.TP
+.IB expr *
+If there is another reference with the same tentative label as
+this reference, then
+.IR expr ,
+otherwise an empty string.
+It tentatively evaluates to an empty string.
+.TP
+.IB expr + n
+.TQ
+.IB expr \- n
+The first
+.RB ( + )
+or last
+.RB ( \- )
+.I n
+upper or lower case letters or digits of
+.IR expr .
+Troff special characters (such as
+.BR \e('a )
+count as a single letter.
+Accent strings are retained but do not count towards the total.
+.TP
+.IB expr .l
+.I expr
+converted to lowercase.
+.TP
+.IB expr .u
+.I expr
+converted to uppercase.
+.TP
+.IB expr .c
+.I expr
+converted to caps and small caps.
+.TP
+.IB expr .r
+.I expr
+reversed so that the last name is first.
+.TP
+.IB expr .a
+.I expr
+with first names abbreviated.
+Note that fields specified in the
+.B abbreviate
+command are abbreviated before any labels are evaluated.
+Thus
+.B .a
+is useful only when you want a field to be abbreviated in a label
+but not in a reference.
+.TP
+.IB expr .y
+The year part of
+.IR expr .
+.TP
+.IB expr .+y
+The part of
+.I expr
+before the year, or the whole of
+.I expr
+if it does not contain a year.
+.TP
+.IB expr .\-y
+The part of
+.I expr
+after the year, or an empty string if
+.I expr
+does not contain a year.
+.TP
+.IB expr .n
+The last name part of
+.IR expr .
+.TP
+.IB expr1 \(ti expr2
+.I expr1
+except that if the last character of
+.I expr1
+is
+.B \-
+then it will be replaced by
+.IR expr2 .
+.TP
+.I expr1\ expr2
+The concatenation of
+.I expr1
+and
+.IR expr2 .
+.TP
+.IB expr1 | expr2
+If
+.I expr1
+is non-empty then
+.I expr1
+otherwise
+.IR expr2 .
+.TP
+.IB expr1 & expr2
+If
+.I expr1
+is non-empty
+then
+.I expr2
+otherwise an empty string.
+.TP
+.IB expr1 ? expr2 : expr3
+If
+.I expr1
+is non-empty
+then
+.I expr2
+otherwise
+.IR expr3 .
+.TP
+.BI < expr >
+The label is in two parts, which are separated by
+.IR expr .
+Two adjacent two-part labels which have the same first part will be
+merged by appending the second part of the second label onto the first
+label separated by the string specified in the
+.B separate-label-second-parts
+command (initially, a comma followed by a space); the resulting label
+will also be a two-part label with the same first part as before
+merging, and so additional labels can be merged into it.
+Note that it is permissible for the first part to be empty;
+this maybe desirable for expressions used in the
+.B short-label
+command.
+.TP
+.BI ( expr )
+The same as
+.IR expr .
+Used for grouping.
+.LP
+The above expressions are listed in order of precedence
+(highest first);
+.B &
+and
+.B |
+have the same precedence.
+.SS Macro interface
+Each reference starts with a call to the macro
+.BR ]- .
+The string
+.B [F
+will be defined to be the label for this reference,
+unless the
+.B no-label-in-reference
+command has been given.
+There then follows a series of string definitions,
+one for each field:
+string
+.BI [ X
+corresponds to field
+.IR X .
+The number register
+.B [P
+is set to 1 if the
+.B P
+field contains a range of pages.
+The
+.BR [T ,
+.B [A
+and
+.B [O
+number registers are set to 1 according as the
+.BR T ,
+.B A
+and
+.B O
+fields end with one of the characters
+.BR .?! .
+The
+.B [E
+number register will be set to 1 if the
+.B [E
+string contains more than one name.
+The reference is followed by a call to the
+.B ][
+macro.
+The first argument to this macro gives a number representing
+the type of the reference.
+If a reference contains a
+.B J
+field, it will be classified as type 1,
+otherwise if it contains a
+.B B
+field, it will type 3,
+otherwise if it contains a
+.B G
+or
+.B R
+field it will be type 4,
+otherwise if contains a
+.B I
+field it will be type 2,
+otherwise it will be type 0.
+The second argument is a symbolic name for the type:
+.BR other ,
+.BR journal-article ,
+.BR book ,
+.B article-in-book
+or
+.BR tech-report .
+Groups of references that have been accumulated
+or are produced by the
+.B bibliography
+command are preceded by a call to the
+.B ]<
+macro and followed by a call to the
+.B ]>
+macro.
+.SH FILES
+.Tp \w'\fB@DEFAULT_INDEX@'u+2n
+.B @DEFAULT_INDEX@
+Default database.
+.TP
+.IB file @INDEX_SUFFIX@
+Index files.
+.SH "SEE ALSO"
+.BR @g@indxbib (@MAN1EXT@),
+.BR @g@lookbib (@MAN1EXT@),
+.BR lkbib (@MAN1EXT@)
+.br
+.SH BUGS
+In label expressions,
+.B <>
+expressions are ignored inside
+.BI . char
+expressions.
diff --git a/contrib/groff/src/preproc/refer/token.cc b/contrib/groff/src/preproc/refer/token.cc
new file mode 100644
index 000000000000..1cf6890f24d4
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/token.cc
@@ -0,0 +1,378 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "refer.h"
+#include "token.h"
+
+#define TOKEN_TABLE_SIZE 1009
+// I believe in Icelandic thorn sorts after z.
+#define THORN_SORT_KEY "{"
+
+struct token_table_entry {
+ const char *tok;
+ token_info ti;
+ token_table_entry();
+};
+
+token_table_entry token_table[TOKEN_TABLE_SIZE];
+int ntokens = 0;
+
+static void skip_name(const char **ptr, const char *end)
+{
+ if (*ptr < end) {
+ switch (*(*ptr)++) {
+ case '(':
+ if (*ptr < end) {
+ *ptr += 1;
+ if (*ptr < end)
+ *ptr += 1;
+ }
+ break;
+ case '[':
+ while (*ptr < end)
+ if (*(*ptr)++ == ']')
+ break;
+ break;
+ }
+ }
+}
+
+int get_token(const char **ptr, const char *end)
+{
+ if (*ptr >= end)
+ return 0;
+ char c = *(*ptr)++;
+ if (c == '\\' && *ptr < end) {
+ switch (**ptr) {
+ default:
+ *ptr += 1;
+ break;
+ case '(':
+ case '[':
+ skip_name(ptr, end);
+ break;
+ case '*':
+ case 'f':
+ *ptr += 1;
+ skip_name(ptr, end);
+ break;
+ }
+ }
+ return 1;
+}
+
+token_info::token_info()
+: type(TOKEN_OTHER), sort_key(0), other_case(0)
+{
+}
+
+void token_info::set(token_type t, const char *sk, const char *oc)
+{
+ assert(oc == 0 || t == TOKEN_UPPER || t == TOKEN_LOWER);
+ type = t;
+ sort_key = sk;
+ other_case = oc;
+}
+
+void token_info::sortify(const char *start, const char *end, string &result)
+ const
+{
+ if (sort_key)
+ result += sort_key;
+ else if (type == TOKEN_UPPER || type == TOKEN_LOWER) {
+ for (; start < end; start++)
+ if (csalpha(*start))
+ result += cmlower(*start);
+ }
+}
+
+int token_info::sortify_non_empty(const char *start, const char *end) const
+{
+ if (sort_key)
+ return *sort_key != '\0';
+ if (type != TOKEN_UPPER && type != TOKEN_LOWER)
+ return 0;
+ for (; start < end; start++)
+ if (csalpha(*start))
+ return 1;
+ return 0;
+}
+
+
+void token_info::lower_case(const char *start, const char *end,
+ string &result) const
+{
+ if (type != TOKEN_UPPER) {
+ while (start < end)
+ result += *start++;
+ }
+ else if (other_case)
+ result += other_case;
+ else {
+ while (start < end)
+ result += cmlower(*start++);
+ }
+}
+
+void token_info::upper_case(const char *start, const char *end,
+ string &result) const
+{
+ if (type != TOKEN_LOWER) {
+ while (start < end)
+ result += *start++;
+ }
+ else if (other_case)
+ result += other_case;
+ else {
+ while (start < end)
+ result += cmupper(*start++);
+ }
+}
+
+token_table_entry::token_table_entry()
+: tok(0)
+{
+}
+
+static void store_token(const char *tok, token_type typ,
+ const char *sk = 0, const char *oc = 0)
+{
+ unsigned n = hash_string(tok, strlen(tok)) % TOKEN_TABLE_SIZE;
+ for (;;) {
+ if (token_table[n].tok == 0) {
+ if (++ntokens == TOKEN_TABLE_SIZE)
+ assert(0);
+ token_table[n].tok = tok;
+ break;
+ }
+ if (strcmp(tok, token_table[n].tok) == 0)
+ break;
+ if (n == 0)
+ n = TOKEN_TABLE_SIZE - 1;
+ else
+ --n;
+ }
+ token_table[n].ti.set(typ, sk, oc);
+}
+
+
+token_info default_token_info;
+
+const token_info *lookup_token(const char *start, const char *end)
+{
+ unsigned n = hash_string(start, end - start) % TOKEN_TABLE_SIZE;
+ for (;;) {
+ if (token_table[n].tok == 0)
+ break;
+ if (strlen(token_table[n].tok) == end - start
+ && memcmp(token_table[n].tok, start, end - start) == 0)
+ return &(token_table[n].ti);
+ if (n == 0)
+ n = TOKEN_TABLE_SIZE - 1;
+ else
+ --n;
+ }
+ return &default_token_info;
+}
+
+static void init_ascii()
+{
+ const char *p;
+ for (p = "abcdefghijklmnopqrstuvwxyz"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ store_token(strsave(buf), TOKEN_LOWER);
+ buf[0] = cmupper(buf[0]);
+ store_token(strsave(buf), TOKEN_UPPER);
+ }
+ for (p = "0123456789"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ const char *s = strsave(buf);
+ store_token(s, TOKEN_OTHER, s);
+ }
+ for (p = ".,:;?!"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ store_token(strsave(buf), TOKEN_PUNCT);
+ }
+ store_token("-", TOKEN_HYPHEN);
+}
+
+static void store_letter(const char *lower, const char *upper,
+ const char *sort_key = 0)
+{
+ store_token(lower, TOKEN_LOWER, sort_key, upper);
+ store_token(upper, TOKEN_UPPER, sort_key, lower);
+}
+
+static void init_letter(unsigned char uc_code, unsigned char lc_code,
+ const char *sort_key)
+{
+ char lbuf[2];
+ lbuf[0] = lc_code;
+ lbuf[1] = 0;
+ char ubuf[2];
+ ubuf[0] = uc_code;
+ ubuf[1] = 0;
+ store_letter(strsave(lbuf), strsave(ubuf), sort_key);
+}
+
+static void init_latin1()
+{
+ init_letter(0xc0, 0xe0, "a");
+ init_letter(0xc1, 0xe1, "a");
+ init_letter(0xc2, 0xe2, "a");
+ init_letter(0xc3, 0xe3, "a");
+ init_letter(0xc4, 0xe4, "a");
+ init_letter(0xc5, 0xe5, "a");
+ init_letter(0xc6, 0xe6, "ae");
+ init_letter(0xc7, 0xe7, "c");
+ init_letter(0xc8, 0xe8, "e");
+ init_letter(0xc9, 0xe9, "e");
+ init_letter(0xca, 0xea, "e");
+ init_letter(0xcb, 0xeb, "e");
+ init_letter(0xcc, 0xec, "i");
+ init_letter(0xcd, 0xed, "i");
+ init_letter(0xce, 0xee, "i");
+ init_letter(0xcf, 0xef, "i");
+
+ init_letter(0xd0, 0xf0, "d");
+ init_letter(0xd1, 0xf1, "n");
+ init_letter(0xd2, 0xf2, "o");
+ init_letter(0xd3, 0xf3, "o");
+ init_letter(0xd4, 0xf4, "o");
+ init_letter(0xd5, 0xf5, "o");
+ init_letter(0xd6, 0xf6, "o");
+ init_letter(0xd8, 0xf8, "o");
+ init_letter(0xd9, 0xf9, "u");
+ init_letter(0xda, 0xfa, "u");
+ init_letter(0xdb, 0xfb, "u");
+ init_letter(0xdc, 0xfc, "u");
+ init_letter(0xdd, 0xfd, "y");
+ init_letter(0xde, 0xfe, THORN_SORT_KEY);
+
+ store_token("\337", TOKEN_LOWER, "ss", "SS");
+ store_token("\377", TOKEN_LOWER, "y", "Y");
+}
+
+static void init_two_char_letter(char l1, char l2, char u1, char u2,
+ const char *sk = 0)
+{
+ char buf[6];
+ buf[0] = '\\';
+ buf[1] = '(';
+ buf[2] = l1;
+ buf[3] = l2;
+ buf[4] = '\0';
+ const char *p = strsave(buf);
+ buf[2] = u1;
+ buf[3] = u2;
+ store_letter(p, strsave(buf), sk);
+ buf[1] = '[';
+ buf[4] = ']';
+ buf[5] = '\0';
+ p = strsave(buf);
+ buf[2] = l1;
+ buf[3] = l2;
+ store_letter(strsave(buf), p, sk);
+
+}
+
+static void init_special_chars()
+{
+ const char *p;
+ for (p = "':^`~"; *p; p++)
+ for (const char *q = "aeiouy"; *q; q++) {
+ // Use a variable to work around bug in gcc 2.0
+ char c = cmupper(*q);
+ init_two_char_letter(*p, *q, *p, c);
+ }
+ for (p = "/l/o~n,coeaeij"; *p; p += 2) {
+ // Use variables to work around bug in gcc 2.0
+ char c0 = cmupper(p[0]);
+ char c1 = cmupper(p[1]);
+ init_two_char_letter(p[0], p[1], c0, c1);
+ }
+ init_two_char_letter('v', 's', 'v', 'S', "s");
+ init_two_char_letter('v', 'z', 'v', 'Z', "z");
+ init_two_char_letter('o', 'a', 'o', 'A', "a");
+ init_two_char_letter('T', 'p', 'T', 'P', THORN_SORT_KEY);
+ init_two_char_letter('-', 'd', '-', 'D');
+
+ store_token("\\(ss", TOKEN_LOWER, 0, "SS");
+ store_token("\\[ss]", TOKEN_LOWER, 0, "SS");
+
+ store_token("\\(Sd", TOKEN_LOWER, "d", "\\(-D");
+ store_token("\\[Sd]", TOKEN_LOWER, "d", "\\[-D]");
+ store_token("\\(hy", TOKEN_HYPHEN);
+ store_token("\\[hy]", TOKEN_HYPHEN);
+ store_token("\\(en", TOKEN_RANGE_SEP);
+ store_token("\\[en]", TOKEN_RANGE_SEP);
+}
+
+static void init_strings()
+{
+ char buf[6];
+ buf[0] = '\\';
+ buf[1] = '*';
+ for (const char *p = "'`^^,:~v_o./;"; *p; p++) {
+ buf[2] = *p;
+ buf[3] = '\0';
+ store_token(strsave(buf), TOKEN_ACCENT);
+ buf[2] = '[';
+ buf[3] = *p;
+ buf[4] = ']';
+ buf[5] = '\0';
+ store_token(strsave(buf), TOKEN_ACCENT);
+ }
+
+ // -ms special letters
+ store_letter("\\*(th", "\\*(Th", THORN_SORT_KEY);
+ store_letter("\\*[th]", "\\*[Th]", THORN_SORT_KEY);
+ store_letter("\\*(d-", "\\*(D-");
+ store_letter("\\*[d-]", "\\*[D-]");
+ store_letter("\\*(ae", "\\*(Ae", "ae");
+ store_letter("\\*[ae]", "\\*[Ae]", "ae");
+ store_letter("\\*(oe", "\\*(Oe", "oe");
+ store_letter("\\*[oe]", "\\*[Oe]", "oe");
+
+ store_token("\\*3", TOKEN_LOWER, "y", "Y");
+ store_token("\\*8", TOKEN_LOWER, "ss", "SS");
+ store_token("\\*q", TOKEN_LOWER, "o", "O");
+}
+
+struct token_initer {
+ token_initer();
+};
+
+static token_initer the_token_initer;
+
+token_initer::token_initer()
+{
+ init_ascii();
+ init_latin1();
+ init_special_chars();
+ init_strings();
+ default_token_info.set(TOKEN_OTHER);
+}
diff --git a/contrib/groff/src/preproc/refer/token.h b/contrib/groff/src/preproc/refer/token.h
new file mode 100644
index 000000000000..6da430d6abad
--- /dev/null
+++ b/contrib/groff/src/preproc/refer/token.h
@@ -0,0 +1,88 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+enum token_type {
+ TOKEN_OTHER,
+ TOKEN_UPPER,
+ TOKEN_LOWER,
+ TOKEN_ACCENT,
+ TOKEN_PUNCT,
+ TOKEN_HYPHEN,
+ TOKEN_RANGE_SEP
+};
+
+class token_info {
+private:
+ token_type type;
+ const char *sort_key;
+ const char *other_case;
+public:
+ token_info();
+ void set(token_type, const char *sk = 0, const char *oc = 0);
+ void lower_case(const char *start, const char *end, string &result) const;
+ void upper_case(const char *start, const char *end, string &result) const;
+ void sortify(const char *start, const char *end, string &result) const;
+ int sortify_non_empty(const char *start, const char *end) const;
+ int is_upper() const;
+ int is_lower() const;
+ int is_accent() const;
+ int is_other() const;
+ int is_punct() const;
+ int is_hyphen() const;
+ int is_range_sep() const;
+};
+
+inline int token_info::is_upper() const
+{
+ return type == TOKEN_UPPER;
+}
+
+inline int token_info::is_lower() const
+{
+ return type == TOKEN_LOWER;
+}
+
+inline int token_info::is_accent() const
+{
+ return type == TOKEN_ACCENT;
+}
+
+inline int token_info::is_other() const
+{
+ return type == TOKEN_OTHER;
+}
+
+inline int token_info::is_punct() const
+{
+ return type == TOKEN_PUNCT;
+}
+
+inline int token_info::is_hyphen() const
+{
+ return type == TOKEN_HYPHEN;
+}
+
+inline int token_info::is_range_sep() const
+{
+ return type == TOKEN_RANGE_SEP;
+}
+
+int get_token(const char **ptr, const char *end);
+const token_info *lookup_token(const char *start, const char *end);
diff --git a/contrib/groff/src/preproc/soelim/Makefile.sub b/contrib/groff/src/preproc/soelim/Makefile.sub
new file mode 100644
index 000000000000..77007e25f452
--- /dev/null
+++ b/contrib/groff/src/preproc/soelim/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=soelim
+MAN1=soelim.n
+XLIBS=$(LIBGROFF)
+OBJS=soelim.o
+CCSRCS=$(srcdir)/soelim.cc
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/soelim/TODO b/contrib/groff/src/preproc/soelim/TODO
new file mode 100644
index 000000000000..f2a3924ef89f
--- /dev/null
+++ b/contrib/groff/src/preproc/soelim/TODO
@@ -0,0 +1 @@
+Understand .pso.
diff --git a/contrib/groff/src/preproc/soelim/soelim.cc b/contrib/groff/src/preproc/soelim/soelim.cc
new file mode 100644
index 000000000000..e05f24065ad4
--- /dev/null
+++ b/contrib/groff/src/preproc/soelim/soelim.cc
@@ -0,0 +1,347 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "nonposix.h"
+
+static int include_list_length;
+static char **include_list;
+
+int compatible_flag = 0;
+
+extern int interpret_lf_args(const char *);
+
+int do_file(const char *filename);
+
+
+static void
+include_path_append(char *path)
+{
+ ++include_list_length;
+ size_t nbytes = include_list_length * sizeof(char *);
+ if (include_list)
+ include_list = (char **)realloc((void *)include_list, nbytes);
+ else
+ include_list = (char **)malloc(nbytes);
+ if (include_list == NULL)
+ {
+ fprintf(stderr, "%s: out of memory\n", program_name);
+ exit(2);
+ }
+ include_list[include_list_length - 1] = path;
+}
+
+
+void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [ -vC ] [ -I file ] [ files ]\n", program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ include_path_append(".");
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "CI:v", long_options, NULL)) != EOF)
+ switch (opt) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU soelim (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'I':
+ include_path_append(optarg);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ int nbad = 0;
+ if (optind >= argc)
+ nbad += !do_file("-");
+ else
+ for (int i = optind; i < argc; i++)
+ nbad += !do_file(argv[i]);
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ return nbad != 0;
+}
+
+void set_location()
+{
+ printf(".lf %d %s\n", current_lineno, current_filename);
+}
+
+void do_so(const char *line)
+{
+ const char *p = line;
+ while (*p == ' ')
+ p++;
+ string filename;
+ int success = 1;
+ for (const char *q = p;
+ success && *q != '\0' && *q != '\n' && *q != ' ';
+ q++)
+ if (*q == '\\') {
+ switch (*++q) {
+ case 'e':
+ case '\\':
+ filename += '\\';
+ break;
+ case ' ':
+ filename += ' ';
+ break;
+ default:
+ success = 0;
+ break;
+ }
+ }
+ else
+ filename += char(*q);
+ if (success && filename.length() > 0) {
+ filename += '\0';
+ const char *fn = current_filename;
+ int ln = current_lineno;
+ current_lineno--;
+ if (do_file(filename.contents())) {
+ current_filename = fn;
+ current_lineno = ln;
+ set_location();
+ return;
+ }
+ current_lineno++;
+ }
+ fputs(".so", stdout);
+ fputs(line, stdout);
+}
+
+int do_file(const char *filename)
+{
+ FILE *fp;
+ string whole_filename;
+ if (strcmp(filename, "-") == 0) {
+ fp = stdin;
+ whole_filename = filename;
+ whole_filename += '\0';
+ }
+ else if (IS_ABSOLUTE(filename)) {
+ whole_filename = filename;
+ whole_filename += '\0';
+ errno = 0;
+ fp = fopen(filename, "r");
+ if (fp == 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ }
+ else {
+ size_t j;
+ for (j = 0; j < include_list_length; ++j)
+ {
+ char *path = include_list[j];
+ if (0 == strcmp(path, "."))
+ whole_filename = filename;
+ else
+ whole_filename = string(path) + "/" + filename;
+ whole_filename += '\0';
+ errno = 0;
+ fp = fopen(whole_filename.contents(), "r");
+ if (fp != 0)
+ break;
+ if (errno != ENOENT) {
+ error("can't open `%1': %2",
+ whole_filename.contents(), strerror(errno));
+ return 0;
+ }
+ }
+ if (j >= include_list_length)
+ {
+ errno = ENOENT;
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ }
+ current_filename = whole_filename.contents();
+ current_lineno = 1;
+ set_location();
+ enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ switch (state) {
+ case START:
+ if (c == '.')
+ state = HAD_DOT;
+ else {
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case MIDDLE:
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ break;
+ case HAD_DOT:
+ if (c == 's')
+ state = HAD_s;
+ else if (c == 'l')
+ state = HAD_l;
+ else {
+ putchar('.');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_s:
+ if (c == 'o')
+ state = HAD_so;
+ else {
+ putchar('.');
+ putchar('s');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_so:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ string line;
+ for (; c != EOF && c != '\n'; c = getc(fp))
+ line += c;
+ current_lineno++;
+ line += '\n';
+ line += '\0';
+ do_so(line.contents());
+ state = START;
+ }
+ else {
+ fputs(".so", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ case HAD_l:
+ if (c == 'f')
+ state = HAD_lf;
+ else {
+ putchar('.');
+ putchar('l');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_lf:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ string line;
+ for (; c != EOF && c != '\n'; c = getc(fp))
+ line += c;
+ current_lineno++;
+ line += '\n';
+ line += '\0';
+ interpret_lf_args(line.contents());
+ printf(".lf%s", line.contents());
+ state = START;
+ }
+ else {
+ fputs(".lf", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+ switch (state) {
+ case HAD_DOT:
+ fputs(".\n", stdout);
+ break;
+ case HAD_l:
+ fputs(".l\n", stdout);
+ break;
+ case HAD_s:
+ fputs(".s\n", stdout);
+ break;
+ case HAD_lf:
+ fputs(".lf\n", stdout);
+ break;
+ case HAD_so:
+ fputs(".so\n", stdout);
+ break;
+ case MIDDLE:
+ putc('\n', stdout);
+ break;
+ case START:
+ break;
+ }
+ if (fp != stdin)
+ fclose(fp);
+ current_filename = 0;
+ return 1;
+}
diff --git a/contrib/groff/src/preproc/soelim/soelim.man b/contrib/groff/src/preproc/soelim/soelim.man
new file mode 100644
index 000000000000..b97ea61262e2
--- /dev/null
+++ b/contrib/groff/src/preproc/soelim/soelim.man
@@ -0,0 +1,85 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH @G@SOELIM @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@soelim \- interpret .so requests in groff input
+.SH SYNOPSIS
+.B @g@soelim
+[
+.B \-Cv
+]
+[
+.BI \-I dir
+]
+[
+.IR files \|.\|.\|.\|
+]
+.PP
+It is possible to have whitespace between the
+.B \-I
+command line option and its parameter.
+.SH DESCRIPTION
+.B @g@soelim
+reads
+.I files
+and replaces lines of the form
+.IP
+.BI .so\ file
+.LP
+by the contents of
+.IR file .
+It is useful if files included with
+.B so
+need to be preprocessed.
+Normally,
+.B @g@soelim
+should be invoked with the
+.B \-s
+option of
+.BR groff .
+.PP
+Note that there must be no whitespace between the leading dot and
+the two characters `s' and `o'. Otherwise, only
+.B groff
+interprets the
+.B .so
+request (and
+.B soelim
+ignores it).
+.SH OPTIONS
+.TP
+.B \-C
+Recognize
+.B .so
+even when followed by a character other than space or newline.
+.TP
+.BI \-I dir
+This option may be used to specify a directory to search for
+files (both those on the command line and those named in
+.B \&.so
+lines).
+The current directory is always searched first.
+This option may be specified more than once,
+the directories will be searched in the order specified.
+No directory search is performed for files specified using an absolute path.
+.TP
+.B \-v
+Print the version number.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@)
diff --git a/contrib/groff/src/preproc/tbl/Makefile.sub b/contrib/groff/src/preproc/tbl/Makefile.sub
new file mode 100644
index 000000000000..224baffcf329
--- /dev/null
+++ b/contrib/groff/src/preproc/tbl/Makefile.sub
@@ -0,0 +1,12 @@
+PROG=tbl
+MAN1=tbl.n
+XLIBS=$(LIBGROFF)
+OBJS=\
+ main.o \
+ table.o
+CCSRCS=\
+ $(srcdir)/main.cc \
+ $(srcdir)/table.cc
+HDRS=\
+ $(srcdir)/table.h
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/preproc/tbl/main.cc b/contrib/groff/src/preproc/tbl/main.cc
new file mode 100644
index 000000000000..a08ea0be2570
--- /dev/null
+++ b/contrib/groff/src/preproc/tbl/main.cc
@@ -0,0 +1,1528 @@
+// -*- 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 "table.h"
+#include "htmlindicate.h"
+
+#define MAX_POINT_SIZE 99
+#define MAX_VERTICAL_SPACING 72
+
+static int compatible_flag = 0;
+
+class table_input {
+ FILE *fp;
+ enum { START, MIDDLE, REREAD_T, REREAD_TE, REREAD_E, END, ERROR } state;
+ string unget_stack;
+public:
+ table_input(FILE *);
+ int get();
+ int ended() { return unget_stack.empty() && state == END; }
+ void unget(char);
+};
+
+table_input::table_input(FILE *p)
+: fp(p), state(START)
+{
+}
+
+void table_input::unget(char c)
+{
+ assert(c != '\0');
+ unget_stack += c;
+ if (c == '\n')
+ current_lineno--;
+}
+
+int table_input::get()
+{
+ int len = unget_stack.length();
+ if (len != 0) {
+ unsigned char c = unget_stack[len - 1];
+ unget_stack.set_length(len - 1);
+ if (c == '\n')
+ current_lineno++;
+ return c;
+ }
+ int c;
+ for (;;) {
+ switch (state) {
+ case START:
+ if ((c = getc(fp)) == '.') {
+ if ((c = getc(fp)) == 'T') {
+ if ((c = getc(fp)) == 'E') {
+ if (compatible_flag) {
+ state = END;
+ return EOF;
+ }
+ else {
+ c = getc(fp);
+ if (c != EOF)
+ ungetc(c, fp);
+ if (c == EOF || c == ' ' || c == '\n') {
+ state = END;
+ return EOF;
+ }
+ state = REREAD_TE;
+ return '.';
+ }
+ }
+ else {
+ if (c != EOF)
+ ungetc(c, fp);
+ state = REREAD_T;
+ return '.';
+ }
+ }
+ else {
+ if (c != EOF)
+ ungetc(c, fp);
+ state = MIDDLE;
+ return '.';
+ }
+ }
+ else if (c == EOF) {
+ state = ERROR;
+ return EOF;
+ }
+ else {
+ if (c == '\n')
+ current_lineno++;
+ else {
+ state = MIDDLE;
+ if (c == '\0') {
+ error("illegal input character code 0");
+ break;
+ }
+ }
+ return c;
+ }
+ break;
+ case MIDDLE:
+ // handle line continuation
+ if ((c = getc(fp)) == '\\') {
+ c = getc(fp);
+ if (c == '\n')
+ c = getc(fp); // perhaps state ought to be START now
+ else {
+ if (c != EOF)
+ ungetc(c, fp);
+ c = '\\';
+ }
+ }
+ if (c == EOF) {
+ state = ERROR;
+ return EOF;
+ }
+ else {
+ if (c == '\n') {
+ state = START;
+ current_lineno++;
+ }
+ else if (c == '\0') {
+ error("illegal input character code 0");
+ break;
+ }
+ return c;
+ }
+ case REREAD_T:
+ state = MIDDLE;
+ return 'T';
+ case REREAD_TE:
+ state = REREAD_E;
+ return 'T';
+ case REREAD_E:
+ state = MIDDLE;
+ return 'E';
+ case END:
+ case ERROR:
+ return EOF;
+ }
+ }
+}
+
+void process_input_file(FILE *);
+void process_table(table_input &in);
+
+void process_input_file(FILE *fp)
+{
+ enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state;
+ state = START;
+ int c;
+ while ((c = getc(fp)) != EOF)
+ switch (state) {
+ case START:
+ if (c == '.')
+ state = HAD_DOT;
+ else {
+ if (c == '\n')
+ current_lineno++;
+ else
+ state = MIDDLE;
+ putchar(c);
+ }
+ break;
+ case MIDDLE:
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ putchar(c);
+ break;
+ case HAD_DOT:
+ if (c == 'T')
+ state = HAD_T;
+ else if (c == 'l')
+ state = HAD_l;
+ else {
+ putchar('.');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_T:
+ if (c == 'S')
+ state = HAD_TS;
+ else {
+ putchar('.');
+ putchar('T');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_TS:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ printf(".if '\\*(.T'html' \\X(table-start(\n");
+ html_begin_suppress(0);
+ putchar('.');
+ putchar('T');
+ putchar('S');
+ while (c != '\n') {
+ if (c == EOF) {
+ error("end of file at beginning of table");
+ return;
+ }
+ putchar(c);
+ c = getc(fp);
+ }
+ putchar('\n');
+ current_lineno++;
+ {
+ table_input input(fp);
+ process_table(input);
+ set_troff_location(current_filename, current_lineno);
+ if (input.ended()) {
+ fputs(".TE", stdout);
+ while ((c = getc(fp)) != '\n') {
+ if (c == EOF) {
+ printf(".if '\\*(.T'html' \\X(table-end(\n");
+ html_end_suppress(0);
+ putchar('\n');
+ return;
+ }
+ putchar(c);
+ }
+ putchar('\n');
+ printf(".if '\\*(.T'html' \\X(table-end(\n");
+ html_end_suppress(0);
+ current_lineno++;
+ }
+ }
+ state = START;
+ }
+ else {
+ fputs(".TS", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ case HAD_l:
+ if (c == 'f')
+ state = HAD_lf;
+ else {
+ putchar('.');
+ putchar('l');
+ putchar(c);
+ if (c == '\n') {
+ current_lineno++;
+ state = START;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case HAD_lf:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ string line;
+ while (c != EOF) {
+ line += c;
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ c = getc(fp);
+ }
+ line += '\0';
+ interpret_lf_args(line.contents());
+ printf(".lf%s", line.contents());
+ state = START;
+ }
+ else {
+ fputs(".lf", stdout);
+ putchar(c);
+ state = MIDDLE;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ switch(state) {
+ case START:
+ break;
+ case MIDDLE:
+ putchar('\n');
+ break;
+ case HAD_DOT:
+ fputs(".\n", stdout);
+ break;
+ case HAD_l:
+ fputs(".l\n", stdout);
+ break;
+ case HAD_T:
+ fputs(".T\n", stdout);
+ break;
+ case HAD_lf:
+ fputs(".lf\n", stdout);
+ break;
+ case HAD_TS:
+ fputs(".TS\n", stdout);
+ break;
+ }
+ if (fp != stdin)
+ fclose(fp);
+}
+
+struct options {
+ unsigned flags;
+ int linesize;
+ char delim[2];
+ char tab_char;
+ char decimal_point_char;
+
+ options();
+};
+
+options::options()
+: flags(0), linesize(0), tab_char('\t'), decimal_point_char('.')
+{
+ delim[0] = delim[1] = '\0';
+}
+
+// Return non-zero if p and q are the same ignoring case.
+
+int strieq(const char *p, const char *q)
+{
+ for (; cmlower(*p) == cmlower(*q); p++, q++)
+ if (*p == '\0')
+ return 1;
+ return 0;
+}
+
+// return 0 if we should give up in this table
+
+options *process_options(table_input &in)
+{
+ options *opt = new options;
+ string line;
+ int level = 0;
+ for (;;) {
+ int c = in.get();
+ if (c == EOF) {
+ int i = line.length();
+ while (--i >= 0)
+ in.unget(line[i]);
+ return opt;
+ }
+ if (c == '\n') {
+ in.unget(c);
+ int i = line.length();
+ while (--i >= 0)
+ in.unget(line[i]);
+ return opt;
+ }
+ else if (c == '(')
+ level++;
+ else if (c == ')')
+ level--;
+ else if (c == ';' && level == 0) {
+ line += '\0';
+ break;
+ }
+ line += c;
+ }
+ if (line.empty())
+ return opt;
+ char *p = &line[0];
+ for (;;) {
+ while (!csalpha(*p) && *p != '\0')
+ p++;
+ if (*p == '\0')
+ break;
+ char *q = p;
+ while (csalpha(*q))
+ q++;
+ char *arg = 0;
+ if (*q != '(' && *q != '\0')
+ *q++ = '\0';
+ while (csspace(*q))
+ q++;
+ if (*q == '(') {
+ *q++ = '\0';
+ arg = q;
+ while (*q != ')' && *q != '\0')
+ q++;
+ if (*q == '\0')
+ error("missing `)'");
+ else
+ *q++ = '\0';
+ }
+ if (*p == '\0') {
+ if (arg)
+ error("argument without option");
+ }
+ else if (strieq(p, "tab")) {
+ if (!arg)
+ error("`tab' option requires argument in parentheses");
+ else {
+ if (arg[0] == '\0' || arg[1] != '\0')
+ error("argument to `tab' option must be a single character");
+ else
+ opt->tab_char = arg[0];
+ }
+ }
+ else if (strieq(p, "linesize")) {
+ if (!arg)
+ error("`linesize' option requires argument in parentheses");
+ else {
+ if (sscanf(arg, "%d", &opt->linesize) != 1)
+ error("bad linesize `%s'", arg);
+ else if (opt->linesize <= 0) {
+ error("linesize must be positive");
+ opt->linesize = 0;
+ }
+ }
+ }
+ else if (strieq(p, "delim")) {
+ if (!arg)
+ error("`delim' option requires argument in parentheses");
+ else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0')
+ error("argument to `delim' option must be two characters");
+ else {
+ opt->delim[0] = arg[0];
+ opt->delim[1] = arg[1];
+ }
+ }
+ else if (strieq(p, "center") || strieq(p, "centre")) {
+ if (arg)
+ error("`center' option does not take a argument");
+ opt->flags |= table::CENTER;
+ }
+ else if (strieq(p, "expand")) {
+ if (arg)
+ error("`expand' option does not take a argument");
+ opt->flags |= table::EXPAND;
+ }
+ else if (strieq(p, "box") || strieq(p, "frame")) {
+ if (arg)
+ error("`box' option does not take a argument");
+ opt->flags |= table::BOX;
+ }
+ else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) {
+ if (arg)
+ error("`doublebox' option does not take a argument");
+ opt->flags |= table::DOUBLEBOX;
+ }
+ else if (strieq(p, "allbox")) {
+ if (arg)
+ error("`allbox' option does not take a argument");
+ opt->flags |= table::ALLBOX;
+ }
+ else if (strieq(p, "nokeep")) {
+ if (arg)
+ error("`nokeep' option does not take a argument");
+ opt->flags |= table::NOKEEP;
+ }
+ else if (strieq(p, "decimalpoint")) {
+ if (!arg)
+ error("`decimalpoint' option requires argument in parentheses");
+ else {
+ if (arg[0] == '\0' || arg[1] != '\0')
+ error("argument to `decimalpoint' option must be a single character");
+ else
+ opt->decimal_point_char = arg[0];
+ }
+ }
+ else {
+ error("unrecognised global option `%1'", p);
+ // delete opt;
+ // return 0;
+ }
+ p = q;
+ }
+ return opt;
+}
+
+entry_modifier::entry_modifier()
+: vertical_alignment(CENTER), zero_width(0), stagger(0)
+{
+ vertical_spacing.inc = vertical_spacing.val = 0;
+ point_size.inc = point_size.val = 0;
+}
+
+entry_modifier::~entry_modifier()
+{
+}
+
+entry_format::entry_format() : type(FORMAT_LEFT)
+{
+}
+
+entry_format::entry_format(format_type t) : type(t)
+{
+}
+
+void entry_format::debug_print() const
+{
+ switch (type) {
+ case FORMAT_LEFT:
+ putc('l', stderr);
+ break;
+ case FORMAT_CENTER:
+ putc('c', stderr);
+ break;
+ case FORMAT_RIGHT:
+ putc('r', stderr);
+ break;
+ case FORMAT_NUMERIC:
+ putc('n', stderr);
+ break;
+ case FORMAT_ALPHABETIC:
+ putc('a', stderr);
+ break;
+ case FORMAT_SPAN:
+ putc('s', stderr);
+ break;
+ case FORMAT_VSPAN:
+ putc('^', stderr);
+ break;
+ case FORMAT_HLINE:
+ putc('_', stderr);
+ break;
+ case FORMAT_DOUBLE_HLINE:
+ putc('=', stderr);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (point_size.val != 0) {
+ putc('p', stderr);
+ if (point_size.inc > 0)
+ putc('+', stderr);
+ else if (point_size.inc < 0)
+ putc('-', stderr);
+ fprintf(stderr, "%d ", point_size.val);
+ }
+ if (vertical_spacing.val != 0) {
+ putc('v', stderr);
+ if (vertical_spacing.inc > 0)
+ putc('+', stderr);
+ else if (vertical_spacing.inc < 0)
+ putc('-', stderr);
+ fprintf(stderr, "%d ", vertical_spacing.val);
+ }
+ if (!font.empty()) {
+ putc('f', stderr);
+ put_string(font, stderr);
+ putc(' ', stderr);
+ }
+ switch (vertical_alignment) {
+ case entry_modifier::CENTER:
+ break;
+ case entry_modifier::TOP:
+ putc('t', stderr);
+ break;
+ case entry_modifier::BOTTOM:
+ putc('d', stderr);
+ break;
+ }
+ if (zero_width)
+ putc('z', stderr);
+ if (stagger)
+ putc('u', stderr);
+}
+
+struct format {
+ int nrows;
+ int ncolumns;
+ int *separation;
+ string *width;
+ char *equal;
+ entry_format **entry;
+ char **vline;
+
+ format(int nr, int nc);
+ ~format();
+ void add_rows(int n);
+};
+
+format::format(int nr, int nc) : nrows(nr), ncolumns(nc)
+{
+ int i;
+ separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
+ for (i = 0; i < ncolumns-1; i++)
+ separation[i] = -1;
+ width = new string[ncolumns];
+ equal = new char[ncolumns];
+ for (i = 0; i < ncolumns; i++)
+ equal[i] = 0;
+ entry = new entry_format *[nrows];
+ for (i = 0; i < nrows; i++)
+ entry[i] = new entry_format[ncolumns];
+ vline = new char*[nrows];
+ for (i = 0; i < nrows; i++) {
+ vline[i] = new char[ncolumns+1];
+ for (int j = 0; j < ncolumns+1; j++)
+ vline[i][j] = 0;
+ }
+}
+
+void format::add_rows(int n)
+{
+ int i;
+ char **old_vline = vline;
+ vline = new char*[nrows + n];
+ for (i = 0; i < nrows; i++)
+ vline[i] = old_vline[i];
+ a_delete old_vline;
+ for (i = 0; i < n; i++) {
+ vline[nrows + i] = new char[ncolumns + 1];
+ for (int j = 0; j < ncolumns + 1; j++)
+ vline[nrows + i][j] = 0;
+ }
+ entry_format **old_entry = entry;
+ entry = new entry_format *[nrows + n];
+ for (i = 0; i < nrows; i++)
+ entry[i] = old_entry[i];
+ a_delete old_entry;
+ for (i = 0; i < n; i++)
+ entry[nrows + i] = new entry_format[ncolumns];
+ nrows += n;
+}
+
+format::~format()
+{
+ a_delete separation;
+ ad_delete(ncolumns) width;
+ a_delete equal;
+ for (int i = 0; i < nrows; i++) {
+ a_delete vline[i];
+ ad_delete(ncolumns) entry[i];
+ }
+ a_delete vline;
+ a_delete entry;
+}
+
+struct input_entry_format : public entry_format {
+ input_entry_format *next;
+ string width;
+ int separation;
+ int vline;
+ int pre_vline;
+ int last_column;
+ int equal;
+ input_entry_format(format_type, input_entry_format * = 0);
+ ~input_entry_format();
+ void debug_print();
+};
+
+input_entry_format::input_entry_format(format_type t, input_entry_format *p)
+: entry_format(t), next(p)
+{
+ separation = -1;
+ last_column = 0;
+ vline = 0;
+ pre_vline = 0;
+ equal = 0;
+}
+
+input_entry_format::~input_entry_format()
+{
+}
+
+void free_input_entry_format_list(input_entry_format *list)
+{
+ while (list) {
+ input_entry_format *tem = list;
+ list = list->next;
+ delete tem;
+ }
+}
+
+void input_entry_format::debug_print()
+{
+ int i;
+ for (i = 0; i < pre_vline; i++)
+ putc('|', stderr);
+ entry_format::debug_print();
+ if (!width.empty()) {
+ putc('w', stderr);
+ putc('(', stderr);
+ put_string(width, stderr);
+ putc(')', stderr);
+ }
+ if (equal)
+ putc('e', stderr);
+ if (separation >= 0)
+ fprintf(stderr, "%d", separation);
+ for (i = 0; i < vline; i++)
+ putc('|', stderr);
+ if (last_column)
+ putc(',', stderr);
+}
+
+// Return zero if we should give up on this table.
+// If this is a continuation format line, current_format will be the current
+// format line.
+
+format *process_format(table_input &in, options *opt,
+ format *current_format = 0)
+{
+ input_entry_format *list = 0;
+ int c = in.get();
+ for (;;) {
+ int pre_vline = 0;
+ int got_format = 0;
+ int got_period = 0;
+ format_type t;
+ for (;;) {
+ if (c == EOF) {
+ error("end of input while processing format");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ switch (c) {
+ case 'n':
+ case 'N':
+ t = FORMAT_NUMERIC;
+ got_format = 1;
+ break;
+ case 'a':
+ case 'A':
+ got_format = 1;
+ t = FORMAT_ALPHABETIC;
+ break;
+ case 'c':
+ case 'C':
+ got_format = 1;
+ t = FORMAT_CENTER;
+ break;
+ case 'l':
+ case 'L':
+ got_format = 1;
+ t = FORMAT_LEFT;
+ break;
+ case 'r':
+ case 'R':
+ got_format = 1;
+ t = FORMAT_RIGHT;
+ break;
+ case 's':
+ case 'S':
+ got_format = 1;
+ t = FORMAT_SPAN;
+ break;
+ case '^':
+ got_format = 1;
+ t = FORMAT_VSPAN;
+ break;
+ case '_':
+ case '-': // tbl also accepts this
+ got_format = 1;
+ t = FORMAT_HLINE;
+ break;
+ case '=':
+ got_format = 1;
+ t = FORMAT_DOUBLE_HLINE;
+ break;
+ case '.':
+ got_period = 1;
+ break;
+ case '|':
+ pre_vline++;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ break;
+ default:
+ if (c == opt->tab_char)
+ break;
+ error("unrecognised format `%1'", char(c));
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ if (got_period)
+ break;
+ c = in.get();
+ if (got_format)
+ break;
+ }
+ if (got_period)
+ break;
+ list = new input_entry_format(t, list);
+ if (pre_vline)
+ list->pre_vline = pre_vline;
+ int success = 1;
+ do {
+ switch (c) {
+ case 't':
+ case 'T':
+ c = in.get();
+ list->vertical_alignment = entry_modifier::TOP;
+ break;
+ case 'd':
+ case 'D':
+ c = in.get();
+ list->vertical_alignment = entry_modifier::BOTTOM;
+ break;
+ case 'u':
+ case 'U':
+ c = in.get();
+ list->stagger = 1;
+ break;
+ case 'z':
+ case 'Z':
+ c = in.get();
+ list->zero_width = 1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int w = 0;
+ do {
+ w = w*10 + (c - '0');
+ c = in.get();
+ } while (c != EOF && csdigit(c));
+ list->separation = w;
+ }
+ break;
+ case 'f':
+ case 'F':
+ do {
+ c = in.get();
+ } while (c == ' ' || c == '\t');
+ if (c == EOF) {
+ error("missing font name");
+ break;
+ }
+ if (c == '(') {
+ for (;;) {
+ c = in.get();
+ if (c == EOF || c == ' ' || c == '\t') {
+ error("missing `)'");
+ break;
+ }
+ if (c == ')') {
+ c = in.get();
+ break;
+ }
+ list->font += char(c);
+ }
+ }
+ else {
+ list->font = c;
+ char cc = c;
+ c = in.get();
+ if (!csdigit(cc)
+ && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') {
+ list->font += char(c);
+ c = in.get();
+ }
+ }
+ break;
+ case 'v':
+ case 'V':
+ c = in.get();
+ list->vertical_spacing.val = 0;
+ list->vertical_spacing.inc = 0;
+ if (c == '+' || c == '-') {
+ list->vertical_spacing.inc = (c == '+' ? 1 : -1);
+ c = in.get();
+ }
+ if (c == EOF || !csdigit(c)) {
+ error("`v' modifier must be followed by number");
+ list->vertical_spacing.inc = 0;
+ }
+ else {
+ do {
+ list->vertical_spacing.val *= 10;
+ list->vertical_spacing.val += c - '0';
+ c = in.get();
+ } while (c != EOF && csdigit(c));
+ }
+ if (list->vertical_spacing.val > MAX_VERTICAL_SPACING
+ || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) {
+ error("unreasonable point size");
+ list->vertical_spacing.val = 0;
+ list->vertical_spacing.inc = 0;
+ }
+ break;
+ case 'p':
+ case 'P':
+ c = in.get();
+ list->point_size.val = 0;
+ list->point_size.inc = 0;
+ if (c == '+' || c == '-') {
+ list->point_size.inc = (c == '+' ? 1 : -1);
+ c = in.get();
+ }
+ if (c == EOF || !csdigit(c)) {
+ error("`p' modifier must be followed by number");
+ list->point_size.inc = 0;
+ }
+ else {
+ do {
+ list->point_size.val *= 10;
+ list->point_size.val += c - '0';
+ c = in.get();
+ } while (c != EOF && csdigit(c));
+ }
+ if (list->point_size.val > MAX_POINT_SIZE
+ || list->point_size.val < -MAX_POINT_SIZE) {
+ error("unreasonable point size");
+ list->point_size.val = 0;
+ list->point_size.inc = 0;
+ }
+ break;
+ case 'w':
+ case 'W':
+ c = in.get();
+ while (c == ' ' || c == '\t')
+ c = in.get();
+ if (c == '(') {
+ list->width = "";
+ c = in.get();
+ while (c != ')') {
+ if (c == EOF || c == '\n') {
+ error("missing `)'");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ list->width += c;
+ c = in.get();
+ }
+ c = in.get();
+ }
+ else {
+ if (c == '+' || c == '-') {
+ list->width = char(c);
+ c = in.get();
+ }
+ else
+ list->width = "";
+ if (c == EOF || !csdigit(c))
+ error("bad argument for `w' modifier");
+ else {
+ do {
+ list->width += char(c);
+ c = in.get();
+ } while (c != EOF && csdigit(c));
+ }
+ }
+ break;
+ case 'e':
+ case 'E':
+ c = in.get();
+ list->equal++;
+ break;
+ case '|':
+ c = in.get();
+ list->vline++;
+ break;
+ case 'B':
+ case 'b':
+ c = in.get();
+ list->font = "B";
+ break;
+ case 'I':
+ case 'i':
+ c = in.get();
+ list->font = "I";
+ break;
+ case ' ':
+ case '\t':
+ c = in.get();
+ break;
+ default:
+ if (c == opt->tab_char)
+ c = in.get();
+ else
+ success = 0;
+ break;
+ }
+ } while (success);
+ if (list->vline > 2) {
+ list->vline = 2;
+ error("more than 2 vertical bars between key letters");
+ }
+ if (c == '\n' || c == ',') {
+ c = in.get();
+ list->last_column = 1;
+ }
+ }
+ if (c == '.') {
+ do {
+ c = in.get();
+ } while (c == ' ' || c == '\t');
+ if (c != '\n') {
+ error("`.' not last character on line");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ }
+ if (!list) {
+ error("no format");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ list->last_column = 1;
+ // now reverse the list so that the first row is at the beginning
+ input_entry_format *rev = 0;
+ while (list != 0) {
+ input_entry_format *tem = list->next;
+ list->next = rev;
+ rev = list;
+ list = tem;
+ }
+ list = rev;
+ input_entry_format *tem;
+
+#if 0
+ for (tem = list; tem; tem = tem->next)
+ tem->debug_print();
+ putc('\n', stderr);
+#endif
+ // compute number of columns and rows
+ int ncolumns = 0;
+ int nrows = 0;
+ int col = 0;
+ for (tem = list; tem; tem = tem->next) {
+ if (tem->last_column) {
+ if (col >= ncolumns)
+ ncolumns = col + 1;
+ col = 0;
+ nrows++;
+ }
+ else
+ col++;
+ }
+ int row;
+ format *f;
+ if (current_format) {
+ if (ncolumns > current_format->ncolumns) {
+ error("cannot increase the number of columns in a continued format");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ f = current_format;
+ row = f->nrows;
+ f->add_rows(nrows);
+ }
+ else {
+ f = new format(nrows, ncolumns);
+ row = 0;
+ }
+ col = 0;
+ for (tem = list; tem; tem = tem->next) {
+ f->entry[row][col] = *tem;
+ if (col < ncolumns-1) {
+ // use the greatest separation
+ if (tem->separation > f->separation[col]) {
+ if (current_format)
+ error("cannot change column separation in continued format");
+ else
+ f->separation[col] = tem->separation;
+ }
+ }
+ else if (tem->separation >= 0)
+ error("column separation specified for last column");
+ if (tem->equal && !f->equal[col]) {
+ if (current_format)
+ error("cannot change which columns are equal in continued format");
+ else
+ f->equal[col] = 1;
+ }
+ if (!tem->width.empty()) {
+ // use the last width
+ if (!f->width[col].empty() && f->width[col] != tem->width)
+ error("multiple widths for column %1", col+1);
+ f->width[col] = tem->width;
+ }
+ if (tem->pre_vline) {
+ assert(col == 0);
+ f->vline[row][col] = tem->pre_vline;
+ }
+ f->vline[row][col+1] = tem->vline;
+ if (tem->last_column) {
+ row++;
+ col = 0;
+ }
+ else
+ col++;
+ }
+ free_input_entry_format_list(list);
+ for (col = 0; col < ncolumns; col++) {
+ entry_format *e = f->entry[f->nrows-1] + col;
+ if (e->type != FORMAT_HLINE
+ && e->type != FORMAT_DOUBLE_HLINE
+ && e->type != FORMAT_SPAN)
+ break;
+ }
+ if (col >= ncolumns) {
+ error("last row of format is all lines");
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+table *process_data(table_input &in, format *f, options *opt)
+{
+ char tab_char = opt->tab_char;
+ int ncolumns = f->ncolumns;
+ int current_row = 0;
+ int format_index = 0;
+ int give_up = 0;
+ enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type;
+ table *tbl = new table(ncolumns, opt->flags, opt->linesize,
+ opt->decimal_point_char);
+ if (opt->delim[0] != '\0')
+ tbl->set_delim(opt->delim[0], opt->delim[1]);
+ for (;;) {
+ // first determine what type of line this is
+ int c = in.get();
+ if (c == EOF)
+ break;
+ if (c == '.') {
+ int d = in.get();
+ if (d != EOF && csdigit(d)) {
+ in.unget(d);
+ type = DATA_INPUT_LINE;
+ }
+ else {
+ in.unget(d);
+ type = TROFF_INPUT_LINE;
+ }
+ }
+ else if (c == '_' || c == '=') {
+ int d = in.get();
+ if (d == '\n') {
+ if (c == '_')
+ type = SINGLE_HLINE;
+ else
+ type = DOUBLE_HLINE;
+ }
+ else {
+ in.unget(d);
+ type = DATA_INPUT_LINE;
+ }
+ }
+ else {
+ type = DATA_INPUT_LINE;
+ }
+ switch (type) {
+ case DATA_INPUT_LINE:
+ {
+ string input_entry;
+ if (format_index >= f->nrows)
+ format_index = f->nrows - 1;
+ // A format row that is all lines doesn't use up a data line.
+ while (format_index < f->nrows - 1) {
+ int c;
+ for (c = 0; c < ncolumns; c++) {
+ entry_format *e = f->entry[format_index] + c;
+ if (e->type != FORMAT_HLINE
+ && e->type != FORMAT_DOUBLE_HLINE
+ // Unfortunately tbl treats a span as needing data.
+ // && e->type != FORMAT_SPAN
+ )
+ break;
+ }
+ if (c < ncolumns)
+ break;
+ for (c = 0; c < ncolumns; c++)
+ tbl->add_entry(current_row, c, input_entry,
+ f->entry[format_index] + c, current_filename,
+ current_lineno);
+ tbl->add_vlines(current_row, f->vline[format_index]);
+ format_index++;
+ current_row++;
+ }
+ entry_format *line_format = f->entry[format_index];
+ int col = 0;
+ int row_comment = 0;
+ for (;;) {
+ if (c == tab_char || c == '\n') {
+ int ln = current_lineno;
+ if (c == '\n')
+ --ln;
+ while (col < ncolumns
+ && line_format[col].type == FORMAT_SPAN) {
+ tbl->add_entry(current_row, col, "", &line_format[col],
+ current_filename, ln);
+ col++;
+ }
+ if (c == '\n' && input_entry.length() == 2
+ && input_entry[0] == 'T' && input_entry[1] == '{') {
+ input_entry = "";
+ ln++;
+ enum {
+ START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT,
+ GOT_l, GOT_lf, END
+ } state = START;
+ while (state != END) {
+ c = in.get();
+ if (c == EOF)
+ break;
+ switch (state) {
+ case START:
+ if (c == 'T')
+ state = GOT_T;
+ else if (c == '.')
+ state = GOT_DOT;
+ else {
+ input_entry += c;
+ if (c != '\n')
+ state = MIDDLE;
+ }
+ break;
+ case GOT_T:
+ if (c == '}')
+ state = GOT_RIGHT_BRACE;
+ else {
+ input_entry += 'T';
+ input_entry += c;
+ state = c == '\n' ? START : MIDDLE;
+ }
+ break;
+ case GOT_DOT:
+ if (c == 'l')
+ state = GOT_l;
+ else {
+ input_entry += '.';
+ input_entry += c;
+ state = c == '\n' ? START : MIDDLE;
+ }
+ break;
+ case GOT_l:
+ if (c == 'f')
+ state = GOT_lf;
+ else {
+ input_entry += ".l";
+ input_entry += c;
+ state = c == '\n' ? START : MIDDLE;
+ }
+ break;
+ case GOT_lf:
+ if (c == ' ' || c == '\n' || compatible_flag) {
+ string args;
+ input_entry += ".lf";
+ while (c != EOF) {
+ args += c;
+ if (c == '\n')
+ break;
+ c = in.get();
+ }
+ args += '\0';
+ interpret_lf_args(args.contents());
+ // remove the '\0'
+ args.set_length(args.length() - 1);
+ input_entry += args;
+ state = START;
+ }
+ else {
+ input_entry += ".lf";
+ input_entry += c;
+ state = MIDDLE;
+ }
+ break;
+ case GOT_RIGHT_BRACE:
+ if (c == '\n' || c == tab_char)
+ state = END;
+ else {
+ input_entry += 'T';
+ input_entry += '}';
+ input_entry += c;
+ state = c == '\n' ? START : MIDDLE;
+ }
+ break;
+ case MIDDLE:
+ if (c == '\n')
+ state = START;
+ input_entry += c;
+ break;
+ case END:
+ default:
+ assert(0);
+ }
+ }
+ if (c == EOF) {
+ error("end of data in middle of text block");
+ give_up = 1;
+ break;
+ }
+ }
+ if (col >= ncolumns) {
+ if (!input_entry.empty()) {
+ if (input_entry.length() >= 2
+ && input_entry[0] == '\\'
+ && input_entry[1] == '"')
+ row_comment = 1;
+ else if (!row_comment) {
+ if (c == '\n')
+ in.unget(c);
+ input_entry += '\0';
+ error("excess data entry `%1' discarded",
+ input_entry.contents());
+ if (c == '\n')
+ (void)in.get();
+ }
+ }
+ }
+ else
+ tbl->add_entry(current_row, col, input_entry,
+ &line_format[col], current_filename, ln);
+ col++;
+ if (c == '\n')
+ break;
+ input_entry = "";
+ }
+ else
+ input_entry += c;
+ c = in.get();
+ if (c == EOF)
+ break;
+ }
+ if (give_up)
+ break;
+ input_entry = "";
+ for (; col < ncolumns; col++)
+ tbl->add_entry(current_row, col, input_entry, &line_format[col],
+ current_filename, current_lineno - 1);
+ tbl->add_vlines(current_row, f->vline[format_index]);
+ current_row++;
+ format_index++;
+ }
+ break;
+ case TROFF_INPUT_LINE:
+ {
+ string line;
+ int ln = current_lineno;
+ for (;;) {
+ line += c;
+ if (c == '\n')
+ break;
+ c = in.get();
+ if (c == EOF) {
+ break;
+ }
+ }
+ tbl->add_text_line(current_row, line, current_filename, ln);
+ if (line.length() >= 4
+ && line[0] == '.' && line[1] == 'T' && line[2] == '&') {
+ format *newf = process_format(in, opt, f);
+ if (newf == 0)
+ give_up = 1;
+ else
+ f = newf;
+ }
+ if (line.length() >= 3
+ && line[0] == '.' && line[1] == 'f' && line[2] == 'f') {
+ line += '\0';
+ interpret_lf_args(line.contents() + 3);
+ }
+ }
+ break;
+ case SINGLE_HLINE:
+ tbl->add_single_hline(current_row);
+ break;
+ case DOUBLE_HLINE:
+ tbl->add_double_hline(current_row);
+ break;
+ default:
+ assert(0);
+ }
+ if (give_up)
+ break;
+ }
+ if (!give_up && current_row == 0) {
+ error("no real data");
+ give_up = 1;
+ }
+ if (give_up) {
+ delete tbl;
+ return 0;
+ }
+ // Do this here rather than at the beginning in case continued formats
+ // change it.
+ int i;
+ for (i = 0; i < ncolumns - 1; i++)
+ if (f->separation[i] >= 0)
+ tbl->set_column_separation(i, f->separation[i]);
+ for (i = 0; i < ncolumns; i++)
+ if (!f->width[i].empty())
+ tbl->set_minimum_width(i, f->width[i]);
+ for (i = 0; i < ncolumns; i++)
+ if (f->equal[i])
+ tbl->set_equal_column(i);
+ return tbl;
+}
+
+void process_table(table_input &in)
+{
+ int c;
+ options *opt = 0;
+ format *form = 0;
+ table *tbl = 0;
+ if ((opt = process_options(in)) != 0
+ && (form = process_format(in, opt)) != 0
+ && (tbl = process_data(in, form, opt)) != 0) {
+ tbl->print();
+ delete tbl;
+ }
+ else {
+ error("giving up on this table");
+ while ((c = in.get()) != EOF)
+ ;
+ }
+ delete opt;
+ delete form;
+ if (!in.ended())
+ error("premature end of file");
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [ -vC ] [ files... ]\n", program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "vCT:", long_options, NULL)) != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU tbl (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'T':
+ // I'm sick of getting bug reports from IRIX users
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n"
+ ".if !dTS .ds TS\n"
+ ".if !dTE .ds TE\n");
+ if (argc > optind) {
+ for (int i = optind; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == '\0') {
+ current_filename = "-";
+ current_lineno = 1;
+ printf(".lf 1 -\n");
+ process_input_file(stdin);
+ }
+ else {
+ errno = 0;
+ FILE *fp = fopen(argv[i], "r");
+ if (fp == 0) {
+ current_lineno = -1;
+ error("can't open `%1': %2", argv[i], strerror(errno));
+ }
+ else {
+ current_lineno = 1;
+ current_filename = argv[i];
+ printf(".lf 1 %s\n", current_filename);
+ process_input_file(fp);
+ }
+ }
+ }
+ else {
+ current_filename = "-";
+ current_lineno = 1;
+ printf(".lf 1 -\n");
+ process_input_file(stdin);
+ }
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ return 0;
+}
+
diff --git a/contrib/groff/src/preproc/tbl/table.cc b/contrib/groff/src/preproc/tbl/table.cc
new file mode 100644
index 000000000000..c7f96cd31632
--- /dev/null
+++ b/contrib/groff/src/preproc/tbl/table.cc
@@ -0,0 +1,2778 @@
+// -*- 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. */
+
+#include "table.h"
+
+#define BAR_HEIGHT ".25m"
+#define DOUBLE_LINE_SEP "2p"
+#define HALF_DOUBLE_LINE_SEP "1p"
+#define LINE_SEP "2p"
+#define BODY_DEPTH ".25m"
+
+const int DEFAULT_COLUMN_SEPARATION = 3;
+
+#define DELIMITER_CHAR "\\[tbl]"
+#define PREFIX "3"
+#define SEPARATION_FACTOR_REG PREFIX "sep"
+#define BOTTOM_REG PREFIX "bot"
+#define RESET_MACRO_NAME PREFIX "init"
+#define LINESIZE_REG PREFIX "lps"
+#define TOP_REG PREFIX "top"
+#define CURRENT_ROW_REG PREFIX "crow"
+#define LAST_PASSED_ROW_REG PREFIX "passed"
+#define TRANSPARENT_STRING_NAME PREFIX "trans"
+#define QUOTE_STRING_NAME PREFIX "quote"
+#define SECTION_DIVERSION_NAME PREFIX "section"
+#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
+#define SAVED_VERTICAL_POS_REG PREFIX "vert"
+#define NEED_BOTTOM_RULE_REG PREFIX "brule"
+#define KEEP_MACRO_NAME PREFIX "keep"
+#define RELEASE_MACRO_NAME PREFIX "release"
+#define SAVED_FONT_REG PREFIX "fnt"
+#define SAVED_SIZE_REG PREFIX "sz"
+#define SAVED_FILL_REG PREFIX "fll"
+#define SAVED_INDENT_REG PREFIX "ind"
+#define SAVED_CENTER_REG PREFIX "cent"
+#define TABLE_DIVERSION_NAME PREFIX "table"
+#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
+#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
+#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
+#define NEEDED_REG PREFIX "needed"
+#define REPEATED_MARK_MACRO PREFIX "rmk"
+#define REPEATED_VPT_MACRO PREFIX "rvpt"
+#define SUPPRESS_BOTTOM_REG PREFIX "supbot"
+#define SAVED_DN_REG PREFIX "dn"
+
+// this must be one character
+#define COMPATIBLE_REG PREFIX "c"
+
+#define BLOCK_WIDTH_PREFIX PREFIX "tbw"
+#define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
+#define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
+#define SPAN_WIDTH_PREFIX PREFIX "w"
+#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
+#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
+#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
+#define COLUMN_SEPARATION_PREFIX PREFIX "cs"
+#define ROW_START_PREFIX PREFIX "rs"
+#define COLUMN_START_PREFIX PREFIX "cl"
+#define COLUMN_END_PREFIX PREFIX "ce"
+#define COLUMN_DIVIDE_PREFIX PREFIX "cd"
+#define ROW_TOP_PREFIX PREFIX "rt"
+
+string block_width_reg(int r, int c);
+string block_diversion_name(int r, int c);
+string block_height_reg(int r, int c);
+string span_width_reg(int start_col, int end_col);
+string span_left_numeric_width_reg(int start_col, int end_col);
+string span_right_numeric_width_reg(int start_col, int end_col);
+string span_alphabetic_width_reg(int start_col, int end_col);
+string column_separation_reg(int col);
+string row_start_reg(int r);
+string column_start_reg(int c);
+string column_end_reg(int c);
+string column_divide_reg(int c);
+string row_top_reg(int r);
+
+void set_inline_modifier(const entry_modifier *);
+void restore_inline_modifier(const entry_modifier *m);
+void set_modifier(const entry_modifier *);
+int find_decimal_point(const char *s, char decimal_point_char,
+ const char *delim);
+
+string an_empty_string;
+int location_force_filename = 0;
+
+void printfs(const char *,
+ const string &arg1 = an_empty_string,
+ const string &arg2 = an_empty_string,
+ const string &arg3 = an_empty_string,
+ const string &arg4 = an_empty_string,
+ const string &arg5 = an_empty_string);
+
+void prints(const string &);
+
+inline void prints(char c)
+{
+ putchar(c);
+}
+
+inline void prints(const char *s)
+{
+ fputs(s, stdout);
+}
+
+void prints(const string &s)
+{
+ if (!s.empty())
+ fwrite(s.contents(), 1, s.length(), stdout);
+}
+
+struct horizontal_span {
+ horizontal_span *next;
+ short start_col;
+ short end_col;
+ horizontal_span(int, int, horizontal_span *);
+};
+
+struct single_line_entry;
+struct double_line_entry;
+struct simple_entry;
+
+class table_entry {
+friend class table;
+ table_entry *next;
+ int input_lineno;
+ const char *input_filename;
+protected:
+ int start_row;
+ int end_row;
+ short start_col;
+ short end_col;
+ const entry_modifier *mod;
+public:
+ void set_location();
+ table_entry(const entry_modifier *);
+ virtual ~table_entry();
+ virtual int divert(int ncols, const string *mw, int *sep);
+ virtual void do_width();
+ virtual void do_depth();
+ virtual void print() = 0;
+ virtual void position_vertically() = 0;
+ virtual single_line_entry *to_single_line_entry();
+ virtual double_line_entry *to_double_line_entry();
+ virtual simple_entry *to_simple_entry();
+ virtual int line_type();
+ virtual void note_double_vrule_on_right(int);
+ virtual void note_double_vrule_on_left(int);
+};
+
+class simple_entry : public table_entry {
+public:
+ simple_entry(const entry_modifier *);
+ void print();
+ void position_vertically();
+ simple_entry *to_simple_entry();
+ virtual void add_tab();
+ virtual void simple_print(int);
+};
+
+class empty_entry : public simple_entry {
+public:
+ empty_entry(const entry_modifier *);
+ int line_type();
+};
+
+class text_entry : public simple_entry {
+protected:
+ char *contents;
+ void print_contents();
+public:
+ text_entry(char *, const entry_modifier *);
+ ~text_entry();
+};
+
+void text_entry::print_contents()
+{
+ set_inline_modifier(mod);
+ prints(contents);
+ restore_inline_modifier(mod);
+}
+
+class repeated_char_entry : public text_entry {
+public:
+ repeated_char_entry(char *s, const entry_modifier *m);
+ void simple_print(int);
+};
+
+class simple_text_entry : public text_entry {
+public:
+ simple_text_entry(char *s, const entry_modifier *m);
+ void do_width();
+};
+
+class left_text_entry : public simple_text_entry {
+public:
+ left_text_entry(char *s, const entry_modifier *m);
+ void simple_print(int);
+ void add_tab();
+};
+
+class right_text_entry : public simple_text_entry {
+public:
+ right_text_entry(char *s, const entry_modifier *m);
+ void simple_print(int);
+ void add_tab();
+};
+
+class center_text_entry : public simple_text_entry {
+public:
+ center_text_entry(char *s, const entry_modifier *m);
+ void simple_print(int);
+ void add_tab();
+};
+
+class numeric_text_entry : public text_entry {
+ int dot_pos;
+public:
+ numeric_text_entry(char *s, const entry_modifier *m, int pos);
+ void do_width();
+ void simple_print(int);
+};
+
+class alphabetic_text_entry : public text_entry {
+public:
+ alphabetic_text_entry(char *s, const entry_modifier *m);
+ void do_width();
+ void simple_print(int);
+ void add_tab();
+};
+
+class line_entry : public simple_entry {
+protected:
+ char double_vrule_on_right;
+ char double_vrule_on_left;
+public:
+ line_entry(const entry_modifier *);
+ void note_double_vrule_on_right(int);
+ void note_double_vrule_on_left(int);
+ void simple_print(int) = 0;
+};
+
+class single_line_entry : public line_entry {
+public:
+ single_line_entry(const entry_modifier *m);
+ void simple_print(int);
+ single_line_entry *to_single_line_entry();
+ int line_type();
+};
+
+class double_line_entry : public line_entry {
+public:
+ double_line_entry(const entry_modifier *m);
+ void simple_print(int);
+ double_line_entry *to_double_line_entry();
+ int line_type();
+};
+
+class short_line_entry : public simple_entry {
+public:
+ short_line_entry(const entry_modifier *m);
+ void simple_print(int);
+ int line_type();
+};
+
+class short_double_line_entry : public simple_entry {
+public:
+ short_double_line_entry(const entry_modifier *m);
+ void simple_print(int);
+ int line_type();
+};
+
+class block_entry : public table_entry {
+ char *contents;
+protected:
+ void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
+public:
+ block_entry(char *s, const entry_modifier *m);
+ ~block_entry();
+ int divert(int ncols, const string *mw, int *sep);
+ void do_width();
+ void do_depth();
+ void position_vertically();
+ void print() = 0;
+};
+
+class left_block_entry : public block_entry {
+public:
+ left_block_entry(char *s, const entry_modifier *m);
+ void print();
+};
+
+class right_block_entry : public block_entry {
+public:
+ right_block_entry(char *s, const entry_modifier *m);
+ void print();
+};
+
+class center_block_entry : public block_entry {
+public:
+ center_block_entry(char *s, const entry_modifier *m);
+ void print();
+};
+
+class alphabetic_block_entry : public block_entry {
+public:
+ alphabetic_block_entry(char *s, const entry_modifier *m);
+ void print();
+ int divert(int ncols, const string *mw, int *sep);
+};
+
+table_entry::table_entry(const entry_modifier *m)
+: next(0), input_lineno(-1), input_filename(0),
+ start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m)
+{
+}
+
+table_entry::~table_entry()
+{
+}
+
+int table_entry::divert(int, const string *, int *)
+{
+ return 0;
+}
+
+void table_entry::do_width()
+{
+}
+
+single_line_entry *table_entry::to_single_line_entry()
+{
+ return 0;
+}
+
+double_line_entry *table_entry::to_double_line_entry()
+{
+ return 0;
+}
+
+simple_entry *table_entry::to_simple_entry()
+{
+ return 0;
+}
+
+void table_entry::do_depth()
+{
+}
+
+void table_entry::set_location()
+{
+ set_troff_location(input_filename, input_lineno);
+}
+
+int table_entry::line_type()
+{
+ return -1;
+}
+
+void table_entry::note_double_vrule_on_right(int)
+{
+}
+
+void table_entry::note_double_vrule_on_left(int)
+{
+}
+
+simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
+{
+}
+
+void simple_entry::add_tab()
+{
+ // do nothing
+}
+
+void simple_entry::simple_print(int)
+{
+ // do nothing
+}
+
+void simple_entry::position_vertically()
+{
+ if (start_row != end_row)
+ switch (mod->vertical_alignment) {
+ case entry_modifier::TOP:
+ printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
+ break;
+ case entry_modifier::CENTER:
+ // Peform the motion in two stages so that the center is rounded
+ // vertically upwards even if net vertical motion is upwards.
+ printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
+ printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n",
+ row_start_reg(start_row));
+ break;
+ case entry_modifier::BOTTOM:
+ printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n",
+ row_start_reg(start_row));
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void simple_entry::print()
+{
+ prints(".ta");
+ add_tab();
+ prints('\n');
+ set_location();
+ prints("\\&");
+ simple_print(0);
+ prints('\n');
+}
+
+simple_entry *simple_entry::to_simple_entry()
+{
+ return this;
+}
+
+empty_entry::empty_entry(const entry_modifier *m)
+: simple_entry(m)
+{
+}
+
+int empty_entry::line_type()
+{
+ return 0;
+}
+
+text_entry::text_entry(char *s, const entry_modifier *m)
+: simple_entry(m), contents(s)
+{
+}
+
+text_entry::~text_entry()
+{
+ a_delete contents;
+}
+
+
+repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
+: text_entry(s, m)
+{
+}
+
+void repeated_char_entry::simple_print(int)
+{
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ set_inline_modifier(mod);
+ printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
+ span_width_reg(start_col, end_col));
+ prints(contents);
+ prints(DELIMITER_CHAR);
+ restore_inline_modifier(mod);
+}
+
+simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
+: text_entry(s, m)
+{
+}
+
+void simple_text_entry::do_width()
+{
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
+ span_width_reg(start_col, end_col));
+ print_contents();
+ prints(DELIMITER_CHAR "\n");
+}
+
+left_text_entry::left_text_entry(char *s, const entry_modifier *m)
+: simple_text_entry(s, m)
+{
+}
+
+void left_text_entry::simple_print(int)
+{
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ print_contents();
+}
+
+// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
+
+void left_text_entry::add_tab()
+{
+ printfs(" \\n[%1]u", column_end_reg(end_col));
+}
+
+right_text_entry::right_text_entry(char *s, const entry_modifier *m)
+: simple_text_entry(s, m)
+{
+}
+
+void right_text_entry::simple_print(int)
+{
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ prints("\002\003");
+ print_contents();
+ prints("\002");
+}
+
+void right_text_entry::add_tab()
+{
+ printfs(" \\n[%1]u", column_end_reg(end_col));
+}
+
+center_text_entry::center_text_entry(char *s, const entry_modifier *m)
+: simple_text_entry(s, m)
+{
+}
+
+void center_text_entry::simple_print(int)
+{
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ prints("\002\003");
+ print_contents();
+ prints("\003\002");
+}
+
+void center_text_entry::add_tab()
+{
+ printfs(" \\n[%1]u", column_end_reg(end_col));
+}
+
+numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
+: text_entry(s, m), dot_pos(pos)
+{
+}
+
+void numeric_text_entry::do_width()
+{
+ if (dot_pos != 0) {
+ set_location();
+ printfs(".nr %1 0\\w" DELIMITER_CHAR,
+ block_width_reg(start_row, start_col));
+ set_inline_modifier(mod);
+ for (int i = 0; i < dot_pos; i++)
+ prints(contents[i]);
+ restore_inline_modifier(mod);
+ prints(DELIMITER_CHAR "\n");
+ printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
+ span_left_numeric_width_reg(start_col, end_col),
+ block_width_reg(start_row, start_col));
+ }
+ else
+ printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
+ if (contents[dot_pos] != '\0') {
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
+ span_right_numeric_width_reg(start_col, end_col));
+ set_inline_modifier(mod);
+ prints(contents + dot_pos);
+ restore_inline_modifier(mod);
+ prints(DELIMITER_CHAR "\n");
+ }
+}
+
+void numeric_text_entry::simple_print(int)
+{
+ printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
+ span_width_reg(start_col, end_col),
+ span_left_numeric_width_reg(start_col, end_col),
+ span_right_numeric_width_reg(start_col, end_col),
+ column_start_reg(start_col),
+ block_width_reg(start_row, start_col));
+ print_contents();
+}
+
+alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
+: text_entry(s, m)
+{
+}
+
+void alphabetic_text_entry::do_width()
+{
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
+ span_alphabetic_width_reg(start_col, end_col));
+ print_contents();
+ prints(DELIMITER_CHAR "\n");
+}
+
+void alphabetic_text_entry::simple_print(int)
+{
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
+ span_width_reg(start_col, end_col),
+ span_alphabetic_width_reg(start_col, end_col));
+ print_contents();
+}
+
+// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr.
+
+void alphabetic_text_entry::add_tab()
+{
+ printfs(" \\n[%1]u", column_end_reg(end_col));
+}
+
+block_entry::block_entry(char *s, const entry_modifier *m)
+: table_entry(m), contents(s)
+{
+}
+
+block_entry::~block_entry()
+{
+ a_delete contents;
+}
+
+void block_entry::position_vertically()
+{
+ if (start_row != end_row)
+ switch(mod->vertical_alignment) {
+ case entry_modifier::TOP:
+ printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
+ break;
+ case entry_modifier::CENTER:
+ // Peform the motion in two stages so that the center is rounded
+ // vertically upwards even if net vertical motion is upwards.
+ printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
+ printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n",
+ row_start_reg(start_row),
+ block_height_reg(start_row, start_col));
+ break;
+ case entry_modifier::BOTTOM:
+ printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n",
+ row_start_reg(start_row),
+ block_height_reg(start_row, start_col));
+ break;
+ default:
+ assert(0);
+ }
+ if (mod->stagger)
+ prints(".sp -.5v\n");
+}
+
+int block_entry::divert(int ncols, const string *mw, int *sep)
+{
+ do_divert(0, ncols, mw, sep);
+ return 1;
+}
+
+void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
+ int *sep)
+{
+ printfs(".di %1\n", block_diversion_name(start_row, start_col));
+ prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
+ ".in 0\n");
+ prints(".ll ");
+ int i;
+ for (i = start_col; i <= end_col; i++)
+ if (mw[i].empty())
+ break;
+ if (i > end_col) {
+ // Every column spanned by this entry has a minimum width.
+ for (int j = start_col; j <= end_col; j++) {
+ if (j > start_col) {
+ if (sep)
+ printfs("+%1n", as_string(sep[j - 1]));
+ prints('+');
+ }
+ printfs("(n;%1)", mw[j]);
+ }
+ printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
+ }
+ else
+ printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))",
+ span_width_reg(start_col, end_col),
+ as_string(end_col - start_col + 1),
+ as_string(ncols + 1));
+ if (alphabetic)
+ prints("-2n");
+ prints("\n");
+ set_modifier(mod);
+ prints(".cp \\n(" COMPATIBLE_REG "\n");
+ set_location();
+ prints(contents);
+ prints(".br\n.di\n.cp 0\n");
+ if (!mod->zero_width) {
+ if (alphabetic) {
+ printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
+ span_width_reg(start_col, end_col));
+ printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
+ span_alphabetic_width_reg(start_col, end_col));
+ }
+ else
+ printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
+ }
+ printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
+ printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
+ prints("." RESET_MACRO_NAME "\n"
+ ".in \\n[" SAVED_INDENT_REG "]u\n"
+ ".nf\n");
+ // the block might have contained .lf commands
+ location_force_filename = 1;
+}
+
+void block_entry::do_width()
+{
+ // do nothing; the action happens in divert
+}
+
+void block_entry::do_depth()
+{
+ printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
+ row_start_reg(start_row),
+ block_height_reg(start_row, start_col));
+}
+
+left_block_entry::left_block_entry(char *s, const entry_modifier *m)
+: block_entry(s, m)
+{
+}
+
+void left_block_entry::print()
+{
+ printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
+ printfs(".%1\n", block_diversion_name(start_row, start_col));
+ prints(".in\n");
+}
+
+
+
+right_block_entry::right_block_entry(char *s, const entry_modifier *m)
+: block_entry(s, m)
+{
+}
+
+void right_block_entry::print()
+{
+ printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
+ column_start_reg(start_col),
+ span_width_reg(start_col, end_col),
+ block_width_reg(start_row, start_col));
+ printfs(".%1\n", block_diversion_name(start_row, start_col));
+ prints(".in\n");
+}
+
+center_block_entry::center_block_entry(char *s, const entry_modifier *m)
+: block_entry(s, m)
+{
+}
+
+void center_block_entry::print()
+{
+ printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
+ column_start_reg(start_col),
+ span_width_reg(start_col, end_col),
+ block_width_reg(start_row, start_col));
+ printfs(".%1\n", block_diversion_name(start_row, start_col));
+ prints(".in\n");
+}
+
+alphabetic_block_entry::alphabetic_block_entry(char *s,
+ const entry_modifier *m)
+: block_entry(s, m)
+{
+}
+
+int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
+{
+ do_divert(1, ncols, mw, sep);
+ return 1;
+}
+
+void alphabetic_block_entry::print()
+{
+ printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
+ column_start_reg(start_col),
+ span_width_reg(start_col, end_col),
+ span_alphabetic_width_reg(start_col, end_col));
+ printfs(".%1\n", block_diversion_name(start_row, start_col));
+ prints(".in\n");
+}
+
+line_entry::line_entry(const entry_modifier *m)
+: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
+{
+}
+
+void line_entry::note_double_vrule_on_right(int is_corner)
+{
+ double_vrule_on_right = is_corner ? 1 : 2;
+}
+
+void line_entry::note_double_vrule_on_left(int is_corner)
+{
+ double_vrule_on_left = is_corner ? 1 : 2;
+}
+
+
+single_line_entry::single_line_entry(const entry_modifier *m)
+: line_entry(m)
+{
+}
+
+int single_line_entry::line_type()
+{
+ return 1;
+}
+
+void single_line_entry::simple_print(int dont_move)
+{
+ printfs("\\h'|\\n[%1]u",
+ column_divide_reg(start_col));
+ if (double_vrule_on_left) {
+ prints(double_vrule_on_left == 1 ? "-" : "+");
+ prints(HALF_DOUBLE_LINE_SEP);
+ }
+ prints("'");
+ if (!dont_move)
+ prints("\\v'-" BAR_HEIGHT "'");
+ printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
+ column_divide_reg(end_col+1));
+ if (double_vrule_on_right) {
+ prints(double_vrule_on_left == 1 ? "+" : "-");
+ prints(HALF_DOUBLE_LINE_SEP);
+ }
+ prints("0'\\s0");
+ if (!dont_move)
+ prints("\\v'" BAR_HEIGHT "'");
+}
+
+single_line_entry *single_line_entry::to_single_line_entry()
+{
+ return this;
+}
+
+double_line_entry::double_line_entry(const entry_modifier *m)
+: line_entry(m)
+{
+}
+
+int double_line_entry::line_type()
+{
+ return 2;
+}
+
+void double_line_entry::simple_print(int dont_move)
+{
+ if (!dont_move)
+ prints("\\v'-" BAR_HEIGHT "'");
+ printfs("\\h'|\\n[%1]u",
+ column_divide_reg(start_col));
+ if (double_vrule_on_left) {
+ prints(double_vrule_on_left == 1 ? "-" : "+");
+ prints(HALF_DOUBLE_LINE_SEP);
+ }
+ prints("'");
+ printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
+ "\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l |\\n[%1]u",
+ column_divide_reg(end_col+1));
+ if (double_vrule_on_right)
+ prints("-" HALF_DOUBLE_LINE_SEP);
+ prints(" 0'");
+ printfs("\\v'" DOUBLE_LINE_SEP "'"
+ "\\D'l |\\n[%1]u",
+ column_divide_reg(start_col));
+ if (double_vrule_on_right) {
+ prints(double_vrule_on_left == 1 ? "+" : "-");
+ prints(HALF_DOUBLE_LINE_SEP);
+ }
+ prints(" 0'");
+ prints("\\s0"
+ "\\v'-" HALF_DOUBLE_LINE_SEP "'");
+ if (!dont_move)
+ prints("\\v'" BAR_HEIGHT "'");
+}
+
+double_line_entry *double_line_entry::to_double_line_entry()
+{
+ return this;
+}
+
+short_line_entry::short_line_entry(const entry_modifier *m)
+: simple_entry(m)
+{
+}
+
+int short_line_entry::line_type()
+{
+ return 1;
+}
+
+void short_line_entry::simple_print(int dont_move)
+{
+ if (mod->stagger)
+ prints("\\v'-.5v'");
+ if (!dont_move)
+ prints("\\v'-" BAR_HEIGHT "'");
+ printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
+ printfs("\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l \\n[%1]u 0'"
+ "\\s0",
+ span_width_reg(start_col, end_col));
+ if (!dont_move)
+ prints("\\v'" BAR_HEIGHT "'");
+ if (mod->stagger)
+ prints("\\v'.5v'");
+}
+
+short_double_line_entry::short_double_line_entry(const entry_modifier *m)
+: simple_entry(m)
+{
+}
+
+int short_double_line_entry::line_type()
+{
+ return 2;
+}
+
+void short_double_line_entry::simple_print(int dont_move)
+{
+ if (mod->stagger)
+ prints("\\v'-.5v'");
+ if (!dont_move)
+ prints("\\v'-" BAR_HEIGHT "'");
+ printfs("\\h'|\\n[%2]u'"
+ "\\v'-" HALF_DOUBLE_LINE_SEP "'"
+ "\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l \\n[%1]u 0'"
+ "\\v'" DOUBLE_LINE_SEP "'"
+ "\\D'l |\\n[%2]u 0'"
+ "\\s0"
+ "\\v'-" HALF_DOUBLE_LINE_SEP "'",
+ span_width_reg(start_col, end_col),
+ column_start_reg(start_col));
+ if (!dont_move)
+ prints("\\v'" BAR_HEIGHT "'");
+ if (mod->stagger)
+ prints("\\v'.5v'");
+}
+
+void set_modifier(const entry_modifier *m)
+{
+ if (!m->font.empty())
+ printfs(".ft %1\n", m->font);
+ if (m->point_size.val != 0) {
+ prints(".ps ");
+ if (m->point_size.inc > 0)
+ prints('+');
+ else if (m->point_size.inc < 0)
+ prints('-');
+ printfs("%1\n", as_string(m->point_size.val));
+ }
+ if (m->vertical_spacing.val != 0) {
+ prints(".vs ");
+ if (m->vertical_spacing.inc > 0)
+ prints('+');
+ else if (m->vertical_spacing.inc < 0)
+ prints('-');
+ printfs("%1\n", as_string(m->vertical_spacing.val));
+ }
+}
+
+void set_inline_modifier(const entry_modifier *m)
+{
+ if (!m->font.empty())
+ printfs("\\f[%1]", m->font);
+ if (m->point_size.val != 0) {
+ prints("\\s[");
+ if (m->point_size.inc > 0)
+ prints('+');
+ else if (m->point_size.inc < 0)
+ prints('-');
+ printfs("%1]", as_string(m->point_size.val));
+ }
+ if (m->stagger)
+ prints("\\v'-.5v'");
+}
+
+void restore_inline_modifier(const entry_modifier *m)
+{
+ if (!m->font.empty())
+ prints("\\f[\\n[" SAVED_FONT_REG "]]");
+ if (m->point_size.val != 0)
+ prints("\\s[\\n[" SAVED_SIZE_REG "]]");
+ if (m->stagger)
+ prints("\\v'.5v'");
+}
+
+
+struct stuff {
+ stuff *next;
+ int row; // occurs before row `row'
+ char printed; // has it been printed?
+
+ stuff(int);
+ virtual void print(table *) = 0;
+ virtual ~stuff();
+ virtual int is_single_line() { return 0; };
+ virtual int is_double_line() { return 0; };
+};
+
+stuff::stuff(int r) : next(0), row(r), printed(0)
+{
+}
+
+stuff::~stuff()
+{
+}
+
+struct text_stuff : public stuff {
+ string contents;
+ const char *filename;
+ int lineno;
+
+ text_stuff(const string &, int r, const char *fn, int ln);
+ ~text_stuff();
+ void print(table *);
+};
+
+
+text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
+: stuff(r), contents(s), filename(fn), lineno(ln)
+{
+}
+
+text_stuff::~text_stuff()
+{
+}
+
+void text_stuff::print(table *)
+{
+ printed = 1;
+ prints(".cp \\n(" COMPATIBLE_REG "\n");
+ set_troff_location(filename, lineno);
+ prints(contents);
+ prints(".cp 0\n");
+ location_force_filename = 1; // it might have been a .lf command
+}
+
+struct single_hline_stuff : public stuff {
+ single_hline_stuff(int r);
+ void print(table *);
+ int is_single_line();
+};
+
+single_hline_stuff::single_hline_stuff(int r) : stuff(r)
+{
+}
+
+void single_hline_stuff::print(table *tbl)
+{
+ printed = 1;
+ tbl->print_single_hline(row);
+}
+
+int single_hline_stuff::is_single_line()
+{
+ return 1;
+}
+
+struct double_hline_stuff : stuff {
+ double_hline_stuff(int r);
+ void print(table *);
+ int is_double_line();
+};
+
+double_hline_stuff::double_hline_stuff(int r) : stuff(r)
+{
+}
+
+void double_hline_stuff::print(table *tbl)
+{
+ printed = 1;
+ tbl->print_double_hline(row);
+}
+
+int double_hline_stuff::is_double_line()
+{
+ return 1;
+}
+
+struct vertical_rule {
+ vertical_rule *next;
+ int start_row;
+ int end_row;
+ short col;
+ char is_double;
+ string top_adjust;
+ string bot_adjust;
+
+ vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
+ ~vertical_rule();
+ void contribute_to_bottom_macro(table *);
+ void print();
+};
+
+vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
+: next(p), start_row(sr), end_row(er), col(c), is_double(dbl)
+{
+}
+
+vertical_rule::~vertical_rule()
+{
+}
+
+void vertical_rule::contribute_to_bottom_macro(table *tbl)
+{
+ printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
+ as_string(start_row));
+ if (end_row != tbl->get_nrows() - 1)
+ printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
+ as_string(end_row));
+ prints(" \\{");
+ printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
+ as_string(start_row),
+ row_top_reg(start_row));
+ const char *offset_table[3];
+ if (is_double) {
+ offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
+ offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
+ offset_table[2] = 0;
+ }
+ else {
+ offset_table[0] = "";
+ offset_table[1] = 0;
+ }
+ for (const char **offsetp = offset_table; *offsetp; offsetp++) {
+ prints(".sp -1\n"
+ "\\v'" BODY_DEPTH);
+ if (!bot_adjust.empty())
+ printfs("+%1", bot_adjust);
+ prints("'");
+ printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
+ column_divide_reg(col),
+ row_top_reg(start_row),
+ *offsetp);
+ if (!bot_adjust.empty())
+ printfs("-(%1)", bot_adjust);
+ // don't perform the top adjustment if the top is actually #T
+ if (!top_adjust.empty())
+ printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
+ top_adjust,
+ as_string(start_row));
+ prints("'\\s0\n");
+ }
+ prints(".\\}\n");
+}
+
+void vertical_rule::print()
+{
+ printfs("\\*[" TRANSPARENT_STRING_NAME "]"
+ ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
+ ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
+ as_string(start_row),
+ row_top_reg(start_row));
+ const char *offset_table[3];
+ if (is_double) {
+ offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
+ offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
+ offset_table[2] = 0;
+ }
+ else {
+ offset_table[0] = "";
+ offset_table[1] = 0;
+ }
+ for (const char **offsetp = offset_table; *offsetp; offsetp++) {
+ prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
+ "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
+ if (!bot_adjust.empty())
+ printfs("+%1", bot_adjust);
+ prints("'");
+ printfs("\\h'\\n[%1]u%3'"
+ "\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
+ column_divide_reg(col),
+ row_top_reg(start_row),
+ *offsetp);
+ if (!bot_adjust.empty())
+ printfs("-(%1)", bot_adjust);
+ // don't perform the top adjustment if the top is actually #T
+ if (!top_adjust.empty())
+ printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
+ LAST_PASSED_ROW_REG "]))",
+ top_adjust,
+ as_string(start_row));
+ prints("'"
+ "\\s0\n");
+ }
+}
+
+table::table(int nc, unsigned f, int ls, char dpc)
+: flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc),
+ vrule_list(0), stuff_list(0), span_list(0),
+ entry_list(0), entry_list_tailp(&entry_list), entry(0),
+ vline(0), row_is_all_lines(0), left_separation(0), right_separation(0),
+ allocated_rows(0)
+{
+ minimum_width = new string[ncolumns];
+ column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
+ equal = new char[ncolumns];
+ int i;
+ for (i = 0; i < ncolumns; i++)
+ equal[i] = 0;
+ for (i = 0; i < ncolumns-1; i++)
+ column_separation[i] = DEFAULT_COLUMN_SEPARATION;
+ delim[0] = delim[1] = '\0';
+}
+
+table::~table()
+{
+ for (int i = 0; i < nrows; i++) {
+ a_delete entry[i];
+ a_delete vline[i];
+ }
+ a_delete entry;
+ a_delete vline;
+ while (entry_list) {
+ table_entry *tem = entry_list;
+ entry_list = entry_list->next;
+ delete tem;
+ }
+ ad_delete(ncolumns) minimum_width;
+ a_delete column_separation;
+ a_delete equal;
+ while (stuff_list) {
+ stuff *tem = stuff_list;
+ stuff_list = stuff_list->next;
+ delete tem;
+ }
+ while (vrule_list) {
+ vertical_rule *tem = vrule_list;
+ vrule_list = vrule_list->next;
+ delete tem;
+ }
+ a_delete row_is_all_lines;
+ while (span_list) {
+ horizontal_span *tem = span_list;
+ span_list = span_list->next;
+ delete tem;
+ }
+}
+
+void table::set_delim(char c1, char c2)
+{
+ delim[0] = c1;
+ delim[1] = c2;
+}
+
+void table::set_minimum_width(int c, const string &w)
+{
+ assert(c >= 0 && c < ncolumns);
+ minimum_width[c] = w;
+}
+
+void table::set_column_separation(int c, int n)
+{
+ assert(c >= 0 && c < ncolumns - 1);
+ column_separation[c] = n;
+}
+
+void table::set_equal_column(int c)
+{
+ assert(c >= 0 && c < ncolumns);
+ equal[c] = 1;
+}
+
+void table::add_stuff(stuff *p)
+{
+ stuff **pp;
+ for (pp = &stuff_list; *pp; pp = &(*pp)->next)
+ ;
+ *pp = p;
+}
+
+void table::add_text_line(int r, const string &s, const char *filename, int lineno)
+{
+ add_stuff(new text_stuff(s, r, filename, lineno));
+}
+
+void table::add_single_hline(int r)
+{
+ add_stuff(new single_hline_stuff(r));
+}
+
+void table::add_double_hline(int r)
+{
+ add_stuff(new double_hline_stuff(r));
+}
+
+void table::allocate(int r)
+{
+ if (r >= nrows) {
+ typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
+ if (r >= allocated_rows) {
+ if (allocated_rows == 0) {
+ allocated_rows = 16;
+ if (allocated_rows <= r)
+ allocated_rows = r + 1;
+ entry = new PPtable_entry[allocated_rows];
+ vline = new char*[allocated_rows];
+ }
+ else {
+ table_entry ***old_entry = entry;
+ int old_allocated_rows = allocated_rows;
+ allocated_rows *= 2;
+ if (allocated_rows <= r)
+ allocated_rows = r + 1;
+ entry = new PPtable_entry[allocated_rows];
+ memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
+ a_delete old_entry;
+ char **old_vline = vline;
+ vline = new char*[allocated_rows];
+ memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
+ a_delete old_vline;
+ }
+ }
+ assert(allocated_rows > r);
+ while (nrows <= r) {
+ entry[nrows] = new table_entry*[ncolumns];
+ int i;
+ for (i = 0; i < ncolumns; i++)
+ entry[nrows][i] = 0;
+ vline[nrows] = new char[ncolumns+1];
+ for (i = 0; i < ncolumns+1; i++)
+ vline[nrows][i] = 0;
+ nrows++;
+ }
+ }
+}
+
+void table::do_hspan(int r, int c)
+{
+ assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
+ if (c == 0) {
+ error("first column cannot be horizontally spanned");
+ return;
+ }
+ table_entry *e = entry[r][c];
+ if (e) {
+ assert(e->start_row <= r && r <= e->end_row
+ && e->start_col <= c && c <= e->end_col
+ && e->end_row - e->start_row > 0
+ && e->end_col - e->start_col > 0);
+ return;
+ }
+ e = entry[r][c-1];
+ // e can be 0 if we had an empty entry or an error
+ if (e == 0)
+ return;
+ if (e->start_row != r) {
+ /*
+ l l
+ ^ s */
+ error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
+ }
+ else {
+ e->end_col = c;
+ entry[r][c] = e;
+ }
+}
+
+void table::do_vspan(int r, int c)
+{
+ assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
+ if (r == 0) {
+ error("first row cannot be vertically spanned");
+ return;
+ }
+ table_entry *e = entry[r][c];
+ if (e) {
+ assert(e->start_row <= r && r <= e->end_row
+ && e->start_col <= c && c <= e->end_col
+ && e->end_row - e->start_row > 0
+ && e->end_col - e->start_col > 0);
+ return;
+ }
+ e = entry[r-1][c];
+ // e can be 0 if we had an empty entry or an error
+ if (e == 0)
+ return;
+ if (e->start_col != c) {
+ /* l s
+ l ^ */
+ error("impossible vertical span at row %1, column %2", r + 1, c + 1);
+ }
+ else {
+ for (int i = c; i <= e->end_col; i++) {
+ assert(entry[r][i] == 0);
+ entry[r][i] = e;
+ }
+ e->end_row = r;
+ }
+}
+
+int find_decimal_point(const char *s, char decimal_point_char,
+ const char *delim)
+{
+ if (s == 0 || *s == '\0')
+ return -1;
+ const char *p;
+ int in_delim = 0; // is p within eqn delimiters?
+ // tbl recognises \& even within eqn delimiters; I don't
+ for (p = s; *p; p++)
+ if (in_delim) {
+ if (*p == delim[1])
+ in_delim = 0;
+ }
+ else if (*p == delim[0])
+ in_delim = 1;
+ else if (p[0] == '\\' && p[1] == '&')
+ return p - s;
+ int possible_pos = -1;
+ in_delim = 0;
+ for (p = s; *p; p++)
+ if (in_delim) {
+ if (*p == delim[1])
+ in_delim = 0;
+ }
+ else if (*p == delim[0])
+ in_delim = 1;
+ else if (p[0] == decimal_point_char && csdigit(p[1]))
+ possible_pos = p - s;
+ if (possible_pos >= 0)
+ return possible_pos;
+ in_delim = 0;
+ for (p = s; *p; p++)
+ if (in_delim) {
+ if (*p == delim[1])
+ in_delim = 0;
+ }
+ else if (*p == delim[0])
+ in_delim = 1;
+ else if (csdigit(*p))
+ possible_pos = p + 1 - s;
+ return possible_pos;
+}
+
+void table::add_entry(int r, int c, const string &str, const entry_format *f,
+ const char *fn, int ln)
+{
+ allocate(r);
+ table_entry *e = 0;
+ if (str == "\\_") {
+ e = new short_line_entry(f);
+ }
+ else if (str == "\\=") {
+ e = new short_double_line_entry(f);
+ }
+ else if (str == "_") {
+ single_line_entry *lefte;
+ if (c > 0 && entry[r][c-1] != 0 &&
+ (lefte = entry[r][c-1]->to_single_line_entry()) != 0
+ && lefte->start_row == r
+ && lefte->mod->stagger == f->stagger) {
+ lefte->end_col = c;
+ entry[r][c] = lefte;
+ }
+ else
+ e = new single_line_entry(f);
+ }
+ else if (str == "=") {
+ double_line_entry *lefte;
+ if (c > 0 && entry[r][c-1] != 0 &&
+ (lefte = entry[r][c-1]->to_double_line_entry()) != 0
+ && lefte->start_row == r
+ && lefte->mod->stagger == f->stagger) {
+ lefte->end_col = c;
+ entry[r][c] = lefte;
+ }
+ else
+ e = new double_line_entry(f);
+ }
+ else if (str == "\\^") {
+ do_vspan(r, c);
+ }
+ else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
+ if (str.search('\n') >= 0)
+ error_with_file_and_line(fn, ln, "bad repeated character");
+ else {
+ char *s = str.substring(2, str.length() - 2).extract();
+ e = new repeated_char_entry(s, f);
+ }
+ }
+ else {
+ int is_block = str.search('\n') >= 0;
+ char *s;
+ switch (f->type) {
+ case FORMAT_SPAN:
+ assert(str.empty());
+ do_hspan(r, c);
+ break;
+ case FORMAT_LEFT:
+ if (!str.empty()) {
+ s = str.extract();
+ if (is_block)
+ e = new left_block_entry(s, f);
+ else
+ e = new left_text_entry(s, f);
+ }
+ else
+ e = new empty_entry(f);
+ break;
+ case FORMAT_CENTER:
+ if (!str.empty()) {
+ s = str.extract();
+ if (is_block)
+ e = new center_block_entry(s, f);
+ else
+ e = new center_text_entry(s, f);
+ }
+ else
+ e = new empty_entry(f);
+ break;
+ case FORMAT_RIGHT:
+ if (!str.empty()) {
+ s = str.extract();
+ if (is_block)
+ e = new right_block_entry(s, f);
+ else
+ e = new right_text_entry(s, f);
+ }
+ else
+ e = new empty_entry(f);
+ break;
+ case FORMAT_NUMERIC:
+ if (!str.empty()) {
+ s = str.extract();
+ if (is_block) {
+ error_with_file_and_line(fn, ln, "can't have numeric text block");
+ e = new left_block_entry(s, f);
+ }
+ else {
+ int pos = find_decimal_point(s, decimal_point_char, delim);
+ if (pos < 0)
+ e = new center_text_entry(s, f);
+ else
+ e = new numeric_text_entry(s, f, pos);
+ }
+ }
+ else
+ e = new empty_entry(f);
+ break;
+ case FORMAT_ALPHABETIC:
+ if (!str.empty()) {
+ s = str.extract();
+ if (is_block)
+ e = new alphabetic_block_entry(s, f);
+ else
+ e = new alphabetic_text_entry(s, f);
+ }
+ else
+ e = new empty_entry(f);
+ break;
+ case FORMAT_VSPAN:
+ do_vspan(r, c);
+ break;
+ case FORMAT_HLINE:
+ if (str.length() != 0)
+ error_with_file_and_line(fn, ln,
+ "non-empty data entry for `_' format ignored");
+ e = new single_line_entry(f);
+ break;
+ case FORMAT_DOUBLE_HLINE:
+ if (str.length() != 0)
+ error_with_file_and_line(fn, ln,
+ "non-empty data entry for `=' format ignored");
+ e = new double_line_entry(f);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ if (e) {
+ table_entry *preve = entry[r][c];
+ if (preve) {
+ /* c s
+ ^ l */
+ error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
+ r + 1, c + 1);
+ delete e;
+ }
+ else {
+ e->input_lineno = ln;
+ e->input_filename = fn;
+ e->start_row = e->end_row = r;
+ e->start_col = e->end_col = c;
+ *entry_list_tailp = e;
+ entry_list_tailp = &e->next;
+ entry[r][c] = e;
+ }
+ }
+}
+
+// add vertical lines for row r
+
+void table::add_vlines(int r, const char *v)
+{
+ allocate(r);
+ for (int i = 0; i < ncolumns+1; i++)
+ vline[r][i] = v[i];
+}
+
+void table::check()
+{
+ table_entry *p = entry_list;
+ int i, j;
+ while (p) {
+ for (i = p->start_row; i <= p->end_row; i++)
+ for (j = p->start_col; j <= p->end_col; j++)
+ assert(entry[i][j] == p);
+ p = p->next;
+ }
+}
+
+void table::print()
+{
+ location_force_filename = 1;
+ check();
+ init_output();
+ determine_row_type();
+ compute_widths();
+ if (!(flags & CENTER))
+ prints(".if \\n[" SAVED_CENTER_REG "] \\{");
+ prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n"
+ ".nr " SAVED_INDENT_REG " \\n[.i]\n");
+ if (!(flags & CENTER))
+ prints(".\\}\n");
+ build_vrule_list();
+ define_bottom_macro();
+ do_top();
+ for (int i = 0; i < nrows; i++)
+ do_row(i);
+ do_bottom();
+}
+
+void table::determine_row_type()
+{
+ row_is_all_lines = new char[nrows];
+ for (int i = 0; i < nrows; i++) {
+ int had_single = 0;
+ int had_double = 0;
+ int had_non_line = 0;
+ for (int c = 0; c < ncolumns; c++) {
+ table_entry *e = entry[i][c];
+ if (e != 0) {
+ if (e->start_row == e->end_row) {
+ int t = e->line_type();
+ switch (t) {
+ case -1:
+ had_non_line = 1;
+ break;
+ case 0:
+ // empty
+ break;
+ case 1:
+ had_single = 1;
+ break;
+ case 2:
+ had_double = 1;
+ break;
+ default:
+ assert(0);
+ }
+ if (had_non_line)
+ break;
+ }
+ c = e->end_col;
+ }
+ }
+ if (had_non_line)
+ row_is_all_lines[i] = 0;
+ else if (had_double)
+ row_is_all_lines[i] = 2;
+ else if (had_single)
+ row_is_all_lines[i] = 1;
+ else
+ row_is_all_lines[i] = 0;
+ }
+}
+
+
+void table::init_output()
+{
+ prints(".nr " COMPATIBLE_REG " \\n(.C\n"
+ ".cp 0\n");
+ if (linesize > 0)
+ printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
+ else
+ prints(".nr " LINESIZE_REG " \\n[.s]\n");
+ if (!(flags & CENTER))
+ prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
+ prints(".de " RESET_MACRO_NAME "\n"
+ ".ft \\n[.f]\n"
+ ".ps \\n[.s]\n"
+ ".vs \\n[.v]u\n"
+ ".in \\n[.i]u\n"
+ ".ll \\n[.l]u\n"
+ ".ls \\n[.L]\n"
+ ".ad \\n[.j]\n"
+ ".ie \\n[.u] .fi\n"
+ ".el .nf\n"
+ ".ce \\n[.ce]\n"
+ "..\n"
+ ".nr " SAVED_INDENT_REG " \\n[.i]\n"
+ ".nr " SAVED_FONT_REG " \\n[.f]\n"
+ ".nr " SAVED_SIZE_REG " \\n[.s]\n"
+ ".nr " SAVED_FILL_REG " \\n[.u]\n"
+ ".nr T. 0\n"
+ ".nr " CURRENT_ROW_REG " 0-1\n"
+ ".nr " LAST_PASSED_ROW_REG " 0-1\n"
+ ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
+ ".ds " TRANSPARENT_STRING_NAME "\n"
+ ".ds " QUOTE_STRING_NAME "\n"
+ ".nr " NEED_BOTTOM_RULE_REG " 1\n"
+ ".nr " SUPPRESS_BOTTOM_REG " 0\n"
+ ".eo\n"
+ ".de " REPEATED_MARK_MACRO "\n"
+ ".mk \\$1\n"
+ ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
+ "..\n"
+ ".de " REPEATED_VPT_MACRO "\n"
+ ".vpt \\$1\n"
+ ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
+ "..\n");
+ if (!(flags & NOKEEP))
+ prints(".de " KEEP_MACRO_NAME "\n"
+ ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
+ ".ds " TRANSPARENT_STRING_NAME " \\!\n"
+ ".di " SECTION_DIVERSION_NAME "\n"
+ ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
+ ".in 0\n"
+ ".\\}\n"
+ "..\n"
+ ".de " RELEASE_MACRO_NAME "\n"
+ ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
+ ".di\n"
+ ".in \\n[" SAVED_INDENT_REG "]u\n"
+ ".nr " SAVED_DN_REG " \\n[dn]\n"
+ ".ds " QUOTE_STRING_NAME "\n"
+ ".ds " TRANSPARENT_STRING_NAME "\n"
+ ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
+ ".if \\n[.t]<=\\n[dn] \\{"
+ ".nr T. 1\n"
+ ".T#\n"
+ ".nr " SUPPRESS_BOTTOM_REG " 1\n"
+ ".sp \\n[.t]u\n"
+ ".nr " SUPPRESS_BOTTOM_REG " 0\n"
+ ".mk #T\n"
+ ".\\}\n"
+ ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
+ /* Since we turn off traps, it won't get into an infinite loop
+ when we try and print it; it will just go off the bottom of the
+ page. */
+ ".tm warning: page \\n%: table text block will not fit on one page\n"
+ ".nf\n"
+ ".ls 1\n"
+ "." SECTION_DIVERSION_NAME "\n"
+ ".ls\n"
+ ".rm " SECTION_DIVERSION_NAME "\n"
+ ".\\}\n"
+ "..\n"
+ ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
+ ".de " TABLE_KEEP_MACRO_NAME "\n"
+ ".if '\\n[.z]'' \\{"
+ ".di " TABLE_DIVERSION_NAME "\n"
+ ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
+ ".\\}\n"
+ "..\n"
+ ".de " TABLE_RELEASE_MACRO_NAME "\n"
+ ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
+ ".di\n"
+ ".nr " SAVED_DN_REG " \\n[dn]\n"
+ ".ne \\n[dn]u+\\n[.V]u\n"
+ ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
+ ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
+ ".el \\{"
+ ".in 0\n"
+ ".ls 1\n"
+ ".nf\n"
+ "." TABLE_DIVERSION_NAME "\n"
+ ".\\}\n"
+ ".rm " TABLE_DIVERSION_NAME "\n"
+ ".\\}\n"
+ "..\n");
+ prints(".ec\n"
+ ".ce 0\n"
+ ".nf\n");
+}
+
+string block_width_reg(int r, int c)
+{
+ static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
+ return string(name);
+}
+
+string block_diversion_name(int r, int c)
+{
+ static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
+ return string(name);
+}
+
+string block_height_reg(int r, int c)
+{
+ static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
+ return string(name);
+}
+
+string span_width_reg(int start_col, int end_col)
+{
+ static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
+ if (end_col != start_col)
+ sprintf(strchr(name, '\0'), ",%d", end_col);
+ return string(name);
+}
+
+string span_left_numeric_width_reg(int start_col, int end_col)
+{
+ static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
+ if (end_col != start_col)
+ sprintf(strchr(name, '\0'), ",%d", end_col);
+ return string(name);
+}
+
+string span_right_numeric_width_reg(int start_col, int end_col)
+{
+ static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
+ if (end_col != start_col)
+ sprintf(strchr(name, '\0'), ",%d", end_col);
+ return string(name);
+}
+
+string span_alphabetic_width_reg(int start_col, int end_col)
+{
+ static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
+ sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
+ if (end_col != start_col)
+ sprintf(strchr(name, '\0'), ",%d", end_col);
+ return string(name);
+}
+
+
+string column_separation_reg(int col)
+{
+ static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
+ sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
+ return string(name);
+}
+
+string row_start_reg(int row)
+{
+ static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
+ sprintf(name, ROW_START_PREFIX "%d", row);
+ return string(name);
+}
+
+string column_start_reg(int col)
+{
+ static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
+ sprintf(name, COLUMN_START_PREFIX "%d", col);
+ return string(name);
+}
+
+string column_end_reg(int col)
+{
+ static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
+ sprintf(name, COLUMN_END_PREFIX "%d", col);
+ return string(name);
+}
+
+string column_divide_reg(int col)
+{
+ static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
+ sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
+ return string(name);
+}
+
+string row_top_reg(int row)
+{
+ static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
+ sprintf(name, ROW_TOP_PREFIX "%d", row);
+ return string(name);
+}
+
+void init_span_reg(int start_col, int end_col)
+{
+ printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
+ span_width_reg(start_col, end_col),
+ span_alphabetic_width_reg(start_col, end_col),
+ span_left_numeric_width_reg(start_col, end_col),
+ span_right_numeric_width_reg(start_col, end_col));
+}
+
+void compute_span_width(int start_col, int end_col)
+{
+ printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
+ ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n",
+ span_width_reg(start_col, end_col),
+ span_left_numeric_width_reg(start_col, end_col),
+ span_right_numeric_width_reg(start_col, end_col),
+ span_alphabetic_width_reg(start_col, end_col));
+
+}
+
+// Increase the widths of columns so that the width of any spanning entry
+// is no greater than the sum of the widths of the columns that it spans.
+// Ensure that the widths of columns remain equal.
+
+void table::divide_span(int start_col, int end_col)
+{
+ assert(end_col > start_col);
+ printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]",
+ span_width_reg(start_col, end_col),
+ span_width_reg(start_col, start_col));
+ int i;
+ for (i = start_col + 1; i <= end_col; i++) {
+ // The column separation may shrink with the expand option.
+ if (!(flags & EXPAND))
+ printfs("+%1n", as_string(column_separation[i - 1]));
+ printfs("+\\n[%1]", span_width_reg(i, i));
+ }
+ prints(")\n");
+ printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
+ as_string(end_col - start_col + 1));
+ prints(".if \\n[" NEEDED_REG "] \\{");
+ for (i = start_col; i <= end_col; i++)
+ printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
+ span_width_reg(i, i));
+ int equal_flag = 0;
+ for (i = start_col; i <= end_col && !equal_flag; i++)
+ if (equal[i])
+ equal_flag = 1;
+ if (equal_flag) {
+ for (i = 0; i < ncolumns; i++)
+ if (i < start_col || i > end_col)
+ printfs(".nr %1 +\\n[" NEEDED_REG "]\n",
+ span_width_reg(i, i));
+ }
+ prints(".\\}\n");
+}
+
+
+void table::sum_columns(int start_col, int end_col)
+{
+ assert(end_col > start_col);
+ printfs(".nr %1 \\n[%2]",
+ span_width_reg(start_col, end_col),
+ span_width_reg(start_col, start_col));
+ for (int i = start_col + 1; i <= end_col; i++)
+ printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
+ as_string(column_separation[i - 1]),
+ span_width_reg(i, i));
+ prints('\n');
+}
+
+horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
+: next(p), start_col(sc), end_col(ec)
+{
+}
+
+void table::build_span_list()
+{
+ span_list = 0;
+ table_entry *p = entry_list;
+ while (p) {
+ if (p->end_col != p->start_col) {
+ horizontal_span *q;
+ for (q = span_list; q; q = q->next)
+ if (q->start_col == p->start_col
+ && q->end_col == p->end_col)
+ break;
+ if (!q)
+ span_list = new horizontal_span(p->start_col, p->end_col, span_list);
+ }
+ p = p->next;
+ }
+ // Now sort span_list primarily by order of end_row, and secondarily
+ // by reverse order of start_row. This ensures that if we divide
+ // spans using the order in span_list, we will get reasonable results.
+ horizontal_span *unsorted = span_list;
+ span_list = 0;
+ while (unsorted) {
+ horizontal_span **pp;
+ for (pp = &span_list; *pp; pp = &(*pp)->next)
+ if (unsorted->end_col < (*pp)->end_col
+ || (unsorted->end_col == (*pp)->end_col
+ && (unsorted->start_col > (*pp)->start_col)))
+ break;
+ horizontal_span *tem = unsorted->next;
+ unsorted->next = *pp;
+ *pp = unsorted;
+ unsorted = tem;
+ }
+}
+
+
+void table::compute_separation_factor()
+{
+ if (flags & (ALLBOX|BOX|DOUBLEBOX))
+ left_separation = right_separation = 1;
+ else {
+ for (int i = 0; i < nrows; i++) {
+ if (vline[i][0] > 0)
+ left_separation = 1;
+ if (vline[i][ncolumns] > 0)
+ right_separation = 1;
+ }
+ }
+ if (flags & EXPAND) {
+ int total_sep = left_separation + right_separation;
+ int i;
+ for (i = 0; i < ncolumns - 1; i++)
+ total_sep += column_separation[i];
+ if (total_sep != 0) {
+ // Don't let the separation factor be negative.
+ prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
+ for (i = 0; i < ncolumns; i++)
+ printfs("-\\n[%1]", span_width_reg(i, i));
+ printfs("/%1>?0\n", as_string(total_sep));
+ }
+ }
+}
+
+void table::compute_column_positions()
+{
+ printfs(".nr %1 0\n", column_divide_reg(0));
+ printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
+ column_start_reg(0),
+ as_string(left_separation));
+ int i;
+ for (i = 1;; i++) {
+ printfs(".nr %1 \\n[%2]+\\n[%3]\n",
+ column_end_reg(i-1),
+ column_start_reg(i-1),
+ span_width_reg(i-1, i-1));
+ if (i >= ncolumns)
+ break;
+ printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
+ column_start_reg(i),
+ column_end_reg(i-1),
+ as_string(column_separation[i-1]));
+ printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
+ column_divide_reg(i),
+ column_end_reg(i-1),
+ column_start_reg(i));
+ }
+ printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
+ column_divide_reg(ncolumns),
+ column_end_reg(i-1),
+ as_string(right_separation));
+ printfs(".nr TW \\n[%1]\n",
+ column_divide_reg(ncolumns));
+ if (flags & DOUBLEBOX) {
+ printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
+ printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
+ }
+}
+
+void table::make_columns_equal()
+{
+ int first = -1; // index of first equal column
+ int i;
+ for (i = 0; i < ncolumns; i++)
+ if (equal[i]) {
+ if (first < 0) {
+ printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
+ first = i;
+ }
+ else
+ printfs(">?\\n[%1]", span_width_reg(i, i));
+ }
+ if (first >= 0) {
+ prints('\n');
+ for (i = first + 1; i < ncolumns; i++)
+ if (equal[i])
+ printfs(".nr %1 \\n[%2]\n",
+ span_width_reg(i, i),
+ span_width_reg(first, first));
+ }
+}
+
+void table::compute_widths()
+{
+ build_span_list();
+ int i;
+ horizontal_span *p;
+ prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
+ for (i = 0; i < ncolumns; i++) {
+ init_span_reg(i, i);
+ if (!minimum_width[i].empty())
+ printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
+ }
+ for (p = span_list; p; p = p->next)
+ init_span_reg(p->start_col, p->end_col);
+ table_entry *q;
+ for (q = entry_list; q; q = q->next)
+ if (!q->mod->zero_width)
+ q->do_width();
+ for (i = 0; i < ncolumns; i++)
+ compute_span_width(i, i);
+ for (p = span_list; p; p = p->next)
+ compute_span_width(p->start_col, p->end_col);
+ make_columns_equal();
+ // Note that divide_span keeps equal width columns equal.
+ for (p = span_list; p; p = p->next)
+ divide_span(p->start_col, p->end_col);
+ for (p = span_list; p; p = p->next)
+ sum_columns(p->start_col, p->end_col);
+ int had_spanning_block = 0;
+ int had_equal_block = 0;
+ for (q = entry_list; q; q = q->next)
+ if (q->divert(ncolumns, minimum_width,
+ (flags & EXPAND) ? column_separation : 0)) {
+ if (q->end_col > q->start_col)
+ had_spanning_block = 1;
+ for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
+ if (equal[i])
+ had_equal_block = 1;
+ }
+ if (had_equal_block)
+ make_columns_equal();
+ if (had_spanning_block)
+ for (p = span_list; p; p = p->next)
+ divide_span(p->start_col, p->end_col);
+ compute_separation_factor();
+ for (p = span_list; p; p = p->next)
+ sum_columns(p->start_col, p->end_col);
+ compute_column_positions();
+}
+
+void table::print_single_hline(int r)
+{
+ prints(".vs " LINE_SEP ">?\\n[.V]u\n"
+ ".ls 1\n"
+ "\\v'" BODY_DEPTH "'"
+ "\\s[\\n[" LINESIZE_REG "]]");
+ if (r > nrows - 1)
+ prints("\\D'l |\\n[TW]u 0'");
+ else {
+ int start_col = 0;
+ for (;;) {
+ while (start_col < ncolumns
+ && entry[r][start_col] != 0
+ && entry[r][start_col]->start_row != r)
+ start_col++;
+ int end_col;
+ for (end_col = start_col;
+ end_col < ncolumns
+ && (entry[r][end_col] == 0
+ || entry[r][end_col]->start_row == r);
+ end_col++)
+ ;
+ if (end_col <= start_col)
+ break;
+ printfs("\\h'|\\n[%1]u",
+ column_divide_reg(start_col));
+ if ((r > 0 && vline[r-1][start_col] == 2)
+ || (r < nrows && vline[r][start_col] == 2))
+ prints("-" HALF_DOUBLE_LINE_SEP);
+ prints("'");
+ printfs("\\D'l |\\n[%1]u",
+ column_divide_reg(end_col));
+ if ((r > 0 && vline[r-1][end_col] == 2)
+ || (r < nrows && vline[r][end_col] == 2))
+ prints("+" HALF_DOUBLE_LINE_SEP);
+ prints(" 0'");
+ start_col = end_col;
+ }
+ }
+ prints("\\s0\n");
+ prints(".ls\n"
+ ".vs\n");
+}
+
+void table::print_double_hline(int r)
+{
+ prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
+ ">?\\n[.V]u\n"
+ ".ls 1\n"
+ "\\v'" BODY_DEPTH "'"
+ "\\s[\\n[" LINESIZE_REG "]]");
+ if (r > nrows - 1)
+ prints("\\v'-" DOUBLE_LINE_SEP "'"
+ "\\D'l |\\n[TW]u 0'"
+ "\\v'" DOUBLE_LINE_SEP "'"
+ "\\h'|0'"
+ "\\D'l |\\n[TW]u 0'");
+ else {
+ int start_col = 0;
+ for (;;) {
+ while (start_col < ncolumns
+ && entry[r][start_col] != 0
+ && entry[r][start_col]->start_row != r)
+ start_col++;
+ int end_col;
+ for (end_col = start_col;
+ end_col < ncolumns
+ && (entry[r][end_col] == 0
+ || entry[r][end_col]->start_row == r);
+ end_col++)
+ ;
+ if (end_col <= start_col)
+ break;
+ const char *left_adjust = 0;
+ if ((r > 0 && vline[r-1][start_col] == 2)
+ || (r < nrows && vline[r][start_col] == 2))
+ left_adjust = "-" HALF_DOUBLE_LINE_SEP;
+ const char *right_adjust = 0;
+ if ((r > 0 && vline[r-1][end_col] == 2)
+ || (r < nrows && vline[r][end_col] == 2))
+ right_adjust = "+" HALF_DOUBLE_LINE_SEP;
+ printfs("\\v'-" DOUBLE_LINE_SEP "'"
+ "\\h'|\\n[%1]u",
+ column_divide_reg(start_col));
+ if (left_adjust)
+ prints(left_adjust);
+ prints("'");
+ printfs("\\D'l |\\n[%1]u",
+ column_divide_reg(end_col));
+ if (right_adjust)
+ prints(right_adjust);
+ prints(" 0'");
+ printfs("\\v'" DOUBLE_LINE_SEP "'"
+ "\\h'|\\n[%1]u",
+ column_divide_reg(start_col));
+ if (left_adjust)
+ prints(left_adjust);
+ prints("'");
+ printfs("\\D'l |\\n[%1]u",
+ column_divide_reg(end_col));
+ if (right_adjust)
+ prints(right_adjust);
+ prints(" 0'");
+ start_col = end_col;
+ }
+ }
+ prints("\\s0\n"
+ ".ls\n"
+ ".vs\n");
+}
+
+void table::compute_vrule_top_adjust(int start_row, int col, string &result)
+{
+ if (row_is_all_lines[start_row] && start_row < nrows - 1) {
+ if (row_is_all_lines[start_row] == 2)
+ result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
+ else
+ result = LINE_SEP ">?\\n[.V]u";
+ start_row++;
+ }
+ else {
+ result = "";
+ if (start_row == 0)
+ return;
+ for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
+ if (p->row == start_row
+ && (p->is_single_line() || p->is_double_line()))
+ return;
+ }
+ int left = 0;
+ if (col > 0) {
+ table_entry *e = entry[start_row-1][col-1];
+ if (e && e->start_row == e->end_row) {
+ if (e->to_double_line_entry() != 0)
+ left = 2;
+ else if (e->to_single_line_entry() != 0)
+ left = 1;
+ }
+ }
+ int right = 0;
+ if (col < ncolumns) {
+ table_entry *e = entry[start_row-1][col];
+ if (e && e->start_row == e->end_row) {
+ if (e->to_double_line_entry() != 0)
+ right = 2;
+ else if (e->to_single_line_entry() != 0)
+ right = 1;
+ }
+ }
+ if (row_is_all_lines[start_row-1] == 0) {
+ if (left > 0 || right > 0) {
+ result += "-" BODY_DEPTH "-" BAR_HEIGHT;
+ if ((left == 2 && right != 2) || (right == 2 && left != 2))
+ result += "-" HALF_DOUBLE_LINE_SEP;
+ else if (left == 2 && right == 2)
+ result += "+" HALF_DOUBLE_LINE_SEP;
+ }
+ }
+ else if (row_is_all_lines[start_row-1] == 2) {
+ if ((left == 2 && right != 2) || (right == 2 && left != 2))
+ result += "-" DOUBLE_LINE_SEP;
+ else if (left == 1 || right == 1)
+ result += "-" HALF_DOUBLE_LINE_SEP;
+ }
+}
+
+void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
+{
+ if (row_is_all_lines[end_row] && end_row > 0) {
+ end_row--;
+ result = "";
+ }
+ else {
+ stuff *p;
+ for (p = stuff_list; p && p->row < end_row + 1; p = p->next)
+ ;
+ if (p && p->row == end_row + 1 && p->is_double_line()) {
+ result = "-" DOUBLE_LINE_SEP;
+ return;
+ }
+ if ((p != 0 && p->row == end_row + 1)
+ || end_row == nrows - 1) {
+ result = "";
+ return;
+ }
+ if (row_is_all_lines[end_row+1] == 1)
+ result = LINE_SEP;
+ else if (row_is_all_lines[end_row+1] == 2)
+ result = LINE_SEP "+" DOUBLE_LINE_SEP;
+ else
+ result = "";
+ }
+ int left = 0;
+ if (col > 0) {
+ table_entry *e = entry[end_row+1][col-1];
+ if (e && e->start_row == e->end_row) {
+ if (e->to_double_line_entry() != 0)
+ left = 2;
+ else if (e->to_single_line_entry() != 0)
+ left = 1;
+ }
+ }
+ int right = 0;
+ if (col < ncolumns) {
+ table_entry *e = entry[end_row+1][col];
+ if (e && e->start_row == e->end_row) {
+ if (e->to_double_line_entry() != 0)
+ right = 2;
+ else if (e->to_single_line_entry() != 0)
+ right = 1;
+ }
+ }
+ if (row_is_all_lines[end_row+1] == 0) {
+ if (left > 0 || right > 0) {
+ result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
+ if ((left == 2 && right != 2) || (right == 2 && left != 2))
+ result += "+" HALF_DOUBLE_LINE_SEP;
+ else if (left == 2 && right == 2)
+ result += "-" HALF_DOUBLE_LINE_SEP;
+ }
+ }
+ else if (row_is_all_lines[end_row+1] == 2) {
+ if (left == 2 && right == 2)
+ result += "-" DOUBLE_LINE_SEP;
+ else if (left != 2 && right != 2 && (left == 1 || right == 1))
+ result += "-" HALF_DOUBLE_LINE_SEP;
+ }
+}
+
+void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
+{
+ vrule_list = new vertical_rule(start_row, end_row, col, is_double,
+ vrule_list);
+ compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
+ compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
+}
+
+void table::build_vrule_list()
+{
+ int col;
+ if (flags & ALLBOX) {
+ for (col = 1; col < ncolumns; col++) {
+ int start_row = 0;
+ for (;;) {
+ while (start_row < nrows && vline_spanned(start_row, col))
+ start_row++;
+ if (start_row >= nrows)
+ break;
+ int end_row = start_row;
+ while (end_row < nrows && !vline_spanned(end_row, col))
+ end_row++;
+ end_row--;
+ add_vertical_rule(start_row, end_row, col, 0);
+ start_row = end_row + 1;
+ }
+ }
+ }
+ if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
+ add_vertical_rule(0, nrows - 1, 0, 0);
+ add_vertical_rule(0, nrows - 1, ncolumns, 0);
+ }
+ for (int end_row = 0; end_row < nrows; end_row++)
+ for (col = 0; col < ncolumns+1; col++)
+ if (vline[end_row][col] > 0
+ && !vline_spanned(end_row, col)
+ && (end_row == nrows - 1
+ || vline[end_row+1][col] != vline[end_row][col]
+ || vline_spanned(end_row+1, col))) {
+ int start_row;
+ for (start_row = end_row - 1;
+ start_row >= 0
+ && vline[start_row][col] == vline[end_row][col]
+ && !vline_spanned(start_row, col);
+ start_row--)
+ ;
+ start_row++;
+ add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
+ }
+ for (vertical_rule *p = vrule_list; p; p = p->next)
+ if (p->is_double)
+ for (int r = p->start_row; r <= p->end_row; r++) {
+ if (p->col > 0 && entry[r][p->col-1] != 0
+ && entry[r][p->col-1]->end_col == p->col-1) {
+ int is_corner = r == p->start_row || r == p->end_row;
+ entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
+ }
+ if (p->col < ncolumns && entry[r][p->col] != 0
+ && entry[r][p->col]->start_col == p->col) {
+ int is_corner = r == p->start_row || r == p->end_row;
+ entry[r][p->col]->note_double_vrule_on_left(is_corner);
+ }
+ }
+}
+
+void table::define_bottom_macro()
+{
+ prints(".eo\n"
+ ".de T#\n"
+ ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
+ "." REPEATED_VPT_MACRO " 0\n"
+ ".mk " SAVED_VERTICAL_POS_REG "\n");
+ if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
+ prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
+ print_single_hline(0);
+ prints(".\\}\n");
+ }
+ prints(".ls 1\n");
+ for (vertical_rule *p = vrule_list; p; p = p->next)
+ p->contribute_to_bottom_macro(this);
+ if (flags & DOUBLEBOX)
+ prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
+ "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l \\n[TW]u 0'\\s0\n"
+ ".vs\n"
+ ".\\}\n"
+ ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
+ ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
+ ".sp -1\n"
+ "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
+ ".sp -1\n"
+ "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
+ "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
+ prints(".ls\n");
+ prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
+ ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
+ "." REPEATED_VPT_MACRO " 1\n"
+ ".\\}\n"
+ "..\n"
+ ".ec\n");
+}
+
+
+// is the vertical line before column c in row r horizontally spanned?
+
+int table::vline_spanned(int r, int c)
+{
+ assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
+ return (c != 0 && c != ncolumns && entry[r][c] != 0
+ && entry[r][c]->start_col != c
+ // horizontally spanning lines don't count
+ && entry[r][c]->to_double_line_entry() == 0
+ && entry[r][c]->to_single_line_entry() == 0);
+}
+
+int table::row_begins_section(int r)
+{
+ assert(r >= 0 && r < nrows);
+ for (int i = 0; i < ncolumns; i++)
+ if (entry[r][i] && entry[r][i]->start_row != r)
+ return 0;
+ return 1;
+}
+
+int table::row_ends_section(int r)
+{
+ assert(r >= 0 && r < nrows);
+ for (int i = 0; i < ncolumns; i++)
+ if (entry[r][i] && entry[r][i]->end_row != r)
+ return 0;
+ return 1;
+}
+
+void table::do_row(int r)
+{
+ if (!(flags & NOKEEP) && row_begins_section(r))
+ prints("." KEEP_MACRO_NAME "\n");
+ int had_line = 0;
+ stuff *p;
+ for (p = stuff_list; p && p->row < r; p = p->next)
+ ;
+ for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
+ if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
+ had_line = 1;
+ break;
+ }
+ if (!had_line && !row_is_all_lines[r])
+ printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
+ had_line = 0;
+ for (; p && p->row == r; p = p->next)
+ if (!p->printed) {
+ p->print(this);
+ if (!had_line && (p->is_single_line() || p->is_double_line())) {
+ printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
+ had_line = 1;
+ }
+ }
+ // Change the row *after* printing the stuff list (which might contain .TH).
+ printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
+ as_string(r));
+ if (!had_line && row_is_all_lines[r])
+ printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
+ // we might have had a .TH, for example, since we last tried
+ if (!(flags & NOKEEP) && row_begins_section(r))
+ prints("." KEEP_MACRO_NAME "\n");
+ printfs(".mk %1\n", row_start_reg(r));
+ prints(".mk " BOTTOM_REG "\n"
+ "." REPEATED_VPT_MACRO " 0\n");
+ int c;
+ int row_is_blank = 1;
+ int first_start_row = r;
+ for (c = 0; c < ncolumns; c++) {
+ table_entry *e = entry[r][c];
+ if (e) {
+ if (e->end_row == r) {
+ e->do_depth();
+ if (e->start_row < first_start_row)
+ first_start_row = e->start_row;
+ row_is_blank = 0;
+ }
+ c = e->end_col;
+ }
+ }
+ if (row_is_blank)
+ prints(".nr " BOTTOM_REG " +1v\n");
+ if (row_is_all_lines[r]) {
+ prints(".vs " LINE_SEP);
+ if (row_is_all_lines[r] == 2)
+ prints("+" DOUBLE_LINE_SEP);
+ prints(">?\\n[.V]u\n.ls 1\n");
+ prints("\\&");
+ prints("\\v'" BODY_DEPTH);
+ if (row_is_all_lines[r] == 2)
+ prints("-" HALF_DOUBLE_LINE_SEP);
+ prints("'");
+ for (c = 0; c < ncolumns; c++) {
+ table_entry *e = entry[r][c];
+ if (e) {
+ if (e->end_row == e->start_row)
+ e->to_simple_entry()->simple_print(1);
+ c = e->end_col;
+ }
+ }
+ prints("\n");
+ prints(".ls\n"
+ ".vs\n");
+ prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
+ printfs(".sp |\\n[%1]u\n", row_start_reg(r));
+ }
+ for (int i = row_is_all_lines[r] ? r - 1 : r;
+ i >= first_start_row;
+ i--) {
+ simple_entry *first = 0;
+ for (c = 0; c < ncolumns; c++) {
+ table_entry *e = entry[r][c];
+ if (e) {
+ if (e->end_row == r && e->start_row == i) {
+ simple_entry *simple = e->to_simple_entry();
+ if (simple) {
+ if (!first) {
+ prints(".ta");
+ first = simple;
+ }
+ simple->add_tab();
+ }
+ }
+ c = e->end_col;
+ }
+ }
+ if (first) {
+ prints('\n');
+ first->position_vertically();
+ first->set_location();
+ prints("\\&");
+ first->simple_print(0);
+ for (c = first->end_col + 1; c < ncolumns; c++) {
+ table_entry *e = entry[r][c];
+ if (e) {
+ if (e->end_row == r && e->start_row == i) {
+ simple_entry *simple = e->to_simple_entry();
+ if (simple)
+ simple->simple_print(0);
+ }
+ c = e->end_col;
+ }
+ }
+ prints('\n');
+ prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
+ printfs(".sp |\\n[%1]u\n", row_start_reg(r));
+ }
+ }
+ for (c = 0; c < ncolumns; c++) {
+ table_entry *e = entry[r][c];
+ if (e) {
+ if (e->end_row == r && e->to_simple_entry() == 0) {
+ e->position_vertically();
+ e->print();
+ prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
+ printfs(".sp |\\n[%1]u\n", row_start_reg(r));
+ }
+ c = e->end_col;
+ }
+ }
+ prints("." REPEATED_VPT_MACRO " 1\n"
+ ".sp |\\n[" BOTTOM_REG "]u\n"
+ "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
+ if (r != nrows - 1 && (flags & ALLBOX)) {
+ print_single_hline(r + 1);
+ prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
+ }
+ if (r != nrows - 1) {
+ if (p && p->row == r + 1
+ && (p->is_single_line() || p->is_double_line())) {
+ p->print(this);
+ prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
+ " 0\n");
+ }
+ int printed_one = 0;
+ for (vertical_rule *vr = vrule_list; vr; vr = vr->next)
+ if (vr->end_row == r) {
+ if (!printed_one) {
+ prints("." REPEATED_VPT_MACRO " 0\n");
+ printed_one = 1;
+ }
+ vr->print();
+ }
+ if (printed_one)
+ prints("." REPEATED_VPT_MACRO " 1\n");
+ if (!(flags & NOKEEP) && row_ends_section(r))
+ prints("." RELEASE_MACRO_NAME "\n");
+ }
+}
+
+void table::do_top()
+{
+ prints(".fc \002\003\n");
+ if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
+ prints("." TABLE_KEEP_MACRO_NAME "\n");
+ if (flags & DOUBLEBOX) {
+ prints(".ls 1\n"
+ ".vs " LINE_SEP ">?\\n[.V]u\n"
+ "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
+ ".vs\n"
+ "." REPEATED_MARK_MACRO " " TOP_REG "\n"
+ ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
+ printfs("\\v'" BODY_DEPTH "'"
+ "\\s[\\n[" LINESIZE_REG "]]"
+ "\\h'\\n[%1]u'"
+ "\\D'l |\\n[%2]u 0'"
+ "\\s0"
+ "\n",
+ column_divide_reg(0),
+ column_divide_reg(ncolumns));
+ prints(".ls\n"
+ ".vs\n");
+ }
+ else if (flags & (ALLBOX|BOX)) {
+ print_single_hline(0);
+ }
+ //printfs(".mk %1\n", row_top_reg(0));
+}
+
+void table::do_bottom()
+{
+ // print stuff after last row
+ for (stuff *p = stuff_list; p; p = p->next)
+ if (p->row > nrows - 1)
+ p->print(this);
+ if (!(flags & NOKEEP))
+ prints("." RELEASE_MACRO_NAME "\n");
+ printfs(".mk %1\n", row_top_reg(nrows));
+ prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
+ ".nr T. 1\n"
+ ".T#\n");
+ if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
+ prints("." TABLE_RELEASE_MACRO_NAME "\n");
+ if (flags & DOUBLEBOX)
+ prints(".sp " DOUBLE_LINE_SEP "\n");
+ prints("." RESET_MACRO_NAME "\n"
+ ".fc\n"
+ ".cp \\n(" COMPATIBLE_REG "\n");
+}
+
+int table::get_nrows()
+{
+ return nrows;
+}
+
+const char *last_filename = 0;
+
+void set_troff_location(const char *fn, int ln)
+{
+ if (!location_force_filename && last_filename != 0
+ && strcmp(fn, last_filename) == 0)
+ printfs(".lf %1\n", as_string(ln));
+ else {
+ printfs(".lf %1 %2\n", as_string(ln), fn);
+ last_filename = fn;
+ location_force_filename = 0;
+ }
+}
+
+void printfs(const char *s, const string &arg1, const string &arg2,
+ const string &arg3, const string &arg4, const string &arg5)
+{
+ if (s) {
+ char c;
+ while ((c = *s++) != '\0') {
+ if (c == '%') {
+ switch (*s++) {
+ case '1':
+ prints(arg1);
+ break;
+ case '2':
+ prints(arg2);
+ break;
+ case '3':
+ prints(arg3);
+ break;
+ case '4':
+ prints(arg4);
+ break;
+ case '5':
+ prints(arg5);
+ break;
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ case '%':
+ prints('%');
+ break;
+ default:
+ assert(0);
+ }
+ }
+ else
+ prints(c);
+ }
+ }
+}
+
diff --git a/contrib/groff/src/preproc/tbl/table.h b/contrib/groff/src/preproc/tbl/table.h
new file mode 100644
index 000000000000..ca55b8026c22
--- /dev/null
+++ b/contrib/groff/src/preproc/tbl/table.h
@@ -0,0 +1,152 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "cset.h"
+#include "cmap.h"
+#include "stringclass.h"
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+
+struct inc_number {
+ short inc;
+ short val;
+};
+
+struct entry_modifier {
+ inc_number point_size;
+ inc_number vertical_spacing;
+ string font;
+ enum { CENTER, TOP, BOTTOM } vertical_alignment;
+ char zero_width;
+ char stagger;
+
+ entry_modifier();
+ ~entry_modifier();
+};
+
+enum format_type {
+ FORMAT_LEFT,
+ FORMAT_CENTER,
+ FORMAT_RIGHT,
+ FORMAT_NUMERIC,
+ FORMAT_ALPHABETIC,
+ FORMAT_SPAN,
+ FORMAT_VSPAN,
+ FORMAT_HLINE,
+ FORMAT_DOUBLE_HLINE
+};
+
+struct entry_format : public entry_modifier {
+ format_type type;
+
+ entry_format(format_type);
+ entry_format();
+ void debug_print() const;
+};
+
+struct table_entry;
+struct horizontal_span;
+struct stuff;
+struct vertical_rule;
+
+class table {
+ unsigned flags;
+ int nrows;
+ int ncolumns;
+ int linesize;
+ char delim[2];
+ char decimal_point_char;
+ vertical_rule *vrule_list;
+ stuff *stuff_list;
+ horizontal_span *span_list;
+ table_entry *entry_list;
+ table_entry **entry_list_tailp;
+ table_entry ***entry;
+ char **vline;
+ char *row_is_all_lines;
+ string *minimum_width;
+ int *column_separation;
+ char *equal;
+ int left_separation;
+ int right_separation;
+ int allocated_rows;
+ void build_span_list();
+ void do_hspan(int r, int c);
+ void do_vspan(int r, int c);
+ void allocate(int r);
+ void compute_widths();
+ void divide_span(int, int);
+ void sum_columns(int, int);
+ void compute_separation_factor();
+ void compute_column_positions();
+ void do_row(int);
+ void init_output();
+ void add_stuff(stuff *);
+ void do_top();
+ void do_bottom();
+ void do_vertical_rules();
+ void build_vrule_list();
+ void add_vertical_rule(int, int, int, int);
+ void define_bottom_macro();
+ int vline_spanned(int r, int c);
+ int row_begins_section(int);
+ int row_ends_section(int);
+ void make_columns_equal();
+ void compute_vrule_top_adjust(int, int, string &);
+ void compute_vrule_bot_adjust(int, int, string &);
+ void determine_row_type();
+public:
+ /* used by flags */
+ enum {
+ CENTER = 01,
+ EXPAND = 02,
+ BOX = 04,
+ ALLBOX = 010,
+ DOUBLEBOX = 020,
+ NOKEEP = 040
+ };
+ table(int nc, unsigned flags, int linesize, char decimal_point_char);
+ ~table();
+
+ void add_text_line(int r, const string &, const char *, int);
+ void add_single_hline(int r);
+ void add_double_hline(int r);
+ void add_entry(int r, int c, const string &, const entry_format *,
+ const char *, int lineno);
+ void add_vlines(int r, const char *);
+ void check();
+ void print();
+ void set_minimum_width(int c, const string &w);
+ void set_column_separation(int c, int n);
+ void set_equal_column(int c);
+ void set_delim(char c1, char c2);
+ void print_single_hline(int r);
+ void print_double_hline(int r);
+ int get_nrows();
+};
+
+void set_troff_location(const char *, int);
diff --git a/contrib/groff/src/preproc/tbl/tbl.man b/contrib/groff/src/preproc/tbl/tbl.man
new file mode 100644
index 000000000000..6016ddf35f8f
--- /dev/null
+++ b/contrib/groff/src/preproc/tbl/tbl.man
@@ -0,0 +1,178 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH @G@TBL @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@tbl \- format tables for troff
+.SH SYNOPSIS
+.B @g@tbl
+[
+.B \-Cv
+]
+[
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+This manual page describes the GNU version of
+.BR tbl ,
+which is part of the groff document formatting system.
+.B tbl
+compiles descriptions of tables embedded within
+.B troff
+input files into commands that are understood by
+.BR troff .
+Normally, it should be invoked using the
+.B \-t
+option of
+.B groff.
+It is highly compatible with Unix
+.BR tbl .
+The output generated by GNU
+.B tbl
+cannot be processed with Unix
+.BR troff ;
+it must be processed with GNU
+.BR troff .
+If no files are given on the command line, the standard input
+will be read.
+A filename of
+.B \-
+will cause the standard input to be read.
+.SH OPTIONS
+.TP
+.B \-C
+Recognize
+.B .TS
+and
+.B .TE
+even when followed by a character other than space or newline.
+.TP
+.B \-v
+Print the version number.
+.SH USAGE
+Only the differences between GNU
+.B tbl
+and Unix
+.B tbl
+are described here.
+.LP
+Normally
+.B tbl
+attempts to prevent undesirable breaks in the table by using diversions.
+This can sometimes interact badly with macro packages' own use of diversions,
+when footnotes, for example, are used.
+The
+.B nokeep
+option tells
+.B tbl
+not to try and prevent breaks in this way.
+.LP
+The
+.B decimalpoint
+option specifies the character to be recognized as the decimal
+point character in place of the default period.
+It takes an argument in parentheses, which must be a single
+character, as for the
+.B tab
+option.
+.LP
+The
+.B f
+format modifier can be followed by an arbitrary length
+font name in parentheses.
+.LP
+There is a
+.B d
+format modifier which means that a vertically spanning entry
+should be aligned at the bottom of its range.
+.LP
+There is no limit on the number of columns in a table, nor any limit
+on the number of text blocks.
+All the lines of a table are considered in deciding column
+widths, not just the first 200.
+Table continuation
+.RB ( .T& )
+lines are not restricted to the first 200 lines.
+.LP
+Numeric and alphabetic items may appear in the same column.
+.LP
+Numeric and alphabetic items may span horizontally.
+.LP
+.B tbl
+uses register, string, macro and diversion names beginning with
+.BR 3 .
+When using
+.B tbl
+you should avoid using any names beginning with a
+.BR 3 .
+.SH BUGS
+You should use
+.BR .TS\ H / .TH
+in conjunction with a supporting macro package for
+.I all
+multi-page boxed tables.
+If there is no header that you wish to appear at the top of each page
+of the table, place the
+.B .TH
+line immediately after the format section.
+Do not enclose a multi-page table within keep/release macros,
+or divert it in any other way.
+.LP
+A text block within a table must be able to fit on one page.
+.LP
+The
+.B bp
+request cannot be used to force a page-break in a multi-page table.
+Instead, define
+.B BP
+as follows
+.IP
+.B .de BP
+.br
+.B .ie '\e\en(.z'' .bp \e\e$1
+.br
+.B .el \e!.BP \e\e$1
+.br
+.B ..
+.br
+.LP
+and use
+.B BP
+instead of
+.BR bp .
+.LP
+Using \ea directly in a table to get leaders will not work.
+This is correct behaviour: \ea is a
+.B uninterpreted
+leader.
+To get leaders use a real leader, either by using a control A or like
+this:
+.IP
+.nf
+.ft B
+\&.ds a \ea
+\&.TS
+tab(;);
+lw(1i) l.
+A\e*a;B
+\&.TE
+.ft
+.fi
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@)
diff --git a/contrib/groff/src/roff/groff/Makefile.sub b/contrib/groff/src/roff/groff/Makefile.sub
new file mode 100644
index 000000000000..42ae221c47fa
--- /dev/null
+++ b/contrib/groff/src/roff/groff/Makefile.sub
@@ -0,0 +1,8 @@
+PROG=groff
+MAN1=groff.n
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=groff.o pipeline.o
+CCSRCS=$(srcdir)/groff.cc
+CSRCS=$(srcdir)/pipeline.c
+HDRS=$(srcdir)/pipeline.h
diff --git a/contrib/groff/src/roff/groff/groff.cc b/contrib/groff/src/roff/groff/groff.cc
new file mode 100644
index 000000000000..aaca4e1fb93d
--- /dev/null
+++ b/contrib/groff/src/roff/groff/groff.cc
@@ -0,0 +1,731 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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. */
+
+// A front end for groff.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "lib.h"
+#include "assert.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "font.h"
+#include "device.h"
+#include "pipeline.h"
+#include "nonposix.h"
+#include "defs.h"
+
+#define GXDITVIEW "gxditview"
+
+// troff will be passed an argument of -rXREG=1 if the -X option is
+// specified
+#define XREG ".X"
+
+#ifdef NEED_DECLARATION_PUTENV
+extern "C" {
+ int putenv(const char *);
+}
+#endif /* NEED_DECLARATION_PUTENV */
+
+const int SOELIM_INDEX = 0;
+const int REFER_INDEX = SOELIM_INDEX + 1;
+const int GRAP_INDEX = REFER_INDEX + 1;
+const int PIC_INDEX = GRAP_INDEX + 1;
+const int TBL_INDEX = PIC_INDEX + 1;
+const int GRN_INDEX = TBL_INDEX + 1;
+const int EQN_INDEX = GRN_INDEX + 1;
+const int TROFF_INDEX = EQN_INDEX + 1;
+const int POST_INDEX = TROFF_INDEX + 1;
+const int SPOOL_INDEX = POST_INDEX + 1;
+
+const int NCOMMANDS = SPOOL_INDEX + 1;
+
+class possible_command {
+ char *name;
+ string args;
+ char **argv;
+
+ void build_argv();
+public:
+ possible_command();
+ ~possible_command();
+ void set_name(const char *);
+ void set_name(const char *, const char *);
+ const char *get_name();
+ void append_arg(const char *, const char * = 0);
+ void insert_arg(const char *);
+ void clear_args();
+ char **get_argv();
+ void print(int is_last, FILE *fp);
+};
+
+int lflag = 0;
+char *spooler = 0;
+char *postdriver = 0;
+char *predriver = 0;
+
+possible_command commands[NCOMMANDS];
+
+int run_commands(int no_pipe);
+void print_commands();
+void append_arg_to_string(const char *arg, string &str);
+void handle_unknown_desc_command(const char *command, const char *arg,
+ const char *filename, int lineno);
+const char *xbasename(const char *);
+
+void usage(FILE *stream);
+void help();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ assert(NCOMMANDS <= MAX_COMMANDS);
+ string Pargs, Largs, Fargs;
+ int vflag = 0;
+ int Vflag = 0;
+ int zflag = 0;
+ int iflag = 0;
+ int Xflag = 0;
+ int safer_flag = 1;
+ int opt;
+ const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
+ if (!command_prefix)
+ command_prefix = PROG_PREFIX;
+ commands[TROFF_INDEX].set_name(command_prefix, "troff");
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv,
+ "abCd:eEf:F:gGhiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
+ long_options, NULL))
+ != EOF) {
+ char buf[3];
+ buf[0] = '-';
+ buf[1] = opt;
+ buf[2] = '\0';
+ switch (opt) {
+ case 'i':
+ iflag = 1;
+ break;
+ case 'I':
+ commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
+ commands[SOELIM_INDEX].append_arg(buf, optarg);
+ break;
+ case 't':
+ commands[TBL_INDEX].set_name(command_prefix, "tbl");
+ break;
+ case 'p':
+ commands[PIC_INDEX].set_name(command_prefix, "pic");
+ break;
+ case 'g':
+ commands[GRN_INDEX].set_name(command_prefix, "grn");
+ break;
+ case 'G':
+ commands[GRAP_INDEX].set_name(command_prefix, "grap");
+ break;
+ case 'e':
+ commands[EQN_INDEX].set_name(command_prefix, "eqn");
+ break;
+ case 's':
+ commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
+ break;
+ case 'R':
+ commands[REFER_INDEX].set_name(command_prefix, "refer");
+ break;
+ case 'z':
+ case 'a':
+ commands[TROFF_INDEX].append_arg(buf);
+ // fall through
+ case 'Z':
+ zflag++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'V':
+ Vflag++;
+ break;
+ case 'v':
+ vflag = 1;
+ {
+ extern const char *Version_string;
+ printf("GNU groff version %s\n", Version_string);
+ printf("Copyright (C) 1989-2001 Free Software Foundation, Inc.\n"
+ "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
+ "You may redistribute copies of groff and its subprograms\n"
+ "under the terms of the GNU General Public License.\n"
+ "For more information about these matters, see the file named COPYING.\n");
+ printf("\ncalled subprograms:\n\n");
+ fflush(stdout);
+ }
+ commands[POST_INDEX].append_arg(buf);
+ // fall through
+ case 'C':
+ commands[SOELIM_INDEX].append_arg(buf);
+ commands[REFER_INDEX].append_arg(buf);
+ commands[PIC_INDEX].append_arg(buf);
+ commands[GRAP_INDEX].append_arg(buf);
+ commands[TBL_INDEX].append_arg(buf);
+ commands[GRN_INDEX].append_arg(buf);
+ commands[EQN_INDEX].append_arg(buf);
+ commands[TROFF_INDEX].append_arg(buf);
+ break;
+ case 'N':
+ commands[EQN_INDEX].append_arg(buf);
+ break;
+ case 'h':
+ help();
+ break;
+ case 'E':
+ case 'b':
+ commands[TROFF_INDEX].append_arg(buf);
+ break;
+ case 'S':
+ safer_flag = 1;
+ break;
+ case 'U':
+ safer_flag = 0;
+ break;
+ case 'T':
+ if (strcmp(optarg, "html") == 0) {
+ // force soelim to aid the html preprocessor
+ commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
+ }
+ if (strcmp(optarg, "Xps") == 0) {
+ warning("-TXps option is obsolete: use -X -Tps instead");
+ device = "ps";
+ Xflag++;
+ }
+ else
+ device = optarg;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ if (Fargs.length() > 0) {
+ Fargs += PATH_SEP[0];
+ Fargs += optarg;
+ }
+ else
+ Fargs = optarg;
+ break;
+ case 'f':
+ case 'o':
+ case 'm':
+ case 'r':
+ case 'd':
+ case 'n':
+ case 'w':
+ case 'W':
+ commands[TROFF_INDEX].append_arg(buf, optarg);
+ break;
+ case 'M':
+ commands[EQN_INDEX].append_arg(buf, optarg);
+ commands[GRAP_INDEX].append_arg(buf, optarg);
+ commands[GRN_INDEX].append_arg(buf, optarg);
+ commands[TROFF_INDEX].append_arg(buf, optarg);
+ break;
+ case 'P':
+ Pargs += optarg;
+ Pargs += '\0';
+ break;
+ case 'L':
+ append_arg_to_string(optarg, Largs);
+ break;
+ case 'X':
+ Xflag++;
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ if (safer_flag)
+ commands[PIC_INDEX].append_arg("-S");
+ else
+ commands[TROFF_INDEX].insert_arg("-U");
+ font::set_unknown_desc_command_handler(handle_unknown_desc_command);
+ if (!font::load_desc())
+ fatal("invalid device `%1'", device);
+ if (!postdriver)
+ fatal("no `postpro' command in DESC file for device `%1'", device);
+
+ if (predriver) {
+ commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
+ const char *p = Pargs.contents();
+ const char *end = p + Pargs.length();
+ while (p < end) {
+ // pass the device arguments to the predrivers as well
+ commands[TROFF_INDEX].insert_arg(p);
+ p = strchr(p, '\0') + 1;
+ }
+ commands[TROFF_INDEX].set_name(predriver);
+ }
+
+ const char *real_driver = 0;
+ if (Xflag) {
+ real_driver = postdriver;
+ postdriver = GXDITVIEW;
+ commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
+ }
+ if (postdriver)
+ commands[POST_INDEX].set_name(postdriver);
+ int gxditview_flag = postdriver && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
+ if (gxditview_flag && argc - optind == 1) {
+ commands[POST_INDEX].append_arg("-title");
+ commands[POST_INDEX].append_arg(argv[optind]);
+ commands[POST_INDEX].append_arg("-xrm");
+ commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
+ string filename_string("|");
+ append_arg_to_string(argv[0], filename_string);
+ append_arg_to_string("-Z", filename_string);
+ for (int i = 1; i < argc; i++)
+ append_arg_to_string(argv[i], filename_string);
+ filename_string += '\0';
+ commands[POST_INDEX].append_arg("-filename");
+ commands[POST_INDEX].append_arg(filename_string.contents());
+ }
+ if (gxditview_flag && Xflag) {
+ string print_string(real_driver);
+ if (spooler) {
+ print_string += " | ";
+ print_string += spooler;
+ print_string += Largs;
+ }
+ print_string += '\0';
+ commands[POST_INDEX].append_arg("-printCommand");
+ commands[POST_INDEX].append_arg(print_string.contents());
+ }
+ const char *p = Pargs.contents();
+ const char *end = p + Pargs.length();
+ while (p < end) {
+ commands[POST_INDEX].append_arg(p);
+ p = strchr(p, '\0') + 1;
+ }
+ if (gxditview_flag)
+ commands[POST_INDEX].append_arg("-");
+ if (lflag && !Xflag && spooler) {
+ commands[SPOOL_INDEX].set_name(BSHELL);
+ commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
+ Largs += '\0';
+ Largs = spooler + Largs;
+ commands[SPOOL_INDEX].append_arg(Largs.contents());
+ }
+ if (zflag) {
+ commands[POST_INDEX].set_name(0);
+ commands[SPOOL_INDEX].set_name(0);
+ }
+ commands[TROFF_INDEX].append_arg("-T", device);
+ // html renders equations as images via ps
+ if (strcmp(device, "html") == 0)
+ commands[EQN_INDEX].append_arg("-Tps:html");
+ else
+ commands[EQN_INDEX].append_arg("-T", device);
+
+ commands[GRN_INDEX].append_arg("-T", device);
+
+ int first_index;
+ for (first_index = 0; first_index < TROFF_INDEX; first_index++)
+ if (commands[first_index].get_name() != 0)
+ break;
+ if (optind < argc) {
+ if (argv[optind][0] == '-' && argv[optind][1] != '\0')
+ commands[first_index].append_arg("--");
+ for (int i = optind; i < argc; i++)
+ commands[first_index].append_arg(argv[i]);
+ if (iflag)
+ commands[first_index].append_arg("-");
+ }
+ if (Fargs.length() > 0) {
+ string e = "GROFF_FONT_PATH";
+ e += '=';
+ e += Fargs;
+ char *fontpath = getenv("GROFF_FONT_PATH");
+ if (fontpath && *fontpath) {
+ e += PATH_SEP[0];
+ e += fontpath;
+ }
+ e += '\0';
+ if (putenv(strsave(e.contents())))
+ fatal("putenv failed");
+ }
+ {
+ // we save the original path in GROFF_PATH__ and put it into the
+ // environment -- troff will pick it up later.
+ char *path = getenv("PATH");
+ string e = "GROFF_PATH__";
+ e += '=';
+ if (path && *path)
+ e += path;
+ e += '\0';
+ if (putenv(strsave(e.contents())))
+ fatal("putenv failed");
+ char *binpath = getenv("GROFF_BIN_PATH");
+ string f = "PATH";
+ f += '=';
+ if (binpath && *binpath)
+ f += binpath;
+ else
+ f += BINPATH;
+ if (path && *path) {
+ f += PATH_SEP[0];
+ f += path;
+ }
+ f += '\0';
+ if (putenv(strsave(f.contents())))
+ fatal("putenv failed");
+ }
+ if (Vflag) {
+ print_commands();
+ exit(0);
+ }
+ return run_commands(vflag);
+}
+
+const char *xbasename(const char *s)
+{
+ if (!s)
+ return 0;
+ // DIR_SEPS[] are possible directory separator characters, see nonposix.h
+ // We want the rightmost separator of all possible ones.
+ // Example: d:/foo\\bar.
+ const char *p = strrchr(s, DIR_SEPS[0]), *p1;
+ const char *sep = &DIR_SEPS[1];
+
+ while (*sep)
+ {
+ p1 = strrchr(s, *sep);
+ if (p1 && (!p || p1 > p))
+ p = p1;
+ sep++;
+ }
+ return p ? p + 1 : s;
+}
+
+void handle_unknown_desc_command(const char *command, const char *arg,
+ const char *filename, int lineno)
+{
+ if (strcmp(command, "print") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`print' command requires an argument");
+ else
+ spooler = strsave(arg);
+ }
+ if (strcmp(command, "prepro") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`prepro' command requires an argument");
+ else {
+ for (const char *p = arg; *p; p++)
+ if (csspace(*p)) {
+ error_with_file_and_line(filename, lineno,
+ "invalid `prepro' argument `%1'"
+ ": program name required", arg);
+ return;
+ }
+ predriver = strsave(arg);
+ }
+ }
+ if (strcmp(command, "postpro") == 0) {
+ if (arg == 0)
+ error_with_file_and_line(filename, lineno,
+ "`postpro' command requires an argument");
+ else {
+ for (const char *p = arg; *p; p++)
+ if (csspace(*p)) {
+ error_with_file_and_line(filename, lineno,
+ "invalid `postpro' argument `%1'"
+ ": program name required", arg);
+ return;
+ }
+ postdriver = strsave(arg);
+ }
+ }
+}
+
+void print_commands()
+{
+ int last;
+ for (last = SPOOL_INDEX; last >= 0; last--)
+ if (commands[last].get_name() != 0)
+ break;
+ for (int i = 0; i <= last; i++)
+ if (commands[i].get_name() != 0)
+ commands[i].print(i == last, stdout);
+}
+
+// Run the commands. Return the code with which to exit.
+
+int run_commands(int no_pipe)
+{
+ char **v[NCOMMANDS];
+ int j = 0;
+ for (int i = 0; i < NCOMMANDS; i++)
+ if (commands[i].get_name() != 0)
+ v[j++] = commands[i].get_argv();
+ return run_pipeline(j, v, no_pipe);
+}
+
+possible_command::possible_command()
+: name(0), argv(0)
+{
+}
+
+possible_command::~possible_command()
+{
+ a_delete name;
+ a_delete argv;
+}
+
+void possible_command::set_name(const char *s)
+{
+ a_delete name;
+ name = strsave(s);
+}
+
+void possible_command::set_name(const char *s1, const char *s2)
+{
+ a_delete name;
+ name = new char[strlen(s1) + strlen(s2) + 1];
+ strcpy(name, s1);
+ strcat(name, s2);
+}
+
+const char *possible_command::get_name()
+{
+ return name;
+}
+
+void possible_command::clear_args()
+{
+ args.clear();
+}
+
+void possible_command::append_arg(const char *s, const char *t)
+{
+ args += s;
+ if (t)
+ args += t;
+ args += '\0';
+}
+
+void possible_command::insert_arg(const char *s)
+{
+ string str(s);
+ str += '\0';
+ str += args;
+ args = str;
+}
+
+void possible_command::build_argv()
+{
+ if (argv)
+ return;
+ // Count the number of arguments.
+ int len = args.length();
+ int argc = 1;
+ char *p = 0;
+ if (len > 0) {
+ p = &args[0];
+ for (int i = 0; i < len; i++)
+ if (p[i] == '\0')
+ argc++;
+ }
+ // Build an argument vector.
+ argv = new char *[argc + 1];
+ argv[0] = name;
+ for (int i = 1; i < argc; i++) {
+ argv[i] = p;
+ p = strchr(p, '\0') + 1;
+ }
+ argv[argc] = 0;
+}
+
+void possible_command::print(int is_last, FILE *fp)
+{
+ build_argv();
+ if (IS_BSHELL(argv[0])
+ && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
+ && argv[2] != 0 && argv[3] == 0)
+ fputs(argv[2], fp);
+ else {
+ fputs(argv[0], fp);
+ string str;
+ for (int i = 1; argv[i] != 0; i++) {
+ str.clear();
+ append_arg_to_string(argv[i], str);
+ put_string(str, fp);
+ }
+ }
+ if (is_last)
+ putc('\n', fp);
+ else
+ fputs(" | ", fp);
+}
+
+void append_arg_to_string(const char *arg, string &str)
+{
+ str += ' ';
+ int needs_quoting = 0;
+ int contains_single_quote = 0;
+ const char*p;
+ for (p = arg; *p != '\0'; p++)
+ switch (*p) {
+ case ';':
+ case '&':
+ case '(':
+ case ')':
+ case '|':
+ case '^':
+ case '<':
+ case '>':
+ case '\n':
+ case ' ':
+ case '\t':
+ case '\\':
+ case '"':
+ case '$':
+ case '?':
+ case '*':
+ needs_quoting = 1;
+ break;
+ case '\'':
+ contains_single_quote = 1;
+ break;
+ }
+ if (contains_single_quote || arg[0] == '\0') {
+ str += '"';
+ for (p = arg; *p != '\0'; p++)
+ switch (*p) {
+ case '"':
+ case '\\':
+ case '$':
+ str += '\\';
+ // fall through
+ default:
+ str += *p;
+ break;
+ }
+ str += '"';
+ }
+ else if (needs_quoting) {
+ str += '\'';
+ str += arg;
+ str += '\'';
+ }
+ else
+ str += arg;
+}
+
+char **possible_command::get_argv()
+{
+ build_argv();
+ return argv;
+}
+
+void synopsis(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-abeghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
+" [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
+" [-Larg] [-Idir] [files...]\n",
+ program_name);
+}
+
+void help()
+{
+ synopsis(stdout);
+ fputs("\n"
+"-h\tprint this message\n"
+"-t\tpreprocess with tbl\n"
+"-p\tpreprocess with pic\n"
+"-e\tpreprocess with eqn\n"
+"-g\tpreprocess with grn\n"
+"-G\tpreprocess with grap\n"
+"-s\tpreprocess with soelim\n"
+"-R\tpreprocess with refer\n"
+"-Tdev\tuse device dev\n"
+"-X\tuse X11 previewer rather than usual postprocessor\n"
+"-mname\tread macros tmac.name\n"
+"-dcs\tdefine a string c as s\n"
+"-rcn\tdefine a number register c as n\n"
+"-nnum\tnumber first page n\n"
+"-olist\toutput only pages in list\n"
+"-ffam\tuse fam as the default font family\n"
+"-Fdir\tsearch dir for device directories\n"
+"-Mdir\tsearch dir for macro files\n"
+"-v\tprint version number\n"
+"-z\tsuppress formatted output\n"
+"-Z\tdon't postprocess\n"
+"-a\tproduce ASCII description of output\n"
+"-i\tread standard input after named input files\n"
+"-wname\tenable warning name\n"
+"-Wname\tinhibit warning name\n"
+"-E\tinhibit all errors\n"
+"-b\tprint backtraces with errors or warnings\n"
+"-l\tspool the output\n"
+"-C\tenable compatibility mode\n"
+"-V\tprint commands on stdout instead of running them\n"
+"-Parg\tpass arg to the postprocessor\n"
+"-Larg\tpass arg to the spooler\n"
+"-N\tdon't allow newlines within eqn delimiters\n"
+"-S\tenable safer mode (the default)\n"
+"-U\tenable unsafe mode\n"
+"-Idir\tsearch dir for soelim. Implies -s\n"
+"\n",
+ stdout);
+ exit(0);
+}
+
+void usage(FILE *stream)
+{
+ synopsis(stream);
+ fprintf(stream, "%s -h gives more help\n", program_name);
+}
+
+extern "C" {
+
+void c_error(const char *format, const char *arg1, const char *arg2,
+ const char *arg3)
+{
+ error(format, arg1, arg2, arg3);
+}
+
+void c_fatal(const char *format, const char *arg1, const char *arg2,
+ const char *arg3)
+{
+ fatal(format, arg1, arg2, arg3);
+}
+
+}
diff --git a/contrib/groff/src/roff/groff/pipeline.c b/contrib/groff/src/roff/groff/pipeline.c
new file mode 100644
index 000000000000..a4573ba72013
--- /dev/null
+++ b/contrib/groff/src/roff/groff/pipeline.c
@@ -0,0 +1,411 @@
+/* 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. */
+
+/*
+Compile options are:
+
+-DWCOREFLAG=0200 (or whatever)
+-DHAVE_SYS_SIGLIST
+-DSYS_SIGLIST_DECLARED
+-DHAVE_UNISTD_H
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+extern char *strerror();
+
+#ifdef _POSIX_VERSION
+
+#include <sys/wait.h>
+
+#define PID_T pid_t
+
+#else /* not _POSIX_VERSION */
+
+/* traditional Unix */
+
+#define WIFEXITED(s) (((s) & 0377) == 0)
+#define WIFSTOPPED(s) (((s) & 0377) == 0177)
+#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
+#define WEXITSTATUS(s) (((s) >> 8) & 0377)
+#define WTERMSIG(s) ((s) & 0177)
+#define WSTOPSIG(s) (((s) >> 8) & 0377)
+
+#ifndef WCOREFLAG
+#define WCOREFLAG 0200
+#endif
+
+#define PID_T int
+
+#endif /* not _POSIX_VERSION */
+
+/* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */
+#ifndef WCOREFLAG
+#ifdef WCOREFLG
+#define WCOREFLAG WCOREFLG
+#endif /* WCOREFLG */
+#endif /* not WCOREFLAG */
+
+#ifndef WCOREDUMP
+#ifdef WCOREFLAG
+#define WCOREDUMP(s) ((s) & WCOREFLAG)
+#else /* not WCOREFLAG */
+#define WCOREDUMP(s) (0)
+#endif /* WCOREFLAG */
+#endif /* not WCOREDUMP */
+
+#include "pipeline.h"
+
+#ifdef __STDC__
+#define P(parms) parms
+#else
+#define P(parms) ()
+#define const /* as nothing */
+#endif
+
+#define error c_error
+extern void error P((const char *, const char *, const char *, const char *));
+extern void c_fatal P((const char *, const char *, const char *, const char *));
+
+static void sys_fatal P((const char *));
+static const char *xstrsignal P((int));
+static char *i_to_a P((int));
+
+/* MSVC can support asynchronous processes, but it's unlikely to have
+ fork(). So, until someone writes an emulation, let them at least
+ have a workable groff by using the good-ole DOS pipe simulation
+ via temporary files... */
+
+#if defined(__MSDOS__) || (defined(_WIN32) && !defined(__CYGWIN32__))
+
+#include <process.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "nonposix.h"
+
+/* A signal handler that just records that a signal has happened. */
+static int child_interrupted;
+
+static RETSIGTYPE signal_catcher (int signo)
+{
+ child_interrupted++;
+}
+
+static const char *sh = "sh";
+static const char *command = "command";
+
+const char *
+system_shell_name (void)
+{
+ static const char *shell_name;
+
+ /* We want them to be able to use a Unixy shell if they have it
+ installed. Let spawnlp try to find it, but if it fails, default
+ to COMMAND.COM. */
+ if (shell_name == NULL)
+ {
+ int sh_found = spawnlp (P_WAIT, sh, sh, "-c", ":", NULL) == 0;
+
+ if (sh_found)
+ shell_name = sh;
+ else
+ shell_name = command;
+ }
+ return shell_name;
+}
+
+const char *
+system_shell_dash_c (void)
+{
+ if (strcmp (system_shell_name(), sh) == 0)
+ return "-c";
+ else
+ return "/c";
+}
+
+int
+is_system_shell (const char *shell)
+{
+ size_t shlen;
+ size_t ibase = 0, idot, i;
+
+ if (!shell) /* paranoia */
+ return 0;
+ idot = shlen = strlen(shell);
+
+ for (i = 0; i < shlen; i++)
+ {
+ if (shell[i] == '.')
+ idot = i;
+ else if (shell[i] == '/' || shell[i] == '\\' || shell[i] == ':')
+ {
+ ibase = i + 1;
+ idot = shlen;
+ }
+ }
+
+ /* "sh" and "sh.exe" should compare equal. */
+ return
+ (strncasecmp (shell + ibase, system_shell_name (), idot - ibase) == 0
+ && (idot == shlen
+ || strcasecmp (shell + idot, ".exe") == 0
+ || strcasecmp (shell + idot, ".com") == 0));
+}
+
+/* MSDOS doesn't have `fork', so we need to simulate the pipe by
+ running the programs in sequence with redirected standard streams. */
+
+int run_pipeline (ncommands, commands, no_pipe)
+ int ncommands;
+ char ***commands;
+ int no_pipe;
+{
+ int save_stdin = dup(0);
+ int save_stdout = dup(1);
+ char *tmpfiles[2];
+ char tem1[L_tmpnam], tem2[L_tmpnam];
+ int infile = 0;
+ int outfile = 1;
+ int i, f, ret = 0;
+
+ tmpfiles[0] = tmpnam(tem1);
+ tmpfiles[1] = tmpnam(tem2);
+
+ for (i = 0; i < ncommands; i++)
+ {
+ int exit_status;
+ RETSIGTYPE (*prev_handler)(int);
+
+ if (i)
+ {
+ /* redirect stdin from temp file */
+ f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
+ if (f < 0)
+ sys_fatal("open stdin");
+ if (dup2(f, 0) < 0)
+ sys_fatal("dup2 stdin");
+ if (close(f) < 0)
+ sys_fatal("close stdin");
+ }
+ if ((i < ncommands - 1) && !no_pipe)
+ {
+ /* redirect stdout to temp file */
+ f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
+ if (f < 0)
+ sys_fatal("open stdout");
+ if (dup2(f, 1) < 0)
+ sys_fatal("dup2 stdout");
+ if (close(f) < 0)
+ sys_fatal("close stdout");
+ }
+ else if (dup2(save_stdout, 1) < 0)
+ sys_fatal("restore stdout");
+
+ /* run the program */
+ child_interrupted = 0;
+ prev_handler = signal(SIGINT, signal_catcher);
+ exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
+ signal(SIGINT, prev_handler);
+ if (child_interrupted)
+ {
+ error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
+ ret |= 2;
+ }
+ else if (exit_status < 0)
+ {
+ error("couldn't exec %1: %2", commands[i][0],
+ strerror(errno), (char *)0);
+ fflush(stderr); /* just in case error() doesn't */
+ ret |= 4;
+ }
+ if (exit_status != 0)
+ ret |= 1;
+
+ /* There's no sense to continue with the pipe if one of the
+ programs has ended abnormally, is there? */
+ if (ret != 0)
+ break;
+
+ /* swap temp files: make output of this program be input for the next */
+ infile = 1 - infile;
+ outfile = 1 - outfile;
+ }
+
+ if (dup2(save_stdin, 0) < 0)
+ sys_fatal("restore stdin");
+
+ unlink(tmpfiles[0]);
+ unlink(tmpfiles[1]);
+
+ return ret;
+}
+
+#else /* not __MSDOS__, not _WIN32 */
+
+int run_pipeline(ncommands, commands, no_pipe)
+ int ncommands;
+ char ***commands;
+ int no_pipe;
+{
+ int i;
+ int last_input = 0;
+ PID_T pids[MAX_COMMANDS];
+ int ret = 0;
+ int proc_count = ncommands;
+
+ for (i = 0; i < ncommands; i++) {
+ int pdes[2];
+ PID_T pid;
+ if ((i != ncommands - 1) && !no_pipe) {
+ if (pipe(pdes) < 0)
+ sys_fatal("pipe");
+ }
+ pid = fork();
+ if (pid < 0)
+ sys_fatal("fork");
+ if (pid == 0) {
+ /* child */
+ if (last_input != 0) {
+ if (close(0) < 0)
+ sys_fatal("close");
+ if (dup(last_input) < 0)
+ sys_fatal("dup");
+ if (close(last_input) < 0)
+ sys_fatal("close");
+ }
+ if ((i != ncommands - 1) && !no_pipe) {
+ if (close(1) < 0)
+ sys_fatal("close");
+ if (dup(pdes[1]) < 0)
+ sys_fatal("dup");
+ if (close(pdes[1]) < 0)
+ sys_fatal("close");
+ if (close(pdes[0]))
+ sys_fatal("close");
+ }
+ execvp(commands[i][0], commands[i]);
+ error("couldn't exec %1: %2", commands[i][0],
+ strerror(errno), (char *)0);
+ fflush(stderr); /* just in case error() doesn't */
+ _exit(EXEC_FAILED_EXIT_STATUS);
+ }
+ /* in the parent */
+ if (last_input != 0) {
+ if (close(last_input) < 0)
+ sys_fatal("close");
+ }
+ if ((i != ncommands - 1) && !no_pipe) {
+ if (close(pdes[1]) < 0)
+ sys_fatal("close");
+ last_input = pdes[0];
+ }
+ pids[i] = pid;
+ }
+ while (proc_count > 0) {
+ int status;
+ PID_T pid = wait(&status);
+ if (pid < 0)
+ sys_fatal("wait");
+ for (i = 0; i < ncommands; i++)
+ if (pids[i] == pid) {
+ pids[i] = -1;
+ --proc_count;
+ if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+#ifdef SIGPIPE
+ if (sig == SIGPIPE) {
+ if (i == ncommands - 1) {
+
+ /* This works around a problem that occurred when using the
+ rerasterize action in gxditview. What seemed to be
+ happening (on SunOS 4.1.1) was that pclose() closed the
+ pipe and waited for groff, gtroff got a SIGPIPE, but
+ gpic blocked writing to gtroff, and so groff blocked
+ waiting for gpic and gxditview blocked waiting for
+ groff. I don't understand why gpic wasn't getting a
+ SIGPIPE. */
+ int j;
+ for (j = 0; j < ncommands; j++)
+ if (pids[j] > 0)
+ (void)kill(pids[j], SIGPIPE);
+ }
+ }
+ else
+#endif /* SIGPIPE */
+ {
+ error("%1: %2%3",
+ commands[i][0],
+ xstrsignal(sig),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+ ret |= 2;
+ }
+ }
+ else if (WIFEXITED(status)) {
+ int exit_status = WEXITSTATUS(status);
+ if (exit_status == EXEC_FAILED_EXIT_STATUS)
+ ret |= 4;
+ else if (exit_status != 0)
+ ret |= 1;
+ }
+ else
+ error("unexpected status %1",
+ i_to_a(status), (char *)0, (char *)0);
+ break;
+ }
+ }
+ return ret;
+}
+
+#endif /* not __MSDOS__, not _WIN32 */
+
+static void sys_fatal(s)
+ const char *s;
+{
+ c_fatal("%1: %2", s, strerror(errno), (char *)0);
+}
+
+static char *i_to_a(n)
+ int n;
+{
+ static char buf[12];
+ sprintf(buf, "%d", n);
+ return buf;
+}
+
+static const char *xstrsignal(n)
+ int n;
+{
+ static char buf[sizeof("Signal ") + 1 + sizeof(int)*3];
+#ifdef NSIG
+#ifdef SYS_SIGLIST_DECLARED
+ if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
+ return sys_siglist[n];
+#endif /* SYS_SIGLIST_DECLARED */
+#endif /* NSIG */
+ sprintf(buf, "Signal %d", n);
+ return buf;
+}
diff --git a/contrib/groff/src/roff/groff/pipeline.h b/contrib/groff/src/roff/groff/pipeline.h
new file mode 100644
index 000000000000..abb4b0cad279
--- /dev/null
+++ b/contrib/groff/src/roff/groff/pipeline.h
@@ -0,0 +1,30 @@
+/* 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 __cplusplus
+extern "C" {
+ int run_pipeline(int, char ***, int);
+}
+#endif
+
+/* run_pipeline can handle at most this many commands */
+#define MAX_COMMANDS 10
+
+/* Children exit with this status if execvp fails. */
+#define EXEC_FAILED_EXIT_STATUS 0xff
diff --git a/contrib/groff/src/roff/grog/Makefile.sub b/contrib/groff/src/roff/grog/Makefile.sub
new file mode 100644
index 000000000000..054d06e1cec9
--- /dev/null
+++ b/contrib/groff/src/roff/grog/Makefile.sub
@@ -0,0 +1,26 @@
+MAN1=grog.n
+CLEANADD=grog
+NAMEPREFIX=$(g)
+
+all: grog
+
+grog: grog.pl grog.sh
+ if test -n "$(PERLPATH)" && test -f "$(PERLPATH)"; then \
+ rm -f $@; \
+ sed -e "s|/usr/bin/perl|$(PERLPATH)|" \
+ -e "s|@VERSION@|$(version)$(revision)|" $(srcdir)/grog.pl >$@; \
+ else \
+ rm -f $@; \
+ sed -e "s|@g@|$(g)|g" \
+ -e "s|@VERSION@|$(version)$(revision)|" \
+ -e $(SH_SCRIPT_SED_CMD) $(srcdir)/grog.sh >$@; \
+ fi
+ chmod +x $@
+
+install_data: grog
+ -test -d $(bindir) || $(mkinstalldirs) $(bindir)
+ -rm -f $(bindir)/grog
+ $(INSTALL_SCRIPT) grog $(bindir)/grog
+
+uninstall_sub:
+ -rm -f $(bindir)/grog
diff --git a/contrib/groff/src/roff/grog/grog.man b/contrib/groff/src/roff/grog/grog.man
new file mode 100644
index 000000000000..4a2d31144be0
--- /dev/null
+++ b/contrib/groff/src/roff/grog/grog.man
@@ -0,0 +1,86 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH GROG @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grog \- guess options for groff command
+.SH SYNOPSIS
+.B grog
+[
+.BI \- option
+\|.\|.\|.
+]
+[
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B grog
+reads
+.I files
+and guesses which of the
+.BR groff (@MAN1EXT@)
+options
+.BR \-e ,
+.BR \-man ,
+.BR \-me ,
+.BR \-mm ,
+.BR \-ms ,
+.BR \-mdoc,
+.BR \-mdoc-old,
+.BR \-p ,
+.BR \-R ,
+.BR \-g ,
+.BR \-G ,
+.BR \-s ,
+and
+.BR \-t
+are required for printing
+.IR files ,
+and prints the groff command including those options on the standard output.
+A filename of
+.B \-
+is taken to refer to the standard input.
+If no files are specified the standard input will be read.
+Any specified options will be included in the printed command.
+No space is allowed between options and their arguments.
+The only options recognized are
+.B \-C
+(which is also passed on) to enable compatibility mode, and
+.B \-v
+to print the version number.
+.LP
+For example,
+.IP
+.B `grog \-Tdvi paper.ms`
+.LP
+will guess the appropriate command to print
+.B paper.ms
+and then run it after adding the
+.B \-Tdvi
+option.
+.SH "SEE ALSO"
+.BR doctype (1),
+.BR groff (@MAN1EXT@),
+.BR @g@troff (@MAN1EXT@),
+.BR @g@tbl (@MAN1EXT@),
+.BR @g@pic (@MAN1EXT@),
+.BR @g@eqn (@MAN1EXT@),
+.BR @g@refer (@MAN1EXT@),
+.BR @g@grn (@MAN1EXT@),
+.BR grap (1),
+.BR @g@soelim (@MAN1EXT@)
diff --git a/contrib/groff/src/roff/grog/grog.pl b/contrib/groff/src/roff/grog/grog.pl
new file mode 100644
index 000000000000..57cd159461bb
--- /dev/null
+++ b/contrib/groff/src/roff/grog/grog.pl
@@ -0,0 +1,183 @@
+#!/usr/bin/perl
+# grog -- guess options for groff command
+# Inspired by doctype script in Kernighan & Pike, Unix Programming
+# Environment, pp 306-8.
+
+$prog = $0;
+$prog =~ s@.*/@@;
+
+$sp = "[\\s\\n]";
+
+push(@command, "groff");
+
+while ($ARGV[0] =~ /^-./) {
+ $arg = shift(@ARGV);
+ $sp = "" if $arg eq "-C";
+ &usage(0) if $arg eq "-v" || $arg eq "--version";
+ &help() if $arg eq "--help";
+ last if $arg eq "--";
+ push(@command, $arg);
+}
+
+if (@ARGV) {
+ foreach $arg (@ARGV) {
+ &process($arg, 0);
+ }
+}
+else {
+ &process("-", 0);
+}
+
+sub process {
+ local($filename, $level) = @_;
+ local(*FILE);
+
+ if (!open(FILE, $filename eq "-" ? $filename : "< $filename")) {
+ print STDERR "$prog: can't open \`$filename': $!\n";
+ exit 1 unless $level;
+ return;
+ }
+ while (<FILE>) {
+ if (/^\.TS$sp/) {
+ $_ = <FILE>;
+ if (!/^\./) {
+ $tbl++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.EQ$sp/) {
+ $_ = <FILE>;
+ if (!/^\./ || /^\.[0-9]/) {
+ $eqn++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.GS$sp/) {
+ $_ = <FILE>;
+ if (!/^\./) {
+ $grn++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.G1$sp/) {
+ $_ = <FILE>;
+ if (!/^\./) {
+ $grap++;
+ $pic++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.PS$sp([ 0-9.<].*)?$/) {
+ if (/^\.PS\s*<\s*(\S+)/) {
+ $pic++;
+ $soelim++ if $level;
+ &process($1, $level);
+ }
+ else {
+ $_ = <FILE>;
+ if (!/^\./ || /^\.ps/) {
+ $pic++;
+ $soelim++ if $level;
+ }
+ }
+ }
+ elsif (/^\.R1$sp/ || /^\.\[$sp/) {
+ $refer++;
+ $soelim++ if $level;
+ }
+ elsif (/^\.[PLI]P$sp/) {
+ $PP++;
+ }
+ elsif (/^\.P$/) {
+ $P++;
+ }
+ elsif (/^\.(PH|SA)$sp/) {
+ $mm++;
+ }
+ elsif (/^\.TH$sp/) {
+ $TH++;
+ }
+ elsif (/^\.SH$sp/) {
+ $SH++;
+ }
+ elsif (/^\.([pnil]p|sh)$sp/) {
+ $me++;
+ }
+ elsif (/^\.Dd$sp/) {
+ $mdoc++;
+ }
+ elsif (/^\.(Tp|Dp|De|Cx|Cl)$sp/) {
+ $mdoc_old = 1;
+ }
+ # In the old version of -mdoc `Oo' is a toggle, in the new it's
+ # closed by `Oc'.
+ elsif (/^\.Oo$sp/) {
+ $Oo++;
+ }
+ elsif (/^\.Oc$sp/) {
+ $Oo--;
+ }
+ if (/^\.so$sp/) {
+ chop;
+ s/^.so *//;
+ s/\\\".*//;
+ s/ .*$//;
+ &process($_, $level + 1) unless /\\/ || $_ eq "";
+ }
+ }
+ close(FILE);
+}
+
+sub usage {
+ local($exit_status) = $_;
+ print "GNU grog (groff) version @VERSION@\n";
+ exit $exit_status;
+}
+
+sub help {
+ print "usage: grog [ option ...] [files...]\n";
+ exit 0;
+}
+
+if ($pic || $tbl || $eqn || $grn || $grap || $refer) {
+ $s = "-";
+ $s .= "s" if $soelim;
+ $s .= "R" if $refer;
+ # grap must be run before pic
+ $s .= "G" if $grap;
+ $s .= "p" if $pic;
+ $s .= "g" if $grn;
+ $s .= "t" if $tbl;
+ $s .= "e" if $eqn;
+ push(@command, $s);
+}
+
+if ($me > 0) {
+ push(@command, "-me");
+}
+elsif ($SH > 0 && $TH > 0) {
+ push(@command, "-man");
+}
+elsif ($PP > 0) {
+ push(@command, "-ms");
+}
+elsif ($P > 0 || $mm > 0) {
+ push(@command, "-mm");
+}
+elsif ($mdoc > 0) {
+ push(@command, ($mdoc_old || $Oo > 0) ? "-mdoc-old" : "-mdoc");
+}
+
+push(@command, "--") if @ARGV && $ARGV[0] =~ /^-./;
+
+push(@command, @ARGV);
+
+# We could implement an option to execute the command here.
+
+foreach (@command) {
+ next unless /[\$\\\"\';&()|<> \t\n]/;
+ s/\'/\'\\\'\'/;
+ $_ = "'" . $_ . "'";
+}
+
+print join(' ', @command), "\n";
diff --git a/contrib/groff/src/roff/grog/grog.sh b/contrib/groff/src/roff/grog/grog.sh
new file mode 100644
index 000000000000..7919dbf5fe70
--- /dev/null
+++ b/contrib/groff/src/roff/grog/grog.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+# grog -- guess options for groff command
+# Like doctype in Kernighan & Pike, Unix Programming Environment, pp 306-8.
+
+soelim=@g@soelim
+
+opts=
+sp="([ ]|$)"
+
+for arg
+do
+ case "$arg" in
+ --)
+ shift; break;;
+ -)
+ break;;
+ -C)
+ sp=; opts="$opts -C"; shift; break;;
+ -v | --version)
+ echo "GNU grog (groff) version @VERSION@"
+ exit 0;;
+ --help)
+ echo "usage: grog [ option ...] [files...]"
+ exit 0;;
+ -*)
+ opts="$opts $arg"; shift;;
+ *)
+ break;;
+ esac
+done
+
+egrep -h "^\.(P|PS|[PLI]P|[pnil]p|sh|Dd|Tp|Dp|De|Cx|Cl|Oo|Oc|TS|EQ|TH|SH|so|\[|R1|GS|G1|PH|SA)$sp" $* \
+| sed -e '/^\.so/s/^.*$/.SO_START\
+&\
+.SO_END/' \
+| $soelim \
+| egrep '^\.(P|PS|[PLI]P|[pnil]p|sh|Dd|Tp|Dp|De|Cx|Cl|Oo|Oc|TS|EQ|TH|SH|\[|R1|GS|G1|PH|SA|SO_START|SO_END)' \
+| awk '
+/^\.SO_START$/ { so = 1 }
+/^\.SO_END$/ { so = 0 }
+/^\.TS/ { tbl++; if (so > 0) soelim++ }
+/^\.PS([ 0-9.<].*)?$/ { pic++; if (so > 0) soelim++ }
+/^\.EQ/ { eqn++; if (so > 0) soelim++ }
+/^\.(R1|\[)/ { refer++; if (so > 0) soelim++ }
+/^\.GS/ { grn++; if (so > 0) soelim++ }
+/^\.G1/ { grap++; pic++; if (so > 0) soelim++ }
+/^\.TH/ { TH++ }
+/^\.[PLI]P/ { PP++ }
+/^\.P$/ { P++ }
+/^\.SH/ { SH++ }
+/^\.(PH|SA)/ { mm++ }
+/^\.([pnil]p|sh)/ { me++ }
+/^\.Dd/ { mdoc++ }
+/^\.(Tp|Dp|De|Cx|Cl)/ { mdoc_old++ }
+/^\.Oo/ { Oo++ }
+/^\.Oc/ { Oo-- }
+
+END {
+ if (files ~ /^-/)
+ files = "-- " files
+ printf "groff"
+ if (pic > 0 || tbl > 0 || grn > 0 || grap > 0 || eqn > 0 || refer > 0) {
+ printf " -"
+ if (soelim > 0) printf "s"
+ if (refer > 0) printf "R"
+ if (grn > 0) printf "g"
+ if (grap > 0) printf "G"
+ if (pic > 0) printf "p"
+ if (tbl > 0) printf "t"
+ if (eqn > 0) printf "e"
+ }
+ if (me > 0)
+ printf " -me"
+ else if (SH > 0 && TH > 0)
+ printf " -man"
+ else if (PP > 0)
+ printf " -ms"
+ else if (P > 0 || mm > 0)
+ printf " -mm"
+ else if (mdoc > 0) {
+ if (mdoc_old > 0 || Oo > 0)
+ printf " -mdoc-old"
+ else
+ printf " -mdoc"
+ }
+ if (opts != "")
+ printf "%s", opts
+ if (files != "")
+ printf " %s", files
+ print ""
+}' "opts=$opts" "files=$*" -
diff --git a/contrib/groff/src/roff/nroff/Makefile.sub b/contrib/groff/src/roff/nroff/Makefile.sub
new file mode 100644
index 000000000000..b9cb4823ba4b
--- /dev/null
+++ b/contrib/groff/src/roff/nroff/Makefile.sub
@@ -0,0 +1,20 @@
+MAN1=nroff.n
+NAMEPREFIX=$(g)
+CLEANADD=nroff
+
+all: nroff
+
+nroff: nroff.sh
+ rm -f $@
+ sed -e "s|@BINDIR@|$(bindir)|g" \
+ -e $(SH_SCRIPT_SED_CMD) \
+ -e "s|@VERSION@|$(version)$(revision)|" $(srcdir)/nroff.sh >$@
+ chmod +x $@
+
+install_data: nroff
+ -test -d $(bindir) || $(mkinstalldirs) $(bindir)
+ -rm -f $(bindir)/$(NAMEPREFIX)nroff
+ $(INSTALL_SCRIPT) nroff $(bindir)/$(NAMEPREFIX)nroff
+
+uninstall_sub:
+ -rm -f $(bindir)/$(NAMEPREFIX)nroff
diff --git a/contrib/groff/src/roff/troff/Makefile.sub b/contrib/groff/src/roff/troff/Makefile.sub
new file mode 100644
index 000000000000..e883959dc007
--- /dev/null
+++ b/contrib/groff/src/roff/troff/Makefile.sub
@@ -0,0 +1,48 @@
+PROG=troff
+MAN1=troff.n
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ env.o \
+ node.o \
+ input.o \
+ div.o \
+ symbol.o \
+ dictionary.o \
+ reg.o \
+ number.o \
+ majorminor.o
+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
+HDRS=\
+ $(srcdir)/charinfo.h \
+ $(srcdir)/dictionary.h \
+ $(srcdir)/div.h \
+ $(srcdir)/env.h \
+ $(srcdir)/hvunits.h \
+ $(srcdir)/input.h \
+ $(srcdir)/node.h \
+ $(srcdir)/reg.h \
+ $(srcdir)/request.h \
+ $(srcdir)/symbol.h \
+ $(srcdir)/token.h \
+ $(srcdir)/troff.h
+GENSRCS=majorminor.cc
+NAMEPREFIX=$(g)
+
+majorminor.cc: $(top_srcdir)/VERSION $(top_srcdir)/REVISION
+ @echo Making $@
+ @-rm -f $@
+ @echo const char \*major_version = \
+ \"`sed -e 's/^\([^.]*\)\..*$$/\1/' $(top_srcdir)/VERSION`\"\; >$@
+ @echo const char \*minor_version = \
+ \"`sed -e 's/^[^.]*\.\([0-9]*\).*$$/\1/' $(top_srcdir)/VERSION`\"\; >>$@
+ @echo const char \*revision = \"`cat $(top_srcdir)/REVISION`\"\; >>$@
diff --git a/contrib/groff/src/roff/troff/TODO b/contrib/groff/src/roff/troff/TODO
new file mode 100644
index 000000000000..66605973e569
--- /dev/null
+++ b/contrib/groff/src/roff/troff/TODO
@@ -0,0 +1,134 @@
+A line prefix request to make e.g. French quotation possible:
+
+ He said: >> blablablabla
+ >> blablabla blabla bla
+ >> blabla blabla bla bla
+ >> bla bla bla blablabla
+ >> blabla. <<
+
+Give a more helpful error message when the indent is set to a value
+greater than the line-length.
+
+Tracing. This is a pain to implement because requests are responsible
+for reading their own arguments.
+
+Possibly implement -s option (stop every N pages). This functionality
+would be more appropriate in a postprocessor.
+
+Line breaking should be smarter. In particular, it should be possible
+to shrink spaces. Also avoid having a line that's been shrunk a lot
+next to a line that's been stretched a lot. The difficulty is to
+design a mechanism that allows the user complete control over the
+decision of where to break the line.
+
+Provide a mechanism to control the shape of the rag in non-justified
+text.
+
+Add a discretionary break escape sequence. \='...'...'...' like TeX.
+
+Think about kerning between characters and spaces. (Need to implement
+get_breakpoints and split methods for kern_pair_node class.)
+
+In troff, if .L > 1 when a diversion is reread in no-fill mode, then
+extra line-spacing is added on. Groff at the moment treats line-spacing
+like vertical spacing and doesn't do this.
+
+Suppose \(ch comes from a special font S, and that the current font is
+R. Suppose that R contains a hyphen character and that S does not.
+Suppose that the current font is R. Suppose that \(ch is in a word
+and has a non-zero hyphen-type. Then we ought to be able to hyphenate,
+but we won't be able to because we will look for the hyphen only in
+font S and not in font R.
+
+Perhaps the current input level should be accessible in a number register.
+
+Should \w deal with a newline like \X?
+
+Have another look at uses of token::delimiter. Perhaps we need to
+distinguish the case where we want to see if a token could start a
+number, from the case where we want to see if it could occur somewhere
+in a number expression.
+
+Provide a facility like copy thru in pic.
+
+Fancier implementation of font families which doesn't group fonts into
+families purely on the basis of their names.
+
+In the DESC file make the number of fonts optional if they are all on
+one line.
+
+Number register to give the diversion level.
+
+Time various alternative implementations of scale (both in font.c and
+number.c). On a sparc it's faster to always do it in floating point.
+
+Devise a more compact representation for the hyphenation patterns trie.
+
+Have a per-environment parameter to increase letter-spacing.
+
+Number register to return character height.
+
+Number register to return character slant.
+
+Request to set character height.
+
+Request to set character slant.
+
+Provide some way to upcase or downcase strings.
+
+Support non-uniformly scalable fonts. Perhaps associate a suffix with
+a particular range of sizes. eg
+ sizesuffix .display 14-512
+Then is you ask for R at pointsize 16, groff will first look for
+R.display and then R. Probably necessary to be able to specify a
+separate unitwidth for each sizesuffix (eg. for X).
+
+Variant of `.it' for which a line interrupted with \c counts as one
+input line.
+
+Make it possible to suppress hyphenation on a word-by-word basis.
+(Perhaps store hyphenation flags in tfont.)
+
+Possibly allow multiple simultaneous input line traps.
+
+Unpaddable, breakable space escape sequence.
+
+Support hanging punctuation.
+
+In justified text, if the last line of a paragraph is only a little
+bit short it might be desirable to justify the line. Allow the user
+control over this.
+
+The pm request could print where the macro was defined. Also could
+optionally print the contents of a macro.
+
+Provide some way to round numbers to multiples of the current
+horizontal or vertical resolution.
+
+Better string-processing support (search).
+
+Generalized ligatures.
+
+Provide some way for a macro to tell whether it was called with `'' or
+`.'. This would be useful for implementing a tracing macro package.
+
+Request to remove an environment. (Maintain a count of the references
+to the environment from the environment table, environment dictionary
+or environment stack.)
+
+Perhaps in the nr request a leading `-' should only be recognized as a
+decrement when it's at the same input level as the request.
+
+Don't ever change a charinfo. Create new variants instead and chain
+them together.
+
+Unix troff appears to read the first character of a request name in
+copy mode. Should we do the same?
+
+Number register giving name of end macro.
+
+More thorough range checking.
+
+Provide syntax for octal and hexadecimal numeric constants. Perhaps
+o#100 and x#7f as per Scheme. Or perhaps PostScript 16#7f. Ambiguity
+between whether `c' is treated as digit or scaling indicator.
diff --git a/contrib/groff/src/roff/troff/charinfo.h b/contrib/groff/src/roff/troff/charinfo.h
new file mode 100644
index 000000000000..a4ecd574828f
--- /dev/null
+++ b/contrib/groff/src/roff/troff/charinfo.h
@@ -0,0 +1,171 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+class macro;
+
+class charinfo {
+ static int next_index;
+ charinfo *translation;
+ int index;
+ int number;
+ macro *mac;
+ unsigned char special_translation;
+ unsigned char hyphenation_code;
+ unsigned char flags;
+ unsigned char ascii_code;
+ char not_found;
+ char transparent_translate; // non-zero means translation applies to
+ // to transparent throughput
+public:
+ enum {
+ ENDS_SENTENCE = 1,
+ BREAK_BEFORE = 2,
+ BREAK_AFTER = 4,
+ OVERLAPS_HORIZONTALLY = 8,
+ OVERLAPS_VERTICALLY = 16,
+ TRANSPARENT = 32,
+ NUMBERED = 64
+ };
+ enum {
+ TRANSLATE_NONE,
+ TRANSLATE_SPACE,
+ TRANSLATE_DUMMY,
+ TRANSLATE_STRETCHABLE_SPACE,
+ TRANSLATE_HYPHEN_INDICATOR
+ };
+ symbol nm;
+ charinfo(symbol s);
+ int get_index();
+ int ends_sentence();
+ int overlaps_vertically();
+ int overlaps_horizontally();
+ int can_break_before();
+ int can_break_after();
+ int transparent();
+ unsigned char get_hyphenation_code();
+ unsigned char get_ascii_code();
+ void set_hyphenation_code(unsigned char);
+ void set_ascii_code(unsigned char);
+ charinfo *get_translation(int = 0);
+ void set_translation(charinfo *, int);
+ void set_flags(unsigned char);
+ void set_special_translation(int, int);
+ int get_special_translation(int = 0);
+ macro *set_macro(macro *);
+ macro *get_macro();
+ int first_time_not_found();
+ void set_number(int);
+ int get_number();
+ int numbered();
+ symbol *get_symbol();
+};
+
+charinfo *get_charinfo(symbol);
+extern charinfo *charset_table[];
+charinfo *get_charinfo_by_number(int);
+
+inline int charinfo::overlaps_horizontally()
+{
+ return flags & OVERLAPS_HORIZONTALLY;
+}
+
+inline int charinfo::overlaps_vertically()
+{
+ return flags & OVERLAPS_VERTICALLY;
+}
+
+inline int charinfo::can_break_before()
+{
+ return flags & BREAK_BEFORE;
+}
+
+inline int charinfo::can_break_after()
+{
+ return flags & BREAK_AFTER;
+}
+
+inline int charinfo::ends_sentence()
+{
+ return flags & ENDS_SENTENCE;
+}
+
+inline int charinfo::transparent()
+{
+ return flags & TRANSPARENT;
+}
+
+inline int charinfo::numbered()
+{
+ return flags & NUMBERED;
+}
+
+inline charinfo *charinfo::get_translation(int transparent_throughput)
+{
+ return (transparent_throughput && !transparent_translate
+ ? 0
+ : translation);
+}
+
+inline unsigned char charinfo::get_hyphenation_code()
+{
+ return hyphenation_code;
+}
+
+inline unsigned char charinfo::get_ascii_code()
+{
+ return ascii_code;
+}
+
+inline void charinfo::set_flags(unsigned char c)
+{
+ flags = c;
+}
+
+inline int charinfo::get_index()
+{
+ return index;
+}
+
+inline int charinfo::get_special_translation(int transparent_throughput)
+{
+ return (transparent_throughput && !transparent_translate
+ ? int(TRANSLATE_NONE)
+ : special_translation);
+}
+
+inline macro *charinfo::get_macro()
+{
+ return mac;
+}
+
+inline int charinfo::first_time_not_found()
+{
+ if (not_found)
+ return 0;
+ else {
+ not_found = 1;
+ return 1;
+ }
+}
+
+inline symbol *charinfo::get_symbol()
+{
+ return( &nm );
+}
diff --git a/contrib/groff/src/roff/troff/column.cc b/contrib/groff/src/roff/troff/column.cc
new file mode 100644
index 000000000000..8d6a6ebe5976
--- /dev/null
+++ b/contrib/groff/src/roff/troff/column.cc
@@ -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.cc b/contrib/groff/src/roff/troff/dictionary.cc
new file mode 100644
index 000000000000..169536c6761b
--- /dev/null
+++ b/contrib/groff/src/roff/troff/dictionary.cc
@@ -0,0 +1,212 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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(int p)
+{
+ const int SMALL = 10;
+ unsigned 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/dictionary.h b/contrib/groff/src/roff/troff/dictionary.h
new file mode 100644
index 000000000000..4f319be49504
--- /dev/null
+++ b/contrib/groff/src/roff/troff/dictionary.h
@@ -0,0 +1,92 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+
+
+// there is no distinction between name with no value and name with NULL value
+// null names are not permitted (they will be ignored).
+
+struct association {
+ symbol s;
+ void *v;
+ association() : v(0) {}
+};
+
+class dictionary;
+
+class dictionary_iterator {
+ dictionary *dict;
+ int i;
+public:
+ dictionary_iterator(dictionary &);
+ int get(symbol *, void **);
+};
+
+class dictionary {
+ int size;
+ int used;
+ double threshold;
+ double factor;
+ association *table;
+ void rehash(int);
+public:
+ dictionary(int);
+ void *lookup(symbol s, void *v=0); // returns value associated with key
+ void *lookup(const char *);
+ // if second parameter not NULL, value will be replaced
+ void *remove(symbol);
+ friend class dictionary_iterator;
+};
+
+class object {
+ int rcount;
+ public:
+ object();
+ virtual ~object();
+ void add_reference();
+ void remove_reference();
+};
+
+class object_dictionary;
+
+class object_dictionary_iterator {
+ dictionary_iterator di;
+public:
+ object_dictionary_iterator(object_dictionary &);
+ int get(symbol *, object **);
+};
+
+class object_dictionary {
+ dictionary d;
+public:
+ object_dictionary(int);
+ object *lookup(symbol nm);
+ void define(symbol nm, object *obj);
+ void rename(symbol oldnm, symbol newnm);
+ void remove(symbol nm);
+ int alias(symbol newnm, symbol oldnm);
+ friend class object_dictionary_iterator;
+};
+
+
+inline int object_dictionary_iterator::get(symbol *sp, object **op)
+{
+ return di.get(sp, (void **)op);
+}
diff --git a/contrib/groff/src/roff/troff/div.cc b/contrib/groff/src/roff/troff/div.cc
new file mode 100644
index 000000000000..281c1af8b452
--- /dev/null
+++ b/contrib/groff/src/roff/troff/div.cc
@@ -0,0 +1,1161 @@
+// -*- 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. */
+
+
+// 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), 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) {
+ space(d, 1);
+ truncated_space = -d;
+ needed_space = n;
+ }
+}
+
+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)
+{
+ 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), no_space_mode(0)
+{
+}
+
+// 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 transparent() 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) {
+ if (begin_page()) {
+ // This happens if there's a top of page trap, and the first-page
+ // transition is caused by `'sp'.
+ truncated_space = n > V0 ? n : V0;
+ 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();
+ 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
+
+int top_level_diversion::begin_page()
+{
+ 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 = V0;
+ 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(".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()
+{
+ if (curdiv == topdiv)
+ topdiv->no_space_mode = 1;
+ skip_line();
+}
+
+void restore_spacing()
+{
+ if (curdiv == topdiv)
+ topdiv->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->space(n);
+ else
+ // The line might have had line spacing that was truncated.
+ truncated_space += n;
+ curenv->add_html_tag(".sp", n.to_units());
+ tok.next();
+}
+
+void blank_line()
+{
+ curenv->do_break();
+ if (!trap_sprung_flag)
+ curdiv->space(curenv->get_vertical_spacing());
+ 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;
+ 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(".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;
+}
+
+void init_div_requests()
+{
+ init_request("wh", when_request);
+ init_request("ch", change_trap);
+ init_request("pl", page_length);
+ init_request("po", page_offset);
+ init_request("rs", restore_spacing);
+ init_request("ns", no_space);
+ init_request("sp", space_request);
+ init_request("di", divert);
+ init_request("da", divert_append);
+ init_request("box", box);
+ init_request("boxa", box_append);
+ init_request("bp", begin_page);
+ init_request("ne", need_space);
+ init_request("pn", page_number);
+ init_request("dt", diversion_trap);
+ init_request("rt", return_request);
+ init_request("mk", mark);
+ init_request("sv", save_vertical_space);
+ init_request("os", output_saved_vertical_space);
+ init_request("fl", flush_output);
+ init_request("vpt", vertical_position_traps);
+ init_request("ptr", print_traps);
+ number_reg_dictionary.define(".a",
+ new constant_int_reg(&last_post_line_extra_space));
+ number_reg_dictionary.define(".z", new diversion_name_reg);
+ number_reg_dictionary.define(".o", new page_offset_reg);
+ number_reg_dictionary.define(".p", new page_length_reg);
+ number_reg_dictionary.define(".d", new vertical_position_reg);
+ number_reg_dictionary.define(".h", new high_water_mark_reg);
+ number_reg_dictionary.define(".t", new distance_to_next_trap_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(".vpt",
+ new constant_int_reg(&vertical_position_traps_flag));
+ number_reg_dictionary.define("%", new page_number_reg);
+ number_reg_dictionary.define(".pn", new next_page_number_reg);
+ number_reg_dictionary.define(".trunc",
+ new constant_vunits_reg(&truncated_space));
+ number_reg_dictionary.define(".ne",
+ new constant_vunits_reg(&needed_space));
+ number_reg_dictionary.define(".pe", new page_ejecting_reg);
+}
diff --git a/contrib/groff/src/roff/troff/div.h b/contrib/groff/src/roff/troff/div.h
new file mode 100644
index 000000000000..83f9e33c7748
--- /dev/null
+++ b/contrib/groff/src/roff/troff/div.h
@@ -0,0 +1,156 @@
+// -*- 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. */
+
+class diversion {
+ friend void do_divert(int append, int boxing);
+ friend void end_diversions();
+ diversion *prev;
+ node *saved_line;
+ hunits saved_width_total;
+ int saved_space_total;
+ hunits saved_saved_indent;
+ hunits saved_target_text_length;
+ int saved_prev_line_interrupted;
+protected:
+ symbol nm;
+ vunits vertical_position;
+ vunits high_water_mark;
+public:
+ vunits marked_place;
+ diversion(symbol s = NULL_SYMBOL);
+ virtual ~diversion();
+ virtual void output(node *nd, int retain_size, vunits vs, vunits post_vs,
+ hunits width) = 0;
+ virtual void transparent_output(unsigned char) = 0;
+ virtual void transparent_output(node *) = 0;
+ virtual void space(vunits distance, int forced = 0) = 0;
+#ifdef COLUMN
+ virtual void vjustify(symbol) = 0;
+#endif /* COLUMN */
+ vunits get_vertical_position() { return vertical_position; }
+ vunits get_high_water_mark() { return high_water_mark; }
+ virtual vunits distance_to_next_trap() = 0;
+ void need(vunits);
+ const char *get_diversion_name() { return nm.contents(); }
+ virtual void set_diversion_trap(symbol, vunits) = 0;
+ virtual void clear_diversion_trap() = 0;
+ virtual void copy_file(const char *filename) = 0;
+};
+
+class macro;
+
+class macro_diversion : public diversion {
+ macro *mac;
+ hunits max_width;
+ symbol diversion_trap;
+ vunits diversion_trap_pos;
+public:
+ macro_diversion(symbol, int);
+ ~macro_diversion();
+ void output(node *nd, int retain_size, vunits vs, vunits post_vs,
+ hunits width);
+ void transparent_output(unsigned char);
+ void transparent_output(node *);
+ void space(vunits distance, int forced = 0);
+#ifdef COLUMN
+ void vjustify(symbol);
+#endif /* COLUMN */
+ vunits distance_to_next_trap();
+ void set_diversion_trap(symbol, vunits);
+ void clear_diversion_trap();
+ void copy_file(const char *filename);
+};
+
+struct trap {
+ trap *next;
+ vunits position;
+ symbol nm;
+ trap(symbol, vunits, trap *);
+};
+
+struct output_file;
+
+class top_level_diversion : public diversion {
+ int page_number;
+ int page_count;
+ int last_page_count;
+ vunits page_length;
+ hunits prev_page_offset;
+ hunits page_offset;
+ trap *page_trap_list;
+ trap *find_next_trap(vunits *);
+ int have_next_page_number;
+ int next_page_number;
+ int ejecting_page; // Is the current page being ejected?
+public:
+ int before_first_page;
+ int no_space_mode;
+ top_level_diversion();
+ void output(node *nd, int retain_size, vunits vs, vunits post_vs,
+ hunits width);
+ void transparent_output(unsigned char);
+ void transparent_output(node *);
+ void space(vunits distance, int forced = 0);
+#ifdef COLUMN
+ void vjustify(symbol);
+#endif /* COLUMN */
+ hunits get_page_offset() { return page_offset; }
+ vunits get_page_length() { return page_length; }
+ vunits distance_to_next_trap();
+ void add_trap(symbol nm, vunits pos);
+ void change_trap(symbol nm, vunits pos);
+ void remove_trap(symbol);
+ void remove_trap_at(vunits pos);
+ void print_traps();
+ int get_page_count() { return page_count; }
+ int get_page_number() { return page_number; }
+ int get_next_page_number();
+ void set_page_number(int n) { page_number = n; }
+ int begin_page();
+ void set_next_page_number(int);
+ void set_page_length(vunits);
+ void copy_file(const char *filename);
+ int get_ejecting() { return ejecting_page; }
+ void set_ejecting() { ejecting_page = 1; }
+ friend void page_offset();
+ void set_diversion_trap(symbol, vunits);
+ void clear_diversion_trap();
+ void set_last_page() { last_page_count = page_count; }
+};
+
+extern top_level_diversion *topdiv;
+extern diversion *curdiv;
+
+extern int exit_started;
+extern int done_end_macro;
+extern int last_page_number;
+extern int seen_last_page_ejector;
+
+void spring_trap(symbol); // implemented by input.c
+extern int trap_sprung_flag;
+void postpone_traps();
+int unpostpone_traps();
+
+void push_page_ejector();
+void continue_page_eject();
+void handle_first_page_transition();
+void blank_line();
+
+extern void cleanup_and_exit(int);
diff --git a/contrib/groff/src/roff/troff/env.cc b/contrib/groff/src/roff/troff/env.cc
new file mode 100644
index 000000000000..56f357c95c8b
--- /dev/null
+++ b/contrib/groff/src/roff/troff/env.cc
@@ -0,0 +1,3439 @@
+// -*- 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 "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 <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)
+{
+ assert(n != 0);
+ 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, 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,
+ 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")) {
+ 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 (fam.is_null()) {
+ 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;
+}
+
+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),
+ 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 */
+ need_eol(0),
+ ignore_next_eol(0),
+ emitted_node(0),
+ 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),
+ 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 */
+ need_eol(0),
+ ignore_next_eol(0),
+ 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;
+ 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;
+}
+
+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(".ps", n);
+ }
+ else
+ curenv->set_size(0);
+ skip_line();
+}
+
+void space_size()
+{
+ int n;
+ if (get_integer(&n) && !compatible_flag) {
+ 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(".fi");
+ 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(".nf");
+ curenv->ignore_next_eol = 1;
+ curenv->add_html_tag(".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(".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(".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(".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 be greater than 0");
+ 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;
+ curenv->add_html_tag(".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(".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;
+ 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) {
+ output_line(to_be_output, to_be_output_width);
+ hyphen_line_count = 0;
+ }
+ if (input_trap_count > 0) {
+ if (--input_trap_count == 0)
+ spring_trap(input_trap);
+ }
+}
+
+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, 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, 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, nn);
+ else {
+ hunits x = w;
+ nn = new hmotion_node(number_text_separation*line_number_digit_width,
+ 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, 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;
+ 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)
+ warning(WARN_BREAK, "can't break line");
+ return best_bp;
+ }
+ return 0;
+}
+
+void environment::hyphenate_line(int start_here)
+{
+ if (line == 0)
+ return;
+ 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;
+ int i = 0;
+ do {
+ ++i;
+ 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;
+ while (tem != end) {
+ sl = tem->get_hyphen_list(sl);
+ 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);
+ 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;
+ 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_eol - add an end of line tag if appropriate.
+ */
+
+void environment::add_html_tag_eol()
+{
+ if (is_html) {
+ if (ignore_next_eol > 0)
+ ignore_next_eol--;
+ else if (need_eol > 0) {
+ need_eol--;
+ add_html_tag("eol");
+ }
+ else if (!fill && emitted_node) {
+ add_html_tag("eol");
+ emitted_node = 0;
+ }
+ }
+}
+
+/*
+ * add_html_tag - emits a special html-tag: to help post-grohtml understand
+ * the key troff commands
+ */
+
+void environment::add_html_tag(const char *name)
+{
+ 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 (!illegal_input_char((unsigned char)*p))
+ m->append(*p);
+ add_node(new special_node(*m));
+ }
+}
+
+/*
+ * 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(const char *name, int i)
+{
+ if (is_html) {
+ if (strcmp(name, ".ce") == 0) {
+ if (i == 0)
+ need_eol = 0;
+ else {
+ need_eol = i;
+ ignore_next_eol = 1; // since the .ce creates an eol
+ }
+ }
+
+ /*
+ * 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 (!illegal_input_char((unsigned char)*p))
+ m->append(*p);
+ m->append(' ');
+ m->append_int(i);
+ // output_pending_lines();
+ output(new special_node(*m), !fill, 0, 0, 0);
+ // output_pending_lines();
+ }
+}
+
+/*
+ * add_html_tag_tabs - emits the tab settings for post-grohtml
+ */
+
+void environment::add_html_tag_tabs()
+{
+ 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(d.to_units());
+ break;
+ case TAB_CENTER:
+ m->append_str(" C ");
+ m->append_int(d.to_units());
+ break;
+ case TAB_RIGHT:
+ m->append_str(" R ");
+ m->append_int(d.to_units());
+ break;
+ case TAB_NONE:
+ break;
+ }
+ } while ((t != TAB_NONE) && (l < get_line_length()));
+ output_pending_lines();
+ output(new special_node(*m), !fill, 0, 0, 0);
+ output_pending_lines();
+ }
+}
+
+void environment::do_break()
+{
+ if (curdiv == topdiv && topdiv->before_first_page) {
+ topdiv->begin_page();
+ return;
+ }
+ if (current_tab)
+ wrap_up_tab();
+ if (line) {
+ line = new space_node(H0, line); // this is so that hyphenation works
+ space_total++;
+ possibly_break_line();
+ }
+ 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 break_request()
+{
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (break_flag) {
+ curenv->do_break();
+ curenv->add_html_tag(".br");
+ }
+ tok.next();
+}
+
+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;
+ 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], 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], 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 input_trap()
+{
+ curenv->input_trap_count = 0;
+ 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();
+}
+
+/* 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 lastpos = 0;
+ tab *tem;
+ for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
+ lastpos = tem->pos;
+ if (tem) {
+ *distance = tem->pos - curpos;
+ 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;
+ 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();
+ skip_line();
+}
+
+const char *environment::get_tabs()
+{
+ return tabs.to_string();
+}
+
+#if 0
+tab_stops saved_tabs;
+
+void tabs_save()
+{
+ saved_tabs = curenv->tabs;
+ skip_line();
+}
+
+void tabs_restore()
+{
+ curenv->tabs = saved_tabs;
+ skip_line();
+}
+#endif
+
+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);
+}
+
+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, next);
+ node *n = new hline_node(d, leader_node, next);
+ leader_node = 0;
+ return n;
+}
+
+void environment::handle_tab(int is_leader)
+{
+ hunits d;
+ 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);
+ switch (t) {
+ case TAB_NONE:
+ return;
+ case TAB_LEFT:
+ add_html_tag("tab left");
+ add_node(make_tab_node(d));
+ return;
+ case TAB_RIGHT:
+ case TAB_CENTER:
+ add_html_tag("tab center");
+ tab_width = 0;
+ tab_distance = d;
+ tab_contents = 0;
+ current_tab = t;
+ tab_field_spaces = 0;
+ return;
+ default:
+ assert(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, tab_contents);
+ tab_field_spaces++;
+ }
+ else {
+ if (line == 0)
+ start_line();
+ line = new space_node(H0, 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_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("it", input_trap);
+ init_request("ad", adjust);
+ init_request("na", no_adjust);
+ init_request("ev", environment_switch);
+ init_request("evc", environment_copy);
+ init_request("lt", title_length);
+ init_request("ps", point_size);
+ init_request("ft", font_change);
+ init_request("fam", family_change);
+ init_request("ss", space_size);
+ init_request("fi", fill);
+ init_request("nf", no_fill);
+ init_request("ce", center);
+ init_request("rj", right_justify);
+ init_request("vs", vertical_spacing);
+ init_request("ls", line_spacing);
+ init_request("ll", line_length);
+ init_request("in", indent);
+ init_request("ti", temporary_indent);
+ init_request("ul", underline);
+ init_request("cu", continuous_underline);
+ init_request("cc", control_char);
+ init_request("c2", no_break_control_char);
+ init_request("br", break_request);
+ init_request("tl", title);
+ init_request("ta", set_tabs);
+ init_request("linetabs", line_tabs_request);
+ init_request("fc", field_characters);
+ init_request("mc", margin_character);
+ init_request("nn", no_number);
+ init_request("nm", number_lines);
+ init_request("tc", tab_character);
+ init_request("lc", leader_character);
+ init_request("hy", hyphenate_request);
+ init_request("hc", hyphen_char);
+ init_request("nh", no_hyphenate);
+ init_request("hlm", hyphen_line_max_request);
+#ifdef WIDOW_CONTROL
+ init_request("wdc", widow_control_request);
+#endif /* WIDOW_CONTROL */
+#if 0
+ init_request("tas", tabs_save);
+ init_request("tar", tabs_restore);
+#endif
+ init_request("hys", hyphenation_space_request);
+ init_request("hym", hyphenation_margin_request);
+ init_request("pvs", post_vertical_spacing);
+ init_int_env_reg(".f", get_font);
+ init_int_env_reg(".b", get_bold);
+ 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(".j", get_adjust_mode);
+ init_hunits_env_reg(".k", get_text_length);
+ init_hunits_env_reg(".l", get_line_length);
+ init_hunits_env_reg(".ll", get_saved_line_length);
+ init_int_env_reg(".L", get_line_spacing);
+ init_hunits_env_reg(".n", get_prev_text_length);
+ init_string_env_reg(".s", get_point_size_string);
+ init_string_env_reg(".sr", get_requested_point_size_string);
+ init_int_env_reg(".ps", get_point_size);
+ init_int_env_reg(".psr", get_requested_point_size);
+ init_int_env_reg(".u", get_fill);
+ init_vunits_env_reg(".v", get_vertical_spacing);
+ init_vunits_env_reg(".pvs", get_post_vertical_spacing);
+ init_hunits_env_reg(".w", get_prev_char_width);
+ init_int_env_reg(".ss", get_space_size);
+ init_int_env_reg(".sss", get_sentence_space_size);
+ init_string_env_reg(".fam", get_font_family_string);
+ init_string_env_reg(".ev", get_name_string);
+ init_int_env_reg(".hy", get_hyphenation_flags);
+ init_int_env_reg(".hlm", get_hyphen_line_max);
+ init_int_env_reg(".hlc", get_hyphen_line_count);
+ init_hunits_env_reg(".lt", get_title_length);
+ init_string_env_reg(".tabs", get_tabs);
+ init_int_env_reg(".linetabs", get_line_tabs);
+ init_hunits_env_reg(".csk", get_prev_char_skew);
+ init_vunits_env_reg(".cht", get_prev_char_height);
+ init_vunits_env_reg(".cdp", get_prev_char_depth);
+ init_int_env_reg(".ce", get_center_lines);
+ init_int_env_reg(".rj", get_right_justify_lines);
+ init_hunits_env_reg(".hys", get_hyphenation_space);
+ init_hunits_env_reg(".hym", get_hyphenation_margin);
+ number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
+ number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
+ number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
+ number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
+ 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("ssc", new variable_reg(&ssc_reg_contents));
+ number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
+ number_reg_dictionary.define("hp", new horizontal_place_reg);
+}
+
+// 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);
+public:
+ hyphen_trie() {}
+ ~hyphen_trie() {}
+ void hyphenate(const char *word, int len, int *hyphens);
+ void read_patterns_file(const char *name);
+};
+
+
+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 = 1024;
+
+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::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;
+ }
+}
+
+void hyphen_trie::read_patterns_file(const char *name)
+{
+ clear();
+ char buf[WORD_MAX];
+ 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 = getc(fp);
+ for (;;) {
+ for (;;) {
+ if (c == '%') {
+ do {
+ c = getc(fp);
+ } while (c != EOF && c != '\n');
+ }
+ if (c == EOF || !csspace(c))
+ break;
+ c = getc(fp);
+ }
+ if (c == EOF)
+ break;
+ int i = 0;
+ num[0] = 0;
+ do {
+ if (csdigit(c))
+ num[i] = c - '0';
+ else {
+ buf[i++] = c;
+ num[i] = 0;
+ }
+ c = getc(fp);
+ } while (i < WORD_MAX && c != EOF && !csspace(c) && c != '%');
+ insert_pattern(buf, i, num);
+ }
+ 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 hyphenation_patterns_file()
+{
+ 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());
+ }
+ skip_line();
+}
+
+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);
+ number_reg_dictionary.define(".hla", new hyphenation_language_reg);
+}
diff --git a/contrib/groff/src/roff/troff/env.h b/contrib/groff/src/roff/troff/env.h
new file mode 100644
index 000000000000..256db512f802
--- /dev/null
+++ b/contrib/groff/src/roff/troff/env.h
@@ -0,0 +1,350 @@
+// -*- 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. */
+
+struct size_range {
+ int min;
+ int max;
+};
+
+class font_size {
+ static size_range *size_table;
+ static int nranges;
+ int p;
+public:
+ font_size();
+ font_size(int points);
+ int to_points();
+ int to_scaled_points();
+ int to_units();
+ int operator==(font_size);
+ int operator!=(font_size);
+ static void init_size_table(int *sizes);
+};
+
+inline font_size::font_size() : p(0)
+{
+}
+
+inline int font_size::operator==(font_size fs)
+{
+ return p == fs.p;
+}
+
+inline int font_size::operator!=(font_size fs)
+{
+ return p != fs.p;
+}
+
+inline int font_size::to_scaled_points()
+{
+ return p;
+}
+
+inline int font_size::to_points()
+{
+ return p/sizescale;
+}
+
+struct environment;
+
+hunits env_digit_width(environment *);
+hunits env_space_width(environment *);
+hunits env_sentence_space_width(environment *);
+hunits env_narrow_space_width(environment *);
+hunits env_half_narrow_space_width(environment *);
+
+struct tab;
+
+enum tab_type { TAB_NONE, TAB_LEFT, TAB_CENTER, TAB_RIGHT };
+
+class tab_stops {
+ tab *initial_list;
+ tab *repeated_list;
+public:
+ tab_stops();
+ tab_stops(hunits distance, tab_type type);
+ tab_stops(const tab_stops &);
+ ~tab_stops();
+ void operator=(const tab_stops &);
+ tab_type distance_to_next_tab(hunits pos, hunits *distance);
+ void clear();
+ void add_tab(hunits pos, tab_type type, int repeated);
+ const char *to_string();
+};
+
+const unsigned MARGIN_CHARACTER_ON = 1;
+const unsigned MARGIN_CHARACTER_NEXT = 2;
+
+struct charinfo;
+struct node;
+struct breakpoint;
+struct font_family;
+struct pending_output_line;
+
+class environment {
+ int dummy; // dummy environment used for \w
+ hunits prev_line_length;
+ hunits line_length;
+ hunits prev_title_length;
+ hunits title_length;
+ font_size prev_size;
+ font_size size;
+ int requested_size;
+ int prev_requested_size;
+ int char_height;
+ int char_slant;
+ int prev_fontno;
+ int fontno;
+ font_family *prev_family;
+ font_family *family;
+ int space_size; // in 36ths of an em
+ int sentence_space_size; // same but for spaces at the end of sentences
+ int adjust_mode;
+ int fill;
+ int interrupted;
+ int prev_line_interrupted;
+ int center_lines;
+ int right_justify_lines;
+ vunits prev_vertical_spacing;
+ vunits vertical_spacing;
+ vunits prev_post_vertical_spacing;
+ vunits post_vertical_spacing;
+ int prev_line_spacing;
+ int line_spacing;
+ hunits prev_indent;
+ hunits indent;
+ hunits temporary_indent;
+ int have_temporary_indent;
+ hunits saved_indent;
+ hunits target_text_length;
+ int pre_underline_fontno;
+ int underline_lines;
+ int underline_spaces;
+ symbol input_trap;
+ int input_trap_count;
+ node *line; // in reverse order
+ hunits prev_text_length;
+ hunits width_total;
+ int space_total;
+ hunits input_line_start;
+ tab_stops tabs;
+ node *tab_contents;
+ hunits tab_width;
+ hunits tab_distance;
+ int line_tabs;
+ tab_type current_tab;
+ node *leader_node;
+ charinfo *tab_char;
+ charinfo *leader_char;
+ int current_field; // is there a current field?
+ hunits field_distance;
+ hunits pre_field_width;
+ int field_spaces;
+ int tab_field_spaces;
+ int tab_precedes_field;
+ int discarding;
+ int spread_flag; // set by \p
+ unsigned margin_character_flags;
+ node *margin_character_node;
+ hunits margin_character_distance;
+ node *numbering_nodes;
+ hunits line_number_digit_width;
+ int number_text_separation; // in digit spaces
+ int line_number_indent; // in digit spaces
+ int line_number_multiple;
+ int no_number_count;
+ unsigned hyphenation_flags;
+ int hyphen_line_count;
+ int hyphen_line_max;
+ hunits hyphenation_space;
+ hunits hyphenation_margin;
+ int composite; // used for construction of composite char?
+ pending_output_line *pending_lines;
+#ifdef WIDOW_CONTROL
+ int widow_control;
+#endif /* WIDOW_CONTROL */
+ int need_eol;
+ int ignore_next_eol;
+ int emitted_node; // have we emitted a node since the last html eol tag?
+
+ tab_type distance_to_next_tab(hunits *);
+ void start_line();
+ void output_line(node *, hunits);
+ void output(node *nd, int retain_size, vunits vs, vunits post_vs,
+ hunits width);
+ void output_title(node *nd, int retain_size, vunits vs, vunits post_vs,
+ hunits width);
+#ifdef WIDOW_CONTROL
+ void mark_last_line();
+#endif /* WIDOW_CONTROL */
+ breakpoint *choose_breakpoint();
+ void hyphenate_line(int start_here = 0);
+ void start_field();
+ void wrap_up_field();
+ void add_padding();
+ node *make_tab_node(hunits d, node *next = 0);
+ node *get_prev_char();
+public:
+ const symbol name;
+ unsigned char control_char;
+ unsigned char no_break_control_char;
+ charinfo *hyphen_indicator_char;
+
+ environment(symbol);
+ environment(const environment *); // for temporary environment
+ ~environment();
+ void copy(const environment *);
+ int is_dummy() { return dummy; }
+ int is_empty();
+ int is_composite() { return composite; }
+ void set_composite() { composite = 1; }
+ vunits get_vertical_spacing(); // .v
+ vunits get_post_vertical_spacing(); // .pvs
+ int get_line_spacing(); // .L
+ vunits total_post_vertical_spacing();
+ int get_point_size() { return size.to_scaled_points(); }
+ font_size get_font_size() { return size; }
+ int get_size() { return size.to_units(); }
+ int get_requested_point_size() { return requested_size; }
+ int get_char_height() { return char_height; }
+ int get_char_slant() { return char_slant; }
+ hunits get_digit_width();
+ int get_font() { return fontno; }; // .f
+ font_family *get_family() { return family; }
+ int get_bold(); // .b
+ int get_adjust_mode(); // .j
+ int get_fill(); // .u
+ hunits get_indent(); // .i
+ hunits get_temporary_indent();
+ hunits get_line_length(); // .l
+ hunits get_saved_line_length(); // .ll
+ hunits get_saved_indent(); // .in
+ hunits get_title_length();
+ hunits get_prev_char_width(); // .w
+ hunits get_prev_char_skew();
+ vunits get_prev_char_height();
+ vunits get_prev_char_depth();
+ hunits get_text_length(); // .k
+ hunits get_prev_text_length(); // .n
+ hunits get_space_width() { return env_space_width(this); }
+ int get_space_size() { return space_size; } // in ems/36
+ int get_sentence_space_size() { return sentence_space_size; }
+ hunits get_narrow_space_width() { return env_narrow_space_width(this); }
+ hunits get_half_narrow_space_width()
+ { return env_half_narrow_space_width(this); }
+ hunits get_input_line_position();
+ const char *get_tabs();
+ int get_line_tabs();
+ int get_hyphenation_flags();
+ int get_hyphen_line_max();
+ int get_hyphen_line_count();
+ hunits get_hyphenation_space();
+ hunits get_hyphenation_margin();
+ int get_center_lines();
+ int get_right_justify_lines();
+ int get_prev_line_interrupted() { return prev_line_interrupted; }
+ node *make_char_node(charinfo *);
+ node *extract_output_line();
+ void width_registers();
+ void wrap_up_tab();
+ void set_font(int);
+ void set_font(symbol);
+ void set_family(symbol);
+ void set_size(int);
+ void set_char_height(int);
+ void set_char_slant(int);
+ void set_input_line_position(hunits); // used by \n(hp
+ void interrupt();
+ void spread() { spread_flag = 1; }
+ void possibly_break_line(int start_here = 0, int forced = 0);
+ void do_break(); // .br
+ void final_break();
+ void add_html_tag_eol();
+ void add_html_tag(const char *);
+ void add_html_tag(const char *, int);
+ void add_html_tag_tabs();
+ void newline();
+ void handle_tab(int is_leader = 0); // do a tab or leader
+ void add_node(node *);
+ void add_char(charinfo *);
+ void add_hyphen_indicator();
+ void add_italic_correction();
+ void space();
+ void space(hunits, hunits);
+ void space_newline();
+ const char *get_font_family_string();
+ const char *get_name_string();
+ const char *get_point_size_string();
+ const char *get_requested_point_size_string();
+ void output_pending_lines();
+
+ friend void title_length();
+ friend void space_size();
+ friend void fill();
+ friend void no_fill();
+ friend void adjust();
+ friend void no_adjust();
+ friend void center();
+ friend void right_justify();
+ friend void vertical_spacing();
+ friend void post_vertical_spacing();
+ friend void line_spacing();
+ friend void line_length();
+ friend void indent();
+ friend void temporary_indent();
+ friend void do_underline(int);
+ friend void input_trap();
+ friend void set_tabs();
+ friend void margin_character();
+ friend void no_number();
+ friend void number_lines();
+ friend void leader_character();
+ friend void tab_character();
+ friend void hyphenate_request();
+ friend void no_hyphenate();
+ friend void hyphen_line_max_request();
+ friend void hyphenation_space_request();
+ friend void hyphenation_margin_request();
+ friend void line_width();
+#if 0
+ friend void tabs_save();
+ friend void tabs_restore();
+#endif
+ friend void line_tabs_request();
+ friend void title();
+#ifdef WIDOW_CONTROL
+ friend void widow_control_request();
+#endif /* WIDOW_CONTROL */
+
+ friend void do_divert(int append, int boxing);
+};
+
+extern environment *curenv;
+extern void pop_env();
+extern void push_env(int);
+
+void init_environments();
+void read_hyphen_file(const char *name);
+
+extern int break_flag;
+extern int compatible_flag;
+extern symbol default_family;
+extern int translate_space_to_dummy;
diff --git a/contrib/groff/src/roff/troff/hvunits.h b/contrib/groff/src/roff/troff/hvunits.h
new file mode 100644
index 000000000000..8efb5ab35972
--- /dev/null
+++ b/contrib/groff/src/roff/troff/hvunits.h
@@ -0,0 +1,340 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+
+class vunits {
+ int n;
+public:
+ vunits();
+ vunits(units);
+ units to_units();
+ int is_zero();
+ vunits& operator+=(const vunits&);
+ vunits& operator-=(const vunits&);
+ friend inline vunits scale(vunits n, units x, units y); // scale n by x/y
+ friend inline vunits scale(vunits n, vunits x, vunits y);
+ friend inline vunits operator +(const vunits&, const vunits&);
+ friend inline vunits operator -(const vunits&, const vunits&);
+ friend inline vunits operator -(const vunits&);
+ friend inline int operator /(const vunits&, const vunits&);
+ friend inline vunits operator /(const vunits&, int);
+ friend inline vunits operator *(const vunits&, int);
+ friend inline vunits operator *(int, const vunits&);
+ friend inline int operator <(const vunits&, const vunits&);
+ friend inline int operator >(const vunits&, const vunits&);
+ friend inline int operator <=(const vunits&, const vunits&);
+ friend inline int operator >=(const vunits&, const vunits&);
+ friend inline int operator ==(const vunits&, const vunits&);
+ friend inline int operator !=(const vunits&, const vunits&);
+};
+
+extern vunits V0;
+
+
+class hunits {
+ int n;
+public:
+ hunits();
+ hunits(units);
+ units to_units();
+ int is_zero();
+ hunits& operator+=(const hunits&);
+ hunits& operator-=(const hunits&);
+ friend inline hunits scale(hunits n, units x, units y); // scale n by x/y
+ friend inline hunits scale(hunits n, double x);
+ friend inline hunits operator +(const hunits&, const hunits&);
+ friend inline hunits operator -(const hunits&, const hunits&);
+ friend inline hunits operator -(const hunits&);
+ friend inline int operator /(const hunits&, const hunits&);
+ friend inline hunits operator /(const hunits&, int);
+ friend inline hunits operator *(const hunits&, int);
+ friend inline hunits operator *(int, const hunits&);
+ friend inline int operator <(const hunits&, const hunits&);
+ friend inline int operator >(const hunits&, const hunits&);
+ friend inline int operator <=(const hunits&, const hunits&);
+ friend inline int operator >=(const hunits&, const hunits&);
+ friend inline int operator ==(const hunits&, const hunits&);
+ friend inline int operator !=(const hunits&, const hunits&);
+};
+
+extern hunits H0;
+
+extern int get_vunits(vunits *, unsigned char si);
+extern int get_hunits(hunits *, unsigned char si);
+extern int get_vunits(vunits *, unsigned char si, vunits prev_value);
+extern int get_hunits(hunits *, unsigned char si, hunits prev_value);
+
+inline vunits:: vunits() : n(0)
+{
+}
+
+inline units vunits::to_units()
+{
+ return n*vresolution;
+}
+
+inline int vunits::is_zero()
+{
+ return n == 0;
+}
+
+inline vunits operator +(const vunits & x, const vunits & y)
+{
+ vunits r;
+ r = x;
+ r.n += y.n;
+ return r;
+}
+
+inline vunits operator -(const vunits & x, const vunits & y)
+{
+ vunits r;
+ r = x;
+ r.n -= y.n;
+ return r;
+}
+
+inline vunits operator -(const vunits & x)
+{
+ vunits r;
+ r.n = -x.n;
+ return r;
+}
+
+inline int operator /(const vunits & x, const vunits & y)
+{
+ return x.n/y.n;
+}
+
+inline vunits operator /(const vunits & x, int n)
+{
+ vunits r;
+ r = x;
+ r.n /= n;
+ return r;
+}
+
+inline vunits operator *(const vunits & x, int n)
+{
+ vunits r;
+ r = x;
+ r.n *= n;
+ return r;
+}
+
+inline vunits operator *(int n, const vunits & x)
+{
+ vunits r;
+ r = x;
+ r.n *= n;
+ return r;
+}
+
+inline int operator <(const vunits & x, const vunits & y)
+{
+ return x.n < y.n;
+}
+
+inline int operator >(const vunits & x, const vunits & y)
+{
+ return x.n > y.n;
+}
+
+inline int operator <=(const vunits & x, const vunits & y)
+{
+ return x.n <= y.n;
+}
+
+inline int operator >=(const vunits & x, const vunits & y)
+{
+ return x.n >= y.n;
+}
+
+inline int operator ==(const vunits & x, const vunits & y)
+{
+ return x.n == y.n;
+}
+
+inline int operator !=(const vunits & x, const vunits & y)
+{
+ return x.n != y.n;
+}
+
+
+inline vunits& vunits::operator+=(const vunits & x)
+{
+ n += x.n;
+ return *this;
+}
+
+inline vunits& vunits::operator-=(const vunits & x)
+{
+ n -= x.n;
+ return *this;
+}
+
+inline hunits:: hunits() : n(0)
+{
+}
+
+inline units hunits::to_units()
+{
+ return n*hresolution;
+}
+
+inline int hunits::is_zero()
+{
+ return n == 0;
+}
+
+inline hunits operator +(const hunits & x, const hunits & y)
+{
+ hunits r;
+ r = x;
+ r.n += y.n;
+ return r;
+}
+
+inline hunits operator -(const hunits & x, const hunits & y)
+{
+ hunits r;
+ r = x;
+ r.n -= y.n;
+ return r;
+}
+
+inline hunits operator -(const hunits & x)
+{
+ hunits r;
+ r = x;
+ r.n = -x.n;
+ return r;
+}
+
+inline int operator /(const hunits & x, const hunits & y)
+{
+ return x.n/y.n;
+}
+
+inline hunits operator /(const hunits & x, int n)
+{
+ hunits r;
+ r = x;
+ r.n /= n;
+ return r;
+}
+
+inline hunits operator *(const hunits & x, int n)
+{
+ hunits r;
+ r = x;
+ r.n *= n;
+ return r;
+}
+
+inline hunits operator *(int n, const hunits & x)
+{
+ hunits r;
+ r = x;
+ r.n *= n;
+ return r;
+}
+
+inline int operator <(const hunits & x, const hunits & y)
+{
+ return x.n < y.n;
+}
+
+inline int operator >(const hunits & x, const hunits & y)
+{
+ return x.n > y.n;
+}
+
+inline int operator <=(const hunits & x, const hunits & y)
+{
+ return x.n <= y.n;
+}
+
+inline int operator >=(const hunits & x, const hunits & y)
+{
+ return x.n >= y.n;
+}
+
+inline int operator ==(const hunits & x, const hunits & y)
+{
+ return x.n == y.n;
+}
+
+inline int operator !=(const hunits & x, const hunits & y)
+{
+ return x.n != y.n;
+}
+
+
+inline hunits& hunits::operator+=(const hunits & x)
+{
+ n += x.n;
+ return *this;
+}
+
+inline hunits& hunits::operator-=(const hunits & x)
+{
+ n -= x.n;
+ return *this;
+}
+
+inline hunits scale(hunits n, units x, units y)
+{
+ hunits r;
+ r.n = scale(n.n, x, y);
+ return r;
+}
+
+inline vunits scale(vunits n, units x, units y)
+{
+ vunits r;
+ r.n = scale(n.n, x, y);
+ return r;
+}
+
+inline vunits scale(vunits n, vunits x, vunits y)
+{
+ vunits r;
+ r.n = scale(n.n, x.n, y.n);
+ return r;
+}
+
+inline hunits scale(hunits n, double x)
+{
+ hunits r;
+ r.n = int(n.n*x);
+ return r;
+}
+
+inline units scale(units n, double x)
+{
+ return int(n*x);
+}
+
+inline units points_to_units(units n)
+{
+ return scale(n, units_per_inch, 72);
+}
+
diff --git a/contrib/groff/src/roff/troff/input.cc b/contrib/groff/src/roff/troff/input.cc
new file mode 100644
index 000000000000..982e5bd5520c
--- /dev/null
+++ b/contrib/groff/src/roff/troff/input.cc
@@ -0,0 +1,6891 @@
+// -*- 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 "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"
+
+// Needed for getpid().
+#include "posix.h"
+
+#include "nonposix.h"
+
+#ifdef NEED_DECLARATION_PUTENV
+extern "C" {
+ int putenv(const char *);
+}
+#endif /* NEED_DECLARATION_PUTENV */
+
+#ifdef ISATTY_MISSING
+#undef isatty
+#define isatty(n) (1)
+#else /* not ISATTY_MISSING */
+#ifndef isatty
+extern "C" {
+ int isatty(int);
+}
+#endif /* not isatty */
+#endif /* not ISATTY_MISSING */
+
+#define MACRO_PREFIX "tmac."
+#define MACRO_POSTFIX ".tmac"
+#define INITIAL_STARTUP_FILE "troffrc"
+#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
+
+#ifdef COLUMN
+void init_column_requests();
+#endif /* COLUMN */
+
+static node *read_draw_node();
+void handle_first_page_transition();
+static void push_token(const token &);
+void copy_file();
+#ifdef COLUMN
+void vjustify();
+#endif /* COLUMN */
+void transparent();
+void transparent_file();
+
+const char *program_name = 0;
+token tok;
+int break_flag = 0;
+static int backtrace_flag = 0;
+#ifndef POPEN_MISSING
+char *pipe_command = 0;
+#endif
+charinfo *charset_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;
+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 tcommand_flag = 0;
+int safer_flag = 1; // safer by default
+
+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);
+
+static symbol read_escape_name();
+static void interpolate_string(symbol);
+static void interpolate_macro(symbol);
+static void interpolate_number_format(symbol);
+static void interpolate_environment_variable(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 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 NULL; }
+ 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; }
+};
+
+input_iterator::input_iterator()
+: ptr(0), eptr(0)
+{
+}
+
+input_iterator::~input_iterator()
+{
+}
+
+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;
+ 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)
+{
+ 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;
+ popened = 0;
+ ptr = 0;
+ eptr = 0;
+ return 1;
+}
+
+int file_iterator::fill(node **)
+{
+ if (newline_flag) {
+ curenv->add_html_tag_eol();
+ 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 (illegal_input_char(c))
+ warning(WARN_INPUT, "illegal input character code %1", int(c));
+ else {
+ *p++ = c;
+ if (c == '\n') {
+ newline_flag = 1;
+ break;
+ }
+ }
+ }
+ if (p > buf) {
+ eptr = p;
+ return *ptr++;
+ }
+ else {
+ eptr = p;
+ return EOF;
+ }
+}
+
+int file_iterator::peek()
+{
+ int c = getc(fp);
+ while (illegal_input_char(c)) {
+ warning(WARN_INPUT, "illegal 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 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)
+{
+ return (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
+}
+
+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 != NULL; 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();
+}
+
+void backtrace_request()
+{
+ input_stack::backtrace_all();
+ fflush(stderr);
+ skip_line();
+}
+
+void next_file()
+{
+ symbol nm = get_long_name(0);
+ 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 c = get_copy(NULL);
+ switch (c) {
+ case EOF:
+ copy_mode_error("end of input in escape name");
+ return '\0';
+ default:
+ if (!illegal_input_char(c))
+ break;
+ // fall through
+ case '\n':
+ if (c == '\n')
+ input_stack::push(make_temp_iterator("\n"));
+ case ' ':
+ 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()
+{
+ int start_level = input_stack::get_level();
+ char abuf[ABUF_SIZE];
+ char *buf = abuf;
+ int buf_size = ABUF_SIZE;
+ int i = 0;
+ for (;;) {
+ int c = get_char_for_escape_name();
+ if (c == 0) {
+ if (buf != abuf)
+ a_delete buf;
+ return NULL_SYMBOL;
+ }
+ 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 (buf == abuf) {
+ if (i == 0) {
+ copy_mode_error("empty escape name");
+ return NULL_SYMBOL;
+ }
+ return symbol(abuf);
+ }
+ else {
+ symbol s(buf);
+ a_delete buf;
+ return s;
+ }
+}
+
+static symbol read_escape_name()
+{
+ 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();
+ 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(NULL);
+ while ((c = input_stack::get(NULL)) != '\n' && c != EOF)
+ ;
+ return c;
+ case '#': // Like \" but newline is ignored.
+ (void)input_stack::get(NULL);
+ while ((c = input_stack::get(NULL)) != '\n')
+ if (c == EOF)
+ return EOF;
+ break;
+ case '$':
+ {
+ (void)input_stack::get(NULL);
+ symbol s = read_escape_name();
+ if (!s.is_null())
+ interpolate_arg(s);
+ break;
+ }
+ case '*':
+ {
+ (void)input_stack::get(NULL);
+ symbol s = read_escape_name();
+ if (!s.is_null())
+ interpolate_string(s);
+ break;
+ }
+ case 'a':
+ (void)input_stack::get(NULL);
+ return '\001';
+ case 'e':
+ (void)input_stack::get(NULL);
+ return ESCAPE_e;
+ case 'E':
+ (void)input_stack::get(NULL);
+ return ESCAPE_E;
+ case 'n':
+ {
+ (void)input_stack::get(NULL);
+ int inc;
+ symbol s = read_increment_and_escape_name(&inc);
+ if (!s.is_null())
+ interpolate_number_reg(s, inc);
+ break;
+ }
+ case 'g':
+ {
+ (void)input_stack::get(NULL);
+ symbol s = read_escape_name();
+ if (!s.is_null())
+ interpolate_number_format(s);
+ break;
+ }
+ case 't':
+ (void)input_stack::get(NULL);
+ return '\t';
+ case 'V':
+ {
+ (void)input_stack::get(NULL);
+ symbol s = read_escape_name();
+ if (!s.is_null())
+ interpolate_environment_variable(s);
+ break;
+ }
+ case '\n':
+ (void)input_stack::get(NULL);
+ if (defining)
+ return ESCAPE_NEWLINE;
+ break;
+ case ' ':
+ (void)input_stack::get(NULL);
+ return ESCAPE_SPACE;
+ case '~':
+ (void)input_stack::get(NULL);
+ return ESCAPE_TILDE;
+ case ':':
+ (void)input_stack::get(NULL);
+ return ESCAPE_COLON;
+ case '|':
+ (void)input_stack::get(NULL);
+ return ESCAPE_BAR;
+ case '^':
+ (void)input_stack::get(NULL);
+ return ESCAPE_CIRCUMFLEX;
+ case '{':
+ (void)input_stack::get(NULL);
+ return ESCAPE_LEFT_BRACE;
+ case '}':
+ (void)input_stack::get(NULL);
+ return ESCAPE_RIGHT_BRACE;
+ case '`':
+ (void)input_stack::get(NULL);
+ return ESCAPE_LEFT_QUOTE;
+ case '\'':
+ (void)input_stack::get(NULL);
+ return ESCAPE_RIGHT_QUOTE;
+ case '-':
+ (void)input_stack::get(NULL);
+ return ESCAPE_HYPHEN;
+ case '_':
+ (void)input_stack::get(NULL);
+ return ESCAPE_UNDERSCORE;
+ case 'c':
+ (void)input_stack::get(NULL);
+ return ESCAPE_c;
+ case '!':
+ (void)input_stack::get(NULL);
+ return ESCAPE_BANG;
+ case '?':
+ (void)input_stack::get(NULL);
+ return ESCAPE_QUESTION;
+ case '&':
+ (void)input_stack::get(NULL);
+ return ESCAPE_AMPERSAND;
+ case ')':
+ (void)input_stack::get(NULL);
+ return ESCAPE_RIGHT_PARENTHESIS;
+ case '.':
+ (void)input_stack::get(NULL);
+ return c;
+ case '%':
+ (void)input_stack::get(NULL);
+ return ESCAPE_PERCENT;
+ default:
+ if (c == escape_char) {
+ (void)input_stack::get(NULL);
+ 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();
+static void do_register();
+
+static node *do_overstrike()
+{
+ token start;
+ overstrike_node *on = new overstrike_node;
+ start.next();
+ tok.next();
+ while (tok != start) {
+ if (tok.newline() || tok.eof()) {
+ warning(WARN_DELIM, "missing closing delimiter");
+ break;
+ }
+ charinfo *ci = tok.get_char(1);
+ if (ci) {
+ node *n = curenv->make_char_node(ci);
+ if (n)
+ on->overstrike(n);
+ }
+ tok.next();
+ }
+ return on;
+}
+
+static node *do_bracket()
+{
+ token start;
+ bracket_node *bn = new bracket_node;
+ start.next();
+ tok.next();
+ while (tok != start) {
+ 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;
+ }
+ charinfo *ci = tok.get_char(1);
+ if (ci) {
+ node *n = curenv->make_char_node(ci);
+ if (n)
+ bn->bracket(n);
+ }
+ tok.next();
+ }
+ 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");
+ 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");
+ 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");
+ break;
+ }
+ if (tok == start
+ && (compatible_flag || input_stack::get_level() == start_level))
+ break;
+ if (!tok.add_to_node_list(&rev))
+ error("illegal 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 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_NODE;
+ nd = new space_char_hmotion_node(curenv->get_space_width());
+ return;
+ case ESCAPE_TILDE:
+ ESCAPE_TILDE:
+ type = TOKEN_STRETCHABLE_SPACE;
+ return;
+ case ESCAPE_COLON:
+ ESCAPE_COLON:
+ type = TOKEN_NODE;
+ nd = new space_node(H0);
+ nd->freeze_space();
+ nd->is_escape_colon();
+ 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());
+ return;
+ case ESCAPE_CIRCUMFLEX:
+ ESCAPE_CIRCUMFLEX:
+ type = TOKEN_NODE;
+ nd = new hmotion_node(curenv->get_half_narrow_space_width());
+ return;
+ case ESCAPE_NEWLINE:
+ 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(NULL);
+ 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());
+ 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(NULL)) != '\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(NULL)) != '\n')
+ if (cc == EOF) {
+ type = TOKEN_EOF;
+ return;
+ }
+ break;
+ case '$':
+ {
+ symbol nm = read_escape_name();
+ if (!nm.is_null())
+ interpolate_arg(nm);
+ break;
+ }
+ case '*':
+ {
+ symbol nm = read_escape_name();
+ if (!nm.is_null())
+ interpolate_string(nm);
+ 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);
+ 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();
+ if (s.is_null())
+ break;
+ const char *p;
+ for (p = s.contents(); *p != '\0'; p++)
+ if (!csdigit(*p))
+ break;
+ if (*p)
+ curenv->set_font(s);
+ else
+ curenv->set_font(atoi(s.contents()));
+ break;
+ }
+ case 'g':
+ {
+ symbol s = read_escape_name();
+ if (!s.is_null())
+ interpolate_number_format(s);
+ break;
+ }
+ case 'h':
+ if (!get_delim_number(&x, 'm'))
+ break;
+ type = TOKEN_NODE;
+ nd = new hmotion_node(x);
+ return;
+ case 'H':
+ if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
+ curenv->set_char_height(x);
+ break;
+ case 'k':
+ nm = read_escape_name();
+ if (nm.is_null())
+ 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 'n':
+ {
+ int inc;
+ symbol nm = read_increment_and_escape_name(&inc);
+ if (!nm.is_null())
+ interpolate_number_reg(nm, 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();
+ 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());
+ return;
+ case 'R':
+ do_register();
+ break;
+ case 's':
+ if (read_size(&x))
+ curenv->set_size(x);
+ break;
+ case 'S':
+ if (get_delim_number(&x, 0))
+ curenv->set_char_slant(x);
+ 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);
+ return;
+ case 'v':
+ if (!get_delim_number(&x, 'v'))
+ break;
+ type = TOKEN_NODE;
+ nd = new vmotion_node(x);
+ return;
+ case 'V':
+ {
+ symbol nm = read_escape_name();
+ if (!nm.is_null())
+ interpolate_environment_variable(nm);
+ 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())
+ 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) {
+ nm = read_long_escape_name();
+ if (nm.is_null())
+ break;
+ 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_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_TAB:
+ return "a tab character";
+ case TOKEN_TRANSPARENT:
+ return "`\\!'";
+ case TOKEN_TRANSPARENT_DUMMY:
+ 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)
+{
+ while (tok.space())
+ tok.next();
+ 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;
+ }
+ }
+ if ((buf[i] = tok.ch()) == 0)
+ break;
+ i++;
+ tok.next();
+ }
+ if (i == 0) {
+ empty_name_warning(required);
+ return NULL_SYMBOL;
+ }
+ 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();
+ 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 saved_compatible_flag = compatible_flag;
+ compatible_flag = 0;
+ symbol nm = get_name();
+ if (nm.is_null())
+ skip_line();
+ else
+ interpolate_macro(nm);
+ compatible_flag = saved_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 (!illegal_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 *bolp)
+{
+ 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 *bolp)
+{
+ 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 &&
+ (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 && !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;
+ 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(curenv->get_space_width()
+ * nspaces));
+ 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;
+ break;
+ }
+ case token::TOKEN_END_TRAP:
+ {
+ if (trap_bol_stack.is_empty())
+ error("spurious end trap token detected!");
+ else
+ bol = trap_bol_stack.pop();
+
+ /* 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);
+ 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++;
+}
+
+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;
+ }
+ length = 0;
+ p = 0;
+}
+
+macro::macro(const macro &m)
+: p(m.p), filename(m.filename), lineno(m.lineno), length(m.length)
+{
+ 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;
+ length = m.length;
+ return *this;
+}
+
+void macro::append(unsigned char c)
+{
+ assert(c != 0);
+ if (p == 0)
+ p = new macro_header;
+ if (p->cl.length() != length) {
+ macro_header *tem = p->copy(length);
+ if (--(p->count) <= 0)
+ delete p;
+ p = tem;
+ }
+ p->cl.append(c);
+ ++length;
+}
+
+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() != length) {
+ macro_header *tem = p->copy(length);
+ if (--(p->count) <= 0)
+ delete p;
+ p = tem;
+ }
+ p->cl.append(0);
+ p->nl.append(n);
+ ++length;
+}
+
+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", length);
+}
+
+// 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;
+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();
+};
+
+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.length;
+ 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 (count <= 0)
+ return EOF;
+ 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 are interpolated using the .macro_name notation
+
+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.length != m2.length)
+ return 0;
+ string_iterator iter1(m1);
+ string_iterator iter2(m2);
+ int n = m1.length;
+ 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,
+ "`%1' not defined (probable missing space after `%2')",
+ nm.contents(), buf);
+ }
+ }
+ if (!warned) {
+ warning(WARN_MAC, "`%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);
+ }
+ }
+}
+
+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 length == 0;
+}
+
+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;
+ }
+}
+
+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(NULL);
+ while (c == ' ')
+ c = get_copy(NULL);
+ while (c != EOF && c != '\n' && c != ' ') {
+ if (!illegal_input_char(c)) {
+ if (reading_from_terminal)
+ fputc(c, stderr);
+ had_prompt = 1;
+ }
+ c = get_copy(NULL);
+ }
+ 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 (illegal_input_char(c))
+ warning(WARN_INPUT, "illegal 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();
+}
+
+void do_define_string(int append)
+{
+ 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 (append && mm)
+ mac = *mm;
+ 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);
+ }
+ *mm = mac;
+ tok.next();
+}
+
+void define_string()
+{
+ do_define_string(0);
+}
+
+void append_string()
+{
+ do_define_string(1);
+}
+
+void define_character()
+{
+ node *n;
+ int c;
+ tok.skip();
+ charinfo *ci = tok.get_char(1);
+ if (ci == 0) {
+ skip_line();
+ return;
+ }
+ 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->set_macro(m);
+ if (m)
+ delete m;
+ tok.next();
+}
+
+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 using \\*");
+ else {
+ string_iterator *si = new string_iterator(*m, "string", nm);
+ input_stack::push(si);
+ }
+}
+
+/* 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(".");
+
+enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
+
+void do_define_macro(define_mode mode, int indirect)
+{
+ symbol nm, term;
+ if (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;
+ 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;
+ // see if it matches term
+ int i;
+ for (i = 0; s[i] != 0; i++) {
+ d = get_copy(&n);
+ if ((unsigned char)s[i] != d)
+ break;
+ }
+ 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);
+ }
+ *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, 0);
+}
+
+void define_indirect_macro()
+{
+ do_define_macro(DEFINE_NORMAL, 1);
+}
+
+void append_macro()
+{
+ do_define_macro(DEFINE_APPEND, 0);
+}
+
+void ignore()
+{
+ ignoring = 1;
+ do_define_macro(DEFINE_IGNORE, 0);
+ 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, "`%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->length == 0)
+ error("cannot chop empty macro");
+ else
+ m->length -= 1;
+ }
+ skip_line();
+}
+
+void substring_macro()
+{
+ int start;
+ symbol s = get_name(1);
+ if (!s.is_null() && get_integer(&start)) {
+ request_or_macro *p = lookup_request(s);
+ macro *m = p->to_macro();
+ if (!m)
+ error("cannot substring request");
+ else {
+ if (start <= 0)
+ start += m->length;
+ else
+ start--;
+ int end = 0;
+ if (!has_arg() || get_integer(&end)) {
+ if (end <= 0)
+ end += m->length;
+ else
+ end--;
+ if (start > end) {
+ int tem = start;
+ start = end;
+ end = tem;
+ }
+ if (start >= m->length || start == end) {
+ m->length = 0;
+ if (m->p) {
+ if (--(m->p->count) <= 0)
+ delete m->p;
+ m->p = 0;
+ }
+ }
+ else if (start == 0)
+ m->length = end;
+ else {
+ string_iterator iter(*m);
+ int i;
+ for (i = 0; i < start; i++)
+ if (iter.get(0) == EOF)
+ break;
+ macro mac;
+ for (; i < end; i++) {
+ node *nd;
+ int c = iter.get(&nd);
+ if (c == EOF)
+ break;
+ if (c == 0)
+ mac.append(nd);
+ else
+ mac.append((unsigned char)c);
+ }
+ *m = mac;
+ }
+ }
+ }
+ }
+ skip_line();
+}
+
+void length_macro()
+{
+ 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);
+ }
+ tok.next();
+ reg *r = (reg*)number_reg_dictionary.lookup(ret);
+ if (r)
+ r->set_value(len);
+ else
+ set_number_reg(ret, len);
+}
+
+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();
+ if (start.delimiter(1)) {
+ tok.next();
+ if (get_number(n, si)) {
+ if (tok.dummy() || tok.transparent_dummy())
+ tok.next();
+ if (start != tok) {
+ *cp = tok.get_char(1);
+ tok.next();
+ }
+ if (start != tok)
+ warning(WARN_DELIM, "closing delimiter does not match");
+ return 1;
+ }
+ }
+ 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:
+ *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);
+ }
+ 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;
+}
+
+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 {
+ error("%1 is illegal 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);
+}
+
+node *do_suppress()
+{
+ tok.next();
+ int c = tok.ch();
+ switch (c) {
+ case '0':
+ if (begin_level == 1)
+ return new suppress_node(0, 0);
+ break;
+ case '1':
+ if (begin_level == 1)
+ return new suppress_node(1, 0);
+ break;
+ case '2':
+ if (begin_level == 1)
+ return new suppress_node(1, 1);
+ break;
+ case '3':
+ begin_level++;
+ break;
+ case '4':
+ begin_level--;
+ break;
+ case '5': {
+ symbol filename = get_delim_name();
+ if (begin_level == 1)
+ return new suppress_node(filename, 'i');
+ return 0;
+ break;
+ }
+ default:
+ error("`%1' is an invalid argument to \\O", char(c));
+ }
+ return 0;
+}
+
+void special_node::tprint(troff_output_file *out)
+{
+ tprint_start(out);
+ string_iterator iter(mac);
+ for (;;) {
+ int c = iter.get(NULL);
+ 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(NULL);
+ 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(NULL)) {
+ case '{':
+ ++level;
+ break;
+ case '}':
+ --level;
+ break;
+ case '"':
+ while ((c = input_stack::get(NULL)) != '\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 == '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;
+ 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();
+ }
+}
+
+/*
+ * begin - if this is the outermost html_begin request then execute the
+ * rest of the line, else skip line
+ */
+
+void begin()
+{
+ begin_level++;
+ if (begin_level == 1)
+ begin_alternative();
+ else
+ skip_alternative();
+}
+
+/*
+ * end - if this is the outermost html_end request then execute the
+ * rest of the line, else skip line
+ */
+
+void end()
+{
+ begin_level--;
+ if (begin_level == 0)
+ begin_alternative();
+ else
+ skip_alternative();
+ if (begin_level < 0)
+ begin_level = 0;
+}
+
+/*
+ * image - implements the directive `.image {l|r|c|i} filename'
+ * which places the filename into a node which is later
+ * written out
+ *
+ * . either as a special in the form of an image tag for -Thtml
+ * . or as an image region definition for all other devices
+ *
+ */
+
+void image()
+{
+ if (has_arg()) {
+ char position = tok.ch();
+ if (!(position == 'l'
+ || position == 'r'
+ || position == 'c'
+ || position == 'i')) {
+ error("l, r, c, or i expected (got %1)", tok.description());
+ position = 'c';
+ }
+ tok.next();
+ symbol filename = get_long_name(1);
+ if (!filename.is_null())
+ curenv->add_node(new suppress_node(filename, position));
+ }
+ skip_line();
+}
+
+static int while_depth = 0;
+static int while_break_flag = 0;
+
+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(NULL) != 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(NULL) != EOF)
+ ;
+ tok.next();
+ }
+}
+
+void while_continue_request()
+{
+ if (!while_depth) {
+ error("no while loop");
+ skip_line();
+ }
+ else {
+ while (input_stack::get(NULL) != 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(NULL)) == ' ' || c == '\t')
+ ;
+ int buf_size = 24;
+ char *buf = new char[buf_size];
+ int buf_used = 0;
+ for (; c != '\n' && c != EOF; c = get_copy(NULL)) {
+ 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.cc
+
+#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("illegal 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;
+ default:
+ if (illegal_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 (illegal_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(NULL);
+ if (string_like && c == '"') {
+ c = get_copy(NULL);
+ break;
+ }
+ if (c != ' ' && c != '\t')
+ break;
+ }
+ for (; c != '\n' && c != EOF; c = get_copy(NULL))
+ 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();
+}
+
+void write_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;
+ }
+ int c;
+ while ((c = get_copy(NULL)) == ' ')
+ ;
+ if (c == '"')
+ c = get_copy(NULL);
+ for (; c != '\n' && c != EOF; c = get_copy(NULL))
+ fputs(asciify(c), fp);
+ fputc('\n', fp);
+ fflush(fp);
+ tok.next();
+}
+
+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();
+}
+
+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("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
+ get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
+ page_character = charset_table['%'];
+}
+
+static void do_translate(int translate_transparent)
+{
+ 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);
+ else
+ ci1->set_translation(ci2, translate_transparent);
+ }
+ tok.next();
+ }
+ skip_line();
+}
+
+void translate()
+{
+ do_translate(1);
+}
+
+void translate_no_transparent()
+{
+ do_translate(0);
+}
+
+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);
+ tok.next();
+ tok.skip();
+ }
+ 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());
+ 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());
+ break;
+ case TOKEN_TRANSPARENT_DUMMY:
+ n = new transparent_dummy_node;
+ 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()));
+ 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()));
+ 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;
+ 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(NULL))
+ 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 (!illegal_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 {
+ if ((pipe_command = read_string()) == 0)
+ error("can't pipe to empty command");
+ }
+#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 (illegal_input_char(c))
+ warning(WARN_INPUT, "illegal 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(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(NULL) != 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 (!illegal_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) {}
+};
+
+static void prepend_string(const char *s, string_list **p)
+{
+ string_list *l = new string_list(s);
+ l->next = *p;
+ *p = l;
+}
+
+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 -abivzCERU -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' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((c = getopt_long(argc, argv, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
+ long_options, NULL))
+ != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *Version_string;
+ 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;
+ 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();
+ 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;
+ 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();
+ init_markup_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("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(int miny)
+{
+ // fprintf(stderr, "reset_output_registers\n");
+ 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_markup_requests()
+{
+ init_request("begin", begin);
+ init_request("end", end);
+ init_request("image", image);
+}
+
+void init_input_requests()
+{
+ init_request("ds", define_string);
+ init_request("as", append_string);
+ init_request("de", define_macro);
+ init_request("dei", define_indirect_macro);
+ init_request("am", append_macro);
+ init_request("ig", ignore);
+ init_request("rm", remove_macro);
+ init_request("rn", rename_macro);
+ init_request("nop", nop_request);
+ init_request("if", if_request);
+ init_request("ie", if_else_request);
+ init_request("el", else_request);
+ init_request("so", source);
+ init_request("nx", next_file);
+ init_request("pm", print_macros);
+ init_request("eo", escape_off);
+ init_request("ec", set_escape_char);
+ init_request("ecs", save_escape_char);
+ init_request("ecr", restore_escape_char);
+ init_request("pc", set_page_character);
+ init_request("tm", terminal);
+ init_request("tm1", terminal1);
+ init_request("tmc", terminal_continue);
+ init_request("ex", exit_request);
+ init_request("return", return_macro_request);
+ init_request("em", end_macro);
+ init_request("blm", blank_line_macro);
+ init_request("tr", translate);
+ init_request("trnt", translate_no_transparent);
+ init_request("ab", abort_request);
+ init_request("pi", pipe_output);
+ init_request("cf", copy_file);
+ init_request("sy", system_request);
+ init_request("lf", line_file);
+ init_request("cflags", char_flags);
+ init_request("shift", shift);
+ init_request("rd", read_request);
+ init_request("cp", compatible);
+ init_request("char", define_character);
+ init_request("rchar", remove_character);
+ init_request("hcode", hyphenation_code);
+ init_request("while", while_request);
+ init_request("break", while_break_request);
+ init_request("continue", while_continue_request);
+ init_request("als", alias_macro);
+ init_request("backtrace", backtrace_request);
+ init_request("chop", chop_macro);
+ init_request("substring", substring_macro);
+ init_request("length", length_macro);
+ init_request("asciify", asciify_macro);
+ init_request("unformat", unformat_macro);
+ init_request("warn", warn_request);
+ init_request("open", open_request);
+ init_request("opena", opena_request);
+ init_request("close", close_request);
+ init_request("write", write_request);
+ init_request("writem", write_macro_request);
+ init_request("trf", transparent_file);
+#ifdef WIDOW_CONTROL
+ init_request("fpl", flush_pending_lines);
+#endif /* WIDOW_CONTROL */
+ init_request("nroff", nroff_request);
+ init_request("troff", troff_request);
+#ifdef COLUMN
+ init_request("vj", vjustify);
+#endif /* COLUMN */
+ init_request("mso", macro_source);
+ init_request("do", do_request);
+#ifndef POPEN_MISSING
+ init_request("pso", pipe_source);
+#endif /* not POPEN_MISSING */
+ init_request("psbb", ps_bbox_request);
+ number_reg_dictionary.define("systat", new variable_reg(&system_status));
+ number_reg_dictionary.define("slimit",
+ new variable_reg(&input_stack::limit));
+ number_reg_dictionary.define(".$", new nargs_reg);
+ number_reg_dictionary.define(".c", new lineno_reg);
+ number_reg_dictionary.define("c.", new writable_lineno_reg);
+ number_reg_dictionary.define(".F", new filename_reg);
+ number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
+ number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
+ number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
+ number_reg_dictionary.define(".R", new constant_reg("10000"));
+ extern const char *major_version;
+ number_reg_dictionary.define(".x", new constant_reg(major_version));
+ extern const char *minor_version;
+ number_reg_dictionary.define(".y", new constant_reg(minor_version));
+ extern const char *revision;
+ number_reg_dictionary.define(".Y", new constant_reg(revision));
+ number_reg_dictionary.define(".g", new constant_reg("1"));
+ number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
+ number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
+ number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
+ number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
+ number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
+ number_reg_dictionary.define("opminx",
+ new variable_reg(&output_reg_minx_contents));
+ number_reg_dictionary.define("opminy",
+ new variable_reg(&output_reg_miny_contents));
+ number_reg_dictionary.define("opmaxx",
+ new variable_reg(&output_reg_maxx_contents));
+ number_reg_dictionary.define("opmaxy",
+ new variable_reg(&output_reg_maxy_contents));
+}
+
+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, "`%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;
+ 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();
+ 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;
+ default:
+ // silently pass it through
+ break;
+ }
+ draw_node *dn = new draw_node(type, point, npoints,
+ curenv->get_font_size());
+ a_delete point;
+ return dn;
+ }
+ else {
+ a_delete point;
+ }
+ }
+ }
+ return 0;
+}
+
+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 },
+ { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
+ { "w", WARN_TOTAL },
+ { "default", DEFAULT_WARNING_MASK },
+};
+
+static int lookup_warning(const char *name)
+{
+ for (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, 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;
+ }
+ 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;
+}
+
+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), not_found(0),
+ transparent_translate(1), nm(s)
+{
+ index = next_index++;
+}
+
+void charinfo::set_hyphenation_code(unsigned char c)
+{
+ hyphenation_code = c;
+}
+
+void charinfo::set_translation(charinfo *ci, int tt)
+{
+ translation = ci;
+ 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;
+}
+
+macro *charinfo::set_macro(macro *m)
+{
+ macro *tem = mac;
+ mac = m;
+ 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);
+ 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/input.h b/contrib/groff/src/roff/troff/input.h
new file mode 100644
index 000000000000..525e1ef686b1
--- /dev/null
+++ b/contrib/groff/src/roff/troff/input.h
@@ -0,0 +1,92 @@
+// -*- C++ -*-
+/* Copyright (C) 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. */
+
+
+/* special character codes */
+
+#ifndef IS_EBCDIC_HOST
+
+const int ESCAPE_QUESTION = 015;
+const int BEGIN_TRAP = 016;
+const int END_TRAP = 017;
+const int PAGE_EJECTOR = 020;
+const int ESCAPE_NEWLINE = 021;
+const int ESCAPE_AMPERSAND = 022;
+const int ESCAPE_UNDERSCORE = 023;
+const int ESCAPE_BAR = 024;
+const int ESCAPE_CIRCUMFLEX = 025;
+const int ESCAPE_LEFT_BRACE = 026;
+const int ESCAPE_RIGHT_BRACE = 027;
+const int ESCAPE_LEFT_QUOTE = 030;
+const int ESCAPE_RIGHT_QUOTE = 031;
+const int ESCAPE_HYPHEN = 032;
+const int ESCAPE_BANG = 033;
+const int ESCAPE_c = 034;
+const int ESCAPE_e = 035;
+const int ESCAPE_PERCENT = 036;
+const int ESCAPE_SPACE = 037;
+
+const int TITLE_REQUEST = 0200;
+const int COPY_FILE_REQUEST = 0201;
+const int TRANSPARENT_FILE_REQUEST = 0202;
+#ifdef COLUMN
+const int VJUSTIFY_REQUEST = 0203;
+#endif /* COLUMN */
+const int ESCAPE_E = 0204;
+const int LAST_PAGE_EJECTOR = 0205;
+const int ESCAPE_RIGHT_PARENTHESIS = 0206;
+const int ESCAPE_TILDE = 0207;
+const int ESCAPE_COLON = 0210;
+
+#else /* IS_EBCDIC_HOST */
+
+const int ESCAPE_QUESTION = 010;
+const int BEGIN_TRAP = 011;
+const int END_TRAP = 013;
+const int PAGE_EJECTOR = 015;
+const int ESCAPE_NEWLINE = 016;
+const int ESCAPE_AMPERSAND = 017;
+const int ESCAPE_UNDERSCORE = 020;
+const int ESCAPE_BAR = 021;
+const int ESCAPE_CIRCUMFLEX = 022;
+const int ESCAPE_LEFT_BRACE = 023;
+const int ESCAPE_RIGHT_BRACE = 024;
+const int ESCAPE_LEFT_QUOTE = 027;
+const int ESCAPE_RIGHT_QUOTE = 030;
+const int ESCAPE_HYPHEN = 031;
+const int ESCAPE_BANG = 032;
+const int ESCAPE_c = 033;
+const int ESCAPE_e = 034;
+const int ESCAPE_PERCENT = 035;
+const int ESCAPE_SPACE = 036;
+
+const int TITLE_REQUEST = 060;
+const int COPY_FILE_REQUEST = 061;
+const int TRANSPARENT_FILE_REQUEST = 062;
+#ifdef COLUMN
+const int VJUSTIFY_REQUEST = 063;
+#endif /* COLUMN */
+const int ESCAPE_E = 064;
+const int LAST_PAGE_EJECTOR = 065;
+const int ESCAPE_RIGHT_PARENTHESIS = 066;
+const int ESCAPE_TILDE = 067;
+const int ESCAPE_COLON = 070;
+
+#endif /* IS_EBCDIC_HOST */
diff --git a/contrib/groff/src/roff/troff/node.cc b/contrib/groff/src/roff/troff/node.cc
new file mode 100644
index 000000000000..2d2d799a0386
--- /dev/null
+++ b/contrib/groff/src/roff/troff/node.cc
@@ -0,0 +1,5621 @@
+// -*- 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. */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#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 "charinfo.h"
+#include "font.h"
+#include "reg.h"
+#include "input.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
+ */
+
+static 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();
+};
+
+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;
+}
+
+// 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;
+}
+
+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 *make_tfont(tfont_spec &spec)
+{
+ for (tfont *p = tfont::tfont_list; p; p = p->next)
+ if (*p == spec)
+ return p;
+ return new tfont(spec);
+}
+
+tfont *tfont::tfont_list = 0;
+
+tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
+{
+ 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; // .output 1 or .output 0 requests
+ 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;
+ 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(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 *ci, tfont *tf);
+ void put_char_width(charinfo *ci, tfont *tf, hunits w, hunits k);
+ void right(hunits);
+ void down(vunits);
+ void moveto(hunits, vunits);
+ void start_special(tfont *tf, int no_init_string = 0);
+ void start_special(int no_init_string = 0);
+ void 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);
+ void determine_line_limits (char code, hvpair *point, int npoints);
+ void check_charinfo(tfont *tf, charinfo *ci);
+ int get_hpos() { return hpos; }
+ int get_vpos() { return vpos; }
+};
+
+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);
+}
+
+void troff_output_file::start_special(tfont *tf, int no_init_string)
+{
+ flush_tbuf();
+
+ /*
+ * although this is extremely unlikely to have an effect on other devices
+ * this way is safer. Currently this is only needed for html.
+ */
+ if (is_html && tf) {
+ if (tf != current_tfont)
+ set_font(tf);
+ }
+ do_motion();
+ if (!no_init_string)
+ put("x X ");
+}
+
+void troff_output_file::start_special(int no_init_string)
+{
+ flush_tbuf();
+ do_motion();
+ if (!no_init_string)
+ 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 width)
+{
+ moveto(x, y);
+ while (n != 0) {
+ if (is_on() || (n->force_tprint()))
+ 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();
+ 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 (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 + current_height);
+
+ 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)
+{
+ int size = tf->get_size().to_scaled_points();
+ int height = tf->get_char_height(ci).to_units();
+ int width = tf->get_width(ci).to_units()
+ + tf->get_italic_correction(ci).to_units();
+ int depth = tf->get_char_depth(ci).to_units();
+ check_output_limits(output_hpos,
+ output_vpos - height);
+ check_output_limits(output_hpos + width,
+ output_vpos + size + depth);
+}
+
+void troff_output_file::put_char_width(charinfo *ci, tfont *tf, hunits w,
+ hunits k)
+{
+ if (tf != current_tfont) {
+ flush_tbuf();
+ set_font(tf);
+ }
+ char c = ci->get_ascii_code();
+ int kk = k.to_units();
+ if (c == '\0') {
+ flush_tbuf();
+ do_motion();
+ 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
+ && 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;
+ }
+ 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 && n > 0 && n < 100 && !force_motion) {
+ put(char(n/10 + '0'));
+ put(char(n%10 + '0'));
+ put(c);
+ output_hpos = hpos;
+ }
+ else {
+ do_motion();
+ put('c');
+ put(c);
+ }
+ hpos += w.to_units() + kk;
+ }
+}
+
+void troff_output_file::put_char(charinfo *ci, tfont *tf)
+{
+ flush_tbuf();
+ if (tf != current_tfont)
+ set_font(tf);
+ char c = ci->get_ascii_code();
+ if (c == '\0') {
+ 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 && n > 0 && n < 100) {
+ put(char(n/10 + '0'));
+ put(char(n%10 + '0'));
+ put(c);
+ output_hpos = hpos;
+ }
+ else {
+ do_motion();
+ put('c');
+ put(c);
+ }
+ }
+}
+
+void troff_output_file::set_font(tfont *tf)
+{
+ if (current_tfont == tf)
+ return;
+ 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;
+}
+
+// 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;
+ 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;
+ default:
+ // remember this doesn't work for arc.. yet
+ 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)
+{
+ flush_tbuf();
+ do_motion();
+ int size = fsize.to_scaled_points();
+ if (current_size != size) {
+ put('s');
+ put(size);
+ put('\n');
+ current_size = size;
+ current_tfont = 0;
+ }
+ put('D');
+ put(code);
+ int i;
+ if (code == 'c') {
+ put(' ');
+ put(point[0].h.to_units());
+ }
+ else
+ for (i = 0; i < npoints; i++) {
+ put(' ');
+ put(point[i].h.to_units());
+ 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;
+ }
+ put('\n');
+}
+
+void troff_output_file::really_on ()
+{
+ flush_tbuf();
+}
+
+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), 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 *filename)
+{
+}
+
+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 && output_on)
+ 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 *filename)
+{
+}
+
+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 width)
+{
+ 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;
+#ifdef STORE_WIDTH
+ hunits wid;
+ glyph_node(charinfo *, tfont *, hunits, node * = 0);
+#endif
+public:
+ void *operator new(size_t);
+ void operator delete(void *);
+ glyph_node(charinfo *, tfont *, 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();
+ void tprint(troff_output_file *);
+ void zero_width_tprint(troff_output_file *);
+ hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
+ 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 *, hunits, node *gn1, node *gn2, node *x = 0);
+#endif
+public:
+ void *operator new(size_t);
+ void operator delete(void *);
+ ligature_node(charinfo *, tfont *, node *gn1, node *gn2, node *x = 0);
+ ~ligature_node();
+ node *copy();
+ node *add_self(node *, hyphen_list **);
+ hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
+ 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 *ss = 0);
+ 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, node *x)
+: charinfo_node(c, x), tf(t)
+{
+#ifdef STORE_WIDTH
+ wid = tf->get_width(ci);
+#endif
+}
+
+#ifdef STORE_WIDTH
+glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x)
+: charinfo_node(c, x), tf(t), wid(w)
+{
+}
+#endif
+
+node *glyph_node::copy()
+{
+#ifdef STORE_WIDTH
+ return new glyph_node(ci, tf, wid);
+#else
+ return new glyph_node(ci, tf);
+#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)
+{
+ return new hyphen_list(ci->get_hyphenation_code(), tail);
+}
+
+tfont *node::get_tfont()
+{
+ return 0;
+}
+
+tfont *glyph_node::get_tfont()
+{
+ return tf;
+}
+
+node *node::merge_glyph_node(glyph_node * /*gn*/)
+{
+ return 0;
+}
+
+node *glyph_node::merge_glyph_node(glyph_node *gn)
+{
+ if (tf == gn->tf) {
+ charinfo *lig;
+ if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
+ node *next1 = next;
+ next = 0;
+ return new ligature_node(lig, tf, this, gn, next1);
+ }
+ 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,
+ node *gn1, node *gn2, node *x)
+: glyph_node(c, t, x), n1(gn1), n2(gn2)
+{
+}
+
+#ifdef STORE_WIDTH
+ligature_node::ligature_node(charinfo *c, tfont *t, hunits w,
+ node *gn1, node *gn2, node *x)
+: glyph_node(c, t, w, x), n1(gn1), n2(gn2)
+{
+}
+#endif
+
+ligature_node::~ligature_node()
+{
+ delete n1;
+ delete n2;
+}
+
+node *ligature_node::copy()
+{
+#ifdef STORE_WIDTH
+ return new ligature_node(ci, tf, wid, n1->copy(), n2->copy());
+#else
+ return new ligature_node(ci, tf, 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)
+{
+ return n1->get_hyphen_list(n2->get_hyphen_list(tail));
+}
+
+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)) {
+ node *next1 = next;
+ next = 0;
+ node *n = copy();
+ glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
+ 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)
+{
+ return tail;
+}
+
+hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail)
+{
+ return n1->get_hyphen_list(n2->get_hyphen_list(tail));
+}
+
+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)) {
+ node *next1 = next;
+ next = 0;
+ node *n = copy();
+ glyph_node *gn = new glyph_node(soft_hyphen_char, tf);
+ 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 *ss = 0);
+ 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)
+{
+ return n->get_hyphen_list(tail);
+}
+
+int italic_corrected_node::character_type()
+{
+ return n->character_type();
+}
+
+class break_char_node : public node {
+ node *ch;
+ char break_code;
+public:
+ break_char_node(node *, int, 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 *s = 0);
+ 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 c, node *x)
+: node(x), ch(n), break_code(c)
+{
+}
+
+break_char_node::~break_char_node()
+{
+ delete ch;
+}
+
+node *break_char_node::copy()
+{
+ return new break_char_node(ch->copy(), break_code);
+}
+
+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, n);
+ n->freeze_space();
+ }
+ next = n;
+ n = this;
+ if ((*p)->breakable && (break_code & 2)) {
+ n = new space_node(H0, 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)
+{
+ 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);
+}
+
+node *space_char_hmotion_node::copy()
+{
+ return new space_char_hmotion_node(n);
+}
+
+node *vmotion_node::copy()
+{
+ return new vmotion_node(n);
+}
+
+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;
+ 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, node *p)
+: node(p), n(nn), set(0), was_escape_colon(0)
+{
+}
+
+space_node::space_node(hunits nn, int s, int flag, node *p)
+: node(p), n(nn), set(s), was_escape_colon(flag)
+{
+}
+
+#if 0
+space_node::~space_node()
+{
+}
+#endif
+
+node *space_node::copy()
+{
+ return new space_node(n, set, was_escape_colon);
+}
+
+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_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, node *next)
+: hmotion_node(i, 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 = curenv->get_font();
+ tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
+ fontno);
+ if (curenv->is_composite())
+ tf = tf->get_plain();
+}
+
+special_node::special_node(const macro &m, tfont *t, int n)
+: mac(m), tf(t), no_init_string(n)
+{
+}
+
+int special_node::same(node *n)
+{
+ return ((mac == ((special_node *)n)->mac)
+ && (tf == ((special_node *)n)->tf)
+ && (no_init_string == ((special_node *)n)->no_init_string));
+}
+
+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, no_init_string);
+}
+
+void special_node::tprint_start(troff_output_file *out)
+{
+ out->start_special(get_tfont(), 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)
+{
+}
+
+suppress_node::suppress_node(symbol f, char p)
+: is_on(2), emit_limits(0), filename(f), position(p)
+{
+}
+
+suppress_node::suppress_node(int issue_limits, int on_or_off,
+ symbol f, char p)
+: is_on(on_or_off), emit_limits(issue_limits), filename(f), position(p)
+{
+}
+
+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));
+;
+}
+
+const char *suppress_node::type()
+{
+ return "suppress_node";
+}
+
+node *suppress_node::copy()
+{
+ return new suppress_node(emit_limits, is_on, filename, position);
+}
+
+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;
+
+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 = get_reg_int("%");
+ // firstly check to see whether this suppress node contains
+ // an image filename & position.
+ if (is_on == 2) {
+ // remember position and filename
+ last_position = position;
+ last_image_filename = filename.contents();
+ }
+ else {
+ // now check whether the suppress node requires us to issue limits.
+ if (emit_limits) {
+ char name[8192];
+ image_no++;
+ // remember that the filename will contain a %d in which the
+ // image_no is placed
+ sprintf(name, last_image_filename, image_no);
+ 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 (current_page != suppress_start_page)
+ error("suppression limit registers span more than one page;\n"
+ "image description %1 will be wrong", image_no);
+ // remember that the filename will contain a %d in which the
+ // image_no is placed
+ fprintf(stderr,
+ "grohtml-info:page %d %d %d %d %d %d %s %d %d %s\n",
+ current_page,
+ 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();
+ else
+ out->off();
+ // lastly we reset the output registers
+ reset_output_registers(out->get_vpos());
+ 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 *tail);
+ 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_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)
+{
+ 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, width_list *w, node *x)
+: space_node(d, x), orig_width(w), unformat(0)
+{
+}
+
+word_space_node::word_space_node(hunits d, int s, width_list *w,
+ int flag, node *x)
+: space_node(d, s, 0, 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, w_new, unformat);
+}
+
+int word_space_node::set_unformat_flag()
+{
+ unformat = 1;
+ return 1;
+}
+
+void word_space_node::tprint(troff_output_file *out)
+{
+ out->word_marker();
+ space_node::tprint(out);
+}
+
+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, node *x)
+: word_space_node(d, 0, x)
+{
+}
+
+unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x)
+: word_space_node(d, s, 0, 0, x)
+{
+}
+
+node *unbreakable_space_node::copy()
+{
+ return new unbreakable_space_node(n, set);
+}
+
+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)
+: npoints(np), sz(s), 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)
+ 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);
+}
+
+void draw_node::tprint(troff_output_file *out)
+{
+ out->draw(code, point, npoints, sz);
+}
+
+/* tprint methods */
+
+void glyph_node::tprint(troff_output_file *out)
+{
+ tfont *ptf = tf->get_plain();
+ if (ptf == tf)
+ out->put_char_width(ci, ptf, 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);
+ out->right(offset);
+ }
+ out->put_char_width(ci, ptf, 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);
+ if (bold) {
+ out->right(offset);
+ out->put_char(ci, ptf);
+ 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);
+ n->tprint(out);
+ out->right(xx - xx2);
+ }
+ else {
+ hunits rem = x - w*i;
+ if (rem > H0)
+ if (n->overlaps_horizontally()) {
+ n->tprint(out);
+ out->right(rem - w);
+ }
+ else
+ out->right(rem);
+ while (--i >= 0)
+ 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);
+ n->tprint(out);
+ out->down(-h);
+ }
+ else {
+ 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);
+ }
+ 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->right(n);
+}
+
+void hmotion_node::tprint(troff_output_file *out)
+{
+ out->right(n);
+}
+
+void vmotion_node::tprint(troff_output_file *out)
+{
+ 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) {
+ 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) {
+ 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 0
+ && global_special_fonts == 0 && font_table[fontno]->sf == 0
+#endif
+ ) {
+ 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();
+ return new glyph_node(s, tf);
+}
+
+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());
+ case charinfo::TRANSLATE_STRETCHABLE_SPACE:
+ return new unbreakable_space_node(env->get_space_width());
+ 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)
+ 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(), this);
+ *widthp += res->width();
+ return res;
+ case charinfo::TRANSLATE_STRETCHABLE_SPACE:
+ res = new unbreakable_space_node(env->get_space_width(), this);
+ *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) {
+ 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, 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;
+}
+
+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)
+{
+ return new hyphen_list(0, tail);
+}
+
+int space_char_hmotion_node::same(node *nd)
+{
+ return n == ((space_char_hmotion_node *)nd)->n;
+}
+
+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)
+{
+ return new hyphen_list(0, tail);
+}
+
+int vmotion_node::same(node *nd)
+{
+ return n == ((vmotion_node *)nd)->n;
+}
+
+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)
+{
+ return n ? n->get_hyphen_list(tail) : 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)
+{
+ 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;
+}
+
+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
+ && 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;
+}
+
+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);
+}
+
+const char *word_space_node::type()
+{
+ return "word_space_node";
+}
+
+int word_space_node::force_tprint()
+{
+ return 0;
+}
+
+int unbreakable_space_node::same(node *nd)
+{
+ return (n == ((unbreakable_space_node *)nd)->n
+ && set == ((unbreakable_space_node *)nd)->set);
+}
+
+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)
+{
+ 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());
+ font_dictionary.lookup(external_name, &a_char);
+ return 0;
+ }
+ 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
+ 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(0);
+ 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;
+}
+
+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] != NULL;
+}
+
+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("fp", font_position);
+ init_request("sty", style);
+ init_request("cs", constant_space);
+ init_request("bd", bold_font);
+ init_request("uf", underline_font);
+ init_request("lg", ligature);
+ init_request("kern", kern_request);
+ init_request("tkf", track_kern);
+ init_request("special", special_request);
+ init_request("fspecial", font_special_request);
+ init_request("ftr", font_translate);
+ init_request("shc", set_soft_hyphen_char);
+ 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
new file mode 100644
index 000000000000..3e5f61595abd
--- /dev/null
+++ b/contrib/groff/src/roff/troff/node.h
@@ -0,0 +1,584 @@
+// -*- 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. */
+
+
+struct hyphen_list {
+ unsigned char hyphen;
+ unsigned char breakable;
+ unsigned char hyphenation_code;
+ hyphen_list *next;
+ hyphen_list(unsigned char code, hyphen_list *p = 0);
+};
+
+void hyphenate(hyphen_list *, unsigned);
+
+enum hyphenation_type { HYPHEN_MIDDLE, HYPHEN_BOUNDARY, HYPHEN_INHIBIT };
+
+class ascii_output_file;
+
+struct breakpoint;
+struct vertical_size;
+struct charinfo;
+
+class macro;
+
+class troff_output_file;
+class tfont;
+class environment;
+
+class glyph_node;
+class diverted_space_node;
+class token_node;
+
+struct node {
+ node *next;
+ node *last;
+ node();
+ node(node *n);
+ node *add_char(charinfo *c, environment *, hunits *widthp, int *spacep);
+
+ virtual ~node();
+ virtual node *copy() = 0;
+ virtual int set_unformat_flag();
+ virtual int force_tprint() = 0;
+ virtual hunits width();
+ virtual hunits subscript_correction();
+ virtual hunits italic_correction();
+ virtual hunits left_italic_correction();
+ virtual hunits skew();
+ virtual int nspaces();
+ virtual int merge_space(hunits, hunits, hunits);
+ virtual vunits vertical_width();
+ virtual node *last_char_node();
+ virtual void vertical_extent(vunits *min, vunits *max);
+ virtual int character_type();
+ virtual void set_vertical_size(vertical_size *);
+ virtual int ends_sentence();
+ 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 void ascii_print(ascii_output_file *);
+ virtual void asciify(macro *);
+ virtual int discardable();
+ virtual void spread_space(int *, hunits *);
+ virtual void freeze_space();
+ virtual void is_escape_colon();
+ virtual breakpoint *get_breakpoints(hunits width, int nspaces,
+ breakpoint *rest = 0,
+ int is_inner = 0);
+ virtual int nbreaks();
+ virtual void split(int, node **, node **);
+ virtual hyphenation_type get_hyphenation_type();
+ virtual int reread(int *);
+ virtual token_node *get_token_node();
+ virtual int overlaps_vertically();
+ virtual int overlaps_horizontally();
+ virtual units size();
+ virtual int interpret(macro *);
+
+ virtual node *merge_glyph_node(glyph_node *);
+ virtual tfont *get_tfont();
+ virtual void tprint(troff_output_file *);
+ virtual void zero_width_tprint(troff_output_file *);
+
+ node *add_italic_correction(hunits *);
+
+ virtual int same(node *) = 0;
+ virtual const char *type() = 0;
+};
+
+inline node::node() : next(0)
+{
+}
+
+inline node::node(node *n) : next(n)
+{
+}
+
+inline node::~node()
+{
+}
+
+// 0 means it doesn't, 1 means it does, 2 means it's transparent
+
+int node_list_ends_sentence(node *);
+
+struct breakpoint {
+ breakpoint *next;
+ hunits width;
+ int nspaces;
+ node *nd;
+ int index;
+ char hyphenated;
+};
+
+class line_start_node : public node {
+public:
+ line_start_node() {}
+ node *copy() { return new line_start_node; }
+ int same(node *);
+ int force_tprint();
+ const char *type();
+ void asciify(macro *);
+};
+
+class space_node : public node {
+private:
+#if 0
+ enum { BLOCK = 1024 };
+ static space_node *free_list;
+ void operator delete(void *);
+#endif
+protected:
+ hunits n;
+ char set;
+ char was_escape_colon;
+ space_node(hunits, int, int, node * = 0);
+public:
+ space_node(hunits d, node *p = 0);
+#if 0
+ ~space_node();
+ void *operator new(size_t);
+#endif
+ node *copy();
+ int nspaces();
+ hunits width();
+ int discardable();
+ int merge_space(hunits, hunits, hunits);
+ void freeze_space();
+ void is_escape_colon();
+ void spread_space(int *, hunits *);
+ void tprint(troff_output_file *);
+ breakpoint *get_breakpoints(hunits width, int nspaces, breakpoint *rest = 0,
+ int is_inner = 0);
+ int nbreaks();
+ void split(int, node **, node **);
+ void ascii_print(ascii_output_file *);
+ int same(node *);
+ void asciify(macro *);
+ const char *type();
+ int force_tprint();
+ hyphenation_type get_hyphenation_type();
+};
+
+struct width_list {
+ width_list *next;
+ hunits width;
+ hunits sentence_width;
+ width_list(hunits, hunits);
+ width_list(width_list *);
+};
+
+class word_space_node : public space_node {
+protected:
+ width_list *orig_width;
+ unsigned char unformat;
+ word_space_node(hunits, int, width_list *, int, node * = 0);
+public:
+ word_space_node(hunits, width_list *, node * = 0);
+ ~word_space_node();
+ node *copy();
+ int reread(int *);
+ int set_unformat_flag();
+ void tprint(troff_output_file *);
+ int same(node *);
+ void asciify(macro *);
+ const char *type();
+ int merge_space(hunits, hunits, hunits);
+ int force_tprint();
+};
+
+class unbreakable_space_node : public word_space_node {
+ unbreakable_space_node(hunits, int, node * = 0);
+public:
+ unbreakable_space_node(hunits, node * = 0);
+ node *copy();
+ int reread(int *);
+ int same(node *);
+ void asciify(macro *);
+ const char *type();
+ int force_tprint();
+ breakpoint *get_breakpoints(hunits width, int nspaces, breakpoint *rest = 0,
+ int is_inner = 0);
+ int nbreaks();
+ 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);
+ hyphenation_type get_hyphenation_type();
+};
+
+class diverted_space_node : public node {
+public:
+ vunits n;
+ diverted_space_node(vunits d, node *p = 0);
+ node *copy();
+ int reread(int *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class diverted_copy_file_node : public node {
+ symbol filename;
+public:
+ vunits n;
+ diverted_copy_file_node(symbol s, node *p = 0);
+ node *copy();
+ int reread(int *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class extra_size_node : public node {
+ vunits n;
+public:
+ extra_size_node(vunits i) : n(i) {}
+ void set_vertical_size(vertical_size *);
+ node *copy();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class vertical_size_node : public node {
+ vunits n;
+public:
+ vertical_size_node(vunits i) : n(i) {}
+ void set_vertical_size(vertical_size *);
+ void asciify(macro *);
+ node *copy();
+ int set_unformat_flag();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class hmotion_node : public node {
+protected:
+ hunits n;
+ unsigned char was_tab;
+ unsigned char unformat;
+public:
+ hmotion_node(hunits i, node *next = 0)
+ : node(next), n(i), was_tab(0), unformat(0) {}
+ hmotion_node(hunits i, int flag1, int flag2, node *next = 0)
+ : node(next), n(i), was_tab(flag1), unformat(flag2) {}
+ node *copy();
+ int reread(int *);
+ int set_unformat_flag();
+ void asciify(macro *);
+ void tprint(troff_output_file *);
+ hunits width();
+ void ascii_print(ascii_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);
+ hyphenation_type get_hyphenation_type();
+};
+
+class space_char_hmotion_node : public hmotion_node {
+public:
+ space_char_hmotion_node(hunits i, node *next = 0);
+ node *copy();
+ void ascii_print(ascii_output_file *);
+ void asciify(macro *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ node *add_self(node *, hyphen_list **);
+ hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
+ hyphenation_type get_hyphenation_type();
+};
+
+class vmotion_node : public node {
+ vunits n;
+public:
+ vmotion_node(vunits i) : n(i) {}
+ void tprint(troff_output_file *);
+ node *copy();
+ vunits vertical_width();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class hline_node : public node {
+ hunits x;
+ node *n;
+public:
+ hline_node(hunits i, node *c, node *next = 0) : node(next), x(i), n(c) {}
+ ~hline_node();
+ node *copy();
+ hunits width();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class vline_node : public node {
+ vunits x;
+ node *n;
+public:
+ vline_node(vunits i, node *c, node *next= 0) : node(next), x(i), n(c) {}
+ ~vline_node();
+ node *copy();
+ void tprint(troff_output_file *);
+ hunits width();
+ vunits vertical_width();
+ void vertical_extent(vunits *, vunits *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+
+class dummy_node : public node {
+public:
+ dummy_node(node *nd = 0) : node(nd) {}
+ node *copy();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ hyphenation_type get_hyphenation_type();
+};
+
+class transparent_dummy_node : public node {
+public:
+ transparent_dummy_node(node *nd = 0) : node(nd) {}
+ node *copy();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ int ends_sentence();
+ hyphenation_type get_hyphenation_type();
+};
+
+class zero_width_node : public node {
+ node *n;
+public:
+ zero_width_node(node *gn);
+ ~zero_width_node();
+ node *copy();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ void append(node *);
+ int character_type();
+ void vertical_extent(vunits *min, vunits *max);
+};
+
+class left_italic_corrected_node : public node {
+ node *n;
+ hunits x;
+public:
+ left_italic_corrected_node(node * = 0);
+ ~left_italic_corrected_node();
+ void tprint(troff_output_file *);
+ void ascii_print(ascii_output_file *);
+ void asciify(macro *);
+ node *copy();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ hunits width();
+ node *last_char_node();
+ void vertical_extent(vunits *, vunits *);
+ int ends_sentence();
+ int overlaps_horizontally();
+ int overlaps_vertically();
+ hyphenation_type get_hyphenation_type();
+ tfont *get_tfont();
+ int character_type();
+ hunits skew();
+ hunits italic_correction();
+ hunits subscript_correction();
+ hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
+ node *add_self(node *, hyphen_list **);
+ node *merge_glyph_node(glyph_node *);
+};
+
+class overstrike_node : public node {
+ node *list;
+ hunits max_width;
+public:
+ overstrike_node();
+ ~overstrike_node();
+ node *copy();
+ void tprint(troff_output_file *);
+ void overstrike(node *); // add another node to be overstruck
+ hunits width();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ node *add_self(node *, hyphen_list **);
+ hyphen_list *get_hyphen_list(hyphen_list *ss = 0);
+ hyphenation_type get_hyphenation_type();
+};
+
+class bracket_node : public node {
+ node *list;
+ hunits max_width;
+public:
+ bracket_node();
+ ~bracket_node();
+ node *copy();
+ void tprint(troff_output_file *);
+ void bracket(node *); // add another node to be overstruck
+ hunits width();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class special_node : public node {
+ macro mac;
+ tfont *tf;
+ int no_init_string;
+ void tprint_start(troff_output_file *);
+ void tprint_char(troff_output_file *, unsigned char);
+ void tprint_end(troff_output_file *);
+public:
+ special_node(const macro &, int = 0);
+ special_node(const macro &, tfont *, int = 0);
+ node *copy();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+ int ends_sentence();
+ tfont *get_tfont();
+};
+
+class suppress_node : public node {
+ int is_on;
+ int emit_limits; // must we issue the extent of the area written out?
+ symbol filename;
+ char position;
+public:
+ suppress_node(int, int);
+ suppress_node(symbol f, char p);
+ suppress_node(int, int, symbol f, char p);
+ node *copy();
+ void tprint(troff_output_file *);
+ hunits width();
+ int same(node *);
+ const char *type();
+ int force_tprint();
+private:
+ void put(troff_output_file *out, const char *s);
+};
+
+struct hvpair {
+ hunits h;
+ vunits v;
+ hvpair();
+};
+
+class draw_node : public node {
+ int npoints;
+ font_size sz;
+ char code;
+ hvpair *point;
+public:
+ draw_node(char, hvpair *, int, font_size);
+ ~draw_node();
+ hunits width();
+ vunits vertical_width();
+ node *copy();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+ int force_tprint();
+};
+
+class charinfo;
+node *make_node(charinfo *ci, environment *);
+int character_exists(charinfo *, environment *);
+
+int same_node_list(node *n1, node *n2);
+node *reverse_node_list(node *n);
+void delete_node_list(node *);
+node *copy_node_list(node *);
+
+int get_bold_fontno(int f);
+
+inline hyphen_list::hyphen_list(unsigned char code, hyphen_list *p)
+: hyphen(0), breakable(0), hyphenation_code(code), next(p)
+{
+}
+
+extern void read_desc();
+extern int mount_font(int n, symbol, symbol = NULL_SYMBOL);
+extern void mount_style(int n, symbol);
+extern int is_good_fontno(int n);
+extern int symbol_fontno(symbol);
+extern int next_available_font_position();
+extern void init_size_table(int *);
+extern int get_underline_fontno();
+
+class output_file {
+ char make_g_plus_plus_shut_up;
+public:
+ output_file();
+ virtual ~output_file();
+ virtual void trailer(vunits page_length);
+ virtual void flush() = 0;
+ virtual void transparent_char(unsigned char) = 0;
+ virtual void print_line(hunits x, vunits y, node *n,
+ vunits before, vunits after, hunits width) = 0;
+ virtual void begin_page(int pageno, vunits page_length) = 0;
+ virtual void copy_file(hunits x, vunits y, const char *filename) = 0;
+ virtual int is_printing() = 0;
+ virtual void put_filename(const char *filename);
+ virtual void on();
+ virtual void off();
+#ifdef COLUMN
+ virtual void vjustify(vunits, symbol);
+#endif /* COLUMN */
+};
+
+#ifndef POPEN_MISSING
+extern char *pipe_command;
+#endif
+
+extern output_file *the_output;
+extern void init_output();
+int in_output_page_list(int n);
+
+class font_family {
+ int *map;
+ int map_size;
+public:
+ const symbol nm;
+ font_family(symbol);
+ ~font_family();
+ int make_definite(int);
+ static void invalidate_fontno(int);
+};
+
+font_family *lookup_family(symbol);
diff --git a/contrib/groff/src/roff/troff/number.cc b/contrib/groff/src/roff/troff/number.cc
new file mode 100644
index 000000000000..539384286da5
--- /dev/null
+++ b/contrib/groff/src/roff/troff/number.cc
@@ -0,0 +1,692 @@
+// -*- 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 "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 "icPmnpuvMsz"
+
+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;
+ 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 '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.cc b/contrib/groff/src/roff/troff/reg.cc
new file mode 100644
index 000000000000..254b0ff4311a
--- /dev/null
+++ b/contrib/groff/src/roff/troff/reg.cc
@@ -0,0 +1,473 @@
+// -*- 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. */
+
+#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 > 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
new file mode 100644
index 000000000000..8d403d442801
--- /dev/null
+++ b/contrib/groff/src/roff/troff/reg.h
@@ -0,0 +1,76 @@
+// -*- 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. */
+
+
+class reg : public object {
+public:
+ virtual const char *get_string() = 0;
+ virtual int get_value(units *);
+ virtual void increment();
+ virtual void decrement();
+ virtual void set_increment(units);
+ virtual void alter_format(char f, int w = 0);
+ virtual const char *get_format();
+ virtual void set_value(units);
+};
+
+class constant_int_reg : public reg {
+ int *p;
+public:
+ constant_int_reg(int *);
+ const char *get_string();
+};
+
+class general_reg : public reg {
+ char format;
+ int width;
+ int inc;
+public:
+ general_reg();
+ const char *get_string();
+ void increment();
+ void decrement();
+ void alter_format(char f, int w = 0);
+ void set_increment(units);
+ const char *get_format();
+ void add_value(units);
+
+ void set_value(units) = 0;
+ int get_value(units *) = 0;
+};
+
+class variable_reg : public general_reg {
+ units *ptr;
+public:
+ variable_reg(int *);
+ void set_value(units);
+ int get_value(units *);
+};
+
+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);
+
+reg *lookup_number_reg(symbol);
+#if 0
+void inline_define_reg();
+#endif
diff --git a/contrib/groff/src/roff/troff/request.h b/contrib/groff/src/roff/troff/request.h
new file mode 100644
index 000000000000..5654b8308572
--- /dev/null
+++ b/contrib/groff/src/roff/troff/request.h
@@ -0,0 +1,84 @@
+// -*- 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. */
+
+typedef void (*REQUEST_FUNCP)();
+
+class macro;
+
+class request_or_macro : public object {
+public:
+ request_or_macro();
+ virtual void invoke(symbol s) = 0;
+ virtual macro *to_macro();
+};
+
+class request : public request_or_macro {
+ REQUEST_FUNCP p;
+public:
+ void invoke(symbol);
+ request(REQUEST_FUNCP);
+};
+
+void delete_request_or_macro(request_or_macro *);
+
+extern object_dictionary request_dictionary;
+
+struct macro_header;
+struct node;
+
+class macro : public request_or_macro {
+ macro_header *p;
+ const char *filename; // where was it defined?
+ int lineno;
+ int length;
+public:
+ macro();
+ ~macro();
+ macro(const macro &);
+ macro &operator=(const macro &);
+ void append(unsigned char);
+ void append(node *);
+ void append_unsigned(unsigned int i);
+ void append_int(int i);
+ void append_str(const char *);
+ void invoke(symbol);
+ macro *to_macro();
+ void print_size();
+ int empty();
+ friend class string_iterator;
+ friend void chop_macro();
+ friend void substring_macro();
+ friend int operator==(const macro &, const macro &);
+};
+
+extern void init_input_requests();
+extern void init_markup_requests();
+extern void init_div_requests();
+extern void init_node_requests();
+extern void init_reg_requests();
+extern void init_env_requests();
+extern void init_hyphen_requests();
+extern void init_request(const char *s, REQUEST_FUNCP f);
+
+class charinfo;
+class environment;
+
+node *charinfo_to_node_list(charinfo *, const environment *);
diff --git a/contrib/groff/src/roff/troff/symbol.cc b/contrib/groff/src/roff/troff/symbol.cc
new file mode 100644
index 000000000000..ce09e393dfbe
--- /dev/null
+++ b/contrib/groff/src/roff/troff/symbol.cc
@@ -0,0 +1,150 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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;
+
+#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
+// I think it unlikely that we'll need more than a million symbols
+static const unsigned int table_sizes[] = {
+101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009, 80021, 160001, 500009, 1000003, 0
+};
+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 || *p == 0) {
+ s = 0;
+ 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/symbol.h b/contrib/groff/src/roff/troff/symbol.h
new file mode 100644
index 000000000000..88e0fff394c7
--- /dev/null
+++ b/contrib/groff/src/roff/troff/symbol.h
@@ -0,0 +1,73 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+#define DONT_STORE 1
+#define MUST_ALREADY_EXIST 2
+
+class symbol {
+ static const char **table;
+ static int table_used;
+ static int table_size;
+ static char *block;
+ static int block_size;
+ const char *s;
+public:
+ symbol(const char *p, int how = 0);
+ symbol();
+ unsigned long hash() const;
+ int operator ==(symbol) const;
+ int operator !=(symbol) const;
+ const char *contents() const;
+ int is_null() const;
+};
+
+
+extern const symbol NULL_SYMBOL;
+
+inline symbol::symbol() : s(0)
+{
+}
+
+inline int symbol::operator==(symbol p) const
+{
+ return s == p.s;
+}
+
+inline int symbol::operator!=(symbol p) const
+{
+ return s != p.s;
+}
+
+inline unsigned long symbol::hash() const
+{
+ return (unsigned long)s;
+}
+
+inline const char *symbol::contents() const
+{
+ return s;
+}
+
+inline int symbol::is_null() const
+{
+ return s == 0;
+}
+
+symbol concat(symbol, symbol);
diff --git a/contrib/groff/src/roff/troff/token.h b/contrib/groff/src/roff/troff/token.h
new file mode 100644
index 000000000000..40283adb5861
--- /dev/null
+++ b/contrib/groff/src/roff/troff/token.h
@@ -0,0 +1,218 @@
+// -*- 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. */
+
+
+struct charinfo;
+struct node;
+struct vunits;
+
+class token {
+ symbol nm;
+ node *nd;
+ unsigned char c;
+ int val;
+ units dim;
+ enum token_type {
+ TOKEN_BACKSPACE,
+ TOKEN_BEGIN_TRAP,
+ TOKEN_CHAR, // a normal printing character
+ TOKEN_DUMMY, // \&
+ TOKEN_EMPTY, // this is the initial value
+ TOKEN_END_TRAP,
+ TOKEN_ESCAPE, // \e
+ TOKEN_HYPHEN_INDICATOR,
+ TOKEN_INTERRUPT, // \c
+ TOKEN_ITALIC_CORRECTION, // \/
+ TOKEN_LEADER, // ^A
+ TOKEN_LEFT_BRACE,
+ TOKEN_MARK_INPUT, // \k -- `nm' is the name of the register
+ TOKEN_NEWLINE, // newline
+ TOKEN_NODE,
+ TOKEN_NUMBERED_CHAR,
+ TOKEN_PAGE_EJECTOR,
+ TOKEN_REQUEST,
+ TOKEN_RIGHT_BRACE,
+ TOKEN_SPACE, // ` ' -- ordinary space
+ TOKEN_SPECIAL, // a special character -- \' \` \- \(xx
+ TOKEN_SPREAD, // \p -- break and spread output line
+ TOKEN_STRETCHABLE_SPACE, // \~
+ TOKEN_TAB, // tab
+ TOKEN_TRANSPARENT, // \!
+ TOKEN_TRANSPARENT_DUMMY, // \)
+ TOKEN_EOF // end of file
+ } type;
+public:
+ token();
+ ~token();
+ token(const token &);
+ void operator=(const token &);
+ void next();
+ void process();
+ void skip();
+ int eof();
+ int nspaces(); // 1 if space, 2 if double space, 0 otherwise
+ int space(); // is the current token a space?
+ int stretchable_space(); // is the current token a stretchable space?
+ int white_space(); // is the current token space or tab?
+ int special(); // is the current token a special character?
+ int newline(); // is the current token a newline?
+ int tab(); // is the current token a tab?
+ int leader();
+ int backspace();
+ int delimiter(int warn = 0); // is it suitable for use as a delimiter?
+ int dummy();
+ int transparent_dummy();
+ int transparent();
+ int left_brace();
+ int right_brace();
+ int page_ejector();
+ int hyphen_indicator();
+ int operator==(const token &); // need this for delimiters, and for conditions
+ int operator!=(const token &); // ditto
+ unsigned char ch();
+ charinfo *get_char(int required = 0);
+ int add_to_node_list(node **);
+ int title();
+ void make_space();
+ void make_newline();
+ const char *description();
+
+ friend void process_input_stack();
+};
+
+extern token tok; // the current token
+
+extern symbol get_name(int required = 0);
+extern symbol get_long_name(int required = 0);
+extern charinfo *get_optional_char();
+extern void check_missing_character();
+extern void skip_line();
+extern void handle_initial_title();
+
+struct hunits;
+extern void read_title_parts(node **part, hunits *part_width);
+
+extern int get_number_rigidly(units *result, unsigned char si);
+
+extern int get_number(units *result, unsigned char si);
+extern int get_integer(int *result);
+
+extern int get_number(units *result, unsigned char si, units prev_value);
+extern int get_integer(int *result, int prev_value);
+
+void interpolate_number_reg(symbol, int);
+
+const char *asciify(int c);
+
+inline int token::newline()
+{
+ return type == TOKEN_NEWLINE;
+}
+
+inline int token::space()
+{
+ return type == TOKEN_SPACE;
+}
+
+inline int token::stretchable_space()
+{
+ return type == TOKEN_STRETCHABLE_SPACE;
+}
+
+inline int token::special()
+{
+ return type == TOKEN_SPECIAL;
+}
+
+inline int token::nspaces()
+{
+ if (type == TOKEN_SPACE)
+ return 1;
+ else
+ return 0;
+}
+
+inline int token::white_space()
+{
+ return type == TOKEN_SPACE || type == TOKEN_TAB;
+}
+
+inline int token::transparent()
+{
+ return type == TOKEN_TRANSPARENT;
+}
+
+inline int token::page_ejector()
+{
+ return type == TOKEN_PAGE_EJECTOR;
+}
+
+inline unsigned char token::ch()
+{
+ return type == TOKEN_CHAR ? c : 0;
+}
+
+inline int token::eof()
+{
+ return type == TOKEN_EOF;
+}
+
+inline int token::dummy()
+{
+ return type == TOKEN_DUMMY;
+}
+
+inline int token::transparent_dummy()
+{
+ return type == TOKEN_TRANSPARENT_DUMMY;
+}
+
+inline int token::left_brace()
+{
+ return type == TOKEN_LEFT_BRACE;
+}
+
+inline int token::right_brace()
+{
+ return type == TOKEN_RIGHT_BRACE;
+}
+
+inline int token::tab()
+{
+ return type == TOKEN_TAB;
+}
+
+inline int token::leader()
+{
+ return type == TOKEN_LEADER;
+}
+
+inline int token::backspace()
+{
+ return type == TOKEN_BACKSPACE;
+}
+
+inline int token::hyphen_indicator()
+{
+ return type == TOKEN_HYPHEN_INDICATOR;
+}
+
+int has_arg();
diff --git a/contrib/groff/src/roff/troff/troff.h b/contrib/groff/src/roff/troff/troff.h
new file mode 100644
index 000000000000..254c6264731b
--- /dev/null
+++ b/contrib/groff/src/roff/troff/troff.h
@@ -0,0 +1,88 @@
+// -*- 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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "lib.h"
+#include "assert.h"
+#include "device.h"
+#include "searchpath.h"
+
+void cleanup_and_exit(int n);
+
+typedef int units;
+
+extern units scale(units n, units x, units y); // scale n by x/y
+
+extern units units_per_inch;
+
+extern int ascii_output_flag;
+extern int suppress_output_flag;
+extern int is_html;
+
+extern int tcommand_flag;
+extern int vresolution;
+extern int hresolution;
+extern int sizescale;
+
+extern search_path *mac_path;
+
+#include "cset.h"
+#include "cmap.h"
+#include "errarg.h"
+#include "error.h"
+
+enum warning_type {
+ WARN_CHAR = 01,
+ WARN_NUMBER = 02,
+ WARN_BREAK = 04,
+ WARN_DELIM = 010,
+ WARN_EL = 020,
+ WARN_SCALE = 040,
+ WARN_RANGE = 0100,
+ WARN_SYNTAX = 0200,
+ WARN_DI = 0400,
+ WARN_MAC = 01000,
+ WARN_REG = 02000,
+ WARN_TAB = 04000,
+ WARN_RIGHT_BRACE = 010000,
+ WARN_MISSING = 020000,
+ WARN_INPUT = 040000,
+ WARN_ESCAPE = 0100000,
+ WARN_SPACE = 0200000,
+ WARN_FONT = 0400000,
+ WARN_IG = 01000000
+ // change WARN_TOTAL if you add more warning types
+};
+
+const int WARN_TOTAL = 01777777;
+
+int warning(warning_type, const char *,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg);
diff --git a/contrib/groff/src/roff/troff/troff.man b/contrib/groff/src/roff/troff/troff.man
new file mode 100644
index 000000000000..7fe052b4e61a
--- /dev/null
+++ b/contrib/groff/src/roff/troff/troff.man
@@ -0,0 +1,2462 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000, 2001 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.
+.\" define a string tx for the TeX logo
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.
+.\" The BSD man macros can't handle " in arguments to font change macros,
+.\" so use \(ts instead of ".
+.tr \(ts"
+.
+.
+.TH @G@TROFF @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.
+.
+.SH NAME
+.
+.
+@g@troff \- format documents
+.
+.
+.SH SYNOPSIS
+.
+.
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fB@g@troff 'u
+.ti \niu
+.B @g@troff
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-abivzCERU
+.OP \-w name
+.OP \-W name
+.OP \-d cs
+.OP \-f fam
+.OP \-m name
+.OP \-n num
+.OP \-o list
+.OP \-r cn
+.OP \-T name
+.OP \-F dir
+.OP \-M dir
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.
+.
+.SH DESCRIPTION
+.
+.
+This manual page describes the GNU version of
+.BR troff ,
+which is part of the groff document formatting system.
+It is highly compatible with UNIX troff.
+Usually it should be invoked using the groff command, which will
+also run preprocessors and postprocessors in the appropriate
+order and with the appropriate options.
+.
+.
+.SH OPTIONS
+.
+.
+.TP \w'\-dname=s'u+2n
+.B \-a
+Generate an
+.SM ASCII
+approximation of the typeset output.
+.TP
+.B \-b
+Print a backtrace with each warning or error message. This backtrace
+should help track down the cause of the error. The line numbers given
+in the backtrace may not always be correct:
+.BR troff 's
+idea of line numbers
+gets confused by
+.B as
+or
+.B am
+requests.
+.TP
+.B \-i
+Read the standard input after all the named input files have been
+processed.
+.TP
+.B \-v
+Print the version number.
+.TP
+.BI \-w name
+Enable warning
+.IR name .
+Available warnings are described in
+the Warnings subsection below.
+Multiple
+.B \-w
+options are allowed.
+.TP
+.BI \-W name
+Inhibit warning
+.IR name .
+Multiple
+.B \-W
+options are allowed.
+.TP
+.B \-E
+Inhibit all error messages.
+.TP
+.B \-z
+Suppress formatted output.
+.TP
+.B \-C
+Enable compatibility mode.
+.TP
+.BI \-d cs
+.TQ
+.BI \-d name = s
+Define
+.I c
+or
+.I name
+to be a string
+.IR s ;
+.I c
+must be a one letter name.
+.TP
+.BI \-f fam
+Use
+.I fam
+as the default font family.
+.TP
+.BI \-m name
+Read in the file
+.IB name .tmac\fR.
+If it isn't found, try
+.BI tmac. name
+instead.
+It will be first searched for in directories given with the
+.B \-M
+command line option, then in directories given
+in the
+.B GROFF_TMAC_PATH
+environment variable, then in the current directory (only if in unsafe
+mode), the home directory, @SYSTEMMACRODIR@, @LOCALMACRODIR@, and
+@MACRODIR@.
+.TP
+.B \-U
+Unsafe mode.
+This will enable the following requests:
+.BR .open ,
+.BR .opena ,
+.BR .pso ,
+.BR .sy ,
+and
+.BR .pi .
+For security reasons, these potentially dangerous requests are disabled
+otherwise. It will also add the current directory to the macro search path.
+.TP
+.B \-R
+Don't load
+.B troffrc
+and
+.BR troffrc-end .
+.TP
+.BI \-n num
+Number the first page
+.IR num .
+.TP
+.BI \-o list
+Output only pages in
+.IR list ,
+which is a comma-separated list of page ranges;
+.I n
+means print page
+.IR n ,
+.IB m \- n
+means print every page between
+.I m
+and
+.IR n ,
+.BI \- n
+means print every page up to
+.IR n ,
+.IB n \-
+means print every page from
+.IR n .
+.B Troff
+will exit after printing the last page in the list.
+.TP
+.BI \-r cn
+.TQ
+.BI \-r name = n
+Set number register
+.I c
+or
+.I name
+to
+.IR n ;
+.I c
+must be a one character name;
+.I n
+can be any troff numeric expression.
+.TP
+.BI \-T name
+Prepare output for device
+.IR name ,
+rather than the default
+.BR @DEVICE@ .
+.TP
+.BI \-F dir
+Search in directory (or directory path)
+.I dir
+for subdirectories
+.BI dev name
+.RI ( name
+is the name of the device) and there for the
+.B DESC
+file and font files.
+.I dir
+is scanned before all other font directories.
+.TP
+.BI \-M dir
+Search directory (or directory path)
+.I dir
+for macro files.
+This is scanned before all other macro directories.
+.
+.
+.SH USAGE
+.
+.
+Only the features not in UNIX troff are described here.
+.
+.SS Long names
+.
+The names of number registers, fonts, strings/macros/diversions,
+special characters can be of any length. In escape sequences, where
+you can use
+.BI ( xx
+for a two character name, you can use
+.BI [ xxx ]
+for a name of arbitrary length:
+.TP
+.BI \e[ xxx ]
+Print the special character called
+.IR xxx .
+.TP
+.BI \ef[ xxx ]
+Set font
+.IR xxx .
+.TP
+.BI \e*[ xxx ]
+Interpolate string
+.IR xxx .
+.TP
+.BI \en[ xxx ]
+Interpolate number register
+.IR xxx .
+.
+.SS Fractional pointsizes
+.
+A
+.I
+scaled point
+is equal to 1/sizescale
+points, where
+sizescale is specified in the
+.B DESC
+file (1 by default).
+There is a new scale indicator
+.B z
+which has the effect of multiplying by sizescale.
+Requests and escape sequences in troff
+interpret arguments that represent a pointsize as being in units
+of scaled points, but they evaluate each such argument
+using a default scale indicator of
+.BR z .
+Arguments treated in this way are
+the argument to the
+.B ps
+request,
+the third argument to the
+.B cs
+request,
+the second and fourth arguments to the
+.B tkf
+request,
+the argument to the
+.B \eH
+escape sequence,
+and those variants of the
+.B \es
+escape sequence that take a numeric expression as their argument.
+.LP
+For example, suppose sizescale is 1000;
+then a scaled point will be equivalent to a millipoint;
+the request
+.B .ps 10.25
+is equivalent to
+.B .ps 10.25z
+and so sets the pointsize to 10250 scaled points,
+which is equal to 10.25 points.
+.LP
+The number register
+.B \en[.s]
+returns the pointsize in points as decimal fraction.
+There is also a new number register
+.B \en[.ps]
+that returns the pointsize in scaled points.
+.LP
+It would make no sense to use the
+.B z
+scale indicator in a numeric expression
+whose default scale indicator was neither
+.B u
+nor
+.BR z ,
+and so
+.B troff
+disallows this.
+Similarly it would make no sense to use a scaling indicator
+other than
+.B z
+or
+.B u
+in a numeric expression whose default scale indicator was
+.BR z ,
+and so
+.B troff
+disallows this as well.
+.LP
+There is also new scale indicator
+.B s
+which multiplies by the number of units in a scaled point.
+So, for example,
+.B \en[.ps]s
+is equal to
+.BR 1m .
+Be sure not to confuse the
+.B s
+and
+.B z
+scale indicators.
+.
+.SS Numeric expressions
+.
+.LP
+Spaces are permitted in a number expression within parentheses.
+.LP
+.B M
+indicates a scale of 100ths of an em.
+.TP
+.IB e1 >? e2
+The maximum of
+.I e1
+and
+.IR e2 .
+.TP
+.IB e1 <? e2
+The minimum of
+.I e1
+and
+.IR e2 .
+.TP
+.BI ( c ; e )
+Evaluate
+.I e
+using
+.I c
+as the default scaling indicator.
+If
+.I c
+is missing, ignore scaling indicators in the evaluation of
+.IR e .
+.
+.SS New escape sequences
+.
+.TP
+.BI \eA' anything '
+This expands to
+.B 1
+or
+.B 0
+according as
+.I anything
+is or is not acceptable as the name of a string, macro, diversion,
+number register, environment or font.
+It will return
+.B 0
+if
+.I anything
+is empty.
+This is useful if you want to lookup user input in some sort of
+associative table.
+.TP
+.BI \eB' anything '
+This expands to
+.B 1
+or
+.B 0
+according as
+.I anything
+is or is not a valid numeric expression.
+It will return
+.B 0
+if
+.I anything
+is empty.
+.TP
+.BI \eC' xxx '
+Typeset character named
+.IR xxx .
+Normally it is more convenient to use
+.BI \e[ xxx ]\fR.
+But
+.B \eC
+has the advantage that it is compatible with recent versions of
+.SM UNIX
+and is available in compatibility mode.
+.TP
+.B \eE
+This is equivalent to an escape character,
+but it's not interpreted in copy-mode.
+For example, strings to start and end superscripting could be defined
+like this:
+.RS
+.IP
+\&.ds { \ev'\-.3m'\es'\eEn[.s]*6u/10u'
+.br
+\&.ds } \es0\ev'.3m'
+.LP
+The use of
+.B \eE
+ensures that these definitions will work even if
+.B \e*{
+gets interpreted in copy-mode
+(for example, by being used in a macro argument).
+.RE
+.TP
+.BI \eN' n '
+Typeset the character with code
+.I n
+in the current font.
+.I n
+can be any integer.
+Most devices only have characters with codes between 0 and 255.
+If the current font does not contain a character with that code,
+special fonts will
+.I not
+be searched.
+The
+.B \eN
+escape sequence can be conveniently used on conjunction with the
+.B char
+request:
+.RS
+.IP
+.B
+\&.char \e[phone] \ef(ZD\eN'37'
+.RE
+.IP
+The code of each character is given in the fourth column in the font
+description file after the
+.B charset
+command.
+It is possible to include unnamed characters in the font description
+file by using a name of
+.BR \-\-\- ;
+the
+.B \eN
+escape sequence is the only way to use these.
+.TP
+.BI \eR' name\ \(+-n '
+This has the same effect as
+.RS
+.IP
+.BI .nr\ name\ \(+-n
+.RE
+.TP
+.BI \es( nn
+.TQ
+.BI \es\(+-( nn
+Set the point size to
+.I nn
+points;
+.I nn
+must be exactly two digits.
+.TP
+.BI \es[\(+- n ]
+.TQ
+.BI \es\(+-[ n ]
+.TQ
+.BI \es'\(+- n '
+.TQ
+.BI \es\(+-' n '
+Set the point size to
+.I n
+scaled points;
+.I n
+is a numeric expression with a default scale indicator of
+.BR z .
+.TP
+.BI \eV x
+.TQ
+.BI \eV( xx
+.TQ
+.BI \eV[ xxx ]
+Interpolate the contents of the environment variable
+.IR xxx ,
+as returned by
+.BR getenv (3).
+.B \eV
+is interpreted in copy-mode.
+.TP
+.BI \eY x
+.TQ
+.BI \eY( xx
+.TQ
+.BI \eY[ xxx ]
+This is approximately equivalent to
+.BI \eX'\e*[ xxx ]'\fR.
+However the contents of the string or macro
+.I xxx
+are not interpreted;
+also it is permitted for
+.I xxx
+to have been defined as a macro and thus contain newlines
+(it is not permitted for the argument to
+.B \eX
+to contain newlines).
+The inclusion of newlines requires an extension to the UNIX troff output
+format, and will confuse drivers that do not know about this
+extension.
+.TP
+.BI \eZ' anything '
+Print anything and then restore the horizontal and vertical
+position;
+.I anything
+may not contain tabs or leaders.
+.TP
+.B \e$0
+The name by which the current macro was invoked.
+The
+.B als
+request can make a macro have more than one name.
+.TP
+.B \e$*
+In a macro, the concatenation of all the arguments separated by spaces.
+.TP
+.B \e$@
+In a macro, the concatenation of all the arguments with each surrounded by
+double quotes, and separated by spaces.
+.TP
+.BI \e$( nn
+.TQ
+.BI \e$[ nnn ]
+In a macro, this gives the
+.IR nn -th
+or
+.IR nnn -th
+argument.
+Macros can have an unlimited number of arguments.
+.TP
+.BI \e? anything \e?
+When used in a diversion, this will transparently embed
+.I anything
+in the diversion.
+.I anything
+is read in copy mode.
+When the diversion is reread,
+.I anything
+will be interpreted.
+.I anything
+may not contain newlines; use
+.B \e!\&
+if you want to embed newlines in a diversion.
+The escape sequence
+.B \e?\&
+is also recognised in copy mode and turned into a single internal
+code; it is this code that terminates
+.IR anything .
+Thus
+.RS
+.RS
+.ft B
+.nf
+.ne 15
+\&.nr x 1
+\&.nf
+\&.di d
+\e?\e\e?\e\e\e\e?\e\e\e\e\e\e\e\enx\e\e\e\e?\e\e?\e?
+\&.di
+\&.nr x 2
+\&.di e
+\&.d
+\&.di
+\&.nr x 3
+\&.di f
+\&.e
+\&.di
+\&.nr x 4
+\&.f
+.fi
+.ft
+.RE
+.RE
+.IP
+will print
+.BR 4 .
+.TP
+.B \e/
+This increases the width of the preceding character so that
+the spacing between that character and the following character
+will be correct if the following character is a roman character.
+For example, if an italic f is immediately followed by a roman
+right parenthesis, then in many fonts the top right portion of the f
+will overlap the top left of the right parenthesis producing \fIf\fR)\fR,
+which is ugly.
+Inserting
+.B \e/
+produces
+.ie \n(.g \fIf\/\fR)\fR
+.el \fIf\|\fR)\fR
+and avoids this problem.
+It is a good idea to use this escape sequence whenever an
+italic character is immediately followed by a roman character without any
+intervening space.
+.TP
+.B \e,
+This modifies the spacing of the following character so that the spacing
+between that character and the preceding character will correct if
+the preceding character is a roman character.
+For example, inserting
+.B \e,
+between the parenthesis and the f changes
+\fR(\fIf\fR to
+.ie \n(.g \fR(\,\fIf\fR.
+.el \fR(\^\fIf\fR.
+It is a good idea to use this escape sequence whenever a
+roman character is immediately followed by an italic character without any
+intervening space.
+.TP
+.B \e)
+Like
+.B \e&
+except that it behaves like a character declared with the
+.B cflags
+request to be transparent for the purposes of end of sentence recognition.
+.TP
+.B \e~
+This produces an unbreakable space that stretches like a normal inter-word
+space when a line is adjusted.
+.TP
+.B \e:
+This causes the insertion of a zero-width break point.
+It is equal to
+.B \e%
+but without insertion of a soft hyphen character.
+.TP
+.B \e#
+Everything up to and including the next newline is ignored.
+This is interpreted in copy mode.
+This is like
+.B \e"
+except that
+.B \e"
+does not ignore the terminating newline.
+.
+.SS New requests
+.
+.TP
+.BI .aln\ xx\ yy
+Create an alias
+.I xx
+for number register object named
+.IR yy .
+The new name and the old name will be exactly equivalent.
+If
+.I yy
+is undefined, a warning of type
+.B reg
+will be generated, and the request will be ignored.
+.TP
+.BI .als\ xx\ yy
+Create an alias
+.I xx
+for request, string, macro, or diversion object named
+.IR yy .
+The new name and the old name will be exactly equivalent (it is similar to a
+hard rather than a soft link).
+If
+.I yy
+is undefined, a warning of type
+.B mac
+will be generated, and the request will be ignored.
+The
+.BR de ,
+.BR am ,
+.BR di ,
+.BR da ,
+.BR ds ,
+and
+.B as
+requests only create a new object if the name of the macro, diversion
+or string diversion is currently undefined or if it is defined to be a
+request; normally they modify the value of an existing object.
+.TP
+.BI .asciify\ xx
+This request `unformats' the diversion
+.I xx
+in such a way that
+.SM ASCII
+and space characters (and some escape sequences) that were formatted and
+diverted into
+.I xx
+will be treated like ordinary input characters when
+.I xx
+is reread.
+Useful for diversions in conjunction with the
+.B .writem
+request.
+It can be also used for gross hacks; for example, this
+.RS
+.IP
+.ne 7v+\n(.Vu
+.ft B
+.nf
+\&.tr @.
+\&.di x
+\&@nr n 1
+\&.br
+\&.di
+\&.tr @@
+\&.asciify x
+\&.x
+.fi
+.RE
+.IP
+will set register
+.B n
+to 1.
+Note that glyph information (font, font size, etc.) is not preserved; use
+.B .unformat
+instead.
+.TP
+.B .backtrace
+Print a backtrace of the input stack on stderr.
+.TP
+.BI .blm\ xx
+Set the blank line macro to
+.IR xx .
+If there is a blank line macro,
+it will be invoked when a blank line is encountered instead of the usual
+troff behaviour.
+.TP
+.BI .box\ xx
+.TQ
+.BI .boxa\ xx
+These requests are similar to the
+.B di
+and
+.B da
+requests with the exception that a partially filled line will not become
+part of the diversion (i.e., the diversion always starts with a new line)
+but restored after ending the diversion, discarding the partially filled
+line which possibly comes from the diversion.
+.TP
+.B .break
+Break out of a while loop.
+See also the
+.B while
+and
+.B continue
+requests.
+Be sure not to confuse this with the
+.B br
+request.
+.TP
+.BI .cflags\ n\ c1\ c2\|.\|.\|.
+Characters
+.IR c1 ,
+.IR c2 ,\|.\|.\|.
+have properties determined by
+.IR n ,
+which is ORed from the following:
+.RS
+.TP
+1
+the character ends sentences
+(initially characters
+.B .?!\&
+have this property);
+.TP
+2
+lines can be broken before the character
+(initially no characters have this property);
+a line will not be broken at a character with this property
+unless the characters on each side both have non-zero
+hyphenation codes.
+.TP
+4
+lines can be broken after the character
+(initially characters
+.B \-\e(hy\e(em
+have this property);
+a line will not be broken at a character with this property
+unless the characters on each side both have non-zero
+hyphenation codes.
+.TP
+8
+the character overlaps horizontally
+(initially characters
+.B \e(ul\e(rn\e(ru
+have this property);
+.TP
+16
+the character overlaps vertically
+(initially character
+.B \e(br
+has this property);
+.TP
+32
+an end of sentence character followed by any number of characters
+with this property will be treated
+as the end of a sentence if followed by a newline or two spaces;
+in other words
+the character is transparent for the purposes of end of sentence
+recognition;
+this is the same as having a zero space factor in \*(tx
+(initially characters
+.B \(ts')]*\e(dg\e(rq
+have this property).
+.RE
+.TP
+.BI .char\ c\ string
+Define character
+.I c
+to be
+.IR string .
+Every time character
+.I c
+needs to be printed,
+.I string
+will be processed in a temporary environment and the result
+will be wrapped up into a single object.
+Compatibility mode will be turned off
+and the escape character will be set to
+.B \e
+while
+.I string
+is being processed.
+Any emboldening, constant spacing or track kerning will be applied
+to this object rather than to individual characters in
+.IR string .
+A character defined by this request can be used just like
+a normal character provided by the output device.
+In particular other characters can be translated to it
+with the
+.B tr
+request;
+it can be made the leader character by the
+.B lc
+request;
+repeated patterns can be drawn with the character using the
+.B \el
+and
+.B \eL
+escape sequences;
+words containing the character can be hyphenated
+correctly, if the
+.B hcode
+request is used to give the character a hyphenation code.
+There is a special anti-recursion feature:
+use of character within the character's definition
+will be handled like normal characters not defined with
+.BR char .
+A character definition can be removed with the
+.B rchar
+request.
+.TP
+.BI .chop\ xx
+Chop the last character off macro, string, or diversion
+.IR xx .
+This is useful for removing the newline from the end of diversions
+that are to be interpolated as strings.
+.TP
+.BI .close\ stream
+Close the stream named
+.IR stream ;
+.I stream
+will no longer be an acceptable argument to the
+.B write
+request.
+See the
+.B open
+request.
+.TP
+.B .continue
+Finish the current iteration of a while loop.
+See also the
+.B while
+and
+.B break
+requests.
+.TP
+.BI .cp\ n
+If
+.I n
+is non-zero or missing, enable compatibility mode, otherwise
+disable it.
+In compatibility mode, long names are not recognised, and the
+incompatibilities caused by long names do not arise.
+.TP
+.BI .dei\ xx\ yy
+Define macro indirectly.
+The following example
+.RS
+.IP
+.ne 2v+\n(.Vu
+.ft B
+.nf
+\&.ds xx aa
+\&.ds yy bb
+\&.dei xx yy
+.fi
+.RE
+.IP
+is equivalent to
+.RS
+.IP
+.B
+\&.de aa bb
+.RE
+.TP
+.BI .do\ xxx
+Interpret
+.I .xxx
+with compatibility mode disabled.
+For example,
+.RS
+.IP
+.B
+\&.do fam T
+.LP
+would have the same effect as
+.IP
+.B
+\&.fam T
+.LP
+except that it would work even if compatibility mode had been enabled.
+Note that the previous compatibility mode is restored before any files
+sourced by
+.I xxx
+are interpreted.
+.RE
+.TP
+.B .ecs
+Save current escape character.
+.TP
+.B .ecr
+Restore escape character saved with
+.BR ecs .
+Without a previous call to
+.BR ecs ,
+.RB ` \e '
+will be the new escape character.
+.TP
+.BI .evc\ xx
+Copy the contents of environment
+.I xx
+to the current environment.
+No pushing or popping of environents will be done.
+.TP
+.BI .fam\ xx
+Set the current font family to
+.IR xx .
+The current font family is part of the current environment.
+If
+.I xx
+is missing, switch back to previous font family.
+See the description of the
+.B sty
+request for more information on font families.
+.TP
+.BI .fspecial\ f\ s1\ s2\|.\|.\|.
+When the current font is
+.IR f ,
+fonts
+.IR s1 ,
+.IR s2 ,\|.\|.\|.
+will be special, that is, they will searched for characters not in
+the current font.
+Any fonts specified in the
+.B special
+request will be searched after fonts specified in the
+.B fspecial
+request.
+.TP
+.BI .ftr\ f\ g
+Translate font
+.I f
+to
+.IR g .
+Whenever a font named
+.I f
+is referred to in
+.B \ef
+escape sequence,
+or in the
+.BR ft ,
+.BR ul ,
+.BR bd ,
+.BR cs ,
+.BR tkf ,
+.BR special ,
+.BR fspecial ,
+.BR fp ,
+or
+.BR sty
+requests,
+font
+.I g
+will be used.
+If
+.I g
+is missing,
+or equal to
+.I f
+then font
+.I f
+will not be translated.
+.TP
+.BI .hcode \ c1\ code1\ c2\ code2\|.\|.\|.
+Set the hyphenation code of character
+.I c1
+to
+.I code1
+and that of
+.I c2
+to
+.IR code2 .
+A hyphenation code must be a single input
+character (not a special character) other than a digit or a space.
+Initially each lower-case letter has a hyphenation code, which
+is itself, and each upper-case letter has a hyphenation code
+which is the lower case version of itself.
+See also the
+.B hpf
+request.
+.TP
+.BI .hla\ lang
+Set the current hyphenation language to
+.IR lang .
+Hyphenation exceptions specified with the
+.B hw
+request and hyphenation patterns specified with the
+.B hpf
+request are both associated with the current hyphenation language.
+The
+.B hla
+request is usually invoked by the
+.B troffrc
+file.
+.TP
+.BI .hlm\ n
+Set the maximum number of consecutive hyphenated lines to
+.IR n .
+If
+.I n
+is negative, there is no maximum.
+The default value is \-1.
+This value is associated with the current environment.
+Only lines output from an environment count towards the maximum associated
+with that environment.
+Hyphens resulting from
+.B \e%
+are counted; explicit hyphens are not.
+.TP
+.BI .hpf\ file
+Read hyphenation patterns from
+.IR file ;
+this will be searched for in the same way that
+.IB name .tmac
+is searched for when the
+.BI \-m name
+option is specified.
+It should have the same format as the argument to
+the \epatterns primitive in \*(tx;
+the letters appearing in this file are interpreted as hyphenation
+codes.
+A
+.B %
+character in the patterns file introduces a comment that continues
+to the end of the line.
+The set of hyphenation patterns is associated with the current language
+set by the
+.B hla
+request.
+The
+.B hpf
+request
+is usually invoked by the
+.B troffrc
+file.
+.TP
+.BI .hym\ n
+Set the
+.I hyphenation margin
+to
+.IR n :
+when the current adjustment mode is not
+.BR b ,
+the line will not be hyphenated if the line is no more than
+.I n
+short.
+The default hyphenation margin is 0.
+The default scaling indicator for this request is
+.IR m .
+The hyphenation margin is associated with the current environment.
+The current hyphenation margin is available in the
+.B \en[.hym]
+register.
+.TP
+.BI .hys\ n
+Set the
+.I hyphenation space
+to
+.IR n :
+when the current adjustment mode is
+.B b
+don't hyphenate the line if the line can be justified by adding no more than
+.I n
+extra space to each word space.
+The default hyphenation space is 0.
+The default scaling indicator for this request is
+.BR m .
+The hyphenation space is associated with the current environment.
+The current hyphenation space is available in the
+.B \en[.hys]
+register.
+.TP
+.BI .kern\ n
+If
+.I n
+is non-zero or missing, enable pairwise kerning, otherwise disable it.
+.TP
+.BI .length\ xx\ string
+Compute the length of
+.I string
+and return it in the number register
+.I xx
+(which is not necessarily defined before).
+.TP
+.BI .linetabs\ n
+If
+.I n
+is non-zero or missing, enable line-tabs mode, otherwise disable it (which
+is the default).
+In line-tabs mode, tab distances are computed relative to the (current)
+output line.
+Otherwise they are taken relative to the input line.
+For example, the following
+.RS
+.IP
+.ne 6v+\n(.Vu
+.ft B
+.nf
+\&.ds x a\et\ec
+\&.ds y b\et\ec
+\&.ds z c
+\&.ta 1i 3i
+\e*x
+\e*y
+\e*z
+.fi
+.RE
+.IP
+yields
+.RS
+.IP
+a b c
+.RE
+.IP
+In line-tabs mode, the same code gives
+.RS
+.IP
+a b c
+.RE
+.IP
+Line-tabs mode is associated with the current environment; the read-only
+number register
+.B \\en[.linetabs]
+is set to\~1 if in line-tabs mode, and 0 otherwise.
+.TP
+.BI .mso\ file
+The same as the
+.B so
+request except that
+.I file
+is searched for in the same directories as macro files for the
+the
+.B \-m
+command line option.
+If the file name to be included
+has the form
+.IB name .tmac
+and it isn't found,
+.B mso
+tries to include
+.BI tmac. name
+instead and vice versa.
+.TP
+.BI .nop \ anything
+Execute
+.IR anything .
+This is similar to `.if\ 1'.
+.TP
+.B .nroff
+Make the
+.B n
+built-in condition true
+and the
+.B t
+built-in condition false.
+This can be reversed using the
+.B troff
+request.
+.TP
+.BI .open\ stream\ filename
+Open
+.I filename
+for writing and associate the stream named
+.I stream
+with it.
+See also the
+.B close
+and
+.B write
+requests.
+.TP
+.BI .opena\ stream\ filename
+Like
+.BR open ,
+but if
+.I filename
+exists, append to it instead of truncating it.
+.TP
+.B .pnr
+Print the names and contents of all currently defined number registers
+on stderr.
+.TP
+.BI .psbb \ filename
+Get the bounding box of a PostScript image
+.IR filename .
+This file must conform to Adobe's Document Structuring Conventions; the
+command looks for a
+.B %%BoundingBox
+comment to extract the bounding box values.
+After a successful call, the coordinates (in PostScript units) of the lower
+left and upper right corner can be found in the registers
+.BR \en[llx] ,
+.BR \en[lly] ,
+.BR \en[urx] ,
+and
+.BR \en[ury] ,
+respectively.
+If some error has occurred, the four registers are set to zero.
+.TP
+.BI .pso \ command
+This behaves like the
+.B so
+request except that input comes from the standard output of
+.IR command .
+.TP
+.B .ptr
+Print the names and positions of all traps (not including input line
+traps and diversion traps) on stderr. Empty slots in the page trap
+list are printed as well, because they can affect the priority of
+subsequently planted traps.
+.TP
+.BI .rchar\ c1\ c2\|.\|.\|.
+Remove the definitions of characters
+.IR c1 ,
+.IR c2 ,\|.\|.\|.
+This undoes the effect of a
+.B char
+request.
+.TP
+.B .return
+Within a macro, return immediately.
+No effect otherwise.
+.TP
+.B .rj
+.TQ
+.BI .rj\ n
+Right justify the next
+.I n
+input lines.
+Without an argument right justify the next input line.
+The number of lines to be right justified is available in the
+.B \en[.rj]
+register.
+This implicitly does
+.BR .ce\ 0 .
+The
+.B ce
+request implicitly does
+.BR .rj\ 0 .
+.TP
+.BI .rnn \ xx\ yy
+Rename number register
+.I xx
+to
+.IR yy .
+.TP
+.BI .shc\ c
+Set the soft hyphen character to
+.IR c .
+If
+.I c
+is omitted,
+the soft hyphen character will be set to the default
+.BR \e(hy .
+The soft hyphen character is the character which will be inserted
+when a word is hyphenated at a line break.
+If the soft hyphen character does not exist in the font of the character
+immediately preceding a potential break point,
+then the line will not be broken at that point.
+Neither definitions (specified with the
+.B char
+request)
+nor translations (specified with the
+.B tr
+request)
+are considered when finding the soft hyphen character.
+.TP
+.BI .shift\ n
+In a macro, shift the arguments by
+.I n
+positions:
+argument
+.I i
+becomes argument
+.IR i \- n ;
+arguments 1 to
+.I n
+will no longer be available.
+If
+.I n
+is missing,
+arguments will be shifted by 1.
+Shifting by negative amounts is currently undefined.
+.TP
+.BI .special\ s1\ s2\|.\|.\|.
+Fonts
+.IR s1 ,
+.IR s2 ,
+are special and will be searched for characters not in the
+current font.
+.TP
+.BI .sty\ n\ f
+Associate style
+.I f
+with font position
+.IR n .
+A font position can be associated either with a font or
+with a style.
+The current font is the index of a font position and so is also
+either a font or a style.
+When it is a style, the font that is actually used is the font the
+name of which is the concatenation of the name of the current family
+and the name of the current style.
+For example, if the current font is 1 and font position 1 is
+associated with style
+.B R
+and the current
+font family is
+.BR T ,
+then font
+.BR TR
+will be used.
+If the current font is not a style, then the current family is ignored.
+When the requests
+.BR cs ,
+.BR bd ,
+.BR tkf ,
+.BR uf ,
+or
+.B fspecial
+are applied to a style,
+then they will instead be applied to the member of the
+current family corresponding to that style.
+The default family can be set with the
+.B \-f
+option.
+The styles command in the
+.SM DESC
+file controls which font positions
+(if any) are initially associated with styles rather than fonts.
+.TP
+.BI .substring\ xx\ n1\ [ n2 ]
+Replace the string in register
+.I xx
+with the substring defined by the indices
+.I n1
+and
+.IR n2 .
+The first character in the string has index one.
+If
+.I n2
+is omitted, it is taken to be equal to the string's length. If the
+index value
+.I n1
+or
+.I n2
+is negative or zero, it will be counted from the end of the string,
+going backwards: The last character has index 0, the character before
+the last character has index -1, etc.
+.TP
+.BI .tkf\ f\ s1\ n1\ s2\ n2
+Enable track kerning for font
+.IR f .
+When the current font is
+.I f
+the width of every character will be increased by an amount
+between
+.I n1
+and
+.IR n2 ;
+when the current point size is less than or equal to
+.I s1
+the width will be increased by
+.IR n1 ;
+when it is greater than or equal to
+.I s2
+the width will be increased by
+.IR n2 ;
+when the point size is greater than or equal to
+.I s1
+and less than or equal to
+.I s2
+the increase in width is a linear function of the point size.
+.TP
+.BI .tm1\ string
+Similar to the
+.B tm
+request,
+.I string
+is read in copy mode and written on the standard error, but an initial
+double quote in
+.I string
+is stripped off to allow initial blanks.
+.TP
+.BI .tmc\ string
+Similar to
+.BR tm1
+but without writing a final newline.
+.TP
+.BI .trf\ filename
+Transparently output the contents of file
+.IR filename .
+Each line is output as it would be were it preceded by
+.BR \e! ;
+however, the lines are not subject to copy-mode interpretation.
+If the file does not end with a newline, then a newline will
+be added.
+For example, you can define a macro
+.I x
+containing the contents of file
+.IR f ,
+using
+.RS
+.IP
+.BI .di\ x
+.br
+.BI .trf\ f
+.br
+.B .di
+.LP
+Unlike with the
+.B cf
+request,
+the file cannot contain characters such as
+.SM NUL
+that are not legal troff input characters.
+.RE
+.TP
+.B .trnt abcd
+This is the same as the
+.B tr
+request except that the translations do not apply to text that is
+transparently throughput into a diversion with
+.BR \e! .
+For example,
+.RS
+.IP
+.nf
+.ft B
+\&.tr ab
+\&.di x
+\e!.tm a
+\&.di
+\&.x
+.fi
+.ft
+.LP
+will print
+.BR b ;
+if
+.B trnt
+is used instead of
+.B tr
+it will print
+.BR a .
+.RE
+.TP
+.B .troff
+Make the
+.B n
+built-in condition false,
+and the
+.B t
+built-in condition true.
+This undoes the effect of the
+.B nroff
+request.
+.TP
+.BI .unformat\ xx
+This request `unformats' the diversion
+.IR xx .
+Contrary to the
+.B .asciify
+request, which tries to convert formatted elements of the diversion back
+to input tokens as much as possible,
+.B .unformat
+will only handle tabs and spaces between words (usually caused by spaces
+or newlines in the input) specially.
+The former are treated as if they were input tokens, and the latter are
+stretchable again.
+Note that the vertical size of lines is not preserved.
+Glyph information (font, font size, space width, etc.) is retained.
+Useful in conjunction with the
+.B .box
+and
+.B .boxa
+requests.
+.TP
+.BI .vpt\ n
+Enable vertical position traps if
+.I n
+is non-zero, disable them otherwise.
+Vertical position traps are traps set by the
+.B wh
+or
+.B dt
+requests.
+Traps set by the
+.B it
+request are not vertical position traps.
+The parameter that controls whether vertical position traps are enabled
+is global.
+Initially vertical position traps are enabled.
+.TP
+.BI .warn\ n
+Control warnings.
+.I n
+is the sum of the numbers associated with each warning that is to be enabled;
+all other warnings will be disabled.
+The number associated with each warning is listed in the `Warnings' section.
+For example,
+.B .warn 0
+will disable all warnings, and
+.B .warn 1
+will disable all warnings except that about missing characters.
+If
+.I n
+is not given,
+all warnings will be enabled.
+.TP
+.BI .while \ c\ anything
+While condition
+.I c
+is true, accept
+.I anything
+as input;
+.I c
+can be any condition acceptable to an
+.B if
+request;
+.I anything
+can comprise multiple lines if the first line starts with
+.B \e{
+and the last line ends with
+.BR \e} .
+See also the
+.B break
+and
+.B continue
+requests.
+.TP
+.BI .write\ stream\ anything
+Write
+.I anything
+to the stream named
+.IR stream .
+.I stream
+must previously have been the subject of an
+.B open
+request.
+.I anything
+is read in copy mode;
+a leading
+.B \(ts
+will be stripped.
+.TP
+.BI .writem\ stream\ xx
+Write the contents of the macro or string
+.I xx
+to the stream named
+.IR stream .
+.I stream
+must previously have been the subject of an
+.B open
+request.
+.I xx
+is read in copy mode.
+.
+.SS Extended requests
+.
+.TP
+.BI .cf\ filename
+When used in a diversion, this will embed in the diversion an object which,
+when reread, will cause the contents of
+.I filename
+to be transparently copied through to the output.
+In UNIX troff, the
+contents of
+.I filename
+is immediately copied through to the output regardless of whether
+there is a current diversion; this behaviour is so anomalous that it
+must be considered a bug.
+.TP
+.BI .ev\ xx
+If
+.I xx
+is not a number, this will switch to a named environment called
+.IR xx .
+The environment should be popped with a matching
+.B ev
+request without any arguments, just as for numbered environments.
+There is no limit on the number of named environments; they will be
+created the first time that they are referenced.
+.TP
+.BI .fp\ n\ f1\ f2
+The
+.B fp
+request has an optional third argument.
+This argument gives the external name of the font,
+which is used for finding the font description file.
+The second argument gives the internal name of the font
+which is used to refer to the font in troff after it has been mounted.
+If there is no third argument then the internal name will be used
+as the external name.
+This feature allows you to use fonts with long names in compatibility mode.
+.TP
+.BI .ss\ m\ n
+When two arguments are given to the
+.B ss
+request, the second argument gives the
+.IR "sentence space size" .
+If the second argument is not given, the sentence space size
+will be the same as the word space size.
+Like the word space size, the sentence space is in units of
+one twelfth of the spacewidth parameter for the current font.
+Initially both the word space size and the sentence
+space size are 12.
+Contrary to UNIX troff, GNU troff handles this request in nroff mode
+also (if not in compatibility mode); a given value is then rounded down
+to the nearest multiple of\~12.
+The sentence space size is used in two circumstances:
+if the end of a sentence occurs at the end of a line in fill mode, then
+both an inter-word space and a sentence space will be added;
+if two spaces follow the end of a sentence in the middle of a line,
+then the second space will be a sentence space.
+Note that the behaviour of UNIX troff will be exactly
+that exhibited by GNU troff if a second argument is never given to the
+.B ss
+request.
+In GNU troff, as in UNIX troff, you should always
+follow a sentence with either a newline or two spaces.
+.TP
+.BI .ta\ n1\ n2\|.\|.\|.nn \ T\ r1\ r2\|.\|.\|.\|rn
+Set tabs at positions
+.IR n1 ,
+.IR n2 ,\|.\|.\|.\|,
+.I nn
+and then set tabs at
+.IR nn + r1 ,
+.IR nn + r2 ,\|.\|.\|.\|.\|,
+.IR nn + rn
+and then at
+.IR nn + rn + r1 ,
+.IR nn + rn + r2 ,\|.\|.\|.\|,
+.IR nn + rn + rn ,
+and so on.
+For example,
+.RS
+.IP
+.B
+\&.ta T .5i
+.LP
+will set tabs every half an inch.
+.RE
+.
+.SS New number registers
+.
+The following read-only registers are available:
+.TP
+.B \en[.C]
+1 if compatibility mode is in effect, 0 otherwise.
+.TP
+.B \en[.cdp]
+The depth of the last character added to the current environment.
+It is positive if the character extends below the baseline.
+.TP
+.B \en[.ce]
+The number of lines remaining to be centered, as set by the
+.B ce
+request.
+.TP
+.B \en[.cht]
+The height of the last character added to the current environment.
+It is positive if the character extends above the baseline.
+.TP
+.B \en[.csk]
+The skew of the last character added to the current environment.
+The
+.I skew
+of a character is how far to the right of the center of a character
+the center of an accent over that character should be placed.
+.TP
+.B \en[.ev]
+The name or number of the current environment.
+This is a string-valued register.
+.TP
+.B \en[.fam]
+The current font family.
+This is a string-valued register.
+.TP
+.B \en[.fp]
+The number of the next free font position.
+.TP
+.B \en[.g]
+Always 1.
+Macros should use this to determine whether they are running
+under GNU troff.
+.TP
+.B \en[.hla]
+The current hyphenation language as set by the
+.B hla
+request.
+.TP
+.B \en[.hlc]
+The number of immediately preceding consecutive hyphenated lines.
+.TP
+.B \en[.hlm]
+The maximum allowed number of consecutive hyphenated lines, as set by the
+.B hlm
+request.
+.TP
+.B \en[.hy]
+The current hyphenation flags (as set by the
+.B hy
+request).
+.TP
+.B \en[.hym]
+The current hyphenation margin (as set by the
+.B hym
+request).
+.TP
+.B \en[.hys]
+The current hyphenation space (as set by the
+.B hys
+request).
+.TP
+.B \en[.in]
+The indent that applies to the current output line.
+.TP
+.B \en[.int]
+Set to a positive value if last output line is interrupted (i.e., if it
+contains
+.IR \ec ).
+.TP
+.B \en[.kern]
+.B 1
+if pairwise kerning is enabled,
+.B 0
+otherwise.
+.TP
+.B \en[.lg]
+The current ligature mode (as set by the
+.B lg
+request).
+.TP
+.B \en[.linetabs]
+The current line-tabs mode (as set by the
+.B linetabs
+request).
+.TP
+.B \en[.ll]
+The line length that applies to the current output line.
+.TP
+.B \en[.lt]
+The title length as set by the
+.B lt
+request.
+.TP
+.B \en[.ne]
+The amount of space that was needed in the last
+.B ne
+request that caused a trap to be sprung.
+Useful in conjunction with the
+.B \en[.trunc]
+register.
+.TP
+.B \en[.pn]
+The number of the next page:
+either the value set by a
+.B pn
+request, or the number of the current page plus 1.
+.TP
+.B \en[.ps]
+The current pointsize in scaled points.
+.TP
+.B \en[.psr]
+The last-requested pointsize in scaled points.
+.TP
+.B \en[.rj]
+The number of lines to be right-justified as set by the
+.B rj
+request.
+.TP
+.B \en[.sr]
+The last requested pointsize in points as a decimal fraction.
+This is a string-valued register.
+.TP
+.B \en[.tabs]
+A string representation of the current tab settings suitable for use as
+an argument to the
+.B ta
+request.
+.TP
+.B \en[.trunc]
+The amount of vertical space truncated by the most recently sprung
+vertical position trap, or,
+if the trap was sprung by a
+.B ne
+request,
+minus the amount of vertical motion produced by the
+.B ne
+request.
+In other words, at the point a trap is sprung, it represents the difference
+of what the vertical position would have been but for the trap,
+and what the vertical position actually is.
+Useful in conjunction with the
+.B \en[.ne]
+register.
+.TP
+.B \en[.ss]
+.TQ
+.B \en[.sss]
+These give the values of the parameters set by the
+first and second arguments of the
+.B ss
+request.
+.TP
+.B \en[.vpt]
+1 if vertical position traps are enabled, 0 otherwise.
+.TP
+.B \en[.warn]
+The sum of the numbers associated with each of the currently enabled
+warnings.
+The number associated with each warning is listed in the `Warnings'
+subsection.
+.TP
+.B \en[.x]
+The major version number.
+For example, if the version number is
+.B 1.03
+then
+.B \en[.x]
+will contain
+.BR 1 .
+.TP
+.B \en[.y]
+The minor version number.
+For example, if the version number is
+.B 1.03
+then
+.B \en[.y]
+will contain
+.BR 03 .
+.TP
+.B \en[.Y]
+The revision number of groff.
+.TP
+.B \en[llx]
+.TQ
+.B \en[lly]
+.TQ
+.B \en[urx]
+.TQ
+.B \en[ury]
+These four registers are set by the
+.B \&.psbb
+request and contain the bounding box values (in PostScript units) of a given
+PostScript image.
+.LP
+The following read/write registers are set by the
+.B \ew
+escape sequence:
+.TP
+.B \en[rst]
+.TQ
+.B \en[rsb]
+Like the
+.B st
+and
+.B sb
+registers, but takes account of the heights and depths of characters.
+.TP
+.B \en[ssc]
+The amount of horizontal space (possibly negative) that should
+be added to the last character before a subscript.
+.TP
+.B \en[skw]
+How far to right of the center of the last character
+in the
+.B \ew
+argument,
+the center of an accent from a roman font should be placed over that character.
+.LP
+Other available read/write number registers are:
+.TP
+.B \en[c.]
+The current input line number.
+.B \en[.c]
+is a read-only alias to this register.
+.TP
+.B \en[hp]
+The current horizontal position at input line.
+.TP
+.B \en[systat]
+The return value of the system() function executed by the last
+.B sy
+request.
+.TP
+.B \en[slimit]
+If greater than 0, the maximum number of objects on the input stack.
+If less than or equal to 0, there is no limit on the number of objects
+on the input stack. With no limit, recursion can continue until
+virtual memory is exhausted.
+.TP
+.B \en[year]
+The current year.
+Note that the traditional
+.B troff
+number register
+.B \en[yr]
+is the current year minus 1900.
+.
+.SS Miscellaneous
+.
+.B @g@troff
+predefines a single (read/write) string-based register,
+.BR \e*(.T ,
+which contains the argument given to the
+.B -T
+command line option, namely the current output device (for example,
+.I latin1
+or
+.IR ascii ).
+Note that this is not the same as the (read-only) number register
+.B \en[.T]
+which is defined to be\ 1 if
+.B troff
+is called with the
+.B -T
+command line option, and zero otherwise. This behaviour is different to
+UNIX troff.
+.LP
+Fonts not listed in the
+.SM DESC
+file are automatically mounted on the next available font position
+when they are referenced.
+If a font is to be mounted explicitly with the
+.B fp
+request on an unused font position,
+it should be mounted on the first unused font position,
+which can be found in the
+.B \en[.fp]
+register;
+although
+.B troff
+does not enforce this strictly,
+it will not allow a font to be mounted at a position whose number is much
+greater than that of any currently used position.
+.LP
+Interpolating a string does not hide existing macro arguments.
+Thus in a macro, a more efficient way of doing
+.IP
+.BI . xx\ \e\e$@
+.LP
+is
+.IP
+.BI \e\e*[ xx ]\e\e
+.LP
+If the font description file contains pairwise kerning information,
+characters from that font will be kerned.
+Kerning between two characters can be inhibited by placing a
+.B \e&
+between them.
+.LP
+In a string comparison in a condition,
+characters that appear at different input levels
+to the first delimiter character will not be recognised
+as the second or third delimiters.
+This applies also to the
+.B tl
+request.
+In a
+.B \ew
+escape sequence,
+a character that appears at a different input level to
+the starting delimiter character will not be recognised
+as the closing delimiter character.
+When decoding a macro argument that is delimited
+by double quotes, a character that appears at a different
+input level to the starting delimiter character will not
+be recognised as the closing delimiter character.
+The implementation of
+.B \e$@
+ensures that the double quotes surrounding an argument
+will appear the same input level, which will be different
+to the input level of the argument itself.
+In a long escape name
+.B ]
+will not be recognized as a closing delimiter except
+when it occurs at the same input level as the opening
+.BR ] .
+In compatibility mode, no attention is paid to the input-level.
+.LP
+There are some new types of condition:
+.TP
+.BI .if\ r xxx
+True if there is a number register named
+.IR xxx .
+.TP
+.BI .if\ d xxx
+True if there is a string, macro, diversion, or request named
+.IR xxx .
+.TP
+.BI .if\ c ch
+True if there is a character
+.IR ch
+available;
+.I ch
+is either an
+.SM ASCII
+character
+or a special character
+.BI \e( xx
+or
+.BI \e[ xxx ]\fR;
+the condition will also be true if
+.I ch
+has been defined by the
+.B char
+request.
+.LP
+The
+.B tr
+request can now map characters onto
+.BR \e~ .
+.
+.SS Warnings
+.
+The warnings that can be given by
+.B troff
+are divided into the following categories.
+The name associated with each warning is used by the
+.B \-w
+and
+.B \-W
+options;
+the number is used by the
+.B warn
+request, and by the
+.B .warn
+register.
+.nr x \w'\fBright-brace'+1n+\w'0000'u
+.ta \nxuR
+.TP \nxu+3n
+.BR char \t1
+Non-existent characters.
+This is enabled by default.
+.TP
+.BR number \t2
+Invalid numeric expressions.
+This is enabled by default.
+.TP
+.BR break \t4
+In fill mode, lines which could not be broken so that their length was
+less than the line length.
+This is enabled by default.
+.TP
+.BR delim \t8
+Missing or mismatched closing delimiters.
+.TP
+.BR el \t16
+Use of the
+.B el
+request with no matching
+.B ie
+request.
+.TP
+.BR scale \t32
+Meaningless scaling indicators.
+.TP
+.BR range \t64
+Out of range arguments.
+.TP
+.BR syntax \t128
+Dubious syntax in numeric expressions.
+.TP
+.BR di \t256
+Use of
+.B di
+or
+.B da
+without an argument when there is no current diversion.
+.TP
+.BR mac \t512
+Use of undefined strings, macros and diversions.
+When an undefined string, macro or diversion is used,
+that string is automatically defined as empty.
+So, in most cases, at most one warning will be given for
+each name.
+.TP
+.BR reg \t1024
+Use of undefined number registers.
+When an undefined number register is used,
+that register is automatically defined to have a value of 0.
+a definition is automatically made with a value of 0.
+So, in most cases, at most one warning will be given for
+use of a particular name.
+.TP
+.BR tab \t2048
+Inappropriate use of a tab character.
+Either use of a tab character where a number was expected,
+or use of tab character in an unquoted macro argument.
+.TP
+.BR right-brace \t4096
+Use of
+.B \e}
+where a number was expected.
+.TP
+.BR missing \t8192
+Requests that are missing non-optional arguments.
+.TP
+.BR input \t16384
+Illegal input characters.
+.TP
+.BR escape \t32768
+Unrecognized escape sequences.
+When an unrecognized escape sequence is encountered,
+the escape character is ignored.
+.TP
+.BR space \t65536
+Missing space between a request or macro and its argument.
+This warning will be given
+when an undefined name longer than two characters is encountered,
+and the first two characters of the name make a defined name.
+The request or macro will not be invoked.
+When this warning is given, no macro is automatically defined.
+This is enabled by default.
+This warning will never occur in compatibility mode.
+.TP
+.BR font \t131072
+Non-existent fonts.
+This is enabled by default.
+.TP
+.BR ig \t262144
+Illegal escapes in text ignored with the
+.B ig
+request.
+These are conditions that are errors when they do not occur
+in ignored text.
+.LP
+There are also names that can be used to refer to groups of warnings:
+.TP
+.B all
+All warnings except
+.BR di ,
+.B mac
+and
+.BR reg .
+It is intended that this covers all warnings
+that are useful with traditional macro packages.
+.TP
+.B w
+All warnings.
+.
+.SS Incompatibilities
+.
+.LP
+Long names cause some incompatibilities.
+UNIX troff will interpret
+.IP
+.B
+\&.dsabcd
+.LP
+as defining a string
+.B ab
+with contents
+.BR cd .
+Normally, GNU troff will interpret this as a call of a macro named
+.BR dsabcd .
+Also UNIX troff will interpret
+.B \e*[
+or
+.B \en[
+as references to a string or number register called
+.BR [ .
+In GNU troff, however, this will normally be interpreted as the start
+of a long name.
+In
+.I compatibility mode
+GNU troff will interpret these things in the traditional way.
+In compatibility mode, however, long names are not recognised.
+Compatibility mode can be turned on with the
+.B \-C
+command line option, and turned on or off with the
+.B cp
+request.
+The number register
+.B \en[.C]
+is 1 if compatibility mode is on, 0 otherwise.
+.LP
+GNU troff
+does not allow the use of the escape sequences
+.BR \\e\e|\e^\e&\e}\e{\e (space) \e'\e`\e-\e_\e!\e%\ec
+in names of strings, macros, diversions, number registers,
+fonts or environments; UNIX troff does.
+The
+.B \eA
+escape sequence may be helpful in avoiding use of these
+escape sequences in names.
+.LP
+Fractional pointsizes cause one noteworthy incompatibility.
+In UNIX troff the
+.B ps
+request ignores scale indicators and so
+.IP
+.B .ps\ 10u
+.LP
+will set the pointsize to 10 points, whereas in
+GNU troff it will set the pointsize to 10 scaled points.
+.LP
+In GNU troff there is a fundamental difference between unformatted,
+input characters, and formatted, output characters.
+Everything that affects how an output character
+will be output is stored with the character; once an output
+character has been constructed it is unaffected by any subsequent
+requests that are executed, including
+.BR bd ,
+.BR cs ,
+.BR tkf ,
+.BR tr ,
+or
+.B fp
+requests.
+Normally output characters are constructed from input
+characters at the moment immediately before the character
+is added to the current output line.
+Macros, diversions and strings are all, in fact, the same type
+of object; they contain lists of input characters and output
+characters in any combination.
+An output character does not behave like an input character
+for the purposes of macro processing; it does not inherit any
+of the special properties that the input character from which it
+was constructed might have had.
+For example,
+.IP
+.nf
+.ft B
+\&.di x
+\e\e\e\e
+\&.br
+\&.di
+\&.x
+.ft
+.fi
+.LP
+will print
+.B \e\e
+in GNU troff;
+each pair of input
+.BR \e s
+is turned into one output
+.B \e
+and the resulting output
+.BR \e s
+are not interpreted as escape characters when they are reread.
+UNIX troff would interpret them as escape characters
+when they were reread and would end up printing one
+.BR \e .
+The correct way to obtain a printable
+.B \e
+is to use the
+.B \ee
+escape sequence: this will always print a single instance of the
+current escape character, regardless of whether or not it is used in a
+diversion; it will also work in both GNU troff and UNIX troff.
+If you wish for some reason to store in a diversion an escape
+sequence that will be interpreted when the diversion is reread,
+you can either use the traditional
+.B \e!\&
+transparent output facility, or, if this is unsuitable, the new
+.B \e?\&
+escape sequence.
+.
+.
+.SH ENVIRONMENT
+.
+.
+.TP
+.SM
+.B GROFF_TMAC_PATH
+A colon separated list of directories in which to search for
+macro files.
+.B troff
+will scan directories given in
+the
+.B \-M
+option before these, and in standard directories (current directory if in
+unsafe mode, home directory,
+.BR @LOCALMACRODIR@ ,
+.BR @SYSTEMMACRODIR@ ,
+.BR @MACRODIR@ )
+after these.
+.TP
+.SM
+.B GROFF_TYPESETTER
+Default device.
+.TP
+.SM
+.B GROFF_FONT_PATH
+A colon separated list of directories in which to search for the
+.BI dev name
+directory.
+.B troff
+will scan directories given in the
+.B \-F
+option before these, and in standard directories
+.RB ( @FONTPATH@ )
+after these.
+.
+.
+.SH FILES
+.
+.
+.Tp \w'@FONTDIR@/devname/DESC'u+3n
+.B @MACRODIR@/troffrc
+Initialization file (called before any other macro package).
+.TP
+.B @MACRODIR@/troffrc-end
+Initialization file (called after any other macro package).
+.TP
+.BI @MACRODIR@/ name .tmac
+.TQ
+.BI @MACRODIR@/tmac. name
+Macro files
+.TP
+.BI @FONTDIR@/dev name /DESC
+Device description file for device
+.IR name .
+.TP
+.BI @FONTDIR@/dev name / F
+Font file for font
+.I F
+of device
+.IR name .
+.LP
+Note that
+.B troffrc
+and
+.B troffrc-end
+are neither searched in the current nor in the home directory by default for
+security reasons (even if the
+.B \-U
+option is given).
+Use the
+.B \-M
+command line option or the
+.B GROFF_TMAC_PATH
+environment variable to add these directories to the search path if
+necessary.
+.
+.
+.SH "SEE ALSO"
+.
+.
+.BR groff (@MAN1EXT@),
+.BR @g@tbl (@MAN1EXT@),
+.BR @g@pic (@MAN1EXT@),
+.BR @g@eqn (@MAN1EXT@),
+.BR @g@refer (@MAN1EXT@),
+.BR @g@soelim (@MAN1EXT@),
+.BR @g@grn (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR grodvi (@MAN1EXT@),
+.BR grotty (@MAN1EXT@),
+.BR grohtml (@MAN1EXT@),
+.BR grolj4 (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/utils/addftinfo/Makefile.sub b/contrib/groff/src/utils/addftinfo/Makefile.sub
new file mode 100644
index 000000000000..23848d54fc72
--- /dev/null
+++ b/contrib/groff/src/utils/addftinfo/Makefile.sub
@@ -0,0 +1,11 @@
+PROG=addftinfo
+MAN1=addftinfo.n
+XLIBS=$(LIBGROFF)
+OBJS=\
+ addftinfo.o \
+ guess.o
+CCSRCS=\
+ $(srcdir)/addftinfo.cc \
+ $(srcdir)/guess.cc
+HDRS=\
+ $(srcdir)/guess.h
diff --git a/contrib/groff/src/utils/addftinfo/addftinfo.cc b/contrib/groff/src/utils/addftinfo/addftinfo.cc
new file mode 100644
index 000000000000..43a81bcbcf7d
--- /dev/null
+++ b/contrib/groff/src/utils/addftinfo/addftinfo.cc
@@ -0,0 +1,218 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "guess.h"
+
+static void usage(FILE *stream);
+static void usage();
+static void version();
+static void convert_font(const font_params &, FILE *, FILE *);
+
+typedef int font_params::*param_t;
+
+static struct {
+ const char *name;
+ param_t par;
+} param_table[] = {
+ { "x-height", &font_params::x_height },
+ { "fig-height", &font_params::fig_height },
+ { "asc-height", &font_params::asc_height },
+ { "body-height", &font_params::body_height },
+ { "cap-height", &font_params::cap_height },
+ { "comma-depth", &font_params::comma_depth },
+ { "desc-depth", &font_params::desc_depth },
+ { "body-depth", &font_params::body_depth },
+};
+
+// These are all in thousandths of an em.
+// These values are correct for PostScript Times Roman.
+
+#define DEFAULT_X_HEIGHT 448
+#define DEFAULT_FIG_HEIGHT 676
+#define DEFAULT_ASC_HEIGHT 682
+#define DEFAULT_BODY_HEIGHT 676
+#define DEFAULT_CAP_HEIGHT 662
+#define DEFAULT_COMMA_DEPTH 143
+#define DEFAULT_DESC_DEPTH 217
+#define DEFAULT_BODY_DEPTH 177
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-v") || !strcmp(argv[i],"--version"))
+ version();
+ if (!strcmp(argv[i],"--help")) {
+ usage(stdout);
+ exit(0);
+ }
+ }
+ if (argc < 4)
+ usage();
+ int resolution;
+ if (sscanf(argv[argc-3], "%d", &resolution) != 1)
+ usage();
+ if (resolution <= 0)
+ fatal("resolution must be > 0");
+ int unitwidth;
+ if (sscanf(argv[argc-2], "%d", &unitwidth) != 1)
+ usage();
+ if (unitwidth <= 0)
+ fatal("unitwidth must be > 0");
+ font_params param;
+ const char *font = argv[argc-1];
+ param.italic = (font[0] != '\0' && strchr(font, '\0')[-1] == 'I');
+ param.em = (resolution*unitwidth)/72;
+ param.x_height = DEFAULT_X_HEIGHT;
+ param.fig_height = DEFAULT_FIG_HEIGHT;
+ param.asc_height = DEFAULT_ASC_HEIGHT;
+ param.body_height = DEFAULT_BODY_HEIGHT;
+ param.cap_height = DEFAULT_CAP_HEIGHT;
+ param.comma_depth = DEFAULT_COMMA_DEPTH;
+ param.desc_depth = DEFAULT_DESC_DEPTH;
+ param.body_depth = DEFAULT_BODY_DEPTH;
+ int i;
+ for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+ if (argv[i][1] == '-' && argv[i][2] == '\0') {
+ i++;
+ break;
+ }
+ if (i + 1 >= argc)
+ usage();
+ int j;
+ for (j = 0;; j++) {
+ if (j >= sizeof(param_table)/sizeof(param_table[0]))
+ fatal("parameter `%1' not recognized", argv[i] + 1);
+ if (strcmp(param_table[j].name, argv[i] + 1) == 0)
+ break;
+ }
+ if (sscanf(argv[i+1], "%d", &(param.*(param_table[j].par))) != 1)
+ fatal("invalid argument `%1'", argv[i+1]);
+ i++;
+ }
+ if (argc - i != 3)
+ usage();
+ errno = 0;
+ FILE *infp = fopen(font, "r");
+ if (infp == 0)
+ fatal("can't open `%1': %2", font, strerror(errno));
+ convert_font(param, infp, stdout);
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-v] [-param value] ... "
+ "resolution unitwidth font\n",
+ program_name);
+}
+static void usage()
+{
+ usage(stderr);
+ exit(1);
+}
+
+static void version()
+{
+ extern const char *Version_string;
+ printf("GNU addftinfo (groff) version %s\n", Version_string);
+ exit(0);
+}
+
+static int get_line(FILE *fp, string *p)
+{
+ int c;
+ p->clear();
+ while ((c = getc(fp)) != EOF) {
+ *p += char(c);
+ if (c == '\n')
+ break;
+ }
+ return p->length() > 0;
+}
+
+static void convert_font(const font_params &param, FILE *infp, FILE *outfp)
+{
+ string s;
+ while (get_line(infp, &s)) {
+ put_string(s, outfp);
+ if (s.length() >= 8
+ && strncmp(&s[0], "charset", 7))
+ break;
+ }
+ while (get_line(infp, &s)) {
+ s += '\0';
+ string name;
+ const char *p = s.contents();
+ while (csspace(*p))
+ p++;
+ while (*p != '\0' && !csspace(*p))
+ name += *p++;
+ while (csspace(*p))
+ p++;
+ for (const char *q = s.contents(); q < p; q++)
+ putc(*q, outfp);
+ char *next;
+ char_metric metric;
+ metric.width = (int)strtol(p, &next, 10);
+ if (next != p) {
+ printf("%d", metric.width);
+ p = next;
+ metric.type = (int)strtol(p, &next, 10);
+ if (next != p) {
+ name += '\0';
+ guess(name.contents(), param, &metric);
+ if (metric.sk == 0) {
+ if (metric.left_ic == 0) {
+ if (metric.ic == 0) {
+ if (metric.depth == 0) {
+ if (metric.height != 0)
+ printf(",%d", metric.height);
+ }
+ else
+ printf(",%d,%d", metric.height, metric.depth);
+ }
+ else
+ printf(",%d,%d,%d", metric.height, metric.depth, metric.ic);
+ }
+ else
+ printf(",%d,%d,%d,%d", metric.height, metric.depth, metric.ic,
+ metric.left_ic);
+ }
+ else
+ printf(",%d,%d,%d,%d,%d", metric.height, metric.depth, metric.ic,
+ metric.left_ic, metric.sk);
+ }
+ }
+ fputs(p, outfp);
+ }
+}
+
diff --git a/contrib/groff/src/utils/addftinfo/addftinfo.man b/contrib/groff/src/utils/addftinfo/addftinfo.man
new file mode 100644
index 000000000000..921f633639d7
--- /dev/null
+++ b/contrib/groff/src/utils/addftinfo/addftinfo.man
@@ -0,0 +1,107 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH ADDFTINFO @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+addftinfo \- add information to troff font files for use with groff
+.SH SYNOPSIS
+.B addftinfo
+[
+.B \-v
+]
+[
+.BI \- param\ value\fR.\|.\|.
+]
+.I res
+.I unitwidth
+.I font
+.SH DESCRIPTION
+.B addftinfo
+reads a troff font file
+and adds some additional font-metric information
+that is used by the groff system.
+The font file with the information added is written on the
+standard output.
+The information added is guessed using
+some parametric information about the font
+and assumptions
+about the traditional troff names for characters.
+The main information added is the heights and depths of characters.
+The
+.I res
+and
+.I unitwidth
+arguments should be the same as the corresponding parameters
+in the DESC file;
+.I font
+is the name of the file describing the font;
+if
+.I font
+ends with
+.B I
+the font will be assumed to be italic.
+.SH OPTIONS
+.B \-v
+prints the version number.
+.LP
+All other options changes one of the parameters that is used
+to derive the heights and depths.
+Like the existing quantities in the font
+file, each
+.I value
+is in
+.RI inches/ res
+for a font whose point size is
+.IR unitwidth .
+.I param
+must be one of:
+.TP
+.B x-height
+The height of lowercase letters without ascenders such as x.
+.TP
+.B fig-height
+The height of figures (digits).
+.TP
+.B asc-height
+The height of characters with ascenders, such as b, d or l.
+.TP
+.B body-height
+The height of characters such as parentheses.
+.TP
+.B cap-height
+The height of uppercase letters such as A.
+.TP
+.B comma-depth
+The depth of a comma.
+.TP
+.B desc-depth
+The depth of characters with descenders, such as p,q, or y.
+.TP
+.B body-depth
+The depth of characters such as parentheses.
+.LP
+.B addftinfo
+makes no attempt to use the specified parameters to guess
+the unspecified parameters.
+If a parameter is not specified the default will be used.
+The defaults are chosen to have the reasonable values for
+a Times font.
+.SH "SEE ALSO"
+.BR groff_font (@MAN5EXT@),
+.BR groff (@MAN1EXT@),
+.BR groff_char (@MAN7EXT@)
diff --git a/contrib/groff/src/utils/addftinfo/guess.cc b/contrib/groff/src/utils/addftinfo/guess.cc
new file mode 100644
index 000000000000..dcfd4c92cb9b
--- /dev/null
+++ b/contrib/groff/src/utils/addftinfo/guess.cc
@@ -0,0 +1,490 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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 "guess.h"
+
+void guess(const char *s, const font_params &param, char_metric *metric)
+{
+ int &height = metric->height;
+ int &depth = metric->depth;
+
+ metric->ic = 0;
+ metric->left_ic = 0;
+ metric->sk = 0;
+ height = 0;
+ depth = 0;
+ if (s[0] == '\0' || (s[1] != '\0' && s[2] != '\0'))
+ goto do_default;
+#define HASH(c1, c2) (((unsigned char)(c1) << 8) | (unsigned char)(c2))
+ switch (HASH(s[0], s[1])) {
+ default:
+ do_default:
+ if (metric->type & 01)
+ depth = param.desc_depth;
+ if (metric->type & 02)
+ height = param.asc_height;
+ else
+ height = param.x_height;
+ break;
+ case HASH('\\', '|'):
+ case HASH('\\', '^'):
+ case HASH('\\', '&'):
+ // these have zero height and depth
+ break;
+ case HASH('f', 0):
+ height = param.asc_height;
+ if (param.italic)
+ depth = param.desc_depth;
+ break;
+ case HASH('a', 0):
+ case HASH('c', 0):
+ case HASH('e', 0):
+ case HASH('m', 0):
+ case HASH('n', 0):
+ case HASH('o', 0):
+ case HASH('r', 0):
+ case HASH('s', 0):
+ case HASH('u', 0):
+ case HASH('v', 0):
+ case HASH('w', 0):
+ case HASH('x', 0):
+ case HASH('z', 0):
+ height = param.x_height;
+ break;
+ case HASH('i', 0):
+ height = param.x_height;
+ break;
+ case HASH('b', 0):
+ case HASH('d', 0):
+ case HASH('h', 0):
+ case HASH('k', 0):
+ case HASH('l', 0):
+ case HASH('F', 'i'):
+ case HASH('F', 'l'):
+ case HASH('f', 'f'):
+ case HASH('f', 'i'):
+ case HASH('f', 'l'):
+ height = param.asc_height;
+ break;
+ case HASH('t', 0):
+ height = param.asc_height;
+ break;
+ case HASH('g', 0):
+ case HASH('p', 0):
+ case HASH('q', 0):
+ case HASH('y', 0):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('j', 0):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('A', 0):
+ case HASH('B', 0):
+ case HASH('C', 0):
+ case HASH('D', 0):
+ case HASH('E', 0):
+ case HASH('F', 0):
+ case HASH('G', 0):
+ case HASH('H', 0):
+ case HASH('I', 0):
+ case HASH('J', 0):
+ case HASH('K', 0):
+ case HASH('L', 0):
+ case HASH('M', 0):
+ case HASH('N', 0):
+ case HASH('O', 0):
+ case HASH('P', 0):
+ case HASH('Q', 0):
+ case HASH('R', 0):
+ case HASH('S', 0):
+ case HASH('T', 0):
+ case HASH('U', 0):
+ case HASH('V', 0):
+ case HASH('W', 0):
+ case HASH('X', 0):
+ case HASH('Y', 0):
+ case HASH('Z', 0):
+ height = param.cap_height;
+ break;
+ case HASH('*', 'A'):
+ case HASH('*', 'B'):
+ case HASH('*', 'C'):
+ case HASH('*', 'D'):
+ case HASH('*', 'E'):
+ case HASH('*', 'F'):
+ case HASH('*', 'G'):
+ case HASH('*', 'H'):
+ case HASH('*', 'I'):
+ case HASH('*', 'K'):
+ case HASH('*', 'L'):
+ case HASH('*', 'M'):
+ case HASH('*', 'N'):
+ case HASH('*', 'O'):
+ case HASH('*', 'P'):
+ case HASH('*', 'Q'):
+ case HASH('*', 'R'):
+ case HASH('*', 'S'):
+ case HASH('*', 'T'):
+ case HASH('*', 'U'):
+ case HASH('*', 'W'):
+ case HASH('*', 'X'):
+ case HASH('*', 'Y'):
+ case HASH('*', 'Z'):
+ height = param.cap_height;
+ break;
+ case HASH('0', 0):
+ case HASH('1', 0):
+ case HASH('2', 0):
+ case HASH('3', 0):
+ case HASH('4', 0):
+ case HASH('5', 0):
+ case HASH('6', 0):
+ case HASH('7', 0):
+ case HASH('8', 0):
+ case HASH('9', 0):
+ case HASH('1', '2'):
+ case HASH('1', '4'):
+ case HASH('3', '4'):
+ height = param.fig_height;
+ break;
+ case HASH('(', 0):
+ case HASH(')', 0):
+ case HASH('[', 0):
+ case HASH(']', 0):
+ case HASH('{', 0):
+ case HASH('}', 0):
+ height = param.body_height;
+ depth = param.body_depth;
+ break;
+ case HASH('i', 's'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+ case HASH('*', 'a'):
+ case HASH('*', 'e'):
+ case HASH('*', 'i'):
+ case HASH('*', 'k'):
+ case HASH('*', 'n'):
+ case HASH('*', 'o'):
+ case HASH('*', 'p'):
+ case HASH('*', 's'):
+ case HASH('*', 't'):
+ case HASH('*', 'u'):
+ case HASH('*', 'w'):
+ height = param.x_height;
+ break;
+ case HASH('*', 'd'):
+ case HASH('*', 'l'):
+ height = param.asc_height;
+ break;
+ case HASH('*', 'g'):
+ case HASH('*', 'h'):
+ case HASH('*', 'm'):
+ case HASH('*', 'r'):
+ case HASH('*', 'x'):
+ case HASH('*', 'y'):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('*', 'b'):
+ case HASH('*', 'c'):
+ case HASH('*', 'f'):
+ case HASH('*', 'q'):
+ case HASH('*', 'z'):
+ height = param.asc_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('t', 's'):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('!', 0):
+ case HASH('?', 0):
+ case HASH('"', 0):
+ case HASH('#', 0):
+ case HASH('$', 0):
+ case HASH('%', 0):
+ case HASH('&', 0):
+ case HASH('*', 0):
+ case HASH('+', 0):
+ height = param.asc_height;
+ break;
+ case HASH('`', 0):
+ case HASH('\'', 0):
+ height = param.asc_height;
+ break;
+ case HASH('~', 0):
+ case HASH('^', 0):
+ case HASH('a', 'a'):
+ case HASH('g', 'a'):
+ height = param.asc_height;
+ break;
+ case HASH('r', 'u'):
+ case HASH('.', 0):
+ break;
+ case HASH(',', 0):
+ depth = param.comma_depth;
+ break;
+ case HASH('m', 'i'):
+ case HASH('-', 0):
+ case HASH('h', 'y'):
+ case HASH('e', 'm'):
+ height = param.x_height;
+ break;
+ case HASH(':', 0):
+ height = param.x_height;
+ break;
+ case HASH(';', 0):
+ height = param.x_height;
+ depth = param.comma_depth;
+ break;
+ case HASH('=', 0):
+ case HASH('e', 'q'):
+ height = param.x_height;
+ break;
+ case HASH('<', 0):
+ case HASH('>', 0):
+ case HASH('>', '='):
+ case HASH('<', '='):
+ case HASH('@', 0):
+ case HASH('/', 0):
+ case HASH('|', 0):
+ case HASH('\\', 0):
+ height = param.asc_height;
+ break;
+ case HASH('_', 0):
+ case HASH('u', 'l'):
+ case HASH('\\', '_'):
+ depth = param.em/4;
+ break;
+ case HASH('r', 'n'):
+ height = (param.em*3)/4;
+ break;
+ case HASH('s', 'r'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+ case HASH('b', 'u'):
+ case HASH('s', 'q'):
+ case HASH('d', 'e'):
+ case HASH('d', 'g'):
+ case HASH('f', 'm'):
+ case HASH('c', 't'):
+ case HASH('r', 'g'):
+ case HASH('c', 'o'):
+ case HASH('p', 'l'):
+ case HASH('*', '*'):
+ case HASH('s', 'c'):
+ case HASH('s', 'l'):
+ case HASH('=', '='):
+ case HASH('~', '='):
+ case HASH('a', 'p'):
+ case HASH('!', '='):
+ case HASH('-', '>'):
+ case HASH('<', '-'):
+ case HASH('u', 'a'):
+ case HASH('d', 'a'):
+ case HASH('m', 'u'):
+ case HASH('d', 'i'):
+ case HASH('+', '-'):
+ case HASH('c', 'u'):
+ case HASH('c', 'a'):
+ case HASH('s', 'b'):
+ case HASH('s', 'p'):
+ case HASH('i', 'b'):
+ case HASH('i', 'p'):
+ case HASH('i', 'f'):
+ case HASH('p', 'd'):
+ case HASH('g', 'r'):
+ case HASH('n', 'o'):
+ case HASH('p', 't'):
+ case HASH('e', 's'):
+ case HASH('m', 'o'):
+ case HASH('b', 'r'):
+ case HASH('d', 'd'):
+ case HASH('r', 'h'):
+ case HASH('l', 'h'):
+ case HASH('o', 'r'):
+ case HASH('c', 'i'):
+ height = param.asc_height;
+ break;
+ case HASH('l', 't'):
+ case HASH('l', 'b'):
+ case HASH('r', 't'):
+ case HASH('r', 'b'):
+ case HASH('l', 'k'):
+ case HASH('r', 'k'):
+ case HASH('b', 'v'):
+ case HASH('l', 'f'):
+ case HASH('r', 'f'):
+ case HASH('l', 'c'):
+ case HASH('r', 'c'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+#if 0
+ case HASH('%', '0'):
+ case HASH('-', '+'):
+ case HASH('-', 'D'):
+ case HASH('-', 'd'):
+ case HASH('-', 'd'):
+ case HASH('-', 'h'):
+ case HASH('.', 'i'):
+ case HASH('.', 'j'):
+ case HASH('/', 'L'):
+ case HASH('/', 'O'):
+ case HASH('/', 'l'):
+ case HASH('/', 'o'):
+ case HASH('=', '~'):
+ case HASH('A', 'E'):
+ case HASH('A', 'h'):
+ case HASH('A', 'N'):
+ case HASH('C', 's'):
+ case HASH('D', 'o'):
+ case HASH('F', 'c'):
+ case HASH('F', 'o'):
+ case HASH('I', 'J'):
+ case HASH('I', 'm'):
+ case HASH('O', 'E'):
+ case HASH('O', 'f'):
+ case HASH('O', 'K'):
+ case HASH('O', 'm'):
+ case HASH('O', 'R'):
+ case HASH('P', 'o'):
+ case HASH('R', 'e'):
+ case HASH('S', '1'):
+ case HASH('S', '2'):
+ case HASH('S', '3'):
+ case HASH('T', 'P'):
+ case HASH('T', 'p'):
+ case HASH('Y', 'e'):
+ case HASH('\\', '-'):
+ case HASH('a', '"'):
+ case HASH('a', '-'):
+ case HASH('a', '.'):
+ case HASH('a', '^'):
+ case HASH('a', 'b'):
+ case HASH('a', 'c'):
+ case HASH('a', 'd'):
+ case HASH('a', 'e'):
+ case HASH('a', 'h'):
+ case HASH('a', 'o'):
+ case HASH('a', 't'):
+ case HASH('a', '~'):
+ case HASH('b', 'a'):
+ case HASH('b', 'b'):
+ case HASH('b', 's'):
+ case HASH('c', '*'):
+ case HASH('c', '+'):
+ case HASH('f', '/'):
+ case HASH('f', 'a'):
+ case HASH('f', 'c'):
+ case HASH('f', 'o'):
+ case HASH('h', 'a'):
+ case HASH('h', 'o'):
+ case HASH('i', 'j'):
+ case HASH('l', 'A'):
+ case HASH('l', 'B'):
+ case HASH('l', 'C'):
+ case HASH('m', 'd'):
+ case HASH('n', 'c'):
+ case HASH('n', 'e'):
+ case HASH('n', 'm'):
+ case HASH('o', 'A'):
+ case HASH('o', 'a'):
+ case HASH('o', 'e'):
+ case HASH('o', 'q'):
+ case HASH('p', 'l'):
+ case HASH('p', 'p'):
+ case HASH('p', 's'):
+ case HASH('r', '!'):
+ case HASH('r', '?'):
+ case HASH('r', 'A'):
+ case HASH('r', 'B'):
+ case HASH('r', 'C'):
+ case HASH('r', 's'):
+ case HASH('s', 'h'):
+ case HASH('s', 's'):
+ case HASH('t', 'e'):
+ case HASH('t', 'f'):
+ case HASH('t', 'i'):
+ case HASH('t', 'm'):
+ case HASH('~', '~'):
+ case HASH('v', 'S'):
+ case HASH('v', 'Z'):
+ case HASH('v', 's'):
+ case HASH('v', 'z'):
+ case HASH('^', 'A'):
+ case HASH('^', 'E'):
+ case HASH('^', 'I'):
+ case HASH('^', 'O'):
+ case HASH('^', 'U'):
+ case HASH('^', 'a'):
+ case HASH('^', 'e'):
+ case HASH('^', 'i'):
+ case HASH('^', 'o'):
+ case HASH('^', 'u'):
+ case HASH('`', 'A'):
+ case HASH('`', 'E'):
+ case HASH('`', 'I'):
+ case HASH('`', 'O'):
+ case HASH('`', 'U'):
+ case HASH('`', 'a'):
+ case HASH('`', 'e'):
+ case HASH('`', 'i'):
+ case HASH('`', 'o'):
+ case HASH('`', 'u'):
+ case HASH('~', 'A'):
+ case HASH('~', 'N'):
+ case HASH('~', 'O'):
+ case HASH('~', 'a'):
+ case HASH('~', 'n'):
+ case HASH('~', 'o'):
+ case HASH('\'', 'A'):
+ case HASH('\'', 'C'):
+ case HASH('\'', 'E'):
+ case HASH('\'', 'I'):
+ case HASH('\'', 'O'):
+ case HASH('\'', 'U'):
+ case HASH('\'', 'a'):
+ case HASH('\'', 'c'):
+ case HASH('\'', 'e'):
+ case HASH('\'', 'i'):
+ case HASH('\'', 'o'):
+ case HASH('\'', 'u')
+ case HASH(':', 'A'):
+ case HASH(':', 'E'):
+ case HASH(':', 'I'):
+ case HASH(':', 'O'):
+ case HASH(':', 'U'):
+ case HASH(':', 'Y'):
+ case HASH(':', 'a'):
+ case HASH(':', 'e'):
+ case HASH(':', 'i'):
+ case HASH(':', 'o'):
+ case HASH(':', 'u'):
+ case HASH(':', 'y'):
+ case HASH(',', 'C'):
+ case HASH(',', 'c'):
+#endif
+ }
+}
diff --git a/contrib/groff/src/utils/addftinfo/guess.h b/contrib/groff/src/utils/addftinfo/guess.h
new file mode 100644
index 000000000000..4471dda8d9a6
--- /dev/null
+++ b/contrib/groff/src/utils/addftinfo/guess.h
@@ -0,0 +1,44 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 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. */
+
+struct font_params {
+ int italic;
+ int em;
+ int x_height;
+ int fig_height;
+ int cap_height;
+ int asc_height;
+ int body_height;
+ int comma_depth;
+ int desc_depth;
+ int body_depth;
+};
+
+struct char_metric {
+ int width;
+ int type;
+ int height;
+ int depth;
+ int ic;
+ int left_ic;
+ int sk;
+};
+
+void guess(const char *s, const font_params &param, char_metric *metric);
diff --git a/contrib/groff/src/utils/afmtodit/Makefile.sub b/contrib/groff/src/utils/afmtodit/Makefile.sub
new file mode 100644
index 000000000000..9176924f6301
--- /dev/null
+++ b/contrib/groff/src/utils/afmtodit/Makefile.sub
@@ -0,0 +1,23 @@
+MAN1=afmtodit.n
+CLEANADD=afmtodit
+
+all: afmtodit
+
+afmtodit: afmtodit.pl
+ if test -n "$(PERLPATH)"; then \
+ sed -e "s|/usr/bin/perl|$(PERLPATH)|" \
+ -e "s|@VERSION@|$(version)$(revision)|" \
+ $(srcdir)/afmtodit.pl >afmtodit; \
+ else \
+ sed -e "s|@VERSION@|$(version)$(revision)|" \
+ $(srcdir)/afmtodit.pl afmtodit; \
+ fi
+ chmod +x afmtodit
+
+install_data: afmtodit
+ -test -d $(bindir) || $(mkinstalldirs) $(bindir)
+ -rm -f $(bindir)/afmtodit
+ $(INSTALL_SCRIPT) afmtodit $(bindir)/afmtodit
+
+uninstall_sub:
+ -rm -f $(bindir)/afmtodit
diff --git a/contrib/groff/src/utils/afmtodit/afmtodit.man b/contrib/groff/src/utils/afmtodit/afmtodit.man
new file mode 100644
index 000000000000..03a6e5cb8e5e
--- /dev/null
+++ b/contrib/groff/src/utils/afmtodit/afmtodit.man
@@ -0,0 +1,225 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH AFMTODIT @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+afmtodit \- create font files for use with groff \-Tps
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBafmtodit 'u
+.ti \niu
+.B afmtodit
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-nsv
+.OP \-d desc_file
+.OP \-e enc_file
+.OP \-i n
+.OP \-a n
+.I afm_file
+.I map_file
+.I font
+.br
+.ad \na
+.SH DESCRIPTION
+.B afmtodit
+creates a font file for use with groff and
+.BR grops .
+.B afmtodit
+is written in perl;
+you must have perl version 3 installed in order to run
+.BR afmtodit .
+.I afm_file
+is the AFM (Adobe Font Metric) file for the font.
+.I map_file
+is a file that says which groff character names map onto
+each PostScript character name;
+this file should contain a sequence of lines of the form
+.IP
+.I
+ps_char groff_char
+.LP
+where
+.I ps_char
+is the PostScript name of the character
+and
+.I groff_char
+is the groff name of the character (as used in the groff font file.)
+The same
+.I ps_char
+can occur multiple times in the file;
+each
+.I groff_char
+must occur at most once.
+.I font
+is the groff name of the font.
+If a PostScript character is in the encoding to be used for the font
+but is not mentioned in
+.I map_file
+then
+.B afmtodit
+will put it in the groff font file as an unnamed character,
+which can be accessed by the
+.B \eN
+escape sequence in
+.BR troff .
+The groff font file will be output to a file called
+.IR font .
+.LP
+If there is a downloadable font file for the font, it may be listed in
+the file
+.BR @FONTDIR@/devps/download ;
+see
+.BR grops (@MAN1EXT@).
+.LP
+If the
+.B \-i
+option is used,
+.B afmtodit
+will automatically generate an italic correction,
+a left italic correction and a subscript correction
+for each character
+(the significance of these parameters is explained in
+.BR groff_font (@MAN5EXT@));
+these parameters may be specified for individual characters by
+adding to the
+.I afm_file
+lines of the form:
+.IP
+.BI italicCorrection\ ps_char\ n
+.br
+.BI leftItalicCorrection\ ps_char\ n
+.br
+.BI subscriptCorrection\ ps_char\ n
+.LP
+where
+.I ps_char
+is the PostScript name of the character,
+and
+.I n
+is the desired value of the corresponding parameter in thousandths of an em.
+These parameters are normally needed only for italic (or oblique) fonts.
+.SH OPTIONS
+.TP
+.B \-v
+Print version.
+.TP
+.B \-n
+Don't output a
+.B ligatures
+command for this font.
+Use this with constant-width fonts.
+.TP
+.B \-s
+The font is special.
+The effect of this option is to add the
+.B special
+command to the font file.
+.TP
+.BI \-d desc_file
+The device description file is
+.I desc_file
+rather than the default
+.BR DESC .
+.TP
+.BI \-e enc_file
+The PostScript font should be reencoded to use the encoding described
+in enc_file.
+The format of
+.I enc_file
+is described in
+.BR grops (@MAN1EXT@).
+.TP
+.BI \-a n
+Use
+.I n
+as the slant parameter in the font file;
+this is used by groff in the positioning of accents.
+By default
+.B afmtodit
+uses the negative of the ItalicAngle specified in the afm file;
+with true italic fonts it is sometimes desirable to use
+a slant that is less than this.
+If you find that characters from an italic font have accents
+placed too far to the right over them,
+then use the
+.B \-a
+option to give the font a smaller slant.
+.TP
+.BI \-i n
+Generate an italic correction for each character so that
+the character's width plus the character's italic correction
+is equal to
+.I n
+thousandths of an em
+plus the amount by which the right edge of the character's bounding
+is to the right of the character's origin.
+If this would result in a negative italic correction, use a zero
+italic correction instead.
+.IP
+Also generate a subscript correction equal to the
+product of the tangent of the slant of the font and
+four fifths of the x-height of the font.
+If this would result in a subscript correction greater than the italic
+correction, use a subscript correction equal to the italic correction
+instead.
+.IP
+Also generate a left italic correction for each character
+equal to
+.I n
+thousandths of an em
+plus the amount by which the left edge of the character's bounding box
+is to the left of the character's origin.
+The left italic correction may be negative.
+.IP
+This option is normally needed only with italic (or oblique) fonts.
+The font files distributed with groff were created using an option of
+.B \-i50
+for italic fonts.
+.SH FILES
+.Tp \w'\fB@FONTDIR@/devps/download'u+2n
+.B @FONTDIR@/devps/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devps/ F
+Font description file for font
+.IR F .
+.TP
+.B @FONTDIR@/devps/download
+List of downloadable fonts.
+.TP
+.B @FONTDIR@/devps/text.enc
+Encoding used for text fonts.
+.TP
+.B @FONTDIR@/devps/generate/textmap
+Standard mapping.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR perl (1)
diff --git a/contrib/groff/src/utils/afmtodit/afmtodit.pl b/contrib/groff/src/utils/afmtodit/afmtodit.pl
new file mode 100644
index 000000000000..661245aa6f93
--- /dev/null
+++ b/contrib/groff/src/utils/afmtodit/afmtodit.pl
@@ -0,0 +1,331 @@
+#! /usr/bin/perl -P-
+# -*- Perl -*-
+# Copyright (C) 1989-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.
+
+$prog = $0;
+$prog =~ s@.*/@@;
+
+do 'getopts.pl';
+do Getopts('ve:sd:i:a:n');
+
+if (opt_v) {
+ print "GNU afmtodit (groff) version @VERSION@\n";
+ exit 0;
+}
+
+if ($#ARGV != 2) {
+ die "Usage: $prog [-nsv] [-d DESC] [-e encoding] [-i n] [-a angle] afmfile mapfile font\n";
+}
+
+$afm = $ARGV[0];
+$map = $ARGV[1];
+$font = $ARGV[2];
+$desc = $opt_d || "DESC";
+
+# read the afm file
+
+open(AFM, $afm) || die "$prog: can't open \`$ARGV[0]': $!\n";
+
+while (<AFM>) {
+ chop;
+ @field = split(' ');
+ if ($field[0] eq "FontName") {
+ $psname = $field[1];
+ }
+ elsif($field[0] eq "ItalicAngle") {
+ $italic_angle = -$field[1];
+ }
+ elsif ($field[0] eq "KPX") {
+ if ($#field == 3) {
+ push(kern1, $field[1]);
+ push(kern2, $field[2]);
+ push(kernx, $field[3]);
+ }
+ }
+ elsif ($field[0] eq "italicCorrection") {
+ $italic_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "leftItalicCorrection") {
+ $left_italic_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "subscriptCorrection") {
+ $subscript_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "StartCharMetrics") {
+ while (<AFM>) {
+ @field = split(' ');
+ last if ($field[0] eq "EndCharMetrics");
+ if ($field[0] eq "C") {
+ $c = -1;
+ $wx = 0;
+ $n = "";
+ $lly = 0;
+ $ury = 0;
+ $llx = 0;
+ $urx = 0;
+ $c = $field[1];
+ $i = 2;
+ while ($i <= $#field) {
+ if ($field[$i] eq "WX") {
+ $w = $field[$i + 1];
+ $i += 2;
+ }
+ elsif ($field[$i] eq "N") {
+ $n = $field[$i + 1];
+ $i += 2;
+ }
+ elsif ($field[$i] eq "B") {
+ $llx = $field[$i + 1];
+ $lly = $field[$i + 2];
+ $urx = $field[$i + 3];
+ $ury = $field[$i + 4];
+ $i += 5;
+ }
+ elsif ($field[$i] eq "L") {
+ push(ligatures, $field[$i + 2]);
+ $i += 3;
+ }
+ else {
+ while ($i <= $#field && $field[$i] ne ";") {
+ $i++;
+ }
+ $i++;
+ }
+ }
+ if (!$opt_e && $c != -1) {
+ $encoding[$c] = $n;
+ $in_encoding{$n} = 1;
+ }
+ $width{$n} = $w;
+ $height{$n} = $ury;
+ $depth{$n} = -$lly;
+ $left_side_bearing{$n} = -$llx;
+ $right_side_bearing{$n} = $urx - $w;
+ }
+ }
+ }
+}
+close(AFM);
+
+# read the DESC file
+
+$sizescale = 1;
+
+open(DESC, $desc) || die "$prog: can't open \`$desc': $!\n";
+while (<DESC>) {
+ next if /^#/;
+ chop;
+ @field = split(' ');
+ last if $field[0] eq "charset";
+ if ($field[0] eq "res") { $resolution = $field[1]; }
+ if ($field[0] eq "unitwidth") { $unitwidth = $field[1]; }
+ if ($field[0] eq "sizescale") { $sizescale = $field[1]; }
+}
+close(DESC);
+
+if ($opt_e) {
+ # read the encoding file
+
+ open(ENCODING, $opt_e) || die "$prog: can't open \`$opt_e': $!\n";
+ while (<ENCODING>) {
+ chop;
+ @field = split(' ');
+ if ($#field == 1) {
+ if ($field[1] >= 0 && defined $width{$field[0]}) {
+ $encoding[$field[1]] = $field[0];
+ $in_encoding{$field[0]} = 1;
+ }
+ }
+ }
+ close(ENCODING);
+}
+
+# read the map file
+
+open(MAP, $map) || die "$prog: can't open \`$map': $!\n";
+while (<MAP>) {
+ next if /^#/;
+ chop;
+ @field = split(' ');
+ if ($#field == 1 && $in_encoding{$field[0]}) {
+ if (defined $mapped{$field[1]}) {
+ warn "Both $mapped{$field[1]} and $field[0] map to $field[1]";
+ }
+ elsif ($field[1] eq "space") {
+ # the PostScript character "space" is automatically mapped
+ # to the groff character "space"; this is for grops
+ warn "you are not allowed to map to the groff character `space'";
+ }
+ elsif ($field[0] eq "space") {
+ warn "you are not allowed to map the PostScript character `space'";
+ }
+ else {
+ $nmap{$field[0]} += 0;
+ $map{$field[0],$nmap{$field[0]}} = $field[1];
+ $nmap{$field[0]} += 1;
+ $mapped{$field[1]} = $field[0];
+ }
+ }
+}
+close(MAP);
+
+$italic_angle = $opt_a if $opt_a;
+
+# print it all out
+
+open(FONT, ">$font") || die "$prog: can't open \`$font' for output: $!\n";
+select(FONT);
+
+print("name $font\n");
+print("internalname $psname\n") if $psname;
+print("special\n") if $opt_s;
+printf("slant %g\n", $italic_angle) if $italic_angle != 0;
+printf("spacewidth %d\n", do conv($width{"space"})) if defined $width{"space"};
+
+if ($opt_e) {
+ $e = $opt_e;
+ $e =~ s@.*/@@;
+ print("encoding $e\n");
+}
+
+if (!$opt_n && $#ligatures >= 0) {
+ print("ligatures");
+ foreach $lig (@ligatures) {
+ print(" $lig");
+ }
+ print(" 0\n");
+}
+
+if ($#kern1 >= 0) {
+ print("kernpairs\n");
+
+ for ($i = 0; $i <= $#kern1; $i++) {
+ $c1 = $kern1[$i];
+ $c2 = $kern2[$i];
+ if ($in_encoding{$c1} == 1 && $nmap{$c1} != 0
+ && $in_encoding{$c2} == 1 && $nmap{$c2} != 0) {
+ for ($j = 0; $j < $nmap{$c1}; $j++) {
+ for ($k = 0; $k < $nmap{$c2}; $k++) {
+ if ($kernx[$i] != 0) {
+ printf("%s %s %d\n",
+ $map{$c1,$j},
+ $map{$c2,$k},
+ do conv($kernx[$i]));
+ }
+ }
+ }
+ }
+ }
+}
+
+# characters not shorter than asc_boundary are considered to have ascenders
+$asc_boundary = $height{"t"} - 1;
+
+# likewise for descenders
+$desc_boundary = $depth{"g"};
+$desc_boundary = $depth{"j"} if $depth{"j"} < $desc_boundary;
+$desc_boundary = $depth{"p"} if $depth{"p"} < $desc_boundary;
+$desc_boundary = $depth{"q"} if $depth{"q"} < $desc_boundary;
+$desc_boundary = $depth{"y"} if $depth{"y"} < $desc_boundary;
+$desc_boundary -= 1;
+
+if (defined $height{"x"}) {
+ $xheight = $height{"x"};
+}
+elsif (defined $height{"alpha"}) {
+ $xheight = $height{"alpha"};
+}
+else {
+ $xheight = 450;
+}
+
+$italic_angle = $italic_angle*3.14159265358979323846/180.0;
+$slant = sin($italic_angle)/cos($italic_angle);
+$slant = 0 if $slant < 0;
+
+print("charset\n");
+for ($i = 0; $i < 256; $i++) {
+ $ch = $encoding[$i];
+ if ($ch ne "" && $ch ne "space") {
+ $map{$ch,"0"} = "---" if $nmap{$ch} == 0;
+ $type = 0;
+ $h = $height{$ch};
+ $h = 0 if $h < 0;
+ $d = $depth{$ch};
+ $d = 0 if $d < 0;
+ $type = 1 if $d >= $desc_boundary;
+ $type += 2 if $h >= $asc_boundary;
+ printf("%s\t%d", $map{$ch,"0"}, do conv($width{$ch}));
+ $italic_correction = 0;
+ $left_math_fit = 0;
+ $subscript_correction = 0;
+ if (defined $opt_i) {
+ $italic_correction = $right_side_bearing{$ch} + $opt_i;
+ $italic_correction = 0 if $italic_correction < 0;
+ $subscript_correction = $slant * $xheight * .8;
+ $subscript_correction = $italic_correction if
+ $subscript_correction > $italic_correction;
+ $left_math_fit = $left_side_bearing{$ch} + $opt_i;
+ }
+ if (defined $italic_correction{$ch}) {
+ $italic_correction = $italic_correction{$ch};
+ }
+ if (defined $left_italic_correction{$ch}) {
+ $left_math_fit = $left_italic_correction{$ch};
+ }
+ if (defined $subscript_correction{$ch}) {
+ $subscript_correction = $subscript_correction{$ch};
+ }
+ if ($subscript_correction != 0) {
+ printf(",%d,%d", do conv($h), do conv($d));
+ printf(",%d,%d,%d", do conv($italic_correction),
+ do conv($left_math_fit),
+ do conv($subscript_correction));
+ }
+ elsif ($left_math_fit != 0) {
+ printf(",%d,%d", do conv($h), do conv($d));
+ printf(",%d,%d", do conv($italic_correction),
+ do conv($left_math_fit));
+ }
+ elsif ($italic_correction != 0) {
+ printf(",%d,%d", do conv($h), do conv($d));
+ printf(",%d", do conv($italic_correction));
+ }
+ elsif ($d != 0) {
+ printf(",%d,%d", do conv($h), do conv($d));
+ }
+ else {
+ # always put the height in to stop groff guessing
+ printf(",%d", do conv($h));
+ }
+ printf("\t%d", $type);
+ printf("\t0%03o\t-- %s\n", $i, $ch);
+ for ($j = 1; $j < $nmap{$ch}; $j++) {
+ printf("%s\t\"\n", $map{$ch,$j});
+ }
+ }
+ if ($ch eq "space" && defined $width{"space"}) {
+ printf("space\t%d\t0\t0%03o\n", do conv($width{"space"}), $i);
+ }
+}
+
+sub conv {
+ $_[0]*$unitwidth*$resolution/(72*1000*$sizescale) + ($_[0] < 0 ? -.5 : .5);
+}
diff --git a/contrib/groff/src/utils/hpftodit/Makefile.sub b/contrib/groff/src/utils/hpftodit/Makefile.sub
new file mode 100644
index 000000000000..f5ff13e48654
--- /dev/null
+++ b/contrib/groff/src/utils/hpftodit/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=hpftodit
+MAN1=hpftodit.n
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=hpftodit.o
+CCSRCS=$(srcdir)/hpftodit.cc
diff --git a/contrib/groff/src/utils/hpftodit/hpftodit.cc b/contrib/groff/src/utils/hpftodit/hpftodit.cc
new file mode 100644
index 000000000000..f81f5ad80952
--- /dev/null
+++ b/contrib/groff/src/utils/hpftodit/hpftodit.cc
@@ -0,0 +1,827 @@
+// -*- C++ -*-
+/* Copyright (C) 1994, 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. */
+
+/*
+TODO
+put human readable font name in device file
+devise new names for useful characters
+use --- for unnamed characters
+option to specify symbol sets to look in
+make it work with TrueType fonts
+put filename in error messages (or fix lib)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include "assert.h"
+#include "lib.h"
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "nonposix.h"
+
+#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
+
+const int MULTIPLIER = 3;
+
+inline
+int scale(int n)
+{
+ return n * MULTIPLIER;
+}
+
+// tags in TFM file
+
+enum tag_type {
+ min_tag = 400,
+ type_tag = 400,
+ symbol_set_tag = 404,
+ msl_tag = 403,
+ inches_per_point_tag = 406,
+ design_units_per_em_tag = 408,
+ posture_tag = 409,
+ stroke_weight_tag = 411,
+ spacing_tag = 412,
+ slant_tag = 413,
+ appearance_width_tag = 414,
+ word_spacing_tag = 421,
+ x_height_tag = 424,
+ lower_ascent_tag = 427,
+ lower_descent_tag = 428,
+ width_tag = 433,
+ left_extent_tag = 435,
+ right_extent_tag = 436,
+ ascent_tag = 437,
+ descent_tag = 438,
+ pair_kern_tag = 439,
+ typeface_tag = 442,
+ max_tag = 443
+ };
+
+// types in TFM file
+
+enum {
+ ENUM_TYPE = 1,
+ BYTE_TYPE = 2,
+ USHORT_TYPE = 3,
+ FLOAT_TYPE = 5,
+ SIGNED_SHORT_TYPE = 17
+ };
+
+
+typedef unsigned char byte;
+typedef unsigned short uint16;
+typedef short int16;
+typedef unsigned int uint32;
+
+class File {
+public:
+ File(const char *);
+ void skip(int n);
+ byte get_byte();
+ uint16 get_uint16();
+ uint32 get_uint32();
+ void seek(uint32 n);
+private:
+ unsigned char *buf_;
+ const unsigned char *ptr_;
+ const unsigned char *end_;
+};
+
+struct entry {
+ char present;
+ uint16 type;
+ uint32 count;
+ uint32 value;
+ entry() : present(0) { }
+};
+
+struct char_info {
+ uint16 msl;
+ uint16 width;
+ uint16 ascent;
+ int16 descent;
+ int16 left_extent;
+ uint16 right_extent;
+ uint16 symbol_set;
+ unsigned char code;
+};
+
+const uint16 NO_SYMBOL_SET = 0;
+
+struct name_list {
+ char *name;
+ name_list *next;
+ name_list(const char *s, name_list *p) : name(strsave(s)), next(p) { }
+ ~name_list() { a_delete name; }
+};
+
+struct symbol_set {
+ uint16 select;
+ uint16 index[256];
+};
+
+#define SYMBOL_SET(n, c) ((n) * 32 + ((c) - 64))
+
+uint16 text_symbol_sets[] = {
+ SYMBOL_SET(0, 'N'), // Latin 1
+ SYMBOL_SET(6, 'J'), // Microsoft Publishing
+ SYMBOL_SET(2, 'N'), // Latin 2
+ 0
+ };
+
+uint16 special_symbol_sets[] = {
+ SYMBOL_SET(8, 'M'),
+ SYMBOL_SET(5, 'M'),
+ SYMBOL_SET(15, 'U'),
+ 0
+ };
+
+entry tags[max_tag + 1 - min_tag];
+
+char_info *char_table;
+uint32 nchars;
+
+int msl_name_table_size = 0;
+name_list **msl_name_table = 0;
+
+int n_symbol_sets;
+symbol_set *symbol_set_table;
+
+static int special_flag = 0;
+static int italic_flag = 0;
+static int italic_sep;
+
+static void usage(FILE *stream);
+static void usage();
+static const char *xbasename(const char *);
+static void read_tags(File &);
+static void check_type();
+static void check_units(File &);
+static int read_map(const char *);
+static void require_tag(tag_type);
+static void dump_tags(File &f);
+static void output_spacewidth();
+static void output_pclweight();
+static void output_pclproportional();
+static void read_and_output_pcltypeface(File &);
+static void output_pclstyle();
+static void output_slant();
+static void output_ligatures();
+static void read_symbol_sets(File &);
+static void read_and_output_kernpairs(File &);
+static void output_charset();
+static void read_char_table(File &f);
+
+inline
+entry &tag_info(tag_type t)
+{
+ return tags[t - min_tag];
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+
+ int opt;
+ int debug_flag = 0;
+
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "dsvi:", long_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'd':
+ debug_flag = 1;
+ break;
+ case 's':
+ special_flag = 1;
+ break;
+ case 'i':
+ italic_flag = 1;
+ italic_sep = atoi(optarg);
+ break;
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU hpftodit (groff) version %s\n", Version_string);
+ exit(0);
+ }
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ }
+ if (argc - optind != 3)
+ usage();
+ File f(argv[optind]);
+ if (!read_map(argv[optind + 1]))
+ exit(1);
+ current_filename = 0;
+ current_lineno = -1; // no line numbers
+ if (freopen(argv[optind + 2], "w", stdout) == 0)
+ fatal("cannot open `%1': %2", argv[optind + 2], strerror(errno));
+ current_filename = argv[optind];
+ printf("name %s\n", xbasename(argv[optind + 2]));
+ if (special_flag)
+ printf("special\n");
+ read_tags(f);
+ check_type();
+ check_units(f);
+ if (debug_flag)
+ dump_tags(f);
+ read_char_table(f);
+ output_spacewidth();
+ output_slant();
+ read_and_output_pcltypeface(f);
+ output_pclproportional();
+ output_pclweight();
+ output_pclstyle();
+ read_symbol_sets(f);
+ output_ligatures();
+ read_and_output_kernpairs(f);
+ output_charset();
+ return 0;
+}
+
+static
+void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-s] [-i n] tfm_file map_file output_font\n",
+ program_name);
+}
+static
+void usage()
+{
+ usage(stderr);
+ exit(1);
+}
+
+File::File(const char *s)
+{
+ // We need to read the file in binary mode because hpftodit relies
+ // on byte counts.
+ int fd = open(s, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ fatal("cannot open `%1': %2", s, strerror(errno));
+ current_filename = s;
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ fatal("cannot stat: %1", strerror(errno));
+ if (!S_ISREG(sb.st_mode))
+ fatal("not a regular file");
+ buf_ = new unsigned char[sb.st_size];
+ long nread = read(fd, buf_, sb.st_size);
+ if (nread < 0)
+ fatal("read error: %1", strerror(errno));
+ if (nread != sb.st_size)
+ fatal("read unexpected number of bytes");
+ ptr_ = buf_;
+ end_ = buf_ + sb.st_size;
+ // These are actually text files, so we must get rid of the `\r'
+ // characters. This is also enabled for Posix systems, in case the
+ // input came from Windows...
+ unsigned char *p = buf_, *q = buf_;
+ while (q < end_)
+ {
+ if (*q == '\r')
+ {
+ if (*++q != '\n')
+ *p++ = '\r';
+ }
+#if defined(__MSDOS__) || defined(_MSC_VER)
+ if (*q == '\032') // ^Z means ``software EOF''
+ break;
+#endif
+ *p++ = *q++;
+ }
+ end_ = p;
+}
+
+void File::skip(int n)
+{
+ if (end_ - ptr_ < n)
+ fatal("unexpected end of file");
+ ptr_ += n;
+}
+
+void File::seek(uint32 n)
+{
+ if (end_ - buf_ < n)
+ fatal("unexpected end of file");
+ ptr_ = buf_ + n;
+}
+
+byte File::get_byte()
+{
+ if (ptr_ >= end_)
+ fatal("unexpected end of file");
+ return *ptr_++;
+}
+
+uint16 File::get_uint16()
+{
+ if (end_ - ptr_ < 2)
+ fatal("unexpected end of file");
+ uint16 n = *ptr_++;
+ return n + (*ptr_++ << 8);
+}
+
+uint32 File::get_uint32()
+{
+ if (end_ - ptr_ < 4)
+ fatal("unexpected end of file");
+ uint32 n = *ptr_++;
+ for (int i = 0; i < 3; i++)
+ n += *ptr_++ << (i + 1)*8;
+ return n;
+}
+
+static
+void read_tags(File &f)
+{
+ if (f.get_byte() != 'I' || f.get_byte() != 'I')
+ fatal("not an Intel format TFM file");
+ f.skip(6);
+ uint16 ntags = f.get_uint16();
+ entry dummy;
+ for (uint16 i = 0; i < ntags; i++) {
+ uint16 tag = f.get_uint16();
+ entry *p;
+ if (min_tag <= tag && tag <= max_tag)
+ p = tags + (tag - min_tag);
+ else
+ p = &dummy;
+ p->present = 1;
+ p->type = f.get_uint16();
+ p->count = f.get_uint32();
+ p->value = f.get_uint32();
+ }
+}
+
+static
+void check_type()
+{
+ require_tag(type_tag);
+ if (tag_info(type_tag).value != 0) {
+ if (tag_info(type_tag).value == 2)
+ fatal("cannot handle TrueType tfm files");
+ fatal("unknown type tag %1", int(tag_info(type_tag).value));
+ }
+}
+
+static
+void check_units(File &f)
+{
+ require_tag(design_units_per_em_tag);
+ f.seek(tag_info(design_units_per_em_tag).value);
+ uint32 num = f.get_uint32();
+ uint32 den = f.get_uint32();
+ if (num != 8782 || den != 1)
+ fatal("design units per em != 8782/1");
+ require_tag(inches_per_point_tag);
+ f.seek(tag_info(inches_per_point_tag).value);
+ num = f.get_uint32();
+ den = f.get_uint32();
+ if (num != 100 || den != 7231)
+ fatal("inches per point not 100/7231");
+}
+
+static
+void require_tag(tag_type t)
+{
+ if (!tag_info(t).present)
+ fatal("tag %1 missing", int(t));
+}
+
+static
+void output_spacewidth()
+{
+ require_tag(word_spacing_tag);
+ printf("spacewidth %d\n", scale(tag_info(word_spacing_tag).value));
+}
+
+static
+void read_symbol_sets(File &f)
+{
+ uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
+ n_symbol_sets = symbol_set_dir_length/14;
+ symbol_set_table = new symbol_set[n_symbol_sets];
+ int i;
+ for (i = 0; i < n_symbol_sets; i++) {
+ f.seek(tag_info(symbol_set_tag).value + i*14);
+ (void)f.get_uint32();
+ uint32 off1 = f.get_uint32();
+ uint32 off2 = f.get_uint32();
+ (void)f.get_uint16(); // what's this for?
+ f.seek(off1);
+ int j;
+ uint16 kind = 0;
+ for (j = 0; j < off2 - off1; j++) {
+ unsigned char c = f.get_byte();
+ if ('0' <= c && c <= '9')
+ kind = kind*10 + (c - '0');
+ else if ('A' <= c && c <= 'Z')
+ kind = kind*32 + (c - 64);
+ }
+ symbol_set_table[i].select = kind;
+ for (j = 0; j < 256; j++)
+ symbol_set_table[i].index[j] = f.get_uint16();
+ }
+ for (i = 0; i < nchars; i++)
+ char_table[i].symbol_set = NO_SYMBOL_SET;
+
+ uint16 *symbol_set_selectors = (special_flag
+ ? special_symbol_sets
+ : text_symbol_sets);
+ for (i = 0; symbol_set_selectors[i] != 0; i++) {
+ int j;
+ for (j = 0; j < n_symbol_sets; j++)
+ if (symbol_set_table[j].select == symbol_set_selectors[i])
+ break;
+ if (j < n_symbol_sets) {
+ for (int k = 0; k < 256; k++) {
+ uint16 index = symbol_set_table[j].index[k];
+ if (index != 0xffff
+ && char_table[index].symbol_set == NO_SYMBOL_SET) {
+ char_table[index].symbol_set = symbol_set_table[j].select;
+ char_table[index].code = k;
+ }
+ }
+ }
+ }
+}
+
+static
+void read_char_table(File &f)
+{
+ require_tag(msl_tag);
+ nchars = tag_info(msl_tag).count;
+ char_table = new char_info[nchars];
+
+ f.seek(tag_info(msl_tag).value);
+ uint32 i;
+ for (i = 0; i < nchars; i++)
+ char_table[i].msl = f.get_uint16();
+
+ require_tag(width_tag);
+ f.seek(tag_info(width_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].width = f.get_uint16();
+
+ require_tag(ascent_tag);
+ f.seek(tag_info(ascent_tag).value);
+ for (i = 0; i < nchars; i++) {
+ char_table[i].ascent = f.get_uint16();
+ }
+
+ require_tag(descent_tag);
+ f.seek(tag_info(descent_tag).value);
+ for (i = 0; i < nchars; i++) {
+ char_table[i].descent = f.get_uint16();
+ if (char_table[i].descent > 0)
+ char_table[i].descent = 0;
+ }
+
+ require_tag(left_extent_tag);
+ f.seek(tag_info(left_extent_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].left_extent = int16(f.get_uint16());
+
+ require_tag(right_extent_tag);
+ f.seek(tag_info(right_extent_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].right_extent = f.get_uint16();
+}
+
+static
+void output_pclweight()
+{
+ require_tag(stroke_weight_tag);
+ int stroke_weight = tag_info(stroke_weight_tag).value;
+ int pcl_stroke_weight;
+ if (stroke_weight < 128)
+ pcl_stroke_weight = -3;
+ else if (stroke_weight == 128)
+ pcl_stroke_weight = 0;
+ else if (stroke_weight <= 145)
+ pcl_stroke_weight = 1;
+ else if (stroke_weight <= 179)
+ pcl_stroke_weight = 3;
+ else
+ pcl_stroke_weight = 4;
+ printf("pclweight %d\n", pcl_stroke_weight);
+}
+
+static
+void output_pclproportional()
+{
+ require_tag(spacing_tag);
+ printf("pclproportional %d\n", tag_info(spacing_tag).value == 0);
+}
+
+static
+void read_and_output_pcltypeface(File &f)
+{
+ printf("pcltypeface ");
+ require_tag(typeface_tag);
+ f.seek(tag_info(typeface_tag).value);
+ for (uint32 i = 0; i < tag_info(typeface_tag).count; i++) {
+ unsigned char c = f.get_byte();
+ if (c == '\0')
+ break;
+ putchar(c);
+ }
+ printf("\n");
+}
+
+static
+void output_pclstyle()
+{
+ unsigned pcl_style = 0;
+ // older tfms don't have the posture tag
+ if (tag_info(posture_tag).present) {
+ if (tag_info(posture_tag).value)
+ pcl_style |= 1;
+ }
+ else {
+ require_tag(slant_tag);
+ if (tag_info(slant_tag).value != 0)
+ pcl_style |= 1;
+ }
+ require_tag(appearance_width_tag);
+ if (tag_info(appearance_width_tag).value < 100) // guess
+ pcl_style |= 4;
+ printf("pclstyle %d\n", pcl_style);
+}
+
+static
+void output_slant()
+{
+ require_tag(slant_tag);
+ int slant = int16(tag_info(slant_tag).value);
+ if (slant != 0)
+ printf("slant %f\n", slant/100.0);
+}
+
+static
+void output_ligatures()
+{
+ // don't use ligatures for fixed space font
+ require_tag(spacing_tag);
+ if (tag_info(spacing_tag).value != 0)
+ return;
+ static const char *ligature_names[] = {
+ "fi", "fl", "ff", "ffi", "ffl"
+ };
+
+ static const char *ligature_chars[] = {
+ "fi", "fl", "ff", "Fi", "Fl"
+ };
+
+ unsigned ligature_mask = 0;
+ int i;
+ for (i = 0; i < nchars; i++) {
+ uint16 msl = char_table[i].msl;
+ if (msl < msl_name_table_size
+ && char_table[i].symbol_set != NO_SYMBOL_SET) {
+ for (name_list *p = msl_name_table[msl]; p; p = p->next)
+ for (int j = 0; j < SIZEOF(ligature_chars); j++)
+ if (strcmp(p->name, ligature_chars[j]) == 0) {
+ ligature_mask |= 1 << j;
+ break;
+ }
+ }
+ }
+ if (ligature_mask) {
+ printf("ligatures");
+ for (i = 0; i < SIZEOF(ligature_names); i++)
+ if (ligature_mask & (1 << i))
+ printf(" %s", ligature_names[i]);
+ printf(" 0\n");
+ }
+}
+
+static
+void read_and_output_kernpairs(File &f)
+{
+ if (tag_info(pair_kern_tag).present) {
+ printf("kernpairs\n");
+ f.seek(tag_info(pair_kern_tag).value);
+ uint16 n_pairs = f.get_uint16();
+ for (int i = 0; i < n_pairs; i++) {
+ uint16 i1 = f.get_uint16();
+ uint16 i2 = f.get_uint16();
+ int16 val = int16(f.get_uint16());
+ if (char_table[i1].symbol_set != NO_SYMBOL_SET
+ && char_table[i2].symbol_set != NO_SYMBOL_SET
+ && char_table[i1].msl < msl_name_table_size
+ && char_table[i2].msl < msl_name_table_size) {
+ for (name_list *p = msl_name_table[char_table[i1].msl];
+ p;
+ p = p->next)
+ for (name_list *q = msl_name_table[char_table[i2].msl];
+ q;
+ q = q->next)
+ printf("%s %s %d\n", p->name, q->name, scale(val));
+ }
+ }
+ }
+}
+
+static
+void output_charset()
+{
+ require_tag(slant_tag);
+ double slant_angle = int16(tag_info(slant_tag).value)*PI/18000.0;
+ double slant = sin(slant_angle)/cos(slant_angle);
+
+ require_tag(x_height_tag);
+ require_tag(lower_ascent_tag);
+ require_tag(lower_descent_tag);
+
+ printf("charset\n");
+ int i;
+ for (i = 0; i < nchars; i++) {
+ uint16 msl = char_table[i].msl;
+ if (msl < msl_name_table_size
+ && msl_name_table[msl]) {
+ if (char_table[i].symbol_set != NO_SYMBOL_SET) {
+ printf("%s\t%d,%d",
+ msl_name_table[msl]->name,
+ scale(char_table[i].width),
+ scale(char_table[i].ascent));
+ int depth = scale(- char_table[i].descent);
+ if (depth < 0)
+ depth = 0;
+ int italic_correction = 0;
+ int left_italic_correction = 0;
+ int subscript_correction = 0;
+ if (italic_flag) {
+ italic_correction = scale(char_table[i].right_extent
+ - char_table[i].width
+ + italic_sep);
+ if (italic_correction < 0)
+ italic_correction = 0;
+ subscript_correction = int((tag_info(x_height_tag).value
+ * slant * .8) + .5);
+ if (subscript_correction > italic_correction)
+ subscript_correction = italic_correction;
+ left_italic_correction = scale(italic_sep
+ - char_table[i].left_extent);
+ }
+ if (subscript_correction != 0)
+ printf(",%d,%d,%d,%d",
+ depth, italic_correction, left_italic_correction,
+ subscript_correction);
+ else if (left_italic_correction != 0)
+ printf(",%d,%d,%d", depth, italic_correction, left_italic_correction);
+ else if (italic_correction != 0)
+ printf(",%d,%d", depth, italic_correction);
+ else if (depth != 0)
+ printf(",%d", depth);
+ // This is fairly arbitrary. Fortunately it doesn't much matter.
+ unsigned type = 0;
+ if (char_table[i].ascent > (tag_info(lower_ascent_tag).value*9)/10)
+ type |= 2;
+ if (char_table[i].descent < (int16(tag_info(lower_descent_tag).value)*9)/10)
+ type |= 1;
+ printf("\t%d\t%d\n",
+ type,
+ char_table[i].symbol_set*256 + char_table[i].code);
+ for (name_list *p = msl_name_table[msl]->next; p; p = p->next)
+ printf("%s\t\"\n", p->name);
+ }
+ else
+ warning("MSL %1 not in any of the searched symbol sets", msl);
+ }
+ }
+}
+
+static
+void dump_tags(File &f)
+{
+ int i;
+ for (i = min_tag; i <= max_tag; i++) {
+ enum tag_type t = tag_type(i);
+ if (tag_info(t).present) {
+ fprintf(stderr,
+ "%d %d %d %d\n", i, tag_info(t).type, tag_info(t).count,
+ tag_info(t).value);
+ if (tag_info(t).type == FLOAT_TYPE
+ && tag_info(t).count == 1) {
+ f.seek(tag_info(t).value);
+ uint32 num = f.get_uint32();
+ uint32 den = f.get_uint32();
+ fprintf(stderr, "(%u/%u = %g)\n", num, den, (double)num/den);
+ }
+ }
+ }
+}
+
+static
+int read_map(const char *file)
+{
+ errno = 0;
+ FILE *fp = fopen(file, "r");
+ if (!fp) {
+ error("can't open `%1': %2", file, strerror(errno));
+ return 0;
+ }
+ current_filename = file;
+ char buf[512];
+ current_lineno = 0;
+ while (fgets(buf, int(sizeof(buf)), fp)) {
+ current_lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+ ptr = strtok(ptr, " \n\t");
+ if (!ptr)
+ continue;
+ int n;
+ if (sscanf(ptr, "%d", &n) != 1) {
+ error("bad map file");
+ fclose(fp);
+ return 0;
+ }
+ if (n < 0) {
+ error("negative code");
+ fclose(fp);
+ return 0;
+ }
+ if (n >= msl_name_table_size) {
+ size_t old_size = msl_name_table_size;
+ name_list **old_table = msl_name_table;
+ msl_name_table_size = n + 256;
+ msl_name_table = new name_list *[msl_name_table_size];
+ if (old_table) {
+ memcpy(msl_name_table, old_table, old_size*sizeof(name_list *));
+ a_delete old_table;
+ }
+ for (size_t i = old_size; i < msl_name_table_size; i++)
+ msl_name_table[i] = 0;
+ }
+ ptr = strtok(0, " \n\t");
+ if (!ptr) {
+ error("missing names");
+ fclose(fp);
+ return 0;
+ }
+ for (; ptr; ptr = strtok(0, " \n\t"))
+ msl_name_table[n] = new name_list(ptr, msl_name_table[n]);
+ }
+ fclose(fp);
+ return 1;
+}
+
+static
+const char *xbasename(const char *s)
+{
+ // DIR_SEPS[] are possible directory separator characters, see
+ // nonposix.h. We want the rightmost separator of all possible
+ // ones. Example: d:/foo\\bar.
+ const char *b = strrchr(s, DIR_SEPS[0]), *b1;
+ const char *sep = &DIR_SEPS[1];
+
+ while (*sep)
+ {
+ b1 = strrchr(s, *sep);
+ if (b1 && (!b || b1 > b))
+ b = b1;
+ sep++;
+ }
+ return b ? b + 1 : s;
+}
diff --git a/contrib/groff/src/utils/hpftodit/hpftodit.man b/contrib/groff/src/utils/hpftodit/hpftodit.man
new file mode 100644
index 000000000000..d107e8c8d549
--- /dev/null
+++ b/contrib/groff/src/utils/hpftodit/hpftodit.man
@@ -0,0 +1,155 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1994-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH HPFTODIT @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+hpftodit \- create font description files for use with groff \-Tlj4
+.SH SYNOPSIS
+.B hpftodit
+[
+.B \-sv
+]
+[
+.BI \-i n
+]
+.I tfm_file
+.I map_file
+.I font
+.PP
+It is possible to have whitespace between the
+.B \-i
+command line option and its parameter.
+.SH DESCRIPTION
+.B hpftodit
+creates a font file for use with
+.B
+groff \-Tlj4\fR
+from an HP tagged font metric file.
+.I tfm_file
+is the name of the tagged font metric file for the font.
+.I map_file
+is a file giving the groff names for characters in the font;
+this file should consist of a sequence of lines of the form:
+.IP
+.I
+n c1 c2 \fR.\|.\|.
+.LP
+where
+.I n
+is a decimal integer giving the MSL number of the character,
+and
+.IR c1 ,
+.IR c2 ,.\|.\|.
+are the groff names of the character.
+.I font
+is the name of the groff font file.
+The groff font file is written to
+.IR font .
+.LP
+The
+.B \-s
+option should be given if the font is special
+(a font is
+.I special
+if
+.B troff
+should search it whenever
+a character is not found in the current font.)
+If the font is special,
+it should be listed in the
+.B fonts
+command in the DESC file;
+if it is not special, there is no need to list it, since
+.B troff
+can automatically mount it when it's first used.
+.LP
+If the
+.B \-i
+option is used,
+.B hpftodit
+will automatically generate an italic correction,
+a left italic correction and a subscript correction
+for each character
+(the significance of these parameters is explained in
+.BR groff_font (@MAN5EXT@)).
+.SH OPTIONS
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-s
+The font is special.
+The effect of this option is to add the
+.B special
+command to the font file.
+.TP
+.BI \-i n
+Generate an italic correction for each character so that
+the character's width plus the character's italic correction
+is equal to
+.I n
+design units
+plus the amount by which the right edge of the character's bounding
+is to the right of the character's origin.
+If this would result in a negative italic correction, use a zero
+italic correction instead.
+There are 8782 design units per em for Intellifont fonts.
+.IP
+Also generate a subscript correction equal to the
+product of the tangent of the slant of the font and
+four fifths of the x-height of the font.
+If this would result in a subscript correction greater than the italic
+correction, use a subscript correction equal to the italic correction
+instead.
+.IP
+Also generate a left italic correction for each character
+equal to
+.I n
+design units
+plus the amount by which the left edge of the character's bounding box
+is to the left of the character's origin.
+The left italic correction may be negative.
+.IP
+This option is normally needed only with italic (or oblique) fonts.
+.SH FILES
+.Tp \w'\fB@FONTDIR@/devlj4/DESC'u+2n
+.B @FONTDIR@/devlj4/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devlj4/ F
+Font description file for font
+.IR F .
+.SH BUGS
+.LP
+This program was written without the benefit of complete, official
+documentation on the tagged font metric format.
+It is therefore likely that it will fail to work on tfm files that are
+dissimilar to those for the internal fonts on the Laserjet 4,
+with which it was tested:
+.LP
+TrueType tfm files are not supported.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR grolj4 (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@)
diff --git a/contrib/groff/src/utils/indxbib/Makefile.sub b/contrib/groff/src/utils/indxbib/Makefile.sub
new file mode 100644
index 000000000000..add99eba531c
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/Makefile.sub
@@ -0,0 +1,31 @@
+PROG=indxbib
+MAN1=indxbib.n
+XLIBS=$(LIBBIB) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=\
+ indxbib.o \
+ dirnamemax.o \
+ signal.o
+CCSRCS=\
+ $(srcdir)/indxbib.cc
+CSRCS=\
+ $(srcdir)/dirnamemax.c \
+ $(srcdir)/signal.c
+NAMEPREFIX=$(g)
+
+install_data: eign
+ -test -d $(datadir) || $(mkinstalldirs) $(datadir)
+ -test -d $(dataprogramdir) || $(mkinstalldirs) $(dataprogramdir)
+ -test -d $(datasubdir) || $(mkinstalldirs) $(datasubdir)
+ if test -f /usr/lib/eign; then \
+ rm -f $(common_words_file); \
+ ln -s /usr/lib/eign $(common_words_file) 2>/dev/null \
+ || ln /usr/lib/eign $(common_words_file) 2>/dev/null \
+ || cp /usr/lib/eign $(common_words_file); \
+ else \
+ rm -f $(common_words_file); \
+ $(INSTALL_DATA) $(srcdir)/eign $(common_words_file); \
+ fi
+
+uninstall_sub:
+ -rm -f $(common_words_file)
diff --git a/contrib/groff/src/utils/indxbib/dirnamemax.c b/contrib/groff/src/utils/indxbib/dirnamemax.c
new file mode 100644
index 000000000000..a8cd9923c469
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/dirnamemax.c
@@ -0,0 +1,49 @@
+/* dir_name_max(dir) does the same as pathconf(dir, _PC_NAME_MAX) */
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef _POSIX_VERSION
+
+long dir_name_max(dir)
+ char *dir;
+{
+ return pathconf(dir, _PC_NAME_MAX);
+}
+
+#else /* not _POSIX_VERSION */
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else /* not HAVE_DIRENT_H */
+#ifdef HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif /* HAVE_SYS_DIR_H */
+#endif /* not HAVE_DIRENT_H */
+
+#ifndef NAME_MAX
+#ifdef MAXNAMLEN
+#define NAME_MAX MAXNAMLEN
+#else /* !MAXNAMLEN */
+#ifdef MAXNAMELEN
+#define NAME_MAX MAXNAMELEN
+#else /* !MAXNAMELEN */
+#define NAME_MAX 14
+#endif /* !MAXNAMELEN */
+#endif /* !MAXNAMLEN */
+#endif /* !NAME_MAX */
+
+long dir_name_max(dir)
+ char *dir;
+{
+ return NAME_MAX;
+}
+
+#endif /* not _POSIX_VERSION */
diff --git a/contrib/groff/src/utils/indxbib/eign b/contrib/groff/src/utils/indxbib/eign
new file mode 100644
index 000000000000..7718c8b1124c
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/eign
@@ -0,0 +1,133 @@
+a
+i
+the
+to
+of
+and
+in
+is
+it
+for
+that
+if
+you
+this
+be
+on
+with
+not
+have
+are
+or
+as
+from
+can
+but
+by
+at
+an
+will
+no
+all
+was
+do
+there
+my
+one
+so
+we
+they
+what
+would
+any
+which
+about
+get
+your
+use
+some
+me
+then
+name
+like
+out
+when
+up
+time
+other
+more
+only
+just
+end
+also
+know
+how
+new
+should
+been
+than
+them
+he
+who
+make
+may
+people
+these
+now
+their
+here
+into
+first
+could
+way
+had
+see
+work
+well
+were
+two
+very
+where
+while
+us
+because
+good
+same
+even
+much
+most
+many
+such
+long
+his
+over
+last
+since
+right
+before
+our
+without
+too
+those
+why
+must
+part
+being
+current
+back
+still
+go
+point
+value
+each
+did
+both
+true
+off
+say
+another
+state
+might
+under
+start
+try
diff --git a/contrib/groff/src/utils/indxbib/indxbib.cc b/contrib/groff/src/utils/indxbib/indxbib.cc
new file mode 100644
index 000000000000..99a6baf4233b
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/indxbib.cc
@@ -0,0 +1,805 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "cmap.h"
+
+#include "defs.h"
+#include "index.h"
+
+#include "nonposix.h"
+
+extern "C" {
+ // Solaris 2.5.1 has these functions,
+ // but its stdlib.h fails to declare them.
+ char *mktemp(char *);
+ int mkstemp(char *);
+}
+
+#define DEFAULT_HASH_TABLE_SIZE 997
+#define TEMP_INDEX_TEMPLATE "indxbibXXXXXX"
+
+// (2^n - MALLOC_OVERHEAD) should be a good argument for malloc().
+
+#define MALLOC_OVERHEAD 16
+
+#ifdef BLOCK_SIZE
+#undef BLOCK_SIZE
+#endif
+
+const int BLOCK_SIZE = ((1024 - MALLOC_OVERHEAD - sizeof(struct block *)
+ - sizeof(int)) / sizeof(int));
+struct block {
+ block *next;
+ int used;
+ int v[BLOCK_SIZE];
+
+ block(block *p = 0) : next(p), used(0) { }
+};
+
+struct block;
+
+union table_entry {
+ block *ptr;
+ int count;
+};
+
+struct word_list {
+ word_list *next;
+ char *str;
+ int len;
+ word_list(const char *, int, word_list *);
+};
+
+table_entry *hash_table;
+int hash_table_size = DEFAULT_HASH_TABLE_SIZE;
+// We make this the same size as hash_table so we only have to do one
+// mod per key.
+static word_list **common_words_table = 0;
+char *key_buffer;
+
+FILE *indxfp;
+int ntags = 0;
+string filenames;
+char *temp_index_file = 0;
+
+const char *ignore_fields = "XYZ";
+const char *common_words_file = COMMON_WORDS_FILE;
+int n_ignore_words = 100;
+int truncate_len = 6;
+int shortest_len = 3;
+int max_keys_per_item = 100;
+
+static void usage(FILE *stream);
+static void write_hash_table();
+static void init_hash_table();
+static void read_common_words_file();
+static int store_key(char *s, int len);
+static void possibly_store_key(char *s, int len);
+static int do_whole_file(const char *filename);
+static int do_file(const char *filename);
+static void store_reference(int filename_index, int pos, int len);
+static void check_integer_arg(char opt, const char *arg, int min, int *res);
+static void store_filename(const char *);
+static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp);
+static char *get_cwd();
+
+extern "C" {
+ void cleanup();
+ long dir_name_max(const char *);
+ void catch_fatal_signals();
+ void ignore_fatal_signals();
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+
+ const char *basename = 0;
+ typedef int (*parser_t)(const char *);
+ parser_t parser = do_file;
+ const char *directory = 0;
+ const char *foption = 0;
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw",
+ long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'c':
+ common_words_file = optarg;
+ break;
+ case 'd':
+ directory = optarg;
+ break;
+ case 'f':
+ foption = optarg;
+ break;
+ case 'h':
+ check_integer_arg('h', optarg, 1, &hash_table_size);
+ if (!is_prime(hash_table_size)) {
+ while (!is_prime(++hash_table_size))
+ ;
+ warning("%1 not prime: using %2 instead", optarg, hash_table_size);
+ }
+ break;
+ case 'i':
+ ignore_fields = optarg;
+ break;
+ case 'k':
+ check_integer_arg('k', optarg, 1, &max_keys_per_item);
+ break;
+ case 'l':
+ check_integer_arg('l', optarg, 0, &shortest_len);
+ break;
+ case 'n':
+ check_integer_arg('n', optarg, 0, &n_ignore_words);
+ break;
+ case 'o':
+ basename = optarg;
+ break;
+ case 't':
+ check_integer_arg('t', optarg, 1, &truncate_len);
+ break;
+ case 'w':
+ parser = do_whole_file;
+ break;
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU indxbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (optind >= argc && foption == 0)
+ fatal("no files and no -f option");
+ if (!directory) {
+ char *path = get_cwd();
+ store_filename(path);
+ a_delete path;
+ }
+ else
+ store_filename(directory);
+ init_hash_table();
+ store_filename(common_words_file);
+ store_filename(ignore_fields);
+ key_buffer = new char[truncate_len];
+ read_common_words_file();
+ if (!basename)
+ basename = optind < argc ? argv[optind] : DEFAULT_INDEX_NAME;
+ const char *p = strrchr(basename, DIR_SEPS[0]), *p1;
+ const char *sep = &DIR_SEPS[1];
+ while (*sep)
+ {
+ p1 = strrchr(basename, *sep);
+ if (p1 && (!p || p1 > p))
+ p = p1;
+ sep++;
+ }
+ long name_max;
+ if (p) {
+ char *dir = strsave(basename);
+ dir[p - basename] = '\0';
+ name_max = dir_name_max(dir);
+ a_delete dir;
+ }
+ else
+ name_max = dir_name_max(".");
+ const char *filename = p ? p + 1 : basename;
+ if (name_max >= 0 && strlen(filename) + sizeof(INDEX_SUFFIX) - 1 > name_max)
+ fatal("`%1.%2' is too long for a filename", filename, INDEX_SUFFIX);
+ if (p) {
+ p++;
+ temp_index_file = new char[p - basename + sizeof(TEMP_INDEX_TEMPLATE)];
+ memcpy(temp_index_file, basename, p - basename);
+ strcpy(temp_index_file + (p - basename), TEMP_INDEX_TEMPLATE);
+ }
+ else {
+ temp_index_file = strsave(TEMP_INDEX_TEMPLATE);
+ }
+#ifndef HAVE_MKSTEMP
+ if (!mktemp(temp_index_file) || !temp_index_file[0])
+ fatal("cannot create file name for temporary file");
+#endif
+ catch_fatal_signals();
+#ifdef HAVE_MKSTEMP
+ int fd = mkstemp(temp_index_file);
+#else
+ int fd = creat(temp_index_file, S_IRUSR|S_IRGRP|S_IROTH);
+#endif
+ if (fd < 0)
+ fatal("can't create temporary index file: %1", strerror(errno));
+ indxfp = fdopen(fd, FOPEN_WB);
+ if (indxfp == 0)
+ fatal("fdopen failed");
+ if (fseek(indxfp, sizeof(index_header), 0) < 0)
+ fatal("can't seek past index header: %1", strerror(errno));
+ int failed = 0;
+ if (foption) {
+ FILE *fp = stdin;
+ if (strcmp(foption, "-") != 0) {
+ errno = 0;
+ fp = fopen(foption, "r");
+ if (!fp)
+ fatal("can't open `%1': %2", foption, strerror(errno));
+ }
+ string path;
+ int lineno = 1;
+ for (;;) {
+ int c;
+ for (c = getc(fp); c != '\n' && c != EOF; c = getc(fp)) {
+ if (c == '\0')
+ error_with_file_and_line(foption, lineno,
+ "nul character in pathname ignored");
+ else
+ path += c;
+ }
+ if (path.length() > 0) {
+ path += '\0';
+ if (!(*parser)(path.contents()))
+ failed = 1;
+ path.clear();
+ }
+ if (c == EOF)
+ break;
+ lineno++;
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ for (int i = optind; i < argc; i++)
+ if (!(*parser)(argv[i]))
+ failed = 1;
+ write_hash_table();
+ if (fclose(indxfp) < 0)
+ fatal("error closing temporary index file: %1", strerror(errno));
+ char *index_file = new char[strlen(basename) + sizeof(INDEX_SUFFIX)];
+ strcpy(index_file, basename);
+ strcat(index_file, INDEX_SUFFIX);
+#ifdef HAVE_RENAME
+ if (rename(temp_index_file, index_file) < 0)
+ {
+#ifdef __MSDOS__
+ // RENAME could fail on plain MSDOS filesystems because
+ // INDEX_FILE is an invalid filename, e.g. it has multiple dots.
+ char *fname = p ? index_file + (p - basename) : 0;
+ char *dot = 0;
+
+ // Replace the dot with an underscore and try again.
+ if (fname
+ && (dot = strchr(fname, '.')) != 0
+ && strcmp(dot, INDEX_SUFFIX) != 0)
+ *dot = '_';
+ if (rename(temp_index_file, index_file) < 0)
+#endif
+ fatal("can't rename temporary index file: %1", strerror(errno));
+ }
+#else /* not HAVE_RENAME */
+ ignore_fatal_signals();
+ if (unlink(index_file) < 0) {
+ if (errno != ENOENT)
+ fatal("can't unlink `%1': %2", index_file, strerror(errno));
+ }
+ if (link(temp_index_file, index_file) < 0)
+ fatal("can't link temporary index file: %1", strerror(errno));
+ if (unlink(temp_index_file) < 0)
+ fatal("can't unlink temporary index file: %1", strerror(errno));
+#endif /* not HAVE_RENAME */
+ temp_index_file = 0;
+ return failed;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-vw] [-c file] [-d dir] [-f file] [-h n] [-i XYZ] [-k n]\n"
+" [-l n] [-n n] [-o base] [-t n] [files...]\n",
+ program_name);
+}
+
+static void check_integer_arg(char opt, const char *arg, int min, int *res)
+{
+ char *ptr;
+ long n = strtol(arg, &ptr, 10);
+ if (n == 0 && ptr == arg)
+ error("argument to -%1 not an integer", opt);
+ else if (n < min)
+ error("argument to -%1 must not be less than %2", opt, min);
+ else {
+ if (n > INT_MAX)
+ error("argument to -%1 greater than maximum integer", opt);
+ else if (*ptr != '\0')
+ error("junk after integer argument to -%1", opt);
+ *res = int(n);
+ }
+}
+
+static char *get_cwd()
+{
+ char *buf;
+ int size = 12;
+
+ for (;;) {
+ buf = new char[size];
+ if (getcwd(buf, size))
+ break;
+ if (errno != ERANGE)
+ fatal("cannot get current working directory: %1", strerror(errno));
+ a_delete buf;
+ if (size == INT_MAX)
+ fatal("current working directory longer than INT_MAX");
+ if (size > INT_MAX/2)
+ size = INT_MAX;
+ else
+ size *= 2;
+ }
+ return buf;
+}
+
+word_list::word_list(const char *s, int n, word_list *p)
+: next(p), len(n)
+{
+ str = new char[n];
+ memcpy(str, s, n);
+}
+
+static void read_common_words_file()
+{
+ if (n_ignore_words <= 0)
+ return;
+ errno = 0;
+ FILE *fp = fopen(common_words_file, "r");
+ if (!fp)
+ fatal("can't open `%1': %2", common_words_file, strerror(errno));
+ common_words_table = new word_list * [hash_table_size];
+ for (int i = 0; i < hash_table_size; i++)
+ common_words_table[i] = 0;
+ int count = 0;
+ int key_len = 0;
+ for (;;) {
+ int c = getc(fp);
+ while (c != EOF && !csalnum(c))
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ do {
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = cmlower(c);
+ c = getc(fp);
+ } while (c != EOF && csalnum(c));
+ if (key_len >= shortest_len) {
+ int h = hash(key_buffer, key_len) % hash_table_size;
+ common_words_table[h] = new word_list(key_buffer, key_len,
+ common_words_table[h]);
+ }
+ if (++count >= n_ignore_words)
+ break;
+ key_len = 0;
+ if (c == EOF)
+ break;
+ }
+ n_ignore_words = count;
+ fclose(fp);
+}
+
+static int do_whole_file(const char *filename)
+{
+ errno = 0;
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ int count = 0;
+ int key_len = 0;
+ int c;
+ while ((c = getc(fp)) != EOF) {
+ if (csalnum(c)) {
+ key_len = 1;
+ key_buffer[0] = c;
+ while ((c = getc(fp)) != EOF) {
+ if (!csalnum(c))
+ break;
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = c;
+ }
+ if (store_key(key_buffer, key_len)) {
+ if (++count >= max_keys_per_item)
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ }
+ store_reference(filenames.length(), 0, 0);
+ store_filename(filename);
+ fclose(fp);
+ return 1;
+}
+
+static int do_file(const char *filename)
+{
+ errno = 0;
+ // Need binary I/O for MS-DOS/MS-Windows, because indxbib relies on
+ // byte counts to be consistent with fseek.
+ FILE *fp = fopen(filename, FOPEN_RB);
+ if (fp == 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ int filename_index = filenames.length();
+ store_filename(filename);
+
+ enum {
+ START, // at the start of the file; also in between references
+ BOL, // in the middle of a reference, at the beginning of the line
+ PERCENT, // seen a percent at the beginning of the line
+ IGNORE, // ignoring a field
+ IGNORE_BOL, // at the beginning of a line ignoring a field
+ KEY, // in the middle of a key
+ DISCARD, // after truncate_len bytes of a key
+ MIDDLE // in between keys
+ } state = START;
+
+ // In states START, BOL, IGNORE_BOL, space_count how many spaces at
+ // the beginning have been seen. In states PERCENT, IGNORE, KEY,
+ // MIDDLE space_count must be 0.
+ int space_count = 0;
+ int byte_count = 0; // bytes read
+ int key_len = 0;
+ int ref_start = -1; // position of start of current reference
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ // We opened the file in binary mode, so we need to skip
+ // every CR character before a Newline.
+ if (c == '\r') {
+ int peek = getc(fp);
+ if (peek = '\n') {
+ byte_count++;
+ c = peek;
+ }
+ else
+ ungetc(peek, fp);
+ }
+#if defined(__MSDOS__) || defined(_MSC_VER)
+ else if (c == 0x1a) // ^Z means EOF in text files
+ break;
+#endif
+ byte_count++;
+ switch (state) {
+ case START:
+ if (c == ' ' || c == '\t') {
+ space_count++;
+ break;
+ }
+ if (c == '\n') {
+ space_count = 0;
+ break;
+ }
+ ref_start = byte_count - space_count - 1;
+ space_count = 0;
+ if (c == '%')
+ state = PERCENT;
+ else if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else
+ state = MIDDLE;
+ break;
+ case BOL:
+ switch (c) {
+ case '%':
+ if (space_count > 0) {
+ space_count = 0;
+ state = MIDDLE;
+ }
+ else
+ state = PERCENT;
+ break;
+ case ' ':
+ case '\t':
+ space_count++;
+ break;
+ case '\n':
+ store_reference(filename_index, ref_start,
+ byte_count - 1 - space_count - ref_start);
+ state = START;
+ space_count = 0;
+ break;
+ default:
+ space_count = 0;
+ if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case PERCENT:
+ if (strchr(ignore_fields, c) != 0)
+ state = IGNORE;
+ else if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ break;
+ case IGNORE:
+ if (c == '\n')
+ state = IGNORE_BOL;
+ break;
+ case IGNORE_BOL:
+ switch (c) {
+ case '%':
+ if (space_count > 0) {
+ state = IGNORE;
+ space_count = 0;
+ }
+ else
+ state = PERCENT;
+ break;
+ case ' ':
+ case '\t':
+ space_count++;
+ break;
+ case '\n':
+ store_reference(filename_index, ref_start,
+ byte_count - 1 - space_count - ref_start);
+ state = START;
+ space_count = 0;
+ break;
+ default:
+ space_count = 0;
+ state = IGNORE;
+ }
+ break;
+ case KEY:
+ if (csalnum(c)) {
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = c;
+ else
+ state = DISCARD;
+ }
+ else {
+ possibly_store_key(key_buffer, key_len);
+ key_len = 0;
+ if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ }
+ break;
+ case DISCARD:
+ if (!csalnum(c)) {
+ possibly_store_key(key_buffer, key_len);
+ key_len = 0;
+ if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ }
+ break;
+ case MIDDLE:
+ if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else if (c == '\n')
+ state = BOL;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ switch (state) {
+ case START:
+ break;
+ case DISCARD:
+ case KEY:
+ possibly_store_key(key_buffer, key_len);
+ // fall through
+ case BOL:
+ case PERCENT:
+ case IGNORE_BOL:
+ case IGNORE:
+ case MIDDLE:
+ store_reference(filename_index, ref_start,
+ byte_count - ref_start - space_count);
+ break;
+ default:
+ assert(0);
+ }
+ fclose(fp);
+ return 1;
+}
+
+static void store_reference(int filename_index, int pos, int len)
+{
+ tag t;
+ t.filename_index = filename_index;
+ t.start = pos;
+ t.length = len;
+ fwrite_or_die(&t, sizeof(t), 1, indxfp);
+ ntags++;
+}
+
+static void store_filename(const char *fn)
+{
+ filenames += fn;
+ filenames += '\0';
+}
+
+static void init_hash_table()
+{
+ hash_table = new table_entry[hash_table_size];
+ for (int i = 0; i < hash_table_size; i++)
+ hash_table[i].ptr = 0;
+}
+
+static void possibly_store_key(char *s, int len)
+{
+ static int last_tagno = -1;
+ static int key_count;
+ if (last_tagno != ntags) {
+ last_tagno = ntags;
+ key_count = 0;
+ }
+ if (key_count < max_keys_per_item) {
+ if (store_key(s, len))
+ key_count++;
+ }
+}
+
+static int store_key(char *s, int len)
+{
+ if (len < shortest_len)
+ return 0;
+ int is_number = 1;
+ for (int i = 0; i < len; i++)
+ if (!csdigit(s[i])) {
+ is_number = 0;
+ s[i] = cmlower(s[i]);
+ }
+ if (is_number && !(len == 4 && s[0] == '1' && s[1] == '9'))
+ return 0;
+ int h = hash(s, len) % hash_table_size;
+ if (common_words_table) {
+ for (word_list *ptr = common_words_table[h]; ptr; ptr = ptr->next)
+ if (len == ptr->len && memcmp(s, ptr->str, len) == 0)
+ return 0;
+ }
+ table_entry *pp = hash_table + h;
+ if (!pp->ptr)
+ pp->ptr = new block;
+ else if (pp->ptr->v[pp->ptr->used - 1] == ntags)
+ return 1;
+ else if (pp->ptr->used >= BLOCK_SIZE)
+ pp->ptr = new block(pp->ptr);
+ pp->ptr->v[(pp->ptr->used)++] = ntags;
+ return 1;
+}
+
+static void write_hash_table()
+{
+ const int minus_one = -1;
+ int li = 0;
+ for (int i = 0; i < hash_table_size; i++) {
+ block *ptr = hash_table[i].ptr;
+ if (!ptr)
+ hash_table[i].count = -1;
+ else {
+ hash_table[i].count = li;
+ block *rev = 0;
+ while (ptr) {
+ block *tem = ptr;
+ ptr = ptr->next;
+ tem->next = rev;
+ rev = tem;
+ }
+ while (rev) {
+ fwrite_or_die(rev->v, sizeof(int), rev->used, indxfp);
+ li += rev->used;
+ block *tem = rev;
+ rev = rev->next;
+ delete tem;
+ }
+ fwrite_or_die(&minus_one, sizeof(int), 1, indxfp);
+ li += 1;
+ }
+ }
+ if (sizeof(table_entry) == sizeof(int))
+ fwrite_or_die(hash_table, sizeof(int), hash_table_size, indxfp);
+ else {
+ // write it out word by word
+ for (int i = 0; i < hash_table_size; i++)
+ fwrite_or_die(&hash_table[i].count, sizeof(int), 1, indxfp);
+ }
+ fwrite_or_die(filenames.contents(), 1, filenames.length(), indxfp);
+ if (fseek(indxfp, 0, 0) < 0)
+ fatal("error seeking on index file: %1", strerror(errno));
+ index_header h;
+ h.magic = INDEX_MAGIC;
+ h.version = INDEX_VERSION;
+ h.tags_size = ntags;
+ h.lists_size = li;
+ h.table_size = hash_table_size;
+ h.strings_size = filenames.length();
+ h.truncate = truncate_len;
+ h.shortest = shortest_len;
+ h.common = n_ignore_words;
+ fwrite_or_die(&h, sizeof(h), 1, indxfp);
+}
+
+static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp)
+{
+ if (fwrite(ptr, size, nitems, fp) != nitems)
+ fatal("fwrite failed: %1", strerror(errno));
+}
+
+void fatal_error_exit()
+{
+ cleanup();
+ exit(3);
+}
+
+extern "C" {
+
+void cleanup()
+{
+ if (temp_index_file)
+ unlink(temp_index_file);
+}
+
+}
diff --git a/contrib/groff/src/utils/indxbib/indxbib.man b/contrib/groff/src/utils/indxbib/indxbib.man
new file mode 100644
index 000000000000..ed84ccaec2c8
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/indxbib.man
@@ -0,0 +1,207 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH @G@INDXBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@indxbib \- make inverted index for bibliographic databases
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fB@g@indxbib 'u
+.ti \niu
+.B @g@indxbib
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-vw
+.OP \-c file
+.OP \-d dir
+.OP \-f file
+.OP \-h n
+.OP \-i string
+.OP \-k n
+.OP \-l n
+.OP \-n n
+.OP \-o file
+.OP \-t n
+.RI [\ filename \|.\|.\|.\ ]
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B @g@indxbib
+makes an inverted index for the bibliographic databases in
+.IR filename \|.\|.\|.
+for use with
+.BR @g@refer (@MAN1EXT@),
+.BR @g@lookbib (@MAN1EXT@),
+and
+.BR lkbib (@MAN1EXT@).
+The index will be named
+.IB filename @INDEX_SUFFIX@\fR;
+the index is written to a temporary file which is then renamed to this.
+If no filenames are given on the command line because the
+.B \-f
+option has been used, and no
+.B \-o
+option is given, the index will be named
+.BR @DEFAULT_INDEX_NAME@@INDEX_SUFFIX@ .
+.LP
+Bibliographic databases are divided into records by blank lines.
+Within a record, each fields starts with a
+.B %
+character at the beginning of a line.
+Fields have a one letter name which follows the
+.B %
+character.
+.LP
+The values set by the
+.BR \-c ,
+.BR \-n ,
+.BR \-l
+and
+.B \-t
+options are stored in the index;
+when the index is searched, keys will be discarded and truncated in a
+manner appropriate to these options;
+the original keys will be used for verifying that any record
+found using the index actually contains the keys.
+This means that a user of an index need not know whether these
+options were used in the creation of the index,
+provided that not all the keys to be searched for
+would have been discarded during indexing
+and that the user supplies at least the part of each key
+that would have remained after being truncated during indexing.
+The value set by the
+.B \-i
+option is also stored in the index
+and will be used in verifying records found using the index.
+.SH OPTIONS
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-w
+Index whole files.
+Each file is a separate record.
+.TP
+.BI \-c file
+Read the list of common words from
+.I file
+instead of
+.BR @COMMON_WORDS_FILE@ .
+.TP
+.BI \-d dir
+Use
+.I dir
+as the pathname of the current working directory to store in the index,
+instead of the path printed by
+.BR pwd (1).
+Usually
+.I dir
+will be a symbolic link that points to the directory printed by
+.BR pwd (1).
+.TP
+.BI \-f file
+Read the files to be indexed from
+.IR file .
+If
+.I file
+is
+.BR \- ,
+files will be read from the standard input.
+The
+.B \-f
+option can be given at most once.
+.TP
+.BI \-i string
+Don't index the contents of fields whose names are in
+.IR string .
+Initially
+.I string
+is
+.BR XYZ .
+.TP
+.BI \-h n
+Use the first prime greater than or equal to
+.I n
+for the size of the hash table.
+Larger values of
+.I n
+will usually make searching faster,
+but will make the index larger
+and
+.B @g@indxbib
+use more memory.
+Initially
+.I n
+is 997.
+.TP
+.BI \-k n
+Use at most
+.I n
+keys per input record.
+Initially
+.I n
+is 100.
+.TP
+.BI \-l n
+Discard keys that are shorter than
+.IR n .
+Initially
+.I n
+is 3.
+.TP
+.BI \-n n
+Discard the
+.I n
+most common words.
+Initially
+.I n
+is 100.
+.TP
+.BI \-o basename
+The index should be named
+.IB basename @INDEX_SUFFIX@\fR.
+.TP
+.BI \-t n
+Truncate keys to
+.IR n .
+Initially
+.I n
+is 6.
+.SH FILES
+.TP \w'\fBindxbib\fIXXXXXX'u+2n
+.IB filename @INDEX_SUFFIX@
+Index.
+.TP
+.B @DEFAULT_INDEX_NAME@@INDEX_SUFFIX@
+Default index name.
+.TP
+.B @COMMON_WORDS_FILE@
+List of common words.
+.TP
+.BI indxbib XXXXXX
+Temporary file.
+.SH "SEE ALSO"
+.BR @g@refer (@MAN1EXT@),
+.BR lkbib (@MAN1EXT@),
+.BR @g@lookbib (@MAN1EXT@)
diff --git a/contrib/groff/src/utils/indxbib/signal.c b/contrib/groff/src/utils/indxbib/signal.c
new file mode 100644
index 000000000000..8078472f95db
--- /dev/null
+++ b/contrib/groff/src/utils/indxbib/signal.c
@@ -0,0 +1,63 @@
+/* Copyright (C) 1992 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. */
+
+/* Unfortunately vendors seem to have problems writing a <signal.h>
+that is correct for C++, so we implement all signal handling in C. */
+
+#include <sys/types.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+extern void cleanup();
+
+static RETSIGTYPE handle_fatal_signal(signum)
+ int signum;
+{
+ signal(signum, SIG_DFL);
+ cleanup();
+ kill(getpid(), signum);
+}
+
+void catch_fatal_signals()
+{
+#ifdef SIGHUP
+ signal(SIGHUP, handle_fatal_signal);
+#endif
+ signal(SIGINT, handle_fatal_signal);
+ signal(SIGTERM, handle_fatal_signal);
+}
+
+#ifndef HAVE_RENAME
+
+void ignore_fatal_signals()
+{
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+}
+
+#endif /* not HAVE_RENAME */
diff --git a/contrib/groff/src/utils/lkbib/Makefile.sub b/contrib/groff/src/utils/lkbib/Makefile.sub
new file mode 100644
index 000000000000..8f31e10f5fad
--- /dev/null
+++ b/contrib/groff/src/utils/lkbib/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=lkbib
+MAN1=lkbib.n
+XLIBS=$(LIBBIB) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=lkbib.o
+CCSRCS=$(srcdir)/lkbib.cc
diff --git a/contrib/groff/src/utils/lkbib/lkbib.cc b/contrib/groff/src/utils/lkbib/lkbib.cc
new file mode 100644
index 000000000000..4d3cadcb0166
--- /dev/null
+++ b/contrib/groff/src/utils/lkbib/lkbib.cc
@@ -0,0 +1,137 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+
+#include "defs.h"
+#include "refid.h"
+#include "search.h"
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-nv] [-p database] [-i XYZ] [-t N] keys ...\n",
+ program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int search_default = 1;
+ search_list list;
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "nvVi:t:p:", long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'V':
+ verify_flag = 1;
+ break;
+ case 'n':
+ search_default = 0;
+ break;
+ case 'i':
+ linear_ignore_fields = optarg;
+ break;
+ case 't':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg) {
+ error("bad integer `%1' in `t' option", optarg);
+ break;
+ }
+ if (n < 1)
+ n = 1;
+ linear_truncate_len = int(n);
+ break;
+ }
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU lkbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'p':
+ list.add_file(optarg);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc) {
+ usage(stderr);
+ exit(1);
+ }
+ char *filename = getenv("REFER");
+ if (filename)
+ list.add_file(filename);
+ else if (search_default)
+ list.add_file(DEFAULT_INDEX, 1);
+ if (list.nfiles() == 0)
+ fatal("no databases");
+ int total_len = 0;
+ int i;
+ for (i = optind; i < argc; i++)
+ total_len += strlen(argv[i]);
+ total_len += argc - optind - 1 + 1; // for spaces and '\0'
+ char *buffer = new char[total_len];
+ char *ptr = buffer;
+ for (i = optind; i < argc; i++) {
+ if (i > optind)
+ *ptr++ = ' ';
+ strcpy(ptr, argv[i]);
+ ptr = strchr(ptr, '\0');
+ }
+ search_list_iterator iter(&list, buffer);
+ const char *start;
+ int len;
+ int count;
+ for (count = 0; iter.next(&start, &len); count++) {
+ if (fwrite(start, 1, len, stdout) != len)
+ fatal("write error on stdout: %1", strerror(errno));
+ // Can happen for last reference in file.
+ if (start[len - 1] != '\n')
+ putchar('\n');
+ putchar('\n');
+ }
+ return !count;
+}
diff --git a/contrib/groff/src/utils/lkbib/lkbib.man b/contrib/groff/src/utils/lkbib/lkbib.man
new file mode 100644
index 000000000000..872313de0155
--- /dev/null
+++ b/contrib/groff/src/utils/lkbib/lkbib.man
@@ -0,0 +1,110 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.ds g \" empty
+.ds G \" empty
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH LKBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+lkbib \- search bibliographic databases
+.SH SYNOPSIS
+.B lkbib
+[
+.B \-v
+]
+[
+.BI \-i fields
+]
+[
+.BI \-p filename
+]
+[
+.BI \-t n
+]
+.IR key \|.\|.\|.
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B lkbib
+searches bibliographic databases for references that contain the keys
+.IR key \|.\|.\|.
+and prints any references found on the standard output.
+.B lkbib
+will search any databases given by
+.B \-p
+options, and then a default database.
+The default database is taken from the
+.SB REFER
+environment variable if it is set,
+otherwise it is
+.BR @DEFAULT_INDEX@ .
+For each database
+.I filename
+to be searched,
+if an index
+.IB filename @INDEX_SUFFIX@
+created by
+.BR @g@indxbib (@MAN1EXT@)
+exists, then it will be searched instead;
+each index can cover multiple databases.
+.SH OPTIONS
+.TP
+.B \-v
+Print the version number.
+.TP
+.BI \-p filename
+Search
+.IR filename .
+Multiple
+.B \-p
+options can be used.
+.TP
+.BI \-i string
+When searching files for which no index exists,
+ignore the contents of fields whose names are in
+.IR string .
+.TP
+.BI \-t n
+Only require the first
+.I n
+characters of keys to be given.
+Initially
+.I n
+is 6.
+.SH ENVIRONMENT
+.TP \w'\fBREFER'u+2n
+.SB REFER
+Default database.
+.SH FILES
+.Tp \w'\fB@DEFAULT_INDEX@'u+2n
+.B @DEFAULT_INDEX@
+Default database to be used if the
+.SB REFER
+environment variable is not set.
+.IB filename @INDEX_SUFFIX@
+Index files.
+.SH "SEE ALSO"
+.BR @g@refer (@MAN1EXT@),
+.BR @g@lookbib (@MAN1EXT@),
+.BR @g@indxbib (@MAN1EXT@)
diff --git a/contrib/groff/src/utils/lookbib/Makefile.sub b/contrib/groff/src/utils/lookbib/Makefile.sub
new file mode 100644
index 000000000000..91b140453153
--- /dev/null
+++ b/contrib/groff/src/utils/lookbib/Makefile.sub
@@ -0,0 +1,7 @@
+PROG=lookbib
+MAN1=lookbib.n
+XLIBS=$(LIBBIB) $(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=lookbib.o
+CCSRCS=$(srcdir)/lookbib.cc
+NAMEPREFIX=$(g)
diff --git a/contrib/groff/src/utils/lookbib/lookbib.cc b/contrib/groff/src/utils/lookbib/lookbib.cc
new file mode 100644
index 000000000000..dc55ed93bc95
--- /dev/null
+++ b/contrib/groff/src/utils/lookbib/lookbib.cc
@@ -0,0 +1,140 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+#include "cset.h"
+
+#include "refid.h"
+#include "search.h"
+
+extern "C" {
+ int isatty(int);
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-v] [-i XYZ] [-t N] database ...\n",
+ program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "vVi:t:", long_options, NULL)) != EOF)
+ switch (opt) {
+ case 'V':
+ verify_flag = 1;
+ break;
+ case 'i':
+ linear_ignore_fields = optarg;
+ break;
+ case 't':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg) {
+ error("bad integer `%1' in `t' option", optarg);
+ break;
+ }
+ if (n < 1)
+ n = 1;
+ linear_truncate_len = int(n);
+ break;
+ }
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU lookbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc) {
+ usage(stderr);
+ exit(1);
+ }
+ search_list list;
+ for (int i = optind; i < argc; i++)
+ list.add_file(argv[i]);
+ if (list.nfiles() == 0)
+ fatal("no databases");
+ char line[1024];
+ int interactive = isatty(fileno(stdin));
+ for (;;) {
+ if (interactive) {
+ fputs("> ", stderr);
+ fflush(stderr);
+ }
+ if (!fgets(line, sizeof(line), stdin))
+ break;
+ char *ptr = line;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0')
+ continue;
+ search_list_iterator iter(&list, line);
+ const char *start;
+ int len;
+ int count;
+ for (count = 0; iter.next(&start, &len); count++) {
+ if (fwrite(start, 1, len, stdout) != len)
+ fatal("write error on stdout: %1", strerror(errno));
+ // Can happen for last reference in file.
+ if (start[len - 1] != '\n')
+ putchar('\n');
+ putchar('\n');
+ }
+ fflush(stdout);
+ if (interactive) {
+ fprintf(stderr, "%d found\n", count);
+ fflush(stderr);
+ }
+ }
+ if (interactive)
+ putc('\n', stderr);
+ return 0;
+}
+
diff --git a/contrib/groff/src/utils/lookbib/lookbib.man b/contrib/groff/src/utils/lookbib/lookbib.man
new file mode 100644
index 000000000000..846798dce02d
--- /dev/null
+++ b/contrib/groff/src/utils/lookbib/lookbib.man
@@ -0,0 +1,78 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH @G@LOOKBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+@g@lookbib \- search bibliographic databases
+.SH SYNOPSIS
+.B @g@lookbib
+[
+.B \-v
+]
+[
+.BI \-i string
+]
+[
+.BI \-t n
+]
+.IR filename \|.\|.\|.
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+@g@lookbib prints a prompt on the standard error (unless the standard input is not a terminal),
+reads from the standard input a line containing a set of keywords,
+searches the bibliographic databases
+.IR filename \|.\|.\|.
+for references containing those keywords,
+prints any references found on the standard output,
+and repeats this process until the end of input.
+For each database
+.I filename
+to be searched,
+if an index
+.IB filename @INDEX_SUFFIX@
+created by
+.BR @g@indxbib (@MAN1EXT@)
+exists, then it will be searched instead;
+each index can cover multiple databases.
+.SH OPTIONS
+.TP
+.B \-v
+Print the version number.
+.TP
+.BI \-i string
+When searching files for which no index exists,
+ignore the contents of fields whose names are in
+.IR string .
+.TP
+.BI \-t n
+Only require the first
+.I n
+characters of keys to be given.
+Initially
+.I n
+is 6.
+.SH FILES
+.TP \w'\fIfilename\fB@INDEX_SUFFIX@'u+2n
+.IB filename @INDEX_SUFFIX@
+Index files.
+.SH "SEE ALSO"
+.BR @g@refer (@MAN1EXT@),
+.BR lkbib (@MAN1EXT@),
+.BR @g@indxbib (@MAN1EXT@)
diff --git a/contrib/groff/src/utils/pfbtops/Makefile.sub b/contrib/groff/src/utils/pfbtops/Makefile.sub
new file mode 100644
index 000000000000..f731ff578ba2
--- /dev/null
+++ b/contrib/groff/src/utils/pfbtops/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=pfbtops
+MAN1=pfbtops.n
+OBJS=pfbtops.o
+CSRCS=$(srcdir)/pfbtops.c
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
diff --git a/contrib/groff/src/utils/pfbtops/pfbtops.c b/contrib/groff/src/utils/pfbtops/pfbtops.c
new file mode 100644
index 000000000000..2159dbd8bf78
--- /dev/null
+++ b/contrib/groff/src/utils/pfbtops/pfbtops.c
@@ -0,0 +1,131 @@
+/* This translates ps fonts in .pfb format to ASCII ps files. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <limits.h>
+
+#include "nonposix.h"
+
+/* Binary bytes per output line. */
+#define BYTES_PER_LINE (64/2)
+#define HEX_DIGITS "0123456789abcdef"
+
+static char *program_name;
+
+static void error(s)
+ char *s;
+{
+ fprintf(stderr, "%s: %s\n", program_name, s);
+ exit(2);
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-v] [pfb_file]\n", program_name);
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int opt;
+ extern int optind;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ program_name = argv[0];
+
+ while ((opt = getopt_long(argc, argv, "v", long_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'v':
+ {
+ extern char *Version_string;
+ printf("GNU pfbtops (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ }
+ }
+
+ if (argc - optind > 1) {
+ usage(stderr);
+ exit(1);
+ }
+ if (argc > optind && !freopen(argv[optind], "r", stdin))
+ {
+ perror(argv[optind]);
+ exit(1);
+ }
+#ifdef SET_BINARY
+ SET_BINARY(fileno(stdin));
+#endif
+ for (;;)
+ {
+ int type, c, i;
+ long n;
+
+ c = getchar();
+ if (c != 0x80)
+ error("first byte of packet not 0x80");
+ type = getchar();
+ if (type == 3)
+ break;
+ if (type != 1 && type != 2)
+ error("bad packet type");
+ n = 0;
+ for (i = 0; i < 4; i++)
+ {
+ c = getchar();
+ if (c == EOF)
+ error("end of file in packet header");
+ n |= (long)c << (i << 3);
+ }
+ if (n < 0)
+ error("negative packet length");
+ if (type == 1)
+ {
+ while (--n >= 0)
+ {
+ c = getchar();
+ if (c == EOF)
+ error("end of file in text packet");
+ if (c == '\r')
+ c = '\n';
+ putchar(c);
+ }
+ if (c != '\n')
+ putchar('\n');
+ }
+ else
+ {
+ int count = 0;
+ while (--n >= 0)
+ {
+ c = getchar();
+ if (c == EOF)
+ error("end of file in binary packet");
+ if (count >= BYTES_PER_LINE)
+ {
+ putchar('\n');
+ count = 0;
+ }
+ count++;
+ putchar(HEX_DIGITS[(c >> 4) & 0xf]);
+ putchar(HEX_DIGITS[c & 0xf]);
+ }
+ putchar('\n');
+ }
+ }
+ exit(0);
+}
diff --git a/contrib/groff/src/utils/pfbtops/pfbtops.man b/contrib/groff/src/utils/pfbtops/pfbtops.man
new file mode 100644
index 000000000000..cfef3e0a1033
--- /dev/null
+++ b/contrib/groff/src/utils/pfbtops/pfbtops.man
@@ -0,0 +1,44 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-1995 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.TH PFBTOPS @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+pfbtops \- translate a PostScript font in .pfb format to ASCII
+.SH SYNOPSIS
+.B pfbtops
+[
+.I pfb_file
+]
+.SH DESCRIPTION
+.B pfbtops
+translates a PostScript font in
+.B .pfb
+format to ASCII.
+If
+.I pfb_file
+is omitted the pfb file will be read from the standard input.
+The ASCII format PostScript font will be written on the standard output.
+PostScript fonts for MS-DOS are normally supplied in
+.B .pfb
+format.
+.LP
+The resulting ASCII format PostScript font can be used with groff.
+It must first be listed in
+.BR @FONTDIR@/devps/download .
+.SH "SEE ALSO"
+.BR grops (@MAN1EXT@)
diff --git a/contrib/groff/src/utils/tfmtodit/Makefile.sub b/contrib/groff/src/utils/tfmtodit/Makefile.sub
new file mode 100644
index 000000000000..057bb3e66131
--- /dev/null
+++ b/contrib/groff/src/utils/tfmtodit/Makefile.sub
@@ -0,0 +1,6 @@
+PROG=tfmtodit
+MAN1=tfmtodit.n
+XLIBS=$(LIBGROFF)
+MLIB=$(LIBM)
+OBJS=tfmtodit.o
+CCSRCS=$(srcdir)/tfmtodit.cc
diff --git a/contrib/groff/src/utils/tfmtodit/tfmtodit.cc b/contrib/groff/src/utils/tfmtodit/tfmtodit.cc
new file mode 100644
index 000000000000..96768d2beba7
--- /dev/null
+++ b/contrib/groff/src/utils/tfmtodit/tfmtodit.cc
@@ -0,0 +1,874 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-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. */
+
+/* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
+but I haven't tested them. */
+
+/* Groff requires more font metric information than TeX. The reason
+for this is that TeX has separate Math Italic fonts, whereas groff
+uses normal italic fonts for math. The two additional pieces of
+information required by groff correspond to the two arguments to the
+math_fit() macro in the Metafont programs for the CM fonts. In the
+case of a font for which math_fitting is false, these two arguments
+are normally ignored by Metafont. We need to get hold of these two
+parameters and put them in the groff font file.
+
+We do this by loading this definition after cmbase when creating cm.base.
+
+def ignore_math_fit(expr left_adjustment,right_adjustment) =
+ special "adjustment";
+ numspecial left_adjustment*16/designsize;
+ numspecial right_adjustment*16/designsize;
+ enddef;
+
+This puts the two arguments to the math_fit macro into the gf file.
+(They will appear in the gf file immediately before the character to
+which they apply.) We then create a gf file using this cm.base. Then
+we run tfmtodit and specify this gf file with the -g option.
+
+This need only be done for a font for which math_fitting is false;
+When it's true, the left_correction and subscript_correction should
+both be zero. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "assert.h"
+#include "cset.h"
+#include "nonposix.h"
+
+/* Values in the tfm file should be multiplied by this. */
+
+#define MULTIPLIER 1
+
+struct char_info_word {
+ unsigned char width_index;
+ char height_index;
+ char depth_index;
+ char italic_index;
+ char tag;
+ unsigned char remainder;
+};
+
+struct lig_kern_command {
+ unsigned char skip_byte;
+ unsigned char next_char;
+ unsigned char op_byte;
+ unsigned char remainder;
+};
+
+class tfm {
+ int bc;
+ int ec;
+ int nw;
+ int nh;
+ int nd;
+ int ni;
+ int nl;
+ int nk;
+ int np;
+ int cs;
+ int ds;
+ char_info_word *char_info;
+ int *width;
+ int *height;
+ int *depth;
+ int *italic;
+ lig_kern_command *lig_kern;
+ int *kern;
+ int *param;
+public:
+ tfm();
+ ~tfm();
+ int load(const char *);
+ int contains(int);
+ int get_width(int);
+ int get_height(int);
+ int get_depth(int);
+ int get_italic(int);
+ int get_param(int, int *);
+ int get_checksum();
+ int get_design_size();
+ int get_lig(unsigned char, unsigned char, unsigned char *);
+ friend class kern_iterator;
+};
+
+class kern_iterator {
+ tfm *t;
+ int c;
+ int i;
+public:
+ kern_iterator(tfm *);
+ int next(unsigned char *c1, unsigned char *c2, int *k);
+};
+
+
+kern_iterator::kern_iterator(tfm *p)
+: t(p), c(t->bc), i(-1)
+{
+}
+
+int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
+{
+ for (; c <= t->ec; c++)
+ if (t->char_info[c - t->bc].tag == 1) {
+ if (i < 0) {
+ i = t->char_info[c - t->bc].remainder;
+ if (t->lig_kern[i].skip_byte > 128)
+ i = (256*t->lig_kern[i].op_byte
+ + t->lig_kern[i].remainder);
+ }
+ for (;;) {
+ int skip = t->lig_kern[i].skip_byte;
+ if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
+ *c1 = c;
+ *c2 = t->lig_kern[i].next_char;
+ *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
+ + t->lig_kern[i].remainder];
+ if (skip == 128) {
+ c++;
+ i = -1;
+ }
+ else
+ i += skip + 1;
+ return 1;
+ }
+ if (skip >= 128)
+ break;
+ i += skip + 1;
+ }
+ i = -1;
+ }
+ return 0;
+}
+
+tfm::tfm()
+: char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
+ kern(0), param(0)
+{
+}
+
+int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
+{
+ if (contains(c1) && char_info[c1 - bc].tag == 1) {
+ int i = char_info[c1 - bc].remainder;
+ if (lig_kern[i].skip_byte > 128)
+ i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
+ for (;;) {
+ int skip = lig_kern[i].skip_byte;
+ if (skip > 128)
+ break;
+ // We are only interested in normal ligatures, for which
+ // op_byte == 0.
+ if (lig_kern[i].op_byte == 0
+ && lig_kern[i].next_char == c2) {
+ *cp = lig_kern[i].remainder;
+ return 1;
+ }
+ if (skip == 128)
+ break;
+ i += skip + 1;
+ }
+ }
+ return 0;
+}
+
+int tfm::contains(int i)
+{
+ return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
+}
+
+int tfm::get_width(int i)
+{
+ return width[char_info[i - bc].width_index];
+}
+
+int tfm::get_height(int i)
+{
+ return height[char_info[i - bc].height_index];
+}
+
+int tfm::get_depth(int i)
+{
+ return depth[char_info[i - bc].depth_index];
+}
+
+int tfm::get_italic(int i)
+{
+ return italic[char_info[i - bc].italic_index];
+}
+
+int tfm::get_param(int i, int *p)
+{
+ if (i <= 0 || i > np)
+ return 0;
+ else {
+ *p = param[i - 1];
+ return 1;
+ }
+}
+
+int tfm::get_checksum()
+{
+ return cs;
+}
+
+int tfm::get_design_size()
+{
+ return ds;
+}
+
+tfm::~tfm()
+{
+ a_delete char_info;
+ a_delete width;
+ a_delete height;
+ a_delete depth;
+ a_delete italic;
+ a_delete lig_kern;
+ a_delete kern;
+ a_delete param;
+}
+
+int read2(unsigned char *&s)
+{
+ int n;
+ n = *s++ << 8;
+ n |= *s++;
+ return n;
+}
+
+int read4(unsigned char *&s)
+{
+ int n;
+ n = *s++ << 24;
+ n |= *s++ << 16;
+ n |= *s++ << 8;
+ n |= *s++;
+ return n;
+}
+
+
+int tfm::load(const char *file)
+{
+ errno = 0;
+ FILE *fp = fopen(file, FOPEN_RB);
+ if (!fp) {
+ error("can't open `%1': %2", file, strerror(errno));
+ return 0;
+ }
+ int c1 = getc(fp);
+ int c2 = getc(fp);
+ if (c1 == EOF || c2 == EOF) {
+ fclose(fp);
+ error("unexpected end of file on `%1'", file);
+ return 0;
+ }
+ int lf = (c1 << 8) + c2;
+ int toread = lf*4 - 2;
+ unsigned char *buf = new unsigned char[toread];
+ if (fread(buf, 1, toread, fp) != toread) {
+ if (feof(fp))
+ error("unexpected end of file on `%1'", file);
+ else
+ error("error on file `%1'", file);
+ a_delete buf;
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ if (lf < 6) {
+ error("bad tfm file `%1': impossibly short", file);
+ a_delete buf;
+ return 0;
+ }
+ unsigned char *ptr = buf;
+ int lh = read2(ptr);
+ bc = read2(ptr);
+ ec = read2(ptr);
+ nw = read2(ptr);
+ nh = read2(ptr);
+ nd = read2(ptr);
+ ni = read2(ptr);
+ nl = read2(ptr);
+ nk = read2(ptr);
+ int ne = read2(ptr);
+ np = read2(ptr);
+ if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
+ error("bad tfm file `%1': lengths do not sum", file);
+ a_delete buf;
+ return 0;
+ }
+ if (lh < 2) {
+ error("bad tfm file `%1': header too short", file);
+ a_delete buf;
+ return 0;
+ }
+ char_info = new char_info_word[ec - bc + 1];
+ width = new int[nw];
+ height = new int[nh];
+ depth = new int[nd];
+ italic = new int[ni];
+ lig_kern = new lig_kern_command[nl];
+ kern = new int[nk];
+ param = new int[np];
+ int i;
+ cs = read4(ptr);
+ ds = read4(ptr);
+ ptr += (lh-2)*4;
+ for (i = 0; i < ec - bc + 1; i++) {
+ char_info[i].width_index = *ptr++;
+ unsigned char tem = *ptr++;
+ char_info[i].depth_index = tem & 0xf;
+ char_info[i].height_index = tem >> 4;
+ tem = *ptr++;
+ char_info[i].italic_index = tem >> 2;
+ char_info[i].tag = tem & 3;
+ char_info[i].remainder = *ptr++;
+ }
+ for (i = 0; i < nw; i++)
+ width[i] = read4(ptr);
+ for (i = 0; i < nh; i++)
+ height[i] = read4(ptr);
+ for (i = 0; i < nd; i++)
+ depth[i] = read4(ptr);
+ for (i = 0; i < ni; i++)
+ italic[i] = read4(ptr);
+ for (i = 0; i < nl; i++) {
+ lig_kern[i].skip_byte = *ptr++;
+ lig_kern[i].next_char = *ptr++;
+ lig_kern[i].op_byte = *ptr++;
+ lig_kern[i].remainder = *ptr++;
+ }
+ for (i = 0; i < nk; i++)
+ kern[i] = read4(ptr);
+ ptr += ne*4;
+ for (i = 0; i < np; i++)
+ param[i] = read4(ptr);
+ assert(ptr == buf + lf*4 - 2);
+ a_delete buf;
+ return 1;
+}
+
+class gf {
+ int left[256];
+ int right[256];
+ static int sread4(int *p, FILE *fp);
+ static int uread3(int *p, FILE *fp);
+ static int uread2(int *p, FILE *fp);
+ static int skip(int n, FILE *fp);
+public:
+ gf();
+ int load(const char *file);
+ int get_left_adjustment(int i) { return left[i]; }
+ int get_right_adjustment(int i) { return right[i]; }
+};
+
+gf::gf()
+{
+ for (int i = 0; i < 256; i++)
+ left[i] = right[i] = 0;
+}
+
+int gf::load(const char *file)
+{
+ enum {
+ paint_0 = 0,
+ paint1 = 64,
+ boc = 67,
+ boc1 = 68,
+ eoc = 69,
+ skip0 = 70,
+ skip1 = 71,
+ new_row_0 = 74,
+ xxx1 = 239,
+ yyy = 243,
+ no_op = 244,
+ pre = 247,
+ post = 248
+ };
+ int got_an_adjustment = 0;
+ int pending_adjustment = 0;
+ int left_adj, right_adj;
+ const int gf_id_byte = 131;
+ errno = 0;
+ FILE *fp = fopen(file, FOPEN_RB);
+ if (!fp) {
+ error("can't open `%1': %2", file, strerror(errno));
+ return 0;
+ }
+ if (getc(fp) != pre || getc(fp) != gf_id_byte) {
+ error("bad gf file");
+ return 0;
+ }
+ int n = getc(fp);
+ if (n == EOF)
+ goto eof;
+ if (!skip(n, fp))
+ goto eof;
+ for (;;) {
+ int op = getc(fp);
+ if (op == EOF)
+ goto eof;
+ if (op == post)
+ break;
+ if ((op >= paint_0 && op <= paint_0 + 63)
+ || (op >= new_row_0 && op <= new_row_0 + 164))
+ continue;
+ switch (op) {
+ case no_op:
+ case eoc:
+ case skip0:
+ break;
+ case paint1:
+ case skip1:
+ if (!skip(1, fp))
+ goto eof;
+ break;
+ case paint1 + 1:
+ case skip1 + 1:
+ if (!skip(2, fp))
+ goto eof;
+ break;
+ case paint1 + 2:
+ case skip1 + 2:
+ if (!skip(3, fp))
+ goto eof;
+ break;
+ case boc:
+ {
+ int code;
+ if (!sread4(&code, fp))
+ goto eof;
+ if (pending_adjustment) {
+ pending_adjustment = 0;
+ left[code & 0377] = left_adj;
+ right[code & 0377] = right_adj;
+ }
+ if (!skip(20, fp))
+ goto eof;
+ break;
+ }
+ case boc1:
+ {
+ int code = getc(fp);
+ if (code == EOF)
+ goto eof;
+ if (pending_adjustment) {
+ pending_adjustment = 0;
+ left[code] = left_adj;
+ right[code] = right_adj;
+ }
+ if (!skip(4, fp))
+ goto eof;
+ break;
+ }
+ case xxx1:
+ {
+ int len = getc(fp);
+ if (len == EOF)
+ goto eof;
+ char buf[256];
+ if (fread(buf, 1, len, fp) != len)
+ goto eof;
+ if (len == 10 /* strlen("adjustment") */
+ && memcmp(buf, "adjustment", len) == 0) {
+ int c = getc(fp);
+ if (c != yyy) {
+ if (c != EOF)
+ ungetc(c, fp);
+ break;
+ }
+ if (!sread4(&left_adj, fp))
+ goto eof;
+ c = getc(fp);
+ if (c != yyy) {
+ if (c != EOF)
+ ungetc(c, fp);
+ break;
+ }
+ if (!sread4(&right_adj, fp))
+ goto eof;
+ got_an_adjustment = 1;
+ pending_adjustment = 1;
+ }
+ break;
+ }
+ case xxx1 + 1:
+ if (!uread2(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case xxx1 + 2:
+ if (!uread3(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case xxx1 + 3:
+ if (!sread4(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case yyy:
+ if (!skip(4, fp))
+ goto eof;
+ break;
+ default:
+ fatal("unrecognized opcode `%1'", op);
+ break;
+ }
+ }
+ if (!got_an_adjustment)
+ warning("no adjustment specials found in gf file");
+ return 1;
+ eof:
+ error("unexpected end of file");
+ return 0;
+}
+
+int gf::sread4(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ if (*p >= 128)
+ *p -= 256;
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::uread3(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::uread2(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::skip(int n, FILE *fp)
+{
+ while (--n >= 0)
+ if (getc(fp) == EOF)
+ return 0;
+ return 1;
+}
+
+
+struct char_list {
+ char *ch;
+ char_list *next;
+ char_list(const char *, char_list * = 0);
+};
+
+char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
+{
+}
+
+
+int read_map(const char *file, char_list **table)
+{
+ errno = 0;
+ FILE *fp = fopen(file, "r");
+ if (!fp) {
+ error("can't open `%1': %2", file, strerror(errno));
+ return 0;
+ }
+ for (int i = 0; i < 256; i++)
+ table[i] = 0;
+ char buf[512];
+ int lineno = 0;
+ while (fgets(buf, int(sizeof(buf)), fp)) {
+ lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+ ptr = strtok(ptr, " \n\t");
+ if (!ptr)
+ continue;
+ int n;
+ if (sscanf(ptr, "%d", &n) != 1) {
+ error("%1:%2: bad map file", file, lineno);
+ fclose(fp);
+ return 0;
+ }
+ if (n < 0 || n > 255) {
+ error("%1:%2: code out of range", file, lineno);
+ fclose(fp);
+ return 0;
+ }
+ ptr = strtok(0, " \n\t");
+ if (!ptr) {
+ error("%1:%2: missing names", file, lineno);
+ fclose(fp);
+ return 0;
+ }
+ for (; ptr; ptr = strtok(0, " \n\t"))
+ table[n] = new char_list(ptr, table[n]);
+ }
+ fclose(fp);
+ return 1;
+}
+
+
+/* Every character that can participate in a ligature appears in the
+lig_chars table. `ch' gives the full-name of the character, `name'
+gives the groff name of the character, `i' gives its index in
+the encoding, which is filled in later (-1 if it does not appear). */
+
+struct {
+ const char *ch;
+ int i;
+} lig_chars[] = {
+ { "f", -1 },
+ { "i", -1 },
+ { "l", -1 },
+ { "ff", -1 },
+ { "fi", -1 },
+ { "fl", -1 },
+ { "Fi", -1 },
+ { "Fl", -1 },
+};
+
+// Indices into lig_chars[].
+
+enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
+
+// Each possible ligature appears in this table.
+
+struct {
+ unsigned char c1, c2, res;
+ const char *ch;
+} lig_table[] = {
+ { CH_f, CH_f, CH_ff, "ff" },
+ { CH_f, CH_i, CH_fi, "fi" },
+ { CH_f, CH_l, CH_fl, "fl" },
+ { CH_ff, CH_i, CH_ffi, "ffi" },
+ { CH_ff, CH_l, CH_ffl, "ffl" },
+ };
+
+static void usage(FILE *stream);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int special_flag = 0;
+ int skewchar = -1;
+ int opt;
+ const char *gf_file = 0;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL)) != EOF)
+ switch (opt) {
+ case 'g':
+ gf_file = optarg;
+ break;
+ case 's':
+ special_flag = 1;
+ break;
+ case 'k':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 0);
+ if ((n == 0 && ptr == optarg)
+ || *ptr != '\0'
+ || n < 0
+ || n > UCHAR_MAX)
+ error("invalid skewchar");
+ else
+ skewchar = (int)n;
+ break;
+ }
+ case 'v':
+ {
+ extern const char *Version_string;
+ printf("GNU tfmtodit (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ case EOF:
+ assert(0);
+ }
+ if (argc - optind != 3) {
+ usage(stderr);
+ exit(1);
+ }
+ gf g;
+ if (gf_file) {
+ if (!g.load(gf_file))
+ return 1;
+ }
+ const char *tfm_file = argv[optind];
+ const char *map_file = argv[optind + 1];
+ const char *font_file = argv[optind + 2];
+ tfm t;
+ if (!t.load(tfm_file))
+ return 1;
+ char_list *table[256];
+ if (!read_map(map_file, table))
+ return 1;
+ errno = 0;
+ if (!freopen(font_file, "w", stdout)) {
+ error("can't open `%1' for writing: %2", font_file, strerror(errno));
+ return 1;
+ }
+ printf("name %s\n", font_file);
+ if (special_flag)
+ fputs("special\n", stdout);
+ char *internal_name = strsave(argv[optind]);
+ int len = strlen(internal_name);
+ if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
+ internal_name[len - 4] = '\0';
+ // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
+ // We want the rightmost separator of all possible ones.
+ // Example: d:/foo\\bar.
+ const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1;
+ const char *sep = &DIR_SEPS[1];
+ while (*sep)
+ {
+ s1 = strrchr(internal_name, *sep);
+ if (s1 && (!s || s1 > s))
+ s = s1;
+ sep++;
+ }
+ printf("internalname %s\n", s ? s + 1 : internal_name);
+ int n;
+ if (t.get_param(2, &n)) {
+ if (n > 0)
+ printf("spacewidth %d\n", n*MULTIPLIER);
+ }
+ if (t.get_param(1, &n) && n != 0)
+ printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI);
+ int xheight;
+ if (!t.get_param(5, &xheight))
+ xheight = 0;
+ int i;
+ // Print the list of ligatures.
+ // First find the indices of each character that can participate in
+ // a ligature.
+ for (i = 0; i < 256; i++)
+ for (int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
+ for (char_list *p = table[i]; p; p = p->next)
+ if (strcmp(lig_chars[j].ch, p->ch) == 0)
+ lig_chars[j].i = i;
+ // For each possible ligature, if its participants all exist,
+ // and it appears as a ligature in the tfm file, include in
+ // the list of ligatures.
+ int started = 0;
+ for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
+ int i1 = lig_chars[lig_table[i].c1].i;
+ int i2 = lig_chars[lig_table[i].c2].i;
+ int r = lig_chars[lig_table[i].res].i;
+ if (i1 >= 0 && i2 >= 0 && r >= 0) {
+ unsigned char c;
+ if (t.get_lig(i1, i2, &c) && c == r) {
+ if (!started) {
+ started = 1;
+ fputs("ligatures", stdout);
+ }
+ printf(" %s", lig_table[i].ch);
+ }
+ }
+ }
+ if (started)
+ fputs(" 0\n", stdout);
+ printf("checksum %d\n", t.get_checksum());
+ printf("designsize %d\n", t.get_design_size());
+ // Now print out the kerning information.
+ int had_kern = 0;
+ kern_iterator iter(&t);
+ unsigned char c1, c2;
+ int k;
+ while (iter.next(&c1, &c2, &k))
+ if (c2 != skewchar) {
+ k *= MULTIPLIER;
+ char_list *q = table[c2];
+ for (char_list *p1 = table[c1]; p1; p1 = p1->next)
+ for (char_list *p2 = q; p2; p2 = p2->next) {
+ if (!had_kern) {
+ printf("kernpairs\n");
+ had_kern = 1;
+ }
+ printf("%s %s %d\n", p1->ch, p2->ch, k);
+ }
+ }
+ printf("charset\n");
+ char_list unnamed("---");
+ for (i = 0; i < 256; i++)
+ if (t.contains(i)) {
+ char_list *p = table[i] ? table[i] : &unnamed;
+ int m[6];
+ m[0] = t.get_width(i);
+ m[1] = t.get_height(i);
+ m[2] = t.get_depth(i);
+ m[3] = t.get_italic(i);
+ m[4] = g.get_left_adjustment(i);
+ m[5] = g.get_right_adjustment(i);
+ printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
+ int j;
+ for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
+ if (m[j] != 0)
+ break;
+ for (k = 1; k <= j; k++)
+ printf(",%d", m[k]*MULTIPLIER);
+ int type = 0;
+ if (m[2] > 0)
+ type = 1;
+ if (m[1] > xheight)
+ type += 2;
+ printf("\t%d\t%04o\n", type, i);
+ for (p = p->next; p; p = p->next)
+ printf("%s\t\"\n", p->ch);
+ }
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
+ program_name);
+}
diff --git a/contrib/groff/src/utils/tfmtodit/tfmtodit.man b/contrib/groff/src/utils/tfmtodit/tfmtodit.man
new file mode 100644
index 000000000000..6e50aa28d6c9
--- /dev/null
+++ b/contrib/groff/src/utils/tfmtodit/tfmtodit.man
@@ -0,0 +1,175 @@
+.ig \"-*- nroff -*-
+Copyright (C) 1989-2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+..
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.\" Like TP, but if specified indent is more than half
+.\" the current line-length - indent, use the default indent.
+.de Tp
+.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP
+.el .TP "\\$1"
+..
+.TH TFMTODIT @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+tfmtodit \- create font files for use with groff \-Tdvi
+.SH SYNOPSIS
+.nr a \n(.j
+.ad l
+.nr i \n(.i
+.in +\w'\fBtfmtodit 'u
+.ti \niu
+.B tfmtodit
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-sv
+.OP \-g gf_file
+.OP \-k skewchar
+.I tfm_file
+.I map_file
+.I font
+.br
+.ad \na
+.PP
+It is possible to have whitespace between a command line option and its
+parameter.
+.SH DESCRIPTION
+.B tfmtodit
+creates a font file for use with
+.B
+groff \-Tdvi\fR.
+.I tfm_file
+is the name of the \*(tx font metric file for the font.
+.I map_file
+is a file giving the groff names for characters in the font;
+this file should consist of a sequence of lines of the form:
+.IP
+.I
+n c1 c2 \fR.\|.\|.
+.LP
+where
+.I n
+is a decimal integer giving the position of the character in the font,
+and
+.IR c1 ,
+.IR c2 ,.\|.\|.
+are the groff names of the character.
+If a character has no groff names but exists in the tfm file,
+then it will be put in the groff font file as an unnamed character.
+.I font
+is the name of the groff font file.
+The groff font file is written to
+.IR font .
+.LP
+The
+.B \-s
+option should be given if the font is special
+(a font is
+.I special
+if
+.B troff
+should search it whenever
+a character is not found in the current font.)
+If the font is special,
+it should be listed in the
+.B fonts
+command in the DESC file;
+if it is not special, there is no need to list it, since
+.B troff
+can automatically mount it when it's first used.
+.LP
+To do a good job of math typesetting, groff requires
+font metric information not present in the tfm file.
+The reason for this is that \*(tx has separate math italic fonts
+whereas groff uses normal italic fonts for math.
+The additional information required by groff is given by the
+two arguments to the
+.B math_fit
+macro in the Metafont programs for the Computer Modern fonts.
+In a text font (a font for which
+.B math_fitting
+is false), Metafont normally ignores these two arguments.
+Metafont can be made to put this information in the gf file
+by loading the following definition after
+.B cmbase
+when creating
+.BR cm.base :
+.IP
+.nf
+.ft B
+def ignore_math_fit(expr left_adjustment,right_adjustment) =
+ special "adjustment";
+ numspecial left_adjustment*16/designsize;
+ numspecial right_adjustment*16/designsize;
+ enddef;
+.fi
+.ft R
+.LP
+The gf file created using this modified
+.B cm.base
+should be specified with the
+.B \-g
+option.
+The
+.B \-g
+option should not be given for a font for which
+.B math_fitting
+is true.
+.SH OPTIONS
+.TP
+.B \-v
+Print the version number.
+.TP
+.B \-s
+The font is special.
+The effect of this option is to add the
+.B special
+command to the font file.
+.TP
+.BI \-k n
+The skewchar of this font is at position
+.IR n .
+.I n
+should be an integer;
+it may be given in decimal,
+or with a leading
+.B 0
+in octal,
+or with a leading
+.B 0x
+in hexadecimal.
+The effect of this option is to ignore any kerns whose second component
+is the specified character.
+.TP
+.BI \-g gf_file
+.I gf_file
+is a gf file produced by Metafont containing special and numspecial
+commands giving additional font metric information.
+.SH FILES
+.Tp \w'\fB@FONTDIR@/devdvi/DESC'u+2n
+.B @FONTDIR@/devdvi/DESC
+Device description file.
+.TP
+.BI @FONTDIR@/devdvi/ F
+Font description file for font
+.IR F .
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR grodvi (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@)
diff --git a/contrib/groff/src/xditview/ChangeLog b/contrib/groff/src/xditview/ChangeLog
new file mode 100644
index 000000000000..ef0a856eba22
--- /dev/null
+++ b/contrib/groff/src/xditview/ChangeLog
@@ -0,0 +1,413 @@
+2001-01-04 Rob Daasch <daasch@ece.pdx.edu>
+
+ * parse.c (ParseInput): Added 'F' to command switch to swallow
+ filename strings as ignored comments.
+
+2000-12-02 Werner LEMBERG <wl@gnu.org>
+
+ * device.c (find_file): Remove home directory in search path.
+
+2000-11-14 Werner LEMBERG <wl@gnu.org>
+
+ * device.c (open_device_file): Remove `path' parameter.
+ (find_file): Construct font path similar to groff: First the contents
+ of GROFF_FONT_PATH, then the home directory, and finally the default
+ font path.
+ * Imakefile.in: Fix GROFF_DATAPROGRAMDIR and GROFF_FONTPATH.
+
+2000-10-23 Werner LEMBERG <wl@gnu.org>
+
+ Change installation structure for data files from .../groff/... to
+ .../groff/<version><revision>/... to be conform with other GNU
+ programs.
+
+ * Imakefile.in: Implement it.
+
+Version 1.16.1 released
+=======================
+
+Version 1.16 released
+=====================
+
+2000-05-18 Werner LEMBERG <wl@gnu.org>
+
+ * DviChar.c: Adding `cq' as an alias for "'" in latin-1 map.
+
+2000-05-03 Werner LEMBERG <wl@gnu.org>
+
+ * DviChar.c: Adding `dq' as an alias for `"' in latin-1 map.
+
+2000-04-28 Werner LEMBERG <wl@gnu.org>
+
+ * DviChar.c: Replacing `md' glyph name with `pc' in latin-1 map to
+ make it distinct from the `md' glyph in the symbol font.
+
+2000-03-03 Werner LEMBERG <wl@gnu.org>
+
+ * Imakefile replaced with Imakefile.in which will be configured by
+ the main configure script of groff. This will set the correct font
+ path, and it will make it possible to build xditview in a directory
+ different from $srcdir.
+
+2000-03-01 Colin Phipps <crp22@cam.ac.uk>
+
+ * Dvi.c (OpenFile): Use tmpdir() for security reasons.
+ * xtotroff.c (MapFont): Avoid race while opening file.
+
+2000-02-06 Werner LEMBERG <wl@gnu.org>
+
+ * Imakefile: Adapted to new directory structure.
+
+ * README: Updated.
+
+Version 1.15 released
+=====================
+
+1999-12-21 Werner LEMBERG <wl@gnu.org>
+
+ * README: Fixed ftp GNU address.
+
+1999-12-13 Werner LEMBERG <wl@gnu.org>
+
+ * device.c: Use extern declarations of strtok(), strchr(), and
+ getenv() only if not defined as macros.
+
+1999-11-18 Larry Jones <larry.jones@sdrc.com>
+
+ * xditview.c: Add fallback_resources to allow running without
+ access to the app-defaults file.
+
+ * Imakefile: Added rule to create app-defaults to a C header file.
+
+ * GXditview-ad.h: New file containing fallback default resources.
+
+ * ad2c: New file to do the app-defaults -> C header file
+ conversion.
+
+1999-10-27 Larry Jones <larry.jones@sdrc.com>
+
+ * font.c (DisposeFontSizes): If there's a problem loading a font,
+ xditview will fall-back and use the default font, but it hasn't
+ checked before unloading fonts which could result in unloading the
+ default font (possibly multiple times) and then X errors.
+
+1999-09-13 Werner LEMBERG <wl@gnu.org>
+
+ * Imakefile (extraclean): Added Makefile.
+
+ * xditview.c (main, MakePrompt): Fixing compilation warnings.
+
+ * TODO: Imakefile should be replaced with a configure script.
+
+1999-09-13 Werner LEMBERG <wl@gnu.org>
+
+ * Makefile: Removed.
+
+1999-09-12 Werner LEMBERG <wl@gnu.org>
+
+ * Imakefile (GROFF_FONTPATH): Another addition.
+
+ * device.c (FONTPATH): Update to match current groff version.
+
+1999-09-11 Larry Jones <larry.jones@sdrc.com>
+
+ * Imakefile (GROFF_LIBDIR, GROFF_FONTPATH): Update to match
+ current groff version.
+
+ * Dvi.c (Realize, Destroy), DviP.h, draw.c (setFillGC), gray*.bm:
+ Allow 8 levels of gray rather than just 1.
+
+ * draw.c (DrawFilledCircle, DrawFilledEllipse, DrawFilledPolygon):
+ Draw outlines to prevent gaps between abutting figures.
+
+1999-05-27 Werner LEMBERG <wl@gnu.org>
+
+ * xtotroff.c (usage): Fixed typo.
+
+Mon Sep 11 10:40:33 1995 James Clark <jjc@jclark.com>
+
+ * device.c (INT_MIN, INT_MAX): Don't define if already defined.
+
+Mon Aug 8 11:14:11 1994 James Clark (jjc@jclark.com)
+
+ * DviChar.c (Adobe_Symbol_map): Use \(nb for notsubset.
+
+Tue Apr 19 04:41:16 1994 James Clark (jjc@jclark.com)
+
+ * Dvi.c (resources): Change default for background and foreground
+ to "XtDefaultBackground" and "XtDefaultForeground".
+
+Sat Feb 12 10:38:47 1994 James Clark (jjc@jclark.com)
+
+ * DviChar.c (Adobe_Symbol_map): Rename radicalex to rn.
+
+Thu May 27 20:30:12 1993 James Clark (jjc@jclark.com)
+
+ * device.c (isascii): Define if necessary.
+ (canonicalize_name): Cast argument to isdigit() to unsigned char.
+
+Thu Apr 29 18:36:57 1993 James Clark (jjc at jclark.com)
+
+ * xditview.c: Include <X11/Xos.h>.
+ (NewFile): Don't declare rindex(). Use strrchr() rather than
+ rindex().
+
+Tue Mar 30 15:12:09 1993 James Clark (jjc at jclark)
+
+ * draw.c (charExists): Check that fi->per_char is not NULL.
+
+Sat Dec 12 17:42:40 1992 James Clark (jjc at jclark)
+
+ * Dvi.c (SetGeometry): Cast XtMakeGeometryRequest arguments.
+
+ * draw.c (DrawPolygon, DrawFilledPolygon): Cast Xtfree argument.
+
+ * font.c (DisposeFontSizes): Add declaration.
+
+ * draw.c (FakeCharacter): Add declaration.
+
+Wed Oct 28 13:24:00 1992 James Clark (jjc at jclark)
+
+ * Imakefile (install.dev): Deleted.
+ (fonts): New target.
+
+Mon Oct 12 10:50:44 1992 James Clark (jjc at jclark)
+
+ * Imakefile (install.dev): Say when we're installing devX*-12.
+
+ * Imakefile (install.dev): Depends on DESC and FontMap.
+
+Thu Oct 1 20:03:45 1992 James Clark (jjc at jclark)
+
+ * xditview.c (Syntax): Mention -filename option.
+
+Sat Aug 15 12:56:39 1992 James Clark (jjc at jclark)
+
+ * GXditview.ad: Bind space and return to NextPage. Bind backspace
+ and delete to previous page.
+
+ * DviChar.c (Adobe_Symbol_map): Add `an'.
+
+ * DviChar.c (Adobe_Symbol_map): Add arrowvertex, arrowverttp, and
+ arrowvertbt.
+
+Mon Aug 10 11:54:27 1992 James Clark (jjc at jclark)
+
+ * FontMap: Add m/p fields to the fonts names.
+
+Sat Aug 8 12:00:28 1992 James Clark (jjc at jclark)
+
+ * DESC: Leave font positions 5-9 blank.
+
+Tue Jul 28 11:37:05 1992 James Clark (jjc at jclark)
+
+ * Imakefile: Don't use gendef. Pass definition of FONTPATH using
+ DEFINES.
+ (path.h): Deleted.
+ (device.c): Don't include path.h. Provide default definition of
+ FONTPATH.
+
+Mon Jul 6 14:06:53 1992 James Clark (jjc at jclark)
+
+ * Imakefile: Don't install tmac.X and tmac.Xps.
+ * tmac.X, tmac.Xps: Moved to ../macros.
+
+ * Imakefile: Don't install eqnchar.
+ * eqnchar: Deleted.
+
+Sun Jun 14 12:55:02 1992 James Clark (jjc@jclark)
+
+ * tmac.Xps: Handle OE, oe, lq, rq.
+ * draw.c (FakeCharacter): Don't handle these.
+
+ * draw.c (FakeCharacter): Don't handle f/.
+
+Mon Jun 8 11:46:37 1992 James Clark (jjc@jclark)
+
+ * tmac.X: Translate char160 to space.
+
+Sun Jun 7 14:39:53 1992 James Clark (jjc@jclark)
+
+ * tmac.X: Do `mso tmac.psic' before restoring compatibility mode.
+
+ * tmac.X: Add \(OE, \(oe, \(ah, \(ao, \(ho.
+
+ * tmac.Xps: Make it work in compatibility mode.
+ Redo existing character definitions with .Xps-char.
+ Add more character definitions.
+ (Xps-char): New macro.
+
+Sat Jun 6 21:46:03 1992 James Clark (jjc@jclark)
+
+ * DviChar.c (Adobe_Symbol_map): Add +h, +f, +p, Fn, lz.
+ * tmac.X: Add \(bq, \(Bq, \(aq.
+ * tmac.Xps: Handle \(aq, \(bq, \(Bq, \(Fn.
+
+Wed Jun 3 11:11:15 1992 James Clark (jjc@jclark)
+
+ * DviChar.c (Adobe_Symbol_map): Add wp.
+
+Tue Apr 21 09:21:59 1992 James Clark (jjc at jclark)
+
+ * GXditview.ad: Bind n, p, q keys to NextPage, PreviousPage and
+ Quit actions.
+
+ * xditview.c (RerasterizeAction): New function.
+ (xditview_actions): Add RerasterizeAction.
+ * GXditview.ad: Bind r key to Rerasterize action.
+
+Fri Apr 17 08:25:36 1992 James Clark (jjc at jclark)
+
+ * xditview.c: Add -filename option.
+ (main): Copy any -filename argument into current_file_name.
+
+Mon Mar 16 10:21:58 1992 James Clark (jjc at jclark)
+
+ * tmac.X: Load tmac.pspic.
+
+Sun Mar 8 11:27:19 1992 James Clark (jjc at jclark)
+
+ * Lex.c (GetLine, GetWord, GetNumber): Rewrite.
+
+Sat Oct 12 22:58:52 1991 James Clark (jjc at jclark)
+
+ * Dvi.c (SetDevice): If the size change request is refused but a
+ larger geometry is offered, request that.
+
+Wed Oct 9 12:27:48 1991 James Clark (jjc at jclark)
+
+ * font.c (InstallFontSizes): Ignore FontNameAverageWidth component.
+
+ * Dvi.c (default_font_map): Add `adobe' to font names to avoid
+ ambiguity.
+
+ * FontMap: New file.
+ * FontMap.X100, FontMap.X75: Deleted.
+ * xtotroff.c (main, usage): Add -s and -r options.
+ (MapFont): Change the font pattern to have the selected resolution and
+ size.
+ * Imakefile (install.dev): Use FontMap and supply appropriate -s
+ and -r options.
+
+ * xtotroff.c (MapFont): Check for ambiguity by comparing canonicalized
+ font names.
+
+ * DviP.h (DviFontList): Add initialized and scalable members.
+ (font.c): Add support for scalable fonts based on R5 xditview.
+
+ * DviChar.c: Use xmalloc rather than malloc.
+ * xditview.c (xmalloc): New function.
+ * xtotroff.c (xmalloc): New function.
+ * other files: Use XtMalloc and XtFree instead of malloc and free.
+
+Thu Aug 29 20:15:31 1991 James Clark (jjc at jclark)
+
+ * draw.c (setGC): Do multiplication in floating point to avoid
+ overflow.
+
+Tue Aug 13 12:04:41 1991 James Clark (jjc at jclark)
+
+ * draw.c (FakeCharacter): Remove casts in defintion of pack2.
+
+Tue Jul 30 11:42:39 1991 James Clark (jjc at jclark)
+
+ * tmac.Xps: New file.
+ * Imakefile (install): Install tmac.Xps.
+
+Tue Jul 2 09:31:37 1991 James Clark (jjc at jclark)
+
+ * xtotroff.c (main): Pass argv[0] to usage().
+
+Sun Jun 30 12:34:06 1991 James Clark (jjc at jclark)
+
+ * xtotroff.c (MapFont): Handle the case where XLoadQueryFont
+ returns NULL.
+
+Sat Jun 29 12:32:52 1991 James Clark (jjc at jclark)
+
+ * Imakefile: Use ../gendef to generate path.h.
+
+Sun Jun 16 13:26:34 1991 James Clark (jjc at jclark)
+
+ * Imakefile (depend.o): Change to device.o.
+
+Sun Jun 2 12:17:56 1991 James Clark (jjc at jclark)
+
+ * Imakefile: Remove spaces from the beginning of variable
+ assignment lines.
+
+Sun May 26 14:14:01 1991 James Clark (jjc at jclark)
+
+ * xditview.c (Syntax): Update.
+
+ * Dvi.c (DviSaveToFile, SaveToFile): New functions.
+ (FindPage): Check that we're not readingTmp before checking for
+ end of file of normal input file.
+ (ClassPartInitialize): New function.
+ * Dvi.h: Add declaration of DviSaveToFile.
+ * DviP.h: Add save method to DviClassPart. Declare
+ InheritSaveToFile.
+ * xditview.c (DoPrint, Print, PrintAction): New functions.
+ * xditview.c: Add print menu entry.
+ * xditview.c: Provide printCommand application resource.
+ * lex.c: Don't output EOF to temporary file.
+
+ * Dvi.c (QueryGeometry): Check request->request_mode.
+
+ * Dvi.c (SetDevice): New function.
+ (SetDeviceResolution): Deleted.
+
+ * Dvi.c: Add resolution resource.
+ * DviP.h: Add definitions of XtNResolution and XtCResolution.
+ * xditview.c: Add -resolution argument.
+ * GXditview.ad: Add default for GXditview.height.
+ * Dvi.c (Initialize, SetDevice): Use default_resolution.
+
+ * Dvi.c: Make MY_HEIGHT and MY_WIDTH use the paperlength and
+ paperwidth commands in the DESC file.
+
+ * Dvi.c: Add SS font to default font map.
+
+ * draw.c: Rewritten so as not to assume device and display
+ resolution is the same.
+ * DviP.h: Include device.h. Add device_font member to DviFontList.
+ Add adjustable arrary to DviCharCache. Add text_x_width,
+ text_device_width, word_flag, device_font, device_font_number,
+ device, native, device_resolution, display_resolution,
+ paperlength, paperwidth, scale_factor, sizescale members.
+ * Dvi.c (Initialize): Initialize new variable used by draw.c
+ (Destroy): Call device_destroy.
+ * font.c (MaxFontPosition): New function.
+ (LookupFontSizeBySize): Handle sizescale.
+ (InstallFont): Load the device font.
+ (ForgetFonts): New function.
+ (QueryDeviceFont): New function.
+ * parse.c (ParseInput): Handle t and u commands. Split off
+ character output into draw.c.
+ (ParseDeviceControl): Ignore res command. Use the device argument
+ to the T command.
+
+ * font.c (MapXNameToDviName): Ifdefed out.
+
+ * path.h: New file.
+ * device.c, device.h: New files.
+
+ * DviChar.c: Add entries for lB, rB, oq, lC, rC, md.
+
+ * INSTALL: New file.
+
+ * libxdvi: Merged into main directory.
+ * xtotroff.c, xditview.c: Change includes accordingly.
+
+ * devX75, devX100: Merged into main directory.
+ * xditview.man: Renamed to gxditview.man.
+
+ * Xditview.ad: Renamed to GXditview.ad.
+ * xditview.c (main): Use class of GXditview rather than xditview.
+
+ * Imakefile: New file.
+ * Makefile: Deleted.
+
+ * xtotroff.c (MapFont): Unlink output file before opening it.
+
+ * Started separate ChangeLog.
diff --git a/contrib/groff/src/xditview/DESC b/contrib/groff/src/xditview/DESC
new file mode 100644
index 000000000000..172170c9c0b5
--- /dev/null
+++ b/contrib/groff/src/xditview/DESC
@@ -0,0 +1,9 @@
+styles R I B BI
+fonts 6 0 0 0 0 0 S
+sizes 8 10 12 14 18 24 0
+res 75
+X11
+hor 1
+vert 1
+unitwidth 10
+postpro gxditview
diff --git a/contrib/groff/src/xditview/Dvi.c b/contrib/groff/src/xditview/Dvi.c
new file mode 100644
index 000000000000..08eb810a7bc8
--- /dev/null
+++ b/contrib/groff/src/xditview/Dvi.c
@@ -0,0 +1,573 @@
+#ifndef SABER
+#ifndef lint
+static char Xrcsid[] = "$XConsortium: Dvi.c,v 1.9 89/12/10 16:12:25 rws Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+/*
+ * Dvi.c - Dvi display widget
+ *
+ */
+
+#define XtStrlen(s) ((s) ? strlen(s) : 0)
+
+ /* The following are defined for the reader's convenience. Any
+ Xt..Field macro in this code just refers to some field in
+ one of the substructures of the WidgetRec. */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xmu/Converters.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "DviP.h"
+
+/****************************************************************
+ *
+ * Full class record constant
+ *
+ ****************************************************************/
+
+/* Private Data */
+
+static char default_font_map[] = "\
+TR -adobe-times-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+TI -adobe-times-medium-i-normal--*-100-*-*-*-*-iso8859-1\n\
+TB -adobe-times-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+TBI -adobe-times-bold-i-normal--*-100-*-*-*-*-iso8859-1\n\
+CR -adobe-courier-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+CI -adobe-courier-medium-o-normal--*-100-*-*-*-*-iso8859-1\n\
+CB -adobe-courier-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+CBI -adobe-courier-bold-o-normal--*-100-*-*-*-*-iso8859-1\n\
+HR -adobe-helvetica-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+HI -adobe-helvetica-medium-o-normal--*-100-*-*-*-*-iso8859-1\n\
+HB -adobe-helvetica-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+HBI -adobe-helvetica-bold-o-normal--*-100-*-*-*-*-iso8859-1\n\
+NR -adobe-new century schoolbook-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+NI -adobe-new century schoolbook-medium-i-normal--*-100-*-*-*-*-iso8859-1\n\
+NB -adobe-new century schoolbook-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+NBI -adobe-new century schoolbook-bold-i-normal--*-100-*-*-*-*-iso8859-1\n\
+S -adobe-symbol-medium-r-normal--*-100-*-*-*-*-adobe-fontspecific\n\
+SS -adobe-symbol-medium-r-normal--*-100-*-*-*-*-adobe-fontspecific\n\
+";
+
+#define offset(field) XtOffset(DviWidget, field)
+
+#define MY_WIDTH(dw) ((int)(dw->dvi.paperwidth * dw->dvi.scale_factor + .5))
+#define MY_HEIGHT(dw) ((int)(dw->dvi.paperlength * dw->dvi.scale_factor + .5))
+
+static XtResource resources[] = {
+ {XtNfontMap, XtCFontMap, XtRString, sizeof (char *),
+ offset(dvi.font_map_string), XtRString, default_font_map},
+ {XtNforeground, XtCForeground, XtRPixel, sizeof (unsigned long),
+ offset(dvi.foreground), XtRString, "XtDefaultForeground"},
+ {XtNbackground, XtCBackground, XtRPixel, sizeof (unsigned long),
+ offset(dvi.background), XtRString, "XtDefaultBackground"},
+ {XtNpageNumber, XtCPageNumber, XtRInt, sizeof (int),
+ offset(dvi.requested_page), XtRString, "1"},
+ {XtNlastPageNumber, XtCLastPageNumber, XtRInt, sizeof (int),
+ offset (dvi.last_page), XtRString, "0"},
+ {XtNfile, XtCFile, XtRFile, sizeof (FILE *),
+ offset (dvi.file), XtRFile, (char *) 0},
+ {XtNseek, XtCSeek, XtRBoolean, sizeof (Boolean),
+ offset(dvi.seek), XtRString, "false"},
+ {XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
+ offset(dvi.default_font), XtRString, "xtdefaultfont"},
+ {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int),
+ offset(dvi.backing_store), XtRString, "default"},
+ {XtNnoPolyText, XtCNoPolyText, XtRBoolean, sizeof (Boolean),
+ offset(dvi.noPolyText), XtRString, "false"},
+ {XtNresolution, XtCResolution, XtRInt, sizeof(int),
+ offset(dvi.default_resolution), XtRString, "75"},
+};
+
+#undef offset
+
+static void ClassInitialize ();
+static void ClassPartInitialize();
+static void Initialize(), Realize (), Destroy (), Redisplay ();
+static Boolean SetValues (), SetValuesHook ();
+static XtGeometryResult QueryGeometry ();
+static void ShowDvi ();
+static void CloseFile (), OpenFile ();
+static void FindPage ();
+
+static void SaveToFile ();
+
+DviClassRec dviClassRec = {
+{
+ &widgetClassRec, /* superclass */
+ "Dvi", /* class_name */
+ sizeof(DviRec), /* size */
+ ClassInitialize, /* class_initialize */
+ ClassPartInitialize, /* class_part_initialize */
+ FALSE, /* class_inited */
+ Initialize, /* initialize */
+ NULL, /* initialize_hook */
+ Realize, /* realize */
+ NULL, /* actions */
+ 0, /* num_actions */
+ resources, /* resources */
+ XtNumber(resources), /* resource_count */
+ NULLQUARK, /* xrm_class */
+ FALSE, /* compress_motion */
+ TRUE, /* compress_exposure */
+ TRUE, /* compress_enterleave */
+ FALSE, /* visible_interest */
+ Destroy, /* destroy */
+ NULL, /* resize */
+ Redisplay, /* expose */
+ SetValues, /* set_values */
+ SetValuesHook, /* set_values_hook */
+ NULL, /* set_values_almost */
+ NULL, /* get_values_hook */
+ NULL, /* accept_focus */
+ XtVersion, /* version */
+ NULL, /* callback_private */
+ 0, /* tm_table */
+ QueryGeometry, /* query_geometry */
+ NULL, /* display_accelerator */
+ NULL /* extension */
+},{
+ SaveToFile, /* save */
+},
+};
+
+WidgetClass dviWidgetClass = (WidgetClass) &dviClassRec;
+
+static void ClassInitialize ()
+{
+ XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
+ NULL, 0 );
+}
+
+/****************************************************************
+ *
+ * Private Procedures
+ *
+ ****************************************************************/
+
+/* ARGSUSED */
+static void Initialize(request, new)
+ Widget request, new;
+{
+ DviWidget dw = (DviWidget) new;
+
+ dw->dvi.current_page = 0;
+ dw->dvi.font_map = 0;
+ dw->dvi.cache.index = 0;
+ dw->dvi.text_x_width = 0;
+ dw->dvi.text_device_width = 0;
+ dw->dvi.word_flag = 0;
+ dw->dvi.file = 0;
+ dw->dvi.tmpFile = 0;
+ dw->dvi.state = 0;
+ dw->dvi.readingTmp = 0;
+ dw->dvi.cache.char_index = 0;
+ dw->dvi.cache.font_size = -1;
+ dw->dvi.cache.font_number = -1;
+ dw->dvi.cache.adjustable[0] = 0;
+ dw->dvi.file_map = 0;
+ dw->dvi.fonts = 0;
+ dw->dvi.seek = False;
+ dw->dvi.device_resolution = dw->dvi.default_resolution;
+ dw->dvi.display_resolution = dw->dvi.default_resolution;
+ dw->dvi.paperlength = dw->dvi.default_resolution*11;
+ dw->dvi.paperwidth = (dw->dvi.default_resolution*8
+ + dw->dvi.default_resolution/2);
+ dw->dvi.scale_factor = 1.0;
+ dw->dvi.sizescale = 1;
+ dw->dvi.line_thickness = -1;
+ dw->dvi.line_width = 1;
+ dw->dvi.fill = DVI_FILL_MAX;
+ dw->dvi.device_font = 0;
+ dw->dvi.device_font_number = -1;
+ dw->dvi.device = 0;
+ dw->dvi.native = 0;
+}
+
+#include "gray1.bm"
+#include "gray2.bm"
+#include "gray3.bm"
+#include "gray4.bm"
+#include "gray5.bm"
+#include "gray6.bm"
+#include "gray7.bm"
+#include "gray8.bm"
+
+static void
+Realize (w, valueMask, attrs)
+ Widget w;
+ XtValueMask *valueMask;
+ XSetWindowAttributes *attrs;
+{
+ DviWidget dw = (DviWidget) w;
+ XGCValues values;
+
+ if (dw->dvi.backing_store != Always + WhenMapped + NotUseful) {
+ attrs->backing_store = dw->dvi.backing_store;
+ *valueMask |= CWBackingStore;
+ }
+ XtCreateWindow (w, (unsigned)InputOutput, (Visual *) CopyFromParent,
+ *valueMask, attrs);
+ values.foreground = dw->dvi.foreground;
+ values.cap_style = CapRound;
+ values.join_style = JoinRound;
+ values.line_width = dw->dvi.line_width;
+ dw->dvi.normal_GC = XCreateGC (XtDisplay (w), XtWindow (w),
+ GCForeground|GCCapStyle|GCJoinStyle
+ |GCLineWidth,
+ &values);
+ dw->dvi.gray[0] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray1_bits,
+ gray1_width, gray1_height);
+ dw->dvi.gray[1] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray2_bits,
+ gray2_width, gray2_height);
+ dw->dvi.gray[2] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray3_bits,
+ gray3_width, gray3_height);
+ dw->dvi.gray[3] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray4_bits,
+ gray4_width, gray4_height);
+ dw->dvi.gray[4] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray5_bits,
+ gray5_width, gray5_height);
+ dw->dvi.gray[5] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray6_bits,
+ gray6_width, gray6_height);
+ dw->dvi.gray[6] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray7_bits,
+ gray7_width, gray7_height);
+ dw->dvi.gray[7] = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray8_bits,
+ gray8_width, gray8_height);
+ values.background = dw->dvi.background;
+ values.stipple = dw->dvi.gray[5];
+ dw->dvi.fill_GC = XCreateGC (XtDisplay (w), XtWindow (w),
+ GCForeground|GCBackground|GCStipple,
+ &values);
+
+ dw->dvi.fill_type = 9;
+
+ if (dw->dvi.file)
+ OpenFile (dw);
+ ParseFontMap (dw);
+}
+
+static void
+Destroy(w)
+ Widget w;
+{
+ DviWidget dw = (DviWidget) w;
+
+ XFreeGC (XtDisplay (w), dw->dvi.normal_GC);
+ XFreeGC (XtDisplay (w), dw->dvi.fill_GC);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[0]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[1]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[2]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[3]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[4]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[5]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[6]);
+ XFreePixmap (XtDisplay (w), dw->dvi.gray[7]);
+ DestroyFontMap (dw->dvi.font_map);
+ DestroyFileMap (dw->dvi.file_map);
+ device_destroy (dw->dvi.device);
+}
+
+/*
+ * Repaint the widget window
+ */
+
+/* ARGSUSED */
+static void
+Redisplay(w, event, region)
+ Widget w;
+ XEvent *event;
+ Region region;
+{
+ DviWidget dw = (DviWidget) w;
+ XRectangle extents;
+
+ XClipBox (region, &extents);
+ dw->dvi.extents.x1 = extents.x;
+ dw->dvi.extents.y1 = extents.y;
+ dw->dvi.extents.x2 = extents.x + extents.width;
+ dw->dvi.extents.y2 = extents.y + extents.height;
+ ShowDvi (dw);
+}
+
+/*
+ * Set specified arguments into widget
+ */
+/* ARGSUSED */
+static Boolean
+SetValues (current, request, new)
+ DviWidget current, request, new;
+{
+ Boolean redisplay = FALSE;
+ char *new_map;
+ int cur, req;
+
+ if (current->dvi.font_map_string != request->dvi.font_map_string) {
+ new_map = XtMalloc (strlen (request->dvi.font_map_string) + 1);
+ if (new_map) {
+ redisplay = TRUE;
+ strcpy (new_map, request->dvi.font_map_string);
+ new->dvi.font_map_string = new_map;
+ if (current->dvi.font_map_string)
+ XtFree (current->dvi.font_map_string);
+ current->dvi.font_map_string = 0;
+ ParseFontMap (new);
+ }
+ }
+
+ req = request->dvi.requested_page;
+ cur = current->dvi.requested_page;
+ if (cur != req) {
+ if (!request->dvi.file)
+ req = 0;
+ else {
+ if (req < 1)
+ req = 1;
+ if (current->dvi.last_page != 0 &&
+ req > current->dvi.last_page)
+ req = current->dvi.last_page;
+ }
+ if (cur != req)
+ redisplay = TRUE;
+ new->dvi.requested_page = req;
+ if (current->dvi.last_page == 0 && req > cur)
+ FindPage (new);
+ }
+
+ return redisplay;
+}
+
+/*
+ * use the set_values_hook entry to check when
+ * the file is set
+ */
+
+static Boolean
+SetValuesHook (dw, args, num_argsp)
+ DviWidget dw;
+ ArgList args;
+ Cardinal *num_argsp;
+{
+ Cardinal i;
+
+ for (i = 0; i < *num_argsp; i++) {
+ if (!strcmp (args[i].name, XtNfile)) {
+ CloseFile (dw);
+ OpenFile (dw);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void CloseFile (dw)
+ DviWidget dw;
+{
+ if (dw->dvi.tmpFile)
+ fclose (dw->dvi.tmpFile);
+ ForgetPagePositions (dw);
+}
+
+static void OpenFile (dw)
+ DviWidget dw;
+{
+ dw->dvi.tmpFile = 0;
+ if (!dw->dvi.seek)
+ dw->dvi.tmpFile = tmpfile();
+ dw->dvi.requested_page = 1;
+ dw->dvi.last_page = 0;
+}
+
+static XtGeometryResult
+QueryGeometry (w, request, geometry_return)
+ Widget w;
+ XtWidgetGeometry *request, *geometry_return;
+{
+ XtGeometryResult ret;
+ DviWidget dw = (DviWidget) w;
+
+ ret = XtGeometryYes;
+ if (((request->request_mode & CWWidth)
+ && request->width < MY_WIDTH(dw))
+ || ((request->request_mode & CWHeight)
+ && request->height < MY_HEIGHT(dw)))
+ ret = XtGeometryAlmost;
+ geometry_return->width = MY_WIDTH(dw);
+ geometry_return->height = MY_HEIGHT(dw);
+ geometry_return->request_mode = CWWidth|CWHeight;
+ return ret;
+}
+
+SetDevice (dw, name)
+ DviWidget dw;
+ char *name;
+{
+ XtWidgetGeometry request, reply;
+ XtGeometryResult ret;
+
+ ForgetFonts (dw);
+ dw->dvi.device = device_load (name);
+ if (!dw->dvi.device)
+ return;
+ dw->dvi.sizescale = dw->dvi.device->sizescale;
+ dw->dvi.device_resolution = dw->dvi.device->res;
+ dw->dvi.native = dw->dvi.device->X11;
+ dw->dvi.paperlength = dw->dvi.device->paperlength;
+ dw->dvi.paperwidth = dw->dvi.device->paperwidth;
+ if (dw->dvi.native) {
+ dw->dvi.display_resolution = dw->dvi.device_resolution;
+ dw->dvi.scale_factor = 1.0;
+ }
+ else {
+ dw->dvi.display_resolution = dw->dvi.default_resolution;
+ dw->dvi.scale_factor = ((double)dw->dvi.display_resolution
+ / dw->dvi.device_resolution);
+ }
+ request.request_mode = CWWidth|CWHeight;
+ request.width = MY_WIDTH(dw);
+ request.height = MY_HEIGHT(dw);
+ ret = XtMakeGeometryRequest ((Widget)dw, &request, &reply);
+ if (ret == XtGeometryAlmost
+ && reply.height >= request.height
+ && reply.width >= request.width) {
+ request.width = reply.width;
+ request.height = reply.height;
+ XtMakeGeometryRequest ((Widget)dw, &request, &reply);
+ }
+}
+
+static void
+ShowDvi (dw)
+ DviWidget dw;
+{
+ if (!dw->dvi.file) {
+ static char Error[] = "No file selected";
+
+ XSetFont (XtDisplay(dw), dw->dvi.normal_GC,
+ dw->dvi.default_font->fid);
+ XDrawString (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ 20, 20, Error, strlen (Error));
+ return;
+ }
+
+ FindPage (dw);
+
+ dw->dvi.display_enable = 1;
+ ParseInput (dw);
+ if (dw->dvi.last_page && dw->dvi.requested_page > dw->dvi.last_page)
+ dw->dvi.requested_page = dw->dvi.last_page;
+}
+
+static void
+FindPage (dw)
+ DviWidget dw;
+{
+ int i;
+ long file_position;
+
+ if (dw->dvi.requested_page < 1)
+ dw->dvi.requested_page = 1;
+
+ if (dw->dvi.last_page != 0 && dw->dvi.requested_page > dw->dvi.last_page)
+ dw->dvi.requested_page = dw->dvi.last_page;
+
+ file_position = SearchPagePosition (dw, dw->dvi.requested_page);
+ if (file_position != -1) {
+ FileSeek(dw, file_position);
+ dw->dvi.current_page = dw->dvi.requested_page;
+ } else {
+ for (i=dw->dvi.requested_page; i > 0; i--) {
+ file_position = SearchPagePosition (dw, i);
+ if (file_position != -1)
+ break;
+ }
+ if (file_position == -1)
+ file_position = 0;
+ FileSeek (dw, file_position);
+
+ dw->dvi.current_page = i;
+
+ dw->dvi.display_enable = 0;
+ while (dw->dvi.current_page != dw->dvi.requested_page) {
+ dw->dvi.current_page = ParseInput (dw);
+ /*
+ * at EOF, seek back to the beginning of this page.
+ */
+ if (!dw->dvi.readingTmp && feof (dw->dvi.file)) {
+ file_position = SearchPagePosition (dw,
+ dw->dvi.current_page);
+ if (file_position != -1)
+ FileSeek (dw, file_position);
+ dw->dvi.requested_page = dw->dvi.current_page;
+ break;
+ }
+ }
+ }
+}
+
+void DviSaveToFile(w, fp)
+ Widget w;
+ FILE *fp;
+{
+ XtCheckSubclass(w, dviWidgetClass, NULL);
+ (*((DviWidgetClass) XtClass(w))->command_class.save)(w, fp);
+}
+
+static
+void SaveToFile(w, fp)
+ Widget w;
+ FILE *fp;
+{
+ DviWidget dw = (DviWidget)w;
+ long pos;
+ int c;
+
+ if (dw->dvi.tmpFile) {
+ pos = ftell(dw->dvi.tmpFile);
+ if (dw->dvi.ungot) {
+ pos--;
+ dw->dvi.ungot = 0;
+ /* The ungot character is in the tmpFile, so we don't
+ want to read it from file. */
+ (void)getc(dw->dvi.file);
+ }
+ }
+ else
+ pos = ftell(dw->dvi.file);
+ FileSeek(dw, 0L);
+ while (DviGetC(dw, &c) != EOF)
+ if (putc(c, fp) == EOF) {
+ /* XXX print error message */
+ break;
+ }
+ FileSeek(dw, pos);
+}
+
+static
+void ClassPartInitialize(widget_class)
+ WidgetClass widget_class;
+{
+ DviWidgetClass wc = (DviWidgetClass)widget_class;
+ DviWidgetClass super = (DviWidgetClass) wc->core_class.superclass;
+ if (wc->command_class.save == InheritSaveToFile)
+ wc->command_class.save = super->command_class.save;
+}
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/Dvi.h b/contrib/groff/src/xditview/Dvi.h
new file mode 100644
index 000000000000..5aab7d8c7558
--- /dev/null
+++ b/contrib/groff/src/xditview/Dvi.h
@@ -0,0 +1,46 @@
+/*
+* $XConsortium: Dvi.h,v 1.4 89/07/21 14:22:06 jim Exp $
+*/
+
+#ifndef _XtDvi_h
+#define _XtDvi_h
+
+/***********************************************************************
+ *
+ * Dvi Widget
+ *
+ ***********************************************************************/
+
+/* Parameters:
+
+ Name Class RepType Default Value
+ ---- ----- ------- -------------
+ background Background pixel White
+ foreground Foreground Pixel Black
+ fontMap FontMap char * ...
+ pageNumber PageNumber int 1
+*/
+
+#define XtNfontMap "fontMap"
+#define XtNpageNumber "pageNumber"
+#define XtNlastPageNumber "lastPageNumber"
+#define XtNnoPolyText "noPolyText"
+#define XtNseek "seek"
+#define XtNresolution "resolution"
+
+#define XtCFontMap "FontMap"
+#define XtCPageNumber "PageNumber"
+#define XtCLastPageNumber "LastPageNumber"
+#define XtCNoPolyText "NoPolyText"
+#define XtCSeek "Seek"
+#define XtCResolution "Resolution"
+
+typedef struct _DviRec *DviWidget; /* completely defined in DviPrivate.h */
+typedef struct _DviClassRec *DviWidgetClass; /* completely defined in DviPrivate.h */
+
+extern WidgetClass dviWidgetClass;
+
+extern void DviSaveToFile();
+
+#endif /* _XtDvi_h */
+/* DON'T ADD STUFF AFTER THIS #endif */
diff --git a/contrib/groff/src/xditview/DviChar.c b/contrib/groff/src/xditview/DviChar.c
new file mode 100644
index 000000000000..2aaba459a28d
--- /dev/null
+++ b/contrib/groff/src/xditview/DviChar.c
@@ -0,0 +1,664 @@
+/*
+ * DviChar.c
+ *
+ * Map DVI (ditroff output) character names to
+ * font indexes and back
+ */
+
+#include "DviChar.h"
+
+extern char *xmalloc();
+
+#define allocHash() ((DviCharNameHash *) xmalloc (sizeof (DviCharNameHash)))
+
+struct map_list {
+ struct map_list *next;
+ DviCharNameMap *map;
+};
+
+static struct map_list *world;
+
+static int standard_maps_loaded = 0;
+static void load_standard_maps ();
+static int hash_name ();
+static dispose_hash(), compute_hash();
+
+DviCharNameMap *
+DviFindMap (encoding)
+ char *encoding;
+{
+ struct map_list *m;
+
+ if (!standard_maps_loaded)
+ load_standard_maps ();
+ for (m = world; m; m=m->next)
+ if (!strcmp (m->map->encoding, encoding))
+ return m->map;
+ return 0;
+}
+
+void
+DviRegisterMap (map)
+ DviCharNameMap *map;
+{
+ struct map_list *m;
+
+ if (!standard_maps_loaded)
+ load_standard_maps ();
+ for (m = world; m; m = m->next)
+ if (!strcmp (m->map->encoding, map->encoding))
+ break;
+ if (!m) {
+ m = (struct map_list *) xmalloc (sizeof *m);
+ m->next = world;
+ world = m;
+ }
+ dispose_hash (map);
+ m->map = map;
+ compute_hash (map);
+}
+
+static
+dispose_hash (map)
+ DviCharNameMap *map;
+{
+ DviCharNameHash **buckets;
+ DviCharNameHash *h, *next;
+ int i;
+
+ buckets = map->buckets;
+ for (i = 0; i < DVI_HASH_SIZE; i++) {
+ for (h = buckets[i]; h; h=next) {
+ next = h->next;
+ free (h);
+ }
+ }
+}
+
+static int
+hash_name (name)
+ char *name;
+{
+ int i = 0;
+
+ while (*name)
+ i = (i << 1) ^ *name++;
+ if (i < 0)
+ i = -i;
+ return i;
+}
+
+static
+compute_hash (map)
+ DviCharNameMap *map;
+{
+ DviCharNameHash **buckets;
+ int c, s, i;
+ DviCharNameHash *h;
+
+ buckets = map->buckets;
+ for (i = 0; i < DVI_HASH_SIZE; i++)
+ buckets[i] = 0;
+ for (c = 0; c < DVI_MAP_SIZE; c++)
+ for (s = 0; s < DVI_MAX_SYNONYMS; s++) {
+ if (!map->dvi_names[c][s])
+ break;
+ i = hash_name (map->dvi_names[c][s]) % DVI_HASH_SIZE;
+ h = allocHash ();
+ h->next = buckets[i];
+ buckets[i] = h;
+ h->name = map->dvi_names[c][s];
+ h->position = c;
+ }
+
+}
+
+int
+DviCharIndex (map, name)
+ DviCharNameMap *map;
+ char *name;
+{
+ int i;
+ DviCharNameHash *h;
+
+ i = hash_name (name) % DVI_HASH_SIZE;
+ for (h = map->buckets[i]; h; h=h->next)
+ if (!strcmp (h->name, name))
+ return h->position;
+ return -1;
+}
+
+static DviCharNameMap ISO8859_1_map = {
+ "iso8859-1",
+ 0,
+{
+{ 0, /* 0 */},
+{ 0, /* 1 */},
+{ 0, /* 2 */},
+{ 0, /* 3 */},
+{ 0, /* 4 */},
+{ 0, /* 5 */},
+{ 0, /* 6 */},
+{ 0, /* 7 */},
+{ 0, /* 8 */},
+{ 0, /* 9 */},
+{ 0, /* 10 */},
+{ 0, /* 11 */},
+{ 0, /* 12 */},
+{ 0, /* 13 */},
+{ 0, /* 14 */},
+{ 0, /* 15 */},
+{ 0, /* 16 */},
+{ 0, /* 17 */},
+{ 0, /* 18 */},
+{ 0, /* 19 */},
+{ 0, /* 20 */},
+{ 0, /* 21 */},
+{ 0, /* 22 */},
+{ 0, /* 23 */},
+{ 0, /* 24 */},
+{ 0, /* 25 */},
+{ 0, /* 26 */},
+{ 0, /* 27 */},
+{ 0, /* 28 */},
+{ 0, /* 29 */},
+{ 0, /* 30 */},
+{ 0, /* 31 */},
+{ 0, /* 32 */},
+{ "!", /* 33 */},
+{ "\"","dq", /* 34 */},
+{ "#","sh", /* 35 */},
+{ "$","Do", /* 36 */},
+{ "%", /* 37 */},
+{ "&", /* 38 */},
+{ "'","cq", /* 39 */},
+{ "(", /* 40 */},
+{ ")", /* 41 */},
+{ "*", /* 42 */},
+{ "+", /* 43 */},
+{ ",", /* 44 */},
+{ "\\-", /* 45 */},
+{ ".", /* 46 */},
+{ "/","sl", /* 47 */},
+{ "0", /* 48 */},
+{ "1", /* 49 */},
+{ "2", /* 50 */},
+{ "3", /* 51 */},
+{ "4", /* 52 */},
+{ "5", /* 53 */},
+{ "6", /* 54 */},
+{ "7", /* 55 */},
+{ "8", /* 56 */},
+{ "9", /* 57 */},
+{ ":", /* 58 */},
+{ ";", /* 59 */},
+{ "<", /* 60 */},
+{ "=","eq", /* 61 */},
+{ ">", /* 62 */},
+{ "?", /* 63 */},
+{ "@","at", /* 64 */},
+{ "A", /* 65 */},
+{ "B", /* 66 */},
+{ "C", /* 67 */},
+{ "D", /* 68 */},
+{ "E", /* 69 */},
+{ "F", /* 70 */},
+{ "G", /* 71 */},
+{ "H", /* 72 */},
+{ "I", /* 73 */},
+{ "J", /* 74 */},
+{ "K", /* 75 */},
+{ "L", /* 76 */},
+{ "M", /* 77 */},
+{ "N", /* 78 */},
+{ "O", /* 79 */},
+{ "P", /* 80 */},
+{ "Q", /* 81 */},
+{ "R", /* 82 */},
+{ "S", /* 83 */},
+{ "T", /* 84 */},
+{ "U", /* 85 */},
+{ "V", /* 86 */},
+{ "W", /* 87 */},
+{ "X", /* 88 */},
+{ "Y", /* 89 */},
+{ "Z", /* 90 */},
+{ "[","lB", /* 91 */},
+{ "\\","rs", /* 92 */},
+{ "]","rB", /* 93 */},
+{ "^","a^","ha" /* 94 */},
+{ "_", /* 95 */},
+{ "`","oq", /* 96 */},
+{ "a", /* 97 */},
+{ "b", /* 98 */},
+{ "c", /* 99 */},
+{ "d", /* 100 */},
+{ "e", /* 101 */},
+{ "f", /* 102 */},
+{ "g", /* 103 */},
+{ "h", /* 104 */},
+{ "i", /* 105 */},
+{ "j", /* 106 */},
+{ "k", /* 107 */},
+{ "l", /* 108 */},
+{ "m", /* 109 */},
+{ "n", /* 110 */},
+{ "o", /* 111 */},
+{ "p", /* 112 */},
+{ "q", /* 113 */},
+{ "r", /* 114 */},
+{ "s", /* 115 */},
+{ "t", /* 116 */},
+{ "u", /* 117 */},
+{ "v", /* 118 */},
+{ "w", /* 119 */},
+{ "x", /* 120 */},
+{ "y", /* 121 */},
+{ "z", /* 122 */},
+{ "{","lC", /* 123 */},
+{ "|","or","ba" /* 124 */},
+{ "}","rC", /* 125 */},
+{ "~","a~","ap","ti" /* 126 */},
+{ 0, /* 127 */},
+{ 0, /* 128 */},
+{ 0, /* 129 */},
+{ 0, /* 130 */},
+{ 0, /* 131 */},
+{ 0, /* 132 */},
+{ 0, /* 133 */},
+{ 0, /* 134 */},
+{ 0, /* 135 */},
+{ 0, /* 136 */},
+{ 0, /* 137 */},
+{ 0, /* 138 */},
+{ 0, /* 139 */},
+{ 0, /* 140 */},
+{ 0, /* 141 */},
+{ 0, /* 142 */},
+{ 0, /* 143 */},
+{ 0, /* 144 */},
+{ 0, /* 145 */},
+{ 0, /* 146 */},
+{ 0, /* 147 */},
+{ 0, /* 148 */},
+{ 0, /* 149 */},
+{ 0, /* 150 */},
+{ 0, /* 151 */},
+{ 0, /* 152 */},
+{ 0, /* 153 */},
+{ 0, /* 154 */},
+{ 0, /* 155 */},
+{ 0, /* 156 */},
+{ 0, /* 157 */},
+{ 0, /* 158 */},
+{ 0, /* 159 */},
+{ 0, /* 160 */},
+{ "r!", "\241", /* 161 */},
+{ "ct", "\242", /* 162 */},
+{ "Po", "\243", /* 163 */},
+{ "Cs", "\244", /* 164 */},
+{ "Ye", "\245", /* 165 */},
+{ "bb", "\246", /* 166 */},
+{ "sc", "\247", /* 167 */},
+{ "ad", "\250", /* 168 */},
+{ "co", "\251", /* 169 */},
+{ "Of", "\252", /* 170 */},
+{ "Fo", "\253", /* 171 */},
+{ "no", "\254", /* 172 */},
+{ "-", "hy", "\255" /* 173 */},
+{ "rg", "\256", /* 174 */},
+{ "a-", "\257", /* 175 */},
+{ "de", "\260", /* 176 */},
+{ "+-", "\261", /* 177 */},
+{ "S2", "\262", /* 178 */},
+{ "S3", "\263", /* 179 */},
+{ "aa", "\264", /* 180 */},
+/* Omit *m here; we want *m to match the other greek letters in the
+ symbol font. */
+{ "\265", /* 181 */},
+{ "ps", "\266", /* 182 */},
+{ "pc", "\267", /* 183 */},
+{ "ac", "\270", /* 184 */},
+{ "S1", "\271", /* 185 */},
+{ "Om", "\272", /* 186 */},
+{ "Fc", "\273", /* 187 */},
+{ "14", "\274", /* 188 */},
+{ "12", "\275", /* 189 */},
+{ "34", "\276", /* 190 */},
+{ "r?", "\277", /* 191 */},
+{ "`A", "\300", /* 192 */},
+{ "'A", "\301", /* 193 */},
+{ "^A", "\302", /* 194 */},
+{ "~A", "\303", /* 195 */},
+{ ":A", "\304", /* 196 */},
+{ "oA", "\305", /* 197 */},
+{ "AE", "\306", /* 198 */},
+{ ",C", "\307", /* 199 */},
+{ "`E", "\310", /* 200 */},
+{ "'E", "\311", /* 201 */},
+{ "^E", "\312", /* 202 */},
+{ ":E", "\313", /* 203 */},
+{ "`I", "\314", /* 204 */},
+{ "'I", "\315", /* 205 */},
+{ "^I", "\316", /* 206 */},
+{ ":I", "\317", /* 207 */},
+{ "-D", "\320", /* 208 */},
+{ "~N", "\321", /* 209 */},
+{ "`O", "\322", /* 210 */},
+{ "'O", "\323", /* 211 */},
+{ "^O", "\324", /* 212 */},
+{ "~O", "\325", /* 213 */},
+{ ":O", "\326", /* 214 */},
+{ "mu", "\327", /* 215 */},
+{ "/O", "\330", /* 216 */},
+{ "`U", "\331", /* 217 */},
+{ "'U", "\332", /* 218 */},
+{ "^U", "\333", /* 219 */},
+{ ":U", "\334", /* 220 */},
+{ "'Y", "\335", /* 221 */},
+{ "TP", "\336", /* 222 */},
+{ "ss", "\337", /* 223 */},
+{ "`a", "\340", /* 224 */},
+{ "'a", "\341", /* 225 */},
+{ "^a", "\342", /* 226 */},
+{ "~a", "\343", /* 227 */},
+{ ":a", "\344", /* 228 */},
+{ "oa", "\345", /* 229 */},
+{ "ae", "\346", /* 230 */},
+{ ",c", "\347", /* 231 */},
+{ "`e", "\350", /* 232 */},
+{ "'e", "\351", /* 233 */},
+{ "^e", "\352", /* 234 */},
+{ ":e", "\353", /* 235 */},
+{ "`i", "\354", /* 236 */},
+{ "'i", "\355", /* 237 */},
+{ "^i", "\356", /* 238 */},
+{ ":i", "\357", /* 239 */},
+{ "Sd", "\360", /* 240 */},
+{ "~n", "\361", /* 241 */},
+{ "`o", "\362", /* 242 */},
+{ "'o", "\363", /* 243 */},
+{ "^o", "\364", /* 244 */},
+{ "~o", "\365", /* 245 */},
+{ ":o", "\366", /* 246 */},
+{ "di", "\367", /* 247 */},
+{ "/o", "\370", /* 248 */},
+{ "`u", "\371", /* 249 */},
+{ "'u", "\372", /* 250 */},
+{ "^u", "\373", /* 251 */},
+{ ":u", "\374", /* 252 */},
+{ "'y", "\375", /* 253 */},
+{ "Tp", "\376", /* 254 */},
+{ ":y", "\377", /* 255 */},
+}};
+
+static DviCharNameMap Adobe_Symbol_map = {
+ "adobe-fontspecific",
+ 1,
+{
+{ 0, /* 0 */},
+{ 0, /* 1 */},
+{ 0, /* 2 */},
+{ 0, /* 3 */},
+{ 0, /* 4 */},
+{ 0, /* 5 */},
+{ 0, /* 6 */},
+{ 0, /* 7 */},
+{ 0, /* 8 */},
+{ 0, /* 9 */},
+{ 0, /* 10 */},
+{ 0, /* 11 */},
+{ 0, /* 12 */},
+{ 0, /* 13 */},
+{ 0, /* 14 */},
+{ 0, /* 15 */},
+{ 0, /* 16 */},
+{ 0, /* 17 */},
+{ 0, /* 18 */},
+{ 0, /* 19 */},
+{ 0, /* 20 */},
+{ 0, /* 21 */},
+{ 0, /* 22 */},
+{ 0, /* 23 */},
+{ 0, /* 24 */},
+{ 0, /* 25 */},
+{ 0, /* 26 */},
+{ 0, /* 27 */},
+{ 0, /* 28 */},
+{ 0, /* 29 */},
+{ 0, /* 30 */},
+{ 0, /* 31 */},
+{ 0, /* 32 */},
+{ "!", /* 33 */},
+{ "fa", /* 34 */},
+{ "#", "sh", /* 35 */},
+{ "te", /* 36 */},
+{ "%", /* 37 */},
+{ "&", /* 38 */},
+{ "st", /* 39 */},
+{ "(", /* 40 */},
+{ ")", /* 41 */},
+{ "**", /* 42 */},
+{ "+", "pl", /* 43 */},
+{ ",", /* 44 */},
+{ "\\-", "mi", /* 45 */},
+{ ".", /* 46 */},
+{ "/", "sl", /* 47 */},
+{ "0", /* 48 */},
+{ "1", /* 49 */},
+{ "2", /* 50 */},
+{ "3", /* 51 */},
+{ "4", /* 52 */},
+{ "5", /* 53 */},
+{ "6", /* 54 */},
+{ "7", /* 55 */},
+{ "8", /* 56 */},
+{ "9", /* 57 */},
+{ ":", /* 58 */},
+{ ";", /* 59 */},
+{ "<", /* 60 */},
+{ "=", "eq", /* 61 */},
+{ ">", /* 62 */},
+{ "?", /* 63 */},
+{ "=~", /* 64 */},
+{ "*A", /* 65 */},
+{ "*B", /* 66 */},
+{ "*X", /* 67 */},
+{ "*D", /* 68 */},
+{ "*E", /* 69 */},
+{ "*F", /* 70 */},
+{ "*G", /* 71 */},
+{ "*Y", /* 72 */},
+{ "*I", /* 73 */},
+{ "+h", /* 74 */},
+{ "*K", /* 75 */},
+{ "*L", /* 76 */},
+{ "*M", /* 77 */},
+{ "*N", /* 78 */},
+{ "*O", /* 79 */},
+{ "*P", /* 80 */},
+{ "*H", /* 81 */},
+{ "*R", /* 82 */},
+{ "*S", /* 83 */},
+{ "*T", /* 84 */},
+{ 0, /* 85 */},
+{ "ts", /* 86 */},
+{ "*W", /* 87 */},
+{ "*C", /* 88 */},
+{ "*Q", /* 89 */},
+{ "*Z", /* 90 */},
+{ "[", "lB", /* 91 */},
+{ "tf", "3d", /* 92 */},
+{ "]", "rB", /* 93 */},
+{ "pp", /* 94 */},
+{ "_", /* 95 */},
+{ "radicalex", /* 96 */},
+{ "*a", /* 97 */},
+{ "*b", /* 98 */},
+{ "*x", /* 99 */},
+{ "*d", /* 100 */},
+{ "*e", /* 101 */},
+{ "*f", /* 102 */},
+{ "*g", /* 103 */},
+{ "*y", /* 104 */},
+{ "*i", /* 105 */},
+{ "+f", /* 106 */},
+{ "*k", /* 107 */},
+{ "*l", /* 108 */},
+{ "*m", "\265", /* 109 */},
+{ "*n", /* 110 */},
+{ "*o", /* 111 */},
+{ "*p", /* 112 */},
+{ "*h", /* 113 */},
+{ "*r", /* 114 */},
+{ "*s", /* 115 */},
+{ "*t", /* 116 */},
+{ "*u", /* 117 */},
+{ "+p", /* 118 */},
+{ "*w", /* 119 */},
+{ "*c", /* 120 */},
+{ "*q", /* 121 */},
+{ "*z", /* 122 */},
+{ "lC", "{", /* 123 */},
+{ "ba", "or", "|", /* 124 */},
+{ "rC", "}", /* 125 */},
+{ "ap", /* 126 */},
+{ 0, /* 127 */},
+{ 0, /* 128 */},
+{ 0, /* 129 */},
+{ 0, /* 130 */},
+{ 0, /* 131 */},
+{ 0, /* 132 */},
+{ 0, /* 133 */},
+{ 0, /* 134 */},
+{ 0, /* 135 */},
+{ 0, /* 136 */},
+{ 0, /* 137 */},
+{ 0, /* 138 */},
+{ 0, /* 139 */},
+{ 0, /* 140 */},
+{ 0, /* 141 */},
+{ 0, /* 142 */},
+{ 0, /* 143 */},
+{ 0, /* 144 */},
+{ 0, /* 145 */},
+{ 0, /* 146 */},
+{ 0, /* 147 */},
+{ 0, /* 148 */},
+{ 0, /* 149 */},
+{ 0, /* 150 */},
+{ 0, /* 151 */},
+{ 0, /* 152 */},
+{ 0, /* 153 */},
+{ 0, /* 154 */},
+{ 0, /* 155 */},
+{ 0, /* 156 */},
+{ 0, /* 157 */},
+{ 0, /* 158 */},
+{ 0, /* 159 */},
+{ 0, /* 160 */},
+{ "*U", /* 161 */},
+{ "fm", /* 162 */},
+{ "<=", /* 163 */},
+{ "f/", /* 164 */},
+{ "if", /* 165 */},
+{ "Fn", /* 166 */},
+{ "CL", /* 167 */},
+{ "DI", /* 168 */},
+{ "HE", /* 169 */},
+{ "SP", /* 170 */},
+{ "<>", /* 171 */},
+{ "<-", /* 172 */},
+{ "ua", "arrowverttp" /* 173 */},
+{ "->", /* 174 */},
+{ "da", "arrowvertbt" /* 175 */},
+{ "de", "\260", /* 176 */},
+{ "+-", "\261", /* 177 */},
+{ "sd", /* 178 */},
+{ ">=", /* 179 */},
+{ "mu", "\327", /* 180 */},
+{ "pt", /* 181 */},
+{ "pd", /* 182 */},
+{ "bu", /* 183 */},
+{ "di", "\367", /* 184 */},
+{ "!=", /* 185 */},
+{ "==", /* 186 */},
+{ "~=", "~~", /* 187 */},
+{ 0, /* 188 */},
+{ "arrowvertex", /* 189 */},
+{ "an", /* 190 */},
+{ "CR", /* 191 */},
+{ "Ah", /* 192 */},
+{ "Im", /* 193 */},
+{ "Re", /* 194 */},
+{ "wp", /* 195 */},
+{ "c*", /* 196 */},
+{ "c+", /* 197 */},
+{ "es", /* 198 */},
+{ "ca", /* 199 */},
+{ "cu", /* 200 */},
+{ "sp", /* 201 */},
+{ "ip", /* 202 */},
+{ "nb", /* 203 */},
+{ "sb", /* 204 */},
+{ "ib", /* 205 */},
+{ "mo", /* 206 */},
+{ "nm", /* 207 */},
+{ "/_", /* 208 */},
+{ "gr", /* 209 */},
+{ "rg", /* 210 */},
+{ "co", /* 211 */},
+{ "tm", /* 212 */},
+{ 0, /* 213 */},
+{ "sr", /* 214 */},
+{ "md", /* 215 */},
+{ "no", "\254", /* 216 */},
+{ "AN", /* 217 */},
+{ "OR", /* 218 */},
+{ "hA", /* 219 */},
+{ "lA", /* 220 */},
+{ "uA", /* 221 */},
+{ "rA", /* 222 */},
+{ "dA", /* 223 */},
+{ "lz", /* 224 */},
+{ "la", /* 225 */},
+{ 0, /* 226 */},
+{ 0, /* 227 */},
+{ 0, /* 228 */},
+{ 0, /* 229 */},
+{ "parenlefttp", /* 230 */},
+{ "parenleftex", /* 231 */},
+{ "parenleftbt", /* 232 */},
+{ "bracketlefttp", "lc", /* 233 */},
+{ "bracketleftex", /* 234 */},
+{ "bracketleftbt", "lf", /* 235 */},
+{ "bracelefttp", "lt", /* 236 */},
+{ "braceleftmid", "lk", /* 237 */},
+{ "braceleftbt", "lb", /* 238 */},
+{ "bracerightex", "braceleftex", "bv", /* 239 */},
+{ 0, /* 240 */},
+{ "ra", /* 241 */},
+{ "is", /* 242 */},
+{ 0, /* 243 */},
+{ 0, /* 244 */},
+{ 0, /* 245 */},
+{ "parenrighttp", /* 246 */},
+{ "parenrightex", /* 247 */},
+{ "parenrightbt", /* 248 */},
+{ "bracketrighttp", "rc", /* 249 */},
+{ "bracketrightex", /* 250 */},
+{ "bracketrightbt", "rf", /* 251 */},
+{ "bracerighttp", "rt" /* 252 */},
+{ "bracerightmid", "rk" /* 253 */},
+{ "bracerightbt", "rb" /* 254 */},
+{ 0, /* 255 */},
+}};
+
+
+static void
+load_standard_maps ()
+{
+ standard_maps_loaded = 1;
+ DviRegisterMap (&ISO8859_1_map);
+ DviRegisterMap (&Adobe_Symbol_map);
+}
diff --git a/contrib/groff/src/xditview/DviChar.h b/contrib/groff/src/xditview/DviChar.h
new file mode 100644
index 000000000000..b075240d3954
--- /dev/null
+++ b/contrib/groff/src/xditview/DviChar.h
@@ -0,0 +1,37 @@
+/*
+ * DviChar.h
+ *
+ * descriptions for mapping dvi names to
+ * font indexes and back. Dvi fonts are all
+ * 256 elements (actually only 256-32 are usable).
+ *
+ * The encoding names are taken from X -
+ * case insensitive, a dash separating the
+ * CharSetRegistry from the CharSetEncoding
+ */
+
+# define DVI_MAX_SYNONYMS 10
+# define DVI_MAP_SIZE 256
+# define DVI_HASH_SIZE 256
+
+typedef struct _dviCharNameHash {
+ struct _dviCharNameHash *next;
+ char *name;
+ int position;
+} DviCharNameHash;
+
+typedef struct _dviCharNameMap {
+ char *encoding;
+ int special;
+ char *dvi_names[DVI_MAP_SIZE][DVI_MAX_SYNONYMS];
+ DviCharNameHash *buckets[DVI_HASH_SIZE];
+} DviCharNameMap;
+
+extern DviCharNameMap *DviFindMap ( /* char *encoding */ );
+extern void DviRegisterMap ( /* DviCharNameMap *map */ );
+#ifdef NOTDEF
+extern char *DviCharName ( /* DviCharNameMap *map, int index, int synonym */ );
+#else
+#define DviCharName(map,index,synonym) ((map)->dvi_names[index][synonym])
+#endif
+extern int DviCharIndex ( /* DviCharNameMap *map, char *name */ );
diff --git a/contrib/groff/src/xditview/DviP.h b/contrib/groff/src/xditview/DviP.h
new file mode 100644
index 000000000000..851fdfeed7f8
--- /dev/null
+++ b/contrib/groff/src/xditview/DviP.h
@@ -0,0 +1,233 @@
+/*
+ * $XConsortium: DviP.h,v 1.5 89/07/22 19:44:08 keith Exp $
+ */
+
+/*
+ * DviP.h - Private definitions for Dvi widget
+ */
+
+#ifndef _XtDviP_h
+#define _XtDviP_h
+
+#include "Dvi.h"
+#include "DviChar.h"
+#include "device.h"
+
+/***********************************************************************
+ *
+ * Dvi Widget Private Data
+ *
+ ***********************************************************************/
+
+/************************************
+ *
+ * Class structure
+ *
+ ***********************************/
+
+/* Type for save method. */
+
+typedef void (*DviSaveProc)();
+
+/*
+ * New fields for the Dvi widget class record
+ */
+
+
+typedef struct _DviClass {
+ DviSaveProc save;
+} DviClassPart;
+
+/*
+ * Full class record declaration
+ */
+
+typedef struct _DviClassRec {
+ CoreClassPart core_class;
+ DviClassPart command_class;
+} DviClassRec;
+
+extern DviClassRec dviClassRec;
+
+/***************************************
+ *
+ * Instance (widget) structure
+ *
+ **************************************/
+
+/*
+ * a list of fonts we've used for this widget
+ */
+
+typedef struct _dviFontSizeList {
+ struct _dviFontSizeList *next;
+ int size;
+ char *x_name;
+ XFontStruct *font;
+ int doesnt_exist;
+} DviFontSizeList;
+
+typedef struct _dviFontList {
+ struct _dviFontList *next;
+ char *dvi_name;
+ char *x_name;
+ int dvi_number;
+ Boolean initialized;
+ Boolean scalable;
+ DviFontSizeList *sizes;
+ DviCharNameMap *char_map;
+ DeviceFont *device_font;
+} DviFontList;
+
+typedef struct _dviFontMap {
+ struct _dviFontMap *next;
+ char *dvi_name;
+ char *x_name;
+} DviFontMap;
+
+#define DVI_TEXT_CACHE_SIZE 256
+#define DVI_CHAR_CACHE_SIZE 1024
+
+typedef struct _dviCharCache {
+ XTextItem cache[DVI_TEXT_CACHE_SIZE];
+ char adjustable[DVI_TEXT_CACHE_SIZE];
+ char char_cache[DVI_CHAR_CACHE_SIZE];
+ int index;
+ int max;
+ int char_index;
+ int font_size;
+ int font_number;
+ XFontStruct *font;
+ int start_x, start_y;
+ int x, y;
+} DviCharCache;
+
+typedef struct _dviState {
+ struct _dviState *next;
+ int font_size;
+ int font_number;
+ int x;
+ int y;
+} DviState;
+
+typedef struct _dviFileMap {
+ struct _dviFileMap *next;
+ long position;
+ int page_number;
+} DviFileMap;
+
+/*
+ * New fields for the Dvi widget record
+ */
+
+typedef struct {
+ /*
+ * resource specifiable items
+ */
+ char *font_map_string;
+ unsigned long foreground;
+ unsigned long background;
+ int requested_page;
+ int last_page;
+ XFontStruct *default_font;
+ FILE *file;
+ Boolean noPolyText;
+ Boolean seek; /* file is "seekable" */
+ int default_resolution;
+ /*
+ * private state
+ */
+ FILE *tmpFile; /* used when reading stdin */
+ char readingTmp; /* reading now from tmp */
+ char ungot; /* have ungetc'd a char */
+ GC normal_GC;
+ GC fill_GC;
+ DviFileMap *file_map;
+ DviFontList *fonts;
+ DviFontMap *font_map;
+ int current_page;
+ int font_size;
+ int font_number;
+ DeviceFont *device_font;
+ int device_font_number;
+ Device *device;
+ int native;
+ int device_resolution;
+ int display_resolution;
+ int paperlength;
+ int paperwidth;
+ double scale_factor; /* display res / device res */
+ int sizescale;
+ int line_thickness;
+ int line_width;
+
+#define DVI_FILL_MAX 1000
+
+ int fill;
+#define DVI_FILL_WHITE 0
+#define DVI_FILL_GRAY 1
+#define DVI_FILL_BLACK 2
+ int fill_type;
+ Pixmap gray[8];
+ int backing_store;
+ XFontStruct *font;
+ int display_enable;
+ struct ExposedExtents {
+ int x1, y1, x2, y2;
+ } extents;
+ DviState *state;
+ DviCharCache cache;
+ int text_x_width;
+ int text_device_width;
+ int word_flag;
+} DviPart;
+
+#define DviGetIn(dw,cp)\
+ (dw->dvi.tmpFile ? (\
+ DviGetAndPut (dw, cp) \
+ ) :\
+ (*cp = getc (dw->dvi.file))\
+)
+
+#define DviGetC(dw, cp)\
+ (dw->dvi.readingTmp ? (\
+ ((*cp = getc (dw->dvi.tmpFile)) == EOF) ? (\
+ fseek (dw->dvi.tmpFile, 0l, 2),\
+ (dw->dvi.readingTmp = 0),\
+ DviGetIn (dw,cp)\
+ ) : (\
+ *cp\
+ )\
+ ) : (\
+ DviGetIn(dw,cp)\
+ )\
+)
+
+#define DviUngetC(dw, c)\
+ (dw->dvi.readingTmp ? (\
+ ungetc (c, dw->dvi.tmpFile)\
+ ) : ( \
+ (dw->dvi.ungot = 1),\
+ ungetc (c, dw->dvi.file)))
+
+/*
+ * Full widget declaration
+ */
+
+typedef struct _DviRec {
+ CorePart core;
+ DviPart dvi;
+} DviRec;
+
+#define InheritSaveToFile ((DviSaveProc)_XtInherit)
+
+extern XFontStruct *QueryFont ();
+
+extern DviCharNameMap *QueryFontMap ();
+
+extern DeviceFont *QueryDeviceFont ();
+
+extern char *GetWord(), *GetLine();
+#endif /* _XtDviP_h */
+
+
diff --git a/contrib/groff/src/xditview/FontMap b/contrib/groff/src/xditview/FontMap
new file mode 100644
index 000000000000..90911f048ded
--- /dev/null
+++ b/contrib/groff/src/xditview/FontMap
@@ -0,0 +1,17 @@
+TR -adobe-times-medium-r-normal--*-*-*-*-p-*-iso8859-1
+TI -adobe-times-medium-i-normal--*-*-*-*-p-*-iso8859-1
+TB -adobe-times-bold-r-normal--*-*-*-*-p-*-iso8859-1
+TBI -adobe-times-bold-i-normal--*-*-*-*-p-*-iso8859-1
+CR -adobe-courier-medium-r-normal--*-*-*-*-m-*-iso8859-1
+CI -adobe-courier-medium-o-normal--*-*-*-*-m-*-iso8859-1
+CB -adobe-courier-bold-r-normal--*-*-*-*-m-*-iso8859-1
+CBI -adobe-courier-bold-o-normal--*-*-*-*-m-*-iso8859-1
+HR -adobe-helvetica-medium-r-normal--*-*-*-*-p-*-iso8859-1
+HI -adobe-helvetica-medium-o-normal--*-*-*-*-p-*-iso8859-1
+HB -adobe-helvetica-bold-r-normal--*-*-*-*-p-*-iso8859-1
+HBI -adobe-helvetica-bold-o-normal--*-*-*-*-p-*-iso8859-1
+NR -adobe-new century schoolbook-medium-r-normal--*-*-*-*-p-*-iso8859-1
+NI -adobe-new century schoolbook-medium-i-normal--*-*-*-*-p-*-iso8859-1
+NB -adobe-new century schoolbook-bold-r-normal--*-*-*-*-p-*-iso8859-1
+NBI -adobe-new century schoolbook-bold-i-normal--*-*-*-*-p-*-iso8859-1
+S -adobe-symbol-medium-r-normal--*-*-*-*-p-*-adobe-fontspecific
diff --git a/contrib/groff/src/xditview/GXditview-ad.h b/contrib/groff/src/xditview/GXditview-ad.h
new file mode 100644
index 000000000000..d9be3da256bc
--- /dev/null
+++ b/contrib/groff/src/xditview/GXditview-ad.h
@@ -0,0 +1,52 @@
+"GXditview.height: 840",
+"GXditview.paned.allowResize: true",
+"GXditview.paned.viewport.allowVert: true",
+"GXditview.paned.viewport.allowHoriz: true",
+"GXditview.paned.viewport.skipAdjust: false",
+"GXditview.paned.viewport.width: 600",
+"GXditview.paned.viewport.height: 800",
+"GXditview.paned.viewport.showGrip: false",
+"GXditview.paned.label.skipAdjust: true",
+"GXditview.paned.viewport.dvi.translations: #augment \
+ <Btn1Down>: XawPositionSimpleMenu(menu) MenuPopup(menu)\\n\
+ <Key>Next: NextPage()\\n\
+ <Key>n: NextPage()\\n\
+ <Key>space: NextPage()\\n\
+ <Key>Return: NextPage()\\n\
+ <Key>Prior: PreviousPage()\\n\
+ <Key>p: PreviousPage()\\n\
+ <Key>BackSpace: PreviousPage()\\n\
+ <Key>Delete: PreviousPage()\\n\
+ <Key>Select: SelectPage()\\n\
+ <Key>Find: OpenFile()\\n\
+ <Key>r: Rerasterize()\\n\
+ <Key>q: Quit()",
+"GXditview.paned.label.translations: #augment \
+ <Btn1Down>: XawPositionSimpleMenu(menu) MenuPopup(menu)\\n\
+ <Key>Next: NextPage()\\n\
+ <Key>n: NextPage()\\n\
+ <Key>space: NextPage()\\n\
+ <Key>Return: NextPage()\\n\
+ <Key>Prior: PreviousPage()\\n\
+ <Key>p: PreviousPage()\\n\
+ <Key>BackSpace: PreviousPage()\\n\
+ <Key>Delete: PreviousPage()\\n\
+ <Key>Select: SelectPage()\\n\
+ <Key>Find: OpenFile()\\n\
+ <Key>r: Rerasterize()\\n\
+ <Key>q: Quit()",
+"GXditview.menu.nextPage.label: Next Page",
+"GXditview.menu.previousPage.label: Previous Page",
+"GXditview.menu.selectPage.label: Select Page",
+"GXditview.menu.print.label: Print",
+"GXditview.menu.openFile.label: Open",
+"GXditview.menu.quit.label: Quit",
+"GXditview.promptShell.allowShellResize: true",
+"GXditview.promptShell.promptDialog.value.translations: #override \
+ <Key>Return: Accept()",
+"GXditview.promptShell.promptDialog.accept.label: Accept",
+"GXditview.promptShell.promptDialog.accept.translations: #override \
+ <BtnUp>: Accept() unset()",
+"GXditview.promptShell.promptDialog.cancel.label: Cancel",
+"GXditview.promptShell.promptDialog.cancel.translations: #override \
+ <BtnUp>: Cancel() unset()",
diff --git a/contrib/groff/src/xditview/GXditview.ad b/contrib/groff/src/xditview/GXditview.ad
new file mode 100644
index 000000000000..e99ff5e16f38
--- /dev/null
+++ b/contrib/groff/src/xditview/GXditview.ad
@@ -0,0 +1,57 @@
+GXditview.height: 840
+
+GXditview.paned.allowResize: true
+GXditview.paned.viewport.allowVert: true
+GXditview.paned.viewport.allowHoriz: true
+GXditview.paned.viewport.skipAdjust: false
+GXditview.paned.viewport.width: 600
+GXditview.paned.viewport.height: 800
+GXditview.paned.viewport.showGrip: false
+GXditview.paned.label.skipAdjust: true
+
+GXditview.paned.viewport.dvi.translations: #augment \
+ <Btn1Down>: XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
+ <Key>Next: NextPage()\n\
+ <Key>n: NextPage()\n\
+ <Key>space: NextPage()\n\
+ <Key>Return: NextPage()\n\
+ <Key>Prior: PreviousPage()\n\
+ <Key>p: PreviousPage()\n\
+ <Key>BackSpace: PreviousPage()\n\
+ <Key>Delete: PreviousPage()\n\
+ <Key>Select: SelectPage()\n\
+ <Key>Find: OpenFile()\n\
+ <Key>r: Rerasterize()\n\
+ <Key>q: Quit()
+GXditview.paned.label.translations: #augment \
+ <Btn1Down>: XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
+ <Key>Next: NextPage()\n\
+ <Key>n: NextPage()\n\
+ <Key>space: NextPage()\n\
+ <Key>Return: NextPage()\n\
+ <Key>Prior: PreviousPage()\n\
+ <Key>p: PreviousPage()\n\
+ <Key>BackSpace: PreviousPage()\n\
+ <Key>Delete: PreviousPage()\n\
+ <Key>Select: SelectPage()\n\
+ <Key>Find: OpenFile()\n\
+ <Key>r: Rerasterize()\n\
+ <Key>q: Quit()
+GXditview.menu.nextPage.label: Next Page
+GXditview.menu.previousPage.label: Previous Page
+GXditview.menu.selectPage.label: Select Page
+GXditview.menu.print.label: Print
+GXditview.menu.openFile.label: Open
+GXditview.menu.quit.label: Quit
+
+GXditview.promptShell.allowShellResize: true
+GXditview.promptShell.promptDialog.value.translations: #override \
+ <Key>Return: Accept()
+
+GXditview.promptShell.promptDialog.accept.label: Accept
+GXditview.promptShell.promptDialog.accept.translations: #override \
+ <BtnUp>: Accept() unset()
+
+GXditview.promptShell.promptDialog.cancel.label: Cancel
+GXditview.promptShell.promptDialog.cancel.translations: #override \
+ <BtnUp>: Cancel() unset()
diff --git a/contrib/groff/src/xditview/INSTALL b/contrib/groff/src/xditview/INSTALL
new file mode 100644
index 000000000000..144118fd7d42
--- /dev/null
+++ b/contrib/groff/src/xditview/INSTALL
@@ -0,0 +1,20 @@
+This version of gxditview uses imake.
+
+Here are the steps needed to install gxditview:
+
+- edit the Imakefile if necessary
+
+- xmkmf
+
+- make depend
+
+- make
+
+- make install
+
+- make install.man (installs the man page)
+
+The gxditview binary will be installed in the usual place for X
+binaries (eg /usr/bin/X11). Previous versions of gxditview were
+installed along with the other groff binaries (eg in /usr/local/bin);
+you will need to remove these by hand.
diff --git a/contrib/groff/src/xditview/Imakefile.in b/contrib/groff/src/xditview/Imakefile.in
new file mode 100644
index 000000000000..3ad244d6e68f
--- /dev/null
+++ b/contrib/groff/src/xditview/Imakefile.in
@@ -0,0 +1,104 @@
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+VPATH=@srcdir@
+top_builddir=@top_builddir@
+
+version=`cat $(top_srcdir)/VERSION`
+# No additional number if revision is zero
+revision=`sed -e 's/^0$$//' -e 's/^[1-9].*$$/.&/' $(top_srcdir)/REVISION`
+
+GROFF_PREFIX = @prefix@
+GROFF_DATADIR = $(GROFF_PREFIX)/share
+GROFF_DATAPROGRAMDIR = $(GROFF_DATADIR)/groff
+GROFF_DATASUBDIR = $(GROFF_DATAPROGRAMDIR)/$(version)$(revision)
+GROFF_FONTDIR = $(GROFF_DATASUBDIR)/font
+GROFF_FONTPATH = $(GROFF_FONTDIR):/usr/local/lib/font:/usr/lib/font
+DPIS = 75 100
+
+PROGRAMS = \
+ gxditview \
+ xtotroff
+DEPLIBS = XawClientDepLibs
+LOCAL_LIBRARIES = XawClientLibs
+SRCS1 = \
+ $(srcdir)/xditview.c \
+ $(srcdir)/Dvi.c \
+ $(srcdir)/draw.c \
+ $(srcdir)/font.c \
+ $(srcdir)/lex.c \
+ $(srcdir)/page.c \
+ $(srcdir)/parse.c \
+ $(srcdir)/XFontName.c \
+ $(srcdir)/DviChar.c \
+ $(srcdir)/device.c
+OBJS1 = \
+ xditview.o \
+ Dvi.o \
+ draw.o \
+ font.o \
+ lex.o \
+ page.o \
+ parse.o \
+ XFontName.o \
+ DviChar.o \
+ device.o
+SRCS2 = \
+ $(srcdir)/xtotroff.c \
+ $(srcdir)/XFontName.c \
+ $(srcdir)/DviChar.c
+OBJS2 = \
+ xtotroff.o \
+ XFontName.o \
+ DviChar.o
+INCLUDES = \
+ -I$(TOOLKITSRC) \
+ -I$(TOP)
+MATHLIB = -lm
+DEFINES = \
+ $(SIGNAL_DEFINES) \
+ -DFONTPATH=\"$(GROFF_FONTPATH)\" # -DX_NOT_STDC_ENV
+
+DEVDIR = $(top_builddir)/font
+MKINSTALLDIRS = $(top_srcdir)/mkinstalldirs
+
+ComplexProgramTarget_1(gxditview,$(LOCAL_LIBRARIES),$(MATHLIB))
+NormalProgramTarget(xtotroff,$(OBJS2),$(DEPXLIB),$(XLIB), /**/)
+
+InstallAppDefaults(GXditview)
+
+fonts: xtotroff $(srcdir)/DESC $(srcdir)/FontMap
+ @dir=`pwd`; \
+ fonts=`sed -e 's/[ ].*//' $(srcdir)/FontMap`; \
+ for dpi in $(DPIS); do \
+ echo Making devX$$dpi; \
+ test -d $(DEVDIR)/devX$$dpi || \
+ $(MKINSTALLDIRS) $(DEVDIR)/devX$$dpi; \
+ rm -f $(DEVDIR)/devX$$dpi/DESC; \
+ sed -e "s/res 75/res $$dpi/" $(srcdir)/DESC \
+ >$(DEVDIR)/devX$$dpi/DESC; \
+ (cd $(DEVDIR)/devX$$dpi; \
+ rm -f Makefile.sub; \
+ echo DEV=X$$dpi >Makefile.sub; \
+ echo DEVFILES=DESC $$fonts >>Makefile.sub; \
+ $$dir/xtotroff -g -r $$dpi -s 10 $(srcdir)/FontMap); \
+ echo Making devX$$dpi-12; \
+ test -d $(DEVDIR)/devX$$dpi-12 || \
+ $(MKINSTALLDIRS) $(DEVDIR)/devX$$dpi-12; \
+ rm -f $(DEVDIR)/devX$$dpi-12/DESC; \
+ sed -e "s/res 75/res $$dpi/" \
+ -e 's/unitwidth 10/unitwidth 12/' $(srcdir)/DESC \
+ >$(DEVDIR)/devX$$dpi-12/DESC; \
+ (cd $(DEVDIR)/devX$$dpi-12; \
+ rm -f Makefile.sub; \
+ echo DEV=X$$dpi-12 >Makefile.sub; \
+ echo DEVFILES=DESC $$fonts >>Makefile.sub; \
+ $$dir/xtotroff -g -r $$dpi -s 12 $(srcdir)/FontMap); \
+ done
+
+GXditview-ad.h: $(srcdir)/GXditview.ad
+ /bin/sh $(srcdir)/ad2c $(srcdir)/GXditview.ad >GXditview-ad.h
+
+extraclean: clean
+ -rm -f junk tmp grot old Makefile Imakefile
+
+FORCE:
diff --git a/contrib/groff/src/xditview/Menu.h b/contrib/groff/src/xditview/Menu.h
new file mode 100644
index 000000000000..c306b2740a61
--- /dev/null
+++ b/contrib/groff/src/xditview/Menu.h
@@ -0,0 +1,46 @@
+/*
+ * $XConsortium: Menu.h,v 1.2 89/07/21 14:22:10 jim Exp $
+ */
+
+#ifndef _XtMenu_h
+#define _XtMenu_h
+
+/***********************************************************************
+ *
+ * Menu Widget
+ *
+ ***********************************************************************/
+
+/* Parameters:
+
+ Name Class RepType Default Value
+ ---- ----- ------- -------------
+ background Background pixel White
+ border BorderColor pixel Black
+ borderWidth BorderWidth int 1
+ height Height int 120
+ mappedWhenManaged MappedWhenManaged Boolean True
+ reverseVideo ReverseVideo Boolean False
+ width Width int 120
+ x Position int 0
+ y Position int 0
+
+*/
+
+#define XtNmenuEntries "menuEntries"
+#define XtNhorizontalPadding "horizontalPadding"
+#define XtNverticalPadding "verticalPadding"
+#define XtNselection "Selection"
+
+#define XtCMenuEntries "MenuEntries"
+#define XtCPadding "Padding"
+#define XtCSelection "Selection"
+
+typedef struct _MenuRec *MenuWidget; /* completely defined in MenuPrivate.h */
+typedef struct _MenuClassRec *MenuWidgetClass; /* completely defined in MenuPrivate.h */
+
+extern WidgetClass menuWidgetClass;
+
+extern Widget XawMenuCreate ();
+#endif /* _XtMenu_h */
+/* DON'T ADD STUFF AFTER THIS #endif */
diff --git a/contrib/groff/src/xditview/README b/contrib/groff/src/xditview/README
new file mode 100644
index 000000000000..b18f64a7be64
--- /dev/null
+++ b/contrib/groff/src/xditview/README
@@ -0,0 +1,14 @@
+This is gxditview, a X11 previewer for groff based on MIT's xditview.
+This version can be used with the output of gtroff -Tps as well as
+with -TX75 and -TX100. You will need X11R5 or newer to install it (it
+might work on X11R4, but I haven't tested it.)
+
+See the file INSTALL in this directory for installation instructions.
+
+xditview is copyrighted by MIT under the usual X terms (see
+gxditview.man); my changes to it are in the public domain.
+
+Please report bugs to bug-groff@gnu.org.
+
+James Clark
+jjc@jclark.com
diff --git a/contrib/groff/src/xditview/TODO b/contrib/groff/src/xditview/TODO
new file mode 100644
index 000000000000..161a7cf115ce
--- /dev/null
+++ b/contrib/groff/src/xditview/TODO
@@ -0,0 +1,17 @@
+Replace Imakefile with a configure script.
+
+Better error handling.
+
+Resource and command-line option to specify font path.
+
+Resource to specify name of environment variable from which to get the
+font path.
+
+Have character substitutions (currently done in draw.c:FakeCharacter)
+specified in a resource (similar format to FontMap).
+
+The initial width of the dialog box should expand to accommodate the
+default value.
+
+Option in Print dialog to specify that only the current page should be
+printed.
diff --git a/contrib/groff/src/xditview/XFontName.c b/contrib/groff/src/xditview/XFontName.c
new file mode 100644
index 000000000000..5ca9bb81e996
--- /dev/null
+++ b/contrib/groff/src/xditview/XFontName.c
@@ -0,0 +1,256 @@
+/*
+ * XFontName.c
+ *
+ * build/parse X Font name strings
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include "XFontName.h"
+#include <ctype.h>
+
+static char *
+extractStringField (name, buffer, size, attrp, bit)
+ char *name;
+ char *buffer;
+ int size;
+ unsigned int *attrp;
+ unsigned int bit;
+{
+ char *buf = buffer;
+
+ if (!*name)
+ return 0;
+ while (*name && *name != '-' && size > 0) {
+ *buf++ = *name++;
+ --size;
+ }
+ if (size <= 0)
+ return 0;
+ *buf = '\0';
+ if (buffer[0] != '*' || buffer[1] != '\0')
+ *attrp |= bit;
+ if (*name == '-')
+ return name+1;
+ return name;
+}
+
+static char *
+extractUnsignedField (name, result, attrp, bit)
+ char *name;
+ unsigned int *result;
+ unsigned int *attrp;
+ unsigned int bit;
+{
+ char buf[256];
+ char *c;
+ unsigned int i;
+
+ name = extractStringField (name, buf, sizeof (buf), attrp, bit);
+ if (!name)
+ return 0;
+ if (!(*attrp & bit))
+ return name;
+ i = 0;
+ for (c = buf; *c; c++) {
+ if (!isdigit (*c))
+ return 0;
+ i = i * 10 + (*c - '0');
+ }
+ *result = i;
+ return name;
+}
+
+Bool
+XParseFontName (fontNameString, fontName, fontNameAttributes)
+ XFontNameString fontNameString;
+ XFontName *fontName;
+ unsigned int *fontNameAttributes;
+{
+ char *name = fontNameString;
+ XFontName temp;
+ unsigned int attributes = 0;
+
+#define GetString(field,bit)\
+ if (!(name = extractStringField \
+ (name, temp.field, sizeof (temp.field),\
+ &attributes, bit))) \
+ return False;
+
+#define GetUnsigned(field,bit)\
+ if (!(name = extractUnsignedField \
+ (name, &temp.field, \
+ &attributes, bit))) \
+ return False;
+
+ GetString (Registry, FontNameRegistry)
+ GetString (Foundry, FontNameFoundry)
+ GetString (FamilyName, FontNameFamilyName)
+ GetString (WeightName, FontNameWeightName)
+ GetString (Slant, FontNameSlant)
+ GetString (SetwidthName, FontNameSetwidthName)
+ GetString (AddStyleName, FontNameAddStyleName)
+ GetUnsigned (PixelSize, FontNamePixelSize)
+ GetUnsigned (PointSize, FontNamePointSize)
+ GetUnsigned (ResolutionX, FontNameResolutionX)
+ GetUnsigned (ResolutionY, FontNameResolutionY)
+ GetString (Spacing, FontNameSpacing)
+ GetUnsigned (AverageWidth, FontNameAverageWidth)
+ GetString (CharSetRegistry, FontNameCharSetRegistry)
+ if (!*name) {
+ temp.CharSetEncoding[0] = '\0';
+ attributes |= FontNameCharSetEncoding;
+ } else {
+ GetString (CharSetEncoding, FontNameCharSetEncoding)
+ }
+ *fontName = temp;
+ *fontNameAttributes = attributes;
+ return True;
+}
+
+static char *
+utoa (u, s, size)
+ unsigned int u;
+ char *s;
+ int size;
+{
+ char *t;
+
+ t = s + size;
+ *--t = '\0';
+ do
+ *--t = (u % 10) + '0';
+ while (u /= 10);
+ return t;
+}
+
+Bool
+XFormatFontName (fontName, fontNameAttributes, fontNameString)
+ XFontName *fontName;
+ unsigned int fontNameAttributes;
+ XFontNameString fontNameString;
+{
+ XFontNameString tmp;
+ char *name = tmp, *f;
+ int left = sizeof (tmp) - 1;
+ char number[32];
+
+#define PutString(field, bit)\
+ f = (fontNameAttributes & bit) ? \
+ fontName->field \
+ : "*"; \
+ if ((left -= strlen (f)) < 0) \
+ return False; \
+ while (*f) \
+ if ((*name++ = *f++) == '-') \
+ return False;
+#define PutHyphen()\
+ if (--left < 0) \
+ return False; \
+ *name++ = '-';
+
+#define PutUnsigned(field, bit) \
+ f = (fontNameAttributes & bit) ? \
+ utoa (fontName->field, number, sizeof (number)) \
+ : "*"; \
+ if ((left -= strlen (f)) < 0) \
+ return False; \
+ while (*f) \
+ *name++ = *f++;
+
+ PutString (Registry, FontNameRegistry)
+ PutHyphen ();
+ PutString (Foundry, FontNameFoundry)
+ PutHyphen ();
+ PutString (FamilyName, FontNameFamilyName)
+ PutHyphen ();
+ PutString (WeightName, FontNameWeightName)
+ PutHyphen ();
+ PutString (Slant, FontNameSlant)
+ PutHyphen ();
+ PutString (SetwidthName, FontNameSetwidthName)
+ PutHyphen ();
+ PutString (AddStyleName, FontNameAddStyleName)
+ PutHyphen ();
+ PutUnsigned (PixelSize, FontNamePixelSize)
+ PutHyphen ();
+ PutUnsigned (PointSize, FontNamePointSize)
+ PutHyphen ();
+ PutUnsigned (ResolutionX, FontNameResolutionX)
+ PutHyphen ();
+ PutUnsigned (ResolutionY, FontNameResolutionY)
+ PutHyphen ();
+ PutString (Spacing, FontNameSpacing)
+ PutHyphen ();
+ PutUnsigned (AverageWidth, FontNameAverageWidth)
+ PutHyphen ();
+ PutString (CharSetRegistry, FontNameCharSetRegistry)
+ PutHyphen ();
+ PutString (CharSetEncoding, FontNameCharSetEncoding)
+ *name = '\0';
+ strcpy (fontNameString, tmp);
+ return True;
+}
+
+Bool
+XCompareFontName (name1, name2, fontNameAttributes)
+ XFontName *name1, *name2;
+ unsigned int fontNameAttributes;
+{
+#define CompareString(field,bit) \
+ if (fontNameAttributes & bit) \
+ if (strcmp (name1->field, name2->field)) \
+ return False;
+
+#define CompareUnsigned(field,bit) \
+ if (fontNameAttributes & bit) \
+ if (name1->field != name2->field) \
+ return False;
+
+ CompareString (Registry, FontNameRegistry)
+ CompareString (Foundry, FontNameFoundry)
+ CompareString (FamilyName, FontNameFamilyName)
+ CompareString (WeightName, FontNameWeightName)
+ CompareString (Slant, FontNameSlant)
+ CompareString (SetwidthName, FontNameSetwidthName)
+ CompareString (AddStyleName, FontNameAddStyleName)
+ CompareUnsigned (PixelSize, FontNamePixelSize)
+ CompareUnsigned (PointSize, FontNamePointSize)
+ CompareUnsigned (ResolutionX, FontNameResolutionX)
+ CompareUnsigned (ResolutionY, FontNameResolutionY)
+ CompareString (Spacing, FontNameSpacing)
+ CompareUnsigned (AverageWidth, FontNameAverageWidth)
+ CompareString (CharSetRegistry, FontNameCharSetRegistry)
+ CompareString (CharSetEncoding, FontNameCharSetEncoding)
+ return True;
+}
+
+XCopyFontName (name1, name2, fontNameAttributes)
+ XFontName *name1, *name2;
+ unsigned int fontNameAttributes;
+{
+#define CopyString(field,bit) \
+ if (fontNameAttributes & bit) \
+ strcpy (name2->field, name1->field);
+
+#define CopyUnsigned(field,bit) \
+ if (fontNameAttributes & bit) \
+ name2->field = name1->field;
+
+ CopyString (Registry, FontNameRegistry)
+ CopyString (Foundry, FontNameFoundry)
+ CopyString (FamilyName, FontNameFamilyName)
+ CopyString (WeightName, FontNameWeightName)
+ CopyString (Slant, FontNameSlant)
+ CopyString (SetwidthName, FontNameSetwidthName)
+ CopyString (AddStyleName, FontNameAddStyleName)
+ CopyUnsigned (PixelSize, FontNamePixelSize)
+ CopyUnsigned (PointSize, FontNamePointSize)
+ CopyUnsigned (ResolutionX, FontNameResolutionX)
+ CopyUnsigned (ResolutionY, FontNameResolutionY)
+ CopyString (Spacing, FontNameSpacing)
+ CopyUnsigned (AverageWidth, FontNameAverageWidth)
+ CopyString (CharSetRegistry, FontNameCharSetRegistry)
+ CopyString (CharSetEncoding, FontNameCharSetEncoding)
+ return True;
+}
diff --git a/contrib/groff/src/xditview/XFontName.h b/contrib/groff/src/xditview/XFontName.h
new file mode 100644
index 000000000000..efe9eb16d1d4
--- /dev/null
+++ b/contrib/groff/src/xditview/XFontName.h
@@ -0,0 +1,45 @@
+typedef struct _xFontName {
+ char Registry[256];
+ char Foundry[256];
+ char FamilyName[256];
+ char WeightName[256];
+ char Slant[3];
+ char SetwidthName[256];
+ char AddStyleName[256];
+ unsigned int PixelSize;
+ unsigned int PointSize;
+ unsigned int ResolutionX;
+ unsigned int ResolutionY;
+ char Spacing[2];
+ unsigned int AverageWidth;
+ char CharSetRegistry[256];
+ char CharSetEncoding[256];
+} XFontName;
+
+#define FontNameRegistry (1<<0)
+#define FontNameFoundry (1<<1)
+#define FontNameFamilyName (1<<2)
+#define FontNameWeightName (1<<3)
+#define FontNameSlant (1<<4)
+#define FontNameSetwidthName (1<<5)
+#define FontNameAddStyleName (1<<6)
+#define FontNamePixelSize (1<<7)
+#define FontNamePointSize (1<<8)
+#define FontNameResolutionX (1<<9)
+#define FontNameResolutionY (1<<10)
+#define FontNameSpacing (1<<11)
+#define FontNameAverageWidth (1<<12)
+#define FontNameCharSetRegistry (1<<13)
+#define FontNameCharSetEncoding (1<<14)
+
+#define SlantRoman "R"
+#define SlantItalic "I"
+#define SlantOblique "O"
+#define SlantReverseItalic "RI"
+#define SlantReverseOblique "RO"
+
+#define SpacingMonoSpaced "M"
+#define SpacingProportional "P"
+#define SpacingCharacterCell "C"
+
+typedef char XFontNameString[256];
diff --git a/contrib/groff/src/xditview/ad2c b/contrib/groff/src/xditview/ad2c
new file mode 100644
index 000000000000..651ab8c40c5f
--- /dev/null
+++ b/contrib/groff/src/xditview/ad2c
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# ad2c : Convert app-defaults file to C strings decls.
+#
+# George Ferguson, ferguson@cs.rcohester.edu, 12 Nov 1990.
+# 19 Mar 1991: gf
+# Made it self-contained.
+# 6 Jan 1992: mycroft@gnu.ai.mit.edu (Charles Hannum)
+# Removed use of "-n" and ":read" label since Gnu and
+# IBM sed print pattern space on "n" command. Still works
+# with Sun sed, of course.
+# 7 Jan 1992: matthew@sunpix.East.Sun.COM (Matthew Stier)
+# Escape quotes after escaping backslashes.
+# 8 Jul 1992: Version 1.6
+# Manpage fixes.
+# 19 Apr 1993: Version 1.7
+# Remove comments that were inside the sed command since
+# some versions of sed don't like them. The comments are
+# now given here in the header.
+#
+# Comments on the script by line:
+# /^!/d Remove comments
+# /^$/d Remove blanks
+# s/\\/\\\\/g Escape backslashes...
+# s/\\$//g ...except the line continuation ones
+# s/"/\\"/g Escape quotes
+# s/^/"/ Add leading quote
+# : test Establish label for later branch
+# /\\$/b slash Branch to label "slash" if line ends in backslash
+# s/$/",/ Otherwise add closing quote and comma...
+# p ...output the line...
+# d ...and clear the pattern space so it's not printed again
+# : slash Branch comes here if line ends in backslash
+# n Read next line, append to pattern space
+# [...] The "d" and "s" commands that follow just delete
+# comments and blank lines and escape control sequences
+# b test Branch up to see if the line ends in backslash or not
+#
+
+sed '
+/^!/d
+/^$/d
+s/\\/\\\\/g
+s/\\$//g
+s/"/\\"/g
+s/^/"/
+: test
+/\\$/b slash
+s/$/",/
+p
+d
+: slash
+n
+/^!/d
+/^$/d
+s/"/\\"/g
+s/\\\\/\\/g
+s/\\n/\\\\n/g
+s/\\t/\\\\t/g
+s/\\f/\\\\f/g
+s/\\b/\\\\b/g
+b test' "$@"
diff --git a/contrib/groff/src/xditview/device.c b/contrib/groff/src/xditview/device.c
new file mode 100644
index 000000000000..264f681ea679
--- /dev/null
+++ b/contrib/groff/src/xditview/device.c
@@ -0,0 +1,600 @@
+/* device.c */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <X11/Xos.h>
+#include <X11/Intrinsic.h>
+
+#include "device.h"
+
+#ifndef FONTPATH
+#define FONTPATH "/usr/local/share/groff/font:/usr/local/lib/font:/usr/lib/font"
+#endif
+
+#ifndef isascii
+#define isascii(c) (1)
+#endif
+
+extern void exit();
+#ifndef strtok
+extern char *strtok();
+#endif
+#ifndef strchr
+extern char *strchr();
+#endif
+#ifndef getenv
+extern char *getenv();
+#endif
+
+/* Name of environment variable containing path to be used for
+searching for device and font description files. */
+#define FONTPATH_ENV_VAR "GROFF_FONT_PATH"
+
+#define WS " \t\r\n"
+
+#ifndef INT_MIN
+/* Minimum and maximum values a `signed int' can hold. */
+#define INT_MIN (-INT_MAX-1)
+#define INT_MAX 2147483647
+#endif
+
+#define CHAR_TABLE_SIZE 307
+
+struct _DeviceFont {
+ char *name;
+ int special;
+ DeviceFont *next;
+ Device *dev;
+ struct charinfo *char_table[CHAR_TABLE_SIZE];
+ struct charinfo *code_table[256];
+};
+
+struct charinfo {
+ int width;
+ int code;
+ struct charinfo *next;
+ struct charinfo *code_next;
+ char name[1];
+};
+
+static char *current_filename = 0;
+static int current_lineno = -1;
+
+static void error();
+static FILE *open_device_file();
+static DeviceFont *load_font();
+static Device *new_device();
+static DeviceFont *new_font();
+static void delete_font();
+static unsigned hash_name();
+static struct charinfo *add_char();
+static int read_charset_section();
+static char *canonicalize_name();
+
+static
+Device *new_device(name)
+ char *name;
+{
+ Device *dev;
+
+ dev = XtNew(Device);
+ dev->sizescale = 1;
+ dev->res = 0;
+ dev->unitwidth = 0;
+ dev->fonts = 0;
+ dev->X11 = 0;
+ dev->paperlength = 0;
+ dev->paperwidth = 0;
+ dev->name = XtNewString(name);
+ return dev;
+}
+
+void device_destroy(dev)
+ Device *dev;
+{
+ DeviceFont *f;
+
+ if (!dev)
+ return;
+ f = dev->fonts;
+ while (f) {
+ DeviceFont *tem = f;
+ f = f->next;
+ delete_font(tem);
+ }
+
+ XtFree(dev->name);
+ XtFree((char *)dev);
+}
+
+Device *device_load(name)
+ char *name;
+{
+ Device *dev;
+ FILE *fp;
+ int err = 0;
+ char buf[256];
+
+ fp = open_device_file(name, "DESC", &current_filename);
+ if (!fp)
+ return 0;
+ dev = new_device(name);
+ current_lineno = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p;
+ current_lineno++;
+ p = strtok(buf, WS);
+ if (p) {
+ int *np = 0;
+ char *q;
+
+ if (strcmp(p, "charset") == 0)
+ break;
+ if (strcmp(p, "X11") == 0)
+ dev->X11 = 1;
+ else if (strcmp(p, "sizescale") == 0)
+ np = &dev->sizescale;
+ else if (strcmp(p, "res") == 0)
+ np = &dev->res;
+ else if (strcmp(p, "unitwidth") == 0)
+ np = &dev->unitwidth;
+ else if (strcmp(p, "paperwidth") == 0)
+ np = &dev->paperwidth;
+ else if (strcmp(p, "paperlength") == 0)
+ np = &dev->paperlength;
+
+ if (np) {
+ q = strtok((char *)0, WS);
+ if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
+ error("bad argument");
+ err = 1;
+ break;
+ }
+ }
+ }
+ }
+ fclose(fp);
+ current_lineno = -1;
+ if (!err) {
+ if (dev->res == 0) {
+ error("missing res line");
+ err = 1;
+ }
+ else if (dev->unitwidth == 0) {
+ error("missing unitwidth line");
+ err = 1;
+ }
+ }
+ if (dev->paperlength == 0)
+ dev->paperlength = dev->res*11;
+ if (dev->paperwidth == 0)
+ dev->paperwidth = dev->res*8 + dev->res/2;
+ if (err) {
+ device_destroy(dev);
+ dev = 0;
+ }
+ XtFree(current_filename);
+ current_filename = 0;
+ return dev;
+}
+
+
+DeviceFont *device_find_font(dev, name)
+ Device *dev;
+ char *name;
+{
+ DeviceFont *f;
+
+ if (!dev)
+ return 0;
+ for (f = dev->fonts; f; f = f->next)
+ if (strcmp(f->name, name) == 0)
+ return f;
+ return load_font(dev, name);
+}
+
+static
+DeviceFont *load_font(dev, name)
+ Device *dev;
+ char *name;
+{
+ FILE *fp;
+ char buf[256];
+ DeviceFont *f;
+ int special = 0;
+
+ fp = open_device_file(dev->name, name, &current_filename);
+ if (!fp)
+ return 0;
+ current_lineno = 0;
+ for (;;) {
+ char *p;
+
+ if (!fgets(buf, sizeof(buf), fp)) {
+ error("no charset line");
+ return 0;
+ }
+ current_lineno++;
+ p = strtok(buf, WS);
+ /* charset must be on a line by itself */
+ if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
+ break;
+ if (p && strcmp(p, "special") == 0)
+ special = 1;
+ }
+ f = new_font(name, dev);
+ f->special = special;
+ if (!read_charset_section(f, fp)) {
+ delete_font(f);
+ f = 0;
+ }
+ else {
+ f->next = dev->fonts;
+ dev->fonts = f;
+ }
+ fclose(fp);
+ XtFree(current_filename);
+ current_filename = 0;
+ return f;
+}
+
+static
+DeviceFont *new_font(name, dev)
+ char *name;
+ Device *dev;
+{
+ int i;
+ DeviceFont *f;
+
+ f = XtNew(DeviceFont);
+ f->name = XtNewString(name);
+ f->dev = dev;
+ f->special = 0;
+ f->next = 0;
+ for (i = 0; i < CHAR_TABLE_SIZE; i++)
+ f->char_table[i] = 0;
+ for (i = 0; i < 256; i++)
+ f->code_table[i] = 0;
+ return f;
+}
+
+static
+void delete_font(f)
+ DeviceFont *f;
+{
+ int i;
+
+ if (!f)
+ return;
+ XtFree(f->name);
+ for (i = 0; i < CHAR_TABLE_SIZE; i++) {
+ struct charinfo *ptr = f->char_table[i];
+ while (ptr) {
+ struct charinfo *tem = ptr;
+ ptr = ptr->next;
+ XtFree((char *)tem);
+ }
+ }
+ XtFree((char *)f);
+}
+
+
+static
+unsigned hash_name(name)
+ char *name;
+{
+ unsigned n = 0;
+ /* XXX do better than this */
+ while (*name)
+ n = (n << 1) ^ *name++;
+
+ return n;
+}
+
+static
+int scale_round(n, x, y)
+ int n, x, y;
+{
+ int y2;
+
+ if (x == 0)
+ return 0;
+ y2 = y/2;
+ if (n >= 0) {
+ if (n <= (INT_MAX - y2)/x)
+ return (n*x + y2)/y;
+ }
+ else if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
+ return (n*x - y2)/y;
+ return (int)(n*(double)x/(double)y + .5);
+}
+
+static
+char *canonicalize_name(s)
+ char *s;
+{
+ static char ch[2];
+ if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
+ char *p;
+ int n;
+
+ for (p = s + 4; *p; p++)
+ if (!isascii(*p) || !isdigit((unsigned char)*p))
+ return s;
+ n = atoi(s + 4);
+ if (n >= 0 && n <= 0xff) {
+ ch[0] = (char)n;
+ return ch;
+ }
+ }
+ return s;
+}
+
+/* Return 1 if the character is present in the font; widthp gets the
+width if non-null. */
+
+int device_char_width(f, ps, name, widthp)
+ DeviceFont *f;
+ int ps;
+ char *name;
+ int *widthp;
+{
+ struct charinfo *p;
+
+ name = canonicalize_name(name);
+ for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
+ if (!p)
+ return 0;
+ if (strcmp(p->name, name) == 0)
+ break;
+ }
+ *widthp = scale_round(p->width, ps, f->dev->unitwidth);
+ return 1;
+}
+
+int device_code_width(f, ps, code, widthp)
+ DeviceFont *f;
+ int ps;
+ int code;
+ int *widthp;
+{
+ struct charinfo *p;
+
+ for (p = f->code_table[code & 0xff];; p = p->code_next) {
+ if (!p)
+ return 0;
+ if (p->code == code)
+ break;
+ }
+ *widthp = scale_round(p->width, ps, f->dev->unitwidth);
+ return 1;
+}
+
+char *device_name_for_code(f, code)
+ DeviceFont *f;
+ int code;
+{
+ static struct charinfo *state = 0;
+ if (f)
+ state = f->code_table[code & 0xff];
+ for (; state; state = state->code_next)
+ if (state->code == code && state->name[0] != '\0') {
+ char *name = state->name;
+ state = state->code_next;
+ return name;
+ }
+ return 0;
+}
+
+int device_font_special(f)
+ DeviceFont *f;
+{
+ return f->special;
+}
+
+static
+struct charinfo *add_char(f, name, width, code)
+ DeviceFont *f;
+ char *name;
+ int width, code;
+{
+ struct charinfo **pp;
+ struct charinfo *ci;
+
+ name = canonicalize_name(name);
+ if (strcmp(name, "---") == 0)
+ name = "";
+
+ ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
+ + strlen(name) + 1);
+
+ strcpy(ci->name, name);
+ ci->width = width;
+ ci->code = code;
+
+ if (*name != '\0') {
+ pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
+ ci->next = *pp;
+ *pp = ci;
+ }
+ pp = &f->code_table[code & 0xff];
+ ci->code_next = *pp;
+ *pp = ci;
+ return ci;
+}
+
+/* Return non-zero for success. */
+
+static
+int read_charset_section(f, fp)
+ DeviceFont *f;
+ FILE *fp;
+{
+ struct charinfo *last_charinfo = 0;
+ char buf[256];
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *name;
+ int width;
+ int code;
+ char *p;
+
+ current_lineno++;
+ name = strtok(buf, WS);
+ if (!name)
+ continue; /* ignore blank lines */
+ p = strtok((char *)0, WS);
+ if (!p) /* end of charset section */
+ break;
+ if (strcmp(p, "\"") == 0) {
+ if (!last_charinfo) {
+ error("first line of charset section cannot use `\"'");
+ return 0;
+ }
+ else
+ (void)add_char(f, name,
+ last_charinfo->width, last_charinfo->code);
+ }
+ else {
+ char *q;
+ if (sscanf(p, "%d", &width) != 1) {
+ error("bad width field");
+ return 0;
+ }
+ p = strtok((char *)0, WS);
+ if (!p) {
+ error("missing type field");
+ return 0;
+ }
+ p = strtok((char *)0, WS);
+ if (!p) {
+ error("missing code field");
+ return 0;
+ }
+ code = (int)strtol(p, &q, 0);
+ if (q == p) {
+ error("bad code field");
+ return 0;
+ }
+ last_charinfo = add_char(f, name, width, code);
+ }
+ }
+ return 1;
+}
+
+static
+FILE *find_file(file, result)
+ char *file, **result;
+{
+ char *buf = NULL;
+ int bufsiz = 0;
+ int flen;
+ FILE *fp;
+ char *path;
+ char *env;
+
+ env = getenv(FONTPATH_ENV_VAR);
+ path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
+ + strlen(FONTPATH) + 1);
+ *path = '\0';
+ if (env && *env) {
+ strcat(path, env);
+ strcat(path, ":");
+ }
+ strcat(path, FONTPATH);
+
+ *result = NULL;
+
+ if (file == NULL)
+ return NULL;
+ if (*file == '\0')
+ return NULL;
+
+ if (*file == '/') {
+ fp = fopen(file, "r");
+ if (fp)
+ *result = XtNewString(file);
+ return fp;
+ }
+
+ flen = strlen(file);
+
+ while (*path) {
+ int len;
+ char *start, *end;
+
+ start = path;
+ end = strchr(path, ':');
+ if (end)
+ path = end + 1;
+ else
+ path = end = strchr(path, '\0');
+ if (start >= end)
+ continue;
+ if (end[-1] == '/')
+ --end;
+ len = (end - start) + 1 + flen + 1;
+ if (len > bufsiz) {
+ if (buf)
+ buf = XtRealloc(buf, len);
+ else
+ buf = XtMalloc(len);
+ bufsiz = len;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '/';
+ strcpy(buf + (end - start) + 1, file);
+ fp = fopen(buf, "r");
+ if (fp) {
+ *result = buf;
+ return fp;
+ }
+ }
+ XtFree(buf);
+ return NULL;
+}
+
+static
+FILE *open_device_file(device_name, file_name, result)
+ char *device_name, *file_name, **result;
+{
+ char *buf, *path;
+ FILE *fp;
+
+ buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
+ sprintf(buf, "dev%s/%s", device_name, file_name);
+ fp = find_file(buf, result);
+ if (!fp) {
+ fprintf(stderr, "can't find device file `%s'\n", file_name);
+ fflush(stderr);
+ }
+ XtFree(buf);
+ return fp;
+}
+
+static
+void error(s)
+ char *s;
+{
+ if (current_filename) {
+ fprintf(stderr, "%s:", current_filename);
+ if (current_lineno > 0)
+ fprintf(stderr, "%d:", current_lineno);
+ putc(' ', stderr);
+ }
+ fputs(s, stderr);
+ putc('\n', stderr);
+ fflush(stderr);
+}
+
+/*
+Local Variables:
+c-indent-level: 4
+c-continued-statement-offset: 4
+c-brace-offset: -4
+c-argdecl-indent: 4
+c-label-offset: -4
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/device.h b/contrib/groff/src/xditview/device.h
new file mode 100644
index 000000000000..2b9a64bac90b
--- /dev/null
+++ b/contrib/groff/src/xditview/device.h
@@ -0,0 +1,21 @@
+
+typedef struct _DeviceFont DeviceFont;
+
+typedef struct _Device {
+ char *name;
+ int sizescale;
+ int res;
+ int unitwidth;
+ int paperlength;
+ int paperwidth;
+ int X11;
+ DeviceFont *fonts;
+} Device;
+
+extern void device_destroy();
+extern Device *device_load();
+extern DeviceFont *device_find_font();
+extern int device_char_width();
+extern char *device_name_for_code();
+extern int device_code_width();
+extern int device_font_special();
diff --git a/contrib/groff/src/xditview/draw.c b/contrib/groff/src/xditview/draw.c
new file mode 100644
index 000000000000..69e86cd6a6e1
--- /dev/null
+++ b/contrib/groff/src/xditview/draw.c
@@ -0,0 +1,721 @@
+/*
+ * draw.c
+ *
+ * accept dvi function calls and translate to X
+ */
+
+#include <X11/Xos.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+/* math.h on a Sequent doesn't define M_PI, apparently */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#include "DviP.h"
+
+#define DeviceToX(dw, n) ((int)((n) * (dw)->dvi.scale_factor + .5))
+#define XPos(dw) (DeviceToX((dw), (dw)->dvi.state->x - \
+ (dw)->dvi.text_device_width) + (dw)->dvi.text_x_width)
+#define YPos(dw) (DeviceToX((dw), (dw)->dvi.state->y))
+
+static int FakeCharacter();
+
+HorizontalMove(dw, delta)
+ DviWidget dw;
+ int delta;
+{
+ dw->dvi.state->x += delta;
+}
+
+HorizontalGoto(dw, NewPosition)
+ DviWidget dw;
+ int NewPosition;
+{
+ dw->dvi.state->x = NewPosition;
+}
+
+VerticalMove(dw, delta)
+ DviWidget dw;
+ int delta;
+{
+ dw->dvi.state->y += delta;
+}
+
+VerticalGoto(dw, NewPosition)
+ DviWidget dw;
+ int NewPosition;
+{
+ dw->dvi.state->y = NewPosition;
+}
+
+AdjustCacheDeltas (dw)
+ DviWidget dw;
+{
+ int extra;
+ int nadj;
+ int i;
+
+ nadj = 0;
+ extra = DeviceToX(dw, dw->dvi.text_device_width)
+ - dw->dvi.text_x_width;
+ if (extra == 0)
+ return;
+ for (i = 0; i <= dw->dvi.cache.index; i++)
+ if (dw->dvi.cache.adjustable[i])
+ ++nadj;
+ if (nadj == 0)
+ return;
+ dw->dvi.text_x_width += extra;
+ for (i = 0; i <= dw->dvi.cache.index; i++)
+ if (dw->dvi.cache.adjustable[i]) {
+ int x;
+ int *deltap;
+
+ x = extra/nadj;
+ deltap = &dw->dvi.cache.cache[i].delta;
+#define MIN_DELTA 2
+ if (*deltap > 0 && x + *deltap < MIN_DELTA) {
+ x = MIN_DELTA - *deltap;
+ if (x <= 0)
+ *deltap = MIN_DELTA;
+ else
+ x = 0;
+ }
+ else
+ *deltap += x;
+ extra -= x;
+ --nadj;
+ dw->dvi.cache.adjustable[i] = 0;
+ }
+}
+
+FlushCharCache (dw)
+ DviWidget dw;
+{
+ if (dw->dvi.cache.char_index != 0) {
+ AdjustCacheDeltas (dw);
+ XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ dw->dvi.cache.start_x, dw->dvi.cache.start_y,
+ dw->dvi.cache.cache, dw->dvi.cache.index + 1);
+ }
+ dw->dvi.cache.index = 0;
+ dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
+#if 0
+ if (dw->dvi.noPolyText)
+ dw->dvi.cache.max = 1;
+#endif
+ dw->dvi.cache.char_index = 0;
+ dw->dvi.cache.cache[0].nchars = 0;
+ dw->dvi.cache.start_x = dw->dvi.cache.x = XPos (dw);
+ dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw);
+}
+
+Newline (dw)
+ DviWidget dw;
+{
+ FlushCharCache (dw);
+ dw->dvi.text_x_width = dw->dvi.text_device_width = 0;
+ dw->dvi.word_flag = 0;
+}
+
+Word (dw)
+ DviWidget dw;
+{
+ dw->dvi.word_flag = 1;
+}
+
+#define charWidth(fi,c) (\
+ (fi)->per_char ?\
+ (fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
+ :\
+ (fi)->max_bounds.width\
+)
+
+
+static
+int charExists (fi, c)
+ XFontStruct *fi;
+ int c;
+{
+ XCharStruct *p;
+
+ if (fi->per_char == NULL ||
+ c < fi->min_char_or_byte2 || c > fi->max_char_or_byte2)
+ return 0;
+ p = fi->per_char + (c - fi->min_char_or_byte2);
+ return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
+ || p->ascent != 0 || p->descent != 0 || p->attributes != 0);
+}
+
+static
+DoCharacter (dw, c, wid)
+ DviWidget dw;
+ int c;
+ int wid; /* width in device units */
+{
+ register XFontStruct *font;
+ register XTextItem *text;
+ int x, y;
+
+ x = XPos(dw);
+ y = YPos(dw);
+
+ /*
+ * quick and dirty extents calculation:
+ */
+ if (!(y + 24 >= dw->dvi.extents.y1
+ && y - 24 <= dw->dvi.extents.y2
+#if 0
+ && x + 24 >= dw->dvi.extents.x1
+ && x - 24 <= dw->dvi.extents.x2
+#endif
+ ))
+ return;
+
+ if (y != dw->dvi.cache.y
+ || dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) {
+ FlushCharCache (dw);
+ x = dw->dvi.cache.x;
+ }
+ /*
+ * load a new font, if the current block is not empty,
+ * step to the next.
+ */
+ if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
+ dw->dvi.cache.font_number != dw->dvi.state->font_number)
+ {
+ dw->dvi.cache.font_size = dw->dvi.state->font_size;
+ dw->dvi.cache.font_number = dw->dvi.state->font_number;
+ dw->dvi.cache.font = QueryFont (dw,
+ dw->dvi.cache.font_number,
+ dw->dvi.cache.font_size);
+ if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
+ ++dw->dvi.cache.index;
+ if (dw->dvi.cache.index >= dw->dvi.cache.max)
+ FlushCharCache (dw);
+ dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
+ dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
+ }
+ }
+ if (x != dw->dvi.cache.x || dw->dvi.word_flag) {
+ if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
+ ++dw->dvi.cache.index;
+ if (dw->dvi.cache.index >= dw->dvi.cache.max)
+ FlushCharCache (dw);
+ dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
+ }
+ dw->dvi.cache.adjustable[dw->dvi.cache.index]
+ = dw->dvi.word_flag;
+ dw->dvi.word_flag = 0;
+ }
+ font = dw->dvi.cache.font;
+ text = &dw->dvi.cache.cache[dw->dvi.cache.index];
+ if (text->nchars == 0) {
+ text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
+ text->delta = x - dw->dvi.cache.x;
+ if (font != dw->dvi.font) {
+ text->font = font->fid;
+ dw->dvi.font = font;
+ } else
+ text->font = None;
+ dw->dvi.cache.x += text->delta;
+ }
+ if (charExists(font, c)) {
+ int w;
+ dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c;
+ ++text->nchars;
+ w = charWidth(font, c);
+ dw->dvi.cache.x += w;
+ if (wid != 0) {
+ dw->dvi.text_x_width += w;
+ dw->dvi.text_device_width += wid;
+ }
+ }
+}
+
+static
+int FindCharWidth (dw, buf, widp)
+ DviWidget dw;
+ char *buf;
+ int *widp;
+{
+ int maxpos;
+ int i;
+
+ if (dw->dvi.device_font == 0
+ || dw->dvi.state->font_number != dw->dvi.device_font_number) {
+ dw->dvi.device_font_number = dw->dvi.state->font_number;
+ dw->dvi.device_font
+ = QueryDeviceFont (dw, dw->dvi.device_font_number);
+ }
+ if (dw->dvi.device_font
+ && device_char_width (dw->dvi.device_font,
+ dw->dvi.state->font_size, buf, widp))
+ return 1;
+
+ maxpos = MaxFontPosition (dw);
+ for (i = 1; i <= maxpos; i++) {
+ DeviceFont *f = QueryDeviceFont (dw, i);
+ if (f && device_font_special (f)
+ && device_char_width (f, dw->dvi.state->font_size,
+ buf, widp)) {
+ dw->dvi.state->font_number = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Return the width of the character in device units. */
+
+int PutCharacter (dw, buf)
+ DviWidget dw;
+ char *buf;
+{
+ int prevFont;
+ int c = -1;
+ int wid = 0;
+ DviCharNameMap *map;
+
+ if (!dw->dvi.display_enable)
+ return 0; /* The width doesn't matter in this case. */
+ prevFont = dw->dvi.state->font_number;
+ if (!FindCharWidth (dw, buf, &wid))
+ return 0;
+ map = QueryFontMap (dw, dw->dvi.state->font_number);
+ if (map)
+ c = DviCharIndex (map, buf);
+ if (c >= 0)
+ DoCharacter (dw, c, wid);
+ else
+ (void) FakeCharacter (dw, buf, wid);
+ dw->dvi.state->font_number = prevFont;
+ return wid;
+}
+
+/* Return 1 if we can fake it; 0 otherwise. */
+
+static
+int FakeCharacter (dw, buf, wid)
+ DviWidget dw;
+ char *buf;
+ int wid;
+{
+ int oldx, oldw;
+ char ch[2];
+ char *chars = 0;
+
+ if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0')
+ return 0;
+#define pack2(c1, c2) (((c1) << 8) | (c2))
+
+ switch (pack2(buf[0], buf[1])) {
+ case pack2('f', 'i'):
+ chars = "fi";
+ break;
+ case pack2('f', 'l'):
+ chars = "fl";
+ break;
+ case pack2('f', 'f'):
+ chars = "ff";
+ break;
+ case pack2('F', 'i'):
+ chars = "ffi";
+ break;
+ case pack2('F', 'l'):
+ chars = "ffl";
+ break;
+ }
+ if (!chars)
+ return 0;
+ oldx = dw->dvi.state->x;
+ oldw = dw->dvi.text_device_width;
+ ch[1] = '\0';
+ for (; *chars; chars++) {
+ ch[0] = *chars;
+ dw->dvi.state->x += PutCharacter (dw, ch);
+ }
+ dw->dvi.state->x = oldx;
+ dw->dvi.text_device_width = oldw + wid;
+ return 1;
+}
+
+PutNumberedCharacter (dw, c)
+ DviWidget dw;
+ int c;
+{
+ char *name;
+ int wid;
+ DviCharNameMap *map;
+
+ if (!dw->dvi.display_enable)
+ return;
+
+ if (dw->dvi.device_font == 0
+ || dw->dvi.state->font_number != dw->dvi.device_font_number) {
+ dw->dvi.device_font_number = dw->dvi.state->font_number;
+ dw->dvi.device_font
+ = QueryDeviceFont (dw, dw->dvi.device_font_number);
+ }
+
+ if (dw->dvi.device_font == 0
+ || !device_code_width (dw->dvi.device_font,
+ dw->dvi.state->font_size, c, &wid))
+ return;
+ if (dw->dvi.native) {
+ DoCharacter (dw, c, wid);
+ return;
+ }
+ map = QueryFontMap (dw, dw->dvi.state->font_number);
+ if (!map)
+ return;
+ for (name = device_name_for_code (dw->dvi.device_font, c);
+ name;
+ name = device_name_for_code ((DeviceFont *)0, c)) {
+ int code = DviCharIndex (map, name);
+ if (code >= 0) {
+ DoCharacter (dw, code, wid);
+ break;
+ }
+ if (FakeCharacter (dw, name, wid))
+ break;
+ }
+}
+
+ClearPage (dw)
+ DviWidget dw;
+{
+ XClearWindow (XtDisplay (dw), XtWindow (dw));
+}
+
+static
+setGC (dw)
+ DviWidget dw;
+{
+ int desired_line_width;
+
+ if (dw->dvi.line_thickness < 0)
+ desired_line_width = (int)(((double)dw->dvi.device_resolution
+ * dw->dvi.state->font_size)
+ / (10.0*72.0*dw->dvi.sizescale));
+ else
+ desired_line_width = dw->dvi.line_thickness;
+
+ if (desired_line_width != dw->dvi.line_width) {
+ XGCValues values;
+ values.line_width = DeviceToX(dw, desired_line_width);
+ if (values.line_width == 0)
+ values.line_width = 1;
+ XChangeGC(XtDisplay (dw), dw->dvi.normal_GC,
+ GCLineWidth, &values);
+ dw->dvi.line_width = desired_line_width;
+ }
+}
+
+static
+setFillGC (dw)
+ DviWidget dw;
+{
+ int fill_type;
+ unsigned long mask = GCFillStyle | GCForeground;
+
+ fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1);
+ if (dw->dvi.fill_type != fill_type) {
+ XGCValues values;
+ if (fill_type <= 0) {
+ values.foreground = dw->dvi.background;
+ values.fill_style = FillSolid;
+ } else if (fill_type >= 9) {
+ values.foreground = dw->dvi.foreground;
+ values.fill_style = FillSolid;
+ } else {
+ values.foreground = dw->dvi.foreground;
+ values.fill_style = FillOpaqueStippled;
+ values.stipple = dw->dvi.gray[fill_type - 1];
+ mask |= GCStipple;
+ }
+ XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values);
+ dw->dvi.fill_type = fill_type;
+ }
+}
+
+DrawLine (dw, x, y)
+ DviWidget dw;
+ int x, y;
+{
+ int xp, yp;
+
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ xp = XPos (dw);
+ yp = YPos (dw);
+ XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ xp, yp,
+ xp + DeviceToX (dw, x), yp + DeviceToX (dw, y));
+}
+
+DrawCircle (dw, diam)
+ DviWidget dw;
+ int diam;
+{
+ int d;
+
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ d = DeviceToX (dw, diam);
+ XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ XPos (dw), YPos (dw) - d/2,
+ d, d, 0, 64*360);
+}
+
+DrawFilledCircle (dw, diam)
+ DviWidget dw;
+ int diam;
+{
+ int d;
+
+ AdjustCacheDeltas (dw);
+ setFillGC (dw);
+ d = DeviceToX (dw, diam);
+ XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ XPos (dw), YPos (dw) - d/2,
+ d, d, 0, 64*360);
+ XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ XPos (dw), YPos (dw) - d/2,
+ d, d, 0, 64*360);
+}
+
+DrawEllipse (dw, a, b)
+ DviWidget dw;
+ int a, b;
+{
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
+ DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
+}
+
+DrawFilledEllipse (dw, a, b)
+ DviWidget dw;
+ int a, b;
+{
+ AdjustCacheDeltas (dw);
+ setFillGC (dw);
+ XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
+ DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
+ XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
+ DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
+}
+
+DrawArc (dw, x0, y0, x1, y1)
+ DviWidget dw;
+ int x0, y0, x1, y1;
+{
+ int angle1, angle2;
+ int rad = (int)((sqrt ((double)x0*x0 + (double)y0*y0)
+ + sqrt ((double)x1*x1 + (double)y1*y1) + 1.0)/2.0);
+ if ((x0 == 0 && y0 == 0) || (x1 == 0 && y1 == 0))
+ return;
+ angle1 = (int)(atan2 ((double)y0, (double)-x0)*180.0*64.0/M_PI);
+ angle2 = (int)(atan2 ((double)-y1, (double)x1)*180.0*64.0/M_PI);
+
+ angle2 -= angle1;
+ if (angle2 < 0)
+ angle2 += 64*360;
+
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+
+ rad = DeviceToX (dw, rad);
+ XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ XPos (dw) + DeviceToX (dw, x0) - rad,
+ YPos (dw) + DeviceToX (dw, y0) - rad,
+ rad*2, rad*2, angle1, angle2);
+}
+
+DrawPolygon (dw, v, n)
+ DviWidget dw;
+ int *v;
+ int n;
+{
+ XPoint *p;
+ int i;
+ int dx, dy;
+
+ n /= 2;
+
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
+ p[0].x = XPos (dw);
+ p[0].y = YPos (dw);
+ dx = 0;
+ dy = 0;
+ for (i = 0; i < n; i++) {
+ dx += v[2*i];
+ p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
+ dy += v[2*i + 1];
+ p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
+ }
+ p[n+1].x = p[0].x;
+ p[n+1].y = p[0].y;
+ XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ p, n + 2, CoordModeOrigin);
+ XtFree((char *)p);
+}
+
+
+DrawFilledPolygon (dw, v, n)
+ DviWidget dw;
+ int *v;
+ int n;
+{
+ XPoint *p;
+ int i;
+ int dx, dy;
+
+ n /= 2;
+ if (n < 2)
+ return;
+
+ AdjustCacheDeltas (dw);
+ setFillGC (dw);
+ p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
+ p[0].x = p[n+1].x = XPos (dw);
+ p[0].y = p[n+1].y = YPos (dw);
+ dx = 0;
+ dy = 0;
+ for (i = 0; i < n; i++) {
+ dx += v[2*i];
+ p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
+ dy += v[2*i + 1];
+ p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
+ }
+ XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ p, n + 1, Complex, CoordModeOrigin);
+ XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ p, n + 2, CoordModeOrigin);
+ XtFree((char *)p);
+}
+
+#define POINTS_MAX 10000
+
+static
+appendPoint(points, pointi, x, y)
+ XPoint *points;
+ int *pointi;
+ int x, y;
+{
+ if (*pointi < POINTS_MAX) {
+ points[*pointi].x = x;
+ points[*pointi].y = y;
+ *pointi += 1;
+ }
+}
+
+#define FLATNESS 1
+
+static
+flattenCurve(points, pointi, x2, y2, x3, y3, x4, y4)
+ XPoint *points;
+ int *pointi;
+ int x2, y2, x3, y3, x4, y4;
+{
+ int x1, y1, dx, dy, n1, n2, n;
+
+ x1 = points[*pointi - 1].x;
+ y1 = points[*pointi - 1].y;
+
+ dx = x4 - x1;
+ dy = y4 - y1;
+
+ n1 = dy*(x2 - x1) - dx*(y2 - y1);
+ n2 = dy*(x3 - x1) - dx*(y3 - y1);
+ if (n1 < 0)
+ n1 = -n1;
+ if (n2 < 0)
+ n2 = -n2;
+ n = n1 > n2 ? n1 : n2;
+
+ if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS)
+ appendPoint (points, pointi, x4, y4);
+ else {
+ flattenCurve (points, pointi,
+ (x1 + x2)/2, (y1 + y2)/2,
+ (x1 + x2*2 + x3)/4, (y1 + y2*2 + y3)/4,
+ (x1 +3*x2 + 3*x3 + x4)/8, (y1 +3*y2 + 3*y3 + y4)/8);
+ flattenCurve (points, pointi,
+ (x2 + x3*2 + x4)/4, (y2 + y3*2 + y4)/4,
+ (x3 + x4)/2, (y3 + y4)/2,
+ x4, y4);
+ }
+}
+
+
+DrawSpline (dw, v, n)
+ DviWidget dw;
+ int *v;
+ int n;
+{
+ int sx, sy, tx, ty;
+ int ox, oy, dx, dy;
+ int i;
+ int pointi;
+ XPoint points[POINTS_MAX];
+
+ if (n == 0 || (n & 1) != 0)
+ return;
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ ox = XPos (dw);
+ oy = YPos (dw);
+ dx = v[0];
+ dy = v[1];
+ sx = ox;
+ sy = oy;
+ tx = sx + DeviceToX (dw, dx);
+ ty = sy + DeviceToX (dw, dy);
+
+ pointi = 0;
+
+ appendPoint (points, &pointi, sx, sy);
+ appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2);
+
+ for (i = 2; i < n; i += 2) {
+ int ux = ox + DeviceToX (dw, dx += v[i]);
+ int uy = oy + DeviceToX (dw, dy += v[i+1]);
+ flattenCurve (points, &pointi,
+ (sx + tx*5)/6, (sy + ty*5)/6,
+ (tx*5 + ux)/6, (ty*5 + uy)/6,
+ (tx + ux)/2, (ty + uy)/2);
+ sx = tx;
+ sy = ty;
+ tx = ux;
+ ty = uy;
+ }
+
+ appendPoint (points, &pointi, tx, ty);
+
+ XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
+ points, pointi, CoordModeOrigin);
+}
+
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/font.c b/contrib/groff/src/xditview/font.c
new file mode 100644
index 000000000000..2e028aa60b0e
--- /dev/null
+++ b/contrib/groff/src/xditview/font.c
@@ -0,0 +1,471 @@
+/*
+ * font.c
+ *
+ * map dvi fonts to X fonts
+ */
+
+#include <X11/Xos.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "DviP.h"
+#include "XFontName.h"
+
+static DisposeFontSizes();
+
+static char *
+savestr (s)
+ char *s;
+{
+ char *n;
+
+ if (!s)
+ return 0;
+ n = XtMalloc (strlen (s) + 1);
+ if (n)
+ strcpy (n, s);
+ return n;
+}
+
+static DviFontList *
+LookupFontByPosition (dw, position)
+ DviWidget dw;
+ int position;
+{
+ DviFontList *f;
+
+ for (f = dw->dvi.fonts; f; f = f->next)
+ if (f->dvi_number == position)
+ break;
+ return f;
+}
+
+int
+MaxFontPosition (dw)
+ DviWidget dw;
+{
+ DviFontList *f;
+ int n = -1;
+
+ for (f = dw->dvi.fonts; f; f = f->next)
+ if (f->dvi_number > n)
+ n = f->dvi_number;
+ return n;
+}
+
+static DviFontSizeList *
+LookupFontSizeBySize (dw, f, size)
+ DviWidget dw;
+ DviFontList *f;
+ int size;
+{
+ DviFontSizeList *fs, *best = 0, *smallest = 0;
+ int bestsize = 0;
+ XFontName fontName;
+ unsigned int fontNameAttributes;
+ char fontNameString[2048];
+ int decipointsize;
+
+ if (f->scalable) {
+ decipointsize = (10*size)/dw->dvi.sizescale;
+ for (best = f->sizes; best; best = best->next)
+ if (best->size == decipointsize)
+ return best;
+ best = (DviFontSizeList *) XtMalloc(sizeof *best);
+ best->next = f->sizes;
+ best->size = decipointsize;
+ f->sizes = best;
+ XParseFontName (f->x_name, &fontName, &fontNameAttributes);
+ fontNameAttributes &= ~(FontNamePixelSize|FontNameAverageWidth);
+ fontNameAttributes |= FontNameResolutionX;
+ fontNameAttributes |= FontNameResolutionY;
+ fontNameAttributes |= FontNamePointSize;
+ fontName.ResolutionX = dw->dvi.display_resolution;
+ fontName.ResolutionY = dw->dvi.display_resolution;
+ fontName.PointSize = decipointsize;
+ XFormatFontName (&fontName, fontNameAttributes, fontNameString);
+ best->x_name = savestr (fontNameString);
+ best->doesnt_exist = 0;
+ best->font = 0;
+ return best;
+ }
+ for (fs = f->sizes; fs; fs=fs->next) {
+ if (dw->dvi.sizescale*fs->size <= 10*size
+ && fs->size >= bestsize) {
+ best = fs;
+ bestsize = fs->size;
+ }
+ if (smallest == 0 || fs->size < smallest->size)
+ smallest = fs;
+ }
+ return best ? best : smallest;
+}
+
+static char *
+SkipFontNameElement (n)
+ char *n;
+{
+ while (*n != '-')
+ if (!*++n)
+ return 0;
+ return n+1;
+}
+
+# define SizePosition 8
+# define EncodingPosition 13
+
+static
+ConvertFontNameToSize (n)
+ char *n;
+{
+ int i, size;
+
+ for (i = 0; i < SizePosition; i++) {
+ n = SkipFontNameElement (n);
+ if (!n)
+ return -1;
+ }
+ size = atoi (n);
+ return size;
+}
+
+static char *
+ConvertFontNameToEncoding (n)
+ char *n;
+{
+ int i;
+ for (i = 0; i < EncodingPosition; i++) {
+ n = SkipFontNameElement (n);
+ if (!n)
+ return 0;
+ }
+ return n;
+}
+
+DviFontSizeList *
+InstallFontSizes (dw, x_name, scalablep)
+ DviWidget dw;
+ char *x_name;
+ Boolean *scalablep;
+{
+ char fontNameString[2048];
+ char **fonts;
+ int i, count;
+ int size;
+ DviFontSizeList *sizes, *new;
+ XFontName fontName;
+ unsigned int fontNameAttributes;
+
+ *scalablep = FALSE;
+ if (!XParseFontName (x_name, &fontName, &fontNameAttributes))
+ return 0;
+ fontNameAttributes &= ~(FontNamePixelSize|FontNamePointSize
+ |FontNameAverageWidth);
+ fontNameAttributes |= FontNameResolutionX;
+ fontNameAttributes |= FontNameResolutionY;
+ fontName.ResolutionX = dw->dvi.display_resolution;
+ fontName.ResolutionY = dw->dvi.display_resolution;
+ XFormatFontName (&fontName, fontNameAttributes, fontNameString);
+ fonts = XListFonts (XtDisplay (dw), fontNameString, 10000000, &count);
+ sizes = 0;
+ for (i = 0; i < count; i++) {
+ size = ConvertFontNameToSize (fonts[i]);
+ if (size == 0) {
+ DisposeFontSizes (dw, sizes);
+ sizes = 0;
+ *scalablep = TRUE;
+ break;
+ }
+ if (size != -1) {
+ new = (DviFontSizeList *) XtMalloc (sizeof *new);
+ new->next = sizes;
+ new->size = size;
+ new->x_name = savestr (fonts[i]);
+ new->doesnt_exist = 0;
+ new->font = 0;
+ sizes = new;
+ }
+ }
+ XFreeFontNames (fonts);
+ return sizes;
+}
+
+static
+DisposeFontSizes (dw, fs)
+ DviWidget dw;
+ DviFontSizeList *fs;
+{
+ DviFontSizeList *next;
+
+ for (; fs; fs=next) {
+ next = fs->next;
+ if (fs->x_name)
+ XtFree (fs->x_name);
+ if (fs->font && fs->font != dw->dvi.default_font) {
+ XUnloadFont (XtDisplay (dw), fs->font->fid);
+ XFree ((char *)fs->font);
+ }
+ XtFree ((char *) fs);
+ }
+}
+
+static DviFontList *
+InstallFont (dw, position, dvi_name, x_name)
+ DviWidget dw;
+ int position;
+ char *dvi_name;
+ char *x_name;
+{
+ DviFontList *f;
+ char *encoding;
+
+ if ((f = LookupFontByPosition (dw, position)) != NULL) {
+ /*
+ * ignore gratuitous font loading
+ */
+ if (!strcmp (f->dvi_name, dvi_name) &&
+ !strcmp (f->x_name, x_name))
+ return f;
+
+ DisposeFontSizes (dw, f->sizes);
+ if (f->dvi_name)
+ XtFree (f->dvi_name);
+ if (f->x_name)
+ XtFree (f->x_name);
+ f->device_font = 0;
+ } else {
+ f = (DviFontList *) XtMalloc (sizeof (*f));
+ f->next = dw->dvi.fonts;
+ dw->dvi.fonts = f;
+ }
+ f->initialized = FALSE;
+ f->dvi_name = savestr (dvi_name);
+ f->device_font = device_find_font (dw->dvi.device, dvi_name);
+ f->x_name = savestr (x_name);
+ f->dvi_number = position;
+ f->sizes = 0;
+ f->scalable = FALSE;
+ if (f->x_name) {
+ encoding = ConvertFontNameToEncoding (f->x_name);
+ f->char_map = DviFindMap (encoding);
+ } else
+ f->char_map = 0;
+ /*
+ * force requery of fonts
+ */
+ dw->dvi.font = 0;
+ dw->dvi.font_number = -1;
+ dw->dvi.cache.font = 0;
+ dw->dvi.cache.font_number = -1;
+ dw->dvi.device_font = 0;
+ dw->dvi.device_font_number = -1;
+ return f;
+}
+
+ForgetFonts (dw)
+ DviWidget dw;
+{
+ DviFontList *f = dw->dvi.fonts;
+
+ while (f) {
+ DviFontList *tem = f;
+
+ if (f->sizes)
+ DisposeFontSizes (dw, f->sizes);
+ if (f->dvi_name)
+ XtFree (f->dvi_name);
+ if (f->x_name)
+ XtFree (f->x_name);
+ f = f->next;
+ XtFree ((char *) tem);
+ }
+
+ /*
+ * force requery of fonts
+ */
+ dw->dvi.font = 0;
+ dw->dvi.font_number = -1;
+ dw->dvi.cache.font = 0;
+ dw->dvi.cache.font_number = -1;
+ dw->dvi.device_font = 0;
+ dw->dvi.device_font_number = -1;
+ dw->dvi.fonts = 0;
+}
+
+
+static char *
+MapDviNameToXName (dw, dvi_name)
+ DviWidget dw;
+ char *dvi_name;
+{
+ DviFontMap *fm;
+
+ for (fm = dw->dvi.font_map; fm; fm=fm->next)
+ if (!strcmp (fm->dvi_name, dvi_name))
+ return fm->x_name;
+ return 0;
+}
+
+#if 0
+static char *
+MapXNameToDviName (dw, x_name)
+ DviWidget dw;
+ char *x_name;
+{
+ DviFontMap *fm;
+
+ for (fm = dw->dvi.font_map; fm; fm=fm->next)
+ if (!strcmp (fm->x_name, x_name))
+ return fm->dvi_name;
+ return 0;
+}
+#endif
+
+ParseFontMap (dw)
+ DviWidget dw;
+{
+ char dvi_name[1024];
+ char x_name[2048];
+ char *m, *s;
+ DviFontMap *fm, *new;
+
+ if (dw->dvi.font_map)
+ DestroyFontMap (dw->dvi.font_map);
+ fm = 0;
+ m = dw->dvi.font_map_string;
+ while (*m) {
+ s = m;
+ while (*m && !isspace (*m))
+ ++m;
+ strncpy (dvi_name, s, m-s);
+ dvi_name[m-s] = '\0';
+ while (isspace (*m))
+ ++m;
+ s = m;
+ while (*m && *m != '\n')
+ ++m;
+ strncpy (x_name, s, m-s);
+ x_name[m-s] = '\0';
+ new = (DviFontMap *) XtMalloc (sizeof *new);
+ new->x_name = savestr (x_name);
+ new->dvi_name = savestr (dvi_name);
+ new->next = fm;
+ fm = new;
+ ++m;
+ }
+ dw->dvi.font_map = fm;
+}
+
+DestroyFontMap (font_map)
+ DviFontMap *font_map;
+{
+ DviFontMap *next;
+
+ for (; font_map; font_map = next) {
+ next = font_map->next;
+ if (font_map->x_name)
+ XtFree (font_map->x_name);
+ if (font_map->dvi_name)
+ XtFree (font_map->dvi_name);
+ XtFree ((char *) font_map);
+ }
+}
+
+/* ARGSUSED */
+
+SetFontPosition (dw, position, dvi_name, extra)
+ DviWidget dw;
+ int position;
+ char *dvi_name;
+ char *extra; /* unused */
+{
+ char *x_name;
+
+ x_name = MapDviNameToXName (dw, dvi_name);
+ if (x_name)
+ (void) InstallFont (dw, position, dvi_name, x_name);
+}
+
+XFontStruct *
+QueryFont (dw, position, size)
+ DviWidget dw;
+ int position;
+ int size;
+{
+ DviFontList *f;
+ DviFontSizeList *fs;
+
+ f = LookupFontByPosition (dw, position);
+ if (!f)
+ return dw->dvi.default_font;
+ if (!f->initialized) {
+ f->sizes = InstallFontSizes (dw, f->x_name, &f->scalable);
+ f->initialized = TRUE;
+ }
+ fs = LookupFontSizeBySize (dw, f, size);
+ if (!fs)
+ return dw->dvi.default_font;
+ if (!fs->font) {
+ if (fs->x_name)
+ fs->font = XLoadQueryFont (XtDisplay (dw), fs->x_name);
+ if (!fs->font)
+ fs->font = dw->dvi.default_font;
+ }
+ return fs->font;
+}
+
+DeviceFont *
+QueryDeviceFont (dw, position)
+ DviWidget dw;
+ int position;
+{
+ DviFontList *f;
+
+ f = LookupFontByPosition (dw, position);
+ if (!f)
+ return 0;
+ return f->device_font;
+}
+
+DviCharNameMap *
+QueryFontMap (dw, position)
+ DviWidget dw;
+ int position;
+{
+ DviFontList *f;
+
+ f = LookupFontByPosition (dw, position);
+ if (f)
+ return f->char_map;
+ else
+ return 0;
+}
+
+#if 0
+LoadFont (dw, position, size)
+ DviWidget dw;
+ int position;
+ int size;
+{
+ XFontStruct *font;
+
+ font = QueryFont (dw, position, size);
+ dw->dvi.font_number = position;
+ dw->dvi.font_size = size;
+ dw->dvi.font = font;
+ XSetFont (XtDisplay (dw), dw->dvi.normal_GC, font->fid);
+ return;
+}
+#endif
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/gray1.bm b/contrib/groff/src/xditview/gray1.bm
new file mode 100644
index 000000000000..c40a95e6eab6
--- /dev/null
+++ b/contrib/groff/src/xditview/gray1.bm
@@ -0,0 +1,4 @@
+#define gray1_width 3
+#define gray1_height 3
+static char gray1_bits[] = {
+ 0x00, 0x02, 0x00};
diff --git a/contrib/groff/src/xditview/gray2.bm b/contrib/groff/src/xditview/gray2.bm
new file mode 100644
index 000000000000..e87a1bcc073c
--- /dev/null
+++ b/contrib/groff/src/xditview/gray2.bm
@@ -0,0 +1,4 @@
+#define gray2_width 3
+#define gray2_height 3
+static char gray2_bits[] = {
+ 0x00, 0x03, 0x00};
diff --git a/contrib/groff/src/xditview/gray3.bm b/contrib/groff/src/xditview/gray3.bm
new file mode 100644
index 000000000000..d9313ebd5e8f
--- /dev/null
+++ b/contrib/groff/src/xditview/gray3.bm
@@ -0,0 +1,4 @@
+#define gray3_width 3
+#define gray3_height 3
+static char gray3_bits[] = {
+ 0x00, 0x03, 0x02};
diff --git a/contrib/groff/src/xditview/gray4.bm b/contrib/groff/src/xditview/gray4.bm
new file mode 100644
index 000000000000..dad142a9b0f0
--- /dev/null
+++ b/contrib/groff/src/xditview/gray4.bm
@@ -0,0 +1,4 @@
+#define gray4_width 3
+#define gray4_height 3
+static char gray4_bits[] = {
+ 0x00, 0x07, 0x02};
diff --git a/contrib/groff/src/xditview/gray5.bm b/contrib/groff/src/xditview/gray5.bm
new file mode 100644
index 000000000000..5f576184172d
--- /dev/null
+++ b/contrib/groff/src/xditview/gray5.bm
@@ -0,0 +1,4 @@
+#define gray5_width 3
+#define gray5_height 3
+static char gray5_bits[] = {
+ 0x04, 0x07, 0x02};
diff --git a/contrib/groff/src/xditview/gray6.bm b/contrib/groff/src/xditview/gray6.bm
new file mode 100644
index 000000000000..b76701db16d3
--- /dev/null
+++ b/contrib/groff/src/xditview/gray6.bm
@@ -0,0 +1,4 @@
+#define gray6_width 3
+#define gray6_height 3
+static char gray6_bits[] = {
+ 0x04, 0x07, 0x03};
diff --git a/contrib/groff/src/xditview/gray7.bm b/contrib/groff/src/xditview/gray7.bm
new file mode 100644
index 000000000000..ef47bc692e99
--- /dev/null
+++ b/contrib/groff/src/xditview/gray7.bm
@@ -0,0 +1,4 @@
+#define gray7_width 3
+#define gray7_height 3
+static char gray7_bits[] = {
+ 0x05, 0x07, 0x03};
diff --git a/contrib/groff/src/xditview/gray8.bm b/contrib/groff/src/xditview/gray8.bm
new file mode 100644
index 000000000000..12de7cb6f577
--- /dev/null
+++ b/contrib/groff/src/xditview/gray8.bm
@@ -0,0 +1,4 @@
+#define gray8_width 3
+#define gray8_height 3
+static char gray8_bits[] = {
+ 0x05, 0x07, 0x07};
diff --git a/contrib/groff/src/xditview/gxditview.man b/contrib/groff/src/xditview/gxditview.man
new file mode 100644
index 000000000000..04cd446dfab6
--- /dev/null
+++ b/contrib/groff/src/xditview/gxditview.man
@@ -0,0 +1,246 @@
+.\" -*- nroff -*-
+.TH GXDITVIEW 1 "Release 5" "X Version 11"
+.SH NAME
+gxditview \- display gtroff output files
+.SH SYNOPSIS
+.B gxditview
+.RI [\fB\- toolkitoption\ .\|.\|.\|]
+.RI [\fB\- option\ .\|.\|.\|]
+.RI [ filename ]
+.SH DESCRIPTION
+The
+.I gxditview
+program displays gtroff output on an X display.
+It uses the standard X11 fonts,
+so it does not require access to the server machine for font loading.
+.PP
+If
+.I filename
+is
+.BR \- ,
+.I gxditview
+will read the standard input.
+.PP
+The left mouse button brings up a menu with the following entries:
+.TP 8
+.B "Next Page"
+Display the next page.
+.TP
+.B "Previous Page"
+Display the previous page.
+.TP
+.B "Select Page"
+Select a particular numbered page specified by a dialog box.
+.TP
+.B Print
+Print the gtroff output using a command specified by a dialog box.
+The default command initially displayed is controlled by the
+.B printCommand
+application resource, and by the
+.B \-printCommand
+option.
+.TP
+.B Open
+Open for display a new file specified by a dialog box.
+The file should contain gtroff output.
+If the filename starts with
+.B |
+it will be taken to be a command to read from.
+.TP
+.B Quit
+Exit from
+.IR gxditview .
+.PP
+The
+.BR n ,
+Space
+and Return keys are bound to the
+.B Next\ Page
+action.
+The
+.BR p ,
+BackSpace
+and
+Delete
+keys are bound to the
+.B Previous\ Page
+action.
+The
+.B q
+key is bound to the
+.B Quit
+action.
+The
+.B r
+key is bound to the
+.B Rerasterize
+action which rereads the current file, and redisplays the current page;
+if the current file is a command, the command will be reexecuted.
+.PP
+The
+.B paperlength
+and
+.B paperwidth
+commands in the DESC file specify the length and width in machine units
+of the virtual page displayed by
+.IR gxditview .
+.SH OPTIONS
+.I Gxditview
+accepts all of the standard X Toolkit command line options along with the
+additional options listed below:
+.TP 8
+.B \-help
+This option indicates that a brief summary of the allowed options should be
+printed.
+.TP
+.B \-page
+This option specifies the page number of the document to be displayed.
+.TP
+.BI \-backingStore\ backing-store-type
+Redisplay of the gtroff output window can take upto a second or so,
+this option causes the server to save the window contents so that when
+it is scrolled around the viewport, the window is painted from
+contents saved in backing store.
+.I backing-store-type
+can be one of
+.BR Always ,
+.B WhenMapped
+or
+.BR NotUseful .
+.TP
+.BI \-printCommand\ command
+The default command displayed in the dialog box for the
+.B Print
+menu entry will be
+.IR command .
+.TP
+.BI \-resolution\ res
+The gtroff output file will be displayed at a resolution of
+.I res
+dpi,
+unless the DESC file contains the
+.B X11
+command, in which case the device resolution will be used.
+This corresponds the
+.I Dvi
+widget's
+.B resolution
+resource.
+The default is 75.
+.TP
+.BI \-filename\ string
+The default filename displayed in the dialog box for the
+.B Open
+menu entry will be
+.IR string .
+This can be either a filename, or a command starting with
+.BR | .
+.PP
+The following standard X Toolkit command line arguments are commonly used with
+.IR gxditview :
+.TP 8
+.BI \-bg\ color
+This option specifies the color to use for the background of the window.
+The default is \fIwhite\fP.
+.TP
+.BI \-bd\ color
+This option specifies the color to use for the border of the window.
+The default is \fIblack\fP.
+.TP
+.BI \-bw\ number
+This option specifies the width in pixels of the border surrounding the window.
+.TP
+.BI \-fg\ color
+This option specifies the color to use for displaying text. The default is
+\fIblack\fP.
+.TP
+.BI \-fn\ font
+This option specifies the font to be used for displaying widget text. The
+default is \fIfixed\fP.
+.TP
+.B \-rv
+This option indicates that reverse video should be simulated by swapping
+the foreground and background colors.
+.TP
+.BI \-geometry\ geometry
+This option specifies the preferred size and position of the window.
+.TP
+.BI \-display\ host : display
+This option specifies the X server to contact.
+.TP
+.BI \-xrm\ resourcestring
+This option specifies a resource string to be used.
+.SH X DEFAULTS
+This program uses the
+.I Dvi
+widget in the X Toolkit. It understands all of the core resource names and
+classes as well as:
+.PP
+.TP 8
+.BR width\ (class\ Width )
+Specifies the width of the window.
+.TP
+.BR height\ (class\ Height )
+Specifies the height of the window.
+.TP
+.BR foreground\ (class\ Foreground )
+Specifies the default foreground color.
+.TP
+.BR font\ (class\ Font )
+Specifies the font to be used for error messages.
+.TP
+.BR fontMap\ (class\ FontMap )
+Specifies the mapping from groff font names to X font names. This
+must be a string containing a sequence of lines. Each line contains
+two whitespace separated fields: first the groff font name, and
+secondly the X font name. The default is
+.nf
+"\e
+TR -adobe-times-medium-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+TI -adobe-times-medium-i-normal--*-100-*-*-*-*-iso8859-1\en\e
+TB -adobe-times-bold-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+TBI -adobe-times-bold-i-normal--*-100-*-*-*-*-iso8859-1\en\e
+CR -adobe-courier-medium-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+CI -adobe-courier-medium-o-normal--*-100-*-*-*-*-iso8859-1\en\e
+CB -adobe-courier-bold-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+CBI -adobe-courier-bold-o-normal--*-100-*-*-*-*-iso8859-1\en\e
+HR -adobe-helvetica-medium-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+HI -adobe-helvetica-medium-o-normal--*-100-*-*-*-*-iso8859-1\en\e
+HB -adobe-helvetica-bold-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+HBI -adobe-helvetica-bold-o-normal--*-100-*-*-*-*-iso8859-1\en\e
+NR -adobe-new century schoolbook-medium-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+NI -adobe-new century schoolbook-medium-i-normal--*-100-*-*-*-*-iso8859-1\en\e
+NB -adobe-new century schoolbook-bold-r-normal--*-100-*-*-*-*-iso8859-1\en\e
+NBI -adobe-new century schoolbook-bold-i-normal--*-100-*-*-*-*-iso8859-1\en\e
+S -adobe-symbol-medium-r-normal--*-100-*-*-*-*-adobe-fontspecific\en\e
+SS -adobe-symbol-medium-r-normal--*-100-*-*-*-*-adobe-fontspecific\en\e
+"
+.fi
+
+.SH "SEE ALSO"
+.IR X (1),
+.IR xrdb (1),
+.IR gtroff (1),
+.IR groff (1)
+.SH ORIGIN
+This program is derived from xditview;
+portions of xditview originated in xtroff which was derived
+from suntroff.
+.SH COPYRIGHT
+Copyright 1989, Massachusetts Institute of Technology.
+.br
+See
+.IR X (1)
+for a full statement of rights and permissions.
+.SH AUTHORS
+Keith Packard (MIT X Consortium)
+.br
+Richard L. Hyde (Purdue)
+.br
+David Slattengren (Berkeley)
+.br
+Malcolm Slaney (Schlumberger Palo Alto Research)
+.br
+Mark Moraes (University of Toronto)
+.br
+James Clark
diff --git a/contrib/groff/src/xditview/lex.c b/contrib/groff/src/xditview/lex.c
new file mode 100644
index 000000000000..32831bdacd43
--- /dev/null
+++ b/contrib/groff/src/xditview/lex.c
@@ -0,0 +1,103 @@
+#include <X11/Xos.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <stdio.h>
+#include "DviP.h"
+
+DviGetAndPut(dw, cp)
+ DviWidget dw;
+ int *cp;
+{
+ if (dw->dvi.ungot) {
+ dw->dvi.ungot = 0;
+ *cp = getc (dw->dvi.file);
+ }
+ else {
+ *cp = getc (dw->dvi.file);
+ if (*cp != EOF)
+ putc (*cp, dw->dvi.tmpFile);
+ }
+ return *cp;
+}
+
+char *
+GetLine(dw, Buffer, Length)
+ DviWidget dw;
+ char *Buffer;
+ int Length;
+{
+ int i = 0, c;
+
+ Length--; /* Save room for final '\0' */
+
+ while (DviGetC (dw, &c) != EOF) {
+ if (Buffer && i < Length)
+ Buffer[i++] = c;
+ if (c == '\n') {
+ DviUngetC(dw, c);
+ break;
+ }
+ }
+ if (Buffer)
+ Buffer[i] = '\0';
+ return Buffer;
+}
+
+char *
+GetWord(dw, Buffer, Length)
+ DviWidget dw;
+ char *Buffer;
+ int Length;
+{
+ int i = 0, c;
+
+ Length--; /* Save room for final '\0' */
+ while (DviGetC(dw, &c) == ' ' || c == '\n')
+ ;
+ while (c != EOF) {
+ if (Buffer && i < Length)
+ Buffer[i++] = c;
+ if (DviGetC(dw, &c) == ' ' || c == '\n') {
+ DviUngetC(dw, c);
+ break;
+ }
+ }
+ if (Buffer)
+ Buffer[i] = '\0';
+ return Buffer;
+}
+
+GetNumber(dw)
+ DviWidget dw;
+{
+ int i = 0, c;
+ int negative = 0;
+
+ while (DviGetC(dw, &c) == ' ' || c == '\n')
+ ;
+ if (c == '-') {
+ negative = 1;
+ DviGetC(dw, &c);
+ }
+
+ for (; c >= '0' && c <= '9'; DviGetC(dw, &c)) {
+ if (negative)
+ i = i*10 - (c - '0');
+ else
+ i = i*10 + c - '0';
+ }
+ if (c != EOF)
+ DviUngetC(dw, c);
+ return i;
+}
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/page.c b/contrib/groff/src/xditview/page.c
new file mode 100644
index 000000000000..9284199cde8d
--- /dev/null
+++ b/contrib/groff/src/xditview/page.c
@@ -0,0 +1,88 @@
+/*
+ * page.c
+ *
+ * map page numbers to file position
+ */
+
+#include <X11/Xos.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "DviP.h"
+
+#ifdef X_NOT_STDC_ENV
+extern long ftell();
+#endif
+
+static DviFileMap *
+MapPageNumberToFileMap (dw, number)
+ DviWidget dw;
+ int number;
+{
+ DviFileMap *m;
+
+ for (m = dw->dvi.file_map; m; m=m->next)
+ if (m->page_number == number)
+ break;
+ return m;
+}
+
+DestroyFileMap (m)
+ DviFileMap *m;
+{
+ DviFileMap *next;
+
+ for (; m; m = next) {
+ next = m->next;
+ XtFree ((char *) m);
+ }
+}
+
+ForgetPagePositions (dw)
+ DviWidget dw;
+{
+ DestroyFileMap (dw->dvi.file_map);
+ dw->dvi.file_map = 0;
+}
+
+RememberPagePosition(dw, number)
+ DviWidget dw;
+ int number;
+{
+ DviFileMap *m;
+
+ if (!(m = MapPageNumberToFileMap (dw, number))) {
+ m = (DviFileMap *) XtMalloc (sizeof *m);
+ m->page_number = number;
+ m->next = dw->dvi.file_map;
+ dw->dvi.file_map = m;
+ }
+ if (dw->dvi.tmpFile)
+ m->position = ftell (dw->dvi.tmpFile);
+ else
+ m->position = ftell (dw->dvi.file);
+}
+
+SearchPagePosition (dw, number)
+ DviWidget dw;
+ int number;
+{
+ DviFileMap *m;
+
+ if (!(m = MapPageNumberToFileMap (dw, number)))
+ return -1;
+ return m->position;
+}
+
+FileSeek(dw, position)
+DviWidget dw;
+long position;
+{
+ if (dw->dvi.tmpFile) {
+ dw->dvi.readingTmp = 1;
+ fseek (dw->dvi.tmpFile, position, 0);
+ } else
+ fseek (dw->dvi.file, position, 0);
+}
+
diff --git a/contrib/groff/src/xditview/parse.c b/contrib/groff/src/xditview/parse.c
new file mode 100644
index 000000000000..e1df8c9dcbb2
--- /dev/null
+++ b/contrib/groff/src/xditview/parse.c
@@ -0,0 +1,335 @@
+/*
+ * parse.c
+ *
+ * parse dvi input
+ */
+
+#include <X11/Xos.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "DviP.h"
+
+static int StopSeen = 0;
+static ParseDrawFunction(), ParseDeviceControl();
+static push_env(), pop_env();
+
+#define HorizontalMove(dw, delta) ((dw)->dvi.state->x += (delta))
+
+
+ParseInput(dw)
+ register DviWidget dw;
+{
+ int n, k;
+ int c;
+ char Buffer[BUFSIZ];
+ int NextPage;
+ int otherc;
+
+ StopSeen = 0;
+
+ /*
+ * make sure some state exists
+ */
+
+ if (!dw->dvi.state)
+ push_env (dw);
+ for (;;) {
+ switch (DviGetC(dw, &c)) {
+ case '\n':
+ break;
+ case ' ': /* when input is text */
+ case 0: /* occasional noise creeps in */
+ break;
+ case '{': /* push down current environment */
+ push_env(dw);
+ break;
+ case '}':
+ pop_env(dw);
+ break;
+ /*
+ * two motion digits plus a character
+ */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ HorizontalMove(dw, (c-'0')*10 +
+ DviGetC(dw,&otherc)-'0');
+ /* fall through */
+ case 'c': /* single ascii character */
+ DviGetC(dw,&c);
+ if (c == ' ')
+ break;
+ Buffer[0] = c;
+ Buffer[1] = '\0';
+ (void) PutCharacter (dw, Buffer);
+ break;
+ case 'C':
+ GetWord (dw, Buffer, BUFSIZ);
+ (void) PutCharacter (dw, Buffer);
+ break;
+ case 't':
+ Buffer[1] = '\0';
+ while (DviGetC (dw, &c) != EOF
+ && c != ' ' && c != '\n') {
+ Buffer[0] = c;
+ HorizontalMove (dw, PutCharacter (dw, Buffer));
+ }
+ break;
+ case 'u':
+ n = GetNumber(dw);
+ Buffer[1] = '\0';
+ while (DviGetC (dw, &c) == ' ')
+ ;
+ while (c != EOF && c != ' ' && c != '\n') {
+ Buffer[0] = c;
+ HorizontalMove (dw,
+ PutCharacter (dw, Buffer) + n);
+ DviGetC (dw, &c);
+ }
+ break;
+
+ case 'D': /* draw function */
+ (void) GetLine(dw, Buffer, BUFSIZ);
+ if (dw->dvi.display_enable)
+ ParseDrawFunction(dw, Buffer);
+ break;
+ case 's': /* ignore fractional sizes */
+ n = GetNumber(dw);
+ dw->dvi.state->font_size = n;
+ break;
+ case 'f':
+ n = GetNumber(dw);
+ dw->dvi.state->font_number = n;
+ break;
+ case 'H': /* absolute horizontal motion */
+ k = GetNumber(dw);
+ HorizontalGoto(dw, k);
+ break;
+ case 'h': /* relative horizontal motion */
+ k = GetNumber(dw);
+ HorizontalMove(dw, k);
+ break;
+ case 'w': /* word space */
+ Word (dw);
+ break;
+ case 'V':
+ n = GetNumber(dw);
+ VerticalGoto(dw, n);
+ break;
+ case 'v':
+ n = GetNumber(dw);
+ VerticalMove(dw, n);
+ break;
+ case 'P': /* new spread */
+ break;
+ case 'p': /* new page */
+ (void) GetNumber(dw);
+ NextPage = dw->dvi.current_page + 1;
+ RememberPagePosition(dw, NextPage);
+ FlushCharCache (dw);
+ return(NextPage);
+ case 'N':
+ n = GetNumber(dw);
+ PutNumberedCharacter (dw, n);
+ break;
+ case 'n': /* end of line */
+ GetNumber(dw);
+ GetNumber(dw);
+ Newline (dw);
+ HorizontalGoto(dw, 0);
+ break;
+ case 'F': /* input files */
+ case '+': /* continuation of X device control */
+ case '#': /* comment */
+ GetLine(dw, NULL, 0);
+ break;
+ case 'x': /* device control */
+ ParseDeviceControl(dw);
+ break;
+ case EOF:
+ dw->dvi.last_page = dw->dvi.current_page;
+ FlushCharCache (dw);
+ return dw->dvi.current_page;
+ default:
+ break;
+ }
+ }
+}
+
+static
+push_env(dw)
+ DviWidget dw;
+{
+ DviState *new;
+
+ new = (DviState *) XtMalloc (sizeof (*new));
+ if (dw->dvi.state)
+ *new = *(dw->dvi.state);
+ else {
+ new->font_size = 10;
+ new->font_number = 1;
+ new->x = 0;
+ new->y = 0;
+ }
+ new->next = dw->dvi.state;
+ dw->dvi.state = new;
+}
+
+static
+pop_env(dw)
+ DviWidget dw;
+{
+ DviState *old;
+
+ old = dw->dvi.state;
+ dw->dvi.state = old->next;
+ XtFree ((char *) old);
+}
+
+static
+InitTypesetter (dw)
+ DviWidget dw;
+{
+ while (dw->dvi.state)
+ pop_env (dw);
+ push_env (dw);
+ FlushCharCache (dw);
+}
+
+#define DRAW_ARGS_MAX 128
+
+static
+ParseDrawFunction(dw, buf)
+DviWidget dw;
+char *buf;
+{
+ int v[DRAW_ARGS_MAX];
+ int i;
+ char *ptr;
+
+ v[0] = v[1] = v[2] = v[3] = 0;
+
+ if (buf[0] == '\0')
+ return;
+ ptr = buf+1;
+
+ for (i = 0; i < DRAW_ARGS_MAX; i++) {
+ if (sscanf(ptr, "%d", v + i) != 1)
+ break;
+ while (*ptr == ' ')
+ ptr++;
+ while (*ptr != '\0' && *ptr != ' ')
+ ptr++;
+ }
+
+ switch (buf[0]) {
+ case 'l': /* draw a line */
+ DrawLine(dw, v[0], v[1]);
+ break;
+ case 'c': /* circle */
+ DrawCircle(dw, v[0]);
+ break;
+ case 'C':
+ DrawFilledCircle(dw, v[0]);
+ break;
+ case 'e': /* ellipse */
+ DrawEllipse(dw, v[0], v[1]);
+ break;
+ case 'E':
+ DrawFilledEllipse(dw, v[0], v[1]);
+ break;
+ case 'a': /* arc */
+ DrawArc(dw, v[0], v[1], v[2], v[3]);
+ break;
+ case 'p':
+ DrawPolygon(dw, v, i);
+ break;
+ case 'P':
+ DrawFilledPolygon(dw, v, i);
+ break;
+ case '~': /* wiggly line */
+ DrawSpline(dw, v, i);
+ break;
+ case 't':
+ dw->dvi.line_thickness = v[0];
+ break;
+ case 'f':
+ if (i > 0 && v[0] >= 0 && v[0] <= DVI_FILL_MAX)
+ dw->dvi.fill = v[0];
+ break;
+ default:
+#if 0
+ warning("unknown drawing function %s", buf);
+#endif
+ break;
+ }
+
+ if (buf[0] == 'e') {
+ if (i > 0)
+ dw->dvi.state->x += v[0];
+ }
+ else {
+ while (--i >= 0) {
+ if (i & 1)
+ dw->dvi.state->y += v[i];
+ else
+ dw->dvi.state->x += v[i];
+ }
+ }
+}
+
+static
+ParseDeviceControl(dw) /* Parse the x commands */
+ DviWidget dw;
+{
+ char str[20], str1[50];
+ int c, n;
+ extern int LastPage, CurrentPage;
+
+ GetWord (dw, str, 20);
+ switch (str[0]) { /* crude for now */
+ case 'T': /* output device */
+ GetWord (dw, str, 20);
+ SetDevice (dw, str);
+ break;
+ case 'i': /* initialize */
+ InitTypesetter (dw);
+ break;
+ case 't': /* trailer */
+ break;
+ case 'p': /* pause -- can restart */
+ break;
+ case 's': /* stop */
+ StopSeen = 1;
+ return;
+ case 'r': /* resolution when prepared */
+ break;
+ case 'f': /* font used */
+ n = GetNumber (dw);
+ GetWord (dw, str, 20);
+ GetLine (dw, str1, 50);
+ SetFontPosition (dw, n, str, str1);
+ break;
+ case 'H': /* char height */
+ break;
+ case 'S': /* slant */
+ break;
+ }
+ while (DviGetC (dw, &c) != '\n') /* skip rest of input line */
+ if (c == EOF)
+ return;
+ return;
+}
+
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/xdit.bm b/contrib/groff/src/xditview/xdit.bm
new file mode 100644
index 000000000000..67b9c8ab56bc
--- /dev/null
+++ b/contrib/groff/src/xditview/xdit.bm
@@ -0,0 +1,14 @@
+#define xdit_width 32
+#define xdit_height 32
+static char xdit_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x03, 0x02, 0x00, 0x00, 0x02,
+ 0x8a, 0xa2, 0xfc, 0x03, 0x52, 0x14, 0x03, 0x04, 0x02, 0x80, 0x00, 0x08,
+ 0x52, 0x54, 0x00, 0x10, 0x8a, 0x22, 0x8f, 0x23, 0x02, 0x20, 0x06, 0x21,
+ 0x8a, 0x12, 0x8c, 0x40, 0x52, 0x14, 0x8c, 0x40, 0x02, 0x10, 0x58, 0x40,
+ 0x52, 0x14, 0x30, 0x40, 0x8a, 0x12, 0x30, 0x40, 0x02, 0x10, 0x70, 0x40,
+ 0x8a, 0x12, 0xc8, 0x40, 0x52, 0x24, 0xc4, 0xe0, 0x02, 0x20, 0x84, 0xe1,
+ 0x52, 0x54, 0xce, 0xf3, 0x8a, 0xa2, 0x00, 0xf8, 0x02, 0x00, 0x03, 0xfc,
+ 0x8a, 0x22, 0xfc, 0xf3, 0x52, 0x14, 0x00, 0xc2, 0x02, 0x00, 0x00, 0x02,
+ 0x52, 0x14, 0x45, 0x02, 0x8a, 0xa2, 0x28, 0x02, 0x02, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/contrib/groff/src/xditview/xdit_mask.bm b/contrib/groff/src/xditview/xdit_mask.bm
new file mode 100644
index 000000000000..f34a4f863f02
--- /dev/null
+++ b/contrib/groff/src/xditview/xdit_mask.bm
@@ -0,0 +1,14 @@
+#define xdit_mask_width 32
+#define xdit_mask_height 32
+static char xdit_mask_bits[] = {
+ 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07,
+ 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x1f,
+ 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xc7,
+ 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07,
+ 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/contrib/groff/src/xditview/xditview.c b/contrib/groff/src/xditview/xditview.c
new file mode 100644
index 000000000000..e836cf507e5a
--- /dev/null
+++ b/contrib/groff/src/xditview/xditview.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright 1991 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+/*
+ * xditview --
+ *
+ * Display ditroff output in an X window
+ */
+
+#ifndef SABER
+#ifndef lint
+static char rcsid[] = "$XConsortium: xditview.c,v 1.17 89/12/10 17:05:08 rws Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+
+#include <signal.h>
+
+#include "Dvi.h"
+
+#include "xdit.bm"
+#include "xdit_mask.bm"
+#include "stdio.h"
+
+extern FILE *popen();
+extern void exit();
+
+static String fallback_resources[] = {
+#include "GXditview-ad.h"
+ NULL
+};
+
+static struct app_resources {
+ char *print_command;
+ char *filename;
+} app_resources;
+
+#define offset(field) XtOffset(struct app_resources *, field)
+
+/* Application resources. */
+
+static XtResource resources[] = {
+ {"printCommand", "PrintCommand", XtRString, sizeof(char*),
+ offset(print_command), XtRString, NULL},
+ {"filename", "Filename", XtRString, sizeof(char*),
+ offset(filename), XtRString, NULL},
+};
+
+#undef offset
+
+/* Command line options table. Only resources are entered here...there is a
+ pass over the remaining options after XtParseCommand is let loose. */
+
+static XrmOptionDescRec options[] = {
+{"-page", "*dvi.pageNumber", XrmoptionSepArg, NULL},
+{"-backingStore", "*dvi.backingStore", XrmoptionSepArg, NULL},
+{"-resolution", "*dvi.resolution", XrmoptionSepArg, NULL},
+{"-printCommand", ".printCommand", XrmoptionSepArg, NULL},
+{"-filename", ".filename", XrmoptionSepArg, NULL},
+{"-noPolyText", "*dvi.noPolyText", XrmoptionNoArg, "TRUE"},
+};
+
+static char current_print_command[1024];
+
+static char current_file_name[1024];
+static FILE *current_file;
+
+/*
+ * Report the syntax for calling xditview.
+ */
+
+static
+Syntax(call)
+ char *call;
+{
+ (void) printf ("Usage: %s [-fg <color>] [-bg <color>]\n", call);
+ (void) printf (" [-bd <color>] [-bw <pixels>] [-help]\n");
+ (void) printf (" [-display displayname] [-geometry geom]\n");
+ (void) printf (" [-page <page-number>] [-backing <backing-store>]\n");
+ (void) printf (" [-resolution <res>] [-print <command>]\n");
+ (void) printf (" [-filename <file>] [filename]\n\n");
+ exit(1);
+}
+
+static void NewFile (), SetPageNumber ();
+static Widget toplevel, paned, viewport, dvi;
+static Widget page;
+static Widget simpleMenu;
+
+static void NextPage(), PreviousPage(), SelectPage(), OpenFile(), Quit();
+static void Print();
+
+static struct menuEntry {
+ char *name;
+ void (*function)();
+} menuEntries[] = {
+ "nextPage", NextPage,
+ "previousPage", PreviousPage,
+ "selectPage", SelectPage,
+ "print", Print,
+ "openFile", OpenFile,
+ "quit", Quit,
+};
+
+static void NextPageAction(), PreviousPageAction(), SelectPageAction();
+static void OpenFileAction(), QuitAction();
+static void AcceptAction(), CancelAction();
+static void PrintAction();
+static void RerasterizeAction();
+
+XtActionsRec xditview_actions[] = {
+ "NextPage", NextPageAction,
+ "PreviousPage", PreviousPageAction,
+ "SelectPage", SelectPageAction,
+ "Print", PrintAction,
+ "OpenFile", OpenFileAction,
+ "Rerasterize", RerasterizeAction,
+ "Quit", QuitAction,
+ "Accept", AcceptAction,
+ "Cancel", CancelAction,
+};
+
+#define MenuNextPage 0
+#define MenuPreviousPage 1
+#define MenuSelectPage 2
+#define MenuPrint 3
+#define MenuOpenFile 4
+#define MenuQuit 5
+
+static char pageLabel[256] = "Page <none>";
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *file_name = 0;
+ int i;
+ static Arg labelArgs[] = {
+ {XtNlabel, (XtArgVal) pageLabel},
+ };
+ XtAppContext xtcontext;
+ Arg topLevelArgs[2];
+ Widget entry;
+ Arg pageNumberArgs[1];
+ int page_number;
+
+ toplevel = XtAppInitialize(&xtcontext, "GXditview",
+ options, XtNumber (options),
+ &argc, argv, fallback_resources, NULL, 0);
+ if (argc > 2)
+ Syntax(argv[0]);
+
+ XtGetApplicationResources(toplevel, (XtPointer)&app_resources,
+ resources, XtNumber(resources),
+ NULL, (Cardinal) 0);
+ if (app_resources.print_command)
+ strcpy(current_print_command, app_resources.print_command);
+
+ XtAppAddActions(xtcontext, xditview_actions, XtNumber (xditview_actions));
+
+ XtSetArg (topLevelArgs[0], XtNiconPixmap,
+ XCreateBitmapFromData (XtDisplay (toplevel),
+ XtScreen(toplevel)->root,
+ xdit_bits, xdit_width, xdit_height));
+
+ XtSetArg (topLevelArgs[1], XtNiconMask,
+ XCreateBitmapFromData (XtDisplay (toplevel),
+ XtScreen(toplevel)->root,
+ xdit_mask_bits,
+ xdit_mask_width, xdit_mask_height));
+ XtSetValues (toplevel, topLevelArgs, 2);
+ if (argc > 1)
+ file_name = argv[1];
+
+ /*
+ * create the menu and insert the entries
+ */
+ simpleMenu = XtCreatePopupShell ("menu", simpleMenuWidgetClass, toplevel,
+ NULL, 0);
+ for (i = 0; i < XtNumber (menuEntries); i++) {
+ entry = XtCreateManagedWidget(menuEntries[i].name,
+ smeBSBObjectClass, simpleMenu,
+ NULL, (Cardinal) 0);
+ XtAddCallback(entry, XtNcallback, menuEntries[i].function, NULL);
+ }
+
+ paned = XtCreateManagedWidget("paned", panedWidgetClass, toplevel,
+ NULL, (Cardinal) 0);
+ viewport = XtCreateManagedWidget("viewport", viewportWidgetClass, paned,
+ NULL, (Cardinal) 0);
+ dvi = XtCreateManagedWidget ("dvi", dviWidgetClass, viewport, NULL, 0);
+ page = XtCreateManagedWidget ("label", labelWidgetClass, paned,
+ labelArgs, XtNumber (labelArgs));
+ XtSetArg (pageNumberArgs[0], XtNpageNumber, &page_number);
+ XtGetValues (dvi, pageNumberArgs, 1);
+ if (file_name)
+ NewFile (file_name);
+ /* NewFile modifies current_file_name, so do this here. */
+ if (app_resources.filename)
+ strcpy(current_file_name, app_resources.filename);
+ XtRealizeWidget (toplevel);
+ if (file_name)
+ SetPageNumber (page_number);
+ XtAppMainLoop(xtcontext);
+ return 0;
+}
+
+static void
+SetPageNumber (number)
+{
+ Arg arg[2];
+ int actual_number, last_page;
+
+ XtSetArg (arg[0], XtNpageNumber, number);
+ XtSetValues (dvi, arg, 1);
+ XtSetArg (arg[0], XtNpageNumber, &actual_number);
+ XtSetArg (arg[1], XtNlastPageNumber, &last_page);
+ XtGetValues (dvi, arg, 2);
+ if (actual_number == 0)
+ sprintf (pageLabel, "Page <none>");
+ else if (last_page > 0)
+ sprintf (pageLabel, "Page %d of %d", actual_number, last_page);
+ else
+ sprintf (pageLabel, "Page %d", actual_number);
+ XtSetArg (arg[0], XtNlabel, pageLabel);
+ XtSetValues (page, arg, 1);
+}
+
+static void
+SelectPageNumber (number_string)
+char *number_string;
+{
+ SetPageNumber (atoi(number_string));
+}
+
+static int hadFile = 0;
+
+static void
+NewFile (name)
+char *name;
+{
+ Arg arg[2];
+ char *n;
+ FILE *new_file;
+ Boolean seek = 0;
+
+ if (current_file) {
+ if (!strcmp (current_file_name, "-"))
+ ;
+ else if (current_file_name[0] == '|')
+ pclose (current_file);
+ else
+ fclose (current_file);
+ }
+ if (!strcmp (name, "-"))
+ new_file = stdin;
+ else if (name[0] == '|')
+ new_file = popen (name+1, "r");
+ else {
+ new_file = fopen (name, "r");
+ seek = 1;
+ }
+ if (!new_file) {
+ /* XXX display error message */
+ return;
+ }
+ XtSetArg (arg[0], XtNfile, new_file);
+ XtSetArg (arg[1], XtNseek, seek);
+ XtSetValues (dvi, arg, 2);
+ if (hadFile || name[0] != '-' || name[1] != '\0') {
+ XtSetArg (arg[0], XtNtitle, name);
+ if (name[0] != '/' && (n = strrchr (name, '/')))
+ n = n + 1;
+ else
+ n = name;
+ XtSetArg (arg[1], XtNiconName, n);
+ XtSetValues (toplevel, arg, 2);
+ }
+ hadFile = 1;
+ SelectPageNumber ("1");
+ strcpy (current_file_name, name);
+ current_file = new_file;
+}
+
+static char fileBuf[1024];
+
+ResetMenuEntry (entry)
+ Widget entry;
+{
+ Arg arg[1];
+
+ XtSetArg (arg[0], XtNpopupOnEntry, entry);
+ XtSetValues (XtParent(entry) , arg, (Cardinal) 1);
+}
+
+/*ARGSUSED*/
+
+static void
+NextPage (entry, name, data)
+ Widget entry;
+ caddr_t name, data;
+{
+ NextPageAction();
+ ResetMenuEntry (entry);
+}
+
+static void
+NextPageAction ()
+{
+ Arg args[1];
+ int number;
+
+ XtSetArg (args[0], XtNpageNumber, &number);
+ XtGetValues (dvi, args, 1);
+ SetPageNumber (number+1);
+}
+
+/*ARGSUSED*/
+
+static void
+PreviousPage (entry, name, data)
+ Widget entry;
+ caddr_t name, data;
+{
+ PreviousPageAction ();
+ ResetMenuEntry (entry);
+}
+
+static void
+PreviousPageAction ()
+{
+ Arg args[1];
+ int number;
+
+ XtSetArg (args[0], XtNpageNumber, &number);
+ XtGetValues (dvi, args, 1);
+ SetPageNumber (number-1);
+}
+
+/* ARGSUSED */
+
+static void
+SelectPage (entry, name, data)
+ Widget entry;
+ caddr_t name, data;
+{
+ SelectPageAction ();
+ ResetMenuEntry (entry);
+}
+
+static void
+SelectPageAction ()
+{
+ MakePrompt (toplevel, "Page number", SelectPageNumber, "");
+}
+
+
+static void
+DoPrint (name)
+ char *name;
+{
+ FILE *print_file;
+#ifdef SIGNALRETURNSINT
+ int (*handler)();
+#else
+ void (*handler)();
+#endif
+ /* Avoid dieing because of an invalid command. */
+ handler = signal(SIGPIPE, SIG_IGN);
+
+ print_file = popen(name, "w");
+ if (!print_file)
+ /* XXX print error message */
+ return;
+ DviSaveToFile(dvi, print_file);
+ pclose(print_file);
+ signal(SIGPIPE, handler);
+ strcpy(current_print_command, name);
+}
+
+static void
+RerasterizeAction()
+{
+ Arg args[1];
+ int number;
+
+ if (current_file_name[0] == 0) {
+ /* XXX display an error message */
+ return;
+ }
+ XtSetArg (args[0], XtNpageNumber, &number);
+ XtGetValues (dvi, args, 1);
+ NewFile(current_file_name);
+ SetPageNumber (number);
+}
+
+/* ARGSUSED */
+
+static void
+Print (entry, name, data)
+ Widget entry;
+ caddr_t name, data;
+{
+ PrintAction ();
+ ResetMenuEntry (entry);
+}
+
+static void
+PrintAction ()
+{
+ if (current_print_command[0])
+ strcpy (fileBuf, current_print_command);
+ else
+ fileBuf[0] = '\0';
+ MakePrompt (toplevel, "Print command:", DoPrint, fileBuf);
+}
+
+
+/* ARGSUSED */
+
+static void
+OpenFile (entry, name, data)
+ Widget entry;
+ caddr_t name, data;
+{
+ OpenFileAction ();
+ ResetMenuEntry (entry);
+}
+
+static void
+OpenFileAction ()
+{
+ if (current_file_name[0])
+ strcpy (fileBuf, current_file_name);
+ else
+ fileBuf[0] = '\0';
+ MakePrompt (toplevel, "File to open:", NewFile, fileBuf);
+}
+
+/* ARGSUSED */
+
+static void
+Quit (entry, closure, data)
+ Widget entry;
+ caddr_t closure, data;
+{
+ QuitAction ();
+}
+
+static void
+QuitAction ()
+{
+ exit (0);
+}
+
+Widget promptShell, promptDialog;
+void (*promptfunction)();
+
+/* ARGSUSED */
+static
+void CancelAction (widget, event, params, num_params)
+ Widget widget;
+ XEvent *event;
+ String *params;
+ Cardinal *num_params;
+{
+ if (promptShell) {
+ XtSetKeyboardFocus(toplevel, (Widget) None);
+ XtDestroyWidget(promptShell);
+ promptShell = (Widget) 0;
+ }
+}
+
+static
+void AcceptAction (widget, event, params, num_params)
+ Widget widget;
+ XEvent *event;
+ String *params;
+ Cardinal *num_params;
+{
+ (*promptfunction)(XawDialogGetValueString(promptDialog));
+ CancelAction (widget, event, params, num_params);
+}
+
+MakePrompt(centerw, prompt, func, def)
+Widget centerw;
+char *prompt;
+void (*func)();
+char *def;
+{
+ static Arg dialogArgs[] = {
+ {XtNlabel, 0},
+ {XtNvalue, 0},
+ };
+ Arg valueArgs[1];
+ Arg centerArgs[2];
+ Position source_x, source_y;
+ Position dest_x, dest_y;
+ Dimension center_width, center_height;
+ Dimension prompt_width, prompt_height;
+ Widget valueWidget;
+
+ CancelAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
+ promptShell = XtCreatePopupShell ("promptShell", transientShellWidgetClass,
+ toplevel, NULL, (Cardinal) 0);
+ dialogArgs[0].value = (XtArgVal)prompt;
+ dialogArgs[1].value = (XtArgVal)def;
+ promptDialog = XtCreateManagedWidget( "promptDialog", dialogWidgetClass,
+ promptShell, dialogArgs, XtNumber (dialogArgs));
+ XawDialogAddButton(promptDialog, "accept", NULL, (caddr_t) 0);
+ XawDialogAddButton(promptDialog, "cancel", NULL, (caddr_t) 0);
+ valueWidget = XtNameToWidget (promptDialog, "value");
+ if (valueWidget) {
+ XtSetArg (valueArgs[0], XtNresizable, TRUE);
+ XtSetValues (valueWidget, valueArgs, 1);
+ /*
+ * as resizable isn't set until just above, the
+ * default value will be displayed incorrectly.
+ * rectify the situation by resetting the values
+ */
+ XtSetValues (promptDialog, dialogArgs, XtNumber (dialogArgs));
+ }
+ XtSetKeyboardFocus (promptDialog, valueWidget);
+ XtSetKeyboardFocus (toplevel, valueWidget);
+ XtRealizeWidget (promptShell);
+ /*
+ * place the widget in the center of the "parent"
+ */
+ XtSetArg (centerArgs[0], XtNwidth, &center_width);
+ XtSetArg (centerArgs[1], XtNheight, &center_height);
+ XtGetValues (centerw, centerArgs, 2);
+ XtSetArg (centerArgs[0], XtNwidth, &prompt_width);
+ XtSetArg (centerArgs[1], XtNheight, &prompt_height);
+ XtGetValues (promptShell, centerArgs, 2);
+ source_x = (center_width - prompt_width) / 2;
+ source_y = (center_height - prompt_height) / 3;
+ XtTranslateCoords (centerw, source_x, source_y, &dest_x, &dest_y);
+ XtSetArg (centerArgs[0], XtNx, dest_x);
+ XtSetArg (centerArgs[1], XtNy, dest_y);
+ XtSetValues (promptShell, centerArgs, 2);
+ XtMapWidget(promptShell);
+ promptfunction = func;
+}
+
+/* For DviChar.c */
+
+char *xmalloc(n)
+ int n;
+{
+ return XtMalloc(n);
+}
+
+/*
+Local Variables:
+c-indent-level: 4
+c-continued-statement-offset: 4
+c-brace-offset: -4
+c-argdecl-indent: 4
+c-label-offset: -4
+c-tab-always-indent: nil
+End:
+*/
diff --git a/contrib/groff/src/xditview/xtotroff.c b/contrib/groff/src/xditview/xtotroff.c
new file mode 100644
index 000000000000..97cac0a5893f
--- /dev/null
+++ b/contrib/groff/src/xditview/xtotroff.c
@@ -0,0 +1,311 @@
+/*
+ * xtotroff
+ *
+ * convert X font metrics into troff font metrics
+ */
+
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "XFontName.h"
+#include "DviChar.h"
+
+#ifdef X_NOT_STDC_ENV
+char *malloc();
+#else
+#include <stdlib.h>
+#endif
+
+#define charWidth(fi,c) ((fi)->per_char[(c) - (fi)->min_char_or_byte2].width)
+#define charHeight(fi,c) ((fi)->per_char[(c) - (fi)->min_char_or_byte2].ascent)
+#define charDepth(fi,c) ((fi)->per_char[(c) - (fi)->min_char_or_byte2].descent)
+#define charLBearing(fi,c) ((fi)->per_char[(c) - (fi)->min_char_or_byte2].lbearing)
+#define charRBearing(fi,c) ((fi)->per_char[(c) - (fi)->min_char_or_byte2].rbearing)
+
+Display *dpy;
+int groff_flag = 0;
+unsigned resolution = 75;
+unsigned point_size = 10;
+
+int charExists (fi, c)
+ XFontStruct *fi;
+ int c;
+{
+ XCharStruct *p;
+
+ if (c < fi->min_char_or_byte2 || c > fi->max_char_or_byte2)
+ return 0;
+ p = fi->per_char + (c - fi->min_char_or_byte2);
+ return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
+ || p->ascent != 0 || p->descent != 0 || p->attributes != 0);
+}
+
+/* Canonicalize the font name by replacing scalable parts by *s. */
+
+CanonicalizeFontName (font_name, canon_font_name)
+ char *font_name, *canon_font_name;
+{
+ unsigned int attributes;
+ XFontName parsed;
+
+ if (!XParseFontName(font_name, &parsed, &attributes)) {
+ fprintf (stderr, "not a standard name: %s\n", font_name);
+ return 0;
+ }
+
+ attributes &= ~(FontNamePixelSize|FontNameAverageWidth
+ |FontNamePointSize
+ |FontNameResolutionX|FontNameResolutionY);
+ XFormatFontName(&parsed, attributes, canon_font_name);
+ return 1;
+}
+
+int FontNamesAmbiguous(font_name, names, count)
+char *font_name;
+char **names;
+int count;
+{
+ char name1[2048], name2[2048];
+ int i;
+
+ if (count == 1)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ if (!CanonicalizeFontName(names[i], i == 0 ? name1 : name2)) {
+ fprintf(stderr, "bad font name: %s\n", names[i]);
+ return 1;
+ }
+ if (i > 0 && strcmp(name1, name2) != 0) {
+ fprintf(stderr, "ambiguous font name: %s\n", font_name);
+ fprintf(stderr, " matches %s\n", names[0]);
+ fprintf(stderr, " and %s\n", names[i]);
+ return 1;
+ }
+
+ }
+ return 0;
+}
+
+MapFont (font_name, troff_name)
+ char *font_name;
+ char *troff_name;
+{
+ XFontStruct *fi;
+ int count;
+ char **names;
+ FILE *out;
+ int c;
+ unsigned int attributes;
+ XFontName parsed;
+ int j, k;
+ DviCharNameMap *char_map;
+ char encoding[256];
+ char *s;
+ int wid;
+ char name_string[2048];
+
+ if (!XParseFontName(font_name, &parsed, &attributes)) {
+ fprintf (stderr, "not a standard name: %s\n", font_name);
+ return 0;
+ }
+
+ attributes &= ~(FontNamePixelSize|FontNameAverageWidth);
+ attributes |= FontNameResolutionX;
+ attributes |= FontNameResolutionY;
+ attributes |= FontNamePointSize;
+ parsed.ResolutionX = resolution;
+ parsed.ResolutionY = resolution;
+ parsed.PointSize = point_size*10;
+ XFormatFontName(&parsed, attributes, name_string);
+
+ names = XListFonts (dpy, name_string, 100000, &count);
+ if (count < 1) {
+ fprintf (stderr, "bad font name: %s\n", font_name);
+ return 0;
+ }
+
+ if (FontNamesAmbiguous(font_name, names, count))
+ return 0;
+
+ XParseFontName(names[0], &parsed, &attributes);
+ sprintf (encoding, "%s-%s", parsed.CharSetRegistry,
+ parsed.CharSetEncoding);
+ for (s = encoding; *s; s++)
+ if (isupper (*s))
+ *s = tolower (*s);
+ char_map = DviFindMap (encoding);
+ if (!char_map) {
+ fprintf (stderr, "not a standard encoding: %s\n", encoding);
+ return 0;
+ }
+
+ fi = XLoadQueryFont (dpy, names[0]);
+ if (!fi) {
+ fprintf (stderr, "font does not exist: %s\n", names[0]);
+ return 0;
+ }
+
+ printf ("%s -> %s\n", names[0], troff_name);
+
+ { /* Avoid race while opening file */
+ int fd;
+ (void) unlink (troff_name);
+ fd = open (troff_name, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ out = fdopen (fd, "w");
+ }
+
+ if (!out) {
+ perror (troff_name);
+ return 0;
+ }
+ fprintf (out, "name %s\n", troff_name);
+ if (!strcmp (char_map->encoding, "adobe-fontspecific"))
+ fprintf (out, "special\n");
+ if (charExists (fi, ' ')) {
+ int w = charWidth (fi, ' ');
+ if (w > 0)
+ fprintf (out, "spacewidth %d\n", w);
+ }
+ fprintf (out, "charset\n");
+ for (c = fi->min_char_or_byte2; c <= fi->max_char_or_byte2; c++) {
+ char *name = DviCharName (char_map,c,0);
+ if (charExists (fi, c) && (groff_flag || name)) {
+
+ wid = charWidth (fi, c);
+
+ fprintf (out, "%s\t%d",
+ name ? name : "---",
+ wid);
+ if (groff_flag) {
+ int param[5];
+ param[0] = charHeight (fi, c);
+ param[1] = charDepth (fi, c);
+ param[2] = 0 /* charRBearing (fi, c) - wid */;
+ param[3] = 0 /* charLBearing (fi, c) */;
+ param[4] = 0; /* XXX */
+ for (j = 0; j < 5; j++)
+ if (param[j] < 0)
+ param[j] = 0;
+ for (j = 4; j >= 0; j--)
+ if (param[j] != 0)
+ break;
+ for (k = 0; k <= j; k++)
+ fprintf (out, ",%d", param[k]);
+ }
+ fprintf (out, "\t0\t0%o\n", c);
+
+ if (name) {
+ for (k = 1; DviCharName(char_map,c,k); k++) {
+ fprintf (out, "%s\t\"\n",
+ DviCharName (char_map,c,k));
+ }
+ }
+ }
+ }
+ XUnloadFont (dpy, fi->fid);
+ fclose (out);
+ return 1;
+}
+
+static usage(prog)
+ char *prog;
+{
+ fprintf (stderr,
+ "usage: %s [-g] [-r resolution] [-s pointsize] FontMap\n",
+ prog);
+ exit (1);
+}
+
+
+/* For use by DviChar.c */
+
+char *xmalloc(n)
+int n;
+{
+ char *p = malloc(n);
+ if (!p) {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+ return p;
+}
+
+main (argc, argv)
+ char **argv;
+{
+ char troff_name[1024];
+ char font_name[1024];
+ char line[1024];
+ char *a, *b, c;
+ int position;
+ FILE *map;
+ int opt;
+ extern int optind;
+ extern char *optarg;
+
+ while ((opt = getopt(argc, argv, "gr:s:")) != EOF) {
+ switch (opt) {
+ case 'g':
+ groff_flag = 1;
+ break;
+ case 'r':
+ sscanf(optarg, "%u", &resolution);
+ break;
+ case 's':
+ sscanf(optarg, "%u", &point_size);
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+ if (argc - optind != 1)
+ usage(argv[0]);
+
+ dpy = XOpenDisplay (0);
+ if (!dpy) {
+ fprintf (stderr, "Can't connect to the X server.\n");
+ fprintf (stderr, "Make sure the DISPLAY environment variable is set correctly.\n");
+ exit (1);
+ }
+ position = 1;
+
+ map = fopen (argv[optind], "r");
+ if (map == NULL) {
+ perror (argv[optind]);
+ exit (1);
+ }
+
+ while (fgets (line, sizeof (line), map)) {
+ for (a=line,b=troff_name; *a; a++,b++) {
+ c = (*b = *a);
+ if (c == ' ' || c == '\t')
+ break;
+ }
+ *b = '\0';
+ while (*a && (*a == ' ' || *a == '\t'))
+ ++a;
+ for (b=font_name; *a; a++,b++)
+ if ((*b = *a) == '\n')
+ break;
+ *b = '\0';
+ if (!MapFont (font_name, troff_name))
+ exit (1);
+ ++position;
+ }
+ exit (0);
+}
+
+/*
+Local Variables:
+c-indent-level: 8
+c-continued-statement-offset: 8
+c-brace-offset: -8
+c-argdecl-indent: 8
+c-label-offset: -8
+c-tab-always-indent: nil
+End:
+*/