summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Clark <jjc@jclark.com>1991-06-02 04:20:34 -0500
committerJames Clark <jjc@jclark.com>1991-06-02 04:20:34 -0500
commit351da0dcdf702cf243d26ffa998961bce2aa8653 (patch)
treed81b8841a9e7c22fbeb6c1e266aadbb5bbdb96e6
groff before CVS: release 1.021.02
[[This repository was stitched together in December 2013 from an incomplete set of early groff release tarballs - versions 1.02 to 1.15 - and the CVS history since shortly before the 1.16 release by Werner Lemberg. CVS commits before Wed Apr 11 22:45:11 2007 lacked commitids; coalescence of those into changesets may not be perfect. The lift was done by Eric S. Raymond using cvs-fast-export and a pre-3.0 version of reposurgeon. Comments double-bracketed like this one were added during the conversion to replace empty log entries.]]
-rw-r--r--BUG-REPORT53
-rw-r--r--CHANGES380
-rw-r--r--ChangeLog2625
-rw-r--r--INSTALL272
-rw-r--r--LICENSE249
-rw-r--r--Makefile361
-rw-r--r--Makefile.bd119
-rw-r--r--PROBLEMS248
-rw-r--r--PROJECTS18
-rw-r--r--README38
-rw-r--r--README.bd15
-rw-r--r--VERSION1
-rw-r--r--doc/Makefile53
-rw-r--r--doc/chars.tr503
-rw-r--r--doc/meintro.me2247
-rw-r--r--doc/meref.me2194
-rw-r--r--driver/Makefile54
-rw-r--r--driver/driver.h35
-rw-r--r--driver/input.c459
-rw-r--r--driver/printer.c169
-rw-r--r--driver/printer.h65
-rw-r--r--dvi/Makefile81
-rw-r--r--dvi/devdvi/B347
-rw-r--r--dvi/devdvi/BI345
-rw-r--r--dvi/devdvi/CW157
-rwxr-xr-xdvi/devdvi/CompileFonts15
-rw-r--r--dvi/devdvi/DESC9
-rw-r--r--dvi/devdvi/EX144
-rw-r--r--dvi/devdvi/FontMakefile74
-rw-r--r--dvi/devdvi/H302
-rw-r--r--dvi/devdvi/HB302
-rw-r--r--dvi/devdvi/HI303
-rw-r--r--dvi/devdvi/I346
-rw-r--r--dvi/devdvi/MI136
-rw-r--r--dvi/devdvi/Makefile39
-rw-r--r--dvi/devdvi/R430
-rw-r--r--dvi/devdvi/S152
-rw-r--r--dvi/devdvi/eqnchar35
-rw-r--r--dvi/devdvi/texb.map127
-rw-r--r--dvi/devdvi/texex.map100
-rw-r--r--dvi/devdvi/texi.map127
-rw-r--r--dvi/devdvi/texmi.map29
-rw-r--r--dvi/devdvi/texr.map127
-rw-r--r--dvi/devdvi/texsy.map100
-rw-r--r--dvi/devdvi/textt.map126
-rw-r--r--dvi/dvi.c895
-rw-r--r--dvi/tfmtodit.c845
-rw-r--r--dvi/tmac.dvi38
-rw-r--r--eqn/Makefile91
-rw-r--r--eqn/NOTES25
-rw-r--r--eqn/TODO39
-rw-r--r--eqn/box.c565
-rw-r--r--eqn/box.h277
-rw-r--r--eqn/delim.c356
-rw-r--r--eqn/eqn.h49
-rw-r--r--eqn/eqn.tab.c1219
-rw-r--r--eqn/eqn.tab.h69
-rw-r--r--eqn/eqn.y331
-rw-r--r--eqn/lex.c1153
-rw-r--r--eqn/limit.c195
-rw-r--r--eqn/list.c236
-rw-r--r--eqn/main.c276
-rw-r--r--eqn/mark.c121
-rw-r--r--eqn/other.c601
-rw-r--r--eqn/over.c196
-rw-r--r--eqn/pbox.h137
-rw-r--r--eqn/pile.c293
-rw-r--r--eqn/script.c221
-rw-r--r--eqn/special.c115
-rw-r--r--eqn/sqrt.c179
-rw-r--r--eqn/text.c526
-rw-r--r--etc/Makefile74
-rw-r--r--etc/addftinfo.c192
-rw-r--r--etc/grog.pl128
-rw-r--r--etc/grog.sh70
-rw-r--r--etc/guess.c490
-rw-r--r--etc/guess.h44
-rw-r--r--etc/soelim.c277
-rw-r--r--groff.c767
-rw-r--r--groff.sh354
-rw-r--r--lib/Makefile100
-rw-r--r--lib/assert.c37
-rw-r--r--lib/assert.h41
-rw-r--r--lib/change_lf.c37
-rw-r--r--lib/cmap.c55
-rw-r--r--lib/cmap.h56
-rw-r--r--lib/cset.c101
-rw-r--r--lib/cset.h71
-rw-r--r--lib/errarg.c114
-rw-r--r--lib/errarg.h46
-rw-r--r--lib/error.c137
-rw-r--r--lib/error.h58
-rw-r--r--lib/fatal.c27
-rw-r--r--lib/filename.c1
-rw-r--r--lib/fmod.c28
-rw-r--r--lib/font.c836
-rw-r--r--lib/font.h110
-rw-r--r--lib/fontfile.c129
-rw-r--r--lib/getpagesize.h25
-rw-r--r--lib/iftoa.c65
-rw-r--r--lib/itoa.c43
-rw-r--r--lib/lf.c61
-rw-r--r--lib/lib.h66
-rw-r--r--lib/lineno.c1
-rw-r--r--lib/malloc.c893
-rw-r--r--lib/matherr.c44
-rw-r--r--lib/nametoindex.c117
-rw-r--r--lib/new.c43
-rw-r--r--lib/prime.c26
-rw-r--r--lib/progname.c1
-rw-r--r--lib/ptable.c49
-rw-r--r--lib/ptable.h159
-rw-r--r--lib/strerror.c35
-rw-r--r--lib/string.c310
-rw-r--r--lib/stringclass.h174
-rw-r--r--lib/strsave.c31
-rw-r--r--lib/strtol.c115
-rw-r--r--macros/Makefile48
-rw-r--r--macros/TODO30
-rw-r--r--macros/fixmacros.sed6
-rw-r--r--macros/mm.diff70
-rw-r--r--macros/tmac.an320
-rw-r--r--macros/tmac.andoc11
-rw-r--r--macros/tmac.doc1825
-rw-r--r--macros/tmac.e1559
-rw-r--r--macros/tmac.pic10
-rw-r--r--macros/tmac.s1808
-rw-r--r--man/Makefile122
-rw-r--r--man/addftinfo.man85
-rw-r--r--man/afmtodit.man182
-rw-r--r--man/geqn.man774
-rw-r--r--man/gindxbib.man188
-rw-r--r--man/glookbib.man60
-rw-r--r--man/gpic.man684
-rw-r--r--man/grefer.man1272
-rw-r--r--man/grodvi.man148
-rw-r--r--man/groff.man283
-rw-r--r--man/groff_font.man342
-rw-r--r--man/groff_me.man271
-rw-r--r--man/groff_ms.man209
-rw-r--r--man/groff_out.man215
-rw-r--r--man/grog.man54
-rw-r--r--man/grops.man704
-rw-r--r--man/grotty.man179
-rw-r--r--man/gsoelim.man42
-rw-r--r--man/gtbl.man119
-rw-r--r--man/gtroff.man1832
-rw-r--r--man/lkbib.man84
-rwxr-xr-xman/mdate.sh34
-rw-r--r--man/pfbtops.man27
-rw-r--r--man/psbb.man26
-rw-r--r--man/tfmtodit.man144
-rw-r--r--pic/Makefile82
-rw-r--r--pic/TODO26
-rw-r--r--pic/common.c489
-rw-r--r--pic/common.h70
-rw-r--r--pic/lex.c1894
-rw-r--r--pic/main.c581
-rw-r--r--pic/object.c1807
-rw-r--r--pic/object.h218
-rw-r--r--pic/output.c4
-rw-r--r--pic/output.h80
-rw-r--r--pic/pic.h94
-rw-r--r--pic/pic.tab.c3205
-rw-r--r--pic/pic.tab.h129
-rw-r--r--pic/pic.y1688
-rw-r--r--pic/position.h47
-rw-r--r--pic/tex.c411
-rw-r--r--pic/text.h28
-rw-r--r--pic/troff.c524
-rw-r--r--ps/Makefile115
-rw-r--r--ps/TODO19
-rw-r--r--ps/devps/AB408
-rw-r--r--ps/devps/ABI409
-rw-r--r--ps/devps/AI409
-rw-r--r--ps/devps/AR408
-rw-r--r--ps/devps/BMB408
-rw-r--r--ps/devps/BMBI409
-rw-r--r--ps/devps/BMI409
-rw-r--r--ps/devps/BMR408
-rw-r--r--ps/devps/CB286
-rw-r--r--ps/devps/CBI287
-rw-r--r--ps/devps/CI287
-rw-r--r--ps/devps/CR286
-rw-r--r--ps/devps/DESC-A411
-rw-r--r--ps/devps/DESC-letter11
-rw-r--r--ps/devps/FontMakefile183
-rw-r--r--ps/devps/HB410
-rw-r--r--ps/devps/HBI409
-rw-r--r--ps/devps/HI409
-rw-r--r--ps/devps/HNB410
-rw-r--r--ps/devps/HNBI409
-rw-r--r--ps/devps/HNI409
-rw-r--r--ps/devps/HNR412
-rw-r--r--ps/devps/HR412
-rw-r--r--ps/devps/Makefile66
-rw-r--r--ps/devps/NB412
-rw-r--r--ps/devps/NBI416
-rw-r--r--ps/devps/NI415
-rw-r--r--ps/devps/NR414
-rw-r--r--ps/devps/PB416
-rw-r--r--ps/devps/PBI418
-rw-r--r--ps/devps/PI420
-rw-r--r--ps/devps/PR423
-rw-r--r--ps/devps/S226
-rw-r--r--ps/devps/SS194
-rw-r--r--ps/devps/TB427
-rw-r--r--ps/devps/TBI414
-rw-r--r--ps/devps/TI432
-rw-r--r--ps/devps/TR418
-rw-r--r--ps/devps/ZCMI420
-rw-r--r--ps/devps/ZD193
-rw-r--r--ps/devps/ZDR193
-rwxr-xr-xps/devps/afmtodit310
-rw-r--r--ps/devps/dingbatsmap2
-rw-r--r--ps/devps/download5
-rw-r--r--ps/devps/eqnchar13
-rw-r--r--ps/devps/lgreekmap25
-rw-r--r--ps/devps/prologue211
-rw-r--r--ps/devps/symbol.diff65
-rw-r--r--ps/devps/symbolchars3
-rw-r--r--ps/devps/symbolsl.afm203
-rw-r--r--ps/devps/symbolsl.ps41
-rw-r--r--ps/devps/text.enc230
-rw-r--r--ps/devps/textmap494
-rw-r--r--ps/devps/zapfdr.ps225
-rw-r--r--ps/pfbtops.c94
-rw-r--r--ps/ps.c1456
-rw-r--r--ps/ps.h127
-rw-r--r--ps/psbb.c164
-rw-r--r--ps/psfig.diff106
-rw-r--r--ps/psrm.c1086
-rw-r--r--ps/tmac.ps115
-rw-r--r--ps/tmac.psatk61
-rw-r--r--ps/tmac.psfig86
-rw-r--r--refer/Makefile165
-rw-r--r--refer/TODO108
-rw-r--r--refer/command.c806
-rw-r--r--refer/command.h36
-rw-r--r--refer/common.c38
-rw-r--r--refer/eign133
-rw-r--r--refer/genlimits.c44
-rw-r--r--refer/index.c637
-rw-r--r--refer/index.h44
-rw-r--r--refer/indxbib.c699
-rw-r--r--refer/label.tab.c1935
-rw-r--r--refer/label.y1174
-rw-r--r--refer/linear.c493
-rw-r--r--refer/lkbib.c114
-rw-r--r--refer/lookbib.c126
-rw-r--r--refer/ref.c1141
-rw-r--r--refer/ref.h120
-rw-r--r--refer/refer.c1228
-rw-r--r--refer/refer.h79
-rw-r--r--refer/refid.h35
-rw-r--r--refer/search.c129
-rw-r--r--refer/search.h97
-rw-r--r--refer/token.c363
-rw-r--r--refer/token.h74
-rw-r--r--stringify5
-rw-r--r--tbl/Makefile64
-rw-r--r--tbl/TODO16
-rw-r--r--tbl/main.c1451
-rw-r--r--tbl/table.c2733
-rw-r--r--tbl/table.h148
-rw-r--r--troff/Makefile114
-rw-r--r--troff/TODO127
-rw-r--r--troff/charinfo.h158
-rw-r--r--troff/column.c732
-rw-r--r--troff/dictionary.c210
-rw-r--r--troff/dictionary.h92
-rw-r--r--troff/div.c1113
-rw-r--r--troff/div.h140
-rw-r--r--troff/env.c2947
-rw-r--r--troff/env.h309
-rw-r--r--troff/hvunits.h340
-rw-r--r--troff/hyphen4447
-rw-r--r--troff/input.c5898
-rw-r--r--troff/node.c4742
-rw-r--r--troff/node.h522
-rw-r--r--troff/number.c663
-rw-r--r--troff/reg.c458
-rw-r--r--troff/reg.h73
-rw-r--r--troff/request.h79
-rw-r--r--troff/symbol.c140
-rw-r--r--troff/symbol.h76
-rw-r--r--troff/token.h218
-rw-r--r--troff/troff.h91
-rw-r--r--tty/Makefile83
-rw-r--r--tty/TODO6
-rw-r--r--tty/devascii/DESC.proto7
-rw-r--r--tty/devascii/Makefile63
-rw-r--r--tty/devascii/R.proto133
-rw-r--r--tty/devlatin1/DESC.proto7
-rw-r--r--tty/devlatin1/Makefile72
-rw-r--r--tty/devlatin1/R.proto320
-rw-r--r--tty/tmac.tty37
-rw-r--r--tty/tty.c361
-rw-r--r--xditview/ChangeLog75
-rw-r--r--xditview/DESC8
-rw-r--r--xditview/Dvi.c537
-rw-r--r--xditview/Dvi.h46
-rw-r--r--xditview/DviChar.c664
-rw-r--r--xditview/DviChar.h37
-rw-r--r--xditview/DviP.h231
-rw-r--r--xditview/FontMap.X10017
-rw-r--r--xditview/FontMap.X7517
-rw-r--r--xditview/GXditview.ad41
-rw-r--r--xditview/INSTALL22
-rw-r--r--xditview/Imakefile64
-rw-r--r--xditview/Makefile517
-rw-r--r--xditview/Menu.h46
-rw-r--r--xditview/README13
-rw-r--r--xditview/TODO12
-rw-r--r--xditview/XFontName.c256
-rw-r--r--xditview/XFontName.h45
-rw-r--r--xditview/device.c578
-rw-r--r--xditview/device.h21
-rw-r--r--xditview/draw.c742
-rw-r--r--xditview/eqnchar13
-rw-r--r--xditview/font.c429
-rw-r--r--xditview/gxditview.man175
-rw-r--r--xditview/lex.c104
-rw-r--r--xditview/page.c86
-rw-r--r--xditview/parse.c335
-rw-r--r--xditview/path.h1
-rw-r--r--xditview/tmac.X34
-rw-r--r--xditview/xdit.bm14
-rw-r--r--xditview/xdit_mask.bm14
-rw-r--r--xditview/xditview.c533
-rw-r--r--xditview/xtotroff.c207
331 files changed, 119360 insertions, 0 deletions
diff --git a/BUG-REPORT b/BUG-REPORT
new file mode 100644
index 000000000..8448d8a95
--- /dev/null
+++ b/BUG-REPORT
@@ -0,0 +1,53 @@
+ Groff Bug Report
+
+Please read the PROBLEMS file before sending in a bug report.
+
+Please fill in all fields, even if you think they are not relevant.
+
+Please report separate bugs separately.
+
+Send the completed form either to bug-groff@prep.ai.mit.edu or
+directly to me: jjc@jclark.uucp or jjc%jclark@mcsun.eu.net (if neither
+of those works for you, jjc@ai.mit.edu should forward.) Messages sent
+to bug-groff can sometimes take several days to reach me.
+
+GROFF VERSION:
+[The version of groff you are using. For example, `1.00']
+
+MACHINE:
+[The machine you are using. For example, `Sun 4/370']
+
+OS:
+[The operating system you are using. For example, `SunOS 4.0.3']
+
+COMPILER:
+[The compiler you are used to compile groff. For example, `g++ 1.37.1']
+
+INPUT FILES: [Include all the files necessary to reproduce the problem
+that are not part of the standard groff distribution. This includes
+font description files, DESC files and macro files (with the exception
+of the -ms and -mm macros: I have them). Send them as as a shell
+archive or as a uuencoded, compressed tar file.
+
+It's easier for me if you can provide an example that doesn't depend
+on any macro package, but obviously if you're reporting a problem with
+a macro package that won't be possible. Also a short example is more
+convenient than a long one, but don't worry if you can't find a short
+example. Don't say something like ``any file that X'': always send a
+definite example.]
+
+COMMAND LINE:
+[The command line that I should run in order to observe the bug. For
+example, `gtroff -Tps bug.tr']
+
+DESCRIPTION OF INCORRECT BEHAVIOUR:
+[What goes wrong when that command line is run? For example, `gtroff
+gets a segmentation fault', or `The output looks bad because the bar
+over the x is too long and is too far over to the left.' If you get
+an error message, include it here without modification: don't edit it
+to make it more readable.]
+
+SUGGESTED FIX [optional]:
+[If you can suggest a fix for the problem, include a context diff
+here. But don't delay sending in a bug report in the hope of finding
+a fix. Guesses about the cause of the bug are not helpful.]
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 000000000..a415584ce
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,380 @@
+This file describes recent user-visible changes in groff. Bug fixes
+are not described. There are more details in the man pages.
+
+VERSION 1.02
+============
+
+There is an implementation of refer and associated programs. groff -R
+preprocesses with grefer; no mechanism is provided for passing
+arguments to grefer because most grefer options have equivalent
+commands which can be included in the file. grog also supports refer.
+
+There is an alternative perl implementation of the grog script.
+
+The code field in lines in the charset section of font description
+files is now allowed to contain an arbitrary integer (previously it
+was required to lie between 0 and 255). Currently grops and grodvi
+use only the low order 8 bits of the value. Grodvi will use the
+complete value; however, this is unlikely to be useful with
+traditional TeX tools (.tfm files only allow 8 bit character codes.)
+
+Left and right double quotes can be obtained with \(lq and \(rq
+respectively.
+
+There is a new program called pfbtops which translates PostScript
+fonts in pfb format to ASCII.
+
+A slightly modified version of the Berkeley tmac.doc is included.
+
+Troff
+-----
+
+In long escape names the closing ] is now required to be at the same
+input level as the opening [.
+
+The \A'S' escape sequence returns 1 or 0 according as S is or is not
+suitable for use as a name.
+
+\~ produces an unbreakable space that can be stretched when the line
+is adjusted.
+
+The `mso' request is like the `so' request except that it searches for
+the file in the same directories in which tmac.X is searched for when
+the -mX option is given.
+
+The escape sequemce `\R' is similar to the `nr' request.
+
+Eqn
+---
+
+A new `special' primitive allows you to add new types of unary
+constructs by writing a troff macro.
+
+Pic
+---
+
+The implementation no longer uses gperf.
+
+Grops
+-----
+
+The compile-time -DBROKEN_SPOOLER option has been replaced by a
+BROKEN_SPOOLER_FLAGS option. This allows more precise control over
+how grops should workaround broken spoolers and previewers. There is
+a new -b option that can change this at run-time.
+
+Grops now generates PostScript that complies with version 3.0 of the
+Document Structuring Convention.
+
+The resource management component of grops (the part that deals with
+imported documents and downloadable fonts) has been rewritten and now
+supports version 3.0 of the Document Structuring Conventions. The
+%%DocumentFonts comment is no longer supported; you must use the
+%%Document{Needed,Supplied}{Fonts,Resources} comments instead
+(or as well.)
+
+tmac.psatk contains some macros that support the mechanism used by the
+Andrew Toolkit for including PostScript graphics in troff documents.
+
+Xditview
+--------
+
+Parts of xditview have been rewritten so that it can be used with the
+output of gtroff -Tps. groff -TXps will run gtroff -Tps with
+gxditview.
+
+There is a new menu entry `Print' which brings up a dialog box for
+specifying a command with which the file being previewed should be
+printed.
+
+Xditview now uses imake.
+
+VERSION 1.01
+============
+
+The groff command now understands the gtroff `-a' and `-i' options.
+
+With the `m' and `n' scale indicators, the scale factor is rounded
+horizontally before being applied. This makes (almost) no difference
+for devices with `hor' equal to 1, but it makes groff with -Tascii or
+-Tlatin1 behave more like nroff in its treatment of these scale
+indicators. Accordingly tmac.tty now calls the `nroff' request so
+that the `n' condition will be true.
+
+The device-specific macros (tmac.ps, tmac.dvi, tmac.tty and tmac.X)
+have been made to work at least somewhat with -C. In particular the
+special characters defined by these macros now work with -C.
+
+groff -Tdvi -p will now pass pic the -x flag; this will enable filling
+of arrowheads and boxes, provided that your dvi driver supports the
+latest version of the tpic specials.
+
+Eqn
+---
+
+There is a new `-N' option that tells eqn not to allow newlines in
+delimiters. This allows eqn to recover better from missing closing
+delimiters. The groff command will pass on a `-N' option to eqn.
+
+Grops
+-----
+
+You can now use psfig with grops. See the file ps/psfig.diff. I do
+not recommend using psfig for new documents.
+
+The command \X'ps: file F' is similar to \X'ps: exec ...' except that
+the PostScript code is read from the file F instead of being contained
+within the \X command. This was added to support psfig.
+
+Grodvi
+------
+
+There are font files HB and HI corresponding to cmsssbx10 and cmssi10.
+
+Macros
+------
+
+The groff -me macros now work with the -C option. As a result, they
+may also work with Unix nroff/troff.
+
+In -me, the $r and $R number registers now contain the line spacing as
+a percentage of the pointsize expressed in units (normally about 120).
+The previous definition was useless with low resolution devices such
+as X75 and X100.
+
+VERSION 1.00
+============
+
+A -ms-like macro-package is now included.
+
+The name for the Icelandic lowercase eth character has been changed
+from \(-d to \(Sd.
+
+Troff
+-----
+
+There is a new request `nroff', which makes the `n' built-in condition
+true and the `t' built-in condition false; also a new request `troff'
+which undoes the effect of the `nroff' request. This is intended only
+for backward compatibility: it is usually better to test \n(.H or
+\n(.V or to use the `c' built-in condition.
+
+The \R escape sequence has been deleted. Use \E instead.
+
+There are `break' and `continue' requests for use with the `while'
+request.
+
+There is a request `hym' that can ensure that when the current
+adjustment mode is not `b' a line will not be hyphenated if it is no
+more than a given amount short, and a request `hys' that can ensure
+that when the current adjustment mode is `b' a line will not be
+hyphenated if it can be justified by adding no more than a given
+amount of extra space to each word space.
+
+There is a request `rj' similar to `ce' that right justifies lines.
+
+A warning of type `space' will be given when a call is made to an
+undefined request or macro with a name longer than two characters, and
+the first two characters of the name make a name that is defined.
+This is intended to find places where a space has been omitted been a
+request or macro and its argument. This type of warning is enabled by
+default.
+
+Pic
+---
+
+A comma is permitted between the arguments to the `reset' command.
+
+For use with TeX, there is a new `-c' option that makes gpic treat
+lines beginning with `.' in a way that is more compatible with tpic
+(but ugly).
+
+Eqn
+---
+
+It is no longer necessary to add `space 0' at the beginning of
+complicated equations inside pictures.
+
+`prime' is now treated as an ordinary character, as in Unix eqn. The
+previous behaviour of `prime' as an operator can now be obtained using
+`opprime'.
+
+Xditview
+--------
+
+There are two new devices X75-12 and X100-12 which are the same as X75
+and X100 except that they are optimized for documents that use mostly
+12 point text.
+
+VERSION 0.6
+===========
+
+The installation process has been refined to make it easy for you to
+share groff with someone who has the same type of machine as you but
+does not have a C++ compiler. See the end of the INSTALL file for
+details.
+
+There is a man page for the tfmtodit program which explains how to use
+your own fonts with groff -Tdvi.
+
+There is a man page for afmtodit which explains how to use your own
+PostScript fonts with groff -Tps.
+
+The \N escape sequence is now fully supported. It can now be used to
+access any character in a font by its output code, even if it doesn't
+have a groff name. This is made possible by a convention in the font
+files that a character name of `---' refers to an unnamed character.
+The drivers now all support the `N' command required for this. The font
+description files have been updated to include unnamed characters.
+
+The `x' command in font description files has been removed: instead
+any unknown commands are automatically made available to the drivers.
+If you constructed your own font files with an earlier version of
+tfmtodit or afmtodit, you must construct them again using the current
+version.
+
+Characters between 0200 and 0237 octal are no longer legal input
+characters. Note that these are not used in ISO 8859.
+
+A command called `grog' has been added, similar to the `doctype'
+command described in Kernighan and Pike.
+
+Groff
+-----
+
+The groff command has some new options: -V prints the pipeline
+instead of executing it; -P passes an argument to the postprocessor,
+-L passes an argument to the spooler.
+
+There is a C++ implementation of the groff command. This handles some
+things slightly better than the shell script. In particular, it can
+correctly handle arguments containing characters that have a special
+meaning to the shell; it can give an error message when child
+processes other than the last in the pipeline terminate abnormally;
+its exit status can take account of the exit statuses of all its child
+processes; it is a little more efficient; when geqn is used, it
+searches for the eqnchar file in the same way that font metric files
+are searched for, rather than expecting to find it in one particular
+directory.
+
+Gtroff
+------
+
+There is font translation feature: For example, you can tell gtroff to
+use font `HR' whenever font `H' is requested with the line
+ .ftr H HR
+This would be useful for a document that uses `H' to refer to
+Helvetica.
+
+There are some new number registers: `.kern' contains the current kern
+mode, `.lg' the current ligature mode, `.x' the major version number,
+`.y' the minor version number, `.ce' the number of lines to be
+centered in the current environment, `.trunc' the amount of vertical
+space truncated by the most recently sprung vertical position trap,
+`.ne' the amount of vertical space needed in the last `ne' request
+that caused a vertical position trap to be sprung.
+
+The `cf' request now behaves sensibly in a diversion. If used in a
+diversion, it will now arrange for the file to be copied to the output
+when the diversion is reread.
+
+There is a new request `trf' (transparent file) similar to `cf', but
+more like `\!'.
+
+There is a new escape sequence `\Y[xxx]', roughly equivalent to
+`\X'\*[xxx]'', except that the contents of string or macro xxx are not
+interpreted, and xxx may contain newlines. This requires an output
+format extension; the drivers have been modified to understand this.
+Grops has also been modified to cope with newlines in the arguments to
+\X commands; grops has a new \X command mdef, which is like def except
+that it has a first argument giving the number of definitions.
+
+There is a new warning category `escape' which warns about unknown
+escape sequences.
+
+The `fp' request now takes an optional third argument giving the external
+name of the font.
+
+The `\_' character is now automatically translated to `\(ul' as in troff.
+
+The environment variable `GROFF_HYPHEN' gives the name of the file
+containing the hyphenation patterns.
+
+There is a `\C'xxx'' escape sequence equivalent to `\[xxx]'.
+
+Characters ", ', ), ], *, \(dg are now initially transparent for the purposes
+of end of sentence recognition.
+
+There is an anti-recusion feature in the `char' request, so you can
+say `.char \(bu \s+2\(bu\s-2'.
+
+The limit on the number of font positions has been removed.
+Accordingly `\n[.fp]' never returns 0.
+
+The restriction on the number of numbered environments has been removed.
+
+There is a new escape sequence `\E' that makes it possible to
+guarantee that an escape sequence won't get interpreted in copy-mode.
+The `\R' escape sequence is accordingly now deprecated.
+
+Gpic
+----
+
+Arguments of the form `X anything X' (in the `copy thru', `sh', `for',
+`if' and `define' constructs) can now be of the form `{ anything }'.
+
+If the `linethick' variable is negative (as it now is initially),
+lines will be drawn with a thickness proportional to the current point
+size.
+
+The `rand' function now takes no arguments and returns a number between
+0 and 1. The old syntax is still supported.
+
+`^' can be used in expressions to indicate exponentiation.
+
+In the `for' construct the argument to the by clause can be prefixed
+by `*' to indicate that the increment is multiplicative.
+
+A bare expression may be used as an attribute. If the current
+direction is `dir', then an attribute `expr' is equivalent to
+`dir expr'
+
+There is a `sprintf' construct that allows numbers to be formatted and used
+wherever a quoted string can be used.
+
+The height of a text object without an explicit height attribute is
+the number of text strings associated with the object times the value
+of the `textht' variable.
+
+The maximum height and width of a picture is controlled by the
+`maxpswid' and `maxpsht' variables.
+
+Gtbl
+----
+
+Gtbl can now handle gracefully the situation where the `ce' request
+has been applied to a table.
+
+Geqn
+----
+
+The `ifdef' primitive has been generalized.
+
+A tilde accent can be put underneath a box using `utilde'. This
+defined using a general `uaccent' primitive.
+
+Grops
+-----
+
+There is a new PostScript font downloading scheme which handles font
+downloading for imported illustrations. Previously, the name of the
+file containing the font was given in the `x download' line in the
+groff font metric file. Now, there is a `download' file which says
+for each PostScript font name which file contains that font. Grops
+can also now handle inter-font dependencies, where one downloadable
+font depends on some other (possibly downloadable) font.
+
+The `T' font has been removed. The characters it used to provide are
+now provided by `char' definitions in tmac.ps. TSymbol.ps has also
+been removed, and the tweaks it provided are now provided by `char'
+definitions.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 000000000..8f186c169
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2625 @@
+Sun Jun 2 10:20:24 1991 James Clark (jjc at jclark)
+
+ * Version 1.02 released.
+
+Sat Jun 1 12:19:46 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.andoc: New file.
+ * macros/Makefile: Install tmac.andoc.
+
+ * troff/node.c, troff/env.c, troff/input.c: Conditionalize use of
+ operator new and delete on OP_DELETE_BROKEN not being defined.
+ * Makefile: Mention OP_DELETE_BROKEN.
+
+Mon May 27 13:49:07 1991 James Clark (jjc at jclark)
+
+ * Makefile (bindist): Pass SUBFLAGS.
+
+Sun May 26 14:13:22 1991 James Clark (jjc at jclark)
+
+ * Makefile, groff.c: Pass definitions to groff.c via device.h.
+
+ * tty/tty.c (tty_font::load_tty_font): Avoid shadowing
+ parameter.
+
+ * ps/Makefile, ps.c: Pass BROKEN_SPOOLER_FLAGS via broken.h.
+
+ * ps/ps.h, ps/psrm.c: Make comment_table and
+ header_comment_table local to resource_manager::process_file.
+
+ * groff.sh: With -TXps pass -printCommand option to gxditview.
+
+ * groff.c (possible_command::print): Implement using
+ append_arg_to_string.
+
+ * xditview: Merge in new implementation with own ChangeLog.
+
+Sat May 25 18:33:20 1991 James Clark (jjc at jclark)
+
+ * groff.c (main): Implement PRINT_OPTION.
+ (append_arg_to_string): New command.
+ (device_table): Set PRINT_OPTION flag for Xps.
+
+Fri May 24 09:48:58 1991 James Clark (jjc at jclark)
+
+ * troff/groff.h: Rename to troff.h.
+
+ * pic/lex.c (lookup_keyword, docmp): New functions.
+ (get_token): Use new lookup_keyword.
+ Don't include key.h.
+ * pic/key.[ch], pic/pic.gperf: Deleted.
+ * pic/Makefile: Remove gperf stuff.
+
+ * pic/Makefile, pic/output.h: Move definition of TEX_SUPPORT
+ into output.h.
+ * pic/tex.c: Move include of pic.h before test of TEX_SUPPORT.
+
+ * troff/Makefile, troff/node.c: Move definition of
+ STORE_WIDTH into node.c.
+
+ * etc/grog.pl, etc/grog.sh: Support -mdoc.
+
+Thu May 23 12:30:49 1991 James Clark (jjc at jclark)
+
+ * dvi/devdvi/texr.map, dvi.devdvi/texi.map,
+ dvi/devdvi/texb.map: Add lq and rq.
+ dvi/devdvi: Regenerate fonts.
+ * ps/devps/textmap: Add lq and rq.
+ * ps/devps: Regenerate fonts.
+ * tty/devascii/R.proto, tty/devlatin1/R.proto: Add lq and rq.
+ * macros/tmac.e: Define \*(lq and \*(rq to be \(lq and \(rq.
+
+ * pic/object.c (position_rectangle): When checking radius
+ cope with possiblity that width or height is negative.
+ (box_object::box_object): Have separate xrad and yrad with
+ signs matching signs of dim components.
+ (box_object::{north,south}_{east,west}): Use xrad and yrad.
+ (box_object::print): With rounded boxes use absolute values
+ for dim and rad arguments.
+
+ * lib/Makefile, lib/fontfile.o: Pass definition of FONTPATH
+ in fontpath.h.
+
+ * eqn/Makefile, eqn/main.c: Pass definition of DEVICE in device.h.
+
+ * various files: Add explicit destructors to keep Saber CC +d
+ happy.
+
+Wed May 22 11:37:11 1991 James Clark (jjc at jclark)
+
+ * eqn/box.c (box::top_level): Restore fonts correctly after
+ font changes in line containing inline equation. Also
+ restore previous font as well as current font.
+ * eqn/pbox.h: Define necessary string and register names.
+
+ * troff/input.c (token::next): Case 'R' calls do_register.
+ (do_register): New function.
+
+Tue May 21 11:28:23 1991 James Clark (jjc at jclark)
+
+ * groff.c, groff.sh: Support Xps device. Allow each device
+ to have a pseudo_name and a real_name.
+
+ * groff.c (run_commands): Don't print `Broken pipe' messages.
+
+ * ps/pfbtops.c: New file.
+ * ps/Makefile: Add pfbtops.
+
+ * troff/number.c (parse_term): Improved error message.
+
+Mon May 20 11:22:14 1991 James Clark (jjc at jclark)
+
+ * groff.c, groff.sh, etc/grog.sh, etc/grog.pl: Support grefer.
+
+ * Makefile: Integrate refer.
+ * refer: New directory.
+ * man/grefer.man, man/glookbib.man, man/gindxbib.man,
+ man/lkbib.man: New files.
+ * man/Makefile: Support refer man pages.
+
+ * lib/lib.h: Declare is_prime.
+ * lib/prime.c: New file.
+
+ * troff/input.c (macro_source): New function.
+ (init_input_requests): Bind "mso" to macro_source.
+
+ * troff/env.c (environment::possibly_break_line): Maintain
+ pointer to pointer to node to be split in ndp so as to avoid
+ using address of freed node.
+
+ * troff/env.c (environment::hyphenate_line): Maintain pointer to
+ pointer to first node to be hyphenated in startp so as to
+ avoid using address of freed node.
+
+ * troff/env.c (class trie, class hyphen_trie): Make the
+ elements of the trie be of type char not unsigned char.
+ Declare arguments to be const char* instead of unsigned char *.
+
+ * troff/env.c (hyphenate): Initialize hbuf[0].
+
+ * troff/input.c (set_string): Declare p to be char * and cast
+ *p to unsigned char when necessary.
+
+ * troff/input.c (do_define_macro): Declare s to be const
+ char*. Cast element to unisgned char when necessary, Declare
+ d to be an int. Handle EOF better.
+
+ * troff/Makefile, troff/input.c: Different scheme for passing
+ definitions of MACROPATH, HYPHENFILE and DEVICE.
+
+Tue May 14 13:41:36 1991 James Clark (jjc at jclark)
+
+ * tty/devascii/R.proto: Delete entry for em.
+ * tty/devlatin1/R.proto: Likewise.
+
+Sat May 11 11:13:28 1991 James Clark (jjc at jclark)
+
+ * troff/input.c (translate): Stop when we get a space. Treat eof
+ like newline.
+
+ * macros/tmac.an (IP): Only pass quoted argument to TP when \n(.$>1.
+
+Wed Apr 24 19:24:33 1991 James Clark (jjc at jclark)
+
+ * tbl/main.c (process_format): A font name following a `f'
+ modifier that starts with a digit can be only one character long.
+ Also deal with EOF on the second character of the font name.
+
+Wed Apr 17 11:23:43 1991 James Clark (jjc at jclark)
+
+ * troff/input.c (token::next): Turn \~ into an
+ unbreakable_space_node.
+ * troff/node.c (unbreakable_space_node): New class.
+ * troff/node.h: Declare it.
+
+Tue Apr 16 10:47:12 1991 James Clark (jjc at jclark)
+
+ * dvi/dvi.c (dvi_printer::set_char): Make code an int. Check that
+ it's >= 0, before outputting it as a single byte.
+
+Mon Apr 15 11:20:23 1991 James Clark (jjc at jclark)
+
+ * lib/font.c: Make font_char_metric::code an int.
+ (font::get_code): Change return type to int.
+ (font::load): Allow code to be arbitrary integer.
+ * lib/font.h (font::get_code): Change return type to int.
+ (font::number_to_index): Change argument type to int.
+ * troff/input.c (token::next): In case 'N', allow any value.
+ Store value in token::val.
+ (token::operator==): For TOKEN_NUMBERED_CHAR test equality of val.
+ (token::get_char, token::add_to_node_list, token::process): Get
+ number from val.
+ (charinfo::set_number): Change argument to int.
+ (charinfo::get_number): Require that NUMBERED flag be set.
+ (get_charinfo_by_number): Store numbered characters not between 0
+ and 255 in a dictionary.
+ * troff/charinfo.h (get_charinfo_by_number): Change argument type
+ to int.
+ (charinfo::number): Change type to int.
+ (charinfo::set_number): Change type of set_number to int.
+ * troff/node.c (troff_output_file::put_char_width,
+ troff_output_file::put_char): Test whether character is numbered
+ using charinfo::numbered().
+ * driver/printer.c (printer::set_numbered_char): Allow arbitrary
+ values of num.
+ * lib/nametoindex.c: New implementation to cope with arbitrary
+ number characters.
+
+ * troff/input.c (token::operator==): Test val for
+ TOKEN_CHAR_HEIGHT, TOKEN_CHAR_SLANT, TOKEN_FONT_POSITION, and
+ TOKEN_SIZE.
+
+ * man/Makefile: Add definiton of BROKEN_SPOOLER_FLAGS.
+ (.man.n): sed out @BROKEN_SPOOLER_FLAGS@.
+
+Sun Apr 14 12:57:00 1991 James Clark (jjc at jclark)
+
+ * ps/devps/zapfdr.ps: Don't copy UniqueID. Avoid use of newdict
+ variable.
+
+ * all Makefiles: rm targets of cp and >.
+
+ * xditview/xtotroff.c (MapFont): Unlink troff_name before opening
+ it.
+
+ * eqn/lex.c (def_table): Add dollar.
+
+Sat Apr 13 13:02:44 1991 James Clark (jjc at jclark)
+
+ * troff/input.c (do_width): Push back newline before closing delim
+ like do_bracket.
+
+Fri Apr 12 15:16:03 1991 James Clark (jjc at jclark)
+
+ * groff.c (possible_command::prepend_arg): New function.
+ (main): Prepend device -m option.
+ * groff.sh: Put device -m options before command-line options.
+
+Tue Apr 9 10:24:43 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.an (IP): Quote argument to TP.
+
+ * ps/ps.c (main): New option -b, which sets...
+ (broken_flags): New variable.
+ (ps_printer::~ps_printer): Incorporate the setup section in the
+ prolog if (broken_flags & NO_SETUP_SECTION).
+ (ps_printer::begin_page): Generate {Begin,End}PageSetup comments.
+ (ps_printer::merge_download_fonts, ps_printer::merge_import_fonts,
+ ps_printer::merge_ps_fonts, ps_printer::print_font_comment,
+ ps_printer::print_needed_font_comment,
+ ps_printer::print_supplied_font_comment,
+ ps_printer::print_include_font_comments,
+ ps_printer::lookup_doc_font, ps_printer::download_fonts,
+ ps_printer::read_download_file, read_document_fonts, add_font,
+ skip_line, parse_fonts_arg, document_font::document_font,
+ document_font::~document_font, document_font::download,
+ ps_output::include_file): Deleted.
+ (ps_printer::~ps_printer): Generate %%EOF. Generate %!PS-Adobe-3.0
+ rather than %!PS-Adobe-2.0. Make calls to
+ resource_manager::need_font for each font that we used. Replace
+ calls to merge_ps_fonts, merge_download_fonts, print_font_comment,
+ print_supplied_font_comment, print_needed_font_comment by call to
+ resource_manager::print_header_comments. Output %%Orientation
+ comment. Output %%Requirements: numcopies comment if ncopies > 1.
+ Don't output the prolog directly. Instead call
+ resource_manager::output_prolog. Only define #copies when ncopies
+ > 1. Delete calls to print_include_font_comments and
+ download_fonts. Add call to resource_manager::document_setup.
+ (ps_printer::do_file): Call resource_manager::import_file instead
+ of including it ourselves.
+ (ps_printer::do_import): Likewise. Also don't call
+ merge_import_fonts. Push userdict on the dictionary stack before
+ and pop it afterwards.
+ Move declaration of ps_output into ps.h.
+ * ps/psrm.c: New file implementing resource_manager class.
+ * ps/ps.h: New file declaring ps_output and resource_manager
+ classes.
+ * ps/devps/zapfdr.ps:
+ * ps/devps/symbolsl.ps:
+ * ps/devps/prologue: Use 3.0 conventions.
+ * ps/Makefile: Pass definition of BROKEN_SPOOLER_FLAGS in DEFINES.
+ Add default definition of BROKEN_SPOOLER_FLAGS.
+ * Makefile: New variable BROKEN_SPOOLER_FLAGS. Add
+ BROKEN_SPOOLER_FLAGS to SUBFLAGS.
+
+Mon Apr 8 09:26:54 1991 James Clark (jjc at jclark)
+
+ * etc/grog.pl: New file.
+ * Makefile (GROG): New variable.
+ Add GROG to SUBFLAGS.
+ * etc/Makefile (GROG): New variable.
+ (install.nobin): Install $(GROG) rather than grog.sh.
+
+Thu Apr 4 11:36:45 1991 James Clark (jjc at jclark)
+
+ * eqn/special.c (special_box::compute_metrics): Make the input and
+ output strings the same. Get the new height and depth from the
+ predefined height and depth registers. Also make subscript kern
+ and skew available.
+ (special_box::compute_subscript_kern, special_box::compute_skew):
+ New functions.
+
+ * eqn/box.c (pointer_box::compute_skew,
+ simple_box::compute_metrics, box::top_level)
+ * eqn/text.c (prime_box::compute_metrics,
+ prime_box::comput_subscript_kern)
+ * eqn/limit.c (limit_box::compute_metrics):
+ * eqn/delim.c (build_extensible, delim_box::compute_metrics):
+ * eqn/sqrt.c (sqrt_box::compute_metrics): Protect possibly
+ negative numbers in `nr' requests with a leading 0.
+
+Wed Apr 3 15:58:23 1991 James Clark (jjc at jclark)
+
+ * eqn/special.c: New file.
+ * eqn/eqn.y: Declare token SPECIAL. Make it right associative.
+ Add new rule for simple.
+ * eqn/lex.c (token_table): Add SPECIAL.
+ * eqn/box.h: Declare make_special_box.
+ * eqn/Makefile: Add special.[co].
+
+Sat Mar 30 10:57:53 1991 James Clark (jjc at jclark)
+
+ * ps/devps/prologue: Possibly set packing to true while defining.
+ Create grops dictionary here. Initialize local variables before
+ defining procedures.
+ (PICTURE): Rename to PBEGIN. Also do save, noop showpage, count
+ the dictionary stack. Set strokeadjust and overprint to false if
+ the relevant operators are defined.
+ (PEND): New procedure.
+ * ps/ps.c (ps_printer::~ps_printer): In the prolog just include
+ prologue. Do everything else in the setup section.
+ (ps_printer::do_import): Just call PBEGIN and PEND around the
+ picture. Also push userdict before, and pop it afterwards.
+
+Wed Mar 27 07:59:50 1991 James Clark (jjc at jclark)
+
+ * troff/node.c (bracket_node::tprint): Brackets were being printed
+ 1m too low.
+
+ * macros/tmac.an (SH, SS): Set fill mode.
+
+Tue Mar 26 07:46:31 1991 James Clark (jjc at jclark)
+
+ * troff/div.c (top_level_diversion::begin_page): Set
+ high_water_mark to 0.
+
+Fri Mar 22 09:19:46 1991 James Clark (jjc at jclark)
+
+ * man/mdate.sh: New file.
+ * man/mdate.c: Deleted.
+ * man/Makefile: Use mdate.sh instead of mdate.
+ (mdate): Deleted.
+
+ * eqn/lex.c (do_gsize): Supply missing argument to error message.
+
+Tue Mar 19 11:06:50 1991 James Clark (jjc at jclark)
+
+ * man/mdate.c: New file.
+ * man/*.man: Replace modification date by @MDATE@.
+ * man/Makefile (.man.n): Replace @MDATE@ by `mdate $<`.
+ (mdate): New target.
+
+ * lib/font.c (text_file::next): Deal with arbitrarily long lines.
+ Remove illegal input characters.
+
+Mon Mar 18 08:32:25 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (pg*start-col): Do .ns *after* running the hooks.
+
+Sat Mar 16 03:52:25 1991 James Clark (jjc at jclark)
+
+ * troff/div.c (begin_page): Change behaviour when
+ !first_page_begun and !break_flag.
+
+ * troff/input.c (do_name_test): Return 0 if argument is empty.
+
+ * troff/input.c (read_long_escape_name): Require closing ] to be
+ at same input level as opening [.
+
+ * troff/input.c (read_increment_and_escape_name): New function.
+ (get_copy, process_input_stack): Use this for \n.
+
+Fri Mar 15 00:31:48 1991 James Clark (jjc at jclark)
+
+ * troff/div.c (top_level_diversion::begin_page): Ignore the
+ current value of page_number if !first_page_begun.
+
+ * groff.c (main): Fix declaration of buf.
+
+ * troff/input.c (do_name_test): New function.
+ (token::next): Implement \A.
+ (token::next): Implement \e by turning it into a TOKEN_ESCAPE.
+ (token::description, token::add_to_node_list, token::process):
+ Handle TOKEN_ESCAPE.
+ * troff/token.h: New token TOKEN_ESCAPE.
+
+Thu Mar 14 10:22:26 1991 James Clark (jjc at jclark)
+
+ * pic/main.c (do_picture): Allow space before and after filename
+ following `<'. Check that the filename is not empty.
+
+Wed Mar 13 12:49:40 1991 James Clark (jjc at jclark)
+
+ * Version 1.01 released.
+
+ * dvi/devdvi/CompileFonts: Add cm*ss10 fonts.
+
+ * dvi/tmac.dvi: ftr HR to H.
+
+ * macros/tmac.e: Round up computation of $r.
+
+ * xditview/tmac.X: Don't give up completely in compatibility mode.
+ Use \n(.s instead of \n[.s].
+
+ * dvi/tmac.dvi: Don't give up completely in compatibility mode.
+ Use \(ci instead of \[ci]. Use \n(.s instead of \n[.s].
+ Add u to factors inside \s[...]. Rename frac to dvi-frac.
+ Translate \(FM onto \[prime] and \(!/ onto \[slashnot]; use these
+ short names in the char definitions.
+
+ * ps/tmac.ps: Don't give up completely in compatibility mode.
+ Fix the fraction definitions to use \n(.s and \(f/. Add an extra
+ quote in front of \n(.s. Add u to factors inside \s[...].
+
+Mon Mar 11 12:01:20 1991 James Clark (jjc at jclark)
+
+ * tty/tmac.tty: Call the nroff request.
+
+ * macros/tmac.e ((x, )x): Better definitions that work properly
+ in a diversion.
+ (@0, @1): Helper macros for (x.
+
+ * macros/tmac.e ($s, hl): Use \l rather than \D.
+
+ * tty/tmac.tty: Make it work better in compatibility mode.
+ (pchar): Rename to tty-char.
+
+ * macros/tmac.e (@E): New macro.
+ (r, i, b, rb, bi): Use @E.
+
+ * macros/tmac.e (@F): Don't use (;...) syntax.
+
+ * macros/tmac.e: Remove mention of \*(||/revisions. Mention that
+ it was modified for groff.
+
+ * macros/tmac.e: Make sure \n(ps and \n(es are >= \n(.V.
+
+ * macros/tmac.e (<., .>): Removed.
+ ([., .]): If \n(.V>=1v, use [] instead of superscripting.
+
+ * macros/tmac.e: Remove check that groff is being used.
+
+ * macros/tmac.e (@C): Change families only if using groff; turn
+ compatibility mode off while changing familes. Save compatibility
+ mode before changing families and restore it afterwards.
+
+ * macros/tmac.e (@h): Remove test for offset + line length.
+
+ * macros/tmac.e (sorry): Rename to @S. Use \$1 instead of \$0
+ (lo, th, ac): Define to call @S instead of using als.
+
+ * macros/tmac.e: Make $r and $R now contain \n(.v*100/\n(.sp, ie
+ the ratio of the vertical spacing to the point size in units
+ expressed as a percentage. Use these instead of $10r and $10R,
+ Delete $10r and $10R.
+
+ * lib/font.c (font::load): In default computation of space_width,
+ divide by sizescale. Use scale_round.
+
+ * macros/tmac.an (TP): Don't call `nf'.
+ (an-do-tag): Don't call `fi'.
+
+Sun Mar 10 09:52:35 1991 James Clark (jjc at jclark)
+
+ * troff/input.c (process_input_stack): Handle the case where
+ spaces at the beginning of an input line are followed by a
+ newline.
+
+Thu Mar 7 20:18:07 1991 James Clark (jjc at jclark)
+
+ * groff.c (device_table): Add PIC_X_OPTION for dvi device.
+ * groff.sh: Use pic -x with the dvi device.
+
+ * dvi/devdvi/FontMakefile (H): Don't use -s.
+
+ * dvi/devdvi/HI, dvi/devdvi/HB: New files.
+ * dvi/devdvi/Makefile: Add HI and HB to FONTS.
+ * dvi/devdvi/FontMakefile: Add rules for HI and HB. Include these
+ in FONTS.
+
+Mon Mar 4 13:20:14 1991 James Clark (jjc at jclark)
+
+ * ps/psfig.diff: New file.
+ * ps/tmac.psfig: New file.
+
+Sat Mar 2 00:15:09 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (]=, ref*do-tl, ref*bib-print): Deleted.
+ (]-): Don't call ref*do-tl.
+
+ * macros/tmac.s (ref*end-print): Use XP if [F not defined.
+
+ * macros/tmac.s (ref*normal-print): Call FS rather than fn@do-FS.
+ (fn@do-FS): Rename to fn*do-FS.
+
+ * troff/input.c (transparent_translate): New function.
+ (process_input_stack): Apply transparent_translate before calling
+ diversion::transparent_output(unsigned char).
+
+Wed Feb 27 00:13:25 1991 James Clark (jjc at jclark)
+
+ * troff/input.c (do_define_macro): Define the macro before calling
+ skip_line.
+
+ * xditview/Makefile: Add DEVICES variable. Change install target
+ to use this.
+
+Tue Feb 26 10:46:22 1991 James Clark (jjc at jclark)
+
+ * groff.c (run_commands): Handle the possibility that there are
+ child processes other than those forked by us.
+
+Sun Feb 24 21:32:30 1991 James Clark (jjc at jclark)
+
+ * lib/string.c (string::append): New function.
+ * lib/stringclass.h: Declare it.
+
+Thu Feb 21 11:49:26 1991 James Clark (jjc at jclark)
+
+ * eqn/main.c (main): New option -N which sets
+ no_newline_in_delim_flag.
+ (do_file): If no_newline_in_delim_flag is set don't allow newlines
+ in delimiters.
+ * groff.c (main): Pass -N on to eqn.
+ (help, synopsis): Mention -N.
+ * groff.sh: Implement -N.
+
+Wed Feb 20 15:16:10 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (]=, ref*bib-print, ref*do-tl): New macros.
+ (]-): Call ref*do-tl if ref*need-tl is non-zero.
+ (XP): Allow as initializer.
+
+Tue Feb 19 14:09:06 1991 James Clark (jjc at jclark)
+
+ * troff/env.c (environment::wrap_up_field): If field_spaces are
+ non-zero and we have a current_tab, subtract padding from
+ tab_distance. If this makes tab_distance <= 0, use the next tab
+ stop instead. If there isn't any next tab or it's a left tab,
+ wrap up the current tab.
+ (environment::start_field): Initialize tab_precedes_field.
+ (environment::wrap_up_tab): If there's a current field, update
+ pre_field_width, field_distance and tab_precedes_field.
+ * troff/env.h (environment::tab_precedes_field): New member.
+
+Fri Feb 15 01:24:00 1991 James Clark (jjc at jclark)
+
+ * ps/ps.c (ps_printer::do_file): New function.
+ (ps_printer::special): Bind to `file' special.
+ (ps_printer::do_exec): Set ndefined_styles to 0.
+
+Sat Feb 9 03:03:04 1991 James Clark (jjc at jclark)
+
+ * eqn/text.c (split_text): Grok \* and similar escapes sequences.
+ Avoid stripping first character from the start of unrecognized
+ escapes. Use lex_error instead of error to report errors.
+ * eqn/lex.c (get_token): Rework handling of escapes.
+ (lex_error): Move declaration into...
+ * eqn/eqn.h.
+
+ * xditview/xditview.c (main): Make -page option work.
+
+ * Makefile: Correct comment about -DBROKEN_SPOOLER and pageview.
+
+Wed Feb 6 12:28:43 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (B2): Correct size of box.
+
+Tue Feb 5 00:37:35 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (B2): Postpone drawing the box until in the
+ top-level diversion.
+
+ * tty/tmac.tty: Add font translations for C, CR, CW.
+
+ * groff.c (synopsis, help): Document -i.
+ * groff.sh: Implement -i.
+
+ * macros/tmac.s (@NH): Put a `.' after multi-part numbers.
+ Simplify the construction of SN.
+
+ * troff/number.c (parse_term): Give `|' a higher precedence.
+ * tbl/table.c (numeric_text_entry::simple_print): Parenthesise
+ accordingly.
+
+ * macros/tmac.s (B2): Use par@finish instead of par@reset.
+
+Mon Feb 4 12:36:09 1991 James Clark (jjc at jclark)
+
+ * lib/string.c (string::move): New function.
+ * lib/stringclass.h: Declare it.
+
+Sat Feb 2 16:02:16 1991 James Clark (jjc at jclark)
+
+ * troff/env.c (distribute_space): Add optional argument
+ `force_forward'.
+ (environment::wrap_up_field): Call distribute_space with
+ `force_forward' argument of 1.
+
+Fri Feb 1 19:36:33 1991 James Clark (jjc at jclark)
+
+ * lib/string.c, lib/stringclass.h (string::operator+=(char)):
+ Inline it. Move reallocation into...
+ (string::grow1): New function.
+ * pic/Makefile, tbl/Makefile, eqn/Makefile, ps/Makefile: Redo
+ dependencies to include library header files.
+ * lib/Makefile: Make string.c and lf.c depend on stringclass.h.
+
+Thu Jan 31 15:02:27 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (@NH): Use the same number registers than -ms does
+ for the heading level counters. Use the same string that -ms does
+ for the number for this heading.
+
+Wed Jan 30 14:25:40 1991 James Clark (jjc at jclark)
+
+ * lib/new.c (operator new): Cast result of malloc to char *.
+
+ * troff/input.c (spring_trap, lookup_request): Add assert that nm
+ is not null.
+
+Tue Jan 29 18:08:05 1991 James Clark (jjc at jclark)
+
+ * groff.c (main): Support -i.
+
+Sun Jan 27 13:23:17 1991 James Clark (jjc at jclark)
+
+ * pic/pic.h: Include <osfcn.h>.
+
+ * ps/ps.c: Add declaration of mktemp.
+
+ * Makefile: Add -DHAVE_UNION_WAIT option for CPPDEFINES.
+ * groff.c: If HAVE_UNION_WAIT is defined, declare wait()'s
+ argument as union wait *.
+ (run_commands): If HAVE_UNION_WAIT is defined cast wait()'s
+ argument to union wait *.
+
+Sat Jan 26 12:04:52 1991 James Clark (jjc at jclark)
+
+ * tty/tmac.tty: Add definition of \(co.
+
+ * pic/object.c (make_arc): Only increase radius when radius
+ strictly less than d.
+ (arc_object::update_bounding_box): May need to add 4.0 to end_quad
+ more than once.
+
+ * troff/env.c (environment::environment(symbol),
+ environment::environment(const environment *)): Initialize
+ input_trap_count.
+
+Sat Jan 19 08:18:35 1991 James Clark (jjc at jclark)
+
+ * tbl/main.c (main): Add exit(0).
+
+ * ps/ps.c (ps_printer::~ps_printer): Use fseek instead of rewind.
+
+ * pic/main.c (main):
+ * eqn/main.c (main):
+ * tbl/main.c (main):
+ * etc/soelim.c (main):
+ * driver/printer.c (printer::~printer):
+ * troff/node.c (real_output_file::~real_output_file,
+ real_output_file::flush): Check for errors on stdout.
+
+ * most files: Add 1991 to copyright notice.
+
+ * macros/tmac.s: Don't test \n(.x and \n(.y.
+
+ * troff/input.c (token::next): Rename `escape_char' label to
+ `handle_escape_char' and `normal_char' label to
+ `handle_normal_char'.
+
+Thu Jan 17 15:46:35 1991 James Clark (jjc at jclark)
+
+ * groff.c (main, synopsis, help): Support -a option.
+ * groff.sh: Likewise. Also eliminate Zflag variable by adding -z
+ to trflags while parsing options.
+
+Tue Jan 15 13:07:27 1991 James Clark (jjc at jclark)
+
+ * troff/number.c (parse_term): With `m', `M' and `n' scale
+ indicators, convert scale factor to hunits before scaling.
+
+Mon Jan 14 12:39:12 1991 James Clark (jjc at jclark)
+
+ * lib/font.c (scale_round): Better test for overflow when n is
+ negative.
+
+Thu Jan 10 11:10:56 1991 James Clark (jjc at jclark)
+
+ * tbl/main.c (process_format): Add second argument of type
+ options*. Change callers. Allow opt->tab_char as well as '\t'
+ between format items.
+
+Mon Jan 7 12:30:18 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.an (PD): With no arguments, make sure register PD is
+ at least \n[.V].
+ (TH): Call PD with no argument, instead of setting register PD
+ directly.
+
+Sun Jan 6 11:18:39 1991 James Clark (jjc at jclark)
+
+ * Version 1.00 released.
+
+Sat Jan 5 08:44:30 1991 James Clark (jjc at jclark)
+
+ * ps/tmac.ps, xditview/tmac.X: Add font translation of C to CR.
+
+ * dvi/devdvi/DESC: Mount CW instead of CR.
+
+ * dvi/tmac.dvi: Add definition of \(tm.
+
+ * dvi/devdvi/texsy.map: Add lh, and rh.
+ * dvi/devdvi/texex.map: Add lt, rt, lb, rb, lk, rk.
+ * dvi/devdvi/texmi.map: Add *o. Regenerate fonts.
+
+ * dvi/devdvi/FontMakefile: Generate H from cmss10.
+ * dvi/devdvi/Makefile: Install H.
+ * dvi/devdvi/H: New file.
+
+Fri Jan 4 15:04:57 1991 James Clark (jjc at jclark)
+
+ * troff/env.c (vertical_spacing): Don't allow vertical spacing to
+ be 0.
+
+Thu Jan 3 13:41:19 1991 James Clark (jjc at jclark)
+
+ * macros/tmac.s (@EN): Add \n(.V to the argument to ds@need.
+
+ * macros/tmac.pic (PS): Avoid attempting to set negative indent.
+
+ * macros/tmac.s (@EN): Handle the case where the equation is empty
+ but the label is not.
+
+Wed Jan 2 10:31:44 1991 James Clark (jjc at jclark)
+
+ * troff/groff.h: New warning category WARN_SPACE.
+ * troff/input.c: Add WARN_SPACE to DEFAULT_WARNING_MASK. Add
+ WARN_SPACE to warning_table.
+ (interpolate_macro): Give a warning of type WARN_SPACE if the name
+ is longer than two characters and is not defined, but the first
+ two characters do make a defined name.
+
+ * PROBLEMS: New file.
+
+ * CHANGES: New file.
+ * README-0.6, README-1.00: Deleted.
+
+ * groff.c, groff.sh: Add X75-12 and X100-12 devices.
+ * xditview/devX75/Makefile: Make devX75-12.
+ * xditview/devX100/Makefile: Make devX100-12.
+
+ * xditview/devX100/eqnchar, xditview/devX75/eqnchar,
+ dvi/devdvi/eqnchar, ps/devps/eqnchar: Remove use of \R.
+
+Tue Jan 1 19:24:01 1991 James Clark (jjc at jclark)
+
+ * README-0.7: Rename to README-1.00.
+
+ * macros/tmac.pic: New file.
+ * macros/Makefile (install.nobin): Install tmac.pic.
+
+Mon Dec 31 10:40:53 1990 James Clark (jjc at jclark)
+
+ * troff/env.c (hyphen_word): Correct the test for whether the
+ token is a hyphen. Reset npos to 0.
+
+ * macros/tmac.s (par@sup-start, par@sup-end): New implementations.
+
+Sun Dec 30 15:53:13 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.s (ds*common-end): Call par*reset.
+ (PE): Likewise.
+ (par@reset-indent): Deleted.
+
+ * macros/tmac.s (@IP): Divert the label.
+
+Sat Dec 29 14:33:32 1990 James Clark (jjc at jclark)
+
+ * xditview/draw.c (setGC): Use a line width of .1m rather than
+ .04m by default; round rather than truncate value.
+
+ * tbl/table.c (class empty_entry): New class.
+ (empty_entry::empty_entry, empty_entry::line_type): New functions.
+ (table::add_entry): Represent empty entries by objects of type
+ empty_entry.
+ (table_entry::line_type): Return -1.
+ (table::determine_row_type): Ignore entries with line_type 0.
+ Treat type -1 as non-lines.
+
+Fri Dec 28 15:04:41 1990 James Clark (jjc at jclark)
+
+ * ps/devps/textmap, xditview/libXdvi/DviChar.c, tty/devlatin1/R.proto,
+ macros/tmac.s: Rename \(-d to \(Sd.
+
+Thu Dec 27 12:35:47 1990 James Clark (jjc at jclark)
+
+ * ps/devps/textmap: Add `sd', `/_' and `3d' characters.
+ * xditview/libXdvi/DviChar.c: Likewise.
+ * dvi/devdvi/texsy.map: Add `<<', `>>'.
+
+Wed Dec 26 13:33:23 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (top_level_diversion::begin_page): Call
+ init_output() if the_output is 0.
+
+Sat Dec 22 12:35:29 1990 James Clark (jjc at jclark)
+
+ * troff/input.c: Replace ESCAPE_E by ESCAPE_e and ESCAPE_C by
+ ESCAPE_c.
+ (get_copy): Turn \E into ESCAPE_E.
+ (token::process, asciify): Handle ESCAPE_E.
+
+ * macros/tmac.s (ds*common-end, par@reset): Add `.rj 0'.
+ (RD): New macro.
+ (DS): Implement `.DS R'.
+
+Fri Dec 21 11:41:53 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.s (FS): New macro.
+
+ * macros/tmac.s (fn@do-FS): Use @LP instead of LP.
+
+ * macros/tmac.s (cov*tl-init): Remove after first execution
+ instead of aliasing to @nop. Call top of page macro explicitly
+ instead of setting trap; call @init first. Set pg@top as top of
+ page macro.
+ (cov*auto-init): Deleted. Set cov*tl-init instead of
+ cov*auto-init as top of page trap.
+ (TL, LP): Do a break instead of calling cov*tl-init.
+ (cov*print): With RP format but no TL, alias FS and FE to @FS and
+ @FE; in this case also give a warning and always start another
+ page. No need to set pg@top here.
+ (cov*tl-init): Rename to cov*first-page-init.
+
+ * macros/tmac.s (RP): Do `.pn 0'.
+ (cov*tl-init): With RP format don't do `.pn 0'.
+
+ * macros/tmac.s (pg@cs-top): Set no space mode.
+
+ * macros/tmac.s (par@TL, par@AU, par@AI): New macros.
+ (cov*ab-init): Alias TL, AU and AI to these.
+
+Thu Dec 20 10:10:50 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.s (@EQ): Move the space before the equation into @EN
+ (@EN): Do nothing unless \n[dl] is > 0.
+
+Tue Dec 18 12:20:47 1990 James Clark (jjc at jclark)
+
+ * pic/object.c (ellipse_object::radius): New function.
+
+ * VERSION: Change version to 0.7.
+
+ * tbl/table.c (block_entry::do_divert): Declare return type as
+ void.
+ (block_entry::divert, alphabetic_block_entry::divert): Return 1.
+
+Mon Dec 17 12:30:34 1990 James Clark (jjc at jclark)
+
+ * troff/column.c: New file.
+ * troff/Makefile: Corresponding changes.
+
+ * troff/hvunits.c (scale(vunits, vunits, vunits)): New function.
+ Friend of vunits.
+
+ * troff/div.c (top_level_diversion::space): If the space causes
+ the first-page transition and springs a trap, truncate the space
+ to 0.
+
+Fri Dec 14 12:30:02 1990 James Clark (jjc at jclark)
+
+ * ps/ps.c (ps_printer::do_import): Add a `clear' after including
+ the document.
+
+ * pic/troff.c (troff_output::line_thickness,
+ troff_output::set_fill): Do a horizontal motion to compensate for
+ the width of the \D escape sequence.
+
+Thu Dec 13 10:17:14 1990 James Clark (jjc at jclark)
+
+ * xditview/tmac.X: Reinstate definition of \(rn, but only for X100
+ (not X75).
+
+ * eqn/sqrt.c (sqrt_box::compute_metrics): Supply missing argument
+ to printf.
+
+ * tbl/table.c (simple_entry::simply_print): Don't declare as pure.
+ Supply empty definition.
+ (text_entry::simple_print, simple_text_entry::simple_print):
+ Delete declarations.
+ (table::add_entry): Represent empty entries by objects of type
+ `simple_entry'.
+
+Wed Dec 12 08:50:48 1990 James Clark (jjc at jclark)
+
+ * troff/Makefile: Remove -DHYPHEN_CONTROL from DEFINES.
+
+ * tbl/table.c (left_text_entry::add_tab): New function.
+
+ * macros/tmac.s: Make @RT an alias for par@reset. Make RT
+ initialize like LP.
+
+Mon Dec 10 11:19:55 1990 James Clark (jjc at jclark)
+
+ * troff/env.c (environment::start_field): Give an error message if
+ there is no next tab.
+
+Sun Dec 9 11:46:40 1990 James Clark (jjc at jclark)
+
+ * troff/env.c (hyphenate): Skip initial elements with zero
+ hyphenation code.
+
+ * macros/tmac.s (par@init): Keep VS in points rather than units.
+
+Sat Dec 8 23:00:27 1990 James Clark (jjc at jclark)
+
+ * pic/main.c (main): Implement `-c' option.
+ * pic/output.h: Declare make_tpic_output().
+ * pic/tex.c (tex_output::set_pen_size): Make it virtual and
+ protected.
+ (tpic_output): New class.
+ (tpic_output::tpic_output, tpic_output::set_pen_size,
+ tpic_output::command, make_tpic_output): New functions.
+
+Fri Dec 7 11:57:41 1990 James Clark (jjc at jclark)
+
+ * tbl/main.c (main): Call `.ab' if \n(.g is false. Define TS/TE
+ if they're not already defined.
+ * tbl/table.c (init_output): Don't test \n(.g.
+
+ * troff/input.c (do_if_request): Delete `g' condition. Recognize
+ `d', `r' and `c' conditions even in compatibility mode.
+
+Tue Dec 4 09:13:47 1990 James Clark (jjc at jclark)
+
+ * ps/tmac.ps (ps-bb): Protect against negative numbers in bounding
+ box.
+
+Mon Dec 3 07:18:26 1990 James Clark (jjc at jclark)
+
+ * troff/env.h (environment::prev_line_interrupted): New member.
+ (environment::get_prev_line_interrupted): New function.
+ * troff/env.c (environment::newline): Set prev_line_interrupted.
+ (environment::environment(const environment *),
+ environment::environment(symbol)): Initialize
+ prev_line_interrupted.
+ * troff/input.c (process_input_stack): Don't give special
+ treatment to space and newline at the beginning of the line if the
+ previous line was interrupted.
+
+Sat Dec 1 15:48:37 1990 James Clark (jjc at jclark)
+
+ * eqn/eqn.y: Disallow PRIME by itself.
+ * eqn/lex.c (token_table): Bind `opprime' instead of `prime' to
+ PRIME.
+ (def_table): Remove definition of '. Define prime to be `.
+
+ * eqn/eqn.y: Split off part of rule `script' into a new rule
+ `nonsup'.
+
+Fri Nov 30 10:23:44 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.s ({, }): New string aliases.
+
+Thu Nov 29 11:34:40 1990 James Clark (jjc at jclark)
+
+ * README-0.7: New file.
+
+Wed Nov 28 10:09:57 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.s: New file.
+ * man/groff_ms.man: New file.
+ * Makefile: Add definition of TMAC_S. Pass TMAC_S in SUBFLAGS.
+ * Makefile.bd: Similarily.
+ * man/Makefile: Add groff_ms.n to MAN7PAGES. Replace @TMAC_S@. Add
+ definition of TMAC_S.
+ * macros/Makefile: Add definition of TMAC_S. Install tmac.s.
+ * macros/TODO: New file.
+
+Sat Nov 24 20:04:54 1990 James Clark (jjc at jclark)
+
+ * troff/env.c (right_justify): New function.
+ (init_env_requests): Bind this to request "rj".
+ (center_lines): Set right_justify_lines to 0. If we get a bad
+ integer, center 1 line.
+ (environment::environment(symbol), environment::environment(const
+ environment *)): Initialize right_justify_lines.
+ (environment::get_right_justify_lines): New function.
+ (init_env_requests): Bind this to number_register ".rj".
+
+ * troff/env.c (environment::choose_breakpoint): Implement
+ hyphenation_margin and hyphenation_space.
+ (environment::get_hyphenation_space,
+ environment::get_hyphenation_margin): New functions.
+ (init_env_requests): Bind these to .hys and .hym.
+ (hyphenation_space_request, hyphenation_margin_request): New
+ functions
+ (init_env_requests): Bind these to hys and hym.
+ (environment::environment(symbol), environment::environment(const
+ environment *)): Initialize hyphenation_margin and
+ hyphenation_space.
+ * troff/env.h: Corresponding changes to class environment.
+
+Fri Nov 23 09:08:16 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (blank_line): Always do a break.
+
+ * eqn/box.c (do_text): Turn off escapes while appending text to
+ string.
+
+Thu Nov 22 10:58:59 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (while_break_request, while_continue_request): New
+ functions.
+ (init_input_requests): Bind these to "break" and "continue".
+ (while_depth, while_break_flag): New variables.
+ (while_request): Update while_depth. Break out of loop if
+ while_break_flag is set.
+
+Wed Nov 21 10:54:40 1990 James Clark (jjc at jclark)
+
+ * tbl/table.c (init_span_reg): Initialize span_width_reg to \n(.H
+ rather than 0.
+
+Mon Nov 19 00:45:03 1990 James Clark (jjc at jclark)
+
+ * Makefile: Include -DBROKEN_SPOOLER by default. Expand comment.
+
+ * stringify: New file.
+ * Makefile (groff.o): Use stringify.
+
+ * xditview/tmac.X: Remove definition of \(rn.
+ * xditview/libXdvi/DviChar.c: Remove radicalex from
+ Adobe_symbol_map.
+
+Sat Nov 17 10:44:58 1990 James Clark (jjc at jclark)
+
+ * tbl/table.c (table::add_entry): Allow alphabetic text blocks.
+ (alphabetic_block_entry::alphabetic_block_entry,
+ alphabetic_block_entry::divert, alphabetic_block_entry::print):
+ New functions.
+ (block_entry::divert): Split off body into ...
+ (block_entry::do_divert): If the block is alphabetic, subtract 2n
+ from the line length; also update the span width to dl+2n, and the
+ alphabetic span width to dl.
+
+ * driver/input.c (do_file): While reading argument to D command,
+ when expanding buffer, multiply szp by sizeof(int) rather than 2
+ in the argument to memcpy.
+
+ * tbl/table.c (compute_span_width): Add 2n rather than 1n to the
+ width of alphabetic columns.
+
+Fri Nov 16 06:34:27 1990 James Clark (jjc at jclark)
+
+ * troff/node.c (lookup_family): Supply second argument to lookup.
+
+ * troff/dictionary.c (dictionary::lookup): After an unsuccesful
+ search, return immediately if v is 0.
+
+ * pic/troff.c: Define EQN_NO_EXTRA_SPACE_REG.
+ (troff_output::start_picture): Set this reg.
+ (troff_output::end_picture): Remove this reg
+ * eqn/box.c (box::extra_space): Don't produce `\x's if
+ EQN_NO_EXTRA_SPACE_REG is defined.
+
+ * eqn/eqn.y: Allow just a PRIME to be a `simple'.
+ * eqn/text.c (split_text): Map ' to \(fm when it's the first
+ character.
+
+Thu Nov 15 10:35:06 1990 James Clark (jjc at jclark)
+
+ * macros/tmac.e: Use font 3 instead of B in $c. Remove `bd'
+ requests.
+
+ * troff/div.c (top_level_diversion::top_level_diversion):
+ Initialize page_number to 0.
+
+Wed Nov 14 21:41:58 1990 James Clark (jjc at jclark)
+
+ * groff/troff (environment::environment(const environment *)):
+ Initialize name to e->name, rather than "anonymous".
+
+Sat Nov 10 01:59:37 1990 James Clark (jjc at jclark)
+
+ * xditview/libXdvi/Dvi.c (ShowDvi): If eof is encountered, reset
+ requested_page. Split middle part into ...
+ (FindPage): New function.
+ (SetValues): If we don't yet know the last page, and the requested
+ page is greater than the current page, call FindPage.
+ Update the font_map_string before doing this.
+
+ * xditview/tmac.X: Add definitions of \(sq, \(ga, \(dg and \(dd.
+ Translate \(lh and \(rh into left and right double arrows.
+
+ * troff/node.c (class hyphen_inhibitor_node): New class.
+ (hyphen_inhibitor_node::hyphen_inhibitor_node,
+ hyphen_inhibitor_node::copy, hyphen_inhibitor_node::same,
+ hyphen_inhibitor_node::type,
+ hyphen_inhibitor_node::get_hyphenation_type): New functions.
+ (node::add_discretionary_hyphen): Use hyphen_inhibitor_node rather
+ than dbreak_node(0, 0) to represent a `\%' at the beginning of a
+ word.
+
+Fri Nov 9 16:05:38 1990 James Clark (jjc at jclark)
+
+ * troff/node.h (dummy_node::get_hyphenation_type,
+ transparent_dummy_node::get_hyphenation_type): Declare them.
+ * troff/node.c: (dummy_node::get_hyphenation_type,
+ transparent_dummy_node::get_hyphenation_type): New functions.
+
+Wed Nov 7 10:09:06 1990 James Clark (jjc at jclark)
+
+ * xditview/libXdvi/draw.c: If M_PI not defined after including
+ math.h, then define it.
+
+ * xditview/Makefile: Add definition of AR. Pass it to the submake
+ in libXdvi.
+ * xditview/libXdvi/Makefile: Add definitions of AR and RANLIB.
+
+Tue Nov 6 10:14:27 1990 James Clark (jjc at jclark)
+
+ * troff/dictionary.h (object_dictionary::alias): Declare return
+ value as int.
+ * troff/dictionary.c (object_dictionary::alias): Return non-zero
+ if the old name was defined.
+ * troff/input.c (alias_macro): Give a warning if the old name was
+ not defined.
+ * troff/reg.c (alias_reg): Likewise.
+
+Mon Nov 5 00:31:39 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (token::next): Delete implementation of \R.
+
+ * macros/Makefile: Strip comments from tmac.e while installing it.
+
+ * troff/input.c: New variable `nroff_mode'.
+ (troff_request, nroff_request): New functions.
+ (init_input_requests): Bind `troff' and `nroff' to troff_request
+ and nroff_request.
+ (do_if_request): Compute results of t and n conditions from
+ nroff_mode.
+
+ * text/text.c (split_text): Fix typo in >=.
+
+ * eqn/lex.c: Add definition of `==' to def_table.
+
+Fri Nov 2 02:49:09 1990 James Clark (jjc at jclark)
+
+ * pic/tex.c (tex_output::start_picture): Change the definitions of
+ \graph and \graphtemp so that they work properly with Plain TeX.
+
+ * pic/tex.c (tex_output::solid_arc): Ensure that the second angle
+ argument to `ar' is not less than the first.
+
+ * pic/pic.y: Allow a comma between elements of the variable list
+ in the argument to `reset'.
+
+ * pic/object.c (arc_object::arc_object): Fix computation of
+ radius.
+
+ * eqn/main.c (main): Add exit(0).
+
+Thu Nov 1 02:03:50 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (begin_page): Test no_space_mode after doing the
+ break, but still push the page ejector cookie before doing the
+ break. Also set the next page number after doing the break.
+
+ * xditview/xditview.c (NewFile): Don't set the title and icon name
+ if this is the first file and its name is `-'.
+ * groff/groff.c: Define a new device flag XT_OPTION. Set it for
+ the X75 and X100 devices.
+ (main): If a device has the XT_OPTION flag set and there's exactly
+ one file argument, pass the driver -xrm and -title options to set
+ the icon name and window title to the name of the file.
+
+ * troff/env.c (environment_switch): If there was an argument but
+ it wasn't a valid number or name, then pop an environment but
+ don't give an error message on underflow.
+
+ * troff/number.c (start_number): Correct spelling in error message.
+
+ * troff/input.c (token::delimiter): Don't print an error message
+ if err is false.
+
+ * xditview/libXdvi/parse.c (ParseInput): In case 'D', only call
+ ParseDrawFunction if dw->display_enable is true.
+
+Wed Oct 31 05:49:50 1990 James Clark (jjc at jclark)
+
+ * pic/pic.y: Parse text positioning like normal attributes, so as
+ to allow `"text" at 0,0 ljust'. Don't allow `center' as a
+ positioning attribute.
+
+Mon Oct 29 22:50:38 1990 James Clark (jjc at jclark)
+
+ * tbl/main.c (process_data): When in state START while reading a
+ text block, don't change to state MIDDLE if c is a newline.
+
+Sun Oct 28 21:59:56 1990 James Clark (jjc at jclark)
+
+ * dvi/dvi.c (dvi_printer::begin_page): Rename `i' variable to `j'
+ so as to avoid shadowing parameter.
+
+Wed Oct 24 18:35:39 1990 James Clark (jjc at jclark)
+
+ * tbl/table.c (trim_space): Deleted.
+ (table::add_entry): Don't call trim_space.
+
+Mon Oct 22 03:48:39 1990 James Clark (jjc at jclark)
+
+ * VERSION: Change version to 0.6.
+
+ * troff/number.c (parse_expr): Make == work.
+
+Sat Oct 20 11:28:17 1990 James Clark (jjc at jclark)
+
+ * man/grog.man: New file.
+ * man/Makefile: Add grog.n to MAN1PAGES.
+ * etc/grog.sh: New file.
+ * etc/Makefile: Install grog.sh as grog.
+
+Fri Oct 19 11:17:15 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (token::next): Implement \E.
+
+Thu Oct 18 11:56:24 1990 James Clark (jjc at jclark)
+
+ * xditview/tmac.X: Change font translations to match tmac.ps.
+
+ * troff/input.c (non_empty_name_warning): Don't give a warning if
+ `\{' terminates the name.
+
+Tue Oct 16 10:04:23 1990 James Clark (jjc at jclark)
+
+ * ps/devps/symbol.diff: New file.
+ * ps/devps/FontMakefile: Mention symbol.diff.
+
+Sun Oct 14 11:46:46 1990 James Clark (jjc at jclark)
+
+ * troff/node.c (font_position): Use get_long_name to read the
+ external_name.
+
+ * troff/env.c (environment_switch): If we get a number that's < 0
+ or >= NENVIRONMENTS, treat it like a name.
+ Change NENVIRONMENTS to 10.
+
+ * troff/groff.h: Remove definition of FONTS_MAX.
+ * troff/node.h (class font_family): Make map a pointer instead of
+ an array. Add a map_size member. Make it a class. Make nm const
+ and public. Make invalidate_fontno a friend.
+ * troff/node.c: Define font_table_size. Make font_info a pointer
+ rather than an array.
+ (class troff_output_file): Allocate font_position dynamically. Add
+ nfont_positions member.
+ (troff_output_file::set_font): Grow font_position if necessary.
+ (troff_output_file::~troff_output_file): Delete font_position.
+ (troff_output_file::troff_output_file): Allocate font_position.
+ (grow_font_table): New function.
+ (troff_output_file::really_begin_page,
+ troff_output_file:really_copy_page): Use nfont_positions rather
+ than FONTS_MAX.
+ (mount_font_no_translate, mount_style): Call grow_font_table if
+ necessary.
+ (font_family::font_family): Allocate map.
+ (font_family::make_definite): Grow map if necessary. Use
+ font_table_size instead of FONTS_MAX.
+ (font_family::~font_family): New function.
+ (invalidate_fontno): Use font_family::map_size.
+ (get_fontno, env_space_width, env_half_narrow_space_width,
+ env_narrow_space_width, symbol_fotno, is_good_fontno,
+ get_bold_fontno, make_glyph_node): Use font_table_size rather than
+ FONTS_MAX.
+ (next_available_font_position): Never return 0.
+
+Fri Oct 12 10:17:52 1990 James Clark (jjc at jclark)
+
+ * ps/tmac.ps: Add font translations for compatibility with dpost.
+
+Thu Oct 11 12:09:03 1990 James Clark (jjc at jclark)
+
+ * eqn/pile.c: Rename default_baseline_sep to baseline_sep.
+ Move BASELINE_SEP_FORMAT and COLUMN_WIDTH_FORMAT into pbox.h.
+ Move definitions baseline_sep, shift_down, column_sep,
+ matrix_side_sep into...
+ * eqn/box.c: Add them to param_table.
+ * eqn/pbox.h: Add declarations to pbox.h.
+
+ * troff/input.c (set_string): Cast value to unsigned char *.
+
+ * troff/token.h (process_input_stack): Declare it static before
+ declaring it a friend.
+
+Wed Oct 10 09:59:13 1990 James Clark (jjc at jclark)
+
+ * dvi/devdvi/texex.map: Fix positions of extensible brace middle
+ and bottom.
+ * dvi/devdvi/EX: Regenerate.
+
+ * troff/input.c (init_charset_table): Make ", ', ), ], *, \(dg
+ transparent.
+
+Tue Oct 9 08:34:02 1990 James Clark (jjc at jclark)
+
+ * eqn/lex.c: In defaults_table, make definition of `dot' call
+ `dot_def'. Don't explicitly make it roman. Similarily for other
+ accents.
+
+ * pic/lex.c (for_input::for_input): Add by_is_multiplicative
+ argument.
+ (for_input::get, for_input::peek): Use this.
+ (do_for): Add by_is_multiplicative argument.
+ * pic/pic.y: Change optional_by clause to allow '*' after `by'.
+ Change semantic value of optional_by to be a double plus a flag
+ saying whethet the by clause is multiplicative.
+
+ * eqn/lex.c (get_delimited_text): Remember location of start of
+ definition. Improve error handling when EOF is encountered.
+
+ * lib/font.h: Rename handle_x_command to
+ handle_unknown_font_command.
+ * lib/font.c (font::load): Call handle_unknown_font_command for
+ any unknown command in the font description file. Don't call
+ handle_x_command. Include the name of the command in the argv.
+ Improve message for unknown command after kernpairs or charset
+ command.
+ * ps/ps.c (ps_font::handle_x_command): Rename to
+ handle_unknown_font_command. Remove message about `x download'
+ command. Give error message for wrong number of arguments.
+ * ps/devps/afmtodit: Generate `encoding' instead of `x encoding'.
+ * dvi/dvi.c (dvi_font::handle_x_command): Rename to
+ handle_unknown_font_command. Give an error message for wrong
+ number of arguments. Rename design_size to designsize.
+ * dvi/tfmtodit.c (main): Generate `checksum' instead of `x
+ checksum', `designsize' instead of `design_size'.
+
+Mon Oct 8 00:38:55 1990 James Clark (jjc at jclark)
+
+ * eqn/*.[chy]: Change underaccent to uaccent.
+
+ * eqn/eqn.y: Add rule for underaccent. Declare UNDERACCENT token;
+ give it the same precedence as ACCENT.
+ * eqn/other.c (make_underaccent_box): New function.
+ * eqn/box.h: Declare it.
+ * eqn/lex.c: Add UNDERACCENT to token_table. Add utilde to
+ def_table.
+
+Sun Oct 7 11:25:16 1990 James Clark (jjc at jclark)
+
+ * pic/pic.y (reset_all): New function. Called in rule for RESET.
+ (parse_init): Call reset_all.
+ (define_variable): When defining scale reset only those
+ pre-defined variables that are scaled.
+ (defaults_table): Add `scale' as non-scaled value.
+
+ * pic/pic.y: Redo parsing of text adjustments: parse adjustments
+ together with the text; allow any number of positioning words;
+ allow center as a positioning word.
+
+ * pic/object.c (output::compute_scale): Get picture maximum height
+ and width from variables called maxpswid and maxpsht.
+ * pic/pic.y: Add maxpswid and maxpsht to defaults_table.
+
+Sat Oct 6 10:16:56 1990 James Clark (jjc at jclark)
+
+ * pic/object.c (object_spec::make_text): Multiply textht by number
+ of text items.
+
+ * pic/pic.y: Allow `sprintf("string", expr,...)' wherever text can
+ occur.
+ (do_sprintf): New function.
+ (pic.gperf): Add sprintf token.
+ (text, sprintf): New rules.
+
+ * pic/pic.y: `rand()' with no arguments returns a random number
+ in the range [0,1).
+
+ * pic/pic.y: Allow a bare expression to be an attribute: change
+ precedences to support this. Change optional_ordinal rule to
+ optional_ordinal_last to avoid reduce/reduce conflict.
+ * pic/object.c (object_spec::object_spec): Initialize direction.
+
+ * pic/pic.y: Implement ^ operator meaning exponentiation.
+
+ * troff/node.h: Add default argument to mount_font.
+ * troff/node.c (font_position): Read an optional third argument
+ giving the external_name.
+ (mount_font): Add optional argument giving the external_name.
+ (mount_font_not_translate): Have additional argument giving
+ external name. Use this name to load the font. Pass both names
+ to font_info::font_info.
+ (font_info::font_info): Have additional argument giving
+ external_name.
+ (class tfont): New member external_name.
+ (font_info::get_tfont): Use external name to construct tfont_spec.
+
+Fri Oct 5 04:03:13 1990 James Clark (jjc at jclark)
+
+ * eqn/lex.c (init_table): Add argument giving device. Define
+ name of device to be "1".
+ (do_ifdef): Counts as true if the argument has been defined with
+ `define'.
+ * eqn/main.c (main): Call init_table with device argument. Make
+ device local to main.
+ * eqn/eqn.h: Change declaration of init_table. Remove declaration
+ of device.
+
+ * pic/lex.c (get_delimited): Allow text to be delimited by
+ matching {}s. Don't recognize ending delimiter within a string.
+
+ * troff/input.c (get_delim_name): New function.
+ (token::next): Implement \C.
+
+ * lib/font.c (font::load): Grok ---. Add an alias for each
+ character based on its code.
+ (font::get_code_width): Deleted.
+ * lib/font.h (class font): Declare font::number_to_index().
+ Remove declaration of font::get_code_width.
+ * lib/nametoindex.c (font::name_to_index): Add 512 rather than 256
+ to indices of named characters.
+ (font::number_to_index): New function.
+ * troff/input.c (font::number_to_index): New function.
+ (get_charinfo_by_number, charinfo::get_number,
+ charinfo::set_number): New functions.
+ (token::next): Turn \N into a TOKEN_NUMBERED_CHAR.
+ (token::process, token::description, token::get_char,
+ token::add_to_node_list, token::operator==): Handle
+ TOKEN_NUMBERED_CHAR.
+ * troff/charinfo.h: Declare get_charinfo_by_number,
+ charinfo::get_number, charinfo::set_number. Add NUMBERED flag to
+ charinfo class.
+ (charinfo::numbered): New function.
+ * troff/token.h: Add TOKEN_NUMBERED_CHAR.
+ * troff/env.h (class environment): Remove declaration of ...
+ * troff/env.c (environment::make_numbered_char_node): Deleted.
+ * troff/node.c (make_numbered_node): Deleted.
+ (class numbered_glyph_node): Remove.
+ (troff_output_file::put_char_width, troff_output_file::put_char):
+ Handle numbered chars.
+ (troff_output_file::numbered_char): Removed.
+ (tfont::get_code_width): Removed.
+ (make_glyph_node): Don't search special fonts for numbered
+ characters.
+ * troff/node.h: Remove declaration of make_numbered_node.
+ * driver/input.c (do_file): Handle N command.
+ * driver/printer.h: Add declaration of ...
+ * driver/printer.c (printer::set_numbered_char): New function.
+ * dvi/tfmtodit.c (main): Generate unnamed entries.
+ * ps/devps/afmtodit: Likewise.
+ * xditview/xtotroff.c (MapFont): Likewise.
+ * xditview/libXdvi/parse.c (ParseInput): Grok N command.
+
+ * tbl/main.c (process_format): If multiple widths are specified
+ for a column but all the widths are the same, don't give an error
+ message.
+
+ * tbl/table.c (table::do_row): If the current row is all lines and
+ the stuff doesn't contains a line, mark the top of the row after
+ printing stuff before the row. If the current row is not all
+ lines and the stuff doesn't contain a line, don't unnecessarily
+ mark the top of the row before printing the stuff.
+
+Mon Oct 1 11:42:00 1990 James Clark (jjc at jclark)
+
+ * troff/groff.h: Remove MAX_PATH.
+ * troff/input.c (open_file): Dynamically allocate space for the
+ path.
+ (open_mac_file, process_macro_file): Corresponding changes.
+
+Sun Sep 23 18:56:26 1990 James Clark (jjc at jclark)
+
+ * troff/node.h (class output_file): Make copy_file pure. Add
+ vspace method ifdef COLUMN. Add is_printing method.
+ * troff/node.c: Add class printing_reg. Add class
+ real_output_file. Derive other output_file classes from
+ real_output_file; in these classes rename begin_page to
+ really_begin_page, print_line to really_print_line, copy_file to
+ really_copy_file, transparent_char to really_transparent_char.
+ Move output_file::flush to real_output_file. Add printing member
+ to class output_file.
+ * troff/div.h: Remove printing member from top_level_diversion.
+ Add vspace member function to class diversion ifdef COLUMN. Add
+ some declarations ifdef COLUMN.
+ * troff/div.c (top_level_diversion::copy_file,
+ top_level_diversion::transparent_output,
+ top_level_diversion::output): Don't test printing member before
+ output.
+ * troff/input.c: Handle initial variable_space_request ifdef
+ COLUMN.
+ * troff/Makefile: Add column.c but comment it out. Add -DCOLUMN
+ but comment it out.
+
+Sat Sep 22 11:32:22 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (diversion::need): Make any space forced. If we
+ sprung a trap, set truncated_space to minus the distance to the
+ trap and set needed_space to the amount that was needed.
+ (top_level_diversion::space): A forced space turns no_space_mode
+ off.
+ (class constant_vunits_reg): New class.
+ (init_div_requests): Implement number registers .trunc and .ne
+ using constant_vunits_reg.
+ (class truncated_space_reg): Deleted.
+
+ * troff/div.h: Don't have a no_space_mode member in diversion.
+ Instead have it in top_level_diversion.
+ * troff/div.c (diversion::diversion): Don't initialize
+ no_space_mode.
+ (top_level_diversion::top_level_diversion): Initialize
+ no_space_mode.
+ (no_space, restore_spacing): Do nothing if curdiv != topdiv.
+ (macro_diversion::output): Don't clear no_space_mode.
+
+ * troff/input.c (diverted_space_node::reread): Don't call
+ environment::do_break. In fill mode, act like a blank line.
+ (diverted_copy_file_node::reread): Don't call
+ environment::do_break.
+
+ * troff/div.c (blank_line): New function.
+ * troff/div.h: Declare it.
+ * troff/input.c (process_input_stack): Call it.
+
+ * troff/div.c (truncated_space_reg::get_string): New function.
+ (init_div_requests): Bind to .trunc.
+ (space_request, top_level_diversion::space,
+ top_level_diversion::output, macro_diversion::space,
+ macro_diversion::output): Update truncated_space.
+ (macro_diversion::output): Redo calculations when trap sprung.
+ (macro_diversion::output, macro_diversion::space): No need for
+ trap_flag.
+
+ * troff/div.c (top_level_diversion::output): Set nl_reg_contents
+ after truncating post line spacing.
+
+Fri Sep 21 11:27:25 1990 James Clark (jjc at jclark)
+
+ * ps/devps/prologue (MF, SF): Make them work even if setfont is
+ defined as a procedure rather than as an operator.
+
+Thu Sep 20 12:55:05 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (macro_diversion::space): Ignore no_space_mode.
+
+Wed Sep 19 10:54:37 1990 James Clark (jjc at jclark)
+
+ * troff/div.c (top_level_diversion::output): Merge
+ output_file::print_line and output_file::end_of_line member
+ functions.
+ * troff/div.h (class output_file):
+ * troff/node.c (troff_output_file::print_line,
+ troff_output_file::end_of_line, output_file::end_of_line,
+ ascii_output_file::print_line, suppress_output_file::print_line):
+ Corresponding changes.
+
+Tue Sep 18 11:31:47 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (token::next): Don't give a warning for `\.'.
+
+ * troff/env.c (environment::get_center_lines): New function.
+ (init_env_requests): Bind number register .ce to it.
+ * troff/env.h: Declare it.
+ * tbl/table.c (table::init_output): Define reset macro to restore
+ .ce. If center option not given, store .ce in SAVED_CENTER_REG.
+ Then do .ce 0.
+ (table::print): If center option not given, then imply center
+ option if SAVED_CENTER_REG > 0.
+
+Mon Sep 17 09:19:19 1990 James Clark (jjc at jclark)
+
+ * ps/devps/Makefile: Remove T from FONTS. Remove TSymbol.ps and
+ Troff.ps from DOWNLOAD.
+
+ * troff/Makefile: Change comment in DEFINES to avoid confusing
+ System V make.
+
+ * ps/ps.c (ps_printer::do_exec): Allow newlines within PostScript
+ code. Don't try to catch errors with stopped.
+ (check_line_lengths): New function.
+ * ps/devps/prologue (EXEC): Deleted.
+ (EBEGIN, EEND): New procedures.
+
+Sun Sep 16 14:51:15 1990 James Clark (jjc at jclark)
+
+ * troff/input.c: Include request.h before node.h.
+ * troff/node.c: Likewise.
+ * troff/env.c: Likewise.
+ * troff/div.c: Likewise.
+ * troff/node.h (class special_node): Store argument as a macro
+ rather than a char *.
+ * troff/node.c (special_node::special_node, special_node::copy):
+ Grok this.
+ (special_node::~special_node): Deleted.
+ (special_node::tprint): Deleted.
+ (special_node::tprint_start, special_node::tprint_end,
+ special_node::tprint_char): New functions.
+ (troff_output_file::special): Deleted.
+ (troff_output_file::start_special, troff_output_file::end_special,
+ troff_output_file::special_char): New functions.
+ * troff/input.c (special_node::tprint): New function.
+ (do_special): Use macro not char *.
+ (do_transparent_macro): Deleted.
+ (token::next): Don't call do_transparent_macro.
+
+ * troff/input.c (token::next): Add 'Y' case.
+ (do_transparent_macro): New function.
+ * troff/node.c (troff_output_file::special): Handle newlines with
+ argument using new continuation convention.
+ * driver/input.c (get_string): Cope with continuation convention.
+ (do_file): Don't call skip_line after calling get_string(1).
+ * ps/ps.c (ps_printer::special, ps_printer::do_import,
+ ps_printer::do_def, ps_printer::do_exec): Cope with newlines in
+ arg.
+ * xditview/libXdvi/parse.c (ParseInput): Ignore lines starting
+ with +.
+
+Sat Sep 15 19:00:10 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (asciify): By default, illegal input characters
+ should return empty string.
+
+ * troff/input.c (copy_file): Handle first page transition like title.
+ (token::next, process_input_stack): Grok COPY_FILE_REQUEST.
+
+ * troff/input.c (token::next): Improve error message for EOF after
+ escape character.
+ (input_char_description): New function.
+ (get_char_for_escape_name): Use input_char_description.
+ (token::next): Warn about unrecognized escape sequences.
+ (warning_table): Add WARN_ESCAPE.
+ * troff/groff.h: Declare WARN_ESCAPE. Change WARN_TOTAL
+ accordingly.
+
+ * troff/token.h: Remove declaration of process_input_stack.
+
+ * troff/input.c: Remove declaration of init_hyphen_requests.
+ * troff/request.h: Correct spelling in declaration of same.
+
+ * troff/input.c (token::next): Check whether escape_char is 0.
+
+Fri Sep 14 12:09:25 1990 James Clark (jjc at jclark)
+
+ * groff.c (main, usage, help): Implement -P and -L options.
+ * groff.sh: Likewise.
+
+ * troff/input.c (token::next): Use some gotos to avoid code
+ duplication.
+
+ * troff/input.c (get_long_name, get_name, read_long_ecsape_name):
+ Avoid calling symbol::symbol if name empty.
+
+Thu Sep 13 06:21:45 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (init_input_requests): Make \n(.x return the major
+ version number and \n(.y return the minor version number.
+ * troff/Makefile: Construct file majorminor.c defining
+ major_version and minor_version automatically from ../VERSION.
+
+ * troff/node.c (class glyph_node): Make operator new and operator
+ delete public.
+ (class ligature_node): Similarily.
+
+ * troff/input.c (operator==(const macro &, const macro &)): New
+ function.
+ (non_interpreted_node::same): Use this.
+ (string_iterator::string_iterator): Make macro& argument const.
+
+ * troff/input.c (input_iterator::get): New function. Don't make
+ asciify_macro or class non_interpreted_node friends of class
+ input_iterator.
+ (non_interpreted_node::interpret): Use input_iterator::get.
+ (asciify_macro): Likewise.
+
+ * troff/input.c (~token_node, ~string_iterator, ~arg_list,
+ ~non_interpreted_node): Deleted.
+ * troff/node.c: (~suppress_output_file, ~ascii_output_file):
+ Deleted.
+
+ * troff/symbol.h: Make all symbol member functions const.
+
+ * lib/strtol.c: New file.
+ * lib/Makefile: Add strtol.c.
+ * Makefile: Define STRTOL as strtol.o to include strtol in
+ libgroff.a.
+
+Wed Sep 12 10:00:49 1990 James Clark (jjc at jclark)
+
+ * pic/troff.c (troff_output::simple_circle): Divide by scale.
+
+Tue Sep 11 14:17:16 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (do_special): Use input_level.
+
+ * troff/token.h (TOKEN_BACKSPACE): New token.
+ (token::backspace): New function.
+ * troff/input.c (token::description, token::next, token::process):
+ Grok TOKEN_BACKSPACE.
+ (do_special): Turn TOKEN_BACKSPACE back into \b.
+
+ * troff/token.h (token::leader): New function.
+ * troff/input.c (do_special): Turn TOKEN_LEADER back into \001.
+
+ * troff/input.c (do_special): Turn TOKEN_TAB back into \t.
+
+ * troff/input.c (do_special): Use token::description in error
+ message.
+
+Mon Sep 10 11:06:27 1990 James Clark (jjc at jclark)
+
+ * troff/input.c (decode_args): Combine quoted and
+ quote_input_level variables. Make it a for (;;) loop.
+
+ * troff/input.c (get_char_for_escape_name): Check for \001 and \b.
+
+ * troff/input.c (read_long_escape_name): The test for whether to
+ expand buffer was off by 1.
+ (read_string): Similarily.
+
+Fri Sep 7 11:45:50 1990 James Clark (jjc at jclark)
+
+ * troff/input.c: Use `const int' rather than `static const int'.
+
+ * troff/div.h (diversion::copy_file): Declare as pure virtual.
+ (macro_diversion::copy_file): New function.
+ * troff/node.h: New class diverted_copy_file_node.
+ * troff/node.c: Implement it.
+ * troff/input.c (copy_file): Use diversion::copy_file. Handle
+ first page transition by pushing a diverted_copy_file_node.
+ * troff/input.c (token::next, process_input_stack): Don't handle
+ COPY_FILE_REQUEST.
+
+Thu Sep 6 13:29:10 1990 James Clark (jjc at jclark)
+
+ * ps/ps.c (flush_sbuf): Remember to add sbuf_kern when checking
+ whether space widths need adjusting.
+
+ * troff/charinfo.h: Generalize translated_to_space to
+ special_translation so as to allow translation to \&.
+ * troff/input.c (translate): Allow translation to \&.
+ (charinfo::*): Corresponding changes.
+ * troff/node.c (make_node, node::add_char): Corresponding changes.
+ * troff/node.h (dummy_node::dummy_node): Allow optional first
+ argument.
+
+ * lib/lib.h: Make codes 0200 to 0237 illegal input characters.
+ * troff/token.h: Remove TOKEN_TITLE. Remove token::title. Add
+ TOKEN_REQUEST.
+ * troff/input.c (token::next): Turn a TITLE_REQUEST into a
+ TOKEN_REQUEST with an argument of TITLE_REQUEST.
+ (token::process): Grok that.
+ * troff/input.c (copy_file): Handle first page transition like
+ title by pushing a COPY_FILE_REQUEST cookie.
+ (token::next, process_input_stack): Grok that.
+ * troff/node.h (output_file::copy_file): Add x and y arguments.
+ Make it non-pure.
+ * troff/div.c (top_level_diversion::copy_file): Supply them.
+ * troff/node.c (troff_output_file::copy_file): Add x and y
+ arguments; moveto specified position. Invalidate font_position
+ array after copying file.
+ (output_file::copy_file): New function.
+ (suppressed_output_file::copy_file, ascii_output::copy_file):
+ Removed.
+ * troff/input.c (transparent_file): New function.
+ (init_input_requests): Bind to "trf".
+ (token::next): Handle TRANSPARENT_FILE_REQUEST cookie.
+ (process_input_stack): Likewise.
+
+ * troff/Makefile: Add ../lib/lib.h to GROFF_H.
+
+ * troff/node.c (init_node_requests): New number registers .kern
+ pointing to global_kern_mode, and .lg pointing to
+ global_ligature_mode.
+
+ * troff/node.c (ligature): Don't change it if we get a bad
+ integer.
+
+ * troff/input.c (do_define_string): Don't strip tabs.
+
+ * troff/input.c (asciify_macro): Make the string_iterator auto.
+
+ * troff/node.c (init_font_requests): Rename to...
+ (init_node_requests):
+ * troff/node.h: Change declaration.
+ * troff/input.c (main): Change call.
+
+ * troff/input.c (node::reread, diverted_space_node::reread): New
+ methods.
+ (process_input_stack): Call reread rather than
+ get_diverted_space_node.
+ * troff/node.c (node::get_diverted_space_node,
+ diverted_space_node::get_diverted_space_node): Removed.
+ * troff/node.h: Declare reread methods instead of
+ get_diverted_space_node methods. Make `n' member private.
+ * troff/input.c: (token::diverted_space): Removed.
+ * troff/token.h: Removed declaration.
+
+
+Tue Sep 4 00:48:04 1990 James Clark (jjc at jclark)
+
+ * eqn/script.c (script_box::compute_metrics): Don't let
+ SUP_RAISE_FORMAT become negative.
+
+ * tbl/table.c (table::do_row): Entries that don't end in the
+ this row shouldn't make the row non-blank.
+
+ * tbl/table.c (table::make_columns_equal): Only set the width of
+ columns which are marked as equal.
+
+ * tbl/main.c (process_data): Before issuing excess data error,
+ if last character was a newline unget it; then get it again after
+ the error. Also include the contents of the entry in the message.
+
+ * groff.c: New file.
+ * Makefile: Build groff from groff.c. Make it possible to use
+ either groff.sh or groff.c as groff.
+ * Makefile.bd: Similarily.
+
+Mon Sep 3 09:39:49 1990 James Clark (jjc at jclark)
+
+ * groff.sh: Don't delay expansion of $@ in assignment to files.
+ Remove occurrences of \".
+
+Sun Sep 2 09:56:59 1990 James Clark (jjc at jclark)
+
+ * all Makefiles: Simplify and rearrange.
+
+ * Makefile: Handle fmod like malloc.
+ * lib/Makefile: Similarily.
+ * lib/fmod.c: Remove #ifdef NEED_FMOD.
+
+ * Makefile: Rename OPTIMISE to OPTIMIZE.
+
+ * groff.sh: Remove assignment to PATH.
+ * Makefile: Remove SHPATH variable.
+ * Makefile.bd: Similarily.
+
+ * groff.sh: Add -V option to print the pipeline instead of
+ executing it.
+
+Fri Aug 31 00:56:46 1990 James Clark (jjc at jclark)
+
+ * lib/font.c: Split off file searching into ...
+ * lib/fontfile.c: New file.
+
+ * lib/strerror.c (strerror): Use `Error %d' for unknown errors.
+
+Thu Aug 30 13:13:55 1990 James Clark (jjc at jclark)
+
+ * tbl/table.c (table::do_hspan): Delete assertion that e != 0.
+ Also change misleading comment.
+ (table::do_vspan): Change similarily misleading comment.
+ * tbl/main.c (process_data): A format row with an explicit `s'
+ uses up a data line, even if all the other columns are `_' or `='.
+
+ * troff/input.c (token::description): Fix description of
+ TOKEN_DUMMY and TOKEN_EMPTY.
+
+Wed Aug 29 04:12:08 1990 James Clark (jjc at jclark)
+
+ * groff.sh: Fix description of -Z in help message.
+
+Tue Aug 28 07:28:33 1990 James Clark (jjc at jclark)
+
+ * pic/object.c (object_spec::make_object): Allow negative and zero
+ line thicknesses.
+ * pic/pic.y: Give linethick default value of -1.0.
+ * pic/troff.c (troff_output::troff_output): Initialize
+ last_line_thickness to BAD_THICKNESS.
+ (troff_output::finish_picture): Set thickness to BAD_THICKNESS.
+ (troff_output::line_thickness): Canonicalize negative thicknesses
+ to RELATIVE_THICKNESS.
+ * pic/tex.c (tex_output::set_pen_size): Silently map negative line
+ thicknesses to DEFAULT_PEN_SIZE. Canonicalize negative pen sizes
+ to -1.0.
+ (tex_output::start_picture): Set pen_size to -2.0.
+
+ * ps/ps.c (ps_printer::set_line_thickness): If line_thickness is
+ 0, then use 0 linewidth.
+ (ps_printer::ps_printer): Initialize line_thickness to -1.
+
+ * pic/troff.c (troff_output::simple_ellipse): Divide by scale.
+
+ * ps/devps/symbolchars: Remove `or'.
+ * ps/tmac.ps: Implement \(or with .char.
+
+ * ps/devps/symbolchars: Move most characters into textmap.
+ * ps/devps/textmap: Add names for troff bracket characters. Remove
+ ul, ru, br, bv.
+
+ * ps/devps/TSymbol.ps: Removed.
+ * ps/devps/FontMakefile: Make S from Symbol not TSymbol.
+ * ps/tmac.ps: Do with .char what TSymbol did.
+ * ps/devps/download: Remove TSymbol.
+
+ * ps/devps/T: Removed.
+ * ps/devps/Troff.ps: Removed.
+ * ps/devps/Troff.afm: Removed.
+ * ps/tmac.ps: Implement \(ru, \(ul, and \(br with .char.
+ * ps/devps/download: Remove Troff.
+ * ps/devps/FontMakefile: Remove T target.
+ * ps/devps/DESC-A4: Remove T from font list.
+ * ps/devps/DESC-letter: Likewise.
+
+ * troff/input.c (macro_to_node): Rename to ...
+ (charinfo_to_node): Don't pass mac argument. Temporarily remove the
+ character's definition while processing it.
+ * troff/node.c (node::add_char, make_node): Change calls to
+ macro_to_node accordingly.
+
+ * troff/input.c (token::next): Translate \_ to \(ul.
+
+ * tty/devascii/R.proto: Add `|'.
+ * tty/devlatin1/R.proto: Likewise.
+
+Mon Aug 27 11:25:41 1990 James Clark (jjc at jclark)
+
+ * man: Put the version number in all the man pages.
+
+Sun Aug 26 11:40:05 1990 James Clark (jjc at jclark)
+
+ * Makefile.bd: New file.
+ * README.bd: New file.
+
+ * VERSION: New file.
+ * lib/version.c: Removed.
+ * lib/Makefile: Create version.c from ../VERSION. Remove version.c
+ in clean target.
+
+ * troff/input.c (main): Get hyphen_file from GROFF_HYPHEN
+ environment variable.
+
+ * all Makefiles: Split install target into install.bin for
+ binaries, and install.nobin for everything else.
+ * Makefile: Add bindist target.
+
+ * man/afmtodit.man: New file.
+ * man/Makefile: Add afmtodit.n to MAN1PAGES.
+ * ps/devps/Makefile: Add textmap to DEVICEFILES. Install afmtodit
+ in BINDIR.
+ * ps/Makefile: Pass BINDIR to make install in devps.
+
+ * ps/ps.c (ps_printer::set_char): Do nothing if the character is
+ the space character.
+
+ * ps/devps/FontMakefile: Rename symbol.afm to tsymbol.afm.
+
+Sat Aug 25 15:39:03 1990 James Clark (jjc at jclark)
+
+ * ps/ps.c: Redo font downloading.
+ * ps/devps/download: New file.
+ * ps/devps/Makefile: Add download to DEVICEFILES.
+ * ps/devps/afmtodit: Remove -d option.
+ * ps/devps/FontMakefile: Don't use -d option with afmtodit.
+ * ps/devps/symbosl.ps: Add %%DocumentFonts comment.
+ * ps/devps/zapfdr.ps: Likewise.
+ * ps/devps/TSymbol.ps: Likewise.
+
+Fri Aug 24 20:10:30 1990 James Clark (jjc at jclark)
+
+ * groff.sh: Initialize dev to ${GROFF_TYPESETTER:-@DEVICE@}.
+
+Thu Aug 23 10:03:47 1990 James Clark (jjc at yquem)
+
+ * ps/ps.c (ps_output::include_file): If BROKEN_SPOOLER is defined,
+ then strip the first line if it starts with %.
+ * Makefile: Add a comment about this.
+
+ * man/tfmtodit.man: New file.
+ * man/Makefile: Add tfmtodit.n to MAN1PAGES.
+ * dvi/Makefile: Install tfmtodit in BINDIR.
+
+ * dvi/tfmtodit.c (usage): Mention -v option.
+
+Wed Aug 22 09:56:36 1990 James Clark (jjc at yquem)
+
+ * troff/node.c (troff_output_file::end_of_line): Call do_motion.
+ * troff/node.c (troff_output_file::transparent_char): Don't call
+ flush_tbuf.
+
+ * eqn: Add check_tabs method to most box classes.
+ * eqn/box.c (box::top_level): Call check_tabs.
+
+ * eqn/script.c (script_box::output): Use \Z.
+ * eqn/limit.c (limit_box::output): Use \Z.
+
+ * eqn/box.c (box::top_level): Use itoa.
+
+Tue Aug 21 09:29:28 1990 James Clark (jjc at yquem)
+
+ * dvi/tmac.dvi: Add font translations for CR, C, TT.
+ * dvi/devdvi/Makefile: Don't make links to CW.
+
+ * ps/tmac.ps: Add font translations for C, CW, CO, CX, CD, H, HO,
+ HX, HD.
+ * xditview/tmac.X: Likewise.
+
+ * troff/node.c: Add font translation feature.
+ (get_font_translation): New function.
+ (symbol_fontno): Translate the font name.
+ (mount_font_no_translate): Rename to mount_font to this.
+ (mount_font): New function.
+ (font_family::make_definite): Call mount_font_no_translate instead
+ of mount_font.
+ (mount_style): Translate the font name.
+ (font_translate): New function.
+ (init_font_requests): Bind "ftr" to font_translate.
+
+ * ps/devps/prologue (SN): New procedure that rounds a position to
+ the nearest (pixel + (.25,.25)).
+ (DL): Use SN to round endpoints.
+
+ * lib/version.c: Changed version to 0.5.
+
+Sat Aug 18 04:43:21 1990 James Clark (jjc at yquem)
+
+ * Makefile: Move definition of PAGE to the very beginning, so that
+ people are less likely to miss it.
+
+Fri Aug 17 02:15:11 1990 James Clark (jjc at yquem)
+
+ * man/Makefile: Don't need to sed out @UPCASE_PROG_PREFIX@.
+
+ * troff/env.c (environment::choose_breakpoint): Make `can't find
+ breakpoint' error a warning of type WARN_BREAK. Change message to
+ `can't break line'.
+ * troff/groff.h: Declare WARN_BREAK with code 4; change WARN_INPUT to
+ code 040000.
+ * troff/input.c: Add WARN_BREAK to warning_table. Include
+ WARN_BREAK in DEFAULT_WARNING_MASK.
+
+ * tty/tmac.tty: Add definition of \(+-.
+
+ * groff.sh: Remove `--' option to set command.
+
+ * dvi/devdvi/texsy.map: Remove duplicate md entry.
+
+ * ps/devps/eqnchar: Better definition of cdot using md.
+ * dvi/devdvi/eqnchar: Likewise.
+ * xditview/devX100/eqnchar: Likewise.
+ * xditview/devX75/eqnchar: Likewise.
+ * eqn/lex.c: Add definition of cdot.
+
+Thu Aug 16 09:33:57 1990 James Clark (jjc at yquem)
+
+ * troff/input.c (get_optional_char): New function.
+ * troff/input.c (set_page_character): Use get_optional_char(),
+ rather than has_arg() and tok.get_char(1).
+ * troff/env.c (tab_character, leader_character, hyphen_char,
+ field_characters): Likewise.
+ (margin_character): Likewise. Also always delete the
+ margin_character_node.
+
+ * troff/input.c (token::get_char): Use token::description.
+
+ * troff/input.c (has_arg): Don't skip over tab and \}.
+ * troff/number.c (start_number): Give a warning if the number
+ starts with \} (WARN_RIGHT_BRACE) or tab (WARN_TAB).
+
+Wed Aug 15 10:04:37 1990 James Clark (jjc at yquem)
+
+ * troff/input.c (empty_name_warning, non_empty_name_warning): New
+ functions.
+ (get_name, get_long_name): Use these. Rename `warn' argument to
+ `required'.
+
+ * troff/node.c (get_fontno): Test that the symbol is not null.
+
+ * troff/input.c (token::description): New function.
+ * troff/number.c (parse_term): Use token::description in `numeric
+ expression expected' message.
+ * troff/groff.h: Add WARN_MISSING.
+ * troff/number.c (start_number): New function.
+ * troff/number.c (get_vunits, get_hunits, get_number, get_integer,
+ get_incr_number): Use start_number().
+ * troff/input.c (DEFAULT_WARNING_MASK): Enable WARN_NUMBER by
+ default.
+ * troff/input.c (get_name, get_long_name): Use WARN_MISSING.
+ * troff/reg.c (alter_format): Use WARN_MISSING. Also use
+ token::descripion.
+ * troff/input.c (token::get_char): Use WARN_MISSING.
+ * troff/input.c (token::delimiter): Use token::description.
+ * troff/env.c (environment_switch): Back out Aug 3 change.
+ * troff/input.c (has_arg): Skip over \}s and tabs but give a
+ warning.
+ * troff/token.h (token::tab): New function.
+ * troff/node.c (get_fontno): Use tok.skip() rather than has_arg().
+ * troff/reg.c (alter_format): Likewise.
+ * troff/node.c (bold_font): Use has_arg() rather than tok.skip().
+
+Tue Aug 14 10:11:21 1990 James Clark (jjc at yquem)
+
+ * troff (most files): Redo warnings. Divide warnings into various
+ categories; warning() has an additional first argument indicating
+ the category it falls into.
+ * troff/input.c (main): -w now takes an argument. New option -W.
+ (enable_warning, disable_warning): New functions.
+
+ * ps/devps/afmtodit: Add -a option to lie about the italic angle.
+ * ps/devps/FontMakefile: Pretend TI has an angle of 7.
+
+Mon Aug 13 10:11:16 1990 James Clark (jjc at yquem)
+
+ * ps/devps/eqnchar: Better definitions of dotdot, vec, dyad, inf.
+ * xditview/devX100/eqnchar: Likewise. Remove definition of dot.
+ * xditview/devX75/eqnchar: Likewise.
+ * dvi/devdvi/eqnchar: Better definitions of vec, dyad, dotdot.
+
+ * eqn/other.c: When bar or over applies to a single character
+ don't produce an overline_box or an underline_box. Instead produce
+ an accent_box or an underaccent_box, with the accent a line
+ whose width is accent_width. New classes underaccent_box,
+ overline_char_box and underline_char_box.
+ * eqn/box.h: Move overline_box, underline_box, accent_box class
+ declarations into eqn/other.c. Add declarations of
+ make_underline_box, make_overline_box, make_accent_box.
+ * eqn/eqn.y: Call make_overline_box, make_underline_box
+ make_accent_box instead of constructors.
+ * eqn/pbox.h, eqn/box.c: Add accent_width parameter.
+
+ * eqn/other.c: Add accent_box::~accent_box.
+ * eqn/box.h: Declare it.
+
+ * groff.sh: With -Tps, use eqn -D.
+
+ * eqn/other.c (overline_box::output): Use \Z. If draw_flag use \D
+ rather than \l.
+ (underline_box::output): Similarily.
+ (accent_box::output): Use \Z.
+
+ * xditview/tmac.X: Add definitions of ~ and ^ (so that they are a
+ bit smaller.)
+
+Sun Aug 12 09:41:15 1990 James Clark (jjc at yquem)
+
+ * troff/div.c (top_level_diversion::transparent_output(unsigned
+ char)): Use asciify.
+ * troff/input.c (asciify): Don't make it static.
+ * troff/token.h (asciify): Declare it.
+
+ * troff/input.c (get_name, get_long_name, token::get_char,
+ token::delimiter): Add an extra default argument which says
+ whether a warning should be printed.
+ * troff: Pass a non-zero argument to one of these rather than
+ printing a warning directly.
+
+Sat Aug 11 09:02:21 1990 James Clark (jjc at yquem)
+
+ * troff: Consistently use symbol::is_null.
+
+ * troff/dictionary.h: Move some inline functions into
+ dictionary.c.
+
+ * troff/request.h: Move inline functions into input.c.
+ (request_or_macro::invoke): Make it pure.
+
+ * troff/input.c, troff/reg.h: New class `constant_int_reg'.
+ * troff/input.c (init_input_requests): Use class constant_int_reg.
+ (class compatible_reg): Deleted.
+ * troff/div.c (init_div_requests): Use class constant_int_reg.
+ (class last_post_line_extra_space_reg): Deleted.
+
+ * troff/env.c (tab_character): Don't change the tab character if
+ we get an invalid argument.
+ (hyphen_char): Similarily.
+
+ * troff/reg.c (alter_format): Check that nm is not null.
+
+ * Makefile, groff.sh: Make it possible to customize the commands
+ used for printing PostScript and dvi files. Also make it possible
+ to customize the path used by groff.sh.
+
+ * eqn/eqn.y: Make `left' right associative.
+
+Fri Aug 10 18:20:39 1990 James Clark (jjc at yquem)
+
+ * pic/pic.h: Added definition of M_SQRT2 for those systems that
+ don't have it.
+
+ * pic/pic.h: Removed definition of INT_MAX.
+
+ * troff/node.c (italic_corrected_node::vertical_extent): Omit
+ `return'.
+
+ * troff/input.c (token::next): Handle \R like \n.
+
+Tue Aug 7 09:46:33 1990 James Clark (jjc at yquem)
+
+ * ps/tmac.pc (PSPIC): Simplify.
+
+ * troff/env.c (tab_stops::to_string):
+ * pic/pic.y (object_type_name):
+ * pic/troff.c (simple_output::line):
+ * pic/tex.c (tex_output::spline):
+ * pic/object.c (object_spec::make_object):
+ * tbl/main.c (process_data): Add cases to switch statements to
+ avoid cfront warnings. (Some of these are spurious, since the
+ switch already has a default case.)
+
+ * ps/tmac.ps (PSPIC): Reformatted. Prefix all local names with
+ `ps-'. Don't test systat; instead check number of arguments to
+ ps-bb.
+
+Mon Aug 6 00:13:07 1990 James Clark (jjc at yquem)
+
+ * macros/tmac.e: Do not decrease the page offset by 0.5i.
+
+ * ps/ps.c (ps_printer::ps_printer): Use mktemp instead of tempnam.
+ Unlink the file as soon as we have opened it, so that we don't
+ have to bother with signal handlers.
+ (handler): Deleted.
+ (fatal_error_exit): Deleted.
+ (main): Don't call signal.
+
+ * dvi/tfmtodit.c: Add -k option so that kerns with the skewchar
+ can be ignored.
+ * dvi/devdvi/Makefile: Use the -k option with S and MI.
+
+ * pic/pic.y: If there is a label, or an nth construction before
+ the first `.' in the argument to `with', ignore it and generate a
+ warning.
+ * pic/lex.c (lex_warning): New function.
+
+ * tbl/table.c (table::init_output): In section keep and release
+ macro, use 0 indent when diverting and the correct indent when
+ rereading.
+
+ * troff/input.c (interpolate_number_format): Do not interpolate
+ anything if the number register is not defined.
+
+ * tbl/main.c (process_data): Don't add entry when col >= ncolumns.
+
+Sat Aug 4 08:12:05 1990 James Clark (jjc at yquem)
+
+ * ps/devps/prologue (PICTURE): Set components of graphics state to
+ their default values.
+
+ * ps/devps/text.enc: Add trademark
+ * ps/devps/textmap: Add names for club, spade, heart, diamond,
+ carriagereturn, suchthat. Use Upsilon1 rather than Upsilon.
+ * ps/devps/symbolchars: Add names for summation and product.
+
+ * dvi/devdvi/texsy.map: Add names for club, spade, heart, diamond,
+ suchthat. Add pp. Add upper-case letters.
+
+ * xditview/libXdvi/DviChar.c: Add names for club, spade, heart,
+ diamond, carriagereturn, suchthat. Use Upsilon1 rather than
+ Upsilon.
+
+ * dvi/devdvi/texsy.map: Rename lA (left angle bracket) to la, and
+ rA (right angle bracket) to ra. Introduce names for double-headed
+ arrows and double-barred arrows: <>, va, lA, rA, hA, uA, dA, vA.
+ * ps/devps/textmap: Likewise for ps device.
+ * xditview/libXdvi/DviChar.c: Likewise for X100 and X75 devices.
+ * tty/devascii/R.proto: Rename lA to la and rA to ra.
+ * tty/devascii/R.proto: Likewise.
+ * tty/tmac.tty: Provide definitions for \(<>, \(lA, \(rA, \(hA,
+ \(uA, \(dA.
+ * eqn/delim.c: In delim_table, rename \(lA to \(la and \(rA to \(ra.
+
+ * xditview/tmac.X: Add definitions for \(fi \(fl \(ff \(Fi \(Fl.
+
+ * eqn/lex.c: Added definitions of `approx', `grad' and `del' to
+ def_table.
+
+Fri Aug 3 09:59:27 1990 James Clark (jjc at yquem)
+
+ * troff/div.c (when_request): Use symbol::is_null rather than
+ has_arg to determine whether we have an argument.
+ (change_trap): Remove the trap if we get an invalid number. Give
+ an error if we don't get at least the macro name.
+ (diversion_trap): Remove trap if we get an invalid name or number.
+
+ * troff/env.c (environment_switch): Pop if we get an invalid
+ symbol or numeric expression.
+
+ * troff/input.c (do_define_macro): If EOF is encoutered while
+ defining the macro, do tok.next() before returning.
+
+ * troff/token.h (has_arg): Move definition from here, to ...
+ * troff/input.c (has_arg): ... here
+
+ * troff/env.c (space_size): Do nothing if we get an invalid argument.
+ * troff/input.c (shift): Likewise.
+
+ * pic/lex.c (get_token_after_dot): Accept `.center' as a synonym
+ for `.c'.
+
+ * pic/troff.c (troff_output::start_picture): Comment out calls to
+ `..'.
+
+ * eqn/main.c (do_file): Subtract 1 from current_lineno if
+ interpret_lf_args succeeds.
+
+ * eqn/main.c (do_file): Don't recognize delimiter if preceded by
+ \\. This avoids problems with \$N.
+
+ * groff.sh: Pass -C to preprocessors.
+
+ * lib/lf.c (interpret_lf_args): Be more flexible.
+
+ * tbl/main.c (main): Add -C option.
+ (table_input::get): Do not recognize TE if followed by character
+ other than a space or newline unless -C option given.
+ (process_input_file): Likewise for lf, TS.
+ (process_data): Likewise for lf in text blocks.
+
+ * eqn/main.c (main): Add -C option.
+ (do_file): Don't recognize EQ, EN or lf if followed by character
+ other than space or newline unless -C option given.
+ * eqn/lex.c (file_input::read_line): Similarily.
+ * eqn/eqn.h: Declare compatible_flag.
+
+ * etc/soelim.c (main): Add -C option.
+ (interpret_lf_args): Use version in libgroff.
+ (do_file):
+
+ * pic/main.c (main): Add -C option, which sets compatible_flag.
+ (top_input::get), (top_input::peek): If -C option not given,
+ do not recognize .PS/.PE/.PF/.lf if followed by a character
+ other than space or newline.
+ * pic/lex.c (file_input::read_line): Similarily.
+ * pic/pic.h: Add declaration of compatible_flag.
+
+Thu Aug 2 11:11:27 1990 James Clark (jjc at yquem)
+
+ * ps/tmac.ps (PSPIC): Avoid use of `echo -n'.
+
+ * troff/node.c, troff/node.h: Add `asciify' methods to classes
+ derived from node. New class space_char_hmotion_node.
+ * troff/input.c (asciify_macro): New function.
+ * troff/input.c (init_input_requests): New request `asciify' bound
+ to asciify_macro.
+ * macros/mm.diff: New file.
+ * Makefile: In install.mm target use `patch' to apply
+ macros/mm.diff.
+
+ * troff/input.c (macro::print_size): Just print the size in bytes.
+
+ * troff/div.c (return_request): Correct the argument
+ interpretation.
+
+Wed Aug 1 12:38:36 1990 James Clark (jjc at yquem)
+
+ * troff/node.h (class composite_node): Add sz member.
+ * troff/node.c (composite_node::size): Return sz.
+ * troff/input.c (macro_to_node): Use the initial size in the
+ environment as the size of the composite_node.
+
+ * troff/node.c (node::zero_width_tprint): Provide a reasonable
+ default.
+
+Tue Jul 31 10:07:10 1990 James Clark (jjc at yquem)
+
+ * troff/div.c (change_trap): If we get a bad number expression,
+ do nothing.
+
+Mon Jul 30 10:30:49 1990 James Clark (jjc at yquem)
+
+ * lib/matherr.c (matherr): Define this only if math.h defines
+ TLOSS.
+
+Sun Jul 29 10:34:27 1990 James Clark (jjc at yquem)
+
+ * troff/div.c (macro_diversion::distance_to_next_trap): If there
+ no diversion trap return vunits(INT_MAX - vresolution).
+
+Sat Jul 28 14:28:14 1990 James Clark (jjc at yquem)
+
+ * troff/input.c (do_zero_width): New implementation that doesn't
+ use a temporary environment. Use instead:
+ (token::add_to_node_list): New function.
+ * troff/env.c (environment::get_prev_char_height),
+ (environment::get_prev_char_height),
+ (environment::get_prev_char_skew): New functions.
+ (environment::get_prev_char): New function.
+ (environment::get_prev_char_width): Change to use get_prev_char.
+ (init_env_request): Implement new registers .cht, .cdp, .csk.
+ * eqn/sqrt.c (sqrt_box::output): Don't rely upon the argument to
+ \Z being processed in a separate environment.
+
+Fri Jul 27 10:21:25 1990 James Clark (jjc at yquem)
+
+ * tbl/table.c: Removed TABLE_BOTTOM_REG.
+
+ * tbl/table.c (table::init_output): In the section release macro,
+ give a warning message if the section won't fit on one page.
+
+ * tbl/table.c (table::do_top): Emit table keep only if table is
+ boxed.
+ (table::do_bottom): Likewise for table release.
+ (table::table), (table::add_vertical_rule): Remove reference to
+ keep member.
+ * tbl/table.h: Remove keep member.
+
+ * tbl/table.c: New register SUPPRESS_BOTTOM_REG. In
+ SECTION_RELEASE_MACRO, if there's not enough space before the next
+ trap to output the diversion, call T# ourselves, set
+ SUPPRESS_BOTTOM_REG to 1, spring the trap, then set
+ SUPPRESS_BOTTOM_REG back to 0. In T#, do nothing if
+ SUPPRESS_BOTTOM_REG is non-zero. In T#, always mark the current
+ vertical position and return to it before turning traps on again.
+
+Thu Jul 26 02:54:32 1990 James Clark (jjc at yquem)
+
+ * troff/node.c, troff/node.h: In classes derived from node,
+ replace prev_char_width method by last_char_node method.
+ * troff/env.c (environment::get_prev_char_width): Use
+ node::last_char_node rather than node::get_prev_char_width.
+
+ * Makefile: Added comment about -fno-inline on 68030-based
+ Apollos.
+
+ * troff/reg.c (number_format_to_ascii), eqn/delim.c (DELIM_TABLE_SIZE),
+ tty/tty.c (tty_font::load_tty_font), dvi/tfmtodit.c (main): Cast
+ expressions using sizeof to int.
+ * dvi/dvi.c (dvi_font::handle_x_command): Avoid long->int warnings.
+
+ * macros/tmac.e (TS): Don't move @f back past the current
+ position.
+
+Wed Jul 25 09:11:08 1990 James Clark (jjc at yquem)
+
+ * ps/ps.c (main): Buffer stderr.
+ * dvi/dvi.c (main): Likewise.
+ * tty/tty.c (main): Likewise.
+
+ * ps/ps.c (ps_printer::do_import): Improve error handling.
+
+ * troff/input.c (abort_request): Use asciify.
+
+ * driver/printer.h (printer::draw), driver/printer.c (printer::draw),
+ ps/ps.c (ps_printer::draw), dvi/dvi.c (dvi_printer::draw): Make
+ type of first argument int rather than char. This works around a
+ bug on the 68030 based Apollo using g++ 1.37.1.
+
+ * tbl/table.h (class table): Add `keep' member.
+ * tbl/table.c (table::table): Initialize `keep'.
+ (table::add_vertical_rule): Set `keep' to 1.
+ (table::do_top): Only emit table keep macro is `keep' is non-zero.
+ (table::do_bottom): Likewise for table release macro.
+ (table::do_row): Emit section keep macro even if the row is 0.
+
+Tue Jul 24 08:35:07 1990 James Clark (jjc at yquem)
+
+ * macros/tmac.e (@C): Preserve the font family across the change
+ in environments.
+
+Mon Jul 23 10:15:23 1990 James Clark (jjc at yquem)
+
+ * lib/font.c: Initialize font::hor and font::vert to 1.
+ (font::load_desc): Check the values of font::hor and font::vert.
+
+ * lib/lib.h: Added definition of INT_DIGITS. Fix it so that it can
+ be included in a C compilation.
+ (iftoa): Use INT_DIGITS. Include lib.h.
+ (itoa): Likewise.
+ (as_string): Likewise.
+ * tbl/table.c: Removed definition of INT_DIGITS.
+ * eqn/box.c (box::top_level): Use INT_DIGITS + 1 instead of 12.
+ * troff/input.c (input_input_requests): Likewise.
+ * ps/ps.c (make_encoding_name): Likewise.
+ (ps_printer::set_style): Likewise.
+ (ps_output::put_number): Use 1 + INT_DIGITS + 1 instead of 12.
+
+ * tty/devascii/R.proto: Map fm onto '.
+ * tty/devlatin1/R.proto: Likewise.
+
+Sat Jul 21 12:45:07 1990 James Clark (jjc at yquem)
+
+ * tbl/table.c: Use ' instead of DELIMITER_CHAR in places where the
+ argument to \w is at a different input level.
+
+ * tbl/table.c (table::init_output): Define a new macro
+ REPEATED_VPT_MACRO, like vpt but if in a diversion also
+ transparently outputs itself.
+ (table::define_bottom_macro): Use REPEATED_VPT_MACRO instead of
+ vpt.
+ (table::do_row): Likewise.
+
+ * tbl/table.c (vertical_rule::print): Prefix the .sp -1 line with
+ TRANSPARENT_STRING_NAME.
+
+ * tbl/table.c (table::init_output): In the table release macro
+ print an error message and don't produce any output if after
+ issuing the need request the table still will not fit. Also
+ remove the diversion after bringing it back.
+
+ * tbl/table.c (table::init_output): Define a new macro
+ REPEATED_MARK_MACRO, like mk but if in a diversion also
+ transparently outputs itself.
+ (table::do_row): Mark row_top_reg using REPEATED_MARK_MACRO. This
+ is necessary because .TH might not call .T#.
+ (table::do_top): Likewise TOP_REG.
+ (table::define_bottom_macro): If TOP_REG is no longer valid, use
+ #T - DOUBLE_LINE_SEP rather than #T. This is necessary because the
+ table header might contain just the two top rules.
+
+Fri Jul 20 10:51:42 1990 James Clark (jjc at yquem)
+
+ * troff/div.c: Implement new request `ptr' to print all traps.
+
+ * troff/env.c (init_env_requests): Implement `.tabs' reg with
+ init_string_env_reg.
+ * troff/env.c (class tab_reg): Deleted.
+
+Thu Jul 19 12:07:16 1990 James Clark (jjc at yquem)
+
+ * troff/div.c: New number register .pn returns the number of the
+ next page as set by the pn request.
+
+ * macros/tmac.an: Redid headers and footers. Number each manual
+ entry starting from 1 unless \nC is > 0, like Sun. Added an
+ optional 5th argument to .TH which specifies the manual name and
+ appears in the center of the header. Understand the X, P and D
+ registers like Sun.
+
+Wed Jul 18 10:23:31 1990 James Clark (jjc at yquem)
+
+ * troff/env.c (init_env_requests): New number register `.lt' to
+ return the title length.
+
+ * troff/node.h (class transparent_dummy_node): New class.
+ * troff/node.c (class transparent_dummy_node): Provide member
+ functions.
+ * troff/env.c (interrupt): Add a transparent_dummy_node, rather
+ than a dummy_node.
+
+ * troff/input.c (token::next): New escape sequence \).
+ * troff/input.c (get_copy): Recognize \) in copy mode.
+
+ * troff/input.c (input_stack::clear): New function.
+ * troff/input.c (exit_request): Use input_stack::clear.
+
+ * troff/token.h: Removed TOKEN_NO_PRINT_CHAR.
+ * troff/input.c (token::process): Removed case TOKEN_NO_PRINT_CHAR.
+
+ * troff/env.c: Move set_page_character to input.c. Move
+ page_character to input.c also.
+ * troff/env.c (title): Split off the reading of the parts of the
+ title into read_title_parts.
+ * troff/input.c (read_title_parts): New function. Check the
+ input_level when testing whether a token matches the delimiter.
+
+ * troff/input.c (exit_request): New function.
+ * troff/input.c (init_input_requests): Bind ex request to
+ exit_request rather than exit_groff.
+
+ * troff/input.c (exit_groff): Call tok.next() before
+ process_input_stack().
+
+Mon Jul 16 09:47:23 1990 James Clark (jjc at yquem)
+
+ * troff/env.c: ifdef widow control support on WIDOW_CONTROL.
+ * troff/env.h: ditto.
+ * troff/input.c: ditto.
+
+ * troff/env.c (environment::is_empty): Test pending_lines.
+
+ * troff/env.c (environment::have_pending_lines): Removed.
+
+ * troff/input.c: Add request to flush pending lines from the
+ environment.
+
+ * troff/env.c, troff/env.h: Add automatic widow control feature.
+
+ * troff/input.c (exit_groff): Do process_input_stack() after
+ do_break() but before setting exit_flag to 2.
+
+ * troff/input.c: Remove FLUSH_PENDING_LINES and
+ TOKEN_FLUSH_PENDING_LINES. Instead, flush pending lines from
+ environment after END_TRAP token seen, but only if there aren't
+ any more traps still unfinished.
+ * troff/token.h: Remove TOKEN_FLUSH_PENDING_LINES.
+
+Sun Jul 15 10:50:08 1990 James Clark (jjc at yquem)
+
+ * troff/env.c: Rename the `retain_size' member of class
+ pending_output_line to `no_fill'.
+
+ * troff/env.c (title): When the line is output, make the
+ retain_size argument !fill.
+
+ * troff/node.h: Add `hyphenated' member to struct breakpoint.
+ * troff/node.c (space_node::get_breakpoints),
+ (dbreak_node::get_breakpoints): Fill this in.
+ * troff/env.c: Allow specification of maximum number of
+ consecutive hyphenated lines.
+
+ * troff/env.c (environment::is_empty): Add test for !current_tab.
+
+Sat Jul 14 11:23:01 1990 James Clark (jjc at yquem)
+
+ * troff/env.c (environment::hyphenate_line): Don't completely give
+ up if the word is not to be hyphenated; continue so that breaks
+ can be made at break_char_node's.
+
+ * lib/lib.h: Only define INT_MAX if it's not already defined;
+ undef INT_MIN if it's already defined.
+
+ * Makefile: Make it easy to define CFRONT_ANSI_BUG.
+
+ * lib/lib.h: If CFRONT_ANSI_BUG is defined, cast INT_MIN to long.
+ This works around a bug in AT&T C++ 2.0 used with an ANSI C
+ compiler.
+
+ * macros/tmac.an (an-header): Set no-space mode.
+
+ * macros/tmac.an (TH): Start a new page if necessary.
+
+ * Started using ChangeLog at version 0.4.
+
+Local Variables:
+version-control: never
+End:
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 000000000..ce3ac520e
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,272 @@
+Groff has been compiled on a Sun 4 under SunOS 4.1.1 with g++ 1.39.1
+and with AT&T C++ 2.0, and on a 386 PC under 386/ix 2.0.1 with g++
+1.37.1 using Michael Bloom's GNU COFF patches. You may encounter
+problems on other machines that I cannot anticipate.
+
+If you are using g++, you will need to install the header files from
+libg++. The only other parts of libg++ used by groff are contained in
+the files xyzzy.c and gnulib3.c; the libg++.a that I use contains only
+xyzzy.o and gnulib3.o. You don't need xyzzy.o unless you're using GNU
+ld.
+
+If you're using g++ 1.39 on a sparc you'll probably want to apply the
+following fix (from Casper H.S. Dik):
+
+*** config/out-sparc.c.org Wed Dec 12 03:13:57 1990
+--- config/out-sparc.c Sat Feb 23 23:21:26 1991
+***************
+*** 908,925 ****
+ else if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
+ {
+ rtx inc_reg = XEXP (XEXP (operands[1], 0), 0);
+ if (inc_reg == frame_pointer_rtx
+ && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == REG
+! && XEXP (XEXP (operands[1], 0), 0) != frame_pointer_rtx)
+ inc_reg = XEXP (XEXP (operands[1], 0), 1);
+ if (inc_reg == frame_pointer_rtx)
+ {
+ output_asm_insn ("mov %%fp,%%g1", xoperands);
+ inc_reg = gen_rtx (REG, SImode, 1);
+ }
+ xoperands[1] = inc_reg;
+ output_asm_insn ("add 4,%1,%1", xoperands);
+! xoperands[1] = operands[1];
+ output_asm_insn ("ld %1,%0", xoperands);
+ xoperands[1] = inc_reg;
+ output_asm_insn ("add -4,%1,%1", xoperands);
+--- 908,931 ----
+ else if (GET_CODE (XEXP (operands[1], 0)) == PLUS)
+ {
+ rtx inc_reg = XEXP (XEXP (operands[1], 0), 0);
++ rtx from = operands[1];
+ if (inc_reg == frame_pointer_rtx
+ && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == REG
+! && XEXP (XEXP (operands[1], 0), 1) != frame_pointer_rtx)
+ inc_reg = XEXP (XEXP (operands[1], 0), 1);
+ if (inc_reg == frame_pointer_rtx)
+ {
+ output_asm_insn ("mov %%fp,%%g1", xoperands);
+ inc_reg = gen_rtx (REG, SImode, 1);
++ from = gen_rtx (GET_CODE (operands[1]),
++ GET_MODE (operands[1]),
++ gen_rtx (PLUS, GET_MODE (XEXP (operands[1], 0)),
++ inc_reg,
++ XEXP (XEXP (operands[1], 0), 1)));
+ }
+ xoperands[1] = inc_reg;
+ output_asm_insn ("add 4,%1,%1", xoperands);
+! xoperands[1] = from;
+ output_asm_insn ("ld %1,%0", xoperands);
+ xoperands[1] = inc_reg;
+ output_asm_insn ("add -4,%1,%1", xoperands);
+***************
+*** 989,1006 ****
+ else if (GET_CODE (XEXP (operands[0], 0)) == PLUS)
+ {
+ rtx inc_reg = XEXP (XEXP (operands[0], 0), 0);
+ if (inc_reg == frame_pointer_rtx
+ && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG
+! && XEXP (XEXP (operands[0], 0), 0) != frame_pointer_rtx)
+ inc_reg = XEXP (XEXP (operands[0], 0), 1);
+ if (inc_reg == frame_pointer_rtx)
+ {
+ output_asm_insn ("mov %%fp,%%g1", xoperands);
+ inc_reg = gen_rtx (REG, SImode, 1);
+ }
+ xoperands[0] = inc_reg;
+ output_asm_insn ("add 4,%0,%0", xoperands);
+! xoperands[0] = operands[0];
+ output_asm_insn ("st %r1,%0", xoperands);
+ xoperands[0] = inc_reg;
+ output_asm_insn ("add -4,%0,%0", xoperands);
+--- 995,1018 ----
+ else if (GET_CODE (XEXP (operands[0], 0)) == PLUS)
+ {
+ rtx inc_reg = XEXP (XEXP (operands[0], 0), 0);
++ rtx to = operands[0];
+ if (inc_reg == frame_pointer_rtx
+ && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG
+! && XEXP (XEXP (operands[0], 0), 1) != frame_pointer_rtx)
+ inc_reg = XEXP (XEXP (operands[0], 0), 1);
+ if (inc_reg == frame_pointer_rtx)
+ {
+ output_asm_insn ("mov %%fp,%%g1", xoperands);
+ inc_reg = gen_rtx (REG, SImode, 1);
++ to = gen_rtx (GET_CODE (operands[0]),
++ GET_MODE (operands[0]),
++ gen_rtx (PLUS, GET_MODE (XEXP (operands[0], 0)),
++ inc_reg,
++ XEXP (XEXP (operands[0], 0), 1)));
+ }
+ xoperands[0] = inc_reg;
+ output_asm_insn ("add 4,%0,%0", xoperands);
+! xoperands[0] = to;
+ output_asm_insn ("st %r1,%0", xoperands);
+ xoperands[0] = inc_reg;
+ output_asm_insn ("add -4,%0,%0", xoperands);
+
+On a Sun 3 and other 68k machines, using libg++ 1.37.0 you will need
+to apply the following change to g++-include/math.h:
+
+*** math.h- Sat Jan 6 14:09:52 1990
+--- math.h Tue Mar 13 02:07:01 1990
+***************
+*** 32,39 ****
+
+
+ #ifdef __HAVE_68881__ /* MC68881/2 Floating-Point Coprocessor */
+- #include <math-68881.h>
+ extern "C" { /* fill in what we've left out */
+
+ double acosh(double);
+ double asinh(double);
+--- 32,39 ----
+
+
+ #ifdef __HAVE_68881__ /* MC68881/2 Floating-Point Coprocessor */
+ extern "C" { /* fill in what we've left out */
++ #include <math-68881.h>
+
+ double acosh(double);
+ double asinh(double);
+
+If you have bison 1.11, you will need to apply the following fix to
+bison.simple if you want change any of the grammars:
+
+*** bison.simple.~1~ Fri Aug 10 12:13:41 1990
+--- bison.simple Fri Aug 10 12:24:46 1990
+***************
+*** 20,26 ****
+--- 20,28 ----
+
+
+ #ifdef __GNUC__
++ #ifndef alloca
+ #define alloca __builtin_alloca
++ #endif /* Not alloca. */
+ #else /* Not GNU C. */
+ #if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
+ #include <alloca.h>
+***************
+*** 114,123 ****
+--- 116,129 ----
+ /* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+ static void
++ #ifdef __cplusplus
++ __yy_bcopy (char *from, char *to, int count)
++ #else
+ __yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
++ #endif
+ {
+ register char *f = from;
+ register char *t = to;
+***************
+*** 127,133 ****
+ *t++ = *f++;
+ }
+
+! #line 131 "/usr/local/lib/bison.simple"
+ int
+ yyparse()
+ {
+--- 133,139 ----
+ *t++ = *f++;
+ }
+
+! #line 137 "/usr/local/lib/bison.simple"
+ int
+ yyparse()
+ {
+
+On a Sequent Symmetry S27 running Dynix 3.0.17, you'll need to use GNU
+make or add 'MAKE=make' to Makefiles which use $(MAKE). You'll also
+need to change
+
+ dev=${GROFF_TYPESETTER:-@DEVICE@}
+
+to
+
+ dev=$GROFF_TYPESETTER
+
+in groff.sh. You should use gcc to compile xditview.
+
+You should only have to edit the top-level Makefile. The comments
+should make it clear what has to be changed. If you don't have a
+separate directory tree for local manual pages you can make
+MAN[157]EXT be l (that's an ell) or n, and MANROOT be /usr/man. The
+changes you make to the top-level Makefile will be propagated to
+sub-makes, but this won't happen if you invoke make in the
+sub-directories.
+
+You might also need to edit groff.sh. This is a shell-script that
+runs gtroff, an appropriate postprocessor and optionally various
+preprocessors. (Actually, the shell-script is created from groff.sh
+by substituting for some variables surrounded by @s). If your kernel
+doesn't understand #!, you will need to arrange for the script to be
+run by /bin/sh in some other way.
+
+If you want to use existing troff drivers you should change groff.sh
+so that it recognises them. It is also a good idea to copy over the
+dev* directory for the device into a directory that's only searched by
+groff (eg /usr/local/lib/groff/font), so that you can take advantage
+of the groff extensions to the DESC and font formats. Groff only uses
+the ASCII versions of the device files so you only need copy them. If
+you want to use GNU eqn, it is essential that the font files contain
+correct height and depth information. The format for this information
+is described in the groff_font(5) page. The best way to add this
+information is to modify the program that generates the font files.
+As a last resort you could try using the program addftinfo: it
+attempts to guess plausible heights and depths. To obtain good
+results you would probably have to do more work on addftinfo.
+
+To compile everything, just do a `make'. If that works, then do a
+`make install'.
+
+If you have problems compiling pic/pic.tab.c or eqn/eqn.tab.c, you might
+want to try using your system's yacc. Set YACC=yacc in the top-level
+Makefile, and also do
+
+ mv pic/pic.tab.c pic/pic.tab.c.dist
+ mv eqn/eqn.tab.c eqn/eqn.tab.c.dist
+
+so that the parsers will be regenerated using yacc (the supplied
+*.tab.[ch] files were generated by bison.)
+
+If you want to install xditview, you'll need to do that separately:
+change directory to xditview, edit the Makefile, do a make and a make
+install. You'll need to be running X11R4.
+
+The dvi files produced by grodvi can use fonts at non-standard
+magnifications. You may need to compile fonts with Metafont at these
+magnifications. The CompileFonts script in the dvi/devdvi directory
+may help you to do this. (It will take a *long* time.)
+
+If you have problems printing existing troff documents, read the
+section on `Incompatbilities' in gtroff(1). If you have existing
+macro packages that are in the habit of omitting the space between a
+macro or request and its arguments, it is good idea to produce a
+version with spaces so that you can use it with groff (without having
+to use the -C flag). The file macros/fixmacros.sed is a sed script
+which will attempt to edit a file of macros so that it can be used
+with groff without the -C flag. If you have the DWB 2.0 mm macros
+installed on your machine, you might want to do a `make install.mm';
+this will copy the mm macros to groff's macro directory and fix a few
+problems that occur when using the mm macros with groff; this requires
+the `patch' program. If the patch in macros/mm.diff is rejected,
+carefully apply it by hand.
+
+You can share groff with a friend who has the same type of machine as
+you, but does not have a C++ compiler. First do `make bindist'; this
+will create a subdirectory `bindist' containing a set of binaries, a
+Makefile and a README. If you want to strip the binaries, now do a
+`make strip' in the bindist directory. Rename the bindist directory
+to something more meaningful, tar it up, and give to your friend along
+with the original groff source distribution. Your friend can then
+install groff just by editing the Makefile in the bindist directory
+and doing a make there; this will automatically install the non-binary
+parts of the groff source distribution as well as the binaries from
+the bindist directory.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..9a1703758
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,249 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The license agreements of most software companies try to keep users
+at the mercy of those companies. By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must tell them their rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications. Each
+licensee is addressed as "you".
+
+ 1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program. You may charge a fee for the physical act of
+transferring a copy.
+
+ 2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating that
+ you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish, that
+ in whole or in part contains the Program or any part thereof, either
+ with or without modifications, to be licensed at no charge to all
+ third parties under the terms of this General Public License (except
+ that you may choose to grant warranty protection to some or all
+ third parties, at your option).
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use
+ in the simplest and most usual way, to print or display an
+ announcement including an appropriate copyright notice and a notice
+ that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this General
+ Public License.
+
+ d) You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+
+ 3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal charge
+ for the cost of distribution) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it. For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+ 4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License. However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+ 5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+
+ 7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+ 8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program 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 1, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19xx name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ program `Gnomovision' (a program to direct compilers to make passes
+ at assemblers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..000de0282
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,361 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Read this Makefile up to the line that says:
+# End of configuration section.
+
+# Define PAGE to be letter if your PostScript printer uses 8.5x11 paper (USA)
+# and define it to be A4, if it uses A4 paper (rest of the world).
+PAGE=A4
+#PAGE=letter
+
+# BINDIR says where to install executables.
+BINDIR=/usr/local/bin
+
+GROFFLIBDIR=/usr/local/lib/groff
+
+# FONTDIR says where to install dev*/*.
+FONTDIR=$(GROFFLIBDIR)/font
+
+# FONTPATH says where to look for dev*/*.
+FONTPATH=.:$(FONTDIR):/usr/local/lib/font:/usr/lib/font
+
+# MACRODIR says where to install macros.
+MACRODIR=$(GROFFLIBDIR)/tmac
+
+# MACROPATH says where to look for macro files.
+MACROPATH=.:$(MACRODIR):/usr/lib/tmac
+
+# DEVICE is the default device.
+DEVICE=ps
+
+# PSPRINT is the command to use for printing a PostScript file.
+# It must be a simple command, not a pipeline.
+PSPRINT=lpr
+
+# DVIPRINT is the command to use for printing a TeX dvi file.
+# It must be a simple command, not a pipeline.
+DVIPRINT=lpr -d
+
+# HYPHENFILE is the file containing the hyphenation patterns.
+HYPHENFILE=$(GROFFLIBDIR)/hyphen
+
+# Suffix to be used for refer index files. Index files are not
+# shareable between different architectures, so you might want to use
+# different suffixes for different architectures. Choose a suffix
+# that doesn't conflict with refer or any other indexing program.
+INDEX_SUFFIX=.i
+
+# Directory containing the default index for refer.
+DEFAULT_INDEX_DIR=/usr/dict/papers
+
+# The filename (without suffix) of the default index for refer.
+DEFAULT_INDEX_NAME=Ind
+
+# COMMON_WORDS_FILE is a file containing a list of common words.
+# If your system provides /usr/lib/eign it will be copied onto this,
+# otherwise the supplied eign file will be used.
+COMMON_WORDS_FILE=$(GROFFLIBDIR)/eign
+
+# MANROOT is the root of the man page directory tree.
+MANROOT=/usr/local/man
+
+# MAN1EXT is the man section for user commands.
+MAN1EXT=1
+MAN1DIR=$(MANROOT)/man$(MAN1EXT)
+
+# MAN5EXT is the man section for file formats.
+MAN5EXT=5
+MAN5DIR=$(MANROOT)/man$(MAN5EXT)
+
+# MAN7EXT is the man section for macros.
+MAN7EXT=7
+MAN7DIR=$(MANROOT)/man$(MAN7EXT)
+
+# The groff ms macros will be available as -m$(TMAC_S).
+# If you use `TMAC_S=s', you can use the Unix ms macros by using
+# groff -ms -M/usr/lib/tmac.
+TMAC_S=gs
+
+# Normally the Postscript driver, grops, produces output that conforms
+# to version 3.0 of the Adobe Document Structuring Conventions.
+# Unfortunately some spoolers and previewers can't handle such output.
+# The BROKEN_SPOOLER_FLAGS variable tells grops what it should do to
+# make its output acceptable to such programs. This variable controls
+# only the default behaviour of grops; the behaviour can be changed at
+# runtime by the grops -b option (and so by groff -P-b).
+# Use a value of 0 if your spoolers and previewers are able to handle
+# conforming PostScript correctly.
+# Add 1 if no %%{Begin,End}DocumentSetup comments should be generated;
+# this is needed for early versions of TranScript that get confused by
+# anything between the %%EndProlog line and the first %%Page: comment.
+# Add 2 if lines in included files beginning with %! should be
+# stripped out; this is needed for Sun's pageview previewer.
+# Add 4 if %%Page, %%Trailer and %%EndProlog comments should be
+# stripped out of included files; this is needed for spoolers that
+# don't understand the %%{Begin,End}Document comments. I suspect this
+# includes early versions of TranScript.
+# A value of 7 is equivalent to -DBROKEN_SPOOLER of earlier releases.
+BROKEN_SPOOLER_FLAGS=7
+
+# Include -DCFRONT_ANSI_BUG in CPPDEFINES if you are using AT&T C++
+# 2.0 with an ANSI C compiler backend.
+# Include -DOP_DELETE_BROKEN if you're using g++ 1.39.0 or 1.39.1.
+# Include -DHAVE_VFORK if you have vfork().
+# Include -DHAVE_SYS_SIGLIST if you have sys_siglist[].
+# Include -DHAVE_UNION_WAIT if wait() is declared by osfcn.h to take
+# an argument of type union wait * (Sun C++ does this). Don't include
+# it if you're using the libg++ header files.
+# Include -DHAVE_MMAP if you have the mmap() system call
+# (and you want to use it).
+# Include -DHAVE_RENAME if you have the rename() system call.
+# Include -DHAVE_GETWD if you have the getwd() library function.
+
+CPPDEFINES=#-DCFRONT_ANSI_BUG -DOP_DELETE_BROKEN -DHAVE_VFORK -DHAVE_SYS_SIGLIST -DHAVE_UNION_WAIT -DHAVE_MMAP -DHAVE_RENAME -DHAVE_GETWD
+
+# Uncomment the next line if you don't have fmod in your math library.
+# I believe this is needed on Ultrix and BSD 4.3.
+# FMOD=fmod.o
+
+# Uncomment the next line if you don't have strtol in your C library.
+# I believe this is needed on BSD 4.3.
+# STRTOL=strtol.o
+
+# Additional flags needed to compile the GNU Emacs malloc.
+# Use this with BSD.
+# MALLOCFLAGS=-DBSD
+# Use this with System V
+# MALLOCFLAGS=-DUSG
+# Use this with SunOS 4.1 and 4.1.1.
+MALLOCFLAGS=-DBSD -DSUNOS_LOCALTIME_BUG
+
+# Comment this out if the GNU malloc gives you problems, or if you would
+# prefer to use the system malloc.
+MALLOC=malloc.o
+
+GROFF=
+# Comment the next line out if groff.c gives problems.
+GROFF=groff
+
+# There is a new version of the grog program written in perl. This is
+# a little more capable than the previous version which was written in
+# shell; in particular, it can distinguish files that need to be run
+# through pic/eqn/tbl from files that have already been run through
+# pic/eqn/tbl. You can only use this version if you have perl
+# available. The first line of the perl program is `#!/usr/bin/perl';
+# if your system doesn't support `#!' or if there is no link to perl
+# in /usr/bin you'll have to edit etc/grog.pl by hand.
+# The next line should be uncommented if you want to use the old shell
+# version.
+GROG=grog.sh
+# The next line should be uncommented if you want to use the new perl
+# version.
+# GROG=grog.pl
+
+# CC is the C++ compiler
+CC=g++
+# I'm told that -fno-inline is needed on a 68030-based Apollo
+# CC=g++ -fno-inline
+
+# OLDCC is the C compiler.
+OLDCC=gcc
+
+PROFILE_FLAG=
+DEBUG_FLAG=-g
+OPTIMIZE_FLAG=-O
+WARNING_FLAGS=#-Wall -Wcast-qual -Wwrite-strings
+
+# Use this to pass additional flags on the command line.
+XCFLAGS=
+
+# CFLAGS are passed to sub makes
+CFLAGS=$(PROFILE_FLAG) $(DEBUG_FLAG) $(OPTIMIZE_FLAG) $(WARNING_FLAGS) \
+ $(CPPDEFINES) $(XCFLAGS)
+
+XOLDCFLAGS=
+# OLDCFLAGS are passed to sub makes
+OLDCFLAGS=$(DEBUG_FLAG) $(PROFILE_FLAG) $(OPTIMIZE_FLAG) $(XOLDCFLAGS)
+
+XLDFLAGS=
+LDFLAGS=$(PROFILE_FLAG) $(DEBUG_FLAG) $(XLDFLAGS)
+# Libraries needed for linking C++ programs.
+LIBS=
+# Libraries needed for linking C++ programs that use libm.a.
+MLIBS=$(LIBS) -lm
+
+AR=ar
+
+# Define RANLIB to be empty if you don't have ranlib.
+RANLIB=ranlib
+
+# YACC can be either yacc or bison -y
+YACC=bison -y
+YACCFLAGS=-v
+
+ETAGS=etags
+# Flag to make etags treat *.[ch] files as C++
+ETAGSFLAGS=-p
+
+# End of configuration section.
+# You shouldn't need to change anything after this point.
+
+SHELL=/bin/sh
+
+SUBDIRS=lib troff pic tbl eqn refer etc driver ps tty dvi macros man
+
+# SUBFLAGS says what flags to pass to sub makes
+SUBFLAGS="CC=$(CC)" "CFLAGS=$(CFLAGS)" "LDFLAGS=$(LDFLAGS)" \
+ "OLDCC=$(OLDCC)" "OLDCFLAGS=$(OLDCFLAGS)" \
+ "YACC=$(YACC)" "YACCFLAGS=$(YACCFLAGS)" \
+ "DEVICE=$(DEVICE)" "FONTPATH=$(FONTPATH)" "MACROPATH=$(MACROPATH)" \
+ "MALLOCFLAGS=$(MALLOCFLAGS)" "MALLOC=$(MALLOC)" \
+ "FMOD=$(FMOD)" "STRTOL=$(STRTOL)" "GROG=$(GROG)" \
+ "AR=$(AR)" "RANLIB=$(RANLIB)" "LIBS=$(LIBS)" "MLIBS=$(MLIBS)" \
+ "FONTDIR=$(FONTDIR)" "BINDIR=$(BINDIR)" "PAGE=$(PAGE)" \
+ "MACRODIR=$(MACRODIR)" "HYPHENFILE=$(HYPHENFILE)" \
+ "TMAC_S=$(TMAC_S)" "MAN1EXT=$(MAN1EXT)" "MAN1DIR=$(MAN1DIR)" \
+ "MAN5EXT=$(MAN5EXT)" "MAN5DIR=$(MAN5DIR)" \
+ "MAN7EXT=$(MAN7EXT)" "MAN7DIR=$(MAN7DIR)" \
+ "BROKEN_SPOOLER_FLAGS=$(BROKEN_SPOOLER_FLAGS)" \
+ "INDEX_SUFFIX=$(INDEX_SUFFIX)" \
+ "DEFAULT_INDEX_DIR=$(DEFAULT_INDEX_DIR)" \
+ "DEFAULT_INDEX_NAME=$(DEFAULT_INDEX_NAME)" \
+ "COMMON_WORDS_FILE=$(COMMON_WORDS_FILE)"
+
+INCLUDES=-Ilib
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: $(SUBDIRS) $(GROFF) shgroff
+
+$(SUBDIRS): FORCE
+ @cd $@; \
+ echo Making all in $@; \
+ $(MAKE) $(SUBFLAGS) all
+
+troff pic tbl eqn refer etc ps tty dvi: lib
+ps tty dvi: driver
+
+TAGS: FORCE
+ @for dir in $(SUBDIRS); do \
+ echo Making TAGS in $$dir; \
+ (cd $$dir; $(MAKE) "ETAGSFLAGS=$(ETAGSFLAGS)" "ETAGS=$(ETAGS)" TAGS); \
+ done
+
+topclean: FORCE
+ -rm -f shgroff
+ -rm -f groff *.o core device.h device.h.n
+
+clean: topclean FORCE
+ @for dir in $(SUBDIRS) doc; do \
+ echo Making clean in $$dir; \
+ (cd $$dir; $(MAKE) clean); done
+
+distclean: topclean FORCE
+ @for dir in $(SUBDIRS) doc; do \
+ echo Making distclean in $$dir; \
+ (cd $$dir; $(MAKE) distclean); done
+
+# You really don't want to use this target.
+realclean: topclean FORCE
+ @for dir in $(SUBDIRS) doc; do \
+ echo Making realclean in $$dir; \
+ (cd $$dir; $(MAKE) realclean); done
+
+install.nobin: FORCE shgroff
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -[ -d $(GROFFLIBDIR) ] || mkdir $(GROFFLIBDIR)
+ -[ -d $(MANROOT) ] || mkdir $(MANROOT)
+ @for dir in $(SUBDIRS); do \
+ echo Making install.nobin in $$dir; \
+ (cd $$dir; $(MAKE) $(SUBFLAGS) install.nobin); done
+ if [ -z "$(GROFF)" ] ; \
+ then rm -f $(BINDIR)/groff ; \
+ cp shgroff $(BINDIR)/groff ; fi
+
+install.bin: FORCE $(GROFF)
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ @for dir in $(SUBDIRS); do \
+ echo Making install.bin in $$dir; \
+ (cd $$dir; $(MAKE) $(SUBFLAGS) install.bin); done
+ if [ -n "$(GROFF)" ] ; \
+ then rm -f $(BINDIR)/groff ; \
+ cp groff $(BINDIR)/groff ; fi
+
+install: install.bin install.nobin
+
+install.mm: FORCE
+ -[ -d $(GROFFLIBDIR) ] || mkdir $(GROFFLIBDIR)
+ -[ -d $(MACRODIR) ] || mkdir $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.m
+ sed -f macros/fixmacros.sed -e 's;/usr/lib/tmac;$(MACRODIR);' \
+ /usr/lib/macros/mmt >$(MACRODIR)/tmac.m
+ -rm -f $(MACRODIR)/sys.name
+ sed -f macros/fixmacros.sed /usr/lib/tmac/sys.name \
+ >$(MACRODIR)/sys.name
+ patch -s $(MACRODIR)/tmac.m macros/mm.diff
+
+shgroff: groff.sh
+ @echo Making $@ from groff.sh
+ @-rm -f $@
+ @sed -e "s;@BINDIR@;$(BINDIR);" \
+ -e "s;@DEVICE@;$(DEVICE);" \
+ -e "s;@PROG_PREFIX@;$(PROG_PREFIX);" \
+ -e "s;@FONTDIR@;$(FONTDIR);" \
+ -e "s;@PSPRINT@;$(PSPRINT);" \
+ -e "s;@DVIPRINT@;$(DVIPRINT);" \
+ groff.sh >$@ || rm -f $@
+ @chmod +x $@
+
+groff: groff.o lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ groff.o lib/libgroff.a $(LIBS)
+
+lib/libgroff.a: lib
+
+device.h: FORCE
+ @echo \#define DEVICE \"$(DEVICE)\" >device.h.n
+ @echo \#define PSPRINT `$(SHELL) stringify $(PSPRINT)` >>device.h.n
+ @echo \#define DVIPRINT `$(SHELL) stringify $(DVIPRINT)` >>device.h.n
+ @cmp -s device.h device.h.n || cp device.h.n device.h
+ @rm -f device.h.n
+
+groff.o: device.h lib/lib.h lib/errarg.h lib/error.h lib/stringclass.h \
+ lib/font.h
+
+bindist: all VERSION Makefile.bd README.bd FORCE
+ -[ -d bindist ] || mkdir bindist
+ @topdir=`pwd`; \
+ for dir in $(SUBDIRS); do \
+ (cd $$dir; $(MAKE) $(SUBFLAGS) "BINDIR=$$topdir/bindist" install.bin); done
+ cp README.bd bindist/README
+ cp VERSION bindist
+ if [ -n "$(GROFF)" ] ; then cp groff bindist/groff ; fi
+ @echo Making bindist/Makefile
+ @sed -e "s;@GROFFLIBDIR@;$(GROFFLIBDIR);" \
+ -e "s;@FONTDIR@;$(FONTDIR);" \
+ -e "s;@FONTPATH@;$(FONTPATH);" \
+ -e "s;@MACRODIR@;$(MACRODIR);" \
+ -e "s;@MACROPATH@;$(MACROPATH);" \
+ -e "s;@HYPHENFILE@;$(HYPHENFILE);" \
+ -e "s;@DEVICE@;$(DEVICE);" \
+ -e "s;@GROFF@;$(GROFF);" \
+ Makefile.bd >bindist/Makefile
+
+FORCE:
diff --git a/Makefile.bd b/Makefile.bd
new file mode 100644
index 000000000..6b1239791
--- /dev/null
+++ b/Makefile.bd
@@ -0,0 +1,119 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This is a Makefile for installing groff from a set of binaries and
+# a source distribution.
+
+# SRCDIR should point to the directory containing the groff
+# source distribution. You'll have to change this.
+SRCDIR=/usr/jjc/groff
+
+# Define PAGE to be letter if your PostScript printer uses 8.5x11 paper (USA);
+# define it to be A4, if it uses A4 paper (rest of the world)
+PAGE=A4
+#PAGE=letter
+
+# BINDIR says where to install the binaries.
+BINDIR=/usr/local/bin
+
+# PSPRINT is the command to use for printing a PostScript file
+PSPRINT=lpr
+
+# DVIPRINT is the command to use for printing a TeX dvi file
+DVIPRINT=lpr -d
+
+MANROOT=/usr/local/man
+
+# MAN1EXT is the man section for user commands
+MAN1EXT=1
+MAN1DIR=$(MANROOT)/man$(MAN1EXT)
+# MAN5EXT is the man section for file formats
+MAN5EXT=5
+MAN5DIR=$(MANROOT)/man$(MAN5EXT)
+# MAN7EXT is the man section for macros
+MAN7EXT=7
+MAN7DIR=$(MANROOT)/man$(MAN7EXT)
+
+# FONTDIR is the directory in which device sub-directories are to be installed.
+# If this is not in the path
+# `@FONTPATH@',
+# you must set the GROFF_FONT_PATH environment variable.
+FONTDIR=@FONTDIR@
+
+# MACRODIR is the directory in which macro files should be installed.
+# If this is not in the path `@MACROPATH@',
+# you must set the GROFF_TMAC_PATH environment variable.
+MACRODIR=@MACRODIR@
+
+# HYPHENFILE is the file containing gtroff's hyphenation patterns.
+# If you change it from `@HYPHENFILE@',
+# you will need to set the GROFF_HYPHEN environment variable.
+HYPHENFILE=@HYPHENFILE@
+
+# If GROFFLIBDIR does not exist, then it will be created before MACRODIR,
+# FONTDIR, or HYPHENFILE are created.
+GROFFLIBDIR=@GROFFLIBDIR@
+
+# DEVICE is the default device used by the groff shell script.
+# If you want the binaries to use a default device other than `@DEVICE@'
+# when not run using the groff shell script,
+# you will need to set the GROFF_TYPESETTER environment variable.
+DEVICE=@DEVICE@
+
+# The groff ms macros will be available as -m$(TMAC_S).
+# If you use `TMAC_S=s', you can use the Unix ms macros by using
+# groff -ms -M/usr/lib/tmac.
+TMAC_S=gs
+
+GROFF=
+
+# Comment this line out if you would rather use the shell version of the
+# groff command.
+GROFF=@GROFF@
+
+STRIP=strip
+BINARIES=gtroff gtbl gpic geqn gsoelim grodvi grotty grops psbb pfbtops \
+ grefer gindxbib glookbib lkbib addftinfo tfmtodit $(GROFF)
+
+INSTALLFLAGS="PAGE=$(PAGE)" "BINDIR=$(BINDIR)" "PSPRINT=$(PSPRINT)" \
+ "DVIPRINT=$(DVIPRINT)" "MANROOT=$(MANROOT)" "MAN1EXT=$(MAN1EXT)" \
+ "MAN1DIR=$(MAN1DIR)" "MAN5EXT=$(MAN5EXT)" "MAN5DIR=$(MAN5DIR)" \
+ "MAN7EXT=$(MAN7EXT)" "MAN7DIR=$(MAN7DIR)" "FONTDIR=$(FONTDIR)" \
+ "GROFFLIBDIR=$(GROFFLIBDIR)" "MACRODIR=$(MACRODIR)" \
+ "HYPHENFILE=$(HYPHENFILE)" "DEVICE=$(DEVICE)" "GROFF=$(GROFF)" \
+ "TMAC_S=$(TMAC_S)"
+
+install: $(BINARIES) checkversions FORCE
+ @echo Making install.nobin in $(SRCDIR)
+ @(cd $(SRCDIR); $(MAKE) $(INSTALLFLAGS) install.nobin)
+ cp $(BINARIES) $(BINDIR)
+
+install.mm: FORCE
+ @echo Making install.mm in $(SRCDIR)
+ @(cd $(SRCDIR); $(MAKE) $(INSTALLFLAGS) install.mm)
+
+strip: FORCE
+ $(STRIP) $(BINARIES)
+
+checkversions: VERSION $(SRCDIR)/VERSION FORCE
+ @echo Checking that the source and binary distributions \
+ have the same version...
+ cmp VERSION $(SRCDIR)/VERSION
+
+FORCE:
diff --git a/PROBLEMS b/PROBLEMS
new file mode 100644
index 000000000..b6e86dc92
--- /dev/null
+++ b/PROBLEMS
@@ -0,0 +1,248 @@
+This file describes various problems that have been encountered in
+compiling, installing and running groff. Suggestions for additions or
+other improvements to this file are welcome.
+
+* I am having problems compiling groff with g++ on a DECstation or
+other machine using a MIPS processor (such as SGI machines).
+
+Try getting g++ from foobar.colorado.edu:pub/Gnu-For-Pmax.
+
+* I am having problems compiling groff with g++ on a 386 PC
+running SVR3.2.
+
+I recommend using Michael Bloom's GNU COFF patches
+(tut.cis.ohio-state.edu:pub/gnu/coff/gnu-coff.tar.Z).
+
+* I get lots of `numeric overflow' error messages whenever I run
+groff; I compiled groff with AT&T C++ 2.0 with an ANSI C compiler.
+
+Include -DCFRONT_ANSI_BUG in CPPDEFINES in the top-level Makefile. If
+that doesn't solve the problem, define INT_MIN as -INT_MAX in
+lib/lib.h.
+
+* Using SunOS 4.1, gtroff aborts in malloc.
+
+Include -DSUNOS_LOCALTIME_BUG in MALLOCFLAGS in the top-level Makefile.
+
+* When I try to compile groff with g++, I get lots of parse errors
+from files in /usr/include.
+
+You must install C++ versions of the standard include files; these are
+included with libg++.
+
+* g++ complains about conflicting declarations of fmod() while
+compiling pic.tab.c; I'm using a 68k based machine.
+
+This is a bug in the libg++ header files. Apply the fix given in the
+INSTALL file.
+
+* I get errors when I try to compile groff with Sun C++.
+
+Groff requires ANSI C style header files. The Sun C++ header files
+need some changes to meet this requirement: <string.h> must declare
+the mem* functions, (just add `#include <memory.h>' to <string.h>);
+the first argument to fopen and freopen should be declared as `const
+char *'; the first argument to fread should be declared as `void *';
+malloc should be declared to return `void *'. You can either change
+them in place, or copy them to some other directory and include that
+directory with a -I option.
+
+* I get the error `make[2]: execve: /bin/sh: Arg list too long.'.
+
+If you're using GNU make, try using /bin/make instead. Otherwise if
+you have the `env' command, try changing occurrences of $(MAKE) in the
+top-level Makefile to
+ env - PATH=/bin:/usr/bin $(MAKE)
+
+* pic output is not centered horizontally; pictures sometimes run off
+the bottom of the page.
+
+The macro package you are using is not supplying appropriate definitions
+of PS and PE. Give groff a -mpic option.
+
+* I'm having problems including PostScript illustrations using the PSPIC
+macro.
+
+A PostScript document must meet three requirements in order to be
+included with the PSPIC macro: it must comply with the Adobe Document
+Structuring Conventions; it must contain a BoundingBox line; it must
+be ``well-behaved''. The BoundingBox line should be of the form:
+
+ %%BoundingBox: llx lly urx ury
+
+where llx, lly, urx, ury are the coordinates of the lower left x,
+lower left y, upper right x, upper right y of the bounding box of
+marks on the page expressed as integers in the default PostScript
+coordinate system (72 units per inch, origin at bottom left corner).
+A useful tactic is to print out the illustration by itself (you may
+need to add a `showpage' at the end), and physically measure the
+bounding box. For more detail on these requirements, read the
+specification of Encapsulated PostScript format. (This is available
+from the Adobe file server; send a message with a body of `help' to
+ps-file-server@adobe.com.)
+
+* I've configured groff for A4 paper, but gtroff still seems to think
+that the length of a page (as returned by \n(.p) is 11 inches.
+
+This is intentional. The PAGE option is used only by grops. For
+compatibility with ditroff, the default page length in gtroff is
+always 11 inches. The page length can be changed with the `pl'
+request.
+
+* Groff doesn't use the font names I'm used to.
+
+Use the `ftr' request. See gtroff(1).
+
+* I get errors using the Unix -ms macros with groff -e -C.
+
+Apply this change:
+
+*** /usr/lib/ms/ms.eqn Tue Apr 25 02:14:28 1989
+--- ms.eqn Sun Nov 11 10:33:59 1990
+***************
+*** 22,29 ****
+ ..
+ . \" EN - end of a displayed equation
+ .de EN
+! .if !\\*(10 .br
+ .di
+ .rm EZ
+ .nr ZN \\n(dn
+ .if \\n(ZN>0 .if \\n(YE=0 .LP
+--- 22,30 ----
+ ..
+ . \" EN - end of a displayed equation
+ .de EN
+! .if \\n(.k>0 .br
+ .di
++ .ds 10 \\*(EZ\\
+ .rm EZ
+ .nr ZN \\n(dn
+ .if \\n(ZN>0 .if \\n(YE=0 .LP
+
+
+* gpic doesn't accept the syntax `chop N M' for chopping both ends of a
+line.
+
+The correct syntax is `chop N chop M'.
+
+* With gpic -t, when I print `line ->; box' using a dvi to ps
+program, the arrow head sticks through into the inside of the box.
+
+The dvi to ps program should be modified to set the line cap and
+line join parameters to 1 while printing tpic specials.
+
+* When I print the output groff -Tps, the output is always shifted up
+by about 0.7 inches; I'm using 8.5x11 inch paper.
+
+Make sure that PAGE is defined to be `letter' in the top-level
+Makefile. If you failed to do this, you can fix the problem by doing:
+ cp ps/devps/DESC-letter /usr/local/lib/groff/font/devps/DESC
+
+* When I try to print the output of groff -Tps, I get no output at all
+from the printer, and the log file shows the error
+%%[ error: undefined; offendingcommand: BP ]%%
+I using TranScript spooling software.
+
+This is a bug in the page reversal filter in early versions of
+TranScript. Define BROKEN_SPOOLER_FLAGS=7 in the top-level Makefile,
+and do a make.
+
+* When I preview groff -Tps output using the Sun OpenWindows 2.0 pageview
+program, all the pages are displayed on top of each other.
+
+This is a defect in pageview. Define BROKEN_SPOOLER_FLAGS=2 in the
+top-level Makefile, and do a make.
+
+* When I try to preview the output of groff -Tps using ralpage, I get
+errors from ralpage.
+
+This is a bug in ralpage. You may be able to work around the bug by
+defining MF and SF in ps/devps/prologue using `def' instead of `bind
+def'.
+
+* With groff -TX75 or -TX100, I can only view the first page.
+
+The left mouse button brings up a menu that allows you to view other
+pages.
+
+* When I print the output of groff -Tdvi, I just get a black dot in
+upper left corner.
+
+Some dvi drivers (notably early versions of xtex) do not correct
+handle dvi files that use a resolution different from that used by dvi
+files produced by TeX. Try getting a more up to date driver.
+
+* I get lots of errors when I use groff with -mm.
+
+-mm needs a few changes to work with groff; `make install.mm' will
+copy your -mm macros to groff's macro directory and make the necessary
+changes. You may need to edit the commands for the install.mm target
+in the Makefile.
+
+* gtroff doesn't understand lines like `.ce99' with no space between
+the name of the request or macro and the arguments.
+
+Gtroff requires a space between macro or request and its arguments
+because it allows the use of long names for macros and requests. You
+can use the -C option or the `cp' request to put gtroff into a
+compatibility mode in which it is not possible to use long names for
+macros but in which no space is required between macros and their
+arguments. The use of compatibility mode is strongly discouraged.
+
+* gtroff gives warnings about lines like
+ .ev \" a comment
+(with a tab after the .ev).
+
+A tab character cannot be used as a substitute for a space character
+(except in one case: between a control character at the beginning of a
+line and the name of a macro or request). For example, in Unix troff
+ .ps \" restore the previous point size
+(with a tab after the .ps) will NOT restore the previous point-size;
+instead it will be silently ignored. Since this is very likely to be
+an error, gtroff can give a warning about it. If you want to align
+comments, you can do it like this:
+ .ev\" \" a comment
+
+* I don't like the page headers and footers produced by groff -man.
+
+There seem to be many different styles of page header and footer
+produced by different versions of the -man macros. You will need to
+modify macros/tmac.an to suit your personal taste. For example, if
+you want the center of the page header to say
+ UNIX Programmer's Manual
+you will need to change the line
+ .el .ds an-extra3 \"UNIX Programmer's Manual
+to
+ .el .ds an-extra3 UNIX Programmer's Manual
+
+* Where is the document ``Groff Character Names'' that is mentioned in various
+man pages?
+
+It is in doc/chars.tr. You can print it out with each device to see
+what characters are available for that device.
+
+* While formatting a manual page, groff complains about not being able to
+break lines. The problem seems to be caused by a line like:
+ .TP \w'label'+2
+
+The -man documentation says that the default scale indicator for TP
+macro is `n'. The groff -man macros implement this correctly, so that
+the argument will be evaluated as if it were
+ \w'label'n+2n
+The Unix -man macros don't implement this correctly (probably because
+it's hard to do in Unix troff); they just append `n' to the entire
+argument, so that it will be evaluated as if it were
+ \w'label'u+2n
+The solution is to fix the manual page:
+ .TP \w'label'u+2
+
+* When I preview documents using -TX75 or -TX100, the layout is not the same
+as when I print the document with -Tps: the line and page breaks come
+in different places.
+
+Use groff -TXps.
+
+* In gpic expressions don't work at all. I'm using g++ 1.39 on a sparc.
+
+This is a bug in gcc/g++ 1.39. Apply the fix given in the INSTALL file.
diff --git a/PROJECTS b/PROJECTS
new file mode 100644
index 000000000..d4d024866
--- /dev/null
+++ b/PROJECTS
@@ -0,0 +1,18 @@
+Here are some things that would be useful additions to groff:
+
+ a driver for the HP Laserjet 2 and 3
+
+ grap
+
+ a deroff that understands long names
+
+ a page-makeup postprocessor and associated macro package
+ (like pm and -mpm)
+
+ a complete, self-contained manual
+
+If you want to work on one of these, I suggest you contact me first.
+
+James Clark
+jjc@jclark.uucp
+jjc@ai.mit.edu
diff --git a/README b/README
new file mode 100644
index 000000000..b4b2ac701
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+This is the beta-test version of the GNU groff document formatting
+system. The version number is given in the file VERSION.
+
+Included in this release are implementations of troff, pic, eqn, tbl,
+refer, the -man macros and the -ms macros, and drivers for PostScript,
+TeX dvi format, and typewriter-like devices. Also included is a
+modified version of the Berkeley -me macros, and an enhanced version
+of the X11R4 xditview.
+
+You will require a C++ compiler implementing version 2.0 of the C++
+language. I suggest GNU C++ 1.37.1 (rather than 1.39.1).
+
+See the file INSTALL for installation instructions.
+
+The file CHANGES describes recent user-visible changes to groff.
+
+Groff is free software. See the file LICENSE for copying permission.
+
+The file PROBLEMS describes various problems that have been
+encountered in compiling, installing, and running groff.
+
+For the moment, the documentation assumes that you are already
+familiar with the Unix versions of troff, -man, -ms and the
+preprocessors.
+
+Please report bugs using the form in the file BUG-REPORT; the idea of
+this is to make sure that I have all the information I need to fix the
+bug. At the very least, read the BUG-REPORT form and make sure that
+you supply all the information that it asks for. Even if you are not
+sure that something is a bug, report it using BUG-REPORT: this will
+enable me to determine whether it really is a bug or not. As well as
+bug reports, I would be interested in suggestions for improvements to
+groff (no matter how small).
+
+James Clark
+jjc@jclark.uucp
+jjc%jclark@mcsun.eu.net
+jjc@gnu.ai.mit.edu
diff --git a/README.bd b/README.bd
new file mode 100644
index 000000000..724c8db5b
--- /dev/null
+++ b/README.bd
@@ -0,0 +1,15 @@
+This directory contains a set of binaries for the GNU groff document
+formatting system. The version number is given in the file VERSION.
+
+In order to use these, you must also have a copy of the groff source
+distribution with exactly the same version. You will not need a C++
+compiler (or indeed any other compiler).
+
+To install groff, edit the Makefile in this directory and type `make'.
+You shouldn't need to change the Makefile in the source directory.
+
+Groff is free software. See the file LICENSE in the groff source
+distribution for copying information.
+
+See also the README and INSTALL files in the groff source
+distribution.
diff --git a/VERSION b/VERSION
new file mode 100644
index 000000000..101047359
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+1.02
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 000000000..7d1d00a9d
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,53 @@
+#Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+TROFF=../troff/troff
+FFLAG=-F../ps
+GROPS=../ps/grops
+DOCS=chars.PS meref.PS meintro.PS
+MEMACROS=../macros/tmac.e
+PSMACROS=../ps/tmac.ps
+SOELIM=../etc/soelim
+
+all: $(DOCS)
+
+.SUFFIXES: .tr .me .PS .dit
+
+.dit.PS:
+ $(GROPS) $(FFLAG) $< >$@
+
+.me.dit:
+ $(SOELIM) $< \
+ | sed -e "s;@VERSION@;`cat ../VERSION`;" \
+ | $(TROFF) -i -Tps $(FFLAG) $(MEMACROS) $(PSMACROS) >$@
+
+.tr.dit:
+ $(TROFF) -Tps $(FFLAG) $(PSMACROS) $< >$@
+
+chars.PS: chars.dit
+meref.PS: meref.dit
+meintro.PS: meintro.dit
+
+install:
+
+clean:
+ -rm -f *.PS *.dit core
+
+distclean: clean
+realclean: distclean
diff --git a/doc/chars.tr b/doc/chars.tr
new file mode 100644
index 000000000..df91dcd54
--- /dev/null
+++ b/doc/chars.tr
@@ -0,0 +1,503 @@
+.\" -*- nroff -*-
+.\" This document describes the standard character names used by
+.\" the groff PostScript driver.
+.if !\n(.g .ab This document requires groff.
+.\" .achar accented-char accent char
+.de achar
+.char \\$1 \\$3\
+\k[acc]\
+\h'(u;-\w'\\$2'-\w'\\$3'/2+\\\\n[skw]+(\w'x'*0)-\\\\n[skw])'\
+\v'(u;\w'x'*0+\\\\n[rst]+(\w'\\$3'*0)-\\\\n[rst])'\
+\\$2\
+\v'(u;\w'x'*0-\\\\n[rst]+(\w'\\$3'*0)+\\\\n[rst])'\
+\h'|\\\\n[acc]u'
+.hcode \\$1\\$3
+..
+.achar \[vc] \[ah] c
+.achar \[vC] \[ah] C
+.de HE
+.sp |1i
+.header
+..
+.de FO
+.sp |\\n(.pu-.5i
+.ev 1
+.tl ''-%-''
+.ev
+'bp
+..
+.wh -1i FO
+.br
+.sp |1i
+.wh 0 HE
+.ce 999
+.ps 12
+.vs 14
+.ft B
+Groff Character Names
+.ce 0
+.vs 12
+.ps 10
+.ft R
+.sp 1v
+.nr sep 3n
+.ta \w'\fIOutput'u+\n[sep]u +\w'\fIInput'u+\n[sep]u +\w'\fIInput'u+\n[sep]u \
++\w'periodcentered'u+\n[sep]u
+.de C0
+.C \\$1 "" \\$1 \\$2 "\\$3"
+..
+.de C1
+.C \e\\$1 "" \\\\\\$1 \\$2 "\\$3"
+..
+.de C2
+.C \e(\\$1 "" \\(\\$1 \\$2 "\\$3"
+..
+.de C3
+.C \e[\\$1] "" \\[\\$1] \\$2 "\\$3"
+..
+.de CD
+.C \[char\\$1] \\$1 \[char\\$1] \\$2 "\\$3"
+..
+.\" input-name decimal-code output-name ps-name description
+.fspecial CR R
+.de C
+.if c\\$3 \{\
+.ft CR
+.tr -\-^\(ha~\(ti
+.di CH
+\&\\$1
+.br
+.di
+.ft
+.ds CH \\*(CH\
+.tr --^^~~
+\&\\$3\t\\*(CH\t\\$2\t\\$4\t\\$5
+.\}
+..
+.de header
+.ft I
+Output\tInput\tInput\tPostScript\tNotes
+\tname\tcode\tname
+.ft
+.sp .4v
+..
+This gives a list of the standard \fBgroff\fP input characters.
+The \fIInput code\fP column applies to characters which can be
+input with a single character, and gives the ISO Latin-1 code
+of that input character.
+All ISO Latin-1 characters print as themselves with the following
+exceptions:
+\- prints as -,
+\(ha prints as ^,
+\(ti prints as ~;
+the corresponding ISO Latin-1 characters can be obtained with
+\e\-, \e(ha and \e(ti.
+The \fIPostScript name\fP column gives the usual PostScript name
+of the output character.
+.sp .3v
+The names are based on
+E.G.Keizer, K.J. Simonsen, J. Akkerhuis,
+``An extension to the troff character set for Europe'',
+EUUG Newsletter, Volume 9, No. 2, Summer 1989.
+.sp
+'nf
+.header
+.CD 33 exclam
+.CD 34 quotedbl
+.CD 35 numbersign
+.CD 36 dollar
+.CD 37 percent
+.CD 38 ampersand
+.CD 39 quoteright
+.CD 40 parenleft
+.CD 41 parenright
+.CD 42 asterisk
+.CD 43 plus
+.CD 44 comma
+.CD 45 hyphen
+.CD 46 period
+.CD 47 slash
+.CD 58 colon
+.CD 59 semicolon
+.CD 60 less
+.CD 61 equal
+.CD 62 greater
+.CD 63 question
+.CD 64 at
+.CD 91 bracketleft
+.CD 92 backslash
+.CD 93 bracketright
+.CD 94 circumflex "circumflex accent"
+.CD 95 underscore
+.CD 96 quoteleft
+.CD 123 braceleft
+.CD 124 bar
+.CD 125 braceright
+.CD 126 tilde "tilde accent"
+.CD 161 exclamdown
+.CD 162 cent
+.CD 163 sterling
+.CD 164 currency
+.CD 165 yen
+.CD 166 brokenbar
+.CD 167 section
+.CD 168 dieresis
+.CD 169 copyright
+.CD 170 ordfeminine
+.CD 171 guillemotleft
+.CD 172 logicalnot
+.CD 173 hyphen
+.CD 174 registered
+.CD 175 macron
+.CD 176 degree
+.CD 177 plusminus
+.CD 178 twosuperior
+.CD 179 threesuperior
+.CD 180 acute "acute accent"
+.CD 181 mu
+.CD 182 paragraph
+.CD 183 periodcentered
+.CD 184 cedilla
+.CD 185 onesuperior
+.CD 186 ordmasculine
+.CD 187 guillemotright
+.CD 188 onequarter
+.CD 189 onehalf
+.CD 190 threequarters
+.CD 191 questiondown
+.CD 192 Agrave
+.CD 193 Aacute
+.CD 194 Acircumflex
+.CD 195 Atilde
+.CD 196 Adieresis
+.CD 197 Aring
+.CD 198 AE
+.CD 199 Ccedilla
+.CD 200 Egrave
+.CD 201 Eacute
+.CD 202 Ecircumflex
+.CD 203 Edieresis
+.CD 204 Igrave
+.CD 205 Iacute
+.CD 206 Icircumflex
+.CD 207 Idieresis
+.CD 208 Eth
+.CD 209 Ntilde
+.CD 210 Ograve
+.CD 211 Oacute
+.CD 212 Ocircumflex
+.CD 213 Otilde
+.CD 214 Odieresis
+.CD 215 multiply
+.CD 216 Oslash
+.CD 217 Ugrave
+.CD 218 Uacute
+.CD 219 Ucircumflex
+.CD 220 Udieresis
+.CD 221 Yacute
+.CD 222 Thorn
+.CD 223 germandbls
+.CD 224 agrave
+.CD 225 aacute
+.CD 226 acircumflex
+.CD 227 atilde
+.CD 228 adieresis
+.CD 229 aring
+.CD 230 ae
+.CD 231 ccedilla
+.CD 232 egrave
+.CD 233 eacute
+.CD 234 ecircumflex
+.CD 235 edieresis
+.CD 236 igrave
+.CD 237 iacute
+.CD 238 icircumflex
+.CD 239 idieresis
+.CD 240 eth
+.CD 241 ntilde
+.CD 242 ograve
+.CD 243 oacute
+.CD 244 ocircumflex
+.CD 245 otilde
+.CD 246 odieresis
+.CD 247 divide
+.CD 248 oslash
+.CD 249 ugrave
+.CD 250 uacute
+.CD 251 ucircumflex
+.CD 252 udieresis
+.CD 253 yacute
+.CD 254 thorn
+.CD 255 ydieresis
+.C2 -D Eth "Icelandic uppercase eth"
+.C2 Sd eth "Icelandic lowercase eth"
+.C2 TP Thorn "Icelandic uppercase thorn"
+.C2 Tp thorn "Icelandic lowercase thorn"
+.C2 AE AE
+.C2 ae ae
+.C2 OE OE
+.C2 oe oe
+.C2 IJ IJ "Dutch IJ ligature"
+.C2 ij ij "Dutch ij ligature"
+.C2 ss germandbls
+.C2 'A Aacute
+.C2 'C Cacute
+.C2 'E Eacute
+.C2 'I Iacute
+.C2 'O Oacute
+.C2 'U Uacute
+.C2 'a aacute
+.C2 'c cacute
+.C2 'e eacute
+.C2 'i iacute
+.C2 'o oacute
+.C2 'u uacute
+.C2 :A Adieresis
+.C2 :E Edieresis
+.C2 :I Idieresis
+.C2 :O Odieresis
+.C2 :U Udieresis
+.C2 :Y Ydieresis
+.C2 :a adieresis
+.C2 :e edieresis
+.C2 :i idieresis
+.C2 :o odieresis
+.C2 :u udieresis
+.C2 :y ydieresis
+.C2 ^A Acircumflex
+.C2 ^E Ecircumflex
+.C2 ^I Icircumflex
+.C2 ^O Ocircumflex
+.C2 ^U Ucircumflex
+.C2 ^a acircumflex
+.C2 ^e ecircumflex
+.C2 ^i icircumflex
+.C2 ^o ocircumflex
+.C2 ^u ucircumflex
+.C2 `A Agrave
+.C2 `E Egrave
+.C2 `I Igrave
+.C2 `O Ograve
+.C2 `U Ugrave
+.C2 `a agrave
+.C2 `e egrave
+.C2 `i igrave
+.C2 `o ograve
+.C2 `u ugrave
+.C2 ~A Atilde
+.C2 ~N Ntilde
+.C2 ~O Otilde
+.C2 ~a atilde
+.C2 ~n ntilde
+.C2 ~o otilde
+.C2 vS Scaron
+.C2 vs scaron
+.C2 vZ Zcaron
+.C2 vz zcaron
+.C2 ,C Ccedilla
+.C2 ,c ccedilla
+.C2 /L Lslash "Polish L with a slash"
+.C2 /l lslash "Polish l with a slash"
+.C2 /O Oslash
+.C2 /o oslash
+.C2 oA Aring
+.C2 oa aring
+.C2 a" hungarumlaut "Hungarian umlaut"
+.C2 a- macron "macron or bar accent"
+.C2 a. dotaccent "dot accent"
+.C2 a^ circumflex "circumflex accent"
+.C2 aa acute "acute accent"
+.C2 ga grave "grave accent"
+.C2 ab breve "breve accent"
+.C2 ac cedilla "cedilla accent"
+.C2 ad dieresis "umlaut or dieresis"
+.C2 ah caron "h\('a\(vcek accent"
+.C2 ao ring "ring or circle accent"
+.C2 a~ tilde "tilde accent"
+.C2 ho ogonek "hook or ogonek accent"
+.C2 .i dotlessi "i without a dot"
+.C2 .j dotlessj "j without a dot"
+.C2 Cs currency "Scandinavian currency sign"
+.C2 Do dollar
+.C2 Po sterling
+.C2 Ye yen
+.C2 ct cent
+.C2 Fo guillemotleft
+.C2 Fc guillemotright
+.C2 fo guilsinglleft
+.C2 fc guilsinglright
+.C2 r! exclamdown
+.C2 r? questiondown
+.C2 ff ff "ff ligature"
+.C2 fi fi "fi ligature"
+.C2 fl fl "fl ligature"
+.C2 Fi ffi "ffi ligature"
+.C2 Fl ffl "ffl ligature"
+.C2 OK \& "check mark, tick"
+.C2 Of ordfeminine
+.C2 Om ordmasculine
+.C2 S1 onesuperior
+.C2 S2 twosuperior
+.C2 S3 threesuperior
+.C2 <- arrowleft
+.C2 -> arrowright
+.C2 <> arrowboth "horizontal double-headed arrow"
+.C2 da arrowdown
+.C2 ua arrowup
+.C2 va \& "vertical double-headed arrow"
+.C2 lA arrowdblleft
+.C2 rA arrowdblright
+.C2 hA arrowdblboth "horizontal double-headed double arrow"
+.C2 dA arrowdbldown
+.C2 uA arrowdblup
+.C2 vA \& "vertical double-headed double arrow"
+.C2 ba bar
+.C2 bb brokenbar
+.C2 br br "box rule with traditional troff metrics"
+.C2 ru ru "baseline rule"
+.C2 ul ul "underline with traditional troff metrics"
+.C2 bv bv "bold vertical"
+.C2 bs bell
+.C2 ci circle
+.C2 bu bullet
+.C2 co copyright
+.C2 rg registered
+.C2 tm trademark
+.C2 dd daggerdbl "double dagger sign"
+.C2 dg dagger
+.C2 ps paragraph
+.C2 sc section
+.C2 de degree
+.C2 em emdash "em dash"
+.C2 en endash "en dash"
+.C2 %0 perthousand "per thousand, per mille sign"
+.C2 12 onehalf
+.C2 14 onequarter
+.C2 34 threequarters
+.C2 f/ fraction "bar for fractions"
+.C2 fm minute "footmark, prime"
+.C2 sd second
+.C2 ha asciicircum "\s-2ASCII\s+2 circumflex, hat, caret"
+.C2 ti asciitilde "\s-2ASCII\s0 tilde"
+.C2 hy hyphen
+.C2 lB bracketleft
+.C2 rB bracketright
+.C2 lC braceleft
+.C2 rC braceright
+.C2 la angleleft "left angle bracket"
+.C2 ra angleright "right angle bracket"
+.C2 lh handleft
+.C2 rh handright
+.C2 lq quotedblleft
+.C2 rq quotedblright
+.C2 oq quoteleft "single open quote"
+.C2 or bar
+.C2 at at
+.C1 - minus "minus sign from current font"
+.C2 sh numbersign
+.C2 sl slash
+.C2 rs backslash
+.C2 sq square
+.C2 3d therefore
+.C2 tf therefore
+.C2 *A Alpha
+.C2 *B Beta
+.C2 *C Xi
+.C2 *D Delta
+.C2 *E Epsilon
+.C2 *F Phi
+.C2 *G Gamma
+.C2 *H Theta
+.C2 *I Iota
+.C2 *K Kappa
+.C2 *L Lambda
+.C2 *M Mu
+.C2 *N Nu
+.C2 *O Omicron
+.C2 *P Pi
+.C2 *Q Psi
+.C2 *R Rho
+.C2 *S Sigma
+.C2 *T Tau
+.C2 *U Upsilon
+.C2 *W Omega
+.C2 *X Chi
+.C2 *Y Eta
+.C2 *Z Zeta
+.C2 *a alpha
+.C2 *b beta
+.C2 *c xi
+.C2 *d delta
+.C2 *e epsilon
+.C2 *f phi
+.C2 *g gamma
+.C2 *h theta
+.C2 *i iota
+.C2 *k kappa
+.C2 *l lambda
+.C2 *m mu
+.C2 *n nu
+.C2 *o omicron
+.C2 *p pi
+.C2 *q psi
+.C2 *r rho
+.C2 *s sigma
+.C2 *t tau
+.C2 *u upsilon
+.C2 *w omega
+.C2 *x chi
+.C2 *y eta
+.C2 *z zeta
+.C2 ts sigma1 "terminal sigma"
+.C2 ~~ approxequal
+.C2 ~= approxequal
+.C2 != notequal
+.C2 ** asteriskmath
+.C2 -+ minusplus
+.C2 +- plusminus
+.C2 <= lessequal
+.C2 == equivalence
+.C2 =~ congruent
+.C2 >= greaterequal
+.C2 AN logicaland
+.C2 OR logicalor
+.C2 no logicalnot
+.C2 te existential "there exists, existential quantifier"
+.C2 fa universal "for all, universal quantifier"
+.C2 Ah aleph
+.C2 Im Ifraktur "Fraktur I, imaginary"
+.C2 Re Rfraktur "Fraktur R, real"
+.C2 if infinity
+.C2 md dotmath
+.C2 mo element
+.C2 mu multiply
+.C2 nc notpropersuperset
+.C2 ne notequivalence
+.C2 nm notelement
+.C2 pl plusmath "plus sign in special font"
+.C2 eq equalmath "equals sign in special font"
+.C2 pt proportional
+.C2 pp perpendicular
+.C2 sb propersubset
+.C2 sp propersuperset
+.C2 ib reflexsubset
+.C2 ip reflexsuperset
+.C2 ap similar
+.C2 pd partialdiff "partial differentiation sign"
+.C2 c* circlemultiply "multiply sign in a circle"
+.C2 c+ circleplus "plus sign in a circle"
+.C2 ca intersection "intersection, cap"
+.C2 cu union "union, cup"
+.C2 di divide "division sign"
+.C2 -h hbar
+.C2 gr gradient
+.C2 es emptyset
+.C2 CL club "club suit"
+.C2 SP spade "spade suit"
+.C2 HE heart "heart suit"
+.C2 DI diamond "diamond suit"
+.C2 CR carriagereturn "carriage return symbol"
+.C2 st suchthat
+.C2 /_ angle
+.C2 << "" "much less"
+.C2 >> "" "much greater"
diff --git a/doc/meintro.me b/doc/meintro.me
new file mode 100644
index 000000000..c31d6f172
--- /dev/null
+++ b/doc/meintro.me
@@ -0,0 +1,2247 @@
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)intro.me 6.4 (Berkeley) 7/17/89
+.\"
+.\" Modified for groff by jjc@jclark.uucp.
+.\"UC 7
+.ll 6.5i
+.lt 6.5i
+.pn 0
+.ds MO @VERSION@
+.nr si 3n
+.he 'USING GROFF AND \-ME''%'
+.\"eh 'USD:22-%''Writing Papers with NROFF using \-me'
+.\"oh 'Writing Papers with NROFF using \-me''USD:22-%'
+.ds U \s-1UNIX\s0
+.ds N \s-1NROFF\s0
+.ds T \s-1TROFF\s0
+.ds G \s-1GROFF\s0
+.+c
+.(l C
+.sz 14
+.b "Writing Papers with GROFF using \-me"
+.sz
+.sp 2
+.ul
+Eric P. Allman*
+.(f
+*Author's current address:
+Britton Lee, Inc.,
+1919 Addison Suite 105,
+Berkeley, California 94704.
+.)f
+.sp
+Project INGRES
+Electronics Research Laboratory
+University of California, Berkeley
+Berkeley, California 94720
+.sp 2
+.i "Modified for \*G by James Clark"
+.)l
+.sp 4
+.pp
+This document describes
+the text processing facilities
+available on the \*U\(dg
+.(f
+\(dg\*U is a trademark
+of AT&T Bell Laboratories
+.)f
+operating system
+via \*G and the
+\-me
+macro package.
+It is assumed
+that the reader
+already is generally familiar
+with the \*U operating system
+and a text editor
+such as
+.b ex .
+This is intended to be a casual introduction,
+and
+as such not all material is covered.
+In particular,
+many variations and additional features
+of the \-me macro package
+are not explained.
+For a complete discussion of this
+and other issues,
+see
+.ul
+The \-me Reference Manual
+and
+.ul
+The \*N/\*T Reference Manual.
+.pp
+\*G, a computer program
+that runs on the \*U operating system,
+reads an input file
+prepared by the user
+and outputs a formatted paper
+suitable for publication or framing.
+The input consists of
+.i text ,
+or words to be printed,
+and
+.i requests ,
+which give instructions
+to the \*G program
+telling how to format the printed copy.
+.pp
+Section 1
+describes the basics
+of text processing.
+Section 2
+describes the basic requests.
+Section 3
+introduces displays.
+Annotations,
+such as footnotes,
+are handled in
+section 4.
+The more complex requests
+which are not discussed in section 2
+are covered in section 5.
+Finally,
+section 6
+discusses things you will need
+to know
+if you want to typeset documents.
+If you are a novice,
+you probably won't want to read beyond section 4
+until you have tried some of the basic features out.
+.pp
+When you have your raw text ready,
+call the \*G formatter by typing
+as a request to the \*U shell:
+.(b
+groff \-me \-T\c
+.i "type files"
+.)b
+where
+.i type
+describes the type of
+output device you are using.
+A complete description of options
+to the \*G command can be found in
+.b groff (1).
+.pp
+The word
+.i argument
+is used in this manual
+to mean a word or number
+which appears on the same line
+as a request
+which modifies the meaning
+of that request.
+For example,
+the request
+.(b
+\&.sp
+.)b
+spaces one line,
+but
+.(b
+\&.sp 4
+.)b
+spaces four lines.
+The number
+.b 4
+is an
+.i argument
+to the
+.b .sp
+request
+which says to space four lines
+instead of one.
+Arguments are separated from the request
+and from each other
+by spaces.
+.sh 1 "Basics of Text Processing"
+.pp
+The primary function
+of \*G
+is to
+.i collect
+words from input lines,
+.i fill
+output lines with those words,
+.i justify
+the right hand margin by inserting extra spaces
+in the line,
+and output the result.
+For example,
+the input:
+.(b
+Now is the time
+for all good men
+to come to the aid
+of their party.
+Four score and seven
+years ago,...
+.)b
+will be read,
+packed onto output lines,
+and justified
+to produce:
+.(b F
+Now is the time
+for all good men
+to come to the aid
+of their party.
+Four score and seven
+years ago,...
+.)b
+Sometimes you may want to start a new output line
+even though the line you are on
+is not yet full;
+for example,
+at the end of a paragraph.
+To do this
+you can cause a
+.i break ,
+which
+starts a new output line.
+Some requests
+cause a break automatically,
+as do blank input lines
+and input lines beginning with a space.
+.pp
+Not all input lines
+are text to be formatted.
+Some of the input lines
+are
+.i requests
+which describe
+how to format the text.
+Requests always have a period
+or an apostrophe
+(\c
+.q "\|\(aa\|" )
+as the first character
+of the input line.
+.pp
+The text formatter
+also does more complex things,
+such as automatically numbering pages,
+skipping over page folds,
+putting footnotes in the correct place,
+and so forth.
+.pp
+I can offer you a few hints
+for preparing text
+for input to \*G.
+First,
+keep the input lines short.
+Short input lines are easier to edit,
+and \*G will pack words onto longer lines
+for you anyhow.
+In keeping with this,
+it is helpful
+to begin a new line
+after every period,
+comma,
+or phrase,
+since common corrections
+are to add or delete sentences
+or phrases.
+Second,
+do not put spaces at the end of lines,
+since this can sometimes confuse the \*N
+processor.
+Third,
+do not hyphenate words at the end of lines
+(except words that should have hyphens in them,
+such as
+.q mother-in-law );
+\*G is smart enough to hyphenate words
+for you as needed,
+but is not smart enough
+to take hyphens out
+and join a word back together.
+Also,
+words such as
+.q mother-in-law
+should not be broken
+over a line,
+since then you will get a space
+where not wanted,
+such as
+.tr @-
+.nh
+.q "mother@\ in@law" .
+.br
+.tr @@
+.hy 14
+.sh 1 "Basic Requests"
+.sh 2 "Paragraphs"
+.pp
+Paragraphs are begun
+by using the
+.b .pp
+request.
+For example,
+the input:
+.(b
+\&.pp
+Now is the time for all good men
+to come to the aid of their party.
+Four score and seven years ago,...
+.)b
+produces a blank line
+followed by an indented first line.
+The result is:
+.(b F
+.ti +\n(piu
+Now is the time for all good men
+to come to the aid of their party.
+Four score and seven years ago,...
+.)b
+.pp
+Notice that the sentences
+of the paragraphs
+.i "must not"
+begin with a space,
+since blank lines
+and lines beginning with spaces
+cause a break.
+For example,
+if I had typed:
+.(b
+\&.pp
+Now is the time for all good men
+ to come to the aid of their party.
+Four score and seven years ago,...
+.)b
+The output would be:
+.(b F
+.ti +\n(piu
+Now is the time for all good men
+ to come to the aid of their party.
+Four score and seven years ago,...
+.)b
+A new line begins after the word
+.q men
+because the second line began with a space character.
+.pp
+There are many
+fancier
+types of paragraphs,
+which will be described later.
+.sh 2 "Headers and Footers"
+.pp
+Arbitrary headers and footers
+can be put
+at the top and bottom
+of every page.
+Two requests
+of the form
+.b .he \ \c
+.i title
+and
+.b .fo \ \c
+.i title
+define the titles to put at the head and the foot
+of every page,
+respectively.
+The titles are called
+.i three-part
+titles,
+that is,
+there is a left-justified part,
+a centered part,
+and a right-justified part.
+To separate these three parts
+the first character of
+.i title
+(whatever it may be)
+is used as a delimiter.
+Any character may be used,
+but
+backslash
+and double quote marks
+should be avoided.
+The percent sign
+is replaced by the current page number
+whenever found in the title.
+For example,
+the input:
+.(b
+\&.he \(aa\(aa%\(aa\(aa
+\&.fo \(aaJane Jones\(aa\(aaMy Book\(aa
+.)b
+results in the page number
+centered at the top
+of each page,
+.q "Jane Jones"
+in the lower left corner,
+and
+.q "My Book"
+in the lower right corner.
+.sh 2 "Double Spacing"
+.pp
+.ls 2
+\*G will double space output text automatically if you
+use the request
+.b ".ls\ 2" ,
+as is done in this section.
+You can revert to single spaced mode
+by typing
+.b ".ls\ 1" .
+.ls 1
+.sh 2 "Page Layout"
+.pp
+A number of requests allow
+you to change the way the printed copy looks,
+sometimes called the
+.i layout
+of the output page.
+Most of these requests adjust the placing
+of
+.q "white space"
+(blank lines or spaces).
+In these explanations,
+characters in italics
+should be replaced with values you wish to use;
+bold characters
+represent characters which should actually be typed.
+.pp
+The
+.b .bp
+request
+starts a new page.
+.pp
+The request
+.b .sp \ \c
+.i N
+leaves
+.i N
+lines of blank space.
+.i N
+can be omitted
+(meaning skip a single line)
+or can be of the form
+.i N \^\c
+.b i
+(for
+.i N
+inches)
+or
+.i N \^\c
+.b c
+(for
+.i N
+centimeters).
+For example, the input:
+.(b
+\&.sp 1.5i
+My thoughts on the subject
+\&.sp
+.)b
+leaves one and a half inches of space,
+followed by the line
+.q "My thoughts on the subject" ,
+followed by a single blank line.
+.pp
+The
+.b .in \ \c
+.i +N
+request
+changes the amount of white space
+on the left of the page
+(the
+.i indent ).
+The argument
+.i N
+can be of the form
+.b + \c
+.i N
+(meaning leave
+.i N
+spaces more than you are already leaving),
+.b \- \c
+.i N
+(meaning leave less than you do now),
+or just
+.i N
+(meaning leave exactly
+.i N
+spaces).
+.i N
+can be of the form
+.i N \^\c
+.b i
+or
+.i N \^\c
+.b c
+also.
+For example,
+the input:
+.(b
+initial text
+\&.in 5
+some text
+\&.in +1i
+more text
+\&.in \-2c
+final text
+.)b
+produces
+.q "some text"
+indented exactly five spaces
+from the left margin,
+.q "more text"
+indented five spaces
+plus one inch
+from the left margin
+(fifteen spaces
+on a pica typewriter),
+and
+.q "final text"
+indented five spaces
+plus one inch
+minus two centimeters
+from the margin.
+That is,
+the output is:
+.(b
+initial text
+.in +5
+some text
+.in +1i
+more text
+.in -2c
+final text
+.)b
+.pp
+The
+.b .ti \ \c
+.i +N
+(temporary indent)
+request is used like
+.b .in \ \c
+.i +N
+when the indent
+should apply to one line only,
+after which it should revert
+to the previous indent.
+For example,
+the input:
+.(b
+\&.in 1i
+\&.ti 0
+Ware, James R. The Best of Confucius,
+Halcyon House, 1950.
+An excellent book containing translations of
+most of Confucius\(aa most delightful sayings.
+A definite must for anyone interested in the early foundations
+of Chinese philosophy.
+.)b
+produces:
+.in 1i+\n($iu
+.ti \n($iu
+Ware, James R. The Best of Confucius,
+Halcyon House, 1950.
+An excellent book containing translations of
+most of Confucius' most delightful sayings.
+A definite must for anyone interested in the early foundations
+of Chinese philosophy.
+.pp
+Text lines can be centered
+by using the
+.b .ce
+request.
+The line after the
+.b .ce
+is centered
+(horizontally)
+on the page.
+To center more than one line,
+use
+.b .ce \ \c
+.i N
+(where
+.i N
+is the number of lines to center),
+followed by the
+.i N
+lines.
+If you want to center many lines
+but don't want to count them,
+type:
+.(b
+\&.ce 1000
+lines to center
+\&.ce 0
+.)b
+The
+.b ".ce\ 0"
+request tells \*G to center zero more lines,
+in other words,
+stop centering.
+.pp
+All of these requests
+cause a break;
+that is,
+they always start
+a new line.
+If you want to start a new line
+without performing any other action,
+use
+.b .br .
+.sh 1 "Displays"
+.pp
+Displays are sections of text
+to be set off
+from the body of the paper.
+Major quotes,
+tables,
+and figures
+are types of displays,
+as are all the examples
+used in this document.
+All displays
+except centered blocks
+are output
+single spaced.
+.sh 2 "Major Quotes"
+.pp
+Major quotes
+are quotes which are several lines long,
+and hence are set in from the rest
+of the text
+without quote marks
+around them.
+These can be generated
+using the commands
+.b .(q
+and
+.b .)q
+to surround the quote.
+For example,
+the input:
+.(b
+As Weizenbaum points out:
+\&.(q
+It is said that to explain is to explain away.
+This maxim is nowhere so well fulfilled
+as in the areas of computer programming,...
+\&.)q
+.)b
+generates as output:
+.lp
+As Weizenbaum points out:
+.(q
+It is said that to explain is to explain away.
+This maxim is nowhere so well fulfilled
+as in the areas of computer programming,...
+.)q
+.sh 2 "Lists"
+.pp
+A
+.i list
+is an indented,
+single spaced,
+unfilled display.
+Lists should be used
+when the material to be printed
+should not be filled and justified
+like normal text,
+such as columns of figures
+or the examples used in this paper.
+Lists are surrounded
+by the requests
+.b .(l
+and
+.b .)l .
+For example,
+type:
+.(b
+Alternatives to avoid deadlock are:
+\&.(l
+Lock in a specified order
+Detect deadlock and back out one process
+Lock all resources needed before proceeding
+\&.)l
+.)b
+will produce:
+.br
+Alternatives to avoid deadlock are:
+.(l
+Lock in a specified order
+Detect deadlock and back out one process
+Lock all resources needed before proceeding
+.)l
+.sh 2 "Keeps"
+.pp
+A
+.i keep
+is a display of lines
+which are kept on a single page
+if possible.
+An example of where you would use a keep
+might be a diagram.
+Keeps differ from lists
+in that lists may be broken
+over a page boundary
+whereas keeps will not.
+.pp
+Blocks are the basic kind of keep.
+They begin with the request
+.b .(b
+and end with the request
+.b .)b .
+If there is not room on the current page
+for everything in the block,
+a new page is begun.
+This has the unpleasant effect
+of leaving blank space
+at the bottom of the page.
+When this is not appropriate,
+you can use the alternative,
+called
+.i "floating keeps" .
+.pp
+.i "Floating keeps"
+move relative to the text.
+Hence,
+they are good for things
+which will be referred to
+by name,
+such as
+.q "See figure 3" .
+A floating keep will appear
+at the bottom of the current page
+if it will fit;
+otherwise,
+it will appear at the top
+of the next page.
+Floating keeps begin with the line
+.b .(z
+and end with the line
+.b .)z .
+For an example of a floating keep,
+see figure 1.
+.(z
+.in 1i
+.xl -1i
+.hl
+\&.(z
+\&.hl
+Text of keep to be floated.
+\&.sp
+\&.ce
+Figure 1. Example of a Floating Keep.
+\&.hl
+\&.)z
+.sp
+.ce
+Figure 1. Example of a Floating Keep.
+.hl
+.)z
+The
+.b .hl
+request is used
+to draw a horizontal line
+so that the figure
+stands out from the text.
+.sh 2 "Fancier Displays"
+.pp
+Keeps and lists are normally collected in
+.i nofill
+mode,
+so that they are good for tables and such.
+If you want a display
+in fill mode
+(for text),
+type
+.b ".(l\ F"
+(Throughout this section,
+comments applied to
+.b .(l
+also apply to
+.b .(b
+and
+.b .(z ).
+This kind of display
+will be indented from both margins.
+For example,
+the input:
+.(b
+\&.(l F
+And now boys and girls,
+a newer, bigger, better toy than ever before!
+Be the first on your block to have your own computer!
+Yes kids, you too can have one of these modern
+data processing devices.
+You too can produce beautifully formatted papers
+without even batting an eye!
+\&.)l
+.)b
+will be output as:
+.(b F
+And now boys and girls,
+a newer, bigger, better toy than ever before!
+Be the first on your block to have your own computer!
+Yes kids, you too can have one of these modern
+data processing devices.
+You too can produce beautifully formatted papers
+without even batting an eye!
+.)b
+.pp
+Lists and blocks are also normally indented
+(floating keeps are normally left justified).
+To get a left-justified list,
+type
+.b ".(l\ L" .
+To get a list centered
+line-for-line,
+type
+.b ".(l C" .
+For example,
+to get a filled,
+left justified list, enter:
+.(b
+\&.(l L F
+text of block
+\&.)l
+.)b
+The input:
+.(b
+\&.(l
+first line of unfilled display
+more lines
+\&.)l
+.)b
+produces the indented text:
+.(b
+first line of unfilled display
+more lines
+.)b
+Typing the character
+.b L
+after the
+.b .(l
+request produces the left justified result:
+.(b L
+first line of unfilled display
+more lines
+.)b
+Using
+.b C
+instead of
+.b L
+produces the line-at-a-time centered output:
+.(b C
+first line of unfilled display
+more lines
+.)b
+.pp
+Sometimes it may be
+that you want to center several lines
+as a group,
+rather than centering them
+one line at a time.
+To do this
+use centered blocks,
+which are surrounded by the requests
+.b .(c
+and
+.b .)c .
+All the lines are centered as a unit,
+such that the longest line is centered
+and the rest are
+lined up around that line.
+Notice that lines
+do not move
+relative to each other
+using centered blocks,
+whereas they do
+using the
+.b C
+argument to keeps.
+.pp
+Centered blocks are
+.i not
+keeps,
+and may be used
+in conjunction
+with keeps.
+For example,
+to center a group of lines
+as a unit
+and keep them
+on one page,
+use:
+.(b
+\&.(b L
+\&.(c
+first line of unfilled display
+more lines
+\&.)c
+\&.)b
+.)b
+to produce:
+.(b L
+.(c
+first line of unfilled display
+more lines
+.)c
+.)b
+If the block requests
+(\c
+.b .(b
+and
+.b .)b )
+had been omitted
+the result would have been the same,
+but with no guarantee
+that the lines of the centered block
+would have all been on one page.
+Note the use of the
+.b L
+argument to
+.b .(b ;
+this causes the centered block
+to center within the entire line
+rather than within the line
+minus the indent.
+Also,
+the center requests
+must
+be nested
+.i inside
+the keep requests.
+.sh 1 "Annotations"
+.pp
+There are a number of requests
+to save text
+for later printing.
+.i Footnotes
+are printed at the bottom of the current page.
+.i "Delayed text"
+is intended to be a variant form
+of footnote;
+the text is printed only
+when explicitly called for,
+such as at the end of each chapter.
+.i Indexes
+are a type of delayed text
+having a tag
+(usually the page number)
+attached to each entry
+after a row of dots.
+Indexes are also saved
+until called for explicitly.
+.sh 2 "Footnotes"
+.pp
+Footnotes begin with the request
+.b .(f
+and end with the request
+.b .)f .
+The current footnote number is maintained
+automatically,
+and can be used by typing \e**,
+to produce a footnote number\**.
+.(f
+\**Like this.
+.)f
+The number is automatically incremented
+after every footnote.
+For example,
+the input:
+.(b
+\&.(q
+A man who is not upright
+and at the same time is presumptuous;
+one who is not diligent and at the same time is ignorant;
+one who is untruthful and at the same time is incompetent;
+such men I do not count among acquaintances.\e**
+\&.(f
+\e**James R. Ware,
+\&.ul
+The Best of Confucius,
+Halcyon House, 1950.
+Page 77.
+\&.)f
+\&.)q
+.)b
+generates the result:
+.(q
+A man who is not upright
+and at the same time is presumptuous;
+one who is not diligent and at the same time is ignorant;
+one who is untruthful and at the same time is incompetent;
+such men I do not count among acquaintances.\**
+.(f
+\**James R. Ware,
+.ul
+The Best of Confucius,
+Halcyon House, 1950.
+Page 77.
+.)f
+.)q
+It is important
+that the footnote
+appears
+.i inside
+the quote,
+so that you can be sure
+that the footnote
+will appear
+on the same page
+as the quote.
+.sh 2 "Delayed Text"
+.pp
+Delayed text
+is very similar to a footnote
+except that it is printed
+when called for explicitly.
+This allows a list of
+references to
+appear
+(for example)
+at the end of each chapter,
+as is the convention in some disciplines.
+Use
+.b \e*#
+on delayed text
+instead of
+.b \e**
+as on footnotes.
+.pp
+If you are using delayed text
+as your standard reference mechanism,
+you can still use footnotes,
+except that you may want to reference them
+with special characters*
+.(f
+*Such as an asterisk.
+.)f
+rather than numbers.
+.sh 2 "Indexes"
+.pp
+An
+.q index
+(actually more like a table of contents,
+since the entries are not sorted alphabetically)
+resembles delayed text,
+in that it is saved until called for.
+However,
+each entry has the page number
+(or some other tag)
+appended to the last line
+of the index entry
+after a row of dots.
+.pp
+Index entries begin with the request
+.b .(x
+and end with
+.b .)x .
+The
+.b .)x
+request may have a argument,
+which is the value to print
+as the
+.q "page number" .
+It defaults to the current page number.
+If the page number given is an underscore
+(\c
+.q _ )
+no page number
+or line of dots
+is printed at all.
+To get the line of dots
+without a page number,
+type
+.b ".)x """"" ,
+which specifies an explicitly null page number.
+.pp
+The
+.b .xp
+request prints the index.
+.pp
+For example,
+the input:
+.(b
+\&.(x
+Sealing wax
+\&.)x
+\&.(x
+Cabbages and kings
+\&.)x _
+\&.(x
+Why the sea is boiling hot
+\&.)x 2.5a
+\&.(x
+Whether pigs have wings
+\&.)x ""
+\&.(x
+This is a terribly long index entry, such as might be used
+for a list of illustrations, tables, or figures; I expect it to
+take at least two lines.
+\&.)x
+\&.xp
+.)b
+generates:
+.(x
+Sealing wax
+.)x
+.(x
+Cabbages and kings
+.)x _
+.(x
+Why the sea is boiling hot
+.)x 2.5a
+.(x
+Whether pigs have wings
+.)x ""
+.(x
+This is a terribly long index entry, such as might be used
+for a list of illustrations, tables, or figures; I expect it to
+take at least two lines.
+.)x
+.xp
+.pp
+The
+.b .(x
+request may have a single character
+argument,
+specifying the
+.q name
+of the index;
+the normal index is
+.b x .
+Thus,
+several
+.q indices
+may be maintained simultaneously
+(such as a list of tables, table of contents, etc.).
+.pp
+Notice that the index must be printed
+at the
+.i end
+of the paper,
+rather than at the beginning
+where it will probably appear
+(as a table of contents);
+the pages may have to be physically rearranged
+after printing.
+.sh 1 "Fancier Features"
+.pp
+A large number of fancier requests
+exist,
+notably requests to provide other sorts of paragraphs,
+numbered sections of the form
+.b 1.2.3
+(such as used in this document),
+and multicolumn output.
+.sh 2 "More Paragraphs"
+.pp
+Paragraphs generally start with
+a blank line
+and with the first line
+indented.
+It is possible to get
+left-justified block-style paragraphs
+by using
+.b .lp
+instead of
+.b .pp ,
+as demonstrated by the next paragraph.
+.lp
+Sometimes you want to use paragraphs
+that have the
+.i body
+indented,
+and the first line
+exdented
+(opposite of indented)
+with a label.
+This can be done with the
+.b .ip
+request.
+A word specified on the same line as
+.b .ip
+is printed in the margin,
+and the body is lined up
+at a prespecified position
+(normally five spaces).
+For example,
+the input:
+.(b
+\&.ip one
+This is the first paragraph.
+Notice how the first line
+of the resulting paragraph lines up
+with the other lines in the paragraph.
+\&.ip two
+And here we are at the second paragraph already.
+You may notice that the argument to \c
+.b .ip
+appears
+in the margin.
+\&.lp
+We can continue text...
+.)b
+produces as output:
+.ip one
+This is the first paragraph.
+Notice how the first line of the resulting paragraph lines up
+with the other lines in the paragraph.
+.ip two
+And here we are at the second paragraph already.
+You may notice that the argument to
+.b .ip
+appears
+in the margin.
+.lp
+We can continue text without starting a new indented
+paragraph
+by using the
+.b .lp
+request.
+.pp
+If you have spaces in the label of a
+.b .ip
+request,
+you must use an
+.q "unpaddable space"
+instead of a regular space.
+This is typed as a backslash character
+(\c
+.q \e )
+followed by a space.
+For example,
+to print the label
+.q "Part 1" ,
+enter:
+.(b
+\&.ip "Part\e 1"
+.)b
+.pp
+If a label of an indented paragraph
+(that is, the argument to
+.b .ip )
+is longer than the space allocated for the label,
+.b .ip
+will begin a new line after the label.
+For example,
+the input:
+.(b
+\&.ip longlabel
+This paragraph had a long label.
+The first character of text on the first line
+will not line up with the text on second and subsequent lines,
+although they will line up with each other.
+.)b
+will produce:
+.ip longlabel
+This paragraph had a long label.
+The first character of text on the first line
+will not line up with the text on second and subsequent lines,
+although they will line up with each other.
+.pp
+It is possible to change the size of the label
+by using a second argument
+which is the size of the label.
+For example,
+the above example could be done correctly
+by saying:
+.(b
+\&.ip longlabel 10
+.)b
+which will make the paragraph indent
+10 spaces for this paragraph only.
+If you have many paragraphs to indent
+all the same amount,
+use the
+.i "number register"
+.b ii .
+For example, to leave one inch of space
+for the label,
+type:
+.(b
+\&.nr ii 1i
+.)b
+somewhere before the first call to
+.b .ip .
+Refer to the reference manual
+for more information.
+.pp
+If
+.b .ip
+is used
+with no argument at all
+no hanging tag will be printed.
+For example,
+the input:
+.(b
+\&.ip [a]
+This is the first paragraph of the example.
+We have seen this sort of example before.
+\&.ip
+This paragraph is lined up with the previous paragraph,
+but it has no tag in the margin.
+.)b
+produces as output:
+.ip [a]
+This is the first paragraph of the example.
+We have seen this sort of example before.
+.ip
+This paragraph is lined up with the previous paragraph,
+but it has no tag in the margin.
+.pp
+A special case of
+.b .ip
+is
+.b .np ,
+which automatically
+numbers paragraphs sequentially from 1.
+The numbering is reset at the next
+.b .pp ,
+.b .lp ,
+or
+.b .sh
+(to be described in the next section)
+request.
+For example,
+the input:
+.(b
+\&.np
+This is the first point.
+\&.np
+This is the second point.
+Points are just regular paragraphs
+which are given sequence numbers automatically
+by the .np request.
+\&.pp
+This paragraph will reset numbering by .np.
+\&.np
+For example,
+we have reverted to numbering from one now.
+.)b
+generates:
+.np
+This is the first point.
+.np
+This is the second point.
+Points are just regular paragraphs
+which are given sequence numbers automatically
+by the .np request.
+.pp
+This paragraph will reset numbering by .np.
+.np
+For example,
+we have reverted to numbering from one now.
+.pp
+The
+.b .bu
+request gives lists of this sort that are identified with
+bullets rather than numbers.
+The paragraphs are also crunched together.
+For example,
+the input:
+.(b
+\&.bu
+\&One egg yolk
+\&.bu
+\&One tablespoon cream or top milk
+\&.bu
+\&Salt, cayenne, and lemon juice to taste
+\&.bu
+\&A generous two tablespoonfuls of butter
+.)b
+produces\**:
+.(f
+\**By the way,
+if you put the first three ingredients in a a heavy, deep pan
+and whisk the ingredients madly over a medium flame
+(never taking your hand off the handle of the pot)
+until the mixture reaches the consistency of custard
+(just a minute or two),
+then mix in the butter off-heat,
+you will have a wonderful Hollandaise sauce.
+.)f
+.bu
+One egg yolk
+.bu
+One tablespoon cream or top milk
+.bu
+Salt, cayenne, and lemon juice to taste
+.bu
+A generous two tablespoonfuls of butter
+.sh 2 "Section Headings"
+.pp
+Section numbers
+(such as the ones used in this document)
+can be automatically generated
+using the
+.b .sh
+request.
+You must tell
+.b .sh
+the
+.i depth
+of the section number
+and a section title.
+The depth
+specifies how many numbers
+are to appear
+(separated by decimal points)
+in the section number.
+For example,
+the section number
+.b 4.2.5
+has a depth of three.
+.pp
+Section numbers
+are incremented
+in a fairly intuitive fashion.
+If you add a number
+(increase the depth),
+the new number starts out
+at one.
+If you subtract section numbers
+(or keep the same number)
+the final number is incremented.
+For example,
+the input:
+.(b
+\&.sh 1 "The Preprocessor"
+\&.sh 2 "Basic Concepts"
+\&.sh 2 "Control Inputs"
+\&.sh 3
+\&.sh 3
+\&.sh 1 "Code Generation"
+\&.sh 3
+.)b
+produces as output the result:
+.(b
+.b
+1. The Preprocessor
+1.1. Basic Concepts
+1.2. Control Inputs
+1.2.1.
+1.2.2.
+2. Code Generation
+2.1.1.
+.)b
+.pp
+You can specify the section number to begin
+by placing the section number after the section title,
+using spaces instead of dots.
+For example,
+the request:
+.(b
+\&.sh 3 "Another section" 7 3 4
+.)b
+will begin the section numbered
+.b 7.3.4 ;
+all subsequent
+.b .sh
+requests will number relative to this number.
+.pp
+There are more complex features
+which will cause each section to be indented
+proportionally to the depth of the section.
+For example, if you enter:
+.(b
+\&.nr si \c
+.i N
+.)b
+each section will be indented by an amount
+.i N .
+.i N
+must have a scaling factor attached,
+that is, it must be of the form
+.i Nx ,
+where
+.i x
+is a character telling what units
+.i N
+is in.
+Common values for
+.i x
+are
+.b i
+for inches,
+.b c
+for centimeters,
+and
+.b n
+for
+.i ens
+(the width of a single character).
+For example,
+to indent each section
+one-half inch,
+type:
+.(b
+\&.nr si 0.5i
+.)b
+After this,
+sections will be indented by
+one-half inch
+per level of depth in the section number.
+For example,
+this document was produced
+using the request
+.(b
+\&.nr si 3n
+.)b
+at the beginning of the input file,
+giving three spaces of indent
+per section depth.
+.pp
+Section headers without automatically generated numbers
+can be done using:
+.(b
+\&.uh "Title"
+.)b
+which will do a section heading,
+but will put no number on the section.
+.sh 2 "Parts of the Basic Paper"
+.pp
+There are some requests
+which assist in setting up
+papers.
+The
+.b .tp
+request
+initializes for a title page.
+There are no headers or footers
+on a title page,
+and unlike other pages
+you can space down
+and leave blank space
+at the top.
+For example,
+a typical title page might appear as:
+.(b
+\&.tp
+\&.sp 2i
+\&.(l C
+THE GROWTH OF TOENAILS
+IN UPPER PRIMATES
+\&.sp
+by
+\&.sp
+Frank N. Furter
+\&.)l
+\&.bp
+.)b
+.pp
+The
+.b .+c \ \c
+.i T
+request can be used
+to start chapters.
+Each chapter is automatically numbered
+from one,
+and a heading is printed at the top of each chapter
+with the chapter number
+and the chapter name
+.i T .
+For example,
+to begin a chapter called
+.q Conclusions ,
+use the request:
+.(b
+\&.+c "CONCLUSIONS"
+.)b
+which will produce,
+on a new page,
+the lines
+.(b C
+CHAPTER 5
+CONCLUSIONS
+.)b
+with appropriate spacing for a thesis.
+Also, the header is moved to the foot of the page
+on the first page of a chapter.
+Although the
+.b .+c
+request was not designed to work only with the
+.b .th
+request,
+it is tuned for the format acceptable
+for a PhD thesis
+at Berkeley.
+.pp
+If the
+title parameter
+.i T
+is omitted from the
+.b .+c
+request,
+the result is a chapter with no heading.
+This can also be used at the beginning
+of a paper;
+for example,
+.b .+c
+was used to generate page one
+of this document.
+.pp
+Although
+papers traditionally have the abstract,
+table of contents,
+and so forth at the front of the paper,
+it is more convenient to format
+and print them last
+when using \*G.
+This is so that index entries
+can be collected and then printed
+for the table of contents
+(or whatever).
+At the end of the paper,
+issue the
+.b ".++ P"
+request,
+which begins the preliminary part
+of the paper.
+After issuing this request,
+the
+.b .+c
+request will begin a preliminary section
+of the paper.
+Most notably,
+this prints the page number
+restarted from one
+in lower case Roman numbers.
+.b .+c
+may be used repeatedly
+to begin different parts of the
+front material
+for example,
+the abstract,
+the table of contents,
+acknowledgments,
+list of illustrations,
+etc.
+The request
+.b ".++ B"
+may also be used
+to begin the bibliographic section
+at the end of the paper.
+For example,
+the paper might appear
+as outlined in figure 2.
+(In this figure,
+comments begin with the sequence
+.b \e" .)
+.(z
+.hl
+.if t .in 0.5i
+.if t .ta 2i
+.if n .ta 3i
+\&.th \e" set for thesis mode
+\&.fo \(aa\(aaDRAFT\(aa\(aa \e" define footer for each page
+\&.tp \e" begin title page
+\&.(l C \e" center a large block
+THE GROWTH OF TOENAILS
+IN UPPER PRIMATES
+\&.sp
+by
+\&.sp
+Frank Furter
+\&.)l \e" end centered part
+\&.+c INTRODUCTION \e" begin chapter named "INTRODUCTION"
+\&.(x t \e" make an entry into index `t'
+Introduction
+\&.)x \e" end of index entry
+text of chapter one
+\&.+c "NEXT CHAPTER" \e" begin another chapter
+\&.(x t \e" enter into index `t' again
+Next Chapter
+\&.)x
+text of chapter two
+\&.+c CONCLUSIONS
+\&.(x t
+Conclusions
+\&.)x
+text of chapter three
+\&.++ B \e" begin bibliographic information
+\&.+c BIBLIOGRAPHY \e" begin another `chapter'
+\&.(x t
+Bibliography
+\&.)x
+text of bibliography
+\&.++ P \e" begin preliminary material
+\&.+c "TABLE OF CONTENTS"
+\&.xp t \e" print index `t' collected above
+\&.+c PREFACE \e" begin another preliminary section
+text of preface
+.sp 2
+.in 0
+.ce
+Figure 2. Outline of a Sample Paper
+.hl
+.)z
+.sh 2 "Equations and Tables"
+.pp
+Two special \*U programs exist
+to format special types of material.
+.b Eqn
+sets equations.
+.b Tbl
+arranges to print
+extremely pretty tables
+in a variety of formats.
+This document will only describe
+the embellishments
+to the standard features;
+consult the reference manuals
+for those processors
+for a description of their use.
+.pp
+The
+.b eqn
+program is described fully
+in the document
+.ul
+Typesetting Mathematics \- User's Guide
+by Brian W. Kernighan
+and Lorinda L. Cherry.
+Equations are centered,
+and are kept on one page.
+They are introduced by the
+.b .EQ
+request and terminated by the
+.b .EN
+request.
+.pp
+The
+.b .EQ
+request may take an
+equation number as an
+optional argument,
+which is printed vertically centered
+on the right hand side
+of the equation.
+If the equation becomes too long
+it should be split
+between two lines.
+To do this, type:
+.(b
+\&.EQ (eq 34)
+text of equation 34
+\&.EN C
+\&.EQ
+continuation of equation 34
+\&.EN
+.)b
+The
+.b C
+on the
+.b .EN
+request
+specifies that the equation
+will be continued.
+.pp
+The
+.b tbl
+program produces tables.
+It is fully described
+(including numerous examples)
+in the document
+.ul
+Tbl \- A Program to Format Tables
+by M. E. Lesk.
+Tables begin with the
+.b .TS
+request
+and end with the
+.b .TE
+request.
+Tables are normally kept on a single page.
+If you have a table which is too big
+to fit on a single page,
+so that you know it will extend
+to several pages,
+begin the table with the request
+.b ".TS\ H"
+and put the request
+.b .TH
+after the part of the table
+which you want
+duplicated at the top of every page
+that the table is printed on.
+For example, a table definition
+for a long table might look like:
+.ds TA \|\h'.4n'\v'-.2n'\s-4\zT\s0\v'.2n'\h'-.4n'\(ci\|
+.if n .ds TA \ \o'-T'\ \"
+.(b
+\&.TS H
+c s s
+n n n.
+THE TABLE TITLE
+\&.TH
+text of the table
+\&.TE
+.)b
+.pp
+.sh 2 "Two Column Output"
+.pp
+You can get two column output
+automatically
+by using the request
+.b .2c .
+This causes everything after it
+to be output in two-column form.
+The request
+.b .bc
+will start a new column;
+it differs from
+.b .bp
+in that
+.b .bp
+may leave a totally blank column
+when it starts a new page.
+To revert to single column output,
+use
+.b .1c .
+.sh 2 "Defining Macros"
+.pp
+A
+.i macro
+is a collection of requests and text
+which may be used
+by stating a simple request.
+Macros begin with the line
+.b ".de" \ \c
+.i xx
+(where
+.i xx
+is the name of the macro to be defined)
+and end with the line consisting of two dots.
+After defining the macro,
+stating the line
+.b . \c
+.i xx
+is the same as stating all the other lines.
+For example,
+to define a macro
+that spaces 3 lines
+and then centers the next input line,
+enter:
+.(b
+\&.de SS
+\&.sp 3
+\&.ce
+\&..
+.)b
+and use it by typing:
+.(b
+\&.SS
+\&Title Line
+(beginning of text)
+.)b
+.pp
+Macro names may be one or two characters.
+In order to avoid conflicts
+with names in \-me,
+always use upper case letters as names.
+The only names to avoid are
+.b TS ,
+.b TH ,
+.b TE ,
+.b EQ ,
+and
+.b EN .
+.sh 2 "Annotations Inside Keeps"
+.pp
+Sometimes you may want to put
+a footnote
+or index entry inside a keep.
+For example,
+if you want to maintain a
+.q "list of figures"
+you will want to do something like:
+.(b
+\&.(z
+\&.(c
+text of figure
+\&.)c
+\&.ce
+Figure 5.
+\&.(x f
+Figure 5
+\&.)x
+\&.)z
+.)b
+which you may hope
+will give you a figure
+with a label
+and an entry in the index
+.b f
+(presumably a list of figures index).
+Unfortunately,
+the
+index entry
+is read and interpreted
+when the keep is read,
+not when it is printed,
+so the page number in the index is likely to be wrong.
+The solution is to use the magic string
+.b \e!
+at the beginning of all the lines dealing with the index.
+In other words,
+you should use:
+.(b
+\&.(z
+\&.(c
+Text of figure
+\&.)c
+\&.ce
+Figure 5.
+\e!.(x f
+\e!Figure 5
+\e!.)x
+\&.)z
+.)b
+which will defer the processing of the index
+until the figure is output.
+This will guarantee
+that the page number in the index
+is correct.
+The same comments apply
+to
+blocks
+(with
+.b .(b
+and
+.b .)b )
+as well.
+.sh 1 "\*T and the Photosetter"
+.pp
+With a little care,
+you can prepare
+documents that
+will print nicely
+on either a regular terminal
+or when phototypeset
+using the \*T formatting program.
+.sh 2 "Fonts"
+.pp
+A
+.i font
+is a style of type.
+There are three fonts
+that are available simultaneously,
+Times Roman,
+Times Italic,
+and Times Bold,
+plus the special math font.
+The normal font is Roman.
+.pp
+There are ways of switching between fonts.
+The requests
+.b .r ,
+.b .i ,
+.b .b ,
+and
+.b .bi
+switch to Roman,
+italic,
+bold,
+and bold-italic fonts respectively.
+You can set a single word
+in some font
+by typing (for example):
+.(b
+\&.i word
+.)b
+which will set
+.i word
+in italics
+but does not affect the surrounding text.
+.pp
+Notice that if you are setting more than one word
+in whatever font,
+you must surround that word with double quote marks
+(`\|"\|')
+so that it will appear to the \*G processor as a single word.
+The quote marks will not appear in the formatted text.
+If you do want a quote mark to appear,
+you should quote the entire string
+(even if a single word),
+and use
+.i two
+quote marks where you want one to appear.
+For example,
+if you want to produce the text:
+.(b
+.i """Master Control\|"""
+.)b
+in italics, you must type:
+.(b
+\&.i """Master Control\e|"""
+.)b
+The
+.b \e|
+produces a very narrow space
+so that the
+.q l
+does not overlap the quote sign in \*G,
+like this:
+.(b
+.i """Master Control"""
+.)b
+.pp
+There are also some
+.q pseudo-fonts
+available.
+The input:
+.(b
+\&.(b
+\&.u underlined
+\&.bx "words in a box"
+\&.)b
+.)b
+generates
+.(b
+.u underlined
+.bx "words in a box"
+.)b
+Notice that pseudo font requests
+set only the single parameter in the pseudo font;
+ordinary font requests will begin setting all text
+in the special font
+if you do not provide a parameter.
+No more than one word
+should appear
+with these three font requests
+in the middle of lines.
+This is because
+of the way \*G justifies text.
+For example,
+if you were to issue the requests:
+.(b
+\&.u "some bold italics"
+and
+\&.bx "words in a box"
+.)b
+in the middle of a line
+\*G would produce
+.u "some bold italics"
+and
+.bx "words in a box" ,\p
+which I think you will agree does not look good.
+.pp
+The second parameter
+of all font requests
+is set in the original font.
+For example,
+the font request:
+.(b
+\&.b bold face
+.)b
+generates
+.q bold
+in bold font,
+but sets
+.q face
+in the font of the surrounding text,
+resulting in:
+.(b
+.b bold face.
+.)b
+To set the two words
+.b bold
+and
+.b face
+both in
+.b "bold face" ,
+type:
+.(b
+\&.b "bold face"
+.)b
+.pp
+You can mix fonts in a word by using the
+special sequence
+.b \ec
+at the end of a line
+to indicate
+.q "continue text processing" ;
+this allows input lines
+to be joined together
+without a space between them.
+For example, the input:
+.(b
+\&.u under \ec
+\&.i italics
+.)b
+generates
+.u under \c
+.i italics ,
+but if we had typed:
+.(b
+\&.u under
+\&.i italics
+.)b
+the result would have been
+.u under
+.i italics
+as two words.
+.sh 2 "Point Sizes"
+.pp
+The phototypesetter
+supports different sizes of type,
+measured in points.
+The default point size
+is 10 points
+for most text,
+8 points for footnotes.
+To change the pointsize,
+type:
+.(b
+\&.sz \c
+.i +N
+.)b
+where
+.i N
+is the size wanted in points.
+The
+.i "vertical spacing"
+(distance between the bottom of most letters
+(the
+.i baseline )
+between adjacent lines)
+is set to be proportional
+to the type size.
+.pp
+These pointsize changes are
+.i temporary !!!
+For example,
+to reset the pointsize of basic text to twelve point, use:
+.(b
+\&.nr pp 12
+\&.nr sp 12
+\&.nr tp 12
+.)b
+to reset the default pointsize of
+paragraphs,
+section headers,
+and titles respectively.
+If you only want to set the names of sections in a larger pointsize,
+use:
+.(b
+\&.nr sp 11
+.)b
+alone \*- this sets section titles
+(e.g.,
+.b "Point Sizes"
+above)
+in a larger font than the default.
+.pp
+A single word or phrase can be set in a smaller pointsize
+than the surrounding text
+using the
+.b .sm
+request.
+This is especially convenient for words that are all capitals,
+due to the optical illusion that makes them look even larger
+than they actually are.
+For example:
+.(b
+\&.sm UNIX
+.)b
+prints as
+.sm UNIX
+rather than
+UNIX.
+.pp
+Warning:
+changing point sizes
+on the phototypesetter
+is a slow mechanical operation.
+On laser printers it may require loading new fonts.
+Size changes
+should be considered carefully.
+.sh 2 "Quotes"
+.pp
+It is conventional when using
+the typesetter to
+use pairs of grave and acute accents
+to generate double quotes,
+rather than the
+double quote character
+(`\|"\|').
+This is because it looks better
+to use grave and acute accents;
+for example, compare
+"quote" to
+``quote''.
+.pp
+You may use the sequences
+.b \e*(lq
+and
+.b \e*(rq
+to stand for the left and right quote
+respectively.
+For example,
+use:
+.(b
+\e*(lqSome things aren\(aat true
+even if they did happen.\e*(rq
+.)b
+to generate the result:
+.(b
+.q "Some things aren't true even if they did happen."
+.)b
+As a shorthand,
+the special font request:
+.(b
+\&.q "quoted text"
+.)b
+will generate
+.q "quoted text" .
+Notice that you must surround
+the material to be quoted
+with double quote marks
+if it is more than one word.
+.sh 0
+.sp 1i
+.b Acknowledgments
+.pp
+I would like to thank
+Bob Epstein,
+Bill Joy,
+and Larry Rowe
+for having the courage
+to use the \-me macros
+to produce non-trivial papers
+during the development stages;
+Ricki Blau,
+Pamela Humphrey,
+and Jim Joyce
+for their help with the documentation phase;
+peter kessler
+for numerous complaints years after I was
+.q done
+with this project,
+most accompanied by fixes
+(hence forcing me to fix several small bugs);
+and the plethora of people who have contributed ideas
+and have given support for the project.
+.sp 1i
+This document was
+\*G'ed
+on \*(td
+and applies to the version of the \-me macros
+included with \*G version \*(MO.
diff --git a/doc/meref.me b/doc/meref.me
new file mode 100644
index 000000000..736698a59
--- /dev/null
+++ b/doc/meref.me
@@ -0,0 +1,2194 @@
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)ref.me 6.4 (Berkeley) 7/17/89
+.\"
+.\" Modified by jjc@jclark.uucp for groff.
+.\"UC 7
+.ll 6.5i
+.lt 6.5i
+.\"pn 0
+.ds MO @VERSION@
+.de TL \" *** title line
+.lp
+.di XX
+..
+.ie \n(.g \{\
+.de DE
+\?\h'|\\n(DIu'\c\?
+..
+.\}
+.el \{\
+.de DE
+\\\\h'|\\\\n(DIu'\\\\c
+..
+.\}
+.am DE
+.br
+.di
+.in +\\n(DIu
+.ti 0
+.cu 1000
+.XX
+.rm XX
+.cu 0
+..
+.ds G \s-1GROFF\s0
+.ds N \s-1NROFF\s0
+.ds T \s-1TROFF\s0
+.nr DI 1.5i
+.he '\-ME REFERENCE MANUAL''%'
+.de NR
+.b "\en\\$1" "\\$2"
+..
+.de ST
+.b "\e*\\$1" "\\$2"
+..
+.\"sc
+.\"eh 'USD:23-%''\-me Reference Manual'
+.\"oh '\-me Reference Manual''USD:23-%'
+.+c
+.ce 20
+.sz 14
+.b "\-ME REFERENCE MANUAL"
+.sz
+.sp
+.i "\*G Version \*(MO\(dg"
+.(f
+\(dgBased on Berkeley Release 2.31.
+.)f
+.sp 2
+.ul
+Eric P. Allman*
+.(f
+*Author's current address:
+Britton Lee, Inc.,
+1919 Addison Suite 105,
+Berkeley, California 94704.
+.)f
+.sp
+Project INGRES
+Electronics Research Laboratory
+University of California, Berkeley
+Berkeley, California 94720
+.sp 2
+.i "Modified for \*G by James Clark"
+.ce 0
+.sp 4
+.pp
+This document describes
+in extremely terse form
+the features
+of the
+.b \-me
+macro package
+for \*G.
+Some familiarity is assumed
+with
+\*G.
+Specifically,
+the reader should understand
+breaks,
+fonts,
+pointsizes,
+the use and definition of number registers
+and strings,
+how to define macros,
+and scaling factors for ens, points,
+.b v 's
+(vertical line spaces),
+etc.
+.pp
+For a more casual introduction
+to text processing
+using \*G,
+refer to the document
+.ul
+Writing Papers with \*G using \-me.
+.pp
+There are a number of macro parameters
+that may be adjusted.
+Fonts may be set to a font number only.
+Font 0 is no font change;
+the font of the surrounding text
+is used instead.
+Notice that font 0 is a
+.q pseudo-font ;
+that is,
+it is simulated by the macros.
+This means that although it is legal to set a font register
+to zero,
+it is not legal to use the escape character form,
+such as:
+.(b
+\ef0
+.)b
+.pp
+All distances
+are in basic units,
+so it is nearly always necessary
+to use a scaling factor.
+For example,
+the request
+to set the paragraph indent
+to eight one-en spaces is:
+.(b
+\&.nr pi 8n
+.)b
+and not
+.(b
+\&.nr pi 8
+.)b
+which would set the paragraph indent to eight basic units,
+or about 0.02 inch.
+Default parameter values are given in brackets
+in the remainder of this document.
+.pp
+Registers and strings
+of the form
+.b $ \c
+.i x
+may be used in expressions
+but should not be changed.
+Macros of the form
+.b $ \c
+.i x
+perform some function
+(as described)
+and may be redefined
+to change this function.
+This may be a sensitive operation;
+look at the body of the original macro
+before changing it.
+.pp
+All names in \-me
+follow a rigid naming convention.
+The user may define number registers,
+strings,
+and macros,
+provided that s/he
+uses single character upper case names
+or double character names
+consisting of letters and digits,
+with at least one upper case letter.
+In no case should special characters
+be used in user-defined names.
+Locally defined macros
+should all be of the form
+.b .* \c
+.i X ,
+where
+.i X
+is any letter
+(upper or lower case)
+or digit.
+.pp
+This documentation was \*G'ed
+on \*(td
+and applies to \*G version
+\*(MO
+of the \-me macros.
+.sh 1 "Paragraphing"
+.pp
+These macros are used
+to begin paragraphs.
+The standard paragraph macro
+is
+.b .pp ;
+the others are all variants
+to be used for special purposes.
+.pp
+After the first call to one of the paragraphing macros
+defined in this section
+or the
+.b .sh
+macro
+(defined in the next session),
+the effects of changing parameters
+which will have a global effect
+on the format of the page
+(notably page length and header and footer margins)
+are not well defined
+and should be avoided.
+.TL
+.b .lp
+.DE
+Begin left-justified paragraph.
+Centering and underlining
+are turned off if they were on,
+the font is set to
+.NR (pf
+[1]
+the type size
+is set to
+.NR (pp
+[10p],
+and a
+.NR (ps
+space is inserted
+before the paragraph
+[0.35v]
+The indent is reset
+to
+.NR ($i
+[0]
+plus
+.NR (po
+[0]
+unless the paragraph
+is inside a display.
+(see
+.b .ba ).
+At least
+the first two lines
+of the paragraph
+are kept together
+on a page.
+.TL
+.b .pp
+.DE
+Like
+.b .lp ,
+except that it puts
+.NR (pi
+[5n]
+units of indent.
+This is the standard paragraph macro.
+.TL
+.b .ip
+.i T
+.i I
+.DE
+Indented paragraph
+with hanging tag.
+The body of the following paragraph
+is indented
+.i I
+spaces
+(or
+.NR (ii
+[5n]
+spaces
+if
+.i I
+is not specified)
+more than a non-indented paragraph
+(such as with
+.b .pp )
+is.
+The title
+.i T
+is exdented (opposite of indented).
+The result is a paragraph
+with an even left edge
+and
+.i T
+printed in the margin.
+Any spaces in
+.i T
+must be unpaddable.
+If
+.i T
+will not fit in the space provided,
+.b .ip
+will start a new line.
+.TL
+.b .np
+.DE
+A variant of .ip which numbers paragraphs.
+Numbering is reset
+after a
+.b .lp ,
+.b .pp ,
+or
+.b .sh .
+The current paragraph number
+is in
+.NR ($p .
+.TL
+.b .bu
+.DE
+Like
+.b .np
+except that paragraphs are marked with bullets (\(bu).
+Leading space is eliminated to create compact lists.
+.sh 1 "Section Headings"
+.pp
+Numbered sections
+are similar to paragraphs
+except that a
+section number
+is automatically
+generated for each one.
+The section numbers are of the form
+.b 1.2.3 .
+The
+.i depth
+of the section
+is the count of numbers
+(separated by decimal points)
+in the section number.
+.pp
+Unnumbered section headings are similar,
+except that no number is attached
+to the heading.
+.TL
+.b .sh
+.i +N
+.i T
+.i "a b c d e f"
+.DE
+Begin numbered section
+of depth
+.i N .
+If
+.i N
+is missing
+the current depth
+(maintained in
+the number register
+.NR ($0 )
+is used.
+The values of
+the individual parts of the section number
+are maintained in
+.NR ($1
+through
+.NR ($6 .
+There is a
+.NR (ss
+[1v]
+space before the section.
+.i T
+is printed
+as a section title
+in font
+.NR (sf
+[8]
+and size
+.NR (sp
+[10p].
+The
+.q name
+of the section may be accessed via
+.ST ($n .
+If
+.NR (si
+is non-zero,
+the base indent
+is set to
+.NR (si
+times the section depth,
+and the section title
+is exdented.
+(See
+.b .ba .)
+Also,
+an additional indent of
+.NR (so
+[0]
+is added to the section title
+(but not to the body of the section).
+The font is then set
+to the paragraph font,
+so that more information may occur
+on the line
+with the section number
+and title.
+.b .sh
+insures that there is enough room
+to print the section head
+plus the beginning of a paragraph
+(about 3 lines total).
+If
+.i a
+through
+.i f
+are specified,
+the section number is set to that number
+rather than incremented automatically.
+If any of
+.i a
+through
+.i f
+are a hyphen
+that number is not reset.
+If
+.i T
+is a single underscore
+(\c
+.q _ )
+then the section depth and numbering is reset,
+but the base indent is not reset
+and nothing is printed out.
+This is useful to automatically
+coordinate section numbers with
+chapter numbers.
+.TL
+.b .sx
+.i +N
+.DE
+Go to section depth
+.i N
+[\c
+.b \-1 ],
+but do not print the number
+and title,
+and do not increment the section number
+at level
+.i N .
+This has the effect
+of starting a new paragraph
+at level
+.i N .
+.TL
+.b .uh
+.i T
+.DE
+Unnumbered section heading.
+The title
+.i T
+is printed
+with the same rules for spacing,
+font, etc.,
+as for
+.b .sh .
+.TL
+.b .$p
+.i T
+.i B
+.i N
+.DE
+Print section heading.
+May be redefined
+to get fancier headings.
+.i T
+is the title passed on the
+.b .sh
+or
+.b .uh
+line;
+.i B
+is the section number for this section,
+and
+.i N
+is the depth of this section.
+These parameters are not always present;
+in particular,
+.b .sh
+passes all three,
+.b .uh
+passes only the first,
+and
+.b .sx
+passes three,
+but the first two
+are null strings.
+Care should be taken if this macro
+is redefined;
+it is quite complex and subtle.
+.TL
+.b .$0
+.i T
+.i B
+.i N
+.DE
+This macro is called automatically
+after every call to
+.b .$p .
+It is normally undefined,
+but may be used
+to automatically put
+every section title
+into the table of contents
+or for some similar function.
+.i T
+is the section title
+for the section title which was just printed,
+.i B
+is the section number,
+and
+.i N
+is the section depth.
+.TL
+.b .$1
+\-
+.b .$6
+.DE
+Traps called just before printing that depth section.
+May be defined to
+(for example)
+give variable spacing
+before sections.
+These macros are called from
+.b .$p ,
+so if you redefine that macro
+you may lose this feature.
+.sh 1 "Headers and Footers"
+.ds TP \fI\(aal\|\(aam\^\(aar\^\(aa\fP
+.pp
+Headers and footers
+are put at the top and bottom
+of every page
+automatically.
+They are set in font
+.NR (tf
+[3]
+and size
+.NR (tp
+[10p].
+Each of the definitions
+apply as of the
+.i next
+page.
+Three-part titles
+must be quoted
+if there are two blanks adjacent
+anywhere in the title
+or more than eight blanks total.
+.pp
+The spacing
+of headers and footers
+are controlled by three number registers.
+.NR (hm
+[4v]
+is the distance from the top of the page
+to the top of the header,
+.NR (fm
+[3v]
+is the distance from the bottom of the page
+to the bottom of the footer,
+.NR (tm
+[7v]
+is the distance from the top of the page
+to the top of the text,
+and
+.NR (bm
+[6v]
+is the distance from the bottom of the page
+to the bottom of the text
+(nominal).
+The macros
+.b .m1 ,
+.b .m2 ,
+.b .m3 ,
+and
+.b .m4
+are also supplied for compatibility
+with
+\s-1ROFF\s0 documents.
+.TL
+.b .he
+\*(TP
+.DE
+Define three-part header,
+to be printed on the top
+of every page.
+.TL
+.b .fo
+\*(TP
+.DE
+Define footer,
+to be printed at the bottom
+of every page.
+.TL
+.b .eh
+\*(TP
+.DE
+Define header,
+to be printed at the top of every
+even-numbered page.
+.TL
+.b .oh
+\*(TP
+.DE
+Define header,
+to be printed at the top of every
+odd-numbered page.
+.TL
+.b .ef
+\*(TP
+.DE
+Define footer,
+to be printed at the bottom
+of every even-numbered page.
+.TL
+.b .of
+\*(TP
+.DE
+Define footer,
+to be printed at the bottom
+of every odd-numbered page.
+.TL
+.b .hx
+.DE
+Suppress headers and footers
+on the next page.
+.TL
+.b .m1
+.i +N
+.DE
+Set the space between the top of the page
+and the header
+[4v].
+.TL
+.b .m2
+.i +N
+.DE
+Set the space between the header
+and the first line of text
+[2v].
+.TL
+.b .m3
+.i +N
+.DE
+Set the space
+between the bottom of the text
+and the footer
+[2v].
+.TL
+.b .m4
+.i +N
+.DE
+Set the space
+between the footer
+and the bottom of the page
+[4v].
+.TL
+.b .ep
+.DE
+End this page,
+but do not begin the next page.
+Useful for forcing out footnotes,
+but other than
+that hardly every used.
+Must be followed by a
+.b .bp
+or the end of input.
+.TL
+.b .$h
+.DE
+Called at every page
+to print the header.
+May be redefined
+to provide fancy
+(e.g.,
+multi-line)
+headers,
+but doing so
+loses the function of the
+.b .he ,
+.b .fo ,
+.b .eh ,
+.b .oh ,
+.b .ef ,
+and
+.b .of
+requests,
+as well as the chapter-style title feature
+of
+.b .+c .
+.TL
+.b .$f
+.DE
+Print footer;
+same comments apply
+as in
+.b .$h .
+.TL
+.b .$H
+.DE
+A normally undefined macro
+which is called
+at the top of each page
+(after putting out
+the header,
+initial saved floating keeps,
+etc.);
+in other words,
+this macro is called immediately before
+printing text
+on a page.
+It can be used for column headings
+and the like.
+.sh 1 "Displays"
+.pp
+All displays except centered blocks
+and block quotes
+are preceded and followed
+by an extra
+.NR (bs
+[same as
+.NR (ps ]
+space.
+Quote spacing is stored in a separate register;
+centered blocks have no default initial or trailing space.
+The vertical spacing of all displays except quotes
+and centered blocks
+is stored in register
+.NR ($R
+instead of
+.NR ($r .
+.TL
+.b .(l
+.i m
+.i f
+.DE
+Begin list.
+Lists are single spaced,
+unfilled text.
+If
+.i f
+is
+.b F ,
+the list will be filled.
+If
+.i m
+[\c
+.b I ]
+is
+.b I
+the list is indented by
+.NR (bi
+[4m];
+if
+.b M
+the list is indented to the left margin;
+if
+.b L
+the list is left justified with respect to the text
+(different from
+.b M
+only if the base indent
+(stored in
+.NR ($i
+and set with
+.b .ba )
+is not zero);
+and if
+.b C
+the list is centered on a line-by-line basis.
+The list is set in font
+.NR (df
+[0].
+Must be matched by a
+.b .)l .
+This macro is almost like
+.b .(b
+except that no attempt is made
+to keep the display on one page.
+.TL
+.b .)l
+.DE
+End list.
+.TL
+.b .(q
+.DE
+Begin major quote.
+These are single spaced,
+filled,
+moved in from the text
+on both sides
+by
+.NR (qi
+[4n],
+preceded and followed
+by
+.NR (qs
+[same as
+.NR (bs ]
+space,
+and are set in point size
+.NR (qp
+[one point smaller than surrounding text].
+.TL
+.b .)q
+.DE
+End major quote.
+.TL
+.b .(b
+.i m
+.i f
+.DE
+Begin block.
+Blocks are a form of
+.i keep ,
+where the text of a keep
+is kept together on one page
+if possible
+(keeps are useful
+for tables and figures
+which should not be broken
+over a page).
+If the block will not fit
+on the current page
+a new page is begun,
+.i unless
+that would leave more than
+.NR (bt
+[0]
+white space
+at the bottom of the text.
+If
+.NR (bt
+is zero, the threshold feature
+is turned off.
+Blocks are not filled
+unless
+.i f
+is
+.b F ,
+when they are filled.
+The block will be left-justified
+if
+.i m
+is
+.b L ,
+indented by
+.NR (bi
+[4m]
+if
+.i m
+is
+.b I
+or absent,
+centered
+(line-for-line)
+if
+.i m
+is
+.b C ,
+and left justified to the margin
+(not to the base indent)
+if
+.i m
+is
+.b M .
+The block is set in font
+.NR (df
+[0].
+.TL
+.b .)b
+.DE
+End block.
+.TL
+.b .(z
+.i m
+.i f
+.DE
+Begin floating keep.
+Like
+.b .(b
+except that the keep is
+.i floated
+to the bottom of the page
+or the top of the next page.
+Therefore,
+its position relative to the text changes.
+The floating keep is preceded and followed
+by
+.NR (zs
+[1v]
+space.
+Also,
+it defaults to mode
+.b M .
+.TL
+.b .)z
+.DE
+End floating keep.
+.TL
+.b .(c
+.DE
+Begin centered block.
+The next keep
+is centered as a block,
+rather than on a line-by-line basis
+as with
+.b ".(b C" .
+This call may be nested
+inside keeps.
+.TL
+.b .)c
+.DE
+End centered block.
+.sh 1 Annotations
+.TL
+.b .(d
+.DE
+Begin delayed text.
+Everything in the next keep
+is saved for output
+later with
+.b .pd ,
+in a manner
+similar to footnotes.
+.TL
+.b .)d
+.i n
+.DE
+End delayed text.
+The delayed text number register
+.NR ($d
+and the associated string
+.ST #
+are incremented if
+.ST #
+has been referenced.
+.TL
+.b .pd
+.DE
+Print delayed text.
+Everything diverted via
+.b .(d
+is printed and truncated.
+This might be used
+at the end of each chapter.
+.TL
+.b .(f
+.DE
+Begin footnote.
+The text of the footnote
+is floated to the bottom
+of the page
+and set in font
+.NR (ff
+[1]
+and size
+.NR (fp
+[8p].
+Each entry
+is preceded by
+.NR (fs
+[0.2v]
+space,
+is indented
+.NR (fi
+[3n]
+on the first line,
+and is indented
+.NR (fu
+[0]
+from the right margin.
+Footnotes line up underneath
+two column output.
+If the text of the footnote
+will not all fit on one page
+it will be carried over
+to the next page.
+.TL
+.b .)f
+.i n
+.DE
+End footnote.
+The number register
+.NR ($f
+and the associated string
+.ST *
+are incremented
+if they have been referenced.
+.TL
+.b .$s
+.DE
+The macro to output the footnote separator.
+This macro may be redefined
+to give other size lines or other types
+of separators.
+Currently
+it draws a 1.5i line.
+.TL
+.b .(x
+.i x
+.DE
+Begin index entry.
+Index entries are saved in the index
+.i x
+[\c
+.b x ]
+until called up with
+.b .xp.
+Each entry is preceded
+by a
+.NR (xs
+[0.2v]
+space.
+Each entry is
+.q undented
+by
+.NR (xu
+[0.5i];
+this register tells how far the page number
+extends into the right margin.
+.TL
+.b .)x
+.i P
+.i A
+.DE
+End index entry.
+The index entry
+is finished with a row of dots
+with
+.i A
+[null]
+right justified on the last line
+(such as for an author's name),
+followed by P
+[\c
+.NR % ].
+If
+.i A
+is specified,
+.i P
+must be specified;
+.NR %
+can be used to print the current page number.
+If
+.i P
+is an underscore,
+no page number
+and no row of dots
+are printed.
+.TL
+.b .xp
+.i x
+.DE
+Print index
+.i x
+[\c
+.b x ].
+The index is formatted in the font, size, and so forth
+in effect at the time it is printed,
+rather than at the time it is collected.
+.sh 1 "Columned Output"
+.TL
+.b .2c
+.i +S
+.i N
+.DE
+Enter two-column mode.
+The column separation is set to
+.i +S
+[4n, 0.5i in ACM mode]
+(saved in
+.NR ($s ).
+The column width,
+calculated to fill the single column line length
+with both columns,
+is stored in
+.NR ($l .
+The current column
+is in
+.NR ($c .
+You can test register
+.NR ($m
+[1]
+to see if you are in single column
+or double column mode.
+Actually,
+the request enters
+.i N
+[2]
+column output.
+.TL
+.b .1c
+.DE
+Revert to single-column mode.
+.TL
+.b .bc
+.DE
+Begin column.
+This is like
+.b .bp
+except that it begins a new column
+on a new page
+only if necessary,
+rather than forcing a whole new page
+if there is another column left
+on the current page.
+.sh 1 "Fonts and Sizes"
+.TL
+.b .sz
+.i +P
+.DE
+The pointsize is set to
+.i P
+[10p],
+and the line spacing is set proportionally.
+The line spacing as a percentage of the pointsize expressed in units
+is stored in
+.NR ($r .
+The percentage used internally
+by displays and annotations
+is stored in
+.NR ($R
+(although this is not used by
+.b .sz ).
+This size is
+.i not
+sticky beyond many macros:
+in particular,
+.NR (pp
+(paragraph pointsize)
+modifies the pointsize every time a new paragraph is begun
+using the
+.b \&.pp ,
+.b \&.lp ,
+.b \&.ip ,
+.b \&.np ,
+or
+.b \&.bu
+macros.
+Also,
+.NR (fp
+(footnote pointsize),
+.NR (qp
+(quote pointsize),
+.NR (sp
+(section header pointsize),
+and
+.NR (tp
+(title pointsize)
+may modify the pointsize.
+.TL
+.b .r
+.i W
+.i X
+.DE
+Set
+.i W
+in roman font,
+appending
+.i X
+in the previous font.
+To append different font requests,
+use
+.i X
+=
+.b \ec .
+If no parameters,
+change to roman font.
+.TL
+.b .i
+.i W
+.i X
+.DE
+Set
+.i W
+in italics,
+appending
+.i X
+in the previous font.
+If no parameters,
+change to italic font.
+.TL
+.b .b
+.i W
+.i X
+.DE
+Set
+.i W
+in bold font
+and append
+.i X
+in the previous font.
+If no parameters,
+switch to bold font.
+.TL
+.b .u
+.i W
+.i X
+.DE
+Underline
+.i W
+and append
+.i X .
+This is a true underlining,
+as opposed to the
+.b .ul
+request,
+which changes to
+.q "underline font"
+(usually italics in \*G).
+It won't work right
+if
+.i W
+is spread or broken (including hyphenated).
+In other words,
+it is safe in nofill mode only.
+.TL
+.b .q
+.i W
+.i X
+.DE
+Quote
+.i W
+and append
+.i X .
+In \*G
+this surrounds
+.i W
+with
+.b \*(lq ,
+and
+.b \*(rq .
+.TL
+.b .bi
+.i W
+.i X
+.DE
+Set
+.i W
+in bold italics
+and append
+.i X .
+.TL
+.b .bx
+.i W
+.i X
+.DE
+Sets
+.i W
+in a box,
+with
+.i X
+appended.
+It won't work right
+if
+.i W
+is spread or broken (including hyphenated).
+In other words,
+it is safe in nofill mode only.
+.TL
+.b sm
+.i W
+.i X
+.DE
+Sets
+.i W
+in a smaller pointsize,
+with
+.i X
+appended.
+.sh 1 "Roff Support"
+.TL
+.b .ix
+.i +N
+.DE
+Indent,
+no break.
+Equivalent to
+.b \(aain
+.i N .
+.TL
+.b .bl
+.i N
+.DE
+Leave
+.i N
+contiguous white space,
+on the next page if not enough room
+on this page.
+Equivalent to a
+.b .sp
+.i N
+inside a block.
+.TL
+.b .pa
+.i +N
+.DE
+Equivalent to
+.b .bp .
+.TL
+.b .ro
+.DE
+Set page number
+in roman numerals.
+Equivalent to
+.b ".af % i" .
+.TL
+.b .ar
+.DE
+Set page number in Arabic.
+Equivalent to
+.b ".af % 1" .
+.TL
+.b .n1
+.DE
+Number lines in margin from one
+on each page.
+.TL
+.b .n2
+.i N
+.DE
+Number lines from
+.i N ,
+stop if
+.i N
+= 0.
+.TL
+.b .sk
+.DE
+Leave the next output page blank,
+except for headers and footers.
+This is used to leave space
+for a full-page diagram
+which is produced externally
+and pasted in later.
+To get a partial-page paste-in display,
+say
+.b .sv \ \c
+.i N ,
+where
+.i N
+is the amount of space
+to leave;
+this space will be output immediately
+if there is room,
+and will otherwise be output
+at the top of the next page.
+However, be warned:
+if
+.i N
+is greater than the amount of available space
+on an empty page,
+no space will ever be output.
+.sh 1 "Preprocessor Support"
+.TL
+.b .EQ
+.i m
+.i T
+.DE
+Begin equation.
+The equation is centered
+if
+.i m
+is
+.b C
+or omitted,
+indented
+.NR (bi
+[4m]
+if
+.i m
+is
+.b I ,
+and left justified if
+.i m
+is
+.b L .
+.i T
+is a title printed on the right margin
+next to the equation.
+See
+.i "Typesetting Mathematics \- User's Guide"
+by Brian W. Kernighan
+and Lorinda L. Cherry.
+.TL
+.b .EN
+.i c
+.DE
+End equation.
+If
+.i c
+is
+.b C
+the equation must be continued
+by immediately following
+with another
+.b .EQ ,
+the text of which
+can be centered
+along with this one.
+Otherwise,
+the equation is printed,
+always on one page,
+with
+.NR (es
+[0.5v]
+space
+above and below it.
+.TL
+.b .TS
+.i h
+.DE
+Table start.
+Tables are single spaced
+and kept on one page
+if possible.
+If you have a large table
+which will not fit on one page,
+use
+.i h
+=
+.b H
+and follow the header part
+(to be printed on every page of the table)
+with a
+.b .TH .
+See
+.i "Tbl \- A Program to Format Tables"
+by M. E. Lesk.
+.TL
+.b .TH
+.DE
+With
+.b ".TS H" ,
+ends the header portion of the table.
+.TL
+.b .TE
+.DE
+Table end.
+Note that this table
+does not float,
+in fact,
+it is not even guaranteed to stay on one page
+if you use requests such as
+.b .sp
+intermixed with the text
+of the table.
+If you want it to float
+(or if you use requests
+inside the table),
+surround the entire table
+(including the
+.b .TS
+and
+.b .TE
+requests)
+with the requests
+.b .(z
+and
+.b .)z .
+.TL
+.b .PS
+.i h
+.i w
+.DE
+Begin
+.i pic
+picture.
+.i H
+is the height and
+.i w
+is the width,
+both in basic units.
+.TL
+.b .PE
+.DE
+End picture.
+.TL
+.b .IS
+.DE
+Begin
+.i ideal
+picture.
+.TL
+.b .IE
+.DE
+End
+.i ideal
+picture.
+.TL
+.b .IF
+.DE
+End
+.i ideal
+picture (alternate form).
+.TL
+.b .GS
+.DE
+Begin
+.i gremlin
+picture.
+.TL
+.b .GE
+.DE
+End
+.i gremlin
+picture.
+.TL
+.b .GF
+.DE
+End
+.i gremlin
+picture (alternate form).
+.sh 1 "Miscellaneous"
+.TL
+.b .re
+.DE
+Reset tabs every 0.5i.
+.TL
+.b .ba
+.i +N
+.DE
+Set the base indent
+to
+.i +N
+[0]
+(saved in
+.NR ($i ).
+All paragraphs,
+sections,
+and displays
+come out indented by this amount.
+Titles and footnotes
+are unaffected.
+The
+.b .sh
+request performs a
+.b .ba
+request
+if
+.NR (si
+[0] is not zero,
+and sets the base indent to
+.NR (si \c
+.b * \c
+.NR ($0 .
+.TL
+.b .xl
+.i +N
+.DE
+Set the line length to
+.i N
+[6.0i].
+This differs
+from
+.b .ll
+because it only affects the current environment.
+.TL
+.b .ll
+.i +N
+.DE
+Set line length in all environments
+to
+.i N
+[6.0i].
+This should not be used
+after output has begun,
+and particularly not in two-column output.
+The current line length is stored in
+.NR ($l .
+.TL
+.b .hl
+.DE
+Draws a horizontal line
+the length of the page.
+This is useful
+inside floating keeps
+to differentiate
+between the text
+and the figure.
+.sh 1 "Standard Papers"
+.TL
+.b .tp
+.DE
+Begin title page.
+Spacing at the top of the page
+can occur,
+and headers and footers are suppressed.
+Also,
+the page number
+is not incremented
+for this page.
+.TL
+.b .++
+.i m
+.i H
+.DE
+This request defines the section of the paper
+which we are entering.
+The section type is defined by
+.i m .
+.b C
+means that we are entering the chapter portion
+of the paper,
+.b A
+means that we are entering the appendix portion
+of the paper,
+.b P
+means that the material following
+should be the preliminary portion
+(abstract, table of contents, etc.)
+portion of the paper,
+.b AB
+means that we are entering the abstract
+(numbered independently from 1
+in Arabic numerals),
+and
+.b B
+means that we are entering the bibliographic
+portion at the end of the paper.
+Also, the variants
+.b RC
+and
+.b RA
+are allowed,
+which specify renumbering of pages
+from one at the beginning of each
+chapter or appendix,
+respectively.
+The
+.i H
+parameter defines the new header.
+If there are any spaces in it,
+the entire header must be quoted.
+If you want the header to have the chapter number
+in it,
+Use the string
+.b "\e\e\e\en(ch" .
+For example, to number appendixes
+.b A.1
+etc.,
+type
+.b ".++ RA \(aa\(aa\(aa\e\e\e\en(ch.%\(aa" .
+Each section
+(chapter, appendix, etc.)
+should be preceded by the
+.b .+c
+request.
+It should be mentioned
+that it is easier when using
+\*T to put the front material
+at the end of the paper,
+so that the table of contents
+can be collected and put out;
+this material can then be physically
+moved to the beginning of the paper.
+.TL
+.b .+c
+.i T
+.DE
+Begin chapter with title
+.i T .
+The chapter number
+is maintained in
+.NR (ch .
+This register is incremented
+every time
+.b .+c
+is called with a parameter.
+The title and chapter number
+are printed by
+.b .$c .
+The header is moved to the footer
+on the first page
+of each chapter.
+If
+.i T
+is omitted,
+.b .$c
+is not called;
+this is useful for doing your own
+.q "title page"
+at the beginning of papers
+without a title page proper.
+.b .$c
+calls
+.b .$C
+as a hook so that chapter titles can be inserted
+into a table of contents automatically.
+The footnote numbering is reset to one.
+.TL
+.b .$c
+.i T
+.DE
+Print chapter number
+(from
+.NR (ch )
+and
+.i T .
+This macro can be redefined to your liking.
+It is defined by default
+to be acceptable
+for a PhD thesis
+at Berkeley.
+This macro calls
+.b $C ,
+which can be defined to make index entries,
+or whatever.
+.TL
+.b .$C
+.i K
+.i N
+.i T
+.DE
+This macro is called by
+.b .$c .
+It is normally undefined,
+but can be used to automatically insert
+index entries,
+or whatever.
+.i K
+is a keyword,
+either
+.q Chapter
+or
+.q Appendix
+(depending on the
+.b .++
+mode);
+.i N
+is the chapter or appendix number,
+and
+.i T
+is the chapter or appendix title.
+.sh 1 "Predefined Strings"
+.TL
+.ST *
+.DE
+Footnote number, actually
+.ST [ \c
+.NR ($f \c
+.ST ] .
+This macro is incremented
+after each call to
+.b .)f .
+.TL
+.ST #
+.DE
+Delayed text number.
+Actually
+[\c
+.NR ($d ].
+.TL
+.ST {
+.DE
+Superscript.
+This string gives upward movement
+and a change to a smaller point size.
+Extra space is left above the line
+to allow room for the superscript.
+.TL
+.ST }
+.DE
+Unsuperscript.
+Inverse to
+.ST { .
+For example,
+to produce a superscript
+you might type
+.b x \c
+.ST { \c
+.b 2 \c
+.ST } ,
+which will produce
+.b x\*{2\*} .
+.TL
+.ST <
+.DE
+Subscript.
+Extra space is left below the line
+to allow for the subscript.
+.TL
+.ST >
+.DE
+Inverse to
+.ST < .
+.TL
+.ST (dw
+.DE
+The day of the week,
+as a word.
+.TL
+.ST (mo
+.DE
+The month,
+as a word.
+.TL
+.ST (td
+.DE
+Today's date,
+directly printable.
+The date is of the form \*(td.
+Other forms of the date can be used
+by using
+.NR (dy
+(the day of the month;
+for example, \n(dy),
+.ST (mo
+(as noted above)
+or
+.NR (mo
+(the same,
+but as an ordinal number;
+for example, \*(mo is \n(mo),
+and
+.NR (yr
+(the last two digits of the current year).
+.TL
+.ST (lq
+.DE
+Left quote marks.
+.TL
+.ST (rq
+.DE
+Right quote.
+.TL
+.ST \-
+.DE
+.ie \w'\(34'>0 \(34
+.el 3/4
+em dash.
+.sh 1 "Special Characters and Marks"
+.pp
+There are a number of special characters
+and diacritical marks
+(such as accents)
+available through \-me.
+.ta 15 +5 +6
+.nf
+Name Usage Example
+Acute accent \e*\(aa a\e*\(aa a\*'
+Grave accent \e*\(ga e\e*\(ga e\*`
+Umlaut \e*: u\e*: u\*:
+Tilde \e*~ n\e*~ n\*~
+Caret \e*^ e\e*^ e\*^
+Cedilla \e*, c\e*, c\*,
+Czech \e*v e\e*v e\*v
+Circle \e*o A\e*o A\*o
+There exists \e*(qe \*(qe
+For all \e*(qa \*(qa
+.fi
+.sp 1i
+.b Acknowledgments
+.pp
+I would like to thank
+Bob Epstein,
+Bill Joy,
+and Larry Rowe
+for having the courage
+to use the \-me macros
+to produce non-trivial papers
+during the development stages;
+Ricki Blau,
+Pamela Humphrey,
+and Jim Joyce
+for their help with the documentation phase;
+peter kessler
+for numerous complaints,
+most accompanied by fixes;
+and the plethora of people who have contributed ideas
+and have given support for the project.
+.bp
+.b Summary
+.pp
+This alphabetical list summarizes all macros, strings, and number registers
+available in the \-me macros.
+Selected
+.i troff
+commands, registers, and functions are included as well;
+those listed can generally be used with impunity.
+.pp
+The columns are the name of the
+command, macro, register, or string;
+the type of the object,
+and the description.
+Types are
+.b M
+for macro or builtin command
+(invoked with
+.b \&.
+or
+.b \&\'
+in the first input column),
+.b S
+for a string
+(invoked with
+.b \e*
+or
+.b \e*( ),
+.b R
+for a number register
+(invoked with
+.b \en
+or
+.b \en( ),
+and
+.b F
+for a
+.i troff
+builtin function
+(invoked by preceding it with a single backslash).
+.pp
+Lines marked with \(sc are
+.i troff
+internal codes.
+Lines marked with \(dg or \(dd
+may be defined by the user to get special functions;
+\(dd indicates that these are defined by default
+and changing them may have unexpected side effects.
+Lines marked with \(de
+are specific to
+.i ditroff
+(device-independent
+.i troff ).
+.de $H
+.ev 1
+.ta \w'\e(space)\(sc\ 'u +\w'TYPE 'u
+NAME TYPE DESCRIPTION
+.ev
+..
+.(l
+.$H
+\e(space) F\(sc unpaddable space
+\e" F\(sc comment (to end of line)
+\e*# S optional delayed text tag string
+\e$\fI\&N\fP F\(sc interpolate argument \fI\&N\fP
+\en($0 R section depth
+\&.$0 M\(dg invoked after section title printed
+\en($1 R first section number
+\&.$1 M\(dg invoked before printing depth 1 section
+\en($2 R second section number
+\&.$2 M\(dg invoked before printing depth 2 section
+\en($3 R third section number
+\&.$3 M\(dg invoked before printing depth 3 section
+\en($4 R fourth section number
+\&.$4 M\(dg invoked before printing depth 4 section
+\en($5 R fifth section number
+\&.$5 M\(dg invoked before printing depth 5 section
+\en($6 R sixth section number
+\&.$6 M\(dg invoked before printing depth 6 section
+\&.$C M\(dg called at beginning of chapter
+\&.$H M\(dg text header
+\en($R R\(dd relative vertical spacing in displays
+\en($c R current column number
+\&.$c M\(dd print chapter title
+\en($d R delayed text number
+\en($f R footnote number
+\&.$f M\(dd print footer
+\&.$h M\(dd print header
+\en($i R paragraph base indent
+\en($l R column width
+\en($m R number of columns in effect
+\e*($n S section name
+\en($p R numbered paragraph number
+\&.$p M\(dd print section heading (internal macro)
+\en($r R\(dd relative vertical spacing in text
+\en($s R column indent
+\&.$s M\(dd footnote separator (from text)
+\en% R\(sc current page number
+\e& F\(sc zero width character, useful for hiding controls
+\e(\fI\&xx\fP F\(sc interpolate special character \fI\&xx\fP
+\&.(b M begin block
+\&.(c M begin centered block
+\&.(d M begin delayed text
+\&.(f M begin footnote
+\&.(l M begin list
+\&.(q M begin quote
+\&.(x M begin index entry
+\&.(z M begin floating keep
+\&.)b M end block
+\&.)c M end centered block
+\&.)d M end delayed text
+\&.)f M end footnote
+\&.)l M end list
+\&.)q M end quote
+\&.)x M end index entry
+\&.)z M end floating keep
+\e*\fI\&x\fP F\(sc interpolate string \fI\&x\fP
+\e*(\fI\&xx\fP F\(sc interpolate string \fI\&xx\fP
+\e** S optional footnote tag string
+\&.++ M set paper section type
+\&.+c M begin chapter
+\e*, S cedilla
+\e\- F\(sc minus sign
+\e*\- S 3/4 em dash
+\e0 F\(sc unpaddable digit-width space
+\&.1c M revert to single column output
+\&.2c M begin two column output
+\e*: S umlat
+\e*< S begin subscript
+\e*> S end subscript
+\&.EN M end equation
+\&.EQ M begin equation
+\eL\'\fI\&d\fP\' F\(sc vertical line drawing function for distance \fI\&d\fP
+\&.GE M\(de end \fIgremlin\fP picture
+\&.GF M\(de end \fIgremlin\fP picture (with flyback)
+\&.GS M\(de start \fIgremlin\fP picture
+\&.IE M\(de end \fIideal\fP picture
+\&.IF M\(de end \fIideal\fP picture (with flyback)
+\&.IS M\(de start \fIideal\fP picture
+\&.PE M\(de end \fIpic\fP picture
+\&.PF M\(de end \fIpic\fP picture (with flyback)
+\&.PS M\(de start \fIpic\fP picture
+\&.TE M end table
+\&.TH M end header of table
+\&.TS M begin table
+\e*{ S begin superscript
+\en(\&.$ R\(sc number of arguments to macro
+\en(\&.i R\(sc current indent
+\en(\&.l R\(sc current line length
+\en(\&.s R\(sc current point size
+\e*(\&\' S acute accent
+\e*(\&\` S grave accent
+\e(\' F\(sc acute accent
+\e(\` F\(sc grave accent
+\e*} S end superscript
+\e^ F\(sc 1/12 em narrow space
+\e*^ S caret
+\&.ad M\(sc set text adjustment
+\&.af M\(sc assign format to register
+\&.am M\(sc append to macro
+\&.ar M set page numbers in Arabic
+\&.as M\(sc append to string
+\&.b M bold font
+\&.ba M set base indent
+\&.bc M begin new column
+\&.bi M bold italic
+\en(bi R display (block) indent
+\&.bl M blank lines (even at top of page)
+\en(bm R bottom title margin
+\&.bp M\(sc begin page
+\&.br M\(sc break (start new line)
+\en(bs R display (block) pre/post spacing
+\en(bt R block keep threshold
+\&.bx M boxed
+\ec F\(sc continue input
+\&.ce M\(sc center lines
+\en(ch R current chapter number
+\&.de M\(sc define macro
+\en(df R display font
+\&.ds M\(sc define string
+\en(dw R\(sc current day of week
+\e*(dw S current day of week
+\en(dy R\(sc day of month
+\ee F\(sc printable version of \e
+\&.ef M set footer (even numbered pages only)
+\&.eh M set header (even numbered pages only)
+\&.el M\(sc else part of conditional
+\&.ep M end page
+\en(es R equation pre/post space
+\ef\fI\&f\fP F\(sc inline font change to font \fI\&f\fP
+\ef(\fI\&ff\fP F\(sc inline font change to font \fI\&ff\fP
+\&.fc M\(sc set field characters
+\en(ff R footnote font
+\&.fi M\(sc fill output lines
+\en(fi R footnote indent (first line only)
+\en(fm R footer margin
+\&.fo M set footer
+\en(fp R footnote pointsize
+\en(fs R footnote prespace
+\en(fu R footnote undent (from right margin)
+\eh\'\fI\&d\fP\' F\(sc local horizontal motion for distance \fI\&d\fP
+\&.hc M\(sc set hyphenation character
+\&.he M set header
+\&.hl M draw horizontal line
+\en(hm R header margin
+\&.hx M suppress headers and footers on next page
+\&.hy M\(sc set hyphenation mode
+\&.i M italic font
+\&.ie M\(sc conditional with else
+\&.if M\(sc conditional
+\en(ii R indented paragraph indent
+\&.in M\(sc indent (transient, use .ba for pervasive)
+\&.ip M begin indented paragraph
+\&.ix M indent, no break
+\el\'\fI\&d\fP\' F\(sc horizontal line drawing function for distance \fI\&d\fP
+\&.lc M\(sc set leader repetition character
+\&.ll M set line length
+\&.lp M begin left justified paragraph
+\e*(lq S left quote marks
+\&.ls M\(sc set multi-line spacing
+\&.m1 M set space from top of page to header
+\&.m2 M set space from header to text
+\&.m3 M set space from text to footer
+\&.m4 M set space from footer to bottom of page
+\&.mc M\(sc insert margin character
+\&.mk M\(sc mark vertical position
+\en(mo R\(sc month of year
+\e*(mo S current month
+\en\fI\&x\fP F\(sc interpolate number register \fI\&x\fP
+\en(\fI\&xx\fP F\(sc interpolate number register \fI\&xx\fP
+\&.n1 M number lines in margin
+\&.n2 M number lines in margin
+\&.na M\(sc turn off text adjustment
+\&.ne M\(sc need vertical space
+\&.nf M\(sc don't fill output lines
+\&.nh M\(sc turn off hyphenation
+\&.np M begin numbered paragraph
+\&.nr M\(sc set number register
+\&.ns M\(sc no space mode
+\e*o S circle (e.g., for Norse A\*o)
+\&.of M set footer (odd numbered pages only)
+\&.oh M set header (odd numbered pages only)
+\&.pa M begin page
+\&.pd M print delayed text
+\en(pf R paragraph font
+\en(pi R paragraph indent
+\&.pl M\(sc set page length
+\&.pn M\(sc set next page number
+\&.po M\(sc page offset
+\en(po R simulated page offset
+\&.pp M begin paragraph
+\en(pp R paragraph pointsize
+\en(ps R paragraph prespace
+\&.q M quoted
+\e*(qa S for all
+\e*(qe S there exists
+\en(qi R quote indent (also shortens line)
+\en(qp R quote pointsize
+\en(qs R quote pre/post space
+\&.r M roman font
+\&.rb M real bold font
+\&.re M reset tabs
+\&.rm M\(sc remove macro or string
+\&.rn M\(sc rename macro or string
+\&.ro M set page numbers in roman
+\e*(rq S right quote marks
+\&.rr M\(sc remove register
+\&.rs M\(sc restore spacing
+\&.rt M\(sc return to vertical position
+\es\fI\&S\fP F\(sc inline size change to size \fI\&S\fP
+\en(sf R section title font
+\&.sh M begin numbered section
+\en(si R relative base indent per section depth
+\&.sk M skip next page
+\&.sm M set argument in a smaller pointsize
+\&.so M\(sc source input file
+\en(so R additional section title offset
+\&.sp M\(sc vertical space
+\en(sp R section title pointsize
+\en(ss R section prespace
+\&.sx M change section depth
+\&.sz M set pointsize and vertical spacing
+\&.ta M\(sc set tab stops
+\&.tc M\(sc set tab repetition character
+\e*(td S today's date
+\en(tf R title font
+\&.ti M\(sc temporary indent (next line only)
+\&.tl M\(sc three part title
+\en(tm R top title margin
+\&.tp M begin title page
+\en(tp R title pointsize
+\&.tr M\(sc translate
+\&.u M underlined
+\&.uh M unnumbered section
+\&.ul M\(sc underline next line
+\ev\'\fI\&d\fP\' F\(sc local vertical motion for distance \fI\&d\fP
+\e*v S inverted `v' for czeck ``e\*v''
+\ew\'\fI\&S\fP\' F\(sc return width of string \fI\&S\fP
+\&.xl M set line length (local)
+\&.xp M print index
+\en(xs R index entry prespace
+\en(xu R index undent (from right margin)
+\en(yr R\(sc year (last two digits only)
+\en(zs R floating keep pre/post space
+\e{ F\(sc begin conditional group
+\e| F\(sc 1/6 em narrow space
+\e} F\(sc end conditional group
+\e*~ S tilde
+.)l
+.rm $H
diff --git a/driver/Makefile b/driver/Makefile
new file mode 100644
index 000000000..62911bbbe
--- /dev/null
+++ b/driver/Makefile
@@ -0,0 +1,54 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CC=g++
+AR=ar
+RANLIB=/bin/ranlib
+INCLUDES=-I../lib
+DEFINES=
+MALLOC=malloc.o
+MALLOCFLAGS=
+OBJECTS=input.o printer.o
+SOURCES=input.c printer.c printer.h driver.h
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: libdriver.a
+
+libdriver.a: $(OBJECTS)
+ $(AR) r libdriver.a $?
+ if test "$(RANLIB)" ; then $(RANLIB) libdriver.a ;fi
+
+$(OBJECTS): printer.h driver.h
+
+TAGS : $(SOURCES)
+ etags $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o core libdriver.a
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+install.bin:
+install.nobin:
+install:
diff --git a/driver/driver.h b/driver/driver.h
new file mode 100644
index 000000000..c375ca9a5
--- /dev/null
+++ b/driver/driver.h
@@ -0,0 +1,35 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <osfcn.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/driver/input.c b/driver/input.c
new file mode 100644
index 000000000..746fe4f16
--- /dev/null
+++ b/driver/input.c
@@ -0,0 +1,459 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "driver.h"
+
+const char *current_filename;
+int current_lineno;
+FILE *current_file;
+
+int get_char();
+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)
+{
+}
+
+void do_file(const char *filename)
+{
+ int npages = 0;
+ if (filename[0] == '-' && filename[1] == '\0') {
+ current_filename = "<standard input>";
+ current_file = stdin;
+ }
+ else {
+ current_file = fopen(filename, "r");
+ if (current_file == 0) {
+ error("can't open `%1'", filename);
+ return;
+ }
+ current_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) {
+ font::set_device_name(dev);
+ if (!font::load_desc())
+ fatal("sorry, I can't continue");
+ }
+ else {
+ const char *d = font::get_device_name();
+ if (d == 0 || strcmp(d, 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 '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 (!isascii(c) || !isdigit(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 ' ':
+ case '\n':
+ break;
+ case 'p':
+ if (npages)
+ pr->end_page();
+ 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 && c != ' ' && c != '\n') {
+ int w;
+ pr->set_ascii_char(c, &env, &w);
+ env.hpos += w + kern;
+ c = get_char();
+ }
+ }
+ break;
+ case 't':
+ {
+ if (npages == 0)
+ fatal("`t' command illegal before first `p' command");
+ int c;
+ while ((c = get_char()) != EOF && c != ' ' && c != '\n') {
+ int w;
+ pr->set_ascii_char(c, &env, &w);
+ env.hpos += w;
+ }
+ }
+ break;
+ case '#':
+ skip_line();
+ 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;
+ for (int 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;
+ delete oldp;
+ }
+ }
+ p[np] = n;
+ }
+ pr->draw(c, p, np, &env);
+ if (c == 'e') {
+ if (np > 0)
+ env.hpos += p[0];
+ }
+ else {
+ for (int 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];
+ }
+ 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;
+ 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();
+}
+
+int get_char()
+{
+ int c = getc(current_file);
+ if (c == '\n')
+ current_lineno++;
+ return c;
+}
+
+int get_integer()
+{
+ int c = getc(current_file);
+ while (c == ' ')
+ c = getc(current_file);
+ int neg = 0;
+ if (c == '-') {
+ neg = 1;
+ c = getc(current_file);
+ }
+ if (!isascii(c) || !isdigit(c))
+ fatal("integer expected");
+ int total = 0;
+ do {
+ total = total*10;
+ if (neg)
+ total -= c - '0';
+ else
+ total += c - '0';
+ c = getc(current_file);
+ } while (isascii(c) && isdigit(c));
+ if (c != EOF)
+ ungetc(c, current_file);
+ return total;
+}
+
+int possibly_get_integer(int *res)
+{
+ int c = getc(current_file);
+ while (c == ' ')
+ c = getc(current_file);
+ int neg = 0;
+ if (c == '-') {
+ neg = 1;
+ c = getc(current_file);
+ }
+ if (!isascii(c) || !isdigit(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 = getc(current_file);
+ } while (isascii(c) && isdigit(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 = getc(current_file);
+ while (c == ' ')
+ c = getc(current_file);
+ 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);
+ delete old_buf;
+ }
+ }
+ if ((!is_long && (c == ' ' || c == '\n')) || c == EOF) {
+ buf[i] = '\0';
+ break;
+ }
+ if (is_long && c == '\n') {
+ current_lineno++;
+ c = getc(current_file);
+ if (c == '+')
+ c = '\n';
+ else {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ buf[i] = c;
+ c = getc(current_file);
+ }
+ if (c != EOF)
+ ungetc(c, current_file);
+ return buf;
+}
+
+void skip_line()
+{
+ int c;
+ while ((c = get_char()) != EOF && c != '\n')
+ ;
+}
+
diff --git a/driver/printer.c b/driver/printer.c
new file mode 100644
index 000000000..e9c5159da
--- /dev/null
+++ b/driver/printer.c
@@ -0,0 +1,169 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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_table(0), nfonts(0), font_list(0)
+{
+}
+
+printer::~printer()
+{
+ 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];
+ for (int i = 0; i < old_nfonts; i++)
+ font_table[i] = old_font_table[i];
+ for (i = old_nfonts; i < nfonts; i++)
+ font_table[i] = 0;
+ }
+ }
+ 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 *)
+{
+}
+
+void printer::draw(int, int *, int, const environment *)
+{
+}
+
+void printer::set_ascii_char(unsigned char c, const environment *env,
+ int *widthp)
+{
+ char buf[2];
+ buf[0] = c;
+ buf[1] = '\0';
+ set_special_char(buf, env, widthp);
+}
+
+void printer::set_special_char(const char *nm, const environment *env,
+ int *widthp)
+{
+ int i = font::name_to_index(nm);
+ 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)) {
+ 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;
+ }
+ int w = f->get_width(i, env->size);
+ if (widthp)
+ *widthp = w;
+ set_char(i, f, env, w);
+}
+
+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);
+}
diff --git a/driver/printer.h b/driver/printer.h
new file mode 100644
index 000000000..3654062c1
--- /dev/null
+++ b/driver/printer.h
@@ -0,0 +1,65 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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);
+ virtual void draw(int code, int *p, int np, const environment *env);
+ virtual void begin_page(int) = 0;
+ virtual void end_page() = 0;
+ virtual font *make_font(const char *nm);
+ virtual void end_of_line();
+ virtual void special(char *arg, const environment *env);
+protected:
+ font_pointer_list *font_list;
+private:
+ font **font_table;
+ int nfonts;
+ font *find_font(const char *);
+ virtual void set_char(int index, font *f, const environment *env,
+ int w) = 0;
+};
+
+printer *make_printer();
diff --git a/dvi/Makefile b/dvi/Makefile
new file mode 100644
index 000000000..5b32f104f
--- /dev/null
+++ b/dvi/Makefile
@@ -0,0 +1,81 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+CC=g++
+CFLAGS=-g -O -Wall -Wcast-qual -Wwrite-strings
+LDFLAGS=-g
+OLDCC=gcc
+MLIBS=-lm
+INCLUDES=-I../driver -I../lib
+DEFINES=
+SOURCES=dvi.c
+MISC=Makefile devgps
+BINDIR=/usr/local/bin
+FONTDIR=/usr/local/lib/groff/font
+MACRODIR=/usr/local/lib/groff/tmac
+ETAGS=etags
+ETAGSFLAGS=-p
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: grodvi tfmtodit devdvi
+
+grodvi: dvi.o ../driver/libdriver.a ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ dvi.o \
+ ../driver/libdriver.a ../lib/libgroff.a $(MLIBS)
+
+tfmtodit: tfmtodit.o ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ tfmtodit.o ../lib/libgroff.a $(MLIBS)
+
+dvi.o: ../driver/printer.h ../driver/driver.h ../lib/font.h
+
+install.bin: grodvi tfmtodit
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/grodvi
+ cp grodvi $(BINDIR)
+ -rm -f $(BINDIR)/tfmtodit
+ cp tfmtodit $(BINDIR)
+ @echo Making install.bin in devdvi
+ @cd devdvi; $(MAKE) "FONTDIR=$(FONTDIR)" install.bin
+
+install.nobin:
+ -[ -d $(MACRODIR) ] || mkdir $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.dvi
+ cp tmac.dvi $(MACRODIR)
+ @echo Making install.nobin in devdvi
+ @cd devdvi; $(MAKE) "FONTDIR=$(FONTDIR)" install.nobin
+
+TAGS: dvi.c
+ $(ETAGS) $(ETAGSFLAGS) dvi.c
+
+clean:
+ -rm -f *.o core grodvi tfmtodit
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+devdvi: FORCE
+ @echo Making all in $@
+ @cd $@; $(MAKE) all
+
+FORCE:
diff --git a/dvi/devdvi/B b/dvi/devdvi/B
new file mode 100644
index 000000000..240731a75
--- /dev/null
+++ b/dvi/devdvi/B
@@ -0,0 +1,347 @@
+name B
+internalname cmbx10
+spacewidth 401952
+ligatures ff fi fl ffi ffl 0
+checksum 452076118
+designsize 10485760
+kernpairs
+ff ' 114323
+ff ? 114323
+ff ! 114323
+ff ) 114323
+ff rB 114323
+ff ] 114323
+' ' -100488
+' ? 133984
+' ! 133984
+A t -33496
+A C -33496
+A O -33496
+A G -33496
+A U -33496
+A Q -33496
+A T -100488
+A Y -100488
+A V -133984
+A W -133984
+D X -33496
+D W -33496
+D A -33496
+D V -33496
+D Y -33496
+F o -100488
+F e -100488
+F u -100488
+F r -100488
+F a -100488
+F A -133984
+F O -33496
+F C -33496
+F G -33496
+F Q -33496
+I I 33496
+K O -33496
+K C -33496
+K G -33496
+K Q -33496
+L T -100488
+L Y -100488
+L V -133984
+L W -133984
+O X -33496
+O W -33496
+O A -33496
+O V -33496
+O Y -33496
+P A -100488
+P o -33496
+P e -33496
+P a -33496
+P . -100488
+P , -100488
+R t -33496
+R C -33496
+R O -33496
+R G -33496
+R U -33496
+R Q -33496
+R T -100488
+R Y -100488
+R V -133984
+R W -133984
+T y -33496
+T e -100488
+T o -100488
+T r -100488
+T a -100488
+T A -100488
+T u -100488
+V o -100488
+V e -100488
+V u -100488
+V r -100488
+V a -100488
+V A -133984
+V O -33496
+V C -33496
+V G -33496
+V Q -33496
+W o -100488
+W e -100488
+W u -100488
+W r -100488
+W a -100488
+W A -133984
+W O -33496
+W C -33496
+W G -33496
+W Q -33496
+X O -33496
+X C -33496
+X G -33496
+X Q -33496
+Y e -100488
+Y o -100488
+Y r -100488
+Y a -100488
+Y A -100488
+Y u -100488
+oq oq -100488
+oq ` -100488
+` oq -100488
+` ` -100488
+a v -33496
+a j 66992
+a y -33496
+a w -33496
+b e 33496
+b o 33496
+b x -33496
+b d 33496
+b c 33496
+b q 33496
+b v -33496
+b j 66992
+b y -33496
+b w -33496
+c h -33496
+c k -33496
+f ' 114323
+f ? 114323
+f ! 114323
+f ) 114323
+f rB 114323
+f ] 114323
+g j 33496
+h t -33496
+h u -33496
+h b -33496
+h y -33496
+h v -33496
+h w -33496
+k a -66992
+k e -33496
+k a -33496
+k o -33496
+k c -33496
+m t -33496
+m u -33496
+m b -33496
+m y -33496
+m v -33496
+m w -33496
+n t -33496
+n u -33496
+n b -33496
+n y -33496
+n v -33496
+n w -33496
+o e 33496
+o o 33496
+o x -33496
+o d 33496
+o c 33496
+o q 33496
+o v -33496
+o j 66992
+o y -33496
+o w -33496
+p e 33496
+p o 33496
+p x -33496
+p d 33496
+p c 33496
+p q 33496
+p v -33496
+p j 66992
+p y -33496
+p w -33496
+t y -33496
+t w -33496
+u w -33496
+v a -66992
+v e -33496
+v a -33496
+v o -33496
+v c -33496
+w e -33496
+w a -33496
+w o -33496
+w c -33496
+y o -33496
+y e -33496
+y a -33496
+y . -100488
+y , -100488
+charset
+*G 725261,719440,0,0,0,-167480 2 0000
+*D 1004880,719440 2 0001
+*H 937888,719440,0,0,-33496,-33496 2 0002
+*L 844682,719440 2 0003
+*C 803904,719440 2 0004
+*P 943714,719440 2 0005
+*S 870896,719440 2 0006
+*U 937888,719440,0,0,-33496,-167480 2 0007
+*F 870896,719440,0,0,-33496 2 0010
+*Q 937888,719440,0,0,-33496,-83741 2 0011
+*W 870896,719440 2 0012
+ff 703416,728178,0,114323 2 0013
+fi 669920,728178 2 0014
+fl 669920,728178 2 0015
+Fi 1004880,728178 2 0016
+Fl 1004880,728178 2 0017
+.i 334960,466034 0 0020
+.j 368456,466034,203890 1 0021
+ga 602928,728178 2 0022
+char180 602928,728178 2 0023
+aa "
+ah 602928,662642 2 0024
+ab 602928,728178 2 0025
+char175 602928,625066 2 0026
+a- "
+ao 911674,728178 2 0027
+char184 535936,0,178403 1 0030
+ac "
+char223 626230,728178 2 0031
+ss "
+char230 870896,466034 0 0032
+ae "
+oe 937888,466034 0 0033
+char248 602928,567979,101946 3 0034
+/o "
+char198 1092261,719440 2 0035
+AE "
+OE 1226245,719440 2 0036
+char216 937888,770413,50973 3 0037
+/O "
+--- 334960,466034 0 0040
+! 367000,728178 2 0041
+rq 632056,728178 2 0042
+sh 1004880,728178,203888 3 0043
+# "
+Do 602928,786432,58254 3 0044
+$ "
+% 1004880,786432,58254 3 0045
+& 937888,728178 2 0046
+' 334960,728178 2 0047
+( 468944,786432,262144 3 0050
+) 468944,786432,262144 3 0051
+* 602928,786432 2 0052
++ 937888,664096,139808 3 0053
+, 334960,163112,203890 1 0054
+char173 401952,466034 0 0055
+hy "
+- "
+. 334960,163112 0 0056
+sl 602928,786432,262144 3 0057
+/ "
+0 602928,675749 2 0060
+1 602928,675749 2 0061
+2 602928,675749 2 0062
+3 602928,675749 2 0063
+4 602928,675749 2 0064
+5 602928,675749 2 0065
+6 602928,675749 2 0066
+7 602928,675749 2 0067
+8 602928,675749 2 0070
+9 602928,675749 2 0071
+: 334960,466034 0 0072
+; 334960,466034,203890 1 0073
+char161 367000,524288,203890 3 0074
+r! "
+= 937888,410110,-114178 0 0075
+char191 569432,524288,203890 3 0076
+r? "
+? 569432,728178 2 0077
+at 937888,728178 2 0100
+@ "
+A 911674,719440 2 0101
+B 857789,719440 2 0102
+C 870896,719440,0,0,-33496 2 0103
+D 924781,719440,0,0,0,-33496 2 0104
+E 792253,719440 2 0105
+F 758757,719440,0,0,0,-167480 2 0106
+G 948083,719440,0,0,-33496 2 0107
+H 943714,719440 2 0110
+I 457294,719440 2 0111
+J 623317,719440,0,0,0,-33496 2 0112
+K 945170,719440 2 0113
+L 725261,719440,0,0,0,66992 2 0114
+M 1144690,719440 2 0115
+N 943714,719440 2 0116
+O 905848,719440,0,0,-33496,-33496 2 0117
+P 824293,719440,0,0,0,-167480 2 0120
+Q 905848,719440,203890,0,-33496 3 0121
+R 904392,719440 2 0122
+S 669920,719440 2 0123
+T 838856,719440,0,0,0,-167480 2 0124
+U 927694,719440,0,0,-20389 2 0125
+V 911674,719440,0,16749,50245,-251219 2 0126
+W 1246634,719440,0,16749,50245,-150731 2 0127
+X 911674,719440 2 0130
+Y 911674,719440,0,30146,33496,-237822 2 0131
+Z 736912,719440 2 0132
+lB 334960,786432,262144 3 0133
+[ "
+lq 632056,728178 2 0134
+rB 334960,786432,262144 3 0135
+] "
+ha 602928,728178 2 0136
+^ "
+a^ "
+a. 334960,728178 2 0137
+oq 334960,728178 2 0140
+` "
+a 586179,466034 0 0141
+b 669920,728178 2 0142
+c 535936,466034 0 0143
+d 669920,728178 2 0144
+e 552685,466034 0 0145
+f 368456,728178,0,114323 2 0146
+g 602928,466034,203890,16749 1 0147
+h 669920,728178 2 0150
+i 334960,728178 2 0151
+j 368456,728178,203890 3 0152
+k 636424,728178 2 0153
+l 334960,728178 2 0154
+m 1004880,466034 0 0155
+n 669920,466034 0 0156
+o 602928,466034 0 0157
+p 669920,466034,203890 1 0160
+q 636424,466034,203890 1 0161
+r 496616,466034 0 0162
+s 475643,466034 0 0163
+t 468944,665763 2 0164
+u 669920,466034 0 0165
+v 636424,466034,0,16749 0 0166
+w 870896,466034,0,16749 0 0167
+x 636424,466034 0 0170
+y 636424,466034,203890,16749 1 0171
+z 535936,466034 0 0172
+en 602928,466034,0,33496 0 0173
+em 1205856,466034,0,33496 0 0174
+a" 602928,728178 2 0175
+~ 602928,728178 2 0176
+a~ "
+char168 602928,728178 2 0177
+ad "
diff --git a/dvi/devdvi/BI b/dvi/devdvi/BI
new file mode 100644
index 000000000..36da04044
--- /dev/null
+++ b/dvi/devdvi/BI
@@ -0,0 +1,345 @@
+name BI
+internalname cmbxti10
+spacewidth 434573
+slant 14.036243
+ligatures ff fi fl ffi ffl 0
+checksum 1175274390
+designsize 10485760
+kernpairs
+ff ' 111848
+ff ? 111848
+ff ! 111848
+ff ) 111848
+ff rB 111848
+ff ] 111848
+' ' -92624
+' ? 123498
+' ! 123498
+A n -30875
+A l -30875
+A r -30875
+A u -30875
+A m -30875
+A t -30875
+A i -30875
+A C -30875
+A O -30875
+A G -30875
+A h -30875
+A b -30875
+A U -30875
+A k -30875
+A v -30875
+A w -30875
+A Q -30875
+A T -92624
+A Y -92624
+A V -123498
+A W -123498
+A e -61749
+A a -61749
+A o -61749
+A d -61749
+A c -61749
+A g -61749
+A q -61749
+D X -30875
+D W -30875
+D A -30875
+D V -30875
+D Y -30875
+F o -92624
+F e -92624
+F u -92624
+F r -92624
+F a -92624
+F A -123498
+F O -30875
+F C -30875
+F G -30875
+F Q -30875
+K O -30875
+K C -30875
+K G -30875
+K Q -30875
+L T -92624
+L Y -92624
+L V -123498
+L W -123498
+L e -61749
+L a -61749
+L o -61749
+L d -61749
+L c -61749
+L g -61749
+L q -61749
+O X -30875
+O W -30875
+O A -30875
+O V -30875
+O Y -30875
+P A -92624
+R n -30875
+R l -30875
+R r -30875
+R u -30875
+R m -30875
+R t -30875
+R i -30875
+R C -30875
+R O -30875
+R G -30875
+R h -30875
+R b -30875
+R U -30875
+R k -30875
+R v -30875
+R w -30875
+R Q -30875
+R T -92624
+R Y -92624
+R V -123498
+R W -123498
+R e -61749
+R a -61749
+R o -61749
+R d -61749
+R c -61749
+R g -61749
+R q -61749
+T y -92624
+T e -92624
+T o -92624
+T r -92624
+T a -92624
+T u -92624
+T A -92624
+V o -92624
+V e -92624
+V u -92624
+V r -92624
+V a -92624
+V A -123498
+V O -30875
+V C -30875
+V G -30875
+V Q -30875
+W A -92624
+X O -30875
+X C -30875
+X G -30875
+X Q -30875
+Y e -92624
+Y o -92624
+Y r -92624
+Y a -92624
+Y u -92624
+Y A -92624
+oq oq -92624
+oq ` -92624
+` oq -92624
+` ` -92624
+b e -61749
+b a -61749
+b o -61749
+b d -61749
+b c -61749
+b g -61749
+b q -61749
+c e -61749
+c a -61749
+c o -61749
+c d -61749
+c c -61749
+c g -61749
+c q -61749
+d l 61749
+e e -61749
+e a -61749
+e o -61749
+e d -61749
+e c -61749
+e g -61749
+e q -61749
+f ' 111848
+f ? 111848
+f ! 111848
+f ) 111848
+f rB 111848
+f ] 111848
+l l 61749
+n ' -123498
+o e -61749
+o a -61749
+o o -61749
+o d -61749
+o c -61749
+o g -61749
+o q -61749
+p e -61749
+p a -61749
+p o -61749
+p d -61749
+p c -61749
+p g -61749
+p q -61749
+r e -61749
+r a -61749
+r o -61749
+r d -61749
+r c -61749
+r g -61749
+r q -61749
+w l 61749
+charset
+*G 731666,719440,0,135298,0,-19075 2 0000
+*D 990312,719440 2 0001
+*H 928563,719440,0,95027,-84834,64152 2 0002
+*L 845843,719440 2 0003
+*C 805066,719440,0,158248,0,79125 2 0004
+*P 939632,719440,0,180443,0,90222 2 0005
+*S 866814,719440,0,119859,0,59930 2 0006
+*U 928563,719440,0,113013,-174763,-41360 2 0007
+*F 866814,719440,0,59054,-120805,59054 2 0010
+*Q 928563,719440,0,113013,-174763,-5243 2 0011
+*W 866814,719440,0,104021,0,52011 2 0012
+ff 792256,728178,203890,228357 3 0013
+fi 707205,728178,203890,113890 3 0014
+fl 738078,728178,203890,113890 3 0015
+Fi 1095466,728178,203890,113890 3 0016
+Fl 1110902,728178,203890,113890 3 0017
+.i 372824,466034,0,98840 0 0020
+.j 403699,466034,203890,48354 1 0021
+ga 619819,728178 2 0022
+char180 619819,728178,0,89421 2 0023
+aa "
+ah 619819,662642,0,86728 2 0024
+ab 619819,728178,0,108354 2 0025
+char175 619819,623318,0,109518 2 0026
+a- "
+ao 994973,728178 2 0027
+char184 558070,0,178403 1 0030
+ac "
+char223 697302,728178,203890,102090 3 0031
+ss "
+char230 866814,466034,0,89131 0 0032
+ae "
+oe 866814,466034,0,89131 0 0033
+char248 619819,567979,101946,99179 3 0034
+/o "
+char198 1072450,719440,0,119859 2 0035
+AE "
+OE 1195947,719440,0,119859 2 0036
+char216 928563,770413,50973,95027 3 0037
+/O "
+--- 311075,466034 0 0040
+! 404864,728178,0,119714 2 0041
+rq 650696,728178,0,83248 2 0042
+sh 990312,728178,203888,71653 3 0043
+# "
+char163 910723,728178 2 0044
+Po "
+% 990312,786432,58254,134859 3 0045
+& 928563,728178,0,89421 2 0046
+' 372824,728178,0,135734 2 0047
+( 496322,786432,262144,165733 3 0050
+) 496322,786432,262144,34661 3 0051
+* 619819,786432,0,150296 2 0052
++ 928563,632637,108349,34661 3 0053
+, 372824,154374,203890 1 0054
+char173 434573,466034,0,27379 0 0055
+hy "
+- "
+. 372824,154374 0 0056
+sl 619819,786432,262144,165733 3 0057
+/ "
+0 619819,675749,0,138062 2 0060
+1 619819,675749,0,138062 2 0061
+2 619819,675749,0,138062 2 0062
+3 619819,675749,0,138062 2 0063
+4 619819,675749,203890,138062 3 0064
+5 619819,675749,0,138062 2 0065
+6 619819,675749,0,138062 2 0066
+7 619819,675749,203890,138062 3 0067
+8 619819,675749,0,138062 2 0070
+9 619819,675749,0,138062 2 0071
+: 372824,466034,0,70198 0 0072
+; 372824,466034,203890,70198 1 0073
+char161 404864,524288,203890,68741 3 0074
+r! "
+= 928563,410110,-114178,71653 0 0075
+char191 619819,524288,203890 3 0076
+r? "
+? 619819,728178,0,120296 2 0077
+at 928563,728178,0,96555 2 0100
+@ "
+A 907592,719440 2 0101
+B 856330,719440,0,104021,0,52011 2 0102
+C 866814,719440,0,148986,-84834,74493 2 0103
+D 918078,719440,0,95027,0,64152 2 0104
+E 793414,719440,0,119859,0,59930 2 0105
+F 762541,719440,0,135298,0,-19075 2 0106
+G 938758,719440,0,77042,-84834,77042 2 0107
+H 939632,719440,0,180443,0,90222 2 0110
+I 494576,719440,0,164424,0,82213 2 0111
+J 640208,719440,0,152045,0,45147 2 0112
+K 938467,719440,0,148986,0,74493 2 0113
+L 731666,719440,0,0,0,61749 2 0114
+M 1124878,719440,0,180443,0,59347 2 0115
+N 939632,719440,0,180443,0,59347 2 0116
+O 896523,719440,0,95027,-84834,64152 2 0117
+P 825454,719440,0,104021,0,-50352 2 0120
+Q 896523,719440,203890,95027,-84834,95027 3 0121
+R 901186,719440,0,26835,0,20126 2 0122
+S 681568,719440,0,118112,0,59056 2 0123
+T 834774,719440,0,135298,-134896,-19075 2 0124
+U 923613,719440,0,180443,-136096,59347 2 0125
+V 907592,719440,0,195298,-133549,-51698 2 0126
+W 1216336,719440,0,195298,-133549,40925 2 0127
+X 907592,719440,0,164424,0,82213 2 0130
+Y 907592,719440,0,207648,-148986,-39347 2 0131
+Z 743317,719440,0,148986,0,74493 2 0132
+lB 373408,786432,262144,196608 3 0133
+[ "
+lq 650696,728178,0,175869 2 0134
+rB 373408,786432,262144,104568 3 0135
+] "
+ha 619819,728178,0,70344 2 0136
+^ "
+a^ "
+a. 372824,728178,0,135734 2 0137
+oq 372824,728178,0,135734 2 0140
+` "
+a 619819,466034,0,98840,-34078,98840 0 0141
+b 558070,728178,0,82430,-34078,82430 2 0142
+c 558070,466034,0,54760,-34078,54760 0 0143
+d 619819,728178,0,113890,-34078,113890 2 0144
+e 558070,466034,0,89131,-34078,89131 0 0145
+f 419432,728178,203890,228357,112722,116509 3 0146
+g 558070,466034,203890,110102,-35829,71266 1 0147
+h 619819,728178,0,98840,0,98840 2 0150
+i 372824,726931,0,119403,-17669,119403 2 0151
+j 372824,726931,203890,175326,50973,110102 3 0152
+k 558070,728178,0,116509,0,98840 2 0153
+l 311075,728178,0,113890,-30875,129714 2 0154
+m 990312,466034,0,98840,-17669,98840 0 0155
+n 681568,466034,0,98840,-17669,98840 0 0156
+o 619819,466034,0,82430,-34078,82430 0 0157
+p 619819,466034,203890,82430,-17182,82430 1 0160
+q 558070,466034,203890,110102,-34078,71266 1 0161
+r 526034,466034,0,116509,-17669,85634 0 0162
+s 510595,466034,0,85634,0,85634 0 0163
+t 403699,665763,0,101072,-39323,101072 2 0164
+u 650694,466034,0,98840,-17669,98840 0 0165
+v 558070,466034,0,116509,-17669,77672 0 0166
+w 805066,466034,0,116509,-17669,87382 0 0167
+x 587782,466034,0,131946,0,131946 0 0170
+y 588946,466034,203890,110102,-17669,71266 1 0171
+z 514382,466034,0,145637,0,98840 0 0172
+en 619819,466034,0,102880 0 0173
+em 1239638,466034,0,102880 0 0174
+a" 619819,728178,0,120296 2 0175
+~ 619819,728178,0,120296 2 0176
+a~ "
+char168 619819,728178,0,120298 2 0177
+ad "
diff --git a/dvi/devdvi/CW b/dvi/devdvi/CW
new file mode 100644
index 000000000..254764821
--- /dev/null
+++ b/dvi/devdvi/CW
@@ -0,0 +1,157 @@
+name CW
+special
+internalname cmtt10
+spacewidth 550498
+checksum -538297224
+designsize 10485760
+charset
+*G 550498,640797 2 0000
+*D 550498,640797 2 0001
+*H 550498,640797 2 0002
+*L 550498,640797 2 0003
+*C 550498,640797 2 0004
+*P 550498,640797 2 0005
+*S 550498,640797 2 0006
+*U 550498,640797 2 0007
+*F 550498,640797 2 0010
+*Q 550498,640797 2 0011
+*W 550498,640797 2 0012
+ff 550498,640797 2 0013
+fi 550498,640797 2 0014
+fl 550498,640797 2 0015
+Fi 550498,407779,233018 1 0016
+Fl 550498,407779,233018 1 0017
+.i 550498,451470 0 0020
+.j 550498,451470,233018 1 0021
+ga 550498,640797 2 0022
+char180 550498,640797 2 0023
+aa "
+ah 550498,593466 2 0024
+ab 550498,640797 2 0025
+char175 550498,593027 2 0026
+a- "
+ao 550498,640797 2 0027
+char184 550498,0,203891 1 0030
+ac "
+char223 550498,640797 2 0031
+ss "
+char230 550498,451470 0 0032
+ae "
+oe 550498,451470 0 0033
+char248 550498,567979,116509 3 0034
+/o "
+char198 550498,640797 2 0035
+AE "
+OE 550498,640797 2 0036
+char216 550498,699051,58254 3 0037
+/O "
+--- 550498,230104,116509 1 0040
+! 550498,640797 2 0041
+" 550498,640797 2 0042
+sh 550498,640797 2 0043
+# "
+Do 550498,728178,87381 3 0044
+$ "
+% 550498,728178,87381 3 0045
+& 550498,640797 2 0046
+' 550498,640797 2 0047
+( 550498,728178,87379 3 0050
+) 550498,728178,87379 3 0051
+* 550498,546134 2 0052
++ 550498,556326,-84470 2 0053
+, 550498,131072,145635 1 0054
+\- 550498,556326,-84470 2 0055
+. 550498,131072 0 0056
+sl 550498,728178,87379 3 0057
+/ "
+0 550498,640797 2 0060
+1 550498,640797 2 0061
+2 550498,640797 2 0062
+3 550498,640797 2 0063
+4 550498,640797 2 0064
+5 550498,640797 2 0065
+6 550498,640797 2 0066
+7 550498,640797 2 0067
+8 550498,640797 2 0070
+9 550498,640797 2 0071
+: 550498,451470 0 0072
+; 550498,451470,145635 1 0073
+< 550498,582542,-58254 2 0074
+= 550498,435813,-204984 0 0075
+> 550498,582542,-58254 2 0076
+? 550498,640797 2 0077
+at 550498,640797 2 0100
+@ "
+A 550498,640797 2 0101
+B 550498,640797 2 0102
+C 550498,640797 2 0103
+D 550498,640797 2 0104
+E 550498,640797 2 0105
+F 550498,640797 2 0106
+G 550498,640797 2 0107
+H 550498,640797 2 0110
+I 550498,640797 2 0111
+J 550498,640797 2 0112
+K 550498,640797 2 0113
+L 550498,640797 2 0114
+M 550498,640797 2 0115
+N 550498,640797 2 0116
+O 550498,640797 2 0117
+P 550498,640797 2 0120
+Q 550498,640797,145635 3 0121
+R 550498,640797 2 0122
+S 550498,640797 2 0123
+T 550498,640797 2 0124
+U 550498,640797 2 0125
+V 550498,640797 2 0126
+W 550498,640797 2 0127
+X 550498,640797 2 0130
+Y 550498,640797 2 0131
+Z 550498,640797 2 0132
+lB 550498,728178,87379 3 0133
+[ "
+rs 550498,728178,87379 3 0134
+\ "
+rB 550498,728178,87379 3 0135
+] "
+ha 550498,640797 2 0136
+^ "
+a^ "
+_ 550498,0,99757 1 0137
+oq 550498,640797 2 0140
+` "
+a 550498,451470 0 0141
+b 550498,640797 2 0142
+c 550498,451470 0 0143
+d 550498,640797 2 0144
+e 550498,451470 0 0145
+f 550498,640797 2 0146
+g 550498,451470,233018 1 0147
+h 550498,640797 2 0150
+i 550498,640797 2 0151
+j 550498,640797,233018 3 0152
+k 550498,640797 2 0153
+l 550498,640797 2 0154
+m 550498,451470 0 0155
+n 550498,451470 0 0156
+o 550498,451470 0 0157
+p 550498,451470,233018 1 0160
+q 550498,451470,233018 1 0161
+r 550498,451470 0 0162
+s 550498,451470 0 0163
+t 550498,580466 2 0164
+u 550498,451470 0 0165
+v 550498,451470 0 0166
+w 550498,451470 0 0167
+x 550498,451470 0 0170
+y 550498,451470,233018 1 0171
+z 550498,451470 0 0172
+{ 550498,728178,87379 3 0173
+lC "
+| 550498,728178,87379 3 0174
+ba "
+} 550498,728178,87379 3 0175
+rC "
+~ 550498,640797 2 0176
+a~ "
+--- 550498,640797 2 0177
diff --git a/dvi/devdvi/CompileFonts b/dvi/devdvi/CompileFonts
new file mode 100755
index 000000000..8859f8ea8
--- /dev/null
+++ b/dvi/devdvi/CompileFonts
@@ -0,0 +1,15 @@
+#! /bin/sh
+# Compile fonts in the sizes needed by groff.
+sizes="5 6 7 8 9 10 11 12 14 16 18 20 22 24 28 36"
+fonts="cmr10 cmti10 cmbx10 cmbxti10 cmtt10 cmex10 cmmi10 cmsy10 cmss10 cmssbx10 cmssi10"
+mode=cx
+dpi=300
+
+for f in $fonts; do
+ for s in $sizes; do
+ virmf "&cm \\mode=$mode; mag=$s/10; batchmode; input $f" >/dev/null
+ mag=`expr $s \* $dpi / 10`
+ gftopk $f.${mag}gf >/dev/null
+ rm $f.${mag}gf
+ done
+done
diff --git a/dvi/devdvi/DESC b/dvi/devdvi/DESC
new file mode 100644
index 000000000..46214b065
--- /dev/null
+++ b/dvi/devdvi/DESC
@@ -0,0 +1,9 @@
+sizescale 100
+unitwidth 131072
+res 57816
+hor 1
+vert 1
+sizes 500 600 700 800 900 1000 1100 1200 1400 1440 1600 1728 1800
+2000 2074 2200 2400 2488 2800 3600 0
+fonts 8 R I B BI MI S EX CW
+tcommand
diff --git a/dvi/devdvi/EX b/dvi/devdvi/EX
new file mode 100644
index 000000000..6e83d02bd
--- /dev/null
+++ b/dvi/devdvi/EX
@@ -0,0 +1,144 @@
+name EX
+special
+internalname cmex10
+checksum -89033454
+designsize 10485760
+charset
+parenleft0 480600,41942,1216362 1 0000
+parenright0 480600,41942,1216362 1 0001
+bracketleft0 436909,41942,1216362 1 0002
+bracketright0 436909,41942,1216362 1 0003
+floorleft0 495163,41942,1216362 1 0004
+floorright0 495163,41942,1216362 1 0005
+ceilingleft0 495163,41942,1216362 1 0006
+ceilingright0 495163,41942,1216362 1 0007
+braceleft0 611672,41942,1216362 1 0010
+braceright0 611672,41942,1216362 1 0011
+angleleft0 495163,41942,1216362 1 0012
+angleright0 495163,41942,1216362 1 0013
+barex 349526,0,629152 1 0014
+bardblex 582544,0,629152 1 0015
+slash0 605845,41942,1216362 1 0016
+backslash0 605845,41942,1216362 1 0017
+parenleft1 626235,41942,1845514 1 0020
+parenright1 626235,41942,1845514 1 0021
+parenleft2 771872,41942,2474666 1 0022
+parenright2 771872,41942,2474666 1 0023
+bracketleft2 553418,41942,2474666 1 0024
+bracketright2 553418,41942,2474666 1 0025
+floorleft2 611672,41942,2474666 1 0026
+floorright2 611672,41942,2474666 1 0027
+ceilingleft2 611672,41942,2474666 1 0030
+ceilingright2 611672,41942,2474666 1 0031
+braceleft2 786434,41942,2474666 1 0032
+braceright2 786434,41942,2474666 1 0033
+angleleft2 786434,41942,2474666 1 0034
+angleright2 786434,41942,2474666 1 0035
+slash2 1095182,41942,2474666 1 0036
+backslash2 1095182,41942,2474666 1 0037
+parenleft3 830126,41942,3103818 1 0040
+parenright3 830126,41942,3103818 1 0041
+bracketleft3 611672,41942,3103818 1 0042
+bracketright3 611672,41942,3103818 1 0043
+floorleft3 669926,41942,3103818 1 0044
+floorright3 669926,41942,3103818 1 0045
+ceilingleft3 669926,41942,3103818 1 0046
+ceilingright3 669926,41942,3103818 1 0047
+braceleft3 844691,41942,3103818 1 0050
+braceright3 844691,41942,3103818 1 0051
+angleleft3 844691,41942,3103818 1 0052
+angleright3 844691,41942,3103818 1 0053
+slash3 1339851,41942,3103818 1 0054
+backslash3 1339851,41942,3103818 1 0055
+slash1 850515,41942,1845514 1 0056
+backslash1 850515,41942,1845514 1 0057
+parenlefttp 917507,41942,1845514 1 0060
+parenrighttp 917507,41942,1845514 1 0061
+bracketlefttp 699053,41942,1845514 1 0062
+bracketrighttp 699053,41942,1845514 1 0063
+bracketleftbt 699053,41942,1845514 1 0064
+bracketrightbt 699053,41942,1845514 1 0065
+bracketleftex 699053,0,629152 1 0066
+bracketrightex 699053,0,629152 1 0067
+lt 932070,0,943728 1 0070
+bracelefttp "
+rt 932070,0,943728 1 0071
+bracerighttp "
+lb 932070,0,943728 1 0072
+braceleftbt "
+rb 932070,0,943728 1 0073
+bracerightbt "
+lk 932070,0,1887456 1 0074
+braceleftmid "
+rk 932070,0,1887456 1 0075
+bracerightmid "
+braceleftex 932070,0,314576 1 0076
+bracerightex "
+braceex "
+arrowvertex 699053,0,629152 1 0077
+parenleftbt 917507,41942,1845514 1 0100
+parenrightbt 917507,41942,1845514 1 0101
+parenleftex 917507,0,629152 1 0102
+parenrightex 917507,0,629152 1 0103
+angleleft1 640798,41942,1845514 1 0104
+angleright1 640798,41942,1845514 1 0105
+--- 873816,0,1048590 1 0106
+--- 1165088,104859,1572877 1 0107
+--- 495162,0,1165096,203891 1 0110
+ointegral 582544,0,2330194,466035 1 0111
+ois "
+--- 1165088,0,1048590 1 0112
+bigcircledot 1584520,104859,1572877 1 0113
+--- 1165088,0,1048590 1 0114
+bigcircleplus 1584520,104859,1572877 1 0115
+--- 1165088,0,1048590 1 0116
+bigcirclemultiply 1584520,104859,1572877 1 0117
+--- 1106834,0,1048590 1 0120
+--- 990325,0,1048590 1 0121
+--- 495162,0,1165096,203891 1 0122
+--- 873816,0,1048590 1 0123
+--- 873816,0,1048590 1 0124
+--- 873816,0,1048590 1 0125
+--- 873816,0,1048590 1 0126
+--- 873816,0,1048590 1 0127
+sum 1514614,104859,1572877 1 0130
+product 1339851,104859,1572877 1 0131
+integral 582544,0,2330194,466035 1 0132
+is "
+bigunion 1165088,104859,1572877 1 0133
+bigintersection 1165088,104859,1572877 1 0134
+bigunionplus 1165088,104859,1572877 1 0135
+biglogicaland 1165088,104859,1572877 1 0136
+biglogicalor 1165088,104859,1572877 1 0137
+--- 990325,0,1048590 1 0140
+coproduct 1339851,104859,1572877 1 0141
+--- 582544,757306 2 0142
+--- 1048579,786432 2 0143
+--- 1514614,786432 2 0144
+--- 582544,757306 2 0145
+--- 1048579,786432 2 0146
+--- 1514614,786432 2 0147
+bracketleft1 495163,41942,1845514 1 0150
+bracketright1 495163,41942,1845514 1 0151
+floorleft1 553418,41942,1845514 1 0152
+floorright1 553418,41942,1845514 1 0153
+ceilingleft1 553418,41942,1845514 1 0154
+ceilingright1 553418,41942,1845514 1 0155
+braceleft1 699053,41942,1845514 1 0156
+braceright1 699053,41942,1845514 1 0157
+sr0 1048579,41942,1216362 1 0160
+sr1 1048579,41942,1845514 1 0161
+sr2 1048579,41942,2474666 1 0162
+sr3 1048579,41942,3103818 1 0163
+--- 1106834,0,1887456 1 0164
+--- 1106834,0,629152 1 0165
+--- 1106834,41942,587210 1 0166
+arrowvertdblex 815562,0,629152 1 0167
+arrowverttp 699053,0,629152 1 0170
+arrowvertbt 699053,0,629152 1 0171
+--- 471864,125827 0 0172
+--- 471864,125827 0 0173
+--- 471864,125827 0 0174
+--- 471864,125827 0 0175
+arrowvertdbltp 815562,0,629152 1 0176
+arrowvertdblbt 815562,0,629152 1 0177
diff --git a/dvi/devdvi/FontMakefile b/dvi/devdvi/FontMakefile
new file mode 100644
index 000000000..fb6f1b864
--- /dev/null
+++ b/dvi/devdvi/FontMakefile
@@ -0,0 +1,74 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+TFMDIR=tfm
+GFDIR=gf
+TFMTODIT=../tfmtodit
+FONTS=R I B BI CW MI S EX H HI HB
+SPECIALFLAG=-s
+
+all: $(FONTS)
+
+# R is special because it contains \(pl \(eq
+
+R: texr.map
+ $(TFMTODIT) $(SPECIALFLAG) -g $(GFDIR)/cmr10.300gf \
+ $(TFMDIR)/cmr10.tfm texr.map $@
+
+# I is special because it contains \(Po
+
+I: texi.map
+ $(TFMTODIT) $(SPECIALFLAG) -g $(GFDIR)/cmti10.300gf \
+ $(TFMDIR)/cmti10.tfm texi.map $@
+
+B: texb.map
+ $(TFMTODIT) -g $(GFDIR)/cmbx10.300gf \
+ $(TFMDIR)/cmbx10.tfm texb.map $@
+
+BI: texi.map
+ $(TFMTODIT) -g $(GFDIR)/cmbxti10.300gf \
+ $(TFMDIR)/cmbxti10.tfm texi.map $@
+
+# CW is special because it contains "
+
+CW: textt.map
+ $(TFMTODIT) $(SPECIALFLAG) $(TFMDIR)/cmtt10.tfm textt.map $@
+
+MI: texmi.map
+ $(TFMTODIT) $(SPECIALFLAG) -k 0177 $(TFMDIR)/cmmi10.tfm texmi.map $@
+
+S: texsy.map
+ $(TFMTODIT) $(SPECIALFLAG) -k 060 $(TFMDIR)/cmsy10.tfm texsy.map $@
+
+
+EX: texex.map
+ $(TFMTODIT) $(SPECIALFLAG) $(TFMDIR)/cmex10.tfm texex.map $@
+
+H: texr.map
+ $(TFMTODIT) -g $(GFDIR)/cmss10.300gf $(TFMDIR)/cmss10.tfm texb.map $@
+
+HB: texr.map
+ $(TFMTODIT) -g $(GFDIR)/cmssbx10.300gf \
+ $(TFMDIR)/cmssbx10.tfm texb.map $@
+
+HI: texr.map
+ $(TFMTODIT) -g $(GFDIR)/cmssi10.300gf \
+ $(TFMDIR)/cmssi10.tfm texb.map $@
+
+$(FONTS): $(TFMTODIT)
diff --git a/dvi/devdvi/H b/dvi/devdvi/H
new file mode 100644
index 000000000..96ae0f5fe
--- /dev/null
+++ b/dvi/devdvi/H
@@ -0,0 +1,302 @@
+name H
+internalname cmss10
+spacewidth 349526
+ligatures ff fi fl ffi ffl 0
+checksum 1831058770
+designsize 10485760
+kernpairs
+ff ' 72818
+ff ? 72818
+ff ! 72818
+ff ) 72818
+ff rB 72818
+ff ] 72818
+' ? 116509
+' ! 116509
+A t -29128
+A C -29128
+A O -29128
+A G -29128
+A U -29128
+A Q -29128
+A T -87382
+A Y -87382
+A V -116509
+A W -116509
+D X -29128
+D W -29128
+D A -29128
+D V -29128
+D Y -29128
+F o -29128
+F e -29128
+F u -29128
+F r -29128
+F a -29128
+F A -87382
+F O -29128
+F C -29128
+F G -29128
+F Q -29128
+I I 29128
+K O -29128
+K C -29128
+K G -29128
+K Q -29128
+L T -87382
+L Y -87382
+L V -116509
+L W -116509
+O X -29128
+O W -29128
+O A -29128
+O V -29128
+O Y -29128
+P A -87382
+P o -29128
+P e -29128
+P a -29128
+P . -87382
+P , -87382
+T y -87382
+T e -87382
+T o -87382
+T r -87382
+T a -87382
+T A -87382
+T u -87382
+V o -29128
+V e -29128
+V u -29128
+V r -29128
+V a -29128
+V A -87382
+V O -29128
+V C -29128
+V G -29128
+V Q -29128
+W o -29128
+W e -29128
+W u -29128
+W r -29128
+W a -29128
+W A -87382
+W O -29128
+W C -29128
+W G -29128
+W Q -29128
+X O -29128
+X C -29128
+X G -29128
+X Q -29128
+Y e -87382
+Y o -87382
+Y r -87382
+Y a -87382
+Y A -87382
+Y u -87382
+a r -29128
+a y -29128
+a w -29128
+b e 29128
+b o 29128
+b x -29128
+b d 29128
+b c 29128
+b q 29128
+b r -29128
+b y -29128
+b w -29128
+f ' 72818
+f ? 72818
+f ! 72818
+f ) 72818
+f rB 72818
+f ] 72818
+g j 29128
+k e -29128
+k a -29128
+k o -29128
+k c -29128
+o e 29128
+o o 29128
+o x -29128
+o d 29128
+o c 29128
+o q 29128
+o r -29128
+o y -29128
+o w -29128
+p e 29128
+p o 29128
+p x -29128
+p d 29128
+p c 29128
+p q 29128
+p r -29128
+p y -29128
+p w -29128
+t y -29128
+t w -29128
+u w -29128
+w e -29128
+w a -29128
+w o -29128
+w c -29128
+y o -29128
+y e -29128
+y a -29128
+y . -87382
+y , -87382
+charset
+*G 567981,728178,0,0,0,-145637 2 0000
+*D 873816,728178 2 0001
+*H 815562,728178,0,0,-29128,-29128 2 0002
+*L 640800,728178 2 0003
+*C 699053,728178 2 0004
+*P 742746,728178 2 0005
+*S 757307,728178 2 0006
+*U 815562,728178,0,0,-29128,-145637 2 0007
+*F 757307,728178,0,0,-29128 2 0010
+*Q 815562,728178,0,0,-29128,-72818 2 0011
+*W 757307,728178 2 0012
+ff 611672,728178,0,72818 2 0013
+fi 562155,728178 2 0014
+fl 562155,728178 2 0015
+Fi 853427,728178 2 0016
+Fl 853427,728178 2 0017
+.i 250494,466034 0 0020
+.j 279622,466034,203890 1 0021
+ga 524290,728178 2 0022
+char180 524290,728178 2 0023
+aa "
+ah 524290,662642 2 0024
+ab 524290,728178 2 0025
+char175 524290,638464 2 0026
+a- "
+ao 699054,728178 2 0027
+char184 466035,0,178403 1 0030
+ac "
+char223 503902,728178 2 0031
+ss "
+char230 757307,466034 0 0032
+ae "
+oe 815562,466034 0 0033
+char248 524290,567979,101946 3 0034
+/o "
+char198 902944,728178 2 0035
+AE "
+OE 1019453,728178 2 0036
+char216 815562,779150,50973 3 0037
+/O "
+--- 250494,466034 0 0040
+! 334963,728178 2 0041
+rq 524290,728178 2 0042
+sh 873816,728178,203888 3 0043
+# "
+Do 524290,786432,58254 3 0044
+$ "
+% 873816,786432,58254 3 0045
+& 795173,728178 2 0046
+' 291272,728178 2 0047
+( 407781,786432,262144 3 0050
+) 407781,786432,262144 3 0051
+* 524290,786432 2 0052
++ 815562,611670,87382 3 0053
+, 291272,87381,131072 1 0054
+char173 349526,466034 0 0055
+hy "
+- "
+. 291272,87381 0 0056
+sl 524290,786432,262144 3 0057
+/ "
+0 524290,687400 2 0060
+1 524290,687400 2 0061
+2 524290,687400 2 0062
+3 524290,687400 2 0063
+4 524290,687400 2 0064
+5 524290,687400 2 0065
+6 524290,687400 2 0066
+7 524290,687400 2 0067
+8 524290,687400 2 0070
+9 524290,687400 2 0071
+: 291272,466034 0 0072
+; 291272,466034,131072 1 0073
+char161 334963,524288,203890 3 0074
+r! "
+= 815562,387973,-136315 0 0075
+char191 495163,524288,203890 3 0076
+r? "
+? 495163,728178 2 0077
+at 699053,728178 2 0100
+@ "
+A 699054,728178 2 0101
+B 699054,728178 2 0102
+C 669926,728178,0,0,-29128 2 0103
+D 757309,728178,0,0,0,-29128 2 0104
+E 626235,728178 2 0105
+F 597109,728178,0,0,0,-145637 2 0106
+G 699053,728178,0,0,-29128 2 0107
+H 742746,728178 2 0110
+I 291274,728178 2 0111
+J 495163,728178,0,0,0,-29128 2 0112
+K 728182,728178 2 0113
+L 567981,728178,0,0,0,58254 2 0114
+M 917509,728178 2 0115
+N 742746,728178 2 0116
+O 771870,728178,0,0,-29128,-29128 2 0117
+P 669926,728178,0,0,0,-145637 2 0120
+Q 771870,728178,131072,0,-29128 3 0121
+R 677208,728178 2 0122
+S 582544,728178 2 0123
+T 713616,728178,0,0,0,-145637 2 0124
+U 720901,728178,0,0,29126 2 0125
+V 699054,728178,0,14563,43691,-218454 2 0126
+W 990326,728178,0,14563,43691,-131074 2 0127
+X 699054,728178 2 0130
+Y 699054,728178,0,26214,29128,-206803 2 0131
+Z 640798,728178 2 0132
+lB 302923,786432,262144 3 0133
+[ "
+lq 524290,728178 2 0134
+rB 302923,786432,262144 3 0135
+] "
+ha 524290,728178 2 0136
+^ "
+a^ "
+a. 291272,712366 2 0137
+oq 291272,728178 2 0140
+` "
+a 503901,466034 0 0141
+b 541766,728178 2 0142
+c 466035,466034 0 0143
+d 541766,728178 2 0144
+e 466035,466034 0 0145
+f 320400,728178,0,72818 2 0146
+g 524290,466034,203890,14563 1 0147
+h 541766,728178 2 0150
+i 250494,712366 2 0151
+j 279622,712366,203890 3 0152
+k 512640,728178 2 0153
+l 250494,728178 2 0154
+m 833038,466034 0 0155
+n 541766,466034 0 0156
+o 524290,466034 0 0157
+p 541766,466034,203890 1 0160
+q 541766,466034,203890 1 0161
+r 358266,466034,0,14563 0 0162
+s 401955,466034 0 0163
+t 378653,599189 2 0164
+u 541766,466034 0 0165
+v 483512,466034,0,14563 0 0166
+w 716530,466034,0,14563 0 0167
+x 483512,466034 0 0170
+y 483512,466034,203890,14563 1 0171
+z 455840,466034 0 0172
+en 524290,466034,0,29128 0 0173
+em 1048579,466034,0,29128 0 0174
+a" 524290,728178 2 0175
+~ 524290,709454 2 0176
+a~ "
+char168 524290,712366 2 0177
+ad "
diff --git a/dvi/devdvi/HB b/dvi/devdvi/HB
new file mode 100644
index 000000000..aaff89681
--- /dev/null
+++ b/dvi/devdvi/HB
@@ -0,0 +1,302 @@
+name HB
+internalname cmssbx10
+spacewidth 384480
+ligatures ff fi fl ffi ffl 0
+checksum -244629176
+designsize 10485760
+kernpairs
+ff ' 80101
+ff ? 80101
+ff ! 80101
+ff ) 80101
+ff rB 80101
+ff ] 80101
+' ? 128160
+' ! 128160
+A t -32040
+A C -32040
+A O -32040
+A G -32040
+A U -32040
+A Q -32040
+A T -96120
+A Y -96120
+A V -128160
+A W -128160
+D X -32040
+D W -32040
+D A -32040
+D V -32040
+D Y -32040
+F o -32040
+F e -32040
+F u -32040
+F r -32040
+F a -32040
+F A -96120
+F O -32040
+F C -32040
+F G -32040
+F Q -32040
+I I 32040
+K O -32040
+K C -32040
+K G -32040
+K Q -32040
+L T -96120
+L Y -96120
+L V -128160
+L W -128160
+O X -32040
+O W -32040
+O A -32040
+O V -32040
+O Y -32040
+P A -96120
+P o -32040
+P e -32040
+P a -32040
+P . -96120
+P , -96120
+T y -96120
+T e -96120
+T o -96120
+T r -96120
+T a -96120
+T A -96120
+T u -96120
+V o -32040
+V e -32040
+V u -32040
+V r -32040
+V a -32040
+V A -96120
+V O -32040
+V C -32040
+V G -32040
+V Q -32040
+W o -32040
+W e -32040
+W u -32040
+W r -32040
+W a -32040
+W A -96120
+W O -32040
+W C -32040
+W G -32040
+W Q -32040
+X O -32040
+X C -32040
+X G -32040
+X Q -32040
+Y e -96120
+Y o -96120
+Y r -96120
+Y a -96120
+Y A -96120
+Y u -96120
+a r -32040
+a y -32040
+a w -32040
+b e 32040
+b o 32040
+b x -32040
+b d 32040
+b c 32040
+b q 32040
+b r -32040
+b y -32040
+b w -32040
+f ' 80101
+f ? 80101
+f ! 80101
+f ) 80101
+f rB 80101
+f ] 80101
+g j 32040
+k e -32040
+k a -32040
+k o -32040
+k c -32040
+o e 32040
+o o 32040
+o x -32040
+o d 32040
+o c 32040
+o q 32040
+o r -32040
+o y -32040
+o w -32040
+p e 32040
+p o 32040
+p x -32040
+p d 32040
+p c 32040
+p q 32040
+p r -32040
+p y -32040
+p w -32040
+t y -32040
+t w -32040
+u w -32040
+w e -32040
+w a -32040
+w o -32040
+w c -32040
+y o -32040
+y e -32040
+y a -32040
+y . -96120
+y , -96120
+charset
+*G 608760,728178,0,0,0,-160200 2 0000
+*D 961200,728178 2 0001
+*H 897120,728178,0,0,-32040,-32040 2 0002
+*L 704880,728178 2 0003
+*C 768960,728178 2 0004
+*P 833040,728178 2 0005
+*S 833040,728178 2 0006
+*U 897120,728178,0,0,-32040,-160200 2 0007
+*F 833040,728178,0,0,-32040 2 0010
+*Q 897120,728178,0,0,-32040,-80101 2 0011
+*W 833040,728178 2 0012
+ff 672840,728178,0,80101 2 0013
+fi 614586,728178 2 0014
+fl 614586,728178 2 0015
+Fi 934986,728178 2 0016
+Fl 934986,728178 2 0017
+.i 267971,480597 0 0020
+.j 300011,480597,203890 1 0021
+ga 576720,728178 2 0022
+char180 576720,728178 2 0023
+aa "
+ah 576720,666283 2 0024
+ab 576720,728178 2 0025
+char175 576720,668757 2 0026
+a- "
+ao 768960,728178 2 0027
+char184 512640,0,178403 1 0030
+ac "
+char223 592739,728178 2 0031
+ss "
+char230 833040,480597 0 0032
+ae "
+oe 897120,480597 0 0033
+char248 576720,582542,101946 3 0034
+/o "
+char198 993240,728178 2 0035
+AE "
+OE 1121400,728178 2 0036
+char216 897120,779150,50973 3 0037
+/O "
+--- 267971,480597 0 0040
+! 384480,728178 2 0041
+rq 585458,728178 2 0042
+sh 961200,728178,203888 3 0043
+# "
+Do 576720,786432,58254 3 0044
+$ "
+% 1079109,786432,58254 3 0045
+& 870906,728178 2 0046
+' 320400,728178 2 0047
+( 448560,786432,262144 3 0050
+) 448560,786432,262144 3 0051
+* 576720,786432 2 0052
++ 897120,646624,122336 3 0053
+, 320400,136898,110683 1 0054
+char173 384480,480597 0 0055
+hy "
+- "
+. 320400,136898 0 0056
+sl 576720,786432,262144 3 0057
+/ "
+0 576720,728178 2 0060
+1 576720,728178 2 0061
+2 576720,728178 2 0062
+3 576720,728178 2 0063
+4 576720,728178 2 0064
+5 576720,728178 2 0065
+6 576720,728178 2 0066
+7 576720,728178 2 0067
+8 576720,728178 2 0070
+9 576720,728178 2 0071
+: 320400,480597 0 0072
+; 320400,480597,110683 1 0073
+char161 384480,524288,203890 3 0074
+r! "
+= 897120,425984,-98304 0 0075
+char191 544680,524288,203890 3 0076
+r? "
+? 544680,728178 2 0077
+at 768960,728178 2 0100
+@ "
+A 768960,728178 2 0101
+B 768960,728178 2 0102
+C 736920,728178,0,0,-32040 2 0103
+D 833040,728178,0,0,0,-32040 2 0104
+E 672840,728178 2 0105
+F 640800,728178,0,0,0,-160200 2 0106
+G 768960,728178,0,0,-32040 2 0107
+H 833040,728178 2 0110
+I 346614,728178 2 0111
+J 544680,728178,0,0,0,-32040 2 0112
+K 801000,728178 2 0113
+L 608760,728178,0,0,0,64080 2 0114
+M 1025280,728178 2 0115
+N 833040,728178 2 0116
+O 833040,728178,0,0,-32040,-32040 2 0117
+P 736920,728178,0,0,0,-160200 2 0120
+Q 833040,728178,110683,0,-32040 3 0121
+R 736920,728178 2 0122
+S 640800,728178 2 0123
+T 768960,728178,0,0,0,-160200 2 0124
+U 801000,728178,0,0,32040 2 0125
+V 768960,728178,0,16021,48061,-240299 2 0126
+W 1089360,728178,0,16021,48061,-144179 2 0127
+X 768960,728178 2 0130
+Y 768960,728178,0,28835,32040,-227485 2 0131
+Z 704880,728178 2 0132
+lB 359722,786432,262144 3 0133
+[ "
+lq 585458,728178 2 0134
+rB 359722,786432,262144 3 0135
+] "
+ha 576720,728178 2 0136
+^ "
+a^ "
+a. 320400,728178 2 0137
+oq 320400,728178 2 0140
+` "
+a 550506,480597 0 0141
+b 588371,728178 2 0142
+c 512640,480597 0 0143
+d 588371,728178 2 0144
+e 535942,480597 0 0145
+f 352440,728178,0,80101 2 0146
+g 576720,480597,203890,16021 1 0147
+h 588371,728178 2 0150
+i 267971,728178 2 0151
+j 300011,728178,203890 3 0152
+k 556331,728178 2 0153
+l 267971,728178 2 0154
+m 908771,480597 0 0155
+n 588371,480597 0 0156
+o 576720,480597 0 0157
+p 588371,480597,203890 1 0160
+q 588371,480597,203890 1 0161
+r 390306,480597,0,16021 0 0162
+s 442152,480597 0 0163
+t 423802,617914 2 0164
+u 588371,480597 0 0165
+v 524291,480597,0,16021 0 0166
+w 780611,480597,0,16021 0 0167
+x 524291,480597 0 0170
+y 524291,480597,203890,16021 1 0171
+z 499533,480597 0 0172
+en 576720,480597,0,32040 0 0173
+em 1153440,480597,0,32040 0 0174
+a" 576720,728178 2 0175
+~ 576720,728178 2 0176
+a~ "
+char168 576720,728178 2 0177
+ad "
diff --git a/dvi/devdvi/HI b/dvi/devdvi/HI
new file mode 100644
index 000000000..dcfcced1a
--- /dev/null
+++ b/dvi/devdvi/HI
@@ -0,0 +1,303 @@
+name HI
+internalname cmssi10
+spacewidth 349526
+slant 11.999911
+ligatures ff fi fl ffi ffl 0
+checksum -984248855
+designsize 10485760
+kernpairs
+ff ' 72818
+ff ? 72818
+ff ! 72818
+ff ) 72818
+ff rB 72818
+ff ] 72818
+' ? 116509
+' ! 116509
+A t -29128
+A C -29128
+A O -29128
+A G -29128
+A U -29128
+A Q -29128
+A T -87382
+A Y -87382
+A V -116509
+A W -116509
+D X -29128
+D W -29128
+D A -29128
+D V -29128
+D Y -29128
+F o -29128
+F e -29128
+F u -29128
+F r -29128
+F a -29128
+F A -87382
+F O -29128
+F C -29128
+F G -29128
+F Q -29128
+I I 29128
+K O -29128
+K C -29128
+K G -29128
+K Q -29128
+L T -87382
+L Y -87382
+L V -116509
+L W -116509
+O X -29128
+O W -29128
+O A -29128
+O V -29128
+O Y -29128
+P A -87382
+P o -29128
+P e -29128
+P a -29128
+P . -87382
+P , -87382
+T y -87382
+T e -87382
+T o -87382
+T r -87382
+T a -87382
+T A -87382
+T u -87382
+V o -29128
+V e -29128
+V u -29128
+V r -29128
+V a -29128
+V A -87382
+V O -29128
+V C -29128
+V G -29128
+V Q -29128
+W o -29128
+W e -29128
+W u -29128
+W r -29128
+W a -29128
+W A -87382
+W O -29128
+W C -29128
+W G -29128
+W Q -29128
+X O -29128
+X C -29128
+X G -29128
+X Q -29128
+Y e -87382
+Y o -87382
+Y r -87382
+Y a -87382
+Y A -87382
+Y u -87382
+a r -29128
+a y -29128
+a w -29128
+b e 29128
+b o 29128
+b x -29128
+b d 29128
+b c 29128
+b q 29128
+b r -29128
+b y -29128
+b w -29128
+f ' 72818
+f ? 72818
+f ! 72818
+f ) 72818
+f rB 72818
+f ] 72818
+g j 29128
+k e -29128
+k a -29128
+k o -29128
+k c -29128
+o e 29128
+o o 29128
+o x -29128
+o d 29128
+o c 29128
+o q 29128
+o r -29128
+o y -29128
+o w -29128
+p e 29128
+p o 29128
+p x -29128
+p d 29128
+p c 29128
+p q 29128
+p r -29128
+p y -29128
+p w -29128
+t y -29128
+t w -29128
+u w -29128
+w e -29128
+w a -29128
+w o -29128
+w c -29128
+y o -29128
+y e -29128
+y a -29128
+y . -87382
+y , -87382
+charset
+*G 567981,728178,0,140214,0,-5422 2 0000
+*D 873816,728178 2 0001
+*H 815562,728178,0,79216,-75562,50088 2 0002
+*L 640800,728178 2 0003
+*C 699053,728178,0,134389,0,67195 2 0004
+*P 742746,728178,0,84870,0,42435 2 0005
+*S 757307,728178,0,125650,0,62826 2 0006
+*U 815562,728178,0,94694,-152950,-50942 2 0007
+*F 757307,728178,0,48261,-106517,48261 2 0010
+*Q 815562,728178,0,94694,-152950,-10907 2 0011
+*W 757307,728178,0,86955,0,43478 2 0012
+ff 611672,728178,0,227595 2 0013
+fi 562155,728178,0,102349 2 0014
+fl 562155,728178,0,99435 2 0015
+Fi 853427,728178,0,102349 2 0016
+Fl 853427,728178,0,99435 2 0017
+.i 250494,466034,0,43715 0 0020
+.j 279622,466034,203890,43715 1 0021
+ga 524290,728178 2 0022
+char180 524290,728178,0,96523 2 0023
+aa "
+ah 524290,662642,0,88419 2 0024
+ab 524290,728178,0,99435 2 0025
+char175 524290,638464,0,92018 2 0026
+a- "
+ao 773347,728178 2 0027
+char184 466035,0,178403 1 0030
+ac "
+char223 503902,728178,0,96523 2 0031
+ss "
+char230 757307,466034,0,71070 0 0032
+ae "
+oe 815562,466034,0,71070 0 0033
+char248 524290,567979,101946,50821 3 0034
+/o "
+char198 902944,728178,0,125650 2 0035
+AE "
+OE 1019453,728178,0,125650 2 0036
+char216 815562,779150,50973,79216 3 0037
+/O "
+--- 250494,466034 0 0040
+! 334963,728178,0,60114 2 0041
+rq 524290,728178,0,3315 2 0042
+sh 873816,728178,203888,53338 3 0043
+# "
+Do 524290,786432,58254,116982 3 0044
+$ "
+% 873816,786432,58254,32782 3 0045
+& 795173,728178,0,32064 2 0046
+' 291272,728178,0,81960 2 0047
+( 407781,786432,262144,138032 3 0050
+) 407781,786432,262144,26592 3 0051
+* 524290,786432,0,123469 2 0052
++ 815562,611670,87382,26592 3 0053
+, 291272,87381,131072 1 0054
+char173 349526,466034,0,20402 0 0055
+hy "
+- "
+. 291272,87381 0 0056
+sl 524290,786432,262144,138032 3 0057
+/ "
+0 524290,687400,0,116982 2 0060
+1 524290,687400,0,116982 2 0061
+2 524290,687400,0,116982 2 0062
+3 524290,687400,0,116982 2 0063
+4 524290,687400,0,116982 2 0064
+5 524290,687400,0,116982 2 0065
+6 524290,687400,0,116982 2 0066
+7 524290,687400,0,116982 2 0067
+8 524290,687400,0,116982 2 0070
+9 524290,687400,0,116982 2 0071
+: 291272,466034,0,26240 0 0072
+; 291272,466034,131072,26240 1 0073
+char161 334963,524288,203890,16776 3 0074
+r! "
+= 815562,387973,-136315,53338 0 0075
+char191 495163,524288,203890 3 0076
+r? "
+? 495163,728178,0,123822 2 0077
+at 699053,728178,0,79216 2 0100
+@ "
+A 699054,728178 2 0101
+B 699054,728178,0,86955,0,43478 2 0102
+C 669926,728178,0,125650,-75562,62826 2 0103
+D 757309,728178,0,79216,0,50088 2 0104
+E 626235,728178,0,125650,0,62826 2 0105
+F 597109,728178,0,140214,0,-5422 2 0106
+G 699053,728178,0,125650,-75562,62826 2 0107
+H 742746,728178,0,84870,0,42435 2 0110
+I 291274,728178,0,140214,0,70107 2 0111
+J 495163,728178,0,84870,0,13307 2 0112
+K 728182,728178,0,125650,0,62826 2 0113
+L 567981,728178,0,0,0,58254 2 0114
+M 917509,728178,0,84870,0,13307 2 0115
+N 742746,728178,0,84870,0,13307 2 0116
+O 771870,728178,0,79216,-75562,50088 2 0117
+P 669926,728178,0,86955,0,-58682 2 0120
+Q 771870,728178,131072,79216,-75562,79216 3 0121
+R 677208,728178,0,86955,0,65216 2 0122
+S 582544,728178,0,96523,0,48262 2 0123
+T 713616,728178,0,140214,-116083,-5422 2 0124
+U 720901,728178,0,84870,-75562,13307 2 0125
+V 699054,728178,0,169341,-111086,-63677 2 0126
+W 990326,728178,0,169341,-111086,23704 2 0127
+X 699054,728178,0,140214,0,70107 2 0130
+Y 699054,728178,0,180992,-125650,-52026 2 0131
+Z 640798,728178,0,125650,0,62826 2 0132
+lB 302923,786432,262144,167160 3 0133
+[ "
+lq 524290,728178,0,148952 2 0134
+rB 302923,786432,262144,91429 3 0135
+] "
+ha 524290,728178,0,83776 2 0136
+^ "
+a^ "
+a. 291272,712366,0,81512 2 0137
+oq 291272,728178,0,81960 2 0140
+` "
+a 503901,466034,0,10283 0 0141
+b 541766,728178,0,32053 2 0142
+c 466035,466034,0,87406 0 0143
+d 541766,728178,0,99435 2 0144
+e 466035,466034,0,71070 0 0145
+f 320400,728178,0,227595 2 0146
+g 524290,466034,203890,113621 1 0147
+h 541766,728178,0,18642 2 0150
+i 250494,712366,0,101901 2 0151
+j 279622,712366,203890,96075 3 0152
+k 512640,728178,0,87406 2 0153
+l 250494,728178,0,99435 2 0154
+m 833038,466034,0,18642 0 0155
+n 541766,466034,0,18642 0 0156
+o 524290,466034,0,69341 0 0157
+p 541766,466034,203890,40790 1 0160
+q 541766,466034,203890,43715 1 0161
+r 358266,466034,0,113621 0 0162
+s 401955,466034,0,81581 0 0163
+t 378653,599189,0,75757 2 0164
+u 541766,466034,0,43715 0 0165
+v 483512,466034,0,113621 0 0166
+w 716530,466034,0,113621 0 0167
+x 483512,466034,0,96144 0 0170
+y 483512,466034,203890,113621 1 0171
+z 455840,466034,0,91776 0 0172
+en 524290,466034,0,90349 0 0173
+em 1048579,466034,0,90349 0 0174
+a" 524290,728178,0,96523 2 0175
+~ 524290,709454,0,92544 2 0176
+a~ "
+char168 524290,712366,0,66949 2 0177
+ad "
diff --git a/dvi/devdvi/I b/dvi/devdvi/I
new file mode 100644
index 000000000..f1b4c0eb1
--- /dev/null
+++ b/dvi/devdvi/I
@@ -0,0 +1,346 @@
+name I
+special
+internalname cmti10
+spacewidth 375155
+slant 14.036243
+ligatures ff fi fl ffi ffl 0
+checksum -50321606
+designsize 10485760
+kernpairs
+ff ' 109373
+ff ? 109373
+ff ! 109373
+ff ) 109373
+ff rB 109373
+ff ] 109373
+' ' -80390
+' ? 107187
+' ! 107187
+A n -26797
+A l -26797
+A r -26797
+A u -26797
+A m -26797
+A t -26797
+A i -26797
+A C -26797
+A O -26797
+A G -26797
+A h -26797
+A b -26797
+A U -26797
+A k -26797
+A v -26797
+A w -26797
+A Q -26797
+A T -80390
+A Y -80390
+A V -107187
+A W -107187
+A e -53594
+A a -53594
+A o -53594
+A d -53594
+A c -53594
+A g -53594
+A q -53594
+D X -26797
+D W -26797
+D A -26797
+D V -26797
+D Y -26797
+F o -80390
+F e -80390
+F u -80390
+F r -80390
+F a -80390
+F A -107187
+F O -26797
+F C -26797
+F G -26797
+F Q -26797
+K O -26797
+K C -26797
+K G -26797
+K Q -26797
+L T -80390
+L Y -80390
+L V -107187
+L W -107187
+L e -53594
+L a -53594
+L o -53594
+L d -53594
+L c -53594
+L g -53594
+L q -53594
+O X -26797
+O W -26797
+O A -26797
+O V -26797
+O Y -26797
+P A -80390
+R n -26797
+R l -26797
+R r -26797
+R u -26797
+R m -26797
+R t -26797
+R i -26797
+R C -26797
+R O -26797
+R G -26797
+R h -26797
+R b -26797
+R U -26797
+R k -26797
+R v -26797
+R w -26797
+R Q -26797
+R T -80390
+R Y -80390
+R V -107187
+R W -107187
+R e -53594
+R a -53594
+R o -53594
+R d -53594
+R c -53594
+R g -53594
+R q -53594
+T y -80390
+T e -80390
+T o -80390
+T r -80390
+T a -80390
+T u -80390
+T A -80390
+V o -80390
+V e -80390
+V u -80390
+V r -80390
+V a -80390
+V A -107187
+V O -26797
+V C -26797
+V G -26797
+V Q -26797
+W A -80390
+X O -26797
+X C -26797
+X G -26797
+X Q -26797
+Y e -80390
+Y o -80390
+Y r -80390
+Y a -80390
+Y u -80390
+Y A -80390
+oq oq -80390
+oq ` -80390
+` oq -80390
+` ` -80390
+b e -53594
+b a -53594
+b o -53594
+b d -53594
+b c -53594
+b g -53594
+b q -53594
+c e -53594
+c a -53594
+c o -53594
+c d -53594
+c c -53594
+c g -53594
+c q -53594
+d l 53594
+e e -53594
+e a -53594
+e o -53594
+e d -53594
+e c -53594
+e g -53594
+e q -53594
+f ' 109373
+f ? 109373
+f ! 109373
+f ) 109373
+f rB 109373
+f ] 109373
+l l 53594
+n ' -107187
+o e -53594
+o a -53594
+o o -53594
+o d -53594
+o c -53594
+o g -53594
+o q -53594
+p e -53594
+p a -53594
+p o -53594
+p d -53594
+p c -53594
+p g -53594
+p q -53594
+r e -53594
+r a -53594
+r o -53594
+r d -53594
+r c -53594
+r g -53594
+r q -53594
+w l 53594
+charset
+*G 657686,716526,0,139518,0,5534 2 0000
+*D 857498,716526 2 0001
+*H 803904,716526,0,98595,-80538,71798 2 0002
+*L 725843,716526 2 0003
+*C 696717,716526,0,160373,0,80187 2 0004
+*P 779437,716526,0,171851,0,85926 2 0005
+*S 750310,716526,0,126120,0,63061 2 0006
+*U 803904,716526,0,116509,-170102,-17475 2 0007
+*F 750310,716526,0,62770,-116363,62770 2 0010
+*Q 803904,716526,0,116509,-170102,4659 2 0011
+*W 750310,716526,0,107552,0,53776 2 0012
+ff 643123,728178,203890,222240 3 0013
+fi 589530,728178,203890,108354 3 0014
+fl 616326,728178,203890,108354 3 0015
+Fi 924490,728178,203890,108354 3 0016
+Fl 937888,728178,203890,108354 3 0017
+.i 321562,451470,0,80440 0 0020
+.j 348358,451470,203890,39176 1 0021
+ga 535936,728178 2 0022
+char180 535936,728178,0,101654 2 0023
+aa "
+ah 535936,659002,0,86982 2 0024
+ab 535936,728178,0,113306 2 0025
+char175 535936,588949,0,108354 2 0026
+a- "
+ao 871672,728178 2 0027
+char184 482342,0,178403 1 0030
+ac "
+char223 562733,728178,203890,110245 3 0031
+ss "
+char230 750310,451470,0,78789 0 0032
+ae "
+oe 750310,451470,0,78789 0 0033
+char248 535936,553416,101946,96411 3 0034
+/o "
+char198 925654,716526,0,126120 2 0035
+AE "
+OE 1032842,716526,0,126120 2 0036
+char216 803904,767499,50973,98595 3 0037
+/O "
+--- 267968,451470 0 0040
+! 321562,728178,0,130200 2 0041
+rq 539432,728178,0,72994 2 0042
+sh 857498,728178,203888,69378 3 0043
+# "
+char163 806453,728178 2 0044
+Po "
+% 857498,786432,58254,143014 3 0045
+& 803904,728178,0,101654 2 0046
+' 321562,728178,0,130200 2 0047
+( 428749,786432,262144,169811 3 0050
+) 428749,786432,262144,38739 3 0051
+* 535936,786432,0,156413 2 0052
++ 803904,588949,59418,38739 3 0053
+, 321562,110683,203890 1 0054
+char173 375155,451470,0,29637 0 0055
+hy "
+- "
+. 321562,110683 0 0056
+sl 535936,786432,262144,169811 3 0057
+/ "
+0 535936,675749,0,142141 2 0060
+1 535936,675749,0,142141 2 0061
+2 535936,675749,0,142141 2 0062
+3 535936,675749,0,142141 2 0063
+4 535936,675749,203890,142141 3 0064
+5 535936,675749,0,142141 2 0065
+6 535936,675749,0,142141 2 0066
+7 535936,675749,203890,142141 3 0067
+8 535936,675749,0,142141 2 0070
+9 535936,675749,0,142141 2 0071
+: 321562,451470,0,61022 0 0072
+; 321562,451470,203890,61022 1 0073
+char161 321562,524288,203890,79227 3 0074
+r! "
+= 803904,384696,-139592,69378 0 0075
+char191 535936,524288,203890 3 0076
+r? "
+? 535936,728178,0,128451 2 0077
+at 803904,728178,0,100634 2 0100
+@ "
+A 779437,716526 2 0101
+B 738077,716526,0,107552,0,53776 2 0102
+C 750310,716526,0,152334,-80538,76168 2 0103
+D 791670,716526,0,98595,0,71798 2 0104
+E 711280,716526,0,126120,0,63061 2 0105
+F 684483,716526,0,139518,0,5534 2 0106
+G 811186,716526,0,91459,-80538,91459 2 0107
+H 779437,716526,0,171851,0,85926 2 0110
+I 404282,716526,0,165733,0,82867 2 0111
+J 550499,716526,0,147093,0,46750 2 0112
+K 806234,716526,0,152334,0,76168 2 0113
+L 657686,716526,0,0,0,53594 2 0114
+M 940218,716526,0,171851,0,59130 2 0115
+N 779437,716526,0,171851,0,59130 2 0116
+O 803904,716526,0,98595,-80538,71798 2 0117
+P 711280,716526,0,107552,0,-26432 2 0120
+Q 803904,716526,203890,98595,-80538,98595 3 0121
+R 764874,716526,0,40560,0,30421 2 0122
+S 589530,716526,0,125538,0,62770 2 0123
+T 750310,716526,0,139518,-134349,5534 2 0124
+U 779437,716526,0,171851,-121898,59130 2 0125
+V 779437,716526,0,192530,-138936,-21845 2 0126
+W 1047405,716526,0,192530,-138936,58546 2 0127
+X 779437,716526,0,165733,0,82867 2 0130
+Y 779437,716526,0,203248,-152334,-11126 2 0131
+Z 643123,716526,0,152334,0,76168 2 0132
+lB 321562,786432,262144,196608 3 0133
+[ "
+lq 539432,728178,0,176685 2 0134
+rB 321562,786432,262144,110392 3 0135
+] "
+ha 535936,728178,0,69688 2 0136
+^ "
+a^ "
+a. 321562,700301,0,123230 2 0137
+oq 321562,728178,0,130200 2 0140
+` "
+a 535936,451470,0,80440,-46677,80440 0 0141
+b 482342,728178,0,66190,-46677,66190 2 0142
+c 482342,451470,0,59274,-46677,59274 0 0143
+d 535936,728178,0,108354,-46677,108354 2 0144
+e 482342,451470,0,78789,-46677,78789 0 0145
+f 321562,728178,203890,222240,104566,112867 3 0146
+g 482342,451470,203890,92770,-21045,55147 1 0147
+h 535936,728178,0,80440,0,80440 2 0150
+i 321562,687194,0,106846,-32427,106846 2 0151
+j 321562,687194,203890,151701,50973,92770 3 0152
+k 482342,728178,0,112867,0,80440 2 0153
+l 267968,728178,0,108354,-26797,107237 2 0154
+m 857498,451470,0,80440,-32427,80440 0 0155
+n 589530,451470,0,80440,-32427,80440 0 0156
+o 535936,451470,0,66190,-46677,66190 0 0157
+p 535936,451470,203890,66190,-22718,66190 1 0160
+q 482342,451470,203890,92770,-46677,55147 1 0161
+r 442147,451470,0,112867,-32427,86070 0 0162
+s 428749,451470,0,86070,0,86070 0 0163
+t 348358,644958,0,99469,-45875,99469 2 0164
+u 562733,451470,0,80440,-32427,80440 0 0165
+v 482342,451470,0,112867,-32427,75245 0 0166
+w 696717,451470,0,112867,-32427,84651 0 0167
+x 486421,451470,0,126266,0,126266 0 0170
+y 509139,451470,203890,92770,-32427,55147 1 0171
+z 428749,451470,0,128888,0,80440 0 0172
+en 535936,451470,0,96552 0 0173
+em 1071872,451470,0,96552 0 0174
+a" 535936,728178,0,128451 2 0175
+~ 535936,700301,0,121482 2 0176
+a~ "
+char168 535936,700301,0,109832 2 0177
+ad "
diff --git a/dvi/devdvi/MI b/dvi/devdvi/MI
new file mode 100644
index 000000000..c48d5ca76
--- /dev/null
+++ b/dvi/devdvi/MI
@@ -0,0 +1,136 @@
+name MI
+special
+internalname cmmi10
+slant 14.036243
+checksum 195060286
+designsize 10485760
+charset
+--- 645166,716526,0,145637 2 0000
+--- 873816,716526 2 0001
+--- 799829,716526,0,29128 2 0002
+--- 728179,716526 2 0003
+--- 778424,716526,0,79371 2 0004
+--- 871630,716526,0,85195 2 0005
+--- 817746,716526,0,60438 2 0006
+--- 611669,716526,0,145637 2 0007
+--- 699051,716526 2 0010
+--- 641962,716526,0,115344 2 0011
+--- 809918,716526,0,52610 2 0012
+*a 670776,451470,0,3882 0 0013
+*b 593102,728178,203890,55342 3 0014
+*g 542880,451470,203890,58254 1 0015
+*d 466034,728178,0,39685 2 0016
+*e 425621,451470 0 0017
+*z 458754,728178,203890,77368 3 0020
+*y 520651,451470,203890,37622 1 0021
+*h 492248,728178,0,29128 2 0022
+*i 371130,451470 0 0023
+*k 604147,451470 0 0024
+*l 611672,728178 2 0025
+char181 631819,451470,203890 1 0026
+*m "
+*n 517979,451470,0,66750 0 0027
+*c 458754,728178,203890,48242 3 0030
+*p 597717,451470,0,37622 0 0031
+*r 542130,451470,203890 1 0032
+*s 599171,451470,0,37622 0 0033
+*t 458390,451470,0,118694 0 0034
+*u 566525,451470,0,37622 0 0035
+*f 624778,728178,203890 3 0036
+*x 656086,451470,203890 1 0037
+*q 683034,728178,203890,37622 3 0040
+*w 652691,451470,0,37622 0 0041
+--- 488970,451470 0 0042
+--- 620170,728178 2 0043
+--- 868357,451470,0,29128 0 0044
+--- 542130,451470,203890 1 0045
+ts 380474,451470,101946,83739 1 0046
+--- 685944,451470,203890 1 0047
+--- 1048579,384696,-139592 0 0050
+--- 1048579,384696,-139592 0 0051
+--- 1048579,384696,-139592 0 0052
+--- 1048579,384696,-139592 0 0053
+--- 291272,486275,-38013 2 0054
+--- 291272,486275,-38013 2 0055
+--- 524290,487880,-36408 2 0056
+--- 524290,487880,-36408 2 0057
+--- 524290,451470 0 0060
+--- 524290,451470 0 0061
+--- 524290,451470 0 0062
+--- 524290,451470,203890 1 0063
+--- 524290,451470,203890 1 0064
+--- 524290,451470,203890 1 0065
+--- 524290,675749 2 0066
+--- 524290,451470,203890 1 0067
+--- 524290,675749 2 0070
+--- 524290,451470,203890 1 0071
+--- 291272,110683 0 0072
+--- 291272,110683,203890 1 0073
+< 815562,565285,40997 3 0074
+--- 524290,786432,262144 3 0075
+> 815562,565285,40997 3 0076
+--- 524290,487880,-36408 2 0077
+pd 556693,728178,0,58254 2 0100
+--- 786434,716526 2 0101
+--- 795355,716526,0,52610 2 0102
+--- 749440,716526,0,75002 2 0103
+--- 868134,716526,0,29128 2 0104
+--- 774054,716526,0,60438 2 0105
+--- 674294,716526,0,145637 2 0106
+--- 824442,716526 2 0107
+--- 871630,716526,0,85195 2 0110
+--- 460938,716526,0,82283 2 0111
+--- 581450,716526,0,100853 2 0112
+--- 890563,716526,0,75002 2 0113
+--- 713616,716526 2 0114
+--- 1017266,716526,0,114323 2 0115
+--- 842502,716526,0,114323 2 0116
+--- 799829,716526,0,29128 2 0117
+--- 673200,716526,0,145637 2 0120
+--- 828957,716526,203890 3 0121
+--- 796173,716526,0,8101 2 0122
+--- 642982,716526,0,60438 2 0123
+--- 612763,716526,0,145637 2 0124
+--- 715944,716526,0,114323 2 0125
+--- 611670,716526,0,233018 2 0126
+--- 990323,716526,0,145637 2 0127
+--- 868718,716526,0,82283 2 0130
+--- 608758,716526,0,233018 2 0131
+--- 715800,716526,0,75002 2 0132
+--- 407781,786432 2 0133
+--- 407781,728178,203890 3 0134
+--- 407781,728178,203890 3 0135
+--- 1048579,375013,-149275 0 0136
+--- 1048579,375013,-149275 0 0137
+--- 436910,728178 2 0140
+--- 554267,451470 0 0141
+--- 450014,728178 2 0142
+--- 453778,451470 0 0143
+--- 545771,728178 2 0144
+--- 488245,451470 0 0145
+--- 513368,728178,203890,112869 3 0146
+--- 500138,451470,203890,37622 1 0147
+--- 604147,728178 2 0150
+--- 361248,691562 2 0151
+--- 431811,691562,203890,60024 3 0152
+--- 545893,728178,0,33010 2 0153
+--- 312874,728178,0,20634 2 0154
+--- 920664,451470 0 0155
+--- 629392,451470 0 0156
+*o 508269,451470 0 0157
+--- 527566,451470,203890 1 0160
+--- 468099,451470,203890,37622 1 0161
+--- 473075,451470,0,29128 0 0162
+--- 491520,451470 0 0163
+--- 378654,644958 2 0164
+--- 600266,451470 0 0165
+--- 508270,451470,0,37622 0 0166
+--- 750694,451470,0,28216 0 0167
+--- 599291,451470 0 0170
+--- 514098,451470,203890,37622 1 0171
+--- 487640,451470,0,46117 0 0172
+--- 338120,451470 0 0173
+--- 402685,451470,203890 1 0174
+wp 667376,451470,203890 1 0175
+--- 524290,749149,0,161291 2 0176
+--- 291272,728178,0,418866 2 0177
diff --git a/dvi/devdvi/Makefile b/dvi/devdvi/Makefile
new file mode 100644
index 000000000..bb318342b
--- /dev/null
+++ b/dvi/devdvi/Makefile
@@ -0,0 +1,39 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+FONTS=R I B BI CW MI S EX H HI HB
+FONTDIR=/usr/local/lib/groff/font
+DEVICEDIR=$(FONTDIR)/devdvi
+
+all:
+
+install.nobin:
+ -[ -d $(FONTDIR) ] || mkdir $(FONTDIR)
+ -[ -d $(DEVICEDIR) ] || mkdir $(DEVICEDIR)
+ -cd $(DEVICEDIR); rm -f $(FONTS) DESC eqnchar
+ cp $(FONTS) DESC eqnchar $(DEVICEDIR)
+
+install.bin:
+
+install: install.bin install.nobin
+
+fonts: FORCE
+ $(MAKE) -f FontMakefile
+
+FORCE:
diff --git a/dvi/devdvi/R b/dvi/devdvi/R
new file mode 100644
index 000000000..3e49b938e
--- /dev/null
+++ b/dvi/devdvi/R
@@ -0,0 +1,430 @@
+name R
+special
+internalname cmr10
+spacewidth 349526
+ligatures ff fi fl ffi ffl 0
+checksum 1274110073
+designsize 10485760
+kernpairs
+ff ' 81557
+ff ? 81557
+ff ! 81557
+ff ) 81557
+ff rB 81557
+ff ] 81557
+' ' -87382
+' ? 116509
+' ! 116509
+*A t -29128
+A t -29128
+*A C -29128
+A C -29128
+*A *O -29128
+*A O -29128
+A *O -29128
+A O -29128
+*A G -29128
+A G -29128
+*A U -29128
+A U -29128
+*A Q -29128
+A Q -29128
+*A *T -87382
+*A T -87382
+A *T -87382
+A T -87382
+*A Y -87382
+A Y -87382
+*A V -116509
+A V -116509
+*A W -116509
+A W -116509
+D *X -29128
+D X -29128
+D W -29128
+D *A -29128
+D A -29128
+D V -29128
+D Y -29128
+F o -87382
+F e -87382
+F u -87382
+F r -87382
+F a -87382
+F *A -116509
+F A -116509
+F *O -29128
+F O -29128
+F C -29128
+F G -29128
+F Q -29128
+*I *I 29128
+*I I 29128
+I *I 29128
+I I 29128
+*K *O -29128
+*K O -29128
+K *O -29128
+K O -29128
+*K C -29128
+K C -29128
+*K G -29128
+K G -29128
+*K Q -29128
+K Q -29128
+L *T -87382
+L T -87382
+L Y -87382
+L V -116509
+L W -116509
+*O *X -29128
+*O X -29128
+O *X -29128
+O X -29128
+*O W -29128
+O W -29128
+*O *A -29128
+*O A -29128
+O *A -29128
+O A -29128
+*O V -29128
+O V -29128
+*O Y -29128
+O Y -29128
+*R *A -87382
+*R A -87382
+P *A -87382
+P A -87382
+*R o -29128
+P o -29128
+*R e -29128
+P e -29128
+*R a -29128
+P a -29128
+*R . -87382
+P . -87382
+*R , -87382
+P , -87382
+R t -29128
+R C -29128
+R *O -29128
+R O -29128
+R G -29128
+R U -29128
+R Q -29128
+R *T -87382
+R T -87382
+R Y -87382
+R V -116509
+R W -116509
+*T y -29128
+T y -29128
+*T e -87382
+T e -87382
+*T o -87382
+T o -87382
+*T r -87382
+T r -87382
+*T a -87382
+T a -87382
+*T *A -87382
+*T A -87382
+T *A -87382
+T A -87382
+*T u -87382
+T u -87382
+V o -87382
+V e -87382
+V u -87382
+V r -87382
+V a -87382
+V *A -116509
+V A -116509
+V *O -29128
+V O -29128
+V C -29128
+V G -29128
+V Q -29128
+W o -87382
+W e -87382
+W u -87382
+W r -87382
+W a -87382
+W *A -116509
+W A -116509
+W *O -29128
+W O -29128
+W C -29128
+W G -29128
+W Q -29128
+*X *O -29128
+*X O -29128
+X *O -29128
+X O -29128
+*X C -29128
+X C -29128
+*X G -29128
+X G -29128
+*X Q -29128
+X Q -29128
+Y e -87382
+Y o -87382
+Y r -87382
+Y a -87382
+Y *A -87382
+Y A -87382
+Y u -87382
+oq oq -87382
+oq ` -87382
+` oq -87382
+` ` -87382
+a v -29128
+a j 58254
+a y -29128
+a w -29128
+b e 29128
+b o 29128
+b x -29128
+b d 29128
+b c 29128
+b q 29128
+b v -29128
+b j 58254
+b y -29128
+b w -29128
+c h -29128
+c k -29128
+f ' 81557
+f ? 81557
+f ! 81557
+f ) 81557
+f rB 81557
+f ] 81557
+g j 29128
+h t -29128
+h u -29128
+h b -29128
+h y -29128
+h v -29128
+h w -29128
+k a -58254
+k e -29128
+k a -29128
+k o -29128
+k c -29128
+m t -29128
+m u -29128
+m b -29128
+m y -29128
+m v -29128
+m w -29128
+n t -29128
+n u -29128
+n b -29128
+n y -29128
+n v -29128
+n w -29128
+o e 29128
+o o 29128
+o x -29128
+o d 29128
+o c 29128
+o q 29128
+o v -29128
+o j 58254
+o y -29128
+o w -29128
+p e 29128
+p o 29128
+p x -29128
+p d 29128
+p c 29128
+p q 29128
+p v -29128
+p j 58254
+p y -29128
+p w -29128
+t y -29128
+t w -29128
+u w -29128
+v a -58254
+v e -29128
+v a -29128
+v o -29128
+v c -29128
+w e -29128
+w a -29128
+w o -29128
+w c -29128
+y o -29128
+y e -29128
+y a -29128
+y . -87382
+y , -87382
+charset
+*G 655362,716526,0,0,0,-145637 2 0000
+*D 873816,716526 2 0001
+*H 815562,716526,0,0,-29128,-29128 2 0002
+*L 728179,716526 2 0003
+*C 699053,716526 2 0004
+*P 786434,716526 2 0005
+*S 757307,716526 2 0006
+*U 815562,716526,0,0,-29128,-145637 2 0007
+*F 757307,716526,0,0,-29128 2 0010
+*Q 815562,716526,0,0,-29128,-72818 2 0011
+*W 757307,716526 2 0012
+ff 611672,728178,0,81557 2 0013
+fi 582544,728178 2 0014
+fl 582544,728178 2 0015
+Fi 873816,728178 2 0016
+Fl 873816,728178 2 0017
+.i 291272,451470 0 0020
+.j 320400,451470,203890 1 0021
+ga 524290,728178 2 0022
+char180 524290,728178 2 0023
+aa "
+ah 524290,659002 2 0024
+ab 524290,728178 2 0025
+char175 524290,595357 2 0026
+a- "
+ao 786434,728178 2 0027
+char184 466035,0,178403 1 0030
+ac "
+char223 524291,728178 2 0031
+ss "
+char230 757307,451470 0 0032
+ae "
+oe 815562,451470 0 0033
+char248 524290,553416,101946 3 0034
+/o "
+char198 946634,716526 2 0035
+AE "
+OE 1063142,716526 2 0036
+char216 815562,767499,50973 3 0037
+/O "
+--- 291272,451470 0 0040
+! 291272,728178 2 0041
+rq 524290,728178 2 0042
+sh 873816,728178,203888 3 0043
+# "
+Do 524290,786432,58254 3 0044
+$ "
+% 873816,786432,58254 3 0045
+& 815562,728178 2 0046
+' 291272,728178 2 0047
+( 407781,786432,262144 3 0050
+) 407781,786432,262144 3 0051
+* 524290,786432 2 0052
+pl 815562,611670,87382 3 0053
++ "
+, 291272,110683,203890 1 0054
+char173 349526,451470 0 0055
+hy "
+- "
+. 291272,110683 0 0056
+sl 524290,786432,262144 3 0057
+/ "
+0 524290,675749 2 0060
+1 524290,675749 2 0061
+2 524290,675749 2 0062
+3 524290,675749 2 0063
+4 524290,675749 2 0064
+5 524290,675749 2 0065
+6 524290,675749 2 0066
+7 524290,675749 2 0067
+8 524290,675749 2 0070
+9 524290,675749 2 0071
+: 291272,451470 0 0072
+; 291272,451470,203890 1 0073
+char161 291272,524288,203890 3 0074
+r! "
+eq 815562,384696,-139592 0 0075
+= "
+char191 495163,524288,203890 3 0076
+r? "
+? 495163,728178 2 0077
+at 815562,728178 2 0100
+@ "
+*A 786434,716526 2 0101
+A "
+*B 742744,716526 2 0102
+B "
+C 757307,716526,0,0,-29128 2 0103
+D 800998,716526,0,0,0,-29128 2 0104
+*E 713616,716526 2 0105
+E "
+F 684490,716526,0,0,0,-145637 2 0106
+G 822843,716526,0,0,-29128 2 0107
+*Y 786434,716526 2 0110
+H "
+*I 378653,716526 2 0111
+I "
+J 538853,716526,0,0,0,-29128 2 0112
+*K 815562,716526 2 0113
+K "
+L 655362,716526,0,0,0,58254 2 0114
+*M 961197,716526 2 0115
+M "
+*N 786434,716526 2 0116
+N "
+*O 815562,716526,0,0,-29128,-29128 2 0117
+O "
+*R 713616,716526,0,0,0,-145637 2 0120
+P "
+Q 815562,716526,203890,0,-29128 3 0121
+R 771870,716526 2 0122
+S 582544,716526 2 0123
+*T 757307,716526,0,0,0,-145637 2 0124
+T "
+U 786434,716526,0,0,-14563 2 0125
+V 786434,716526,0,14563,43691,-218454 2 0126
+W 1077706,716526,0,14563,43691,-131074 2 0127
+*X 786434,716526 2 0130
+X "
+Y 786434,716526,0,26214,29128,-206803 2 0131
+*Z 640798,716526 2 0132
+Z "
+lB 291272,786432,262144 3 0133
+[ "
+lq 524290,728178 2 0134
+rB 291272,786432,262144 3 0135
+] "
+ha 524290,728178 2 0136
+^ "
+a^ "
+a. 291272,700301 2 0137
+oq 291272,728178 2 0140
+` "
+a 524290,451470 0 0141
+b 582544,728178 2 0142
+c 466035,451470 0 0143
+d 582544,728178 2 0144
+e 466035,451470 0 0145
+f 320400,728178,0,81557 2 0146
+g 524290,451470,203890,14563 1 0147
+h 582544,728178 2 0150
+i 291272,700301 2 0151
+j 320400,700301,203890 3 0152
+k 553418,728178 2 0153
+l 291272,728178 2 0154
+m 873816,451470 0 0155
+n 582544,451470 0 0156
+o 524290,451470 0 0157
+p 582544,451470,203890 1 0160
+q 553416,451470,203890 1 0161
+r 410694,451470 0 0162
+s 413606,451470 0 0163
+t 407781,644958 2 0164
+u 582544,451470 0 0165
+v 553418,451470,0,14563 0 0166
+w 757307,451470,0,14563 0 0167
+x 553418,451470 0 0170
+y 553418,451470,203890,14563 1 0171
+z 466035,451470 0 0172
+en 524290,451470,0,29128 0 0173
+em 1048579,451470,0,29128 0 0174
+a" 524290,728178 2 0175
+~ 524290,700301 2 0176
+a~ "
+char168 524290,700301 2 0177
+ad "
diff --git a/dvi/devdvi/S b/dvi/devdvi/S
new file mode 100644
index 000000000..11b79ff9c
--- /dev/null
+++ b/dvi/devdvi/S
@@ -0,0 +1,152 @@
+name S
+special
+internalname cmsy10
+slant 14.036243
+checksum 555887770
+designsize 10485760
+charset
+mi 815562,611670,87382 3 0000
+\- "
+md 291272,466035,-58253 2 0001
+char215 815562,611670,87382 3 0002
+mu "
+** 524290,487880,-36408 2 0003
+char247 815562,611670,87382 3 0004
+di "
+--- 524290,466035,-58253 2 0005
+char177 815562,611670,87382 3 0006
++- "
+-+ 815562,611670,87382 3 0007
+c+ 815562,611670,87382 3 0010
+--- 815562,611670,87382 3 0011
+c* 815562,611670,87382 3 0012
+--- 815562,611670,87382 3 0013
+--- 815562,611670,87382 3 0014
+ci 1048579,728178,203890 3 0015
+--- 524290,466035,-58253 2 0016
+bu 524290,466035,-58253 2 0017
+--- 815562,486275,-38013 2 0020
+== 815562,486275,-38013 2 0021
+ib 815562,666864,142576 3 0022
+ip 815562,666864,142576 3 0023
+<= 815562,666864,142576 3 0024
+>= 815562,666864,142576 3 0025
+--- 815562,666864,142576 3 0026
+--- 815562,666864,142576 3 0027
+ti 815562,384696,-139592 0 0030
+ap "
+~~ 815562,506590,-17698 2 0031
+sb 815562,565285,40997 3 0032
+sp 815562,565285,40997 3 0033
+<< 1048579,565285,40997 3 0034
+>> 1048579,565285,40997 3 0035
+--- 815562,565285,40997 3 0036
+--- 815562,565285,40997 3 0037
+<- 1048579,384696,-139592 0 0040
+-> 1048579,384696,-139592 0 0041
+ua 524290,728178,203888 3 0042
+da 524290,728178,203888 3 0043
+<> 1048579,384696,-139592 0 0044
+--- 1048579,728178,203888 3 0045
+--- 1048579,728178,203888 3 0046
+~= 815562,486275,-38013 2 0047
+lh 1048579,384696,-139592 0 0050
+lA "
+rh 1048579,384696,-139592 0 0051
+rA "
+uA 640798,728178,203888 3 0052
+dA 640798,728178,203888 3 0053
+hA 1048579,384696,-139592 0 0054
+--- 1048579,728178,203888 3 0055
+--- 1048579,728178,203888 3 0056
+pt 815562,451470 0 0057
+prime 288358,582544 2 0060
+if 1048579,451470 0 0061
+mo 699053,565285,40997 3 0062
+st 699053,565285,40997 3 0063
+--- 932070,728178,203890 3 0064
+--- 932070,728178,203890 3 0065
+slashnot 0,728178,203888 3 0066
+--- 0,384696,-139592 0 0067
+fa 582544,728178 2 0070
+te 582544,728178 2 0071
+char172 699053,451470 0 0072
+no "
+es 524290,786432,58254 3 0073
+Re 757307,728178 2 0074
+Im 757307,728178 2 0075
+--- 815562,728178 2 0076
+pp 815562,728178 2 0077
+Ah 640798,728178 2 0100
+A 837258,716526 2 0101
+B 688715,716526,0,31890 2 0102
+C 552106,716526,0,61170 2 0103
+D 808864,716526,0,29128 2 0104
+E 553419,716526,0,93786 2 0105
+F 753662,716526,0,104130 2 0106
+G 623762,716526,101946,62184 3 0107
+H 885541,716526,0,10123 2 0110
+I 570966,716526,0,77408 2 0111
+J 710704,716526,101946,193694 3 0112
+K 798963,716526,0,15147 2 0113
+L 723229,716526 2 0114
+M 1259235,716526 2 0115
+N 860347,716526,0,154518 2 0116
+O 834786,716526,0,29128 2 0117
+P 729347,716526,0,86216 2 0120
+Q 856341,716526,101946 3 0121
+R 888672,716526 2 0122
+S 634974,716526,0,78638 2 0123
+T 571101,716526,0,266514 2 0124
+U 656232,716526,0,104130 2 0125
+V 642549,716526,0,86216 2 0126
+W 1035766,716526,0,86216 2 0127
+X 747946,716526,0,153541 2 0130
+Y 700802,716526,101946,86216 3 0131
+Z 759930,716526,0,83302 2 0132
+cu 699053,582544 2 0133
+ca 699053,582544 2 0134
+--- 699053,582544 2 0135
+AN 699053,582544 2 0136
+OR 699053,582544 2 0137
+--- 640798,728178 2 0140
+--- 640798,728178 2 0141
+lf 466035,786432,262144 3 0142
+rf 466035,786432,262144 3 0143
+lc 466035,786432,262144 3 0144
+rc 466035,786432,262144 3 0145
+{ 524290,786432,262144 3 0146
+lC "
+} 524290,786432,262144 3 0147
+rC "
+la 407781,786432,262144 3 0150
+ra 407781,786432,262144 3 0151
+bar 291272,786432,262144 3 0152
+or "
+bv "
+| "
+ba "
+bardbl 524290,786432,262144 3 0153
+va 524290,786432,262144 3 0154
+vA 640798,786432,262144 3 0155
+rs 524290,786432,262144 3 0156
+\ "
+--- 291272,728178,203888 3 0157
+sr 873816,41942,1006634 1 0160
+--- 786434,716526 2 0161
+gr 873816,716526 2 0162
+--- 436909,728178,203890,116509 3 0163
+--- 699053,582544 2 0164
+--- 699053,582544 2 0165
+--- 815562,666864,142576 3 0166
+--- 815562,666864,142576 3 0167
+char167 466037,728178,203890 3 0170
+sc "
+dg 466035,728178,203890 3 0171
+dd 466035,728178,203890 3 0172
+char182 640798,728178,203890 3 0173
+ps "
+CL 815562,728178,135926 3 0174
+DI 815562,728178,135926 3 0175
+HE 815562,728178,135926 3 0176
+SP 815562,728178,135926 3 0177
diff --git a/dvi/devdvi/eqnchar b/dvi/devdvi/eqnchar
new file mode 100644
index 000000000..6fa442566
--- /dev/null
+++ b/dvi/devdvi/eqnchar
@@ -0,0 +1,35 @@
+.EQ
+sdefine << %{ < back 20 < }%
+sdefine >> %{ > back 20 > }%
+
+sdefine dot %accent "\fR\(a.\fP"%
+sdefine dotdot %accent "\fR\(ad\fP"%
+sdefine vec %accent {up 52 "\s[\En[.s]/2u]\(->\s0"}%
+sdefine dyad %accent {up 52 "\s[\En[.s]/2u]\(<>\s0"}%
+
+sdefine int %{type "operator" vcenter \(is}%
+sdefine sum %{type "operator" vcenter \[sum]}%
+sdefine prod %{type "operator" vcenter \[product]}%
+sdefine coprod %{type "operator" vcenter \[coproduct]}%
+
+sdefine cdot %type "binary" \(md%
+
+set num1 68
+set num2 39
+set denom1 69
+set denom2 34
+set sup1 41
+set sup2 36
+set sup3 29
+set sup_drop 39
+set sub_drop 5
+set axis_height 25
+set x_height 43
+set default_rule_thickness 4
+set big_op_spacing1 11
+set big_op_spacing2 16
+set big_op_spacing3 20
+set big_op_spacing4 60
+set big_op_spacing5 10
+
+.EN
diff --git a/dvi/devdvi/texb.map b/dvi/devdvi/texb.map
new file mode 100644
index 000000000..67008339e
--- /dev/null
+++ b/dvi/devdvi/texb.map
@@ -0,0 +1,127 @@
+0 *G
+1 *D
+2 *H
+3 *L
+4 *C
+5 *P
+6 *S
+7 *U
+8 *F
+9 *Q
+10 *W
+11 ff
+12 fi
+13 fl
+14 Fi
+15 Fl
+16 .i
+17 .j
+18 ga
+19 aa char180
+20 ah
+21 ab
+22 a- char175
+23 ao
+24 ac char184
+25 ss char223
+26 ae char230
+27 oe
+28 /o char248
+29 AE char198
+30 OE
+31 /O char216
+33 !
+34 rq
+35 # sh
+36 $ Do
+37 %
+38 &
+39 '
+40 (
+41 )
+42 *
+43 +
+44 ,
+45 - hy char173
+46 .
+47 / sl
+48 0
+49 1
+50 2
+51 3
+52 4
+53 5
+54 6
+55 7
+56 8
+57 9
+58 :
+59 ;
+60 r! char161
+61 =
+62 r? char191
+63 ?
+64 @ at
+65 A
+66 B
+67 C
+68 D
+69 E
+70 F
+71 G
+72 H
+73 I
+74 J
+75 K
+76 L
+77 M
+78 N
+79 O
+80 P
+81 Q
+82 R
+83 S
+84 T
+85 U
+86 V
+87 W
+88 X
+89 Y
+90 Z
+91 [ lB
+92 lq
+93 ] rB
+94 a^ ^ ha
+95 a.
+96 ` oq
+97 a
+98 b
+99 c
+100 d
+101 e
+102 f
+103 g
+104 h
+105 i
+106 j
+107 k
+108 l
+109 m
+110 n
+111 o
+112 p
+113 q
+114 r
+115 s
+116 t
+117 u
+118 v
+119 w
+120 x
+121 y
+122 z
+123 en
+124 em
+125 a"
+126 a~ ~
+127 ad char168
diff --git a/dvi/devdvi/texex.map b/dvi/devdvi/texex.map
new file mode 100644
index 000000000..a5b26901d
--- /dev/null
+++ b/dvi/devdvi/texex.map
@@ -0,0 +1,100 @@
+0 parenleft0
+1 parenright0
+2 bracketleft0
+3 bracketright0
+4 floorleft0
+5 floorright0
+6 ceilingleft0
+7 ceilingright0
+8 braceleft0
+9 braceright0
+10 angleleft0
+11 angleright0
+12 barex
+13 bardblex
+14 slash0
+15 backslash0
+16 parenleft1
+17 parenright1
+18 parenleft2
+19 parenright2
+20 bracketleft2
+21 bracketright2
+22 floorleft2
+23 floorright2
+24 ceilingleft2
+25 ceilingright2
+26 braceleft2
+27 braceright2
+28 angleleft2
+29 angleright2
+30 slash2
+31 backslash2
+32 parenleft3
+33 parenright3
+34 bracketleft3
+35 bracketright3
+36 floorleft3
+37 floorright3
+38 ceilingleft3
+39 ceilingright3
+40 braceleft3
+41 braceright3
+42 angleleft3
+43 angleright3
+44 slash3
+45 backslash3
+46 slash1
+47 backslash1
+48 parenlefttp
+49 parenrighttp
+50 bracketlefttp
+51 bracketrighttp
+52 bracketleftbt
+53 bracketrightbt
+54 bracketleftex
+55 bracketrightex
+56 bracelefttp lt
+57 bracerighttp rt
+58 braceleftbt lb
+59 bracerightbt rb
+60 braceleftmid lk
+61 bracerightmid rk
+62 braceex bracerightex braceleftex
+63 arrowvertex
+64 parenleftbt
+65 parenrightbt
+66 parenleftex
+67 parenrightex
+68 angleleft1
+69 angleright1
+73 ois ointegral
+75 bigcircledot
+77 bigcircleplus
+79 bigcirclemultiply
+88 sum
+89 product
+90 is integral
+91 bigunion
+92 bigintersection
+93 bigunionplus
+94 biglogicaland
+95 biglogicalor
+97 coproduct
+104 bracketleft1
+105 bracketright1
+106 floorleft1
+107 floorright1
+108 ceilingleft1
+109 ceilingright1
+110 braceleft1
+111 braceright1
+112 sr0
+113 sr1
+114 sr2
+115 sr3
+119 arrowvertdblex
+120 arrowverttp
+121 arrowvertbt
+126 arrowvertdbltp
+127 arrowvertdblbt
diff --git a/dvi/devdvi/texi.map b/dvi/devdvi/texi.map
new file mode 100644
index 000000000..d881e5f18
--- /dev/null
+++ b/dvi/devdvi/texi.map
@@ -0,0 +1,127 @@
+0 *G
+1 *D
+2 *H
+3 *L
+4 *C
+5 *P
+6 *S
+7 *U
+8 *F
+9 *Q
+10 *W
+11 ff
+12 fi
+13 fl
+14 Fi
+15 Fl
+16 .i
+17 .j
+18 ga
+19 aa char180
+20 ah
+21 ab
+22 a- char175
+23 ao
+24 ac char184
+25 ss char223
+26 ae char230
+27 oe
+28 /o char248
+29 AE char198
+30 OE
+31 /O char216
+33 !
+34 rq
+35 # sh
+36 Po char163
+37 %
+38 &
+39 '
+40 (
+41 )
+42 *
+43 +
+44 ,
+45 - hy char173
+46 .
+47 / sl
+48 0
+49 1
+50 2
+51 3
+52 4
+53 5
+54 6
+55 7
+56 8
+57 9
+58 :
+59 ;
+60 r! char161
+61 =
+62 r? char191
+63 ?
+64 @ at
+65 A
+66 B
+67 C
+68 D
+69 E
+70 F
+71 G
+72 H
+73 I
+74 J
+75 K
+76 L
+77 M
+78 N
+79 O
+80 P
+81 Q
+82 R
+83 S
+84 T
+85 U
+86 V
+87 W
+88 X
+89 Y
+90 Z
+91 [ lB
+92 lq
+93 ] rB
+94 a^ ^ ha
+95 a.
+96 ` oq
+97 a
+98 b
+99 c
+100 d
+101 e
+102 f
+103 g
+104 h
+105 i
+106 j
+107 k
+108 l
+109 m
+110 n
+111 o
+112 p
+113 q
+114 r
+115 s
+116 t
+117 u
+118 v
+119 w
+120 x
+121 y
+122 z
+123 en
+124 em
+125 a"
+126 a~ ~
+127 ad char168
diff --git a/dvi/devdvi/texmi.map b/dvi/devdvi/texmi.map
new file mode 100644
index 000000000..ee64fece8
--- /dev/null
+++ b/dvi/devdvi/texmi.map
@@ -0,0 +1,29 @@
+11 *a
+12 *b
+13 *g
+14 *d
+15 *e
+16 *z
+17 *y
+18 *h
+19 *i
+20 *k
+21 *l
+22 *m char181
+23 *n
+24 *c
+25 *p
+26 *r
+27 *s
+28 *t
+29 *u
+30 *f
+31 *x
+32 *q
+33 *w
+38 ts
+60 <
+62 >
+64 pd
+111 *o
+125 wp
diff --git a/dvi/devdvi/texr.map b/dvi/devdvi/texr.map
new file mode 100644
index 000000000..bebf67b80
--- /dev/null
+++ b/dvi/devdvi/texr.map
@@ -0,0 +1,127 @@
+0 *G
+1 *D
+2 *H
+3 *L
+4 *C
+5 *P
+6 *S
+7 *U
+8 *F
+9 *Q
+10 *W
+11 ff
+12 fi
+13 fl
+14 Fi
+15 Fl
+16 .i
+17 .j
+18 ga
+19 aa char180
+20 ah
+21 ab
+22 a- char175
+23 ao
+24 ac char184
+25 ss char223
+26 ae char230
+27 oe
+28 /o char248
+29 AE char198
+30 OE
+31 /O char216
+33 !
+34 rq
+35 # sh
+36 $ Do
+37 %
+38 &
+39 '
+40 (
+41 )
+42 *
+43 + pl
+44 ,
+45 - hy char173
+46 .
+47 / sl
+48 0
+49 1
+50 2
+51 3
+52 4
+53 5
+54 6
+55 7
+56 8
+57 9
+58 :
+59 ;
+60 r! char161
+61 = eq
+62 r? char191
+63 ?
+64 @ at
+65 A *A
+66 B *B
+67 C
+68 D
+69 E *E
+70 F
+71 G
+72 H *Y
+73 I *I
+74 J
+75 K *K
+76 L
+77 M *M
+78 N *N
+79 O *O
+80 P *R
+81 Q
+82 R
+83 S
+84 T *T
+85 U
+86 V
+87 W
+88 X *X
+89 Y
+90 Z *Z
+91 [ lB
+92 lq
+93 ] rB
+94 a^ ^ ha
+95 a.
+96 ` oq
+97 a
+98 b
+99 c
+100 d
+101 e
+102 f
+103 g
+104 h
+105 i
+106 j
+107 k
+108 l
+109 m
+110 n
+111 o
+112 p
+113 q
+114 r
+115 s
+116 t
+117 u
+118 v
+119 w
+120 x
+121 y
+122 z
+123 en
+124 em
+125 a"
+126 a~ ~
+127 ad char168
diff --git a/dvi/devdvi/texsy.map b/dvi/devdvi/texsy.map
new file mode 100644
index 000000000..2c970bc2a
--- /dev/null
+++ b/dvi/devdvi/texsy.map
@@ -0,0 +1,100 @@
+0 \- mi
+1 md
+2 mu char215
+3 **
+4 di char247
+6 +- char177
+7 -+
+8 c+
+10 c*
+13 ci
+15 bu
+17 ==
+18 ib
+19 ip
+20 <=
+21 >=
+24 ap ti
+25 ~~
+26 sb
+27 sp
+28 <<
+29 >>
+32 <-
+33 ->
+34 ua
+35 da
+36 <>
+39 ~=
+40 lA lh
+41 rA rh
+42 uA
+43 dA
+44 hA
+47 pt
+48 prime
+49 if
+50 mo
+51 st
+54 slashnot
+56 fa
+57 te
+58 no char172
+59 es
+60 Re
+61 Im
+63 pp
+64 Ah
+65 A
+66 B
+67 C
+68 D
+69 E
+70 F
+71 G
+72 H
+73 I
+74 J
+75 K
+76 L
+77 M
+78 N
+79 O
+80 P
+81 Q
+82 R
+83 S
+84 T
+85 U
+86 V
+87 W
+88 X
+89 Y
+90 Z
+91 cu
+92 ca
+94 AN
+95 OR
+98 lf
+99 rf
+100 lc
+101 rc
+102 lC {
+103 rC }
+104 la
+105 ra
+106 ba | bv or bar
+107 bardbl
+108 va
+109 vA
+110 \ rs
+112 sr
+114 gr
+120 sc char167
+121 dg
+122 dd
+123 ps char182
+124 CL
+125 DI
+126 HE
+127 SP
diff --git a/dvi/devdvi/textt.map b/dvi/devdvi/textt.map
new file mode 100644
index 000000000..ce1185dd9
--- /dev/null
+++ b/dvi/devdvi/textt.map
@@ -0,0 +1,126 @@
+0 *G
+1 *D
+2 *H
+3 *L
+4 *C
+5 *P
+6 *S
+7 *U
+8 *F
+9 *Q
+10 *W
+11 ff
+12 fi
+13 fl
+14 Fi
+15 Fl
+16 .i
+17 .j
+18 ga
+19 aa char180
+20 ah
+21 ab
+22 a- char175
+23 ao
+24 ac char184
+25 ss char223
+26 ae char230
+27 oe
+28 /o char248
+29 AE char198
+30 OE
+31 /O char216
+33 !
+34 "
+35 # sh
+36 $ Do
+37 %
+38 &
+39 '
+40 (
+41 )
+42 *
+43 +
+44 ,
+45 \-
+46 .
+47 / sl
+48 0
+49 1
+50 2
+51 3
+52 4
+53 5
+54 6
+55 7
+56 8
+57 9
+58 :
+59 ;
+60 <
+61 =
+62 >
+63 ?
+64 @ at
+65 A
+66 B
+67 C
+68 D
+69 E
+70 F
+71 G
+72 H
+73 I
+74 J
+75 K
+76 L
+77 M
+78 N
+79 O
+80 P
+81 Q
+82 R
+83 S
+84 T
+85 U
+86 V
+87 W
+88 X
+89 Y
+90 Z
+91 [ lB
+92 \ rs
+93 ] rB
+94 a^ ^ ha
+95 _
+96 ` oq
+97 a
+98 b
+99 c
+100 d
+101 e
+102 f
+103 g
+104 h
+105 i
+106 j
+107 k
+108 l
+109 m
+110 n
+111 o
+112 p
+113 q
+114 r
+115 s
+116 t
+117 u
+118 v
+119 w
+120 x
+121 y
+122 z
+123 lC {
+124 ba |
+125 rC }
+126 a~ ~
diff --git a/dvi/dvi.c b/dvi/dvi.c
new file mode 100644
index 000000000..e106fe682
--- /dev/null
+++ b/dvi/dvi.c
@@ -0,0 +1,895 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "driver.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(int argc, const char **argv);
+ 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(int argc, const char **argv)
+{
+ char *ptr;
+ if (strcmp(argv[0], "checksum") == 0) {
+ if (argc != 2)
+ fatal("`checksum' command requires exactly 1 argument");
+ checksum = int(strtol(argv[1], &ptr, 10));
+ if (checksum == 0 && ptr == argv[1]) {
+ fatal("bad checksum");
+ }
+ }
+ else if (strcmp(argv[0], "designsize") == 0) {
+ if (argc != 2)
+ fatal("`designsize' command requires exactly 1 argument");
+ design_size = int(strtol(argv[1], &ptr, 10));
+ if (design_size == 0 && ptr == argv[1]) {
+ fatal("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();
+ void set_char(int, font *, const environment *, int w);
+ void special(char *arg, const environment *env);
+ 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();
+};
+
+dvi_printer::dvi_printer()
+: byte_count(0), last_bop(-1), page_count(0), cur_font(0), fp(stdout),
+ max_h(0), max_v(0), 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)
+{
+ int code = f->get_code(index);
+ if (env->size != cur_point_size || f != cur_font) {
+ cur_font = f;
+ cur_point_size = env->size;
+ for (int 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()
+{
+ if (pushed)
+ end_of_line();
+ out1(eop);
+ cur_font = 0;
+}
+
+void draw_dvi_printer::end_page()
+{
+ dvi_printer::end_page();
+ 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)
+{
+ 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 = 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);
+ int ch = p[0];
+ int cv = p[1];
+ int eh = p[0] + p[2];
+ int ev = p[1] + p[3];
+ double n = (double(ch)*eh) + (double(cv)*ev);
+ if (n == 0) {
+ moveto(env->hpos, env->vpos);
+ do_special("pa 0 0");
+ sprintf(buf, "pa %d %d", milliinches(eh), milliinches(ev));
+ do_special(buf);
+ do_special("fp");
+ }
+ else {
+ double k = (double(eh)*eh + double(ev)*ev)/(2.0*n);
+ double h = k*ch;
+ double v = k*cv;
+ double start_angle = atan2(-v, -h);
+ double end_angle = atan2(ev - v, eh - h);
+ int rad = milliinches(int(sqrt(h*h + v*v) + .5));
+ moveto(env->hpos + int(h), env->vpos + int(v));
+ sprintf(buf, "ar 0 0 %d %d %f %f",
+ rad,
+ rad,
+ end_angle,
+ start_angle);
+ do_special(buf);
+ }
+ 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();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ while ((c = getopt(argc, argv, "F:vw:d")) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "grodvi version %s\n", version_string);
+ fflush(stderr);
+ 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 '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ exit(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
+ program_name);
+ exit(1);
+}
diff --git a/dvi/tfmtodit.c b/dvi/tfmtodit.c
new file mode 100644
index 000000000..b2f4b56c4
--- /dev/null
+++ b/dvi/tfmtodit.c
@@ -0,0 +1,845 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "assert.h"
+#include "cset.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), i(-1), c(t->bc)
+{
+}
+
+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()
+{
+ delete char_info;
+ delete width;
+ delete height;
+ delete depth;
+ delete italic;
+ delete lig_kern;
+ delete kern;
+ 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)
+{
+ FILE *fp = fopen(file, "r");
+ 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);
+ delete buf;
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ if (lf < 6) {
+ error("bad tfm file `%1': impossibly short", file);
+ 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);
+ delete buf;
+ return 0;
+ }
+ if (lh < 2) {
+ error("bad tfm file `%1': header too short", file);
+ 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);
+ 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;
+ FILE *fp = fopen(file, "r");
+ 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)
+{
+ 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();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int special_flag = 0;
+ int skewchar = -1;
+ int opt;
+ const char *gf_file = 0;
+ while ((opt = getopt(argc, argv, "svg:k:")) != 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;
+ fprintf(stderr, "tfmtodit version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case '?':
+ usage();
+ break;
+ case EOF:
+ assert(0);
+ }
+ if (argc - optind != 3)
+ usage();
+ 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;
+ 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';
+ char *s = strrchr(internal_name, '/');
+ 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/M_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);
+ for (int j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
+ if (m[j] != 0)
+ break;
+ for (int 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()
+{
+ fprintf(stderr, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
+ program_name);
+ exit(1);
+}
diff --git a/dvi/tmac.dvi b/dvi/tmac.dvi
new file mode 100644
index 000000000..213e04da1
--- /dev/null
+++ b/dvi/tmac.dvi
@@ -0,0 +1,38 @@
+.nr _C \n(.C
+.cp 0
+.ftr CR CW
+.ftr C CW
+.ftr TT CW
+.ftr HR H
+.\" We need short names for \[prime] and \[slashnot].
+.tr \[FM]\[prime]
+.tr \[!/]\[slashnot]
+.\" Do it like this to avoid rounding problems.
+.char \[ul] \v'.23m'\D'R .5m .04m'\v'-.04m'\v'-.23m'
+.char _ \v'.23m'\D'R .5m .04m'\v'-.04m'\v'-.23m'
+.char \[rn] \D'R .5m -.04m'\v'.04m'
+.char \[br] \Z'\v'.25m'\D'R .04m -1m''
+.char \[ru] \v'-.02m'\D'R .5m .04m'\v'-.04m'\v'.02m'
+.char \[co] \z\(ci\h'\w'\(ci'u-\w'c'u/2u'c\h'\w'\(ci'u-\w'c'u/2u'
+.char \[rg] \z\(ci\h'\w'\(ci'u-\w'r'u/2u'r\h'\w'\(ci'u-\w'r'u/2u'
+.char \[fm] \v'-.35m'\s[\\n(.s*7u/10u]\(FM\s0\v'.35m'
+.char \[de] \h'.05m'\v'-.54m'\D'c .3m'\v'.54m'\h'.05m'
+.char \[ct] \o'c/'
+.char \[sq] \Z'\h'.05m'\D'R .4m -.04m'\v'.04m'\h'-.04m'\
+\D'R .04m -.4m'\v'.04m'\D'R -.4m -.04m'\D'R .04m .4m''\h'.5m'
+.\"char \[sq] \h'.05m'\D'l .4m 0'\D'l 0 -.4m'\D'l -.4m 0'\D'l 0 .4m'\h'.45m'
+.char \[!=] \(!/\(eq
+.char \[tm] \v'-.3m'\s[\\n(.s/2u]TM\s0\v'.3m'
+.\" Define some fractions.
+.de dvi-frac
+.char \[\\$1\\$2] \v'-.25m'\s[\\\\n(.s*7u/10u]\\$1\s0\v'.25m'\h'-.2m'\
+/\h'-.2m'\v'.25m'\s[\\\\n(.s*7u/10u]\\$2\s0\v'-.25m'
+..
+.dvi-frac 1 2
+.dvi-frac 3 4
+.dvi-frac 1 4
+.dvi-frac 1 8
+.dvi-frac 3 8
+.dvi-frac 5 8
+.dvi-frac 7 8
+.cp \n(_C
diff --git a/eqn/Makefile b/eqn/Makefile
new file mode 100644
index 000000000..a81c60798
--- /dev/null
+++ b/eqn/Makefile
@@ -0,0 +1,91 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+# default device
+DEVICE=ps
+CFLAGS=-g -O -Wall
+CC=g++
+INCLUDES=-I../lib
+YACC=bison -y
+YACCFLAGS=-v
+ETAGS=etags
+ETAGSFLAGS=-p
+OBJECTS=eqn.tab.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
+SOURCES=main.c lex.c eqn.y box.c limit.c list.c over.c text.c \
+ script.c mark.c other.c delim.c sqrt.c pile.c special.c \
+ eqn.h box.h pbox.h
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: eqn
+
+eqn: $(OBJECTS) ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(LIBS)
+
+eqn.tab.c: eqn.y
+ $(YACC) $(YACCFLAGS) -d eqn.y
+ mv y.tab.c eqn.tab.c
+ mv y.tab.h eqn.tab.h
+
+eqn.tab.o: eqn.h box.h
+box.o: eqn.h box.h pbox.h
+limit.o: eqn.h box.h pbox.h
+text.o: eqn.h box.h pbox.h
+over.o: eqn.h box.h pbox.h
+list.o: eqn.h box.h pbox.h
+script.o: eqn.h box.h pbox.h
+mark.o: eqn.h box.h pbox.h
+other.o: eqn.h box.h pbox.h
+delim.o: eqn.h box.h pbox.h
+sqrt.o: eqn.h box.h pbox.h
+pile.o: eqn.h box.h pbox.h
+special.o: eqn.h box.h pbox.h
+main.o: device.h eqn.h box.h ../lib/stringclass.h
+lex.o: eqn.h eqn.tab.c box.h ../lib/stringclass.h
+
+device.h: FORCE
+ @echo \#define DEVICE \"$(DEVICE)\" >device.h.n
+ @cmp -s device.h device.h.n || cp device.h.n device.h
+ @rm -f device.h.n
+
+install.bin: eqn
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/geqn
+ cp eqn $(BINDIR)/geqn
+
+install.nobin:
+
+install: install.bin install.nobin
+
+TAGS: $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o core eqn gmon.out device.h
+
+distclean: clean
+ -rm -f TAGS eqn.output y.output
+
+realclean: distclean
+ -rm -f eqn.tab.c eqn.tab.h
+
+FORCE:
diff --git a/eqn/NOTES b/eqn/NOTES
new file mode 100644
index 000000000..3ccbd4ca5
--- /dev/null
+++ b/eqn/NOTES
@@ -0,0 +1,25 @@
+Differences between textstyle and displaystyle
+
+superscripts are a little higher in displaystyle
+(does not apply to cramped variants)
+
+operators are bigger in ds
+[not done automatically]
+
+more separation between square-root bar and top of contents
+in displaystyle
+
+fractions are different: in textstyle change to script style;
+also num and denom positions different; also minimum
+separation between bar and contents different.
+[dealt with by smallover]
+
+
+ D D' T T' S S'
+sup sup1 sup3 sup2 sup3 sup2 sup3
+sroot xh xh rt rt rt rt
+space yes yes yes yes no no
+
+If we make text-style--->display-style,
+
+too much separation square roots
diff --git a/eqn/TODO b/eqn/TODO
new file mode 100644
index 000000000..7d0deb54c
--- /dev/null
+++ b/eqn/TODO
@@ -0,0 +1,39 @@
+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 shuld 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.
diff --git a/eqn/box.c b/eqn/box.c
new file mode 100644
index 000000000..6178cf3f3
--- /dev/null
+++ b/eqn/box.c
@@ -0,0 +1,565 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 = 75;
+int body_depth = 25;
+
+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
+
+
+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,
+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;
+}
+
+void set_gsize(int n)
+{
+ gsize = n;
+}
+
+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)
+{
+ delete gfont;
+ gfont = strsave(s);
+}
+
+void set_grfont(const char *s)
+{
+ delete grfont;
+ grfont = strsave(s);
+}
+
+void set_gbfont(const char *s)
+{
+ 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() : uid(next_uid++), spacing_type(ORDINARY_TYPE)
+{
+}
+
+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());
+ if (gsize > 0)
+ b = new size_box(strsave(itoa(gsize)), 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.
+ printf(".ds " RESTORE_FONT_STRING " "
+ "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]"
+ "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]\n");
+ printf(".as " LINE_STRING " "
+ "\\R'" SAVED_INLINE_FONT_REG " \\En[.f]'"
+ "\\fP"
+ "\\R'" SAVED_INLINE_PREV_FONT_REG " \\En[.f]'");
+ printf("\\f[%s]", get_gfont());
+ 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()
+{
+ if (positive_space >= 0 || negative_space >= 0) {
+ if (positive_space > 0)
+ printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
+ ".as " LINE_STRING " \\x'-%dM'\n", positive_space);
+ if (negative_space > 0)
+ printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
+ ".as " LINE_STRING " \\x'%dM'\n", negative_space);
+ positive_space = negative_space = -1;
+ }
+ else {
+ printf(".if !r" 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 !r" 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);
+ delete oldp;
+ }
+ p[len++] = pp;
+}
+
+box_list::~box_list()
+{
+ for (int i = 0; i < len; i++)
+ delete p[i];
+ 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()
+{
+ 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/eqn/box.h b/eqn/box.h
new file mode 100644
index 000000000..aeaf5e46b
--- /dev/null
+++ b/eqn/box.h
@@ -0,0 +1,277 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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);
+void set_gsize(int);
+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/eqn/delim.c b/eqn/delim.c
new file mode 100644
index 000000000..634e95633
--- /dev/null
+++ b/eqn/delim.c
@@ -0,0 +1,356 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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]",
+ },
+};
+
+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') {
+ delete l;
+ l = 0;
+ }
+ if (r != 0 && *r == '\0') {
+ 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()
+{
+ delete left;
+ 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;
+ for (int i = 0; i < DELIM_TABLE_SIZE; i++, d++)
+ if (strcmp(delim, d->name) == 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);
+ 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()
+{
+ 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);
+ p->debug_print();
+ fprintf(stderr, " }");
+ if (right)
+ fprintf(stderr, " right \"%s\"", right);
+}
+
diff --git a/eqn/eqn.h b/eqn/eqn.h
new file mode 100644
index 000000000..ceff89d5f
--- /dev/null
+++ b/eqn/eqn.h
@@ -0,0 +1,49 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.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;
+
+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/eqn/eqn.tab.c b/eqn/eqn.tab.c
new file mode 100644
index 000000000..eeb58029a
--- /dev/null
+++ b/eqn/eqn.tab.c
@@ -0,0 +1,1219 @@
+
+/* A Bison parser, made from eqn.y */
+
+#define OVER 258
+#define SMALLOVER 259
+#define SQRT 260
+#define SUB 261
+#define SUP 262
+#define LPILE 263
+#define RPILE 264
+#define CPILE 265
+#define PILE 266
+#define LEFT 267
+#define RIGHT 268
+#define TO 269
+#define FROM 270
+#define SIZE 271
+#define FONT 272
+#define ROMAN 273
+#define BOLD 274
+#define ITALIC 275
+#define FAT 276
+#define ACCENT 277
+#define BAR 278
+#define UNDER 279
+#define ABOVE 280
+#define TEXT 281
+#define QUOTED_TEXT 282
+#define FWD 283
+#define BACK 284
+#define DOWN 285
+#define UP 286
+#define MATRIX 287
+#define COL 288
+#define LCOL 289
+#define RCOL 290
+#define CCOL 291
+#define MARK 292
+#define LINEUP 293
+#define TYPE 294
+#define VCENTER 295
+#define PRIME 296
+#define SPLIT 297
+#define NOSPLIT 298
+#define UACCENT 299
+#define SPECIAL 300
+#define SPACE 301
+#define GFONT 302
+#define GSIZE 303
+#define DEFINE 304
+#define NDEFINE 305
+#define TDEFINE 306
+#define SDEFINE 307
+#define UNDEF 308
+#define IFDEF 309
+#define INCLUDE 310
+#define DELIM 311
+#define CHARTYPE 312
+#define SET 313
+#define GRFONT 314
+#define GBFONT 315
+
+#line 19 "eqn.y"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "box.h"
+extern int non_empty_flag;
+char *strsave(const char *);
+#define YYDEBUG 1
+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;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __STDC__
+#define const
+#endif
+
+
+
+#define YYFINAL 142
+#define YYFLAG -32768
+#define YYNTBASE 66
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 315 ? yytranslate[x] : 83)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 63, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 61, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 64, 2, 65, 62, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60
+};
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 123, 125, 129, 132, 142, 145, 147, 151, 154, 156,
+ 158, 160, 164, 167, 169, 171, 175, 178, 182, 185,
+ 187, 191, 194, 196, 198, 200, 202, 204, 206, 208,
+ 210, 212, 214, 216, 218, 220, 222, 224, 226, 228,
+ 230, 232, 234, 236, 238, 240, 242, 244, 246, 248,
+ 250, 252, 254, 256, 260, 270, 273, 277, 280, 284,
+ 287, 291, 294, 298, 301, 305, 308, 310, 312, 316,
+ 318, 322, 325, 327
+};
+
+static const char * const yytname[] = { 0,
+"error","$illegal.","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",
+"'^'","'~'","'\\t'","'{'","'}'","top"
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 66, 66, 67, 67, 68, 68, 68, 69, 69, 69,
+ 69, 69, 70, 70, 70, 70, 71, 71, 72, 72,
+ 72, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 74, 75, 75, 76, 76, 77,
+ 77, 78, 78, 79, 79, 80, 80, 80, 80, 81,
+ 81, 82, 82, 82
+};
+
+static const short yyr2[] = { 0,
+ 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 const short yydefact[] = { 1,
+ 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,
+ 2, 3, 5, 8, 13, 17, 19, 14, 70, 71,
+ 0, 0, 31, 55, 32, 33, 30, 73, 74, 72,
+ 0, 0, 0, 42, 43, 44, 45, 0, 0, 0,
+ 0, 0, 6, 7, 0, 53, 24, 25, 0, 0,
+ 4, 0, 0, 0, 0, 0, 0, 0, 37, 38,
+ 39, 0, 56, 0, 0, 36, 47, 46, 48, 49,
+ 51, 50, 0, 0, 0, 0, 0, 60, 52, 54,
+ 29, 15, 16, 9, 10, 20, 19, 18, 40, 41,
+ 0, 58, 0, 0, 0, 0, 66, 67, 68, 69,
+ 34, 61, 0, 0, 0, 57, 59, 35, 62, 0,
+ 0, 11, 12, 21, 0, 64, 0, 63, 65, 0,
+ 0, 0
+};
+
+static const short yydefgoto[] = { 140,
+ 83, 32, 33, 34, 35, 36, 37, 42, 84, 43,
+ 97, 130, 117, 98, 44, 51
+};
+
+static const short yypact[] = { 228,
+ 267, 8, 8, 8, 8, 6, 68, 68, 306, 306,
+ 306, 306,-32768,-32768, 68, 68, 68, 68, -51, 228,
+ 228, 68, 306, -12, 5, 68,-32768,-32768,-32768, 228,
+ 228,-32768,-32768, 25,-32768,-32768, 58,-32768,-32768,-32768,
+ 228, -46,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ 228, 306, 306, 81, 81, 81, 81, 306, 306, 306,
+ 306, 83,-32768,-32768, 306, 81,-32768,-32768, 306, 128,
+-32768, 267, 267, 267, 267, 306, 306, 306,-32768,-32768,
+-32768, 306, 228, -9, 228, 189, 81, 81, 81, 81,
+ 81, 81, 11, 11, 11, 11, 12,-32768, 81, 81,
+-32768,-32768,-32768,-32768, 64,-32768, 333,-32768,-32768,-32768,
+ 228,-32768, -8, 6, 228, -38,-32768,-32768,-32768,-32768,
+-32768,-32768, 267, 267, 306, 228,-32768,-32768, 228, -6,
+ 228,-32768,-32768,-32768, 228,-32768, -5, 228,-32768, 27,
+ 36,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ 0, -17, -62, 1, -76, -26, 32, -7, -33, 19,
+-32768, -77, 33, -39, -1, -31
+};
+
+
+#define YYLAST 377
+
+
+static const short yytable[] = { 31,
+ 108, 38, 63, 64, 50, 52, 53, 58, 59, 60,
+ 61, 104, 62, 71, 67, 111, 111, 85, 135, 135,
+ 65, 45, 46, 47, 69, 131, 141, 72, 73, 70,
+ 68, 39, 40, 39, 40, 142, 39, 40, 74, 75,
+ 54, 55, 56, 57, 93, 94, 95, 96, 134, 106,
+ 86, 113, 71, 137, 66, 112, 127, 122, 136, 139,
+ 132, 133, 0, 76, 77, 71, 72, 73, 71, 48,
+ 49, 41, 102, 103, 115, 105, 121, 123, 124, 78,
+ 79, 80, 128, 87, 88, 116, 116, 116, 116, 89,
+ 90, 91, 92, 39, 40, 0, 99, 0, 81, 0,
+ 100, 82, 78, 79, 80, 0, 0, 107, 71, 109,
+ 126, 71, 50, 110, 129, 93, 94, 95, 96, 0,
+ 71, 81, 0, 0, 82, 0, 118, 119, 120, 0,
+ 129, 0, 1, 0, 138, 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, 0, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 27, 28,
+ 29, 30, 101, 1, 0, 0, 2, 3, 4, 5,
+ 6, 114, 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, 27,
+ 28, 29, 30, 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, 27, 28,
+ 29, 30, 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, 27, 28, 29,
+ 30, 13, 14, 15, 16, 17, 18, 19, 76, 125,
+ 0, 0, 0, 0, 22, 23, 0, 24, 25, 0,
+ 26, 0, 0, 0, 78, 79, 80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 27, 28, 29, 30,
+ 0, 0, 0, 81, 0, 0, 82
+};
+
+static const short yycheck[] = { 0,
+ 77, 1, 20, 21, 6, 7, 8, 15, 16, 17,
+ 18, 74, 64, 31, 27, 25, 25, 64, 25, 25,
+ 22, 3, 4, 5, 26, 64, 0, 3, 4, 30,
+ 26, 26, 27, 26, 27, 0, 26, 27, 14, 15,
+ 9, 10, 11, 12, 33, 34, 35, 36, 125, 76,
+ 51, 85, 70, 131, 23, 65, 65, 97, 65, 65,
+ 123, 124, -1, 6, 7, 83, 3, 4, 86, 64,
+ 65, 64, 72, 73, 64, 75, 65, 14, 15, 22,
+ 23, 24, 114, 52, 53, 93, 94, 95, 96, 58,
+ 59, 60, 61, 26, 27, -1, 65, -1, 41, -1,
+ 69, 44, 22, 23, 24, -1, -1, 76, 126, 78,
+ 111, 129, 114, 82, 115, 33, 34, 35, 36, -1,
+ 138, 41, -1, -1, 44, -1, 94, 95, 96, -1,
+ 131, -1, 5, -1, 135, 8, 9, 10, 11, 12,
+ -1, -1, -1, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, 26, 27, 28, 29, 30, 31, 32,
+ -1, -1, -1, -1, 37, 38, 39, 40, -1, 42,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 61, 62,
+ 63, 64, 65, 5, -1, -1, 8, 9, 10, 11,
+ 12, 13, -1, -1, 16, 17, 18, 19, 20, 21,
+ -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
+ 32, -1, -1, -1, -1, 37, 38, 39, 40, -1,
+ 42, 43, 5, 45, -1, 8, 9, 10, 11, 12,
+ -1, -1, -1, 16, 17, 18, 19, 20, 21, 61,
+ 62, 63, 64, 26, 27, 28, 29, 30, 31, 32,
+ -1, -1, -1, -1, 37, 38, 39, 40, -1, 42,
+ 43, 5, 45, -1, 8, 9, 10, 11, 12, -1,
+ -1, -1, 16, 17, 18, 19, 20, 21, 61, 62,
+ 63, 64, 26, 27, 28, 29, 30, 31, 32, -1,
+ -1, -1, -1, -1, -1, 39, 40, -1, 42, 43,
+ -1, 45, -1, 8, 9, 10, 11, 12, -1, -1,
+ -1, 16, 17, 18, 19, 20, 21, 61, 62, 63,
+ 64, 26, 27, 28, 29, 30, 31, 32, 6, 7,
+ -1, -1, -1, -1, 39, 40, -1, 42, 43, -1,
+ 45, -1, -1, -1, 22, 23, 24, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 61, 62, 63, 64,
+ -1, -1, -1, 41, -1, -1, 44
+};
+#define YYPURE 1
+
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program 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 1, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifdef __GNUC__
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif /* Not alloca. */
+#else /* Not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
+#include <alloca.h>
+#endif /* Sparc. */
+#endif /* Not GNU C. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYIMPURE
+#define YYLEX yylex()
+#endif
+
+#ifndef YYPURE
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYIMPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* YYIMPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+#ifdef __cplusplus
+__yy_bcopy (char *from, char *to, int count)
+#else
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+#endif
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#line 137 "/usr/local/lib/bison.simple"
+int
+yyparse()
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+
+#define YYPOPSTACK (yyvsp--, yysp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yysp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifndef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+#ifdef YYLSP_NEEDED
+ &yyls1, size * sizeof (*yylsp),
+#endif
+ &yystacksize);
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Next token is %d (%s)\n", yychar, yytname[yychar1]);
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ if (yylen == 1)
+ fprintf (stderr, "Reducing 1 value via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+ else
+ fprintf (stderr, "Reducing %d values via rule %d (line %d), ",
+ yylen, yyn, yyrline[yyn]);
+ }
+#endif
+
+
+ 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;
+ 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;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 362 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) xmalloc(size + 15);
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 331 "eqn.y"
+
diff --git a/eqn/eqn.tab.h b/eqn/eqn.tab.h
new file mode 100644
index 000000000..4cc78d483
--- /dev/null
+++ b/eqn/eqn.tab.h
@@ -0,0 +1,69 @@
+typedef union {
+ char *str;
+ box *b;
+ pile_box *pb;
+ matrix_box *mb;
+ int n;
+ column *col;
+} YYSTYPE;
+#define OVER 258
+#define SMALLOVER 259
+#define SQRT 260
+#define SUB 261
+#define SUP 262
+#define LPILE 263
+#define RPILE 264
+#define CPILE 265
+#define PILE 266
+#define LEFT 267
+#define RIGHT 268
+#define TO 269
+#define FROM 270
+#define SIZE 271
+#define FONT 272
+#define ROMAN 273
+#define BOLD 274
+#define ITALIC 275
+#define FAT 276
+#define ACCENT 277
+#define BAR 278
+#define UNDER 279
+#define ABOVE 280
+#define TEXT 281
+#define QUOTED_TEXT 282
+#define FWD 283
+#define BACK 284
+#define DOWN 285
+#define UP 286
+#define MATRIX 287
+#define COL 288
+#define LCOL 289
+#define RCOL 290
+#define CCOL 291
+#define MARK 292
+#define LINEUP 293
+#define TYPE 294
+#define VCENTER 295
+#define PRIME 296
+#define SPLIT 297
+#define NOSPLIT 298
+#define UACCENT 299
+#define SPECIAL 300
+#define SPACE 301
+#define GFONT 302
+#define GSIZE 303
+#define DEFINE 304
+#define NDEFINE 305
+#define TDEFINE 306
+#define SDEFINE 307
+#define UNDEF 308
+#define IFDEF 309
+#define INCLUDE 310
+#define DELIM 311
+#define CHARTYPE 312
+#define SET 313
+#define GRFONT 314
+#define GBFONT 315
+
+
+extern YYSTYPE yylval;
diff --git a/eqn/eqn.y b/eqn/eqn.y
new file mode 100644
index 000000000..c6f8fc439
--- /dev/null
+++ b/eqn/eqn.y
@@ -0,0 +1,331 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+%{
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "box.h"
+extern int non_empty_flag;
+char *strsave(const char *);
+#define YYDEBUG 1
+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;
+ 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/eqn/lex.c b/eqn/lex.c
new file mode 100644
index 000000000..2efa20ade
--- /dev/null
+++ b/eqn/lex.c
@@ -0,0 +1,1153 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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), contents(0), is_simple(0)
+{
+}
+
+definition::~definition()
+{
+ if (is_macro)
+ 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", "\"^\"",
+ "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)
+{
+ for (int 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()
+{
+ 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()
+{
+ 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()
+{
+ 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;
+}
+
+#define ARG1 11
+
+argument_macro_input::argument_macro_input(const char *body, int ac,
+ char **av, input *x)
+: input(x), argc(ac), ap(0)
+{
+ for (int 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++)
+ delete argv[i];
+ 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 == '\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;
+ for (int 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();
+ 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) {
+ 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';
+ char *ptr;
+ long n = strtol(token_buffer.contents(), &ptr, 10);
+ if (n == 0 && ptr == token_buffer.contents())
+ lex_error("bad argument `%1' to gsize command", ptr);
+ else
+ set_gsize(int(n));
+}
+
+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");
+ 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 chartype");
+ return;
+ }
+ token_buffer += '\0';
+ string param = token_buffer;
+ t = get_token();
+ if (t != TEXT && t != QUOTED_TEXT) {
+ lex_error("bad chartype");
+ 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 TDEFINE:
+ case DEFINE:
+ do_definition(0);
+ break;
+ case NDEFINE:
+ 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/eqn/limit.c b/eqn/limit.c
new file mode 100644
index 000000000..6672df01f
--- /dev/null
+++ b/eqn/limit.c
@@ -0,0 +1,195 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/eqn/list.c b/eqn/list.c
new file mode 100644
index 000000000..682cf9728
--- /dev/null
+++ b/eqn/list.c
@@ -0,0 +1,236 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 (int 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;
+ for (int 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 (int 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/eqn/main.c b/eqn/main.c
new file mode 100644
index 000000000..dd3c25c12
--- /dev/null
+++ b/eqn/main.c
@@ -0,0 +1,276 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "eqn.h"
+#include "stringclass.h"
+#include "device.h"
+
+extern int yyparse();
+
+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 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(const char *filename)
+{
+ string linebuf;
+ string str;
+ FILE *fp;
+ if (strcmp(filename, "-") == 0)
+ fp = stdin;
+ else {
+ fp = fopen(filename, "r");
+ if (fp == 0)
+ fatal("can't open `%1': %2", filename, strerror(errno));
+ }
+ printf(".lf 1 %s\n", filename);
+ current_filename = filename;
+ current_lineno = 0;
+ while (read_line(fp, &linebuf)) {
+ int i;
+ 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() >= 4
+ && linebuf[0] == '.'
+ && linebuf[1] == 'E'
+ && linebuf[2] == 'Q'
+ && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
+ 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);
+ }
+ else if (start_delim != '\0' && (i = linebuf.search(start_delim)) >= 0
+ && (i == 0 || linebuf[i - 1] != '\\')) {
+ start_string();
+ linebuf += '\0';
+ char *ptr = &linebuf[0];
+ inline_flag = 1;
+ for (;;) {
+ char *start = strchr(ptr, start_delim);
+ if (start == 0) {
+ char *nl = strchr(ptr, '\n');
+ if (nl != 0)
+ *nl = '\0';
+ do_text(ptr);
+ break;
+ }
+ 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';
+ init_lex(str.contents(), current_filename, start_lineno);
+ yyparse();
+ }
+ printf(".lf %d\n", current_lineno);
+ output_string();
+ restore_compatibility();
+ printf(".lf %d\n", current_lineno + 1);
+ }
+ else {
+ put_string(linebuf, stdout);
+ }
+ }
+ if (fp != stdin)
+ fclose(fp);
+ current_filename = 0;
+ current_lineno = 0;
+}
+
+void usage()
+{
+ fprintf(stderr, "usage: %s [ -vrDCN ] -dxx -fn -sn -pn -mn -Ts [ files ... ]\n",
+ program_name);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ const char *device = DEVICE;
+ const char *tem = getenv("GROFF_TYPESETTER");
+ if (tem)
+ device = tem;
+ while ((opt = getopt(argc, argv, "DCvd:f:p:s:m:T:rN")) != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU eqn version %s\n", version_string);
+ fflush(stderr);
+ 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;
+ break;
+ case 's':
+ {
+ int n;
+ if (sscanf(optarg, "%d", &n) == 1)
+ set_gsize(n);
+ else
+ error("bad 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':
+ draw_flag = 1;
+ break;
+ case 'N':
+ no_newline_in_delim_flag = 1;
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ init_table(device);
+ init_char_table();
+ if (optind >= argc)
+ do_file("-");
+ else
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ exit(0);
+}
+
+
diff --git a/eqn/mark.c b/eqn/mark.c
new file mode 100644
index 000000000..27fe29bfe
--- /dev/null
+++ b/eqn/mark.c
@@ -0,0 +1,121 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/eqn/other.c b/eqn/other.c
new file mode 100644
index 000000000..eba99e742
--- /dev/null
+++ b/eqn/other.c
@@ -0,0 +1,601 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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) : ab(qq), pointer_box(pp)
+{
+}
+
+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) : size(s), pointer_box(pp)
+{
+}
+
+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()
+{
+ 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()
+{
+ 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) : n(i), pointer_box(pp)
+{
+}
+
+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) : n(i), pointer_box(pp)
+{
+}
+
+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/eqn/over.c b/eqn/over.c
new file mode 100644
index 000000000..faf3e740d
--- /dev/null
+++ b/eqn/over.c
@@ -0,0 +1,196 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+: num(pp), den(qq), reduce_size(is_small)
+{
+ 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/eqn/pbox.h b/eqn/pbox.h
new file mode 100644
index 000000000..d14d2854f
--- /dev/null
+++ b/eqn/pbox.h
@@ -0,0 +1,137 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 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/eqn/pile.c b/eqn/pile.c
new file mode 100644
index 000000000..89337ad1d
--- /dev/null
+++ b/eqn/pile.c
@@ -0,0 +1,293 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 " \\n[" SUP_RAISE_FORMAT "]+(0",
+ uid, uid);
+ for (i = 0; i < len; i++)
+ printf(">?\\n[" HEIGHT_FORMAT "]", p[i]->p[0]->uid);
+ printf(")\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(")\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];
+ 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);
+ 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/eqn/script.c b/eqn/script.c
new file mode 100644
index 000000000..eeab15836
--- /dev/null
+++ b/eqn/script.c
@@ -0,0 +1,221 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/eqn/special.c b/eqn/special.c
new file mode 100644
index 000000000..69ed1b74f
--- /dev/null
+++ b/eqn/special.c
@@ -0,0 +1,115 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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) :macro_name(s), pointer_box(pp)
+{
+}
+
+special_box::~special_box()
+{
+ 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/eqn/sqrt.c b/eqn/sqrt.c
new file mode 100644
index 000000000..f6142dbdb
--- /dev/null
+++ b/eqn/sqrt.c
@@ -0,0 +1,179 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 "\\(rn"
+
+#define SQRT_CHAIN "\\[sr\\\\n[" INDEX_REG "]]"
+#define BAR_CHAIN "\\[rn\\\\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/eqn/text.c b/eqn/text.c
new file mode 100644
index 000000000..c5fbd2d90
--- /dev/null
+++ b/eqn/text.c
@@ -0,0 +1,526 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+ delete type;
+}
+
+char_box::char_box(unsigned char cc)
+: c(cc), prev_is_italic(0), next_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()
+{
+ 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);
+ 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/etc/Makefile b/etc/Makefile
new file mode 100644
index 000000000..e6733af79
--- /dev/null
+++ b/etc/Makefile
@@ -0,0 +1,74 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+CC=g++
+CFLAGS=-g -Wall
+INCLUDES=-I../lib
+DEFINES=
+GROG=grog.sh
+SOURCES=addftinfo.c guess.c guess.h
+OBJECTS=addftinfo.o guess.o
+ETAGS=etags
+ETAGSFLAGS=-p
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: addftinfo soelim
+
+addftinfo: $(OBJECTS) ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(LIBS)
+
+addftinfo.o: guess.h ../lib/lib.h ../lib/errarg.h ../lib/error.h \
+ ../lib/stringclass.h
+
+guess.o: guess.h
+
+soelim: soelim.o ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ soelim.o ../lib/libgroff.a $(LIBS)
+
+soelim.o: ../lib/lib.h ../lib/errarg.h ../lib/error.h \
+ ../lib/stringclass.h
+
+TAGS: $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o soelim addftinfo core
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+install.bin: all
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/addftinfo
+ cp addftinfo $(BINDIR)/addftinfo
+ -rm -f $(BINDIR)/gsoelim
+ cp soelim $(BINDIR)/gsoelim
+
+install.nobin:
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/grog
+ cp $(GROG) $(BINDIR)/grog
+ chmod +x $(BINDIR)/grog
+
+install: install.bin install.nobin
diff --git a/etc/addftinfo.c b/etc/addftinfo.c
new file mode 100644
index 000000000..b0211baa1
--- /dev/null
+++ b/etc/addftinfo.c
@@ -0,0 +1,192 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "guess.h"
+
+static void usage();
+static void convert_font(const font_params &, FILE *, FILE *);
+
+typedef 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];
+ 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;
+ for (int i = 1; i < argc && argv[i][0] == '-'; i++) {
+ if (argv[i][1] == '-' && argv[i][2] == '\0') {
+ i++;
+ break;
+ }
+ if (i + 1 >= argc)
+ usage();
+ for (int 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();
+ FILE *infp = fopen(font, "r");
+ if (infp == 0)
+ fatal("can't open `%1': %2", font, strerror(errno));
+ convert_font(param, infp, stdout);
+ exit(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-param value] ... resolution unitwidth font\n",
+ program_name);
+ exit(1);
+}
+
+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/etc/grog.pl b/etc/grog.pl
new file mode 100644
index 000000000..b896b0b16
--- /dev/null
+++ b/etc/grog.pl
@@ -0,0 +1,128 @@
+#!/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@.*/@@;
+
+push(@command, "groff");
+
+while ($ARGV[0] =~ /^-./) {
+ $arg = shift(@ARGV);
+ 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/) {
+ $_ = <FILE>;
+ if (!/^\./) {
+ $tbl++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.EQ/) {
+ $_ = <FILE>;
+ if (!/^\./ || /^\.[0-9]/) {
+ $eqn++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.PS/) {
+ $_ = <FILE>;
+ if (!/^\./ || /^\.ps/) {
+ $pic++;
+ $soelim++ if $level;
+ }
+ }
+ elsif (/^\.R1/ || /^\.\[/) {
+ $refer++;
+ $soelim++ if $level;
+ }
+ elsif (/^\.[PLI]P/) {
+ $PP++;
+ }
+ elsif (/^\.P$/) {
+ $P++;
+ }
+ elsif (/^\.TH/) {
+ $TH++;
+ }
+ elsif (/^\.SH/) {
+ $SH++;
+ }
+ elsif (/^\.[pnil]p/) {
+ $me++;
+ }
+ elsif (/^\.Dd/) {
+ $mdoc++;
+ }
+ if (/^\.so/) {
+ chop;
+ s/^.so *//;
+ s/\\\".*//;
+ s/ .*$//;
+ &process($_, $level + 1) unless /\\/ || $_ eq "";
+ }
+ }
+ close(FILE);
+}
+
+if ($pic || $tbl || $eqn || $refer) {
+ $s = "-";
+ $s .= "s" if $soelim;
+ $s .= "R" if $refer;
+ $s .= "p" if $pic;
+ $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) {
+ push(@command, "-mm");
+}
+elsif ($mdoc > 0) {
+ push(@command, "-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/etc/grog.sh b/etc/grog.sh
new file mode 100644
index 000000000..3b32f1990
--- /dev/null
+++ b/etc/grog.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# grog -- guess options for groff command
+# Like doctype in Kernighan & Pike, Unix Programming Environment, pp 306-8.
+
+soelim=gsoelim
+
+opts=
+
+for arg
+do
+ case "$arg" in
+ --)
+ shift; break;;
+ -)
+ break;;
+ -*)
+ opts="$opts $arg"; shift;;
+ *)
+ break;;
+ esac
+done
+
+egrep -h '^\.(P|[LI]P|[pnil]p|Dd|TS|EQ|TH|SH|so|\[|R1)' $* \
+| sed -e '/^\.so/s/^.*$/.SO_START\
+&\
+.SO_END/' \
+| $soelim \
+| egrep '^\.(P|[LI]P|[pnil]p|Dd|TS|EQ|TH|SH|\[|R1|SO_START|SO_END)' \
+| awk '
+/^\.SO_START$/ { so = 1 }
+/^\.SO_END$/ { so = 0 }
+/^\.TS/ { tbl++; if (so > 0) soelim++ }
+/^\.PS/ { pic++; if (so > 0) soelim++ }
+/^\.EQ/ { eqn++; if (so > 0) soelim++ }
+/^\.(R1|\[)/ { refer++; if (so > 0) soelim++ }
+/^\.TH/ { TH++ }
+/^\.[PLI]P/ { PP++ }
+/^\.P$/ { P++ }
+/^\.SH/ { SH++ }
+/^\.[pnil]p/ { me++ }
+/^\.Dd/ { mdoc++ }
+
+END {
+ if (files ~ /^-/)
+ files = "-- " files
+ printf "groff"
+ if (pic > 0 || tbl > 0 || eqn > 0 || refer > 0) {
+ printf " -"
+ if (soelim > 0) printf "s"
+ if (refer > 0) printf "R"
+ 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)
+ printf " -mm"
+ else if (mdoc > 0)
+ printf " -mdoc"
+ if (opts != "")
+ printf "%s", opts
+ if (files != "")
+ printf " %s", files
+ print
+}' "opts=$opts" "files=$*" -
diff --git a/etc/guess.c b/etc/guess.c
new file mode 100644
index 000000000..304e25f4d
--- /dev/null
+++ b/etc/guess.c
@@ -0,0 +1,490 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/etc/guess.h b/etc/guess.h
new file mode 100644
index 000000000..6f1125650
--- /dev/null
+++ b/etc/guess.h
@@ -0,0 +1,44 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/etc/soelim.c b/etc/soelim.c
new file mode 100644
index 000000000..3e7dfd1cf
--- /dev/null
+++ b/etc/soelim.c
@@ -0,0 +1,277 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+
+int compatible_flag = 0;
+
+extern int interpret_lf_args(const char *);
+
+int do_file(const char *filename);
+
+void usage()
+{
+ fprintf(stderr, "usage: %s [ -vC ] [ files ]\n", program_name);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int opt;
+ while ((opt = getopt(argc, argv, "vC")) != EOF)
+ switch (opt) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU soelim version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case '?':
+ usage();
+ 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");
+ exit(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;
+ 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 0;
+ }
+ }
+ current_filename = filename;
+ 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/groff.c b/groff.c
new file mode 100644
index 000000000..ee82f910c
--- /dev/null
+++ b/groff.c
@@ -0,0 +1,767 @@
+// -*- C++ -*-
+/* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+// A C++ implementation of groff.sh.
+
+#include <stdio.h>
+#include <string.h>
+#include <osfcn.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "lib.h"
+#include "assert.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "font.h"
+
+#include "device.h"
+
+const char *strsignal(int);
+
+// HAVE_UNION_WAIT exists only because Sun's osfcn.h includes sys/wait.h.
+
+extern "C" {
+#ifdef HAVE_UNION_WAIT
+ int wait(union wait *);
+#else /* !HAVE_UNION_WAIT */
+ int wait(int *);
+#endif /* !HAVE_UNION_WAIT */
+}
+
+const char *ps_print[] = { PSPRINT 0 };
+
+const char *dvi_print[] = { DVIPRINT 0 };
+
+const char *grops[] = { "grops", 0 };
+const char *grotty[] = { "grotty", 0 };
+const char *grodvi[] = { "grodvi", 0 };
+const char *gxditview[] = { "gxditview", "-", 0 };
+
+struct {
+ const char *pseudo_name;
+ const char *real_name;
+ const char *description; // Description of device (used in help message).
+ const char **driver; // Driver argument vector.
+ unsigned flags; // Bitwise OR of
+#define EQN_D_OPTION 01 /* Add -D option to eqn. */
+#define PIC_X_OPTION 02 /* Add -x option to pic. */
+#define PIC_P_OPTION 04 /* Add -p option to pic. */
+#define GROFF_OPTIONS 010 /* Driver understands -F and -v options. */
+#define XT_OPTION 020 /* Driver understands -title and -xrm options. */
+#define PRINT_OPTION 040 /* Understands -printCommand. */
+ const char *macro_file; // -m option to pass to troff
+ const char **spooler; // Spooler argument vector.
+} device_table[] = {
+ {
+ "ps",
+ "ps",
+ "PostScript",
+ grops,
+ EQN_D_OPTION|PIC_X_OPTION|PIC_P_OPTION|GROFF_OPTIONS,
+ "ps",
+ ps_print,
+ },
+ {
+ "ascii",
+ "ascii",
+ "ASCII",
+ grotty,
+ GROFF_OPTIONS,
+ "tty",
+ 0
+ },
+ {
+ "latin1",
+ "latin1",
+ "ISO Latin-1",
+ grotty,
+ GROFF_OPTIONS,
+ "tty",
+ 0
+ },
+ {
+ "dvi",
+ "dvi",
+ "TeX dvi format",
+ grodvi,
+ GROFF_OPTIONS|PIC_X_OPTION,
+ "dvi",
+ dvi_print,
+ },
+ {
+ "X100",
+ "X100",
+ "X11 previewer at 100dpi",
+ gxditview,
+ EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
+ "X",
+ 0
+ },
+ {
+ "X75",
+ "X75",
+ "X11 previewer at 75dpi",
+ gxditview,
+ EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
+ "X",
+ 0
+ },
+ {
+ "X100-12",
+ "X100-12",
+ "X11 previewer at 100dpi (optimized for 12 point text)",
+ gxditview,
+ EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
+ "X",
+ 0
+ },
+ {
+ "X75-12",
+ "X75-12",
+ "X11 previewer at 75dpi (optimized for 12 point text)",
+ gxditview,
+ EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
+ "X",
+ 0
+ },
+ {
+ "Xps",
+ "ps",
+ "X11 previewer with PostScript metrics",
+ gxditview,
+ EQN_D_OPTION|PIC_X_OPTION|PIC_P_OPTION|XT_OPTION|PRINT_OPTION,
+ "ps",
+ 0
+ },
+};
+
+const int SOELIM_INDEX = 0;
+const int REFER_INDEX = SOELIM_INDEX + 1;
+const int PIC_INDEX = REFER_INDEX + 1;
+const int TBL_INDEX = PIC_INDEX + 1;
+const int EQN_INDEX = TBL_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;
+
+// Children exit with this status if the execvp failed.
+const int EXEC_FAILED_EXIT_STATUS = 0xff;
+
+class possible_command {
+ char *name;
+ string args;
+ char **argv;
+public:
+ int pid;
+
+ possible_command();
+ ~possible_command();
+ void set_name(const char *);
+ const char *get_name();
+ void append_arg(const char *, const char * = 0);
+ void prepend_arg(const char *, const char * = 0);
+ void execp();
+ void clear_args();
+ void build_argv();
+ void print(int is_last, FILE *fp);
+};
+
+int lflag = 0;
+
+possible_command commands[NCOMMANDS];
+
+int run_commands();
+void print_commands();
+void append_arg_to_string(const char *arg, string &str);
+
+void usage();
+void help();
+void devices();
+void sys_fatal(const char *);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ string Pargs, Largs;
+ int Vflag = 0;
+ int zflag = 0;
+ int iflag = 0;
+ int opt;
+ const char *device = getenv("GROFF_TYPESETTER");
+ if (!device)
+ device = DEVICE;
+ commands[TROFF_INDEX].set_name("gtroff");
+ while ((opt = getopt(argc, argv,
+ "itpeRszavVhblCENZH:F:m:T:f:w:W:M:d:r:n:o:P:L:"))
+ != EOF) {
+ char buf[3];
+ buf[0] = '-';
+ buf[1] = opt;
+ buf[2] = '\0';
+ switch (opt) {
+ case 'i':
+ iflag = 1;
+ break;
+ case 't':
+ commands[TBL_INDEX].set_name("gtbl");
+ break;
+ case 'p':
+ commands[PIC_INDEX].set_name("gpic");
+ break;
+ case 'e':
+ commands[EQN_INDEX].set_name("geqn");
+ break;
+ case 's':
+ commands[SOELIM_INDEX].set_name("gsoelim");
+ break;
+ case 'R':
+ commands[REFER_INDEX].set_name("grefer");
+ 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':
+ commands[POST_INDEX].append_arg(buf);
+ // fall through
+ case 'C':
+ commands[SOELIM_INDEX].append_arg(buf);
+ commands[PIC_INDEX].append_arg(buf);
+ commands[TBL_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 'T':
+ device = optarg;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ commands[POST_INDEX].append_arg(buf, optarg);
+ // fall through
+ case 'f':
+ case 'o':
+ case 'm':
+ case 'r':
+ case 'M':
+ case 'H':
+ case 'd':
+ case 'n':
+ case 'w':
+ case 'W':
+ commands[TROFF_INDEX].append_arg(buf, optarg);
+ break;
+ case 'P':
+ Pargs += optarg;
+ Pargs += '\0';
+ break;
+ case 'L':
+ Largs += optarg;
+ Largs += '\0';
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ int found = 0;
+ for (int device_index = 0;
+ device_index < sizeof(device_table)/sizeof(device_table[0]);
+ device_index++)
+ if (strcmp(device, device_table[device_index].pseudo_name) == 0) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ error("unknown device `%1'", device);
+ devices();
+ exit(1);
+ }
+ device = device_table[device_index].real_name;
+ if (device_table[device_index].macro_file != 0)
+ commands[TROFF_INDEX].prepend_arg("-m",
+ device_table[device_index].macro_file);
+ commands[POST_INDEX].set_name(device_table[device_index].driver[0]);
+ if (!(device_table[device_index].flags & GROFF_OPTIONS))
+ commands[POST_INDEX].clear_args();
+ if ((device_table[device_index].flags & XT_OPTION)
+ && 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]);
+ }
+ if (device_table[device_index].flags & PRINT_OPTION) {
+ found = 0;
+ for (int di = 0; di < sizeof(device_table)/sizeof(device_table[0]); di++) {
+ if (strcmp(device, device_table[di].pseudo_name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ assert(found);
+ string print_string(device_table[di].driver[0]);
+ int i;
+ for (i = 1; device_table[di].driver[i]; i++)
+ append_arg_to_string(device_table[di].driver[i], print_string);
+ if (device_table[di].spooler) {
+ print_string += " |";
+ for (i = 0; device_table[di].spooler[i]; i++)
+ append_arg_to_string(device_table[di].spooler[i], print_string);
+ }
+ 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;
+ }
+ int i;
+ for (i = 1; device_table[device_index].driver[i]; i++)
+ commands[POST_INDEX].append_arg(device_table[device_index].driver[i]);
+ if (device_table[device_index].flags & PIC_X_OPTION)
+ commands[PIC_INDEX].append_arg("-x");
+ if (device_table[device_index].flags & PIC_P_OPTION)
+ commands[PIC_INDEX].append_arg("-p");
+ if (device_table[device_index].flags & EQN_D_OPTION)
+ commands[EQN_INDEX].append_arg("-D");
+ if (lflag && device_table[device_index].spooler) {
+ commands[SPOOL_INDEX].set_name(device_table[device_index].spooler[0]);
+ p = Largs.contents();
+ end = p + Largs.length();
+ while (p < end) {
+ commands[SPOOL_INDEX].append_arg(p);
+ p = strchr(p, '\0') + 1;
+ }
+ for (i = 1; device_table[device_index].spooler[i]; i++)
+ commands[SPOOL_INDEX].append_arg(device_table[device_index].spooler[i]);
+ }
+ if (zflag) {
+ commands[POST_INDEX].set_name(0);
+ commands[SPOOL_INDEX].set_name(0);
+ }
+ commands[TROFF_INDEX].append_arg("-T", device);
+ int have_eqnchar = 0;
+ if (commands[EQN_INDEX].get_name() != 0) {
+ commands[EQN_INDEX].append_arg("-T", device);
+ font::set_device_name(device);
+ char *path = 0;
+ FILE *fp = font::open_file("eqnchar", &path);
+ if (fp) {
+ fclose(fp);
+ if (path[0] == '-' && path[1] != '\0')
+ commands[EQN_INDEX].append_arg("--");
+ commands[EQN_INDEX].append_arg(path);
+ delete path;
+ have_eqnchar = 1;
+ }
+ }
+ for (int 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'
+ && (!have_eqnchar || first_index != EQN_INDEX))
+ commands[first_index].append_arg("--");
+ for (i = optind; i < argc; i++)
+ commands[first_index].append_arg(argv[i]);
+ if (iflag)
+ commands[first_index].append_arg("-");
+ }
+ if (have_eqnchar && (first_index != EQN_INDEX || optind >= argc))
+ commands[EQN_INDEX].append_arg("-");
+ if (Vflag) {
+ print_commands();
+ exit(0);
+ }
+ exit(run_commands());
+}
+
+void print_commands()
+{
+ for (int 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()
+{
+ for (int last = SPOOL_INDEX; last >= 0; last--)
+ if (commands[last].get_name() != 0)
+ break;
+ int last_input = 0;
+ int proc_count = 0;
+ for (int i = 0; i <= last; i++)
+ if (commands[i].get_name() != 0) {
+ proc_count++;
+ int pdes[2];
+ if (i != last) {
+ if (pipe(pdes) < 0)
+ sys_fatal("pipe");
+ }
+#ifdef HAVE_VFORK
+ int pid = vfork();
+ if (pid < 0)
+ sys_fatal("vfork");
+#else /* !HAVE_VFORK */
+ int pid = fork();
+ if (pid < 0)
+ sys_fatal("fork");
+#endif /* !HAVE_VFORK */
+ 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 != last) {
+ 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");
+ }
+ commands[i].execp();
+ }
+ // in the parent
+ if (last_input != 0) {
+ if (close(last_input) < 0)
+ sys_fatal("close");
+ }
+ if (i != last) {
+ if (close(pdes[1]) < 0)
+ sys_fatal("close");
+ last_input = pdes[0];
+ }
+ commands[i].pid = pid;
+ }
+ int ret = 0;
+ while (proc_count > 0) {
+ int status;
+#ifdef HAVE_UNION_WAIT
+ // union wait is just syntactic sugar: it's really just an int.
+ int pid = wait((union wait *)&status);
+#else /* !HAVE_UNION_WAIT */
+ int pid = wait(&status);
+#endif /* !HAVE_UNION_WAIT */
+ if (pid < 0)
+ sys_fatal("wait");
+ for (i = 0; i < NCOMMANDS; i++)
+ if (commands[i].pid == pid) {
+ --proc_count;
+ if ((status & 0xffff) != 0) {
+ if ((status & 0xff) != 0) {
+ if ((status & 0x7f) != SIGPIPE)
+ error("%1: %2%3",
+ commands[i].get_name(),
+ strsignal(status & 0x7f),
+ (status & 0x80) ? " (core dumped)" : "");
+ ret |= 2;
+ }
+ else {
+ int exit_status = (status >> 8) & 0xff;
+ if (exit_status == EXEC_FAILED_EXIT_STATUS)
+ ret |= 4;
+ else if (exit_status != 0)
+ ret |= 1;
+ }
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+possible_command::possible_command()
+: pid(-1), name(0), argv(0)
+{
+}
+
+possible_command::~possible_command()
+{
+ delete name;
+ delete argv;
+}
+
+void possible_command::set_name(const char *s)
+{
+ name = strsave(s);
+}
+
+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::prepend_arg(const char *s, const char *t)
+{
+ string old(args);
+ args = s;
+ if (t)
+ args += t;
+ args += '\0';
+ args += old;
+}
+
+void possible_command::build_argv()
+{
+ // 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();
+ 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;
+ for (const char *p = arg; *p != '\0'; p++)
+ switch (*p) {
+ case ';':
+ case '&':
+ case '(':
+ case ')':
+ case '|':
+ case '^':
+ case '<':
+ case '>':
+ case '\n':
+ case ' ':
+ case '\t':
+ 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;
+}
+
+
+void possible_command::execp()
+{
+ build_argv();
+ execvp(name, argv);
+ error("couldn't exec %1: %2", name, strerror(errno));
+#ifdef HAVE_VFORK
+ // vfork(2) says not to call exit when execve fails after vforking.
+ _exit(EXEC_FAILED_EXIT_STATUS);
+#else
+ exit(EXEC_FAILED_EXIT_STATUS);
+#endif
+}
+
+void synopsis()
+{
+ fprintf(stderr,
+"usage: %s [-abehilpstvzCENRVZ] [-Hfile] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
+" [-wname] [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
+" [-Larg] [files...]\n",
+ program_name);
+}
+
+void devices()
+{
+ fputs("Available devices are:\n", stderr);
+ for (int i = 0; i < sizeof(device_table)/sizeof(device_table[0]); i++)
+ fprintf(stderr, "%s\t%s\n",
+ device_table[i].pseudo_name,
+ device_table[i].description);
+}
+
+void help()
+{
+ synopsis();
+ fputs("\n"
+"-h\tprint this message\n"
+"-t\tpreprocess with tbl\n"
+"-p\tpreprocess with pic\n"
+"-e\tpreprocess with eqn\n"
+"-s\tpreprocess with soelim\n"
+"-R\tpreprocess with refer\n"
+"-Tdev\tuse device dev\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 directory dir for device directories\n"
+"-Mdir\tsearch dir for macro files\n"
+"-Hfile\tread hyphenation patterns from file\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"
+"\n",
+ stderr);
+ devices();
+ exit(0);
+}
+
+void usage()
+{
+ synopsis();
+ fprintf(stderr, "%s -h gives more help\n", program_name);
+ exit(1);
+}
+
+void sys_fatal(const char *s)
+{
+ fatal("%1: %2", s, strerror(errno));
+}
+
+#ifdef HAVE_SYS_SIGLIST
+extern "C" {
+ extern char *sys_siglist[];
+}
+#endif /* HAVE_SYS_SIGLIST */
+
+const char *strsignal(int n)
+{
+ static char buf[sizeof("Signal ") + 1 + INT_DIGITS];
+#ifdef HAVE_SYS_SIGLIST
+ if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
+ return sys_siglist[n];
+#endif /* HAVE_SYS_SIGLIST */
+ sprintf(buf, "Signal %d", n);
+ return buf;
+}
diff --git a/groff.sh b/groff.sh
new file mode 100644
index 000000000..4776322af
--- /dev/null
+++ b/groff.sh
@@ -0,0 +1,354 @@
+#!/bin/sh
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Add new devices below, where it says `Add local devices here.'
+
+prog=`basename $0`
+optF=
+optP=
+optL=
+trflags=
+tflag=
+pflag=
+eflag=
+sflag=
+lflag=
+zflag=
+vflag=
+iflag=
+Cflag=
+Nflag=
+Rflag=
+Vflag=
+dev=${GROFF_TYPESETTER:-@DEVICE@}
+
+synopsis="\
+usage: $prog [-abehilpstvzCENRVZ] [-Hfile] [-Fdir] [-mname] [-Tdev] [-ffam]
+ [-wname] [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]
+ [-Larg] [files...]"
+
+devices="Available devices are:
+X100 X11 previewer at 100dpi
+X75 X11 previewer at 75dpi
+X100-12 X11 previewer at 100dpi (optimized for 12 point text)
+X75-12 X11 previewer at 75dpi (optimized for 12 point text)
+ps PostScript
+Xps X11 previewer with PostScript metrics
+dvi TeX dvi format
+latin1 ISO Latin-1
+ascii ASCII"
+
+help="$synopsis
+-h print this message
+-t preprocess with tbl
+-p preprocess with pic
+-e preprocess with eqn
+-s preprocess with soelim
+-R preprocess with refer
+-Tdev use device dev
+-mname read macros tmac.name
+-dcs define a string c as s
+-rcn define a number register c as n
+-nnum number first page n
+-olist output only pages in list
+-ffam use fam as the default font family
+-Fdir search directory dir for device directories
+-Mdir search dir for macro files
+-Hfile read hyphenation patterns from file
+-v print version number
+-z suppress formatted output
+-Z don't postprocess
+-a produce ASCII description of output
+-i read standard input after named input files
+-wname enable warning name
+-Wname inhibit warning name
+-E inhibit all errors
+-b print backtraces with errors or warnings
+-l spool the output (@PSPRINT@ or @DVIPRINT@)
+-C enable compatibility mode
+-V print the pipeline on stdout instead of executing it
+-Parg pass arg to the postprocessor
+-Larg pass arg to the spooler
+-N don't allow newlines within eqn delimiters
+
+$devices"
+
+usage="$synopsis
+$prog -h gives more help"
+
+while test $# -gt 0
+do
+ case $1 in
+ -h*)
+ echo "$help" >&2
+ exit 0
+ ;;
+ -[az])
+ trflags="$trflags $1"
+ zflag=1
+ ;;
+ -Z)
+ zflag=1
+ ;;
+ -i)
+ iflag=1
+ ;;
+ -V)
+ Vflag=1
+ ;;
+ -t)
+ tflag=1
+ ;;
+ -p)
+ pflag=1
+ ;;
+ -e)
+ eflag=1
+ ;;
+ -s)
+ sflag=1
+ ;;
+ -R)
+ Rflag=1
+ ;;
+ -l)
+ lflag=1
+ ;;
+ -v)
+ vflag=-v
+ ;;
+ -C)
+ Cflag=-C
+ ;;
+ -N)
+ Nflag=-N
+ ;;
+ -[bE])
+ trflags="$trflags $1"
+ ;;
+ -[aiztpeslvbECNRVZ]*)
+ first=`expr "$1" : '\(-.\)'`
+ rest=`expr "$1" : '-.\(.*\)$'`
+ shift
+ set "" "$first" "-$rest" "$@"
+ ;;
+ -F)
+ if test $# -lt 2
+ then
+ echo "$prog: option -F requires an argument" >&2
+ exit 1
+ else
+ optF="$optF -F$2"
+ shift
+ fi
+ ;;
+ -F*)
+ optF="$optF $1"
+ ;;
+ -T)
+ if test $# -lt 2
+ then
+ echo "$prog: option -T requires an argument" >&2
+ exit 1
+ else
+ dev="$2"
+ shift
+ fi
+ ;;
+
+ -T*)
+ dev=`expr "$1" : '-T\(.*\)$'`
+ ;;
+ -[fomrMHdnwW])
+ if test $# -lt 2
+ then
+ echo "$prog: option $1 requires an argument" >&2
+ exit 1
+ else
+ trflags="$trflags $1$2"
+ shift
+ fi
+ ;;
+ -[fomrMHdnwW]*)
+ trflags="$trflags $1"
+ ;;
+ -P)
+ if test $# -lt 2
+ then
+ echo "$prog: option -P requires an argument" >&2
+ exit 1
+ else
+ optP="$optP $2"
+ shift
+ fi
+ ;;
+ -P*)
+ optP="$optP `expr "$1" : '-.\(.*\)$'`"
+ ;;
+ -L)
+ if test $# -lt 2
+ then
+ echo "$prog: option -L requires an argument" >&2
+ exit 1
+ else
+ optL="$optL $2"
+ shift
+ fi
+ ;;
+ -L*)
+ optL="$optL `expr "$1" : '-.\(.*\)$'`"
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -)
+ break
+ ;;
+ -*)
+ echo "$prog: unrecognized option $1" >&2
+ echo "$usage" >&2
+ exit 1
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+if test $# -gt 0
+then
+ files="$@"
+ if test "$iflag"
+ then
+ files="$files -"
+ fi
+else
+ files=-
+fi
+
+eqnchar=
+eqnflag=
+picflag=
+postpro=
+
+case $dev in
+ps)
+ trflags="-mps $trflags"
+ eqnchar=@FONTDIR@/devps/eqnchar
+ postpro="| grops $vflag $optF $optP"
+ picflag="-x -p"
+ eqnflag=-D
+ if test "$lflag"
+ then
+ postpro="$postpro | @PSPRINT@ $optL"
+ fi
+ ;;
+
+X100|X75|X100-12|X75-12)
+ trflags="-mX $trflags"
+ picflag=-x
+ eqnflag=-D
+ postpro="| gxditview $optP -"
+ eqnchar=@FONTDIR@/dev$dev/eqnchar
+ ;;
+
+Xps)
+ trflags="-mps $trflags"
+ eqnchar=@FONTDIR@/devps/eqnchar
+ postpro="| gxditview $optP -printCommand 'grops | @PSPRINT@' -"
+ picflag="-x -p"
+ eqnflag=-D
+ dev="ps"
+ ;;
+
+ascii|latin1)
+ trflags="-mtty $trflags"
+ postpro="| grotty $vflag $optF $optP"
+ ;;
+
+dvi)
+ trflags="-mdvi $trflags"
+ eqnchar=@FONTDIR@/devdvi/eqnchar
+ picflag=-x
+ postpro="| grodvi $vflag $optF $optP"
+ if test "$lflag"
+ then
+ postpro="$postpro | @DVIPRINT@ $optL"
+ fi
+ ;;
+
+
+# Add local devices here.
+
+*)
+ echo "$prog: unknown device \`$dev'" >&2
+ echo "$devices" >&2
+ exit 1
+ ;;
+esac
+
+prepro=
+
+if test "$sflag"
+then
+ prepro="$prepro gsoelim $vflag $Cflag $files |"
+ files=-
+fi
+
+if test "$Rflag"
+then
+ prepro="$prepro grefer $vflag $files |"
+ files=-
+fi
+
+if test "$pflag"
+then
+ prepro="$prepro gpic $vflag $Cflag $picflag $files |"
+ files=-
+fi
+
+if test "$tflag"
+then
+ prepro="$prepro gtbl $vflag $Cflag $files |"
+ files=-
+fi
+
+if test "$eflag"
+then
+ prepro="$prepro geqn $eqnflag $vflag $Cflag $Nflag -T$dev -- $eqnchar $files |"
+ files=-
+fi
+
+if test "$zflag"
+then
+ postpro=
+fi
+
+pipe="$prepro gtroff -T$dev $vflag $Cflag $trflags $optF -- $files $postpro"
+
+if test "$Vflag"
+then
+ echo $pipe
+else
+ eval $pipe
+fi
+exit 0
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 000000000..721b44857
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,100 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# FONTPATH says where to look for dev*/*
+FONTPATH=.:/usr/local/lib/groff/font:/usr/local/lib/font:/usr/lib/font
+OLDCC=gcc
+CC=g++
+AR=ar
+RANLIB=/bin/ranlib
+INCLUDES=-I.
+MALLOC=malloc.o
+MALLOCFLAGS=
+FMOD=#fmod.o
+STRTOL=#strtol.o
+OBJECTS=$(MALLOC) new.o itoa.o strerror.o error.o errarg.o fatal.o prime.o \
+ strsave.o matherr.o assert.o iftoa.o string.o cset.o cmap.o \
+ ptable.o font.o fontfile.o nametoindex.o filename.o lineno.o \
+ progname.o lf.o change_lf.o version.o $(FMOD) $(STRTOL)
+SOURCES=new.c itoa.c strerror.c error.c errarg.c fatal.c prime.c \
+ strsave.c matherr.c fmod.c assert.c iftoa.c \
+ string.c cset.c cmap.c ptable.c font.c fontfile.c nametoindex.c \
+ filename.c lineno.c progname.c lf.c change_lf.c version.c \
+ assert.h cset.h cmap.h errarg.h error.h font.h getpagesize.h \
+ lib.h ptable.h stringclass.h
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: libgroff.a
+
+libgroff.a: $(OBJECTS)
+ $(AR) r libgroff.a $?
+ if test "$(RANLIB)" ; then $(RANLIB) libgroff.a ;fi
+
+version.c: ../VERSION
+ @echo Making version.c
+ @echo const char \*version_string = \"`cat ../VERSION`\"\; >$@
+
+malloc.o: malloc.c getpagesize.h
+ $(OLDCC) $(OLDCFLAGS) $(MALLOCFLAGS) -c malloc.c
+
+itoa.o: itoa.c
+ $(OLDCC) $(OLDCFLAGS) -c itoa.c
+
+iftoa.o: iftoa.c
+ $(OLDCC) $(OLDCFLAGS) -c iftoa.c
+
+strerror.o: strerror.c
+ $(OLDCC) $(OLDCFLAGS) -c strerror.c
+
+matherr.o: matherr.c
+ $(OLDCC) $(OLDCFLAGS) -c matherr.c
+
+fmod.o: fmod.c
+ $(OLDCC) $(OLDCFLAGS) -c fmod.c
+
+strtol.o: strtol.c
+ $(OLDCC) $(OLDCFLAGS) -c strtol.c
+
+string.o: stringclass.h
+lf.o: stringclass.h
+fontfile.o: fontpath.h
+
+fontpath.h: FORCE
+ @echo \#define FONTPATH \"$(FONTPATH)\" >fontpath.h.n
+ @cmp -s fontpath.h fontpath.h.n || cp fontpath.h.n fontpath.h
+ @rm -f fontpath.h.n
+
+TAGS : $(SOURCES)
+ etags $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o core libgroff.a version.c fontpath.h
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+install:
+install.bin:
+install.nobin:
+
+FORCE:
diff --git a/lib/assert.c b/lib/assert.c
new file mode 100644
index 000000000..54b1205e9
--- /dev/null
+++ b/lib/assert.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "assert.h"
+
+extern const char *program_name;
+
+#ifdef __GNUG__
+volatile
+#endif
+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/lib/assert.h b/lib/assert.h
new file mode 100644
index 000000000..41b3ee7cd
--- /dev/null
+++ b/lib/assert.h
@@ -0,0 +1,41 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef ASSERT_H
+#define ASSERT_H
+#ifdef __GNUG__
+volatile
+#endif
+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/lib/change_lf.c b/lib/change_lf.c
new file mode 100644
index 000000000..2eb58d438
--- /dev/null
+++ b/lib/change_lf.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/cmap.c b/lib/cmap.c
new file mode 100644
index 000000000..b8bf84dc0
--- /dev/null
+++ b/lib/cmap.c
@@ -0,0 +1,55 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <ctype.h>
+#include "cmap.h"
+
+cmap cmlower(CMAP_BUILTIN);
+cmap cmupper(CMAP_BUILTIN);
+
+#define ISASCII(c) isascii(c)
+#if 0
+#define ISASCII(c) 1 /* use this is you have an ANSI ctype.h */
+#endif
+
+cmap::cmap()
+{
+ unsigned char *p = v;
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ p[i] = i;
+}
+
+cmap::cmap(cmap_builtin)
+{
+ // these are initialised by cmap_init::cmap_init()
+}
+
+int cmap_init::initialised = 0;
+
+cmap_init::cmap_init()
+{
+ if (initialised)
+ return;
+ initialised = 1;
+ for (int i = 0; i <= UCHAR_MAX; i++) {
+ cmupper.v[i] = ISASCII(i) && islower(i) ? toupper(i) : i;
+ cmlower.v[i] = ISASCII(i) && isupper(i) ? tolower(i) : i;
+ }
+}
diff --git a/lib/cmap.h b/lib/cmap.h
new file mode 100644
index 000000000..a19e2fc14
--- /dev/null
+++ b/lib/cmap.h
@@ -0,0 +1,56 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/cset.c b/lib/cset.c
new file mode 100644
index 000000000..9ca7bac74
--- /dev/null
+++ b/lib/cset.c
@@ -0,0 +1,101 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <ctype.h>
+#include "cset.h"
+
+cset csalpha(CSET_BUILTIN);
+cset csupper(CSET_BUILTIN);
+cset cslower(CSET_BUILTIN);
+cset csdigit(CSET_BUILTIN);
+cset csxdigit(CSET_BUILTIN);
+cset csspace(CSET_BUILTIN);
+cset cspunct(CSET_BUILTIN);
+cset csalnum(CSET_BUILTIN);
+cset csprint(CSET_BUILTIN);
+cset csgraph(CSET_BUILTIN);
+cset cscntrl(CSET_BUILTIN);
+
+#define ISASCII(c) isascii(c)
+#if 0
+#define ISASCII(c) 1 /* use this is you have an ANSI ctype.h */
+#endif
+
+void cset::clear()
+{
+ char *p = v;
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ p[i] = 0;
+}
+
+cset::cset()
+{
+ clear();
+}
+
+cset::cset(const char *s)
+{
+ clear();
+ while (*s)
+ v[(unsigned char)*s++] = 1;
+}
+
+cset::cset(const unsigned char *s)
+{
+ clear();
+ while (*s)
+ v[*s++] = 1;
+}
+
+cset::cset(cset_builtin)
+{
+ // these are initialised by cset_init::cset_init()
+}
+
+cset &cset::operator|=(const cset &cs)
+{
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ if (cs.v[i])
+ v[i] = 1;
+ return *this;
+}
+
+
+int cset_init::initialised = 0;
+
+cset_init::cset_init()
+{
+ if (initialised)
+ return;
+ initialised = 1;
+ for (int i = 0; i <= UCHAR_MAX; i++) {
+ csalpha.v[i] = ISASCII(i) && isalpha(i);
+ csupper.v[i] = ISASCII(i) && isupper(i);
+ cslower.v[i] = ISASCII(i) && islower(i);
+ csdigit.v[i] = ISASCII(i) && isdigit(i);
+ csxdigit.v[i] = ISASCII(i) && isxdigit(i);
+ csspace.v[i] = ISASCII(i) && isspace(i);
+ cspunct.v[i] = ISASCII(i) && ispunct(i);
+ csalnum.v[i] = ISASCII(i) && isalnum(i);
+ csprint.v[i] = ISASCII(i) && isprint(i);
+ csgraph.v[i] = ISASCII(i) && isgraph(i);
+ cscntrl.v[i] = ISASCII(i) && iscntrl(i);
+ }
+}
diff --git a/lib/cset.h b/lib/cset.h
new file mode 100644
index 000000000..cc8c2e6d3
--- /dev/null
+++ b/lib/cset.h
@@ -0,0 +1,71 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+
+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/lib/errarg.c b/lib/errarg.c
new file mode 100644
index 000000000..9ade99954
--- /dev/null
+++ b/lib/errarg.c
@@ -0,0 +1,114 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "assert.h"
+#include "errarg.h"
+
+errarg::errarg(const char *p) : s(p == 0 ? "(null)" : p), type(STRING)
+{
+}
+
+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 *itoa(int);
+}
+
+void errarg::print() const
+{
+ switch (type) {
+ case INTEGER:
+ fputs(itoa(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/lib/errarg.h b/lib/errarg.h
new file mode 100644
index 000000000..e1cf08a54
--- /dev/null
+++ b/lib/errarg.h
@@ -0,0 +1,46 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/error.c b/lib/error.c
new file mode 100644
index 000000000..a8dec38a0
--- /dev/null
+++ b/lib/error.c
@@ -0,0 +1,137 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/error.h b/lib/error.h
new file mode 100644
index 000000000..19250cd0a
--- /dev/null
+++ b/lib/error.h
@@ -0,0 +1,58 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/fatal.c b/lib/fatal.c
new file mode 100644
index 000000000..22dc670ff
--- /dev/null
+++ b/lib/fatal.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+
+#define FATAL_ERROR_EXIT_CODE 3
+
+void fatal_error_exit()
+{
+ exit(FATAL_ERROR_EXIT_CODE);
+}
diff --git a/lib/filename.c b/lib/filename.c
new file mode 100644
index 000000000..1cbaa93dd
--- /dev/null
+++ b/lib/filename.c
@@ -0,0 +1 @@
+const char *current_filename = 0;
diff --git a/lib/fmod.c b/lib/fmod.c
new file mode 100644
index 000000000..f850867b7
--- /dev/null
+++ b/lib/fmod.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/font.c b/lib/font.c
new file mode 100644
index 000000000..98f7c68a6
--- /dev/null
+++ b/lib/font.c
@@ -0,0 +1,836 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+};
+
+struct font_kern_list {
+ int i1;
+ int i2;
+ int amount;
+ font_kern_list *next;
+
+ font_kern_list(int, int, int, font_kern_list * = 0);
+};
+
+/* 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)
+: lineno(0), buf(0), size(0), skip_comments(1), fp(p), path(s)
+{
+}
+
+text_file::~text_file()
+{
+ delete buf;
+ 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);
+ 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 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)
+: special(0), ligatures(0), kern_hash_table(0), space_width(0),
+ ch(0), ch_used(0), ch_size(0), ch_index(0), nindices(0)
+{
+ name = new char[strlen(s) + 1];
+ strcpy(name, s);
+ internalname = 0;
+ slant = 0.0;
+ // load(); // for testing
+}
+
+font::~font()
+{
+ delete ch;
+ 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;
+ }
+ }
+ delete kern_hash_table;
+ }
+ delete name;
+ delete internalname;
+}
+
+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;
+ }
+ 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)*M_PI/180.0) + .5);
+}
+
+int font::contains(int c)
+{
+ return c >= 0 && c < nindices && ch_index[c] >= 0;
+}
+
+int font::is_special()
+{
+ return special;
+}
+
+int font::get_width(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].width, point_size);
+}
+
+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;
+}
+
+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;
+ 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));
+ delete old_ch;
+ }
+}
+
+void font::compact()
+{
+ for (int 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));
+ 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));
+ 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)
+{
+ font *f = new font(s);
+ if (!f->load()) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+int font::load()
+{
+ char *path;
+ FILE *fp;
+ if ((fp = open_file(name, &path)) == NULL) {
+ 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) {
+ int nargv = 5;
+ const char **argv = new char *[nargv];
+ int argc = 0;
+ do {
+ if (argc < nargv) {
+ const char **old_argv = argv;
+ int old_nargv = nargv;
+ nargv *= 2;
+ argv = new char *[nargv];
+ memcpy(argv, old_argv, sizeof(char*)*old_nargv);
+ delete old_argv;
+ }
+ argv[argc++] = p;
+ p = strtok(0, WS);
+ } while (p != 0);
+ handle_unknown_font_command(argc, argv);
+ delete argv;
+ }
+ 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;
+ }
+ 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 open `DESC'");
+ 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;
+ for (int i = 0; !found && i < sizeof(table)/sizeof(table[0]); i++)
+ if (strcmp(table[i].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[i-1].ptr));
+ int *ptr = table[i-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("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 = 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;
+ 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 = new char *[style_table_size];
+ for (int 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 = 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;
+ 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;
+ }
+ 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' commmand");
+ 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(int, const char **)
+{
+}
+
diff --git a/lib/font.h b/lib/font.h
new file mode 100644
index 000000000..a8b7ffb5b
--- /dev/null
+++ b/lib/font.h
@@ -0,0 +1,110 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+struct font_kern_list;
+struct font_char_metric;
+
+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_name();
+ const char *get_internal_name();
+
+ static font *load_font(const char *);
+ static void command_line_font_dir(const char *path);
+ static void forget_command_line_font_dirs();
+ static void set_device_name(const char *);
+ static const char *get_device_name();
+ 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 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 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;
+
+ static char *dev_name;
+ static char *cl_font_dirs;
+
+ 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(int argc, const char **argv);
+protected:
+ font(const char *);
+ int load();
+};
diff --git a/lib/fontfile.c b/lib/fontfile.c
new file mode 100644
index 000000000..1d11fde79
--- /dev/null
+++ b/lib/fontfile.c
@@ -0,0 +1,129 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "font.h"
+#include "lib.h"
+#include "fontpath.h"
+
+const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
+
+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;
+const char **font::font_name_table = 0;
+int *font::sizes = 0;
+char *font::dev_name = 0;
+char *font::cl_font_dirs = 0;
+const char *font::family = 0;
+const char **font::style_table = 0;
+
+void font::command_line_font_dir(const char *dir)
+{
+ if (cl_font_dirs == 0) {
+ cl_font_dirs = new char[strlen(dir)+1];
+ strcpy(cl_font_dirs, dir);
+ }
+ else {
+ int len = strlen(cl_font_dirs);
+ int need_colon = 0;
+ if (len > 0 && cl_font_dirs[len-1] != ':')
+ need_colon = 1;
+ char *old_dirs = cl_font_dirs;
+ cl_font_dirs = new char[len + need_colon + strlen(dir) + 1];
+ strcpy(cl_font_dirs, old_dirs);
+ if (need_colon)
+ strcat(cl_font_dirs, ":");
+ strcat(cl_font_dirs, dir);
+ delete old_dirs;
+ }
+}
+
+void font::forget_command_line_font_dirs()
+{
+ delete cl_font_dirs;
+ cl_font_dirs = 0;
+}
+
+FILE *font::open_file(const char *name, char **pathp)
+{
+ assert(dev_name != 0);
+ const char *dir_vec[3];
+ dir_vec[0] = cl_font_dirs;
+ dir_vec[1] = getenv(FONT_ENV_VAR);
+ dir_vec[2] = FONTPATH;
+ for (int i = 0; i < 3; i++)
+ if (dir_vec[i] != 0) {
+ const char *dirs = dir_vec[i];
+ while (*dirs != '\0') {
+ const char *p = strchr(dirs, ':');
+ if (p != dirs) {
+ if (p == 0)
+ p = strchr(dirs, '\0');
+ int need_slash = 0;
+ if (p > dirs && p[-1] != '/')
+ need_slash = 1;
+ char *path = new char[(p - dirs) + need_slash + 3
+ + strlen(dev_name) + 1
+ + strlen(name) + 1];
+ memcpy(path, dirs, p - dirs);
+ path[p - dirs] = '\0';
+ if (need_slash)
+ strcat(path, "/");
+ strcat(path, "dev");
+ strcat(path, dev_name);
+ strcat(path, "/");
+ strcat(path, name);
+ FILE *fp = fopen(path, "r");
+ if (fp != 0) {
+ *pathp = path;
+ return fp;
+ }
+ delete path;
+ if (*p == '\0')
+ break;
+ }
+ dirs = p + 1;
+ }
+ }
+ return 0;
+}
+
+void font::set_device_name(const char *s)
+{
+ dev_name = new char[strlen(s)+1];
+ strcpy(dev_name, s);
+}
+
+const char *font::get_device_name()
+{
+ return dev_name;
+}
+
diff --git a/lib/getpagesize.h b/lib/getpagesize.h
new file mode 100644
index 000000000..32adae61e
--- /dev/null
+++ b/lib/getpagesize.h
@@ -0,0 +1,25 @@
+#ifdef BSD
+#ifndef BSD4_1
+#define HAVE_GETPAGESIZE
+#endif
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+
+#include <sys/param.h>
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#define getpagesize() NBPC
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
+
diff --git a/lib/iftoa.c b/lib/iftoa.c
new file mode 100644
index 000000000..a5d1532ee
--- /dev/null
+++ b/lib/iftoa.c
@@ -0,0 +1,65 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "lib.h"
+
+char *iftoa(i, decimal_point)
+ int i, decimal_point;
+{
+ /* room for a -, 10 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/lib/itoa.c b/lib/itoa.c
new file mode 100644
index 000000000..b970eb1a6
--- /dev/null
+++ b/lib/itoa.c
@@ -0,0 +1,43 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "lib.h"
+
+char *itoa(i)
+ int i;
+{
+ /* Room for 10 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/lib/lf.c b/lib/lf.c
new file mode 100644
index 000000000..263d15cd0
--- /dev/null
+++ b/lib/lf.c
@@ -0,0 +1,61 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+ }
+ for (const char *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/lib/lib.h b/lib/lib.h
new file mode 100644
index 000000000..22fb1a3ff
--- /dev/null
+++ b/lib/lib.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file is included in both C and C++ compilations. */
+
+#ifdef __cplusplus
+extern "C" {
+ const char *strerror(int);
+ const char *itoa(int);
+ const char *iftoa(int, int);
+};
+
+char *strsave(const char *s);
+int is_prime(unsigned);
+
+int interpret_lf_args(const char *p);
+
+inline int illegal_input_char(int c)
+{
+ return c == 000 || (c > 012 && c < 040) || (c >= 0200 && c < 0240);
+}
+
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+/* 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 /* CFRONT_ANSI_BUG */
+
+#define INT_MIN (-INT_MAX-1)
+
+#endif /* CFRONT_ANSI_BUG */
+
+/* Maximum number of digits in the decimal representation of an int
+(not including the -). */
+
+#define INT_DIGITS 10
diff --git a/lib/lineno.c b/lib/lineno.c
new file mode 100644
index 000000000..f7138dba0
--- /dev/null
+++ b/lib/lineno.c
@@ -0,0 +1 @@
+int current_lineno = 0;
diff --git a/lib/malloc.c b/lib/malloc.c
new file mode 100644
index 000000000..b27c27779
--- /dev/null
+++ b/lib/malloc.c
@@ -0,0 +1,893 @@
+/* dynamic memory allocation for GNU.
+ Copyright (C) 1985, 1987 Free Software Foundation, Inc.
+
+ NO WARRANTY
+
+ BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
+NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
+WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
+RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
+WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
+AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
+STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
+WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
+OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
+DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
+A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
+PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
+
+ GENERAL PUBLIC LICENSE TO COPY
+
+ 1. You may copy and distribute verbatim copies of this source file
+as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy a valid copyright notice "Copyright
+(C) 1985 Free Software Foundation, Inc."; and include following the
+copyright notice a verbatim copy of the above disclaimer of warranty
+and of this License. You may charge a distribution fee for the
+physical act of transferring a copy.
+
+ 2. You may modify your copy or copies of this source file or
+any portion of it, and copy and distribute such modifications under
+the terms of Paragraph 1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish,
+ that in whole or in part contains or is a derivative of this
+ program or any part thereof, to be licensed at no charge to all
+ third parties on terms identical to those contained in this
+ License Agreement (except that you may choose to grant more extensive
+ warranty protection to some or all third parties, at your option).
+
+ c) You may charge a distribution fee for the physical act of
+ transferring a copy, and you may at your option offer warranty
+ protection in exchange for a fee.
+
+Mere aggregation of another unrelated program with this program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other program under the scope of these terms.
+
+ 3. You may copy and distribute this program (or a portion or derivative
+of it, under Paragraph 2) in object code or executable form under the terms
+of Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal
+ shipping charge) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+For an executable file, complete source code means all the source code for
+all modules it contains; but, as a special exception, it need not include
+source code for modules which are standard libraries that accompany the
+operating system on which the executable file runs.
+
+ 4. You may not copy, sublicense, distribute or transfer this program
+except as expressly provided under this License Agreement. Any attempt
+otherwise to copy, sublicense, distribute or transfer this program is void and
+your rights to use the program under this License agreement shall be
+automatically terminated. However, parties who have received computer
+software programs from you with this License Agreement will not have
+their licenses terminated so long as such parties remain in full compliance.
+
+ 5. If you wish to incorporate parts of this program into other free
+programs whose distribution conditions are different, write to the Free
+Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet
+worked out a simple rule that can be stated here, but we will often permit
+this. We will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software.
+
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding! */
+
+
+/*
+ * @(#)nmalloc.c 1 (Caltech) 2/21/82
+ *
+ * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs
+ *
+ * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks
+ * that don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are (2^n)-4 (or -16) bytes long.
+ * This is designed for use in a program that uses vast quantities of
+ * memory, but bombs when it runs out. To make it a little better, it
+ * warns the user when he starts to get near the end.
+ *
+ * June 84, ACT: modified rcheck code to check the range given to malloc,
+ * rather than the range determined by the 2-power used.
+ *
+ * Jan 85, RMS: calls malloc_warning to issue warning on nearly full.
+ * No longer Emacs-specific; can serve as all-purpose malloc for GNU.
+ * You should call malloc_init to reinitialize after loading dumped Emacs.
+ * Call malloc_stats to get info on memory stats if MSTATS turned on.
+ * realloc knows how to return same block given, just changing its size,
+ * if the power of 2 is correct.
+ */
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information will
+ * go in the first int of the block, and the returned pointer will point
+ * to the second.
+ *
+#ifdef MSTATS
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+#endif MSTATS
+ */
+
+#ifdef emacs
+/* config.h specifies which kind of system this is. */
+#include "config.h"
+#else
+
+/* Determine which kind of system this is. */
+#include <signal.h>
+#ifndef SIGTSTP
+#ifndef VMS
+#ifndef USG
+#define USG
+#endif
+#endif /* not VMS */
+#else /* SIGTSTP */
+#ifdef SIGIO
+#define BSD4_2
+#endif /* SIGIO */
+#endif /* SIGTSTP */
+
+#endif /* not emacs */
+
+/* Define getpagesize () if the system does not. */
+#include "getpagesize.h"
+
+#ifndef BSD4_2
+#ifndef USG
+#include <sys/vlimit.h> /* warn the user when near the end */
+#endif /* not USG */
+#else /* if BSD4_2 */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif /* BSD4_2 */
+
+#ifdef USG
+#define bcopy(from, to, n) memcpy(to, from, n)
+#endif
+
+extern char *start_of_data ();
+
+#ifdef BSD
+#ifndef DATA_SEG_BITS
+#define start_of_data() &etext
+#endif
+#endif
+
+#ifndef emacs
+#define start_of_data() &etext
+#endif
+
+#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */
+#define ISFREE ((char) 0x54) /* magic byte that implies free block */
+ /* this is for error checking only */
+#define ISMEMALIGN ((char) 0xd6) /* Stored before the value returned by
+ memalign, with the rest of the word
+ being the distance to the true
+ beginning of the block. */
+
+extern char etext;
+
+/* These two are for user programs to look at, when they are interested. */
+
+unsigned int malloc_sbrk_used; /* amount of data space used now */
+unsigned int malloc_sbrk_unused; /* amount more we can have */
+
+/* start of data space; can be changed by calling init_malloc */
+static char *data_space_start;
+
+#ifdef MSTATS
+static int nmalloc[30];
+static int nmal, nfre;
+#endif /* MSTATS */
+
+/* If range checking is not turned on, all we have is a flag indicating
+ whether memory is allocated, an index in nextf[], and a size field; to
+ realloc() memory we copy either size bytes or 1<<(index+3) bytes depending
+ on whether the former can hold the exact size (given the value of
+ 'index'). If range checking is on, we always need to know how much space
+ is allocated, so the 'size' field is never used. */
+
+struct mhead {
+ char mh_alloc; /* ISALLOC or ISFREE */
+ char mh_index; /* index in nextf[] */
+/* Remainder are valid only when block is allocated */
+ unsigned short mh_size; /* size, if < 0x10000 */
+#ifdef rcheck
+ unsigned mh_nbytes; /* number of bytes allocated */
+ int mh_magic4; /* should be == MAGIC4 */
+#endif /* rcheck */
+};
+
+/* Access free-list pointer of a block.
+ It is stored at block + 4.
+ This is not a field in the mhead structure
+ because we want sizeof (struct mhead)
+ to describe the overhead for when the block is in use,
+ and we do not want the free-list pointer to count in that. */
+
+#define CHAIN(a) \
+ (*(struct mhead **) (sizeof (char *) + (char *) (a)))
+
+#ifdef rcheck
+
+/* To implement range checking, we write magic values in at the beginning and
+ end of each allocated block, and make sure they are undisturbed whenever a
+ free or a realloc occurs. */
+/* Written in each of the 4 bytes following the block's real space */
+#define MAGIC1 0x55
+/* Written in the 4 bytes before the block's real space */
+#define MAGIC4 0x55555555
+#define ASSERT(p) if (!(p)) botch("p"); else
+#define EXTRA 4 /* 4 bytes extra for MAGIC1s */
+#else
+#define ASSERT(p)
+#define EXTRA 0
+#endif /* rcheck */
+
+
+/* nextf[i] is free list of blocks of size 2**(i + 3) */
+
+static struct mhead *nextf[30];
+
+/* busy[i] is nonzero while allocation of block size i is in progress. */
+
+static char busy[30];
+
+/* Number of bytes of writable memory we can expect to be able to get */
+static unsigned int lim_data;
+
+/* Level number of warnings already issued.
+ 0 -- no warnings issued.
+ 1 -- 75% warning already issued.
+ 2 -- 85% warning already issued.
+*/
+static int warnlevel;
+
+/* Function to call to issue a warning;
+ 0 means don't issue them. */
+static void (*warnfunction) ();
+
+/* nonzero once initial bunch of free blocks made */
+static int gotpool;
+
+char *_malloc_base;
+
+static void getpool ();
+
+/* Cause reinitialization based on job parameters;
+ also declare where the end of pure storage is. */
+void
+malloc_init (start, warnfun)
+ char *start;
+ void (*warnfun) ();
+{
+ if (start)
+ data_space_start = start;
+ lim_data = 0;
+ warnlevel = 0;
+ warnfunction = warnfun;
+}
+
+/* Return the maximum size to which MEM can be realloc'd
+ without actually requiring copying. */
+
+int
+malloc_usable_size (mem)
+ char *mem;
+{
+ int blocksize = 8 << (((struct mhead *) mem) - 1) -> mh_index;
+
+ return blocksize - sizeof (struct mhead) - EXTRA;
+}
+
+static void
+morecore (nu) /* ask system for more memory */
+ register int nu; /* size index to get more of */
+{
+ char *sbrk ();
+ register char *cp;
+ register int nblks;
+ register unsigned int siz;
+ int oldmask;
+
+#ifdef BSD
+#ifndef BSD4_1
+ /* ?? There was a suggestion not to block SIGILL, somehow for GDB's sake. */
+ oldmask = sigsetmask (-1);
+#endif
+#endif
+
+ if (!data_space_start)
+ {
+ data_space_start = start_of_data ();
+ }
+
+ if (lim_data == 0)
+ get_lim_data ();
+
+ /* On initial startup, get two blocks of each size up to 1k bytes */
+ if (!gotpool)
+ { getpool (); getpool (); gotpool = 1; }
+
+ /* Find current end of memory and issue warning if getting near max */
+
+#ifndef VMS
+ /* Maximum virtual memory on VMS is difficult to calculate since it
+ * depends on several dynmacially changing things. Also, alignment
+ * isn't that important. That is why much of the code here is ifdef'ed
+ * out for VMS systems.
+ */
+ cp = sbrk (0);
+ siz = cp - data_space_start;
+
+ if (warnfunction)
+ switch (warnlevel)
+ {
+ case 0:
+ if (siz > (lim_data / 4) * 3)
+ {
+ warnlevel++;
+ (*warnfunction) ("Warning: past 75% of memory limit");
+ }
+ break;
+ case 1:
+ if (siz > (lim_data / 20) * 17)
+ {
+ warnlevel++;
+ (*warnfunction) ("Warning: past 85% of memory limit");
+ }
+ break;
+ case 2:
+ if (siz > (lim_data / 20) * 19)
+ {
+ warnlevel++;
+ (*warnfunction) ("Warning: past 95% of memory limit");
+ }
+ break;
+ }
+
+ if ((int) cp & 0x3ff) /* land on 1K boundaries */
+ sbrk (1024 - ((int) cp & 0x3ff));
+#endif /* not VMS */
+
+ /* Take at least 2k, and figure out how many blocks of the desired size
+ we're about to get */
+ nblks = 1;
+ if ((siz = nu) < 8)
+ nblks = 1 << ((siz = 8) - nu);
+
+ if ((cp = sbrk (1 << (siz + 3))) == (char *) -1)
+ {
+#ifdef BSD
+#ifndef BSD4_1
+ sigsetmask (oldmask);
+#endif
+#endif
+ return; /* no more room! */
+ }
+ malloc_sbrk_used = siz;
+ malloc_sbrk_unused = lim_data - siz;
+
+#ifndef VMS
+ if ((int) cp & 7)
+ { /* shouldn't happen, but just in case */
+ cp = (char *) (((int) cp + 8) & ~7);
+ nblks--;
+ }
+#endif /* not VMS */
+
+ /* save new header and link the nblks blocks together */
+ nextf[nu] = (struct mhead *) cp;
+ siz = 1 << (nu + 3);
+ while (1)
+ {
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = nu;
+ if (--nblks <= 0) break;
+ CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz);
+ cp += siz;
+ }
+ CHAIN ((struct mhead *) cp) = 0;
+
+#ifdef BSD
+#ifndef BSD4_1
+ sigsetmask (oldmask);
+#endif
+#endif
+}
+
+static void
+getpool ()
+{
+ register int nu;
+ char * sbrk ();
+ register char *cp = sbrk (0);
+
+ if ((int) cp & 0x3ff) /* land on 1K boundaries */
+ sbrk (1024 - ((int) cp & 0x3ff));
+
+ /* Record address of start of space allocated by malloc. */
+ if (_malloc_base == 0)
+ _malloc_base = cp;
+
+ /* Get 2k of storage */
+
+ cp = sbrk (04000);
+ if (cp == (char *) -1)
+ return;
+
+ /* Divide it into an initial 8-word block
+ plus one block of size 2**nu for nu = 3 ... 10. */
+
+ CHAIN (cp) = nextf[0];
+ nextf[0] = (struct mhead *) cp;
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = 0;
+ cp += 8;
+
+ for (nu = 0; nu < 7; nu++)
+ {
+ CHAIN (cp) = nextf[nu];
+ nextf[nu] = (struct mhead *) cp;
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = nu;
+ cp += 8 << nu;
+ }
+}
+
+char *
+malloc (n) /* get a block */
+ unsigned n;
+{
+ register struct mhead *p;
+ register unsigned int nbytes;
+ register int nunits = 0;
+
+ /* Figure out how many bytes are required, rounding up to the nearest
+ multiple of 8, then figure out which nestf[] area to use.
+ Both the beginning of the header and the beginning of the
+ block should be on an eight byte boundary. */
+#ifdef SUNOS_LOCALTIME_BUG
+ /* SunOS 4.1 localtime scribbles on the ninth byte. */
+ nbytes = (n + ((sizeof *p + 15) & ~15) + EXTRA + 15) & ~15;
+#else
+ nbytes = (n + ((sizeof *p + 7) & ~7) + EXTRA + 7) & ~7;
+#endif
+ {
+ register unsigned int shiftr = (nbytes - 1) >> 2;
+
+ while (shiftr >>= 1)
+ nunits++;
+ }
+
+ /* In case this is reentrant use of malloc from signal handler,
+ pick a block size that no other malloc level is currently
+ trying to allocate. That's the easiest harmless way not to
+ interfere with the other level of execution. */
+ while (busy[nunits]) nunits++;
+ busy[nunits] = 1;
+
+ /* If there are no blocks of the appropriate size, go get some */
+ /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */
+ if (nextf[nunits] == 0)
+ morecore (nunits);
+
+ /* Get one block off the list, and set the new list head */
+ if ((p = nextf[nunits]) == 0)
+ {
+ busy[nunits] = 0;
+ return 0;
+ }
+ nextf[nunits] = CHAIN (p);
+ busy[nunits] = 0;
+
+ /* Check for free block clobbered */
+ /* If not for this check, we would gobble a clobbered free chain ptr */
+ /* and bomb out on the NEXT allocate of this size block */
+ if (p -> mh_alloc != ISFREE || p -> mh_index != nunits)
+#ifdef rcheck
+ botch ("block on free list clobbered");
+#else /* not rcheck */
+ abort ();
+#endif /* not rcheck */
+
+ /* Fill in the info, and if range checking, set up the magic numbers */
+ p -> mh_alloc = ISALLOC;
+#ifdef rcheck
+ p -> mh_nbytes = n;
+ p -> mh_magic4 = MAGIC4;
+ {
+ /* Get the location n after the beginning of the user's space. */
+ register char *m = (char *) p + ((sizeof *p + 7) & ~7) + n;
+
+ *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1;
+ }
+#else /* not rcheck */
+ p -> mh_size = n;
+#endif /* not rcheck */
+#ifdef MSTATS
+ nmalloc[nunits]++;
+ nmal++;
+#endif /* MSTATS */
+ return (char *) p + ((sizeof *p + 7) & ~7);
+}
+
+free (mem)
+ char *mem;
+{
+ register struct mhead *p;
+ {
+ register char *ap = mem;
+
+ if (ap == 0)
+ return;
+
+ p = (struct mhead *) (ap - ((sizeof *p + 7) & ~7));
+ if (p -> mh_alloc == ISMEMALIGN)
+ {
+ ap -= p->mh_size;
+ p = (struct mhead *) (ap - ((sizeof *p + 7) & ~7));
+ }
+
+#ifndef rcheck
+ if (p -> mh_alloc != ISALLOC)
+ abort ();
+
+#else rcheck
+ if (p -> mh_alloc != ISALLOC)
+ {
+ if (p -> mh_alloc == ISFREE)
+ botch ("free: Called with already freed block argument\n");
+ else
+ botch ("free: Called with bad argument\n");
+ }
+
+ ASSERT (p -> mh_magic4 == MAGIC4);
+ ap += p -> mh_nbytes;
+ ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1);
+ ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1);
+#endif /* rcheck */
+ }
+ {
+ register int nunits = p -> mh_index;
+
+ ASSERT (nunits <= 29);
+ p -> mh_alloc = ISFREE;
+
+ /* Protect against signal handlers calling malloc. */
+ busy[nunits] = 1;
+ /* Put this block on the free list. */
+ CHAIN (p) = nextf[nunits];
+ nextf[nunits] = p;
+ busy[nunits] = 0;
+
+#ifdef MSTATS
+ nmalloc[nunits]--;
+ nfre++;
+#endif /* MSTATS */
+ }
+}
+
+char *
+realloc (mem, n)
+ char *mem;
+ register unsigned n;
+{
+ register struct mhead *p;
+ register unsigned int tocopy;
+ register unsigned int nbytes;
+ register int nunits;
+
+ if (mem == 0)
+ return malloc (n);
+ p = (struct mhead *) (mem - ((sizeof *p + 7) & ~7));
+ nunits = p -> mh_index;
+ ASSERT (p -> mh_alloc == ISALLOC);
+#ifdef rcheck
+ ASSERT (p -> mh_magic4 == MAGIC4);
+ {
+ register char *m = mem + (tocopy = p -> mh_nbytes);
+ ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1);
+ ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1);
+ }
+#else /* not rcheck */
+ if (p -> mh_index >= 13)
+ tocopy = (1 << (p -> mh_index + 3)) - ((sizeof *p + 7) & ~7);
+ else
+ tocopy = p -> mh_size;
+#endif /* not rcheck */
+
+ /* See if desired size rounds to same power of 2 as actual size. */
+ nbytes = (n + ((sizeof *p + 7) & ~7) + EXTRA + 7) & ~7;
+
+ /* If ok, use the same block, just marking its size as changed. */
+ if (nbytes > (4 << nunits) && nbytes <= (8 << nunits))
+ {
+#ifdef rcheck
+ register char *m = mem + tocopy;
+ *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0;
+ p-> mh_nbytes = n;
+ m = mem + n;
+ *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1;
+#else /* not rcheck */
+ p -> mh_size = n;
+#endif /* not rcheck */
+ return mem;
+ }
+
+ if (n < tocopy)
+ tocopy = n;
+ {
+ register char *new;
+
+ if ((new = malloc (n)) == 0)
+ return 0;
+ bcopy (mem, new, tocopy);
+ free (mem);
+ return new;
+ }
+}
+
+#ifndef VMS
+
+char *
+memalign (alignment, size)
+ unsigned alignment, size;
+{
+ register char *ptr = malloc (size + alignment);
+ register char *aligned;
+ register struct mhead *p;
+
+ if (ptr == 0)
+ return 0;
+ /* If entire block has the desired alignment, just accept it. */
+ if (((int) ptr & (alignment - 1)) == 0)
+ return ptr;
+ /* Otherwise, get address of byte in the block that has that alignment. */
+ aligned = (char *) (((int) ptr + alignment - 1) & -alignment);
+
+ /* Store a suitable indication of how to free the block,
+ so that free can find the true beginning of it. */
+ p = (struct mhead *) aligned - 1;
+ p -> mh_size = aligned - ptr;
+ p -> mh_alloc = ISMEMALIGN;
+ return aligned;
+}
+
+#ifndef HPUX
+/* This runs into trouble with getpagesize on HPUX.
+ Patching out seems cleaner than the ugly fix needed. */
+char *
+valloc (size)
+{
+ return memalign (getpagesize (), size);
+}
+#endif /* not HPUX */
+#endif /* not VMS */
+
+#ifdef MSTATS
+/* Return statistics describing allocation of blocks of size 2**n. */
+
+struct mstats_value
+ {
+ int blocksize;
+ int nfree;
+ int nused;
+ };
+
+struct mstats_value
+malloc_stats (size)
+ int size;
+{
+ struct mstats_value v;
+ register int i;
+ register struct mhead *p;
+
+ v.nfree = 0;
+
+ if (size < 0 || size >= 30)
+ {
+ v.blocksize = 0;
+ v.nused = 0;
+ return v;
+ }
+
+ v.blocksize = 1 << (size + 3);
+ v.nused = nmalloc[size];
+
+ for (p = nextf[size]; p; p = CHAIN (p))
+ v.nfree++;
+
+ return v;
+}
+int
+malloc_mem_used ()
+{
+ int i;
+ int size_used;
+
+ size_used = 0;
+
+ for (i = 0; i < 30; i++)
+ {
+ int allocation_size = 1 << (i + 3);
+ struct mhead *p;
+
+ size_used += nmalloc[i] * allocation_size;
+ }
+
+ return size_used;
+}
+
+int
+malloc_mem_free ()
+{
+ int i;
+ int size_unused;
+
+ size_unused = 0;
+
+ for (i = 0; i < 30; i++)
+ {
+ int allocation_size = 1 << (i + 3);
+ struct mhead *p;
+
+ for (p = nextf[i]; p ; p = CHAIN (p))
+ size_unused += allocation_size;
+ }
+
+ return size_unused;
+}
+#endif /* MSTATS */
+
+/*
+ * This function returns the total number of bytes that the process
+ * will be allowed to allocate via the sbrk(2) system call. On
+ * BSD systems this is the total space allocatable to stack and
+ * data. On USG systems this is the data space only.
+ */
+
+#ifdef USG
+
+get_lim_data ()
+{
+ extern long ulimit ();
+
+#ifdef ULIMIT_BREAK_VALUE
+ lim_data = ULIMIT_BREAK_VALUE;
+#else
+ lim_data = ulimit (3, 0);
+#endif
+
+ lim_data -= (long) data_space_start;
+}
+
+#else /* not USG */
+#ifndef BSD4_2
+
+get_lim_data ()
+{
+ lim_data = vlimit (LIM_DATA, -1);
+}
+
+#else /* BSD4_2 */
+
+get_lim_data ()
+{
+ struct rlimit XXrlimit;
+
+ getrlimit (RLIMIT_DATA, &XXrlimit);
+#ifdef RLIM_INFINITY
+ lim_data = XXrlimit.rlim_cur & RLIM_INFINITY; /* soft limit */
+#else
+ lim_data = XXrlimit.rlim_cur; /* soft limit */
+#endif
+}
+
+#endif /* BSD4_2 */
+#endif /* not USG */
+
+#ifdef VMS
+/* There is a problem when dumping and restoring things on VMS. Calls
+ * to SBRK don't necessarily result in contiguous allocation. Dumping
+ * doesn't work when it isn't. Therefore, we make the initial
+ * allocation contiguous by allocating a big chunk, and do SBRKs from
+ * there. Once Emacs has dumped there is no reason to continue
+ * contiguous allocation, malloc doesn't depend on it.
+ *
+ * There is a further problem of using brk and sbrk while using VMS C
+ * run time library routines malloc, calloc, etc. The documentation
+ * says that this is a no-no, although I'm not sure why this would be
+ * a problem. In any case, we remove the necessity to call brk and
+ * sbrk, by calling calloc (to assure zero filled data) rather than
+ * sbrk.
+ *
+ * VMS_ALLOCATION_SIZE is the size of the allocation array. This
+ * should be larger than the malloc size before dumping. Making this
+ * too large will result in the startup procedure slowing down since
+ * it will require more space and time to map it in.
+ *
+ * The value for VMS_ALLOCATION_SIZE in the following define was determined
+ * by running emacs linked (and a large allocation) with the debugger and
+ * looking to see how much storage was used. The allocation was 201 pages,
+ * so I rounded it up to a power of two.
+ */
+#ifndef VMS_ALLOCATION_SIZE
+#define VMS_ALLOCATION_SIZE (512*256)
+#endif
+
+/* Use VMS RTL definitions */
+#undef sbrk
+#undef brk
+#undef malloc
+int vms_out_initial = 0;
+char vms_initial_buffer[VMS_ALLOCATION_SIZE];
+static char *vms_current_brk = &vms_initial_buffer;
+static char *vms_end_brk = &vms_initial_buffer[VMS_ALLOCATION_SIZE-1];
+
+#include <stdio.h>
+
+char *
+sys_sbrk (incr)
+ int incr;
+{
+ char *sbrk(), *temp, *ptr;
+
+ if (vms_out_initial)
+ {
+ /* out of initial allocation... */
+ if (!(temp = malloc (incr)))
+ temp = (char *) -1;
+ }
+ else
+ {
+ /* otherwise, go out of our area */
+ ptr = vms_current_brk + incr; /* new current_brk */
+ if (ptr <= vms_end_brk)
+ {
+ temp = vms_current_brk;
+ vms_current_brk = ptr;
+ }
+ else
+ {
+ vms_out_initial = 1; /* mark as out of initial allocation */
+ if (!(temp = malloc (incr)))
+ temp = (char *) -1;
+ }
+ }
+ return temp;
+}
+#endif /* VMS */
diff --git a/lib/matherr.c b/lib/matherr.c
new file mode 100644
index 000000000..a2a7b0b74
--- /dev/null
+++ b/lib/matherr.c
@@ -0,0 +1,44 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <math.h>
+#include <errno.h>
+
+#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 */
+
diff --git a/lib/nametoindex.c b/lib/nametoindex.c
new file mode 100644
index 000000000..693f17e59
--- /dev/null
+++ b/lib/nametoindex.c
@@ -0,0 +1,117 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+{
+ for (int 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, itoa(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/lib/new.c b/lib/new.c
new file mode 100644
index 000000000..56fbe2a21
--- /dev/null
+++ b/lib/new.c
@@ -0,0 +1,43 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <osfcn.h>
+#include <string.h>
+
+extern const char *program_name;
+
+static void ewrite(const char *s)
+{
+ write(2, s, strlen(s));
+}
+
+void *operator new(size_t size)
+{
+ char *p = (char *)malloc(unsigned(size));
+ if (p != 0)
+ return p;
+ if (program_name) {
+ ewrite(program_name);
+ ewrite(": ");
+ }
+ ewrite("out of memory\n");
+ _exit(-1);
+}
diff --git a/lib/prime.c b/lib/prime.c
new file mode 100644
index 000000000..f0b1eadcc
--- /dev/null
+++ b/lib/prime.c
@@ -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/lib/progname.c b/lib/progname.c
new file mode 100644
index 000000000..a70e3419c
--- /dev/null
+++ b/lib/progname.c
@@ -0,0 +1 @@
+const char *program_name = 0;
diff --git a/lib/ptable.c b/lib/ptable.c
new file mode 100644
index 000000000..5f2a1640c
--- /dev/null
+++ b/lib/ptable.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ptable.h"
+
+unsigned hash_string(const char *s)
+{
+ assert(s != 0);
+ unsigned h = 0, g;
+ while (*s != 0) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ return h;
+}
+
+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
+};
+
+int next_ptable_size(int n)
+{
+ for (const int *p = table_sizes; *p <= n && *p != 0; p++)
+ ;
+ assert(*p != 0);
+ return *p;
+}
diff --git a/lib/ptable.h b/lib/ptable.h
new file mode 100644
index 000000000..5825223ed
--- /dev/null
+++ b/lib/ptable.h
@@ -0,0 +1,159 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <assert.h>
+#include <generic.h>
+#include <string.h>
+
+#define PTABLE(T) name2(T,_ptable)
+#define PASSOC(T) name2(T,_passoc)
+#define PTABLE_ITERATOR(T) name2(T,_ptable_iterator)
+
+extern int next_ptable_size(int);
+extern unsigned 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; \
+ int i; \
+public: \
+ PTABLE_ITERATOR(T)(PTABLE(T) *); \
+ int next(const char **, T **); \
+}; \
+ \
+class PTABLE(T) { \
+ PASSOC(T) *v; \
+ int size; \
+ int 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 (int i = 0; i < size; i++) { \
+ delete v[i].key; \
+ delete v[i].val; \
+ } \
+} \
+ \
+void PTABLE(T)::define(const char *key, T *val) \
+{ \
+ assert(key != 0); \
+ int h = hash_string(key); \
+ for (int n = 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; \
+ int old_size = size; \
+ size = next_ptable_size(size); \
+ v = new PASSOC(T)[size]; \
+ for (int i = 0; i < old_size; i++) \
+ if (oldv[i].key != 0) { \
+ if (oldv[i].val == 0) \
+ delete oldv[i].key; \
+ else { \
+ for (int j = 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 = h % size; \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ ; \
+ 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 (int n = 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) \
+{ \
+ int 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/lib/strerror.c b/lib/strerror.c
new file mode 100644
index 000000000..eec102df4
--- /dev/null
+++ b/lib/strerror.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "lib.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+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/lib/string.c b/lib/string.c
new file mode 100644
index 000000000..d6a75684a
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,310 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+{
+ delete ptr;
+}
+
+static char *sfree_alloc(char *ptr, int oldsz, int len, int *sizep)
+{
+ if (oldsz >= len) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ 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) {
+ delete ptr;
+ *sizep = 0;
+ return 0;
+ }
+ else {
+ char *p = new char[*sizep = newlen*2];
+ if (oldlen < newlen && oldlen != 0)
+ memcpy(p, ptr, oldlen);
+ delete ptr;
+ return p;
+ }
+}
+
+string::string() : len(0), ptr(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 = (char *)memchr(ptr, c, len);
+ return p ? p - ptr : -1;
+}
+
+// we silently strip nuls
+
+char *string::extract() const
+{
+ char *p = ptr;
+ int n = len;
+ int nnuls = 0;
+ for (int 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/lib/stringclass.h b/lib/stringclass.h
new file mode 100644
index 000000000..db8753c22
--- /dev/null
+++ b/lib/stringclass.h
@@ -0,0 +1,174 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+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)
+{
+ if (s2 == 0)
+ return s1;
+ else
+ return string(s1.ptr, s1.len, s2, strlen(s2));
+}
+
+inline string operator+(const char *s1, const string &s2)
+{
+ if (s1 == 0)
+ return s2;
+ else
+ return string(s1, strlen(s1), s2.ptr, s2.len);
+}
+
+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/lib/strsave.c b/lib/strsave.c
new file mode 100644
index 000000000..75121dd11
--- /dev/null
+++ b/lib/strsave.c
@@ -0,0 +1,31 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/lib/strtol.c b/lib/strtol.c
new file mode 100644
index 000000000..596899e86
--- /dev/null
+++ b/lib/strtol.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+/* Not everybody has limits.h. Sigh. */
+
+#include "lib.h"
+#ifndef LONG_MAX
+#define LONG_MAX INT_MAX
+#endif
+#ifndef LONG_MIN
+#define LONG_MIN INT_MIN
+#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(*str) && isspace(*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(*str) && isupper(*str) ? tolower(*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(*str) && isupper(*str) ? tolower(*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/macros/Makefile b/macros/Makefile
new file mode 100644
index 000000000..edf6d2c42
--- /dev/null
+++ b/macros/Makefile
@@ -0,0 +1,48 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+MACRODIR=/usr/local/lib/groff/tmac
+TMAC_S=gs
+
+all:
+clean:
+distclean:
+realclean:
+TAGS:
+
+install.nobin:
+ -[ -d $(MACRODIR) ] || mkdir $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.an
+ cp tmac.an $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.e
+ @echo Installing tmac.e
+ @sed -e '/%beginstrip%/,$$s/[ ]*\\".*//' -e '/^\.$$/d' \
+ tmac.e > $(MACRODIR)/tmac.e
+ -rm -f $(MACRODIR)/tmac.$(TMAC_S)
+ cp tmac.s $(MACRODIR)/tmac.$(TMAC_S)
+ -rm -f $(MACRODIR)/tmac.pic
+ cp tmac.pic $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.doc
+ cp tmac.doc $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.andoc
+ cp tmac.andoc $(MACRODIR)
+
+install.bin:
+
+install: install.bin install.nobin
diff --git a/macros/TODO b/macros/TODO
new file mode 100644
index 000000000..a0f75f36d
--- /dev/null
+++ b/macros/TODO
@@ -0,0 +1,30 @@
+Improve the device independence of the character definitions.
+
+If we have footnotes in the abstract in RP format, then the footnote
+will appear on the cover sheet, which it should, but also on the first
+page, which it should not.
+
+Should we allow multi-page cover-sheets?
+
+Warn about automatically numbered footnotes in floating keeps.
+
+When we bring back the footnote overflow at the top of page, it would
+be more efficient to avoid diverting it again. (Need to keep track of
+footnote height.)
+
+Possibly have a place above which the footnote trap must not be
+placed.
+
+Improved indexing, not using tm, controlled by string variable (eg
+-dIDX=file.idx).
+
+Think about multiple line-spacing.
+
+When changing from multi-column to narrower columns, we could avoid
+doing a @super-eject. (This might not be a good idea.)
+
+Think about cutmarks. Possibly implement CM.
+
+Implement thesis Mode (TM, CT).
+
+Implement more V10 features.
diff --git a/macros/fixmacros.sed b/macros/fixmacros.sed
new file mode 100644
index 000000000..63cce22a0
--- /dev/null
+++ b/macros/fixmacros.sed
@@ -0,0 +1,6 @@
+s/^\([.'][ ]*[^\\ ][^ \\]\)\([^ ]\)/\1 \2/
+s/^\(\\![.'][ ]*[^\\ ][^ \\]\)\([^ ]\)/\1 \2/
+s/\([.'][ ]*i[ef] *[^ ]* [.'][ ]*[^\\0-9 ][^ \\]\)\([^ ]\)/\1 \2/
+s/\([.'][ ]*i[ef] *[^ ]* \\{[.'][ ]*[^\\0-9 ][^ \\]\)\([^ ]\)/\1 \2/
+s/\([.'][ ]*[da]s *[^ \\][^ \\]\)\([^ ]\)/\1 \2/
+s/\\\*\[/\\*[[]/
diff --git a/macros/mm.diff b/macros/mm.diff
new file mode 100644
index 000000000..d6694aa35
--- /dev/null
+++ b/macros/mm.diff
@@ -0,0 +1,70 @@
+*** tmac.m- Thu Aug 2 14:49:57 1990
+--- tmac.m Thu Aug 2 14:55:40 1990
+***************
+*** 744,749 ****
+ .fi
+ .di >A
+! \!.cs 3 48
+! .if !0\\$1-\\n(:d .if !\\n(:f \\*(}y\\$4\\\\f3\\a\\\\fP\\*(}3
+ .if !0\\$1-\\n(:d .if \\n(:f \\*(}y\\$4\\t\\*(}3
+ .if 0\\$1-\\n(:d \\*(}y\\$4\\*(}3
+--- 744,749 ----
+ .fi
+ .di >A
+! .cs 3 48
+! .if !0\\$1-\\n(:d .if !\\n(:f \\*(}y\\$4\?\f3\?\\a\?\fP\?\\*(}3
+ .if !0\\$1-\\n(:d .if \\n(:f \\*(}y\\$4\\t\\*(}3
+ .if 0\\$1-\\n(:d \\*(}y\\$4\\*(}3
+***************
+*** 750,754 ****
+ .br
+ \!.br
+! \!.cs 3
+ .di
+ .br
+--- 750,754 ----
+ .br
+ \!.br
+! .cs 3
+ .di
+ .br
+***************
+*** 1397,1406 ****
+ .tr ,+
+ .tr |.
+- .ie \\nE \{\
+- |ds\ >8\ \f3\\\\$1+\\\\$2+\\\\$3+\\\\$4+\\\\$5+\\\\$6+\\\\$7+\\\\$8+\\\\$9\fP
+- 'br \}
+- .el \{\
+ |ds\ >8\ \\\\$1+\\\\$2+\\\\$3+\\\\$4+\\\\$5+\\\\$6+\\\\$7+\\\\$8+\\\\$9
+- 'br \}
+ .br
+ \!.br
+--- 1397,1401 ----
+***************
+*** 1407,1410 ****
+--- 1402,1406 ----
+ .tr ,,++||
+ .di
++ .asciify >A
+ \\..
+ .>7 \\*(>1
+***************
+*** 1424,1429 ****
+--- 1420,1427 ----
+ .tr ||++
+ .di
++ .asciify >7
+ .de >A
+ .de >T
++ .if \\nE .ft 3
+ .if \\\\n(.$ \\\\$1
+ .if \\\\n(.$-1 \\\\$2
+***************
+*** 1435,1438 ****
+--- 1433,1437 ----
+ .if \\\\n(.$-7 \\\\$8
+ .if \\\\n(.$-8 \\\\$9
++ .if \\nE .ft
+ \\\\..
+ \\..
diff --git a/macros/tmac.an b/macros/tmac.an
new file mode 100644
index 000000000..96553d50b
--- /dev/null
+++ b/macros/tmac.an
@@ -0,0 +1,320 @@
+.\"Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+.\" Written by James Clark (jjc@jclark.uucp)
+.\"
+.\"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 1, 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 LICENSE. If not, write to the Free Software
+.\"Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\" -rC1 => number pages continuously, rather than start each at 1
+.\" -rD1 => double-sided printing, ie different odd and even page footers
+.\" -rPnnn => number first page nnn
+.\" -rXnnn => number pages after nnn as nnna, nnnb, nnnc, ...
+.\"
+.if !\n(.g .ab These man macros work only with groff.
+.if \n(.C \{\
+. tm Warning: turning off compatibility mode
+. cp 0
+.\}
+.if !rD .nr D 0
+.if !rC .nr C 0
+.if rP .pn 0\nP
+.\" .TH title section extra1 extra2 extra3
+.de TH
+.ds an-title "\\$1
+.ds an-section "\\$2
+.ds an-extra1 "\\$3
+.ie \\n[.$]>3 .ds an-extra2 "\\$4
+.el .ds an-extra2 \"Sun Release 4.0
+.ie \\n[.$]>4 .ds an-extra3 "\\$5
+.el .ds an-extra3 \"UNIX Programmer's Manual
+.DT
+.nr IN 7.2n
+.nr LL 6.5i
+.PD
+.nr PS 10 \" normal point-size
+.nr SN 3n \" the indentation of sub-sub-headings relative to sub-headings
+.nr an-level 1
+.nr an-margin \\n[IN]
+.nr an-prevailing-indent \\n[IN]
+.nr an-tag-sep 1n
+.nr an-no-space-flag 0
+.nr an-break-flag 0
+.nr an-div? 0
+.if \\n[nl]>0 \{\
+. ie \\nC .bp \\n%+1
+. el .bp 1
+.\}
+..
+.de DT
+.ta T .5i \" This sets tabs every .5 inches
+..
+.de PD
+.ie \\n[.$] .nr PD (v;\\$1)
+.el .nr PD .4v>?\n[.V]
+..
+.wh 0 an-header
+.de an-header
+.ev 1
+.sp .5i
+.tl '\\*[an-title](\\*[an-section])'\\*[an-extra3]'\\*[an-title](\\*[an-section])'
+.sp |1i
+.ev
+.ns
+..
+.wh -1i an-footer
+.de an-footer
+'bp
+..
+.wh -.5i an-p-footer
+.af an-page-letter a
+.de an-p-footer
+.ev 1
+.ds an-page-string \\n%
+.if rX \{\
+. if \\n%>\\nX \{\
+. nr an-page-letter \\n%-\\nX
+. ds an-page-string \\nX\\n[an-page-letter]
+.\}\}
+.ie \\nD \{\
+. if o .tl '\\*[an-extra2]'\\*[an-extra1]'\\*[an-page-string]'
+. if e .tl '\\*[an-page-string]'\\*[an-extra1]'\\*[an-extra2]'
+.\}
+.el .tl '\\*[an-extra2]'\\*[an-extra1]'\\*[an-page-string]'
+.ev
+..
+.de SH
+.sp \\n[PD]u
+.nr an-level 1
+.nr an-margin \\n[IN]
+.nr an-prevailing-indent \\n[IN]
+.fi
+.in \\n[IN]u
+.ti 0
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.ps \\n[PS]-1
+.ft B
+.ne 2v+1u
+.if \\n[.$] \&\\$*
+..
+.de SS
+.sp \\n[PD]u
+.nr an-level 1
+.nr an-margin \\n[IN]
+.nr an-prevailing-indent \\n[IN]
+.fi
+.in \\n[IN]u
+.ti \\n[SN]u
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.ps \\n[PS]
+.ft B
+.ne 2v+1u
+.if \\n[.$] \&\\$*
+..
+.de R
+.ft R
+.if \\n[.$] \&\\$*
+..
+.de B
+.it 1 an-trap
+.ft B
+.if \\n[.$] \&\\$*
+..
+.de I
+.it 1 an-trap
+.ft I
+.if \\n[.$] \&\\$*
+..
+.de SM
+.it 1 an-trap
+.ps -1
+.if \\n[.$] \&\\$*
+..
+.de SB
+.it 1 an-trap
+.ps -1
+.ft B
+.if \\n[.$] \&\\$*
+..
+.de TP
+.sp \\n[PD]u
+.if \\n[.$] .nr an-prevailing-indent (n;\\$1)
+.it 1 an-trap
+.di an-div
+.in 0
+.nr an-div? 1
+..
+.de an-trap
+.ft R
+.ps \\n[PS]
+.if \\n[an-break-flag] \{\
+. br
+. nr an-break-flag 0
+.\}
+.if \\n[an-no-space-flag] \{\
+. ns
+. nr an-no-space-flag 0
+.\}
+.if \\n[an-div?] .an-do-tag
+..
+.de an-do-tag
+.nr an-div? 0
+.br
+.di
+.in \\n[an-margin]u+\\n[an-prevailing-indent]u
+.ti -\\n[an-prevailing-indent]u
+.ie \\n[dl]+\\n[an-tag-sep]>\\n[an-prevailing-indent] \{\
+. ne 2v+1u
+. an-div
+. br
+.\}
+.el \{\
+. chop an-div
+. ne 1v+1u
+\\*[an-div]\\h'|\\n[an-prevailing-indent]u'\c
+.\}
+..
+.de LP
+.br
+.sp \\n[PD]u
+.ps \\n[PS]
+.ft R
+.in \\n[an-margin]u
+.nr an-prevailing-indent \\n[IN]
+..
+.als PP LP
+.als P LP
+.de IP
+.ie !\\n[.$] \{\
+. ps \\n[PS]
+. ft R
+. sp \\n[PD]u
+. ne 1v+1u
+. in \\n[an-margin]u+\\n[an-prevailing-indent]u
+.\}
+.el \{\
+. ie \\n[.$]-1 .TP "\\$2"
+. el .TP
+\&\\$1
+.\}
+..
+.de HP
+.ps \\n[PS]
+.ft R
+.sp \\n[PD]u
+.ne 1v+1u
+.if \\n[.$] .nr an-prevailing-indent (n;\\$1)
+.in \\n[an-margin]u+\\n[an-prevailing-indent]u
+.ti \\n[an-margin]u
+..
+.de RI
+.if \\n[.$] \{\
+. ds an-result \&\\$1
+. shift
+. while \\n[.$]>=2 \{\
+. as an-result \,\fI\\$1\fR\/\\$2
+. shift 2
+. \}
+. if \\n[.$] .as an-result \,\fI\\$1\fR
+\\*[an-result]
+.\}
+..
+.de IR
+.if \\n[.$] \{\
+. ds an-result \&\fI\\$1\fR
+. shift
+. while \\n[.$]>=2 \{\
+. as an-result \/\\$1\fI\,\\$2\fR
+. shift 2
+. \}
+. if \\n[.$] .as an-result \/\\$1
+\\*[an-result]
+.\}
+..
+.de IB
+.if \\n[.$] \{\
+. ds an-result \&\fI\\$1
+. shift
+. while \\n[.$]>=2 \{\
+. as an-result \/\\fB\\$1\fI\,\\$2
+. shift 2
+. \}
+. if \\n[.$] .as an-result \/\\fB\\$1
+\\*[an-result]
+. ft R
+.\}
+..
+.de BI
+.if \\n[.$] \{\
+. ds an-result \&\fB\\$1
+. shift
+. while \\n[.$]>=2 \{\
+. as an-result \,\fI\\$1\fB\/\\$2
+. shift 2
+. \}
+. if \\n[.$] .as an-result \,\fI\\$1
+\\*[an-result]
+. ft R
+.\}
+..
+.de RB
+.ds an-result \&
+.while \\n[.$]>=2 \{\
+. as an-result \fR\\$1\fB\\$2
+. shift 2
+.\}
+.if \\n[.$] .as an-result \fR\\$1
+\\*[an-result]
+.ft R
+..
+.de BR
+.ds an-result \&
+.while \\n[.$]>=2 \{\
+. as an-result \fB\\$1\fR\\$2
+. shift 2
+.\}
+.if \\n[.$] .as an-result \fB\\$1
+\\*[an-result]
+.ft R
+..
+.de RS
+.br
+.nr an-saved-margin\\n[an-level] \\n[an-margin]
+.nr an-saved-prevailing-indent\\n[an-level] \\n[an-prevailing-indent]
+.ie \\n[.$] .nr an-margin +(n;\\$1)
+.el .nr an-margin +\\n[an-prevailing-indent]
+.in \\n[an-margin]u
+.nr an-prevailing-indent \\n[IN]
+.nr an-level +1
+..
+.de RE
+.br
+.ie \\n[.$] .nr an-level (;\\$1)<?\\n[an-level]
+.el .nr an-level -1
+.nr an-level 1>?\\n[an-level]
+.nr an-margin \\n[an-saved-margin\\n[an-level]]
+.nr an-prevailing-indent \\n[an-saved-prevailing-indent\\n[an-level]]
+.in \\n[an-margin]u
+..
+.ds S \s[\\n[PS]]
+.ie c\[rg] .ds R \[rg]
+.el .ds R (Reg.)
+.ie c\[tm] .ds T \[tm]
+.el .ds T (TM)
+.als Tm T
+.hy 14
diff --git a/macros/tmac.andoc b/macros/tmac.andoc
new file mode 100644
index 000000000..cd870c3b3
--- /dev/null
+++ b/macros/tmac.andoc
@@ -0,0 +1,11 @@
+.\" Load either tmac.an or tmac.doc.
+.de Dd
+.rm Dd
+.mso tmac.doc
+\\*(Dd\\
+..
+.de TH
+.rm TH
+.mso tmac.an
+\\*(TH\\
+..
diff --git a/macros/tmac.doc b/macros/tmac.doc
new file mode 100644
index 000000000..80986bc3b
--- /dev/null
+++ b/macros/tmac.doc
@@ -0,0 +1,1825 @@
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted provided
+.\" that: (1) source distributions retain this entire copyright notice and
+.\" comment, and (2) distributions including binaries display the following
+.\" acknowledgement: ``This product includes software developed by the
+.\" University of California, Berkeley and its contributors'' in the
+.\" documentation or other materials provided with the distribution and in
+.\" all advertising materials mentioning features or use of this software.
+.\" Neither the name of the University nor the names of its contributors may
+.\" be used to endorse or promote products derived from this software without
+.\" specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)tmac.doc 5.9 (Berkeley) 7/24/90
+.\" Modified by jjc@jclark.UUCP for groff.
+.\"
+.if !\n(.g .ab This version of \-mdoc works only with groff.
+.\" Assume nroff on crt's only if cR==1
+.if n .nr cR 1
+.\" STRING CONSTANTS
+.\" DITROFF
+.if t \{\
+.\" Address Style
+.ds aD \fI
+.\" Argument Reference Style
+.ds aR \f(CO
+.\" Interactive Comand Modifier (flag)
+.ds cM \f(CB
+.\" Emphasis (in the English sense - usually italics)
+.ds eM \fI
+.\" Errno Style
+.ds eR \fC
+.\" Environment Variable Style
+.ds eV \fC
+.\" Command Line Flag Style
+.ds fL \f(CB
+.\" Header String Style
+.ds Hs \fR
+.\" Interactive Command Style
+.ds iC \f(CB
+.\" Literal Style
+.ds lI \fC
+.\" Left Parenthesis Style
+.ds lP \fR\|(\|\fP
+.\" Right Parenthesis Style
+.ds rP \fR\|)\|\fP
+.\" Options Open Bracket Style
+.ds lB \fR\^[\^\fP
+.\" Options Open Bracket Style
+.ds rB \fR\^]\fP
+.\" Name (subject of manpage) Style
+.ds nM \f(CB
+.\" Pathname Style
+.ds pA \fC
+.\" Accepted punctuation string for -mdoc syntax
+.ds Pu \fR[.,:;(\^)[\^]\fR]
+.\" Section Header Style
+.ds Sp \s12\fB
+.\" .ds sT \s-2\fR
+.\" Symbolic Emphasis (boldface)
+.ds sY \f(CB
+.\" Generic Variable Style
+.ds vA \fI
+.\" Volume Title Style
+.ds Vs \fR
+.\" Cross Reference STyle (man page only)
+.ds xR \fC
+.\" Math *
+.tr *\(**
+.\}
+.\" NROFF
+.if n \{\
+.\" Address Style
+.ds aD \fI
+.\" Argument Reference Style
+.ds aR \fI
+.\" Interactive Command Modifier (flag)
+.ds cM \fB
+.\" Emphasis (in the English sense - usually italics)
+.ds eM \fI
+.\" Errno Style
+.ds eR \fR
+.\" Environment Variable Style
+.ds eV \fR
+.\" Command Line Flag Style
+.ds fL \fB
+.\" Header String Style
+.ds Hs \fR
+.\" Interactive Command Style
+.ds iC \fB
+.\" Literal Style
+.ds lI \fR
+.\" Left Parenthesis Style
+.ds lP \fR\|(\fP
+.\" Right Parenthesis Style
+.ds rP \fR\|)\fP
+.\" Options Open Bracket Style
+.ds lB \fR\|[\|\fP
+.\" Options Open Bracket Style
+.ds rB \fR\|]\fP
+.\" Name (subject of manpage) Style
+.ds nM \fB
+.\" Pathname Style
+.ds pA \fI
+.\" Accepted punctuation string for -mdoc syntax
+.ds Pu [.,;:()[]]
+.\" Section Header Style
+.ds Sp \s12\fB
+.\" .ds sT \s-2\fR
+.\" .ds sT \s-2\fR
+.\" Symbol, Mode or Mask Style
+.ds sY \fB
+.\" Generic Variable Style
+.ds vA \fI
+.\" Volume Title Style
+.ds Vs \fR
+.\" Cross Reference Style (man page only)
+.ds xR \fR
+.\}
+.\" INDENTS - Subheaders(sI), Text(Ti) between Section Headers and Subsects
+.if t \{\
+. nr sI \w'\fC,'u*5
+. nr Ti \n(sIu
+.\}
+.if n \{\
+. nr sI .5i
+. nr Ti .5i
+.\}
+.\" Flags for macros names which are used only for .Ds
+.nr dI 6n
+.nr dC 1
+.nr dL 1
+.nr dR 1
+.\" INDENT WIDTHS (for Lists)
+.\" Width Needed for Address Tag (indented amount)
+.nr Ad 12n
+.\" Angle Quote Width
+.nr Aq 12n
+.\" Width Needed for Argument
+.nr Ar 12n
+.\" Width Needed for Column offset
+.nr Cl 15n
+.\" Width neeeded for Interactive Command Modifier
+.nr Cm 10n
+.\" Width Needed for Complex Expressions
+.nr Cx 20n
+.\" Indent Width Needed for Display (right and left margins)
+.nr Ds 6n
+.\" Double Quote Width
+.nr Dq 12n
+.\" tI is dependent on Ds and used by .Dp
+.nr tI \n(Dsu
+.\" Width Needed for Display
+.nr Em 10n
+.\" Width Needed for Errno Types
+.nr Er 15n
+.\" Width Needed for Environment Variables
+.nr Ev 15n
+.\" Width Needed for Example Indent
+.nr Ex 10n
+.\" Width Needed for Flag
+.nr Fl 10n
+.\" Width Needed for Function
+.nr Fn 16n
+.\" Width neeeded for Interactive Command Name
+.nr Ic 10n
+.\" Width Needed for Constant
+.nr Li 16n
+.\" Width Needed for Math Symbol ? not sure if needed
+.nr Ms 6n
+.\" Width Needed for Name
+.nr Nm 10n
+.\" Width Needed for Option Begin
+.nr Ob 14n
+.\" Width Needed for Option End
+.nr Oe 14n
+.\" Width Needed for Option (one line)
+.nr Op 14n
+.\" Width Needed for Pathname
+.nr Pa 32n
+.\" Parenthesis Quote Width
+.nr Pq 12n
+.\" Single Quote Width
+.nr Sq 12n
+.\" Width Needed for Symbols, Modes or Masks
+.nr Sy 6n
+.\" Width needed for default or unknown text width
+.nr Tx 22n
+.\" Width Needed for Generic Variable
+.nr Va 12n
+.\" Width Needed for Cross Reference, should the cross ref be annotated.
+.nr Xr 10n
+.\" PARAGRAPH SPACE
+.if t \{\
+. nr Pp .5v
+.\}
+.if n \{\
+. nr Pp 1v
+.\}
+.\" PAGE LAYOUT
+.\" .Li Tagged Paragraph Style - zero if break on oversized tag
+.\" one if add em space and continue filling line.
+.nr tP 0
+.\" Page Layout Macro
+.de pL
+.\" DITROFF
+.ie t \{\
+.\" Header Margin
+. nr Hm .5i
+.\" Footer Margin
+. nr Fm .5i
+.\" Line length
+. nr ll 5.5i
+.\" Line length
+. ll 5.5i
+.\" Title length
+. nr lt 5.5i
+.\" Title length
+. lt 5.5i
+.\" Page offset
+. nr po 1.56i
+.\" Page offset
+. po 1.56i
+.\" Vertical space distance (from Section headers/Lists/Subsections)
+. nr vV .5v
+.\" em space
+. ds tP \|\|\|\|\|\|
+.\}
+.el \{\
+.\" Line length
+. nr ll 78n
+. ll 78n
+.\" Title length
+. nr lt 78n
+.\" Title length
+. lt 78n
+.\" Page offset
+. nr po 0i
+.\" Page offset
+. po 0i
+.\" Vertical space distance (from Section headers/Lists/Subsections)
+. nr vV 1v
+.\" em space
+. ds tP \0\0
+.\" Test for crt
+. ie \\n(cR .nr Hm 0
+. el .nr Hm .5i
+.\" Footer Margin
+. nr Fm .5i
+.\}
+..
+.\" Adjustment mode
+.if n \{\
+.ad l
+.na
+..
+.\}
+.\" PREDEFINED STRINGS
+.if t \{\
+. ds <= \(<=
+. ds >= \(>=
+. ds Lq \&``
+. ds Rq \&''
+. ds ua \(ua
+. ds aa \(aa
+. ds ga \(ga
+. ds sR \(aa
+. ds sL \(ga
+.\}
+.if n \{\
+. ds <= \&<\&=
+. ds >= \&>\&=
+. ds Rq ''
+. ds Lq ``
+. ds ua ^
+. ds aa '
+. ds ga `
+. ds sL `
+. ds sR '
+.\}
+.\" Note: The distances from the bottom or top of the page are set
+.\" in headers (macro .hK): to -1.25 for troff, and -1.167 for nroff
+.\" bottoms, and top is 0.
+.\"
+.\" .Dt Document/manpage_title section/chapter volume
+.\" The \{ and \} is necessary as roff doesn't nest if-elses
+.\" properly, especially with .ds.
+.\" TODO: separate Dt into Dt, Ch and Vt for supp docs.
+.de Dt
+.ds dT UNTITLED
+.ds vT Local
+.ds cH Null
+.\" Volume and Section Number or Chapter Number
+.if !"\\$1"" .ds dT \\$1
+.if !"\\$2"" \{\
+. ds cH \\$2
+. if "\\$3"" \{\
+. \" Volume Title if none given
+. if \\$2>=1 .if \\$2<=8 \{\
+. ds vT UNIX Reference Manual
+. if \\$2>1 .if \\$2<6 .ds vT UNIX Programmer's Manual
+. if "\\$2"8" .ds vT UNIX System Manager's Manual
+. \}
+. if "\\$2"unass" .ds vT DRAFT
+. if "\\$2"draft" .ds vT DRAFT
+. if "\\$2"paper" .ds vT Null
+. \}
+.\}
+.if !"\\$3"" \{\
+. \" Volume Title if given
+. if "\\$3"USD" .ds vT UNIX User's Supplementary Documents
+. if "\\$3"PS1" .ds vT UNIX Programmers's Supplementary Documents
+. if "\\$3"AMD" .ds vT UNIX Ancestral Manual Documents
+. if "\\$3"SMM" .ds vT UNIX System Manager's Manual
+. if "\\$3"URM" .ds vT UNIX Reference Manual
+. if "\\$3"PRM" .ds vT UNIX Programmers's Manual
+. if "\\$3"IND" .ds vT UNIX Manual Master Index
+. if "\\$3"CON" .ds vT UNIX Contributed Software Manual
+. if "\\$3"IMP" .ds vT UNIX Implementation Notes
+. if "\\$3"HOW" .ds vT UNIX How Pocket Manual
+. if "\\$3"LOCAL" .ds vT UNIX Local Manual
+. if "\\*(vT"Local" .ds vT \\$3
+.\}
+..
+.\"
+.\" .Os Operating System/Standard and Release or Version Number
+.\"
+.de Os
+.ds oS Null
+.if "\\$1"" \{\
+. ds oS 4.4 Berkeley UNIX
+.\}
+.if "\\$2"" \{\
+. ds o1 Non-Null
+.\}
+.if "\\$1"ATT" \{\
+. ds oS AT&T
+. if "\\$2"" .as oS \0UNIX
+. if "\\$2"7th" .as oS \07th Edition
+. if "\\$2"7" .as oS \07th Edition
+. if "\\$2"III" .as oS \0System III
+. if "\\$2"3" .as oS \0System III
+. if "\\$2"V" .as oS \0System V
+. if "\\$2"V.2" .as oS \0System V Release 2
+. if "\\$2"V.3" .as oS \0System V Release 3
+. if "\\$2"V.4" .as oS \0System V Release 4
+.\}
+.if "\\$1"BSD" \{\
+. if "\\$2"3" .ds oS 3rd Berkeley Distribution
+. if "\\$2"4" .ds oS 4th Berkeley Distribution
+. if "\\$2"4.1" .ds oS 4.1 Berkeley Distribution
+. if "\\$2"4.2" .ds oS 4.2 Berkeley Distribution
+. if "\\$2"4.3" .ds oS 4.3 Berkeley Distribution
+. if "\\$2"4.3+" .ds oS 4.3+tahoe Berkeley Distribution
+. if "\\$2"4.4" .ds oS 4.4 Berkeley Distribution
+.\}
+.if "\\*(oS"Null" .ds oS \\$1
+.if "\\*(o1"Non-Null" .as oS \0\\$2
+.rm o1
+..
+.\"
+.\" Standards
+.\"
+.\" .de St
+.\" .ds sT Null
+.\" .if "\\$1"POSIX" \{\
+.\" . ds sT IEEE Standard POSIX
+.\" . if \\$2 .as sT \0\\$2
+.\" .\}
+.\" .if "\\$1"ANSI" \{\
+.\" . ds sT ANSI Standard
+.\" . if \\$2 .as sT \0\\$2
+.\" .\}
+.\" .if "\\$1"ISO" \{\
+.\" . ds sT ISO Standard
+.\" . if \\$2 .as sT \0\\$2
+.\" .\}
+.\" .if "\\*(sT"Null" .ds sR \\$3
+.\" ..
+.\"
+.\" .de Gp
+.\" .ie !"\\$1"" .ds gP \&\\$1 \\$2 \\$3 \\$4 \\$5
+.\" .el .ds gP Null
+.\" ..
+.\"
+.\"
+.de Dd
+.nr aa 0
+.ie \\n(.$>0 \{\
+. ie \\n(.$<4 \{\
+. ds dD \\$1 \\$2 \\$3
+. \}
+. el .tm Usage: .Dd Month Day, Year (e.g July 4, 1977).
+.\}
+.el \{\
+. ds dD Epoch
+.\}
+..
+.\"
+.\" House Keeping Macro - Make sense of dT, cH, vT, sT, gP and dS
+.\" TODO: Try to get else's for efficiency
+.\" TODO: GET RID OF .wh -1.167i (its in v7)
+.\"
+.\"
+.de hK
+.nr % 1
+.ds hT \\*(dT
+.if !"\\*(cH"Null" \{\
+. ie !"\\*(gP"Null" .as hT \|(\|\\*(cH\\*(gP\|)
+. el .as hT \\|(\\|\\*(cH\\|)
+.\}
+.if "\\*(cH"Null" .if !"\\*(gP"Null" .as hT \&\|(\|\\*(gP\|)
+.if t \{\
+. wh 0 hM
+. wh -1.25i fM
+.\}
+.if n \{\
+. ie \\n(cR \{\
+. hM
+. wh -0v fM
+. \}
+. el \{\
+. wh 0 hM
+. wh -1.167i fM
+. \}
+.\}
+.if n \{\
+. if \\n(nl==0:\\n(nl==-1 'bp
+.\}
+.if t 'bp
+.em lM
+..
+.\" Header Macro
+.\"
+.de hM
+.ev 1
+.pL
+.if !\\n(cR 'sp \\n(Hmu
+.tl @\\*(Hs\\*(hT\fP@\\*(Vs\\*(vT\fP@\\*(Hs\\*(hT\fP@
+'sp \\n(Hmu
+.ev
+..
+.\"
+.de fM
+.ev 1
+.pL
+.if !\\n(cR \{\
+' sp \\n(Fmu
+. tl @\\*(Hs\\*(oS\fP@\\*(Vs\\*(dD\fP@%@
+' bp
+.\}
+.if \\n(cR \{\
+.\" . tl @\\*(Hs\\*(oS\fP@\\*(Vs\\*(dD\fP@%@
+.\" ' bp
+.\}
+.ev
+..
+.de lM
+.fl
+.if \\n(cR \{\
+. fM
+. pl \\n(nlu
+.\}
+..
+.de Pp
+.sp \\n(Ppu
+.ne 2
+.ns
+..
+.de Lp
+.Pp
+..
+.de LP
+.tm Not a \-mdoc command: .LP
+..
+.de PP
+.tm Not a \-mdoc command: .PP
+..
+.de pp
+.tm Not a \-mdoc command: .pp
+..
+.de Co
+.tm Not a \-mdoc command: .Co
+..
+.nr z. 1
+.nr z, 1
+.nr z: 1
+.nr z; 1
+.nr z) 1
+.nr z( 1
+.nr z[ 1
+.nr z] 1
+.\" This is disgusting, troff not parse if stmt properly
+.nr z1 0
+.nr z2 0
+.nr z3 0
+.nr z4 0
+.nr z5 0
+.nr z6 0
+.nr z7 0
+.nr z8 0
+.nr z9 0
+.nr z0 0
+.nr z# 0
+.\"
+.de Ad
+.ie \\n(.$==0 \{\
+. tm Usage: .Ad address [...] \\*(Pu
+.\}
+.el \{\
+. ds sV \\*(aD
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.\" Command Line Argument Macro
+.\"
+.de Ar
+.ie \\n(.$==0 \{\
+. ie !"\\*(iM"" .as f1 \&[\|\\*(aRfile\ ...\fP\|]
+. el \&[\|\\*(aRfile\ ...\fP\|]
+.\}
+.el \{\
+. ds sV \\*(aR
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Em
+.ie \\n(.$==0 \{\
+. tm Usage: .Em text ... \\*(Pu
+.\}
+.el \{\
+. ds sV \\*(eM
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Er
+.ie \\n(.$==0 \{\
+. tm Usage: .Er ERRNOTYPE ... \\*(Pu
+. \}
+.el \{\
+. ds sV \\*(eR
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Ev
+.ie \\n(.$==0 \{\
+. tm Usage: .Ev ENVIRONMENT_VARIABLE(s) ... \\*(Pu
+. \}
+.el \{\
+. ds sV \\*(eV
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.\" Flag Name Macro
+.\"
+.de Fl
+.ie \\n(.$==0 \{\
+. ie !"\\*(iM"" .as f1 \&\\*(fL\-\fP
+. el \&\\*(fL\-\fP
+.\}
+.el \{\
+. nr rZ 0
+. sW \\$1
+. if (\\n(sW==1&\\n(.$==1) .rZ \\$1
+. ds sV \\*(fL
+. nr cF \\n(.f
+. ie \\n(rZ \{\
+. ie "\\*(iM"" .ds f1 \&\\*(sV\-\f\\n(cF\\$1
+. el \&\\*(sV\-\f\\n(cF\\$1
+. \}
+. el \{\
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. fB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+. \}
+.\}
+..
+.\" Interactive Commands Macro
+.\"
+.de Ic
+.ie \\n(.$==0 \{\
+. tm Usage: .Ic Interactive Commands(s) ... \\*(Pu
+.\}
+.el \{\
+. ds sV \\*(iC
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.\" Interactive Command Modifiers (flags)
+.\"
+.de Cm
+.ie \\n(.$==0 \{\
+. tm Usage: .Cm Interactive Command Modifier(s) ... \\*(Pu
+.\}
+.el \{\
+. ds sV \\*(cM
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Li
+.ie \\n(.$==0 \{\
+. tm Usage: .Li literal ... \\*(Pu
+. \}
+.el \{\
+. ds sV \\*(lI
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\" If in nroff or any other case where the default font
+.\" is constant width, and literal means zilch, single quote instead.
+.ie n \{\
+.de Ql
+. ie \\n(.$==0 \{\
+. tm Usage: .Ql literal ... \\*(Pu
+. \}
+. el \{\
+. Sq \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+..
+.\}
+.el \{\
+.de Ql
+. ie \\n(.$==0 \{\
+. tm Usage: .Ql literal ... \\*(Pu
+. \}
+. el \{\
+. Li \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+..
+.\}
+.\"
+.de Nm
+.ie \\n(.$==0 \{\
+. if "\\*(n1"" .tm Usage: .Nm Name(s) ... \\*(Pu
+. ie !"\\*(iM"" .as f1 \&\\*(nM\\*(n1\\$1\fP
+. el \&\\*(nM\\*(n1\\$1\fP
+.\}
+.el \{\
+. ds sV \\*(nM
+. nr cF \\n(.f
+. if \\n(nS \{\
+. rs
+. in -\\n(iSu
+. ie \\n(nS>1 .br
+. el \{\
+. sW \\$1
+. nr iS ((\\n(sW+1)*\\n(fW)u
+. \}
+. in +\\n(iSu
+. ti -\\n(iSu
+. nr nS \\n(nS+1
+. \}
+. if "\\*(n1"" .ds n1 \\$1
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Pa
+.ie \\n(.$==0 \{\
+\&\\*(pA~\fP
+.\}
+.el \{\
+. ds sV \\*(pA
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Sy
+.ie \\n(.$==0 \{\
+. tm Usage: .Sy Symbolic Text ... \\*(Pu
+. \}
+.el \{\
+. ds sV \\*(sY
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Ms
+.ie \\n(.$==0 \{\
+. tm Usage: .Ms Math Symbol ... \\*(Pu
+. \}
+.el \{\
+. ds sV \\*(sY
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de Va
+.ie \\n(.$==0 \{\
+. tm Usage: .Va variable_name(s) ... \\*(Pu
+.\}
+.el \{\
+. ds sV \\*(vA
+. nr cF \\n(.f
+. ie "\\*(iM"" .ds f1 \&\\*(sV
+. el .as f1 \&\\*(sV
+. nB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+.\}
+..
+.\"
+.de nB
+.hy 0
+.if \\n(.$==0 .tm Usage error: called with empty arguments (empty quotes)?
+.ie \\n(.$>1 \{\
+. rZ \\$1
+. ie \\n(rZ .as f1 \&\f\\n(cF\\$1\fP
+. el .as f1 \&\\$1
+. rZ \\$2
+. if !\\n(rZ \{\
+. ie !"\\*(iM""\{\
+.\" I surrender
+. if "\\*(iM"Tp" .as f1 \&\ \&
+. if "\\*(iM"Dp" .as f1 \&\ \&
+. if "\\*(iM"Op" .as f1 \&\ \&
+. if "\\*(iM"Cx" .as f1 \&\ \&
+. if "\\*(iM"Dq" .as f1 \& \&
+. if "\\*(iM"Sq" .as f1 \& \&
+. if "\\*(iM"Pq" .as f1 \& \&
+. if "\\*(iM"Aq" .as f1 \& \&
+. \}
+. el .as f1 \& \&
+. \}
+. nB \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.\}
+.el \{\
+. rZ \\$1
+. ie \\n(rZ .as f1 \&\f\\n(cF\\$1
+. el .as f1 \&\\$1\f\\n(cF
+. if "\\*(iM"" \{\&\\*(f1
+. ds f1
+. \}
+. hy
+.\}
+..
+.de fB
+.hy 0
+.if \\n(.$==0 .tm Usage error: called with empty arguments (empty quotes)?
+.ie \\n(.$>1 \{\
+. rZ \\$1
+. ie \\n(rZ .as f1 \&\f\\n(cF\\$1\fP
+. el \{\
+. ie "\\$1"-" .as f1 \&\-\-
+. el .as f1 \&\-\\$1
+. \}
+. rZ \\$2
+. if !\\n(rZ \{\
+. ie !"\\*(iM""\{\
+.\" I surrender
+. if "\\*(iM"Tp" .as f1 \&\ \&
+. if "\\*(iM"Dp" .as f1 \&\ \&
+. if "\\*(iM"Op" .as f1 \&\ \&
+. if "\\*(iM"Cx" .as f1 \&\ \&
+. if "\\*(iM"Dq" .as f1 \& \&
+. if "\\*(iM"Sq" .as f1 \& \&
+. if "\\*(iM"Pq" .as f1 \& \&
+. if "\\*(iM"Aq" .as f1 \& \&
+. \}
+. el .as f1 \& \&
+. \}
+. fB \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.\}
+.el \{\
+. rZ \\$1
+. ie \\n(rZ .as f1 \&\f\\n(cF\\$1
+. el \{\
+. ie "\\$1"-" .as f1 \&\-\-\f\\n(cF
+. el .as f1 \&\-\\$1\f\\n(cF
+. \}
+. if "\\*(iM"" \{\&\\*(f1
+. ds f1
+. \}
+. hy
+.\}
+..
+.\"
+.\" Single quoted Items
+.\" eF, sB g[0-9] and f2
+.de Sq
+.nr eF 0
+.ie \\n(.$==0 \{\
+. ie "\\*(iM"" \&\\*(sL\&\\*sR
+. el .as f1 \&\\*(sL\&\\*(sR
+.\}
+.el \{\
+. ie "\\*(iM"" \{\
+. ds f1 \&\\*(sL
+. ds iM Sq
+. \}
+. el .as f1 \&\\*(sL
+. sB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ie \\n(eF>0 .\\*(g1 \\*(g2 \\*(g3 \\*(g4 \\*(g5 \\*(g6 \\*(g7 \\*(g8
+. el .as f1 \\*(g0
+. as f1 \\*(sR
+. if !"\\*(f2"" .as f1 \\*(f2
+. if "\\*(iM"Sq" \{\
+\&\\*(f1
+. ds f1
+. ds iM
+. \}
+. ds f2
+. rm g0 g1 g2 g3 g4 g5 g6 g7 g8 g9
+. nr eF 0
+.\}
+..
+.\"
+.\" Double quoted Items
+.de Dq
+.nr Ef 0
+.ie \\n(.$==0 \{\
+. ie "\\*(iM"" \&\\*(Lq\&\\*(Rq
+. el .as f1 \&\\*(Lq\&\\*(Rq
+.\}
+.el \{\
+. ie "\\*(iM"" \{\
+. ds f1 \&\\*(Lq
+. ds iM Dq
+. \}
+. el .as f1 \&\\*(Lq
+. Sb \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ie \\n(Ef>0 .\\*(k1 \\*(k2 \\*(k3 \\*(k4 \\*(k5 \\*(k6 \\*(k7 \\*(k8
+. el .as f1 \\*(k0
+. as f1 \\*(Rq
+. if !"\\*(f4"" .as f1 \\*(f4
+. if "\\*(iM"Dq" \{\
+\&\\*(f1
+. ds f1
+. ds iM
+. \}
+. ds f4
+. rm k0 k1 k2 k3 k4 k5 k6 k7 k8 k9
+. nr Ef 0
+.\}
+..
+.\"
+.\" Parenthesis quoted Items
+.de Pq
+.nr pQ 0
+.ie \\n(.$==0 \{\
+. ie "\\*(iM"" \&(\&)
+. el .as f1 \&(\&)
+.\}
+.el \{\
+. ie "\\*(iM"" \{\
+. ds f1 \&(
+. ds iM Pq
+. \}
+. el .as f1 \&(
+. pB \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ie \\n(pQ>0 .\\*(y1 \\*(y2 \\*(y3 \\*(y4 \\*(y5 \\*(y6 \\*(y7 \\*(y8
+. el .as f1 \\*(y0
+. as f1 \&)
+. if !"\\*(f3"" .as f1 \\*(f3
+. if "\\*(iM"Pq" \{\
+\&\\*(f1
+. ds f1
+. ds iM
+. \}
+. ds f3
+. rm y0 y1 y2 y3 y4 y5 y6 y7 y8 y9
+. nr pQ 0
+.\}
+..
+.\" eF, sB g[0-9] and f2
+.de sB
+.hy 0
+.ie \\n(.$==0 .tm Sick Logic: macro sB
+.el \{\
+. ie \\n(eF>=1 .nr eF \\n(eF+1
+. el \{\
+. mN \\$1
+. if \\n(mN .nr eF \\n(eF+1
+. \}
+. rZ \\$1
+. ie \\n(rZ .as f2 \\$1
+. el \{\
+. ie \\n(eF<1 .as g\\n(eF \\$1
+. el .as g\\n(eF \\$1
+. \}
+. if \\n(.$>1 \{\
+. rZ \\$2
+. if \\n(rZ==0 \{\
+. if \\n(eF<1 \{\
+. as g\\n(eF \& \&
+. \}
+. \}
+. sB \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.de Sb
+.hy 0
+.ie \\n(.$==0 .tm Sick Logic: macro Sb
+.el \{\
+. ie \\n(Ef>=1 .nr Ef \\n(Ef+1
+. el \{\
+. mN \\$1
+. if \\n(mN .nr Ef \\n(Ef+1
+. \}
+. rZ \\$1
+. ie \\n(rZ .as f4 \\$1
+. el \{\
+. ie \\n(Ef<1 .as k\\n(Ef \\$1
+. el .as k\\n(Ef \\$1
+. \}
+. if \\n(.$>1 \{\
+. rZ \\$2
+. if \\n(rZ==0 \{\
+. if \\n(Ef<1 \{\
+. as k\\n(Ef \& \&
+. \}
+. \}
+. Sb \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.de pB
+.hy 0
+.ie \\n(.$==0 .tm Sick Logic: macro pB
+.el \{\
+. ie \\n(pQ>=1 .nr pQ \\n(pQ+1
+. el \{\
+. mN \\$1
+. if \\n(mN .nr pQ \\n(pQ+1
+. \}
+. rZ \\$1
+. ie \\n(rZ .as f3 \\$1
+. el \{\
+. ie \\n(pQ<1 .as y\\n(pQ \\$1
+. el .as y\\n(pQ \\$1
+. \}
+. if \\n(.$>1 \{\
+. rZ \\$2
+. if \\n(rZ==0 \{\
+. if \\n(pQ<1 \{\
+. as y\\n(pQ \& \&
+. \}
+. \}
+. pB \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.de aQ
+.hy 0
+.ie \\n(.$==0 .tm Bad Syntax: .Aq
+.el \{\
+. ie \\n(aQ>=1 .nr aQ \\n(aQ+1
+. el \{\
+. mN \\$1
+. if \\n(mN .nr aQ \\n(aQ+1
+. \}
+. rZ \\$1
+. ie \\n(rZ .as aZ \\$1
+. el \{\
+. ie \\n(aQ<1 .as a\\n(aQ \\$1
+. el .as a\\n(aQ \\$1
+. \}
+. if \\n(.$>1 \{\
+. rZ \\$2
+. if \\n(rZ==0 \{\
+. if \\n(aQ<1 \{\
+. as a\\n(aQ \& \&
+. \}
+. \}
+. aQ \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.\" Angle Bracket Quoted Items
+.de Aq
+.nr aQ 0
+.ie \\n(.$==0 \{\
+. ie "\\*(iM"" \&<\&>
+. el .as f1 \&<\&>
+.\}
+.el \{\
+. ie "\\*(iM"" \{\
+. ds f1 \&<
+. ds iM Aq
+. \}
+. el .as f1 \&<
+. aQ \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+. ie \\n(aQ>0 .\\*(a1 \\*(a2 \\*(a3 \\*(a4 \\*(a5 \\*(a6 \\*(a7 \\*(a8
+. el .as f1 \\*(a0
+. as f1 \&>
+. if !"\\*(aZ"" .as f1 \\*(aZ
+. if "\\*(iM"Aq" \{\
+\&\\*(f1
+. ds f1
+. ds iM
+. \}
+. ds aZ
+. rm a0 a1 a2 a3 a4 a5 a6 a7 a8
+. nr aQ 0
+.\}
+..
+.\" macro Name test, return macro register value if true
+.de mN
+.nr mN 0
+.if \A'\\$1' \{\
+. sW \\$1
+. if \\n(sW==2 \{\
+. if \\n(\\$1 .nr mN \\n(\\$1
+. \}
+.\}
+..
+.\" Punctuation test (using z registers), return 1 if true
+.de rZ
+.nr rZ 0
+.if \A'\\$1' \{\
+. sW \\$1
+. if \\n(sW==1 \{\
+. if \\n(z\\$1==1 \{\
+. nr rZ 1
+. \}
+. \}
+.\}
+..
+.\"
+.\" sW returns number of characters in a string
+.if t \{\
+.nr fW \w'\fC,'
+.de sW
+.nr sW \w'\fC\\$1'
+.\}
+.if n \{\
+.nr fW \w'0'
+.de sW
+.nr sW \w'\\$1'
+.\}
+.ie \\n(sW>=\\n(fW \{\
+. ie \\n(sW%\\n(fW .nr sW (\\n(sW/\\n(fW)+1
+. el .nr sW \\n(sW/\\n(fW
+.\}
+.el .nr sW 0
+..
+.\" Option Expression -
+.\" TODO - add line overflow check (right!)
+.nr eP 0
+.ds e1
+.nr oE 0
+.nr hP 0
+.ds hP
+.nr Ep 0
+.de Op
+.hy 0
+.if "\\*(iM"" \{\
+. ds iM Op
+. ds f1 \&
+.\}
+.as f1 \&\\*(lB
+.\" .tm Op: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.dO \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.ie !"\\$1"Cx" .oE
+.el .nr oE \\n(oE+1
+..
+.\"
+.\" just for mike, with every bite of garlic in mind (oops, i mean burp).
+.\" dO: go dOwn an argument vector and test each argument to see if
+.\" a macro name or punctuation. stash in respective place along
+.\" with its arguments.
+.nr oO 0
+.nr oP 0
+.nr aO 0
+.de dO
+.mN \\$1
+.ie \\n(mN \{\
+. if \\n(oP \{\
+. if \\n(hP \{\
+. nr oZ 1
+. oZ
+. Oz
+. \}
+. if \\n(e1==1 \{\
+.\\*(e1 \\*(e2 \\*(e3 \\*(e4 \\*(e5 \\*(e6 \\*(e7 \\*(e8 \\*(e9
+. \}
+. uO
+. if !(\\n(oO:\\n(aO) .as f1 \& \&
+. \}
+. ie "\\$1"Op" \{\
+. as f1 \&\\*(lB
+. nr aO \\n(aO+1
+. \}
+. el \{\
+. nr eP \\n(eP+1
+. ds e\\n(eP \\$1
+. nr e\\n(eP 1
+. \}
+.\}
+.el \{\
+.\" .tm dO: $1: \\$1: eP \\n(eP e[\\n(eP]: \\*(e\\n(ePEE
+. rZ \\$1
+. ie \\n(rZ \{\
+.\" .tm dO:rZ: $1: \\$1: eP \\n(eP e[\\n(eP]: \\*(e\\n(eP
+. nr hP \\n(hP+1
+. ds h\\n(hP \\$1
+. \}
+. el \{\
+.\" .tm dO:word $1: \\$1: eP \\n(eP e[\\n(eP]: \\*(e\\n(ePEE
+. if \\n(eP==0:\\n(e\\n(eP==1 .nr eP \\n(eP+1
+. if \\n(eZ .as e\\n(eP \& \&
+. as e\\n(eP " \&\\$1
+.\" . ds e\\n(eP \&\\$1
+. nr eZ \\n(eZ+1
+. \}
+.\}
+.nr oP 1
+.ie \\n(.$>1 \{\
+. dO \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.\}
+.el \{\
+. ie \\n(e1 \{\
+.\\*(e1 \\*(e2 \\*(e3 \\*(e4 \\*(e5 \\*(e6 \\*(e7 \\*(e8 \\*(e9
+. \}
+. el \{\
+. as f1 \\*(e1
+. \}
+.\}
+..
+.\" handle old style arguments such as the arg -Idir
+.\" in adb, .Oo is a toggle.
+.de Oo
+.ie \\n(oO .nr oO 0
+.el .nr oO 1
+..
+.\" stash punctuation
+.de oZ
+.if \\n(hP>=\\n(oZ \{\
+. nr eP \\n(eP+1
+. ds e\\n(eP \\*(h\\n(oZ
+. nr oZ \\n(oZ+1
+. oZ
+.\}
+..
+.\" clean up punctuation vector
+.de Oz
+.if \\n(hP>0 \{\
+. rm h\\n(hP
+. nr hP \\n(hP-1
+. Oz
+.\}
+..
+.\" uO: go back up created vector cleaning it up along the way
+.de uO
+.if \\n(eP>0 \{\
+. rm e\\n(eP
+. rr e\\n(eP
+. nr eP \\n(eP-1
+. nr oP 0
+. nr eZ 0
+. uO
+.\}
+..
+.\" option end
+.de oE
+.uO
+.ie \\n(hP \{\
+. as f1 \\*(rB\\*(h1\\*(h2\\*(h3
+. Oz
+. nr oZ 0
+.\}
+.el \{\
+. as f1 \\*(rB
+.\}
+.ie "\\*(iM"Op" \{\
+. if \\n(aO .aO
+.if t \{\
+. if (\\n(.lu-\\n(.ku-\\n(.ou-(2*\\n(fWu))<\w'\fC\\*(f1'u .br
+.\}
+.if n \{\
+. nr aa \w'\\*(f1'u
+.\" . nr qq \\n(.lu-\\n(.ku-\\n(.ou
+.\" \&aa == \\n(aa, f1==\\*(f1, qq==\\n(qq
+. if (\\n(.lu-\\n(.ku-\\n(.ou-\\n(aau)<=(8*\\n(fWu) .br
+.\}
+\&\\*(f1
+. ds iM
+. ds f1
+. hy
+.\}
+.el .nr oE \\n(oE-1
+..
+.de aO
+.as f1 \\*(rB
+.nr aO \\n(aO-1
+.if \\n(aO >0 .aO
+..
+.\"
+.de Xr
+.if \\n(.$<=1 \{\
+. ie \\n(.$==1 \{\
+. if !"\\*(iM"" .as f1 \&\\*(xR\\$1\fP
+. if "\\*(iM"" \&\\*(xR\\$1\fP
+. \}
+. el .tm Xr Usage: .Xr manpage_name [section#] \\*(Pu
+.\}
+.if \\n(.$==2 \{\
+. rZ \\$2
+. ie "\\*(iM"" \{\
+. ie \\n(rZ \&\\*(xR\\$1\fP\\$2
+. el \&\\*(xR\\$1\fP(\\$2)
+. \}
+. el \{\
+. ie \\n(rZ .as f1 \&\\*(xR\\$1\fP\\$2
+. el .as f1 \&\\*(xR\\$1\fP(\\$2)
+. \}
+.\}
+.if \\n(.$>=3 \{\
+. rZ \\$2
+. ie \\n(rZ \{\
+. ie !"\\*(iM"" .as f1 \&\\*(xR\\$1\fP\\$2\\$3\\$4\\$5\\$6\\$7\\$8
+. el \&\\*(xR\\$1\fP\\$2\\$3\\$4\\$5\\$6\\$7\\$8
+. \}
+. el \{\
+. rZ \\$3
+. ie \\n(rZ \{\
+. if !"\\*(iM"" \{\
+. as f1 \&\\*(xR\\$1\fP(\\$2)\\$3\\$4\\$5\\$6\\$7\\$8
+. \}
+. if "\\*(iM"" \{\
+\&\\*(xR\\$1\fP(\\$2)\\$3\\$4\\$5\\$6\\$7\\$8
+. \}
+. \}
+. el \{\
+. tm rZ = \\n(rZ the arg is \\$3
+. tm Xr-XX Usage: .Xr manpage_name [section#] \\*(Pu
+. \}
+. \}
+.\}
+..
+.\"
+.\"
+.de Ex
+.tm Ex defunct, Use .Dl: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+..
+.\" Display (one) Line of text.
+.de Dl
+.ie "\\*(iM"" \{\
+' ta .5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i 6.5i
+. in \\n(.iu+\\n(Dsu
+. mN \\$1
+. ie \\n(mN .\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. el \{\
+. nr cF \\n(.f
+.\" Literal font is none specified
+\&\\*(lI\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. ft \\n(cF
+. \}
+. in \\n(.iu-\\n(Dsu
+.\}
+.el \{\
+. mN \\$1
+. ie \\n(mN .\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+. el \{\
+. nr cF \\n(.f
+. ds f1 \&\\*(lI\\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+. as f1 \&\f\\n(cF
+. \}
+.\}
+..
+.\"
+.\"
+.\" user set Tagged Paragraph Width (used in both Dp and Tp)
+.de Tw
+.ie \\n(.$==0 \{\
+. nr aa 0
+.\}
+.el \{\
+. mN \\$1
+. ie \\n(sW>2 \{\
+. nr tW (\\n(sW+3)*\\n(fWu)
+. \}
+. el \{\
+. ie \\n(mN .nr tW \\n(mN
+. el .nr tW \\$1
+. \}
+. nr tF 1
+.\}
+..
+.\"
+.de Dw
+.Tw \\$1
+..
+.\"
+.de Di
+.ie \\n(.$==0 \{\
+. nr tI \\n(Dsu
+.\}
+.el \{\
+. sW \\$1
+. if \\n(sW>=2 \{\
+. nr tI \\$1u
+. \}
+. if \\n(sW<2 \{\
+. if "\\$1"L" \{\
+. nr tI 0
+. \}
+. \}
+.\}
+..
+.\" tagged paragraph
+.\" initialize baby stack variables
+.nr np 0
+.nr p1 0
+.ds s\n(np
+.\"
+.de Tp
+.ie "\\$1"" .pE p s np
+.el \{\
+. ds iM Tp
+. mN \\$1
+. ie \\n(tF \{\
+. ds tC Tw
+. nr tC 1
+. nr tF 0
+. \}
+. el \{\
+. if !"Tw"\\*(s\\n(np" \{\
+. ie \\n(mN \{\
+. ds tC \\$1
+. nr tW \\n(mN
+. \}
+. el \{\
+. ds tC Tx
+. nr tW \\n(Tx
+. \}
+. if !"\\*(tC"\\*(s\\n(np" .nr tC 1
+. \}
+. \}
+. sp \\n(vVu
+. if !\\n(cR .ne 2
+. if \\n(tC \{\
+. nr np \\n(np+1
+. nr p\\n(np \\n(tW
+. ds s\\n(np \\*(tC
+. nr tC 0
+. ds tC
+. in \\n(.iu+\\n(p\\n(npu
+. \}
+. ie \\n(mN \{\
+. ds f1
+. \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. if !"\\$1"Cx" .pT st p np
+. \}
+. el \{\
+. br
+. ev 1
+. fi
+. di Td
+\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. br
+. di
+. ev
+. na
+. ds tD \\*(Td\\
+. pT di p np
+. \}
+.\}
+..
+.\"
+.\"
+.\" Complex Expression Macro
+.\"
+.\" TODO: add length across line boundary check (like Li)
+.de Cx
+.hy 0
+.ie \\n(.$==0 \{\
+. if "\\*(iM"Cx" \{\
+. ds iM
+. if \\n(oE .oE
+\&\\*(f1
+. ds f1
+. \}
+. if "\\*(iM"Tp" .pT st p np
+. if "\\*(iM"Dp" .pT st q mp
+.\}
+.el \{\
+. if "\\*(iM"" \{\
+. ds iM Cx
+. ds f1 \&
+. \}
+. mN \\$1
+.\" Here are the args: `\\$1' `\\$2' `\\$3' `\\$4'
+. ie \\n(mN .\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. el \{\
+. as f1 \&\\$1
+. if \\n(.$>1 .Cx \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.\" Prefix string in default font to content specified string
+.de Pf
+.Cx \\$1
+.\\$2 \\$3 \\$4 \\$5
+.Cx
+..
+.\" Suffix string in default font to content specified string
+.de Sf
+.Cx \\$1 \\$2
+.Cx \\$3
+.Cx
+..
+.\" Simple Option Begin
+.de Ob
+.hy 0
+.ie "\\*(iM"" \{\
+. ev 2
+. fi
+. di oB
+.\}
+.el \{\
+.tm shouldn't be here
+. as f1 \&[
+. mN \\$1
+. ie \\n(mN .\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. el \{\
+. as f1 \&\\$1
+. if \\n(.$>1 .Oc \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+.\}
+..
+.de Oc
+.as f1 \&\\$1
+.if \\n(.$>1 .Oc \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+..
+.de Oe
+.hy 0
+.ie "\\*(iM"" \{\
+. br
+. di
+. ev
+. ds bO \\*(oB\\
+\&[\\*(bO\&]
+.\}
+.el \{\
+. as f1 \&]
+.\}
+..
+.\" White space for Cx
+.de Ws
+.Cx \&\ \&
+..
+.\" tagged paragraph
+.\" initialize baby stack variables
+.nr mp 0
+.nr q1 0
+.ds r\n(np
+.\"
+.\" Complex Dp tag
+.de Dc
+.Dp Cx \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+..
+.\" Complex Tp tag
+.de Tc
+.Tp Cx \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+..
+.\" Tag with a flag and an argument with a space
+.de Ta
+.if "\\$2"" \{\
+. Tp Fl \\$1
+.\}
+.el \{\
+. Tp Fl \\$1
+. Cx \&\ \&
+. Ar \\$2 \\$3
+. Cx
+.\}
+..
+.de Da
+.Dp Cx Fl \\$1
+.Ws
+.Ar \\$2 \\$3
+.Cx
+..
+.de To
+.Tp Cx Fl \\$1
+.Ar \\$2 \\$3
+.Cx
+..
+.de Do
+.Dp Cx Fl \\$1
+.Ar \\$2 \\$3
+.Cx
+..
+.\" Blended tag toggle
+.de Bt
+.ie \\n(tP==0 .nr tP 1
+.el .nr tP 0
+..
+.\" Bullet paragraph
+.de Bu
+.Tp Sy \&\(bu
+..
+.\" Display tagged paragraph
+.de Dp
+.ie "\\$1"" \{\
+. pE q r mp
+. sp \\n(vVu
+.\}
+.el \{\
+. ds iM Dp
+. mN \\$1
+. ie \\n(tF \{\
+. ds tC Tw
+. nr tC 1
+. nr tF 0
+. \}
+. el \{\
+. if !"Tw"\\*(r\\n(mp" \{\
+. ie \\n(mN \{\
+. ds tC \\$1
+. nr tW \\n(mN
+. \}
+. el \{\
+. ds tC Tx
+. nr tW \\n(Tx
+. \}
+. if !"\\*(tC"\\*(r\\n(mp" .nr tC 1
+. \}
+. \}
+. if !\\n(cR .ne 2
+. if \\n(tC \{\
+. nr mp \\n(mp+1
+. nr q\\n(mp \\n(tW
+. ds r\\n(mp \\*(tC
+. nr tC 0
+. ds tC
+. ie \\n(tIu==\\n(Dsu .nr i\\n(mp \\n(Dsu
+. el \{\
+. nr i\\n(mp \\n(tIu
+. nr tI \\n(Dsu
+. \}
+. in \\n(.iu+\\n(i\\n(mpu
+. sp \\n(vVu
+. in \\n(.iu+\\n(\\q\\n(mpu
+. \}
+. ie \\n(mN \{\
+. \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. if !"\\$1"Cx" .pT st q mp
+. \}
+. el \{\
+. br
+. ev 1
+. fi
+. di Td
+\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. br
+. di
+. ev
+. na
+. ds tD \\*(Td\\
+. pT di q mp
+. \}
+.\}
+..
+.\"
+.\" .pE number_stack string_stack counter
+.de pE
+.ie "\\$3"mp" \{\
+. in \\n(.iu-(\\n(\\$1\\n(\\$3u)-(\\n(i\\n(mpu)
+. rr i\\n(mp
+.\}
+.el .in \\n(.iu-\\n(\\$1\\n(\\$3u
+.\" .in \\n(.iu-\\n(\\$1\\n(\\$3u
+.if \\n(\\$3<=0 .tm Extraneous call .Tp or .Dp
+.rr \\$1\\n(\\$3
+.rm \\$2\\n(\\$3
+.nr \\$3 \\n(\\$3-1
+.ds iM
+..
+.\"
+.\" .pT [st or di] number_stack counter
+.de pT
+.ie "\\$1"st" \{\
+. nr bb \\n(\\$2\\n(\\$3u
+. ti -\\n(bbu
+. ie (\\n(\\$2\\n(\\$3u-2n)<=\w'\\*(f1'u \{\&\\*(f1\\*(tP
+. if \\n(tP==0 .br
+. \}
+. el \\*(f1\h'|\\n(\\$2\\n(\\$3u'\c
+.\}
+.el \{\
+. ti -\\n(\\$2\\n(\\$3u
+. ie (\\n(\\$2\\n(\\$3u-2n)<=\\n(dlu \{\&\\*(tD\\*(tP
+. if !\\n(tP .br
+. \}
+. el \\*(tD\h'|\\n(\\$2\\n(\\$3u'\c
+. if t 'ad
+.\}
+. ds iM
+. ds f1
+'fi
+..
+.\"
+.\" The new SH
+.\"
+.de Sh
+.\" set Sh state off, check for list state before calling indent (.In)
+.nr nS 0
+.nr sE 0
+.ie "\\$1"NAME" \{\
+.\" name state on, housekeep (headers & footers)
+. hK
+' in 0
+.\}
+.el \{\
+. if "\\$1"SYNOPSIS" .nr nS 1
+. in 0
+.\}
+.pL
+'sp
+.ns
+.ta .5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i 6.5i
+.if !\\n(cR .ne 3
+'fi
+\&\fB\\$1 \|\\$2 \|\\$3 \|\\$4 \|\\$5 \|\\$6 \|\\$7 \|\\$8 \|\\$9
+\&\fP\&
+.in \\n(.iu+\\n(Tiu
+.if "\\$1"SEE" .nr sE 1
+.ns
+..
+.\"
+.\" Nd minus sign for an en dash used in .Sh Name
+.de Nd
+\&\-\& \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+..
+.de Ss
+.sp
+.ti -.25i
+\&\fB\\$1 \|\\$2 \|\\$3 \|\\$4 \|\\$5 \|\\$6 \|\\$7 \|\\$8 \|\\$9
+\&\fP\&
+.ta .5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i 6.5i
+.if !\\n(cR .ne 2
+.br
+..
+.\" .if "\\$1"Ss" .in \\n(.iu+\\n(sIu
+.\"..
+.\"
+.\"
+.\" Column Macro
+.\"
+.hy 0
+.de Cw
+.ie \\n(.$==0 \{\
+. br
+. in \\n(.iu-\\n(eWu
+. ta .5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i 6.5i
+.\}
+.el \{\
+. Pp
+. if \\n(.$==1 \{\
+. ta \w'\\$1 'u
+. nr eW \w'\\$1 'u
+' in \\n(.iu+\\n(eWu
+. \}
+. if \\n(.$==2 \{\
+. ta \w'\\$1 'u +\w'\\$2 'u
+. nr eW \w'\\$1 'u+\w'\\$2 'u
+' in \\n(.iu+\\n(eWu
+. \}
+. if \\n(.$==3 \{\
+. ta \w'\\$1 'u +\w'\\$2 'u +\w'\\$3 'u
+. nr eW \w'\\$1 'u+\w'\\$2 'u+\w'\\$3 'u
+' in \\n(.iu+\\n(eWu
+. \}
+. if \\n(.$==4 \{\
+. ta \w'\\$1 'u +\w'\\$2 'u +\w'\\$3 'u +\w'\\$4 'u
+. nr eW \w'\\$1 'u+\w'\\$2 'u+\w'\\$3 'u +\w'\\$4 'u
+' in \\n(.iu+\\n(eWu
+. \}
+. if \\n(.$==5 \{\
+.ta \w'\\$1 'u +\w'\\$2 'u +\w'\\$3 'u +\w'\\$4 'u +\w'\\$5 'u
+.nr eW \w'\\$1 'u +\w'\\$2 'u +\w'\\$3 'u +\w'\\$4 'u +\w'\\$5 'u
+' in \\n(.iu+\\n(eWu
+. \}
+.\}
+..
+.de Cl
+.ti -\\n(eWu
+.mN \\$1
+.ie \\n(mN .\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.el \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+..
+.nr dQ 0
+.de Ds
+.ie !"\\$1"" \{\
+. mN d\\$1
+. if \\n(mN \{\
+. nr dQ \\n(dQ+1
+. d\\$1
+. \}
+.\}
+.el .br
+.nf
+..
+.de Df
+.ie !"\\$1"" \{\
+. mN d\\$1
+. if \\n(mN \{\
+. nr dQ \\n(dQ+1
+. d\\$1
+. \}
+.\}
+.el .br
+..
+.de Dn
+\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.nf
+..
+.de dI
+.nr d\\n(dQ \\n(dIu
+.in \\n(.iu+\\n(dIu
+..
+.de dC
+.nr d\\n(dQ (\\n(.l-\\n(.i)/4u
+.in \\n(.iu+\\n(d\\n(dQu
+..
+.de dR
+.nr d\\n(dQ (\\n(.l/3)u
+.in \\n(.iu+\\n(d\\n(dQu
+..
+.de dL
+.nr aa 0
+..
+.de De
+.br
+.if \\n(d\\n(dQ \{\
+. in \\n(.iu-\\n(d\\n(dQu
+. rr d\\n(dQ
+. nr dQ \\n(dQ-1
+.\}
+.fi
+..
+.\"
+.de Fn
+.ie \\n(.$==0 \{\
+. tm Usage: .Fn function_name function_arg(s) ... \\*(Pu
+.\}
+.el \{\
+. nr cF \\n(.f
+. ie \\n(.$==1 .ds f1 \&\\*(nM\\$1\fP\\*(lP\fP\\*(rP\fP
+. el \{\
+. ds f1 \\*(nM\\$1\fP\\*(lP
+. nr aa 0
+. rC \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+. \}
+. if "\\*(iM"" \{\\&\\*(f1
+. ds f1
+. \}
+.\}
+..
+.\"
+.de rC
+.rZ \\$1
+.ie \\n(rZ \{\
+. as f1 \f\\n(cF\\*(rP\f\\n(cF\\$1\\$2\\$3\\$4\\$5\\$6\\$7
+.\}
+.el \{\
+. ie \\n(aa .as f1 \fP, \\*(aR\\$1
+. el .as f1 \\*(aR\\$1
+. nr aa 1
+. ie \\n(.$>1 .rC \\$2 \\$3 \\$4 \\$5 \\$6 \\$7
+. el .as f1 \fP\\*(rP\fP
+.\}
+..
diff --git a/macros/tmac.e b/macros/tmac.e
new file mode 100644
index 000000000..4b77d2383
--- /dev/null
+++ b/macros/tmac.e
@@ -0,0 +1,1559 @@
+.\" @(#)tmac.e 2.31 (Berkeley) 5/21/88
+.\" Modified by James Clark for use with groff.
+.\"
+.\" Copyright (c) 1988 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that this notice is preserved and that due credit is given
+.\" to the University of California at Berkeley. The name of the University
+.\" may not be used to endorse or promote products derived from this
+.\" software without specific prior written permission. This software
+.\" is provided ``as is'' without express or implied warranty.
+.\" %beginstrip%
+.\"
+.\"**********************************************************************
+.\"* *
+.\"* ****** - M E N R O F F / T R O F F M A C R O S ****** *
+.\"* *
+.\"* Produced for your edification and enjoyment by: *
+.\"* Eric Allman *
+.\"* Electronics Research Laboratory *
+.\"* U.C. Berkeley. *
+.\"* current address: *
+.\"* Britton-Lee, Inc. *
+.\"* 1919 Addison Street Suite 105 *
+.\"* Berkeley, California 94704 *
+.\"* *
+.\"* VERSION 2.31 First Release: 11 Sept 1978 *
+.\"* *
+.\"* Documentation is available. *
+.\"* *
+.\"**********************************************************************
+.\"
+.\" Code on .de commands:
+.\" *** a user interface macro.
+.\" &&& a user interface macro which is redefined
+.\" when used to be the real thing.
+.\" $$$ a macro which may be redefined by the user
+.\" to provide variant functions.
+.\" --- an internal macro.
+.\"
+.if \n(pf \
+. nx /dev/null
+.\" *** INTERNAL GP MACROS ***
+.de @C \" --- change ev's, taking info with us
+.nr _S \\n(.s
+.nr _V \\n(.v
+.nr _F \\n(.f
+.if \n(.g \
+\{\
+. nr _C \\n(.C
+. cp 0
+. ds _A \\n[.fam]
+.\}
+.nr _I \\n(.i
+.ev \\$1
+.ps \\n(_S
+.vs \\n(_Vu
+.ft \\n(_F
+.if \n(.g \
+\{\
+. fam \\*(_A
+. cp \\n(_C
+.\}
+'in \\n(_Iu
+.xl \\n($lu
+.lt \\n($lu
+.rr _S
+.rr _V
+.rr _F
+.rr _I
+.ls 1
+'ce 0
+..
+.de @D \" --- determine display type (Indent, Left, Center)
+.ds |p "\\$3
+.nr _d \\$1
+.ie "\\$2"C" \
+. nr _d 1
+.el .ie "\\$2"L" \
+. nr _d 2
+.el .ie "\\$2"I" \
+. nr _d 3
+.el .ie "\\$2"M" \
+. nr _d 4
+.el \
+. ds |p "\\$2
+..
+.de @z \" --- end macro
+.if \n@>1 .tm >> @z, .z=\\n(.z ?a=\\n(?a
+.if !"\\n(.z"" \
+\{\
+. tm Line \\n(c. -- Unclosed block, footnote, or other diversion (\\n(.z)
+. di
+. ex
+.\}
+.if \\n(?a \
+. bp \" force out final table
+.rm bp
+.rm @b\" \" don't start another page
+.wh -1p @m
+.br
+.if \n@>1 .tm << @z
+..
+.\" *** STANDARD HEADERS AND FOOTERS ***
+.de he \" *** define header
+.ie !\\n(.$ \
+\{\
+. rm |4
+. rm |5
+.\}
+.el \
+\{\
+. ds |4 "\\$*
+. ds |5 "\\$*
+.\}
+..
+.de eh \" *** define even header
+.ie !\\n(.$ \
+. rm |4
+.el \
+. ds |4 "\\$*
+..
+.de oh \" *** define odd header
+.ie !\\n(.$ \
+. rm |5
+.el \
+. ds |5 "\\$*
+..
+.de fo \" *** define footer
+.ie !\\n(.$ \
+\{\
+. rm |6
+. rm |7
+.\}
+.el \
+\{\
+. ds |6 "\\$*
+. ds |7 "\\$*
+.\}
+..
+.de ef \" *** define even foot
+.ie !\\n(.$ \
+. rm |6
+.el \
+. ds |6 "\\$*
+..
+.de of \" *** define odd footer
+.ie !\\n(.$ \
+. rm |7
+.el \
+. ds |7 "\\$*
+..
+.de ep \" *** end page (must always be followed by a .bp)
+.if \\n(nl>0 \
+\{\
+. wh 0
+. rs
+. @b
+.\}
+..
+.\" *** INTERNAL HEADER AND FOOTER MACROS ***
+.de @h \" --- header
+.if \n@>1 .tm >> @h %=\\n% ?a=\\n(?a ?b=\\n(?b ?w=\\n(?w
+.if (\\n(.i+\\n(.o)>=\\n(.l \
+. tm Line \\n(c. -- Offset + indent exceeds line length
+.\" initialize a pile of junk
+.nr ?h \\n(?H \" transfer "next page" to "this page"
+.rr ?H
+.nr ?c \\n(?C
+.rr ?C
+.rn |4 |0
+.rn |5 |1
+.rn |6 |2
+.rn |7 |3
+.nr _w 0 \" reset max footnote width
+.nr ?W 0 \" no wide floats this page (yet)
+.nr ?I 1
+.\" begin actual header stuff
+.ev 2
+.rs
+.@m \" output cut mark
+.if \\n(hm>0 \
+. sp |\\n(hmu \" move to header position
+.@t $h \" output header title
+.if \\n(tm<=0 \
+. nr tm \n(.Vu
+.sp |\\n(tmu \" move to top of text
+.ev
+.mk _k \" for columned output
+.if \\n(?n .nm 1 \" restore line numbering if n1 mode
+.nr $c 1 \" set first column
+.if \n@>4 .tm -- @h >> .ns nl=\\n(nl %=\\n% _k=\\n(_k tm=\\n(tm
+.ie \\n(?s \
+\{\
+. rr ?s
+. rs
+' @b
+.\}
+.el \
+. @n \" begin the column
+.if \n@>2 .tm << @h
+..
+.if \nv=2 \
+\{\
+. de @m \" --- output cut mark (only on C/A/T-style)
+. @O 0
+. lt 7.5i
+. tl '\(rn''\(rn'
+. @O
+. lt
+..
+.\}
+.de @n \" --- new column or page
+.if \n@>3 .tm >> @n nl=\\n(nl %=\\n% ?f=\\n(?f ?o=\\n(?o
+.if \\n(bm<=0 \
+. nr bm \\n(.Vu
+.if (\\n(_w<=\\n($l)&(\\n(?W=0) \
+\{\
+. nr _b (\\n(ppp*\\n($ru)/200u \" compute fudge factor (must be < 1P)
+. if \\n(_bu>((\\n(bmu-\\n(fmu-((\\n(tpp*\\n($ru)/100u))/2u) \
+. nr _b (\\n(ppp*\\n($ru)/100u-\n(.Vu
+. nr _b +\\n(bmu
+.\}
+.nr _B \\n(_bu
+.ch @f
+.wh -\\n(_bu @f
+.nr _b +(\\n(ppp*\\n($ru)/100u \" add 1 paragraph v in case of sweep past
+.if \n@>2 .tm @n .p=\\n(.p bm=\\n(bm _b=\\n(_b _B=\\n(_B
+.nr ?f 0 \" reset footnote flag
+.if \\n(?o \
+\{\
+. (f _ \" reprocess footnotes which run off page
+. nf
+. |o
+. fi
+. )f
+. rm |o
+.\}
+.nr ?o 0
+.if \\n(?T \
+\{\
+. nr _i \\n(.i
+. in \\n($iu
+. |h \" output the table header
+. in \\n(_iu
+. rr _i
+. mk #T \" for tbl commands
+. ns
+.\}
+.if (\\n(?a)&((\\n($c<2):(\\n(?w=0)) \
+\{\
+. nr ?a 0 \" output floating keep
+. @k |t
+. if \\n(?w \
+. mk _k \" don't overstrike wide keeps
+. nr ?w 0
+.\}
+.os
+.$H \" special column header macro
+.ns
+..
+.de @f \" --- footer
+.if \n@>1 .tm >> @f %=\\n% nl=\\n(nl ?a=\\n(?a ?b=\\n(?b ?f=\\n(?f
+.if \n@>2 .nr VL \\n(.pu-\\n(nlu
+.if \n@>2 .tm @f bm=\\n(bm _B=\\n(_B _b=\\n(_b .p-nl=\\n(VL
+.ec
+.if \\n(?T \
+\{\
+. nr T. 1 \" for tbl commands (to output bottom line)
+. T# 1 \" output the sides and bottom lines
+. br
+.\}
+.ev 2
+.ce 0
+.if \\n(?b \
+\{\
+. nr ?b 0
+. @k |b\" \" output bottom of page tables
+.\}
+.if \\n(?f \
+. @o \" output footnote if present
+.ie \\n($c<\\n($m \
+. @c \" handle new column
+.el \
+. @e \" new page
+.ev
+.if \n@>2 .tm << @f
+..
+.de @o \" --- output footnote
+.nf
+.ls 1
+.in 0
+.if \n@>2 .tm @o last printed text = \\n(nl placing @r trap at -\\n(_B
+.wh -\\n(_Bu @r
+.|f
+.fi
+.if \n@>2 .tm @o triggered @r (?o) = \\n(?o
+.if \\n(?o \
+\{\
+. di \" just in case triggered @r
+. if \\n(dn=0 \
+\{\
+. rm |o
+. nr ?o 0
+. \}
+. nr dn \\n(_D
+. rr _D
+.\}
+.rm |f
+.ch @r
+..
+.de @c \" --- new column
+.if \n@>2 .tm >> @c %=\\n%
+.rs
+.sp |\\n(_ku
+.@O +\\n($lu+\\n($su
+.nr $c +1
+.@n
+..
+.de @e \" --- end page
+.if \n@>2 .tm >> @e
+.@O \\n(_ou
+.rs
+.sp |\\n(.pu-\\n(fmu-((\\n(tpp*\\n($ru)/100u) \" move to footer position
+.@t $f \" output footer title
+.nr ?h 0
+.bp
+..
+.de @t \" --- output header or footer title
+.if !\\n(?h \
+\{\
+. sz \\n(tp \" set header/footer type fonts, etc.
+. @F \\n(tf
+. lt \\n(_Lu \" make title span entire page
+. nf
+. \\$1
+. br
+.\}
+..
+.de $h \" $$$ print header
+.rm |z
+.if !\\n(?c \
+\{\
+. if e .ds |z "\\*(|0
+. if o .ds |z "\\*(|1
+.\}
+.if !\(ts\\*(|z\(ts\(ts \
+' tl \\*(|z
+.rm |z
+..
+.de $f \" $$$ print footer
+.rm |z
+.if \\n(?c \
+\{\
+. if e .ds |z "\\*(|0
+. if o .ds |z "\\*(|1
+.\}
+.if \(ts\\*(|z\(ts\(ts \
+\{\
+. if e .ds |z "\\*(|2
+. if o .ds |z "\\*(|3
+.\}
+.if !\(ts\\*(|z\(ts\(ts \
+' tl \\*(|z
+.rm |z
+..
+.de @r \" --- reprocess overflow footnotes
+.if \n@>3 .tm >> @r .z=\\n(.z ?f=\\n(?f ?a=\\n(?a ?b=\\n(?b _b=\\n(_b
+.di |o \" save overflow footnote
+.nr ?o 1
+.nr _D \\n(dn
+.ns
+..
+.\" *** COMMANDS WITH VARIANT DEFINITIONS ***
+.rn bp @b \" --- begin page
+.de bp \" *** begin new page (overrides columns)
+.nr $c \\n($m \" force new page, not new column
+.ie \\n(nl>0 \
+. @b \\$1
+.el \
+\{\
+. if \\n(.$>0 \
+. pn \\$1
+. if \\n(?I \
+. @h \" 'spring' the header trap
+.\}
+.br
+.wh 0 @h \" reset header
+..
+.rn ll xl \" *** special line length (local)
+.de ll \" *** line length (global to environments)
+.xl \\$1
+.lt \\$1
+.nr $l \\n(.l
+.if (\\n($m<=1):(\\n($l>\\n(_L) \
+. nr _L \\n(.l
+..
+.rn po @O \" --- local page offset
+.de po \" *** page offset
+.@O \\$1
+.nr _o \\n(.o
+..
+.\" *** MISCELLANEOUS ROFF COMMANDS ***
+.de hx \" *** suppress headers and footers next page
+.nr ?H 1
+..
+.de ix \" *** indent, no break
+'in \\$1
+..
+.de bl \" *** contiguous blank lines
+.br
+.ne \\$1
+.rs
+.sp \\$1
+..
+.de n1 \" *** line numbering 1
+.nm 1
+.xl -\w'0000'u
+.nr ?n 1
+..
+.de n2 \" *** line numbering 2
+.nm \\$1
+.ie \\n(.$ \
+. xl -\w'0000'u
+.el \
+. xl \\n($lu
+..
+.de pa \" *** new page
+.bp \\$1
+..
+.de ro \" *** roman page numbers
+.af % i
+..
+.de ar \" *** arabic page numbers
+.af % 1
+..
+.de m1 \" *** position one space
+.nr _0 \\n(hmu
+.nr hm \\$1v
+.nr tm +\\n(hmu-\\n(_0u
+.rr _0
+..
+.de m2 \" *** position two space
+.nr tm \\n(hmu+\\n(tpp+\\$1v
+..
+.de m3 \" *** position three space
+.nr bm \\n(fmu+\\n(tpp+\\$1v
+..
+.de m4 \" *** position four space
+.nr _0 \\n(fmu
+.nr fm \\$1v
+.nr bm +\\n(fmu-\\n(_0u
+..
+.de sk \" *** leave a blank page (next page)
+.if \\n(.$>0 \
+. tm Line \\n(c. -- I cannot skip multiple pages
+.nr ?s 1
+..
+.\" *** MISCELLANEOUS USER SUPPORT COMMANDS ***
+.de re \" *** reset tabs (TROFF defines 15 stops default)
+.ta T 0.5i
+..
+.de ba \" *** set base indent
+.ie \\n(.$ \
+. nr $i \\$1n
+.el \
+. nr $i \\n(siu*\\n($0u
+..
+.de hl \" *** draw horizontal line
+.br
+\l'\\n(.lu-\\n(.iu'
+.sp
+..
+.\" *** PARAGRAPHING ***
+.de pp \" *** paragraph
+.lp \\n(piu
+..
+.de lp \" *** left aligned paragraph
+.@p
+.if \\n(.$ \
+. ti +\\$1
+.nr $p 0 1
+..
+.de ip \" *** indented paragraph w/ optional tag
+.if (\\n(ii>0)&(\\n(ii<1n) \
+. nr ii \\n(iin
+.nr _0 \\n(ii
+.if \\n(.$>1 \
+. nr _0 \\$2n
+.@p \\n(_0u
+.if \\w"\\$1" \
+\{\
+. ti -\\n(_0u
+. ie \\w"\\$1">=\\n(_0 \
+\{\
+\&\\$1
+. br
+. \}
+. el \&\\$1\h'|\\n(_0u'\c
+.\}
+.rr _0
+..
+.de np \" *** numbered paragraph
+.if \\n($p<0 \
+. nr $p 0 \" reset number after .bu
+.nr $p +1 \" increment paragraph number
+.@p \w'\0(000)\0'u
+.ti -\w'\0(000)\0'u
+\0(\\n($p)\h'|\w'\0(000)\0'u'\c
+..
+.de bu \" *** bulleted paragraph
+.br
+.if \\n($p<0 \
+. ns \" don't space between .bu paragraphs
+.nr $p 0-1 \" mark "bulleted paragraph" mode
+.@p \w'\0\(bu\0'u
+.ti -\w'\0\(bu\0'u
+\0\(bu\0\c
+..
+.de @p \" --- initialize for paragraph
+.if "\\n(.z"|e" .tm Line \\n(c. -- Unmatched continued equation
+.in \\n($iu+\\n(pou
+.if \\n(.$ \
+. in +\\$1n
+.ce 0
+.fi
+.@F \\n(pf
+.sz \\n(pp
+.sp \\n(psu
+.ne \\n(.Lv+\\n(.Vu
+.ns
+..
+.\" *** SECTION HEADINGS ***
+.de sh \" *** section heading
+.fi
+.if (\\n(si>0)&(\\n(si<1n) \
+. nr si \\n(sin
+.ce 0
+.@d "\\$1" +1 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+.if !"\\$2"_" \
+\{\
+. ds |n \&\\$2
+. $p "\\*(|n" "\\*($n" \\n($0
+. $0 "\\*(|n" "\\*($n" \\n($0
+. rm |n
+.\}
+.nr $p 0 1 \" reset .np count
+..
+.de @d \" --- change section depth
+.if !""\\$1" \
+. nr $0 \\$1
+.if \\n($0&(\\n(.$>1) \
+. nr $\\n($0 \\$2
+.ds $n \&\"
+.ie \\n($0>=1 \
+\{\
+. if '\\n($1'0' \
+. nr $1 1
+. if (\\n(.$>=3) .if !"\\$3"-" \
+. nr $1 \\$3
+. as $n \\n($1
+.\}
+.el \
+. nr $1 0
+.ie \\n($0>=2 \
+\{\
+. if '\\n($2'0' \
+. nr $2 1
+. if (\\n(.$>=4) .if !"\\$4"-" \
+. nr $2 \\$4
+. as $n .\\n($2
+.\}
+.el \
+. nr $2 0
+.ie \\n($0>=3 \
+\{\
+. if '\\n($3'0' \
+. nr $3 1
+. if (\\n(.$>=5) .if !"\\$5"-" \
+. nr $3 \\$5
+. as $n .\\n($3
+.\}
+.el \
+. nr $3 0
+.ie \\n($0>=4 \
+\{\
+. if '\\n($4'0' \
+. nr $4 1
+. if (\\n(.$>=6) .if !"\\$6"-" \
+. nr $4 \\$6
+. as $n .\\n($4
+.\}
+.el \
+. nr $4 0
+.ie \\n($0>=5 \
+\{\
+. if '\\n($5'0' \
+. nr $5 1
+. if (\\n(.$>=7) .if !"\\$7"-" \
+. nr $5 \\$7
+. as $n .\\n($5
+.\}
+.el \
+. nr $5 0
+.ie \\n($0>=6 \
+\{\
+. if '\\n($6'0' \
+. nr $6 1
+. if (\\n(.$>=8) .if !"\\$8"-" \
+. nr $6 \\$8
+. as $n .\\n($6
+.\}
+.el \
+. nr $6 0
+..
+.de sx \" *** heading up, no increment (2.1.1 -> 2.1)
+.ce 0
+.ul 0
+.nr _0 \\n($0-1
+.if \\n(.$ .nr _0 +1
+.if \\n(.$ .nr _0 \\$1
+.@d \\n(_0
+.rr _0
+.$p "" "" \\n($0
+.nr $p 0 1 \" reset .np count
+..
+.de uh \" *** unnumbered section heading
+.$p "\\$1"
+.$0 "\\$1"
+..
+.de $p \" $$$ print section heading
+.if (\\n(si>0)&(\\n(.$>2) \
+. nr $i \\$3*\\n(si
+.in \\n($iu
+.ie !"\\$1\\$2"" \
+\{\
+. sp \\n(ssu \" one of them is non-null
+. ne \\n(.Lv+\\n(.Vu+\\n(psu+((\\n(spp*\\n($ru*\\n(.Lu)/100u)
+. ie \\n(.$>2 \
+. ti -(\\n(siu-\\n(sou)
+. el \
+. ti +\\n(sou
+. @F \\n(sf
+. sz \\n(sp
+. if \\$3>0 \
+. $\\$3
+. if \w"\\$2">0 \\$2.
+. if \w"\\$1">0 \\$1\f1\ \ \&
+.\}
+.el \
+. sp \\n(psu
+.@F \\n(pf
+.sz \\n(pp
+..
+.\" *** COLUMNNED OUTPUT ***
+.de 2c \" *** double columned output
+.br
+.if \\n($m>1 \
+. 1c \" revert to 1c if already 2c
+.nr $c 1
+.nr $m 2
+.if \\n(.$>1 \
+. nr $m \\$2
+.if \\n(.$>0 \
+. nr $s \\$1n \" param 1: column seperation
+.nr $l (\\n(.l-((\\n($m-1)*\\n($s))/\\n($m
+.xl \\n($lu
+.mk _k
+.ns
+..
+.de 1c \" *** single columned output
+.br
+.nr $c 1
+.nr $m 1
+.ll \\n(_Lu \" return to normal output
+.sp |\\n(.hu
+.@O \\n(_ou
+..
+.de bc \" *** begin column
+.sp 24i
+..
+.\" *** FLOATING TABLES AND NONFLOATING BLOCKS ***
+.de (z \" *** begin floating keep
+.if \n@>4 .tm >> (z, .z=\n(.z
+.@D 4 \\$1 \\$2
+.@(
+.nr ?T 0
+..
+.de )z \" *** end floating keep
+.if \n@>4 .tm >> )z, .z=\n(.z
+.sp \\n(zsu
+.@)
+.if \n@>4 .tm -- )z << @), .z=\n(.z
+.rr _0
+.if !\\n(?b \
+. nr dn +(\\n(ppp*\\n($ru)/200u+\\n(zsu
+.nr dl -\n(.H \" fudge factor necessary to make it work
+.ie ((\\n(dn+\n(.V)>=\\n(.t):(\\n(?a):((\\n(dl>\\n($l)&(\\n($c>1)) \
+\{\
+. nr ?a 1
+. if (\\n(dl>\\n($l)&(\\n($m>1) \
+. nr ?w 1 \" mark wider than one column (top)
+. ds |x |t
+.\}
+.el \
+\{\
+. nr ?b 1
+. if (\\n(dl>\\n($l)&(\\n($m>1) \
+. nr ?W 1 \" mark wider than one column (bottom)
+. nr _b +\\n(dnu
+. ch @f -\\n(_bu
+. ds |x |b
+.\}
+.da \\*(|x \" copy to save macro
+.nf
+.ls 1
+.nr ?k 1
+.if \n@>4 .tm -- )z >> \\*(|x
+\!.if \\\\n(nl>(\\\\n(tm+2v) .ne \\n(dnu-\\n(zsu
+.|k\" \" and the body
+.ec
+.if \n@>4 .tm -- )z << \\*(|x, .z=\\n(.z
+.nr ?k 0
+.rm |k\" \" remove the temp macro
+.da
+.in 0
+.ls 1
+.xl \\n($lu
+.ev
+.if \n@>4 .tm << )z, .z=\\n(.z
+..
+.de @k \" --- output floating keep
+.if \n@>4 .tm >> @k, $1=\\$1, .z=\\n(.z
+.ev 1
+.nf
+.ls 1
+.in 0
+.sp \\n(zsu
+.\\$1
+.ec
+.br
+.rm \\$1
+.ev
+.nr ?T 0
+..
+.de (t \" XXX temp ref to (z
+.(z \\$1 \\$2
+..
+.de )t \" XXX temp ref to )t
+.)z \\$1 \\$2
+..
+.de (b \" *** begin block
+.br
+.@D 3 \\$1 \\$2
+.sp \\n(bsu
+.@(
+..
+.de )b \" *** end block
+.br
+.@)
+.if (\\n(bt=0):(\\n(.t<\\n(bt) \
+. ne \\n(dnu \" make it all on one page
+.ls 1
+.nf
+.|k
+.ec
+.fi
+.in 0
+.xl \\n($lu
+.ev
+.rm |k
+.sp \\n(bsu+\\n(.Lv-1v
+..
+.de @( \" --- begin keep
+.if !"\\n(.z"" .tm Line \\n(c. -- Illegal nested keep \\n(.z
+.@M
+.di |k
+\!'rs
+..
+.de @M \" --- set modes for display
+.nr ?k 1
+.@C 1
+.@F \\n(df
+.vs \\n(.sp*\\n($Ru/100u
+.nf
+.if "\\*(|p"F" \
+. fi \" set fill mode if "F" parameter
+.if \\n(_d=4 \
+. in 0
+.if \\n(_d=3 \
+\{\
+. in +\\n(biu
+. xl -\\n(biu
+.\}
+.if \\n(_d=1 \
+. ce 10000
+..
+.de @) \" --- end keep
+.br
+.if !"\\n(.z"|k" .tm Line \\n(c. -- Close of a keep which has never been opened
+.nr ?k 0
+.di
+.in 0
+.ce 0
+..
+.de (c \" *** begin block centered text
+.if "\\n(.z"|c" .tm Line \\n(c. -- Nested .(c requests
+.di |c
+..
+.de )c \" *** end block centered text
+.if !"\\n(.z"|c" .tm Line \\n(c. -- Unmatched .)c
+.br \" force out final line
+.di
+.if \n@>4 .tm >> .)c .l=\\n(.l .i=\\n(.i $i=\\n($i dl=\\n(dl
+.ev 1
+.ls 1
+.in (\\n(.lu-\\n(.iu-\\n(dlu)/2u
+.if \n@>4 .tm -- .)c << .in .l=\\n(.l .i=\\n(.i dl=\\n(dl
+.nf
+.|c
+.ec
+.in
+.ls
+.ev
+.rm |c
+..
+.\" *** BLOCK QUOTES (OR WHATEVER) AND LISTS ***
+.de (q \" *** begin block quote
+.br
+.@C 1
+.fi
+.sp \\n(qsu
+.in +\\n(qiu
+.xl -\\n(qiu
+.sz \\n(qp
+..
+.de )q \" *** end block quote
+.br
+.ev
+.sp \\n(qsu+\\n(.Lv-1v
+.nr ?k 0
+..
+.de (l \" *** begin list
+.br
+.sp \\n(bsu
+.@D 3 \\$1 \\$2
+.@M
+..
+.de )l \" *** end list
+.br
+.ev
+.sp \\n(bsu+\\n(.Lv-1v
+.nr ?k 0
+..
+.\" *** PREPROCESSOR SUPPORT ***
+.\"
+.\" EQN
+.\"
+.de EQ \" *** equation start
+.ec
+.if !\\n(?e \
+\{\
+. if "\\n(.z"|e" .tm Line \\n(c. -- Nested .EQ request
+. @D 1 "\\$1" "\\$2"
+. @C 2
+. di |e
+.\}
+.ls 1
+.in 0
+.nf
+..
+.de EN \" *** equation end
+.br
+.ie "\\$1"C" \
+\{\
+. nr ?e 1
+. sp \\n(esu
+.\}
+.el \
+\{\
+. nr ?e 0
+. di
+. if \\n(dn \
+. @q \" actual equation output
+. rm |e
+. ev
+.\}
+..
+.de @q \" --- equation output
+.nr _Q \\n(dnu
+.ev
+.sp \\n(esu \" output rest of preceeding text
+.@C 2 \" .ev 2 may be jumbled from header
+.ie \\n(_d=1 \
+. in (\\n(.lu+\\n($iu-\\n(dlu)/2u
+.el .ie \\n(_d=2 \
+. in \\n($iu
+.el .ie \\n(_d=3 \
+. in \\n(biu+\\n($iu
+.el .if \\n(_d=4 \
+. in 0
+.if !"\\n(.z"" \!.ne \\n(_Qu
+.ne \\n(_Qu+\n(.Vu \" keep it on one page
+.mk _q
+.if \n@>1 .tm --@e: _Q=\\n(_Q _q=\\n(_q nl=\\n(nl |p=\\*(|p
+.if !"\\*(|p"" \
+\{\
+. rs
+. sp (\\n(_Qu-\\n(.vu)/2u
+. tl """\\*(|p"
+. rt \\n(_qu
+.\}
+.|e
+.sp |\\n(_qu+\\n(_Qu
+.sp \\n(esu+\\n(.Lv-1v
+.rr _q
+.rr _Q
+..
+.\"
+.\" TBL
+.\"
+.de TS \" *** table start
+.sp \\n(bsu
+.@C 1
+.fi \" drop into fill mode for text boxes
+.if "\\$1"H" \
+\{\
+. di |h \" save header part
+. nr ?T 1
+.\}
+.ls 1
+.ch @f -(\\n(_bu+1v) \" set pseudo-trap for bottom line
+.if \\n(.p-\\n(_b-1v<=\\n(nl \
+. ch @f \\n(nlu+\n(.Vu
+..
+.de TH \" *** end header part of table
+.nr T. 0
+.T# 0
+.di
+.nr _i \\n(.i
+.in 0
+.|h \" put in the initial header
+.in \\n(_iu
+.rr _i
+.mk #T
+..
+.de TE \" *** table end
+.nr ?T 0
+.ch @f -\\n(_bu \" reset pseudo-trap
+.if \\n(.p-\\n(_b<=\\n(nl \
+. ch @f \\n(nlu+\n(.Vu
+.ev
+.sp \\n(bsu+\\n(.Lv-1v
+.re
+..
+.\"
+.\" REFER
+.\"
+.de ][ \" *** refer output
+.if \\$1>5 .tm Bad arg to []
+.[\\$1
+..
+.de [0 \" --- other
+.(f
+.ip "\\*([F.\0"
+.if !"\\*([A"" \\*([A,
+.if !"\\*([T"" \\f2\\*([T\\f1\c
+.if !"\\*([T"" .if !"\\*([O"" ,\
+.ie !"\\*([O"" \\*([O
+.el .if !"\\*([T"" \&.
+.if !"\\*([D"" \\*([D.
+.@p
+.)f
+..
+.de [1 \" --- journal article
+.(f
+.ip "\\*([F.\0"
+\\*([A,
+.if !"\\*([T"" \\*(lq\\*([T,\\*(rq
+.if "\\*([V"" \\f2\\*([J\\f1,
+.if !"\\*([V"" \\f2\\*([J\\f1
+.if !"\\*([V"" \\f3\\*([V\\f1\c
+.if !"\\*([N"" (\\*([N)\c
+.if !"\\*([P"" \
+\{\
+. ie \\n([P>0 \ pp.\&
+. el \ p.\&
+\\*([P
+.\}
+.if !"\\*([I"" .if "\\*([R"" \\*([I,
+(\\*([D).
+.if !"\\*([O"" \\*([O
+.)f
+..
+.de [2 \" --- book
+.(f
+.ip "\\*([F.\0"
+\\*([A, \\f2\\*([T,\\f1
+\\*([I\c
+.if !"\\*([C"" , \\*([C\c
+ (\\*([D).
+.if !"\\*([G"" Gov't. ordering no. \\*([G
+.if !"\\*([O"" \\*([O
+.)f
+..
+.de [3 \" --- article in book
+.(f
+.ip "\\*([F.\0"
+\\*([A, \\*(lq\\*([T,\\*(rq
+.if !"\\*([P"" pp. \\*([P
+in \\f2\\*([B\\f1, \c
+.if !"\\*([E"" ed. \\*([E,\c
+.if !"\\*([I"" \\*([I\c
+.if !"\\*([C"" , \\*([C\c
+ (\\*([D).
+.if !"\\*([O"" \\*([O
+.)f
+..
+.de [4 \" --- report
+.(f
+.ip "\\*([F.\0"
+\\*([A, \\*(lq\\*([T,\\*(rq
+\\*([R\c
+.if !"\\*([G"" \& (\\*([G)\c
+.if !"\\*([I"" , \\*([I\c
+.if !"\\*([C"" , \\*([C\c
+ (\\*([D).
+.if !"\\*([O"" \\*([O
+.)f
+..
+.de [5 \" --- tm style
+.(f
+.ip "\\*([F.\0"
+\\*([A, \\f2\\*([T\\f1,
+.ie \\n(TN \\*([M.
+.el Bell Laboratories internal memorandum (\\*([D).
+.)f
+..
+.de ]<
+.$p References
+.lp
+.rm (f )f
+..
+.de ]>
+.sp
+..
+.de ]-
+.rm [V [P [A [T [N [C [B [O [R [I [E [D
+..
+.ie \n(.V<1v \
+\{\
+. ds [. \s-2\v'-.4m'\f1
+. ds .] \v'.4m'\s+2\fP
+.\}
+.el \
+\{\
+. ds [. " [
+. ds .] ]
+.\}
+.\"
+.\" IDEAL
+.\"
+.de IS \" *** start ideal picture
+.nr g7 \\n(.u
+.ls 1
+..
+.de IF
+.if \\n(g7 .fi
+.ls
+..
+.de IE \" *** end ideal picture
+.if \\n(g7 .fi
+.ls
+..
+.\"
+.\" PIC
+.\"
+.de PS \" *** start picture: $1=height, $2=width in units or inches
+.sp 0.3
+.nr g7 \\$2
+.in (\\n(.lu-\\n(g7u)/2u
+.ne \\$1u
+.nr g7 \\n(.u
+.ls 1
+..
+.de PE \" *** end picture
+.ls
+.in
+.if \\n(g7 .fi
+.sp .6
+..
+.\"
+.\" GREMLIN
+.\"
+.de GS \" *** start gremlin picture
+.nr g7 (\\n(.lu-\\n(g1u)/2u
+.if "\\$1"L" .nr g7 \\n(.iu
+.if "\\$1"R" .nr g7 \\n(.lu-\\n(g1u
+.in \\n(g7u
+.nr g7 \\n(.u
+.ls 1
+.nf
+.ne \\n(g2u
+..
+.de GE \" *** end gremlin picture
+.GF
+.sp .6
+..
+.de GF \" *** finish gremlin picture; stay at top
+.ls
+.in
+.if \\n(g7 .fi
+..
+.\" *** FONT AIDS ***
+.de sz \" *** set point size and vertical spacing
+.ps \\$1
+.vs \\n(.sp*\\n($ru/100u \" default vs at pointsize + 20%
+..
+.de @E \" --- store in _F argument to \f for restoring font
+.ie \\n(.f<10 \
+. ds _F \\n(.f
+.el \
+\{\
+. ie \\n(.f<100&\n(.g \
+. ds _F (\\n(.f
+. el \
+. ds _F P
+.\}
+..
+.de r \" *** enter roman font
+.@E
+.ft 1
+.if \\n(.$ \&\\$1\f\\*(_F\\$2
+..
+.de i \" *** enter italic
+.@E
+.ft 2
+.if \\n(.$ \&\\$1\f\\*(_F\\$2
+..
+.de b \" *** enter boldface
+.@E
+.ft 3
+.if \\n(.$ \&\\$1\f\\*(_F\\$2
+..
+.de rb \" *** enter real boldface
+.@E
+.ft 3
+.if \\n(.$ \&\\$1\f\\*(_F\\$2
+..
+.de bi \" *** enter bold italic
+.@E
+.ft 4
+.if \\n(.$ \&\\$1\f\\*(_F\\$2
+..
+.de u \" *** enter underlined word
+\&\\$1\l'|0\(ul'\\$2
+..
+.de q \" *** enter quoted word
+\&\\*(lq\\$1\\*(rq\\$2
+..
+.de bx \" *** enter boxed word
+\k~\(br\|\\$1\|\(br\l'|\\n~u\(rn'\l'|\\n~u\(ul'\^\\$2
+..
+.de sm \" *** print in smaller font
+\s-1\\$1\\s0\\$2
+..
+.de @F \" --- change font (0 -> no change)
+.nr ~ \\$1
+.if \\n~>0 \
+. ft \\n~
+.rr ~
+..
+.\" *** FOOTNOTING ***
+.de (f \" *** begin footnote
+.ec
+.if "\\n(.z"|f" .tm Line \\n(c. -- Illegal footnote nesting
+.ie "\\n(.z"" \
+\{\
+. nr _D \\n(dn
+. nr _0 1v+\\n(nlu
+. ev 2
+. da |f
+. in 0
+. xl \\n($lu-\\n(fuu
+. @F \\n(ff
+. sz \\n(fp
+. vs \\n(.sp*\\n($Ru/100u
+. if !\\n(?f \
+\{\
+. nr _b +1v \" allow space for $s
+. $s
+. \}
+. br
+. if \\n(.p-\\n(_b-\\n(_0-\\n(.h-1v-\\n(fs<0 \
+\{\
+. da\" \" won't fit on page at all
+. bc
+. if !\\n(?f \
+. rm |f
+. da |f
+.\" next five lines could be dropped if headers had their own environment
+. in 0 \" reset everything from .bc
+. xl \\n($lu-\\n(fuu
+. @F \\n(ff
+. sz \\n(fp
+. vs \\n(.sp*\\n($Ru/100u
+. if !\\n(?f \
+. $s
+. br
+. \}
+. rr _0
+. sp \\n(fsu
+. nr ?f 1
+. fi
+. if !"\\$1"_" \
+. ti \\n(fiu
+. if \n@>2 .tm << (f $f=\\n($f
+.\}
+.el \
+\{\
+. ev 2
+\!.(f \\$1
+.\}
+..
+.de )f \" *** end footnote
+.ie "\\n(.z"|f" \
+\{\
+. if \\n* \
+. nr $f +1
+. ds * \\*{\\n($f\\*}\k*
+. rr *
+. in 0
+. da
+. ev
+. if \\n(_w<\\n(dl \
+. nr _w \\n(dl \" compute maximum fn width
+. nr _b +\\n(dn
+. ch @f -\\n(_bu
+. if \\n(.p-\\n(_b<=\\n(nl \
+. ch @f \\n(nlu+\n(.Vu
+. nr dn \\n(_D
+. rr _D
+.\}
+.el \
+\{\
+. br
+\!.)f
+. ev
+.\}
+..
+.if \n(ff<=0 \
+. nr ff 1 \" footnote font: Times Roman
+.if \n(fp<=0 \
+. nr fp 8 \" footnote pointsize
+.de $s \" $$$ footnote separator
+\l'2i'
+..
+.\" *** DELAYED TEXT ***
+.de (d \" *** begin delayed text
+.am |d )d
+.sp \\n(bsu
+..
+.de )d \" *** end delayed text
+.if \\n# \
+. nr $d +1
+.ds # [\\n($d]\k#
+.rr #
+..
+.de pd \" *** print delayed text
+.|d
+.rm |d
+.nr $d 1 1
+.ds # [1]\k#
+..
+.\" *** INDEXES (TABLE OF CONTENTS) ***
+.nr _x 0 1
+.af _x a
+.de (x \" *** begin index entry
+.if \n@>4 .tm >> (x, .z=\\n(.z
+.ds |X x
+.if \\n(.$>0 \
+. ds |X \\$1
+.ie "\\n(.z"" \
+. nr _z 0
+.el \
+. nr _z 1
+.@\\n(_z
+..
+.de @0 \" --- finish (x if no current diversion
+.am %\\*(|X )x
+.sp \\n(xsu
+.ti -\\n(piu
+..
+.de @1 \" --- finish (x if current diversion
+.if "\\n(_x"z" .nr _x 0
+.de =\\n+(_x )x
+..
+.de )x \" *** end index entry
+.if \n@>4 .tm >> )x, .z=\\n(.z
+.ie "\\n(.z"" \
+\{\
+. ds |x \\n%
+. if \\n(.$>0 \
+. ds |x \\$1
+. if "\\*(|x"_" \
+. ig ..
+. am %\\*(|X ..
+. if \w"\\$2">(\\n(.l-\\n(.i-\\n(.k) \
+. ti +\\n(xuu
+\\\\a\\\\t\\$2\\*(|x
+...
+. rm |x
+. rm |X
+.\}
+.el \
+\{\
+\!.(x \\*(|X
+\!\\\\*(=\\n(_x\\\\
+\!.)x \\$1 \\$2
+\!.rm =\\n(_x
+.\}
+..
+.de xp \" *** print the index
+.br
+.@C 2
+.ls 1
+.vs \\n(.sp*\\n($Ru/100u
+.fi
+.in +\\n(piu
+.ds |X x
+.if \\n(.$>0 \
+. ds |X \\$1
+.xl -(\\n(xuu+\w'...'u)
+.di |x
+.%\\*(|X
+.br
+.di
+.rm %\\*(|X
+.xl \\n($lu
+.rm |X
+.ev
+.nf
+.in 0
+.ta \\n(.lu-\\n(xuuR \\n(.luR
+.|x
+.fi
+.in
+.rm |x
+..
+.de +c \" *** begin chapter
+.ep \" force out footnotes
+.if \\n(?o:\\n(?a \
+\{\
+. bp \" force out a table or more footnote
+. rs
+. ep
+.\}
+.nr ?C 1
+.nr $f 1 1
+.ds * \\*{1\\*}\k*
+.if \\n(?R \
+. pn 1
+.bp
+.in \\n($iu \" reset the indent
+.rs
+.ie \\n(.$ \
+. $c "\\$1"
+.el \
+. sp 3
+..
+.de ++ \" *** declare chapter type
+.nr _0 0
+.if "\\$1"C" \
+. nr _0 1 \" chapter
+.if "\\$1"RC" \
+. nr _0 11 \" renumbered chapter
+.if "\\$1"A" \
+. nr _0 2 \" appendix
+.if "\\$1"RA" \
+. nr _0 12 \" renumbered appendix
+.if "\\$1"P" \
+. nr _0 3 \" preliminary material
+.if "\\$1"B" \
+. nr _0 4 \" bibliographic material
+.if "\\$1"AB" \
+. nr _0 5 \" abstract
+.if \\n(_0=0 \
+. tm Line \\n(c. -- Bad mode to .++
+.nr ?R 0
+.if \\n(_0>10 \
+.\{
+. nr ?R 1
+. nr _0 -10
+.\}
+.nr ch 0 1
+.if (\\n(_0=3):(\\n(_0=5) \
+. pn 1 \" must do before .ep
+.ep \" end page for correct page number types
+.if \\n(_0=1 \
+\{\
+. af ch 1
+. af % 1
+.\}
+.if \\n(_0=2 \
+\{\
+. af ch A
+. af % 1
+.\}
+.if \\n(_0=3 \
+. af % i
+.if \\n(_0=4 \
+. af % 1
+.if \\n(_0=5 \
+. af % 1
+.if \\n(.$>1 \
+. he \\$2
+.if !\\n(_0=\\n(_M .if \\n(_M=3 \
+. pn 1
+.nr _M \\n(_0
+.rr _0
+..
+.de $c \" $$$ print chapter title
+.sz 12
+.ft 3
+.ce 1000
+.if \\n(_M<3 \
+. nr ch +1
+.ie \\n(_M=1 CHAPTER\ \ \\n(ch
+.el .if \\n(_M=2 APPENDIX\ \ \\n(ch
+.if \w"\\$1" .sp 3-\\n(.L
+.if \w"\\$1" \\$1
+.if (\\n(_M<3):(\w"\\$1") \
+. sp 4-\\n(.L
+.ce 0
+.ft
+.sz
+.ie \\n(_M=1 \
+. $C Chapter \\n(ch "\\$1"
+.el .if \\n(_M=2 \
+. $C Appendix \\n(ch "\\$1"
+..
+.de tp \" *** title page
+.hx
+.bp
+.br
+.rs
+.pn \\n%
+..
+.\" *** DATES ***
+.if \n(mo=1 .ds mo January
+.if \n(mo=2 .ds mo February
+.if \n(mo=3 .ds mo March
+.if \n(mo=4 .ds mo April
+.if \n(mo=5 .ds mo May
+.if \n(mo=6 .ds mo June
+.if \n(mo=7 .ds mo July
+.if \n(mo=8 .ds mo August
+.if \n(mo=9 .ds mo September
+.if \n(mo=10 .ds mo October
+.if \n(mo=11 .ds mo November
+.if \n(mo=12 .ds mo December
+.if \n(dw=1 .ds dw Sunday
+.if \n(dw=2 .ds dw Monday
+.if \n(dw=3 .ds dw Tuesday
+.if \n(dw=4 .ds dw Wednesday
+.if \n(dw=5 .ds dw Thursday
+.if \n(dw=6 .ds dw Friday
+.if \n(dw=7 .ds dw Saturday
+.ds td \*(mo \n(dy, 19\n(yr
+.\" *** PARAMETRIC INITIALIZATIONS ***
+.rr x
+.nr $r \n(.v*100+\n(.sp-1u/\n(.sp \" ratio of vs to ps for .sz request
+.nr $R \n($r \" ratio for displays & footnotes
+.nr hm 4v \" header margin
+.nr tm 7v \" top margin
+.nr bm 6v \" bottom margin
+.nr fm 3v \" footer margin
+.nr tf 3 \" title font: (real) Times Bold
+.nr tp 10 \" title point size
+.hy 14
+.nr bi 4m \" indent for blocks
+.nr pi 5n \" indent for paragraphs
+.nr pf 1 \" normal text font
+.nr pp 10 \" normal text point size
+.nr qi 4n \" indent for quotes
+.nr qp -1 \" down one point
+.nr ii 5n \" indent for .ip's and .np's
+.nr $m 1 \" max number of columns
+.nr $s 4n \" column separation
+.nr sf 3 \" section font -- Times Bold
+.nr sp 10 \" section title pointsize
+.nr ss 12p \" section prespacing
+.nr si 0 \" section indent
+.\" *** OTHER INITIALIZATION ***
+.ds { \v'-0.4m'\x'-0.2m'\s-3
+.ds } \s0\v'0.4m'
+.ds < \v'0.4m'\x'0.2m'\s-3
+.ds > \s0\v'-0.4m'
+.ds - \(em
+.nr fi 0.3i
+.nr _o \n(.o
+.nr $b 3 \" bold
+.nr ps 0.35v
+.if \n(ps<\n(.V .nr ps \n(.V
+.nr bs \n(ps \" block pre/post spacing
+.nr qs \n(ps \" quote pre/post spacing
+.nr zs 1v \" float-block pre/postspacing
+.nr xs 0.2v \" index prespacing
+.nr xu 0.5i \" index undent
+.nr fs 0.2v \" footnote prespacing
+.nr es 0.5v \" equation pre/postspacing
+.if \n(es<\n(.V .nr es \n(.V
+.wh 0 @h \" set header
+.nr $l \n(.lu \" line length
+.nr _L \n(.lu \" line length of page
+.nr $c 1 \" current column number
+.nr $f 1 1 \" footnote number
+.ds * \*{1\*}\k*\" \" footnote "name"
+.nr $d 1 1 \" delayed text number
+.ds # [1]\k#\" \" delayed text "name"
+.nr _M 1 \" chapter mode is chapter
+.ds lq \(lq\" \" left quote
+.ds rq \(rq\" \" right quote
+.em @z
+.\" *** FOREIGN LETTERS AND SPECIAL CHARACTERS ***
+.ds #h ((1u-(\\\\n(.fu%2u))*0.13m)
+.ds #v 0.6m
+.\" \" accents
+.ds ' \k_\h'-(\\n(.wu*8/10-\*(#h)'\(aa\h'|\\n_u'
+.ds ` \k_\h'-(\\n(.wu*7/10-\*(#h)'\(ga\h'|\\n_u'
+.\" \" umlaut
+.ds : \k_\h'-(\\n(.wu*8/10-\*(#h+0.1m)'\v'-\*(#v'\z.\h'0.2m'.\h'|\\n_u'\v'\*(#v'
+.\" \" circumflex and tilde
+.ds ^ \k_\h'-(\\n(.wu-\*(#h-0.05m)'^\h'|\\n_u'
+.ds ~ \k_\h'-(\\n(.wu-\*(#h-0.05m)'~\h'|\\n_u'
+.\" \" cedilla and czech
+.ds , \k_\h'-(\\n(.wu)'\*(#[,\h'|\\n_u'
+.ds v \k_\h'-(\\n(.wu*9/10-\*(#h)'\v'-\*(#v'\s-4v\s0\v'\*(#v'\h'|\\n_u'
+.\" \" Norwegian A or angstrom
+.ds o \k_\h'-(\\n(.wu+\w'\(de'u-\*(#h)/2u'\v'-0.4n'\z\(de\v'0.4n'\h'|\\n_u'
+.\" \" there exists, for all
+.ds qe \s-2\v'0.45m'\z\(em\v'-0.625m'\z\(em\v'-0.625m'\(em\v'0.8m'\s0\h'-0.1m'\v'-0.05m'\(br\v'0.05m'\h'0.1m'
+.ds qa \z\e\h'0.35m'\z\(sl\h'-0.33m'\v'-0.3m'\s-4\(em\s0\v'0.3m'\h'0.15m'
+.rm #h #v
+.ll 6.0i
+.lt 6.0i
+.de @S
+.tm The \\$1 macro has been removed from this version of the -me macros.
+..
+.de lo
+.@S lo
+..
+.de th
+.@S th
+..
+.de ac
+.@S ac
+..
diff --git a/macros/tmac.pic b/macros/tmac.pic
new file mode 100644
index 000000000..1177fc09d
--- /dev/null
+++ b/macros/tmac.pic
@@ -0,0 +1,10 @@
+.de PS
+.br
+.sp .3v
+.ne 0\\$1+1v+\n(.Vu
+.in \\n(.lu-\\n(.iu-0\\$2/2u>?0
+..
+.de PE
+.in
+.sp .3v+.5m
+..
diff --git a/macros/tmac.s b/macros/tmac.s
new file mode 100644
index 000000000..4430bdf71
--- /dev/null
+++ b/macros/tmac.s
@@ -0,0 +1,1808 @@
+.ig
+Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+..
+.if !\n(.g .ab These ms macros require groff.
+.if \n(.C \
+. ab The groff ms macros do not work in compatibility mode.
+.\" Enable warnings. You can delete this if you want.
+.warn
+.\" See if already loaded.
+.if r GS .nx /dev/null
+.nr GS 1
+.de @error
+.tm \\n(.F:\\n(.c: macro error: \\$*
+..
+.de @warning
+.tm \\n(.F:\\n(.c: macro warning: \\$*
+..
+.de @fatal
+.ab \\n(.F:\\n(.c: fatal macro error: \\$*
+..
+.de @not-implemented
+.@error sorry, \\$0 not implemented
+.als \\$0 @nop
+..
+.als TM @not-implemented
+.als CT @not-implemented
+.de @nop
+..
+.de @init
+.\" a non-empty environment
+.ev ne
+\c
+.ev
+.ev nf
+'nf
+.ev
+..
+.ds REFERENCES References
+.ds ABSTRACT ABSTRACT
+.ds TOC Table of Contents
+.ds MONTH1 January
+.ds MONTH2 February
+.ds MONTH3 March
+.ds MONTH4 April
+.ds MONTH5 May
+.ds MONTH6 June
+.ds MONTH7 July
+.ds MONTH8 August
+.ds MONTH9 September
+.ds MONTH10 October
+.ds MONTH11 November
+.ds MONTH12 December
+.ds MO \\*[MONTH\n[mo]]
+.nr *year \n[yr]+1900
+.ds DY \n[dy] \*[MO] \n[*year]
+.de ND
+.if \\n[.$] .ds DY "\\$*
+..
+.de DA
+.if \\n[.$] .ds DY "\\$*
+.ds CF \\*[DY]
+..
+.\" indexing
+.de IX
+.tm \\$1\t\\$2\t\\$3\t\\$4 ... \\n[PN]
+..
+.\" print an error message and then try to recover
+.de @error-recover
+.@error \\$@ (recovering)
+.nr *pop-count 0
+.while !'\\n(.z'' \{\
+. \"@warning automatically terminating diversion \\n(.z
+. ie d @div-end!\\n(.z .@div-end!\\n(.z
+. el .*div-end-default
+. nr *pop-count +1
+. \" ensure that we don't loop forever
+. if \\n[*pop-count]>20 .@fatal recovery failed
+.\}
+.while !'\\n[.ev]'0' .ev
+.par@reset-env
+.par@reset
+..
+.de *div-end-default
+.ds *last-div \\n(.z
+.br
+.di
+.ev nf
+.\\*[*last-div]
+.ev
+..
+.\" ****************************
+.\" ******** module cov ********
+.\" ****************************
+.\" Cover sheet and first page.
+.de cov*err-not-after-first-page
+.@error \\$0 is not allowed after the first page has started
+..
+.de cov*err-not-before-tl
+.@error \\$0 is not allowed before TL
+..
+.de cov*err-not-again
+.@error \\$0 is not allowed more than once
+..
+.de cov*err-not-after-ab
+.@error \\$0 is not allowed after first AB, LP, PP, IP, SH or NH
+..
+.als AU cov*err-not-before-tl
+.als AI cov*err-not-before-tl
+.als AB cov*err-not-before-tl
+.de cov*first-page-init
+.rm cov*first-page-init
+.par@init
+.als RP cov*err-not-after-first-page
+.@init
+.ie \\n[cov*rp-format] \{\
+. pg@cs-top
+. als FS cov*FS
+. als FE cov*FE
+.\}
+.el \{\
+. pg@top
+. als FS @FS
+. als FE @FE
+.\}
+.wh 0 pg@top
+..
+.wh 0 cov*first-page-init
+.\" This handles the case where FS occurs before TL or LP.
+.de FS
+.br
+\\*[FS]\\
+..
+.nr cov*rp-format 0
+.nr cov*rp-no 0
+.\" released paper format
+.de RP
+.nr cov*rp-format 1
+.if \\n[.$] .if '\\$1'no' .nr cov*rp-no 1
+.pn 0
+..
+.de TL
+.br
+.als TL cov*err-not-again
+.rn @AB AB
+.rn @AU AU
+.rn @AI AI
+.di cov*tl-div
+.par@reset
+.ft B
+.ps +2
+.vs +3p
+.ll (u;\\n[LL]*5/6)
+.nr cov*n-au 0
+..
+.de @AU
+.par@reset
+.if !'\\n(.z'' \{\
+. br
+. di
+.\}
+.nr cov*n-au +1
+.di cov*au-div!\\n[cov*n-au]
+.nf
+.ft I
+.ps \\n[PS]
+..
+.de @AI
+.par@reset
+.if !'\\n(.z'' \{\
+. br
+. di
+.\}
+.ie !\\n[cov*n-au] .@error AI before AU
+.el \{\
+. di cov*ai-div!\\n[cov*n-au]
+. nf
+. ft R
+. ps \\n[PS]
+.\}
+..
+.de LP
+.if !'\\n[.z]'' \{\
+. br
+. di
+.\}
+.br
+.cov*ab-init
+.cov*print
+\\*[\\$0]\\
+..
+.als IP LP
+.als PP LP
+.als XP LP
+.als NH LP
+.als SH LP
+.als MC LP
+.als RT LP
+.de cov*ab-init
+.als cov*ab-init @nop
+.als LP @LP
+.als IP @IP
+.als PP @PP
+.als XP @XP
+.als RT @RT
+.als SH @SH
+.als NH @NH
+.als QP @QP
+.als RS @RS
+.als RE @RE
+.als QS @QS
+.als QE @QE
+.als MC @MC
+.als EQ @EQ
+.als EN @EN
+.als AB cov*err-not-after-ab
+.als AU par@AU
+.als AI par@AI
+.als TL par@TL
+..
+.de @AB
+.if !'\\n(.z'' \{\
+. br
+. di
+.\}
+.cov*ab-init
+.di cov*ab-div
+.par@ab-indent
+.par@reset
+.if !'\\$1'no' \{\
+. ft I
+. ce 1
+\\*[ABSTRACT]
+. sp
+. ft R
+.\}
+.ns
+.@PP
+..
+.de AE
+.ie '\\n(.z'cov*ab-div' \{\
+. als AE cov*err-not-again
+. br
+. di
+.\" nr cov*ab-height \\n[dn]
+. par@reset-env
+. par@reset
+. cov*print
+.\}
+.el .@error AE without AB
+..
+.de @div-end!cov*ab-div
+.AE
+..
+.de cov*print
+.als cov*print @nop
+.ie d cov*tl-div \{\
+. ie \\n[cov*rp-format] .cov*rp-print
+. el .cov*draft-print
+.\}
+.el \{\
+. if \\n[cov*rp-format] \{\
+. @warning RP format but no TL
+. bp 1
+. als FS @FS
+. als FE @FE
+. \}
+. br
+.\}
+..
+.de cov*rp-print
+.nr cov*page-length \\n[.p]
+.pl 1000i
+.cov*tl-au-print
+.sp 3
+.if d cov*ab-div \{\
+. nf
+. cov*ab-div
+.\}
+.sp 3
+.par@reset
+\\*[DY]
+.br
+.if \\n[cov*fn-height] \{\
+. sp |(u;\\n[cov*page-length]-\\n[FM]\
+-\\n[cov*fn-height]-\\n[fn@sep-dist]>?\\n[nl])
+. fn@print-sep
+. ev nf
+. cov*fn-div
+. ev
+. ie \\n[cov*rp-no] .rm cov*fn-div
+. el \{\
+. rn cov*fn-div fn@overflow-div
+. nr fn@have-overflow 1
+. \}
+.\}
+.als FS @FS
+.als FE @FE
+.\" If anything was printed below where the footer line is normally printed,
+.\" then that's an overflow.
+.if -\\n[FM]/2+1v+\\n[cov*page-length]<\\n[nl] .@error cover sheet overflow
+.pl \\n[cov*page-length]u
+.bp 1
+.if !\\n[cov*rp-no] .cov*tl-au-print
+.rs
+.sp 1
+..
+.de cov*draft-print
+.cov*tl-au-print
+.if d cov*ab-div \{\
+. nf
+. sp 2
+. cov*ab-div
+.\}
+.sp 1
+..
+.de cov*tl-au-print
+.par@reset
+.nf
+.rs
+.sp 3
+.ce 9999
+.cov*tl-div
+.nr cov*i 1
+.nr cov*sp 1v
+.while \\n[cov*i]<=\\n[cov*n-au] \{\
+. sp \\n[cov*sp]u
+. cov*au-div!\\n[cov*i]
+. ie d cov*ai-div!\\n[cov*i] \{\
+. sp .5v
+. cov*ai-div!\\n[cov*i]
+. nr cov*sp 1v
+. \}
+. el .nr cov*sp .5v
+. nr cov*i +1
+.\}
+.ce 0
+..
+.nr cov*fn-height 0
+.nr cov*in-fn 0
+.\" start of footnote on cover
+.de cov*FS
+.if \\n[cov*in-fn] \{\
+. @error nested FS
+. FE
+.\}
+.nr cov*in-fn 1
+.ev fn
+.par@reset-env
+.da cov*fn-div
+.if !\\n[cov*fn-height] .ns
+.ie \\n[.$] .FP "\\$1" no
+.el .@LP
+..
+.de @div-end!cov*fn-div
+.cov*FE
+..
+.\" end of footnote on cover
+.de cov*FE
+.ie '\\n(.z'cov*fn-div' \{\
+. br
+. ev
+. di
+. nr cov*in-fn 0
+. nr cov*fn-height +\\n[dn]
+.\}
+.el .@error FE without matching FS
+..
+.\" ***************************
+.\" ******** module pg ********
+.\" ***************************
+.\" Page-level formatting.
+.\" > 0 if we have a footnote on the current page
+.nr pg@fn-flag 0
+.nr pg@colw 0
+.nr pg@fn-colw 0
+.nr HM 1i
+.nr FM 1i
+.nr PO 1i
+.ds LF
+.ds CF
+.ds RF
+.ds LH
+.ds CH -\\n[PN]-
+.ds RH
+.ds pg*OH '\\*[LH]'\\*[CH]'\\*[RH]'
+.ds pg*EH '\\*[LH]'\\*[CH]'\\*[RH]'
+.ds pg*OF '\\*[LF]'\\*[CF]'\\*[RF]'
+.ds pg*EF '\\*[LF]'\\*[CF]'\\*[RF]'
+.de OH
+.ds pg*\\$0 "\\$*
+..
+.als EH OH
+.als OF OH
+.als EF OH
+.de PT
+.ie \\n%=1 .if \\n[pg*P1] .tl \\*[pg*OH]
+.el \{\
+. ie o .tl \\*[pg*OH]
+. el .tl \\*[pg*EH]
+.\}
+..
+.de BT
+.ie o .tl \\*[pg*OF]
+.el .tl \\*[pg*EF]
+..
+.nr pg*P1 0
+.de P1
+.nr pg*P1 1
+..
+.wh -\n[FM]u pg@bottom
+.wh -\n[FM]u/2u pg*footer
+.nr MINGW 2n
+.nr pg@ncols 1
+.de @MC
+.if !'\\n(.z'' .error-recover MC while diversion open
+.br
+.ie \\n[pg@ncols]>1 .pg@super-eject
+.el \{\
+. \" flush out any floating keeps
+. while \\n[kp@tail]>\\n[kp@head] \{\
+. rs
+. bp
+. \}
+.\}
+.ie !\\n(.$ \{\
+. nr pg@colw \\n[LL]*7/15
+. nr pg*gutw \\n[LL]-(2*\\n[pg@colw])
+. nr pg@ncols 2
+.\}
+.el \{\
+. nr pg@colw (n;\\$1)<?\\n[LL]
+. ie \\n[.$]<2 .nr pg*gutw \\n[MINGW]
+. el .nr pg*gutw (n;\\$2)
+. nr pg@ncols \\n[LL]-\\n[pg@colw]/(\\n[pg@colw]+\\n[pg*gutw])+1
+. ie \\n[pg@ncols]>1 \
+. nr pg*gutw \\n[LL]-(\\n[pg@ncols]*\\n[pg@colw])/(\\n[pg@ncols]-1)
+. el .nr pg*gutw 0
+.\}
+.mk pg*col-top
+.ns
+.nr pg*col-num 0
+.nr pg@fn-colw \\n[pg@colw]*5/6
+.par@reset
+..
+.de 2C
+.MC
+..
+.de 1C
+.MC \\n[LL]u
+..
+.\" top of page macro
+.de pg@top
+.ch pg*footer -\\n[FM]u/2u
+.nr PN \\n%
+.nr pg*col-num 0
+.nr pg@fn-bottom-margin 0
+.nr pg*saved-po \\n[PO]
+.po \\n[PO]u
+.ev h
+.par@reset
+.sp (u;\\n[HM]/2)
+.PT
+.sp |\\n[HM]u
+.if d HD .HD
+.mk pg@header-bottom
+.ev
+.mk pg*col-top
+.pg*start-col
+..
+.de pg*start-col
+.\" Handle footnote overflow before floating keeps, because the keep
+.\" might contain an embedded footnote.
+.fn@top-hook
+.kp@top-hook
+.tbl@top-hook
+.ns
+..
+.de pg@cs-top
+.sp \\n[HM]u
+.\" move pg@bottom and pg*footer out of the way
+.ch pg@bottom \\n[.p]u*2u
+.ch pg*footer \\n[.p]u*2u
+.ns
+..
+.de pg@bottom
+.tbl@bottom-hook
+.if \\n[pg@fn-flag] .fn@bottom-hook
+.nr pg*col-num +1
+.ie \\n[pg*col-num]<\\n[pg@ncols] .pg*end-col
+.el .pg*end-page
+..
+.de pg*end-col
+'sp |\\n[pg*col-top]u
+.po (u;\\n[pg*saved-po]+(\\n[pg@colw]+\\n[pg*gutw]*\\n[pg*col-num]))
+.\"po +(u;\\n[pg@colw]+\\n[pg*gutw])
+.pg*start-col
+..
+.de pg*end-page
+.po \\n[pg*saved-po]u
+.\" Make sure we don't exit if there are still floats or footnotes left-over.
+.ie \\n[kp@head]<\\n[kp@tail]:\\n[fn@have-overflow] \{\
+. \" Switching environments ensures that we don't get an unnecessary
+. \" blank line at the top of the page.
+. ev ne
+' bp
+. ev
+.\}
+.el \{\
+. if r pg*next-number \{\
+. pn \\n[pg*next-number]
+. rr pg*next-number
+. if d pg*next-format \{\
+. af PN \\*[pg*next-format]
+. rm pg*next-format
+. \}
+. \}
+' bp
+.\}
+..
+.\" pg@begin number format
+.de pg@begin
+.ie \\n[.$]>0 \{\
+. nr pg*next-number (;\\$1)
+. ie \\n[.$]>1 .ds pg*next-format \\$2
+. el .rm pg*next-format
+.\}
+.el .rr pg*next-number
+.pg@super-eject
+..
+.\" print the footer line
+.de pg*footer
+.ev h
+.par@reset
+.BT
+.ev
+..
+.\" flush out any keeps or footnotes
+.de pg@super-eject
+.br
+.if !'\\n(.z'' .@error-recover diversion open while ejecting page
+.\" Make sure we stay in the end macro while there is still footnote overflow
+.\" left, or floating keeps.
+.while \\n[kp@tail]>\\n[kp@head]:\\n[pg@fn-flag] \{\
+. rs
+. bp
+.\}
+.bp
+..
+.em pg@super-eject
+.\" ***************************
+.\" ******** module fn ********
+.\" ***************************
+.\" Footnotes.
+.nr fn@sep-dist 8p
+.ev fn
+.\" Round it vertically
+.vs \n[fn@sep-dist]u
+.nr fn@sep-dist \n[.v]
+.ev
+.nr fn*text-num 0 1
+.nr fn*note-num 0 1
+.ds * \\*[par@sup-start]\En+[fn*text-num]\\*[par@sup-end]
+.nr fn*open 0
+.\" normal FS
+.de @FS
+.ie \\n[.$] .fn*do-FS "\\$1" no
+.el \{\
+. ie \\n[fn*text-num]>\\n[fn*note-num] .fn*do-FS \\n+[fn*note-num]
+. el .fn*do-FS
+.\}
+..
+.\" Second argument of `no' means don't embellish the first argument.
+.de fn*do-FS
+.if \\n[fn*open] .@error-recover nested FS
+.nr fn*open 1
+.if \\n[.u] \{\
+. \" Ensure that the first line of the footnote is on the same page
+. \" as the reference. I think this is minimal.
+. ev fn
+. nr fn*need 1v
+. ev
+. ie \\n[pg@fn-flag] .nr fn*need +\\n[fn:PD]
+. el .nr fn*need +\\n[fn@sep-dist]
+. ne \\n[fn*need]u+\\n[.V]u>?0
+.\}
+.ev fn
+.par@reset-env
+.fn*start-div
+.par@reset
+.ie \\n[.$] .FP \\$@
+.el .@LP
+..
+.de @FE
+.ie !\\n[fn*open] .@error FE without FS
+.el \{\
+. nr fn*open 0
+. br
+. ev
+. fn*end-div
+.\}
+..
+.nr fn@have-overflow 0
+.\" called at the top of each column
+.de fn@top-hook
+.nr fn*max-width 0
+.nr fn*page-bottom-pos 0-\\n[FM]-\\n[pg@fn-bottom-margin]
+.ch pg@bottom \\n[fn*page-bottom-pos]u
+.if \\n[fn@have-overflow] \{\
+. nr fn@have-overflow 0
+. fn*start-div
+. ev nf
+. fn@overflow-div
+. ev
+. fn*end-div
+.\}
+..
+.\" This is called at the bottom of the column if pg@fn-flag is set.
+.de fn@bottom-hook
+.nr pg@fn-flag 0
+.nr fn@have-overflow 0
+.nr fn@bottom-pos \\n[.p]-\\n[FM]-\\n[pg@fn-bottom-margin]+\\n[.v]
+.ev fn
+.nr fn@bottom-pos -\\n[.v]
+.ev
+.ie \\n[nl]+\\n[fn@sep-dist]+\n[.V]>\\n[fn@bottom-pos] \{\
+. rn fn@div fn@overflow-div
+. nr fn@have-overflow 1
+.\}
+.el \{\
+. if \\n[pg@ncols]>1 \
+. if \\n[fn*max-width]>\\n[pg@fn-colw] \
+. nr pg@fn-bottom-margin \\n[.p]-\\n[FM]-\\n[nl]+1v
+. wh \\n[fn@bottom-pos]u fn*catch-overflow
+. fn@print-sep
+. ev nf
+. fn@div
+. rm fn@div
+. ev
+. if '\\n(.z'fn@overflow-div' \{\
+. di
+. nr fn@have-overflow \\n[dn]>0
+. \}
+. ch fn*catch-overflow
+.\}
+..
+.de fn*catch-overflow
+.di fn@overflow-div
+..
+.nr fn*embed-count 0
+.de @div-end!fn@div
+.br
+.if '\\n[.ev]'fn' .ev
+.fn*end-div
+.nr fn*open 0
+..
+.als @div-end!fn*embed-div @div-end!fn@div
+.de fn*start-div
+.ie '\\n(.z'' \{\
+. da fn@div
+. if !\\n[pg@fn-flag] .ns
+.\}
+.el .di fn*embed-div
+..
+.de fn*end-div
+.ie '\\n(.z'fn@div' \{\
+. di
+. nr fn*page-bottom-pos -\\n[dn]
+. nr fn*max-width \\n[fn*max-width]>?\\n[dl]
+. if !\\n[pg@fn-flag] .nr fn*page-bottom-pos -\\n[fn@sep-dist]
+. nr pg@fn-flag 1
+. nr fn*page-bottom-pos \\n[nl]-\\n[.p]+\n[.V]>?\\n[fn*page-bottom-pos]
+. ch pg@bottom \\n[fn*page-bottom-pos]u
+.\}
+.el \{\
+. ie '\\n(.z'fn*embed-div' \{\
+. di
+. rn fn*embed-div fn*embed-div!\\n[fn*embed-count]
+\!. fn*embed-start \\n[fn*embed-count]
+. rs
+' sp (u;\\n[dn]+\\n[fn@sep-dist]+\\n[.V])
+\!. fn*embed-end
+. nr fn*embed-count +1
+. \}
+. el \{\
+. ev fn
+. @error-recover unclosed diversion within footnote
+. \}
+.\}
+..
+.de fn*embed-start
+.ie '\\n(.z'' \{\
+. fn*start-div
+. ev nf
+. fn*embed-div!\\$1
+. rm fn*embed-div!\\$1
+. ev
+. fn*end-div
+. di fn*null
+.\}
+.el \{\
+\!. fn*embed-start \\$1
+. rs
+.\}
+..
+.de fn*embed-end
+.ie '\\n(.z'fn*null' \{\
+. di
+. rm fn*null
+.\}
+.el \!.fn*embed-end
+..
+.\" It's important that fn@print-sep use up exactly fn@sep-dist vertical space.
+.de fn@print-sep
+.ev fn
+.in 0
+.vs \\n[fn@sep-dist]u
+\D'l 1i 0'
+.br
+.ev
+..
+.\" ***************************
+.\" ******** module kp ********
+.\" ***************************
+.\" Keeps.
+.de KS
+.br
+.di kp*div
+..
+.de KF
+.if !'\\n(.z'' .@error-recover KF while open diversion
+.di kp*fdiv
+.ev k
+.par@reset-env
+.par@reset
+..
+.de KE
+.ie '\\n(.z'kp*div' .kp*end
+.el \{\
+. ie '\\n(.z'kp*fdiv' .kp*fend
+. el .@error KE without KS or KF
+.\}
+..
+.de @div-end!kp*div
+.kp*end
+..
+.de @div-end!kp*fdiv
+.kp*fend
+..
+.de kp*need
+.ie '\\n(.z'' .ds@need \\$1
+.el \!.kp*need \\$1
+..
+.\" end non-floating keep
+.de kp*end
+.br
+.di
+.kp*need \\n[dn]
+.ev nf
+.kp*div
+.ev
+.rm kp*div
+..
+.\" Floating keeps.
+.nr kp@head 0
+.nr kp@tail 0
+.\" end floating keep
+.de kp*fend
+.br
+.ev
+.di
+.ie \\n[.t]-(\\n[.k]>0*1v)>\\n[dn] \{\
+. br
+. ev nf
+. kp*fdiv
+. rm kp*fdiv
+. ev
+.\}
+.el \{\
+. rn kp*fdiv kp*div!\\n[kp@tail]
+. nr kp*ht!\\n[kp@tail] 0\\n[dn]
+. nr kp@tail +1
+.\}
+..
+.\" top of page processing for KF
+.nr kp*doing-top 0
+.de kp@top-hook
+.if !\\n[kp*doing-top] \{\
+. nr kp*doing-top 1
+. kp*do-top
+. nr kp*doing-top 0
+.\}
+..
+.de kp*do-top
+.\" If the first keep won't fit, only force it out if we haven't had a footnote
+.\" and we're at the top of the page.
+.nr kp*force \\n[pg@fn-flag]=0&(\\n[nl]<=\\n[pg@header-bottom])
+.nr kp*fits 1
+.while \\n[kp@tail]>\\n[kp@head]&\\n[kp*fits] \{\
+. ie \\n[.t]>\\n[kp*ht!\\n[kp@head]]:\\n[kp*force] \{\
+. nr kp*force 0
+. \" It's important to advance kp@head before bringing
+. \" back the keep, so that if the last line of the
+. \" last keep springs the bottom of page trap, a new
+. \" page will not be started unnecessarily.
+. rn kp*div!\\n[kp@head] kp*temp
+. nr kp@head +1
+. ev nf
+. kp*temp
+. ev
+. rm kp*temp
+. \}
+. el .nr kp*fits 0
+.\}
+..
+.\" ***************************
+.\" ******** module ds ********
+.\" ***************************
+.\" Displays and non-floating keeps.
+.de DE
+.ds*end!\\n[\\n[.ev]:ds-type]
+.nr \\n[.ev]:ds-type 0
+..
+.de ds@auto-end
+.if \\n[\\n[.ev]:ds-type] \{\
+. @error automatically terminating display
+. DE
+.\}
+..
+.de @div-end!ds*div
+.ie \\n[\\n[.ev]:ds-type] .DE
+.el .ds*end!2
+..
+.de ds*end!0
+.@error DE without DS, ID, CD, LD or BD
+..
+.de LD
+.br
+.nr \\n[.ev]:ds-type 1
+.par@reset
+.nf
+.sp \\n[DD]u
+..
+.de ID
+.LD
+.ie \\n[.$] .in +(n;\\$1)
+.el .in +\\n[DI]u
+..
+.de CD
+.LD
+.ce 9999
+..
+.de RD
+.LD
+.rj 9999
+..
+.de ds*common-end
+.par@reset
+.sp \\n[DD]u
+..
+.als ds*end!1 ds*common-end
+.de BD
+.LD
+.nr \\n[.ev]:ds-type 2
+.di ds*div
+..
+.de ds*end!2
+.br
+.ie '\\n(.z'ds*div' \{\
+. di
+. nf
+. in (u;\\n[.l]-\\n[dl]/2)
+. ds*div
+. rm ds*div
+. ds*common-end
+.\}
+.el .@error-recover mismatched DE
+..
+.de DS
+.br
+.di ds*div
+.ie '\\$1'B' \{\
+. LD
+. nr \\n[.ev]:ds-type 4
+.\}
+.el \{\
+. ie '\\$1'L' .LD
+. el \{\
+. ie '\\$1'C' .CD
+. el \{\
+. ie '\\$1'R' .RD
+. el \{\
+. ie '\\$1'I' .ID \\$2
+. el .ID \\$1
+. \}
+. \}
+. \}
+. nr \\n[.ev]:ds-type 3
+.\}
+..
+.de ds@need
+.if '\\n(.z'' \{\
+. while \\n[.t]<=(\\$1)&(\\n[nl]>\\n[pg@header-bottom]) \{\
+. rs
+' sp \\n[.t]u
+. \}
+.\}
+..
+.de ds*end!3
+.br
+.ie '\\n(.z'ds*div' \{\
+. di
+. ds@need \\n[dn]
+. ev nf
+. ds*div
+. ev
+. rm ds*div
+. ds*common-end
+.\}
+.el .@error-recover mismatched DE
+..
+.de ds*end!4
+.ie '\\n(.z'ds*div' \{\
+. br
+. di
+. nf
+. in (u;\\n[.l]-\\n[dl]/2)
+. ds@need \\n[dn]
+. ds*div
+. rm ds*div
+. ds*common-end
+.\}
+.el .@error-recover mismatched DE
+..
+.\" ****************************
+.\" ******** module par ********
+.\" ****************************
+.\" Paragraph-level formatting.
+.nr PS 10
+.nr LL 6i
+.de par*vs
+.\" If it's too big to be in points, treat it as units.
+.ie (p;\\$1)>=40p .vs (u;\\$1)
+.el .vs (p;\\$1)
+..
+.de par@ab-indent
+.nr 0:li (u;\\n[LL]/12)
+.nr 0:ri \\n[0:li]
+..
+.de par*env-init
+.aln \\n[.ev]:PS PS
+.aln \\n[.ev]:VS VS
+.aln \\n[.ev]:LL LL
+.aln \\n[.ev]:MCLL LL
+.aln \\n[.ev]:LT LT
+.aln \\n[.ev]:MCLT LT
+.aln \\n[.ev]:PI PI
+.aln \\n[.ev]:PD PD
+.par@reset-env
+..
+.\" happens when the first page begins
+.de par@init
+.if !rLT .nr LT \\n[LL]
+.if !rFL .nr FL \\n[LL]*5/6
+.if !rVS .nr VS \\n[PS]+2
+.ps \\n[PS]
+.if !rDI .nr DI .5i
+.if !rQI .nr QI 5n
+.if !rPI .nr PI 5n
+.par*vs \\n[VS]
+.if !rPD .nr PD .3v
+.if !rDD .nr DD .5v
+.if !dFAM .ds FAM \\n[.fam]
+.nr par*adj \\n[.j]
+.par*env-init
+.ev h
+.par*env-init
+.ev
+.ev fn
+.par*env-init
+.ev
+.ev k
+.par*env-init
+.ev
+.aln 0:MCLL pg@colw
+.aln 0:MCLT pg@colw
+.aln k:MCLL pg@colw
+.aln k:MCLT pg@colw
+.if !rFPS .nr FPS \\n[PS]-2
+.if !rFVS .nr FVS (p;\\n[FPS]+2)
+.if !rFI .nr FI 2n
+.if !rFPD .nr FPD \\n[PD]/2
+.aln fn:PS FPS
+.aln fn:VS FVS
+.aln fn:LL FL
+.aln fn:LT FL
+.aln fn:PI FI
+.aln fn:PD FPD
+.aln fn:MCLL pg@fn-colw
+.aln fn:MCLT pg@fn-colw
+..
+.de par@reset-env
+.nr \\n[.ev]:il 0
+.nr \\n[.ev]:li 0
+.nr \\n[.ev]:ri 0
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+.nr \\n[.ev]:pli 0
+.nr \\n[.ev]:pri 0
+.nr \\n[.ev]:ds-type 0
+..
+.\" par@reset
+.de par@reset
+.br
+.ce 0
+.rj 0
+.ul 0
+.fi
+.ad \\n[par*adj]
+.ie \\n[pg@ncols]>1 \{\
+. ll (u;\\n[\\n[.ev]:MCLL]-\\n[\\n[.ev]:ri]-\\n[\\n[.ev]:pri])
+. lt \\n[\\n[.ev]:MCLT]u
+.\}
+.el \{\
+. ll (u;\\n[\\n[.ev]:LL]-\\n[\\n[.ev]:ri]-\\n[\\n[.ev]:pri])
+. lt \\n[\\n[.ev]:LT]u
+.\}
+.in (u;\\n[\\n[.ev]:li]+\\n[\\n[.ev]:pli])
+.ft 1
+.fam \\*[FAM]
+.ps \\n[\\n[.ev]:PS]
+.par*vs \\n[\\n[.ev]:VS]
+.ls 1
+.TA
+.hy 14
+..
+.als @RT par@reset
+.\" This can be redefined by the user.
+.de TA
+.ta T 5n
+..
+.de par*start
+.ds@auto-end
+.nr \\n[.ev]:pli \\$1
+.nr \\n[.ev]:pri \\$2
+.par@reset
+.sp \\n[\\n[.ev]:PD]u
+.ne 1v+\\n(.Vu
+..
+.de par@finish
+.nr \\n[.ev]:pli 0
+.nr \\n[.ev]:pri 0
+.par@reset
+..
+.\" normal LP
+.de @LP
+.par*start 0 0
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+..
+.de @PP
+.par*start 0 0
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+.ti +\\n[\\n[.ev]:ai]u
+..
+.de @QP
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+.par*start \\n[QI] \\n[QI]
+..
+.de @XP
+.par*start \\n[\\n[.ev]:PI] 0
+.ti -\\n[\\n[.ev]:PI]u
+..
+.de @IP
+.if \\n[.$]>1 .nr \\n[.ev]:ai (n;\\$2)
+.par*start \\n[\\n[.ev]:ai] 0
+.if !'\\$1'' \{\
+. \" Divert the label so as to freeze any spaces.
+. di par*label
+. in 0
+. nf
+\&\\$1
+. di
+. in
+. fi
+. chop par*label
+. ti -\\n[\\n[.ev]:ai]u
+. ie \\n[dl]+1n<=\\n[\\n[.ev]:ai] \\*[par*label]\h'|\\n[\\n[.ev]:ai]u'\c
+. el \{\
+\\*[par*label]
+. br
+. \}
+. rm par*label
+.\}
+..
+.de @RS
+.br
+.nr \\n[.ev]:li!\\n[\\n[.ev]:il] \\n[\\n[.ev]:li]
+.nr \\n[.ev]:ri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ri]
+.nr \\n[.ev]:ai!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ai]
+.nr \\n[.ev]:pli!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pli]
+.nr \\n[.ev]:pri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pri]
+.nr \\n[.ev]:il +1
+.nr \\n[.ev]:li +\\n[\\n[.ev]:ai]
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+.par@reset
+..
+.de @RE
+.br
+.ie \\n[\\n[.ev]:il] \{\
+. nr \\n[.ev]:il -1
+. nr \\n[.ev]:ai \\n[\\n[.ev]:ai!\\n[\\n[.ev]:il]]
+. nr \\n[.ev]:li \\n[\\n[.ev]:li!\\n[\\n[.ev]:il]]
+. nr \\n[.ev]:ri \\n[\\n[.ev]:ri!\\n[\\n[.ev]:il]]
+. nr \\n[.ev]:pli \\n[\\n[.ev]:pli!\\n[\\n[.ev]:il]]
+. nr \\n[.ev]:pri \\n[\\n[.ev]:pri!\\n[\\n[.ev]:il]]
+.\}
+.el .@error unbalanced \\$0
+.par@reset
+..
+.de @QS
+.br
+.nr \\n[.ev]:li!\\n[\\n[.ev]:il] \\n[\\n[.ev]:li]
+.nr \\n[.ev]:ri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ri]
+.nr \\n[.ev]:ai!\\n[\\n[.ev]:il] \\n[\\n[.ev]:ai]
+.nr \\n[.ev]:pli!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pli]
+.nr \\n[.ev]:pri!\\n[\\n[.ev]:il] \\n[\\n[.ev]:pri]
+.nr \\n[.ev]:il +1
+.nr \\n[.ev]:li +\\n[QI]
+.nr \\n[.ev]:ri +\\n[QI]
+.nr \\n[.ev]:ai \\n[\\n[.ev]:PI]
+.par@reset
+..
+.als @QE @RE
+.\" start boxed text
+.de B1
+.br
+.di par*box-div
+.nr \\n[.ev]:li +1n
+.nr \\n[.ev]:ri +1n
+.par@reset
+..
+.de @div-end!par*box-div
+.B2
+..
+.\" end boxed text
+.\" Postpone the drawing of the box until we're in the top-level diversion,
+.\" in case there's a footnote inside the box.
+.de B2
+.ie '\\n(.z'par*box-div' \{\
+. br
+. di
+. ds@need \\n[dn]
+. par*box-mark-top
+. ev nf
+. par*box-div
+. ev
+. nr \\n[.ev]:ri -1n
+. nr \\n[.ev]:li -1n
+. par@finish
+. par*box-draw \\n[.i]u \\n[.l]u
+.\}
+.el .@error B2 without B1
+..
+.de par*box-mark-top
+.ie '\\n[.z]'' .mk par*box-top
+.el \!.par*box-mark-top
+..
+.de par*box-draw
+.ie '\\n[.z]'' \{\
+. nr par*box-in \\n[.i]
+. nr par*box-ll \\n[.l]
+. nr par*box-vpt \\n[.vpt]
+. vpt 0
+. in \\$1
+. ll \\$2
+\v'-1v+.25m'\
+\D'l (u;\\n[.l]-\\n[.i]) 0'\
+\D'l 0 |\\n[par*box-top]u'\
+\D'l -(u;\\n[.l]-\\n[.i]) 0'\
+\D'l 0 -|\\n[par*box-top]u'
+. br
+. sp -1
+. in \\n[par*box-in]u
+. ll \\n[par*box-ll]u
+. vpt \\n[par*box-vpt]
+.\}
+.el \!.par*box-draw \\$1 \\$2
+..
+.de @SH
+.par@finish
+.\" Keep together the heading and the first two lines of the next paragraph.
+.ne 3v+\\n[\\n[.ev]:PD]u+\\n(.Vu
+.sp 1
+.ft B
+..
+.\" TL, AU, and AI are aliased to these in cov*ab-init.
+.de par@TL
+.par@finish
+.sp 1
+.ft B
+.ps +2
+.vs +3p
+.ce 9999
+..
+.de par@AU
+.par@finish
+.sp 1
+.ft I
+.ce 9999
+..
+.de par@AI
+.par@finish
+.sp .5
+.ce 9999
+..
+.\" In paragraph macros.
+.de NL
+.ps \\n[\\n[.ev]:PS]
+..
+.de SM
+.ps -2
+..
+.de LG
+.ps +2
+..
+.de R
+.ft R
+..
+.de par*set-font
+.ie \\n[.$] \{\
+. nr par*prev-font \\n[.f]
+\&\\$3\f[\\*[par*font-name!\\$0]]\\$1\f[\\n[par*prev-font]]\\$2
+.\}
+.el .ft \\*[par*font-name!\\$0]
+..
+.ds par*font-name!B B
+.ds par*font-name!I I
+.ds par*font-name!BI BI
+.ds par*font-name!CW CR
+.als B par*set-font
+.als I par*set-font
+.als BI par*set-font
+.als CW par*set-font
+.\" underline a word
+.de UL
+\Z'\\$1'\v'.25m'\D'l \w'\\$1'u 0'\v'-.25m'\\$2
+..
+.\" box a word
+.de BX
+.nr par*bxw \w'\\$1'+.4m
+\Z'\v'.25m'\D'l 0 -1m'\D'l \\n[par*bxw]u 0'\D'l 0 1m'\D'l -\\n[par*bxw]u 0''\
+\Z'\h'.2m'\\$1'\
+\h'\\n[par*bxw]u'
+..
+.\" The first time UX is used, put a registered mark after it.
+.ds par*ux-rg \(rg
+.de UX
+\s[\\n[.s]*8u/10u]UNIX\s0\\$1\\*[par*ux-rg]
+.ds par*ux-rg
+..
+.ds par@sup-start \v'-.9m\s'\En[.s]*7u/10u'+.7m'
+.als { par@sup-start
+.ds par@sup-end \v'-.7m\s0+.9m'
+.als } par@sup-end
+.\" footnote paragraphs
+.\" FF is the footnote format
+.nr FF 0
+.\" This can be redefined. It gets a second argument of `no' if the first
+.\" argument was supplied by the user, rather than automatically.
+.de FP
+.br
+.if !d par*fp!\\n[FF] \{\
+. @error unknown footnote format `\\n[FF]'
+. nr FF 0
+.\}
+.ie '\\$2'no' .par*fp!\\n[FF]-no "\\$1"
+.el .par*fp!\\n[FF] "\\$1"
+..
+.de par*fp!0
+.@PP
+\&\\*[par@sup-start]\\$1\\*[par@sup-end]\ \c
+..
+.de par*fp!0-no
+.@PP
+\&\\$1\ \c
+..
+.de par*fp!1
+.@PP
+\&\\$1.\ \c
+..
+.de par*fp!1-no
+.@PP
+\&\\$1\ \c
+..
+.de par*fp!2
+.@LP
+\&\\$1.\ \c
+..
+.de par*fp!2-no
+.@LP
+\&\\$1\ \c
+..
+.de par*fp!3
+.@IP "\\$1." (u;\\n[\\n[.ev]:PI]*2)
+..
+.de par*fp!3-no
+.@IP "\\$1" (u;\\n[\\n[.ev]:PI]*2)
+..
+.\" ***************************
+.\" ******** module nh ********
+.\" ***************************
+.\" Numbered headings.
+.\" nh*hl is the level of the last heading
+.nr nh*hl 0
+.\" numbered heading
+.de @NH
+.ie '\\$1'S' \{\
+. shift
+. nr nh*hl 0
+. while \\n[.$] \{\
+. nr nh*hl +1
+. nr H\\n[nh*hl] 0\\$1
+. shift
+. \}
+. if !\\n[nh*hl] \{\
+. nr H1 1
+. nr nh*hl 1
+. @error missing arguments to .NH S
+. \}
+.\}
+.el \{\
+. nr nh*ohl \\n[nh*hl]
+. ie \\n[.$] \{\
+. nr nh*hl 0\\$1
+. ie \\n[nh*hl]<=0 \{\
+. nr nh*ohl 0
+. nr nh*hl 1
+. \}
+. el \{\
+. if \\n[nh*hl]-\\n[nh*ohl]>1 \
+. @warning .NH \\n[nh*ohl] followed by .NH \\n[nh*hl]
+. \}
+. \}
+. el .nr nh*hl 1
+. while \\n[nh*hl]>\\n[nh*ohl] \{\
+. nr nh*ohl +1
+. nr H\\n[nh*ohl] 0
+. \}
+. nr H\\n[nh*hl] +1
+.\}
+.ds SN
+.nr nh*i 0
+.while \\n[nh*i]<\\n[nh*hl] \{\
+. nr nh*i +1
+. as SN \\n[H\\n[nh*i]].
+.\}
+.SH
+\\*[SN]
+..
+.\" ****************************
+.\" ******** module toc ********
+.\" ****************************
+.\" Table of contents generation.
+.de XS
+.da toc*div
+.ev h
+.par@reset
+.fi
+.ie \\n[.$] .XA "\\$1"
+.el .XA
+..
+.de @div-end!toc*div
+.XE
+..
+.de XA
+.ie '\\n(.z'toc*div' \{\
+. if d toc*num .toc*end-entry
+. ie \\n[.$] \{\
+. ie '\\$1'no' .ds toc*num
+. el .ds toc*num "\\$1
+. \}
+. el .ds toc*num \\n[PN]
+. in (n;0\\$2)
+.\}
+.el .@error XA without XS
+..
+.de XE
+.ie '\\n(.z'toc*div' \{\
+. if d toc*num .toc*end-entry
+. ev
+. di
+.\}
+.el .@error XS without XE
+..
+.de toc*end-entry
+\\a\\t\\*[toc*num]
+.br
+.rm toc*num
+..
+.de PX
+.1C
+.if !'\\$1'no' \{\
+. ce 1
+. ps \\n[PS]+2
+. ft B
+\\*[TOC]
+. ft
+. ps
+.\}
+.nf
+.char \[toc*leader-char] .\h'1m'
+.lc \[toc*leader-char]
+.ta (u;\\n[.l]-\\n[.i]-\w'000') (u;\\n[.l]-\\n[.i])R
+.sp 2
+.toc*div
+.par@reset
+..
+.\" print the table of contents on page i
+.de TC
+.P1
+.pg@begin 1 i
+.PX \\$1
+..
+.\" ****************************
+.\" ******** module eqn ********
+.\" ****************************
+.\" Eqn support.
+.de EQ
+..
+.de EN
+..
+.de @EQ
+.br
+.ds eqn*num "\\$2
+.ie '\\$1'L' .nr eqn*type 0
+.el \{\
+. ie '\\$1'I' .nr eqn*type 1
+. el \{\
+. nr eqn*type 2
+. if !'\\$1'C' .ds eqn*num "\\$1
+. \}
+.\}
+.di eqn*div
+.in 0
+.nf
+..
+.de @div-end!eqn*div
+.@EN
+..
+.\" Note that geqn mark and lineup work correctly in centered equations.
+.de @EN
+.ie !'\\n(.z'eqn*div' .@error-recover mismatched EN
+.el \{\
+. br
+. di
+. nr eqn*have-num 0
+. if !'\\*[eqn*num]'' .nr eqn*have-num 1
+. if \\n[dl]:\\n[eqn*have-num] \{\
+. sp \\n[DD]u
+. par@reset
+. ds eqn*tabs \\n[.tabs]
+. nf
+. ie \\n[dl] \{\
+. ds@need \\n[dn]u-1v+\n[.V]u
+. chop eqn*div
+. ie \\n[eqn*type]=0 \{\
+. ta (u;\\n[.l]-\\n[.i])R
+\\*[eqn*div]\t\\*[eqn*num]
+. \}
+. el \{\
+. ie \\n[eqn*type]=1 .ta \\n[DI]u \
+(u;\\n[.l]-\\n[.i])R
+. el .ta (u;\\n[.l]-\\n[.i]/2)C \
+(u;\\n[.l]-\\n[.i])R
+\t\\*[eqn*div]\t\\*[eqn*num]
+. \}
+. \}
+. el \{\
+. ta (u;\\n[.l]-\\n[.i])R
+\t\\*[eqn*num]
+. \}
+. sp \\n[DD]u
+. fi
+. ta \\*[eqn*tabs]
+. \}
+.\}
+..
+.\" ****************************
+.\" ******** module tbl ********
+.\" ****************************
+.\" Tbl support.
+.nr tbl*have-header 0
+.de TS
+.\" The break is necessary in the case where the first page has not yet begun.
+.br
+.sp \\n[DD]u
+.if '\\$1'H' .di tbl*header-div
+..
+.de tbl@top-hook
+.if \\n[tbl*have-header] \{\
+. ie \\n[.t]-\\n[tbl*header-ht]-1v .tbl*print-header
+. el .sp \\n[.t]u
+.\}
+..
+.de tbl*print-header
+.ev nf
+.tbl*header-div
+.ev
+.mk #T
+..
+.de TH
+.ie '\\n[.z]'tbl*header-div' \{\
+. nr T. 0
+. T#
+. br
+. di
+. ie \\n[dn]+\\n[FM]+\\n[HM]+2v>=\\n[.p] \{\
+. @error ridiculously long table header
+. ds@need \\n[dn]
+. tbl*print-header
+. \}
+. el \{\
+. nr tbl*header-ht \\n[dn]
+. ds@need \\n[dn]u+1v
+. tbl*print-header
+. nr tbl*have-header 1
+. \}
+.\}
+.el .@error-recover .TH without .TS H
+..
+.de @div-end!tbl*header-div
+.TH
+.TE
+..
+.de TE
+.ie '\\n(.z'tbl*header-div' .@error-recover .TS H but no .TH before .TE
+.el \{\
+. nr tbl*have-header 0
+. sp \\n[DD]u
+.\}
+.\" reset tabs
+.TA
+..
+.de tbl@bottom-hook
+.if \\n[tbl*have-header] \{\
+. nr T. 1
+. T#
+.\}
+..
+.de T&
+..
+.\" ****************************
+.\" ******** module pic ********
+.\" ****************************
+.\" Pic support.
+.\" PS height width
+.de PS
+.br
+.sp \\n[DD]u
+.ie \\n[.$]<2 .@error bad arguments to PS (not preprocessed with pic?)
+.el \{\
+. ds@need (u;\\$1)+1v
+. in +(u;\\n[.l]-\\n[.i]-\\$2/2)
+.\}
+..
+.de PE
+.par@reset
+.sp \\n[DD]u+.5m
+..
+.\" ****************************
+.\" ******** module ref ********
+.\" ****************************
+.\" Refer support.
+.de ]-
+.rm [A [B [C [D [E [G [I [J [N [O [P [Q [R [S [T [V
+.rm ref*string
+..
+.\" Other
+.ds ref*spec!0 Q A T S V N P I C D O
+.\" Journal article
+.ds ref*spec!1 Q A T J S V N P I C D O
+.\" Book
+.ds ref*spec!2 Q A T S V P I C D O
+.\" Article within book
+.ds ref*spec!3 Q A T B E S V P I C D O
+.\" Tech report
+.ds ref*spec!4 Q A T R G P I C D O
+.\" ][ type
+.de ][
+.ie d ref*spec!\\$1 .ref*build \\*[ref*spec!\\$1]
+.el \{\
+. @error unknown reference type `\\$1'
+. ref*build \\*[ref*spec!0]
+.\}
+.ref*print
+.rm ref*string
+.rm [F
+..
+.\" start of reference number
+.ds [. \\*[par@sup-start]
+.\" end of reference number
+.ds .] \\*[par@sup-end]
+.\" period before reference
+.ds <. .
+.\" period after reference
+.ds >. \" empty
+.\" comma before reference
+.ds <, ,
+.\" comma after reference
+.ds >, \" empty
+.\" start collected references
+.de ]<
+.als ref*print ref*end-print
+.SH
+\&\\*[REFERENCES]
+.par@reset
+..
+.\" end collected references
+.de ]>
+.par@finish
+.als ref*print ref*normal-print
+..
+.de ref*normal-print
+.ie d [F .FS "\\*([.\\*([F\\*(.]"
+.el .FS \&
+\\*[ref*string]
+.FE
+..
+.de ref*end-print
+.ie d [F .IP "\\*([F."
+.el .XP
+\\*[ref*string]
+..
+.als ref*print ref*normal-print
+.de ref*build
+.rm ref*string ref*post-punct
+.nr ref*suppress-period 1
+.while \\n[.$] \{\
+. if d [\\$1 \{\
+. ie d ref*add-\\$1 .ref*add-\\$1
+. el .ref*add-dflt \\$1
+. \}
+. shift
+.\}
+.\" now add a final period
+.ie d ref*string \{\
+. if !\\n[ref*suppress-period] .as ref*string .
+. if d ref*post-punct \{\
+. as ref*string "\\*[ref*post-punct]
+. rm ref*post-punct
+. \}
+.\}
+.el .ds ref*string
+..
+.de ref*add-T
+.ref*field T , "\\*Q" "" "\\*U"
+.if r [T .nr ref*suppress-period \\n([T
+..
+.de ref*add-P
+.ie \\n([P>0 .ref*field P , "pp. "
+.el .ref*field P , "p. "
+..
+.de ref*add-J
+.ref*field J , \fI "" \fP
+..
+.de ref*add-D
+.ref*field D "" ( )
+..
+.de ref*add-E
+.ref*field E , "ed. "
+..
+.de ref*add-G
+.ref*field G "" ( )
+..
+.de ref*add-B
+.ref*field B "" "in \fI" "" \fP
+..
+.de ref*add-O
+.ref*field O .
+.ie r [O .nr ref*suppress-period \\n([O
+.el .nr ref*suppress-period 1
+..
+.de ref*add-A
+.ref*field A ,
+.if r [A .nr ref*suppress-period \\n([A
+..
+.de ref*add-dflt
+.ref*field \\$1 ,
+..
+.\" First argument is the field letter.
+.\" Second argument is the punctuation character to use to separate this field
+.\" from the previous field.
+.\" Third argument is a string with which to prefix this field.
+.\" Fourth argument is a string with which to postfix this field.
+.\" Fifth argument is a string to add after the punctuation character supplied
+.\" by the next field.
+.de ref*field
+.if d ref*string \{\
+. ie d ref*post-punct \{\
+. as ref*string "\\$2\\*[ref*post-punct] \"
+. rm ref*post-punct
+. \}
+. el .as ref*string "\\$2 \"
+.\}
+.as ref*string "\\$3\\*([\\$1\\$4
+.if \\n[.$]>4 .ds ref*post-punct "\\$5
+.nr ref*suppress-period 0
+..
+.\" ****************************
+.\" ******** module acc ********
+.\" ****************************
+.\" Accents and special characters.
+.ds Q \)``\)
+.ds U \)''\)
+.ds - \(em
+.\" Characters
+.if !c\(rg .char \(rg (R)
+.if !c\(ah .char \(ah \v'-.55m'\s[\En[.s]/2u]v\s0\v'.55m'
+.if !c\(ad .char \(ad \v'-.55m'\s[\En[.s]*7u/10u].\h'.05m'.\s0\v'.55m'
+.if !c\(a- .char \(a- \v'-.55m'\D'l .25m 0'\v'.55m'
+.if !c\(ao .char \(ao \v'-.55m'\s[\En[.s]*6u/10u]\D'c .25m'\s0\v'.55m'
+.if !c\(ac .char \(ac \s[\En[.s]*8u/10u]\v'.05m',\v'-.05m'\s0
+.if !c\(ho .char \(ho \s[\En[.s]/2u]\v'.4m'c\v'-.4m'\s0
+.if !c\(-D .char \(-D \Z'\v'-.1m'-'D
+.if !c\(Sd .char \(Sd \Z'\v'-.3m'\h'.2m'-'\(pd
+.if !c\(TP .char \(TP I\h'-.25m'\v'-.33m'\s[\En[.s]*6u/10u]\v'.33m'D\
+\v'-.33m'\s0\v'.33m'
+.if !c\(Tp .char \(Tp \zlp
+.if !c\(ss .char \(ss \(*b
+.if !c\(AE .char \(AE A\h'-.3m'E
+.if !c\(ae .char \(ae a\h'-.19m'e
+.if !c\(OE .char \(OE O\h'-.25m'E
+.if !c\(oe .char \(oe o\h'-.14m'e
+.if !c\(r? .char \(r? \Z'\h'.1m'\v'-.15m'\s[\En[.s]*7u/10u]i\s0\v'.15m''\
+\v'.15m'\s[\En[.s]*7u/10u]c\s0\v'-.15m'
+.if !c\(r! .char \(r! \h'.1m'\Z'\v'-.4m'\s[\En[.s]*8u/10u].\s0\v'.4m''\
+\s[\En[.s]*8u/10u]\v'.4m'\(or\v'-.4m'\s0\h'.1m'
+.\" The idea of this definition is for the top of the 3 to be at the x-height.
+.\" A yogh really ought to have a little line going north-west from the top
+.\" left of the 3.
+.if !c\[yogh] .char \[yogh] \Z'\v'\w'x'*0-\En[rst]u'\s[\En[.s]*8u/10u]\
+\v'\w'3'*0+\En[rst]u'3\s0'\h'\w'\s[\En[.s]*8u/10u]3'u'
+.\" Accents
+.de acc*over-def
+.ds \\$1 \Z'\v'(u;\w'x'*0+\En[rst]-\En[.cht])'\
+\h'(u;-\En[skw]+(-\En[.w]-\w'\\$2'/2)+\En[.csk])'\\$2'
+..
+.de acc*under-def
+.ds \\$1 \Z'\v'\En[.cdp]u'\h'(u;-\En[.w]-\w'\\$2'/2)'\\$2'
+..
+.de acc*slash-def
+.ds \\$1 \Z'\h'(u;-\En[.w]-\w'\\$2'/2)'\
+\v'(u;\En[.cdp]-\En[.cht]+\En[rst]+\En[rsb]/2)'\\$2'
+..
+.de acc*prefix-def
+.ds \\$1 \Z'\h'(u;\w'x'-\w'\\$2'/2)'\\$2'
+..
+.acc*prefix-def ' \'
+.acc*prefix-def ` \`
+.acc*prefix-def ^ ^
+.acc*prefix-def , \(ac
+.acc*prefix-def : \(ad
+.acc*prefix-def ~ ~
+.\" improved accent marks
+.de AM
+.acc*over-def ' \'
+.acc*over-def ` \`
+.acc*over-def ^ ^
+.acc*over-def ~ ~
+.acc*over-def : \(ad
+.acc*over-def v \(ah
+.acc*over-def _ \(a-
+.acc*over-def o \(ao
+.acc*under-def , \(ac
+.acc*under-def . \s[\En[.s]*8u/10u]\v'.2m'.\v'-.2m'\s0
+.acc*under-def hook \(ho
+.acc*slash-def / /
+.char \[hooko] o\\\\*[hook]
+.ds q \[hooko]
+.ds 3 \[yogh]
+.ds D- \(-D\" Icelandic uppercase eth
+.ds d- \(Sd\" Icelandic lowercase eth
+.ds Th \(TP\" Icelandic uppercase thorn
+.ds th \(Tp\" Icelandic lowercase thorn
+.ds 8 \(ss\" German double s
+.ds Ae \(AE\" AE ligature
+.ds ae \(ae\" ae ligature
+.ds Oe \(OE\" OE ligature
+.ds oe \(oe\" oe ligature
+.ds ? \(r?\" upside down ?
+.ds ! \(r!\" upside down !
+..
+.\" Make sure that no blank lines creep in at the end of this file.
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 000000000..213ed43f0
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,122 @@
+#Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+MANROOT=/usr/local/man
+# MAN1EXT is the man section for user commands
+MAN1EXT=1
+MAN1DIR=$(MANROOT)/man$(MAN1EXT)
+# MAN5EXT is the man section for file formats
+MAN5EXT=5
+MAN5DIR=$(MANROOT)/man$(MAN5EXT)
+# MAN7EXT is the man section for macros
+MAN7EXT=7
+MAN7DIR=$(MANROOT)/man$(MAN7EXT)
+# FONTDIR says where to install dev*/*
+FONTDIR=/usr/local/lib/groff/font
+# FONTPATH says where to look for dev*/*
+FONTPATH=.:$(FONTDIR):/usr/lib/font
+MACRODIR=/usr/local/lib/groff/tmac
+# MACROPATH says where to look for tmac.* macro files
+MACROPATH=.:$(MACRODIR):/usr/lib/tmac
+# DEVICE is the default device
+DEVICE=ps
+# HYPHENFILE is the file containing the hyphenation patterns
+HYPHENFILE=/usr/local/lib/groff/hyphen
+# Suffix to be used for refer index files.
+INDEX_SUFFIX=.i
+# Directory containing the default refer index.
+DEFAULT_INDEX_DIR=/usr/dict/papers
+# The filename (without suffix) of the default refer index.
+DEFAULT_INDEX_NAME=Ind
+# COMMON_WORDS_FILE is a file containing a list of common words.
+COMMON_WORDS_FILE=/usr/local/lib/eign
+TMAC_S=gs
+BROKEN_SPOOLER_FLAGS=0
+SHELL=/bin/sh
+MAN1PAGES=gtroff.n gpic.n grops.n groff.n geqn.n gtbl.n psbb.n gsoelim.n \
+ addftinfo.n grodvi.n grotty.n tfmtodit.n afmtodit.n grog.n \
+ grefer.n gindxbib.n glookbib.n lkbib.n pfbtops.n
+MAN5PAGES=groff_font.n groff_out.n
+MAN7PAGES=groff_me.n groff_ms.n
+MANPAGES= $(MAN1PAGES) $(MAN5PAGES) $(MAN7PAGES)
+
+.SUFFIXES: .man .n
+
+.man.n:
+ @echo Making $@ from $<
+ @-rm -f $@
+ @sed -e "s;@HYPHENFILE@;$(HYPHENFILE);" \
+ -e "s;@FONTDIR@;$(FONTDIR);" \
+ -e "s;@FONTPATH@;$(FONTPATH);" \
+ -e "s;@MACRODIR@;$(MACRODIR);" \
+ -e "s;@MACROPATH@;$(MACROPATH);" \
+ -e "s;@DEVICE@;$(DEVICE);" \
+ -e "s;@DEFAULT_INDEX@;$(DEFAULT_INDEX_DIR)/$(DEFAULT_INDEX_NAME);" \
+ -e "s;@DEFAULT_INDEX_NAME@;$(DEFAULT_INDEX_NAME);" \
+ -e "s;@INDEX_SUFFIX@;$(INDEX_SUFFIX);" \
+ -e "s;@COMMON_WORDS_FILE@;$(COMMON_WORDS_FILE);" \
+ -e "s;@MAN1EXT@;$(MAN1EXT);" \
+ -e "s;@MAN5EXT@;$(MAN5EXT);" \
+ -e "s;@MAN7EXT@;$(MAN7EXT);" \
+ -e "s;@TMAC_S@;$(TMAC_S);" \
+ -e "s;@BROKEN_SPOOLER_FLAGS@;$(BROKEN_SPOOLER_FLAGS);" \
+ -e "s;@VERSION@;`cat ../VERSION`;" \
+ -e "s;@MDATE@;`$(SHELL) mdate.sh $<`;" \
+ $< >$@
+ @chmod 444 $@
+
+all: $(MANPAGES)
+
+install.nobin: $(MANPAGES)
+ -[ -d $(MAN1DIR) ] || mkdir $(MAN1DIR)
+ -[ -d $(MAN5DIR) ] || mkdir $(MAN5DIR)
+ -[ -d $(MAN7DIR) ] || mkdir $(MAN7DIR)
+ @for page in $(MAN1PAGES) ; do \
+ target=$(MAN1DIR)/`basename $$page .n`.$(MAN1EXT); \
+ rm -f $$target ; \
+ echo cp $$page $$target ; \
+ cp $$page $$target ; \
+ done
+ @for page in $(MAN5PAGES) ; do \
+ target=$(MAN5DIR)/`basename $$page .n`.$(MAN5EXT); \
+ rm -f $$target ; \
+ echo cp $$page $$target ; \
+ cp $$page $$target ; \
+ done
+ @for page in $(MAN7PAGES) ; do \
+ target=$(MAN7DIR)/`basename $$page .n`.$(MAN7EXT); \
+ rm -f $$target ; \
+ echo cp $$page $$target ; \
+ cp $$page $$target ; \
+ done
+
+$(MANPAGES): ../VERSION
+
+install.bin:
+
+install: install.bin install.nobin
+
+clean:
+ -rm -f $(MANPAGES)
+
+distclean: clean
+
+realclean: clean
+
+TAGS:
diff --git a/man/addftinfo.man b/man/addftinfo.man
new file mode 100644
index 000000000..7c937479f
--- /dev/null
+++ b/man/addftinfo.man
@@ -0,0 +1,85 @@
+.\" -*- nroff -*-
+.TH ADDFTINFO @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+addftinfo \- add information to troff font files for use with groff
+.SH SYNOPSIS
+.B addftinfo
+[
+.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
+Each of the 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 font (5)
+.BR groff_font (@MAN5EXT@),
+.BR groff (@MAN1EXT@),
+.I "Groff Character Names"
diff --git a/man/afmtodit.man b/man/afmtodit.man
new file mode 100644
index 000000000..9a85c9a03
--- /dev/null
+++ b/man/afmtodit.man
@@ -0,0 +1,182 @@
+.TH AFMTODIT @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+afmtodit \- create font files for use with groff \-Tps
+.SH SYNOPSIS
+.B afmtodit
+[
+.B \-s
+]
+[
+.BI \-e enc_file
+]
+[
+.BI \-i n
+]
+[
+.BI \-a n
+]
+.I afm_file
+.I map_file
+.I font
+.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 gtroff .
+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 \-s
+The font is special.
+The effect of this option is to add the
+.B special
+command to the font file.
+.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 desciption 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/textmap
+Standard mapping.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR perl (1)
diff --git a/man/geqn.man b/man/geqn.man
new file mode 100644
index 000000000..5df3017ef
--- /dev/null
+++ b/man/geqn.man
@@ -0,0 +1,774 @@
+.\" -*- nroff -*-
+.ie \n(.V<\n(.v .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.TH GEQN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+geqn \- groff equation formatter
+.SH SYNOPSIS
+.B geqn
+[
+.B \-C
+]
+[
+.B \-D
+]
+[
+.B \-N
+]
+[
+.BI \-d cc
+]
+[
+.BI \-T dev
+]
+[
+.B \-r
+]
+[
+.B \-v
+]
+[
+.BI \-f F
+]
+[
+.BI \-s n
+]
+[
+.BI \-p n
+]
+[
+.BI \-m n
+]
+[
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B geqn
+is a preprocessor for
+.B groff
+for formatting equations.
+Normally, it should be invoked using the
+.B \-e
+option of
+.BR groff .
+The syntax is quite compatible with
+.BR eqn .
+The output of
+.B geqn
+cannot be processed with
+.BR ditroff ;
+it must be processed with
+.BR gtroff .
+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 .EQ
+and
+.B .EN
+even when followed by a character other than space or newline.
+.TP
+.B \-D
+Draw fraction bars using the
+.B \eD
+escape sequence, rather than with the
+.B \el
+escape sequence and the
+.B \e(ru
+character.
+.TP
+.B \-N
+Don't allow newlines within delimiters.
+This option allows
+.B geqn
+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 .
+Geqn will not reduce the size of subscripts or superscripts to
+a smaller size than
+.IR n .
+.TP
+.BI \-T dev
+The output is for device
+.IR dev .
+A macro whose name is the name of the output device
+is automatically defined with a value of
+.BR 1 .
+Nothing else depends on what the output device is;
+geqn does not itself read font or device description files.
+The default output device is
+.BR @DEVICE@ .
+.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.
+Geqn 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 geqn and eqn are described here.
+.LP
+Most of the new features of
+.B geqn
+are based on \*(tx.
+There are some references to the differences between \*(tx and
+.B geqn
+below;
+these may safely be ignored if you do not know \*(tx.
+.SS Automatic spacing
+.LP
+.B geqn
+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 punctutation 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\ " text """"
+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 """" text """"
+.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 gtroff (@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\ }
+.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 geqn
+should assume an x height of 0.45 ems.
+.RS
+.LP
+Possible parameters are as follows.
+Values are in units of hundreths 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 geqn
+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.
+.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\ " file """"
+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 geqn
+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 "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.I The\ \*(txbook
diff --git a/man/gindxbib.man b/man/gindxbib.man
new file mode 100644
index 000000000..e3c32c358
--- /dev/null
+++ b/man/gindxbib.man
@@ -0,0 +1,188 @@
+.\" -*- nroff -*-
+.ds g g
+.ds G G
+.TH \*GINDXBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+\*gindxbib \- make inverted index for bibliographic databases
+.SH SYNOPSIS
+.ad l
+.nr i \n(.i
+.in +\w'\fB\*gindxbib 'u
+.ti \niu
+.B \*gindxbib
+.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 b
+.SH DESCRIPTION
+.B \*gindxbib
+makes an inverted index for the bibliographic databases in
+.IR filename \|.\|.\|.
+for use with
+.BR \*grefer (@MAN1EXT@),
+.BR \*glookbib (@MAN1EXT@),
+and
+.BR \*glkbib (@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
+.B 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 \*gindxbib
+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 \*grefer (@MAN1EXT@),
+.BR \*glkbib (@MAN1EXT@),
+.BR \*glookbib (@MAN1EXT@)
diff --git a/man/glookbib.man b/man/glookbib.man
new file mode 100644
index 000000000..1a01f57f8
--- /dev/null
+++ b/man/glookbib.man
@@ -0,0 +1,60 @@
+.\" -*- nroff -*-
+.ds g g
+.ds G G
+.TH \*GLOOKBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+\*glookbib \- search bibliographic databases
+.SH SYNOPSIS
+.B \*glookbib
+[
+.B \-v
+]
+[
+.BI \-i string
+]
+[
+.BI \-t n
+]
+.IR filename \|.\|.\|.
+.SH DESCRIPTION
+\*glookbib 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 \*gindxbib (@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 \*grefer (@MAN1EXT@),
+.BR \*glkbib (@MAN1EXT@),
+.BR \*gindxbib (@MAN1EXT@)
diff --git a/man/gpic.man b/man/gpic.man
new file mode 100644
index 000000000..21f664ab7
--- /dev/null
+++ b/man/gpic.man
@@ -0,0 +1,684 @@
+.\" -*- nroff -*-
+.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 \^
+.TH GPIC @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+gpic \- compile pictures for troff or TeX
+.SH SYNOPSIS
+.B gpic
+[
+.B \-pvxzC
+]
+[
+.I filename
+\&.\|.\|.
+]
+.br
+.B gpic
+.B \-t
+[
+.B \-cvzC
+]
+[
+.I filename
+\&.\|.\|.
+]
+.SH DESCRIPTION
+.LP
+.B gpic
+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
+.LP
+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 \-p
+Grops mode.
+Use this if the output of gpic will be processed by
+.B gtroff
+and
+.BR grops .
+In this mode gpic can make use the
+.BI \eX'ps:\ anything '
+command.
+Currently it does this only for the
+.B aligned
+attribute.
+.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 \-x
+Assume a troff driver that supports the gpic extensions
+to the standard troff drawing commands.
+These are described in
+.BR groff_out (@MAN5EXT@).
+These extensions can be used with device-independent troff,
+provided that the version being used passes through unrecognised
+drawing commands in the standard way.
+This implies
+.BR \-z .
+.TP
+.B \-z
+Draw dots using zero-length lines. This applies to both \*(tx mode and
+.B troff
+mode.
+.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 gpic and the original pic.
+Many of these differences also apply to newer versions of pic.
+.SS \*(tx mode
+.LP
+\*(tx mode is enabled by the
+.B \-t
+option.
+In \*(tx mode, gpic 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 \fIexpr\fR
+.ns
+.TP
+\fBprint\fR \fIposition\fR
+.ns
+.TP
+\fBprint\fR \fB"\fItext\*(ic\fB"\fR
+Print the value of
+.IR expr ,
+.I position
+or
+.I text
+on stderr.
+.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 """%g"""
+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;
+gpic does only very limited checking of the string.
+This is deprecated in favour of
+.BR sprintf .
+.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 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
+.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 gpic will always scale a picture by the same amount
+vertically as horizontally.
+This is different from the
+.SM DWB
+2.0 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(" format ""","\ arg , .\|.\|. )
+.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 gpic 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 is only available with the
+.B \-p
+option.
+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.
+.SH FILES
+.TP \w'\fB@MACRODIR@/tmac.pic'u+3n
+.B
+@MACRODIR@/tmac.pic
+Example definitions of the
+.B PS
+and
+.B PE
+macros.
+.SH "SEE ALSO"
+.BR gtroff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR tex (1),
+.I
+Tpic: Pic for
+.IR \*(tx .
+.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.
diff --git a/man/grefer.man b/man/grefer.man
new file mode 100644
index 000000000..f5bc52d84
--- /dev/null
+++ b/man/grefer.man
@@ -0,0 +1,1272 @@
+.\" -*- nroff -*-
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.ds g g
+.ds G G
+.TH \*GREFER @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+\*grefer \- preprocess bibliographic references for groff
+.SH SYNOPSIS
+.ad l
+.nr i \n(.i
+.in +\w'\fB\*grefer 'u
+.ti \niu
+.B \*grefer
+.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 b
+.SH DESCRIPTION
+.B \*grefer
+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 \*grefer
+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 \*grefer
+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 in a single formatted reference.
+.LP
+The interpretation of lines between
+.B .R1
+and
+.B .R2
+as commands is a new feature of
+.BR \*grefer .
+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 \*grefer
+generates
+.B .lf
+lines so that filenames and line numbers in messages produced
+by commands that read
+.B \*grefer
+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 gsoelim (@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-puntuation
+.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 occurences of the
+.B A
+field.
+The order is siginificant.
+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 occurence 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 charater 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 """"
+extends to the next
+.B """"
+that is not followed by another
+.BR """" .
+If there is no such
+.B """"
+the word extends to the end of the line.
+Pairs of
+.B """"
+in a word beginning with
+.B """"
+collapse to a single
+.BR """" .
+Neither
+.B #
+nor
+.B ;
+are recognized inside
+.BR """" 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 bibligraphic databases
+.IR filename \|.\|.\|.
+For each
+.I filename
+if an index
+.IB filename @INDEX_SUFFIX@
+created by
+.BR \*gindxbib (@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-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.
+\*grefer 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 \*gindxbib (@MAN1EXT@),
+.BR \*glookbib (@MAN1EXT@),
+.BR \*glkbib (@MAN1EXT@)
+.br
+.SH BUGS
+In label expressions,
+.B <>
+expressions are ignored inside
+.BI . char
+expressions.
diff --git a/man/grodvi.man b/man/grodvi.man
new file mode 100644
index 000000000..e3c1ecb00
--- /dev/null
+++ b/man/grodvi.man
@@ -0,0 +1,148 @@
+.\" -*- nroff -*-
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.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 \|.\|.\|.
+]
+.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 gtroff\ \-Tdvi ;
+it will also input the macros
+.BR @MACRODIR@/tmac.dvi ;
+if the input is being preprocessed with
+.B geqn
+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 gtroff
+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
+Search directory
+.IB dir /devdvi
+for font and device description files.
+.SH FILES
+.TP
+.B @FONTDIR@/devdvi/DESC
+Device desciption file.
+.TP
+.B @FONTDIR@/devdvi/ F
+Font description file for font
+.IR F .
+.TP
+.B @MACRODIR@/tmac.dvi
+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 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 gtroff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@)
+.br
+.I "Groff Character Names"
diff --git a/man/groff.man b/man/groff.man
new file mode 100644
index 000000000..ddcf7c015
--- /dev/null
+++ b/man/groff.man
@@ -0,0 +1,283 @@
+.\" -*- nroff -*-
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.TH GROFF @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+groff \- front end for the groff document formatting system
+.SH SYNOPSIS
+.B groff
+[
+.B \-tpeszaivhblCENRVZ
+]
+[
+.BI \-w name
+]
+[
+.BI \-W name
+]
+[
+.BI \-H file
+]
+[
+.BI \-m name
+]
+[
+.BI \-F dir
+]
+[
+.BI \-T dev
+]
+[
+.BI \-f fam
+]
+[
+.BI \-M dir
+]
+[
+.BI \-d cs
+]
+[
+.BI \-r cn
+]
+[
+.BI \-n num
+]
+[
+.BI \-o list
+]
+[
+.BI \-P arg
+]
+[
+.IR files \|.\|.\|.\|
+]
+.SH DESCRIPTION
+.B groff
+is a front-end to the groff document formatting systems.
+Normally it runs the
+.B gtroff
+program and a postprocessor appropriate for the selected
+device.
+Available devices are:
+.TP
+.B ps
+For PostScript printers and previewers
+.TP
+.B dvi
+For TeX dvi format
+.TP
+.B X75
+For a 75 dpi X11 previewer
+.TP
+.B X100
+For a 100dpi X11 previewer
+.TP
+.B Xps
+For X11 previewer used with intermediate output for
+.B ps
+device.
+.TP
+.B ascii
+For typewriter-like devices
+.TP
+.B latin1
+For typewriter-like devices using the ISO Latin-1 character set.
+.LP
+The default device is
+.BR @DEVICE@ .
+It can optionally preprocess with any of
+.BR gpic ,
+.BR geqn ,
+.BR gtbl ,
+.BR grefer ,
+or
+.B gsoelim.
+It will also load any macros and eqn definitions
+required for a particular device.
+It can also spool the resulting output.
+.LP
+Options without an argument can be grouped behind a single
+.BR \- .
+A filename of
+.B \-
+denotes the standard input.
+.SH OPTIONS
+.TP
+.B \-h
+Print a help message.
+.TP
+.B \-e
+Preprocess with geqn.
+.TP
+.B \-t
+Preprocess with gtbl.
+.TP
+.B \-p
+Preprocess with gpic.
+.TP
+.B \-s
+Preprocess with gsoelim.
+.TP
+.B \-R
+Preprocess with grefer.
+No mechanism is provided for passing arguments to
+.B grefer
+because most grefer options have equivalent commands
+which can be included in the file.
+See
+.BR grefer (1)
+for more details.
+.TP
+.B \-v
+Make programs run by
+.B groff
+print out their version number.
+.TP
+.B \-V
+Print the pipeline on stdout instead of executing it.
+.TP
+.B \-z
+Suppress output from
+.BR gtroff .
+Only error messages will be printed.
+.TP
+.B \-Z
+Do not postprocess the output of
+.BR gtroff .
+Normally
+.B groff
+will automatically run the appropriate postprocessor.
+.TP
+.BI \-P arg
+Pass
+.I arg
+to the postprocessor.
+Each argument should be passed with a separate
+.B \-P
+option.
+.TP
+.B \-l
+Spool the output.
+.TP
+.BI \-L arg
+Pass
+.I arg
+to the spooler.
+Each argument should be passed with a separate
+.B \-L
+option.
+.TP
+.BI \-T dev
+Prepare output for device
+.IR dev .
+The default device is
+.BR @DEVICE@ .
+.TP
+.B \-N
+Don't allow newlines with eqn delimiters.
+This is the same as the
+.B \-N
+option in
+.BR geqn .
+.TP
+.B \-a
+.TQ
+.B \-b
+.TQ
+.B \-i
+.TQ
+.B \-C
+.TQ
+.B \-E
+.TQ
+.BI \-w name
+.TQ
+.BI \-W name
+.TQ
+.BI \-m name
+.TQ
+.BI \-o list
+.TQ
+.BI \-d cs
+.TQ
+.BI \-r cn
+.TQ
+.BI \-F dir
+.TQ
+.BI \-M dir
+.TQ
+.BI \-H file
+.TQ
+.BI \-f fam
+.TQ
+.BI \-n num
+These are as described in
+.BR gtroff (@MAN1EXT@) .
+.SH ENVIRONMENT
+.TP
+.SM
+.B GROFF_TMAC_PATH
+A colon separated list of directories in which to search for
+macro files.
+.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.
+.TP
+.SM
+.B GROFF_HYPHEN
+File containing hyphenation patterns.
+.TP
+.SM
+.B PATH
+The search path for commands executed by
+.BR groff .
+.SH FILES
+.TP \w'\fB@FONTDIR@/dev\fIname\fB/eqnchar'u+3n
+.B @HYPHENFILE@
+Hyphenation patterns
+.TP
+.BI @MACRODIR@/tmac. name
+Macro file used by
+.BI \-m name\fR.
+.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 .
+.TP
+.BI @FONTDIR@/dev name /eqnchar
+.B geqn
+definitions for device
+.IR name .
+.SH "SEE ALSO"
+.BR grog (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR gtbl (@MAN1EXT@),
+.BR gpic (@MAN1EXT@),
+.BR geqn (@MAN1EXT@),
+.BR gsoelim (@MAN1EXT@) ,
+.BR grefer (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR grodvi (@MAN1EXT@),
+.BR grotty (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_ms (@MAN7EXT@),
+.BR groff_me (@MAN7EXT@)
+.br
+.I "Groff Character Names"
diff --git a/man/groff_font.man b/man/groff_font.man
new file mode 100644
index 000000000..5ae0e29df
--- /dev/null
+++ b/man/groff_font.man
@@ -0,0 +1,342 @@
+.\" -*- nroff -*-
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.TH GROFF_FONT @MAN5EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH SYNOPSIS
+groff_font \- format of groff device and font description files
+.SH DESCRIPTION
+The groff font format is roughly a superset of the ditroff
+font format.
+Unlike the ditroff font format, there is no associated binary
+format.
+The font files for device
+.I name
+are stored in a directory
+.BI dev name.
+There are two types of file: a
+device description file called
+.B DESC
+and for each font
+.I F
+a font file called
+.IR F .
+These are text files;
+there is no associated binary format.
+.SS DESC file format
+The DESC file can contain the following types of line:
+.TP
+.BI res\ n
+There are
+.I n
+machine units per inch.
+.TP
+.BI hor\ n
+The horizontal resolution is
+.I n
+machine units.
+.TP
+.BI vert\ n
+The vertical resolution is
+.I n
+machine units.
+.TP
+.BI sizescale\ n
+The scale factor for pointsizes.
+By default this has a value of 1.
+One
+.I
+scaled point
+is equal to
+one
+.RI point/ n .
+The arguments to the
+.B unitwidth
+and
+.B sizes
+commands are given in scaled points.
+.TP
+.BI unitwidth\ n
+Quantities in the font files are given in machine units
+for fonts whose point size is
+.I n
+scaled points.
+.TP
+.B tcommand
+This means that the postprocessor can handle the
+.B t
+and
+.B u
+output commands.
+.TP
+.BI sizes\ s1\ s2\|.\|.\|.\|sn\ 0
+This means that the device has fonts at
+.IR s1 ,
+.IR s2 ,\|.\|.\|.\| sn
+scaled points.
+The list of sizes must be terminated by a
+.BR 0 .
+Each
+.BI s i
+can also be a range of sizes
+.IR m \- n .
+The list can extend over more than one line.
+.TP
+.BI styles\ S1\ S2\|.\|.\|.\|Sm
+The first
+.I m
+font positions will be associated with styles
+.IR S1\|.\|.\|.\|Sm .
+.TP
+.BI fonts\ n\ F1\ F2\ F3\|.\|.\|.\|Fn
+Fonts
+.I F1\|.\|.\|.\|Fn
+will be mounted in the font positions
+.IR m +1,\|.\|.\|., m + n
+where
+.I m
+is the number of styles.
+This command may extend over more than one line.
+.TP
+.BI family\ fam
+The default font family is
+.IR fam .
+.TP
+.B charset
+This line and everything following in the file are ignored.
+It is allowed for the sake of backwards compatibility.
+.LP
+The res, unitwidth, fonts and sizes lines are compulsory.
+Other commands are ignored by
+.B gtroff
+but may be used by postprocessors to store arbitrary information
+about the device in the DESC file.
+.SS Font file format
+A font file has two sections. The first section is a sequence
+of lines each containing a sequence of blank delimited
+words; the first word in the line is a key, and subsequent
+words give a value for that key.
+.TP
+.BI name\ F
+The name of the font is
+.IR F .
+.TP
+.BI spacewidth\ n
+The normal width of a space is
+.IR n .
+.TP
+.BI slant\ n
+The characters of the font have a slant of
+.I n
+degrees. (Positive means forward.)
+.TP
+.BI ligatures\ lig1\ lig2\|.\|.\|.\|lign\ \fR[ 0 \fR]
+Characters
+.IR lig1 ,
+.IR lig2 ,\|.\|.\|., lign
+are ligatures; possible ligatures are
+.BR ff ,
+.BR fi ,
+.BR fl
+and
+.BR ffl .
+For backwards compatibiliy, the list of ligatures may be terminated
+with a
+.BR 0.
+The list of ligatures may not extend over more than one line.
+.TP
+.B special
+The font is
+.IR special ;
+this means that when a character is requested that is not present in
+the current font, it will be searched for in any special fonts that
+are mounted.
+.LP
+Other commands are ignored by
+.B gtroff
+but may be used by postprocessors to store arbitrary information
+about the font in the font file.
+.LP
+The first section can contain comments which start with the
+.B #
+character and extend to the end of a line.
+.LP
+The second section contains one or two subsections.
+It must contain a
+.I charset
+subsection
+and it may also contain a
+.I kernpairs
+subsection.
+These subsections can appear in any order.
+Each subsection starts with a word on a line by itself.
+.LP
+The word
+.B charset
+starts the charset subsection.
+The
+.B charset
+line is followed by a sequence of lines.
+Each line gives information for one character.
+A line comprises a number of fields separated
+by blanks or tabs. The format is
+.IP
+.I
+name metrics type code comment
+.LP
+.I name
+identifies the character:
+if
+.I name
+is a single character
+.I c
+then it corresponds to the groff input character
+.IR c ;
+if it is of the form
+.BI \e c
+where c is a single character, then it
+corresponds to the groff input character
+.BI \e c\fR;
+otherwise it corresponds to the groff input character
+.BI \e[ name ]
+(if it is exactly two characters
+.I xx
+it can be entered as
+.BI \e( xx\fR.)
+Groff supports eight bit characters; however some utilities
+has difficulties with eight bit characters.
+For this reason, there is a convention that the name
+.BI char n
+is equivalent to the single character whose code is
+.I n .
+For example,
+.B char163
+would be equivalent to the character with code 163
+which is the pounds sterling sign in ISO Latin-1.
+The name
+.B \-\-\-
+is special and indicates that the character is unnamed;
+such characters can only be used by means of the
+.B \eN
+escape sequence in
+.BR gtroff .
+.LP
+The
+.I type
+field gives the character type:
+.TP
+1
+means the character has an descender, for example, p;
+.TP
+2
+means the character has an ascender, for example, b;
+.TP
+3
+means the character has both an ascender and a descender, for example,
+(.
+.LP
+The
+.I code
+field gives the code which the postprocessor uses to print the character.
+The character can also be input to groff using this code by means of the
+.B \eN
+escape sequence.
+The code can be any integer.
+If it starts with a
+.B 0
+it will be interpreted as octal;
+if it starts with
+.B 0x
+or
+.B 0X
+it will be intepreted as hexdecimal.
+.LP
+Anything on the line after the code field will be ignored.
+.LP
+The
+.I metrics
+field has the form:
+.IP
+.IR width [\fB, height [\fB, depth [\fB, italic_correction [\fB, \
+left_italic_correction [\fB, subscript_correction ]]]]]
+.LP
+There must not be any spaces between these subfields.
+Missing subfields are assumed to be 0.
+The subfields are all decimal integers.
+Since there is no associated binary format, these
+values are not required to fit into a variable of type
+.B char
+as they are in ditroff.
+The
+.I width
+subfields gives the width of the character.
+The
+.I height
+subfield gives the height of the character (upwards is positive);
+if a character does not extend above the baseline, it should be
+given a zero height, rather than a negative height.
+The
+.I depth
+subfield gives the depth of the character, that is, the distance
+below the lowest point below the baseline to which the
+character extends (downwards is positive);
+if a character does not extend below above the baseline, it should be
+given a zero depth, rather than a negative depth.
+The
+.I italic_correction
+subfield gives the amount of space that should be added after the
+character when it is immediately to be followed by a character
+from a roman font.
+The
+.I left_italic_correction
+subfield gives the amount of space that should be added before the
+character when it is immediately to be preceded by a character
+from a roman font.
+The
+.I subscript_correction
+gives the amount of space that should be added after a character
+before adding a subscript.
+This should be less than the italic correction.
+.LP
+A line in the charset section can also have the format
+.IP
+.I
+name \fB"
+.LP
+This indicates that
+.I name
+is just another name for the character mentioned in the
+preceding line.
+.LP
+The word
+.B kernpairs
+starts the kernpairs section.
+This contains a sequence of lines of the form:
+.IP
+.I
+c1 c2 n
+.LP
+This means that when character
+.I c1
+appears next to character
+.I c2
+the space between them should be increased by
+.IR n .
+Most entries in kernpairs section will have a negative value for
+.IR n .
+.SH FILES
+.TP \w'@FONTDIR@/devname/DESC'u+3n
+.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 .
+.SH "SEE ALSO"
+.BR groff_out (@MAN5EXT@),
+.BR gtroff (@MAN1EXT@).
diff --git a/man/groff_me.man b/man/groff_me.man
new file mode 100644
index 000000000..6f1d5a622
--- /dev/null
+++ b/man/groff_me.man
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1980 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)me.7 6.4 (Berkeley) 4/13/90
+.\"
+.\" Modified for groff by jjc@jclark.uucp.
+.hc %
+.TH GROFF_ME @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@"
+.UC 3
+.SH NAME
+groff_me \- groff macros for formatting papers
+.SH SYNOPSIS
+.B "groff \-me"
+[ options ]
+file ...
+.br
+.B "troff \-me"
+[ options ]
+file ...
+.SH DESCRIPTION
+This package of
+.I groff
+macro definitions provides a canned formatting
+facility for tech%nical papers in various formats.
+.PP
+The macro requests are defined below.
+Many
+.I groff
+requests are unsafe in conjunction with
+this package, however, these requests may be used with
+impunity after the first .pp:
+.nf
+.IP
+.ta \w'.sz +n 'u
+\&.bp begin new page
+\&.br break output line here
+\&.sp n insert n spacing lines
+\&.ls n (line spacing) n=1 single, n=2 double space
+\&.na no alignment of right margin
+\&.ce n center next n lines
+\&.ul n underline next n lines
+.fi
+.PP
+Output of the
+.I gpic,
+.I geqn,
+.I refer,
+and
+.I gtbl
+preprocessors
+is acceptable as input.
+.SH FILES
+@MACRODIR@/tmac.e
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@)
+.br
+\-me Reference Manual, Eric P. Allman
+.br
+Writing Papers with Groff Using \-me
+.tr &.
+.SH REQUESTS
+This list is incomplete;
+see
+.I "The \-me Reference Manual"
+for interesting details.
+.PP
+.ta \w'.eh \'x\'y\'z\' 'u +\w'Initial 'u +\w'Cause 'u
+.br
+.di x
+ \ka
+.br
+.di
+.in \nau
+.ti 0
+Request Initial Cause Explanation
+.ti 0
+ Value Break
+.br
+.in \nau
+.ti 0
+\&.(c - yes Begin centered block
+.ti 0
+\&.(d - no Begin delayed text
+.ti 0
+\&.(f - no Begin footnote
+.ti 0
+\&.(l - yes Begin list
+.ti 0
+\&.(q - yes Begin major quote
+.ti 0
+\&.(x \fIx\fR - no Begin indexed item in index
+.I x
+.ti 0
+\&.(z - no Begin floating keep
+.ti 0
+\&.)c - yes End centered block
+.ti 0
+\&.)d - yes End delayed text
+.ti 0
+\&.)f - yes End footnote
+.ti 0
+\&.)l - yes End list
+.ti 0
+\&.)q - yes End major quote
+.ti 0
+\&.)x - yes End index item
+.ti 0
+\&.)z - yes End floating keep
+.ti 0
+\&.++ \fIm H\fR - no Define paper section.
+.I m
+defines the part of the paper, and can be
+.B C
+(chapter),
+.B A
+(appendix),
+.B P
+(preliminary, e.g., abstract, table of contents, etc.),
+.B B
+(bibliography),
+.B RC
+(chapters renumbered from page one each chapter),
+or
+.B RA
+(appendix renumbered from page one).
+.ti 0
+\&.+c \fIT\fR - yes Begin chapter (or appendix, etc., as
+set by .++).
+.I T
+is the chapter title.
+.ti 0
+\&.1c 1 yes One column format on a new page.
+.ti 0
+\&.2c 1 yes Two column format.
+.ti 0
+\&.EN - yes Space after equation
+produced by
+.I eqn
+or
+.IR neqn .
+.ti 0
+\&.EQ \fIx y\fR - yes Precede equation; break out and
+add space.
+Equation number is
+.IR y .
+The optional argument \fIx\fR
+may be
+.I I
+to indent equation (default),
+.I L
+to left-adjust the equation, or
+.I C
+to center the equation.
+.ti 0
+\&.GE - yes End \fIgremlin\fP picture.
+.ti 0
+\&.GS - yes Begin \fIgremlin\fP picture.
+.ti 0
+\&.PE - yes End \fIpic\fP picture.
+.ti 0
+\&.PS - yes Begin \fIpic\fP picture.
+.ti 0
+\&.TE - yes End table.
+.ti 0
+\&.TH - yes End heading section of table.
+.ti 0
+\&.TS \fIx\fR - yes Begin table; if \fIx\fR is
+.I H
+table has repeated heading.
+.ti 0
+\&.b \fIx\fR no no Print
+.I x
+in boldface; if no argument switch to boldface.
+.ti 0
+\&.ba \fI+n\fR 0 yes Augments the base indent by
+.I n.
+This indent is used to set the indent on regular text
+(like paragraphs).
+.ti 0
+\&.bc no yes Begin new column
+.ti 0
+\&.bi \fIx\fR no no Print
+.I x
+in bold italics (nofill only)
+.ti 0
+\&.bu - yes Begin bulleted paragraph
+.ti 0
+\&.bx \fIx\fR no no Print \fIx\fR in a box (nofill only).
+.ti 0
+\&.ef \fI\'x\'y\'z\'\fR \'\'\'\' no Set even footer to x y z
+.ti 0
+\&.eh \fI\'x\'y\'z\'\fR \'\'\'\' no Set even header to x y z
+.ti 0
+\&.fo \fI\'x\'y\'z\'\fR \'\'\'\' no Set footer to x y z
+.ti 0
+\&.hx - no Suppress headers and footers on next page.
+.ti 0
+\&.he \fI\'x\'y\'z\'\fR \'\'\'\' no Set header to x y z
+.ti 0
+\&.hl - yes Draw a horizontal line
+.ti 0
+\&.i \fIx\fR no no Italicize
+.I x;
+if
+.I x
+missing, italic text follows.
+.ti 0
+\&.ip \fIx y\fR no yes Start indented paragraph,
+with hanging tag
+.IR x .
+Indentation is
+.I y
+ens (default 5).
+.ti 0
+\&.lp yes yes Start left-blocked paragraph.
+.ti 0
+\&.np 1 yes Start numbered paragraph.
+.ti 0
+\&.of \fI\'x\'y\'z\'\fR \'\'\'\' no Set odd footer to x y z
+.ti 0
+\&.oh \fI\'x\'y\'z\'\fR \'\'\'\' no Set odd header to x y z
+.ti 0
+\&.pd - yes Print delayed text.
+.ti 0
+\&.pp no yes Begin paragraph.
+First line indented.
+.ti 0
+\&.r yes no Roman text follows.
+.ti 0
+\&.re - no Reset tabs to default values.
+.ti 0
+\&.sh \fIn x\fR - yes Section head follows,
+font automatically bold.
+.I n
+is level of section,
+.I x
+is title of section.
+.ti 0
+\&.sk no no Leave the next page blank.
+Only one page is remembered ahead.
+.ti 0
+\&.sm \fIx\fR - no Set
+.I x
+in a smaller pointsize.
+.ti 0
+\&.sz \fI+n\fR 10p no Augment the point size by
+.I n
+points.
+.ti 0
+\&.tp no yes Begin title page.
+.ti 0
+\&.u \fIx\fR - no Underline argument (even in \fItroff\fR).
+(Nofill only).
+.ti 0
+\&.uh - yes Like .sh but unnumbered.
+.ti 0
+\&.xp \fIx\fR - no Print index
+.I x.
diff --git a/man/groff_ms.man b/man/groff_ms.man
new file mode 100644
index 000000000..4a420ea9f
--- /dev/null
+++ b/man/groff_ms.man
@@ -0,0 +1,209 @@
+.\" -*- nroff -*-
+.TH GROFF_MS @MAN7EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+groff_ms \- groff ms macros
+.SH SYNOPSIS
+.B groff
+.B \-m@TMAC_S@
+[
+.IR options .\|.\|.
+]
+[
+.IR files .\|.\|.
+]
+.SH DESCRIPTION
+The groff ms macros are intended to be compatible with the 4.3
+.SM BSD
+Unix ms macros subject to the following limitations:
+.IP \(bu
+the internals of groff ms are not similar to the internals of Unix ms
+and so documents that depend upon implementation details of Unix ms
+may well not work with groff ms;
+.IP \(bu
+there is no support for typewriter-like devices;
+.IP \(bu
+Berkeley localisms, in particular the
+.B TM
+and
+.B CT
+macros, are not implemented;
+.IP \(bu
+groff ms
+does not provide cut marks;
+.IP \(bu
+multiple line spacing is not allowed
+(use a larger vertical spacing instead);
+.IP \(bu
+groff ms does not work in compatibility mode (eg with the
+.B \-C
+option);
+.IP \(bu
+the error-handling policy of groff ms
+is to detect and report errors,
+rather than silently to ignore them.
+.LP
+The groff ms macros make use of many features of
+.B gtroff
+and therefore cannot be used with any other troff.
+.LP
+Bell Labs localisms are not implemented in either the
+.SM BSD
+ms macros or in the groff ms macros.
+.LP
+Some Unix ms documentation says that the
+.B CW
+and
+.B GW
+number registers can be used to control the column width and
+gutter width respectively.
+This is not the case.
+These number registers are not used in groff ms.
+.LP
+Macros that cause a reset set the indent.
+Macros that change the indent do not increment or decrement
+the indent, but rather set it absolutely.
+This can cause problems for documents that define
+additional macros of their own.
+The solution is to use not the
+.B in
+request but instead the
+.B RS
+and
+.B RE
+macros.
+.LP
+The number register
+.B GS
+is set to 1 by the groff ms macros,
+but is not used by the Unix ms macros.
+It is intended that documents that need to determine whether
+they are being formatted with Unix ms or groff ms make use of this
+number register.
+.LP
+Footnotes are implemented so that they can safely be used within
+keeps and displays.
+Automatically numbered footnotes within floating keeps are
+not recommended.
+It is safe to have another
+.B \e**
+between a
+.B \e**
+and the corresponding
+.BR .FS ;
+it is required only that each
+.B .FS
+occur after the corresponding
+.B \e**
+and that the occurrences of
+.B .FS
+are in the same order as the corresponding occurrences of
+.BR \e** .
+.LP
+The strings
+.B \e*{
+and
+.B \e*}
+can be used to begin and end a superscript.
+.LP
+Some Unix V10 ms features are implemented.
+The
+.BR B ,
+.BR I
+and
+.B BI
+macros can have an optional third argument which will be printed
+in the current font before the first argument.
+There is a macro
+.B CW
+like
+.B B
+that changes to a constant-width font.
+.LP
+The following strings can be redefined to adapt the groff ms macros
+to languages other than English:
+.LP
+.nf
+.ta \w'REFERENCES'u+2n
+String Default Value
+.sp .3v
+REFERENCES References
+ABSTRACT ABSTRACT
+TOC Table of Contents
+MONTH1 January
+MONTH2 February
+MONTH3 March
+MONTH4 April
+MONTH5 May
+MONTH6 June
+MONTH7 July
+MONTH8 August
+MONTH9 September
+MONTH10 October
+MONTH11 November
+MONTH12 December
+.fi
+.LP
+The font family is reset from the string
+.BR FAM ;
+at initialization if this string is undefined it is set to the current
+font family.
+The point size, vertical spacing, and inter-paragraph spacing for footnotes
+are taken from the number registers
+.BR FPS ,
+.BR FVS ,
+and
+.BR FPD ;
+at initialization these are set to
+.BR \en(PS-2 ,
+.BR \en[FPS]+2 ,
+and
+.B \en(PD/2
+respectively; however, if any of these registers has been defined
+before initialization, it will not be set.
+.LP
+Right-aligned displays are available with
+.B ".DS R"
+and
+.BR .RD .
+.LP
+The following conventions are used for names of macros, strings and
+number registers.
+External names available to documents that use the groff ms
+macros contain only uppercase letters and digits.
+Internally the macros are divided into modules.
+Names used only within one module are of the form
+.IB module * name\fR.
+Names used outside the module in which they are defined are of the form
+.IB module @ name\fR.
+Names associated with a particular environment are of the form
+.IB environment : name;
+these are used only within the
+.B par
+module,
+and
+.I name
+does not have a module prefix.
+Constructed names used to implement arrays are of the form
+.IB array ! index\fR.
+Thus the groff ms macros reserve the following names:
+.IP \(bu
+names containing
+.BR * ;
+.IP \(bu
+names containing
+.BR @ ;
+.IP \(bu
+names containing
+.BR : ;
+.IP \(bu
+names containing only uppercase letters and digits.
+.SH FILES
+.B @MACRODIR@/tmac.@TMAC_S@
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR gtbl (@MAN1EXT@),
+.BR gpic (@MAN1EXT@),
+.BR geqn (@MAN1EXT@)
+.br
+.BR ms (@MAN7EXT@)
diff --git a/man/groff_out.man b/man/groff_out.man
new file mode 100644
index 000000000..e07be5669
--- /dev/null
+++ b/man/groff_out.man
@@ -0,0 +1,215 @@
+'\" e
+.\" -*- nroff -*-
+.\" This man page must be preprocessed with eqn.
+.ie \n(.g .ds ic \/
+.el .ds ic \^
+.TH GROFF_OUT @MAN5EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH SYNOPSIS
+groff_out \- format of groff output
+.SH DESCRIPTION
+The output format used by groff is very similar to that used
+by device-independent troff. Only the differences are documented
+here.
+.LP
+The argument to the
+.B s
+command is in scaled points (units of
+.IR points/ n ,
+where
+.I n
+is the argument to the
+.B sizescale
+command in the DESC file.)
+The argument to the
+.B x\ Height
+command is also in scaled points.
+.LP
+The first three output commands are guaranteed to be:
+.IP
+.BI x\ T\ device
+.br
+.BI x\ res\ n\ h\ v
+.br
+.B x init
+.LP
+If the
+.B tcommand
+line is present in the DESC file, gtroff will use the following
+two commands
+.TP
+.BI t xxx
+.I xxx
+is any sequence of characters terminated by a space or a newline;
+the first character should be printed at the current position,
+the the current horizontal position should be increased by
+the width of the first character, and so on for each character.
+The width of the character is that given in the font file,
+appropriately scaled for the current point size, and rounded
+so that it is a multiple of the horizontal resolution.
+Special characters cannot be printed using this command.
+.TP
+.BI u n\ xxx
+This is same as the
+.B t
+command except that after printing each character, the current horizontal
+position is increased by the sum of the width of that character
+and
+.IR n .
+.LP
+Note that single characters can have the eighth bit set, as can the
+names of fonts and special characters.
+.LP
+The names of characters and fonts an be of arbitrary length; drivers
+should not assume that they will be only two characters long.
+.LP
+When a character is to be printed, that character will always be
+in the current font.
+Unlike device-independent troff, it is not necessary
+for drivers to search special fonts to find a character.
+.LP
+The
+.B D
+drawing command has been extended.
+These extensions will only be used by gpic
+.B \-x
+option is given.
+.TP
+\fBDf \fIn\fR\*(ic\en
+Set the shade of gray to be used for filling solid objects to
+.IR n ;
+.I n
+must be an integer between 0 and 1000, where 0 corresponds solid white
+and 1000 to solid black, and values in between correspond to
+intermediate shades of gray.
+This applies only to solid circles, solid ellipses and solid
+polygons.
+By default, a level of 1000 will be used.
+Whatever color a solid object has, it should completely obscure
+everything beneath it.
+A value greater than 1000 or less than 0 can also be used:
+this means fill with the shade of gray that is currently being used
+for lines and text.
+Normally this will be black, but some drivers may provide
+a way of changing this.
+.TP
+\fBDC \fId\fR\*(ic\en
+Draw a solid circle with a diameter of
+.I d
+with the leftmost point at the current position.
+.TP
+\fBDE \fIdx dy\fR\*(ic\en
+Draw a solid ellipse with a horizontal diameter of
+.I dx
+and a vertical diameter of
+.I dy
+with the leftmost point at the current position.
+.EQ
+delim $$
+.EN
+.TP
+\fBDp\fR $dx sub 1$ $dy sub 1$ $dx sub 2$ $dy sub 2$ $...$ $dx sub n$ $dy sub n$\en
+Draw a polygon with,
+for $i = 1 ,..., n+1$, the
+.IR i -th
+vertex at the current position
+$+ sum from j=1 to i-1 ( dx sub j , dy sub j )$.
+At the moment,
+gpic only uses this command to generate triangles and rectangles.
+.TP
+\fBDP\fR $dx sub 1$ $dy sub 1$ $dx sub 2$ $dy sub 2$ $...$ $dx sub n$ $dy sub n$\en
+Like
+.B Dp
+but draw a solid rather than outlined polygon.
+.TP
+\fBDt \fIn\fR\*(ic\en
+Set the current line thickness to
+.I n
+machine units.
+Traditionally troff drivers use a line thickness proportional to the current
+point size; drivers should continue to do this if no
+.B Dt
+command has been given, or if a
+.B Dt
+command has been given with a negative value of
+.IR n .
+A zero value of
+.I n
+selects the smallest available line thickness.
+.LP
+A difficulty arises in how the current position should be changed after
+the execution of these commands.
+This is not of great importance since the code generated by gpic
+does not depend on this.
+Given a drawing command of the form
+.IP
+\fB\eD\(fm\fIc\fR $x sub 1$ $y sub 1$ $x sub 2$ $y sub 2$ $...$ $x sub n$ $y sub n$\(fm
+.LP
+where
+.I c
+is not one of
+.BR c ,
+.BR e ,
+.BR l ,
+.B a
+or
+.BR ~ ,
+troff will treat each of the $x sub i$ as a horizontal quantity,
+and each of the $y sub i$ as a vertical quantity and will assume that
+the width of the drawn object is $sum from i=1 to n x sub i$,
+and that the height is $sum from i=1 to n y sub i$.
+(The assumption about the height can be seen by examining the
+.B st
+and
+.B sb
+registers after using such a
+.B D
+command in a \ew escape sequence.)
+This rule also holds for all the original drawing commands
+with the exception of
+.BR De .
+For the sake of compatibility gtroff also follows this rule,
+even though it produces an ugly result in the case of the
+.BR Df ,
+.BR Dt ,
+and, to a lesser extent,
+.B DE
+commands.
+Thus after executing a
+.B D
+command of the form
+.IP
+\fBD\fIc\fR $x sub 1$ $y sub 1$ $x sub 2$ $y sub 2$ $...$ $x sub n$ $y sub n$\en
+.LP
+the current position should be increased by
+$( sum from i=1 to n x sub i , sum from i=1 to n y sub i )$.
+.LP
+There is a continuation convention which permits the argument to the
+.B x\ X
+command to contain newlines:
+when outputting the argument to the
+.B x\ X
+command,
+.B gtroff
+will follow each newline in the argument with a
+.B +
+character
+(as usual, it will terminate the entire argument with a newline);
+thus if the line after the line containing the
+.B x\ X
+command starts with
+.BR + ,
+then the newline ending the line containing the
+.B x\ X
+command should be treated as part of the argument to the
+.B x\ X
+command,
+the
+.B +
+should be ignored,
+and the part of the line following the
+.B +
+should be treated like the part of the line following the
+.B x\ X
+command.
+.SH "SEE ALSO"
+.BR groff_font (@MAN5EXT@)
diff --git a/man/grog.man b/man/grog.man
new file mode 100644
index 000000000..d1f91550e
--- /dev/null
+++ b/man/grog.man
@@ -0,0 +1,54 @@
+.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 \-p ,
+.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.
+For example,
+.IP
+.B `grog \-Tdvi paper.ms`
+.LP
+will guess the approriate 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 gtroff (@MAN1EXT@),
+.BR gtbl (@MAN1EXT@),
+.BR gpic (@MAN1EXT@),
+.BR geqn (@MAN1EXT@),
+.BR gsoelim (@MAN1EXT@)
diff --git a/man/grops.man b/man/grops.man
new file mode 100644
index 000000000..e7b066356
--- /dev/null
+++ b/man/grops.man
@@ -0,0 +1,704 @@
+.\" -*- nroff -*-
+.TH GROPS @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grops \- PostScript driver for groff
+.SH SYNOPSIS
+.B grops
+[
+.B \-lv
+] [
+.BI \-b n
+] [
+.BI \-c n
+] [
+.BI \-w n
+] [
+.BI \-F dir
+] [
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B grops
+translates the output of
+.B gtroff
+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.
+The default value is @BROKEN_SPOOLER_FLAGS@.
+.TP
+.BI \-c n
+Print
+.I n
+copies of each page.
+.TP
+.BI \-l
+Print the document in landscape format.
+.TP
+.BI \-F dir
+Search the directory
+.IB dir /dev name
+for font and device description files;
+.I name
+is the name of the device, usually
+.BR ps .
+.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 avilable 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.
+Any definitions will also 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 apppropriately 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 gtroff ;
+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 sy
+request to run the
+.B psbb
+command.
+.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
+.BI .PSPIC\ file\ \fR[ width\ \fR[ height \fR]]
+.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.
+.RE
+.LP
+The input to
+.B grops
+must be in the format output by
+.BR gtroff (@MAN1EXT@).
+This is described in
+.BR groff_out (@MAN1EXT@).
+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 gtroff
+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 FILES
+.TP \w'\fB@FONTDIR@/devps/download'u+2n
+.B @FONTDIR@/devps/DESC
+Device desciption 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@/tmac.ps
+Macros for use with
+.BR grops .
+.TP
+.BI /tmp/grops XXXXXX
+Temporary file.
+.SH "SEE ALSO"
+.BR afmtodit (@MAN1EXT@),
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR psbb (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@)
+.br
+.I "Groff Character Names"
diff --git a/man/grotty.man b/man/grotty.man
new file mode 100644
index 000000000..a114e7107
--- /dev/null
+++ b/man/grotty.man
@@ -0,0 +1,179 @@
+.\" -*- nroff -*-
+.TH GROTTY @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+grotty \- groff driver for typewriter-like devices
+.SH SYNOPSIS
+.B grotty
+[
+.B \-hfbuoBUv
+] [
+.BI \-F dir
+] [
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B grotty
+translates the output of
+.B gtroff
+into a form suitable for typewriter-like devices.
+Normally
+.B grotty
+should invoked by using the groff command
+with a
+.B \-Tascii
+or
+.B \-Tlatin1
+option.
+If no files are given,
+.B grotty
+will read the standard input.
+A filename of
+.B \-
+will also cause
+.B grotty
+to read the standard input.
+Output is written to the standard output.
+.LP
+Normally
+.B grotty
+prints a bold character
+.I c
+using the sequence
+.RI ` c
+.SM BACKSPACE
+.IR c '
+and a italic character
+.I c
+by the sequence
+.RB ` _
+.SM BACKSPACE
+.IR c '.
+These sequences can be displayed on a terminal
+by piping through
+.BR ul (1).
+Pagers such as
+.BR more (1)
+or
+.BR less (1)
+are also able to display these sequences.
+Use either
+.B \-B
+or
+.B \-U
+when piping into
+.BR less (1);
+use
+.B \-b
+when piping into
+.BR more (1).
+There is no need to filter the output through
+.BR col (1)
+since
+.B grotty
+never outputs reverse line feeds.
+.LP
+The font description file may contain a command
+.IP
+.BI internalname\ n
+.LP
+where
+.I n
+is a decimal integer.
+If the 01 bit in
+.I n
+is set,
+then the font will be treated as an italic font;
+if the 02 bit is set,
+then it will be treated as a bold font.
+The code field in the font description field gives the
+code which will be used to output the character.
+This code can also be used in the
+.B \eN
+escape sequence in
+.BR gtroff .
+.SH OPTIONS
+.TP
+.BI \-F dir
+Search the directory
+.IB dir /dev name
+for font and device description files;
+.I name
+is the name of the device, usually
+.B ascii
+or
+.BR latin1 .
+.TP
+.B \-h
+Use horizontal tabs in the output.
+Tabs are assumed to be set every 8 columns.
+.TP
+.B \-f
+Use form feeds in the output.
+A form feed will be output at the end of each page that has no output
+on its last line.
+.TP
+.B \-b
+Suppress the use of overstriking for bold characters.
+.TP
+.B \-u
+Suppress the use of underlining for italic characters.
+.TP
+.B \-B
+Use only overstriking for bold, italic characters.
+.TP
+.B \-U
+Use only underlining for bold, italic characters.
+.TP
+.B \-o
+Suppress overstriking (other than for bold or underlined characters).
+.TP
+.B \-v
+Print the version number.
+.SH FILES
+.TP
+.B @FONTDIR@/devascii/DESC
+Device desciption file for
+.B ascii
+device.
+.TP
+.B @FONTDIR@/devascii/ F
+Font description file for font
+.I F
+of
+.B ascii device.
+.TP
+.B @FONTDIR@/devlatin1/DESC
+Device desciption file for
+.B latin1
+device.
+.TP
+.B @FONTDIR@/devlatin1/ F
+Font description file for font
+.I F
+of
+.B latin1 device.
+.TP
+.B @MACRODIR@/tmac.tty
+Macros for use with
+.BR grotty .
+.SH BUGS
+.LP
+.B grotty
+is intended only for simple documents.
+.LP
+There is no support for fractional horizontal or vertical motions.
+.LP
+There is no pic support.
+.LP
+Characters above the first line (ie with a vertical position of 0)
+cannot be printed.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR groff_out (@MAN5EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR ul (1),
+.BR more (1),
+.BR less (1)
+.br
+.I "Groff Character Names"
diff --git a/man/gsoelim.man b/man/gsoelim.man
new file mode 100644
index 000000000..1da6c5a0d
--- /dev/null
+++ b/man/gsoelim.man
@@ -0,0 +1,42 @@
+.\" -*- nroff -*-
+.TH GSOELIM @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+gsoelim \- interpret .so requests in groff input
+.SH SYNOPSIS
+.B gsoelim
+[
+.B \-Cv
+]
+[
+.IR files \|.\|.\|.\|
+]
+.SH DESCRIPTION
+.B gsoelim
+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 gsoelim
+should be invoked with the
+.B \-s
+option of
+.BR groff .
+.SH OPTIONS
+.TP
+.B \-C
+Recognize
+.B .so
+even when followed by a character other than space or newline.
+.TP
+.B \-v
+Print the version number.
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@)
diff --git a/man/gtbl.man b/man/gtbl.man
new file mode 100644
index 000000000..9fbeea813
--- /dev/null
+++ b/man/gtbl.man
@@ -0,0 +1,119 @@
+.\" -*- nroff -*-
+.TH GTBL @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+gtbl \- table formatter for groff
+.SH SYNOPSIS
+.B gtbl
+[
+.B \-Cv
+]
+[
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B gtbl
+is a preprocessor for
+.B groff
+for formatting tables.
+Normally, it should be invoked using the
+.B \-t
+option of
+.B groff.
+It is highly compatible with
+.BR tbl .
+The output generated by
+.B gtbl
+cannot be processed with
+.BR ditroff ;
+it must be processed with
+.BR gtroff .
+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
+.B gtbl
+and
+.B tbl
+are described here.
+.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 gtbl
+uses register, string, macro and diversion names beginning with
+.BR 3 .
+When using
+.B gtbl
+you should avoid using any names beginning with a
+.BR 3 .
+.SH BUGS
+You should use
+.BR .TS\ H / .TH
+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 .
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@)
diff --git a/man/gtroff.man b/man/gtroff.man
new file mode 100644
index 000000000..0336d6f20
--- /dev/null
+++ b/man/gtroff.man
@@ -0,0 +1,1832 @@
+.\" -*- nroff -*-
+.\" 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
+..
+.TH GTROFF 1 "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+gtroff \- format documents
+.SH SYNOPIS
+.ad l
+.nr i \n(.i
+.in +\w'\fBgtroff 'u
+.ti \niu
+.B gtroff
+.de OP
+.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]"
+.el .RB "[\ " "\\$1" "\ ]"
+..
+.OP \-abivzCE
+.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
+.OP \-H file
+.RI "[\ " files\|.\|.\|. "\ ]"
+.br
+.ad b
+.SH DESCRIPTION
+gtroff is the central part of the groff document formatting system.
+It is highly compatible with device-independent 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 correct: gtroff'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
+.BI tmac. name\fR.
+Normally this will be searched for in @MACRODIR@.
+.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
+mans print every page up to
+.IR n ,
+.IB n \-
+means print every page from
+.IR n .
+.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 gtroff numeric expression.
+.TP
+.BI \-T name
+Prepare output for device
+.IR name ,
+rather than the default
+.BR @DEVICE@ .
+.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 and font files before the normal
+.BR @FONTDIR@ .
+.TP
+.BI \-H file
+Read hyphenation patterns from
+.IR file ,
+rather than the default
+.BR @HYPHENFILE@ .
+.TP
+.BI \-M dir
+Search directory
+.I dir
+for macro files before the normal
+.BR @MACRODIR@ .
+.SH USAGE
+Only the features not in device-independent 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 gtroff
+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 gtroff
+disallows this.
+Similarily 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 gtroff
+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 \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 \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 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 a 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.
+.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 only exists in order to make it possible
+to make certain gross troff hacks work with gtroff.
+It `unformats' the diversion
+.I xx
+in such a way that
+.SM ASCII
+characters that were formatted and diverted into
+.I xx
+will be treated like ordinary input characters when
+.I xx
+is reread.
+For example, this
+.RS
+.IP
+.ne 7v+\n(.Vu
+.ft B
+.nf
+.ss 24
+\&.tr @.
+\&.di x
+\&@nr\e n\e 1
+\&.br
+\&.di
+\&.tr @@
+\&.asciify x
+\&.x
+.ss 12
+.fi
+.RE
+.IP
+will set register
+.B n
+to 1.
+.TP
+.B .backtrace
+Print a backtrace of the input stack on stderr.
+.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);
+.TP
+4
+lines can be broken after the character
+(initially characters
+.B \-\e(hy\e(em
+have this property);
+.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 """')]*\e(dg"
+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.
+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 .
+.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 .fam\ xx
+Set the current font family to
+.IR xx .
+The current font family is part of the current environment.
+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 before 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.
+Hyphenation is controlled by a file of hyphenation patterns
+(normally @HYPHENFILE@);
+this file 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.
+.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 .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 .mso\ file
+The same as the
+.B so
+request except that
+.I file
+is searched for in the same way that
+.BI tmac. name
+is searched for when the
+.BI \-m name
+option is specified.
+.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
+.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
+.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 justifed 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 .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 .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 .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 gtroff input characters.
+.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 .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 """"
+will be stripped.
+.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 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 gtroff 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.
+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 troff will be exactly
+that exhibited by gtroff if a second argument is never given to the
+.B ss
+request.
+In gtroff, as in 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 gtroff.
+.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[.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 .
+.LP
+The following 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
+The following read/write number registers are available:
+.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.
+.SS Miscellaneous
+.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 gtroff
+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.
+.SS Warnings
+The warnings that can be given by
+.B gtroff
+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
+Use of a tab character where a number was expected.
+.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.
+.B mac
+is given.
+This warning will never occur in compatibility mode.
+.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.
+Troff will interpret
+.IP
+.B
+\&.dsabcd
+.LP
+as defining a string
+.B ab
+with contents
+.BR cd .
+Normally, gtroff will interpret this as a call of a macro named
+.BR dsabcd .
+Also troff will interpret
+.B \e*[
+or
+.B \en[
+as references to a string or number register called
+.BR [ .
+In gtroff, however, this will normally be interpreted as the start
+of a long name.
+In
+.I compatibility mode
+gtroff 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
+.B gtroff
+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; traditional 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
+.B troff
+the
+.B ps
+request ignores scale indicators and so
+.IP
+.B .ps\ 10u
+.LP
+will set the pointsize to 10 points, whereas in
+.B gtroff
+it will set the pointsize to 10 scaled points.
+.LP
+In gtroff 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 gtroff;
+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.
+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 gtroff and 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.
+.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.
+Gtroff will search in directories given in the
+.B \-F
+option before these, and in standard directories
+.RB ( @FONTPATH@ )
+after these.
+.TP
+.SM
+.B GROFF_HYPHEN
+File containing hyphenation patterns.
+.SH FILES
+.TP \w'@FONTDIR@/devname/DESC'u+3n
+.B @HYPHENFILE@
+Hyphenation patterns
+.TP
+.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 .
+.SH "SEE ALSO"
+.BR groff (@MAN1EXT@)
+.BR gtbl (@MAN1EXT@),
+.BR gpic (@MAN1EXT@),
+.BR geqn (@MAN1EXT@),
+.BR grops (@MAN1EXT@),
+.BR grodvi (@MAN1EXT@),
+.BR grotty (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.BR groff_out (@MAN5EXT@)
+.br
+.I "Groff Character Names"
diff --git a/man/lkbib.man b/man/lkbib.man
new file mode 100644
index 000000000..e93d9908d
--- /dev/null
+++ b/man/lkbib.man
@@ -0,0 +1,84 @@
+.\" -*- nroff -*-
+.ds g \" empty
+.ds G \" empty
+.TH \*GLKBIB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+\*glkbib \- search bibliographic databases
+.SH SYNOPSIS
+.B \*glkbib
+[
+.B \-v
+]
+[
+.BI \-i fields
+]
+[
+.BI \-p filename
+]
+[
+.BI \-t n
+]
+.IR key \|.\|.\|.
+.SH DESCRIPTION
+.B \*glkbib
+searches bibliographic databases for references that contain the keys
+.IR key \|.\|.\|.
+and prints any references found on the standard output.
+.B \*glkbib
+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 \*gindxbib (@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 \*grefer (@MAN1EXT@),
+.BR \*glookbib (@MAN1EXT@),
+.BR \*gindxbib (@MAN1EXT@)
diff --git a/man/mdate.sh b/man/mdate.sh
new file mode 100755
index 000000000..5ccc3f67a
--- /dev/null
+++ b/man/mdate.sh
@@ -0,0 +1,34 @@
+#! /bin/sh
+
+# Print the modification date of $1 `nicely'.
+
+(date; ls -l $1) | awk '
+BEGIN {
+ full["Jan"] = "January"; number["Jan"] = 1;
+ full["Feb"] = "February"; number["Feb"] = 2;
+ full["Mar"] = "March"; number["Mar"] = 3;
+ full["Apr"] = "April"; number["Apr"] = 4;
+ full["May"] = "May"; number["May"] = 5;
+ full["Jun"] = "June"; number["Jun"] = 6;
+ full["Jul"] = "July"; number["Jul"] = 7;
+ full["Aug"] = "August"; number["Aug"] = 8;
+ full["Sep"] = "September"; number["Sep"] = 9;
+ full["Oct"] = "October"; number["Oct"] = 10;
+ full["Nov"] = "November"; number["Nov"] = 11;
+ full["Dec"] = "December"; number["Dec"] = 12;
+}
+
+NR == 1 {
+ month = $2;
+ year = $6;
+}
+
+NR == 2 {
+ if ($8 ~ /:/) {
+ if (number[$6] > number[month])
+ year--;
+ }
+ else
+ year = $8;
+ print $7, full[$6], year
+}'
diff --git a/man/pfbtops.man b/man/pfbtops.man
new file mode 100644
index 000000000..6400f5242
--- /dev/null
+++ b/man/pfbtops.man
@@ -0,0 +1,27 @@
+.\" -*- nroff -*-
+.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
+.B @FONTDIR@/devps/download .
+.SH "SEE ALSO"
+.BR grops (@MAN1EXT@)
diff --git a/man/psbb.man b/man/psbb.man
new file mode 100644
index 000000000..e5c00851c
--- /dev/null
+++ b/man/psbb.man
@@ -0,0 +1,26 @@
+.\" -*- nroff -*-
+.TH PSBB @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+psbb \- extract bounding box from PostScript document
+.SH SYNOPSIS
+.B psbb
+.I file
+.SH DESCRIPTION
+psbb reads
+.I file
+which should be a PostScript document conforming to
+the Document Structuring conventions
+and looks for a
+.B %%BoundingBox
+comment.
+If it finds one,
+it prints a line
+.IP
+.I
+llx lly urx ury
+.LP
+on the standard output and exits with zero status.
+If it doesn't find such a line or if the line is invalid
+it prints a message and exits with non-zero status.
+.SH "SEE ALSO"
+.BR grops (1)
diff --git a/man/tfmtodit.man b/man/tfmtodit.man
new file mode 100644
index 000000000..68c95fe8e
--- /dev/null
+++ b/man/tfmtodit.man
@@ -0,0 +1,144 @@
+.\" -*- nroff -*-
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.TH TFMTODIT @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@"
+.SH NAME
+tfmtodit \- create font files for use with groff \-Tdvi
+.SH SYNOPSIS
+.B tfmtodit
+[
+.B \-sv
+]
+[
+.BI \-g gf_file
+]
+[
+.BI \-k skewchar
+]
+.I tfm_file
+.I map_file
+.I font
+.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 gtroff
+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 gtroff
+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 desciption 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/pic/Makefile b/pic/Makefile
new file mode 100644
index 000000000..31c26cd8c
--- /dev/null
+++ b/pic/Makefile
@@ -0,0 +1,82 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+CC=g++
+CFLAGS=-g -O -Wall
+INCLUDES=-I../lib
+MLIBS=-lm
+YACCFLAGS=-v
+YACC=bison -y
+ETAGS=etags
+ETAGSFLAGS=-p
+
+OBJECTS=pic.tab.o lex.o main.o object.o common.o troff.o tex.o # fig.o
+SOURCES=pic.tab.c lex.c main.c object.c common.c troff.c tex.c
+HEADERS=pic.h text.h output.h object.h common.h position.h
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: pic
+
+pic: $(OBJECTS) ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(MLIBS)
+
+pic.tab.c: pic.y
+ $(YACC) $(YACCFLAGS) -d pic.y
+ mv y.tab.c pic.tab.c
+ mv y.tab.h pic.tab.h
+
+PIC_H= pic.h text.h output.h position.h \
+ ../lib/lib.h ../lib/errarg.h ../lib/error.h ../lib/assert.h \
+ ../lib/stringclass.h ../lib/cset.h
+
+pic.tab.o: $(PIC_H) object.h
+object.o: $(PIC_H) object.h
+troff.o: $(PIC_H) common.h
+tex.o: $(PIC_H) common.h
+# fig.o: $(PIC_H)
+common.o: $(PIC_H) common.h
+main.o: $(PIC_H)
+lex.o: $(PIC_H) pic.tab.c object.h
+
+saber_pic:
+ @#load $(INCLUDES) $(CFLAGS) $(SOURCES) ../lib/libgroff.a -lm
+
+TAGS : $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES) $(HEADERS)
+
+clean:
+ -rm -f *.o core pic
+
+distclean: clean
+ -rm -f pic.output y.output TAGS
+
+realclean: distclean
+ -rm -f pic.tab.c pic.tab.h
+
+install.bin: pic
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/gpic
+ cp pic $(BINDIR)/gpic
+
+install.nobin:
+
+install: install.bin install.nobin
diff --git a/pic/TODO b/pic/TODO
new file mode 100644
index 000000000..03c02fd83
--- /dev/null
+++ b/pic/TODO
@@ -0,0 +1,26 @@
+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.
+
+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 tesg in a for loop should be fuzzy (as it is in grap).
+
+Possibly change fillval so that zero is black.
diff --git a/pic/common.c b/pic/common.c
new file mode 100644
index 000000000..1f31c04fb
--- /dev/null
+++ b/pic/common.c
@@ -0,0 +1,489 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+ 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;
+ 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);
+ 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);
+ 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/pic/common.h b/pic/common.h
new file mode 100644
index 000000000..283d8ff86
--- /dev/null
+++ b/pic/common.h
@@ -0,0 +1,70 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/pic/lex.c b/pic/lex.c
new file mode 100644
index 000000000..9634ea48a
--- /dev/null
+++ b/pic/lex.c
@@ -0,0 +1,1894 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+: lineno(0), ptr(""), filename(fn)
+{
+ fp = f;
+}
+
+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()
+{
+ 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;
+}
+
+#define ARG1 11
+
+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)
+: argc(ac), ap(0)
+{
+ 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++)
+ delete argv[i];
+ 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 inline int bol() { return bol_flag; }
+};
+
+input *input_stack::current_input = 0;
+int input_stack::bol_flag = 0;
+
+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;
+ for (int i = 0; i < 9; i++)
+ argv[i] = 0;
+ int level = 0;
+ int c;
+ do {
+ token_buffer.clear();
+ for (;;) {
+ c = input_stack::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);
+ 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,
+ "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", MAX,
+ "min", 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,
+ "spline", SPLINE,
+ "sprintf", SPRINTF,
+ "sqrt", SQRT,
+ "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";
+ }
+ 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";
+ }
+ 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;
+ }
+ 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 != 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 == '\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), to(t), by_is_multiplicative(bim), by(b), body(bd), p(body),
+ done_newline(0)
+{
+}
+
+for_input::~for_input()
+{
+ delete var;
+ 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)
+{
+ 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)
+: in(i), copy_thru_input(b, u)
+{
+}
+
+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()
+{
+ delete body;
+ 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), fp(p), lineno(1)
+{
+}
+
+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)
+{
+ 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:
+ 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/pic/main.c b/pic/main.c
new file mode 100644
index 000000000..45cdd5c04
--- /dev/null
+++ b/pic/main.c
@@ -0,0 +1,581 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "pic.h"
+
+extern int yyparse();
+
+output *out;
+
+int flyback_flag;
+int zero_length_line_flag = 0;
+int driver_extension_flag = 0;
+int compatible_flag = 0;
+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'
+
+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())
+ 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 {
+ 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 {
+ fp = fopen(filename, "r");
+ if (fp == 0)
+ fatal("can't open `%1': %2", filename, strerror(errno));
+ }
+ lex_init(new file_input(fp, filename));
+ yyparse();
+ parse_cleanup();
+ lex_cleanup();
+}
+#endif
+
+void usage()
+{
+ fprintf(stderr, "usage: %s [ -pxvzC ] [ filename ... ]\n", program_name);
+#ifdef TEX_SUPPORT
+ fprintf(stderr, " %s -t [ -cvzC ] [ filename ... ]\n", program_name);
+#endif
+#ifdef FIG_SUPPORT
+ fprintf(stderr, " %s -f [ -v ] [ filename ]\n", program_name);
+#endif
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ 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
+ int grops_flag = 0;
+#ifdef FIG_SUPPORT
+ int whole_file_flag = 0;
+ int fig_flag = 0;
+#endif
+ while ((opt = getopt(argc, argv, "T:CDtcvxzpf")) != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'D':
+ case 'T':
+ break;
+ case 'f':
+#ifdef FIG_SUPPORT
+ whole_file_flag++;
+ fig_flag++;
+#else
+ fatal("fig support not included");
+#endif
+ break;
+ case 'p':
+ grops_flag++;
+ 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;
+ fprintf(stderr, "GNU pic version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'x':
+ driver_extension_flag++;
+ /* fall through */
+ case 'z':
+ // zero length lines will be printed as dots
+ zero_length_line_flag++;
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ parse_init();
+ if (grops_flag)
+ out = make_grops_output();
+#ifdef TEX_SUPPORT
+ else if (tpic_flag) {
+ out = make_tpic_output();
+ lf_flag = 0;
+ }
+ else if (tex_flag) {
+ out = make_tex_output();
+ command_char = '\\';
+ lf_flag = 0;
+ }
+#endif
+#ifdef FIG_SUPPORT
+ else if (fig_flag)
+ out = make_fig_output();
+#endif
+ else
+ out = make_troff_output();
+#ifdef FIG_SUPPORT
+ if (whole_file_flag) {
+ if (optind >= argc)
+ do_whole_file("-");
+ else if (argc - optind > 1)
+ usage();
+ 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");
+ exit(0);
+}
+
diff --git a/pic/object.c b/pic/object.c
new file mode 100644
index 000000000..5b09ff9db
--- /dev/null
+++ b/pic/object.c
@@ -0,0 +1,1807 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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() : desired_height(0.0), desired_width(0.0), args(0)
+{
+}
+
+output::~output()
+{
+ 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)
+{
+ 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;
+ lookup_variable("maxpswid", &max_width);
+ double max_height;
+ 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)
+ *this = pl.obj->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);
+ 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)
+: pos(a), is_absolute(n), next(p)
+{
+}
+
+text_item::text_item(char *t, const char *fn, int ln)
+: filename(fn), lineno(ln), text(t), next(0)
+{
+ adj.h = CENTER_ADJUST;
+ adj.v = NONE_ADJUST;
+}
+
+text_item::~text_item()
+{
+ 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()
+{
+ 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)
+{
+ for (object *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()
+{
+ 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;
+ for (text_item *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()
+{
+ 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)
+: oblist(ol), tbl(t), rectangle_object(d)
+{
+}
+
+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)
+: strt(s), en(e), arrow_at_start(0), arrow_at_end(0)
+{
+}
+
+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)
+: v(p), n(i), linear_object(s, e)
+{
+}
+
+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()
+{
+ 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;
+ for (segment *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()
+{
+ 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)
+: label_list(0), crn(c)
+{
+}
+
+path::path(char *l, corner c)
+: crn(c)
+{
+ label_list = new string_list(l);
+}
+
+path::~path()
+{
+ while (label_list) {
+ string_list *tem = label_list;
+ label_list = label_list->next;
+ delete tem;
+ }
+}
+
+void path::append(corner c)
+{
+ assert(crn == 0);
+ crn = c;
+}
+
+void path::append(char *s)
+{
+ for (string_list **p = &label_list; *p; p = &(*p)->next)
+ ;
+ *p = new string_list(s);
+}
+
+// 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;
+ }
+ 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/pic/object.h b/pic/object.h
new file mode 100644
index 000000000..d1e17a87f
--- /dev/null
+++ b/pic/object.h
@@ -0,0 +1,218 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+public:
+ path(corner = 0);
+ path(char *, corner = 0);
+ ~path();
+ void append(corner);
+ void append(char *);
+ 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 : 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();
+};
+
+
+enum {
+ IS_DOTTED = 01,
+ IS_DASHED = 02,
+ IS_CLOCKWISE = 04,
+ IS_INVISIBLE = 020,
+ HAS_LEFT_ARROW_HEAD = 040,
+ HAS_RIGHT_ARROW_HEAD = 0100,
+ HAS_SEGMENT = 0200,
+ IS_SAME = 0400,
+ HAS_FROM = 01000,
+ HAS_AT = 02000,
+ HAS_WITH = 04000,
+ HAS_HEIGHT = 010000,
+ HAS_WIDTH = 020000,
+ HAS_RADIUS = 040000,
+ HAS_TO = 0100000,
+ IS_CHOPPED = 0200000,
+ IS_DEFAULT_CHOPPED = 0400000,
+ HAS_THICKNESS = 01000000,
+ IS_FILLED = 02000000,
+ IS_DEFAULT_FILLED = 04000000,
+ 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 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/pic/output.c b/pic/output.c
new file mode 100644
index 000000000..6f9abc069
--- /dev/null
+++ b/pic/output.c
@@ -0,0 +1,4 @@
+// -*- C++ -*-
+
+#include "pic.h"
+
diff --git a/pic/output.h b/pic/output.h
new file mode 100644
index 000000000..b54f77b12
--- /dev/null
+++ b/pic/output.h
@@ -0,0 +1,80 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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();
+output *make_grops_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/pic/pic.h b/pic/pic.h
new file mode 100644
index 000000000..5c657b96e
--- /dev/null
+++ b/pic/pic.h
@@ -0,0 +1,94 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <osfcn.h>
+#include <errno.h>
+
+#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
+
+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;
diff --git a/pic/pic.tab.c b/pic/pic.tab.c
new file mode 100644
index 000000000..b346e45dd
--- /dev/null
+++ b/pic/pic.tab.c
@@ -0,0 +1,3205 @@
+
+/* A Bison parser, made from pic.y */
+
+#define LABEL 258
+#define VARIABLE 259
+#define NUMBER 260
+#define TEXT 261
+#define COMMAND 262
+#define DELIMITED 263
+#define ORDINAL 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 DOTTED 291
+#define DASHED 292
+#define CHOP 293
+#define SAME 294
+#define INVISIBLE 295
+#define LJUST 296
+#define RJUST 297
+#define ABOVE 298
+#define BELOW 299
+#define OF 300
+#define THE 301
+#define WAY 302
+#define BETWEEN 303
+#define AND 304
+#define HERE 305
+#define DOT_N 306
+#define DOT_E 307
+#define DOT_W 308
+#define DOT_S 309
+#define DOT_NE 310
+#define DOT_SE 311
+#define DOT_NW 312
+#define DOT_SW 313
+#define DOT_C 314
+#define DOT_START 315
+#define DOT_END 316
+#define DOT_X 317
+#define DOT_Y 318
+#define DOT_HT 319
+#define DOT_WID 320
+#define DOT_RAD 321
+#define SIN 322
+#define COS 323
+#define ATAN2 324
+#define LOG 325
+#define EXP 326
+#define SQRT 327
+#define MAX 328
+#define MIN 329
+#define INT 330
+#define RAND 331
+#define COPY 332
+#define THRU 333
+#define TOP 334
+#define BOTTOM 335
+#define UPPER 336
+#define LOWER 337
+#define SH 338
+#define PRINT 339
+#define CW 340
+#define CCW 341
+#define FOR 342
+#define DO 343
+#define IF 344
+#define ELSE 345
+#define ANDAND 346
+#define OROR 347
+#define NOTEQUAL 348
+#define EQUALEQUAL 349
+#define LESSEQUAL 350
+#define GREATEREQUAL 351
+#define LEFT_CORNER 352
+#define RIGHT_CORNER 353
+#define CENTER 354
+#define END 355
+#define START 356
+#define RESET 357
+#define UNTIL 358
+#define PLOT 359
+#define THICKNESS 360
+#define FILL 361
+#define ALIGNED 362
+#define SPRINTF 363
+#define DEFINE 364
+#define UNDEF 365
+
+#line 19 "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();
+
+extern "C" {
+ double fmod(double, double);
+ int rand();
+}
+
+#define YYDEBUG 1
+
+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 69 "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;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __STDC__
+#define const
+#endif
+
+
+
+#define YYFINAL 373
+#define YYFLAG -32768
+#define YYNTBASE 131
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 365 ? yytranslate[x] : 170)
+
+static const short yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 120, 2, 2, 2, 119, 2, 2, 111,
+ 129, 117, 115, 112, 116, 130, 118, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 124, 122, 113,
+ 123, 114, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 127, 2, 128, 121, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 125, 2, 126, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110
+};
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 263, 265, 273, 278, 281, 285, 287, 290, 292, 295,
+ 301, 303, 305, 307, 309, 315, 320, 326, 331, 334,
+ 339, 346, 349, 351, 359, 362, 364, 371, 374, 380,
+ 388, 391, 402, 403, 407, 410, 412, 416, 420, 423,
+ 426, 430, 433, 439, 446, 449, 451, 455, 470, 472,
+ 480, 486, 492, 499, 507, 512, 516, 520, 525, 532,
+ 539, 546, 553, 558, 563, 570, 585, 597, 603, 609,
+ 615, 621, 640, 647, 654, 661, 668, 675, 682, 689,
+ 696, 703, 718, 730, 736, 743, 755, 761, 767, 773,
+ 779, 784, 790, 806, 823, 828, 833, 838, 843, 848,
+ 853, 858, 865, 874, 883, 892, 901, 907, 914, 919,
+ 929, 936, 957, 960, 968, 971, 976, 981, 986, 991,
+ 998, 1000, 1003, 1006, 1010, 1013, 1019, 1025, 1031, 1039,
+ 1050, 1054, 1062, 1065, 1069, 1084, 1100, 1103, 1105, 1107,
+ 1109, 1111, 1113, 1115, 1117, 1121, 1126, 1133, 1141, 1145,
+ 1152, 1158, 1164, 1170, 1176, 1184, 1187, 1189, 1191, 1193,
+ 1195, 1197, 1199, 1201, 1203, 1205, 1207, 1209, 1211, 1213,
+ 1215, 1217, 1219, 1221, 1223, 1225, 1227, 1229, 1231, 1233,
+ 1235, 1237, 1239, 1243, 1252, 1254, 1261, 1268, 1275, 1282,
+ 1289, 1291, 1293, 1295, 1303, 1311, 1324, 1326, 1328, 1337,
+ 1346, 1359, 1368, 1377, 1386, 1388, 1390, 1392, 1394, 1400,
+ 1402, 1404, 1406, 1408, 1410, 1412, 1414, 1416
+};
+
+static const char * const yytname[] = { 0,
+"error","$illegal.","LABEL","VARIABLE","NUMBER","TEXT","COMMAND","DELIMITED","ORDINAL","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","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","MAX","MIN","INT","RAND","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","DEFINE","UNDEF",
+"'('","','","'<'","'>'","'+'","'-'","'*'","'/'","'%'","'!'",
+"'^'","';'","'='","':'","'{'","'}'","'['","']'","')'","'.'",
+"top"
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 131, 131, 132, 133, 133, 134, 134, 135, 135, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 137, 136,
+ 136, 138, 139, 136, 140, 141, 136, 142, 136, 136,
+ 143, 136, 136, 136, 144, 144, 144, 146, 145, 147,
+ 147, 148, 148, 148, 149, 149, 149, 150, 150, 150,
+ 150, 151, 150, 150, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 153, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152, 154, 154,
+ 155, 155, 156, 156, 157, 157, 157, 157, 157, 157,
+ 158, 158, 159, 159, 160, 160, 160, 160, 160, 161,
+ 161, 161, 162, 162, 163, 163, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 165, 165, 166, 166, 166,
+ 167, 167, 167, 167, 167, 168, 168, 168, 168, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 168, 168, 168, 169, 169, 169, 169, 169, 169, 169,
+ 169, 169, 169, 169, 169, 169, 169, 169, 169, 169,
+ 169, 169, 169, 169, 169, 169, 169, 169, 169, 169,
+ 169, 169, 169, 169, 169, 169, 169, 169
+};
+
+static const short yyr2[] = { 0,
+ 1, 1, 3, 1, 3, 0, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 2, 2, 2, 0, 3,
+ 2, 0, 0, 7, 0, 0, 6, 0, 10, 1,
+ 0, 4, 1, 1, 2, 2, 3, 0, 5, 0,
+ 2, 1, 3, 3, 0, 2, 3, 1, 4, 4,
+ 4, 0, 4, 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, 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, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 3, 1, 1, 2,
+ 1, 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, 3,
+ 3, 3, 3, 3, 3, 3, 3, 2
+};
+
+static const short yydefact[] = { 6,
+ 8, 2, 1, 7, 0, 0, 109, 15, 11, 12,
+ 13, 14, 55, 56, 57, 58, 59, 60, 61, 62,
+ 0, 19, 0, 0, 0, 34, 0, 0, 52, 66,
+ 6, 54, 33, 30, 4, 48, 63, 9, 6, 0,
+ 21, 25, 0, 130, 184, 185, 0, 133, 169, 170,
+ 129, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 167, 168, 0, 0, 175, 176, 181, 183,
+ 182, 0, 0, 0, 17, 18, 113, 115, 114, 125,
+ 0, 131, 0, 16, 0, 0, 0, 0, 0, 42,
+ 35, 64, 0, 6, 6, 3, 7, 36, 0, 31,
+ 97, 98, 99, 73, 75, 79, 77, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 86, 87, 89, 93,
+ 95, 96, 103, 104, 105, 106, 100, 101, 0, 91,
+ 108, 102, 72, 0, 10, 22, 0, 20, 145, 134,
+ 137, 138, 139, 140, 141, 142, 143, 0, 135, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 171,
+ 173, 177, 179, 172, 174, 178, 180, 0, 115, 0,
+ 197, 218, 0, 0, 186, 187, 188, 189, 190, 0,
+ 126, 136, 0, 127, 0, 121, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 38, 0, 0, 0, 65, 111,
+ 0, 0, 0, 5, 37, 0, 74, 76, 80, 78,
+ 68, 69, 70, 71, 81, 0, 82, 83, 0, 0,
+ 0, 0, 149, 151, 84, 148, 0, 85, 0, 88,
+ 90, 94, 107, 92, 130, 184, 13, 14, 49, 0,
+ 50, 51, 0, 26, 144, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 209, 0, 0, 124, 198, 116,
+ 117, 132, 128, 0, 216, 217, 215, 214, 211, 213,
+ 123, 0, 210, 212, 191, 192, 193, 194, 195, 196,
+ 0, 0, 0, 44, 43, 210, 0, 53, 67, 32,
+ 155, 0, 0, 0, 146, 0, 150, 0, 0, 23,
+ 40, 199, 200, 0, 202, 203, 204, 0, 0, 207,
+ 208, 0, 0, 0, 0, 0, 39, 0, 110, 0,
+ 154, 153, 147, 40, 0, 27, 0, 0, 0, 118,
+ 122, 0, 119, 45, 112, 152, 24, 41, 201, 205,
+ 206, 120, 0, 0, 0, 46, 28, 47, 0, 29,
+ 0, 0, 0
+};
+
+static const short yydefgoto[] = { 371,
+ 2, 31, 222, 4, 32, 43, 263, 344, 147, 321,
+ 369, 226, 33, 34, 303, 346, 97, 364, 35, 104,
+ 36, 105, 37, 307, 86, 87, 212, 88, 99, 90,
+ 91, 92, 159, 243, 244, 245, 93, 236
+};
+
+static const short yypact[] = { -101,
+-32768,-32768, 535, -99, -96, -89,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ 23,-32768, 750, 37, 851, 38, 1057, -65,-32768,-32768,
+ -101,-32768, 1, -42,-32768, 641,-32768,-32768, -101, 851,
+ -4,-32768, 68,-32768,-32768,-32768, 62,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768, -32, -21, -9, -7, 25, 41, 42, 46,
+ 48, 52,-32768,-32768, 2, 21,-32768,-32768,-32768,-32768,
+-32768, 1158, 1057, 1057,-32768, -59,-32768,-32768, 232, 1673,
+ 363,-32768, 13, 1646, 7, 1057, 97, 28, 232, 1973,
+-32768, 984, 143, -101, -101,-32768, 509,-32768, 150,-32768,
+-32768,-32768,-32768, 1499, 1499, 1339, 1419, 1057, 1057, 1057,
+ 1057, 1158, 1158, 1158, 153, 1259,-32768, 1499, 1499, 1499,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 1057, 1499,
+-32768,-32768, 1973, 394,-32768,-32768, 157,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768, 39,-32768, 1057,
+ 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 276,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768, -61, 44, 1594,
+ 50, 50, 1259, 1259,-32768,-32768,-32768,-32768,-32768, 190,
+-32768,-32768, 111, 64, 126,-32768, 1057, 1057, 1057, 1057,
+ 1057, 1057, 1057, 1158, 1057, 1057, 1057, 1057, 1057, 1057,
+ 1057, 1158, 1057, 1701,-32768, 0, 0, 1057,-32768,-32768,
+ 70, 535, 69,-32768,-32768, 194, 1973, 1973, 1973, 1973,
+ 1973, 1973, 1973, 1973, -59, 1646, -59, -59, 1569, 253,
+ 363, 200, 1621,-32768,-32768,-32768, 1259,-32768, 677, 1973,
+ 1973, 1973, 1973, 1973, -96, -89, 22, 27,-32768, -59,
+ 19, 154, 207,-32768,-32768, 1732, 1741, 1540, 1772, 1781,
+ 1812, 1932, 1942, 1821,-32768, 1852, 1158,-32768,-32768,-32768,
+-32768,-32768, 64, 174, 720, 778, 767, 767, 125, 125,
+ 1973, 11, 110, 125, 169, 169, 50, 50, 50, 50,
+ -38, 486, 215,-32768,-32768, 125, -80,-32768,-32768,-32768,
+-32768, 363, 1569, 1569,-32768, 227,-32768, 44, 1692,-32768,
+ 133,-32768,-32768, 1057,-32768,-32768,-32768, 1057, 1057,-32768,
+-32768, -63, 189, 1158, 1158, 1057,-32768, 1057,-32768, 1569,
+-32768,-32768,-32768, 133, 239,-32768, 1861, 1892, 1901,-32768,
+-32768, -52, -59, 452, 1973,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768, 952, 159, 1057, 1973,-32768, 1973, 240,-32768,
+ 249, 257,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ 43,-32768, 8, 229,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768, -86, 221,-32768, -74,-32768,
+-32768,-32768, -16,-32768, -79, 118,-32768, -55, -13, -81,
+-32768,-32768, -90,-32768, -225,-32768, -75, -23
+};
+
+
+#define YYLAST 2094
+
+
+static const short yytable[] = { 94,
+ 192, 100, 178, 102, 108, 7, 85, 3, 98, 89,
+ 335, 194, 143, 311, 191, 44, 100, 170, 171, 142,
+ 1, 47, 38, 98, -169, 48, 179, 39, 41, -170,
+ -169, 338, 224, 40, -169, -170, 174, 175, 106, -170,
+ 95, 101, 235, 237, 238, 103, 144, 110, 339, 246,
+ 277, 183, 184, 183, 184, 183, 184, 193, 180, 181,
+ 182, 362, 183, 184, 260, 350, -169, 149, 89, 259,
+ 248, -170, 214, 146, 150, 148, 183, 184, 160, 151,
+ 152, 153, 154, 155, 156, 219, 157, 341, 342, 161,
+ 227, 228, 229, 230, 231, 232, 233, 234, 172, 173,
+ 42, 162, 249, 163, 250, 251, 252, 28, 89, 89,
+ 89, 283, 109, 44, 356, 253, 254, 176, 177, 47,
+ 216, 217, 334, 48, 292, 183, 184, 280, 281, 213,
+ 262, 215, 301, -113, -113, 164, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 276, 221, 223, 220, 313,
+ 314, 165, 166, 225, 195, 239, 167, 196, 168, 249,
+ 249, 240, 169, 246, 264, 241, 265, 317, 49, 50,
+ 211, 284, 278, 285, 286, 287, 288, 289, 290, 291,
+ 293, 294, 295, 296, 297, 298, 299, 300, 158, 302,
+ 89, 318, 282, 190, 306, 308, 309, 332, 89, 304,
+ 305, 310, 315, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 320, 185, 186, 187, 188, 189,
+ 333, 340, 337, 319, 206, 207, 208, 209, 210, 343,
+ 211, 73, 74, 75, 76, 345, 351, 246, 246, 206,
+ 207, 208, 209, 210, 358, 211, 367, 370, 372, 77,
+ 78, 79, 80, 81, 352, 353, 373, 357, 149, 107,
+ 145, 261, 0, 89, 246, 312, 0, 0, -114, -114,
+ 151, 152, 153, 154, 155, 156, 0, 157, 44, 45,
+ 46, 0, 242, 0, 47, 208, 209, 210, 48, 211,
+ 0, 49, 50, 185, 186, 187, 188, 189, 0, 0,
+ 347, 0, 0, 0, 348, 349, 0, 0, 0, 0,
+ 0, 0, 354, 0, 355, 0, 0, 0, 0, 0,
+ 89, 89, 0, 0, 0, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 0, 0, 366,
+ 0, 368, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 0, 0, 73, 74, 75, 76, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 149, 0,
+ 0, 0, 77, 78, 79, 80, 81, 0, 0, 158,
+ 151, 152, 153, 154, 155, 156, 96, 157, 0, 0,
+ 0, 83, 0, 0, 0, 84, 255, 256, 46, 7,
+ 8, 0, 47, 0, 275, 0, 48, 9, 10, 257,
+ 258, 13, 14, 15, 16, 17, 18, 19, 20, 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, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 0, 0, 0, 0, 0,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 21, 0, 73, 74, 75, 76, 22, 23, 0, 0,
+ 24, 0, 25, 0, 0, 363, 0, 0, 0, 158,
+ 77, 78, 79, 80, 81, 26, 0, 27, 0, 0,
+ 0, 28, 0, 0, 82, 0, 0, 0, 0, 83,
+ 0, 5, 6, 84, 7, 8, 336, 0, 29, 0,
+ 30, 0, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 0, 0, 0, 5, 6, 0,
+ 7, 8, 197, 198, 199, 200, 201, 202, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 0, 0, 0, 197, 198, 199, 200,
+ 201, 202, 0, 0, 0, 21, 0, 0, 0, 0,
+ 0, 22, 23, 0, 0, 24, 0, 25, 218, 205,
+ 206, 207, 208, 209, 210, 0, 211, 0, 0, 0,
+ 26, 21, 27, 0, 0, 0, 28, 22, 23, 0,
+ 0, 24, 0, 25, 0, 0, 0, 0, 0, 0,
+ 38, 0, 0, 29, 0, 30, 26, 0, 27, 0,
+ 0, 0, 28, 44, 45, 46, 7, 0, 0, 47,
+ 111, 112, 113, 48, 114, 115, 116, 117, 0, 29,
+ 0, 30, 0, 0, 0, 0, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 0, 0, 0, 0, 0,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 0, 0, 0, 0, 0, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 0, 0, 73,
+ 74, 75, 76, 0, 0, 137, 138, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 77, 78, 79,
+ 80, 81, 0, 0, 0, 139, 140, 141, 28, 0,
+ 0, 96, 44, 45, 46, 7, 83, 0, 47, 0,
+ 84, 0, 48, 0, 0, 49, 50, 197, 198, 199,
+ 200, 201, 202, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 203, 218,
+ 205, 206, 207, 208, 209, 210, 0, 211, 0, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 0, 199, 200, 201, 202, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 0, 0, 73, 74,
+ 75, 76, 218, 205, 206, 207, 208, 209, 210, 0,
+ 211, 0, 0, 0, 0, 0, 77, 78, 79, 80,
+ 81, 0, 0, 44, 45, 46, 7, 28, 0, 47,
+ 82, 201, 202, 48, 0, 83, 49, 50, 197, 84,
+ 199, 200, 201, 202, 0, 0, 0, 0, 0, 218,
+ 205, 206, 207, 208, 209, 210, 0, 211, 0, 0,
+ 218, 205, 206, 207, 208, 209, 210, 0, 211, 0,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 0, 0, 0, 0, 0, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 0, 0, 73,
+ 74, 75, 76, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 77, 78, 79,
+ 80, 81, 0, 0, 44, 45, 46, 0, 28, 0,
+ 47, 96, 0, 0, 48, 0, 83, 49, 50, 0,
+ 84, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 0, 0, 0, 0, 0, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 0, 0,
+ 73, 74, 75, 76, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 77, 78,
+ 79, 80, 81, 0, 0, 0, 0, 0, 0, 44,
+ 45, 46, 96, 0, 0, 47, 0, 83, 365, 48,
+ 0, 84, 49, 50, 197, 198, 199, 200, 201, 202,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 28, 0, 0, 0, 0, 218, 205, 206, 207,
+ 208, 209, 210, 0, 211, 0, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 0, 0,
+ 0, 0, 0, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 0, 0, 73, 74, 75, 76, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 77, 78, 79, 80, 81, 0, 0,
+ 44, 45, 46, 0, 0, 0, 47, 96, 0, 0,
+ 48, 0, 83, 49, 50, 0, 84, 0, 0, 0,
+ 0, 0, 0, 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, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 0,
+ 0, 0, 0, 0, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 0, 0, 73, 74, 75, 76,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 77, 78, 79, 80, 81, 0,
+ 0, 44, 45, 46, 0, 0, 0, 47, 82, 0,
+ 0, 48, 0, 83, 49, 50, 0, 84, 0, 0,
+ 0, 0, 0, 0, 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, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 0, 0, 0, 0, 0, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 0, 0, 73, 74, 75,
+ 76, 44, 45, 46, 0, 0, 0, 47, 0, 0,
+ 0, 48, 0, 0, 0, 77, 78, 79, 80, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 247,
+ 0, 0, 0, 0, 83, 0, 0, 0, 84, 0,
+ 0, 0, 0, -169, 0, 0, 0, 0, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 0, 0, 0, 0, 0, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 0, 0, 73, 74, 75,
+ 76, 44, 45, 46, 0, 0, 0, 47, 0, 0,
+ 0, 48, 0, 0, 0, 77, 78, 79, 80, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,
+ 0, 0, 0, 0, 83, 0, 0, 0, 84, 0,
+ 0, 0, 0, -170, 0, 0, 0, 0, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 0, 0, 0, 0, 0, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 0, 0, 73, 74, 75,
+ 76, 44, 45, 46, 0, 0, 0, 47, 0, 0,
+ 0, 48, 0, 0, 0, 77, 78, 79, 80, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,
+ 0, 0, 0, 0, 83, 0, 0, 0, 84, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 0, 0, 0, 0, 0, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 0, 0, 73, 74, 75,
+ 76, 0, 0, 0, 49, 50, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 77, 78, 79, 80, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,
+ 0, 0, 0, 0, 83, 0, 0, 0, 84, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 197, 198, 199, 200, 201, 202, 49, 50, 195, 0,
+ 0, 196, 0, 0, 0, 0, 0, 73, 74, 75,
+ 76, 324, 218, 205, 206, 207, 208, 209, 210, 0,
+ 211, 0, 0, 0, 0, 77, 78, 79, 80, 81,
+ 0, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 0, 0, 197, 198, 199, 200, 201, 202,
+ 195, 0, 0, 196, 0, 0, 0, 0, 242, 73,
+ 74, 75, 76, 0, 0, 203, 204, 205, 206, 207,
+ 208, 209, 210, 0, 211, 0, 0, 77, 78, 79,
+ 80, 81, 279, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 0, 0, 197, 198, 199, 200,
+ 201, 202, 0, 0, 0, 0, 0, 0, 0, 0,
+ 316, 73, 74, 75, 76, 0, 0, 203, 204, 205,
+ 206, 207, 208, 209, 210, 0, 211, 0, 0, 77,
+ 78, 79, 80, 81, 0, 0, 0, 0, 0, 0,
+ 0, 0, 197, 198, 199, 200, 201, 202, 0, 0,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 190, 203, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 279, 211, 197, 198, 199, 200, 201, 202, 0, 279,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 322, 211, 197, 198, 199, 200, 201, 202, 0, 323,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 325, 211, 197, 198, 199, 200, 201, 202, 0, 326,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 327, 211, 197, 198, 199, 200, 201, 202, 0, 330,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 331, 211, 197, 198, 199, 200, 201, 202, 0, 359,
+ 0, 197, 198, 199, 200, 201, 202, 0, 0, 0,
+ 0, 0, 0, 0, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 218, 205, 206, 207, 208, 209, 210,
+ 360, 211, 197, 198, 199, 200, 201, 202, 0, 361,
+ 0, 0, 197, 198, 199, 200, 201, 202, 0, 0,
+ 0, 0, 0, 328, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 329, 218, 205, 206, 207, 208, 209,
+ 210, 0, 211, 197, 198, 199, 200, 201, 202, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 218, 205, 206, 207, 208,
+ 209, 210, 0, 211
+};
+
+static const short yycheck[] = { 23,
+ 91, 25, 82, 27, 4, 6, 23, 0, 25, 23,
+ 49, 93, 36, 239, 90, 3, 40, 16, 17, 36,
+ 122, 9, 122, 40, 3, 13, 82, 124, 6, 3,
+ 9, 112, 107, 123, 13, 9, 16, 17, 31, 13,
+ 4, 4, 122, 123, 124, 111, 39, 90, 129, 125,
+ 112, 115, 116, 115, 116, 115, 116, 45, 82, 83,
+ 84, 114, 115, 116, 144, 129, 45, 6, 82, 144,
+ 126, 45, 96, 78, 13, 8, 115, 116, 111, 18,
+ 19, 20, 21, 22, 23, 102, 25, 313, 314, 111,
+ 114, 115, 116, 117, 118, 119, 120, 121, 97, 98,
+ 78, 111, 126, 111, 128, 129, 130, 108, 122, 123,
+ 124, 193, 112, 3, 340, 139, 140, 97, 98, 9,
+ 93, 94, 112, 13, 204, 115, 116, 183, 184, 123,
+ 144, 35, 212, 115, 116, 111, 160, 161, 162, 163,
+ 164, 165, 166, 167, 168, 169, 104, 105, 6, 240,
+ 241, 111, 111, 4, 45, 3, 111, 48, 111, 183,
+ 184, 9, 111, 239, 8, 13, 128, 243, 16, 17,
+ 121, 46, 129, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 210, 211, 127, 213,
+ 204, 247, 3, 130, 218, 126, 128, 277, 212, 216,
+ 217, 8, 3, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 8, 62, 63, 64, 65, 66,
+ 47, 312, 8, 247, 115, 116, 117, 118, 119, 3,
+ 121, 79, 80, 81, 82, 103, 48, 313, 314, 115,
+ 116, 117, 118, 119, 6, 121, 88, 8, 0, 97,
+ 98, 99, 100, 101, 334, 335, 0, 344, 6, 31,
+ 40, 144, -1, 277, 340, 13, -1, -1, 115, 116,
+ 18, 19, 20, 21, 22, 23, -1, 25, 3, 4,
+ 5, -1, 130, -1, 9, 117, 118, 119, 13, 121,
+ -1, 16, 17, 62, 63, 64, 65, 66, -1, -1,
+ 324, -1, -1, -1, 328, 329, -1, -1, -1, -1,
+ -1, -1, 336, -1, 338, -1, -1, -1, -1, -1,
+ 334, 335, -1, -1, -1, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, 363,
+ -1, 365, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, -1, 79, 80, 81, 82, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 6, -1,
+ -1, -1, 97, 98, 99, 100, 101, -1, -1, 127,
+ 18, 19, 20, 21, 22, 23, 111, 25, -1, -1,
+ -1, 116, -1, -1, -1, 120, 3, 4, 5, 6,
+ 7, -1, 9, -1, 129, -1, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, -1, 79, 80, 81, 82, 83, 84, -1, -1,
+ 87, -1, 89, -1, -1, 34, -1, -1, -1, 127,
+ 97, 98, 99, 100, 101, 102, -1, 104, -1, -1,
+ -1, 108, -1, -1, 111, -1, -1, -1, -1, 116,
+ -1, 3, 4, 120, 6, 7, 31, -1, 125, -1,
+ 127, -1, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, -1, -1, -1, 3, 4, -1,
+ 6, 7, 91, 92, 93, 94, 95, 96, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, -1, -1, -1, 91, 92, 93, 94,
+ 95, 96, -1, -1, -1, 77, -1, -1, -1, -1,
+ -1, 83, 84, -1, -1, 87, -1, 89, 113, 114,
+ 115, 116, 117, 118, 119, -1, 121, -1, -1, -1,
+ 102, 77, 104, -1, -1, -1, 108, 83, 84, -1,
+ -1, 87, -1, 89, -1, -1, -1, -1, -1, -1,
+ 122, -1, -1, 125, -1, 127, 102, -1, 104, -1,
+ -1, -1, 108, 3, 4, 5, 6, -1, -1, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, -1, 125,
+ -1, 127, -1, -1, -1, -1, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, -1, -1, -1, -1, -1,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, -1, -1, -1, -1, -1, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, -1, -1, 79,
+ 80, 81, 82, -1, -1, 85, 86, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 97, 98, 99,
+ 100, 101, -1, -1, -1, 105, 106, 107, 108, -1,
+ -1, 111, 3, 4, 5, 6, 116, -1, 9, -1,
+ 120, -1, 13, -1, -1, 16, 17, 91, 92, 93,
+ 94, 95, 96, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 112, 113,
+ 114, 115, 116, 117, 118, 119, -1, 121, -1, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, -1, 93, 94, 95, 96, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, -1, -1, 79, 80,
+ 81, 82, 113, 114, 115, 116, 117, 118, 119, -1,
+ 121, -1, -1, -1, -1, -1, 97, 98, 99, 100,
+ 101, -1, -1, 3, 4, 5, 6, 108, -1, 9,
+ 111, 95, 96, 13, -1, 116, 16, 17, 91, 120,
+ 93, 94, 95, 96, -1, -1, -1, -1, -1, 113,
+ 114, 115, 116, 117, 118, 119, -1, 121, -1, -1,
+ 113, 114, 115, 116, 117, 118, 119, -1, 121, -1,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, -1, -1, -1, -1, -1, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, -1, -1, 79,
+ 80, 81, 82, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 97, 98, 99,
+ 100, 101, -1, -1, 3, 4, 5, -1, 108, -1,
+ 9, 111, -1, -1, 13, -1, 116, 16, 17, -1,
+ 120, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 6,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, -1, -1, -1, -1, -1, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, -1, -1,
+ 79, 80, 81, 82, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 97, 98,
+ 99, 100, 101, -1, -1, -1, -1, -1, -1, 3,
+ 4, 5, 111, -1, -1, 9, -1, 116, 117, 13,
+ -1, 120, 16, 17, 91, 92, 93, 94, 95, 96,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 108, -1, -1, -1, -1, 113, 114, 115, 116,
+ 117, 118, 119, -1, 121, -1, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
+ -1, -1, -1, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, -1, -1, 79, 80, 81, 82, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 97, 98, 99, 100, 101, -1, -1,
+ 3, 4, 5, -1, -1, -1, 9, 111, -1, -1,
+ 13, -1, 116, 16, 17, -1, 120, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
+ -1, -1, -1, -1, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, -1, -1, 79, 80, 81, 82,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 97, 98, 99, 100, 101, -1,
+ -1, 3, 4, 5, -1, -1, -1, 9, 111, -1,
+ -1, 13, -1, 116, 16, 17, -1, 120, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, -1, -1, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, -1, -1, 79, 80, 81,
+ 82, 3, 4, 5, -1, -1, -1, 9, -1, -1,
+ -1, 13, -1, -1, -1, 97, 98, 99, 100, 101,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 111,
+ -1, -1, -1, -1, 116, -1, -1, -1, 120, -1,
+ -1, -1, -1, 45, -1, -1, -1, -1, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, -1, -1, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, -1, -1, 79, 80, 81,
+ 82, 3, 4, 5, -1, -1, -1, 9, -1, -1,
+ -1, 13, -1, -1, -1, 97, 98, 99, 100, 101,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 111,
+ -1, -1, -1, -1, 116, -1, -1, -1, 120, -1,
+ -1, -1, -1, 45, -1, -1, -1, -1, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, -1, -1, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, -1, -1, 79, 80, 81,
+ 82, 3, 4, 5, -1, -1, -1, 9, -1, -1,
+ -1, 13, -1, -1, -1, 97, 98, 99, 100, 101,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 111,
+ -1, -1, -1, -1, 116, -1, -1, -1, 120, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, -1, -1, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, -1, -1, 79, 80, 81,
+ 82, -1, -1, -1, 16, 17, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 97, 98, 99, 100, 101,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 111,
+ -1, -1, -1, -1, 116, -1, -1, -1, 120, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 91, 92, 93, 94, 95, 96, 16, 17, 45, -1,
+ -1, 48, -1, -1, -1, -1, -1, 79, 80, 81,
+ 82, 112, 113, 114, 115, 116, 117, 118, 119, -1,
+ 121, -1, -1, -1, -1, 97, 98, 99, 100, 101,
+ -1, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, -1, -1, 91, 92, 93, 94, 95, 96,
+ 45, -1, -1, 48, -1, -1, -1, -1, 130, 79,
+ 80, 81, 82, -1, -1, 112, 113, 114, 115, 116,
+ 117, 118, 119, -1, 121, -1, -1, 97, 98, 99,
+ 100, 101, 129, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, -1, -1, 91, 92, 93, 94,
+ 95, 96, -1, -1, -1, -1, -1, -1, -1, -1,
+ 130, 79, 80, 81, 82, -1, -1, 112, 113, 114,
+ 115, 116, 117, 118, 119, -1, 121, -1, -1, 97,
+ 98, 99, 100, 101, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, 92, 93, 94, 95, 96, -1, -1,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, 130, 112, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, 91, 92, 93, 94, 95, 96, -1, -1, -1,
+ -1, -1, -1, -1, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 113, 114, 115, 116, 117, 118, 119,
+ 129, 121, 91, 92, 93, 94, 95, 96, -1, 129,
+ -1, -1, 91, 92, 93, 94, 95, 96, -1, -1,
+ -1, -1, -1, 112, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 112, 113, 114, 115, 116, 117, 118,
+ 119, -1, 121, 91, 92, 93, 94, 95, 96, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 113, 114, 115, 116, 117,
+ 118, 119, -1, 121
+};
+#define YYPURE 1
+
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program 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 1, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifdef __GNUC__
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif /* Not alloca. */
+#else /* Not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
+#include <alloca.h>
+#endif /* Sparc. */
+#endif /* Not GNU C. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYIMPURE
+#define YYLEX yylex()
+#endif
+
+#ifndef YYPURE
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYIMPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* YYIMPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+#ifdef __cplusplus
+__yy_bcopy (char *from, char *to, int count)
+#else
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+#endif
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#line 137 "/usr/local/lib/bison.simple"
+int
+yyparse()
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+
+#define YYPOPSTACK (yyvsp--, yysp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yysp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifndef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+#ifdef YYLSP_NEEDED
+ &yyls1, size * sizeof (*yylsp),
+#endif
+ &yystacksize);
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Next token is %d (%s)\n", yychar, yytname[yychar1]);
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ if (yylen == 1)
+ fprintf (stderr, "Reducing 1 value via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+ else
+ fprintf (stderr, "Reducing %d values via rule %d (line %d), ",
+ yylen, yyn, yyrline[yyn]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 2:
+#line 266 "pic.y"
+{
+ if (olist.head)
+ print_picture(olist.head);
+ ;
+ break;}
+case 3:
+#line 275 "pic.y"
+{ yyval.pl = yyvsp[-1].pl; ;
+ break;}
+case 4:
+#line 280 "pic.y"
+{ yyval.pl = yyvsp[0].pl; ;
+ break;}
+case 5:
+#line 282 "pic.y"
+{ yyval.pl = yyvsp[-2].pl; ;
+ break;}
+case 10:
+#line 297 "pic.y"
+{
+ define_variable(yyvsp[-2].str, yyvsp[0].x);
+ delete yyvsp[-2].str;
+ ;
+ break;}
+case 11:
+#line 302 "pic.y"
+{ current_direction = UP_DIRECTION; ;
+ break;}
+case 12:
+#line 304 "pic.y"
+{ current_direction = DOWN_DIRECTION; ;
+ break;}
+case 13:
+#line 306 "pic.y"
+{ current_direction = LEFT_DIRECTION; ;
+ break;}
+case 14:
+#line 308 "pic.y"
+{ current_direction = RIGHT_DIRECTION; ;
+ break;}
+case 15:
+#line 310 "pic.y"
+{
+ olist.append(make_command_object(yyvsp[0].lstr.str,
+ yyvsp[0].lstr.filename,
+ yyvsp[0].lstr.lineno));
+ ;
+ break;}
+case 16:
+#line 316 "pic.y"
+{
+ fprintf(stderr, "%g\n", yyvsp[0].x);
+ fflush(stderr);
+ ;
+ break;}
+case 17:
+#line 321 "pic.y"
+{
+ fprintf(stderr, "%s\n", yyvsp[0].lstr.str);
+ delete yyvsp[0].lstr.str;
+ fflush(stderr);
+ ;
+ break;}
+case 18:
+#line 327 "pic.y"
+{
+ fprintf(stderr, "%g, %g\n", yyvsp[0].pair.x, yyvsp[0].pair.y);
+ fflush(stderr);
+ ;
+ break;}
+case 19:
+#line 332 "pic.y"
+{ delim_flag = 1; ;
+ break;}
+case 20:
+#line 334 "pic.y"
+{
+ delim_flag = 0;
+ system(yyvsp[0].str);
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 21:
+#line 340 "pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ do_copy(yyvsp[0].lstr.str);
+ // do not delete the filename
+ ;
+ break;}
+case 22:
+#line 347 "pic.y"
+{ delim_flag = 2; ;
+ break;}
+case 23:
+#line 349 "pic.y"
+{ delim_flag = 0; ;
+ break;}
+case 24:
+#line 351 "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
+ delete yyvsp[-2].str;
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 25:
+#line 360 "pic.y"
+{ delim_flag = 2; ;
+ break;}
+case 26:
+#line 362 "pic.y"
+{ delim_flag = 0; ;
+ break;}
+case 27:
+#line 364 "pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ copy_rest_thru(yyvsp[-2].str, yyvsp[0].str);
+ delete yyvsp[-2].str;
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 28:
+#line 372 "pic.y"
+{ delim_flag = 1; ;
+ break;}
+case 29:
+#line 374 "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 381 "pic.y"
+{
+ if (yychar < 0)
+ do_lookahead();
+ if (yyvsp[0].if_data.x != 0.0)
+ push_body(yyvsp[0].if_data.body);
+ delete yyvsp[0].if_data.body;
+ ;
+ break;}
+case 31:
+#line 389 "pic.y"
+{ delim_flag = 1; ;
+ break;}
+case 32:
+#line 391 "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);
+ delete yyvsp[-3].if_data.body;
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 34:
+#line 404 "pic.y"
+{ define_variable("scale", 1.0); ;
+ break;}
+case 35:
+#line 409 "pic.y"
+{ reset(yyvsp[0].str); delete yyvsp[0].str; ;
+ break;}
+case 36:
+#line 411 "pic.y"
+{ reset(yyvsp[0].str); delete yyvsp[0].str; ;
+ break;}
+case 37:
+#line 413 "pic.y"
+{ reset(yyvsp[0].str); delete yyvsp[0].str; ;
+ break;}
+case 38:
+#line 418 "pic.y"
+{ delim_flag = 1; ;
+ break;}
+case 39:
+#line 420 "pic.y"
+{ delim_flag = 0; yyval.if_data.x = yyvsp[-3].x; yyval.if_data.body = yyvsp[0].str; ;
+ break;}
+case 40:
+#line 425 "pic.y"
+{ yyval.str = 0; ;
+ break;}
+case 41:
+#line 427 "pic.y"
+{ yyval.str = yyvsp[0].lstr.str; ;
+ break;}
+case 42:
+#line 432 "pic.y"
+{ yyval.x = yyvsp[0].x; ;
+ break;}
+case 43:
+#line 434 "pic.y"
+{
+ yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) == 0;
+ delete yyvsp[-2].lstr.str;
+ delete yyvsp[0].lstr.str;
+ ;
+ break;}
+case 44:
+#line 440 "pic.y"
+{
+ yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) != 0;
+ delete yyvsp[-2].lstr.str;
+ delete yyvsp[0].lstr.str;
+ ;
+ break;}
+case 45:
+#line 448 "pic.y"
+{ yyval.by.val = 1.0; yyval.by.is_multiplicative = 0; ;
+ break;}
+case 46:
+#line 450 "pic.y"
+{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 0; ;
+ break;}
+case 47:
+#line 452 "pic.y"
+{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 1; ;
+ break;}
+case 48:
+#line 457 "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 49:
+#line 471 "pic.y"
+{ yyval.pl = yyvsp[0].pl; define_label(yyvsp[-3].str, & yyval.pl); delete yyvsp[-3].str; ;
+ break;}
+case 50:
+#line 473 "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);
+ delete yyvsp[-3].str;
+ ;
+ break;}
+case 51:
+#line 481 "pic.y"
+{
+ yyval.pl = yyvsp[0].pl;
+ define_label(yyvsp[-3].str, & yyval.pl);
+ delete yyvsp[-3].str;
+ ;
+ break;}
+case 52:
+#line 487 "pic.y"
+{
+ yyval.state.x = current_position.x;
+ yyval.state.y = current_position.y;
+ yyval.state.dir = current_direction;
+ ;
+ break;}
+case 53:
+#line 493 "pic.y"
+{
+ current_position.x = yyvsp[-2].state.x;
+ current_position.y = yyvsp[-2].state.y;
+ current_direction = yyvsp[-2].state.dir;
+ yyval.pl = yyvsp[-1].pl;
+ ;
+ break;}
+case 54:
+#line 500 "pic.y"
+{
+ yyval.pl.obj = 0;
+ yyval.pl.x = current_position.x;
+ yyval.pl.y = current_position.y;
+ ;
+ break;}
+case 55:
+#line 509 "pic.y"
+{
+ yyval.spec = new object_spec(BOX_OBJECT);
+ ;
+ break;}
+case 56:
+#line 513 "pic.y"
+{
+ yyval.spec = new object_spec(CIRCLE_OBJECT);
+ ;
+ break;}
+case 57:
+#line 517 "pic.y"
+{
+ yyval.spec = new object_spec(ELLIPSE_OBJECT);
+ ;
+ break;}
+case 58:
+#line 521 "pic.y"
+{
+ yyval.spec = new object_spec(ARC_OBJECT);
+ yyval.spec->dir = current_direction;
+ ;
+ break;}
+case 59:
+#line 526 "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 60:
+#line 533 "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 61:
+#line 540 "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 62:
+#line 547 "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 63:
+#line 554 "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 64:
+#line 559 "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 65:
+#line 564 "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);
+ delete yyvsp[0].lstr.str;
+ ;
+ break;}
+case 66:
+#line 571 "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 67:
+#line 586 "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 68:
+#line 598 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->height = yyvsp[0].x;
+ yyval.spec->flags |= HAS_HEIGHT;
+ ;
+ break;}
+case 69:
+#line 604 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->radius = yyvsp[0].x;
+ yyval.spec->flags |= HAS_RADIUS;
+ ;
+ break;}
+case 70:
+#line 610 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->width = yyvsp[0].x;
+ yyval.spec->flags |= HAS_WIDTH;
+ ;
+ break;}
+case 71:
+#line 616 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->radius = yyvsp[0].x/2.0;
+ yyval.spec->flags |= HAS_RADIUS;
+ ;
+ break;}
+case 72:
+#line 622 "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 73:
+#line 641 "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 74:
+#line 648 "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 75:
+#line 655 "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 76:
+#line 662 "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 77:
+#line 669 "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 78:
+#line 676 "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 79:
+#line 683 "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 80:
+#line 690 "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 81:
+#line 697 "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 82:
+#line 704 "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 83:
+#line 719 "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 84:
+#line 731 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_WITH;
+ yyval.spec->with = yyvsp[0].pth;
+ ;
+ break;}
+case 85:
+#line 737 "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 86:
+#line 744 "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 87:
+#line 756 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DOTTED;
+ lookup_variable("dashwid", & yyval.spec->dash_width);
+ ;
+ break;}
+case 88:
+#line 762 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_DOTTED;
+ yyval.spec->dash_width = yyvsp[0].x;
+ ;
+ break;}
+case 89:
+#line 768 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DASHED;
+ lookup_variable("dashwid", & yyval.spec->dash_width);
+ ;
+ break;}
+case 90:
+#line 774 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_DASHED;
+ yyval.spec->dash_width = yyvsp[0].x;
+ ;
+ break;}
+case 91:
+#line 780 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_DEFAULT_FILLED;
+ ;
+ break;}
+case 92:
+#line 785 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= IS_FILLED;
+ yyval.spec->fill = yyvsp[0].x;
+ ;
+ break;}
+case 93:
+#line 791 "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 94:
+#line 807 "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 95:
+#line 824 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_SAME;
+ ;
+ break;}
+case 96:
+#line 829 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_INVISIBLE;
+ ;
+ break;}
+case 97:
+#line 834 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= HAS_LEFT_ARROW_HEAD;
+ ;
+ break;}
+case 98:
+#line 839 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= HAS_RIGHT_ARROW_HEAD;
+ ;
+ break;}
+case 99:
+#line 844 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
+ ;
+ break;}
+case 100:
+#line 849 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_CLOCKWISE;
+ ;
+ break;}
+case 101:
+#line 854 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags &= ~IS_CLOCKWISE;
+ ;
+ break;}
+case 102:
+#line 859 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ for (text_item **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 103:
+#line 866 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ for (text_item *p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.h = LEFT_ADJUST;
+ }
+ ;
+ break;}
+case 104:
+#line 875 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ for (text_item *p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.h = RIGHT_ADJUST;
+ }
+ ;
+ break;}
+case 105:
+#line 884 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ for (text_item *p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.v = ABOVE_ADJUST;
+ }
+ ;
+ break;}
+case 106:
+#line 893 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ if (yyval.spec->text) {
+ for (text_item *p = yyval.spec->text; p->next; p = p->next)
+ ;
+ p->adj.v = BELOW_ADJUST;
+ }
+ ;
+ break;}
+case 107:
+#line 902 "pic.y"
+{
+ yyval.spec = yyvsp[-2].spec;
+ yyval.spec->flags |= HAS_THICKNESS;
+ yyval.spec->thickness = yyvsp[0].x;
+ ;
+ break;}
+case 108:
+#line 908 "pic.y"
+{
+ yyval.spec = yyvsp[-1].spec;
+ yyval.spec->flags |= IS_ALIGNED;
+ ;
+ break;}
+case 109:
+#line 916 "pic.y"
+{
+ yyval.lstr = yyvsp[0].lstr;
+ ;
+ break;}
+case 110:
+#line 920 "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);
+ delete yyvsp[-1].dv.v;
+ delete yyvsp[-2].lstr.str;
+ ;
+ break;}
+case 111:
+#line 931 "pic.y"
+{
+ yyval.dv.v = 0;
+ yyval.dv.nv = 0;
+ yyval.dv.maxv = 0;
+ ;
+ break;}
+case 112:
+#line 937 "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));
+ delete oldv;
+ }
+ }
+ yyval.dv.v[yyval.dv.nv] = yyvsp[0].x;
+ yyval.dv.nv += 1;
+ ;
+ break;}
+case 113:
+#line 959 "pic.y"
+{ yyval.pair = yyvsp[0].pair; ;
+ break;}
+case 114:
+#line 961 "pic.y"
+{
+ position pos = yyvsp[0].pl;
+ yyval.pair.x = pos.x;
+ yyval.pair.y = pos.y;
+ ;
+ break;}
+case 115:
+#line 970 "pic.y"
+{ yyval.pair = yyvsp[0].pair; ;
+ break;}
+case 116:
+#line 972 "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 117:
+#line 977 "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 118:
+#line 982 "pic.y"
+{
+ yyval.pair.x = yyvsp[-3].pair.x;
+ yyval.pair.y = yyvsp[-1].pair.y;
+ ;
+ break;}
+case 119:
+#line 987 "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 120:
+#line 992 "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 123:
+#line 1005 "pic.y"
+{ yyval.pair.x = yyvsp[-2].x; yyval.pair.y = yyvsp[0].x; ;
+ break;}
+case 124:
+#line 1007 "pic.y"
+{ yyval.pair = yyvsp[-1].pair; ;
+ break;}
+case 125:
+#line 1012 "pic.y"
+{ yyval.pl = yyvsp[0].pl; ;
+ break;}
+case 126:
+#line 1014 "pic.y"
+{
+ path pth(yyvsp[0].crn);
+ if (!pth.follow(yyvsp[-1].pl, & yyval.pl))
+ YYABORT;
+ ;
+ break;}
+case 127:
+#line 1020 "pic.y"
+{
+ path pth(yyvsp[-1].crn);
+ if (!pth.follow(yyvsp[0].pl, & yyval.pl))
+ YYABORT;
+ ;
+ break;}
+case 128:
+#line 1026 "pic.y"
+{
+ path pth(yyvsp[-2].crn);
+ if (!pth.follow(yyvsp[0].pl, & yyval.pl))
+ YYABORT;
+ ;
+ break;}
+case 129:
+#line 1032 "pic.y"
+{
+ yyval.pl.x = current_position.x;
+ yyval.pl.y = current_position.y;
+ yyval.pl.obj = 0;
+ ;
+ break;}
+case 130:
+#line 1041 "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;
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 131:
+#line 1051 "pic.y"
+{
+ yyval.pl.obj = yyvsp[0].obj;
+ ;
+ break;}
+case 132:
+#line 1055 "pic.y"
+{
+ path pth(yyvsp[0].str);
+ if (!pth.follow(yyvsp[-2].pl, & yyval.pl))
+ YYABORT;
+ ;
+ break;}
+case 133:
+#line 1064 "pic.y"
+{ yyval.n = 1; ;
+ break;}
+case 134:
+#line 1066 "pic.y"
+{ yyval.n = yyvsp[-1].n; ;
+ break;}
+case 135:
+#line 1071 "pic.y"
+{
+ int count = 0;
+ for (object *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 136:
+#line 1085 "pic.y"
+{
+ int count = 0;
+ for (object *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 137:
+#line 1102 "pic.y"
+{ yyval.obtype = BOX_OBJECT; ;
+ break;}
+case 138:
+#line 1104 "pic.y"
+{ yyval.obtype = CIRCLE_OBJECT; ;
+ break;}
+case 139:
+#line 1106 "pic.y"
+{ yyval.obtype = ELLIPSE_OBJECT; ;
+ break;}
+case 140:
+#line 1108 "pic.y"
+{ yyval.obtype = ARC_OBJECT; ;
+ break;}
+case 141:
+#line 1110 "pic.y"
+{ yyval.obtype = LINE_OBJECT; ;
+ break;}
+case 142:
+#line 1112 "pic.y"
+{ yyval.obtype = ARROW_OBJECT; ;
+ break;}
+case 143:
+#line 1114 "pic.y"
+{ yyval.obtype = SPLINE_OBJECT; ;
+ break;}
+case 144:
+#line 1116 "pic.y"
+{ yyval.obtype = BLOCK_OBJECT; ;
+ break;}
+case 145:
+#line 1118 "pic.y"
+{ yyval.obtype = TEXT_OBJECT; ;
+ break;}
+case 146:
+#line 1123 "pic.y"
+{
+ yyval.pth = new path(yyvsp[0].str);
+ ;
+ break;}
+case 147:
+#line 1127 "pic.y"
+{
+ yyval.pth = yyvsp[-2].pth;
+ yyval.pth->append(yyvsp[0].str);
+ ;
+ break;}
+case 148:
+#line 1135 "pic.y"
+{
+ yyval.pth = new path(yyvsp[0].crn);
+ ;
+ break;}
+case 149:
+#line 1142 "pic.y"
+{
+ yyval.pth = yyvsp[0].pth;
+ ;
+ break;}
+case 150:
+#line 1146 "pic.y"
+{
+ yyval.pth = yyvsp[-1].pth;
+ yyval.pth->append(yyvsp[0].crn);
+ ;
+ break;}
+case 151:
+#line 1154 "pic.y"
+{
+ yyval.pth = yyvsp[0].pth;
+ ;
+ break;}
+case 152:
+#line 1159 "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 153:
+#line 1165 "pic.y"
+{
+ lex_warning("`last %1' in `with' argument ignored",
+ object_type_name(yyvsp[-1].obtype));
+ yyval.pth = yyvsp[0].pth;
+ ;
+ break;}
+case 154:
+#line 1171 "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 155:
+#line 1177 "pic.y"
+{
+ lex_warning("initial `%1' in `with' argument ignored", yyvsp[-1].str);
+ delete yyvsp[-1].str;
+ yyval.pth = yyvsp[0].pth;
+ ;
+ break;}
+case 156:
+#line 1186 "pic.y"
+{ yyval.crn = &object::north; ;
+ break;}
+case 157:
+#line 1188 "pic.y"
+{ yyval.crn = &object::east; ;
+ break;}
+case 158:
+#line 1190 "pic.y"
+{ yyval.crn = &object::west; ;
+ break;}
+case 159:
+#line 1192 "pic.y"
+{ yyval.crn = &object::south; ;
+ break;}
+case 160:
+#line 1194 "pic.y"
+{ yyval.crn = &object::north_east; ;
+ break;}
+case 161:
+#line 1196 "pic.y"
+{ yyval.crn = &object:: south_east; ;
+ break;}
+case 162:
+#line 1198 "pic.y"
+{ yyval.crn = &object::north_west; ;
+ break;}
+case 163:
+#line 1200 "pic.y"
+{ yyval.crn = &object::south_west; ;
+ break;}
+case 164:
+#line 1202 "pic.y"
+{ yyval.crn = &object::center; ;
+ break;}
+case 165:
+#line 1204 "pic.y"
+{ yyval.crn = &object::start; ;
+ break;}
+case 166:
+#line 1206 "pic.y"
+{ yyval.crn = &object::end; ;
+ break;}
+case 167:
+#line 1208 "pic.y"
+{ yyval.crn = &object::north; ;
+ break;}
+case 168:
+#line 1210 "pic.y"
+{ yyval.crn = &object::south; ;
+ break;}
+case 169:
+#line 1212 "pic.y"
+{ yyval.crn = &object::west; ;
+ break;}
+case 170:
+#line 1214 "pic.y"
+{ yyval.crn = &object::east; ;
+ break;}
+case 171:
+#line 1216 "pic.y"
+{ yyval.crn = &object::north_west; ;
+ break;}
+case 172:
+#line 1218 "pic.y"
+{ yyval.crn = &object::south_west; ;
+ break;}
+case 173:
+#line 1220 "pic.y"
+{ yyval.crn = &object::north_east; ;
+ break;}
+case 174:
+#line 1222 "pic.y"
+{ yyval.crn = &object::south_east; ;
+ break;}
+case 175:
+#line 1224 "pic.y"
+{ yyval.crn = &object::west; ;
+ break;}
+case 176:
+#line 1226 "pic.y"
+{ yyval.crn = &object::east; ;
+ break;}
+case 177:
+#line 1228 "pic.y"
+{ yyval.crn = &object::north_west; ;
+ break;}
+case 178:
+#line 1230 "pic.y"
+{ yyval.crn = &object::south_west; ;
+ break;}
+case 179:
+#line 1232 "pic.y"
+{ yyval.crn = &object::north_east; ;
+ break;}
+case 180:
+#line 1234 "pic.y"
+{ yyval.crn = &object::south_east; ;
+ break;}
+case 181:
+#line 1236 "pic.y"
+{ yyval.crn = &object::center; ;
+ break;}
+case 182:
+#line 1238 "pic.y"
+{ yyval.crn = &object::start; ;
+ break;}
+case 183:
+#line 1240 "pic.y"
+{ yyval.crn = &object::end; ;
+ break;}
+case 184:
+#line 1245 "pic.y"
+{
+ if (!lookup_variable(yyvsp[0].str, & yyval.x)) {
+ lex_error("there is no variable `%1'", yyvsp[0].str);
+ YYABORT;
+ }
+ delete yyvsp[0].str;
+ ;
+ break;}
+case 185:
+#line 1253 "pic.y"
+{ yyval.x = yyvsp[0].x; ;
+ break;}
+case 186:
+#line 1255 "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 187:
+#line 1262 "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 188:
+#line 1269 "pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->height();
+ else
+ yyval.x = 0.0;
+ ;
+ break;}
+case 189:
+#line 1276 "pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->width();
+ else
+ yyval.x = 0.0;
+ ;
+ break;}
+case 190:
+#line 1283 "pic.y"
+{
+ if (yyvsp[-1].pl.obj != 0)
+ yyval.x = yyvsp[-1].pl.obj->radius();
+ else
+ yyval.x = 0.0;
+ ;
+ break;}
+case 191:
+#line 1290 "pic.y"
+{ yyval.x = yyvsp[-2].x + yyvsp[0].x; ;
+ break;}
+case 192:
+#line 1292 "pic.y"
+{ yyval.x = yyvsp[-2].x - yyvsp[0].x; ;
+ break;}
+case 193:
+#line 1294 "pic.y"
+{ yyval.x = yyvsp[-2].x * yyvsp[0].x; ;
+ break;}
+case 194:
+#line 1296 "pic.y"
+{
+ if (yyvsp[0].x == 0.0) {
+ lex_error("division by zero");
+ YYABORT;
+ }
+ yyval.x = yyvsp[-2].x/yyvsp[0].x;
+ ;
+ break;}
+case 195:
+#line 1304 "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 196:
+#line 1312 "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 197:
+#line 1325 "pic.y"
+{ yyval.x = -yyvsp[0].x; ;
+ break;}
+case 198:
+#line 1327 "pic.y"
+{ yyval.x = yyvsp[-1].x; ;
+ break;}
+case 199:
+#line 1329 "pic.y"
+{
+ errno = 0;
+ yyval.x = sin(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("sin result out of range");
+ YYABORT;
+ }
+ ;
+ break;}
+case 200:
+#line 1338 "pic.y"
+{
+ errno = 0;
+ yyval.x = cos(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("cos result out of range");
+ YYABORT;
+ }
+ ;
+ break;}
+case 201:
+#line 1347 "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 202:
+#line 1360 "pic.y"
+{
+ errno = 0;
+ yyval.x = log10(yyvsp[-1].x);
+ if (errno == ERANGE) {
+ lex_error("log result out of range");
+ YYABORT;
+ }
+ ;
+ break;}
+case 203:
+#line 1369 "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 204:
+#line 1378 "pic.y"
+{
+ errno = 0;
+ yyval.x = sqrt(yyvsp[-1].x);
+ if (errno == EDOM) {
+ lex_error("sqrt argument out of domain");
+ YYABORT;
+ }
+ ;
+ break;}
+case 205:
+#line 1387 "pic.y"
+{ yyval.x = yyvsp[-3].x > yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; ;
+ break;}
+case 206:
+#line 1389 "pic.y"
+{ yyval.x = yyvsp[-3].x > yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; ;
+ break;}
+case 207:
+#line 1391 "pic.y"
+{ yyval.x = floor(yyvsp[-1].x); ;
+ break;}
+case 208:
+#line 1393 "pic.y"
+{ yyval.x = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*yyvsp[-1].x); ;
+ break;}
+case 209:
+#line 1395 "pic.y"
+{
+ /* return a random number in the range [0,1) */
+ /* portable, but not very random */
+ yyval.x = (rand() & 0x7fff) / double(0x8000);
+ ;
+ break;}
+case 210:
+#line 1401 "pic.y"
+{ yyval.x = (yyvsp[-2].x < yyvsp[0].x); ;
+ break;}
+case 211:
+#line 1403 "pic.y"
+{ yyval.x = (yyvsp[-2].x <= yyvsp[0].x); ;
+ break;}
+case 212:
+#line 1405 "pic.y"
+{ yyval.x = (yyvsp[-2].x > yyvsp[0].x); ;
+ break;}
+case 213:
+#line 1407 "pic.y"
+{ yyval.x = (yyvsp[-2].x >= yyvsp[0].x); ;
+ break;}
+case 214:
+#line 1409 "pic.y"
+{ yyval.x = (yyvsp[-2].x == yyvsp[0].x); ;
+ break;}
+case 215:
+#line 1411 "pic.y"
+{ yyval.x = (yyvsp[-2].x != yyvsp[0].x); ;
+ break;}
+case 216:
+#line 1413 "pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); ;
+ break;}
+case 217:
+#line 1415 "pic.y"
+{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); ;
+ break;}
+case 218:
+#line 1417 "pic.y"
+{ yyval.x = (yyvsp[0].x == 0.0); ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 362 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) xmalloc(size + 15);
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 1421 "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());
+}
diff --git a/pic/pic.tab.h b/pic/pic.tab.h
new file mode 100644
index 000000000..33af90e0e
--- /dev/null
+++ b/pic/pic.tab.h
@@ -0,0 +1,129 @@
+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;
+#define LABEL 258
+#define VARIABLE 259
+#define NUMBER 260
+#define TEXT 261
+#define COMMAND 262
+#define DELIMITED 263
+#define ORDINAL 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 DOTTED 291
+#define DASHED 292
+#define CHOP 293
+#define SAME 294
+#define INVISIBLE 295
+#define LJUST 296
+#define RJUST 297
+#define ABOVE 298
+#define BELOW 299
+#define OF 300
+#define THE 301
+#define WAY 302
+#define BETWEEN 303
+#define AND 304
+#define HERE 305
+#define DOT_N 306
+#define DOT_E 307
+#define DOT_W 308
+#define DOT_S 309
+#define DOT_NE 310
+#define DOT_SE 311
+#define DOT_NW 312
+#define DOT_SW 313
+#define DOT_C 314
+#define DOT_START 315
+#define DOT_END 316
+#define DOT_X 317
+#define DOT_Y 318
+#define DOT_HT 319
+#define DOT_WID 320
+#define DOT_RAD 321
+#define SIN 322
+#define COS 323
+#define ATAN2 324
+#define LOG 325
+#define EXP 326
+#define SQRT 327
+#define MAX 328
+#define MIN 329
+#define INT 330
+#define RAND 331
+#define COPY 332
+#define THRU 333
+#define TOP 334
+#define BOTTOM 335
+#define UPPER 336
+#define LOWER 337
+#define SH 338
+#define PRINT 339
+#define CW 340
+#define CCW 341
+#define FOR 342
+#define DO 343
+#define IF 344
+#define ELSE 345
+#define ANDAND 346
+#define OROR 347
+#define NOTEQUAL 348
+#define EQUALEQUAL 349
+#define LESSEQUAL 350
+#define GREATEREQUAL 351
+#define LEFT_CORNER 352
+#define RIGHT_CORNER 353
+#define CENTER 354
+#define END 355
+#define START 356
+#define RESET 357
+#define UNTIL 358
+#define PLOT 359
+#define THICKNESS 360
+#define FILL 361
+#define ALIGNED 362
+#define SPRINTF 363
+#define DEFINE 364
+#define UNDEF 365
+
+
+extern YYSTYPE yylval;
diff --git a/pic/pic.y b/pic/pic.y
new file mode 100644
index 000000000..e82a35468
--- /dev/null
+++ b/pic/pic.y
@@ -0,0 +1,1688 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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();
+
+extern "C" {
+ double fmod(double, double);
+ int rand();
+}
+
+#define YYDEBUG 1
+
+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
+%token <str> DELIMITED
+%token <n> ORDINAL
+%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 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 MAX
+%token MIN
+%token INT
+%token RAND
+%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 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 DASHED DOTTED UP DOWN FILL
+%left LABEL
+
+%left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT MAX MIN INT RAND 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 conditional_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
+%type <str> until
+%type <dv> sprintf_args
+%type <lstr> text
+
+%%
+
+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 '=' conditional_expr
+ {
+ define_variable($1, $3);
+ delete $1;
+ }
+ | UP
+ { current_direction = UP_DIRECTION; }
+ | DOWN
+ { current_direction = DOWN_DIRECTION; }
+ | LEFT
+ { current_direction = LEFT_DIRECTION; }
+ | RIGHT
+ { current_direction = RIGHT_DIRECTION; }
+ | COMMAND
+ {
+ olist.append(make_command_object($1.str,
+ $1.filename,
+ $1.lineno));
+ }
+ | PRINT expr
+ {
+ fprintf(stderr, "%g\n", $2);
+ fflush(stderr);
+ }
+ | PRINT text
+ {
+ fprintf(stderr, "%s\n", $2.str);
+ delete $2.str;
+ fflush(stderr);
+ }
+ | PRINT position
+ {
+ fprintf(stderr, "%g, %g\n", $2.x, $2.y);
+ fflush(stderr);
+ }
+ | SH
+ { delim_flag = 1; }
+ DELIMITED
+ {
+ delim_flag = 0;
+ system($3);
+ 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
+ delete $5;
+ delete $7;
+ }
+ | COPY THRU
+ { delim_flag = 2; }
+ DELIMITED
+ { delim_flag = 0; }
+ until
+ {
+ if (yychar < 0)
+ do_lookahead();
+ copy_rest_thru($4, $6);
+ delete $4;
+ 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);
+ 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);
+ delete $1.body;
+ delete $4;
+ }
+ | reset_variables
+ | RESET
+ { define_variable("scale", 1.0); }
+ ;
+
+reset_variables:
+ RESET VARIABLE
+ { reset($2); delete $2; }
+ | reset_variables VARIABLE
+ { reset($2); delete $2; }
+ | reset_variables ',' VARIABLE
+ { reset($3); delete $3; }
+ ;
+
+simple_if:
+ IF conditional_expr THEN
+ { delim_flag = 1; }
+ DELIMITED
+ { delim_flag = 0; $$.x = $2; $$.body = $5; }
+ ;
+
+until:
+ /* empty */
+ { $$ = 0; }
+ | UNTIL TEXT
+ { $$ = $2.str; }
+ ;
+
+conditional_expr:
+ expr
+ { $$ = $1; }
+ | text EQUALEQUAL text
+ {
+ $$ = strcmp($1.str, $3.str) == 0;
+ delete $1.str;
+ delete $3.str;
+ }
+ | text NOTEQUAL text
+ {
+ $$ = strcmp($1.str, $3.str) != 0;
+ delete $1.str;
+ delete $3.str;
+ }
+
+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, & $$); delete $1; }
+ | LABEL ':' optional_separator position_not_place
+ {
+ $$.obj = 0;
+ $$.x = $4.x;
+ $$.y = $4.y;
+ define_label($1, & $$);
+ delete $1;
+ }
+ | LABEL ':' optional_separator place
+ {
+ $$ = $4;
+ define_label($1, & $$);
+ 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;
+ $$ = $3;
+ }
+ | placeless_element
+ {
+ $$.obj = 0;
+ $$.x = current_position.x;
+ $$.y = current_position.y;
+ }
+ ;
+
+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);
+ 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 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;
+ for (text_item **p = & $$->text; *p; p = &(*p)->next)
+ ;
+ *p = new text_item($2.str, $2.filename, $2.lineno);
+ }
+ | object_spec LJUST
+ {
+ $$ = $1;
+ if ($$->text) {
+ for (text_item *p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.h = LEFT_ADJUST;
+ }
+ }
+ | object_spec RJUST
+ {
+ $$ = $1;
+ if ($$->text) {
+ for (text_item *p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.h = RIGHT_ADJUST;
+ }
+ }
+ | object_spec ABOVE
+ {
+ $$ = $1;
+ if ($$->text) {
+ for (text_item *p = $$->text; p->next; p = p->next)
+ ;
+ p->adj.v = ABOVE_ADJUST;
+ }
+ }
+ | object_spec BELOW
+ {
+ $$ = $1;
+ if ($$->text) {
+ for (text_item *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);
+ delete $4.v;
+ 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));
+ 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;
+ delete $1;
+ }
+ | nth_primitive
+ {
+ $$.obj = $1;
+ }
+ | label '.' LABEL
+ {
+ path pth($3);
+ if (!pth.follow($1, & $$))
+ YYABORT;
+ }
+ ;
+
+optional_ordinal_last:
+ LAST
+ { $$ = 1; }
+ | ORDINAL LAST
+ { $$ = $1; }
+ ;
+
+nth_primitive:
+ ORDINAL object_type
+ {
+ int count = 0;
+ for (object *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;
+ for (object *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;
+ }
+ /* 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);
+ 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;
+ }
+ 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; }
+ | '(' expr ')'
+ { $$ = $2; }
+ | SIN '(' expr ')'
+ {
+ errno = 0;
+ $$ = sin($3);
+ if (errno == ERANGE) {
+ lex_error("sin result out of range");
+ YYABORT;
+ }
+ }
+ | COS '(' expr ')'
+ {
+ errno = 0;
+ $$ = cos($3);
+ if (errno == ERANGE) {
+ lex_error("cos result out of range");
+ YYABORT;
+ }
+ }
+ | ATAN2 '(' expr ',' 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 '(' expr ')'
+ {
+ errno = 0;
+ $$ = log10($3);
+ if (errno == ERANGE) {
+ lex_error("log result out of range");
+ YYABORT;
+ }
+ }
+ | EXP '(' expr ')'
+ {
+ errno = 0;
+ $$ = pow(10.0, $3);
+ if (errno == ERANGE) {
+ lex_error("exp result out of range");
+ YYABORT;
+ }
+ }
+ | SQRT '(' expr ')'
+ {
+ errno = 0;
+ $$ = sqrt($3);
+ if (errno == EDOM) {
+ lex_error("sqrt argument out of domain");
+ YYABORT;
+ }
+ }
+ | MAX '(' expr ',' expr ')'
+ { $$ = $3 > $5 ? $3 : $5; }
+ | MIN '(' expr ',' expr ')'
+ { $$ = $3 > $5 ? $3 : $5; }
+ | INT '(' expr ')'
+ { $$ = floor($3); }
+ | RAND '(' 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);
+ }
+ | 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/pic/position.h b/pic/position.h
new file mode 100644
index 000000000..418f98caa
--- /dev/null
+++ b/pic/position.h
@@ -0,0 +1,47 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/pic/tex.c b/pic/tex.c
new file mode 100644
index 000000000..bd1d04997
--- /dev/null
+++ b/pic/tex.c
@@ -0,0 +1,411 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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) ? 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/pic/text.h b/pic/text.h
new file mode 100644
index 000000000..f9d348750
--- /dev/null
+++ b/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/pic/troff.c b/pic/troff.c
new file mode 100644
index 000000000..6af66dd6b
--- /dev/null
+++ b/pic/troff.c
@@ -0,0 +1,524 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "pic.h"
+#include "common.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 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;
+ 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");
+}
+
+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(".rr " EQN_NO_EXTRA_SPACE_REG "\n");
+ // this is a little gross
+ set_location(current_filename, current_lineno);
+ fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
+}
+
+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 v = temp - pos;
+ pos = temp;
+ if (i != 0)
+ putchar(' ');
+ printf("%.3fi %.3fi", v.x, v.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 v = temp - pos;
+ pos = temp;
+ if (i != 1)
+ putchar(' ');
+ printf("%.3fi %.3fi", v.x, v.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)
+{
+ 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);
+ }
+}
+
+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 (zero_length_line_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;
+ }
+}
+
+class grops_output : public troff_output {
+public:
+ grops_output();
+ ~grops_output();
+ void text(const position &, text_piece *, int, double);
+};
+
+grops_output::grops_output()
+{
+}
+
+grops_output::~grops_output()
+{
+}
+
+output *make_grops_output()
+{
+ return new grops_output;
+}
+
+void grops_output::text(const position &center, text_piece *v, int n,
+ double ang)
+{
+ int rotate_flag = 0;
+ if (ang != 0.0) {
+ rotate_flag = 1;
+ position c = transform(center);
+#if 0
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\X'ps: rotate %.4f'"
+ "\n.sp -1\n",
+ c.x, c.y, -ang*180.0/M_PI);
+#else
+ printf("\\h'%.3fi'"
+ "\\v'%.3fi'"
+ "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
+ "\n.sp -1\n",
+ c.x, c.y, -ang*180.0/M_PI);
+#endif
+ }
+ troff_output::text(center, v, n, ang);
+ if (rotate_flag)
+#if 0
+ printf("\\X'ps: end rotate'\n.sp -1\n");
+#else
+ printf("\\X'ps: exec grestore'\n.sp -1\n");
+#endif
+}
diff --git a/ps/Makefile b/ps/Makefile
new file mode 100644
index 000000000..069599ce6
--- /dev/null
+++ b/ps/Makefile
@@ -0,0 +1,115 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# define PAGE to be letter if your PostScript printer uses 8.5x11 paper (USA)
+# and define it to be A4, if it uses A4 paper (rest of the world)
+PAGE=A4
+#PAGE=letter
+BINDIR=/usr/local/bin
+CC=g++
+BROKEN_SPOOLER_FLAGS=0
+CFLAGS=-g
+OLDCC=gcc
+OLDCFLAGS=-g
+MLIBS=-lm
+INCLUDES=-I../driver -I../lib
+LDFLAGS=-g
+OBJECTS=ps.o psrm.o
+SOURCES=ps.c psrm.c ps.h
+MISC=Makefile devgps
+BINDIR=/usr/local/bin
+FONTDIR=/usr/local/lib/groff/font
+MACRODIR=/usr/local/lib/groff/tmac
+ETAGS=etags
+ETAGSFLAGS=-p
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: grops psbb pfbtops devps
+
+grops: $(OBJECTS) ../driver/libdriver.a ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) \
+ ../driver/libdriver.a ../lib/libgroff.a $(MLIBS)
+
+ps.o: broken.h ps.h ../driver/printer.h ../driver/driver.h ../lib/font.h \
+ ../lib/stringclass.h ../lib/cset.h
+
+psrm.o: ps.h ../driver/printer.h ../driver/driver.h ../lib/font.h \
+ ../lib/stringclass.h ../lib/cset.h
+
+broken.h: FORCE
+ @echo \#define BROKEN_SPOOLER_FLAGS $(BROKEN_SPOOLER_FLAGS) >broken.h.n
+ @cmp -s broken.h broken.h.n || cp broken.h.n broken.h
+ @rm -f broken.h.n
+
+psbb: psbb.o
+ $(OLDCC) $(LDFLAGS) -o $@ psbb.o
+
+psbb.o: psbb.c
+ $(OLDCC) $(OLDCFLAGS) -c psbb.c
+
+pfbtops: pfbtops.o
+ $(OLDCC) $(LDFLAGS) -o $@ pfbtops.o
+
+pfbtops.o: pfbtops.c
+ $(OLDCC) $(OLDCFLAGS) -c pfbtops.c
+
+saber_grops:
+ #load $(INCLUDES) $(CFLAGS) $(DEFINES) ps.c psrm.c \
+ ../driver/libdriver.a ../lib/libgroff.a -lm
+
+install.bin: grops psbb
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/grops $(BINDIR)/psbb
+ cp grops psbb pfbtops $(BINDIR)
+ @cd devps; \
+ $(MAKE) \
+ "FONTDIR=$(FONTDIR)" "PAGE=$(PAGE)" "BINDIR=$(BINDIR)" install.bin
+
+install.nobin:
+ -[ -d $(MACRODIR) ] || mkdir $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.ps
+ cp tmac.ps $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.psatk
+ cp tmac.psatk $(MACRODIR)
+ @echo Making install.nobin in devps
+ @cd devps; \
+ $(MAKE) \
+ "FONTDIR=$(FONTDIR)" "PAGE=$(PAGE)" "BINDIR=$(BINDIR)" install.nobin
+
+install: install.bin install.nobin
+
+
+TAGS : $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o psbb core grops broken.h
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+devps: FORCE
+ @echo Making all in devps
+ @cd devps; $(MAKE) "FONTDIR=$(FONTDIR)" "PAGE=$(PAGE)" all
+
+FORCE:
diff --git a/ps/TODO b/ps/TODO
new file mode 100644
index 000000000..fb8742445
--- /dev/null
+++ b/ps/TODO
@@ -0,0 +1,19 @@
+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.
diff --git a/ps/devps/AB b/ps/devps/AB
new file mode 100644
index 000000000..779d4670d
--- /dev/null
+++ b/ps/devps/AB
@@ -0,0 +1,408 @@
+name AB
+internalname AvantGarde-Demi
+spacewidth 280
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -50
+A w -50
+A v -50
+A Y -139
+A W -96
+A V -127
+A T -73
+F . -42
+F , -42
+F A -95
+L y -75
+L Y -90
+L W -90
+L V -90
+L T -50
+P . -16
+P , -16
+P A -100
+R y 31
+R Y -20
+R W -20
+R V -26
+R T 16
+T y -12
+T w -15
+T u -56
+T ; -12
+T s -43
+T r -42
+T . 4
+T o -50
+T i -5
+T - -10
+T hy -10
+T char173 -10
+T e -50
+T , 4
+T : -12
+T c -50
+T a -40
+T A -70
+V y -15
+V u -68
+V ; -13
+V r -40
+V . -13
+V o -80
+V i -9
+V - -20
+V hy -20
+V char173 -20
+V e -80
+V , -13
+V : -13
+V a -80
+V A -125
+W y 1
+W u -40
+W ; -12
+W r -39
+W . -12
+W o -50
+W i -7
+W - -10
+W hy -10
+W char173 -10
+W e -50
+W , -12
+W : -12
+W a -50
+W A -94
+Y v -25
+Y u -82
+Y ; -8
+Y q -100
+Y . -8
+Y p -60
+Y o -100
+Y i -5
+Y - -60
+Y hy -60
+Y char173 -60
+Y e -100
+Y , -8
+Y : -8
+Y a -100
+Y A -138
+f f 38
+r q -18
+r . 9
+r o -18
+r n 5
+r m 5
+r - -10
+r hy -10
+r char173 -10
+r h 0
+r g -8
+r f 38
+r e -8
+r d -8
+r , 9
+r c -27
+charset
+ha 600,739 2 0000 asciicircum
+ti 600,421 0 0001 asciitilde
+vS 520,943,18 2 0002 Scaron
+vZ 500,943 2 0003 Zcaron
+vs 440,758,18 2 0004 scaron
+vz 460,758 2 0005 zcaron
+:Y 620,938 2 0006 Ydieresis
+tm 1000,739 2 0007 trademark
+space 280 0 0040
+! 280,739 2 0041 exclam
+" 360,739 2 0042 quotedbl
+# 600,740 2 0043 numbersign
+sh "
+$ 560,855,85 3 0044 dollar
+Do "
+% 860,753,18 2 0045 percent
+& 680,753,18 2 0046 ampersand
+' 280,739 2 0047 quoteright
+( 380,739,128 3 0050 parenleft
+) 380,739,128 3 0051 parenright
+* 440,753 2 0052 asterisk
++ 600,570 0 0053 plus
+, 280,133,140 1 0054 comma
+- 420,377 0 0055 hyphen
+hy "
+char173 "
+. 280,133 0 0056 period
+/ 460,739,128 3 0057 slash
+sl "
+0 560,753,18 2 0060 zero
+1 560,739 2 0061 one
+2 560,753 2 0062 two
+3 560,753,18 2 0063 three
+4 560,739 2 0064 four
+5 560,739,18 2 0065 five
+6 560,739,18 2 0066 six
+7 560,739 2 0067 seven
+8 560,753,18 2 0070 eight
+9 560,753 2 0071 nine
+: 280,502 0 0072 colon
+; 280,502,140 1 0073 semicolon
+< 600,593 0 0074 less
+= 600,473 0 0075 equal
+> 600,593 0 0076 greater
+? 560,753 2 0077 question
+@ 740,753,18 2 0100 at
+at "
+A 740,739 2 0101 A
+B 580,739 2 0102 B
+C 780,753,18 2 0103 C
+D 700,739 2 0104 D
+E 520,739 2 0105 E
+F 480,739 2 0106 F
+G 840,753,18 2 0107 G
+H 680,739 2 0110 H
+I 280,739 2 0111 I
+J 480,739,18 2 0112 J
+K 620,739 2 0113 K
+L 440,739 2 0114 L
+M 900,739 2 0115 M
+N 740,739 2 0116 N
+O 840,753,18 2 0117 O
+P 560,739 2 0120 P
+Q 840,753,18 2 0121 Q
+R 580,739 2 0122 R
+S 520,753,18 2 0123 S
+T 420,739 2 0124 T
+U 640,739,18 2 0125 U
+V 700,739 2 0126 V
+W 900,739 2 0127 W
+X 680,739 2 0130 X
+Y 620,739 2 0131 Y
+Z 500,739 2 0132 Z
+[ 320,739,127 3 0133 bracketleft
+lB "
+\ 640,739 2 0134 backslash
+rs "
+] 320,739,127 3 0135 bracketright
+rB "
+a^ 540,758 2 0136 circumflex
+^ "
+_ 500,0,138 1 0137 underscore
+` 280,753 2 0140 quoteleft
+oq "
+a 660,572,18 0 0141 a
+b 660,739,18 2 0142 b
+c 640,572,18 0 0143 c
+d 660,739,18 2 0144 d
+e 640,572,18 0 0145 e
+f 280,753 2 0146 f
+g 660,572,225 1 0147 g
+h 600,739 2 0150 h
+i 240,739 2 0151 i
+j 260,739,185 3 0152 j
+k 580,739 2 0153 k
+l 240,739 2 0154 l
+m 940,572 0 0155 m
+n 600,572 0 0156 n
+o 640,572,18 0 0157 o
+p 660,572,185 1 0160 p
+q 660,572,185 1 0161 q
+r 320,572 0 0162 r
+s 440,572,18 0 0163 s
+t 300,739 2 0164 t
+u 600,554,18 0 0165 u
+v 560,554 0 0166 v
+w 800,554 0 0167 w
+x 560,554 0 0170 x
+y 580,554,185 1 0171 y
+z 460,554 0 0172 z
+lC 340,738,127 3 0173 braceleft
+{ "
+ba 600,739 2 0174 bar
+| "
+rC 340,738,127 3 0175 braceright
+} "
+a~ 480,749 2 0176 tilde
+~ "
+--- 280,133,140 1 0200 quotesinglbase
+Fo 460,498 0 0201 guillemotleft
+char171 "
+Fc 460,498 0 0202 guillemotright
+char187 "
+bu 600,486 0 0203 bullet
+--- 560,822,150 3 0204 florin
+f/ 160,739 2 0205 fraction
+%0 1280,753,18 2 0206 perthousand
+dg 560,739,128 3 0207 dagger
+dd 560,739,128 3 0210 daggerdbl
+en 500,377 0 0211 endash
+em 1000,377 0 0212 emdash
+fi 520,753 2 0214 fi
+fl 520,753 2 0215 fl
+.i 240,554 0 0220 dotlessi
+ga 420,840 2 0222 grave
+a" 700,845 2 0223 hungarumlaut
+a. 280,753 2 0224 dotaccent
+ab 480,756 2 0225 breve
+ah 540,758 2 0226 caron
+ao 360,826 2 0227 ring
+ho 340,0,203 1 0230 ogonek
+lq 480,753 2 0231 quotedblleft
+rq 480,739 2 0232 quotedblright
+oe 1080,572,18 0 0233 oe
+/l 320,739 2 0234 lslash
+--- 480,133,140 1 0235 quotedblbase
+OE 1060,753,14 2 0236 OE
+/L 480,739 2 0237 Lslash
+r! 280,554,185 1 0241 exclamdown
+char161 "
+ct 560,713 2 0242 cent
+char162 "
+Po 560,753 2 0243 sterling
+char163 "
+Cs 600,653 2 0244 currency
+char164 "
+Ye 560,739 2 0245 yen
+char165 "
+sc 560,768,141 3 0247 section
+char167 "
+ad 500,753 2 0250 dieresis
+char168 "
+co 740,753,18 2 0251 copyright
+char169 "
+Of 360,753 2 0252 ordfeminine
+char170 "
+fo 240,498 0 0253 guilsinglleft
+no 600,473 0 0254 logicalnot
+char172 "
+\- 600,376 0 0255 minus
+rg 740,753,18 2 0256 registered
+char174 "
+a- 420,730 2 0257 macron
+char175 "
+aa 420,838 2 0264 acute
+char180 "
+ps 600,739,128 3 0266 paragraph
+char182 "
+char183 280,384 0 0267 periodcentered
+ac 340,0,251 1 0270 cedilla
+char184 "
+Om 360,753 2 0272 ordmasculine
+char186 "
+fc 240,498 0 0273 guilsinglright
+r? 560,554,199 1 0277 questiondown
+char191 "
+`A 740,1025 2 0300 Agrave
+char192 "
+'A 740,1023 2 0301 Aacute
+char193 "
+^A 740,943 2 0302 Acircumflex
+char194 "
+~A 740,934 2 0303 Atilde
+char195 "
+:A 740,938 2 0304 Adieresis
+char196 "
+oA 740,1011 2 0305 Aring
+char197 "
+AE 900,739 2 0306 AE
+char198 "
+,C 780,753,251 3 0307 Ccedilla
+char199 "
+`E 520,1025 2 0310 Egrave
+char200 "
+'E 520,1023 2 0311 Eacute
+char201 "
+^E 520,943 2 0312 Ecircumflex
+char202 "
+:E 520,938 2 0313 Edieresis
+char203 "
+`I 280,1025 2 0314 Igrave
+char204 "
+'I 280,1023 2 0315 Iacute
+char205 "
+^I 280,943 2 0316 Icircumflex
+char206 "
+:I 280,938 2 0317 Idieresis
+char207 "
+~N 740,934 2 0321 Ntilde
+char209 "
+`O 840,1025,18 2 0322 Ograve
+char210 "
+'O 840,1023,18 2 0323 Oacute
+char211 "
+^O 840,943,18 2 0324 Ocircumflex
+char212 "
+~O 840,934,18 2 0325 Otilde
+char213 "
+:O 840,938,18 2 0326 Odieresis
+char214 "
+/O 840,813,70 3 0330 Oslash
+char216 "
+`U 640,1025,18 2 0331 Ugrave
+char217 "
+'U 640,1023,18 2 0332 Uacute
+char218 "
+^U 640,943,18 2 0333 Ucircumflex
+char219 "
+:U 640,938,18 2 0334 Udieresis
+char220 "
+ss 600,753,18 2 0337 germandbls
+char223 "
+`a 660,840,18 2 0340 agrave
+char224 "
+'a 660,838,18 2 0341 aacute
+char225 "
+^a 660,758,18 2 0342 acircumflex
+char226 "
+~a 660,749,18 2 0343 atilde
+char227 "
+:a 660,753,18 2 0344 adieresis
+char228 "
+oa 660,826,18 2 0345 aring
+char229 "
+ae 1080,572,18 0 0346 ae
+char230 "
+,c 640,572,251 1 0347 ccedilla
+char231 "
+`e 640,840,18 2 0350 egrave
+char232 "
+'e 640,838,18 2 0351 eacute
+char233 "
+^e 640,758,18 2 0352 ecircumflex
+char234 "
+:e 640,753,18 2 0353 edieresis
+char235 "
+`i 240,840 2 0354 igrave
+char236 "
+'i 240,838 2 0355 iacute
+char237 "
+^i 240,758 2 0356 icircumflex
+char238 "
+:i 240,753 2 0357 idieresis
+char239 "
+~n 600,749 2 0361 ntilde
+char241 "
+`o 640,840,18 2 0362 ograve
+char242 "
+'o 640,838,18 2 0363 oacute
+char243 "
+^o 640,758,18 2 0364 ocircumflex
+char244 "
+~o 640,749,18 2 0365 otilde
+char245 "
+:o 640,753,18 2 0366 odieresis
+char246 "
+/o 660,606,50 0 0370 oslash
+char248 "
+`u 600,840,18 2 0371 ugrave
+char249 "
+'u 600,838,18 2 0372 uacute
+char250 "
+^u 600,758,18 2 0373 ucircumflex
+char251 "
+:u 600,753,18 2 0374 udieresis
+char252 "
+:y 580,753,185 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/ABI b/ps/devps/ABI
new file mode 100644
index 000000000..0c30ca722
--- /dev/null
+++ b/ps/devps/ABI
@@ -0,0 +1,409 @@
+name ABI
+internalname AvantGarde-DemiOblique
+slant 10.5
+spacewidth 280
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -60
+A w -60
+A v -60
+A Y -139
+A W -96
+A V -127
+A T -73
+F . -42
+F , -42
+F A -95
+L y -60
+L Y -90
+L W -90
+L V -90
+L T -50
+P . -15
+P , -15
+P A -100
+R y 31
+R Y -10
+R W -10
+R V -10
+R T 16
+T y -12
+T w -15
+T u -40
+T ; -12
+T s -35
+T r -42
+T . 4
+T o -35
+T i 5
+T - -10
+T hy -10
+T char173 -10
+T e -35
+T , 4
+T : -12
+T c -35
+T a -35
+T A -70
+V y -15
+V u -68
+V ; -13
+V r -40
+V . -30
+V o -80
+V i -9
+V - -20
+V hy -20
+V char173 -20
+V e -80
+V , -30
+V : -13
+V a -80
+V A -125
+W y 1
+W u -40
+W ; -12
+W r -39
+W . -12
+W o -50
+W i -7
+W - -10
+W hy -10
+W char173 -10
+W e -50
+W , -12
+W : -12
+W a -50
+W A -94
+Y v -25
+Y u -82
+Y ; -8
+Y q -100
+Y . -8
+Y p -60
+Y o -100
+Y i -5
+Y - -60
+Y hy -60
+Y char173 -60
+Y e -100
+Y , -20
+Y : -8
+Y a -100
+Y A -138
+f f 38
+r q -18
+r . 9
+r o -25
+r n 15
+r m 15
+r - -10
+r hy -10
+r char173 -10
+r h 0
+r g -8
+r f 38
+r e -8
+r d -8
+r , 9
+r c -27
+charset
+ha 600,739,0,60,-80,60 2 0000 asciicircum
+ti 600,421,0,69,-55,69 0 0001 asciitilde
+vS 520,943,18,167,1,82 2 0002 Scaron
+vZ 500,943,0,177,32,82 2 0003 Zcaron
+vs 440,758,18,173,1,82 2 0004 scaron
+vz 460,758,0,163,30,82 2 0005 zcaron
+:Y 620,938,0,193,-90,82 2 0006 Ydieresis
+tm 1000,739,0,116,-144,82 2 0007 trademark
+space 280 0 0040
+! 280,739,0,112,-22,82 2 0041 exclam
+" 360,739,0,110,-119,82 2 0042 quotedbl
+# 600,740,0,96,-50,82 2 0043 numbersign
+sh "
+$ 560,855,85,65,-50,65 3 0044 dollar
+Do "
+% 860,753,18,58,-74,58 2 0045 percent
+& 680,753,18,111,-21,82 2 0046 ampersand
+' 280,739,0,112,-109,82 2 0047 quoteright
+( 380,739,128,156,-72,82 3 0050 parenleft
+) 380,739,128,47,37,47 3 0051 parenright
+* 440,753,0,100,-126,82 2 0052 asterisk
++ 600,570,0,75,-50,75 0 0053 plus
+, 280,133,140,0,3 1 0054 comma
+- 420,377,0,51,-72,51 0 0055 hyphen
+hy "
+char173 "
+. 280,133,0,0,-22 0 0056 period
+/ 460,739,128,170,48,82 3 0057 slash
+sl "
+0 560,753,18,113,-18,82 2 0060 zero
+1 560,739,0,6,-196,6 2 0061 one
+2 560,753,0,109,8,82 2 0062 two
+3 560,753,18,61,-3,61 2 0063 three
+4 560,739,0,79,30,79 2 0064 four
+5 560,739,18,76,0,76 2 0065 five
+6 560,739,18,74,-12,74 2 0066 six
+7 560,739,0,127,-35,82 2 0067 seven
+8 560,753,18,72,-19,72 2 0070 eight
+9 560,753,0,118,-58,82 2 0071 nine
+: 280,502,0,68,-22,68 0 0072 colon
+; 280,502,140,68,3,68 1 0073 semicolon
+< 600,593,0,118,-48,82 0 0074 less
+= 600,473,0,93,-31,82 0 0075 equal
+> 600,593,0,75,-6,75 0 0076 greater
+? 560,753,0,76,-79,76 2 0077 question
+@ 740,753,18,141,-14,82 2 0100 at
+at "
+A 740,739,0,43,43,43 2 0101 A
+B 580,739,0,77,-18,77 2 0102 B
+C 780,753,18,128,-42,82 2 0103 C
+D 700,739,0,84,-18,82 2 0104 D
+E 520,739,0,132,-18,82 2 0105 E
+F 480,739,0,151,-18,82 2 0106 F
+G 840,753,18,98,-41,82 2 0107 G
+H 680,739,0,113,-18,82 2 0110 H
+I 280,739,0,112,-18,82 2 0111 I
+J 480,739,18,121,12,82 2 0112 J
+K 620,739,0,166,-18,82 2 0113 K
+L 440,739,0,66,-18,66 2 0114 L
+M 900,739,0,127,-18,82 2 0115 M
+N 740,739,0,114,-18,82 2 0116 N
+O 840,753,18,86,-41,82 2 0117 O
+P 560,739,0,129,-18,82 2 0120 P
+Q 840,753,18,99,-41,82 2 0121 Q
+R 580,739,0,127,-18,82 2 0122 R
+S 520,753,18,109,1,82 2 0123 S
+T 420,739,0,185,-69,82 2 0124 T
+U 640,739,18,114,-59,82 2 0125 U
+V 700,739,0,181,-95,82 2 0126 V
+W 900,739,0,185,-94,82 2 0127 W
+X 680,739,0,183,45,82 2 0130 X
+Y 620,739,0,193,-90,82 2 0131 Y
+Z 500,739,0,148,32,82 2 0132 Z
+[ 320,739,127,159,-50,82 3 0133 bracketleft
+lB "
+\ 640,739,0,0,-171 2 0134 backslash
+rs "
+] 320,739,127,76,37,76 3 0135 bracketright
+rB "
+a^ 540,758,0,98,-139,82 2 0136 circumflex
+^ "
+_ 500,0,138,41,76,41 1 0137 underscore
+` 280,753,0,113,-111,82 2 0140 quoteleft
+oq "
+a 660,572,18,108,-25,82 0 0141 a
+b 660,739,18,85,-4,82 2 0142 b
+c 640,572,18,80,-27,80 0 0143 c
+d 660,739,18,140,-26,82 2 0144 d
+e 640,572,18,75,-26,75 0 0145 e
+f 280,753,0,183,-7,82 2 0146 f
+g 660,572,225,104,20,82 1 0147 g
+h 600,739,0,63,-4,63 2 0150 h
+i 240,739,0,134,-4,82 2 0151 i
+j 260,739,185,132,67,82 3 0152 j
+k 580,739,0,95,-4,82 2 0153 k
+l 240,739,0,134,-4,82 2 0154 l
+m 940,572,0,62,-4,62 0 0155 m
+n 600,572,0,63,-4,63 0 0156 n
+o 640,572,18,85,-26,82 0 0157 o
+p 660,572,185,81,30,81 1 0160 p
+q 660,572,185,104,-27,82 1 0161 q
+r 320,572,0,142,-4,82 0 0162 r
+s 440,572,18,92,1,82 0 0163 s
+t 300,739,0,140,-24,82 2 0164 t
+u 600,554,18,100,-38,82 0 0165 u
+v 560,554,0,146,-54,82 0 0166 v
+w 800,554,0,138,-63,82 0 0167 w
+x 560,554,0,119,49,82 0 0170 x
+y 580,554,185,141,-23,82 1 0171 y
+z 460,554,0,118,30,82 0 0172 z
+lC 340,738,127,149,-44,82 3 0173 braceleft
+{ "
+ba 600,739,0,0,-193 2 0174 bar
+| "
+rC 340,738,127,70,36,70 3 0175 braceright
+} "
+a~ 480,749,0,98,-156,82 2 0176 tilde
+~ "
+--- 280,133,140,0,4 1 0200 quotesinglbase
+Fo 460,498,0,70,-35,70 0 0201 guillemotleft
+char171 "
+Fc 460,498,0,83,-48,82 0 0202 guillemotright
+char187 "
+bu 600,486,0,0,-140 0 0203 bullet
+--- 560,822,150,136,94,82 3 0204 florin
+f/ 160,739,0,307,171,82 2 0205 fraction
+%0 1280,753,18,51,-74,51 2 0206 perthousand
+dg 560,739,128,98,-79,82 3 0207 dagger
+dd 560,739,128,105,-46,82 3 0210 daggerdbl
+en 500,377,0,118,-1,82 0 0211 endash
+em 1000,377,0,38,-81,38 0 0212 emdash
+fi 520,753,0,128,-5,82 2 0214 fi
+fl 520,753,0,128,-5,82 2 0215 fl
+.i 240,554,0,100,-4,82 0 0220 dotlessi
+ga 420,840,0,114,-161,82 2 0222 grave
+a" 700,845,0,118,-154,82 2 0223 hungarumlaut
+a. 280,753,0,117,-139,82 2 0224 dotaccent
+ab 480,756,0,114,-164,82 2 0225 breve
+ah 540,758,0,123,-164,82 2 0226 caron
+ao 360,826,0,106,-156,82 2 0227 ring
+ho 340,0,203,0,-2 1 0230 ogonek
+lq 480,753,0,118,-109,82 2 0231 quotedblleft
+rq 480,739,0,117,-107,82 2 0232 quotedblright
+oe 1080,572,18,75,-26,75 0 0233 oe
+/l 320,739,0,150,-9,82 2 0234 lslash
+--- 480,133,140,5,5,5 1 0235 quotedblbase
+OE 1060,753,14,131,-50,82 2 0236 OE
+/L 480,739,0,68,1,68 2 0237 Lslash
+r! 280,554,185,78,12,78 1 0241 exclamdown
+char161 "
+ct 560,713,0,83,-56,82 2 0242 cent
+char162 "
+Po 560,753,0,106,10,82 2 0243 sterling
+char163 "
+Cs 600,653,0,112,-41,82 2 0244 currency
+char164 "
+Ye 560,739,0,216,-1,82 2 0245 yen
+char165 "
+sc 560,768,141,98,-18,82 3 0247 section
+char167 "
+ad 500,753,0,107,-139,82 2 0250 dieresis
+char168 "
+co 740,753,18,144,0,82 2 0251 copyright
+char169 "
+Of 360,753,0,170,-40,82 2 0252 ordfeminine
+char170 "
+fo 240,498,0,77,-35,77 0 0253 guilsinglleft
+no 600,473,0,93,-67,82 0 0254 logicalnot
+char172 "
+\- 600,376,0,75,-49,75 0 0255 minus
+rg 740,753,18,144,0,82 2 0256 registered
+char174 "
+a- 420,730,0,116,-139,82 2 0257 macron
+char175 "
+aa 420,838,0,118,-155,82 2 0264 acute
+char180 "
+ps 600,739,128,114,-83,82 3 0266 paragraph
+char182 "
+char183 280,384,0,46,-69,46 0 0267 periodcentered
+ac 340,0,251,0,8 1 0270 cedilla
+char184 "
+Om 360,753,0,141,-40,82 2 0272 ordmasculine
+char186 "
+fc 240,498,0,90,-48,82 0 0273 guilsinglright
+r? 560,554,199,18,-21,18 1 0277 questiondown
+char191 "
+`A 740,1025,0,43,43,43 2 0300 Agrave
+char192 "
+'A 740,1023,0,43,43,43 2 0301 Aacute
+char193 "
+^A 740,943,0,43,43,43 2 0302 Acircumflex
+char194 "
+~A 740,934,0,43,43,43 2 0303 Atilde
+char195 "
+:A 740,938,0,43,43,43 2 0304 Adieresis
+char196 "
+oA 740,1011,0,43,43,43 2 0305 Aring
+char197 "
+AE 900,739,0,130,33,82 2 0306 AE
+char198 "
+,C 780,753,251,128,-42,82 3 0307 Ccedilla
+char199 "
+`E 520,1025,0,132,-18,82 2 0310 Egrave
+char200 "
+'E 520,1023,0,132,-18,82 2 0311 Eacute
+char201 "
+^E 520,943,0,142,-18,82 2 0312 Ecircumflex
+char202 "
+:E 520,938,0,132,-18,82 2 0313 Edieresis
+char203 "
+`I 280,1025,0,218,-18,82 2 0314 Igrave
+char204 "
+'I 280,1023,0,222,-18,82 2 0315 Iacute
+char205 "
+^I 280,943,0,262,-18,82 2 0316 Icircumflex
+char206 "
+:I 280,938,0,251,-18,82 2 0317 Idieresis
+char207 "
+~N 740,934,0,114,-18,82 2 0321 Ntilde
+char209 "
+`O 840,1025,18,86,-41,82 2 0322 Ograve
+char210 "
+'O 840,1023,18,86,-41,82 2 0323 Oacute
+char211 "
+^O 840,943,18,86,-41,82 2 0324 Ocircumflex
+char212 "
+~O 840,934,18,86,-41,82 2 0325 Otilde
+char213 "
+:O 840,938,18,86,-41,82 2 0326 Odieresis
+char214 "
+/O 840,813,70,97,-41,82 3 0330 Oslash
+char216 "
+`U 640,1025,18,114,-59,82 2 0331 Ugrave
+char217 "
+'U 640,1023,18,114,-59,82 2 0332 Uacute
+char218 "
+^U 640,943,18,114,-59,82 2 0333 Ucircumflex
+char219 "
+:U 640,938,18,114,-59,82 2 0334 Udieresis
+char220 "
+ss 600,753,18,78,-1,78 2 0337 germandbls
+char223 "
+`a 660,840,18,108,-25,82 2 0340 agrave
+char224 "
+'a 660,838,18,108,-25,82 2 0341 aacute
+char225 "
+^a 660,758,18,108,-25,82 2 0342 acircumflex
+char226 "
+~a 660,749,18,108,-25,82 2 0343 atilde
+char227 "
+:a 660,753,18,108,-25,82 2 0344 adieresis
+char228 "
+oa 660,826,18,108,-25,82 2 0345 aring
+char229 "
+ae 1080,572,18,78,-26,78 0 0346 ae
+char230 "
+,c 640,572,251,80,-27,80 1 0347 ccedilla
+char231 "
+`e 640,840,18,75,-26,75 2 0350 egrave
+char232 "
+'e 640,838,18,75,-26,75 2 0351 eacute
+char233 "
+^e 640,758,18,75,-26,75 2 0352 ecircumflex
+char234 "
+:e 640,753,18,75,-26,75 2 0353 edieresis
+char235 "
+`i 240,840,0,204,-4,82 2 0354 igrave
+char236 "
+'i 240,838,0,208,-4,82 2 0355 iacute
+char237 "
+^i 240,758,0,248,11,82 2 0356 icircumflex
+char238 "
+:i 240,753,0,237,-4,82 2 0357 idieresis
+char239 "
+~n 600,749,0,63,-4,63 2 0361 ntilde
+char241 "
+`o 640,840,18,85,-26,82 2 0362 ograve
+char242 "
+'o 640,838,18,85,-26,82 2 0363 oacute
+char243 "
+^o 640,758,18,85,-26,82 2 0364 ocircumflex
+char244 "
+~o 640,749,18,85,-26,82 2 0365 otilde
+char245 "
+:o 640,753,18,85,-26,82 2 0366 odieresis
+char246 "
+/o 660,606,50,70,-26,70 0 0370 oslash
+char248 "
+`u 600,840,18,100,-38,82 2 0371 ugrave
+char249 "
+'u 600,838,18,100,-38,82 2 0372 uacute
+char250 "
+^u 600,758,18,100,-38,82 2 0373 ucircumflex
+char251 "
+:u 600,753,18,100,-38,82 2 0374 udieresis
+char252 "
+:y 580,753,185,141,-23,82 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/AI b/ps/devps/AI
new file mode 100644
index 000000000..b388efef3
--- /dev/null
+++ b/ps/devps/AI
@@ -0,0 +1,409 @@
+name AI
+internalname AvantGarde-BookOblique
+slant 10.5
+spacewidth 277
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -72
+A w -65
+A v -73
+A Y -122
+A W -73
+A V -122
+A T -81
+F . -79
+F , -122
+F A -60
+L y -23
+L Y -91
+L W -67
+L V -113
+L T -46
+P . -91
+P , -123
+P A -74
+R y 32
+R Y -20
+R W 2
+R V -39
+R T 6
+T y 5
+T w 7
+T u -20
+T ; -29
+T s -23
+T r -20
+T . -71
+T o -49
+T i 40
+T - 0
+T hy 0
+T char173 0
+T e -49
+T , -102
+T : 3
+T c -51
+T a -52
+T A -60
+V y -25
+V u -40
+V ; -33
+V r -20
+V . -75
+V o -80
+V i 15
+V - 0
+V hy 0
+V char173 0
+V e -80
+V , -106
+V : -1
+V a -80
+V A -122
+W y -2
+W u -30
+W r -29
+W o -46
+W i 12
+W e -47
+W a -50
+W ; -33
+W . -74
+W - 0
+W hy 0
+W char173 0
+W , -106
+W : -1
+W A -73
+Y v -17
+Y u -55
+Y ; -40
+Y q -93
+Y . -75
+Y p -67
+Y o -89
+Y i 13
+Y - 0
+Y hy 0
+Y char173 0
+Y e -89
+Y , -110
+Y : -10
+Y a -93
+Y A -122
+f f 3
+r q -8
+r . -73
+r o -4
+r n 35
+r m 35
+r - 0
+r hy 0
+r char173 0
+r h 29
+r g 1
+r f 48
+r e -4
+r d -6
+r , -105
+r c -7
+charset
+ha 672,739,0,32,-34,32 2 0000 asciicircum
+ti 606,391,0,63,-49,63 0 0001 asciitilde
+vS 498,931,13,151,-6,81 2 0002 Scaron
+vZ 480,931,0,170,33,81 2 0003 Zcaron
+vs 388,739,13,170,7,81 2 0004 scaron
+vz 425,739,0,152,40,81 2 0005 zcaron
+:Y 592,931,0,185,-89,81 2 0006 Ydieresis
+tm 1000,739,0,125,-139,81 2 0007 trademark
+space 277 0 0040
+! 295,739,0,77,-61,77 2 0041 exclam
+" 309,739,0,115,-119,81 2 0042 quotedbl
+# 720,739,0,116,-21,81 2 0043 numbersign
+sh "
+$ 554,808,54,71,-62,71 2 0044 dollar
+Do "
+% 775,751,13,60,-67,60 2 0045 percent
+& 757,751,13,67,-43,67 2 0046 ampersand
+' 351,739,0,89,-142,81 2 0047 quoteright
+( 369,739,127,153,-56,81 3 0050 parenleft
+) 369,739,127,58,40,58 3 0051 parenright
+* 425,751,0,102,-122,81 2 0052 asterisk
++ 606,554,0,62,-50,62 0 0053 plus
+, 277,126,67,49,-29,49 1 0054 comma
+- 332,334,0,82,-30,81 0 0055 hyphen
+hy "
+char173 "
+. 277,126,0,0,-51 0 0056 period
+/ 437,739,128,147,34,81 3 0057 slash
+sl "
+0 554,751,13,117,-21,81 2 0060 zero
+1 554,739,0,0,-230 2 0061 one
+2 554,751,0,103,17,81 2 0062 two
+3 554,751,13,60,-21,60 2 0063 three
+4 554,739,0,93,13,81 2 0064 four
+5 554,739,13,100,-17,81 2 0065 five
+6 554,739,13,75,-16,75 2 0066 six
+7 554,739,0,123,-58,81 2 0067 seven
+8 554,751,13,76,-28,76 2 0070 eight
+9 554,751,0,121,-62,81 2 0071 nine
+: 277,510,0,43,-51,43 0 0072 colon
+; 277,510,67,71,21,71 1 0073 semicolon
+< 606,565,0,107,-49,81 0 0074 less
+= 606,436,0,80,-32,80 0 0075 equal
+> 606,565,0,63,-5,63 0 0076 greater
+? 591,751,0,86,-109,81 2 0077 question
+@ 867,752,12,66,-83,66 2 0100 at
+at "
+A 740,739,0,39,39,39 2 0101 A
+B 574,739,0,83,-26,81 2 0102 B
+C 813,751,13,106,-56,81 2 0103 C
+D 744,739,0,80,-26,80 2 0104 D
+E 536,739,0,131,-26,81 2 0105 E
+F 485,739,0,153,-26,81 2 0106 F
+G 872,751,13,72,-57,72 2 0107 G
+H 683,739,0,111,-26,81 2 0110 H
+I 226,739,0,111,-26,81 2 0111 I
+J 482,739,13,111,9,81 2 0112 J
+K 591,739,0,181,-26,81 2 0113 K
+L 462,739,0,56,-26,56 2 0114 L
+M 919,739,0,111,-26,81 2 0115 M
+N 740,739,0,111,-26,81 2 0116 N
+O 869,750,13,81,-56,81 2 0117 O
+P 592,739,0,123,-26,81 2 0120 P
+Q 871,751,13,92,-55,81 2 0121 Q
+R 607,739,0,116,-26,81 2 0122 R
+S 498,751,13,116,-6,81 2 0123 S
+T 426,739,0,180,-82,81 2 0124 T
+U 655,739,13,111,-69,81 2 0125 U
+V 702,739,0,177,-97,81 2 0126 V
+W 960,739,0,177,-97,81 2 0127 W
+X 609,739,0,164,42,81 2 0130 X
+Y 592,739,0,185,-89,81 2 0131 Y
+Z 480,739,0,170,33,81 2 0132 Z
+[ 351,739,127,149,-82,81 3 0133 bracketleft
+lB "
+\ 605,739,0,0,-171 2 0134 backslash
+rs "
+] 351,739,127,31,36,31 3 0135 bracketright
+rB "
+a^ 502,739,0,90,-138,81 2 0136 circumflex
+^ "
+_ 500,0,125,38,73,38 1 0137 underscore
+` 351,739,0,60,-185,60 2 0140 quoteleft
+oq "
+a 683,559,13,88,-38,81 0 0141 a
+b 682,739,13,64,-13,64 2 0142 b
+c 647,559,13,82,-38,81 0 0143 c
+d 685,739,13,124,-38,81 2 0144 d
+e 650,559,13,65,-38,65 0 0145 e
+f 314,739,0,183,-51,81 2 0146 f
+g 673,559,214,88,-10,81 1 0147 g
+h 610,739,0,49,-13,49 2 0150 h
+i 200,739,0,124,-13,81 2 0151 i
+j 203,739,192,124,131,81 3 0152 j
+k 502,739,0,132,-13,81 2 0153 k
+l 200,739,0,124,-13,81 2 0154 l
+m 938,559,0,53,-13,53 0 0155 m
+n 610,559,0,49,-13,49 0 0156 n
+o 655,559,13,64,-37,64 0 0157 o
+p 682,559,192,63,23,63 1 0160 p
+q 682,559,192,88,-37,81 1 0161 q
+r 301,559,0,145,-13,81 0 0162 r
+s 388,559,13,87,7,81 0 0163 s
+t 339,739,0,139,-51,81 2 0164 t
+u 608,547,13,88,-49,81 0 0165 u
+v 554,547,0,143,-59,81 0 0166 v
+w 831,547,0,144,-58,81 0 0167 w
+x 480,547,0,143,42,81 0 0170 x
+y 536,547,192,142,-47,81 1 0171 y
+z 425,547,0,122,40,81 0 0172 z
+lC 351,739,127,107,-44,81 3 0173 braceleft
+{ "
+ba 672,739,0,0,-254 2 0174 bar
+| "
+rC 351,739,127,25,36,25 3 0175 braceright
+} "
+a~ 439,715,0,97,-150,81 2 0176 tilde
+~ "
+--- 354,126,67,0,-27 1 0200 quotesinglbase
+Fo 425,500,0,97,-46,81 0 0201 guillemotleft
+char171 "
+Fc 425,500,0,66,-15,66 0 0202 guillemotright
+char187 "
+bu 606,486,0,0,-121 0 0203 bullet
+--- 554,751,214,181,71,81 3 0204 florin
+f/ 166,739,0,302,165,81 2 0205 fraction
+%0 1174,751,13,60,-67,60 2 0206 perthousand
+dg 553,739,127,91,-96,81 3 0207 dagger
+dd 553,739,128,95,-71,81 3 0210 daggerdbl
+en 500,334,0,105,-7,81 0 0211 endash
+em 1000,334,0,22,-89,22 0 0212 emdash
+fi 487,739,0,124,-48,81 2 0214 fi
+fl 485,739,0,124,-48,81 2 0215 fl
+.i 200,547,0,88,-13,81 0 0220 dotlessi
+ga 378,779,0,100,-157,81 2 0222 grave
+a" 552,785,0,113,-147,81 2 0223 hungarumlaut
+a. 222,739,0,113,-138,81 2 0224 dotaccent
+ab 453,716,0,108,-157,81 2 0225 breve
+ah 502,739,0,113,-161,81 2 0226 caron
+ao 332,797,0,108,-153,81 2 0227 ring
+ho 302,0,190,0,-3 1 0230 ogonek
+lq 502,739,0,55,-183,55 2 0231 quotedblleft
+rq 484,739,0,96,-138,81 2 0232 quotedblright
+oe 1137,559,13,66,-38,66 0 0233 oe
+/l 300,739,0,120,-44,81 2 0234 lslash
+--- 502,126,67,0,-27 1 0235 quotedblbase
+OE 1194,751,13,131,-56,81 2 0236 OE
+/L 517,739,0,47,-20,47 2 0237 Lslash
+r! 295,547,192,41,-25,41 1 0241 exclamdown
+char161 "
+ct 554,705,0,91,-63,81 2 0242 cent
+char162 "
+Po 554,751,0,151,-8,81 2 0243 sterling
+char163 "
+Cs 554,553,0,111,-1,81 0 0244 currency
+char164 "
+Ye 554,739,0,222,-47,81 2 0245 yen
+char165 "
+sc 615,751,139,31,-64,31 3 0247 section
+char167 "
+ad 369,739,0,113,-138,81 2 0250 dieresis
+char168 "
+co 747,751,13,133,-3,81 2 0251 copyright
+char169 "
+Of 369,751,0,158,-41,81 2 0252 ordfeminine
+char170 "
+fo 251,500,0,97,-46,81 0 0253 guilsinglleft
+no 606,436,0,80,-68,80 0 0254 logicalnot
+char172 "
+\- 606,339,0,62,-50,62 0 0255 minus
+rg 747,751,13,133,-3,81 2 0256 registered
+char174 "
+a- 485,680,0,102,-138,81 2 0257 macron
+char175 "
+aa 375,785,0,111,-148,81 2 0264 acute
+char180 "
+ps 564,739,127,114,-85,81 3 0266 paragraph
+char182 "
+char183 277,364,0,15,-95,15 0 0267 periodcentered
+ac 324,0,223,0,4 1 0270 cedilla
+char184 "
+Om 369,751,0,131,-42,81 2 0272 ordmasculine
+char186 "
+fc 251,500,0,66,-15,66 0 0273 guilsinglright
+r? 591,547,214,0,-14 1 0277 questiondown
+char191 "
+`A 740,971,0,39,39,39 2 0300 Agrave
+char192 "
+'A 740,977,0,39,39,39 2 0301 Aacute
+char193 "
+^A 740,931,0,39,39,39 2 0302 Acircumflex
+char194 "
+~A 740,907,0,39,39,39 2 0303 Atilde
+char195 "
+:A 740,931,0,39,39,39 2 0304 Adieresis
+char196 "
+oA 740,989,0,39,39,39 2 0305 Aring
+char197 "
+AE 992,739,0,131,39,81 2 0306 AE
+char198 "
+,C 813,751,223,106,-56,81 3 0307 Ccedilla
+char199 "
+`E 536,971,0,131,-26,81 2 0310 Egrave
+char200 "
+'E 536,977,0,131,-26,81 2 0311 Eacute
+char201 "
+^E 536,931,0,131,-26,81 2 0312 Ecircumflex
+char202 "
+:E 536,931,0,131,-26,81 2 0313 Edieresis
+char203 "
+`I 226,971,0,212,-26,81 2 0314 Igrave
+char204 "
+'I 226,977,0,222,-26,81 2 0315 Iacute
+char205 "
+^I 226,931,0,264,-26,81 2 0316 Icircumflex
+char206 "
+:I 226,931,0,221,-26,81 2 0317 Idieresis
+char207 "
+~N 740,907,0,111,-26,81 2 0321 Ntilde
+char209 "
+`O 869,971,13,81,-56,81 2 0322 Ograve
+char210 "
+'O 869,977,13,81,-56,81 2 0323 Oacute
+char211 "
+^O 869,931,13,81,-56,81 2 0324 Ocircumflex
+char212 "
+~O 869,907,13,81,-56,81 2 0325 Otilde
+char213 "
+:O 869,931,13,81,-56,81 2 0326 Odieresis
+char214 "
+/O 868,821,83,107,-22,81 3 0330 Oslash
+char216 "
+`U 655,971,13,111,-69,81 2 0331 Ugrave
+char217 "
+'U 655,977,13,111,-69,81 2 0332 Uacute
+char218 "
+^U 655,931,13,111,-69,81 2 0333 Ucircumflex
+char219 "
+:U 655,931,13,111,-69,81 2 0334 Udieresis
+char220 "
+ss 554,751,13,72,-9,72 2 0337 germandbls
+char223 "
+`a 683,779,13,88,-38,81 2 0340 agrave
+char224 "
+'a 683,785,13,88,-38,81 2 0341 aacute
+char225 "
+^a 683,739,13,88,-38,81 2 0342 acircumflex
+char226 "
+~a 683,715,13,88,-38,81 2 0343 atilde
+char227 "
+:a 683,739,13,88,-38,81 2 0344 adieresis
+char228 "
+oa 683,797,13,88,-38,81 2 0345 aring
+char229 "
+ae 1157,559,13,65,-38,65 0 0346 ae
+char230 "
+,c 647,559,223,82,-38,81 1 0347 ccedilla
+char231 "
+`e 650,779,13,65,-38,65 2 0350 egrave
+char232 "
+'e 650,785,13,65,-38,65 2 0351 eacute
+char233 "
+^e 650,739,13,65,-38,65 2 0352 ecircumflex
+char234 "
+:e 650,739,13,65,-38,65 2 0353 edieresis
+char235 "
+`i 200,779,0,189,-13,81 2 0354 igrave
+char236 "
+'i 200,785,0,199,-13,81 2 0355 iacute
+char237 "
+^i 200,739,0,241,13,81 2 0356 icircumflex
+char238 "
+:i 200,739,0,198,-13,81 2 0357 idieresis
+char239 "
+~n 610,715,0,49,-13,49 2 0361 ntilde
+char241 "
+`o 655,779,13,64,-37,64 2 0362 ograve
+char242 "
+'o 655,785,13,64,-37,64 2 0363 oacute
+char243 "
+^o 655,739,13,64,-37,64 2 0364 ocircumflex
+char244 "
+~o 655,715,13,64,-37,64 2 0365 otilde
+char245 "
+:o 655,739,13,64,-37,64 2 0366 odieresis
+char246 "
+/o 653,594,45,84,-26,81 0 0370 oslash
+char248 "
+`u 608,779,13,88,-49,81 2 0371 ugrave
+char249 "
+'u 608,785,13,88,-49,81 2 0372 uacute
+char250 "
+^u 608,739,13,88,-49,81 2 0373 ucircumflex
+char251 "
+:u 608,739,13,88,-49,81 2 0374 udieresis
+char252 "
+:y 536,739,192,142,-47,81 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/AR b/ps/devps/AR
new file mode 100644
index 000000000..29f606fbc
--- /dev/null
+++ b/ps/devps/AR
@@ -0,0 +1,408 @@
+name AR
+internalname AvantGarde-Book
+spacewidth 277
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -72
+A w -65
+A v -73
+A Y -122
+A W -95
+A V -122
+A T -81
+F . -79
+F , -122
+F A -60
+L y -23
+L Y -91
+L W -67
+L V -113
+L T -46
+P . -91
+P , -123
+P A -74
+R y 32
+R Y -20
+R W 2
+R V -39
+R T 6
+T y 5
+T w 7
+T u -46
+T ; -29
+T s -23
+T r -30
+T . -71
+T o -49
+T i 31
+T - 0
+T hy 0
+T char173 0
+T e -49
+T , -102
+T : 3
+T c -51
+T a -52
+T A -81
+V y -25
+V u -40
+V ; -33
+V r -40
+V . -75
+V o -101
+V i 5
+V - 0
+V hy 0
+V char173 0
+V e -101
+V , -106
+V : -1
+V a -104
+V A -122
+W y -2
+W u -30
+W r -29
+W o -46
+W i 6
+W e -47
+W a -50
+W ; -33
+W . -74
+W - 0
+W hy 0
+W char173 0
+W , -106
+W : -1
+W A -73
+Y v -17
+Y u -69
+Y ; -23
+Y q -93
+Y . -65
+Y p -67
+Y o -89
+Y i 13
+Y - 0
+Y hy 0
+Y char173 0
+Y e -89
+Y , -97
+Y : 9
+Y a -93
+Y A -122
+f f 3
+r q -8
+r . -73
+r o -4
+r n 21
+r m 28
+r - 0
+r hy 0
+r char173 0
+r h 29
+r g 1
+r f 48
+r e -4
+r d -6
+r , -105
+r c -7
+charset
+ha 672,739 2 0000 asciicircum
+ti 606,391 0 0001 asciitilde
+vS 498,931,13 2 0002 Scaron
+vZ 480,931 2 0003 Zcaron
+vs 388,739,13 2 0004 scaron
+vz 425,739 2 0005 zcaron
+:Y 592,931 2 0006 Ydieresis
+tm 1000,739 2 0007 trademark
+space 277 0 0040
+! 295,739 2 0041 exclam
+" 309,739 2 0042 quotedbl
+# 720,739 2 0043 numbersign
+sh "
+$ 554,808,54 2 0044 dollar
+Do "
+% 775,751,13 2 0045 percent
+& 757,751,13 2 0046 ampersand
+' 351,739 2 0047 quoteright
+( 369,739,127 3 0050 parenleft
+) 369,739,127 3 0051 parenright
+* 425,751 2 0052 asterisk
++ 606,554 0 0053 plus
+, 277,126,67 1 0054 comma
+- 332,334 0 0055 hyphen
+hy "
+char173 "
+. 277,126 0 0056 period
+/ 437,739,128 3 0057 slash
+sl "
+0 554,751,13 2 0060 zero
+1 554,739 2 0061 one
+2 554,751 2 0062 two
+3 554,751,13 2 0063 three
+4 554,739 2 0064 four
+5 554,739,13 2 0065 five
+6 554,739,13 2 0066 six
+7 554,739 2 0067 seven
+8 554,751,13 2 0070 eight
+9 554,751 2 0071 nine
+: 277,510 0 0072 colon
+; 277,510,67 1 0073 semicolon
+< 606,565 0 0074 less
+= 606,436 0 0075 equal
+> 606,565 0 0076 greater
+? 591,751 2 0077 question
+@ 867,752,12 2 0100 at
+at "
+A 740,739 2 0101 A
+B 574,739 2 0102 B
+C 813,751,13 2 0103 C
+D 744,739 2 0104 D
+E 536,739 2 0105 E
+F 485,739 2 0106 F
+G 872,751,13 2 0107 G
+H 683,739 2 0110 H
+I 226,739 2 0111 I
+J 482,739,13 2 0112 J
+K 591,739 2 0113 K
+L 462,739 2 0114 L
+M 919,739 2 0115 M
+N 740,739 2 0116 N
+O 869,750,13 2 0117 O
+P 592,739 2 0120 P
+Q 871,751,13 2 0121 Q
+R 607,739 2 0122 R
+S 498,751,13 2 0123 S
+T 426,739 2 0124 T
+U 655,739,13 2 0125 U
+V 702,739 2 0126 V
+W 960,739 2 0127 W
+X 609,739 2 0130 X
+Y 592,739 2 0131 Y
+Z 480,739 2 0132 Z
+[ 351,739,127 3 0133 bracketleft
+lB "
+\ 605,739 2 0134 backslash
+rs "
+] 351,739,127 3 0135 bracketright
+rB "
+a^ 502,739 2 0136 circumflex
+^ "
+_ 500,0,125 1 0137 underscore
+` 351,739 2 0140 quoteleft
+oq "
+a 683,559,13 0 0141 a
+b 682,739,13 2 0142 b
+c 647,559,13 0 0143 c
+d 685,739,13 2 0144 d
+e 650,559,13 0 0145 e
+f 314,739 2 0146 f
+g 673,559,214 1 0147 g
+h 610,739 2 0150 h
+i 200,739 2 0151 i
+j 203,739,192 3 0152 j
+k 502,739 2 0153 k
+l 200,739 2 0154 l
+m 938,559 0 0155 m
+n 610,559 0 0156 n
+o 655,559,13 0 0157 o
+p 682,559,192 1 0160 p
+q 682,559,192 1 0161 q
+r 301,559 0 0162 r
+s 388,559,13 0 0163 s
+t 339,739 2 0164 t
+u 608,547,13 0 0165 u
+v 554,547 0 0166 v
+w 831,547 0 0167 w
+x 480,547 0 0170 x
+y 536,547,192 1 0171 y
+z 425,547 0 0172 z
+lC 351,739,127 3 0173 braceleft
+{ "
+ba 672,739 2 0174 bar
+| "
+rC 351,739,127 3 0175 braceright
+} "
+a~ 439,715 2 0176 tilde
+~ "
+--- 354,126,67 1 0200 quotesinglbase
+Fo 425,500 0 0201 guillemotleft
+char171 "
+Fc 425,500 0 0202 guillemotright
+char187 "
+bu 606,486 0 0203 bullet
+--- 554,751,214 3 0204 florin
+f/ 166,739 2 0205 fraction
+%0 1174,751,13 2 0206 perthousand
+dg 553,739,127 3 0207 dagger
+dd 553,739,128 3 0210 daggerdbl
+en 500,334 0 0211 endash
+em 1000,334 0 0212 emdash
+fi 487,739 2 0214 fi
+fl 485,739 2 0215 fl
+.i 200,547 0 0220 dotlessi
+ga 378,779 2 0222 grave
+a" 552,785 2 0223 hungarumlaut
+a. 222,739 2 0224 dotaccent
+ab 453,716 2 0225 breve
+ah 502,739 2 0226 caron
+ao 332,797 2 0227 ring
+ho 302,0,190 1 0230 ogonek
+lq 502,739 2 0231 quotedblleft
+rq 484,739 2 0232 quotedblright
+oe 1137,559,13 0 0233 oe
+/l 300,739 2 0234 lslash
+--- 502,126,67 1 0235 quotedblbase
+OE 1194,751,13 2 0236 OE
+/L 517,739 2 0237 Lslash
+r! 295,547,192 1 0241 exclamdown
+char161 "
+ct 554,705 2 0242 cent
+char162 "
+Po 554,751 2 0243 sterling
+char163 "
+Cs 554,553 0 0244 currency
+char164 "
+Ye 554,739 2 0245 yen
+char165 "
+sc 615,751,139 3 0247 section
+char167 "
+ad 369,739 2 0250 dieresis
+char168 "
+co 747,751,13 2 0251 copyright
+char169 "
+Of 369,751 2 0252 ordfeminine
+char170 "
+fo 251,500 0 0253 guilsinglleft
+no 606,436 0 0254 logicalnot
+char172 "
+\- 606,339 0 0255 minus
+rg 747,751,13 2 0256 registered
+char174 "
+a- 485,680 2 0257 macron
+char175 "
+aa 375,785 2 0264 acute
+char180 "
+ps 564,739,127 3 0266 paragraph
+char182 "
+char183 277,364 0 0267 periodcentered
+ac 324,0,223 1 0270 cedilla
+char184 "
+Om 369,751 2 0272 ordmasculine
+char186 "
+fc 251,500 0 0273 guilsinglright
+r? 591,547,214 1 0277 questiondown
+char191 "
+`A 740,971 2 0300 Agrave
+char192 "
+'A 740,977 2 0301 Aacute
+char193 "
+^A 740,931 2 0302 Acircumflex
+char194 "
+~A 740,907 2 0303 Atilde
+char195 "
+:A 740,931 2 0304 Adieresis
+char196 "
+oA 740,989 2 0305 Aring
+char197 "
+AE 992,739 2 0306 AE
+char198 "
+,C 813,751,223 3 0307 Ccedilla
+char199 "
+`E 536,971 2 0310 Egrave
+char200 "
+'E 536,977 2 0311 Eacute
+char201 "
+^E 536,931 2 0312 Ecircumflex
+char202 "
+:E 536,931 2 0313 Edieresis
+char203 "
+`I 226,971 2 0314 Igrave
+char204 "
+'I 226,977 2 0315 Iacute
+char205 "
+^I 226,931 2 0316 Icircumflex
+char206 "
+:I 226,931 2 0317 Idieresis
+char207 "
+~N 740,907 2 0321 Ntilde
+char209 "
+`O 869,971,13 2 0322 Ograve
+char210 "
+'O 869,977,13 2 0323 Oacute
+char211 "
+^O 869,931,13 2 0324 Ocircumflex
+char212 "
+~O 869,907,13 2 0325 Otilde
+char213 "
+:O 869,931,13 2 0326 Odieresis
+char214 "
+/O 868,821,83 3 0330 Oslash
+char216 "
+`U 655,971,13 2 0331 Ugrave
+char217 "
+'U 655,977,13 2 0332 Uacute
+char218 "
+^U 655,931,13 2 0333 Ucircumflex
+char219 "
+:U 655,931,13 2 0334 Udieresis
+char220 "
+ss 554,751,13 2 0337 germandbls
+char223 "
+`a 683,779,13 2 0340 agrave
+char224 "
+'a 683,785,13 2 0341 aacute
+char225 "
+^a 683,739,13 2 0342 acircumflex
+char226 "
+~a 683,715,13 2 0343 atilde
+char227 "
+:a 683,739,13 2 0344 adieresis
+char228 "
+oa 683,797,13 2 0345 aring
+char229 "
+ae 1157,559,13 0 0346 ae
+char230 "
+,c 647,559,223 1 0347 ccedilla
+char231 "
+`e 650,779,13 2 0350 egrave
+char232 "
+'e 650,785,13 2 0351 eacute
+char233 "
+^e 650,739,13 2 0352 ecircumflex
+char234 "
+:e 650,739,13 2 0353 edieresis
+char235 "
+`i 200,779 2 0354 igrave
+char236 "
+'i 200,785 2 0355 iacute
+char237 "
+^i 200,739 2 0356 icircumflex
+char238 "
+:i 200,739 2 0357 idieresis
+char239 "
+~n 610,715 2 0361 ntilde
+char241 "
+`o 655,779,13 2 0362 ograve
+char242 "
+'o 655,785,13 2 0363 oacute
+char243 "
+^o 655,739,13 2 0364 ocircumflex
+char244 "
+~o 655,715,13 2 0365 otilde
+char245 "
+:o 655,739,13 2 0366 odieresis
+char246 "
+/o 653,594,45 0 0370 oslash
+char248 "
+`u 608,779,13 2 0371 ugrave
+char249 "
+'u 608,785,13 2 0372 uacute
+char250 "
+^u 608,739,13 2 0373 ucircumflex
+char251 "
+:u 608,739,13 2 0374 udieresis
+char252 "
+:y 536,739,192 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/BMB b/ps/devps/BMB
new file mode 100644
index 000000000..3f10f3a0e
--- /dev/null
+++ b/ps/devps/BMB
@@ -0,0 +1,408 @@
+name BMB
+internalname Bookman-Demi
+spacewidth 340
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -1
+A w -9
+A v -8
+A Y -52
+A W -20
+A V -68
+A T -40
+F . -132
+F , -130
+F A -59
+L y 19
+L Y -35
+L W -41
+L V -50
+L T -4
+P . -128
+P , -129
+P A -46
+R y -8
+R Y -20
+R W -24
+R V -29
+R T -4
+T y 0
+T w 0
+T u 0
+T ; 5
+T s -10
+T r 27
+T . -122
+T o -28
+T i 27
+T - -10
+T hy -10
+T char173 -10
+T e -29
+T , -122
+T : 7
+T c -29
+T a -24
+T A -42
+V y 12
+V u -11
+V ; -38
+V r -15
+V . -105
+V o -79
+V i 15
+V - -10
+V hy -10
+V char173 -10
+V e -80
+V , -103
+V : -37
+V a -74
+V A -88
+W y 12
+W u -11
+W ; -38
+W r -15
+W . -105
+W o -78
+W i 15
+W - -10
+W hy -10
+W char173 -10
+W e -79
+W , -103
+W : -37
+W a -73
+W A -60
+Y v 24
+Y u -13
+Y ; -34
+Y q -66
+Y . -105
+Y p -23
+Y o -66
+Y i 2
+Y - -10
+Y hy -10
+Y char173 -10
+Y e -67
+Y , -103
+Y : -32
+Y a -60
+Y A -56
+f f 21
+r q -9
+r . -102
+r o -9
+r n 20
+r m 20
+r - -10
+r hy -10
+r char173 -10
+r h -23
+r g -9
+r f 20
+r e -10
+r d -10
+r , -101
+r c -9
+charset
+ha 600,681 2 0000 asciicircum
+ti 600,368 0 0001 asciitilde
+vS 660,896,17 2 0002 Scaron
+vZ 640,896 2 0003 Zcaron
+vs 520,717,8 2 0004 scaron
+vz 560,717 2 0005 zcaron
+:Y 700,877 2 0006 Ydieresis
+tm 980,681 2 0007 trademark
+space 340 0 0040
+! 360,698,8 2 0041 exclam
+" 420,698 2 0042 quotedbl
+# 600,681 2 0043 numbersign
+sh "
+$ 660,805,119 3 0044 dollar
+Do "
+% 940,698,8 2 0045 percent
+& 800,698,17 2 0046 ampersand
+' 320,698 2 0047 quoteright
+( 320,749,150 3 0050 parenleft
+) 320,749,150 3 0051 parenright
+* 460,697 2 0052 asterisk
++ 600,514 0 0053 plus
+, 340,162,124 1 0054 comma
+- 360,318 0 0055 hyphen
+hy "
+char173 "
+. 340,172,8 0 0056 period
+/ 600,725,149 3 0057 slash
+sl "
+0 660,698,17 2 0060 zero
+1 660,681 2 0061 one
+2 660,698 2 0062 two
+3 660,698,17 2 0063 three
+4 660,681 2 0064 four
+5 660,723,17 2 0065 five
+6 660,698,17 2 0066 six
+7 660,681 2 0067 seven
+8 660,698,17 2 0070 eight
+9 660,698,17 2 0071 nine
+: 340,515,8 0 0072 colon
+; 340,515,124 1 0073 semicolon
+< 600,542,9 0 0074 less
+= 600,421 0 0075 equal
+> 600,542,9 0 0076 greater
+? 660,698,8 2 0077 question
+@ 820,698,17 2 0100 at
+at "
+A 720,681 2 0101 A
+B 720,681 2 0102 B
+C 740,698,17 2 0103 C
+D 780,681 2 0104 D
+E 720,681 2 0105 E
+F 680,681 2 0106 F
+G 780,698,17 2 0107 G
+H 820,681 2 0110 H
+I 400,681 2 0111 I
+J 640,681,17 2 0112 J
+K 800,681 2 0113 K
+L 640,681 2 0114 L
+M 940,681 2 0115 M
+N 740,681 2 0116 N
+O 800,698,17 2 0117 O
+P 660,681 2 0120 P
+Q 800,698,226 3 0121 Q
+R 780,681 2 0122 R
+S 660,698,17 2 0123 S
+T 700,681 2 0124 T
+U 740,681,17 2 0125 U
+V 720,681 2 0126 V
+W 940,681 2 0127 W
+X 780,681 2 0130 X
+Y 700,681 2 0131 Y
+Z 640,681 2 0132 Z
+[ 300,725,138 3 0133 bracketleft
+lB "
+\ 600,725 2 0134 backslash
+rs "
+] 300,725,138 3 0135 bracketright
+rB "
+a^ 500,731 2 0136 circumflex
+^ "
+_ 500,0,174 1 0137 underscore
+` 320,698 2 0140 quoteleft
+oq "
+a 580,515,8 0 0141 a
+b 600,725,8 2 0142 b
+c 580,515,8 0 0143 c
+d 640,725,8 2 0144 d
+e 580,515,8 0 0145 e
+f 380,741 2 0146 f
+g 580,595,243 3 0147 g
+h 680,725 2 0150 h
+i 360,729 2 0151 i
+j 340,729,221 3 0152 j
+k 660,725 2 0153 k
+l 340,725 2 0154 l
+m 1000,515 0 0155 m
+n 680,515 0 0156 n
+o 620,515,8 0 0157 o
+p 640,515,212 1 0160 p
+q 620,515,212 1 0161 q
+r 460,502 0 0162 r
+s 520,515,8 0 0163 s
+t 460,660,8 2 0164 t
+u 660,502,8 0 0165 u
+v 600,502 0 0166 v
+w 800,502 0 0167 w
+x 600,502 0 0170 x
+y 620,502,221 1 0171 y
+z 560,502 0 0172 z
+lC 320,726,139 3 0173 braceleft
+{ "
+ba 600,725 2 0174 bar
+| "
+rC 320,726,139 3 0175 braceright
+} "
+a~ 480,691 2 0176 tilde
+~ "
+--- 320,144,114 1 0200 quotesinglbase
+Fo 400,457 0 0201 guillemotleft
+char171 "
+Fc 400,457 0 0202 guillemotright
+char187 "
+bu 460,511 0 0203 bullet
+--- 660,749,209 3 0204 florin
+f/ 120,681 2 0205 fraction
+%0 1360,698,8 2 0206 perthousand
+dg 440,698,156 3 0207 dagger
+dd 380,698,156 3 0210 daggerdbl
+en 500,318 0 0211 endash
+em 1000,318 0 0212 emdash
+fi 740,741 2 0214 fi
+fl 740,741 2 0215 fl
+.i 360,502 0 0220 dotlessi
+ga 400,730 2 0222 grave
+a" 440,741 2 0223 hungarumlaut
+a. 320,730 2 0224 dotaccent
+ab 500,722 2 0225 breve
+ah 500,717 2 0226 caron
+ao 340,755 2 0227 ring
+ho 320,0,163 1 0230 ogonek
+lq 540,698 2 0231 quotedblleft
+rq 540,698 2 0232 quotedblright
+oe 940,515,8 0 0233 oe
+/l 340,725 2 0234 lslash
+--- 540,144,114 1 0235 quotedblbase
+OE 1220,698,17 2 0236 OE
+/L 640,681 2 0237 Lslash
+r! 360,515,191 1 0241 exclamdown
+char161 "
+ct 660,674 2 0242 cent
+char162 "
+Po 660,698,17 2 0243 sterling
+char163 "
+Cs 600,593 2 0244 currency
+char164 "
+Ye 660,681 2 0245 yen
+char165 "
+sc 600,698,153 3 0247 section
+char167 "
+ad 500,698 2 0250 dieresis
+char168 "
+co 740,698,17 2 0251 copyright
+char169 "
+Of 400,698 2 0252 ordfeminine
+char170 "
+fo 220,457 0 0253 guilsinglleft
+no 600,421 0 0254 logicalnot
+char172 "
+\- 600,323 0 0255 minus
+rg 740,698,17 2 0256 registered
+char174 "
+a- 460,663 2 0257 macron
+char175 "
+aa 400,731 2 0264 acute
+char180 "
+ps 800,681,101 3 0266 paragraph
+char182 "
+char183 340,355 0 0267 periodcentered
+ac 360,0,213 1 0270 cedilla
+char184 "
+Om 400,698 2 0272 ordmasculine
+char186 "
+fc 220,457 0 0273 guilsinglright
+r? 660,515,191 1 0277 questiondown
+char191 "
+`A 720,909 2 0300 Agrave
+char192 "
+'A 720,910 2 0301 Aacute
+char193 "
+^A 720,910 2 0302 Acircumflex
+char194 "
+~A 720,870 2 0303 Atilde
+char195 "
+:A 720,877 2 0304 Adieresis
+char196 "
+oA 720,934 2 0305 Aring
+char197 "
+AE 1140,681 2 0306 AE
+char198 "
+,C 740,698,213 3 0307 Ccedilla
+char199 "
+`E 720,909 2 0310 Egrave
+char200 "
+'E 720,910 2 0311 Eacute
+char201 "
+^E 720,910 2 0312 Ecircumflex
+char202 "
+:E 720,877 2 0313 Edieresis
+char203 "
+`I 400,909 2 0314 Igrave
+char204 "
+'I 400,910 2 0315 Iacute
+char205 "
+^I 400,910 2 0316 Icircumflex
+char206 "
+:I 400,877 2 0317 Idieresis
+char207 "
+~N 740,870 2 0321 Ntilde
+char209 "
+`O 800,909,17 2 0322 Ograve
+char210 "
+'O 800,910,17 2 0323 Oacute
+char211 "
+^O 800,910,17 2 0324 Ocircumflex
+char212 "
+~O 800,870,17 2 0325 Otilde
+char213 "
+:O 800,877,17 2 0326 Odieresis
+char214 "
+/O 800,781,110 3 0330 Oslash
+char216 "
+`U 740,909,17 2 0331 Ugrave
+char217 "
+'U 740,910,17 2 0332 Uacute
+char218 "
+^U 740,910,17 2 0333 Ucircumflex
+char219 "
+:U 740,877,17 2 0334 Udieresis
+char220 "
+ss 660,699,91 3 0337 germandbls
+char223 "
+`a 580,730,8 2 0340 agrave
+char224 "
+'a 580,731,8 2 0341 aacute
+char225 "
+^a 580,731,8 2 0342 acircumflex
+char226 "
+~a 580,691,8 2 0343 atilde
+char227 "
+:a 580,698,8 2 0344 adieresis
+char228 "
+oa 580,755,8 2 0345 aring
+char229 "
+ae 880,515,8 0 0346 ae
+char230 "
+,c 580,515,213 1 0347 ccedilla
+char231 "
+`e 580,730,8 2 0350 egrave
+char232 "
+'e 580,731,8 2 0351 eacute
+char233 "
+^e 580,731,8 2 0352 ecircumflex
+char234 "
+:e 580,698,8 2 0353 edieresis
+char235 "
+`i 360,730 2 0354 igrave
+char236 "
+'i 360,731 2 0355 iacute
+char237 "
+^i 360,731 2 0356 icircumflex
+char238 "
+:i 360,698 2 0357 idieresis
+char239 "
+~n 680,691 2 0361 ntilde
+char241 "
+`o 620,730,8 2 0362 ograve
+char242 "
+'o 620,731,8 2 0363 oacute
+char243 "
+^o 620,731,8 2 0364 ocircumflex
+char244 "
+~o 620,691,8 2 0365 otilde
+char245 "
+:o 620,698,8 2 0366 odieresis
+char246 "
+/o 620,551,40 0 0370 oslash
+char248 "
+`u 660,730,8 2 0371 ugrave
+char249 "
+'u 660,731,8 2 0372 uacute
+char250 "
+^u 660,731,8 2 0373 ucircumflex
+char251 "
+:u 660,698,8 2 0374 udieresis
+char252 "
+:y 620,698,221 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/BMBI b/ps/devps/BMBI
new file mode 100644
index 000000000..fe8f90169
--- /dev/null
+++ b/ps/devps/BMBI
@@ -0,0 +1,409 @@
+name BMBI
+internalname Bookman-DemiItalic
+slant 10
+spacewidth 340
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y 20
+A w 20
+A v 20
+A Y -25
+A W -35
+A V -40
+A T -17
+F . -105
+F , -98
+F A -35
+L y 62
+L Y -5
+L W -15
+L V -19
+L T -26
+P . -105
+P , -98
+P A -31
+R y 27
+R Y 4
+R W -4
+R V -8
+R T -3
+T y 56
+T w 69
+T u 42
+T ; 31
+T s -1
+T r 41
+T . -107
+T o -5
+T i 42
+T - -20
+T hy -20
+T char173 -20
+T e -10
+T , -100
+T : 26
+T c -8
+T a -8
+T A -42
+V y 17
+V u -1
+V ; -22
+V r 2
+V . -115
+V o -50
+V i 32
+V - -20
+V hy -20
+V char173 -20
+V e -50
+V , -137
+V : -28
+V a -50
+V A -50
+W y -51
+W u -69
+W ; -81
+W r -66
+W . -183
+W o -100
+W i -36
+W - -22
+W hy -22
+W char173 -22
+W e -100
+W , -201
+W : -86
+W a -100
+W A -77
+Y v 26
+Y u -1
+Y ; -4
+Y q -43
+Y . -113
+Y p 0
+Y o -41
+Y i 20
+Y - -20
+Y hy -20
+Y char173 -20
+Y e -46
+Y , -106
+Y : -9
+Y a -45
+Y A -30
+f f 10
+r q -3
+r . -120
+r o -1
+r n 39
+r m 39
+r - -20
+r hy -20
+r char173 -20
+r h -35
+r g -23
+r f 42
+r e -6
+r d -3
+r , -113
+r c -5
+charset
+ha 620,681,0,24,-42,24 2 0000 asciicircum
+ti 620,368,0,35,-51,35 0 0001 asciitilde
+vS 700,915,17,81,-9,73 2 0002 Scaron
+vZ 680,915,0,110,27,73 2 0003 Zcaron
+vs 540,749,8,83,18,73 2 0004 scaron
+vz 560,749,8,76,14,73 2 0005 zcaron
+:Y 660,900,0,207,-22,73 2 0006 Ydieresis
+tm 940,681,0,92,8,73 2 0007 trademark
+space 340 0 0040
+! 320,698,8,96,-36,73 2 0041 exclam
+" 380,697,0,197,-70,73 2 0042 quotedbl
+# 600,681,0,59,-67,59 2 0043 numbersign
+sh "
+$ 680,790,164,67,5,67 3 0044 dollar
+Do "
+% 880,698,17,69,-56,69 2 0045 percent
+& 980,698,17,86,2,73 2 0046 ampersand
+' 320,698,0,79,-121,73 2 0047 quoteright
+( 260,741,134,178,19,73 3 0050 parenleft
+) 260,741,134,112,85,73 3 0051 parenright
+* 460,698,0,98,-76,73 2 0052 asterisk
++ 600,514,0,45,-41,45 0 0053 plus
+, 340,185,124,8,-50,8 1 0054 comma
+- 280,313,0,89,-9,73 0 0055 hyphen
+hy "
+char173 "
+. 340,177,8,6,-56,6 0 0056 period
+/ 360,741,106,192,41,73 3 0057 slash
+sl "
+0 680,698,17,73,-37,73 2 0060 zero
+1 680,681,0,0,-73 2 0061 one
+2 680,698,0,44,-17,44 2 0062 two
+3 680,698,17,53,-22,53 2 0063 three
+4 680,681,0,78,-13,73 2 0064 four
+5 680,681,17,39,-28,39 2 0065 five
+6 680,698,17,74,-38,73 2 0066 six
+7 680,681,0,109,-73,73 2 0067 seven
+8 680,698,17,56,-18,56 2 0070 eight
+9 680,698,17,82,-21,73 2 0071 nine
+: 340,515,8,66,-56,66 0 0072 colon
+; 340,515,124,62,-50,62 1 0073 semicolon
+< 620,540,9,18,-29,18 0 0074 less
+= 600,421,0,45,-41,45 0 0075 equal
+> 620,540,9,28,-39,28 0 0076 greater
+? 620,698,8,98,-95,73 2 0077 question
+@ 780,698,17,60,-30,60 2 0100 at
+at "
+A 720,681,0,99,77,73 2 0101 A
+B 720,681,0,92,36,73 2 0102 B
+C 700,698,17,104,-28,73 2 0103 C
+D 760,681,0,95,36,73 2 0104 D
+E 720,681,0,107,36,73 2 0105 E
+F 660,681,0,153,36,73 2 0106 F
+G 760,698,17,118,-27,73 2 0107 G
+H 800,681,0,160,36,73 2 0110 H
+I 380,681,0,155,36,73 2 0111 I
+J 620,681,17,151,42,73 2 0112 J
+K 780,681,0,149,36,73 2 0113 K
+L 640,681,0,135,36,73 2 0114 L
+M 860,681,0,160,36,73 2 0115 M
+N 740,681,0,155,36,73 2 0116 N
+O 760,698,17,96,-28,73 2 0117 O
+P 640,681,0,134,56,73 2 0120 P
+Q 760,698,213,95,13,73 3 0121 Q
+R 740,681,0,75,36,73 2 0122 R
+S 700,698,17,81,-9,73 2 0123 S
+T 700,681,0,152,-20,73 2 0124 T
+U 740,681,17,165,-62,73 2 0125 U
+V 660,681,0,209,-22,73 2 0126 V
+W 1000,681,0,140,-22,73 2 0127 W
+X 740,681,0,145,57,73 2 0130 X
+Y 660,681,0,207,-22,73 2 0131 Y
+Z 680,681,0,110,27,73 2 0132 Z
+[ 260,741,118,164,41,73 3 0133 bracketleft
+lB "
+\ 580,741,0,45,-23,45 2 0134 backslash
+rs "
+] 260,741,118,137,68,73 3 0135 bracketright
+rB "
+a^ 480,749,0,93,-133,73 2 0136 circumflex
+^ "
+_ 500,0,159,50,50,50 1 0137 underscore
+` 320,698,0,63,-105,63 2 0140 quoteleft
+oq "
+a 680,515,8,105,-34,73 0 0141 a
+b 600,732,8,83,-7,73 2 0142 b
+c 560,515,8,87,-8,73 0 0143 c
+d 680,732,8,84,-10,73 2 0144 d
+e 560,515,8,86,-9,73 0 0145 e
+f 420,741,213,271,242,73 3 0146 f
+g 620,515,213,99,29,73 1 0147 g
+h 700,732,8,86,-43,73 2 0150 h
+i 380,755,8,90,-33,73 2 0151 i
+j 320,755,213,122,210,73 3 0152 j
+k 700,732,8,82,-47,73 2 0153 k
+l 380,732,8,80,-59,73 2 0154 l
+m 960,515,8,86,-33,73 0 0155 m
+n 680,515,8,85,-33,73 0 0156 n
+o 600,515,8,77,-9,73 0 0157 o
+p 660,515,213,72,74,72 1 0160 p
+q 620,515,213,70,-10,70 1 0161 q
+r 500,515,0,132,-34,73 0 0162 r
+s 540,515,8,83,18,73 0 0163 s
+t 440,658,8,98,-56,73 2 0164 t
+u 680,507,8,90,-33,73 0 0165 u
+v 540,515,8,82,-6,73 0 0166 v
+w 860,515,8,81,-6,73 0 0167 w
+x 620,515,8,84,40,73 0 0170 x
+y 600,507,213,92,25,73 1 0171 y
+z 560,515,8,76,14,73 0 0172 z
+lC 300,742,123,159,-3,73 3 0173 braceleft
+{ "
+ba 620,741,0,0,-233 2 0174 bar
+| "
+rC 300,742,123,102,54,73 3 0175 braceright
+} "
+a~ 480,709,0,103,-128,73 2 0176 tilde
+~ "
+--- 300,166,112,34,-56,34 1 0200 quotesinglbase
+Fo 380,503,0,76,-12,73 0 0201 guillemotleft
+char171 "
+Fc 380,503,0,76,-12,73 0 0202 guillemotright
+char187 "
+bu 360,511,0,94,-10,73 0 0203 bullet
+--- 680,741,199,113,78,73 3 0204 florin
+f/ 120,681,0,262,244,73 2 0205 fraction
+%0 1360,698,17,78,-1,73 2 0206 perthousand
+dg 420,698,137,96,-39,73 3 0207 dagger
+dd 420,698,137,116,-29,73 3 0210 daggerdbl
+en 500,311,0,113,-7,73 0 0211 endash
+em 1000,311,0,113,-7,73 0 0212 emdash
+fi 820,741,213,80,241,73 3 0214 fi
+fl 820,741,213,80,241,73 3 0215 fl
+.i 380,507,8,90,-33,73 0 0220 dotlessi
+ga 380,771,0,94,-143,73 2 0222 grave
+a" 560,775,0,106,-131,73 2 0223 hungarumlaut
+a. 380,734,0,15,-130,15 2 0224 dotaccent
+ab 460,707,0,106,-127,73 2 0225 breve
+ah 480,749,0,93,-133,73 2 0226 caron
+ao 360,775,0,96,-135,73 2 0227 ring
+ho 320,0,182,0,-18 1 0230 ogonek
+lq 520,698,0,75,-106,73 2 0231 quotedblleft
+rq 520,698,0,90,-121,73 2 0232 quotedblright
+oe 920,515,8,91,2,73 0 0233 oe
+/l 380,732,8,82,-13,73 2 0234 lslash
+--- 520,166,112,25,-56,25 1 0235 quotedblbase
+OE 1180,698,17,115,-44,73 2 0236 OE
+/L 640,681,0,134,36,73 2 0237 Lslash
+r! 320,515,191,74,-14,73 1 0241 exclamdown
+char161 "
+ct 680,718,0,0,-111 2 0242 cent
+char162 "
+Po 680,698,17,157,50,73 2 0243 sterling
+char163 "
+Cs 680,571,0,0,14 2 0244 currency
+char164 "
+Ye 680,681,0,152,-42,73 2 0245 yen
+char165 "
+sc 620,698,137,68,4,68 3 0247 section
+char167 "
+ad 520,734,0,99,-130,73 2 0250 dieresis
+char168 "
+co 780,698,17,53,-33,53 2 0251 copyright
+char169 "
+Of 440,685,0,105,-5,73 2 0252 ordfeminine
+char170 "
+fo 220,503,0,79,-12,73 0 0253 guilsinglleft
+no 620,421,0,15,-31,15 0 0254 logicalnot
+char172 "
+\- 600,323,0,45,-41,45 0 0255 minus
+rg 780,698,17,53,-33,53 2 0256 registered
+char174 "
+a- 480,691,0,101,-127,73 2 0257 macron
+char175 "
+aa 340,771,0,117,-126,73 2 0264 acute
+char180 "
+ps 680,681,204,96,-20,73 3 0266 paragraph
+char182 "
+char183 340,358,0,26,-76,26 0 0267 periodcentered
+ac 360,0,220,0,-18 1 0270 cedilla
+char184 "
+Om 440,685,0,77,3,73 2 0272 ordmasculine
+char186 "
+fc 220,503,0,79,-12,73 0 0273 guilsinglright
+r? 620,515,189,36,-33,36 1 0277 questiondown
+char191 "
+`A 720,937,0,99,77,73 2 0300 Agrave
+char192 "
+'A 720,937,0,99,77,73 2 0301 Aacute
+char193 "
+^A 720,915,0,99,77,73 2 0302 Acircumflex
+char194 "
+~A 720,875,0,99,77,73 2 0303 Atilde
+char195 "
+:A 720,900,0,99,77,73 2 0304 Adieresis
+char196 "
+oA 720,941,0,99,77,73 2 0305 Aring
+char197 "
+AE 1140,681,0,117,77,73 2 0306 AE
+char198 "
+,C 700,698,220,104,-28,73 3 0307 Ccedilla
+char199 "
+`E 720,937,0,107,36,73 2 0310 Egrave
+char200 "
+'E 720,937,0,107,36,73 2 0311 Eacute
+char201 "
+^E 720,915,0,107,36,73 2 0312 Ecircumflex
+char202 "
+:E 720,900,0,107,36,73 2 0313 Edieresis
+char203 "
+`I 380,937,0,155,36,73 2 0314 Igrave
+char204 "
+'I 380,937,0,155,36,73 2 0315 Iacute
+char205 "
+^I 380,915,0,163,36,73 2 0316 Icircumflex
+char206 "
+:I 380,900,0,169,36,73 2 0317 Idieresis
+char207 "
+~N 740,875,0,155,36,73 2 0321 Ntilde
+char209 "
+`O 760,937,17,96,-28,73 2 0322 Ograve
+char210 "
+'O 760,937,17,96,-28,73 2 0323 Oacute
+char211 "
+^O 760,915,17,96,-28,73 2 0324 Ocircumflex
+char212 "
+~O 760,875,17,96,-28,73 2 0325 Otilde
+char213 "
+:O 760,900,17,96,-28,73 2 0326 Odieresis
+char214 "
+/O 760,725,29,137,29,73 2 0330 Oslash
+char216 "
+`U 740,937,17,165,-62,73 2 0331 Ugrave
+char217 "
+'U 740,937,17,165,-62,73 2 0332 Uacute
+char218 "
+^U 740,915,17,165,-62,73 2 0333 Ucircumflex
+char219 "
+:U 740,900,17,165,-62,73 2 0334 Udieresis
+char220 "
+ss 660,741,213,92,281,73 3 0337 germandbls
+char223 "
+`a 680,771,8,105,-34,73 2 0340 agrave
+char224 "
+'a 680,771,8,105,-34,73 2 0341 aacute
+char225 "
+^a 680,749,8,105,-34,73 2 0342 acircumflex
+char226 "
+~a 680,709,8,105,-34,73 2 0343 atilde
+char227 "
+:a 680,734,8,105,-34,73 2 0344 adieresis
+char228 "
+oa 680,775,8,105,-34,73 2 0345 aring
+char229 "
+ae 880,515,8,83,11,73 0 0346 ae
+char230 "
+,c 560,515,220,87,-8,73 1 0347 ccedilla
+char231 "
+`e 560,771,8,86,-9,73 2 0350 egrave
+char232 "
+'e 560,771,8,86,-9,73 2 0351 eacute
+char233 "
+^e 560,749,8,86,-9,73 2 0352 ecircumflex
+char234 "
+:e 560,734,8,86,-9,73 2 0353 edieresis
+char235 "
+`i 380,771,8,94,-33,73 2 0354 igrave
+char236 "
+'i 380,771,8,90,-33,73 2 0355 iacute
+char237 "
+^i 380,749,8,103,-33,73 2 0356 icircumflex
+char238 "
+:i 380,734,8,149,-33,73 2 0357 idieresis
+char239 "
+~n 680,709,8,85,-33,73 2 0361 ntilde
+char241 "
+`o 600,771,8,77,-9,73 2 0362 ograve
+char242 "
+'o 600,771,8,77,-9,73 2 0363 oacute
+char243 "
+^o 600,749,8,77,-9,73 2 0364 ocircumflex
+char244 "
+~o 600,709,8,77,-9,73 2 0365 otilde
+char245 "
+:o 600,734,8,77,-9,73 2 0366 odieresis
+char246 "
+/o 600,571,54,111,33,73 2 0370 oslash
+char248 "
+`u 680,771,8,90,-33,73 2 0371 ugrave
+char249 "
+'u 680,771,8,90,-33,73 2 0372 uacute
+char250 "
+^u 680,749,8,90,-33,73 2 0373 ucircumflex
+char251 "
+:u 680,734,8,90,-33,73 2 0374 udieresis
+char252 "
+:y 600,734,213,92,25,73 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/BMI b/ps/devps/BMI
new file mode 100644
index 000000000..bccda195b
--- /dev/null
+++ b/ps/devps/BMI
@@ -0,0 +1,409 @@
+name BMI
+internalname Bookman-LightItalic
+slant 10
+spacewidth 300
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y 0
+A w 0
+A v 0
+A Y -62
+A W -73
+A V -78
+A T -5
+F . -97
+F , -98
+F A -16
+L y 20
+L Y 7
+L W 9
+L V 4
+L T 0
+P . -105
+P , -106
+P A -30
+R y 0
+R Y 11
+R W 2
+R V 2
+R T 65
+T y 0
+T w 0
+T u 0
+T ; 48
+T s -7
+T r 67
+T . -78
+T o 14
+T i 71
+T - 20
+T hy 20
+T char173 20
+T e 10
+T , -79
+T : 48
+T c 16
+T a 9
+T A -14
+V y -14
+V u -10
+V ; -44
+V r -20
+V . -100
+V o -70
+V i 3
+V - 20
+V hy 20
+V char173 20
+V e -70
+V , -109
+V : -35
+V a -70
+V A -70
+W y -14
+W u -20
+W r -30
+W o -60
+W i 3
+W ; -42
+W . -100
+W - 20
+W hy 20
+W char173 20
+W e -60
+W , -109
+W : -35
+W a -60
+W A -60
+Y v -19
+Y u -31
+Y ; -40
+Y q -72
+Y . -100
+Y p -37
+Y o -75
+Y i -11
+Y - 20
+Y hy 20
+Y char173 20
+Y e -78
+Y , -109
+Y : -35
+Y a -79
+Y A -82
+f f -19
+r q -14
+r . -134
+r o -10
+r n 38
+r m 37
+r - 20
+r hy 20
+r char173 20
+r h -20
+r g -3
+r f -9
+r e -15
+r d -9
+r , -143
+r c -8
+charset
+ha 600,681,0,49,-47,49 2 0000 asciicircum
+ti 600,386,0,45,-41,45 0 0001 asciitilde
+vS 640,871,17,78,-11,70 2 0002 Scaron
+vZ 580,871,0,165,42,70 2 0003 Zcaron
+vs 540,684,8,57,-15,57 2 0004 scaron
+vz 520,684,8,91,12,70 2 0005 zcaron
+:Y 660,875,0,199,-37,70 2 0006 Ydieresis
+tm 980,681,0,35,-19,35 2 0007 trademark
+space 300 0 0040
+! 320,698,8,72,-53,70 2 0041 exclam
+" 360,699,0,92,-57,70 2 0042 quotedbl
+# 600,681,0,38,-47,38 2 0043 numbersign
+sh "
+$ 620,762,85,49,-28,49 3 0044 dollar
+Do "
+% 800,691,8,61,-6,61 2 0045 percent
+& 820,698,18,78,-15,70 2 0046 ampersand
+' 280,698,0,58,-98,58 2 0047 quoteright
+( 280,727,146,153,-46,70 3 0050 parenleft
+) 280,727,146,49,58,49 3 0051 parenright
+* 440,698,0,115,-89,70 2 0052 asterisk
++ 600,548,0,45,-41,45 2 0053 plus
+, 300,112,115,0,-38 1 0054 comma
+- 320,325,0,49,-28,49 0 0055 hyphen
+hy "
+char173 "
+. 300,127,8,0,-46 0 0056 period
+/ 600,717,149,12,-54,12 3 0057 slash
+sl "
+0 620,698,17,76,-36,70 2 0060 zero
+1 620,681,0,0,-104 2 0061 one
+2 620,698,0,66,-16,66 2 0062 two
+3 620,698,17,52,-5,52 2 0063 three
+4 620,681,0,64,-19,64 2 0064 four
+5 620,681,17,44,-20,44 2 0065 five
+6 620,698,17,87,-39,70 2 0066 six
+7 620,681,0,102,-93,70 2 0067 seven
+8 620,698,17,85,-11,70 2 0070 eight
+9 620,698,17,79,-27,70 2 0071 nine
+: 300,494,8,42,-46,42 0 0072 colon
+; 300,494,114,42,-38,42 1 0073 semicolon
+< 600,561,0,38,-29,38 2 0074 less
+= 600,433,0,45,-41,45 0 0075 equal
+> 600,561,0,52,-43,52 2 0076 greater
+? 540,698,8,114,-64,70 2 0077 question
+@ 780,698,17,47,-27,47 2 0100 at
+at "
+A 700,681,0,70,75,70 2 0101 A
+B 720,681,0,76,29,70 2 0102 B
+C 720,698,17,76,-38,70 2 0103 C
+D 740,681,0,92,29,70 2 0104 D
+E 680,681,0,106,29,70 2 0105 E
+F 620,681,0,173,29,70 2 0106 F
+G 760,698,17,103,-38,70 2 0107 G
+H 800,681,0,138,29,70 2 0110 H
+I 320,681,0,142,29,70 2 0111 I
+J 560,681,17,156,52,70 2 0112 J
+K 720,681,0,134,29,70 2 0113 K
+L 580,681,0,126,29,70 2 0114 L
+M 860,681,0,146,32,70 2 0115 M
+N 720,681,0,153,32,70 2 0116 N
+O 760,698,17,89,-38,70 2 0117 O
+P 600,681,0,131,29,70 2 0120 P
+Q 780,698,191,82,-11,70 3 0121 Q
+R 700,681,0,86,29,70 2 0122 R
+S 640,698,17,78,-11,70 2 0123 S
+T 600,681,0,175,0,70 2 0124 T
+U 720,681,17,172,-68,70 2 0125 U
+V 680,681,0,185,-37,70 2 0126 V
+W 960,681,0,185,-37,70 2 0127 W
+X 700,681,0,165,75,70 2 0130 X
+Y 660,681,0,199,-37,70 2 0131 Y
+Z 580,681,0,165,42,70 2 0132 Z
+[ 260,717,136,141,-6,70 3 0133 bracketleft
+lB "
+\ 600,717,0,0,-34 2 0134 backslash
+rs "
+] 260,717,136,99,35,70 3 0135 bracketright
+rB "
+a^ 440,685,0,89,-126,70 2 0136 circumflex
+^ "
+_ 500,0,140,50,50,50 1 0137 underscore
+` 280,698,0,100,-141,70 2 0140 quoteleft
+oq "
+a 620,494,8,116,-21,70 0 0141 a
+b 600,717,8,71,-38,70 2 0142 b
+c 480,494,8,92,-15,70 0 0143 c
+d 640,717,8,105,-15,70 2 0144 d
+e 540,494,8,85,-15,70 0 0145 e
+f 340,725,218,267,210,70 3 0146 f
+g 560,494,221,71,46,70 1 0147 g
+h 620,717,8,119,-38,70 2 0150 h
+i 280,663,8,121,-38,70 2 0151 i
+j 280,663,221,78,250,70 3 0152 j
+k 600,717,8,107,-38,70 2 0153 k
+l 280,717,8,112,-50,70 2 0154 l
+m 880,494,8,122,-38,70 0 0155 m
+n 620,494,8,103,-38,70 0 0156 n
+o 540,494,8,82,-15,70 0 0157 o
+p 600,494,212,70,74,70 1 0160 p
+q 560,494,212,74,-15,70 1 0161 q
+r 400,494,0,131,-38,70 0 0162 r
+s 540,494,8,57,-15,57 0 0163 s
+t 340,664,8,121,-38,70 2 0164 t
+u 620,484,8,116,-38,70 0 0165 u
+v 540,494,8,72,-38,70 0 0166 v
+w 880,494,8,63,-38,63 0 0167 w
+x 540,494,8,136,41,70 0 0170 x
+y 600,484,221,59,-10,59 1 0171 y
+z 520,494,8,91,12,70 0 0172 z
+lC 360,717,191,129,-75,70 3 0173 braceleft
+{ "
+ba 600,717,0,0,-244 2 0174 bar
+| "
+rC 380,717,191,0,35 3 0175 braceright
+} "
+a~ 440,671,0,98,-130,70 2 0176 tilde
+~ "
+--- 320,113,114,0,-37 1 0200 quotesinglbase
+Fo 300,434,0,63,-20,63 0 0201 guillemotleft
+char171 "
+Fc 300,434,0,53,-10,53 0 0202 guillemotright
+char187 "
+bu 460,511,0,34,-50,34 0 0203 bullet
+--- 620,725,218,122,76,70 3 0204 florin
+f/ 20,681,0,353,278,70 2 0205 fraction
+%0 1180,691,8,69,-6,69 2 0206 perthousand
+dg 620,698,130,0,-142 3 0207 dagger
+dd 620,698,122,0,-94 3 0210 daggerdbl
+en 500,325,0,80,-36,70 0 0211 endash
+em 1000,325,0,0,-68 0 0212 emdash
+fi 640,725,222,119,209,70 3 0214 fi
+fl 660,725,218,103,209,70 3 0215 fl
+.i 280,484,8,121,-38,70 0 0220 dotlessi
+ga 340,706,0,87,-132,70 2 0222 grave
+a" 340,738,0,112,-117,70 2 0223 hungarumlaut
+a. 260,664,0,80,-119,70 2 0224 dotaccent
+ab 440,680,0,110,-141,70 2 0225 breve
+ah 440,684,0,91,-128,70 2 0226 caron
+ao 300,706,0,84,-128,70 2 0227 ring
+ho 260,0,173,0,-1 1 0230 ogonek
+lq 440,698,0,103,-141,70 2 0231 quotedblleft
+rq 440,698,0,61,-98,61 2 0232 quotedblright
+oe 900,494,8,98,-15,70 0 0233 oe
+/l 340,717,8,108,0,70 2 0234 lslash
+--- 480,113,114,0,-37 1 0235 quotedblbase
+OE 1180,698,17,107,-38,70 2 0236 OE
+/L 580,681,0,126,29,70 2 0237 Lslash
+r! 320,494,213,31,-23,31 1 0241 exclamdown
+char161 "
+ct 620,715,29,26,-98,26 2 0242 cent
+char162 "
+Po 620,698,17,132,46,70 2 0243 sterling
+char163 "
+Cs 600,591,0,45,-40,45 2 0244 currency
+char164 "
+Ye 620,681,0,165,-21,70 2 0245 yen
+char165 "
+sc 620,698,178,68,12,68 3 0247 section
+char167 "
+ad 420,688,0,97,-135,70 2 0250 dieresis
+char168 "
+co 740,698,17,49,11,49 2 0251 copyright
+char169 "
+Of 440,698,0,123,-44,70 2 0252 ordfeminine
+char170 "
+fo 180,434,0,78,-25,70 0 0253 guilsinglleft
+no 600,433,0,45,-41,45 0 0254 logicalnot
+char172 "
+\- 600,335,0,45,-41,45 0 0255 minus
+rg 740,698,17,49,11,49 2 0256 registered
+char174 "
+a- 440,658,0,94,-128,70 2 0257 macron
+char175 "
+aa 320,706,0,103,-128,70 2 0264 acute
+char180 "
+ps 620,681,0,148,-62,70 2 0266 paragraph
+char182 "
+char183 300,364,0,41,-106,41 0 0267 periodcentered
+ac 320,0,178,0,5 1 0270 cedilla
+char184 "
+Om 400,698,0,105,-44,70 2 0272 ordmasculine
+char186 "
+fc 180,434,0,73,-20,70 0 0273 guilsinglright
+r? 540,494,212,18,32,18 1 0277 questiondown
+char191 "
+`A 700,893,0,70,75,70 2 0300 Agrave
+char192 "
+'A 700,893,0,70,75,70 2 0301 Aacute
+char193 "
+^A 700,872,0,70,75,70 2 0302 Acircumflex
+char194 "
+~A 700,858,0,70,75,70 2 0303 Atilde
+char195 "
+:A 700,875,0,70,75,70 2 0304 Adieresis
+char196 "
+oA 700,893,0,70,75,70 2 0305 Aring
+char197 "
+AE 1220,681,0,99,95,70 2 0306 AE
+char198 "
+,C 720,698,178,76,-38,70 3 0307 Ccedilla
+char199 "
+`E 680,893,0,106,29,70 2 0310 Egrave
+char200 "
+'E 680,893,0,106,29,70 2 0311 Eacute
+char201 "
+^E 680,872,0,106,29,70 2 0312 Ecircumflex
+char202 "
+:E 680,875,0,106,29,70 2 0313 Edieresis
+char203 "
+`I 320,893,0,142,29,70 2 0314 Igrave
+char204 "
+'I 320,893,0,142,29,70 2 0315 Iacute
+char205 "
+^I 320,872,0,142,29,70 2 0316 Icircumflex
+char206 "
+:I 320,875,0,142,29,70 2 0317 Idieresis
+char207 "
+~N 720,858,0,153,32,70 2 0321 Ntilde
+char209 "
+`O 760,893,17,89,-38,70 2 0322 Ograve
+char210 "
+'O 760,893,17,89,-38,70 2 0323 Oacute
+char211 "
+^O 760,872,17,89,-38,70 2 0324 Ocircumflex
+char212 "
+~O 760,858,17,89,-38,70 2 0325 Otilde
+char213 "
+:O 760,875,17,89,-38,70 2 0326 Odieresis
+char214 "
+/O 760,777,95,89,-38,70 3 0330 Oslash
+char216 "
+`U 720,893,17,172,-68,70 2 0331 Ugrave
+char217 "
+'U 720,893,17,172,-68,70 2 0332 Uacute
+char218 "
+^U 720,872,17,172,-68,70 2 0333 Ucircumflex
+char219 "
+:U 720,875,17,172,-68,70 2 0334 Udieresis
+char220 "
+ss 620,698,111,83,171,70 3 0337 germandbls
+char223 "
+`a 620,706,8,116,-21,70 2 0340 agrave
+char224 "
+'a 620,706,8,116,-21,70 2 0341 aacute
+char225 "
+^a 620,685,8,116,-21,70 2 0342 acircumflex
+char226 "
+~a 620,671,8,116,-21,70 2 0343 atilde
+char227 "
+:a 620,688,8,116,-21,70 2 0344 adieresis
+char228 "
+oa 620,706,8,116,-21,70 2 0345 aring
+char229 "
+ae 880,494,8,88,-21,70 0 0346 ae
+char230 "
+,c 480,494,178,92,-15,70 1 0347 ccedilla
+char231 "
+`e 540,706,8,85,-15,70 2 0350 egrave
+char232 "
+'e 540,706,8,85,-15,70 2 0351 eacute
+char233 "
+^e 540,685,8,85,-15,70 2 0352 ecircumflex
+char234 "
+:e 540,688,8,85,-15,70 2 0353 edieresis
+char235 "
+`i 280,706,8,121,-38,70 2 0354 igrave
+char236 "
+'i 280,706,8,121,-38,70 2 0355 iacute
+char237 "
+^i 280,685,8,139,-16,70 2 0356 icircumflex
+char238 "
+:i 280,688,8,127,-25,70 2 0357 idieresis
+char239 "
+~n 620,671,8,103,-38,70 2 0361 ntilde
+char241 "
+`o 540,706,8,82,-15,70 2 0362 ograve
+char242 "
+'o 540,706,8,82,-15,70 2 0363 oacute
+char243 "
+^o 540,685,8,82,-15,70 2 0364 ocircumflex
+char244 "
+~o 540,671,8,82,-15,70 2 0365 otilde
+char245 "
+:o 540,688,8,82,-15,70 2 0366 odieresis
+char246 "
+/o 540,532,49,81,-15,70 0 0370 oslash
+char248 "
+`u 620,706,8,116,-38,70 2 0371 ugrave
+char249 "
+'u 620,706,8,116,-38,70 2 0372 uacute
+char250 "
+^u 620,685,8,116,-38,70 2 0373 ucircumflex
+char251 "
+:u 620,688,8,116,-38,70 2 0374 udieresis
+char252 "
+:y 600,688,221,59,-10,59 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/BMR b/ps/devps/BMR
new file mode 100644
index 000000000..2a583ca8a
--- /dev/null
+++ b/ps/devps/BMR
@@ -0,0 +1,408 @@
+name BMR
+internalname Bookman-Light
+spacewidth 320
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y 32
+A w 4
+A v 7
+A Y -35
+A W -40
+A V -56
+A T 1
+F . -46
+F , -41
+F A -21
+L y 79
+L Y 13
+L W 1
+L V -4
+L T 28
+P . -60
+P , -55
+P A -8
+R y 59
+R Y 26
+R W 13
+R V 8
+R T 71
+T y 0
+T w 0
+T u 0
+T ; 0
+T s 16
+T r 38
+T . -33
+T o 15
+T i 42
+T - 90
+T hy 90
+T char173 90
+T e 13
+T , -28
+T : 0
+T c 14
+T a 17
+T A 1
+V y 15
+V u -38
+V ; 0
+V r -41
+V . -40
+V o -71
+V i -20
+V - 11
+V hy 11
+V char173 11
+V e -72
+V , -34
+V : 0
+V a -69
+V A -66
+W y 15
+W u -38
+W ; 0
+W r -41
+W . -40
+W o -68
+W i -20
+W - 11
+W hy 11
+W char173 11
+W e -69
+W , -34
+W : 0
+W a -66
+W A -64
+Y v 15
+Y u -38
+Y ; 0
+Y q -55
+Y . -40
+Y p -31
+Y o -57
+Y i -37
+Y - 11
+Y hy 11
+Y char173 11
+Y e -58
+Y , -34
+Y : 0
+Y a -54
+Y A -53
+f f 29
+r q 9
+r . -64
+r o 8
+r n 31
+r m 31
+r - 70
+r hy 70
+r char173 70
+r h -21
+r g -4
+r f 33
+r e 7
+r d 7
+r , -58
+r c 7
+charset
+ha 600,681 2 0000 asciicircum
+ti 600,352 0 0001 asciitilde
+vS 660,869,17 2 0002 Scaron
+vZ 640,869 2 0003 Zcaron
+vs 520,672,8 2 0004 scaron
+vz 480,672 2 0005 zcaron
+:Y 640,871 2 0006 Ydieresis
+tm 980,681 2 0007 trademark
+space 320 0 0040
+! 300,698,8 2 0041 exclam
+" 380,698 2 0042 quotedbl
+# 600,681 2 0043 numbersign
+sh "
+$ 620,791,109 3 0044 dollar
+Do "
+% 900,698,8 2 0045 percent
+& 800,698,17 2 0046 ampersand
+' 220,698 2 0047 quoteright
+( 300,727,145 3 0050 parenleft
+) 300,727,146 3 0051 parenright
+* 440,698 2 0052 asterisk
++ 600,513 0 0053 plus
+, 320,114,114 1 0054 comma
+- 400,292 0 0055 hyphen
+hy "
+char173 "
+. 320,123,8 0 0056 period
+/ 600,717,149 3 0057 slash
+sl "
+0 620,698,17 2 0060 zero
+1 620,681 2 0061 one
+2 620,698 2 0062 two
+3 620,698,17 2 0063 three
+4 620,681 2 0064 four
+5 620,717,17 2 0065 five
+6 620,698,17 2 0066 six
+7 620,681 2 0067 seven
+8 620,698,17 2 0070 eight
+9 620,698,17 2 0071 nine
+: 320,494,8 0 0072 colon
+; 320,494,114 1 0073 semicolon
+< 600,526,2 0 0074 less
+= 600,398 0 0075 equal
+> 600,526,2 0 0076 greater
+? 540,698,8 2 0077 question
+@ 820,698,17 2 0100 at
+at "
+A 680,681 2 0101 A
+B 740,681 2 0102 B
+C 740,698,17 2 0103 C
+D 800,681 2 0104 D
+E 720,681 2 0105 E
+F 640,681 2 0106 F
+G 800,698,17 2 0107 G
+H 800,681 2 0110 H
+I 340,681 2 0111 I
+J 600,681,17 2 0112 J
+K 720,681 2 0113 K
+L 600,681 2 0114 L
+M 920,681 2 0115 M
+N 740,681 2 0116 N
+O 800,698,17 2 0117 O
+P 620,681 2 0120 P
+Q 820,698,189 3 0121 Q
+R 720,681 2 0122 R
+S 660,698,17 2 0123 S
+T 620,681 2 0124 T
+U 780,681,17 2 0125 U
+V 700,681 2 0126 V
+W 960,681 2 0127 W
+X 720,681 2 0130 X
+Y 640,681 2 0131 Y
+Z 640,681 2 0132 Z
+[ 300,717,136 3 0133 bracketleft
+lB "
+\ 600,717 2 0134 backslash
+rs "
+] 300,717,136 3 0135 bracketright
+rB "
+a^ 420,685 2 0136 circumflex
+^ "
+_ 500,0,155 1 0137 underscore
+` 220,698 2 0140 quoteleft
+oq "
+a 580,494,8 0 0141 a
+b 620,717,8 2 0142 b
+c 520,494,8 0 0143 c
+d 620,717,8 2 0144 d
+e 520,494,8 0 0145 e
+f 320,734 2 0146 f
+g 540,567,243 3 0147 g
+h 660,717 2 0150 h
+i 300,654 2 0151 i
+j 300,654,251 3 0152 j
+k 620,717 2 0153 k
+l 300,717 2 0154 l
+m 940,494 0 0155 m
+n 660,494 0 0156 n
+o 560,494,8 0 0157 o
+p 620,494,228 1 0160 p
+q 580,494,228 1 0161 q
+r 440,494 0 0162 r
+s 520,494,8 0 0163 s
+t 380,667,8 2 0164 t
+u 680,484,8 0 0165 u
+v 520,484 0 0166 v
+w 780,484 0 0167 w
+x 560,484 0 0170 x
+y 540,484,236 1 0171 y
+z 480,484 0 0172 z
+lC 280,717,136 3 0173 braceleft
+{ "
+ba 600,717 2 0174 bar
+| "
+rC 280,717,136 3 0175 braceright
+} "
+a~ 440,661 2 0176 tilde
+~ "
+--- 220,110,108 1 0200 quotesinglbase
+Fo 360,437 0 0201 guillemotleft
+char171 "
+Fc 360,437 0 0202 guillemotright
+char187 "
+bu 460,511 0 0203 bullet
+--- 620,749,155 3 0204 florin
+f/ 140,681 2 0205 fraction
+%0 1280,698,8 2 0206 perthousand
+dg 540,698,156 3 0207 dagger
+dd 540,698,156 3 0210 daggerdbl
+en 500,292 0 0211 endash
+em 1000,292 0 0212 emdash
+fi 620,734 2 0214 fi
+fl 620,734 2 0215 fl
+.i 300,484 0 0220 dotlessi
+ga 340,689 2 0222 grave
+a" 380,698 2 0223 hungarumlaut
+a. 260,672 2 0224 dotaccent
+ab 460,687 2 0225 breve
+ah 420,672 2 0226 caron
+ao 320,731 2 0227 ring
+ho 320,0,145 1 0230 ogonek
+lq 400,698 2 0231 quotedblleft
+rq 400,698 2 0232 quotedblright
+oe 900,494,8 0 0233 oe
+/l 320,717 2 0234 lslash
+--- 400,110,108 1 0235 quotedblbase
+OE 1240,698,17 2 0236 OE
+/L 600,681 2 0237 Lslash
+r! 300,494,214 1 0241 exclamdown
+char161 "
+ct 620,651 2 0242 cent
+char162 "
+Po 620,698,17 2 0243 sterling
+char163 "
+Cs 600,591 2 0244 currency
+char164 "
+Ye 620,681 2 0245 yen
+char165 "
+sc 520,698,178 3 0247 section
+char167 "
+ad 420,674 2 0250 dieresis
+char168 "
+co 740,698,17 2 0251 copyright
+char169 "
+Of 420,698 2 0252 ordfeminine
+char170 "
+fo 240,437 0 0253 guilsinglleft
+no 600,398 0 0254 logicalnot
+char172 "
+\- 600,300 0 0255 minus
+rg 740,698,17 2 0256 registered
+char174 "
+a- 440,635 2 0257 macron
+char175 "
+aa 340,689 2 0264 acute
+char180 "
+ps 600,681 2 0266 paragraph
+char182 "
+char183 320,327 0 0267 periodcentered
+ac 320,0,200 1 0270 cedilla
+char184 "
+Om 420,698 2 0272 ordmasculine
+char186 "
+fc 240,437 0 0273 guilsinglright
+r? 540,494,217 1 0277 questiondown
+char191 "
+`A 680,886 2 0300 Agrave
+char192 "
+'A 680,886 2 0301 Aacute
+char193 "
+^A 680,882 2 0302 Acircumflex
+char194 "
+~A 680,858 2 0303 Atilde
+char195 "
+:A 680,871 2 0304 Adieresis
+char196 "
+oA 680,928 2 0305 Aring
+char197 "
+AE 1260,681 2 0306 AE
+char198 "
+,C 740,698,200 3 0307 Ccedilla
+char199 "
+`E 720,886 2 0310 Egrave
+char200 "
+'E 720,886 2 0311 Eacute
+char201 "
+^E 720,882 2 0312 Ecircumflex
+char202 "
+:E 720,871 2 0313 Edieresis
+char203 "
+`I 340,886 2 0314 Igrave
+char204 "
+'I 340,886 2 0315 Iacute
+char205 "
+^I 340,882 2 0316 Icircumflex
+char206 "
+:I 340,871 2 0317 Idieresis
+char207 "
+~N 740,858 2 0321 Ntilde
+char209 "
+`O 800,886,17 2 0322 Ograve
+char210 "
+'O 800,886,17 2 0323 Oacute
+char211 "
+^O 800,882,17 2 0324 Ocircumflex
+char212 "
+~O 800,858,17 2 0325 Otilde
+char213 "
+:O 800,871,17 2 0326 Odieresis
+char214 "
+/O 800,733,53 2 0330 Oslash
+char216 "
+`U 780,886,17 2 0331 Ugrave
+char217 "
+'U 780,886,17 2 0332 Uacute
+char218 "
+^U 780,882,17 2 0333 Ucircumflex
+char219 "
+:U 780,871,17 2 0334 Udieresis
+char220 "
+ss 660,698,110 3 0337 germandbls
+char223 "
+`a 580,689,8 2 0340 agrave
+char224 "
+'a 580,689,8 2 0341 aacute
+char225 "
+^a 580,685,8 2 0342 acircumflex
+char226 "
+~a 580,661,8 2 0343 atilde
+char227 "
+:a 580,674,8 2 0344 adieresis
+char228 "
+oa 580,731,8 2 0345 aring
+char229 "
+ae 860,494,8 0 0346 ae
+char230 "
+,c 520,494,200 1 0347 ccedilla
+char231 "
+`e 520,689,8 2 0350 egrave
+char232 "
+'e 520,689,8 2 0351 eacute
+char233 "
+^e 520,685,8 2 0352 ecircumflex
+char234 "
+:e 520,674,8 2 0353 edieresis
+char235 "
+`i 300,689 2 0354 igrave
+char236 "
+'i 300,689 2 0355 iacute
+char237 "
+^i 300,685 2 0356 icircumflex
+char238 "
+:i 300,674 2 0357 idieresis
+char239 "
+~n 660,661 2 0361 ntilde
+char241 "
+`o 560,689,8 2 0362 ograve
+char242 "
+'o 560,689,8 2 0363 oacute
+char243 "
+^o 560,685,8 2 0364 ocircumflex
+char244 "
+~o 560,661,8 2 0365 otilde
+char245 "
+:o 560,674,8 2 0366 odieresis
+char246 "
+/o 560,534,40 0 0370 oslash
+char248 "
+`u 680,689,8 2 0371 ugrave
+char249 "
+'u 680,689,8 2 0372 uacute
+char250 "
+^u 680,685,8 2 0373 ucircumflex
+char251 "
+:u 680,674,8 2 0374 udieresis
+char252 "
+:y 540,674,236 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/CB b/ps/devps/CB
new file mode 100644
index 000000000..357b76272
--- /dev/null
+++ b/ps/devps/CB
@@ -0,0 +1,286 @@
+name CB
+internalname Courier-Bold
+spacewidth 600
+encoding text.enc
+charset
+ha 600,674 2 0000 asciicircum
+ti 600,433 0 0001 asciitilde
+vS 600,824,65 2 0002 Scaron
+vZ 600,824,50 2 0003 Zcaron
+vs 600,695,65 2 0004 scaron
+vz 600,695,50 2 0005 zcaron
+:Y 600,781,50 2 0006 Ydieresis
+space 600 0 0040
+! 600,689,65 2 0041 exclam
+" 600,663 2 0042 quotedbl
+# 600,725,142 3 0043 numbersign
+sh "
+$ 600,735,173 3 0044 dollar
+Do "
+% 600,689,65 2 0045 percent
+& 600,600,65 2 0046 ampersand
+' 600,674 2 0047 quoteright
+( 600,683,204 3 0050 parenleft
+) 600,683,204 3 0051 parenright
+* 600,674 2 0052 asterisk
++ 600,610,48 2 0053 plus
+, 600,215,215 1 0054 comma
+- 600,381 0 0055 hyphen
+hy "
+char173 "
+. 600,150,50 0 0056 period
+/ 600,746,163 3 0057 slash
+sl "
+0 600,689,65 2 0060 zero
+1 600,674,50 2 0061 one
+2 600,689,50 2 0062 two
+3 600,689,65 2 0063 three
+4 600,674,50 2 0064 four
+5 600,674,65 2 0065 five
+6 600,689,65 2 0066 six
+7 600,674,50 2 0067 seven
+8 600,689,65 2 0070 eight
+9 600,689,65 2 0071 nine
+: 600,472,50 0 0072 colon
+; 600,472,176 1 0073 semicolon
+< 600,610,48 2 0074 less
+= 600,474 0 0075 equal
+> 600,610,48 2 0076 greater
+? 600,648,65 2 0077 question
+@ 600,705,142 3 0100 at
+at "
+A 600,633,50 2 0101 A
+B 600,633,50 2 0102 B
+C 600,648,65 2 0103 C
+D 600,633,50 2 0104 D
+E 600,633,50 2 0105 E
+F 600,633,50 2 0106 F
+G 600,648,65 2 0107 G
+H 600,633,50 2 0110 H
+I 600,633,50 2 0111 I
+J 600,633,65 2 0112 J
+K 600,633,50 2 0113 K
+L 600,633,50 2 0114 L
+M 600,633,50 2 0115 M
+N 600,633,50 2 0116 N
+O 600,648,65 2 0117 O
+P 600,633,50 2 0120 P
+Q 600,648,196 3 0121 Q
+R 600,633,50 2 0122 R
+S 600,648,65 2 0123 S
+T 600,633,50 2 0124 T
+U 600,633,65 2 0125 U
+V 600,633,50 2 0126 V
+W 600,633,50 2 0127 W
+X 600,633,50 2 0130 X
+Y 600,633,50 2 0131 Y
+Z 600,633,50 2 0132 Z
+[ 600,674,204 3 0133 bracketleft
+lB "
+\ 600,746,163 3 0134 backslash
+rs "
+] 600,674,204 3 0135 bracketright
+rB "
+a^ 600,674 2 0136 circumflex
+^ "
+_ 600,0,350 1 0137 underscore
+` 600,674 2 0140 quoteleft
+oq "
+a 600,502,65 0 0141 a
+b 600,674,65 2 0142 b
+c 600,502,65 0 0143 c
+d 600,674,65 2 0144 d
+e 600,502,65 0 0145 e
+f 600,674,50 2 0146 f
+g 600,502,257 1 0147 g
+h 600,674,50 2 0150 h
+i 600,725,50 2 0151 i
+j 600,725,257 3 0152 j
+k 600,674,50 2 0153 k
+l 600,674,50 2 0154 l
+m 600,502,50 0 0155 m
+n 600,502,50 0 0156 n
+o 600,502,65 0 0157 o
+p 600,502,257 1 0160 p
+q 600,502,257 1 0161 q
+r 600,501,50 0 0162 r
+s 600,502,65 0 0163 s
+t 600,642,65 2 0164 t
+u 600,487,65 0 0165 u
+v 600,487,50 0 0166 v
+w 600,487,50 0 0167 w
+x 600,487,50 0 0170 x
+y 600,487,257 1 0171 y
+z 600,487,50 0 0172 z
+lC 600,674,204 3 0173 braceleft
+{ "
+ba 600,674,204 3 0174 bar
+| "
+rC 600,674,204 3 0175 braceright
+} "
+a~ 600,640 2 0176 tilde
+~ "
+--- 600,204,225 1 0200 quotesinglbase
+Fo 600,487,50 0 0201 guillemotleft
+char171 "
+Fc 600,487,50 0 0202 guillemotright
+char187 "
+bu 600,406 0 0203 bullet
+--- 600,689,173 3 0204 florin
+f/ 600,552 0 0205 fraction
+dg 600,674,142 3 0207 dagger
+dd 600,674,142 3 0210 daggerdbl
+en 600,381 0 0211 endash
+em 600,381 0 0212 emdash
+.i 600,487,50 0 0220 dotlessi
+ga 600,689 2 0222 grave
+a" 600,689 2 0223 hungarumlaut
+a. 600,631 2 0224 dotaccent
+ab 600,674 2 0225 breve
+ah 600,674 2 0226 caron
+ao 600,694 2 0227 ring
+ho 600,100,225 1 0230 ogonek
+lq 600,678 2 0231 quotedblleft
+rq 600,678 2 0232 quotedblright
+/l 600,674,50 2 0234 lslash
+--- 600,199,199 1 0235 quotedblbase
+/L 600,633,50 2 0237 Lslash
+r! 600,475,257 1 0241 exclamdown
+char161 "
+ct 600,725,79 2 0242 cent
+char162 "
+Po 600,648,50 2 0243 sterling
+char163 "
+Cs 600,569 2 0244 currency
+char164 "
+Ye 600,633,50 2 0245 yen
+char165 "
+sc 600,689,147 3 0247 section
+char167 "
+ad 600,631 2 0250 dieresis
+char168 "
+Of 600,649 2 0252 ordfeminine
+char170 "
+fo 600,487,50 0 0253 guilsinglleft
+a- 600,616 2 0257 macron
+char175 "
+aa 600,689 2 0264 acute
+char180 "
+ps 600,689,147 3 0266 paragraph
+char182 "
+char183 600,406 0 0267 periodcentered
+ac 600,100,246 1 0270 cedilla
+char184 "
+Om 600,649 2 0272 ordmasculine
+char186 "
+fc 600,487,50 0 0273 guilsinglright
+r? 600,475,257 1 0277 questiondown
+char191 "
+`A 600,839,50 2 0300 Agrave
+char192 "
+'A 600,839,50 2 0301 Aacute
+char193 "
+^A 600,824,50 2 0302 Acircumflex
+char194 "
+~A 600,790,50 2 0303 Atilde
+char195 "
+:A 600,781,50 2 0304 Adieresis
+char196 "
+oA 600,855,50 2 0305 Aring
+char197 "
+,C 600,648,246 3 0307 Ccedilla
+char199 "
+`E 600,839,50 2 0310 Egrave
+char200 "
+'E 600,839,50 2 0311 Eacute
+char201 "
+^E 600,824,50 2 0312 Ecircumflex
+char202 "
+:E 600,781,50 2 0313 Edieresis
+char203 "
+`I 600,839,50 2 0314 Igrave
+char204 "
+'I 600,839,50 2 0315 Iacute
+char205 "
+^I 600,824,50 2 0316 Icircumflex
+char206 "
+:I 600,781,50 2 0317 Idieresis
+char207 "
+~N 600,790,50 2 0321 Ntilde
+char209 "
+`O 600,839,65 2 0322 Ograve
+char210 "
+'O 600,839,65 2 0323 Oacute
+char211 "
+^O 600,824,65 2 0324 Ocircumflex
+char212 "
+~O 600,790,65 2 0325 Otilde
+char213 "
+:O 600,781,65 2 0326 Odieresis
+char214 "
+/O 600,683,121 3 0330 Oslash
+char216 "
+`U 600,839,65 2 0331 Ugrave
+char217 "
+'U 600,839,65 2 0332 Uacute
+char218 "
+^U 600,824,65 2 0333 Ucircumflex
+char219 "
+:U 600,781,65 2 0334 Udieresis
+char220 "
+ss 600,674,65 2 0337 germandbls
+char223 "
+`a 600,710,65 2 0340 agrave
+char224 "
+'a 600,710,65 2 0341 aacute
+char225 "
+^a 600,703,65 2 0342 acircumflex
+char226 "
+~a 600,678,65 2 0343 atilde
+char227 "
+:a 600,652,65 2 0344 adieresis
+char228 "
+oa 600,746,65 2 0345 aring
+char229 "
+,c 600,502,246 1 0347 ccedilla
+char231 "
+`e 600,714,65 2 0350 egrave
+char232 "
+'e 600,714,65 2 0351 eacute
+char233 "
+^e 600,703,65 2 0352 ecircumflex
+char234 "
+:e 600,652,65 2 0353 edieresis
+char235 "
+`i 600,706,50 2 0354 igrave
+char236 "
+'i 600,710,50 2 0355 iacute
+char237 "
+^i 600,684,50 2 0356 icircumflex
+char238 "
+:i 600,652,50 2 0357 idieresis
+char239 "
+~n 600,678,50 2 0361 ntilde
+char241 "
+`o 600,699,65 2 0362 ograve
+char242 "
+'o 600,699,65 2 0363 oacute
+char243 "
+^o 600,703,65 2 0364 ocircumflex
+char244 "
+~o 600,657,65 2 0365 otilde
+char245 "
+:o 600,652,65 2 0366 odieresis
+char246 "
+/o 600,538,121 1 0370 oslash
+char248 "
+`u 600,706,65 2 0371 ugrave
+char249 "
+'u 600,706,65 2 0372 uacute
+char250 "
+^u 600,684,65 2 0373 ucircumflex
+char251 "
+:u 600,652,65 2 0374 udieresis
+char252 "
+:y 600,631,257 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/CBI b/ps/devps/CBI
new file mode 100644
index 000000000..6fe267dc4
--- /dev/null
+++ b/ps/devps/CBI
@@ -0,0 +1,287 @@
+name CBI
+internalname Courier-BoldOblique
+slant 12
+spacewidth 600
+encoding text.enc
+charset
+ha 600,674,0,97,-63,83 2 0000 asciicircum
+ti 600,433,0,102,-17,83 0 0001 asciitilde
+vS 600,824,65,130,27,83 2 0002 Scaron
+vZ 600,824,50,129,16,83 2 0003 Zcaron
+vs 600,695,65,101,16,83 2 0004 scaron
+vz 600,695,50,101,4,83 2 0005 zcaron
+:Y 600,781,50,192,-26,83 2 0006 Ydieresis
+space 600 0 0040
+! 600,689,65,0,-147 2 0041 exclam
+" 600,663,0,104,-121,83 2 0042 quotedbl
+# 600,725,142,122,-2,83 3 0043 numbersign
+sh "
+$ 600,735,173,109,-1,83 3 0044 dollar
+Do "
+% 600,689,65,121,-8,83 2 0045 percent
+& 600,600,65,57,-2,57 2 0046 ampersand
+' 600,674,0,0,-98 2 0047 quoteright
+( 600,683,204,112,-205,83 3 0050 parenleft
+) 600,683,204,0,5 3 0051 parenright
+* 600,674,0,115,-81,83 2 0052 asterisk
++ 600,610,48,118,-2,83 2 0053 plus
+, 600,215,215,0,-1 1 0054 comma
+- 600,381,0,118,-2,83 0 0055 hyphen
+hy "
+char173 "
+. 600,150,50,0,-151 0 0056 period
+/ 600,746,163,154,30,83 3 0057 slash
+sl "
+0 600,689,65,99,-32,83 2 0060 zero
+1 600,674,50,28,6,28 2 0061 one
+2 600,689,50,106,35,83 2 0062 two
+3 600,689,65,109,20,83 2 0063 three
+4 600,674,50,68,-15,68 2 0064 four
+5 600,674,65,110,17,83 2 0065 five
+6 600,689,65,162,-58,83 2 0066 six
+7 600,674,50,130,-86,83 2 0067 seven
+8 600,689,65,105,-14,83 2 0070 eight
+9 600,689,65,115,-17,83 2 0071 nine
+: 600,472,50,0,-151 0 0072 colon
+; 600,472,176,0,-13 1 0073 semicolon
+< 600,610,48,166,-2,83 2 0074 less
+= 600,474,0,158,39,83 0 0075 equal
+> 600,610,48,118,47,83 2 0076 greater
+? 600,648,65,107,-98,83 2 0077 question
+@ 600,705,142,90,-11,83 3 0100 at
+at "
+A 600,633,50,132,110,83 2 0101 A
+B 600,633,50,120,76,83 2 0102 B
+C 600,648,65,163,19,83 2 0103 C
+D 600,633,50,120,76,83 2 0104 D
+E 600,633,50,142,76,83 2 0105 E
+F 600,633,50,163,76,83 2 0106 F
+G 600,648,65,163,21,83 2 0107 G
+H 600,633,50,173,66,83 2 0110 H
+I 600,633,50,130,6,83 2 0111 I
+J 600,633,65,226,28,83 2 0112 J
+K 600,633,50,194,76,83 2 0113 K
+L 600,633,50,115,56,83 2 0114 L
+M 600,633,50,226,108,83 2 0115 M
+N 600,633,50,205,76,83 2 0116 N
+O 600,648,65,146,24,83 2 0117 O
+P 600,633,50,116,76,83 2 0120 P
+Q 600,648,196,146,24,83 3 0121 Q
+R 600,633,50,130,76,83 2 0122 R
+S 600,648,65,130,27,83 2 0123 S
+T 600,633,50,171,-22,83 2 0124 T
+U 600,633,65,203,-11,83 2 0125 U
+V 600,633,50,234,8,83 2 0126 V
+W 600,633,50,223,0,83 2 0127 W
+X 600,633,50,192,79,83 2 0130 X
+Y 600,633,50,192,-26,83 2 0131 Y
+Z 600,633,50,119,16,83 2 0132 Z
+[ 600,674,204,97,-128,83 3 0133 bracketleft
+lB "
+\ 600,746,163,4,-120,4 3 0134 backslash
+rs "
+] 600,674,204,0,-3 3 0135 bracketright
+rB "
+a^ 600,674,0,79,-129,79 2 0136 circumflex
+^ "
+_ 600,0,350,89,195,83 1 0137 underscore
+` 600,674,0,48,-272,48 2 0140 quoteleft
+oq "
+a 600,502,65,82,34,82 0 0141 a
+b 600,674,65,120,97,83 2 0142 b
+c 600,502,65,122,6,83 0 0143 c
+d 600,674,65,151,27,83 2 0144 d
+e 600,502,65,100,25,83 0 0145 e
+f 600,674,50,190,14,83 2 0146 f
+g 600,502,257,174,25,83 1 0147 g
+h 600,674,50,92,66,83 2 0150 h
+i 600,725,50,49,27,49 2 0151 i
+j 600,725,257,70,16,70 3 0152 j
+k 600,674,50,111,56,83 2 0153 k
+l 600,674,50,49,27,49 2 0154 l
+m 600,502,50,134,108,83 0 0155 m
+n 600,502,50,82,66,82 0 0156 n
+o 600,502,65,106,16,83 0 0157 o
+p 600,502,257,121,141,83 1 0160 p
+q 600,502,257,195,23,83 1 0161 q
+r 600,501,50,149,35,83 0 0162 r
+s 600,502,65,88,16,83 0 0163 s
+t 600,642,65,49,5,49 2 0164 t
+u 600,487,65,90,5,83 0 0165 u
+v 600,487,50,182,18,83 0 0166 v
+w 600,487,50,182,18,83 0 0167 w
+x 600,487,50,140,68,83 0 0170 x
+y 600,487,257,161,112,83 1 0171 y
+z 600,487,50,90,4,83 0 0172 z
+lC 600,674,204,55,-118,55 3 0173 braceleft
+{ "
+ba 600,674,204,0,-128 3 0174 bar
+| "
+rC 600,674,204,0,-45 3 0175 braceright
+} "
+a~ 600,640,0,96,-121,83 2 0176 tilde
+~ "
+--- 600,204,225,0,2 1 0200 quotesinglbase
+Fo 600,487,50,153,23,83 0 0201 guillemotleft
+char171 "
+Fc 600,487,50,115,56,83 0 0202 guillemotright
+char187 "
+bu 600,406,0,0,-215 0 0203 bullet
+--- 600,689,173,170,56,83 3 0204 florin
+f/ 600,552,0,175,45,83 0 0205 fraction
+dg 600,674,142,88,-76,83 3 0207 dagger
+dd 600,674,142,88,-25,83 3 0210 daggerdbl
+en 600,381,0,118,-2,83 0 0211 endash
+em 600,381,0,189,69,83 0 0212 emdash
+.i 600,487,50,49,27,49 0 0220 dotlessi
+ga 600,689,0,0,-150 2 0222 grave
+a" 600,689,0,100,-129,83 2 0223 hungarumlaut
+a. 600,631,0,0,-263 2 0224 dotaccent
+ab 600,674,0,97,-146,83 2 0225 breve
+ah 600,674,0,97,-147,83 2 0226 caron
+ao 600,694,0,36,-187,36 2 0227 ring
+ho 600,100,225,0,-134 1 0230 ogonek
+lq 600,678,0,98,-106,83 2 0231 quotedblleft
+rq 600,678,0,140,-64,83 2 0232 quotedblright
+/l 600,674,50,54,27,54 2 0234 lslash
+--- 600,199,199,38,38,38 1 0235 quotedblbase
+/L 600,633,50,115,56,83 2 0237 Lslash
+r! 600,475,257,0,-93 1 0241 exclamdown
+char161 "
+ct 600,725,79,93,-46,83 2 0242 cent
+char162 "
+Po 600,648,50,70,35,70 2 0243 sterling
+char163 "
+Cs 600,569,0,127,3,83 2 0244 currency
+char164 "
+Ye 600,633,50,192,-27,83 2 0245 yen
+char165 "
+sc 600,689,147,147,32,83 3 0247 section
+char167 "
+ad 600,631,0,67,-159,67 2 0250 dieresis
+char168 "
+Of 600,649,0,40,-64,40 2 0252 ordfeminine
+char170 "
+fo 600,487,50,0,23 0 0253 guilsinglleft
+a- 600,616,0,85,-135,83 2 0257 macron
+char175 "
+aa 600,689,0,100,-254,83 2 0264 acute
+char180 "
+ps 600,689,147,180,-22,83 3 0266 paragraph
+char182 "
+char183 600,406,0,0,-205 0 0267 periodcentered
+ac 600,100,246,0,-53 1 0270 cedilla
+char184 "
+Om 600,649,0,73,-68,73 2 0272 ordmasculine
+char186 "
+fc 600,487,50,115,-174,83 0 0273 guilsinglright
+r? 600,475,257,0,24 1 0277 questiondown
+char191 "
+`A 600,839,50,132,110,83 2 0300 Agrave
+char192 "
+'A 600,839,50,132,110,83 2 0301 Aacute
+char193 "
+^A 600,824,50,132,110,83 2 0302 Acircumflex
+char194 "
+~A 600,790,50,132,110,83 2 0303 Atilde
+char195 "
+:A 600,781,50,132,110,83 2 0304 Adieresis
+char196 "
+oA 600,855,50,132,110,83 2 0305 Aring
+char197 "
+,C 600,648,246,163,19,83 3 0307 Ccedilla
+char199 "
+`E 600,839,50,142,76,83 2 0310 Egrave
+char200 "
+'E 600,839,50,142,76,83 2 0311 Eacute
+char201 "
+^E 600,824,50,142,76,83 2 0312 Ecircumflex
+char202 "
+:E 600,781,50,142,76,83 2 0313 Edieresis
+char203 "
+`I 600,839,50,130,6,83 2 0314 Igrave
+char204 "
+'I 600,839,50,130,6,83 2 0315 Iacute
+char205 "
+^I 600,824,50,130,6,83 2 0316 Icircumflex
+char206 "
+:I 600,781,50,130,6,83 2 0317 Idieresis
+char207 "
+~N 600,790,50,205,76,83 2 0321 Ntilde
+char209 "
+`O 600,839,65,146,24,83 2 0322 Ograve
+char210 "
+'O 600,839,65,146,24,83 2 0323 Oacute
+char211 "
+^O 600,824,65,146,24,83 2 0324 Ocircumflex
+char212 "
+~O 600,790,65,146,24,83 2 0325 Otilde
+char213 "
+:O 600,781,65,146,24,83 2 0326 Odieresis
+char214 "
+/O 600,683,121,214,94,83 3 0330 Oslash
+char216 "
+`U 600,839,65,203,-11,83 2 0331 Ugrave
+char217 "
+'U 600,839,65,203,-11,83 2 0332 Uacute
+char218 "
+^U 600,824,65,203,-11,83 2 0333 Ucircumflex
+char219 "
+:U 600,781,65,203,-11,83 2 0334 Udieresis
+char220 "
+ss 600,674,65,68,76,68 2 0337 germandbls
+char223 "
+`a 600,710,65,82,34,82 2 0340 agrave
+char224 "
+'a 600,710,65,82,34,82 2 0341 aacute
+char225 "
+^a 600,703,65,85,34,83 2 0342 acircumflex
+char226 "
+~a 600,678,65,104,34,83 2 0343 atilde
+char227 "
+:a 600,652,65,82,34,82 2 0344 adieresis
+char228 "
+oa 600,746,65,82,34,82 2 0345 aring
+char229 "
+,c 600,502,246,122,6,83 1 0347 ccedilla
+char231 "
+`e 600,714,65,100,25,83 2 0350 egrave
+char232 "
+'e 600,714,65,100,25,83 2 0351 eacute
+char233 "
+^e 600,703,65,100,25,83 2 0352 ecircumflex
+char234 "
+:e 600,652,65,100,25,83 2 0353 edieresis
+char235 "
+`i 600,706,50,49,27,49 2 0354 igrave
+char236 "
+'i 600,710,50,49,27,49 2 0355 iacute
+char237 "
+^i 600,684,50,49,27,49 2 0356 icircumflex
+char238 "
+:i 600,652,50,49,27,49 2 0357 idieresis
+char239 "
+~n 600,678,50,82,66,82 2 0361 ntilde
+char241 "
+`o 600,699,65,106,16,83 2 0362 ograve
+char242 "
+'o 600,699,65,106,16,83 2 0363 oacute
+char243 "
+^o 600,703,65,106,16,83 2 0364 ocircumflex
+char244 "
+~o 600,657,65,106,16,83 2 0365 otilde
+char245 "
+:o 600,652,65,106,16,83 2 0366 odieresis
+char246 "
+/o 600,538,121,166,81,83 1 0370 oslash
+char248 "
+`u 600,706,65,90,5,83 2 0371 ugrave
+char249 "
+'u 600,706,65,90,5,83 2 0372 uacute
+char250 "
+^u 600,684,65,90,5,83 2 0373 ucircumflex
+char251 "
+:u 600,652,65,90,5,83 2 0374 udieresis
+char252 "
+:y 600,631,257,161,112,83 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/CI b/ps/devps/CI
new file mode 100644
index 000000000..56a2881a3
--- /dev/null
+++ b/ps/devps/CI
@@ -0,0 +1,287 @@
+name CI
+internalname Courier-Oblique
+slant 12
+spacewidth 600
+encoding text.enc
+charset
+ha 600,624,0,37,-123,37 2 0000 asciicircum
+ti 600,373,0,42,-77,42 0 0001 asciitilde
+vS 600,774,35,72,-26,72 2 0002 Scaron
+vZ 600,774,20,71,-37,71 2 0003 Zcaron
+vs 600,645,35,43,-37,43 2 0004 scaron
+vz 600,645,20,43,-49,43 2 0005 zcaron
+:Y 600,721,20,134,-79,74 2 0006 Ydieresis
+space 600 0 0040
+! 600,639,5,0,-207 2 0041 exclam
+" 600,603,0,44,-181,44 2 0042 quotedbl
+# 600,665,82,58,-66,58 2 0043 numbersign
+sh "
+$ 600,675,113,51,-61,51 3 0044 dollar
+Do "
+% 600,639,35,61,-68,61 2 0045 percent
+& 600,540,35,0,-62 2 0046 ampersand
+' 600,613,0,0,-158 2 0047 quoteright
+( 600,623,144,52,-265,52 3 0050 parenleft
+) 600,623,144,0,-55 3 0051 parenright
+* 600,624,0,55,-141,55 2 0052 asterisk
++ 600,550,0,58,-62,58 2 0053 plus
+, 600,155,155,0,-61 1 0054 comma
+- 600,321,0,58,-62,58 0 0055 hyphen
+hy "
+char173 "
+. 600,90,0,0,-211 0 0056 period
+/ 600,686,103,94,-30,74 3 0057 slash
+sl "
+0 600,639,35,40,-89,40 2 0060 zero
+1 600,624,20,0,-47 2 0061 one
+2 600,639,20,46,-18,46 2 0062 two
+3 600,639,35,49,-40,49 2 0063 three
+4 600,624,20,10,-75,10 2 0064 four
+5 600,624,35,52,-43,52 2 0065 five
+6 600,639,35,104,-117,74 2 0066 six
+7 600,624,20,72,-146,72 2 0067 seven
+8 600,639,35,45,-74,45 2 0070 eight
+9 600,639,35,56,-70,56 2 0071 nine
+: 600,392,0,0,-211 0 0072 colon
+; 600,392,116,0,-73 1 0073 semicolon
+< 600,550,0,106,-62,74 2 0074 less
+= 600,394,0,94,-25,74 0 0075 equal
+> 600,550,0,58,-13,58 2 0076 greater
+? 600,598,5,47,-161,47 2 0077 question
+@ 600,644,82,30,-70,30 2 0100 at
+at "
+A 600,583,20,65,57,65 2 0101 A
+B 600,583,20,60,23,60 2 0102 B
+C 600,598,35,105,-41,74 2 0103 C
+D 600,583,20,60,23,60 2 0104 D
+E 600,583,20,84,23,74 2 0105 E
+F 600,583,20,105,23,74 2 0106 F
+G 600,598,35,105,-39,74 2 0107 G
+H 600,583,20,115,13,74 2 0110 H
+I 600,583,20,72,-47,72 2 0111 I
+J 600,583,35,168,-32,74 2 0112 J
+K 600,583,20,136,23,74 2 0113 K
+L 600,583,20,55,3,55 2 0114 L
+M 600,583,20,168,55,74 2 0115 M
+N 600,583,20,147,23,74 2 0116 N
+O 600,598,35,86,-34,74 2 0117 O
+P 600,583,20,56,23,56 2 0120 P
+Q 600,598,136,86,-34,74 3 0121 Q
+R 600,583,20,63,23,63 2 0122 R
+S 600,598,35,72,-26,72 2 0123 S
+T 600,583,20,113,-79,74 2 0124 T
+U 600,583,35,145,-69,74 2 0125 U
+V 600,583,20,176,-54,74 2 0126 V
+W 600,583,20,165,-53,74 2 0127 W
+X 600,583,20,134,26,74 2 0130 X
+Y 600,583,20,134,-79,74 2 0131 Y
+Z 600,583,20,61,-37,61 2 0132 Z
+[ 600,624,144,39,-188,39 3 0133 bracketleft
+lB "
+\ 600,686,103,0,-180 3 0134 backslash
+rs "
+] 600,624,144,0,-63 3 0135 bracketright
+rB "
+a^ 600,624,0,19,-189,19 2 0136 circumflex
+^ "
+_ 600,0,290,29,135,29 1 0137 underscore
+` 600,613,0,0,-332 2 0140 quoteleft
+oq "
+a 600,452,35,15,-24,15 0 0141 a
+b 600,624,35,60,44,60 2 0142 b
+c 600,452,35,64,-54,64 0 0143 c
+d 600,624,35,93,-33,74 2 0144 d
+e 600,452,35,40,-35,40 0 0145 e
+f 600,624,20,132,-39,74 2 0146 f
+g 600,452,207,116,-35,74 1 0147 g
+h 600,624,20,25,13,25 2 0150 h
+i 600,665,20,0,-26 2 0151 i
+j 600,665,207,12,-42,12 3 0152 j
+k 600,624,20,53,3,53 2 0153 k
+l 600,624,20,0,-26 2 0154 l
+m 600,452,20,71,55,71 0 0155 m
+n 600,452,20,15,13,15 0 0156 n
+o 600,452,35,47,-41,47 0 0157 o
+p 600,452,207,62,83,62 1 0160 p
+q 600,452,207,137,-35,74 1 0161 q
+r 600,448,20,89,-18,74 0 0162 r
+s 600,452,35,30,-37,30 0 0163 s
+t 600,582,35,0,-57 2 0164 t
+u 600,437,35,32,-57,32 0 0165 u
+v 600,437,20,124,-44,74 0 0166 v
+w 600,437,20,124,-44,74 0 0167 w
+x 600,437,20,82,15,74 0 0170 x
+y 600,437,207,103,54,74 1 0171 y
+z 600,437,20,32,-49,32 0 0172 z
+lC 600,624,144,0,-178 3 0173 braceleft
+{ "
+ba 600,624,144,0,-188 3 0174 bar
+| "
+rC 600,624,144,0,-105 3 0175 braceright
+} "
+a~ 600,580,0,36,-181,36 2 0176 tilde
+~ "
+--- 600,144,165,0,-58 1 0200 quotesinglbase
+Fo 600,437,20,95,-37,74 0 0201 guillemotleft
+char171 "
+Fc 600,437,20,55,3,55 0 0202 guillemotright
+char187 "
+bu 600,346,0,0,-275 0 0203 bullet
+--- 600,639,113,113,-4,74 3 0204 florin
+f/ 600,492,0,115,-15,74 0 0205 fraction
+dg 600,624,82,30,-138,30 2 0207 dagger
+dd 600,624,82,30,-85,30 2 0210 daggerdbl
+en 600,321,0,58,-62,58 0 0211 endash
+em 600,321,0,129,9,74 0 0212 emdash
+.i 600,437,20,0,-26 0 0220 dotlessi
+ga 600,639,0,0,-212 2 0222 grave
+a" 600,639,0,42,-189,42 2 0223 hungarumlaut
+a. 600,571,0,0,-323 2 0224 dotaccent
+ab 600,624,0,39,-208,39 2 0225 breve
+ah 600,624,0,39,-209,39 2 0226 caron
+ao 600,634,0,0,-247 2 0227 ring
+ho 600,40,165,0,-194 1 0230 ogonek
+lq 600,619,0,38,-166,38 2 0231 quotedblleft
+rq 600,619,0,80,-124,74 2 0232 quotedblright
+/l 600,624,20,0,-26 2 0234 lslash
+--- 600,139,139,0,-22 1 0235 quotedblbase
+/L 600,583,20,55,3,55 2 0237 Lslash
+r! 600,415,207,0,-159 1 0241 exclamdown
+char161 "
+ct 600,665,19,33,-106,33 2 0242 cent
+char162 "
+Po 600,598,20,10,-18,10 2 0243 sterling
+char163 "
+Cs 600,509,0,67,-57,67 2 0244 currency
+char164 "
+Ye 600,583,20,134,-87,74 2 0245 yen
+char165 "
+sc 600,629,87,87,-28,74 3 0247 section
+char167 "
+ad 600,571,0,7,-219,7 2 0250 dieresis
+char168 "
+Of 600,598,0,0,-124 2 0252 ordfeminine
+char170 "
+fo 600,437,20,0,-37 0 0253 guilsinglleft
+a- 600,556,0,25,-195,25 2 0257 macron
+char175 "
+aa 600,639,0,42,-314,42 2 0264 acute
+char180 "
+ps 600,629,87,120,-82,74 3 0266 paragraph
+char182 "
+char183 600,346,0,0,-265 0 0267 periodcentered
+ac 600,40,186,0,-113 1 0270 cedilla
+char184 "
+Om 600,598,0,13,-128,13 2 0272 ordmasculine
+char186 "
+fc 600,437,20,55,-227,55 0 0273 guilsinglright
+r? 600,415,207,0,-35 1 0277 questiondown
+char191 "
+`A 600,789,20,65,57,65 2 0300 Agrave
+char192 "
+'A 600,789,20,65,57,65 2 0301 Aacute
+char193 "
+^A 600,774,20,65,57,65 2 0302 Acircumflex
+char194 "
+~A 600,730,20,65,57,65 2 0303 Atilde
+char195 "
+:A 600,721,20,65,57,65 2 0304 Adieresis
+char196 "
+oA 600,795,20,65,57,65 2 0305 Aring
+char197 "
+,C 600,598,186,105,-41,74 3 0307 Ccedilla
+char199 "
+`E 600,789,20,84,23,74 2 0310 Egrave
+char200 "
+'E 600,789,20,84,23,74 2 0311 Eacute
+char201 "
+^E 600,774,20,84,23,74 2 0312 Ecircumflex
+char202 "
+:E 600,721,20,84,23,74 2 0313 Edieresis
+char203 "
+`I 600,789,20,72,-47,72 2 0314 Igrave
+char204 "
+'I 600,789,20,72,-47,72 2 0315 Iacute
+char205 "
+^I 600,774,20,72,-47,72 2 0316 Icircumflex
+char206 "
+:I 600,721,20,72,-47,72 2 0317 Idieresis
+char207 "
+~N 600,730,20,147,23,74 2 0321 Ntilde
+char209 "
+`O 600,789,35,86,-34,74 2 0322 Ograve
+char210 "
+'O 600,789,35,86,-34,74 2 0323 Oacute
+char211 "
+^O 600,774,35,86,-34,74 2 0324 Ocircumflex
+char212 "
+~O 600,730,35,86,-34,74 2 0325 Otilde
+char213 "
+:O 600,721,35,86,-34,74 2 0326 Odieresis
+char214 "
+/O 600,623,61,154,34,74 2 0330 Oslash
+char216 "
+`U 600,789,35,145,-69,74 2 0331 Ugrave
+char217 "
+'U 600,789,35,145,-69,74 2 0332 Uacute
+char218 "
+^U 600,774,35,145,-69,74 2 0333 Ucircumflex
+char219 "
+:U 600,721,35,145,-69,74 2 0334 Udieresis
+char220 "
+ss 600,624,35,8,23,8 2 0337 germandbls
+char223 "
+`a 600,660,35,15,-24,15 2 0340 agrave
+char224 "
+'a 600,660,35,15,-24,15 2 0341 aacute
+char225 "
+^a 600,653,35,25,-24,25 2 0342 acircumflex
+char226 "
+~a 600,618,35,44,-24,44 2 0343 atilde
+char227 "
+:a 600,592,35,15,-24,15 2 0344 adieresis
+char228 "
+oa 600,686,35,15,-24,15 2 0345 aring
+char229 "
+,c 600,452,186,64,-54,64 1 0347 ccedilla
+char231 "
+`e 600,664,35,40,-35,40 2 0350 egrave
+char232 "
+'e 600,664,35,40,-35,40 2 0351 eacute
+char233 "
+^e 600,653,35,40,-35,40 2 0352 ecircumflex
+char234 "
+:e 600,592,35,40,-35,40 2 0353 edieresis
+char235 "
+`i 600,656,20,0,-26 2 0354 igrave
+char236 "
+'i 600,660,20,0,-26 2 0355 iacute
+char237 "
+^i 600,634,20,0,-26 2 0356 icircumflex
+char238 "
+:i 600,592,20,0,-26 2 0357 idieresis
+char239 "
+~n 600,618,20,19,13,19 2 0361 ntilde
+char241 "
+`o 600,649,35,47,-41,47 2 0362 ograve
+char242 "
+'o 600,649,35,47,-41,47 2 0363 oacute
+char243 "
+^o 600,653,35,47,-41,47 2 0364 ocircumflex
+char244 "
+~o 600,597,35,47,-41,47 2 0365 otilde
+char245 "
+:o 600,592,35,47,-41,47 2 0366 odieresis
+char246 "
+/o 600,478,61,106,21,74 0 0370 oslash
+char248 "
+`u 600,656,35,32,-57,32 2 0371 ugrave
+char249 "
+'u 600,656,35,32,-57,32 2 0372 uacute
+char250 "
+^u 600,634,35,32,-57,32 2 0373 ucircumflex
+char251 "
+:u 600,592,35,32,-57,32 2 0374 udieresis
+char252 "
+:y 600,571,207,103,54,74 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/CR b/ps/devps/CR
new file mode 100644
index 000000000..96b39bc49
--- /dev/null
+++ b/ps/devps/CR
@@ -0,0 +1,286 @@
+name CR
+internalname Courier
+spacewidth 600
+encoding text.enc
+charset
+ha 600,624 2 0000 asciicircum
+ti 600,373 0 0001 asciitilde
+vS 600,774,35 2 0002 Scaron
+vZ 600,774,20 2 0003 Zcaron
+vs 600,645,35 2 0004 scaron
+vz 600,645,20 2 0005 zcaron
+:Y 600,721,20 2 0006 Ydieresis
+space 600 0 0040
+! 600,639,5 2 0041 exclam
+" 600,603 2 0042 quotedbl
+# 600,665,82 2 0043 numbersign
+sh "
+$ 600,675,113 3 0044 dollar
+Do "
+% 600,639,35 2 0045 percent
+& 600,540,35 2 0046 ampersand
+' 600,613 2 0047 quoteright
+( 600,623,144 3 0050 parenleft
+) 600,623,144 3 0051 parenright
+* 600,624 2 0052 asterisk
++ 600,550 2 0053 plus
+, 600,155,155 1 0054 comma
+- 600,321 0 0055 hyphen
+hy "
+char173 "
+. 600,90 0 0056 period
+/ 600,686,103 3 0057 slash
+sl "
+0 600,639,35 2 0060 zero
+1 600,624,20 2 0061 one
+2 600,639,20 2 0062 two
+3 600,639,35 2 0063 three
+4 600,624,20 2 0064 four
+5 600,624,35 2 0065 five
+6 600,639,35 2 0066 six
+7 600,624,20 2 0067 seven
+8 600,639,35 2 0070 eight
+9 600,639,35 2 0071 nine
+: 600,392 0 0072 colon
+; 600,392,116 1 0073 semicolon
+< 600,550 2 0074 less
+= 600,394 0 0075 equal
+> 600,550 2 0076 greater
+? 600,598,5 2 0077 question
+@ 600,644,82 2 0100 at
+at "
+A 600,583,20 2 0101 A
+B 600,583,20 2 0102 B
+C 600,598,35 2 0103 C
+D 600,583,20 2 0104 D
+E 600,583,20 2 0105 E
+F 600,583,20 2 0106 F
+G 600,598,35 2 0107 G
+H 600,583,20 2 0110 H
+I 600,583,20 2 0111 I
+J 600,583,35 2 0112 J
+K 600,583,20 2 0113 K
+L 600,583,20 2 0114 L
+M 600,583,20 2 0115 M
+N 600,583,20 2 0116 N
+O 600,598,35 2 0117 O
+P 600,583,20 2 0120 P
+Q 600,598,136 3 0121 Q
+R 600,583,20 2 0122 R
+S 600,598,35 2 0123 S
+T 600,583,20 2 0124 T
+U 600,583,35 2 0125 U
+V 600,583,20 2 0126 V
+W 600,583,20 2 0127 W
+X 600,583,20 2 0130 X
+Y 600,583,20 2 0131 Y
+Z 600,583,20 2 0132 Z
+[ 600,624,144 3 0133 bracketleft
+lB "
+\ 600,686,103 3 0134 backslash
+rs "
+] 600,624,144 3 0135 bracketright
+rB "
+a^ 600,624 2 0136 circumflex
+^ "
+_ 600,0,290 1 0137 underscore
+` 600,613 2 0140 quoteleft
+oq "
+a 600,452,35 0 0141 a
+b 600,624,35 2 0142 b
+c 600,452,35 0 0143 c
+d 600,624,35 2 0144 d
+e 600,452,35 0 0145 e
+f 600,624,20 2 0146 f
+g 600,452,207 1 0147 g
+h 600,624,20 2 0150 h
+i 600,665,20 2 0151 i
+j 600,665,207 3 0152 j
+k 600,624,20 2 0153 k
+l 600,624,20 2 0154 l
+m 600,452,20 0 0155 m
+n 600,452,20 0 0156 n
+o 600,452,35 0 0157 o
+p 600,452,207 1 0160 p
+q 600,452,207 1 0161 q
+r 600,448,20 0 0162 r
+s 600,452,35 0 0163 s
+t 600,582,35 2 0164 t
+u 600,437,35 0 0165 u
+v 600,437,20 0 0166 v
+w 600,437,20 0 0167 w
+x 600,437,20 0 0170 x
+y 600,437,207 1 0171 y
+z 600,437,20 0 0172 z
+lC 600,624,144 3 0173 braceleft
+{ "
+ba 600,624,144 3 0174 bar
+| "
+rC 600,624,144 3 0175 braceright
+} "
+a~ 600,580 2 0176 tilde
+~ "
+--- 600,144,165 1 0200 quotesinglbase
+Fo 600,437,20 0 0201 guillemotleft
+char171 "
+Fc 600,437,20 0 0202 guillemotright
+char187 "
+bu 600,346 0 0203 bullet
+--- 600,639,113 3 0204 florin
+f/ 600,492 0 0205 fraction
+dg 600,624,82 2 0207 dagger
+dd 600,624,82 2 0210 daggerdbl
+en 600,321 0 0211 endash
+em 600,321 0 0212 emdash
+.i 600,437,20 0 0220 dotlessi
+ga 600,639 2 0222 grave
+a" 600,639 2 0223 hungarumlaut
+a. 600,571 2 0224 dotaccent
+ab 600,624 2 0225 breve
+ah 600,624 2 0226 caron
+ao 600,634 2 0227 ring
+ho 600,40,165 1 0230 ogonek
+lq 600,619 2 0231 quotedblleft
+rq 600,619 2 0232 quotedblright
+/l 600,624,20 2 0234 lslash
+--- 600,139,139 1 0235 quotedblbase
+/L 600,583,20 2 0237 Lslash
+r! 600,415,207 1 0241 exclamdown
+char161 "
+ct 600,665,19 2 0242 cent
+char162 "
+Po 600,598,20 2 0243 sterling
+char163 "
+Cs 600,509 2 0244 currency
+char164 "
+Ye 600,583,20 2 0245 yen
+char165 "
+sc 600,629,87 3 0247 section
+char167 "
+ad 600,571 2 0250 dieresis
+char168 "
+Of 600,598 2 0252 ordfeminine
+char170 "
+fo 600,437,20 0 0253 guilsinglleft
+a- 600,556 2 0257 macron
+char175 "
+aa 600,639 2 0264 acute
+char180 "
+ps 600,629,87 3 0266 paragraph
+char182 "
+char183 600,346 0 0267 periodcentered
+ac 600,40,186 1 0270 cedilla
+char184 "
+Om 600,598 2 0272 ordmasculine
+char186 "
+fc 600,437,20 0 0273 guilsinglright
+r? 600,415,207 1 0277 questiondown
+char191 "
+`A 600,789,20 2 0300 Agrave
+char192 "
+'A 600,789,20 2 0301 Aacute
+char193 "
+^A 600,774,20 2 0302 Acircumflex
+char194 "
+~A 600,730,20 2 0303 Atilde
+char195 "
+:A 600,721,20 2 0304 Adieresis
+char196 "
+oA 600,795,20 2 0305 Aring
+char197 "
+,C 600,598,186 3 0307 Ccedilla
+char199 "
+`E 600,789,20 2 0310 Egrave
+char200 "
+'E 600,789,20 2 0311 Eacute
+char201 "
+^E 600,774,20 2 0312 Ecircumflex
+char202 "
+:E 600,721,20 2 0313 Edieresis
+char203 "
+`I 600,789,20 2 0314 Igrave
+char204 "
+'I 600,789,20 2 0315 Iacute
+char205 "
+^I 600,774,20 2 0316 Icircumflex
+char206 "
+:I 600,721,20 2 0317 Idieresis
+char207 "
+~N 600,730,20 2 0321 Ntilde
+char209 "
+`O 600,789,35 2 0322 Ograve
+char210 "
+'O 600,789,35 2 0323 Oacute
+char211 "
+^O 600,774,35 2 0324 Ocircumflex
+char212 "
+~O 600,730,35 2 0325 Otilde
+char213 "
+:O 600,721,35 2 0326 Odieresis
+char214 "
+/O 600,623,61 2 0330 Oslash
+char216 "
+`U 600,789,35 2 0331 Ugrave
+char217 "
+'U 600,789,35 2 0332 Uacute
+char218 "
+^U 600,774,35 2 0333 Ucircumflex
+char219 "
+:U 600,721,35 2 0334 Udieresis
+char220 "
+ss 600,624,35 2 0337 germandbls
+char223 "
+`a 600,660,35 2 0340 agrave
+char224 "
+'a 600,660,35 2 0341 aacute
+char225 "
+^a 600,653,35 2 0342 acircumflex
+char226 "
+~a 600,618,35 2 0343 atilde
+char227 "
+:a 600,592,35 2 0344 adieresis
+char228 "
+oa 600,686,35 2 0345 aring
+char229 "
+,c 600,452,186 1 0347 ccedilla
+char231 "
+`e 600,664,35 2 0350 egrave
+char232 "
+'e 600,664,35 2 0351 eacute
+char233 "
+^e 600,653,35 2 0352 ecircumflex
+char234 "
+:e 600,592,35 2 0353 edieresis
+char235 "
+`i 600,656,20 2 0354 igrave
+char236 "
+'i 600,660,20 2 0355 iacute
+char237 "
+^i 600,634,20 2 0356 icircumflex
+char238 "
+:i 600,592,20 2 0357 idieresis
+char239 "
+~n 600,618,20 2 0361 ntilde
+char241 "
+`o 600,649,35 2 0362 ograve
+char242 "
+'o 600,649,35 2 0363 oacute
+char243 "
+^o 600,653,35 2 0364 ocircumflex
+char244 "
+~o 600,597,35 2 0365 otilde
+char245 "
+:o 600,592,35 2 0366 odieresis
+char246 "
+/o 600,478,61 0 0370 oslash
+char248 "
+`u 600,656,35 2 0371 ugrave
+char249 "
+'u 600,656,35 2 0372 uacute
+char250 "
+^u 600,634,35 2 0373 ucircumflex
+char251 "
+:u 600,592,35 2 0374 udieresis
+char252 "
+:y 600,571,207 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/DESC-A4 b/ps/devps/DESC-A4
new file mode 100644
index 000000000..9ddfc5cf7
--- /dev/null
+++ b/ps/devps/DESC-A4
@@ -0,0 +1,11 @@
+paperlength 841890
+res 72000
+hor 1
+vert 1
+sizescale 1000
+unitwidth 1000
+sizes 3000-10000000 0
+styles R I B BI
+family T
+fonts 4 SS S ZD ZDR
+tcommand
diff --git a/ps/devps/DESC-letter b/ps/devps/DESC-letter
new file mode 100644
index 000000000..2e29983d1
--- /dev/null
+++ b/ps/devps/DESC-letter
@@ -0,0 +1,11 @@
+paperlength 792000
+res 72000
+hor 1
+vert 1
+sizescale 1000
+unitwidth 1000
+sizes 3000-10000000 0
+styles R I B BI
+family T
+fonts 4 SS S ZD ZDR
+tcommand
diff --git a/ps/devps/FontMakefile b/ps/devps/FontMakefile
new file mode 100644
index 000000000..0108b464c
--- /dev/null
+++ b/ps/devps/FontMakefile
@@ -0,0 +1,183 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#This is the Makefile used for constructing the groff font files
+#from the afm files.
+#To use this, you need perl 3.0, and afm files.
+#The symbol.afm used has some tweaks; symbol.diff is a diff between the
+#original symbol.afm and the tweaked symbol.afm.
+
+SPECIALFONTS=S
+DINGBATSFONTS=ZD ZDR
+GREEKFONTS=SS
+TEXTFONTS=AB ABI AI AR \
+ BMB BMBI BMI BMR \
+ CB CBI CI CR \
+ HB HBI HI HR \
+ HNB HNBI HNI HNR \
+ NB NBI NI NR \
+ PB PBI PI PR \
+ TB TBI TI TR \
+ ZCMI
+
+AFMTODIT=./afmtodit
+IFLAG=-i 50
+TEXTENC=text.enc
+EFLAG=-e $(TEXTENC)
+TEXTMAP=textmap
+SYMBOLMAP=symbolmap
+
+FONTS=$(TEXTFONTS) $(SPECIALFONTS) $(GREEKFONTS) $(DINGBATSFONTS)
+
+all: $(FONTS)
+
+VPATH=/usr/local/afm
+
+TR: timesr.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+TB: timesb.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+TI: timesi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) -a 7 $< $(TEXTMAP) $@
+
+TBI: timesbi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+HR: helve.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+HB: helveb.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+HI: helveo.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+HBI: helvebo.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+CR: couri.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+CB: courib.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+CI: courio.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+CBI: couribo.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+PR: palatr.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+PB: palatb.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+PI: palati.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+PBI: palatbi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+NR: newcsr.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+NB: newcsb.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+NI: newcsi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+NBI: newcsbi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+BMR: bookml.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+BMB: bookmd.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+BMI: bookmli.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+BMBI: bookmdi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+AR: avangbk.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+AB: avangd.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+AI: avangbko.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+ABI: avangdo.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+HNR: helven.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+HNB: helvenb.afm
+ $(AFMTODIT) $(EFLAG) $< $(TEXTMAP) $@
+
+HNI: helveno.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+HNBI: helvenbo.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+ZCMI: zapfcmi.afm
+ $(AFMTODIT) $(EFLAG) $(IFLAG) $< $(TEXTMAP) $@
+
+SS: symbolsl.afm
+ $(AFMTODIT) -s $(IFLAG) $< lgreekmap $@
+
+S: symbol.afm
+ $(AFMTODIT) -s $< $(SYMBOLMAP) $@
+
+ZD: zapfd.afm
+ $(AFMTODIT) -s $< dingbatsmap $@
+
+# zapdfr.afm is exatly the same as zapfd.afm but with a FontName of
+# ZapfDingbats-Reverse
+
+ZDR: zapfdr.afm
+ $(AFMTODIT) -s $< dingbatsrmap $@
+
+ZD: dingbatsmap
+
+ZDR: dingbatsrmap
+
+$(FONTS): $(AFMTODIT)
+
+$(TEXTFONTS): $(TEXTMAP) $(TEXTENC)
+
+$(SPECIALFONTS): $(SYMBOLMAP)
+
+$(GREEKFONTS): lgreekmap
+
+$(SYMBOLMAP): $(TEXTMAP) symbolchars
+ cat $(TEXTMAP) symbolchars >$@
+
+clean:
+ -rm -f $(FONTS)
+ -rm -f $(SYMBOLMAP)
diff --git a/ps/devps/HB b/ps/devps/HB
new file mode 100644
index 000000000..446f23dee
--- /dev/null
+++ b/ps/devps/HB
@@ -0,0 +1,410 @@
+name HB
+internalname Helvetica-Bold
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -37
+A w -18
+A v -37
+A ' -55
+A Y -92
+A W -55
+A V -74
+A T -74
+F . -111
+F , -111
+F A -55
+L y -37
+L ' -55
+L Y -92
+L W -55
+L V -74
+L T -74
+P . -129
+P , -129
+P A -74
+R Y -37
+R W -18
+R V -18
+T y -74
+T w -74
+T u -74
+T ; -111
+T s -74
+T r -55
+T . -111
+T o -74
+T i -18
+T - -55
+T hy -55
+T char173 -55
+T e -74
+T , -111
+T : -111
+T c -74
+T a -74
+T O -18
+T A -74
+V y -37
+V u -37
+V ; -55
+V r -55
+V . -92
+V o -74
+V i -18
+V - -55
+V hy -55
+V char173 -55
+V e -55
+V , -92
+V : -55
+V a -55
+V A -74
+W y -18
+W u -18
+W ; -18
+W r -18
+W . -55
+W o -18
+W i -9
+W - -20
+W hy -20
+W char173 -20
+W e -18
+W , -55
+W : -18
+W a -37
+W A -55
+Y v -55
+Y u -55
+Y ; -74
+Y q -74
+Y . -111
+Y p -55
+Y o -74
+Y i -37
+Y - -55
+Y hy -55
+Y char173 -55
+Y e -55
+Y , -111
+Y : -74
+Y a -55
+Y A -92
+f ' 18
+1 1 -55
+` ` -37
+` oq -37
+oq ` -37
+oq oq -37
+' s -37
+' ' -37
+r ' 37
+r . -55
+r , -55
+v . -74
+v , -74
+w . -37
+w , -37
+y . -74
+y , -74
+charset
+ha 584,696 2 0000 asciicircum
+ti 584,322 0 0001 asciitilde
+vS 667,936,23 2 0002 Scaron
+vZ 611,936 2 0003 Zcaron
+vs 556,745,23 2 0004 scaron
+vz 500,745 2 0005 zcaron
+:Y 667,922 2 0006 Ydieresis
+tm 1000,745 2 0007 trademark
+space 278 0 0040
+! 333,729 2 0041 exclam
+" 474,729 2 0042 quotedbl
+# 556,696,30 2 0043 numbersign
+sh "
+$ 556,765,125 3 0044 dollar
+Do "
+% 889,708,18 2 0045 percent
+& 722,729,20 2 0046 ampersand
+' 278,729 2 0047 quoteright
+( 333,729,202 3 0050 parenleft
+) 333,729,202 3 0051 parenright
+* 389,730 2 0052 asterisk
++ 584,474,10 0 0053 plus
+, 278,146,174 1 0054 comma
+- 333,344 0 0055 hyphen
+hy "
+char173 "
+. 278,146 0 0056 period
+/ 278,715,14 2 0057 slash
+sl "
+0 556,725,23 2 0060 zero
+1 556,709 2 0061 one
+2 556,726 2 0062 two
+3 556,726,23 2 0063 three
+4 556,709 2 0064 four
+5 556,709,24 2 0065 five
+6 556,727,23 2 0066 six
+7 556,709 2 0067 seven
+8 556,726,23 2 0070 eight
+9 556,728,23 2 0071 nine
+: 333,521 0 0072 colon
+; 333,521,174 1 0073 semicolon
+< 584,474,10 0 0074 less
+= 584,412 0 0075 equal
+> 584,474,10 0 0076 greater
+? 611,744 2 0077 question
+@ 975,746,136 3 0100 at
+at "
+A 722,729 2 0101 A
+B 722,729 2 0102 B
+C 722,741,23 2 0103 C
+D 722,729 2 0104 D
+E 667,729 2 0105 E
+F 611,729 2 0106 F
+G 778,741,24 2 0107 G
+H 722,729 2 0110 H
+I 278,729 2 0111 I
+J 556,729,23 2 0112 J
+K 722,729 2 0113 K
+L 611,729 2 0114 L
+M 833,729 2 0115 M
+N 722,729 2 0116 N
+O 778,741,23 2 0117 O
+P 667,729 2 0120 P
+Q 778,741,54 2 0121 Q
+R 722,729 2 0122 R
+S 667,741,23 2 0123 S
+T 611,729 2 0124 T
+U 722,729,23 2 0125 U
+V 667,729 2 0126 V
+W 944,729 2 0127 W
+X 667,729 2 0130 X
+Y 667,729 2 0131 Y
+Z 611,729 2 0132 Z
+[ 333,729,202 3 0133 bracketleft
+lB "
+\ 278,708,21 2 0134 backslash
+rs "
+] 333,729,202 3 0135 bracketright
+rB "
+a^ 333,745 2 0136 circumflex
+^ "
+_ 556,0,200 1 0137 underscore
+` 278,729 2 0140 quoteleft
+oq "
+a 556,551,24 0 0141 a
+b 611,729,23 2 0142 b
+c 556,551,23 0 0143 c
+d 611,729,23 2 0144 d
+e 556,551,23 0 0145 e
+f 333,729 2 0146 f
+g 611,551,220 1 0147 g
+h 611,729 2 0150 h
+i 278,729 2 0151 i
+j 278,729,219 3 0152 j
+k 556,729 2 0153 k
+l 278,729 2 0154 l
+m 889,553 0 0155 m
+n 611,551 0 0156 n
+o 611,551,23 0 0157 o
+p 611,551,219 1 0160 p
+q 611,551,219 1 0161 q
+r 389,553 0 0162 r
+s 556,551,23 0 0163 s
+t 333,678,23 2 0164 t
+u 611,542,23 0 0165 u
+v 556,542 0 0166 v
+w 778,542 0 0167 w
+x 556,542 0 0170 x
+y 556,542,219 1 0171 y
+z 500,542 0 0172 z
+lC 389,729,202 3 0173 braceleft
+{ "
+ba 280,729,202 3 0174 bar
+| "
+rC 389,729,202 3 0175 braceright
+} "
+a~ 333,729 2 0176 tilde
+~ "
+--- 278,125,135 1 0200 quotesinglbase
+Fo 556,484 0 0201 guillemotleft
+char171 "
+Fc 556,482 0 0202 guillemotright
+char187 "
+bu 350,425 0 0203 bullet
+--- 556,745,221 3 0204 florin
+f/ 167,715,20 2 0205 fraction
+%0 1000,745,20 2 0206 perthousand
+dg 556,708,195 3 0207 dagger
+dd 556,708,195 3 0210 daggerdbl
+en 556,313 0 0211 endash
+em 1000,313 0 0212 emdash
+fi 611,729 2 0214 fi
+fl 611,729 2 0215 fl
+.i 278,542 0 0220 dotlessi
+ga 333,745 2 0222 grave
+a" 333,745 2 0223 hungarumlaut
+a. 333,729 2 0224 dotaccent
+ab 333,736 2 0225 breve
+ah 333,745 2 0226 caron
+ao 333,745 2 0227 ring
+ho 333,38,195 1 0230 ogonek
+lq 500,729 2 0231 quotedblleft
+rq 500,729 2 0232 quotedblright
+oe 944,554,21 0 0233 oe
+/l 278,729 2 0234 lslash
+--- 500,141,164 1 0235 quotedblbase
+OE 1000,741,20 2 0236 OE
+/L 611,729 2 0237 Lslash
+r! 333,542,187 1 0241 exclamdown
+char161 "
+ct 556,637,122 3 0242 cent
+char162 "
+Po 556,717,20 2 0243 sterling
+char163 "
+Cs 556,604 2 0244 currency
+char164 "
+Ye 556,705 2 0245 yen
+char165 "
+sc 556,728,201 3 0247 section
+char167 "
+ad 333,731 2 0250 dieresis
+char168 "
+co 737,745,20 2 0251 copyright
+char169 "
+Of 370,746 2 0252 ordfeminine
+char170 "
+fo 333,476 0 0253 guilsinglleft
+no 584,412 0 0254 logicalnot
+char172 "
+\- 584,290 0 0255 minus
+rg 737,745,20 2 0256 registered
+char174 "
+a- 333,717 2 0257 macron
+char175 "
+aa 333,745 2 0264 acute
+char180 "
+ps 556,729,195 3 0266 paragraph
+char182 "
+char183 278,442 0 0267 periodcentered
+ac 333,0,220 1 0270 cedilla
+char184 "
+Om 365,745 2 0272 ordmasculine
+char186 "
+fc 333,476 0 0273 guilsinglright
+r? 611,542,192 1 0277 questiondown
+char191 "
+`A 722,936 2 0300 Agrave
+char192 "
+'A 722,936 2 0301 Aacute
+char193 "
+^A 722,936 2 0302 Acircumflex
+char194 "
+~A 722,920 2 0303 Atilde
+char195 "
+:A 722,922 2 0304 Adieresis
+char196 "
+oA 722,936 2 0305 Aring
+char197 "
+AE 1000,729 2 0306 AE
+char198 "
+,C 722,741,220 3 0307 Ccedilla
+char199 "
+`E 667,936 2 0310 Egrave
+char200 "
+'E 667,936 2 0311 Eacute
+char201 "
+^E 667,936 2 0312 Ecircumflex
+char202 "
+:E 667,922 2 0313 Edieresis
+char203 "
+`I 278,936 2 0314 Igrave
+char204 "
+'I 278,936 2 0315 Iacute
+char205 "
+^I 278,936 2 0316 Icircumflex
+char206 "
+:I 278,922 2 0317 Idieresis
+char207 "
+~N 722,920 2 0321 Ntilde
+char209 "
+`O 778,936,23 2 0322 Ograve
+char210 "
+'O 778,936,23 2 0323 Oacute
+char211 "
+^O 778,936,23 2 0324 Ocircumflex
+char212 "
+~O 778,920,23 2 0325 Otilde
+char213 "
+:O 778,922,23 2 0326 Odieresis
+char214 "
+/O 778,754,34 2 0330 Oslash
+char216 "
+`U 722,936,23 2 0331 Ugrave
+char217 "
+'U 722,936,23 2 0332 Uacute
+char218 "
+^U 722,936,23 2 0333 Ucircumflex
+char219 "
+:U 722,922,23 2 0334 Udieresis
+char220 "
+ss 611,730,16 2 0337 germandbls
+char223 "
+`a 556,745,24 2 0340 agrave
+char224 "
+'a 556,745,24 2 0341 aacute
+char225 "
+^a 556,745,24 2 0342 acircumflex
+char226 "
+~a 556,729,24 2 0343 atilde
+char227 "
+:a 556,731,24 2 0344 adieresis
+char228 "
+oa 556,745,24 2 0345 aring
+char229 "
+ae 889,555,20 0 0346 ae
+char230 "
+,c 556,551,220 1 0347 ccedilla
+char231 "
+`e 556,745,23 2 0350 egrave
+char232 "
+'e 556,745,23 2 0351 eacute
+char233 "
+^e 556,745,23 2 0352 ecircumflex
+char234 "
+:e 556,731,23 2 0353 edieresis
+char235 "
+`i 278,745 2 0354 igrave
+char236 "
+'i 278,745 2 0355 iacute
+char237 "
+^i 278,745 2 0356 icircumflex
+char238 "
+:i 278,731 2 0357 idieresis
+char239 "
+~n 611,729 2 0361 ntilde
+char241 "
+`o 611,745,23 2 0362 ograve
+char242 "
+'o 611,745,23 2 0363 oacute
+char243 "
+^o 611,745,23 2 0364 ocircumflex
+char244 "
+~o 611,729,23 2 0365 otilde
+char245 "
+:o 611,731,23 2 0366 odieresis
+char246 "
+/o 611,561,34 0 0370 oslash
+char248 "
+`u 611,745,23 2 0371 ugrave
+char249 "
+'u 611,745,23 2 0372 uacute
+char250 "
+^u 611,745,23 2 0373 ucircumflex
+char251 "
+:u 611,731,23 2 0374 udieresis
+char252 "
+:y 556,731,219 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HBI b/ps/devps/HBI
new file mode 100644
index 000000000..6459493d8
--- /dev/null
+++ b/ps/devps/HBI
@@ -0,0 +1,409 @@
+name HBI
+internalname Helvetica-BoldOblique
+slant 12
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A ' -55
+A Y -74
+A W -55
+A V -74
+A T -74
+F . -111
+F , -111
+F A -55
+L ' -74
+L Y -74
+L W -55
+L V -55
+L T -74
+P . -129
+P , -129
+P A -74
+R Y -18
+R W -18
+R T -18
+T y -37
+T w -37
+T u -18
+T ; -74
+T s -37
+T r -18
+T . -74
+T o -37
+T i -18
+T - -55
+T hy -55
+T char173 -55
+T e -37
+T , -74
+T : -74
+T c -37
+T a -37
+T O -18
+T A -74
+V y -18
+V u -18
+V ; -37
+V r -18
+V . -92
+V o -37
+V i -37
+V - -37
+V hy -37
+V char173 -37
+V e -37
+V , -92
+V : -37
+V a -37
+V A -74
+W y -18
+W u -18
+W ; -37
+W r -18
+W . -74
+W o -18
+W i -9
+W - -37
+W hy -37
+W char173 -37
+W e -18
+W , -74
+W : -37
+W a -18
+W A -55
+Y v -37
+Y u -37
+Y ; -55
+Y q -37
+Y . -92
+Y p -37
+Y i -37
+Y o -37
+Y - -74
+Y hy -74
+Y char173 -74
+Y e -37
+Y , -92
+Y : -55
+Y a -37
+Y A -74
+f ' 18
+f f -18
+1 1 -74
+` ` -37
+` oq -37
+oq ` -37
+oq oq -37
+' t 18
+' s -18
+' ' -37
+r ' 37
+r . -55
+r , -55
+v . -55
+v , -55
+w . -37
+w , -37
+y . -37
+y , -37
+charset
+ha 584,696,0,46,-69,46 2 0000 asciicircum
+ti 584,322,0,47,-47,47 0 0001 asciitilde
+vS 667,936,23,108,-26,92 2 0002 Scaron
+vZ 611,936,0,172,20,92 2 0003 Zcaron
+vs 556,745,23,91,-10,91 2 0004 scaron
+vz 500,745,0,125,29,92 2 0005 zcaron
+:Y 667,922,0,188,-132,92 2 0006 Ydieresis
+tm 1000,745,0,137,-163,92 2 0007 trademark
+space 278 0 0040
+! 333,729,0,134,-62,92 2 0041 exclam
+" 474,729,0,155,-127,92 2 0042 quotedbl
+# 556,696,30,154,17,92 2 0043 numbersign
+sh "
+$ 556,765,125,122,-9,92 3 0044 dollar
+Do "
+% 889,708,18,64,-79,64 2 0045 percent
+& 722,729,20,48,-39,48 2 0046 ampersand
+' 278,729,0,128,-116,92 2 0047 quoteright
+( 333,729,202,175,-34,92 3 0050 parenleft
+) 333,729,202,73,71,73 3 0051 parenright
+* 389,730,0,139,-95,92 2 0052 asterisk
++ 584,474,10,62,-37,62 0 0053 plus
+, 278,146,174,17,23,17 1 0054 comma
+- 333,344,0,88,-20,88 0 0055 hyphen
+hy "
+char173 "
+. 278,146,0,17,-14,17 0 0056 period
+/ 278,715,14,199,51,92 2 0057 slash
+sl "
+0 556,725,23,108,-31,92 2 0060 zero
+1 556,709,0,23,-122,23 2 0061 one
+2 556,726,0,122,20,92 2 0062 two
+3 556,726,23,107,-17,92 2 0063 three
+4 556,709,0,93,-7,92 2 0064 four
+5 556,709,24,135,-9,92 2 0065 five
+6 556,727,23,119,-35,92 2 0066 six
+7 556,709,0,173,-81,92 2 0067 seven
+8 556,726,23,114,-10,92 2 0070 eight
+9 556,728,23,105,-18,92 2 0071 nine
+: 333,521,0,91,-63,91 0 0072 colon
+; 333,521,174,91,-26,91 1 0073 semicolon
+< 584,474,10,96,-27,92 0 0074 less
+= 584,412,0,88,-11,88 0 0075 equal
+> 584,474,10,57,12,57 0 0076 greater
+? 611,744,0,111,-118,92 2 0077 question
+@ 975,746,136,107,-23,92 3 0100 at
+at "
+A 722,729,0,31,24,31 2 0101 A
+B 722,729,0,90,-32,90 2 0102 B
+C 722,741,23,121,-57,92 2 0103 C
+D 722,729,0,104,-27,92 2 0104 D
+E 667,729,0,145,-29,92 2 0105 E
+F 611,729,0,180,-24,92 2 0106 F
+G 778,741,24,91,-57,91 2 0107 G
+H 722,729,0,140,-18,92 2 0110 H
+I 278,729,0,140,-13,92 2 0111 I
+J 556,729,23,135,-9,92 2 0112 J
+K 722,729,0,171,-24,92 2 0113 K
+L 611,729,0,45,-30,45 2 0114 L
+M 833,729,0,148,-16,92 2 0115 M
+N 722,729,0,144,-18,92 2 0116 N
+O 778,741,23,100,-56,92 2 0117 O
+P 667,729,0,130,-26,92 2 0120 P
+Q 778,741,54,103,-59,92 2 0121 Q
+R 722,729,0,113,-30,92 2 0122 R
+S 667,741,23,108,-26,92 2 0123 S
+T 611,729,0,192,-92,92 2 0124 T
+U 722,729,23,137,-69,92 2 0125 U
+V 667,729,0,185,-129,92 2 0126 V
+W 944,729,0,193,-118,92 2 0127 W
+X 667,729,0,185,28,92 2 0130 X
+Y 667,729,0,188,-132,92 2 0131 Y
+Z 611,729,0,172,20,92 2 0132 Z
+[ 333,729,202,180,27,92 3 0133 bracketleft
+lB "
+\ 278,708,21,57,-88,57 2 0134 backslash
+rs "
+] 333,729,202,132,75,92 3 0135 bracketright
+rB "
+a^ 333,745,0,170,-85,92 2 0136 circumflex
+^ "
+_ 556,0,200,44,115,44 1 0137 underscore
+` 278,729,0,129,-117,92 2 0140 quoteleft
+oq "
+a 556,551,24,72,0,72 0 0141 a
+b 611,729,23,79,-9,79 2 0142 b
+c 556,551,23,91,-27,91 0 0143 c
+d 611,729,23,139,-29,92 2 0144 d
+e 556,551,23,85,-14,85 0 0145 e
+f 333,729,0,181,-40,92 2 0146 f
+g 611,551,220,95,24,92 1 0147 g
+h 611,729,0,68,-17,68 2 0150 h
+i 278,729,0,134,-17,92 2 0151 i
+j 278,729,219,137,93,92 3 0152 j
+k 556,729,0,145,-9,92 2 0153 k
+l 278,729,0,134,-17,92 2 0154 l
+m 889,553,0,72,-10,72 0 0155 m
+n 611,551,0,68,-13,68 0 0156 n
+o 611,551,23,73,-32,73 0 0157 o
+p 611,551,219,76,39,76 1 0160 p
+q 611,551,219,98,-22,92 1 0161 q
+r 389,553,0,148,-13,92 0 0162 r
+s 556,551,23,83,-10,83 0 0163 s
+t 333,678,23,131,-51,92 2 0164 t
+u 611,542,23,95,-38,92 0 0165 u
+v 556,542,0,145,-79,92 0 0166 v
+w 778,542,0,153,-70,92 0 0167 w
+x 556,542,0,142,34,92 0 0170 x
+y 556,542,219,147,13,92 1 0171 y
+z 500,542,0,125,29,92 0 0172 z
+lC 389,729,202,133,-34,92 3 0173 braceleft
+{ "
+ba 280,729,202,105,-7,92 3 0174 bar
+| "
+rC 389,729,202,80,21,80 3 0175 braceright
+} "
+a~ 333,729,0,217,-67,92 2 0176 tilde
+~ "
+--- 278,125,135,0,13 1 0200 quotesinglbase
+Fo 556,484,0,65,-85,65 0 0201 guillemotleft
+char171 "
+Fc 556,482,0,27,-53,27 0 0202 guillemotright
+char187 "
+bu 350,425,0,67,-61,67 0 0203 bullet
+--- 556,745,221,184,71,92 3 0204 florin
+f/ 167,715,20,372,227,92 2 0205 fraction
+%0 1000,745,20,71,-22,71 2 0206 perthousand
+dg 556,708,195,120,-59,92 3 0207 dagger
+dd 556,708,195,117,15,92 3 0210 daggerdbl
+en 556,313,0,118,15,92 0 0211 endash
+em 1000,313,0,120,13,92 0 0212 emdash
+fi 611,729,0,142,-35,92 2 0214 fi
+fl 611,729,0,140,-38,92 2 0215 fl
+.i 278,542,0,94,-17,92 0 0220 dotlessi
+ga 333,745,0,56,-125,56 2 0222 grave
+a" 333,745,0,215,-32,92 2 0223 hungarumlaut
+a. 333,729,0,94,-191,92 2 0224 dotaccent
+ab 333,736,0,172,-138,92 2 0225 breve
+ah 333,745,0,202,-117,92 2 0226 caron
+ao 333,745,0,115,-164,92 2 0227 ring
+ho 333,38,195,0,27 1 0230 ogonek
+lq 500,729,0,138,-121,92 2 0231 quotedblleft
+rq 500,729,0,145,-123,92 2 0232 quotedblright
+oe 944,554,21,92,-21,92 0 0233 oe
+/l 278,729,0,144,0,92 2 0234 lslash
+--- 500,141,164,12,13,12 1 0235 quotedblbase
+OE 1000,741,20,157,-40,92 2 0236 OE
+/L 611,729,0,63,-4,63 2 0237 Lslash
+r! 333,542,187,48,24,48 1 0241 exclamdown
+char161 "
+ct 556,637,122,92,-29,92 3 0242 cent
+char162 "
+Po 556,717,20,123,1,92 2 0243 sterling
+char163 "
+Cs 556,604,0,138,-16,92 2 0244 currency
+char164 "
+Ye 556,705,0,196,-57,92 2 0245 yen
+char165 "
+sc 556,728,201,90,-6,90 3 0247 section
+char167 "
+ad 333,731,0,186,-97,92 2 0250 dieresis
+char168 "
+co 737,745,20,150,-4,92 2 0251 copyright
+char169 "
+Of 370,746,0,131,-46,92 2 0252 ordfeminine
+char170 "
+fo 333,476,0,68,-78,68 0 0253 guilsinglleft
+no 584,412,0,98,-53,92 0 0254 logicalnot
+char172 "
+\- 584,290,0,72,-27,72 0 0255 minus
+rg 737,745,20,150,-5,92 2 0256 registered
+char174 "
+a- 333,717,0,184,-100,92 2 0257 macron
+char175 "
+aa 333,745,0,192,-197,92 2 0264 acute
+char180 "
+ps 556,729,195,178,-71,92 3 0266 paragraph
+char182 "
+char183 278,442,0,42,-93,42 0 0267 periodcentered
+ac 333,0,220,0,63 1 0270 cedilla
+char184 "
+Om 365,745,0,156,-42,92 2 0272 ordmasculine
+char186 "
+fc 333,476,0,36,-46,36 0 0273 guilsinglright
+r? 611,542,192,0,-2 1 0277 questiondown
+char191 "
+`A 722,936,0,31,24,31 2 0300 Agrave
+char192 "
+'A 722,936,0,42,24,42 2 0301 Aacute
+char193 "
+^A 722,936,0,31,24,31 2 0302 Acircumflex
+char194 "
+~A 722,920,0,67,24,67 2 0303 Atilde
+char195 "
+:A 722,922,0,36,24,36 2 0304 Adieresis
+char196 "
+oA 722,936,0,31,24,31 2 0305 Aring
+char197 "
+AE 1000,729,0,154,49,92 2 0306 AE
+char198 "
+,C 722,741,220,121,-57,92 3 0307 Ccedilla
+char199 "
+`E 667,936,0,145,-29,92 2 0310 Egrave
+char200 "
+'E 667,936,0,145,-29,92 2 0311 Eacute
+char201 "
+^E 667,936,0,145,-29,92 2 0312 Ecircumflex
+char202 "
+:E 667,922,0,145,-29,92 2 0313 Edieresis
+char203 "
+`I 278,936,0,140,-13,92 2 0314 Igrave
+char204 "
+'I 278,936,0,261,-13,92 2 0315 Iacute
+char205 "
+^I 278,936,0,239,-13,92 2 0316 Icircumflex
+char206 "
+:I 278,922,0,255,-13,92 2 0317 Idieresis
+char207 "
+~N 722,920,0,144,-18,92 2 0321 Ntilde
+char209 "
+`O 778,936,23,100,-56,92 2 0322 Ograve
+char210 "
+'O 778,936,23,100,-56,92 2 0323 Oacute
+char211 "
+^O 778,936,23,100,-56,92 2 0324 Ocircumflex
+char212 "
+~O 778,920,23,100,-56,92 2 0325 Otilde
+char213 "
+:O 778,922,23,100,-56,92 2 0326 Odieresis
+char214 "
+/O 778,754,34,178,16,92 2 0330 Oslash
+char216 "
+`U 722,936,23,137,-69,92 2 0331 Ugrave
+char217 "
+'U 722,936,23,137,-69,92 2 0332 Uacute
+char218 "
+^U 722,936,23,137,-69,92 2 0333 Ucircumflex
+char219 "
+:U 722,922,23,137,-69,92 2 0334 Udieresis
+char220 "
+ss 611,730,16,93,-17,92 2 0337 germandbls
+char223 "
+`a 556,745,24,72,0,72 2 0340 agrave
+char224 "
+'a 556,745,24,81,0,81 2 0341 aacute
+char225 "
+^a 556,745,24,72,0,72 2 0342 acircumflex
+char226 "
+~a 556,729,24,106,0,92 2 0343 atilde
+char227 "
+:a 556,731,24,75,0,75 2 0344 adieresis
+char228 "
+oa 556,745,24,72,0,72 2 0345 aring
+char229 "
+ae 889,555,20,88,-4,88 0 0346 ae
+char230 "
+,c 556,551,220,91,-27,91 1 0347 ccedilla
+char231 "
+`e 556,745,23,85,-14,85 2 0350 egrave
+char232 "
+'e 556,745,23,85,-14,85 2 0351 eacute
+char233 "
+^e 556,745,23,85,-14,85 2 0352 ecircumflex
+char234 "
+:e 556,731,23,85,-14,85 2 0353 edieresis
+char235 "
+`i 278,745,0,94,-17,92 2 0354 igrave
+char236 "
+'i 278,745,0,220,-17,92 2 0355 iacute
+char237 "
+^i 278,745,0,198,-17,92 2 0356 icircumflex
+char238 "
+:i 278,731,0,214,-17,92 2 0357 idieresis
+char239 "
+~n 611,729,0,85,-13,85 2 0361 ntilde
+char241 "
+`o 611,745,23,73,-32,73 2 0362 ograve
+char242 "
+'o 611,745,23,73,-32,73 2 0363 oacute
+char243 "
+^o 611,745,23,73,-32,73 2 0364 ocircumflex
+char244 "
+~o 611,729,23,78,-32,78 2 0365 otilde
+char245 "
+:o 611,731,23,73,-32,73 2 0366 odieresis
+char246 "
+/o 611,561,34,148,38,92 0 0370 oslash
+char248 "
+`u 611,745,23,95,-38,92 2 0371 ugrave
+char249 "
+'u 611,745,23,95,-38,92 2 0372 uacute
+char250 "
+^u 611,745,23,95,-38,92 2 0373 ucircumflex
+char251 "
+:u 611,731,23,95,-38,92 2 0374 udieresis
+char252 "
+:y 556,731,219,147,13,92 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HI b/ps/devps/HI
new file mode 100644
index 000000000..81b101154
--- /dev/null
+++ b/ps/devps/HI
@@ -0,0 +1,409 @@
+name HI
+internalname Helvetica-Oblique
+slant 12
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -9
+A w -18
+A v -18
+A ' -37
+A Y -74
+A W -18
+A V -55
+A T -74
+F . -129
+F , -129
+F A -74
+L y -18
+L ' -55
+L Y -92
+L W -37
+L V -55
+L T -74
+P . -129
+P , -129
+P A -74
+R Y -37
+R W -18
+R V -18
+R T -18
+T y -74
+T w -74
+T u -74
+T ; -74
+T s -92
+T r -74
+T . -92
+T o -92
+T i -9
+T - -92
+T hy -92
+T char173 -92
+T e -92
+T , -92
+T : -74
+T c -92
+T a -92
+T O -18
+T A -74
+V y -18
+V u -18
+V ; -18
+V r -18
+V . -74
+V o -37
+V i -18
+V - -37
+V hy -37
+V char173 -37
+V e -37
+V , -74
+V : -18
+V a -37
+V A -55
+W . -37
+W i -9
+W - -18
+W hy -18
+W char173 -18
+W e -18
+W , -37
+W a -18
+W A -18
+Y v -37
+Y u -37
+Y ; -37
+Y q -55
+Y . -92
+Y p -55
+Y o -55
+Y i -18
+Y - -74
+Y hy -74
+Y char173 -74
+Y e -55
+Y , -92
+Y : -37
+Y a -74
+Y A -55
+f ' 37
+1 1 -74
+` ` -37
+` oq -37
+oq ` -37
+oq oq -37
+' s -18
+' ' -37
+r ' 37
+r . -37
+r - -18
+r hy -18
+r char173 -18
+r , -55
+v . -74
+v , -74
+w . -55
+w , -55
+y . -74
+y , -74
+charset
+ha 469,713,0,77,-65,77 2 0000 asciicircum
+ti 584,438,0,60,-87,60 0 0001 asciitilde
+vS 667,939,23,97,-39,89 2 0002 Scaron
+vZ 611,939,0,176,22,89 2 0003 Zcaron
+vs 500,740,24,97,-11,89 2 0004 scaron
+vz 500,740,0,107,19,89 2 0005 zcaron
+:Y 667,907,0,199,-118,89 2 0006 Ydieresis
+tm 1000,741,0,146,-158,89 2 0007 trademark
+space 278 0 0040
+! 278,729,0,135,-74,89 2 0041 exclam
+" 355,708,0,150,-127,89 2 0042 quotedbl
+# 556,698,20,143,-4,89 2 0043 numbersign
+sh "
+$ 556,770,125,107,-19,89 3 0044 dollar
+Do "
+% 889,708,20,56,-84,56 2 0045 percent
+& 667,710,23,27,-33,27 2 0046 ampersand
+' 222,708,0,136,-115,89 2 0047 quoteright
+( 333,729,213,163,-63,89 3 0050 parenleft
+) 333,729,213,42,57,42 3 0051 parenright
+* 389,740,0,132,-119,89 2 0052 asterisk
++ 584,474,10,57,-42,57 0 0053 plus
+, 278,104,150,0,-5 1 0054 comma
+- 333,313,0,68,-47,68 0 0055 hyphen
+hy "
+char173 "
+. 278,104,0,0,-37 0 0056 period
+/ 278,708,21,206,62,89 2 0057 slash
+sl "
+0 556,709,23,92,-48,89 2 0060 zero
+1 556,709,0,0,-158 2 0061 one
+2 556,710,0,114,16,89 2 0062 two
+3 556,709,23,93,-21,89 2 0063 three
+4 556,709,0,67,-13,67 2 0064 four
+5 556,709,23,123,-20,89 2 0065 five
+6 556,709,23,105,-43,89 2 0066 six
+7 556,709,0,165,-87,89 2 0067 seven
+8 556,709,23,98,-24,89 2 0070 eight
+9 556,709,23,93,-33,89 2 0071 nine
+: 278,525,0,98,-60,89 0 0072 colon
+; 278,516,150,97,-28,89 1 0073 semicolon
+< 584,474,10,101,-37,89 0 0074 less
+= 584,352,0,75,-24,75 0 0075 equal
+> 584,474,10,62,2,62 0 0076 greater
+? 556,738,0,124,-134,89 2 0077 question
+@ 1015,737,146,71,-30,71 3 0100 at
+at "
+A 667,729,0,36,33,36 2 0101 A
+B 667,729,0,94,-29,89 2 0102 B
+C 722,741,23,98,-62,89 2 0103 C
+D 722,729,0,87,-39,87 2 0104 D
+E 667,729,0,134,-40,89 2 0105 E
+F 611,729,0,173,-40,89 2 0106 F
+G 778,741,23,81,-59,81 2 0107 G
+H 722,729,0,127,-33,89 2 0110 H
+I 278,729,0,121,-50,89 2 0111 I
+J 500,729,26,131,3,89 2 0112 J
+K 667,729,0,196,-29,89 2 0113 K
+L 556,729,0,45,-30,45 2 0114 L
+M 833,729,0,133,-25,89 2 0115 M
+N 722,729,0,129,-26,89 2 0116 N
+O 778,741,23,100,-54,89 2 0117 O
+P 667,730,0,116,-41,89 2 0120 P
+Q 778,741,59,100,-54,89 2 0121 Q
+R 722,729,0,98,-43,89 2 0122 R
+S 667,741,23,97,-39,89 2 0123 S
+T 611,729,0,187,-108,89 2 0124 T
+U 722,729,23,128,-74,89 2 0125 U
+V 667,729,0,183,-135,89 2 0126 V
+W 944,729,0,190,-127,89 2 0127 W
+X 667,729,0,177,28,89 2 0130 X
+Y 667,729,0,199,-118,89 2 0131 Y
+Z 611,729,0,176,22,89 2 0132 Z
+[ 278,729,214,177,31,89 3 0133 bracketleft
+lB "
+\ 278,729,20,52,-97,52 2 0134 backslash
+rs "
+] 278,729,215,136,73,89 3 0135 bracketright
+rB "
+a^ 333,741,0,150,-96,89 2 0136 circumflex
+^ "
+_ 556,0,175,45,109,45 1 0137 underscore
+` 222,708,0,136,-113,89 2 0140 quoteleft
+oq "
+a 556,540,23,62,-15,62 0 0141 a
+b 556,729,23,82,-4,82 2 0142 b
+c 500,540,23,104,-26,89 0 0143 c
+d 556,729,23,144,-23,89 2 0144 d
+e 556,541,23,74,-34,74 0 0145 e
+f 278,733,0,185,-39,89 2 0146 f
+g 556,540,220,95,18,89 1 0147 g
+h 556,729,0,68,-20,68 2 0150 h
+i 222,729,0,133,-16,89 2 0151 i
+j 222,729,220,136,115,89 3 0152 j
+k 500,729,0,134,-8,89 2 0153 k
+l 222,729,0,135,-18,89 2 0154 l
+m 833,540,0,69,-21,69 0 0155 m
+n 556,540,0,68,-20,68 0 0156 n
+o 556,540,23,70,-30,70 0 0157 o
+p 556,540,219,80,43,80 1 0160 p
+q 556,540,219,101,-21,89 1 0161 q
+r 333,540,0,153,-19,89 0 0162 r
+s 500,540,24,70,-11,70 0 0163 s
+t 278,667,24,138,-47,89 2 0164 t
+u 556,525,23,88,-38,88 0 0165 u
+v 500,525,0,148,-72,89 0 0166 v
+w 722,525,0,148,-68,89 0 0167 w
+x 500,525,0,133,33,89 0 0170 x
+y 500,525,219,140,42,89 1 0171 y
+z 500,525,0,107,19,89 0 0172 z
+lC 334,731,214,147,-41,89 3 0173 braceleft
+{ "
+ba 260,729,215,105,-4,89 3 0174 bar
+| "
+rC 334,731,214,40,66,40 3 0175 braceright
+} "
+a~ 333,716,0,188,-80,89 2 0176 tilde
+~ "
+--- 222,103,129,8,13,8 1 0200 quotesinglbase
+Fo 556,438,0,42,-97,42 0 0201 guillemotleft
+char171 "
+Fc 556,438,0,12,-71,12 0 0202 guillemotright
+char187 "
+bu 350,470,0,76,-70,76 0 0203 bullet
+--- 556,742,214,190,82,89 3 0204 florin
+f/ 167,708,21,369,228,89 2 0205 fraction
+%0 1000,740,20,74,-43,74 2 0206 perthousand
+dg 556,710,178,114,-77,89 3 0207 dagger
+dd 556,710,178,114,-1,89 3 0210 daggerdbl
+en 556,313,0,122,4,89 0 0211 endash
+em 1000,313,0,118,8,89 0 0212 emdash
+fi 500,733,0,141,-33,89 2 0214 fi
+fl 500,733,0,135,-38,89 2 0215 fl
+.i 278,525,0,62,-44,62 0 0220 dotlessi
+ga 333,740,0,74,-129,74 2 0222 grave
+a" 333,740,0,222,-41,89 2 0223 hungarumlaut
+a. 333,709,0,87,-194,87 2 0224 dotaccent
+ab 333,729,0,188,-115,89 2 0225 breve
+ah 333,740,0,180,-126,89 2 0226 caron
+ao 333,741,0,113,-166,89 2 0227 ring
+ho 333,15,189,0,15 1 0230 ogonek
+lq 333,708,0,166,-96,89 2 0231 quotedblleft
+rq 333,708,0,169,-100,89 2 0232 quotedblright
+oe 944,540,22,72,-35,72 0 0233 oe
+/l 222,729,0,140,-12,89 2 0234 lslash
+--- 333,103,129,39,30,39 1 0235 quotedblbase
+OE 1000,739,20,158,-51,89 2 0236 OE
+/L 556,729,0,64,-11,64 2 0237 Lslash
+r! 333,525,214,34,-26,34 1 0241 exclamdown
+char161 "
+ct 556,628,120,79,-46,79 3 0242 cent
+char162 "
+Po 556,726,21,122,6,89 2 0243 sterling
+char163 "
+Cs 556,554,0,87,-60,87 0 0244 currency
+char164 "
+Ye 556,710,0,190,-50,89 2 0245 yen
+char165 "
+sc 556,729,215,83,-13,83 3 0247 section
+char167 "
+ad 333,708,0,163,-109,89 2 0250 dieresis
+char168 "
+co 737,741,23,149,-5,89 2 0251 copyright
+char169 "
+Of 370,740,0,121,-57,89 2 0252 ordfeminine
+char170 "
+fo 333,436,0,53,-90,53 0 0253 guilsinglleft
+no 584,352,0,85,-49,85 0 0254 logicalnot
+char172 "
+\- 584,270,0,67,-31,67 0 0255 minus
+rg 737,741,23,149,-5,89 2 0256 registered
+char174 "
+a- 333,694,0,167,-110,89 2 0257 macron
+char175 "
+aa 333,740,0,175,-168,89 2 0264 acute
+char180 "
+ps 537,729,178,190,-95,89 3 0266 paragraph
+char182 "
+char183 278,442,0,65,-116,65 0 0267 periodcentered
+ac 333,0,214,0,49 1 0270 cedilla
+char184 "
+Om 365,741,0,137,-64,89 2 0272 ordmasculine
+char186 "
+fc 333,436,0,24,-59,24 0 0273 guilsinglright
+r? 611,525,213,0,-36 1 0277 questiondown
+char191 "
+`A 667,939,0,36,33,36 2 0300 Agrave
+char192 "
+'A 667,939,0,50,33,50 2 0301 Aacute
+char193 "
+^A 667,940,0,36,33,36 2 0302 Acircumflex
+char194 "
+~A 667,915,0,63,33,63 2 0303 Atilde
+char195 "
+:A 667,907,0,38,33,38 2 0304 Adieresis
+char196 "
+oA 667,940,0,36,33,36 2 0305 Aring
+char197 "
+AE 1000,729,0,137,39,89 2 0306 AE
+char198 "
+,C 722,741,214,98,-62,89 3 0307 Ccedilla
+char199 "
+`E 667,939,0,134,-40,89 2 0310 Egrave
+char200 "
+'E 667,939,0,134,-40,89 2 0311 Eacute
+char201 "
+^E 667,940,0,134,-40,89 2 0312 Ecircumflex
+char202 "
+:E 667,907,0,134,-40,89 2 0313 Edieresis
+char203 "
+`I 278,939,0,150,-50,89 2 0314 Igrave
+char204 "
+'I 278,939,0,251,-50,89 2 0315 Iacute
+char205 "
+^I 278,940,0,226,-50,89 2 0316 Icircumflex
+char206 "
+:I 278,907,0,239,-50,89 2 0317 Idieresis
+char207 "
+~N 722,915,0,129,-26,89 2 0321 Ntilde
+char209 "
+`O 778,939,23,100,-54,89 2 0322 Ograve
+char210 "
+'O 778,939,23,100,-54,89 2 0323 Oacute
+char211 "
+^O 778,940,23,100,-54,89 2 0324 Ocircumflex
+char212 "
+~O 778,915,23,100,-54,89 2 0325 Otilde
+char213 "
+:O 778,907,23,100,-54,89 2 0326 Odieresis
+char214 "
+/O 778,742,23,139,18,89 2 0330 Oslash
+char216 "
+`U 722,939,23,128,-74,89 2 0331 Ugrave
+char217 "
+'U 722,939,23,128,-74,89 2 0332 Uacute
+char218 "
+^U 722,940,23,128,-74,89 2 0333 Ucircumflex
+char219 "
+:U 722,907,23,128,-74,89 2 0334 Udieresis
+char220 "
+ss 611,729,20,94,-76,89 2 0337 germandbls
+char223 "
+`a 556,740,23,62,-15,62 2 0340 agrave
+char224 "
+'a 556,740,23,64,-15,64 2 0341 aacute
+char225 "
+^a 556,741,23,62,-15,62 2 0342 acircumflex
+char226 "
+~a 556,716,23,77,-15,77 2 0343 atilde
+char227 "
+:a 556,708,23,62,-15,62 2 0344 adieresis
+char228 "
+oa 556,741,23,62,-15,62 2 0345 aring
+char229 "
+ae 889,546,20,76,-9,76 0 0346 ae
+char230 "
+,c 500,540,214,104,-26,89 1 0347 ccedilla
+char231 "
+`e 556,740,23,74,-34,74 2 0350 egrave
+char232 "
+'e 556,740,23,74,-34,74 2 0351 eacute
+char233 "
+^e 556,741,23,74,-34,74 2 0352 ecircumflex
+char234 "
+:e 556,708,23,74,-34,74 2 0353 edieresis
+char235 "
+`i 278,740,0,102,-44,89 2 0354 igrave
+char236 "
+'i 278,740,0,203,-44,89 2 0355 iacute
+char237 "
+^i 278,741,0,178,-44,89 2 0356 icircumflex
+char238 "
+:i 278,708,0,191,-44,89 2 0357 idieresis
+char239 "
+~n 556,716,0,83,-20,83 2 0361 ntilde
+char241 "
+`o 556,740,23,70,-30,70 2 0362 ograve
+char242 "
+'o 556,740,23,70,-30,70 2 0363 oacute
+char243 "
+^o 556,741,23,70,-30,70 2 0364 ocircumflex
+char244 "
+~o 556,716,23,77,-30,77 2 0365 otilde
+char245 "
+:o 556,708,23,70,-30,70 2 0366 odieresis
+char246 "
+/o 611,548,27,78,31,78 0 0370 oslash
+char248 "
+`u 556,740,23,88,-38,88 2 0371 ugrave
+char249 "
+'u 556,740,23,88,-38,88 2 0372 uacute
+char250 "
+^u 556,741,23,88,-38,88 2 0373 ucircumflex
+char251 "
+:u 556,708,23,88,-38,88 2 0374 udieresis
+char252 "
+:y 500,708,219,140,42,89 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HNB b/ps/devps/HNB
new file mode 100644
index 000000000..2ec3b8721
--- /dev/null
+++ b/ps/devps/HNB
@@ -0,0 +1,410 @@
+name HNB
+internalname Helvetica-Narrow-Bold
+spacewidth 228
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -30
+A w -14
+A v -30
+A ' -45
+A Y -75
+A W -45
+A V -60
+A T -60
+F . -91
+F , -91
+F A -45
+L y -30
+L ' -45
+L Y -75
+L W -45
+L V -60
+L T -60
+P . -105
+P , -105
+P A -60
+R Y -30
+R W -14
+R V -14
+T y -60
+T w -60
+T u -60
+T ; -91
+T s -60
+T r -45
+T . -91
+T o -60
+T i -14
+T - -45
+T hy -45
+T char173 -45
+T e -60
+T , -91
+T : -91
+T c -60
+T a -60
+T O -14
+T A -60
+V y -30
+V u -30
+V ; -45
+V r -45
+V . -75
+V o -60
+V i -14
+V - -45
+V hy -45
+V char173 -45
+V e -45
+V , -75
+V : -45
+V a -45
+V A -60
+W y -14
+W u -14
+W ; -14
+W r -14
+W . -45
+W o -14
+W i -7
+W - -16
+W hy -16
+W char173 -16
+W e -14
+W , -45
+W : -14
+W a -30
+W A -45
+Y v -45
+Y u -45
+Y ; -60
+Y q -60
+Y . -91
+Y p -45
+Y o -60
+Y i -30
+Y - -45
+Y hy -45
+Y char173 -45
+Y e -45
+Y , -91
+Y : -60
+Y a -45
+Y A -75
+f ' 14
+1 1 -45
+` ` -30
+` oq -30
+oq ` -30
+oq oq -30
+' s -30
+' ' -30
+r ' 30
+r . -45
+r , -45
+v . -60
+v , -60
+w . -30
+w , -30
+y . -60
+y , -60
+charset
+ha 479,696 2 0000 asciicircum
+ti 479,322 0 0001 asciitilde
+vS 547,936,23 2 0002 Scaron
+vZ 501,936 2 0003 Zcaron
+vs 456,745,23 2 0004 scaron
+vz 410,745 2 0005 zcaron
+:Y 547,922 2 0006 Ydieresis
+tm 820,745 2 0007 trademark
+space 228 0 0040
+! 273,729 2 0041 exclam
+" 389,729 2 0042 quotedbl
+# 456,696,30 2 0043 numbersign
+sh "
+$ 456,765,125 3 0044 dollar
+Do "
+% 729,708,18 2 0045 percent
+& 592,729,20 2 0046 ampersand
+' 228,729 2 0047 quoteright
+( 273,729,202 3 0050 parenleft
+) 273,729,202 3 0051 parenright
+* 319,730 2 0052 asterisk
++ 479,474,10 0 0053 plus
+, 228,146,174 1 0054 comma
+- 273,344 0 0055 hyphen
+hy "
+char173 "
+. 228,146 0 0056 period
+/ 228,715,14 2 0057 slash
+sl "
+0 456,725,23 2 0060 zero
+1 456,709 2 0061 one
+2 456,726 2 0062 two
+3 456,726,23 2 0063 three
+4 456,709 2 0064 four
+5 456,709,24 2 0065 five
+6 456,727,23 2 0066 six
+7 456,709 2 0067 seven
+8 456,726,23 2 0070 eight
+9 456,728,23 2 0071 nine
+: 273,521 0 0072 colon
+; 273,521,174 1 0073 semicolon
+< 479,474,10 0 0074 less
+= 479,412 0 0075 equal
+> 479,474,10 0 0076 greater
+? 501,744 2 0077 question
+@ 800,746,136 3 0100 at
+at "
+A 592,729 2 0101 A
+B 592,729 2 0102 B
+C 592,741,23 2 0103 C
+D 592,729 2 0104 D
+E 547,729 2 0105 E
+F 501,729 2 0106 F
+G 638,741,24 2 0107 G
+H 592,729 2 0110 H
+I 228,729 2 0111 I
+J 456,729,23 2 0112 J
+K 592,729 2 0113 K
+L 501,729 2 0114 L
+M 683,729 2 0115 M
+N 592,729 2 0116 N
+O 638,741,23 2 0117 O
+P 547,729 2 0120 P
+Q 638,741,54 2 0121 Q
+R 592,729 2 0122 R
+S 547,741,23 2 0123 S
+T 501,729 2 0124 T
+U 592,729,23 2 0125 U
+V 547,729 2 0126 V
+W 774,729 2 0127 W
+X 547,729 2 0130 X
+Y 547,729 2 0131 Y
+Z 501,729 2 0132 Z
+[ 273,729,202 3 0133 bracketleft
+lB "
+\ 228,708,21 2 0134 backslash
+rs "
+] 273,729,202 3 0135 bracketright
+rB "
+a^ 273,745 2 0136 circumflex
+^ "
+_ 456,0,200 1 0137 underscore
+` 228,729 2 0140 quoteleft
+oq "
+a 456,551,24 0 0141 a
+b 501,729,23 2 0142 b
+c 456,551,23 0 0143 c
+d 501,729,23 2 0144 d
+e 456,551,23 0 0145 e
+f 273,729 2 0146 f
+g 501,551,220 1 0147 g
+h 501,729 2 0150 h
+i 228,729 2 0151 i
+j 228,729,219 3 0152 j
+k 456,729 2 0153 k
+l 228,729 2 0154 l
+m 729,553 0 0155 m
+n 501,551 0 0156 n
+o 501,551,23 0 0157 o
+p 501,551,219 1 0160 p
+q 501,551,219 1 0161 q
+r 319,553 0 0162 r
+s 456,551,23 0 0163 s
+t 273,678,23 2 0164 t
+u 501,542,23 0 0165 u
+v 456,542 0 0166 v
+w 638,542 0 0167 w
+x 456,542 0 0170 x
+y 456,542,219 1 0171 y
+z 410,542 0 0172 z
+lC 319,729,202 3 0173 braceleft
+{ "
+ba 230,729,202 3 0174 bar
+| "
+rC 319,729,202 3 0175 braceright
+} "
+a~ 273,729 2 0176 tilde
+~ "
+--- 228,125,135 1 0200 quotesinglbase
+Fo 456,484 0 0201 guillemotleft
+char171 "
+Fc 456,482 0 0202 guillemotright
+char187 "
+bu 287,425 0 0203 bullet
+--- 456,745,221 3 0204 florin
+f/ 137,715,20 2 0205 fraction
+%0 820,745,20 2 0206 perthousand
+dg 456,708,195 3 0207 dagger
+dd 456,708,195 3 0210 daggerdbl
+en 456,313 0 0211 endash
+em 820,313 0 0212 emdash
+fi 501,729 2 0214 fi
+fl 501,729 2 0215 fl
+.i 228,542 0 0220 dotlessi
+ga 273,745 2 0222 grave
+a" 273,745 2 0223 hungarumlaut
+a. 273,729 2 0224 dotaccent
+ab 273,736 2 0225 breve
+ah 273,745 2 0226 caron
+ao 273,745 2 0227 ring
+ho 273,38,195 1 0230 ogonek
+lq 410,729 2 0231 quotedblleft
+rq 410,729 2 0232 quotedblright
+oe 774,554,21 0 0233 oe
+/l 228,729 2 0234 lslash
+--- 410,141,164 1 0235 quotedblbase
+OE 820,741,20 2 0236 OE
+/L 501,729 2 0237 Lslash
+r! 273,542,187 1 0241 exclamdown
+char161 "
+ct 456,637,122 3 0242 cent
+char162 "
+Po 456,717,20 2 0243 sterling
+char163 "
+Cs 456,604 2 0244 currency
+char164 "
+Ye 456,705 2 0245 yen
+char165 "
+sc 456,728,201 3 0247 section
+char167 "
+ad 273,731 2 0250 dieresis
+char168 "
+co 604,745,20 2 0251 copyright
+char169 "
+Of 303,746 2 0252 ordfeminine
+char170 "
+fo 273,476 0 0253 guilsinglleft
+no 479,412 0 0254 logicalnot
+char172 "
+\- 479,290 0 0255 minus
+rg 604,745,20 2 0256 registered
+char174 "
+a- 273,717 2 0257 macron
+char175 "
+aa 273,745 2 0264 acute
+char180 "
+ps 456,729,195 3 0266 paragraph
+char182 "
+char183 228,442 0 0267 periodcentered
+ac 273,0,220 1 0270 cedilla
+char184 "
+Om 299,745 2 0272 ordmasculine
+char186 "
+fc 273,476 0 0273 guilsinglright
+r? 501,542,192 1 0277 questiondown
+char191 "
+`A 592,936 2 0300 Agrave
+char192 "
+'A 592,936 2 0301 Aacute
+char193 "
+^A 592,936 2 0302 Acircumflex
+char194 "
+~A 592,920 2 0303 Atilde
+char195 "
+:A 592,922 2 0304 Adieresis
+char196 "
+oA 592,936 2 0305 Aring
+char197 "
+AE 820,729 2 0306 AE
+char198 "
+,C 592,741,220 3 0307 Ccedilla
+char199 "
+`E 547,936 2 0310 Egrave
+char200 "
+'E 547,936 2 0311 Eacute
+char201 "
+^E 547,936 2 0312 Ecircumflex
+char202 "
+:E 547,922 2 0313 Edieresis
+char203 "
+`I 228,936 2 0314 Igrave
+char204 "
+'I 228,936 2 0315 Iacute
+char205 "
+^I 228,936 2 0316 Icircumflex
+char206 "
+:I 228,922 2 0317 Idieresis
+char207 "
+~N 592,920 2 0321 Ntilde
+char209 "
+`O 638,936,23 2 0322 Ograve
+char210 "
+'O 638,936,23 2 0323 Oacute
+char211 "
+^O 638,936,23 2 0324 Ocircumflex
+char212 "
+~O 638,920,23 2 0325 Otilde
+char213 "
+:O 638,922,23 2 0326 Odieresis
+char214 "
+/O 638,754,34 2 0330 Oslash
+char216 "
+`U 592,936,23 2 0331 Ugrave
+char217 "
+'U 592,936,23 2 0332 Uacute
+char218 "
+^U 592,936,23 2 0333 Ucircumflex
+char219 "
+:U 592,922,23 2 0334 Udieresis
+char220 "
+ss 501,730,16 2 0337 germandbls
+char223 "
+`a 456,745,24 2 0340 agrave
+char224 "
+'a 456,745,24 2 0341 aacute
+char225 "
+^a 456,745,24 2 0342 acircumflex
+char226 "
+~a 456,729,24 2 0343 atilde
+char227 "
+:a 456,731,24 2 0344 adieresis
+char228 "
+oa 456,745,24 2 0345 aring
+char229 "
+ae 729,555,20 0 0346 ae
+char230 "
+,c 456,551,220 1 0347 ccedilla
+char231 "
+`e 456,745,23 2 0350 egrave
+char232 "
+'e 456,745,23 2 0351 eacute
+char233 "
+^e 456,745,23 2 0352 ecircumflex
+char234 "
+:e 456,731,23 2 0353 edieresis
+char235 "
+`i 228,745 2 0354 igrave
+char236 "
+'i 228,745 2 0355 iacute
+char237 "
+^i 228,745 2 0356 icircumflex
+char238 "
+:i 228,731 2 0357 idieresis
+char239 "
+~n 501,729 2 0361 ntilde
+char241 "
+`o 501,745,23 2 0362 ograve
+char242 "
+'o 501,745,23 2 0363 oacute
+char243 "
+^o 501,745,23 2 0364 ocircumflex
+char244 "
+~o 501,729,23 2 0365 otilde
+char245 "
+:o 501,731,23 2 0366 odieresis
+char246 "
+/o 501,561,34 0 0370 oslash
+char248 "
+`u 501,745,23 2 0371 ugrave
+char249 "
+'u 501,745,23 2 0372 uacute
+char250 "
+^u 501,745,23 2 0373 ucircumflex
+char251 "
+:u 501,731,23 2 0374 udieresis
+char252 "
+:y 456,731,219 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HNBI b/ps/devps/HNBI
new file mode 100644
index 000000000..840c93112
--- /dev/null
+++ b/ps/devps/HNBI
@@ -0,0 +1,409 @@
+name HNBI
+internalname Helvetica-Narrow-BoldOblique
+slant 12
+spacewidth 228
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A ' -45
+A Y -60
+A W -45
+A V -60
+A T -60
+F . -91
+F , -91
+F A -45
+L ' -75
+L Y -60
+L W -45
+L V -45
+L T -60
+P . -105
+P , -105
+P A -60
+R Y -14
+R W -14
+R T -14
+T y -30
+T w -30
+T u -6
+T ; -60
+T s -30
+T r -6
+T . -60
+T o -30
+T i -6
+T - -45
+T hy -45
+T char173 -45
+T e -30
+T , -60
+T : -60
+T c -30
+T a -30
+T O -14
+T A -60
+V y -14
+V u -14
+V ; -30
+V r -14
+V . -75
+V o -30
+V i -30
+V - -30
+V hy -30
+V char173 -30
+V e -30
+V , -75
+V : -30
+V a -30
+V A -60
+W y -14
+W u -14
+W ; -30
+W r -14
+W . -60
+W o -14
+W i -7
+W - -30
+W hy -30
+W char173 -30
+W e -14
+W , -60
+W : -30
+W a -14
+W A -45
+Y v -30
+Y u -30
+Y ; -45
+Y q -30
+Y . -75
+Y p -30
+Y i -30
+Y o -30
+Y - -60
+Y hy -60
+Y char173 -60
+Y e -30
+Y , -75
+Y : -45
+Y a -30
+Y A -60
+f ' 14
+f f -14
+1 1 -60
+` ` -30
+` oq -30
+oq ` -30
+oq oq -30
+' t 14
+' s -14
+' ' -30
+r ' 30
+r . -45
+r , -45
+v . -45
+v , -45
+w . -30
+w , -30
+y . -30
+y , -30
+charset
+ha 479,696,0,46,-47,46 2 0000 asciicircum
+ti 479,322,0,48,-30,48 0 0001 asciitilde
+vS 547,936,23,98,-12,92 2 0002 Scaron
+vZ 501,936,0,150,25,92 2 0003 Zcaron
+vs 456,745,23,84,1,84 2 0004 scaron
+vz 410,745,0,112,33,92 2 0005 zcaron
+:Y 547,922,0,163,-99,92 2 0006 Ydieresis
+tm 820,745,0,122,-125,92 2 0007 trademark
+space 228 0 0040
+! 273,729,0,119,-42,92 2 0041 exclam
+" 389,729,0,136,-95,92 2 0042 quotedbl
+# 456,696,30,135,23,92 2 0043 numbersign
+sh "
+$ 456,765,125,109,2,92 3 0044 dollar
+Do "
+% 729,708,18,62,-56,62 2 0045 percent
+& 592,729,20,48,-23,48 2 0046 ampersand
+' 228,729,0,114,-86,92 2 0047 quoteright
+( 273,729,202,153,-19,92 3 0050 parenleft
+) 273,729,202,69,67,69 3 0051 parenright
+* 319,730,0,123,-69,92 2 0052 asterisk
++ 479,474,10,59,-21,59 0 0053 plus
+, 228,146,174,22,28,22 1 0054 comma
+- 273,344,0,81,-8,81 0 0055 hyphen
+hy "
+char173 "
+. 228,146,0,22,-2,22 0 0056 period
+/ 228,715,14,172,51,92 2 0057 slash
+sl "
+0 456,725,23,98,-17,92 2 0060 zero
+1 456,709,0,27,-91,27 2 0061 one
+2 456,726,0,109,25,92 2 0062 two
+3 456,726,23,97,-6,92 2 0063 three
+4 456,709,0,84,4,84 2 0064 four
+5 456,709,24,119,2,92 2 0065 five
+6 456,727,23,106,-20,92 2 0066 six
+7 456,709,0,151,-58,92 2 0067 seven
+8 456,726,23,102,1,92 2 0070 eight
+9 456,728,23,95,-6,92 2 0071 nine
+: 273,521,0,84,-43,84 0 0072 colon
+; 273,521,174,84,-13,84 1 0073 semicolon
+< 479,474,10,87,-13,87 0 0074 less
+= 479,412,0,81,0,81 0 0075 equal
+> 479,474,10,56,19,56 0 0076 greater
+? 501,744,0,100,-88,92 2 0077 question
+@ 800,746,136,97,-10,92 3 0100 at
+at "
+A 592,729,0,34,29,34 2 0101 A
+B 592,729,0,83,-17,83 2 0102 B
+C 592,741,23,108,-38,92 2 0103 C
+D 592,729,0,95,-13,92 2 0104 D
+E 547,729,0,128,-15,92 2 0105 E
+F 501,729,0,157,-11,92 2 0106 F
+G 638,741,24,84,-38,84 2 0107 G
+H 592,729,0,124,-6,92 2 0110 H
+I 228,729,0,124,-2,92 2 0111 I
+J 456,729,23,120,1,92 2 0112 J
+K 592,729,0,150,-11,92 2 0113 K
+L 501,729,0,46,-16,46 2 0114 L
+M 683,729,0,131,-4,92 2 0115 M
+N 592,729,0,127,-6,92 2 0116 N
+O 638,741,23,91,-37,91 2 0117 O
+P 547,729,0,116,-12,92 2 0120 P
+Q 638,741,54,94,-39,92 2 0121 Q
+R 592,729,0,102,-16,92 2 0122 R
+S 547,741,23,98,-12,92 2 0123 S
+T 501,729,0,167,-67,92 2 0124 T
+U 592,729,23,121,-47,92 2 0125 U
+V 547,729,0,161,-97,92 2 0126 V
+W 774,729,0,167,-88,92 2 0127 W
+X 547,729,0,161,32,92 2 0130 X
+Y 547,729,0,163,-99,92 2 0131 Y
+Z 501,729,0,150,25,92 2 0132 Z
+[ 273,729,202,156,31,92 3 0133 bracketleft
+lB "
+\ 228,708,21,55,-64,55 2 0134 backslash
+rs "
+] 273,729,202,117,70,92 3 0135 bracketright
+rB "
+a^ 273,745,0,148,-60,92 2 0136 circumflex
+^ "
+_ 456,0,200,45,103,45 1 0137 underscore
+` 228,729,0,115,-87,92 2 0140 quoteleft
+oq "
+a 456,551,24,68,8,68 0 0141 a
+b 501,729,23,74,2,74 2 0142 b
+c 456,551,23,83,-13,83 0 0143 c
+d 501,729,23,123,-15,92 2 0144 d
+e 456,551,23,79,-3,79 0 0145 e
+f 273,729,0,158,-24,92 2 0146 f
+g 501,551,220,87,29,87 1 0147 g
+h 501,729,0,65,-5,65 2 0150 h
+i 228,729,0,119,-5,92 2 0151 i
+j 228,729,219,121,85,92 3 0152 j
+k 456,729,0,128,2,92 2 0153 k
+l 228,729,0,119,-5,92 2 0154 l
+m 729,553,0,68,1,68 0 0155 m
+n 501,551,0,65,-2,65 0 0156 n
+o 501,551,23,69,-17,69 0 0157 o
+p 501,551,219,71,41,71 1 0160 p
+q 501,551,219,89,-9,89 1 0161 q
+r 319,553,0,131,-2,92 0 0162 r
+s 456,551,23,77,1,77 0 0163 s
+t 273,678,23,116,-33,92 2 0164 t
+u 501,542,23,87,-22,87 0 0165 u
+v 456,542,0,128,-56,92 0 0166 v
+w 638,542,0,135,-49,92 0 0167 w
+x 456,542,0,126,37,92 0 0170 x
+y 456,542,219,130,19,92 1 0171 y
+z 410,542,0,112,33,92 0 0172 z
+lC 319,729,202,118,-19,92 3 0173 braceleft
+{ "
+ba 230,729,202,95,3,92 3 0174 bar
+| "
+rC 319,729,202,74,26,74 3 0175 braceright
+} "
+a~ 273,729,0,187,-46,92 2 0176 tilde
+~ "
+--- 228,125,135,9,19,9 1 0200 quotesinglbase
+Fo 456,484,0,62,-60,62 0 0201 guillemotleft
+char171 "
+Fc 456,482,0,31,-34,31 0 0202 guillemotright
+char187 "
+bu 287,425,0,64,-41,64 0 0203 bullet
+--- 456,745,221,160,67,92 3 0204 florin
+f/ 137,715,20,314,195,92 2 0205 fraction
+%0 820,745,20,67,-9,67 2 0206 perthousand
+dg 456,708,195,108,-40,92 3 0207 dagger
+dd 456,708,195,105,21,92 3 0210 daggerdbl
+en 456,313,0,105,21,92 0 0211 endash
+em 820,313,0,107,19,92 0 0212 emdash
+fi 501,729,0,125,-20,92 2 0214 fi
+fl 501,729,0,124,-22,92 2 0215 fl
+.i 228,542,0,86,-5,86 0 0220 dotlessi
+ga 273,745,0,56,-94,56 2 0222 grave
+a" 273,745,0,186,-18,92 2 0223 hungarumlaut
+a. 273,729,0,86,-148,86 2 0224 dotaccent
+ab 273,736,0,150,-104,92 2 0225 breve
+ah 273,745,0,175,-87,92 2 0226 caron
+ao 273,745,0,104,-126,92 2 0227 ring
+ho 273,38,195,0,31 1 0230 ogonek
+lq 410,729,0,122,-90,92 2 0231 quotedblleft
+rq 410,729,0,128,-92,92 2 0232 quotedblright
+oe 774,554,21,85,-8,85 0 0233 oe
+/l 228,729,0,126,9,92 2 0234 lslash
+--- 410,141,164,18,19,18 1 0235 quotedblbase
+OE 820,741,20,138,-24,92 2 0236 OE
+/L 501,729,0,61,6,61 2 0237 Lslash
+r! 273,542,187,48,29,48 1 0241 exclamdown
+char161 "
+ct 456,637,122,85,-14,85 3 0242 cent
+char162 "
+Po 456,717,20,110,9,92 2 0243 sterling
+char163 "
+Cs 456,604,0,122,-4,92 2 0244 currency
+char164 "
+Ye 456,705,0,170,-38,92 2 0245 yen
+char165 "
+sc 456,728,201,83,4,83 3 0247 section
+char167 "
+ad 273,731,0,162,-71,92 2 0250 dieresis
+char168 "
+co 604,745,20,132,5,92 2 0251 copyright
+char169 "
+Of 303,746,0,116,-28,92 2 0252 ordfeminine
+char170 "
+fo 273,476,0,65,-55,65 0 0253 guilsinglleft
+no 479,412,0,89,-35,89 0 0254 logicalnot
+char172 "
+\- 479,290,0,68,-13,68 0 0255 minus
+rg 604,745,20,132,5,92 2 0256 registered
+char174 "
+a- 273,717,0,160,-73,92 2 0257 macron
+char175 "
+aa 273,745,0,167,-153,92 2 0264 acute
+char180 "
+ps 456,729,195,154,-49,92 3 0266 paragraph
+char182 "
+char183 228,442,0,43,-67,43 0 0267 periodcentered
+ac 273,0,220,0,61 1 0270 cedilla
+char184 "
+Om 299,745,0,136,-25,92 2 0272 ordmasculine
+char186 "
+fc 273,476,0,39,-29,39 0 0273 guilsinglright
+r? 501,542,192,5,7,5 1 0277 questiondown
+char191 "
+`A 592,936,0,34,29,34 2 0300 Agrave
+char192 "
+'A 592,936,0,44,29,44 2 0301 Aacute
+char193 "
+^A 592,936,0,34,29,34 2 0302 Acircumflex
+char194 "
+~A 592,920,0,64,29,64 2 0303 Atilde
+char195 "
+:A 592,922,0,39,29,39 2 0304 Adieresis
+char196 "
+oA 592,936,0,34,29,34 2 0305 Aring
+char197 "
+AE 820,729,0,135,49,92 2 0306 AE
+char198 "
+,C 592,741,220,108,-38,92 3 0307 Ccedilla
+char199 "
+`E 547,936,0,128,-15,92 2 0310 Egrave
+char200 "
+'E 547,936,0,128,-15,92 2 0311 Eacute
+char201 "
+^E 547,936,0,128,-15,92 2 0312 Ecircumflex
+char202 "
+:E 547,922,0,128,-15,92 2 0313 Edieresis
+char203 "
+`I 228,936,0,124,-2,92 2 0314 Igrave
+char204 "
+'I 228,936,0,223,-2,92 2 0315 Iacute
+char205 "
+^I 228,936,0,204,-2,92 2 0316 Icircumflex
+char206 "
+:I 228,922,0,218,-2,92 2 0317 Idieresis
+char207 "
+~N 592,920,0,127,-6,92 2 0321 Ntilde
+char209 "
+`O 638,936,23,91,-37,91 2 0322 Ograve
+char210 "
+'O 638,936,23,91,-37,91 2 0323 Oacute
+char211 "
+^O 638,936,23,91,-37,91 2 0324 Ocircumflex
+char212 "
+~O 638,920,23,91,-37,91 2 0325 Otilde
+char213 "
+:O 638,922,23,91,-37,91 2 0326 Odieresis
+char214 "
+/O 638,754,34,155,23,92 2 0330 Oslash
+char216 "
+`U 592,936,23,121,-47,92 2 0331 Ugrave
+char217 "
+'U 592,936,23,121,-47,92 2 0332 Uacute
+char218 "
+^U 592,936,23,121,-47,92 2 0333 Ucircumflex
+char219 "
+:U 592,922,23,121,-47,92 2 0334 Udieresis
+char220 "
+ss 501,730,16,85,-5,85 2 0337 germandbls
+char223 "
+`a 456,745,24,68,8,68 2 0340 agrave
+char224 "
+'a 456,745,24,76,8,76 2 0341 aacute
+char225 "
+^a 456,745,24,68,8,68 2 0342 acircumflex
+char226 "
+~a 456,729,24,96,8,92 2 0343 atilde
+char227 "
+:a 456,731,24,71,8,71 2 0344 adieresis
+char228 "
+oa 456,745,24,68,8,68 2 0345 aring
+char229 "
+ae 729,555,20,81,6,81 0 0346 ae
+char230 "
+,c 456,551,220,83,-13,83 1 0347 ccedilla
+char231 "
+`e 456,745,23,79,-3,79 2 0350 egrave
+char232 "
+'e 456,745,23,79,-3,79 2 0351 eacute
+char233 "
+^e 456,745,23,79,-3,79 2 0352 ecircumflex
+char234 "
+:e 456,731,23,79,-3,79 2 0353 edieresis
+char235 "
+`i 228,745,0,86,-5,86 2 0354 igrave
+char236 "
+'i 228,745,0,190,-5,92 2 0355 iacute
+char237 "
+^i 228,745,0,171,-5,92 2 0356 icircumflex
+char238 "
+:i 228,731,0,185,-5,92 2 0357 idieresis
+char239 "
+~n 501,729,0,79,-2,79 2 0361 ntilde
+char241 "
+`o 501,745,23,69,-17,69 2 0362 ograve
+char242 "
+'o 501,745,23,69,-17,69 2 0363 oacute
+char243 "
+^o 501,745,23,69,-17,69 2 0364 ocircumflex
+char244 "
+~o 501,729,23,73,-17,73 2 0365 otilde
+char245 "
+:o 501,731,23,69,-17,69 2 0366 odieresis
+char246 "
+/o 501,561,34,130,41,92 0 0370 oslash
+char248 "
+`u 501,745,23,87,-22,87 2 0371 ugrave
+char249 "
+'u 501,745,23,87,-22,87 2 0372 uacute
+char250 "
+^u 501,745,23,87,-22,87 2 0373 ucircumflex
+char251 "
+:u 501,731,23,87,-22,87 2 0374 udieresis
+char252 "
+:y 456,731,219,130,19,92 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HNI b/ps/devps/HNI
new file mode 100644
index 000000000..3f3cbeed3
--- /dev/null
+++ b/ps/devps/HNI
@@ -0,0 +1,409 @@
+name HNI
+internalname Helvetica-Narrow-Oblique
+slant 12
+spacewidth 228
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -7
+A w -14
+A v -14
+A ' -40
+A Y -60
+A W -30
+A V -60
+A T -60
+F . -105
+F , -105
+F A -60
+L y -14
+L ' -45
+L Y -75
+L W -30
+L V -45
+L T -60
+P . -105
+P , -105
+P A -60
+R Y -30
+R W -14
+R V -14
+R T -14
+T y -60
+T w -60
+T u -60
+T ; -60
+T s -75
+T r -45
+T . -75
+T o -75
+T i 0
+T - -75
+T hy -75
+T char173 -75
+T e -75
+T , -75
+T : -60
+T c -75
+T a -75
+T O -14
+T A -60
+V y -14
+V u -14
+V ; -14
+V r -14
+V . -60
+V o -30
+V i -14
+V - -30
+V hy -30
+V char173 -30
+V e -30
+V , -60
+V : -14
+V a -30
+V A -60
+W . -30
+W i -7
+W - -14
+W hy -14
+W char173 -14
+W e -14
+W , -30
+W a -14
+W A -14
+Y v -30
+Y u -30
+Y ; -30
+Y q -45
+Y . -75
+Y p -45
+Y o -45
+Y i -14
+Y - -60
+Y hy -60
+Y char173 -60
+Y e -45
+Y , -75
+Y : -30
+Y a -60
+Y A -45
+f ' 30
+1 1 -60
+` ` -30
+` oq -30
+oq ` -30
+oq oq -30
+' s -14
+' ' -30
+r ' 30
+r . -30
+r - -14
+r hy -14
+r char173 -14
+r , -45
+v . -60
+v , -60
+w . -45
+w , -45
+y . -60
+y , -60
+charset
+ha 385,713,0,72,-44,72 2 0000 asciicircum
+ti 479,438,0,58,-63,58 0 0001 asciitilde
+vS 547,939,23,88,-23,88 2 0002 Scaron
+vZ 501,939,0,153,27,89 2 0003 Zcaron
+vs 410,740,24,89,1,89 2 0004 scaron
+vz 410,740,0,96,25,89 2 0005 zcaron
+:Y 547,907,0,172,-87,89 2 0006 Ydieresis
+tm 820,741,0,128,-120,89 2 0007 trademark
+space 228 0 0040
+! 228,729,0,120,-52,89 2 0041 exclam
+" 291,708,0,133,-96,89 2 0042 quotedbl
+# 456,698,20,126,5,89 2 0043 numbersign
+sh "
+$ 456,770,125,97,-7,89 3 0044 dollar
+Do "
+% 729,708,20,55,-60,55 2 0045 percent
+& 547,710,23,31,-18,31 2 0046 ampersand
+' 182,708,0,121,-86,89 2 0047 quoteright
+( 273,729,213,143,-43,89 3 0050 parenleft
+) 273,729,213,44,56,44 3 0051 parenright
+* 319,740,0,117,-89,89 2 0052 asterisk
++ 479,474,10,55,-25,55 0 0053 plus
+, 228,104,150,0,5 1 0054 comma
+- 273,313,0,64,-30,64 0 0055 hyphen
+hy "
+char173 "
+. 228,104,0,0,-21 0 0056 period
+/ 228,708,21,178,60,89 2 0057 slash
+sl "
+0 456,709,23,84,-30,84 2 0060 zero
+1 456,709,0,3,-121,3 2 0061 one
+2 456,710,0,102,22,89 2 0062 two
+3 456,709,23,85,-8,85 2 0063 three
+4 456,709,0,64,-2,64 2 0064 four
+5 456,709,23,110,-8,89 2 0065 five
+6 456,709,23,96,-26,89 2 0066 six
+7 456,709,0,144,-62,89 2 0067 seven
+8 456,709,23,89,-11,89 2 0070 eight
+9 456,709,23,85,-18,85 2 0071 nine
+: 228,525,0,89,-40,89 0 0072 colon
+; 228,516,150,88,-14,88 1 0073 semicolon
+< 479,474,10,92,-21,89 0 0074 less
+= 479,352,0,70,-11,70 0 0075 equal
+> 479,474,10,60,11,60 0 0076 greater
+? 456,738,0,110,-101,89 2 0077 question
+@ 832,737,146,68,-15,68 3 0100 at
+at "
+A 547,729,0,38,36,38 2 0101 A
+B 547,729,0,87,-15,87 2 0102 B
+C 592,741,23,89,-42,89 2 0103 C
+D 592,729,0,80,-23,80 2 0104 D
+E 547,729,0,119,-24,89 2 0105 E
+F 501,729,0,151,-24,89 2 0106 F
+G 638,741,23,75,-39,75 2 0107 G
+H 592,729,0,113,-18,89 2 0110 H
+I 228,729,0,108,-32,89 2 0111 I
+J 410,729,26,116,11,89 2 0112 J
+K 547,729,0,170,-15,89 2 0113 K
+L 456,729,0,46,-16,46 2 0114 L
+M 683,729,0,118,-11,89 2 0115 M
+N 592,729,0,115,-12,89 2 0116 N
+O 638,741,23,91,-35,89 2 0117 O
+P 547,730,0,104,-25,89 2 0120 P
+Q 638,741,59,91,-35,89 2 0121 Q
+R 592,729,0,90,-26,89 2 0122 R
+S 547,741,23,88,-23,88 2 0123 S
+T 501,729,0,162,-80,89 2 0124 T
+U 592,729,23,114,-52,89 2 0125 U
+V 547,729,0,159,-102,89 2 0126 V
+W 774,729,0,165,-95,89 2 0127 W
+X 547,729,0,154,32,89 2 0130 X
+Y 547,729,0,172,-87,89 2 0131 Y
+Z 501,729,0,153,27,89 2 0132 Z
+[ 228,729,214,154,35,89 3 0133 bracketleft
+lB "
+\ 228,729,20,51,-71,51 2 0134 backslash
+rs "
+] 228,729,215,121,68,89 3 0135 bracketright
+rB "
+a^ 273,741,0,131,-69,89 2 0136 circumflex
+^ "
+_ 456,0,175,46,99,46 1 0137 underscore
+` 182,708,0,121,-84,89 2 0140 quoteleft
+oq "
+a 456,540,23,60,-3,60 0 0141 a
+b 456,729,23,76,6,76 2 0142 b
+c 410,540,23,94,-12,89 0 0143 c
+d 456,729,23,127,-10,89 2 0144 d
+e 456,541,23,69,-19,69 0 0145 e
+f 228,733,0,161,-23,89 2 0146 f
+g 456,540,220,86,23,86 1 0147 g
+h 456,729,0,65,-7,65 2 0150 h
+i 182,729,0,118,-4,89 2 0151 i
+j 182,729,220,121,102,89 3 0152 j
+k 410,729,0,119,2,89 2 0153 k
+l 182,729,0,120,-6,89 2 0154 l
+m 683,540,0,65,-8,65 0 0155 m
+n 456,540,0,64,-7,64 0 0156 n
+o 456,540,23,66,-16,66 0 0157 o
+p 456,540,219,75,44,75 1 0160 p
+q 456,540,219,91,-8,89 1 0161 q
+r 273,540,0,135,-7,89 0 0162 r
+s 410,540,24,66,1,66 0 0163 s
+t 228,667,24,122,-30,89 2 0164 t
+u 456,525,23,80,-22,80 0 0165 u
+v 410,525,0,130,-49,89 0 0166 v
+w 592,525,0,131,-47,89 0 0167 w
+x 410,525,0,118,36,89 0 0170 x
+y 410,525,219,123,44,89 1 0171 y
+z 410,525,0,96,25,89 0 0172 z
+lC 274,731,214,129,-24,89 3 0173 braceleft
+{ "
+ba 213,729,215,95,6,89 3 0174 bar
+| "
+rC 274,731,214,42,64,42 3 0175 braceright
+} "
+a~ 273,716,0,164,-57,89 2 0176 tilde
+~ "
+--- 182,103,129,16,20,16 1 0200 quotesinglbase
+Fo 456,438,0,43,-70,43 0 0201 guillemotleft
+char171 "
+Fc 456,438,0,19,-49,19 0 0202 guillemotright
+char187 "
+bu 287,470,0,71,-49,71 0 0203 bullet
+--- 456,742,214,164,77,89 3 0204 florin
+f/ 137,708,21,312,196,89 2 0205 fraction
+%0 820,740,20,70,-26,70 2 0206 perthousand
+dg 456,710,178,103,-55,89 3 0207 dagger
+dd 456,710,178,102,8,89 3 0210 daggerdbl
+en 456,313,0,109,12,89 0 0211 endash
+em 820,313,0,105,16,89 0 0212 emdash
+fi 410,733,0,125,-18,89 2 0214 fi
+fl 410,733,0,120,-22,89 2 0215 fl
+.i 228,525,0,59,-27,59 0 0220 dotlessi
+ga 273,740,0,70,-97,70 2 0222 grave
+a" 273,740,0,191,-24,89 2 0223 hungarumlaut
+a. 273,709,0,80,-149,80 2 0224 dotaccent
+ab 273,729,0,163,-85,89 2 0225 breve
+ah 273,740,0,157,-94,89 2 0226 caron
+ao 273,741,0,101,-127,89 2 0227 ring
+ho 273,15,189,0,21 1 0230 ogonek
+lq 273,708,0,145,-70,89 2 0231 quotedblleft
+rq 273,708,0,148,-73,89 2 0232 quotedblright
+oe 774,540,22,68,-19,68 0 0233 oe
+/l 182,729,0,123,-1,89 2 0234 lslash
+--- 273,103,129,41,34,41 1 0235 quotedblbase
+OE 820,739,20,139,-32,89 2 0236 OE
+/L 456,729,0,61,-1,61 2 0237 Lslash
+r! 273,525,214,36,-12,36 1 0241 exclamdown
+char161 "
+ct 456,628,120,74,-29,74 3 0242 cent
+char162 "
+Po 456,726,21,109,14,89 2 0243 sterling
+char163 "
+Cs 456,554,0,80,-40,80 0 0244 currency
+char164 "
+Ye 456,710,0,165,-32,89 2 0245 yen
+char165 "
+sc 456,729,215,77,-1,77 3 0247 section
+char167 "
+ad 273,708,0,144,-80,89 2 0250 dieresis
+char168 "
+co 604,741,23,132,5,89 2 0251 copyright
+char169 "
+Of 303,740,0,109,-38,89 2 0252 ordfeminine
+char170 "
+fo 273,436,0,53,-65,53 0 0253 guilsinglleft
+no 479,352,0,79,-31,79 0 0254 logicalnot
+char172 "
+\- 479,270,0,64,-17,64 0 0255 minus
+rg 604,741,23,132,5,89 2 0256 registered
+char174 "
+a- 273,694,0,146,-81,89 2 0257 macron
+char175 "
+aa 273,740,0,153,-129,89 2 0264 acute
+char180 "
+ps 440,729,178,165,-69,89 3 0266 paragraph
+char182 "
+char183 228,442,0,62,-86,62 0 0267 periodcentered
+ac 273,0,214,0,49 1 0270 cedilla
+char184 "
+Om 299,741,0,122,-44,89 2 0272 ordmasculine
+char186 "
+fc 273,436,0,29,-39,29 0 0273 guilsinglright
+r? 501,525,213,0,-20 1 0277 questiondown
+char191 "
+`A 547,939,0,38,36,38 2 0300 Agrave
+char192 "
+'A 547,939,0,51,36,51 2 0301 Aacute
+char193 "
+^A 547,940,0,38,36,38 2 0302 Acircumflex
+char194 "
+~A 547,915,0,62,36,62 2 0303 Atilde
+char195 "
+:A 547,907,0,42,36,42 2 0304 Adieresis
+char196 "
+oA 547,940,0,38,36,38 2 0305 Aring
+char197 "
+AE 820,729,0,121,41,89 2 0306 AE
+char198 "
+,C 592,741,214,89,-42,89 3 0307 Ccedilla
+char199 "
+`E 547,939,0,119,-24,89 2 0310 Egrave
+char200 "
+'E 547,939,0,119,-24,89 2 0311 Eacute
+char201 "
+^E 547,940,0,119,-24,89 2 0312 Ecircumflex
+char202 "
+:E 547,907,0,119,-24,89 2 0313 Edieresis
+char203 "
+`I 228,939,0,132,-32,89 2 0314 Igrave
+char204 "
+'I 228,939,0,215,-32,89 2 0315 Iacute
+char205 "
+^I 228,940,0,193,-32,89 2 0316 Icircumflex
+char206 "
+:I 228,907,0,206,-32,89 2 0317 Idieresis
+char207 "
+~N 592,915,0,115,-12,89 2 0321 Ntilde
+char209 "
+`O 638,939,23,91,-35,89 2 0322 Ograve
+char210 "
+'O 638,939,23,91,-35,89 2 0323 Oacute
+char211 "
+^O 638,940,23,91,-35,89 2 0324 Ocircumflex
+char212 "
+~O 638,915,23,91,-35,89 2 0325 Otilde
+char213 "
+:O 638,907,23,91,-35,89 2 0326 Odieresis
+char214 "
+/O 638,742,23,122,24,89 2 0330 Oslash
+char216 "
+`U 592,939,23,114,-52,89 2 0331 Ugrave
+char217 "
+'U 592,939,23,114,-52,89 2 0332 Uacute
+char218 "
+^U 592,940,23,114,-52,89 2 0333 Ucircumflex
+char219 "
+:U 592,907,23,114,-52,89 2 0334 Udieresis
+char220 "
+ss 501,729,20,86,-53,86 2 0337 germandbls
+char223 "
+`a 456,740,23,60,-3,60 2 0340 agrave
+char224 "
+'a 456,740,23,62,-3,62 2 0341 aacute
+char225 "
+^a 456,741,23,60,-3,60 2 0342 acircumflex
+char226 "
+~a 456,716,23,73,-3,73 2 0343 atilde
+char227 "
+:a 456,708,23,60,-3,60 2 0344 adieresis
+char228 "
+oa 456,741,23,60,-3,60 2 0345 aring
+char229 "
+ae 729,546,20,71,2,71 0 0346 ae
+char230 "
+,c 410,540,214,94,-12,89 1 0347 ccedilla
+char231 "
+`e 456,740,23,69,-19,69 2 0350 egrave
+char232 "
+'e 456,740,23,69,-19,69 2 0351 eacute
+char233 "
+^e 456,741,23,69,-19,69 2 0352 ecircumflex
+char234 "
+:e 456,708,23,69,-19,69 2 0353 edieresis
+char235 "
+`i 228,740,0,93,-27,89 2 0354 igrave
+char236 "
+'i 228,740,0,176,-27,89 2 0355 iacute
+char237 "
+^i 228,741,0,154,-27,89 2 0356 icircumflex
+char238 "
+:i 228,708,0,167,-27,89 2 0357 idieresis
+char239 "
+~n 456,716,0,78,-7,78 2 0361 ntilde
+char241 "
+`o 456,740,23,66,-16,66 2 0362 ograve
+char242 "
+'o 456,740,23,66,-16,66 2 0363 oacute
+char243 "
+^o 456,741,23,66,-16,66 2 0364 ocircumflex
+char244 "
+~o 456,716,23,73,-16,73 2 0365 otilde
+char245 "
+:o 456,708,23,66,-16,66 2 0366 odieresis
+char246 "
+/o 501,548,27,73,35,73 0 0370 oslash
+char248 "
+`u 456,740,23,80,-22,80 2 0371 ugrave
+char249 "
+'u 456,740,23,80,-22,80 2 0372 uacute
+char250 "
+^u 456,741,23,80,-22,80 2 0373 ucircumflex
+char251 "
+:u 456,708,23,80,-22,80 2 0374 udieresis
+char252 "
+:y 410,708,219,123,44,89 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HNR b/ps/devps/HNR
new file mode 100644
index 000000000..1b42a3bad
--- /dev/null
+++ b/ps/devps/HNR
@@ -0,0 +1,412 @@
+name HNR
+internalname Helvetica-Narrow
+spacewidth 228
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -14
+A w -14
+A v -14
+A ' -60
+A Y -60
+A W -30
+A V -60
+A T -60
+F . -91
+F , -91
+F A -45
+L y -30
+L ' -45
+L Y -60
+L W -60
+L V -60
+L T -60
+P . -105
+P , -105
+P A -60
+R Y -14
+R W -14
+R V -14
+R T -14
+T y -45
+T w -45
+T u -30
+T ; -91
+T s -70
+T r -30
+T . -91
+T o -91
+T i -18
+T - -45
+T hy -45
+T char173 -45
+T e -91
+T , -91
+T : -91
+T c -91
+T a -91
+T O -14
+T A -60
+V y -30
+V u -30
+V ; -30
+V r -30
+V . -75
+V o -45
+V i -14
+V - -45
+V hy -45
+V char173 -45
+V e -45
+V , -75
+V : -30
+V a -60
+V A -60
+W y -7
+W u -14
+W ; -14
+W r -14
+W . -45
+W o -14
+W i 0
+W - -14
+W hy -14
+W char173 -14
+W e -14
+W , -45
+W : -14
+W a -30
+W A -30
+Y v -45
+Y u -45
+Y ; -53
+Y q -75
+Y . -105
+Y p -60
+Y o -75
+Y i -30
+Y - -75
+Y hy -75
+Y char173 -75
+Y e -75
+Y , -105
+Y : -45
+Y a -60
+Y A -60
+f ' 14
+f f -14
+1 1 -60
+` ` -14
+` oq -14
+oq ` -14
+oq oq -14
+' s -14
+' ' -14
+r ' 30
+r . -45
+r , -45
+v . -60
+v , -60
+w . -45
+w , -45
+y . -60
+y , -60
+charset
+ha 385,713 2 0000 asciicircum
+ti 479,438 0 0001 asciitilde
+vS 547,939,23 2 0002 Scaron
+vZ 501,939 2 0003 Zcaron
+vs 410,740,24 2 0004 scaron
+vz 410,740 2 0005 zcaron
+:Y 547,907 2 0006 Ydieresis
+tm 820,741 2 0007 trademark
+space 228 0 0040
+! 228,729 2 0041 exclam
+" 291,708 2 0042 quotedbl
+# 456,698,20 2 0043 numbersign
+sh "
+$ 456,770,125 3 0044 dollar
+Do "
+% 729,708,20 2 0045 percent
+& 547,710,23 2 0046 ampersand
+' 182,708 2 0047 quoteright
+( 273,729,213 3 0050 parenleft
+) 273,729,213 3 0051 parenright
+* 319,740 2 0052 asterisk
++ 479,474,10 0 0053 plus
+, 228,104,150 1 0054 comma
+- 273,313 0 0055 hyphen
+hy "
+char173 "
+. 228,104 0 0056 period
+/ 228,708,21 2 0057 slash
+sl "
+0 456,709,23 2 0060 zero
+1 456,709 2 0061 one
+2 456,710 2 0062 two
+3 456,709,23 2 0063 three
+4 456,709 2 0064 four
+5 456,709,23 2 0065 five
+6 456,709,23 2 0066 six
+7 456,709 2 0067 seven
+8 456,709,23 2 0070 eight
+9 456,709,23 2 0071 nine
+: 228,525 0 0072 colon
+; 228,516,150 1 0073 semicolon
+< 479,474,10 0 0074 less
+= 479,352 0 0075 equal
+> 479,474,10 0 0076 greater
+? 456,738 2 0077 question
+@ 832,737,146 3 0100 at
+at "
+A 547,729 2 0101 A
+B 547,729 2 0102 B
+C 592,741,23 2 0103 C
+D 592,729 2 0104 D
+E 547,729 2 0105 E
+F 501,729 2 0106 F
+G 638,741,23 2 0107 G
+H 592,729 2 0110 H
+I 228,729 2 0111 I
+J 410,729,26 2 0112 J
+K 547,729 2 0113 K
+L 456,729 2 0114 L
+M 683,729 2 0115 M
+N 592,729 2 0116 N
+O 638,741,23 2 0117 O
+P 547,730 2 0120 P
+Q 638,741,59 2 0121 Q
+R 592,729 2 0122 R
+S 547,741,23 2 0123 S
+T 501,729 2 0124 T
+U 592,729,23 2 0125 U
+V 547,729 2 0126 V
+W 774,729 2 0127 W
+X 547,729 2 0130 X
+Y 547,729 2 0131 Y
+Z 501,729 2 0132 Z
+[ 228,729,214 3 0133 bracketleft
+lB "
+\ 228,729,20 2 0134 backslash
+rs "
+] 228,729,215 3 0135 bracketright
+rB "
+a^ 273,741 2 0136 circumflex
+^ "
+_ 456,0,175 1 0137 underscore
+` 182,708 2 0140 quoteleft
+oq "
+a 456,540,23 0 0141 a
+b 456,729,23 2 0142 b
+c 410,540,23 0 0143 c
+d 456,729,23 2 0144 d
+e 456,541,23 0 0145 e
+f 228,733 2 0146 f
+g 456,540,220 1 0147 g
+h 456,729 2 0150 h
+i 182,729 2 0151 i
+j 182,729,220 3 0152 j
+k 410,729 2 0153 k
+l 182,729 2 0154 l
+m 683,540 0 0155 m
+n 456,540 0 0156 n
+o 456,540,23 0 0157 o
+p 456,540,219 1 0160 p
+q 456,540,219 1 0161 q
+r 273,540 0 0162 r
+s 410,540,24 0 0163 s
+t 228,667,24 2 0164 t
+u 456,525,23 0 0165 u
+v 410,525 0 0166 v
+w 592,525 0 0167 w
+x 410,525 0 0170 x
+y 410,525,219 1 0171 y
+z 410,525 0 0172 z
+lC 274,731,214 3 0173 braceleft
+{ "
+ba 213,729,215 3 0174 bar
+| "
+rC 274,731,214 3 0175 braceright
+} "
+a~ 273,716 2 0176 tilde
+~ "
+--- 182,103,129 1 0200 quotesinglbase
+Fo 456,438 0 0201 guillemotleft
+char171 "
+Fc 456,438 0 0202 guillemotright
+char187 "
+bu 287,470 0 0203 bullet
+--- 456,742,214 3 0204 florin
+f/ 137,708,21 2 0205 fraction
+%0 820,740,20 2 0206 perthousand
+dg 456,710,178 3 0207 dagger
+dd 456,710,178 3 0210 daggerdbl
+en 456,313 0 0211 endash
+em 820,313 0 0212 emdash
+fi 410,733 2 0214 fi
+fl 410,733 2 0215 fl
+.i 228,525 0 0220 dotlessi
+ga 273,740 2 0222 grave
+a" 273,740 2 0223 hungarumlaut
+a. 273,709 2 0224 dotaccent
+ab 273,729 2 0225 breve
+ah 273,740 2 0226 caron
+ao 273,741 2 0227 ring
+ho 273,15,189 1 0230 ogonek
+lq 273,708 2 0231 quotedblleft
+rq 273,708 2 0232 quotedblright
+oe 774,540,22 0 0233 oe
+/l 182,729 2 0234 lslash
+--- 273,103,129 1 0235 quotedblbase
+OE 820,739,20 2 0236 OE
+/L 456,729 2 0237 Lslash
+r! 273,525,214 1 0241 exclamdown
+char161 "
+ct 456,628,120 3 0242 cent
+char162 "
+Po 456,726,21 2 0243 sterling
+char163 "
+Cs 456,554 0 0244 currency
+char164 "
+Ye 456,710 2 0245 yen
+char165 "
+sc 456,729,215 3 0247 section
+char167 "
+ad 273,708 2 0250 dieresis
+char168 "
+co 604,741,23 2 0251 copyright
+char169 "
+Of 303,740 2 0252 ordfeminine
+char170 "
+fo 273,436 0 0253 guilsinglleft
+no 479,352 0 0254 logicalnot
+char172 "
+\- 479,270 0 0255 minus
+rg 604,741,23 2 0256 registered
+char174 "
+a- 273,694 2 0257 macron
+char175 "
+aa 273,740 2 0264 acute
+char180 "
+ps 440,729,178 3 0266 paragraph
+char182 "
+char183 228,442 0 0267 periodcentered
+ac 273,0,214 1 0270 cedilla
+char184 "
+Om 299,741 2 0272 ordmasculine
+char186 "
+fc 273,436 0 0273 guilsinglright
+r? 501,525,213 1 0277 questiondown
+char191 "
+`A 547,939 2 0300 Agrave
+char192 "
+'A 547,939 2 0301 Aacute
+char193 "
+^A 547,940 2 0302 Acircumflex
+char194 "
+~A 547,915 2 0303 Atilde
+char195 "
+:A 547,907 2 0304 Adieresis
+char196 "
+oA 547,940 2 0305 Aring
+char197 "
+AE 820,729 2 0306 AE
+char198 "
+,C 592,741,214 3 0307 Ccedilla
+char199 "
+`E 547,939 2 0310 Egrave
+char200 "
+'E 547,939 2 0311 Eacute
+char201 "
+^E 547,940 2 0312 Ecircumflex
+char202 "
+:E 547,907 2 0313 Edieresis
+char203 "
+`I 228,939 2 0314 Igrave
+char204 "
+'I 228,939 2 0315 Iacute
+char205 "
+^I 228,940 2 0316 Icircumflex
+char206 "
+:I 228,907 2 0317 Idieresis
+char207 "
+~N 592,915 2 0321 Ntilde
+char209 "
+`O 638,939,23 2 0322 Ograve
+char210 "
+'O 638,939,23 2 0323 Oacute
+char211 "
+^O 638,940,23 2 0324 Ocircumflex
+char212 "
+~O 638,915,23 2 0325 Otilde
+char213 "
+:O 638,907,23 2 0326 Odieresis
+char214 "
+/O 638,742,23 2 0330 Oslash
+char216 "
+`U 592,939,23 2 0331 Ugrave
+char217 "
+'U 592,939,23 2 0332 Uacute
+char218 "
+^U 592,940,23 2 0333 Ucircumflex
+char219 "
+:U 592,907,23 2 0334 Udieresis
+char220 "
+ss 501,729,20 2 0337 germandbls
+char223 "
+`a 456,740,23 2 0340 agrave
+char224 "
+'a 456,740,23 2 0341 aacute
+char225 "
+^a 456,741,23 2 0342 acircumflex
+char226 "
+~a 456,716,23 2 0343 atilde
+char227 "
+:a 456,708,23 2 0344 adieresis
+char228 "
+oa 456,741,23 2 0345 aring
+char229 "
+ae 729,546,20 0 0346 ae
+char230 "
+,c 410,540,214 1 0347 ccedilla
+char231 "
+`e 456,740,23 2 0350 egrave
+char232 "
+'e 456,740,23 2 0351 eacute
+char233 "
+^e 456,741,23 2 0352 ecircumflex
+char234 "
+:e 456,708,23 2 0353 edieresis
+char235 "
+`i 228,740 2 0354 igrave
+char236 "
+'i 228,740 2 0355 iacute
+char237 "
+^i 228,741 2 0356 icircumflex
+char238 "
+:i 228,708 2 0357 idieresis
+char239 "
+~n 456,716 2 0361 ntilde
+char241 "
+`o 456,740,23 2 0362 ograve
+char242 "
+'o 456,740,23 2 0363 oacute
+char243 "
+^o 456,741,23 2 0364 ocircumflex
+char244 "
+~o 456,716,23 2 0365 otilde
+char245 "
+:o 456,708,23 2 0366 odieresis
+char246 "
+/o 501,548,27 0 0370 oslash
+char248 "
+`u 456,740,23 2 0371 ugrave
+char249 "
+'u 456,740,23 2 0372 uacute
+char250 "
+^u 456,741,23 2 0373 ucircumflex
+char251 "
+:u 456,708,23 2 0374 udieresis
+char252 "
+:y 410,708,219 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/HR b/ps/devps/HR
new file mode 100644
index 000000000..749ed1746
--- /dev/null
+++ b/ps/devps/HR
@@ -0,0 +1,412 @@
+name HR
+internalname Helvetica
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -18
+A w -18
+A v -18
+A ' -74
+A Y -74
+A W -37
+A V -74
+A T -74
+F . -111
+F , -111
+F A -55
+L y -37
+L ' -55
+L Y -74
+L W -74
+L V -74
+L T -74
+P . -129
+P , -129
+P A -74
+R Y -18
+R W -18
+R V -18
+R T -18
+T y -55
+T w -55
+T u -37
+T ; -111
+T s -111
+T r -37
+T . -111
+T o -111
+T i -37
+T - -55
+T hy -55
+T char173 -55
+T e -111
+T , -111
+T : -111
+T c -111
+T a -111
+T O -18
+T A -74
+V y -37
+V u -37
+V ; -37
+V r -37
+V . -92
+V o -55
+V i -18
+V - -55
+V hy -55
+V char173 -55
+V e -55
+V , -92
+V : -37
+V a -74
+V A -74
+W y -9
+W u -18
+W ; -18
+W r -18
+W . -55
+W o -18
+W i 0
+W - -18
+W hy -18
+W char173 -18
+W e -18
+W , -55
+W : -18
+W a -37
+W A -37
+Y v -55
+Y u -55
+Y ; -65
+Y q -92
+Y . -129
+Y p -74
+Y o -92
+Y i -37
+Y - -92
+Y hy -92
+Y char173 -92
+Y e -92
+Y , -129
+Y : -55
+Y a -74
+Y A -74
+f ' 18
+f f -18
+1 1 -74
+` ` -18
+` oq -18
+oq ` -18
+oq oq -18
+' s -18
+' ' -18
+r ' 37
+r . -55
+r , -55
+v . -74
+v , -74
+w . -55
+w , -55
+y . -74
+y , -74
+charset
+ha 469,713 2 0000 asciicircum
+ti 584,438 0 0001 asciitilde
+vS 667,939,23 2 0002 Scaron
+vZ 611,939 2 0003 Zcaron
+vs 500,740,24 2 0004 scaron
+vz 500,740 2 0005 zcaron
+:Y 667,907 2 0006 Ydieresis
+tm 1000,741 2 0007 trademark
+space 278 0 0040
+! 278,729 2 0041 exclam
+" 355,708 2 0042 quotedbl
+# 556,698,20 2 0043 numbersign
+sh "
+$ 556,770,125 3 0044 dollar
+Do "
+% 889,708,20 2 0045 percent
+& 667,710,23 2 0046 ampersand
+' 222,708 2 0047 quoteright
+( 333,729,213 3 0050 parenleft
+) 333,729,213 3 0051 parenright
+* 389,740 2 0052 asterisk
++ 584,474,10 0 0053 plus
+, 278,104,150 1 0054 comma
+- 333,313 0 0055 hyphen
+hy "
+char173 "
+. 278,104 0 0056 period
+/ 278,708,21 2 0057 slash
+sl "
+0 556,709,23 2 0060 zero
+1 556,709 2 0061 one
+2 556,710 2 0062 two
+3 556,709,23 2 0063 three
+4 556,709 2 0064 four
+5 556,709,23 2 0065 five
+6 556,709,23 2 0066 six
+7 556,709 2 0067 seven
+8 556,709,23 2 0070 eight
+9 556,709,23 2 0071 nine
+: 278,525 0 0072 colon
+; 278,516,150 1 0073 semicolon
+< 584,474,10 0 0074 less
+= 584,352 0 0075 equal
+> 584,474,10 0 0076 greater
+? 556,738 2 0077 question
+@ 1015,737,146 3 0100 at
+at "
+A 667,729 2 0101 A
+B 667,729 2 0102 B
+C 722,741,23 2 0103 C
+D 722,729 2 0104 D
+E 667,729 2 0105 E
+F 611,729 2 0106 F
+G 778,741,23 2 0107 G
+H 722,729 2 0110 H
+I 278,729 2 0111 I
+J 500,729,26 2 0112 J
+K 667,729 2 0113 K
+L 556,729 2 0114 L
+M 833,729 2 0115 M
+N 722,729 2 0116 N
+O 778,741,23 2 0117 O
+P 667,730 2 0120 P
+Q 778,741,59 2 0121 Q
+R 722,729 2 0122 R
+S 667,741,23 2 0123 S
+T 611,729 2 0124 T
+U 722,729,23 2 0125 U
+V 667,729 2 0126 V
+W 944,729 2 0127 W
+X 667,729 2 0130 X
+Y 667,729 2 0131 Y
+Z 611,729 2 0132 Z
+[ 278,729,214 3 0133 bracketleft
+lB "
+\ 278,729,20 2 0134 backslash
+rs "
+] 278,729,215 3 0135 bracketright
+rB "
+a^ 333,741 2 0136 circumflex
+^ "
+_ 556,0,175 1 0137 underscore
+` 222,708 2 0140 quoteleft
+oq "
+a 556,540,23 0 0141 a
+b 556,729,23 2 0142 b
+c 500,540,23 0 0143 c
+d 556,729,23 2 0144 d
+e 556,541,23 0 0145 e
+f 278,733 2 0146 f
+g 556,540,220 1 0147 g
+h 556,729 2 0150 h
+i 222,729 2 0151 i
+j 222,729,220 3 0152 j
+k 500,729 2 0153 k
+l 222,729 2 0154 l
+m 833,540 0 0155 m
+n 556,540 0 0156 n
+o 556,540,23 0 0157 o
+p 556,540,219 1 0160 p
+q 556,540,219 1 0161 q
+r 333,540 0 0162 r
+s 500,540,24 0 0163 s
+t 278,667,24 2 0164 t
+u 556,525,23 0 0165 u
+v 500,525 0 0166 v
+w 722,525 0 0167 w
+x 500,525 0 0170 x
+y 500,525,219 1 0171 y
+z 500,525 0 0172 z
+lC 334,731,214 3 0173 braceleft
+{ "
+ba 260,729,215 3 0174 bar
+| "
+rC 334,731,214 3 0175 braceright
+} "
+a~ 333,716 2 0176 tilde
+~ "
+--- 222,103,129 1 0200 quotesinglbase
+Fo 556,438 0 0201 guillemotleft
+char171 "
+Fc 556,438 0 0202 guillemotright
+char187 "
+bu 350,470 0 0203 bullet
+--- 556,742,214 3 0204 florin
+f/ 167,708,21 2 0205 fraction
+%0 1000,740,20 2 0206 perthousand
+dg 556,710,178 3 0207 dagger
+dd 556,710,178 3 0210 daggerdbl
+en 556,313 0 0211 endash
+em 1000,313 0 0212 emdash
+fi 500,733 2 0214 fi
+fl 500,733 2 0215 fl
+.i 278,525 0 0220 dotlessi
+ga 333,740 2 0222 grave
+a" 333,740 2 0223 hungarumlaut
+a. 333,709 2 0224 dotaccent
+ab 333,729 2 0225 breve
+ah 333,740 2 0226 caron
+ao 333,741 2 0227 ring
+ho 333,15,189 1 0230 ogonek
+lq 333,708 2 0231 quotedblleft
+rq 333,708 2 0232 quotedblright
+oe 944,540,22 0 0233 oe
+/l 222,729 2 0234 lslash
+--- 333,103,129 1 0235 quotedblbase
+OE 1000,739,20 2 0236 OE
+/L 556,729 2 0237 Lslash
+r! 333,525,214 1 0241 exclamdown
+char161 "
+ct 556,628,120 3 0242 cent
+char162 "
+Po 556,726,21 2 0243 sterling
+char163 "
+Cs 556,554 0 0244 currency
+char164 "
+Ye 556,710 2 0245 yen
+char165 "
+sc 556,729,215 3 0247 section
+char167 "
+ad 333,708 2 0250 dieresis
+char168 "
+co 737,741,23 2 0251 copyright
+char169 "
+Of 370,740 2 0252 ordfeminine
+char170 "
+fo 333,436 0 0253 guilsinglleft
+no 584,352 0 0254 logicalnot
+char172 "
+\- 584,270 0 0255 minus
+rg 737,741,23 2 0256 registered
+char174 "
+a- 333,694 2 0257 macron
+char175 "
+aa 333,740 2 0264 acute
+char180 "
+ps 537,729,178 3 0266 paragraph
+char182 "
+char183 278,442 0 0267 periodcentered
+ac 333,0,214 1 0270 cedilla
+char184 "
+Om 365,741 2 0272 ordmasculine
+char186 "
+fc 333,436 0 0273 guilsinglright
+r? 611,525,213 1 0277 questiondown
+char191 "
+`A 667,939 2 0300 Agrave
+char192 "
+'A 667,939 2 0301 Aacute
+char193 "
+^A 667,940 2 0302 Acircumflex
+char194 "
+~A 667,915 2 0303 Atilde
+char195 "
+:A 667,907 2 0304 Adieresis
+char196 "
+oA 667,940 2 0305 Aring
+char197 "
+AE 1000,729 2 0306 AE
+char198 "
+,C 722,741,214 3 0307 Ccedilla
+char199 "
+`E 667,939 2 0310 Egrave
+char200 "
+'E 667,939 2 0311 Eacute
+char201 "
+^E 667,940 2 0312 Ecircumflex
+char202 "
+:E 667,907 2 0313 Edieresis
+char203 "
+`I 278,939 2 0314 Igrave
+char204 "
+'I 278,939 2 0315 Iacute
+char205 "
+^I 278,940 2 0316 Icircumflex
+char206 "
+:I 278,907 2 0317 Idieresis
+char207 "
+~N 722,915 2 0321 Ntilde
+char209 "
+`O 778,939,23 2 0322 Ograve
+char210 "
+'O 778,939,23 2 0323 Oacute
+char211 "
+^O 778,940,23 2 0324 Ocircumflex
+char212 "
+~O 778,915,23 2 0325 Otilde
+char213 "
+:O 778,907,23 2 0326 Odieresis
+char214 "
+/O 778,742,23 2 0330 Oslash
+char216 "
+`U 722,939,23 2 0331 Ugrave
+char217 "
+'U 722,939,23 2 0332 Uacute
+char218 "
+^U 722,940,23 2 0333 Ucircumflex
+char219 "
+:U 722,907,23 2 0334 Udieresis
+char220 "
+ss 611,729,20 2 0337 germandbls
+char223 "
+`a 556,740,23 2 0340 agrave
+char224 "
+'a 556,740,23 2 0341 aacute
+char225 "
+^a 556,741,23 2 0342 acircumflex
+char226 "
+~a 556,716,23 2 0343 atilde
+char227 "
+:a 556,708,23 2 0344 adieresis
+char228 "
+oa 556,741,23 2 0345 aring
+char229 "
+ae 889,546,20 0 0346 ae
+char230 "
+,c 500,540,214 1 0347 ccedilla
+char231 "
+`e 556,740,23 2 0350 egrave
+char232 "
+'e 556,740,23 2 0351 eacute
+char233 "
+^e 556,741,23 2 0352 ecircumflex
+char234 "
+:e 556,708,23 2 0353 edieresis
+char235 "
+`i 278,740 2 0354 igrave
+char236 "
+'i 278,740 2 0355 iacute
+char237 "
+^i 278,741 2 0356 icircumflex
+char238 "
+:i 278,708 2 0357 idieresis
+char239 "
+~n 556,716 2 0361 ntilde
+char241 "
+`o 556,740,23 2 0362 ograve
+char242 "
+'o 556,740,23 2 0363 oacute
+char243 "
+^o 556,741,23 2 0364 ocircumflex
+char244 "
+~o 556,716,23 2 0365 otilde
+char245 "
+:o 556,708,23 2 0366 odieresis
+char246 "
+/o 611,548,27 0 0370 oslash
+char248 "
+`u 556,740,23 2 0371 ugrave
+char249 "
+'u 556,740,23 2 0372 uacute
+char250 "
+^u 556,741,23 2 0373 ucircumflex
+char251 "
+:u 556,708,23 2 0374 udieresis
+char252 "
+:y 500,708,219 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/Makefile b/ps/devps/Makefile
new file mode 100644
index 000000000..ce184e1a4
--- /dev/null
+++ b/ps/devps/Makefile
@@ -0,0 +1,66 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#PAGE=letter
+PAGE=A4
+BINDIR=/usr/local/bin
+FONTDIR=/usr/local/lib/groff/font
+DEVICEDIR=$(FONTDIR)/devps
+FONTS = AB ABI AI AR \
+ BMB BMBI BMI BMR \
+ CB CBI CI CR \
+ HB HBI HI HR \
+ HNB HNBI HNI HNR \
+ NB NBI NI NR \
+ PB PBI PI PR \
+ TB TBI TI TR \
+ ZCMI S SS ZD ZDR
+
+DOWNLOAD=symbolsl.ps zapfdr.ps
+
+DEVICEFILES=$(FONTS) $(DOWNLOAD) text.enc prologue eqnchar download textmap
+
+all: $(DEVICEFILES)
+
+install.nobin: $(DEVICEFILES)
+ -[ -d $(FONTDIR) ] || mkdir $(FONTDIR)
+ -[ -d $(DEVICEDIR) ] || mkdir $(DEVICEDIR)
+ -cd $(DEVICEDIR); rm -f $(DEVICEFILES) DESC
+ cp $(DEVICEFILES) $(DEVICEDIR)
+ cp DESC-$(PAGE) $(DEVICEDIR)/DESC
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/afmtodit
+ cp afmtodit $(BINDIR)
+ chmod +x $(BINDIR)/afmtodit
+
+install.bin:
+
+install: install.bin install.nobin
+
+clean:
+
+realclean: clean
+
+distclean: clean
+
+fonts: FORCE DESC
+ $(MAKE) -f FontMakefile
+TAGS:
+
+FORCE:
diff --git a/ps/devps/NB b/ps/devps/NB
new file mode 100644
index 000000000..5347d8cdd
--- /dev/null
+++ b/ps/devps/NB
@@ -0,0 +1,412 @@
+name NB
+internalname NewCenturySchlbk-Bold
+spacewidth 287
+encoding text.enc
+kernpairs
+A y -74
+A w -74
+A v -74
+A ' -74
+A Y -92
+A W -74
+A V -92
+A T -55
+F . -111
+F , -111
+F A -74
+L y -55
+L ' -55
+L Y -92
+L W -74
+L V -92
+L T -55
+P . -129
+P , -129
+P A -74
+R y -18
+R Y -37
+R W -37
+R V -37
+T y -52
+T w -71
+T u -71
+T ; -55
+T s -62
+T r -62
+T . -92
+T o -92
+T i -25
+T - -92
+T hy -92
+T char173 -92
+T e -92
+T , -92
+T : -55
+T c -81
+T a -62
+T A -55
+V y -92
+V u -74
+V ; -74
+V r -74
+V . -129
+V o -92
+V i -44
+V - -92
+V hy -92
+V char173 -92
+V e -92
+V : -74
+V a -92
+V A -92
+W y -74
+W u -55
+W ; -37
+W r -55
+W . -111
+W o -55
+W i -37
+W - -37
+W hy -37
+W char173 -37
+W e -55
+W , -111
+W : -37
+W a -74
+W A -74
+Y v -81
+Y u -92
+Y ; -92
+Y q -111
+Y . -111
+Y p -81
+Y o -111
+Y i -44
+Y - -111
+Y hy -111
+Y char173 -111
+Y e -111
+Y , -111
+Y : -92
+Y a -111
+Y A -92
+f ' 94
+1 1 -55
+` ` -18
+` oq -18
+oq ` -18
+oq oq -18
+' t -18
+' s -37
+' ' -18
+r ' 55
+r . -74
+r - -18
+r hy -18
+r char173 -18
+r , -74
+v . -111
+v , -111
+w . -92
+w , -92
+y . -111
+y , -111
+charset
+ha 606,722 2 0000 asciicircum
+ti 606,347 0 0001 asciitilde
+vS 667,969,15 2 0002 Scaron
+vZ 667,969 2 0003 Zcaron
+vs 500,722,15 2 0004 scaron
+vz 537,722 2 0005 zcaron
+:Y 722,940 2 0006 Ydieresis
+tm 1000,722 2 0007 trademark
+space 287 0 0040
+! 296,737,15 2 0041 exclam
+" 333,737 2 0042 quotedbl
+# 574,705 2 0043 numbersign
+sh "
+$ 574,801,140 3 0044 dollar
+Do "
+% 833,704,17 2 0045 percent
+& 852,737,15 2 0046 ampersand
+' 241,737 2 0047 quoteright
+( 389,737,122 3 0050 parenleft
+) 389,737,122 3 0051 parenright
+* 500,738 2 0052 asterisk
++ 606,514 0 0053 plus
+, 278,169,189 1 0054 comma
+- 333,309 0 0055 hyphen
+hy "
+char173 "
+. 278,172,15 0 0056 period
+/ 278,737,15 2 0057 slash
+sl "
+0 574,705,15 2 0060 zero
+1 574,705 2 0061 one
+2 574,705,4 2 0062 two
+3 574,705,15 2 0063 three
+4 574,705 2 0064 four
+5 574,705,15 2 0065 five
+6 574,705,15 2 0066 six
+7 574,705,15 2 0067 seven
+8 574,705,15 2 0070 eight
+9 574,705,15 2 0071 nine
+: 278,485,15 0 0072 colon
+; 278,485,189 1 0073 semicolon
+< 606,538,13 2 0074 less
+= 606,399 0 0075 equal
+> 606,538,13 2 0076 greater
+? 500,737,15 2 0077 question
+@ 747,737,15 2 0100 at
+at "
+A 759,737 2 0101 A
+B 778,722 2 0102 B
+C 778,737,15 2 0103 C
+D 833,722 2 0104 D
+E 759,722 2 0105 E
+F 722,722 2 0106 F
+G 833,737,15 2 0107 G
+H 870,722 2 0110 H
+I 444,722 2 0111 I
+J 648,722,15 2 0112 J
+K 815,722 2 0113 K
+L 722,722 2 0114 L
+M 981,722 2 0115 M
+N 833,722,8 2 0116 N
+O 833,737,15 2 0117 O
+P 759,722 2 0120 P
+Q 833,737,189 3 0121 Q
+R 815,722,15 2 0122 R
+S 667,737,15 2 0123 S
+T 722,722 2 0124 T
+U 833,722,15 2 0125 U
+V 759,722,15 2 0126 V
+W 981,722,15 2 0127 W
+X 722,722 2 0130 X
+Y 722,722 2 0131 Y
+Z 667,722 2 0132 Z
+[ 389,722,113 3 0133 bracketleft
+lB "
+\ 606,737 2 0134 backslash
+rs "
+] 389,722,113 3 0135 bracketright
+rB "
+a^ 333,722 2 0136 circumflex
+^ "
+_ 500,0,148 1 0137 underscore
+` 241,747 2 0140 quoteleft
+oq "
+a 611,485,15 0 0141 a
+b 648,737,15 2 0142 b
+c 556,485,15 0 0143 c
+d 667,737,15 2 0144 d
+e 574,485,15 0 0145 e
+f 389,737 2 0146 f
+g 611,536,205 3 0147 g
+h 685,737 2 0150 h
+i 370,737 2 0151 i
+j 352,736,205 3 0152 j
+k 667,737 2 0153 k
+l 352,737 2 0154 l
+m 963,485 0 0155 m
+n 685,485 0 0156 n
+o 611,485,15 0 0157 o
+p 667,485,205 1 0160 p
+q 648,485,205 1 0161 q
+r 519,485 0 0162 r
+s 500,485,15 0 0163 s
+t 426,675,15 2 0164 t
+u 685,475,15 0 0165 u
+v 611,475,8 0 0166 v
+w 889,475,8 0 0167 w
+x 611,475 0 0170 x
+y 611,475,208 1 0171 y
+z 537,475 0 0172 z
+lC 389,723,111 3 0173 braceleft
+{ "
+ba 606,737 2 0174 bar
+| "
+rC 389,723,111 3 0175 braceright
+} "
+a~ 333,704 2 0176 tilde
+~ "
+--- 241,169,189 1 0200 quotesinglbase
+Fo 500,405 0 0201 guillemotleft
+char171 "
+Fc 500,405 0 0202 guillemotright
+char187 "
+bu 606,511 0 0203 bullet
+--- 574,737,205 3 0204 florin
+f/ 167,705 2 0205 fraction
+%0 1000,694,27 2 0206 perthousand
+dg 500,737,88 3 0207 dagger
+dd 500,735,89 3 0210 daggerdbl
+en 500,296 0 0211 endash
+em 1000,296 0 0212 emdash
+fi 685,737 2 0214 fi
+fl 685,737 2 0215 fl
+.i 370,475 0 0220 dotlessi
+ga 333,734 2 0222 grave
+a" 333,737 2 0223 hungarumlaut
+a. 333,693 2 0224 dotaccent
+ab 333,712 2 0225 breve
+ah 333,722 2 0226 caron
+ao 333,760 2 0227 ring
+ho 333,0,163 1 0230 ogonek
+lq 481,747 2 0231 quotedblleft
+rq 481,737 2 0232 quotedblright
+oe 907,485,15 0 0233 oe
+/l 352,737 2 0234 lslash
+--- 481,169,189 1 0235 quotedblbase
+OE 1000,722 2 0236 OE
+/L 722,722 2 0237 Lslash
+r! 296,547,205 3 0241 exclamdown
+char161 "
+ct 574,566,108 3 0242 cent
+char162 "
+Po 574,705,15 2 0243 sterling
+char163 "
+Cs 606,591 2 0244 currency
+char164 "
+Ye 722,705 2 0245 yen
+char165 "
+sc 500,737,86 3 0247 section
+char167 "
+ad 333,693 2 0250 dieresis
+char168 "
+co 747,737,15 2 0251 copyright
+char169 "
+Of 367,737 2 0252 ordfeminine
+char170 "
+fo 333,405 0 0253 guilsinglleft
+no 606,399 0 0254 logicalnot
+char172 "
+\- 606,302 0 0255 minus
+rg 747,737,15 2 0256 registered
+char174 "
+a- 333,663 2 0257 macron
+char175 "
+aa 333,737 2 0264 acute
+char180 "
+ps 747,722 2 0266 paragraph
+char182 "
+char183 278,338 0 0267 periodcentered
+ac 333,0,221 1 0270 cedilla
+char184 "
+Om 367,737 2 0272 ordmasculine
+char186 "
+fc 333,408 0 0273 guilsinglright
+r? 500,547,205 3 0277 questiondown
+char191 "
+`A 759,981 2 0300 Agrave
+char192 "
+'A 759,984 2 0301 Aacute
+char193 "
+^A 759,969 2 0302 Acircumflex
+char194 "
+~A 759,951 2 0303 Atilde
+char195 "
+:A 759,940 2 0304 Adieresis
+char196 "
+oA 759,1007 2 0305 Aring
+char197 "
+AE 981,722 2 0306 AE
+char198 "
+,C 778,737,221 3 0307 Ccedilla
+char199 "
+`E 759,981 2 0310 Egrave
+char200 "
+'E 759,984 2 0311 Eacute
+char201 "
+^E 759,969 2 0312 Ecircumflex
+char202 "
+:E 759,940 2 0313 Edieresis
+char203 "
+`I 444,981 2 0314 Igrave
+char204 "
+'I 444,984 2 0315 Iacute
+char205 "
+^I 444,969 2 0316 Icircumflex
+char206 "
+:I 444,940 2 0317 Idieresis
+char207 "
+~N 833,951,8 2 0321 Ntilde
+char209 "
+`O 833,981,15 2 0322 Ograve
+char210 "
+'O 833,984,15 2 0323 Oacute
+char211 "
+^O 833,969,15 2 0324 Ocircumflex
+char212 "
+~O 833,951,15 2 0325 Otilde
+char213 "
+:O 833,940,15 2 0326 Odieresis
+char214 "
+/O 833,768,60 2 0330 Oslash
+char216 "
+`U 833,981,15 2 0331 Ugrave
+char217 "
+'U 833,984,15 2 0332 Uacute
+char218 "
+^U 833,969,15 2 0333 Ucircumflex
+char219 "
+:U 833,940,15 2 0334 Udieresis
+char220 "
+ss 611,737,15 2 0337 germandbls
+char223 "
+`a 611,734,15 2 0340 agrave
+char224 "
+'a 611,737,15 2 0341 aacute
+char225 "
+^a 611,722,15 2 0342 acircumflex
+char226 "
+~a 611,704,15 2 0343 atilde
+char227 "
+:a 611,693,15 2 0344 adieresis
+char228 "
+oa 611,760,15 2 0345 aring
+char229 "
+ae 870,485,15 0 0346 ae
+char230 "
+,c 556,485,221 1 0347 ccedilla
+char231 "
+`e 574,734,15 2 0350 egrave
+char232 "
+'e 574,737,15 2 0351 eacute
+char233 "
+^e 574,722,15 2 0352 ecircumflex
+char234 "
+:e 574,693,15 2 0353 edieresis
+char235 "
+`i 370,734 2 0354 igrave
+char236 "
+'i 370,737 2 0355 iacute
+char237 "
+^i 370,722 2 0356 icircumflex
+char238 "
+:i 370,693 2 0357 idieresis
+char239 "
+~n 685,704 2 0361 ntilde
+char241 "
+`o 611,734,15 2 0362 ograve
+char242 "
+'o 611,737,15 2 0363 oacute
+char243 "
+^o 611,722,15 2 0364 ocircumflex
+char244 "
+~o 611,704,15 2 0365 otilde
+char245 "
+:o 611,693,15 2 0366 odieresis
+char246 "
+/o 611,565,111 3 0370 oslash
+char248 "
+`u 685,734,15 2 0371 ugrave
+char249 "
+'u 685,737,15 2 0372 uacute
+char250 "
+^u 685,722,15 2 0373 ucircumflex
+char251 "
+:u 685,693,15 2 0374 udieresis
+char252 "
+:y 611,693,208 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/NBI b/ps/devps/NBI
new file mode 100644
index 000000000..52ffeaecc
--- /dev/null
+++ b/ps/devps/NBI
@@ -0,0 +1,416 @@
+name NBI
+internalname NewCenturySchlbk-BoldItalic
+slant 16
+spacewidth 287
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -50
+A w -20
+A v -20
+A ' -74
+A Y -74
+A W -92
+A V -111
+A T -74
+F . -129
+F f -18
+F , -129
+F A -74
+L y -55
+L ' -55
+L Y -55
+L W -55
+L V -74
+L T -55
+P . -129
+P , -129
+P A -92
+R y -50
+R Y -20
+R W -20
+R V -20
+R T -20
+T y -89
+T w -89
+T u -89
+T ; -72
+T s -89
+T r -89
+T . -111
+T o -109
+T i -71
+T - -129
+T hy -129
+T char173 -129
+T e -109
+T , -111
+T : -62
+T c -89
+T a -74
+T A -50
+V y -89
+V u -89
+V ; -74
+V r -109
+V . -111
+V o -129
+V i -72
+V - -74
+V hy -74
+V char173 -74
+V e -129
+V , -111
+V : -74
+V a -129
+V A -129
+W y -74
+W u -74
+W ; -37
+W r -74
+W . -55
+W o -74
+W i -74
+W - -37
+W hy -37
+W char173 -37
+W e -74
+W , -55
+W : -37
+W a -74
+W A -89
+Y v -129
+Y u -129
+Y ; -111
+Y q -129
+Y . -129
+Y p -129
+Y o -129
+Y i -90
+Y - -129
+Y hy -129
+Y char173 -129
+Y e -129
+Y , -129
+Y : -111
+Y a -129
+Y A -111
+f ' 74
+1 1 -74
+` ` -18
+` oq -18
+oq ` -18
+oq oq -18
+' t -37
+' s -37
+' ' -18
+r ' 55
+r . -18
+r . -129
+r , -18
+r , -129
+v . -37
+v , -37
+w . -37
+w , -37
+y . -55
+y , -55
+charset
+ha 606,722,0,0,-2 2 0000 asciicircum
+ti 606,353,0,0,-1 0 0001 asciitilde
+vS 685,954,15,31,49,31 2 0002 Scaron
+vZ 704,954,0,61,79,61 2 0003 Zcaron
+vs 481,709,15,46,48,46 2 0004 scaron
+vz 519,709,15,27,69,27 2 0005 zcaron
+:Y 704,930,0,123,35,109 2 0006 Ydieresis
+tm 950,722,0,82,8,82 2 0007 trademark
+space 287 0 0040
+! 333,737,15,53,47,53 2 0041 exclam
+" 400,736,0,157,-50,109 2 0042 quotedbl
+# 606,705,0,0,-7 2 0043 numbersign
+sh "
+$ 574,799,143,35,47,35 3 0044 dollar
+Do "
+% 889,721,34,0,-1 2 0045 percent
+& 889,737,15,0,19 2 0046 ampersand
+' 259,739,0,92,-20,92 2 0047 quoteright
+( 407,740,123,95,-21,95 3 0050 parenleft
+) 407,740,123,0,120 3 0051 parenright
+* 500,704,0,22,18,22 2 0052 asterisk
++ 606,505,0,0,9 0 0053 plus
+, 287,159,190,0,98 1 0054 comma
+- 333,297,0,0,40 0 0055 hyphen
+hy "
+char173 "
+. 287,159,15,0,38 0 0056 period
+/ 278,737,15,83,75,83 2 0057 slash
+sl "
+0 574,705,15,29,29,29 2 0060 zero
+1 574,705,0,0,25 2 0061 one
+2 574,705,0,14,90,14 2 0062 two
+3 574,705,15,14,55,14 2 0063 three
+4 574,705,0,20,63,20 2 0064 four
+5 574,705,15,49,50,49 2 0065 five
+6 574,705,15,49,20,49 2 0066 six
+7 574,705,15,69,-14,69 2 0067 seven
+8 574,705,15,27,50,27 2 0070 eight
+9 574,705,15,20,49,20 2 0071 nine
+: 287,477,15,20,52,20 0 0072 colon
+; 287,477,190,20,89,20 1 0073 semicolon
+< 606,524,15,0,36 0 0074 less
+= 606,402,0,0,9 0 0075 equal
+> 606,524,15,0,12 0 0076 greater
+? 481,737,15,21,-29,21 2 0077 question
+@ 747,737,15,25,28,25 2 0100 at
+at "
+A 741,737,0,25,125,25 2 0101 A
+B 759,722,0,11,100,11 2 0102 B
+C 759,737,15,50,15,50 2 0103 C
+D 833,722,0,13,99,13 2 0104 D
+E 741,722,0,37,91,37 2 0105 E
+F 704,722,0,72,91,72 2 0106 F
+G 815,737,15,37,16,37 2 0107 G
+H 870,722,0,92,91,92 2 0110 H
+I 444,722,0,92,91,92 2 0111 I
+J 667,722,15,101,59,101 2 0112 J
+K 778,722,0,104,91,104 2 0113 K
+L 704,722,0,16,91,16 2 0114 L
+M 944,722,0,94,74,94 2 0115 M
+N 852,722,16,110,107,109 2 0116 N
+O 833,737,15,14,14,14 2 0117 O
+P 741,722,0,40,88,40 2 0120 P
+Q 833,737,186,13,15,13 3 0121 Q
+R 796,722,15,5,91,5 2 0122 R
+S 685,737,15,31,49,31 2 0123 S
+T 722,722,0,90,6,90 2 0124 T
+U 833,722,15,112,-38,109 2 0125 U
+V 741,722,15,111,18,109 2 0126 V
+W 944,722,15,100,10,100 2 0127 W
+X 741,722,0,120,122,109 2 0130 X
+Y 704,722,0,123,35,109 2 0131 Y
+Z 704,722,0,61,79,61 2 0132 Z
+[ 407,737,110,75,81,75 3 0133 bracketleft
+lB "
+\ 606,737,0,0,-31 2 0134 backslash
+rs "
+] 407,737,110,57,99,57 3 0135 bracketright
+rB "
+a^ 333,709,0,82,27,82 2 0136 circumflex
+^ "
+_ 500,0,129,50,50,50 1 0137 underscore
+` 259,747,0,61,11,61 2 0140 quoteleft
+oq "
+a 667,477,15,29,35,29 0 0141 a
+b 611,737,15,0,22 2 0142 b
+c 537,477,15,0,47 0 0143 c
+d 667,737,15,46,50,46 2 0144 d
+e 519,477,15,9,52,9 0 0145 e
+f 389,737,205,211,98,109 3 0146 f
+g 611,529,205,41,116,41 3 0147 g
+h 685,737,15,2,50,2 2 0150 h
+i 389,737,15,4,22,4 2 0151 i
+j 370,737,205,61,220,61 3 0152 j
+k 648,737,15,0,55 2 0153 k
+l 389,737,15,31,24,31 2 0154 l
+m 944,477,15,26,41,26 0 0155 m
+n 685,477,15,7,47,7 0 0156 n
+o 574,477,15,16,40,16 0 0157 o
+p 648,477,205,0,168 1 0160 p
+q 630,477,205,7,50,7 1 0161 q
+r 519,486,0,55,47,55 0 0162 r
+s 481,477,15,6,48,6 0 0163 s
+t 407,650,15,47,26,47 2 0164 t
+u 685,477,15,4,12,4 0 0165 u
+v 556,477,15,0,18 0 0166 v
+w 833,477,15,5,28,5 0 0167 w
+x 574,477,15,47,99,47 0 0170 x
+y 519,477,205,23,116,23 1 0171 y
+z 519,477,15,2,69,2 0 0172 z
+lC 407,738,115,45,-2,45 3 0173 braceleft
+{ "
+ba 606,737,0,0,-212 2 0174 bar
+| "
+rC 407,738,115,0,80 3 0175 braceright
+} "
+a~ 333,683,0,121,16,109 2 0176 tilde
+~ "
+--- 259,159,191,0,93 1 0200 quotesinglbase
+Fo 481,409,0,0,85 0 0201 guillemotleft
+char171 "
+Fc 481,408,0,25,56,25 0 0202 guillemotright
+char187 "
+bu 606,537,0,0,-59 2 0203 bullet
+--- 574,737,205,64,77,64 3 0204 florin
+f/ 167,705,15,216,216,109 2 0205 fraction
+%0 1167,721,34,34,24,34 2 0206 perthousand
+dg 500,737,146,60,0,60 3 0207 dagger
+dd 500,737,147,64,98,64 3 0210 daggerdbl
+en 500,286,0,68,68,68 0 0211 endash
+em 1000,286,0,68,68,68 0 0212 emdash
+fi 685,737,205,11,120,11 3 0214 fi
+fl 685,737,205,46,120,46 3 0215 fl
+.i 389,477,15,14,12,14 0 0220 dotlessi
+ga 333,719,0,11,-24,11 2 0222 grave
+a" 333,719,0,189,27,109 2 0223 hungarumlaut
+a. 333,685,0,5,-95,5 2 0224 dotaccent
+ab 333,698,0,107,-17,107 2 0225 breve
+ah 333,709,0,120,-10,109 2 0226 caron
+ao 333,745,0,149,-167,109 2 0227 ring
+ho 333,0,155,0,-18 1 0230 ogonek
+lq 481,747,0,91,-2,91 2 0231 quotedblleft
+rq 481,739,0,102,-11,102 2 0232 quotedblright
+oe 852,477,15,8,55,8 0 0233 oe
+/l 389,737,15,58,24,58 2 0234 lslash
+--- 481,159,191,0,120 1 0235 quotedblbase
+OE 963,722,0,67,31,67 2 0236 OE
+/L 704,722,0,16,91,16 2 0237 Lslash
+r! 333,544,205,6,89,6 3 0241 exclamdown
+char161 "
+ct 574,600,124,0,20 3 0242 cent
+char162 "
+Po 574,705,15,42,68,42 2 0243 sterling
+char163 "
+Cs 574,612,0,22,10,22 2 0244 currency
+char164 "
+Ye 574,705,0,111,27,109 2 0245 yen
+char165 "
+sc 500,737,145,55,90,55 3 0247 section
+char167 "
+ad 333,685,0,109,16,109 2 0250 dieresis
+char168 "
+co 747,737,15,26,27,26 2 0251 copyright
+char169 "
+Of 412,737,0,87,21,87 2 0252 ordfeminine
+char170 "
+fo 278,409,0,0,85 0 0253 guilsinglleft
+no 606,402,0,0,9 0 0254 logicalnot
+char172 "
+\- 606,304,0,0,9 0 0255 minus
+rg 747,737,15,26,27,26 2 0256 registered
+char174 "
+a- 333,649,0,104,18,104 2 0257 macron
+char175 "
+aa 333,719,0,88,-73,88 2 0264 acute
+char180 "
+ps 650,737,0,61,-17,61 2 0266 paragraph
+char182 "
+char183 287,342,0,0,9 0 0267 periodcentered
+ac 333,3,220,0,137 1 0270 cedilla
+char184 "
+Om 356,737,0,77,21,77 2 0272 ordmasculine
+char186 "
+fc 278,408,0,25,56,25 0 0273 guilsinglright
+r? 481,544,205,0,49 3 0277 questiondown
+char191 "
+`A 741,964,0,25,125,25 2 0300 Agrave
+char192 "
+'A 741,964,0,25,125,25 2 0301 Aacute
+char193 "
+^A 741,954,0,25,125,25 2 0302 Acircumflex
+char194 "
+~A 741,928,0,25,125,25 2 0303 Atilde
+char195 "
+:A 741,930,0,25,125,25 2 0304 Adieresis
+char196 "
+oA 741,990,0,25,125,25 2 0305 Aring
+char197 "
+AE 889,722,0,67,131,67 2 0306 AE
+char198 "
+,C 759,737,220,50,15,50 3 0307 Ccedilla
+char199 "
+`E 741,964,0,37,91,37 2 0310 Egrave
+char200 "
+'E 741,964,0,37,91,37 2 0311 Eacute
+char201 "
+^E 741,954,0,37,91,37 2 0312 Ecircumflex
+char202 "
+:E 741,930,0,37,91,37 2 0313 Edieresis
+char203 "
+`I 444,964,0,92,91,92 2 0314 Igrave
+char204 "
+'I 444,964,0,92,91,92 2 0315 Iacute
+char205 "
+^I 444,954,0,92,91,92 2 0316 Icircumflex
+char206 "
+:I 444,930,0,104,91,104 2 0317 Idieresis
+char207 "
+~N 852,928,16,110,107,109 2 0321 Ntilde
+char209 "
+`O 833,964,15,14,14,14 2 0322 Ograve
+char210 "
+'O 833,964,15,14,14,14 2 0323 Oacute
+char211 "
+^O 833,954,15,14,14,14 2 0324 Ocircumflex
+char212 "
+~O 833,928,15,14,14,14 2 0325 Otilde
+char213 "
+:O 833,930,15,14,14,14 2 0326 Odieresis
+char214 "
+/O 833,775,82,14,33,14 3 0330 Oslash
+char216 "
+`U 833,964,15,112,-38,109 2 0331 Ugrave
+char217 "
+'U 833,964,15,112,-38,109 2 0332 Uacute
+char218 "
+^U 833,954,15,112,-38,109 2 0333 Ucircumflex
+char219 "
+:U 833,930,15,112,-38,109 2 0334 Udieresis
+char220 "
+ss 574,737,205,0,116 3 0337 germandbls
+char223 "
+`a 667,719,15,29,35,29 2 0340 agrave
+char224 "
+'a 667,719,15,29,35,29 2 0341 aacute
+char225 "
+^a 667,709,15,29,35,29 2 0342 acircumflex
+char226 "
+~a 667,683,15,29,35,29 2 0343 atilde
+char227 "
+:a 667,685,15,29,35,29 2 0344 adieresis
+char228 "
+oa 667,745,15,29,35,29 2 0345 aring
+char229 "
+ae 815,477,15,9,69,9 0 0346 ae
+char230 "
+,c 537,477,220,0,47 1 0347 ccedilla
+char231 "
+`e 519,719,15,9,52,9 2 0350 egrave
+char232 "
+'e 519,719,15,9,52,9 2 0351 eacute
+char233 "
+^e 519,709,15,9,52,9 2 0352 ecircumflex
+char234 "
+:e 519,685,15,26,52,26 2 0353 edieresis
+char235 "
+`i 389,719,15,14,12,14 2 0354 igrave
+char236 "
+'i 389,719,15,60,12,60 2 0355 iacute
+char237 "
+^i 389,709,15,44,12,44 2 0356 icircumflex
+char238 "
+:i 389,685,15,71,12,71 2 0357 idieresis
+char239 "
+~n 685,683,15,7,47,7 2 0361 ntilde
+char241 "
+`o 574,719,15,16,40,16 2 0362 ograve
+char242 "
+'o 574,719,15,16,40,16 2 0363 oacute
+char243 "
+^o 574,709,15,16,40,16 2 0364 ocircumflex
+char244 "
+~o 574,683,15,16,40,16 2 0365 otilde
+char245 "
+:o 574,685,15,16,40,16 2 0366 odieresis
+char246 "
+/o 574,578,126,16,40,16 3 0370 oslash
+char248 "
+`u 685,719,15,4,12,4 2 0371 ugrave
+char249 "
+'u 685,719,15,4,12,4 2 0372 uacute
+char250 "
+^u 685,709,15,4,12,4 2 0373 ucircumflex
+char251 "
+:u 685,685,15,4,12,4 2 0374 udieresis
+char252 "
+:y 519,685,205,23,116,23 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/NI b/ps/devps/NI
new file mode 100644
index 000000000..c8d1e9379
--- /dev/null
+++ b/ps/devps/NI
@@ -0,0 +1,415 @@
+name NI
+internalname NewCenturySchlbk-Italic
+slant 16
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -54
+A w -20
+A v -20
+A ' -74
+A Y -74
+A W -92
+A V -111
+A T -74
+F . -129
+F f -18
+F , -129
+F A -74
+L y -55
+L ' -55
+L Y -55
+L W -55
+L V -74
+L T -55
+P . -129
+P , -129
+P A -92
+R y -54
+R Y -74
+R W -55
+R V -20
+R T -20
+T y -89
+T w -89
+T u -89
+T ; -92
+T s -89
+T r -89
+T . -111
+T o -89
+T i -71
+T - -129
+T hy -129
+T char173 -129
+T e -89
+T , -111
+T : -92
+T c -89
+T a -74
+T A -18
+V y -109
+V u -109
+V ; -74
+V r -109
+V . -111
+V o -129
+V i -62
+V - -74
+V hy -74
+V char173 -74
+V e -129
+V : -74
+V a -129
+V A -111
+W y -74
+W u -74
+W ; -37
+W r -74
+W . -55
+W o -74
+W i -74
+W - -37
+W hy -37
+W char173 -37
+W e -74
+W , -55
+W : -37
+W a -74
+W A -74
+Y v -99
+Y u -99
+Y ; -111
+Y q -129
+Y . -129
+Y p -129
+Y o -129
+Y i -70
+Y - -129
+Y hy -129
+Y char173 -129
+Y e -129
+Y , -129
+Y : -111
+Y a -129
+Y A -74
+f ' 94
+1 1 -74
+` ` -18
+` oq -18
+oq ` -18
+oq oq -18
+' t -37
+' s -37
+' ' -18
+r ' 55
+r . -18
+r . -129
+r , -18
+r , -129
+v . -37
+v , -37
+w . -37
+w , -37
+y . -55
+y , -55
+charset
+ha 606,722,0,0,-2 2 0000 asciicircum
+ti 606,335,0,0,9 0 0001 asciitilde
+vS 667,944,15,15,50,15 2 0002 Scaron
+vZ 667,944,0,50,75,50 2 0003 Zcaron
+vs 444,688,15,40,51,40 2 0004 scaron
+vz 463,688,15,30,83,30 2 0005 zcaron
+:Y 685,900,0,123,18,107 2 0006 Ydieresis
+tm 950,722,0,71,2,71 2 0007 trademark
+space 278 0 0040
+! 333,737,15,33,20,33 2 0041 exclam
+" 400,737,0,45,-50,45 2 0042 quotedbl
+# 606,705,0,0,-7 2 0043 numbersign
+sh "
+$ 556,800,133,30,47,30 3 0044 dollar
+Do "
+% 833,705,17,9,4,9 2 0045 percent
+& 852,737,15,0,26 2 0046 ampersand
+' 204,737,0,76,11,76 2 0047 quoteright
+( 333,737,124,116,10,107 3 0050 parenleft
+) 333,737,124,0,143 3 0051 parenright
+* 500,705,0,2,17,2 2 0052 asterisk
++ 606,504,0,0,13 0 0053 plus
+, 278,109,165,0,89 1 0054 comma
+- 333,273,0,0,18 0 0055 hyphen
+hy "
+char173 "
+. 278,109,15,0,33 0 0056 period
+/ 606,737,102,0,-90 3 0057 slash
+sl "
+0 556,705,15,21,21,21 2 0060 zero
+1 556,705 2 0061 one
+2 556,705,0,2,85,2 2 0062 two
+3 556,705,15,0,52 2 0063 three
+4 556,705,0,6,58,6 2 0064 four
+5 556,705,15,34,46,34 2 0065 five
+6 556,705,15,41,14,41 2 0066 six
+7 556,705,15,55,-19,55 2 0067 seven
+8 556,705,15,21,42,21 2 0070 eight
+9 556,705,15,13,43,13 2 0071 nine
+: 278,466,15,26,8,26 0 0072 colon
+; 278,466,165,33,64,33 1 0073 semicolon
+< 606,518,10,0,16 2 0074 less
+= 606,381,0,0,14 0 0075 equal
+> 606,518,10,6,-3,6 2 0076 greater
+? 444,737,15,23,-52,23 2 0077 question
+@ 747,737,15,31,22,31 2 0100 at
+at "
+A 704,737,0,14,132,14 2 0101 A
+B 722,722,0,0,81 2 0102 B
+C 722,737,15,41,10,41 2 0103 C
+D 778,722,0,7,88,7 2 0104 D
+E 722,722,0,29,87,29 2 0105 E
+F 667,722,0,81,84,81 2 0106 F
+G 778,737,15,35,11,35 2 0107 G
+H 833,722,0,84,88,84 2 0110 H
+I 407,722,0,74,83,74 2 0111 I
+J 611,722,17,85,63,85 2 0112 J
+K 741,722,0,123,90,107 2 0113 K
+L 667,722,0,13,87,13 2 0114 L
+M 944,722,0,79,76,79 2 0115 M
+N 815,722,17,94,98,94 2 0116 N
+O 778,737,15,7,10,7 2 0117 O
+P 667,722,0,46,83,46 2 0120 P
+Q 778,737,190,7,10,7 3 0121 Q
+R 741,722,17,0,91 2 0122 R
+S 667,737,15,15,50,15 2 0123 S
+T 685,722,0,91,10,91 2 0124 T
+U 815,722,15,95,-43,95 2 0125 U
+V 704,722,15,118,14,107 2 0126 V
+W 926,722,15,97,-3,97 2 0127 W
+X 704,722,0,116,123,107 2 0130 X
+Y 685,722,0,123,18,107 2 0131 Y
+Z 667,722,0,50,75,50 2 0132 Z
+[ 333,737,109,127,83,107 3 0133 bracketleft
+lB "
+\ 606,737,0,0,-39 2 0134 backslash
+rs "
+] 333,737,109,76,133,76 3 0135 bracketright
+rB "
+a^ 333,688,0,48,13,48 2 0136 circumflex
+^ "
+_ 500,0,123,50,50,50 1 0137 underscore
+` 204,749,0,98,-11,98 2 0140 quoteleft
+oq "
+a 574,466,15,0,49 0 0141 a
+b 556,737,15,0,18 2 0142 b
+c 444,466,15,7,45,7 0 0143 c
+d 611,737,15,25,47,25 2 0144 d
+e 444,466,15,0,56 0 0145 e
+f 333,737,205,187,118,107 3 0146 f
+g 537,499,205,36,129,36 1 0147 g
+h 611,737,15,0,50 2 0150 h
+i 333,715,15,0,23 2 0151 i
+j 315,715,205,52,216,52 3 0152 j
+k 556,737,15,0,55 2 0153 k
+l 333,737,15,11,34,11 2 0154 l
+m 889,466,15,0,35 0 0155 m
+n 611,466,15,2,36,2 0 0156 n
+o 500,466,15,0,45 0 0157 o
+p 574,466,205,0,151 1 0160 p
+q 556,466,205,0,50 1 0161 q
+r 444,466,0,39,41,39 0 0162 r
+s 444,466,15,0,51 0 0163 s
+t 352,619,15,27,25,27 2 0164 t
+u 611,466,15,0,9 0 0165 u
+v 519,466,15,0,16 0 0166 v
+w 778,466,15,0,18 0 0167 w
+x 500,466,15,21,83,21 0 0170 x
+y 500,466,205,4,129,4 1 0171 y
+z 463,466,15,4,83,4 0 0172 z
+lC 333,737,116,98,-2,98 3 0173 braceleft
+{ "
+ba 606,737,0,0,-219 2 0174 bar
+| "
+rC 333,737,116,0,148 3 0175 braceright
+} "
+a~ 333,650,0,97,-2,97 2 0176 tilde
+~ "
+--- 204,109,167,0,129 1 0200 quotesinglbase
+Fo 426,399,0,26,65,26 0 0201 guillemotleft
+char171 "
+Fc 426,394,0,24,67,24 0 0202 guillemotright
+char187 "
+bu 606,537,0,0,-64 2 0203 bullet
+--- 556,737,205,57,107,57 3 0204 florin
+f/ 167,705,17,216,154,107 2 0205 fraction
+%0 1000,705,15,53,56,53 2 0206 perthousand
+dg 500,737,147,68,-14,68 3 0207 dagger
+dd 500,737,148,78,80,78 3 0210 daggerdbl
+en 500,260,0,68,68,68 0 0211 endash
+em 1000,260,0,68,68,68 0 0212 emdash
+fi 611,737,205,3,110,3 3 0214 fi
+fl 611,737,205,33,110,33 3 0215 fl
+.i 333,466,15,0,23 0 0220 dotlessi
+ga 333,691,0,0,-23 2 0222 grave
+a" 333,689,0,237,-84,107 2 0223 hungarumlaut
+a. 333,644,0,0,-68 2 0224 dotaccent
+ab 333,677,0,87,-19,87 2 0225 breve
+ah 333,688,0,95,-23,95 2 0226 caron
+ao 333,712,0,146,-180,107 2 0227 ring
+ho 333,0,155,0,-18 1 0230 ogonek
+lq 389,749,0,93,-13,93 2 0231 quotedblleft
+rq 389,737,0,68,12,68 2 0232 quotedblright
+oe 778,466,17,0,50 0 0233 oe
+/l 333,737,15,59,34,59 2 0234 lslash
+--- 389,109,167,0,129 1 0235 quotedblbase
+OE 981,722,0,32,14,32 2 0236 OE
+/L 667,722,0,13,87,13 2 0237 Lslash
+r! 333,542,205,0,57 3 0241 exclamdown
+char161 "
+ct 556,595,129,0,-11 3 0242 cent
+char162 "
+Po 556,705,15,39,58,39 2 0243 sterling
+char163 "
+Cs 606,603 2 0244 currency
+char164 "
+Ye 556,705,0,117,10,107 2 0245 yen
+char165 "
+sc 500,737,147,30,61,30 3 0247 section
+char167 "
+ad 333,644,0,76,-9,76 2 0250 dieresis
+char168 "
+co 747,737,15,27,26,27 2 0251 copyright
+char169 "
+Of 422,737,0,50,-33,50 2 0252 ordfeminine
+char170 "
+fo 333,399,0,0,8 0 0253 guilsinglleft
+no 606,381,0,0,9 0 0254 logicalnot
+char172 "
+\- 606,287,0,0,9 0 0255 minus
+rg 747,737,15,26,27,26 2 0256 registered
+char174 "
+a- 333,610,0,80,-1,80 2 0257 macron
+char175 "
+aa 333,689,0,72,-82,72 2 0264 acute
+char180 "
+ps 650,737,0,45,-38,45 2 0266 paragraph
+char182 "
+char183 278,316,0,0,-27 0 0267 periodcentered
+ac 333,0,227,0,47 1 0270 cedilla
+char184 "
+Om 372,737,0,50,-33,50 2 0272 ordmasculine
+char186 "
+fc 333,394,0,0,10 0 0273 guilsinglright
+r? 444,542,205,0,43 3 0277 questiondown
+char191 "
+`A 704,947,0,14,132,14 2 0300 Agrave
+char192 "
+'A 704,945,0,14,132,14 2 0301 Aacute
+char193 "
+^A 704,944,0,14,132,14 2 0302 Acircumflex
+char194 "
+~A 704,906,0,14,132,14 2 0303 Atilde
+char195 "
+:A 704,900,0,14,132,14 2 0304 Adieresis
+char196 "
+oA 704,968,0,14,132,14 2 0305 Aring
+char197 "
+AE 870,722,0,50,142,50 2 0306 AE
+char198 "
+,C 722,737,227,41,10,41 3 0307 Ccedilla
+char199 "
+`E 722,947,0,29,87,29 2 0310 Egrave
+char200 "
+'E 722,945,0,29,87,29 2 0311 Eacute
+char201 "
+^E 722,944,0,29,87,29 2 0312 Ecircumflex
+char202 "
+:E 722,900,0,29,87,29 2 0313 Edieresis
+char203 "
+`I 407,947,0,74,83,74 2 0314 Igrave
+char204 "
+'I 407,945,0,74,83,74 2 0315 Iacute
+char205 "
+^I 407,944,0,74,83,74 2 0316 Icircumflex
+char206 "
+:I 407,900,0,89,83,89 2 0317 Idieresis
+char207 "
+~N 815,906,17,94,98,94 2 0321 Ntilde
+char209 "
+`O 778,947,15,7,10,7 2 0322 Ograve
+char210 "
+'O 778,945,15,7,10,7 2 0323 Oacute
+char211 "
+^O 778,944,15,7,10,7 2 0324 Ocircumflex
+char212 "
+~O 778,906,15,7,10,7 2 0325 Otilde
+char213 "
+:O 778,900,15,7,10,7 2 0326 Odieresis
+char214 "
+/O 778,755,87,7,49,7 3 0330 Oslash
+char216 "
+`U 815,947,15,95,-43,95 2 0331 Ugrave
+char217 "
+'U 815,945,15,95,-43,95 2 0332 Uacute
+char218 "
+^U 815,944,15,95,-43,95 2 0333 Ucircumflex
+char219 "
+:U 815,900,15,95,-43,95 2 0334 Udieresis
+char220 "
+ss 556,737,205,19,126,19 3 0337 germandbls
+char223 "
+`a 574,691,15,0,49 2 0340 agrave
+char224 "
+'a 574,689,15,0,49 2 0341 aacute
+char225 "
+^a 574,688,15,0,49 2 0342 acircumflex
+char226 "
+~a 574,650,15,0,49 2 0343 atilde
+char227 "
+:a 574,644,15,0,49 2 0344 adieresis
+char228 "
+oa 574,712,15,0,49 2 0345 aring
+char229 "
+ae 722,466,15,0,68 0 0346 ae
+char230 "
+,c 444,466,227,7,45,7 1 0347 ccedilla
+char231 "
+`e 444,691,15,0,56 2 0350 egrave
+char232 "
+'e 444,689,15,17,56,17 2 0351 eacute
+char233 "
+^e 444,688,15,0,56 2 0352 ecircumflex
+char234 "
+:e 444,644,15,11,56,11 2 0353 edieresis
+char235 "
+`i 333,691,15,0,23 2 0354 igrave
+char236 "
+'i 333,689,15,72,23,72 2 0355 iacute
+char237 "
+^i 333,688,15,8,53,8 2 0356 icircumflex
+char238 "
+:i 333,644,15,66,23,66 2 0357 idieresis
+char239 "
+~n 611,650,15,2,36,2 2 0361 ntilde
+char241 "
+`o 500,691,15,0,45 2 0362 ograve
+char242 "
+'o 500,689,15,0,45 2 0363 oacute
+char243 "
+^o 500,688,15,0,45 2 0364 ocircumflex
+char244 "
+~o 500,650,15,4,45,4 2 0365 otilde
+char245 "
+:o 500,644,15,0,45 2 0366 odieresis
+char246 "
+/o 500,549,121,0,55 3 0370 oslash
+char248 "
+`u 611,691,15,0,9 2 0371 ugrave
+char249 "
+'u 611,689,15,0,9 2 0372 uacute
+char250 "
+^u 611,688,15,0,9 2 0373 ucircumflex
+char251 "
+:u 611,644,15,0,9 2 0374 udieresis
+char252 "
+:y 500,644,205,4,129,4 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/NR b/ps/devps/NR
new file mode 100644
index 000000000..b8d862e43
--- /dev/null
+++ b/ps/devps/NR
@@ -0,0 +1,414 @@
+name NR
+internalname NewCenturySchlbk-Roman
+spacewidth 278
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -74
+A w -74
+A v -74
+A ' -74
+A Y -92
+A W -92
+A V -111
+A T -55
+F . -111
+F , -111
+F A -74
+L y -55
+L ' -55
+L Y -92
+L W -74
+L V -92
+L T -55
+P . -129
+P , -129
+P A -74
+R y -18
+R Y -37
+R W -37
+R V -37
+T y -52
+T w -71
+T u -71
+T ; -55
+T s -62
+T r -62
+T . -92
+T o -92
+T i -25
+T - -92
+T hy -92
+T char173 -92
+T e -92
+T , -92
+T : -55
+T c -81
+T a -62
+T A -55
+V y -92
+V u -74
+V ; -74
+V r -74
+V . -129
+V o -92
+V i -44
+V - -92
+V hy -92
+V char173 -92
+V e -92
+V , -129
+V : -74
+V a -92
+V A -111
+W y -74
+W u -55
+W ; -37
+W r -55
+W . -111
+W o -55
+W i -37
+W - -37
+W hy -37
+W char173 -37
+W e -55
+W , -111
+W : -37
+W a -74
+W A -92
+Y v -111
+Y u -92
+Y ; -92
+Y q -111
+Y . -111
+Y p -111
+Y o -111
+Y i -44
+Y - -111
+Y hy -111
+Y char173 -111
+Y e -111
+Y , -111
+Y : -92
+Y a -111
+Y A -92
+f ' 114
+1 1 -55
+` ` -18
+` oq -18
+oq ` -18
+oq oq -18
+' t -18
+' s -37
+' ' -18
+r ' 55
+r . -74
+r - -18
+r hy -18
+r char173 -18
+r , -74
+v . -111
+v , -111
+w . -92
+w , -92
+y . -111
+y , -111
+charset
+ha 606,722 2 0000 asciicircum
+ti 606,329 0 0001 asciitilde
+vS 630,952,15 2 0002 Scaron
+vZ 611,952 2 0003 Zcaron
+vs 463,694,15 2 0004 scaron
+vz 481,694 2 0005 zcaron
+:Y 704,902 2 0006 Ydieresis
+tm 1000,737 2 0007 trademark
+space 278 0 0040
+! 296,737,15 2 0041 exclam
+" 389,737 2 0042 quotedbl
+# 556,690 2 0043 numbersign
+sh "
+$ 556,804,129 3 0044 dollar
+Do "
+% 833,707,17 2 0045 percent
+& 815,737,15 2 0046 ampersand
+' 204,737 2 0047 quoteright
+( 333,737,124 3 0050 parenleft
+) 333,737,124 3 0051 parenright
+* 500,737 2 0052 asterisk
++ 606,492,13 0 0053 plus
+, 278,109,184 1 0054 comma
+- 333,279 0 0055 hyphen
+hy "
+char173 "
+. 278,109,15 0 0056 period
+/ 278,737,15 2 0057 slash
+sl "
+0 556,705,15 2 0060 zero
+1 556,705 2 0061 one
+2 556,705,4 2 0062 two
+3 556,705,15 2 0063 three
+4 556,705 2 0064 four
+5 556,705,15 2 0065 five
+6 556,705,15 2 0066 six
+7 556,705,15 2 0067 seven
+8 556,705,15 2 0070 eight
+9 556,705,15 2 0071 nine
+: 278,475,15 0 0072 colon
+; 278,475,189 1 0073 semicolon
+< 606,503,25 0 0074 less
+= 606,374 0 0075 equal
+> 606,503,25 0 0076 greater
+? 444,737,15 2 0077 question
+@ 737,737,15 2 0100 at
+at "
+A 722,737 2 0101 A
+B 722,722 2 0102 B
+C 722,737,15 2 0103 C
+D 778,722 2 0104 D
+E 722,722 2 0105 E
+F 667,722 2 0106 F
+G 778,737,15 2 0107 G
+H 833,722 2 0110 H
+I 407,722 2 0111 I
+J 556,722,15 2 0112 J
+K 778,722 2 0113 K
+L 667,722 2 0114 L
+M 944,722 2 0115 M
+N 815,722,15 2 0116 N
+O 778,737,15 2 0117 O
+P 667,722 2 0120 P
+Q 778,737,189 3 0121 Q
+R 722,722,15 2 0122 R
+S 630,737,15 2 0123 S
+T 667,722 2 0124 T
+U 815,722,15 2 0125 U
+V 722,722,15 2 0126 V
+W 981,722,15 2 0127 W
+X 704,722 2 0130 X
+Y 704,722 2 0131 Y
+Z 611,722 2 0132 Z
+[ 333,722,109 3 0133 bracketleft
+lB "
+\ 606,737 2 0134 backslash
+rs "
+] 333,723,108 3 0135 bracketright
+rB "
+a^ 333,694 2 0136 circumflex
+^ "
+_ 500,0,134 1 0137 underscore
+` 204,737 2 0140 quoteleft
+oq "
+a 556,479,15 0 0141 a
+b 556,737,15 2 0142 b
+c 444,479,15 0 0143 c
+d 574,737,15 2 0144 d
+e 500,479,15 0 0145 e
+f 333,737 2 0146 f
+g 537,494,205 1 0147 g
+h 611,737 2 0150 h
+i 315,716 2 0151 i
+j 296,716,205 3 0152 j
+k 593,737 2 0153 k
+l 315,737 2 0154 l
+m 889,479 0 0155 m
+n 611,479 0 0156 n
+o 500,479,15 0 0157 o
+p 574,479,205 1 0160 p
+q 556,479,205 1 0161 q
+r 444,479 0 0162 r
+s 463,479,15 0 0163 s
+t 389,666,15 2 0164 t
+u 611,464,15 0 0165 u
+v 537,464,15 0 0166 v
+w 778,464,15 0 0167 w
+x 537,464 0 0170 x
+y 537,464,205 1 0171 y
+z 481,464 0 0172 z
+lC 333,722,109 3 0173 braceleft
+{ "
+ba 606,737 2 0174 bar
+| "
+rC 333,722,109 3 0175 braceright
+} "
+a~ 333,659 2 0176 tilde
+~ "
+--- 204,104,189 1 0200 quotesinglbase
+Fo 426,398 0 0201 guillemotleft
+char171 "
+Fc 426,399 0 0202 guillemotright
+char187 "
+bu 606,554 2 0203 bullet
+--- 556,737,205 3 0204 florin
+f/ 167,705 2 0205 fraction
+%0 1000,699 2 0206 perthousand
+dg 500,737,147 3 0207 dagger
+dd 500,737,151 3 0210 daggerdbl
+en 556,269 0 0211 endash
+em 1000,269 0 0212 emdash
+fi 611,737 2 0214 fi
+fl 611,737 2 0215 fl
+.i 315,464 0 0220 dotlessi
+ga 333,699 2 0222 grave
+a" 333,714 2 0223 hungarumlaut
+a. 333,644 2 0224 dotaccent
+ab 333,685 2 0225 breve
+ah 333,694 2 0226 caron
+ao 333,722 2 0227 ring
+ho 333,0,163 1 0230 ogonek
+lq 389,737 2 0231 quotedblleft
+rq 389,737 2 0232 quotedblright
+oe 833,479,15 0 0233 oe
+/l 315,737 2 0234 lslash
+--- 389,104,189 1 0235 quotedblbase
+OE 1000,722 2 0236 OE
+/L 667,722 2 0237 Lslash
+r! 296,547,205 3 0241 exclamdown
+char161 "
+ct 556,584,141 3 0242 cent
+char162 "
+Po 556,705,15 2 0243 sterling
+char163 "
+Cs 606,603 2 0244 currency
+char164 "
+Ye 704,690 2 0245 yen
+char165 "
+sc 500,737,147 3 0247 section
+char167 "
+ad 333,644 2 0250 dieresis
+char168 "
+co 737,737,15 2 0251 copyright
+char169 "
+Of 334,722 2 0252 ordfeminine
+char170 "
+fo 259,398 0 0253 guilsinglleft
+no 606,374 0 0254 logicalnot
+char172 "
+\- 606,277 0 0255 minus
+rg 737,737,15 2 0256 registered
+char174 "
+a- 333,622 2 0257 macron
+char175 "
+aa 333,699 2 0264 acute
+char180 "
+ps 606,722,147 3 0266 paragraph
+char182 "
+char183 278,302 0 0267 periodcentered
+ac 333,0,215 1 0270 cedilla
+char184 "
+Om 300,722 2 0272 ordmasculine
+char186 "
+fc 259,399 0 0273 guilsinglright
+r? 444,547,205 3 0277 questiondown
+char191 "
+`A 722,957 2 0300 Agrave
+char192 "
+'A 722,957 2 0301 Aacute
+char193 "
+^A 722,952 2 0302 Acircumflex
+char194 "
+~A 722,917 2 0303 Atilde
+char195 "
+:A 722,902 2 0304 Adieresis
+char196 "
+oA 722,980 2 0305 Aring
+char197 "
+AE 1000,722 2 0306 AE
+char198 "
+,C 722,737,215 3 0307 Ccedilla
+char199 "
+`E 722,957 2 0310 Egrave
+char200 "
+'E 722,957 2 0311 Eacute
+char201 "
+^E 722,952 2 0312 Ecircumflex
+char202 "
+:E 722,902 2 0313 Edieresis
+char203 "
+`I 407,957 2 0314 Igrave
+char204 "
+'I 407,957 2 0315 Iacute
+char205 "
+^I 407,952 2 0316 Icircumflex
+char206 "
+:I 407,902 2 0317 Idieresis
+char207 "
+~N 815,917,15 2 0321 Ntilde
+char209 "
+`O 778,957,15 2 0322 Ograve
+char210 "
+'O 778,957,15 2 0323 Oacute
+char211 "
+^O 778,952,15 2 0324 Ocircumflex
+char212 "
+~O 778,917,15 2 0325 Otilde
+char213 "
+:O 778,902,15 2 0326 Odieresis
+char214 "
+/O 778,760,74 3 0330 Oslash
+char216 "
+`U 815,957,15 2 0331 Ugrave
+char217 "
+'U 815,957,15 2 0332 Uacute
+char218 "
+^U 815,952,15 2 0333 Ucircumflex
+char219 "
+:U 815,902,15 2 0334 Udieresis
+char220 "
+ss 574,737,15 2 0337 germandbls
+char223 "
+`a 556,699,15 2 0340 agrave
+char224 "
+'a 556,699,15 2 0341 aacute
+char225 "
+^a 556,694,15 2 0342 acircumflex
+char226 "
+~a 556,659,15 2 0343 atilde
+char227 "
+:a 556,644,15 2 0344 adieresis
+char228 "
+oa 556,722,15 2 0345 aring
+char229 "
+ae 796,479,15 0 0346 ae
+char230 "
+,c 444,479,215 1 0347 ccedilla
+char231 "
+`e 500,699,15 2 0350 egrave
+char232 "
+'e 500,699,15 2 0351 eacute
+char233 "
+^e 500,694,15 2 0352 ecircumflex
+char234 "
+:e 500,644,15 2 0353 edieresis
+char235 "
+`i 315,699 2 0354 igrave
+char236 "
+'i 315,699 2 0355 iacute
+char237 "
+^i 315,694 2 0356 icircumflex
+char238 "
+:i 315,644 2 0357 idieresis
+char239 "
+~n 611,659 2 0361 ntilde
+char241 "
+`o 500,699,15 2 0362 ograve
+char242 "
+'o 500,699,15 2 0363 oacute
+char243 "
+^o 500,694,15 2 0364 ocircumflex
+char244 "
+~o 500,659,15 2 0365 otilde
+char245 "
+:o 500,644,15 2 0366 odieresis
+char246 "
+/o 500,556,102 3 0370 oslash
+char248 "
+`u 611,699,15 2 0371 ugrave
+char249 "
+'u 611,699,15 2 0372 uacute
+char250 "
+^u 611,694,15 2 0373 ucircumflex
+char251 "
+:u 611,644,15 2 0374 udieresis
+char252 "
+:y 537,644,205 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/PB b/ps/devps/PB
new file mode 100644
index 000000000..4b5dee7ba
--- /dev/null
+++ b/ps/devps/PB
@@ -0,0 +1,416 @@
+name PB
+internalname Palatino-Bold
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -70
+A w -70
+A v -70
+A ' -92
+A Y -111
+A W -90
+A V -129
+A T -92
+F . -111
+F , -111
+F A -55
+L y -74
+L ' -74
+L Y -92
+L W -92
+L V -92
+L T -74
+P . -129
+P , -129
+P A -74
+R y -30
+R Y -55
+R W -37
+R V -74
+R T -55
+T y -90
+T w -90
+T u -129
+T ; -74
+T s -111
+T r -111
+T . -92
+T o -111
+T i -55
+T - -92
+T hy -92
+T char173 -92
+T e -111
+T , -92
+T : -74
+T c -129
+T a -111
+T A -92
+V y -90
+V u -92
+V ; -74
+V r -111
+V . -129
+V o -111
+V i -55
+V - -92
+V hy -92
+V char173 -92
+V e -111
+V , -129
+V : -74
+V a -111
+V A -129
+W y -74
+W u -74
+W ; -37
+W r -74
+W . 92
+W o -74
+W i -37
+W - -37
+W hy -37
+W char173 -37
+W e -74
+W , -92
+W : -37
+W a -74
+W A -90
+Y v -74
+Y u -74
+Y ; -55
+Y q -92
+Y . -74
+Y p -74
+Y o -74
+Y i -55
+Y - -74
+Y hy -74
+Y char173 -74
+Y e -74
+Y , -74
+Y : -55
+Y a -74
+Y A -55
+f ' 37
+f f -18
+1 1 -37
+` ` -55
+` oq -55
+oq ` -55
+oq oq -55
+' t -18
+' s -55
+' ' -55
+r ' 55
+r . -55
+r - -18
+r hy -18
+r char173 -18
+r , -55
+v . -111
+v , -111
+w . -92
+w , -92
+y . -92
+y , -92
+charset
+ha 606,678 2 0000 asciicircum
+ti 606,342 0 0001 asciitilde
+vS 611,933,17 2 0002 Scaron
+vZ 667,933,5 2 0003 Zcaron
+vs 444,709,17 2 0004 scaron
+vz 500,709 2 0005 zcaron
+:Y 667,902,5 2 0006 Ydieresis
+tm 998,678 2 0007 trademark
+space 250 0 0040
+! 278,688,12 2 0041 exclam
+" 402,695 2 0042 quotedbl
+# 606,673 2 0043 numbersign
+sh "
+$ 500,721,115 3 0044 dollar
+Do "
+% 889,678,17 2 0045 percent
+& 833,684,17 2 0046 ampersand
+' 278,695 2 0047 quoteright
+( 333,723,104 3 0050 parenleft
+) 333,723,105 3 0051 parenright
+* 444,695 2 0052 asterisk
++ 606,505 0 0053 plus
+, 250,141,166 1 0054 comma
+- 333,305 0 0055 hyphen
+hy "
+char173 "
+. 250,144,12 0 0056 period
+/ 296,695,17 2 0057 slash
+sl "
+0 500,660,17 2 0060 zero
+1 500,671,5 2 0061 one
+2 500,662,2 2 0062 two
+3 500,660,17 2 0063 three
+4 500,670,3 2 0064 four
+5 500,657,17 2 0065 five
+6 500,664,17 2 0066 six
+7 500,656,2 2 0067 seven
+8 500,660,17 2 0070 eight
+9 500,662,17 2 0071 nine
+: 250,454,12 0 0072 colon
+; 250,454,166 1 0073 semicolon
+< 606,519,15 0 0074 less
+= 606,396 0 0075 equal
+> 606,519,15 0 0076 greater
+? 444,687,12 2 0077 question
+@ 747,681,12 2 0100 at
+at "
+A 778,686,5 2 0101 A
+B 667,683,5 2 0102 B
+C 722,695,17 2 0103 C
+D 833,683 2 0104 D
+E 611,683,5 2 0105 E
+F 556,683,5 2 0106 F
+G 833,695,17 2 0107 G
+H 833,683,5 2 0110 H
+I 389,683,5 2 0111 I
+J 389,683,213 3 0112 J
+K 778,683,5 2 0113 K
+L 611,683,5 2 0114 L
+M 1000,683,10 2 0115 M
+N 833,683,16 2 0116 N
+O 833,695,17 2 0117 O
+P 611,678,5 2 0120 P
+Q 833,695,188 3 0121 Q
+R 722,683,5 2 0122 R
+S 611,695,17 2 0123 S
+T 667,683,5 2 0124 T
+U 778,683,17 2 0125 U
+V 778,683,3 2 0126 V
+W 1000,686,3 2 0127 W
+X 667,695 2 0130 X
+Y 667,695,5 2 0131 Y
+Z 667,683,5 2 0132 Z
+[ 333,720,104 3 0133 bracketleft
+lB "
+\ 606,723 2 0134 backslash
+rs "
+] 333,720,104 3 0135 bracketright
+rB "
+a^ 333,709 2 0136 circumflex
+^ "
+_ 500,0,148 1 0137 underscore
+` 278,695 2 0140 quoteleft
+oq "
+a 500,471,15 0 0141 a
+b 611,719,17 2 0142 b
+c 444,471,17 0 0143 c
+d 611,719,17 2 0144 d
+e 500,471,17 0 0145 e
+f 389,725,5 2 0146 f
+g 556,471,266 1 0147 g
+h 611,720,5 2 0150 h
+i 333,706,5 2 0151 i
+j 333,705,265 3 0152 j
+k 611,720,5 2 0153 k
+l 333,720,5 2 0154 l
+m 889,471,5 0 0155 m
+n 611,471,5 0 0156 n
+o 556,471,17 0 0157 o
+p 611,471,258 1 0160 p
+q 611,471,258 1 0161 q
+r 389,471,5 0 0162 r
+s 444,471,17 0 0163 s
+t 333,632,17 2 0164 t
+u 611,471,17 0 0165 u
+v 556,459,3 0 0166 v
+w 833,472,3 0 0167 w
+x 500,471 0 0170 x
+y 556,459,263 1 0171 y
+z 500,459 0 0172 z
+lC 310,725,116 3 0173 braceleft
+{ "
+ba 606,723 2 0174 bar
+| "
+rC 310,725,116 3 0175 braceright
+} "
+a~ 333,678 2 0176 tilde
+~ "
+--- 333,130,160 1 0200 quotesinglbase
+Fo 500,438 0 0201 guillemotleft
+char171 "
+Fc 500,438 0 0202 guillemotright
+char187 "
+bu 606,502 0 0203 bullet
+--- 500,697,246 3 0204 florin
+f/ 167,623 2 0205 fraction
+%0 1000,678,17 2 0206 perthousand
+dg 500,682,6 2 0207 dagger
+dd 500,683,244 3 0210 daggerdbl
+en 500,291 0 0211 endash
+em 1000,291 0 0212 emdash
+fi 611,722,5 2 0214 fi
+fl 611,722,5 2 0215 fl
+.i 333,471,5 0 0220 dotlessi
+ga 333,719 2 0222 grave
+a" 333,719 2 0223 hungarumlaut
+a. 333,678 2 0224 dotaccent
+ab 333,697 2 0225 breve
+ah 333,709 2 0226 caron
+ao 333,742 2 0227 ring
+ho 333,0,228 1 0230 ogonek
+lq 500,693 2 0231 quotedblleft
+rq 500,695 2 0232 quotedblright
+oe 833,471,17 0 0233 oe
+/l 333,720,5 2 0234 lslash
+--- 500,130,160 1 0235 quotedblbase
+OE 1000,695,17 2 0236 OE
+/L 611,683,5 2 0237 Lslash
+r! 278,471,230 1 0241 exclamdown
+char161 "
+ct 500,554,106 3 0242 cent
+char162 "
+Po 500,676,19 2 0243 sterling
+char163 "
+Cs 606,588 2 0244 currency
+char164 "
+Ye 500,670,3 2 0245 yen
+char165 "
+sc 500,695,217 3 0247 section
+char167 "
+ad 333,678 2 0250 dieresis
+char168 "
+co 747,695,17 2 0251 copyright
+char169 "
+Of 438,678 2 0252 ordfeminine
+char170 "
+fo 389,438 0 0253 guilsinglleft
+no 606,396 0 0254 logicalnot
+char172 "
+\- 606,298 0 0255 minus
+rg 747,695,17 2 0256 registered
+char174 "
+a- 333,642 2 0257 macron
+char175 "
+aa 333,719 2 0264 acute
+char180 "
+ps 641,678,95 3 0266 paragraph
+char182 "
+char183 250,336 0 0267 periodcentered
+ac 333,0,218 1 0270 cedilla
+char184 "
+Om 488,678 2 0272 ordmasculine
+char186 "
+fc 389,438 0 0273 guilsinglright
+r? 444,471,230 1 0277 questiondown
+char191 "
+`A 778,943,5 2 0300 Agrave
+char192 "
+'A 778,943,5 2 0301 Aacute
+char193 "
+^A 778,933,5 2 0302 Acircumflex
+char194 "
+~A 778,902,5 2 0303 Atilde
+char195 "
+:A 778,902,5 2 0304 Adieresis
+char196 "
+oA 778,966,5 2 0305 Aring
+char197 "
+AE 1000,683,5 2 0306 AE
+char198 "
+,C 722,695,218 3 0307 Ccedilla
+char199 "
+`E 611,943,5 2 0310 Egrave
+char200 "
+'E 611,943,5 2 0311 Eacute
+char201 "
+^E 611,933,5 2 0312 Ecircumflex
+char202 "
+:E 611,902,5 2 0313 Edieresis
+char203 "
+`I 389,943,5 2 0314 Igrave
+char204 "
+'I 389,943,5 2 0315 Iacute
+char205 "
+^I 389,933,5 2 0316 Icircumflex
+char206 "
+:I 389,902,5 2 0317 Idieresis
+char207 "
+~N 833,902,16 2 0321 Ntilde
+char209 "
+`O 833,943,17 2 0322 Ograve
+char210 "
+'O 833,943,17 2 0323 Oacute
+char211 "
+^O 833,933,17 2 0324 Ocircumflex
+char212 "
+~O 833,902,17 2 0325 Otilde
+char213 "
+:O 833,902,17 2 0326 Odieresis
+char214 "
+/O 833,698,20 2 0330 Oslash
+char216 "
+`U 778,943,17 2 0331 Ugrave
+char217 "
+'U 778,943,17 2 0332 Uacute
+char218 "
+^U 778,933,17 2 0333 Ucircumflex
+char219 "
+:U 778,902,17 2 0334 Udieresis
+char220 "
+ss 611,720,17 2 0337 germandbls
+char223 "
+`a 500,719,15 2 0340 agrave
+char224 "
+'a 500,719,15 2 0341 aacute
+char225 "
+^a 500,709,15 2 0342 acircumflex
+char226 "
+~a 500,678,15 2 0343 atilde
+char227 "
+:a 500,678,15 2 0344 adieresis
+char228 "
+oa 500,742,15 2 0345 aring
+char229 "
+ae 778,471,17 0 0346 ae
+char230 "
+,c 444,471,218 1 0347 ccedilla
+char231 "
+`e 500,719,17 2 0350 egrave
+char232 "
+'e 500,719,17 2 0351 eacute
+char233 "
+^e 500,709,17 2 0352 ecircumflex
+char234 "
+:e 500,678,17 2 0353 edieresis
+char235 "
+`i 333,719,5 2 0354 igrave
+char236 "
+'i 333,719,5 2 0355 iacute
+char237 "
+^i 333,709,5 2 0356 icircumflex
+char238 "
+:i 333,678,5 2 0357 idieresis
+char239 "
+~n 611,678,5 2 0361 ntilde
+char241 "
+`o 556,719,17 2 0362 ograve
+char242 "
+'o 556,719,17 2 0363 oacute
+char243 "
+^o 556,709,17 2 0364 ocircumflex
+char244 "
+~o 556,678,17 2 0365 otilde
+char245 "
+:o 556,678,17 2 0366 odieresis
+char246 "
+/o 556,471,18 0 0370 oslash
+char248 "
+`u 611,719,17 2 0371 ugrave
+char249 "
+'u 611,719,17 2 0372 uacute
+char250 "
+^u 611,709,17 2 0373 ucircumflex
+char251 "
+:u 611,678,17 2 0374 udieresis
+char252 "
+:y 556,678,263 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/PBI b/ps/devps/PBI
new file mode 100644
index 000000000..00248e731
--- /dev/null
+++ b/ps/devps/PBI
@@ -0,0 +1,418 @@
+name PBI
+internalname Palatino-BoldItalic
+slant 10
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -55
+A w -37
+A v -55
+A ' -55
+A Y -74
+A W -74
+A V -74
+A T -55
+F . -111
+F , -111
+F A -74
+L y -37
+L ' -55
+L Y -74
+L W -74
+L V -74
+L T -74
+P . -129
+P , -129
+P A -92
+R y -20
+R Y -37
+R W -55
+R V -55
+R T -37
+T y -80
+T w -50
+T u -92
+T ; -55
+T s -92
+T r -92
+T . -55
+T o -111
+T i -74
+T - -92
+T hy -92
+T char173 -92
+T e -111
+T , -55
+T : -55
+T c -92
+T a -111
+T O -18
+T A -55
+V y -50
+V u -50
+V ; -37
+V r -74
+V . -111
+V o -74
+V i -50
+V - -37
+V hy -37
+V char173 -37
+V e -74
+V , -111
+V : -37
+V a -92
+V A -74
+W y -30
+W u -30
+W ; -18
+W r -30
+W . -55
+W o -55
+W i -30
+W e -55
+W , -55
+W : -28
+W a -74
+W A -74
+Y v -30
+Y u -50
+Y ; -55
+Y q -92
+Y . -55
+Y p -74
+Y o -111
+Y i -54
+Y - -55
+Y hy -55
+Y char173 -55
+Y e -92
+Y , -55
+Y : -55
+Y a -111
+Y A -55
+f ' 37
+f f -37
+1 1 -55
+` ` -55
+` oq -55
+oq ` -55
+oq oq -55
+' t -18
+' s -37
+' ' -55
+r ' 55
+r q -18
+r . -55
+r o -18
+r h -18
+r g -18
+r e -18
+r , -55
+r c -18
+v . -55
+v , -55
+w . -55
+w , -55
+y . -37
+y , -37
+charset
+ha 606,678,0,0,-13 2 0000 asciicircum
+ti 606,346,0,0,-1 0 0001 asciitilde
+vS 556,919,17,42,8,42 2 0002 Scaron
+vZ 667,919,5,57,49,57 2 0003 Zcaron
+vs 444,705,17,98,25,66 2 0004 scaron
+vz 500,705,17,70,-8,66 2 0005 zcaron
+:Y 611,884,5,114,-4,66 2 0006 Ydieresis
+tm 1000,683,0,32,8,32 2 0007 trademark
+space 250 0 0040
+! 333,695,17,39,2,39 2 0041 exclam
+" 500,693,0,47,-40,47 2 0042 quotedbl
+# 606,671,0,0,-7 2 0043 numbersign
+sh "
+$ 500,737,108,27,29,27 3 0044 dollar
+Do "
+% 889,683,10,0,-32 2 0045 percent
+& 833,695,17,28,-24,28 2 0046 ampersand
+' 278,695,0,72,-26,66 2 0047 quoteright
+( 333,726,126,106,-29,66 3 0050 parenleft
+) 333,726,126,0,84 3 0051 parenright
+* 444,685,0,63,-59,63 2 0052 asterisk
++ 606,501,5 0 0053 plus
+, 250,147,164,8,83,8 1 0054 comma
+- 389,300,0,22,13,22 0 0055 hyphen
+hy "
+char173 "
+. 250,135,17,0,-4 0 0056 period
+/ 315,726,0,58,54,58 2 0057 slash
+sl "
+0 500,683,17,37,8,37 2 0060 zero
+1 500,683,5,0,9 2 0061 one
+2 500,683,5,4,49,4 2 0062 two
+3 500,683,17,0,42 2 0063 three
+4 500,685,5,37,47,37 2 0064 four
+5 500,671,17,25,43,25 2 0065 five
+6 500,683,17,38,11,38 2 0066 six
+7 500,671,5,94,-19,66 2 0067 seven
+8 500,683,17,34,24,34 2 0070 eight
+9 500,683,17,41,23,41 2 0071 nine
+: 250,452,5,84,-19,66 0 0072 colon
+; 250,452,152,65,62,65 1 0073 semicolon
+< 606,517,21,2,1,2 0 0074 less
+= 606,390,0,0,-1 0 0075 equal
+> 606,517,21,1,2,1 0 0076 greater
+? 444,695,17,56,-41,56 2 0077 question
+@ 833,695,17,0,8 2 0100 at
+at "
+A 722,683,5,13,85,13 2 0101 A
+B 667,683,5,12,42,12 2 0102 B
+C 685,695,17,63,-22,63 2 0103 C
+D 778,683,5,19,50,19 2 0104 D
+E 611,683,5,45,43,45 2 0105 E
+F 556,683,5,87,51,66 2 0106 F
+G 778,695,17,22,-23,22 2 0107 G
+H 778,683,5,90,62,66 2 0110 H
+I 389,683,5,86,50,66 2 0111 I
+J 389,683,207,96,63,66 3 0112 J
+K 722,683,5,86,47,66 2 0113 K
+L 611,683,5,21,27,21 2 0114 L
+M 944,683,17,91,73,66 2 0115 M
+N 778,683,5,101,52,66 2 0116 N
+O 833,695,17,8,-23,8 2 0117 O
+P 667,683,5,56,41,56 2 0120 P
+Q 833,695,222,9,-24,9 3 0121 Q
+R 722,683,5,28,43,28 2 0122 R
+S 556,695,17,24,8,24 2 0123 S
+T 611,683,5,113,-7,66 2 0124 T
+U 778,683,17,94,-30,66 2 0125 U
+V 667,683,5,128,-17,66 2 0126 V
+W 1000,689,5,123,-18,66 2 0127 W
+X 722,683,5,100,59,66 2 0130 X
+Y 611,692,5,114,-4,66 2 0131 Y
+Z 667,683,5,57,49,57 2 0132 Z
+[ 333,726,102,111,8,66 3 0133 bracketleft
+lB "
+\ 606,726,0,0,-17 2 0134 backslash
+rs "
+] 333,726,102,33,84,33 3 0135 bracketright
+rB "
+a^ 333,705,0,109,-15,66 2 0136 circumflex
+^ "
+_ 500,0,169,50,50,50 1 0137 underscore
+` 278,695,0,63,-15,63 2 0140 quoteleft
+oq "
+a 556,470,17,15,3,15 0 0141 a
+b 537,726,17,7,6,7 2 0142 b
+c 444,469,17,48,12,48 0 0143 c
+d 556,726,17,47,12,47 2 0144 d
+e 444,469,17,27,19,27 0 0145 e
+f 333,726,271,128,106,66 3 0146 f
+g 500,469,271,79,100,66 1 0147 g
+h 556,726,17,16,26,16 2 0150 h
+i 333,695,17,32,22,32 2 0151 i
+j 333,695,271,38,117,38 3 0152 j
+k 556,726,17,23,12,23 2 0153 k
+l 333,726,17,36,-17,36 2 0154 l
+m 833,469,13,21,31,21 0 0155 m
+n 556,469,17,22,33,22 0 0156 n
+o 556,469,17,0,2 0 0157 o
+p 556,469,271,11,71,11 1 0160 p
+q 537,469,271,26,18,26 1 0161 q
+r 389,468,17,75,27,66 0 0162 r
+s 444,469,17,12,25,12 0 0163 s
+t 389,636,16,73,5,66 2 0164 t
+u 556,467,18,15,28,15 0 0165 u
+v 556,469,17,7,31,7 0 0166 v
+w 833,469,17,9,35,9 0 0167 w
+x 500,469,17,50,58,50 0 0170 x
+y 556,464,271,35,36,35 1 0171 y
+z 500,469,17,47,-8,47 0 0172 z
+lC 333,726,101,42,41,42 3 0173 braceleft
+{ "
+ba 606,726,0,0,-208 2 0174 bar
+| "
+rC 333,726,101,41,41,41 3 0175 braceright
+} "
+a~ 333,670,0,149,-23,66 2 0176 tilde
+~ "
+--- 250,145,144,38,37,38 1 0200 quotesinglbase
+Fo 500,446,0,8,15,8 0 0201 guillemotleft
+char171 "
+Fc 500,446,0,7,15,7 0 0202 guillemotright
+char187 "
+bu 606,508,0,0,-84 0 0203 bullet
+--- 500,694,250,37,35,37 3 0204 florin
+f/ 167,685,17,216,216,66 2 0205 fraction
+%0 1000,678,15,35,33,35 2 0206 perthousand
+dg 556,683,5,0,-17 2 0207 dagger
+dd 556,693,153,31,21,31 3 0210 daggerdbl
+en 500,282,0,57,57,57 0 0211 endash
+em 1000,282,0,65,66,65 0 0212 emdash
+fi 611,726,270,41,109,41 3 0214 fi
+fl 611,726,271,51,106,51 3 0215 fl
+.i 333,468,15,13,22,13 0 0220 dotlessi
+ga 333,712,0,39,-60,39 2 0222 grave
+a" 333,712,0,120,-114,66 2 0223 hungarumlaut
+a. 333,670,0,0,-52 2 0224 dotaccent
+ab 333,693,0,129,-46,66 2 0225 breve
+ah 333,705,0,153,-54,66 2 0226 caron
+ao 556,738,0,0,-217 2 0227 ring
+ho 333,0,206,0,18 1 0230 ogonek
+lq 500,695,0,60,-15,60 2 0231 quotedblleft
+rq 500,695,0,67,-25,66 2 0232 quotedblright
+oe 778,469,17,27,4,27 0 0233 oe
+/l 333,726,17,101,51,66 2 0234 lslash
+--- 500,145,144,0,70 1 0235 quotedblbase
+OE 944,695,17,69,11,66 2 0236 OE
+/L 611,683,5,21,27,21 2 0237 Lslash
+r! 333,469,235,0,48 1 0241 exclamdown
+char161 "
+ct 500,546,105,13,-9,13 3 0242 cent
+char162 "
+Po 500,683,5,64,5,64 2 0243 sterling
+char163 "
+Cs 606,588 2 0244 currency
+char164 "
+Ye 500,695,5,98,61,66 2 0245 yen
+char165 "
+sc 556,695,151,0,3 3 0247 section
+char167 "
+ad 333,670,0,139,-30,66 2 0250 dieresis
+char168 "
+co 747,695,17,73,-26,66 2 0251 copyright
+char169 "
+Of 333,726,0,72,6,66 2 0252 ordfeminine
+char170 "
+fo 333,446,0,9,-10,9 0 0253 guilsinglleft
+no 606,395,0,0,-1 0 0254 logicalnot
+char172 "
+\- 606,292,0,0,-1 0 0255 minus
+rg 747,695,17,73,-27,66 2 0256 registered
+char174 "
+a- 333,638,0,135,-26,66 2 0257 macron
+char175 "
+aa 333,712,0,120,-114,66 2 0264 acute
+char180 "
+ps 556,695,151,76,50,66 3 0266 paragraph
+char182 "
+char183 250,324,0,26,-37,26 0 0267 periodcentered
+ac 333,5,218,0,50 1 0270 cedilla
+char184 "
+Om 333,726,0,63,2,63 2 0272 ordmasculine
+char186 "
+fc 333,446,0,0,15 0 0273 guilsinglright
+r? 444,469,235,0,58 1 0277 questiondown
+char191 "
+`A 722,926,5,13,85,13 2 0300 Agrave
+char192 "
+'A 722,926,5,13,85,13 2 0301 Aacute
+char193 "
+^A 722,919,5,13,85,13 2 0302 Acircumflex
+char194 "
+~A 722,884,5,13,85,13 2 0303 Atilde
+char195 "
+:A 722,884,5,13,85,13 2 0304 Adieresis
+char196 "
+oA 722,952,5,13,85,13 2 0305 Aring
+char197 "
+AE 944,683,5,39,79,39 2 0306 AE
+char198 "
+,C 685,695,218,63,-22,63 3 0307 Ccedilla
+char199 "
+`E 611,926,5,45,43,45 2 0310 Egrave
+char200 "
+'E 611,926,5,45,43,45 2 0311 Eacute
+char201 "
+^E 611,919,5,45,43,45 2 0312 Ecircumflex
+char202 "
+:E 611,884,5,45,43,45 2 0313 Edieresis
+char203 "
+`I 389,926,5,86,50,66 2 0314 Igrave
+char204 "
+'I 389,926,5,92,50,66 2 0315 Iacute
+char205 "
+^I 389,919,5,86,50,66 2 0316 Icircumflex
+char206 "
+:I 389,884,5,111,50,66 2 0317 Idieresis
+char207 "
+~N 778,884,5,101,52,66 2 0321 Ntilde
+char209 "
+`O 833,926,17,8,-23,8 2 0322 Ograve
+char210 "
+'O 833,926,17,8,-23,8 2 0323 Oacute
+char211 "
+^O 833,919,17,8,-23,8 2 0324 Ocircumflex
+char212 "
+~O 833,884,17,8,-23,8 2 0325 Otilde
+char213 "
+:O 833,884,17,8,-23,8 2 0326 Odieresis
+char214 "
+/O 833,732,53,12,-4,12 2 0330 Oslash
+char216 "
+`U 778,926,17,94,-30,66 2 0331 Ugrave
+char217 "
+'U 778,926,17,94,-30,66 2 0332 Uacute
+char218 "
+^U 778,919,17,94,-30,66 2 0333 Ucircumflex
+char219 "
+:U 778,884,17,94,-30,66 2 0334 Udieresis
+char220 "
+ss 556,726,281,29,108,29 3 0337 germandbls
+char223 "
+`a 556,712,17,15,3,15 2 0340 agrave
+char224 "
+'a 556,712,17,15,3,15 2 0341 aacute
+char225 "
+^a 556,705,17,15,3,15 2 0342 acircumflex
+char226 "
+~a 556,670,17,38,3,38 2 0343 atilde
+char227 "
+:a 556,670,17,28,3,28 2 0344 adieresis
+char228 "
+oa 556,738,17,15,3,15 2 0345 aring
+char229 "
+ae 738,469,17,27,3,27 0 0346 ae
+char230 "
+,c 444,469,218,48,12,48 1 0347 ccedilla
+char231 "
+`e 444,712,17,27,19,27 2 0350 egrave
+char232 "
+'e 444,712,17,65,19,65 2 0351 eacute
+char233 "
+^e 444,705,17,54,19,54 2 0352 ecircumflex
+char234 "
+:e 444,670,17,84,19,66 2 0353 edieresis
+char235 "
+`i 333,712,15,39,22,39 2 0354 igrave
+char236 "
+'i 333,712,15,120,22,66 2 0355 iacute
+char237 "
+^i 333,705,15,109,22,66 2 0356 icircumflex
+char238 "
+:i 333,670,15,139,22,66 2 0357 idieresis
+char239 "
+~n 556,670,17,38,33,38 2 0361 ntilde
+char241 "
+`o 556,712,17,0,2 2 0362 ograve
+char242 "
+'o 556,712,17,9,2,9 2 0363 oacute
+char243 "
+^o 556,705,17,0,2 2 0364 ocircumflex
+char244 "
+~o 556,670,17,38,2,38 2 0365 otilde
+char245 "
+:o 556,670,17,28,2,28 2 0366 odieresis
+char246 "
+/o 556,506,53,16,36,16 0 0370 oslash
+char248 "
+`u 556,712,18,15,28,15 2 0371 ugrave
+char249 "
+'u 556,712,18,15,28,15 2 0372 uacute
+char250 "
+^u 556,705,18,15,28,15 2 0373 ucircumflex
+char251 "
+:u 556,670,18,28,28,28 2 0374 udieresis
+char252 "
+:y 556,670,271,35,36,35 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/PI b/ps/devps/PI
new file mode 100644
index 000000000..12be8d64a
--- /dev/null
+++ b/ps/devps/PI
@@ -0,0 +1,420 @@
+name PI
+internalname Palatino-Italic
+slant 10
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -55
+A w -37
+A v -37
+A ' -55
+A Y -55
+A W -55
+A V -74
+A T -55
+F . -111
+F , -111
+F A -111
+L y -37
+L ' -37
+L Y -74
+L W -74
+L V -74
+L T -74
+P . -129
+P , -129
+P A -129
+R y -37
+R Y -55
+R W -55
+R V -74
+R T -55
+T y -92
+T w -92
+T u -111
+T ; -74
+T s -111
+T r -111
+T . -74
+T o -111
+T i -55
+T - -55
+T hy -55
+T char173 -55
+T e -111
+T , -74
+T : -74
+T c -111
+T a -111
+T O -18
+T A -92
+V y -74
+V u -74
+V ; -37
+V r -92
+V . -129
+V o -74
+V i -74
+V - -55
+V hy -55
+V char173 -55
+V e -92
+V , -129
+V : -37
+V a -74
+V A -210
+W y -20
+W u -20
+W ; -18
+W r -20
+W . -55
+W o -20
+W i -20
+W - -18
+W hy -18
+W char173 -18
+W e -20
+W , -55
+W : -18
+W a -20
+W A -92
+Y v -74
+Y u -92
+Y ; -74
+Y q -92
+Y . -92
+Y p -74
+Y o -111
+Y i -55
+Y - -74
+Y hy -74
+Y char173 -74
+Y e -111
+Y , -92
+Y : -74
+Y a -92
+Y A -92
+f ' 55
+1 1 -55
+` ` -74
+` oq -74
+oq ` -74
+oq oq -74
+' t -37
+' s -55
+' ' -74
+r ' 37
+r q -18
+r . -74
+r o -18
+r h -18
+r g -18
+r e -18
+r , -74
+r c -18
+v . -55
+v , -55
+w . -55
+w , -55
+y . -37
+y , -37
+charset
+ha 606,693,0,0,-1 2 0000 asciicircum
+ti 606,339,0,0,-1 0 0001 asciitilde
+vS 556,946,18,27,1,27 2 0002 Scaron
+vZ 667,946,5,20,30,20 2 0003 Zcaron
+vs 389,733,11,110,41,68 2 0004 scaron
+vz 444,733,5,83,51,68 2 0005 zcaron
+:Y 667,858,5,58,-2,58 2 0006 Ydieresis
+tm 1000,693,0,0,-2 2 0007 trademark
+space 250 0 0040
+! 333,733,5,9,-26,9 2 0041 exclam
+" 500,706,0,15,-120,15 2 0042 quotedbl
+# 606,693,0,0,-7 2 0043 numbersign
+sh "
+$ 500,737,113,3,35,3 3 0044 dollar
+Do "
+% 889,693,16,0,-29 2 0045 percent
+& 778,693,18,38,3,38 2 0046 ampersand
+' 278,733,0,16,-9,16 2 0047 quoteright
+( 333,733,103,47,-4,47 3 0050 parenleft
+) 333,733,101,0,44 3 0051 parenright
+* 389,705,0,61,-26,61 2 0052 asterisk
++ 606,504,0,0,-2 0 0053 plus
+, 250,123,143,23,22,23 1 0054 comma
+- 333,281,0,21,31,21 0 0055 hyphen
+hy "
+char173 "
+. 250,112,5,0,-23 0 0056 period
+/ 296,733,181,56,49,56 3 0057 slash
+sl "
+0 500,699,11,18,26,18 2 0060 zero
+1 500,699,5,0,8 2 0061 one
+2 500,699,5,0,50 2 0062 two
+3 500,699,11,0,50 2 0063 three
+4 500,699,5,16,51,16 2 0064 four
+5 500,693,11,29,48,29 2 0065 five
+6 500,702,11,7,13,7 2 0066 six
+7 500,693,5,40,9,40 2 0067 seven
+8 500,699,11,7,26,7 2 0070 eight
+9 500,699,11,6,30,6 2 0071 nine
+: 250,460,3,7,6,7 0 0072 colon
+; 250,459,143,39,39,39 1 0073 semicolon
+< 606,514,8,2,1,2 0 0074 less
+= 606,382,0,0,-1 0 0075 equal
+> 606,514,8,1,2,1 0 0076 greater
+? 500,705,6,0,-57 2 0077 question
+@ 747,705,18,21,23,21 2 0100 at
+at "
+A 722,705,5,20,54,20 2 0101 A
+B 611,693,5,2,19,2 2 0102 B
+C 667,705,18,34,5,34 2 0103 C
+D 778,693,5,16,19,16 2 0104 D
+E 611,693,5,11,14,11 2 0105 E
+F 556,693,5,47,60,47 2 0106 F
+G 722,705,18,22,0,22 2 0107 G
+H 778,693,5,82,64,68 2 0110 H
+I 333,693,5,84,60,68 2 0111 I
+J 333,693,208,80,85,68 3 0112 J
+K 667,693,5,66,41,66 2 0113 K
+L 556,693,5,16,30,16 2 0114 L
+M 944,693,19,46,71,46 2 0115 M
+N 778,693,11,86,48,68 2 0116 N
+O 778,705,18,23,-6,23 2 0117 O
+P 611,693,5,33,47,33 2 0120 P
+Q 778,705,201,23,-6,23 3 0121 Q
+R 667,693,5,22,41,22 2 0122 R
+S 556,705,18,7,1,7 2 0123 S
+T 611,693,5,76,-3,68 2 0124 T
+U 778,693,18,86,-34,68 2 0125 U
+V 722,693,8,70,-13,68 2 0126 V
+W 944,700,8,126,-61,68 2 0127 W
+X 722,693,5,62,30,62 2 0130 X
+Y 667,705,5,58,-2,58 2 0131 Y
+Z 667,693,5,20,30,20 2 0132 Z
+[ 333,733,101,43,33,43 3 0133 bracketleft
+lB "
+\ 606,693,0,0,-31 2 0134 backslash
+rs "
+] 333,733,102,34,43,34 3 0135 bracketright
+rB "
+a^ 333,699,0,91,-30,68 2 0136 circumflex
+^ "
+_ 500,0,163,50,50,50 1 0137 underscore
+` 278,733,0,46,-44,46 2 0140 quoteleft
+oq "
+a 444,481,11,8,49,8 0 0141 a
+b 463,733,11,20,13,20 2 0142 b
+c 407,480,11,30,25,30 0 0143 c
+d 500,733,11,26,36,26 2 0144 d
+e 389,480,11,35,35,35 0 0145 e
+f 278,729,276,132,121,68 3 0146 f
+g 500,480,276,40,77,40 1 0147 g
+h 500,733,11,15,40,15 2 0150 h
+i 278,712,10,36,15,36 2 0151 i
+j 278,712,276,41,117,41 3 0152 j
+k 444,733,11,55,41,55 2 0153 k
+l 278,733,10,23,12,23 2 0154 l
+m 778,483,11,7,30,7 0 0155 m
+n 556,483,11,2,22,2 0 0156 n
+o 444,480,11,17,33,17 0 0157 o
+p 500,483,276,25,47,25 1 0160 p
+q 463,480,276,12,33,12 1 0161 q
+r 389,483,11,45,24,45 0 0162 r
+s 389,480,11,6,41,6 0 0163 s
+t 333,646,10,27,9,27 2 0164 t
+u 556,483,11,1,18,1 0 0165 u
+v 500,480,11,27,34,27 0 0166 v
+w 722,480,11,27,33,27 0 0167 w
+x 500,480,11,34,41,34 0 0170 x
+y 500,484,276,40,58,40 1 0171 y
+z 444,480,5,22,51,22 0 0172 z
+lC 333,713,116,36,35,36 3 0173 braceleft
+{ "
+ba 606,733,0,0,-220 2 0174 bar
+| "
+rC 333,713,116,34,35,34 3 0175 braceright
+} "
+a~ 333,661,0,137,-43,68 2 0176 tilde
+~ "
+--- 278,120,122,3,3,3 1 0200 quotesinglbase
+Fo 500,438,0,0,-4 0 0201 guillemotleft
+char171 "
+Fc 500,438,0,0,-4 0 0202 guillemotright
+char187 "
+bu 500,530,0,30,-76,30 0 0203 bullet
+--- 500,705,276,36,37,36 3 0204 florin
+f/ 167,693,0,216,216,68 2 0205 fraction
+%0 1000,714,41,42,44,42 2 0206 perthousand
+dg 500,693,0,9,12,9 2 0207 dagger
+dd 500,693,162,44,41,44 3 0210 daggerdbl
+en 500,278,0,59,58,59 0 0211 endash
+em 1000,278,0,66,65,66 0 0212 emdash
+fi 528,733,276,33,121,33 3 0214 fi
+fl 545,733,277,21,121,21 3 0215 fl
+.i 278,483,10,9,15,9 0 0220 dotlessi
+ga 333,704,0,23,-66,23 2 0222 grave
+a" 333,741,0,180,-133,68 2 0223 hungarumlaut
+a. 333,307,0,0,-59 0 0224 dotaccent
+ab 333,723,0,130,-62,68 2 0225 breve
+ah 333,733,0,138,-66,68 2 0226 caron
+ao 333,733,0,83,-116,68 2 0227 ring
+ho 333,0,207,13,-88,13 1 0230 ogonek
+lq 500,733,0,25,-48,25 2 0231 quotedblleft
+rq 500,733,0,0,-22 2 0232 quotedblright
+oe 669,480,11,35,33,35 0 0233 oe
+/l 278,733,10,63,43,63 2 0234 lslash
+--- 500,120,122,0,-19 1 0235 quotedblbase
+OE 1028,705,18,11,-6,11 2 0236 OE
+/L 556,693,5,16,30,16 2 0237 Lslash
+r! 333,467,276,0,15 1 0241 exclamdown
+char161 "
+ct 500,551,96,0,-26 3 0242 cent
+char162 "
+Po 500,705,18,31,43,31 2 0243 sterling
+char163 "
+Cs 606,597 2 0244 currency
+char164 "
+Ye 667,699,5,57,-1,57 2 0245 yen
+char165 "
+sc 500,705,221,13,36,13 3 0247 section
+char167 "
+ad 333,645,0,119,-52,68 2 0250 dieresis
+char168 "
+co 747,705,18,38,38,38 2 0251 copyright
+char169 "
+Of 333,705,0,97,-31,68 2 0252 ordfeminine
+char170 "
+fo 333,438,0,0,-7 0 0253 guilsinglleft
+no 606,382,0,0,-1 0 0254 logicalnot
+char172 "
+\- 606,285,0,0,-1 0 0255 minus
+rg 747,705,18,38,38,38 2 0256 registered
+char174 "
+a- 333,622,0,123,-44,68 2 0257 macron
+char175 "
+aa 333,700,0,115,-125,68 2 0264 acute
+char180 "
+ps 500,693,221,120,7,68 3 0266 paragraph
+char182 "
+char183 250,312,0,0,-23 0 0267 periodcentered
+ac 333,9,207,0,-1 1 0270 cedilla
+char184 "
+Om 333,705,0,79,-33,68 2 0272 ordmasculine
+char186 "
+fc 333,438,0,0,-8 0 0273 guilsinglright
+r? 500,437,276,0,-23 1 0277 questiondown
+char191 "
+`A 722,917,5,20,54,20 2 0300 Agrave
+char192 "
+'A 722,913,5,20,54,20 2 0301 Aacute
+char193 "
+^A 722,912,5,20,54,20 2 0302 Acircumflex
+char194 "
+~A 722,874,5,20,54,20 2 0303 Atilde
+char195 "
+:A 722,858,5,20,54,20 2 0304 Adieresis
+char196 "
+oA 722,946,5,20,54,20 2 0305 Aring
+char197 "
+AE 941,693,5,11,54,11 2 0306 AE
+char198 "
+,C 667,705,207,34,5,34 3 0307 Ccedilla
+char199 "
+`E 611,917,5,11,14,11 2 0310 Egrave
+char200 "
+'E 611,913,5,11,14,11 2 0311 Eacute
+char201 "
+^E 611,912,5,11,14,11 2 0312 Ecircumflex
+char202 "
+:E 611,858,5,11,14,11 2 0313 Edieresis
+char203 "
+`I 333,917,5,84,60,68 2 0314 Igrave
+char204 "
+'I 333,913,5,115,60,68 2 0315 Iacute
+char205 "
+^I 333,912,5,91,60,68 2 0316 Icircumflex
+char206 "
+:I 333,858,5,119,60,68 2 0317 Idieresis
+char207 "
+~N 778,874,11,86,48,68 2 0321 Ntilde
+char209 "
+`O 778,917,18,23,-6,23 2 0322 Ograve
+char210 "
+'O 778,913,18,23,-6,23 2 0323 Oacute
+char211 "
+^O 778,912,18,23,-6,23 2 0324 Ocircumflex
+char212 "
+~O 778,874,18,23,-6,23 2 0325 Otilde
+char213 "
+:O 778,858,18,23,-6,23 2 0326 Odieresis
+char214 "
+/O 778,721,39,37,15,37 2 0330 Oslash
+char216 "
+`U 778,917,18,86,-34,68 2 0331 Ugrave
+char217 "
+'U 778,913,18,86,-34,68 2 0332 Uacute
+char218 "
+^U 778,912,18,86,-34,68 2 0333 Ucircumflex
+char219 "
+:U 778,858,18,86,-34,68 2 0334 Udieresis
+char220 "
+ss 500,733,274,30,130,30 3 0337 germandbls
+char223 "
+`a 444,704,11,8,49,8 2 0340 agrave
+char224 "
+'a 444,700,11,60,49,60 2 0341 aacute
+char225 "
+^a 444,699,11,36,49,36 2 0342 acircumflex
+char226 "
+~a 444,661,11,82,49,68 2 0343 atilde
+char227 "
+:a 444,645,11,64,49,64 2 0344 adieresis
+char228 "
+oa 444,733,11,28,49,28 2 0345 aring
+char229 "
+ae 638,480,11,35,49,35 0 0346 ae
+char230 "
+,c 407,480,207,30,25,30 1 0347 ccedilla
+char231 "
+`e 389,704,11,35,35,35 2 0350 egrave
+char232 "
+'e 389,700,11,87,35,68 2 0351 eacute
+char233 "
+^e 389,699,11,63,35,63 2 0352 ecircumflex
+char234 "
+:e 389,645,11,91,35,68 2 0353 edieresis
+char235 "
+`i 278,704,10,51,15,51 2 0354 igrave
+char236 "
+'i 278,700,10,143,15,68 2 0355 iacute
+char237 "
+^i 278,699,10,119,15,68 2 0356 icircumflex
+char238 "
+:i 278,645,10,147,15,68 2 0357 idieresis
+char239 "
+~n 556,661,11,26,22,26 2 0361 ntilde
+char241 "
+`o 444,704,11,17,33,17 2 0362 ograve
+char242 "
+'o 444,700,11,60,33,60 2 0363 oacute
+char243 "
+^o 444,699,11,36,33,36 2 0364 ocircumflex
+char244 "
+~o 444,661,11,82,33,68 2 0365 otilde
+char245 "
+:o 444,645,11,64,33,64 2 0366 odieresis
+char246 "
+/o 444,506,29,63,68,63 0 0370 oslash
+char248 "
+`u 556,704,11,1,18,1 2 0371 ugrave
+char249 "
+'u 556,700,11,4,18,4 2 0372 uacute
+char250 "
+^u 556,699,11,1,18,1 2 0373 ucircumflex
+char251 "
+:u 556,645,11,8,18,8 2 0374 udieresis
+char252 "
+:y 500,645,276,40,58,40 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/PR b/ps/devps/PR
new file mode 100644
index 000000000..f61a59ec6
--- /dev/null
+++ b/ps/devps/PR
@@ -0,0 +1,423 @@
+name PR
+internalname Palatino-Roman
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -74
+A w -74
+A v -92
+A ' -74
+A Y -111
+A W -74
+A V -111
+A T -74
+F . -92
+F , -92
+F A -74
+L y -55
+L ' -74
+L Y -92
+L W -74
+L V -92
+L T -74
+P . -129
+P , -129
+P A -92
+R y -37
+R Y -37
+R W -37
+R V -55
+R T -37
+T y -90
+T w -90
+T u -90
+T ; -55
+T s -90
+T r -90
+T . -74
+T o -92
+T i -55
+T - -55
+T hy -55
+T char173 -55
+T e -92
+T , -74
+T : -55
+T c -111
+T a -92
+T O -18
+T A -74
+V y -92
+V u -92
+V ; -55
+V r -92
+V . -129
+V o -111
+V i -55
+V - -74
+V hy -74
+V char173 -74
+V e -111
+V , -129
+V : -55
+V a -92
+V A -111
+W y -50
+W u -50
+W ; -18
+W r -74
+W . -92
+W o -92
+W i -55
+W - -55
+W hy -55
+W char173 -55
+W e -92
+W , -92
+W : -18
+W a -92
+W A -92
+Y v -90
+Y u -90
+Y ; -74
+Y q -90
+Y . -111
+Y p -111
+Y o -92
+Y i -55
+Y - -92
+Y hy -92
+Y char173 -92
+Y e -92
+Y , -111
+Y : -74
+Y a -92
+Y A -92
+f ' 55
+f f -18
+1 1 -55
+` ` -37
+` oq -37
+oq ` -37
+oq oq -37
+' ' -37
+r u -8
+r ' 74
+r q -18
+r . -74
+r o -18
+r - -18
+r hy -18
+r char173 -18
+r h -18
+r g -18
+r e -18
+r d -18
+r , -74
+r c -18
+v . -111
+v , -111
+w . -92
+w , -92
+y . -111
+y , -111
+charset
+ha 606,731 2 0000 asciicircum
+ti 606,347 0 0001 asciitilde
+vS 525,915,20 2 0002 Scaron
+vZ 667,915,5 2 0003 Zcaron
+vs 424,689,20 2 0004 scaron
+vz 500,689,5 2 0005 zcaron
+:Y 667,902,5 2 0006 Ydieresis
+tm 979,689 2 0007 trademark
+space 250 0 0040
+! 278,694,5 2 0041 exclam
+" 371,694 2 0042 quotedbl
+# 606,684 2 0043 numbersign
+sh "
+$ 500,731,116 3 0044 dollar
+Do "
+% 840,689,20 2 0045 percent
+& 778,689,20 2 0046 ampersand
+' 278,694 2 0047 quoteright
+( 333,731,116 3 0050 parenleft
+) 333,731,116 3 0051 parenright
+* 389,689 2 0052 asterisk
++ 606,512 0 0053 plus
+, 250,123,155 1 0054 comma
+- 333,287 0 0055 hyphen
+hy "
+char173 "
+. 250,111,5 0 0056 period
+/ 606,731,116 3 0057 slash
+sl "
+0 500,689,20 2 0060 zero
+1 500,684,5 2 0061 one
+2 500,689,5 2 0062 two
+3 500,689,16 2 0063 three
+4 500,689,5 2 0064 four
+5 500,689,20 2 0065 five
+6 500,689,20 2 0066 six
+7 500,689,5 2 0067 seven
+8 500,689,20 2 0070 eight
+9 500,689,20 2 0071 nine
+: 250,457,5 0 0072 colon
+; 250,457,153 1 0073 semicolon
+< 606,522 2 0074 less
+= 606,390 0 0075 equal
+> 606,522 2 0076 greater
+? 444,694,5 2 0077 question
+@ 747,694,20 2 0100 at
+at "
+A 778,700,5 2 0101 A
+B 611,694,6 2 0102 B
+C 709,709,20 2 0103 C
+D 774,694,6 2 0104 D
+E 611,694,5 2 0105 E
+F 556,694,5 2 0106 F
+G 763,709,20 2 0107 G
+H 832,694,5 2 0110 H
+I 337,694,5 2 0111 I
+J 333,694,194 3 0112 J
+K 726,694,5 2 0113 K
+L 611,694,5 2 0114 L
+M 946,694,13 2 0115 M
+N 831,694,20 2 0116 N
+O 786,709,20 2 0117 O
+P 604,694,5 2 0120 P
+Q 786,709,177 3 0121 Q
+R 668,694,5 2 0122 R
+S 525,709,20 2 0123 S
+T 613,694,5 2 0124 T
+U 778,694,20 2 0125 U
+V 722,694,9 2 0126 V
+W 1000,700,9 2 0127 W
+X 667,694,5 2 0130 X
+Y 667,701,5 2 0131 Y
+Z 667,694,5 2 0132 Z
+[ 333,731,116 3 0133 bracketleft
+lB "
+\ 606,731 2 0134 backslash
+rs "
+] 333,731,116 3 0135 bracketright
+rB "
+a^ 333,689 2 0136 circumflex
+^ "
+_ 500,0,160 1 0137 underscore
+` 278,694 2 0140 quoteleft
+oq "
+a 500,469,20 0 0141 a
+b 553,723,12 2 0142 b
+c 444,469,20 0 0143 c
+d 611,723,12 2 0144 d
+e 479,469,20 0 0145 e
+f 333,723,5 2 0146 f
+g 556,469,283 1 0147 g
+h 582,723,5 2 0150 h
+i 291,687,5 2 0151 i
+j 234,689,283 3 0152 j
+k 556,723,12 2 0153 k
+l 291,723,5 2 0154 l
+m 883,469,5 0 0155 m
+n 582,469,5 0 0156 n
+o 546,469,20 0 0157 o
+p 601,469,283 1 0160 p
+q 560,469,283 1 0161 q
+r 395,469,5 0 0162 r
+s 424,469,20 0 0163 s
+t 326,621,12 2 0164 t
+u 603,469,12 0 0165 u
+v 565,459,7 0 0166 v
+w 834,464,7 0 0167 w
+x 516,468,5 0 0170 x
+y 556,459,283 1 0171 y
+z 500,464,5 0 0172 z
+lC 333,731,116 3 0173 braceleft
+{ "
+ba 606,731 2 0174 bar
+| "
+rC 333,731,116 3 0175 braceright
+} "
+a~ 333,654 2 0176 tilde
+~ "
+--- 278,110,153 1 0200 quotesinglbase
+Fo 500,428 0 0201 guillemotleft
+char171 "
+Fc 500,428 0 0202 guillemotright
+char187 "
+bu 606,516 0 0203 bullet
+--- 500,706,262 3 0204 florin
+f/ 167,692,13 2 0205 fraction
+%0 1144,709,50 2 0206 perthousand
+dg 500,694,5 2 0207 dagger
+dd 500,694,249 3 0210 daggerdbl
+en 500,277 0 0211 endash
+em 1000,277 0 0212 emdash
+fi 605,723,5 2 0214 fi
+fl 608,723,5 2 0215 fl
+.i 287,469,5 0 0220 dotlessi
+ga 333,695 2 0222 grave
+a" 380,700 2 0223 hungarumlaut
+a. 250,639 2 0224 dotaccent
+ab 333,681 2 0225 breve
+ah 333,689 2 0226 caron
+ao 333,720 2 0227 ring
+ho 313,0,155 1 0230 ogonek
+lq 500,694 2 0231 quotedblleft
+rq 500,694 2 0232 quotedblright
+oe 827,469,20 0 0233 oe
+/l 291,723,5 2 0234 lslash
+--- 500,110,153 1 0235 quotedblbase
+OE 998,709,20 2 0236 OE
+/L 611,694,5 2 0237 Lslash
+r! 278,469,225 1 0241 exclamdown
+char161 "
+ct 500,562,101 3 0242 cent
+char162 "
+Po 500,694,13 2 0243 sterling
+char163 "
+Cs 606,595 2 0244 currency
+char164 "
+Ye 500,701,5 2 0245 yen
+char165 "
+sc 500,694,197 3 0247 section
+char167 "
+ad 333,676 2 0250 dieresis
+char168 "
+co 747,694,20 2 0251 copyright
+char169 "
+Of 333,694 2 0252 ordfeminine
+char170 "
+fo 331,428 0 0253 guilsinglleft
+no 606,390 0 0254 logicalnot
+char172 "
+\- 606,293 0 0255 minus
+rg 747,694,20 2 0256 registered
+char174 "
+a- 333,617 2 0257 macron
+char175 "
+aa 333,696 2 0264 acute
+char180 "
+ps 628,689,150 3 0266 paragraph
+char182 "
+char183 250,319 0 0267 periodcentered
+ac 333,9,206 1 0270 cedilla
+char184 "
+Om 333,694 2 0272 ordmasculine
+char186 "
+fc 331,428 0 0273 guilsinglright
+r? 444,469,230 1 0277 questiondown
+char191 "
+`A 778,921,5 2 0300 Agrave
+char192 "
+'A 778,922,5 2 0301 Aacute
+char193 "
+^A 778,915,5 2 0302 Acircumflex
+char194 "
+~A 778,880,5 2 0303 Atilde
+char195 "
+:A 778,902,5 2 0304 Adieresis
+char196 "
+oA 778,925,5 2 0305 Aring
+char197 "
+AE 944,694,5 2 0306 AE
+char198 "
+,C 709,709,206 3 0307 Ccedilla
+char199 "
+`E 611,921,5 2 0310 Egrave
+char200 "
+'E 611,922,5 2 0311 Eacute
+char201 "
+^E 611,915,5 2 0312 Ecircumflex
+char202 "
+:E 611,902,5 2 0313 Edieresis
+char203 "
+`I 337,921,5 2 0314 Igrave
+char204 "
+'I 337,922,5 2 0315 Iacute
+char205 "
+^I 337,915,5 2 0316 Icircumflex
+char206 "
+:I 337,902,5 2 0317 Idieresis
+char207 "
+~N 831,880,20 2 0321 Ntilde
+char209 "
+`O 786,921,20 2 0322 Ograve
+char210 "
+'O 786,922,20 2 0323 Oacute
+char211 "
+^O 786,915,20 2 0324 Ocircumflex
+char212 "
+~O 786,880,20 2 0325 Otilde
+char213 "
+:O 786,902,20 2 0326 Odieresis
+char214 "
+/O 833,709,20 2 0330 Oslash
+char216 "
+`U 778,921,20 2 0331 Ugrave
+char217 "
+'U 778,922,20 2 0332 Uacute
+char218 "
+^U 778,915,20 2 0333 Ucircumflex
+char219 "
+:U 778,902,20 2 0334 Udieresis
+char220 "
+ss 556,731,9 2 0337 germandbls
+char223 "
+`a 500,695,20 2 0340 agrave
+char224 "
+'a 500,696,20 2 0341 aacute
+char225 "
+^a 500,689,20 2 0342 acircumflex
+char226 "
+~a 500,654,20 2 0343 atilde
+char227 "
+:a 500,676,20 2 0344 adieresis
+char228 "
+oa 500,720,20 2 0345 aring
+char229 "
+ae 758,469,20 0 0346 ae
+char230 "
+,c 444,469,206 1 0347 ccedilla
+char231 "
+`e 479,695,20 2 0350 egrave
+char232 "
+'e 479,696,20 2 0351 eacute
+char233 "
+^e 479,689,20 2 0352 ecircumflex
+char234 "
+:e 479,676,20 2 0353 edieresis
+char235 "
+`i 287,695,5 2 0354 igrave
+char236 "
+'i 287,696,5 2 0355 iacute
+char237 "
+^i 287,689,5 2 0356 icircumflex
+char238 "
+:i 287,676,5 2 0357 idieresis
+char239 "
+~n 582,654,5 2 0361 ntilde
+char241 "
+`o 546,695,20 2 0362 ograve
+char242 "
+'o 546,696,20 2 0363 oacute
+char243 "
+^o 546,689,20 2 0364 ocircumflex
+char244 "
+~o 546,654,20 2 0365 otilde
+char245 "
+:o 546,676,20 2 0366 odieresis
+char246 "
+/o 556,474,23 0 0370 oslash
+char248 "
+`u 603,695,12 2 0371 ugrave
+char249 "
+'u 603,696,12 2 0372 uacute
+char250 "
+^u 603,689,12 2 0373 ucircumflex
+char251 "
+:u 603,676,12 2 0374 udieresis
+char252 "
+:y 556,676,283 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/S b/ps/devps/S
new file mode 100644
index 000000000..139b9c14a
--- /dev/null
+++ b/ps/devps/S
@@ -0,0 +1,226 @@
+name S
+internalname Symbol
+special
+spacewidth 250
+charset
+space 250 0 0040
+! 333,673,17 2 0041 exclam
+fa 713,705 2 0042 universal
+# 500,673,17 2 0043 numbersign
+sh "
+te 549,707 2 0044 existential
+% 833,655,36 2 0045 percent
+& 778,661,18 2 0046 ampersand
+st 439,499,17 2 0047 suchthat
+( 333,673,191 3 0050 parenleft
+) 333,674,191 3 0051 parenright
+** 500,551 2 0052 asteriskmath
++ 549,533 2 0053 plus
+pl "
+, 250,104,152 3 0054 comma
+\- 549,288 2 0055 minus
+mi "
+. 250,95,17 2 0056 period
+/ 278,646,17 2 0057 slash
+sl "
+0 500,685,17 2 0060 zero
+1 500,673 2 0061 one
+2 500,686 2 0062 two
+3 500,687,17 2 0063 three
+4 500,685 2 0064 four
+5 500,685,17 2 0065 five
+6 500,685,17 2 0066 six
+7 500,673,17 2 0067 seven
+8 500,686,18 2 0070 eight
+9 500,685,17 2 0071 nine
+: 278,460,17 2 0072 colon
+; 278,460,152 3 0073 semicolon
+< 549,522 2 0074 less
+= 549,390 2 0075 equal
+eq "
+> 549,522 2 0076 greater
+? 444,685,17 2 0077 question
+=~ 549,475 2 0100 congruent
+*A 722,673 2 0101 Alpha
+*B 667,672 2 0102 Beta
+*X 722,673 2 0103 Chi
+*D 612,688 2 0104 Delta
+*E 611,673 2 0105 Epsilon
+*F 763,672 2 0106 Phi
+*G 603,673 2 0107 Gamma
+*Y 722,673 2 0110 Eta
+*I 333,673 2 0111 Iota
+--- 631,689,17 2 0112 theta1
+*K 722,672 2 0113 Kappa
+*L 686,688 2 0114 Lambda
+*M 889,673 2 0115 Mu
+*N 722,673,8 2 0116 Nu
+*O 722,685,17 2 0117 Omicron
+*P 768,673 2 0120 Pi
+*H 741,685,17 2 0121 Theta
+*R 556,673 2 0122 Rho
+*S 592,673 2 0123 Sigma
+*T 611,673 2 0124 Tau
+--- 690,673 2 0125 Upsilon
+ts 439,500,233 3 0126 sigma1
+*W 768,688 2 0127 Omega
+*C 645,672 2 0130 Xi
+*Q 795,683 2 0131 Psi
+*Z 611,673 2 0132 Zeta
+[ 333,673,155 3 0133 bracketleft
+lB "
+3d 863,478 2 0134 therefore
+tf "
+] 333,673,155 3 0135 bracketright
+rB "
+pp 658,674 2 0136 perpendicular
+_ 500,0,252 1 0137 underscore
+rn 500,917 2 0140 radicalex
+*a 631,500,17 2 0141 alpha
+*b 549,741,222 3 0142 beta
+*x 549,500,231 3 0143 chi
+*d 494,740,17 2 0144 delta
+*e 439,502,19 2 0145 epsilon
+*f 521,670,224 3 0146 phi
+*g 411,499,225 3 0147 gamma
+*y 603,514,202 3 0150 eta
+*i 329,503,18 2 0151 iota
+--- 603,499,224 3 0152 phi1
+*k 549,502 2 0153 kappa
+*l 549,739,18 2 0154 lambda
+*m 576,500,223 3 0155 mu
+char181 "
+*n 521,507,17 2 0156 nu
+*o 549,499,19 2 0157 omicron
+*p 549,486,20 2 0160 pi
+*h 521,690,18 2 0161 theta
+*r 549,499,230 3 0162 rho
+*s 603,500,21 2 0163 sigma
+*t 439,500,18 2 0164 tau
+*u 576,507,17 2 0165 upsilon
+--- 713,583,17 2 0166 omega1
+*w 686,500,17 2 0167 omega
+*c 493,766,224 3 0170 xi
+*q 686,500,228 3 0171 psi
+*z 494,756,225 3 0172 zeta
+lC 480,673,183 3 0173 braceleft
+{ "
+ba 200,673,177 3 0174 bar
+| "
+rC 480,673,183 3 0175 braceright
+} "
+ap 549,307 2 0176 similar
+*U 620,684 2 0241 Upsilon1
+fm 247,735 2 0242 minute
+<= 549,639 2 0243 lessequal
+f/ 167,677,12 2 0244 fraction
+if 713,404 2 0245 infinity
+--- 500,687,193 3 0246 florin
+CL 753,533,26 2 0247 club
+DI 753,550,36 2 0250 diamond
+HE 753,532,33 2 0251 heart
+SP 753,548,36 2 0252 spade
+<> 1042,511,15 2 0253 arrowboth
+<- 987,511,15 2 0254 arrowleft
+ua 603,910 2 0255 arrowup
+-> 987,511,15 2 0256 arrowright
+da 603,888,22 2 0257 arrowdown
+de 400,684 2 0260 degree
+char176 "
++- 549,645 2 0261 plusminus
+char177 "
+sd 411,737 2 0262 second
+>= 549,639 2 0263 greaterequal
+mu 549,524 2 0264 multiply
+char215 "
+pt 713,404 2 0265 proportional
+pd 494,746,20 2 0266 partialdiff
+bu 460,473 2 0267 bullet
+di 549,456 2 0270 divide
+char247 "
+!= 549,549,25 2 0271 notequal
+== 549,443 2 0272 equivalence
+~~ 549,394 2 0273 approxequal
+~= "
+--- 1000,95,17 2 0274 ellipsis
+--- 603,1010,120 3 0275 arrowvertex
+--- 1000,276 2 0276 arrowhorizex
+CR 658,629,16 2 0277 carriagereturn
+Ah 823,658,18 2 0300 aleph
+Im 686,740,53 3 0301 Ifraktur
+Re 795,734,15 2 0302 Rfraktur
+wp 987,573,211 3 0303 weierstrass
+c* 768,673,17 2 0304 circlemultiply
+c+ 768,675,15 2 0305 circleplus
+es 823,719,24 2 0306 emptyset
+ca 768,509 2 0307 intersection
+cu 768,492,17 2 0310 union
+sp 713,470 2 0311 propersuperset
+ip 713,470,125 3 0312 reflexsuperset
+--- 713,540,70 3 0313 notsubset
+sb 713,470 2 0314 propersubset
+ib 713,470,125 3 0315 reflexsubset
+mo 713,468 2 0316 element
+nm 713,555,58 3 0317 notelement
+/_ 768,673 2 0320 angle
+gr 713,718,19 2 0321 gradient
+--- 790,670,20 2 0322 registerserif
+--- 790,675,15 2 0323 copyrightserif
+--- 890,673 2 0324 trademarkserif
+product 823,751,101 3 0325 product
+sr 549,917,38 2 0326 radical
+md 250,310 2 0327 dotmath
+no 713,288 2 0330 logicalnot
+char172 "
+AN 603,454 2 0331 logicaland
+OR 603,477 2 0332 logicalor
+hA 1042,510,20 2 0333 arrowdblboth
+lA 987,513,15 2 0334 arrowdblleft
+uA 603,911 2 0335 arrowdblup
+rA 987,508,20 2 0336 arrowdblright
+dA 603,890,19 2 0337 arrowdbldown
+--- 494,745 2 0340 lozenge
+la 329,746,198 3 0341 angleleft
+--- 790,670,20 2 0342 registersans
+--- 790,675,15 2 0343 copyrightsans
+--- 786,673 2 0344 trademarksans
+sum 713,752,108 3 0345 summation
+parenlefttp 384,926,290 3 0346 parenlefttp
+parenleftex 384,920,80 3 0347 parenleftex
+parenleftbt 384,920,293 3 0350 parenleftbt
+bracketlefttp 384,925,75 3 0351 bracketlefttp
+lc "
+bracketleftex 384,925,75 3 0352 bracketleftex
+bracketleftbt 384,925,75 3 0353 bracketleftbt
+lf "
+bracelefttp 494,925,75 3 0354 bracelefttp
+lt "
+braceleftmid 494,925,75 3 0355 braceleftmid
+lk "
+braceleftbt 494,925,75 3 0356 braceleftbt
+lb "
+braceex 494,925,75 3 0357 braceex
+bracerightex "
+braceleftex "
+barex "
+bv "
+--- 790,808,3 2 0360 apple
+ra 329,746,198 3 0361 angleright
+is 274,916,107,67,52,-10 3 0362 integral
+--- 686,922,83 3 0363 integraltp
+--- 686,975,88 3 0364 integralex
+--- 686,921,81 3 0365 integralbt
+parenrighttp 384,926,293 3 0366 parenrighttp
+parenrightex 384,925,85 3 0367 parenrightex
+parenrightbt 384,926,293 3 0370 parenrightbt
+bracketrighttp 384,925,75 3 0371 bracketrighttp
+rc "
+bracketrightex 384,925,75 3 0372 bracketrightex
+bracketrightbt 384,925,75 3 0373 bracketrightbt
+rf "
+bracerighttp 494,925,75 3 0374 bracerighttp
+rt "
+bracerightmid 494,925,75 3 0375 bracerightmid
+rk "
+bracerightbt 494,925,75 3 0376 bracerightbt
+rb "
diff --git a/ps/devps/SS b/ps/devps/SS
new file mode 100644
index 000000000..cd4ff28d8
--- /dev/null
+++ b/ps/devps/SS
@@ -0,0 +1,194 @@
+name SS
+internalname Symbol-Slanted
+special
+slant 15.5
+spacewidth 223
+charset
+space 223 0 0040
+--- 296,599,15,137,-72,99 2 0041 exclam
+--- 635,627,0,216,-173,99 2 0042 universal
+--- 445,599,15,162,-21,99 2 0043 numbersign
+--- 489,629,0,183,28,99 2 0044 existential
+--- 741,583,32,75,-97,75 2 0045 percent
+--- 692,589,16,103,-18,99 2 0046 ampersand
+--- 391,444,15,109,6,99 2 0047 suchthat
+--- 296,599,170,203,-39,99 3 0050 parenleft
+--- 296,600,170,93,72,93 3 0051 parenright
+--- 445,490,0,105,-76,99 2 0052 asteriskmath
+--- 489,474,0,123,-26,99 2 0053 plus
+--- 223,93,136,10,41,10 3 0054 comma
+--- 489,256,0,117,-24,99 2 0055 minus
+--- 223,85,15,1,-20,1 2 0056 period
+--- 247,575,15,208,55,99 2 0057 slash
+--- 445,610,15,142,-40,99 2 0060 zero
+--- 445,599,0,56,-57,56 2 0061 one
+--- 445,611,0,129,28,99 2 0062 two
+--- 445,611,16,115,-6,99 2 0063 three
+--- 445,610,0,135,-8,99 2 0064 four
+--- 445,610,15,188,14,99 2 0065 five
+--- 445,610,16,193,-37,99 2 0066 six
+--- 445,599,15,190,-101,99 2 0067 seven
+--- 445,611,16,141,-32,99 2 0070 eight
+--- 445,609,15,140,-2,99 2 0071 nine
+--- 247,409,15,89,-31,89 2 0072 colon
+--- 247,409,136,99,17,99 3 0073 semicolon
+--- 489,464,0,171,-37,99 2 0074 less
+--- 489,347,0,147,1,99 2 0075 equal
+--- 489,464,0,108,27,99 2 0076 greater
+--- 395,610,15,172,-113,99 2 0077 question
+--- 489,423,0,158,40,99 2 0100 congruent
+--- 643,599,0,21,47,21 2 0101 Alpha
+--- 594,598,0,101,24,99 2 0102 Beta
+--- 643,599,0,205,58,99 2 0103 Chi
+--- 545,612,0,46,45,46 2 0104 Delta
+--- 544,599,0,194,22,99 2 0105 Epsilon
+--- 679,598,0,132,-55,99 2 0106 Phi
+--- 537,599,0,227,19,99 2 0107 Gamma
+--- 643,599,0,243,15,99 2 0110 Eta
+--- 296,599,0,222,22,99 2 0111 Iota
+--- 562,614,15,133,-58,99 2 0112 theta1
+--- 643,598,0,185,19,99 2 0113 Kappa
+--- 611,612,0,49,45,49 2 0114 Lambda
+--- 791,599,0,233,22,99 2 0115 Mu
+--- 643,599,7,234,24,99 2 0116 Nu
+--- 643,610,15,154,-62,99 2 0117 Omicron
+--- 684,599,0,213,28,99 2 0120 Pi
+--- 659,610,15,138,-62,99 2 0121 Theta
+--- 495,599,0,200,25,99 2 0122 Rho
+--- 527,599,0,186,45,99 2 0123 Sigma
+--- 544,599,0,229,-109,99 2 0124 Tau
+--- 614,599,0,240,-125,99 2 0125 Upsilon
+ts 391,445,208,151,-28,99 3 0126 sigma1
+--- 684,612,0,126,20,99 2 0127 Omega
+--- 574,598,0,176,14,99 2 0130 Xi
+--- 708,608,0,227,-138,99 2 0131 Psi
+--- 544,599,0,231,11,99 2 0132 Zeta
+--- 296,599,138,207,16,99 3 0133 bracketleft
+--- 768,426,0,0,-110 2 0134 therefore
+--- 296,599,138,159,64,99 3 0135 bracketright
+--- 586,600,0,60,37,60 2 0136 perpendicular
+--- 445,0,224,0,122 1 0137 underscore
+--- 445,816,0,829,-622,99 2 0140 radicalex
+*a 562,445,15,146,-34,99 2 0141 alpha
+*b 489,659,198,139,57,99 3 0142 beta
+*x 489,445,206,134,98,99 3 0143 chi
+*d 440,658,16,181,-33,99 2 0144 delta
+*e 391,447,17,127,1,99 2 0145 epsilon
+*f 464,596,200,103,-28,99 3 0146 phi
+*g 366,444,200,252,-42,99 3 0147 gamma
+*y 537,457,180,68,-50,68 3 0150 eta
+*i 293,448,16,53,-47,53 2 0151 iota
+--- 537,444,199,117,-42,99 3 0152 phi1
+*k 489,447,0,182,-56,99 2 0153 kappa
+*l 489,658,16,91,29,91 2 0154 lambda
+*m 513,445,198,70,68,70 3 0155 mu
+*n 464,451,15,134,-69,99 2 0156 nu
+*o 489,444,17,87,-36,87 2 0157 omicron
+*p 489,433,18,160,-8,99 2 0160 pi
+*h 464,614,16,140,-53,99 2 0161 theta
+*r 489,444,205,82,69,82 3 0162 rho
+*s 537,445,19,175,-37,99 2 0163 sigma
+*t 391,445,16,170,-45,99 2 0164 tau
+*u 513,451,15,95,-55,95 2 0165 upsilon
+--- 635,519,15,173,-28,99 2 0166 omega1
+*w 611,445,16,126,-35,99 2 0167 omega
+*c 439,681,200,126,-20,99 3 0170 xi
+*q 611,445,203,198,-91,99 3 0171 psi
+*z 440,673,200,190,-50,99 3 0172 zeta
+--- 427,599,163,163,-66,99 3 0173 braceleft
+--- 178,599,158,179,41,99 3 0174 bar
+--- 427,599,163,67,31,67 3 0175 braceright
+--- 489,273,0,110,-28,99 2 0176 similar
+--- 552,609,0,208,-84,99 2 0241 Upsilon1
+--- 220,654,0,223,-106,99 2 0242 minute
+--- 489,569,0,206,24,99 2 0243 lessequal
+--- 149,603,11,391,214,99 2 0244 fraction
+--- 635,360,0,107,-41,99 2 0245 infinity
+--- 445,612,172,219,86,99 3 0246 florin
+--- 670,474,23,25,-69,25 2 0247 club
+--- 670,490,32,0,-148 2 0250 diamond
+--- 670,473,29,59,-155,59 2 0251 heart
+--- 670,488,32,0,-82 2 0252 spade
+--- 927,455,13,103,-40,99 2 0253 arrowboth
+--- 878,455,13,87,-47,87 2 0254 arrowleft
+--- 537,810,0,204,-173,99 2 0255 arrowup
+--- 878,455,13,94,-55,94 2 0256 arrowright
+--- 537,790,20,85,-54,85 2 0257 arrowdown
+--- 356,609,0,160,-137,99 2 0260 degree
+--- 489,574,0,154,41,99 2 0261 plusminus
+--- 366,656,0,244,-100,99 2 0262 second
+--- 489,569,0,143,24,99 2 0263 greaterequal
+--- 489,466,0,170,22,99 2 0264 multiply
+--- 635,360,0,82,-40,82 2 0265 proportional
+--- 440,664,18,152,-12,99 2 0266 partialdiff
+--- 409,421,0,95,-68,95 2 0267 bullet
+--- 489,406,0,119,-24,99 2 0270 divide
+--- 489,489,22,148,-1,99 2 0271 notequal
+--- 489,394,0,163,15,99 2 0272 equivalence
+--- 489,351,0,133,-7,99 2 0273 approxequal
+--- 890,85,15,0,-57 2 0274 ellipsis
+--- 537,899,107,92,-166,92 3 0275 arrowvertex
+--- 890,246,0,171,42,99 2 0276 arrowhorizex
+--- 586,560,14,174,10,99 2 0277 carriagereturn
+--- 732,586,16,58,-109,58 2 0300 aleph
+--- 611,659,47,123,24,99 2 0301 Ifraktur
+--- 708,653,13,175,-21,99 2 0302 Rfraktur
+--- 878,510,188,50,-62,50 3 0303 weierstrass
+--- 684,599,15,124,-64,99 2 0304 circlemultiply
+--- 684,601,13,125,-65,99 2 0305 circleplus
+--- 732,640,21,202,12,99 2 0306 emptyset
+--- 684,453,0,80,14,80 2 0307 intersection
+--- 684,438,15,154,-60,99 2 0310 union
+--- 635,418,0,90,32,90 2 0311 propersuperset
+--- 635,418,111,89,67,89 3 0312 reflexsuperset
+--- 635,481,62,159,-37,99 3 0313 notsubset
+--- 635,418,0,159,-37,99 2 0314 propersubset
+--- 635,418,111,159,34,99 3 0315 reflexsubset
+--- 635,417,0,0,-43 2 0316 element
+--- 635,494,52,0,-24 3 0317 notelement
+--- 684,599,0,199,27,99 2 0320 angle
+--- 635,639,17,220,-181,99 2 0321 gradient
+--- 703,596,18,110,-70,99 2 0322 registerserif
+--- 703,601,13,113,-72,99 2 0323 copyrightserif
+--- 792,599,0,205,-119,99 2 0324 trademarkserif
+--- 732,668,90,238,56,99 3 0325 product
+--- 489,816,34,272,-84,99 2 0326 radical
+--- 223,276,0,52,-81,52 2 0327 dotmath
+--- 635,256,0,100,-28,99 2 0330 logicalnot
+--- 537,404,0,32,29,32 2 0331 logicaland
+--- 537,424,0,152,-101,99 2 0332 logicalor
+--- 927,454,18,101,-42,99 2 0333 arrowdblboth
+--- 878,457,13,114,-46,99 2 0334 arrowdblleft
+--- 537,811,0,201,-102,99 2 0335 arrowdblup
+--- 878,452,18,89,-21,89 2 0336 arrowdblright
+--- 537,792,17,152,-53,99 2 0337 arrowdbldown
+--- 440,663,0,129,-71,99 2 0340 lozenge
+--- 293,664,176,229,-48,99 3 0341 angleleft
+--- 703,596,18,110,-70,99 2 0342 registersans
+--- 703,601,13,111,-70,99 2 0343 copyrightsans
+--- 700,599,0,182,-129,99 2 0344 trademarksans
+--- 635,669,96,171,65,99 3 0345 summation
+--- 342,824,261,350,96,99 3 0346 parenlefttp
+--- 342,823,76,46,38,46 3 0347 parenleftex
+--- 342,824,261,47,-63,47 3 0350 parenleftbt
+--- 342,824,71,268,72,99 3 0351 bracketlefttp
+--- 342,823,70,13,72,13 3 0352 bracketleftex
+--- 342,824,71,14,72,14 3 0353 bracketleftbt
+--- 440,824,67,258,-108,99 3 0354 bracelefttp
+--- 440,832,76,96,-76,96 3 0355 braceleftmid
+--- 440,824,62,94,-169,94 3 0356 braceleftbt
+--- 440,832,71,96,-107,96 3 0357 braceex
+--- 293,664,176,102,79,99 3 0361 angleright
+--- 244,815,95,305,63,99 3 0362 integral
+--- 611,820,74,312,-222,99 3 0363 integraltp
+--- 611,868,78,79,-221,79 3 0364 integralex
+--- 611,820,72,64,20,64 3 0365 integralbt
+--- 342,824,261,206,-223,99 3 0366 parenrighttp
+--- 342,823,76,365,-281,99 3 0367 parenrightex
+--- 342,824,261,365,80,99 3 0370 parenrightbt
+--- 342,824,71,285,-199,99 3 0371 bracketrighttp
+--- 342,823,70,285,-200,99 3 0372 bracketrightex
+--- 342,824,71,285,54,99 3 0373 bracketrightbt
+--- 440,824,67,35,-108,35 3 0374 bracerighttp
+--- 440,832,76,127,-105,99 3 0375 bracerightmid
+--- 440,824,62,94,54,94 3 0376 bracerightbt
diff --git a/ps/devps/TB b/ps/devps/TB
new file mode 100644
index 000000000..1c6154c48
--- /dev/null
+++ b/ps/devps/TB
@@ -0,0 +1,427 @@
+name TB
+internalname Times-Bold
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -74
+A w -74
+A v -74
+A ' -74
+A Y -92
+A W -111
+A V -129
+A T -74
+F . -92
+F , -92
+F A -74
+L y -55
+L ' -92
+L Y -92
+L W -92
+L V -92
+L T -92
+P . -92
+P , -92
+P A -74
+R y -35
+R Y -35
+R W -35
+R V -35
+R T -35
+T y -74
+T w -74
+T u -92
+T ; -74
+T s -92
+T r -74
+T . -74
+T o -92
+T i -18
+T - -92
+T hy -92
+T char173 -92
+T e -92
+T , -74
+T : -74
+T c -92
+T a -92
+T O -18
+T A -74
+V y -92
+V u -92
+V ; -92
+V r -74
+V . -129
+V o -92
+V i -37
+V - -74
+V hy -74
+V char173 -74
+V e -92
+V , -129
+V : -92
+V a -92
+V O -20
+V A -129
+W y -37
+W u -18
+W ; -55
+W r -18
+W . -92
+W o -55
+W i -18
+W - -37
+W hy -37
+W char173 -37
+W e -55
+W , -92
+W : -55
+W a -55
+W A -111
+Y v -111
+Y u -92
+Y ; -92
+Y q -111
+Y . -92
+Y p -92
+Y o -111
+Y i -37
+Y - -92
+Y hy -92
+Y char173 -92
+Y e -111
+Y , -92
+Y : -92
+Y a -111
+Y A -92
+f ' 55
+f f 0
+1 1 -55
+` ` -74
+` oq -74
+oq ` -74
+oq oq -74
+' s -37
+' ' -74
+r z 0
+r y 0
+r x 0
+r w 0
+r t 0
+r ' 18
+r q -18
+r . -92
+r o -18
+r - -37
+r hy -37
+r char173 -37
+r h 0
+r e -18
+r , -92
+r c -18
+v . -55
+v , -55
+w . -55
+w , -55
+y . -55
+y , -55
+charset
+ha 581,690 2 0000 asciicircum
+ti 520,461 0 0001 asciitilde
+vS 556,909,19 2 0002 Scaron
+vZ 667,909 2 0003 Zcaron
+vs 389,690,17 2 0004 scaron
+vz 444,690 2 0005 zcaron
+:Y 722,871 2 0006 Ydieresis
+tm 1000,681 2 0007 trademark
+space 250 0 0040
+! 333,690,18 2 0041 exclam
+" 555,690 2 0042 quotedbl
+# 500,684,17 2 0043 numbersign
+sh "
+$ 500,732,116 3 0044 dollar
+Do "
+% 1000,692,11 2 0045 percent
+& 833,690,17 2 0046 ampersand
+' 333,680 2 0047 quoteright
+( 333,699,169 3 0050 parenleft
+) 333,699,169 3 0051 parenright
+* 500,690 2 0052 asterisk
++ 570,460,10 0 0053 plus
+, 250,157,181 1 0054 comma
+- 333,285 0 0055 hyphen
+hy "
+char173 "
+. 250,145,19 0 0056 period
+/ 278,750,17 2 0057 slash
+sl "
+0 500,690,18 2 0060 zero
+1 500,690 2 0061 one
+2 500,683 2 0062 two
+3 500,683,19 2 0063 three
+4 500,681 2 0064 four
+5 500,681,17 2 0065 five
+6 500,684,18 2 0066 six
+7 500,679 2 0067 seven
+8 500,685,17 2 0070 eight
+9 500,684,18 2 0071 nine
+: 333,473,18 0 0072 colon
+; 333,472,181 1 0073 semicolon
+< 570,460,10 0 0074 less
+= 570,375 0 0075 equal
+> 570,460,10 0 0076 greater
+? 500,681,17 2 0077 question
+@ 930,677,147 3 0100 at
+at "
+A 722,681 2 0101 A
+B 667,681 2 0102 B
+C 722,690,17 2 0103 C
+D 722,681 2 0104 D
+E 667,681 2 0105 E
+F 611,681 2 0106 F
+G 778,690,17 2 0107 G
+H 778,681 2 0110 H
+I 389,680 2 0111 I
+J 500,681,89 3 0112 J
+K 778,681 2 0113 K
+L 667,681 2 0114 L
+M 944,681 2 0115 M
+N 722,681,10 2 0116 N
+O 778,690,18 2 0117 O
+P 611,681 2 0120 P
+Q 778,690,182 3 0121 Q
+R 722,681 2 0122 R
+S 556,690,19 2 0123 S
+T 667,681 2 0124 T
+U 722,681,19 2 0125 U
+V 722,681,18 2 0126 V
+W 1000,680,18 2 0127 W
+X 722,681 2 0130 X
+Y 722,680 2 0131 Y
+Z 667,681 2 0132 Z
+[ 333,674,142 3 0133 bracketleft
+lB "
+\ 278,750,17 2 0134 backslash
+rs "
+] 333,674,142 3 0135 bracketright
+rB "
+a^ 333,690 2 0136 circumflex
+^ "
+_ 500,0,256 1 0137 underscore
+` 333,691 2 0140 quoteleft
+oq "
+a 500,472,19 0 0141 a
+b 556,670,18 2 0142 b
+c 444,472,17 0 0143 c
+d 556,670,17 2 0144 d
+e 444,474,18 0 0145 e
+f 333,690 2 0146 f
+g 500,472,210 1 0147 g
+h 556,670 2 0150 h
+i 278,690 2 0151 i
+j 333,690,212 3 0152 j
+k 556,670 2 0153 k
+l 278,670 2 0154 l
+m 833,471 0 0155 m
+n 556,473 0 0156 n
+o 500,472,18 0 0157 o
+p 556,473,210 1 0160 p
+q 556,472,210 1 0161 q
+r 444,473 0 0162 r
+s 389,472,17 0 0163 s
+t 333,627,19 2 0164 t
+u 556,460,17 0 0165 u
+v 500,460,14 0 0166 v
+w 722,460,14 0 0167 w
+x 500,460 0 0170 x
+y 500,460,212 1 0171 y
+z 444,460 0 0172 z
+lC 394,674,142 3 0173 braceleft
+{ "
+ba 220,720,195 3 0174 bar
+| "
+rC 394,674,142 3 0175 braceright
+} "
+a~ 333,671 2 0176 tilde
+~ "
+--- 333,154,179 1 0200 quotesinglbase
+Fo 500,436 0 0201 guillemotleft
+char171 "
+Fc 500,436 0 0202 guillemotright
+char187 "
+bu 350,425 0 0203 bullet
+--- 500,713,157 3 0204 florin
+f/ 167,690,17 2 0205 fraction
+%0 1000,718,55 2 0206 perthousand
+dg 500,690,141 3 0207 dagger
+dd 500,681,138 3 0210 daggerdbl
+en 500,270 0 0211 endash
+em 1000,280 0 0212 emdash
+fi 556,690 2 0214 fi
+fl 556,691 2 0215 fl
+.i 278,460 0 0220 dotlessi
+ga 333,695 2 0222 grave
+a" 333,753 2 0223 hungarumlaut
+a. 333,625 2 0224 dotaccent
+ab 333,667 2 0225 breve
+ah 333,690 2 0226 caron
+ao 333,746 2 0227 ring
+ho 333,70,179 1 0230 ogonek
+lq 500,679 2 0231 quotedblleft
+rq 500,680 2 0232 quotedblright
+oe 722,473,17 0 0233 oe
+/l 278,670 2 0234 lslash
+--- 500,154,179 1 0235 quotedblbase
+OE 1000,683,7 2 0236 OE
+/L 667,681 2 0237 Lslash
+r! 333,498,210 1 0241 exclamdown
+char161 "
+ct 500,586,148 3 0242 cent
+char162 "
+Po 500,682,17 2 0243 sterling
+char163 "
+Cs 500,604 2 0244 currency
+char164 "
+Ye 500,681 2 0245 yen
+char165 "
+sc 500,677,148 3 0247 section
+char167 "
+ad 333,652 2 0250 dieresis
+char168 "
+co 747,690,17 2 0251 copyright
+char169 "
+Of 300,685 2 0252 ordfeminine
+char170 "
+fo 333,436 0 0253 guilsinglleft
+no 570,375 0 0254 logicalnot
+char172 "
+\- 570,262 0 0255 minus
+rg 747,690,17 2 0256 registered
+char174 "
+a- 333,600 2 0257 macron
+char175 "
+aa 333,695 2 0264 acute
+char180 "
+ps 540,681,190 3 0266 paragraph
+char182 "
+char183 250,394 0 0267 periodcentered
+ac 333,0,211 1 0270 cedilla
+char184 "
+Om 330,685 2 0272 ordmasculine
+char186 "
+fc 333,436 0 0273 guilsinglright
+r? 500,488,210 1 0277 questiondown
+char191 "
+`A 722,914 2 0300 Agrave
+char192 "
+'A 722,914 2 0301 Aacute
+char193 "
+^A 722,909 2 0302 Acircumflex
+char194 "
+~A 722,890 2 0303 Atilde
+char195 "
+:A 722,871 2 0304 Adieresis
+char196 "
+oA 722,965 2 0305 Aring
+char197 "
+AE 1000,681 2 0306 AE
+char198 "
+,C 722,690,211 3 0307 Ccedilla
+char199 "
+`E 667,914 2 0310 Egrave
+char200 "
+'E 667,914 2 0311 Eacute
+char201 "
+^E 667,909 2 0312 Ecircumflex
+char202 "
+:E 667,871 2 0313 Edieresis
+char203 "
+`I 389,914 2 0314 Igrave
+char204 "
+'I 389,914 2 0315 Iacute
+char205 "
+^I 389,909 2 0316 Icircumflex
+char206 "
+:I 389,871 2 0317 Idieresis
+char207 "
+~N 722,890,10 2 0321 Ntilde
+char209 "
+`O 778,914,18 2 0322 Ograve
+char210 "
+'O 778,914,18 2 0323 Oacute
+char211 "
+^O 778,909,18 2 0324 Ocircumflex
+char212 "
+~O 778,890,18 2 0325 Otilde
+char213 "
+:O 778,871,18 2 0326 Odieresis
+char214 "
+/O 778,740,75 3 0330 Oslash
+char216 "
+`U 722,914,19 2 0331 Ugrave
+char217 "
+'U 722,914,19 2 0332 Uacute
+char218 "
+^U 722,909,19 2 0333 Ucircumflex
+char219 "
+:U 722,871,19 2 0334 Udieresis
+char220 "
+ss 556,689,18 2 0337 germandbls
+char223 "
+`a 500,695,19 2 0340 agrave
+char224 "
+'a 500,695,19 2 0341 aacute
+char225 "
+^a 500,690,19 2 0342 acircumflex
+char226 "
+~a 500,671,19 2 0343 atilde
+char227 "
+:a 500,652,19 2 0344 adieresis
+char228 "
+oa 500,746,19 2 0345 aring
+char229 "
+ae 722,474,17 0 0346 ae
+char230 "
+,c 444,472,211 1 0347 ccedilla
+char231 "
+`e 444,695,18 2 0350 egrave
+char232 "
+'e 444,695,18 2 0351 eacute
+char233 "
+^e 444,690,18 2 0352 ecircumflex
+char234 "
+:e 444,652,18 2 0353 edieresis
+char235 "
+`i 278,695 2 0354 igrave
+char236 "
+'i 278,695 2 0355 iacute
+char237 "
+^i 278,690 2 0356 icircumflex
+char238 "
+:i 278,652 2 0357 idieresis
+char239 "
+~n 556,671 2 0361 ntilde
+char241 "
+`o 500,695,18 2 0362 ograve
+char242 "
+'o 500,695,18 2 0363 oacute
+char243 "
+^o 500,690,18 2 0364 ocircumflex
+char244 "
+~o 500,671,18 2 0365 otilde
+char245 "
+:o 500,652,18 2 0366 odieresis
+char246 "
+/o 500,550,95 3 0370 oslash
+char248 "
+`u 556,695,17 2 0371 ugrave
+char249 "
+'u 556,695,17 2 0372 uacute
+char250 "
+^u 556,690,17 2 0373 ucircumflex
+char251 "
+:u 556,652,17 2 0374 udieresis
+char252 "
+:y 500,652,212 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/TBI b/ps/devps/TBI
new file mode 100644
index 000000000..3acf3d14c
--- /dev/null
+++ b/ps/devps/TBI
@@ -0,0 +1,414 @@
+name TBI
+internalname Times-BoldItalic
+slant 15
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -74
+A w -74
+A v -74
+A ' -74
+A Y -55
+A W -92
+A V -74
+A T -55
+F . -129
+F , -129
+F A -92
+L y -37
+L ' -55
+L Y -37
+L W -37
+L V -37
+L T -18
+P . -129
+P , -129
+P A -74
+R y -18
+R Y -18
+R W -18
+R V -18
+T y -37
+T w -37
+T u -37
+T ; -74
+T s -92
+T r -37
+T . -92
+T o -92
+T i -37
+T - -92
+T hy -92
+T char173 -92
+T e -92
+T , -92
+T : -74
+T c -92
+T a -92
+T O -18
+T A -55
+V y -74
+V u -55
+V ; -74
+V r -55
+V . -129
+V o -111
+V i -55
+V - -55
+V hy -55
+V char173 -55
+V e -111
+V , -129
+V : -74
+V a -111
+V A -74
+W y -55
+W u -55
+W ; -55
+W r -74
+W . -74
+W o -74
+W i -37
+W - -37
+W hy -37
+W char173 -37
+W e -74
+W , -74
+W : -55
+W a -74
+W A -74
+Y v -92
+Y u -92
+Y ; -92
+Y q -111
+Y . -74
+Y p -74
+Y o -111
+Y i -55
+Y - -92
+Y hy -92
+Y char173 -92
+Y e -111
+Y , -92
+Y : -92
+Y a -92
+Y A -74
+f ' 55
+f f -18
+1 1 -55
+` ` -74
+` oq -74
+oq ` -74
+oq oq -74
+' t -37
+' s -74
+' ' -74
+r ' 37
+r . -55
+r , -55
+v . -37
+v , -37
+w . -37
+w , -37
+y . -37
+y , -37
+charset
+ha 570,662,0,16,16,16 2 0000 asciicircum
+ti 570,353,0,17,17,17 0 0001 asciitilde
+vS 556,887,18,43,44,43 2 0002 Scaron
+vZ 611,887,0,33,51,33 2 0003 Zcaron
+vs 389,683,13,126,34,98 2 0004 scaron
+vz 389,683,58,126,74,98 2 0005 zcaron
+:Y 611,852,0,64,4,64 2 0006 Ydieresis
+tm 1000,676,0,30,10,30 2 0007 trademark
+space 250 0 0040
+! 389,676,13,28,-16,28 2 0041 exclam
+" 555,693,0,44,-92,44 2 0042 quotedbl
+# 500,662,0,46,46,46 2 0043 numbersign
+sh "
+$ 500,723,101,42,70,42 3 0044 dollar
+Do "
+% 833,685,8,1,11,1 2 0045 percent
+& 778,676,19,0,9 2 0046 ampersand
+' 333,675,0,0,-30 2 0047 quoteright
+( 333,676,179,57,22,57 3 0050 parenleft
+) 333,676,179,0,94 3 0051 parenright
+* 500,676,0,0,-6 2 0052 asterisk
++ 570,505,0,17,17,17 0 0053 plus
+, 250,132,181,0,60 1 0054 comma
+- 333,282,0,16,17,16 0 0055 hyphen
+hy "
+char173 "
+. 250,133,13,0,27 0 0056 period
+/ 278,682,18,61,61,61 2 0057 slash
+sl "
+0 500,676,13,22,33,22 2 0060 zero
+1 500,676,0,0,45 2 0061 one
+2 500,676,0,0,77 2 0062 two
+3 500,676,13,0,65 2 0063 three
+4 500,676,0,48,65,48 2 0064 four
+5 500,662,13,32,61,32 2 0065 five
+6 500,676,13,54,27,54 2 0066 six
+7 500,662,0,69,-1,69 2 0067 seven
+8 500,676,13,21,47,21 2 0070 eight
+9 500,676,13,20,62,20 2 0071 nine
+: 333,458,13,8,-2,8 0 0072 colon
+; 333,458,181,8,37,8 1 0073 semicolon
+< 570,524,14,20,19,20 2 0074 less
+= 570,401,0,17,17,17 0 0075 equal
+> 570,524,14,20,19,20 2 0076 greater
+? 500,676,13,15,-28,15 2 0077 question
+@ 832,691,150,56,59,56 3 0100 at
+at "
+A 667,676,0,0,101 2 0101 A
+B 667,662,0,1,74,1 2 0102 B
+C 667,677,18,43,28,43 2 0103 C
+D 722,662,0,21,81,21 2 0104 D
+E 667,662,0,29,77,29 2 0105 E
+F 667,662,0,29,70,29 2 0106 F
+G 722,676,18,27,29,27 2 0107 G
+H 778,662,0,63,74,63 2 0110 H
+I 389,662,0,73,72,73 2 0111 I
+J 500,662,98,69,95,69 3 0112 J
+K 667,662,0,68,81,68 2 0113 K
+L 611,662,0,23,72,23 2 0114 L
+M 889,662,12,68,79,68 2 0115 M
+N 722,662,18,68,77,68 2 0116 N
+O 722,676,18,12,23,12 2 0117 O
+P 611,662,0,47,77,47 2 0120 P
+Q 722,676,203,12,23,12 3 0121 Q
+R 667,662,0,0,79 2 0122 R
+S 556,676,18,18,44,18 2 0123 S
+T 611,662,0,71,11,71 2 0124 T
+U 722,662,18,64,-16,64 2 0125 U
+V 667,662,18,75,2,75 2 0126 V
+W 889,662,18,75,2,75 2 0127 W
+X 667,662,0,70,74,70 2 0130 X
+Y 611,662,0,64,4,64 2 0131 Y
+Z 611,662,0,33,51,33 2 0132 Z
+[ 333,682,157,105,57,98 3 0133 bracketleft
+lB "
+\ 278,682,0,237,49,98 2 0134 backslash
+rs "
+] 333,682,157,47,115,47 3 0135 bracketright
+rB "
+a^ 333,682,0,111,-20,98 2 0136 circumflex
+^ "
+_ 500,0,127,50,50,50 1 0137 underscore
+` 333,676,0,36,-67,36 2 0140 quoteleft
+oq "
+a 500,458,14,30,41,30 0 0141 a
+b 500,682,13,24,29,24 2 0142 b
+c 444,458,13,24,25,24 0 0143 c
+d 500,682,13,91,41,91 2 0144 d
+e 444,458,13,19,25,19 0 0145 e
+f 333,682,203,177,196,98 3 0146 f
+g 500,458,203,48,77,48 1 0147 g
+h 556,682,13,12,38,12 2 0150 h
+i 278,676,13,56,25,56 2 0151 i
+j 278,676,203,83,202,83 3 0152 j
+k 500,682,13,61,40,61 2 0153 k
+l 278,682,13,84,19,84 2 0154 l
+m 778,458,13,16,34,16 0 0155 m
+n 556,458,13,12,26,12 0 0156 n
+o 500,458,13,17,23,17 0 0157 o
+p 500,458,203,31,129,31 1 0160 p
+q 500,459,203,36,29,36 1 0161 q
+r 389,458,0,76,41,76 0 0162 r
+s 389,459,13,25,34,25 0 0163 s
+t 278,592,14,77,34,77 2 0164 t
+u 556,458,13,15,2,15 0 0165 u
+v 444,458,13,38,0,38 0 0166 v
+w 667,458,13,25,0,25 0 0167 w
+x 500,458,13,48,55,48 0 0170 x
+y 444,458,203,29,110,29 1 0171 y
+z 389,448,58,55,74,55 0 0172 z
+lC 348,686,154,83,19,83 3 0173 braceleft
+{ "
+ba 220,682,0,0,-20 2 0174 bar
+| "
+rC 348,679,161,21,81,21 3 0175 braceright
+} "
+a~ 333,648,0,141,-19,98 2 0176 tilde
+~ "
+--- 333,132,181,0,-16 1 0200 quotesinglbase
+Fo 500,416,0,24,29,24 0 0201 guillemotleft
+char171 "
+Fc 500,421,0,24,29,24 0 0202 guillemotright
+char187 "
+bu 350,425 0 0203 bullet
+--- 500,682,154,80,136,80 3 0204 florin
+f/ 167,662,0,210,211,98 2 0205 fraction
+%0 1000,699,49,35,43,35 2 0206 perthousand
+dg 500,676,146,39,-40,39 3 0207 dagger
+dd 500,675,143,37,39,37 3 0210 daggerdbl
+en 500,266,0,61,61,61 0 0211 endash
+em 1000,266,0,64,64,64 0 0212 emdash
+fi 556,682,203,32,207,32 3 0214 fi
+fl 556,682,203,71,199,71 3 0215 fl
+.i 278,458,13,32,23,32 0 0220 dotlessi
+ga 333,690,0,42,-65,42 2 0222 grave
+a" 333,750,0,56,22,56 2 0223 hungarumlaut
+a. 333,648,0,25,-130,25 2 0224 dotaccent
+ab 333,671,0,131,-49,98 2 0225 breve
+ah 333,683,0,154,-59,98 2 0226 caron
+ao 333,676,0,69,-91,69 2 0227 ring
+ho 333,44,173,0,87 1 0230 ogonek
+lq 500,676,0,63,-7,63 2 0231 quotedblleft
+rq 500,675,0,59,-6,59 2 0232 quotedblright
+oe 722,458,13,15,24,15 0 0233 oe
+/l 278,682,13,98,38,98 2 0234 lslash
+--- 500,132,181,0,107 1 0235 quotedblbase
+OE 944,670,8,42,27,42 2 0236 OE
+/L 611,662,0,23,72,23 2 0237 Lslash
+r! 389,458,232,0,29 1 0241 exclamdown
+char161 "
+ct 500,570,142 3 0242 cent
+char162 "
+Po 500,676,13,55,82,55 2 0243 sterling
+char163 "
+Cs 500,612,0,53,53,53 2 0244 currency
+char164 "
+Ye 500,662,0,115,65,98 2 0245 yen
+char165 "
+sc 500,676,143,4,14,4 3 0247 section
+char167 "
+ad 333,648,0,141,-35,98 2 0250 dieresis
+char168 "
+co 747,676,18,26,27,26 2 0251 copyright
+char169 "
+Of 266,676,0,75,74,75 2 0252 ordfeminine
+char170 "
+fo 333,416,0,27,8,27 0 0253 guilsinglleft
+no 606,401,0,0,-1 0 0254 logicalnot
+char172 "
+\- 606,300,0,0,-1 0 0255 minus
+rg 747,676,18,26,27,26 2 0256 registered
+char174 "
+a- 333,616,0,137,-31,98 2 0257 macron
+char175 "
+aa 333,690,0,122,-118,98 2 0264 acute
+char180 "
+ps 500,682,189,142,-11,98 3 0266 paragraph
+char182 "
+char183 250,328,0,0,-1 0 0267 periodcentered
+ac 333,5,216,0,18 1 0270 cedilla
+char184 "
+Om 300,676,0,50,49,50 2 0272 ordmasculine
+char186 "
+fc 333,421,0,8,27,8 0 0273 guilsinglright
+r? 500,487,203,0,20 1 0277 questiondown
+char191 "
+`A 667,894,0,0,101 2 0300 Agrave
+char192 "
+'A 667,894,0,0,101 2 0301 Aacute
+char193 "
+^A 667,886,0,0,101 2 0302 Acircumflex
+char194 "
+~A 667,852,0,0,101 2 0303 Atilde
+char195 "
+:A 667,852,0,0,101 2 0304 Adieresis
+char196 "
+oA 667,880,0,0,101 2 0305 Aring
+char197 "
+AE 944,662,0,37,91,37 2 0306 AE
+char198 "
+,C 667,677,216,43,28,43 3 0307 Ccedilla
+char199 "
+`E 667,894,0,29,77,29 2 0310 Egrave
+char200 "
+'E 667,894,0,29,77,29 2 0311 Eacute
+char201 "
+^E 667,886,0,29,77,29 2 0312 Ecircumflex
+char202 "
+:E 667,852,0,29,77,29 2 0313 Edieresis
+char203 "
+`I 389,894,0,73,72,73 2 0314 Igrave
+char204 "
+'I 389,894,0,94,72,94 2 0315 Iacute
+char205 "
+^I 389,886,0,83,72,83 2 0316 Icircumflex
+char206 "
+:I 389,852,0,113,72,98 2 0317 Idieresis
+char207 "
+~N 722,852,18,68,77,68 2 0321 Ntilde
+char209 "
+`O 722,894,18,12,23,12 2 0322 Ograve
+char210 "
+'O 722,894,18,12,23,12 2 0323 Oacute
+char211 "
+^O 722,886,18,12,23,12 2 0324 Ocircumflex
+char212 "
+~O 722,852,18,12,23,12 2 0325 Otilde
+char213 "
+:O 722,852,18,12,23,12 2 0326 Odieresis
+char214 "
+/O 722,754,124,12,23,12 3 0330 Oslash
+char216 "
+`U 722,894,18,64,-16,64 2 0331 Ugrave
+char217 "
+'U 722,894,18,64,-16,64 2 0332 Uacute
+char218 "
+^U 722,886,18,64,-16,64 2 0333 Ucircumflex
+char219 "
+:U 722,852,18,64,-16,64 2 0334 Udieresis
+char220 "
+ss 500,682,203,47,218,47 3 0337 germandbls
+char223 "
+`a 500,690,14,30,41,30 2 0340 agrave
+char224 "
+'a 500,690,14,39,41,39 2 0341 aacute
+char225 "
+^a 500,682,14,30,41,30 2 0342 acircumflex
+char226 "
+~a 500,648,14,58,41,58 2 0343 atilde
+char227 "
+:a 500,648,14,58,41,58 2 0344 adieresis
+char228 "
+oa 500,676,14,30,41,30 2 0345 aring
+char229 "
+ae 722,458,13,13,35,13 0 0346 ae
+char230 "
+,c 444,458,216,24,25,24 1 0347 ccedilla
+char231 "
+`e 444,690,13,19,25,19 2 0350 egrave
+char232 "
+'e 444,690,13,67,25,67 2 0351 eacute
+char233 "
+^e 444,682,13,56,25,56 2 0352 ecircumflex
+char234 "
+:e 444,648,13,86,25,86 2 0353 edieresis
+char235 "
+`i 278,690,13,70,23,70 2 0354 igrave
+char236 "
+'i 278,690,13,150,23,98 2 0355 iacute
+char237 "
+^i 278,682,13,139,23,98 2 0356 icircumflex
+char238 "
+:i 278,648,13,169,23,98 2 0357 idieresis
+char239 "
+~n 556,648,13,30,26,30 2 0361 ntilde
+char241 "
+`o 500,690,13,17,23,17 2 0362 ograve
+char242 "
+'o 500,690,13,39,23,39 2 0363 oacute
+char243 "
+^o 500,682,13,28,23,28 2 0364 ocircumflex
+char244 "
+~o 500,648,13,58,23,58 2 0365 otilde
+char245 "
+:o 500,648,13,58,23,58 2 0366 odieresis
+char246 "
+/o 500,556,118,17,23,17 3 0370 oslash
+char248 "
+`u 556,690,13,15,2,15 2 0371 ugrave
+char249 "
+'u 556,690,13,15,2,15 2 0372 uacute
+char250 "
+^u 556,682,13,15,2,15 2 0373 ucircumflex
+char251 "
+:u 556,648,13,30,2,30 2 0374 udieresis
+char252 "
+:y 444,648,203,86,110,86 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/TI b/ps/devps/TI
new file mode 100644
index 000000000..bd842e65f
--- /dev/null
+++ b/ps/devps/TI
@@ -0,0 +1,432 @@
+name TI
+internalname Times-Italic
+slant 7
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -55
+A w -55
+A v -55
+A ' -37
+A Y -55
+A W -37
+A V -50
+A T -37
+F . -129
+F , -129
+F A -129
+L y -30
+L ' -37
+L Y -20
+L W -37
+L V -37
+L T -20
+P . -129
+P , -129
+P A -129
+R y -18
+R Y -18
+R W -18
+R V -18
+R T 0
+T y -74
+T w -74
+T u -55
+T ; -65
+T s -92
+T r -55
+T . -74
+T o -92
+T i -55
+T - -74
+T hy -74
+T char173 -74
+T e -92
+T , -74
+T : -55
+T c -92
+T a -92
+T O -18
+T A -74
+V y -92
+V u -74
+V ; -74
+V r -74
+V . -129
+V o -111
+V i -74
+V - -55
+V hy -55
+V char173 -55
+V e -111
+V , -129
+V : -65
+V a -111
+V O -30
+V A -74
+W y -92
+W u -55
+W ; -65
+W r -55
+W . -92
+W o -92
+W i -55
+W - -37
+W hy -37
+W char173 -37
+W e -92
+W , -92
+W : -65
+W a -92
+W A -70
+Y v -92
+Y u -92
+Y ; -65
+Y q -111
+Y . -92
+Y p -92
+Y o -92
+Y i -74
+Y - -74
+Y hy -74
+Y char173 -74
+Y e -92
+Y , -92
+Y : -65
+Y a -92
+Y A -70
+f ' 92
+1 1 -74
+` ` -111
+` oq -111
+oq ` -111
+oq oq -111
+' t -111
+' s -129
+' ' -111
+r y 0
+r x 0
+r w 0
+r v 0
+r u 0
+r t 0
+r r 0
+r ' 37
+r q -37
+r . -111
+r o -37
+r - -20
+r hy -20
+r char173 -20
+r h -18
+r g -37
+r e -37
+r d -37
+r , -111
+r c -37
+v . -74
+v , -74
+w . -74
+w , -74
+y . -55
+y , -55
+charset
+ha 422,660,0,131,50,44 2 0000 asciicircum
+ti 541,340,0,31,32,31 0 0001 asciitilde
+vS 500,896,22,56,41,44 2 0002 Scaron
+vZ 556,896,0,75,69,44 2 0003 Zcaron
+vs 389,669,14,111,34,44 2 0004 scaron
+vz 389,669,0,111,48,44 2 0005 zcaron
+:Y 556,861,0,94,6,44 2 0006 Ydieresis
+tm 980,672,0,15,15,15 2 0007 trademark
+space 250 0 0040
+! 333,670,10,13,4,13 2 0041 exclam
+" 420,673,0,32,-57,32 2 0042 quotedbl
+# 500,683,6,58,57,44 2 0043 numbersign
+sh "
+$ 500,735,102,31,37,31 3 0044 dollar
+Do "
+% 833,682,14,0,-13 2 0045 percent
+& 778,673,22,0,-10 2 0046 ampersand
+' 333,678,0,0,-19 2 0047 quoteright
+( 333,662,180,29,9,29 3 0050 parenleft
+) 333,664,178,3,31,3 3 0051 parenright
+* 500,684,0,0,-10 2 0052 asterisk
++ 675,505,0,0,-35 2 0053 plus
+, 250,94,126,0,-7 1 0054 comma
+- 333,254,0,0,-5 0 0055 hyphen
+hy "
+char173 "
+. 250,90,10,0,-25 0 0056 period
+/ 278,641,14,24,48,24 2 0057 slash
+sl "
+0 500,683,9,20,31,20 2 0060 zero
+1 500,684,0,0,19 2 0061 one
+2 500,682,0,0,57 2 0062 two
+3 500,682,12,0,57 2 0063 three
+4 500,681,0,4,58,4 2 0064 four
+5 500,666,15,12,62,12 2 0065 five
+6 500,685,8,47,26,44 2 0066 six
+7 500,666,12,62,-6,44 2 0067 seven
+8 500,681,7,25,38,25 2 0070 eight
+9 500,684,18,20,40,20 2 0071 nine
+: 333,444,10,1,-36,1 0 0072 colon
+; 333,441,124,9,-13,9 1 0073 semicolon
+< 675,515,7,0,-33 2 0074 less
+= 675,383,0,0,-35 0 0075 equal
+> 675,515,7,0,-32 2 0076 greater
+? 500,670,10,0,-55 2 0077 question
+@ 920,648,191,0,11 3 0100 at
+at "
+A 611,672,0,3,95,3 2 0101 A
+B 611,660,0,1,78,1 2 0102 B
+C 667,672,23,36,17,36 2 0103 C
+D 722,660,0,0,77 2 0104 D
+E 611,660,0,48,67,44 2 0105 E
+F 611,660,0,48,67,44 2 0106 F
+G 722,672,23,29,19,29 2 0107 G
+H 722,660,0,70,76,44 2 0110 H
+I 333,660,0,74,76,44 2 0111 I
+J 444,660,22,85,86,44 2 0112 J
+K 667,660,0,85,65,44 2 0113 K
+L 556,660,0,29,82,29 2 0114 L
+M 833,660,0,67,74,44 2 0115 M
+N 667,660,12,81,86,44 2 0116 N
+O 722,671,23,4,8,4 2 0117 O
+P 611,660,0,21,66,21 2 0120 P
+Q 722,671,186,9,9,9 3 0121 Q
+R 611,660,0,5,82,5 2 0122 R
+S 500,674,22,33,41,33 2 0123 S
+T 556,660,0,96,18,44 2 0124 T
+U 722,660,21,75,-27,44 2 0125 U
+V 611,660,20,98,6,44 2 0126 V
+W 833,660,20,92,15,44 2 0127 W
+X 611,660,0,72,95,44 2 0130 X
+Y 556,660,0,94,6,44 2 0131 Y
+Z 556,660,0,75,69,44 2 0132 Z
+[ 389,654,170,52,28,44 3 0133 bracketleft
+lB "
+\ 278,651,12,24,48,24 2 0134 backslash
+rs "
+] 389,654,170,2,81,2 3 0135 bracketright
+rB "
+a^ 333,669,0,91,-46,44 2 0136 circumflex
+^ "
+_ 500,0,252,60,59,44 1 0137 underscore
+` 333,677,0,3,-99,3 2 0140 quoteleft
+oq "
+a 500,446,11,24,35,24 0 0141 a
+b 500,682,12,25,26,25 2 0142 b
+c 444,446,11,26,18,26 0 0143 c
+d 500,684,11,71,35,44 2 0144 d
+e 444,446,13,18,16,18 0 0145 e
+f 278,684,207,187,198,44 3 0146 f
+g 500,445,209,21,40,21 1 0147 g
+h 500,684,10,23,27,23 2 0150 h
+i 278,660,10,35,7,35 2 0151 i
+j 278,660,207,59,159,44 3 0152 j
+k 444,685,12,66,34,44 2 0153 k
+l 278,685,10,48,9,44 2 0154 l
+m 722,447,10,26,39,26 0 0155 m
+n 500,447,10,21,27,21 0 0156 n
+o 500,448,13,17,23,17 0 0157 o
+p 500,446,206,15,125,15 1 0160 p
+q 500,445,206,33,30,33 1 0161 q
+r 389,446,0,53,26,44 0 0162 r
+s 389,446,14,28,34,28 0 0163 s
+t 278,548,10,60,12,44 2 0164 t
+u 500,447,11,22,8,22 0 0165 u
+v 444,444,11,29,26,29 0 0166 v
+w 667,447,10,33,36,33 0 0167 w
+x 444,446,10,56,81,44 0 0170 x
+y 444,445,209,26,77,26 1 0171 y
+z 389,434,0,41,48,41 0 0172 z
+lC 400,675,179,61,-15,44 3 0173 braceleft
+{ "
+ba 275,670,188,26,72,26 3 0174 bar
+| "
+rC 400,675,179,0,116 3 0175 braceright
+} "
+a~ 333,639,0,103,-64,44 2 0176 tilde
+~ "
+--- 333,94,126,0,-33 1 0200 quotesinglbase
+Fo 500,400,0,0,-4 0 0201 guillemotleft
+char171 "
+Fc 500,406,0,0,-9 0 0202 guillemotright
+char187 "
+bu 350,425 0 0203 bullet
+--- 500,688,189,42,47,42 3 0204 florin
+f/ 167,672,15,221,226,44 2 0205 fraction
+%0 1000,690,65,40,41,40 3 0206 perthousand
+dg 500,734,93,30,-42,30 3 0207 dagger
+dd 500,743,93,32,30,32 3 0210 daggerdbl
+en 500,242,0,51,53,44 0 0211 endash
+em 889,242,0,106,115,44 0 0212 emdash
+fi 500,684,207,18,186,18 3 0214 fi
+fl 500,684,207,59,190,44 3 0215 fl
+.i 278,447,10,0,3 0 0220 dotlessi
+ga 333,659,0,50,-110,44 2 0222 grave
+a" 333,749,0,65,-12,44 2 0223 hungarumlaut
+a. 333,625,0,0,-62 2 0224 dotaccent
+ab 333,645,0,118,-90,44 2 0225 breve
+ah 333,669,0,139,-88,44 2 0226 caron
+ao 333,703,0,150,-189,44 2 0227 ring
+ho 333,40,159,0,94 1 0230 ogonek
+lq 556,677,0,4,-116,4 2 0231 quotedblleft
+rq 556,678,0,0,-18 2 0232 quotedblright
+oe 667,445,15,26,24,26 0 0233 oe
+/l 278,685,10,36,50,36 2 0234 lslash
+--- 556,94,126,0,-13 1 0235 quotedblbase
+OE 944,668,10,49,20,44 2 0236 OE
+/L 556,660,0,61,50,44 2 0237 Lslash
+r! 389,468,213,0,-9 1 0241 exclamdown
+char161 "
+ct 500,564,146,0,-12 3 0242 cent
+char162 "
+Po 500,672,9,48,55,44 2 0243 sterling
+char163 "
+Cs 500,604,0,48,47,44 2 0244 currency
+char164 "
+Ye 500,684,0,159,37,44 2 0245 yen
+char165 "
+sc 500,743,96,5,8,5 3 0247 section
+char167 "
+ad 333,634,0,106,-67,44 2 0250 dieresis
+char168 "
+co 760,672,22,9,10,9 2 0251 copyright
+char169 "
+Of 276,677,0,84,18,44 2 0252 ordfeminine
+char170 "
+fo 333,400,0,2,-10,2 0 0253 guilsinglleft
+no 675,383,0,0,-35 0 0254 logicalnot
+char172 "
+\- 675,286,0,0,-35 0 0255 minus
+rg 760,672,22,9,10,9 2 0256 registered
+char174 "
+a- 333,603,0,97,-70,44 2 0257 macron
+char175 "
+aa 333,680,0,92,-104,44 2 0264 acute
+char180 "
+ps 523,675,196,60,-37,44 3 0266 paragraph
+char182 "
+char183 250,316,0,0,-25 0 0267 periodcentered
+ac 333,0,206,0,80 1 0270 cedilla
+char184 "
+Om 310,679,0,50,5,44 2 0272 ordmasculine
+char186 "
+fc 333,406,0,0,1 0 0273 guilsinglright
+r? 500,462,215,0,-5 1 0277 questiondown
+char191 "
+`A 611,886,0,3,95,3 2 0300 Agrave
+char192 "
+'A 611,907,0,3,95,3 2 0301 Aacute
+char193 "
+^A 611,896,0,3,95,3 2 0302 Acircumflex
+char194 "
+~A 611,866,0,3,95,3 2 0303 Atilde
+char195 "
+:A 611,861,0,3,95,3 2 0304 Adieresis
+char196 "
+oA 611,930,0,3,95,3 2 0305 Aring
+char197 "
+AE 889,660,0,50,96,44 2 0306 AE
+char198 "
+,C 667,672,206,36,17,36 3 0307 Ccedilla
+char199 "
+`E 611,886,0,48,67,44 2 0310 Egrave
+char200 "
+'E 611,907,0,48,67,44 2 0311 Eacute
+char201 "
+^E 611,896,0,48,67,44 2 0312 Ecircumflex
+char202 "
+:E 611,861,0,48,67,44 2 0313 Edieresis
+char203 "
+`I 333,886,0,74,76,44 2 0314 Igrave
+char204 "
+'I 333,907,0,106,76,44 2 0315 Iacute
+char205 "
+^I 333,896,0,105,76,44 2 0316 Icircumflex
+char206 "
+:I 333,861,0,120,76,44 2 0317 Idieresis
+char207 "
+~N 667,866,12,81,86,44 2 0321 Ntilde
+char209 "
+`O 722,886,23,4,8,4 2 0322 Ograve
+char210 "
+'O 722,907,23,4,8,4 2 0323 Oacute
+char211 "
+^O 722,896,23,4,8,4 2 0324 Ocircumflex
+char212 "
+~O 722,866,23,4,8,4 2 0325 Otilde
+char213 "
+:O 722,861,23,4,8,4 2 0326 Odieresis
+char214 "
+/O 722,738,110,11,10,11 3 0330 Oslash
+char216 "
+`U 722,886,21,75,-27,44 2 0331 Ugrave
+char217 "
+'U 722,907,21,75,-27,44 2 0332 Uacute
+char218 "
+^U 722,896,21,75,-27,44 2 0333 Ucircumflex
+char219 "
+:U 722,861,21,75,-27,44 2 0334 Udieresis
+char220 "
+ss 500,684,209,42,217,42 3 0337 germandbls
+char223 "
+`a 500,659,11,24,35,24 2 0340 agrave
+char224 "
+'a 500,680,11,24,35,24 2 0341 aacute
+char225 "
+^a 500,669,11,24,35,24 2 0342 acircumflex
+char226 "
+~a 500,639,11,26,35,26 2 0343 atilde
+char227 "
+:a 500,634,11,29,35,29 2 0344 adieresis
+char228 "
+oa 500,703,11,24,35,24 2 0345 aring
+char229 "
+ae 667,448,12,21,26,21 0 0346 ae
+char230 "
+,c 444,446,206,26,18,26 1 0347 ccedilla
+char231 "
+`e 444,659,13,18,16,18 2 0350 egrave
+char232 "
+'e 444,680,13,50,16,44 2 0351 eacute
+char233 "
+^e 444,669,13,49,16,44 2 0352 ecircumflex
+char234 "
+:e 444,634,13,64,16,44 2 0353 edieresis
+char235 "
+`i 278,659,10,71,3,44 2 0354 igrave
+char236 "
+'i 278,680,10,113,3,44 2 0355 iacute
+char237 "
+^i 278,669,10,112,3,44 2 0356 icircumflex
+char238 "
+:i 278,634,10,127,3,44 2 0357 idieresis
+char239 "
+~n 500,639,10,21,27,21 2 0361 ntilde
+char241 "
+`o 500,659,13,17,23,17 2 0362 ograve
+char242 "
+'o 500,680,13,17,23,17 2 0363 oacute
+char243 "
+^o 500,669,13,17,23,17 2 0364 ocircumflex
+char244 "
+~o 500,639,13,26,23,26 2 0365 otilde
+char245 "
+:o 500,634,13,29,23,29 2 0366 odieresis
+char246 "
+/o 500,560,132,18,22,18 3 0370 oslash
+char248 "
+`u 500,659,11,22,8,22 2 0371 ugrave
+char249 "
+'u 500,680,11,22,8,22 2 0372 uacute
+char250 "
+^u 500,669,11,22,8,22 2 0373 ucircumflex
+char251 "
+:u 500,634,11,23,8,23 2 0374 udieresis
+char252 "
+:y 444,634,209,51,77,44 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/TR b/ps/devps/TR
new file mode 100644
index 000000000..8239c88a8
--- /dev/null
+++ b/ps/devps/TR
@@ -0,0 +1,418 @@
+name TR
+internalname Times-Roman
+spacewidth 250
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y -92
+A w -92
+A v -74
+A ' -111
+A Y -92
+A W -80
+A V -129
+A T -111
+F . -80
+F , -80
+F A -74
+L y -55
+L ' -92
+L Y -100
+L W -74
+L V -92
+L T -92
+P . -111
+P , -111
+P A -92
+R y -40
+R Y -55
+R W -55
+R V -80
+R T -60
+T y -70
+T w -70
+T u -35
+T ; -55
+T s -70
+T r -35
+T . -74
+T o -70
+T i -35
+T - -92
+T hy -92
+T char173 -92
+T e -70
+T , -74
+T : -50
+T c -70
+T a -70
+T O -18
+T A -80
+V y -111
+V u -60
+V ; -74
+V r -60
+V . -129
+V o -129
+V i -60
+V - -92
+V hy -92
+V char173 -92
+V e -111
+V , -129
+V : -74
+V a -111
+V A -129
+W y -60
+W u -40
+W ; -37
+W r -40
+W . -92
+W o -80
+W i -40
+W - -55
+W hy -55
+W char173 -55
+W e -80
+W , -92
+W : -37
+W a -80
+W A -111
+Y v -100
+Y u -111
+Y ; -92
+Y q -111
+Y . -129
+Y p -92
+Y o -100
+Y i -55
+Y - -111
+Y hy -111
+Y char173 -111
+Y e -100
+Y , -129
+Y : -92
+Y a -100
+Y A -111
+f ' 55
+f f -18
+1 1 -37
+` ` -74
+` oq -74
+oq ` -74
+oq oq -74
+' t -18
+' s -55
+' ' -74
+r ' 37
+r . -55
+r - -20
+r hy -20
+r char173 -20
+r g -18
+r , -40
+v . -65
+v , -65
+w . -65
+w , -65
+y . -65
+y , -65
+charset
+ha 469,662 2 0000 asciicircum
+ti 541,347 0 0001 asciitilde
+vS 556,888,14 2 0002 Scaron
+vZ 611,888 2 0003 Zcaron
+vs 389,674,10 2 0004 scaron
+vz 444,674 2 0005 zcaron
+:Y 722,837 2 0006 Ydieresis
+tm 980,662 2 0007 trademark
+space 250 0 0040
+! 333,676,14 2 0041 exclam
+" 408,685 2 0042 quotedbl
+# 500,662 2 0043 numbersign
+sh "
+$ 500,727,87 3 0044 dollar
+Do "
+% 833,676,14 2 0045 percent
+& 778,676,14 2 0046 ampersand
+' 333,676 2 0047 quoteright
+( 333,676,177 3 0050 parenleft
+) 333,676,177 3 0051 parenright
+* 500,683 2 0052 asterisk
++ 564,512 2 0053 plus
+, 250,101,143 1 0054 comma
+- 333,257 0 0055 hyphen
+hy "
+char173 "
+. 250,101,14 0 0056 period
+/ 278,682,108 3 0057 slash
+sl "
+0 500,676,14 2 0060 zero
+1 500,676 2 0061 one
+2 500,676 2 0062 two
+3 500,676,14 2 0063 three
+4 500,676 2 0064 four
+5 500,688,14 2 0065 five
+6 500,682,14 2 0066 six
+7 500,662,14 2 0067 seven
+8 500,676,14 2 0070 eight
+9 500,676,22 2 0071 nine
+: 278,458,14 0 0072 colon
+; 278,458,143 1 0073 semicolon
+< 564,522 2 0074 less
+= 564,390 0 0075 equal
+> 564,522 2 0076 greater
+? 444,676,14 2 0077 question
+@ 921,675,155 3 0100 at
+at "
+A 722,676 2 0101 A
+B 667,662 2 0102 B
+C 667,676,14 2 0103 C
+D 722,662 2 0104 D
+E 611,662 2 0105 E
+F 556,662 2 0106 F
+G 722,676,14 2 0107 G
+H 722,662 2 0110 H
+I 333,662 2 0111 I
+J 389,662,14 2 0112 J
+K 722,662 2 0113 K
+L 611,662 2 0114 L
+M 889,662 2 0115 M
+N 722,662,14 2 0116 N
+O 722,676,14 2 0117 O
+P 556,662 2 0120 P
+Q 722,676,177 3 0121 Q
+R 667,662 2 0122 R
+S 556,676,14 2 0123 S
+T 611,662 2 0124 T
+U 722,662,14 2 0125 U
+V 722,662,14 2 0126 V
+W 944,662,14 2 0127 W
+X 722,662 2 0130 X
+Y 722,662 2 0131 Y
+Z 611,662 2 0132 Z
+[ 333,662,156 3 0133 bracketleft
+lB "
+\ 278,682 2 0134 backslash
+rs "
+] 333,662,156 3 0135 bracketright
+rB "
+a^ 333,674 2 0136 circumflex
+^ "
+_ 500,0,133 1 0137 underscore
+` 333,676 2 0140 quoteleft
+oq "
+a 444,458,10 0 0141 a
+b 500,682,10 2 0142 b
+c 444,458,10 0 0143 c
+d 500,682,13 2 0144 d
+e 444,458,10 0 0145 e
+f 333,682 2 0146 f
+g 500,458,217 1 0147 g
+h 500,682 2 0150 h
+i 278,682 2 0151 i
+j 278,682,217 3 0152 j
+k 500,682 2 0153 k
+l 278,682 2 0154 l
+m 778,458 0 0155 m
+n 500,458 0 0156 n
+o 500,458,10 0 0157 o
+p 500,458,217 1 0160 p
+q 500,459,217 1 0161 q
+r 333,458 0 0162 r
+s 389,458,10 0 0163 s
+t 278,580,10 2 0164 t
+u 500,448,10 0 0165 u
+v 500,448,10 0 0166 v
+w 722,448,10 0 0167 w
+x 500,448 0 0170 x
+y 500,448,217 1 0171 y
+z 444,448 0 0172 z
+lC 480,682,165 3 0173 braceleft
+{ "
+ba 200,682 2 0174 bar
+| "
+rC 480,682,165 3 0175 braceright
+} "
+a~ 333,638 2 0176 tilde
+~ "
+--- 333,101,143 1 0200 quotesinglbase
+Fo 500,422 0 0201 guillemotleft
+char171 "
+Fc 500,422 0 0202 guillemotright
+char187 "
+bu 350,425 0 0203 bullet
+--- 500,676,185 3 0204 florin
+f/ 167,676,14 2 0205 fraction
+%0 1000,676,14 2 0206 perthousand
+dg 500,676,149 3 0207 dagger
+dd 500,676,153 3 0210 daggerdbl
+en 500,250 0 0211 endash
+em 1000,250 0 0212 emdash
+fi 556,678 2 0214 fi
+fl 556,682 2 0215 fl
+.i 278,458 0 0220 dotlessi
+ga 333,678 2 0222 grave
+a" 333,700 2 0223 hungarumlaut
+a. 333,623 2 0224 dotaccent
+ab 333,664 2 0225 breve
+ah 333,674 2 0226 caron
+ao 333,682 2 0227 ring
+ho 333,0,155 1 0230 ogonek
+lq 444,676 2 0231 quotedblleft
+rq 444,676 2 0232 quotedblright
+oe 722,458,10 0 0233 oe
+/l 278,682 2 0234 lslash
+--- 444,101,143 1 0235 quotedblbase
+OE 889,669,7 2 0236 OE
+/L 611,662 2 0237 Lslash
+r! 333,458,217 1 0241 exclamdown
+char161 "
+ct 500,579,138 3 0242 cent
+char162 "
+Po 500,676,14 2 0243 sterling
+char163 "
+Cs 500,600 2 0244 currency
+char164 "
+Ye 500,662 2 0245 yen
+char165 "
+sc 500,676,148 3 0247 section
+char167 "
+ad 333,623 2 0250 dieresis
+char168 "
+co 760,676,14 2 0251 copyright
+char169 "
+Of 276,676 2 0252 ordfeminine
+char170 "
+fo 333,422 0 0253 guilsinglleft
+no 564,390 0 0254 logicalnot
+char172 "
+\- 564,293 0 0255 minus
+rg 760,676,14 2 0256 registered
+char174 "
+a- 333,601 2 0257 macron
+char175 "
+aa 333,678 2 0264 acute
+char180 "
+ps 453,662,207 3 0266 paragraph
+char182 "
+char183 250,319 0 0267 periodcentered
+ac 333,0,215 1 0270 cedilla
+char184 "
+Om 310,676 2 0272 ordmasculine
+char186 "
+fc 333,423 0 0273 guilsinglright
+r? 444,458,217 1 0277 questiondown
+char191 "
+`A 722,892 2 0300 Agrave
+char192 "
+'A 722,892 2 0301 Aacute
+char193 "
+^A 722,888 2 0302 Acircumflex
+char194 "
+~A 722,852 2 0303 Atilde
+char195 "
+:A 722,837 2 0304 Adieresis
+char196 "
+oA 722,896 2 0305 Aring
+char197 "
+AE 889,662 2 0306 AE
+char198 "
+,C 667,676,215 3 0307 Ccedilla
+char199 "
+`E 611,892 2 0310 Egrave
+char200 "
+'E 611,892 2 0311 Eacute
+char201 "
+^E 611,888 2 0312 Ecircumflex
+char202 "
+:E 611,837 2 0313 Edieresis
+char203 "
+`I 333,892 2 0314 Igrave
+char204 "
+'I 333,892 2 0315 Iacute
+char205 "
+^I 333,888 2 0316 Icircumflex
+char206 "
+:I 333,837 2 0317 Idieresis
+char207 "
+~N 722,852,14 2 0321 Ntilde
+char209 "
+`O 722,892,14 2 0322 Ograve
+char210 "
+'O 722,892,14 2 0323 Oacute
+char211 "
+^O 722,888,14 2 0324 Ocircumflex
+char212 "
+~O 722,852,14 2 0325 Otilde
+char213 "
+:O 722,837,14 2 0326 Odieresis
+char214 "
+/O 722,734,80 3 0330 Oslash
+char216 "
+`U 722,892,14 2 0331 Ugrave
+char217 "
+'U 722,892,14 2 0332 Uacute
+char218 "
+^U 722,888,14 2 0333 Ucircumflex
+char219 "
+:U 722,837,14 2 0334 Udieresis
+char220 "
+ss 500,682,10 2 0337 germandbls
+char223 "
+`a 444,678,10 2 0340 agrave
+char224 "
+'a 444,678,10 2 0341 aacute
+char225 "
+^a 444,674,10 2 0342 acircumflex
+char226 "
+~a 444,638,10 2 0343 atilde
+char227 "
+:a 444,623,10 2 0344 adieresis
+char228 "
+oa 444,682,10 2 0345 aring
+char229 "
+ae 667,458,10 0 0346 ae
+char230 "
+,c 444,458,215 1 0347 ccedilla
+char231 "
+`e 444,678,10 2 0350 egrave
+char232 "
+'e 444,678,10 2 0351 eacute
+char233 "
+^e 444,674,10 2 0352 ecircumflex
+char234 "
+:e 444,623,10 2 0353 edieresis
+char235 "
+`i 278,678 2 0354 igrave
+char236 "
+'i 278,678 2 0355 iacute
+char237 "
+^i 278,674 2 0356 icircumflex
+char238 "
+:i 278,623 2 0357 idieresis
+char239 "
+~n 500,638 2 0361 ntilde
+char241 "
+`o 500,678,10 2 0362 ograve
+char242 "
+'o 500,678,10 2 0363 oacute
+char243 "
+^o 500,674,10 2 0364 ocircumflex
+char244 "
+~o 500,638,10 2 0365 otilde
+char245 "
+:o 500,623,10 2 0366 odieresis
+char246 "
+/o 500,549,108 3 0370 oslash
+char248 "
+`u 500,678,10 2 0371 ugrave
+char249 "
+'u 500,678,10 2 0372 uacute
+char250 "
+^u 500,674,10 2 0373 ucircumflex
+char251 "
+:u 500,623,10 2 0374 udieresis
+char252 "
+:y 500,623,217 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/ZCMI b/ps/devps/ZCMI
new file mode 100644
index 000000000..fb4358fc6
--- /dev/null
+++ b/ps/devps/ZCMI
@@ -0,0 +1,420 @@
+name ZCMI
+internalname ZapfChancery-MediumItalic
+slant 14
+spacewidth 220
+encoding text.enc
+ligatures fi fl 0
+kernpairs
+A y 3
+A w -12
+A v -10
+A Y 19
+A W -21
+A V -20
+A T 25
+F . -137
+F , -63
+F A -64
+L y 0
+L Y -20
+L W -40
+L V -40
+L T -20
+P . -66
+P , -69
+P A 16
+R y 32
+R Y 70
+R W 6
+R V 6
+R T 51
+S t -20
+S p -10
+T y 2
+T w 0
+T u -1
+T ; 50
+T s 0
+T r -2
+T . -2
+T o -19
+T i 40
+T - -10
+T hy -10
+T char173 -10
+T e -15
+T , 29
+T : 34
+T c -6
+T a -8
+T A 15
+V y 0
+V u 0
+V r 0
+V o -50
+V i 21
+V e -50
+V a -50
+V ; 27
+V . -72
+V - -10
+V hy -10
+V char173 -10
+V , -16
+V : -11
+V A -14
+W y 15
+W u 15
+W ; 4
+W r 15
+W . -96
+W o -25
+W i 15
+W - -10
+W hy -10
+W char173 -10
+W e -25
+W , -38
+W : -24
+W a -25
+W A -7
+Y v -22
+Y u -8
+Y ; 25
+Y q -64
+Y . -65
+Y p -4
+Y o -65
+Y i 40
+Y - -10
+Y hy -10
+Y char173 -10
+Y e -60
+Y , 0
+Y : -4
+Y a -63
+Y A -24
+a t -10
+d t -10
+f f -57
+g g 20
+o t -20
+r r 20
+r q 0
+r . -45
+r o -8
+r n 45
+r m 45
+r l 10
+r - 10
+r hy 10
+r char173 10
+r h 3
+r g -30
+r f 11
+r e -12
+r d -25
+r , -40
+r c -16
+s t -20
+v y 20
+w e 20
+charset
+ha 520,681,0,2,-162,2 2 0000 asciicircum
+ti 520,261,0,56,-36,56 0 0001 asciitilde
+vS 460,811,67,197,50,81 3 0002 Scaron
+vZ 620,811,18,105,-12,81 2 0003 Zcaron
+vs 320,640,15,167,9,81 2 0004 scaron
+vz 440,640,12,107,4,81 2 0005 zcaron
+:Y 560,773,152,253,19,81 3 0006 Ydieresis
+tm 1000,673,0,100,-115,81 2 0007 trademark
+space 220 0 0040
+! 280,588,9,117,-59,81 2 0041 exclam
+" 220,681,0,163,-120,81 2 0042 quotedbl
+# 680,574,1,35,-72,35 2 0043 numbersign
+sh "
+$ 440,675,145,71,-10,71 3 0044 dollar
+Do "
+% 680,585,7,71,-112,71 2 0045 percent
+& 780,558,15,125,-71,81 2 0046 ampersand
+' 240,675,0,168,-178,81 2 0047 quoteright
+( 260,667,165,202,-66,81 3 0050 parenleft
+) 220,668,164,143,33,81 3 0051 parenright
+* 420,676,0,142,-170,81 2 0052 asterisk
++ 520,410,0,43,-53,43 0 0053 plus
+, 220,171,98,45,20,45 1 0054 comma
+- 280,236,0,70,-65,70 0 0055 hyphen
+hy "
+char173 "
+. 220,127,9,58,-57,58 0 0056 period
+/ 340,677,168,245,56,81 3 0057 slash
+sl "
+0 380,576,7,134,-19,81 2 0060 zero
+1 440,584,0,24,-12,24 2 0061 one
+2 440,572,17,59,23,59 2 0062 two
+3 440,576,6,47,34,47 2 0063 three
+4 440,585,26,65,-19,65 2 0064 four
+5 440,641,8,155,3,81 2 0065 five
+6 440,585,8,142,-45,81 2 0066 six
+7 440,612,18,151,-115,81 2 0067 seven
+8 440,573,9,104,-25,81 2 0070 eight
+9 440,576,8,90,8,81 2 0071 nine
+: 260,408,9,79,-48,79 0 0072 colon
+; 240,408,98,87,21,81 1 0073 semicolon
+< 520,429,17,87,-70,81 0 0074 less
+= 520,297,0,48,-58,48 0 0075 equal
+> 520,429,17,86,-69,81 0 0076 greater
+? 380,583,9,167,-80,81 2 0077 question
+@ 700,690,15,75,-12,75 2 0100 at
+at "
+A 620,593,16,117,27,81 2 0101 A
+B 600,618,0,92,-25,81 2 0102 B
+C 520,592,73,165,-38,81 3 0103 C
+D 700,618,0,109,-36,81 2 0104 D
+E 620,587,8,98,-26,81 2 0105 E
+F 580,621,114,203,-10,81 3 0106 F
+G 620,592,236,100,-68,81 3 0107 G
+H 680,681,7,278,17,81 2 0110 H
+I 380,573,0,151,-31,81 2 0111 I
+J 400,573,122,141,49,81 3 0112 J
+K 660,578,143,241,-38,81 3 0113 K
+L 580,577,6,101,17,81 2 0114 L
+M 840,671,9,219,-8,81 2 0115 M
+N 700,682,158,265,10,81 3 0116 N
+O 600,589,13,102,-54,81 2 0117 O
+P 540,602,0,155,13,81 2 0120 P
+Q 600,589,189,301,-54,81 3 0121 Q
+R 600,615,147,276,12,81 3 0122 R
+S 460,590,67,107,50,81 3 0123 S
+T 500,650,0,268,-8,81 2 0124 T
+U 740,581,10,88,-66,81 2 0125 U
+V 640,689,8,219,-54,81 2 0126 V
+W 880,677,17,205,-54,81 2 0127 W
+X 560,580,13,161,50,81 2 0130 X
+Y 560,608,152,253,19,81 3 0131 Y
+Z 620,598,18,105,-12,81 2 0132 Z
+[ 240,667,164,224,-7,81 3 0133 bracketleft
+lB "
+\ 480,578,0,54,-135,54 2 0134 backslash
+rs "
+] 320,667,164,151,-13,81 3 0135 bracketright
+rB "
+a^ 340,640,0,107,-133,81 2 0136 circumflex
+^ "
+_ 500,0,145,50,50,50 1 0137 underscore
+` 240,675,0,194,-179,81 2 0140 quoteleft
+oq "
+a 420,415,11,122,-37,81 0 0141 a
+b 420,678,17,96,-37,81 2 0142 b
+c 340,411,13,93,-37,81 0 0143 c
+d 440,678,12,239,-37,81 2 0144 d
+e 340,412,12,90,-37,81 0 0145 e
+f 320,680,247,266,154,81 3 0146 f
+g 400,409,248,131,128,81 1 0147 g
+h 440,679,15,116,-25,81 2 0150 h
+i 240,617,12,121,-31,81 2 0151 i
+j 220,617,247,133,183,81 3 0152 j
+k 440,675,148,265,-37,81 3 0153 k
+l 240,681,11,244,-37,81 2 0154 l
+m 620,413,10,123,-31,81 0 0155 m
+n 460,411,15,110,-31,81 0 0156 n
+o 400,411,13,99,-37,81 0 0157 o
+p 440,411,248,92,58,81 1 0160 p
+q 400,480,250,135,-37,81 3 0161 q
+r 300,411,10,159,-31,81 0 0162 r
+s 320,410,15,107,9,81 0 0163 s
+t 320,504,12,125,-49,81 2 0164 t
+u 460,416,12,108,-32,81 0 0165 u
+v 440,438,13,120,-37,81 0 0166 v
+w 680,445,13,127,-37,81 0 0167 w
+x 420,408,159,213,-20,81 1 0170 x
+y 400,415,249,123,39,81 1 0171 y
+z 440,426,12,85,4,81 0 0172 z
+lC 240,667,164,225,-65,81 3 0173 braceleft
+{ "
+ba 520,578,0,0,-264 2 0174 bar
+| "
+rC 240,667,163,137,20,81 3 0175 braceright
+} "
+a~ 440,602,0,123,-133,81 2 0176 tilde
+~ "
+--- 180,111,109,40,10,40 1 0200 quotesinglbase
+Fo 340,414,9,114,-48,81 0 0201 guillemotleft
+char171 "
+Fc 380,424,2,77,-37,77 0 0202 guillemotright
+char187 "
+bu 600,458,0,18,-178,18 0 0203 bullet
+--- 400,682,247,217,119,81 3 0204 florin
+f/ 60,585,4,270,165,81 2 0205 fraction
+%0 960,585,7,65,-112,65 2 0206 perthousand
+dg 460,607,138,119,-48,81 3 0207 dagger
+dd 480,608,138,99,-48,81 3 0210 daggerdbl
+en 500,236,0,35,-66,35 0 0211 endash
+em 1000,236,0,0,-333 0 0212 emdash
+fi 520,681,248,136,154,81 3 0214 fi
+fl 520,690,247,240,155,81 3 0215 fl
+.i 240,412,11,121,-31,81 0 0220 dotlessi
+ga 220,639,0,111,-123,81 2 0222 grave
+a" 400,639,0,113,-133,81 2 0223 hungarumlaut
+a. 220,602,0,139,-156,81 2 0224 dotaccent
+ab 440,597,0,126,-133,81 2 0225 breve
+ah 340,640,0,157,-184,81 2 0226 caron
+ao 300,631,0,159,-178,81 2 0227 ring
+ho 280,0,193,0,-8 1 0230 ogonek
+lq 340,675,0,210,-179,81 2 0231 quotedblleft
+rq 360,675,0,163,-178,81 2 0232 quotedblright
+oe 560,409,13,92,-38,81 0 0233 oe
+/l 300,681,11,241,-37,81 2 0234 lslash
+--- 280,111,109,56,9,56 1 0235 quotedblbase
+OE 820,588,12,100,-54,81 2 0236 OE
+/L 580,577,6,101,17,81 2 0237 Lslash
+r! 280,410,187,65,-7,65 1 0241 exclamdown
+char161 "
+ct 440,647,76,63,-77,63 3 0242 cent
+char162 "
+Po 480,578,53,84,85,81 2 0243 sterling
+char163 "
+Cs 600,457,44,55,-50,55 0 0244 currency
+char164 "
+Ye 720,607,153,161,-54,81 3 0245 yen
+char165 "
+sc 420,576,165,94,-13,81 3 0247 section
+char167 "
+ad 360,602,0,109,-133,81 2 0250 dieresis
+char168 "
+co 740,683,10,140,-86,81 2 0251 copyright
+char169 "
+Of 260,576,0,114,-31,81 2 0252 ordfeminine
+char170 "
+fo 240,415,9,95,-48,81 0 0253 guilsinglleft
+no 600,297,0,56,-52,56 0 0254 logicalnot
+char172 "
+\- 600,230,0,105,-101,81 0 0255 minus
+rg 740,683,10,138,-85,81 2 0256 registered
+char174 "
+a- 440,569,0,125,-133,81 2 0257 macron
+char175 "
+aa 300,639,0,138,-155,81 2 0264 acute
+char180 "
+ps 500,585,247,200,21,81 3 0266 paragraph
+char182 "
+char183 220,283,0,59,-58,59 0 0267 periodcentered
+ac 300,6,189,0,9 1 0270 cedilla
+char184 "
+Om 260,576,0,113,-48,81 2 0272 ordmasculine
+char186 "
+fc 260,424,2,74,-36,74 0 0273 guilsinglright
+r? 400,410,181,75,-7,75 1 0277 questiondown
+char191 "
+`A 620,810,16,117,27,81 2 0300 Agrave
+char192 "
+'A 620,810,16,117,27,81 2 0301 Aacute
+char193 "
+^A 620,811,16,117,27,81 2 0302 Acircumflex
+char194 "
+~A 620,773,16,183,27,81 2 0303 Atilde
+char195 "
+:A 620,773,16,129,27,81 2 0304 Adieresis
+char196 "
+oA 620,802,16,117,27,81 2 0305 Aring
+char197 "
+AE 740,571,5,108,18,81 2 0306 AE
+char198 "
+,C 520,592,257,165,-38,81 3 0307 Ccedilla
+char199 "
+`E 620,810,8,98,-26,81 2 0310 Egrave
+char200 "
+'E 620,810,8,98,-26,81 2 0311 Eacute
+char201 "
+^E 620,811,8,98,-26,81 2 0312 Ecircumflex
+char202 "
+:E 620,773,8,98,-26,81 2 0313 Edieresis
+char203 "
+`I 380,810,0,151,-31,81 2 0314 Igrave
+char204 "
+'I 380,810,0,198,-31,81 2 0315 Iacute
+char205 "
+^I 380,811,0,157,-31,81 2 0316 Icircumflex
+char206 "
+:I 380,773,0,151,-31,81 2 0317 Idieresis
+char207 "
+~N 700,773,158,265,10,81 3 0321 Ntilde
+char209 "
+`O 600,810,13,102,-54,81 2 0322 Ograve
+char210 "
+'O 600,810,13,102,-54,81 2 0323 Oacute
+char211 "
+^O 600,811,13,102,-54,81 2 0324 Ocircumflex
+char212 "
+~O 600,773,13,128,-54,81 2 0325 Otilde
+char213 "
+:O 600,773,13,102,-54,81 2 0326 Odieresis
+char214 "
+/O 660,687,128,97,-60,81 3 0330 Oslash
+char216 "
+`U 740,810,10,88,-66,81 2 0331 Ugrave
+char217 "
+'U 740,810,10,88,-66,81 2 0332 Uacute
+char218 "
+^U 740,811,10,88,-66,81 2 0333 Ucircumflex
+char219 "
+:U 740,773,10,88,-66,81 2 0334 Udieresis
+char220 "
+ss 420,681,248,136,177,81 3 0337 germandbls
+char223 "
+`a 420,639,11,122,-37,81 2 0340 agrave
+char224 "
+'a 420,639,11,122,-37,81 2 0341 aacute
+char225 "
+^a 420,640,11,122,-37,81 2 0342 acircumflex
+char226 "
+~a 420,602,11,133,-37,81 2 0343 atilde
+char227 "
+:a 420,602,11,122,-37,81 2 0344 adieresis
+char228 "
+oa 420,631,11,122,-37,81 2 0345 aring
+char229 "
+ae 540,441,13,98,-37,81 0 0346 ae
+char230 "
+,c 340,411,189,93,-11,81 1 0347 ccedilla
+char231 "
+`e 340,639,12,90,-37,81 2 0350 egrave
+char232 "
+'e 340,639,12,118,-37,81 2 0351 eacute
+char233 "
+^e 340,640,12,107,-37,81 2 0352 ecircumflex
+char234 "
+:e 340,602,12,119,-37,81 2 0353 edieresis
+char235 "
+`i 240,639,11,121,-31,81 2 0354 igrave
+char236 "
+'i 240,639,11,168,-31,81 2 0355 iacute
+char237 "
+^i 240,640,11,157,-31,81 2 0356 icircumflex
+char238 "
+:i 240,602,11,169,-31,81 2 0357 idieresis
+char239 "
+~n 460,602,15,113,-31,81 2 0361 ntilde
+char241 "
+`o 400,639,13,99,-37,81 2 0362 ograve
+char242 "
+'o 400,639,13,99,-37,81 2 0363 oacute
+char243 "
+^o 400,640,13,99,-37,81 2 0364 ocircumflex
+char244 "
+~o 400,602,13,143,-37,81 2 0365 otilde
+char245 "
+:o 400,602,13,99,-37,81 2 0366 odieresis
+char246 "
+/o 440,545,147,77,-42,77 3 0370 oslash
+char248 "
+`u 460,639,12,108,-32,81 2 0371 ugrave
+char249 "
+'u 460,639,12,108,-32,81 2 0372 uacute
+char250 "
+^u 460,640,12,108,-32,81 2 0373 ucircumflex
+char251 "
+:u 460,602,12,108,-32,81 2 0374 udieresis
+char252 "
+:y 400,602,249,123,39,81 3 0377 ydieresis
+char255 "
diff --git a/ps/devps/ZD b/ps/devps/ZD
new file mode 100644
index 000000000..4cd930d7f
--- /dev/null
+++ b/ps/devps/ZD
@@ -0,0 +1,193 @@
+name ZD
+internalname ZapfDingbats
+special
+spacewidth 278
+charset
+space 278 0 0040
+--- 974,621 2 0041 a1
+--- 961,611 2 0042 a2
+--- 974,621 2 0043 a202
+--- 980,692 2 0044 a3
+--- 719,566 2 0045 a4
+--- 789,705,14 2 0046 a5
+--- 790,705,14 2 0047 a119
+--- 791,705,14 2 0050 a118
+--- 690,553 2 0051 a117
+--- 960,568 2 0052 a11
+rh 939,559 2 0053 a12
+--- 549,705,11 2 0054 a13
+--- 855,632 2 0055 a14
+--- 911,642 2 0056 a15
+--- 933,550 2 0057 a16
+--- 911,642 2 0060 a105
+--- 945,553 2 0061 a17
+--- 974,587 2 0062 a18
+OK 755,705,13 2 0063 a19
+--- 846,705,14 2 0064 a20
+--- 762,692 2 0065 a21
+--- 761,692 2 0066 a22
+--- 571,661,68 3 0067 a23
+--- 677,705,14 2 0070 a24
+--- 763,692 2 0071 a25
+--- 760,692 2 0072 a26
+--- 759,692 2 0073 a27
+--- 754,692 2 0074 a28
+--- 494,692 2 0075 a6
+--- 552,692 2 0076 a7
+--- 537,692 2 0077 a8
+--- 577,596 2 0100 a9
+--- 692,705,14 2 0101 a10
+--- 786,705,14 2 0102 a29
+--- 788,705,14 2 0103 a30
+--- 788,705,14 2 0104 a31
+--- 790,705,14 2 0105 a32
+--- 793,705,14 2 0106 a33
+--- 794,705,14 2 0107 a34
+--- 816,705,14 2 0110 a35
+--- 823,705,14 2 0111 a36
+--- 789,705,14 2 0112 a37
+--- 841,705,14 2 0113 a38
+--- 823,705,14 2 0114 a39
+--- 833,705,14 2 0115 a40
+--- 816,705,13 2 0116 a41
+--- 831,705,14 2 0117 a42
+--- 923,705,14 2 0120 a43
+--- 744,692 2 0121 a44
+--- 723,692 2 0122 a45
+--- 749,692 2 0123 a46
+--- 790,705,14 2 0124 a47
+--- 792,705,14 2 0125 a48
+--- 695,705,14 2 0126 a49
+--- 776,699,6 2 0127 a50
+--- 768,699,7 2 0130 a51
+--- 792,705,14 2 0131 a52
+--- 759,692 2 0132 a53
+--- 707,705,14 2 0133 a54
+--- 708,705,14 2 0134 a55
+--- 682,705,14 2 0135 a56
+--- 701,705,14 2 0136 a57
+--- 826,705,14 2 0137 a58
+--- 815,705,14 2 0140 a59
+--- 789,705,14 2 0141 a60
+--- 789,705,14 2 0142 a61
+--- 707,705,14 2 0143 a62
+--- 687,692 2 0144 a63
+--- 696,691 2 0145 a64
+--- 689,692 2 0146 a65
+--- 786,705,14 2 0147 a66
+--- 787,705,14 2 0150 a67
+--- 713,705,14 2 0151 a68
+--- 791,705,14 2 0152 a69
+--- 785,705,14 2 0153 a70
+--- 791,705,14 2 0154 a71
+--- 873,705,14 2 0155 a72
+--- 761,692 2 0156 a73
+--- 762,692 2 0157 a74
+--- 762,692 2 0160 a203
+--- 759,692 2 0161 a75
+--- 759,692 2 0162 a204
+--- 892,705 2 0163 a76
+--- 892,692,14 2 0164 a77
+--- 788,705,14 2 0165 a78
+--- 784,705,14 2 0166 a79
+--- 438,705,14 2 0167 a81
+--- 138,692 2 0170 a82
+--- 277,692 2 0171 a83
+--- 415,692 2 0172 a84
+--- 392,705 2 0173 a97
+--- 392,705 2 0174 a98
+--- 668,705 2 0175 a99
+--- 668,705 2 0176 a100
+--- 732,806,143 3 0241 a101
+--- 544,705,14 2 0242 a102
+--- 544,705,14 2 0243 a103
+--- 910,651 2 0244 a104
+--- 667,705,14 2 0245 a106
+--- 760,705,14 2 0246 a107
+--- 760,569 2 0247 a108
+--- 776,705 2 0250 a112
+--- 595,705,14 2 0251 a111
+--- 694,705,14 2 0252 a110
+--- 626,705 2 0253 a109
+--- 788,705,14 2 0254 a120
+--- 788,705,14 2 0255 a121
+--- 788,705,14 2 0256 a122
+--- 788,705,14 2 0257 a123
+--- 788,705,14 2 0260 a124
+--- 788,705,14 2 0261 a125
+--- 788,705,14 2 0262 a126
+--- 788,705,14 2 0263 a127
+--- 788,705,14 2 0264 a128
+--- 788,705,14 2 0265 a129
+--- 788,705,14 2 0266 a130
+--- 788,705,14 2 0267 a131
+--- 788,705,14 2 0270 a132
+--- 788,705,14 2 0271 a133
+--- 788,705,14 2 0272 a134
+--- 788,705,14 2 0273 a135
+--- 788,705,14 2 0274 a136
+--- 788,705,14 2 0275 a137
+--- 788,705,14 2 0276 a138
+--- 788,705,14 2 0277 a139
+--- 788,705,14 2 0300 a140
+--- 788,705,14 2 0301 a141
+--- 788,705,14 2 0302 a142
+--- 788,705,14 2 0303 a143
+--- 788,705,14 2 0304 a144
+--- 788,705,14 2 0305 a145
+--- 788,705,14 2 0306 a146
+--- 788,705,14 2 0307 a147
+--- 788,705,14 2 0310 a148
+--- 788,705,14 2 0311 a149
+--- 788,705,14 2 0312 a150
+--- 788,705,14 2 0313 a151
+--- 788,705,14 2 0314 a152
+--- 788,705,14 2 0315 a153
+--- 788,705,14 2 0316 a154
+--- 788,705,14 2 0317 a155
+--- 788,705,14 2 0320 a156
+--- 788,705,14 2 0321 a157
+--- 788,705,14 2 0322 a158
+--- 788,705,14 2 0323 a159
+--- 894,634 2 0324 a160
+--- 838,541 2 0325 a161
+--- 1016,541 2 0326 a163
+--- 458,820,127 3 0327 a164
+--- 748,597 2 0330 a196
+--- 924,552 2 0331 a165
+--- 748,597 2 0332 a192
+--- 918,526 2 0333 a166
+--- 927,660 2 0334 a167
+--- 928,562 2 0335 a168
+--- 928,563 2 0336 a169
+--- 834,537 2 0337 a170
+--- 873,599 2 0340 a171
+--- 828,588 2 0341 a172
+--- 924,594 2 0342 a173
+--- 924,594 2 0343 a162
+--- 917,692 2 0344 a174
+--- 930,608 2 0345 a175
+--- 931,608 2 0346 a176
+--- 463,791,99 3 0347 a177
+--- 883,623 2 0350 a178
+--- 836,648 2 0351 a179
+--- 836,648 2 0352 a193
+--- 867,591 2 0353 a180
+--- 867,591 2 0354 a199
+--- 696,648 2 0355 a181
+--- 696,648 2 0356 a200
+--- 874,619 2 0357 a182
+--- 874,615 2 0361 a201
+--- 760,692 2 0362 a183
+--- 946,533 2 0363 a184
+--- 771,655 2 0364 a197
+--- 865,481 2 0365 a185
+--- 771,655 2 0366 a194
+--- 888,712,19 2 0367 a198
+--- 967,568 2 0370 a186
+--- 888,712,19 2 0371 a195
+--- 831,579 2 0372 a187
+--- 873,578 2 0373 a188
+--- 927,542 2 0374 a189
+--- 970,616 2 0375 a190
+--- 918,593 2 0376 a191
diff --git a/ps/devps/ZDR b/ps/devps/ZDR
new file mode 100644
index 000000000..57dcdb76d
--- /dev/null
+++ b/ps/devps/ZDR
@@ -0,0 +1,193 @@
+name ZDR
+internalname ZapfDingbats-Reverse
+special
+spacewidth 278
+charset
+space 278 0 0040
+--- 974,621 2 0041 a1
+--- 961,611 2 0042 a2
+--- 974,621 2 0043 a202
+--- 980,692 2 0044 a3
+--- 719,566 2 0045 a4
+--- 789,705,14 2 0046 a5
+--- 790,705,14 2 0047 a119
+--- 791,705,14 2 0050 a118
+--- 690,553 2 0051 a117
+--- 960,568 2 0052 a11
+lh 939,559 2 0053 a12
+--- 549,705,11 2 0054 a13
+--- 855,632 2 0055 a14
+--- 911,642 2 0056 a15
+--- 933,550 2 0057 a16
+--- 911,642 2 0060 a105
+--- 945,553 2 0061 a17
+--- 974,587 2 0062 a18
+--- 755,705,13 2 0063 a19
+--- 846,705,14 2 0064 a20
+--- 762,692 2 0065 a21
+--- 761,692 2 0066 a22
+--- 571,661,68 3 0067 a23
+--- 677,705,14 2 0070 a24
+--- 763,692 2 0071 a25
+--- 760,692 2 0072 a26
+--- 759,692 2 0073 a27
+--- 754,692 2 0074 a28
+--- 494,692 2 0075 a6
+--- 552,692 2 0076 a7
+--- 537,692 2 0077 a8
+--- 577,596 2 0100 a9
+--- 692,705,14 2 0101 a10
+--- 786,705,14 2 0102 a29
+--- 788,705,14 2 0103 a30
+--- 788,705,14 2 0104 a31
+--- 790,705,14 2 0105 a32
+--- 793,705,14 2 0106 a33
+--- 794,705,14 2 0107 a34
+--- 816,705,14 2 0110 a35
+--- 823,705,14 2 0111 a36
+--- 789,705,14 2 0112 a37
+--- 841,705,14 2 0113 a38
+--- 823,705,14 2 0114 a39
+--- 833,705,14 2 0115 a40
+--- 816,705,13 2 0116 a41
+--- 831,705,14 2 0117 a42
+--- 923,705,14 2 0120 a43
+--- 744,692 2 0121 a44
+--- 723,692 2 0122 a45
+--- 749,692 2 0123 a46
+--- 790,705,14 2 0124 a47
+--- 792,705,14 2 0125 a48
+--- 695,705,14 2 0126 a49
+--- 776,699,6 2 0127 a50
+--- 768,699,7 2 0130 a51
+--- 792,705,14 2 0131 a52
+--- 759,692 2 0132 a53
+--- 707,705,14 2 0133 a54
+--- 708,705,14 2 0134 a55
+--- 682,705,14 2 0135 a56
+--- 701,705,14 2 0136 a57
+--- 826,705,14 2 0137 a58
+--- 815,705,14 2 0140 a59
+--- 789,705,14 2 0141 a60
+--- 789,705,14 2 0142 a61
+--- 707,705,14 2 0143 a62
+--- 687,692 2 0144 a63
+--- 696,691 2 0145 a64
+--- 689,692 2 0146 a65
+--- 786,705,14 2 0147 a66
+--- 787,705,14 2 0150 a67
+--- 713,705,14 2 0151 a68
+--- 791,705,14 2 0152 a69
+--- 785,705,14 2 0153 a70
+--- 791,705,14 2 0154 a71
+--- 873,705,14 2 0155 a72
+--- 761,692 2 0156 a73
+--- 762,692 2 0157 a74
+--- 762,692 2 0160 a203
+--- 759,692 2 0161 a75
+--- 759,692 2 0162 a204
+--- 892,705 2 0163 a76
+--- 892,692,14 2 0164 a77
+--- 788,705,14 2 0165 a78
+--- 784,705,14 2 0166 a79
+--- 438,705,14 2 0167 a81
+--- 138,692 2 0170 a82
+--- 277,692 2 0171 a83
+--- 415,692 2 0172 a84
+--- 392,705 2 0173 a97
+--- 392,705 2 0174 a98
+--- 668,705 2 0175 a99
+--- 668,705 2 0176 a100
+--- 732,806,143 3 0241 a101
+--- 544,705,14 2 0242 a102
+--- 544,705,14 2 0243 a103
+--- 910,651 2 0244 a104
+--- 667,705,14 2 0245 a106
+--- 760,705,14 2 0246 a107
+--- 760,569 2 0247 a108
+--- 776,705 2 0250 a112
+--- 595,705,14 2 0251 a111
+--- 694,705,14 2 0252 a110
+--- 626,705 2 0253 a109
+--- 788,705,14 2 0254 a120
+--- 788,705,14 2 0255 a121
+--- 788,705,14 2 0256 a122
+--- 788,705,14 2 0257 a123
+--- 788,705,14 2 0260 a124
+--- 788,705,14 2 0261 a125
+--- 788,705,14 2 0262 a126
+--- 788,705,14 2 0263 a127
+--- 788,705,14 2 0264 a128
+--- 788,705,14 2 0265 a129
+--- 788,705,14 2 0266 a130
+--- 788,705,14 2 0267 a131
+--- 788,705,14 2 0270 a132
+--- 788,705,14 2 0271 a133
+--- 788,705,14 2 0272 a134
+--- 788,705,14 2 0273 a135
+--- 788,705,14 2 0274 a136
+--- 788,705,14 2 0275 a137
+--- 788,705,14 2 0276 a138
+--- 788,705,14 2 0277 a139
+--- 788,705,14 2 0300 a140
+--- 788,705,14 2 0301 a141
+--- 788,705,14 2 0302 a142
+--- 788,705,14 2 0303 a143
+--- 788,705,14 2 0304 a144
+--- 788,705,14 2 0305 a145
+--- 788,705,14 2 0306 a146
+--- 788,705,14 2 0307 a147
+--- 788,705,14 2 0310 a148
+--- 788,705,14 2 0311 a149
+--- 788,705,14 2 0312 a150
+--- 788,705,14 2 0313 a151
+--- 788,705,14 2 0314 a152
+--- 788,705,14 2 0315 a153
+--- 788,705,14 2 0316 a154
+--- 788,705,14 2 0317 a155
+--- 788,705,14 2 0320 a156
+--- 788,705,14 2 0321 a157
+--- 788,705,14 2 0322 a158
+--- 788,705,14 2 0323 a159
+--- 894,634 2 0324 a160
+--- 838,541 2 0325 a161
+--- 1016,541 2 0326 a163
+--- 458,820,127 3 0327 a164
+--- 748,597 2 0330 a196
+--- 924,552 2 0331 a165
+--- 748,597 2 0332 a192
+--- 918,526 2 0333 a166
+--- 927,660 2 0334 a167
+--- 928,562 2 0335 a168
+--- 928,563 2 0336 a169
+--- 834,537 2 0337 a170
+--- 873,599 2 0340 a171
+--- 828,588 2 0341 a172
+--- 924,594 2 0342 a173
+--- 924,594 2 0343 a162
+--- 917,692 2 0344 a174
+--- 930,608 2 0345 a175
+--- 931,608 2 0346 a176
+--- 463,791,99 3 0347 a177
+--- 883,623 2 0350 a178
+--- 836,648 2 0351 a179
+--- 836,648 2 0352 a193
+--- 867,591 2 0353 a180
+--- 867,591 2 0354 a199
+--- 696,648 2 0355 a181
+--- 696,648 2 0356 a200
+--- 874,619 2 0357 a182
+--- 874,615 2 0361 a201
+--- 760,692 2 0362 a183
+--- 946,533 2 0363 a184
+--- 771,655 2 0364 a197
+--- 865,481 2 0365 a185
+--- 771,655 2 0366 a194
+--- 888,712,19 2 0367 a198
+--- 967,568 2 0370 a186
+--- 888,712,19 2 0371 a195
+--- 831,579 2 0372 a187
+--- 873,578 2 0373 a188
+--- 927,542 2 0374 a189
+--- 970,616 2 0375 a190
+--- 918,593 2 0376 a191
diff --git a/ps/devps/afmtodit b/ps/devps/afmtodit
new file mode 100755
index 000000000..3914b9729
--- /dev/null
+++ b/ps/devps/afmtodit
@@ -0,0 +1,310 @@
+#! /usr/bin/perl -P- # -*- Perl -*-
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+$prog = $0;
+
+do 'getopts.pl';
+do Getopts('e:sd:i:a:');
+
+if ($#ARGV != 2) {
+ die "Usage: $prog [-s] [-e encoding] [-i n] [-a angle] afmfile mapfile font\n";
+}
+
+$afm = $ARGV[0];
+$map = $ARGV[1];
+$font = $ARGV[2];
+
+# read the afm file
+
+open(AFM, $afm) || die "Can't open " . $ARGV[0];
+
+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 "Can't open DESC";
+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 "Can't open $opt_e";
+ 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 "Can't open $map";
+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 "Can't open $font for output";
+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"};
+print("encoding $opt_e\n") if $opt_e;
+
+if ($#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++) {
+ printf("%s %s %d\n",
+ $map{$c1,$j},
+ $map{$c2,$k},
+ do conv($kernx[$i]));
+ }
+ }
+ }
+ }
+}
+
+# characters taller than asc_boundary are considered to have ascenders
+$asc_boundary = $height{"a"} + 50;
+
+# likewise for descenders
+$desc_boundary = $depth{"a"} + 50;
+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/ps/devps/dingbatsmap b/ps/devps/dingbatsmap
new file mode 100644
index 000000000..3a97fa99b
--- /dev/null
+++ b/ps/devps/dingbatsmap
@@ -0,0 +1,2 @@
+a19 OK
+a12 rh
diff --git a/ps/devps/download b/ps/devps/download
new file mode 100644
index 000000000..7da0011e1
--- /dev/null
+++ b/ps/devps/download
@@ -0,0 +1,5 @@
+# List of downloadable fonts
+# PostScript-name Filename
+
+Symbol-Slanted symbolsl.ps
+ZapfDingbats-Reverse zapfdr.ps
diff --git a/ps/devps/eqnchar b/ps/devps/eqnchar
new file mode 100644
index 000000000..1bcd21ad9
--- /dev/null
+++ b/ps/devps/eqnchar
@@ -0,0 +1,13 @@
+.EQ
+sdefine << %{ < back 20 < }%
+sdefine >> %{ > back 20 > }%
+
+sdefine dot %accent "\fR\(a.\fP"%
+sdefine dotdot %accent "\fR\(ad\fP"%
+sdefine vec %accent {up 52 "\s[\En[.s]/2u]\(->\s0"}%
+sdefine dyad %accent {up 52 "\s[\En[.s]/2u]\(<>\s0"}%
+
+sdefine inf %"\s[\En[.s]*13u/10u]\v'12M'\(if\v'-12M'\s0"%
+
+sdefine cdot %type "binary" \(md%
+.EN
diff --git a/ps/devps/lgreekmap b/ps/devps/lgreekmap
new file mode 100644
index 000000000..7df97581b
--- /dev/null
+++ b/ps/devps/lgreekmap
@@ -0,0 +1,25 @@
+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
+sigma1 ts
diff --git a/ps/devps/prologue b/ps/devps/prologue
new file mode 100644
index 000000000..027e432f3
--- /dev/null
+++ b/ps/devps/prologue
@@ -0,0 +1,211 @@
+%!PS-Adobe-3.0 Resource-ProcSet
+
+/setpacking where {
+ pop
+ currentpacking
+ true setpacking
+} if
+
+/grops 120 dict dup begin
+
+% The ASCII code of the space character.
+/SC 32 def
+
+/A /show load def
+/B { 0 SC 3 -1 roll widthshow } bind def
+/C { 0 exch ashow } bind def
+/D { 0 exch 0 SC 5 2 roll awidthshow } bind def
+/E { 0 rmoveto show } bind def
+/F { 0 rmoveto 0 SC 3 -1 roll widthshow } bind def
+/G { 0 rmoveto 0 exch ashow } bind def
+/H { 0 rmoveto 0 exch 0 SC 5 2 roll awidthshow } bind def
+/I { 0 exch rmoveto show } bind def
+/J { 0 exch rmoveto 0 SC 3 -1 roll widthshow } bind def
+/K { 0 exch rmoveto 0 exch ashow } bind def
+/L { 0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow } bind def
+/M { rmoveto show } bind def
+/N { rmoveto 0 SC 3 -1 roll widthshow } bind def
+/O { rmoveto 0 exch ashow } bind def
+/P { rmoveto 0 exch 0 SC 5 2 roll awidthshow } bind def
+/Q { moveto show } bind def
+/R { moveto 0 SC 3 -1 roll widthshow } bind def
+/S { moveto 0 exch ashow } bind def
+/T { moveto 0 exch 0 SC 5 2 roll awidthshow } bind def
+
+% name size font SF -
+
+/SF {
+ findfont exch
+ [ exch dup 0 exch 0 exch neg 0 0 ] makefont
+ dup setfont
+ [ exch /setfont cvx ] cvx bind def
+} bind def
+
+% name a c d font MF -
+
+/MF {
+ findfont
+ [ 5 2 roll
+ 0 3 1 roll % b
+ neg 0 0 ] makefont
+ dup setfont
+ [ exch /setfont cvx ] cvx bind def
+} bind def
+
+/level0 0 def
+/RES 0 def
+/PL 0 def
+/LS 0 def
+
+% BP -
+
+/BP {
+ /level0 save def
+ 1 setlinecap
+ 1 setlinejoin
+ 72 RES div dup scale
+ LS {
+ 90 rotate
+ } {
+ 0 PL translate
+ } ifelse
+ 1 -1 scale
+} bind def
+
+/EP {
+ level0 restore
+ showpage
+} bind def
+
+
+% centerx centery radius startangle endangle DA -
+
+/DA {
+ newpath arcn stroke
+} bind def
+
+% x y SN - x' y'
+% round a position to nearest (pixel + (.25,.25))
+
+/SN {
+ transform
+ .25 sub exch .25 sub exch
+ round .25 add exch round .25 add exch
+ itransform
+} bind def
+
+% endx endy startx starty DL -
+% we round the endpoints of the line, so that parallel horizontal
+% and vertical lines will appear even
+
+/DL {
+ SN
+ moveto
+ SN
+ lineto stroke
+} bind def
+
+% centerx centery radius DC -
+
+/DC {
+ newpath 0 360 arc closepath
+} bind def
+
+
+/TM matrix def
+
+% width height centerx centery DE -
+
+/DE {
+ TM currentmatrix pop
+ translate scale newpath 0 0 .5 0 360 arc closepath
+ TM setmatrix
+} bind def
+
+% these are for splines
+
+/RC /rcurveto load def
+/RL /rlineto load def
+/ST /stroke load def
+/MT /moveto load def
+/CL /closepath load def
+
+% fill the last path
+
+% amount FL -
+
+/FL {
+ currentgray exch setgray fill setgray
+} bind def
+
+% fill with the ``current color''
+
+/BL /fill load def
+
+/LW /setlinewidth load def
+% new_font_name encoding_vector old_font_name RE -
+
+/RE {
+ findfont
+ dup maxlength dict begin
+ {
+ 1 index /FID ne { def } { pop pop } ifelse
+ } forall
+ /Encoding exch def
+ dup /FontName exch def
+ currentdict end definefont pop
+} bind def
+
+/DEFS 0 def
+
+% hpos vpos EBEGIN -
+
+/EBEGIN {
+ moveto
+ DEFS begin
+} bind def
+
+/EEND /end load def
+
+/CNT 0 def
+/level1 0 def
+
+% llx lly newwid wid newht ht newllx newlly PBEGIN -
+
+/PBEGIN {
+ /level1 save def
+ translate
+ div 3 1 roll div exch scale
+ neg exch neg exch translate
+ % set the graphics state to default values
+ 0 setgray
+ 0 setlinecap
+ 1 setlinewidth
+ 0 setlinejoin
+ 10 setmiterlimit
+ [] 0 setdash
+ /setstrokeadjust where {
+ pop
+ false setstrokeadjust
+ } if
+ /setoverprint where {
+ pop
+ false setoverprint
+ } if
+ newpath
+ /CNT countdictstack def
+ /showpage {} def
+} bind def
+
+/PEND {
+ clear
+ countdictstack CNT sub { end } repeat
+ level1 restore
+} bind def
+
+end def
+
+/setpacking where {
+ pop
+ setpacking
+} if
diff --git a/ps/devps/symbol.diff b/ps/devps/symbol.diff
new file mode 100644
index 000000000..adbde6e67
--- /dev/null
+++ b/ps/devps/symbol.diff
@@ -0,0 +1,65 @@
+*** /usr/local/afm/symbol.afm Mon Mar 26 11:40:00 1990
+--- symbol.afm Tue Oct 16 09:46:37 1990
+***************
+*** 177,192 ****
+ C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ;
+ C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ;
+ C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ;
+! C 230 ; WX 384 ; N parenlefttp ; B 40 -293 436 926 ;
+! C 231 ; WX 384 ; N parenleftex ; B 40 -85 92 925 ;
+! C 232 ; WX 384 ; N parenleftbt ; B 40 -293 436 926 ;
+! C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 341 926 ;
+! C 234 ; WX 384 ; N bracketleftex ; B 0 -79 55 925 ;
+! C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 340 926 ;
+! C 236 ; WX 494 ; N bracelefttp ; B 201 -75 439 926 ;
+! C 237 ; WX 494 ; N braceleftmid ; B 14 -85 255 935 ;
+! C 238 ; WX 494 ; N braceleftbt ; B 201 -70 439 926 ;
+! C 239 ; WX 494 ; N braceex ; B 201 -80 255 935 ;
+ C 240 ; WX 790 ; N apple ; B 56 -3 733 808 ;
+ C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ;
+ C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ;
+--- 177,192 ----
+ C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ;
+ C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ;
+ C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ;
+! C 230 ; WX 384 ; N parenlefttp ; B 40 -290 436 926 ;
+! C 231 ; WX 384 ; N parenleftex ; B 40 -80 92 920 ;
+! C 232 ; WX 384 ; N parenleftbt ; B 40 -293 436 920 ;
+! C 233 ; WX 384 ; N bracketlefttp ; B 0 -75 341 925 ;
+! C 234 ; WX 384 ; N bracketleftex ; B 0 -75 55 925 ;
+! C 235 ; WX 384 ; N bracketleftbt ; B 0 -75 340 925 ;
+! C 236 ; WX 494 ; N bracelefttp ; B 201 -75 439 925 ;
+! C 237 ; WX 494 ; N braceleftmid ; B 14 -75 255 925 ;
+! C 238 ; WX 494 ; N braceleftbt ; B 201 -75 439 925 ;
+! C 239 ; WX 494 ; N braceex ; B 201 -75 255 925 ;
+ C 240 ; WX 790 ; N apple ; B 56 -3 733 808 ;
+ C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ;
+ C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ;
+***************
+*** 196,206 ****
+ C 246 ; WX 384 ; N parenrighttp ; B 54 -293 450 926 ;
+ C 247 ; WX 384 ; N parenrightex ; B 398 -85 450 925 ;
+ C 248 ; WX 384 ; N parenrightbt ; B 54 -293 450 926 ;
+! C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 360 926 ;
+! C 250 ; WX 384 ; N bracketrightex ; B 305 -79 360 925 ;
+! C 251 ; WX 384 ; N bracketrightbt ; B 20 -80 360 926 ;
+! C 252 ; WX 494 ; N bracerighttp ; B 17 -75 255 926 ;
+! C 253 ; WX 494 ; N bracerightmid ; B 201 -85 442 935 ;
+! C 254 ; WX 494 ; N bracerightbt ; B 17 -70 255 926 ;
+ EndCharMetrics
+ EndFontMetrics
+--- 196,209 ----
+ C 246 ; WX 384 ; N parenrighttp ; B 54 -293 450 926 ;
+ C 247 ; WX 384 ; N parenrightex ; B 398 -85 450 925 ;
+ C 248 ; WX 384 ; N parenrightbt ; B 54 -293 450 926 ;
+! C 249 ; WX 384 ; N bracketrighttp ; B 22 -75 360 925 ;
+! C 250 ; WX 384 ; N bracketrightex ; B 305 -75 360 925 ;
+! C 251 ; WX 384 ; N bracketrightbt ; B 20 -75 360 925 ;
+! C 252 ; WX 494 ; N bracerighttp ; B 17 -75 255 925 ;
+! C 253 ; WX 494 ; N bracerightmid ; B 201 -75 442 925 ;
+! C 254 ; WX 494 ; N bracerightbt ; B 17 -75 255 925 ;
+ EndCharMetrics
++ italicCorrection integral 67
++ leftItalicCorrection integral 52
++ subscriptCorrection integral -10
+ EndFontMetrics
diff --git a/ps/devps/symbolchars b/ps/devps/symbolchars
new file mode 100644
index 000000000..c5d4c445d
--- /dev/null
+++ b/ps/devps/symbolchars
@@ -0,0 +1,3 @@
+plus pl
+minus mi
+equal eq
diff --git a/ps/devps/symbolsl.afm b/ps/devps/symbolsl.afm
new file mode 100644
index 000000000..90939ada9
--- /dev/null
+++ b/ps/devps/symbolsl.afm
@@ -0,0 +1,203 @@
+StartFontMetrics 2.0
+FontName Symbol-Slanted
+Version 001.001
+FamilyName Symbol
+ItalicAngle -15.5
+IsFixedPitch false
+UnderlineThickness 54
+Weight Medium
+UnderlinePosition -98
+FullName Symbol
+FontBBox -241 -261 1250 899
+StartCharMetrics 189
+C 32 ; WX 223 ; N space ; B 223 0 223 0 ;
+C 33 ; WX 296 ; N exclam ; B 122 -15 383 599 ;
+C 34 ; WX 635 ; N universal ; B 223 0 801 627 ;
+C 35 ; WX 445 ; N numbersign ; B 71 -15 557 599 ;
+C 36 ; WX 489 ; N existential ; B 22 0 622 629 ;
+C 37 ; WX 741 ; N percent ; B 147 -32 766 583 ;
+C 38 ; WX 692 ; N ampersand ; B 68 -16 745 589 ;
+C 39 ; WX 391 ; N suchthat ; B 44 -15 450 444 ;
+C 40 ; WX 296 ; N parenleft ; B 89 -170 449 599 ;
+C 41 ; WX 296 ; N parenright ; B -22 -170 339 600 ;
+C 42 ; WX 445 ; N asteriskmath ; B 126 119 500 490 ;
+C 43 ; WX 489 ; N plus ; B 76 0 562 474 ;
+C 44 ; WX 223 ; N comma ; B 9 -136 183 93 ;
+C 45 ; WX 489 ; N minus ; B 74 207 556 256 ;
+C 46 ; WX 223 ; N period ; B 70 -15 174 85 ;
+C 47 ; WX 247 ; N slash ; B -5 -15 405 575 ;
+C 48 ; WX 445 ; N zero ; B 90 -15 537 610 ;
+C 49 ; WX 445 ; N one ; B 107 0 451 599 ;
+C 50 ; WX 445 ; N two ; B 22 0 524 611 ;
+C 51 ; WX 445 ; N three ; B 56 -16 510 611 ;
+C 52 ; WX 445 ; N four ; B 58 0 530 610 ;
+C 53 ; WX 445 ; N five ; B 36 -15 583 610 ;
+C 54 ; WX 445 ; N six ; B 87 -16 588 610 ;
+C 55 ; WX 445 ; N seven ; B 151 -15 585 599 ;
+C 56 ; WX 445 ; N eight ; B 82 -16 536 611 ;
+C 57 ; WX 445 ; N nine ; B 52 -15 535 609 ;
+C 58 ; WX 247 ; N colon ; B 81 -15 286 409 ;
+C 59 ; WX 247 ; N semicolon ; B 33 -136 296 409 ;
+C 60 ; WX 489 ; N less ; B 87 0 610 464 ;
+C 61 ; WX 489 ; N equal ; B 49 126 586 347 ;
+C 62 ; WX 489 ; N greater ; B 23 0 547 464 ;
+C 63 ; WX 395 ; N question ; B 163 -15 517 610 ;
+C 64 ; WX 489 ; N congruent ; B 10 0 597 423 ;
+C 65 ; WX 643 ; N Alpha ; B 3 0 614 599 ;
+C 66 ; WX 594 ; N Beta ; B 26 0 645 598 ;
+C 67 ; WX 643 ; N Chi ; B -8 0 798 599 ;
+C 68 ; WX 545 ; N Delta ; B 5 0 541 612 ;
+C 69 ; WX 544 ; N Epsilon ; B 28 0 688 599 ;
+C 70 ; WX 679 ; N Phi ; B 105 0 761 598 ;
+C 71 ; WX 537 ; N Gamma ; B 31 0 714 599 ;
+C 72 ; WX 643 ; N Eta ; B 35 0 836 599 ;
+C 73 ; WX 296 ; N Iota ; B 28 0 468 599 ;
+C 74 ; WX 562 ; N theta1 ; B 108 -15 645 614 ;
+C 75 ; WX 643 ; N Kappa ; B 31 0 778 598 ;
+C 76 ; WX 611 ; N Lambda ; B 5 0 610 612 ;
+C 77 ; WX 791 ; N Mu ; B 28 0 974 599 ;
+C 78 ; WX 643 ; N Nu ; B 26 -7 827 599 ;
+C 79 ; WX 643 ; N Omicron ; B 112 -15 747 610 ;
+C 80 ; WX 684 ; N Pi ; B 22 0 847 599 ;
+C 81 ; WX 659 ; N Theta ; B 112 -15 747 610 ;
+C 82 ; WX 495 ; N Rho ; B 25 0 645 599 ;
+C 83 ; WX 527 ; N Sigma ; B 5 0 663 599 ;
+C 84 ; WX 544 ; N Tau ; B 159 0 723 599 ;
+C 85 ; WX 614 ; N Upsilon ; B 175 0 804 599 ;
+C 86 ; WX 391 ; N sigma1 ; B 78 -208 492 445 ;
+C 87 ; WX 684 ; N Omega ; B 30 0 760 612 ;
+C 88 ; WX 574 ; N Xi ; B 36 0 700 598 ;
+C 89 ; WX 708 ; N Psi ; B 188 0 885 608 ;
+C 90 ; WX 544 ; N Zeta ; B 39 0 725 599 ;
+C 91 ; WX 296 ; N bracketleft ; B 34 -138 453 599 ;
+C 92 ; WX 768 ; N therefore ; B 160 0 645 426 ;
+C 93 ; WX 296 ; N bracketright ; B -14 -138 405 599 ;
+C 94 ; WX 586 ; N perpendicular ; B 13 0 596 600 ;
+C 95 ; WX 445 ; N underscore ; B -72 -224 390 -183 ;
+C 96 ; WX 445 ; N radicalex ; B 672 784 1224 816 ;
+C 97 ; WX 562 ; N alpha ; B 84 -15 658 445 ;
+C 98 ; WX 489 ; N beta ; B -7 -198 578 659 ;
+C 99 ; WX 489 ; N chi ; B -48 -206 573 445 ;
+C 100 ; WX 440 ; N delta ; B 83 -16 571 658 ;
+C 101 ; WX 391 ; N epsilon ; B 49 -17 468 447 ;
+C 102 ; WX 464 ; N phi ; B 78 -200 517 596 ;
+C 103 ; WX 366 ; N gamma ; B 92 -200 568 444 ;
+C 104 ; WX 537 ; N eta ; B 100 -180 555 457 ;
+C 105 ; WX 293 ; N iota ; B 97 -16 296 448 ;
+C 106 ; WX 537 ; N phi1 ; B 92 -199 604 444 ;
+C 107 ; WX 489 ; N kappa ; B 106 0 621 447 ;
+C 108 ; WX 489 ; N lambda ; B 21 -16 530 658 ;
+C 109 ; WX 513 ; N mu ; B -18 -198 533 445 ;
+C 110 ; WX 464 ; N nu ; B 119 -15 548 451 ;
+C 111 ; WX 489 ; N omicron ; B 86 -17 526 444 ;
+C 112 ; WX 489 ; N pi ; B 58 -18 599 433 ;
+C 113 ; WX 464 ; N theta ; B 103 -16 554 614 ;
+C 114 ; WX 489 ; N rho ; B -19 -205 521 444 ;
+C 115 ; WX 537 ; N sigma ; B 87 -19 662 445 ;
+C 116 ; WX 391 ; N tau ; B 95 -16 511 445 ;
+C 117 ; WX 513 ; N upsilon ; B 105 -15 558 451 ;
+C 118 ; WX 635 ; N omega1 ; B 78 -15 758 519 ;
+C 119 ; WX 611 ; N omega ; B 85 -16 687 445 ;
+C 120 ; WX 439 ; N xi ; B 70 -200 515 681 ;
+C 121 ; WX 611 ; N psi ; B 141 -203 759 445 ;
+C 122 ; WX 440 ; N zeta ; B 100 -200 580 673 ;
+C 123 ; WX 427 ; N braceleft ; B 116 -163 540 599 ;
+C 124 ; WX 178 ; N bar ; B 9 -158 307 599 ;
+C 125 ; WX 427 ; N braceright ; B 19 -163 444 599 ;
+C 126 ; WX 489 ; N similar ; B 78 181 549 273 ;
+C 161 ; WX 552 ; N Upsilon1 ; B 134 0 710 609 ;
+C 162 ; WX 220 ; N minute ; B 156 408 393 654 ;
+C 163 ; WX 489 ; N lessequal ; B 26 0 645 569 ;
+C 164 ; WX 149 ; N fraction ; B -164 -11 490 603 ;
+C 165 ; WX 635 ; N infinity ; B 91 111 692 360 ;
+C 166 ; WX 445 ; N florin ; B -36 -172 614 612 ;
+C 167 ; WX 670 ; N club ; B 119 -23 645 474 ;
+C 168 ; WX 670 ; N diamond ; B 198 -32 605 490 ;
+C 169 ; WX 670 ; N heart ; B 205 -29 679 473 ;
+C 170 ; WX 670 ; N spade ; B 132 -32 604 488 ;
+C 171 ; WX 927 ; N arrowboth ; B 90 -13 980 455 ;
+C 172 ; WX 878 ; N arrowleft ; B 97 -13 915 455 ;
+C 173 ; WX 537 ; N arrowup ; B 223 0 691 810 ;
+C 174 ; WX 878 ; N arrowright ; B 105 -13 922 455 ;
+C 175 ; WX 537 ; N arrowdown ; B 104 -20 572 790 ;
+C 176 ; WX 356 ; N degree ; B 187 343 466 609 ;
+C 177 ; WX 489 ; N plusminus ; B 9 0 593 574 ;
+C 178 ; WX 366 ; N second ; B 150 408 560 656 ;
+C 179 ; WX 489 ; N greaterequal ; B 26 0 582 569 ;
+C 180 ; WX 489 ; N multiply ; B 28 7 609 466 ;
+C 181 ; WX 635 ; N proportional ; B 90 110 667 360 ;
+C 182 ; WX 440 ; N partialdiff ; B 62 -18 542 664 ;
+C 183 ; WX 409 ; N bullet ; B 118 101 454 421 ;
+C 184 ; WX 489 ; N divide ; B 74 63 558 406 ;
+C 185 ; WX 489 ; N notequal ; B 51 -22 587 489 ;
+C 186 ; WX 489 ; N equivalence ; B 35 73 602 394 ;
+C 187 ; WX 489 ; N approxequal ; B 57 120 572 351 ;
+C 188 ; WX 890 ; N ellipsis ; B 107 -15 804 85 ;
+C 189 ; WX 537 ; N arrowvertex ; B 216 -107 579 899 ;
+C 190 ; WX 890 ; N arrowhorizex ; B 8 196 1011 246 ;
+C 191 ; WX 586 ; N carriagereturn ; B 40 -14 710 560 ;
+C 192 ; WX 732 ; N aleph ; B 159 -16 740 586 ;
+C 193 ; WX 611 ; N Ifraktur ; B 26 -47 684 659 ;
+C 194 ; WX 708 ; N Rfraktur ; B 71 -13 833 653 ;
+C 195 ; WX 878 ; N weierstrass ; B 112 -188 878 510 ;
+C 196 ; WX 684 ; N circlemultiply ; B 114 -15 758 599 ;
+C 197 ; WX 684 ; N circleplus ; B 115 -13 759 601 ;
+C 198 ; WX 732 ; N emptyset ; B 38 -21 884 640 ;
+C 199 ; WX 684 ; N intersection ; B 36 0 714 453 ;
+C 200 ; WX 684 ; N union ; B 110 -15 788 438 ;
+C 201 ; WX 635 ; N propersuperset ; B 18 0 675 418 ;
+C 202 ; WX 635 ; N reflexsuperset ; B -17 -111 674 418 ;
+C 203 ; WX 635 ; N notsubset ; B 87 -62 744 481 ;
+C 204 ; WX 635 ; N propersubset ; B 87 0 744 418 ;
+C 205 ; WX 635 ; N reflexsubset ; B 16 -111 744 418 ;
+C 206 ; WX 635 ; N element ; B 93 0 579 417 ;
+C 207 ; WX 635 ; N notelement ; B 74 -52 579 494 ;
+C 208 ; WX 684 ; N angle ; B 23 0 833 599 ;
+C 209 ; WX 635 ; N gradient ; B 231 -17 805 639 ;
+C 210 ; WX 703 ; N registerserif ; B 120 -18 763 596 ;
+C 211 ; WX 703 ; N copyrightserif ; B 122 -13 766 601 ;
+C 212 ; WX 792 ; N trademarkserif ; B 169 261 947 599 ;
+C 213 ; WX 732 ; N product ; B -6 -90 920 668 ;
+C 214 ; WX 489 ; N radical ; B 134 -34 711 816 ;
+C 215 ; WX 223 ; N dotmath ; B 131 187 225 276 ;
+C 216 ; WX 635 ; N logicalnot ; B 78 0 685 256 ;
+C 217 ; WX 537 ; N logicaland ; B 21 0 519 404 ;
+C 218 ; WX 537 ; N logicalor ; B 151 0 639 424 ;
+C 219 ; WX 927 ; N arrowdblboth ; B 92 -18 978 454 ;
+C 220 ; WX 878 ; N arrowdblleft ; B 96 -13 942 457 ;
+C 221 ; WX 537 ; N arrowdblup ; B 152 2 688 811 ;
+C 222 ; WX 878 ; N arrowdblright ; B 71 -18 917 452 ;
+C 223 ; WX 537 ; N arrowdbldown ; B 103 -17 639 792 ;
+C 224 ; WX 440 ; N lozenge ; B 121 0 519 663 ;
+C 225 ; WX 293 ; N angleleft ; B 98 -176 472 664 ;
+C 226 ; WX 703 ; N registersans ; B 120 -18 763 596 ;
+C 227 ; WX 703 ; N copyrightsans ; B 120 -13 764 601 ;
+C 228 ; WX 700 ; N trademarksans ; B 179 261 832 599 ;
+C 229 ; WX 635 ; N summation ; B -15 -96 756 669 ;
+C 230 ; WX 342 ; N parenlefttp ; B -46 -261 642 824 ;
+C 231 ; WX 342 ; N parenleftex ; B 12 -76 338 823 ;
+C 232 ; WX 342 ; N parenleftbt ; B 113 -261 339 824 ;
+C 233 ; WX 342 ; N bracketlefttp ; B -22 -71 560 824 ;
+C 234 ; WX 342 ; N bracketleftex ; B -22 -70 305 823 ;
+C 235 ; WX 342 ; N bracketleftbt ; B -22 -71 306 824 ;
+C 236 ; WX 440 ; N bracelefttp ; B 158 -67 648 824 ;
+C 237 ; WX 440 ; N braceleftmid ; B 126 -76 486 832 ;
+C 238 ; WX 440 ; N braceleftbt ; B 219 -62 484 824 ;
+C 239 ; WX 440 ; N braceex ; B 157 -71 486 832 ;
+C 241 ; WX 293 ; N angleright ; B -29 -176 345 664 ;
+C 242 ; WX 244 ; N integral ; B -13 -95 499 815 ;
+C 243 ; WX 611 ; N integraltp ; B 272 -74 873 820 ;
+C 244 ; WX 611 ; N integralex ; B 271 -78 640 868 ;
+C 245 ; WX 611 ; N integralbt ; B 30 -72 625 820 ;
+C 246 ; WX 342 ; N parenrighttp ; B 273 -261 498 824 ;
+C 247 ; WX 342 ; N parenrightex ; B 331 -76 657 823 ;
+C 248 ; WX 342 ; N parenrightbt ; B -30 -261 657 824 ;
+C 249 ; WX 342 ; N bracketrighttp ; B 249 -71 577 824 ;
+C 250 ; WX 342 ; N bracketrightex ; B 250 -70 577 823 ;
+C 251 ; WX 342 ; N bracketrightbt ; B -4 -71 577 824 ;
+C 252 ; WX 440 ; N bracerighttp ; B 158 -67 425 824 ;
+C 253 ; WX 440 ; N bracerightmid ; B 155 -76 517 832 ;
+C 254 ; WX 440 ; N bracerightbt ; B -4 -62 484 824 ;
+C -1 ; WX 703 ; N apple ; B 136 -2 784 719 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/ps/devps/symbolsl.ps b/ps/devps/symbolsl.ps
new file mode 100644
index 000000000..d384203e2
--- /dev/null
+++ b/ps/devps/symbolsl.ps
@@ -0,0 +1,41 @@
+%!PS-Adobe-3.0 Resource-Font
+%%DocumentNeededResources: font Symbol
+
+% newfontname matrix oldfontname MakeTransformedFont
+
+/MakeTransformedFont {
+ findfont dup maxlength dict begin
+ {
+ exch dup dup /FID ne exch /UniqueID ne and {
+ exch def
+ } {
+ pop pop
+ } ifelse
+ } forall
+ % first copy FontBBox
+ /FontBBox
+ % FontBBox sometimes seems to have the executable
+ % attribute set
+ % so to get the array on the stack, we have to do this
+ currentdict /FontBBox get
+ 4 array copy def
+ % now transform it
+ FontBBox aload pop
+ 4 index transform 4 2 roll
+ 4 index transform 4 2 roll
+ FontBBox astore pop
+ % matrix
+ % now transform FontMatrix
+ FontMatrix exch matrix concatmatrix
+ /FontMatrix exch def
+ dup /FontName exch def
+ currentdict end
+ definefont pop
+} bind def
+
+%%IncludeResource: font Symbol
+
+/Symbol-Slanted
+[.89 0.0 15.5 dup sin exch cos div .89 0.0 0.0]
+/Symbol
+MakeTransformedFont
diff --git a/ps/devps/text.enc b/ps/devps/text.enc
new file mode 100644
index 000000000..681a22c1c
--- /dev/null
+++ b/ps/devps/text.enc
@@ -0,0 +1,230 @@
+asciicircum 0
+asciitilde 1
+Scaron 2
+Zcaron 3
+scaron 4
+zcaron 5
+Ydieresis 6
+trademark 7
+space 32
+exclam 33
+quotedbl 34
+numbersign 35
+dollar 36
+percent 37
+ampersand 38
+quoteright 39
+parenleft 40
+parenright 41
+asterisk 42
+plus 43
+comma 44
+hyphen 45
+period 46
+slash 47
+zero 48
+one 49
+two 50
+three 51
+four 52
+five 53
+six 54
+seven 55
+eight 56
+nine 57
+colon 58
+semicolon 59
+less 60
+equal 61
+greater 62
+question 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
+bracketleft 91
+backslash 92
+bracketright 93
+circumflex 94
+underscore 95
+quoteleft 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
+braceleft 123
+bar 124
+braceright 125
+tilde 126
+quotesinglbase 128
+guillemotleft 129
+guillemotright 130
+bullet 131
+florin 132
+fraction 133
+perthousand 134
+dagger 135
+daggerdbl 136
+endash 137
+emdash 138
+ff 139
+fi 140
+fl 141
+ffi 142
+ffl 143
+dotlessi 144
+dotlessj 145
+grave 146
+hungarumlaut 147
+dotaccent 148
+breve 149
+caron 150
+ring 151
+ogonek 152
+quotedblleft 153
+quotedblright 154
+oe 155
+lslash 156
+quotedblbase 157
+OE 158
+Lslash 159
+exclamdown 161
+cent 162
+sterling 163
+currency 164
+yen 165
+brokenbar 166
+section 167
+dieresis 168
+copyright 169
+ordfeminine 170
+guilsinglleft 171
+logicalnot 172
+registered 174
+minus 173
+macron 175
+degree 176
+plusminus 177
+twosuperior 178
+threesuperior 179
+acute 180
+mu 181
+paragraph 182
+periodcentered 183
+cedilla 184
+onesuperior 185
+ordmasculine 186
+guilsinglright 187
+onequarter 188
+onehalf 189
+threequarters 190
+questiondown 191
+Agrave 192
+Aacute 193
+Acircumflex 194
+Atilde 195
+Adieresis 196
+Aring 197
+AE 198
+Ccedilla 199
+Egrave 200
+Eacute 201
+Ecircumflex 202
+Edieresis 203
+Igrave 204
+Iacute 205
+Icircumflex 206
+Idieresis 207
+Eth 208
+Ntilde 209
+Ograve 210
+Oacute 211
+Ocircumflex 212
+Otilde 213
+Odieresis 214
+multiply 215
+Oslash 216
+Ugrave 217
+Uacute 218
+Ucircumflex 219
+Udieresis 220
+Yacute 221
+Thorn 222
+germandbls 223
+agrave 224
+aacute 225
+acircumflex 226
+atilde 227
+adieresis 228
+aring 229
+ae 230
+ccedilla 231
+egrave 232
+eacute 233
+ecircumflex 234
+edieresis 235
+igrave 236
+iacute 237
+icircumflex 238
+idieresis 239
+eth 240
+ntilde 241
+ograve 242
+oacute 243
+ocircumflex 244
+otilde 245
+odieresis 246
+divide 247
+oslash 248
+ugrave 249
+uacute 250
+ucircumflex 251
+udieresis 252
+yacute 253
+thorn 254
+ydieresis 255
diff --git a/ps/devps/textmap b/ps/devps/textmap
new file mode 100644
index 000000000..e71fd58cb
--- /dev/null
+++ b/ps/devps/textmap
@@ -0,0 +1,494 @@
+a a
+b b
+c c
+d d
+e e
+f f
+g g
+h h
+i i
+j j
+k k
+l l
+m m
+n n
+o o
+p p
+q q
+r r
+s s
+t t
+u u
+v v
+w w
+x x
+y y
+z z
+A A
+B B
+C C
+D D
+E E
+F F
+G G
+H H
+I I
+J J
+K K
+L L
+M M
+N N
+O O
+P P
+Q Q
+R R
+S S
+T T
+U U
+V V
+W W
+X X
+Y Y
+Z Z
+AE AE
+Aacute 'A
+Acircumflex ^A
+Adieresis :A
+Agrave `A
+Alpha *A
+Aring oA
+Atilde ~A
+Beta *B
+Cacute 'C
+Ccedilla ,C
+Chi *X
+Delta *D
+Eacute 'E
+Ecircumflex ^E
+Edieresis :E
+Egrave `E
+Epsilon *E
+Eta *Y
+Eth -D
+Gamma *G
+IJ IJ
+Iacute 'I
+Icircumflex ^I
+Idieresis :I
+Ifraktur Im
+Igrave `I
+Iota *I
+Kappa *K
+Lambda *L
+Lslash /L
+Mu *M
+Ntilde ~N
+Nu *N
+OE OE
+Oacute 'O
+Ocircumflex ^O
+Odieresis :O
+Ograve `O
+Omega *W
+Omicron *O
+Oslash /O
+Otilde ~O
+Phi *F
+Pi *P
+Psi *Q
+Rfraktur Re
+Rho *R
+Scaron vS
+Sigma *S
+Tau *T
+Theta *H
+Thorn TP
+Uacute 'U
+Ucircumflex ^U
+Udieresis :U
+Ugrave `U
+Upsilon1 *U
+Xi *C
+Yacute 'Y
+Ydieresis :Y
+Zcaron vZ
+Zeta *Z
+aacute 'a
+acircumflex ^a
+acute aa
+adieresis :a
+ae ae
+agrave `a
+angle /_
+angleleft la
+angleright ra
+aleph Ah
+alpha *a
+ampersand &
+approxequal ~~
+aring oa
+arrowboth <>
+arrowdblboth hA
+arrowdbldown dA
+arrowdblleft lA
+arrowdblright rA
+arrowdblup uA
+arrowdown da
+arrowleft <-
+arrowright ->
+arrowup ua
+asciicircum ha
+asciitilde ti
+asterisk *
+asteriskmath **
+at @
+at at
+atilde ~a
+backslash \
+backslash rs
+bar ba
+bar |
+bell bs
+beta *b
+braceleft lC
+braceleft {
+braceright rC
+braceright }
+bracketleft [
+bracketleft lB
+bracketright ]
+bracketright rB
+breve ab
+brokenbar bb
+bullet bu
+cacute 'c
+caron ah
+ccedilla ,c
+cedilla ac
+cent ct
+checkmark OK
+chi *x
+circle ci
+circlemultiply c*
+circleplus c+
+circumflex a^
+circumflex ^
+colon :
+comma ,
+congruent =~
+copyright co
+currency Cs
+dagger dg
+daggerdbl dd
+degree de
+delta *d
+dieresis ad
+divide di
+dollar $
+dollar Do
+dotaccent a.
+dotlessi .i
+dotlessj .j
+dotmath md
+eacute 'e
+ecircumflex ^e
+edieresis :e
+egrave `e
+eight 8
+element mo
+emdash em
+emptyset es
+endash en
+epsilon *e
+equal =
+equalmath eq
+equivalence ==
+eta *y
+eth Sd
+exclam !
+exclamdown r!
+existential te
+ff ff
+ffi Fi
+ffl Fl
+fi fi
+five 5
+fl fl
+four 4
+gamma *g
+germandbls ss
+gradient gr
+grave ga
+greater >
+greaterequal >=
+guillemotleft Fo
+guillemotright Fc
+guilsinglleft fo
+guilsinglright fc
+handleft lh
+handright rh
+hbar -h
+hungarumlaut a"
+hyphen -
+hyphen hy
+iacute 'i
+icircumflex ^i
+idieresis :i
+igrave `i
+ij ij
+infinity if
+integral is
+intersection ca
+iota *i
+kappa *k
+lambda *l
+less <
+lessequal <=
+logicaland AN
+logicalnot no
+logicalor OR
+lslash /l
+macron a-
+minus \-
+minusplus -+
+minute fm
+mu *m
+multiply mu
+nine 9
+notelement nm
+notequal !=
+notequivalence ne
+notpropersuperset nc
+ntilde ~n
+nu *n
+numbersign #
+numbersign sh
+oacute 'o
+ocircumflex ^o
+odieresis :o
+oe oe
+ogonek ho
+ograve `o
+omega *w
+omicron *o
+one 1
+onehalf 12
+onequarter 14
+onesuperior S1
+ordfeminine Of
+ordmasculine Om
+oslash /o
+otilde ~o
+paragraph ps
+parenleft (
+parenright )
+partialdiff pd
+percent %
+period .
+perthousand %0
+perpendicular pp
+phi *f
+pi *p
+plus +
+plusmath pl
+plusminus +-
+propersubset sb
+propersuperset sp
+proportional pt
+psi *q
+question ?
+questiondown r?
+quotedbl "
+quotedblleft lq
+quotedblright rq
+quoteleft `
+quoteleft oq
+quoteright '
+reflexsubset ib
+reflexsuperset ip
+registered rg
+rho *r
+ring ao
+scaron vs
+second sd
+section sc
+semicolon ;
+seven 7
+sigma *s
+sigma1 ts
+similar ap
+similarequal ~=
+six 6
+slash /
+slash sl
+square sq
+sterling Po
+tau *t
+therefore 3d
+therefore tf
+theta *h
+thorn Tp
+three 3
+threequarters 34
+threesuperior S3
+tilde a~
+tilde ~
+trademark tm
+two 2
+twosuperior S2
+uacute 'u
+ucircumflex ^u
+udieresis :u
+ugrave `u
+underscore _
+union cu
+universal fa
+upsilon *u
+weierstrass wp
+xi *c
+yacute 'y
+ydieresis :y
+yen Ye
+zcaron vz
+zero 0
+zeta *z
+exclamdown char161
+cent char162
+sterling char163
+currency char164
+yen char165
+brokenbar char166
+section char167
+dieresis char168
+copyright char169
+ordfeminine char170
+guillemotleft char171
+logicalnot char172
+hyphen char173
+registered char174
+macron char175
+degree char176
+plusminus char177
+twosuperior char178
+threesuperior char179
+acute char180
+mu char181
+paragraph char182
+periodcentered char183
+cedilla char184
+onesuperior char185
+ordmasculine char186
+guillemotright char187
+onequarter char188
+onehalf char189
+threequarters char190
+questiondown char191
+Agrave char192
+Aacute char193
+Acircumflex char194
+Atilde char195
+Adieresis char196
+Aring char197
+AE char198
+Ccedilla char199
+Egrave char200
+Eacute char201
+Ecircumflex char202
+Edieresis char203
+Igrave char204
+Iacute char205
+Icircumflex char206
+Idieresis char207
+Eth char208
+Ntilde char209
+Ograve char210
+Oacute char211
+Ocircumflex char212
+Otilde char213
+Odieresis char214
+multiply char215
+Oslash char216
+Ugrave char217
+Uacute char218
+Ucircumflex char219
+Udieresis char220
+Yacute char221
+Thorn char222
+germandbls char223
+agrave char224
+aacute char225
+acircumflex char226
+atilde char227
+adieresis char228
+aring char229
+ae char230
+ccedilla char231
+egrave char232
+eacute char233
+ecircumflex char234
+edieresis char235
+igrave char236
+iacute char237
+icircumflex char238
+idieresis char239
+eth char240
+ntilde char241
+ograve char242
+oacute char243
+ocircumflex char244
+otilde char245
+odieresis char246
+divide char247
+oslash char248
+ugrave char249
+uacute char250
+ucircumflex char251
+udieresis char252
+yacute char253
+thorn char254
+ydieresis char255
+fraction f/
+club CL
+diamond DI
+heart HE
+spade SP
+carriagereturn CR
+suchthat st
+bracelefttp bracelefttp
+braceleftmid braceleftmid
+braceleftbt braceleftbt
+braceex braceex
+braceex bracerightex
+braceex braceleftex
+braceex barex
+bracerighttp bracerighttp
+bracerightmid bracerightmid
+bracerightbt bracerightbt
+parenlefttp parenlefttp
+parenleftbt parenleftbt
+parenleftex parenleftex
+parenrighttp parenrighttp
+parenrightbt parenrightbt
+parenrightex parenrightex
+bracketlefttp bracketlefttp
+bracketleftbt bracketleftbt
+bracketleftex bracketleftex
+bracketrighttp bracketrighttp
+bracketrightbt bracketrightbt
+bracketrightex bracketrightex
+radical sr
+radicalex rn
+approxequal ~=
+bracketlefttp lc
+bracketleftbt lf
+bracketrighttp rc
+bracketrightbt rf
+bracelefttp lt
+braceleftmid lk
+braceleftbt lb
+braceex bv
+bracerighttp rt
+bracerightmid rk
+bracerightbt rb
+summation sum
+product product
diff --git a/ps/devps/zapfdr.ps b/ps/devps/zapfdr.ps
new file mode 100644
index 000000000..8283be26d
--- /dev/null
+++ b/ps/devps/zapfdr.ps
@@ -0,0 +1,225 @@
+%!PS-Adobe-3.0 Resource-Font
+%%DocumentNeededResources: font ZapfDingbats
+
+%%IncludeResource: font ZapfDingbats
+
+/ZapfDingbats findfont [-1 0 0 1 0 0] makefont
+
+dup length 1 add dict begin
+{
+ exch dup dup /FID ne exch /UniqueID ne and {
+ exch def
+ } {
+ pop pop
+ } ifelse
+} forall
+
+/FontName /ZapfDingbats-Reverse def
+
+/Metrics 202 dict dup begin
+ /space [0 -278] def
+ /a1 [-939 -974] def
+ /a2 [-926 -961] def
+ /a202 [-939 -974] def
+ /a3 [-945 -980] def
+ /a4 [-685 -719] def
+ /a5 [-754 -789] def
+ /a119 [-755 -790] def
+ /a118 [-756 -791] def
+ /a117 [-655 -690] def
+ /a11 [-925 -960] def
+ /a12 [-904 -939] def
+ /a13 [-520 -549] def
+ /a14 [-821 -855] def
+ /a15 [-876 -911] def
+ /a16 [-898 -933] def
+ /a105 [-876 -911] def
+ /a17 [-910 -945] def
+ /a18 [-939 -974] def
+ /a19 [-721 -755] def
+ /a20 [-811 -846] def
+ /a21 [-727 -762] def
+ /a22 [-726 -761] def
+ /a23 [-572 -571] def
+ /a24 [-641 -677] def
+ /a25 [-728 -763] def
+ /a26 [-725 -760] def
+ /a27 [-724 -759] def
+ /a28 [-719 -754] def
+ /a6 [-459 -494] def
+ /a7 [-517 -552] def
+ /a8 [-502 -537] def
+ /a9 [-542 -577] def
+ /a10 [-657 -692] def
+ /a29 [-751 -786] def
+ /a30 [-753 -788] def
+ /a31 [-753 -788] def
+ /a32 [-755 -790] def
+ /a33 [-758 -793] def
+ /a34 [-759 -794] def
+ /a35 [-781 -816] def
+ /a36 [-788 -823] def
+ /a37 [-754 -789] def
+ /a38 [-806 -841] def
+ /a39 [-788 -823] def
+ /a40 [-798 -833] def
+ /a41 [-781 -816] def
+ /a42 [-796 -831] def
+ /a43 [-888 -923] def
+ /a44 [-709 -744] def
+ /a45 [-688 -723] def
+ /a46 [-714 -749] def
+ /a47 [-756 -790] def
+ /a48 [-757 -792] def
+ /a49 [-660 -695] def
+ /a50 [-741 -776] def
+ /a51 [-733 -768] def
+ /a52 [-757 -792] def
+ /a53 [-724 -759] def
+ /a54 [-672 -707] def
+ /a55 [-673 -708] def
+ /a56 [-647 -682] def
+ /a57 [-666 -701] def
+ /a58 [-791 -826] def
+ /a59 [-780 -815] def
+ /a60 [-754 -789] def
+ /a61 [-754 -789] def
+ /a62 [-673 -707] def
+ /a63 [-651 -687] def
+ /a64 [-661 -696] def
+ /a65 [-654 -689] def
+ /a66 [-752 -786] def
+ /a67 [-752 -787] def
+ /a68 [-678 -713] def
+ /a69 [-756 -791] def
+ /a70 [-749 -785] def
+ /a71 [-756 -791] def
+ /a72 [-838 -873] def
+ /a73 [-726 -761] def
+ /a74 [-727 -762] def
+ /a203 [-727 -762] def
+ /a75 [-724 -759] def
+ /a204 [-724 -759] def
+ /a76 [-857 -892] def
+ /a77 [-857 -892] def
+ /a78 [-753 -788] def
+ /a79 [-749 -784] def
+ /a81 [-403 -438] def
+ /a82 [-103 -138] def
+ /a83 [-242 -277] def
+ /a84 [-380 -415] def
+ /a97 [-357 -392] def
+ /a98 [-358 -392] def
+ /a99 [-633 -668] def
+ /a100 [-632 -668] def
+ /a101 [-697 -732] def
+ /a102 [-488 -544] def
+ /a103 [-510 -544] def
+ /a104 [-875 -910] def
+ /a106 [-632 -667] def
+ /a107 [-725 -760] def
+ /a108 [-760 -760] def
+ /a112 [-741 -776] def
+ /a111 [-561 -595] def
+ /a110 [-659 -694] def
+ /a109 [-592 -626] def
+ /a120 [-753 -788] def
+ /a121 [-753 -788] def
+ /a122 [-753 -788] def
+ /a123 [-753 -788] def
+ /a124 [-753 -788] def
+ /a125 [-753 -788] def
+ /a126 [-753 -788] def
+ /a127 [-753 -788] def
+ /a128 [-753 -788] def
+ /a129 [-753 -788] def
+ /a130 [-753 -788] def
+ /a131 [-753 -788] def
+ /a132 [-753 -788] def
+ /a133 [-753 -788] def
+ /a134 [-753 -788] def
+ /a135 [-753 -788] def
+ /a136 [-753 -788] def
+ /a137 [-753 -788] def
+ /a138 [-753 -788] def
+ /a139 [-753 -788] def
+ /a140 [-753 -788] def
+ /a141 [-753 -788] def
+ /a142 [-753 -788] def
+ /a143 [-753 -788] def
+ /a144 [-753 -788] def
+ /a145 [-753 -788] def
+ /a146 [-753 -788] def
+ /a147 [-753 -788] def
+ /a148 [-753 -788] def
+ /a149 [-753 -788] def
+ /a150 [-753 -788] def
+ /a151 [-753 -788] def
+ /a152 [-753 -788] def
+ /a153 [-753 -788] def
+ /a154 [-753 -788] def
+ /a155 [-753 -788] def
+ /a156 [-753 -788] def
+ /a157 [-753 -788] def
+ /a158 [-753 -788] def
+ /a159 [-753 -788] def
+ /a160 [-859 -894] def
+ /a161 [-803 -838] def
+ /a163 [-982 -1016] def
+ /a164 [-423 -458] def
+ /a196 [-713 -748] def
+ /a165 [-889 -924] def
+ /a192 [-713 -748] def
+ /a166 [-883 -918] def
+ /a167 [-892 -927] def
+ /a168 [-893 -928] def
+ /a169 [-893 -928] def
+ /a170 [-799 -834] def
+ /a171 [-838 -873] def
+ /a172 [-793 -828] def
+ /a173 [-889 -924] def
+ /a162 [-889 -924] def
+ /a174 [-882 -917] def
+ /a175 [-895 -930] def
+ /a176 [-896 -931] def
+ /a177 [-428 -463] def
+ /a178 [-848 -883] def
+ /a179 [-801 -836] def
+ /a193 [-801 -836] def
+ /a180 [-832 -867] def
+ /a199 [-832 -867] def
+ /a181 [-661 -696] def
+ /a200 [-661 -696] def
+ /a182 [-839 -874] def
+ /a201 [-839 -874] def
+ /a183 [-725 -760] def
+ /a184 [-911 -946] def
+ /a197 [-737 -771] def
+ /a185 [-830 -865] def
+ /a194 [-737 -771] def
+ /a198 [-854 -888] def
+ /a186 [-932 -967] def
+ /a195 [-854 -888] def
+ /a187 [-796 -831] def
+ /a188 [-837 -873] def
+ /a189 [-892 -927] def
+ /a190 [-935 -970] def
+ /a191 [-884 -918] def
+ /a205 [-474 -509] def
+ /a206 [-375 -410] def
+ /a85 [-474 -509] def
+ /a86 [-375 -410] def
+ /a87 [-199 -234] def
+ /a88 [-199 -234] def
+ /a89 [-355 -390] def
+ /a90 [-355 -390] def
+ /a91 [-241 -276] def
+ /a92 [-241 -276] def
+ /a93 [-282 -317] def
+ /a94 [-282 -317] def
+ /a95 [-299 -334] def
+ /a96 [-299 -334] def
+
+end def
+
+/ZapfDingbats-Reverse currentdict end definefont pop
diff --git a/ps/pfbtops.c b/ps/pfbtops.c
new file mode 100644
index 000000000..37bbf0809
--- /dev/null
+++ b/ps/pfbtops.c
@@ -0,0 +1,94 @@
+/* This translates ps fonts in .pfb format to ASCII ps files. */
+
+#include <stdio.h>
+
+/* Binary bytes per output line. */
+#define BYTES_PER_LINE (79/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()
+{
+ fprintf(stderr, "usage: %s [pfb_file]\n", program_name);
+ exit(1);
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ program_name = argv[0];
+ if (argc > 2)
+ usage();
+ if (argc == 2 && !freopen(argv[1], "r", stdin))
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+ 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/ps/ps.c b/ps/ps.c
new file mode 100644
index 000000000..170a5f6ae
--- /dev/null
+++ b/ps/ps.c
@@ -0,0 +1,1456 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#include "ps.h"
+#include "broken.h"
+
+static int landscape_flag = 0;
+static int ncopies = 1;
+static int linewidth = -1;
+
+unsigned broken_flags = BROKEN_SPOOLER_FLAGS;
+
+#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
+#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/M_PI;
+}
+
+double radians(double d)
+{
+ return d*M_PI/180.0;
+}
+
+inline double transform_fill(int fill)
+{
+ return 1 - fill/double(FILL_MAX);
+}
+
+ps_output::ps_output(FILE *f, int n)
+: fp(f), max_line_length(n), col(0), 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;
+ for (int i = 0; i < n; i++) {
+ char c = s[i];
+ if (isascii(c) && isprint(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 (isascii(c) && isprint(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 = iftoa(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(int argc, const char **argv);
+ 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(0), reencoded_name(0), encoding_index(-1)
+{
+}
+
+ps_font::~ps_font()
+{
+ delete encoding;
+ delete reencoded_name;
+}
+
+void ps_font::handle_unknown_font_command(int argc, const char **argv)
+{
+ if (strcmp(argv[0], "encoding") == 0) {
+ if (argc != 2)
+ error("`encoding' command requires exactly 1 argument");
+ else
+ encoding = strsave(argv[1]);
+ }
+}
+
+
+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;
+
+ 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 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);
+ void draw(int code, int *p, int np, const environment *env);
+ void begin_page(int);
+ void end_page();
+ void special(char *arg, const environment *env);
+ font *make_font(const char *);
+ void end_of_line();
+};
+
+ps_printer::ps_printer()
+: pages_output(0),
+ sbuf_len(0),
+ output_hpos(-1),
+ output_vpos(-1),
+ out(0, 79),
+ ndefined_styles(0),
+ next_encoding_index(0),
+ line_thickness(-1),
+ fill(FILL_MAX + 1),
+ ndefs(0)
+{
+ static char temp_filename[] = "/tmp/gropsXXXXXX";
+ mktemp(temp_filename);
+ tempfp = fopen(temp_filename, "w+");
+ if (tempfp == 0)
+ fatal("can't open temporary file `%1': %2",
+ temp_filename, strerror(errno));
+ if (unlink(temp_filename) < 0)
+ error("can't unlink `%1': %2", temp_filename, strerror(errno));
+ 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)
+{
+ if (i == space_char_index)
+ 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;
+}
+
+int is_small_h(int n)
+{
+ return n < (font::res*2)/72 && n > -(font::res*10)/72;
+}
+
+int is_small_v(int n)
+{
+ return n < (font::res*4)/72 && n > -(font::res*4)/72;
+}
+
+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];
+ for (int 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 (isascii(*p) && isspace(*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++;
+ }
+ 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]);
+ }
+ 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);
+ }
+ }
+ 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
+ || !is_small_h(output_hpos - sbuf_start_hpos)
+ || !is_small_v(output_vpos - sbuf_vpos))
+ 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 sym[2];
+ sym[0] = 'A' + 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)
+{
+ 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);
+ int x = p[0] + p[2];
+ int y = p[1] + p[3];
+ double n = p[0]*double(x) + p[1]*double(y);
+ if (n == 0)
+ out.put_fix_number(x + env->hpos)
+ .put_fix_number(y + env->vpos)
+ .put_fix_number(env->hpos)
+ .put_fix_number(env->vpos)
+ .put_symbol("DL");
+ else {
+ double k = (double(x)*x + double(y)*y)/(2.0*n);
+ double cx = k*p[0];
+ double cy = k*p[1];
+ out.put_fix_number(env->hpos + int(cx))
+ .put_fix_number(env->vpos + int(cy))
+ .put_fix_number(int(sqrt(cx*cx + cy*cy)))
+ .put_float(degrees(atan2(-cy, -cx)))
+ .put_float(degrees(atan2(y - cy, x - cx)))
+ .put_symbol("DA");
+ }
+ }
+ 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(itoa(n));
+ out.comment_arg(itoa(++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()
+{
+ flush_sbuf();
+ out.put_symbol("EP");
+}
+
+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-3.0\n", stdout);
+ 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();
+ }
+ 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(itoa(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").put_fix_number(paper_length).put_symbol("def");
+ out.put_literal_symbol("LS")
+ .put_symbol(landscape_flag ? "true" : "false")
+ .put_symbol("def");
+ 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)
+{
+ 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,
+ };
+ for (char *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) {
+ flush_sbuf();
+ (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)
+{
+ 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;
+}
+
+void ps_printer::do_file(char *arg, const environment *env)
+{
+ 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;
+}
+
+void ps_printer::do_def(char *arg, const environment *)
+{
+ 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 *)
+{
+ 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)
+{
+ while (*arg == ' ' || *arg == '\n')
+ arg++;
+ for (char *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");
+ out.put_symbol("userdict")
+ .put_symbol("begin");
+ rm.import_file(arg, out);
+ out.put_symbol("end");
+ out.put_symbol("PEND");
+}
+
+
+printer *make_printer()
+{
+ return new ps_printer;
+}
+
+static void usage();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ while ((c = getopt(argc, argv, "F:lc:w:vb:")) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "grops version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'c':
+ if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
+ error("bad number of copies `%s'", optarg);
+ ncopies = 1;
+ }
+ break;
+ case 'l':
+ landscape_flag = 1;
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'w':
+ if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
+ error("bad linewidth `%s'", optarg);
+ linewidth = -1;
+ }
+ break;
+ case 'b':
+ // XXX check this
+ broken_flags = atoi(optarg);
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ exit(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-blv] [-c n] [-w n] [-F dir] [files ...]\n",
+ program_name);
+ exit(1);
+}
diff --git a/ps/ps.h b/ps/ps.h
new file mode 100644
index 000000000..1faae0bf5
--- /dev/null
+++ b/ps/ps.h
@@ -0,0 +1,127 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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
+ };
+
+
+extern "C" {
+ // Sun's stdlib.h fails to declare this.
+ char *mktemp(char *);
+}
diff --git a/ps/psbb.c b/ps/psbb.c
new file mode 100644
index 000000000..d63bfcd8c
--- /dev/null
+++ b/ps/psbb.c
@@ -0,0 +1,164 @@
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+
+struct bounding_box {
+ int llx, lly, urx, ury;
+};
+
+#ifdef __STDC__
+const char *do_file(FILE *, struct bounding_box *);
+int parse_bounding_box(char *, struct bounding_box *);
+#else
+#define const /* as nothing */
+const char *do_file();
+int parse_bounding_box();
+#endif
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ FILE *fp;
+ const char *message;
+ struct bounding_box bb;
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
+ exit(3);
+ }
+ fp = fopen(argv[1], "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: can't open `%s': ", argv[0], argv[1]);
+ perror((char *)NULL);
+ exit(2);
+ }
+ message = do_file(fp, &bb);
+ if (message) {
+ fprintf(stderr, "%s: ", argv[0]);
+ fprintf(stderr, message, argv[1]);
+ putc('\n', stderr);
+ exit(1);
+ }
+ printf("%d %d %d %d\n", bb.llx, bb.lly, bb.urx, bb.ury);
+ exit(0);
+}
+
+/* If the bounding box was found return NULL, and store the bounding box
+in bb. If the bounding box was not found return a string suitable for
+giving to printf with the filename as an argument saying why not. */
+
+const char *do_file(fp, bb)
+FILE *fp;
+struct bounding_box *bb;
+{
+ int bb_at_end = 0;
+ char buf[256];
+ if (!fgets(buf, sizeof(buf), fp))
+ return "%s is empty";
+ if (strncmp("%!PS-Adobe-", buf, 11) != 0)
+ return "%s is not conforming";
+ while (fgets(buf, sizeof(buf), fp) != 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)
+ return NULL;
+ else if (res == 2) {
+ bb_at_end = 1;
+ break;
+ }
+ else
+ return "the arguments to the %%%%BoundingBox comment in %s are bad";
+ }
+ }
+ 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 (fgets(buf, sizeof(buf), fp) != 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)
+ return "`(atend)' not allowed in trailer";
+ else
+ return "the arguments to the %%%%BoundingBox comment in %s are bad";
+ }
+ }
+ }
+ }
+ if (got_bb)
+ return NULL;
+ }
+ }
+ return "%%%%BoundingBox comment not found in %s";
+}
+
+/* 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(p, bb)
+char *p;
+struct 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;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/ps/psfig.diff b/ps/psfig.diff
new file mode 100644
index 000000000..1339ba221
--- /dev/null
+++ b/ps/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.psfig in this directory. 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.uucp
+
+*** 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/ps/psrm.c b/ps/psrm.c
new file mode 100644
index 000000000..8db4432da
--- /dev/null
+++ b/ps/psrm.c
@@ -0,0 +1,1086 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "driver.h"
+#include "stringclass.h"
+#include "cset.h"
+
+#include "ps.h"
+
+#define 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]);
+
+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)
+: type(t), revision(r), flags (0), filename(0), rank(-1), next(0)
+{
+ 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()
+{
+ 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()
+: resource_list(0), extensions(0), language_level(0)
+{
+ read_download_file();
+ string procset_name("grops");
+ extern const char *version_string;
+ string procset_version(version_string);
+ procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
+ procset_version, 0);
+ 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)
+{
+ for (resource *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)
+{
+ for (resource *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;
+ for (resource *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];
+ for (int 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];
+ }
+ delete head;
+ 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;
+ 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);
+ 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);
+ delete r->filename;
+ r->filename = 0;
+ }
+ }
+ else {
+ fp = fopen(r->filename, "r");
+ if (!fp) {
+ error("can't open `%1': %2", r->filename, strerror(errno));
+ 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)
+ 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;
+ 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 (++i == PS_LINE_MAX)
+ error("line too long");
+ 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;
+}
+
+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;
+}
+
+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;
+ }
+ for (int 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));
+ for (int 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;
+ for (int 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 (int 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);
+ }
+ 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;
+ for (int 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]);
+ 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/ps/tmac.ps b/ps/tmac.ps
new file mode 100644
index 000000000..07196d961
--- /dev/null
+++ b/ps/tmac.ps
@@ -0,0 +1,115 @@
+.nr _C \n(.C
+.cp 0
+.ftr AX ABI
+.ftr KR BMR
+.ftr KI BMI
+.ftr KB BMB
+.ftr KX BMBI
+.ftr CW CR
+.ftr CO CI
+.ftr CX CBI
+.ftr H HR
+.ftr HO HI
+.ftr HX HBI
+.ftr Hr HNR
+.ftr Hi HNI
+.ftr Hb HNB
+.ftr Hx HNBI
+.ftr NX NBI
+.ftr PA PR
+.ftr PX PBI
+.ftr ZI ZCMI
+.ftr C CR
+.char \(rn \h'-\w'\(sr'u'\(rn\h'\w'\(sr'u'
+.char \[parenlefttp] \[parenlefttp]\h'.016m'
+.char \[parenleftbt] \[parenleftbt]\h'.016m'
+.char \[parenleftex] \[parenleftex]\h'.016m'
+.char \[parenrighttp] \[parenrighttp]\h'.016m'
+.char \[parenrightbt] \[parenrightbt]\h'.016m'
+.char \[parenrightex] \[parenrightex]\h'.016m'
+.char \[ci] \v'-.25m'\h'.05m'\D'c .5m'\h'.05m'\v'.25m'
+.char \[sq] \h'.05m'\D'l .5m 0'\D'l 0 -.5m'\D'l -.5m 0'\D'l 0 .5m'\h'.55m'
+.char \[ru] \D'l .5m 0'
+.char \[ul] \v'.25m'\D'l .5m 0'\v'-.25m'
+.char \[br] \Z'\v'.25m'\D'l 0 -1m''
+.char \[or] \h'.1m'\Z'\D'l 0 -.675m''\h'.1m'
+.char \[12] \v'-.7m\s[\\n(.s*6u/10u]+.7m'1\v'-.7m\s0+.7m'\
+\(f/\s[\\n(.s*6u/10u]2\s0
+.char \[14] \v'-.7m\s[\\n(.s*6u/10u]+.7m'1\v'-.7m\s0+.7m'\
+\(f/\s[\\n(.s*6u/10u]4\s0
+.char \[34] \v'-.7m\s[\\n(.s*6u/10u]+.7m'3\v'-.7m\s0+.7m'\
+\(f/\s[\\n(.s*6u/10u]4\s0
+.char \[S1] \v'-.2m'\s-31\s+3\v'+.2m'
+.char \[S2] \v'-.2m'\s-32\s+3\v'+.2m'
+.char \[S3] \v'-.2m'\s-33\s+3\v'+.2m'
+.tr \[char178]\[S2]
+.tr \[char179]\[S3]
+.tr \[char185]\[S1]
+.tr \[char188]\[14]
+.tr \[char189]\[12]
+.tr \[char190]\[34]
+.char \[Fi] ffi
+.char \[Fl] ffl
+.char \[ff] ff
+.char \[ij] ij
+.char \[IJ] IJ
+.if !c\[tm] .char \[tm] \s-3\v'-.3m'TM\v'+.3m'\s+3
+.cp \n(_C
+.\" ignore the rest of the file in compatibility mode
+.if \n(.C .nx /dev/null
+.de ps-achar
+.char \\$1 \\$3\
+\k[acc]\
+\h'(u;-\w'\\$2'-\w'\\$3'/2+\\\\n[skw]+(\w'x'*0)-\\\\n[skw])'\
+\v'(u;\w'x'*0+\\\\n[rst]+(\w'\\$3'*0)-\\\\n[rst])'\
+\\$2\
+\v'(u;\w'x'*0-\\\\n[rst]+(\w'\\$3'*0)+\\\\n[rst])'\
+\h'|\\\\n[acc]u'
+.ie '\\$3'\(.i' .hcode \\$1i
+.el .hcode \\$1\\$3
+..
+.if !c\['y] \{\
+.ps-achar \['y] \[aa] y
+.tr \[char253]\['y]
+.\}
+.if !c\['Y] \{\
+.ps-achar \['Y] \[aa] Y
+.tr \[char221]\['Y]
+.\}
+.if !c\['c] .ps-achar \['c] \[aa] c
+.if !c\['C] .ps-achar \['C] \[aa] C
+.de ps-bb
+.nr ps-nargs \\n[.$]
+.if \\n[ps-nargs]=4 \{\
+. nr ps-llx 0\\$1
+. nr ps-lly 0\\$2
+. nr ps-urx 0\\$3
+. nr ps-ury 0\\$4
+.\}
+..
+.de PSPIC
+.sy echo .ps-bb `psbb \\$1` >/tmp/psbb\\n[$$]
+.so /tmp/psbb\\n[$$]
+.if \\n[ps-nargs]=4 \{\
+. nr ps-wid (\\n[ps-urx]-\\n[ps-llx])
+. nr ps-ht (\\n[ps-ury]-\\n[ps-lly])
+. if \\n[ps-wid]<0 .nr ps-wid 0-\\n[ps-wid]
+. if \\n[ps-ht]<0 .nr ps-ht 0-\\n[ps-ht]
+. ie \\n[.$]>=2 .nr ps-deswid (i;\\$2)
+. el .nr ps-deswid \\n[.l]-\\n[.i]<?\\n[ps-wid]p
+. ie \\n[.$]>=3 .nr ps-desht (i;\\$3)
+. el .nr ps-desht \\n[ps-deswid]*1000+(\\n[ps-wid]/2)/\\n[ps-wid]\
+*\\n[ps-ht]+500/1000
+. ne \\n[ps-desht]u+1v
+. sp \\n[ps-desht]u
+. nr ps-offset \\n[.l]-\\n[.i]-\\n[ps-deswid]/2
+. ie \\n[.$]>=3 \
+\h'\\n[ps-offset]u'\X'ps: import \\$1 \\n[ps-llx] \\n[ps-lly] \\n[ps-urx] \
+\\n[ps-ury] \\n[ps-deswid] \\n[ps-desht]'
+. el \
+\h'\\n[ps-offset]u'\X'ps: import \\$1 \\n[ps-llx] \\n[ps-lly] \\n[ps-urx] \
+\\n[ps-ury] \\n[ps-deswid]'
+. br
+.\}
+.sy rm /tmp/psbb\\n[$$]
+..
diff --git a/ps/tmac.psatk b/ps/tmac.psatk
new file mode 100644
index 000000000..3993c86fb
--- /dev/null
+++ b/ps/tmac.psatk
@@ -0,0 +1,61 @@
+.\" Implementation of the ATK PB and PE macros for use with groff and grops.
+.\" Load this after tmac.atk.
+.nr zT 0
+.if '\*(.T'ps' .nr zT 1
+.nr psatk-unit 1p
+.de psatk-defs
+ps: mdef 5
+/PB {
+ /saved save def
+ currentpoint translate
+ \n[psatk-unit] u -\n[psatk-unit] u scale
+ /showpage {} def
+ userdict begin
+} bind def
+/PE {
+ end
+ saved restore
+} bind def
+/troffadjust {
+ pop 0
+} bind def
+..
+.de PB
+.ne \\$1p
+.nr zT \\n(zT>0
+\\*[PB\\n(zT]\\
+..
+.de PE
+\\*[PE\\n(zT]\\
+..
+.ds PB0
+.\" The last line before the "'PE" is "\}" rather than ".\}". This
+.\" would cause a spurious space to be introduced before any picture
+.\" that was the first thing on a line. So we have to catch that and
+.\" remove it.
+.de PB1
+.ev psatk
+.fi
+.di psatk-mac
+\!ps: exec PB
+..
+.de PE0
+\v'-.75m'\
+\D'l \\$1p 0'\D'l 0 \\$2p'\D'l -\\$1p 0'\D'l 0 -\\$2p'\
+\h'\\$1p'\v'.75m'\x'\\$2p-1m>?0'\c
+..
+.ds psatk-init \Y[psatk-defs]
+.de PE1
+\!PE
+.di
+.di null
+.br
+.di
+.rm null
+.ev
+\v'-.75m'\
+\\*[psatk-init]\Y[psatk-mac]\
+\h'\\$1p'\v'.75m'\x'\\$2p-1m>?0'\c
+.rm psatk-mac
+.if \\n(.P .ds psatk-init
+..
diff --git a/ps/tmac.psfig b/ps/tmac.psfig
new file mode 100644
index 000000000..6db9a7114
--- /dev/null
+++ b/ps/tmac.psfig
@@ -0,0 +1,86 @@
+.\" These are macros to make psfig work with groff. See the file psfig.diff.
+.de psfig-defs
+ps: mdef 100
+
+% wid ht llx lly urx ury psfigstart -
+
+/psfigstart {
+ /level1 save def
+ /ury exch def
+ /urx exch def
+ /lly exch def
+ /llx exch def
+ /ht exch u def
+ /wid exch u def
+ currentpoint ht add translate
+ wid urx llx sub div ht ury lly sub div neg scale
+ llx neg lly neg translate
+
+ % set the graphics state to default values
+ 0 setgray
+ 0 setlinecap
+ 1 setlinewidth
+ 0 setlinejoin
+ 10 setmiterlimit
+ [] 0 setdash
+ newpath
+ /showpage {} def
+} bind def
+
+% psfigclip -
+
+/psfigclip {
+ currentpoint newpath
+ llx lly moveto
+ urx lly lineto
+ urx ury lineto
+ llx ury lineto
+ closepath clip
+ newpath moveto
+} bind def
+
+% psfigend -
+
+/psfigend {
+ level1 restore
+} bind def
+
+% globalstart -
+
+/globalstart {
+ % save the current space code on the stack
+ SC
+ level0 restore
+} bind def
+
+% globalend -
+
+/globalend {
+ end
+ BP
+ /SC exch def
+ DEFS begin
+} bind def
+..
+.de psfig-init
+.if \\n[.P] \{\
+\Y[psfig-defs]
+. br
+. sp -1
+. ds psfig-init\" empty
+. rm psfig-defs
+.\}
+..
+.de F+
+.br
+.psfig-init
+.nr psfig-fill \\n[.u]
+.nf
+.sp -.5
+.if !\\n[.$] .ce 9999
+..
+.de F-
+.br
+.ce 0
+.if \\n[psfig-fill] .fi
+..
diff --git a/refer/Makefile b/refer/Makefile
new file mode 100644
index 000000000..879cfb750
--- /dev/null
+++ b/refer/Makefile
@@ -0,0 +1,165 @@
+#Copyright (C) 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# BINDIR says where to install the executables.
+BINDIR=/usr/local/bin
+# CC is the C++ compiler.
+CC=g++
+# OLDCC is the C compiler.
+OLDCC=gcc
+# Suffix to be used for index files.
+INDEX_SUFFIX=.i
+# Directory containing the default index.
+DEFAULT_INDEX_DIR=/usr/dict/papers
+# The filename (without suffix) of the default index.
+DEFAULT_INDEX_NAME=Ind
+# COMMON_WORDS_FILE is a file containing a list of common words.
+# If your system provides /usr/lib/eign it will be copied onto this,
+# otherwise the supplied eign file will be used.
+COMMON_WORDS_FILE=/usr/local/lib/groff/eign
+
+CFLAGS=-g -O -Wall #-DHAVE_MMAP -DHAVE_RENAME -DHAVE_GETWD
+LDFLAGS=-g
+INCLUDES=-I../lib
+OLDCFLAGS=-g
+LIBS=
+MLIBS=$(LIBS) -lm
+# YACC can be either yacc or bison -y
+YACC=bison -y
+YACCFLAGS=-v
+ETAGS=etags
+ETAGSFLAGS=-p
+
+PROGS=refer lookbib indxbib lkbib
+
+INDXBIB_OBJECTS=indxbib.o common.o ../lib/libgroff.a
+LOOKBIB_OBJECTS=lookbib.o search.o linear.o index.o common.o \
+ ../lib/libgroff.a
+REFER_OBJECTS=refer.o ref.o token.o search.o linear.o index.o \
+ label.tab.o command.o common.o ../lib/libgroff.a
+LKBIB_OBJECTS=lkbib.o search.o linear.o index.o common.o ../lib/libgroff.a
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: $(PROGS)
+
+refer: $(REFER_OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $(REFER_OBJECTS) $(MLIBS)
+
+indxbib: $(INDXBIB_OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $(INDXBIB_OBJECTS) $(MLIBS)
+
+lookbib: $(LOOKBIB_OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $(LOOKBIB_OBJECTS) $(MLIBS)
+
+lkbib: $(LKBIB_OBJECTS)
+ $(CC) $(LDFLAGS) -o $@ $(LKBIB_OBJECTS) $(MLIBS)
+
+label.tab.c: label.y
+ $(YACC) $(YACCFLAGS) label.y
+ mv y.tab.c label.tab.c
+
+limits.h: genlimits
+ genlimits >$@
+
+genlimits: genlimits.c
+ defs=; \
+ if [ -f /usr/include/limits.h ]; \
+ then defs=-DHAVE_LIMITS_H; \
+ fi; \
+ if [ -f /usr/include/dirent.h ]; \
+ then defs="$$defs -DHAVE_DIRENT_H"; \
+ fi; \
+ $(OLDCC) -o $@ $$defs genlimits.c
+
+suffix.h: FORCE
+ @echo \#define INDEX_SUFFIX \"$(INDEX_SUFFIX)\" >suffix.h.n
+ @cmp -s suffix.h.n suffix.h || cp suffix.h.n suffix.h
+ @rm -f suffix.h.n
+
+path.h: FORCE
+ @echo \#define COMMON_WORDS_FILE \"$(COMMON_WORDS_FILE)\" >path.h.n
+ @echo \#define DEFAULT_INDEX_DIR \"$(DEFAULT_INDEX_DIR)\" >>path.h.n
+ @echo \#define DEFAULT_INDEX_NAME \"$(DEFAULT_INDEX_NAME)\" >>path.h.n
+ @cmp -s path.h.n path.h || cp path.h.n path.h
+ @rm -f path.h.n
+
+index.o: refid.h search.h index.h suffix.h ../lib/lib.h ../lib/cset.h \
+ ../lib/cmap.h ../lib/errarg.h ../lib/error.h
+indxbib.o: refer.h path.h limits.h index.h suffix.h \
+ ../lib/errarg.h ../lib/error.h ../lib/cset.h ../lib/cmap.h \
+ ../lib/stringclass.h ../lib/lib.h
+linear.o: refid.h search.h ../lib/lib.h ../lib/errarg.h \
+ ../lib/error.h ../lib/cset.h ../lib/cmap.h
+lookbib.o: refid.h search.h ../lib/errarg.h ../lib/error.h \
+ ../lib/lib.h ../lib/cset.h
+ref.o: refer.h path.h refid.h ref.h token.h ../lib/errarg.h \
+ ../lib/error.h ../lib/lib.h ../lib/stringclass.h \
+ ../lib/cset.h ../lib/cmap.h
+refer.o: refer.h path.h refid.h ref.h token.h search.h command.h \
+ ../lib/errarg.h ../lib/error.h ../lib/lib.h \
+ ../lib/stringclass.h ../lib/cset.h ../lib/cmap.h
+command.o: refer.h path.h command.h refid.h search.h ../lib/lib.h \
+ ../lib/cset.h ../lib/cmap.h ../lib/errarg.h ../lib/error.h
+search.o: refid.h search.h ../lib/lib.h ../lib/errarg.h \
+ ../lib/error.h
+token.o: refer.h path.h token.h ../lib/errarg.h ../lib/error.h \
+ ../lib/lib.h ../lib/stringclass.h ../lib/cset.h \
+ ../lib/cmap.h
+label.tab.o: refer.h path.h refid.h ref.h token.h \
+ ../lib/errarg.h ../lib/error.h ../lib/lib.h \
+ ../lib/stringclass.h ../lib/cset.h ../lib/cmap.h
+lkbib.o: refer.h path.h refid.h search.h ../lib/errarg.h ../lib/error.h \
+ ../lib/lib.h ../lib/cset.h ../lib/cmap.h \
+ ../lib/stringclass.h
+
+clean:
+ -rm -f *.o $(PROGS) core a.out *.n gmon.out path.h suffix.h
+
+distclean: clean
+ -rm -f genlimits TAGS limits.h
+
+realclean: distclean
+ -rm -f label.tab.c
+
+install.bin: $(PROGS)
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/grefer
+ cp refer $(BINDIR)/grefer
+ -rm -f $(BINDIR)/gindxbib
+ cp indxbib $(BINDIR)/gindxbib
+ -rm -f $(BINDIR)/glookbib
+ cp lookbib $(BINDIR)/glookbib
+ -rm -f $(BINDIR)/lkbib
+ cp lkbib $(BINDIR)/lkbib
+
+
+install.nobin:
+ @if [ -f /usr/lib/eign ]; \
+ then echo cp /usr/lib/eign $(COMMON_WORDS_FILE); \
+ rm -f $(COMMON_WORDS_FILE); \
+ cp /usr/lib/eign $(COMMON_WORDS_FILE); \
+ else echo cp eign $(COMMON_WORDS_FILE); \
+ rm -f $(COMMON_WORDS_FILE); \
+ cp eign $(COMMON_WORDS_FILE); fi
+
+install: install.bin install.nobin
+
+FORCE:
diff --git a/refer/TODO b/refer/TODO
new file mode 100644
index 000000000..13eb08821
--- /dev/null
+++ b/refer/TODO
@@ -0,0 +1,108 @@
+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/refer/command.c b/refer/command.c
new file mode 100644
index 000000000..745db7c77
--- /dev/null
+++ b/refer/command.c
@@ -0,0 +1,806 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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()
+{
+ 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 {
+ 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);
+ 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/refer/command.h b/refer/command.h
new file mode 100644
index 000000000..54fab2b34
--- /dev/null
+++ b/refer/command.h
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/refer/common.c b/refer/common.c
new file mode 100644
index 000000000..86a2b6b0e
--- /dev/null
+++ b/refer/common.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/refer/eign b/refer/eign
new file mode 100644
index 000000000..7718c8b11
--- /dev/null
+++ b/refer/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/refer/genlimits.c b/refer/genlimits.c
new file mode 100644
index 000000000..8f6b7bf0d
--- /dev/null
+++ b/refer/genlimits.c
@@ -0,0 +1,44 @@
+/* This tries to generate definitions of PATH_MAX and NAME_MAX. */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif /* HAVE_DIRENT_H */
+
+#ifndef PATH_MAX
+#include <sys/param.h>
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX ((MAXPATHLEN)-1)
+#else /* !MAXPATHLEN */
+#define PATH_MAX 255
+#endif /* !MAXPATHLEN */
+#endif /* !PATH_MAX */
+#endif /* !PATH_MAX */
+
+#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 */
+
+#include <stdio.h>
+
+main()
+{
+ printf("#define NAME_MAX %d\n", NAME_MAX);
+ printf("#define PATH_MAX %d\n", PATH_MAX);
+ exit(0);
+}
diff --git a/refer/index.c b/refer/index.c
new file mode 100644
index 000000000..1647ddf54
--- /dev/null
+++ b/refer/index.c
@@ -0,0 +1,637 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <osfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+extern "C" {
+ int stat(const char *, struct stat *);
+ int fstat(int, struct stat *);
+}
+
+#ifdef HAVE_MMAP
+#ifdef __GNUG__
+#define mmap _____mmap
+#define munmap _____munmap
+#include <sys/mman.h>
+#undef mmap
+#undef munmap
+extern "C" {
+ void *mmap(void *addr, int len, int prot, int flags, int fd, off_t off);
+ int munmap(void *addr, int len);
+}
+#else /* not __GNUG__ */
+// Assume the vendor has provided a proper <sys/mman.h>
+#include <sys/mman.h>
+#endif /* not __GNUG__ */
+#endif /* HAVE_MMAP */
+
+#include "lib.h"
+#include "cset.h"
+#include "cmap.h"
+#include "errarg.h"
+#include "error.h"
+
+#include "refid.h"
+#include "search.h"
+#include "index.h"
+
+const 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;
+#ifdef HAVE_MMAP
+ char *map_addr;
+ int map_size;
+#endif /* HAVE_MMAP */
+ 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), key_buffer(0),
+ filename_buffer(0), filename_buflen(0), common_words_table(0),
+#ifdef HAVE_MMAP
+ map_addr((char *)-1), map_size(0),
+#endif /* HAVE_MMAP */
+ buffer(0)
+{
+}
+
+index_search_item::~index_search_item()
+{
+ if (buffer)
+ free(buffer);
+#ifdef HAVE_MMAP
+ if (map_addr != (char *)-1 && munmap(map_addr, map_size) < 0)
+ error("munmap: %1", strerror(errno));
+#endif /* HAVE_MMAP */
+ while (out_of_date_files) {
+ search_item *tem = out_of_date_files;
+ out_of_date_files = out_of_date_files->next;
+ delete tem;
+ }
+ delete filename_buffer;
+ delete key_buffer;
+ if (common_words_table) {
+ for (int i = 0; i < common_words_table_size; i++)
+ delete common_words_table[i];
+ delete common_words_table;
+ }
+}
+
+class file_closer {
+ int *fdp;
+public:
+ file_closer(int &fd) : fdp(&fd) { }
+ ~file_closer() { close(*fdp); }
+};
+
+int index_search_item::load(int fd)
+{
+ file_closer fd_closer(fd); // close fd on return
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ error("can't fstat `%1': %2", name, strerror(errno));
+ return 0;
+ }
+ if ((sb.st_mode & S_IFMT) != S_IFREG) {
+ error("`%1' is not a regular file", name);
+ return 0;
+ }
+ mtime = sb.st_mtime;
+ int size = int(sb.st_size);
+ char *addr;
+#ifdef HAVE_MMAP
+ map_addr = (char *)mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_addr != (char *)-1) {
+ addr = map_addr;
+ map_size = size;
+ }
+ else
+#endif /* HAVE_MMAP */
+ {
+ 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, 0);
+ if (fd < 0)
+ return 0;
+ index_search_item *item = new index_search_item(index_filename, fid);
+ 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), buf(0), buflen(0), temp_list(0), query(strsave(q)),
+ searcher(q, strlen(q), ind->ignore_fields, ind->header.truncate),
+ out_of_date_files_iter(0), next_out_of_date_file(0)
+{
+ 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()
+{
+ delete temp_list;
+ delete buf;
+ 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, 0);
+ 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, "r");
+ 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 ((sb.st_mode & S_IFMT) != S_IFREG) {
+ error("`%1' is not a regular file", filename);
+ err = 1;
+ }
+ else
+ length = int(sb.st_size);
+ }
+ if (!err) {
+ if (length + 2 > buflen) {
+ 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';
+ 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 (filename[0] == '/')
+ return filename;
+ const char *cwd = pool;
+ int need_slash = (cwd[0] != 0 && strchr(cwd, '\0')[-1] != '/');
+ int len = strlen(cwd) + strlen(filename) + need_slash + 1;
+ if (len > filename_buflen) {
+ 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) {
+ 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;
+ for (const int *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) {
+ delete matches;
+ return list;
+ }
+ merge(matches, matches, list);
+ if (*matches < 0) {
+ 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);
+ 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)
+{
+ for (search_item **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, 0);
+ 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/refer/index.h b/refer/index.h
new file mode 100644
index 000000000..4df7de6be
--- /dev/null
+++ b/refer/index.h
@@ -0,0 +1,44 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define INDEX_MAGIC 0x23021964
+#define INDEX_VERSION 1
+
+#include "suffix.h"
+
+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/refer/indxbib.c b/refer/indxbib.c
new file mode 100644
index 000000000..ca73dd5ac
--- /dev/null
+++ b/refer/indxbib.c
@@ -0,0 +1,699 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <osfcn.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "refer.h"
+
+// limits.h defines NAME_MAX and PATH_MAX
+#include "limits.h"
+#include "index.h"
+
+#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
+
+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();
+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);
+
+extern "C" { void fatal_signal(int); }
+
+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;
+ while ((opt = getopt(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw")) != 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;
+ fprintf(stderr, "GNU indxbib version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (optind >= argc && foption == 0)
+ fatal("no files and no -f option");
+ if (!directory) {
+#ifdef HAVE_GETWD
+ char path[PATH_MAX + 1];
+ if (!getwd(path))
+ fatal("getwd: %1", path);
+#else /* !HAVE_GETWD */
+ char path[PATH_MAX + 2];
+ if (!getcwd(path, PATH_MAX + 2))
+ fatal("getcwd: %1", strerror(errno));
+#endif /* !HAVE_GETWD */
+ store_filename(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, '/');
+ if (strlen(p ? p + 1 : basename) + 2 > NAME_MAX
+ || strlen(basename) + 2 > PATH_MAX)
+ fatal("`%1.i' would not be a valid filename", basename);
+ 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);
+ }
+ mktemp(temp_index_file);
+ signal(SIGHUP, fatal_signal);
+ signal(SIGINT, fatal_signal);
+ signal(SIGTERM, fatal_signal);
+ int fd = creat(temp_index_file, 0444);
+ if (fd < 0)
+ fatal("can't create temporary index file: %1", strerror(errno));
+ indxfp = fdopen(fd, "w");
+ 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) {
+ fp = fopen(foption, "r");
+ if (!fp)
+ fatal("can't open `%1': %2", foption, strerror(errno));
+ }
+ string path;
+ int lineno = 1;
+ for (;;) {
+ for (int 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)
+ fatal("can't rename temporary index file: %1", strerror(errno));
+#else /* not HAVE_RENAME */
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ 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;
+ exit(failed);
+}
+
+static void usage()
+{
+ fprintf(stderr,
+"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);
+ exit(1);
+}
+
+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);
+ }
+}
+
+
+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;
+ 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)
+{
+ 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)
+{
+ FILE *fp = fopen(filename, "r");
+ 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;
+ 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 {
+ assert(0);
+ // write it out word by word
+ }
+ 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()
+{
+ if (temp_index_file)
+ unlink(temp_index_file);
+ exit(3);
+}
+
+extern "C" {
+
+void fatal_signal(int signum)
+{
+ signal(signum, SIG_DFL);
+ if (temp_index_file)
+ unlink(temp_index_file);
+ kill(getpid(), signum);
+}
+
+}
diff --git a/refer/label.tab.c b/refer/label.tab.c
new file mode 100644
index 000000000..9ed67aea2
--- /dev/null
+++ b/refer/label.tab.c
@@ -0,0 +1,1935 @@
+
+/* A Bison parser, made from label.y */
+
+#define TOKEN_LETTER 258
+#define TOKEN_LITERAL 259
+#define TOKEN_DIGIT 260
+
+#line 21 "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) : name(nm), number(num) { }
+ 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_t(const char *, const char *, string &);
+
+class map_expr : public unary_expr {
+ map_t *func;
+public:
+ map_expr(expression *e, map_t *f) : unary_expr(e), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+typedef const char *extractor_t(const char *, const char *, const char **);
+
+class extractor_expr : public unary_expr {
+ int part;
+ extractor_t *func;
+public:
+ enum { BEFORE = +1, MATCH = 0, AFTER = -1 };
+ extractor_expr(expression *e, extractor_t *f, int pt)
+ : unary_expr(e), func(f), part(pt) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class truncate_expr : public unary_expr {
+ int n;
+public:
+ truncate_expr(expression *e, int i) : n(i), unary_expr(e) { }
+ 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;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __STDC__
+#define const
+#endif
+
+
+
+#define YYFINAL 49
+#define YYFLAG -32768
+#define YYNTBASE 21
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 260 ? yytranslate[x] : 32)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 12, 9, 2, 17,
+ 18, 16, 14, 2, 15, 13, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 7, 2, 19,
+ 2, 20, 6, 11, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 8, 2, 10, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5
+};
+
+static const short yyprhs[] = { 0,
+ 0, 2, 4, 10, 11, 13, 15, 19, 23, 25,
+ 28, 30, 34, 36, 38, 40, 43, 46, 49, 55,
+ 59, 63, 66, 70, 74, 75, 77, 79, 82, 84,
+ 87, 88, 90
+};
+
+static const short yyrhs[] = { 23,
+ 0, 24, 0, 24, 6, 23, 7, 22, 0, 0,
+ 22, 0, 25, 0, 24, 8, 25, 0, 24, 9,
+ 25, 0, 26, 0, 25, 26, 0, 27, 0, 26,
+ 10, 27, 0, 11, 0, 4, 0, 3, 0, 3,
+ 29, 0, 12, 3, 0, 12, 30, 0, 27, 13,
+ 31, 3, 28, 0, 27, 14, 29, 0, 27, 15,
+ 29, 0, 27, 16, 0, 17, 23, 18, 0, 19,
+ 23, 20, 0, 0, 29, 0, 5, 0, 29, 5,
+ 0, 5, 0, 30, 5, 0, 0, 14, 0, 15,
+ 0
+};
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 248, 253, 256, 260, 263, 267, 270, 272, 276, 279,
+ 283, 286, 290, 293, 298, 300, 302, 318, 322, 353,
+ 355, 357, 359, 361, 365, 368, 372, 375, 379, 382,
+ 387, 390, 392
+};
+
+static const char * const yytname[] = { "$",
+"error","$illegal.","TOKEN_LETTER","TOKEN_LITERAL","TOKEN_DIGIT","'?'","':'","'|'","'&'","'~'",
+"'@'","'%'","'.'","'+'","'-'","'*'","'('","')'","'<'","'>'",
+"expr","conditional","optional_conditional","alternative","list","substitute","string","optional_number","number","digits",
+"flag",""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 21, 22, 22, 23, 23, 24, 24, 24, 25, 25,
+ 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 28, 28, 29, 29, 30, 30,
+ 31, 31, 31
+};
+
+static const short yyr2[] = { 0,
+ 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
+};
+
+static const short yydefact[] = { 4,
+ 15, 14, 13, 0, 4, 4, 5, 1, 2, 6,
+ 9, 11, 27, 16, 17, 29, 18, 0, 0, 4,
+ 0, 0, 10, 0, 31, 0, 0, 22, 28, 30,
+ 23, 24, 0, 7, 8, 12, 32, 33, 0, 20,
+ 21, 0, 25, 3, 19, 26, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 47,
+ 7, 8, 9, 10, 11, 12, 45, 14, 17, 39
+};
+
+static const short yypact[] = { 0,
+ 4,-32768,-32768, 5, 0, 0,-32768,-32768, 7, 0,
+ -5, 13,-32768, 15,-32768,-32768, 27, -4, 14, 0,
+ 0, 0, -5, 0, 8, 4, 4,-32768,-32768,-32768,
+-32768,-32768, 26, 0, 0, 13,-32768,-32768, 32, 15,
+ 15, 0, 4,-32768,-32768, 15, 36, 37,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ -3, 1,-32768, 9, -10, 16,-32768, -25,-32768,-32768
+};
+
+
+#define YYLAST 40
+
+
+static const short yytable[] = { 23,
+ 40, 41, 1, 2, 24, 18, 19, 15, 13, 16,
+ 3, 4, 20, 31, 21, 22, 5, 46, 6, 29,
+ 33, 37, 38, 23, 23, 25, 26, 27, 28, 34,
+ 35, 30, 42, 32, 43, 48, 49, 0, 44, 36
+};
+
+static const short yycheck[] = { 10,
+ 26, 27, 3, 4, 10, 5, 6, 3, 5, 5,
+ 11, 12, 6, 18, 8, 9, 17, 43, 19, 5,
+ 20, 14, 15, 34, 35, 13, 14, 15, 16, 21,
+ 22, 5, 7, 20, 3, 0, 0, -1, 42, 24
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program 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 1, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* Not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
+#include <alloca.h>
+#else /* Not sparc */
+#ifdef MSDOS
+#include <malloc.h>
+#endif /* MSDOS */
+#endif /* Not sparc. */
+#endif /* Not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#define YYLEX yylex(&yylval, &yylloc)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+
+#line 160 "/usr/local/lib/bison.simple"
+int
+yyparse()
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+
+#define YYPOPSTACK (yyvsp--, yysp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yysp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+#ifdef YYLSP_NEEDED
+ &yyls1, size * sizeof (*yylsp),
+#endif
+ &yystacksize);
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Next token is %d (%s)\n", yychar, yytname[yychar1]);
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symboles being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ 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[-4].expr));
+ 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;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 423 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) xmalloc(size + 15);
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 396 "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 {
+ char buf[INT_DIGITS + 1];
+ char *ptr = buf;
+ if (width > INT_DIGITS)
+ ptr = new char[width + 1];
+ sprintf(ptr, "%0*d", width, num + first_number);
+ result += ptr;
+ if (ptr != buf)
+ delete 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 itoa(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;
+ for (int 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)
+: count(0), total(1), length(s.length()), start(label_pool.length())
+{
+ 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;
+ for (label_info **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);
+ for (label_info **p = label_table + (h % label_table_size);
+ *p != 0;
+ (p == label_table)
+ ? (p = label_table + label_table_size - 1)
+ : --p)
+ ;
+ *p = old_table[i];
+ }
+ 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 (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;
+ for (int na = 0; get_author(na, &dummy) != 0; na++)
+ ;
+ ((reference *)this)->nauthors = na;
+ }
+ return nauthors;
+}
diff --git a/refer/label.y b/refer/label.y
new file mode 100644
index 000000000..95c24a1b9
--- /dev/null
+++ b/refer/label.y
@@ -0,0 +1,1174 @@
+/* -*- C++ -*-
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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) : name(nm), number(num) { }
+ 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_t(const char *, const char *, string &);
+
+class map_expr : public unary_expr {
+ map_t *func;
+public:
+ map_expr(expression *e, map_t *f) : unary_expr(e), func(f) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+typedef const char *extractor_t(const char *, const char *, const char **);
+
+class extractor_expr : public unary_expr {
+ int part;
+ extractor_t *func;
+public:
+ enum { BEFORE = +1, MATCH = 0, AFTER = -1 };
+ extractor_expr(expression *e, extractor_t *f, int pt)
+ : unary_expr(e), func(f), part(pt) { }
+ void evaluate(int, const reference &, string &, substring_position &);
+};
+
+class truncate_expr : public unary_expr {
+ int n;
+public:
+ truncate_expr(expression *e, int i) : n(i), unary_expr(e) { }
+ 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($1));
+ 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 {
+ char buf[INT_DIGITS + 1];
+ char *ptr = buf;
+ if (width > INT_DIGITS)
+ ptr = new char[width + 1];
+ sprintf(ptr, "%0*d", width, num + first_number);
+ result += ptr;
+ if (ptr != buf)
+ delete 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 itoa(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;
+ for (int 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)
+: count(0), total(1), length(s.length()), start(label_pool.length())
+{
+ 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;
+ for (label_info **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);
+ for (label_info **p = label_table + (h % label_table_size);
+ *p != 0;
+ (p == label_table)
+ ? (p = label_table + label_table_size - 1)
+ : --p)
+ ;
+ *p = old_table[i];
+ }
+ 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 (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;
+ for (int na = 0; get_author(na, &dummy) != 0; na++)
+ ;
+ ((reference *)this)->nauthors = na;
+ }
+ return nauthors;
+}
diff --git a/refer/linear.c b/refer/linear.c
new file mode 100644
index 000000000..1b2aa921e
--- /dev/null
+++ b/refer/linear.c
@@ -0,0 +1,493 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <osfcn.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+extern "C" {
+ int stat(const char *, struct stat *);
+ int fstat(int, struct stat *);
+}
+
+#include "lib.h"
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "cmap.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()
+{
+ for (int 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)] != pattern[--j])
+ break;
+ }
+ k++;
+ }
+ return 0;
+}
+
+bmpattern::~bmpattern()
+{
+ 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;
+ for (const char *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;
+ for (const char *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;
+ }
+ for (const char *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()
+{
+ 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 ((sb.st_mode & S_IFMT) != S_IFREG)
+ 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 addional 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 {
+ close(fd);
+ buffer[3] = '\n';
+ bufend = buffer + 4 + size;
+ if (bufend[-1] != '\n')
+ *bufend++ = '\n';
+ return 1;
+ }
+ }
+ delete buffer;
+ buffer = 0;
+ }
+ close(fd);
+ return 0;
+}
+
+linear_searcher::linear_searcher(const char *query, int query_len,
+ const char *ign, int trunc)
+: keys(0), nkeys(0), truncate_len(trunc), ignore_fields(ign)
+{
+ 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) {
+ delete keys;
+ keys = 0;
+ }
+}
+
+linear_searcher::~linear_searcher()
+{
+ for (int i = 0; i < nkeys; i++)
+ delete keys[i];
+ 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());
+ for (int 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;
+}
+
+int linear_searcher::get_nkeys() const
+{
+ return nkeys;
+}
+
+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/refer/lkbib.c b/refer/lkbib.c
new file mode 100644
index 000000000..432674bd2
--- /dev/null
+++ b/refer/lkbib.c
@@ -0,0 +1,114 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "refer.h"
+
+#include "refid.h"
+#include "search.h"
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-nv] [-p database] [-i XYZ] [-t N] keys ...\n",
+ program_name);
+ exit(1);
+}
+
+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;
+ while ((opt = getopt(argc, argv, "nvVi:t:p:")) != 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;
+ fprintf(stderr, "GNU lkbib version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'p':
+ list.add_file(optarg);
+ break;
+ case '?':
+ usage();
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ usage();
+ 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;
+ for (int 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);
+ char *start;
+ int len;
+ for (int count = 0; iter.next(&start, &len); count++) {
+ if (count > 0)
+ putchar('\n');
+ 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');
+ }
+ exit(!count);
+}
diff --git a/refer/lookbib.c b/refer/lookbib.c
new file mode 100644
index 000000000..47a28e5a4
--- /dev/null
+++ b/refer/lookbib.c
@@ -0,0 +1,126 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+#include "cset.h"
+
+#include "refid.h"
+#include "search.h"
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-v] [-i XYZ] [-t N] database ...\n",
+ program_name);
+ exit(1);
+}
+
+main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ while ((opt = getopt(argc, argv, "vVi:t:")) != 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;
+ fprintf(stderr, "GNU lookbib version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case '?':
+ usage();
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ usage();
+ 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 first = 1;
+ 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);
+ char *start;
+ int len;
+ for (int count = 0; iter.next(&start, &len); count++) {
+ if (first)
+ first = 0;
+ else
+ putchar('\n');
+ 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');
+ }
+ fflush(stdout);
+ if (interactive) {
+ fprintf(stderr, "%d found\n", count);
+ fflush(stderr);
+ }
+ }
+ if (interactive)
+ putc('\n', stderr);
+ return 0;
+}
+
diff --git a/refer/ref.c b/refer/ref.c
new file mode 100644
index 000000000..5d96bfde8
--- /dev/null
+++ b/refer/ref.c
@@ -0,0 +1,1141 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+: no(-1), field(0), nfields(0), h(0), merged(0), label_ptr(0),
+ computed_authors(0), last_needed_author(-1), nauthors(-1)
+{
+ for (int 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)
+ 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) {
+ 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;
+ for (int i = 0; i < 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]);
+ 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];
+ for (int i = 0; i < 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]);
+ 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)) {
+ for (int 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 (;;) {
+ for (const char *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;
+ for (int 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;
+ if (f.length() > 0 && memchr(f.contents(), '-', f.length()) != 0)
+ multiple_pages = 1;
+ 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/refer/ref.h b/refer/ref.h
new file mode 100644
index 000000000..8b52c0ec0
--- /dev/null
+++ b/refer/ref.h
@@ -0,0 +1,120 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/refer/refer.c b/refer/refer.c
new file mode 100644
index 000000000..c59573b7a
--- /dev/null
+++ b/refer/refer.c
@@ -0,0 +1,1228 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <osfcn.h>
+
+#include "refer.h"
+#include "refid.h"
+#include "ref.h"
+#include "token.h"
+#include "search.h"
+#include "command.h"
+
+#define TEMP_TEMPLATE "/tmp/greferXXXXXX"
+
+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();
+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();
+ }
+ }
+ else {
+ num = opt;
+ opt = 0;
+ }
+ for (const char *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':
+ {
+ for (const char *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'), "+%d", 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'), "-%d", 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();
+ }
+ }
+ 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 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU refer version %s\n", version_string);
+ fflush(stderr);
+ opt++;
+ break;
+ }
+ case '-':
+ if (opt[1] == '\0') {
+ finished_options = 1;
+ opt++;
+ break;
+ }
+ // fall through
+ default:
+ error("unrecognized option `%1'", *opt);
+ usage();
+ 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");
+ exit(0);
+}
+
+static void usage()
+{
+ fprintf(stderr,
+"usage: %s [-benvCPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N] [-p file]\n"
+" [-sXYZ] [-tN] [-BL.M] [files ...]\n",
+ program_name);
+ exit(1);
+}
+
+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 {
+ 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;
+ pending_line += LABEL_MARKER + lt;
+ 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;
+ }
+ 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()
+{
+ char temp_filename[sizeof(TEMP_TEMPLATE)];
+ strcpy(temp_filename, TEMP_TEMPLATE);
+ mktemp(temp_filename);
+ outfp = fopen(temp_filename, "w+");
+ if (outfp == 0)
+ fatal("can't open temporary file `%1': %2",
+ temp_filename, strerror(errno));
+ if (unlink(temp_filename) < 0)
+ error("can't unlink `%1': %2", temp_filename, strerror(errno));
+}
+
+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 *));
+ 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();
+ for (reference **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]) {
+ for (reference **p = reference_hash_table + (h % hash_table_size);
+ *p;
+ ((p == reference_hash_table)
+ ? (p = reference_hash_table + hash_table_size - 1)
+ : --p))
+ ;
+ *p = old_table[i];
+ }
+ 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];
+ for (int 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" {
+
+static int rcompare(void *p1, 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 {
+ 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
+ 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
+ };
+
+ for (const int *p = table_sizes; *p <= n && *p != 0; p++)
+ ;
+ assert(*p != 0);
+ return *p;
+}
+
diff --git a/refer/refer.h b/refer/refer.h
new file mode 100644
index 000000000..e1bb5224b
--- /dev/null
+++ b/refer/refer.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "lib.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "cmap.h"
+
+#include "path.h"
+
+#define DEFAULT_INDEX DEFAULT_INDEX_DIR "/" DEFAULT_INDEX_NAME
+
+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/refer/refid.h b/refer/refid.h
new file mode 100644
index 000000000..d4cf97f66
--- /dev/null
+++ b/refer/refid.h
@@ -0,0 +1,35 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 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/refer/search.c b/refer/search.c
new file mode 100644
index 000000000..fd87d10c8
--- /dev/null
+++ b/refer/search.c
@@ -0,0 +1,129 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <osfcn.h>
+#include <string.h>
+#include <assert.h>
+
+#include "lib.h"
+#include "errarg.h"
+#include "error.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, 0);
+ 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) {
+ for (search_item **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;
+ 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)
+: next(0), name(strsave(nm)), filename_id(fid)
+{
+}
+
+search_item::~search_item()
+{
+ 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/refer/search.h b/refer/search.h
new file mode 100644
index 000000000..41a6db3cd
--- /dev/null
+++ b/refer/search.h
@@ -0,0 +1,97 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+ int get_nkeys() 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/refer/token.c b/refer/token.c
new file mode 100644
index 000000000..03e4697ae
--- /dev/null
+++ b/refer/token.c
@@ -0,0 +1,363 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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(OTHER), sort_key(0), other_case(0)
+{
+}
+
+void token_info::set(token_type t, const char *sk, const char *oc)
+{
+ assert(oc == 0 || t == UPPER || t == 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 == UPPER || type == 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 != UPPER && type != 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 != 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 != 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;
+ while (n >= 0) {
+ 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;
+ }
+ 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;
+ while (n >= 0) {
+ 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;
+ }
+ return &default_token_info;
+}
+
+static void init_ascii()
+{
+ for (const char *p = "abcdefghijklmnopqrstuvwxyz"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ store_token(strsave(buf), token_info::LOWER);
+ buf[0] = cmupper(buf[0]);
+ store_token(strsave(buf), token_info::UPPER);
+ }
+ for (p = "0123456789"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ const char *s = strsave(buf);
+ store_token(s, token_info::OTHER, s);
+ }
+ for (p = ".,:;?!"; *p; p++) {
+ char buf[2];
+ buf[0] = *p;
+ buf[1] = '\0';
+ store_token(strsave(buf), token_info::PUNCT);
+ }
+ store_token("-", token_info::HYPHEN);
+}
+
+static void store_letter(const char *lower, const char *upper,
+ const char *sort_key = 0)
+{
+ store_token(lower, token_info::LOWER, sort_key, upper);
+ store_token(upper, token_info::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_info::LOWER, "ss", "SS");
+ store_token("\377", token_info::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()
+{
+ for (const char *p = "':^`~"; *p; p++)
+ for (const char *q = "aeiouy"; *q; q++)
+ init_two_char_letter(*p, *q, *p, cmupper(*q));
+ for (p = "/l/o~n,coeaeij"; *p; p += 2)
+ init_two_char_letter(p[0], p[1], cmupper(p[0]), cmupper(p[1]));
+ 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_info::LOWER, 0, "SS");
+ store_token("\\[ss]", token_info::LOWER, 0, "SS");
+
+ store_token("\\(Sd", token_info::LOWER, "d", "\\(-D");
+ store_token("\\[Sd]", token_info::LOWER, "d", "\\[-D]");
+ store_token("\\(hy", token_info::HYPHEN);
+ store_token("\\[hy]", token_info::HYPHEN);
+}
+
+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_info::ACCENT);
+ buf[2] = '[';
+ buf[3] = *p;
+ buf[4] = ']';
+ buf[5] = '\0';
+ store_token(strsave(buf), token_info::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_info::LOWER, "y", "Y");
+ store_token("\\*8", token_info::LOWER, "ss", "SS");
+ store_token("\\*q", token_info::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_info::OTHER);
+}
diff --git a/refer/token.h b/refer/token.h
new file mode 100644
index 000000000..15311ab1a
--- /dev/null
+++ b/refer/token.h
@@ -0,0 +1,74 @@
+// -*- C++ -*-
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+class token_info {
+public:
+ enum token_type { OTHER, UPPER, LOWER, ACCENT, PUNCT, HYPHEN };
+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;
+};
+
+inline int token_info::is_upper() const
+{
+ return type == UPPER;
+}
+
+inline int token_info::is_lower() const
+{
+ return type == LOWER;
+}
+
+inline int token_info::is_accent() const
+{
+ return type == ACCENT;
+}
+
+inline int token_info::is_other() const
+{
+ return type == OTHER;
+}
+
+inline int token_info::is_punct() const
+{
+ return type == PUNCT;
+}
+
+inline int token_info::is_hyphen() const
+{
+ return type == HYPHEN;
+}
+
+int get_token(const char **ptr, const char *end);
+const token_info *lookup_token(const char *start, const char *end);
diff --git a/stringify b/stringify
new file mode 100644
index 000000000..7dec124d5
--- /dev/null
+++ b/stringify
@@ -0,0 +1,5 @@
+# `stringify foo bar' should output `"foo","bar",' (without any newlines).
+for arg
+do
+ echo \"$arg\",
+done | tr -d "\012"
diff --git a/tbl/Makefile b/tbl/Makefile
new file mode 100644
index 000000000..468dea481
--- /dev/null
+++ b/tbl/Makefile
@@ -0,0 +1,64 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+CC=g++
+CFLAGS=-g
+INCLUDES=-I../lib
+DEFINES=
+SOURCES=main.c table.c table.h
+MISC=Makefile TODO
+ETAGS=etags
+ETAGSFLAGS=-p
+BINDIR=/usr/local/bin
+OBJECTS=main.o table.o
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: tbl
+
+tbl: $(OBJECTS) ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(LIBS)
+
+TABLE_H=table.h ../lib/cset.h ../lib/cmap.h ../lib/stringclass.h \
+ ../lib/errarg.h ../lib/error.h ../lib/lib.h
+
+main.o: $(TABLE_H)
+table.o: $(TABLE_H)
+
+TAGS : $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+ -rm -f *.o core tbl
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+install.bin: tbl
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/gtbl
+ cp tbl $(BINDIR)/gtbl
+
+install.nobin:
+
+install: install.bin install.nobin
diff --git a/tbl/TODO b/tbl/TODO
new file mode 100644
index 000000000..4a8573540
--- /dev/null
+++ b/tbl/TODO
@@ -0,0 +1,16 @@
+Improve implementation of staggered columns.
+
+Perhaps move interpretation of .lf commands with tables into
+the table_input class.
+
+Think about multi-page tables without .TS H/.TH. At the beginning of a
+section we could transparently output a need request that references a
+number register that we could set at the end of the section. This
+won't work for boxed tables, because there's no way to draw the top
+rule, and it won't work for tables with vertical rules unless the
+macro package calls .T# even for tables that don't start with .TS H.
+
+Support fractional point-sizes: both `linesize' option and `p'
+modifier.
+
+Make it possible to split a table section that's longer than a page.
diff --git a/tbl/main.c b/tbl/main.c
new file mode 100644
index 000000000..b087f49b1
--- /dev/null
+++ b/tbl/main.c
@@ -0,0 +1,1451 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "table.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) {
+ 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) {
+ putchar('\n');
+ return;
+ }
+ putchar(c);
+ }
+ putchar('\n');
+ 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;
+
+ options();
+};
+
+options::options()
+: flags(0), tab_char('\t'), linesize(0)
+{
+ 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 (csspace(*p) || *p == ',')
+ p++;
+ if (*p == '\0')
+ break;
+ char *q = p;
+ while (*q != ' ' && *q != '\0' && *q != '\t' && *q != ',' && *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 {
+ 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(LEFT)
+{
+}
+
+entry_format::entry_format(format_type t) : type(t)
+{
+}
+
+void entry_format::debug_print() const
+{
+ switch (type) {
+ case LEFT:
+ putc('l', stderr);
+ break;
+ case CENTER:
+ putc('c', stderr);
+ break;
+ case RIGHT:
+ putc('r', stderr);
+ break;
+ case NUMERIC:
+ putc('n', stderr);
+ break;
+ case ALPHABETIC:
+ putc('a', stderr);
+ break;
+ case SPAN:
+ putc('s', stderr);
+ break;
+ case VSPAN:
+ putc('^', stderr);
+ break;
+ case HLINE:
+ putc('_', stderr);
+ break;
+ case 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 = new int[ncolumns - 1];
+ 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];
+ 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];
+ delete old_entry;
+ for (i = 0; i < n; i++)
+ entry[nrows + i] = new entry_format[ncolumns];
+ nrows += n;
+}
+
+format::~format()
+{
+ delete separation;
+ delete [ncolumns] width;
+ delete equal;
+ for (int i = 0; i < nrows; i++) {
+ delete vline[i];
+ delete [ncolumns] entry[i];
+ }
+ delete vline;
+ delete entry;
+}
+
+struct input_entry_format : 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()
+{
+ for (int 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 (;;) {
+ while (c == ' ' || c == '\t' || c == opt->tab_char || c == '\n')
+ c = in.get();
+ int pre_vline = 0;
+ if (c == '|') {
+ pre_vline = 1;
+ c = in.get();
+ while (c == ' ' || c == '\t' || c == opt->tab_char)
+ c = in.get();
+ if (c == '|') {
+ pre_vline = 2;
+ c = in.get();
+ while (c == ' ' || c == '\t' || c == opt->tab_char)
+ c = in.get();
+ }
+ }
+ if (c == '.')
+ break;
+ if (c == EOF) {
+ error("end of input while processing format");
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ format_type t;
+ switch (c) {
+ case 'n':
+ case 'N':
+ t = entry_format::NUMERIC;
+ break;
+ case 'a':
+ case 'A':
+ t = entry_format::ALPHABETIC;
+ break;
+ case 'c':
+ case 'C':
+ t = entry_format::CENTER;
+ break;
+ case 'l':
+ case 'L':
+ t = entry_format::LEFT;
+ break;
+ case 'r':
+ case 'R':
+ t = entry_format::RIGHT;
+ break;
+ case 's':
+ case 'S':
+ t = entry_format::SPAN;
+ break;
+ case '^':
+ t = entry_format::VSPAN;
+ break;
+ case '_':
+ t = entry_format::HLINE;
+ break;
+ case '=':
+ t = entry_format::DOUBLE_HLINE;
+ break;
+ default:
+ error("unrecognised format `%1'", char(c));
+ free_input_entry_format_list(list);
+ return 0;
+ }
+ c = in.get();
+ list = new input_entry_format(t, list);
+ if (pre_vline)
+ list->pre_vline = pre_vline;
+ int success = 1;
+ do {
+ while (c == ' ' || c == '\t' || c == opt->tab_char)
+ c = in.get();
+ 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;
+ default:
+ 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 (!current_format) {
+ if (col < ncolumns-1) {
+ // use the greatest separation
+ if (tem->separation > f->separation[col])
+ f->separation[col] = tem->separation;
+ }
+ else if (tem->separation >= 0)
+ error("column separation specified for last column");
+ 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->equal)
+ f->equal[col] = 1;
+ }
+ 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 != entry_format::HLINE
+ && e->type != entry_format::DOUBLE_HLINE
+ && e->type != entry_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);
+ for (int 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);
+ 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) {
+ for (int c = 0; c < ncolumns; c++) {
+ entry_format *e = f->entry[format_index] + c;
+ if (e->type != entry_format::HLINE
+ && e->type != entry_format::DOUBLE_HLINE
+ // Unfortunately tbl treats a span as needing data.
+ // && e->type != entry_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;
+ for (;;) {
+ if (c == tab_char || c == '\n') {
+ int ln = current_lineno;
+ if (c == '\n')
+ --ln;
+ while (col < ncolumns
+ && line_format[col].type == entry_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 (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;
+ }
+ 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()
+{
+ fprintf(stderr, "usage: %s [ -vC ] [ files... ]\n", program_name);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ while ((opt = getopt(argc, argv, "vC")) != EOF)
+ switch (opt) {
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU tbl version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case '?':
+ usage();
+ 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;
+ if (i != 1)
+ printf(".lf 1 -\n");
+ process_input_file(stdin);
+ }
+ else {
+ 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;
+ process_input_file(stdin);
+ }
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+ exit(0);
+}
+
diff --git a/tbl/table.c b/tbl/table.c
new file mode 100644
index 000000000..f4147fb44
--- /dev/null
+++ b/tbl/table.c
@@ -0,0 +1,2733 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "table.h"
+
+#define BAR_HEIGHT ".25m"
+#define DOUBLE_LINE_SEP "2p"
+#define HALF_DOUBLE_LINE_SEP "1p"
+#define BODY_DEPTH ".25m"
+#define BODY_HEIGHT ".75m"
+
+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"
+#define TEXT_STRING_PREFIX PREFIX "s"
+#define RIGHT_TEXT_STRING_PREFIX PREFIX "ss"
+
+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);
+string text_string_name(int r, int c);
+string right_text_string_name(int r, int c);
+
+void set_inline_modifier(const entry_modifier *);
+void restore_inline_modifier(const entry_modifier *m);
+void set_modifier(const entry_modifier *);
+int find_dot(const char *s, 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 char *);
+void prints(const string &);
+void prints(char);
+
+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 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:
+ short start_row;
+ short start_col;
+ short end_row;
+ 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);
+ 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();
+ virtual void note_double_vrule_on_left();
+};
+
+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;
+public:
+ text_entry(char *, const entry_modifier *);
+ ~text_entry();
+};
+
+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);
+};
+
+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();
+ void note_double_vrule_on_left();
+ 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);
+public:
+ block_entry(char *s, const entry_modifier *m);
+ ~block_entry();
+ int divert(int ncols, const string *mw);
+ 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);
+};
+
+table_entry::table_entry(const entry_modifier *m)
+: next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m),
+ input_lineno(-1), input_filename(0)
+{
+}
+
+table_entry::~table_entry()
+{
+}
+
+int table_entry::divert(int, const string *)
+{
+ 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()
+{
+}
+
+void table_entry::note_double_vrule_on_left()
+{
+}
+
+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:
+ printfs(".sp |\\n[%1]u+(\\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)
+: contents(s), simple_entry(m)
+{
+}
+
+text_entry::~text_entry()
+{
+ 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()
+{
+ printfs(".ds %1 \"", text_string_name(start_row, start_col));
+ set_inline_modifier(mod);
+ prints(contents);
+ restore_inline_modifier(mod);
+ prints('\n');
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
+ span_width_reg(start_col, end_col),
+ text_string_name(start_row, start_col));
+}
+
+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));
+ printfs("\\*[%1]", text_string_name(start_row, start_col));
+}
+
+// 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));
+ printfs("\002\003\\*[%1]\002", text_string_name(start_row, start_col));
+}
+
+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));
+ printfs("\002\003\\*[%1]\003\002", text_string_name(start_row, start_col));
+}
+
+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) {
+ printfs(".ds %1 \"", text_string_name(start_row, start_col));
+ set_inline_modifier(mod);
+ for (int i = 0; i < dot_pos; i++)
+ prints(contents[i]);
+ restore_inline_modifier(mod);
+ prints('\n');
+ set_location();
+ printfs(".nr %1 0\\w'\\*[%2]'\n",
+ block_width_reg(start_row, start_col),
+ text_string_name(start_row, start_col));
+ 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') {
+ printfs(".ds %1 \"", right_text_string_name(start_row, start_col));
+ set_inline_modifier(mod);
+ prints(contents + dot_pos);
+ restore_inline_modifier(mod);
+ prints('\n');
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
+ span_right_numeric_width_reg(start_col, end_col),
+ right_text_string_name(start_row, start_col));
+ }
+}
+
+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));
+ if (dot_pos != 0) {
+ printfs("\\*[%1]", text_string_name(start_row, start_col));
+ }
+ if (contents[dot_pos] != '\0')
+ printfs("\\*[%1]", right_text_string_name(start_row, start_col));
+}
+
+alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
+: text_entry(s, m)
+{
+}
+
+void alphabetic_text_entry::do_width()
+{
+ printfs(".ds %1 \"", text_string_name(start_row, start_col));
+ set_inline_modifier(mod);
+ prints(contents);
+ restore_inline_modifier(mod);
+ prints('\n');
+ set_location();
+ printfs(".nr %1 \\n[%1]>?\\w'\\*[%2]'\n",
+ span_alphabetic_width_reg(start_col, end_col),
+ text_string_name(start_row, start_col));
+}
+
+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));
+ printfs("\\*[%1]", text_string_name(start_row, start_col));
+}
+
+block_entry::block_entry(char *s, const entry_modifier *m)
+: table_entry(m), contents(s)
+{
+}
+
+block_entry::~block_entry()
+{
+ 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:
+ printfs(".sp |\\n[%1]u+(\\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)
+{
+ do_divert(0, ncols, mw);
+ return 1;
+}
+
+void block_entry::do_divert(int alphabetic, int ncols, const string *mw)
+{
+ printfs(".di %1\n", block_diversion_name(start_row, start_col));
+ prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
+ ".in 0\n");
+ if (start_col == end_col && !mw[start_col].empty())
+ printfs(".ll (n;%1)>?\\n[%2]u",
+ mw[start_col],
+ span_width_reg(start_col, start_col));
+ else
+ printfs(".ll (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)
+{
+ do_divert(1, ncols, mw);
+ 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_row, 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()
+{
+ double_vrule_on_right = 1;
+}
+
+void line_entry::note_double_vrule_on_left()
+{
+ double_vrule_on_left = 1;
+}
+
+
+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("+" 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("-" 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("+" 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_left)
+ 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) : row(r), next(0), printed(0)
+{
+}
+
+stuff::~stuff()
+{
+}
+
+struct text_stuff : 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)
+: contents(s), stuff(r), 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 : 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;
+ short start_row;
+ short 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)
+: start_row(sr), end_row(er), col(c), is_double(dbl), next(p)
+{
+}
+
+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)
+: ncolumns(nc), flags(f), linesize(ls),
+ nrows(0), allocated_rows(0), entry(0), entry_list(0),
+ left_separation(0), right_separation(0), stuff_list(0), vline(0),
+ vrule_list(0), row_is_all_lines(0)
+{
+ minimum_width = new string[ncolumns];
+ column_separation = new int[ncolumns - 1];
+ 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++) {
+ delete entry[i];
+ delete vline[i];
+ }
+ delete entry;
+ delete vline;
+ while (entry_list) {
+ table_entry *tem = entry_list;
+ entry_list = entry_list->next;
+ delete tem;
+ }
+ delete [ncolumns] minimum_width;
+ delete column_separation;
+ 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;
+ }
+ delete row_is_all_lines;
+}
+
+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)
+{
+ for (stuff **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);
+ char **old_vline = vline;
+ vline = new char*[allocated_rows];
+ memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
+ }
+ }
+ assert(allocated_rows > r);
+ while (nrows <= r) {
+ entry[nrows] = new table_entry*[ncolumns];
+ for (int 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_dot(const char *s, 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] == '.' && 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 entry_format::SPAN:
+ assert(str.empty());
+ do_hspan(r, c);
+ break;
+ case entry_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 entry_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 entry_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 entry_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_dot(s, 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 entry_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 entry_format::VSPAN:
+ do_vspan(r, c);
+ break;
+ case entry_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 entry_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;
+ for (table_entry **p = &entry_list; *p; p = &(*p)->next)
+ ;
+ *p = e;
+ 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;
+ }
+}
+
+struct horizontal_span {
+ horizontal_span *next;
+ short start_col;
+ short end_col;
+ horizontal_span(int, int, horizontal_span *);
+};
+
+horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
+: start_col(sc), end_col(ec), next(p)
+{
+}
+
+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"
+ ".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 " 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\n"
+ ".el \\{"
+ ".in 0\n"
+ ".ls 1\n"
+ ".nf\n"
+ "." TABLE_DIVERSION_NAME "\n"
+ ".\\}\n"
+ ".rm " TABLE_DIVERSION_NAME "\n"
+ ".\\}\n"
+ "..\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"
+ ".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 text_string_name(int r, int c)
+{
+ static char name[sizeof(TEXT_STRING_PREFIX) + INT_DIGITS + 1 + INT_DIGITS];
+ sprintf(name, TEXT_STRING_PREFIX "%d,%d", r, c);
+ return string(name);
+}
+
+string right_text_string_name(int r, int c)
+{
+ static char name[sizeof(RIGHT_TEXT_STRING_PREFIX)+INT_DIGITS+ 1+INT_DIGITS];
+ sprintf(name, RIGHT_TEXT_STRING_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 columsn 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));
+ for (int i = start_col + 1; i <= end_col; i++)
+ printfs("+%1n+\\n[%2]",
+ as_string(column_separation[i - 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');
+}
+
+void table::build_span_list()
+{
+ span_list = 0;
+ table_entry *p = entry_list;
+ while (p) {
+ if (p->end_col != p->start_col) {
+ for (horizontal_span *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) {
+ for (horizontal_span **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;
+ for (int i = 0; i < ncolumns - 1; i++)
+ total_sep += column_separation[i];
+ if (total_sep != 0) {
+ // don't let the separation factor be less than 1n
+ 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>?1n\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));
+ for (int 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
+ for (int 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)) {
+ 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 \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "\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++;
+ for (int 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 \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" DOUBLE_LINE_SEP "\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'"
+ "\n");
+ else {
+ int start_col = 0;
+ for (;;) {
+ while (start_col < ncolumns
+ && entry[r][start_col] != 0
+ && entry[r][start_col]->start_row != r)
+ start_col++;
+ for (int 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 = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" DOUBLE_LINE_SEP;
+ else
+ result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH;
+ 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 {
+ for (stuff *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 = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH;
+ else if (row_is_all_lines[end_row+1] == 2)
+ result = "\\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "+" 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))) {
+ for (int 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)
+ entry[r][p->col-1]->note_double_vrule_on_right();
+ if (p->col < ncolumns && entry[r][p->col] != 0
+ && entry[r][p->col]->start_col == p->col)
+ entry[r][p->col]->note_double_vrule_on_left();
+ }
+}
+
+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'" 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 (row_begins_section(r))
+ prints("." KEEP_MACRO_NAME "\n");
+ int had_line = 0;
+ for (stuff *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;
+ printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
+ as_string(r));
+ 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;
+ }
+ }
+ 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 (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 \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH);
+ if (row_is_all_lines[r] == 2)
+ prints("+" DOUBLE_LINE_SEP);
+ prints("\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 *p = vrule_list; p; p = p->next)
+ if (p->end_row == r) {
+ if (!printed_one) {
+ prints("." REPEATED_VPT_MACRO " 0\n");
+ printed_one = 1;
+ }
+ p->print();
+ }
+ if (printed_one)
+ prints("." REPEATED_VPT_MACRO " 1\n");
+ if (row_ends_section(r))
+ prints("." RELEASE_MACRO_NAME "\n");
+ }
+}
+
+void table::do_top()
+{
+ prints(".fc \002\003\n");
+ if (flags & (BOX|DOUBLEBOX|ALLBOX))
+ prints("." TABLE_KEEP_MACRO_NAME "\n");
+ if (flags & DOUBLEBOX) {
+ prints(".ls 1\n"
+ ".vs \\n[.v]u-" BODY_HEIGHT "-" BODY_DEPTH "\n"
+ "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\n"
+ ".vs\n"
+ "." REPEATED_MARK_MACRO " " TOP_REG "\n"
+ ".vs " DOUBLE_LINE_SEP "\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);
+ 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 & (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/tbl/table.h b/tbl/table.h
new file mode 100644
index 000000000..4a2744049
--- /dev/null
+++ b/tbl/table.h
@@ -0,0 +1,148 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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();
+};
+
+struct entry_format : entry_modifier {
+ enum format_type {
+ LEFT,
+ CENTER,
+ RIGHT,
+ NUMERIC,
+ ALPHABETIC,
+ SPAN,
+ VSPAN,
+ HLINE,
+ DOUBLE_HLINE,
+ }
+ 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];
+ vertical_rule *vrule_list;
+ stuff *stuff_list;
+ horizontal_span *span_list;
+ table_entry *entry_list;
+ 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
+ };
+ table(int nc, unsigned flags, int linesize);
+ ~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/troff/Makefile b/troff/Makefile
new file mode 100644
index 000000000..3d53f7904
--- /dev/null
+++ b/troff/Makefile
@@ -0,0 +1,114 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+MACROPATH=.:/usr/local/lib/groff/tmac:/usr/lib/tmac
+# DEVICE is the default device
+DEVICE=ps
+HYPHENFILE=/usr/local/lib/groff/hyphen
+BINDIR=/usr/local/bin
+INCLUDES=-I../lib
+CC=g++
+MLIBS=-lm
+CFLAGS=-g -O -Wall #-DOP_DELETE_BROKEN
+LDFLAGS=-g
+ETAGS=etags
+ETAGSFLAGS=-p
+OBJECTS=env.o node.o input.o div.o column.o symbol.o dictionary.o \
+ reg.o number.o majorminor.o # dump.o unexec.o
+
+SOURCES=env.c node.c input.c div.c column.c symbol.c dictionary.c \
+ number.c reg.c majorminor.c
+
+HEADERS=troff.h hvunits.h symbol.h dictionary.h env.h reg.h \
+ token.h charinfo.h div.h node.h request.h
+
+TROFF_H=troff.h ../lib/errarg.h ../lib/error.h ../lib/cset.h ../lib/lib.h
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $<
+
+all: troff
+
+troff: $(OBJECTS) ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(MLIBS)
+
+majorminor.c: ../VERSION
+ @echo Making $@
+ @-rm -f $@
+ @echo const char \*major_version = \
+ \"`sed -e 's/^\(.*\)\..*$$/\1/' ../VERSION`\"\; >$@
+ @echo const char \*minor_version = \
+ \"`sed -e 's/^.*\.\(.*\)$$/\1/' ../VERSION`\"\; >>$@
+
+dictionary.o: dictionary.h $(TROFF_H) symbol.h
+symbol.o: $(TROFF_H) symbol.h
+number.o: div.h env.h $(TROFF_H) hvunits.h symbol.h token.h
+reg.o : $(TROFF_H) symbol.h dictionary.h token.h request.h reg.h
+div.o: dictionary.h div.h env.h $(TROFF_H) hvunits.h \
+ node.h reg.h request.h symbol.h token.h
+env.o: charinfo.h dictionary.h div.h env.h $(TROFF_H) \
+ hvunits.h node.h reg.h request.h symbol.h token.h
+input.o: config.h charinfo.h dictionary.h div.h env.h $(TROFF_H) \
+ hvunits.h node.h reg.h request.h symbol.h token.h
+node.o: charinfo.h dictionary.h env.h reg.h \
+ $(TROFF_H) hvunits.h node.h request.h symbol.h \
+ token.h ../lib/font.h
+column.o: dictionary.h div.h env.h $(TROFF_H) hvunits.h \
+ node.h reg.h request.h symbol.h token.h ../lib/stringclass.h
+
+config.h: FORCE
+ @echo \#define MACROPATH \"$(MACROPATH)\" >config.h.n
+ @echo \#define HYPHENFILE \"$(HYPHENFILE)\" >>config.h.n
+ @echo \#define DEVICE \"$(DEVICE)\" >>config.h.n
+ @cmp -s config.h config.h.n || cp config.h.n config.h
+ @rm -f config.h.n
+
+TAGS : $(SOURCES)
+ $(ETAGS) $(ETAGSFLAGS) $(SOURCES) $(HEADERS)
+
+saber_troff:
+ @#load $(INCLUDES) $(CFLAGS) $(DEFINES) $(SOURCES) \
+ ../lib/libgroff.a -lm
+
+clean:
+ -rm -f *.o core troff gmon.out mon.out majorminor.c config.h
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+install.bin : troff
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/gtroff
+ cp troff $(BINDIR)/gtroff
+
+install.nobin: hyphen
+ -rm -f $(HYPHENFILE)
+ cp hyphen $(HYPHENFILE)
+
+install: install.bin install.nobin
+
+# dump.o: dump.c config.h
+# cc -g $(DUMPFLAG) -c dump.c
+#
+# unexec.o: unexec.c config.h
+# cc -g $(DUMPFLAG) -I../lib -c unexec.c
+
+FORCE:
diff --git a/troff/TODO b/troff/TODO
new file mode 100644
index 000000000..88d63ed09
--- /dev/null
+++ b/troff/TODO
@@ -0,0 +1,127 @@
+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.
+
+Make it possible to have multilingual documents, by having a ``current
+language'' with which hyphenation information (the pattern trie and
+exception dictionary) is associated; the current language should be
+associated with the environment. Get some pattern tries for other
+languages (assuming ISO Latin-1).
+
+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. \B'...'...'...' 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.
+
+Variant of tm which doesn't write a newline.
+
+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 scaleable 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).
+
+Request to copy an environment into the current environment.
+
+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.
+
+Have a blank line macro like the end macro. When a blank line macro
+has been set, then a blank line causes the blank line macro to be
+called rather than doing the equivalent of .sp.
+
+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 (substring, length, 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 packge.
+
+Request to remove an environment. (Need to check it's not on the
+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.
+
+Make it possible to tr characters onto \~.
diff --git a/troff/charinfo.h b/troff/charinfo.h
new file mode 100644
index 000000000..9fb28028e
--- /dev/null
+++ b/troff/charinfo.h
@@ -0,0 +1,158 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+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,
+ };
+ 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();
+ void set_translation(charinfo *);
+ void set_flags(unsigned char);
+ void set_special_translation(int);
+ int get_special_translation();
+ macro *set_macro(macro *);
+ macro *get_macro();
+ int first_time_not_found();
+ void set_number(int);
+ int get_number();
+ int numbered();
+};
+
+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()
+{
+ return 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()
+{
+ return 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;
+ }
+}
diff --git a/troff/column.c b/troff/column.c
new file mode 100644
index 000000000..690a8c1eb
--- /dev/null
+++ b/troff/column.c
@@ -0,0 +1,732 @@
+// -*- C++ -*-
+/* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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()
+{
+ delete type;
+ 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];
+ delete old_type;
+ vunits *old_amount = amount;
+ amount = new vunits[maxn];
+ for (i = 0; i < n; i++)
+ amount[i] = old_amount[i];
+ 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 itoa(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 itoa(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/troff/dictionary.c b/troff/dictionary.c
new file mode 100644
index 000000000..fa67a5dbe
--- /dev/null
+++ b/troff/dictionary.c
@@ -0,0 +1,210 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+ for (unsigned 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) : threshold(0.5), factor(1.5), used(0), size(n)
+{
+ table = new association[n];
+ for (int i = 0; i < n; i++)
+ table[i].v = 0;
+}
+
+// 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)
+{
+ for (int i = 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);
+ }
+ 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
+ for (int i = 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 = table[i].s.hash() % size;
+ } while ((i <= r && r < j) || (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/troff/dictionary.h b/troff/dictionary.h
new file mode 100644
index 000000000..c0829d14f
--- /dev/null
+++ b/troff/dictionary.h
@@ -0,0 +1,92 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/troff/div.c b/troff/div.c
new file mode 100644
index 000000000..f3a980091
--- /dev/null
+++ b/troff/div.c
@@ -0,0 +1,1113 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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"
+
+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)
+: nm(s), prev(0), vertical_position(V0), marked_place(V0), high_water_mark(V0)
+{
+}
+
+struct vertical_size {
+ vunits pre_extra, post_extra, pre, post;
+ vertical_size(vunits vs, int ls);
+};
+
+vertical_size::vertical_size(vunits vs, int ls)
+: pre_extra(V0), post_extra(V0), pre(vs)
+{
+ if (ls > 1)
+ post = vs*(ls - 1);
+ else
+ post = V0;
+}
+
+void node::set_vertical_size(vertical_size *v)
+{
+}
+
+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)
+{
+ tok.skip();
+ symbol nm = get_name();
+ if (nm.is_null()) {
+ if (curdiv->prev) {
+ 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;
+ }
+ skip_line();
+}
+
+void divert()
+{
+ do_divert(0);
+}
+
+void divert_append()
+{
+ do_divert(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, int ls, hunits width)
+{
+ vertical_size v(vs, ls);
+ 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);
+ }
+ }
+ if (!v.post_extra.is_zero())
+ last_post_line_extra_space = v.post_extra.to_units();
+ if (!retain_size) {
+ v.pre = vs;
+ v.post = (ls > 1) ? vs*(ls - 1) : V0;
+ }
+ 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 > high_water_mark)
+ high_water_mark = vertical_position;
+}
+
+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;
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+}
+
+void macro_diversion::copy_file(const char *filename)
+{
+ mac->append(new diverted_copy_file_node(filename));
+}
+
+top_level_diversion::top_level_diversion()
+: page_count(0), have_next_page_number(0), page_length(units_per_inch*11),
+ page_offset(units_per_inch), prev_page_offset(units_per_inch),
+ ejecting_page(0), page_trap_list(0), first_page_begun(0), no_space_mode(0),
+ page_number(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, int ls, hunits /*width*/)
+{
+ no_space_mode = 0;
+ vunits next_trap_pos;
+ trap *next_trap = find_next_trap(&next_trap_pos);
+ if (!first_page_begun && begin_page())
+ fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
+ vertical_size v(vs, ls);
+ for (node *tem = nd; tem != 0; tem = tem->next)
+ tem->set_vertical_size(&v);
+ if (!v.post_extra.is_zero())
+ last_post_line_extra_space = v.post_extra.to_units();
+ if (!retain_size) {
+ v.pre = vs;
+ v.post = (ls > 1) ? vs*(ls - 1) : V0;
+ }
+ 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);
+ vertical_position += v.post_extra;
+ 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;
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+ 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();
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+ 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();
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+ }
+ }
+ else {
+ nl_reg_contents = vertical_position.to_units();
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+ }
+}
+
+void top_level_diversion::transparent_output(unsigned char c)
+{
+ if (!first_page_begun && 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 (!first_page_begun && 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 (!first_page_begun) {
+ 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;
+ if (vertical_position > high_water_mark)
+ high_water_mark = 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();
+ if (vertical_position > high_water_mark)
+ high_water_mark = vertical_position;
+ }
+}
+
+trap::trap(symbol s, vunits n, trap *p)
+ : nm(s), next(p), position(n)
+{
+}
+
+void top_level_diversion::add_trap(symbol nm, vunits pos)
+{
+ trap *first_free_slot = 0;
+ for (trap **p = &page_trap_list; *p; p = &(*p)->next) {
+ if ((*p)->position == pos) {
+ (*p)->nm = nm;
+ return;
+ }
+ else if ((*p)->nm.is_null() && first_free_slot == 0)
+ first_free_slot = *p;
+ }
+ 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;
+ }
+}
+
+NO_RETURN void cleanup_and_exit(int exit_code)
+{
+ if (the_output)
+ delete the_output;
+ exit(exit_code);
+}
+
+int exit_flag = 0;
+
+// returns non-zero if it sprung a top of page trap
+
+int top_level_diversion::begin_page()
+{
+ if (exit_flag == 2 || (exit_flag == 1 && curenv->is_empty()))
+ 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 (!first_page_begun)
+ 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;
+ first_page_begun = 1;
+ ejecting_page = 0;
+ the_output->begin_page(page_number, page_length);
+ 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;
+ if (!has_arg()) {
+ hunits temp = topdiv->page_offset;
+ topdiv->page_offset = topdiv->prev_page_offset;
+ topdiv->prev_page_offset = temp;
+ }
+ else if (get_hunits(&n, 'v', topdiv->page_offset)) {
+ topdiv->prev_page_offset = topdiv->page_offset;
+ topdiv->page_offset = n;
+ }
+ skip_line();
+}
+
+void page_length()
+{
+ vunits n;
+ if (!has_arg())
+ topdiv->set_page_length(11*units_per_inch);
+ else if (get_vunits(&n, 'v', topdiv->get_page_length()))
+ topdiv->set_page_length(n);
+ 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->first_page_begun) {
+ 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();
+ int err = 0;
+ vunits n;
+ if (!has_arg())
+ n = curenv->get_vertical_spacing();
+ else if (!get_vunits(&n, 'v'))
+ err = 1;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (!unpostpone_traps()) {
+ if (!err)
+ curdiv->space(n);
+ }
+ else {
+ // The line might have had line spacing that was truncated.
+ if (!err)
+ truncated_space += n;
+ }
+ 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()
+{
+ int err = 0;
+ vunits n;
+ if (!has_arg())
+ n = curenv->get_vertical_spacing();
+ else if (!get_vunits(&n, 'v'))
+ err = 1;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (!err)
+ 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 (get_vunits(&x, 'v')) {
+ 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();
+ 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
+ set_number_reg(s, curdiv->get_vertical_position().to_units());
+ skip_line();
+}
+
+void return_request()
+{
+ vunits dist = V0;
+ // This is truly bizarre. It is documented in the SQ manual.
+ 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') && x >= V0)
+ dist = x - curdiv->get_vertical_position();
+ }
+ }
+ else
+ dist = curdiv->marked_place - curdiv->get_vertical_position();
+ if (dist < V0)
+ curdiv->space(dist);
+ skip_line();
+}
+
+void vertical_position_traps()
+{
+ int n;
+ if (!has_arg())
+ vertical_position_traps_flag = 1;
+ else if (get_integer(&n))
+ vertical_position_traps_flag = (n != 0);
+ 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 itoa(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 itoa(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->first_page_begun)
+ *res = -1;
+ else
+ *res = curdiv->get_vertical_position().to_units();
+ return 1;
+}
+
+const char *vertical_position_reg::get_string()
+{
+ if (curdiv == topdiv && !topdiv->first_page_begun)
+ return "-1";
+ else
+ return itoa(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 itoa(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 itoa(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 itoa(topdiv->get_next_page_number());
+}
+
+class page_ejecting_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *page_ejecting_reg::get_string()
+{
+ return itoa(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 itoa(p->to_units());
+}
+
+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("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 variable_reg(&nl_reg_contents));
+ 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/troff/div.h b/troff/div.h
new file mode 100644
index 000000000..c090731b2
--- /dev/null
+++ b/troff/div.h
@@ -0,0 +1,140 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+class diversion {
+ friend void do_divert(int append);
+ friend void end_diversions();
+ diversion *prev;
+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, int ls, 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, int ls, 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;
+ 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 first_page_begun;
+ int no_space_mode;
+ top_level_diversion();
+ void output(node *nd, int retain_size, vunits vs, int ls, 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();
+};
+
+extern top_level_diversion *topdiv;
+extern diversion *curdiv;
+
+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/troff/env.c b/troff/env.c
new file mode 100644
index 000000000..61515b4af
--- /dev/null
+++ b/troff/env.c
@@ -0,0 +1,2947 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 <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;
+
+
+
+class pending_output_line {
+ node *nd;
+ int no_fill;
+ vunits vs;
+ int ls;
+ 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, int, 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, int, hunits);
+#endif /* WIDOW_CONTROL */
+};
+
+pending_output_line::pending_output_line(node *n, int nf, vunits v, int l,
+ hunits w, pending_output_line *p)
+: nd(n), no_fill(nf), vs(v), ls(l), 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*ls + vunits(vresolution));
+ if (trap_sprung_flag) {
+ next->last_line = 0; // Try to avoid infinite loops.
+ return 0;
+ }
+ }
+#endif
+ curdiv->output(nd, no_fill, vs, ls, width);
+ nd = 0;
+ return 1;
+}
+
+void environment::output(node *nd, int no_fill, vunits vs, int ls,
+ 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, ls, width);
+ else {
+ for (pending_output_line **p = &pending_lines; *p; p = &(*p)->next)
+ ;
+ *p = new pending_output_line(nd, no_fill, vs, ls, width);
+ }
+}
+
+// a line from .tl goes at the head of the queue
+
+void environment::output_title(node *nd, int no_fill, vunits vs, int ls,
+ hunits width)
+{
+ if (!trap_sprung_flag)
+ curdiv->output(nd, no_fill, vs, ls, width);
+ else
+ pending_lines = new pending_output_line(nd, no_fill, vs, ls, 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()
+{
+ if (has_arg()) {
+ int n;
+ if (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;
+
+static int compare_ranges(void *p1, 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)
+{
+ 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) {
+ if (current_tab) {
+ tab_contents = new space_node(H0, tab_contents);
+ tab_field_spaces++;
+ }
+ else {
+ line = new space_node(H0, line);
+ field_spaces++;
+ }
+ }
+ 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);
+ 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);
+ 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()) {
+ 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);
+}
+
+int environment::get_space_size()
+{
+ return space_size;
+}
+
+int environment::get_sentence_space_size()
+{
+ return sentence_space_size;
+}
+
+hunits environment::get_space_width()
+{
+ return scale(env_space_width(this), space_size, 12);
+}
+
+hunits environment::get_narrow_space_width()
+{
+ return env_narrow_space_width(this);
+}
+
+hunits environment::get_half_narrow_space_width()
+{
+ return env_half_narrow_space_width(this);
+}
+
+void environment::space_newline()
+{
+ assert(!current_tab && !current_field);
+ if (interrupted)
+ return;
+ int ss = space_size;
+ if (node_list_ends_sentence(line) == 1)
+ ss += sentence_space_size;
+ hunits x = scale(env_space_width(this), ss, 12);
+ if (line != 0 && line->merge_space(x)) {
+ width_total += x;
+ return;
+ }
+ add_node(new word_space_node(x));
+ possibly_break_line(spread_flag);
+ spread_flag = 0;
+}
+
+void environment::space()
+{
+ if (interrupted)
+ return;
+ if (current_field && padding_indicator_char == 0) {
+ node **pp = current_tab ? &tab_contents : &line;
+ int *hp = current_tab ? &tab_field_spaces : &field_spaces;
+ *pp = new space_node(H0, *pp);
+ *hp += 1;
+ return;
+ }
+ hunits x = scale(env_space_width(this), space_size, 12);
+ 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 = scale(env_space_width(this), sentence_space_size, 12);
+ if (p->merge_space(xx)) {
+ *tp += xx;
+ return;
+ }
+ }
+ if (p && p->merge_space(x)) {
+ *tp += x;
+ return;
+ }
+ add_node(new word_space_node(x));
+ possibly_break_line(spread_flag);
+ spread_flag = 0;
+}
+
+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 {
+ 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;
+ prev_fontno = fontno;
+ fontno = n;
+ }
+}
+
+void environment::set_font(int n)
+{
+ if (interrupted)
+ return;
+ if (is_good_fontno(n)) {
+ prev_fontno = fontno;
+ fontno = n;
+ }
+ else
+ error("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)
+: name(nm),
+ 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_line_spacing(1),
+ line_spacing(1),
+ prev_indent(0),
+ indent(0),
+ have_temporary_indent(0),
+ temporary_indent(0),
+ underline_lines(0),
+ input_trap_count(0),
+ prev_text_length(0),
+ width_total(0),
+ space_total(0),
+ input_line_start(0),
+ control_char('.'),
+ no_break_control_char('\''),
+ hyphen_indicator_char(0),
+ spread_flag(0),
+ line(0),
+ pending_lines(0),
+ discarding(0),
+ tabs(units_per_inch/2, TAB_LEFT),
+ current_tab(TAB_NONE),
+ current_field(0),
+ margin_character_node(0),
+ margin_character_distance(points_to_units(10)),
+ numbering_nodes(0),
+ number_text_separation(1),
+ line_number_multiple(1),
+ line_number_indent(0),
+ no_number_count(0),
+ tab_char(0),
+ leader_char(charset_table['.']),
+ hyphenation_flags(1),
+ dummy(0),
+ leader_node(0),
+#ifdef WIDOW_CONTROL
+ widow_control(0),
+#endif /* WIDOW_CONTROL */
+ hyphen_line_count(0),
+ hyphen_line_max(-1),
+ hyphenation_space(H0),
+ hyphenation_margin(H0)
+{
+ 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)
+: name(e->name), // so that eg `.if "\n[.ev]"0"' works
+ 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_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),
+ 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),
+ current_tab(TAB_NONE),
+ current_field(0),
+ 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(1),
+ 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)
+{
+}
+
+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;
+}
+
+int environment::get_line_spacing()
+{
+ return line_spacing;
+}
+
+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(itoa(n));
+ curenv = env_table[n];
+ }
+ else
+ nm = itoa(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();
+}
+
+
+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(1);
+ if (!s.is_null())
+ curenv->set_family(s);
+ skip_line();
+}
+
+#if 0
+void point_size()
+{
+ int n;
+ if (has_arg()) {
+ if (get_number(&n, 0, curenv->get_requested_point_size()/sizescale)) {
+ n *= sizescale;
+ if (n <= 0)
+ n = 1;
+ curenv->set_size(n);
+ }
+ }
+ else
+ curenv->set_size(0);
+ skip_line();
+}
+#endif
+
+void point_size()
+{
+ int n;
+ if (has_arg()) {
+ if (get_number(&n, 'z', curenv->get_requested_point_size())) {
+ if (n <= 0)
+ n = 1;
+ curenv->set_size(n);
+ }
+ }
+ else
+ curenv->set_size(0);
+ skip_line();
+}
+
+void space_size()
+{
+ int n;
+ if (get_integer(&n)) {
+ curenv->space_size = n;
+ if (has_arg()) {
+ if (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;
+ tok.next();
+}
+
+void no_fill()
+{
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (break_flag)
+ curenv->do_break();
+ curenv->fill = 0;
+ tok.next();
+}
+
+void center()
+{
+ int n;
+ if (!has_arg() || !get_integer(&n))
+ n = 1;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (break_flag)
+ curenv->do_break();
+ curenv->right_justify_lines = 0;
+ curenv->center_lines = n;
+ tok.next();
+}
+
+void right_justify()
+{
+ int n;
+ if (!has_arg() || !get_integer(&n))
+ n = 1;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (break_flag)
+ curenv->do_break();
+ curenv->center_lines = 0;
+ curenv->right_justify_lines = n;
+ tok.next();
+}
+
+void line_length()
+{
+ hunits temp;
+ if (!has_arg()) {
+ hunits temp = curenv->line_length;
+ curenv->line_length = curenv->prev_line_length;
+ curenv->prev_line_length = temp;
+ }
+ else if (get_hunits(&temp, 'm', curenv->line_length)) {
+ if (temp < H0) {
+ warning(WARN_RANGE, "bad line length %1u", temp.to_units());
+ temp = H0;
+ }
+ curenv->prev_line_length = curenv->line_length;
+ curenv->line_length = temp;
+ }
+ skip_line();
+}
+
+void title_length()
+{
+ hunits temp;
+ if (!has_arg()) {
+ hunits temp = curenv->title_length;
+ curenv->title_length = curenv->prev_title_length;
+ curenv->prev_title_length = temp;
+ }
+ else if (get_hunits(&temp, 'm', curenv->title_length)) {
+ if (temp < H0) {
+ warning(WARN_RANGE, "bad title length %1u", temp.to_units());
+ temp = H0;
+ }
+ curenv->prev_title_length = curenv->title_length;
+ curenv->title_length = temp;
+ }
+ skip_line();
+}
+
+void vertical_spacing()
+{
+ if (!has_arg()) {
+ vunits temp = curenv->vertical_spacing;
+ curenv->vertical_spacing = curenv->prev_vertical_spacing;
+ curenv->prev_vertical_spacing = temp;
+ }
+ else {
+ vunits temp;
+ if (get_vunits(&temp, 'p', curenv->vertical_spacing)) {
+ if (temp <= V0) {
+ warning(WARN_RANGE, "vertical spacing must be greater than 0");
+ temp = vresolution;
+ }
+ curenv->prev_vertical_spacing = curenv->vertical_spacing;
+ curenv->vertical_spacing = temp;
+ }
+ }
+ skip_line();
+}
+
+void line_spacing()
+{
+ int temp;
+ if (!has_arg()) {
+ temp = curenv->line_spacing;
+ curenv->line_spacing = curenv->prev_line_spacing;
+ curenv->prev_line_spacing = temp;
+ }
+ else if (get_integer(&temp)) {
+ if (temp < 1) {
+ warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
+ temp = 1;
+ }
+ curenv->prev_line_spacing = curenv->line_spacing;
+ curenv->line_spacing = temp;
+ }
+ skip_line();
+}
+
+void indent()
+{
+ hunits temp;
+ int err = 0;
+ if (!has_arg())
+ temp = curenv->prev_indent;
+ else if (!get_hunits(&temp, 'm', curenv->indent))
+ err = 1;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (break_flag)
+ curenv->do_break();
+ if (temp < H0) {
+ warning(WARN_RANGE, "indent cannot be negative");
+ temp = H0;
+ }
+ if (!err) {
+ curenv->have_temporary_indent = 0;
+ curenv->prev_indent = curenv->indent;
+ curenv->indent = temp;
+ }
+ 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;
+ }
+ tok.next();
+}
+
+void underline()
+{
+ int n = 0;
+ if (!has_arg())
+ n = 1;
+ else if (!get_integer(&n))
+ n = 0;
+ if (n <= 0) {
+ if (curenv->underline_lines > 0) {
+ curenv->prev_fontno = curenv->fontno;
+ curenv->fontno = curenv->pre_underline_fontno;
+ }
+ curenv->underline_lines = 0;
+ }
+ else {
+ curenv->underline_lines = n;
+ curenv->pre_underline_fontno = curenv->fontno;
+ curenv->fontno = get_underline_fontno();
+ }
+ skip_line();
+}
+
+
+void control_char()
+{
+ if (!has_arg())
+ curenv->control_char = '.';
+ else if (tok.ch() == 0)
+ error("bad control character");
+ else
+ curenv->control_char = tok.ch();
+ skip_line();
+}
+
+void no_break_control_char()
+{
+ if (!has_arg())
+ curenv->no_break_control_char = '\'';
+ else if (tok.ch() == 0)
+ error("bad control character");
+ else
+ curenv->no_break_control_char = tok.ch();
+ skip_line();
+}
+
+
+void margin_character()
+{
+ if (curenv->margin_character_node) {
+ delete curenv->margin_character_node;
+ curenv->margin_character_node = 0;
+ }
+ charinfo *ci = get_optional_char();
+ if (ci) {
+ curenv->margin_character_node = curenv->make_char_node(ci);
+ hunits d;
+ if (curenv->margin_character_node && has_arg() && get_hunits(&d, 'm'))
+ curenv->margin_character_distance = d;
+ }
+ skip_line();
+}
+
+void number_lines()
+{
+ delete_node_list(curenv->numbering_nodes);
+ curenv->numbering_nodes = 0;
+ int n;
+ if (has_arg() && 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;
+ }
+ 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);
+ 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()
+{
+ if (has_arg()) {
+ int n;
+ if (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()) {
+ if (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()
+{
+ if (has_arg()) {
+ int n;
+ if (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 (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 = interrupted;
+ if (dummy)
+ space_newline();
+ else if (interrupted)
+ interrupted = 0;
+ 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_node) {
+ hunits d = line_length + margin_character_distance - saved_indent - width;
+ if (d > 0) {
+ n = new hmotion_node(d, n);
+ width += d;
+ }
+ node *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, line_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()
+{
+ if (line == 0)
+ return;
+ hyphenation_type prev_type = line->get_hyphenation_type();
+ for (node **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;
+ }
+ // 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() <= line_spacing*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_forward = 0)
+{
+ static int reverse = 0;
+ if (!force_forward && reverse)
+ n = node_list_reverse(n);
+ for (node *tem = n; tem; tem = tem->next)
+ tem->spread_space(&nspaces, &desired_space);
+ if (!force_forward) {
+ if (reverse)
+ (void)node_list_reverse(n);
+ reverse = !reverse;
+ }
+ assert(desired_space.is_zero() && nspaces == 0);
+}
+
+void environment::possibly_break_line(int forced)
+{
+ if (!fill || current_tab || current_field || dummy)
+ return;
+ while (line != 0 && (forced || width_total > target_text_length)) {
+ hyphenate_line();
+ 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);
+ node *tem = line;
+ line = 0;
+ output_line(pre, bp->width + extra_space_width);
+ line = tem;
+ input_line_start -= bp->width + extra_space_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;
+ 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;
+ }
+ 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;
+ }
+ }
+}
+
+void environment::do_break()
+{
+ if (curdiv == topdiv && !topdiv->first_page_begun) {
+ 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;
+ }
+#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();
+ tok.next();
+}
+
+void title()
+{
+ if (curdiv == topdiv && !topdiv->first_page_begun) {
+ 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->line_spacing, title_length);
+ curenv->hyphen_line_count = 0;
+ tok.next();
+}
+
+void adjust()
+{
+ if (!has_arg())
+ curenv->adjust_mode |= 1;
+ else
+ 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 (0 <= n && n <= 5)
+ curenv->adjust_mode = n;
+ else
+ warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
+ }
+ }
+ skip_line();
+}
+
+void no_adjust()
+{
+ curenv->adjust_mode &= ~1;
+ skip_line();
+}
+
+void input_trap()
+{
+ int n;
+ if (!has_arg())
+ curenv->input_trap_count = 0;
+ else if (get_integer(&n)) {
+ if (n <= 0)
+ error("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);
+#ifndef OP_DELETE_BROKEN
+ enum { BLOCK = 1024 };
+ static tab *free_list;
+ void *operator new(size_t);
+ void operator delete(void *);
+#endif /* not OP_DELETE_BROKEN */
+};
+
+#ifndef OP_DELETE_BROKEN
+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;
+ }
+}
+
+#endif /* not OP_DELETE_BROKEN */
+
+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;
+ for (tab *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;
+ for (tab *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)
+ delete buf;
+ buf_size = need;
+ buf = new char[buf_size];
+ }
+ char *ptr = buf;
+ for (p = initial_list; p; p = p->next) {
+ strcpy(ptr, itoa(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, itoa(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)
+{
+ for (tab **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 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;
+ if (pos <= prev_pos) {
+ warning(WARN_RANGE, "positions of tab stops must be strictly increasing");
+ continue;
+ }
+ 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();
+ }
+ tabs.add_tab(pos, type, repeated);
+ prev_pos = pos;
+ }
+ curenv->tabs = 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 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 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) {
+ for (node *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, 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_node(make_tab_node(d));
+ return;
+ case TAB_RIGHT:
+ case 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)
+ p->freeze_space();
+ tab_precedes_field = current_tab != TAB_NONE;
+ }
+ else
+ error("zero field width");
+}
+
+void environment::wrap_up_field()
+{
+ 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) {
+ for (node *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;
+}
+
+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 itoa((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 itoa((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 itoa((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 itoa(sp);
+ if (sp % sizescale == 0)
+ return itoa(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 iftoa(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 iftoa(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("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", 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("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_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(".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_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_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.
+
+const int WORD_MAX = 1024;
+
+dictionary exception_dictionary(501);
+
+static void hyphen_word()
+{
+ 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 *)exception_dictionary.lookup(symbol(buf), tem);
+ if (tem)
+ delete tem;
+ }
+ }
+ skip_line();
+}
+
+struct trie_node;
+
+class trie {
+ trie_node *tp;
+ virtual void do_match(int len, void *val) = 0;
+public:
+ trie() : tp(0) {}
+ void insert(const char *, int, void *);
+ // find calls do_match for each match it finds
+ void find(const char *pat, int patlen);
+ void clear();
+};
+
+struct trie_node {
+ char c;
+ trie_node *down;
+ trie_node *right;
+ void *val;
+ trie_node(char, trie_node *);
+ ~trie_node();
+};
+
+trie_node::trie_node(char ch, trie_node *p)
+: c(ch), right(p), down(0), val(0)
+{
+}
+
+trie_node::~trie_node()
+{
+ if (down)
+ delete down;
+ if (right)
+ delete right;
+ if (val)
+ delete val;
+}
+
+void trie::clear()
+{
+ if (tp) {
+ delete tp;
+ tp = 0;
+ }
+}
+
+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;
+ }
+}
+
+class hyphen_trie : private trie {
+ int *h;
+ void do_match(int i, void *v);
+ void insert_pattern(const char *pat, int patlen, int *num);
+public:
+ hyphen_trie() {}
+ void hyphenate(const char *word, int len, int *hyphens);
+ void read_patterns_file(const char *name);
+};
+
+struct operation {
+ operation *next;
+ short distance;
+ short num;
+ operation(int, int, operation *);
+};
+
+operation::operation(int i, int j, operation *op)
+: num(i), distance(j), next(op)
+{
+}
+
+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)
+{
+ for (int 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::read_patterns_file(const char *name)
+{
+ clear();
+ char buf[WORD_MAX];
+ int num[WORD_MAX+1];
+ FILE *fp = fopen(name, "r");
+ if (fp == 0)
+ fatal("can't open hyphenation patterns file `%1': %2",
+ name, strerror(errno));
+ int c = getc(fp);
+ for (;;) {
+ while (c != EOF && csspace(c))
+ 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));
+ insert_pattern(buf, i, num);
+ }
+ fclose(fp);
+}
+
+hyphen_trie ht;
+
+void hyphenate(hyphen_list *h, unsigned flags)
+{
+ while (h && h->hyphenation_code == 0)
+ h = h->next;
+ int len = 0;
+ char hbuf[WORD_MAX+2];
+ char *buf = hbuf + 1;
+ for (hyphen_list *tem = h; tem && len < WORD_MAX; tem = tem->next) {
+ if (tem->hyphenation_code != 0)
+ buf[len++] = tem->hyphenation_code;
+ else
+ break;
+ }
+ if (len > 2) {
+ buf[len] = 0;
+ unsigned char *pos = (unsigned char *)exception_dictionary.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];
+ ht.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;
+ }
+ }
+}
+
+void read_hyphen_file(const char *name)
+{
+ ht.read_patterns_file(name);
+}
+
+void init_hyphen_requests()
+{
+ init_request("hw", hyphen_word);
+}
diff --git a/troff/env.h b/troff/env.h
new file mode 100644
index 000000000..e8ca90dc9
--- /dev/null
+++ b/troff/env.h
@@ -0,0 +1,309 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 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();
+};
+
+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;
+ 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;
+ 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;
+ 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
+ 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;
+ pending_output_line *pending_lines;
+#ifdef WIDOW_CONTROL
+ int widow_control;
+#endif /* WIDOW_CONTROL */
+
+ tab_type distance_to_next_tab(hunits *);
+ void start_line();
+ void output_line(node *, hunits);
+ void output(node *nd, int retain_size, vunits vs, int ls, hunits width);
+ void output_title(node *nd, int retain_size, vunits vs, int ls,
+ hunits width);
+#ifdef WIDOW_CONTROL
+ void mark_last_line();
+#endif /* WIDOW_CONTROL */
+ void possibly_break_line(int forced = 0);
+ breakpoint *choose_breakpoint();
+ void hyphenate_line();
+ void start_field();
+ void wrap_up_field();
+ 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();
+ int is_dummy() { return dummy; }
+ int is_empty();
+ vunits get_vertical_spacing(); // .v
+ int get_line_spacing(); // .L
+ 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();
+ int get_space_size(); // in ems/36
+ int get_sentence_space_size();
+ hunits get_narrow_space_width();
+ hunits get_half_narrow_space_width();
+ hunits get_input_line_position();
+ const char *get_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 do_break(); // .br
+ 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_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 line_spacing();
+ friend void line_length();
+ friend void indent();
+ friend void temporary_indent();
+ friend void underline();
+ 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 title();
+#ifdef WIDOW_CONTROL
+ friend void widow_control_request();
+#endif /* WIDOW_CONTROL */
+};
+
+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 symbol default_family;
diff --git a/troff/hvunits.h b/troff/hvunits.h
new file mode 100644
index 000000000..63b126467
--- /dev/null
+++ b/troff/hvunits.h
@@ -0,0 +1,340 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+class vunits {
+ int n;
+public:
+ vunits();
+ vunits(units);
+ units to_units();
+ int is_zero();
+ vunits& operator+=(const vunits&);
+ vunits& operator-=(const vunits&);
+ friend vunits scale(vunits n, units x, units y); // scale n by x/y
+ friend vunits scale(vunits n, vunits x, vunits y);
+ friend vunits operator +(const vunits&, const vunits&);
+ friend vunits operator -(const vunits&, const vunits&);
+ friend vunits operator -(const vunits&);
+ friend int operator /(const vunits&, const vunits&);
+ friend vunits operator /(const vunits&, int);
+ friend vunits operator *(const vunits&, int);
+ friend vunits operator *(int, const vunits&);
+ friend int operator <(const vunits&, const vunits&);
+ friend int operator >(const vunits&, const vunits&);
+ friend int operator <=(const vunits&, const vunits&);
+ friend int operator >=(const vunits&, const vunits&);
+ friend int operator ==(const vunits&, const vunits&);
+ friend 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 hunits scale(hunits n, units x, units y); // scale n by x/y
+ friend hunits scale(hunits n, double x);
+ friend hunits operator +(const hunits&, const hunits&);
+ friend hunits operator -(const hunits&, const hunits&);
+ friend hunits operator -(const hunits&);
+ friend int operator /(const hunits&, const hunits&);
+ friend hunits operator /(const hunits&, int);
+ friend hunits operator *(const hunits&, int);
+ friend hunits operator *(int, const hunits&);
+ friend int operator <(const hunits&, const hunits&);
+ friend int operator >(const hunits&, const hunits&);
+ friend int operator <=(const hunits&, const hunits&);
+ friend int operator >=(const hunits&, const hunits&);
+ friend int operator ==(const hunits&, const hunits&);
+ friend 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/troff/hyphen b/troff/hyphen
new file mode 100644
index 000000000..b398bb889
--- /dev/null
+++ b/troff/hyphen
@@ -0,0 +1,4447 @@
+.ach4
+.ad4der
+.af1t
+.al3t
+.am5at
+.an5c
+.ang4
+.ani5m
+.ant4
+.an3te
+.anti5s
+.ar5s
+.ar4tie
+.ar4ty
+.as3c
+.as1p
+.as1s
+.aster5
+.atom5
+.au1d
+.av4i
+.awn4
+.ba4g
+.ba5na
+.bas4e
+.ber4
+.be5ra
+.be3sm
+.be5sto
+.bri2
+.but4ti
+.cam4pe
+.can5c
+.capa5b
+.car5ol
+.ca4t
+.ce4la
+.ch4
+.chill5i
+.ci2
+.cit5r
+.co3e
+.co4r
+.cor5ner
+.de4moi
+.de3o
+.de3ra
+.de3ri
+.des4c
+.dictio5
+.do4t
+.du4c
+.dumb5
+.earth5
+.eas3i
+.eb4
+.eer4
+.eg2
+.el5d
+.el3em
+.enam3
+.en3g
+.en3s
+.eq5ui5t
+.er4ri
+.es3
+.eu3
+.eye5
+.fes3
+.for5mer
+.ga2
+.ge2
+.gen3t4
+.ge5og
+.gi5a
+.gi4b
+.go4r
+.hand5i
+.han5k
+.he2
+.hero5i
+.hes3
+.het3
+.hi3b
+.hi3er
+.hon5ey
+.hon3o
+.hov5
+.id4l
+.idol3
+.im3m
+.im5pin
+.in1
+.in3ci
+.ine2
+.in2k
+.in3s
+.ir5r
+.is4i
+.ju3r
+.la4cy
+.la4m
+.lat5er
+.lath5
+.le2
+.leg5e
+.len4
+.lep5
+.lev1
+.li4g
+.lig5a
+.li2n
+.li3o
+.li4t
+.mag5a5
+.mal5o
+.man5a
+.mar5ti
+.me2
+.mer3c
+.me5ter
+.mis1
+.mist5i
+.mon3e
+.mo3ro
+.mu5ta
+.muta5b
+.ni4c
+.od2
+.odd5
+.of5te
+.or5ato
+.or3c
+.or1d
+.or3t
+.os3
+.os4tl
+.oth3
+.out3
+.ped5al
+.pe5te
+.pe5tit
+.pi4e
+.pio5n
+.pi2t
+.pre3m
+.ra4c
+.ran4t
+.ratio5na
+.ree2
+.re5mit
+.res2
+.re5stat
+.ri4g
+.rit5u
+.ro4q
+.ros5t
+.row5d
+.ru4d
+.sci3e
+.self5
+.sell5
+.se2n
+.se5rie
+.sh2
+.si2
+.sing4
+.st4
+.sta5bl
+.sy2
+.ta4
+.te4
+.ten5an
+.th2
+.ti2
+.til4
+.tim5o5
+.ting4
+.tin5k
+.ton4a
+.to4p
+.top5i
+.tou5s
+.trib5ut
+.un1a
+.un3ce
+.under5
+.un1e
+.un5k
+.un5o
+.un3u
+.up3
+.ure3
+.us5a
+.ven4de
+.ve5ra
+.wil5i
+.ye4
+4ab.
+a5bal
+a5ban
+abe2
+ab5erd
+abi5a
+ab5it5ab
+ab5lat
+ab5o5liz
+4abr
+ab5rog
+ab3ul
+a4car
+ac5ard
+ac5aro
+a5ceou
+ac1er
+a5chet
+4a2ci
+a3cie
+ac1in
+a3cio
+ac5rob
+act5if
+ac3ul
+ac4um
+a2d
+ad4din
+ad5er.
+2adi
+a3dia
+ad3ica
+adi4er
+a3dio
+a3dit
+a5diu
+ad4le
+ad3ow
+ad5ran
+ad4su
+4adu
+a3duc
+ad5um
+ae4r
+aeri4e
+a2f
+aff4
+a4gab
+aga4n
+ag5ell
+age4o
+4ageu
+ag1i
+4ag4l
+ag1n
+a2go
+3agog
+ag3oni
+a5guer
+ag5ul
+a4gy
+a3ha
+a3he
+ah4l
+a3ho
+ai2
+a5ia
+a3ic.
+ai5ly
+a4i4n
+ain5in
+ain5o
+ait5en
+a1j
+ak1en
+al5ab
+al3ad
+a4lar
+4aldi
+2ale
+al3end
+a4lenti
+a5le5o
+al1i
+al4ia.
+ali4e
+al5lev
+4allic
+4alm
+a5log.
+a4ly.
+4alys
+5a5lyst
+5alyt
+3alyz
+4ama
+am5ab
+am3ag
+ama5ra
+am5asc
+a4matis
+a4m5ato
+am5era
+am3ic
+am5if
+am5ily
+am1in
+ami4no
+a2mo
+a5mon
+amor5i
+amp5en
+a2n
+an3age
+3analy
+a3nar
+an3arc
+anar4i
+a3nati
+4and
+ande4s
+an3dis
+an1dl
+an4dow
+a5nee
+a3nen
+an5est.
+a3neu
+2ang
+ang5ie
+an1gl
+a4n1ic
+a3nies
+an3i3f
+an4ime
+a5nimi
+a5nine
+an3io
+a3nip
+an3ish
+an3it
+a3niu
+an4kli
+5anniz
+ano4
+an5ot
+anoth5
+an2sa
+an4sco
+an4sn
+an2sp
+ans3po
+an4st
+an4sur
+antal4
+an4tie
+4anto
+an2tr
+an4tw
+an3ua
+an3ul
+a5nur
+4ao
+apar4
+ap5at
+ap5ero
+a3pher
+4aphi
+a4pilla
+ap5illar
+ap3in
+ap3ita
+a3pitu
+a2pl
+apoc5
+ap5ola
+apor5i
+apos3t
+aps5es
+a3pu
+aque5
+2a2r
+ar3act
+a5rade
+ar5adis
+ar3al
+a5ramete
+aran4g
+ara3p
+ar4at
+a5ratio
+ar5ativ
+a5rau
+ar5av4
+araw4
+arbal4
+ar4chan
+ar5dine
+ar4dr
+ar5eas
+a3ree
+ar3ent
+a5ress
+ar4fi
+ar4fl
+ar1i
+ar5ial
+ar3ian
+a3riet
+ar4im
+ar5inat
+ar3io
+ar2iz
+ar2mi
+ar5o5d
+a5roni
+a3roo
+ar2p
+ar3q
+arre4
+ar4sa
+ar2sh
+4as.
+as4ab
+as3ant
+ashi4
+a5sia.
+a3sib
+a3sic
+5a5si4t
+ask3i
+as4l
+a4soc
+as5ph
+as4sh
+as3ten
+as1tr
+asur5a
+a2ta
+at3abl
+at5ac
+at3alo
+at5ap
+ate5c
+at5ech
+at3ego
+at3en.
+at3era
+ater5n
+a5terna
+at3est
+at5ev
+4ath
+ath5em
+a5then
+at4ho
+ath5om
+4ati.
+a5tia
+at5i5b
+at1ic
+at3if
+ation5ar
+at3itu
+a4tog
+a2tom
+at5omiz
+a4top
+a4tos
+a1tr
+at5rop
+at4sk
+at4tag
+at5te
+at4th
+a2tu
+at5ua
+at5ue
+at3ul
+at3ura
+a2ty
+au4b
+augh3
+au3gu
+au4l2
+aun5d
+au3r
+au5sib
+aut5en
+au1th
+a2va
+av3ag
+a5van
+ave4no
+av3era
+av5ern
+av5ery
+av1i
+avi4er
+av3ig
+av5oc
+a1vor
+3away
+aw3i
+aw4ly
+aws4
+ax4ic
+ax4id
+ay5al
+aye4
+ays4
+azi4er
+azz5i
+5ba.
+bad5ger
+ba4ge
+bal1a
+ban5dag
+ban4e
+ban3i
+barbi5
+bari4a
+bas4si
+1bat
+ba4z
+2b1b
+b2be
+b3ber
+bbi4na
+4b1d
+4be.
+beak4
+beat3
+4be2d
+be3da
+be3de
+be3di
+be3gi
+be5gu
+1bel
+be1li
+be3lo
+4be5m
+be5nig
+be5nu
+4bes4
+be3sp
+be5str
+3bet
+bet5iz
+be5tr
+be3tw
+be3w
+be5yo
+2bf
+4b3h
+bi2b
+bi4d
+3bie
+bi5en
+bi4er
+2b3if
+1bil
+bi3liz
+bina5r4
+bin4d
+bi5net
+bi3ogr
+bi5ou
+bi2t
+3bi3tio
+bi3tr
+3bit5ua
+b5itz
+b1j
+bk4
+b2l2
+blath5
+b4le.
+blen4
+5blesp
+b3lis
+b4lo
+blun4t
+4b1m
+4b3n
+bne5g
+3bod
+bod3i
+bo4e
+bol3ic
+bom4bi
+bon4a
+bon5at
+3boo
+5bor.
+4b1ora
+bor5d
+5bore
+5bori
+5bos4
+b5ota
+both5
+bo4to
+bound3
+4bp
+4brit
+broth3
+2b5s2
+bsor4
+2bt
+bt4l
+b4to
+b3tr
+buf4fer
+bu4ga
+bu3li
+bumi4
+bu4n
+bunt4i
+bu3re
+bus5ie
+buss4e
+5bust
+4buta
+3butio
+b5uto
+b1v
+4b5w
+5by.
+bys4
+1ca
+cab3in
+ca1bl
+cach4
+ca5den
+4cag4
+2c5ah
+ca3lat
+cal4la
+call5in
+4calo
+can5d
+can4e
+can4ic
+can5is
+can3iz
+can4ty
+cany4
+ca5per
+car5om
+cast5er
+cas5tig
+4casy
+ca4th
+4cativ
+cav5al
+c3c
+ccha5
+cci4a
+ccompa5
+ccon4
+ccou3t
+2ce.
+4ced.
+4ceden
+3cei
+5cel.
+3cell
+1cen
+3cenc
+2cen4e
+4ceni
+3cent
+3cep
+ce5ram
+4cesa
+3cessi
+ces5si5b
+ces5t
+cet4
+c5e4ta
+cew4
+2ch
+4ch.
+4ch3ab
+5chanic
+ch5a5nis
+che2
+cheap3
+4ched
+che5lo
+3chemi
+ch5ene
+ch3er.
+ch3ers
+4ch1in
+5chine.
+ch5iness
+5chini
+5chio
+3chit
+chi2z
+3cho2
+ch4ti
+1ci
+3cia
+ci2a5b
+cia5r
+ci5c
+4cier
+5cific.
+4cii
+ci4la
+3cili
+2cim
+2cin
+c4ina
+3cinat
+cin3em
+c1ing
+c5ing.
+5cino
+cion4
+4cipe
+ci3ph
+4cipic
+4cista
+4cisti
+2c1it
+cit3iz
+5ciz
+ck1
+ck3i
+1c4l4
+4clar
+c5laratio
+5clare
+cle4m
+4clic
+clim4
+cly4
+c5n
+1co
+co5ag
+coe2
+2cog
+co4gr
+coi4
+co3inc
+col5i
+5colo
+col3or
+com5er
+con4a
+c4one
+con3g
+con5t
+co3pa
+cop3ic
+co4pl
+4corb
+coro3n
+cos4e
+cov1
+cove4
+cow5a
+coz5e
+co5zi
+c1q
+cras5t
+5crat.
+5cratic
+cre3at
+5cred
+4c3reta
+cre4v
+cri2
+cri5f
+c4rin
+cris4
+5criti
+cro4pl
+crop5o
+cros4e
+cru4d
+4c3s2
+2c1t
+cta4b
+ct5ang
+c5tant
+c2te
+c3ter
+c4ticu
+ctim3i
+ctu4r
+c4tw
+cud5
+c4uf
+c4ui
+cu5ity
+5culi
+cul4tis
+3cultu
+cu2ma
+c3ume
+cu4mi
+3cun
+cu3pi
+cu5py
+cur5a4b
+cu5ria
+1cus
+cuss4i
+3c4ut
+cu4tie
+4c5utiv
+4cutr
+1cy
+cze4
+1d2a
+5da.
+2d3a4b
+dach4
+4daf
+2dag
+da2m2
+dan3g
+dard5
+dark5
+4dary
+3dat
+4dativ
+4dato
+5dav4
+dav5e
+5day
+d1b
+d5c
+d1d4
+2de.
+deaf5
+deb5it
+de4bon
+decan4
+de4cil
+de5com
+2d1ed
+4dee.
+de5if
+deli4e
+del5i5q
+de5lo
+d4em
+5dem.
+3demic
+dem5ic.
+de5mil
+de4mons
+demor5
+1den
+de4nar
+de3no
+denti5f
+de3nu
+de1p
+de3pa
+depi4
+de2pu
+d3eq
+d4erh
+5derm
+dern5iz
+der5s
+des2
+d2es.
+de1sc
+de2s5o
+des3ti
+de3str
+de4su
+de1t
+de2to
+de1v
+dev3il
+4dey
+4d1f
+d4ga
+d3ge4t
+dg1i
+d2gy
+d1h2
+5di.
+1d4i3a
+dia5b
+di4cam
+d4ice
+3dict
+3did
+5di3en
+d1if
+di3ge
+di4lato
+d1in
+1dina
+3dine.
+5dini
+di5niz
+1dio
+dio5g
+di4pl
+dir2
+di1re
+dirt5i
+dis1
+5disi
+d4is3t
+d2iti
+1di1v
+d1j
+d5k2
+4d5la
+3dle.
+3dled
+3dles.
+4dless
+2d3lo
+4d5lu
+2dly
+d1m
+4d1n4
+1do
+3do.
+do5de
+5doe
+2d5of
+d4og
+do4la
+doli4
+do5lor
+dom5iz
+do3nat
+doni4
+doo3d
+dop4p
+d4or
+3dos
+4d5out
+do4v
+3dox
+d1p
+1dr
+drag5on
+4drai
+dre4
+drea5r
+5dren
+dri4b
+dril4
+dro4p
+4drow
+5drupli
+4dry
+2d1s2
+ds4p
+d4sw
+d4sy
+d2th
+1du
+d1u1a
+du2c
+d1uca
+duc5er
+4duct.
+4ducts
+du5el
+du4g
+d3ule
+dum4be
+du4n
+4dup
+du4pe
+d1v
+d1w
+d2y
+5dyn
+dy4se
+dys5p
+e1a4b
+e3act
+ead1
+ead5ie
+ea4ge
+ea5ger
+ea4l
+eal5er
+eal3ou
+eam3er
+e5and
+ear3a
+ear4c
+ear5es
+ear4ic
+ear4il
+ear5k
+ear2t
+eart3e
+ea5sp
+e3ass
+east3
+ea2t
+eat5en
+eath3i
+e5atif
+e4a3tu
+ea2v
+eav3en
+eav5i
+eav5o
+2e1b
+e4bel.
+e4bels
+e4ben
+e4bit
+e3br
+e4cad
+ecan5c
+ecca5
+e1ce
+ec5essa
+ec2i
+e4cib
+ec5ificat
+ec5ifie
+ec5ify
+ec3im
+eci4t
+e5cite
+e4clam
+e4clus
+e2col
+e4comm
+e4compe
+e4conc
+e2cor
+ec3ora
+eco5ro
+e1cr
+e4crem
+ec4tan
+ec4te
+e1cu
+e4cul
+ec3ula
+2e2da
+4ed3d
+e4d1er
+ede4s
+4edi
+e3dia
+ed3ib
+ed3ica
+ed3im
+ed1it
+edi5z
+4edo
+e4dol
+edon2
+e4dri
+e4dul
+ed5ulo
+ee2c
+eed3i
+ee2f
+eel3i
+ee4ly
+ee2m
+ee4na
+ee4p1
+ee2s4
+eest4
+ee4ty
+e5ex
+e1f
+e4f3ere
+1eff
+e4fic
+5efici
+efil4
+e3fine
+ef5i5nite
+3efit
+efor5es
+e4fuse.
+4egal
+eger4
+eg5ib
+eg4ic
+eg5ing
+e5git5
+eg5n
+e4go.
+e4gos
+eg1ul
+e5gur
+5egy
+e1h4
+eher4
+ei2
+e5ic
+ei5d
+eig2
+ei5gl
+e3imb
+e3inf
+e1ing
+e5inst
+eir4d
+eit3e
+ei3th
+e5ity
+e1j
+e4jud
+ej5udi
+eki4n
+ek4la
+e1la
+e4la.
+e4lac
+elan4d
+el5ativ
+e4law
+elaxa4
+e3lea
+el5ebra
+5elec
+e4led
+el3ega
+e5len
+e4l1er
+e1les
+el2f
+el2i
+e3libe
+e4l5ic.
+el3ica
+e3lier
+el5igib
+e5lim
+e4l3ing
+e3lio
+e2lis
+el5ish
+e3liv3
+4ella
+el4lab
+ello4
+e5loc
+el5og
+el3op.
+el2sh
+el4ta
+e5lud
+el5ug
+e4mac
+e4mag
+e5man
+em5ana
+em5b
+e1me
+e2mel
+e4met
+em3ica
+emi4e
+em5igra
+em1in2
+em5ine
+em3i3ni
+e4mis
+em5ish
+e5miss
+em3iz
+5emniz
+emo4g
+emoni5o
+em3pi
+e4mul
+em5ula
+emu3n
+e3my
+en5amo
+e4nant
+ench4er
+en3dic
+e5nea
+e5nee
+en3em
+en5ero
+en5esi
+en5est
+en3etr
+e3new
+en5ics
+e5nie
+e5nil
+e3nio
+en3ish
+en3it
+e5niu
+5eniz
+4enn
+4eno
+eno4g
+e4nos
+en3ov
+en4sw
+ent5age
+4enthes
+en3ua
+en5uf
+e3ny.
+4en3z
+e5of
+eo2g
+e4oi4
+e3ol
+eop3ar
+e1or
+eo3re
+eo5rol
+eos4
+e4ot
+eo4to
+e5out
+e5ow
+e2pa
+e3pai
+ep5anc
+e5pel
+e3pent
+ep5etitio
+ephe4
+e4pli
+e1po
+e4prec
+ep5reca
+e4pred
+ep3reh
+e3pro
+e4prob
+ep4sh
+ep5ti5b
+e4put
+ep5uta
+e1q
+equi3l
+e4q3ui3s
+er1a
+era4b
+4erand
+er3ar
+4erati.
+2erb
+er4bl
+er3ch
+er4che
+2ere.
+e3real
+ere5co
+ere3in
+er5el.
+er3emo
+er5ena
+er5ence
+4erene
+er3ent
+ere4q
+er5ess
+er3est
+eret4
+er1h
+er1i
+e1ria4
+5erick
+e3rien
+eri4er
+er3ine
+e1rio
+4erit
+er4iu
+eri4v
+e4riva
+er3m4
+er4nis
+4ernit
+5erniz
+er3no
+2ero
+er5ob
+e5roc
+ero4r
+er1ou
+er1s
+er3set
+ert3er
+4ertl
+er3tw
+4eru
+eru4t
+5erwau
+e1s4a
+e4sage.
+e4sages
+es2c
+e2sca
+es5can
+e3scr
+es5cu
+e1s2e
+e2sec
+es5ecr
+es5enc
+e4sert.
+e4serts
+e4serva
+4esh
+e3sha
+esh5en
+e1si
+e2sic
+e2sid
+es5iden
+es5igna
+e2s5im
+es4i4n
+esis4te
+esi4u
+e5skin
+es4mi
+e2sol
+es3olu
+e2son
+es5ona
+e1sp
+es3per
+es5pira
+es4pre
+2ess
+es4si4b
+estan4
+es3tig
+es5tim
+4es2to
+e3ston
+2estr
+e5stro
+estruc5
+e2sur
+es5urr
+es4w
+eta4b
+eten4d
+e3teo
+ethod3
+et1ic
+e5tide
+etin4
+eti4no
+e5tir
+e5titio
+et5itiv
+4etn
+et5ona
+e3tra
+e3tre
+et3ric
+et5rif
+et3rog
+et5ros
+et3ua
+et5ym
+et5z
+4eu
+e5un
+e3up
+eu3ro
+eus4
+eute4
+euti5l
+eu5tr
+eva2p5
+e2vas
+ev5ast
+e5vea
+ev3ell
+evel3o
+e5veng
+even4i
+ev1er
+e5verb
+e1vi
+ev3id
+evi4l
+e4vin
+evi4v
+e5voc
+e5vu
+e1wa
+e4wag
+e5wee
+e3wh
+ewil5
+ew3ing
+e3wit
+1exp
+5eyc
+5eye.
+eys4
+1fa
+fa3bl
+fab3r
+fa4ce
+4fag
+fain4
+fall5e
+4fa4ma
+fam5is
+5far
+far5th
+fa3ta
+fa3the
+4fato
+fault5
+4f5b
+4fd
+4fe.
+feas4
+feath3
+fe4b
+4feca
+5fect
+2fed
+fe3li
+fe4mo
+fen2d
+fend5e
+fer1
+5ferr
+fev4
+4f1f
+f4fes
+f4fie
+f5fin.
+f2f5is
+f4fly
+f2fy
+4fh
+1fi
+fi3a
+2f3ic.
+4f3ical
+f3ican
+4ficate
+f3icen
+fi3cer
+fic4i
+5ficia
+5ficie
+4fics
+fi3cu
+fi5del
+fight5
+fil5i
+fill5in
+4fily
+2fin
+5fina
+fin2d5
+fi2ne
+f1in3g
+fin4n
+fis4ti
+f4l2
+f5less
+flin4
+flo3re
+f2ly5
+4fm
+4fn
+1fo
+5fon
+fon4de
+fon4t
+fo2r
+fo5rat
+for5ay
+fore5t
+for4i
+fort5a
+fos5
+4f5p
+fra4t
+f5rea
+fres5c
+fri2
+fril4
+frol5
+2f3s
+2ft
+f4to
+f2ty
+3fu
+fu5el
+4fug
+fu4min
+fu5ne
+fu3ri
+fusi4
+fus4s
+4futa
+1fy
+1ga
+gaf4
+5gal.
+3gali
+ga3lo
+2gam
+ga5met
+g5amo
+gan5is
+ga3niz
+gani5za
+4gano
+gar5n4
+gass4
+gath3
+4gativ
+4gaz
+g3b
+gd4
+2ge.
+2ged
+geez4
+gel4in
+ge5lis
+ge5liz
+4gely
+1gen
+ge4nat
+ge5niz
+4geno
+4geny
+1geo
+ge3om
+g4ery
+5gesi
+geth5
+4geto
+ge4ty
+ge4v
+4g1g2
+g2ge
+g3ger
+gglu5
+ggo4
+gh3in
+gh5out
+gh4to
+5gi.
+1gi4a
+gia5r
+g1ic
+5gicia
+g4ico
+gien5
+5gies.
+gil4
+g3imen
+3g4in.
+gin5ge
+5g4ins
+5gio
+3gir
+gir4l
+g3isl
+gi4u
+5giv
+3giz
+gl2
+gla4
+glad5i
+5glas
+1gle
+gli4b
+g3lig
+3glo
+glo3r
+g1m
+g4my
+gn4a
+g4na.
+gnet4t
+g1ni
+g2nin
+g4nio
+g1no
+g4non
+1go
+3go.
+gob5
+5goe
+3g4o4g
+go3is
+gon2
+4g3o3na
+gondo5
+go3ni
+5goo
+go5riz
+gor5ou
+5gos.
+gov1
+g3p
+1gr
+4grada
+g4rai
+gran2
+5graph.
+g5rapher
+5graphic
+4graphy
+4gray
+gre4n
+4gress.
+4grit
+g4ro
+gruf4
+gs2
+g5ste
+gth3
+gu4a
+3guard
+2gue
+5gui5t
+3gun
+3gus
+4gu4t
+g3w
+1gy
+2g5y3n
+gy5ra
+h3ab4l
+hach4
+hae4m
+hae4t
+h5agu
+ha3la
+hala3m
+ha4m
+han4ci
+han4cy
+5hand.
+han4g
+hang5er
+hang5o
+h5a5niz
+han4k
+han4te
+hap3l
+hap5t
+ha3ran
+ha5ras
+har2d
+hard3e
+har4le
+harp5en
+har5ter
+has5s
+haun4
+5haz
+haz3a
+h1b
+1head
+3hear
+he4can
+h5ecat
+h4ed
+he5do5
+he3l4i
+hel4lis
+hel4ly
+h5elo
+hem4p
+he2n
+hena4
+hen5at
+heo5r
+hep5
+h4era
+hera3p
+her4ba
+here5a
+h3ern
+h5erou
+h3ery
+h1es
+he2s5p
+he4t
+het4ed
+heu4
+h1f
+h1h
+hi5an
+hi4co
+high5
+h4il2
+himer4
+h4ina
+hion4e
+hi4p
+hir4l
+hi3ro
+hir4p
+hir4r
+his3el
+his4s
+hith5er
+hi2v
+4hk
+4h1l4
+hlan4
+h2lo
+hlo3ri
+4h1m
+hmet4
+2h1n
+h5odiz
+h5ods
+ho4g
+hoge4
+hol5ar
+3hol4e
+ho4ma
+home3
+hon4a
+ho5ny
+3hood
+hoon4
+hor5at
+ho5ris
+hort3e
+ho5ru
+hos4e
+ho5sen
+hos1p
+1hous
+house3
+hov5el
+4h5p
+4hr4
+hree5
+hro5niz
+hro3po
+4h1s2
+h4sh
+h4tar
+ht1en
+ht5es
+h4ty
+hu4g
+hu4min
+hun5ke
+hun4t
+hus3t4
+hu4t
+h1w
+h4wart
+hy3pe
+hy3ph
+hy2s
+2i1a
+i2al
+iam4
+iam5ete
+i2an
+4ianc
+ian3i
+4ian4t
+ia5pe
+iass4
+i4ativ
+ia4tric
+i4atu
+ibe4
+ib3era
+ib5ert
+ib5ia
+ib3in
+ib5it.
+ib5ite
+i1bl
+ib3li
+i5bo
+i1br
+i2b5ri
+i5bun
+4icam
+5icap
+4icar
+i4car.
+i4cara
+icas5
+i4cay
+iccu4
+4iceo
+4ich
+2ici
+i5cid
+ic5ina
+i2cip
+ic3ipa
+i4cly
+i2c5oc
+4i1cr
+5icra
+i4cry
+ic4te
+ictu2
+ic4t3ua
+ic3ula
+ic4um
+ic5uo
+i3cur
+2id
+i4dai
+id5anc
+id5d
+ide3al
+ide4s
+i2di
+id5ian
+idi4ar
+i5die
+id3io
+idi5ou
+id1it
+id5iu
+i3dle
+i4dom
+id3ow
+i4dr
+i2du
+id5uo
+2ie4
+ied4e
+5ie5ga
+ield3
+ien5a4
+ien4e
+i5enn
+i3enti
+i1er.
+i3esc
+i1est
+i3et
+4if.
+if5ero
+iff5en
+if4fr
+4ific.
+i3fie
+i3fl
+4ift
+2ig
+iga5b
+ig3era
+ight3i
+4igi
+i3gib
+ig3il
+ig3in
+ig3it
+i4g4l
+i2go
+ig3or
+ig5ot
+i5gre
+igu5i
+ig1ur
+i3h
+4i5i4
+i3j
+4ik
+i1la
+il3a4b
+i4lade
+i2l5am
+ila5ra
+i3leg
+il1er
+ilev4
+il5f
+il1i
+il3ia
+il2ib
+il3io
+il4ist
+2ilit
+il2iz
+ill5ab
+4iln
+il3oq
+il4ty
+il5ur
+il3v
+i4mag
+im3age
+ima5ry
+imenta5r
+4imet
+im1i
+im5ida
+imi5le
+i5mini
+4imit
+im4ni
+i3mon
+i2mu
+im3ula
+2in.
+i4n3au
+4inav
+incel4
+in3cer
+4ind
+in5dling
+2ine
+i3nee
+iner4ar
+i5ness
+4inga
+4inge
+in5gen
+4ingi
+in5gling
+4ingo
+4ingu
+2ini
+i5ni.
+i4nia
+in3io
+in1is
+i5nite.
+5initio
+in3ity
+4ink
+4inl
+2inn
+2i1no
+i4no4c
+ino4s
+i4not
+2ins
+in3se
+insur5a
+2int.
+2in4th
+in1u
+i5nus
+4iny
+2io
+4io.
+ioge4
+io2gr
+i1ol
+io4m
+ion3at
+ion4ery
+ion3i
+io5ph
+ior3i
+i4os
+io5th
+i5oti
+io4to
+i4our
+2ip
+ipe4
+iphras4
+ip3i
+ip4ic
+ip4re4
+ip3ul
+i3qua
+iq5uef
+iq3uid
+iq3ui3t
+4ir
+i1ra
+ira4b
+i4rac
+ird5e
+ire4de
+i4ref
+i4rel4
+i4res
+ir5gi
+ir1i
+iri5de
+ir4is
+iri3tu
+5i5r2iz
+ir4min
+iro4g
+5iron.
+ir5ul
+2is.
+is5ag
+is3ar
+isas5
+2is1c
+is3ch
+4ise
+is3er
+3isf
+is5han
+is3hon
+ish5op
+is3ib
+isi4d
+i5sis
+is5itiv
+4is4k
+islan4
+4isms
+i2so
+iso5mer
+is1p
+is2pi
+is4py
+4is1s
+is4sal
+issen4
+is4ses
+is4ta.
+is1te
+is1ti
+ist4ly
+4istral
+i2su
+is5us
+4ita.
+ita4bi
+i4tag
+4ita5m
+i3tan
+i3tat
+2ite
+it3era
+i5teri
+it4es
+2ith
+i1ti
+4itia
+4i2tic
+it3ica
+5i5tick
+it3ig
+it5ill
+i2tim
+2itio
+4itis
+i4tism
+i2t5o5m
+4iton
+i4tram
+it5ry
+4itt
+it3uat
+i5tud
+it3ul
+4itz.
+i1u
+2iv
+iv3ell
+iv3en.
+i4v3er.
+i4vers.
+iv5il.
+iv5io
+iv1it
+i5vore
+iv3o3ro
+i4v3ot
+4i5w
+ix4o
+4iy
+4izar
+izi4
+5izont
+5ja
+jac4q
+ja4p
+1je
+jer5s
+4jestie
+4jesty
+jew3
+jo4p
+5judg
+3ka.
+k3ab
+k5ag
+kais4
+kal4
+k1b
+k2ed
+1kee
+ke4g
+ke5li
+k3en4d
+k1er
+kes4
+k3est.
+ke4ty
+k3f
+kh4
+k1i
+5ki.
+5k2ic
+k4ill
+kilo5
+k4im
+k4in.
+kin4de
+k5iness
+kin4g
+ki4p
+kis4
+k5ish
+kk4
+k1l
+4kley
+4kly
+k1m
+k5nes
+1k2no
+ko5r
+kosh4
+k3ou
+kro5n
+4k1s2
+k4sc
+ks4l
+k4sy
+k5t
+k1w
+lab3ic
+l4abo
+laci4
+l4ade
+la3dy
+lag4n
+lam3o
+3land
+lan4dl
+lan5et
+lan4te
+lar4g
+lar3i
+las4e
+la5tan
+4lateli
+4lativ
+4lav
+la4v4a
+2l1b
+lbin4
+4l1c2
+lce4
+l3ci
+2ld
+l2de
+ld4ere
+ld4eri
+ldi4
+ld5is
+l3dr
+l4dri
+le2a
+le4bi
+left5
+5leg.
+5legg
+le4mat
+lem5atic
+4len.
+3lenc
+5lene.
+1lent
+le3ph
+le4pr
+lera5b
+ler4e
+3lerg
+3l4eri
+l4ero
+les2
+le5sco
+5lesq
+3less
+5less.
+l3eva
+lev4er.
+lev4era
+lev4ers
+3ley
+4leye
+2lf
+l5fr
+4l1g4
+l5ga
+lgar3
+l4ges
+lgo3
+2l3h
+li4ag
+li2am
+liar5iz
+li4as
+li4ato
+li5bi
+5licio
+li4cor
+4lics
+4lict.
+l4icu
+l3icy
+l3ida
+lid5er
+3lidi
+lif3er
+l4iff
+li4fl
+5ligate
+3ligh
+li4gra
+3lik
+4l4i4l
+lim4bl
+lim3i
+li4mo
+l4im4p
+l4ina
+1l4ine
+lin3ea
+lin3i
+link5er
+li5og
+4l4iq
+lis4p
+l1it
+l2it.
+5litica
+l5i5tics
+liv3er
+l1iz
+4lj
+lka3
+l3kal
+lka4t
+l1l
+l4law
+l2le
+l5lea
+l3lec
+l3leg
+l3lel
+l3le4n
+l3le4t
+ll2i
+l2lin4
+l5lina
+ll4o
+lloqui5
+ll5out
+l5low
+2lm
+l5met
+lm3ing
+l4mod
+lmon4
+2l1n2
+3lo.
+lob5al
+lo4ci
+4lof
+3logic
+l5ogo
+3logu
+lom3er
+5long
+lon4i
+l3o3niz
+lood5
+5lope.
+lop3i
+l3opm
+lora4
+lo4rato
+lo5rie
+lor5ou
+5los.
+los5et
+5losophiz
+5losophy
+los4t
+lo4ta
+loun5d
+2lout
+4lov
+2lp
+lpa5b
+l3pha
+l5phi
+lp5ing
+l3pit
+l4pl
+l5pr
+4l1r
+2l1s2
+l4sc
+l2se
+l4sie
+4lt
+lt5ag
+ltane5
+l1te
+lten4
+ltera4
+lth3i
+l5ties.
+ltis4
+l1tr
+ltu2
+ltur3a
+lu5a
+lu3br
+luch4
+lu3ci
+lu3en
+luf4
+lu5id
+lu4ma
+5lumi
+l5umn.
+5lumnia
+lu3o
+luo3r
+4lup
+luss4
+lus3te
+1lut
+l5ven
+l5vet4
+2l1w
+1ly
+4lya
+4lyb
+ly5me
+ly3no
+2lys4
+l5yse
+1ma
+2mab
+ma2ca
+ma5chine
+ma4cl
+mag5in
+5magn
+2mah
+maid5
+4mald
+ma3lig
+ma5lin
+mal4li
+mal4ty
+5mania
+man5is
+man3iz
+4map
+ma5rine.
+ma5riz
+mar4ly
+mar3v
+ma5sce
+mas4e
+mas1t
+5mate
+math3
+ma3tis
+4matiza
+4m1b
+mba4t5
+m5bil
+m4b3ing
+mbi4v
+4m5c
+4me.
+2med
+4med.
+5media
+me3die
+m5e5dy
+me2g
+mel5on
+mel4t
+me2m
+mem1o3
+1men
+men4a
+men5ac
+men4de
+4mene
+men4i
+mens4
+mensu5
+3ment
+men4te
+me5on
+m5ersa
+2mes
+3mesti
+me4ta
+met3al
+me1te
+me5thi
+m4etr
+5metric
+me5trie
+me3try
+me4v
+4m1f
+2mh
+5mi.
+mi3a
+mid4a
+mid4g
+mig4
+3milia
+m5i5lie
+m4ill
+min4a
+3mind
+m5inee
+m4ingl
+min5gli
+m5ingly
+min4t
+m4inu
+miot4
+m2is
+mis4er.
+mis5l
+mis4ti
+m5istry
+4mith
+m2iz
+4mk
+4m1l
+m1m
+mma5ry
+4m1n
+mn4a
+m4nin
+mn4o
+1mo
+4mocr
+5mocratiz
+mo2d1
+mo4go
+mois2
+moi5se
+4mok
+mo5lest
+mo3me
+mon5et
+mon5ge
+moni3a
+mon4ism
+mon4ist
+mo3niz
+monol4
+mo3ny.
+mo2r
+4mora.
+mos2
+mo5sey
+mo3sp
+moth3
+m5ouf
+3mous
+mo2v
+4m1p
+mpara5
+mpa5rab
+mpar5i
+m3pet
+mphas4
+m2pi
+mpi4a
+mp5ies
+m4p1in
+m5pir
+mp5is
+mpo3ri
+mpos5ite
+m4pous
+mpov5
+mp4tr
+m2py
+4m3r
+4m1s2
+m4sh
+m5si
+4mt
+1mu
+mula5r4
+5mult
+multi3
+3mum
+mun2
+4mup
+mu4u
+4mw
+1na
+2n1a2b
+n4abu
+4nac.
+na4ca
+n5act
+nag5er.
+nak4
+na4li
+na5lia
+4nalt
+na5mit
+n2an
+nanci4
+nan4it
+nank4
+nar3c
+4nare
+nar3i
+nar4l
+n5arm
+n4as
+nas4c
+nas5ti
+n2at
+na3tal
+nato5miz
+n2au
+nau3se
+3naut
+nav4e
+4n1b4
+ncar5
+n4ces.
+n3cha
+n5cheo
+n5chil
+n3chis
+nc1in
+nc4it
+ncour5a
+n1cr
+n1cu
+n4dai
+n5dan
+n1de
+nd5est.
+ndi4b
+n5d2if
+n1dit
+n3diz
+n5duc
+ndu4r
+nd2we
+2ne.
+n3ear
+ne2b
+neb3u
+ne2c
+5neck
+2ned
+ne4gat
+neg5ativ
+5nege
+ne4la
+nel5iz
+ne5mi
+ne4mo
+1nen
+4nene
+3neo
+ne4po
+ne2q
+n1er
+nera5b
+n4erar
+n2ere
+n4er5i
+ner4r
+1nes
+2nes.
+4nesp
+2nest
+4nesw
+3netic
+ne4v
+n5eve
+ne4w
+n3f
+n4gab
+n3gel
+nge4n4e
+n5gere
+n3geri
+ng5ha
+n3gib
+ng1in
+n5git
+n4gla
+ngov4
+ng5sh
+n1gu
+n4gum
+n2gy
+4n1h4
+nha4
+nhab3
+nhe4
+3n4ia
+ni3an
+ni4ap
+ni3ba
+ni4bl
+ni4d
+ni5di
+ni4er
+ni2fi
+ni5ficat
+n5igr
+nik4
+n1im
+ni3miz
+n1in
+5nine.
+nin4g
+ni4o
+5nis.
+nis4ta
+n2it
+n4ith
+3nitio
+n3itor
+ni3tr
+n1j
+4nk2
+n5kero
+n3ket
+nk3in
+n1kl
+4n1l
+n5m
+nme4
+nmet4
+4n1n2
+nne4
+nni3al
+nni4v
+nob4l
+no3ble
+n5ocl
+4n3o2d
+3noe
+4nog
+noge4
+nois5i
+no5l4i
+5nologis
+3nomic
+n5o5miz
+no4mo
+no3my
+no4n
+non4ag
+non5i
+n5oniz
+4nop
+5nop5o5li
+nor5ab
+no4rary
+4nosc
+nos4e
+nos5t
+no5ta
+1nou
+3noun
+nov3el3
+nowl3
+n1p4
+npi4
+npre4c
+n1q
+n1r
+nru4
+2n1s2
+ns5ab
+nsati4
+ns4c
+n2se
+n4s3es
+nsid1
+nsig4
+n2sl
+ns3m
+n4soc
+ns4pe
+n5spi
+nsta5bl
+n1t
+nta4b
+nter3s
+nt2i
+n5tib
+nti4er
+nti2f
+n3tine
+n4t3ing
+nti4p
+ntrol5li
+nt4s
+ntu3me
+nu1a
+nu4d
+nu5en
+nuf4fe
+n3uin
+3nu3it
+n4um
+nu1me
+n5umi
+3nu4n
+n3uo
+nu3tr
+n1v2
+n1w4
+nym4
+nyp4
+4nz
+n3za
+4oa
+oad3
+o5a5les
+oard3
+oas4e
+oast5e
+oat5i
+ob3a3b
+o5bar
+obe4l
+o1bi
+o2bin
+ob5ing
+o3br
+ob3ul
+o1ce
+och4
+o3chet
+ocif3
+o4cil
+o4clam
+o4cod
+oc3rac
+oc5ratiz
+ocre3
+5ocrit
+octor5a
+oc3ula
+o5cure
+od5ded
+od3ic
+odi3o
+o2do4
+odor3
+od5uct.
+od5ucts
+o4el
+o5eng
+o3er
+oe4ta
+o3ev
+o2fi
+of5ite
+ofit4t
+o2g5a5r
+og5ativ
+o4gato
+o1ge
+o5gene
+o5geo
+o4ger
+o3gie
+1o1gis
+og3it
+o4gl
+o5g2ly
+3ogniz
+o4gro
+ogu5i
+1ogy
+2ogyn
+o1h2
+ohab5
+oi2
+oic3es
+oi3der
+oiff4
+oig4
+oi5let
+o3ing
+oint5er
+o5ism
+oi5son
+oist5en
+oi3ter
+o5j
+2ok
+o3ken
+ok5ie
+o1la
+o4lan
+olass4
+ol2d
+old1e
+ol3er
+o3lesc
+o3let
+ol4fi
+ol2i
+o3lia
+o3lice
+ol5id.
+o3li4f
+o5lil
+ol3ing
+o5lio
+o5lis.
+ol3ish
+o5lite
+o5litio
+o5liv
+olli4e
+ol5ogiz
+olo4r
+ol5pl
+ol2t
+ol3ub
+ol3ume
+ol3un
+o5lus
+ol2v
+o2ly
+om5ah
+oma5l
+om5atiz
+om2be
+om4bl
+o2me
+om3ena
+om5erse
+o4met
+om5etry
+o3mia
+om3ic.
+om3ica
+o5mid
+om1in
+o5mini
+5ommend
+omo4ge
+o4mon
+om3pi
+ompro5
+o2n
+on1a
+on4ac
+o3nan
+on1c
+3oncil
+2ond
+on5do
+o3nen
+on5est
+on4gu
+on1ic
+o3nio
+on1is
+o5niu
+on3key
+on4odi
+on3omy
+on3s
+onspi4
+onspir5a
+onsu4
+onten4
+on3t4i
+ontif5
+on5um
+onva5
+oo2
+ood5e
+ood5i
+oo4k
+oop3i
+o3ord
+oost5
+o2pa
+ope5d
+op1er
+3opera
+4operag
+2oph
+o5phan
+o5pher
+op3ing
+o3pit
+o5pon
+o4posi
+o1pr
+op1u
+opy5
+o1q
+o1ra
+o5ra.
+o4r3ag
+or5aliz
+or5ange
+ore5a
+o5real
+or3ei
+ore5sh
+or5est.
+orew4
+or4gu
+4o5ria
+or3ica
+o5ril
+or1in
+o1rio
+or3ity
+o3riu
+or2mi
+orn2e
+o5rof
+or3oug
+or5pe
+3orrh
+or4se
+ors5en
+orst4
+or3thi
+or3thy
+or4ty
+o5rum
+o1ry
+os3al
+os2c
+os4ce
+o3scop
+4oscopi
+o5scr
+os4i4e
+os5itiv
+os3ito
+os3ity
+osi4u
+os4l
+o2so
+os4pa
+os4po
+os2ta
+o5stati
+os5til
+os5tit
+o4tan
+otele4g
+ot3er.
+ot5ers
+o4tes
+4oth
+oth5esi
+oth3i4
+ot3ic.
+ot5ica
+o3tice
+o3tif
+o3tis
+oto5s
+ou2
+ou3bl
+ouch5i
+ou5et
+ou4l
+ounc5er
+oun2d
+ou5v
+ov4en
+over4ne
+over3s
+ov4ert
+o3vis
+oviti4
+o5v4ol
+ow3der
+ow3el
+ow5est
+ow1i
+own5i
+o4wo
+oy1a
+1pa
+pa4ca
+pa4ce
+pac4t
+p4ad
+5pagan
+p3agat
+p4ai
+pain4
+p4al
+pan4a
+pan3el
+pan4ty
+pa3ny
+pa1p
+pa4pu
+para5bl
+par5age
+par5di
+3pare
+par5el
+p4a4ri
+par4is
+pa2te
+pa5ter
+5pathic
+pa5thy
+pa4tric
+pav4
+3pay
+4p1b
+pd4
+4pe.
+3pe4a
+pear4l
+pe2c
+2p2ed
+3pede
+3pedi
+pedia4
+ped4ic
+p4ee
+pee4d
+pek4
+pe4la
+peli4e
+pe4nan
+p4enc
+pen4th
+pe5on
+p4era.
+pera5bl
+p4erag
+p4eri
+peri5st
+per4mal
+perme5
+p4ern
+per3o
+per3ti
+pe5ru
+per1v
+pe2t
+pe5ten
+pe5tiz
+4pf
+4pg
+4ph.
+phar5i
+phe3no
+ph4er
+ph4es.
+ph1ic
+5phie
+ph5ing
+5phisti
+3phiz
+ph2l
+3phob
+3phone
+5phoni
+pho4r
+4phs
+ph3t
+5phu
+1phy
+pi3a
+pian4
+pi4cie
+pi4cy
+p4id
+p5ida
+pi3de
+5pidi
+3piec
+pi3en
+pi4grap
+pi3lo
+pi2n
+p4in.
+pind4
+p4ino
+3pi1o
+pion4
+p3ith
+pi5tha
+pi2tu
+2p3k2
+1p2l2
+3plan
+plas5t
+pli3a
+pli5er
+4plig
+pli4n
+ploi4
+plu4m
+plum4b
+4p1m
+2p3n
+po4c
+5pod.
+po5em
+po3et5
+5po4g
+poin2
+5point
+poly5t
+po4ni
+po4p
+1p4or
+po4ry
+1pos
+pos1s
+p4ot
+po4ta
+5poun
+4p1p
+ppa5ra
+p2pe
+p4ped
+p5pel
+p3pen
+p3per
+p3pet
+ppo5site
+pr2
+pray4e
+5preci
+pre5co
+pre3em
+pref5ac
+pre4la
+pre3r
+p3rese
+3press
+pre5ten
+pre3v
+5pri4e
+prin4t3
+pri4s
+pris3o
+p3roca
+prof5it
+pro3l
+pros3e
+pro1t
+2p1s2
+p2se
+ps4h
+p4sib
+2p1t
+pt5a4b
+p2te
+p2th
+pti3m
+ptu4r
+p4tw
+pub3
+pue4
+puf4
+pul3c
+pu4m
+pu2n
+pur4r
+5pus
+pu2t
+5pute
+put3er
+pu3tr
+put4ted
+put4tin
+p3w
+qu2
+qua5v
+2que.
+3quer
+3quet
+2rab
+ra3bi
+rach4e
+r5acl
+raf5fi
+raf4t
+r2ai
+ra4lo
+ram3et
+r2ami
+rane5o
+ran4ge
+r4ani
+ra5no
+rap3er
+3raphy
+rar5c
+rare4
+rar5ef
+4raril
+r2as
+ration4
+rau4t
+ra5vai
+rav3el
+ra5zie
+r1b
+r4bab
+r4bag
+rbi2
+rbi4f
+r2bin
+r5bine
+rb5ing.
+rb4o
+r1c
+r2ce
+rcen4
+r3cha
+rch4er
+r4ci4b
+rc4it
+rcum3
+r4dal
+rd2i
+rdi4a
+rdi4er
+rdin4
+rd3ing
+2re.
+re1al
+re3an
+re5arr
+5reav
+re4aw
+r5ebrat
+rec5oll
+rec5ompe
+re4cre
+2r2ed
+re1de
+re3dis
+red5it
+re4fac
+re2fe
+re5fer.
+re3fi
+re4fy
+reg3is
+re5it
+re1li
+re5lu
+r4en4ta
+ren4te
+re1o
+re5pin
+re4posi
+re1pu
+r1er4
+r4eri
+rero4
+re5ru
+r4es.
+re4spi
+ress5ib
+res2t
+re5stal
+re3str
+re4ter
+re4ti4z
+re3tri
+reu2
+re5uti
+rev2
+re4val
+rev3el
+r5ev5er.
+re5vers
+re5vert
+re5vil
+rev5olu
+re4wh
+r1f
+rfu4
+r4fy
+rg2
+rg3er
+r3get
+r3gic
+rgi4n
+rg3ing
+r5gis
+r5git
+r1gl
+rgo4n
+r3gu
+rh4
+4rh.
+4rhal
+ri3a
+ria4b
+ri4ag
+r4ib
+rib3a
+ric5as
+r4ice
+4rici
+5ricid
+ri4cie
+r4ico
+rid5er
+ri3enc
+ri3ent
+ri1er
+ri5et
+rig5an
+5rigi
+ril3iz
+5riman
+rim5i
+3rimo
+rim4pe
+r2ina
+5rina.
+rin4d
+rin4e
+rin4g
+ri1o
+5riph
+riph5e
+ri2pl
+rip5lic
+r4iq
+r2is
+r4is.
+ris4c
+r3ish
+ris4p
+ri3ta3b
+r5ited.
+rit5er.
+rit5ers
+rit3ic
+ri2tu
+rit5ur
+riv5el
+riv3et
+riv3i
+r3j
+r3ket
+rk4le
+rk4lin
+r1l
+rle4
+r2led
+r4lig
+r4lis
+rl5ish
+r3lo4
+r1m
+rma5c
+r2me
+r3men
+rm5ers
+rm3ing
+r4ming.
+r4mio
+r3mit
+r4my
+r4nar
+r3nel
+r4ner
+r5net
+r3ney
+r5nic
+r1nis4
+r3nit
+r3niv
+rno4
+r4nou
+r3nu
+rob3l
+r2oc
+ro3cr
+ro4e
+ro1fe
+ro5fil
+rok2
+ro5ker
+5role.
+rom5ete
+rom4i
+rom4p
+ron4al
+ron4e
+ro5n4is
+ron4ta
+1room
+5root
+ro3pel
+rop3ic
+ror3i
+ro5ro
+ros5per
+ros4s
+ro4the
+ro4ty
+ro4va
+rov5el
+rox5
+r1p
+r4pea
+r5pent
+rp5er.
+r3pet
+rp4h4
+rp3ing
+r3po
+r1r4
+rre4c
+rre4f
+r4reo
+rre4st
+rri4o
+rri4v
+rron4
+rros4
+rrys4
+4rs2
+r1sa
+rsa5ti
+rs4c
+r2se
+r3sec
+rse4cr
+rs5er.
+rs3es
+rse5v2
+r1sh
+r5sha
+r1si
+r4si4b
+rson3
+r1sp
+r5sw
+rtach4
+r4tag
+r3teb
+rten4d
+rte5o
+r1ti
+rt5ib
+rti4d
+r4tier
+r3tig
+rtil3i
+rtil4l
+r4tily
+r4tist
+r4tiv
+r3tri
+rtroph4
+rt4sh
+ru3a
+ru3e4l
+ru3en
+ru4gl
+ru3in
+rum3pl
+ru2n
+runk5
+run4ty
+r5usc
+ruti5n
+rv4e
+rvel4i
+r3ven
+rv5er.
+r5vest
+r3vey
+r3vic
+rvi4v
+r3vo
+r1w
+ry4c
+5rynge
+ry3t
+sa2
+2s1ab
+5sack
+sac3ri
+s3act
+5sai
+salar4
+sal4m
+sa5lo
+sal4t
+3sanc
+san4de
+s1ap
+sa5ta
+5sa3tio
+sat3u
+sau4
+sa5vor
+5saw
+4s5b
+scan4t5
+sca4p
+scav5
+s4ced
+4scei
+s4ces
+sch2
+s4cho
+3s4cie
+5scin4d
+scle5
+s4cli
+scof4
+4scopy
+scour5a
+s1cu
+4s5d
+4se.
+se4a
+seas4
+sea5w
+se2c3o
+3sect
+4s4ed
+se4d4e
+s5edl
+se2g
+seg3r
+5sei
+se1le
+5self
+5selv
+4seme
+se4mol
+sen5at
+4senc
+sen4d
+s5ened
+sen5g
+s5enin
+4sentd
+4sentl
+sep3a3
+4s1er.
+s4erl
+ser4o
+4servo
+s1e4s
+se5sh
+ses5t
+5se5um
+5sev
+sev3en
+sew4i
+5sex
+4s3f
+2s3g
+s2h
+2sh.
+sh1er
+5shev
+sh1in
+sh3io
+3ship
+shiv5
+sho4
+sh5old
+shon3
+shor4
+short5
+4shw
+si1b
+s5icc
+3side.
+5sides
+5sidi
+si5diz
+4signa
+sil4e
+4sily
+2s1in
+s2ina
+5sine.
+s3ing
+1sio
+5sion
+sion5a
+si2r
+sir5a
+1sis
+3sitio
+5siu
+1siv
+5siz
+sk2
+4ske
+s3ket
+sk5ine
+sk5ing
+s1l2
+s3lat
+s2le
+slith5
+2s1m
+s3ma
+small3
+sman3
+smel4
+s5men
+5smith
+smol5d4
+s1n4
+1so
+so4ce
+soft3
+so4lab
+sol3d2
+so3lic
+5solv
+3som
+3s4on.
+sona4
+son4g
+s4op
+5sophic
+s5ophiz
+s5ophy
+sor5c
+sor5d
+4sov
+so5vi
+2spa
+5spai
+spa4n
+spen4d
+2s5peo
+2sper
+s2phe
+3spher
+spho5
+spil4
+sp5ing
+4spio
+s4ply
+s4pon
+spor4
+4spot
+squal4l
+s1r
+2ss
+s1sa
+ssas3
+s2s5c
+s3sel
+s5seng
+s4ses.
+s5set
+s1si
+s4sie
+ssi4er
+ss5ily
+s4sl
+ss4li
+s4sn
+sspend4
+ss2t
+ssur5a
+ss5w
+2st.
+s2tag
+s2tal
+stam4i
+5stand
+s4ta4p
+5stat.
+s4ted
+stern5i
+s5tero
+ste2w
+stew5a
+s3the
+st2i
+s4ti.
+s5tia
+s1tic
+5stick
+s4tie
+s3tif
+st3ing
+5stir
+s1tle
+5stock
+stom3a
+5stone
+s4top
+3store
+st4r
+s4trad
+5stratu
+s4tray
+s4trid
+4stry
+4st3w
+s2ty
+1su
+su1al
+su4b3
+su2g3
+su5is
+suit3
+s4ul
+su2m
+sum3i
+su2n
+su2r
+4sv
+sw2
+4swo
+s4y
+4syc
+3syl
+syn5o
+sy5rin
+1ta
+3ta.
+2tab
+ta5bles
+5taboliz
+4taci
+ta5do
+4taf4
+tai5lo
+ta2l
+ta5la
+tal5en
+tal3i
+4talk
+tal4lis
+ta5log
+ta5mo
+tan4de
+tanta3
+ta5per
+ta5pl
+tar4a
+4tarc
+4tare
+ta3riz
+tas4e
+ta5sy
+4tatic
+ta4tur
+taun4
+tav4
+2taw
+tax4is
+2t1b
+4tc
+t4ch
+tch5et
+4t1d
+4te.
+tead4i
+4teat
+tece4
+5tect
+2t1ed
+te5di
+1tee
+teg4
+te5ger
+te5gi
+3tel.
+teli4
+5tels
+te2ma2
+tem3at
+3tenan
+3tenc
+3tend
+4tenes
+1tent
+ten4tag
+1teo
+te4p
+te5pe
+ter3c
+5ter3d
+1teri
+ter5ies
+ter3is
+teri5za
+5ternit
+ter5v
+4tes.
+4tess
+t3ess.
+teth5e
+3teu
+3tex
+4tey
+2t1f
+4t1g
+2th.
+than4
+th2e
+4thea
+th3eas
+the5at
+the3is
+3thet
+th5ic.
+th5ica
+4thil
+5think
+4thl
+th5ode
+5thodic
+4thoo
+thor5it
+tho5riz
+2ths
+1tia
+ti4ab
+ti4ato
+2ti2b
+4tick
+t4ico
+t4ic1u
+5tidi
+3tien
+tif2
+ti5fy
+2tig
+5tigu
+till5in
+1tim
+4timp
+tim5ul
+2t1in
+t2ina
+3tine.
+3tini
+1tio
+ti5oc
+tion5ee
+5tiq
+ti3sa
+3tise
+tis4m
+ti5so
+tis4p
+5tistica
+ti3tl
+ti4u
+1tiv
+tiv4a
+1tiz
+ti3za
+ti3zen
+2tl
+t5la
+tlan4
+3tle.
+3tled
+3tles.
+t5let.
+t5lo
+4t1m
+tme4
+2t1n2
+1to
+to3b
+to5crat
+4todo
+2tof
+to2gr
+to5ic
+to2ma
+tom4b
+to3my
+ton4ali
+to3nat
+4tono
+4tony
+to2ra
+to3rie
+tor5iz
+tos2
+5tour
+4tout
+to3war
+4t1p
+1tra
+tra3b
+tra5ch
+traci4
+trac4it
+trac4te
+tras4
+tra5ven
+trav5es5
+tre5f
+tre4m
+trem5i
+5tria
+tri5ces
+5tricia
+4trics
+2trim
+tri4v
+tro5mi
+tron5i
+4trony
+tro5phe
+tro3sp
+tro3v
+tru5i
+trus4
+4t1s2
+t4sc
+tsh4
+t4sw
+4t3t2
+t4tes
+t5to
+ttu4
+1tu
+tu1a
+tu3ar
+tu4bi
+tud2
+4tue
+4tuf4
+5tu3i
+3tum
+tu4nis
+2t3up.
+3ture
+5turi
+tur3is
+tur5o
+tu5ry
+3tus
+4tv
+tw4
+4t1wa
+twis4
+4two
+1ty
+4tya
+2tyl
+type3
+ty5ph
+4tz
+tz4e
+4uab
+uac4
+ua5na
+uan4i
+uar5ant
+uar2d
+uar3i
+uar3t
+u1at
+uav4
+ub4e
+u4bel
+u3ber
+u4bero
+u1b4i
+u4b5ing
+u3ble.
+u3ca
+uci4b
+uc4it
+ucle3
+u3cr
+u3cu
+u4cy
+ud5d
+ud3er
+ud5est
+udev4
+u1dic
+ud3ied
+ud3ies
+ud5is
+u5dit
+u4don
+ud4si
+u4du
+u4ene
+uens4
+uen4te
+uer4il
+3ufa
+u3fl
+ugh3en
+ug5in
+2ui2
+uil5iz
+ui4n
+u1ing
+uir4m
+uita4
+uiv3
+uiv4er.
+u5j
+4uk
+u1la
+ula5b
+u5lati
+ulch4
+5ulche
+ul3der
+ul4e
+u1len
+ul4gi
+ul2i
+u5lia
+ul3ing
+ul5ish
+ul4lar
+ul4li4b
+ul4lis
+4ul3m
+u1l4o
+4uls
+uls5es
+ul1ti
+ultra3
+4ultu
+u3lu
+ul5ul
+ul5v
+um5ab
+um4bi
+um4bly
+u1mi
+u4m3ing
+umor5o
+um2p
+unat4
+u2ne
+un4er
+u1ni
+un4im
+u2nin
+un5ish
+uni3v
+un3s4
+un4sw
+unt3ab
+un4ter.
+un4tes
+unu4
+un5y
+un5z
+u4ors
+u5os
+u1ou
+u1pe
+uper5s
+u5pia
+up3ing
+u3pl
+up3p
+upport5
+upt5ib
+uptu4
+u1ra
+4ura.
+u4rag
+u4ras
+ur4be
+urc4
+ur1d
+ure5at
+ur4fer
+ur4fr
+u3rif
+uri4fic
+ur1in
+u3rio
+u1rit
+ur3iz
+ur2l
+url5ing.
+ur4no
+uros4
+ur4pe
+ur4pi
+urs5er
+ur5tes
+ur3the
+urti4
+ur4tie
+u3ru
+2us
+u5sad
+u5san
+us4ap
+usc2
+us3ci
+use5a
+u5sia
+u3sic
+us4lin
+us1p
+us5sl
+us5tere
+us1tr
+u2su
+usur4
+uta4b
+u3tat
+4ute.
+4utel
+4uten
+uten4i
+4u1t2i
+uti5liz
+u3tine
+ut3ing
+ution5a
+u4tis
+5u5tiz
+u4t1l
+ut5of
+uto5g
+uto5matic
+u5ton
+u4tou
+uts4
+u3u
+uu4m
+u1v2
+uxu3
+uz4e
+1va
+5va.
+2v1a4b
+vac5il
+vac3u
+vag4
+va4ge
+va5lie
+val5o
+val1u
+va5mo
+va5niz
+va5pi
+var5ied
+3vat
+4ve.
+4ved
+veg3
+v3el.
+vel3li
+ve4lo
+v4ely
+ven3om
+v5enue
+v4erd
+5vere.
+v4erel
+v3eren
+ver5enc
+v4eres
+ver3ie
+vermi4n
+3verse
+ver3th
+v4e2s
+4ves.
+ves4te
+ve4te
+vet3er
+ve4ty
+vi5ali
+5vian
+5vide.
+5vided
+4v3iden
+5vides
+5vidi
+v3if
+vi5gn
+vik4
+2vil
+5vilit
+v3i3liz
+v1in
+4vi4na
+v2inc
+vin5d
+4ving
+vio3l
+v3io4r
+vi1ou
+vi4p
+vi5ro
+vis3it
+vi3so
+vi3su
+4viti
+vit3r
+4vity
+3viv
+5vo.
+voi4
+3vok
+vo4la
+v5ole
+5volt
+3volv
+vom5i
+vor5ab
+vori4
+vo4ry
+vo4ta
+4votee
+4vv4
+v4y
+w5abl
+2wac
+wa5ger
+wag5o
+wait5
+w5al.
+wam4
+war4t
+was4t
+wa1te
+wa5ver
+w1b
+wea5rie
+weath3
+wed4n
+weet3
+wee5v
+wel4l
+w1er
+west3
+w3ev
+whi4
+wi2
+wil2
+will5in
+win4de
+win4g
+wir4
+3wise
+with3
+wiz5
+w4k
+wl4es
+wl3in
+w4no
+1wo2
+wom1
+wo5ven
+w5p
+wra4
+wri4
+writa4
+w3sh
+ws4l
+ws4pe
+w5s4t
+4wt
+wy4
+x1a
+xac5e
+x4ago
+xam3
+x4ap
+xas5
+x3c2
+x1e
+xe4cuto
+x2ed
+xer4i
+xe5ro
+x1h
+xhi2
+xhil5
+xhu4
+x3i
+xi5a
+xi5c
+xi5di
+x4ime
+xi5miz
+x3o
+x4ob
+x3p
+xpan4d
+xpecto5
+xpe3d
+x1t2
+x3ti
+x1u
+xu3a
+xx4
+y5ac
+3yar4
+y5at
+y1b
+y1c
+y2ce
+yc5er
+y3ch
+ych4e
+ycom4
+ycot4
+y1d
+y5ee
+y1er
+y4erf
+yes4
+ye4t
+y5gi
+4y3h
+y1i
+y3la
+ylla5bl
+y3lo
+y5lu
+ymbol5
+yme4
+ympa3
+yn3chr
+yn5d
+yn5g
+yn5ic
+5ynx
+y1o4
+yo5d
+y4o5g
+yom4
+yo5net
+y4ons
+y4os
+y4ped
+yper5
+yp3i
+y3po
+y4poc
+yp2ta
+y5pu
+yra5m
+yr5ia
+y3ro
+yr4r
+ys4c
+y3s2e
+ys3ica
+ys3io
+3ysis
+y4so
+yss4
+ys1t
+ys3ta
+ysur4
+y3thin
+yt3ic
+y1w
+za1
+z5a2b
+zar2
+4zb
+2ze
+ze4n
+ze4p
+z1er
+ze3ro
+zet4
+2z1i
+z4il
+z4is
+5zl
+4zm
+1zo
+zo4m
+zo5ol
+zte4
+4z1z2
+z4zy
diff --git a/troff/input.c b/troff/input.c
new file mode 100644
index 000000000..ef8a6a230
--- /dev/null
+++ b/troff/input.c
@@ -0,0 +1,5898 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#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 "font.h"
+
+#define MACROPATH_ENVVAR "GROFF_TMAC_PATH"
+
+#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)
+#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();
+void copy_file();
+#ifdef COLUMN
+void vjustify();
+#endif /* COLUMN */
+void transparent();
+void transparent_file();
+
+const char *program_name = 0;
+token tok;
+const char *device = DEVICE;
+int break_flag = 0;
+static int backtrace_flag = 0;
+char *pipe_command = 0;
+charinfo *charset_table[256];
+
+static int warning_mask = DEFAULT_WARNING_MASK;
+static int inhibit_errors = 0;
+
+static void enable_warning(const char *);
+static void disable_warning(const char *);
+
+static int escape_char = '\\';
+static symbol end_macro_name;
+static int compatible_flag = 0;
+static void process_input_stack();
+int ascii_output_flag = 0;
+int suppress_output_flag = 0;
+
+int tcommand_flag = 0;
+
+static int get_copy(node**, int = 0);
+static symbol read_escape_name();
+static void interpolate_string(symbol);
+static void interpolate_macro(symbol);
+static void interpolate_number_format(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();
+
+struct input_iterator;
+input_iterator *make_temp_iterator(const char *);
+const char *input_char_description(int);
+
+const int ESCAPE_RIGHT_PARENTHESIS = 014;
+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;
+
+void set_escape_char()
+{
+ if (!has_arg())
+ escape_char = '\\';
+ else if (!tok.ch())
+ error("bad escape character");
+ else
+ escape_char = tok.ch();
+ skip_line();
+}
+
+void escape_off()
+{
+ escape_char = 0;
+ 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 n) {}
+ virtual int is_boundary();
+ virtual int internal_level() { 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;
+}
+
+int input_iterator::is_boundary()
+{
+ return 0;
+}
+
+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 file_iterator : public input_iterator {
+ FILE *fp;
+ int lineno;
+ const char *filename;
+ int newline_flag;
+ enum { BUF_SIZE = 512 };
+ unsigned char buf[BUF_SIZE];
+public:
+ file_iterator(FILE *, const char *);
+ ~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 *);
+};
+
+file_iterator::file_iterator(FILE *f, const char *fn)
+: fp(f), filename(fn), lineno(1), newline_flag(0)
+{
+}
+
+file_iterator::~file_iterator()
+{
+ if (fp != stdin)
+ fclose(fp);
+ else
+ clearerr(stdin);
+}
+
+int file_iterator::next_file(FILE *f, const char *s)
+{
+ if (fp != stdin)
+ fclose(fp);
+ else
+ clearerr(stdin);
+ filename = s;
+ fp = f;
+ lineno = 1;
+ newline_flag = 0;
+ ptr = 0;
+ eptr = 0;
+ return 1;
+}
+
+int file_iterator::fill(node **)
+{
+ if (newline_flag)
+ lineno++;
+ newline_flag = 0;
+ unsigned char *p = buf;
+ ptr = p;
+ unsigned char *e = p + BUF_SIZE;
+ while (p < e) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ if (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: file `%1'\n", filename, lineno);
+}
+
+int file_iterator::set_location(const char *f, int ln)
+{
+ if (f)
+ 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 shift(int n);
+ static void add_boundary();
+ static void remove_boundary();
+ static int get_level();
+ static void clear();
+
+ 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::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");
+ 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()
+{
+ 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)
+{
+ for (input_iterator *p = top; p; p = p->next)
+ if (p->next_file(fp, s))
+ return;
+ for (input_iterator **pp = &top; *pp; pp = &(*pp)->next)
+ ;
+ *pp = new file_iterator(fp, s);
+}
+
+void input_stack::clear()
+{
+ while (!top->is_boundary() && top != &nil_iterator) {
+ input_iterator *tem = top;
+ top = top->next;
+ level--;
+ delete tem;
+ }
+}
+
+void backtrace_request()
+{
+ input_stack::backtrace_all();
+ fflush(stderr);
+ skip_line();
+}
+
+void next_file()
+{
+ symbol nm = get_long_name(1);
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ if (!nm.is_null()) {
+ 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 = 1;
+ if (!has_arg() || get_integer(&n))
+ input_stack::shift(n);
+ skip_line();
+}
+
+static int get_char_for_escape_name()
+{
+ int c = get_copy(NULL);
+ switch (c) {
+ case EOF:
+ error("end of input in escape name");
+ return '\0';
+ default:
+ if (!illegal_input_char(c))
+ break;
+ // fall through
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\001':
+ case '\b':
+ 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)
+ 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;
+ delete old_buf;
+ }
+ }
+ if (c == ']' && input_stack::get_level() == start_level)
+ break;
+ buf[i++] = c;
+ }
+ buf[i] = 0;
+ if (buf == abuf) {
+ if (i == 0) {
+ error("empty escape name");
+ return NULL_SYMBOL;
+ }
+ return symbol(abuf);
+ }
+ else {
+ symbol s(buf);
+ 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 '$':
+ {
+ (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 '\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_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 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";
+}
+
+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 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;
+ }
+ if (tok.changes_env())
+ tok.process();
+ else {
+ 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;
+ }
+ if (tok.changes_env())
+ tok.process();
+ else {
+ 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;
+}
+
+#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();
+};
+
+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";
+}
+
+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)
+{
+ nd = t.nd ? t.nd->copy() : 0;
+}
+
+void token::operator=(const token &t)
+{
+ delete nd;
+ nm = t.nm;
+ nd = t.nd ? t.nd->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 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_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_NODE;
+ nd = new transparent_dummy_node;
+ 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;
+ 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 '~':
+ nd = new unbreakable_space_node(curenv->get_space_width());
+ type = TOKEN_NODE;
+ return;
+ case '"':
+ while ((cc = input_stack::get(NULL)) != '\n' && cc != EOF)
+ ;
+ if (cc == '\n')
+ type = TOKEN_NEWLINE;
+ else
+ type = TOKEN_EOF;
+ return;
+ 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 '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;
+ for (const char *p = s.contents(); *p != '\0'; p++)
+ if (!csdigit(*p)) {
+ type = TOKEN_FONT_NAME;
+ nm = s;
+ return;
+ }
+ type = TOKEN_FONT_POSITION;
+ val = atoi(s.contents());
+ return;
+ }
+ 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()))
+ break;
+ type = TOKEN_CHAR_HEIGHT;
+ val = x;
+ return;
+ 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 '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))
+ break;
+ type = TOKEN_SIZE;
+ val = x;
+ return;
+ case 'S':
+ if (!get_delim_number(&x, 0))
+ break;
+ type = TOKEN_CHAR_SLANT;
+ val = x;
+ return;
+ 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 '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 transparently throughput a request");
+ break;
+ }
+ nd = new special_node(*m);
+ type = TOKEN_NODE;
+ return;
+ }
+ case 'z':
+ {
+ next();
+ 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:
+ case TOKEN_CHAR_HEIGHT:
+ case TOKEN_CHAR_SLANT:
+ case TOKEN_FONT_POSITION:
+ case TOKEN_SIZE:
+ 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_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_CHAR_HEIGHT:
+ return "`\\H'";
+ case TOKEN_CHAR_SLANT:
+ return "`\\S'";
+ case TOKEN_DUMMY:
+ return "`\\&'";
+ case TOKEN_ESCAPE:
+ return "`\\e'";
+ case TOKEN_FONT_NAME:
+ case TOKEN_FONT_POSITION:
+ return "`\\f'";
+ 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_SIZE:
+ return "`\\s'";
+ case TOKEN_SPACE:
+ return "a space";
+ case TOKEN_SPECIAL:
+ return "a special character";
+ case TOKEN_SPREAD:
+ return "`\\p'";
+ case TOKEN_TAB:
+ return "a tab character";
+ case TOKEN_TRANSPARENT:
+ 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())
+ compatible_flag = 1;
+ else if (get_integer(&n))
+ compatible_flag = n != 0;
+ 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;
+ 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);
+ delete buf;
+ return s;
+ }
+}
+
+extern int exit_flag;
+
+NO_RETURN void exit_groff()
+{
+ exit_flag = 1;
+ if (!end_macro_name.is_null()) {
+ spring_trap(end_macro_name);
+ tok.next();
+ process_input_stack();
+ }
+ curenv->do_break();
+ tok.next();
+ process_input_stack();
+ end_diversions();
+ exit_flag = 2;
+ topdiv->set_ejecting();
+ push_page_ejector();
+ topdiv->space(topdiv->get_page_length(), 1);
+ tok.next();
+ process_input_stack();
+ // This will only be 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_groff().
+
+NO_RETURN void exit_request()
+{
+ input_stack::clear();
+ exit_groff();
+}
+
+void end_macro()
+{
+ end_macro_name = get_name();
+ skip_line();
+}
+
+inline int possibly_handle_first_page_transition()
+{
+ if (!topdiv->first_page_begun && 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];
+ if (ci->get_special_translation() == charinfo::TRANSLATE_SPACE)
+ return ' ';
+ if (ci->get_special_translation() == charinfo::TRANSLATE_DUMMY)
+ return ESCAPE_AMPERSAND;
+#if 0
+ // This is just too ugly.
+ ci = ci->get_translation();
+ if (ci) {
+ int c = ci->get_ascii_code();
+ if (c != '\0')
+ return c;
+ }
+#endif
+ }
+ 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())
+ 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;
+}
+
+static 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())
+ 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()) {
+ curenv->do_break();
+ int nspaces = 0;
+ do {
+ nspaces += tok.nspaces();
+ tok.next();
+ } while (tok.space());
+ if (tok.newline())
+ blank_line();
+ else {
+ curenv->add_node(new hmotion_node(curenv->get_space_width()*nspaces));
+ suppress_next = 1;
+ 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;
+ }
+ 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()
+: head(0), tail(0), ptr(0), len(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 {
+ 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)
+: filename(m.filename), lineno(m.lineno), p(m.p), 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(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::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)
+: lineno(1), mac(m), newline_flag(0), how_invoked(p), 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;
+}
+
+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()
+{
+ delete base;
+}
+
+class small_temp_iterator : public input_iterator {
+private:
+ small_temp_iterator(const char *, int);
+#ifndef OP_DELETE_BROKEN
+ ~small_temp_iterator();
+ enum { BLOCK = 16 };
+ static small_temp_iterator *free_list;
+ void *operator new(size_t);
+ void operator delete(void *);
+#endif /* not OP_DELETE_BROKEN */
+ enum { SIZE = 12 };
+ unsigned char buf[SIZE];
+ friend input_iterator *make_temp_iterator(const char *);
+};
+
+#ifndef OP_DELETE_BROKEN
+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()
+{
+}
+
+#endif /* not OP_DELETE_BROKEN */
+
+#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 &);
+ 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);
+};
+
+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)
+{
+ for (arg_list **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) {
+ char buf[3];
+ buf[0] = s[0];
+ buf[1] = s[1];
+ buf[2] = '\0';
+ if (request_dictionary.lookup(symbol(buf)) != 0)
+ warned = warning(WARN_SPACE,
+ "space required between `%1' and argument", 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;
+ 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
+ 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;
+}
+
+macro_iterator::macro_iterator(symbol s, macro &m)
+: string_iterator(m, "macro", 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;
+ }
+}
+
+void read_request()
+{
+ macro_iterator *mi = new macro_iterator;
+ 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)) {
+ fputc(c, stderr);
+ had_prompt = 1;
+ }
+ c = get_copy(NULL);
+ }
+ if (c == ' ') {
+ tok.make_space();
+ decode_args(mi);
+ }
+ }
+ fputc(had_prompt ? ':' : '\007', 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);
+ }
+ }
+ 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.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.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 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')
+ 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 {
+ for (const char *p = s; *p && csdigit(*p); p++)
+ ;
+ if (*p)
+ error("bad argument name `%1'", s);
+ else
+ input_stack::push(input_stack::get_arg(atoi(s)));
+ }
+}
+
+/* We need to unget the current token; we fake this by wrapping it up
+in a token_node, and wrapping that up in a string_iterator. */
+
+void handle_first_page_transition()
+{
+ macro mac;
+ mac.append(new token_node(tok));
+ input_stack::push(new string_iterator(mac));
+ topdiv->begin_page();
+}
+
+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);
+}
+
+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 string_iterator(*m, "trap-invoked macro", nm));
+ 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;
+}
+
+// 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)
+{
+ symbol nm;
+ if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
+ nm = get_name(1);
+ if (nm.is_null()) {
+ skip_line();
+ return;
+ }
+ }
+ symbol term = get_name(); // the request that terminates the definition
+ if (term.is_null())
+ term = dot_symbol;
+ while (!tok.newline() && !tok.eof())
+ tok.next();
+ 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 (c == EOF) {
+ error("end of file while defining macro");
+ tok.next();
+ return;
+ }
+ if (bol && c == '.') {
+ const char *s = term.contents();
+ int d;
+ // see if it matches term
+ for (int i = 0; s[i] != 0; i++) {
+ d = get_copy(&n);
+ if ((unsigned char)s[i] != d)
+ break;
+ }
+ if (d == EOF) {
+ error("end of file while defining macro");
+ tok.next();
+ return;
+ }
+ 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)
+ 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 (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);
+}
+
+void append_macro()
+{
+ do_define_macro(DEFINE_APPEND);
+}
+
+void ignore()
+{
+ do_define_macro(DEFINE_IGNORE);
+}
+
+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 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 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.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;
+ 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)
+ 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);
+ 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(itoa(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();
+};
+
+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";
+}
+
+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);
+}
+
+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();
+ if (c == '\0')
+ error("%1 is illegal within \\X", tok.description());
+ else
+ mac.append(c);
+ }
+ return new special_node(mac);
+}
+
+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();
+}
+
+
+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);
+ tok.next();
+ curenv = oldenv;
+ }
+ else {
+ units n;
+ if (!get_number(&n, 'u')) {
+ skip_alternative();
+ return 0;
+ }
+ else
+ result = n > 0;
+ }
+ if (invert)
+ result = !result;
+ if (result)
+ begin_alternative();
+ else
+ skip_alternative();
+ return result;
+}
+
+void if_else_request()
+{
+ if_else_stack.push(do_if_request());
+}
+
+void if_request()
+{
+ do_if_request();
+}
+
+void else_request()
+{
+ if (if_else_stack.is_empty()) {
+ warning(WARN_EL, "unbalanced .el request");
+ skip_alternative();
+ }
+ else {
+ if (if_else_stack.pop())
+ skip_alternative();
+ else
+ begin_alternative();
+ }
+}
+
+static int while_depth = 0;
+static int while_break_flag = 0;
+
+void while_request()
+{
+ macro mac;
+ int escaped = 0;
+ int level = 0;
+ mac.append(new token_node(tok));
+ for (;;) {
+ node *n;
+ int c = input_stack::get(&n);
+ if (c == EOF)
+ break;
+ if (c == 0) {
+ escaped = 0;
+ mac.append(n);
+ }
+ else if (escaped) {
+ if (c == '{')
+ level += 1;
+ else if (c == '}')
+ level -= 1;
+ escaped = 0;
+ mac.append(c);
+ }
+ else {
+ if (c == ESCAPE_LEFT_BRACE)
+ level += 1;
+ else if (c == ESCAPE_RIGHT_BRACE)
+ level -= 1;
+ else if (c == escape_char)
+ escaped = 1;
+ mac.append(c);
+ if (c == '\n' && level <= 0)
+ break;
+ }
+ }
+ if (level != 0)
+ error("unbalanced \\{ \\}");
+ else {
+ while_depth++;
+ input_stack::add_boundary();
+ for (;;) {
+ input_stack::push(new string_iterator(mac, "while loop"));
+ tok.next();
+ if (!do_if_request()) {
+ while (input_stack::get(NULL) != EOF)
+ ;
+ break;
+ }
+ process_input_stack();
+ if (while_break_flag) {
+ 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();
+ 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();
+ }
+}
+
+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_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;
+ 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
+
+void terminal()
+{
+ int c;
+ while ((c = get_copy(NULL)) == ' ' || c == '\t')
+ ;
+ for (; c != '\n' && c != EOF; c = get_copy(NULL))
+ fputs(asciify(c), stderr);
+ fputc('\n', stderr);
+ fflush(stderr);
+ tok.next();
+}
+
+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()) {
+ 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()
+{
+ do_open(0);
+}
+
+void opena_request()
+{
+ 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);
+ tok.next();
+}
+
+static void init_charset_table()
+{
+ char buf[16];
+ strcpy(buf, "char");
+ for (int i = 0; i < 256; i++) {
+ strcpy(buf + 4, itoa(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("hy"))->set_flags(charinfo::BREAK_AFTER);
+ 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("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
+ get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
+ page_character = charset_table['%'];
+}
+
+void translate()
+{
+ tok.skip();
+ while (!tok.newline() && !tok.space() && !tok.eof()) {
+ charinfo *ci1 = tok.get_char(1);
+ if (ci1 == 0)
+ break;
+ tok.next();
+ if (tok.newline() || tok.space() || tok.eof()) {
+ ci1->set_special_translation(charinfo::TRANSLATE_SPACE);
+ break;
+ }
+ if (tok.dummy())
+ ci1->set_special_translation(charinfo::TRANSLATE_DUMMY);
+ else {
+ charinfo *ci2 = tok.get_char(1);
+ if (ci2 == 0)
+ break;
+ if (ci1 == ci2)
+ ci1->set_translation(0);
+ else
+ ci1->set_translation(ci2);
+ }
+ tok.next();
+ }
+ skip_line();
+}
+
+
+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();
+ }
+ 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 (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) {
+ if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
+ error("normal or special character expected (got %1): "
+ "treated as missing",
+ tok.description());
+ }
+ else
+ tok.next();
+ return ci;
+}
+
+int token::add_to_node_list(node **pp)
+{
+ hunits w;
+ node *n = 0;
+ switch (type) {
+ case TOKEN_CHAR:
+ *pp = (*pp)->add_char(charset_table[c], curenv, &w);
+ break;
+ case TOKEN_CHAR_HEIGHT:
+ curenv->set_char_height(val);
+ break;
+ case TOKEN_CHAR_SLANT:
+ curenv->set_char_slant(val);
+ 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);
+ break;
+ case TOKEN_FONT_NAME:
+ curenv->set_font(nm);
+ break;
+ case TOKEN_FONT_POSITION:
+ curenv->set_font(val);
+ 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);
+ break;
+ case TOKEN_RIGHT_BRACE:
+ break;
+ case TOKEN_SIZE:
+ curenv->set_size(val);
+ 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);
+ 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_CHAR_HEIGHT:
+ curenv->set_char_height(val);
+ break;
+ case TOKEN_CHAR_SLANT:
+ curenv->set_char_slant(val);
+ break;
+ case TOKEN_DUMMY:
+ curenv->add_node(new dummy_node);
+ break;
+ case TOKEN_EOF:
+ assert(0);
+ break;
+ case TOKEN_EMPTY:
+ assert(0);
+ break;
+ case TOKEN_ESCAPE:
+ if (escape_char != 0)
+ curenv->add_char(charset_table[escape_char]);
+ break;
+ case TOKEN_FONT_NAME:
+ curenv->set_font(nm);
+ break;
+ case TOKEN_FONT_POSITION:
+ curenv->set_font(val);
+ 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_SIZE:
+ curenv->set_size(val);
+ 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_TAB:
+ curenv->handle_tab(0);
+ break;
+ case TOKEN_TRANSPARENT:
+ break;
+ default:
+ assert(0);
+ }
+}
+
+class nargs_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *nargs_reg::get_string()
+{
+ return itoa(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 itoa(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 itoa(*p);
+}
+
+void abort_request()
+{
+ int c;
+ 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;
+ delete tem;
+ }
+ s[i++] = c;
+ }
+ c = get_copy(0);
+ }
+ s[i] = '\0';
+ tok.next();
+ if (i == 0) {
+ delete s;
+ return 0;
+ }
+ return s;
+}
+
+void pipe_output()
+{
+ 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");
+ }
+}
+
+static int system_status;
+
+void system_request()
+{
+ char *command = read_string();
+ if (!command)
+ error("empty command");
+ else {
+ system_status = system(command);
+ delete command;
+ }
+}
+
+void copy_file()
+{
+ if (curdiv == topdiv && !topdiv->first_page_begun) {
+ 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->first_page_begun) {
+ 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->first_page_begun) {
+ 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()) {
+ 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;
+ 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;
+ }
+}
+
+struct string_list {
+ const char *s;
+ string_list *next;
+ string_list(const char *ss) : s(ss), next(0) {}
+};
+
+string_list *mac_dirs;
+string_list *font_dirs;
+
+// open filename, searching in dirs and put the result in pathp
+
+static FILE *open_file(const char *filename, string_list *dirs, char **pathp)
+{
+ int fnlen = strlen(filename);
+ while (dirs) {
+ int dlen = 0;
+ int need_slash = 0;
+ if (dirs->s) {
+ dlen = strlen(dirs->s);
+ if (dlen && dirs->s[dlen-1] != '/')
+ need_slash = 1;
+ }
+ char *path = new char[dlen + need_slash + fnlen + 1];
+ if (dirs->s)
+ strcpy(path, dirs->s);
+ else
+ path[0] = 0;
+ if (need_slash)
+ strcat(path, "/");
+ strcat(path, filename);
+ FILE *fp = fopen(path, "r");
+ if (fp) {
+ *pathp = path;
+ return fp;
+ }
+ delete path;
+ dirs = dirs->next;
+ }
+ return 0;
+}
+
+static void add_string(const char *s, string_list **p)
+{
+ while (*p)
+ p = &((*p)->next);
+ *p = new string_list(s);
+}
+
+static void init_dirs(const char *e, const char *standard, string_list **dirs)
+{
+ char *var = getenv(e);
+ if (!var) {
+ var = new char[strlen(standard)+1];
+ strcpy(var, standard);
+ }
+ for (char *p = strtok(var, ":"); p; p = strtok(NULL, ":"))
+ add_string(p, dirs);
+}
+
+
+#define MACRO_PREFIX "tmac."
+
+static FILE *open_mac_file(const char *mac, char **path)
+{
+ char *s = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
+ strcpy(s, MACRO_PREFIX);
+ strcat(s, mac);
+ FILE *fp = open_file(s, mac_dirs, path);
+ delete s;
+ 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();
+ delete path;
+ input_stack::push(new file_iterator(fp, s));
+ tok.next();
+ process_input_stack();
+}
+
+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 = open_file(nm.contents(), mac_dirs, &path);
+ if (fp) {
+ input_stack::push(new file_iterator(fp, symbol(path).contents()));
+ 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 {
+ 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);
+ 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);
+ delete buf;
+ }
+}
+
+#define USAGE_EXIT_CODE 1
+
+void usage(const char *prog)
+{
+ errprint(
+"usage: %1 -abivzCE -wname -Wname -dcstring -mname -nN -olist -rcN\n"
+" -Tname -Fdir -Mdir -Hfile [ files ]\n",
+ prog);
+ exit(USAGE_EXIT_CODE);
+}
+
+int
+#ifdef DUMP
+normal_main
+#else
+main
+#endif
+ (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;
+ const char *hyphen_file = HYPHENFILE;
+ int iflag = 0;
+ int tflag = 0;
+ int fflag = 0;
+ int nflag = 0;
+ int next_page_number;
+ opterr = 0;
+ hresolution = vresolution = 1;
+ const char *tem = getenv("GROFF_TYPESETTER");
+ if (tem)
+ device = tem;
+ tem = getenv("GROFF_HYPHEN");
+ if (tem)
+ hyphen_file = tem;
+ while ((c = getopt(argc, argv, "abivw:W:zCEf:m:n:o:r:d:F:H:M:T:tqs:")) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU troff version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'T':
+ device = optarg;
+ tflag = 1;
+ break;
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'M':
+ add_string(optarg, &mac_dirs);
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'H':
+ hyphen_file = optarg;
+ break;
+ case 'm':
+ add_string(optarg, &macros);
+ break;
+ case 'E':
+ inhibit_errors = 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 '?':
+ usage(argv[0]);
+ default:
+ assert(0);
+ }
+ set_string(".T", device);
+ init_dirs(MACROPATH_ENVVAR, MACROPATH, &mac_dirs);
+ init_charset_table();
+ font::set_device_name(device);
+ 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++)
+ mount_font(j++, symbol(font::font_name_table[i]));
+ curdiv = topdiv = new top_level_diversion;
+ if (nflag)
+ topdiv->set_next_page_number(next_page_number);
+ init_input_requests();
+ init_env_requests();
+ init_div_requests();
+#ifdef COLUMN
+ init_column_requests();
+#endif /* COLUMN */
+ init_node_requests();
+ number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
+ init_registers();
+ init_reg_requests();
+ read_hyphen_file(hyphen_file);
+ 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;
+ }
+ while (macros) {
+ process_macro_file(macros->s);
+ string_list *tem = macros;
+ macros = macros->next;
+ delete tem;
+ }
+ for (i = optind; i < argc; i++)
+ process_input_file(argv[i]);
+ if (optind >= argc || iflag)
+ process_input_file("-");
+ exit_groff();
+}
+
+#ifdef DUMP
+
+int dumped_main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int c;
+ string_list *macros = 0;
+ string_list *register_assignments = 0;
+ string_list *string_assignments = 0;
+ int iflag = 0;
+ int nflag = 0;
+ int next_page_number;
+ opterr = 0;
+ while ((c = getopt(argc, argv, "abivw:W:zECf:m:n:o:r:d:F:H:M:T:tqs:")) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "GNU troff version %s\n", version_string);
+ fflush(stderr);
+ break;
+ }
+ case 'f':
+ case 'H':
+ case 'T':
+ error("`-%1' option illegal when dumped", char(c));
+ break;
+ case 'C':
+ compatible_flag = 1;
+ break;
+ case 'M':
+ add_string(optarg, &mac_dirs);
+ break;
+ case 'F':
+ font::command_line_font_dir(optarg);
+ break;
+ case 'm':
+ add_string(optarg, &macros);
+ break;
+ case 'E':
+ inhibit_errors = 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 'q':
+ case 's':
+ case 't':
+ // silently ignore these
+ break;
+ case '?':
+ usage(argv[0]);
+ default:
+ assert(0);
+ }
+ init_dirs(MACROPATH_ENVVAR, MACROPATH, &mac_dirs);
+ init_registers();
+ if (nflag)
+ topdiv->set_next_page_number(next_page_number);
+ 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;
+ }
+ while (macros) {
+ process_macro_file(macros->s);
+ string_list *tem = macros;
+ macros = macros->next;
+ delete tem;
+ }
+ int i;
+ for (i = optind; i < argc; i++)
+ process_input_file(argv[i]);
+ if (optind >= argc || iflag)
+ process_input_file("-");
+ exit_groff();
+}
+
+static int dumped = 0;
+
+extern "C" {
+
+int unexec(const char *new_name, const char *a_name, unsigned data_start = 0,
+ unsigned bss_start = 0, unsigned entry_address = 0);
+
+#ifdef __GNUG__
+#define CPLUS_INIT_FUNC __main
+#else
+#define CPLUS_INIT_FUNC _main
+#endif
+
+extern int CPLUS_INIT_FUNC();
+
+int c_main(int argc, char **argv)
+{
+ if (dumped)
+ return dumped_main(argc, argv);
+ else {
+ CPLUS_INIT_FUNC();
+ return normal_main(argc, argv);
+ }
+}
+
+#define OPTIND_INITIAL_VALUE 1
+
+}
+
+void dump_request()
+{
+ symbol new_name = get_long_name(1);
+ if (new_name.is_null()) {
+ skip_line();
+ return;
+ }
+ symbol a_name = get_long_name(1);
+ if (a_name.is_null()) {
+ skip_line();
+ return;
+ }
+ if (topdiv->first_page_begun)
+ fatal("can't dump after first page begun");
+ while (input_stack::get(NULL) != EOF)
+ ;
+ suppress_output_flag = 0;
+ ascii_output_flag = 0;
+ dumped = 1;
+ optind = OPTIND_INITIAL_VALUE;
+ number_reg_dictionary.remove(".A");
+ while (output_page_list) {
+ page_range *tem = output_page_list;
+ output_page_list = output_page_list->next;
+ delete tem;
+ }
+ while (mac_dirs) {
+ string_list *tem = mac_dirs;
+ mac_dirs = mac_dirs->next;
+ delete tem;
+ }
+ font::forget_command_line_font_dirs();
+ fflush(stderr);
+ clearerr(stdin);
+ // clean up the diversion_stack
+ // clean up environment stack
+ if (unexec(new_name.contents(), a_name.contents()) < 0)
+ exit(1);
+ else
+ exit(0);
+}
+
+#endif /* DUMP */
+
+void warn_request()
+{
+ int n;
+ if (!has_arg())
+ warning_mask = WARN_TOTAL;
+ else if (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;
+ }
+ skip_line();
+}
+
+static void init_registers()
+{
+ long t;
+ time(&t);
+ 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("yr", int(tt->tm_year));
+ set_number_reg("$$", getpid());
+ number_reg_dictionary.define(".A", new constant_reg(ascii_output_flag ? "1" : "0"));
+}
+
+void init_input_requests()
+{
+ init_request("ds", define_string);
+ init_request("as", append_string);
+ init_request("de", define_macro);
+ init_request("am", append_macro);
+ init_request("ig", ignore);
+ init_request("rm", remove_macro);
+ init_request("rn", rename_macro);
+ 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("pc", set_page_character);
+ init_request("tm", terminal);
+ init_request("ex", REQUEST_FUNCP(exit_request));
+ init_request("em", end_macro);
+ init_request("tr", translate);
+ 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("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("asciify", asciify_macro);
+ init_request("warn", warn_request);
+#ifdef DUMP
+ init_request("dump", dump_request);
+#endif
+ init_request("open", open_request);
+ init_request("opena", opena_request);
+ init_request("close", close_request);
+ init_request("write", write_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);
+ 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));
+ number_reg_dictionary.define(".g", new constant_reg("1"));
+ number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
+}
+
+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(charinfo *ci, const environment *envp)
+{
+ macro *mac = ci->set_macro(0);
+ assert(mac != 0);
+ environment *oldenv = curenv;
+ environment env(envp);
+ curenv = &env;
+ font_size sz = env.get_font_size();
+ 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();
+ composite_node *cn = new composite_node(n, ci, sz);
+ input_stack::remove_boundary();
+ ci->set_macro(mac);
+ tok = old_tok;
+ curenv = oldenv;
+ return cn;
+}
+
+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;
+ delete oldpoint;
+ }
+ if (!get_hunits(&point[i].h, '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());
+ delete point;
+ return dn;
+ }
+ else {
+ 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,
+ "di", WARN_DI,
+ "mac", WARN_MAC,
+ "reg", WARN_REG,
+ "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);
+}
+
+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)
+: nm(s), hyphenation_code(0), translation(0), flags(0), ascii_code(0),
+ special_translation(TRANSLATE_NONE), mac(0), not_found(0)
+{
+ index = next_index++;
+}
+
+void charinfo::set_hyphenation_code(unsigned char c)
+{
+ hyphenation_code = c;
+}
+
+void charinfo::set_translation(charinfo *ci)
+{
+ translation = ci;
+ special_translation = TRANSLATE_NONE;
+}
+
+void charinfo::set_special_translation(int c)
+{
+ special_translation = c;
+ translation = 0;
+}
+
+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(itoa(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/troff/node.c b/troff/node.c
new file mode 100644
index 000000000..9a38c4f4e
--- /dev/null
+++ b/troff/node.c
@@ -0,0 +1,4742 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 "charinfo.h"
+#include "font.h"
+#include "reg.h"
+
+#define STORE_WIDTH 1
+
+symbol HYPHEN_SYMBOL("hy");
+
+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);
+ 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 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());
+}
+
+static void invalidate_fontno(int n);
+
+/* 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)
+: internal_name(nm), external_name(enm), fm(f), number(n),
+ is_constant_spaced(CONSTANT_SPACE_NONE),
+ sf(0), is_bold(0), cond_bold_list(0),
+ last_ligature_mode(1), last_kern_mode(1),
+ last_tfont(0), last_size(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)
+ : fontno(f), offset(h), next(x)
+{
+}
+
+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)
+{
+ return hunits(fm->get_space_width(fs.to_scaled_points()));
+}
+
+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();
+ for (tfont *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 {
+ int piped;
+ int printing;
+ virtual void really_transparent_char(unsigned char) = 0;
+ virtual void really_print_line(hunits x, vunits y, node *n,
+ vunits before, vunits after) = 0;
+ virtual void really_begin_page(int pageno, vunits page_length) = 0;
+ virtual void really_copy_file(hunits x, vunits y, const char *filename);
+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);
+ void begin_page(int pageno, vunits page_length);
+ 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);
+ 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);
+ 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;
+ void do_motion();
+ vunits page_length;
+ 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 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();
+ 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);
+ void really_begin_page(int pageno, vunits page_length);
+ void really_copy_file(hunits x, vunits y, const char *filename);
+ void draw(char, hvpair *, int, font_size);
+ 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(itoa(i), fp);
+}
+
+void troff_output_file::start_special()
+{
+ flush_tbuf();
+ do_motion();
+ put("x X ");
+}
+
+void troff_output_file::special_char(unsigned char c)
+{
+ put(c);
+ if (c == '\n')
+ put('+');
+}
+
+void troff_output_file::end_special()
+{
+ put('\n');
+}
+
+inline void troff_output_file::moveto(hunits h, vunits v)
+{
+ hpos = h.to_units();
+ vpos = v.to_units();
+}
+
+void troff_output_file::really_print_line(hunits x, vunits y, node *n,
+ vunits before, vunits after)
+{
+ moveto(x, y);
+ while (n != 0) {
+ n->tprint(this);
+ n = n->next;
+ }
+ flush_tbuf();
+ // This ensures that transparent throughput will have a more predictable
+ // position.
+ do_motion();
+ force_motion = 1;
+ hpos = 0;
+ put('n');
+ put(before.to_units());
+ put(' ');
+ put(after.to_units());
+ put('\n');
+}
+
+inline void troff_output_file::word_marker()
+{
+ flush_tbuf();
+ 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(' ');
+ }
+ for (int i = 0; i < tbuf_len; i++)
+ put(tbuf[i]);
+ put('\n');
+ tbuf_len = 0;
+}
+
+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();
+ 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) {
+ tbuf[tbuf_len++] = c;
+ output_hpos += w.to_units() + kk;
+ hpos = output_hpos;
+ return;
+ }
+ flush_tbuf();
+ do_motion();
+ tbuf[tbuf_len++] = c;
+ output_hpos += w.to_units() + kk;
+ tbuf_kern = kk;
+ hpos = output_hpos;
+ }
+ else {
+ // flush_tbuf();
+ int n = hpos - output_hpos;
+ 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));
+ 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;
+}
+
+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());
+ }
+ 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_begin_page(int pageno, vunits pl)
+{
+ flush_tbuf();
+ if (page_length > V0) {
+ put('V');
+ put(page_length.to_units());
+ put('\n');
+ }
+ page_length = pl;
+ 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();
+ 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()
+{
+ flush_tbuf();
+ if (page_length > V0) {
+ put("x trailer\n");
+ put('V');
+ put(page_length.to_units());
+ put('\n');
+ }
+ put("x stop\n");
+ delete font_position;
+}
+
+troff_output_file::troff_output_file()
+: current_height(0), current_slant(0), tbuf_len(0), nfont_positions(10)
+{
+ 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()
+{
+}
+
+real_output_file::real_output_file()
+: printing(0)
+{
+ if (pipe_command) {
+ if ((fp = popen(pipe_command, "w")) != 0) {
+ piped = 1;
+ return;
+ }
+ error("pipe open failed: %1", strerror(errno));
+ }
+ piped = 0;
+ 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");
+ }
+ if (piped) {
+ int result = pclose(fp);
+ fp = 0;
+ if (result < 0)
+ fatal("pclose failed");
+ if ((result & 0x7f) != 0)
+ error("output process `%1' got fatal signal %2",
+ pipe_command, result & 0x7f);
+ else {
+ int exit_status = (result >> 8) & 0xff;
+ if (exit_status != 0)
+ error("output process `%1' exited with status %2",
+ pipe_command, exit_status);
+ }
+ }
+ else if (fclose(fp) < 0) {
+ fp = 0;
+ fatal("error closing output file");
+ }
+}
+
+void real_output_file::flush()
+{
+ if (fflush(fp) < 0)
+ fatal("error writing output file");
+}
+
+int real_output_file::is_printing()
+{
+ return printing;
+}
+
+void real_output_file::begin_page(int pageno, vunits page_length)
+{
+ printing = in_output_page_list(pageno);
+ if (printing)
+ really_begin_page(pageno, page_length);
+}
+
+void real_output_file::copy_file(hunits x, vunits y, const char *filename)
+{
+ if (printing)
+ really_copy_file(x, y, filename);
+}
+
+void real_output_file::transparent_char(unsigned char c)
+{
+ if (printing)
+ really_transparent_char(c);
+}
+
+void real_output_file::print_line(hunits x, vunits y, node *n,
+ vunits before, vunits after)
+{
+ if (printing)
+ really_print_line(x, y, n, before, after);
+ delete_node_list(n);
+}
+
+void real_output_file::really_copy_file(hunits, vunits, const char *)
+{
+ // do nothing
+}
+
+
+/* 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)
+{
+ 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)
+{
+}
+
+void suppress_output_file::really_begin_page(int pageno, vunits page_length)
+{
+}
+
+void suppress_output_file::really_transparent_char(unsigned char c)
+{
+}
+
+/* glyphs, ligatures, kerns, discretionary breaks */
+
+class glyph_node : public node {
+ static glyph_node *free_list;
+protected:
+ charinfo *ci;
+ tfont *tf;
+#ifdef STORE_WIDTH
+ hunits wid;
+ glyph_node(charinfo *, tfont *, hunits, node * = 0);
+#endif
+public:
+#ifndef OP_DELETE_BROKEN
+ void *operator new(size_t);
+ void operator delete(void *);
+#endif /* not OP_DELETE_BROKEN */
+ glyph_node(charinfo *, tfont *, node * = 0);
+#ifndef OP_DELETE_BROKEN
+ ~glyph_node() {}
+#endif /* not OP_DELETE_BROKEN */
+ 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 **);
+ int ends_sentence();
+ int overlaps_vertically();
+ int overlaps_horizontally();
+ void ascii_print(ascii_output_file *);
+ void asciify(macro *);
+ int character_type();
+ int same(node *);
+ const char *type();
+};
+
+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:
+#ifndef OP_DELETE_BROKEN
+ void *operator new(size_t);
+ void operator delete(void *);
+#endif /* OP_DELETE_BROKEN */
+ 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();
+};
+
+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();
+};
+
+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();
+};
+
+#ifndef OP_DELETE_BROKEN
+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 p;
+}
+#endif /* OP_DELETE_BROKEN */
+
+glyph_node::glyph_node(charinfo *c, tfont *t, node *x)
+ : ci(c), tf(t), node(x)
+{
+#ifdef STORE_WIDTH
+ wid = tf->get_width(ci);
+#endif
+}
+
+#ifdef STORE_WIDTH
+glyph_node::glyph_node(charinfo *c, tfont *t, hunits w, node *x)
+ : ci(c), tf(t), wid(w), node(x)
+{
+}
+#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;
+}
+
+int glyph_node::overlaps_horizontally()
+{
+ return ci->overlaps_horizontally();
+}
+
+int glyph_node::overlaps_vertically()
+{
+ return ci->overlaps_vertically();
+}
+
+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;
+}
+
+int glyph_node::ends_sentence()
+{
+ if (ci->ends_sentence())
+ return 1;
+ else if (ci->transparent())
+ return 2;
+ else
+ return 0;
+}
+
+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), n1(first), n2(second), amount(n)
+{
+}
+
+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();
+}
+
+node *kern_pair_node::add_discretionary_hyphen()
+{
+ charinfo *hci = get_charinfo(HYPHEN_SYMBOL);
+ tfont *tf = n2->get_tfont();
+ if (tf) {
+ if (tf->contains(hci)) {
+ node *next1 = next;
+ next = 0;
+ node *n = copy();
+ glyph_node *gn = new glyph_node(hci, 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();
+ 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";
+}
+
+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);
+ charinfo *hci = get_charinfo(HYPHEN_SYMBOL);
+ if (tf->contains(hci)) {
+ node *next1 = next;
+ next = 0;
+ node *n = copy();
+ glyph_node *gn = new glyph_node(hci, 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;
+}
+
+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 *m);
+ 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();
+};
+
+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)
+: n(nn), x(xx), node(p)
+{
+ 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 *m);
+ hyphenation_type get_hyphenation_type();
+ int overlaps_vertically();
+ int overlaps_horizontally();
+ units size();
+ tfont *get_tfont();
+ int same(node *);
+ const char *type();
+};
+
+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);
+}
+
+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() : max_width(H0), list(0)
+{
+}
+
+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;
+ for (node **p = &list; *p; p = &(*p)->next)
+ ;
+ n->next = 0;
+ *p = n;
+}
+
+hunits overstrike_node::width()
+{
+ return max_width;
+}
+
+bracket_node::bracket_node() : max_width(H0), list(0)
+{
+}
+
+bracket_node::~bracket_node()
+{
+ delete_node_list(list);
+}
+
+node *bracket_node::copy()
+{
+ bracket_node *on = new bracket_node;
+ for (node *tem = list; tem; tem = tem->next)
+ 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)
+{
+ 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)
+{
+}
+
+space_node::space_node(hunits nn, int s, node *p) : node(p), n(nn), set(s)
+{
+}
+
+#if 0
+space_node::~space_node()
+{
+}
+#endif
+
+node *space_node::copy()
+{
+ return new space_node(n, set);
+}
+
+int space_node::nspaces()
+{
+ return set ? 0 : 1;
+}
+
+int space_node::merge_space(hunits h)
+{
+ 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;
+}
+
+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::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 *ascii)
+{
+}
+
+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 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 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;
+}
+
+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(' ');
+ 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 {
+ for (node *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;
+}
+
+int node::interpret(macro *)
+{
+ return 0;
+}
+
+special_node::special_node(const macro &m)
+: mac(m)
+{
+}
+
+int special_node::same(node *n)
+{
+ return mac == ((special_node *)n)->mac;
+}
+
+const char *special_node::type()
+{
+ return "special_node";
+}
+
+node *special_node::copy()
+{
+ return new special_node(mac);
+}
+
+void special_node::tprint_start(troff_output_file *out)
+{
+ out->start_special();
+}
+
+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();
+}
+
+/* composite_node */
+
+composite_node::composite_node(node *p, charinfo *c, font_size s, node *x)
+ : node(x), n(p), ci(c), sz(s)
+{
+}
+
+composite_node::~composite_node()
+{
+ delete_node_list(n);
+}
+
+node *composite_node::copy()
+{
+ return new composite_node(copy_node_list(n), ci, sz);
+}
+
+hunits composite_node::width()
+{
+ hunits x = H0;
+ for (node *tem = n; tem; tem = tem->next)
+ x += tem->width();
+ 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 sz.to_units();
+}
+
+hyphenation_type composite_node::get_hyphenation_type()
+{
+ return HYPHEN_MIDDLE;
+}
+
+int composite_node::overlaps_horizontally()
+{
+ return ci->overlaps_horizontally();
+}
+
+int composite_node::overlaps_vertically()
+{
+ return ci->overlaps_vertically();
+}
+
+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()
+{
+ if (n)
+ return n->get_tfont();
+ else
+ return 0;
+}
+
+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);
+}
+
+word_space_node::word_space_node(hunits d, node *x) : space_node(d, x)
+{
+}
+
+word_space_node::word_space_node(hunits d, int s, node *x)
+: space_node(d, s, x)
+{
+}
+
+node *word_space_node::copy()
+{
+ return new word_space_node(n, set);
+}
+
+void word_space_node::tprint(troff_output_file *out)
+{
+ out->word_marker();
+ space_node::tprint(out);
+}
+
+unbreakable_space_node::unbreakable_space_node(hunits d, node *x)
+: word_space_node(d, x)
+{
+}
+
+unbreakable_space_node::unbreakable_space_node(hunits d, int s, node *x)
+: word_space_node(d, s, x)
+{
+}
+
+node *unbreakable_space_node::copy()
+{
+ return new unbreakable_space_node(n, set);
+}
+
+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)
+{
+ return 0;
+}
+
+hvpair::hvpair()
+{
+}
+
+draw_node::draw_node(char c, hvpair *p, int np, font_size s)
+ : code(c), npoints(np), sz(s)
+{
+ 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";
+}
+
+draw_node::~draw_node()
+{
+ if (point)
+ 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;
+ for (node *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 *out)
+{
+}
+
+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)
+{
+ tprint_reverse_node_list(out, n);
+}
+
+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
+ 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);
+ 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_DUMMY:
+ return new dummy_node;
+ }
+ charinfo *tem = ci->get_translation();
+ if (tem)
+ ci = tem;
+ macro *mac = ci->get_macro();
+ if (mac)
+ return charinfo_to_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)
+{
+ 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_DUMMY:
+ return new dummy_node(this);
+ }
+ charinfo *tem = ci->get_translation();
+ if (tem)
+ ci = tem;
+ macro *mac = ci->get_macro();
+ if (mac) {
+ res = charinfo_to_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 vertical_size_node::same(node *nd)
+{
+ return n == ((vertical_size_node *)nd)->n;
+}
+
+const char *vertical_size_node::type()
+{
+ return "vertical_size_node";
+}
+
+int hmotion_node::same(node *nd)
+{
+ return n == ((hmotion_node *)nd)->n;
+}
+
+const char *hmotion_node::type()
+{
+ return "hmotion_node";
+}
+
+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 vmotion_node::same(node *nd)
+{
+ return n == ((vmotion_node *)nd)->n;
+}
+
+const char *vmotion_node::type()
+{
+ return "vmotion_node";
+}
+
+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 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 dummy_node::same(node * /*nd*/)
+{
+ return 1;
+}
+
+const char *dummy_node::type()
+{
+ return "dummy_node";
+}
+
+int transparent_dummy_node::same(node * /*nd*/)
+{
+ return 1;
+}
+
+const char *transparent_dummy_node::type()
+{
+ return "transparent_dummy_node";
+}
+
+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 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";
+}
+
+
+left_italic_corrected_node::left_italic_corrected_node(node *x)
+: n(0), node(x)
+{
+}
+
+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::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 bracket_node::same(node *nd)
+{
+ return same_node_list(list, ((bracket_node *)nd)->list);
+}
+
+const char *bracket_node::type()
+{
+ return "bracket_node";
+}
+
+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 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 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 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 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 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 line_start_node::same(node * /*nd*/)
+{
+ return 1;
+}
+
+const char *line_start_node::type()
+{
+ return "line_start_node";
+}
+
+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 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";
+}
+
+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_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";
+}
+
+// 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 *));
+ 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) {
+ fm = font::load_font(external_name.contents());
+ if (!fm) {
+ font_dictionary.lookup(external_name, &a_char);
+ return 0;
+ }
+ font_dictionary.lookup(name, fm);
+ }
+ else if (p == &a_char) {
+ error("invalid font `%1'", external_name.contents());
+ 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);
+ 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);
+ 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)
+: nm(s), map_size(10)
+{
+ map = new int[map_size];
+ for (int i = 0; i < map_size; i++)
+ map[i] = -1;
+}
+
+font_family::~font_family()
+{
+ 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));
+ 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;
+}
+
+static void 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()
+{
+ for (int 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 fs.to_units()/3;
+ else
+ return font_table[fn]->get_space_width(fs);
+}
+
+
+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) {
+ if (has_arg()) {
+ units offset;
+ if (get_number(&offset, 'u')) {
+ if (offset >= 1)
+ font_table[f]->set_conditional_bold(n, hunits(offset - 1));
+ else
+ font_table[f]->conditional_unbold(n);
+ }
+ }
+ else
+ font_table[f]->conditional_unbold(n);
+ }
+ }
+ else {
+ units offset;
+ if (get_number(&offset, 'u')) {
+ if (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) {
+ if (has_arg()) {
+ int min_s, max_s;
+ hunits min_a, max_a;
+ if (!get_number(&min_s, 'z')
+ || !get_hunits(&min_a, 'p')
+ || !get_number(&max_s, 'z')
+ || !get_hunits(&max_a, 'p'))
+ error("bad arguments for track kerning");
+ else {
+ 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 x, y;
+ int n = get_fontno();
+ if (n >= 0) {
+ if (!has_arg())
+ font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
+ else if (get_integer(&x)) {
+ if (!has_arg())
+ font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
+ else if (get_number(&y, 'z'))
+ font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
+ scale(y*x,
+ units_per_inch,
+ 36*72*sizescale));
+ }
+ }
+ skip_line();
+}
+
+void ligature()
+{
+ if (has_arg()) {
+ int lig;
+ if (get_integer(&lig)) {
+ if (lig > 2 || lig < 0)
+ lig = 1;
+ global_ligature_mode = lig;
+ }
+ }
+ else
+ global_ligature_mode = 1;
+ skip_line();
+}
+
+void kern_request()
+{
+ if (has_arg()) {
+ int k;
+ if (get_integer(&k))
+ global_kern_mode = k != 0;
+ }
+ else
+ global_kern_mode = 1;
+ 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 itoa(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);
+ 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);
+}
+
diff --git a/troff/node.h b/troff/node.h
new file mode 100644
index 000000000..63b8d6cbf
--- /dev/null
+++ b/troff/node.h
@@ -0,0 +1,522 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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();
+ node(node *n);
+ node *add_char(charinfo *c, environment *, hunits *widthp);
+
+ virtual ~node();
+ virtual node *copy() = 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);
+ 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 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 *);
+ 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;
+ space_node(hunits, 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);
+ void freeze_space();
+ 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 *);
+ const char *type();
+};
+
+class word_space_node : public space_node {
+protected:
+ word_space_node(hunits, int, node * = 0);
+public:
+ word_space_node(hunits, node * = 0);
+ node *copy();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+};
+
+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 same(node *);
+ const char *type();
+ 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);
+};
+
+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();
+};
+
+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();
+};
+
+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();
+};
+
+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 same(node *);
+ const char *type();
+};
+
+class hmotion_node : public node {
+protected:
+ hunits n;
+public:
+ hmotion_node(hunits i, node *next = 0) : node(next), n(i) {}
+ node *copy();
+ void tprint(troff_output_file *);
+ hunits width();
+ void ascii_print(ascii_output_file *);
+ int same(node *);
+ const char *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();
+};
+
+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();
+};
+
+
+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();
+};
+
+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();
+};
+
+
+class dummy_node : public node {
+ public:
+ dummy_node(node *nd = 0) : node(nd) {}
+ node *copy();
+ int same(node *);
+ const char *type();
+ hyphenation_type get_hyphenation_type();
+};
+
+class transparent_dummy_node : public node {
+public:
+ transparent_dummy_node() {}
+ node *copy();
+ int same(node *);
+ const char *type();
+ 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();
+ 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();
+ 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();
+};
+
+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();
+};
+
+class special_node : public node {
+ macro mac;
+ 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 &);
+ node *copy();
+ void tprint(troff_output_file *);
+ int same(node *);
+ const char *type();
+};
+
+class charinfo;
+
+class composite_node : public node {
+ charinfo *ci;
+ node *n;
+ font_size sz;
+public:
+ composite_node(node *, charinfo *, font_size, node * = 0);
+ ~composite_node();
+ node *copy();
+ hunits width();
+ node *last_char_node();
+ units size();
+ void tprint(troff_output_file *);
+ hyphenation_type get_hyphenation_type();
+ int overlaps_horizontally();
+ int overlaps_vertically();
+ 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();
+ void vertical_extent(vunits *, vunits *);
+ vunits vertical_width();
+};
+
+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();
+};
+
+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 *);
+
+hunits env_digit_width(environment *);
+hunits env_space_width(environment *);
+hunits env_narrow_space_width(environment *);
+hunits env_half_narrow_space_width(environment *);
+int get_bold_fontno(int f);
+
+inline hyphen_list::hyphen_list(unsigned char code, hyphen_list *p)
+: hyphenation_code(code), next(p), hyphen(0), breakable(0)
+{
+}
+
+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 flush() = 0;
+ virtual void transparent_char(unsigned char) = 0;
+ virtual void print_line(hunits x, vunits y, node *n,
+ vunits before, vunits after) = 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;
+#ifdef COLUMN
+ virtual void vjustify(vunits, symbol);
+#endif /* COLUMN */
+};
+
+extern char *pipe_command;
+
+extern output_file *the_output;
+extern void init_output();
+int in_output_page_list(int n);
+
+static void invalidate_fontno(int);
+
+class font_family {
+ int *map;
+ int map_size;
+public:
+ const symbol nm;
+
+ font_family(symbol);
+ ~font_family();
+ make_definite(int);
+ friend void invalidate_fontno(int);
+};
+
+font_family *lookup_family(symbol);
diff --git a/troff/number.c b/troff/number.c
new file mode 100644
index 000000000..6dfdbce41
--- /dev/null
+++ b/troff/number.c
@@ -0,0 +1,663 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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);
+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;
+}
+
+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);
+
+static int parse_expr(units *v, int scale_indicator, int parenthesised)
+{
+ int result = parse_term(v, scale_indicator, parenthesised);
+ 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))
+ 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 negative = 0;
+ for (;;)
+ if (parenthesised && tok.space())
+ tok.next();
+ else if (tok.ch() == '+')
+ tok.next();
+ else if (tok.ch() == '-') {
+ tok.next();
+ negative = !negative;
+ }
+ else if (tok.is_size()) {
+ tok.process();
+ tok.next();
+ }
+ 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))
+ return 0;
+ int tem = (scale_indicator == 'v'
+ ? curdiv->get_vertical_position().to_units()
+ : curenv->get_input_line_position().to_units());
+ if (*v < INT_MIN + 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 == ')') {
+ 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))
+ return 0;
+ tok.skip();
+ if (tok.ch() != ')') {
+ 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 - (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 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;
+ 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;
+ }
+ tok.next();
+ }
+ 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 (tok.is_size()) {
+ tok.process();
+ 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/troff/reg.c b/troff/reg.c
new file mode 100644
index 000000000..ce71113df
--- /dev/null
+++ b/troff/reg.c
@@ -0,0 +1,458 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 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 itoa(value);
+ else if (width > sizeof(buf) - 2)
+ sprintf(buf, "%.*d", 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 itoa(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 + 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);
+ 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 (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/troff/reg.h b/troff/reg.h
new file mode 100644
index 000000000..f84281293
--- /dev/null
+++ b/troff/reg.h
@@ -0,0 +1,73 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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);
+
+reg *lookup_number_reg(symbol);
+#if 0
+void inline_define_reg();
+#endif
diff --git a/troff/request.h b/troff/request.h
new file mode 100644
index 000000000..405a8ee4e
--- /dev/null
+++ b/troff/request.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 invoke(symbol);
+ macro *to_macro();
+ void print_size();
+ friend class string_iterator;
+ friend void chop_macro();
+ friend int operator==(const macro &, const macro &);
+};
+
+extern void init_input_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);
+
+extern int no_break_flag; // indicates whether request was invoked with . or '
+
+class charinfo;
+class environment;
+
+node *charinfo_to_node(charinfo *, const environment *);
diff --git a/troff/symbol.c b/troff/symbol.c
new file mode 100644
index 000000000..ff0d7cf5c
--- /dev/null
+++ b/troff/symbol.c
@@ -0,0 +1,140 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "troff.h"
+#include "symbol.h"
+
+const char **symbol::table = 0;
+int symbol::table_used = 0;
+int symbol::table_size = 0;
+char *symbol::block = 0;
+int symbol::block_size = 0;
+
+const symbol NULL_SYMBOL;
+
+const 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;
+}
+
+symbol::symbol(const char *p, int how)
+{
+ if (p == 0 || *p == 0) {
+ s = 0;
+ return;
+ }
+ if (table == 0) {
+ table_size = table_sizes[0];
+ table = new char*[table_size];
+ for (int i = 0; i < table_size; i++)
+ table[i] = 0;
+ table_used = 0;
+ }
+ unsigned int hc = hash_string(p);
+ for (const char **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;
+ for (int 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 = 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 */
+ }
+ 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);
+ delete buf;
+ return res;
+}
+
diff --git a/troff/symbol.h b/troff/symbol.h
new file mode 100644
index 000000000..bb3491042
--- /dev/null
+++ b/troff/symbol.h
@@ -0,0 +1,76 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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();
+ int hash() const;
+ operator ==(symbol) const;
+ 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;
+}
+
+// guaranteed to return a positive integer
+
+inline int symbol::hash() const
+{
+ int i = int(s);
+ return i < 0 ? -i : i;
+}
+
+inline const char *symbol::contents() const
+{
+ return s;
+}
+
+inline int symbol::is_null() const
+{
+ return s == 0;
+}
+
+symbol concat(symbol, symbol);
diff --git a/troff/token.h b/troff/token.h
new file mode 100644
index 000000000..2ce4447b5
--- /dev/null
+++ b/troff/token.h
@@ -0,0 +1,218 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+struct charinfo;
+struct node;
+struct vunits;
+
+// See ARM p251.
+static void process_input_stack();
+
+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_CHAR_HEIGHT, // \H
+ TOKEN_CHAR_SLANT, // \S
+ TOKEN_DUMMY,
+ TOKEN_EMPTY, // this is the initial value
+ TOKEN_END_TRAP,
+ TOKEN_ESCAPE, // \e
+ TOKEN_FONT_NAME, // \f followed by a name
+ TOKEN_FONT_POSITION, // \f followed by a digit
+ 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_SIZE, // \s
+ TOKEN_SPACE, // ` ' -- ordinary space
+ TOKEN_SPECIAL, // a special character -- \' \` \- \(xx
+ TOKEN_SPREAD, // \p -- break and spread output line
+ TOKEN_TAB, // tab
+ TOKEN_TRANSPARENT, // \!
+ 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 it a space or double space?
+ int white_space(); // is the current token space or tab?
+ 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?
+ symbol special();
+ int dummy();
+ int transparent();
+ int left_brace();
+ int right_brace();
+ int page_ejector();
+ 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 changes_env();
+ int is_size();
+ 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 skip_line();
+extern void handle_initial_title();
+
+struct hunits;
+extern void read_title_parts(node **part, hunits *part_width);
+
+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::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 symbol token::special()
+{
+ return type == TOKEN_SPECIAL ? nm : symbol();
+}
+
+inline int token::dummy()
+{
+ return type == TOKEN_DUMMY;
+}
+
+inline int token::is_size()
+{
+ return type == TOKEN_SIZE;
+}
+
+inline int token::left_brace()
+{
+ return type == TOKEN_LEFT_BRACE;
+}
+
+inline int token::right_brace()
+{
+ return type == TOKEN_RIGHT_BRACE;
+}
+
+inline int token::changes_env()
+{
+ return (type == TOKEN_CHAR_HEIGHT
+ || type == TOKEN_CHAR_SLANT
+ || type == TOKEN_FONT_NAME
+ || type == TOKEN_FONT_POSITION
+ || type == TOKEN_SIZE);
+}
+
+inline int token::tab()
+{
+ return type == TOKEN_TAB;
+}
+
+inline int token::leader()
+{
+ return type == TOKEN_LEADER;
+}
+
+inline int token::backspace()
+{
+ return type == TOKEN_BACKSPACE;
+}
+
+int has_arg();
diff --git a/troff/troff.h b/troff/troff.h
new file mode 100644
index 000000000..e2c38ebb8
--- /dev/null
+++ b/troff/troff.h
@@ -0,0 +1,91 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <osfcn.h>
+#include <errno.h>
+
+#include "lib.h"
+#include "assert.h"
+
+#ifdef __GNUG__
+#define NO_RETURN volatile
+#else
+#define NO_RETURN
+#endif
+
+NO_RETURN 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 units points_to_units(units n);
+extern units scale(units, double);
+
+extern const char *device;
+extern int ascii_output_flag;
+extern int suppress_output_flag;
+
+extern int tcommand_flag;
+extern int vresolution;
+extern int hresolution;
+extern int sizescale;
+
+#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,
+ // change WARN_TOTAL if you add more warning types
+};
+
+const int WARN_TOTAL = 0377777;
+
+int warning(warning_type, const char *,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg,
+ const errarg & = empty_errarg);
diff --git a/tty/Makefile b/tty/Makefile
new file mode 100644
index 000000000..1a33240e3
--- /dev/null
+++ b/tty/Makefile
@@ -0,0 +1,83 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+CC=g++
+CFLAGS=-g -O -Wall -Wcast-qual -Wwrite-strings
+LDFLAGS=-g
+OLDCC=gcc
+OLDCFLAGS=-g
+MLIBS=-lm
+INCLUDES=-I../driver -I../lib
+DEFINES=
+BINDIR=/usr/local/bin
+FONTDIR=/usr/local/lib/groff/font
+MACRODIR=/usr/local/lib/groff/tmac
+ETAGS=etags
+ETAGSFLAGS=-p
+DEVICES=devascii devlatin1
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: grotty $(DEVICES)
+
+grotty: tty.o ../driver/libdriver.a ../lib/libgroff.a
+ $(CC) $(LDFLAGS) -o $@ tty.o \
+ ../driver/libdriver.a ../lib/libgroff.a $(MLIBS)
+
+tty.o : ../driver/printer.h ../driver/driver.h ../lib/font.h
+
+install.bin: grotty
+ -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+ -rm -f $(BINDIR)/grotty
+ cp grotty $(BINDIR)
+ @for dir in $(DEVICES); do \
+ echo Making install.bin in $$dir; \
+ (cd $$dir; $(MAKE) "FONTDIR=$(FONTDIR)" install.bin); done
+
+install.nobin:
+ -[ -d $(MACRODIR) ] || mkdir $(MACRODIR)
+ -rm -f $(MACRODIR)/tmac.tty
+ cp tmac.tty $(MACRODIR)
+ @for dir in $(DEVICES); do \
+ echo Making install.nobin in $$dir; \
+ (cd $$dir; $(MAKE) "FONTDIR=$(FONTDIR)" install.nobin); done
+
+install: install.bin install.nobin
+
+clean:
+ -rm -f *.o core grotty
+ @for dir in $(DEVICES); do \
+ echo Making clean in $$dir; \
+ (cd $$dir; $(MAKE) clean); done
+
+
+distclean: clean
+ -rm -f TAGS
+
+realclean: distclean
+
+TAGS: tty.c
+ $(ETAGS) $(ETAGSFLAGS) tty.c
+
+$(DEVICES): FORCE
+ @echo Making all in $@
+ @cd $@; $(MAKE) all
+FORCE:
diff --git a/tty/TODO b/tty/TODO
new file mode 100644
index 000000000..c2472ca98
--- /dev/null
+++ b/tty/TODO
@@ -0,0 +1,6 @@
+Add character types to R.proto.
+
+Option to set number of lines and columns on the command line.
+
+Option not to limit the number of lines on a page and mark the end
+with a formfeed.
diff --git a/tty/devascii/DESC.proto b/tty/devascii/DESC.proto
new file mode 100644
index 000000000..882ff5e47
--- /dev/null
+++ b/tty/devascii/DESC.proto
@@ -0,0 +1,7 @@
+res 240
+hor 24
+vert 40
+unitwidth 10
+sizes 10 0
+fonts 4 R I B BI
+tcommand
diff --git a/tty/devascii/Makefile b/tty/devascii/Makefile
new file mode 100644
index 000000000..115563c2e
--- /dev/null
+++ b/tty/devascii/Makefile
@@ -0,0 +1,63 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+FONTDIR=/usr/local/lib/groff/font
+DEVICEDIR=$(FONTDIR)/devascii
+RES=240
+CPI=10
+LPI=6
+FONTS=R I B BI
+
+all: $(FONTS) DESC
+
+$(FONTS): R.proto
+ @echo Making $@
+ @-rm -f $@
+ @(charwidth=`expr $(RES) / $(CPI)` ; \
+ sed -e "s/^name [A-Z]*$$/name $@/" \
+ -e "s/^\\([^ ]*\\) [0-9]+ /\\1 $$charwidth /" \
+ -e "s/^spacewidth [0-9]+$$/spacewidth $$charwidth/" \
+ -e "s/^internalname .*$$/internalname $@/" \
+ -e "/^internalname/s/BI/3/" \
+ -e "/^internalname/s/B/2/" \
+ -e "/^internalname/s/I/1/" \
+ -e "/^internalname .*[^ 0-9]/d" \
+ R.proto >$@)
+
+DESC: DESC.proto
+ @echo Making $@
+ @-rm -f $@
+ @sed -e "s/^res .*$$/res $(RES)/" \
+ -e "s/^hor .*$$/hor `expr $(RES) / $(CPI)`/" \
+ -e "s/^vert .*$$/vert `expr $(RES) / $(LPI)`/" \
+ -e "s/^fonts .*$$/fonts `set $(FONTS); echo $$#` $(FONTS)/" \
+ DESC.proto >$@
+
+install.nobin: all
+ -[ -d $(FONTDIR) ] || mkdir $(FONTDIR)
+ -[ -d $(DEVICEDIR) ] || mkdir $(DEVICEDIR)
+ -cd $(DEVICEDIR); rm -f $(FONTS) DESC
+ cp $(FONTS) DESC $(DEVICEDIR)
+
+install.bin:
+
+install: install.bin install.nobin
+
+clean:
+ -rm -f $(FONTS) DESC
diff --git a/tty/devascii/R.proto b/tty/devascii/R.proto
new file mode 100644
index 000000000..29bec7f97
--- /dev/null
+++ b/tty/devascii/R.proto
@@ -0,0 +1,133 @@
+name R
+internalname 0
+spacewidth 24
+charset
+! 24 0 0041
+" 24 0 0042
+lq "
+rq "
+# 24 0 0043
+sh "
+$ 24 0 0044
+Do "
+% 24 0 0045
+& 24 0 0046
+' 24 0 0047
+aa "
+fm "
+( 24 0 0050
+) 24 0 0051
+* 24 0 0052
+** "
++ 24 0 0053
+pl "
+, 24 0 0054
+\- 24 0 0055
+hy "
+- "
+mi "
+. 24 0 0056
+/ 24 0 0057
+sl "
+0 24 0 0060
+1 24 0 0061
+2 24 0 0062
+3 24 0 0063
+4 24 0 0064
+5 24 0 0065
+6 24 0 0066
+7 24 0 0067
+8 24 0 0070
+9 24 0 0071
+: 24 0 0072
+; 24 0 0073
+< 24 0 0074
+la "
+= 24 0 0075
+eq "
+> 24 0 0076
+ra "
+? 24 0 0077
+@ 24 0 0100
+at "
+A 24 0 0101
+B 24 0 0102
+C 24 0 0103
+D 24 0 0104
+E 24 0 0105
+F 24 0 0106
+G 24 0 0107
+H 24 0 0110
+I 24 0 0111
+J 24 0 0112
+K 24 0 0113
+L 24 0 0114
+M 24 0 0115
+N 24 0 0116
+O 24 0 0117
+ci "
+P 24 0 0120
+Q 24 0 0121
+R 24 0 0122
+S 24 0 0123
+T 24 0 0124
+U 24 0 0125
+V 24 0 0126
+W 24 0 0127
+X 24 0 0130
+Y 24 0 0131
+Z 24 0 0132
+[ 24 0 0133
+lB "
+\ 24 0 0134
+rs "
+] 24 0 0135
+rB "
+a^ 24 0 0136
+^ "
+_ 24 0 0137
+ru "
+ul "
+` 24 0 0140
+oq "
+ga "
+a 24 0 0141
+b 24 0 0142
+c 24 0 0143
+d 24 0 0144
+e 24 0 0145
+f 24 0 0146
+g 24 0 0147
+h 24 0 0150
+i 24 0 0151
+j 24 0 0152
+k 24 0 0153
+l 24 0 0154
+m 24 0 0155
+n 24 0 0156
+o 24 0 0157
+bu "
+p 24 0 0160
+q 24 0 0161
+r 24 0 0162
+s 24 0 0163
+t 24 0 0164
+u 24 0 0165
+v 24 0 0166
+w 24 0 0167
+x 24 0 0170
+mu "
+y 24 0 0171
+z 24 0 0172
+lC 24 0 0173
+{ "
+ba 24 0 0174
+or "
+bv "
+br "
+| "
+rC 24 0 0175
+} "
+a~ 24 0 0176
+~ "
+ap "
diff --git a/tty/devlatin1/DESC.proto b/tty/devlatin1/DESC.proto
new file mode 100644
index 000000000..882ff5e47
--- /dev/null
+++ b/tty/devlatin1/DESC.proto
@@ -0,0 +1,7 @@
+res 240
+hor 24
+vert 40
+unitwidth 10
+sizes 10 0
+fonts 4 R I B BI
+tcommand
diff --git a/tty/devlatin1/Makefile b/tty/devlatin1/Makefile
new file mode 100644
index 000000000..13a07508f
--- /dev/null
+++ b/tty/devlatin1/Makefile
@@ -0,0 +1,72 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE. If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+FONTDIR=/usr/local/lib/groff/font
+DEVICEDIR=$(FONTDIR)/devlatin1
+# resolution in units per inch
+RES=240
+# characters per inch
+CPI=10
+# lines per inch
+LPI=6
+FONTS=R I B BI
+
+all: $(FONTS) DESC
+
+$(FONTS): R.proto
+ @echo Making $@
+ @-rm -f $@
+ @(charwidth=`expr $(RES) / $(CPI)` ; \
+ sed -e "s/^name [A-Z]*$$/name $@/" \
+ -e "s/^\\([^ ]*\\) [0-9]+ /\\1 $$charwidth /" \
+ -e "s/^spacewidth [0-9]+$$/spacewidth $$charwidth/" \
+ -e "s/^internalname .*$$/internalname $@/" \
+ -e "/^internalname/s/BI/3/" \
+ -e "/^internalname/s/B/2/" \
+ -e "/^internalname/s/I/1/" \
+ -e "/^internalname .*[^ 0-9]/d" \
+ R.proto >$@)
+
+DESC: DESC.proto
+ @echo Making $@
+ @-rm -f $@
+ @sed -e "s/^res .*$$/res $(RES)/" \
+ -e "s/^hor .*$$/hor `expr $(RES) / $(CPI)`/" \
+ -e "s/^vert .*$$/vert `expr $(RES) / $(LPI)`/" \
+ -e "s/^fonts .*$$/fonts `set $(FONTS); echo $$#` $(FONTS)/" \
+ DESC.proto >$@
+
+install.nobin: all
+ -[ -d $(FONTDIR) ] || mkdir $(FONTDIR)
+ -[ -d $(DEVICEDIR) ] || mkdir $(DEVICEDIR)
+ -cd $(DEVICEDIR); rm -f $(FONTS) DESC
+ cp $(FONTS) DESC $(DEVICEDIR)
+
+install.bin:
+
+install: install.bin install.nobin
+
+clean:
+ -rm -f $(FONTS) DESC
+
+distclean: clean
+
+realclean: distclean
+
+TAGS:
diff --git a/tty/devlatin1/R.proto b/tty/devlatin1/R.proto
new file mode 100644
index 000000000..5d6de0ead
--- /dev/null
+++ b/tty/devlatin1/R.proto
@@ -0,0 +1,320 @@
+name R
+internalname 0
+spacewidth 24
+charset
+! 24 0 0041
+" 24 0 0042
+lq "
+rq "
+# 24 0 0043
+sh "
+$ 24 0 0044
+Do "
+% 24 0 0045
+& 24 0 0046
+' 24 0 0047
+fm "
+( 24 0 0050
+) 24 0 0051
+* 24 0 0052
+** "
++ 24 0 0053
+pl "
+, 24 0 0054
+\- 24 0 0055
+mi "
+. 24 0 0056
+/ 24 0 0057
+sl "
+0 24 0 0060
+1 24 0 0061
+2 24 0 0062
+3 24 0 0063
+4 24 0 0064
+5 24 0 0065
+6 24 0 0066
+7 24 0 0067
+8 24 0 0070
+9 24 0 0071
+: 24 0 0072
+; 24 0 0073
+< 24 0 0074
+la "
+= 24 0 0075
+eq "
+> 24 0 0076
+ra "
+? 24 0 0077
+@ 24 0 0100
+at "
+A 24 0 0101
+B 24 0 0102
+C 24 0 0103
+D 24 0 0104
+E 24 0 0105
+F 24 0 0106
+G 24 0 0107
+H 24 0 0110
+I 24 0 0111
+J 24 0 0112
+K 24 0 0113
+L 24 0 0114
+M 24 0 0115
+N 24 0 0116
+O 24 0 0117
+ci "
+P 24 0 0120
+Q 24 0 0121
+R 24 0 0122
+S 24 0 0123
+T 24 0 0124
+U 24 0 0125
+V 24 0 0126
+W 24 0 0127
+X 24 0 0130
+Y 24 0 0131
+Z 24 0 0132
+[ 24 0 0133
+lB "
+\ 24 0 0134
+rs "
+] 24 0 0135
+rB "
+a^ 24 0 0136
+^ "
+_ 24 0 0137
+ru "
+ul "
+` 24 0 0140
+oq "
+ga "
+a 24 0 0141
+b 24 0 0142
+c 24 0 0143
+d 24 0 0144
+e 24 0 0145
+f 24 0 0146
+g 24 0 0147
+h 24 0 0150
+i 24 0 0151
+j 24 0 0152
+k 24 0 0153
+l 24 0 0154
+m 24 0 0155
+n 24 0 0156
+o 24 0 0157
+bu "
+p 24 0 0160
+q 24 0 0161
+r 24 0 0162
+s 24 0 0163
+t 24 0 0164
+u 24 0 0165
+v 24 0 0166
+w 24 0 0167
+x 24 0 0170
+y 24 0 0171
+z 24 0 0172
+lC 24 0 0173
+{ "
+ba 24 0 0174
+or "
+bv "
+br "
+| "
+rC 24 0 0175
+} "
+a~ 24 0 0176
+~ "
+ap "
+r! 24 0 0241
+char161 "
+ct 24 0 0242
+char162 "
+Po 24 0 0243
+char163 "
+Cs 24 0 0244
+char164 "
+Ye 24 0 0245
+char165 "
+bb 24 0 0246
+char166 "
+sc 24 0 0247
+char167 "
+ad 24 0 0250
+char168 "
+co 24 0 0251
+char169 "
+Of 24 0 0252
+char170 "
+Fo 24 0 0253
+char171 "
+no 24 0 0254
+char172 "
+- 24 0 0255
+hy "
+char173 "
+rg 24 0 0256
+char174 "
+a- 24 0 0257
+char175 "
+de 24 0 0260
+char176 "
++- 24 0 0261
+char177 "
+S2 24 0 0262
+char178 "
+S3 24 0 0263
+char179 "
+aa 24 0 0264
+char180 "
+*m 24 0 0265
+char181 "
+ps 24 0 0266
+char182 "
+md 24 0 0267
+char183 "
+ac 24 0 0270
+char184 "
+S1 24 0 0271
+char185 "
+Om 24 0 0272
+char186 "
+Fc 24 0 0273
+char187 "
+14 24 0 0274
+char188 "
+12 24 0 0275
+char189 "
+34 24 0 0276
+char190 "
+r? 24 0 0277
+char191 "
+`A 24 0 0300
+char192 "
+'A 24 0 0301
+char193 "
+^A 24 0 0302
+char194 "
+~A 24 0 0303
+char195 "
+:A 24 0 0304
+char196 "
+oA 24 0 0305
+char197 "
+AE 24 0 0306
+char198 "
+,C 24 0 0307
+char199 "
+`E 24 0 0310
+char200 "
+'E 24 0 0311
+char201 "
+^E 24 0 0312
+char202 "
+:E 24 0 0313
+char203 "
+`I 24 0 0314
+char204 "
+'I 24 0 0315
+char205 "
+^I 24 0 0316
+char206 "
+:I 24 0 0317
+char207 "
+-D 24 0 0320
+char208 "
+~N 24 0 0321
+char209 "
+`O 24 0 0322
+char210 "
+'O 24 0 0323
+char211 "
+^O 24 0 0324
+char212 "
+~O 24 0 0325
+char213 "
+:O 24 0 0326
+char214 "
+mu 24 0 0327
+char215 "
+/O 24 0 0330
+char216 "
+`U 24 0 0331
+char217 "
+'U 24 0 0332
+char218 "
+^U 24 0 0333
+char219 "
+:U 24 0 0334
+char220 "
+'Y 24 0 0335
+char221 "
+TP 24 0 0336
+char222 "
+ss 24 0 0337
+char223 "
+`a 24 0 0340
+char224 "
+'a 24 0 0341
+char225 "
+^a 24 0 0342
+char226 "
+~a 24 0 0343
+char227 "
+:a 24 0 0344
+char228 "
+oa 24 0 0345
+char229 "
+ae 24 0 0346
+char230 "
+,c 24 0 0347
+char231 "
+`e 24 0 0350
+char232 "
+'e 24 0 0351
+char233 "
+^e 24 0 0352
+char234 "
+:e 24 0 0353
+char235 "
+`i 24 0 0354
+char236 "
+'i 24 0 0355
+char237 "
+^i 24 0 0356
+char238 "
+:i 24 0 0357
+char239 "
+Sd 24 0 0360
+char240 "
+~n 24 0 0361
+char241 "
+`o 24 0 0362
+char242 "
+'o 24 0 0363
+char243 "
+^o 24 0 0364
+char244 "
+~o 24 0 0365
+char245 "
+:o 24 0 0366
+char246 "
+di 24 0 0367
+char247 "
+/o 24 0 0370
+char248 "
+`u 24 0 0371
+char249 "
+'u 24 0 0372
+char250 "
+^u 24 0 0373
+char251 "
+:u 24 0 0374
+char252 "
+'y 24 0 0375
+char253 "
+Tp 24 0 0376
+char254 "
+:y 24 0 0377
+char255 "
diff --git a/tty/tmac.tty b/tty/tmac.tty
new file mode 100644
index 000000000..4343bd9a9
--- /dev/null
+++ b/tty/tmac.tty
@@ -0,0 +1,37 @@
+.nr _C \n(.C
+.cp 0
+.nroff
+.ftr CW B
+.ftr C B
+.ftr CR B
+.po 0
+.de tty-char
+.if !c\\$1 .char \\$1 \\$2
+..
+.tty-char \(14 1/4
+.tty-char \(12 1/2
+.tty-char \(34 1/4
+.tty-char \(fi fi
+.tty-char \(fl fl
+.tty-char \(Fi ffi
+.tty-char \(Fl ffl
+.tty-char \(<- <-
+.tty-char \(-> ->
+.tty-char \(<> <->
+.tty-char \(lA <=
+.tty-char \(rA =>
+.tty-char \(hA <=>
+.tty-char \(ua ^
+.tty-char \(da v
+.tty-char \(uA ^
+.tty-char \(dA v
+.tty-char \(em --
+.tty-char \(en -
+.tty-char \(ru _
+.tty-char \(ul _
+.tty-char \(br |
+.tty-char \(bv |
+.tty-char \(sl /
+.tty-char \(+- +-
+.tty-char \(co (C)
+.cp \n(_C
diff --git a/tty/tty.c b/tty/tty.c
new file mode 100644
index 000000000..fdb953853
--- /dev/null
+++ b/tty/tty.c
@@ -0,0 +1,361 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "driver.h"
+
+#ifndef USHRT_MAX
+#define USHRT_MAX 65535
+#endif
+
+#define DEFAULT_LINES_PER_PAGE 66
+
+#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;
+
+enum { UNDERLINE_MODE = 01, BOLD_MODE = 02 };
+
+// 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
+
+// hpos and vpos must be non-adjacent, to work round a bug in g++ 1.37.1
+
+struct glyph {
+ unsigned short hpos;
+ unsigned short serial;
+ unsigned short vpos;
+ unsigned char code;
+ unsigned char mode;
+};
+
+class tty_printer : public printer {
+ enum { INITIAL_VEC_SIZE = 32 };
+ glyph *vec;
+ int vec_used;
+ int vec_size;
+ int lines_per_page;
+ int columns_per_page;
+public:
+ tty_printer();
+ ~tty_printer();
+ void set_char(int, font *, const environment *, int);
+ void begin_page(int) { }
+ void end_page();
+ font *make_font(const char *);
+};
+
+tty_printer::tty_printer()
+: vec_used(0), vec_size(0), vec(0)
+{
+ if (font::paperlength == 0)
+ lines_per_page = DEFAULT_LINES_PER_PAGE;
+ else if (font::paperlength % font::vert != 0)
+ fatal("paperlength not a multiple of vertical resolution");
+ else
+ lines_per_page = font::paperlength/font::vert;
+ if (lines_per_page > USHRT_MAX || lines_per_page <= 0)
+ fatal("ridiculous paperlength");
+ columns_per_page = font::paperwidth/font::hor;
+ // If columns_per_page is zero, we won't truncate.
+ if (columns_per_page < 0)
+ columns_per_page = 0;
+}
+
+tty_printer::~tty_printer()
+{
+ delete vec;
+}
+
+void tty_printer::set_char(int i, font *f, const environment *env, int w)
+{
+ int h = env->hpos;
+ if (h % font::hor != 0)
+ fatal("horizontal position not a multiple of horizontal resolution");
+ h /= font::hor;
+ if (h < 0) {
+ error("character to the left of first column discarded");
+ return;
+ }
+ if (columns_per_page != 0 && h >= columns_per_page) {
+ error("character to the right of last column discarded");
+ return;
+ }
+ if (h > USHRT_MAX) {
+ error("character with ridiculously large horizontal position discarded");
+ return;
+ }
+ int v = env->vpos;
+ if (v % font::vert != 0)
+ fatal("vertical position not a multiple of vertical resolution");
+ v /= font::vert;
+ // Note that the first output line corresponds to groff position font::vert.
+ if (v <= 0) {
+ error("character above first line discarded");
+ return;
+ }
+ if (v > lines_per_page) {
+ error("character below last line discarded");
+ return;
+ }
+ if (w != font::hor)
+ fatal("width of character not equal to horizontal resolution");
+ if (vec_used >= vec_size) {
+ if (vec_size == 0)
+ vec_size = INITIAL_VEC_SIZE;
+ else {
+ if (vec_size > USHRT_MAX/2) {
+ if (vec_size >= USHRT_MAX)
+ fatal("too many characters on the page");
+ vec_size = USHRT_MAX;
+ }
+ else
+ vec_size *= 2;
+ }
+ glyph *old_vec = vec;
+ vec = new glyph [vec_size];
+ if (vec_used)
+ memcpy(vec, old_vec, vec_used*sizeof(glyph));
+ delete old_vec;
+ }
+ // We need a stable sort, but qsort is not stable, so we fake it.
+ vec[vec_used].serial = vec_used;
+ vec[vec_used].hpos = h;
+ vec[vec_used].vpos = v;
+ vec[vec_used].code = f->get_code(i);
+ vec[vec_used].mode = ((tty_font *)f)->get_mode();
+ vec_used++;
+}
+
+extern "C" {
+static int compare_glyph(void *p1, void *p2)
+{
+ int v1 = ((glyph *)p1)->vpos;
+ int v2 = ((glyph *)p2)->vpos;
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return 1;
+ int h1 = ((glyph *)p1)->hpos;
+ int h2 = ((glyph *)p2)->hpos;
+ if (h1 < h2)
+ return -1;
+ if (h1 > h2)
+ return 1;
+ if (((glyph *)p1)->serial < ((glyph *)p2)->serial)
+ return -1;
+ return 1;
+}
+}
+
+void tty_printer::end_page()
+{
+ qsort(vec, vec_used, sizeof(glyph), compare_glyph);
+ int hpos = 0;
+ int vpos = 1;
+ // We have already discarded characters with vpos < 1 or > lines_per_page.
+ for (int i = 0; i < vec_used; i++) {
+ assert(vpos <= vec[i].vpos);
+ if (!overstrike_flag
+ && i + 1 < vec_used
+ && vec[i].hpos == vec[i + 1].hpos
+ && vec[i].vpos == vec[i + 1].vpos)
+ continue;
+ for (; vpos < vec[i].vpos; vpos++) {
+ putchar('\n');
+ hpos = 0;
+ }
+ if (hpos > vec[i].hpos) {
+ putchar('\b');
+ hpos--;
+ }
+ else {
+ if (horizontal_tab_flag) {
+ for (;;) {
+ int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
+ if (next_tab_pos > vec[i].hpos)
+ break;
+ putchar('\t');
+ hpos = next_tab_pos;
+ }
+ }
+ for (; hpos < vec[i].hpos; hpos++)
+ putchar(' ');
+ }
+ assert(hpos == vec[i].hpos && vpos == vec[i].vpos);
+ if (vec[i].mode & UNDERLINE_MODE) {
+ putchar('_');
+ putchar('\b');
+ }
+ if (vec[i].mode & BOLD_MODE) {
+ putchar(vec[i].code);
+ putchar('\b');
+ }
+ putchar(vec[i].code);
+ hpos++;
+ }
+ if (form_feed_flag) {
+ if (hpos != 0) {
+ putchar('\n');
+ vpos++;
+ }
+ if (vpos <= lines_per_page)
+ putchar('\f');
+ }
+ else {
+ for (; vpos <= lines_per_page; vpos++)
+ putchar('\n');
+ }
+ vec_used = 0;
+}
+
+font *tty_printer::make_font(const char *nm)
+{
+ return tty_font::load_tty_font(nm);
+}
+
+printer *make_printer()
+{
+ return new tty_printer;
+}
+
+static void usage();
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int c;
+ while ((c = getopt(argc, argv, "F:vhfbuoBU")) != EOF)
+ switch(c) {
+ case 'v':
+ {
+ extern const char *version_string;
+ fprintf(stderr, "grotty version %s\n", version_string);
+ fflush(stderr);
+ 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 '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc)
+ do_file("-");
+ else {
+ for (int i = optind; i < argc; i++)
+ do_file(argv[i]);
+ }
+ delete pr;
+ exit(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-hfvbuoBU] [-F dir] [files ...]\n",
+ program_name);
+ exit(1);
+}
diff --git a/xditview/ChangeLog b/xditview/ChangeLog
new file mode 100644
index 000000000..68cf445eb
--- /dev/null
+++ b/xditview/ChangeLog
@@ -0,0 +1,75 @@
+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/xditview/DESC b/xditview/DESC
new file mode 100644
index 000000000..4d0c86177
--- /dev/null
+++ b/xditview/DESC
@@ -0,0 +1,8 @@
+styles R I B BI
+fonts 1 S
+sizes 8 10 12 14 18 24 0
+res 75
+X11
+hor 1
+vert 1
+unitwidth 10
diff --git a/xditview/Dvi.c b/xditview/Dvi.c
new file mode 100644
index 000000000..17eaea1e9
--- /dev/null
+++ b/xditview/Dvi.c
@@ -0,0 +1,537 @@
+#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 -*-times-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+TI -*-times-medium-i-normal--*-100-*-*-*-*-iso8859-1\n\
+TB -*-times-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+TBI -*-times-bold-i-normal--*-100-*-*-*-*-iso8859-1\n\
+CR -*-courier-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+CI -*-courier-medium-o-normal--*-100-*-*-*-*-iso8859-1\n\
+CB -*-courier-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+CBI -*-courier-bold-o-normal--*-100-*-*-*-*-iso8859-1\n\
+HR -*-helvetica-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+HI -*-helvetica-medium-o-normal--*-100-*-*-*-*-iso8859-1\n\
+HB -*-helvetica-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+HBI -*-helvetica-bold-o-normal--*-100-*-*-*-*-iso8859-1\n\
+NR -*-new century schoolbook-medium-r-normal--*-100-*-*-*-*-iso8859-1\n\
+NI -*-new century schoolbook-medium-i-normal--*-100-*-*-*-*-iso8859-1\n\
+NB -*-new century schoolbook-bold-r-normal--*-100-*-*-*-*-iso8859-1\n\
+NBI -*-new century schoolbook-bold-i-normal--*-100-*-*-*-*-iso8859-1\n\
+S -*-symbol-medium-r-normal--*-100-*-*-*-*-adobe-fontspecific\n\
+SS -*-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, "black"},
+ {XtNbackground, XtCBackground, XtRPixel, sizeof (unsigned long),
+ offset(dvi.background), XtRString, "white"},
+ {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 <X11/bitmaps/gray>
+
+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 = XCreateBitmapFromData(XtDisplay (w), XtWindow (w),
+ gray_bits,
+ gray_width, gray_height);
+ values.background = dw->dvi.background;
+ values.stipple = dw->dvi.gray;
+ dw->dvi.fill_GC = XCreateGC (XtDisplay (w), XtWindow (w),
+ GCForeground|GCBackground|GCStipple,
+ &values);
+
+ dw->dvi.fill_type = DVI_FILL_BLACK;
+
+ 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);
+ 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;
+ extern char *malloc ();
+ char *new_map;
+ int cur, req;
+
+ if (current->dvi.font_map_string != request->dvi.font_map_string) {
+ new_map = malloc (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)
+ free (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;
+{
+ char tmpName[sizeof ("/tmp/dviXXXXXX")];
+
+ dw->dvi.tmpFile = 0;
+ if (!dw->dvi.seek) {
+ strcpy (tmpName, "/tmp/dviXXXXXX");
+ mktemp (tmpName);
+ dw->dvi.tmpFile = fopen (tmpName, "w+");
+ unlink (tmpName);
+ }
+ 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;
+
+ 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);
+ XtMakeGeometryRequest (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 begining 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/xditview/Dvi.h b/xditview/Dvi.h
new file mode 100644
index 000000000..5aab7d8c7
--- /dev/null
+++ b/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/xditview/DviChar.c b/xditview/DviChar.c
new file mode 100644
index 000000000..a76fd725a
--- /dev/null
+++ b/xditview/DviChar.c
@@ -0,0 +1,664 @@
+/*
+ * DviChar.c
+ *
+ * Map DVI (ditroff output) character names to
+ * font indexes and back
+ */
+
+# include "DviChar.h"
+
+# define allocHash() ((DviCharNameHash *) malloc (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 ();
+
+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;
+ static dispose_hash(), compute_hash();
+ extern char *malloc();
+
+ 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 *) malloc (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 */},
+{ "\"", /* 34 */},
+{ "#","sh", /* 35 */},
+{ "$","Do", /* 36 */},
+{ "%", /* 37 */},
+{ "&", /* 38 */},
+{ "'", /* 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 */},
+{ "md", "\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 */},
+{ 0, /* 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 */},
+{ "rn", /* 96 */},
+{ "*a", /* 97 */},
+{ "*b", /* 98 */},
+{ "*x", /* 99 */},
+{ "*d", /* 100 */},
+{ "*e", /* 101 */},
+{ "*f", /* 102 */},
+{ "*g", /* 103 */},
+{ "*y", /* 104 */},
+{ "*i", /* 105 */},
+{ 0, /* 106 */},
+{ "*k", /* 107 */},
+{ "*l", /* 108 */},
+{ "*m", "\265", /* 109 */},
+{ "*n", /* 110 */},
+{ "*o", /* 111 */},
+{ "*p", /* 112 */},
+{ "*h", /* 113 */},
+{ "*r", /* 114 */},
+{ "*s", /* 115 */},
+{ "*t", /* 116 */},
+{ "*u", /* 117 */},
+{ 0, /* 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 */},
+{ 0, /* 166 */},
+{ "CL", /* 167 */},
+{ "DI", /* 168 */},
+{ "HE", /* 169 */},
+{ "SP", /* 170 */},
+{ "<>", /* 171 */},
+{ "<-", /* 172 */},
+{ "ua", /* 173 */},
+{ "->", /* 174 */},
+{ "da", /* 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 */},
+{ 0, /* 189 */},
+{ 0, /* 190 */},
+{ "CR", /* 191 */},
+{ "Ah", /* 192 */},
+{ "Im", /* 193 */},
+{ "Re", /* 194 */},
+{ 0, /* 195 */},
+{ "c*", /* 196 */},
+{ "c+", /* 197 */},
+{ "es", /* 198 */},
+{ "ca", /* 199 */},
+{ "cu", /* 200 */},
+{ "sp", /* 201 */},
+{ "ip", /* 202 */},
+{ 0, /* 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 */},
+{ 0, /* 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/xditview/DviChar.h b/xditview/DviChar.h
new file mode 100644
index 000000000..4a8f6bbe6
--- /dev/null
+++ b/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 seperating 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/xditview/DviP.h b/xditview/DviP.h
new file mode 100644
index 000000000..394b10861
--- /dev/null
+++ b/xditview/DviP.h
@@ -0,0 +1,231 @@
+/*
+ * $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;
+ 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;
+ 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/xditview/FontMap.X100 b/xditview/FontMap.X100
new file mode 100644
index 000000000..ab83af21c
--- /dev/null
+++ b/xditview/FontMap.X100
@@ -0,0 +1,17 @@
+TR -*-times-medium-r-normal--*-100-100-100-*-*-iso8859-1
+TI -*-times-medium-i-normal--*-100-100-100-*-*-iso8859-1
+TB -*-times-bold-r-normal--*-100-100-100-*-*-iso8859-1
+TBI -*-times-bold-i-normal--*-100-100-100-*-*-iso8859-1
+CR -*-courier-medium-r-normal--*-100-100-100-*-*-iso8859-1
+CI -*-courier-medium-o-normal--*-100-100-100-*-*-iso8859-1
+CB -*-courier-bold-r-normal--*-100-100-100-*-*-iso8859-1
+CBI -*-courier-bold-o-normal--*-100-100-100-*-*-iso8859-1
+HR -*-helvetica-medium-r-normal--*-100-100-100-*-*-iso8859-1
+HI -*-helvetica-medium-o-normal--*-100-100-100-*-*-iso8859-1
+HB -*-helvetica-bold-r-normal--*-100-100-100-*-*-iso8859-1
+HBI -*-helvetica-bold-o-normal--*-100-100-100-*-*-iso8859-1
+NR -*-new century schoolbook-medium-r-normal--*-100-100-100-*-*-iso8859-1
+NI -*-new century schoolbook-medium-i-normal--*-100-100-100-*-*-iso8859-1
+NB -*-new century schoolbook-bold-r-normal--*-100-100-100-*-*-iso8859-1
+NBI -*-new century schoolbook-bold-i-normal--*-100-100-100-*-*-iso8859-1
+S -*-symbol-medium-r-normal--*-100-100-100-*-*-adobe-fontspecific
diff --git a/xditview/FontMap.X75 b/xditview/FontMap.X75
new file mode 100644
index 000000000..b026bea02
--- /dev/null
+++ b/xditview/FontMap.X75
@@ -0,0 +1,17 @@
+TR -*-times-medium-r-normal--*-100-75-75-*-*-iso8859-1
+TI -*-times-medium-i-normal--*-100-75-75-*-*-iso8859-1
+TB -*-times-bold-r-normal--*-100-75-75-*-*-iso8859-1
+TBI -*-times-bold-i-normal--*-100-75-75-*-*-iso8859-1
+CR -*-courier-medium-r-normal--*-100-75-75-*-*-iso8859-1
+CI -*-courier-medium-o-normal--*-100-75-75-*-*-iso8859-1
+CB -*-courier-bold-r-normal--*-100-75-75-*-*-iso8859-1
+CBI -*-courier-bold-o-normal--*-100-75-75-*-*-iso8859-1
+HR -*-helvetica-medium-r-normal--*-100-75-75-*-*-iso8859-1
+HI -*-helvetica-medium-o-normal--*-100-75-75-*-*-iso8859-1
+HB -*-helvetica-bold-r-normal--*-100-75-75-*-*-iso8859-1
+HBI -*-helvetica-bold-o-normal--*-100-75-75-*-*-iso8859-1
+NR -*-new century schoolbook-medium-r-normal--*-100-75-75-*-*-iso8859-1
+NI -*-new century schoolbook-medium-i-normal--*-100-75-75-*-*-iso8859-1
+NB -*-new century schoolbook-bold-r-normal--*-100-75-75-*-*-iso8859-1
+NBI -*-new century schoolbook-bold-i-normal--*-100-75-75-*-*-iso8859-1
+S -*-symbol-medium-r-normal--*-100-75-75-*-*-adobe-fontspecific
diff --git a/xditview/GXditview.ad b/xditview/GXditview.ad
new file mode 100644
index 000000000..74b83ed8d
--- /dev/null
+++ b/xditview/GXditview.ad
@@ -0,0 +1,41 @@
+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>Prior: PreviousPage()\n\
+ <Key>Select: SelectPage()\n\
+ <Key>Find: OpenFile()
+GXditview.paned.label.translations: #augment \
+ <Btn1Down>: XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
+ <Key>Next: NextPage()\n\
+ <Key>Prior: PreviousPage()\n\
+ <Key>Select: SelectPage()\n\
+ <Key>Find: OpenFile()
+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/xditview/INSTALL b/xditview/INSTALL
new file mode 100644
index 000000000..11c05c194
--- /dev/null
+++ b/xditview/INSTALL
@@ -0,0 +1,22 @@
+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)
+
+- make install.dev (only needed if you want to use -TX75 or -TX100)
+
+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/xditview/Imakefile b/xditview/Imakefile
new file mode 100644
index 000000000..bd025cbda
--- /dev/null
+++ b/xditview/Imakefile
@@ -0,0 +1,64 @@
+ GROFF_LIBDIR = /usr/local/lib/groff
+ GROFF_FONTDIR = $(GROFF_LIBDIR)/font
+ GROFF_FONTPATH = .:$(GROFF_FONTDIR):/usr/local/lib/font:/usr/lib/font
+ GROFF_MACRODIR = $(GROFF_LIBDIR)/tmac
+ DPIS = 75 100
+
+ PROGRAMS = gxditview xtotroff
+ DEPLIBS = XawClientDepLibs
+LOCAL_LIBRARIES = XawClientLibs
+ SRCS1 = xditview.c Dvi.c draw.c font.c lex.c page.c \
+ parse.c XFontName.c DviChar.c device.c
+ OBJS1 = xditview.o Dvi.o draw.o font.o lex.o page.o \
+ parse.o XFontName.o DviChar.o device.o
+ SRCS2 = xtotroff.c XFontName.c DviChar.c
+ OBJS2 = xtotroff.o XFontName.o DviChar.o
+ INCLUDES = -I$(TOOLKITSRC) -I$(TOP)
+ MATHLIB = -lm
+ DEFINES = $(SIGNAL_DEFINES)
+
+ComplexProgramTarget_1(gxditview,$(LOCAL_LIBRARIES),$(MATHLIB))
+NormalProgramTarget(xtotroff,$(OBJS2),$(XDEPLIB),$(XLIB), /**/)
+
+InstallAppDefaults(GXditview)
+
+install.dev: xtotroff
+ -[ -d $(GROFF_MACRODIR) ] || mkdir $(GROFF_MACRODIR)
+ -rm -f $(GROFF_MACRODIR)/tmac.X
+ cp tmac.X $(GROFF_MACRODIR)
+ -[ -d $(GROFF_FONTDIR) ] || mkdir $(GROFF_FONTDIR)
+ @dir=`pwd`; \
+ for dpi in $(DPIS); do \
+ echo Installing devX$$dpi; \
+ [ -d $(GROFF_FONTDIR)/devX$$dpi ] \
+ || mkdir $(GROFF_FONTDIR)/devX$$dpi; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi/DESC; \
+ sed -e "s/res 75/res $$dpi/" DESC >$(GROFF_FONTDIR)/devX$$dpi/DESC; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi/eqnchar; \
+ cp eqnchar $(GROFF_FONTDIR)/devX$$dpi; \
+ (cd $(GROFF_FONTDIR)/devX$$dpi; \
+ $$dir/xtotroff -g $$dir/FontMap.X$$dpi); \
+ [ -d $(GROFF_FONTDIR)/devX$$dpi-12 ] \
+ || mkdir $(GROFF_FONTDIR)/devX$$dpi-12; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi-12/eqnchar; \
+ cp eqnchar $(GROFF_FONTDIR)/devX$$dpi-12; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi-12/DESC; \
+ sed -e "s/res 75/res $$dpi/" -e 's/unitwidth 10/unitwidth 12/' DESC \
+ >$(GROFF_FONTDIR)/devX$$dpi-12/DESC; \
+ sed -e "s/100-$$dpi-$$dpi/120-$$dpi-$$dpi/" \
+ FontMap.X$$dpi >FontMap.X$$dpi-12; \
+ (cd $(GROFF_FONTDIR)/devX$$dpi-12; \
+ $$dir/xtotroff -g $$dir/FontMap.X$$dpi-12); \
+ done
+
+path.h: FORCE
+ @echo \#define FONTPATH \"$(GROFF_FONTPATH)\" >path.h.n
+ @cmp -s path.h.n path.h || cp path.h.n path.h
+ @rm -f path.h.n
+
+depend.o: path.h
+
+clean::
+ for dpi in $(DPIS); do $(RM) FontMap.X$$dpi-12; done
+
+FORCE:
diff --git a/xditview/Makefile b/xditview/Makefile
new file mode 100644
index 000000000..5adfe2757
--- /dev/null
+++ b/xditview/Makefile
@@ -0,0 +1,517 @@
+# Makefile generated by imake - do not edit!
+# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
+#
+# The cpp used on this machine replaces all newlines and multiple tabs and
+# spaces in a macro expansion with a single space. Imake tries to compensate
+# for this, but is not always successful.
+#
+
+###########################################################################
+# Makefile generated from "Imake.tmpl" and <Imakefile>
+# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
+#
+# Platform-specific parameters may be set in the appropriate .cf
+# configuration files. Site-wide parameters may be set in the file
+# site.def. Full rebuilds are recommended if any parameters are changed.
+#
+# If your C preprocessor doesn't define any unique symbols, you'll need
+# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
+# "make Makefile", "make Makefiles", or "make World").
+#
+# If you absolutely can't get imake to work, you'll need to set the
+# variables at the top of each Makefile as well as the dependencies at the
+# bottom (makedepend will do this automatically).
+#
+
+###########################################################################
+# platform-specific configuration parameters - edit sun.cf to change
+
+# platform: $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
+# operating system: SunOS 4.1.1
+
+###########################################################################
+# site-specific configuration parameters - edit site.def to change
+
+# site: $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $
+
+ SHELL = /bin/sh
+
+ TOP = .
+ CURRENT_DIR = .
+
+ AR = ar cq
+ BOOTSTRAPCFLAGS =
+ CC = cc
+
+ COMPRESS = compress
+ CPP = /lib/cpp $(STD_CPP_DEFINES)
+ PREPROCESSCMD = cc -E $(STD_CPP_DEFINES)
+ INSTALL = install
+ LD = ld
+ LINT = lint
+ LINTLIBFLAG = -C
+ LINTOPTS = -axz
+ LN = ln -s
+ MAKE = make
+ MV = mv
+ CP = cp
+ RANLIB = ranlib
+ RANLIBINSTFLAGS =
+ RM = rm -f
+ STD_INCLUDES =
+ STD_CPP_DEFINES =
+ STD_DEFINES =
+ EXTRA_LOAD_FLAGS =
+ EXTRA_LIBRARIES =
+ TAGS = ctags
+
+ SHAREDCODEDEF = -DSHAREDCODE
+ SHLIBDEF = -DSUNSHLIB
+
+ PROTO_DEFINES =
+
+ INSTPGMFLAGS =
+
+ INSTBINFLAGS = -m 0755
+ INSTUIDFLAGS = -m 4755
+ INSTLIBFLAGS = -m 0664
+ INSTINCFLAGS = -m 0444
+ INSTMANFLAGS = -m 0444
+ INSTDATFLAGS = -m 0444
+ INSTKMEMFLAGS = -m 4755
+
+ DESTDIR =
+
+ TOP_INCLUDES = -I$(INCROOT)
+
+ CDEBUGFLAGS = -O
+ CCOPTIONS =
+ COMPATFLAGS =
+
+ ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
+ ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
+ CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
+ LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
+ LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+ LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
+ LDCOMBINEFLAGS = -X -r
+
+ MACROFILE = sun.cf
+ RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
+
+ IMAKE_DEFINES =
+
+ IRULESRC = $(CONFIGDIR)
+ IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+ ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
+ $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
+ $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
+
+###########################################################################
+# X Window System Build Parameters
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+###########################################################################
+# X Window System make variables; this need to be coordinated with rules
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+ PATHSEP = /
+ USRLIBDIR = /local/lib
+ BINDIR = /local/bin/X11
+ INCROOT = /local/include
+ BUILDINCROOT = $(TOP)
+ BUILDINCDIR = $(BUILDINCROOT)/X11
+ BUILDINCTOP = ..
+ INCDIR = $(INCROOT)/X11
+ ADMDIR = $(DESTDIR)/usr/adm
+ LIBDIR = $(USRLIBDIR)/X11
+ CONFIGDIR = $(LIBDIR)/config
+ LINTLIBDIR = $(USRLIBDIR)/lint
+
+ FONTDIR = $(LIBDIR)/fonts
+ XINITDIR = $(LIBDIR)/xinit
+ XDMDIR = $(LIBDIR)/xdm
+ AWMDIR = $(LIBDIR)/awm
+ TWMDIR = $(LIBDIR)/twm
+ GWMDIR = $(LIBDIR)/gwm
+ MANPATH = $(DESTDIR)/local/man
+ MANSOURCEPATH = $(MANPATH)/man
+ MANDIR = $(MANSOURCEPATH)1
+ LIBMANDIR = $(MANSOURCEPATH)3
+ XAPPLOADDIR = $(LIBDIR)/app-defaults
+
+ SOXLIBREV = 4.2
+ SOXTREV = 4.0
+ SOXAWREV = 4.0
+ SOOLDXREV = 4.0
+ SOXMUREV = 4.0
+ SOXEXTREV = 4.0
+
+ FONTCFLAGS = -t
+
+ INSTAPPFLAGS = $(INSTDATFLAGS)
+
+ IMAKE = imake
+ DEPEND = makedepend
+ RGB = rgb
+ FONTC = bdftosnf
+ MKFONTDIR = mkfontdir
+ MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
+
+ CONFIGSRC = $(TOP)/config
+ CLIENTSRC = $(TOP)/clients
+ DEMOSRC = $(TOP)/demos
+ LIBSRC = $(TOP)/lib
+ FONTSRC = $(TOP)/fonts
+ INCLUDESRC = $(TOP)/X11
+ SERVERSRC = $(TOP)/server
+ UTILSRC = $(TOP)/util
+ SCRIPTSRC = $(UTILSRC)/scripts
+ EXAMPLESRC = $(TOP)/examples
+ CONTRIBSRC = $(TOP)/../contrib
+ DOCSRC = $(TOP)/doc
+ RGBSRC = $(TOP)/rgb
+ DEPENDSRC = $(UTILSRC)/makedepend
+ IMAKESRC = $(CONFIGSRC)
+ XAUTHSRC = $(LIBSRC)/Xau
+ XLIBSRC = $(LIBSRC)/X
+ XMUSRC = $(LIBSRC)/Xmu
+ TOOLKITSRC = $(LIBSRC)/Xt
+ AWIDGETSRC = $(LIBSRC)/Xaw
+ OLDXLIBSRC = $(LIBSRC)/oldX
+ XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+ BDFTOSNFSRC = $(FONTSRC)/bdftosnf
+ MKFONTDIRSRC = $(FONTSRC)/mkfontdir
+ EXTENSIONSRC = $(TOP)/extensions
+
+ DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a
+ EXTENSIONLIB = -lXext
+
+ DEPXLIB = $(DEPEXTENSIONLIB)
+ XLIB = $(EXTENSIONLIB) -lX11
+
+ DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+ XAUTHLIB = -lXau
+
+ DEPXMULIB =
+ XMULIB = -lXmu
+
+ DEPOLDXLIB =
+ OLDXLIB = -loldX
+
+ DEPXTOOLLIB =
+ XTOOLLIB = -lXt
+
+ DEPXAWLIB =
+ XAWLIB = -lXaw
+
+ LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
+ LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
+ LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
+ LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
+ LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
+
+ DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+ DEPLIBS1 = $(DEPLIBS)
+ DEPLIBS2 = $(DEPLIBS)
+ DEPLIBS3 = $(DEPLIBS)
+
+###########################################################################
+# Imake rules for building libraries, programs, scripts, and data files
+# rules: $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
+
+###########################################################################
+# start of Imakefile
+
+ GROFF_LIBDIR = /usr/local/lib/groff
+ GROFF_FONTDIR = $(GROFF_LIBDIR)/font
+GROFF_FONTPATH = .:$(GROFF_FONTDIR):/usr/local/lib/font:/usr/lib/font
+ GROFF_MACRODIR = $(GROFF_LIBDIR)/tmac
+ DPIS = 75 100
+
+ PROGRAMS = gxditview xtotroff
+ DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+LOCAL_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
+ SRCS1 = xditview.c Dvi.c draw.c font.c lex.c page.c \
+ parse.c XFontName.c DviChar.c device.c
+ OBJS1 = xditview.o Dvi.o draw.o font.o lex.o page.o \
+ parse.o XFontName.o DviChar.o device.o
+ SRCS2 = xtotroff.c XFontName.c DviChar.c
+ OBJS2 = xtotroff.o XFontName.o DviChar.o
+ INCLUDES = -I$(TOOLKITSRC) -I$(TOP)
+ MATHLIB = -lm
+ DEFINES = $(SIGNAL_DEFINES)
+
+ OBJS = $(OBJS1) $(OBJS2) $(OBJS3)
+ SRCS = $(SRCS1) $(SRCS2) $(SRCS3)
+
+all:: $(PROGRAMS)
+
+gxditview: $(OBJS1) $(DEPLIBS1)
+ $(RM) $@
+ $(CC) -o $@ $(LDOPTIONS) $(OBJS1) $(LOCAL_LIBRARIES) $(LDLIBS) $(MATHLIB) $(EXTRA_LOAD_FLAGS)
+
+install:: gxditview
+ $(INSTALL) -c $(INSTPGMFLAGS) gxditview $(BINDIR)
+
+install.man:: gxditview.man
+ $(INSTALL) -c $(INSTMANFLAGS) gxditview.man $(MANDIR)/gxditview.1
+
+saber_gxditview:
+ #load -C $(ALLDEFINES) $(SRCS1) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) $(MATHLIB)
+
+osaber_gxditview:
+ #load -C $(ALLDEFINES) $(OBJS1) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) $(MATHLIB)
+
+depend::
+ $(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(SRCS)
+
+lint:
+ $(LINT) $(LINTFLAGS) $(SRCS) $(LINTLIBS)
+lint1:
+ $(LINT) $(LINTFLAGS) $(FILE) $(LINTLIBS)
+
+clean::
+ $(RM) $(PROGRAMS)
+
+xtotroff: $(OBJS2) $(XDEPLIB)
+ $(RM) $@
+ $(CC) -o $@ $(OBJS2) $(LDOPTIONS) $(XLIB) $(LDLIBS) $(EXTRA_LOAD_FLAGS)
+
+clean::
+ $(RM) xtotroff
+
+install:: GXditview.ad
+ $(INSTALL) -c $(INSTAPPFLAGS) GXditview.ad $(XAPPLOADDIR)/GXditview
+
+install.dev: xtotroff
+ -[ -d $(GROFF_MACRODIR) ] || mkdir $(GROFF_MACRODIR)
+ -rm -f $(GROFF_MACRODIR)/tmac.X
+ cp tmac.X $(GROFF_MACRODIR)
+ -[ -d $(GROFF_FONTDIR) ] || mkdir $(GROFF_FONTDIR)
+ @dir=`pwd`; \
+ for dpi in $(DPIS); do \
+ echo Installing devX$$dpi; \
+ [ -d $(GROFF_FONTDIR)/devX$$dpi ] \
+ || mkdir $(GROFF_FONTDIR)/devX$$dpi; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi/DESC; \
+ sed -e "s/res 75/res $$dpi/" DESC >$(GROFF_FONTDIR)/devX$$dpi/DESC; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi/eqnchar; \
+ cp eqnchar $(GROFF_FONTDIR)/devX$$dpi; \
+ (cd $(GROFF_FONTDIR)/devX$$dpi; \
+ $$dir/xtotroff -g $$dir/FontMap.X$$dpi); \
+ [ -d $(GROFF_FONTDIR)/devX$$dpi-12 ] \
+ || mkdir $(GROFF_FONTDIR)/devX$$dpi-12; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi-12/eqnchar; \
+ cp eqnchar $(GROFF_FONTDIR)/devX$$dpi-12; \
+ rm -f $(GROFF_FONTDIR)/devX$$dpi-12/DESC; \
+ sed -e "s/res 75/res $$dpi/" -e 's/unitwidth 10/unitwidth 12/' DESC \
+ >$(GROFF_FONTDIR)/devX$$dpi-12/DESC; \
+ sed -e "s/100-$$dpi-$$dpi/120-$$dpi-$$dpi/" \
+ FontMap.X$$dpi >FontMap.X$$dpi-12; \
+ (cd $(GROFF_FONTDIR)/devX$$dpi-12; \
+ $$dir/xtotroff -g $$dir/FontMap.X$$dpi-12); \
+ done
+
+path.h: FORCE
+ @echo \#define FONTPATH \"$(GROFF_FONTPATH)\" >path.h.n
+ @cmp -s path.h.n path.h || cp path.h.n path.h
+ @rm -f path.h.n
+
+depend.o: path.h
+
+clean::
+ for dpi in $(DPIS); do $(RM) FontMap.X$$dpi-12; done
+
+FORCE:
+
+###########################################################################
+# common rules for all Makefiles - do not edit
+
+emptyrule::
+
+clean::
+ $(RM_CMD) \#*
+
+Makefile::
+ -@if [ -f Makefile ]; then \
+ echo " $(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
+ $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+ else exit 0; fi
+ $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+ $(TAGS) -w *.[ch]
+ $(TAGS) -xw *.[ch] > TAGS
+
+saber:
+ #load $(ALLDEFINES) $(SRCS)
+
+osaber:
+ #load $(ALLDEFINES) $(OBJS)
+
+###########################################################################
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+ @echo "install in $(CURRENT_DIR) done"
+
+install.man::
+ @echo "install.man in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+###########################################################################
+# dependencies generated by makedepend
+
+# DO NOT DELETE
+
+xditview.o: /local/include/X11/Xatom.h /local/include/X11/Xlib.h
+xditview.o: /usr/include/sys/types.h /usr/include/sys/stdtypes.h
+xditview.o: /usr/include/sys/sysmacros.h /local/include/X11/X.h
+xditview.o: /local/include/X11/Intrinsic.h /local/include/X11/Xutil.h
+xditview.o: /local/include/X11/Xresource.h /local/include/X11/Xos.h
+xditview.o: /usr/include/strings.h /usr/include/sys/file.h
+xditview.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+xditview.o: /usr/include/sys/time.h /usr/include/sys/time.h
+xditview.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+xditview.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+xditview.o: /local/include/X11/RectObj.h /local/include/X11/StringDefs.h
+xditview.o: /local/include/X11/Shell.h /local/include/X11/Xaw/Paned.h
+xditview.o: /local/include/X11/Xaw/Viewport.h /local/include/X11/Xaw/Form.h
+xditview.o: /local/include/X11/Xaw/Box.h /local/include/X11/Xaw/Command.h
+xditview.o: /local/include/X11/Xaw/Label.h /local/include/X11/Xaw/Simple.h
+xditview.o: /local/include/X11/Xmu/Converters.h
+xditview.o: /local/include/X11/Xaw/Dialog.h
+xditview.o: /local/include/X11/Xaw/SimpleMenu.h
+xditview.o: /local/include/X11/Xaw/SmeBSB.h /local/include/X11/Xaw/Sme.h
+xditview.o: /usr/include/signal.h /usr/include/sys/signal.h
+xditview.o: /usr/include/vm/faultcode.h Dvi.h xdit.bm xdit_mask.bm
+xditview.o: /usr/include/stdio.h
+Dvi.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+Dvi.o: /local/include/X11/Xlib.h /usr/include/sys/types.h
+Dvi.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+Dvi.o: /local/include/X11/X.h /local/include/X11/Xutil.h
+Dvi.o: /local/include/X11/Xresource.h /local/include/X11/Xos.h
+Dvi.o: /usr/include/strings.h /usr/include/sys/file.h
+Dvi.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+Dvi.o: /usr/include/sys/time.h /usr/include/sys/time.h
+Dvi.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+Dvi.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+Dvi.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+Dvi.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+Dvi.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+Dvi.o: /local/include/X11/StringDefs.h /local/include/X11/Xmu/Converters.h
+Dvi.o: /usr/include/stdio.h /usr/include/ctype.h DviP.h Dvi.h DviChar.h
+Dvi.o: device.h /local/include/X11/bitmaps/gray
+draw.o: /local/include/X11/Xos.h /usr/include/sys/types.h
+draw.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+draw.o: /usr/include/strings.h /usr/include/sys/file.h
+draw.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+draw.o: /usr/include/sys/time.h /usr/include/sys/time.h
+draw.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+draw.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+draw.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+draw.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+draw.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+draw.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+draw.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+draw.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+draw.o: /local/include/X11/StringDefs.h /usr/include/stdio.h
+draw.o: /usr/include/ctype.h /usr/include/math.h /usr/include/floatingpoint.h
+draw.o: /usr/include/sys/ieeefp.h DviP.h Dvi.h DviChar.h device.h
+font.o: /local/include/X11/Xos.h /usr/include/sys/types.h
+font.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+font.o: /usr/include/strings.h /usr/include/sys/file.h
+font.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+font.o: /usr/include/sys/time.h /usr/include/sys/time.h
+font.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+font.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+font.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+font.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+font.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+font.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+font.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+font.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+font.o: /local/include/X11/StringDefs.h /usr/include/stdio.h
+font.o: /usr/include/ctype.h DviP.h Dvi.h DviChar.h device.h XFontName.h
+lex.o: /local/include/X11/Xos.h /usr/include/sys/types.h
+lex.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+lex.o: /usr/include/strings.h /usr/include/sys/file.h
+lex.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+lex.o: /usr/include/sys/time.h /usr/include/sys/time.h
+lex.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+lex.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+lex.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+lex.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+lex.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+lex.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+lex.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+lex.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+lex.o: /local/include/X11/StringDefs.h /usr/include/stdio.h DviP.h Dvi.h
+lex.o: DviChar.h device.h
+page.o: /local/include/X11/Xos.h /usr/include/sys/types.h
+page.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+page.o: /usr/include/strings.h /usr/include/sys/file.h
+page.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+page.o: /usr/include/sys/time.h /usr/include/sys/time.h
+page.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+page.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+page.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+page.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+page.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+page.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+page.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+page.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+page.o: /local/include/X11/StringDefs.h /usr/include/stdio.h
+page.o: /usr/include/ctype.h DviP.h Dvi.h DviChar.h device.h
+parse.o: /local/include/X11/Xos.h /usr/include/sys/types.h
+parse.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+parse.o: /usr/include/strings.h /usr/include/sys/file.h
+parse.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+parse.o: /usr/include/sys/time.h /usr/include/sys/time.h
+parse.o: /local/include/X11/IntrinsicP.h /local/include/X11/Intrinsic.h
+parse.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+parse.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+parse.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+parse.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+parse.o: /local/include/X11/RectObj.h /local/include/X11/CoreP.h
+parse.o: /local/include/X11/CompositeP.h /local/include/X11/ConstrainP.h
+parse.o: /local/include/X11/ObjectP.h /local/include/X11/RectObjP.h
+parse.o: /local/include/X11/StringDefs.h /usr/include/stdio.h
+parse.o: /usr/include/ctype.h DviP.h Dvi.h DviChar.h device.h
+XFontName.o: /local/include/X11/Xlib.h /usr/include/sys/types.h
+XFontName.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+XFontName.o: /local/include/X11/X.h /local/include/X11/Xos.h
+XFontName.o: /usr/include/strings.h /usr/include/sys/file.h
+XFontName.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+XFontName.o: /usr/include/sys/time.h /usr/include/sys/time.h XFontName.h
+XFontName.o: /usr/include/ctype.h
+DviChar.o: DviChar.h
+device.o: /usr/include/stdio.h /usr/include/ctype.h /local/include/X11/Xos.h
+device.o: /usr/include/sys/types.h /usr/include/sys/stdtypes.h
+device.o: /usr/include/sys/sysmacros.h /usr/include/strings.h
+device.o: /usr/include/sys/file.h /usr/include/sys/fcntlcom.h
+device.o: /usr/include/sys/stat.h /usr/include/sys/time.h
+device.o: /usr/include/sys/time.h /local/include/X11/Intrinsic.h
+device.o: /local/include/X11/Xlib.h /local/include/X11/X.h
+device.o: /local/include/X11/Xutil.h /local/include/X11/Xresource.h
+device.o: /local/include/X11/Core.h /local/include/X11/Composite.h
+device.o: /local/include/X11/Constraint.h /local/include/X11/Object.h
+device.o: /local/include/X11/RectObj.h device.h path.h
+xtotroff.o: /local/include/X11/Xlib.h /usr/include/sys/types.h
+xtotroff.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+xtotroff.o: /local/include/X11/X.h /usr/include/stdio.h /usr/include/ctype.h
+xtotroff.o: XFontName.h DviChar.h
+XFontName.o: /local/include/X11/Xlib.h /usr/include/sys/types.h
+XFontName.o: /usr/include/sys/stdtypes.h /usr/include/sys/sysmacros.h
+XFontName.o: /local/include/X11/X.h /local/include/X11/Xos.h
+XFontName.o: /usr/include/strings.h /usr/include/sys/file.h
+XFontName.o: /usr/include/sys/fcntlcom.h /usr/include/sys/stat.h
+XFontName.o: /usr/include/sys/time.h /usr/include/sys/time.h XFontName.h
+XFontName.o: /usr/include/ctype.h
+DviChar.o: DviChar.h
diff --git a/xditview/Menu.h b/xditview/Menu.h
new file mode 100644
index 000000000..c306b2740
--- /dev/null
+++ b/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/xditview/README b/xditview/README
new file mode 100644
index 000000000..50433c960
--- /dev/null
+++ b/xditview/README
@@ -0,0 +1,13 @@
+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 X11R4 to install it.
+
+See the file INSTALL in this directory for installation instructions.
+
+xditview is copyrighted by MIT under the usual X terms (see
+gxditiew.man); my changes to it are in the public domain.
+
+Please report bugs to me.
+
+James Clark
+jjc@jclark.uucp
diff --git a/xditview/TODO b/xditview/TODO
new file mode 100644
index 000000000..eb69bf915
--- /dev/null
+++ b/xditview/TODO
@@ -0,0 +1,12 @@
+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 accomodate the
+default value.
diff --git a/xditview/XFontName.c b/xditview/XFontName.c
new file mode 100644
index 000000000..5ca9bb81e
--- /dev/null
+++ b/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/xditview/XFontName.h b/xditview/XFontName.h
new file mode 100644
index 000000000..efe9eb16d
--- /dev/null
+++ b/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/xditview/device.c b/xditview/device.c
new file mode 100644
index 000000000..3f49a9961
--- /dev/null
+++ b/xditview/device.c
@@ -0,0 +1,578 @@
+/* device.c */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <X11/Xos.h>
+#include <X11/Intrinsic.h>
+
+#include "device.h"
+#include "path.h"
+
+extern void exit();
+extern char *strtok(), *strchr();
+extern char *getenv();
+
+/* 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"
+
+/* Minimum and maximum values a `signed int' can hold. */
+#define INT_MIN (-INT_MAX-1)
+#define INT_MAX 2147483647
+
+#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(*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, path, result)
+ char *file, *path, **result;
+{
+ char *buf = NULL;
+ int bufsiz = 0;
+ int flen;
+ FILE *fp;
+
+ *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);
+
+ if (!path)
+ return NULL;
+
+ 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);
+ path = getenv(FONTPATH_ENV_VAR);
+ if (!path)
+ path = FONTPATH;
+ fp = find_file(buf, path, 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/xditview/device.h b/xditview/device.h
new file mode 100644
index 000000000..2b9a64bac
--- /dev/null
+++ b/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/xditview/draw.c b/xditview/draw.c
new file mode 100644
index 000000000..11fbee107
--- /dev/null
+++ b/xditview/draw.c
@@ -0,0 +1,742 @@
+/*
+ * 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))
+
+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 (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) (((unsigned char)(c1) << 8) | (unsigned char)(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;
+ case pack2('e', 'm'): /* em dash */
+ chars = "--";
+ break;
+ case pack2('f', '/'): /* fraction slash */
+ chars = "/";
+ break;
+ case pack2('l', 'q'):
+ chars = "``";
+ break;
+ case pack2('r', 'q'):
+ chars = "''";
+ break;
+ case pack2('O', 'E'):
+ chars = "OE";
+ break;
+ case pack2('o', 'e'):
+ chars = "oe";
+ 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 = ((dw->dvi.device_resolution
+ * dw->dvi.state->font_size)
+ / (10*72*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;
+
+ if (dw->dvi.fill == DVI_FILL_MAX)
+ fill_type = DVI_FILL_BLACK;
+ else if (dw->dvi.fill == 0)
+ fill_type = DVI_FILL_WHITE;
+ else
+ fill_type = DVI_FILL_GRAY;
+ if (dw->dvi.fill_type != fill_type) {
+ XGCValues values;
+ switch (fill_type) {
+ case DVI_FILL_WHITE:
+ values.foreground = dw->dvi.background;
+ values.fill_style = FillSolid;
+ break;
+ case DVI_FILL_BLACK:
+ values.foreground = dw->dvi.foreground;
+ values.fill_style = FillSolid;
+ break;
+ case DVI_FILL_GRAY:
+ values.foreground = dw->dvi.foreground;
+ values.fill_style = FillOpaqueStippled;
+ break;
+ }
+ XChangeGC(XtDisplay (dw), dw->dvi.fill_GC,
+ GCFillStyle|GCForeground,
+ &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);
+}
+
+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);
+}
+
+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;
+{
+ extern char *malloc();
+ XPoint *p;
+ int i;
+ int dx, dy;
+
+ n /= 2;
+
+ AdjustCacheDeltas (dw);
+ setGC (dw);
+ p = (XPoint *)malloc((n + 2)*sizeof(XPoint));
+ if (!p)
+ return;
+ 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);
+ free(p);
+}
+
+
+DrawFilledPolygon (dw, v, n)
+ DviWidget dw;
+ int *v;
+ int n;
+{
+ extern char *malloc();
+ XPoint *p;
+ int i;
+ int dx, dy;
+
+ n /= 2;
+ if (n < 2)
+ return;
+
+ AdjustCacheDeltas (dw);
+ setFillGC (dw);
+ p = (XPoint *)malloc((n + 1)*sizeof(XPoint));
+ if (!p)
+ return;
+ 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;
+ }
+ XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
+ p, n + 1, Complex, CoordModeOrigin);
+ free(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/xditview/eqnchar b/xditview/eqnchar
new file mode 100644
index 000000000..386a51887
--- /dev/null
+++ b/xditview/eqnchar
@@ -0,0 +1,13 @@
+.EQ
+sdefine << %{ < back 20 < }%
+sdefine >> %{ > back 20 > }%
+
+sdefine dotdot %accent "\fR\(ad\fP"%
+sdefine vec %accent {up 52 "\s[\En[.s]/2u]\(->\s0"}%
+sdefine dyad %accent {up 52 "\s[\En[.s]/2u]\(<>\s0"}%
+
+sdefine inf %"\s[\En[.s]*13u/10u]\v'12M'\(if\v'-12M'\s0"%
+sdefine cdot %type "binary" \(md%
+
+set axis_height 32
+.EN
diff --git a/xditview/font.c b/xditview/font.c
new file mode 100644
index 000000000..c35057f65
--- /dev/null
+++ b/xditview/font.c
@@ -0,0 +1,429 @@
+/*
+ * 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"
+
+extern char *malloc ();
+
+static char *
+savestr (s)
+ char *s;
+{
+ char *n;
+
+ if (!s)
+ return 0;
+ n = malloc (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 (f, size, sizescale)
+ DviFontList *f;
+ int size, sizescale;
+{
+ DviFontSizeList *fs, *best = 0, *smallest = 0;
+ int bestsize = 0;
+
+ for (fs = f->sizes; fs; fs=fs->next) {
+ if (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)
+ DviWidget dw;
+ char *x_name;
+{
+ char fontNameString[2048];
+ char **fonts;
+ int i, count;
+ int size;
+ DviFontSizeList *sizes, *new;
+ XFontName fontName;
+ unsigned int fontNameAttributes;
+
+ if (!XParseFontName (x_name, &fontName, &fontNameAttributes))
+ return 0;
+ fontNameAttributes &= ~(FontNamePixelSize|FontNamePointSize);
+ 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++) {
+ if ((size = ConvertFontNameToSize (fonts[i])) != -1) {
+ new = (DviFontSizeList *) malloc (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 (fs)
+ DviFontSizeList *fs;
+{
+ DviFontSizeList *next;
+
+ for (; fs; fs=next) {
+ next = fs->next;
+ if (fs->x_name)
+ free (fs->x_name);
+ if (fs->font)
+ XFree ((char *)fs->font);
+ free ((char *) fs);
+ }
+}
+
+static DviFontList *
+InstallFont (dw, position, dvi_name, x_name)
+ DviWidget dw;
+ int position;
+ char *dvi_name;
+ char *x_name;
+{
+ DviFontList *f;
+ DviFontSizeList *sizes;
+ 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;
+
+ sizes = InstallFontSizes (dw, x_name);
+ if (!sizes)
+ return f;
+
+ DisposeFontSizes (f->sizes);
+ if (f->dvi_name)
+ free (f->dvi_name);
+ if (f->x_name)
+ free (f->x_name);
+ f->device_font = 0;
+ } else {
+ sizes = InstallFontSizes (dw, x_name);
+ if (!sizes)
+ return 0;
+ f = (DviFontList *) malloc (sizeof (*f));
+ f->next = dw->dvi.fonts;
+ dw->dvi.fonts = f;
+ }
+ 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 = sizes;
+ 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 (f->sizes);
+ if (f->dvi_name)
+ free (f->dvi_name);
+ if (f->x_name)
+ free (f->x_name);
+ f = f->next;
+ free ((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 *) malloc (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)
+ free (font_map->x_name);
+ if (font_map->dvi_name)
+ free (font_map->dvi_name);
+ free ((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;
+ fs = LookupFontSizeBySize (f, size, dw->dvi.sizescale);
+ 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/xditview/gxditview.man b/xditview/gxditview.man
new file mode 100644
index 000000000..89634341e
--- /dev/null
+++ b/xditview/gxditview.man
@@ -0,0 +1,175 @@
+.\" -*- nroff -*-
+.TH GXDITVIEW 1 "Release 4" "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 .
+.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.
+.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.
+.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
+Malcom Slaney (Schlumberger Palo Alto Research)
+.br
+Mark Moraes (University of Toronto)
+.br
+James Clark
diff --git a/xditview/lex.c b/xditview/lex.c
new file mode 100644
index 000000000..76e86e7ee
--- /dev/null
+++ b/xditview/lex.c
@@ -0,0 +1,104 @@
+#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;
+ char *p = Buffer;
+
+ Length--; /* Save room for final NULL */
+
+ while (i < Length && DviGetC (dw, &c) != EOF && c != '\n')
+ if (p)
+ *p++ = c;
+ if (c == '\n' && p) /* Retain the newline like fgets */
+ *p++ = c;
+ if (c == '\n')
+ DviUngetC(dw, c);
+ if (p)
+ *p = NULL;
+ return (Buffer);
+}
+
+char *
+GetWord(dw, Buffer, Length)
+ DviWidget dw;
+ char *Buffer;
+ int Length;
+{
+ int i = 0, c;
+ char *p = Buffer;
+
+ Length--; /* Save room for final NULL */
+ while (DviGetC(dw, &c) != EOF && (c == ' ' || c == '\n'))
+ ;
+ if (c != EOF)
+ DviUngetC(dw, c);
+ while (i < Length && DviGetC(dw, &c) != EOF && c != ' ' && c != '\n')
+ if (p)
+ *p++ = c;
+ if (c != EOF)
+ DviUngetC(dw, c);
+ if (p)
+ *p = NULL;
+ return (Buffer);
+}
+
+GetNumber(dw)
+ DviWidget dw;
+{
+ int i = 0, c;
+ int negative = 0;
+
+ while (DviGetC(dw, &c) != EOF && (c == ' ' || c == '\n'))
+ ;
+ if (c != EOF)
+ DviUngetC(dw, c);
+
+ while (DviGetC(dw, &c) != EOF && c >= '0' && c <= '9') {
+ 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/xditview/page.c b/xditview/page.c
new file mode 100644
index 000000000..553bb3f13
--- /dev/null
+++ b/xditview/page.c
@@ -0,0 +1,86 @@
+/*
+ * 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"
+
+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;
+ free ((char *) m);
+ }
+}
+
+ForgetPagePositions (dw)
+ DviWidget dw;
+{
+ DestroyFileMap (dw->dvi.file_map);
+ dw->dvi.file_map = 0;
+}
+
+RememberPagePosition(dw, number)
+ DviWidget dw;
+ int number;
+{
+ extern long ftell();
+ DviFileMap *m;
+ extern char *malloc();
+
+ if (!(m = MapPageNumberToFileMap (dw, number))) {
+ m = (DviFileMap *) malloc (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/xditview/parse.c b/xditview/parse.c
new file mode 100644
index 000000000..2abe81404
--- /dev/null
+++ b/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 '+': /* 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;
+ extern char *malloc();
+
+ new = (DviState *) malloc (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;
+ free ((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/xditview/path.h b/xditview/path.h
new file mode 100644
index 000000000..66c66aae5
--- /dev/null
+++ b/xditview/path.h
@@ -0,0 +1 @@
+#define FONTPATH ".:/usr/local/lib/groff/font:/usr/local/lib/font:/usr/lib/font"
diff --git a/xditview/tmac.X b/xditview/tmac.X
new file mode 100644
index 000000000..13e2862f3
--- /dev/null
+++ b/xditview/tmac.X
@@ -0,0 +1,34 @@
+.nr _C \n(.C
+.cp 0
+.ftr CW CR
+.ftr C CR
+.ftr CO CI
+.ftr CX CBI
+.ftr H HR
+.ftr HO HI
+.ftr HX HBI
+.ftr NX NBI
+.char \(ru \D'l .5m 0'
+.char \(ul \v'.25m'\D'l .5m 0'\v'-.25m'
+.char \(br \v'.25m'\D'l 0 -1m'\v'.75m'
+.char ~ \v'-.55m'\\s[\\n(.s/2u]\v'.2m'\(ti\v'-.2m'\s0\v'.55m'
+.char ^ \v'-.55m'\\s[\\n(.s/2u]\v'.3m'\(ha\v'-.3m'\s0\v'.55m'
+.if !c\(em .char \(em --
+.if !c\(fi .char \(fi fi
+.if !c\(fl .char \(fl fl
+.if !c\(ff .char \(ff ff
+.if !c\(Fi .char \(Fi ffi
+.if !c\(Fl .char \(Fl ffl
+.if !c\(ci .char \(ci \v'-.25m'\h'.05m'\D'c .5m'\h'.05m'\v'.25m'
+.if !c\(sq .char \(sq \h'.05m'\D'l .5m 0'\D'l 0 -.5m'\D'l -.5m 0'\D'l 0 .5m'\h'.55m'
+.if !c\(ga .char \(ga \Z'\v'-.7m'\D'l .22m .18m''\h'.33m'
+.if !c\(dg .char \(dg \Z'\h'.25m'\v'.15m'\D'l 0 -.8m'\v'.2m'\h'-.195m'\
+\D'l .39m 0''\h'.5m'
+.if !c\(dd .char \(dd \Z'\h'.25m'\v'.15m'\D'l 0 -.8m'\v'.2m'\h'-.195m'\
+\D'l .39m 0'\v'.4m'\D'l -.39m 0''\h'.5m'
+.if !c\(lq .char \(lq ``
+.if !c\(rq .char \(rq ''
+.if !c\(lh .tr \(lh\(lA
+.if !c\(rh .tr \(rh\(rA
+.if '\*(.T'X100' .char \(rn \h'-\w'\(sr'u'\(rn\h'\w'\(sr'u'
+.cp \n(_C
diff --git a/xditview/xdit.bm b/xditview/xdit.bm
new file mode 100644
index 000000000..67b9c8ab5
--- /dev/null
+++ b/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/xditview/xdit_mask.bm b/xditview/xdit_mask.bm
new file mode 100644
index 000000000..f34a4f863
--- /dev/null
+++ b/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/xditview/xditview.c b/xditview/xditview.c
new file mode 100644
index 000000000..c984a9b9e
--- /dev/null
+++ b/xditview/xditview.c
@@ -0,0 +1,533 @@
+/*
+ * 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/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 struct app_resources {
+ char *print_command;
+} 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},
+};
+
+#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},
+{"-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]\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();
+
+XtActionsRec xditview_actions[] = {
+ "NextPage", NextPageAction,
+ "PreviousPage", PreviousPageAction,
+ "SelectPage", SelectPageAction,
+ "Print", PrintAction,
+ "OpenFile", OpenFileAction,
+ "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>";
+
+void main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *file_name = 0;
+ int i;
+ static Arg labelArgs[] = {
+ {XtNlabel, (XtArgVal) pageLabel},
+ };
+ Arg topLevelArgs[2];
+ Widget entry;
+ Arg pageNumberArgs[1];
+ int page_number;
+
+ toplevel = XtInitialize("main", "GXditview",
+ options, XtNumber (options),
+ &argc, argv);
+ 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(XtWidgetToApplicationContext(toplevel),
+ 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);
+ XtRealizeWidget (toplevel);
+ if (file_name)
+ SetPageNumber (page_number);
+ XtMainLoop();
+}
+
+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, *rindex ();
+ 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 = rindex (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);
+}
+
+/* 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, NULL},
+ {XtNvalue, NULL},
+ };
+ 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;
+}
+
+/*
+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/xditview/xtotroff.c b/xditview/xtotroff.c
new file mode 100644
index 000000000..3004d065a
--- /dev/null
+++ b/xditview/xtotroff.c
@@ -0,0 +1,207 @@
+/*
+ * xtotroff
+ *
+ * convert X font metrics into troff font metrics
+ */
+
+# include <X11/Xlib.h>
+# include <stdio.h>
+# include <ctype.h>
+# include "XFontName.h"
+# include "DviChar.h"
+
+# 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;
+
+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);
+}
+
+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;
+
+ names = XListFonts (dpy, font_name, 100000, &count);
+ if (count < 1) {
+ fprintf (stderr, "bad font name: %s\n", font_name);
+ return 0;
+ }
+ if (count > 1) {
+ fprintf (stderr, "ambiguous font name: %s\n", font_name);
+ return 0;
+ }
+ if (!XParseFontName (names[0], &parsed, &attributes)) {
+ fprintf (stderr, "not a standard name: %s\n", names[0]);
+ return 0;
+ }
+ 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;
+ }
+
+ (void) unlink (troff_name);
+ out = fopen (troff_name, "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");
+ fi = XLoadQueryFont (dpy, names[0]);
+ 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] FontMap\n", prog);
+ exit (1);
+}
+
+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;
+
+ while ((opt = getopt(argc, argv, "g")) != EOF) {
+ switch (opt) {
+ case 'g':
+ groff_flag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ 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';
+ printf ("%s -> %s\n", font_name, troff_name);
+ 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:
+*/