summaryrefslogtreecommitdiff
path: root/sys/src/cmd/postscript
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/postscript
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/postscript')
-rwxr-xr-xsys/src/cmd/postscript/NOTES32
-rwxr-xr-xsys/src/cmd/postscript/README179
-rwxr-xr-xsys/src/cmd/postscript/VERSION372
-rwxr-xr-xsys/src/cmd/postscript/buildtables/README5
-rwxr-xr-xsys/src/cmd/postscript/buildtables/buildtables.1214
-rwxr-xr-xsys/src/cmd/postscript/buildtables/buildtables.mk64
-rwxr-xr-xsys/src/cmd/postscript/buildtables/buildtables.sh94
-rwxr-xr-xsys/src/cmd/postscript/changes95
-rwxr-xr-xsys/src/cmd/postscript/common/bbox.c257
-rwxr-xr-xsys/src/cmd/postscript/common/comments.h127
-rwxr-xr-xsys/src/cmd/postscript/common/common.c264
-rwxr-xr-xsys/src/cmd/postscript/common/common.h43
-rwxr-xr-xsys/src/cmd/postscript/common/ext.h40
-rwxr-xr-xsys/src/cmd/postscript/common/gen.h65
-rwxr-xr-xsys/src/cmd/postscript/common/getopt.c56
-rwxr-xr-xsys/src/cmd/postscript/common/glob.c29
-rwxr-xr-xsys/src/cmd/postscript/common/misc.c230
-rwxr-xr-xsys/src/cmd/postscript/common/mkfile24
-rwxr-xr-xsys/src/cmd/postscript/common/path.h32
-rwxr-xr-xsys/src/cmd/postscript/common/request.c119
-rwxr-xr-xsys/src/cmd/postscript/common/request.h22
-rwxr-xr-xsys/src/cmd/postscript/common/rune.c142
-rwxr-xr-xsys/src/cmd/postscript/common/rune.h19
-rwxr-xr-xsys/src/cmd/postscript/common/tempnam.c36
-rwxr-xr-xsys/src/cmd/postscript/config11
-rwxr-xr-xsys/src/cmd/postscript/cropmarks/cropmarks.ps131
-rwxr-xr-xsys/src/cmd/postscript/cropmarks/cropmarks.rc74
-rwxr-xr-xsys/src/cmd/postscript/cropmarks/mkfile22
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/C1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/C2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/C3161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F3161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F4161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F5161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/F6161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/G1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/G2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/G3161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/G4161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/Gb161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/Gi161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/Gr161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/Gx161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H3161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H4161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H5161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H6161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H7161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/H8161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/HC161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/HK161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/HL161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/HY161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/MU173
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/OA161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/OB161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/OI161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/OX161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/a1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/a2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/c1161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/c2161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/c3161
-rwxr-xr-xsys/src/cmd/postscript/devpost.add/devpost.add.mk49
-rwxr-xr-xsys/src/cmd/postscript/download/README11
-rwxr-xr-xsys/src/cmd/postscript/download/download.c543
-rwxr-xr-xsys/src/cmd/postscript/download/download.h14
-rwxr-xr-xsys/src/cmd/postscript/download/mkfile27
-rwxr-xr-xsys/src/cmd/postscript/g3p9bit/btab109
-rwxr-xr-xsys/src/cmd/postscript/g3p9bit/g3p9bit.c455
-rwxr-xr-xsys/src/cmd/postscript/g3p9bit/mkfile14
-rwxr-xr-xsys/src/cmd/postscript/g3p9bit/wtab109
-rwxr-xr-xsys/src/cmd/postscript/grabit/grabit.ps522
-rwxr-xr-xsys/src/cmd/postscript/grabit/grabit.rc64
-rwxr-xr-xsys/src/cmd/postscript/grabit/mkfile20
-rwxr-xr-xsys/src/cmd/postscript/hardcopy/README11
-rwxr-xr-xsys/src/cmd/postscript/hardcopy/hardcopy.ps196
-rwxr-xr-xsys/src/cmd/postscript/hardcopy/hardcopy.rc70
-rwxr-xr-xsys/src/cmd/postscript/hardcopy/mkfile20
-rwxr-xr-xsys/src/cmd/postscript/mcolor/color.sr45
-rwxr-xr-xsys/src/cmd/postscript/mcolor/mcolor.560
-rwxr-xr-xsys/src/cmd/postscript/mcolor/mcolor.mk53
-rwxr-xr-xsys/src/cmd/postscript/misc/README15
-rwxr-xr-xsys/src/cmd/postscript/misc/ibmfont.c293
-rwxr-xr-xsys/src/cmd/postscript/misc/laserbar.141
-rwxr-xr-xsys/src/cmd/postscript/misc/laserbar.c166
-rwxr-xr-xsys/src/cmd/postscript/misc/lp.model66
-rwxr-xr-xsys/src/cmd/postscript/misc/macfont.c296
-rwxr-xr-xsys/src/cmd/postscript/misc/pscrypt.c333
-rwxr-xr-xsys/src/cmd/postscript/misc/setbaud.ps9
-rwxr-xr-xsys/src/cmd/postscript/mkfile50
-rwxr-xr-xsys/src/cmd/postscript/mpictures/mkfile18
-rwxr-xr-xsys/src/cmd/postscript/mpictures/pictures.sr154
-rwxr-xr-xsys/src/cmd/postscript/p9bitpost/mkfile17
-rwxr-xr-xsys/src/cmd/postscript/p9bitpost/p9bitpost.c105
-rwxr-xr-xsys/src/cmd/postscript/p9bitpost/pslib.c813
-rwxr-xr-xsys/src/cmd/postscript/p9bitpost/pslib.h6
-rwxr-xr-xsys/src/cmd/postscript/picpack/mkfile57
-rwxr-xr-xsys/src/cmd/postscript/picpack/picpack.1123
-rwxr-xr-xsys/src/cmd/postscript/picpack/picpack.c441
-rwxr-xr-xsys/src/cmd/postscript/picpack/picpack.mk75
-rwxr-xr-xsys/src/cmd/postscript/postbgi/README21
-rwxr-xr-xsys/src/cmd/postscript/postbgi/mkfile61
-rwxr-xr-xsys/src/cmd/postscript/postbgi/postbgi.1243
-rwxr-xr-xsys/src/cmd/postscript/postbgi/postbgi.c1523
-rwxr-xr-xsys/src/cmd/postscript/postbgi/postbgi.h203
-rwxr-xr-xsys/src/cmd/postscript/postbgi/postbgi.mk93
-rwxr-xr-xsys/src/cmd/postscript/postbgi/postbgi.ps135
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/Opostdaisy.c1222
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/README4
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/mkfile62
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/postdaisy.1217
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/postdaisy.c1225
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/postdaisy.h88
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/postdaisy.mk93
-rwxr-xr-xsys/src/cmd/postscript/postdaisy/postdaisy.ps74
-rwxr-xr-xsys/src/cmd/postscript/postdmd/README22
-rwxr-xr-xsys/src/cmd/postscript/postdmd/mkfile61
-rwxr-xr-xsys/src/cmd/postscript/postdmd/postdmd.1206
-rwxr-xr-xsys/src/cmd/postscript/postdmd/postdmd.c729
-rwxr-xr-xsys/src/cmd/postscript/postdmd/postdmd.mk92
-rwxr-xr-xsys/src/cmd/postscript/postdmd/postdmd.ps124
-rwxr-xr-xsys/src/cmd/postscript/postgif/mkfile32
-rwxr-xr-xsys/src/cmd/postscript/postgif/postgif.c755
-rwxr-xr-xsys/src/cmd/postscript/postgif/postgif.ps104
-rwxr-xr-xsys/src/cmd/postscript/postio/README20
-rwxr-xr-xsys/src/cmd/postscript/postio/ifdef.c867
-rwxr-xr-xsys/src/cmd/postscript/postio/ifdef.h66
-rwxr-xr-xsys/src/cmd/postscript/postio/postio.1308
-rwxr-xr-xsys/src/cmd/postscript/postio/postio.c1212
-rwxr-xr-xsys/src/cmd/postscript/postio/postio.h209
-rwxr-xr-xsys/src/cmd/postscript/postio/postio.mk109
-rwxr-xr-xsys/src/cmd/postscript/postio/postio.mk.old84
-rwxr-xr-xsys/src/cmd/postscript/postio/slowsend.c121
-rwxr-xr-xsys/src/cmd/postscript/postmd/README23
-rwxr-xr-xsys/src/cmd/postscript/postmd/mkfile61
-rwxr-xr-xsys/src/cmd/postscript/postmd/postmd.1330
-rwxr-xr-xsys/src/cmd/postscript/postmd/postmd.c1157
-rwxr-xr-xsys/src/cmd/postscript/postmd/postmd.h69
-rwxr-xr-xsys/src/cmd/postscript/postmd/postmd.mk95
-rwxr-xr-xsys/src/cmd/postscript/postmd/postmd.ps177
-rwxr-xr-xsys/src/cmd/postscript/postprint/README4
-rwxr-xr-xsys/src/cmd/postscript/postprint/mkfile33
-rwxr-xr-xsys/src/cmd/postscript/postprint/postprint.c794
-rwxr-xr-xsys/src/cmd/postscript/postprint/postprint.h49
-rwxr-xr-xsys/src/cmd/postscript/postprint/postprint.ps75
-rwxr-xr-xsys/src/cmd/postscript/postreverse/README10
-rwxr-xr-xsys/src/cmd/postscript/postreverse/mkfile29
-rwxr-xr-xsys/src/cmd/postscript/postreverse/postreverse.c541
-rwxr-xr-xsys/src/cmd/postscript/postreverse/postreverse.h21
-rwxr-xr-xsys/src/cmd/postscript/postscript.mk202
-rwxr-xr-xsys/src/cmd/postscript/posttek/README4
-rwxr-xr-xsys/src/cmd/postscript/posttek/mkfile33
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.c1199
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.h183
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.ps106
-rwxr-xr-xsys/src/cmd/postscript/printfont/mkfile22
-rwxr-xr-xsys/src/cmd/postscript/printfont/printfont.ps321
-rwxr-xr-xsys/src/cmd/postscript/printfont/printfont.rc105
-rwxr-xr-xsys/src/cmd/postscript/psencoding/Latin1.enc299
-rwxr-xr-xsys/src/cmd/postscript/psencoding/UTF.enc332
-rwxr-xr-xsys/src/cmd/postscript/psencoding/mkfile22
-rwxr-xr-xsys/src/cmd/postscript/psencoding/psencoding.rc39
-rwxr-xr-xsys/src/cmd/postscript/psfiles/Nroundpage.ps11
-rwxr-xr-xsys/src/cmd/postscript/psfiles/README11
-rwxr-xr-xsys/src/cmd/postscript/psfiles/aps.ps127
-rwxr-xr-xsys/src/cmd/postscript/psfiles/banner.ps40
-rwxr-xr-xsys/src/cmd/postscript/psfiles/baseline.ps156
-rwxr-xr-xsys/src/cmd/postscript/psfiles/color.ps65
-rwxr-xr-xsys/src/cmd/postscript/psfiles/fatcourier.ps26
-rwxr-xr-xsys/src/cmd/postscript/psfiles/forms.ps213
-rwxr-xr-xsys/src/cmd/postscript/psfiles/mkfile12
-rwxr-xr-xsys/src/cmd/postscript/psfiles/ps.requests16
-rwxr-xr-xsys/src/cmd/postscript/psfiles/roundpage.ps30
-rwxr-xr-xsys/src/cmd/postscript/psfiles/shade.ps52
-rwxr-xr-xsys/src/cmd/postscript/psfiles/unbind.ps28
-rwxr-xr-xsys/src/cmd/postscript/tcpostio/dial.c109
-rwxr-xr-xsys/src/cmd/postscript/tcpostio/mkfile16
-rwxr-xr-xsys/src/cmd/postscript/tcpostio/tcpostio.c534
-rwxr-xr-xsys/src/cmd/postscript/tests/postbgi1bin0 -> 26442 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/postdaisy1bin0 -> 954 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/postdmd1bin0 -> 15881 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/postgif1bin0 -> 6144 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/postmd1552
-rwxr-xr-xsys/src/cmd/postscript/tests/postplot1bin0 -> 273 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/postprint113
-rwxr-xr-xsys/src/cmd/postscript/tests/posttek1bin0 -> 89578 bytes
-rwxr-xr-xsys/src/cmd/postscript/tests/runtests72
-rwxr-xr-xsys/src/cmd/postscript/tests/troff118
-rwxr-xr-xsys/src/cmd/postscript/text2post/mkfile22
-rwxr-xr-xsys/src/cmd/postscript/text2post/pjw.char.ps142
-rwxr-xr-xsys/src/cmd/postscript/text2post/text2post.c565
-rwxr-xr-xsys/src/cmd/postscript/tr2post/Bgetfield.c153
-rwxr-xr-xsys/src/cmd/postscript/tr2post/chartab.c459
-rwxr-xr-xsys/src/cmd/postscript/tr2post/conv.c100
-rwxr-xr-xsys/src/cmd/postscript/tr2post/devcntl.c178
-rwxr-xr-xsys/src/cmd/postscript/tr2post/draw.c342
-rwxr-xr-xsys/src/cmd/postscript/tr2post/mkfile35
-rwxr-xr-xsys/src/cmd/postscript/tr2post/pictures.c295
-rwxr-xr-xsys/src/cmd/postscript/tr2post/ps_include.c191
-rwxr-xr-xsys/src/cmd/postscript/tr2post/ps_include.h66
-rwxr-xr-xsys/src/cmd/postscript/tr2post/readDESC.c139
-rwxr-xr-xsys/src/cmd/postscript/tr2post/shell.lib1238
-rwxr-xr-xsys/src/cmd/postscript/tr2post/tr2post.c219
-rwxr-xr-xsys/src/cmd/postscript/tr2post/tr2post.h103
-rwxr-xr-xsys/src/cmd/postscript/tr2post/utfmap47
-rwxr-xr-xsys/src/cmd/postscript/tr2post/utils.c264
-rwxr-xr-xsys/src/cmd/postscript/trofftable/mkfile42
-rwxr-xr-xsys/src/cmd/postscript/trofftable/trofftable.1164
-rwxr-xr-xsys/src/cmd/postscript/trofftable/trofftable.mk74
-rwxr-xr-xsys/src/cmd/postscript/trofftable/trofftable.ps189
-rwxr-xr-xsys/src/cmd/postscript/trofftable/trofftable.rc123
-rwxr-xr-xsys/src/cmd/postscript/trofftable/trofftable.sh129
217 files changed, 38659 insertions, 0 deletions
diff --git a/sys/src/cmd/postscript/NOTES b/sys/src/cmd/postscript/NOTES
new file mode 100755
index 000000000..fb673ce01
--- /dev/null
+++ b/sys/src/cmd/postscript/NOTES
@@ -0,0 +1,32 @@
+ Directory dpost is DWB 3.3 version without UTF changes. dpost.utf is
+ stuff for Plan 9. Both build and install dpost, so only pick one. The
+ makefile I sent (postscript.mk) builds dpost.utf.
+
+ Left READING set to ONEBYTE in common/gen.h. Expect dpost errors unless
+ 'x E UTF' is added to troff output or READING set to UTFENCODING. Easy
+ to make 'x E UTF' anything else too.
+
+ Left RUNELIB defined in common/rune.h so rune.c stuff is used when
+ dpost.utf is built.
+
+ UTF.enc is in directory psencoding. Install and link to Default.enc on
+ Plan 9.
+
+ Carmela requested two new characters: \(bs for backslash and \(dq for
+ double quote. Both are in devLatin1 and devpost tables. Carmela also
+ asked for a bunch of her devpost accent characters in devLatin1. Added
+ them to the end of the devLatin1 tables.
+
+ A word of warning about devLatin1. Carmela, Peter and others complained
+ about - and hyphens being too long in the Latin1 fonts. I used Abode's
+ choice but nobody liked it. New devLatin1 tables use a smaller character
+ for hy. Looks better (I guess) but a width change affects line and page
+ breaks!! Not sure what you want to do. Complaints on this one go to
+ npn and carmela!!!
+
+ Didn't take your suggested pathname change in download. Didn't want to
+ risk breaking Unix 4.0 lp. What's there is bogus but was only for Unix
+ 4.0. The -r option accomplishes something similiar but needs a full path.
+
+ postio.mk is very different and not tested on V9.
+
diff --git a/sys/src/cmd/postscript/README b/sys/src/cmd/postscript/README
new file mode 100755
index 000000000..7deebb02d
--- /dev/null
+++ b/sys/src/cmd/postscript/README
@@ -0,0 +1,179 @@
+Stuff appears to work, but it's obviously not well tested. I fully
+expect several iterations before things are correct!! Make sure you
+can back this out quickly.
+
+This code supports UTF encoding. Directory dpost.utf is a version that
+reads UTF encoded files. Directory dpost is DWB 3.3 source and should be
+close to what you're currently using. Main source code changes were in
+dpost.utf (files font.h, font.c, and dpost.c). Select either dpost or
+dpost.utf in TARGETS in postscript.mk. Both build and install a program
+called dpost!!
+
+dpost.utf is more general and includes code that lets it read either
+format. Only catch is troff must tell it (with x E UTF) that the file
+is UTF and troff currently doesn't output encoding info, so you're
+stuck with two post-processors!
+
+Added common/rune.h and common/rune.c so code can be compiled elsewere.
+Both files are only used by dpost. Remove RUNELIB in commmon/rune.h if
+fullrune(), chartorune(), and runetochar() are available on your system.
+You will also need to set READING in common/gen.h. It controls how dpost
+(from dpost.utf) reads troff output. It should be UTFENCODING on Plan 9
+and ONEBYTE elsewhere. If troff includes encoding hint (x E UTF) then
+READING selects the default which sould be ONEBYTE.
+
+Leave WRITING (in common/gen.h) set to ONEBYTE. It only controls dpost
+output and dpost (right now) does not work 100% with UTF.enc. Fix should
+be easy, but I don't have time now.
+
+Other translators passed bytes through so only needed slightly modified
+proglogues and a new encoding scheme (psencoding/UTF.enc). It works for
+Latin1, but still needs a bit more attention. Prologue changes were easy
+and only involved adding lines like,
+
+ /show {show} bind def
+ /stringwidth {stringwidth} bind def
+
+Guarantees text procedures used in prologues aren't operators and can be
+successfully redefined in UTF.enc. Unbinding means a small but probably
+not noticeable speed penalty. You may not want to include those changes
+on other system.
+
+-------------
+Major Changes
+-------------
+
+See the VERSION file.
+
+-------------------
+Tuning The Makefile
+-------------------
+
+Source files, man pages, and low level makefiles can all be updated
+to reflect settings in postscript.mk in one simple step (described
+later). In most cases you only need to edit file postscript.mk.
+
+First save a copy of file postscript.mk. Then adjust the following
+definitions in file postscript.mk:
+
+ SYSTEM best match for your version of Unix. Current choices for
+ SYSTEM are:
+
+ SYSV - System V
+ V9 - Ninth Edition
+ BSD4_2 - Berkeley (eg. Sun)
+
+ Controls conditional compilation in a few places.
+
+ GROUP group assigned to all installed files
+
+ OWNER owner of everything that's installed
+
+ BINDIR dpost and picpack go here. All other programs go in POSTBIN.
+ BINDIR must already exist - it will not be created during an
+ install.
+
+ HOSTDIR hostresident font directory for PostScript printers. Only
+ used in the font download program.
+
+ FONTDIR width table directory - for troff and most postprocessors
+
+ MAN1DIR command manpages. A command and its manpage are installed
+ together - there's no easy way to avoid it. Setting MAN1DIR
+ to an existing temporary directory (e.g. /tmp) means an
+ install will work but manpages won't go anywhere permanent.
+ MAN1DIR must already exist - it will not be created during
+ an install.
+
+ POSTBIN where most PostScript support programs go. dpost and picpack
+ the exceptions.
+
+ POSTLIB prologues and miscellaneous PostScript files. Primarily for
+ the programs that live in POSTBIN.
+
+ CFLGS common compiler options - used to build CFLAGS in the low
+ level makefiles. CLFGS and LDFLGS are best set on the make
+ command line.
+
+ LDFLGS common link editor options - used to build LDFLAGS in the
+ low level makefiles. LDFLGS and CFLGS are best set on the
+ make command line.
+
+ DKHOST set it to TRUE to compile the DKHOST Datakit support code
+ in postio. Temporarily resets SYSTEM to SYSV if DKHOST is
+ TRUE and SYSTEM is BSD4_2. Ignored if SYSTEM is not SYSV
+ or BSD4_2.
+
+ DKSTREAMS enables streams based DKHOST support in postio when DKHOST
+ is TRUE and SYSTEM is SYSV or BSD4_2. Choices are TRUE,
+ FALSE, or a stream module name (e.g. dknetty or dkty). TRUE
+ selects dknetty. Newer systems may expect dkty.
+
+ ROUNDPAGE must only be set to TRUE or FALSE. TRUE means PostScript
+ translators include code that maps clipping path dimensions
+ into known paper sizes.
+
+ TARGETS the default list of what's built by make. Each target must
+ be the name of a source directory. A target that names a
+ non-existent source directory is ignored. Setting TARGETS
+ on the make command line overrides the default list.
+
+-------------------
+Updating The Source
+-------------------
+
+Whenever file postscript.mk changes you should update source files,
+man pages, and low level makefiles by typing,
+
+ make -f postscript.mk changes
+
+------------------------
+More System Dependencies
+------------------------
+
+The package has been compiled and tested on System V and Ninth Edition
+Unix Systems and on Sun workstations. Most differences are handled via
+the SYSTEM definition in postscript.mk. Problems that remain are:
+
+ SYSV - System V
+ Use the native compiler if you're on an internal System V UTS
+ machine.
+
+ V9 - Ninth or Tenth Edition
+ chown is in /etc and chgrp no longer exists - it's been folded into
+ the chown command. You may be forced to build a simple chgrp shell
+ script (put it in your bin) that calls chown. If you're not superuser
+ set OWNER to your login name and GROUP to your group id.
+
+ BSD4_2 - Sun Workstations
+ Use the Bourne shell. chown is should be in /usr/etc. Add /usr/etc
+ to your PATH and export PATH. If you're not superuser set OWNER to
+ your login name and GROUP to your group id.
+
+----------------------
+Installing The Package
+----------------------
+
+To build (but not install) the default package (i.e. everything named by
+TARGETS in postscript.mk) type,
+
+ make -f postscript.mk all
+
+To build and install the package type,
+
+ make -f postscript.mk all install
+
+After the package is installed use,
+
+ make -f postscript.mk clobber
+
+to delete binary files and compiled programs from the source directories.
+
+To select part of the package define TARGETS on the command line. For
+example,
+
+ make -f postscript.mk TARGETS="dpost devpost" all install
+
+builds and installs dpost and the PostScript font tables. Quotes hide
+white space from the shell.
+
diff --git a/sys/src/cmd/postscript/VERSION b/sys/src/cmd/postscript/VERSION
new file mode 100755
index 000000000..6fb4722e5
--- /dev/null
+++ b/sys/src/cmd/postscript/VERSION
@@ -0,0 +1,372 @@
+------------------------
+Version 3.3.2 7/7/92
+------------------------
+
+ 1: Added UTF support for Plan 9. Only signigficant source code changes were
+ in dpost.utf (font.h, font.c, dpost.c). Added common/rune.[hc] so code
+ can be compiled elsewere. Remove RUNELIB in commmon/rune.h if fullrune(),
+ chartorune(), and runetochar() are available on your system. Original
+ DWB 3.3 dpost source is in directory dpost. You should select dpost or
+ dpost.utf in postscript.mk. Both compile and install a program called
+ dpost so don't pick both!
+
+ 2: dpost can read old or UTF troff output. Default is whatever is assigned
+ to READING (file common/gen.h). You get one or the other, unless troff
+ tells dpost what encoding to use (currently x E UTF).
+
+ 3: Most other translators passed bytes through and so only needed slightly
+ modified proglogues and a new encoding scheme (psencoding/UTF.enc). It
+ works for Latin1, but still needs a bit more attention. Prologue changes
+ were easy and only involved adding lines like,
+
+ /show {show} bind def
+ /stringwidth {stringwidth} bind def
+
+ Guarantees text procedures used in prologues aren't operators and can be
+ successfully redefined in UTF.enc. Unbinding means a small but probably
+ not noticeable speed penalty. You may not want to include those changes
+ on other system.
+
+ 4: Operator redefinitions means dpost should work in it's own dictionary
+ (rather than userdict). Not implemented yet, but should be easy. Only
+ potential problem is with picture inclusion when dpost reads UTF.enc.
+
+------------------------
+Version 3.3.2 5/15/92
+------------------------
+
+ 1: postio now outputs all unrecognized status reports - primarly for spooler
+ accounting purposes.
+
+ 2: The makefiles also enable the selection of alternate stream module names
+ for streams based DKHOST support in postio.
+
+ 3: dpost now assumes the optional fifth token in a font mounting command
+ (e.g. x font 2 R /usr/me/font/R) is the full pathname of the font. troff
+ outputs the pathname when a .fp request contains a third argument that
+ begins with a / as in .fp 1 R /usr/me/font/R.
+
+ 4: By request Latin1's - character has been changed from minus to the smaller
+ hyphen character. Added \(dq and \(bs (for " and \ characters) to devpost
+ and devLatin1 tables. Also added \(!! and \(?? to devpost tables.
+
+ 5: Helvetica-Light and Helvetica-LightOblique tables are included as HL and
+ HK in devpost and devLatin1, even though fonts aren't generally available.
+ Also copy H to HM during an install of devpost and devLatin1 tables.
+
+ 6: LH is a horizontally arranged color Lucent logo with text which replaces the AT&T logo.
+ LV is a vertically arranged monochrome AT&T logo with text.
+ FA is a horizontally arranged monochrome Lucent logo with text (low res.).
+ L1 is a monochrome AT&T logo, no text.
+ LA is a monochrome AT&T text.
+
+ 7: Included L1 and LA outlines in devpost and devLatin1. Adjusted LA scaling
+ so size of "AT&T" matches what's in LV. Original PostScript came
+ from Matthijs Melchior.
+
+ 8: Included the "symmetric clippath" version of roundpage.ps as Nroundpage.ps
+ in directory postscript/psfiles. Move it to roundpage.ps if you want it to
+ be the default.
+
+ 9: Added a few lines of code to dpost for handling current implementation of
+ the portrait/landscape mode macros.
+
+10: The man page for download now documents the -r option and notes that -p
+ is for Unix 4.0 lp.
+
+------------------------
+Version 3.3.1 4/30/91
+------------------------
+
+ 1: buildtables stuff has been cleaned up and is now a user level command.
+ Uses shell.lib files that are installed with font tables. The devpost
+ tables were built on a version 47.0 PS-810. The devLatin1 tables were
+ built on a version 51.7 PS-820.
+
+ 2: The devLatin1 tables provide support for the ISO Latin1 alphabet on
+ PostScript printers.
+
+ 3: All translators support different text font encoding schemes using the
+ -E option and *.enc files installed in POSTLIB. The ISO Latin 1 alphabet
+ is supported with file /usr/lib/postscript/Latin1.enc.
+
+ 4: printfont prints a table of the available (encoded) characters in one
+ or more PostScript fonts. It also understands the -E option.
+
+ 5: grabit and hardcopy are two new programs that may be of interest to the
+ more serious PostScript programmer. grabit resembles ==, but produces
+ output that's usally easier to read. hardcopy redirects output from
+ PostScript file output operators (or procedures) to paper. It's useful
+ if you don't have direct access to a printer.
+
+ 6: Prologues and programs are stored together. Other common PostScript files
+ are now in the psfiles directory.
+
+------------------------
+Verions 3.3 4/16/90
+------------------------
+
+ 1: The package is now included in DWB. Version numbers are a bit misleading.
+ The one in postscript.mk refers to the DWB package.
+
+ 2: dpost (and troff) now read ASCII font tables. makedev and the old binary
+ format are gone.
+
+ 3: The devpost directory came directly from the DWB package. The font tables
+ originally distributed with this package are in directory devopost. They
+ are not installed. If possible we recommend you use the devpost tables.
+ The old tables can be installed by adding devopost to the TARGETS list in
+ file postscript.mk.
+
+ 4: dpost recognizes two new fields in font tables. Entries for the full
+ PostScript font look like,
+
+ fontname Times-Roman
+
+ The fontname field is helps manage host resident fonts, the DocumentFonts
+ comment, and is used to define font name abbreviations like the ones in
+ dpost.ps. A font table entry that looks like,
+
+ named in prologue
+
+ disables the runtime abbreviation for the font - dpost assumes it's already
+ in the prologue.
+
+ 5: Extra font tables included in DWB 3.0's devpost font collection are in
+ directory devpost.add. They included here, but should probably not be
+ used.
+
+ 6: Bracket building has been fixed and tested on a wide range of PostScript
+ printers. It will likely still not work on many clones. Real problem
+ appears to be with Adobe's braceleftbt character.
+
+ 7: Most of the special tuning code for device emulation has been removed.
+ Emulation still works, but there may be cases where it's not as good as
+ earlier versions.
+
+ 8: Several problems with color and reverse video printing have been fixed.
+
+ 9: buildtables directory has been cleanup up. The template files in directory
+ buildtables/devpost.data were used to build the devpost tables.
+
+10: postplot and download are two new programs. postplot is for the System V
+ plot package only. Both were written for the Unix 4.0 lp package.
+
+11: postgif is also relatively new - it came from Chi Choy.
+
+12: The translators now rotate clockwise when printing in landscape mode. If
+ you want the old behavior set ROTATION to -1 in postscript.mk.
+
+13: forms.ps has been cleaned up some. Better behavior when you print 2
+ landscape pages on one sheet.
+
+14: Handling of Datakit code for System V has been changed some. Makefiles
+ now expect to find libdk.a and dk.h in standard places (e.g /usr/lib and
+ /usr/include). Set DKHOST to TRUE in postscript.mk to get Datakit support
+ on System V.
+
+ If you're stuck and need to have things behave as they did in the past
+ take a look at file postio/postio.mk. Define DKHOSTDIR and uncomment
+ three lines and the behavior should be close to what it was.
+
+15: Picture inclusion and color macros are gone. They're included in the DWB
+ package, and not here.
+
+------------------------
+Version 3.2 11/27/89
+------------------------
+
+ 1: Implemented height and slant requests in dpost.
+
+ 2: Modified the behavior of all translators so save objects are no longer left
+ on the stack. The original implementation was a mistake and occasionally
+ (e.g. picture inclusion with forms.ps) resulted in invalid restores.
+
+ 3: Fixed the mistake in the external declaration of mesg in postio/slowsend.c.
+
+ 4: The malloc() call in postdmd (routine dimensions()) is only made if patterns
+ is positive.
+
+ 5: Changed definition of De in draw.ps so savematrix is loaded with the CTM
+ whenever De is executed. Original implementation didn't work with forms.ps
+ because the CTM is changed with each page image. (4/29/89)
+
+ 6: Flush stdout when postio is invoked with the -t option - just convenient
+ not necessary. (4/30/89)
+
+ 7: Included a man page for the picture inclusion macros - file man/mpictures.5.
+ (5/6/89)
+
+ 8: Added BoundingBox code to dpost - still needs to go in other translators.
+ Most of the work is done in common/bbox.c. (5/7/89)
+
+ 9: Fiddled with the bracket building stuff in dpost.ps so things finally look
+ decent. Was particularly bad on the typesetter.
+
+10: dpost now generates a PageBoundingBox comment and ps_include.c accepts
+ the comment. Added -B option to enable/disable the BoundingBox calculations.
+ -Bon to enable and -Boff to disable. On by default now, but that may change.
+ Add similar code to the rest of the translators (6/20/89).
+
+11: Fixed ps_include.c so it properly handles %%BeginGlobal and %%EndGlobal.
+ Added braces and compare page.start to page.end instead of 0.
+
+12: Added xymove(hpos, vpos) for \X'PS ...' request - near the end of devcntrl().
+ Must output position info for following PostScript.
+
+13: Added a call to endtext() immediately before the oput() call for \N'...'
+ requests. Without it spacing often messed up with -e2 but not -e0.
+
+------------------------
+Version 3.1 11/15/88
+------------------------
+
+ 1: postio can run as one or two processes (-R option) and can establish an
+ interactive connection with a postscript printer (-i option). Parsing of
+ status reports has been improved. The status query mechanism can be disabled
+ using the -q option. An exit status of 1 implies a system error (eg. can't
+ open the line) while 2 usually means there was an error in the PostScript
+ file. By default postio runs as a single process. The -B, -R, and -q options
+ can be used to speed things up some. A version of the program (previously
+ supplied in postio.tmp) that can help if you seem to be having flow control
+ problems can be obtained using the -S option. It's not generally recommended
+ and should only be used as a last resort!
+
+ 2: Several widthshow encoding schemes have been added to dpost and can reduce
+ print time by 20% or more. The method used to encode lines of text can be
+ changed on the command line using the -e option. Level 0 produces output
+ essentially identical to previous versions of dpost. The default can be
+ changed by modifying the definition of ENCODING in ./Makefile. At present
+ only level 0 is thoroughly tested, although level 2 (ie. -e2) may be the
+ default and is undoubtedly worth a try.
+
+ 3: dpost now supports color selection and reverse video. Access in troff is via
+ the stand-alone macro package ./macros/color. Examples are,
+
+ .so /usr/lib/macros/color
+ .CL red "this prints in red"
+ .CL "white on black" "and this prints white text on a black background"
+
+ The postscript procedures that handle color and reverse video can be found
+ in ./postscript/color.ps. Additional colors can be added to the colordict
+ dictionary defined in ./postscript/color.ps.
+
+ 4: The dpost drawing routines have been improved and have been moved from the
+ prologue (ie. ./postscript/dpost.ps) to ./postscript/draw.ps. That file is
+ only included if needed. Drawing routines now support the ability to group
+ a series of drawing commands together as a single path. May be useful for
+ future versions of pic that wish to fill regions with colors or gray levels.
+ Access is via the new "x X BeginPath" and "x X DrawPath" device control
+ commands. In addition there's some complicated PostScript code in file
+ ./postscript/baseline.ps, that can be used to set text along an arbitrary
+ curve. It's terribly confusing and I doubt anyone will have the patience to
+ bother to figure it out.
+
+ 5: A simple picture packing troff preprocessor (picpack) has been included and
+ the code needed to recover pictures and text has been added to dpost. The
+ program is designed to supplement to the original picture inclusion mechanism,
+ and should ONLY be used when absolutely necessary. Using dpost to pull picture
+ files into a document is strongly recommended and will always be the more
+ efficient and portable approach. picpack simply provides a way to combine
+ pictures and text in a single file before dpost is executed. It may help in
+ a distributed printing environment where the user runs everything through
+ troff while a spooling daemon (eg. lp) handles the postprocessing. There
+ are serious disadvantages to this approach, with perhaps the most important
+ being that troff output files (when picpack is used) will likely result in
+ files that can no longer be reliably passed through other important post-
+ processors like proof.
+
+ 6: Code to handle host resident PostScript fonts in dpost has been tested and
+ finally works. The -H option points dpost to a host resident font directory,
+ which by default is NULL. Host resident font files stored in that directory
+ must be assigned a name that corresponds to the one or two character troff
+ font name. Width tables must also be built (see buildtables/README), the new
+ binary font files must be installed in /usr/lib/font/devpost, and a mapping
+ definition from troff's name to the PostScript font name must be added to
+ ./postscript/dpost.ps.
+
+ 7: The default pattern length in postdmd has been reduced to from 10 to 6 bytes.
+ Printers with fast processors (eg. PS-810s) often benefit from a further
+ reduction, while optimal performance on slower printers (eg PS-800s) may
+ require larger pattern sizes. The pattern length can be set using the -b
+ option. Increasing the pattern size usually increases the size of the output
+ file.
+
+ 8: Line drawing in posttek and postbgi includes code that automatically ties
+ lines to device space coordinates when you select a non-zero width. Helps
+ eliminate the variation in line thickness that many observed. The default
+ line width in posttek and postbgi is still 0 (which gets 1 pixel). If you
+ want a different default change the definition of variable linewidth in files
+ ./postscript/posttek.ps and ./postscript/postbgi.ps.
+
+ 9: Defocused lines in posttek have been fixed.
+
+10: postbgi now supports color and can be used to translate most PRISM (color
+ BGI) jobs. Special device specific tuning needed for many PRISM jobs can be
+ enabled by using the -P"/prism true" option. Missing pieces (eg. subroutines)
+ needed for translating PRISM jobs, have also been implemented.
+
+11: postreverse can reverse the pages in documents that conform to Adobe's 1.0
+ or 2.0 file structuring conventions, and it works with all the translators in
+ this package. The new version is backwards compatible, but files produced by
+ the new translators should not be passed through old versions of postreverse.
+ The likely result will be no output at all. If you choose to do a partial
+ installation put the new postreverse up first!
+
+12: All translators attempt to conform to Adobe's Version 2.0 file structuring
+ conventions. dpost output falls short, but only in the interest of efficiency.
+ Passing dpost output through postreverse (perhaps with the -r option) produces
+ a minimally conforming PostScript file.
+
+13: All the translators now support three options that pass arbitrary PostScript
+ through to the output file. The -P and -C options add a string and the
+ contents of a file respectively immediately after the prologue. It's assumed
+ whatever is added is legitimate PostScript - there is no checking. In each
+ case the added PostScript code becomes part of the job's global environment.
+
+ The -R option can be used to request special action (eg. manualfeed) on a
+ global or page basis. The argument should be "request", "request:page", or
+ "request:page:file". If page is given as 0 or omitted the request applies
+ globally. If file is omitted the lookup is in /usr/lib/postscript/ps.requests.
+ The collection of recognized requests can be modified or extended by changing
+ /usr/lib/postscript/ps.requests.
+
+14: PostScript code (from Johnathan Shopiro) that produces bolder versions of the
+ Courier fonts has been included in file postscript/fatcourier.ps. The file
+ can be added to individual prologue files (eg. dpost.ps) or pulled in as
+ needed using the -C option.
+
+15: postmd is a new program that can be used to display a large matrix as a gray
+ scale image. May help if you're looking for patterns in a large matrix. A very
+ optimistic estimate suggests you can display up to a 600x600 matrix (with five
+ different shades of gray) on 300dpi printer using 8.5x11 inch paper.
+
+16: What's available in buildtables has been cleaned up and works well with the
+ new version of postio. It can be used to have PostScript printers build troff
+ width tables for both printer and host resident fonts.
+
+17: The PostScript bind operator has been applied to all procedures that are
+ expected to be executed more than once. Redefined save and restore procedures
+ are no longer needed and saverestore.ps is not included in this package.
+
+18: The bizarre PostScript code used to get to the upper left corner of a page
+ in old versions of dpost.ps and postprint.ps has been replaced by something
+ that's at least slightly more comprehensible. All prologues have also been
+ changed so picture inclusion (eg. including a pic picture that's been run
+ through troff and dpost) should work better than previous versions. Still
+ missing (from most translators) is the %%BoundingBox comment and even when
+ it's put out (by postdmd) only the dimensions are correct - sorry!
+
+19: The careless mistake in the DKHOST section of postio that some noticed belongs
+ to me (not Allan Buckwalter) and has now been fixed.
+
+20: By default all prologues still use the current clipping path to determine page
+ dimensions, but that behavior can be disabled by setting boolean useclippath
+ (in each prologue) to false. In that case the page dimensions will be taken
+ from array pagebbox, which by default is initialized to 8x11 inch paper. The
+ -P options (in each translator) can change useclippth and pagebbox.
+
+21: New in the misc directory is sample lp support from Maryann Csaszar and a
+ simple program that converts host resident font files obtained from a Macintosh
+ to a format that works on Unix.
+
diff --git a/sys/src/cmd/postscript/buildtables/README b/sys/src/cmd/postscript/buildtables/README
new file mode 100755
index 000000000..caf5f3f1e
--- /dev/null
+++ b/sys/src/cmd/postscript/buildtables/README
@@ -0,0 +1,5 @@
+Programs for building troff width tables on a PostScript printer.
+Assumes you have direct access to the printer's serial port. Also
+needs a special table dependent shell library file to build the
+tables (e.g. ../devLatin1/shell.lib).
+
diff --git a/sys/src/cmd/postscript/buildtables/buildtables.1 b/sys/src/cmd/postscript/buildtables/buildtables.1
new file mode 100755
index 000000000..dd7ce7de9
--- /dev/null
+++ b/sys/src/cmd/postscript/buildtables/buildtables.1
@@ -0,0 +1,214 @@
+.ds dF /usr/lib/font
+.ds dQ /usr/lib/postscript
+.TH BUILDTABLES 1 "DWB 3.2"
+.SH NAME
+.B buildtables
+\- build
+.B troff
+tables on a PostScript printer
+.SH SYNOPSIS
+\*(mBbuildtables\f1
+.OP "" options []
+.OP "" "name \(el" []
+.SH DESCRIPTION
+.B buildtables
+builds font width tables or the typesetter description
+file on a PostScript printer.
+No arguments means build a default set of tables;
+usually a superset of the LaserWriter Plus collection.
+The following
+.I options
+are understood:
+.TP 1.0i
+.OP \-b speed
+Transmit data over
+.I line
+at baud rate
+.I speed.
+Recognized baud rates are 1200, 2400, 4800, 9600, and 19200.
+The default
+.I speed
+is 9600 baud.
+.TP 1.0i
+.OP \-l line
+Build the tables on the PostScript printer attached to
+.I line.
+There is no default.
+.TP 1.0i
+.OP \-t name
+Use
+.I name
+as the template for fonts not in the default set.
+Choose
+.MW R
+for proportionally spaced fonts and
+.MW CW
+for fixed width fonts.
+Try
+.MW ZD
+(ZapfDingbats) if the font has a non-standard
+character set.
+The default is
+.MR R .
+.TP 1.0i
+.OP \-C file
+Copy
+.I file
+into each PostScript table program;
+.I file
+must contain legitimate PostScript.
+.TP 1.0i
+.OP \-H hostdir
+Use
+.I hostdir
+as the host-resident font directory.
+A file in
+.I hostdir
+that matches the name of the
+.B troff
+font is assumed to be a host-resident font program and is included
+in the PostScript width table program.
+There is no default.
+.TP 1.0i
+.OP \-S file
+Use
+.I file
+as the shell library file.
+Overrides the choice made with the
+.OP \-T
+option.
+.TP 1.0i
+.OP \-T name
+Set the target device to
+.I name.
+.br
+Device
+.I name
+means
+.ft 2
+.MI \*(dF/dev name /shell.lib
+.ft 1
+is the shell library file.
+There is no default.
+.PP
+If
+.OP \-l
+is omitted output files are the PostScript programs that
+build the tables, rather than the tables themselves.
+One of
+.OP \-T
+or
+.OP \-S
+is required.
+If both are given
+.OP \-S
+wins.
+Although
+.OP \-H
+is the preferred mechanism for including host-resident font files,
+.OP \-C
+makes sense when only one width table is built.
+.PP
+The shell library file defines a collection of functions used to
+build
+.BR troff (1)
+tables.
+The default set of tables is the list of names returned by the
+.MW AllTables
+function.
+Changes to the default list can be made by updating the
+.MW BuiltinTables
+function.
+.PP
+Each
+.B buildtables
+argument
+must be a default table name, or a pair of names enclosed in quotes.
+If the argument is a pair, the first name is the
+.B troff
+font and the second is the full PostScript font name.
+Tables are created in the current directory.
+Each is assigned a name that matches the
+.B troff
+table name.
+.PP
+The PostScript table programs created by
+.BR trofftable (1)
+are written to files that have
+.MW .ps
+appended to the
+.B troff
+table name.
+The
+.MW .ps
+file is deleted after the table is built.
+Options not listed above are passed to
+.B trofftable.
+The PostScript table programs return data to the host computer using
+PostScript's
+.MW print
+operator.
+See
+.BR hardcopy (1)
+if you do not have access to the printer's serial port.
+.SH EXAMPLES
+.PP
+Build the default collection of devpost tables on the printer
+connected to
+.MW /dev/tty00
+(no font name arguments):
+.EX
+buildtables -l/dev/tty00 -Tpost
+.EE
+To do the same and to restrict the tables that are built,
+Add
+.B troff
+font names (or
+.MR DESC )
+to restrict the tables built on the printer connected to
+.MR /dev/tty00 :
+.EX
+buildtables -l/dev/tty00 -Tpost R I B BI DESC S
+.EE
+Enclose the
+.B troff
+and PostScript font names in quotes to
+build the width table for a font not in the default set
+(also on the printer connected to
+.MR /dev/tty00 ):
+.EX
+buildtables -l/dev/tty00 -TLatin1 "GL Garamond-Light"
+.EE
+A font must be available on the printer when the table is built.
+Use
+.OP \-H
+or
+.OP \-C
+to include host-resident fonts.
+.SH WARNINGS
+.PP
+A width table will not build properly if the printer cannot access
+the PostScript font.
+.PP
+The
+.OP \-TLatin1
+option only works on PostScript printers that support the full
+.SM ISO
+Latin-1 character set.
+The error message from older printers will likely indicate a missing
+.MW ISOLatin1Encoding
+array.
+.SH FILES
+.MW \*(dF/dev*/shell.lib
+.br
+.MW \*(dQ/dpost.ps
+.br
+.MW \*(dQ/trofftable.ps
+.br
+.SH SEE ALSO
+.BR dpost (1),
+.BR hardcopy (1),
+.BR postio (1),
+.BR troff (1),
+.BR trofftable (1),
+.BR font (5)
diff --git a/sys/src/cmd/postscript/buildtables/buildtables.mk b/sys/src/cmd/postscript/buildtables/buildtables.mk
new file mode 100755
index 000000000..73c77473b
--- /dev/null
+++ b/sys/src/cmd/postscript/buildtables/buildtables.mk
@@ -0,0 +1,64 @@
+MAKE=/bin/make
+MAKEFILE=buildtables.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+FONTDIR=/usr/lib/font
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+MAN1DIR=/tmp
+
+all : buildtables
+
+install : all
+ @if [ ! -d $(POSTBIN) ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ cp buildtables $(POSTBIN)/buildtables
+ @chmod 755 $(POSTBIN)/buildtables
+ @chgrp $(GROUP) $(POSTBIN)/buildtables
+ @chown $(OWNER) $(POSTBIN)/buildtables
+ cp buildtables.1 $(MAN1DIR)/buildtables.1
+ @chmod 644 $(MAN1DIR)/buildtables.1
+ @chgrp $(GROUP) $(MAN1DIR)/buildtables.1
+ @chown $(OWNER) $(MAN1DIR)/buildtables.1
+
+clean :
+
+clobber : clean
+ rm -f buildtables
+
+buildtables : buildtables.sh
+ sed \
+ -e "s'^FONTDIR=.*'FONTDIR=$(FONTDIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ buildtables.sh >buildtables
+ @chmod 755 buildtables
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^FONTDIR=.*'FONTDIR=$(FONTDIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dF.*'.ds dF $(FONTDIR)'" \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ buildtables.1 >XXX.1; \
+ mv XXX.1 buildtables.1
+
diff --git a/sys/src/cmd/postscript/buildtables/buildtables.sh b/sys/src/cmd/postscript/buildtables/buildtables.sh
new file mode 100755
index 000000000..f9e4404d7
--- /dev/null
+++ b/sys/src/cmd/postscript/buildtables/buildtables.sh
@@ -0,0 +1,94 @@
+#
+# Builds one or more font width tables or the typesetter description
+# file on a PostScript printer. Assumes you have direct access to the
+# printer's serial port. No arguments means build a standard collection
+# of tables - usually the LaserWriter Plus set. See trofftable and the
+# shell library files /usr/lib/font/dev*/shell.lib for more details.
+#
+
+set -e
+
+POSTBIN=/usr/lbin/postscript
+POSTLIB=/usr/lib/postscript
+FONTDIR=/usr/lib/font
+
+POSTIO=$POSTBIN/postio
+TROFFTABLE=$POSTBIN/trofftable
+
+BAUDRATE=
+DEVICE=
+LIBRARY=
+
+while [ -n "$1" ]; do
+ case $1 in
+ -C) shift; OPTIONS="$OPTIONS -C$1";;
+ -C*) OPTIONS="$OPTIONS $1";;
+
+ -F) shift; FONTDIR=$1;;
+ -F*) FONTDIR=`echo $1 | sed s/-F//`;;
+
+ -H) shift; OPTIONS="$OPTIONS -H$1";;
+ -H*) OPTIONS="$OPTIONS $1";;
+
+ -S) shift; LIBRARY=$1;;
+ -S*) LIBRARY=`echo $1 | sed s/-S//`;;
+
+ -T) shift; DEVICE=$1;;
+ -T*) DEVICE=`echo $1 | sed s/-T//`;;
+
+ -b) shift; BAUDRATE=$1;;
+ -b*) BAUDRATE=`echo $1 | sed s/-b//`;;
+
+ -c) shift; OPTIONS="$OPTIONS -c$1";;
+ -c*) OPTIONS="$OPTIONS $1";;
+
+ -l) shift; LINE=$1;;
+ -l*) LINE=`echo $1 | sed s/-l//`;;
+
+ -s) shift; OPTIONS="$OPTIONS -s$1";;
+ -s*) OPTIONS="$OPTIONS $1";;
+
+ -t) shift; OPTIONS="$OPTIONS -t$1";;
+ -t*) OPTIONS="$OPTIONS $1";;
+
+ -?) OPTIONS="$OPTIONS $1$2"; shift;;
+ -?*) OPTIONS="$OPTIONS $1";;
+
+ *) break;;
+ esac
+ shift
+done
+
+if [ ! "$DEVICE" -a ! "$LIBRARY" ]; then
+ echo "$0: no device or shell library" >&2
+ exit 1
+fi
+
+LIBRARY=${LIBRARY:-${FONTDIR}/dev${DEVICE}/shell.lib}
+
+#
+# No arguments means build everything return by the AllTables function.
+#
+
+if [ $# -eq 0 ]; then
+ . $LIBRARY
+ set -- `AllTables`
+fi
+
+for i do
+ SHORT=`echo $i | awk '{print $1}'`
+ LONG=`echo $i | awk '{print $2}'`
+
+ if [ "$LINE" ]
+ then echo "==== Building table $SHORT ===="
+ else echo "==== Creating table program $SHORT.ps ===="
+ fi
+
+ $TROFFTABLE -S$LIBRARY $OPTIONS $SHORT $LONG >$SHORT.ps
+
+ if [ "$LINE" ]; then
+ $POSTIO -t -l$LINE ${BAUDRATE:+-b${BAUDRATE}} $SHORT.ps >$SHORT
+ rm -f $SHORT.ps
+ fi
+done
+
diff --git a/sys/src/cmd/postscript/changes b/sys/src/cmd/postscript/changes
new file mode 100755
index 000000000..500eda2a6
--- /dev/null
+++ b/sys/src/cmd/postscript/changes
@@ -0,0 +1,95 @@
+add mkfile to each directory for plan9.
+
+common:
+add getopt.c to common.
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in bbox.c.
+comment out strtok(), ftell(), sqrt() atan2() defs in ext.h.
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in misc.c.
+comment out RUNELIB on #endif line in rune.c
+add defined(plan9) in tempnam.c.
+change path.h for plan9!
+
+cropmarks:
+add cropmarks.rc to cropmarks.
+
+download:
+move #include <sys/types.h> before #include <fcntl.h> for off_t in download.c.
+
+dpost:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in dpost.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in dpost.c.
+
+grabit:
+add grabit.rc to grabit.
+
+hardcopy:
+add hardcopy.rc to hardcopy.
+
+postbgi:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postbgi.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postbgi.c.
+
+postdaisy:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postdaisy.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postdaisy.c.
+
+postdmd:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postdmd.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postdmd.c.
+
+postgif:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postgif.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postgif.c.
+
+download:
+added #include <string.h> in download.c.
+
+dpost:
+added #include <string.h> in draw.c.
+
+picpack:
+added #include <string.h> in picpack.c.
+
+postmd:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postmd.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postmd.c.
+added #include <string.h> in picpack.c.
+
+postprint:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postprint.c.
+added
+ #ifdef plan9
+ #define isascii(c) ((unsigned char)(c)<=0177)
+ #endif
+after #include <ctype.h> in postprint.c.
+
+postreverse:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in postreverse.c.
+
+posttek:
+insert #include <sys/types.h> before #include <fcntl.h> for off_t in posttek.c.
+
diff --git a/sys/src/cmd/postscript/common/bbox.c b/sys/src/cmd/postscript/common/bbox.c
new file mode 100755
index 000000000..7e1f14a50
--- /dev/null
+++ b/sys/src/cmd/postscript/common/bbox.c
@@ -0,0 +1,257 @@
+/*
+ *
+ * Boundingbox code for PostScript translators. The boundingbox for each page
+ * is accumulated in bbox - the one for the whole document goes in docbbox. A
+ * call to writebbox() puts out an appropriate comment, updates docbbox, and
+ * resets bbox for the next page. The assumption made at the end of writebbox()
+ * is that we're really printing the current page only if output is now going
+ * to stdout - a valid assumption for all supplied translators. Needs the math
+ * library.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* a few general purpose definitions */
+#include "ext.h" /* external variable declarations */
+
+typedef struct bbox {
+ int set;
+ double llx, lly;
+ double urx, ury;
+} Bbox;
+
+Bbox bbox = {FALSE, 0.0, 0.0, 0.0, 0.0};
+Bbox docbbox = {FALSE, 0.0, 0.0, 0.0, 0.0};
+
+double ctm[6] = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
+double matrix1[6], matrix2[6];
+
+/*****************************************************************************/
+
+cover(x, y)
+
+ double x, y;
+
+{
+
+/*
+ *
+ * Adds point (x, y) to bbox. Coordinates are in user space - the transformation
+ * to default coordinates happens in writebbox().
+ *
+ */
+
+ if ( bbox.set == FALSE ) {
+ bbox.llx = bbox.urx = x;
+ bbox.lly = bbox.ury = y;
+ bbox.set = TRUE;
+ } else {
+ if ( x < bbox.llx )
+ bbox.llx = x;
+ if ( y < bbox.lly )
+ bbox.lly = y;
+ if ( x > bbox.urx )
+ bbox.urx = x;
+ if ( y > bbox.ury )
+ bbox.ury = y;
+ } /* End else */
+
+} /* End of cover */
+
+/*****************************************************************************/
+
+writebbox(fp, keyword, slop)
+
+ FILE *fp; /* the comment is written here */
+ char *keyword; /* the boundingbox comment string */
+ int slop; /* expand (or contract?) the box a bit */
+
+{
+
+ Bbox ubbox; /* user space bounding box */
+ double x, y;
+
+/*
+ *
+ * Transforms the numbers in the bbox[] using ctm[], adjusts the corners a bit
+ * (depending on slop) and then writes comment. If *keyword is BoundingBox use
+ * whatever's been saved in docbbox, otherwise assume the comment is just for
+ * the current page.
+ *
+ */
+
+ if ( strcmp(keyword, BOUNDINGBOX) == 0 )
+ bbox = docbbox;
+
+ if ( bbox.set == TRUE ) {
+ ubbox = bbox;
+ bbox.set = FALSE; /* so cover() works properly */
+ x = ctm[0] * ubbox.llx + ctm[2] * ubbox.lly + ctm[4];
+ y = ctm[1] * ubbox.llx + ctm[3] * ubbox.lly + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.llx + ctm[2] * ubbox.ury + ctm[4];
+ y = ctm[1] * ubbox.llx + ctm[3] * ubbox.ury + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.urx + ctm[2] * ubbox.ury + ctm[4];
+ y = ctm[1] * ubbox.urx + ctm[3] * ubbox.ury + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.urx + ctm[2] * ubbox.lly + ctm[4];
+ y = ctm[1] * ubbox.urx + ctm[3] * ubbox.lly + ctm[5];
+ cover(x, y);
+ bbox.llx -= slop + 0.5;
+ bbox.lly -= slop + 0.5;
+ bbox.urx += slop + 0.5;
+ bbox.ury += slop + 0.5;
+ fprintf(fp, "%s %d %d %d %d\n", keyword, (int)bbox.llx, (int)bbox.lly,(int)bbox.urx, (int)bbox.ury);
+ bbox = ubbox;
+ } /* End if */
+
+ resetbbox((fp == stdout) ? TRUE : FALSE);
+
+} /* End of writebbox */
+
+/*****************************************************************************/
+
+resetbbox(output)
+
+ int output;
+
+{
+
+/*
+ *
+ * Adds bbox to docbbox and resets bbox for the next page. Only update docbbox
+ * if we really did output on the last page.
+ *
+ */
+
+ if ( docbbox.set == TRUE ) {
+ cover(docbbox.llx, docbbox.lly);
+ cover(docbbox.urx, docbbox.ury);
+ } /* End if */
+
+ if ( output == TRUE ) {
+ docbbox = bbox;
+ docbbox.set = TRUE;
+ } /* End if */
+
+ bbox.set = FALSE;
+
+} /* End of resetbbox */
+
+/*****************************************************************************/
+
+scale(sx, sy)
+
+ double sx, sy;
+
+{
+
+/*
+ *
+ * Scales the default matrix.
+ *
+ */
+
+ matrix1[0] = sx;
+ matrix1[1] = 0;
+ matrix1[2] = 0;
+ matrix1[3] = sy;
+ matrix1[4] = 0;
+ matrix1[5] = 0;
+
+ concat(matrix1);
+
+} /* End of scale */
+
+/*****************************************************************************/
+
+translate(tx, ty)
+
+ double tx, ty;
+
+{
+
+/*
+ *
+ * Translates the default matrix.
+ *
+ */
+
+ matrix1[0] = 1.0;
+ matrix1[1] = 0.0;
+ matrix1[2] = 0.0;
+ matrix1[3] = 1.0;
+ matrix1[4] = tx;
+ matrix1[5] = ty;
+
+ concat(matrix1);
+
+} /* End of translate */
+
+/*****************************************************************************/
+
+rotate(angle)
+
+ double angle;
+
+{
+
+/*
+ *
+ * Rotates by angle degrees.
+ *
+ */
+
+ angle *= 3.1416 / 180;
+
+ matrix1[0] = matrix1[3] = cos(angle);
+ matrix1[1] = sin(angle);
+ matrix1[2] = -matrix1[1];
+ matrix1[4] = 0.0;
+ matrix1[5] = 0.0;
+
+ concat(matrix1);
+
+} /* End of rotate */
+
+/*****************************************************************************/
+
+concat(m1)
+
+ double m1[];
+
+{
+
+ double m2[6];
+
+/*
+ *
+ * Replaces the ctm[] by the result of the matrix multiplication m1[] x ctm[].
+ *
+ */
+
+ m2[0] = ctm[0];
+ m2[1] = ctm[1];
+ m2[2] = ctm[2];
+ m2[3] = ctm[3];
+ m2[4] = ctm[4];
+ m2[5] = ctm[5];
+
+ ctm[0] = m1[0] * m2[0] + m1[1] * m2[2];
+ ctm[1] = m1[0] * m2[1] + m1[1] * m2[3];
+ ctm[2] = m1[2] * m2[0] + m1[3] * m2[2];
+ ctm[3] = m1[2] * m2[1] + m1[3] * m2[3];
+ ctm[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4];
+ ctm[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5];
+
+} /* End of concat */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/common/comments.h b/sys/src/cmd/postscript/common/comments.h
new file mode 100755
index 000000000..6b409cad1
--- /dev/null
+++ b/sys/src/cmd/postscript/common/comments.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * Currently defined file structuring comments from Adobe - plus a few others.
+ * Ones that end with a colon expect arguments, while those ending with a newline
+ * stand on their own. Truly overkill on Adobe's part and mine for including them
+ * all!
+ *
+ * All PostScript files should begin with a header that starts with one of the
+ * following comments.
+ *
+ */
+
+#define NONCONFORMING "%!PS\n"
+#define MINCONFORMING "%!PS-Adobe-\n"
+#define OLDCONFORMING "%!PS-Adobe-1.0\n"
+
+#define CONFORMING "%!PS-Adobe-2.0\n"
+#define CONFORMINGEPS "%!PS-Adobe-2.0 EPS\n"
+#define CONFORMINGQUERY "%!PS-Adobe-2.0 Query\n"
+#define CONFORMINGEXITSERVER "%!PS-Adobe-2.0 ExitServer\n"
+
+/*
+ *
+ * Header comments - immediately follow the appropriate document classification
+ * comment.
+ *
+ */
+
+#define TITLE "%%Title:"
+#define CREATOR "%%Creator:"
+#define CREATIONDATE "%%CreationDate:"
+#define FOR "%%For:"
+#define ROUTING "%%Routing:"
+#define BOUNDINGBOX "%%BoundingBox:"
+#define PAGES "%%Pages:"
+#define REQUIREMENTS "%%Requirements:"
+
+#define DOCUMENTFONTS "%%DocumentFonts:"
+#define DOCUMENTNEEDEDFONTS "%%DocumentNeededFonts:"
+#define DOCUMENTSUPPLIEDFONTS "%%DocumentSuppliedFonts:"
+#define DOCUMENTNEEDEDPROCSETS "%%DocumentNeededProcSets:"
+#define DOCUMENTSUPPLIEDPROCSETS "%%DocumentSuppliedProcSets:"
+#define DOCUMENTNEEDEDFILES "%%DocumentNeededFiles:"
+#define DOCUMENTSUPPLIEDFILES "%%DocumentSuppliedFiles:"
+#define DOCUMENTPAPERSIZES "%%DocumentPaperSizes:"
+#define DOCUMENTPAPERFORMS "%%DocumentPaperForms:"
+#define DOCUMENTPAPERCOLORS "%%DocumentPaperColors:"
+#define DOCUMENTPAPERWEIGHTS "%%DocumentPaperWeights:"
+#define DOCUMENTPRINTERREQUIRED "%%DocumentPrinterREquired:"
+#define ENDCOMMENTS "%%EndComments\n"
+#define ENDPROLOG "%%EndProlog\n"
+
+/*
+ *
+ * Body comments - can appear anywhere in a document.
+ *
+ */
+
+#define BEGINSETUP "%%BeginSetup\n"
+#define ENDSETUP "%%EndSetup\n"
+#define BEGINDOCUMENT "%%BeginDocument:"
+#define ENDDOCUMENT "%%EndDocument\n"
+#define BEGINFILE "%%BeginFile:"
+#define ENDFILE "%%EndFile\n"
+#define BEGINPROCSET "%%BeginProcSet:"
+#define ENDPROCSET "%%EndProcSet\n"
+#define BEGINBINARY "%%BeginBinary:"
+#define ENDBINARY "%%EndBinary\n"
+#define BEGINPAPERSIZE "%%BeginePaperSize:"
+#define ENDPAPERSIZE "%%EndPaperSize\n"
+#define BEGINFEATURE "%%BeginFeature:"
+#define ENDFEATURE "%%EndFeature\n"
+#define BEGINEXITSERVER "%%BeginExitServer:"
+#define ENDEXITSERVER "%%EndExitServer\n"
+#define TRAILER "%%Trailer\n"
+
+/*
+ *
+ * Page level comments - usually will occur once per page.
+ *
+ */
+
+#define PAGE "%%Page:"
+#define PAGEFONTS "%%PageFonts:"
+#define PAGEFILES "%%PageFiles:"
+#define PAGEBOUNDINGBOX "%%PageBoundingBox:"
+#define BEGINPAGESETUP "%%BeginPageSetup\n"
+#define BEGINOBJECT "%%BeginObject:"
+#define ENDOBJECT "%%EndObject\n"
+
+/*
+ *
+ * Resource requirements - again can appear anywhere in a document.
+ *
+ */
+
+#define INCLUDEFONT "%%IncludeFont:"
+#define INCLUDEPROCSET "%%IncludeProcSet:"
+#define INCLUDEFILE "%%IncludeFile:"
+#define EXECUTEFILE "%%ExecuteFile:"
+#define CHANGEFONT "%%ChangeFont:"
+#define PAPERFORM "%%PaparForm:"
+#define PAPERCOLOR "%%PaperColor:"
+#define PAPERWEIGHT "%%PaperWeight:"
+#define PAPERSIZE "%%PaperSize:"
+#define FEATURE "%%Feature:"
+#define ENDOFFILE "%%EOF\n"
+
+#define CONTINUECOMMENT "%%+"
+#define ATEND "(atend)"
+
+/*
+ *
+ * Some non-standard document comments. Global definitions are occasionally used
+ * in dpost and are marked by BEGINGLOBAL and ENDGLOBAL. The resulting document
+ * violates page independence, but can easily be converted to a conforming file
+ * using a utililty program.
+ *
+ */
+
+#define BEGINSCRIPT "%%BeginScript\n"
+#define BEGINGLOBAL "%%BeginGlobal\n"
+#define ENDGLOBAL "%%EndGlobal\n"
+#define ENDPAGE "%%EndPage:"
+#define FORMSPERPAGE "%%FormsPerPage:"
+#define VERSION "%%Version:"
+
diff --git a/sys/src/cmd/postscript/common/common.c b/sys/src/cmd/postscript/common/common.c
new file mode 100755
index 000000000..24c2f55cb
--- /dev/null
+++ b/sys/src/cmd/postscript/common/common.c
@@ -0,0 +1,264 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "common.h"
+#include "comments.h"
+#include "path.h"
+
+struct strtab charcode[FONTSIZE] = {
+ {4, "\\000"}, {4, "\\001"}, {4, "\\002"}, {4, "\\003"},
+ {4, "\\004"}, {4, "\\005"}, {4, "\\006"}, {4, "\\007"},
+ {4, "\\010"}, {4, "\\011"}, {4, "\\012"}, {4, "\\013"},
+ {4, "\\014"}, {4, "\\015"}, {4, "\\016"}, {4, "\\017"},
+ {4, "\\020"}, {4, "\\021"}, {4, "\\022"}, {4, "\\023"},
+ {4, "\\024"}, {4, "\\025"}, {4, "\\026"}, {4, "\\027"},
+ {4, "\\030"}, {4, "\\031"}, {4, "\\032"}, {4, "\\033"},
+ {4, "\\034"}, {4, "\\035"}, {4, "\\036"}, {4, "\\037"},
+ {1, " "}, {1, "!"}, {1, "\""}, {1, "#"},
+ {1, "$"}, {1, "%"}, {1, "&"}, {1, "'"},
+ {2, "\\("}, {2, "\\)"}, {1, "*"}, {1, "+"},
+ {1, ","}, {1, "-"}, {1, "."}, {1, "/"},
+ {1, "0"}, {1, "1"}, {1, "2"}, {1, "3"},
+ {1, "4"}, {1, "5"}, {1, "6"}, {1, "7"},
+ {1, "8"}, {1, "9"}, {1, ":"}, {1, ";"},
+ {1, "<"}, {1, "="}, {1, ">"}, {1, "?"},
+ {1, "@"}, {1, "A"}, {1, "B"}, {1, "C"},
+ {1, "D"}, {1, "E"}, {1, "F"}, {1, "G"},
+ {1, "H"}, {1, "I"}, {1, "J"}, {1, "K"},
+ {1, "L"}, {1, "M"}, {1, "N"}, {1, "O"},
+ {1, "P"}, {1, "Q"}, {1, "R"}, {1, "S"},
+ {1, "T"}, {1, "U"}, {1, "V"}, {1, "W"},
+ {1, "X"}, {1, "Y"}, {1, "Z"}, {1, "["},
+ {2, "\\\\"}, {1, "]"}, {1, "^"}, {1, "_"},
+ {1, "`"}, {1, "a"}, {1, "b"}, {1, "c"},
+ {1, "d"}, {1, "e"}, {1, "f"}, {1, "g"},
+ {1, "h"}, {1, "i"}, {1, "j"}, {1, "k"},
+ {1, "l"}, {1, "m"}, {1, "n"}, {1, "o"},
+ {1, "p"}, {1, "q"}, {1, "r"}, {1, "s"},
+ {1, "t"}, {1, "u"}, {1, "v"}, {1, "w"},
+ {1, "x"}, {1, "y"}, {1, "z"}, {1, "{"},
+ {1, "|"}, {1, "}"}, {1, "~"}, {4, "\\177"},
+ {4, "\\200"}, {4, "\\201"}, {4, "\\202"}, {4, "\\203"},
+ {4, "\\204"}, {4, "\\205"}, {4, "\\206"}, {4, "\\207"},
+ {4, "\\210"}, {4, "\\211"}, {4, "\\212"}, {4, "\\213"},
+ {4, "\\214"}, {4, "\\215"}, {4, "\\216"}, {4, "\\217"},
+ {4, "\\220"}, {4, "\\221"}, {4, "\\222"}, {4, "\\223"},
+ {4, "\\224"}, {4, "\\225"}, {4, "\\226"}, {4, "\\227"},
+ {4, "\\230"}, {4, "\\231"}, {4, "\\232"}, {4, "\\233"},
+ {4, "\\234"}, {4, "\\235"}, {4, "\\236"}, {4, "\\237"},
+ {4, "\\240"}, {4, "\\241"}, {4, "\\242"}, {4, "\\243"},
+ {4, "\\244"}, {4, "\\245"}, {4, "\\246"}, {4, "\\247"},
+ {4, "\\250"}, {4, "\\251"}, {4, "\\252"}, {4, "\\253"},
+ {4, "\\254"}, {4, "\\255"}, {4, "\\256"}, {4, "\\257"},
+ {4, "\\260"}, {4, "\\261"}, {4, "\\262"}, {4, "\\263"},
+ {4, "\\264"}, {4, "\\265"}, {4, "\\266"}, {4, "\\267"},
+ {4, "\\270"}, {4, "\\271"}, {4, "\\272"}, {4, "\\273"},
+ {4, "\\274"}, {4, "\\275"}, {4, "\\276"}, {4, "\\277"},
+ {4, "\\300"}, {4, "\\301"}, {4, "\\302"}, {4, "\\303"},
+ {4, "\\304"}, {4, "\\305"}, {4, "\\306"}, {4, "\\307"},
+ {4, "\\310"}, {4, "\\311"}, {4, "\\312"}, {4, "\\313"},
+ {4, "\\314"}, {4, "\\315"}, {4, "\\316"}, {4, "\\317"},
+ {4, "\\320"}, {4, "\\321"}, {4, "\\322"}, {4, "\\323"},
+ {4, "\\324"}, {4, "\\325"}, {4, "\\326"}, {4, "\\327"},
+ {4, "\\330"}, {4, "\\331"}, {4, "\\332"}, {4, "\\333"},
+ {4, "\\334"}, {4, "\\335"}, {4, "\\336"}, {4, "\\337"},
+ {4, "\\340"}, {4, "\\341"}, {4, "\\342"}, {4, "\\343"},
+ {4, "\\344"}, {4, "\\345"}, {4, "\\346"}, {4, "\\347"},
+ {4, "\\350"}, {4, "\\351"}, {4, "\\352"}, {4, "\\353"},
+ {4, "\\354"}, {4, "\\355"}, {4, "\\356"}, {4, "\\357"},
+ {4, "\\360"}, {4, "\\361"}, {4, "\\362"}, {4, "\\363"},
+ {4, "\\364"}, {4, "\\365"}, {4, "\\366"}, {4, "\\367"},
+ {4, "\\370"}, {4, "\\371"}, {4, "\\372"}, {4, "\\373"},
+ {4, "\\374"}, {4, "\\375"}, {4, "\\376"}, {4, "\\377"}
+};
+
+static BOOLEAN in_string = FALSE;
+int char_no = 0;
+int line_no = 0;
+int page_no = 0; /* page number in a document */
+int pages_printed = 0;
+static int pplistmaxsize=0;
+
+static unsigned char *pplist=0; /* bitmap list for storing pages to print */
+
+void
+pagelist(char *list) {
+ char c;
+ int n, m;
+ int state, start;
+
+ if (list == 0) return;
+ state = 1;
+ start = 0;
+ while ((c=*list) != '\0') {
+ n = 0;
+ while (isdigit(c)) {
+ n = n * 10 + c - '0';
+ c = *++list;
+ }
+ switch (state) {
+ case 1:
+ start = n;
+ case 2:
+ if (n/8+1 > pplistmaxsize) {
+ pplistmaxsize = n/8+1;
+ pplist = galloc(pplist, n/8+1, "page list");
+ }
+ for (m=start; m<=n; m++)
+ pplist[m/8] |= 1<<(m%8);
+ break;
+ }
+ switch (c) {
+ case '-':
+ state = 2;
+ list++;
+ break;
+ case ',':
+ state = 1;
+ list++;
+ break;
+ case '\0':
+ break;
+ }
+ }
+}
+
+BOOLEAN
+pageon(void) {
+ extern BOOLEAN debug;
+ static BOOLEAN privdebug = FALSE;
+
+ if (pplist == 0 && page_no != 0) {
+ if (privdebug && !debug) {
+ privdebug = FALSE;
+ debug = TRUE;
+ }
+ return(TRUE); /* no page list, print all pages */
+ }
+ if (page_no/8 < pplistmaxsize && (pplist[page_no/8] & 1<<(page_no%8))) {
+ if (privdebug && !debug) {
+ privdebug = FALSE;
+ debug = TRUE;
+ }
+ return(TRUE);
+ } else {
+ if (!privdebug && debug) {
+ privdebug = TRUE;
+ debug = FALSE;
+ }
+ return(FALSE);
+ }
+}
+
+static int stringhpos, stringvpos;
+
+void
+startstring(void) {
+ if (!in_string) {
+ stringhpos = hpos;
+ stringvpos = vpos;
+ if (pageon()) Bprint(Bstdout, "(");
+ in_string = 1;
+ }
+}
+
+void
+endstring(void) {
+ if (in_string) {
+ if (pageon()) Bprint(Bstdout, ") %d %d w\n", stringhpos, stringvpos);
+ in_string = 0;
+ }
+}
+
+BOOLEAN
+isinstring(void) {
+ return(in_string);
+}
+
+void
+startpage(void) {
+ ++char_no;
+ ++line_no;
+ ++page_no;
+ if (pageon()) {
+ ++pages_printed;
+ Bprint(Bstdout, "%s %d %d\n", PAGE, page_no, pages_printed);
+ Bprint(Bstdout, "/saveobj save def\n");
+ Bprint(Bstdout, "mark\n");
+ Bprint(Bstdout, "%d pagesetup\n", pages_printed);
+ }
+}
+
+void
+endpage(void) {
+ endstring();
+ curpostfontid = -1;
+ line_no = 0;
+ char_no = 0;
+ if (pageon()) {
+ Bprint(Bstdout, "cleartomark\n");
+ Bprint(Bstdout, "showpage\n");
+ Bprint(Bstdout, "saveobj restore\n");
+ Bprint(Bstdout, "%s %d %d\n", ENDPAGE, page_no, pages_printed);
+ }
+}
+
+/* This was taken from postprint */
+
+int
+cat(char *filename) {
+ Biobuf *bfile;
+ Biobufhdr *Bfile;
+ int n;
+ static char buf[Bsize];
+
+ if ((bfile = Bopen(filename, OREAD)) == 0) {
+ return(1);
+ }
+ Bfile = &(bfile->Biobufhdr);
+ while ((n=Bread(Bfile, buf, Bsize)) > 0) {
+ if (Bwrite(Bstdout, buf, n) != n)
+ break;
+ }
+ Bterm(Bfile);
+ if (n != 0) {
+ return(1);
+ }
+ return(0);
+}
+extern int debug;
+void *
+galloc(void *ptr, int size, char *perstr) {
+ void *x;
+
+ if ((x=realloc(ptr, size)) == 0) {
+ perror(perstr);
+ exits("malloc");
+ }
+ return(x);
+}
+
+static char *errorstrings[] = {
+ {""}, /* NONE */
+ {"WARNING"},
+ {"FATAL"}
+};
+
+char *programname;
+char *inputfilename = "<stdin>";
+int inputlineno;
+
+void
+error(int errtype, char *fmt, ...) {
+ va_list arg;
+
+ Bflush(Bstdout);
+ Bflush(Bstderr);
+ fprint(2, "%s: %s:%d :%s: ", programname, inputfilename, inputlineno, errorstrings[errtype]);
+ va_start(arg, fmt);
+ vfprint(2, fmt, arg);
+ va_end(arg);
+ if (errtype == FATAL)
+ exits("fatal error");
+}
diff --git a/sys/src/cmd/postscript/common/common.h b/sys/src/cmd/postscript/common/common.h
new file mode 100755
index 000000000..85805bbf8
--- /dev/null
+++ b/sys/src/cmd/postscript/common/common.h
@@ -0,0 +1,43 @@
+#define NONE 0
+#define WARNING 1
+#define FATAL 2
+
+#define RUNEGETGROUP(a) ((a>>8)&0xff)
+#define RUNEGETCHAR(a) (a&0xff)
+
+typedef int BOOLEAN;
+
+#define TRUE 1
+#define FALSE 0
+
+#define NUMOFONTS 0x100
+#define FONTSIZE 0x100
+
+extern char *programname;
+extern char *inputfilename;
+extern int inputlineno;
+
+extern int page_no;
+extern int pages_printed;
+extern int curpostfontid;
+extern int hpos, vpos;
+
+extern Biobufhdr *Bstdout, *Bstderr;
+
+struct strtab {
+ int size;
+ char *str;
+ int used;
+};
+
+extern struct strtab charcode[];
+BOOLEAN pageon(void);
+void startstring(void);
+void endstring(void);
+BOOLEAN isinstring(void);
+void startpage(void);
+void endpage(void);
+int cat(char *);
+int Bgetfield(Biobufhdr *, int, void *, int);
+void *galloc(void *, int, char *);
+void pagelist(char *);
diff --git a/sys/src/cmd/postscript/common/ext.h b/sys/src/cmd/postscript/common/ext.h
new file mode 100755
index 000000000..a811cca67
--- /dev/null
+++ b/sys/src/cmd/postscript/common/ext.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * External varibles - most are in glob.c.
+ *
+ */
+
+extern char **argv; /* global so everyone can use them */
+extern int argc;
+
+extern int x_stat; /* program exit status */
+extern int debug; /* debug flag */
+extern int ignore; /* what we do with FATAL errors */
+
+extern long lineno; /* line number */
+extern long position; /* byte position */
+extern char *prog_name; /* and program name - for errors */
+extern char *temp_file; /* temporary file - for some programs */
+extern char *fontencoding; /* text font encoding scheme */
+
+extern int dobbox; /* enable BoundingBox stuff if TRUE */
+extern double pageheight; /* only for BoundingBox calculations! */
+extern double pagewidth;
+
+extern int reading; /* input */
+extern int writing; /* and output encoding */
+
+extern char *optarg; /* for getopt() */
+extern int optind;
+
+extern void interrupt();
+extern char *tempnam(char*,char*);
+/*
+ * extern char *malloc();
+ * extern char *calloc();
+ * extern char *strtok();
+ * extern long ftell();
+ * extern double atof();
+ * extern double sqrt();
+ * extern double atan2();
+ */
diff --git a/sys/src/cmd/postscript/common/gen.h b/sys/src/cmd/postscript/common/gen.h
new file mode 100755
index 000000000..ba8cbbac3
--- /dev/null
+++ b/sys/src/cmd/postscript/common/gen.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * A few definitions that shouldn't have to change. Used by most programs in
+ * this package.
+ *
+ */
+
+#define PROGRAMVERSION "3.3.2"
+
+#define NON_FATAL 0
+#define FATAL 1
+#define USER_FATAL 2
+
+#define OFF 0
+#define ON 1
+
+#define FALSE 0
+#define TRUE 1
+
+#define BYTE 8
+#define BMASK 0377
+
+#define POINTS 72.3
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+#define ONEBYTE 0
+#define UTFENCODING 1
+
+#define READING ONEBYTE
+#define WRITING ONEBYTE
+
+/*
+ *
+ * DOROUND controls whether some translators include file ROUNDPAGE (path.h)
+ * after the prologue. Used to round page dimensions obtained from the clippath
+ * to know paper sizes. Enabled by setting DOROUND to TRUE (or 1).
+ *
+ */
+
+#define DOROUND TRUE
+
+/*
+ *
+ * Default resolution and the height and width of a page (in case we need to get
+ * to upper left corner) - only used in BoundingBox calculations!!
+ *
+ */
+
+#define DEFAULT_RES 72
+#define PAGEHEIGHT 11.0 * DEFAULT_RES
+#define PAGEWIDTH 8.5 * DEFAULT_RES
+
+/*
+ *
+ * Simple macros.
+ *
+ */
+
+#define ABS(A) ((A) >= 0 ? (A) : -(A))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
diff --git a/sys/src/cmd/postscript/common/getopt.c b/sys/src/cmd/postscript/common/getopt.c
new file mode 100755
index 000000000..3922c12b8
--- /dev/null
+++ b/sys/src/cmd/postscript/common/getopt.c
@@ -0,0 +1,56 @@
+#ifndef _POSIX_SOURCE
+#include <u.h>
+#include <libc.h>
+#endif
+#include <stdio.h>
+#define ERR(str, chr) if(opterr){fprintf(stderr, "%s%s%c\n", argv[0], str, chr);}
+int opterr = 1;
+int optind = 1;
+int optopt;
+char *optarg;
+char *strchr();
+
+int
+getopt (argc, argv, opts)
+char **argv, *opts;
+{
+ static int sp = 1;
+ register c;
+ register char *cp;
+
+ if (sp == 1)
+ if (optind >= argc ||
+ argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return EOF;
+ else if (strcmp(argv[optind], "--") == 0) {
+ optind++;
+ return EOF;
+ }
+ optopt = c = argv[optind][sp];
+ if (c == ':' || (cp=strchr(opts, c)) == NULL) {
+ ERR (": illegal option -- ", c);
+ if (argv[optind][++sp] == '\0') {
+ optind++;
+ sp = 1;
+ }
+ return '?';
+ }
+ if (*++cp == ':') {
+ if (argv[optind][sp+1] != '\0')
+ optarg = &argv[optind++][sp+1];
+ else if (++optind >= argc) {
+ ERR (": option requires an argument -- ", c);
+ sp = 1;
+ return '?';
+ } else
+ optarg = argv[optind++];
+ sp = 1;
+ } else {
+ if (argv[optind][++sp] == '\0') {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return c;
+}
diff --git a/sys/src/cmd/postscript/common/glob.c b/sys/src/cmd/postscript/common/glob.c
new file mode 100755
index 000000000..2826f4e5f
--- /dev/null
+++ b/sys/src/cmd/postscript/common/glob.c
@@ -0,0 +1,29 @@
+/*
+ *
+ * Global varibles - for PostScript translators.
+ *
+ */
+
+#include <stdio.h>
+#include "gen.h"
+
+char **argv; /* global so everyone can use them */
+int argc;
+
+int x_stat = 0; /* program exit status */
+int debug = OFF; /* debug flag */
+int ignore = OFF; /* what we do with FATAL errors */
+
+long lineno = 0; /* line number */
+long position = 0; /* byte position */
+char *prog_name = ""; /* and program name - for errors */
+char *temp_file = NULL; /* temporary file - for some programs */
+char *fontencoding = NULL; /* text font encoding scheme */
+
+int dobbox = FALSE; /* enable BoundingBox stuff if TRUE */
+double pageheight = PAGEHEIGHT; /* only for BoundingBox calculations! */
+double pagewidth = PAGEWIDTH;
+
+int reading = UTFENCODING; /* input */
+int writing = WRITING; /* and output encoding */
+
diff --git a/sys/src/cmd/postscript/common/misc.c b/sys/src/cmd/postscript/common/misc.c
new file mode 100755
index 000000000..25bd37aac
--- /dev/null
+++ b/sys/src/cmd/postscript/common/misc.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * General purpose routines.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "gen.h"
+#include "ext.h"
+#include "path.h"
+
+int nolist = 0; /* number of specified ranges */
+int olist[50]; /* processing range pairs */
+
+/*****************************************************************************/
+
+out_list(str)
+
+ char *str;
+
+{
+
+ int start, stop;
+
+/*
+ *
+ * Grab page ranges from str, save them in olist[], and update the nolist
+ * count. Range syntax matches nroff/troff syntax.
+ *
+ */
+
+ while ( *str && nolist < sizeof(olist) - 2 ) {
+ start = stop = str_convert(&str, 0);
+
+ if ( *str == '-' && *str++ )
+ stop = str_convert(&str, 9999);
+
+ if ( start > stop )
+ error(FATAL, "illegal range %d-%d", start, stop);
+
+ olist[nolist++] = start;
+ olist[nolist++] = stop;
+
+ if ( *str != '\0' ) str++;
+ } /* End while */
+
+ olist[nolist] = 0;
+
+} /* End of out_list */
+
+/*****************************************************************************/
+
+in_olist(num)
+
+ int num;
+
+{
+
+ int i;
+
+/*
+ *
+ * Return ON if num is in the current page range list. Print everything if
+ * there's no list.
+ *
+ */
+ if ( nolist == 0 )
+ return(ON);
+
+ for ( i = 0; i < nolist; i += 2 )
+ if ( num >= olist[i] && num <= olist[i+1] )
+ return(ON);
+
+ return(OFF);
+
+} /* End of in_olist */
+
+/*****************************************************************************/
+
+setencoding(name)
+
+ char *name;
+
+{
+
+ char path[150];
+
+/*
+ *
+ * Include the font encoding file selected by name. It's a full pathname if
+ * it begins with /, otherwise append suffix ".enc" and look for the file in
+ * ENCODINGDIR. Missing files are silently ignored.
+ *
+ */
+
+ if ( name == NULL )
+ name = "Default";
+
+ if ( *name == '/' )
+ strcpy(path, name);
+ else sprintf(path, "%s/%s.enc", ENCODINGDIR, name);
+
+ if ( cat(path) == TRUE )
+ writing = strncmp(name, "UTF", 3) == 0;
+
+} /* End of setencoding */
+
+/*****************************************************************************/
+
+cat(file)
+
+ char *file;
+
+{
+
+ int fd_in;
+ int fd_out;
+ char buf[512];
+ int count;
+
+/*
+ *
+ * Copy *file to stdout. Return FALSE is there was a problem.
+ *
+ */
+
+ fflush(stdout);
+
+ if ( (fd_in = open(file, O_RDONLY)) == -1 )
+ return(FALSE);
+
+ fd_out = fileno(stdout);
+ while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
+ write(fd_out, buf, count);
+
+ close(fd_in);
+
+ return(TRUE);
+
+} /* End of cat */
+
+/*****************************************************************************/
+
+str_convert(str, err)
+
+ char **str;
+ int err;
+
+{
+
+ int i;
+
+/*
+ *
+ * Grab the next integer from **str and return its value or err if *str
+ * isn't an integer. *str is modified after each digit is read.
+ *
+ */
+
+ if ( ! isdigit(**str) )
+ return(err);
+
+ for ( i = 0; isdigit(**str); *str += 1 )
+ i = 10 * i + **str - '0';
+
+ return(i);
+
+} /* End of str_convert */
+
+/*****************************************************************************/
+
+error(kind, mesg, a1, a2, a3)
+
+ int kind;
+ char *mesg;
+ unsigned a1, a2, a3;
+
+{
+
+/*
+ *
+ * Print an error message and quit if kind is FATAL.
+ *
+ */
+
+ if ( mesg != NULL && *mesg != '\0' ) {
+ fprintf(stderr, "%s: ", prog_name);
+ fprintf(stderr, mesg, a1, a2, a3);
+ if ( lineno > 0 )
+ fprintf(stderr, " (line %d)", lineno);
+ if ( position > 0 )
+ fprintf(stderr, " (near byte %d)", position);
+ putc('\n', stderr);
+ } /* End if */
+
+ if ( kind == FATAL && ignore == OFF ) {
+ if ( temp_file != NULL )
+ unlink(temp_file);
+ exit(x_stat | 01);
+ } /* End if */
+
+} /* End of error */
+
+/*****************************************************************************/
+
+void interrupt(sig)
+
+ int sig;
+
+{
+
+/*
+ *
+ * Signal handler for translators.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+ exit(1);
+
+} /* End of interrupt */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/common/mkfile b/sys/src/cmd/postscript/common/mkfile
new file mode 100755
index 000000000..821c2b1b6
--- /dev/null
+++ b/sys/src/cmd/postscript/common/mkfile
@@ -0,0 +1,24 @@
+</$objtype/mkfile
+
+<../config
+
+LIB=com.a$O
+OFILES=bbox.$O\
+ glob.$O\
+ misc.$O\
+ request.$O\
+ rune.$O\
+ tempnam.$O\
+ getopt.$O\
+
+HFILES=comments.h\
+ gen.h\
+ ext.h\
+ request.h\
+ path.h\
+ rune.h\
+
+</sys/src/cmd/mklib
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -B
+CC=pcc
diff --git a/sys/src/cmd/postscript/common/path.h b/sys/src/cmd/postscript/common/path.h
new file mode 100755
index 000000000..afb9c4dad
--- /dev/null
+++ b/sys/src/cmd/postscript/common/path.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * pathname definitions for important files and directories.
+ *
+ */
+
+#define DPOST "/sys/lib/postscript/prologues/dpost.ps"
+#define POSTBGI "/sys/lib/postscript/prologues/postbgi.ps"
+#define POSTDAISY "/sys/lib/postscript/prologues/postdaisy.ps"
+#define POSTDMD "/sys/lib/postscript/prologues/postdmd.ps"
+#define POSTMD "/sys/lib/postscript/prologues/postmd.ps"
+#define POSTPLOT "/sys/lib/postscript/prologues/postplot.ps"
+#define POSTPRINT "/sys/lib/postscript/prologues/postprint.ps"
+#define POSTNPRINT "/sys/lib/postscript/prologues/postnprint.ps"
+#define POSTTEK "/sys/lib/postscript/prologues/posttek.ps"
+#define POSTGIF "/sys/lib/postscript/prologues/postgif.ps"
+
+#define BASELINE "/sys/lib/postscript/prologues/baseline.ps"
+#define COLOR "/sys/lib/postscript/prologues/color.ps"
+#define DRAW "/sys/lib/postscript/prologues/draw.ps"
+#define FORMFILE "/sys/lib/postscript/prologues/forms.ps"
+#define SHADEFILE "/sys/lib/postscript/prologues/shade.ps"
+#define KERNING "/sys/lib/postscript/prologues/kerning.ps"
+#define REQUESTFILE "/sys/lib/postscript/prologues/ps.requests"
+#define ROUNDPAGE "/sys/lib/postscript/prologues/roundpage.ps"
+
+#define ENCODINGDIR "/sys/lib/postscript/prologues"
+#define HOSTDIR "/sys/lib/postscript/font"
+#define FONTDIR "/sys/lib/troff/font"
+#define POSTLIBDIR "/sys/lib/postscript/prologues"
+#define TEMPDIR "/tmp"
+
diff --git a/sys/src/cmd/postscript/common/request.c b/sys/src/cmd/postscript/common/request.c
new file mode 100755
index 000000000..d8d7dd130
--- /dev/null
+++ b/sys/src/cmd/postscript/common/request.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * Things used to handle special requests (eg. manual feed) globally or on a per
+ * page basis. Requests are passed through to the translator using the -R option.
+ * The argument to -R can be "request", "request:page", or "request:page:file".
+ * If page is omitted (as in the first form) or set to 0 request will be applied
+ * to the global environment. In all other cases it applies only to the selected
+ * page. If a file is given, page must be supplied, and the lookup is in that file
+ * rather than *requestfile.
+ *
+ */
+
+#include <stdio.h>
+
+#include "gen.h" /* general purpose definitions */
+#include "request.h" /* a few special definitions */
+#include "path.h" /* for the default request file */
+
+Request request[MAXREQUEST]; /* next page or global request */
+int nextreq = 0; /* goes in request[nextreq] */
+char *requestfile = REQUESTFILE; /* default lookup file */
+
+/*****************************************************************************/
+
+saverequest(want)
+
+ char *want; /* grab code for this stuff */
+
+{
+
+ char *page; /* and save it for this page */
+ char *strtok();
+
+/*
+ *
+ * Save the request until we get to appropriate page - don't even bother with
+ * the lookup right now. Format of *want string is "request", "request:page", or
+ * "request:page:file", and we assume we can change the string here as needed.
+ * If page is omitted or given as 0 the request will be done globally. If *want
+ * includes a file, request and page must also be given, and in that case *file
+ * will be used for the lookup.
+ *
+ */
+
+ if ( nextreq < MAXREQUEST ) {
+ request[nextreq].want = strtok(want, ": ");
+ if ( (page = strtok(NULL, ": ")) == NULL )
+ request[nextreq].page = 0;
+ else request[nextreq].page = atoi(page);
+ if ( (request[nextreq].file = strtok(NULL, ": ")) == NULL )
+ request[nextreq].file = requestfile;
+ nextreq++;
+ } else error(NON_FATAL, "too many requests - ignoring %s", want);
+
+} /* End of saverequest */
+
+/*****************************************************************************/
+
+writerequest(page, fp_out)
+
+ int page; /* write everything for this page */
+ FILE *fp_out; /* to this file */
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Writes out all the requests that have been saved for page. Page 0 refers to
+ * the global environment and is done during initial setup.
+ *
+ */
+
+ for ( i = 0; i < nextreq; i++ )
+ if ( request[i].page == page )
+ dumprequest(request[i].want, request[i].file, fp_out);
+
+} /* End of writerequest */
+
+/*****************************************************************************/
+
+dumprequest(want, file, fp_out)
+
+ char *want; /* look for this string */
+ char *file; /* in this file */
+ FILE *fp_out; /* and write the value out here */
+
+{
+
+ char buf[100]; /* line buffer for reading *file */
+ FILE *fp_in;
+
+/*
+ *
+ * Looks for *want in the request file and if it's found the associated value
+ * is copied to the output file. Keywords (ie. the *want strings) begin an @ in
+ * the first column of file, while the values (ie. the stuff that's copied to
+ * the output file) starts on the next line and extends to the next keyword or
+ * to the end of file.
+ *
+ */
+
+ if ( (fp_in = fopen(file, "r")) != NULL ) {
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] == '@' && strncmp(want, &buf[1], strlen(want)) == 0 )
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] == '#' || buf[0] == '%' )
+ continue;
+ else if ( buf[0] != '@' )
+ fprintf(fp_out, "%s", buf);
+ else break;
+ fclose(fp_in);
+ } /* End if */
+
+} /* End of dumprequest */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/common/request.h b/sys/src/cmd/postscript/common/request.h
new file mode 100755
index 000000000..25d94d01a
--- /dev/null
+++ b/sys/src/cmd/postscript/common/request.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * Things used to handle special PostScript requests (like manual feed) globally
+ * or on a per page basis. All the translators I've supplied accept the -R option
+ * that can be used to insert special PostScript code before the global setup is
+ * done, or at the start of named pages. The argument to the -R option is a string
+ * that can be "request", "request:page", or "request:page:file". If page isn't
+ * given (as in the first form) or if it's 0 in the last two, the request applies
+ * to the global environment, otherwise request holds only for the named page.
+ * If a file name is given a page number must be supplied, and in that case the
+ * request will be looked up in that file.
+ *
+ */
+
+#define MAXREQUEST 30
+
+typedef struct {
+ char *want;
+ int page;
+ char *file;
+} Request;
+
diff --git a/sys/src/cmd/postscript/common/rune.c b/sys/src/cmd/postscript/common/rune.c
new file mode 100755
index 000000000..01ee6ba81
--- /dev/null
+++ b/sys/src/cmd/postscript/common/rune.c
@@ -0,0 +1,142 @@
+#include "rune.h"
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(unsigned char*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(unsigned char*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(unsigned char*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(unsigned char*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/postscript/common/rune.h b/sys/src/cmd/postscript/common/rune.h
new file mode 100755
index 000000000..9c1fd4fd0
--- /dev/null
+++ b/sys/src/cmd/postscript/common/rune.h
@@ -0,0 +1,19 @@
+/*
+ *
+ * Rune declarations - for supporting UTF encoding.
+ *
+ */
+
+#define RUNELIB 1
+
+#ifdef RUNELIB
+typedef unsigned short Rune;
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a utf sequence (<) */
+ Runeself = 0x80, /* rune and utf sequences are the same (<) */
+ Runeerror = 0xFFFD, /* decoding error in utf */
+};
+#endif
diff --git a/sys/src/cmd/postscript/common/tempnam.c b/sys/src/cmd/postscript/common/tempnam.c
new file mode 100755
index 000000000..00d819cab
--- /dev/null
+++ b/sys/src/cmd/postscript/common/tempnam.c
@@ -0,0 +1,36 @@
+#if defined(V9) || defined(BSD4_2) || defined(plan9)
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+char *
+tempnam(char *dir, char *pfx)
+{
+ int pid;
+ char *tnm;
+ struct stat stb;
+ static int seq = 0;
+
+ if (dir == NULL)
+ dir = ".";
+#ifdef plan9
+ /* our access emulation has a race when checking for write access */
+ if (access(dir, R_OK|X_OK) == -1)
+#else
+ if (access(dir, R_OK|W_OK|X_OK) == -1)
+#endif
+ return NULL;
+ pid = getpid();
+ tnm = malloc(strlen(dir) + 1 + strlen(pfx) + 2*20 + 1);
+ if (tnm == NULL)
+ return NULL;
+ do {
+ sprintf(tnm, "%s/%s.%d.%d", dir, pfx, pid, seq++);
+ errno = 0;
+ } while (stat(tnm, &stb) >= 0 && seq < 256);
+ return tnm;
+}
+#endif
diff --git a/sys/src/cmd/postscript/config b/sys/src/cmd/postscript/config
new file mode 100755
index 000000000..742b4560a
--- /dev/null
+++ b/sys/src/cmd/postscript/config
@@ -0,0 +1,11 @@
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+POSTBIN=$ROOT/$objtype/bin/aux
+DKHOST=FALSE
+DKSTREAMS=FALSE
+ROUNDPAGE=TRUE
+
+FONTDIR=/sys/lib/troff/font
+POSTLIB=/sys/lib/postscript/prologues
diff --git a/sys/src/cmd/postscript/cropmarks/cropmarks.ps b/sys/src/cmd/postscript/cropmarks/cropmarks.ps
new file mode 100755
index 000000000..256891a9a
--- /dev/null
+++ b/sys/src/cmd/postscript/cropmarks/cropmarks.ps
@@ -0,0 +1,131 @@
+%
+% Center pages, based on pageheight and pagewidth, and redefine showpage
+% to put cropmarks at each corner. Device dependent code to expand the
+% paper size goes in procedure expandpagesize. Currently only supports
+% a Linotronic 200P typesetter using 12 inch wide paper. You'll have to
+% add code to expandpagesize to support different typesetters or even a
+% 200P that's running differently.
+%
+
+/CropmarkDict 40 dict dup begin
+
+/expandpage true def
+/magnification 1 def
+/pageheight 11.0 def
+/pagewidth 8.5 def
+/scaletofit false def
+/scaling 1 def
+
+/marklength .3 def % inches
+/markstart .125 def % inches
+/markend .04 def % inches
+/marklinewidth .25 def % points
+
+/inch {72 mul} def
+/min {2 copy gt {exch} if pop} def
+/max {2 copy lt {exch} if pop} def
+
+/setup {
+ /markspace markstart marklength add markend add inch marklinewidth add def
+ /totalheight pageheight inch markspace 2 mul add def
+ /totalwidth pagewidth inch markspace 2 mul add def
+
+ pagedimensions
+ checkpagesize
+ /scaling getscaling def
+ xcenter ycenter translate
+ scaling scaling scale
+ pagewidth inch 2 div neg pageheight inch 2 div neg translate
+ clippage
+} def
+
+/pagedimensions {
+ clippath pathbbox newpath
+ 4 -1 roll exch 4 1 roll 4 copy
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+} def
+
+/checkpagesize {
+ height totalheight lt width totalwidth lt or expandpage and {
+ expandpagesize
+ pagedimensions
+ } if
+} def
+
+/expandpagesize { % device dependent code
+ /Product statusdict begin /product where {pop product}{()} ifelse end def
+
+ Product (Linotype) eq { % Linotronic 200P and other models?
+ statusdict /setpageparams known {
+ /maxwidth 12.0 inch def % 12 inch wide paper?
+ totalheight maxwidth le {
+ totalheight
+ totalwidth
+ maxwidth totalheight sub 2 div
+ 0
+ }{
+ totalwidth maxwidth min
+ totalheight
+ maxwidth totalwidth maxwidth min sub 2 div
+ 1
+ } ifelse
+ statusdict /setpageparams get exec
+ } if
+ } if
+} def
+
+/getscaling {
+ scaletofit
+ {height totalheight div width totalwidth div min 1 min}
+ {1}
+ ifelse
+} def
+
+/clippage {
+ newpath
+ 0 0 moveto
+ pagewidth inch 0 rlineto
+ 0 pageheight inch rlineto
+ pagewidth neg inch 0 rlineto
+ closepath clip
+ newpath
+} def
+
+/cropmark {
+ gsave
+ translate
+ rotate
+ marklinewidth dup translate
+ 0 0 transform round exch round exch itransform translate
+ markstart inch 0 moveto marklength inch 0 rlineto stroke
+ 0 markstart inch moveto 0 marklength inch rlineto stroke
+ grestore
+} bind def
+
+/@PreviousShowpage /showpage load def
+
+end def
+
+%
+% Cropmarks - in the default coordinate system.
+%
+
+/showpage {
+ gsave
+ CropmarkDict begin
+ initgraphics
+ marklinewidth setlinewidth
+ xcenter ycenter translate
+ scaling scaling scale
+ 0 pagewidth inch 2 div pageheight inch 2 div cropmark
+ 90 pagewidth inch neg 2 div pageheight inch 2 div cropmark
+ 180 pagewidth inch neg 2 div pageheight inch 2 div neg cropmark
+ 270 pagewidth inch 2 div pageheight inch 2 div neg cropmark
+ @PreviousShowpage
+ end
+ grestore
+} bind def
+
diff --git a/sys/src/cmd/postscript/cropmarks/cropmarks.rc b/sys/src/cmd/postscript/cropmarks/cropmarks.rc
new file mode 100755
index 000000000..43ada5c69
--- /dev/null
+++ b/sys/src/cmd/postscript/cropmarks/cropmarks.rc
@@ -0,0 +1,74 @@
+#!/bin/rc
+# Center pages and put cropmarks at each corner. Physical page size
+# is set with -w and -h. The default is 8.5 by 11.0 inches. Device
+# dependent code to change paper size (e.g. with setpageparams) goes
+# in the prologue. You may need to customize the device dependent
+# code that we distribute. By default it only supports variable page
+# sizes on Linotronic typesetters, and assumes those typesetters are
+# using 12 inch wide paper. Use -d to disable execution of device
+# dependent PostScript code.
+#
+# What's here was written quickly and will likely be very different
+# in our next release. It should be part of a more general program!!
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+PROLOGUE=$POSTLIB/cropmarks.ps
+
+EXPANDPAGE=true
+PAGEWIDTH=8.5
+PAGEHEIGHT=11.0
+SCALETOFIT=false
+XOFFSET=0.0
+YOFFSET=0.0
+
+NONCONFORMING=%!PS
+ENDPROLOG=%%EndProlog
+BEGINSETUP=%%BeginSetup
+ENDSETUP=%%EndSetup
+
+while (! ~ $#* 0 && ~ $1 -*) {
+ switch ($1) {
+ case -d; EXPANDPAGE=false
+
+ case -h; shift; PAGEHEIGHT=$1
+ case -h*; PAGEHEIGHT=`{echo $1 | sed s/-h//}
+
+ case -s; SCALETOFIT=true
+
+ case -w; shift; PAGEWIDTH=$1
+ case -w*; PAGEWIDTH=`{echo $1 | sed s/-w//}
+
+ case -x; shift; XOFFSET=$1
+ case -x*; XOFFSET=`{echo $1 | sed s/-x//}
+
+ case -y; shift; YOFFSET=$1
+ case -y*; YOFFSET=`{echo $1 | sed s/-y//}
+
+ case -L; shift; PROLOGUE=$1
+ case -L*; PROLOGUE=`{echo $1 | sed s/-L//}
+
+ case --;
+
+ case -*; echo '$0: illegal option $1' >[1=2]; exit 1
+
+ }
+ shift
+}
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo $ENDPROLOG
+echo $BEGINSETUP
+echo 'CropmarkDict begin'
+echo '/pageheight '$PAGEHEIGHT' def'
+echo '/pagewidth '$PAGEWIDTH' def'
+echo '/expandpage '$EXPANDPAGE' def'
+echo '/scaletofit '$SCALETOFIT' def'
+echo '/xoffset '$XOFFSET' def'
+echo '/yoffset '$YOFFSET' def'
+echo 'setup'
+echo 'end'
+echo $ENDSETUP
+
+cat $*
diff --git a/sys/src/cmd/postscript/cropmarks/mkfile b/sys/src/cmd/postscript/cropmarks/mkfile
new file mode 100755
index 000000000..ce9dbd4df
--- /dev/null
+++ b/sys/src/cmd/postscript/cropmarks/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+
+<../config
+
+all:V: cropmarks
+
+install:V: $POSTBIN/cropmarks $POSTLIB/cropmarks.ps
+
+installall:V: install
+
+$POSTLIB/cropmarks.ps: cropmarks.ps
+ cp $prereq $target
+
+$POSTBIN/cropmarks: cropmarks
+ cp $prereq $target
+
+cropmarks: cropmarks.rc
+ sed 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' cropmarks.rc >cropmarks
+ chmod 775 cropmarks
+
+clean nuke:V:
+ rm -f cropmarks
diff --git a/sys/src/cmd/postscript/devpost.add/C1 b/sys/src/cmd/postscript/devpost.add/C1
new file mode 100755
index 000000000..19be6b2fd
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/C1
@@ -0,0 +1,161 @@
+name C1
+fontname CenturyOldStyle-Regular
+ligatures fi fl 0
+spacewidth 25
+charset
+! 26 2 33
+" 40 2 34
+dq "
+# 50 2 35
+$ 50 3 36
+% 58 2 37
+& 79 2 38
+' 21 2 39
+( 40 3 40
+) 40 3 41
+* 50 2 42
++ 50 0 43
+, 25 1 44
+hy 24 0 45
+- "
+. 25 0 46
+/ 53 3 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 40 2 63
+@ 85 2 64
+A 66 2 65
+B 69 2 66
+C 69 2 67
+D 76 2 68
+E 66 2 69
+F 61 2 70
+G 74 2 71
+H 76 2 72
+I 34 2 73
+J 40 3 74
+K 71 2 75
+L 58 2 76
+M 95 2 77
+N 76 2 78
+O 76 2 79
+P 61 2 80
+Q 76 3 81
+R 63 2 82
+S 55 2 83
+T 63 2 84
+U 74 2 85
+V 66 2 86
+W 95 2 87
+X 63 2 88
+Y 63 2 89
+Z 58 2 90
+[ 40 3 91
+\ 25 2 92
+bs "
+] 40 3 93
+--- 50 2 94
+--- 50 1 95
+` 21 2 96
+a 47 0 97
+b 55 2 98
+c 47 0 99
+d 55 2 100
+e 50 0 101
+f 29 2 102
+g 55 1 103
+h 58 2 104
+i 26 2 105
+j 24 3 106
+k 55 2 107
+l 26 2 108
+m 84 0 109
+n 55 0 110
+o 53 0 111
+p 53 1 112
+q 55 1 113
+r 40 0 114
+s 45 0 115
+t 31 2 116
+u 55 0 117
+v 47 0 118
+w 71 0 119
+x 53 0 120
+y 50 1 121
+z 45 0 122
+{ 40 3 123
+--- 25 2 124
+} 40 3 125
+--- 50 0 126
+--- 26 1 161
+ct 50 2 162
+ps 50 2 163
+fr 9 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 21 2 169
+`` 40 2 170
+--- 47 0 171
+--- 29 0 172
+--- 29 0 173
+fi 58 2 174
+fl 55 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 25 0 180
+pg 66 3 182
+--- 66 0 183
+--- 21 1 184
+--- 40 1 185
+'' 40 2 186
+--- 47 0 187
+--- 100 0 188
+--- 92 2 189
+--- 40 1 191
+ga 29 2 193
+\` "
+aa 29 2 194
+\' "
+^a 37 2 195
+^ "
+~a 50 2 196
+~ "
+-a 47 2 197
+Ua 50 2 198
+.a 26 2 199
+:a 47 2 200
+oa 34 2 202
+,a 34 1 203
+"a 40 2 205
+Ca 37 1 206
+va 37 2 207
+em 100 0 208
+--- 103 2 225
+--- 34 2 227
+--- 58 2 232
+--- 76 2 233
+--- 108 2 234
+--- 34 2 235
+--- 76 0 241
+--- 26 0 245
+--- 26 2 248
+--- 53 0 249
+--- 84 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/C2 b/sys/src/cmd/postscript/devpost.add/C2
new file mode 100755
index 000000000..359341a67
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/C2
@@ -0,0 +1,161 @@
+name C2
+fontname CenturyOldStyle-Italic
+ligatures fi fl 0
+spacewidth 27
+charset
+! 31 2 33
+" 35 2 34
+dq "
+# 54 0 35
+$ 54 3 36
+% 70 2 37
+& 75 2 38
+' 21 2 39
+( 46 3 40
+) 46 3 41
+* 59 2 42
++ 54 0 43
+, 27 1 44
+hy 25 0 45
+- "
+. 27 0 46
+/ 46 3 47
+0 54 2 48
+1 54 0 49
+2 54 2 50
+3 54 2 51
+4 54 0 52
+5 54 0 53
+6 54 2 54
+7 54 0 55
+8 54 2 56
+9 54 2 57
+: 27 0 58
+; 27 1 59
+--- 54 0 60
+= 54 0 61
+--- 54 0 62
+? 48 2 63
+@ 78 2 64
+A 67 2 65
+B 68 2 66
+C 65 2 67
+D 75 2 68
+E 66 2 69
+F 61 2 70
+G 72 2 71
+H 77 2 72
+I 36 2 73
+J 34 3 74
+K 68 2 75
+L 58 2 76
+M 90 2 77
+N 74 2 78
+O 74 2 79
+P 59 2 80
+Q 75 3 81
+R 65 2 82
+S 57 2 83
+T 63 2 84
+U 72 2 85
+V 62 2 86
+W 88 2 87
+X 64 2 88
+Y 61 2 89
+Z 57 2 90
+[ 46 3 91
+\ 27 2 92
+bs "
+] 46 3 93
+--- 54 2 94
+--- 50 1 95
+` 21 2 96
+a 54 0 97
+b 46 2 98
+c 41 0 99
+d 50 2 100
+e 42 0 101
+f 25 3 102
+g 45 1 103
+h 52 2 104
+i 31 2 105
+j 27 3 106
+k 48 2 107
+l 27 2 108
+m 82 0 109
+n 56 0 110
+o 47 0 111
+p 50 1 112
+q 49 1 113
+r 40 0 114
+s 35 0 115
+t 30 0 116
+u 56 0 117
+v 47 0 118
+w 69 0 119
+x 41 0 120
+y 40 1 121
+z 39 0 122
+{ 46 3 123
+--- 27 2 124
+} 46 3 125
+--- 54 0 126
+--- 31 1 161
+ct 54 0 162
+ps 54 2 163
+fr 5 2 164
+yn 54 0 165
+fn 54 3 166
+sc 59 3 167
+cr 54 0 168
+--- 19 2 169
+`` 37 2 170
+--- 45 0 171
+--- 29 0 172
+--- 29 0 173
+fi 52 3 174
+fl 52 3 175
+en 50 0 177
+\- "
+dg 59 3 178
+dd 59 3 179
+--- 27 0 180
+pg 61 3 182
+--- 61 0 183
+--- 21 1 184
+--- 37 1 185
+'' 37 2 186
+--- 45 0 187
+--- 100 0 188
+--- 103 2 189
+--- 48 1 191
+ga 39 2 193
+\` "
+aa 31 2 194
+\' "
+^a 40 2 195
+^ "
+~a 51 2 196
+~ "
+-a 47 0 197
+Ua 51 2 198
+.a 26 2 199
+:a 46 2 200
+oa 32 2 202
+,a 35 1 203
+"a 39 2 205
+Ca 34 1 206
+va 40 2 207
+em 100 0 208
+--- 94 2 225
+--- 32 2 227
+--- 58 2 232
+--- 74 2 233
+--- 107 2 234
+--- 32 2 235
+--- 69 0 241
+--- 31 0 245
+--- 27 2 248
+--- 47 0 249
+--- 71 0 250
+--- 53 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/C3 b/sys/src/cmd/postscript/devpost.add/C3
new file mode 100755
index 000000000..eef7eef09
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/C3
@@ -0,0 +1,161 @@
+name C3
+fontname CenturyOldStyle-Bold
+ligatures fi fl 0
+spacewidth 33
+charset
+! 34 2 33
+" 40 2 34
+dq "
+# 56 2 35
+$ 66 3 36
+% 79 2 37
+& 84 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 56 2 42
++ 56 0 43
+, 33 1 44
+hy 32 0 45
+- "
+. 33 0 46
+/ 51 3 47
+0 66 2 48
+1 66 2 49
+2 66 2 50
+3 66 2 51
+4 66 2 52
+5 66 2 53
+6 66 2 54
+7 66 2 55
+8 66 2 56
+9 66 2 57
+: 33 0 58
+; 33 1 59
+--- 56 0 60
+= 56 0 61
+--- 56 0 62
+? 47 2 63
+@ 72 2 64
+A 69 2 65
+B 76 2 66
+C 70 2 67
+D 80 2 68
+E 71 2 69
+F 64 2 70
+G 78 2 71
+H 83 2 72
+I 41 2 73
+J 48 2 74
+K 76 2 75
+L 63 2 76
+M 94 2 77
+N 80 2 78
+O 81 2 79
+P 72 2 80
+Q 81 3 81
+R 72 2 82
+S 61 2 83
+T 60 2 84
+U 78 2 85
+V 64 2 86
+W 97 2 87
+X 68 2 88
+Y 67 2 89
+Z 59 2 90
+[ 39 3 91
+\ 28 2 92
+bs "
+] 39 3 93
+--- 56 2 94
+--- 50 1 95
+` 28 2 96
+a 53 0 97
+b 60 2 98
+c 52 0 99
+d 62 2 100
+e 54 0 101
+f 30 2 102
+g 52 3 103
+h 62 2 104
+i 31 2 105
+j 27 3 106
+k 60 2 107
+l 30 2 108
+m 92 0 109
+n 62 0 110
+o 57 0 111
+p 61 1 112
+q 60 1 113
+r 44 0 114
+s 50 0 115
+t 33 2 116
+u 63 0 117
+v 50 0 118
+w 74 0 119
+x 53 0 120
+y 49 1 121
+z 44 0 122
+{ 39 3 123
+--- 28 2 124
+} 39 3 125
+--- 56 0 126
+--- 34 1 161
+ct 66 2 162
+ps 66 2 163
+fr 10 2 164
+yn 66 2 165
+fn 66 3 166
+sc 56 3 167
+cr 66 2 168
+--- 22 2 169
+`` 52 2 170
+--- 58 0 171
+--- 37 0 172
+--- 37 0 173
+fi 62 2 174
+fl 61 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 33 0 180
+pg 62 3 182
+--- 62 0 183
+--- 28 1 184
+--- 52 1 185
+'' 52 2 186
+--- 58 0 187
+--- 100 0 188
+--- 118 2 189
+--- 47 1 191
+ga 34 2 193
+\` "
+aa 34 2 194
+\' "
+^a 45 2 195
+^ "
+~a 56 2 196
+~ "
+-a 53 2 197
+Ua 56 2 198
+.a 29 2 199
+:a 51 2 200
+oa 34 2 202
+,a 37 1 203
+"a 50 2 205
+Ca 38 1 206
+va 45 2 207
+em 100 0 208
+--- 109 2 225
+--- 40 2 227
+--- 63 2 232
+--- 81 2 233
+--- 114 2 234
+--- 40 2 235
+--- 83 0 241
+--- 31 0 245
+--- 30 2 248
+--- 57 0 249
+--- 90 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F1 b/sys/src/cmd/postscript/devpost.add/F1
new file mode 100755
index 000000000..e6e95cd2c
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F1
@@ -0,0 +1,161 @@
+name F1
+fontname FranklinGothic-Book
+ligatures fi fl 0
+spacewidth 30
+charset
+! 30 2 33
+" 46 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 70 2 37
+& 68 2 38
+' 30 2 39
+( 30 3 40
+) 30 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 22 0 45
+- "
+. 30 0 46
+/ 52 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 54 2 63
+@ 80 2 64
+A 56 2 65
+B 62 2 66
+C 62 2 67
+D 66 2 68
+E 56 2 69
+F 48 2 70
+G 66 2 71
+H 66 2 72
+I 26 2 73
+J 40 2 74
+K 62 2 75
+L 50 2 76
+M 82 2 77
+N 66 2 78
+O 66 2 79
+P 56 2 80
+Q 66 3 81
+R 62 2 82
+S 62 2 83
+T 50 2 84
+U 64 2 85
+V 56 2 86
+W 82 2 87
+X 54 2 88
+Y 56 2 89
+Z 54 2 90
+[ 30 3 91
+\ 52 2 92
+bs "
+] 30 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 54 0 97
+b 54 2 98
+c 48 0 99
+d 54 2 100
+e 54 0 101
+f 30 2 102
+g 50 1 103
+h 54 2 104
+i 24 2 105
+j 22 3 106
+k 50 2 107
+l 24 2 108
+m 82 0 109
+n 54 0 110
+o 54 0 111
+p 54 1 112
+q 54 1 113
+r 32 0 114
+s 52 0 115
+t 32 2 116
+u 54 0 117
+v 44 0 118
+w 66 0 119
+x 44 0 120
+y 42 1 121
+z 42 0 122
+{ 30 3 123
+--- 26 2 124
+} 30 3 125
+--- 60 0 126
+--- 30 1 161
+ct 60 2 162
+ps 60 2 163
+fr 16 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 46 2 170
+--- 40 0 171
+--- 26 0 172
+--- 26 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 46 1 185
+'' 46 2 186
+--- 40 0 187
+--- 100 0 188
+--- 104 2 189
+--- 54 1 191
+ga 38 2 193
+\` "
+aa 38 2 194
+\' "
+^a 48 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 48 2 198
+.a 24 2 199
+:a 40 2 200
+oa 32 2 202
+,a 32 1 203
+"a 50 2 205
+Ca 32 1 206
+va 48 2 207
+em 100 0 208
+--- 96 2 225
+--- 36 2 227
+--- 50 2 232
+--- 66 2 233
+--- 100 2 234
+--- 36 2 235
+--- 90 0 241
+--- 24 0 245
+--- 20 2 248
+--- 54 0 249
+--- 88 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F2 b/sys/src/cmd/postscript/devpost.add/F2
new file mode 100755
index 000000000..c237ae2bd
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F2
@@ -0,0 +1,161 @@
+name F2
+fontname FranklinGothic-BookOblique
+ligatures fi fl 0
+spacewidth 30
+charset
+! 30 2 33
+" 46 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 70 2 37
+& 68 2 38
+' 30 2 39
+( 30 3 40
+) 30 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 22 0 45
+- "
+. 30 0 46
+/ 52 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 54 2 63
+@ 80 2 64
+A 56 2 65
+B 62 2 66
+C 62 2 67
+D 66 2 68
+E 56 2 69
+F 48 2 70
+G 66 2 71
+H 66 2 72
+I 26 2 73
+J 40 2 74
+K 62 2 75
+L 50 2 76
+M 82 2 77
+N 66 2 78
+O 66 2 79
+P 56 2 80
+Q 66 3 81
+R 62 2 82
+S 62 2 83
+T 50 2 84
+U 64 2 85
+V 56 2 86
+W 82 2 87
+X 54 2 88
+Y 56 2 89
+Z 54 2 90
+[ 30 3 91
+\ 52 2 92
+bs "
+] 30 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 54 0 97
+b 54 2 98
+c 48 0 99
+d 54 2 100
+e 54 0 101
+f 30 2 102
+g 50 1 103
+h 54 2 104
+i 24 2 105
+j 22 3 106
+k 50 2 107
+l 24 2 108
+m 82 0 109
+n 54 0 110
+o 54 0 111
+p 54 1 112
+q 54 1 113
+r 32 0 114
+s 52 0 115
+t 32 2 116
+u 54 0 117
+v 44 0 118
+w 66 0 119
+x 44 0 120
+y 42 1 121
+z 42 0 122
+{ 30 3 123
+--- 26 2 124
+} 30 3 125
+--- 60 0 126
+--- 30 1 161
+ct 60 2 162
+ps 60 2 163
+fr 16 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 46 2 170
+--- 40 0 171
+--- 26 0 172
+--- 26 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 46 1 185
+'' 46 2 186
+--- 40 0 187
+--- 100 0 188
+--- 104 2 189
+--- 54 1 191
+ga 38 2 193
+\` "
+aa 38 2 194
+\' "
+^a 48 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 48 2 198
+.a 24 2 199
+:a 40 2 200
+oa 32 2 202
+,a 32 1 203
+"a 50 2 205
+Ca 32 1 206
+va 48 2 207
+em 100 0 208
+--- 96 2 225
+--- 36 2 227
+--- 50 2 232
+--- 66 2 233
+--- 100 2 234
+--- 36 2 235
+--- 90 0 241
+--- 24 0 245
+--- 20 2 248
+--- 54 0 249
+--- 88 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F3 b/sys/src/cmd/postscript/devpost.add/F3
new file mode 100755
index 000000000..cedb0e451
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F3
@@ -0,0 +1,161 @@
+name F3
+fontname FranklinGothic-Demi
+ligatures fi fl 0
+spacewidth 30
+charset
+! 32 2 33
+" 46 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 70 2 37
+& 72 2 38
+' 30 2 39
+( 38 3 40
+) 38 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 24 0 45
+- "
+. 30 0 46
+/ 60 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 54 2 63
+@ 80 2 64
+A 64 2 65
+B 66 2 66
+C 66 2 67
+D 66 2 68
+E 58 2 69
+F 54 2 70
+G 66 2 71
+H 66 2 72
+I 30 2 73
+J 40 2 74
+K 64 2 75
+L 50 2 76
+M 88 2 77
+N 66 2 78
+O 66 2 79
+P 62 2 80
+Q 66 3 81
+R 66 2 82
+S 60 2 83
+T 54 2 84
+U 66 2 85
+V 60 2 86
+W 90 2 87
+X 64 2 88
+Y 60 2 89
+Z 66 2 90
+[ 38 3 91
+\ 60 2 92
+bs "
+] 38 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 54 0 97
+b 54 2 98
+c 54 0 99
+d 54 2 100
+e 54 0 101
+f 30 2 102
+g 56 3 103
+h 54 2 104
+i 26 2 105
+j 26 3 106
+k 56 2 107
+l 26 2 108
+m 82 0 109
+n 54 0 110
+o 54 0 111
+p 54 1 112
+q 54 1 113
+r 34 0 114
+s 50 0 115
+t 38 2 116
+u 54 0 117
+v 48 0 118
+w 74 0 119
+x 54 0 120
+y 48 1 121
+z 42 0 122
+{ 38 3 123
+--- 30 2 124
+} 38 3 125
+--- 60 0 126
+--- 32 1 161
+ct 60 2 162
+ps 60 2 163
+fr 8 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 48 2 170
+--- 40 0 171
+--- 26 0 172
+--- 26 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 48 1 185
+'' 48 2 186
+--- 40 0 187
+--- 100 0 188
+--- 104 2 189
+--- 54 1 191
+ga 38 2 193
+\` "
+aa 40 2 194
+\' "
+^a 50 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 52 2 198
+.a 26 2 199
+:a 42 2 200
+oa 32 2 202
+,a 34 1 203
+"a 52 2 205
+Ca 34 1 206
+va 52 2 207
+em 100 0 208
+--- 90 2 225
+--- 36 2 227
+--- 52 2 232
+--- 66 2 233
+--- 96 2 234
+--- 36 2 235
+--- 82 0 241
+--- 26 0 245
+--- 26 2 248
+--- 54 0 249
+--- 86 0 250
+--- 66 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F4 b/sys/src/cmd/postscript/devpost.add/F4
new file mode 100755
index 000000000..c3093a531
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F4
@@ -0,0 +1,161 @@
+name F4
+fontname FranklinGothic-DemiOblique
+ligatures fi fl 0
+spacewidth 30
+charset
+! 32 2 33
+" 46 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 70 2 37
+& 72 2 38
+' 30 2 39
+( 38 3 40
+) 38 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 24 0 45
+- "
+. 30 0 46
+/ 60 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 54 2 63
+@ 80 2 64
+A 64 2 65
+B 66 2 66
+C 66 2 67
+D 66 2 68
+E 58 2 69
+F 54 2 70
+G 66 2 71
+H 66 2 72
+I 30 2 73
+J 40 2 74
+K 64 2 75
+L 50 2 76
+M 88 2 77
+N 66 2 78
+O 66 2 79
+P 62 2 80
+Q 66 3 81
+R 66 2 82
+S 60 2 83
+T 54 2 84
+U 66 2 85
+V 60 2 86
+W 90 2 87
+X 64 2 88
+Y 60 2 89
+Z 66 2 90
+[ 38 3 91
+\ 60 2 92
+bs "
+] 38 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 54 0 97
+b 54 2 98
+c 54 0 99
+d 54 2 100
+e 54 0 101
+f 30 2 102
+g 56 3 103
+h 54 2 104
+i 26 2 105
+j 26 3 106
+k 56 2 107
+l 26 2 108
+m 82 0 109
+n 54 0 110
+o 54 0 111
+p 54 1 112
+q 54 1 113
+r 34 0 114
+s 50 0 115
+t 38 2 116
+u 54 0 117
+v 48 0 118
+w 74 0 119
+x 54 0 120
+y 48 1 121
+z 42 0 122
+{ 38 3 123
+--- 30 2 124
+} 38 3 125
+--- 60 0 126
+--- 32 1 161
+ct 60 2 162
+ps 60 2 163
+fr 8 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 48 2 170
+--- 40 0 171
+--- 26 0 172
+--- 26 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 48 1 185
+'' 48 2 186
+--- 40 0 187
+--- 100 0 188
+--- 104 2 189
+--- 54 1 191
+ga 38 2 193
+\` "
+aa 40 2 194
+\' "
+^a 50 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 52 2 198
+.a 26 2 199
+:a 42 2 200
+oa 32 2 202
+,a 34 1 203
+"a 52 2 205
+Ca 34 1 206
+va 52 2 207
+em 100 0 208
+--- 90 2 225
+--- 36 2 227
+--- 52 2 232
+--- 66 2 233
+--- 96 2 234
+--- 36 2 235
+--- 82 0 241
+--- 26 0 245
+--- 26 2 248
+--- 54 0 249
+--- 86 0 250
+--- 66 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F5 b/sys/src/cmd/postscript/devpost.add/F5
new file mode 100755
index 000000000..d4d45d43a
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F5
@@ -0,0 +1,161 @@
+name F5
+fontname FranklinGothic-Heavy
+ligatures fi fl 0
+spacewidth 30
+charset
+! 32 2 33
+" 52 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 74 2 37
+& 78 2 38
+' 30 2 39
+( 38 3 40
+) 38 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 26 0 45
+- "
+. 30 0 46
+/ 60 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 60 2 63
+@ 80 2 64
+A 66 2 65
+B 66 2 66
+C 66 2 67
+D 68 2 68
+E 60 2 69
+F 54 2 70
+G 68 2 71
+H 68 2 72
+I 32 2 73
+J 44 2 74
+K 66 2 75
+L 54 2 76
+M 86 2 77
+N 70 2 78
+O 70 2 79
+P 66 2 80
+Q 70 3 81
+R 68 2 82
+S 66 2 83
+T 50 2 84
+U 66 2 85
+V 66 2 86
+W 92 2 87
+X 70 2 88
+Y 66 2 89
+Z 60 2 90
+[ 38 3 91
+\ 60 2 92
+bs "
+] 38 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 58 0 97
+b 58 2 98
+c 54 0 99
+d 58 2 100
+e 60 0 101
+f 40 2 102
+g 58 3 103
+h 58 2 104
+i 30 2 105
+j 30 3 106
+k 58 2 107
+l 30 2 108
+m 86 0 109
+n 58 0 110
+o 60 0 111
+p 58 1 112
+q 58 1 113
+r 38 0 114
+s 54 0 115
+t 40 2 116
+u 60 0 117
+v 54 0 118
+w 78 0 119
+x 58 0 120
+y 54 1 121
+z 50 0 122
+{ 38 3 123
+--- 30 2 124
+} 38 3 125
+--- 60 0 126
+--- 32 1 161
+ct 60 2 162
+ps 60 2 163
+fr 12 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 52 2 170
+--- 42 0 171
+--- 26 0 172
+--- 26 0 173
+fi 62 2 174
+fl 60 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 52 1 185
+'' 52 2 186
+--- 42 0 187
+--- 100 0 188
+--- 108 2 189
+--- 60 1 191
+ga 40 2 193
+\` "
+aa 40 2 194
+\' "
+^a 54 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 52 2 198
+.a 28 2 199
+:a 44 2 200
+oa 34 2 202
+,a 34 1 203
+"a 60 2 205
+Ca 34 1 206
+va 54 2 207
+em 100 0 208
+--- 96 2 225
+--- 38 2 227
+--- 56 2 232
+--- 70 2 233
+--- 98 2 234
+--- 38 2 235
+--- 82 0 241
+--- 30 0 245
+--- 30 2 248
+--- 60 0 249
+--- 86 0 250
+--- 66 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/F6 b/sys/src/cmd/postscript/devpost.add/F6
new file mode 100755
index 000000000..f7edf3efa
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/F6
@@ -0,0 +1,161 @@
+name F6
+fontname FranklinGothic-HeavyOblique
+ligatures fi fl 0
+spacewidth 30
+charset
+! 32 2 33
+" 52 2 34
+dq "
+# 60 2 35
+$ 60 2 36
+% 74 2 37
+& 78 2 38
+' 30 2 39
+( 38 3 40
+) 38 3 41
+* 60 2 42
++ 60 0 43
+, 30 1 44
+hy 26 0 45
+- "
+. 30 0 46
+/ 60 3 47
+0 60 2 48
+1 60 2 49
+2 60 2 50
+3 60 2 51
+4 60 2 52
+5 60 2 53
+6 60 2 54
+7 60 2 55
+8 60 2 56
+9 60 2 57
+: 30 0 58
+; 30 1 59
+--- 60 0 60
+= 60 0 61
+--- 60 0 62
+? 60 2 63
+@ 80 2 64
+A 66 2 65
+B 66 2 66
+C 66 2 67
+D 68 2 68
+E 60 2 69
+F 54 2 70
+G 68 2 71
+H 68 2 72
+I 32 2 73
+J 44 2 74
+K 66 2 75
+L 54 2 76
+M 86 2 77
+N 70 2 78
+O 70 2 79
+P 66 2 80
+Q 70 3 81
+R 68 2 82
+S 66 2 83
+T 50 2 84
+U 66 2 85
+V 66 2 86
+W 92 2 87
+X 70 2 88
+Y 66 2 89
+Z 60 2 90
+[ 38 3 91
+\ 60 2 92
+bs "
+] 38 3 93
+--- 60 2 94
+--- 50 1 95
+` 30 2 96
+a 58 0 97
+b 58 2 98
+c 54 0 99
+d 58 2 100
+e 60 0 101
+f 40 2 102
+g 58 3 103
+h 58 2 104
+i 30 2 105
+j 30 3 106
+k 58 2 107
+l 30 2 108
+m 86 0 109
+n 58 0 110
+o 60 0 111
+p 58 1 112
+q 58 1 113
+r 38 0 114
+s 54 0 115
+t 40 2 116
+u 60 0 117
+v 54 0 118
+w 78 0 119
+x 58 0 120
+y 54 1 121
+z 50 0 122
+{ 38 3 123
+or 30 2 124
+} 38 3 125
+--- 60 0 126
+--- 32 1 161
+ct 60 2 162
+ps 60 2 163
+fr 12 2 164
+yn 60 2 165
+fn 60 3 166
+sc 60 3 167
+cr 60 0 168
+--- 30 2 169
+`` 52 2 170
+--- 42 0 171
+--- 26 0 172
+--- 26 0 173
+fi 62 2 174
+fl 60 2 175
+en 50 0 177
+\- "
+dg 60 3 178
+dd 60 3 179
+--- 30 0 180
+pg 54 3 182
+--- 60 0 183
+--- 30 1 184
+--- 52 1 185
+'' 52 2 186
+--- 42 0 187
+--- 100 0 188
+--- 108 2 189
+--- 60 1 191
+ga 40 2 193
+\` "
+aa 40 2 194
+\' "
+^a 54 2 195
+^ "
+~a 50 2 196
+~ "
+-a 46 2 197
+Ua 52 2 198
+.a 28 2 199
+:a 44 2 200
+oa 34 2 202
+,a 34 1 203
+"a 60 2 205
+Ca 34 1 206
+va 54 2 207
+em 100 0 208
+--- 96 2 225
+--- 38 2 227
+--- 56 2 232
+--- 70 2 233
+--- 98 2 234
+--- 38 2 235
+--- 82 0 241
+--- 30 0 245
+--- 30 2 248
+--- 60 0 249
+--- 86 0 250
+--- 66 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/G1 b/sys/src/cmd/postscript/devpost.add/G1
new file mode 100755
index 000000000..6ad946b74
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/G1
@@ -0,0 +1,161 @@
+name G1
+fontname Garamond-Light
+ligatures fi fl 0
+spacewidth 32
+charset
+! 22 2 33
+" 34 2 34
+dq "
+# 60 2 35
+$ 50 2 36
+% 76 2 37
+& 70 2 38
+' 22 2 39
+( 36 3 40
+) 36 3 41
+* 34 2 42
++ 56 0 43
+, 26 1 44
+hy 32 0 45
+- "
+. 26 0 46
+/ 40 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 26 0 58
+; 26 1 59
+--- 56 0 60
+= 56 0 61
+--- 56 0 62
+? 30 2 63
+@ 74 3 64
+A 64 2 65
+B 64 2 66
+C 62 2 67
+D 74 2 68
+E 56 2 69
+F 52 2 70
+G 74 2 71
+H 74 2 72
+I 32 2 73
+J 32 2 74
+K 66 2 75
+L 48 2 76
+M 80 2 77
+N 70 2 78
+O 78 2 79
+P 56 2 80
+Q 78 3 81
+R 58 2 82
+S 48 2 83
+T 58 2 84
+U 70 2 85
+V 64 2 86
+W 92 2 87
+X 64 2 88
+Y 66 2 89
+Z 60 2 90
+[ 24 3 91
+\ 52 2 92
+bs "
+] 24 3 93
+--- 56 2 94
+--- 50 1 95
+` 22 2 96
+a 48 0 97
+b 56 2 98
+c 46 0 99
+d 56 2 100
+e 50 0 101
+f 30 2 102
+g 52 1 103
+h 56 2 104
+i 26 2 105
+j 22 3 106
+k 54 2 107
+l 26 2 108
+m 82 0 109
+n 56 0 110
+o 56 0 111
+p 58 1 112
+q 56 1 113
+r 34 0 114
+s 40 0 115
+t 28 2 116
+u 56 0 117
+v 50 0 118
+w 78 0 119
+x 52 0 120
+y 50 1 121
+z 46 0 122
+{ 26 3 123
+--- 56 3 124
+} 26 3 125
+--- 56 0 126
+--- 22 1 161
+ct 50 2 162
+ps 64 2 163
+fr 16 2 164
+yn 66 2 165
+fn 50 3 166
+sc 36 3 167
+cr 50 2 168
+--- 20 2 169
+`` 38 2 170
+--- 26 0 171
+--- 16 0 172
+--- 16 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 40 3 178
+dd 40 3 179
+--- 26 0 180
+pg 62 3 182
+--- 62 0 183
+--- 22 1 184
+--- 36 1 185
+'' 38 2 186
+--- 26 0 187
+--- 100 0 188
+--- 108 2 189
+--- 30 1 191
+ga 36 2 193
+\` "
+aa 36 2 194
+\' "
+^a 42 2 195
+^ "
+~a 42 2 196
+~ "
+-a 38 2 197
+Ua 40 2 198
+.a 22 2 199
+:a 40 2 200
+oa 28 2 202
+,a 32 1 203
+"a 38 2 205
+Ca 30 1 206
+va 42 2 207
+em 100 0 208
+--- 94 2 225
+--- 34 2 227
+--- 48 2 232
+--- 78 2 233
+--- 100 2 234
+--- 34 2 235
+--- 74 0 241
+--- 26 0 245
+--- 26 2 248
+--- 56 0 249
+--- 88 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/G2 b/sys/src/cmd/postscript/devpost.add/G2
new file mode 100755
index 000000000..0f9b00008
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/G2
@@ -0,0 +1,161 @@
+name G2
+fontname Garamond-LightItalic
+ligatures fi fl 0
+spacewidth 27
+charset
+! 26 2 33
+" 35 2 34
+dq "
+# 54 2 35
+$ 55 2 36
+% 78 2 37
+& 64 2 38
+' 25 2 39
+( 38 3 40
+) 38 3 41
+* 32 2 42
++ 57 0 43
+, 27 1 44
+hy 33 0 45
+- "
+. 27 0 46
+/ 30 2 47
+0 55 2 48
+1 55 2 49
+2 55 2 50
+3 55 2 51
+4 55 2 52
+5 55 2 53
+6 55 2 54
+7 55 2 55
+8 55 2 56
+9 55 2 57
+: 27 0 58
+; 27 1 59
+--- 57 0 60
+= 57 0 61
+--- 57 0 62
+? 35 2 63
+@ 69 3 64
+A 64 2 65
+B 63 2 66
+C 63 2 67
+D 72 2 68
+E 55 2 69
+F 52 2 70
+G 71 2 71
+H 73 2 72
+I 31 2 73
+J 30 2 74
+K 63 2 75
+L 46 2 76
+M 80 2 77
+N 66 2 78
+O 72 2 79
+P 58 2 80
+Q 73 3 81
+R 58 2 82
+S 46 2 83
+T 54 2 84
+U 63 2 85
+V 62 2 86
+W 90 2 87
+X 65 2 88
+Y 58 2 89
+Z 59 2 90
+[ 23 3 91
+\ 51 2 92
+bs "
+] 23 3 93
+--- 57 2 94
+--- 50 1 95
+` 25 2 96
+a 57 0 97
+b 52 2 98
+c 46 0 99
+d 57 2 100
+e 44 0 101
+f 30 2 102
+g 48 1 103
+h 56 2 104
+i 30 2 105
+j 27 3 106
+k 49 2 107
+l 26 2 108
+m 84 0 109
+n 62 0 110
+o 50 0 111
+p 51 1 112
+q 52 1 113
+r 39 0 114
+s 36 0 115
+t 28 2 116
+u 61 0 117
+v 46 0 118
+w 73 0 119
+x 53 0 120
+y 47 1 121
+z 51 0 122
+{ 26 3 123
+--- 57 3 124
+} 26 3 125
+--- 57 0 126
+--- 26 1 161
+ct 55 2 162
+ps 65 2 163
+fr 12 2 164
+yn 55 2 165
+fn 55 3 166
+sc 36 3 167
+cr 56 2 168
+--- 19 2 169
+`` 41 2 170
+--- 27 0 171
+--- 16 0 172
+--- 16 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 42 3 178
+dd 42 3 179
+--- 27 0 180
+pg 61 3 182
+--- 62 0 183
+--- 25 1 184
+--- 41 1 185
+'' 41 2 186
+--- 27 0 187
+--- 100 0 188
+--- 113 2 189
+--- 35 1 191
+ga 39 2 193
+\` "
+aa 34 2 194
+\' "
+^a 43 2 195
+^ "
+~a 46 2 196
+~ "
+-a 38 2 197
+Ua 43 2 198
+.a 21 2 199
+:a 36 2 200
+oa 29 2 202
+,a 33 1 203
+"a 32 2 205
+Ca 29 1 206
+va 43 2 207
+em 100 0 208
+--- 80 2 225
+--- 37 2 227
+--- 47 2 232
+--- 72 2 233
+--- 92 2 234
+--- 37 2 235
+--- 78 0 241
+--- 30 0 245
+--- 29 2 248
+--- 50 0 249
+--- 81 0 250
+--- 59 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/G3 b/sys/src/cmd/postscript/devpost.add/G3
new file mode 100755
index 000000000..5f6ddc970
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/G3
@@ -0,0 +1,161 @@
+name G3
+fontname Garamond-Bold
+ligatures fi fl 0
+spacewidth 28
+charset
+! 28 2 33
+" 40 2 34
+dq "
+# 60 2 35
+$ 56 2 36
+% 88 2 37
+& 76 2 38
+' 26 2 39
+( 40 3 40
+) 40 3 41
+* 34 2 42
++ 56 0 43
+, 28 1 44
+hy 30 0 45
+- "
+. 28 0 46
+/ 44 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 56 0 60
+= 56 0 61
+--- 56 0 62
+? 42 2 63
+@ 72 3 64
+A 66 2 65
+B 64 2 66
+C 66 2 67
+D 76 2 68
+E 60 2 69
+F 56 2 70
+G 72 2 71
+H 78 2 72
+I 36 2 73
+J 40 2 74
+K 74 2 75
+L 54 2 76
+M 86 2 77
+N 72 2 78
+O 76 2 79
+P 60 2 80
+Q 76 3 81
+R 66 2 82
+S 52 2 83
+T 60 2 84
+U 70 2 85
+V 64 2 86
+W 94 2 87
+X 70 2 88
+Y 68 2 89
+Z 62 2 90
+[ 28 3 91
+\ 54 2 92
+bs "
+] 28 3 93
+--- 56 2 94
+--- 50 1 95
+` 26 2 96
+a 52 0 97
+b 60 2 98
+c 50 0 99
+d 60 2 100
+e 52 0 101
+f 36 2 102
+g 54 1 103
+h 66 2 104
+i 32 2 105
+j 30 3 106
+k 60 2 107
+l 32 2 108
+m 94 0 109
+n 66 0 110
+o 60 0 111
+p 64 1 112
+q 60 1 113
+r 46 0 114
+s 46 0 115
+t 34 2 116
+u 60 0 117
+v 54 0 118
+w 82 0 119
+x 62 0 120
+y 56 1 121
+z 48 0 122
+{ 28 3 123
+--- 56 3 124
+} 28 3 125
+--- 56 0 126
+--- 28 1 161
+ct 56 2 162
+ps 68 2 163
+fr 56 2 164
+yn 72 2 165
+fn 66 3 166
+sc 40 3 167
+cr 56 2 168
+--- 22 2 169
+`` 44 2 170
+--- 34 0 171
+--- 20 0 172
+--- 20 0 173
+fi 70 2 174
+fl 70 2 175
+en 50 0 177
+\- "
+dg 54 3 178
+dd 54 3 179
+--- 28 0 180
+pg 66 3 182
+--- 62 0 183
+--- 26 1 184
+--- 44 1 185
+'' 44 2 186
+--- 34 0 187
+--- 100 0 188
+--- 126 2 189
+--- 42 1 191
+ga 40 2 193
+\` "
+aa 40 2 194
+\' "
+^a 50 2 195
+^ "
+~a 52 2 196
+~ "
+-a 52 2 197
+Ua 48 2 198
+.a 30 2 199
+:a 44 2 200
+oa 32 2 202
+,a 32 1 203
+"a 42 2 205
+Ca 34 1 206
+va 50 2 207
+em 100 0 208
+--- 96 2 225
+--- 36 2 227
+--- 54 2 232
+--- 76 2 233
+--- 100 2 234
+--- 36 2 235
+--- 80 0 241
+--- 32 0 245
+--- 34 2 248
+--- 60 0 249
+--- 92 0 250
+--- 64 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/G4 b/sys/src/cmd/postscript/devpost.add/G4
new file mode 100755
index 000000000..b35b361fd
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/G4
@@ -0,0 +1,161 @@
+name G4
+fontname Garamond-BoldItalic
+ligatures fi fl 0
+spacewidth 28
+charset
+! 24 2 33
+" 40 2 34
+dq "
+# 56 2 35
+$ 56 2 36
+% 76 2 37
+& 74 2 38
+' 22 2 39
+( 48 3 40
+) 48 3 41
+* 54 2 42
++ 56 0 43
+, 24 1 44
+hy 30 0 45
+- "
+. 24 0 46
+/ 44 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 24 0 58
+; 24 1 59
+--- 56 0 60
+= 56 0 61
+--- 56 0 62
+? 40 2 63
+@ 76 3 64
+A 64 2 65
+B 64 2 66
+C 64 2 67
+D 76 2 68
+E 64 2 69
+F 60 2 70
+G 72 2 71
+H 84 2 72
+I 42 2 73
+J 40 2 74
+K 76 2 75
+L 56 2 76
+M 88 2 77
+N 74 2 78
+O 74 2 79
+P 68 2 80
+Q 74 3 81
+R 72 2 82
+S 52 2 83
+T 58 2 84
+U 72 2 85
+V 64 2 86
+W 90 2 87
+X 72 2 88
+Y 62 2 89
+Z 62 2 90
+[ 34 3 91
+\ 54 2 92
+bs "
+] 34 3 93
+--- 56 2 94
+--- 50 1 95
+` 22 2 96
+a 62 0 97
+b 56 2 98
+c 50 0 99
+d 62 2 100
+e 52 0 101
+f 38 2 102
+g 56 1 103
+h 64 2 104
+i 34 2 105
+j 32 3 106
+k 60 2 107
+l 28 2 108
+m 86 0 109
+n 58 0 110
+o 60 0 111
+p 64 1 112
+q 60 1 113
+r 50 0 114
+s 50 0 115
+t 34 2 116
+u 64 0 117
+v 52 0 118
+w 82 0 119
+x 66 0 120
+y 54 1 121
+z 56 0 122
+{ 34 3 123
+--- 56 3 124
+} 34 3 125
+--- 56 0 126
+--- 24 1 161
+ct 56 2 162
+ps 56 2 163
+fr 18 2 164
+yn 56 2 165
+fn 56 3 166
+sc 54 3 167
+cr 56 2 168
+--- 22 2 169
+`` 42 2 170
+--- 42 0 171
+--- 24 0 172
+--- 24 0 173
+fi 70 2 174
+fl 66 2 175
+en 50 0 177
+\- "
+dg 54 3 178
+dd 54 3 179
+--- 24 0 180
+pg 70 3 182
+--- 62 0 183
+--- 22 1 184
+--- 42 1 185
+'' 42 2 186
+--- 42 0 187
+--- 100 0 188
+--- 114 2 189
+--- 40 1 191
+ga 42 2 193
+\` "
+aa 38 2 194
+\' "
+^a 50 2 195
+^ "
+~a 56 2 196
+~ "
+-a 52 2 197
+Ua 50 2 198
+.a 26 2 199
+:a 48 2 200
+oa 32 2 202
+,a 38 1 203
+"a 38 2 205
+Ca 34 1 206
+va 50 2 207
+em 100 0 208
+--- 100 2 225
+--- 40 2 227
+--- 56 2 232
+--- 76 2 233
+--- 104 2 234
+--- 38 2 235
+--- 88 0 241
+--- 34 0 245
+--- 28 2 248
+--- 60 0 249
+--- 90 0 250
+--- 64 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/Gb b/sys/src/cmd/postscript/devpost.add/Gb
new file mode 100755
index 000000000..c95b5c1a8
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/Gb
@@ -0,0 +1,161 @@
+name Gb
+fontname Goudy-Bold
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 50 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 89 2 37
+& 89 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 50 2 42
++ 61 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 33 0 58
+; 33 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 39 2 63
+@ 75 3 64
+A 78 2 65
+B 67 2 66
+C 72 2 67
+D 78 2 68
+E 61 2 69
+F 56 2 70
+G 78 2 71
+H 83 2 72
+I 39 2 73
+J 39 2 74
+K 72 2 75
+L 61 2 76
+M 89 2 77
+N 83 2 78
+O 78 2 79
+P 61 2 80
+Q 78 3 81
+R 72 2 82
+S 56 2 83
+T 72 2 84
+U 83 2 85
+V 78 2 86
+W 100 2 87
+X 72 2 88
+Y 67 2 89
+Z 61 2 90
+[ 33 3 91
+\ 61 2 92
+bs "
+] 33 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 44 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 44 0 101
+f 33 2 102
+g 44 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 56 2 107
+l 28 2 108
+m 78 0 109
+n 56 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 39 0 114
+s 39 0 115
+t 33 2 116
+u 56 0 117
+v 50 0 118
+w 72 0 119
+x 50 0 120
+y 50 1 121
+z 39 0 122
+{ 33 3 123
+--- 61 3 124
+} 33 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 50 3 167
+cr 56 2 168
+--- 28 2 169
+`` 50 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 28 0 180
+pg 63 3 182
+--- 61 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 28 2 227
+--- 61 2 232
+--- 78 2 233
+--- 100 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 78 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/Gi b/sys/src/cmd/postscript/devpost.add/Gi
new file mode 100755
index 000000000..ae88b7344
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/Gi
@@ -0,0 +1,161 @@
+name Gi
+fontname Goudy-Italic
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 44 2 34
+dq "
+# 61 2 35
+$ 50 2 36
+% 83 2 37
+& 83 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 50 2 42
++ 61 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 33 0 58
+; 33 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 39 2 63
+@ 75 3 64
+A 72 2 65
+B 61 2 66
+C 72 2 67
+D 72 2 68
+E 61 2 69
+F 56 2 70
+G 78 2 71
+H 78 2 72
+I 33 2 73
+J 33 2 74
+K 67 2 75
+L 56 2 76
+M 89 2 77
+N 78 2 78
+O 78 2 79
+P 56 2 80
+Q 78 3 81
+R 61 2 82
+S 50 2 83
+T 67 2 84
+U 78 2 85
+V 67 2 86
+W 94 2 87
+X 67 2 88
+Y 56 2 89
+Z 56 2 90
+[ 28 3 91
+\ 61 2 92
+bs "
+] 28 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 44 0 97
+b 44 2 98
+c 39 0 99
+d 44 2 100
+e 39 0 101
+f 28 2 102
+g 39 1 103
+h 44 2 104
+i 22 2 105
+j 22 3 106
+k 44 2 107
+l 22 2 108
+m 72 0 109
+n 50 0 110
+o 44 0 111
+p 44 1 112
+q 44 1 113
+r 33 0 114
+s 33 0 115
+t 28 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 28 3 123
+--- 61 3 124
+} 28 3 125
+--- 61 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 61 2 168
+--- 28 2 169
+`` 44 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 63 3 182
+--- 61 0 183
+--- 28 1 184
+--- 44 1 185
+'' 44 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 94 2 225
+--- 50 2 227
+--- 56 2 232
+--- 78 2 233
+--- 94 2 234
+--- 50 2 235
+--- 67 0 241
+--- 22 0 245
+--- 22 2 248
+--- 44 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/Gr b/sys/src/cmd/postscript/devpost.add/Gr
new file mode 100755
index 000000000..b19a6a37a
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/Gr
@@ -0,0 +1,161 @@
+name Gr
+fontname Goudy-Regular
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 37 2 34
+dq "
+# 61 2 35
+$ 50 2 36
+% 83 2 37
+& 83 2 38
+' 22 2 39
+( 39 3 40
+) 39 3 41
+* 50 2 42
++ 61 0 43
+, 25 1 44
+hy 33 0 45
+- "
+. 25 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 33 2 63
+@ 75 3 64
+A 78 2 65
+B 61 2 66
+C 72 2 67
+D 72 2 68
+E 56 2 69
+F 50 2 70
+G 78 2 71
+H 78 2 72
+I 33 2 73
+J 33 2 74
+K 67 2 75
+L 56 2 76
+M 89 2 77
+N 78 2 78
+O 78 2 79
+P 56 2 80
+Q 78 3 81
+R 67 2 82
+S 56 2 83
+T 67 2 84
+U 78 2 85
+V 72 2 86
+W 100 2 87
+X 67 2 88
+Y 61 2 89
+Z 56 2 90
+[ 28 3 91
+\ 61 2 92
+bs "
+] 28 3 93
+--- 61 2 94
+--- 50 1 95
+` 22 2 96
+a 44 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 44 0 101
+f 28 2 102
+g 44 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 78 0 109
+n 56 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 33 0 115
+t 33 2 116
+u 50 0 117
+v 50 0 118
+w 67 0 119
+x 50 0 120
+y 44 1 121
+z 33 0 122
+{ 28 3 123
+--- 61 3 124
+} 28 3 125
+--- 61 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 22 2 169
+`` 39 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 25 0 180
+pg 63 3 182
+--- 61 0 183
+--- 22 1 184
+--- 39 1 185
+'' 39 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 33 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 39 2 227
+--- 56 2 232
+--- 78 2 233
+--- 100 2 234
+--- 43 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 78 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/Gx b/sys/src/cmd/postscript/devpost.add/Gx
new file mode 100755
index 000000000..fdfd2de55
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/Gx
@@ -0,0 +1,161 @@
+name Gx
+fontname Goudy-BoldItalic
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 50 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 83 2 37
+& 83 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 50 2 42
++ 61 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 33 0 58
+; 33 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 39 2 63
+@ 75 3 64
+A 72 2 65
+B 67 2 66
+C 67 2 67
+D 78 2 68
+E 67 2 69
+F 61 2 70
+G 72 2 71
+H 78 2 72
+I 39 2 73
+J 44 2 74
+K 72 2 75
+L 61 2 76
+M 94 2 77
+N 78 2 78
+O 78 2 79
+P 67 2 80
+Q 78 3 81
+R 72 2 82
+S 50 2 83
+T 72 2 84
+U 78 2 85
+V 67 2 86
+W 94 2 87
+X 72 2 88
+Y 61 2 89
+Z 61 2 90
+[ 39 3 91
+\ 61 2 92
+bs "
+] 39 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 44 0 101
+f 28 2 102
+g 44 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 78 0 109
+n 56 0 110
+o 44 0 111
+p 50 1 112
+q 50 1 113
+r 39 0 114
+s 39 0 115
+t 33 2 116
+u 56 0 117
+v 44 0 118
+w 72 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 39 3 123
+--- 61 3 124
+} 39 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 50 3 167
+cr 61 2 168
+--- 28 2 169
+`` 50 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 63 3 182
+--- 61 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 89 2 225
+--- 30 2 227
+--- 61 2 232
+--- 78 2 233
+--- 94 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 44 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H1 b/sys/src/cmd/postscript/devpost.add/H1
new file mode 100755
index 000000000..94de6bda7
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H1
@@ -0,0 +1,161 @@
+name H1
+fontname Helvetica-Condensed-Light
+ligatures fi fl 0
+spacewidth 22
+charset
+! 22 2 33
+" 31 2 34
+dq "
+# 44 2 35
+$ 44 2 36
+% 78 2 37
+& 61 2 38
+' 16 2 39
+( 28 3 40
+) 28 3 41
+* 39 2 42
++ 44 0 43
+, 22 1 44
+hy 33 0 45
+- "
+. 22 0 46
+/ 28 2 47
+0 44 2 48
+1 44 2 49
+2 44 2 50
+3 44 2 51
+4 44 2 52
+5 44 2 53
+6 44 2 54
+7 44 2 55
+8 44 2 56
+9 44 2 57
+: 22 0 58
+; 22 1 59
+--- 44 0 60
+= 44 0 61
+--- 44 0 62
+? 39 2 63
+@ 80 3 64
+A 50 2 65
+B 50 2 66
+C 56 2 67
+D 56 2 68
+E 44 2 69
+F 44 2 70
+G 56 2 71
+H 56 2 72
+I 22 2 73
+J 39 2 74
+K 50 2 75
+L 44 2 76
+M 72 2 77
+N 56 2 78
+O 56 2 79
+P 50 2 80
+Q 56 3 81
+R 50 2 82
+S 50 2 83
+T 44 2 84
+U 56 2 85
+V 50 2 86
+W 72 2 87
+X 50 2 88
+Y 50 2 89
+Z 44 2 90
+[ 28 3 91
+\ 22 2 92
+bs "
+] 28 3 93
+--- 44 2 94
+--- 50 1 95
+` 22 2 96
+a 39 0 97
+b 44 2 98
+c 39 0 99
+d 44 2 100
+e 39 0 101
+f 22 2 102
+g 44 1 103
+h 44 2 104
+i 22 2 105
+j 22 3 106
+k 39 2 107
+l 22 2 108
+m 67 0 109
+n 44 0 110
+o 44 0 111
+p 44 1 112
+q 44 1 113
+r 28 0 114
+s 39 0 115
+t 22 2 116
+u 44 0 117
+v 39 0 118
+w 56 0 119
+x 39 0 120
+y 39 1 121
+z 33 0 122
+{ 35 3 123
+--- 22 3 124
+} 35 3 125
+--- 44 0 126
+--- 22 1 161
+ct 44 2 162
+ps 44 2 163
+fr 17 2 164
+yn 44 2 165
+fn 44 3 166
+sc 44 3 167
+cr 44 2 168
+--- 20 2 169
+`` 33 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 44 2 174
+fl 44 2 175
+en 50 0 177
+\- "
+dg 44 3 178
+dd 44 3 179
+--- 22 0 180
+pg 56 3 182
+--- 61 0 183
+--- 22 1 184
+--- 33 1 185
+'' 33 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 72 2 225
+--- 27 2 227
+--- 44 2 232
+--- 56 2 233
+--- 78 2 234
+--- 27 2 235
+--- 61 0 241
+--- 20 0 245
+--- 22 2 248
+--- 44 0 249
+--- 67 0 250
+--- 44 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H2 b/sys/src/cmd/postscript/devpost.add/H2
new file mode 100755
index 000000000..c67ed79bc
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H2
@@ -0,0 +1,161 @@
+name H2
+fontname Helvetica-Condensed-LightOblique
+ligatures fi fl 0
+spacewidth 22
+charset
+! 22 2 33
+" 31 2 34
+dq "
+# 44 2 35
+$ 44 2 36
+% 78 2 37
+& 61 2 38
+' 16 2 39
+( 28 3 40
+) 28 3 41
+* 39 2 42
++ 44 0 43
+, 22 1 44
+hy 33 0 45
+- "
+. 22 0 46
+/ 28 2 47
+0 44 2 48
+1 44 2 49
+2 44 2 50
+3 44 2 51
+4 44 2 52
+5 44 2 53
+6 44 2 54
+7 44 2 55
+8 44 2 56
+9 44 2 57
+: 22 0 58
+; 22 1 59
+--- 44 0 60
+= 44 0 61
+--- 44 0 62
+? 39 2 63
+@ 80 3 64
+A 50 2 65
+B 50 2 66
+C 56 2 67
+D 56 2 68
+E 44 2 69
+F 44 2 70
+G 56 2 71
+H 56 2 72
+I 22 2 73
+J 39 2 74
+K 50 2 75
+L 44 2 76
+M 72 2 77
+N 56 2 78
+O 56 2 79
+P 50 2 80
+Q 56 3 81
+R 50 2 82
+S 50 2 83
+T 44 2 84
+U 56 2 85
+V 50 2 86
+W 72 2 87
+X 50 2 88
+Y 50 2 89
+Z 44 2 90
+[ 28 3 91
+\ 22 2 92
+bs "
+] 28 3 93
+--- 44 2 94
+--- 50 1 95
+` 22 2 96
+a 39 0 97
+b 44 2 98
+c 39 0 99
+d 44 2 100
+e 39 0 101
+f 22 2 102
+g 44 1 103
+h 44 2 104
+i 22 2 105
+j 22 3 106
+k 39 2 107
+l 22 2 108
+m 67 0 109
+n 44 0 110
+o 44 0 111
+p 44 1 112
+q 44 1 113
+r 28 0 114
+s 39 0 115
+t 22 2 116
+u 44 0 117
+v 39 0 118
+w 56 0 119
+x 39 0 120
+y 39 1 121
+z 33 0 122
+{ 35 3 123
+--- 22 3 124
+} 35 3 125
+--- 44 0 126
+--- 22 1 161
+ct 44 2 162
+ps 44 2 163
+fr 17 2 164
+yn 44 2 165
+fn 44 3 166
+sc 44 3 167
+cr 44 2 168
+--- 20 2 169
+`` 33 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 44 2 174
+fl 44 2 175
+en 50 0 177
+\- "
+dg 44 3 178
+dd 44 3 179
+--- 22 0 180
+pg 56 3 182
+--- 61 0 183
+--- 22 1 184
+--- 33 1 185
+'' 33 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 72 2 225
+--- 27 2 227
+--- 44 2 232
+--- 56 2 233
+--- 78 2 234
+--- 27 2 235
+--- 61 0 241
+--- 20 0 245
+--- 22 2 248
+--- 44 0 249
+--- 67 0 250
+--- 44 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H3 b/sys/src/cmd/postscript/devpost.add/H3
new file mode 100755
index 000000000..bbda3bf38
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H3
@@ -0,0 +1,161 @@
+name H3
+fontname Helvetica-Condensed
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 25 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 22 2 39
+( 33 3 40
+) 33 3 41
+* 50 2 42
++ 50 0 43
+, 25 1 44
+hy 33 0 45
+- "
+. 25 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 80 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 61 2 68
+E 50 2 69
+F 44 2 70
+G 61 2 71
+H 61 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 50 2 76
+M 78 2 77
+N 61 2 78
+O 61 2 79
+P 56 2 80
+Q 61 3 81
+R 61 2 82
+S 56 2 83
+T 50 2 84
+U 61 2 85
+V 56 2 86
+W 83 2 87
+X 56 2 88
+Y 56 2 89
+Z 50 2 90
+[ 33 3 91
+\ 25 2 92
+bs "
+] 33 3 93
+--- 50 2 94
+--- 50 1 95
+` 22 2 96
+a 44 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 44 0 101
+f 28 2 102
+g 50 1 103
+h 50 2 104
+i 22 2 105
+j 22 3 106
+k 44 2 107
+l 22 2 108
+m 78 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 28 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 48 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 39 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 28 0 180
+pg 44 3 182
+--- 33 0 183
+--- 22 1 184
+--- 39 1 185
+'' 39 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 83 2 225
+--- 30 2 227
+--- 50 2 232
+--- 61 2 233
+--- 83 2 234
+--- 30 2 235
+--- 67 0 241
+--- 22 0 245
+--- 22 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H4 b/sys/src/cmd/postscript/devpost.add/H4
new file mode 100755
index 000000000..7a9b2a6c0
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H4
@@ -0,0 +1,161 @@
+name H4
+fontname Helvetica-Condensed-Oblique
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 25 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 22 2 39
+( 33 3 40
+) 33 3 41
+* 50 2 42
++ 50 0 43
+, 25 1 44
+hy 33 0 45
+- "
+. 25 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 80 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 61 2 68
+E 50 2 69
+F 44 2 70
+G 61 2 71
+H 61 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 50 2 76
+M 78 2 77
+N 61 2 78
+O 61 2 79
+P 56 2 80
+Q 61 3 81
+R 61 2 82
+S 56 2 83
+T 50 2 84
+U 61 2 85
+V 56 2 86
+W 83 2 87
+X 56 2 88
+Y 56 2 89
+Z 50 2 90
+[ 33 3 91
+\ 25 2 92
+bs "
+] 33 3 93
+--- 50 2 94
+--- 50 1 95
+` 22 2 96
+a 44 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 44 0 101
+f 28 2 102
+g 50 1 103
+h 50 2 104
+i 22 2 105
+j 22 3 106
+k 44 2 107
+l 22 2 108
+m 78 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 28 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 48 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 39 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 28 0 180
+pg 44 3 182
+--- 33 0 183
+--- 22 1 184
+--- 39 1 185
+'' 39 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 83 2 225
+--- 30 2 227
+--- 50 2 232
+--- 61 2 233
+--- 83 2 234
+--- 30 2 235
+--- 67 0 241
+--- 22 0 245
+--- 22 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H5 b/sys/src/cmd/postscript/devpost.add/H5
new file mode 100755
index 000000000..48a564532
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H5
@@ -0,0 +1,161 @@
+name H5
+fontname Helvetica-Condensed-Bold
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 28 2 39
+( 33 3 40
+) 33 3 41
+* 50 2 42
++ 50 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 28 0 58
+; 28 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 83 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 61 2 68
+E 50 2 69
+F 50 2 70
+G 61 2 71
+H 61 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 50 2 76
+M 78 2 77
+N 61 2 78
+O 61 2 79
+P 56 2 80
+Q 61 3 81
+R 61 2 82
+S 56 2 83
+T 50 2 84
+U 61 2 85
+V 56 2 86
+W 83 2 87
+X 56 2 88
+Y 56 2 89
+Z 50 2 90
+[ 33 3 91
+\ 25 2 92
+bs "
+] 33 3 93
+--- 50 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 50 0 101
+f 28 2 102
+g 50 1 103
+h 50 2 104
+i 28 2 105
+j 28 3 106
+k 44 2 107
+l 28 2 108
+m 78 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 28 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 50 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 50 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 55 3 182
+--- 42 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 78 2 225
+--- 30 2 227
+--- 50 2 232
+--- 61 2 233
+--- 83 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H6 b/sys/src/cmd/postscript/devpost.add/H6
new file mode 100755
index 000000000..e02a0c14d
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H6
@@ -0,0 +1,161 @@
+name H6
+fontname Helvetica-Condensed-BoldOblique
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 28 2 39
+( 33 3 40
+) 33 3 41
+* 50 2 42
++ 50 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 28 0 58
+; 28 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 83 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 61 2 68
+E 50 2 69
+F 50 2 70
+G 61 2 71
+H 61 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 50 2 76
+M 78 2 77
+N 61 2 78
+O 61 2 79
+P 56 2 80
+Q 61 3 81
+R 61 2 82
+S 56 2 83
+T 50 2 84
+U 61 2 85
+V 56 2 86
+W 83 2 87
+X 56 2 88
+Y 56 2 89
+Z 50 2 90
+[ 33 3 91
+\ 25 2 92
+bs "
+] 33 3 93
+--- 50 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 50 2 98
+c 44 0 99
+d 50 2 100
+e 50 0 101
+f 28 2 102
+g 50 1 103
+h 50 2 104
+i 28 2 105
+j 28 3 106
+k 44 2 107
+l 28 2 108
+m 78 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 28 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 50 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 50 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 55 3 182
+--- 42 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 78 2 225
+--- 30 2 227
+--- 50 2 232
+--- 61 2 233
+--- 83 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H7 b/sys/src/cmd/postscript/devpost.add/H7
new file mode 100755
index 000000000..8fb6c4809
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H7
@@ -0,0 +1,161 @@
+name H7
+fontname Helvetica-Condensed-Black
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 28 2 39
+( 28 3 40
+) 28 3 41
+* 50 2 42
++ 50 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 28 0 58
+; 28 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 83 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 56 2 68
+E 50 2 69
+F 50 2 70
+G 56 2 71
+H 56 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 44 2 76
+M 78 2 77
+N 56 2 78
+O 56 2 79
+P 56 2 80
+Q 56 3 81
+R 56 2 82
+S 50 2 83
+T 50 2 84
+U 56 2 85
+V 56 2 86
+W 78 2 87
+X 56 2 88
+Y 56 2 89
+Z 44 2 90
+[ 28 3 91
+\ 25 2 92
+bs "
+] 28 3 93
+--- 50 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 50 2 98
+c 50 0 99
+d 50 2 100
+e 50 0 101
+f 33 2 102
+g 50 1 103
+h 50 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 72 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 33 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 50 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 50 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 55 3 182
+--- 42 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 78 2 225
+--- 30 2 227
+--- 44 2 232
+--- 56 2 233
+--- 78 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/H8 b/sys/src/cmd/postscript/devpost.add/H8
new file mode 100755
index 000000000..164aa9ad0
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/H8
@@ -0,0 +1,161 @@
+name H8
+fontname Helvetica-Condensed-BlackOblique
+ligatures fi fl 0
+spacewidth 25
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 50 2 35
+$ 50 2 36
+% 83 2 37
+& 67 2 38
+' 28 2 39
+( 28 3 40
+) 28 3 41
+* 50 2 42
++ 50 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 28 0 58
+; 28 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 50 2 63
+@ 83 3 64
+A 56 2 65
+B 56 2 66
+C 56 2 67
+D 56 2 68
+E 50 2 69
+F 50 2 70
+G 56 2 71
+H 56 2 72
+I 28 2 73
+J 44 2 74
+K 56 2 75
+L 44 2 76
+M 78 2 77
+N 56 2 78
+O 56 2 79
+P 56 2 80
+Q 56 3 81
+R 56 2 82
+S 50 2 83
+T 50 2 84
+U 56 2 85
+V 56 2 86
+W 78 2 87
+X 56 2 88
+Y 56 2 89
+Z 44 2 90
+[ 28 3 91
+\ 25 2 92
+bs "
+] 28 3 93
+--- 50 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 50 2 98
+c 50 0 99
+d 50 2 100
+e 50 0 101
+f 33 2 102
+g 50 1 103
+h 50 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 72 0 109
+n 50 0 110
+o 50 0 111
+p 50 1 112
+q 50 1 113
+r 33 0 114
+s 44 0 115
+t 33 2 116
+u 50 0 117
+v 44 0 118
+w 67 0 119
+x 44 0 120
+y 44 1 121
+z 39 0 122
+{ 27 3 123
+--- 25 3 124
+} 27 3 125
+--- 50 0 126
+--- 33 1 161
+ct 50 2 162
+ps 50 2 163
+fr 17 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 25 2 169
+`` 50 2 170
+--- 50 0 171
+--- 28 0 172
+--- 28 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 33 0 180
+pg 55 3 182
+--- 42 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 111 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 78 2 225
+--- 30 2 227
+--- 44 2 232
+--- 56 2 233
+--- 78 2 234
+--- 30 2 235
+--- 72 0 241
+--- 28 0 245
+--- 28 2 248
+--- 50 0 249
+--- 72 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/HC b/sys/src/cmd/postscript/devpost.add/HC
new file mode 100755
index 000000000..70ceee144
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/HC
@@ -0,0 +1,161 @@
+name HC
+fontname Helvetica-Black
+ligatures fi fl 0
+spacewidth 33
+charset
+! 33 2 33
+" 50 2 34
+dq "
+# 66 2 35
+$ 67 2 36
+% 100 2 37
+& 89 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 56 2 42
++ 66 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 67 2 48
+1 67 2 49
+2 67 2 50
+3 67 2 51
+4 67 2 52
+5 67 2 53
+6 67 2 54
+7 67 2 55
+8 67 2 56
+9 67 2 57
+: 33 0 58
+; 33 1 59
+--- 66 0 60
+= 66 0 61
+--- 66 0 62
+? 61 2 63
+@ 74 3 64
+A 78 2 65
+B 78 2 66
+C 78 2 67
+D 78 2 68
+E 72 2 69
+F 67 2 70
+G 83 2 71
+H 83 2 72
+I 39 2 73
+J 67 2 74
+K 83 2 75
+L 67 2 76
+M 94 2 77
+N 83 2 78
+O 83 2 79
+P 72 2 80
+Q 83 3 81
+R 78 2 82
+S 72 2 83
+T 72 2 84
+U 83 2 85
+V 78 2 86
+W 100 2 87
+X 78 2 88
+Y 78 2 89
+Z 72 2 90
+[ 39 3 91
+\ 28 2 92
+bs "
+] 39 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 67 0 97
+b 67 2 98
+c 67 0 99
+d 67 2 100
+e 67 0 101
+f 38 2 102
+g 67 1 103
+h 67 2 104
+i 33 2 105
+j 33 3 106
+k 67 2 107
+l 33 2 108
+m 100 0 109
+n 67 0 110
+o 67 0 111
+p 67 1 112
+q 67 1 113
+r 44 0 114
+s 61 0 115
+t 44 2 116
+u 67 0 117
+v 61 0 118
+w 94 0 119
+x 67 0 120
+y 61 1 121
+z 56 0 122
+{ 39 3 123
+--- 28 3 124
+} 39 3 125
+--- 66 0 126
+--- 33 1 161
+ct 67 2 162
+ps 67 2 163
+fr 17 2 164
+yn 67 2 165
+fn 67 3 166
+sc 67 3 167
+cr 66 2 168
+--- 28 2 169
+`` 50 2 170
+--- 67 0 171
+--- 33 0 172
+--- 33 0 173
+fi 67 2 174
+fl 67 2 175
+en 50 0 177
+\- "
+dg 67 3 178
+dd 67 3 179
+--- 33 0 180
+pg 85 3 182
+--- 50 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 67 0 187
+--- 100 0 188
+--- 100 2 189
+--- 61 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 40 2 227
+--- 67 2 232
+--- 83 2 233
+--- 100 2 234
+--- 40 2 235
+--- 100 0 241
+--- 33 0 245
+--- 33 2 248
+--- 67 0 249
+--- 100 0 250
+--- 67 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/HK b/sys/src/cmd/postscript/devpost.add/HK
new file mode 100755
index 000000000..1008dfaa3
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/HK
@@ -0,0 +1,161 @@
+name HK
+fontname Helvetica-LightOblique
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 28 2 34
+dq "
+# 66 2 35
+$ 56 2 36
+% 89 2 37
+& 67 2 38
+' 22 2 39
+( 33 3 40
+) 33 3 41
+* 39 2 42
++ 66 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 66 0 60
+= 66 0 61
+--- 66 0 62
+? 50 2 63
+@ 80 3 64
+A 67 2 65
+B 67 2 66
+C 72 2 67
+D 72 2 68
+E 61 2 69
+F 56 2 70
+G 78 2 71
+H 72 2 72
+I 28 2 73
+J 50 2 74
+K 67 2 75
+L 56 2 76
+M 83 2 77
+N 72 2 78
+O 78 2 79
+P 61 2 80
+Q 78 3 81
+R 67 2 82
+S 61 2 83
+T 56 2 84
+U 72 2 85
+V 61 2 86
+W 89 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 28 2 92
+bs "
+] 33 3 93
+--- 66 2 94
+--- 50 1 95
+` 22 2 96
+a 56 0 97
+b 61 2 98
+c 56 0 99
+d 61 2 100
+e 56 0 101
+f 28 2 102
+g 61 1 103
+h 56 2 104
+i 22 2 105
+j 22 3 106
+k 50 2 107
+l 22 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 61 1 112
+q 61 1 113
+r 33 0 114
+s 50 0 115
+t 28 2 116
+u 56 0 117
+v 50 0 118
+w 72 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 22 3 124
+} 33 3 125
+--- 66 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 56 3 167
+cr 56 2 168
+--- 22 2 169
+`` 39 2 170
+--- 56 0 171
+--- 39 0 172
+--- 39 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 28 0 180
+pg 65 3 182
+--- 50 0 183
+--- 22 1 184
+--- 39 1 185
+'' 39 2 186
+--- 56 0 187
+--- 100 0 188
+--- 100 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 33 2 227
+--- 56 2 232
+--- 78 2 233
+--- 100 2 234
+--- 33 2 235
+--- 89 0 241
+--- 22 0 245
+--- 22 2 248
+--- 56 0 249
+--- 94 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/HL b/sys/src/cmd/postscript/devpost.add/HL
new file mode 100755
index 000000000..349335db3
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/HL
@@ -0,0 +1,161 @@
+name HL
+fontname Helvetica-Light
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 28 2 34
+dq "
+# 66 2 35
+$ 56 2 36
+% 89 2 37
+& 67 2 38
+' 22 2 39
+( 33 3 40
+) 33 3 41
+* 39 2 42
++ 66 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 66 0 60
+= 66 0 61
+--- 66 0 62
+? 50 2 63
+@ 80 3 64
+A 67 2 65
+B 67 2 66
+C 72 2 67
+D 72 2 68
+E 61 2 69
+F 56 2 70
+G 78 2 71
+H 72 2 72
+I 28 2 73
+J 50 2 74
+K 67 2 75
+L 56 2 76
+M 83 2 77
+N 72 2 78
+O 78 2 79
+P 61 2 80
+Q 78 3 81
+R 67 2 82
+S 61 2 83
+T 56 2 84
+U 72 2 85
+V 61 2 86
+W 89 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 28 2 92
+bs "
+] 33 3 93
+--- 66 2 94
+--- 50 1 95
+` 22 2 96
+a 56 0 97
+b 61 2 98
+c 56 0 99
+d 61 2 100
+e 56 0 101
+f 28 2 102
+g 61 1 103
+h 56 2 104
+i 22 2 105
+j 22 3 106
+k 50 2 107
+l 22 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 61 1 112
+q 61 1 113
+r 33 0 114
+s 50 0 115
+t 28 2 116
+u 56 0 117
+v 50 0 118
+w 72 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 22 3 124
+} 33 3 125
+--- 66 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 56 3 167
+cr 56 2 168
+--- 22 2 169
+`` 39 2 170
+--- 56 0 171
+--- 39 0 172
+--- 39 0 173
+fi 50 2 174
+fl 50 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 28 0 180
+pg 65 3 182
+--- 50 0 183
+--- 22 1 184
+--- 39 1 185
+'' 39 2 186
+--- 56 0 187
+--- 100 0 188
+--- 100 2 189
+--- 50 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 33 2 227
+--- 56 2 232
+--- 78 2 233
+--- 100 2 234
+--- 33 2 235
+--- 89 0 241
+--- 22 0 245
+--- 22 2 248
+--- 56 0 249
+--- 94 0 250
+--- 50 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/HY b/sys/src/cmd/postscript/devpost.add/HY
new file mode 100755
index 000000000..7a050d65b
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/HY
@@ -0,0 +1,161 @@
+name HY
+fontname Helvetica-BlackOblique
+ligatures fi fl 0
+spacewidth 33
+charset
+! 33 2 33
+" 50 2 34
+dq "
+# 66 2 35
+$ 67 2 36
+% 100 2 37
+& 89 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 56 2 42
++ 66 0 43
+, 33 1 44
+hy 33 0 45
+- "
+. 33 0 46
+/ 28 2 47
+0 67 2 48
+1 67 2 49
+2 67 2 50
+3 67 2 51
+4 67 2 52
+5 67 2 53
+6 67 2 54
+7 67 2 55
+8 67 2 56
+9 67 2 57
+: 33 0 58
+; 33 1 59
+--- 66 0 60
+= 66 0 61
+--- 66 0 62
+? 61 2 63
+@ 74 3 64
+A 78 2 65
+B 78 2 66
+C 78 2 67
+D 78 2 68
+E 72 2 69
+F 67 2 70
+G 83 2 71
+H 83 2 72
+I 39 2 73
+J 67 2 74
+K 83 2 75
+L 67 2 76
+M 94 2 77
+N 83 2 78
+O 83 2 79
+P 72 2 80
+Q 83 3 81
+R 78 2 82
+S 72 2 83
+T 72 2 84
+U 83 2 85
+V 78 2 86
+W 100 2 87
+X 78 2 88
+Y 78 2 89
+Z 72 2 90
+[ 39 3 91
+\ 28 2 92
+bs "
+] 39 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 67 0 97
+b 67 2 98
+c 67 0 99
+d 67 2 100
+e 67 0 101
+f 38 2 102
+g 67 1 103
+h 67 2 104
+i 33 2 105
+j 33 3 106
+k 67 2 107
+l 33 2 108
+m 100 0 109
+n 67 0 110
+o 67 0 111
+p 67 1 112
+q 67 1 113
+r 44 0 114
+s 61 0 115
+t 44 2 116
+u 67 0 117
+v 61 0 118
+w 94 0 119
+x 67 0 120
+y 61 1 121
+z 56 0 122
+{ 39 3 123
+--- 28 3 124
+} 39 3 125
+--- 66 0 126
+--- 33 1 161
+ct 67 2 162
+ps 67 2 163
+fr 17 2 164
+yn 67 2 165
+fn 67 3 166
+sc 67 3 167
+cr 66 2 168
+--- 28 2 169
+`` 50 2 170
+--- 67 0 171
+--- 33 0 172
+--- 33 0 173
+fi 67 2 174
+fl 67 2 175
+en 50 0 177
+\- "
+dg 67 3 178
+dd 67 3 179
+--- 33 0 180
+pg 85 3 182
+--- 50 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 67 0 187
+--- 100 0 188
+--- 100 2 189
+--- 61 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 100 2 225
+--- 40 2 227
+--- 67 2 232
+--- 83 2 233
+--- 100 2 234
+--- 40 2 235
+--- 100 0 241
+--- 33 0 245
+--- 33 2 248
+--- 67 0 249
+--- 100 0 250
+--- 67 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/MU b/sys/src/cmd/postscript/devpost.add/MU
new file mode 100755
index 000000000..7be9d2a75
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/MU
@@ -0,0 +1,173 @@
+fontname Sonata
+name MU
+charset
+--- 15 0 32
+--- 21 0 33
+--- 51 2 34
+--- 22 1 35
+--- 51 3 36
+--- 52 1 37
+--- 61 3 38
+--- 13 0 39
+--- 17 2 40
+--- 17 2 41
+--- 36 0 42
+--- 30 0 43
+--- 18 1 44
+--- 30 0 45
+--- 9 0 46
+--- 17 2 47
+--- 36 1 48
+--- 25 1 49
+--- 34 1 50
+--- 32 1 51
+--- 33 1 52
+--- 30 1 53
+--- 33 1 54
+--- 34 1 55
+--- 33 1 56
+--- 33 1 57
+--- 15 1 58
+--- 27 2 59
+--- 100 2 61
+--- 42 0 62
+--- 69 2 63
+--- 20 0 64
+--- 30 1 65
+--- 67 2 66
+--- 42 1 67
+--- 81 0 68
+--- 30 1 69
+--- 83 2 70
+--- 15 0 71
+--- 30 1 72
+--- 11 0 73
+--- 0 1 74
+--- 0 1 75
+--- 41 1 76
+--- 59 0 77
+--- 30 0 79
+--- 80 1 80
+--- 30 3 81
+--- 20 3 82
+--- 60 3 83
+--- 62 0 84
+--- 66 2 85
+--- 72 2 86
+--- 61 0 87
+--- 30 1 88
+--- 56 1 89
+--- 65 3 90
+--- 15 2 91
+--- 0 2 92
+--- 46 2 93
+--- 6 2 94
+--- 50 0 95
+--- 50 0 96
+--- 30 3 97
+--- 20 3 98
+--- 42 1 99
+--- 75 0 100
+--- 55 3 101
+--- 52 3 102
+--- 14 2 103
+--- 30 3 104
+--- 10 1 105
+--- 0 2 106
+--- 0 2 107
+--- 4 2 108
+--- 6 0 109
+--- 18 1 110
+--- 12 1 111
+--- 45 1 112
+--- 30 3 113
+--- 56 3 114
+--- 23 0 115
+--- 52 2 116
+--- 66 1 117
+--- 26 2 118
+--- 41 0 119
+--- 56 3 120
+--- 30 3 121
+--- 29 0 122
+--- 13 2 123
+--- 0 1 124
+--- 46 2 125
+--- 34 0 126
+--- 17 2 160
+--- 79 2 161
+--- 25 0 162
+--- 24 0 163
+--- 21 0 164
+--- 26 0 165
+--- 23 0 166
+--- 30 3 167
+--- 21 3 168
+--- 15 2 169
+--- 25 0 170
+--- 8 2 172
+--- 30 0 173
+--- 13 0 174
+--- 30 1 175
+--- 23 0 176
+--- 30 1 177
+--- 30 0 178
+--- 34 0 179
+--- 30 3 180
+--- 85 0 181
+--- 31 0 183
+--- 120 1 184
+--- 82 1 185
+--- 37 1 186
+--- 22 0 187
+--- 23 0 188
+--- 48 0 189
+--- 30 2 190
+--- 30 3 191
+--- 30 0 192
+--- 20 0 193
+--- 41 2 194
+--- 57 2 195
+--- 78 3 196
+--- 14 1 197
+--- 56 3 198
+--- 0 0 199
+--- 0 1 200
+--- 28 2 201
+--- 28 0 202
+--- 26 1 206
+--- 30 0 207
+--- 30 0 208
+--- 30 0 209
+--- 26 2 210
+--- 46 2 211
+--- 61 2 212
+--- 28 2 214
+--- 56 2 215
+--- 47 2 217
+--- 56 2 218
+--- 27 0 220
+--- 50 2 221
+--- 44 1 222
+--- 23 0 224
+--- 30 1 225
+--- 30 1 226
+--- 15 0 227
+--- 7 1 228
+--- 35 3 229
+--- 56 1 231
+--- 28 0 232
+--- 9 1 233
+--- 30 3 234
+--- 105 3 236
+--- 15 1 237
+--- 31 0 238
+--- 30 1 239
+--- 0 2 240
+--- 4 2 241
+--- 37 2 242
+--- 28 0 243
+--- 28 3 244
+--- 0 0 246
+--- 30 0 250
+--- 0 1 251
diff --git a/sys/src/cmd/postscript/devpost.add/OA b/sys/src/cmd/postscript/devpost.add/OA
new file mode 100755
index 000000000..a5d5e6a78
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/OA
@@ -0,0 +1,161 @@
+fontname Optima-Regular
+name OA
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 89 2 37
+& 72 2 38
+' 28 2 39
+( 28 3 40
+) 28 3 41
+* 44 2 42
++ 61 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 39 2 63
+@ 80 3 64
+A 67 2 65
+B 61 2 66
+C 67 2 67
+D 78 2 68
+E 50 2 69
+F 50 2 70
+G 78 2 71
+H 78 2 72
+I 28 2 73
+J 28 2 74
+K 61 2 75
+L 50 2 76
+M 89 2 77
+N 78 2 78
+O 83 2 79
+P 56 2 80
+Q 83 3 81
+R 61 2 82
+S 50 2 83
+T 56 2 84
+U 78 2 85
+V 67 2 86
+W 100 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 50 2 92
+bs "
+] 33 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 56 2 98
+c 50 0 99
+d 56 2 100
+e 50 0 101
+f 28 2 102
+g 50 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 56 1 112
+q 56 1 113
+r 33 0 114
+s 39 0 115
+t 28 2 116
+u 56 0 117
+v 50 0 118
+w 78 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 33 3 124
+} 33 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 50 3 167
+cr 56 2 168
+--- 28 2 169
+`` 44 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 28 0 180
+pg 80 3 182
+--- 61 0 183
+--- 28 1 184
+--- 44 1 185
+'' 44 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 83 2 225
+--- 32 2 227
+--- 50 2 232
+--- 83 2 233
+--- 100 2 234
+--- 34 2 235
+--- 78 0 241
+--- 28 0 245
+--- 28 2 248
+--- 56 0 249
+--- 89 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/OB b/sys/src/cmd/postscript/devpost.add/OB
new file mode 100755
index 000000000..e6564bf95
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/OB
@@ -0,0 +1,161 @@
+name OB
+fontname Optima-Bold
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 100 2 37
+& 72 2 38
+' 28 2 39
+( 33 3 40
+) 33 3 41
+* 44 2 42
++ 61 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 39 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 44 2 63
+@ 75 3 64
+A 67 2 65
+B 61 2 66
+C 67 2 67
+D 78 2 68
+E 50 2 69
+F 50 2 70
+G 78 2 71
+H 78 2 72
+I 33 2 73
+J 33 2 74
+K 61 2 75
+L 50 2 76
+M 89 2 77
+N 78 2 78
+O 83 2 79
+P 56 2 80
+Q 83 3 81
+R 61 2 82
+S 50 2 83
+T 56 2 84
+U 78 2 85
+V 67 2 86
+W 100 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 52 2 92
+bs "
+] 33 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 56 2 98
+c 50 0 99
+d 56 2 100
+e 50 0 101
+f 32 2 102
+g 50 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 56 1 112
+q 56 1 113
+r 39 0 114
+s 39 0 115
+t 33 2 116
+u 56 0 117
+v 50 0 118
+w 78 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 61 3 124
+} 33 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 56 3 167
+cr 56 2 168
+--- 28 2 169
+`` 50 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 61 2 174
+fl 61 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 28 0 180
+pg 70 3 182
+--- 61 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 44 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 89 2 225
+--- 33 2 227
+--- 50 2 232
+--- 83 2 233
+--- 100 2 234
+--- 34 2 235
+--- 78 0 241
+--- 28 0 245
+--- 28 2 248
+--- 56 0 249
+--- 89 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/OI b/sys/src/cmd/postscript/devpost.add/OI
new file mode 100755
index 000000000..2297e422f
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/OI
@@ -0,0 +1,161 @@
+name OI
+fontname Optima-Oblique
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 89 2 37
+& 72 2 38
+' 28 2 39
+( 28 3 40
+) 28 3 41
+* 44 2 42
++ 61 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 28 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 39 2 63
+@ 80 3 64
+A 67 2 65
+B 61 2 66
+C 67 2 67
+D 78 2 68
+E 50 2 69
+F 50 2 70
+G 78 2 71
+H 78 2 72
+I 28 2 73
+J 28 2 74
+K 61 2 75
+L 50 2 76
+M 89 2 77
+N 78 2 78
+O 83 2 79
+P 56 2 80
+Q 83 3 81
+R 61 2 82
+S 50 2 83
+T 56 2 84
+U 78 2 85
+V 67 2 86
+W 100 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 50 2 92
+bs "
+] 33 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 56 2 98
+c 50 0 99
+d 56 2 100
+e 50 0 101
+f 28 2 102
+g 50 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 56 1 112
+q 56 1 113
+r 33 0 114
+s 39 0 115
+t 28 2 116
+u 56 0 117
+v 50 0 118
+w 78 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 33 3 124
+} 33 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 50 3 167
+cr 56 2 168
+--- 28 2 169
+`` 44 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 56 2 174
+fl 56 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 28 0 180
+pg 80 3 182
+--- 61 0 183
+--- 28 1 184
+--- 44 1 185
+'' 44 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 39 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 83 2 225
+--- 32 2 227
+--- 50 2 232
+--- 83 2 233
+--- 100 2 234
+--- 34 2 235
+--- 78 0 241
+--- 28 0 245
+--- 28 2 248
+--- 56 0 249
+--- 89 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/OX b/sys/src/cmd/postscript/devpost.add/OX
new file mode 100755
index 000000000..622dd6792
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/OX
@@ -0,0 +1,161 @@
+name OX
+fontname Optima-BoldOblique
+ligatures fi fl 0
+spacewidth 28
+charset
+! 33 2 33
+" 33 2 34
+dq "
+# 61 2 35
+$ 56 2 36
+% 100 2 37
+& 72 2 38
+' 28 2 39
+( 33 3 40
+) 33 3 41
+* 44 2 42
++ 61 0 43
+, 28 1 44
+hy 33 0 45
+- "
+. 28 0 46
+/ 39 2 47
+0 56 2 48
+1 56 2 49
+2 56 2 50
+3 56 2 51
+4 56 2 52
+5 56 2 53
+6 56 2 54
+7 56 2 55
+8 56 2 56
+9 56 2 57
+: 28 0 58
+; 28 1 59
+--- 61 0 60
+= 61 0 61
+--- 61 0 62
+? 44 2 63
+@ 75 3 64
+A 67 2 65
+B 61 2 66
+C 67 2 67
+D 78 2 68
+E 50 2 69
+F 50 2 70
+G 78 2 71
+H 78 2 72
+I 33 2 73
+J 33 2 74
+K 61 2 75
+L 50 2 76
+M 89 2 77
+N 78 2 78
+O 83 2 79
+P 56 2 80
+Q 83 3 81
+R 61 2 82
+S 50 2 83
+T 56 2 84
+U 78 2 85
+V 67 2 86
+W 100 2 87
+X 61 2 88
+Y 61 2 89
+Z 61 2 90
+[ 33 3 91
+\ 52 2 92
+bs "
+] 33 3 93
+--- 61 2 94
+--- 50 1 95
+` 28 2 96
+a 50 0 97
+b 56 2 98
+c 50 0 99
+d 56 2 100
+e 50 0 101
+f 32 2 102
+g 50 1 103
+h 56 2 104
+i 28 2 105
+j 28 3 106
+k 50 2 107
+l 28 2 108
+m 83 0 109
+n 56 0 110
+o 56 0 111
+p 56 1 112
+q 56 1 113
+r 39 0 114
+s 39 0 115
+t 33 2 116
+u 56 0 117
+v 50 0 118
+w 78 0 119
+x 50 0 120
+y 50 1 121
+z 50 0 122
+{ 33 3 123
+--- 61 3 124
+} 33 3 125
+--- 61 0 126
+--- 33 1 161
+ct 56 2 162
+ps 56 2 163
+fr 17 2 164
+yn 56 2 165
+fn 56 3 166
+sc 56 3 167
+cr 56 2 168
+--- 28 2 169
+`` 50 2 170
+--- 50 0 171
+--- 33 0 172
+--- 33 0 173
+fi 61 2 174
+fl 61 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 28 0 180
+pg 70 3 182
+--- 61 0 183
+--- 28 1 184
+--- 50 1 185
+'' 50 2 186
+--- 50 0 187
+--- 100 0 188
+--- 100 2 189
+--- 44 1 191
+ga 33 2 193
+\` "
+aa 33 2 194
+\' "
+^a 33 2 195
+^ "
+~a 33 2 196
+~ "
+-a 33 2 197
+Ua 33 2 198
+.a 33 2 199
+:a 33 2 200
+oa 33 2 202
+,a 33 1 203
+"a 33 2 205
+Ca 33 1 206
+va 33 2 207
+em 100 0 208
+--- 89 2 225
+--- 33 2 227
+--- 50 2 232
+--- 83 2 233
+--- 100 2 234
+--- 34 2 235
+--- 78 0 241
+--- 28 0 245
+--- 28 2 248
+--- 56 0 249
+--- 89 0 250
+--- 56 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/a1 b/sys/src/cmd/postscript/devpost.add/a1
new file mode 100755
index 000000000..9d50cb4d5
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/a1
@@ -0,0 +1,161 @@
+name a1
+fontname CenturyOldStyleAbstract-Regular
+ligatures fi fl 0
+spacewidth 25
+charset
+! 26 2 33
+" 40 2 34
+dq "
+# 50 2 35
+$ 50 3 36
+% 58 2 37
+& 79 2 38
+' 21 2 39
+( 40 3 40
+) 40 3 41
+* 50 2 42
++ 50 0 43
+, 25 1 44
+hy 24 0 45
+- "
+. 25 0 46
+/ 53 3 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 40 2 63
+@ 85 2 64
+A 66 2 65
+B 69 2 66
+C 69 2 67
+D 76 2 68
+E 66 2 69
+F 61 2 70
+G 74 2 71
+H 76 2 72
+I 34 2 73
+J 40 3 74
+K 71 2 75
+L 58 2 76
+M 95 2 77
+N 76 2 78
+O 76 2 79
+P 61 2 80
+Q 76 3 81
+R 63 2 82
+S 55 2 83
+T 63 2 84
+U 74 2 85
+V 66 2 86
+W 95 2 87
+X 63 2 88
+Y 63 2 89
+Z 58 2 90
+[ 40 3 91
+\ 25 2 92
+bs "
+] 40 3 93
+--- 50 2 94
+--- 50 1 95
+` 21 2 96
+a 47 0 97
+b 55 2 98
+c 47 0 99
+d 55 2 100
+e 50 0 101
+f 29 2 102
+g 55 1 103
+h 58 2 104
+i 26 2 105
+j 24 3 106
+k 55 2 107
+l 26 2 108
+m 84 0 109
+n 55 0 110
+o 53 0 111
+p 53 1 112
+q 55 1 113
+r 40 0 114
+s 45 0 115
+t 31 2 116
+u 55 0 117
+v 47 0 118
+w 71 0 119
+x 53 0 120
+y 50 1 121
+z 45 0 122
+{ 40 3 123
+--- 25 2 124
+} 40 3 125
+--- 50 0 126
+--- 26 1 161
+ct 50 2 162
+ps 50 2 163
+fr 9 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 21 2 169
+`` 40 2 170
+--- 47 0 171
+--- 29 0 172
+--- 29 0 173
+fi 58 2 174
+fl 55 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 25 0 180
+pg 66 3 182
+--- 66 0 183
+--- 21 1 184
+--- 40 1 185
+'' 40 2 186
+--- 47 0 187
+--- 100 0 188
+--- 92 2 189
+--- 40 1 191
+ga 29 2 193
+\` "
+aa 29 2 194
+\' "
+^a 37 2 195
+^ "
+~a 50 2 196
+~ "
+-a 47 2 197
+Ua 50 2 198
+.a 26 2 199
+:a 47 2 200
+oa 34 2 202
+,a 34 1 203
+"a 40 2 205
+Ca 37 1 206
+va 37 2 207
+em 100 0 208
+--- 103 2 225
+--- 34 2 227
+--- 58 2 232
+--- 76 2 233
+--- 108 2 234
+--- 34 2 235
+--- 76 0 241
+--- 26 0 245
+--- 26 2 248
+--- 53 0 249
+--- 84 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/a2 b/sys/src/cmd/postscript/devpost.add/a2
new file mode 100755
index 000000000..0f133dacd
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/a2
@@ -0,0 +1,161 @@
+name a2
+fontname CenturyOldStyleAbstract-Italic
+ligatures fi fl 0
+spacewidth 27
+charset
+! 31 2 33
+" 35 2 34
+dq "
+# 54 0 35
+$ 54 3 36
+% 70 2 37
+& 75 2 38
+' 21 2 39
+( 46 3 40
+) 46 3 41
+* 59 2 42
++ 54 0 43
+, 27 1 44
+hy 25 0 45
+- "
+. 27 0 46
+/ 46 3 47
+0 54 2 48
+1 54 0 49
+2 54 2 50
+3 54 2 51
+4 54 0 52
+5 54 0 53
+6 54 2 54
+7 54 0 55
+8 54 2 56
+9 54 2 57
+: 27 0 58
+; 27 1 59
+--- 54 0 60
+= 54 0 61
+--- 54 0 62
+? 48 2 63
+@ 78 2 64
+A 67 2 65
+B 68 2 66
+C 65 2 67
+D 75 2 68
+E 66 2 69
+F 61 2 70
+G 72 2 71
+H 77 2 72
+I 36 2 73
+J 34 3 74
+K 68 2 75
+L 58 2 76
+M 90 2 77
+N 74 2 78
+O 74 2 79
+P 59 2 80
+Q 75 3 81
+R 65 2 82
+S 57 2 83
+T 63 2 84
+U 72 2 85
+V 62 2 86
+W 88 2 87
+X 64 2 88
+Y 61 2 89
+Z 57 2 90
+[ 46 3 91
+\ 27 2 92
+bs "
+] 46 3 93
+--- 54 2 94
+--- 50 1 95
+` 21 2 96
+a 54 0 97
+b 46 2 98
+c 41 0 99
+d 50 2 100
+e 42 0 101
+f 25 3 102
+g 45 1 103
+h 52 2 104
+i 31 2 105
+j 27 3 106
+k 48 2 107
+l 27 2 108
+m 82 0 109
+n 56 0 110
+o 47 0 111
+p 50 1 112
+q 49 1 113
+r 40 0 114
+s 35 0 115
+t 30 0 116
+u 56 0 117
+v 47 0 118
+w 69 0 119
+x 41 0 120
+y 40 1 121
+z 39 0 122
+{ 46 3 123
+--- 27 2 124
+} 46 3 125
+--- 54 0 126
+--- 31 1 161
+ct 54 0 162
+ps 54 2 163
+fr 5 2 164
+yn 54 0 165
+fn 54 3 166
+sc 59 3 167
+cr 54 0 168
+--- 19 2 169
+`` 37 2 170
+--- 45 0 171
+--- 29 0 172
+--- 29 0 173
+fi 52 3 174
+fl 52 3 175
+en 50 0 177
+\- "
+dg 59 3 178
+dd 59 3 179
+--- 27 0 180
+pg 61 3 182
+--- 61 0 183
+--- 21 1 184
+--- 37 1 185
+'' 37 2 186
+--- 45 0 187
+--- 100 0 188
+--- 103 2 189
+--- 48 1 191
+ga 39 2 193
+\` "
+aa 31 2 194
+\' "
+^a 40 2 195
+^ "
+~a 51 2 196
+~ "
+-a 47 0 197
+Ua 51 2 198
+.a 26 2 199
+:a 46 2 200
+oa 32 2 202
+,a 35 1 203
+"a 39 2 205
+Ca 34 1 206
+va 40 2 207
+em 100 0 208
+--- 94 2 225
+--- 32 2 227
+--- 58 2 232
+--- 74 2 233
+--- 107 2 234
+--- 32 2 235
+--- 69 0 241
+--- 31 0 245
+--- 27 2 248
+--- 47 0 249
+--- 71 0 250
+--- 53 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/c1 b/sys/src/cmd/postscript/devpost.add/c1
new file mode 100755
index 000000000..19be6b2fd
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/c1
@@ -0,0 +1,161 @@
+name C1
+fontname CenturyOldStyle-Regular
+ligatures fi fl 0
+spacewidth 25
+charset
+! 26 2 33
+" 40 2 34
+dq "
+# 50 2 35
+$ 50 3 36
+% 58 2 37
+& 79 2 38
+' 21 2 39
+( 40 3 40
+) 40 3 41
+* 50 2 42
++ 50 0 43
+, 25 1 44
+hy 24 0 45
+- "
+. 25 0 46
+/ 53 3 47
+0 50 2 48
+1 50 2 49
+2 50 2 50
+3 50 2 51
+4 50 2 52
+5 50 2 53
+6 50 2 54
+7 50 2 55
+8 50 2 56
+9 50 2 57
+: 25 0 58
+; 25 1 59
+--- 50 0 60
+= 50 0 61
+--- 50 0 62
+? 40 2 63
+@ 85 2 64
+A 66 2 65
+B 69 2 66
+C 69 2 67
+D 76 2 68
+E 66 2 69
+F 61 2 70
+G 74 2 71
+H 76 2 72
+I 34 2 73
+J 40 3 74
+K 71 2 75
+L 58 2 76
+M 95 2 77
+N 76 2 78
+O 76 2 79
+P 61 2 80
+Q 76 3 81
+R 63 2 82
+S 55 2 83
+T 63 2 84
+U 74 2 85
+V 66 2 86
+W 95 2 87
+X 63 2 88
+Y 63 2 89
+Z 58 2 90
+[ 40 3 91
+\ 25 2 92
+bs "
+] 40 3 93
+--- 50 2 94
+--- 50 1 95
+` 21 2 96
+a 47 0 97
+b 55 2 98
+c 47 0 99
+d 55 2 100
+e 50 0 101
+f 29 2 102
+g 55 1 103
+h 58 2 104
+i 26 2 105
+j 24 3 106
+k 55 2 107
+l 26 2 108
+m 84 0 109
+n 55 0 110
+o 53 0 111
+p 53 1 112
+q 55 1 113
+r 40 0 114
+s 45 0 115
+t 31 2 116
+u 55 0 117
+v 47 0 118
+w 71 0 119
+x 53 0 120
+y 50 1 121
+z 45 0 122
+{ 40 3 123
+--- 25 2 124
+} 40 3 125
+--- 50 0 126
+--- 26 1 161
+ct 50 2 162
+ps 50 2 163
+fr 9 2 164
+yn 50 2 165
+fn 50 3 166
+sc 50 3 167
+cr 50 2 168
+--- 21 2 169
+`` 40 2 170
+--- 47 0 171
+--- 29 0 172
+--- 29 0 173
+fi 58 2 174
+fl 55 2 175
+en 50 0 177
+\- "
+dg 50 3 178
+dd 50 3 179
+--- 25 0 180
+pg 66 3 182
+--- 66 0 183
+--- 21 1 184
+--- 40 1 185
+'' 40 2 186
+--- 47 0 187
+--- 100 0 188
+--- 92 2 189
+--- 40 1 191
+ga 29 2 193
+\` "
+aa 29 2 194
+\' "
+^a 37 2 195
+^ "
+~a 50 2 196
+~ "
+-a 47 2 197
+Ua 50 2 198
+.a 26 2 199
+:a 47 2 200
+oa 34 2 202
+,a 34 1 203
+"a 40 2 205
+Ca 37 1 206
+va 37 2 207
+em 100 0 208
+--- 103 2 225
+--- 34 2 227
+--- 58 2 232
+--- 76 2 233
+--- 108 2 234
+--- 34 2 235
+--- 76 0 241
+--- 26 0 245
+--- 26 2 248
+--- 53 0 249
+--- 84 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/c2 b/sys/src/cmd/postscript/devpost.add/c2
new file mode 100755
index 000000000..359341a67
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/c2
@@ -0,0 +1,161 @@
+name C2
+fontname CenturyOldStyle-Italic
+ligatures fi fl 0
+spacewidth 27
+charset
+! 31 2 33
+" 35 2 34
+dq "
+# 54 0 35
+$ 54 3 36
+% 70 2 37
+& 75 2 38
+' 21 2 39
+( 46 3 40
+) 46 3 41
+* 59 2 42
++ 54 0 43
+, 27 1 44
+hy 25 0 45
+- "
+. 27 0 46
+/ 46 3 47
+0 54 2 48
+1 54 0 49
+2 54 2 50
+3 54 2 51
+4 54 0 52
+5 54 0 53
+6 54 2 54
+7 54 0 55
+8 54 2 56
+9 54 2 57
+: 27 0 58
+; 27 1 59
+--- 54 0 60
+= 54 0 61
+--- 54 0 62
+? 48 2 63
+@ 78 2 64
+A 67 2 65
+B 68 2 66
+C 65 2 67
+D 75 2 68
+E 66 2 69
+F 61 2 70
+G 72 2 71
+H 77 2 72
+I 36 2 73
+J 34 3 74
+K 68 2 75
+L 58 2 76
+M 90 2 77
+N 74 2 78
+O 74 2 79
+P 59 2 80
+Q 75 3 81
+R 65 2 82
+S 57 2 83
+T 63 2 84
+U 72 2 85
+V 62 2 86
+W 88 2 87
+X 64 2 88
+Y 61 2 89
+Z 57 2 90
+[ 46 3 91
+\ 27 2 92
+bs "
+] 46 3 93
+--- 54 2 94
+--- 50 1 95
+` 21 2 96
+a 54 0 97
+b 46 2 98
+c 41 0 99
+d 50 2 100
+e 42 0 101
+f 25 3 102
+g 45 1 103
+h 52 2 104
+i 31 2 105
+j 27 3 106
+k 48 2 107
+l 27 2 108
+m 82 0 109
+n 56 0 110
+o 47 0 111
+p 50 1 112
+q 49 1 113
+r 40 0 114
+s 35 0 115
+t 30 0 116
+u 56 0 117
+v 47 0 118
+w 69 0 119
+x 41 0 120
+y 40 1 121
+z 39 0 122
+{ 46 3 123
+--- 27 2 124
+} 46 3 125
+--- 54 0 126
+--- 31 1 161
+ct 54 0 162
+ps 54 2 163
+fr 5 2 164
+yn 54 0 165
+fn 54 3 166
+sc 59 3 167
+cr 54 0 168
+--- 19 2 169
+`` 37 2 170
+--- 45 0 171
+--- 29 0 172
+--- 29 0 173
+fi 52 3 174
+fl 52 3 175
+en 50 0 177
+\- "
+dg 59 3 178
+dd 59 3 179
+--- 27 0 180
+pg 61 3 182
+--- 61 0 183
+--- 21 1 184
+--- 37 1 185
+'' 37 2 186
+--- 45 0 187
+--- 100 0 188
+--- 103 2 189
+--- 48 1 191
+ga 39 2 193
+\` "
+aa 31 2 194
+\' "
+^a 40 2 195
+^ "
+~a 51 2 196
+~ "
+-a 47 0 197
+Ua 51 2 198
+.a 26 2 199
+:a 46 2 200
+oa 32 2 202
+,a 35 1 203
+"a 39 2 205
+Ca 34 1 206
+va 40 2 207
+em 100 0 208
+--- 94 2 225
+--- 32 2 227
+--- 58 2 232
+--- 74 2 233
+--- 107 2 234
+--- 32 2 235
+--- 69 0 241
+--- 31 0 245
+--- 27 2 248
+--- 47 0 249
+--- 71 0 250
+--- 53 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/c3 b/sys/src/cmd/postscript/devpost.add/c3
new file mode 100755
index 000000000..eef7eef09
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/c3
@@ -0,0 +1,161 @@
+name C3
+fontname CenturyOldStyle-Bold
+ligatures fi fl 0
+spacewidth 33
+charset
+! 34 2 33
+" 40 2 34
+dq "
+# 56 2 35
+$ 66 3 36
+% 79 2 37
+& 84 2 38
+' 28 2 39
+( 39 3 40
+) 39 3 41
+* 56 2 42
++ 56 0 43
+, 33 1 44
+hy 32 0 45
+- "
+. 33 0 46
+/ 51 3 47
+0 66 2 48
+1 66 2 49
+2 66 2 50
+3 66 2 51
+4 66 2 52
+5 66 2 53
+6 66 2 54
+7 66 2 55
+8 66 2 56
+9 66 2 57
+: 33 0 58
+; 33 1 59
+--- 56 0 60
+= 56 0 61
+--- 56 0 62
+? 47 2 63
+@ 72 2 64
+A 69 2 65
+B 76 2 66
+C 70 2 67
+D 80 2 68
+E 71 2 69
+F 64 2 70
+G 78 2 71
+H 83 2 72
+I 41 2 73
+J 48 2 74
+K 76 2 75
+L 63 2 76
+M 94 2 77
+N 80 2 78
+O 81 2 79
+P 72 2 80
+Q 81 3 81
+R 72 2 82
+S 61 2 83
+T 60 2 84
+U 78 2 85
+V 64 2 86
+W 97 2 87
+X 68 2 88
+Y 67 2 89
+Z 59 2 90
+[ 39 3 91
+\ 28 2 92
+bs "
+] 39 3 93
+--- 56 2 94
+--- 50 1 95
+` 28 2 96
+a 53 0 97
+b 60 2 98
+c 52 0 99
+d 62 2 100
+e 54 0 101
+f 30 2 102
+g 52 3 103
+h 62 2 104
+i 31 2 105
+j 27 3 106
+k 60 2 107
+l 30 2 108
+m 92 0 109
+n 62 0 110
+o 57 0 111
+p 61 1 112
+q 60 1 113
+r 44 0 114
+s 50 0 115
+t 33 2 116
+u 63 0 117
+v 50 0 118
+w 74 0 119
+x 53 0 120
+y 49 1 121
+z 44 0 122
+{ 39 3 123
+--- 28 2 124
+} 39 3 125
+--- 56 0 126
+--- 34 1 161
+ct 66 2 162
+ps 66 2 163
+fr 10 2 164
+yn 66 2 165
+fn 66 3 166
+sc 56 3 167
+cr 66 2 168
+--- 22 2 169
+`` 52 2 170
+--- 58 0 171
+--- 37 0 172
+--- 37 0 173
+fi 62 2 174
+fl 61 2 175
+en 50 0 177
+\- "
+dg 56 3 178
+dd 56 3 179
+--- 33 0 180
+pg 62 3 182
+--- 62 0 183
+--- 28 1 184
+--- 52 1 185
+'' 52 2 186
+--- 58 0 187
+--- 100 0 188
+--- 118 2 189
+--- 47 1 191
+ga 34 2 193
+\` "
+aa 34 2 194
+\' "
+^a 45 2 195
+^ "
+~a 56 2 196
+~ "
+-a 53 2 197
+Ua 56 2 198
+.a 29 2 199
+:a 51 2 200
+oa 34 2 202
+,a 37 1 203
+"a 50 2 205
+Ca 38 1 206
+va 45 2 207
+em 100 0 208
+--- 109 2 225
+--- 40 2 227
+--- 63 2 232
+--- 81 2 233
+--- 114 2 234
+--- 40 2 235
+--- 83 0 241
+--- 31 0 245
+--- 30 2 248
+--- 57 0 249
+--- 90 0 250
+--- 58 2 251
diff --git a/sys/src/cmd/postscript/devpost.add/devpost.add.mk b/sys/src/cmd/postscript/devpost.add/devpost.add.mk
new file mode 100755
index 000000000..2a6b9ebeb
--- /dev/null
+++ b/sys/src/cmd/postscript/devpost.add/devpost.add.mk
@@ -0,0 +1,49 @@
+MAKE=/bin/make
+MAKEFILE=devpost.add.mk
+
+SYSTEM=SYSV
+VERSION=3.2
+
+GROUP=bin
+OWNER=bin
+
+FONTDIR=/usr/lib/font
+FONTFILES=??
+
+all :
+
+install : all
+ @if [ ! -d $(FONTDIR) ]; then \
+ mkdir $(FONTDIR); \
+ chmod 755 $(FONTDIR); \
+ chgrp $(GROUP) $(FONTDIR); \
+ chown $(OWNER) $(FONTDIR); \
+ fi
+ @if [ ! -d $(FONTDIR)/devpost ]; then \
+ mkdir $(FONTDIR)/devpost; \
+ chmod 755 $(FONTDIR)/devpost; \
+ chgrp $(GROUP) $(FONTDIR)/devpost; \
+ chown $(OWNER) $(FONTDIR)/devpost; \
+ fi
+ cp $(FONTFILES) $(FONTDIR)/devpost
+ @for i in $(FONTFILES); do \
+ chmod 644 $(FONTDIR)/devpost/$$i; \
+ chgrp $(GROUP) $(FONTDIR)/devpost/$$i; \
+ chown $(OWNER) $(FONTDIR)/devpost/$$i; \
+ done
+
+clean :
+
+clobber : clean
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^FONTDIR=.*'FONTDIR=$(FONTDIR)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE)
+
diff --git a/sys/src/cmd/postscript/download/README b/sys/src/cmd/postscript/download/README
new file mode 100755
index 000000000..9bfce12a8
--- /dev/null
+++ b/sys/src/cmd/postscript/download/README
@@ -0,0 +1,11 @@
+
+A simple program that scans PostScript files for %%DocumentFonts:
+comments and prepends requested host resident font files to the
+input. Written for Unix 4.0 lp.
+
+Downloaded fonts are the ones named in the %%DocumentFonts: comment
+and listed in a special map file (which can be selected using the
+-m option). See example.map and comments in download.c for examples
+of map files. By default map files and font files are in *hostfontdir.
+It's initialized using HOSTDIR (file ../common/path.h).
+
diff --git a/sys/src/cmd/postscript/download/download.c b/sys/src/cmd/postscript/download/download.c
new file mode 100755
index 000000000..78a0341cf
--- /dev/null
+++ b/sys/src/cmd/postscript/download/download.c
@@ -0,0 +1,543 @@
+/*
+ *
+ * download - host resident font downloader
+ *
+ * Prepends host resident fonts to PostScript input files. The program assumes
+ * the input files are part of a single PostScript job and that requested fonts
+ * can be downloaded at the start of each input file. Downloaded fonts are the
+ * ones named in a %%DocumentFonts: comment and listed in a special map table.
+ * Map table pathnames (supplied using the -m option) that begin with a / are
+ * taken as is. Otherwise the final pathname is built using *hostfontdir (-H
+ * option), *mapname (-m option), and *suffix.
+ *
+ * The map table consists of fontname-filename pairs, separated by white space.
+ * Comments are introduced by % (as in PostScript) and extend to the end of the
+ * current line. The only fonts that can be downloaded are the ones listed in
+ * the active map table that point the program to a readable Unix file. A request
+ * for an unlisted font or inaccessible file is ignored. All font requests are
+ * ignored if the map table can't be read. In that case the program simply copies
+ * the input files to stdout.
+ *
+ * An example (but not one to follow) of what can be in a map table is,
+ *
+ * %
+ * % Map requests for Bookman-Light to file *hostfontdir/KR
+ * %
+ *
+ * Bookman-Light KR % Keeping everything (including the map
+ * % table) in *hostfontdir seems like the
+ * % cleanest approach.
+ *
+ * %
+ * % Map Palatino-Roman to file *hostfontdir/palatino/Roman
+ * %
+ * Palatino-Roman palatino/Roman
+ *
+ * % Map ZapfDingbats to file /usr/lib/host/dingbats
+ *
+ * ZapfDingbats /usr/lib/host/dingbats
+ *
+ * Once again, file names that begin with a / are taken as is. All others have
+ * *hostfontdir/ prepended to the file string associated with a particular font.
+ *
+ * Map table can be associated with a printer model (e.g. a LaserWriter), a
+ * printer destination, or whatever - the choice is up to an administrator.
+ * By destination may be best if your spooler is running several private
+ * printers. Host resident fonts are usually purchased under a license that
+ * restricts their use to a limited number of printers. A font licensed for
+ * a single printer should only be used on that printer.
+ *
+ * Was written quickly, so there's much room for improvement. Undoubtedly should
+ * be a more general program (e.g. scan for other comments).
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for temporary directory */
+#include "ext.h" /* external variable declarations */
+#include "download.h" /* a few special definitions */
+
+char *temp_dir = TEMPDIR; /* temp directory - for copying stdin */
+char *hostfontdir = HOSTDIR; /* host resident directory */
+char *mapname = "map"; /* map table - usually in *hostfontdir */
+char *suffix = ""; /* appended to the map table pathname */
+Map *map = NULL; /* device font map table */
+char *stringspace = NULL; /* for storing font and file strings */
+int next = 0; /* next free slot in map[] */
+
+char *residentfonts = NULL; /* list of printer resident fonts */
+char *printer = NULL; /* printer name - only for Unix 4.0 lp */
+
+char buf[2048]; /* input file line buffer */
+char *comment = DOCUMENTFONTS; /* look for this comment */
+int atend = FALSE; /* TRUE only if a comment says so */
+
+FILE *fp_in = stdin; /* next input file */
+FILE *fp_temp = NULL; /* for copying stdin */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * Host resident font downloader. The input files are assumed to be part of a
+ * single PostScript job.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ options(); /* first get command line options */
+ readmap(); /* read the font map table */
+ readresident(); /* and the optional resident font list */
+ arguments(); /* then process non-option arguments */
+ done(); /* and clean things up */
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Makes sure we handle interrupts properly.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ char *optnames = "c:fm:p:r:H:T:DI";
+
+ extern char *optarg; /* used by getopt() */
+ extern int optind;
+
+/*
+ *
+ * Reads and processes the command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'c': /* look for this comment */
+ comment = optarg;
+ break;
+
+ case 'f': /* force a complete input file scan */
+ atend = TRUE;
+ break;
+
+ case 'm': /* printer map table name */
+ mapname = optarg;
+ break;
+
+ case 'p': /* printer name - for Unix 4.0 lp */
+ printer = optarg;
+ break;
+
+ case 'r': /* resident font list */
+ residentfonts = optarg;
+ break;
+
+ case 'H': /* host resident font directory */
+ hostfontdir = optarg;
+ break;
+
+ case 'T': /* temporary file directory */
+ temp_dir = optarg;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+readmap()
+
+{
+
+ char *path;
+ char *ptr;
+ int fd;
+ struct stat sbuf;
+
+/*
+ *
+ * Initializes the map table by reading an ASCII mapping file. If mapname begins
+ * with a / it's the map table. Otherwise hostfontdir, mapname, and suffix are
+ * combined to build the final pathname. If we can open the file we read it all
+ * into memory, erase comments, and separate the font and file name pairs. When
+ * we leave next points to the next free slot in the map[] array. If it's zero
+ * nothing was in the file or we couldn't open it.
+ *
+ */
+
+ if ( hostfontdir == NULL || mapname == NULL )
+ return;
+
+ if ( *mapname != '/' ) {
+ if ( (path = (char *)malloc(strlen(hostfontdir) + strlen(mapname) +
+ strlen(suffix) + 2)) == NULL )
+ error(FATAL, "no memory");
+ sprintf(path, "%s/%s%s", hostfontdir, mapname, suffix);
+ } else path = mapname;
+
+ if ( (fd = open(path, 0)) != -1 ) {
+ if ( fstat(fd, &sbuf) == -1 )
+ error(FATAL, "can't fstat %s", path);
+ if ( (stringspace = (char *)malloc(sbuf.st_size + 2)) == NULL )
+ error(FATAL, "no memory");
+ if ( read(fd, stringspace, sbuf.st_size) == -1 )
+ error(FATAL, "can't read %s", path);
+ close(fd);
+
+ stringspace[sbuf.st_size] = '\n'; /* just to be safe */
+ stringspace[sbuf.st_size+1] = '\0';
+ for ( ptr = stringspace; *ptr != '\0'; ptr++ ) /* erase comments */
+ if ( *ptr == '%' )
+ for ( ; *ptr != '\n' ; ptr++ )
+ *ptr = ' ';
+
+ for ( ptr = stringspace; ; next++ ) {
+ if ( (next % 50) == 0 )
+ map = allocate(map, next+50);
+ map[next].downloaded = FALSE;
+ map[next].font = strtok(ptr, " \t\n");
+ map[next].file = strtok(ptr = NULL, " \t\n");
+ if ( map[next].font == NULL )
+ break;
+ if ( map[next].file == NULL )
+ error(FATAL, "map table format error - check %s", path);
+ } /* End for */
+ } /* End if */
+
+} /* End of readmap */
+
+/*****************************************************************************/
+
+readresident()
+
+{
+
+ FILE *fp;
+ char *path;
+ int ch;
+ int n;
+
+/*
+ *
+ * Reads a file that lists the resident fonts for a particular printer and marks
+ * each font as already downloaded. Nothing's done if the file can't be read or
+ * there's no mapping file. Comments, as in the map file, begin with a % and
+ * extend to the end of the line. Added for Unix 4.0 lp.
+ *
+ */
+
+ if ( next == 0 || (printer == NULL && residentfonts == NULL) )
+ return;
+
+ if ( printer != NULL ) { /* use Unix 4.0 lp pathnames */
+ sprintf(buf, "%s/printers/%s", HOSTDIR, printer);
+ path = buf;
+ } else path = residentfonts;
+
+ if ( (fp = fopen(path, "r")) != NULL ) {
+ while ( fscanf(fp, "%s", buf) != EOF )
+ if ( buf[0] == '%' )
+ while ( (ch = getc(fp)) != EOF && ch != '\n' ) ;
+ else if ( (n = lookup(buf)) < next )
+ map[n].downloaded = TRUE;
+ fclose(fp);
+ } /* End if */
+
+} /* End of readresident */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll translate stdin. Assumes input files are part of a single PostScript
+ * job and fonts can be downloaded at the start of each file.
+ *
+ */
+
+ if ( argc < 1 )
+ download();
+ else {
+ while ( argc > 0 ) {
+ fp_temp = NULL;
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ download();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ if ( fp_temp != NULL )
+ fclose(fp_temp);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Clean things up before we quit.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+download()
+
+{
+
+ int infontlist = FALSE;
+
+/*
+ *
+ * If next is zero the map table is empty and all we do is copy the input file
+ * to stdout. Otherwise we read the input file looking for %%DocumentFonts: or
+ * continuation comments, add any accessible fonts to the output file, and then
+ * append the input file. When reading stdin we append lines to fp_temp and
+ * recover them when we're ready to copy the input file. fp_temp will often
+ * only contain part of stdin - if there's no %%DocumentFonts: (atend) comment
+ * we stop reading fp_in after the header.
+ *
+ */
+
+ if ( next > 0 ) {
+ if ( fp_in == stdin ) {
+ if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
+ error(FATAL, "can't generate temp file name");
+ if ( (fp_temp = fopen(temp_file, "w+r")) == NULL )
+ error(FATAL, "can't open %s", temp_file);
+ unlink(temp_file);
+ } /* End if */
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL ) {
+ if ( fp_temp != NULL )
+ fprintf(fp_temp, "%s", buf);
+ if ( buf[0] != '%' || buf[1] != '%' ) {
+ if ( (buf[0] != '%' || buf[1] != '!') && atend == FALSE )
+ break;
+ infontlist = FALSE;
+ } else if ( strncmp(buf, comment, strlen(comment)) == 0 ) {
+ copyfonts(buf);
+ infontlist = TRUE;
+ } else if ( buf[2] == '+' && infontlist == TRUE )
+ copyfonts(buf);
+ else infontlist = FALSE;
+ } /* End while */
+ } /* End if */
+
+ copyinput();
+
+} /* End of download */
+
+/*****************************************************************************/
+
+copyfonts(list)
+
+ char *list;
+
+{
+
+ char *font;
+ char *path;
+ int n;
+
+/*
+ *
+ * list points to a %%DocumentFonts: or continuation comment. What follows the
+ * the keyword will be a list of fonts separated by white space (or (atend)).
+ * Look for each font in the map table and if it's found copy the font file to
+ * stdout (once only).
+ *
+ */
+
+ strtok(list, " \n"); /* skip to the font list */
+
+ while ( (font = strtok(NULL, " \t\n")) != NULL ) {
+ if ( strcmp(font, ATEND) == 0 ) {
+ atend = TRUE;
+ break;
+ } /* End if */
+ if ( (n = lookup(font)) < next ) {
+ if ( *map[n].file != '/' ) {
+ if ( (path = (char *)malloc(strlen(hostfontdir)+strlen(map[n].file)+2)) == NULL )
+ error(FATAL, "no memory");
+ sprintf(path, "%s/%s", hostfontdir, map[n].file);
+ cat(path);
+ free(path);
+ } else cat(map[n].file);
+ map[n].downloaded = TRUE;
+ } /* End if */
+ } /* End while */
+
+} /* End of copyfonts */
+
+/*****************************************************************************/
+
+copyinput()
+
+{
+
+/*
+ *
+ * Copies the input file to stdout. If fp_temp isn't NULL seek to the start and
+ * add it to the output file - it's a partial (or complete) copy of stdin made
+ * by download(). Then copy fp_in, but only seek to the start if it's not stdin.
+ *
+ */
+
+ if ( fp_temp != NULL ) {
+ fseek(fp_temp, 0L, 0);
+ while ( fgets(buf, sizeof(buf), fp_temp) != NULL )
+ printf("%s", buf);
+ } /* End if */
+
+ if ( fp_in != stdin )
+ fseek(fp_in, 0L, 0);
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ printf("%s", buf);
+
+} /* End of copyinput */
+
+/*****************************************************************************/
+
+lookup(font)
+
+ char *font;
+
+{
+
+ int i;
+
+/*
+ *
+ * Looks for *font in the map table. Return the map table index if found and
+ * not yet downloaded - otherwise return next.
+ *
+ */
+
+ for ( i = 0; i < next; i++ )
+ if ( strcmp(font, map[i].font) == 0 ) {
+ if ( map[i].downloaded == TRUE )
+ i = next;
+ break;
+ } /* End if */
+
+ return(i);
+
+} /* End of lookup */
+
+/*****************************************************************************/
+
+Map *allocate(ptr, num)
+
+ Map *ptr;
+ int num;
+
+{
+
+/*
+ *
+ * Allocates space for num Map elements. Calls malloc() if ptr is NULL and
+ * realloc() otherwise.
+ *
+ */
+
+ if ( ptr == NULL )
+ ptr = (Map *)malloc(num * sizeof(Map));
+ else ptr = (Map *)realloc(ptr, num * sizeof(Map));
+
+ if ( ptr == NULL )
+ error(FATAL, "no map memory");
+
+ return(ptr);
+
+} /* End of allocate */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/download/download.h b/sys/src/cmd/postscript/download/download.h
new file mode 100755
index 000000000..f88cc57ad
--- /dev/null
+++ b/sys/src/cmd/postscript/download/download.h
@@ -0,0 +1,14 @@
+/*
+ *
+ * The font data for a printer is saved in an array of the following type.
+ *
+ */
+
+typedef struct map {
+ char *font; /* a request for this PostScript font */
+ char *file; /* means copy this unix file */
+ int downloaded; /* TRUE after *file is downloaded */
+} Map;
+
+Map *allocate();
+
diff --git a/sys/src/cmd/postscript/download/mkfile b/sys/src/cmd/postscript/download/mkfile
new file mode 100755
index 000000000..a153f5b6c
--- /dev/null
+++ b/sys/src/cmd/postscript/download/mkfile
@@ -0,0 +1,27 @@
+</$objtype/mkfile
+
+<../config
+TARG=download
+
+OFILES=download.$O
+
+COMMONDIR=../common
+
+HFILES=download.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+ $COMMONDIR/ext.h\
+
+LIB=$COMMONDIR/com.a$O
+BIN=$POSTBIN
+
+</sys/src/cmd/mkone
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/g3p9bit/btab b/sys/src/cmd/postscript/g3p9bit/btab
new file mode 100755
index 000000000..45ca2c80a
--- /dev/null
+++ b/sys/src/cmd/postscript/g3p9bit/btab
@@ -0,0 +1,109 @@
+{"0000110111", 0},
+{"010", 1},
+{"11", 2},
+{"10", 3},
+{"011", 4},
+{"0011", 5},
+{"0010", 6},
+{"00011", 7},
+{"000101", 8},
+{"000100", 9},
+{"0000100", 10},
+{"0000101", 11},
+{"0000111", 12},
+{"00000100", 13},
+{"00000111", 14},
+{"000011000", 15},
+{"0000010111", 16},
+{"0000011000", 17},
+{"0000001000", 18},
+{"00001100111", 19},
+{"00001101000", 20},
+{"00001101100", 21},
+{"00000110111", 22},
+{"00000101000", 23},
+{"00000010111", 24},
+{"00000011000", 25},
+{"000011001010", 26},
+{"000011001011", 27},
+{"000011001100", 28},
+{"000011001101", 29},
+{"000001101000", 30},
+{"000001101001", 31},
+{"000001101010", 32},
+{"000001101011", 33},
+{"000011010010", 34},
+{"000011010011", 35},
+{"000011010100", 36},
+{"000011010101", 37},
+{"000011010110", 38},
+{"000011010111", 39},
+{"000001101100", 40},
+{"000001101101", 41},
+{"000011011010", 42},
+{"000011011011", 43},
+{"000001010100", 44},
+{"000001010101", 45},
+{"000001010110", 46},
+{"000001010111", 47},
+{"000001100100", 48},
+{"000001100101", 49},
+{"000001010010", 50},
+{"000001010011", 51},
+{"000000100100", 52},
+{"000000110111", 53},
+{"000000111000", 54},
+{"000000100111", 55},
+{"000000101000", 56},
+{"000001011000", 57},
+{"000001011001", 58},
+{"000000101011", 59},
+{"000000101100", 60},
+{"000001011010", 61},
+{"000001100110", 62},
+{"000001100111", 63},
+
+{"0000001111", 64},
+{"000011001000", 128},
+{"000011001001", 192},
+{"000001011011", 256},
+{"000000110011", 320},
+{"000000110100", 384},
+{"000000110101", 448},
+{"0000001101100", 512},
+{"0000001101101", 576},
+{"0000001001010", 640},
+{"0000001001011", 704},
+{"0000001001100", 768},
+{"0000001001101", 832},
+{"0000001110010", 896},
+{"0000001110011", 960},
+{"0000001110100", 1024},
+{"0000001110101", 1088},
+{"0000001110110", 1152},
+{"0000001110111", 1216},
+{"0000001010010", 1280},
+{"0000001010011", 1344},
+{"0000001010100", 1408},
+{"0000001010101", 1472},
+{"0000001011010", 1536},
+{"0000001011011", 1600},
+{"0000001100100", 1664},
+{"0000001100101", 1728},
+
+{"00000001000", 1792},
+{"00000001100", 1856},
+{"00000001101", 1920},
+{"000000010010", 1984},
+{"000000010011", 2048},
+{"000000010100", 2112},
+{"000000010101", 2176},
+{"000000010110", 2240},
+{"000000010111", 2304},
+{"000000011100", 2368},
+{"000000011101", 2432},
+{"000000011110", 2496},
+{"000000011111", 2560},
+
+{"000000000001", -1},
+{"000000000000", -2},
diff --git a/sys/src/cmd/postscript/g3p9bit/g3p9bit.c b/sys/src/cmd/postscript/g3p9bit/g3p9bit.c
new file mode 100755
index 000000000..2650d8b08
--- /dev/null
+++ b/sys/src/cmd/postscript/g3p9bit/g3p9bit.c
@@ -0,0 +1,455 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+ ERR,
+ EOL,
+ MAKE,
+ TERM,
+};
+
+enum
+{
+ White,
+ Black,
+};
+
+typedef struct Tab
+{
+ ushort run;
+ ushort bits;
+ int code;
+} Tab;
+
+Tab wtab[8192];
+Tab btab[8192];
+uchar bitrev[256];
+uchar bitnonrev[256];
+
+int readrow(uchar *rev, int*);
+void initwbtab(void);
+void sync(uchar*);
+int readfile(int, char*, char*);
+
+int nbytes;
+uchar *bytes;
+uchar *pixels;
+uchar *buf;
+int y;
+uint bitoffset;
+uint word24;
+
+enum
+{
+ Bytes = 1024*1024,
+ Lines = 1410, /* 1100 for A4, 1410 for B4 */
+ Dots = 1728,
+};
+
+void
+error(char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+
+ if(fmt){
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+ fprint(2, "g3: %s\n", buf);
+ }
+ exits(fmt);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: g3p9bit [-gy] file\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int y, fd, n, m;
+ char *t;
+ char *file, err[ERRMAX], tbuf[5*12+1];
+ int gray=0;
+ int yscale=1;
+
+ ARGBEGIN{
+ case 'g':
+ /* do simulated 2bit gray to compress x */
+ gray++;
+ break;
+ case 'y':
+ /* double each scan line to double the y resolution */
+ yscale=2;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 1)
+ usage();
+
+ initwbtab();
+ buf = malloc(1024*1024);
+ t = malloc((Dots/8)*Lines);
+ if(buf==nil || t==nil)
+ error("malloc failed: %r\n");
+ pixels = (uchar*)t;
+
+ file = "<stdin>";
+ fd = 0;
+ if(argc > 0){
+ file = argv[0];
+ fd = open(file, OREAD);
+ if(fd < 0)
+ error("can't open %s", file);
+ }
+ y = readfile(fd, file, err);
+ if(y < 0)
+ error(err);
+ sprint(tbuf, "%11d %11d %11d %11d %11d ", gray, 0, 0, Dots/(gray+1), y*yscale);
+ write(1, tbuf, 5*12);
+ n = (Dots/8)*y*yscale;
+ /* write in pieces; brazil pipes work badly with huge counts */
+ while(n > 0){
+ if(yscale > 1) /* write one scan line */
+ m = Dots/8;
+ else{ /* write lots */
+ m = n;
+ if(m > 8192)
+ m = 8192;
+ }
+ for(y=0; y<yscale; y++){
+ if(write(1, t, m) != m)
+ error("write error");
+ n -= m;
+ }
+ t += m;
+ }
+ if(err[0])
+ error(err);
+ error(nil);
+}
+
+enum{
+ Hvres,
+ Hbaud,
+ Hwidth,
+ Hlength,
+ Hcomp,
+ HenabECM,
+ HenabBFT,
+ Hmsperscan,
+};
+
+int defhdr[8] = {
+ 0, /* 98 lpi */
+ 0, /* 2400 baud */
+ 0, /* 1728 pixels in 215mm */
+ 0, /* A4, 297mm */
+ 0, /* 1-D modified huffman */
+ 0, /* disable ECM */
+ 0, /* disable BFT */
+ 3, /* 10 ms per scan */
+};
+
+int
+crackhdr(uchar *ap, int *hdr)
+{
+ char *p, *q;
+ int i;
+
+ p = (char*)ap;
+ q = p;
+ for(i=0; i<8; i++){
+ if(*p<'0' || '9'<*p)
+ return -1;
+ hdr[i] = strtol(p, &q, 0);
+ p = q+1;
+ }
+ return p-(char*)ap;
+}
+
+int
+readfile(int f, char *file, char *err)
+{
+ int i, r, lines;
+ uchar *rev;
+ int hdr[8];
+
+ err[0] = 0;
+ memset(pixels, 0, (Dots/8) * Lines);
+ nbytes = readn(f, buf, 1024*1024);
+ if(nbytes==1024*1024 || nbytes<=100){
+ bad:
+ sprint(err, "g3: file improper size or format: %s", file);
+ return -1;
+ }
+ bytes = buf;
+ if(bytes[0]=='I' && bytes[1]=='I' && bytes[2]=='*'){ /* dumb PC format */
+ bytes += 0xf3;
+ nbytes -= 0xf3;
+ rev = bitrev;
+ memmove(hdr, defhdr, sizeof defhdr);
+ }else if(bytes[0] == 0 && strcmp((char*)bytes+1, "PC Research, Inc") == 0){ /* digifax format */
+ memmove(hdr, defhdr, sizeof defhdr);
+ if(bytes[45] == 0x40 && bytes[29] == 1) /* high resolution */
+ hdr[Hvres] = 1;
+ else
+ hdr[Hvres] = 0;
+ /* hdr[26] | (hdr[27]<<8) is page number */
+
+ bytes += 64;
+ nbytes -= 64;
+ rev = bitnonrev;
+ }else{
+ while(nbytes > 2){
+ if(bytes[0]=='\n'){
+ if(strncmp((char*)bytes+1, "FDCS=", 5) == 0){
+ i = crackhdr(bytes+6, hdr);
+ if(i < 0){
+ sprint(err, "g3: bad FDCS in header: %s", file);
+ return -1;
+ }
+ if(hdr[Hwidth] != 0){
+ sprint(err, "g3: unsupported width: %s", file);
+ return -1;
+ }
+ if(hdr[Hcomp] != 0){
+ sprint(err, "g3: unsupported compression: %s", file);
+ return -1;
+ }
+ bytes += i+1;
+ nbytes -= i+1;
+ continue;
+ }
+ if(bytes[1] == '\n'){
+ bytes += 2;
+ nbytes -= 2;
+ break;
+ }
+ }
+ bytes++;
+ nbytes--;
+ }
+ if(nbytes < 2)
+ goto bad;
+ rev = bitnonrev;
+ }
+ bitoffset = 24;
+ word24 = 0;
+ sync(rev);
+ lines = Lines;
+ if(hdr[Hvres] == 1)
+ lines *= 2;
+ for(y=0; y<lines; y++){
+ r = readrow(rev, hdr);
+ if(r < 0)
+ break;
+ if(r == 0)
+ sync(rev);
+ }
+ if(hdr[Hvres] == 1)
+ y /= 2;
+// if(y < 100)
+// goto bad;
+ return y;
+}
+
+int
+readrow(uchar *rev, int *hdr)
+{
+ int bo, state;
+ Tab *tab, *t;
+ int x, oldx, x2, oldx2, dx, xx;
+ uint w24;
+ uchar *p, *q;
+
+ state = White;
+ oldx = 0;
+ bo = bitoffset;
+ w24 = word24;
+ x = y;
+ if(hdr[Hvres] == 1) /* high resolution */
+ x /= 2;
+ p = pixels + x*Dots/8;
+ x = 0;
+
+loop:
+ if(x > Dots)
+ return 0;
+ if(state == White)
+ tab = wtab;
+ else
+ tab = btab;
+ if(bo > (24-13)) {
+ do {
+ if(nbytes <= 0)
+ return -1;
+ w24 = (w24<<8) | rev[*bytes];
+ bo -= 8;
+ bytes++;
+ nbytes--;
+ } while(bo >= 8);
+ }
+
+ t = tab + ((w24 >> (24-13-bo)) & 8191);
+ x += t->run;
+ bo += t->bits;
+ if(t->code == TERM){
+ if(state == White)
+ oldx = x;
+ else{
+ oldx2 = oldx;
+ x2 = x;
+ xx = oldx2&7;
+ q = p+oldx2/8;
+ if(x2/8 == oldx2/8) /* all in one byte, but if((x2&7)==0), do harder case */
+ *q |= (0xFF>>xx) & (0xFF<<(8-(x2&7)));
+ else{
+ dx = x2 - oldx2;
+ /* leading edge */
+ if(xx){
+ *q++ |= 0xFF>>xx;
+ dx -= 8-xx;
+ }
+ /* middle */
+ while(dx >= 8){
+ *q++ = 0xFF;
+ dx -= 8;
+ }
+ /* trailing edge */
+ if(dx)
+ *q |= 0xFF<<(8-dx);
+ }
+ }
+ state ^= White^Black;
+ goto loop;
+ }
+ if(t->code == ERR){
+ bitoffset = bo;
+ word24 = w24;
+ return 0;
+ }
+ if(t->code == EOL){
+ bitoffset = bo;
+ word24 = w24;
+ return 1;
+ }
+ goto loop;
+}
+
+
+void
+sync(uchar *rev)
+{
+ Tab *t;
+ int c;
+
+ c = 0;
+loop:
+ if(bitoffset > (24-13)) {
+ do {
+ if(nbytes <= 0)
+ return;
+ word24 = (word24<<8) | rev[*bytes];
+ bitoffset -= 8;
+ bytes++;
+ nbytes--;
+ } while(bitoffset >= 8);
+ }
+ t = wtab + ((word24 >> (24-13-bitoffset)) & 8191);
+ if(t->code != EOL) {
+ bitoffset++;
+ c++;
+ goto loop;
+ }
+ bitoffset += t->bits;
+}
+
+typedef struct File
+{
+ char *val;
+ int code;
+}File;
+
+File ibtab[] = {
+#include "btab"
+{nil, 0}
+};
+
+File iwtab[] = {
+#include "wtab"
+{nil, 0}
+};
+
+int
+binary(char *s)
+{
+ int n;
+
+ n = 0;
+ while(*s)
+ n = n*2 + *s++-'0';
+ return n;
+}
+
+void
+tabinit(File *file, Tab *tab)
+{
+ int i, j, v, r, l;
+ char *b;
+
+ for(v=0; v<8192; v++) {
+ tab[v].run = 0;
+ tab[v].bits = 1;
+ tab[v].code = ERR;
+ }
+ for(i=0; b=file[i].val; i++){
+ l = strlen(b);
+ v = binary(b);
+ r = file[i].code;
+ if(l > 13)
+ fprint(2, "g3: oops1 l = %d %s\n", l, b);
+
+ v = v<<(13-l);
+ for(j=0; j<(1<<((13-l))); j++) {
+ if(tab[v].code != ERR)
+ fprint(2, "g3: oops2 %d %s\n", r, b);
+ tab[v].run = r;
+ tab[v].bits = l;
+ tab[v].code = TERM;
+ if(r < 0) {
+ tab[v].run = 0;
+ tab[v].code = EOL;
+ if(r < -1) {
+ tab[v].bits = 1;
+ tab[v].code = MAKE;
+ }
+ }
+ if(r >= 64) {
+ tab[v].code = MAKE;
+ }
+ v++;
+ }
+ }
+
+ for(i=0; i<256; i++)
+ for(j=0; j<8; j++)
+ if(i & (1<<j))
+ bitrev[i] |= 0x80 >> j;
+ for(i=0; i<256; i++)
+ bitnonrev[i] = i;
+}
+
+void
+initwbtab(void)
+{
+ tabinit(iwtab, wtab);
+ tabinit(ibtab, btab);
+}
diff --git a/sys/src/cmd/postscript/g3p9bit/mkfile b/sys/src/cmd/postscript/g3p9bit/mkfile
new file mode 100755
index 000000000..2475a75bc
--- /dev/null
+++ b/sys/src/cmd/postscript/g3p9bit/mkfile
@@ -0,0 +1,14 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=g3p9bit
+
+OFILES=g3p9bit.$O
+
+HFILES=wtab btab
+
+BIN=$POSTBIN
+
+</sys/src/cmd/mkone
+
diff --git a/sys/src/cmd/postscript/g3p9bit/wtab b/sys/src/cmd/postscript/g3p9bit/wtab
new file mode 100755
index 000000000..580b68a48
--- /dev/null
+++ b/sys/src/cmd/postscript/g3p9bit/wtab
@@ -0,0 +1,109 @@
+{"00110101", 0},
+{"000111", 1},
+{"0111", 2},
+{"1000", 3},
+{"1011", 4},
+{"1100", 5},
+{"1110", 6},
+{"1111", 7},
+{"10011", 8},
+{"10100", 9},
+{"00111", 10},
+{"01000", 11},
+{"001000", 12},
+{"000011", 13},
+{"110100", 14},
+{"110101", 15},
+{"101010", 16},
+{"101011", 17},
+{"0100111", 18},
+{"0001100", 19},
+{"0001000", 20},
+{"0010111", 21},
+{"0000011", 22},
+{"0000100", 23},
+{"0101000", 24},
+{"0101011", 25},
+{"0010011", 26},
+{"0100100", 27},
+{"0011000", 28},
+{"00000010", 29},
+{"00000011", 30},
+{"00011010", 31},
+{"00011011", 32},
+{"00010010", 33},
+{"00010011", 34},
+{"00010100", 35},
+{"00010101", 36},
+{"00010110", 37},
+{"00010111", 38},
+{"00101000", 39},
+{"00101001", 40},
+{"00101010", 41},
+{"00101011", 42},
+{"00101100", 43},
+{"00101101", 44},
+{"00000100", 45},
+{"00000101", 46},
+{"00001010", 47},
+{"00001011", 48},
+{"01010010", 49},
+{"01010011", 50},
+{"01010100", 51},
+{"01010101", 52},
+{"00100100", 53},
+{"00100101", 54},
+{"01011000", 55},
+{"01011001", 56},
+{"01011010", 57},
+{"01011011", 58},
+{"01001010", 59},
+{"01001011", 60},
+{"00110010", 61},
+{"00110011", 62},
+{"00110100", 63},
+
+{"11011", 64},
+{"10010", 128},
+{"010111", 192},
+{"0110111", 256},
+{"00110110", 320},
+{"00110111", 384},
+{"01100100", 448},
+{"01100101", 512},
+{"01101000", 576},
+{"01100111", 640},
+{"011001100", 704},
+{"011001101", 768},
+{"011010010", 832},
+{"011010011", 896},
+{"011010100", 960},
+{"011010101", 1024},
+{"011010110", 1088},
+{"011010111", 1152},
+{"011011000", 1216},
+{"011011001", 1280},
+{"011011010", 1344},
+{"011011011", 1408},
+{"010011000", 1472},
+{"010011001", 1536},
+{"010011010", 1600},
+{"011000", 1664},
+{"010011011", 1728},
+
+{"00000001000", 1792},
+{"00000001100", 1856},
+{"00000001101", 1920},
+{"000000010010", 1984},
+{"000000010011", 2048},
+{"000000010100", 2112},
+{"000000010101", 2176},
+{"000000010110", 2240},
+{"000000010111", 2304},
+{"000000011100", 2368},
+{"000000011101", 2432},
+{"000000011110", 2496},
+{"000000011111", 2560},
+
+{"000000000001", -1},
+{"000000000000", -2},
diff --git a/sys/src/cmd/postscript/grabit/grabit.ps b/sys/src/cmd/postscript/grabit/grabit.ps
new file mode 100755
index 000000000..dab313c51
--- /dev/null
+++ b/sys/src/cmd/postscript/grabit/grabit.ps
@@ -0,0 +1,522 @@
+%
+% Dump a PostScript object, occasionally in a form that can be sent back
+% through the interpreter. Similiar to Adobe's == procedure, but output
+% is usually easier to read. No binding so operators like rcheck and exec
+% can be conviently redefined.
+%
+
+/GrabitDict 100 dict dup begin
+
+/recursive true def
+/scratchstring 200 string def
+/slowdown 100 def
+
+/column 0 def
+/lastcolumn 80 def
+/level 0 def
+/multiline 100 array def
+/nextname 0 def
+/arraylength 0 def
+/lengthonly false def
+
+/GrabitSetup {
+ counttomark {OmitNames exch true put} repeat pop
+ 0 0 moveto % for hardcopy output
+} def
+
+/OmitNames 30 dict def % ignore these names
+/OtherDicts 200 dict def % unrecognized dictionaries
+
+%
+% All strings returned to the host go through Print. First pass through an
+% array has lengthonly set to true.
+%
+
+/Print {
+ dup type /stringtype ne {scratchstring cvs} if
+ lengthonly {
+ length arraylength add /arraylength exch def
+ }{
+ dup length column add /column exch def
+ print flush
+ slowdown {1 pop} repeat
+ } ifelse
+} def
+
+/Indent {level {( ) Print} repeat} def
+/Newline {(\n) Print lengthonly not {/column 0 def} if} def
+
+/NextLevel {/level level 1 add def multiline level 0 put} def
+/LastLevel {/level level 1 sub def} def
+
+%
+% Make a unique name for each unrecognized dictionary and remember the name
+% and dictionary in OtherDicts.
+%
+
+/Register {
+ dup type /dicttype eq {
+ /nextname nextname 1 add def
+ dup (UnknownDict ) dup
+ (UnknownDict) length nextname ( ) cvs putinterval
+ 0 (UnknownDict) length nextname ( ) cvs length add getinterval cvn
+ exch OtherDicts 3 1 roll put
+ } if
+} def
+
+%
+% Replace array or dictionary values by known names. Lookups are in the
+% standard PostScript dictionaries and in OtherDicts. If found replace
+% the value by the name and make it executable so nametype omits the
+% leading /.
+%
+
+/Replace {
+ false
+ 1 index type /dicttype eq {pop true} if
+ 1 index type /arraytype eq 2 index xcheck not and {pop true} if
+ {
+ false
+ [userdict systemdict statusdict serverdict OtherDicts] {
+ {
+ 3 index eq
+ {exch pop exch pop cvx true exit}
+ {pop}
+ ifelse
+ } forall
+ dup {exit} if
+ } forall
+ pop
+ } if
+} def
+
+%
+% Simple type handlers. In some cases (e.g. savetype) what's returned can't
+% be sent back through the interpreter.
+%
+
+/booleantype {{(true )}{(false )} ifelse Print} def
+/marktype {pop (mark ) Print} def
+/nulltype {pop (null ) Print} def
+/integertype {Print ( ) Print} def
+/realtype {Print ( ) Print} def
+/filetype {pop (-file- ) Print} def
+/fonttype {pop (-fontID- ) Print} def
+/savetype {pop (-saveobj- ) Print} def
+
+%
+% Special formatting for operators is enabled if the flag in multiline
+% (for the current level) is set to 1. In that case each operator, after
+% being printed, is looked up in OperatorDict. If found the value is used
+% as an index into the OperatorProcs array and the object at that index
+% is retrieved and executed. Currently only used to choose the operators
+% that end a line.
+%
+
+/operatortype {
+ dup Print ( ) Print
+ multiline level get 1 eq {
+ scratchstring cvs cvn dup OperatorDict exch known {
+ OperatorDict exch get
+ OperatorProcs exch get exec
+ }{
+ pop
+ column lastcolumn gt {Newline Indent} if
+ } ifelse
+ }{pop} ifelse
+} def
+
+%
+% Executable names are passed to operatortype. Non-executable names get a
+% leading /.
+%
+
+/nametype {
+ dup xcheck {
+ operatortype
+ }{
+ (/) Print Print ( ) Print
+ } ifelse
+} def
+
+%
+% Arrays are processed in two passes. The first computes the length of the
+% string returned to the host without any special formatting. If it extends
+% past the last column special formatting is enabled by setting a flag in
+% array multiline. Arrays are processed in a for loop so the last element
+% easily recognized. At that point special fortmatting is disabled.
+%
+
+/packedarraytype {arraytype} def
+
+/arraytype {
+ NextLevel
+ lengthonly not {
+ /lengthonly true def
+ /arraylength 0 def
+ dup dup type exec
+ arraylength 20 gt arraylength column add lastcolumn gt and {
+ multiline level 1 put
+ } if
+ /lengthonly false def
+ } if
+
+ dup rcheck not {
+ (-array- ) Print pop
+ }{
+ dup xcheck {({)}{([)} ifelse Print
+ multiline level get 0 ne {Newline Indent}{( ) Print} ifelse
+ 0 1 2 index length 1 sub {
+ 2 copy exch length 1 sub eq multiline level get 1 eq and {
+ multiline level 2 put
+ } if
+ 2 copy get exch pop
+ dup type /dicttype eq {
+ Replace
+ dup type /dicttype eq {
+ dup Register Replace
+ recursive {
+ 2 copy cvlit
+ /def load 3 1 roll
+ count 3 roll
+ } if
+ exch pop
+ } if
+ } if
+ dup type exec
+ dup xcheck not multiline level get 1 eq and {
+ 0 index type /arraytype eq
+ 1 index type /packedarray eq or
+ 1 index type /stringtype eq or {Newline Indent} if
+ } if
+ } for
+ multiline level get 0 ne {Newline LastLevel Indent NextLevel} if
+ xcheck {(} )}{(] )} ifelse Print
+ } ifelse
+ LastLevel
+} def
+
+%
+% Dictionary handler. Try to replace the value by a name before processing
+% the dictionary.
+%
+
+/dicttype {
+ dup
+ rcheck not {
+ (-dictionary- ) Print pop
+ }{
+ dup maxlength Print ( dict dup begin) Print Newline
+ NextLevel
+ {
+ 1 index OmitNames exch known {
+ pop pop
+ }{
+ Indent
+ Replace % arrays and dicts by known names
+ Register % new dictionaries in OtherDicts
+ exch
+ cvlit dup type exec % key first - force a /
+ dup type exec % then the value
+ (def) Print Newline
+ } ifelse
+ } forall
+ LastLevel
+ Indent
+ (end ) Print
+ } ifelse
+} def
+
+%
+% Strings containing characters not in AsciiDict are returned in hex. All
+% others are ASCII strings and use AsciiDict for character mapping.
+%
+
+/onecharstring ( ) def
+/twocharstring ( ) def
+
+/stringtype {
+ dup
+ rcheck not {
+ (-string- ) Print
+ }{
+ /hexit false def
+ dup {
+ onecharstring 0 3 -1 roll put
+ AsciiDict onecharstring cvn known not {
+ /hexit true def exit
+ } if
+ } forall
+
+ hexit {(<)}{(\()} ifelse Print
+ 0 1 2 index length 1 sub {
+ 2 copy 1 getinterval exch pop
+ hexit {
+ 0 get /n exch def
+ n -4 bitshift 16#F and 16 twocharstring cvrs pop
+ n 16#F and twocharstring 1 1 getinterval 16 exch cvrs pop
+ twocharstring
+ }{cvn AsciiDict exch get} ifelse
+ Print
+ column lastcolumn gt {
+ hexit not {(\\) Print} if
+ Newline
+ } if
+ } for
+ hexit {(> )}{(\) )} ifelse Print
+ } ifelse
+ pop
+} def
+
+%
+% ASCII characters and replacement strings. Ensures the returned string will
+% reproduce the original when passed through the scanner. Strings containing
+% characters not in this list should be returned as hex strings.
+%
+
+/AsciiDict 128 dict dup begin
+ (\n) cvn (\\n) def
+ (\r) cvn (\\r) def
+ (\t) cvn (\\t) def
+ (\b) cvn (\\b) def
+ (\f) cvn (\\f) def
+ ( ) cvn ( ) def
+ (!) cvn (!) def
+ (") cvn (") def
+ (#) cvn (#) def
+ ($) cvn ($) def
+ (%) cvn (\\%) def
+ (&) cvn (&) def
+ (') cvn (') def
+ (\() cvn (\\\() def
+ (\)) cvn (\\\)) def
+ (*) cvn (*) def
+ (+) cvn (+) def
+ (,) cvn (,) def
+ (-) cvn (-) def
+ (.) cvn (.) def
+ (/) cvn (/) def
+ (0) cvn (0) def
+ (1) cvn (1) def
+ (2) cvn (2) def
+ (3) cvn (3) def
+ (4) cvn (4) def
+ (5) cvn (5) def
+ (6) cvn (6) def
+ (7) cvn (7) def
+ (8) cvn (8) def
+ (9) cvn (9) def
+ (:) cvn (:) def
+ (;) cvn (;) def
+ (<) cvn (<) def
+ (=) cvn (=) def
+ (>) cvn (>) def
+ (?) cvn (?) def
+ (@) cvn (@) def
+ (A) cvn (A) def
+ (B) cvn (B) def
+ (C) cvn (C) def
+ (D) cvn (D) def
+ (E) cvn (E) def
+ (F) cvn (F) def
+ (G) cvn (G) def
+ (H) cvn (H) def
+ (I) cvn (I) def
+ (J) cvn (J) def
+ (K) cvn (K) def
+ (L) cvn (L) def
+ (M) cvn (M) def
+ (N) cvn (N) def
+ (O) cvn (O) def
+ (P) cvn (P) def
+ (Q) cvn (Q) def
+ (R) cvn (R) def
+ (S) cvn (S) def
+ (T) cvn (T) def
+ (U) cvn (U) def
+ (V) cvn (V) def
+ (W) cvn (W) def
+ (X) cvn (X) def
+ (Y) cvn (Y) def
+ (Z) cvn (Z) def
+ ([) cvn ([) def
+ (\\) cvn (\\\\) def
+ (]) cvn (]) def
+ (^) cvn (^) def
+ (_) cvn (_) def
+ (`) cvn (`) def
+ (a) cvn (a) def
+ (b) cvn (b) def
+ (c) cvn (c) def
+ (d) cvn (d) def
+ (e) cvn (e) def
+ (f) cvn (f) def
+ (g) cvn (g) def
+ (h) cvn (h) def
+ (i) cvn (i) def
+ (j) cvn (j) def
+ (k) cvn (k) def
+ (l) cvn (l) def
+ (m) cvn (m) def
+ (n) cvn (n) def
+ (o) cvn (o) def
+ (p) cvn (p) def
+ (q) cvn (q) def
+ (r) cvn (r) def
+ (s) cvn (s) def
+ (t) cvn (t) def
+ (u) cvn (u) def
+ (v) cvn (v) def
+ (w) cvn (w) def
+ (x) cvn (x) def
+ (y) cvn (y) def
+ (z) cvn (z) def
+ ({) cvn ({) def
+ (|) cvn (|) def
+ (}) cvn (}) def
+ (~) cvn (~) def
+end def
+
+%
+% OperatorDict can help format procedure listings. The value assigned to each
+% name is used as an index into the OperatorProcs array. The procedure at that
+% index is fetched and executed after the named operator is printed. What's in
+% OperatorDict is a matter of taste rather than correctness. The default list
+% represents our choice of which of Adobe's operators should end a line.
+%
+
+/OperatorProcs [{} {Newline Indent}] def
+
+/OperatorDict 250 dict def
+
+OperatorDict /arc 1 put
+OperatorDict /arcn 1 put
+OperatorDict /ashow 1 put
+OperatorDict /awidthshow 1 put
+OperatorDict /banddevice 1 put
+OperatorDict /begin 1 put
+OperatorDict /charpath 1 put
+OperatorDict /clear 1 put
+OperatorDict /cleardictstack 1 put
+OperatorDict /cleartomark 1 put
+OperatorDict /clip 1 put
+OperatorDict /clippath 1 put
+OperatorDict /closefile 1 put
+OperatorDict /closepath 1 put
+OperatorDict /concat 1 put
+OperatorDict /copypage 1 put
+OperatorDict /curveto 1 put
+OperatorDict /def 1 put
+OperatorDict /end 1 put
+OperatorDict /eoclip 1 put
+OperatorDict /eofill 1 put
+OperatorDict /erasepage 1 put
+OperatorDict /exec 1 put
+OperatorDict /exit 1 put
+OperatorDict /fill 1 put
+OperatorDict /flattenpath 1 put
+OperatorDict /flush 1 put
+OperatorDict /flushfile 1 put
+OperatorDict /for 1 put
+OperatorDict /forall 1 put
+OperatorDict /framedevice 1 put
+OperatorDict /grestore 1 put
+OperatorDict /grestoreall 1 put
+OperatorDict /gsave 1 put
+OperatorDict /handleerror 1 put
+OperatorDict /if 1 put
+OperatorDict /ifelse 1 put
+OperatorDict /image 1 put
+OperatorDict /imagemask 1 put
+OperatorDict /initclip 1 put
+OperatorDict /initgraphics 1 put
+OperatorDict /initmatrix 1 put
+OperatorDict /kshow 1 put
+OperatorDict /lineto 1 put
+OperatorDict /loop 1 put
+OperatorDict /moveto 1 put
+OperatorDict /newpath 1 put
+OperatorDict /nulldevice 1 put
+OperatorDict /pathforall 1 put
+OperatorDict /print 1 put
+OperatorDict /prompt 1 put
+OperatorDict /put 1 put
+OperatorDict /putinterval 1 put
+OperatorDict /quit 1 put
+OperatorDict /rcurveto 1 put
+OperatorDict /renderbands 1 put
+OperatorDict /repeat 1 put
+OperatorDict /resetfile 1 put
+OperatorDict /restore 1 put
+OperatorDict /reversepath 1 put
+OperatorDict /rlineto 1 put
+OperatorDict /rmoveto 1 put
+OperatorDict /rotate 1 put
+OperatorDict /run 1 put
+OperatorDict /scale 1 put
+OperatorDict /setcachedevice 1 put
+OperatorDict /setcachelimit 1 put
+OperatorDict /setcacheparams 1 put
+OperatorDict /setcharwidth 1 put
+OperatorDict /setdash 1 put
+OperatorDict /setdefaulttimeouts 1 put
+OperatorDict /setdostartpage 1 put
+OperatorDict /seteescratch 1 put
+OperatorDict /setflat 1 put
+OperatorDict /setfont 1 put
+OperatorDict /setgray 1 put
+OperatorDict /sethsbcolor 1 put
+OperatorDict /setidlefonts 1 put
+OperatorDict /setjobtimeout 1 put
+OperatorDict /setlinecap 1 put
+OperatorDict /setlinejoin 1 put
+OperatorDict /setlinewidth 1 put
+OperatorDict /setmargins 1 put
+OperatorDict /setmatrix 1 put
+OperatorDict /setmiterlimit 1 put
+OperatorDict /setpacking 1 put
+OperatorDict /setpagetype 1 put
+OperatorDict /setprintname 1 put
+OperatorDict /setrgbcolor 1 put
+OperatorDict /setsccbatch 1 put
+OperatorDict /setsccinteractive 1 put
+OperatorDict /setscreen 1 put
+OperatorDict /settransfer 1 put
+OperatorDict /show 1 put
+OperatorDict /showpage 1 put
+OperatorDict /start 1 put
+OperatorDict /stop 1 put
+OperatorDict /store 1 put
+OperatorDict /stroke 1 put
+OperatorDict /strokepath 1 put
+OperatorDict /translate 1 put
+OperatorDict /widthshow 1 put
+OperatorDict /write 1 put
+OperatorDict /writehexstring 1 put
+OperatorDict /writestring 1 put
+
+end def
+
+%
+% Put an object on the stack and call Grabit. Output continues until stack
+% is empty. For example,
+%
+% /letter load Grabit
+%
+% prints a listing of the letter procedure.
+%
+
+/Grabit {
+ /saveobj save def
+ GrabitDict begin
+ {
+ count 0 eq {exit} if
+ count {dup type exec} repeat
+ (\n) print flush
+ } loop
+ end
+ currentpoint % for hardcopy output
+ saveobj restore
+ moveto
+} def
+
diff --git a/sys/src/cmd/postscript/grabit/grabit.rc b/sys/src/cmd/postscript/grabit/grabit.rc
new file mode 100755
index 000000000..1f06c2176
--- /dev/null
+++ b/sys/src/cmd/postscript/grabit/grabit.rc
@@ -0,0 +1,64 @@
+#!/bin/rc
+# Print a listing of an object, often a dictionary or an array. Something
+# like ==, but the output is often easier to read and closer to PostScript
+# that can be sent back through the interpreter.
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+PROLOGUE=$POSTLIB/grabit.ps
+
+COPYFILE=
+RECURSIVE=true
+OMITNAMES='/Grabit /GrabitDict'
+
+NONCONFORMING='%!PS'
+ENDPROLOG='%%EndProlog'
+BEGINSETUP='%%BeginSetup'
+ENDSETUP='%%EndSetup'
+TRAILER='%%Trailer'
+
+SETUP=GrabitSetup
+
+while (! ~ $#* 0 && ~ $1 -*) {
+ switch ($1) {
+ case -d; RECURSIVE=false
+
+ case -o; shift; OMITNAMES=$OMITNAMES' '$1
+ case -o*; OMITNAMES=$OMITNAMES' '`{echo $1 | sed s/-o//}
+
+ case -C; shift; COPYFILE=$COPYFILE' '$1
+ case -C*; COPYFILE=$COPYFILE' '`{echo $1 | sed s/-C//}
+
+ case -L; shift; PROLOGUE=$1
+ case -L*; PROLOGUE=`{echo $1 | sed s/-L//}
+
+ case --;
+
+ case -*; echo $0: illegal option $1 >[1=2]; exit 1
+ }
+ shift
+done
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo $ENDPROLOG
+echo $BEGINSETUP
+if (~ $COPYFILE '') COPYFILE=/dev/null
+cat $COPYFILE
+echo 'GrabitDict begin'
+echo '/recursive '$RECURSIVE' def'
+
+echo mark
+for (i in $OMITNAMES) {
+ switch ($i) {
+ case /*; echo $i
+ case ?*; echo /$i
+ }
+}
+echo GrabitSetup
+
+echo end
+echo $ENDSETUP
+
+for (i) echo $i Grabit
+
diff --git a/sys/src/cmd/postscript/grabit/mkfile b/sys/src/cmd/postscript/grabit/mkfile
new file mode 100755
index 000000000..ca2b3c928
--- /dev/null
+++ b/sys/src/cmd/postscript/grabit/mkfile
@@ -0,0 +1,20 @@
+</$objtype/mkfile
+
+<../config
+
+all:V: grabit
+
+install installall:V: $POSTBIN/grabit $POSTLIB/grabit.ps
+
+$POSTBIN/grabit: grabit
+ cp $prereq $target
+
+$POSTLIB/grabit.ps: grabit.ps
+ cp $prereq $target
+
+grabit: grabit.rc
+ sed 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' grabit.rc >grabit
+ chmod 775 grabit
+
+clean nuke:V:
+ rm -f grabit
diff --git a/sys/src/cmd/postscript/hardcopy/README b/sys/src/cmd/postscript/hardcopy/README
new file mode 100755
index 000000000..2ad29065a
--- /dev/null
+++ b/sys/src/cmd/postscript/hardcopy/README
@@ -0,0 +1,11 @@
+Redirect output of the print operator to paper. Particularly useful if
+you're trying to extract information from a printer, but you don't have
+access to the data the printer returns to the host.
+
+For example,
+
+ FontDirectory {pop ==} forall
+
+names all the fonts registered in the FontDirectory dictionary. Simple,
+but only if you can read the data returned to the host by the printer.
+
diff --git a/sys/src/cmd/postscript/hardcopy/hardcopy.ps b/sys/src/cmd/postscript/hardcopy/hardcopy.ps
new file mode 100755
index 000000000..65eb24df9
--- /dev/null
+++ b/sys/src/cmd/postscript/hardcopy/hardcopy.ps
@@ -0,0 +1,196 @@
+%
+% Redefiniton of the PostScript file output operators so results go to paper.
+% Complicated and slow, but the implementation doesn't place many demands on
+% included PostScript. About all that's required is gentle treatment of the
+% graphics state between write calls.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/font /Courier def
+/formsperpage 1 def
+/landscape false def
+/magnification 1 def
+/orientation 0 def
+/pointsize 10 def
+/rotation 1 def
+/xoffset .1 def
+/yoffset .1 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} def
+/min {2 copy gt {exch} if pop} def
+
+/HardcopySetup {
+ landscape {/orientation 90 orientation add def} if
+ font findfont 1 1.1 div scalefont setfont
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ pointsize 1.1 mul dup scale
+ magnification dup aspectratio mul scale
+ height width div 1 min dup scale
+ 0 -1 translate
+ 0 0 moveto
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+} def
+
+%
+% Unbind the operators in an executable array or packedarray. Leaves the
+% unbound array or the original object on the stack.
+%
+
+/Unbind {
+ 0 index xcheck
+ 1 index type /arraytype eq
+ 2 index type /packedarraytype eq or and {
+ dup length array copy cvx
+ dup 0 exch {
+ dup type /operatortype eq {
+ ( ) cvs cvn cvx
+ } if
+
+ dup type /dicttype eq {
+ dup maxlength dict exch {
+ Unbind
+ 3 copy put pop pop
+ } forall
+ } if
+
+ 0 index xcheck
+ 1 index type /arraytype eq
+ 2 index type /packedarraytype eq or and {
+ Unbind
+ } if
+
+ 3 copy put pop
+ 1 add
+ } forall
+ pop
+ } if
+} def
+
+%
+% New write operator - don't bind the definition! Expands tabs and backspaces,
+% wraps long lines, and starts a new page whenever necessary. The code that
+% handles newlines assumes lines are separated by one vertical unit.
+%
+
+/write {
+ true exch
+
+ %%case '\b':
+ dup 8#10 eq {
+ ( ) stringwidth pop neg 0 rmoveto
+ currentpoint pop 0 lt {
+ currentpoint exch pop 0 exch moveto
+ } if
+ exch pop false exch
+ } if
+
+ %%case '\t':
+ dup 8#11 eq {
+ currentpoint pop ( ) stringwidth pop div round cvi
+ 8 mod 8 exch sub {
+ 2 index 8#40 write
+ } repeat
+ exch pop false exch
+ } if
+
+ %%case '\n':
+ dup 8#12 eq {
+ currentpoint 0 exch 1 sub moveto pop
+
+ gsave clippath pathbbox pop pop exch pop grestore
+ currentpoint exch pop 1 sub ge {
+ 2 index 8#14 write
+ } if
+ exch pop false exch
+ } if
+
+ %%case '\f':
+ dup 8#14 eq {
+ gsave showpage grestore
+ 0 0 moveto
+ exch pop false exch
+ } if
+
+ %%case '\r':
+ dup 8#15 eq {
+ currentpoint 0 exch moveto pop
+ exch pop false exch
+ } if
+
+ %%case EOF:
+ dup -1 eq {
+ currentpoint 0 ne exch 0 ne or {
+ 2 index 8#14 write
+ } if
+ exch pop false exch
+ } if
+
+ %%default:
+ exch {
+ dup
+ gsave clippath pathbbox pop 3 1 roll pop pop grestore
+ ( ) stringwidth pop currentpoint pop add le {
+ 2 index 8#12 write
+ } if
+ ( ) dup 0 4 -1 roll put show
+ } if
+
+ pop % the character
+ pop % and file object
+} def
+
+%
+% All the other file output operators call our redefined write operator.
+%
+
+/print {
+ (%stdout) (w) file exch {1 index exch write} forall
+ pop
+} def
+
+/writestring {
+ {1 index exch write} forall
+ pop
+} def
+
+/writehexstring {
+ (0123456789ABCDEF) 3 1 roll {
+ dup
+ 3 index exch -4 bitshift 16#F and get 2 index exch write
+ 2 index exch 16#F and get 1 index exch write
+ } forall
+ pop pop
+} def
+
+%
+% Unbind and redefine the remaining file output procedures.
+%
+
+/= dup load Unbind def
+/== dup load Unbind def
+/stack dup load Unbind def
+/pstack dup load Unbind def
+
diff --git a/sys/src/cmd/postscript/hardcopy/hardcopy.rc b/sys/src/cmd/postscript/hardcopy/hardcopy.rc
new file mode 100755
index 000000000..0077aa716
--- /dev/null
+++ b/sys/src/cmd/postscript/hardcopy/hardcopy.rc
@@ -0,0 +1,70 @@
+#!/bin/rc
+# Generate paper output from the data that a PostScript program normally
+# sends back to a host computer using file output operators.
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+PROLOGUE=$POSTLIB/hardcopy.ps
+
+OPTIONS=
+MODE=portrait
+
+NONCONFORMING='%!PS'
+ENDPROLOG='%%EndProlog'
+BEGINSETUP='%%BeginSetup'
+ENDSETUP='%%EndSetup'
+TRAILER='%%Trailer'
+
+SETUP=HardcopySetup
+DONE='(%stdout)(w) file -1 write'
+
+while (! ~ $#* 0 && ~ $1 -*) {
+ switch ($1) {
+ case -c; shift; OPTIONS=$OPTIONS' /#copies '$1' store'
+ case -c*; OPTIONS=$OPTIONS' /#copies `{echo $1 | sed s/-c//}' store'
+
+ case -f; shift; OPTIONS=$OPTIONS' /font '/$1' def'
+ case -f*; OPTIONS=$OPTIONS' /font '/`{echo $1 | sed s/-f//}' def'
+
+ case -p; shift; MODE=$1
+ case -p*; MODE=`{echo $1 | sed s/-p//}
+
+ case -m; shift; OPTIONS=$OPTIONS' /magnification '$1' def'
+ case -m*; OPTIONS=$OPTIONS' /magnification '`{echo $1 | sed s/-m//}' def'
+
+ case -s; shift; OPTIONS=$OPTIONS' /pointsize '$1' def'
+ case -s*; OPTIONS=$OPTIONS' /pointsize '`{echo $1 | sed s/-s//}' def'
+
+ case -x; shift; OPTIONS=$OPTIONS' /xoffset '$1' def'
+ case -x*; OPTIONS=$OPTIONS' /xoffset '`{echo $1 | sed s/-x//}' def'
+
+ case -y; shift; OPTIONS=$OPTIONS' /yoffset '$1' def'
+ case -y*; OPTIONS=$OPTIONS' /yoffset '`{echo $1 | sed s/-y//}' def'
+
+ case -L; shift; PROLOGUE=$1
+ case -L*; PROLOGUE=`{echo $1 | sed s/-L//}
+
+ case --;
+
+ case -*; echo '$0: illegal option $1' >&2; exit 1
+ }
+ shift
+}
+
+switch ($MODE) {
+ case l*; OPTIONS=$OPTIONS' /landscape true def'
+ case *; OPTIONS=$OPTIONS' /landscape false def'
+}
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo $ENDPROLOG
+echo $BEGINSETUP
+echo $OPTIONS
+echo $SETUP
+echo $ENDSETUP
+
+cat $*
+
+echo $TRAILER
+echo $DONE
diff --git a/sys/src/cmd/postscript/hardcopy/mkfile b/sys/src/cmd/postscript/hardcopy/mkfile
new file mode 100755
index 000000000..c6131dbac
--- /dev/null
+++ b/sys/src/cmd/postscript/hardcopy/mkfile
@@ -0,0 +1,20 @@
+</$objtype/mkfile
+
+<../config
+
+all:V: hardcopy
+
+installall install:V: $POSTBIN/hardcopy $POSTLIB/hardcopy.ps
+
+$POSTBIN/hardcopy: hardcopy
+ cp $prereq $target
+
+$POSTLIB/hardcopy.ps: hardcopy.ps
+ cp $prereq $target
+
+hardcopy: hardcopy.rc
+ sed 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' hardcopy.rc >hardcopy
+ chmod 775 hardcopy
+
+clean nuke:V:
+ rm -f hardcopy
diff --git a/sys/src/cmd/postscript/mcolor/color.sr b/sys/src/cmd/postscript/mcolor/color.sr
new file mode 100755
index 000000000..0a63aad52
--- /dev/null
+++ b/sys/src/cmd/postscript/mcolor/color.sr
@@ -0,0 +1,45 @@
+.ds Dc black
+.ds Cc \*(Dc
+.de CL \" Color selection macro
+. \"
+. \" $1=color (e.g. .CL red) or
+. \" $1=textcolor on backgroundcolor (e.g. .CL "red on blue")
+. \" $1=red green blue rgb (e.g. .CL ".2 .3 .4 rgb") or
+. \" $1=hue saturation brightness hsb (e.g. .CL ".5 .6 .7 hsb")
+. \" $2=text
+. \"
+. \" If no arguments are given the default color (usually black) will be
+. \" restored. If $2 is omitted the color selected by $1 remains in effect
+. \" until another color is selected. If two arguments are given $2 will be
+. \" printed in color $1 and then the default color will be restored.
+. \"
+. \" The color of the text and the background can be selected by setting $1
+. \" to "textcolor on backgroundcolor" where textcolor and backgroundcolor
+. \" can be any of the known colors. For example use .CL "white on black"
+. \" for reverse video printing. Changing color in a word can be accomplished
+. \" by preceeding the characters with a \X'SetColor:val' command, where val
+. \" is the color you wish to use. Named colors are case independent.
+. \"
+. \" Implementation details are device dependent and are handled in the
+. \" appropriate post-processor. Requesting a color that's not available
+. \" (eg. not defined in /usr/lib/postscript/color.ps) results in the use
+. \" of default colors - black or white on black for reverse video mode.
+. \"
+. \" All colors may not be supported on every output device, and the direct
+. \" specification of a color via an explicit rgb or hsb argument may also
+. \" be device dependent. In any case, to be safe on PostScript devices, all
+. \" numeric paramenters in the direct rgb or hsb specifications should lie
+. \" between 0 and 1 (inclusive). The names of the currently available colors
+. \" on PostScript printers are listed in file /usr/lib/postscript/color.ps.
+. \"
+.mk Ov
+.if \\n(.$=0 .ds Cc \\*(Dc
+.if \\n(.$=1 .ds Cc \\$1
+.if \\n(.$<2 \\X'SetColor:\\*(Cc'
+.if \\n(.$=2 \\X'SetColor:\\$1'\\c
+.if \\n(.$=2 'sp |\\n(Ovu
+.if \\n(.$=2 \\$2
+.if \\n(.$=2 .mk Ov
+.if \\n(.$=2 \\X'SetColor:\\*(Cc'\\c
+'sp |\\n(Ovu
+..
diff --git a/sys/src/cmd/postscript/mcolor/mcolor.5 b/sys/src/cmd/postscript/mcolor/mcolor.5
new file mode 100755
index 000000000..bf0181fda
--- /dev/null
+++ b/sys/src/cmd/postscript/mcolor/mcolor.5
@@ -0,0 +1,60 @@
+.ds dP /usr/lib/postscript
+.ds dT /usr/lib/tmac
+.TH MCOLOR 5
+.SH NAME
+.B mcolor
+\- color and reverse video macro
+.SH SYNOPSIS
+\*(mBtroff \-mcolor\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B mcolor
+is a macro package for color selection and reverse video printing
+on PostScript printers.
+The package is compatible with most existing macro packages
+and includes the following macro:
+.TP 1.25i
+.MI .CL "\0color\0text"
+Prints
+.I text
+in
+.IR color .
+No arguments restores the default color (black).
+If
+.I text
+is omitted the selected
+.I color
+remains in effect until another
+.I color
+is selected.
+If two arguments are given the
+.I text
+is printed in
+.I color
+and then the default color is restored.
+.PP
+Both the text and background color can be selected.
+A
+.I color
+argument of
+.RI `` color1
+.MW on
+.IR color2 ''
+prints text in
+.I color1
+on a background in
+.I color2 .
+.PP
+Named colors must be listed in the
+``colordict''
+dictionary in file
+.MR \*(dP/color.ps .
+.SH FILES
+.MW \*(dT/tmac.color
+.br
+.MW \*(dP/color.ps
+.SH SEE ALSO
+.BR troff (1),
+.BR dpost (1),
+.BR mps (5)
diff --git a/sys/src/cmd/postscript/mcolor/mcolor.mk b/sys/src/cmd/postscript/mcolor/mcolor.mk
new file mode 100755
index 000000000..aacadcfb6
--- /dev/null
+++ b/sys/src/cmd/postscript/mcolor/mcolor.mk
@@ -0,0 +1,53 @@
+#
+# Not installing the man page.
+#
+
+MAKE=/bin/make
+MAKEFILE=mcolor.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+OWNER=bin
+GROUP=bin
+
+POSTLIB=/usr/lib/postscript
+TMACDIR=/usr/lib/tmac
+
+all :
+ @cp color.sr tmac.color
+
+install : all
+ @if [ ! -d $(TMACDIR) ]; then \
+ mkdir $(TMACDIR); \
+ chmod 755 $(TMACDIR); \
+ chgrp $(GROUP) $(TMACDIR); \
+ chown $(OWNER) $(TMACDIR); \
+ fi
+ cp tmac.color $(TMACDIR)/tmac.color
+ @chmod 644 $(TMACDIR)/tmac.color
+ @chgrp $(GROUP) $(TMACDIR)/tmac.color
+ @chown $(OWNER) $(TMACDIR)/tmac.color
+
+clean :
+ rm -f tmac.color
+
+clobber : clean
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ -e "s'^TMACDIR=.*'TMACDIR=$(TMACDIR)'" \
+ $(MAKEFILE) >X$(MAKEFILE); \
+ mv X$(MAKEFILE) $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dP.*'.ds dP $(POSTLIB)'" \
+ -e "s'^.ds dT.*'.ds dT $(TMACDIR)'" \
+ mcolor.5 >Xmcolor.5; \
+ mv Xmcolor.5 mcolor.5
+
diff --git a/sys/src/cmd/postscript/misc/README b/sys/src/cmd/postscript/misc/README
new file mode 100755
index 000000000..d17f15da2
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/README
@@ -0,0 +1,15 @@
+Miscellaneous programs - all are unsupported.
+
+ ibmfont.c IBM PC to Unix font conversion program
+ laserbar.[c1] Barcode filter and man page - Jan Wolitzky
+ lp.model Sample lp interface (not for Unix 4.0) - Maryann Csaszar
+ macfont.c Macintosh to Unix font conversion program
+ pscrypt.c Quick implementation of Adobe's encryption/decryption algorithm
+ setbaud.ps Example of how to change the printer's baudrate
+
+Use make to compile C programs. For example,
+
+ make macfont
+
+compiles macfont.c.
+
diff --git a/sys/src/cmd/postscript/misc/ibmfont.c b/sys/src/cmd/postscript/misc/ibmfont.c
new file mode 100755
index 000000000..99e53c346
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/ibmfont.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * Program that converts IBM font files to a format that works on Unix systems.
+ * Essentially all the information needed came from the Adobe paper "Supporting
+ * Downloadable PostScript Fonts". To use the program type,
+ *
+ * ibmfont font.ibm >font.unix
+ *
+ * where font.ibm is the font file, exactly as it came over from an IBM PC,
+ * and font.unix is equivalent host resident font file usable on Unix systems.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#define OFF 0
+#define ON 1
+
+#define NON_FATAL 0
+#define FATAL 1
+
+#define FALSE 0
+#define TRUE 1
+
+char **argv;
+int argc;
+
+char *prog_name;
+
+int x_stat;
+int debug = OFF;
+int ignore = OFF;
+
+FILE *fp_in = stdin;
+FILE *fp_out = stdout;
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * IBM PC to Unix font converter.
+ *
+ */
+
+ argc = agc;
+ argv = agv;
+ prog_name = argv[0];
+
+ options();
+ arguments();
+ exit(x_stat);
+
+} /* End of main */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch;
+ char *names = "DI";
+
+ extern char *optarg;
+ extern int optind;
+
+/*
+ *
+ * Command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, names)) != EOF ) {
+ switch ( ch ) {
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind;
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Everything esle is an input file. No arguments or '-' means stdin.
+ *
+ */
+
+
+ if ( argc < 1 )
+ conv();
+ else
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ conv();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+conv()
+
+{
+
+ int blocksize;
+ int blocktype;
+ int seg;
+ long ftell();
+
+/*
+ *
+ * Font files on the IBM PC are stored in a compressed binary format. Individual
+ * segments in the file are preceeded by a header that looks like,
+ *
+ * Byte 1: 128
+ * Byte 2: segment type (1=ASCII, 2=TOHEX, or 3=EOF)
+ * Bytes 3-6: length of the segment
+ * Bytes 7 ... data
+ *
+ */
+
+ while ( 1 ) {
+ seg = ftell(fp_in);
+ if ( getc(fp_in) != 128 )
+ error(FATAL, "bad file format");
+ blocktype = getc(fp_in);
+ blocksize = getint(fp_in);
+ if ( debug == ON ) {
+ fprintf(stderr, "blocktype = %d, blocksize = %d\n", blocktype, blocksize);
+ fprintf(stderr, "start=0%o, end=0%o\n", seg, seg+blocksize+6);
+ fprintf(stderr, "start=%d, end=%d\n", seg, seg+blocksize+6);
+ } /* End if */
+ switch ( blocktype ) {
+ case 1:
+ asciitext(blocksize);
+ break;
+
+ case 2:
+ hexdata(blocksize);
+ break;
+
+ case 3:
+ return;
+
+ default:
+ error(FATAL, "unknown resource type %d", blocktype);
+ } /* End switch */
+ } /* End while */
+
+} /* End of conv */
+
+/*****************************************************************************/
+
+asciitext(count)
+
+ int count; /* bytes left in the block */
+
+{
+
+ int ch;
+ int i = 0;
+
+/*
+ *
+ * Handles type 1 (ie. ASCII text) blocks. Changing carriage returns to newlines
+ * is all I've done.
+ *
+ */
+
+ for ( i = 0; i < count; i++ ) {
+ if ( (ch = getc(fp_in)) == '\r' )
+ ch = '\n';
+ putc(ch, fp_out);
+ } /* End for */
+
+} /* End of asciitext */
+
+/*****************************************************************************/
+
+hexdata(count)
+
+ int count; /* bytes left in the block */
+
+{
+
+ int i;
+ int n;
+
+/*
+ *
+ * Reads the next count bytes and converts each byte to hex. Also starts a new
+ * line every 80 hex characters.
+ *
+ */
+
+ for ( i = 0, n = 0; i < count; i++ ) {
+ fprintf(fp_out, "%.2X", getc(fp_in));
+ if ( (++n % 40) == 0 )
+ putc('\n', fp_out);
+ } /* End for */
+
+} /* End of hexdata */
+
+/*****************************************************************************/
+
+getint()
+
+{
+
+ int val;
+
+/*
+ *
+ * Reads the next four bytes into an integer and returns the value to the caller.
+ * First two bytes are probably always 0.
+ *
+ */
+
+ val = getc(fp_in);
+ val |= (getc(fp_in) << 8);
+ val |= (getc(fp_in) << 16);
+ val |= (getc(fp_in) << 24);
+
+ return(val);
+
+} /* End of getint */
+
+/*****************************************************************************/
+
+error(kind, mesg, a1, a2, a3)
+
+ int kind;
+ char *mesg;
+ unsigned a1, a2, a3;
+
+{
+
+/*
+ *
+ * Print mesg and quit if kind is FATAL.
+ *
+ */
+
+ if ( mesg != NULL && *mesg != '\0' ) {
+ fprintf(stderr, "%s: ", prog_name);
+ fprintf(stderr, mesg, a1, a2, a3);
+ putc('\n', stderr);
+ } /* End if */
+
+ if ( kind == FATAL && ignore == OFF )
+ exit(x_stat | 01);
+
+} /* End of error */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/misc/laserbar.1 b/sys/src/cmd/postscript/misc/laserbar.1
new file mode 100755
index 000000000..e845fc9bc
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/laserbar.1
@@ -0,0 +1,41 @@
+.TH LASERBAR 1
+.SH NAME
+laserbar \- produce bar codes on a PostScript laser printer
+.SH SYNOPSIS
+.B laserbar
+[\fB-r\fP rotate] [\fB-x\fP xoffset] [\fB-y\fP yoffset]
+[\fB-X\fP xscale] [\fB-Y\fP yscale] [\fB-lns\fP] string
+.SH DESCRIPTION
+.I Laserbar
+prints on the standard output the PostScript text that will produce
+(on a suitable laser printer) the \s-2CODE-39\s+2 bar code
+corresponding to
+.I string.
+The \fBr\fP option may be used to specify a rotation (in
+degrees) of the bar code.
+The \fBx\fP, \fBy\fP, \fBX\fP, and \fBY\fP options may be used to specify
+an x- or y-axis offset (in inches) or scaling factor, respectively.
+(The offset is measured from the lower left corner of the page
+to the upper left corner of the bar
+code. By default, the bar code produced is one inch high, and is scaled
+so that the narrowest elements are each 1/72-inch \- i.e., one point \- wide.)
+If the \fBl\fP option is specified, the bar code produced is labeled.
+If the \fBn\fP option is specified, the resulting PostScript text
+includes a leading \f(CWnewpath\fP command, so that the text may stand
+alone or precede any other PostScript commands.
+If the \fBs\fP option is specified, the resulting PostScript text includes
+a trailing \f(CWshowpage\fP command, so that the text may stand alone
+or follow any other PostScript commands.
+.P
+This manual page (if it appears with a bar code printed on it) was
+produced by something like the following sequence:
+.IP
+.ft CW
+laserbar -x 2.5 -y 3 -l -n ABC123xyz > tempfile
+.br
+troff -man -Tpost laserbar.1 | dpost >> tempfile
+.br
+prt -dprinter -lpostscript tempfile
+.ft P
+.SH SEE ALSO
+laserbar(3), prt(1), dpost(1), postbgi(1), postprint(1), postdmd(1), posttek(1), etc.
diff --git a/sys/src/cmd/postscript/misc/laserbar.c b/sys/src/cmd/postscript/misc/laserbar.c
new file mode 100755
index 000000000..d9f586476
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/laserbar.c
@@ -0,0 +1,166 @@
+/* laserbar -- filter to print barcodes on postscript printer */
+
+#define MAIN 1
+
+#define LABEL 01
+#define NFLAG 02
+#define SFLAG 04
+
+#include <stdio.h>
+#include <ctype.h>
+
+static int code39[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* sp ! " # $ % & ' */
+ 0304, 0, 0, 0, 0250, 0052, 0, 0,
+/* ( ) * + , - - / */
+ 0, 0, 0224, 0212, 0, 0205, 0604, 0242,
+/* 0 1 2 3 4 5 6 7 */
+ 0064, 0441, 0141, 0540, 0061, 0460, 0160, 0045,
+/* 8 9 : ; < = > ? */
+ 0444, 0144, 0, 0, 0, 0, 0, 0,
+/* @ A B C D E F G */
+ 0, 0411, 0111, 0510, 0031, 0430, 0130, 0015,
+/* H I J K L M N O */
+ 0414, 0114, 0034, 0403, 0103, 0502, 0023, 0422,
+/* P Q R S T U V W */
+ 0122, 0007, 0406, 0106, 0026, 0601, 0301, 0700,
+/* X Y Z [ \ ] ^ _ */
+ 0221, 0620, 0320, 0, 0, 0, 0, 0,
+/* ` a b c d e f g */
+ 0, 0411, 0111, 0510, 0031, 0430, 0130, 0015,
+/* h i j k l m n o */
+ 0414, 0114, 0034, 0403, 0103, 0502, 0023, 0422,
+/* p q r s t u v w */
+ 0122, 0007, 0406, 0106, 0026, 0601, 0301, 0700,
+/* x y z { | } ~ del */
+ 0221, 0620, 0320, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void barprt();
+void laserbar();
+
+#ifdef MAIN
+
+main(argc, argv)
+char **argv;
+{
+ int c, flags = 0, error = 0;
+ double rotate = 0, xoffset = 0, yoffset = 0, xscale = 1, yscale = 1;
+ extern char *optarg;
+ extern int optind;
+ extern double atof();
+ extern void exit();
+
+ while ((c = getopt(argc, argv, "r:x:y:X:Y:lns")) != EOF) {
+ switch(c) {
+ case 'r':
+ rotate = atof(optarg);
+ break;
+ case 'x':
+ xoffset = atof(optarg);
+ break;
+ case 'y':
+ yoffset = atof(optarg);
+ break;
+ case 'X':
+ xscale = atof(optarg);
+ break;
+ case 'Y':
+ yscale = atof(optarg);
+ break;
+ case 'l':
+ flags |= LABEL;
+ break;
+ case 'n':
+ flags |= NFLAG;
+ break;
+ case 's':
+ flags |= SFLAG;
+ break;
+ case '?':
+ ++error;
+ }
+ }
+ if ((argc - optind) != 1)
+ ++error;
+ if (error) {
+ (void) fprintf(stderr,
+"Usage: %s [-r rotate] [-x xoffset] [-y yoffset] [-X xscale] [-Y yscale] [-lns] string\n",
+ *argv);
+ exit(1);
+ }
+ laserbar(stdout, argv[optind], rotate, xoffset, yoffset, xscale, yscale, flags);
+ return 0;
+}
+
+#endif MAIN
+
+static int right = 0;
+
+void
+laserbar(fp, str, rotate, xoffset, yoffset, xscale, yscale, flags)
+FILE *fp;
+char *str;
+double rotate, xoffset, yoffset, xscale, yscale;
+int flags;
+{
+ xoffset *= 72.;
+ yoffset *= 72.;
+ (void) fprintf(fp, "gsave %s\n", (flags & NFLAG) ? "newpath" : "");
+ if (xoffset || yoffset)
+ (void) fprintf(fp, "%f %f moveto\n", xoffset, yoffset);
+ if (xscale != 1 || yscale != 1)
+ (void) fprintf(fp, "%f %f scale\n", xscale, yscale);
+ if (rotate)
+ (void) fprintf(fp, "%f rotate\n", rotate);
+ (void) fputs("/Helvetica findfont 16 scalefont setfont\n", fp);
+ (void) fputs("/w { 0 rmoveto gsave 3 setlinewidth 0 -72 rlineto stroke grestore } def\n", fp);
+ (void) fputs("/n { 0 rmoveto gsave 1 setlinewidth 0 -72 rlineto stroke grestore } def\n", fp);
+ (void) fputs("/l { gsave 2 -88 rmoveto show grestore } def\n", fp);
+ barprt(fp, '*', 0);
+ while (*str)
+ barprt(fp, *(str++), (flags & LABEL));
+ barprt(fp, '*', 0);
+ (void) fprintf(fp, "%sgrestore\n", (flags & SFLAG) ? "showpage " : "");
+ right = 0;
+}
+
+static void
+barprt(fp, c, label)
+FILE *fp;
+int c, label;
+{
+ int i, mask, bar, wide;
+
+ if (!(i = code39[c]))
+ return;
+ if (islower(c))
+ c = toupper(c);
+ if (label)
+ (void) fprintf(fp, "(%c) l", c);
+ else
+ (void) fputs(" ", fp);
+ for (bar = 1, mask = 0400; mask; bar = 1 - bar, mask >>= 1) {
+ wide = mask & i;
+ if (bar) {
+ if (wide)
+ ++right;
+ (void) fprintf(fp, " %d %s", right, wide ? "w" : "n");
+ right = (wide ? 2 : 1);
+ }
+ else
+ right += (wide ? 3 : 1);
+ }
+ (void) fputs("\n", fp);
+ ++right;
+}
diff --git a/sys/src/cmd/postscript/misc/lp.model b/sys/src/cmd/postscript/misc/lp.model
new file mode 100755
index 000000000..966621e39
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/lp.model
@@ -0,0 +1,66 @@
+
+# qmsps800 mac 10/22/86
+#
+LPDEST=`basename $0`
+QMS_FILE="$1"
+DATE="`date +%D`"
+TIME="`date +%T`"
+owner="$2"
+site=`uname`
+port="`/usr/bin/lpstat -v$LPDEST | sed -e 's/.*: //'`"
+filter_cmd="/usr/lbin/postscript/postio"
+filter="$filter_cmd -l $port"
+landscape="" formsperpage=""
+path=/usr/lbin/postscript
+printer=postprint
+bannerflag=ON
+prev="| $path/postreverse"
+
+for i in $5
+do
+ case "$i" in
+ L2)
+ formsperpage="-n2"
+ ;;
+ land)
+ landscape="-pland"
+ ;;
+ dpost|postprint|posttek|postbgi|postdmd|postio)
+ printer="$i"
+ ;;
+
+ postreverse)
+ prev=""
+ ;;
+ nobanner)
+ bannerflag=OFF
+ ;;
+ F*)
+ QMS_FILE="`expr $i : 'F\(.*\)'`"
+ ;;
+ esac
+done
+
+if [ -n "$filter_cmd" -a ! -x "$filter_cmd" ]
+then
+ disable -r"can't execute filter: $filter_cmd" $LPDEST
+ exit 1
+fi
+
+shift; shift; shift; shift; shift
+files="$*"
+cp /usr/spool/lp/model/banner.ps /tmp/ban.$$
+echo "($QMS_FILE) ($LPDEST) ($TIME) ($DATE) ($owner) banner" >> /tmp/ban.$$
+if [ "$printer" = "postio" ]
+ then
+ eval $filter $files 2> /dev/null
+else
+ eval $path/$printer $landscape $formsperpage $files $prev | $filter 2> /dev/null
+ fi
+if [ "$bannerflag" = "ON" ]
+ then
+ eval $filter /tmp/ban.$$ 2> /dev/null
+ fi
+rm -f /tmp/ban.$$
+exit 0
+
diff --git a/sys/src/cmd/postscript/misc/macfont.c b/sys/src/cmd/postscript/misc/macfont.c
new file mode 100755
index 000000000..dffbe3d21
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/macfont.c
@@ -0,0 +1,296 @@
+/*
+ *
+ * Program that converts Macintosh font files to a format that works on Unix
+ * systems. Essentially all the information needed came from the Adobe paper
+ * "Supporting Downloadable PostScript Fonts". To use the program type,
+ *
+ * macfont font.mac >font.unix
+ *
+ * where font.mac is the font file, exactly as it came over from a Macintosh,
+ * and font.unix is equivalent host resident font file usable on Unix systems.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#define OFF 0
+#define ON 1
+
+#define NON_FATAL 0
+#define FATAL 1
+
+#define FALSE 0
+#define TRUE 1
+
+char **argv;
+int argc;
+
+char *prog_name;
+
+int x_stat;
+int debug = OFF;
+int ignore = OFF;
+
+FILE *fp_in = stdin;
+FILE *fp_out = stdout;
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * Macintosh to Unix font converter.
+ *
+ */
+
+ argc = agc;
+ argv = agv;
+ prog_name = argv[0];
+
+ options();
+ arguments();
+ exit(x_stat);
+
+} /* End of main */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch;
+ char *names = "DI";
+
+ extern char *optarg;
+ extern int optind;
+
+/*
+ *
+ * Command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, names)) != EOF ) {
+ switch ( ch ) {
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind;
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+
+/*
+ *
+ * Everything else is an input file. No arguments or '-' means stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ conv();
+ else
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ conv();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+conv()
+
+{
+
+ int blocksize;
+ int blocktype;
+
+/*
+ *
+ * The first four bytes (in a block) are the block size, the fifth is the block
+ * type, and the sixth always appears to be NULL. Type 0 blocks are comments and
+ * are always skipped. Type 1 blocks are ASCII text, type 2 is binary data that
+ * should be converted to hex, while type 5 blocks represent the end of the font
+ * file. Commment block lengths appear to be from the first byte, while other
+ * lengths seem to be measured from block type byte (ie. the fifth byte). Type
+ * four blocks aren't used, while type 3 blocks mean an end of file indication
+ * should be sent to the printer. Haven't done anything with type 3 blocks.
+ *
+ */
+
+ while ( 1 ) {
+ blocksize = getint(fp_in);
+ blocktype = getc(fp_in);
+ getc(fp_in);
+ if ( debug == ON )
+ fprintf(stderr, "blocktype = %d, blocksize = %d\n", blocktype, blocksize);
+ switch ( blocktype ) {
+ case 0: /* comment - skip blockcount bytes */
+ fseek(fp_in, (long) blocksize - 6, 1);
+ break;
+
+ case 1:
+ asciitext(blocksize - 2);
+ break;
+
+ case 2:
+ hexdata(blocksize - 2);
+ break;
+
+ case 3:
+ case 4:
+ error(FATAL, "resource type %d not implemented", blocktype);
+ break;
+
+ case 5:
+ return;
+
+ default:
+ error(FATAL, "unknown resource type %d", blocktype);
+ } /* End switch */
+ } /* End while */
+
+} /* End of conv */
+
+/*****************************************************************************/
+
+asciitext(count)
+
+ int count; /* bytes left in the block */
+
+{
+
+ int ch;
+ int i = 0;
+
+/*
+ *
+ * Handles type 1 (ie. ASCII text) blocks. Changing carriage returns to newlines
+ * is all I've done.
+ *
+ */
+
+ for ( i = 0; i < count; i++ ) {
+ if ( (ch = getc(fp_in)) == '\r' )
+ ch = '\n';
+ putc(ch, fp_out);
+ } /* End for */
+
+} /* End of asciitext */
+
+/*****************************************************************************/
+
+hexdata(count)
+
+ int count; /* bytes left in the block */
+
+{
+
+ int i;
+ int n;
+
+/*
+ *
+ * Reads the next count bytes and converts each byte to hex. Also starts a new
+ * line every 80 hex characters.
+ *
+ */
+
+ for ( i = 0, n = 0; i < count; i++ ) {
+ fprintf(fp_out, "%.2X", getc(fp_in));
+ if ( (++n % 40) == 0 )
+ putc('\n', fp_out);
+ } /* End for */
+
+} /* End of hexdata */
+
+/*****************************************************************************/
+
+getint()
+
+{
+
+ int val;
+ int i;
+
+/*
+ *
+ * Reads the next four bytes into an integer and returns the value to the caller.
+ * First two bytes are probably always 0.
+ *
+ */
+
+ for ( i = 0, val = (getc(fp_in) & 0377); i < 3; i++ )
+ val = (val << 8) | (getc(fp_in) & 0377);
+
+ return(val);
+
+} /* End of getint */
+
+/*****************************************************************************/
+
+error(kind, mesg, a1, a2, a3)
+
+
+ int kind;
+ char *mesg;
+ unsigned a1, a2, a3;
+
+{
+
+/*
+ *
+ * Print *mesg then quit if kind is FATAL.
+ *
+ */
+
+ if ( mesg != NULL && *mesg != '\0' ) {
+ fprintf(stderr, "%s: ", prog_name);
+ fprintf(stderr, mesg, a1, a2, a3);
+ putc('\n', stderr);
+ } /* End if */
+
+ if ( kind == FATAL && ignore == OFF )
+ exit(x_stat | 01);
+
+} /* End of error */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/misc/pscrypt.c b/sys/src/cmd/postscript/misc/pscrypt.c
new file mode 100755
index 000000000..316be5887
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/pscrypt.c
@@ -0,0 +1,333 @@
+/*
+ *
+ * Adobe's encryption/decryption algorithm for eexec and show. Runs in
+ * eexec mode unless told otherwise. Use,
+ *
+ * pscrypt file.cypher > file.clear
+ *
+ * to decrypt eexec input. Assumes file.cypher is hex with the key as the
+ * first four bytes, and writes file.clear as binary (omitting the key).
+ * Use
+ *
+ * pscrypt -e12ab34ef file.clear >file.cypher
+ *
+ * to encrypt file.clear (for eexec) using 12ab34ef as the key. Input is
+ * binary and output is hex. The key must be given as a hex number. Use
+ * -sshow to encrypt or decrypt a CharString or Subr,
+ *
+ * pscrypt -sshow file.cypher > file.clear
+ *
+ * Use -b or -x to read binary or hex input, and -B or -X to output binary
+ * or hex.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define ENCRYPT 0
+#define DECRYPT 1
+
+#define NOTSET -1
+#define BINARY 0
+#define HEX 1
+#define LINELENGTH 40
+
+#define CHARSTRING 4330
+#define EEXEC 55665
+#define MAGIC1 52845
+#define MAGIC2 22719
+
+int argc;
+char **argv;
+
+int mode = DECRYPT;
+int input = NOTSET;
+int output = NOTSET;
+int outoffset = NOTSET;
+int inoffset = NOTSET;
+
+int cryptkey = 0; /* encryption key set with -e */
+int linelength = LINELENGTH; /* only for hex output */
+int lastchar = 0;
+
+unsigned long seed = EEXEC;
+unsigned long key;
+
+FILE *fp_in = stdin;
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * Implementation of the encryption/decryption used by eexec and show.
+ *
+ */
+
+ argc = agc;
+ argv = agv;
+
+ options();
+ initialize();
+ arguments();
+
+ exit(0);
+
+} /* End of main */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch;
+ char *names = "bde:l:os:xBSX";
+
+ extern char *optarg;
+ extern int optind;
+
+/*
+ *
+ * Command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, names)) != EOF )
+ switch ( ch ) {
+ case 'b': /* binary input */
+ input = BINARY;
+ break;
+
+ case 'd': /* decrypt */
+ mode = DECRYPT;
+ break;
+
+ case 'e': /* encrypt */
+ mode = ENCRYPT;
+ if ( *optarg == '0' && *optarg == 'x' )
+ optarg += 2;
+ sscanf(optarg, "%8x", &cryptkey);
+ break;
+
+ case 'l': /* line length hex output */
+ linelength = atoi(optarg);
+ break;
+
+ case 'o': /* output all bytes - debugging */
+ outoffset = 0;
+ break;
+
+ case 's': /* seed */
+ if ( *optarg == 'e' )
+ seed = EEXEC;
+ else if ( *optarg == 's' )
+ seed = CHARSTRING;
+ else if ( *optarg == '0' && *(optarg+1) == 'x' )
+ sscanf(optarg+2, "%x", &seed);
+ else if ( *optarg == '0' )
+ sscanf(optarg, "%o", &seed);
+ else sscanf(optarg, "%d", &seed);
+ break;
+
+ case 'x': /* hex input */
+ input = HEX;
+ break;
+
+ case 'B': /* binary output */
+ output = BINARY;
+ break;
+
+ case 'X': /* hex output */
+ output = HEX;
+ break;
+
+ case '?': /* don't understand the option */
+ fprintf(stderr, "bad option -%c\n", ch);
+ exit(1);
+ break;
+
+ default: /* don't know what to do for ch */
+ fprintf(stderr, "missing case for option -%c\n", ch);
+ exit(1);
+ break;
+ } /* End switch */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+initialize()
+
+{
+
+/*
+ *
+ * Initialization that has to be done after the options.
+ *
+ */
+
+ key = seed;
+
+ if ( mode == DECRYPT ) {
+ input = (input == NOTSET) ? HEX : input;
+ output = (output == NOTSET) ? BINARY : output;
+ inoffset = (inoffset == NOTSET) ? 0 : inoffset;
+ outoffset = (outoffset == NOTSET) ? -4 : outoffset;
+ } else {
+ input = (input == NOTSET) ? BINARY : input;
+ output = (output == NOTSET) ? HEX : output;
+ inoffset = (inoffset == NOTSET) ? 4 : inoffset;
+ outoffset = (outoffset == NOTSET) ? 0 : outoffset;
+ } /* End else */
+
+ if ( linelength <= 0 )
+ linelength = LINELENGTH;
+
+} /* End of initialize */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Everything left is an input file. No arguments or '-' means stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ crypt();
+ else
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL ) {
+ fprintf(stderr, "can't open %s\n", *argv);
+ exit(1);
+ } /* End if */
+ crypt();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+crypt()
+
+{
+
+ unsigned int cypher;
+ unsigned int clear;
+
+/*
+ *
+ * Runs the encryption/decryption algorithm.
+ *
+ */
+
+ while ( lastchar != EOF ) {
+ cypher = nextbyte();
+ clear = ((key >> 8) ^ cypher) & 0xFF;
+ key = (key + (mode == DECRYPT ? cypher : clear)) * MAGIC1 + MAGIC2;
+ if ( ++outoffset > 0 && lastchar != EOF ) {
+ if ( output == HEX ) {
+ printf("%.2X", clear);
+ if ( linelength > 0 && (outoffset % linelength) == 0 )
+ putchar('\n');
+ } else putchar(clear);
+ } /* End if */
+ } /* End while */
+
+} /* End of crypt */
+
+/*****************************************************************************/
+
+nextbyte()
+
+{
+
+ int val = EOF;
+
+/*
+ *
+ * Returns the next byte. Uses cryptkey (i.e. what followed -e) while inoffset is
+ * positive, otherwise reads (hex or binary) from fp_in.
+ *
+ */
+
+ if ( inoffset-- > 0 )
+ val = (cryptkey >> (inoffset*8)) & 0xFF;
+ else if ( input == HEX ) {
+ if ( (val = nexthexchar()) != EOF )
+ val = (val << 4) | nexthexchar();
+ } else if ( input == BINARY )
+ val = Getc(fp_in);
+
+ return(val);
+
+} /* End of nextbyte */
+
+/*****************************************************************************/
+
+nexthexchar()
+
+{
+
+ int ch;
+
+/*
+ *
+ * Reads the next hex character.
+ *
+ */
+
+ while ( (ch = Getc(fp_in)) != EOF && ! isxdigit(ch) ) ;
+
+ if ( isdigit(ch) )
+ ch -= '0';
+ else if ( isupper(ch) )
+ ch -= 'A' - 10;
+ else if ( islower(ch) )
+ ch -= 'a' - 10;
+
+ return(ch);
+
+} /* End of nexthexchar */
+
+/*****************************************************************************/
+
+Getc(fp)
+
+ FILE *fp;
+
+{
+
+/*
+ *
+ * Reads the next byte from *fp, sets lastchar, and returns the character.
+ *
+ */
+
+ return(lastchar = getc(fp));
+
+} /* End of Getc */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/misc/setbaud.ps b/sys/src/cmd/postscript/misc/setbaud.ps
new file mode 100755
index 000000000..93148d0c4
--- /dev/null
+++ b/sys/src/cmd/postscript/misc/setbaud.ps
@@ -0,0 +1,9 @@
+%
+% Sets baud rate to 9600, options to 0 assuming the password is 0.
+%
+
+serverdict begin
+ 0 exitserver
+ statusdict begin
+ 25 9600 0 setsccbatch
+ end
diff --git a/sys/src/cmd/postscript/mkfile b/sys/src/cmd/postscript/mkfile
new file mode 100755
index 000000000..7b9f4b3c0
--- /dev/null
+++ b/sys/src/cmd/postscript/mkfile
@@ -0,0 +1,50 @@
+</$objtype/mkfile
+
+<config
+
+#
+# $(TARGETS) is the default list of things built by make.
+#
+# buildtables
+
+TARGETS=common\
+ cropmarks\
+ download\
+ grabit\
+ hardcopy\
+ mpictures\
+ postgif\
+ postprint\
+ postreverse\
+ posttek\
+ printfont\
+ psencoding\
+ psfiles\
+ g3p9bit\
+ p9bitpost\
+ tcpostio\
+ text2post\
+ tr2post
+
+# UNIX remnants
+# trofftable\
+# picpack\
+# postbgi\
+# postdaisy\
+# postdmd\
+# postmd
+# postio
+
+default:V: all
+
+$TARGETS:V:
+ cd $target
+ mk all
+
+&:VQ:
+ test -d $POSTBIN || mkdir $POSTBIN
+ for (i in $TARGETS) @{
+ echo ---- Making $target in $i ----;
+ cd $i;
+ mk $target
+ }
diff --git a/sys/src/cmd/postscript/mpictures/mkfile b/sys/src/cmd/postscript/mpictures/mkfile
new file mode 100755
index 000000000..cc4d48c24
--- /dev/null
+++ b/sys/src/cmd/postscript/mpictures/mkfile
@@ -0,0 +1,18 @@
+</$objtype/mkfile
+
+<../config
+
+TMACDIR=/sys/lib/tmac
+
+all:V: tmac.pictures
+
+install installall:V: $TMACDIR/tmac.pictures
+
+clean nuke:V:
+ rm -f tmac.pictures
+
+$TMACDIR/tmac.pictures: tmac.pictures
+ cp $prereq $target
+
+tmac.pictures : pictures.sr
+ cp $prereq $target
diff --git a/sys/src/cmd/postscript/mpictures/pictures.sr b/sys/src/cmd/postscript/mpictures/pictures.sr
new file mode 100755
index 000000000..6ff4f96e7
--- /dev/null
+++ b/sys/src/cmd/postscript/mpictures/pictures.sr
@@ -0,0 +1,154 @@
+.de PI \" Picture Include
+. \" $1=file(page) $2=height,width,yoff,xoff $3=flags
+. \" Height, width, xoff, and yoff are for the frame, flags is for the
+. \" picture. Default dimensions are inches.
+\\X'PI:\\n(.o:\\n(.i:\\n(.l:\\n(.t:\\$1:\\$2:\\$3:'\\c
+..
+.nr FT 0
+.de BP \" Begin a Picture
+. \"
+. \" $1=file(page) $2=height $3=width $4=position $5=offset $6=flags $7=label
+. \"
+. \" Height, width, position, and offset are for the frame, flags is for
+. \" the picture. The postion flag should be l, c, r, or "". Omitting the
+. \" position argument is also allowed for compatibility with the original
+. \" version of the macro. In that case offset is taken relative to your
+. \" left margin.
+.if \\n(FT>1 .EP
+. \" Need these when we switch environments.
+.nr Ov \\n(.v
+.nr Oi \\n(.i
+.nr Ol \\n(.l
+. \" Remember the arguments - sometimes as both a string and number register.
+.nr $2 \\$2i
+.nr $3 \\$3i
+.nr $4 \\$4i
+.ds $4 \\$4
+.nr $5 \\$5i
+.ds $5 \\$6
+.ds $6 \\$7
+. \" Accept a few unadvertised position flags.
+.if '\\*($4'L' .ds $4 l
+.if '\\*($4'C' .ds $4 c
+.if '\\*($4'R' .ds $4 r
+. \" A null with more than three arguments means l.
+.if \\n(.$>3 .if '\\*($4'' .ds $4 l
+. \" Default frame dimensions if missing, zero, or null.
+.if !\\n($2>0 .nr $2 3.0i
+.if !\\n($3>0 .nr $3 \\n(.lu-\\n(.iu-\\n($4u
+.if !\\n($3>0 .nr $3 \\n($2u
+. \" Figure out the offset that will be used the rest of the way.
+.if '\\*($4'l' .nr $4 \\n($5u
+.if '\\*($4'c' .nr $4 (\\n(.lu-\\n(.iu-\\n($3u)/2u+\\n($5u
+.if '\\*($4'r' .nr $4 \\n(.lu-\\n(.iu-\\n($3u+\\n($5u
+. \" If we haven't recognized the position flag assume it wasn't given and
+. \" treat argument four as an offset from the left.
+.if !'\\*($4'l' .if !'\\*($4'c' .if !'\\*($4'r' .ds $5 \\$5
+.if !'\\*($4'l' .if !'\\*($4'c' .if !'\\*($4'r' .ds $6 \\$6
+. \" Set increments for line length and indent.
+.nr Ii \\n($3u+\\n($4u+.1i
+.nr Il \\n(.lu-\\n(.iu-\\n($4u+.1i
+. \" Set the frame type to one of:
+. \" 0 - frame is as wide as a line of text - skip over it.
+. \" 1 - fits in left or right margins
+. \" 2 - fill with text on the right
+. \" 3 - on the left
+. \" 4 - or on both sides of the frame
+. \" 5 - only set in EP if FT was 4 and now filling on the right.
+. \" Assume the frame is as wide as a line of text, then check dimensions
+. \" to see what's really true. The order of these tests is important!
+.nr FT 0
+.if \\n($4u>1.0i .nr FT 3
+.if \\n($4u+\\n(.iu>=\\n(.lu .nr FT 1
+.if \\n($3u+\\n($4u+\\n(.iu+1.0i<\\n(.lu .nr FT 2
+.if \\n($3u+\\n($4u<=0 .nr FT 1
+.if \\n(FT=2 .if \\n($4u>1.0i .nr FT 4
+. \" Ask for some vertical space - labeled pictures need a little extra,
+. \" margin pictures a little less.
+.if \\n(FT=1 .if '\\*($6'' .ne \\n($2u
+.if \\n(FT=1 .if !'\\*($6'' .ne \\n($2u+2v
+.if !\\n(FT=1 .if '\\*($6'' .ne \\n($2u+3v
+.if !\\n(FT=1 .if !'\\*($6'' .ne \\n($2u+5v
+. \" Save our place, draw the picture, label it, and return. Need precise
+. \" control of when \X'...' is put out - thus the new environment.
+.mk Oh
+.ev 1
+.in \\n(Oiu
+.ll \\n(Olu
+.vs \\n(Ovu
+.if \\n(FT=1 .sp -1v
+.if \\n(FT=1 .PI \\$1 \\n($2u,\\n($3u,\\n(.vu,\\n($4u t\\*($5
+.if !\\n(FT=1 .PI \\$1 \\n($2u,\\n($3u,\\n(.vu,\\n($4u \\*($5
+.in
+.ll
+.vs
+.ev
+.lt \\n($3u
+.tl \(ts\(ts\\h'\\n($4u+\\n(.iu'\\v'\\n($2u+1.5v'\\*($6\\v'-\\n($2u-1.5v'\\h'-\\n($4u-\\n(.iu'\(ts\(ts
+.lt
+'sp |\\n(Ohu
+. \" Figure out what to do with the text that follows.
+.if !'\\*($6'' .nr $2 +2v
+.if \\n(FT=0 .sp \\n($2u+2v
+.if \\n(FT=1 .nr FT 0
+.if \\n(FT=2 'in +\\n(Iiu
+.if \\n(FT>2 .ll -\\n(Ilu
+.if \\n(FT>1 .di BB
+.if \\n(FT>1 .dt \\n($2u+2v+1u EP
+. \" Clean things up.
+.rr $2
+.rr $3
+.rr $4
+.rm $4
+.rr $5
+.rm $5
+.rm $6
+.rr Oh
+.rr Oi
+.rr Ol
+.rr Ov
+.if \\n(FT=0 .EP
+..
+.de EP \" End the Picture - Normally called from a trap, although it can be used
+. \" on its own to mark the end of a picture.
+.nr Ot 0
+.if \\n(.tu<\\n(.pu .nr Ot \\n(.tu
+.if \\n(Ot>0 .if \\n(FT=4 .nr FT 3
+.if \\n(FT<2 .nr Ot 0
+.if \\n(Ot>0 .br
+.if \\n(FT=5 .nr Ot 0
+.if \\n(FT>1 \{\
+. ev 1
+. eo
+. br
+. di
+. nf
+. in 0
+. BB
+. in
+. fi
+. ec
+. ev
+. rm BB\}
+.if \\n(FT=5 \{\
+. nr FT 2
+' sp |\\n(Nhu+1v\}
+.if \\n(FT=4 \{\
+. mk Nh
+. nr Nh -1v
+. nr FT 5
+' sp -\\n(dnu+1v
+' in +\\n(Iiu
+. ll +\\n(Ilu
+. di BB
+. dt \\n(dnu-2v+1u EP\}
+.if \\n(FT=2 'in -\\n(Iiu
+.if \\n(FT=3 .ll +\\n(Ilu
+.if \\n(FT<4 .nr FT 0
+.if \\n(Ot>0 .sp \\n(Otu
+.rr Ot
+.if \\n(FT=0 \{\
+. rr Nh
+. rr Ii
+. rr Il\}
+..
diff --git a/sys/src/cmd/postscript/p9bitpost/mkfile b/sys/src/cmd/postscript/p9bitpost/mkfile
new file mode 100755
index 000000000..f99668432
--- /dev/null
+++ b/sys/src/cmd/postscript/p9bitpost/mkfile
@@ -0,0 +1,17 @@
+</$objtype/mkfile
+
+
+<../config
+
+COMMONDIR=../common
+
+TARG=p9bitpost
+OFILES=pslib.$O\
+ p9bitpost.$O\
+
+BIN=$POSTBIN
+
+LIB=/$objtype/lib/libmemdraw.a
+< /sys/src/cmd/mkone
+
+CFLAGS=-w -I$COMMONDIR
diff --git a/sys/src/cmd/postscript/p9bitpost/p9bitpost.c b/sys/src/cmd/postscript/p9bitpost/p9bitpost.c
new file mode 100755
index 000000000..2816c5139
--- /dev/null
+++ b/sys/src/cmd/postscript/p9bitpost/p9bitpost.c
@@ -0,0 +1,105 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "pslib.h"
+
+#define HDLEN 60
+
+int dpi = -1;
+int debug = 0;
+int landscape = 0;
+char *file = "<stdin>";
+
+int paperlength = 11*72;
+int paperwidth = 612; /* 8.5*72 */
+
+void
+error(char *s)
+{
+ fprint(2, "p9bitpost: can't %s file %s: %r\n", s, file);
+ exits("error");
+}
+
+void
+main(int argc, char *argv[]) {
+ int i, fd = 0;
+ double xmag = 1.0, ymag = 1.0;
+ char *optstr, *Patch;
+ Memimage *memimage;
+
+ Patch = nil;
+ for (i=1; i<argc; i++) {
+ if (*argv[i] != '-') break;
+ switch(argv[i][1]) {
+ case 'b':
+ if (argv[i][2] == '\0')
+ dpi = atoi(argv[++i]);
+ else
+ dpi = atoi(&(argv[i][2]));
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'm':
+ if (argv[i][2] == '\0')
+ optstr = argv[++i];
+ else
+ optstr = &(argv[i][2]);
+ if ((optstr=strtok(optstr, " ,")) != 0)
+ xmag = ymag = atof(optstr);
+ if ((optstr=strtok(0, " ,")) != 0)
+ ymag = atof(optstr);
+ break;
+ case 'L':
+ landscape = 1;
+ break;
+ case 'P':
+ if (argv[i][2] == '\0')
+ Patch = argv[++i];
+ else
+ Patch = &(argv[i][2]);
+ break;
+ case 'p':
+ optstr = argv[++i];
+ if(optstr == nil)
+ goto Usage;
+ paperlength = 72*atof(optstr);
+ optstr = argv[++i];
+ if(optstr == nil)
+ goto Usage;
+ paperwidth = 72*atof(optstr);
+ if(paperlength < 72 || paperwidth < 72)
+ goto Usage;
+ break;
+ default:
+ Usage:
+ fprint(2, "usage: %s [-b dpi] [-m magnification] [-L] [-P postscript_patch_string] [-p paperlength paperwidth (in inches)] inputfile\n", argv[0]);
+ exits("usage");
+ }
+ }
+
+ if (i < argc) {
+ file = argv[i];
+ fd = open(file, OREAD);
+ if (fd < 0)
+ error("open");
+ }
+
+ memimageinit();
+ memimage = readmemimage(fd);
+ if(memimage == nil)
+ error("alloc memory for");
+
+ psinit(0, 0);
+ if(xmag != 1.0)
+ psopt("xmagnification", &xmag);
+ if(ymag != 1.0)
+ psopt("ymagnification", &ymag);
+ if(landscape)
+ psopt("landscape", &landscape);
+ if(Patch)
+ psopt("Patch", &Patch);
+ image2psfile(1, memimage, dpi);
+ exits("");
+}
diff --git a/sys/src/cmd/postscript/p9bitpost/pslib.c b/sys/src/cmd/postscript/p9bitpost/pslib.c
new file mode 100755
index 000000000..afe2e7646
--- /dev/null
+++ b/sys/src/cmd/postscript/p9bitpost/pslib.c
@@ -0,0 +1,813 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+#include "pslib.h"
+/* implement PsLib;
+/*
+/* include "sys.m";
+/* sys: Sys;
+/*
+/* include "draw.m";
+/* draw : Draw;
+/* Image, Display,Rect,Point : import draw;
+/*
+/* include "bufio.m";
+/* bufmod : Bufio;
+/*
+/* include "tk.m";
+/* tk: Tk;
+/* Toplevel: import tk;
+/*
+/* Iobuf : import bufmod;
+/*
+/* include "string.m";
+/* str : String;
+/*
+/* include "daytime.m";
+/* time : Daytime;
+/*
+/* include "pslib.m";
+/*
+/* ASCII,RUNE,IMAGE : con iota;
+/*
+*/
+struct iteminfo {
+ int itype;
+ int offset; /* offset from the start of line. */
+ int width; /* width.... */
+ int ascent; /* ascent of the item */
+ int font; /* font */
+ int line; /* line its on */
+ char *buf;
+};
+
+struct lineinfo {
+ int xorg;
+ int yorg;
+ int width;
+ int height;
+ int ascent;
+};
+
+
+/* font_arr := array[256] of {* => (-1,"")};
+/* remap := array[20] of (string,string);
+/*
+/* PXPI : con 100;
+/* PTPI : con 100;
+/*
+*/
+char *noinit = "pslib not properly initialized";
+/*
+*/
+static int boxes;
+static int debug;
+static int totitems;
+static int totlines;
+static int curfont;
+static char *def_font;
+static int def_font_type;
+static int curfonttype;
+static int pagestart;
+static int started;
+
+static int bps;
+static int width;
+static int height;
+static int iwidth;
+static int iheight;
+static int xstart;
+static int ystart;
+static double xmagnification = 1.0, ymagnification = 1.0;
+static int rotation = 0;
+static int landscape = 0;
+static char *Patch = nil;
+
+/* ctxt : ref Draw->Context;
+/* t : ref Toplevel;
+*/
+char*
+psinit(int box, int deb) { /* d: ref Toplevel, */
+/* t=d; */
+ debug = deb;
+ totlines=0;
+ totitems=0;
+ pagestart=0;
+ boxes=box; /* #box; */
+ curfont=0;
+/* e := loadfonts();
+/* if (e != "")
+/* return e;
+*/
+ started=1;
+ return "";
+}
+
+/* stats() : (int,int,int)
+/* {
+/* return (totitems,totlines,curfont);
+/* }
+/*
+/* loadfonts() : string
+/* {
+/* input : string;
+/* iob:=bufmod->open("/fonts/psrename",bufmod->OREAD);
+/* if (iob==nil)
+/* return sys->sprint("can't open /fonts/psrename: %r");
+/* i:=0;
+/* while((input=iob.gets('\n'))!=nil){
+/* (tkfont,psfont):=str->splitl(input," ");
+/* psfont=psfont[1:len psfont -1];
+/* remap[i]=(tkfont,psfont);
+/* i++;
+/* }
+/* return "";
+/* }
+/*
+*/
+static char *username;
+
+int
+preamble(Biobuf *ioutb, Rectangle bb) {
+
+ if (!started) return 1;
+ username = getuser();
+ if(bb.max.x == 0 && bb.max.y == 0) {
+ bb.max.x = 612;
+ bb.max.y = 792;
+ }
+ Bprint(ioutb, "%%!PS-Adobe-3.0\n");
+ Bprint(ioutb, "%%%%Creator: PsLib 1.0 (%s)\n",username);
+ Bprint(ioutb, "%%%%CreationDate: %s", ctime(time(nil)));
+ Bprint(ioutb, "%%%%Pages: (atend) \n");
+ Bprint(ioutb, "%%%%BoundingBox: %d %d %d %d\n", bb.min.x, bb.min.y, bb.max.x, bb.max.y);
+ Bprint(ioutb, "%%%%EndComments\n");
+ Bprint(ioutb, "%%%%BeginProlog\n");
+ Bprint(ioutb, "/doimage {\n");
+ Bprint(ioutb, "/grey exch def\n");
+ Bprint(ioutb, "/bps exch def\n");
+ Bprint(ioutb, "/width exch def\n");
+ Bprint(ioutb, "/height exch def\n");
+ Bprint(ioutb, "/xstart exch def\n");
+ Bprint(ioutb, "/ystart exch def\n");
+ Bprint(ioutb, "/iwidth exch def\n");
+ Bprint(ioutb, "/ascent exch def\n");
+ Bprint(ioutb, "/iheight exch def\n");
+ Bprint(ioutb, "gsave\n");
+ if(boxes)
+ Bprint(ioutb, "xstart ystart iwidth iheight rectstroke\n");
+/* # if bps==8, use inferno colormap; else (bps < 8) it's grayscale or true color */
+ Bprint(ioutb, "bps 8 eq grey false eq and {\n");
+ Bprint(ioutb, " [/Indexed /DeviceRGB 255 <\n");
+ Bprint(ioutb, " ffffff ffffaa ffff55 ffff00 ffaaff ffaaaa ffaa55 ffaa00 ff55ff ff55aa ff5555 ff5500\n");
+ Bprint(ioutb, " ff00ff ff00aa ff0055 ff0000 ee0000 eeeeee eeee9e eeee4f eeee00 ee9eee ee9e9e ee9e4f\n");
+ Bprint(ioutb, " ee9e00 ee4fee ee4f9e ee4f4f ee4f00 ee00ee ee009e ee004f dd0049 dd0000 dddddd dddd93\n");
+ Bprint(ioutb, " dddd49 dddd00 dd93dd dd9393 dd9349 dd9300 dd49dd dd4993 dd4949 dd4900 dd00dd dd0093\n");
+ Bprint(ioutb, " cc0088 cc0044 cc0000 cccccc cccc88 cccc44 cccc00 cc88cc cc8888 cc8844 cc8800 cc44cc\n");
+ Bprint(ioutb, " cc4488 cc4444 cc4400 cc00cc aaffaa aaff55 aaff00 aaaaff bbbbbb bbbb5d bbbb00 aa55ff\n");
+ Bprint(ioutb, " bb5dbb bb5d5d bb5d00 aa00ff bb00bb bb005d bb0000 aaffff 9eeeee 9eee9e 9eee4f 9eee00\n");
+ Bprint(ioutb, " 9e9eee aaaaaa aaaa55 aaaa00 9e4fee aa55aa aa5555 aa5500 9e00ee aa00aa aa0055 aa0000\n");
+ Bprint(ioutb, " 990000 93dddd 93dd93 93dd49 93dd00 9393dd 999999 99994c 999900 9349dd 994c99 994c4c\n");
+ Bprint(ioutb, " 994c00 9300dd 990099 99004c 880044 880000 88cccc 88cc88 88cc44 88cc00 8888cc 888888\n");
+ Bprint(ioutb, " 888844 888800 8844cc 884488 884444 884400 8800cc 880088 55ff55 55ff00 55aaff 5dbbbb\n");
+ Bprint(ioutb, " 5dbb5d 5dbb00 5555ff 5d5dbb 777777 777700 5500ff 5d00bb 770077 770000 55ffff 55ffaa\n");
+ Bprint(ioutb, " 4fee9e 4fee4f 4fee00 4f9eee 55aaaa 55aa55 55aa00 4f4fee 5555aa 666666 666600 4f00ee\n");
+ Bprint(ioutb, " 5500aa 660066 660000 4feeee 49dddd 49dd93 49dd49 49dd00 4993dd 4c9999 4c994c 4c9900\n");
+ Bprint(ioutb, " 4949dd 4c4c99 555555 555500 4900dd 4c0099 550055 550000 440000 44cccc 44cc88 44cc44\n");
+ Bprint(ioutb, " 44cc00 4488cc 448888 448844 448800 4444cc 444488 444444 444400 4400cc 440088 440044\n");
+ Bprint(ioutb, " 00ff00 00aaff 00bbbb 00bb5d 00bb00 0055ff 005dbb 007777 007700 0000ff 0000bb 000077\n");
+ Bprint(ioutb, " 333333 00ffff 00ffaa 00ff55 00ee4f 00ee00 009eee 00aaaa 00aa55 00aa00 004fee 0055aa\n");
+ Bprint(ioutb, " 006666 006600 0000ee 0000aa 000066 222222 00eeee 00ee9e 00dd93 00dd49 00dd00 0093dd\n");
+ Bprint(ioutb, " 009999 00994c 009900 0049dd 004c99 005555 005500 0000dd 000099 000055 111111 00dddd\n");
+ Bprint(ioutb, " 00cccc 00cc88 00cc44 00cc00 0088cc 008888 008844 008800 0044cc 004488 004444 004400\n");
+ Bprint(ioutb, " 0000cc 000088 000044 000000>\n");
+ Bprint(ioutb, " ] setcolorspace\n");
+ Bprint(ioutb, " /decodemat [0 255] def\n");
+ Bprint(ioutb, "}\n");
+/* # else, bps != 8 */
+ Bprint(ioutb, "{\n");
+/* is it greyscale or is it 24-bit color? */
+ Bprint(ioutb, " grey true eq {\n");
+ Bprint(ioutb, " [/DeviceGray] setcolorspace\n");
+ Bprint(ioutb, " /decodemat [1 0] def\n");
+ Bprint(ioutb, " }\n");
+ Bprint(ioutb, " {\n");
+/* must be color */
+ Bprint(ioutb, " [/DeviceRGB] setcolorspace\n");
+ Bprint(ioutb, " /bps 8 def\n");
+ Bprint(ioutb, " /decodemat [1 0 1 0 1 0] def\n");
+ Bprint(ioutb, " }\n");
+ Bprint(ioutb, " ifelse\n");
+ Bprint(ioutb, "}\n");
+ Bprint(ioutb, "ifelse\n");
+ Bprint(ioutb, "/xmagnification %g def\n", xmagnification);
+ Bprint(ioutb, "/ymagnification %g def\n", ymagnification);
+ Bprint(ioutb, "/rotation %d def\n", rotation);
+ Bprint(ioutb, "xstart ystart translate rotation rotate\n");
+ Bprint(ioutb, "iwidth xmagnification mul iheight ymagnification mul scale\n");
+ Bprint(ioutb, "<<\n");
+ Bprint(ioutb, " /ImageType 1\n");
+ Bprint(ioutb, " /Width width \n");
+ Bprint(ioutb, " /Height height \n");
+ Bprint(ioutb, " /BitsPerComponent bps %% bits/sample\n");
+ Bprint(ioutb, " /Decode decodemat %% Brazil/Inferno cmap or DeviceGray value\n");
+ Bprint(ioutb, " /ImageMatrix [width 0 0 height neg 0 height]\n");
+ Bprint(ioutb, " /DataSource currentfile /ASCII85Decode filter\n");
+ Bprint(ioutb, ">> \n");
+ Bprint(ioutb, "image\n");
+ Bprint(ioutb, "grestore\n");
+ Bprint(ioutb, "} def\n");
+ Bprint(ioutb, "%%%%EndProlog\n");
+ if (Patch != nil)
+ Bprint(ioutb, "%s\n", Patch);
+ return 0;
+}
+
+int
+trailer(Biobuf *ioutb ,int pages) {
+ if(!started)
+ return 1;
+ Bprint(ioutb, "%%%%Trailer\n%%%%Pages: %d\n%%%%EOF\n", pages);
+ return 0;
+}
+
+void
+printnewpage(int pagenum, int end, Biobuf *ioutb)
+{
+ if (!started) return;
+ if (end){
+/* # bounding box */
+ if (boxes){
+ Bprint(ioutb, "18 18 moveto 594 18 lineto 594 774 lineto 18 774 lineto closepath stroke\n");
+ }
+ Bprint(ioutb, "showpage\n%%%%EndPage %d %d\n", pagenum, pagenum);
+ } else
+ Bprint(ioutb, "%%%%Page: %d %d\n", pagenum, pagenum);
+}
+
+/* int
+/* printimage(FILE *ioutb, struct lineinfo line, struct iteminfo imag) {
+/* int RM;
+/*
+/* RM=612-18;
+/* class:=tk->cmd(t,"winfo class "+imag.buf);
+/* #sys->print("Looking for [%s] of type [%s]\n",imag.buf,class);
+/* if (line.xorg+imag.offset+imag.width>RM)
+/* imag.width=RM-line.xorg-imag.offset;
+/* case class {
+/* "button" or "menubutton" =>
+/* # try to get the text out and print it....
+/* ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
+/* line.yorg));
+/* msg:=tk->cmd(t,sys->sprint("%s cget -text",imag.buf));
+/* ft:=tk->cmd(t,sys->sprint("%s cget -font",imag.buf));
+/* sys->print("font is [%s]\n",ft);
+/* ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
+/* line.xorg+imag.offset,line.yorg,imag.width,
+/* line.height));
+/* return (class,msg);
+/* "label" =>
+/* (im,im2,err) := tk->imageget(t,imag.buf);
+/* if (im!=nil){
+/* bps := 1<<im.ldepth;
+/* ioutb.puts(sys->sprint("%d %d %d %d %d %d %d %d doimage\n",
+/* im.r.dy(),line.ascent,im.r.dx(),line.yorg,
+/* line.xorg+imag.offset,im.r.dy(), im.r.dx(), bps));
+/* imagebits(ioutb,im);
+/* }
+/* return (class,"");
+/* "entry" =>
+/* ioutb.puts(sys->sprint("%d %d moveto\n",line.xorg+imag.offset,
+/* line.yorg));
+/* ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",
+/* line.xorg+imag.offset,line.yorg,imag.width,
+/* line.height));
+/* return (class,"");
+/* * =>
+/* sys->print("Unhandled class [%s]\n",class);
+/* return (class,"Error");
+/*
+/* }
+/* return ("","");
+/* }
+/*
+/* printline(ioutb: ref Iobuf,line : lineinfo,items : array of iteminfo)
+/* {
+/* xstart:=line.xorg;
+/* wid:=xstart;
+/* # items
+/* if (len items == 0) return;
+/* for(j:=0;j<len items;j++){
+/* msg:="";
+/* class:="";
+/* if (items[j].itype==IMAGE)
+/* (class,msg)=printimage(ioutb,line,items[j]);
+/* if (items[j].itype!=IMAGE || class=="button"|| class=="menubutton"){
+/* setfont(ioutb,items[j].font);
+/* if (msg!=""){
+/* # position the text in the center of the label
+/* # moveto curpoint
+/* # (msg) stringwidth pop xstart sub 2 div
+/* ioutb.puts(sys->sprint("%d %d moveto\n",xstart+items[j].offset,
+/* line.yorg+line.height-line.ascent));
+/* ioutb.puts(sys->sprint("(%s) dup stringwidth pop 2 div",
+/* msg));
+/* ioutb.puts(" 0 rmoveto show\n");
+/* }
+/* else {
+/* ioutb.puts(sys->sprint("%d %d moveto\n",
+/* xstart+items[j].offset,line.yorg+line.height
+/* -line.ascent));
+/* ioutb.puts(sys->sprint("(%s) show\n",items[j].buf));
+/* }
+/* }
+/* wid=xstart+items[j].offset+items[j].width;
+/* }
+/* if (boxes)
+/* ioutb.puts(sys->sprint("%d %d %d %d rectstroke\n",line.xorg,line.yorg,
+/* wid,line.height));
+/* }
+/*
+/* setfont(ioutb: ref Iobuf,font : int){
+/* ftype : int;
+/* fname : string;
+/* if ((curfonttype&font)!=curfonttype){
+/* for(f:=0;f<curfont;f++){
+/* (ftype,fname)=font_arr[f];
+/* if ((ftype&font)==ftype)
+/* break;
+/* }
+/* if (f==curfont){
+/* fname=def_font;
+/* ftype=def_font_type;
+/* }
+/* ioutb.puts(sys->sprint("%s setfont\n",fname));
+/* curfonttype=ftype;
+/* }
+/* }
+/*
+/* parseTkline(ioutb: ref Iobuf,input : string) : string
+/* {
+/* if (!started) return noinit;
+/* thisline : lineinfo;
+/* PS:=792-18-18; # page size in points
+/* TM:=792-18; # top margin in points
+/* LM:=18; # left margin 1/4 in. in
+/* BM:=18; # bottom margin 1/4 in. in
+/* x : int;
+/* (x,input)=str->toint(input,10);
+/* thisline.xorg=(x*PTPI)/PXPI;
+/* (x,input)=str->toint(input,10);
+/* thisline.yorg=(x*PTPI)/PXPI;
+/* (x,input)=str->toint(input,10);
+/* thisline.width=(x*PTPI)/PXPI;
+/* (x,input)=str->toint(input,10);
+/* thisline.height=(x*PTPI)/PXPI;
+/* (x,input)=str->toint(input,10);
+/* thisline.ascent=(x*PTPI)/PXPI;
+/* (x,input)=str->toint(input,10);
+/* # thisline.numitems=x;
+/* if (thisline.width==0 || thisline.height==0)
+/* return "";
+/* if (thisline.yorg+thisline.height-pagestart>PS){
+/* pagestart=thisline.yorg;
+/* return "newpage";
+/* # must resend this line....
+/* }
+/* thisline.yorg=TM-thisline.yorg-thisline.height+pagestart;
+/* thisline.xorg+=LM;
+/* (items, err) :=getline(totlines,input);
+/* if(err != nil)
+/* return err;
+/* totitems+=len items;
+/* totlines++;
+/* printline(ioutb,thisline,items);
+/* return "";
+/* }
+/*
+/*
+/* getfonts(input: string) : string
+/* {
+/* if (!started) return "Error";
+/* tkfont,psfont : string;
+/* j : int;
+/* retval := "";
+/* if (input[0]=='%')
+/* return "";
+/* # get a line of the form
+/* # 5::/fonts/lucida/moo.16.font
+/* # translate it to...
+/* # 32 f32.16
+/* # where 32==1<<5 and f32.16 is a postscript function that loads the
+/* # appropriate postscript font (from remap)
+/* # and writes it to fonts....
+/* (bits,font):=str->toint(input,10);
+/* if (bits!=-1)
+/* bits=1<<bits;
+/* else{
+/* bits=1;
+/* def_font_type=bits;
+/* curfonttype=def_font_type;
+/* }
+/* font=font[2:];
+/* for(i:=0;i<len remap;i++){
+/* (tkfont,psfont)=remap[i];
+/* if (tkfont==font)
+/* break;
+/* }
+/* if (i==len remap)
+/* psfont="Times-Roman";
+/* (font,nil)=str->splitr(font,".");
+/* (nil,font)=str->splitr(font[0:len font-1],".");
+/* (fsize,nil):=str->toint(font,10);
+/* fsize=(PTPI*3*fsize)/(2*PXPI);
+/* enc_font:="f"+string bits+"."+string fsize;
+/* ps_func:="/"+enc_font+" /"+psfont+" findfont "+string fsize+
+/* " scalefont def\n";
+/* sy_font:="sy"+string fsize;
+/* xtra_func:="/"+sy_font+" /Symbol findfont "+string fsize+
+/* " scalefont def\n";
+/* for(i=0;i<len font_arr;i++){
+/* (j,font)=font_arr[i];
+/* if (j==-1) break;
+/* }
+/* if (j==len font_arr)
+/* return "Error";
+/* font_arr[i]=(bits,enc_font);
+/* if (bits==1)
+/* def_font=enc_font;
+/* curfont++;
+/* retval+= ps_func;
+/* retval+= xtra_func;
+/* return retval;
+/* }
+/*
+/* deffont() : string
+/* {
+/* return def_font;
+/* }
+/*
+/* getline(k : int, input : string) : (array of iteminfo, string)
+/* {
+/* lineval,args : string;
+/* j, nb : int;
+/* lw:=0;
+/* wid:=0;
+/* flags:=0;
+/* item_arr := array[32] of {* => iteminfo(-1,-1,-1,-1,-1,-1,"")};
+/* curitem:=0;
+/* while(input!=nil){
+/* (nil,input)=str->splitl(input,"[");
+/* if (input==nil)
+/* break;
+/* com:=input[1];
+/* input=input[2:];
+/* case com {
+/* 'A' =>
+/* nb=0;
+/* # get the width of the item
+/* (wid,input)=str->toint(input,10);
+/* wid=(wid*PTPI)/PXPI;
+/* if (input[0]!='{')
+/* return (nil, sys->sprint(
+/* "line %d item %d Bad Syntax : '{' expected",
+/* k,curitem));
+/* # get the args.
+/* (args,input)=str->splitl(input,"}");
+/* # get the flags.
+/* # assume there is only one int flag..
+/* (flags,args)=str->toint(args[1:],16);
+/* if (args!=nil && debug){
+/* sys->print("line %d item %d extra flags=%s\n",
+/* k,curitem,args);
+/* }
+/* if (flags<1024) flags=1;
+/* item_arr[curitem].font=flags;
+/* item_arr[curitem].offset=lw;
+/* item_arr[curitem].width=wid;
+/* lw+=wid;
+/* for(j=1;j<len input;j++){
+/* if ((input[j]==')')||(input[j]=='('))
+/* lineval[len lineval]='\\';
+/* if (input[j]=='[')
+/* nb++;
+/* if (input[j]==']')
+/* if (nb==0)
+/* break;
+/* else
+/* nb--;
+/* lineval[len lineval]=input[j];
+/* }
+/* if (j<len input)
+/* input=input[j:];
+/* item_arr[curitem].buf=lineval;
+/* item_arr[curitem].line=k;
+/* item_arr[curitem].itype=ASCII;
+/* curitem++;
+/* lineval="";
+/* 'R' =>
+/* nb=0;
+/* # get the width of the item
+/* (wid,input)=str->toint(input,10);
+/* wid=(wid*PTPI)/PXPI;
+/* if (input[0]!='{')
+/* return (nil, "Bad Syntax : '{' expected");
+/* # get the args.
+/* (args,input)=str->splitl(input,"}");
+/* # get the flags.
+/* # assume there is only one int flag..
+/* (flags,args)=str->toint(args[1:],16);
+/* if (args!=nil && debug){
+/* sys->print("line %d item %d Bad Syntax args=%s",
+/* k,curitem,args);
+/* }
+/* item_arr[curitem].font=flags;
+/* item_arr[curitem].offset=lw;
+/* item_arr[curitem].width=wid;
+/* lw+=wid;
+/* for(j=1;j<len input;j++){
+/* if (input[j]=='[')
+/* nb++;
+/* if (input[j]==']')
+/* if (nb==0)
+/* break;
+/* else
+/* nb--;
+/* case input[j] {
+/* 8226 => # bullet
+/* lineval+="\\267 ";
+/* 169 => # copyright
+/* lineval+="\\251 ";
+/* curitem++;
+/* * =>
+/* lineval[len lineval]=input[j];
+/* }
+/* }
+/* if (j>len input)
+/* input=input[j:];
+/* item_arr[curitem].buf=lineval;
+/* item_arr[curitem].line=k;
+/* item_arr[curitem].itype=RUNE;
+/* curitem++;
+/* lineval="";
+/* 'N' or 'C'=>
+/* # next item
+/* for(j=0;j<len input;j++)
+/* if (input[j]==']')
+/* break;
+/* if (j>len input)
+/* input=input[j:];
+/* 'T' =>
+/* (wid,input)=str->toint(input,10);
+/* wid=(wid*PTPI)/PXPI;
+/* item_arr[curitem].offset=lw;
+/* item_arr[curitem].width=wid;
+/* lw+=wid;
+/* lineval[len lineval]='\t';
+/* # next item
+/* for(j=0;j<len input;j++)
+/* if (input[j]==']')
+/* break;
+/* if (j>len input)
+/* input=input[j:];
+/* item_arr[curitem].buf=lineval;
+/* item_arr[curitem].line=k;
+/* item_arr[curitem].itype=ASCII;
+/* curitem++;
+/* lineval="";
+/* 'W' =>
+/* (wid,input)=str->toint(input,10);
+/* wid=(wid*PTPI)/PXPI;
+/* item_arr[curitem].offset=lw;
+/* item_arr[curitem].width=wid;
+/* item_arr[curitem].itype=IMAGE;
+/* lw+=wid;
+/* # next item
+/* for(j=1;j<len input;j++){
+/* if (input[j]==']')
+/* break;
+/* lineval[len lineval]=input[j];
+/* }
+/* item_arr[curitem].buf=lineval;
+/* if (j>len input)
+/* input=input[j:];
+/* curitem++;
+/* lineval="";
+/* * =>
+/* # next item
+/* for(j=0;j<len input;j++)
+/* if (input[j]==']')
+/* break;
+/* if (j>len input)
+/* input=input[j:];
+/*
+/* }
+/* }
+/* return (item_arr[0:curitem], "");
+/* }
+*/
+
+void
+cmap2ascii85(uchar *b, uchar *c) {
+ int i;
+ unsigned long i1;
+
+/* fprintf(stderr, "addr=0x%x %x %x %x %x\n", b, b[0], b[1], b[2], b[3]); */
+ b--; /* one-index b */
+ c--; /* one-index c */
+ i1 = (b[1]<<24)+(b[2]<<16)+(b[3]<<8)+b[4];
+ if(i1 == 0){
+ c[1] = 'z';
+ c[2] = '\0';
+ return;
+ }
+ for(i=0; i<=4; i++){
+ c[5-i] = '!' + (i1 % 85);
+ i1 /= 85;
+ }
+ c[6] = '\0';
+}
+
+static uchar *arr = nil;
+ulong onesbits = ~0;
+void
+imagebits(Biobuf *ioutb, Memimage *im)
+{
+ int spb;
+ int bitoff;
+ int j, n, n4, i, bpl, nrest;
+ int lsf;
+ uchar c85[6], *data, *src, *dst;
+ Memimage *tmp;
+ Rectangle r;
+
+ tmp = nil;
+ if (debug)
+ fprint(2, "imagebits, r=%d %d %d %d, depth=%d\n",
+ im->r.min.x, im->r.min.y, im->r.max.x, im->r.max.y, im->depth);
+ width = Dx(im->r);
+ height = Dy(im->r);
+ bps = im->depth; /* # bits per sample */
+ bitoff = 0; /* # bit offset of beginning sample within first byte */
+ if (bps < 8) {
+ spb = 8 / bps;
+ bitoff = (im->r.min.x % spb) * bps;
+ }
+ if (bitoff != 0) {
+/* # Postscript image wants beginning of line at beginning of byte */
+ r = im->r;
+ r.min.x -= bitoff/im->depth;
+ r.max.x -= bitoff/im->depth;
+ tmp = allocmemimage(r, im->chan);
+ if(tmp == nil){
+ fprint(2, "p9bitpost: allocmemimage failed: %r\n");
+ exits("alloc");
+ }
+ memimagedraw(tmp, r, im, im->r.min, nil, ZP, S);
+ im = tmp;
+ }
+ lsf = 0;
+ /* compact data to remove word-boundary padding */
+ bpl = bytesperline(im->r, im->depth);
+ n = bpl*Dy(im->r);
+ data = malloc(n);
+ if(data == nil){
+ fprint(2, "p9bitpost: malloc failed: %r\n");
+ exits("malloc");
+ }
+ for(i=0; i<Dy(im->r); i++){
+ /* memmove(data+bpl*i, byteaddr(im, Pt(im->r.min.x, im->r.min.y+i)), bpl); with inversion */
+ dst = data+bpl*i;
+ src = byteaddr(im, Pt(im->r.min.x, im->r.min.y+i));
+ for(j=0; j<bpl; j++)
+ *dst++ = 255 - *src++;
+ }
+ n4 = (n / 4) * 4;
+ for (i = 0; i < n4; i += 4){
+ cmap2ascii85(data+i, c85);
+ lsf += strlen((char *)c85);
+ Bprint(ioutb, "%s", c85);
+ if (lsf > 74) {
+ Bprint(ioutb, "\n");
+ lsf = 0;
+ }
+ }
+ nrest = n - n4;
+ if (nrest != 0) {
+ uchar foo[4];
+
+ for (i=0; i<nrest; i++)
+ foo[i] = data[n4+i];
+ for (i=nrest; i<4; i++)
+ foo[i] = '\0';
+ cmap2ascii85(foo, c85);
+ if (strcmp((char *)c85, "z") == 0 )
+ strcpy((char *)c85, "!!!!!");
+ Bprint(ioutb, "%.*s", nrest+1, c85);
+ }
+ Bprint(ioutb, "\n~>");
+ Bprint(ioutb, "\n");
+ freememimage(tmp);
+}
+
+int
+image2psfile(int fd, Memimage *im, int dpi) {
+ Rectangle r;
+ Rectangle bbox;
+ int e;
+ int xmargin = 36;
+ int ymargin = 36;
+ double paperaspectratio;
+ double imageaspectratio;
+ Biobuf ioutb;
+ Memimage *tmp;
+
+ if(im->depth >= 8 && im->chan != CMAP8 && im->chan != GREY8){
+ /*
+ * the postscript libraries can only handle [1248]-bit grey, 8-bit cmap,
+ * and 24-bit color, so convert.
+ */
+ tmp = allocmemimage(im->r, strtochan("b8g8r8"));
+ if(tmp == nil)
+ return 1;
+ memimagedraw(tmp, tmp->r, im, im->r.min, nil, ZP, S);
+ freememimage(im);
+ im = tmp;
+ }
+
+ Binit(&ioutb, fd, OWRITE);
+ r = im->r;
+ width = Dx(r);
+ height = Dy(r);
+ imageaspectratio = (double) width / (double) height;
+ if (landscape) {
+ paperaspectratio = ((double)paperlength - (ymargin * 2)) / ((double)paperwidth - (xmargin * 2));
+ if (dpi > 0) {
+ iwidth = width * 72 / dpi;
+ iheight = height * 72 / dpi;
+ } else if (imageaspectratio > paperaspectratio) {
+ iwidth = paperlength - (ymargin * 2);
+ iheight = iwidth / imageaspectratio;
+ } else {
+ iheight = paperwidth - (xmargin * 2);
+ iwidth = iheight * imageaspectratio;
+ }
+ xstart = paperwidth - xmargin - (iheight * ymagnification);
+ ystart = paperlength - ymargin;
+ rotation = -90;
+ } else {
+ paperaspectratio = ((double)paperwidth - (xmargin * 2)) / ((double)paperlength - (ymargin * 2));
+ if (dpi > 0) {
+ iwidth = width * 72 / dpi;
+ iheight = height * 72 / dpi;
+ } else if (imageaspectratio > paperaspectratio) {
+ iwidth = paperwidth - (xmargin * 2);
+ iheight = iwidth / imageaspectratio;
+ } else {
+ iheight = paperlength - (ymargin * 2);
+ iwidth = iheight * imageaspectratio;
+ }
+ xstart = xmargin;
+ ystart = paperlength - ymargin - (iheight * ymagnification);
+ rotation = 0;
+ }
+ bbox = Rect(xstart,ystart,xstart+iwidth,ystart+iheight);
+ e = preamble(&ioutb, bbox);
+ if(e != 0)
+ return e;
+ Bprint(&ioutb, "%%%%Page: 1\n%%%%BeginPageSetup\n");
+ Bprint(&ioutb, "/pgsave save def\n");
+ Bprint(&ioutb, "%%%%EndPageSetup\n");
+ bps = im->depth;
+ Bprint(&ioutb, "%d 0 %d %d %d %d %d %d %s doimage\n", iheight, iwidth, ystart, xstart, height, width, bps, im->flags&Fgrey ? "true" : "false");
+ imagebits(&ioutb, im);
+ Bprint(&ioutb, "pgsave restore\nshowpage\n");
+ e = trailer(&ioutb, 1);
+ if(e != 0)
+ return e;
+ Bterm(&ioutb);
+ return 0;
+}
+
+/* set local variables by string and pointer to its value
+ * the variables are:
+ * int magnification
+ * int landscape
+ * char *Patch
+ */
+void
+psopt(char *s, void *val)
+{
+ if(s == nil)
+ return;
+ if(strcmp("xmagnification", s) == 0)
+ xmagnification = *((double *)val);
+ if(strcmp("ymagnification", s) == 0)
+ ymagnification = *((double *)val);
+ if(strcmp("landscape", s) == 0)
+ landscape = *((int *)val);
+ if(strcmp("Patch", s) == 0)
+ Patch = *((char **)val);
+}
diff --git a/sys/src/cmd/postscript/p9bitpost/pslib.h b/sys/src/cmd/postscript/p9bitpost/pslib.h
new file mode 100755
index 000000000..85a7db0ae
--- /dev/null
+++ b/sys/src/cmd/postscript/p9bitpost/pslib.h
@@ -0,0 +1,6 @@
+char *psinit(int, int); /* second arg is debug flag; returns "" on success */
+int image2psfile(int, Memimage*, int);
+void psopt(char *, void *);
+
+int paperlength;
+int paperwidth;
diff --git a/sys/src/cmd/postscript/picpack/mkfile b/sys/src/cmd/postscript/picpack/mkfile
new file mode 100755
index 000000000..4c713bb60
--- /dev/null
+++ b/sys/src/cmd/postscript/picpack/mkfile
@@ -0,0 +1,57 @@
+BUILTINS=
+</$objtype/mkfile
+MAKE=mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/sys/lib/postscript/bin
+
+COMMONDIR=../common
+
+CC=pcc
+LD=pcc
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+LDFLAGS=
+
+all :V: $O.out
+
+install :V: $POSTBIN/$objtype/picpack $MAN1DIR/picpack.1
+
+installall :V:
+ for(objtype in $CPUS) { \
+ $MAKE 'MAKE=$MAKE' \
+ 'SYSTEM=$SYSTEM' 'VERSION=$VERSION' \
+ 'FONTDIR=$FONTDIR' 'HOSTDIR=$HOSTDIR' 'MAN1DIR=$MAN1DIR' \
+ 'POSTBIN=$POSTBIN' 'POSTLIB=$POSTLIB' 'TMACDIR=$TMACDIR' \
+ 'DKHOST=$DKHOST' 'DKSTREAMS=$DKSTREAMS' \
+ 'ROUNDPAGE=$ROUNDPAGE' \
+ 'CC=$CC' 'LD=$LD' 'CFLAGS=$CFLAGS' 'LDFLAGS=$LDFLAGS' \
+ install \
+ }
+
+clean :V:
+ rm -f *.$O
+
+clobber :V: clean
+ rm -f $O.out
+
+$POSTBIN/$objtype/picpack : $O.out
+ cp $prereq $target
+
+$MAN1DIR/picpack.1 : picpack.1
+ cp $prereq $target
+
+$O.out : picpack.$O $COMMONDIR/glob.$O $COMMONDIR/misc.$O $COMMONDIR/tempnam.$O $COMMONDIR/getopt.$O
+ $LD $LDFLAGS $prereq
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+picpack.$O : picpack.c $COMMONDIR/ext.h $COMMONDIR/gen.h $COMMONDIR/path.h
+
+common :V:
+ cd $COMMONDIR; $MAKE
diff --git a/sys/src/cmd/postscript/picpack/picpack.1 b/sys/src/cmd/postscript/picpack/picpack.1
new file mode 100755
index 000000000..ee5a428fa
--- /dev/null
+++ b/sys/src/cmd/postscript/picpack/picpack.1
@@ -0,0 +1,123 @@
+.TH PICPACK 1
+.SH NAME
+.B picpack
+\- PostScript picture packing preprocessor
+.SH SYNOPSIS
+\*(mBpicpack\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B picpack
+copies
+.I files
+to stdout, expanding picture inclusion requests
+(marked by the
+.MW .BP
+or
+.MW .PI
+macros) into an in-line
+format that can be passed through
+.B troff
+and handled by
+.BR dpost .
+If no
+.I files
+are specified
+or if
+.OP \-
+is one of the input
+.I files
+standard input is read.
+The following
+.I options
+are understood:
+.TP 0.75i
+.OP \-k list
+.I list
+is a comma- or space-separated string of words used to locate
+picture inclusion requests.
+The start of every line in the input
+.I files
+is compared with each word in
+.I list .
+If there is a match, the second string on the line is
+taken as the pathname of a picture file that is added
+to the output file.
+The default
+.I list
+is
+.RM `` ".BP .PI ''.
+.TP
+.OP \-q
+Suppress ``missing picture file'' error messages.
+.PP
+.B picpack
+is a trivial preprocessor that, in a sense, duplicates some of the
+picture inclusion capabilities already available in
+.BR dpost .
+.B picpack
+should not be used if your formatting command line includes
+a call to
+.BR dpost .
+Its only purpose is to combine picture files with text in a single
+file that can be passed through
+.B troff
+and unpacked, at some later time, by
+.BR dpost .
+The original picture inclusion mechanism, with files are pulled in by
+.BR dpost ,
+is the preferred approach.
+.SH EXAMPLES
+A typical application might be in a distributed printing environment
+where everything up to
+.B troff
+is run by the user and everything after
+.B troff
+is handled by a spooling daemon (perhaps
+.BR lp ).
+In that case the command line would be,
+.EX
+pic \f2file\fP | tbl | eqn | picpack | troff -mm -Tpost | lp
+.EE
+A poor example, although one that should still work, would be,
+.EX
+pic \f2file\fP | tbl | eqn | picpack | troff -mm -Tpost | dpost >\f2file\fP.ps
+.EE
+In this case picture inclusion requests could (and should) be handled by
+.BR dpost .
+Running
+.B picpack
+is not needed or even recommended.
+It should be dropped from any pipeline that includes a call to
+.BR dpost .
+.SH DIAGNOSTICS
+A 0 exit status is returned if
+.I files
+were successfully processed.
+.SH WARNINGS
+.PP
+Combining pictures and text using the capabilities available in
+.B dpost
+is the recommended approach and is always guaranteed to be more
+efficient than
+.BR picpack .
+Running
+.B picpack
+and
+.B dpost
+in the same pipeline makes little sense.
+.PP
+Using
+.B picpack
+will likely result in files that can no longer be reliably passed
+through other important
+.B troff
+postprocessors like
+.BR proof .
+At present
+.B picpack
+is only guaranteed to work with
+.BR dpost .
+.SH SEE ALSO
+.BR dpost (1),
+.BR troff (1)
diff --git a/sys/src/cmd/postscript/picpack/picpack.c b/sys/src/cmd/postscript/picpack/picpack.c
new file mode 100755
index 000000000..7d96bc02b
--- /dev/null
+++ b/sys/src/cmd/postscript/picpack/picpack.c
@@ -0,0 +1,441 @@
+/*
+ *
+ * picpack - picture packing pre-processor
+ *
+ * A trivial troff pre-processor that copies files to stdout, expanding picture
+ * requests into an in-line format that's passed transparently through troff and
+ * handled by dpost. The program is an attempt to address requirements, expressed
+ * by several organizations, of being able to store a document as a single file
+ * (usually troff input) that can then be sent through dpost and ultimately to
+ * a PostScript printer.
+ *
+ * The program looks for strings listed in the keys[] array at the start of each
+ * line. When a picture request (as listed in keys[]) is found the second string
+ * on the line is taken to be a picture file pathname that's added (in transparent
+ * mode) to the output file. In addition each in-line picture file is preceeded by
+ * device control command (again passed through in transparent mode) that looks
+ * like,
+ *
+ * x X InlinePicture filename bytes
+ *
+ * where bytes is the size of the picture file (which begins on the next line)
+ * and filename is the pathname of the picture file. dpost uses both arguments to
+ * manage in-line pictures (in a big temp file). To handle pictures in diversions
+ * picpack reads each input file twice. The first pass looks for picture inclusion
+ * requests and copies each picture file transparently to the output file, while
+ * second pass just copies the input file to the output file. Things could still
+ * break, but the two pass method should handle most jobs.
+ *
+ * The recognized in-line picture requests are saved in keys[] and by default only
+ * expand .BP and .PI macro calls. The -k option changes the recognized strings,
+ * and may be needed if you've built your own picture inclusion macros on top of
+ * .BP or .PI or decided to list each picture file at the start of your input file
+ * using a dummy macro. For example you could require every in-line picture be
+ * named by a dummy macro (say .iP), then the command line,
+ *
+ * picpack -k.iP file > file.pack
+ *
+ * hits on lines that begin with .iP (rather than .BP or .PI), and the only files
+ * pulled in would be ones named as the second argument to the new .iP macro. The
+ * -k option accepts a space or comma separated list of up to 10 different key
+ * strings. picpack imposes no contraints on key strings, other than not allowing
+ * spaces or commas. A key string can begin with \" and in that case it would be
+ * troff comment.
+ *
+ * Although the program will help some users, there are obvious disadvantages.
+ * Perhaps the most important is that troff output files (with in-line pictures
+ * included) don't fit the device independent language accepted by important post
+ * processors like proof, and that means you won't be able to reliably preview a
+ * packed file on your 5620 or whatever. Another potential problem is that picture
+ * files can be large. Packing everything together in a single file at an early
+ * stage has a better chance of exceeding your system's ulimit.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "gen.h" /* general purpose definitions */
+#include "ext.h" /* external variable definitions */
+#include "path.h" /* just for TEMPDIR definition */
+
+char *keys[11] = {".BP", ".PI", NULL};
+int quiet = FALSE;
+
+FILE *fp_in = stdin; /* input */
+FILE *fp_out = stdout; /* and output files */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A picture packing pre-processor that copies input files to stdout, expanding
+ * picture requests (as listed in keys[]) to an in-line format that can be passed
+ * through troff (using transparent mode) and handled later by dpost.
+ *
+ */
+
+ argc = agc; /* global so everyone can use them */
+ argv = agv;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ options(); /* command line options */
+ arguments(); /* translate all the input files */
+ done(); /* clean things up */
+
+ exit(x_stat); /* everything probably went OK */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* name returned by getopt() */
+
+ extern char *optarg; /* option argument set by getopt() */
+ extern int optind;
+
+/*
+ *
+ * Handles the command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, "k:qDI")) != EOF ) {
+ switch ( ch ) {
+ case 'k': /* new expansion key strings */
+ newkeys(optarg);
+ break;
+
+ case 'q': /* disables "missing picture" messages */
+ quiet = TRUE;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't know the option */
+ error(FATAL, "");
+ break;
+
+ default:
+ error(FATAL, "missing case for option %c", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-options args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+newkeys(list)
+
+ char *list; /* comma or space separated key strings */
+
+{
+
+ char *p; /* next key string from *list */
+ int i; /* goes in keys[i] */
+ int n; /* last key string slot in keys[] */
+
+/*
+ *
+ * Separates *list into space or comma separated strings and adds each to the
+ * keys[] array. The strings in keys[] are used to locate the picture inclusion
+ * requests that are translated to the in-line format. The keys array must end
+ * with a NULL pointer and by default only expands .BP and .PI macro calls.
+ *
+ */
+
+ n = (sizeof(keys) / sizeof(char *)) - 1;
+
+ for ( i = 0, p = strtok(list, " ,"); p != NULL; i++, p = strtok(NULL, " ,") )
+ if ( i >= n )
+ error(FATAL, "too many key strings");
+ else keys[i] = p;
+
+ keys[i] = NULL;
+
+} /* End of newkeys */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+ FILE *copystdin();
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we process stdin, after copying it to a temporary file.
+ *
+ */
+
+ if ( argc < 1 ) {
+ fp_in = copystdin();
+ picpack();
+ } else
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = copystdin();
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ picpack();
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+FILE *copystdin()
+
+{
+
+ char *tfile; /* temporary file name */
+ int fd_out; /* and its file descriptor */
+ FILE *fp; /* return value - the new input file */
+
+/*
+ *
+ * Copies stdin to a temp file, unlinks the file, and returns the file pointer
+ * for the new temporary file to the caller. Needed because we read each input
+ * file twice in an attempt to handle pictures in diversions.
+ *
+ */
+
+ if ( (tfile = tempnam(TEMPDIR, "post")) == NULL )
+ error(FATAL, "can't generate temp file name");
+
+ if ( (fd_out = creat(tfile, 0660)) == -1 )
+ error(FATAL, "can't create %s", tfile);
+
+ copyfile(fileno(stdin), fd_out);
+ close(fd_out);
+
+ if ( (fp = fopen(tfile, "r")) == NULL )
+ error(FATAL, "can't open %s", tfile);
+
+ unlink(tfile);
+ return(fp);
+
+} /* End of copystdin */
+
+/*****************************************************************************/
+
+copyfile(fd_in, fd_out)
+
+ int fd_in; /* input */
+ int fd_out; /* and output files */
+
+{
+
+ char buf[512]; /* internal buffer for reads and writes */
+ int count; /* number of bytes put in buf[] */
+
+/*
+ *
+ * Copies file fd_in to fd_out. Handles the second pass for each input file and
+ * also used to copy stdin to a temporary file.
+ *
+ */
+
+ while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
+ if ( write(fd_out, buf, count) != count )
+ error(FATAL, "write error");
+
+} /* End of copyfile */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files - unlink the temporary file that was used
+ * to record the in-line picture file pathnames.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+picpack()
+
+{
+
+ char line[512]; /* next input line */
+ char name[100]; /* picture file names - from BP or PI */
+ int i; /* for looking through keys[] */
+
+/*
+ *
+ * Handles the two passes over the next input file. First pass compares the start
+ * of each line in *fp_in with the key strings saved in the keys[] array. If a
+ * match is found inline() is called to copy the picture file (ie. the file named
+ * as the second string in line[]) to stdout, provided the file hasn't previously
+ * been copied. The second pass goes back to the start of fp_in and copies it all
+ * to the output file.
+ *
+ */
+
+ while ( fgets(line, sizeof(line), fp_in) != NULL ) {
+ for ( i = 0; keys[i] != NULL; i++ )
+ if ( strncmp(line, keys[i], strlen(keys[i])) == 0 ) {
+ if ( sscanf(line, "%*s %s", name) == 1 ) {
+ strtok(name, "(");
+ if ( gotpicfile(name) == FALSE )
+ inline(name);
+ } /* End if */
+ } /* End if */
+ } /* End while */
+
+ fflush(fp_out); /* second pass - copy fp_in to fp_out */
+ fseek(fp_in, 0L, 0);
+ copyfile(fileno(fp_in), fileno(fp_out));
+
+} /* End of picpack */
+
+/*****************************************************************************/
+
+inline(name)
+
+ char *name; /* name of the in-line picture file */
+
+{
+
+ long size; /* size in bytes - from fstat */
+ FILE *fp; /* for reading *name */
+ int ch; /* next character from picture file */
+ int lastch = '\n'; /* so we know when to put out \! */
+
+ struct stat sbuf; /* for the picture file size */
+
+/*
+ *
+ * Copies the picture file *name to the output file in an in-line format that can
+ * be passed through troff and recovered later by dpost. Transparent mode is used
+ * so each line starts with \! and all \ characters must be escaped. The in-line
+ * picture sequence begins with an "x X InlinePicture" device control command that
+ * names the picture file and gives its size (in bytes).
+ *
+ */
+
+ if ( (fp = fopen(name, "r")) != NULL ) {
+ fstat(fileno(fp), &sbuf);
+ if ( (size = sbuf.st_size) > 0 ) {
+ fprintf(fp_out, "\\!x X InlinePicture %s %ld\n", name, size);
+ while ( (ch = getc(fp)) != EOF ) {
+ if ( lastch == '\n' )
+ fprintf(fp_out, "\\!");
+ if ( ch == '\\' )
+ putc('\\', fp_out);
+ putc(lastch = ch, fp_out);
+ } /* End while */
+ if ( lastch != '\n' )
+ putc('\n', fp_out);
+ } /* End if */
+ fclose(fp);
+ addpicfile(name);
+ } else if ( quiet == FALSE )
+ error(NON_FATAL, "can't read picture file %s", name);
+
+} /* End of inline */
+
+/*****************************************************************************/
+
+gotpicfile(name)
+
+ char *name;
+
+{
+
+ char buf[100];
+ FILE *fp_pic;
+
+/*
+ *
+ * Checks the list of previously added picture files in *temp_file and returns
+ * FALSE if it's a new file and TRUE otherwise. Probably should open the temp
+ * file once for update and leave it open, rather than opening and closing it
+ * every time.
+ *
+ */
+
+ if ( temp_file != NULL )
+ if ( (fp_pic = fopen(temp_file, "r")) != NULL ) {
+ while ( fscanf(fp_pic, "%s", buf) != EOF )
+ if ( strcmp(buf, name) == 0 ) {
+ fclose(fp_pic);
+ return(TRUE);
+ } /* End if */
+ fclose(fp_pic);
+ } /* End if */
+
+ return(FALSE);
+
+} /* End of gotpicfile */
+
+/*****************************************************************************/
+
+addpicfile(name)
+
+ char *name;
+
+{
+
+ FILE *fp_pic;
+
+/*
+ *
+ * Adds string *name to the list of in-line picture files that's maintained in
+ * *temp_file. Should undoubtedly open the file once for update and use fseek()
+ * to move around in the file!
+ *
+ */
+
+ if ( temp_file == NULL )
+ if ( (temp_file = tempnam(TEMPDIR, "picpac")) == NULL )
+ return;
+
+ if ( (fp_pic = fopen(temp_file, "a")) != NULL ) {
+ fprintf(fp_pic, "%s\n", name);
+ fclose(fp_pic);
+ } /* End if */
+
+} /* End of addpicfile */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/picpack/picpack.mk b/sys/src/cmd/postscript/picpack/picpack.mk
new file mode 100755
index 000000000..e79febae7
--- /dev/null
+++ b/sys/src/cmd/postscript/picpack/picpack.mk
@@ -0,0 +1,75 @@
+MAKE=/bin/make
+MAKEFILE=picpack.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+HFILES=$(COMMONDIR)/ext.h\
+ $(COMMONDIR)/gen.h\
+ $(COMMONDIR)/path.h
+
+OFILES=picpack.o\
+ $(COMMONDIR)/glob.o\
+ $(COMMONDIR)/misc.o\
+ $(COMMONDIR)/tempnam.o
+
+all : picpack
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ cp picpack $(POSTBIN)/picpack
+ @chmod 755 $(POSTBIN)/picpack
+ @chgrp $(GROUP) $(POSTBIN)/picpack
+ @chown $(OWNER) $(POSTBIN)/picpack
+ cp picpack.1 $(MAN1DIR)/picpack.1
+ @chmod 644 $(MAN1DIR)/picpack.1
+ @chgrp $(GROUP) $(MAN1DIR)/picpack.1
+ @chown $(OWNER) $(MAN1DIR)/picpack.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f picpack
+
+picpack : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o picpack $(OFILES)
+
+picpack.o : $(HFILES)
+
+$(COMMONDIR)/glob.o\
+$(COMMONDIR)/misc.o\
+$(COMMONDIR)/tempnam.o :
+ @cd $(COMMONDIR); $(MAKE) -f common.mk SYSTEM=$(SYSTEM) `basename $@`
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE)
+
diff --git a/sys/src/cmd/postscript/postbgi/README b/sys/src/cmd/postscript/postbgi/README
new file mode 100755
index 000000000..de815330e
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/README
@@ -0,0 +1,21 @@
+
+BGI (Basic Graphical Instructions) to PostScript translator. Probably
+not useful outside the Computer Centers. Added code to tie lines to
+device space coordinates. Helps eliminate variations in line widths
+noticeable when users selected a non-zero linewidth with the -w option.
+
+Much that was omitted from early versions of the program has been
+implemented. What's in place will handle most STARE (black and white)
+and PRISM (color) BGI jobs. PRISM jobs often fill regions with color,
+and need require device specific tuning to get things just right. An
+easy solution is add "-P/prism true def" option to the postbgi command
+line when you translate PRISM jobs.
+
+A typical command line for STARE jobs would be,
+
+ postbgi file >file.ps
+
+while for PRISM jobs use,
+
+ postbgi -P"/prism true def" file >file.ps
+
diff --git a/sys/src/cmd/postscript/postbgi/mkfile b/sys/src/cmd/postscript/postbgi/mkfile
new file mode 100755
index 000000000..5722ae6ca
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/mkfile
@@ -0,0 +1,61 @@
+BUILTINS=
+</$objtype/mkfile
+MAKE=mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/rc/bin/postscript/
+POSTLIB=$ROOT/sys/lib/postscript/prologues
+
+COMMONDIR=../common
+
+CC=pcc
+LD=pcc
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+LDFLAGS=
+
+all :V: $O.out
+
+install :V: $POSTBIN/$objtype/postbgi $POSTLIB/postbgi.ps $MAN1DIR/postbgi.1
+
+installall :V:
+ for(objtype in $CPUS) { \
+ $MAKE 'MAKE=$MAKE' \
+ 'SYSTEM=$SYSTEM' 'VERSION=$VERSION' \
+ 'FONTDIR=$FONTDIR' 'HOSTDIR=$HOSTDIR' 'MAN1DIR=$MAN1DIR' \
+ 'POSTBIN=$POSTBIN' 'POSTLIB=$POSTLIB' 'TMACDIR=$TMACDIR' \
+ 'DKHOST=$DKHOST' 'DKSTREAMS=$DKSTREAMS' \
+ 'ROUNDPAGE=$ROUNDPAGE' \
+ 'CC=$CC' 'LD=$LD' 'CFLAGS=$CFLAGS' 'LDFLAGS=$LDFLAGS' \
+ install \
+ }
+
+clean :V:
+ rm -f *.$O
+
+clobber :V: clean
+ rm -f $O.out
+
+$POSTBIN/$objtype/postbgi : $O.out
+ cp $prereq $target
+
+$POSTLIB/postbgi.ps : postbgi.ps
+ cp $prereq $target
+
+$MAN1DIR/postbgi.1 : postbgi.1
+ cp $prereq $target
+
+$O.out : postbgi.$O $COMMONDIR/glob.$O $COMMONDIR/misc.$O $COMMONDIR/request.$O $COMMONDIR/getopt.$O
+ $LD $LDFLAGS $prereq
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+postbgi.$O : $COMMONDIR/comments.h $COMMONDIR/gen.h $COMMONDIR/path.h $COMMONDIR/ext.h postbgi.h
+
+common :V:
+ cd $COMMONDIR; $MAKE
diff --git a/sys/src/cmd/postscript/postbgi/postbgi.1 b/sys/src/cmd/postscript/postbgi/postbgi.1
new file mode 100755
index 000000000..971e14a0c
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/postbgi.1
@@ -0,0 +1,243 @@
+.ds dQ /usr/lib/postscript
+.TH POSTBGI 1 "DWB 3.2"
+.SH NAME
+.B postbgi
+\- PostScript translator for
+.SM BGI
+(Basic Graphical Instructions) files
+.SH SYNOPSIS
+\*(mBpostbgi\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B postbgi
+translates
+.SM BGI
+(Basic Graphical Instructions)
+.I files
+into PostScript and writes the results on the
+standard output.
+If no
+.I files
+are specified, or if
+.OP \-
+is one of the input
+.IR files ,
+the standard input is read.
+The following
+.I options
+are understood:
+.TP 0.75i
+.OP \-c num
+Print
+.I num
+copies of each page.
+By default only one copy is printed.
+.TP
+.OP \-f name
+Print text using font
+.IR name .
+Any PostScript font can be used,
+although the best results will only be
+obtained with constant width fonts.
+The default font is Courier.
+.TP
+.OP \-m num
+Magnify each logical page by the factor
+.IR num .
+Pages are scaled uniformly about the origin,
+which by default is located at the center of
+each page.
+The default magnification is 1.0.
+.TP
+.OP \-n num
+Print
+.I num
+logical pages on each piece of paper,
+where
+.I num
+can be any positive integer.
+By default
+.I num
+is set to 1.
+.TP
+.OP \-o list
+Print pages whose numbers are given in the comma-separated
+.IR list .
+The list contains single numbers
+.I N
+and ranges
+.IR N1\-\|N2 .
+A missing
+.I N1
+means the lowest numbered page, a missing
+.I N2
+means the highest.
+.TP
+.OP \-p mode
+Print
+.I files
+in either \*(mBportrait\fP or \*(mBlandscape\fP
+.IR mode .
+Only the first character of
+.I mode
+is significant.
+The default
+.I mode
+is \*(mBportrait\fP.
+.TP
+.OP \-w num
+Set the line width used for graphics to
+.I num
+points, where a point is approximately 1/72
+of an inch.
+By default
+.I num
+is set to 0 points, which forces lines to be
+one pixel wide.
+.TP
+.OP \-x num
+Translate the origin
+.I num
+inches along the positive x axis.
+The default
+coordinate system has the origin fixed at the
+center of the page, with positive
+x to the right and positive y up the page.
+Positive
+.I num
+moves everything right.
+The default offset is 0 inches.
+.TP
+.OP \-y num
+Translate the origin
+.I num
+inches along the positive y axis.
+Positive
+.I num
+moves everything up the page.
+The default offset is 0 inches.
+.TP
+.OP \-E name
+Set the character encoding for text fonts to
+.IR name .
+Requesting
+.I name
+means include file
+.MI \*(dQ name .enc \f1.
+A nonexistent encoding file is silently ignored.
+The default selects file
+.MR \*(dQ/Default.enc .
+.TP
+.OP \-L file
+Use
+.I file
+as the PostScript prologue.
+.br
+The default is
+.MR \*(dQ/postbgi.ps .
+.PP
+Three options allow insertion of arbitrary PostScript
+at controlled points in the translation process:
+.TP 0.75i
+.OP \-C file
+Copy
+.I file
+to the output file;
+.I file
+must contain legitimate PostScript.
+.TP
+.OP \-P string
+Include
+.I string
+in the output file;
+.I string
+must be legitimate PostScript.
+.TP
+.OP \-R action
+Requests special
+.I action
+(e.g.,
+.MR manualfeed )
+on a per page or global basis.
+The
+.I action
+string can be given as
+.IR request ,
+.IM request : page\f1\|,
+or
+.IM request : page : file\f1\|.
+If
+.I page
+is omitted or given as 0, the request
+applies to all pages.
+If
+.I file
+is omitted, the request
+lookup is done in
+.MR \*(dQ/ps.requests .
+.PP
+.B postbgi
+can handle
+.SM STARE
+(black and white) and
+.SM PRISM
+(color)
+.SM BGI
+jobs.
+By default plots are rigidly scaled to fill the page, which produces
+the good results for most
+.SM STARE
+jobs.
+.SM PRISM
+jobs typically fill regions with colors, and often require device
+specific tuning to produce acceptable results.
+Adding the
+.MW \-P"/prism\ true\ def"
+option is strongly recommended when
+.B postbgi
+is translating
+.SM PRISM
+jobs.
+.br
+.ne 7v
+.SH EXAMPLES
+For most
+.SM STARE
+jobs,
+.EX
+postbgi \f2file
+.EE
+gives good results, while
+.EX
+postbgi \-P"/prism true def" \f2file
+.EE
+is recommended when translating
+.SM PRISM
+jobs.
+.SH DIAGNOSTICS
+A 0 exit status is returned if
+.I files
+were successfully processed.
+.SH BUGS
+The default line width is too small for write-white
+print engines, like the one used by the PS-2400.
+Several
+.SM BGI
+opcodes have not been implemented.
+.SH FILES
+.MW \*(dQ/postbgi.ps
+.br
+.MW \*(dQ/forms.ps
+.br
+.MW \*(dQ/ps.requests
+.SH SEE ALSO
+.BR dpost (1),
+.BR postdaisy (1),
+.BR postdmd (1),
+.BR postio (1),
+.BR postmd (1),
+.BR postprint (1),
+.BR postreverse (1),
+.BR posttek (1),
+.BR psencoding (1)
diff --git a/sys/src/cmd/postscript/postbgi/postbgi.c b/sys/src/cmd/postscript/postbgi/postbgi.c
new file mode 100755
index 000000000..c6047d75b
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/postbgi.c
@@ -0,0 +1,1523 @@
+/*
+ *
+ * postbgi - BGI (Basic Graphical Instructions) to PostScript translator.
+ *
+ * A simple program that translates BGI files into PostScript. Probably only
+ * useful in Computer Centers that support STARE or PRISM plotters. Most of the
+ * code was borrowed from the corresponding program that was written for printers
+ * that understand Impress.
+ *
+ * Extending the original program to handle PRISM jobs was not trivial. Graphics
+ * packages that support PRISM occasionally use BGI commands that I ignored in the
+ * STARE implementation. Subroutines, color requests, patterns (for filling), and
+ * filled trapeziods were the most important omissions. All are now implemented,
+ * and at present only repeats, filled slices, and raster rectangles are missing.
+ *
+ * Pattern filling results were not always predictable or even good, unless the
+ * halftone screen definitions were changed and scaling was adjusted so one pixel
+ * in user space mapped into an integral number of device space pixels. Doing that
+ * makes the resulting PostScript output device dependent, but was often necessary.
+ * I've added two booleans to the PostScript prologue (fixscreen and scaletodevice)
+ * that control what's done. By default both are false (check postbgi.ps) but can
+ * be set to true on the command line using the -P option or by hand by changing
+ * the definitions in the prologue. A command line that would set fixscreen and
+ * scaletodevice true would look like,
+ *
+ * postbgi -P"/fixscreen true" -P"/scaletodevice true" file >file.ps
+ *
+ * Several other approaches are available if you want to have your spooler handle
+ * STARE and PRISM jobs differently. A boolean called prism is defined in the
+ * prologue (postbgi.ps) and if it's set to true PostScript procedure setup will
+ * set fixscreen and scaletodevice to true before anything important is done. That
+ * means the following command line,
+ *
+ * postbgi -P"/prism true" file >file.ps
+ *
+ * accomplishes the same things as the last example. Two different prologue files,
+ * one for STARE jobs and the other for PRISM, could be used and the spooler could
+ * point postbgi to the appropriate one using the -L option. In that case the only
+ * important difference in the two prologues would be the definition of prism. The
+ * prologue used for PRISM jobs would have prism set to true, while the STARE
+ * prologue would have it set to false.
+ *
+ * Also included is code that ties lines to device space coordinates. What you get
+ * is a consistent line thickness, but placement of lines won't be exact. It's a
+ * trade-off that should be right for most jobs. Everything is implemented in the
+ * prologue (postbgi.ps) and nothing will be done if the linewidth is zero or if
+ * the boolean fixlinewidth (again in postbgi.ps) is false. Once again the -P
+ * option can be used to set fixlinewidth to whatever you choose.
+ *
+ * BGI supports color mixing but PostScript doesn't. BGI files that expect to mix
+ * colors won't print properly. PostScript's fill operator overlays whatever has
+ * already been put down. Implementing color mixing would have been a terribly
+ * difficult job - not worth the effort!
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how the program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * v
+ *
+ * dx1 dy1 ... dxn dyn x y v -
+ *
+ * Draws the vector described by the numbers on the stack. The top two
+ * numbers are the coordinates of the starting point. The rest of the
+ * numbers are relative displacements from the preceeding point.
+ *
+ * pp
+ *
+ * x1 y1 ... xn yn string pp -
+ *
+ * Prints string, which is always a single character, at the points
+ * represented by the rest of the numbers on the stack.
+ *
+ * R
+ *
+ * n deltax deltay x y R -
+ *
+ * Creates a rectangular path with its lower left corner at (x, y) and
+ * sides of length deltax and deltay. The resulting path is stroked if
+ * n is 0 and filled otherwise.
+ *
+ * T
+ *
+ * dx3 dy3 dx2 dy2 dx1 dy1 x y T -
+ *
+ * Fills a trapezoid starting at (x, y) and having relative displacements
+ * given by the (dx, dy) pairs.
+ *
+ * t
+ *
+ * angle x y string t -
+ *
+ * Prints string starting at (x, y) using an orientation of angle degrees.
+ * The PostScript procedure can handle any angle, but BGI files will only
+ * request 0 or 90 degrees. Text printed at any other orientation will be
+ * vector generated.
+ *
+ * p
+ *
+ * x y p -
+ *
+ * Called to mark the point (x, y). It fills a small circle, that right
+ * now has a constant radius. This stuff could probably be much more
+ * efficient?
+ *
+ * l
+ *
+ * array l -
+ *
+ * Sets the line drawing mode according to the description given in
+ * array. The arrays that describe the different line styles are declared
+ * in STYLES (file posttek.h), although it would be better to have them
+ * defined in the prologue.
+ *
+ * c
+ *
+ * red green blue c -
+ *
+ * Sets the current PostScript RGB color using setrgbcolor. Also used for
+ * selecting appropriate patterns as colors.
+ *
+ * f
+ *
+ * bgisize f -
+ *
+ * Changes the size of the font that's used to print text. bgisize is a
+ * grid separation in a 5 by 7 array in which characters are assumed to
+ * be built.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * The default line width is zero, which forces lines to be one pixel wide. That
+ * works well for 'write to black' engines but won't be right for 'write to white'
+ * engines. The line width can be changed using the -w option, or you can change
+ * the initialization of linewidth in the prologue. Code in the prologue supports
+ * the generation of uniform width lines when linewidth is non-zero and boolean
+ * fixlinewidth is true.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <math.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+#include "postbgi.h" /* a few definitions just used here */
+
+char *optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
+
+char *prologue = POSTBGI; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+char *styles[] = STYLES; /* descriptions of line styles */
+
+int hpos = 0; /* current horizontal */
+int vpos = 0; /* and vertical position */
+
+int bgisize = BGISIZE; /* just the character grid spacing */
+int linespace; /* distance between lines of text */
+
+int bgimode; /* character or graph mode */
+
+int in_subr = FALSE; /* currently defining a subroutine */
+int in_global = FALSE; /* to save space with subroutine defs */
+int subr_id = 0; /* defining this subroutine */
+int shpos = 0; /* starting horizontal */
+int svpos = 0; /* and vertical positions - subroutines */
+Disp displacement[64]; /* dx and dy after a subroutine call */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+FILE *fp_in = stdin; /* read from this file */
+FILE *fp_out = NULL; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A program that converts BGI (Basic Graphical Instructions) files generated by
+ * packages like GRAFPAC and DISSPLA into PostScript. It does an adequate job but
+ * is far from perfect. A few things still haven't been implemented (eg. repeats
+ * and raster rectangles), but what's here should be good enough for most of our
+ * STARE and PRISM jobs. Color mixing (in PRISM jobs) won't work on PostScript
+ * printers, and there's no chance I'll implement it!
+ *
+ */
+
+ argc = agc; /* global so everyone can use them */
+ argv = agv;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* set up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* everything probably went OK */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Make sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* option name - from getopt() */
+
+/*
+ *
+ * Reads and processes the command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s def\n", optarg);
+ break;
+
+ case 'f': /* new font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'w': /* line width */
+ fprintf(stdout, "/linewidth %s def\n", optarg);
+ break;
+
+ case 'x': /* shift horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* Postscript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't know the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line options are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ conv();
+ else
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ conv();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with the last input file, so mark the end of the pages, make sure the
+ * last page is printed, and restore the initial environment.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct, provided it's not NULL.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+conv()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Controls the conversion of BGI files into PostScript. Not everything has been
+ * implemented, but what's been done should be good enough for our purposes.
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ bgimode = 0;
+ formfeed();
+
+ while ( (ch = get_char()) != EOF ) {
+ switch ( ch ) {
+ case BRCHAR: /* rotated character mode */
+ bgimode = ch;
+ text(90);
+ break;
+
+ case BCHAR: /* graphical character mode */
+ bgimode = ch;
+ text(0);
+ break;
+
+ case BGRAPH: /* graphical master mode */
+ bgimode = ch;
+ break;
+
+ case BSUB: /* subroutine definition */
+ subr_def();
+ break;
+
+ case BRET: /* end of subroutine */
+ subr_end();
+ break;
+
+ case BCALL: /* subroutine call */
+ subr_call();
+ break;
+
+ case BEND: /* end display - page */
+ formfeed();
+ break;
+
+ case BERASE: /* erase - shouldn't be used */
+ error(FATAL, "BGI erase opcode obsolete");
+ break;
+
+ case BREP: /* repeat */
+ error(FATAL, "Repeat not implemented");
+ repeat();
+ break;
+
+ case BSETX: /* new x coordinate */
+ hgoto(get_int(0));
+ break;
+
+ case BSETY: /* new y coordinate */
+ vgoto(get_int(0));
+ break;
+
+ case BSETXY: /* new x and y coordinates */
+ hgoto(get_int(0));
+ vgoto(get_int(0));
+ break;
+
+ case BINTEN: /* mark the current point */
+ fprintf(fp_out, "%d %d p\n", hpos, vpos);
+ break;
+
+ case BVISX: /* visible x */
+ vector(X_COORD, VISIBLE);
+ break;
+
+ case BINVISX: /* invisible x */
+ vector(X_COORD, INVISIBLE);
+ break;
+
+ case BVISY: /* visible y */
+ vector(Y_COORD, VISIBLE);
+ break;
+
+ case BINVISY: /* invisible y */
+ vector(Y_COORD, INVISIBLE);
+ break;
+
+ case BVEC: /* arbitrary vector */
+ vector(LONGVECTOR, VISIBLE);
+ break;
+
+ case BSVEC: /* short vector */
+ vector(SHORTVECTOR, VISIBLE);
+ break;
+
+ case BRECT: /* draw rectangle */
+ rectangle(OUTLINE);
+ break;
+
+ case BPOINT1: /* point plot 1 */
+ case BPOINT: /* point plot 2 */
+ point_plot(ch, get_char());
+ break;
+
+ case BLINE: /* line plot */
+ line_plot();
+ break;
+
+ case BLTY: /* line type */
+ fprintf(fp_out, "%s l\n", styles[get_data()]);
+ break;
+
+ case BARC: /* circular arc */
+ arc(OUTLINE);
+ break;
+
+ case BFARC: /* filled circle */
+ arc(FILL);
+ break;
+
+ case BFRECT: /* filled rectangle */
+ rectangle(FILL);
+ break;
+
+ case BRASRECT: /* raster rectangle */
+ error(FATAL, "Raster Rectangle not implemented");
+ break;
+
+ case BCOL: /* select color */
+ set_color(get_data());
+ break;
+
+ case BFTRAPH: /* filled trapezoid */
+ trapezoid();
+ break;
+
+ case BPAT: /* pattern for area filling */
+ pattern();
+ break;
+
+ case BCSZ: /* change BGI character 'size' */
+ setsize(get_data());
+ break;
+
+ case BNOISE: /* from bad file format */
+ break;
+
+ default: /* don't recognize the code */
+ error(FATAL, "bad BGI command %d (0%o)", ch, ch);
+ break;
+ } /* End switch */
+
+ if ( debug == ON )
+ fprintf(stderr, "\n");
+ } /* End while */
+
+ formfeed(); /* in case BEND was missing */
+
+} /* End of conv */
+
+/*****************************************************************************/
+
+hgoto(n)
+
+ int n; /* new horizontal position */
+
+{
+
+/*
+ *
+ * Sets the current BGI horizontal position to n.
+ *
+ */
+
+ hpos = n;
+
+} /* End of hgoto */
+
+/*****************************************************************************/
+
+vgoto(n)
+
+ int n; /* move to this vertical position */
+
+{
+
+/*
+ *
+ * Sets the absolute vertical position to n.
+ *
+ */
+
+ vpos = n;
+
+} /* End of vgoto */
+
+/*****************************************************************************/
+
+setsize(n)
+
+ int n; /* BGI size - just a grid separation */
+
+{
+
+/*
+ *
+ * Called when we're supposed to change the BGI character size to n. The BGI
+ * size is the grid separation in a 5 by 7 array in which characters are assumed
+ * to be built.
+ *
+ */
+
+ bgisize = n;
+ linespace = LINESPACE(bgisize);
+
+ fprintf(fp_out, "%d f\n", bgisize);
+
+ if ( debug == ON )
+ fprintf(stderr, "BGI size = %d\n", n);
+
+} /* End of setsize */
+
+/*****************************************************************************/
+
+repeat()
+
+{
+
+ int count; /* repeat this many times */
+ int ch; /* next input character */
+
+/*
+ *
+ * Haven't implemented repeats, although it wouldn't be difficult. Apparently it's
+ * not used by any graphics packages that generate BGI.
+ *
+ */
+
+ count = get_int(); /* get the repeat count */
+
+ while ( (ch = get_char()) != EOF && ch != BENDR ) ;
+
+} /* End of repeat */
+
+/*****************************************************************************/
+
+text(angle)
+
+ int angle; /* either 0 or 90 degrees */
+
+{
+
+ int ch; /* next character from file *fp_in */
+
+/*
+ *
+ * Called from conv() after we've entered one of the graphical character modes.
+ * Characters are read from the input file and printed until the next mode change
+ * opcode is found (or until EOF). angle will be 90 for rotated character mode
+ * and 0 otherwise.
+ *
+ *
+ */
+
+ fprintf(fp_out, "%d %d %d(", angle, hpos, vpos);
+
+ while ( (ch = get_char()) != EOF ) {
+ if ( ch == BGRAPH || ch == BCHAR || ch == BRCHAR ) {
+ ungetc(ch, fp_in);
+ position--;
+ break;
+ } /* End if */
+
+ switch ( ch ) {
+ case '\012':
+ vgoto(vpos - linespace);
+
+ case '\015':
+ hgoto(0);
+ fprintf(fp_out, ")t\n%d %d %d(", angle, hpos, vpos);
+ break;
+
+ case '(':
+ case ')':
+ case '\\':
+ putc('\\', fp_out);
+
+ default:
+ if ( isascii(ch) && isprint(ch) )
+ putc(ch, fp_out);
+ else fprintf(fp_out, "\\%.3o", ch & 0377);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ fprintf(fp_out, ") t\n");
+
+} /* End of text */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+ int ch; /* repeat count for this page */
+
+/*
+ *
+ * Does whatever is needed to print the last page and get ready for the next one.
+ * It's called, from conv(), after a BEND code is processed. I'm ignoring the
+ * copy count that's expected to follow each page.
+ *
+ */
+
+ if ( bgimode == BGRAPH && (ch = get_char()) != EOF && ! (ch & MSB) ) {
+ ungetc(ch, fp_in);
+ position--;
+ } /* End if */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ fprintf(fp_out, "cleartomark\n");
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ while ( (ch = get_char()) == 0 ) ; /* skip any NULL characters */
+ ungetc(ch, fp_in);
+ position--;
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+
+ setsize(bgisize);
+ hpos = vpos = 0;
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+subr_def()
+
+{
+
+/*
+ *
+ * Starts a subroutine definition. All subroutines are defined as PostScript
+ * procedures that begin with the character S and end with the subroutine's id
+ * (a number between 0 and 63 - I guess). The primary, and perhaps only use of
+ * subroutines is in special color plots produced by several graphics libraries,
+ * and even there it's not all that common. I've also chosen not to worry about
+ * nested subroutine definitions - that would certainly be overkill!
+ *
+ * All subroutines set up their own (translated) coordinate system, do their work
+ * in that system, and restore things when they exit. To make everything work
+ * properly we save the current point (in shpos and svpos), set our position to
+ * (0, 0), and restore things at the end of the subroutine definition. That means
+ * hpos and vpos measure the relative displacement after a subroutine returns, and
+ * we save those values in the displacement[] array. The displacements are used
+ * (in subr_call()) to properly adjust our position after each subroutine call,
+ * and all subroutines are called with the current x and y coordinates on top of
+ * the stack.
+ *
+ */
+
+ if ( in_subr == TRUE ) /* a nested subroutine definition?? */
+ error(FATAL, "can't handle nested subroutine definitions");
+
+ if ( (subr_id = get_data()) == EOF )
+ error(FATAL, "missing subroutine identifier");
+
+ if ( in_global == FALSE ) { /* just used to reduce file size some */
+ fprintf(fp_out, "cleartomark\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s", BEGINGLOBAL);
+ in_global = TRUE;
+ } /* End if */
+
+ fprintf(fp_out, "/S%d {\n", subr_id);
+ fprintf(fp_out, "gsave translate\n");
+
+ shpos = hpos; /* save our current position */
+ svpos = vpos;
+
+ hgoto(0); /* start at the origin */
+ vgoto(0);
+
+ in_subr = TRUE; /* in a subroutine definition */
+
+} /* End of subr_def */
+
+/*****************************************************************************/
+
+subr_end()
+
+{
+
+ int ch; /* for looking at next opcode */
+
+/*
+ *
+ * Handles stuff needed at the end of each subroutine. Want to remember the change
+ * in horizontal and vertical positions for each subroutine so we can adjust our
+ * position after each call - just in case. The current position was set to (0, 0)
+ * before we started the subroutine definition, so when we get here hpos and vpos
+ * are the relative displacements after the subroutine is called. They're saved in
+ * the displacement[] array and used to adjust the current position when we return
+ * from a subroutine.
+ *
+ */
+
+ if ( in_subr == FALSE ) /* not in a subroutine definition?? */
+ error(FATAL, "subroutine end without corresponding start");
+
+ fprintf(fp_out, "grestore\n");
+ fprintf(fp_out, "} def\n");
+
+ if ( in_global == TRUE && (ch = get_char()) != BSUB ) {
+ fprintf(fp_out, "%s", ENDGLOBAL);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ in_global = FALSE;
+ } /* End if */
+
+ ungetc(ch, fp_in); /* put back the next opcode */
+
+ displacement[subr_id].dx = hpos;
+ displacement[subr_id].dy = vpos;
+
+ hgoto(shpos); /* back to where we started */
+ vgoto(svpos);
+
+ in_subr = FALSE; /* done with the definition */
+
+} /* End of subr_end */
+
+/*****************************************************************************/
+
+subr_call()
+
+{
+
+ int ch; /* next byte from *fp_in */
+ int id; /* subroutine id if ch wasn't an opcode */
+
+/*
+ *
+ * Handles subroutine calls. Everything that follows the BCALL opcode (up to the
+ * next opcode) is taken as a subroutine identifier - thus the loop that generates
+ * the subroutine calls.
+ *
+ */
+
+ while ( (ch = get_char()) != EOF && (ch & MSB) ) {
+ id = ch & DMASK;
+ fprintf(fp_out, "%d %d S%d\n", hpos, vpos, id);
+
+ hgoto(hpos + displacement[id].dx); /* adjust our position */
+ vgoto(vpos + displacement[id].dy);
+ } /* End while */
+
+ ungetc(ch, fp_in);
+
+} /* End of subr_call */
+
+/*****************************************************************************/
+
+vector(var, mode)
+
+ int var; /* coordinate that varies next? */
+ int mode; /* VISIBLE or INVISIBLE vectors */
+
+{
+
+ int ch; /* next character from *fp_in */
+ int x, y; /* line drawn to this point */
+ int count = 0; /* number of points so far */
+
+/*
+ *
+ * Handles plotting of all types of BGI vectors. If it's a manhattan vector var
+ * specifies which coordinate will be changed by the next number in the input
+ * file.
+ *
+ */
+
+ x = hpos; /* set up the first point */
+ y = vpos;
+
+ while ( (ch = get_char()) != EOF && ch & MSB ) {
+ if ( var == X_COORD ) /* next length is change in x */
+ x += get_int(ch);
+ else if ( var == Y_COORD ) /* it's the change in y */
+ y += get_int(ch);
+ else if ( var == LONGVECTOR ) { /* long vector */
+ x += get_int(ch);
+ y += get_int(0);
+ } else { /* must be a short vector */
+ x += ((ch & MSBMAG) * ((ch & SGNB) ? -1 : 1));
+ y += (((ch = get_data()) & MSBMAG) * ((ch & SGNB) ? -1 : 1));
+ } /* End else */
+
+ if ( mode == VISIBLE ) { /* draw the line segment */
+ fprintf(fp_out, "%d %d\n", hpos - x, vpos - y);
+ count++;
+ } /* End if */
+
+ hgoto(x); /* adjust the current BGI position */
+ vgoto(y);
+
+ if ( var == X_COORD ) /* vertical length comes next */
+ var = Y_COORD;
+ else if ( var == Y_COORD ) /* change horizontal next */
+ var = X_COORD;
+ } /* End while */
+
+ if ( count > 0 )
+ fprintf(fp_out, "%d %d v\n", hpos, vpos);
+
+ ungetc(ch, fp_in); /* it wasn't part of the vector */
+ position--;
+
+} /* End of vector */
+
+/*****************************************************************************/
+
+rectangle(mode)
+
+ int mode; /* FILL or OUTLINE the rectangle */
+
+{
+
+ int deltax; /* displacement for horizontal side */
+ int deltay; /* same but for vertical sides */
+
+/*
+ *
+ * Draws a rectangle and either outlines or fills it, depending on the value of
+ * mode. Would be clearer, and perhaps better, if {stroke} or {fill} were put on
+ * the stack instead of 0 or 1. R could then define the path and just do an exec
+ * to fill or stroke it.
+ *
+ */
+
+ deltax = get_int(0); /* get the height and width */
+ deltay = get_int(0);
+
+ if ( mode == OUTLINE )
+ fprintf(fp_out, "0 %d %d %d %d R\n", deltax, deltay, hpos, vpos);
+ else fprintf(fp_out, "1 %d %d %d %d R\n", deltax, deltay, hpos, vpos);
+
+} /* End of rectangle */
+
+/*****************************************************************************/
+
+trapezoid()
+
+{
+
+ int kind; /* which sides are parallel */
+ int d[6]; /* true displacements - depends on kind */
+
+/*
+ *
+ * Handles filled trapeziods. A data byte of 0101 following the opcode means the
+ * horizontal sides are parallel, 0102 means the vertical sides are parallel.
+ * Filling is handled by eofill so we don't need to get things in the right order.
+ *
+ */
+
+ kind = get_data();
+
+ d[0] = get_int(0);
+ d[1] = 0;
+ d[2] = get_int(0);
+ d[3] = get_int(0);
+ d[4] = get_int(0);
+ d[5] = 0;
+
+ if ( kind == 2 ) { /* parallel sides are vertical */
+ d[1] = d[0];
+ d[0] = 0;
+ d[5] = d[4];
+ d[4] = 0;
+ } /* End if */
+
+ fprintf(fp_out, "%d %d %d %d %d %d %d %d T\n", d[4], d[5], d[2], d[3], d[0], d[1], hpos, vpos);
+
+} /* End of trapezoid */
+
+/*****************************************************************************/
+
+point_plot(mode, ch)
+
+ int mode; /* plotting mode BPOINT or BPOINT1 */
+ int ch; /* will be placed at the points */
+
+{
+
+ int c; /* next character from input file */
+ int x, y; /* ch gets put here next */
+ int deltax; /* x increment for BPOINT1 mode */
+
+/*
+ *
+ * The two point plot modes are used to place a character at selected points. The
+ * difference in the two modes, namely BPOINT and BPOINT1, is the way we get the
+ * coordinates of the next point. In BPOINT1 the two bytes immediately following
+ * ch select a constant horizontal change, while both coordinates are given for
+ * all points in BPOINT mode.
+ *
+ */
+
+ if ( mode == BPOINT1 ) { /* first integer is change in x */
+ deltax = get_int(0);
+ x = hpos - deltax;
+ } /* End if */
+
+ while ( (c = get_char()) != EOF && (c & MSB) ) {
+ if ( mode == BPOINT1 ) { /* only read y coordinate */
+ y = get_int(c);
+ x += deltax;
+ } else { /* get new x and y from input file */
+ x = get_int(c);
+ y = get_int(0);
+ } /* End else */
+
+ hgoto(x); /* adjust BGI position */
+ vgoto(y);
+
+ fprintf(fp_out, "%d %d\n", hpos, vpos);
+ } /* End while */
+
+ putc('(', fp_out);
+
+ switch ( ch ) {
+ case '(':
+ case ')':
+ case '\\':
+ putc('\\', fp_out);
+
+ default:
+ putc(ch, fp_out);
+ } /* End switch */
+
+ fprintf(fp_out, ")pp\n");
+
+ ungetc(c, fp_in); /* it wasn't part of the point plot */
+ position--;
+
+} /* End of point_plot */
+
+/*****************************************************************************/
+
+line_plot()
+
+{
+
+ int c; /* next input character from fp_in */
+ int deltax; /* change in x coordinate */
+ int x0, y0; /* starting point for next segment */
+ int x1, y1; /* endpoint of the line */
+ int count = 0; /* number of points so far */
+
+/*
+ *
+ * Essentially the same format as BPOINT1, except that in this case we connect
+ * pairs of points by line segments.
+ *
+ */
+
+ deltax = get_int(0); /* again the change in x is first */
+
+ x1 = hpos; /* so it works first time through */
+ y1 = get_int(0);
+
+ while ( (c = get_char()) != EOF && (c & MSB) ) {
+ x0 = x1; /* line starts here */
+ y0 = y1;
+
+ x1 += deltax; /* and ends at this point */
+ y1 = get_int(c);
+
+ fprintf(fp_out, "%d %d\n", -deltax, y0 - y1);
+ count++;
+ } /* End while */
+
+ hgoto(x1); /* adjust current BGI position */
+ vgoto(y1);
+
+ if ( count > 0 )
+ fprintf(fp_out, "%d %d v\n", hpos, vpos);
+
+ ungetc(c, fp_in); /* wasn't part of the line */
+ position--;
+
+} /* End of line_plot */
+
+/*****************************************************************************/
+
+arc(mode)
+
+ int mode; /* FILL or OUTLINE the path */
+
+{
+
+ int dx1, dy1; /* displacements for first point */
+ int dx2, dy2; /* same for the second point */
+ int radius; /* of the arc */
+ int angle1, angle2; /* starting and ending angles */
+
+/*
+ *
+ * Called whenever we need to draw an arc. I'm ignoring filled slices for now.
+ *
+ */
+
+ dx1 = get_int(0); /* displacements relative to center */
+ dy1 = get_int(0);
+ dx2 = get_int(0);
+ dy2 = get_int(0);
+
+ radius = get_int(0); /* and the radius */
+
+ if ( radius == 0 ) /* nothing to do */
+ return;
+
+ angle1 = (atan2((double) dy1, (double) dx1) * 360) / (2 * PI) + .5;
+ angle2 = (atan2((double) dy2, (double) dx2) * 360) / (2 * PI) + .5;
+
+ fprintf(fp_out, "%d %d %d %d %d arcn stroke\n", hpos, vpos, radius, angle1, angle2);
+
+} /* End of arc */
+
+/*****************************************************************************/
+
+pattern()
+
+{
+
+ double red = 0; /* color components */
+ double green = 0;
+ double blue = 0;
+ int kind; /* corse or fine pattern */
+ int val; /* next color data byte */
+ int i; /* loop index */
+
+/*
+ *
+ * Handles patterns by setting the current color based of the values assigned to
+ * the next four data bytes. BGI supports two kinds of patterns (fine or coarse)
+ * but I'm handling each in the same way - for now. In a fine pattern the four
+ * data bytes assign a color to four individual pixels (upperleft first) while
+ * in a coarse pattern the four colors are assigned to groups of four pixels,
+ * for a total of 16. Again the first color goes to the group in the upper left
+ * corner. The byte immediately following the BPAT opcode selects fine (040) or
+ * coarse (041) patterns. The PostScript RGB color is assigned by averaging the
+ * RED, GREEN, and BLUE components assigned to the four pixels (or groups of
+ * pixels). Acceptable results, but there's no distinction between fine and
+ * coarse patterns.
+ *
+ */
+
+ if ( (kind = get_char()) == EOF )
+ error(FATAL, "bad pattern command");
+
+ for ( i = 0; i < 4; i++ ) {
+ val = get_data();
+ red += get_color(val, RED);
+ green += get_color(val, GREEN);
+ blue += get_color(val, BLUE);
+ } /* End for */
+
+ fprintf(fp_out, "%g %g %g c\n", red/4, green/4, blue/4);
+
+} /* End of pattern */
+
+/*****************************************************************************/
+
+get_color(val, component)
+
+ int val; /* color data byte */
+ int component; /* RED, GREEN, or BLUE component */
+
+{
+
+
+ int primary; /* color mixing mode - bits 2 to 4 */
+ int plane; /* primary color plane - bits 5 to 7 */
+ unsigned rgbcolor; /* PostScript expects an RGB triple */
+
+/*
+ *
+ * Picks the requested color component (RED, GREEN, or BLUE) from val and returns
+ * the result to the caller. BGI works with Cyan, Yellow, and Magenta so the one's
+ * complement stuff (following the exclusive or'ing) recovers the RED, BLUE, and
+ * GREEN components that PostScript's setrgbcolor operator needs. The PostScript
+ * interpreter in the ColorScript 100 has a setcmycolor operator, but it's not
+ * generally available so I've decided to stick with setrgbcolor.
+ *
+ */
+
+ primary = (val >> 3) & 07;
+ plane = val & 07;
+ rgbcolor = (~(primary ^ plane)) & 07;
+
+ if ( debug == ON )
+ fprintf(stderr, "val = %o, primary = %o, plane = %o, rgbcolor = %o\n",
+ val, primary, plane, rgbcolor);
+
+ switch ( component ) {
+ case RED:
+ return(rgbcolor>>2);
+
+ case GREEN:
+ return(rgbcolor&01);
+
+ case BLUE:
+ return((rgbcolor>>1)&01);
+
+ default:
+ error(FATAL, "unknown color component");
+ return(0);
+ } /* End switch */
+
+} /* End of get_color */
+
+/*****************************************************************************/
+
+set_color(val)
+
+ int val; /* color data byte */
+
+{
+
+/*
+ *
+ * Arranges to have the color set to the value requested in the BGI data byte val.
+ *
+ */
+
+ fprintf(fp_out, "%d %d %d c\n", get_color(val, RED), get_color(val, GREEN), get_color(val, BLUE));
+
+} /* End of set_color */
+
+/*****************************************************************************/
+
+get_int(highbyte)
+
+ int highbyte; /* already read this byte */
+
+{
+
+ int lowbyte; /* this and highbyte make the int */
+
+/*
+ *
+ * Figures out the value on the integer (sign magnitude form) that's next in the
+ * input file. If highbyte is nonzero we'll use it and the next byte to build the
+ * integer, otherwise two bytes are read from fp_in.
+ *
+ */
+
+
+ if ( highbyte == 0 ) /* need to read the first byte */
+ highbyte = get_data();
+
+ lowbyte = get_data(); /* always need the second byte */
+
+ return(highbyte & SGNB ? -MAG(highbyte, lowbyte) : MAG(highbyte, lowbyte));
+
+} /* End of get_int */
+
+/*****************************************************************************/
+
+get_data()
+
+{
+
+ int val; /* data value returned to caller */
+
+/*
+ *
+ * Called when we expect to find a single data character in the input file. The
+ * data bit is turned off and the resulting value is returned to the caller.
+ *
+ */
+
+ if ( (val = get_char()) == EOF || ! (val & MSB) )
+ error(FATAL, "missing data value");
+
+ return(val & DMASK);
+
+} /* End of get_data */
+
+/*****************************************************************************/
+
+get_char()
+
+{
+
+ int ch; /* character we just read */
+
+/*
+ *
+ * Reads the next character from file *fp_in and returns the value to the caller.
+ * This routine isn't really needed, but we may want to deal directly with some
+ * screwball file formats so I thought it would probably be a good idea to isolate
+ * all the input in one routine that could be easily changed.
+ *
+ */
+
+ if ( (ch = getc(fp_in)) != EOF ) {
+ position++;
+ ch &= CHMASK;
+ } /* End if */
+
+ if ( debug == ON )
+ fprintf(stderr, "%o ", ch);
+
+ return(ch);
+
+} /* End of get_char */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postbgi/postbgi.h b/sys/src/cmd/postscript/postbgi/postbgi.h
new file mode 100755
index 000000000..41e3003c1
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/postbgi.h
@@ -0,0 +1,203 @@
+/*
+ *
+ * BGI opcodes.
+ *
+ */
+
+#define BRCHAR 033 /* rotated character mode */
+#define BCHAR 034 /* graphical character mode */
+#define BGRAPH 035 /* graphical master mode */
+
+#define BSUB 042 /* subroutine definition */
+#define BRET 043 /* end of subroutine */
+#define BCALL 044 /* subroutine call */
+
+#define BEND 045 /* end page */
+#define BERASE 046 /* erase - obsolete */
+#define BREP 047 /* repeat */
+#define BENDR 050 /* end repeat */
+
+#define BSETX 051 /* set horizontal position */
+#define BSETY 052 /* set vertical position */
+#define BSETXY 053 /* set horizontal and vertical positions */
+#define BINTEN 054 /* intensify - mark current pixel */
+
+#define BVISX 055 /* manhattan vector - change x first */
+#define BINVISX 056 /* same as BVISX but nothing drawn */
+#define BVISY 057 /* manhattan vector - change y first */
+#define BINVISY 060 /* same as BVISY but nothing drawn */
+
+#define BVEC 061 /* arbitrary long vector */
+#define BSVEC 062 /* arbitrary short vector */
+#define BRECT 063 /* outline rectangle */
+#define BPOINT1 064 /* point plot - mode 1 */
+#define BPOINT 065 /* point plot - mode 2 */
+#define BLINE 066 /* line plot */
+
+#define BCSZ 067 /* set character size */
+#define BLTY 070 /* select line type */
+#define BARC 071 /* draw circular arc */
+#define BFARC 072 /* filled circular arc */
+#define BFRECT 073 /* filled rectangle */
+#define BRASRECT 074 /* raster rectangle */
+#define BCOL 075 /* select color */
+#define BFTRAPH 076 /* filled trapezoid */
+#define BPAT 077 /* pattern are for filling - no info */
+
+#define BNOISE 0 /* from bad file format */
+
+/*
+ *
+ * Character size is controlled by the spacing of dots in a 5x7 dot matrix, which
+ * by default is set to BGISIZE.
+ *
+ */
+
+#define BGISIZE 2 /* default character grid spacing */
+
+/*
+ *
+ * Definitions used to decode the bytes read from a BGI file.
+ *
+ */
+
+#define CHMASK 0177 /* characters only use 7 bits */
+#define DMASK 077 /* data values use lower 6 bits */
+#define MSB 0100 /* used to check for data or opcode */
+#define SGNB 040 /* sign bit for integers */
+#define MSBMAG 037 /* mag of most sig byte in a BGI int */
+
+/*
+ *
+ * Descriptions of BGI vectors and what's done when they're drawn.
+ *
+ */
+
+#define X_COORD 0 /* change x next in manhattan vector */
+#define Y_COORD 1 /* same but y change comes next */
+#define LONGVECTOR 2 /* arbitrary long vector */
+#define SHORTVECTOR 3 /* components given in 6 bits */
+
+#define VISIBLE 0 /* really draw the vector */
+#define INVISIBLE 1 /* just move the current position */
+
+/*
+ *
+ * What's done with a closed path.
+ *
+ */
+
+#define OUTLINE 0 /* outline the defined path */
+#define FILL 1 /* fill it in */
+
+/*
+ *
+ * BGI line style definitions. They're used as an index into the STYLES array,
+ * which really belongs in the prologue.
+ *
+ */
+
+#define SOLID 0
+#define DOTTED 1
+#define SHORTDASH 2
+#define DASH 3
+#define LONGDASH 4
+#define DOTDASH 5
+#define THREEDOT 6
+
+#define STYLES \
+ \
+ { \
+ "[]", \
+ "[.5 2]", \
+ "[2 4]", \
+ "[4 4]", \
+ "[8 4]", \
+ "[.5 2 4 2]", \
+ "[.5 2 .5 2 .5 2 4 2]" \
+ }
+
+/*
+ *
+ * Three constants used to choose which component (RED, GREEN, or BLUE) we're
+ * interested in. BGI colors are specified as a single data byte and pulling a
+ * particular component out of the BGI color byte is handled by procedure
+ * get_color().
+ *
+ */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+/*
+ *
+ * An array of type Disp is used to save the horizontal and vertical displacements
+ * that result after a subroutine has been called. Needed so we can properly adjust
+ * our horizontal and vertical positions after a subroutine call. Entries are made
+ * immediately after a subroutine is defined and used after the call. Subroutine
+ * names are integers that range from 0 to 63 (assigned in the BG file) and the
+ * name is used as an index into the Disp array when we save or retrieve the
+ * displacement.
+ *
+ */
+
+typedef struct {
+ int dx; /* horizontal and */
+ int dy; /* vertical displacements */
+} Disp;
+
+/*
+ *
+ * An array of type Fontmap helps convert font names requested by users into
+ * legitimate PostScript names. The array is initialized using FONTMAP, which must
+ * end with and entry that has NULL defined as its name field.
+ *
+ */
+
+typedef struct {
+ char *name; /* user's font name */
+ char *val; /* corresponding PostScript name */
+} Fontmap;
+
+#define FONTMAP \
+ \
+ { \
+ "R", "Courier", \
+ "I", "Courier-Oblique", \
+ "B", "Courier-Bold", \
+ "CO", "Courier", \
+ "CI", "Courier-Oblique", \
+ "CB", "Courier-Bold", \
+ "CW", "Courier", \
+ "PO", "Courier", \
+ "courier", "Courier", \
+ "cour", "Courier", \
+ "co", "Courier", \
+ NULL, NULL \
+ }
+
+/*
+ *
+ * Two macros that are useful in processing BGI files:
+ *
+ * MAG(A, B) - Takes bytes A and B which have been read from a BGI file
+ * and returns the magnitude of the integer represented by
+ * the two bytes.
+ *
+ * LINESPACE(A) - Takes BGI size A and returns the number of address units
+ * that can be used for a reasonable interline spacing.
+ *
+ */
+
+#define MAG(A, B) (((A & MSBMAG) << 6) | (B & DMASK))
+#define LINESPACE(A) (8 * A)
+
+/*
+ *
+ * Some of the non-integer valued functions in postdmd.c.
+ *
+ */
+
+char *get_font();
+
diff --git a/sys/src/cmd/postscript/postbgi/postbgi.mk b/sys/src/cmd/postscript/postbgi/postbgi.mk
new file mode 100755
index 000000000..24ad6b27d
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/postbgi.mk
@@ -0,0 +1,93 @@
+MAKE=/bin/make
+MAKEFILE=postbgi.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+HFILES=postbgi.h\
+ $(COMMONDIR)/comments.h\
+ $(COMMONDIR)/ext.h\
+ $(COMMONDIR)/gen.h\
+ $(COMMONDIR)/path.h
+
+OFILES=postbgi.o\
+ $(COMMONDIR)/glob.o\
+ $(COMMONDIR)/misc.o\
+ $(COMMONDIR)/request.o
+
+all : postbgi
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ @if [ ! -d "$(POSTLIB)" ]; then \
+ mkdir $(POSTLIB); \
+ chmod 755 $(POSTLIB); \
+ chgrp $(GROUP) $(POSTLIB); \
+ chown $(OWNER) $(POSTLIB); \
+ fi
+ cp postbgi $(POSTBIN)/postbgi
+ @chmod 755 $(POSTBIN)/postbgi
+ @chgrp $(GROUP) $(POSTBIN)/postbgi
+ @chown $(OWNER) $(POSTBIN)/postbgi
+ cp postbgi.ps $(POSTLIB)/postbgi.ps
+ @chmod 644 $(POSTLIB)/postbgi.ps
+ @chgrp $(GROUP) $(POSTLIB)/postbgi.ps
+ @chown $(OWNER) $(POSTLIB)/postbgi.ps
+ cp postbgi.1 $(MAN1DIR)/postbgi.1
+ @chmod 644 $(MAN1DIR)/postbgi.1
+ @chgrp $(GROUP) $(MAN1DIR)/postbgi.1
+ @chown $(OWNER) $(MAN1DIR)/postbgi.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postbgi
+
+postbgi : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postbgi $(OFILES) -lm
+
+postbgi.o : $(HFILES)
+
+$(COMMONDIR)/glob.o\
+$(COMMONDIR)/misc.o\
+$(COMMONDIR)/request.o :
+ @cd $(COMMONDIR); $(MAKE) -f common.mk `basename $@`
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ postbgi.1 >XXX.1; \
+ mv XXX.1 postbgi.1
+
diff --git a/sys/src/cmd/postscript/postbgi/postbgi.ps b/sys/src/cmd/postscript/postbgi/postbgi.ps
new file mode 100755
index 000000000..1fa486f2a
--- /dev/null
+++ b/sys/src/cmd/postscript/postbgi/postbgi.ps
@@ -0,0 +1,135 @@
+%
+% Version 3.3.2 prologue for BGI files - STARE or PRISM.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/fixlinewidth true def
+/fixscreen false def
+/font /Courier def
+/formsperpage 1 def
+/landscape false def
+/linewidth 0 def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/prism false def
+/resolution 128 def
+/rotation 1 def
+/scaletodevice false def
+/screenheight 1280 def
+/screenwidth 1024 def
+/xoffset 0 def
+/yoffset 0 def
+
+/devres 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/kshow {kshow} bind def % so later references don't bind
+/show {show} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+ prism {/fixscreen true def /scaletodevice true def} if
+ prism linewidth 0 eq and {/linewidth .3 def} if
+ fixscreen {devres 4 div orientation currentscreen 3 1 roll pop pop setscreen} if
+
+ pagedimensions
+ /scaling
+ scaletodevice
+ {devres resolution div truncate 72 mul devres div dup}
+ {height margin sub screenheight div width margin sub screenwidth div}
+ ifelse
+ min def
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ magnification dup aspectratio mul scale
+ scaling scaling scale
+ screenwidth 2 div neg screenheight 2 div neg translate
+
+ tietodevicespace
+ linewidth scaling div setlinewidth
+ 1 setlinecap
+ newpath
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/tietodevicespace {
+ fixlinewidth linewidth 0 gt and linewidth 1 lt and {
+ /moveto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ moveto
+ } bind def
+ /lineto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ lineto
+ } bind def
+ /rlineto {Y add exch X add exch lineto} bind def
+ /v V 0 get bind def
+ } if
+} def
+
+/V [{moveto counttomark 2 idiv {rlineto} repeat stroke}] def
+/v V 0 get bind def
+/p {linewidth 2 div 0 360 arc fill} bind def
+/pp {/ch exch def counttomark 2 idiv {moveto xc yc rmoveto ch show} repeat} bind def
+
+/l {{scaling div} forall counttomark array astore 0 setdash} bind def
+/c {setrgbcolor} bind def
+
+/T {newpath moveto rlineto rlineto rlineto closepath eofill} bind def
+
+/R {
+ newpath moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath
+ 0 eq {stroke} {eofill} ifelse
+} bind def
+
+/f {
+ dup dup
+ /charwidth exch 6 mul def
+ /xc exch -2.5 mul def
+ /yc exch -3.5 mul def
+ font findfont charwidth .6 div scalefont setfont
+} bind def
+
+/t {
+ /str exch def
+ gsave
+ translate rotate
+ xc yc moveto
+ currentpoint
+ {
+ pop pop
+ exch charwidth add exch
+ moveto currentpoint
+ } str kshow
+ pop pop
+ grestore
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/postdaisy/Opostdaisy.c b/sys/src/cmd/postscript/postdaisy/Opostdaisy.c
new file mode 100755
index 000000000..a440fd604
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/Opostdaisy.c
@@ -0,0 +1,1222 @@
+/*
+ *
+ * postdaisy - PostScript translator for Diablo 1640 files.
+ *
+ * A program that translates Diablo 1640 files into PostScript. Absolutely nothing
+ * is guaranteed. Quite a few things haven't been implemented, and what's been
+ * done isn't well tested. Most of the documentation used to write this program
+ * was taken from the 'Diablo Emulator' section of a recent Imagen manual.
+ *
+ * Some of document comments that are generated may not be right. Most of the test
+ * files I used produced a trailing blank page. I've put a check in formfeed() that
+ * won't print the last page if it doesn't contain any text, but PAGES comments may
+ * not be right. The DOCUMENTFONTS comment will also be wrong if auto underline or
+ * bold printing have been turned on by escape commands.
+ *
+ * The brute force approach used to implement horizontal and vertical tabs leaves
+ * much to be desired, and may not work for very small initial hmi and vmi values.
+ * At the very least I should have used malloc() to get space for the two tabstop
+ * arrays after hmi and vmi are known!
+ *
+ * Reverse printing mode hasn't been tested at all, but what's here should be
+ * close even though it's not efficient.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how this program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects to
+ * find the current page number on the stack.
+ *
+ * t
+ *
+ * mark str1 x1 str2 x2 ... strn xn y hmi t mark
+ *
+ * Handles all the text on the stack. Characters in the strings are
+ * printed using hmi as the character advance, and all strings are at
+ * vertical position y. Each string is begins at the horizontal position
+ * that preceeds it.
+ *
+ * f
+ *
+ * font f -
+ *
+ * Use font f, where f is the full PostScript font name. Only used when
+ * we switch to auto underline (Courier-Italic) or bold (Courier-Bold)
+ * printing.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+#include "postdaisy.h" /* a few special definitions */
+
+char *optnames = "a:c:f:h:l:m:n:o:p:r:s:v:x:y:A:C:E:J:L:P:DI";
+
+char *prologue = POSTDAISY; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+char htabstops[COLUMNS]; /* horizontal */
+char vtabstops[ROWS]; /* and vertical tabs */
+
+int res = RES; /* input file resolution - sort of */
+
+int hmi = HMI; /* horizontal motion index - 1/120 inch */
+int vmi = VMI; /* vertical motion index - 1/48 inch */
+int ohmi = HMI; /* original hmi */
+int ovmi = VMI; /* and vmi - for tabs and char size */
+
+int hpos = 0; /* current horizontal */
+int vpos = 0; /* and vertical position */
+
+int lastx = -1; /* printer's last horizontal */
+int lasty = -1; /* and vertical position */
+int lasthmi = -1; /* hmi for current text strings */
+
+int lastc = -1; /* last printed character */
+int prevx = -1; /* at this position */
+
+int leftmargin = LEFTMARGIN; /* page margins */
+int rightmargin = RIGHTMARGIN;
+int topmargin = TOPMARGIN;
+int bottommargin = BOTTOMMARGIN;
+
+int stringcount = 0; /* number of strings on the stack */
+int stringstart = 1; /* column where current one starts */
+int advance = 1; /* -1 if in backward print mode */
+
+int lfiscr = OFF; /* line feed implies carriage return */
+int crislf = OFF; /* carriage return implies line feed */
+
+int linespp = 0; /* lines per page if it's positive */
+int markedpage = FALSE; /* helps prevent trailing blank page */
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+int shadowprint = OFF; /* automatic bold printing if ON */
+
+FILE *fp_in; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that translates Diablo 1640 files into PostScript. Nothing
+ * is guaranteed - the program not well tested and doesn't implement everything.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+ int interrupt(); /* signal handler */
+
+/*
+ *
+ * Makes sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ if ( DOROUND )
+ cat(ROUNDPAGE);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ int n; /* for CR and LF modes */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ * Although any PostScript font can be used, things will only work for constant
+ * width fonts.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f': /* use this PostScript font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'h': /* default character spacing */
+ ohmi = hmi = atoi(optarg) * HSCALE;
+ fprintf(stdout, "/hmi %s def\n", optarg);
+ break;
+
+ case 'l': /* lines per page */
+ linespp = atoi(optarg);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'r': /* set CR and LF modes */
+ n = atoi(optarg);
+ if ( n & 01 )
+ lfiscr = ON;
+ else lfiscr = OFF;
+ if ( n & 02 )
+ crislf = ON;
+ else crislf = OFF;
+ break;
+
+ case 's': /* point size */
+ fprintf(stdout, "/pointsize %s def\n", optarg);
+ break;
+
+ case 'v': /* default line spacing */
+ ovmi = vmi = atoi(optarg) * VSCALE;
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font, although things will only work well for constant width
+ * fonts.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ fp_in = stdin;
+
+ if ( argc < 1 )
+ text();
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ text();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages, make sure the
+ * last page is printed, and restore the initial environment.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
+ * is requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+text()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Translates the next input file into PostScript. The redirect(-1) call forces
+ * the initial output to go to /dev/null - so the stuff formfeed() does at the
+ * end of each page doesn't go to stdout.
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ formfeed(); /* force PAGE comment etc. */
+ inittabs();
+
+ while ( (ch = getc(fp_in)) != EOF )
+ switch ( ch ) {
+ case '\010': /* backspace */
+ backspace();
+ break;
+
+ case '\011': /* horizontal tab */
+ htab();
+ break;
+
+ case '\012': /* new line */
+ linefeed();
+ break;
+
+ case '\013': /* vertical tab */
+ vtab();
+ break;
+
+ case '\014': /* form feed */
+ formfeed();
+ break;
+
+ case '\015': /* carriage return */
+ carriage();
+ break;
+
+ case '\016': /* extended character set - SO */
+ break;
+
+ case '\017': /* extended character set - SI */
+ break;
+
+ case '\031': /* next char from supplementary set */
+ break;
+
+ case '\033': /* 2 or 3 byte escape sequence */
+ escape();
+ break;
+
+ default:
+ if ( isascii(ch) && isprint(ch) )
+ oput(ch);
+ break;
+ } /* End switch */
+
+ formfeed(); /* next file starts on a new page? */
+
+} /* End of text */
+
+/*****************************************************************************/
+
+inittabs()
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Initializes the horizontal and vertical tab arrays. The way tabs are handled is
+ * quite inefficient and may not work for all initial hmi or vmi values.
+ *
+ */
+
+ for ( i = 0; i < COLUMNS; i++ )
+ htabstops[i] = ((i % 8) == 0) ? ON : OFF;
+
+ for ( i = 0; i < ROWS; i++ )
+ vtabstops[i] = ((i * ovmi) > BOTTOMMARGIN) ? ON : OFF;
+
+} /* End of inittabs */
+
+/*****************************************************************************/
+
+cleartabs()
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Clears all horizontal and vertical tab stops.
+ *
+ */
+
+ for ( i = 0; i < ROWS; i++ )
+ htabstops[i] = OFF;
+
+ for ( i = 0; i < COLUMNS; i++ )
+ vtabstops[i] = OFF;
+
+} /* End of cleartabs */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+/*
+ *
+ * Called whenever we've finished with the last page and want to get ready for the
+ * next one. Also used at the beginning and end of each input file, so we have to
+ * be careful about what's done. I've added a simple test before the showpage that
+ * should eliminate the extra blank page that was put out at the end of many jobs,
+ * but the PAGES comments may be wrong.
+ *
+ */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ endline(); /* print the last line */
+
+ fprintf(fp_out, "cleartomark\n");
+ if ( feof(fp_in) == 0 || markedpage == TRUE )
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+
+ vgoto(topmargin);
+ hgoto(leftmargin);
+
+ markedpage = FALSE;
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+linefeed()
+
+{
+
+ int line = 0; /* current line - based on ovmi */
+
+/*
+ *
+ * Adjust our current vertical position. If we've passed the bottom of the page
+ * or exceeded the number of lines per page, print it and go to the upper left
+ * corner of the next page. This routine is also called from carriage() if crislf
+ * is ON.
+ *
+ */
+
+ vmot(vmi);
+
+ if ( lfiscr == ON )
+ hgoto(leftmargin);
+
+ if ( linespp > 0 ) /* means something so see where we are */
+ line = vpos / ovmi + 1;
+
+ if ( vpos > bottommargin || line > linespp )
+ formfeed();
+
+} /* End of linefeed */
+
+/*****************************************************************************/
+
+carriage()
+
+{
+
+/*
+ *
+ * Handles carriage return character. If crislf is ON we'll generate a line feed
+ * every time we get a carriage return character.
+ *
+ */
+
+ if ( shadowprint == ON ) /* back to normal mode */
+ changefont(fontname);
+
+ advance = 1;
+ shadowprint = OFF;
+
+ hgoto(leftmargin);
+
+ if ( crislf == ON )
+ linefeed();
+
+} /* End of carriage */
+
+/*****************************************************************************/
+
+htab()
+
+{
+
+ int col; /* 'column' we'll be at next */
+ int i; /* loop index */
+
+/*
+ *
+ * Tries to figure out where the next tab stop is. Wasn't positive about this
+ * one, since hmi can change. I'll assume columns are determined by the original
+ * value of hmi. That fixes them on the page, which seems to make more sense than
+ * letting them float all over the place.
+ *
+ */
+
+ endline();
+
+ col = hpos/ohmi + 1;
+ for ( i = col; i < ROWS; i++ )
+ if ( htabstops[i] == ON ) {
+ col = i;
+ break;
+ } /* End if */
+
+ hgoto(col * ohmi);
+ lastx = hpos;
+
+} /* End of htab */
+
+/*****************************************************************************/
+
+vtab()
+
+{
+
+ int line; /* line we'll be at next */
+ int i; /* loop index */
+
+/*
+ *
+ * Looks for the next vertical tab stop in the vtabstops[] array and moves to that
+ * line. If we don't find a tab we'll just move down one line - shouldn't happen.
+ *
+ */
+
+ endline();
+
+ line = vpos/ovmi + 1;
+ for ( i = line; i < COLUMNS; i++ )
+ if ( vtabstops[i] == ON ) {
+ line = i;
+ break;
+ } /* End if */
+
+ vgoto(line * ovmi);
+
+} /* End of vtab */
+
+/*****************************************************************************/
+
+backspace()
+
+{
+
+/*
+ *
+ * Moves backwards a distance equal to the current value of hmi, but don't go
+ * past the left margin.
+ *
+ */
+
+ endline();
+
+ if ( hpos - leftmargin >= hmi )
+ hmot(-hmi);
+ else hgoto(leftmargin); /* maybe just ignore the backspace?? */
+
+ lastx = hpos;
+
+} /* End of backspace */
+
+/*****************************************************************************/
+
+escape()
+
+{
+
+ int ch; /* control character */
+
+/*
+ *
+ * Handles special codes that are expected to follow an escape character. The
+ * initial escape character is followed by one or two bytes.
+ *
+ */
+
+ switch ( ch = getc(fp_in) ) {
+ case 'T': /* top margin */
+ topmargin = vpos;
+ break;
+
+ case 'L': /* bottom margin */
+ bottommargin = vpos;
+ break;
+
+ case 'C': /* clear top and bottom margins */
+ bottommargin = BOTTOMMARGIN;
+ topmargin = TOPMARGIN;
+ break;
+
+ case '9': /* left margin */
+ leftmargin = hpos;
+ break;
+
+ case '0': /* right margin */
+ rightmargin = hpos;
+ break;
+
+ case '1': /* set horizontal tab */
+ htabstops[hpos/ohmi] = ON;
+ break;
+
+ case '8': /* clear horizontal tab at hpos */
+ htabstops[hpos/ohmi] = OFF;
+ break;
+
+ case '-': /* set vertical tab */
+ vtabstops[vpos/ovmi] = ON;
+ break;
+
+ case '2': /* clear all tabs */
+ cleartabs();
+ break;
+
+ case '\014': /* set lines per page */
+ linespp = getc(fp_in);
+ break;
+
+ case '\037': /* set hmi to next byte minus 1 */
+ hmi = HSCALE * (getc(fp_in) - 1);
+ break;
+
+ case 'S': /* reset hmi to default */
+ hmi = ohmi;
+ break;
+
+ case '\011': /* move to column given by next byte */
+ hgoto((getc(fp_in)-1) * ohmi);
+ break;
+
+ case '?': /* do carriage return after line feed */
+ lfiscr = ON;
+ break;
+
+ case '!': /* don't generate carriage return */
+ lfiscr = OFF;
+ break;
+
+ case '5': /* forward print mode */
+ advance = 1;
+ break;
+
+ case '6': /* backward print mode */
+ advance = -1;
+ break;
+
+ case '\036': /* set vmi to next byte minus 1 */
+ vmi = VSCALE * (getc(fp_in) - 1);
+ break;
+
+ case '\013': /* move to line given by next byte */
+ vgoto((getc(fp_in)-1) * ovmi);
+ break;
+
+ case 'U': /* positive half line feed */
+ vmot(vmi/2);
+ break;
+
+ case 'D': /* negative half line feed */
+ vmot(-vmi/2);
+ break;
+
+ case '\012': /* negative line feed */
+ vmot(-vmi);
+ break;
+
+ case '\015': /* clear all margins */
+ bottommargin = BOTTOMMARGIN;
+ topmargin = TOPMARGIN;
+ leftmargin = BOTTOMMARGIN;
+ rightmargin = RIGHTMARGIN;
+ break;
+
+ case 'E': /* auto underscore - use italic font */
+ changefont("/Courier-Oblique");
+ break;
+
+ case 'R': /* disable auto underscore */
+ changefont(fontname);
+ break;
+
+ case 'O': /* bold/shadow printing */
+ case 'W':
+ changefont("/Courier-Bold");
+ shadowprint = ON;
+ break;
+
+ case '&': /* disable bold printing */
+ changefont(fontname);
+ shadowprint = OFF;
+ break;
+
+ case '/': /* ignored 2 byte escapes */
+ case '\\':
+ case '<':
+ case '>':
+ case '%':
+ case '=':
+ case '.':
+ case '4':
+ case 'A':
+ case 'B':
+ case 'M':
+ case 'N':
+ case 'P':
+ case 'Q':
+ case 'X':
+ case '\010':
+ break;
+
+ case ',': /* ignored 3 byte escapes */
+ case '\016':
+ case '\021':
+ getc(fp_in);
+ break;
+
+ case '3': /* graphics mode - should quit! */
+ case '7':
+ case 'G':
+ case 'V':
+ case 'Y':
+ case 'Z':
+ error(FATAL, "graphics mode is not implemented");
+ break;
+
+ default:
+ error(FATAL, "missing case for escape o%o\n", ch);
+ break;
+ } /* End switch */
+
+} /* End of escape */
+
+/*****************************************************************************/
+
+vmot(n)
+
+ int n; /* move this far vertically */
+
+{
+
+/*
+ *
+ * Move vertically n units from where we are.
+ *
+ */
+
+ vpos += n;
+
+} /* End of vmot */
+
+/*****************************************************************************/
+
+vgoto(n)
+
+ int n; /* new vertical position */
+
+{
+
+/*
+ *
+ * Moves to absolute vertical position n.
+ *
+ */
+
+ vpos = n;
+
+} /* End of vgoto */
+
+/*****************************************************************************/
+
+hmot(n)
+
+ int n; /* move this horizontally */
+
+{
+
+/*
+ *
+ * Moves horizontally n units from our current position.
+ *
+ */
+
+ hpos += n * advance;
+
+ if ( hpos < leftmargin )
+ hpos = leftmargin;
+
+} /* End of hmot */
+
+/*****************************************************************************/
+
+hgoto(n)
+
+ int n; /* go to this horizontal position */
+
+{
+
+/*
+ *
+ * Moves to absolute horizontal position n.
+ *
+ */
+
+ hpos = n;
+
+} /* End of hgoto */
+
+/*****************************************************************************/
+
+changefont(name)
+
+ char *name;
+
+{
+
+/*
+ *
+ * Changes the current font. Used to get in and out of auto underscore and bold
+ * printing.
+ *
+ */
+
+ endline();
+ fprintf(fp_out, "%s f\n", name);
+
+} /* End of changefont */
+
+/*****************************************************************************/
+
+startline()
+
+{
+
+/*
+ *
+ * Called whenever we want to be certain we're ready to start pushing characters
+ * into an open string on the stack. If stringcount is positive we've already
+ * started, so there's nothing to do. The first string starts in column 1.
+ *
+ */
+
+ if ( stringcount < 1 ) {
+ putc('(', fp_out);
+ stringstart = lastx = hpos;
+ lasty = vpos;
+ lasthmi = hmi;
+ lastc = -1;
+ prevx = -1;
+ stringcount = 1;
+ } /* End if */
+
+} /* End of startline */
+
+/*****************************************************************************/
+
+endline()
+
+{
+
+/*
+ *
+ * Generates a call to the PostScript procedure that processes the text on the
+ * the stack - provided stringcount is positive.
+ *
+ */
+
+ if ( stringcount > 0 )
+ fprintf(fp_out, ")%d %d %d t\n", stringstart, lasty, lasthmi);
+
+ stringcount = 0;
+
+} /* End of endline */
+
+/*****************************************************************************/
+
+endstring()
+
+{
+
+/*
+ *
+ * Takes the string we've been working on and adds it to the output file. Called
+ * when we need to adjust our horizontal position before starting a new string.
+ * Also called from endline() when we're done with the current line.
+ *
+ */
+
+ if ( stringcount > 0 ) {
+ fprintf(fp_out, ")%d(", stringstart);
+ lastx = stringstart = hpos;
+ stringcount++;
+ } /* End if */
+
+} /* End of endstring */
+
+/*****************************************************************************/
+
+oput(ch)
+
+ int ch; /* next output character */
+
+{
+
+/*
+ *
+ * Responsible for adding all printing characters from the input file to the
+ * open string on top of the stack. The only other characters that end up in
+ * that string are the quotes required for special characters. Reverse printing
+ * mode hasn't been tested but it should be close. hpos and lastx should disagree
+ * each time (except after startline() does something), and that should force a
+ * call to endstring() for every character.
+ *
+ */
+
+ if ( stringcount > 100 ) /* don't put too much on the stack */
+ endline();
+
+ if ( vpos != lasty )
+ endline();
+
+ if ( advance == -1 ) /* for reverse printing - move first */
+ hmot(hmi);
+
+ startline();
+
+ if ( lastc != ch || hpos != prevx ) {
+ if ( lastx != hpos )
+ endstring();
+
+ if ( ch == '\\' || ch == '(' || ch == ')' )
+ putc('\\', fp_out);
+ putc(ch, fp_out);
+
+ lastc = ch;
+ prevx = hpos;
+ lastx += lasthmi;
+ } /* End if */
+
+ if ( advance != -1 )
+ hmot(hmi);
+
+ markedpage = TRUE;
+
+} /* End of oput */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postdaisy/README b/sys/src/cmd/postscript/postdaisy/README
new file mode 100755
index 000000000..53c0485cc
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/README
@@ -0,0 +1,4 @@
+
+Diablo 630 to PostScript translator. Not terribly useful, and much has
+been omitted.
+
diff --git a/sys/src/cmd/postscript/postdaisy/mkfile b/sys/src/cmd/postscript/postdaisy/mkfile
new file mode 100755
index 000000000..e596ef0a6
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/mkfile
@@ -0,0 +1,62 @@
+BUILTINS=
+</$objtype/mkfile
+MAKE=mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/rc/bin/postscript/
+POSTLIB=$ROOT/sys/lib/postscript/prologues
+
+COMMONDIR=../common
+
+CC=pcc
+LD=pcc
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+LDFLAGS=
+
+all :V: $O.out
+
+install :V: $POSTBIN/$objtype/postdaisy $POSTLIB/postdaisy.ps $MAN1DIR/postdaisy.1
+
+installall :V:
+ for(objtype in $CPUS) { \
+ $MAKE 'MAKE=$MAKE' \
+ 'SYSTEM=$SYSTEM' 'VERSION=$VERSION' \
+ 'FONTDIR=$FONTDIR' 'HOSTDIR=$HOSTDIR' 'MAN1DIR=$MAN1DIR' \
+ 'POSTBIN=$POSTBIN' 'POSTLIB=$POSTLIB' 'TMACDIR=$TMACDIR' \
+ 'DKHOST=$DKHOST' 'DKSTREAMS=$DKSTREAMS' \
+ 'ROUNDPAGE=$ROUNDPAGE' \
+ 'CC=$CC' 'LD=$LD' 'CFLAGS=$CFLAGS' 'LDFLAGS=$LDFLAGS' \
+ install \
+ }
+
+clean :V:
+ rm -f *.$O
+
+clobber :V: clean
+ rm -f $O.out
+
+$POSTBIN/$objtype/postdaisy : $O.out
+ cp $prereq $target
+
+$POSTLIB/postdaisy.ps : postdaisy.ps
+ cp $prereq $target
+
+$MAN1DIR/postdaisy.1 : postdaisy.1
+ cp $prereq $target
+
+$O.out : postdaisy.$O $COMMONDIR/glob.$O $COMMONDIR/misc.$O $COMMONDIR/request.$O $COMMONDIR/getopt.$O
+ $LD $LDFLAGS $prereq
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+postdaisy.$O : postdaisy.c postdaisy.h $COMMONDIR/comments.h $COMMONDIR/ext.h $COMMONDIR/gen.h $COMMONDIR/path.h
+
+
+common :V:
+ cd $COMMONDIR; $MAKE
diff --git a/sys/src/cmd/postscript/postdaisy/postdaisy.1 b/sys/src/cmd/postscript/postdaisy/postdaisy.1
new file mode 100755
index 000000000..a8716aa2e
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/postdaisy.1
@@ -0,0 +1,217 @@
+.ds dQ /usr/lib/postscript
+.TH POSTDAISY 1 "DWB 3.2"
+.SH NAME
+.B postdaisy
+\- PostScript translator for Diablo 630 files
+.SH SYNOPSIS
+\*(mBpostdaisy\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B postdaisy
+translates Diablo 630 daisy-wheel
+.I files
+into PostScript and writes the results on the
+standard output.
+If no
+.I files
+are specified, or if
+.OP \-
+is one of the input
+.IR files ,
+the standard input is read.
+The following
+.I options
+are understood:
+.TP 0.75i
+.OP \-c num
+Print
+.I num
+copies of each page.
+By default only one copy is printed.
+.TP
+.OP \-f name
+Print
+.I files
+using font
+.IR name .
+Any PostScript font can be used,
+although the best results will only be
+obtained with constant width fonts.
+The default font is Courier.
+.TP
+.OP \-h num
+Set the initial horizontal motion index to
+.IR num .
+Determines the character advance and the default
+point size, unless the
+.OP \-s
+option is used.
+The default is 12.
+.TP
+.OP \-m num
+Magnify each logical page by the factor
+.IR num .
+Pages are scaled uniformly about the origin,
+which is located near the upper left corner of
+each page.
+The default magnification is 1.0.
+.TP
+.OP \-n num
+Print
+.I num
+logical pages on each piece of paper,
+where
+.I num
+can be any positive integer.
+By default
+.I num
+is set to 1.
+.TP
+.OP \-o list
+Print pages whose numbers are given in the comma-separated
+.IR list .
+The list contains single numbers
+.I N
+and ranges
+.IR N1\-\|N2 .
+A missing
+.I N1
+means the lowest numbered page, a missing
+.I N2
+means the highest.
+.TP
+.OP \-p mode
+Print
+.I files
+in either \*(mBportrait\fP or \*(mBlandscape\fP
+.IR mode .
+Only the first character of
+.I mode
+is significant.
+The default
+.I mode
+is \*(mBportrait\fP.
+.TP
+.OP \-r num
+Selects carriage return and line feed behavior.
+If
+.I num
+is 1 a line feed generates a carriage return.
+If
+.I num
+is 2 a carriage return generates a line feed.
+Setting
+.I num
+to 3 enables both modes.
+.TP
+.OP \-s num
+Use point size
+.I num
+instead of the default value set by the
+initial horizontal motion index.
+.TP
+.OP \-v num
+Set the initial vertical motion index to
+.IR num .
+The default is 8.
+.TP
+.OP \-x num
+Translate the origin
+.I num
+inches along the positive x axis.
+The default
+coordinate system has the origin fixed near the
+upper left corner of the page, with positive
+x to the right and positive y down the page.
+Positive
+.I num
+moves everything right.
+The default offset is 0.25 inches.
+.TP
+.OP \-y num
+Translate the origin
+.I num
+inches along the positive y axis.
+Positive
+.I num
+moves text down the page.
+The default offset is 0.25 inches.
+.TP
+.OP \-E name
+Set the character encoding for text fonts to
+.IR name .
+Requesting
+.I name
+means include file
+.MI \*(dQ/ name .enc \f1.
+A nonexistent encoding file is silently ignored.
+The default selects file
+.MR \*(dQ/Default.enc .
+.TP
+.OP \-L file
+Use
+.I file
+as the PostScript prologue.
+.br
+The default is
+.MR \*(dQ/postdaisy.ps .
+.PP
+Three options allow insertion of arbitrary PostScript
+at controlled points in the translation process:
+.TP 0.75i
+.OP \-C file
+Copy
+.I file
+to the output file;
+.I file
+must contain legitimate PostScript.
+.TP
+.OP \-P string
+Include
+.I string
+in output file;
+.I string
+must be legitimate PostScript.
+.TP
+.OP \-R action
+Requests special
+.I action
+(e.g.,
+.MR manualfeed )
+on a per page or global basis.
+The
+.I action
+string can be given as
+.IR request ,
+.IM request : page\f1\|,
+or
+.IM request : page : file\f1\|.
+If
+.I page
+is omitted or given as 0, the request
+applies to all pages.
+If
+.I file
+is omitted, the request
+lookup is done in
+.MR \*(dQ/ps.requests .
+.SH DIAGNOSTICS
+A 0 exit status is returned if
+.I files
+were successfully processed.
+.SH FILES
+.MW \*(dQ/postdaisy.ps
+.br
+.MW \*(dQ/forms.ps
+.br
+.MW \*(dQ/ps.requests
+.SH SEE ALSO
+.BR dpost (1),
+.BR postdmd (1),
+.BR postio (1),
+.BR postmd (1),
+.BR postprint (1),
+.BR postreverse (1),
+.BR posttek (1),
+.BR psencoding (1)
diff --git a/sys/src/cmd/postscript/postdaisy/postdaisy.c b/sys/src/cmd/postscript/postdaisy/postdaisy.c
new file mode 100755
index 000000000..8588cf9af
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/postdaisy.c
@@ -0,0 +1,1225 @@
+/*
+ *
+ * postdaisy - PostScript translator for Diablo 1640 files.
+ *
+ * A program that translates Diablo 1640 files into PostScript. Absolutely nothing
+ * is guaranteed. Quite a few things haven't been implemented, and what's been
+ * done isn't well tested. Most of the documentation used to write this program
+ * was taken from the 'Diablo Emulator' section of a recent Imagen manual.
+ *
+ * Some of document comments that are generated may not be right. Most of the test
+ * files I used produced a trailing blank page. I've put a check in formfeed() that
+ * won't print the last page if it doesn't contain any text, but PAGES comments may
+ * not be right. The DOCUMENTFONTS comment will also be wrong if auto underline or
+ * bold printing have been turned on by escape commands.
+ *
+ * The brute force approach used to implement horizontal and vertical tabs leaves
+ * much to be desired, and may not work for very small initial hmi and vmi values.
+ * At the very least I should have used malloc() to get space for the two tabstop
+ * arrays after hmi and vmi are known!
+ *
+ * Reverse printing mode hasn't been tested at all, but what's here should be
+ * close even though it's not efficient.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how this program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects to
+ * find the current page number on the stack.
+ *
+ * t
+ *
+ * mark str1 x1 str2 x2 ... strn xn y hmi t mark
+ *
+ * Handles all the text on the stack. Characters in the strings are
+ * printed using hmi as the character advance, and all strings are at
+ * vertical position y. Each string is begins at the horizontal position
+ * that preceeds it.
+ *
+ * f
+ *
+ * font f -
+ *
+ * Use font f, where f is the full PostScript font name. Only used when
+ * we switch to auto underline (Courier-Italic) or bold (Courier-Bold)
+ * printing.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+#include "postdaisy.h" /* a few special definitions */
+
+char *optnames = "a:c:f:h:l:m:n:o:p:r:s:v:x:y:A:C:E:J:L:P:DI";
+
+char *prologue = POSTDAISY; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+char htabstops[COLUMNS]; /* horizontal */
+char vtabstops[ROWS]; /* and vertical tabs */
+
+int res = RES; /* input file resolution - sort of */
+
+int hmi = HMI; /* horizontal motion index - 1/120 inch */
+int vmi = VMI; /* vertical motion index - 1/48 inch */
+int ohmi = HMI; /* original hmi */
+int ovmi = VMI; /* and vmi - for tabs and char size */
+
+int hpos = 0; /* current horizontal */
+int vpos = 0; /* and vertical position */
+
+int lastx = -1; /* printer's last horizontal */
+int lasty = -1; /* and vertical position */
+int lasthmi = -1; /* hmi for current text strings */
+
+int lastc = -1; /* last printed character */
+int prevx = -1; /* at this position */
+
+int leftmargin = LEFTMARGIN; /* page margins */
+int rightmargin = RIGHTMARGIN;
+int topmargin = TOPMARGIN;
+int bottommargin = BOTTOMMARGIN;
+
+int stringcount = 0; /* number of strings on the stack */
+int stringstart = 1; /* column where current one starts */
+int advance = 1; /* -1 if in backward print mode */
+
+int lfiscr = OFF; /* line feed implies carriage return */
+int crislf = OFF; /* carriage return implies line feed */
+
+int linespp = 0; /* lines per page if it's positive */
+int markedpage = FALSE; /* helps prevent trailing blank page */
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+int shadowprint = OFF; /* automatic bold printing if ON */
+
+FILE *fp_in; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that translates Diablo 1640 files into PostScript. Nothing
+ * is guaranteed - the program not well tested and doesn't implement everything.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Makes sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ if ( DOROUND )
+ cat(ROUNDPAGE);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ int n; /* for CR and LF modes */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ * Although any PostScript font can be used, things will only work for constant
+ * width fonts.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f': /* use this PostScript font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'h': /* default character spacing */
+ ohmi = hmi = atoi(optarg) * HSCALE;
+ fprintf(stdout, "/hmi %s def\n", optarg);
+ break;
+
+ case 'l': /* lines per page */
+ linespp = atoi(optarg);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'r': /* set CR and LF modes */
+ n = atoi(optarg);
+ if ( n & 01 )
+ lfiscr = ON;
+ else lfiscr = OFF;
+ if ( n & 02 )
+ crislf = ON;
+ else crislf = OFF;
+ break;
+
+ case 's': /* point size */
+ fprintf(stdout, "/pointsize %s def\n", optarg);
+ break;
+
+ case 'v': /* default line spacing */
+ ovmi = vmi = atoi(optarg) * VSCALE;
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font, although things will only work well for constant width
+ * fonts.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ fp_in = stdin;
+
+ if ( argc < 1 )
+ text();
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ text();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages, make sure the
+ * last page is printed, and restore the initial environment.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
+ * is requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+text()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Translates the next input file into PostScript. The redirect(-1) call forces
+ * the initial output to go to /dev/null - so the stuff formfeed() does at the
+ * end of each page doesn't go to stdout.
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ formfeed(); /* force PAGE comment etc. */
+ inittabs();
+
+ while ( (ch = getc(fp_in)) != EOF )
+ switch ( ch ) {
+ case '\010': /* backspace */
+ backspace();
+ break;
+
+ case '\011': /* horizontal tab */
+ htab();
+ break;
+
+ case '\012': /* new line */
+ linefeed();
+ break;
+
+ case '\013': /* vertical tab */
+ vtab();
+ break;
+
+ case '\014': /* form feed */
+ formfeed();
+ break;
+
+ case '\015': /* carriage return */
+ carriage();
+ break;
+
+ case '\016': /* extended character set - SO */
+ break;
+
+ case '\017': /* extended character set - SI */
+ break;
+
+ case '\031': /* next char from supplementary set */
+ break;
+
+ case '\033': /* 2 or 3 byte escape sequence */
+ escape();
+ break;
+
+ default:
+ oput(ch);
+ break;
+ } /* End switch */
+
+ formfeed(); /* next file starts on a new page? */
+
+} /* End of text */
+
+/*****************************************************************************/
+
+inittabs()
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Initializes the horizontal and vertical tab arrays. The way tabs are handled is
+ * quite inefficient and may not work for all initial hmi or vmi values.
+ *
+ */
+
+ for ( i = 0; i < COLUMNS; i++ )
+ htabstops[i] = ((i % 8) == 0) ? ON : OFF;
+
+ for ( i = 0; i < ROWS; i++ )
+ vtabstops[i] = ((i * ovmi) > BOTTOMMARGIN) ? ON : OFF;
+
+} /* End of inittabs */
+
+/*****************************************************************************/
+
+cleartabs()
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Clears all horizontal and vertical tab stops.
+ *
+ */
+
+ for ( i = 0; i < ROWS; i++ )
+ htabstops[i] = OFF;
+
+ for ( i = 0; i < COLUMNS; i++ )
+ vtabstops[i] = OFF;
+
+} /* End of cleartabs */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+/*
+ *
+ * Called whenever we've finished with the last page and want to get ready for the
+ * next one. Also used at the beginning and end of each input file, so we have to
+ * be careful about what's done. I've added a simple test before the showpage that
+ * should eliminate the extra blank page that was put out at the end of many jobs,
+ * but the PAGES comments may be wrong.
+ *
+ */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ endline(); /* print the last line */
+
+ fprintf(fp_out, "cleartomark\n");
+ if ( feof(fp_in) == 0 || markedpage == TRUE )
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+
+ vgoto(topmargin);
+ hgoto(leftmargin);
+
+ markedpage = FALSE;
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+linefeed()
+
+{
+
+ int line = 0; /* current line - based on ovmi */
+
+/*
+ *
+ * Adjust our current vertical position. If we've passed the bottom of the page
+ * or exceeded the number of lines per page, print it and go to the upper left
+ * corner of the next page. This routine is also called from carriage() if crislf
+ * is ON.
+ *
+ */
+
+ vmot(vmi);
+
+ if ( lfiscr == ON )
+ hgoto(leftmargin);
+
+ if ( linespp > 0 ) /* means something so see where we are */
+ line = vpos / ovmi + 1;
+
+ if ( vpos > bottommargin || line > linespp )
+ formfeed();
+
+} /* End of linefeed */
+
+/*****************************************************************************/
+
+carriage()
+
+{
+
+/*
+ *
+ * Handles carriage return character. If crislf is ON we'll generate a line feed
+ * every time we get a carriage return character.
+ *
+ */
+
+ if ( shadowprint == ON ) /* back to normal mode */
+ changefont(fontname);
+
+ advance = 1;
+ shadowprint = OFF;
+
+ hgoto(leftmargin);
+
+ if ( crislf == ON )
+ linefeed();
+
+} /* End of carriage */
+
+/*****************************************************************************/
+
+htab()
+
+{
+
+ int col; /* 'column' we'll be at next */
+ int i; /* loop index */
+
+/*
+ *
+ * Tries to figure out where the next tab stop is. Wasn't positive about this
+ * one, since hmi can change. I'll assume columns are determined by the original
+ * value of hmi. That fixes them on the page, which seems to make more sense than
+ * letting them float all over the place.
+ *
+ */
+
+ endline();
+
+ col = hpos/ohmi + 1;
+ for ( i = col; i < ROWS; i++ )
+ if ( htabstops[i] == ON ) {
+ col = i;
+ break;
+ } /* End if */
+
+ hgoto(col * ohmi);
+ lastx = hpos;
+
+} /* End of htab */
+
+/*****************************************************************************/
+
+vtab()
+
+{
+
+ int line; /* line we'll be at next */
+ int i; /* loop index */
+
+/*
+ *
+ * Looks for the next vertical tab stop in the vtabstops[] array and moves to that
+ * line. If we don't find a tab we'll just move down one line - shouldn't happen.
+ *
+ */
+
+ endline();
+
+ line = vpos/ovmi + 1;
+ for ( i = line; i < COLUMNS; i++ )
+ if ( vtabstops[i] == ON ) {
+ line = i;
+ break;
+ } /* End if */
+
+ vgoto(line * ovmi);
+
+} /* End of vtab */
+
+/*****************************************************************************/
+
+backspace()
+
+{
+
+/*
+ *
+ * Moves backwards a distance equal to the current value of hmi, but don't go
+ * past the left margin.
+ *
+ */
+
+ endline();
+
+ if ( hpos - leftmargin >= hmi )
+ hmot(-hmi);
+ else hgoto(leftmargin); /* maybe just ignore the backspace?? */
+
+ lastx = hpos;
+
+} /* End of backspace */
+
+/*****************************************************************************/
+
+escape()
+
+{
+
+ int ch; /* control character */
+
+/*
+ *
+ * Handles special codes that are expected to follow an escape character. The
+ * initial escape character is followed by one or two bytes.
+ *
+ */
+
+ switch ( ch = getc(fp_in) ) {
+ case 'T': /* top margin */
+ topmargin = vpos;
+ break;
+
+ case 'L': /* bottom margin */
+ bottommargin = vpos;
+ break;
+
+ case 'C': /* clear top and bottom margins */
+ bottommargin = BOTTOMMARGIN;
+ topmargin = TOPMARGIN;
+ break;
+
+ case '9': /* left margin */
+ leftmargin = hpos;
+ break;
+
+ case '0': /* right margin */
+ rightmargin = hpos;
+ break;
+
+ case '1': /* set horizontal tab */
+ htabstops[hpos/ohmi] = ON;
+ break;
+
+ case '8': /* clear horizontal tab at hpos */
+ htabstops[hpos/ohmi] = OFF;
+ break;
+
+ case '-': /* set vertical tab */
+ vtabstops[vpos/ovmi] = ON;
+ break;
+
+ case '2': /* clear all tabs */
+ cleartabs();
+ break;
+
+ case '\014': /* set lines per page */
+ linespp = getc(fp_in);
+ break;
+
+ case '\037': /* set hmi to next byte minus 1 */
+ hmi = HSCALE * (getc(fp_in) - 1);
+ break;
+
+ case 'S': /* reset hmi to default */
+ hmi = ohmi;
+ break;
+
+ case '\011': /* move to column given by next byte */
+ hgoto((getc(fp_in)-1) * ohmi);
+ break;
+
+ case '?': /* do carriage return after line feed */
+ lfiscr = ON;
+ break;
+
+ case '!': /* don't generate carriage return */
+ lfiscr = OFF;
+ break;
+
+ case '5': /* forward print mode */
+ advance = 1;
+ break;
+
+ case '6': /* backward print mode */
+ advance = -1;
+ break;
+
+ case '\036': /* set vmi to next byte minus 1 */
+ vmi = VSCALE * (getc(fp_in) - 1);
+ break;
+
+ case '\013': /* move to line given by next byte */
+ vgoto((getc(fp_in)-1) * ovmi);
+ break;
+
+ case 'U': /* positive half line feed */
+ vmot(vmi/2);
+ break;
+
+ case 'D': /* negative half line feed */
+ vmot(-vmi/2);
+ break;
+
+ case '\012': /* negative line feed */
+ vmot(-vmi);
+ break;
+
+ case '\015': /* clear all margins */
+ bottommargin = BOTTOMMARGIN;
+ topmargin = TOPMARGIN;
+ leftmargin = BOTTOMMARGIN;
+ rightmargin = RIGHTMARGIN;
+ break;
+
+ case 'E': /* auto underscore - use italic font */
+ changefont("/Courier-Oblique");
+ break;
+
+ case 'R': /* disable auto underscore */
+ changefont(fontname);
+ break;
+
+ case 'O': /* bold/shadow printing */
+ case 'W':
+ changefont("/Courier-Bold");
+ shadowprint = ON;
+ break;
+
+ case '&': /* disable bold printing */
+ changefont(fontname);
+ shadowprint = OFF;
+ break;
+
+ case '/': /* ignored 2 byte escapes */
+ case '\\':
+ case '<':
+ case '>':
+ case '%':
+ case '=':
+ case '.':
+ case '4':
+ case 'A':
+ case 'B':
+ case 'M':
+ case 'N':
+ case 'P':
+ case 'Q':
+ case 'X':
+ case '\010':
+ break;
+
+ case ',': /* ignored 3 byte escapes */
+ case '\016':
+ case '\021':
+ getc(fp_in);
+ break;
+
+ case '3': /* graphics mode - should quit! */
+ case '7':
+ case 'G':
+ case 'V':
+ case 'Y':
+ case 'Z':
+ error(FATAL, "graphics mode is not implemented");
+ break;
+
+ default:
+ error(FATAL, "missing case for escape o%o\n", ch);
+ break;
+ } /* End switch */
+
+} /* End of escape */
+
+/*****************************************************************************/
+
+vmot(n)
+
+ int n; /* move this far vertically */
+
+{
+
+/*
+ *
+ * Move vertically n units from where we are.
+ *
+ */
+
+ vpos += n;
+
+} /* End of vmot */
+
+/*****************************************************************************/
+
+vgoto(n)
+
+ int n; /* new vertical position */
+
+{
+
+/*
+ *
+ * Moves to absolute vertical position n.
+ *
+ */
+
+ vpos = n;
+
+} /* End of vgoto */
+
+/*****************************************************************************/
+
+hmot(n)
+
+ int n; /* move this horizontally */
+
+{
+
+/*
+ *
+ * Moves horizontally n units from our current position.
+ *
+ */
+
+ hpos += n * advance;
+
+ if ( hpos < leftmargin )
+ hpos = leftmargin;
+
+} /* End of hmot */
+
+/*****************************************************************************/
+
+hgoto(n)
+
+ int n; /* go to this horizontal position */
+
+{
+
+/*
+ *
+ * Moves to absolute horizontal position n.
+ *
+ */
+
+ hpos = n;
+
+} /* End of hgoto */
+
+/*****************************************************************************/
+
+changefont(name)
+
+ char *name;
+
+{
+
+/*
+ *
+ * Changes the current font. Used to get in and out of auto underscore and bold
+ * printing.
+ *
+ */
+
+ endline();
+ fprintf(fp_out, "%s f\n", name);
+
+} /* End of changefont */
+
+/*****************************************************************************/
+
+startline()
+
+{
+
+/*
+ *
+ * Called whenever we want to be certain we're ready to start pushing characters
+ * into an open string on the stack. If stringcount is positive we've already
+ * started, so there's nothing to do. The first string starts in column 1.
+ *
+ */
+
+ if ( stringcount < 1 ) {
+ putc('(', fp_out);
+ stringstart = lastx = hpos;
+ lasty = vpos;
+ lasthmi = hmi;
+ lastc = -1;
+ prevx = -1;
+ stringcount = 1;
+ } /* End if */
+
+} /* End of startline */
+
+/*****************************************************************************/
+
+endline()
+
+{
+
+/*
+ *
+ * Generates a call to the PostScript procedure that processes the text on the
+ * the stack - provided stringcount is positive.
+ *
+ */
+
+ if ( stringcount > 0 )
+ fprintf(fp_out, ")%d %d %d t\n", stringstart, lasty, lasthmi);
+
+ stringcount = 0;
+
+} /* End of endline */
+
+/*****************************************************************************/
+
+endstring()
+
+{
+
+/*
+ *
+ * Takes the string we've been working on and adds it to the output file. Called
+ * when we need to adjust our horizontal position before starting a new string.
+ * Also called from endline() when we're done with the current line.
+ *
+ */
+
+ if ( stringcount > 0 ) {
+ fprintf(fp_out, ")%d(", stringstart);
+ lastx = stringstart = hpos;
+ stringcount++;
+ } /* End if */
+
+} /* End of endstring */
+
+/*****************************************************************************/
+
+oput(ch)
+
+ int ch; /* next output character */
+
+{
+
+/*
+ *
+ * Responsible for adding all printing characters from the input file to the
+ * open string on top of the stack. The only other characters that end up in
+ * that string are the quotes required for special characters. Reverse printing
+ * mode hasn't been tested but it should be close. hpos and lastx should disagree
+ * each time (except after startline() does something), and that should force a
+ * call to endstring() for every character.
+ *
+ */
+
+ if ( stringcount > 100 ) /* don't put too much on the stack */
+ endline();
+
+ if ( vpos != lasty )
+ endline();
+
+ if ( advance == -1 ) /* for reverse printing - move first */
+ hmot(hmi);
+
+ startline();
+
+ if ( lastc != ch || hpos != prevx ) {
+ if ( lastx != hpos )
+ endstring();
+
+ if ( isascii(ch) && isprint(ch) ) {
+ if ( ch == '\\' || ch == '(' || ch == ')' )
+ putc('\\', fp_out);
+ putc(ch, fp_out);
+ } else fprintf(fp_out, "\\%.3o", ch & 0377);
+
+ lastc = ch;
+ prevx = hpos;
+ lastx += lasthmi;
+ } /* End if */
+
+ if ( advance != -1 )
+ hmot(hmi);
+
+ markedpage = TRUE;
+
+} /* End of oput */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postdaisy/postdaisy.h b/sys/src/cmd/postscript/postdaisy/postdaisy.h
new file mode 100755
index 000000000..e8eef0c16
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/postdaisy.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Definitions used by the PostScript translator for Diablo 1640 files.
+ *
+ * Diablo printers have horizontal and vertical resolutions of 120 and 48 dpi.
+ * We'll use a single resolution of 240 dpi and let the program scale horizontal
+ * and vertical positions by HSCALE and VSCALE.
+ *
+ */
+
+#define RES 240
+#define HSCALE 2
+#define VSCALE 5
+
+/*
+ *
+ * HMI is the default character spacing and VMI is the line spacing. Both values
+ * are in terms of the 240 dpi resolution.
+ *
+ */
+
+#define HMI (12 * HSCALE)
+#define VMI (8 * VSCALE)
+
+/*
+ *
+ * Paper dimensions don't seem to be all that important. They're just used to
+ * set the right and bottom margins. Both are given in terms of the 240 dpi
+ * resolution.
+ *
+ */
+
+#define LEFTMARGIN 0
+#define RIGHTMARGIN 3168
+#define TOPMARGIN 0
+#define BOTTOMMARGIN 2640
+
+/*
+ *
+ * ROWS and COLUMNS set the dimensions of the horizontal and vertical tab arrays.
+ * The way I've implemented both kinds of tabs leaves something to be desired, but
+ * it was simple and should be good enough for now. If arrays are going to be used
+ * to mark tab stops I probably should use malloc() to get enough space once the
+ * initial hmi and vmi are know.
+ *
+ */
+
+#define ROWS 400
+#define COLUMNS 200
+
+/*
+ *
+ * An array of type Fontmap helps convert font names requested by users into
+ * legitimate PostScript names. The array is initialized using FONTMAP, which must
+ * end with an entry that has NULL defined as its name field.
+ *
+ */
+
+typedef struct {
+ char *name; /* user's font name */
+ char *val; /* corresponding PostScript name */
+} Fontmap;
+
+#define FONTMAP \
+ \
+ { \
+ "R", "Courier", \
+ "I", "Courier-Oblique", \
+ "B", "Courier-Bold", \
+ "CO", "Courier", \
+ "CI", "Courier-Oblique", \
+ "CB", "Courier-Bold", \
+ "CW", "Courier", \
+ "PO", "Courier", \
+ "courier", "Courier", \
+ "cour", "Courier", \
+ "co", "Courier", \
+ NULL, NULL \
+ }
+
+/*
+ *
+ * Some of the non-integer functions in postdaisy.c.
+ *
+ */
+
+char *get_font();
+
diff --git a/sys/src/cmd/postscript/postdaisy/postdaisy.mk b/sys/src/cmd/postscript/postdaisy/postdaisy.mk
new file mode 100755
index 000000000..a771ccd6e
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/postdaisy.mk
@@ -0,0 +1,93 @@
+MAKE=/bin/make
+MAKEFILE=postdaisy.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+HFILES=postdaisy.h \
+ $(COMMONDIR)/comments.h\
+ $(COMMONDIR)/ext.h\
+ $(COMMONDIR)/gen.h\
+ $(COMMONDIR)/path.h
+
+OFILES=postdaisy.o\
+ $(COMMONDIR)/glob.o\
+ $(COMMONDIR)/misc.o\
+ $(COMMONDIR)/request.o
+
+all : postdaisy
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ @if [ ! -d "$(POSTLIB)" ]; then \
+ mkdir $(POSTLIB); \
+ chmod 755 $(POSTLIB); \
+ chgrp $(GROUP) $(POSTLIB); \
+ chown $(OWNER) $(POSTLIB); \
+ fi
+ cp postdaisy $(POSTBIN)/postdaisy
+ @chmod 755 $(POSTBIN)/postdaisy
+ @chgrp $(GROUP) $(POSTBIN)/postdaisy
+ @chown $(OWNER) $(POSTBIN)/postdaisy
+ cp postdaisy.ps $(POSTLIB)/postdaisy.ps
+ @chmod 644 $(POSTLIB)/postdaisy.ps
+ @chgrp $(GROUP) $(POSTLIB)/postdaisy.ps
+ @chown $(OWNER) $(POSTLIB)/postdaisy.ps
+ cp postdaisy.1 $(MAN1DIR)/postdaisy.1
+ @chmod 644 $(MAN1DIR)/postdaisy.1
+ @chgrp $(GROUP) $(MAN1DIR)/postdaisy.1
+ @chown $(OWNER) $(MAN1DIR)/postdaisy.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postdaisy
+
+postdaisy : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postdaisy $(OFILES)
+
+postdaisy.o : $(HFILES)
+
+$(COMMONDIR)/glob.o\
+$(COMMONDIR)/misc.o\
+$(COMMONDIR)/request.o :
+ @cd $(COMMONDIR); $(MAKE) -f common.mk `basename $@`
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ postdaisy.1 >XXX.1; \
+ mv XXX.1 postdaisy.1
+
diff --git a/sys/src/cmd/postscript/postdaisy/postdaisy.ps b/sys/src/cmd/postscript/postdaisy/postdaisy.ps
new file mode 100755
index 000000000..eda3a9d35
--- /dev/null
+++ b/sys/src/cmd/postscript/postdaisy/postdaisy.ps
@@ -0,0 +1,74 @@
+%
+% Version 3.3.2 prologue for Diablo 1640 files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/font /Courier def
+/formsperpage 1 def
+/hmi 12 def
+/landscape false def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/resolution 240 def
+/rotation 1 def
+/xoffset .25 def
+/yoffset .25 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/ashow {ashow} bind def % so later references don't bind
+/stringwidth {stringwidth} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+ /scaling 72 resolution div def
+ currentdict /pointsize known not {/pointsize hmi def} if
+ font findfont pointsize scaling div scalefont setfont
+ /charwidth (M) stringwidth pop def
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ margin 2 div dup neg translate
+ magnification dup aspectratio mul scale
+ height width div 1 min dup scale
+ scaling dup scale
+} def
+
+/pagedimensions {
+ useclippath userdict /gotpagebbox known not and {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def 0 0 moveto} bind def
+
+/t {
+ charwidth sub /advance exch def
+ neg /y exch def
+ counttomark 2 idiv {y moveto advance 0 3 -1 roll ashow} repeat
+} bind def
+
+/f {findfont pointsize scaling div scalefont setfont} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/postdmd/README b/sys/src/cmd/postscript/postdmd/README
new file mode 100755
index 000000000..0a8b4fcc0
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/README
@@ -0,0 +1,22 @@
+
+DMD bitmap to PostScript translator. Much of the code came from abm,
+which was written by Guy Riddle.
+
+By default 6 byte patterns are used to encode the output bitmap. Use
+the -b option to change the pattern size. Bitmaps are unpacked one
+scanline at a time and re-encoded in a format that looks like,
+
+ bytes patterns count
+
+where bytes and count are decimal integers and patterns is a series
+of hex digits. Bytes is the number of bytes represented by the hex
+pattern, and count is the number of additional times the pattern
+should be repeated. For example,
+
+ 2 FFFF 4
+ 5 FFFFFFFFFF 1
+ 10 FFFFFFFFFFFFFFFFFFFF 0
+
+all represent 10 consecutive bytes of ones. Scanlines are terminated
+by a 0 on a line by itself.
+
diff --git a/sys/src/cmd/postscript/postdmd/mkfile b/sys/src/cmd/postscript/postdmd/mkfile
new file mode 100755
index 000000000..1ccfb2562
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/mkfile
@@ -0,0 +1,61 @@
+BUILTINS=
+</$objtype/mkfile
+MAKE=mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/sys/lib/postscript/bin
+POSTLIB=$ROOT/sys/lib/postscript/prologues
+
+COMMONDIR=../common
+
+CC=pcc
+LD=pcc
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+LDFLAGS=
+
+all :V: $O.out
+
+install :V: $POSTBIN/$objtype/postdmd $POSTLIB/postdmd.ps $MAN1DIR/postdmd.1
+
+installall :V:
+ for(objtype in $CPUS) { \
+ $MAKE 'MAKE=$MAKE' \
+ 'SYSTEM=$SYSTEM' 'VERSION=$VERSION' \
+ 'FONTDIR=$FONTDIR' 'HOSTDIR=$HOSTDIR' 'MAN1DIR=$MAN1DIR' \
+ 'POSTBIN=$POSTBIN' 'POSTLIB=$POSTLIB' 'TMACDIR=$TMACDIR' \
+ 'DKHOST=$DKHOST' 'DKSTREAMS=$DKSTREAMS' \
+ 'ROUNDPAGE=$ROUNDPAGE' \
+ 'CC=$CC' 'LD=$LD' 'CFLAGS=$CFLAGS' 'LDFLAGS=$LDFLAGS' \
+ install \
+ }
+
+clean :V:
+ rm -f *.$O
+
+clobber :V: clean
+ rm -f $O.out
+
+$POSTBIN/$objtype/postdmd : $O.out
+ cp $prereq $target
+
+$POSTLIB/postdmd.ps : postdmd.ps
+ cp $prereq $target
+
+$MAN1DIR/postdmd.1 : postdmd.1
+ cp $prereq $target
+
+$O.out : postdmd.$O $COMMONDIR/glob.$O $COMMONDIR/misc.$O $COMMONDIR/request.$O $COMMONDIR/getopt.$O
+ $LD $LDFLAGS $prereq
+
+postdmd.$O : postdmd.c $COMMONDIR/comments.h $COMMONDIR/ext.h $COMMONDIR/gen.h $COMMONDIR/path.h
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+common :V:
+ cd $COMMONDIR; $MAKE
diff --git a/sys/src/cmd/postscript/postdmd/postdmd.1 b/sys/src/cmd/postscript/postdmd/postdmd.1
new file mode 100755
index 000000000..cb3317b14
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/postdmd.1
@@ -0,0 +1,206 @@
+.ds dQ /usr/lib/postscript
+.TH POSTDMD 1
+.SH NAME
+.B postdmd
+\- PostScript translator for
+.SM DMD
+bitmap files
+.SH SYNOPSIS
+\*(mBpostdmd\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B postdmd
+translates
+.SM DMD
+bitmap
+.IR files ,
+as produced by
+.BR dmdps ,
+or
+.I files
+written in the Ninth Edition
+.BR bitfile (9.5)
+format
+into PostScript and writes the results on the
+standard output.
+If no
+.I files
+are specified, or if
+.OP \-
+is one of the input
+.IR files ,
+the standard input is read.
+The following
+.I options
+are understood:
+.TP 0.75i
+.OP \-b num
+Pack the bitmap in the output file using
+.I num
+byte patterns.
+A value of 0 turns off all packing of the output file.
+By default
+.I num
+is 6.
+.TP
+.OP \-c num
+Print
+.I num
+copies of each page.
+By default only one copy is printed.
+.TP
+.OP \-f
+Flip the sense of the bits in
+.I files
+before printing the bitmaps.
+.TP
+.OP \-m num
+Magnify each logical page by the factor
+.IR num .
+Pages are scaled uniformly about the origin,
+which by default is located at the center of
+each page.
+The default magnification is 1.0.
+.TP
+.OP \-n num
+Print
+.I num
+logical pages on each piece of paper,
+where
+.I num
+can be any positive integer.
+By default
+.I num
+is set to 1.
+.TP
+.OP \-o list
+Print pages whose numbers are given in the comma-separated
+.IR list .
+The list contains single numbers
+.I N
+and ranges
+.IR N1\-\|N2 .
+A missing
+.I N1
+means the lowest numbered page, a missing
+.I N2
+means the highest.
+.TP
+.OP \-p mode
+Print
+.I files
+in either \*(mBportrait\fP or \*(mBlandscape\fP
+.IR mode .
+Only the first character of
+.I mode
+is significant.
+The default
+.I mode
+is \*(mBportrait\fP.
+.TP
+.OP \-u
+Disables much of the unpacking for Eighth
+Edition bitmap files.
+Usually results in smaller output files that take longer to print.
+Not a recommended option.
+.TP
+.OP \-x num
+Translate the origin
+.I num
+inches along the positive x axis.
+The default
+coordinate system has the origin fixed at the
+center of the page, with positive
+x to the right and positive y up the page.
+Positive
+.I num
+moves everything right.
+The default offset is 0 inches.
+.TP
+.OP \-y num
+Translate the origin
+.I num
+inches along the positive y axis.
+Positive
+.I num
+moves everything up the page.
+The default offset is 0.
+.TP
+.TP
+.OP \-L file
+Use
+.I file
+as the PostScript prologue.
+.br
+The default is
+.MR \*(dQ/postdmd.ps .
+.PP
+Three options allow insertion of arbitrary PostScript
+at controlled points in the translation process:
+.TP 0.75i
+.OP \-C file
+Copy
+.I file
+to the output file;
+.I file
+must contain legitimate PostScript.
+.TP
+.OP \-P string
+Include
+.I string
+in the output file;
+.I string
+must be legitimate PostScript.
+.TP
+.OP \-R action
+Requests special
+.I action
+(e.g.,
+.MR manualfeed )
+on a per page or global basis.
+The
+.I action
+string can be given as
+.IR request,
+.IM request : page\f1\|,
+or
+.IM request : page : file\f1\|.
+If
+.I page
+is omitted or given as 0, the request applies to all pages.
+If
+.I file
+is omitted, the request lookup is done in
+.MR \*(dQ/ps.requests .
+.PP
+Only one bitmap is printed on each logical page, and each of the input
+.I files
+must contain complete descriptions of at least one bitmap.
+Decreasing the pattern size using the
+.OP \-b
+option may help throughput on printers with fast processors
+(e.g., \s-1PS\s+1-810),
+while increasing the pattern size will often be the right move
+on older models
+(.e.g, \s-1PS\s+1-800).
+.SH DIAGNOSTICS
+A 0 exit status is returned if
+.I files
+were successfully processed.
+.br
+.ne 4v
+.SH FILES
+.MW \*(dQ/postdmd.ps
+.br
+.MW \*(dQ/forms.ps
+.br
+.MW \*(dQ/ps.requests
+.SH SEE ALSO
+.BR dpost (1),
+.BR postdaisy (1),
+.BR postio (1),
+.BR postmd (1),
+.BR postprint (1),
+.BR postreverse (1),
+.BR posttek (1)
diff --git a/sys/src/cmd/postscript/postdmd/postdmd.c b/sys/src/cmd/postscript/postdmd/postdmd.c
new file mode 100755
index 000000000..27cdfe0dd
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/postdmd.c
@@ -0,0 +1,729 @@
+/*
+ *
+ * postdmd - PostScript translator for DMD bitmap files.
+ *
+ * A simple program that can be used to print DMD bitmaps on PostScript printers.
+ * Much of the code was borrowed from abm, which was written by Guy Riddle.
+ *
+ * Although the program supports two different input bitmap formats, by far the
+ * most important is the Eighth (and Ninth) Edition bitfile format. A bitmap in
+ * the bitfile format begins with a 10 byte header with the first two bytes set to
+ * zero. The next 8 bytes set the x and y coordinates of the bitmap's origin and
+ * corner (ie. the upper left and lower right corners). The compressed raster data
+ * follows the header and consists of control bytes followed an appropriate number
+ * of data bytes. Control bytes (ie. n) less than 127 means read the next 2*n bytes
+ * of raster data directly from the input file, while if n is larger than 128 we
+ * read two bytes from the input file and replicate the bytes n-128 times. After
+ * each scan line is recovered it's exclusive-or'd with the preceeding line to
+ * generate the real raster data.
+ *
+ * After each raster line is recovered postdmd encodes it in a slightly different
+ * format that's designed to be unpacked by a PostScript procedure that's defined
+ * in the prologue. By default no exclusive-or'ing is done and packing of pattern
+ * data can be based on any number of bytes rather than just the next two bytes.
+ * By default 6 byte patterns are used, but any number can be selected with the -b
+ * option. A non-positive argument (eg. -b0) disables all pattern encoding. Larger
+ * patterns increase the size of the output file, but reduce the work load that's
+ * forced on the PostScript interpreter. The default choices I've made (ie. 6 byte
+ * patterns and no exclusive-or'ing) do a decent balancing job across currently
+ * available PostScript printers. Larger patterns (eg. -b16) increase the output
+ * file size, but may be appropriate if you're running at a high baud rate (eg.
+ * 19.2KB), while smaller patter size (eg. -b4) may help if you've got a printer
+ * with a fast processor (eg. a PS-810).
+ *
+ * The encoding produced by the program (and decoded on the printer) looks like,
+ *
+ * bytes patterns count
+ *
+ * where bytes and count are decimal integers and patterns is a hex string. Bytes
+ * is the number of bytes represented by the hex patterns and count is the number
+ * of additional times the patterns should be repeated. For example,
+ *
+ * 2 FFFF 4
+ * 5 FFFFFFFFFF 1
+ * 10 FFFFFFFFFFFFFFFFFFFF 0
+ *
+ * all represent 10 consecutive bytes of ones. Scanlines are terminated by a 0 on
+ * a line by itself.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how this program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * bitmap
+ *
+ * v8format flip scanlength scanlines bitmap -
+ *
+ * Prints the bitmap that's read from standard input. The bitmap consists
+ * of scanlines lines, each of which includes scanlength pixels. If
+ * v8format is true the picture is assumed to be an Eighth Edition bitmap,
+ * and the exclusive-or'ing will be done on the printer.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+
+char *optnames = "a:b:c:fm:n:o:p:ux:y:A:C:E:J:L:P:DI";
+
+char *prologue = POSTDMD; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int bbox[2] = {0, 0}; /* upper right coordinates only */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+int bytespp = 6; /* bytes per pattern - on output */
+int flip = FALSE; /* ones complement the bitmap */
+int v8undo = TRUE; /* xor'ing done on host if TRUE */
+int v8format = FALSE; /* for Eighth Edition bitmaps */
+
+int page = 0; /* last page we worked on */
+int printed = 0; /* and the number of pages printed */
+
+int patterns; /* 16 bit patterns per scan line */
+int scanlines; /* lines in the bitmap */
+int patcount = 0; /* should be patterns * scanlines */
+
+char *raster = NULL; /* next raster line */
+char *prevrast = NULL; /* and the previous one - v8format */
+char *rptr; /* next free byte in raster */
+char *eptr; /* one past the last byte in raster */
+
+FILE *fp_in = NULL; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that translates DMD bitmap files into PostScript. There can
+ * be more than one bitmap per file, but none can be split across input files.
+ * Each bitmap goes on a page by itself.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Make sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'b': /* bytes per pattern */
+ bytespp = atoi(optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f': /* ones complement - sort of */
+ flip = TRUE;
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'u': /* don't undo Eighth Edition bitmaps */
+ v8undo = FALSE;
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding - unnecessary */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding); /* unnecessary */
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) { /* followed by stuff for multiple pages */
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+ FILE *fp; /* next input file */
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ bitmap(stdin);
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp = stdin;
+ else if ( (fp = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ bitmap(fp);
+ if ( fp != stdin )
+ fclose(fp);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages with a TRAILER
+ * comment, make sure the last page prints, and add things like the PAGES comment
+ * that can only be determined after all the input files have been read.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s 0 0 %d %d\n", BOUNDINGBOX, (bbox[0]*72+100)/100, (bbox[1]*72+100)/100);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
+ * requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+bitmap(fp)
+
+ FILE *fp; /* next input file */
+
+{
+
+ int count; /* pattern repeats this many times */
+ long total; /* expect this many patterns */
+
+/*
+ *
+ * Reads all the bitmaps from the next input file, translates each one into
+ * PostScript, and arranges to have one bitmap printed on each page. Multiple
+ * bitmaps per input file work.
+ *
+ */
+
+ fp_in = fp; /* everyone reads from this file */
+
+ while ( dimensions() == TRUE ) {
+ patcount = 0;
+ total = scanlines * patterns;
+
+ bbox[0] = MAX(bbox[0], patterns*16); /* for BoundingBox comment */
+ bbox[1] = MAX(bbox[1], scanlines);
+
+ redirect(++page);
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ writerequest(printed+1, fp_out);
+
+ fprintf(fp_out, "%s ", (v8format == TRUE && v8undo == FALSE) ? "true" : "false");
+ fprintf(fp_out, "%s ", (flip == TRUE) ? "true" : "false");
+ fprintf(fp_out, "%d %d bitmap\n", patterns * 16, scanlines);
+
+ while ( patcount != total && (count = getc(fp)) != EOF ) {
+ addrast(count);
+ patcount += (count & 0177);
+ if ( patcount % patterns == 0 )
+ putrast();
+ } /* End while */
+
+ if ( debug == ON )
+ fprintf(stderr, "patterns = %d, scanlines = %d, patcount = %d\n", patterns, scanlines, patcount);
+
+ if ( total != patcount )
+ error(FATAL, "bitmap format error");
+
+ if ( fp_out == stdout ) printed++;
+
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+ } /* End while */
+
+} /* End of bitmap */
+
+/*****************************************************************************/
+
+dimensions()
+
+{
+
+ int ox, oy; /* coordinates of the origin */
+ int cx, cy; /* and right corner of the bitmap */
+ int i; /* loop index */
+
+/*
+ *
+ * Determines the dimensions and type of the next bitmap. Eighth edition bitmaps
+ * have a zero in the first 16 bits. If valid dimensions are read TRUE is returned
+ * to the caller. Changed so the check of whether we're done (by testing scanlines
+ * or patterns) comes before the malloc().
+ *
+ */
+
+ if ( (scanlines = getint()) == 0 ) {
+ ox = getint();
+ oy = getint();
+ cx = getint();
+ cy = getint();
+ scanlines = cy - oy;
+ patterns = (cx - ox + 15) / 16;
+ v8format = TRUE;
+ } else patterns = getint();
+
+ if ( scanlines <= 0 || patterns <= 0 ) /* done - don't do the malloc() */
+ return(FALSE);
+
+ if ( raster != NULL ) free(raster);
+ if ( prevrast != NULL ) free(prevrast);
+
+ if ( (rptr = raster = (char *) malloc(patterns * 2)) == NULL )
+ error(FATAL, "no memory");
+
+ if ( (prevrast = (char *) malloc(patterns * 2)) == NULL )
+ error(FATAL, "no memory");
+
+ for ( i = 0; i < patterns * 2; i++ )
+ *(prevrast+i) = 0377;
+
+ eptr = rptr + patterns * 2;
+
+ return(TRUE);
+
+} /* End of dimensions */
+
+/*****************************************************************************/
+
+addrast(count)
+
+ int count; /* repeat count for next pattern */
+
+{
+
+ int size; /* number of bytes in next pattern */
+ int l, h; /* high and low bytes */
+ int i, j; /* loop indices */
+
+/*
+ *
+ * Reads the input file and adds the appropriate number of bytes to the output
+ * raster line. If count has bit 7 on, one 16 bit pattern is read and repeated
+ * count & 0177 times. If bit 7 is off, count is the number of patterns read from
+ * fp_in - each one repeated once.
+ *
+ */
+
+ if ( count & 0200 ) {
+ size = 1;
+ count &= 0177;
+ } else {
+ size = count;
+ count = 1;
+ } /* End else */
+
+ for ( i = size; i > 0; i-- ) {
+ if ( (l = getc(fp_in)) == EOF || (h = getc(fp_in)) == EOF )
+ return;
+ for ( j = count; j > 0; j-- ) {
+ *rptr++ = l;
+ *rptr++ = h;
+ } /* End for */
+ } /* End for */
+
+} /* End of addrast */
+
+/*****************************************************************************/
+
+putrast()
+
+{
+
+ char *p1, *p2; /* starting and ending patterns */
+ int n; /* set to bytes per pattern */
+ int i; /* loop index */
+
+/*
+ *
+ * Takes the scanline that's been saved in *raster, encodes it according to the
+ * value that's been assigned to bytespp, and writes the result to *fp_out. Each
+ * line in the output bitmap is terminated by a 0 on a line by itself.
+ *
+ */
+
+ n = (bytespp <= 0) ? 2 * patterns : bytespp;
+
+ if ( v8format == TRUE && v8undo == TRUE )
+ for ( i = 0; i < patterns * 2; i++ )
+ *(raster+i) = (*(prevrast+i) ^= *(raster+i));
+
+ for ( p1 = raster, p2 = raster + n; p1 < eptr; p1 = p2 )
+ if ( patncmp(p1, n) == TRUE ) {
+ while ( patncmp(p2, n) == TRUE ) p2 += n;
+ p2 += n;
+ fprintf(fp_out, "%d ", n);
+ for ( i = 0; i < n; i++, p1++ )
+ fprintf(fp_out, "%.2X", ((int) *p1) & 0377);
+ fprintf(fp_out, " %d\n", (p2 - p1) / n);
+ } else {
+ while ( p2 < eptr && patncmp(p2, n) == FALSE ) p2 += n;
+ if ( p2 > eptr ) p2 = eptr;
+ fprintf(fp_out, "%d ", p2 - p1);
+ while ( p1 < p2 )
+ fprintf(fp_out, "%.2X", ((int) *p1++) & 0377);
+ fprintf(fp_out, " 0\n");
+ } /* End else */
+
+ fprintf(fp_out, "0\n");
+
+ rptr = raster;
+
+} /* End of putrast */
+
+/*****************************************************************************/
+
+patncmp(p1, n)
+
+ char *p1; /* first patterns starts here */
+ int n; /* and extends this many bytes */
+
+{
+
+ char *p2; /* address of the second pattern */
+
+/*
+ *
+ * Compares the two n byte patterns *p1 and *(p1+n). FALSE is returned if they're
+ * different or extend past the end of the current raster line.
+ *
+ */
+
+ p2 = p1 + n;
+
+ for ( ; n > 0; n--, p1++, p2++ )
+ if ( p2 >= eptr || *p1 != *p2 )
+ return(FALSE);
+
+ return(TRUE);
+
+} /* End of patncmp */
+
+/*****************************************************************************/
+
+getint()
+
+{
+
+ int h, l; /* high and low bytes */
+
+/*
+ *
+ * Reads the next two bytes from *fp_in and returns the resulting integer.
+ *
+ */
+
+ if ( (l = getc(fp_in)) == EOF || (h = getc(fp_in)) == EOF )
+ return(-1);
+
+ return((h & 0377) << 8 | (l & 0377));
+
+} /* End of getint */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postdmd/postdmd.mk b/sys/src/cmd/postscript/postdmd/postdmd.mk
new file mode 100755
index 000000000..d60ba84b8
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/postdmd.mk
@@ -0,0 +1,92 @@
+MAKE=/bin/make
+MAKEFILE=postdmd.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+HFILES=$(COMMONDIR)/comments.h\
+ $(COMMONDIR)/ext.h\
+ $(COMMONDIR)/gen.h\
+ $(COMMONDIR)/path.h
+
+OFILES=postdmd.o\
+ $(COMMONDIR)/glob.o\
+ $(COMMONDIR)/misc.o\
+ $(COMMONDIR)/request.o
+
+all : postdmd
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ @if [ ! -d "$(POSTLIB)" ]; then \
+ mkdir $(POSTLIB); \
+ chmod 755 $(POSTLIB); \
+ chgrp $(GROUP) $(POSTLIB); \
+ chown $(OWNER) $(POSTLIB); \
+ fi
+ cp postdmd $(POSTBIN)/postdmd
+ @chmod 755 $(POSTBIN)/postdmd
+ @chgrp $(GROUP) $(POSTBIN)/postdmd
+ @chown $(OWNER) $(POSTBIN)/postdmd
+ cp postdmd.ps $(POSTLIB)/postdmd.ps
+ @chmod 644 $(POSTLIB)/postdmd.ps
+ @chgrp $(GROUP) $(POSTLIB)/postdmd.ps
+ @chown $(OWNER) $(POSTLIB)/postdmd.ps
+ cp postdmd.1 $(MAN1DIR)/postdmd.1
+ @chmod 644 $(MAN1DIR)/postdmd.1
+ @chgrp $(GROUP) $(MAN1DIR)/postdmd.1
+ @chown $(OWNER) $(MAN1DIR)/postdmd.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postdmd
+
+postdmd : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postdmd $(OFILES)
+
+postdmd.o : $(HFILES)
+
+$(COMMONDIR)/glob.o\
+$(COMMONDIR)/misc.o\
+$(COMMONDIR)/request.o :
+ @cd $(COMMONDIR); $(MAKE) -f common.mk `basename $@`
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ postdmd.1 >XXX.1; \
+ mv XXX.1 postdmd.1
+
diff --git a/sys/src/cmd/postscript/postdmd/postdmd.ps b/sys/src/cmd/postscript/postdmd/postdmd.ps
new file mode 100755
index 000000000..fa312af02
--- /dev/null
+++ b/sys/src/cmd/postscript/postdmd/postdmd.ps
@@ -0,0 +1,124 @@
+%
+% Version 3.3.2 prologue for DMD bitmap files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/rotation 1 def
+/screenres 100 def
+/xoffset 0 def
+/yoffset 0 def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ magnification dup aspectratio mul scale
+
+ /height height margin sub def
+ /width width margin sub def
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/bitmap {
+ /scanlines exch def
+ /scanlength exch def
+ /flip exch def
+ /v8format exch def
+
+ /bytelength scanlength 8 idiv def
+ /picstr bytelength string def
+ /lpicstr bytelength string def
+ /bytelength bytelength 1 sub def
+
+ gsave
+
+% First the overall scaling.
+
+ height scanlines div width scanlength div min
+ 72 screenres div min
+ dup scale
+
+% Followed by the one for the unit square.
+
+ scanlength neg 2 div scanlines neg 2 div translate
+ scanlength scanlines scale
+ v8format {getv8bitmap} {getbitmap} ifelse
+ grestore
+} bind def
+
+/getbitmap {
+ scanlength scanlines flip [scanlength 0 0 scanlines neg 0 scanlines] {
+ 0 {
+ currentfile token pop dup
+ 0 eq {pop pop exit} if
+ /charcount exch def
+ picstr 1 index charcount getinterval
+ /repl exch def
+ currentfile repl readhexstring pop pop
+ charcount add
+ currentfile token pop {
+ picstr 1 index repl putinterval
+ charcount add
+ } repeat
+ } loop
+ picstr
+ } imagemask
+} bind def
+
+/getv8bitmap {
+ scanlength scanlines flip not [scanlength 0 0 scanlines neg 0 scanlines] {
+ 0 {
+ currentfile token pop dup
+ 0 eq {pop pop exit} if
+ /charcount exch def
+ picstr 1 index charcount getinterval
+ /repl exch def
+ currentfile repl readhexstring pop pop
+ charcount add
+ currentfile token pop {
+ picstr 1 index repl putinterval
+ charcount add
+ } repeat
+ } loop
+ 0 0 picstr {
+ exch lpicstr exch get xor
+ lpicstr exch 2 index exch put
+ 1 add dup
+ } forall
+ pop pop lpicstr
+ } imagemask
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/postgif/mkfile b/sys/src/cmd/postscript/postgif/mkfile
new file mode 100755
index 000000000..667f07acb
--- /dev/null
+++ b/sys/src/cmd/postscript/postgif/mkfile
@@ -0,0 +1,32 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=postgif
+OFILES=postgif.$O\
+
+COMMONDIR=../common
+
+HFILES=$COMMONDIR/comments.h\
+ $COMMONDIR/ext.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+LIB=$COMMONDIR/com.a$O
+
+</sys/src/cmd/mkone
+
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+install:V: $POSTLIB/postgif.ps
+
+$POSTLIB/postgif.ps: postgif.ps
+ cp $prereq $target
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/postgif/postgif.c b/sys/src/cmd/postscript/postgif/postgif.c
new file mode 100755
index 000000000..d66ec6543
--- /dev/null
+++ b/sys/src/cmd/postscript/postgif/postgif.c
@@ -0,0 +1,755 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h"
+#include "gen.h"
+#include "path.h"
+#include "ext.h"
+
+#define dbprt if (debug) fprintf
+
+char *optnames = "a:c:fglm:n:o:p:x:y:C:E:DG:IL:P:";
+char *prologue = POSTGIF; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+int page = 0; /* last page we worked on */
+int printed = 0; /* and the number of pages printed */
+
+extern char *malloc();
+extern void free();
+extern double atof(), pow();
+
+unsigned char ibuf[BUFSIZ];
+unsigned char *cmap, *gcmap, *lcmap;
+unsigned char *gmap, *ggmap, *lgmap;
+unsigned char *pmap;
+double gamma;
+float cr = 0.3, cg = 0.59, cb = 0.11;
+int maplength, gmaplength, lmaplength;
+int scrwidth, scrheight;
+int gcolormap, lcolormap;
+int bitperpixel, background;
+int imageleft, imagetop;
+int imagewidth, imageheight;
+int interlaced, lbitperpixel;
+int gray = 0;
+int gammaflag = 0;
+int negative = 0;
+int terminate = 0;
+int codesize, clearcode, endcode, curstblsize, pmindex, byteinibuf, bitsleft;
+int prefix[4096], suffix[4096], cstbl[4096];
+int bburx = -32767, bbury = -32767;
+FILE *fp_in = NULL;
+FILE *fp_out = stdout;
+
+char *
+allocate(size)
+ int size;
+{
+ char *p;
+
+ if ((p = malloc(size)) == NULL) error(FATAL, "not enough memory");
+ return(p);
+}
+
+void
+puthex(c, fp)
+ unsigned char c;
+ FILE *fp;
+{
+ static char hextbl[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+ };
+
+ putc(hextbl[(c >> 4) & 017], fp);
+ putc(hextbl[c & 017], fp);
+}
+
+void
+setcolormap(bp)
+ int bp;
+{
+ int i, entries = 1, scale = 1;
+ unsigned char *p, *q;
+
+ for (i = 0; i < bp; i++) entries *= 2;
+ for (i = 0; i < 8 - bp; i++) scale *= 2;
+ gcmap = (unsigned char *) allocate(entries*3);
+ ggmap = (unsigned char *) allocate(entries);
+ gmaplength = entries;
+ for (i = 0, p = gcmap, q = ggmap; i < 256; i += scale, p += 3, q++) {
+ if (negative) {
+ *p = 255 - i; p[1] = *p; p[2] = *p;
+ *q = *p;
+ }
+ else {
+ *p = i; p[1] = i; p[2] = i;
+ *q = i;
+ }
+ }
+ if (gammaflag)
+ for (i = 0, p = gcmap; i < 256; i += scale, p += 3) {
+ *p = (unsigned char) (pow((double) *p/256.0, gamma)*256);
+ p[1] = *p; p[2] = *p;
+ }
+dbprt(stderr,"default color map:\n");
+for (i = 0; i < entries*3; i += 3)
+dbprt(stderr, "%d, %d, %d\n", gcmap[i], gcmap[i+1], gcmap[i+2]);
+}
+
+void
+readgcolormap(bp)
+ int bp;
+{
+ int i, entries = 1;
+ unsigned char *p, *q;
+
+ for (i = 0; i < bp; i++) entries *= 2;
+ gcmap = (unsigned char *) allocate(entries*3);
+ ggmap = (unsigned char *) allocate(entries);
+ gmaplength = entries;
+ fread(gcmap, sizeof(*gcmap), entries*3, fp_in);
+ if (negative)
+ for (i = 0, p = gcmap; i < entries*3; i++, p++) *p = 255 - *p;
+ for (i = 0, p = gcmap, q = ggmap; i < entries; i++, p += 3, q++)
+ *q = cr*(int)p[0] + cg*(int)p[1] + cb*(int)p[2] + 0.5;
+ if (gammaflag)
+ for (i = 0, p = gcmap; i < entries*3; i++, p++)
+ *p = (unsigned char) (pow((double) *p/256.0, gamma)*256);
+dbprt(stderr,"global color map:\n");
+for (i = 0; i < entries*3; i += 3)
+dbprt(stderr, "%d, %d, %d\n", gcmap[i], gcmap[i+1], gcmap[i+2]);
+}
+
+void
+readlcolormap(bp)
+ int bp;
+{
+ int i, entries = 1;
+ unsigned char *p, *q;
+
+ for (i = 0; i < bp; i++) entries *= 2;
+ lcmap = (unsigned char *) allocate(entries*3);
+ lgmap = (unsigned char *) allocate(entries);
+ lmaplength = entries;
+ fread(lcmap, sizeof(*lcmap), entries*3, fp_in);
+ if (negative)
+ for (i = 0, p = lcmap; i < entries*3; i++, p++) *p = 255 - *p;
+ for (i = 0, p = lcmap, q = lgmap; i < entries; i++, p += 3, q++)
+ *q = cr*(int)p[0] + cg*(int)p[1] + cb*(int)p[2] + 0.5;
+ if (gammaflag)
+ for (i = 0, p = lcmap; i < entries*3; i++, p++)
+ *p = (unsigned char) (pow((double) *p/256.0, gamma)*256);
+dbprt(stderr,"local color map:\n");
+for (i = 0; i < entries*3; i += 3)
+dbprt(stderr, "%d, %d, %d\n", lcmap[i], lcmap[i+1], lcmap[i+2]);
+}
+
+void
+initstbl()
+{
+ int i, entries = 1, *p, *s;
+
+ for (i = 0; i < codesize; i++) entries *= 2;
+ clearcode = entries;
+ endcode = clearcode + 1;
+ for (i = 0, p = prefix, s = suffix; i <= endcode; i++, p++, s++) {
+ *p = endcode;
+ *s = i;
+ }
+ curstblsize = endcode + 1;
+ pmindex = 0;
+ byteinibuf = 0;
+ bitsleft = 0;
+}
+
+int
+nextbyte()
+{
+ static ibufindex;
+
+ if (byteinibuf) {
+ byteinibuf--;
+ ibufindex++;
+ }
+ else {
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ byteinibuf = ibuf[0];
+dbprt(stderr, "byte count: %d\n", byteinibuf);
+ if (byteinibuf) fread(ibuf, sizeof(*ibuf), byteinibuf, fp_in);
+ else error(FATAL, "encounter zero byte count block before end code");
+ ibufindex = 0;
+ byteinibuf--;
+ ibufindex++;
+ }
+ return(ibuf[ibufindex-1]);
+}
+
+int masktbl[25] = {
+ 0, 01, 03, 07, 017, 037, 077, 0177, 0377, 0777, 01777, 03777, 07777,
+ 017777, 037777, 077777, 0177777, 0377777, 0777777, 01777777, 03777777,
+ 07777777, 017777777, 037777777, 077777777
+};
+
+int
+getcode()
+{
+ int cs, c;
+ static int oldc;
+
+ if (curstblsize < 4096) cs = cstbl[curstblsize];
+ else cs = 12;
+ while (bitsleft < cs) {
+ oldc = (oldc & masktbl[bitsleft]) | ((nextbyte() & 0377) << bitsleft);
+ bitsleft += 8;
+ }
+ c = oldc & masktbl[cs];
+ oldc = oldc >> cs;
+ bitsleft -= cs;
+/* dbprt(stderr, "code: %d %d %d\n", curstblsize, cs, c); */
+ return(c);
+}
+
+void
+putcode(c)
+ int c;
+{
+ if (prefix[c] != endcode) {
+ putcode(prefix[c]);
+ pmap[pmindex] = suffix[c];
+ pmindex++;
+ }
+ else {
+ pmap[pmindex] = suffix[c];
+ pmindex++;
+ }
+}
+
+int
+firstof(c)
+ int c;
+{
+ while (prefix[c] != endcode) c = prefix[c];
+ return(suffix[c]);
+}
+
+void
+writeimage()
+{
+ int i, j, k;
+
+dbprt(stderr, "pmindex: %d\n", pmindex);
+ fputs("save\n", fp_out);
+ fprintf(fp_out, "/codestr %d string def\n", imagewidth);
+ if (!gray) {
+ fprintf(fp_out, "/colortbl currentfile %d string readhexstring\n",
+ maplength*3);
+ for (i = 0; i < maplength; i++) puthex(cmap[i], fp_out);
+ fputs("\n", fp_out);
+ for (i = maplength ; i < maplength*2; i++) puthex(cmap[i], fp_out);
+ fputs("\n", fp_out);
+ for (i = maplength*2 ; i < maplength*3; i++) puthex(cmap[i], fp_out);
+ fputs("\npop def\n", fp_out);
+ fprintf(fp_out, "/graytbl currentfile %d string readhexstring\n",
+ maplength);
+ for (i = 0; i < maplength; i++) puthex(gmap[i], fp_out);
+ fputs("\npop def\n", fp_out);
+ }
+ fprintf(fp_out, "%s %d %d %d %d gifimage\n",
+ gray ? "true" : "false", imagewidth, imageheight,
+ scrwidth - imageleft - imagewidth, scrheight - imagetop - imageheight);
+ if (gray) {
+ if (interlaced) {
+ int *iltbl;
+
+ iltbl = (int *) allocate(imageheight*sizeof(int));
+ j = 0;
+ for (i = 0; i < imageheight; i += 8) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass1: %d\n", j);
+ for (i = 4; i < imageheight; i += 8) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass2: %d\n", j);
+ for (i = 2; i < imageheight; i += 4) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass3: %d\n", j);
+ for (i = 1; i < imageheight; i += 2) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass4: %d\n", j);
+
+ for (i = 0; i < imageheight; i++) {
+ k = iltbl[i];
+ for (j = 0; j < imagewidth; j++, k++)
+ puthex(gmap[pmap[k]], fp_out);
+ fputs("\n", fp_out);
+ }
+ }
+ else {
+ for (i = 0, k = 0; i < imageheight; i++) {
+ for (j = 0; j < imagewidth; j++, k++)
+ puthex(gmap[pmap[k]], fp_out);
+ fputs("\n", fp_out);
+ }
+ }
+ }
+ else {
+ if (interlaced) {
+ int *iltbl;
+
+ iltbl = (int *) allocate(imageheight*sizeof(int));
+ j = 0;
+ for (i = 0; i < imageheight; i += 8) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass1: %d\n", j);
+ for (i = 4; i < imageheight; i += 8) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass2: %d\n", j);
+ for (i = 2; i < imageheight; i += 4) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass3: %d\n", j);
+ for (i = 1; i < imageheight; i += 2) {
+ iltbl[i] = j;
+ j += imagewidth;
+ }
+dbprt(stderr, "pass4: %d\n", j);
+
+ for (i = 0; i < imageheight; i++) {
+ k = iltbl[i];
+ for (j = 0; j < imagewidth; j++, k++) puthex(pmap[k], fp_out);
+ fputs("\n", fp_out);
+ }
+ }
+ else {
+ for (i = 0, k = 0; i < imageheight; i++) {
+ for (j = 0; j < imagewidth; j++, k++) puthex(pmap[k], fp_out);
+ fputs("\n", fp_out);
+ }
+ }
+ }
+ fputs("restore\n", fp_out);
+}
+
+void
+readimage()
+{
+ int bytecount, zerobytecount = 0;
+ int code, oldcode;
+
+ fread(ibuf, sizeof(*ibuf), 9, fp_in);
+ imageleft = ibuf[0] + 256*ibuf[1];
+ imagetop = ibuf[2] + 256*ibuf[3];
+ imagewidth = ibuf[4] + 256*ibuf[5];
+ imageheight = ibuf[6] + 256*ibuf[7];
+ lcolormap = ibuf[8] & 0200;
+ interlaced = ibuf[8] & 0100;
+ lbitperpixel = (ibuf[8] & 07) + 1;
+dbprt(stderr, "imageleft: %d\n", imageleft);
+dbprt(stderr, "imagetop: %d\n", imagetop);
+dbprt(stderr, "imagewidth: %d\n", imagewidth);
+dbprt(stderr, "imgaeheight: %d\n", imageheight);
+dbprt(stderr, "lcolormap: %d\n", lcolormap ? 1 : 0);
+dbprt(stderr, "interlaced: %d\n", interlaced ? 1 : 0);
+dbprt(stderr, "lbitperpixel: %d\n", lbitperpixel);
+ if (lcolormap) {
+ readlcolormap(lbitperpixel);
+ cmap = lcmap;
+ gmap = lgmap;
+ maplength = lmaplength;
+ }
+
+dbprt(stderr, "start reading raster data\n");
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ codesize = ibuf[0];
+dbprt(stderr, "codesize: %d\n", codesize);
+ pmap = (unsigned char *) allocate(imagewidth*imageheight);
+ initstbl();
+ while ((code = getcode()) != endcode) {
+ if (code == clearcode) {
+ curstblsize = endcode + 1;
+ code = getcode();
+ putcode(code);
+ oldcode = code;
+ }
+ else if (code < curstblsize) {
+ putcode(code);
+ prefix[curstblsize] = oldcode;
+ suffix[curstblsize] = firstof(code);
+ curstblsize++;
+ oldcode = code;
+ }
+ else {
+ if (code != curstblsize) error(FATAL, "code out of order");
+ prefix[curstblsize] = oldcode;
+ suffix[curstblsize] = firstof(oldcode);
+ curstblsize++;
+ putcode(curstblsize-1);
+ oldcode = code;
+ }
+ }
+dbprt(stderr, "finish reading raster data\n");
+
+ /* read the rest of the raster data */
+ do {
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ bytecount = ibuf[0];
+dbprt(stderr, "byte count: %d\n", bytecount);
+ if (bytecount) fread(ibuf, sizeof(*ibuf), bytecount, fp_in);
+ else zerobytecount = 1;
+ } while (!zerobytecount);
+
+ writeimage();
+
+ if (lcolormap) {
+ cmap = gcmap;
+ gmap = ggmap;
+ maplength = gmaplength;
+ free(lcmap);
+ free(lgmap);
+ }
+}
+
+void
+readextensionblock()
+{
+ int functioncode, bytecount, zerobytecount = 0;
+
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ functioncode = ibuf[0];
+dbprt(stderr, "function code: %d\n", functioncode);
+ do {
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ bytecount = ibuf[0];
+dbprt(stderr, "byte count: %d\n", bytecount);
+ if (bytecount) fread(ibuf, sizeof(*ibuf), bytecount, fp_in);
+ else zerobytecount = 1;
+ } while (!zerobytecount);
+}
+
+void
+writebgscr()
+{
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fputs("/saveobj save def\n", fp_out);
+ fprintf(fp_out, "%s: %d %d %d %d\n",
+ "%%PageBoundingBox", 0, 0, scrwidth, scrheight);
+ if (scrwidth > bburx) bburx = scrwidth;
+ if (scrheight > bbury) bbury = scrheight;
+ fprintf(fp_out, "%d %d gifscreen\n", scrwidth, scrheight);
+}
+
+void
+writeendscr()
+{
+ if ( fp_out == stdout ) printed++;
+ fputs("showpage\n", fp_out);
+ fputs("saveobj restore\n", fp_out);
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+}
+
+void
+redirect(pg)
+ int pg; /* next page we're printing */
+{
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+}
+
+void
+readgif()
+{
+ int i, j, k;
+
+ for (i = 0, j = 1, k = 0; i < 13; i++) {
+ for (; k < j; k++) cstbl[k] = i;
+ j *= 2;
+ }
+
+ fread(ibuf, sizeof(*ibuf), 6, fp_in);
+dbprt(stderr, "%.6s\n", ibuf);
+ if (strncmp((char *)ibuf, "GIF87a", 6) != 0) {
+ fread(ibuf, sizeof(*ibuf), 122, fp_in);
+ fread(ibuf, sizeof(*ibuf), 6, fp_in);
+dbprt(stderr, "%.6s\n", ibuf);
+ if (strncmp((char *)ibuf, "GIF87a", 6) != 0)
+ error(FATAL, "wrong GIF signature");
+ }
+ fread(ibuf, sizeof(*ibuf), 7, fp_in);
+ scrwidth = ibuf[0] + 256*ibuf[1];
+ scrheight = ibuf[2] + 256*ibuf[3];
+ gcolormap = ibuf[4] & 0200;
+ bitperpixel = (ibuf[4] & 07) + 1;
+ background = ibuf[5];
+dbprt(stderr, "scrwidth: %d\n", scrwidth);
+dbprt(stderr, "scrheight: %d\n", scrheight);
+dbprt(stderr, "gcolormap: %d\n", gcolormap ? 1 : 0);
+dbprt(stderr, "bitperpixel: %d\n", bitperpixel);
+dbprt(stderr, "background: %d\n", background);
+ if (ibuf[6] != 0) error(FATAL, "wrong screen descriptor");
+ if (gcolormap) readgcolormap(bitperpixel);
+ else setcolormap(bitperpixel);
+
+ redirect(++page);
+ writebgscr();
+
+ cmap = gcmap;
+ gmap = ggmap;
+ maplength = gmaplength;
+
+ do {
+ fread(ibuf, sizeof(*ibuf), 1, fp_in);
+ if (ibuf[0] == ',') readimage();
+ else if (ibuf[0] == ';') terminate = 1;
+ else if (ibuf[0] == '!') readextensionblock();
+ else
+ error(FATAL, "wrong image separator character or wrong GIF terminator");
+ } while (!terminate);
+
+ writeendscr();
+
+ free(gcmap);
+ free(ggmap);
+}
+
+void
+init_signals()
+{
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ }
+ else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ }
+
+ signal(SIGTERM, interrupt);
+}
+
+void
+header()
+{
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", BOUNDINGBOX, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+}
+
+void
+options()
+{
+ int ch; /* return value from getopt() */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f':
+ negative = TRUE;
+ break;
+
+ case 'g':
+ gray = TRUE;
+ break;
+
+ case 'l':
+ fprintf(stdout, "/alignment true def\n");
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding - unnecessary */
+ fontencoding = optarg;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'G':
+ gammaflag = ON;
+ gamma = atof(optarg);
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+
+ }
+ }
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+}
+
+void
+setup()
+{
+ /*setencoding(fontencoding);*/
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) { /* followed by stuff for multiple pages
+*/
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+}
+
+void
+arguments()
+{
+ if ( argc < 1 ) {
+ fp_in = stdin;
+ readgif();
+ }
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ readgif();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ }
+ }
+}
+
+void
+done()
+{
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s 0 0 %d %d\n", BOUNDINGBOX, bburx, bbury);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+}
+
+main(agc, agv)
+ int agc;
+ char *agv[];
+{
+ argc = agc;
+ argv = agv;
+ prog_name = argv[0];
+
+ init_signals();
+ header();
+ options();
+ setup();
+ arguments();
+ done();
+
+ exit(0);
+}
+
diff --git a/sys/src/cmd/postscript/postgif/postgif.ps b/sys/src/cmd/postscript/postgif/postgif.ps
new file mode 100755
index 000000000..9827857f7
--- /dev/null
+++ b/sys/src/cmd/postscript/postgif/postgif.ps
@@ -0,0 +1,104 @@
+%
+% Version 3.3.2 prologue for GIF pixmap files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/formsperpage 1 def
+/landscape false def
+/magnification 1 def
+/margin 0 def
+/orientation 0 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ magnification dup aspectratio mul scale
+
+ /height height margin sub def
+ /width width margin sub def
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/done {/lastpage where {pop lastpage} if} def
+
+/alignment false def
+
+/gifscreen { % scrwidth scrheight $
+ 2 copy
+
+ alignment {
+ 100 dup dtransform exch 100 exch div abs exch 100 exch div abs
+ 2 copy scale
+ /height exch height exch div def
+ /width exch width exch div def
+ } if
+
+ height exch div exch width exch div
+ 2 copy lt { pop } { exch pop } ifelse
+
+ alignment { cvi } if
+
+ dup scale
+
+ neg 2 div exch neg 2 div exch translate
+} def
+
+/gifimage { % gray imagewidth imageheight xorigin yorigin $
+ translate
+ 2 copy scale
+ /imageheight exch def
+ /imagewidth exch def
+ /gray exch def
+ imagewidth imageheight 8 [imagewidth 0 0 imageheight neg 0 imageheight]
+ gray {
+ { currentfile codestr readhexstring pop } image
+ } {
+ /colorimage where {
+ pop
+ /picstr imagewidth 3 mul string def
+ { currentfile codestr readhexstring pop pop
+ 0 1 imagewidth 1 sub {
+ picstr exch dup 3 mul exch colortbl exch codestr exch get
+ 3 mul 3 getinterval putinterval
+ } for picstr
+ } false 3 colorimage
+ } {
+ { currentfile codestr readhexstring pop pop
+ 0 1 imagewidth 1 sub {
+ codestr exch dup graytbl exch codestr exch get get put
+ } for codestr
+ } image
+ } ifelse
+ } ifelse
+} def
diff --git a/sys/src/cmd/postscript/postio/README b/sys/src/cmd/postscript/postio/README
new file mode 100755
index 000000000..88da4bc4f
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/README
@@ -0,0 +1,20 @@
+Serial communications program for PostScript printers.
+
+Runs as a single read/write process (by default). Use the -R2 option
+or set splitme to TRUE (file postio.c) to get separate read and write
+processes. Although not the default, we recommend using separate read
+and write processes.
+
+Sends occasional status queries (control Ts) while transmitting files.
+Use the -q option or set quiet (file postio.c) to TRUE to disable status
+queries.
+
+Datakit connections are supported on System V and Ninth Edition systems.
+The syntax (for connecting to a Datakit destination) varies. Check the
+SYSV and V9 versions of setupline() in file ifdef.c.
+
+Set DKHOST and DKSTREAMS to TRUE in postio.mk for streams based DKHOST
+support. When DKSTREAMS is TRUE postio.mk uses "dknetty" as the stream
+module. Settings like DKSTREAMS=dkty select a different stream module
+and may be required for full Datakit support on some systems.
+
diff --git a/sys/src/cmd/postscript/postio/ifdef.c b/sys/src/cmd/postscript/postio/ifdef.c
new file mode 100755
index 000000000..1d7be0905
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/ifdef.c
@@ -0,0 +1,867 @@
+/*
+ *
+ * Conditionally compiled routines for setting up and reading the line. Things
+ * were getting out of hand with all the ifdefs, and even though this defeats
+ * part of the purpose of conditional complilation directives, I think it's easier
+ * to follow this way. Thanks to Alan Buckwalter for the System V DKHOST code.
+ *
+ * postio now can be run as separate read and write processes, but requires that
+ * you write a procedure called resetline() and perhaps modify readline() some.
+ * I've already tested the code on System V and it seems to work. Ninth Edition
+ * and BSD code may be missing.
+ *
+ * By request I've changed the way some of the setupline() procedures (eg. in the
+ * System V implementation) handle things when no line has been given. If line is
+ * NULL the new setupline() procedures try to continue, assuming whoever called
+ * postio connected stdout to the printer. Things will only work if we can read
+ * and write stdout!
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "ifdef.h" /* conditional header file inclusion */
+#include "gen.h" /* general purpose definitions */
+
+FILE *fp_ttyi, *fp_ttyo;
+char *ptr = mesg;
+
+extern int window_size;
+
+/*****************************************************************************/
+
+#ifdef SYSV
+setupline()
+
+{
+
+ struct termio termio;
+
+/*
+ *
+ * Line initialization for SYSV. For now if no line is given (ie. line == NULL )
+ * we continue on as before using stdout as ttyi and ttyo. Doesn't work when we're
+ * running in interactive mode or forcing stuff that comes back from the printer
+ * to stdout. Both cases are now caught by a test that's been added to routine
+ * initialize(). The change is primarily for the version of lp that's available
+ * with SVR3.2.
+ *
+ */
+
+#ifdef DKHOST
+ if ( line != NULL && *line != '/' ) {
+ if ( strncmp(line, "DK:", 3) == 0 )
+ line += 3;
+ dkhost_connect();
+#ifdef DKSTREAMS
+ if ( ioctl(ttyi, I_PUSH, DKSTREAMS) == -1 )
+ error(FATAL, "ioctl error - %s", DKSTREAMS);
+ if ( ioctl(ttyi, I_PUSH, "ldterm") == -1 )
+ error(FATAL, "ioctl error - ldterm");
+#endif
+ } else
+#endif
+
+ if ( line == NULL )
+ ttyi = fileno(stdout);
+ else if ( (ttyi = open(line, O_RDWR)) == -1 )
+ error(FATAL, "can't open %s", line);
+
+ if ( (ttyo = dup(ttyi)) == -1 )
+ error(FATAL, "can't dup file descriptor for %s", line);
+
+ if ( stopbits == 1 )
+ stopbits = 0;
+ else stopbits = CSTOPB;
+
+ if ( fcntl(ttyi, F_SETFL, O_NDELAY) == -1 )
+ error(FATAL, "fcntl error - F_SETFL");
+
+ if ( ioctl(ttyi, TCGETA, &termio) == -1 )
+ error(FATAL, "ioctl error - TCGETA");
+
+ termio.c_iflag = IXON | IGNCR;
+ termio.c_oflag = 0;
+ termio.c_cflag = HUPCL | CREAD | CS8 | stopbits | baudrate;
+ termio.c_lflag = 0;
+ termio.c_cc[VMIN] = termio.c_cc[VTIME] = 0;
+
+ if ( ioctl(ttyi, TCSETA, &termio) == -1 )
+ error(FATAL, "ioctl error - TCSETA");
+
+ if ( ioctl(ttyi, TCFLSH, 2) == -1 )
+ error(FATAL, "ioctl error - TCFLSH");
+
+ fp_ttyi = fdopen(ttyi, "r");
+
+} /* End of setupline */
+
+/*****************************************************************************/
+
+resetline()
+
+{
+
+ int flags; /* for turning O_NDELAY off */
+ struct termio termio; /* so we can reset flow control */
+
+/*
+ *
+ * Only used if we're running the program as separate read and write processes.
+ * Called from split() after the initial connection has been made and returns
+ * TRUE if two processes should work. Don't know if the O_NDELAY stuff is really
+ * needed, but setting c_cc[VMIN] to 1 definitely is. If we leave it be (as a 0)
+ * the read in readline() won't block!
+ *
+ */
+
+ if ( (flags = fcntl(ttyi, F_GETFL, 0)) == -1 )
+ error(FATAL, "fcntl error - F_GETFL");
+
+ flags &= ~O_NDELAY;
+
+ if ( fcntl(ttyi, F_SETFL, flags) == -1 )
+ error(FATAL, "fcntl error - F_SETFL");
+
+ if ( ioctl(ttyi, TCGETA, &termio) == -1 )
+ error(FATAL, "ioctl error - TCGETA");
+
+ termio.c_iflag &= ~IXANY;
+ termio.c_iflag |= IXON | IXOFF;
+ termio.c_cc[VMIN] = 1;
+ termio.c_cc[VTIME] = 0;
+
+ if ( ioctl(ttyi, TCSETA, &termio) == -1 )
+ error(FATAL, "ioctl error - TCSETA");
+
+ return(TRUE);
+
+} /* End of resetline */
+
+/*****************************************************************************/
+
+setupstdin(mode)
+
+ int mode; /* what to do with stdin settings */
+
+{
+
+ struct termio termio;
+
+ static int saved = FALSE;
+ static struct termio oldtermio;
+
+/*
+ *
+ * Save (mode = 0), reset (mode = 1), or restore (mode = 2) the tty settings for
+ * stdin. Expect something like raw mode with no echo will be set up. Explicit
+ * code to ensure blocking reads probably isn't needed because blocksize is set
+ * to 1 when we're in interactive mode, but I've included it anyway.
+ *
+ */
+
+ if ( interactive == TRUE )
+ switch ( mode ) {
+ case 0:
+ if ( isatty(0) != 1 )
+ error(FATAL, "stdin not a terminal - can't run interactive mode");
+ if ( ioctl(0, TCGETA, &oldtermio) == -1 )
+ error(FATAL, "can't save terminal settings");
+ saved = TRUE;
+ break;
+
+ case 1:
+ termio = oldtermio;
+ termio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+ termio.c_cc[VMIN] = 1;
+ termio.c_cc[VTIME] = 0;
+ ioctl(0, TCSETA, &termio);
+ break;
+
+ case 2:
+ if ( saved == TRUE )
+ ioctl(0, TCSETA, &oldtermio);
+ break;
+ } /* End switch */
+
+} /* End of setupstdin */
+
+/*****************************************************************************/
+
+readline()
+
+{
+
+ int n; /* read() return value */
+ int ch; /* for interactive mode */
+
+ static int tries = 0; /* consecutive times read returned 0 */
+
+/*
+ *
+ * Reads characters coming back from the printer on ttyi up to a newline (or EOF)
+ * or until no more characters are available. Characters are put in mesg[], the
+ * string is terminated with '\0' when we're done with a line and TRUE is returned
+ * to the caller. If complete line wasn't available FALSE is returned. Interactive
+ * mode should loop here forever, except during start(), echoing characters to
+ * stdout. If it happens to leave FALSE should be returned. The non-blocking read
+ * gets us out until split() is called.
+ *
+ * Some users (apparently just on 3B2 DKHOST systems) have had problems with the
+ * two process implementation that's forced me to kludge things up some. When a
+ * printer (on those systems) is turned off while postio is transmitting files
+ * the write process hangs in writeblock() (postio.c) - it's typically in the
+ * middle of a write() call, while the read() call (below) continually returns 0.
+ * In the original code readline() returned FALSE when read() returned 0 and we
+ * get into a loop that never ends - because the write process is hung. In the
+ * one process implementation having read return 0 is legitimate because the line
+ * is opened for no delay, but with two processes the read() blocks and a return
+ * value of 0 should never occur. From my point of view the real problem is that
+ * the write() call hangs on 3B2 DKHOST systems and apparently doesn't anywhere
+ * else. If the write returned anything less than or equal to 0 writeblock() would
+ * shut things down. The kludge I've implemented counts the number of consecutive
+ * times read() returns a 0 and if it exceeds a limit (100) the read process will
+ * shut things down. In fact one return of 0 from read() when we're in the two
+ * process mode is undoubtedly sufficient and no counting should be necessary!!!
+ * Moving the check to getstatus() should also work and is probably where things
+ * belong.
+ *
+ */
+
+ if ( interactive == FALSE ) {
+ while ( (n = read(ttyi, ptr, 1)) != 0 ) {
+ if ( n < 0 )
+ if ( errno == EINTR )
+ continue;
+ else error(FATAL, "error reading %s", line);
+ tries = 0;
+ if ( *ptr == '\n' || *ptr == '\004' || ptr >= endmesg ) {
+ *(ptr+1) = '\0';
+ if ( *ptr == '\004' )
+ strcpy(ptr, "%%[ status: endofjob ]%%\n");
+ ptr = mesg;
+ return(TRUE);
+ } /* End if */
+ ptr++;
+ } /* End while */
+ if ( canread == TRUE && canwrite == FALSE ) /* read process kludge */
+ if ( ++tries > 100 )
+ error(FATAL, "printer appears to be offline - shutting down");
+ return(FALSE);
+ } /* End if */
+
+ if ( canwrite == TRUE ) /* don't block during start() */
+ return(FALSE);
+
+ while ( (ch = getc(fp_ttyi)) != EOF )
+ putc(ch, stdout);
+ return(FALSE);
+
+} /* End of readline */
+#endif
+
+/*****************************************************************************/
+
+#ifdef V9
+#include <ipc.h>
+
+char tbuf[256]; /* temporary input buffer */
+char *nptr = tbuf; /* next character comes from here */
+char *eptr = tbuf; /* one past the last character in tbuf */
+
+setupline()
+
+{
+
+ struct sgttyb sgtty;
+ struct ttydevb ttydev; /* for setting up the line */
+ static struct tchars tchar = { '\377', /* interrupt */
+ '\377', /* quit */
+ '\021', /* start output */
+ '\023', /* stop output */
+ '\377', /* end-of-file */
+ '\377' /* input delimiter */
+ };
+
+/*
+ *
+ * Line initialization for V9.
+ *
+ */
+
+ if ( line == NULL ) {
+ ttyi = ttyo = 1;
+ return;
+ } /* End if */
+ alarm(120); /* watch for hanging opens */
+ if ( line[0] == '/' ) {
+ if ( (ttyi = open(line, O_RDWR)) == -1 )
+ error(FATAL, "can't open %s", line);
+ } else if ((ttyi = ipcopen(ipcpath(line, "dk", 0), "")) < 0) {
+ sleep(5); /* wait for Datakit to hangup */
+ if ((ttyi = ipcopen(ipcpath(line, "dk", 0), "")) < 0) {
+ fprintf(stderr, "%s", errstr);
+ error(FATAL, "can't ipcopen %s", line);
+ }
+ }
+ alarm(0);
+
+ if ( (ttyo = dup(ttyi)) == -1 )
+ error(FATAL, "can't dup file descriptor for %s", line);
+
+ if ( ioctl(ttyi, FIOPUSHLD, &tty_ld) == -1 )
+ error(FATAL, "ioctl error - FIOPUSHLD");
+
+ if ( ioctl(ttyi, TIOCGDEV, &ttydev) == -1 )
+ error(FATAL, "ioctl error - TIOCGDEV");
+
+ if ( ioctl(ttyi, TIOCGETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCGETP");
+
+ sgtty.sg_flags &= ~ECHO;
+ sgtty.sg_flags &= ~CRMOD;
+ sgtty.sg_flags |= CBREAK;
+ ttydev.ispeed = baudrate;
+ ttydev.ospeed = baudrate;
+
+ if ( ioctl(ttyi, TIOCSDEV, &ttydev) == -1 )
+ error(FATAL, "ioctl error - TIOCSDEV");
+
+ if ( ioctl(ttyi, TIOCSETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCSETP");
+
+ if ( ioctl(ttyi, TIOCSETC, &tchar) == -1 )
+ error(FATAL, "ioctl error - TIOCSETC");
+
+ fp_ttyi = fdopen(ttyi, "r");
+
+} /* End of setupline */
+
+/*****************************************************************************/
+
+resetline()
+
+{
+
+ struct sgttyb sgtty;
+
+/*
+ *
+ * Only used if we're running the program as separate read and write processes.
+ * Called from split() after the initial connection has been made and returns
+ * TRUE if two processes should work. Haven't tested or even compiled the stuff
+ * for separate read and write processes on Ninth Edition systems - no guarantees
+ * even though we return TRUE!
+ *
+ */
+
+ if ( ioctl(ttyi, TIOCGETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCGETP");
+
+ sgtty.sg_flags |= TANDEM;
+
+ if ( ioctl(ttyi, TIOCSETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCSETP");
+
+ return(TRUE);
+
+} /* End of resetline */
+
+/*****************************************************************************/
+
+setupstdin(mode)
+
+ int mode; /* what to do with stdin settings */
+
+{
+
+ struct sgttyb sgtty;
+
+ static int saved = FALSE;
+ static struct sgttyb oldsgtty;
+
+/*
+ *
+ * Save (mode = 0), reset (mode = 1), or restore (mode = 2) the tty settings for
+ * stdin. Expect something like raw mode with no echo will be set up. Need to make
+ * sure interrupt and quit still work - they're the only good way to exit when
+ * we're running interactive mode. I haven't tested or even compiled this code
+ * so there are no guarantees.
+ *
+ */
+
+ if ( interactive == TRUE )
+ switch ( mode ) {
+ case 0:
+ if ( ioctl(0, TIOCGETP, &oldsgtty) == -1 )
+ error(FATAL, "can't save terminal settings");
+ saved = TRUE;
+ break;
+
+ case 1:
+ sgtty = oldsgtty;
+ sgtty.sg_flags &= ~ECHO;
+ sgtty.sg_flags |= CBREAK;
+ ioctl(0, TIOCSETP, &sgtty);
+ break;
+
+ case 2:
+ if ( saved == TRUE )
+ ioctl(0, TIOCSETP, &oldsgtty);
+ break;
+ } /* End switch */
+
+} /* End of setupstdin */
+
+/*****************************************************************************/
+
+readline()
+
+{
+
+ int n; /* read() return value */
+ int ch; /* for interactive mode */
+
+/*
+ *
+ * Reads characters coming back from the printer on ttyi up to a newline (or EOF)
+ * and transfers each line to the mesg[] array. Everything available on ttyi is
+ * initially stored in tbuf[] and a line at a time is transferred from there to
+ * mesg[]. The string in mesg[] is terminated with a '\0' and TRUE is returned to
+ * the caller when we find a newline, EOF, or reach the end of the mesg[] array.
+ * If nothing is available on ttyi we return FALSE if a single process is being
+ * used for reads and writes, while in the two process implementation we force a
+ * one character read. Interactive mode loops here forever, except during start(),
+ * echoing everything that comes back on ttyi to stdout. The performance of a
+ * simple getc/putc loop for interactive mode was unacceptable when run under mux
+ * and has been replaced by more complicated code. When layers wasn't involved
+ * the getc/putc loop worked well.
+ *
+ */
+
+ if ( interactive == FALSE ) {
+ while ( 1 ) {
+ while ( nptr < eptr ) { /* grab characters from tbuf */
+ *ptr = *nptr++;
+ if ( *ptr == '\r' ) continue;
+ if ( *ptr == '\n' || *ptr == '\004' || ptr >= endmesg ) {
+ *(ptr+1) = '\0';
+ if ( *ptr == '\004' )
+ strcpy(ptr, "%%[ status: endofjob ]%%\n");
+ ptr = mesg;
+ return(TRUE);
+ } /* End if */
+ ++ptr;
+ } /* End for */
+
+ nptr = eptr = tbuf;
+ if ( ioctl(ttyi, FIONREAD, &n) < 0 )
+ if ( errno == EINTR )
+ continue;
+ else error(FATAL, "ioctl error - FIONREAD");
+ if ( n <= 0 )
+ if ( canwrite == TRUE )
+ return(FALSE);
+ n = ((n < 1) ? 1 : ((n < sizeof(tbuf)) ? n : sizeof(tbuf)));
+ if ( (n = read(ttyi, tbuf, n)) < 0 )
+ if ( errno == EINTR )
+ continue;
+ else error(FATAL, "error reading line %s", line);
+ else eptr = nptr + n;
+ } /* End while */
+ } /* End if */
+
+ if ( canwrite == TRUE ) /* don't block during start() */
+ return(FALSE);
+
+ while ( 1 ) { /* only interactive mode gets here */
+ if ( ioctl(ttyi, FIONREAD, &n) < 0 )
+ error(FATAL, "ioctl error - FIONREAD");
+ n = ((n < 1) ? 1 : ((n < sizeof(tbuf)) ? n : sizeof(tbuf)));
+ if ( (n = read(ttyi, tbuf, n)) < 0 )
+ error(FATAL, "error reading line %s", line);
+ else if ( n == 0 ) /* should not happen */
+ error(FATAL, "end of file in interactive mode");
+ if ( write(1, tbuf, n) != n )
+ error(FATAL, "error writing to stdout");
+ } /* End while */
+
+ return(FALSE);
+
+} /* End of readline */
+#endif
+
+/*****************************************************************************/
+
+#ifdef BSD4_2
+setupline()
+
+{
+
+ struct sgttyb sgtty;
+ static struct tchars tchar = { '\377', /* interrupt */
+ '\377', /* quit */
+ '\021', /* start output */
+ '\023', /* stop output */
+ '\377', /* end-of-file */
+ '\377' /* input delimiter */
+ };
+ long lmodes;
+ int disc = NTTYDISC;
+
+/*
+ *
+ * Line initialization for BSD4_2. As in the System V code, if no line is given
+ * (ie. line == NULL) we continue on as before using stdout as ttyi and ttyo.
+ *
+ */
+
+ if ( line == NULL )
+ ttyi = fileno(stdout);
+ else if ( (ttyi = open(line, O_RDWR)) == -1 )
+ error(FATAL, "can't open %s", line);
+
+ if ( (ttyo = dup(ttyi)) == -1 )
+ error(FATAL, "can't dup file descriptor for %s", line);
+
+ if (ioctl(ttyi, TIOCSETD, &disc) == -1 )
+ error(FATAL, "ioctl error - TIOCSETD");
+
+ if ( ioctl(ttyi, TIOCGETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCGETP");
+
+ if ( ioctl(ttyi, TIOCLGET, &lmodes) == -1 )
+ error(FATAL, "ioctl error - TIOCLGET");
+
+ sgtty.sg_flags &= ~ECHO;
+ sgtty.sg_flags &= ~CRMOD;
+ sgtty.sg_flags |= CBREAK;
+ sgtty.sg_ispeed = baudrate;
+ sgtty.sg_ospeed = baudrate;
+ lmodes |= LDECCTQ;
+
+ if ( ioctl(ttyi, TIOCSETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCSETP");
+
+ if ( ioctl(ttyi, TIOCSETC, &tchar) == -1 )
+ error(FATAL, "ioctl error - TIOCSETC");
+
+ if ( ioctl(ttyi, TIOCLSET, &lmodes) == -1 )
+ error(FATAL, "ioctl error - TIOCLSET");
+
+ fp_ttyi = fdopen(ttyi, "r");
+
+} /* End of setupline */
+
+/*****************************************************************************/
+
+resetline()
+
+{
+
+ struct sgttyb sgtty;
+
+/*
+ *
+ * Only used if we're running the program as separate read and write processes.
+ * Called from split() after the initial connection has been made and returns
+ * TRUE if two processes should work. Haven't tested or even compiled the stuff
+ * for separate read and write processes on Berkeley systems - no guarantees
+ * even though we return TRUE!
+ *
+ */
+
+ if ( ioctl(ttyi, TIOCGETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCGETP");
+
+ sgtty.sg_flags |= TANDEM;
+
+ if ( ioctl(ttyi, TIOCSETP, &sgtty) == -1 )
+ error(FATAL, "ioctl error - TIOCSETP");
+
+ return(TRUE);
+
+} /* End of resetline */
+
+/*****************************************************************************/
+
+setupstdin(mode)
+
+ int mode; /* what to do with stdin settings */
+
+{
+
+ struct sgttyb sgtty;
+
+ static int saved = FALSE;
+ static struct sgttyb oldsgtty;
+
+/*
+ *
+ * Save (mode = 0), reset (mode = 1), or restore (mode = 2) the tty settings for
+ * stdin. Expect something like raw mode with no echo will be set up. Need to make
+ * sure interrupt and quit still work - they're the only good way to exit when
+ * we're running interactive mode. I haven't tested or even compiled this code
+ * so there are no guarantees.
+ *
+ */
+
+ if ( interactive == TRUE )
+ switch ( mode ) {
+ case 0:
+ if ( isatty(0) != 1 )
+ error(FATAL, "stdin not a terminal - can't run interactive mode");
+ if ( ioctl(0, TIOCGETP, &oldsgtty) == -1 )
+ error(FATAL, "can't save terminal settings");
+ saved = TRUE;
+ break;
+
+ case 1:
+ sgtty = oldsgtty;
+ sgtty.sg_flags &= ~ECHO;
+ sgtty.sg_flags |= CBREAK;
+ ioctl(0, TIOCSETP, &sgtty);
+ break;
+
+ case 2:
+ if ( saved == TRUE )
+ ioctl(0, TIOCSETP, &oldsgtty);
+ break;
+ } /* End switch */
+
+} /* End of setupstdin */
+
+/*****************************************************************************/
+
+readline()
+
+{
+
+ int n; /* read() return value */
+ int ch; /* for interactive mode */
+
+/*
+ *
+ * Reads characters coming back from the printer on ttyo up to a newline (or EOF)
+ * or until no more characters are available. Characters are put in mesg[], the
+ * string is terminated with '\0' when we're done with a line and TRUE is returned
+ * to the caller. If complete line wasn't available FALSE is returned. Interactive
+ * mode should loop here forever, except during start(), echoing characters to
+ * stdout. If it happens to leave FALSE should be returned. Probably should read
+ * everything available on ttyi into a temporary buffer and work from there rather
+ * than reading one character at a time.
+ *
+ */
+
+ if ( interactive == FALSE ) {
+ while ( 1 ) {
+ if ( ioctl(ttyi, FIONREAD, &n) < 0 )
+ if ( errno == EINTR )
+ continue;
+ else error(FATAL, "ioctl error - FIONREAD");
+ if ( n <= 0 )
+ if ( canwrite == TRUE )
+ return(FALSE);
+ else n = 1;
+ for ( ; n > 0; n-- ) {
+ /*if ( read(ttyi, ptr, 1) < 0 )*/
+ if ( (*ptr = getc(fp_ttyi)) == EOF )
+ if ( errno == EINTR )
+ continue;
+ else error(FATAL, "error reading %s", line);
+ if ( *ptr == '\r' ) continue;
+ if ( *ptr == '\n' || *ptr == '\004' || ptr >= endmesg ) {
+ *(ptr+1) = '\0';
+ if ( *ptr == '\004' )
+ strcpy(ptr, "%%[ status: endofjob ]%%\n");
+ ptr = mesg;
+ return(TRUE);
+ } /* End if */
+ ++ptr;
+ } /* End for */
+ } /* End while */
+ } /* End if */
+
+ if ( canwrite == TRUE ) /* don't block during start() */
+ return(FALSE);
+
+ while ( (ch = getc(fp_ttyi)) != EOF )
+ putc(ch, stdout);
+ return(FALSE);
+
+} /* End of readline */
+
+/*****************************************************************************/
+
+/* @(#)strspn.c 1.2 */
+/*LINTLIBRARY*/
+/*
+ * Return the number of characters in the maximum leading segment
+ * of string which consists solely of characters from charset.
+ */
+int
+strspn(string, charset)
+char *string;
+register char *charset;
+{
+ register char *p, *q;
+
+ for(q=string; *q != '\0'; ++q) {
+ for(p=charset; *p != '\0' && *p != *q; ++p)
+ ;
+ if(*p == '\0')
+ break;
+ }
+ return(q-string);
+}
+
+/* @(#)strpbrk.c 1.2 */
+/*LINTLIBRARY*/
+/*
+ * Return ptr to first occurance of any character from `brkset'
+ * in the character string `string'; NULL if none exists.
+ */
+
+char *
+strpbrk(string, brkset)
+register char *string, *brkset;
+{
+ register char *p;
+
+ do {
+ for(p=brkset; *p != '\0' && *p != *string; ++p)
+ ;
+ if(*p != '\0')
+ return(string);
+ }
+ while(*string++);
+ return((char*)0);
+}
+
+/* @(#)strtok.c 1.2 */
+/* 3.0 SID # 1.2 */
+/*LINTLIBRARY*/
+/*
+ * uses strpbrk and strspn to break string into tokens on
+ * sequentially subsequent calls. returns NULL when no
+ * non-separator characters remain.
+ * `subsequent' calls are calls with first argument NULL.
+ */
+
+
+extern int strspn();
+extern char *strpbrk();
+
+char *
+strtok(string, sepset)
+char *string, *sepset;
+{
+ register char *p, *q, *r;
+ static char *savept;
+
+ /*first or subsequent call*/
+ p = (string == (char*)0)? savept: string;
+
+ if(p == 0) /* return if no tokens remaining */
+ return((char*)0);
+
+ q = p + strspn(p, sepset); /* skip leading separators */
+
+ if(*q == '\0') /* return if no tokens remaining */
+ return((char*)0);
+
+ if((r = strpbrk(q, sepset)) == (char*)0) /* move past token */
+ savept = 0; /* indicate this is last token */
+ else {
+ *r = '\0';
+ savept = ++r;
+ }
+ return(q);
+}
+#endif
+
+/*****************************************************************************/
+
+#ifdef DKHOST
+
+#ifndef DKSTREAMS
+short dkrmode[3] = {DKR_TIME, 0, 0};
+#endif
+
+dkhost_connect()
+
+{
+
+ int ofd; /* for saving and restoring stderr */
+ int dfd;
+ int retrytime = 5;
+
+/*
+ *
+ * Tries to connect to a Datakit destination. The extra stuff I've added to save
+ * and later restore stderr is primarily for our spooling setup at Murray Hill.
+ * postio is usually called with stderr directed to a file that will be returned
+ * to the user when the job finishes printing. Problems encountered by dkdial(),
+ * like busy messages, go to stderr but don't belong in the user's mail. They'll
+ * be temporarily directed to the log file. After we've connected stderr will be
+ * restored.
+ *
+ */
+
+ if ( *line == '\0' )
+ error(FATAL, "incomplete Datakit line");
+
+ if ( fp_log != NULL && fp_log != stderr ) { /* redirect dkdial errors */
+ ofd = dup(2);
+ close(2);
+ dup(fileno(fp_log));
+ } /* End if */
+
+ while ( (dfd = ttyi = dkdial(line)) < 0 ) {
+ if ( retrytime < 0 )
+ error(FATAL, "can't connect to %s", line);
+ sleep(retrytime++);
+ if ( retrytime > 60 )
+ retrytime = 60;
+ } /* End while */
+
+ if ( fp_log != NULL && fp_log != stderr ) { /* restore stderr */
+ close(2);
+ dup(ofd);
+ close(ofd);
+ } /* End if */
+
+#ifndef DKSTREAMS
+ if ( ioctl(ttyi, DIOCRMODE, dkrmode) == -1 )
+ error(FATAL, "ioctl error - DIOCRMODE");
+
+#ifdef DIOURPWD
+ if ( window_size > 0 ) {
+ short dkparm[3];
+
+ dkparm[0] = dkminor(ttyi);
+ dkparm[1] = 1;
+ dkparm[2] = window_size;
+ if ( ioctl(ttyi, DIOURPWD, dkparm) < 0 || ioctl(ttyi, DIOCFLUSH, 0) < 0 )
+ error(NON_FATAL, "WSA failed");
+ } /* End if */
+#endif
+
+ line = dtnamer(dkminor(ttyi));
+
+ if ( (ttyi = open(line, O_RDWR)) == -1 )
+ error(FATAL, "can't open %s", line);
+
+ close(dfd);
+#endif
+
+} /* End of dkhost_connect */
+#endif
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postio/ifdef.h b/sys/src/cmd/postscript/postio/ifdef.h
new file mode 100755
index 000000000..f158c46db
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/ifdef.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Conditional compilation definitions needed in ifdef.c and postio.c.
+ *
+ */
+
+#ifdef SYSV
+#include <termio.h>
+
+#ifdef DKSTREAMS
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#endif
+
+#endif
+
+#ifdef V9
+#include <sys/filio.h>
+#include <sys/ttyio.h>
+
+extern int tty_ld;
+#endif
+
+#ifdef BSD4_2
+#include <sgtty.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define FD_ZERO(s) (s) = 0
+#define FD_SET(n,s) (s) |= 1 << (n)
+
+extern int errno;
+#endif
+
+#ifdef DKHOST
+#include <dk.h>
+#include <sysexits.h>
+
+extern char *dtnamer();
+extern int dkminor();
+#endif
+
+/*
+ *
+ * External variable declarations - most (if not all) are defined in postio.c and
+ * needed by the routines in ifdef.c.
+ *
+ */
+
+extern char *line; /* printer is on this line */
+extern int ttyi; /* input */
+extern int ttyo; /* and output file descriptors */
+extern FILE *fp_log; /* just for DKHOST stuff */
+
+extern char mesg[]; /* exactly what came back on ttyi */
+extern char *endmesg; /* one in front of last free slot in mesg */
+extern int next; /* next character goes in mesg[next] */
+
+extern short baudrate; /* printer is running at this speed */
+extern int stopbits; /* and expects this many stop bits */
+extern int interactive; /* TRUE for interactive mode */
+
+extern int whatami; /* a READ or WRITE process - or both */
+extern int canread; /* allows reads */
+extern int canwrite; /* and writes if TRUE */
+
diff --git a/sys/src/cmd/postscript/postio/postio.1 b/sys/src/cmd/postscript/postio/postio.1
new file mode 100755
index 000000000..3075951b7
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/postio.1
@@ -0,0 +1,308 @@
+.TH POSTIO 1 "DWB 3.2"
+.SH NAME
+.B postio
+\- serial interface for PostScript printers
+.SH SYNOPSIS
+\*(mBpostio\f1
+.OP \-l line
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B postio
+sends
+.I files
+to the PostScript printer attached to
+.IR line .
+If no
+.I files
+are specified the standard input is sent.
+The first group of
+.I options
+should be sufficient for most applications:
+.TP 0.75i
+.OP \-b speed
+Transmit data over
+.I line
+at baud rate
+.I speed.
+Recognized baud rates are 1200, 2400, 4800, 9600, and 19200.
+The default
+.I speed
+is 9600 baud.
+.TP
+.OP \-c
+Do not send
+.MR ^C s
+(interrupts) to the printer,
+which means
+.B postio
+does not force a busy printer into the idle state.
+.TP
+.OP \-l line
+Connect to printer attached to
+.IR line .
+In most cases there is no default and
+.B postio
+must be able to read and write
+.IR line .
+If
+.I line
+does not begin with
+.MW /
+it is treated as a Datakit destination.
+.TP
+.OP \-q
+Prevents status queries while
+.I files
+are being sent to the printer.
+When status queries are disabled a dummy message is appended
+to the log file before each block is transmitted.
+.TP
+.OP \-B num
+Set internal buffer size for reading and writing
+.I files
+to
+.I num
+bytes
+(default is 2048 bytes).
+.TP
+.OP \-D
+Enable debug mode.
+Guarantees that everything read on
+.I line
+will be added to the log file (standard error by default).
+.TP
+.OP \-L file
+Data received on
+.I line
+gets put in
+.IR file .
+The default log
+.I file
+is standard error.
+Printer or status messages that do not indicate a change in state
+are not normally written to
+.I file
+but can be forced out using the
+.OP \-D
+option.
+.TP
+.OP \-P string
+Send
+.I string
+to the printer before any of the input files.
+The default
+.I string
+is simple PostScript code that disables timeouts.
+.TP
+.OP \-R num
+Run
+.B postio
+as a single process if
+.I num
+is 1 or as separate read and write processes if
+.I num
+is 2.
+By default
+.B postio
+runs as a single process.
+.PP
+The next two
+.I options
+are provided for users who expect to run
+.B postio
+on their own.
+Neither is suitable for use in spooler interface
+programs:
+.TP 0.35i
+.OP \-i
+Run the program in interactive mode.
+Any
+.I files
+are sent first and followed by the standard input.
+Forces separate read and write processes
+and overrides many other options.
+To exit interactive mode use your interrupt or quit character.
+To get a friendly interactive connection with the printer type
+.MW executive
+on a line by itself.
+.TP
+.OP \-t
+Data received on
+.I line
+and not recognized as printer or status information is written to
+the standard output.
+Forces separate read and write processes.
+Convenient if you have a PostScript program that
+will be returning useful data to the host.
+.PP
+The last option is not generally recommended and should only
+be used if all else fails to provide a reliable connection:
+.TP 0.35i
+.OP \-S
+Slow the transmission of data to the printer.
+Severely limits throughput, runs as a single process,
+disables the
+.OP \-q
+option, limits the internal buffer size to 1024 bytes,
+can use an excessive amount of
+.SM CPU
+time, and does nothing in interactive mode.
+.PP
+Best performance is usually obtained by using
+a large internal buffer
+.OP -B "" ) (
+and by running the program as separate read and write processes
+.OP \-R2 "" ). (
+Inability to fork the additional process causes
+.B postio
+to continue as a single read/write process.
+When one process is used, only data sent to the printer is flow-controlled.
+.PP
+The options are not all mutually exclusive.
+The
+.OP \-i
+option always wins, selecting its own settings for whatever is
+needed to run interactive mode, independent of anything else
+found on the command line.
+Interactive mode runs as separate read and write processes
+and few of the other
+.I options
+accomplish anything in the presence of the
+.OP \-i
+option.
+The
+.OP \-t
+option needs a reliable two way connection to the printer and
+therefore tries to force separate read and write processes.
+The
+.OP \-S
+option relies on the status query mechanism, so
+.OP \-q
+is disabled and the program runs as a single process.
+.PP
+In most cases
+.B postio
+starts by making a connection to
+.I line
+and then attempts to force the printer into the
+.SM IDLE
+state by sending an appropriate sequence of
+.MW ^T
+(status query),
+.MW ^C
+(interrupt), and
+.MW ^D
+(end of job) characters.
+When the printer goes
+.SM IDLE
+.I files
+are transmitted along with an occasional
+.MW ^T
+(unless the
+.OP \-q
+option was used).
+After all the
+.I files
+are sent the program waits until it is reasonably sure the
+job is complete.
+Printer generated error messages received at any time
+except while establishing the initial connection
+(or when running interactive mode) cause
+.B postio
+to exit with a non-zero status.
+In addition to being added to the log file, printer error messages
+are also echoed to standard error.
+.SH EXAMPLES
+Run as a single process at 9600 baud and send
+.I file1
+and
+.I file2
+to the printer attached to
+.MR /dev/tty01 :
+.EX
+postio -l /dev/tty01 \f2file1 file2
+.EE
+Same as above except two processes are used,
+the internal buffer is set to 4096 bytes,
+and data returned by the printer gets put in file
+.MR log :
+.EX
+postio -R2 -B4096 -l/dev/tty01 -Llog \f2file1 file2
+.EE
+Establish an interactive connection with the printer at Datakit
+destination
+.MR my/printer :
+.EX
+postio -i -l my/printer
+.EE
+Send file
+.MW program
+to the printer connected to
+.MR /dev/tty22 ,
+recover any data in file
+.MR results ,
+and put log messages in file
+.MR log :
+.EX
+postio -t -l /dev/tty22 -L log program >results
+.EE
+.SH DIAGNOSTICS
+A 0 exit status is returned if the files ran successfully.
+System errors (e.g., ``can't open the line'') set the low order
+bit in the exit status, while PostScript errors set bit 1.
+An exit status of 2 usually means the printer
+detected a PostScript error in the input
+.IR files .
+.SH WARNINGS
+.PP
+The input
+.I files
+are handled as a single PostScript job.
+Sending several different jobs, each with their own internal
+end of job mark
+.RM ( ^D )
+is not guaranteed to work properly.
+.B postio
+may quit before all the jobs have completed and could be restarted
+before the last one finishes.
+.PP
+All the capabilities described above may not be available on every
+machine or even across the different versions of
+.SM UNIX
+that are currently supported by the program.
+For example, the code needed to connect to a Datakit destination may only
+work on System\ V and may require that the
+.SM DKHOST
+software package be available at compile time.
+.PP
+There may be no default
+.I line
+so using
+.OP \-l
+option is strongly recommended.
+If omitted
+.B postio
+may attempt to connect to the printer using the standard output.
+If Datakit is involved the
+.OP \-b
+may be ineffective and attempts by
+.B postio
+to flow control data in both directions may not work.
+The
+.OP \-q
+option can help if the printer is connected to \s-1RADIAN\s+1.
+The
+.OP \-S
+option is not generally recommended and should only be used if
+all else fails to establish a reliable connection.
+.SH SEE ALSO
+.BR buildtables (1),
+.BR dpost (1),
+.BR postdaisy (1),
+.BR postdmd (1),
+.BR postmd (1),
+.BR postprint (1),
+.BR postreverse (1),
+.BR posttek (1),
+.BR printfont (1)
diff --git a/sys/src/cmd/postscript/postio/postio.c b/sys/src/cmd/postscript/postio/postio.c
new file mode 100755
index 000000000..14adc77a1
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/postio.c
@@ -0,0 +1,1212 @@
+/*
+ *
+ * postio - RS-232 serial interface for PostScript printers
+ *
+ * A simple program that manages input and output for PostScript printers. Much
+ * has been added and changed from early versions of the program, but the basic
+ * philosophy is still the same. Don't send real data until we're certain we've
+ * connected to a PostScript printer that's in the idle state and try to hold the
+ * connection until the job is completely done. It's more work than you might
+ * expect is necessary, but should provide a reasonably reliable spooler interface
+ * that can return error indications to the caller via the program's exit status.
+ *
+ * I've added code that will let you split the program into separate read/write
+ * processes. Although it's not the default it should be useful if you have a file
+ * that will be returning useful data from the printer. The two process stuff was
+ * laid down on top of the single process code and both methods still work. The
+ * implementation isn't as good as it could be, but didn't require many changes
+ * to the original program (despite the fact that there are now many differences).
+ *
+ * By default the program still runs as a single process. The -R2 option forces
+ * separate read and write processes after the intial connection is made. If you
+ * want that as the default initialize splitme (below) to TRUE. In addition the
+ * -t option that's used to force stuff not recognized as status reports to stdout
+ * also tries to run as two processes (by setting splitme to TRUE). It will only
+ * work if the required code (ie. resetline() in ifdef.c) has been implemented
+ * for your Unix system. I've only tested the System V code.
+ *
+ * Code needed to support interactive mode has also been added, although again it's
+ * not as efficient as it could be. It depends on the system dependent procedures
+ * resetline() and setupstdin() (file ifdef.c) and for now is only guaranteed to
+ * work on System V. Can be requested using the -i option.
+ *
+ * Quiet mode (-q option) is also new, but was needed for some printers connected
+ * to RADIAN. If you're running in quiet mode no status requests will be sent to
+ * the printer while files are being transmitted (ie. in send()).
+ *
+ * The program expects to receive printer status lines that look like,
+ *
+ * %%[ status: idle; source: serial 25 ]%%
+ * %%[ status: waiting; source: serial 25 ]%%
+ * %%[ status: initializing; source: serial 25 ]%%
+ * %%[ status: busy; source: serial 25 ]%%
+ * %%[ status: printing; source: serial 25 ]%%
+ * %%[ status: PrinterError: out of paper; source: serial 25 ]%%
+ * %%[ status: PrinterError: no paper tray; source: serial 25 ]%%
+ *
+ * although this list isn't complete. Sending a '\024' (control T) character forces
+ * the return of a status report. PostScript errors detected on the printer result
+ * in the immediate transmission of special error messages that look like,
+ *
+ * %%[ Error: undefined; OffendingCommand: xxx ]%%
+ * %%[ Flushing: rest of job (to end-of-file) will be ignored ]%%
+ *
+ * although we only use the Error and Flushing keywords. Finally conditions, like
+ * being out of paper, result in other messages being sent back from the printer
+ * over the communications line. Typical PrinterError messages look like,
+ *
+ * %%[ PrinterError: out of paper; source: serial 25 ]%%
+ * %%[ PrinterError: paper jam; source: serial 25 ]%%
+ *
+ * although we only use the PrinterError keyword rather than trying to recognize
+ * all possible printer errors.
+ *
+ * The implications of using one process and only flow controlling data going to
+ * the printer are obvious. Job transmission should be reliable, but there can be
+ * data loss in stuff sent back from the printer. Usually that only caused problems
+ * with jobs designed to run on the printer and return useful data back over the
+ * communications line. If that's the kind of job you're sending call postio with
+ * the -t option. That should force the program to split into separate read and
+ * write processes and everything not bracketed by "%%[ " and " ]%%" strings goes
+ * to stdout. In otherwords the data you're expecting should be separated from the
+ * status stuff that goes to the log file (or stderr). The -R2 option does almost
+ * the same thing (ie. separate read and write processes), but everything that
+ * comes back from the printer goes to the log file (stderr by default) and you'll
+ * have to separate your data from any printer messages.
+ *
+ * A typical command line might be,
+ *
+ * postio -l /dev/tty01 -b 9600 -L log file1 file2
+ *
+ * where -l selects the line, -b sets the baud rate, and -L selects the printer
+ * log file. Since there's no default line, at least not right now, you'll always
+ * need to use the -l option, and if you don't choose a log file stderr will be
+ * used. If you have a program that will be returning data the command line might
+ * look like,
+ *
+ * postio -t -l/dev/tty01 -b9600 -Llog file >results
+ *
+ * Status stuff goes to file log while the data you're expecting back from the
+ * printer gets put in file results.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "ifdef.h" /* conditional compilation stuff */
+#include "gen.h" /* general purpose definitions */
+#include "postio.h" /* some special definitions */
+
+char **argv; /* global so everyone can use them */
+int argc;
+
+char *prog_name = ""; /* really just for error messages */
+int x_stat = 0; /* program exit status */
+int debug = OFF; /* debug flag */
+int ignore = OFF; /* what's done for FATAL errors */
+
+char *line = NULL; /* printer is on this tty line */
+short baudrate = BAUDRATE; /* and running at this baud rate */
+Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */
+
+int stopbits = 1; /* number of stop bits */
+int tostdout = FALSE; /* non-status stuff goes to stdout? */
+int quiet = FALSE; /* no status queries in send() if TRUE */
+int interactive = FALSE; /* interactive mode */
+char *postbegin = POSTBEGIN; /* preceeds all the input files */
+int useslowsend = FALSE; /* not recommended! */
+int sendctrlC = TRUE; /* interrupt with ctrl-C when BUSY */
+int window_size = -1; /* for Datakit - use -w */
+
+char *block = NULL; /* input file buffer */
+int blocksize = BLOCKSIZE; /* and its size in bytes */
+int head = 0; /* block[head] is the next character */
+int tail = 0; /* one past the last byte in block[] */
+
+int splitme = FALSE; /* into READ and WRITE processes if TRUE */
+int whatami = READWRITE; /* a READ or WRITE process - or both */
+int canread = TRUE; /* allow reads */
+int canwrite = TRUE; /* and writes if TRUE */
+int otherpid = -1; /* who gets signals if greater than 1 */
+int joinsig = SIGTRAP; /* reader gets this when writing is done */
+int writedone = FALSE; /* and then sets this to TRUE */
+
+char mesg[MESGSIZE]; /* exactly what came back on ttyi */
+char sbuf[MESGSIZE]; /* for parsing the message */
+int next = 0; /* next character goes in mesg[next] */
+char *mesgptr = NULL; /* printer message starts here in mesg[] */
+char *endmesg = NULL; /* as far as readline() can go in mesg[] */
+
+Status status[] = STATUS; /* for converting status strings */
+int nostatus = NOSTATUS; /* default getstatus() return value */
+
+int currentstate = NOTCONNECTED; /* what's happening START, SEND, or DONE */
+
+int ttyi = 0; /* input */
+int ttyo = 2; /* and output file descriptors */
+
+FILE *fp_log = stderr; /* log file for stuff from the printer */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that manages input and output for PostScript printers. Can run
+ * as a single process or as separate read/write processes. What's done depends on
+ * the value assigned to splitme when split() is called.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ options(); /* get command line options */
+ initialize(); /* must be done after options() */
+ start(); /* make sure the printer is ready */
+ split(); /* into read/write processes - maybe */
+ arguments(); /* then send each input file */
+ done(); /* wait until the printer is finished */
+ cleanup(); /* make sure the write process stops */
+
+ exit(x_stat); /* everything probably went OK */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+ void interrupt(); /* handles them if we catch signals */
+
+/*
+ *
+ * Makes sure we handle interrupts. The proper way to kill the program, if
+ * necessary, is to do a kill -15. That forces a call to interrupt(), which in
+ * turn tries to reset the printer and then exits with a non-zero status. If the
+ * program is running as two processes, sending SIGTERM to either the parent or
+ * child should clean things up.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_sig */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ char *optnames = "b:cil:qs:tw:B:L:P:R:SDI";
+
+ extern char *optarg; /* used by getopt() */
+ extern int optind;
+
+/*
+ *
+ * Reads and processes the command line options. The -R2, -t, and -i options all
+ * force separate read and write processes by eventually setting splitme to TRUE
+ * (check initialize()). The -S option is not recommended and should only be used
+ * as a last resort!
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'b': /* baud rate string */
+ baudrate = getbaud(optarg);
+ break;
+
+ case 'c': /* no ctrl-C's */
+ sendctrlC = FALSE;
+ break;
+
+ case 'i': /* interactive mode */
+ interactive = TRUE;
+ break;
+
+ case 'l': /* printer line */
+ line = optarg;
+ break;
+
+ case 'q': /* no status queries - for RADIAN? */
+ quiet = TRUE;
+ break;
+
+ case 's': /* use 2 stop bits - for UNISON? */
+ if ( (stopbits = atoi(optarg)) < 1 || stopbits > 2 )
+ stopbits = 1;
+ break;
+
+ case 't': /* non-status stuff goes to stdout */
+ tostdout = TRUE;
+ break;
+
+ case 'w': /* Datakit window size */
+ window_size = atoi(optarg);
+ break;
+
+ case 'B': /* set the job buffer size */
+ if ( (blocksize = atoi(optarg)) <= 0 )
+ blocksize = BLOCKSIZE;
+ break;
+
+ case 'L': /* printer log file */
+ if ( (fp_log = fopen(optarg, "w")) == NULL ) {
+ fp_log = stderr;
+ error(NON_FATAL, "can't open log file %s", optarg);
+ } /* End if */
+ break;
+
+ case 'P': /* initial PostScript code */
+ postbegin = optarg;
+ break;
+
+ case 'R': /* run as one or two processes */
+ if ( atoi(optarg) == 2 )
+ splitme = TRUE;
+ else splitme = FALSE;
+ break;
+
+ case 'S': /* slow and kludged up version of send */
+ useslowsend = TRUE;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+getbaud(rate)
+
+ char *rate; /* string representing the baud rate */
+
+{
+
+ int i; /* for looking through baudtable[] */
+
+/*
+ *
+ * Called from options() to convert a baud rate string into an appropriate termio
+ * value. *rate is looked up in baudtable[] and if it's found, the corresponding
+ * value is returned to the caller.
+ *
+ */
+
+ for ( i = 0; baudtable[i].rate != NULL; i++ )
+ if ( strcmp(rate, baudtable[i].rate) == 0 )
+ return(baudtable[i].val);
+
+ error(FATAL, "don't recognize baud rate %s", rate);
+
+} /* End of getbaud */
+
+/*****************************************************************************/
+
+initialize()
+
+{
+
+/*
+ *
+ * Initialization, a few checks, and a call to setupline() (file ifdef.c) to open
+ * and configure the communications line. Settings for interactive mode always
+ * take precedence. The setupstdin() call with an argument of 0 saves the current
+ * terminal settings if interactive mode has been requested - otherwise nothing's
+ * done. Unbuffering stdout (via the setbuf() call) isn't really needed on System V
+ * since it's flushed whenever terminal input is requested. It's more efficient if
+ * we buffer the stdout (on System V) but safer (for other versions of Unix) if we
+ * include the setbuf() call.
+ *
+ */
+
+ whatami = READWRITE; /* always run start() as one process */
+ canread = canwrite = TRUE;
+
+ if ( tostdout == TRUE ) /* force separate read/write processes */
+ splitme = TRUE;
+
+ if ( interactive == TRUE ) { /* interactive mode settings always win */
+ quiet = FALSE;
+ tostdout = FALSE;
+ splitme = TRUE;
+ blocksize = 1;
+ postbegin = NULL;
+ useslowsend = FALSE;
+ nostatus = INTERACTIVE;
+ setbuf(stdout, NULL);
+ } /* End if */
+
+ if ( useslowsend == TRUE ) { /* last resort only - not recommended */
+ quiet = FALSE;
+ splitme = FALSE;
+ if ( blocksize > 1024 ) /* don't send too much all at once */
+ blocksize = 1024;
+ } /* End if */
+
+ if ( tostdout == TRUE && fp_log == stderr )
+ fp_log = NULL;
+
+ if ( line == NULL && (interactive == TRUE || tostdout == TRUE) )
+ error(FATAL, "a printer line must be supplied - use the -l option");
+
+ if ( (block = malloc(blocksize)) == NULL )
+ error(FATAL, "no memory");
+
+ endmesg = mesg + sizeof mesg - 2; /* one byte from last position in mesg */
+
+ setupline(); /* configure the communications line */
+ setupstdin(0); /* save current stdin terminal settings */
+
+} /* End of initialize */
+
+/*****************************************************************************/
+
+start()
+
+{
+
+/*
+ *
+ * Tries to put the printer in the IDLE state before anything important is sent.
+ * Run as a single process no matter what has been assigned to splitme. Separate
+ * read and write processes, if requested, will be created after we're done here.
+ *
+ */
+
+ logit("printer startup\n");
+
+ currentstate = START;
+ clearline();
+
+ while ( 1 )
+ switch ( getstatus(1) ) {
+ case IDLE:
+ case INTERACTIVE:
+ if ( postbegin != NULL && *postbegin != '\0' )
+ Write(ttyo, postbegin, strlen(postbegin));
+ clearline();
+ return;
+
+ case BUSY:
+ if ( sendctrlC == TRUE ) {
+ Write(ttyo, "\003", 1);
+ Rest(1);
+ } /* End if */
+ break;
+
+ case WAITING:
+ case ERROR:
+ case FLUSHING:
+ Write(ttyo, "\004", 1);
+ Rest(1);
+ break;
+
+ case PRINTERERROR:
+ Rest(15);
+ break;
+
+ case DISCONNECT:
+ error(FATAL, "Disconnected - printer may be offline");
+ break;
+
+ case ENDOFJOB:
+ case UNKNOWN:
+ clearline();
+ break;
+
+ default:
+ Rest(1);
+ break;
+ } /* End switch */
+
+} /* End of start */
+
+/*****************************************************************************/
+
+split()
+
+{
+
+ int pid;
+ void interrupt();
+
+/*
+ *
+ * If splitme is TRUE we fork a process, make the parent handle reading, and let
+ * the child take care of writing. resetline() (file ifdef.c) contains all the
+ * system dependent code needed to reset the communications line for separate
+ * read and write processes. For now it's expected to return TRUE or FALSE and
+ * that value controls whether we try the fork. I've only tested the two process
+ * stuff for System V. Other versions of resetline() may just be dummy procedures
+ * that always return FALSE. If the fork() failed previous versions continued as
+ * a single process, although the implementation wasn't quite right, but I've now
+ * decided to quit. The main reason is a Datakit channel may be configured to
+ * flow control data in both directions, and if we run postio over that channel
+ * as a single process we likely will end up in deadlock.
+ *
+ */
+
+ if ( splitme == TRUE )
+ if ( resetline() == TRUE ) {
+ pid = getpid();
+ signal(joinsig, interrupt);
+ if ( (otherpid = fork()) == -1 )
+ error(FATAL, "can't fork");
+ else if ( otherpid == 0 ) {
+ whatami = WRITE;
+ nostatus = WRITEPROCESS;
+ otherpid = pid;
+ setupstdin(1);
+ } else whatami = READ;
+ } else if ( interactive == TRUE || tostdout == TRUE )
+ error(FATAL, "can't create two process - check resetline()");
+ else error(NON_FATAL, "running as a single process - check resetline()");
+
+ canread = (whatami & READ) ? TRUE : FALSE;
+ canwrite = (whatami & WRITE) ? TRUE : FALSE;
+
+} /* End of split */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+ int fd_in; /* next input file */
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If there
+ * aren't any arguments left when we get here we'll send stdin. Input files are
+ * only read and sent to the printer if canwrite is TRUE. Checking it here means
+ * we won't have to do it in send(). If interactive mode is TRUE we'll stay here
+ * forever sending stdin when we run out of files - exit with a break. Actually
+ * the loop is bogus and used at most once when we're in interactive mode because
+ * stdin is in a pseudo raw mode and the read() in readblock() should never see
+ * the end of file.
+ *
+ */
+
+ if ( canwrite == TRUE )
+ do /* loop is for interactive mode */
+ if ( argc < 1 )
+ send(fileno(stdin), "pipe.end");
+ else {
+ while ( argc > 0 ) {
+ if ( (fd_in = open(*argv, O_RDONLY)) == -1 )
+ error(FATAL, "can't open %s", *argv);
+ send(fd_in, *argv);
+ close(fd_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+ while ( interactive == TRUE );
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+send(fd_in, name)
+
+ int fd_in; /* next input file */
+ char *name; /* and it's pathname */
+
+{
+
+/*
+ *
+ * Sends file *name to the printer. There's nothing left here that depends on
+ * sending and receiving status reports, although it can be reassuring to know
+ * the printer is responding and processing our job. Only the writer gets here
+ * in the two process implementation, and in that case split() has reset nostatus
+ * to WRITEPROCESS and that's what getstatus() always returns. For now we accept
+ * the IDLE state and ENDOFJOB as legitimate and ignore the INITIALIZING state.
+ *
+ */
+
+ if ( interactive == FALSE )
+ logit("sending file %s\n", name);
+
+ currentstate = SEND;
+
+ if ( useslowsend == TRUE ) {
+ slowsend(fd_in);
+ return;
+ } /* End if */
+
+ while ( readblock(fd_in) )
+ switch ( getstatus(0) ) {
+ case IDLE:
+ case BUSY:
+ case WAITING:
+ case PRINTING:
+ case ENDOFJOB:
+ case PRINTERERROR:
+ case UNKNOWN:
+ case NOSTATUS:
+ case WRITEPROCESS:
+ case INTERACTIVE:
+ writeblock();
+ break;
+
+ case ERROR:
+ fprintf(stderr, "%s", mesg); /* for csw */
+ error(USER_FATAL, "PostScript Error");
+ break;
+
+ case FLUSHING:
+ error(USER_FATAL, "Flushing Job");
+ break;
+
+ case DISCONNECT:
+ error(FATAL, "Disconnected - printer may be offline");
+ break;
+ } /* End switch */
+
+} /* End of send */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+ int sleeptime = 15; /* for 'out of paper' etc. */
+
+/*
+ *
+ * Tries to stay connected to the printer until we're reasonably sure the job is
+ * complete. It's the only way we can recover error messages or data generated by
+ * the PostScript program and returned over the communication line. Actually doing
+ * it correctly for all possible PostScript jobs is more difficult that it might
+ * seem. For example if we've sent several jobs, each with their own EOF mark, then
+ * waiting for ENDOFJOB won't guarantee all the jobs have completed. Even waiting
+ * for IDLE isn't good enough. Checking for the WAITING state after all the files
+ * have been sent and then sending an EOF may be the best approach, but even that
+ * won't work all the time - we could miss it or might not get there. Even sending
+ * our own special PostScript job after all the input files has it's own different
+ * set of problems, but probably could work (perhaps by printing a fake status
+ * message or just not timing out). Anyway it's probably not worth the trouble so
+ * for now we'll quit if writedone is TRUE and we get ENDOFJOB or IDLE.
+ *
+ * If we're running separate read and write processes the reader gets here after
+ * after split() while the writer goes to send() and only gets here after all the
+ * input files have been transmitted. When they're both here the writer sends the
+ * reader signal joinsig and that forces writedone to TRUE in the reader. At that
+ * point the reader can begin looking for an indication of the end of the job.
+ * The writer hangs around until the reader kills it (usually in cleanup()) sending
+ * occasional status requests.
+ *
+ */
+
+ if ( canwrite == TRUE )
+ logit("waiting for end of job\n");
+
+ currentstate = DONE;
+ writedone = (whatami == READWRITE) ? TRUE : FALSE;
+
+ while ( 1 ) {
+ switch ( getstatus(1) ) {
+
+ case WRITEPROCESS:
+ if ( writedone == FALSE ) {
+ sendsignal(joinsig);
+ Write(ttyo, "\004", 1);
+ writedone = TRUE;
+ sleeptime = 1;
+ } /* End if */
+ Rest(sleeptime++);
+ break;
+
+ case WAITING:
+ Write(ttyo, "\004", 1);
+ Rest(1);
+ sleeptime = 15;
+ break;
+
+ case IDLE:
+ case ENDOFJOB:
+ if ( writedone == TRUE ) {
+ logit("job complete\n");
+ return;
+ } /* End if */
+ break;
+
+ case BUSY:
+ case PRINTING:
+ case INTERACTIVE:
+ sleeptime = 15;
+ break;
+
+ case PRINTERERROR:
+ Rest(sleeptime++);
+ break;
+
+ case ERROR:
+ fprintf(stderr, "%s", mesg); /* for csw */
+ error(USER_FATAL, "PostScript Error");
+ return;
+
+ case FLUSHING:
+ error(USER_FATAL, "Flushing Job");
+ return;
+
+ case DISCONNECT:
+ error(FATAL, "Disconnected - printer may be offline");
+ return;
+
+ default:
+ Rest(1);
+ break;
+ } /* End switch */
+
+ if ( sleeptime > 60 )
+ sleeptime = 60;
+ } /* End while */
+
+} /* End of done */
+
+/*****************************************************************************/
+
+cleanup()
+
+{
+
+ int w;
+
+/*
+ *
+ * Only needed if we're running separate read and write processes. Makes sure the
+ * write process is killed after the read process has successfully finished with
+ * all the jobs. sendsignal() returns a -1 if there's nobody to signal so things
+ * work when we're running a single process.
+ *
+ */
+
+ while ( sendsignal(SIGKILL) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
+
+} /* End of cleanup */
+
+/*****************************************************************************/
+
+readblock(fd_in)
+
+ int fd_in; /* current input file */
+
+{
+
+ static long blocknum = 1;
+
+/*
+ *
+ * Fills the input buffer with the next block, provided we're all done with the
+ * last one. Blocks from fd_in are stored in array block[]. head is the index
+ * of the next byte in block[] that's supposed to go to the printer. tail points
+ * one past the last byte in the current block. head is adjusted in writeblock()
+ * after each successful write, while head and tail are reset here each time
+ * a new block is read. Returns the number of bytes left in the current block.
+ * Read errors cause the program to abort. The fake status message that's put out
+ * in quiet mode is only so you can look at the log file and know something's
+ * happening - take it out if you want.
+ *
+ */
+
+ if ( head >= tail ) { /* done with the last block */
+ if ( (tail = read(fd_in, block, blocksize)) == -1 )
+ error(FATAL, "error reading input file");
+ if ( quiet == TRUE && tail > 0 ) /* put out a fake message? */
+ logit("%%%%[ status: busy; block: %d ]%%%%\n", blocknum++);
+ head = 0;
+ } /* End if */
+
+ return(tail - head);
+
+} /* End of readblock */
+
+/*****************************************************************************/
+
+writeblock()
+
+{
+
+ int count; /* bytes successfully written */
+
+/*
+ *
+ * Called from send() when it's OK to send the next block to the printer. head
+ * is adjusted after the write, and the number of bytes that were successfully
+ * written is returned to the caller.
+ *
+ */
+
+ if ( (count = write(ttyo, &block[head], tail - head)) == -1 )
+ error(FATAL, "error writing to %s", line);
+ else if ( count == 0 )
+ error(FATAL, "printer appears to be offline");
+
+ head += count;
+ return(count);
+
+} /* End of writeblock */
+
+/*****************************************************************************/
+
+getstatus(t)
+
+ int t; /* sleep time after sending '\024' */
+
+{
+
+ int gotline = FALSE; /* value returned by readline() */
+ int state = nostatus; /* representation of the current state */
+ int mesgch; /* to restore mesg[] when tostdout == TRUE */
+
+ static int laststate = NOSTATUS; /* last state recognized */
+
+/*
+ *
+ * Looks for things coming back from the printer on the communications line, parses
+ * complete lines retrieved by readline(), and returns an integer representation
+ * of the current printer status to the caller. If nothing was available a status
+ * request (control T) is sent to the printer and nostatus is returned to the
+ * caller (provided quiet isn't TRUE). Interactive mode either never returns from
+ * readline() or returns FALSE.
+ *
+ */
+
+ if ( canread == TRUE && (gotline = readline()) == TRUE ) {
+ state = parsemesg();
+ if ( state != laststate || state == UNKNOWN || mesgptr != mesg || debug == ON )
+ logit("%s", mesg);
+
+ if ( tostdout == TRUE && currentstate != START ) {
+ mesgch = *mesgptr;
+ *mesgptr = '\0';
+ fprintf(stdout, "%s", mesg);
+ fflush(stdout);
+ *mesgptr = mesgch; /* for ERROR in send() and done() */
+ } /* End if */
+ return(laststate = state);
+ } /* End if */
+
+ if ( (quiet == FALSE || currentstate != SEND) &&
+ (tostdout == FALSE || currentstate == START) && interactive == FALSE ) {
+ if ( Write(ttyo, "\024", 1) != 1 )
+ error(FATAL, "printer appears to be offline");
+ if ( t > 0 ) Rest(t);
+ } /* End if */
+
+ return(nostatus);
+
+} /* End of getstatus */
+
+/*****************************************************************************/
+
+parsemesg()
+
+{
+
+ char *e; /* end of printer message in mesg[] */
+ char *key, *val; /* keyword/value strings in sbuf[] */
+ char *p; /* for converting to lower case etc. */
+ int i; /* where *key was found in status[] */
+
+/*
+ *
+ * Parsing the lines that readline() stores in mesg[] is messy, and what's done
+ * here isn't completely correct nor as fast as it could be. The general format
+ * of lines that come back from the printer (assuming no data loss) is:
+ *
+ * str%%[ key: val; key: val; key: val ]%%\n
+ *
+ * where str can be most anything not containing a newline and printer reports
+ * (eg. status or error messages) are bracketed by "%%[ " and " ]%%" strings and
+ * end with a newline. Usually we'll have the string or printer report but not
+ * both. For most jobs the leading string will be empty, but could be anything
+ * generated on a printer and returned over the communications line using the
+ * PostScript print operator. I'll assume PostScript jobs are well behaved and
+ * never bracket their messages with "%%[ " and " ]%%" strings that delimit status
+ * or error messages.
+ *
+ * Printer reports consist of one or more key/val pairs, and what we're interested
+ * in (status or error indications) may not be the first pair in the list. In
+ * addition we'll sometimes want the value associated with a keyword (eg. when
+ * key = status) and other times we'll want the keyword (eg. when key = Error or
+ * Flushing). The last pair isn't terminated by a semicolon and a value string
+ * often contains many space separated words and it can even include colons in
+ * meaningful places. I've also decided to continue converting things to lower
+ * case before doing the lookup in status[]. The isupper() test is for Berkeley
+ * systems.
+ *
+ */
+
+ if ( *(mesgptr = find("%%[ ", mesg)) != '\0' && *(e = find(" ]%%", mesgptr+4)) != '\0' ) {
+ strcpy(sbuf, mesgptr+4); /* don't change mesg[] */
+ sbuf[e-mesgptr-4] = '\0'; /* ignore the trailing " ]%%" */
+
+ for ( key = strtok(sbuf, " :"); key != NULL; key = strtok(NULL, " :") ) {
+ if ( (val = strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0 )
+ key = val;
+
+ for ( ; *key == ' '; key++ ) ; /* skip any leading spaces */
+ for ( p = key; *p; p++ ) /* convert to lower case */
+ if ( *p == ':' ) {
+ *p = '\0';
+ break;
+ } else if ( isupper(*p) ) *p = tolower(*p);
+
+ for ( i = 0; status[i].state != NULL; i++ )
+ if ( strcmp(status[i].state, key) == 0 )
+ return(status[i].val);
+ } /* End for */
+ } else if ( strcmp(mesg, "CONVERSATION ENDED.\n") == 0 )
+ return(DISCONNECT);
+
+ return(mesgptr == '\0' ? nostatus : UNKNOWN);
+
+} /* End of parsemesg */
+
+/*****************************************************************************/
+
+char *find(str1, str2)
+
+ char *str1; /* look for this string */
+ char *str2; /* in this one */
+
+{
+
+ char *s1, *s2; /* can't change str1 or str2 too fast */
+
+/*
+ *
+ * Looks for *str1 in string *str2. Returns a pointer to the start of the substring
+ * if it's found or to the end of string str2 otherwise.
+ *
+ */
+
+ for ( ; *str2 != '\0'; str2++ ) {
+ for ( s1 = str1, s2 = str2; *s1 != '\0' && *s1 == *s2; s1++, s2++ ) ;
+ if ( *s1 == '\0' )
+ break;
+ } /* End for */
+
+ return(str2);
+
+} /* End of find */
+
+/*****************************************************************************/
+
+clearline()
+
+{
+
+/*
+ *
+ * Reads characters from the input line until nothing's left. Don't do anything if
+ * we're currently running separate read and write processes.
+ *
+ */
+
+ if ( whatami == READWRITE )
+ while ( readline() != FALSE ) ;
+
+} /* End of clearline */
+
+/*****************************************************************************/
+
+sendsignal(sig)
+
+ int sig; /* this goes to the other process */
+
+{
+
+/*
+ *
+ * Sends signal sig to the other process if we're running as separate read and
+ * write processes. Returns the result of the kill if there's someone else to
+ * signal or -1 if we're running alone.
+ *
+ */
+
+ if ( whatami != READWRITE && otherpid > 1 )
+ return(kill(otherpid, sig));
+
+ return(-1);
+
+} /* End of sendsignal */
+
+/*****************************************************************************/
+
+void interrupt(sig)
+
+ int sig; /* signal that we caught */
+
+{
+
+/*
+ *
+ * Caught a signal - all except joinsig cause the program to quit. joinsig is the
+ * signal sent by the writer to the reader after all the jobs have been transmitted.
+ * Used to tell the read process when it can start looking for the end of the job.
+ *
+ */
+
+ signal(sig, SIG_IGN);
+
+ if ( sig != joinsig ) {
+ x_stat |= FATAL;
+ if ( canread == TRUE )
+ if ( interactive == FALSE )
+ error(NON_FATAL, "signal %d abort", sig);
+ else error(NON_FATAL, "quitting");
+ quit(sig);
+ } /* End if */
+
+ writedone = TRUE;
+ signal(joinsig, interrupt);
+
+} /* End of interrupt */
+
+/*****************************************************************************/
+
+logit(mesg, a1, a2, a3)
+
+ char *mesg; /* control string */
+ unsigned a1, a2, a3; /* and possible arguments */
+
+{
+
+/*
+ *
+ * Simple routine that's used to write a message to the log file.
+ *
+ */
+
+ if ( mesg != NULL && fp_log != NULL ) {
+ fprintf(fp_log, mesg, a1, a2, a3);
+ fflush(fp_log);
+ } /* End if */
+
+} /* End of logit */
+
+/*****************************************************************************/
+
+error(kind, mesg, a1, a2, a3)
+
+ int kind; /* FATAL or NON_FATAL error */
+ char *mesg; /* error message control string */
+ unsigned a1, a2, a3; /* control string arguments */
+
+{
+
+ FILE *fp_err;
+
+/*
+ *
+ * Called when we've run into some kind of program error. First *mesg is printed
+ * using the control string arguments a?. If kind is FATAL and we're not ignoring
+ * errors the program will be terminated. If mesg is NULL or *mesg is the NULL
+ * string nothing will be printed.
+ *
+ */
+
+ fp_err = (fp_log != NULL) ? fp_log : stderr;
+
+ if ( mesg != NULL && *mesg != '\0' ) {
+ fprintf(fp_err, "%s: ", prog_name);
+ fprintf(fp_err, mesg, a1, a2, a3);
+ putc('\n', fp_err);
+ } /* End if */
+
+ x_stat |= kind;
+
+ if ( kind != NON_FATAL && ignore == OFF )
+ quit(SIGTERM);
+
+} /* End of error */
+
+/*****************************************************************************/
+
+quit(sig)
+
+ int sig;
+
+{
+
+ int w;
+
+/*
+ *
+ * Makes sure everything is properly cleaned up if there's a signal or FATAL error
+ * that should cause the program to terminate. The sleep by the write process is
+ * to help give the reset sequence a chance to reach the printer before we break
+ * the connection - primarily for printers connected to Datakit. There's a very
+ * slight chance the reset sequence that's sent to the printer could get us stuck
+ * here. Simplest solution is don't bother to send it - everything works without it.
+ * Flushing ttyo would be better, but means yet another system dependent procedure
+ * in ifdef.c! I'll leave things be for now.
+ *
+ * Obscure problem on PS-810 turbos says wait a bit after sending an interrupt.
+ * Seem to remember the printer getting into a bad state immediately after the
+ * top was opened when the toner light was on. A sleep after sending the ctrl-C
+ * seemed to fix things.
+ *
+ */
+
+ signal(sig, SIG_IGN);
+ ignore = ON;
+
+ while ( sendsignal(sig) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
+
+ setupstdin(2);
+
+ if ( currentstate != NOTCONNECTED ) {
+ if ( sendctrlC == TRUE ) {
+ Write(ttyo, "\003", 1);
+ Rest(1); /* PS-810 turbo problem?? */
+ } /* End if */
+ Write(ttyo, "\004", 1);
+ } /* End if */
+
+ alarm(0); /* prevents sleep() loop on V9 systems */
+ Rest(2);
+
+ exit(x_stat);
+
+} /* End of quit */
+
+/*****************************************************************************/
+
+Rest(t)
+
+ int t;
+
+{
+
+/*
+ *
+ * Used to replace sleep() calls. Only needed if we're running the program as
+ * a read and write process and don't want to have the read process sleep. Most
+ * sleeps are in the code because of the non-blocking read used by the single
+ * process implementation. Probably should be a macro.
+ *
+ */
+
+ if ( t > 0 && canwrite == TRUE )
+ sleep(t);
+
+} /* End of Rest */
+
+/*****************************************************************************/
+
+Read(fd, buf, n)
+
+ int fd;
+ char *buf;
+ int n;
+
+{
+
+ int count;
+
+/*
+ *
+ * Used to replace some of the read() calls. Only needed if we're running separate
+ * read and write processes. Should only be used to replace read calls on ttyi.
+ * Always returns 0 to the caller if the process doesn't have its READ flag set.
+ * Probably should be a macro.
+ *
+ */
+
+ if ( canread == TRUE ) {
+ if ( (count = read(fd, buf, n)) == -1 && errno == EINTR )
+ count = 0;
+ } else count = 0;
+
+ return(count);
+
+} /* End of Read */
+
+/*****************************************************************************/
+
+Write(fd, buf, n)
+
+ int fd;
+ char *buf;
+ int n;
+
+{
+
+ int count;
+
+/*
+ *
+ * Used to replace some of the write() calls. Again only needed if we're running
+ * separate read and write processes. Should only be used to replace write calls
+ * on ttyo. Always returns n to the caller if the process doesn't have its WRITE
+ * flag set. Should also probably be a macro.
+ *
+ */
+
+ if ( canwrite == TRUE ) {
+ if ( (count = write(fd, buf, n)) == -1 && errno == EINTR )
+ count = n;
+ } else count = n;
+
+ return(count);
+
+} /* End of Write */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postio/postio.h b/sys/src/cmd/postscript/postio/postio.h
new file mode 100755
index 000000000..c0ac6ad18
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/postio.h
@@ -0,0 +1,209 @@
+/*
+ *
+ * POSTBEGIN, if it's not NULL, is some PostScript code that's sent to the printer
+ * before any of the input files. It's not terribly important since the same thing
+ * can be accomplished in other ways, but this approach is convenient. POSTBEGIN
+ * is initialized so as to disable job timeouts. The string can also be set on the
+ * command line using the -P option.
+ *
+ */
+
+#define POSTBEGIN "statusdict /waittimeout 0 put\n"
+
+/*
+ *
+ * The following help determine where postio is when it's running - either in the
+ * START, SEND, or DONE states. Primarily controls what's done in getstatus().
+ * RADIAN occasionally had problems with two way conversations. Anyway this stuff
+ * can be used to prevent status queries while we're transmitting a job. Enabled
+ * by the -q option.
+ *
+ */
+
+#define NOTCONNECTED 0
+#define START 1
+#define SEND 2
+#define DONE 3
+
+/*
+ *
+ * Previous versions of postio only ran as a single process. That was (and still
+ * is) convenient, but meant we could only flow control one direction. Data coming
+ * back from the printer occasionally got lost, but that didn't often hurt (except
+ * for lost error messages). Anyway I've added code that lets you split the program
+ * into separate read and write processes, thereby helping to prevent data loss in
+ * both directions. It should be particularly useful when you're sending a job that
+ * you expect will be returning useful data over the communications line.
+ *
+ * The next three definitions control what's done with data on communications line.
+ * The READ flag means the line can be read, while the WRITE flag means it can be
+ * written. When we're running as a single process both flags are set. I tried to
+ * overlay the separate read/write process code on what was there and working for
+ * one process. The implementation isn't as good as it could be, but should be
+ * safe. The single process version still works, and remains the default.
+ *
+ */
+
+#define READ 1
+#define WRITE 2
+#define READWRITE 3
+
+/*
+ *
+ * Messages generated on the printer and returned over the communications line
+ * look like,
+ *
+ * %%[ status: idle; source: serial 25 ]%%
+ * %%[ status: waiting; source: serial 25 ]%%
+ * %%[ status: initializing; source: serial 25 ]%%
+ * %%[ status: busy; source: serial 25 ]%%
+ * %%[ status: printing; source: serial 25 ]%%
+ * %%[ status: PrinterError: out of paper; source: serial 25 ]%%
+ * %%[ status: PrinterError: no paper tray; source: serial 25 ]%%
+ *
+ * %%[ PrinterError: out of paper; source: serial 25 ]%%
+ * %%[ PrinterError: no paper tray; source: serial 25 ]%%
+ *
+ * %%[ Error: undefined; OffendingCommand: xxx ]%%
+ * %%[ Flushing: rest of job (to end-of-file) will be ignored ]%%
+ *
+ * although the list isn't meant to be complete.
+ *
+ * The following constants are used to classify the recognized printer states.
+ * readline() reads complete lines from ttyi and stores them in array mesg[].
+ * getstatus() looks for the "%%[ " and " ]%%" delimiters that bracket printer
+ * messages and if found it tries to parse the enclosed message. After the lookup
+ * one of the following numbers is returned as an indication of the existence or
+ * content of the printer message. The return value is used in start(), send(),
+ * and done() to figure out what's happening and what can be done next.
+ *
+ */
+
+#define BUSY 0 /* processing data already sent */
+#define WAITING 1 /* printer wants more data */
+#define PRINTING 2 /* printing a page */
+#define IDLE 3 /* ready to start the next job */
+#define ENDOFJOB 4 /* readline() builds this up on EOF */
+#define PRINTERERROR 5 /* PrinterError - eg. out of paper */
+#define ERROR 6 /* some kind of PostScript error */
+#define FLUSHING 7 /* throwing out the rest of the job */
+#define INITIALIZING 8 /* printer is booting */
+#define DISCONNECT 9 /* from Datakit! */
+#define UNKNOWN 10 /* in case we missed anything */
+#define NOSTATUS 11 /* no response from the printer */
+
+#define WRITEPROCESS 12 /* dummy states for write process */
+#define INTERACTIVE 13 /* and interactive mode */
+
+/*
+ *
+ * An array of type Status is used, in getstatus(), to figure out the printer's
+ * current state. Just helps convert strings representing the current state into
+ * integer codes that other routines use.
+ *
+ */
+
+typedef struct {
+ char *state; /* printer's current status */
+ int val; /* value returned by getstatus() */
+} Status;
+
+/*
+ *
+ * STATUS is used to initialize an array of type Status that translates the ASCII
+ * strings returned by the printer into appropriate codes that can be used later
+ * on in the program. getstatus() converts characters to lower case, so if you
+ * add any entries make them lower case and put them in before the UNKNOWN entry.
+ * The lookup terminates when we get a match or when an entry with a NULL state
+ * is found.
+ *
+ */
+
+#define STATUS \
+ \
+ { \
+ "busy", BUSY, \
+ "waiting", WAITING, \
+ "printing", PRINTING, \
+ "idle", IDLE, \
+ "endofjob", ENDOFJOB, \
+ "printererror", PRINTERERROR, \
+ "error", ERROR, \
+ "flushing", FLUSHING, \
+ "initializing", INITIALIZING, \
+ NULL, UNKNOWN \
+ }
+
+/*
+ *
+ * The baud rate can be set on the command line using the -b option. If you omit
+ * it BAUDRATE will be used.
+ *
+ */
+
+#define BAUDRATE B9600
+
+/*
+ *
+ * An array of type Baud is used, in routine getbaud(), to translate ASCII strings
+ * into termio values that represent the requested baud rate.
+ *
+ */
+
+typedef struct {
+ char *rate; /* string identifying the baud rate */
+ short val; /* and its termio.h value */
+} Baud;
+
+/*
+ *
+ * BAUDTABLE initializes the array that's used to translate baud rate requests
+ * into termio values. It needs to end with an entry that has NULL assigned to
+ * the rate field.
+ *
+ */
+
+#define BAUDTABLE \
+ \
+ { \
+ "9600", B9600, \
+ "B9600", B9600, \
+ "19200", EXTA, \
+ "19.2", EXTA, \
+ "B19200", EXTA, \
+ "EXTA", EXTA, \
+ "1200", B1200, \
+ "B1200", B1200, \
+ "2400", B2400, \
+ "B2400", B2400, \
+ "B4800", B4800, \
+ "4800", B4800, \
+ "38400", EXTB, \
+ "38.4", EXTB, \
+ "B38400", EXTB, \
+ "EXTB", EXTB, \
+ NULL, B9600 \
+ }
+
+/*
+ *
+ * A few miscellaneous definitions. BLOCKSIZE is the default size of the buffer
+ * used for reading the input files (changed with the -B option). MESGSIZE is the
+ * size of the character array used to store printer status lines - don't make it
+ * too small!
+ *
+ */
+
+#define BLOCKSIZE 2048
+#define MESGSIZE 512
+
+/*
+ *
+ * Some of the non-integer valued functions used in postio.c.
+ *
+ */
+
+char *find();
+
+char *malloc();
+char *strtok();
diff --git a/sys/src/cmd/postscript/postio/postio.mk b/sys/src/cmd/postscript/postio/postio.mk
new file mode 100755
index 000000000..6e17273fd
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/postio.mk
@@ -0,0 +1,109 @@
+MAKE=/bin/make
+MAKEFILE=postio.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+DKLIB=-ldk
+DKHOST=FALSE
+DKSTREAMS=FALSE
+
+#
+# Need dk.h and libdk.a for Datakit support on System V. We recommend you put
+# them in standard places. If it's not possible define DKHOSTDIR (below) and
+# try uncommenting the following lines:
+#
+# DKHOSTDIR=/usr
+# CFLAGS=$(CFLGS) -D$(SYSTEM) -I$(COMMONDIR) -I$(DKHOSTDIR)/include
+# EXTRA=-Wl,-L$(DKHOSTDIR)/lib
+#
+
+HFILES=postio.h\
+ ifdef.h\
+ $(COMMONDIR)/gen.h
+
+OFILES=postio.o\
+ ifdef.o\
+ slowsend.o
+
+all : postio
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ cp postio $(POSTBIN)/postio
+ @chmod 755 $(POSTBIN)/postio
+ @chgrp $(GROUP) $(POSTBIN)/postio
+ @chown $(OWNER) $(POSTBIN)/postio
+ cp postio.1 $(MAN1DIR)/postio.1
+ @chmod 644 $(MAN1DIR)/postio.1
+ @chgrp $(GROUP) $(MAN1DIR)/postio.1
+ @chown $(OWNER) $(MAN1DIR)/postio.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postio
+
+postio ::
+ @CFLAGS="$(CFLAGS)"; export CFLAGS; \
+ DKLIB=" "; export DKLIB; \
+ if [ "$(SYSTEM)" != V9 ]; \
+ then \
+ if [ "$(DKHOST)" = TRUE ]; then \
+ if [ "$(DKSTREAMS)" != FALSE ]; then \
+ if [ "$(DKSTREAMS)" = TRUE ]; \
+ then CFLAGS="$$CFLAGS -DDKSTREAMS=\\\"dknetty\\\""; \
+ else CFLAGS="$$CFLAGS -DDKSTREAMS=\\\"$(DKSTREAMS)\\\""; \
+ fi; \
+ fi; \
+ CFLAGS="$$CFLAGS -DDKHOST"; export CFLAGS; \
+ DKLIB=-ldk; export DKLIB; \
+ SYSTEM=SYSV; export SYSTEM; \
+ fi; \
+ else DKLIB=-lipc; export DKLIB; \
+ fi; \
+ CFLAGS="$$CFLAGS -D$$SYSTEM"; export CFLAGS; \
+ $(MAKE) -e -f $@.mk compile
+
+compile : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postio $(OFILES) $(EXTRA) $(DKLIB)
+
+postio.o : $(HFILES)
+slowsend.o : postio.h $(COMMONDIR)/gen.h
+ifdef.o : ifdef.h $(COMMONDIR)/gen.h
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^DKLIB=.*'DKLIB=$(DKLIB)'" \
+ -e "s'^DKHOST=.*'DKHOST=$(DKHOST)'" \
+ -e "s'^DKSTREAMS=.*'DKSTREAMS=$(DKSTREAMS)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE)
+
diff --git a/sys/src/cmd/postscript/postio/postio.mk.old b/sys/src/cmd/postscript/postio/postio.mk.old
new file mode 100755
index 000000000..e5469869a
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/postio.mk.old
@@ -0,0 +1,84 @@
+MAKE=/bin/make
+MAKEFILE=postio.mk
+
+SYSTEM=V9
+VERSION=3.3.1
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+
+COMMONDIR=../common
+
+DKLIB=-lipc
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -D$(SYSTEM) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+#
+# Need dk.h and libdk.a for Datakit support on System V. We recommend you put
+# them in standard places. If it's not possible define DKHOSTDIR (below) and
+# try uncommenting the following lines:
+#
+# DKHOSTDIR=/usr
+# CFLAGS=$(CFLGS) -D$(SYSTEM) -I$(COMMONDIR) -I$(DKHOSTDIR)/include
+# EXTRA=-Wl,-L$(DKHOSTDIR)/lib
+#
+
+HFILES=postio.h\
+ ifdef.h\
+ $(COMMONDIR)/gen.h
+
+OFILES=postio.o\
+ ifdef.o\
+ slowsend.o
+
+all : postio
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ cp postio $(POSTBIN)/postio
+ @chmod 755 $(POSTBIN)/postio
+ @chgrp $(GROUP) $(POSTBIN)/postio
+ @chown $(OWNER) $(POSTBIN)/postio
+ cp postio.1 $(MAN1DIR)/postio.1
+ @chmod 644 $(MAN1DIR)/postio.1
+ @chgrp $(GROUP) $(MAN1DIR)/postio.1
+ @chown $(OWNER) $(MAN1DIR)/postio.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postio
+
+postio : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postio $(OFILES) $(EXTRA) $(DKLIB)
+
+postio.o : $(HFILES)
+slowsend.o : postio.h $(COMMONDIR)/gen.h
+ifdef.o : ifdef.h $(COMMONDIR)/gen.h
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^CFLGS=.*'CFLGS=$(CFLGS)'" \
+ -e "s'^DKLIB=.*'DKLIB=$(DKLIB)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE)
+
diff --git a/sys/src/cmd/postscript/postio/slowsend.c b/sys/src/cmd/postscript/postio/slowsend.c
new file mode 100755
index 000000000..dbe8ca874
--- /dev/null
+++ b/sys/src/cmd/postscript/postio/slowsend.c
@@ -0,0 +1,121 @@
+/*
+ *
+ * Stuff that slows the transmission of jobs to PostScript printers. ONLY use it
+ * if you appear to be having trouble with flow control. The idea is simple - only
+ * send a significant amount of data when we're certain the printer is in the
+ * WAITING state. Depends on receiving status messages and only works when the
+ * program is run as a single process. What's done should stop printer generated
+ * XOFFs - provided our input buffer (ie. blocksize) is sufficiently small. Was
+ * originally included in the postio.tmp directory, but can now be requested with
+ * the -S option. Considered eliminating this code, but some printers still depend
+ * on it. In particular Datakit connections made using Datakit PVCs and DACUs seem
+ * to have the most problems. Much of the new stuff that was added can't work when
+ * you use this code and will be automatically disabled.
+ *
+ */
+
+#include <stdio.h>
+
+#include "gen.h"
+#include "postio.h"
+
+extern char *block;
+extern int blocksize;
+extern int head;
+extern int tail;
+extern char *line;
+extern char mesg[];
+extern int ttyo;
+
+/*****************************************************************************/
+
+slowsend(fd_in)
+
+ int fd_in; /* next input file */
+
+{
+
+/*
+ *
+ * A slow version of send() that's very careful about when data is sent to the
+ * printer. Should help prevent overflowing the printer's input buffer, provided
+ * blocksize is sufficiently small (1024 should be safe). It's a totally kludged
+ * up routine that should ONLY be used if you have constant transmission problems.
+ * There's really no way it will be able to drive a printer much faster that about
+ * six pages a minute, even for the simplest jobs. Get it by using the -S option.
+ *
+ */
+
+ while ( readblock(fd_in) )
+ switch ( getstatus(0) ) {
+ case WAITING:
+ writeblock(blocksize);
+ break;
+
+ case BUSY:
+ case IDLE:
+ case PRINTING:
+ writeblock(30);
+ break;
+
+ case NOSTATUS:
+ case UNKNOWN:
+ break;
+
+ case PRINTERERROR:
+ sleep(30);
+ break;
+
+ case ERROR:
+ fprintf(stderr, "%s", mesg); /* for csw */
+ error(FATAL, "PostScript Error");
+ break;
+
+ case FLUSHING:
+ error(FATAL, "Flushing Job");
+ break;
+
+ case DISCONNECT:
+ error(FATAL, "Disconnected - printer may be offline");
+ break;
+
+ default:
+ sleep(2);
+ break;
+ } /* End switch */
+
+} /* End of send */
+
+/*****************************************************************************/
+
+static writeblock(num)
+
+ int num; /* most bytes we'll write */
+
+{
+
+ int count; /* bytes successfully written */
+
+/*
+ *
+ * Called from send() when it's OK to send the next block to the printer. head
+ * is adjusted after the write, and the number of bytes that were successfully
+ * written is returned to the caller.
+ *
+ */
+
+ if ( num > tail - head )
+ num = tail - head;
+
+ if ( (count = write(ttyo, &block[head], num)) == -1 )
+ error(FATAL, "error writing to %s", line);
+ else if ( count == 0 )
+ error(FATAL, "printer appears to be offline");
+
+ head += count;
+ return(count);
+
+} /* End of writeblock */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postmd/README b/sys/src/cmd/postscript/postmd/README
new file mode 100755
index 000000000..43433c360
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/README
@@ -0,0 +1,23 @@
+
+A program that displays a matrix as a gray scale image on PostScript
+printers. May be useful if you have a large matrix and want a simple
+way to look for patterns. Expect a 600x600 matrix is an optimistic
+upper limit on a 300 dpi printers using 5 shades of gray and 8.5x11
+inch paper.
+
+Matrix elements are a series of floating point numbers arranged in
+the input file in row major order. By default each matrix is assumed
+to be square and the number of rows (and columns) is set to the square
+root of the number of elements in the input file. White space, including
+newlines, is not used to determine the matrix dimensions. Each matrix
+element is mapped into an integer in the range 0 to 255 (254 by default)
+and PostScript's image operator maps that integer into a gray scale
+appropriate for the printer.
+
+The mapping from floating point matrix elements to integers is controlled
+by an interval list and grayscale map. The default interval list is
+"-1,0,1" which partitions the real line into 7 regions. The default
+grayscale map gets darker as the regions move from left to right along
+the real line. The -i option changes the interval list and the -g option
+modifies the grayscale mapping. Check the man page for more details.
+
diff --git a/sys/src/cmd/postscript/postmd/mkfile b/sys/src/cmd/postscript/postmd/mkfile
new file mode 100755
index 000000000..0b9cf9872
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/mkfile
@@ -0,0 +1,61 @@
+BUILTINS=
+</$objtype/mkfile
+MAKE=mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/sys/lib/postscript/bin
+POSTLIB=$ROOT/sys/lib/postscript/prologues
+
+COMMONDIR=../common
+
+CC=pcc
+LD=pcc
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+LDFLAGS=
+
+all :V: $O.out
+
+install :V: $POSTBIN/$objtype/postmd $POSTLIB/postmd.ps $MAN1DIR/postmd.1
+
+installall :V:
+ for(objtype in $CPUS) { \
+ $MAKE 'MAKE=$MAKE' \
+ 'SYSTEM=$SYSTEM' 'VERSION=$VERSION' \
+ 'FONTDIR=$FONTDIR' 'HOSTDIR=$HOSTDIR' 'MAN1DIR=$MAN1DIR' \
+ 'POSTBIN=$POSTBIN' 'POSTLIB=$POSTLIB' 'TMACDIR=$TMACDIR' \
+ 'DKHOST=$DKHOST' 'DKSTREAMS=$DKSTREAMS' \
+ 'ROUNDPAGE=$ROUNDPAGE' \
+ 'CC=$CC' 'LD=$LD' 'CFLAGS=$CFLAGS' 'LDFLAGS=$LDFLAGS' \
+ install \
+ }
+
+clean :V:
+ rm -f *.$O
+
+clobber :V: clean
+ rm -f $O.out
+
+$POSTBIN/$objtype/postmd : $O.out
+ cp $prereq $target
+
+$POSTLIB/postmd.ps : postmd.ps
+ cp $prereq $target
+
+$MAN1DIR/postmd.1 : postmd.1
+ cp $prereq $target
+
+$O.out : postmd.$O $COMMONDIR/glob.$O $COMMONDIR/misc.$O $COMMONDIR/request.$O $COMMONDIR/tempnam.$O $COMMONDIR/getopt.$O
+ $LD $LDFLAGS $prereq
+
+postmd.o : postmd.h $COMMONDIR/comments.h $COMMONDIR/ext.h $COMMONDIR/gen.h $COMMONDIR/path.h
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+common :V:
+ cd $COMMONDIR; $MAKE
diff --git a/sys/src/cmd/postscript/postmd/postmd.1 b/sys/src/cmd/postscript/postmd/postmd.1
new file mode 100755
index 000000000..fb67d969e
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/postmd.1
@@ -0,0 +1,330 @@
+.ds dQ /usr/lib/postscript
+.TH POSTMD 1 "DWB 3.2"
+.SH NAME
+.B postmd
+\- matrix display program for PostScript printers
+.SH SYNOPSIS
+\*(mBpostmd\f1
+.OP "" options []
+.OP "" files []
+.SH DESCRIPTION
+.B postmd
+reads a series of floating point numbers from
+.IR files ,
+translates them into a PostScript gray scale image,
+and writes the results on the standard output.
+In a typical application the numbers might be
+the elements of a large matrix,
+written in row major order,
+while the printed image could help locate
+patterns in the matrix.
+If no
+.I files
+are specified, or if
+.OP \-
+is one of the input
+.IR files ,
+the standard input is read.
+The following
+.I options
+are understood:
+.TP 0.75i
+.OP \-b num
+Pack the bitmap in the output file using
+.I num
+byte patterns.
+A value of 0 turns off all packing of the output file.
+By default
+.I num
+is 6.
+.TP
+.OP \-c num
+Print
+.I num
+copies of each page.
+By default only one copy is printed.
+.TP
+.OP \-d dimen
+Sets the default matrix dimensions for all input
+.I files
+to
+.IR dimen .
+The
+.I dimen
+string can be given as rows or rows\^\(mu\^columns.
+If columns is omitted it will be set to rows.
+By default
+.B postmd
+assumes each matrix is square and sets the number of rows
+and columns to the square root of the number of elements in
+each input file.
+.TP
+.OP \-g list
+.I list
+is a comma- or space-separated string of integers, each lying between
+0 and 255 inclusive,
+that assigns PostScript gray scales to the regions of the real line
+selected by the
+.OP \-i
+option.
+255 corresponds to white and 0 to black.
+.B postmd
+assigns a default gray scale that omits white (i.e., 255) and gets
+darker as the regions move from left to right along the real line.
+.TP
+.OP \-i list
+.I list
+is a comma- or space-separated string of
+.I N
+floating point numbers that
+partition the real line into
+.RI 2 N +1
+regions.
+The
+.I list
+must be given in increasing numerical order.
+The partitions are used to map floating point numbers read from the input
+.I files
+into gray scale integers that are assigned automatically by
+.B postmd
+or arbitrarily selected using the
+.OP \-g
+option.
+The default interval
+.I list
+is ``\*(mB\-1,0,1\fP'' which partions the real line into 7 regions.
+.TP
+.OP \-m num
+Magnify each logical page by the factor
+.IR num .
+Pages are scaled uniformly about the origin,
+which by default is located at the center of
+each page.
+The default magnification is 1.0.
+.TP
+.OP \-n num
+Print
+.I num
+logical pages on each piece of paper,
+where
+.I num
+can be any positive integer.
+By default
+.I num
+is set to 1.
+.TP
+.OP \-o list
+Print pages whose numbers are given in the comma separated
+.IR list .
+The list contains single numbers
+.I N
+and ranges
+.IR N1\-\|N2 .
+A missing
+.I N1
+means the lowest numbered page, a missing
+.I N2
+means the highest.
+.TP
+.OP \-p mode
+Print
+.I files
+in either \*(mBportrait\fP or \*(mBlandscape\fP
+.IR mode .
+Only the first character of
+.I mode
+is significant.
+The default
+.I mode
+is \*(mBportrait\fP.
+.TP
+.OP \-w window
+.I window
+is a comma- or space-separated list of four positive integers that
+select the upper left and lower right corners of a submatrix from
+each of the input
+.IR files .
+Row and column indices start at 1 in the upper left corner and the
+numbers in the input
+.I files
+are assumed to be written in row major order.
+By default the entire matrix is displayed.
+.TP
+.OP \-x num
+Translate the origin
+.I num
+inches along the positive x axis.
+The default
+coordinate system has the origin fixed at the
+center of the page, with positive
+x to the right and positive y up the page.
+Positive
+.I num
+moves everything right.
+The default offset is 0 inches.
+.TP
+.OP \-y num
+Translate the origin
+.I num
+inches along the positive y axis.
+Positive
+.I num
+moves everything up the page.
+The default offset is 0.
+.TP
+.OP \-E name
+Set the character encoding for text fonts to
+.IR name .
+Requesting
+.I name
+means include file
+.MI \*(dQ/ name .enc \f1.
+A nonexistent encoding file is silently ignored.
+The default selects file
+.MR \*(dQ/Default.enc .
+.TP
+.OP \-L file
+Use
+.I file
+as the PostScript prologue.
+.br
+The default is
+.MR \*(dQ/postmd.ps .
+.PP
+Three options allow insertion of arbitrary PostScript
+at controlled points in the translation process:
+.TP 0.75i
+.OP \-C file
+Copy
+.I file
+to the output file;
+.I file
+must contain legitimate PostScript.
+.TP
+.OP \-P string
+Include
+.I string
+in the output file;
+.I string
+must be legitimate PostScript.
+.TP
+.OP \-R action
+Requests special
+.I action
+(e.g.,
+.MR manualfeed )
+on a per page or global basis.
+The
+.I action
+string can be given as
+.IR request ,
+.IM request : page\f1\|,
+or
+.IM request : page : file\f1\|.
+If
+.I page
+is omitted or given as 0, the request
+applies to all pages.
+If
+.I file
+is omitted, the request
+lookup is done in
+.MR \*(dQ/ps.requests .
+.PP
+Only one matrix is displayed on each logical page,
+and each of the input
+.I files
+must contain complete descriptions of exactly one matrix.
+Matrix elements are floating point numbers arranged in row major order in
+each input file.
+White space, including newlines, is not used to determine matrix
+dimensions.
+By default
+.B postmd
+assumes each matrix is square and sets the number of rows and columns
+to the square root of the number of elements in the input file.
+Supplying default dimensions on the command line using the
+.OP \-d
+option overrides this default behavior, and in that case the
+dimensions apply to all input
+.IR files .
+.PP
+An optional header can be supplied with each input file and is used
+to set the matrix dimensions, the partition of the real line, the gray scale
+map, and a window into the matrix.
+The header consists of keyword/value pairs, each on a separate line.
+It begins on the first line of each input file and ends with the
+first unrecognized string, which should be the first matrix element.
+Values set in the header take precedence, but only apply to the
+current input file.
+Recognized header keywords are
+.MR dimension ,
+.MR interval ,
+.MR grayscale ,
+and
+.MR window .
+The syntax of the value string that follows each keyword parallels what is
+accepted by the
+.OP \-d ,
+.OP \-i ,
+.OP \-g ,
+and
+.OP \-w
+options.
+.SH EXAMPLES
+For example, suppose
+.I file
+initially contains the 1000 numbers
+in a 20\(mu50 matrix.
+Then the command line:
+.EX
+postmd -d20x50 -i"-100 100" -g0,128,254,128,0 \f2file
+.EE
+and prepending the header,
+.EX
+dimension 20x50
+interval -100.0 .100e+3
+grayscale 0 128 254 128 0
+.EE
+to
+.I file
+and typing the command line:
+.EX
+postmd \f2file
+.EE
+produce exactly the same output.
+The interval list partitions the real line into five regions and
+the gray scale list maps numbers less than \-100 or greater than 100
+into 0 (i.e., black), numbers equal to \-100 or 100 into 128
+(i.e., 50 percent
+black), and numbers between \-100 and 100 into 254 (i.e., almost white).
+.SH DIAGNOSTICS
+A 0 exit status is returned if
+.I files
+were successfully processed.
+.SH WARNINGS
+The largest matrix that can be adequately displayed is a function
+of the interval and gray scale lists, the printer resolution,
+and the paper size.
+A 600\(mu600 matrix is an optimistic upper bound for a two element interval
+list (i.e. five regions) using 8.5\(mu11 inch paper on a 300 dpi printer.
+.PP
+Using white (i.e., 255) in a gray scale list is not recommended and will not
+show up in the legend and bar graph that
+.B postmd
+displays below each image.
+.SH FILES
+.MW \*(dQ/postmd.ps
+.br
+.MW \*(dQ/forms.ps
+.br
+.MW \*(dQ/ps.requests
+.SH SEE ALSO
+.BR dpost (1),
+.BR postdaisy (1),
+.BR postdmd (1),
+.BR postio (1),
+.BR postprint (1),
+.BR postreverse (1),
+.BR posttek (1),
+.BR psencoding (1)
diff --git a/sys/src/cmd/postscript/postmd/postmd.c b/sys/src/cmd/postscript/postmd/postmd.c
new file mode 100755
index 000000000..01df4b43d
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/postmd.c
@@ -0,0 +1,1157 @@
+/*
+ *
+ * postmd - matrix display program for PostScript printers.
+ *
+ * A simple program that can be used to display a matrix as a gray scale image on
+ * a PostScript printer using the image operator. Much of the code was borrowed
+ * from postdmd, the bitmap display program DMD screen dumps. May help if you have
+ * a large matix (of floating point numbers) and want a simple way to look for
+ * patterns.
+ *
+ * Matrix elements are a series of floating point numbers arranged in the input
+ * file in row major order. The actual matrix elements can be preceeded by a simple
+ * header that sets things like the matrix dimensions, interval list, and possibly
+ * a window into the matrix that we'll use for display. The dimension statement is
+ * perhaps the most important. If present it determines the number of rows and
+ * columns in the matrix. For example, either of the following defines a 50x50
+ * matrix,
+ *
+ * dimension 50
+ * dimension 50x50
+ *
+ * If no dimension statement appears in the input file, the matrix is assumed to
+ * be square, and the number of rows (and columns) is set to the square root of
+ * the number of elements in the input file.
+ *
+ * Each matrix element is mapped into an integer in the range 0 to 255 (actually
+ * 254) and PostScript's image operator then maps that number into a gray scale
+ * appropriate for the particular printer. The mapping from the floating point
+ * matrix elements to integers is accomplished using an interval list that can be
+ * set using the -i option. The format of the interval string is,
+ *
+ * num1,num2,num3,...,numn
+ *
+ * where each num is a floating point number. The list must be given in increasing
+ * numerical order. A list of n numbers partitions the real line into 2n+1 regions
+ * given as,
+ *
+ * region1 element < num1
+ * region2 element = num1
+ * region3 element < num2
+ * region4 element = num2
+ * .
+ * .
+ * .
+ * region2n element = numn
+ * region2n+1 element > numn
+ *
+ * Every number in a region is mapped one integer in the range 0 to 254, and that
+ * number, when displayed on a printer using the image operator, prints as a square
+ * filled with a gray shade that reflects the integer that was chosen. 0 maps to
+ * black and 255 maps to white (which by default will not be used).
+ *
+ * The default gray scale gets darker as the region number increases, but can be
+ * changed by supplying a gray scale list with the -g option or in the optional
+ * matrix header. The color map is again a comman or space separated list that
+ * looks like,
+ *
+ * color1,color2, ... ,color2n+1
+ *
+ * where color1 applies to region 1 and color2n+1 applies to region2n+1. Each
+ * number in the list should be an integer between 0 and 255. If less than 2n+1
+ * colors are given default assignments will be used for missing regions.
+ *
+ * The size of the matrix that we can display reasonably well is a function of the
+ * number of elements in the interval list, paper size, and printer resolution.
+ * For example a 300dpi printer using 8.5x11 inch paper gives us an image area of
+ * about 2400x2400 pixels. An interval list of two numbers generates five separate
+ * regions and will therefore need that many different shades of gray. Since we're
+ * not using white we'll need to partion our image area into 4x4 pixel squares,
+ * and that means a 600x600 matrix is about as big as we can go. In practice that's
+ * optimistic, but the argument illustrates some of the limitations.
+ *
+ * A submatrix can be selected to display by windowing into the matrix. The window
+ * list can be given using the -w option or can be set in the optional header that
+ * can preceed each matrix. The list should be a comma or space separated list
+ * that looks like,
+ *
+ * lower-column, lower-row, upper-column, upper-row
+ *
+ * where each element in the list must be a positive integer. Rows and columns in
+ * the input matrix start at 1. The dimension of the displayed window will be from
+ * lower-column to upper-column and from lower-row to upper-row inclusive.
+ *
+ * The encoding produced by the program is essentially identical to what's done
+ * by postdmd. See the comments at the beginning of that program if you need more
+ * details. The prologue also shares much of the same code.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how this program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * bitmap
+ *
+ * columns rows bitmap -
+ *
+ * Prints the image that's read as a hex string from standard input. The
+ * image consists of rows lines, each of which includes columns elements.
+ * Eight bits per pixel are used to encode the matrix elements.
+ *
+ * labelmatrix
+ *
+ * matrixname matrixlimits labelmatrix -
+ *
+ * Prints string matrixname just below the lower left corner of the image
+ * and prints string martixlimits near the lower right corner. Outlines
+ * the entire image with a (one pixel wide) box and then draws tick marks
+ * along the top and left sides of the image. One tick mark is printed
+ * for every ten elements.
+ *
+ * legend
+ *
+ * n1 ... nN N c1 m1 ... cM mM total regions legend -
+ *
+ * Prints the legend as a bar graph below the matrix image. n1 ... nN are
+ * strings that represent the interval list. c1 m1 ... cm mM are pairs
+ * that consist of a region's color and the statistics count. Actually
+ * the c's are trivial procedures that just leave a one character string
+ * on the stack when they're executed by image - which is the way the
+ * bar graph is drawn.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+#include "postmd.h" /* special matrix display definitions */
+
+char *optnames = "a:b:c:d:g:i:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
+
+char *prologue = POSTMD; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+char *temp_dir = TEMPDIR; /* temp directory for copying stdin */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+int bytespp = 6; /* bytes per pattern - on output */
+
+int dostats = ON; /* permanent statistics flag */
+int nxtstat = ON; /* and the one for the next matrix */
+
+char *interval = DFLTILIST; /* string representations of the interval */
+char *colormap = NULL; /* color map */
+char *window = NULL; /* and window lists */
+char *matrixname = "pipe.end"; /* name for the next plot */
+
+Ilist ilist[128]; /* active interval list and color map */
+int next = 0; /* one past the last element in ilist[] */
+int regions; /* an index assigned to the last region */
+int wlist[4]; /* upper left and lower right corners */
+
+int page = 0; /* last page we worked on */
+int printed = 0; /* and the number of pages printed */
+
+int dfltrows = 0; /* default rows */
+int dfltcols = 0; /* and columns - changed by -d option */
+int rows; /* real number of rows */
+int columns; /* and columns in the matrix */
+int patcount = 0; /* will be set to columns * rows */
+
+double element; /* next matrix element */
+
+char *raster = NULL; /* next raster line */
+char *rptr; /* next free byte in raster */
+char *eptr; /* one past the last byte in raster */
+
+FILE *fp_in = stdin; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * Bitmap display program for matrices. Only one matrix is allowed per input file,
+ * and each one will be displayed on a page by itself. Input files consist of an
+ * optional header followed by floating point numbers that represent the matrix
+ * elements - in row major order.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Make sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+ signal(SIGFPE, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'b': /* bytes per pattern - on output */
+ bytespp = atoi(optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'd': /* default matrix dimensions */
+ sscanf(optarg, "%dx%d", &dfltrows, &dfltcols);
+ break;
+
+ case 'g': /* set the colormap (ie. grayscale) */
+ colormap = optarg;
+ break;
+
+ case 'i': /* matrix element interval list */
+ interval = optarg;
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'w': /* set the window */
+ window = optarg;
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ matrix();
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ matrixname = *argv;
+ if ( strcmp(*argv, "-") == 0 ) {
+ fp_in = stdin;
+ matrixname = "pipe.end";
+ } else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ matrix();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages, make sure the
+ * last page is printed, and restore the initial environment.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
+ * is requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+matrix()
+
+{
+
+ int count; /* pattern repeats this many times */
+ long total; /* expect this many patterns */
+
+/*
+ *
+ * Reads a matrix from *fp_in, translates it into a PostScript gray scale image,
+ * and writes the result on stdout. For now only one matrix is allowed per input
+ * file. Matrix elements are floating point numbers arranged in row major order
+ * in the input file. In addition each input file may contain an optional header
+ * that defines special things like the dimension of the matrix, a window into
+ * the matrix that will be displayed, and an interval list.
+ *
+ * If we're reading from stdin we first make a copy in a temporary file so we can
+ * can properly position ourselves after we've looked for the header. Originally
+ * wasn't always making a copy of stdin, but I've added a few things to what's
+ * accepted in the header and this simplifies the job. An alternative would be
+ * to always require a header and mark the end of it by some string. Didn't like
+ * that approach much - may fix things up later.
+ *
+ */
+
+ if ( fp_in == stdin ) /* make a copy so we can seek etc. */
+ copystdin();
+
+ rows = dfltrows; /* new dimensions for the next matrix */
+ columns = dfltcols;
+
+ buildilist(interval); /* build the default ilist[] */
+ addcolormap(colormap); /* add the colormap - if not NULL */
+ setwindow(window); /* and setup the initial matrix window */
+ nxtstat = dostats; /* want statistics? */
+ getheader(); /* matrix dimensions at the very least */
+ dimensions(); /* make sure we have the dimensions etc. */
+
+ patcount = 0;
+ total = rows * columns;
+
+ eptr = rptr + (wlist[2] - wlist[0] + 1);
+
+ redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d %d bitmap\n", wlist[2] - wlist[0] + 1, wlist[3] - wlist[1] + 1);
+
+ while ( patcount != total && fscanf(fp_in, "%f", &element) != EOF ) {
+ if ( inwindow() ) *rptr++ = mapfloat(element);
+ if ( ++patcount % columns == 0 )
+ if ( inrange() )
+ putrow();
+ } /* End while */
+
+ if ( total != patcount )
+ error(FATAL, "matrix format error");
+
+ labelmatrix();
+
+ if ( fp_out == stdout ) printed++;
+
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+} /* End of matrix */
+
+/*****************************************************************************/
+
+copystdin()
+
+{
+
+ int fd_out; /* for the temporary file */
+ int fd_in; /* for stdin */
+ int buf[512]; /* buffer for reads and writes */
+ int count; /* number of bytes put in buf */
+
+/*
+ *
+ * If we're reading the matrix from stdin and the matrix dimension isn't set by
+ * a dimension statement at the beginning of the file we'll copy stdin to a
+ * temporary file and reset *fp_in so reads come from the temp file. Simplifies
+ * reading the header (if present), but is expensive.
+ *
+ */
+
+ if ( temp_file != NULL ) /* been here already */
+ unlink(temp_file);
+
+ if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
+ error(FATAL, "can't generate temp file name");
+
+ if ( (fd_out = creat(temp_file, 0660)) == -1 )
+ error(FATAL, "can't create %s", temp_file);
+
+ fd_in = fileno(stdin);
+
+ while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
+ if ( write(fd_out, buf, count) != count )
+ error(FATAL, "error writing to %s", temp_file);
+
+ close(fd_out);
+
+ if ( (fp_in = fopen(temp_file, "r")) == NULL )
+ error(FATAL, "can't open %s", temp_file);
+
+} /* End of copystdin */
+
+/*****************************************************************************/
+
+getheader()
+
+{
+
+ char buf[512]; /* temporary string space */
+ char *cmap = NULL; /* remember header colormap list */
+ long pos; /* for seeking back to first element */
+
+/*
+ *
+ * Looks for the optional header information at the beginning of the input file,
+ * reads it if it's there, and sets *fp_in to be just past the header. That should
+ * be the beginning of the matrix element list. The recognized header keywords are
+ * dimension, interval, colormap (or grayscale), window, name, and statistics. All
+ * are optional, but may be useful in a spooling environment when the user doesn't
+ * doesn't actually run the translator.
+ *
+ * The dimension statement specifies the number of rows and columns. For example
+ * either of the following two lines define a 50 by 50 element matrix,
+ *
+ * dimension 50
+ * dimension 50x50
+ *
+ * The first integer is the number of rows and the second, if given, is the number
+ * of columns. If columns are missing from the dimension statement we assume the
+ * matrix is square.
+ *
+ * interval can be used to redefine the interval list used for mapping floating
+ * point numbers into integers in the range 0 to 254. The string following the
+ * interval keyword has the same format as the -i option. For example to set the
+ * interval list to -1, 0, and 1 you can add the line,
+ *
+ * interval -1,0,1
+ *
+ * The numbers are floats given in increasing order, and separated by commas or
+ * blanks. The last interval list in a header takes precedence.
+ *
+ * colormap can be used to redefine the grayscale list. The string following
+ * the colormap keyword has the same format as the -g option. For example
+ *
+ * colormap 0,50,100,150,200,250
+ * or grayscale 0,50,100,150,200,250
+ *
+ * The window keyword can be used to select a submatrix. The numbers following
+ * window are the upper left and lower right matix coordinates. May not be
+ * implemented yet but shouldn't be difficult. For example
+ *
+ * window 10 10 40 40
+ *
+ * selects the submatrix with corners at (10, 10) and (40, 40). The edges of the
+ * window are included in the display.
+ *
+ * The name keyword can be used to define the title of the display. For example,
+ *
+ * name Plot Of Matrix 1
+ *
+ * prints the string "Plot Of Matrix 1" at the top of the page. Everything up to
+ * the next newline is taken as the name string.
+ *
+ */
+
+ pos = ftell(fp_in);
+
+ while ( fscanf(fp_in, "%s", buf) != EOF ) {
+ if ( strncmp(buf, "dimension", strlen("dimension")) == 0 )
+ fscanf(fp_in, "%dx%d", &rows, &columns);
+ else if ( strncmp(buf, "window", strlen("window")) == 0 ) {
+ fgets(buf, sizeof(buf), fp_in);
+ setwindow(buf);
+ } else if ( strncmp(buf, "name", strlen("name")) == 0 ) {
+ fgets(buf, sizeof(buf), fp_in);
+ matrixname = savestring(buf);
+ } else if ( strncmp(buf, "colormap", strlen("colormap")) == 0 ) {
+ fgets(buf, sizeof(buf), fp_in);
+ cmap = savestring(buf);
+ } else if ( strncmp(buf, "grayscale", strlen("grayscale")) == 0 ) {
+ fgets(buf, sizeof(buf), fp_in);
+ cmap = savestring(buf);
+ } else if ( strncmp(buf, "interval", strlen("interval")) == 0 ) {
+ fgets(buf, sizeof(buf), fp_in);
+ buildilist(buf);
+ } else if ( strncmp(buf, "statistics", strlen("statistics")) == 0 ) {
+ fscanf(fp_in, "%s", buf);
+ if ( strcmp(buf, "on") == 0 || strcmp(buf, "ON") == 0 )
+ nxtstat = ON;
+ else nxtstat = OFF;
+ } else break;
+ pos = ftell(fp_in);
+ } /* End while */
+
+ addcolormap(cmap); /* must happen last */
+ fseek(fp_in, pos, 0); /* back to the start of the matrix */
+
+} /* End of getheader */
+
+/*****************************************************************************/
+
+dimensions()
+
+{
+
+ char buf[100]; /* temporary storage for the elements */
+ long count = 0; /* number of elements in the matrix */
+ long pos; /* matrix elements start here */
+
+/*
+ *
+ * Need to know the dimensions of the matrix before we can go any farther. If
+ * rows and columns are still 0 we'll read the entire input file, starting from
+ * the current position, count the number of elements, take the square root of it,
+ * and use it as the number of rows and columns. Then we seek back to the start
+ * of the real matrix, make sure columns is set, and allocate enough memory for
+ * storing each raster line. After we're certain we've got the number of rows and
+ * columns we check the window coordinates, and if they're not legitimate they're
+ * reset to cover the entire matrix.
+ *
+ */
+
+ if ( rows == 0 ) {
+ pos = ftell(fp_in);
+ while ( fscanf(fp_in, "%s", buf) != EOF )
+ count++;
+ rows = sqrt((double) count);
+ fseek(fp_in, pos, 0);
+ } /* End if */
+
+ if ( columns <= 0 ) columns = rows;
+
+ if ( raster != NULL ) free(raster);
+
+ if ( (rptr = raster = malloc(columns)) == NULL )
+ error(FATAL, "no memory");
+
+ eptr = rptr + columns;
+
+ if ( rows <= 0 || columns <= 0 )
+ error(FATAL, "bad matrix dimensions");
+
+ if ( wlist[0] > wlist[2] || wlist[1] > wlist[3] ) {
+ wlist[0] = wlist[1] = 1;
+ wlist[2] = columns;
+ wlist[3] = rows;
+ } /* End if */
+
+} /* End of dimensions */
+
+/*****************************************************************************/
+
+buildilist(list)
+
+ char *list; /* use this as the interval list */
+
+{
+
+ static char *templist = NULL; /* a working copy of the list */
+ char *ptr; /* next number in *templist */
+ int i; /* loop index - for checking the list */
+
+/*
+ *
+ * Reads string *list and builds up the ilist[] that will be used in the next
+ * matrix. Since strtok() modifies the string it's parsing we make a copy first.
+ * The format of the interval list is described in detail in the comments at the
+ * beginning of this program. Basically consists of a comma or space separated
+ * list of floating point numbers that must be given in increasing numerical order.
+ * The list determines how floating point numbers are mapped into integers in the
+ * range 0 to 254.
+ *
+ */
+
+ if ( templist != NULL ) /* free the space used by the last list */
+ free(templist);
+
+ while ( isascii(*list) && isspace(*list) )
+ list++;
+
+ for ( ptr = list, regions = 3; *ptr != '\0'; ptr++ ) {
+ if ( *ptr == ',' || *ptr == '/' || isspace(*ptr) )
+ regions += 2;
+ while ( isascii(*ptr) && isspace(*ptr) ) ptr++;
+ } /* End for */
+
+ next = 0;
+ templist = savestring(list);
+
+ ptr = strtok(templist, ",/ \t\n");
+ while ( ptr != NULL ) {
+ ilist[next].count = 0;
+ ilist[next++].color = 254 * (regions - 1 - next) / (regions - 1);
+ ilist[next].val = atof(ptr);
+ ilist[next].count = 0;
+ ilist[next++].color = 254 * (regions - 1 - next) / (regions - 1);
+ ptr = strtok(NULL, ",/ \t\n");
+ } /* End while */
+
+ ilist[next].count = 0;
+ ilist[next].color = 254 * (regions - 1 - next) / (regions - 1);
+
+ if ( next == 0 ) /* make sure we have a list */
+ error(FATAL, "missing interval list");
+
+ for ( i = 3; i < next; i += 2 ) /* that's in increasing numerical order */
+ if ( ilist[i].val <= ilist[i-2].val )
+ error(FATAL, "bad interval list");
+
+} /* End of buildilist */
+
+/*****************************************************************************/
+
+addcolormap(list)
+
+ char *list; /* use this color map */
+
+{
+
+ static char *templist = NULL; /* a working copy of the color list */
+ char *ptr; /* next color in *templist */
+ int i = 0; /* assigned to this region in ilist[] */
+
+/*
+ *
+ * Assigns the integers in *list to the color field for the regions defined in
+ * ilist[]. Assumes ilist[] has already been setup.
+ *
+ */
+
+ if ( list != NULL ) {
+ if ( templist != NULL )
+ free(templist);
+ templist = savestring(list);
+
+ ptr = strtok(templist, ",/ \t\n");
+ while ( ptr != NULL ) {
+ ilist[i++].color = atoi(ptr) % 256;
+ ptr = strtok(NULL, ",/ \t\n");
+ } /* End while */
+ } /* End if */
+
+} /* End of addcolormap */
+
+/*****************************************************************************/
+
+setwindow(list)
+
+ char *list; /* corners of window into the matrix */
+
+{
+
+ static char *templist = NULL; /* a working copy of the window list */
+ char *ptr; /* next window coordinate in *templist */
+ int i = 0; /* assigned to this region in wlist[] */
+
+/*
+ *
+ * Sets up an optional window into the matrix.
+ *
+ */
+
+ wlist[0] = wlist[1] = 1;
+ wlist[2] = wlist[3] = 0;
+
+ if ( list != NULL ) {
+ if ( templist != NULL )
+ free(templist);
+ templist = savestring(list);
+
+ ptr = strtok(templist, ",/ \t\n");
+ while ( ptr != NULL ) {
+ wlist[i++] = atoi(ptr);
+ ptr = strtok(NULL, ",/ \t\n");
+ } /* End while */
+ } /* End if */
+
+} /* End of setwindow */
+
+/*****************************************************************************/
+
+inwindow()
+
+{
+
+ int r; /* row of the patcount element */
+ int c; /* column of the patcount element */
+
+/*
+ *
+ * Checks if the patcount element of the matrix is in the window.
+ *
+ */
+
+ r = (patcount/columns) + 1;
+ c = (patcount%columns) + 1;
+
+ return((c >= wlist[0]) && (r >= wlist[1]) && (c <= wlist[2]) && (r <= wlist[3]));
+
+} /* End of inwindow */
+
+/*****************************************************************************/
+
+inrange()
+
+{
+
+/*
+ *
+ * Checks if the current row lies in the window. Used right before we output the
+ * raster lines.
+ *
+ */
+
+ return(((patcount/columns) >= wlist[1]) && ((patcount/columns) <= wlist[3]));
+
+} /* End of inrange */
+
+/*****************************************************************************/
+
+mapfloat(element)
+
+ double element; /* floating point matrix element */
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Maps element into an integer in the range 0 to 255, and returns the result to
+ * the caller. Mapping is done using the color map that was saved in ilist[]. Also
+ * updates the count field for the region that contains element - not good!
+ *
+ */
+
+ for ( i = 1; i < next && ilist[i].val < element; i += 2 ) ;
+
+ if ( i > next || element < ilist[i].val )
+ i--;
+
+ ilist[i].count++;
+ return(ilist[i].color);
+
+} /* End of mapfloat */
+
+/*****************************************************************************/
+
+putrow()
+
+{
+
+ char *p1, *p2; /* starting and ending columns */
+ int n; /* set to bytes per pattern */
+ int i; /* loop index */
+
+/*
+ *
+ * Takes the scanline that's been saved in *raster, encodes it according to the
+ * value that's been assigned to bytespp, and writes the result to *fp_out. Each
+ * line in the output bitmap is terminated by a 0 on a line by itself.
+ *
+ */
+
+ n = (bytespp <= 0) ? columns : bytespp;
+
+ for ( p1 = raster, p2 = raster + n; p1 < eptr; p1 = p2 )
+ if ( patncmp(p1, n) == TRUE ) {
+ while ( patncmp(p2, n) == TRUE ) p2 += n;
+ p2 += n;
+ fprintf(fp_out, "%d ", n);
+ for ( i = 0; i < n; i++, p1++ )
+ fprintf(fp_out, "%.2X", ((int) *p1) & 0377);
+ fprintf(fp_out, " %d\n", (p2 - p1) / n);
+ } else {
+ while ( p2 < eptr && patncmp(p2, n) == FALSE ) p2 += n;
+ if ( p2 > eptr ) p2 = eptr;
+ fprintf(fp_out, "%d ", p2 - p1);
+ while ( p1 < p2 )
+ fprintf(fp_out, "%.2X", ((int) *p1++) & 0377);
+ fprintf(fp_out, " 0\n");
+ } /* End else */
+
+ fprintf(fp_out, "0\n");
+
+ rptr = raster;
+
+} /* End of putrow */
+
+/*****************************************************************************/
+
+labelmatrix()
+
+{
+
+ int total; /* number of elements in the window */
+ int i; /* loop index */
+
+/*
+ *
+ * Responsible for generating the PostScript calls that label the matrix, generate
+ * the legend, and print the matrix name.
+ *
+ */
+
+ fprintf(fp_out, "(%s) ((%d, %d) to (%d, %d)) labelmatrix\n", matrixname,
+ wlist[0], wlist[1], wlist[2], wlist[3]);
+
+ total = (wlist[2] - wlist[0] + 1) * (wlist[3] - wlist[1] + 1);
+
+ if ( nxtstat == OFF )
+ for ( i = 0; i < regions; i++ )
+ ilist[i].count = 0;
+
+ for ( i = 1; i < next; i += 2 )
+ fprintf(fp_out, "(%g) ", ilist[i].val);
+ fprintf(fp_out, "%d ", (regions - 1) / 2);
+
+ for ( i = regions - 1; i >= 0; i-- )
+ fprintf(fp_out, "{(\\%.3o)} %d ", ilist[i].color, ilist[i].count);
+ fprintf(fp_out, "%d %d legend\n", total, regions);
+
+} /* End of labelmatrix */
+
+/*****************************************************************************/
+
+patncmp(p1, n)
+
+ char *p1; /* first patterns starts here */
+ int n; /* and extends this many bytes */
+
+{
+
+ char *p2; /* address of the second pattern */
+
+/*
+ *
+ * Compares the two n byte patterns *p1 and *(p1+n). FALSE if returned is they're
+ * different or extend past the end of the current raster line.
+ *
+ */
+
+ p2 = p1 + n;
+
+ for ( ; n > 0; n--, p1++, p2++ )
+ if ( p2 >= eptr || *p1 != *p2 )
+ return(FALSE);
+
+ return(TRUE);
+
+} /* End of patncmp */
+
+/*****************************************************************************/
+
+char *savestring(str)
+
+ char *str; /* save this string */
+
+{
+
+ char *ptr = NULL; /* at this address */
+
+/*
+ *
+ * Copies string *str to a permanent place and returns the address to the caller.
+ *
+ */
+
+ if ( str != NULL && *str != '\0' ) {
+ if ( (ptr = malloc(strlen(str) + 1)) == NULL )
+ error(FATAL, "no memory available for string %s", str);
+ strcpy(ptr, str);
+ } /* End if */
+
+ return(ptr);
+
+} /* End of savestring */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postmd/postmd.h b/sys/src/cmd/postscript/postmd/postmd.h
new file mode 100755
index 000000000..715596a1f
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/postmd.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * An interval list used to map matrix elements into integers in the range 0 to
+ * 254 representing shades of gray on a PostScript printer. The list can be given
+ * using the -i option or can be set in the optional header that can preceed each
+ * matrix. The list should be a comma or space separated list that looks like,
+ *
+ * num1,num2, ... ,numn
+ *
+ * where each num is a floating point number. The list must be given in increasing
+ * numerical order. The n numbers in the list partion the real line into 2n+1
+ * regions given by,
+ *
+ * region1 element < num1
+ * region2 element = num1
+ * region3 element < num2
+ * region4 element = num3
+ * . .
+ * . .
+ * . .
+ * region2n element = numn
+ * region2n+1 element > numn
+ *
+ * Every number in a given region is mapped into an integer in the range 0 to 254
+ * and that number, when displayed on a PostScript printer using the image operator,
+ * prints as a square filled with a gray scale that reflects the integer that was
+ * chosen. 0 maps to black and 255 white (that's why 255 is normally omitted).
+ *
+ * The shades of gray chosen by the program are normally generated automatically,
+ * but can be reassigned using the -g option or by including a grayscale line in
+ * the optional header. The grayscale list is comma or space separated list of
+ * integers between 0 and 255 that's used to map individual regions into arbitray
+ * shade of gray, thus overriding the default choice made in the program. The list
+ * should look like,
+ *
+ * color1,color2, ... ,color2n+1
+ *
+ * where color1 applies to region1 and color2n+1 applies to region2n+1. If less
+ * than 2n+1 numbers are given the default assignments will be used for the missing
+ * regions. Each color must be an integer in the range 0 to 255.
+ *
+ * The default interval list is given below. The default grayscale maps 254 (almost
+ * white) into the first region and 0 (black) into the last.
+ *
+ */
+
+#define DFLTILIST "-1,0,1"
+
+/*
+ *
+ * The active interval list is built from an interval string and stored in an array
+ * whose elements are of type Ilist.
+ *
+ */
+
+typedef struct {
+ double val; /* only valid in kind is ENDPOINT */
+ int color; /* gray scale color */
+ long count; /* statistics for each region */
+} Ilist;
+
+/*
+ *
+ * Non-integer function declarations.
+ *
+ */
+
+char *savestring();
+
diff --git a/sys/src/cmd/postscript/postmd/postmd.mk b/sys/src/cmd/postscript/postmd/postmd.mk
new file mode 100755
index 000000000..ca131c555
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/postmd.mk
@@ -0,0 +1,95 @@
+MAKE=/bin/make
+MAKEFILE=postmd.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+MAN1DIR=/tmp
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+
+COMMONDIR=../common
+
+CFLGS=-O
+LDFLGS=-s
+
+CFLAGS=$(CFLGS) -I$(COMMONDIR)
+LDFLAGS=$(LDFLGS)
+
+HFILES=postmd.h\
+ $(COMMONDIR)/comments.h\
+ $(COMMONDIR)/ext.h\
+ $(COMMONDIR)/gen.h\
+ $(COMMONDIR)/path.h
+
+OFILES=postmd.o\
+ $(COMMONDIR)/glob.o\
+ $(COMMONDIR)/misc.o\
+ $(COMMONDIR)/request.o\
+ $(COMMONDIR)/tempnam.o
+
+all : postmd
+
+install : all
+ @if [ ! -d "$(POSTBIN)" ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ @if [ ! -d "$(POSTLIB)" ]; then \
+ mkdir $(POSTLIB); \
+ chmod 755 $(POSTLIB); \
+ chgrp $(GROUP) $(POSTLIB); \
+ chown $(OWNER) $(POSTLIB); \
+ fi
+ cp postmd $(POSTBIN)/postmd
+ @chmod 755 $(POSTBIN)/postmd
+ @chgrp $(GROUP) $(POSTBIN)/postmd
+ @chown $(OWNER) $(POSTBIN)/postmd
+ cp postmd.ps $(POSTLIB)/postmd.ps
+ @chmod 644 $(POSTLIB)/postmd.ps
+ @chgrp $(GROUP) $(POSTLIB)/postmd.ps
+ @chown $(OWNER) $(POSTLIB)/postmd.ps
+ cp postmd.1 $(MAN1DIR)/postmd.1
+ @chmod 644 $(MAN1DIR)/postmd.1
+ @chgrp $(GROUP) $(MAN1DIR)/postmd.1
+ @chown $(OWNER) $(MAN1DIR)/postmd.1
+
+clean :
+ rm -f *.o
+
+clobber : clean
+ rm -f postmd
+
+postmd : $(OFILES)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o postmd $(OFILES) -lm
+
+postmd.o : $(HFILES)
+
+$(COMMONDIR)/glob.o\
+$(COMMONDIR)/misc.o\
+$(COMMONDIR)/request.o\
+$(COMMONDIR)/tempnam.o :
+ @cd $(COMMONDIR); $(MAKE) -f common.mk SYSTEM=$(SYSTEM) `basename $@`
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ postmd.1 >XXX.1; \
+ mv XXX.1 postmd.1
+
diff --git a/sys/src/cmd/postscript/postmd/postmd.ps b/sys/src/cmd/postscript/postmd/postmd.ps
new file mode 100755
index 000000000..6a5661e2c
--- /dev/null
+++ b/sys/src/cmd/postscript/postmd/postmd.ps
@@ -0,0 +1,177 @@
+%
+% Version 3.3.2 prologue for matrix display files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/font /Helvetica def
+/formsperpage 1 def
+/landscape false def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/rotation 1 def
+/size 9 def
+/statspace 1.6 def
+/ticklength .06 def
+/tickspacing 10 def
+/xoffset 0 def
+/yoffset 0 def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/show {show} bind def % so later references don't bind
+/stringwidth {stringwidth} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ height width lt {
+ /statspace statspace height width div mul def
+ /size size height width div mul def
+ /ticklength ticklength height width div mul def
+ } if
+ /height height margin sub statspace inch sub ticklength inch sub size 6 mul sub def
+ /width width margin sub ticklength inch sub def
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ 0 height 2 div height width min 2 div sub translate
+ 0 statspace inch 2 div translate
+ magnification dup aspectratio mul scale
+
+ 0 setlinewidth
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/bitmap {
+ /scanlines exch def
+ /scanlength exch def
+
+ /picstr scanlength string def
+
+ gsave
+ height scanlines div width scanlength div min
+ /scaling exch def
+ scaling scaling scale
+
+ scanlength neg 2 div scanlines neg 2 div translate
+ scanlength scanlines scale
+ getbitmap
+ grestore
+} bind def
+
+/getbitmap {
+ scanlength scanlines 8 [scanlength 0 0 scanlines neg 0 scanlines] {
+ 0 {
+ currentfile token pop dup
+ 0 eq {pop pop exit} if
+ /charcount exch def
+ picstr 1 index charcount getinterval
+ /repl exch def
+ currentfile repl readhexstring pop pop
+ charcount add
+ currentfile token pop {
+ picstr 1 index repl putinterval
+ charcount add
+ } repeat
+ } loop
+ picstr
+ } image
+} bind def
+
+/labelmatrix {
+ /matrixlimits exch def
+ /matrixname exch def
+
+ gsave
+ scaling scaling scale
+ font findfont size scaling div scalefont setfont
+ scanlength neg 2 div scanlines 2 div translate
+
+ 0 scanlines size 1.5 mul scaling div add neg moveto
+ matrixname show
+
+ scanlength scanlines size 1.5 mul scaling div add neg moveto
+ matrixlimits stringwidth pop neg 0 rmoveto
+ matrixlimits show
+
+ newpath
+ 0 0 moveto
+ scanlength 0 rlineto
+ 0 scanlines neg rlineto
+ scanlength neg 0 rlineto
+ closepath stroke
+
+ scanlength tickspacing idiv 1 add tickspacing 0 ticks
+ scanlines tickspacing idiv 1 add 0 tickspacing neg ticks
+ grestore
+} bind def
+
+/ticks {
+ /dy exch def
+ /dx exch def
+
+ /tl ticklength inch scaling div def
+ newpath
+ 0 0 moveto
+ {
+ gsave dx 0 eq {tl neg 0} {0 tl} ifelse rlineto stroke grestore
+ dx dy rmoveto
+ } repeat
+} bind def
+
+/legend {
+ /regions exch def
+ /total exch def
+
+ gsave
+ width height min 2 div neg dup size 2 mul sub translate
+ 0 statspace inch neg translate
+
+ gsave
+ regions {
+ gsave
+ total div statspace inch size 2 mul sub mul size 2 mul add
+ width height min regions div exch scale
+ 1 1 8 [1 0 0 1 0 0] 5 -1 roll image
+ grestore
+ width height min regions div 0 translate
+ } repeat
+ grestore
+
+ width height min size 1.5 mul neg translate
+ font findfont size scalefont setfont
+ dup dup add 1 add width height min exch div /interval exch def
+ {
+ interval neg 0 translate
+ interval 2 div neg 0 translate
+ dup stringwidth pop 2 div neg 0 moveto show
+ interval 2 div neg 0 translate
+ } repeat
+ grestore
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/postprint/README b/sys/src/cmd/postscript/postprint/README
new file mode 100755
index 000000000..f6185bdce
--- /dev/null
+++ b/sys/src/cmd/postscript/postprint/README
@@ -0,0 +1,4 @@
+
+Simple ASCII file to PostScript translator. The -e options is new
+and allows access to all characters in PostScript fonts.
+
diff --git a/sys/src/cmd/postscript/postprint/mkfile b/sys/src/cmd/postscript/postprint/mkfile
new file mode 100755
index 000000000..c1b3572d7
--- /dev/null
+++ b/sys/src/cmd/postscript/postprint/mkfile
@@ -0,0 +1,33 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=postprint
+OFILES=postprint.$O\
+
+COMMONDIR=../common
+
+HFILES=postprint.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/ext.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+LIB=$COMMONDIR/com.a$O
+
+</sys/src/cmd/mkone
+
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+install:V: $POSTLIB/postprint.ps
+
+$POSTLIB/postprint.ps: postprint.ps
+ cp $prereq $target
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/postprint/postprint.c b/sys/src/cmd/postscript/postprint/postprint.c
new file mode 100755
index 000000000..6dab5c118
--- /dev/null
+++ b/sys/src/cmd/postscript/postprint/postprint.c
@@ -0,0 +1,794 @@
+/*
+ *
+ * postprint - PostScript translator for ASCII files.
+ *
+ * A simple program that translates ASCII files into PostScript. All it really
+ * does is expand tabs and backspaces, handle character quoting, print text lines,
+ * and control when pages are started based on the requested number of lines per
+ * page.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following procedures are defined
+ * in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how the program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * l
+ *
+ * string l -
+ *
+ * Prints string starting in the first column and then goes to the next
+ * line.
+ *
+ * L
+ *
+ * mark string column string column ... L mark
+ *
+ * Prints each string on the stack starting at the horizontal position
+ * selected by column. Used when tabs and spaces can be sufficiently well
+ * compressed to make the printer overhead worthwhile. Always used when
+ * we have to back up.
+ *
+ * LL
+ *
+ * mark string column string column ... LL mark
+ *
+ * Like L, but only used to prevent potential PostScript stack overflow
+ * from too many string/column pairs. Stays on the current line. It will
+ * not be needed often!!
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * Almost everything has been changed in this version of postprint. The program
+ * is more intelligent, especially about tabs, spaces, and backspacing, and as a
+ * result output files usually print faster. Output files also now conform to
+ * Adobe's file structuring conventions, which is undoubtedly something I should
+ * have done in the first version of the program. If the number of lines per page
+ * is set to 0, which can be done using the -l option, pointsize will be used to
+ * guess a reasonable value. The estimate is based on the values of LINESPP,
+ * POINTSIZE, and pointsize, and assumes LINESPP lines would fit on a page if
+ * we printed in size POINTSIZE. Selecting a point size using the -s option and
+ * adding -l0 to the command line forces the guess to be made.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#ifdef plan9
+#define isascii(c) ((unsigned char)(c)<=0177)
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable declarations */
+#include "postprint.h" /* a few special definitions */
+
+char *optnames = "a:c:ef:l:m:n:o:p:r:s:t:x:y:A:C:E:J:L:P:R:DI";
+
+char *prologue = POSTPRINT; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+int linespp = LINESPP; /* number of lines per page */
+int pointsize = POINTSIZE; /* in this point size */
+int tabstops = TABSTOPS; /* tabs set at these columns */
+int crmode = 0; /* carriage return mode - 0, 1, or 2 */
+int extended = TRUE; /* use escapes for unprintable chars */
+
+int col = 1; /* next character goes in this column */
+int line = 1; /* on this line */
+
+int stringcount = 0; /* number of strings on the stack */
+int stringstart = 1; /* column where current one starts */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+FILE *fp_in = stdin; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that translates ASCII files into PostScript. If there's more
+ * than one input file, each begins on a new page.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* really just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header and prologue */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Makes sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ if ( DOROUND )
+ cat(ROUNDPAGE);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ * Although any PostScript font can be used, things will only work well for
+ * constant width fonts.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'e': /* obsolete - it's now always on */
+ extended = TRUE;
+ break;
+
+ case 'f': /* use this PostScript font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'l': /* lines per page */
+ linespp = atoi(optarg);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'r': /* carriage return mode */
+ crmode = atoi(optarg);
+ break;
+
+ case 's': /* point size */
+ pointsize = atoi(optarg);
+ fprintf(stdout, "/pointsize %s def\n", optarg);
+ break;
+
+ case 't': /* tabstops */
+ tabstops = atoi(optarg);
+ break;
+
+ case 'x': /* shift things horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font, although things will only work well for constant width
+ * fonts.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed. linespp (lines per page) can be set using the -l
+ * option. If it's not positive we calculate a reasonable value using the
+ * requested point size - assuming LINESPP lines fit on a page in point size
+ * POINTSIZE.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+ if ( linespp <= 0 )
+ linespp = LINESPP * POINTSIZE / pointsize;
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll translate stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ text();
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ text();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages with a TRAILER
+ * comment, make sure the last page prints, and add things like the PAGES comment
+ * that can only be determined after all the input files have been read.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
+ * requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+text()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Translates *fp_in into PostScript. Intercepts space, tab, backspace, newline,
+ * return, and formfeed. Everything else goes to oput(), which handles quoting
+ * (if needed) and escapes for nonascii characters if extended is TRUE. The
+ * redirect(-1) call forces the initial output to go to /dev/null - so stuff
+ * that formfeed() does at the end of each page goes to /dev/null rather than
+ * the real output file.
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ formfeed(); /* force PAGE comment etc. */
+
+ while ( (ch = getc(fp_in)) != EOF )
+ switch ( ch ) {
+ case '\n':
+ newline();
+ break;
+
+ case '\t':
+ case '\b':
+ case ' ':
+ spaces(ch);
+ break;
+
+ case '\014':
+ formfeed();
+ break;
+
+ case '\r':
+ if ( crmode == 1 )
+ spaces(ch);
+ else if ( crmode == 2 )
+ newline();
+ break;
+
+ default:
+ oput(ch);
+ break;
+ } /* End switch */
+
+ formfeed(); /* next file starts on a new page? */
+
+} /* End of text */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+/*
+ *
+ * Called whenever we've finished with the last page and want to get ready for the
+ * next one. Also used at the beginning and end of each input file, so we have to
+ * be careful about what's done. The first time through (up to the redirect() call)
+ * output goes to /dev/null.
+ *
+ * Adobe now recommends that the showpage operator occur after the page level
+ * restore so it can be easily redefined to have side-effects in the printer's VM.
+ * Although it seems reasonable I haven't implemented it, because it makes other
+ * things, like selectively setting manual feed or choosing an alternate paper
+ * tray, clumsy - at least on a per page basis.
+ *
+ */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ endline(); /* print the last line */
+
+ fprintf(fp_out, "cleartomark\n");
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+
+ line = 1;
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+newline()
+
+{
+
+/*
+ *
+ * Called when we've read a newline character. The call to startline() ensures
+ * that at least an empty string is on the stack.
+ *
+ */
+
+ startline();
+ endline(); /* print the current line */
+
+ if ( ++line > linespp ) /* done with this page */
+ formfeed();
+
+} /* End of newline */
+
+/*****************************************************************************/
+
+spaces(ch)
+
+ int ch; /* next input character */
+
+{
+
+ int endcol; /* ending column */
+ int i; /* final distance - in spaces */
+
+/*
+ *
+ * Counts consecutive spaces, tabs, and backspaces and figures out where the next
+ * string should start. Once that's been done we try to choose an efficient way
+ * to output the required number of spaces. The choice is between using procedure
+ * l with a single string on the stack and L with several string and column pairs.
+ * We usually break even, in terms of the size of the output file, if we need four
+ * consecutive spaces. More means using L decreases the size of the file. For now
+ * if there are less than 6 consecutive spaces we just add them to the current
+ * string, otherwise we end that string, follow it by its starting position, and
+ * begin a new one that starts at endcol. Backspacing is always handled this way.
+ *
+ */
+
+ startline(); /* so col makes sense */
+ endcol = col;
+
+ do {
+ if ( ch == ' ' )
+ endcol++;
+ else if ( ch == '\t' )
+ endcol += tabstops - ((endcol - 1) % tabstops);
+ else if ( ch == '\b' )
+ endcol--;
+ else if ( ch == '\r' )
+ endcol = 1;
+ else break;
+ } while ( ch = getc(fp_in) ); /* if ch is 0 we'd quit anyway */
+
+ ungetc(ch, fp_in); /* wasn't a space, tab, or backspace */
+
+ if ( endcol < 1 ) /* can't move past left edge */
+ endcol = 1;
+
+ if ( (i = endcol - col) >= 0 && i < 6 )
+ for ( ; i > 0; i-- )
+ oput((int)' ');
+ else {
+ endstring();
+ col = stringstart = endcol;
+ } /* End else */
+
+} /* End of spaces */
+
+/*****************************************************************************/
+
+startline()
+
+{
+
+/*
+ *
+ * Called whenever we want to be certain we're ready to start pushing characters
+ * into an open string on the stack. If stringcount is positive we've already
+ * started, so there's nothing to do. The first string starts in column 1.
+ *
+ */
+
+ if ( stringcount < 1 ) {
+ putc('(', fp_out);
+ stringstart = col = 1;
+ stringcount = 1;
+ } /* End if */
+
+} /* End of startline */
+
+/*****************************************************************************/
+
+endstring()
+
+{
+
+/*
+ *
+ * End the current string and start a new one.
+ *
+ */
+
+ if ( stringcount > 100 ) { /* don't put too much on the stack */
+ fprintf(fp_out, ")%d LL\n(", stringstart-1);
+ stringcount = 2; /* kludge - don't let endline() use l */
+ } else {
+ fprintf(fp_out, ")%d(", stringstart-1);
+ stringcount++;
+ } /* End else */
+
+} /* End of endstring */
+
+/*****************************************************************************/
+
+endline()
+
+{
+
+/*
+ *
+ * Generates a call to the PostScript procedure that processes all the text on
+ * the stack - provided stringcount is positive. If one string is on the stack
+ * the fast procedure (ie. l) is used to print the line, otherwise the slower
+ * one that processes string and column pairs is used.
+ *
+ */
+
+ if ( stringcount == 1 )
+ fprintf(fp_out, ")l\n");
+ else if ( stringcount > 1 )
+ fprintf(fp_out, ")%d L\n", stringstart-1);
+
+ stringcount = 0;
+
+} /* End of endline */
+
+/*****************************************************************************/
+
+oput(ch)
+
+ int ch; /* next output character */
+
+{
+
+/*
+ *
+ * Responsible for adding all printing characters from the input file to the
+ * open string on top of the stack.
+ *
+ */
+
+ if ( isascii(ch) && isprint(ch) ) {
+ startline();
+ if ( ch == '(' || ch == ')' || ch == '\\' )
+ putc('\\', fp_out);
+ putc(ch, fp_out);
+ col++;
+ } else if ( extended == TRUE ) {
+ startline();
+ fprintf(fp_out, "\\%.3o", ch & 0377);
+ col++;
+ } /* End if */
+
+} /* End of oput */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postprint/postprint.h b/sys/src/cmd/postscript/postprint/postprint.h
new file mode 100755
index 000000000..07c14fe51
--- /dev/null
+++ b/sys/src/cmd/postscript/postprint/postprint.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Default lines per page, tab stops, and point size.
+ *
+ */
+
+#define LINESPP 66
+#define TABSTOPS 8
+#define POINTSIZE 10
+
+/*
+ *
+ * An array of type Fontmap helps convert font names requested by users into
+ * legitimate PostScript names. The array is initialized using FONTMAP, which must
+ * end with an entry that has NULL defined as its name field. The only fonts that
+ * are guaranteed to work well are the constant width fonts.
+ *
+ */
+
+typedef struct {
+ char *name; /* user's font name */
+ char *val; /* corresponding PostScript name */
+} Fontmap;
+
+#define FONTMAP \
+ \
+ { \
+ "R", "Courier", \
+ "I", "Courier-Oblique", \
+ "B", "Courier-Bold", \
+ "CO", "Courier", \
+ "CI", "Courier-Oblique", \
+ "CB", "Courier-Bold", \
+ "CW", "Courier", \
+ "PO", "Courier", \
+ "courier", "Courier", \
+ "cour", "Courier", \
+ "co", "Courier", \
+ NULL, NULL \
+ }
+
+/*
+ *
+ * Some of the non-integer functions in postprint.c.
+ *
+ */
+
+char *get_font();
+
diff --git a/sys/src/cmd/postscript/postprint/postprint.ps b/sys/src/cmd/postscript/postprint/postprint.ps
new file mode 100755
index 000000000..315b5639a
--- /dev/null
+++ b/sys/src/cmd/postscript/postprint/postprint.ps
@@ -0,0 +1,75 @@
+%
+% Version 3.3.2 prologue for text files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/font /Courier def
+/formsperpage 1 def
+/landscape false def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/pointsize 10 def
+/rotation 1 def
+/xoffset .25 def
+/yoffset .25 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/show {show} bind def % so later references don't bind
+/stringwidth {stringwidth} bind def
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+ font findfont pointsize scalefont setfont
+ /charwidth (M) stringwidth pop def
+ /linespace pointsize pointsize .10 mul add neg def
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ margin 2 div dup neg translate
+ magnification dup aspectratio mul scale
+ height width div 1 min dup scale
+ 0 linespace translate
+} def
+
+/pagedimensions {
+ useclippath userdict /gotpagebbox known not and {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def 0 0 moveto 0} bind def
+
+/L {
+ counttomark 2 idiv {charwidth mul currentpoint exch pop moveto show} repeat
+ linespace add dup 0 exch moveto
+} bind def
+
+/l {show linespace add dup 0 exch moveto} bind def
+
+/LL {
+ counttomark 2 idiv {charwidth mul currentpoint exch pop moveto show} repeat
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/postreverse/README b/sys/src/cmd/postscript/postreverse/README
new file mode 100755
index 000000000..d7038ae88
--- /dev/null
+++ b/sys/src/cmd/postscript/postreverse/README
@@ -0,0 +1,10 @@
+
+A simple program that reverses the pages in PostScript files that
+conform to Adobe's Version 1.0 or Version 2.0 file structuring
+conventions.
+
+postrevese also handles a small class of files that violate page
+independence (eg. output from dpost) and can be used with all the
+translators in this package. The output can be conforming even if
+the input file wasn't.
+
diff --git a/sys/src/cmd/postscript/postreverse/mkfile b/sys/src/cmd/postscript/postreverse/mkfile
new file mode 100755
index 000000000..0e935e804
--- /dev/null
+++ b/sys/src/cmd/postscript/postreverse/mkfile
@@ -0,0 +1,29 @@
+</$objtype/mkfile
+
+<../config
+
+COMMONDIR=../common
+
+TARG=postreverse
+
+OFILES=postreverse.$O\
+
+HFILES=postreverse.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/ext.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+LIB=$COMMONDIR/com.a$O
+
+</sys/src/cmd/mkone
+
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/postreverse/postreverse.c b/sys/src/cmd/postscript/postreverse/postreverse.c
new file mode 100755
index 000000000..de7448f09
--- /dev/null
+++ b/sys/src/cmd/postscript/postreverse/postreverse.c
@@ -0,0 +1,541 @@
+/*
+ *
+ * postreverse - reverse the page order in certain PostScript files.
+ *
+ * Page reversal relies on being able to locate sections of a document using file
+ * structuring comments defined by Adobe (ie. the 1.0 and now 2.0 conventions) and
+ * a few I've added. Among other things a minimally conforming document, according
+ * to the 1.0 conventions,
+ *
+ * 1) Marks the end of the prologue with an %%EndProlog comment.
+ *
+ * 2) Starts each page with a %%Page: comment.
+ *
+ * 3) Marks the end of all the pages %%Trailer comment.
+ *
+ * 4) Obeys page independence (ie. pages can be arbitrarily rearranged).
+ *
+ * The most important change (at least for this program) that Adobe made in going
+ * from the 1.0 to the 2.0 structuring conventions was in the prologue. They now
+ * say the prologue should only define things, and the global initialization that
+ * was in the prologue (1.0 conventions) should now come after the %%EndProlog
+ * comment but before the first %%Page: comment and be bracketed by %%BeginSetup
+ * and %%EndSetup comments. So a document that conforms to Adobe's 2.0 conventions,
+ *
+ * 1) Marks the end of the prologue (only definitions) with %%EndProlog.
+ *
+ * 2) Brackets global initialization with %%BeginSetup and %%EndSetup comments
+ * which come after the prologue but before the first %Page: comment.
+ *
+ * 3) Starts each page with a %%Page: comment.
+ *
+ * 4) Marks the end of all the pages with a %%Trailer comment.
+ *
+ * 5) Obeys page independence.
+ *
+ * postreverse can handle documents that follow the 1.0 or 2.0 conventions, but has
+ * also been extended slightly so it works properly with the translators (primarily
+ * dpost) supplied with this package. The page independence requirement has been
+ * relaxed some. In particular definitions exported to the global environment from
+ * within a page should be bracketed by %%BeginGlobal and %%EndGlobal comments.
+ * postreverse pulls them out of each page and inserts them in the setup section
+ * of the document, immediately before it writes the %%EndProlog (for version 1.0)
+ * or %%EndSetup (for version 2.0) comments.
+ *
+ * In addition postreverse accepts documents that choose to mark the end of each
+ * page with a %%EndPage: comment, which from a translator's point of view is often
+ * a more natural approach. Both page boundary comments (ie. Page: and %%EndPage:)
+ * are also accepted, but be warned that everything between consecutive %%EndPage:
+ * and %%Page: comments will be ignored.
+ *
+ * So a document that will reverse properly with postreverse,
+ *
+ * 1) Marks the end of the prologue with %%EndProlog.
+ *
+ * 2) May have a %%BeginSetup/%%EndSetup comment pair before the first %%Page:
+ * comment that brackets any global initialization.
+ *
+ * 3) Marks the start of each page with a %%Page: comment, or the end of each
+ * page with a %%EndPage: comment. Both page boundary comments are allowed.
+ *
+ * 4) Marks the end of all the pages with a %%Trailer comment.
+ *
+ * 5) Obeys page independence or violates it to a rather limited extent and
+ * marks the violations with %%BeginGlobal and %%EndGlobal comments.
+ *
+ * If no file arguments are given postreverse copies stdin to a temporary file and
+ * then processes that file. That means the input is read three times (rather than
+ * two) whenever we handle stdin. That's expensive, and shouldn't be too difficult
+ * to fix, but I haven't gotten around to it yet.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for temporary directory */
+#include "ext.h" /* external variable declarations */
+#include "postreverse.h" /* a few special definitions */
+
+int page = 1; /* current page number */
+int forms = 1; /* forms per page in the input file */
+
+char *temp_dir = TEMPDIR; /* temp directory for copying stdin */
+
+Pages pages[1000]; /* byte offsets for all pages */
+int next_page = 0; /* next page goes here */
+long start; /* starting offset for next page */
+long endoff = -1; /* offset where TRAILER was found */
+int noreverse = FALSE; /* don't reverse pages if TRUE */
+char *endprolog = ENDPROLOG; /* occasionally changed to ENDSETUP */
+
+double version = 3.3; /* of the input file */
+int ignoreversion = FALSE; /* ignore possible forms.ps problems */
+
+char buf[2048]; /* line buffer for input file */
+
+FILE *fp_in; /* stuff is read from this file */
+FILE *fp_out = stdout; /* and written here */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that reverses the pages in specially formatted PostScript
+ * files. Will work with all the translators in this package, and should handle
+ * any document that conforms to Adobe's version 1.0 or 2.0 file structuring
+ * conventions. Only one input file is allowed, and it can either be a named (on
+ * the command line) file or stdin.
+ *
+ */
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ options(); /* first get command line options */
+ arguments(); /* then process non-option arguments */
+ done(); /* and clean things up */
+
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Makes sure we handle interrupts properly.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ char *optnames = "n:o:rvT:DI";
+
+ extern char *optarg; /* used by getopt() */
+ extern int optind;
+
+/*
+ *
+ * Reads and processes the command line options. The -r option (ie. the one that
+ * turns page reversal off) is really only useful if you want to take dpost output
+ * and produce a page independent output file. In that case global definitions
+ * made within pages and bracketed by %%BeginGlobal/%%EndGlobal comments will be
+ * moved into the prologue or setup section of the document.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'n': /* forms per page */
+ if ( (forms = atoi(optarg)) <= 0 )
+ error(FATAL, "illegal forms request %s", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'r': /* don't reverse the pages */
+ noreverse = TRUE;
+ break;
+
+ case 'v': /* ignore possible forms.ps problems */
+ ignoreversion = TRUE;
+ break;
+
+ case 'T': /* temporary file directory */
+ temp_dir = optarg;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+ char *name; /* name of the input file */
+
+/*
+ *
+ * postreverse only handles one input file at a time, so if there's more than one
+ * argument left when we get here we'll quit. If none remain we copy stdin to a
+ * temporary file and process that file.
+ *
+ */
+
+ if ( argc > 1 ) /* can't handle more than one file */
+ error(FATAL, "too many arguments");
+
+ if ( argc == 0 ) /* copy stdin to a temporary file */
+ name = copystdin();
+ else name = *argv;
+
+ if ( (fp_in = fopen(name, "r")) == NULL )
+ error(FATAL, "can't open %s", name);
+
+ reverse();
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Cleans things up after we've finished reversing the pages in the input file.
+ * All that's really left to do is remove the temp file, provided we used one.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+char *copystdin()
+
+{
+
+ int fd_out; /* for the temporary file */
+ int fd_in; /* for stdin */
+ int count; /* number of bytes put in buf[] */
+
+/*
+ *
+ * Copies stdin to a temporary file and returns the pathname of that file to the
+ * caller. It's an expensive way of doing things, because it means we end up
+ * reading the input file three times - rather than just twice. Could probably be
+ * fixed by creating the temporary file on the fly as we read the file the first
+ * time.
+ *
+ */
+
+ if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
+ error(FATAL, "can't generate temp file name");
+
+ if ( (fd_out = creat(temp_file, 0660)) == -1 )
+ error(FATAL, "can't open %s", temp_file);
+
+ fd_in = fileno(stdin);
+
+ while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
+ if ( write(fd_out, buf, count) != count )
+ error(FATAL, "error writing to %s", temp_file);
+
+ close(fd_out);
+
+ return(temp_file);
+
+} /* End of copystdin */
+
+/*****************************************************************************/
+
+reverse()
+
+{
+
+/*
+ *
+ * Begins by looking for the ENDPROLOG comment in the input file. Everything up to
+ * that comment is copied to the output file. If the comment isn't found the entire
+ * input file is copied and moreprolog() returns FALSE. Otherwise readpages() reads
+ * the rest of the input file and remembers (in pages[]) where each page starts and
+ * ends. In addition everything bracketed by %%BeginGlobal and %%EndGlobal comments
+ * is immediately added to the new prologue (or setup section) and ends up being
+ * removed from the individual pages. When readpages() finds the TRAILER comment
+ * or gets to the end of the input file we go back to the pages[] array and use
+ * the saved offsets to write the pages out in reverse order. Finally everything
+ * from the TRAILER comment to the end of the input file is copied to the output
+ * file.
+ *
+ */
+
+ if ( moreprolog(ENDPROLOG) == TRUE ) {
+ readpages();
+ writepages();
+ trailer();
+ } /* End if */
+
+} /* End of reverse */
+
+/*****************************************************************************/
+
+moreprolog(str)
+
+ char *str; /* copy everything up to this string */
+
+{
+
+ int len; /* length of FORMSPERPAGE string */
+ int vlen; /* length of VERSION string */
+
+/*
+ *
+ * Looks for string *str at the start of a line and copies everything up to that
+ * string to the output file. If *str isn't found the entire input file will end
+ * up being copied to the output file and FALSE will be returned to the caller.
+ * The first call (made from reverse()) looks for ENDPROLOG. Any other call comes
+ * from readpages() and will be looking for the ENDSETUP comment.
+ *
+ */
+
+ len = strlen(FORMSPERPAGE);
+ vlen = strlen(VERSION);
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL ) {
+ if ( strcmp(buf, str) == 0 )
+ return(TRUE);
+ else if ( strncmp(buf, FORMSPERPAGE, len) == 0 )
+ forms = atoi(&buf[len+1]);
+ else if ( strncmp(buf, VERSION, vlen) == 0 )
+ version = atof(&buf[vlen+1]);
+ fprintf(fp_out, "%s", buf);
+ } /* End while */
+
+ return(FALSE);
+
+} /* End of moreprolog */
+
+/*****************************************************************************/
+
+readpages()
+
+{
+
+ int endpagelen; /* length of ENDPAGE */
+ int pagelen; /* and PAGE strings */
+ int sawendpage = TRUE; /* ENDPAGE equivalent marked last page */
+ int gotpage = FALSE; /* TRUE disables BEGINSETUP stuff */
+
+/*
+ *
+ * Records starting and ending positions of the requested pages (usually all of
+ * them), puts global definitions in the prologue, and remembers where the TRAILER
+ * was found.
+ *
+ * Page boundaries are marked by the strings PAGE, ENDPAGE, or perhaps both.
+ * Application programs will normally find one or the other more convenient, so
+ * in most cases only one kind of page delimiter will be found in a particular
+ * document.
+ *
+ */
+
+ pages[0].start = ftell(fp_in); /* first page starts after ENDPROLOG */
+ endprolog = ENDPROLOG;
+
+ endpagelen = strlen(ENDPAGE);
+ pagelen = strlen(PAGE);
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] != '%' )
+ continue;
+ else if ( strncmp(buf, ENDPAGE, endpagelen) == 0 ) {
+ if ( in_olist(page++) == ON ) {
+ pages[next_page].empty = FALSE;
+ pages[next_page++].stop = ftell(fp_in);
+ } /* End if */
+ pages[next_page].start = ftell(fp_in);
+ sawendpage = TRUE;
+ gotpage = TRUE;
+ } else if ( strncmp(buf, PAGE, pagelen) == 0 ) {
+ if ( sawendpage == FALSE && in_olist(page++) == ON ) {
+ pages[next_page].empty = FALSE;
+ pages[next_page++].stop = ftell(fp_in) - strlen(buf);
+ } /* End if */
+ pages[next_page].start = ftell(fp_in) - strlen(buf);
+ sawendpage = FALSE;
+ gotpage = TRUE;
+ } else if ( gotpage == FALSE && strcmp(buf, BEGINSETUP) == 0 ) {
+ fprintf(fp_out, "%s", endprolog);
+ fprintf(fp_out, "%s", BEGINSETUP);
+ moreprolog(ENDSETUP);
+ endprolog = ENDSETUP;
+ } else if ( strcmp(buf, BEGINGLOBAL) == 0 ) {
+ moreprolog(ENDGLOBAL);
+ } else if ( strcmp(buf, TRAILER) == 0 ) {
+ if ( sawendpage == FALSE )
+ pages[next_page++].stop = ftell(fp_in) - strlen(buf);
+ endoff = ftell(fp_in);
+ break;
+ } /* End if */
+
+} /* End of readpages */
+
+/*****************************************************************************/
+
+writepages()
+
+{
+
+ int i, j, k; /* loop indices */
+
+/*
+ *
+ * Goes through the pages[] array, usually from the bottom up, and writes out all
+ * the pages. Documents that print more than one form per page cause things to get
+ * a little more complicated. Each physical page has to have its subpages printed
+ * in the correct order, and we have to build a few dummy subpages for the last
+ * (and now first) sheet of paper, otherwise things will only occasionally work.
+ *
+ */
+
+ fprintf(fp_out, "%s", endprolog);
+
+ if ( noreverse == FALSE ) /* fill out the first page */
+ for ( i = (forms - next_page % forms) % forms; i > 0; i--, next_page++ )
+ pages[next_page].empty = TRUE;
+ else forms = next_page; /* turns reversal off in next loop */
+
+ for ( i = next_page - forms; i >= 0; i -= forms )
+ for ( j = i, k = 0; k < forms; j++, k++ )
+ if ( pages[j].empty == TRUE ) {
+ if ( ignoreversion == TRUE || version > 3.1 ) {
+ fprintf(fp_out, "%s 0 0\n", PAGE);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s 0 0\n", ENDPAGE);
+ } else {
+ fprintf(fp_out, "%s 0 0\n", PAGE);
+ fprintf(fp_out, "save showpage restore\n");
+ fprintf(fp_out, "%s 0 0\n", ENDPAGE);
+ } /* End else */
+ } else copypage(pages[j].start, pages[j].stop);
+
+} /* End of writepages */
+
+/*****************************************************************************/
+
+copypage(start, stop)
+
+ long start; /* starting from this offset */
+ long stop; /* and ending here */
+
+{
+
+/*
+ *
+ * Copies the page beginning at offset start and ending at stop to the output
+ * file. Global definitions are skipped since they've already been added to the
+ * prologue.
+ *
+ */
+
+ fseek(fp_in, start, 0);
+
+ while ( ftell(fp_in) < stop && fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] == '%' && strcmp(buf, BEGINGLOBAL) == 0 )
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL && strcmp(buf, ENDGLOBAL) != 0 ) ;
+ else fprintf(fp_out, "%s", buf);
+
+} /* End of copypage */
+
+/*****************************************************************************/
+
+trailer()
+
+{
+
+/*
+ *
+ * Makes sure everything from the TRAILER string to EOF is copied to the output
+ * file.
+ *
+ */
+
+ if ( endoff > 0 ) {
+ fprintf(fp_out, "%s", TRAILER);
+ fseek(fp_in, endoff, 0);
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ fprintf(fp_out, "%s", buf);
+ } /* End if */
+
+} /* End of trailer */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/postreverse/postreverse.h b/sys/src/cmd/postscript/postreverse/postreverse.h
new file mode 100755
index 000000000..edae7a277
--- /dev/null
+++ b/sys/src/cmd/postscript/postreverse/postreverse.h
@@ -0,0 +1,21 @@
+/*
+ *
+ * An array of type Pages is used to keep track of the starting and ending byte
+ * offsets for the pages we've been asked to print.
+ *
+ */
+
+typedef struct {
+ long start; /* page starts at this byte offset */
+ long stop; /* and ends here */
+ int empty; /* dummy page if TRUE */
+} Pages;
+
+/*
+ *
+ * Some of the non-integer functions in postreverse.c.
+ *
+ */
+
+char *copystdin();
+
diff --git a/sys/src/cmd/postscript/postscript.mk b/sys/src/cmd/postscript/postscript.mk
new file mode 100755
index 000000000..6e65d2e58
--- /dev/null
+++ b/sys/src/cmd/postscript/postscript.mk
@@ -0,0 +1,202 @@
+#
+# Top level makefile. Instructions are included here and in the README file.
+#
+# First save a copy of this file. Then adjust the following definitions (all
+# come immediatedly after the initial block of comments):
+#
+# MAKE where make lives
+#
+# MAKEFILE name of this file - for recursive make calls. Must change
+# if you rename this file.
+#
+# SYSTEM best match for your version of Unix. Current choices for
+# SYSTEM are:
+#
+# SYSV - System V
+# V9 - Ninth Edition
+# BSD4_2 - Berkeley (eg. Sun)
+#
+# Controls conditional compilation in a few places.
+#
+# VERSION refers to the Version of the DWB package
+#
+# GROUP group assigned to all installed files
+#
+# OWNER owner of everything that's installed
+#
+# HOSTDIR hostresident font directory for PostScript printers. Only
+# used in the font download program.
+#
+# FONTDIR width table directory - for troff and dpost
+#
+# MAN1DIR command manpages. A command and its manpage are installed
+# together - there's no easy way to avoid it. Setting MAN1DIR
+# to an existing temporary directory (e.g. /tmp) means an
+# install will work but manpages won't go anywhere permanent.
+# MAN1DIR must already exist - it will not be created during
+# an install.
+#
+# POSTBIN where most PostScript support programs go. dpost and picpack
+# are the exceptions.
+#
+# POSTLIB prologues and miscellaneous PostScript files. Primarily for
+# the programs that live in POSTBIN.
+#
+# CFLGS common compiler options - used to build CFLAGS in the low
+# level makefiles. CFLGS and LDFLGS are best set on the make
+# command line.
+#
+# LDFLGS common link editor options - used to build LDFLAGS in the
+# low level makefiles. LDFLGS and CFLGS are best set on the
+# make command line.
+#
+# DKHOST set it to TRUE to compile the DKHOST Datakit support code
+# in postio. Temporarily resets SYSTEM to SYSV if DKHOST is
+# TRUE and SYSTEM is BSD4_2. Ignored if SYSTEM is not SYSV
+# or BSD4_2.
+#
+# DKSTREAMS enables streams based DKHOST support in postio when DKHOST
+# is TRUE and SYSTEM is SYSV or BSD4_2. Choices are TRUE,
+# FALSE, or a stream module name (e.g. dknetty or dkty). TRUE
+# selects dknetty. Newer systems may expect dkty.
+#
+# ROUNDPAGE must only be set to TRUE or FALSE. TRUE means translators
+# include code that maps clipping path dimensions into known
+# paper sizes.
+#
+# TARGETS the default list of what's built by make. Each target must
+# be the name of a source directory. A target that names a
+# non-existent source directory is ignored. Setting TARGETS
+# on the make command line overrides the default list.
+#
+# Source files must be updated whenever this file changes. If you change any
+# definitions type,
+#
+# make -f postscript.mk changes
+#
+# to update the source files, man pages, and low level makefiles.
+#
+# To build (but not install) the default package (i.e. everything named by
+# TARGETS) type,
+#
+# make -f postscript.mk all
+#
+# The recommended way to build and install the package is,
+#
+# make -f postscript.mk all install
+#
+# Although you'll likely have to be root for the install to work.
+#
+# After the package is installed use,
+#
+# make -f postscript.mk clobber
+#
+# to delete binary files and compiled programs from the source directories.
+#
+# Set TARGETS on the command line to select part of the package. For example,
+#
+# make -f postscript.mk TARGETS="dpost devpost" all install
+#
+# builds and installs dpsot and the PostScript font tables. Quotes hide white
+# space from the shell.
+#
+
+MAKE=/bin/make
+MAKEFILE=postscript.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+ROOT=
+FONTDIR=$(ROOT)/usr/lib/font
+HOSTDIR=$(ROOT)/usr/lib/font/postscript
+MAN1DIR=$(ROOT)/tmp
+POSTBIN=$(ROOT)/usr/bin/postscript
+POSTLIB=$(ROOT)/usr/lib/postscript
+TMACDIR=$(ROOT)/usr/lib/tmac
+
+COMMONDIR=common
+CURRENTDIR=.
+
+CFLGS=-O
+LDFLGS=-s
+
+DKHOST=FALSE
+DKSTREAMS=FALSE
+ROUNDPAGE=TRUE
+
+#
+# $(TARGETS) is the default list of things built by make. Pick dpost or
+# dpost.utf but not both!
+#
+
+TARGETS=buildtables\
+ common\
+ cropmarks\
+ devLatin1\
+ devpost\
+ download\
+ dpost.utf\
+ grabit\
+ hardcopy\
+ mpictures\
+ picpack\
+ postbgi\
+ postdaisy\
+ postdmd\
+ postgif\
+ postio\
+ postmd\
+ postprint\
+ postreverse\
+ posttek\
+ printfont\
+ psencoding\
+ psfiles\
+ trofftable
+
+ACTION=all
+
+all : $(TARGETS)
+
+clean clobber :
+ @$(MAKE) -e -f $(MAKEFILE) MAKE=$(MAKE) ACTION=$@ $(TARGETS)
+
+install changes :
+ @SYSTEM='$(SYSTEM)'; export SYSTEM; \
+ VERSION='$(VERSION)'; export VERSION; \
+ GROUP='$(GROUP)'; export GROUP; \
+ OWNER='$(OWNER)'; export OWNER; \
+ FONTDIR='$(FONTDIR)'; export FONTDIR; \
+ HOSTDIR='$(HOSTDIR)'; export HOSTDIR; \
+ MAN1DIR='$(MAN1DIR)'; export MAN1DIR; \
+ POSTBIN='$(POSTBIN)'; export POSTBIN; \
+ POSTLIB='$(POSTLIB)'; export POSTLIB; \
+ TMACDIR='$(TMACDIR)'; export TMACDIR; \
+ ROUNDPAGE='$(ROUNDPAGE)'; export ROUNDPAGE; \
+ $(MAKE) -e -f $(MAKEFILE) MAKE=$(MAKE) ACTION=$@ $(TARGETS)
+
+$(TARGETS) ::
+ @TARGETS=; unset TARGETS; \
+ HFILES=; unset HFILES; \
+ OFILES=; unset OFILES; \
+ CFLAGS=; unset CFLAGS; \
+ LDFLAGS=; unset LDFLAGS; \
+ YFLAGS=; unset YFLAGS; \
+ SYSTEM='$(SYSTEM)'; export SYSTEM; \
+ VERSION='$(VERSION)'; export VERSION; \
+ CFLGS='$(CFLGS)'; export CFLGS; \
+ LDFLGS='$(LDFLGS)'; export LDFLGS; \
+ COMMONDIR='../$(COMMONDIR)'; export COMMONDIR; \
+ DKHOST='$(DKHOST)'; export DKHOST; \
+ DKSTREAMS='$(DKSTREAMS)'; export DKSTREAMS; \
+ if [ -d $@ -a -f $@/$@.mk ]; then \
+ cd $@; \
+ echo "---- Making $(ACTION) in directory $(CURRENTDIR)/$@ ----"; \
+ $(MAKE) -e -f $@.mk MAKE=$(MAKE) $(ACTION); \
+ echo; \
+ fi
+
diff --git a/sys/src/cmd/postscript/posttek/README b/sys/src/cmd/postscript/posttek/README
new file mode 100755
index 000000000..f46380669
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/README
@@ -0,0 +1,4 @@
+
+Tektronix 4014 to PostScript translator. Much of the code was
+borrowed from the 5620 Tektronix emulator.
+
diff --git a/sys/src/cmd/postscript/posttek/mkfile b/sys/src/cmd/postscript/posttek/mkfile
new file mode 100755
index 000000000..d5549b2d5
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/mkfile
@@ -0,0 +1,33 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=posttek
+OFILES=posttek.$O\
+
+COMMONDIR=../common
+
+HFILES=posttek.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/ext.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+LIB=$COMMONDIR/com.a$O
+
+</sys/src/cmd/mkone
+
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+install:V: $POSTLIB/posttek.ps
+
+$POSTLIB/posttek.ps : posttek.ps
+ cp $prereq $target
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/posttek/posttek.c b/sys/src/cmd/postscript/posttek/posttek.c
new file mode 100755
index 000000000..a2b1b1fae
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.c
@@ -0,0 +1,1199 @@
+/*
+ *
+ * posttek - PostScript translator for tektronix 4014 files
+ *
+ * A program that can be used to translate tektronix 4014 files into PostScript.
+ * Most of the code was borrowed from the tektronix 4014 emulator that was written
+ * for DMDs. Things have been cleaned up some, but there's still plently that
+ * could be done.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how the program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * v
+ *
+ * mark dx1 dy1 ... dxn dyn x y v mark
+ *
+ * Draws the vector described by the numbers on the stack. The top two
+ * numbers are the starting point. The rest are relative displacements
+ * from the preceeding point. Must make sure we don't put too much on
+ * the stack!
+ *
+ * t
+ *
+ * x y string t -
+ *
+ * Prints the string that's on the top of the stack starting at point
+ * (x, y).
+ *
+ * p
+ *
+ * x y p -
+ *
+ * Marks the point (x, y) with a circle whose radius varies with the
+ * current intensity setting.
+ *
+ * i
+ *
+ * percent focus i -
+ *
+ * Changes the size of the circle used to mark individual points to
+ * percent of maximum for focused mode (focus=1) or defocused mode
+ * (focus=0). The implementation leaves much to be desired!
+ *
+ * l
+ *
+ * mark array l mark
+ *
+ * Set the line drawing mode according to the description given in array.
+ * The arrays that describe the different line styles are declared in
+ * STYLES (file posttek.h). The array really belongs in the prologue!
+ *
+ * w
+ *
+ * n w -
+ *
+ * Adjusts the line width for vector drawing. Used to select normal (n=0)
+ * or defocused (n=1) mode.
+ *
+ * f
+ *
+ * size f -
+ *
+ * Changes the size of the font that's used to print characters in alpha
+ * mode. size is the tektronix character width and is used to choose an
+ * appropriate point size in the current font.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * The default line width is zero, which forces lines to be one pixel wide. That
+ * works well on 'write to black' engines but won't be right for 'write to white'
+ * engines. The line width can be changed using the -w option, or you can change
+ * the initialization of linewidth in the prologue.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable definitions */
+#include "posttek.h" /* control codes and other definitions */
+
+char *optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
+
+char *prologue = POSTTEK; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+int charheight[] = CHARHEIGHT; /* height */
+int charwidth[] = CHARWIDTH; /* and width arrays for tek characters */
+int tekfont = TEKFONT; /* index into charheight[] and charwidth[] */
+
+char intensity[] = INTENSITY; /* special point intensity array */
+char *styles[] = STYLES; /* description of line styles */
+int linestyle = 0; /* index into styles[] */
+int linetype = 0; /* 0 for normal, 1 for defocused */
+
+int dispmode = ALPHA; /* current tektronix state */
+int points = 0; /* points making up the current vector */
+int characters = 0; /* characters waiting to be printed */
+int pen = UP; /* just for point plotting */
+int margin = 0; /* left edge - ALPHA state */
+
+Point cursor; /* should be current cursor position */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+FILE *fp_in; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that can be used to translate tektronix 4014 files into
+ * PostScript. Most of the code was taken from the DMD tektronix 4014 emulator,
+ * although things have been cleaned up some.
+ *
+ */
+
+ argv = agv; /* so everyone can use them */
+ argc = agc;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* nothing could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Make sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* value returned by getopt() */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f': /* use this PostScript font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'w': /* line width */
+ fprintf(stdout, "/linewidth %s def\n", optarg);
+ break;
+
+ case 'x': /* shift horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't know the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind;
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ statemachine(fp_in = stdin);
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ statemachine(fp_in);
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages with a TRAILER
+ * comment, make sure the last page prints, and add things like the PAGES comment
+ * that can only be determined after all the input files have been read.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
+ * is requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+statemachine(fp)
+
+ FILE *fp; /* used to set fp_in */
+
+{
+
+/*
+ *
+ * Controls the translation of the next input file. Tektronix states (dispmode)
+ * are typically changed in control() and esc().
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ formfeed();
+ dispmode = RESET;
+
+ while ( 1 )
+ switch ( dispmode ) {
+ case RESET:
+ reset();
+ break;
+
+ case ALPHA:
+ alpha();
+ break;
+
+ case GIN:
+ gin();
+ break;
+
+ case GRAPH:
+ graph();
+ break;
+
+ case POINT:
+ case SPECIALPOINT:
+ point();
+ break;
+
+ case INCREMENTAL:
+ incremental();
+ break;
+
+ case EXIT:
+ formfeed();
+ return;
+ } /* End switch */
+
+} /* End of statemachine */
+
+/*****************************************************************************/
+
+reset()
+
+{
+
+/*
+ *
+ * Called to reset things, typically only at the beginning of each input file.
+ *
+ */
+
+ tekfont = -1;
+ home();
+ setfont(TEKFONT);
+ setmode(ALPHA);
+
+} /* End of reset */
+
+/*****************************************************************************/
+
+alpha()
+
+{
+
+ int c; /* next character */
+ int x, y; /* cursor will be here when we're done */
+
+/*
+ *
+ * Takes care of printing characters in the current font.
+ *
+ */
+
+ if ( (c = nextchar()) == OUTMODED )
+ return;
+
+ if ( (c < 040) && ((c = control(c)) <= 0) )
+ return;
+
+ x = cursor.x; /* where the cursor is right now */
+ y = cursor.y;
+
+ switch ( c ) {
+ case DEL:
+ return;
+
+ case BS:
+ if ((x -= charwidth[tekfont]) < margin)
+ x = TEKXMAX - charwidth[tekfont];
+ break;
+
+ case NL:
+ y -= charheight[tekfont];
+ break;
+
+ case CR:
+ x = margin;
+ break;
+
+ case VT:
+ if ((y += charheight[tekfont]) >= TEKYMAX)
+ y = 0;
+ break;
+
+ case HT:
+ case ' ':
+ default:
+ if ( characters++ == 0 )
+ fprintf(fp_out, "%d %d (", cursor.x, cursor.y);
+ switch ( c ) {
+ case '(':
+ case ')':
+ case '\\':
+ putc('\\', fp_out);
+
+ default:
+ putc(c, fp_out);
+ } /* End switch */
+ x += charwidth[tekfont];
+ move(x, y);
+ break;
+ } /* End switch */
+
+ if (x >= TEKXMAX) {
+ x = margin;
+ y -= charheight[tekfont];
+ } /* End if */
+
+ if (y < 0) {
+ y = TEKYMAX - charheight[tekfont];
+ x -= margin;
+ margin = (TEKXMAX/2) - margin;
+ if ((x += margin) > TEKXMAX)
+ x -= margin;
+ } /* End if */
+
+ if ( y != cursor.y || x != cursor.x )
+ text();
+
+ move(x, y);
+
+} /* End of alpha */
+
+/*****************************************************************************/
+
+graph()
+
+{
+
+ int c; /* next character */
+ int b; /* for figuring out loy */
+ int x, y; /* next point in the vector */
+ static int hix, hiy; /* upper */
+ static int lox, loy; /* and lower part of the address */
+ static int extra; /* for extended addressing */
+
+/*
+ *
+ * Handles things when we're in GRAPH, POINT, or SPECIALPOINT mode.
+ *
+ */
+
+ if ((c = nextchar()) < 040) {
+ control(c);
+ return;
+ } /* End if */
+
+ if ((c & 0140) == 040) { /* new hiy */
+ hiy = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } /* End if */
+
+ if ((c & 0140) == 0140) { /* new loy */
+ b = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ if ((c & 0140) == 0140) { /* no, it was extra */
+ extra = b;
+ loy = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } else loy = b;
+ } /* End if */
+
+ if ((c & 0140) == 040) { /* new hix */
+ hix = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } /* End if */
+
+ lox = c & 037; /* this should be lox */
+ if (extra & 020)
+ margin = TEKXMAX/2;
+
+ x = (hix<<7) | (lox<<2) | (extra & 03);
+ y = (hiy<<7) | (loy<<2) | ((extra & 014)>>2);
+
+ if ( points > 100 ) { /* don't put too much on the stack */
+ draw();
+ points = 1;
+ } /* End if */
+
+ if ( points++ )
+ fprintf(fp_out, "%d %d\n", cursor.x - x, cursor.y - y);
+
+ move(x, y); /* adjust the cursor */
+
+} /* End of graph */
+
+/*****************************************************************************/
+
+point()
+
+{
+
+ int c; /* next input character */
+
+/*
+ *
+ * Special point mode permits gray scaling by varying the size of the stored
+ * point, which is controlled by an intensity character that preceeds each point
+ * address.
+ *
+ */
+
+ if ( dispmode == SPECIALPOINT ) {
+ if ( (c = nextchar()) < 040 || c > 0175 )
+ return(control(c));
+
+ fprintf(fp_out, "%d %d i\n", intensity[c - ' '], c & 0100);
+ } /* End if */
+
+ graph();
+ draw();
+
+} /* End of point */
+
+/*****************************************************************************/
+
+incremental()
+
+{
+
+ int c; /* for the next few characters */
+ int x, y; /* cursor position when we're done */
+
+/*
+ *
+ * Handles incremental plot mode. It's entered after the RS control code and is
+ * used to mark points relative to our current position. It's typically followed
+ * by one or two bytes that set the pen state and are used to increment the
+ * current position.
+ *
+ */
+
+ if ( (c = nextchar()) == OUTMODED )
+ return;
+
+ if ( (c < 040) && ((c = control(c)) <= 0) )
+ return;
+
+ x = cursor.x; /* where we are right now */
+ y = cursor.y;
+
+ if ( c & 060 )
+ pen = ( c & 040 ) ? UP : DOWN;
+
+ if ( c & 04 ) y++;
+ if ( c & 010 ) y--;
+ if ( c & 01 ) x++;
+ if ( c & 02 ) x--;
+
+ move(x, y);
+
+ if ( pen == DOWN ) {
+ points = 1;
+ draw();
+ } /* End if */
+
+} /* End of incremental */
+
+/*****************************************************************************/
+
+gin()
+
+{
+
+/*
+ *
+ * All we really have to do for GIN mode is make sure it's properly ended.
+ *
+ */
+
+ control(nextchar());
+
+} /* End of gin */
+
+/*****************************************************************************/
+
+control(c)
+
+ int c; /* check this control character */
+
+{
+
+/*
+ *
+ * Checks character c and does special things, like mode changes, that depend
+ * not only on the character, but also on the current state. If the mode changed
+ * becuase of c, OUTMODED is returned to the caller. In all other cases the
+ * return value is c or 0, if c doesn't make sense in the current mode.
+ *
+ */
+
+ switch ( c ) {
+ case BEL:
+ return(0);
+
+ case BS:
+ case HT:
+ case VT:
+ return(dispmode == ALPHA ? c : 0);
+
+ case CR:
+ if ( dispmode != ALPHA ) {
+ setmode(ALPHA);
+ ungetc(c, fp_in);
+ return(OUTMODED);
+ } else return(c);
+
+ case FS:
+ if ( (dispmode == ALPHA) || (dispmode == GRAPH) ) {
+ setmode(POINT);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case GS:
+ if ( (dispmode == ALPHA) || (dispmode == GRAPH) ) {
+ setmode(GRAPH);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case NL:
+ ungetc(CR, fp_in);
+ return(dispmode == ALPHA ? c : 0);
+
+ case RS:
+ if ( dispmode != GIN ) {
+ setmode(INCREMENTAL);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case US:
+ if ( dispmode == ALPHA )
+ return(0);
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case ESC:
+ return(esc());
+
+ case OUTMODED:
+ return(c);
+
+ default:
+ return(c < 040 ? 0 : c);
+ } /* End switch */
+
+} /* End of control */
+
+/*****************************************************************************/
+
+esc()
+
+{
+
+ int c; /* next input character */
+ int ignore; /* skip it if nonzero */
+
+/*
+ *
+ * Handles tektronix escape code. Called from control() whenever an ESC character
+ * is found in the input file.
+ *
+ */
+
+ do {
+ c = nextchar();
+ ignore = 0;
+ switch ( c ) {
+ case CAN:
+ return(0);
+
+ case CR:
+ ignore = 1;
+ break;
+
+ case ENQ:
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case ETB:
+ return(0);
+
+ case FF:
+ formfeed();
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case FS:
+ if ( (dispmode == INCREMENTAL) || ( dispmode == GIN) )
+ return(0);
+ setmode(SPECIALPOINT);
+ return(OUTMODED);
+
+ case SI:
+ case SO:
+ return(0);
+
+ case SUB:
+ setmode(GIN);
+ return(OUTMODED);
+
+ case OUTMODED:
+ return(OUTMODED);
+
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ setfont(c - '8');
+ return(0);
+
+ default:
+ if ( c == '?' && dispmode == GRAPH )
+ return(DEL);
+ if ( (c<'`') || (c>'w') )
+ break;
+ c -= '`';
+ if ( (c & 010) != linetype )
+ fprintf(fp_out, "%d w\n", (linetype = (c & 010))/010);
+ if ( ((c + 1) & 7) >= 6 )
+ break;
+ if ( (c + 1) & 7 )
+ if ( (c & 7) != linestyle ) {
+ linestyle = c & 7;
+ setmode(dispmode);
+ fprintf(fp_out, "%s l\n", styles[linestyle]);
+ } /* End if */
+ return(0);
+ } /* End switch */
+
+ } while (ignore);
+
+ return(0);
+
+} /* End of esc */
+
+/*****************************************************************************/
+
+move(x, y)
+
+ int x, y; /* move the cursor here */
+
+{
+
+/*
+ *
+ * Moves the cursor to the point (x, y).
+ *
+ */
+
+ cursor.x = x;
+ cursor.y = y;
+
+} /* End of move */
+
+/*****************************************************************************/
+
+setmode(mode)
+
+ int mode; /* this should be the new mode */
+
+{
+
+/*
+ *
+ * Makes sure the current mode is properly ended and then sets dispmode to mode.
+ *
+ */
+
+ switch ( dispmode ) {
+ case ALPHA:
+ text();
+ break;
+
+ case GRAPH:
+ draw();
+ break;
+
+ case INCREMENTAL:
+ pen = UP;
+ break;
+ } /* End switch */
+
+ dispmode = mode;
+
+} /* End of setmode */
+
+/*****************************************************************************/
+
+home()
+
+{
+
+/*
+ *
+ * Makes sure the cursor is positioned at the upper left corner of the page.
+ *
+ */
+
+ margin = 0;
+ move(0, TEKYMAX);
+
+} /* End of home */
+
+/*****************************************************************************/
+
+setfont(newfont)
+
+ int newfont; /* use this font next */
+
+{
+
+/*
+ *
+ * Generates the call to the procedure that's responsible for changing the
+ * tektronix font (really just the size).
+ *
+ */
+
+ if ( newfont != tekfont ) {
+ setmode(dispmode);
+ fprintf(fp_out, "%d f\n", charwidth[newfont]);
+ } /* End if */
+
+ tekfont = newfont;
+
+} /* End of setfont */
+
+/*****************************************************************************/
+
+text()
+
+{
+
+/*
+ *
+ * Makes sure any text we've put on the stack is printed.
+ *
+ */
+
+ if ( dispmode == ALPHA && characters > 0 )
+ fprintf(fp_out, ") t\n");
+
+ characters = 0;
+
+} /* End of text */
+
+/*****************************************************************************/
+
+draw()
+
+{
+
+/*
+ *
+ * Called whenever we need to draw a vector or plot a point. Nothing will be
+ * done if points is 0 or if it's 1 and we're in GRAPH mode.
+ *
+ */
+
+ if ( points > 1 ) /* it's a vector */
+ fprintf(fp_out, "%d %d v\n", cursor.x, cursor.y);
+ else if ( points == 1 && dispmode != GRAPH )
+ fprintf(fp_out, "%d %d p\n", cursor.x, cursor.y);
+
+ points = 0;
+
+} /* End of draw */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+/*
+ *
+ * Usually called when we've finished the last page and want to get ready for the
+ * next one. Also used at the beginning and end of each input file, so we have to
+ * be careful about exactly what's done.
+ *
+ */
+
+ setmode(dispmode); /* end any outstanding text or graphics */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ fprintf(fp_out, "cleartomark\n");
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+ fprintf(fp_out, "%d f\n", charwidth[tekfont]);
+ fprintf(fp_out, "%s l\n", styles[linestyle]);
+
+ home();
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+nextchar()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Reads the next character from the current input file and returns it to the
+ * caller. When we're finished with the file dispmode is set to EXIT and OUTMODED
+ * is returned to the caller.
+ *
+ */
+
+ if ( (ch = getc(fp_in)) == EOF ) {
+ setmode(EXIT);
+ ch = OUTMODED;
+ } /* End if */
+
+ return(ch);
+
+} /* End of nextchar */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/posttek/posttek.h b/sys/src/cmd/postscript/posttek/posttek.h
new file mode 100755
index 000000000..99d10133c
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * Tektronix 4014 control codes.
+ *
+ */
+
+#define NUL '\000'
+#define SOH '\001'
+#define STX '\002'
+#define ETX '\003'
+#define EOT '\004'
+#define ENQ '\005'
+#define ACK '\006'
+#define BEL '\007'
+#define BS '\010'
+#define HT '\011'
+#define NL '\012'
+#define VT '\013'
+#define FF '\014'
+#define CR '\015'
+#define SO '\016'
+#define SI '\017'
+#define DLE '\020'
+#define DC1 '\021'
+#define DC2 '\022'
+#define DC3 '\023'
+#define DC4 '\024'
+#define NAK '\025'
+#define SYN '\026'
+#define ETB '\027'
+#define CAN '\030'
+#define EM '\031'
+#define SUB '\032'
+#define ESC '\033'
+#define FS '\034'
+#define GS '\035'
+#define RS '\036'
+#define US '\037'
+#define DEL '\177'
+
+/*
+ *
+ * A few definitions used to classify the different tektronix states. OUTMODED
+ * is returned by control() and esc(), and typically means the state has changed.
+ *
+ */
+
+#define OUTMODED -1
+#define ALPHA 0
+#define GIN 1
+#define GRAPH 2
+#define POINT 3
+#define SPECIALPOINT 4
+#define INCREMENTAL 5
+#define RESET 6
+#define EXIT 7
+
+/*
+ *
+ * The pen state, either UP or DOWN, controls whether vectors are drawn.
+ *
+ */
+
+#define UP 0
+#define DOWN 1
+
+/*
+ *
+ * Coordinates of the upper right corner of the screen - almost the real screen
+ * dimensions.
+ *
+ */
+
+#define TEKXMAX 4096
+#define TEKYMAX 3120
+
+/*
+ *
+ * The size of the spot in SPECIALPOINT mode is controlled by a non-linear
+ * function that has a domain that consists of the integers from 040 to 0175.
+ * The next definition is used to initialize the special point mode intensity
+ * array that implements the function. Data came from table F-6 in the tektronix
+ * 4014 manual.
+ *
+ */
+
+#define INTENSITY \
+ \
+ { \
+ 14, 16, 17, 19, 20, 22, 23, 25, \
+ 28, 31, 34, 38, 41, 44, 47, 50, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 0, 1, 1, 1, 1, 1, 1, 2, \
+ 2, 2, 2, 2, 3, 3, 3, 3, \
+ 4, 4, 4, 5, 5, 5, 6, 6, \
+ 7, 8, 9, 10, 11, 12, 12, 13, \
+ 14, 16, 17, 19, 20, 22, 23, 25, \
+ 28, 31, 34, 38, 41, 44, 47, 50, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ }
+
+/*
+ *
+ * The next two definitions give the height and width of characters in the four
+ * different sizes available on tektronix terminals. TEKFONT is the default index
+ * into CHARHEIGHT and CHARWIDTH.
+ *
+ */
+
+#define CHARHEIGHT {88, 82, 53, 48}
+#define CHARWIDTH {56, 51, 34, 31}
+#define TEKFONT 2
+
+/*
+ *
+ * The entries defined in STYLES are passed on to the PostScript operator setdash.
+ * They're used to implement the different tektronix line styles. Belongs in the
+ * prologue!
+ *
+ */
+
+#define STYLES \
+ \
+ { \
+ "[]", \
+ "[.5 2]", \
+ "[.5 2 4 2]", \
+ "[4 4]", \
+ "[8 4]", \
+ "[]" \
+ }
+
+/*
+ *
+ * Variables of type Point are used to keep track of the cursor position.
+ *
+ */
+
+typedef struct {
+ int x;
+ int y;
+} Point;
+
+/*
+ *
+ * An array of type Fontmap helps convert font names requested by users into
+ * legitimate PostScript names. The array is initialized using FONTMAP, which must
+ * end with an entry that has NULL defined as its name field.
+ *
+ */
+
+typedef struct {
+ char *name; /* user's font name */
+ char *val; /* corresponding PostScript name */
+} Fontmap;
+
+#define FONTMAP \
+ \
+ { \
+ "R", "Courier", \
+ "I", "Courier-Oblique", \
+ "B", "Courier-Bold", \
+ "CO", "Courier", \
+ "CI", "Courier-Oblique", \
+ "CB", "Courier-Bold", \
+ "CW", "Courier", \
+ "PO", "Courier", \
+ "courier", "Courier", \
+ "cour", "Courier", \
+ "co", "Courier", \
+ NULL, NULL \
+ }
+
+/*
+ *
+ * Some of the non-integer valued functions in posttek.c.
+ *
+ */
+
+char *get_font();
+
diff --git a/sys/src/cmd/postscript/posttek/posttek.ps b/sys/src/cmd/postscript/posttek/posttek.ps
new file mode 100755
index 000000000..ee2428ced
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.ps
@@ -0,0 +1,106 @@
+%
+% Version 3.3.2 prologue for tektronix 4014 files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/fixlinewidth true def
+/font /Courier def
+/formsperpage 1 def
+/landscape true def
+/linewidth 0 def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/rotation 1 def
+/screenheight 3120 def
+/screenwidth 4150 def
+/spotsize 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/kshow {kshow} bind def % so later references don't bind
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ /scaling
+ height margin sub screenheight div
+ width margin sub screenwidth div
+ min def
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ magnification dup aspectratio mul scale
+ scaling scaling scale
+ screenwidth 2 div neg screenheight 2 div neg translate
+
+ tietodevicespace
+ linewidth scaling div setlinewidth
+ 1 setlinecap
+ newpath
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/tietodevicespace {
+ fixlinewidth linewidth 0 gt and linewidth 1 lt and {
+ /moveto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ moveto
+ } bind def
+ /lineto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ lineto
+ } bind def
+ /rlineto {Y add exch X add exch lineto} bind def
+ /v V 0 get bind def
+ } if
+} def
+
+/V [{moveto counttomark 2 idiv {rlineto} repeat stroke}] def
+/v V 0 get bind def
+/p {newpath spotsize 0 360 arc fill} bind def
+
+/l {{scaling div} forall counttomark array astore 0 setdash} bind def
+/w {linewidth 0 eq {.3} {linewidth} ifelse mul linewidth add scaling div setlinewidth} bind def
+/i {3 mul 4 sub -100 div mul .5 add /spotsize exch def} bind def
+
+/f {/charwidth exch def font findfont charwidth .6 div scalefont setfont} bind def
+
+/t {
+ 3 1 roll moveto
+ currentpoint {
+ pop pop
+ exch charwidth add exch
+ moveto currentpoint
+ } 4 -1 roll kshow
+ pop pop
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def
diff --git a/sys/src/cmd/postscript/printfont/mkfile b/sys/src/cmd/postscript/printfont/mkfile
new file mode 100755
index 000000000..8ddadb25f
--- /dev/null
+++ b/sys/src/cmd/postscript/printfont/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+
+<../config
+
+POSTBIN=/rc/bin
+
+all:V: printfont
+
+install installall:V: $POSTBIN/printfont $POSTLIB/printfont.ps
+
+clean nuke:V:
+ rm -f printfont
+
+$POSTBIN/printfont: printfont
+ cp $prereq $target
+
+$POSTLIB/printfont.ps: printfont.ps
+ cp $prereq $target
+
+printfont: printfont.rc
+ sed 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' printfont.rc >printfont
+ chmod 775 printfont
diff --git a/sys/src/cmd/postscript/printfont/printfont.ps b/sys/src/cmd/postscript/printfont/printfont.ps
new file mode 100755
index 000000000..7a881a4d4
--- /dev/null
+++ b/sys/src/cmd/postscript/printfont/printfont.ps
@@ -0,0 +1,321 @@
+%
+% Formatted font dump. Assumes all fonts include valid FontBBox arrays.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/landscape false def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/rotation 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/axescount 0 def
+/charwidth false def
+/graynotdef 0.85 def
+/hireslinewidth 0.2 def
+/longnames false def
+/maxsize 6.0 def
+/minsize 4.5 def
+/numbercell true def
+/radix 16 def
+/labelfont /Helvetica def
+/labelspace 36 def
+/zerocell 0 def
+
+/roundpage true def
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} def
+/min {2 copy gt {exch} if pop} def
+/max {2 copy lt {exch} if pop} def
+
+/LLx {0 get} bind def
+/LLy {1 get} bind def
+/URx {2 get} bind def
+/URy {3 get} bind def
+/BBoxHeight {dup URy exch LLy sub} bind def
+/BBoxWidth {dup URx exch LLx sub} bind def
+
+/setup {
+ /graylevels [1 0 0] def
+ /scratchstring 512 string def
+ /Product statusdict begin /product where {pop product}{(Unknown)} ifelse end def
+ /Resolution 0 72 dtransform dup mul exch dup mul add sqrt cvi def
+ /Version /version where {pop version}{(???)} ifelse def
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ width 2 div neg height 2 div translate
+ xoffset inch yoffset inch neg translate
+ margin dup neg translate
+ 0 labelspace .75 mul neg translate
+ magnification dup aspectratio mul scale
+ 0 0 transform round exch round exch itransform translate
+
+ currentdict /linewidth known not {
+ /linewidth Resolution 400 le {0}{hireslinewidth} ifelse def
+ } if
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ roundpage currentdict /roundpagebbox known and {roundpagebbox} if
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+} def
+
+/CharSetup {
+ /chcode exch def
+ /chname Encoding chcode get def
+ /chstring ( ) dup 0 chcode put def
+ /chknown true def
+
+ graylevels 0 1 put % initial cell fill
+ graylevels 1 0 put % cell text
+ graylevels 2 0 put % cell border
+
+ FontDict /CharStrings known {
+ FontDict /CharStrings get chname known not {
+ /chknown false def
+ graylevels 0 0 put
+ graylevels 1 1 put
+ } if
+ } if
+
+ chname /.notdef eq {
+ /chknown false def
+ graylevels 0 graynotdef put
+ graylevels 1 graynotdef put
+ } if
+
+ /chwid chknown
+ {FontDict 1 scalefont setfont chstring stringwidth pop}
+ {0}
+ ifelse def
+} bind def
+
+/CellSetup {
+ /gridwidth width margin 2 mul sub def
+ /gridheight height labelspace sub margin 2 mul sub def
+ /cellwidth gridwidth radix div def
+ /cellheight gridheight Entries radix div ceiling div def
+
+ cellwidth cellheight dtransform truncate exch truncate exch idtransform
+ /cellheight exch def
+ /cellwidth exch def
+
+ labelfont findfont 1 scalefont setfont
+ /LabelBBox currentfont /FontBBox get TransformBBox def
+
+ LabelBBox 2 0 Encoding {
+ scratchstring cvs stringwidth pop
+ 2 copy lt {exch} if
+ pop
+ } forall put
+
+ /CellLabelSize
+ cellheight .20 mul cellwidth .90 mul LabelBBox BestFit
+ minsize max
+ maxsize min
+ def
+ zerocell CellOrigin cellheight add neg exch neg exch translate
+} bind def
+
+/FontSetup {
+ FontName findfont 1 scalefont setfont
+ /BBox currentfont /FontBBox get TransformBBox def
+ /PointSize cellheight .5 mul cellwidth .8 mul BBox BestFit def
+ BBox {PointSize mul} forall BBox astore pop
+
+ /xorigin cellwidth BBox BBoxWidth sub 2 div BBox LLx sub def
+ /yorigin cellheight BBox BBoxHeight sub 2 div BBox LLy sub def
+} bind def
+
+/BestFit {
+ /bbox exch def
+ bbox BBoxWidth div exch
+ bbox BBoxHeight div min
+} bind def
+
+/TransformBBox { % font bbox to user space
+ aload pop
+ currentfont /FontMatrix get dtransform 4 2 roll
+ currentfont /FontMatrix get dtransform 4 2 roll
+ 4 array astore % should build user space bbox if all zeros
+} bind def
+
+/CellOrigin {
+ dup
+ exch radix mod cellwidth mul
+ exch radix idiv 1 add neg cellheight mul
+} bind def
+
+/CellOutline {
+ newpath
+ CellOrigin moveto
+ cellwidth 0 rlineto
+ 0 cellheight rlineto
+ cellwidth neg 0 rlineto
+ closepath
+} bind def
+
+/LabelCell {
+ gsave
+ chcode CellOrigin translate
+ linewidth .5 mul setlinewidth
+ labelfont findfont CellLabelSize scalefont setfont
+
+ numbercell {
+ cellwidth .025 mul cellheight .05 mul moveto
+ chcode radix scratchstring cvrs show
+ } if
+
+ charwidth chknown and {
+ /wid chwid 0.0005 add scratchstring cvs 0 5 getinterval def
+ cellwidth wid stringwidth pop 1.10 mul sub cellheight .05 mul moveto
+ wid show
+ } if
+
+ longnames chknown not or {
+ cellwidth .025 mul
+ cellheight LabelBBox URy CellLabelSize mul sub .05 sub moveto
+ Encoding chcode get scratchstring cvs show
+ } if
+
+ axescount 1 ge chknown and { % gsave/grestore if not last
+ newpath
+ xorigin yorigin translate
+
+ BBox LLx 0 moveto % baseline
+ BBox URx 0 lineto stroke
+
+ axescount 2 ge { % vertical through current origin
+ 0 BBox LLy moveto
+ 0 BBox URy lineto stroke
+ } if
+
+ axescount 3 ge { % vertical through next origin
+ chwid PointSize mul BBox LLy
+ dtransform round exch round exch idtransform moveto
+ 0 BBox BBoxHeight rlineto stroke
+ %chwid PointSize mul BBox URy lineto stroke
+ } if
+ } if
+ grestore
+} bind def
+
+/PlaceChar {
+ FontName findfont PointSize scalefont setfont
+ chcode CellOrigin moveto
+ xorigin yorigin rmoveto
+ ( ) dup 0 chcode put show
+} bind def
+
+/LabelPage {
+ labelfont findfont labelspace .75 mul .75 mul 18 min scalefont setfont
+ 0 labelspace .75 mul .25 mul moveto
+ FontName scratchstring cvs show
+
+ labelfont findfont labelspace .25 mul .75 mul 9 min scalefont setfont
+ 0 gridheight neg moveto
+ 0 labelspace .25 mul .75 mul neg rmoveto
+ Product show ( Version ) show Version show
+ ( \() show Resolution scratchstring cvs show (dpi\)) show
+
+ gridwidth gridheight neg moveto
+ 0 labelspace .25 mul .75 mul neg rmoveto
+ (size=, ) stringwidth pop neg 0 rmoveto
+ PointSize cvi scratchstring cvs stringwidth pop neg 0 rmoveto
+ (gray=, ) stringwidth pop neg 0 rmoveto
+ graynotdef scratchstring cvs stringwidth pop neg 0 rmoveto
+ (linewidth=) stringwidth pop neg 0 rmoveto
+ linewidth scratchstring cvs stringwidth pop neg 0 rmoveto
+ (size=) show PointSize cvi scratchstring cvs show (, ) show
+ (gray=) show graynotdef scratchstring cvs show (, ) show
+ (linewidth=) show linewidth scratchstring cvs show
+} bind def
+
+%
+% Formatted dump of the encoded characters in a single font.
+%
+
+/PrintFont {
+ /saveobj save def
+ /FontName exch def
+ /FontDict FontName findfont def
+ /Encoding FontDict /Encoding get def
+ /Entries Encoding length def
+
+ CellSetup
+ FontSetup
+ LabelPage
+ zerocell 1 Entries 1 sub {
+ CharSetup
+ graylevels 0 get setgray
+ chcode CellOutline fill
+ graylevels 1 get setgray
+ LabelCell
+ PlaceChar
+ graylevels 2 get setgray
+ linewidth setlinewidth
+ chcode CellOutline stroke
+ } for
+ showpage
+ saveobj restore
+} bind def
+
+%
+% Dump of all ROM and disk fonts - in alphabetical order.
+%
+
+/AllFonts {
+ /AllFontNames FontDirectory maxlength array def
+ AllFontNames 0 0 put
+
+ FontDirectory {pop AllFontNames Insert} forall
+
+ /filenameforall where {
+ pop
+ (fonts/*)
+ {(fonts/) search pop pop pop AllFontNames Insert}
+ 200 string
+ filenameforall
+ } if
+
+ 1 1 AllFontNames 0 get {
+ AllFontNames exch get cvn PrintFont
+ } for
+} bind def
+
+/Insert { % name in a sorted list
+ /List exch def
+ /Name exch 128 string cvs def
+
+ /Slot 1 def
+ List 0 get {
+ Name List Slot get le {exit} if
+ /Slot Slot 1 add def
+ } repeat
+
+ List 0 get -1 Slot {
+ dup List exch get
+ List 3 1 roll exch 1 add exch put
+ } for
+ List Slot Name put
+ List 0 List 0 get 1 add put
+} bind def
+
diff --git a/sys/src/cmd/postscript/printfont/printfont.rc b/sys/src/cmd/postscript/printfont/printfont.rc
new file mode 100755
index 000000000..266102f9d
--- /dev/null
+++ b/sys/src/cmd/postscript/printfont/printfont.rc
@@ -0,0 +1,105 @@
+#!/bin/rc
+# Formatted dump of encoded characters in one or more PostScript fonts.
+# Arguments should be PostScript font names or the word all, which dumps
+# all ROM and disk based fonts.
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+PROLOGUE=$POSTLIB/printfont.ps
+
+OPTIONS=''
+COPYFILE=''
+MODE=portrait
+FONTENCODING=Default
+
+NONCONFORMING='%!PS'
+ENDPROLOG='%%EndProlog'
+BEGINSETUP='%%BeginSetup'
+ENDSETUP='%%EndSetup'
+TRAILER='%%Trailer'
+
+SETUP=setup
+
+while (! ~ $#* 0 && ~ $1 -*) {
+ switch ($1) {
+ case -a; shift; OPTIONS=$OPTIONS' /axescount $1 def'
+ case -a*; OPTIONS=$OPTIONS' /axescount '`{echo $1 | sed s/-a//}' def'
+
+ case -b; shift; OPTIONS=$OPTIONS' /radix '$1' def'
+ case -b*; OPTIONS=$OPTIONS' /radix '`{echo $1 | sed s/-b//}' def'
+
+ case -c; shift; OPTIONS=$OPTIONS' /#copies '$1' store'
+ case -c*; OPTIONS=$OPTIONS' /#copies '`{echo $1 | sed s/-c//}' store'
+
+ case -f; shift; OPTIONS=$OPTIONS' /labelfont /'$1' def'
+ case -f*; OPTIONS=$OPTIONS' /labelfont /'`{echo $1 | sed s/-f//}' def'
+
+ case -g; shift; OPTIONS=$OPTIONS' /graynotdef '$1' def'
+ case -g*; OPTIONS=$OPTIONS' /graynotdef '`{echo $1 | sed s/-g//}' def'
+
+ case -p; shift; MODE=$1
+ case -p*; MODE=`{echo $1 | sed s/-p//}
+
+ case -q; OPTIONS=$OPTIONS' /longnames false def /charwidth false def'
+
+ case -m; shift; OPTIONS=$OPTIONS' /magnification '$1' def'
+ case -m*; OPTIONS=$OPTIONS' /magnification '`{echo $1 | sed s/-m//}' def'
+
+ case -v; OPTIONS=$OPTIONS' /longnames true def /charwidth true def'
+
+ case -w; shift; OPTIONS=$OPTIONS' /linewidth '$1' def'
+ case -w*; OPTIONS=$OPTIONS' /linewidth '`{echo $1 | sed s/-w//}' def'
+
+ case -x; shift; OPTIONS=$OPTIONS' /xoffset '$1' def'
+ case -x*; OPTIONS=$OPTIONS' /xoffset '`{echo $1 | sed s/-x//}' def'
+
+ case -y; shift; OPTIONS=$OPTIONS' /yoffset '$1' def'
+ case -y*; OPTIONS=$OPTIONS' /yoffset '`{echo $1 | sed s/-y//}' def'
+
+ case -z; shift; OPTIONS=$OPTIONS' /zerocell '$1' def'
+ case -z*; OPTIONS=$OPTIONS' /zerocell '`{echo $1 | sed s/-z//}' def'
+
+ case -C; shift; COPYFILE=$COPYFILE' '$1
+ case -C*; COPYFILE=$COPYFILE' '`{echo $1 | sed s/-C//}
+
+ case -E; shift; FONTENCODING=$1
+ case -E*; FONTENCODING=`{echo $1 | sed s/-E//}
+
+ case -L; shift; PROLOGUE=$1
+ case -L*; PROLOGUE=`{echo $1 | sed s/-L//}
+
+ case -*; echo $0:' illegal option '$1 >[1=2]; exit 1
+ }
+ shift
+}
+
+switch ($MODE) {
+case l*; OPTIONS=$OPTIONS' /landscape true def'
+case *; OPTIONS=$OPTIONS' /landscape false def'
+}
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo $ENDPROLOG
+echo $BEGINSETUP
+if (~ $#COPYFILE 0 || ~ $COPYFILE '') COPYFILE=/dev/null
+cat $COPYFILE
+echo $OPTIONS
+
+switch ($FONTENCODING) {
+case /*; cat $FONTENCODING
+case ?*; cat $POSTLIB^/$FONTENCODING^.enc >[2]/dev/null
+}
+
+echo $SETUP
+echo $ENDSETUP
+
+for (i) {
+ switch ($i) {
+ case all; echo AllFonts
+ case /*; echo $i' PrintFont'
+ case ?*; echo /$i' PrintFont'
+ }
+}
+
+echo $TRAILER
diff --git a/sys/src/cmd/postscript/psencoding/Latin1.enc b/sys/src/cmd/postscript/psencoding/Latin1.enc
new file mode 100755
index 000000000..1e6e60b14
--- /dev/null
+++ b/sys/src/cmd/postscript/psencoding/Latin1.enc
@@ -0,0 +1,299 @@
+%
+% Encoding vector and redefinition of findfont for the ISO Latin1 standard.
+% The 18 characters missing from ROM based fonts on older printers are noted
+% below.
+%
+
+/ISOLatin1Encoding [
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /space
+ /exclam
+ /quotedbl
+ /numbersign
+ /dollar
+ /percent
+ /ampersand
+ /quoteright
+ /parenleft
+ /parenright
+ /asterisk
+ /plus
+ /comma
+ /minus
+ /period
+ /slash
+ /zero
+ /one
+ /two
+ /three
+ /four
+ /five
+ /six
+ /seven
+ /eight
+ /nine
+ /colon
+ /semicolon
+ /less
+ /equal
+ /greater
+ /question
+ /at
+ /A
+ /B
+ /C
+ /D
+ /E
+ /F
+ /G
+ /H
+ /I
+ /J
+ /K
+ /L
+ /M
+ /N
+ /O
+ /P
+ /Q
+ /R
+ /S
+ /T
+ /U
+ /V
+ /W
+ /X
+ /Y
+ /Z
+ /bracketleft
+ /backslash
+ /bracketright
+ /asciicircum
+ /underscore
+ /quoteleft
+ /a
+ /b
+ /c
+ /d
+ /e
+ /f
+ /g
+ /h
+ /i
+ /j
+ /k
+ /l
+ /m
+ /n
+ /o
+ /p
+ /q
+ /r
+ /s
+ /t
+ /u
+ /v
+ /w
+ /x
+ /y
+ /z
+ /braceleft
+ /bar
+ /braceright
+ /asciitilde
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /dotlessi
+ /grave
+ /acute
+ /circumflex
+ /tilde
+ /macron
+ /breve
+ /dotaccent
+ /dieresis
+ /.notdef
+ /ring
+ /cedilla
+ /.notdef
+ /hungarumlaut
+ /ogonek
+ /caron
+ /space
+ /exclamdown
+ /cent
+ /sterling
+ /currency
+ /yen
+ /brokenbar % missing
+ /section
+ /dieresis
+ /copyright
+ /ordfeminine
+ /guillemotleft
+ /logicalnot
+ /hyphen
+ /registered
+ /macron
+ /degree % missing
+ /plusminus % missing
+ /twosuperior % missing
+ /threesuperior % missing
+ /acute
+ /mu % missing
+ /paragraph
+ /periodcentered
+ /cedilla
+ /onesuperior % missing
+ /ordmasculine
+ /guillemotright
+ /onequarter % missing
+ /onehalf % missing
+ /threequarters % missing
+ /questiondown
+ /Agrave
+ /Aacute
+ /Acircumflex
+ /Atilde
+ /Adieresis
+ /Aring
+ /AE
+ /Ccedilla
+ /Egrave
+ /Eacute
+ /Ecircumflex
+ /Edieresis
+ /Igrave
+ /Iacute
+ /Icircumflex
+ /Idieresis
+ /Eth % missing
+ /Ntilde
+ /Ograve
+ /Oacute
+ /Ocircumflex
+ /Otilde
+ /Odieresis
+ /multiply % missing
+ /Oslash
+ /Ugrave
+ /Uacute
+ /Ucircumflex
+ /Udieresis
+ /Yacute % missing
+ /Thorn % missing
+ /germandbls
+ /agrave
+ /aacute
+ /acircumflex
+ /atilde
+ /adieresis
+ /aring
+ /ae
+ /ccedilla
+ /egrave
+ /eacute
+ /ecircumflex
+ /edieresis
+ /igrave
+ /iacute
+ /icircumflex
+ /idieresis
+ /eth % missing
+ /ntilde
+ /ograve
+ /oacute
+ /ocircumflex
+ /otilde
+ /odieresis
+ /divide % missing
+ /oslash
+ /ugrave
+ /uacute
+ /ucircumflex
+ /udieresis
+ /yacute % missing
+ /thorn % missing
+ /ydieresis
+] def
+
+/NewFontDirectory FontDirectory maxlength dict def
+
+%
+% Apparently no guarantee findfont is defined in systemdict so the obvious
+%
+% systemdict /findfont get exec
+%
+% can generate an error. So far the only exception is a VT600 (version 48.0).
+%
+
+userdict /@RealFindfont known not {
+ userdict begin
+ /@RealFindfont systemdict begin /findfont load end def
+ end
+} if
+
+/findfont {
+ dup NewFontDirectory exch known not {
+ dup
+ %dup systemdict /findfont get exec % not always in systemdict
+ dup userdict /@RealFindfont get exec
+ dup /Encoding get StandardEncoding eq {
+ dup length dict begin
+ {1 index /FID ne {def}{pop pop} ifelse} forall
+ /Encoding ISOLatin1Encoding def
+ currentdict
+ end
+ /DummyFontName exch definefont
+ } if
+ NewFontDirectory 3 1 roll put
+ } if
+ NewFontDirectory exch get
+} bind def
+
diff --git a/sys/src/cmd/postscript/psencoding/UTF.enc b/sys/src/cmd/postscript/psencoding/UTF.enc
new file mode 100755
index 000000000..234764e34
--- /dev/null
+++ b/sys/src/cmd/postscript/psencoding/UTF.enc
@@ -0,0 +1,332 @@
+%
+% Encoding vector, operator and procedure redefinitions for Plan 9 UTF
+% encoding. Prologues are expected to take steps to ensure operator
+% redefinitions given here are actually used. Current implementation
+% assumes UTF byte streams that represent ASCII or Latin1 text.
+%
+
+/UTFLatin1Encoding [
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /space
+ /exclam
+ /quotedbl
+ /numbersign
+ /dollar
+ /percent
+ /ampersand
+ /quoteright
+ /parenleft
+ /parenright
+ /asterisk
+ /plus
+ /comma
+ /minus
+ /period
+ /slash
+ /zero
+ /one
+ /two
+ /three
+ /four
+ /five
+ /six
+ /seven
+ /eight
+ /nine
+ /colon
+ /semicolon
+ /less
+ /equal
+ /greater
+ /question
+ /at
+ /A
+ /B
+ /C
+ /D
+ /E
+ /F
+ /G
+ /H
+ /I
+ /J
+ /K
+ /L
+ /M
+ /N
+ /O
+ /P
+ /Q
+ /R
+ /S
+ /T
+ /U
+ /V
+ /W
+ /X
+ /Y
+ /Z
+ /bracketleft
+ /backslash
+ /bracketright
+ /asciicircum
+ /underscore
+ /quoteleft
+ /a
+ /b
+ /c
+ /d
+ /e
+ /f
+ /g
+ /h
+ /i
+ /j
+ /k
+ /l
+ /m
+ /n
+ /o
+ /p
+ /q
+ /r
+ /s
+ /t
+ /u
+ /v
+ /w
+ /x
+ /y
+ /z
+ /braceleft
+ /bar
+ /braceright
+ /asciitilde
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /dotlessi
+ /grave
+ /acute
+ /circumflex
+ /tilde
+ /macron
+ /breve
+ /dotaccent
+ /dieresis
+ /.notdef
+ /ring
+ /cedilla
+ /.notdef
+ /hungarumlaut
+ /ogonek
+ /caron
+ /.notdef % was space
+ /exclamdown
+ /cent
+ /sterling
+ /currency
+ /yen
+ /brokenbar
+ /section
+ /dieresis
+ /copyright
+ /ordfeminine
+ /guillemotleft
+ /logicalnot
+ /hyphen
+ /registered
+ /macron
+ /degree
+ /plusminus
+ /twosuperior
+ /threesuperior
+ /acute
+ /mu
+ /paragraph
+ /periodcentered
+ /cedilla
+ /onesuperior
+ /ordmasculine
+ /guillemotright
+ /onequarter
+ /onehalf
+ /threequarters
+ /questiondown
+ /Agrave
+ /Aacute
+ /Acircumflex
+ /Atilde
+ /Adieresis
+ /Aring
+ /AE
+ /Ccedilla
+ /Egrave
+ /Eacute
+ /Ecircumflex
+ /Edieresis
+ /Igrave
+ /Iacute
+ /Icircumflex
+ /Idieresis
+ /Eth
+ /Ntilde
+ /Ograve
+ /Oacute
+ /Ocircumflex
+ /Otilde
+ /Odieresis
+ /multiply
+ /Oslash
+ /Ugrave
+ /Uacute
+ /Ucircumflex
+ /Udieresis
+ /Yacute
+ /Thorn
+ /germandbls
+ /agrave
+ /aacute
+ /acircumflex
+ /atilde
+ /adieresis
+ /aring
+ /ae
+ /ccedilla
+ /egrave
+ /eacute
+ /ecircumflex
+ /edieresis
+ /igrave
+ /iacute
+ /icircumflex
+ /idieresis
+ /eth
+ /ntilde
+ /ograve
+ /oacute
+ /ocircumflex
+ /otilde
+ /odieresis
+ /divide
+ /oslash
+ /ugrave
+ /uacute
+ /ucircumflex
+ /udieresis
+ /yacute
+ /thorn
+ /ydieresis
+] def
+
+/NewFontDirectory FontDirectory maxlength dict def
+
+%
+% Apparently no guarantee findfont is defined in systemdict so the obvious
+%
+% systemdict /findfont get exec
+%
+% can generate an error. So far the only exception is a VT600 (version 48.0).
+%
+
+userdict /@RealFindfont known not {
+ userdict begin
+ /@RealFindfont systemdict begin /findfont load end def
+ end
+} if
+
+/findfont {
+ dup NewFontDirectory exch known not {
+ dup
+ %dup systemdict /findfont get exec % not always in systemdict
+ dup userdict /@RealFindfont get exec
+ dup /Encoding get StandardEncoding eq {
+ dup length 1 add dict begin
+ {1 index /FID ne {def}{pop pop} ifelse} forall
+ /Encoding UTFLatin1Encoding def
+ /Metrics 1 dict def
+ Metrics /.notdef 0 put
+ currentdict
+ end
+ /DummyFontName exch definefont
+ } if
+ NewFontDirectory 3 1 roll put
+ } if
+ NewFontDirectory exch get
+} bind def
+
+%
+% Assume A0, except for A0A0 which is replaced by 20A0, can be ignored.
+% Works with ASCII or Latin1 because A0 has been re-encoded as a zero
+% width non-printing character.
+%
+
+/UTFstring {
+ dup {
+ (\240\240) search {
+ pop
+ 0 16#20 put
+ }{pop exit} ifelse
+ } loop
+} bind def
+
+/ashow {mark 4 1 roll UTFstring //ashow cvx exec cleartomark} bind def
+/awidthshow {mark 7 1 roll UTFstring //awidthshow cvx exec cleartomark} bind def
+/show {mark exch UTFstring //show cvx exec cleartomark} bind def
+/stringwidth {UTFstring //stringwidth cvx exec} bind def
+/widthshow {mark 5 1 roll UTFstring //widthshow cvx exec cleartomark} bind def
+
+%
+% kshow is harder - stack can't change because of the procedure.
+%
+
+/kshow dup load type /operatortype eq
+ {{UTFstring kshow} bind}
+ {{UTFstring //kshow cvx exec} bind}
+ifelse def
+
diff --git a/sys/src/cmd/postscript/psencoding/mkfile b/sys/src/cmd/postscript/psencoding/mkfile
new file mode 100755
index 000000000..eaceebdb1
--- /dev/null
+++ b/sys/src/cmd/postscript/psencoding/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+
+<../config
+
+POSTBIN=$ROOT/rc/bin/postscript
+
+all:V: psencoding
+
+install installall:V: $POSTBIN/psencoding $POSTLIB/Latin1.enc $POSTLIB/UTF.enc
+
+clean nuke:V:
+ rm -f psencoding
+
+$POSTBIN/psencoding: psencoding
+ cp $prereq $target
+
+$POSTLIB/%.enc: %.enc
+ cp $prereq $target
+
+psencoding: psencoding.rc
+ sed 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' psencoding.rc >psencoding
+ chmod 775 psencoding
diff --git a/sys/src/cmd/postscript/psencoding/psencoding.rc b/sys/src/cmd/postscript/psencoding/psencoding.rc
new file mode 100755
index 000000000..c0b6aeff9
--- /dev/null
+++ b/sys/src/cmd/postscript/psencoding/psencoding.rc
@@ -0,0 +1,39 @@
+#!/bin/rc
+# Trivial script for checking and setting the default PostScript font
+# encoding. Changing the default assumes you can write in $POSTLIB.
+# Available font encodings are files in $POSTLIB that end in .enc.
+# The default is $POSTLIB/Default.enc.
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+DEFAULT=Default.enc
+CURRENTDEFAULT=Standard
+
+if (~ $#* 0) {
+ cd $POSTLIB
+ for (i in *.enc) {
+ if (test -f $i -a $i '!=' $DEFAULT) {
+ NAME=`{echo $i | sed s/\\.enc//}
+ if (cmp $i $DEFAULT >/dev/null >[2]/dev/null) {
+ CURRENTDEFAULT=$NAME
+ }
+ echo $NAME
+ }
+ }
+ echo Standard
+ echo Default'='$CURRENTDEFAULT
+}
+if not {
+ switch ($1) {
+ case Default
+ ;
+ case Standard
+ rm -f $POSTLIB/$DEFAULT
+ case *
+ if (test -f $POSTLIB/$1.enc) {
+ rm -f $POSTLIB/$DEFAULT
+ cp $POSTLIB/$1.enc $POSTLIB/$DEFAULT
+ }
+ if not echo unrecognized encoding name $1 >[1=2]
+ }
+}
diff --git a/sys/src/cmd/postscript/psfiles/Nroundpage.ps b/sys/src/cmd/postscript/psfiles/Nroundpage.ps
new file mode 100755
index 000000000..44a67a6d7
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/Nroundpage.ps
@@ -0,0 +1,11 @@
+%
+% A version of roundpage.ps that assumes a symmetric clipping path. Thanks
+% to Matthijs Melchior for the suggestion.
+%
+
+/roundpagebbox {
+ pagebbox dup 0 get pagebbox 2 get add 2 exch put
+ pagebbox dup 1 get pagebbox 3 get add 3 exch put
+ pagebbox 0 0 put
+ pagebbox 1 0 put
+} bind def
diff --git a/sys/src/cmd/postscript/psfiles/README b/sys/src/cmd/postscript/psfiles/README
new file mode 100755
index 000000000..c1a70140f
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/README
@@ -0,0 +1,11 @@
+PostScript files that go in $(POSTLIB). Several, like forms.ps, are
+used by most translators supplied in this package. Most PostScript
+files only used by a single translator (e.g. the prologue) have been
+been moved into the appropriate source directory. Files that end in
+.enc support alternate character sets (e.g. ISO Latin 1 alphabet).
+The implementation is left open, but typically redefines findfont.
+That approach works because findfont is a procedure rather than an
+operator, so it's not affected by bind. Also can't depend on having
+a systemdict definition for findfont. It's in userdict on Version
+48.0 VT600s.
+
diff --git a/sys/src/cmd/postscript/psfiles/aps.ps b/sys/src/cmd/postscript/psfiles/aps.ps
new file mode 100755
index 000000000..9ba0d3095
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/aps.ps
@@ -0,0 +1,127 @@
+%
+% Tune things up so Linotronic output looks more like the APS-5. Pull this file
+% into dpost output using the -C option. To get the best looking output run dpost
+% with the -e2 option and use special font files that look like the APS tables but
+% have character codes (ie. the fourth column in the width tables) appropriate for
+% PostScript fonts. Widths in these tables must be for APS fonts!
+%
+% Start with fat versions of the stroked Courier and Courier-Oblique fonts - from
+% Johnathan Shopiro.
+%
+
+/newdict /Courier findfont length dict def
+/Courier findfont {
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+newdict /StrokeWidth 65 put
+/Courier newdict definefont pop
+
+/newdict /Courier-Oblique findfont length dict def
+/Courier-Oblique findfont {
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+newdict /StrokeWidth 65 put
+/Courier-Oblique newdict definefont pop
+
+%
+% Scaled down versions of the Helvetica font family.
+%
+
+/newdict /Helvetica findfont length dict def
+/Helvetica findfont {
+ 1 index /FontMatrix eq {.922 .922 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Helvetica newdict definefont pop
+
+/newdict /Helvetica-Oblique findfont length dict def
+/Helvetica-Oblique findfont {
+ 1 index /FontMatrix eq {.922 .922 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Helvetica-Oblique newdict definefont pop
+
+/newdict /Helvetica-Bold findfont length dict def
+/Helvetica-Bold findfont {
+ 1 index /FontMatrix eq {.922 .922 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Helvetica-Bold newdict definefont pop
+
+/newdict /Helvetica-BoldOblique findfont length dict def
+/Helvetica-BoldOblique findfont {
+ 1 index /FontMatrix eq {.922 .922 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Helvetica-BoldOblique newdict definefont pop
+
+%
+% Scaled up versions of the Times font family.
+%
+
+/newdict /Times-Roman findfont length dict def
+/Times-Roman findfont {
+ 1 index /FontMatrix eq {1.0225 1.0225 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Times-Roman newdict definefont pop
+
+/newdict /Times-Italic findfont length dict def
+/Times-Italic findfont {
+ 1 index /FontMatrix eq {1.0225 1.0225 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Times-Italic newdict definefont pop
+
+/newdict /Times-Bold findfont length dict def
+/Times-Bold findfont {
+ 1 index /FontMatrix eq {1.0225 1.0225 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Times-Bold newdict definefont pop
+
+/newdict /Times-BoldItalic findfont length dict def
+/Times-BoldItalic findfont {
+ 1 index /FontMatrix eq {1.0225 1.0225 matrix scale matrix concatmatrix} if
+
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+/Times-BoldItalic newdict definefont pop
+
diff --git a/sys/src/cmd/postscript/psfiles/banner.ps b/sys/src/cmd/postscript/psfiles/banner.ps
new file mode 100755
index 000000000..d5637416e
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/banner.ps
@@ -0,0 +1,40 @@
+%
+% Simple program to print a banner page
+%
+
+/banner {
+ /saveobj save def
+ erasepage initgraphics
+
+ /#copies 1 def
+ /inch {72 mul} bind def
+ /pagebbox [clippath pathbbox newpath] def
+
+ /font /Helvetica def
+ /size 20 def
+ /height pagebbox 3 get def
+ /width pagebbox 2 get .09 mul def
+
+ .92 setgray
+ pagebbox 0 get pagebbox 1 get moveto
+ width 0 rlineto 0 height rlineto width neg 0 rlineto closepath eofill
+ pagebbox 2 get pagebbox 1 get moveto
+ width neg 0 rlineto 0 height rlineto width 0 rlineto closepath eofill
+ 0 setgray
+
+ font findfont size scalefont setfont
+ /linesp size size .15 mul add neg def
+ /tab (Destination) stringwidth pop 1.5 mul def
+ /nextline {0 0 moveto show tab 0 moveto show 0 linesp translate} def
+
+ pagebbox 0 get 1.5 width mul add pagebbox 3 get 2.0 width mul sub translate
+ (Bin) nextline
+ (Name) nextline
+ (Owner) nextline
+ (File) nextline
+ (Account) nextline
+ (Destination) nextline
+ (Spooldate) nextline
+ showpage
+ saveobj restore
+} bind def
diff --git a/sys/src/cmd/postscript/psfiles/baseline.ps b/sys/src/cmd/postscript/psfiles/baseline.ps
new file mode 100755
index 000000000..470047c9a
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/baseline.ps
@@ -0,0 +1,156 @@
+%
+% Stuff used to draw or set text along a baseline specified by parametric equations
+% for x and y.
+%
+
+/left -1 def
+/center 0 def
+/right 1 def
+
+/baselinedict 50 dict def
+
+/newbaseline {
+ baselinedict begin
+ /g' exch bind def
+ /f' exch bind def
+ /g exch bind def
+ /f exch bind def
+
+ counttomark 2 eq {/hoffset exch def} if
+ /res exch def
+
+ /t 0 def
+ /s 0 def
+ /voffset false def
+ cleartomark
+ end
+} bind def
+
+/drawfunnytext {
+ baselinedict begin
+ /t exch def
+ /mode exch def
+ /str exch def
+
+ mode left eq {
+ /leftstring emptystring def
+ /rightstring str def
+ } if
+
+ mode right eq {
+ /leftstring str reversestring def
+ /rightstring emptystring def
+ } if
+
+ mode center eq {
+ str splitstring
+ /rightstring exch def
+ /leftstring exch reversestring def
+ } if
+
+ gsave currentpoint translate leftstring left t baselineshow grestore
+ gsave currentpoint translate rightstring right t baselineshow grestore
+
+ /t 0 def
+ /s 0 def
+ /voffset false def
+ cleartomark
+ end
+} bind def
+
+/setfunnytext {
+ baselinedict begin
+ /vpos exch def
+ /hpos exch def
+ /str exch def
+
+ voffset vpos ne {
+ /voffset vpos def
+ /t 0 def
+ /s hoffset def
+ } if
+
+ gsave
+ hoffset voffset translate
+ 0 0 moveto
+ /ds hpos s sub def
+ /dt ds t f' dup mul t g' dup mul add sqrt res mul div def
+ /s s ds add def
+ /t t dt add def
+ str right t baselineshow
+ grestore
+ end
+} bind def
+
+baselinedict begin
+
+/f {} bind def
+/g {pop 0} bind def
+/f' {pop 1} bind def
+/g' {pop 0} bind def
+
+/s 0 def
+/t 0 def
+/res 72 def
+
+/onecharstring ( ) def
+/emptystring () def
+
+/baselineshow {
+ /t exch def
+ /mode exch def
+ /str exch def
+
+ gsave
+ t f res mul t g res mul translate
+ 0 0 moveto
+ t g' t f' atan rotate
+ {
+ mode right eq {pop} if
+ grestore gsave
+ onecharstring 0 3 -1 roll put onecharstring stringwidth pop
+ /ds exch mode mul def
+ /dt ds t f' dup mul t g' dup mul add sqrt res mul div def
+ /t t dt add def
+ /s s ds add def
+ t f res mul t g res mul translate
+ 0 0 moveto
+ t g' t f' atan rotate
+ mode left eq {pop} if
+ } str kshow
+ grestore
+} bind def
+
+/reversestring {
+ /str1 exch def
+
+ /str2 str1 length string def
+ /i 0 def
+ /n str1 length 1 sub def
+
+ {
+ str1 n get str2 exch i exch put
+ /i i 1 add def
+ /n n 1 sub def
+ n 0 lt {exit} if
+ } loop
+ str2
+} bind def
+
+/splitstring {
+ /str1 exch def
+
+ /len str1 stringwidth pop def
+ /s 0 def
+ /n 0 def
+ str1 length {
+ str1 n get onecharstring exch 0 exch put
+ /s onecharstring stringwidth pop s add def
+ s len 2 div ge {exit} if
+ /n n 1 add def
+ } repeat
+ str1 0 n 1 add getinterval
+ str1 n str1 length n sub getinterval
+} bind def
+
+end
diff --git a/sys/src/cmd/postscript/psfiles/color.ps b/sys/src/cmd/postscript/psfiles/color.ps
new file mode 100755
index 000000000..e304cc3a3
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/color.ps
@@ -0,0 +1,65 @@
+%
+% Color and reverse video support for dpost. A call made to setcolor with two
+% arguments implies reverse video printing.
+%
+
+/rgb {setrgbcolor} bind def
+/hsb {sethsbcolor} bind def
+
+/colordict 50 dict dup begin
+ /red { 1 0 0 } def
+ /green { 0 1 0 } def
+ /blue { 0 0 1 } def
+ /cyan { 0 1 1 } def
+ /magenta { 1 0 1 } def
+ /yellow { 1 1 0 } def
+ /white { 1 1 1 } def
+ /black { 0 0 0 } def
+end def
+
+/setcolor {
+ counttomark 1 eq {
+ dup colordict exch known not {pop /black} if
+ colordict exch get exec setrgbcolor
+ } if
+ counttomark 2 eq {
+ /backcolor exch def
+ /textcolor exch def
+ colordict backcolor known not colordict textcolor known not or {
+ /backcolor colordict /black get def
+ /textcolor colordict /white get def
+ } if
+ /backcolor colordict backcolor get def
+ /textcolor colordict textcolor get def
+ /dY1 0 def
+ /dY2 0 def
+ textcolor exec setrgbcolor
+ } if
+} bind def
+
+/drawrvbox {
+ /x2 exch def
+ /x1 exch def
+
+ currentpoint dup
+ /y1 exch def
+ /y2 exch def pop
+
+ dY1 0 eq dY2 0 eq and {
+ currentfont /FontBBox get aload pop
+ currentfont /FontMatrix get dtransform /dY2 exch def pop
+ currentfont /FontMatrix get dtransform /dY1 exch def pop
+ } if
+
+ /y1 y1 dY1 add def
+ /y2 y2 dY2 add def
+
+ backcolor exec setrgbcolor
+ newpath
+ x1 y1 moveto
+ x2 y1 lineto
+ x2 y2 lineto
+ x1 y2 lineto
+ closepath fill
+ textcolor exec setrgbcolor
+} bind def
diff --git a/sys/src/cmd/postscript/psfiles/fatcourier.ps b/sys/src/cmd/postscript/psfiles/fatcourier.ps
new file mode 100755
index 000000000..11d718814
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/fatcourier.ps
@@ -0,0 +1,26 @@
+%
+% Fat versions of the stroked Courier and Courier-Oblique - from Johnathan Shopiro.
+% Can be selectively pulled in using the -C option that's available with all the
+% PostScript translators or permanently added to any of the prologues. Helps on
+% Linotronic typesetters, where Courier and Courier-Oblique are too light!
+%
+
+/newdict /Courier findfont length 1 add dict def
+/Courier findfont {
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+newdict /StrokeWidth 60 put
+/Courier newdict definefont pop
+
+/newdict /Courier-Oblique findfont length 1 add dict def
+/Courier-Oblique findfont {
+ 1 index /FID ne
+ {newdict 3 1 roll put}
+ {pop pop}
+ ifelse
+} forall
+newdict /StrokeWidth 60 put
+/Courier-Oblique newdict definefont pop
diff --git a/sys/src/cmd/postscript/psfiles/forms.ps b/sys/src/cmd/postscript/psfiles/forms.ps
new file mode 100755
index 000000000..a96fcdaef
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/forms.ps
@@ -0,0 +1,213 @@
+%
+% Procedures that let you print any number of pages on each sheet of paper. It's
+% far from perfect and won't handle everything (eg. it's not recursive), but should
+% be good enough for now. Assumes the default page coordinate system has been set
+% up before setupforms is called. lastpage makes certain the last page is printed,
+% and should be called immediately after the %%Trailer comment.
+%
+% Three lines of code needed for page image clipping have been commented out for
+% now. It works, but can really slow things down on some versions of PostScript.
+% Uncomment them if you want to clip pages.
+%
+
+/setupforms {
+ /formsperpage exch def
+
+ /currentform 0 def
+ /slop 5 def
+ /min {2 copy gt {exch} if pop} def
+
+%
+% Save the current environment so the real showpage can be restored when we're all
+% done. Occasionally helps when a banner page is included with the job.
+%
+
+ /saveobj save def
+
+%
+% Number of rows and columns we'll need - may exchange them later.
+%
+
+ /columns formsperpage sqrt ceiling cvi def
+ /rows formsperpage columns div ceiling cvi def
+
+%
+% Slop leaves a little room around the edge so page images can be outlined and have
+% the borders show up. Distance is in default coordinates, so we need to figure out
+% how it maps into user coordinates.
+%
+
+ 6 array defaultmatrix
+ 6 array currentmatrix
+ 6 array invertmatrix
+ 6 array concatmatrix
+ /tempmatrix exch def
+
+ 0 slop tempmatrix dtransform dup mul exch dup mul add sqrt
+ /slop exch def
+
+%
+% Determine how big the image area is, using the clipping path bounding box minus
+% a little and leave the coordinates of the lower left corner of the clipping path
+% on the stack. Also temporarily set the size of each page (ie. formheight and
+% formwidth) from the clipping path - just in case old software uses this stuff.
+% Only works for coordinate systems that have been rotated by a multiple of 90
+% degrees.
+%
+
+ newpath clippath pathbbox
+ 2 index sub dup /formheight exch def slop 2 mul sub /pageheight exch def
+ 2 index sub dup /formwidth exch def slop 2 mul sub /pagewidth exch def
+
+%
+% New translators all store the size of each page in default coordinates in the
+% pagebbox array and it can be different than the size determined by the clipping
+% path. If we can find pagebbox use it to set the real dimensions of each page.
+% Leaves the coordinates of the lower left corner on the stack, (either from
+% pagebbox or clippath) so four numbers are there when we're done.
+%
+
+ userdict /gotpagebbox known userdict /pagebbox known and {
+ newpath
+ pagebbox 0 get pagebbox 1 get tempmatrix transform moveto
+ pagebbox 0 get pagebbox 3 get tempmatrix transform lineto
+ pagebbox 2 get pagebbox 3 get tempmatrix transform lineto
+ pagebbox 2 get pagebbox 1 get tempmatrix transform lineto
+ closepath pathbbox
+ 2 index sub /formheight exch def
+ 2 index sub /formwidth exch def
+ } {2 copy} ifelse
+
+%
+% Top two numbers are the displacement from the job's origin to the lower left
+% corner of each page image when we finish setting up the new coordinate system.
+%
+
+ /ycorner exch def
+ /xcorner exch def
+
+%
+% The two numbers left on the stack are the coordinates of the lower left corner
+% of the clipping path. Go there and then up a bit so page images can be outlined.
+%
+
+ translate
+ slop slop translate
+
+%
+% If the page is wider than high we may be able to do better if we exchange rows
+% and columns. Won't make a difference in the current orientation or if rows and
+% columns are the same.
+%
+
+ pagewidth pageheight gt {
+ rows columns /rows exch def /columns exch def
+ } if
+
+%
+% Find the orientation and scaling that makes things as large as possible. More
+% than what's really needed. First calculation essentially finds the minimum of
+% 1/rows and 1/columns.
+%
+
+ pagewidth formwidth columns mul div pageheight formheight rows mul div min
+ pageheight formwidth columns mul div pagewidth formheight rows mul div min
+
+ 2 copy lt {
+ rotation 1 eq {
+ landscape {
+ 0 pageheight translate
+ -90 rotate
+ }{
+ pagewidth 0 translate
+ 90 rotate
+ } ifelse
+ }{
+ landscape {
+ pagewidth 0 translate
+ 90 rotate
+ }{
+ 0 pageheight translate
+ -90 rotate
+ } ifelse
+ } ifelse
+ pagewidth pageheight /pagewidth exch def /pageheight exch def
+ exch
+ } if
+
+%
+% Second number from the top is the best choice. Scale so everything will fit on
+% the current page, go back to the original origin, and then get ready for the
+% first page - which goes in the upper left corner.
+%
+
+ pop dup dup scale
+ xcorner neg ycorner neg translate
+ 0 rows 1 sub formheight mul translate
+
+%
+% Try to center everything on the page - scaling we used is on top of the stack.
+%
+
+ dup pagewidth exch div formwidth columns mul sub 2 div
+ exch pageheight exch div formheight rows mul sub 2 div translate
+
+%
+% Redefine showpage.
+%
+
+ /!PreForms~showpage~ /showpage load def % save current showpage
+
+ /showpage {
+ saveobj restore
+% initclip
+ formsperpage 1 gt {
+ gsave .1 setlinewidth outlineform stroke grestore
+ } if
+ formwidth 0 translate
+ /currentform currentform 1 add def
+ currentform columns mod 0 eq {
+ columns formwidth mul neg formheight neg translate
+ } if
+ currentform formsperpage mod 0 eq {
+ gsave !PreForms~showpage~ grestore
+ currentform columns mod formwidth mul neg
+ formsperpage columns idiv formheight mul translate
+ /currentform 0 def
+ } if
+% outlineform clip newpath
+ /saveobj save def
+ } bind def
+
+ /outlineform {
+ newpath
+ xcorner ycorner moveto
+ formwidth 0 rlineto
+ 0 formheight rlineto
+ formwidth neg 0 rlineto
+ closepath
+ } bind def
+
+ /lastpage {
+ formsperpage 1 gt {
+ currentform 0 ne {
+ /saveobj save def
+ 0 1 formsperpage currentform sub formsperpage mod {
+ pop showpage
+ } for
+ saveobj restore
+ } if
+ saveobj restore
+ saveobj restore
+ } if
+ } def
+
+%
+% Clip the first page image and save the environment we just set up, including
+% the redefined showpage.
+%
+
+% outlineform clip
+ newpath
+ /saveobj save def
+} def
diff --git a/sys/src/cmd/postscript/psfiles/mkfile b/sys/src/cmd/postscript/psfiles/mkfile
new file mode 100755
index 000000000..ee75ec5ac
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/mkfile
@@ -0,0 +1,12 @@
+</$objtype/mkfile
+
+<../config
+
+all:VQ:
+ ;
+
+install installall:V:
+ cp *.ps ps.* $POSTLIB
+
+clean nuke:VQ:
+ ;
diff --git a/sys/src/cmd/postscript/psfiles/ps.requests b/sys/src/cmd/postscript/psfiles/ps.requests
new file mode 100755
index 000000000..59e2a571d
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/ps.requests
@@ -0,0 +1,16 @@
+%
+% Keywords begin with an @ in the first column. The value follows on the next
+% line and includes everything up to next keyword line, except for comments
+% which are lines that begin with % in the first column.
+%
+
+@manualfeed
+ statusdict begin
+ /manualfeedtimeout 300 def
+ /manualfeed true def
+ end
+
+@ledgertray
+ statusdict begin
+ ledgertray
+ end
diff --git a/sys/src/cmd/postscript/psfiles/roundpage.ps b/sys/src/cmd/postscript/psfiles/roundpage.ps
new file mode 100755
index 000000000..68d2d443a
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/roundpage.ps
@@ -0,0 +1,30 @@
+%
+% Tries to round clipping path dimensions, as stored in array pagebbox, so they
+% match one of the known sizes in the papersizes array. Lower left coordinates
+% are always set to 0.
+%
+
+/roundpagebbox {
+ 7 dict begin
+ /papersizes [8.5 inch 11 inch 14 inch 17 inch] def
+
+ /mappapersize {
+ /val exch def
+ /slop .5 inch def
+ /diff slop def
+ /j 0 def
+ 0 1 papersizes length 1 sub {
+ /i exch def
+ papersizes i get val sub abs
+ dup diff le {/diff exch def /j i def} {pop} ifelse
+ } for
+ diff slop lt {papersizes j get} {val} ifelse
+ } def
+
+ pagebbox 0 0 put
+ pagebbox 1 0 put
+ pagebbox dup 2 get mappapersize 2 exch put
+ pagebbox dup 3 get mappapersize 3 exch put
+ end
+} bind def
+
diff --git a/sys/src/cmd/postscript/psfiles/shade.ps b/sys/src/cmd/postscript/psfiles/shade.ps
new file mode 100755
index 000000000..47e316798
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/shade.ps
@@ -0,0 +1,52 @@
+%
+% Shading support - primarily for ASCII file translators.
+%
+
+/grays [0.98 0.9 0.75 0.6] def
+
+/setshade {
+ /level exch def
+ level 0 le {
+ /textgray 0 def
+ /backgray 1 def
+ }{
+ /backgray level grays length gt
+ {/textgray 1 def 0}
+ {/textgray 0 def grays level 1 sub get}
+ ifelse def
+ } ifelse
+ textgray setgray
+ /dY1 0 def
+ /dY2 0 def
+} bind def
+
+/drawrvbox {
+ /x2 exch charwidth mul def
+ /x1 exch charwidth mul def
+
+ x1 x2 lt {
+ dup % expects y on top
+ /y1 exch linespace mul def
+ /y2 y1 def
+
+ dY1 0 eq dY2 0 eq and {
+ currentfont /FontBBox get aload pop
+ 160 sub
+ currentfont /FontMatrix get dtransform /dY2 exch def pop
+ 100 add
+ currentfont /FontMatrix get dtransform /dY1 exch def pop
+ } if
+
+ /y1 y1 dY1 add def
+ /y2 y2 dY2 add def
+
+ backgray setgray
+ newpath
+ x1 y1 moveto
+ x2 y1 lineto
+ x2 y2 lineto
+ x1 y2 lineto
+ closepath fill
+ } if
+ textgray setgray
+} bind def
diff --git a/sys/src/cmd/postscript/psfiles/unbind.ps b/sys/src/cmd/postscript/psfiles/unbind.ps
new file mode 100755
index 000000000..98e6283e6
--- /dev/null
+++ b/sys/src/cmd/postscript/psfiles/unbind.ps
@@ -0,0 +1,28 @@
+%
+% Unbind the operators in an executable array or packedarray. Leaves the
+% unbound array or the original object on the stack.
+%
+
+/unbind {
+ 0 index xcheck
+ 1 index type /arraytype eq
+ 2 index type /packedarraytype eq or and {
+ dup length array copy cvx
+ dup 0 exch {
+ dup type /operatortype eq {
+ ( ) cvs cvn cvx
+ } if
+
+ 0 index xcheck
+ 1 index type /arraytype eq
+ 2 index type /packedarraytype eq or and {
+ unbind
+ } if
+
+ 3 copy put pop
+ 1 add
+ } forall
+ pop
+ } if
+} def
+
diff --git a/sys/src/cmd/postscript/tcpostio/dial.c b/sys/src/cmd/postscript/tcpostio/dial.c
new file mode 100755
index 000000000..da885d9b6
--- /dev/null
+++ b/sys/src/cmd/postscript/tcpostio/dial.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+
+#define DIALTIMEOUT 30
+
+/* This is a dummy routine for non Plan9 systems.
+ * No attempt has been made to be clever, it's just
+ * supposed to work in this program.
+ */
+int dial_debug = 0;
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp) {
+ int sockconn, lport;
+ struct hostent *hp; /* Pointer to host info */
+ struct sockaddr_in sin; /* Socket address, Internet style */
+ struct servent *sp = 0;
+ char *tdest, *netname, *hostname, *servname;
+ int sock_type;
+#ifndef plan9
+#define USED(x) if(x); else
+ int sockoption, sockoptsize;
+#endif
+
+ USED(dir);
+ USED(cfdp);
+ if ((tdest = malloc(strlen(dest)+1)) == NULL) {
+ if (dial_debug) fprintf(stderr, "dial: could not allocate memory\n");
+ return(-1);
+ }
+ strcpy(tdest, dest);
+
+ if ((netname = strtok(tdest, "!")) == NULL) {
+ fprintf(stderr, "dial: no network name\n");
+ return(-1);
+ }
+ if (strcmp(netname, "tcp") == 0) {
+ sock_type = SOCK_STREAM;
+ } else if (strcmp(netname, "udp") == 0) {
+ sock_type = SOCK_DGRAM;
+ } else {
+ fprintf(stderr, "dial: network protocol name `%s' is invalid; must be `tcp' or `udp'\n", netname);
+ return(-1);
+ }
+ if ((hostname = strtok(0, "!")) == NULL) {
+ fprintf(stderr, "dial: no host name or number\n");
+ return(-1);
+ }
+ if ((servname = strtok(0, "!")) == NULL) {
+ fprintf(stderr, "dial: no service name or number\n");
+ return(-1);
+ }
+ hp = gethostbyname(hostname);
+ if (hp == (struct hostent *)NULL) {
+ if (dial_debug) fprintf(stderr, "host `%s' unknown by local host\n", hostname);
+ return(-1);
+ }
+ if (!isdigit(servname[0]))
+ sp = getservbyname(servname, netname);
+ sin.sin_addr.s_addr = *(unsigned long*)hp->h_addr;
+ sin.sin_port = htons((sp==0)?atoi(servname):sp->s_port);
+ sin.sin_family = AF_INET;
+ if (local == NULL) {
+ if ((sockconn = socket(AF_INET, sock_type, 0)) < 0) {
+ if (dial_debug) perror("dial:socket():");
+ return(-1);
+ }
+ if (dial_debug) fprintf(stderr, "socket FD=%d\n", sockconn);
+ } else {
+ lport = atoi(local);
+ if ((lport < 512) || (lport >= 1024)) {
+ fprintf(stderr, "dial:invalid local port %d\n", lport);
+ return(-1);
+ }
+ if ((sockconn = rresvport(&lport)) < 0) {
+ if (dial_debug) perror("dial:rresvport():");
+ return(-1);
+ }
+ }
+ if (dial_debug) {
+ fprintf(stderr, "sin size=%d\n", sizeof(sin));
+ }
+ alarm(DIALTIMEOUT);
+ if ((connect(sockconn, (struct sockaddr *) &sin, sizeof(sin)) < 0)) {
+ if (dial_debug) perror("dial:connect():");
+ return(-1);
+ }
+ alarm(0);
+#ifndef plan9
+ sockoptsize = sizeof(sockoption);
+ if (getsockopt(sockconn, SOL_SOCKET, SO_KEEPALIVE, &sockoption, &sockoptsize) < 0) {
+ if (dial_debug) perror("dial:getsockopt():");
+ return(-1);
+ }
+ if (sockoptsize == sizeof(sockoption) && !sockoption) {
+ if (setsockopt(sockconn, SOL_SOCKET, SO_KEEPALIVE, &sockoption, sockoptsize) < 0) {
+ if (dial_debug) perror("dial:getsockopt():");
+ return(-1);
+ }
+ }
+#endif
+ return(sockconn);
+}
diff --git a/sys/src/cmd/postscript/tcpostio/mkfile b/sys/src/cmd/postscript/tcpostio/mkfile
new file mode 100755
index 000000000..539f5907d
--- /dev/null
+++ b/sys/src/cmd/postscript/tcpostio/mkfile
@@ -0,0 +1,16 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=tcpostio
+OFILES= tcpostio.$O\
+ dial.$O\
+
+BIN=$POSTBIN
+
+</sys/src/cmd/mkone
+CC=pcc
+CFLAGS=-Bw -c -D_BSD_EXTENSION -D_NET_EXTENSION -Dplan9
+
+syms:V:
+ vc $CFLAGS -a tcpostio.c > syms
diff --git a/sys/src/cmd/postscript/tcpostio/tcpostio.c b/sys/src/cmd/postscript/tcpostio/tcpostio.c
new file mode 100755
index 000000000..4f0fdc007
--- /dev/null
+++ b/sys/src/cmd/postscript/tcpostio/tcpostio.c
@@ -0,0 +1,534 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef plan9
+#include <bsd.h>
+#endif
+
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+
+extern int dial_debug;
+extern int dial(char*, char*, char*, int*);
+
+
+/* debug = 0 for no debugging */
+/* debug = 1 for readprinter debugging */
+/* debug = 2 for sendprinter debugging */
+/* debug = 3 for full debugging, its hard to read the messages */
+
+int debug = 0;
+#define READTIMEOUT 300
+#define RCVSELTIMEOUT 30
+#define SNDSELTIMEOUT 300
+
+void
+rdtmout(void) {
+ fprintf(stderr, "read timeout occurred, check printer\n");
+}
+
+int
+getline(int fd, char *buf, int len) {
+ char *bp, c;
+ int i = 0, n;
+
+ bp = buf;
+ while (alarm(READTIMEOUT),(n=read(fd, bp, 1)) == 1) {
+ alarm(0);
+ if (*bp == '\r') continue;
+ i += n;
+
+ c = *bp++;
+ if (c == '\n' || c == '\004' || i >= len-1)
+ break;
+ }
+ alarm(0);
+ if (n < 0)
+ return(n);
+ *bp = '\0';
+ return(i);
+}
+
+typedef struct {
+ char *state; /* printer's current status */
+ int val; /* value returned by getstatus() */
+} Status;
+
+/* printer states */
+#define INITIALIZING 0
+#define IDLE 1
+#define BUSY 2
+#define WAITING 3
+#define PRINTING 4
+#define PRINTERERROR 5
+#define ERROR 6
+#define FLUSHING 7
+#define UNKNOWN 8
+
+/* protocol requests and program states */
+#define START 'S'
+unsigned char Start[] = { START };
+#define ID_LE 'L'
+unsigned char Id_le[] = { ID_LE };
+#define REQ_STAT 'T'
+unsigned char Req_stat[] = { REQ_STAT };
+#define SEND_DATA 'D'
+unsigned char Send_data[] = { SEND_DATA };
+#define SENT_DATA 'A'
+unsigned char Sent_data[] = { SENT_DATA };
+#define WAIT_FOR_EOJ 'W'
+unsigned char Wait_for_eoj[] = { WAIT_FOR_EOJ };
+#define END_OF_JOB 'E'
+unsigned char End_of_job[] = { END_OF_JOB };
+#define FATAL_ERROR 'F'
+unsigned char Fatal_error[] = { FATAL_ERROR };
+#define WAIT_FOR_IDLE 'I'
+unsigned char Wait_for_idle[] = { WAIT_FOR_IDLE };
+#define OVER_AND_OUT 'O'
+unsigned char Over_and_out[] = { OVER_AND_OUT };
+
+Status statuslist[] = {
+ "initializing", INITIALIZING,
+ "idle", IDLE,
+ "busy", BUSY,
+ "waiting", WAITING,
+ "printing", PRINTING,
+ "printererror", PRINTERERROR,
+ "Error", ERROR,
+ "flushing", FLUSHING,
+ NULL, UNKNOWN
+};
+
+
+/* find returns a pointer to the location of string str2 in string str1,
+ * if it exists. Otherwise, it points to the end of str1.
+ */
+char *
+find(char *str1, char *str2) {
+ char *s1, *s2;
+
+ for (; *str1!='\0'; str1++) {
+ for (s1=str1,s2=str2; *s2!='\0'&&*s1==*s2; s1++,s2++) ;
+ if ( *s2 == '\0' )
+ break;
+ }
+
+ return(str1);
+}
+
+#define MESGSIZE 16384
+int blocksize = 1920; /* 19200/10, with 1 sec delay between transfers
+ * this keeps the queues from building up.
+ */
+char mesg[MESGSIZE]; /* exactly what came back on ttyi */
+
+int
+parsmesg(char *buf) {
+ static char sbuf[MESGSIZE];
+ char *s; /* start of printer messsage in mesg[] */
+ char *e; /* end of printer message in mesg[] */
+ char *key, *val; /* keyword/value strings in sbuf[] */
+ char *p; /* for converting to lower case etc. */
+ int i; /* where *key was found in statuslist[] */
+
+ if (*(s=find(buf, "%[ "))!='\0' && *(e=find(s, " ]%"))!='\0') {
+ strcpy(sbuf, s+3); /* don't change mesg[] */
+ sbuf[e-(s+3)] = '\0'; /* ignore the trailing " ]%" */
+
+ for (key=strtok(sbuf, " :"); key != NULL; key=strtok(NULL, " :")) {
+ if (strcmp(key, "Error") == 0)
+ return(ERROR);
+ if ((val=strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0)
+ key = val;
+
+ for (; *key == ' '; key++) ; /* skip any leading spaces */
+ for (p = key; *p; p++) /* convert to lower case */
+ if (*p == ':') {
+ *p = '\0';
+ break;
+ } else if (isupper(*p)) *p = tolower(*p);
+
+ for (i=0; statuslist[i].state != NULL; i++) {
+ if (strcmp(statuslist[i].state, key) == 0)
+ return(statuslist[i].val);
+ }
+ }
+ }
+ return(UNKNOWN);
+}
+
+char buf[MESGSIZE];
+fd_set readfds, writefds, exceptfds;
+struct timeval rcvtimeout = { RCVSELTIMEOUT, 0 };
+struct timeval sndtimeout = { SNDSELTIMEOUT, 0 };
+
+int
+readprinter(int printerfd, int pipefd)
+{
+ unsigned char proto;
+ int progstate = START;
+ int print_wait_msg = 0;
+ int tocount = 0;
+ int c, printstat, lastprintstat, n, nfds;
+
+
+ nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
+ printstat = 0;
+ signal(SIGALRM, rdtmout);
+ do {
+
+reselect:
+ /* ask sending process to request printer status */
+ if (write(pipefd, Req_stat, 1) != 1) {
+ fprintf(stderr, "request status failed\n");
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ FD_ZERO(&readfds); /* lets be anal */
+ FD_SET(printerfd, &readfds);
+ FD_SET(pipefd, &readfds);
+ FD_ZERO(&exceptfds);
+ FD_SET(printerfd, &exceptfds);
+ FD_SET(pipefd, &exceptfds);
+ n = select(nfds, &readfds, (fd_set *)0, &exceptfds, &rcvtimeout);
+ if (debug&0x1) fprintf(stderr, "readprinter select returned %d\n", n);
+ if (n == 0) {
+ /* a timeout occurred */
+ if (++tocount > 4) {
+ fprintf(stderr, "printer appears to be offline.\nHP4m printers may be out of paper.\n");
+ tocount = 0;
+ }
+ goto reselect;
+ }
+ if (n > 0 && FD_ISSET(printerfd, &exceptfds)) {
+ /* printer problem */
+ fprintf(stderr, "printer exception\n");
+ if (write(pipefd, Fatal_error, 1) != 1) {
+ fprintf(stderr, "'fatal error' write to pipe failed\n");
+ }
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ if (n > 0 && FD_ISSET(pipefd, &exceptfds)) {
+ /* pipe problem */
+ fprintf(stderr, "pipe exception\n");
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ if (n > 0 && FD_ISSET(pipefd, &readfds)) {
+ /* protocol pipe wants to be read */
+ if (debug&0x1) fprintf(stderr, "pipe wants to be read\n");
+ if (read(pipefd, &proto, 1) != 1) {
+ fprintf(stderr, "read protocol pipe failed\n");
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ if (debug&0x1) fprintf(stderr, "readprinter: proto=%c\n", proto);
+ /* change state? */
+ switch (proto) {
+ case SENT_DATA:
+ break;
+ case WAIT_FOR_EOJ:
+ if (!print_wait_msg) {
+ print_wait_msg = 1;
+ fprintf(stderr, "waiting for end of job\n");
+ }
+ progstate = proto;
+ break;
+ default:
+ fprintf(stderr, "received unknown protocol request <%c> from sendfile\n", proto);
+ break;
+ }
+ n--;
+ }
+ if (n > 0 && FD_ISSET(printerfd, &readfds)) {
+ /* printer wants to be read */
+ if (debug&0x1) fprintf(stderr, "printer wants to be read\n");
+ if ((c=getline(printerfd, buf, MESGSIZE)) < 0) {
+ fprintf(stderr, "read printer failed\n");
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ if (debug&0x1) fprintf(stderr, "%s\n", buf);
+ if (c==1 && *buf == '\004') {
+ if (progstate == WAIT_FOR_EOJ) {
+ if (debug&0x1) fprintf(stderr, "progstate=%c, ", progstate);
+ fprintf(stderr, "%%[ status: endofjob ]%%\n");
+/* progstate = WAIT_FOR_IDLE; */
+ progstate = OVER_AND_OUT;
+ if (write(pipefd, Over_and_out, 1) != 1) {
+ fprintf(stderr, "'fatal error' write to pipe failed\n");
+ }
+ continue;
+ } else {
+ if (printstat == ERROR) {
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ if (progstate != START && progstate != WAIT_FOR_IDLE)
+ fprintf(stderr, "warning: EOF received; program status is '%c'\n", progstate);
+
+ }
+ continue;
+ }
+
+ /* figure out if it was a status line */
+ lastprintstat = printstat;
+ printstat = parsmesg(buf);
+ if (printstat == UNKNOWN || printstat == ERROR
+ || lastprintstat != printstat) {
+ /* print whatever it is that was read */
+ fprintf(stderr, buf);
+ fflush(stderr);
+ if (printstat == UNKNOWN) {
+ printstat = lastprintstat;
+ continue;
+ }
+ }
+ switch (printstat) {
+ case UNKNOWN:
+ continue; /* shouldn't get here */
+ case FLUSHING:
+ case ERROR:
+ progstate = FATAL_ERROR;
+ /* ask sending process to die */
+ if (write(pipefd, Fatal_error, 1) != 1) {
+ fprintf(stderr, "Fatal_error mesg write to pipe failed\n");
+ }
+ continue;
+ case INITIALIZING:
+ case PRINTERERROR:
+ sleep(1);
+ break;
+ case IDLE:
+ if (progstate == WAIT_FOR_IDLE) {
+ progstate = OVER_AND_OUT;
+ if (write(pipefd, Over_and_out, 1) != 1) {
+ fprintf(stderr, "'fatal error' write to pipe failed\n");
+ }
+ continue;
+ }
+ progstate = SEND_DATA;
+
+ goto dowait;
+ case BUSY:
+ case WAITING:
+ default:
+ sleep(1);
+dowait:
+ switch (progstate) {
+ case WAIT_FOR_IDLE:
+ case WAIT_FOR_EOJ:
+ case START:
+ sleep(5);
+ break;
+
+ case SEND_DATA:
+ if (write(pipefd, Send_data, 1) != 1) {
+ fprintf(stderr, "send data write to pipe failed\n");
+ progstate = FATAL_ERROR;
+ continue;
+ }
+ break;
+ default:
+ fprintf(stderr, "unexpected program state %c\n", progstate);
+ exit(1);
+ }
+ break;
+ }
+ n--;
+ }
+ if (n > 0) {
+ fprintf(stderr, "more fds selected than requested!\n");
+ exit(1);
+ };
+ } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
+
+ if (progstate == FATAL_ERROR)
+ return(1);
+ else
+ return(0);
+}
+
+int
+sendfile(int infd, int printerfd, int pipefd)
+{
+ unsigned char proto;
+ int progstate = START;
+ int i, n, nfds;
+ int bytesread, bytesent = 0;
+
+ nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
+
+ if (write(printerfd, "\004", 1)!=1) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ }
+ do {
+ FD_ZERO(&readfds); /* lets be anal */
+ FD_SET(pipefd, &readfds);
+ n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &sndtimeout);
+ if (debug&02) fprintf(stderr, "sendfile select returned %d\n", n);
+ if (n > 0 && FD_ISSET(pipefd, &readfds)) {
+ /* protocol pipe wants to be read */
+ if (read(pipefd, &proto, 1) != 1) {
+ fprintf(stderr, "read protocol pipe failed\n");
+ return(1);
+ }
+ /* change state? */
+ if (debug&02) fprintf(stderr, "sendfile command - <%c>\n", proto);
+ switch (proto) {
+ case OVER_AND_OUT:
+ case END_OF_JOB:
+ progstate = proto;
+ break;
+ case SEND_DATA:
+ bytesread = 0;
+ do {
+ i = read(infd, &buf[bytesread], blocksize-bytesread);
+ if (debug&02) fprintf(stderr, "read %d bytes\n", i);
+ if (i > 0)
+ bytesread += i;
+ } while((i > 0) && (bytesread < blocksize));
+ if (i < 0) {
+ fprintf(stderr, "input file read error\n");
+ progstate = FATAL_ERROR;
+ break; /* from switch */
+ }
+ if (bytesread > 0) {
+ if (debug&02) fprintf(stderr, "writing %d bytes\n", bytesread);
+ if (write(printerfd, buf, bytesread)!=bytesread) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ } else if (write(pipefd, Sent_data, 1)!=1) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ } else {
+ bytesent += bytesread;
+ }
+ fprintf(stderr, "%d sent\n", bytesent);
+ fflush(stderr);
+
+ /* we have reached the end of the input file */
+ }
+ if (i == 0) {
+ if (progstate != WAIT_FOR_EOJ) {
+ if (write(printerfd, "\004", 1)!=1) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ } else if (write(pipefd, Wait_for_eoj, 1)!=1) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ } else {
+ progstate = WAIT_FOR_EOJ;
+ }
+ }
+ }
+ break;
+ case REQ_STAT:
+ if (write(printerfd, "\024", 1)!=1) {
+ fprintf(stderr, "write to printer failed\n");
+ progstate = FATAL_ERROR;
+ }
+ if (debug&02) fprintf(stderr, "^T");
+ break;
+ case FATAL_ERROR:
+ progstate = FATAL_ERROR;
+ }
+ } else if (n < 0) {
+ perror("sendfile:select:");
+ progstate = FATAL_ERROR;
+ } else if (n == 0) {
+ sleep(1);
+ fprintf(stderr, "sendfile timeout\n");
+ progstate = FATAL_ERROR;
+ }
+ } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
+ if (write(printerfd, "\004", 1)!=1) {
+ perror("sendfile:write:");
+ progstate = FATAL_ERROR;
+ }
+
+ if (debug&02) fprintf(stderr, "%d bytes sent\n", bytesent);
+ if (progstate == FATAL_ERROR)
+ return(1);
+ else
+ return(0);
+}
+
+void main(int argc, char *argv[]) {
+ int c, usgflg=0, infd, printerfd;
+ int cpid;
+ int pipefd[2];
+ char *dialstr;
+ unsigned long rprv, sprv;
+
+ dialstr = 0;
+
+ while ((c = getopt(argc, argv, "b:d:")) != -1)
+ switch (c) {
+ case 'b':
+ blocksize = atoi(optarg)/10;
+ if (blocksize > MESGSIZE || blocksize < 1)
+ blocksize = MESGSIZE;
+ break;
+ case 'd':
+ debug = atoi(optarg);
+ dial_debug = debug;
+ break;
+ case '?':
+ fprintf(stderr, "unknown option %c\n", c);
+ usgflg++;
+ }
+ if (optind < argc)
+ dialstr = argv[optind++];
+ else {
+ usgflg++;
+ }
+ if (usgflg) {
+ fprintf(stderr, "usage: %s [-b baudrate] net!host!service [infile]\n", argv[0]);
+ exit (2);
+ }
+ if (optind < argc) {
+ infd = open(argv[optind], 0);
+ if (infd < 0) {
+ fprintf(stderr, "cannot open %s\n", argv[optind]);
+ exit(1);
+ }
+ optind++;
+ } else
+ infd = 0;
+
+ if (debug & 02) fprintf(stderr, "blocksize=%d\n", blocksize);
+ if (debug) fprintf(stderr, "dialing address=%s\n", dialstr);
+ printerfd = dial(dialstr, 0, 0, 0);
+ if (printerfd < 0) exit(1);
+
+ fprintf(stderr, "printer startup\n");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipefd) < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+ switch(cpid = fork()){
+ case -1:
+ perror("fork error");
+ exit(1);
+ case 0:
+ close(pipefd[1]);
+ sprv = sendfile(infd, printerfd, pipefd[0]); /* child - to printer */
+ if (debug) fprintf(stderr, "to remote - exiting\n");
+ exit(sprv);
+ default:
+ close(pipefd[0]);
+ rprv = readprinter(printerfd, pipefd[1]); /* parent - from printer */
+ if (debug) fprintf(stderr, "from remote - exiting\n");
+ while(wait(&sprv) != cpid);
+ exit(rprv|sprv);
+ }
+}
diff --git a/sys/src/cmd/postscript/tests/postbgi1 b/sys/src/cmd/postscript/tests/postbgi1
new file mode 100755
index 000000000..da04a506a
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postbgi1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/postdaisy1 b/sys/src/cmd/postscript/tests/postdaisy1
new file mode 100755
index 000000000..6f228a880
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postdaisy1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/postdmd1 b/sys/src/cmd/postscript/tests/postdmd1
new file mode 100755
index 000000000..bc8ea6461
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postdmd1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/postgif1 b/sys/src/cmd/postscript/tests/postgif1
new file mode 100755
index 000000000..916014e2c
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postgif1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/postmd1 b/sys/src/cmd/postscript/tests/postmd1
new file mode 100755
index 000000000..5318760e0
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postmd1
@@ -0,0 +1,552 @@
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ -.5602721e+08 .1143984e+09 -.6207247e+08 .0000000e+00 .0000000e+00
+ -.1149580e+09 .2301959e+09 -.1138958e+09 .0000000e+00 .0000000e+00
+ -.5853962e+08 .1144553e+09 -.5355661e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2468074e+08 .1401407e+07 .2608215e+08 .0000000e+00 .0000000e+00
+ -.5473053e+06 -.2402412e+07 -.1855106e+07 .0000000e+00 .0000000e+00
+ .2522804e+08 .1001005e+07 -.2422704e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 -.5219836e+08 .1123168e+09 -.6715163e+08 .0000000e+00
+ .0000000e+00 -.1138958e+09 .2284693e+09 -.1110422e+09 .0000000e+00
+ .0000000e+00 -.6060955e+08 .1126212e+09 -.4850979e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.2391785e+08 .3480520e+07 .2739837e+08 .0000000e+00
+ .0000000e+00 -.1855106e+07 -.5966605e+07 -.4111499e+07 .0000000e+00
+ .0000000e+00 .2577296e+08 .2486086e+07 -.2328688e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 -.4569685e+08 .1058615e+09 -.8114649e+08
+ .0000000e+00 .0000000e+00 -.1110422e+09 .2277521e+09 -.1023531e+09
+ .0000000e+00 .0000000e+00 -.6383463e+08 .1075339e+09 -.3707416e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.2260163e+08 .8226099e+07 .3082772e+08
+ .0000000e+00 .0000000e+00 -.4111499e+07 -.1410188e+08 -.9990385e+07
+ .0000000e+00 .0000000e+00 .2671312e+08 .5875785e+07 -.2083734e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.5644212e+08 .1144553e+09 -.6060955e+08 .0000000e+00 .0000000e+00
+ -.1149657e+09 .2301010e+09 -.1139830e+09 .0000000e+00 .0000000e+00
+ -.5811705e+08 .1144933e+09 -.5493231e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2477196e+08 .1001005e+07 .2577296e+08 .0000000e+00 .0000000e+00
+ -.3648702e+06 -.1601608e+07 -.1236738e+07 .0000000e+00 .0000000e+00
+ .2513683e+08 .6006029e+06 -.2453622e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.5355661e+08 .1126212e+09 -.6383463e+08 .0000000e+00
+ .0000000e+00 -.1139830e+09 .2279620e+09 -.1114623e+09 .0000000e+00
+ .0000000e+00 -.5916407e+08 .1128241e+09 -.5140673e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.2422704e+08 .2486086e+07 .2671312e+08 .0000000e+00
+ .0000000e+00 -.1236738e+07 -.3977737e+07 -.2740999e+07 .0000000e+00
+ .0000000e+00 .2546378e+08 .1491651e+07 -.2397213e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.4850979e+08 .1075339e+09 -.7261747e+08
+ .0000000e+00 .0000000e+00 -.1114623e+09 .2249648e+09 -.1047203e+09
+ .0000000e+00 .0000000e+00 -.6060164e+08 .1086488e+09 -.4323591e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.2328688e+08 .5875785e+07 .2916266e+08
+ .0000000e+00 .0000000e+00 -.2740999e+07 -.9401256e+07 -.6660257e+07
+ .0000000e+00 .0000000e+00 .2602787e+08 .3525471e+07 -.2250240e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.5685856e+08 .1144933e+09 -.5916407e+08 .0000000e+00 .0000000e+00
+ -.1149703e+09 .2300441e+09 -.1140353e+09 .0000000e+00 .0000000e+00
+ -.5769602e+08 .1145123e+09 -.5632545e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2486317e+08 .6006029e+06 .2546378e+08 .0000000e+00 .0000000e+00
+ -.1824351e+06 -.8008039e+06 -.6183688e+06 .0000000e+00 .0000000e+00
+ .2504561e+08 .2002010e+06 -.2484541e+08 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.5493231e+08 .1128241e+09 -.6060164e+08 .0000000e+00
+ .0000000e+00 -.1140353e+09 .2276577e+09 -.1117143e+09 .0000000e+00
+ .0000000e+00 -.5773604e+08 .1129256e+09 -.5438769e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.2453622e+08 .1491651e+07 .2602787e+08 .0000000e+00
+ .0000000e+00 -.6183688e+06 -.1988868e+07 -.1370500e+07 .0000000e+00
+ .0000000e+00 .2515459e+08 .4972171e+06 -.2465738e+08 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.5140673e+08 .1086488e+09 -.6456189e+08
+ .0000000e+00 .0000000e+00 -.1117143e+09 .2232924e+09 -.1061407e+09
+ .0000000e+00 .0000000e+00 -.5745266e+08 .1092063e+09 -.4987112e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.2397213e+08 .3525471e+07 .2749760e+08
+ .0000000e+00 .0000000e+00 -.1370500e+07 -.4700628e+07 -.3330128e+07
+ .0000000e+00 .0000000e+00 .2534262e+08 .1175157e+07 -.2416747e+08
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ -.2468074e+08 .2531926e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.5473053e+06 -.5473053e+06 .0000000e+00 .0000000e+00 .0000000e+00
+ .2522804e+08 -.2477196e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .2174439e+08 .2174439e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ -.2468074e+08 .1401407e+07 .2608215e+08 .0000000e+00 .0000000e+00
+ -.5473053e+06 -.2402412e+07 -.1855106e+07 .0000000e+00 .0000000e+00
+ .2522804e+08 .1001005e+07 -.2422704e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+ .2174439e+08 .4366323e+08 .2191884e+08 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 -.2391785e+08 .3480520e+07 .2739837e+08 .0000000e+00
+ .0000000e+00 -.1855106e+07 -.5966605e+07 -.4111499e+07 .0000000e+00
+ .0000000e+00 .2577296e+08 .2486086e+07 -.2328688e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+ .0000000e+00 .2191884e+08 .4427633e+08 .2235749e+08 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 -.2260163e+08 .8226099e+07 .3082772e+08
+ .0000000e+00 .0000000e+00 -.4111499e+07 -.1410188e+08 -.9990385e+07
+ .0000000e+00 .0000000e+00 .2671312e+08 .5875785e+07 -.2083734e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+ .0000000e+00 .0000000e+00 .2235749e+08 .4578053e+08 .2342304e+08
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2477196e+08 .2522804e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.3648702e+06 -.3648702e+06 .0000000e+00 .0000000e+00 .0000000e+00
+ .2513683e+08 -.2486317e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .2174439e+08 .2174439e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2477196e+08 .1001005e+07 .2577296e+08 .0000000e+00 .0000000e+00
+ -.3648702e+06 -.1601608e+07 -.1236738e+07 .0000000e+00 .0000000e+00
+ .2513683e+08 .6006029e+06 -.2453622e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+ .2174439e+08 .4366323e+08 .2191884e+08 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.2422704e+08 .2486086e+07 .2671312e+08 .0000000e+00
+ .0000000e+00 -.1236738e+07 -.3977737e+07 -.2740999e+07 .0000000e+00
+ .0000000e+00 .2546378e+08 .1491651e+07 -.2397213e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+ .0000000e+00 .2191884e+08 .4427633e+08 .2235749e+08 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.2328688e+08 .5875785e+07 .2916266e+08
+ .0000000e+00 .0000000e+00 -.2740999e+07 -.9401256e+07 -.6660257e+07
+ .0000000e+00 .0000000e+00 .2602787e+08 .3525471e+07 -.2250240e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+ .0000000e+00 .0000000e+00 .2235749e+08 .4578053e+08 .2342304e+08
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2486317e+08 .2513683e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1824351e+06 -.1824351e+06 .0000000e+00 .0000000e+00 .0000000e+00
+ .2504561e+08 -.2495439e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ .2174439e+08 .2174439e+08 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.1087220e+08 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.2486317e+08 .6006029e+06 .2546378e+08 .0000000e+00 .0000000e+00
+ -.1824351e+06 -.8008039e+06 -.6183688e+06 .0000000e+00 .0000000e+00
+ .2504561e+08 .2002010e+06 -.2484541e+08 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+ .2174439e+08 .4366323e+08 .2191884e+08 .0000000e+00 .0000000e+00
+ -.1087220e+08 -.2183162e+08 -.1095942e+08 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.2453622e+08 .1491651e+07 .2602787e+08 .0000000e+00
+ .0000000e+00 -.6183688e+06 -.1988868e+07 -.1370500e+07 .0000000e+00
+ .0000000e+00 .2515459e+08 .4972171e+06 -.2465738e+08 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+ .0000000e+00 .2191884e+08 .4427633e+08 .2235749e+08 .0000000e+00
+ .0000000e+00 -.1095942e+08 -.2213817e+08 -.1117875e+08 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.2397213e+08 .3525471e+07 .2749760e+08
+ .0000000e+00 .0000000e+00 -.1370500e+07 -.4700628e+07 -.3330128e+07
+ .0000000e+00 .0000000e+00 .2534262e+08 .1175157e+07 -.2416747e+08
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+ .0000000e+00 .0000000e+00 .2235749e+08 .4578053e+08 .2342304e+08
+ .0000000e+00 .0000000e+00 -.1117875e+08 -.2289026e+08 -.1171152e+08
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01 .0000000e+00
+
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00
+ .0000000e+00 .0000000e+00 .0000000e+00 .0000000e+00 .1000000e+01
+
+
+
diff --git a/sys/src/cmd/postscript/tests/postplot1 b/sys/src/cmd/postscript/tests/postplot1
new file mode 100755
index 000000000..edd2a504c
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postplot1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/postprint1 b/sys/src/cmd/postscript/tests/postprint1
new file mode 100755
index 000000000..686040f98
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/postprint1
@@ -0,0 +1,13 @@
+
+An Ascii test file for
+ postprint. Probably
+
+
+
+
+should have more than one line, and
+a few blank ones too.
+
+
+
+That's it.
diff --git a/sys/src/cmd/postscript/tests/posttek1 b/sys/src/cmd/postscript/tests/posttek1
new file mode 100755
index 000000000..d45e68d1f
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/posttek1
Binary files differ
diff --git a/sys/src/cmd/postscript/tests/runtests b/sys/src/cmd/postscript/tests/runtests
new file mode 100755
index 000000000..0c349b943
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/runtests
@@ -0,0 +1,72 @@
+#
+# Runs the test files that you'll find in this directory. You may want to change
+# the definitions of PRINT and BINDIR. The default definition of BINDIR assumes
+# the translators are installed in /usr/lbin/postscript, while PRINT just writes
+# everything to stdout. Unrecognized options (ie. options other than -P and -B)
+# are passed along to the translator.
+#
+# For example, if postio is installed in /usr/lbin/postscript, the following runs
+# the dmd bitmap translator on the test file ./postdmd1 and sends the output to
+# the printer attached to /dev/tty01.
+#
+# runtests -P'/usr/lbin/postscript/postio -l /dev/tty01' -pland postdmd
+#
+
+OPTIONS=
+PRINT=cat
+BINDIR=/usr/lbin/postscript
+
+for i do
+ case $i in
+ -P*) PRINT=`echo $i | sed s/-P//`;;
+
+ -B*) BINDIR=`echo $i | sed s/-B//`;;
+
+ -*) OPTIONS="$OPTIONS $i";;
+
+ *) break;;
+ esac
+ shift
+done
+
+for i do
+ for j in ${i}*; do
+ if [ ! -r "$j" ]; then
+ break
+ fi
+ case $j in
+ dpost*)
+ $BINDIR/dpost $OPTIONS $j | $PRINT;;
+
+ postbgi*)
+ $BINDIR/postbgi $OPTIONS $j | $PRINT;;
+
+ posttek*)
+ $BINDIR/posttek $OPTIONS $j | $PRINT;;
+
+ postdmd*)
+ $BINDIR/postdmd $OPTIONS $j | $PRINT;;
+
+ postmd*)
+ $BINDIR/postmd $OPTIONS $j | $PRINT;;
+
+ postdaisy*)
+ $BINDIR/postdaisy $OPTIONS $j | $PRINT;;
+
+ postprint*)
+ $BINDIR/postprint $OPTIONS $j | $PRINT;;
+
+ postplot*)
+ $BINDIR/postplot $OPTIONS $j | $PRINT;;
+
+ postgif*)
+ $BINDIR/postgif $OPTIONS $j | $PRINT;;
+
+ troff*)
+ pic $j | tbl | eqn | troff -mm -Tpost | $BINDIR/dpost $OPTIONS | $PRINT;;
+
+ man*)
+ troff -man -Tpost $j | $BINDIR/dpost $OPTIONS | $PRINT;;
+ esac
+ done
+done
diff --git a/sys/src/cmd/postscript/tests/troff1 b/sys/src/cmd/postscript/tests/troff1
new file mode 100755
index 000000000..0f92ce9b9
--- /dev/null
+++ b/sys/src/cmd/postscript/tests/troff1
@@ -0,0 +1,18 @@
+.EQ
+delim $$
+.EN
+.TS
+doublebox;
+c c
+l l.
+Name Definition
+.sp
+.vs +2p
+Gamma $GAMMA (z) = int sub 0 sup inf t sup {z-1} e sup -t dt$
+Sine $sin (x) = 1 over 2i ( e sup ix - e sup -ix )$
+Error $ roman erf (z) = 2 over sqrt pi int sub 0 sup z e sup {-t sup 2} dt$
+Bessel $ J sub 0 (z) = 1 over pi int sub 0 sup pi cos ( z sin theta ) d theta $
+Zeta $ zeta (s) = sum from k=1 to inf k sup -s ~~( Re~s > 1)$
+.vs -2p
+.sp 2
+.TE
diff --git a/sys/src/cmd/postscript/text2post/mkfile b/sys/src/cmd/postscript/text2post/mkfile
new file mode 100755
index 000000000..65adb4c38
--- /dev/null
+++ b/sys/src/cmd/postscript/text2post/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+
+<../config
+
+COMMONDIR=../common
+
+TARG=text2post
+
+OFILES=text2post.$O\
+
+HFILES=$COMMONDIR/comments.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+</sys/src/cmd/mkone
+
+CFLAGS=-D'PROGRAMVERSION="0.1"' -D'DOROUND=1' -I$COMMONDIR
+
+install:V: $POSTLIB/pjw.char.ps
+
+$POSTLIB/pjw.char.ps: pjw.char.ps
+ cp $prereq $target
diff --git a/sys/src/cmd/postscript/text2post/pjw.char.ps b/sys/src/cmd/postscript/text2post/pjw.char.ps
new file mode 100755
index 000000000..553086189
--- /dev/null
+++ b/sys/src/cmd/postscript/text2post/pjw.char.ps
@@ -0,0 +1,142 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%BoundingBox: 1 -1 199 258
+%%Creator: MetaPost
+%%CreationDate: 1994.06.28:1046
+/pjw1 {
+37 211 moveto
+37 206 lineto
+41 206 lineto
+43 212 lineto
+44 212 46 212 46 210 curveto
+41 198 35 186 35 174 curveto
+50 174 66 175 81 173 curveto
+81 171 lineto
+61 171 lineto
+61 170 lineto
+86 170 lineto
+88 168 89 166 90 164 curveto
+101 164 111 169 122 172 curveto
+139 172 lineto
+125 194 115 219 93 233 curveto
+87 234 80 236 74 235 curveto
+64 229 54 224 46 217 curveto
+45 217 44 217 44 218 curveto
+61 242 lineto
+67 248 72 258 82 258 curveto
+94 257 107 256 119 254 curveto
+134 247 147 239 161 231 curveto
+180 231 lineto
+180 226 174 228 171 226 curveto
+178 220 185 214 192 207 curveto
+189 207 187 207 187 205 curveto
+190 205 193 204 196 203 curveto
+198 193 202 181 193 181 curveto
+193 177 198 175 198 171 curveto
+196 171 194 171 194 169 curveto
+195 165 196 161 198 157 curveto
+194 147 193 135 184 130 curveto
+180 130 lineto
+169 107 lineto
+171 103 176 104 180 104 curveto
+180 93 176 81 166 76 curveto
+162 76 157 77 155 80 curveto
+153 80 152 80 151 80 curveto
+151 71 lineto
+155 71 lineto
+155 68 153 65 152 63 curveto
+145 62 lineto
+141 47 144 31 144 15 curveto
+141 0 lineto
+124 0 107 -1 90 1 curveto
+72 36 lineto
+86 48 105 49 122 54 curveto
+122 54 lineto
+110 61 97 65 84 69 curveto
+81 71 78 73 78 76 curveto
+86 77 93 77 101 77 curveto
+106 79 113 79 113 84 curveto
+95 86 77 87 59 89 curveto
+59 92 61 95 64 95 curveto
+69 88 80 92 89 92 curveto
+95 92 104 90 104 96 curveto
+93 97 lineto
+87 103 82 109 77 115 curveto
+89 116 101 117 113 118 curveto
+99 154 lineto
+90 154 87 144 82 138 curveto
+77 132 73 124 66 120 curveto
+63 120 59 119 59 122 curveto
+62 123 66 123 66 126 curveto
+58 126 50 127 42 128 curveto
+34 137 lineto
+34 140 34 144 31 144 curveto
+30 135 31 125 31 116 curveto
+25 116 22 122 19 127 curveto
+16 131 15 136 12 139 curveto
+5 139 15 127 9 127 curveto
+3 133 3 142 1 150 curveto
+1 158 6 166 9 173 curveto
+18 186 25 199 35 211 curveto
+closepath
+} bind def
+
+/pjw2 {
+27 112 moveto
+40 75 lineto
+40 74 38 73 37 73 curveto
+25 87 lineto
+17 112 lineto
+closepath
+43 154 moveto
+43 167 lineto
+41 167 38 167 39 169 curveto
+57 171 lineto
+74 167 lineto
+74 166 73 165 72 165 curveto
+64 164 56 162 49 158 curveto
+48 154 45 149 49 149 curveto
+51 151 53 152 55 154 curveto
+87 153 lineto
+87 144 80 136 74 129 curveto
+64 128 53 126 43 129 curveto
+37 135 lineto
+37 138 36 141 36 145 curveto
+40 145 lineto
+41 148 42 151 43 154 curveto closepath
+108 145 moveto
+119 145 130 143 141 140 curveto
+146 134 155 129 149 126 curveto
+138 124 126 123 116 129 curveto
+113 134 108 139 108 145 curveto
+closepath
+114 96 moveto
+116 103 118 110 121 117 curveto
+128 117 134 112 139 107 curveto
+139 101 137 96 132 93 curveto
+closepath
+134 162 moveto
+115 162 lineto
+115 162 115 163 115 164 curveto
+134 164 lineto
+closepath
+117 73 moveto
+115 78 121 81 125 85 curveto
+129 85 lineto
+130 83 131 81 131 79 curveto
+128 74 lineto
+124 74 121 73 117 73 curveto closepath
+141 119 moveto
+134 119 126 117 126 123 curveto
+131 123 136 122 141 121 curveto
+closepath
+} bind def
+
+/pw {
+%% pop
+gsave
+pointsize .0022 mul dup scale
+currentpoint translate
+ pjw1 pjw2 eofill
+grestore
+6 0 rmoveto
+} bind def
diff --git a/sys/src/cmd/postscript/text2post/text2post.c b/sys/src/cmd/postscript/text2post/text2post.c
new file mode 100755
index 000000000..9f547f4d3
--- /dev/null
+++ b/sys/src/cmd/postscript/text2post/text2post.c
@@ -0,0 +1,565 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <comments.h>
+#include <path.h>
+
+#define UNKNOWNCHAR "/sys/lib/postscript/prologues/pjw.char.ps"
+
+char *optnames = "a:c:f:l:m:n:o:p:s:t:x:y:P:";
+
+Biobuf *bstdin, *bstdout, *bstderr;
+Biobufhdr *Bstdin, *Bstdout, *Bstderr;
+int char_no = 0; /* character to be done on a line */
+int line_no = 0; /* line number on a page */
+int page_no = 0; /* page number in a document */
+int in_string; /* Boolean, to know whether or not we are inside a Postscript string */
+int spaces = 0;
+int tabs = 0;
+int pages_printed;
+double aspectratio = 1.0;
+int copies = 1;
+double magnification = 1.0;
+int landscape = 0;
+int formsperpage = 1;
+int linesperpage = 66;
+int pointsize = 10;
+double xoffset = .25;
+double yoffset = .25;
+char *passthrough = 0;
+static int pplistmaxsize=0;
+
+unsigned char *pplist=0; /* bitmap list for storing pages to print */
+
+struct strtab {
+ int size;
+ char *str;
+ int used;
+};
+
+struct strtab charcode[256] = {
+ {4, "\\000"}, {4, "\\001"}, {4, "\\002"}, {4, "\\003"},
+ {4, "\\004"}, {4, "\\005"}, {4, "\\006"}, {4, "\\007"},
+ {4, "\\010"}, {4, "\\011"}, {4, "\\012"}, {4, "\\013"},
+ {4, "\\014"}, {4, "\\015"}, {4, "\\016"}, {4, "\\017"},
+ {4, "\\020"}, {4, "\\021"}, {4, "\\022"}, {4, "\\023"},
+ {4, "\\024"}, {4, "\\025"}, {4, "\\026"}, {4, "\\027"},
+ {4, "\\030"}, {4, "\\031"}, {4, "\\032"}, {4, "\\033"},
+ {4, "\\034"}, {4, "\\035"}, {4, "\\036"}, {4, "\\037"},
+ {1, " "}, {1, "!"}, {1, "\""}, {1, "#"},
+ {1, "$"}, {1, "%"}, {1, "&"}, {1, "'"},
+ {2, "\\("}, {2, "\\)"}, {1, "*"}, {1, "+"},
+ {1, ","}, {1, "-"}, {1, "."}, {1, "/"},
+ {1, "0"}, {1, "1"}, {1, "2"}, {1, "3"},
+ {1, "4"}, {1, "5"}, {1, "6"}, {1, "7"},
+ {1, "8"}, {1, "9"}, {1, ":"}, {1, ";"},
+ {1, "<"}, {1, "="}, {1, ">"}, {1, "?"},
+ {1, "@"}, {1, "A"}, {1, "B"}, {1, "C"},
+ {1, "D"}, {1, "E"}, {1, "F"}, {1, "G"},
+ {1, "H"}, {1, "I"}, {1, "J"}, {1, "K"},
+ {1, "L"}, {1, "M"}, {1, "N"}, {1, "O"},
+ {1, "P"}, {1, "Q"}, {1, "R"}, {1, "S"},
+ {1, "T"}, {1, "U"}, {1, "V"}, {1, "W"},
+ {1, "X"}, {1, "Y"}, {1, "Z"}, {1, "["},
+ {2, "\\\\"}, {1, "]"}, {1, "^"}, {1, "_"},
+ {1, "`"}, {1, "a"}, {1, "b"}, {1, "c"},
+ {1, "d"}, {1, "e"}, {1, "f"}, {1, "g"},
+ {1, "h"}, {1, "i"}, {1, "j"}, {1, "k"},
+ {1, "l"}, {1, "m"}, {1, "n"}, {1, "o"},
+ {1, "p"}, {1, "q"}, {1, "r"}, {1, "s"},
+ {1, "t"}, {1, "u"}, {1, "v"}, {1, "w"},
+ {1, "x"}, {1, "y"}, {1, "z"}, {1, "{"},
+ {1, "|"}, {1, "}"}, {1, "~"}, {4, "\\177"},
+ {4, "\\200"}, {4, "\\201"}, {4, "\\202"}, {4, "\\203"},
+ {4, "\\204"}, {4, "\\205"}, {4, "\\206"}, {4, "\\207"},
+ {4, "\\210"}, {4, "\\211"}, {4, "\\212"}, {4, "\\213"},
+ {4, "\\214"}, {4, "\\215"}, {4, "\\216"}, {4, "\\217"},
+ {4, "\\220"}, {4, "\\221"}, {4, "\\222"}, {4, "\\223"},
+ {4, "\\224"}, {4, "\\225"}, {4, "\\226"}, {4, "\\227"},
+ {4, "\\230"}, {4, "\\231"}, {4, "\\232"}, {4, "\\233"},
+ {4, "\\234"}, {4, "\\235"}, {4, "\\236"}, {4, "\\237"},
+ {4, "\\240"}, {4, "\\241"}, {4, "\\242"}, {4, "\\243"},
+ {4, "\\244"}, {4, "\\245"}, {4, "\\246"}, {4, "\\247"},
+ {4, "\\250"}, {4, "\\251"}, {4, "\\252"}, {4, "\\253"},
+ {4, "\\254"}, {4, "\\255"}, {4, "\\256"}, {4, "\\257"},
+ {4, "\\260"}, {4, "\\261"}, {4, "\\262"}, {4, "\\263"},
+ {4, "\\264"}, {4, "\\265"}, {4, "\\266"}, {4, "\\267"},
+ {4, "\\270"}, {4, "\\271"}, {4, "\\272"}, {4, "\\273"},
+ {4, "\\274"}, {4, "\\275"}, {4, "\\276"}, {4, "\\277"},
+ {4, "\\300"}, {4, "\\301"}, {4, "\\302"}, {4, "\\303"},
+ {4, "\\304"}, {4, "\\305"}, {4, "\\306"}, {4, "\\307"},
+ {4, "\\310"}, {4, "\\311"}, {4, "\\312"}, {4, "\\313"},
+ {4, "\\314"}, {4, "\\315"}, {4, "\\316"}, {4, "\\317"},
+ {4, "\\320"}, {4, "\\321"}, {4, "\\322"}, {4, "\\323"},
+ {4, "\\324"}, {4, "\\325"}, {4, "\\326"}, {4, "\\327"},
+ {4, "\\330"}, {4, "\\331"}, {4, "\\332"}, {4, "\\333"},
+ {4, "\\334"}, {4, "\\335"}, {4, "\\336"}, {4, "\\337"},
+ {4, "\\340"}, {4, "\\341"}, {4, "\\342"}, {4, "\\343"},
+ {4, "\\344"}, {4, "\\345"}, {4, "\\346"}, {4, "\\347"},
+ {4, "\\350"}, {4, "\\351"}, {4, "\\352"}, {4, "\\353"},
+ {4, "\\354"}, {4, "\\355"}, {4, "\\356"}, {4, "\\357"},
+ {4, "\\360"}, {4, "\\361"}, {4, "\\362"}, {4, "\\363"},
+ {4, "\\364"}, {4, "\\365"}, {4, "\\366"}, {4, "\\367"},
+ {4, "\\370"}, {4, "\\371"}, {4, "\\372"}, {4, "\\373"},
+ {4, "\\374"}, {4, "\\375"}, {4, "\\376"}, {4, "\\377"}
+};
+
+#define FONTABSIZE 0x27
+
+struct strtab fontname[FONTABSIZE] = {
+ {19, "LucidaSansUnicode00", 0},
+ {19, "LucidaSansUnicode01", 0},
+ {19, "LucidaSansUnicode02", 0},
+ {19, "LucidaSansUnicode03", 0},
+ {19, "LucidaSansUnicode04", 0},
+ {19, "LucidaSansUnicode05", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {0, "", 0},
+ {19, "LucidaSansUnicode20", 0},
+ {19, "LucidaSansUnicode21", 0},
+ {19, "LucidaSansUnicode22", 0},
+ {0, "", 0},
+ {19, "LucidaSansUnicode24", 0},
+ {19, "LucidaSansUnicode25", 0},
+ {7, "Courier", 0}
+};
+
+/* This was taken from postprint */
+
+int
+cat(char *filename) {
+ Biobuf *bfile;
+ Biobufhdr *Bfile;
+ int n;
+ static char buf[Bsize];
+
+ bstdin = Bopen(filename, 0);
+ if (bstdin == 0) {
+ return(1);
+ }
+ Bstdin = &(bstdin->Biobufhdr);
+ if ((bfile = Bopen(filename, OREAD)) == 0) {
+ return(1);
+ }
+ Bfile = &(bfile->Biobufhdr);
+ while ((n=Bread(Bfile, buf, Bsize)) > 0) {
+ if (Bwrite(Bstdout, buf, n) != n) {
+ return(1);
+ }
+ }
+ if (n != 0) {
+ return(1);
+ }
+ return(0);
+}
+
+void
+prologues(void) {
+ char *ts;
+ int tabstop;
+
+ Bprint(Bstdout, "%s", CONFORMING);
+ Bprint(Bstdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ Bprint(Bstdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ Bprint(Bstdout, "%s %s\n", PAGES, ATEND);
+ Bprint(Bstdout, "%s", ENDCOMMENTS);
+
+ if (cat(POSTPRINT)) {
+ Bprint(Bstderr, "can't read %s", POSTPRINT);
+ exits("prologue");
+ }
+
+ if (DOROUND)
+ cat(ROUNDPAGE);
+
+ tabstop = 0;
+ ts = getenv("tabstop");
+ if(ts != nil)
+ tabstop = strtol(ts, nil, 0);
+ if(tabstop == 0)
+ tabstop = 8;
+ Bprint(Bstdout, "/f {findfont pointsize scalefont setfont} bind def\n");
+ Bprint(Bstdout, "/tabwidth /Courier f (");
+ while(tabstop--)
+ Bputc(Bstdout, 'n');
+ Bprint(Bstdout, ") stringwidth pop def\n");
+ Bprint(Bstdout, "/tab {tabwidth 0 ne {currentpoint 3 1 roll exch tabwidth mul add tabwidth\n");
+ Bprint(Bstdout, "\tdiv truncate tabwidth mul exch moveto} if} bind def\n");
+ Bprint(Bstdout, "/spacewidth /%s f ( ) stringwidth pop def\n", fontname[0].str);
+ Bprint(Bstdout, "/sp {spacewidth mul 0 rmoveto} bind def\n");
+ Bprint(Bstdout, "%s", ENDPROLOG);
+ Bprint(Bstdout, "%s", BEGINSETUP);
+ Bprint(Bstdout, "mark\n");
+
+ if (formsperpage > 1) {
+ Bprint(Bstdout, "%s %d\n", FORMSPERPAGE, formsperpage);
+ Bprint(Bstdout, "/formsperpage %d def\n", formsperpage);
+ }
+ if (aspectratio != 1) Bprint(Bstdout, "/aspectratio %g def\n", aspectratio);
+ if (copies != 1) Bprint(Bstdout, "/#copies %d store\n", copies);
+ if (landscape) Bprint(Bstdout, "/landscape true def\n");
+ if (magnification != 1) Bprint(Bstdout, "/magnification %s def\n", magnification);
+ if (pointsize != 10) Bprint(Bstdout, "/pointsize %d def\n", pointsize);
+ if (xoffset != .25) Bprint(Bstdout, "/xoffset %g def\n", xoffset);
+ if (yoffset != .25) Bprint(Bstdout, "/yoffset %g def\n", yoffset);
+ cat(ENCODINGDIR"/Latin1.enc");
+ if (passthrough != 0) Bprint(Bstdout, "%s\n", passthrough);
+ Bprint(Bstdout, "setup\n");
+ if (formsperpage > 1) {
+ cat(FORMFILE);
+ Bprint(Bstdout, "%d setupforms \n", formsperpage);
+ }
+ if (cat(UNKNOWNCHAR))
+ Bprint(Bstderr, "cannot open %s\n", UNKNOWNCHAR);
+ Bprint(Bstdout, "%s", ENDSETUP);
+}
+
+int
+pageon(void) {
+ if (pplist == 0 && page_no != 0) return(1); /* no page list, print all pages */
+ if (page_no/8 < pplistmaxsize && (pplist[page_no/8] & 1<<(page_no%8)))
+ return(1);
+ else
+ return(0);
+}
+
+void
+startpage(void) {
+ ++char_no;
+ ++line_no;
+ ++page_no;
+ if (pageon()) {
+ ++pages_printed;
+ Bprint(Bstdout, "%s %d %d\n", PAGE, page_no, pages_printed);
+ Bprint(Bstdout, "/saveobj save def\n");
+ Bprint(Bstdout, "mark\n");
+ Bprint(Bstdout, "%d pagesetup\n", pages_printed);
+ }
+}
+
+void
+endpage(void) {
+ line_no = 0;
+ char_no = 0;
+ if (pageon()) {
+ Bprint(Bstdout, "cleartomark\n");
+ Bprint(Bstdout, "showpage\n");
+ Bprint(Bstdout, "saveobj restore\n");
+ Bprint(Bstdout, "%s %d %d\n", ENDPAGE, page_no, pages_printed);
+ }
+}
+
+void
+startstring(void) {
+ if (!in_string) {
+ if (pageon()) Bprint(Bstdout, "(");
+ in_string = 1;
+ }
+}
+
+void
+endstring(void) {
+ if (in_string) {
+ if (pageon()) Bprint(Bstdout, ") show ");
+ in_string = 0;
+ }
+}
+
+void
+prspace(void) {
+ if (spaces) {
+ endstring();
+ if (pageon()) Bprint(Bstdout, "%d sp ", spaces);
+ spaces = 0;
+ }
+}
+
+void
+prtab(void) {
+ if (tabs) {
+ endstring();
+ if (pageon()) Bprint(Bstdout, "%d tab ", tabs);
+ tabs = 0;
+ }
+}
+
+void
+txt2post(void) {
+ int lastfont = -1;
+ int lastchar = -1;
+ int thisfont, thischar;
+ long r;
+
+ in_string = 0;
+ char_no = 0;
+ line_no = 0;
+ page_no = 0;
+ spaces = 0;
+ fontname[0].used++;
+ while ((r=Bgetrune(Bstdin)) >= 0) {
+ thischar = r & 0xff;
+ thisfont = (r>>8) & 0xff;
+
+ if (line_no == 0 && char_no == 0)
+ startpage();
+
+ if (line_no == 1 && char_no == 1) {
+ if (pageon()) Bprint(Bstdout, " /%s f\n", fontname[thisfont].str);
+ lastfont = thisfont;
+ }
+
+ switch (r) {
+ case ' ':
+ prtab();
+ if (lastfont > 0) {
+ spaces++;
+ continue;
+ }
+ break;
+ case '\n':
+ case '\f':
+ startstring();
+ if (pageon()) Bprint(Bstdout, ")l\n");
+ char_no = 1;
+ in_string = 0;
+ spaces = 0;
+ tabs = 0;
+ if (++line_no > linesperpage || r == '\f') {
+ endpage();
+ }
+ lastchar = -1;
+ continue;
+ case '\t':
+ prspace();
+ tabs++;
+ char_no++;
+ lastchar = -1;
+ continue;
+ case '\b':
+ /* just toss out backspaces for now */
+ if (lastchar != -1) {
+ endstring();
+ if (pageon()) Bprint(Bstdout, "(%s) stringwidth pop neg 0 rmoveto ", charcode[lastchar].str);
+ }
+ char_no++;
+ lastchar = -1;
+ continue;
+ }
+
+ /* do something if font is out of table range */
+ if (thisfont>=FONTABSIZE || fontname[thisfont].size == 0) {
+ prspace();
+ prtab();
+ endstring();
+ Bprint(Bstdout, "pw ");
+ char_no++;
+ lastchar = -1;
+ continue;
+ }
+
+ if (thisfont != lastfont) {
+ endstring();
+ if (pageon()) {
+ Bprint(Bstdout, "/%s f\n", fontname[thisfont].str);
+ }
+ fontname[thisfont].used++;
+ }
+ prspace();
+ prtab();
+ startstring();
+ if (pageon()) Bprint(Bstdout, "%s", charcode[thischar].str);
+/* if (pageon()) Bprint(Bstdout, "%2.2x", thischar); /* try hex strings*/
+ char_no++;
+ lastchar = thischar;
+ lastfont = thisfont;
+ }
+ if (line_no != 0 || char_no != 0) {
+ if (char_no != 1) {
+ Bprint(Bstderr, "premature EOF: newline appended\n");
+ startstring();
+ if (pageon()) Bprint(Bstdout, ")l\n");
+ }
+ endpage();
+ }
+}
+
+void
+pagelist(char *list) {
+ char c;
+ int n, m;
+ int state, start, end;
+
+ if (list == 0) return;
+ state = 1;
+ while ((c=*list) != '\0') {
+ n = 0;
+ while (isdigit(c)) {
+ n = n * 10 + c - '0';
+ c = *++list;
+ }
+ switch (state) {
+ case 1:
+ start = n;
+ case 2:
+ if (n/8+1 > pplistmaxsize) {
+ pplistmaxsize = n/8+1;
+ if ((pplist = realloc(pplist, n/8+1)) == 0) {
+ Bprint(Bstderr, "cannot allocate memory for page list\n");
+ exits("malloc");
+ }
+ }
+ for (m=start; m<=n; m++)
+ pplist[m/8] |= 1<<(m%8);
+ break;
+ }
+ switch (c) {
+ case '-':
+ state = 2;
+ list++;
+ break;
+ case ',':
+ state = 1;
+ list++;
+ break;
+ case '\0':
+ break;
+ }
+ }
+}
+
+void
+finish(void) {
+ int i;
+
+ Bprint(Bstdout, "%s", TRAILER);
+ Bprint(Bstdout, "done\n");
+ Bprint(Bstdout, "%s", DOCUMENTFONTS);
+
+ for (i=0; i<FONTABSIZE; i++)
+ if (fontname[i].used)
+ Bprint(Bstdout, " %s", fontname[i].str);
+ Bprint(Bstdout, "\n");
+
+ Bprint(Bstdout, "%s %d\n", PAGES, pages_printed);
+
+}
+
+main(int argc, char *argv[]) {
+ int i;
+ char *t;
+ Biobuf *input;
+
+ if ((bstderr = (Biobuf *)malloc(sizeof(Biobuf))) == nil)
+ exits("malloc");
+ if (Binit(bstderr, 2, OWRITE) == Beof)
+ exits("Binit");
+ Bstderr = &(bstderr->Biobufhdr);
+
+ if ((bstdout = (Biobuf *)malloc(sizeof(Biobuf))) == nil)
+ exits("malloc");
+ if (Binit(bstdout, 1, OWRITE) == Beof)
+ exits("Binit");
+ Bstdout = &(bstdout->Biobufhdr);
+
+ ARGBEGIN{
+ case 'a': /* aspect ratio */
+ aspectratio = atof(ARGF());
+ break;
+ case 'c': /* copies */
+ copies = atoi(ARGF());
+ break;
+ case 'f': /* primary font, for now */
+ t = ARGF();
+ fontname[0].str = malloc(strlen(t)+1);
+ strcpy(fontname[0].str, t);
+ break;
+ case 'l': /* lines per page */
+ linesperpage = atoi(ARGF());
+ break;
+ case 'm': /* magnification */
+ magnification = atof(ARGF());
+ break;
+ case 'n': /* forms per page */
+ formsperpage = atoi(ARGF());
+ break;
+ case 'o': /* output page list */
+ pagelist(ARGF());
+ break;
+ case 'p': /* landscape or portrait mode */
+ if ( ARGF()[0] == 'l' )
+ landscape = 1;
+ else
+ landscape = 0;
+ break;
+ case 's': /* point size */
+ pointsize = atoi(ARGF());
+ break;
+ case 'x': /* shift things horizontally */
+ xoffset = atof(ARGF());
+ break;
+
+ case 'y': /* and vertically on the page */
+ yoffset = atof(ARGF());
+ break;
+ case 'P': /* PostScript pass through */
+ t = ARGF();
+ i = strlen(t) + 1;
+ passthrough = malloc(i);
+ if (passthrough == 0) {
+ Bprint(Bstderr, "cannot allocate memory for argument string\n");
+ exits("malloc");
+ }
+ strncpy(passthrough, t, i);
+ break;
+ default: /* don't know what to do for ch */
+ Bprint(Bstderr, "unknown option %C\n", ARGC());
+ break;
+ }ARGEND;
+ prologues();
+ if (argc <= 0) {
+ if ((bstdin = (Biobuf *)malloc(sizeof(Biobuf))) == nil)
+ exits("malloc");
+ if (Binit(bstdin, 0, OREAD) == Beof) {
+ fprint(2, "cannot Binit stdin\n");
+ exits("Binit");
+ }
+ Bstdin = &(bstdin->Biobufhdr);
+ txt2post();
+ }
+ for (i=0; i<argc; i++) {
+ bstdin = Bopen(argv[i], 0);
+ if (bstdin == 0) {
+ fprint(2, "cannot open file %s\n", argv[i]);
+ continue;
+ }
+ Bstdin = &(bstdin->Biobufhdr);
+ txt2post();
+ }
+ finish();
+ exits("");
+}
diff --git a/sys/src/cmd/postscript/tr2post/Bgetfield.c b/sys/src/cmd/postscript/tr2post/Bgetfield.c
new file mode 100755
index 000000000..e4305c6ea
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/Bgetfield.c
@@ -0,0 +1,153 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+int
+isspace(Rune r)
+{
+ return(r==' ' || r=='\t' || r=='\n' || r == '\r' || r=='\f');
+}
+
+int
+Bskipws(Biobufhdr *bp) {
+ int r;
+ char c[UTFmax];
+ int sindex = 0;
+
+ /* skip over initial white space */
+ do {
+ r = Bgetrune(bp);
+ if (r == '\n') inputlineno++;
+ sindex++;
+ } while (r>=0 && isspace(r));
+ if (r<0) {
+ return(-1);
+ } else if (!isspace(r)) {
+ Bungetrune(bp);
+ --sindex;
+ }
+ return(sindex);
+}
+
+int
+asc2dig(char c, int base) {
+ if (c >= '0' && c <= '9')
+ if (base == 8 && c > '7') return(-1);
+ else return(c - '0');
+
+ if (base == 16)
+ if (c >= 'a' && c <= 'f') return(10 + c - 'a');
+ else if (c >= 'A' && c <= 'F') return(10 + c - 'A');
+
+ return(-1);
+}
+
+/* get a string of type: "d" for decimal integer, "u" for unsigned,
+ * "s" for string", "c" for char,
+ * return the number of characters gotten for the field. If nothing
+ * was gotten and the end of file was reached, a negative value
+ * from the Bgetrune is returned.
+ */
+
+int
+Bgetfield(Biobufhdr *bp, int type, void *thing, int size) {
+ int r;
+ Rune R;
+ char c[UTFmax];
+ int sindex = 0, i, j, n = 0;
+ int negate = 0;
+ int base = 10;
+ BOOLEAN bailout = FALSE;
+ int dig;
+ unsigned int u = 0;
+
+ /* skip over initial white space */
+ if (Bskipws(bp) < 0)
+ return(-1);
+
+ switch (type) {
+ case 'd':
+ while (!bailout && (r = Bgetrune(bp))>=0) {
+ switch (sindex++) {
+ case 0:
+ switch (r) {
+ case '-':
+ negate = 1;
+ continue;
+ case '+':
+ continue;
+ case '0':
+ base = 8;
+ continue;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ if ((r == 'x' || r == 'X') && base == 8) {
+ base = 16;
+ continue;
+ }
+ }
+ if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
+ else n = dig + (n * base);
+ }
+ if (r < 0) return(-1);
+ *(int *)thing = (negate)?-n:n;
+ Bungetrune(bp);
+ break;
+ case 'u':
+ while (!bailout && (r = Bgetrune(bp))>=0) {
+ switch (sindex++) {
+ case 0:
+ if (*c == '0') {
+ base = 8;
+ continue;
+ }
+ break;
+ case 1:
+ if ((r == 'x' || r == 'X') && base == 8) {
+ base = 16;
+ continue;
+ }
+ }
+ if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
+ else u = dig + (n * base);
+ }
+ *(int *)thing = u;
+ if (r < 0) return(-1);
+ Bungetrune(bp);
+ break;
+ case 's':
+ j = 0;
+ while ((size>j+UTFmax) && (r = Bgetrune(bp))>=0 && !isspace(r)) {
+ R = r;
+ i = runetochar(&(((char *)thing)[j]), &R);
+ j += i;
+ sindex++;
+ }
+ ((char *)thing)[j++] = '\0';
+ if (r < 0) return(-1);
+ Bungetrune(bp);
+ break;
+ case 'r':
+ if ((r = Bgetrune(bp))>=0) {
+ *(Rune *)thing = r;
+ sindex++;
+ return(sindex);
+ }
+ if (r <= 0) return(-1);
+ Bungetrune(bp);
+ break;
+ default:
+ return(-2);
+ }
+ if (r < 0 && sindex == 0)
+ return(r);
+ else if (bailout && sindex == 1) {
+ return(0);
+ } else
+ return(sindex);
+}
diff --git a/sys/src/cmd/postscript/tr2post/chartab.c b/sys/src/cmd/postscript/tr2post/chartab.c
new file mode 100755
index 000000000..f2baa804a
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/chartab.c
@@ -0,0 +1,459 @@
+/* Unicode | PostScript
+ * start end | offset font name
+ * 0x0000 0x00ff 0x00 LucidaSansUnicode00
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+/* Postscript font names, e.g., `LucidaSansUnicode00'
+ * names may only be added because reference to the
+ * names is made by indexing into this table.
+ */
+static struct pfnament *pfnafontmtab = 0;
+static int pfnamcnt = 0;
+int curpostfontid = -1;
+int curfontsize = -1;
+int curtrofffontid = -1;
+static int curfontpos = -1;
+static int fontheight = 0;
+static int fontslant = 0;
+
+/* This is troffs mounted font table. It is an anachronism resulting
+ * from the design of the APS typesetter. fontmnt is the
+ * number of positions available. fontmnt is really 11, but
+ * should not be limited.
+ */
+int fontmnt = 0;
+char **fontmtab;
+
+struct troffont *troffontab = 0;
+
+int troffontcnt = 0;
+
+void
+mountfont(int pos, char *fontname) {
+ int i;
+
+ if (debug) Bprint(Bstderr, "mountfont(%d, %s)\n", pos, fontname);
+ if (pos < 0 || pos >= fontmnt)
+ error(FATAL, "cannot mount a font at position %d,\n can only mount into postions 0-%d\n",
+ pos, fontmnt-1);
+
+ i = strlen(fontname);
+ fontmtab[pos] = galloc(fontmtab[pos], i+1, "mountfont():fontmtab");
+ strcpy(fontmtab[pos], fontname);
+ if (curfontpos == pos) curfontpos = -1;
+}
+
+void
+settrfont(void) {
+ if (curfontpos == fontpos) return;
+
+ if (fontmtab[fontpos] == 0)
+ error(FATAL, "Font at position %d was not initialized, botch!\n", fontpos);
+
+ curtrofffontid = findtfn(fontmtab[fontpos], 1);
+ if (debug) Bprint(Bstderr, "settrfont()-> curtrofffontid=%d\n", curtrofffontid);
+ curfontpos = fontpos;
+ if (curtrofffontid < 0) {
+ int i;
+
+ error(WARNING, "fontpos=%d\n", fontpos);
+ for (i=0; i<fontmnt; i++)
+ if (fontmtab[i] == 0)
+ error(WARNING, "fontmtab[%d]=0x0\n", i);
+ else
+ error(WARNING, "fontmtab[%d]=%s\n", i, fontmtab[i]);
+ exits("settrfont()");
+ }
+}
+
+void
+setpsfont(int psftid, int fontsize) {
+ if (psftid == curpostfontid && fontsize == curfontsize) return;
+ if (psftid >= pfnamcnt)
+ error(FATAL, "Postscript font index=%d used but not defined, there are only %d fonts\n",
+ psftid, pfnamcnt);
+
+ endstring();
+ if (pageon()) {
+ Bprint(Bstdout, "%d /%s f\n", fontsize, pfnafontmtab[psftid].str);
+ if ( fontheight != 0 || fontslant != 0 )
+ Bprint(Bstdout, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : fontsize);
+ pfnafontmtab[psftid].used = 1;
+ curpostfontid = psftid;
+ curfontsize = fontsize;
+ }
+}
+
+/* find index of PostScript font name in table
+ * returns -1 if name is not in table
+ * If insflg is not zero
+ * and the name is not found in the table, insert it.
+ */
+int
+findpfn(char *fontname, int insflg) {
+ char *tp;
+ int i;
+
+ for (i=0; i<pfnamcnt; i++) {
+ if (strcmp(pfnafontmtab[i].str, fontname) == 0)
+ return(i);
+ }
+ if (insflg) {
+ tp = galloc(pfnafontmtab, sizeof(struct pfnament)*(pfnamcnt+1), "findpfn():pfnafontmtab");
+ if (tp == 0)
+ return(-2);
+ pfnafontmtab = (struct pfnament *)tp;
+ i = strlen(fontname);
+ pfnafontmtab[pfnamcnt].str = galloc(0, i+1, "findpfn():pfnafontmtab[].str");
+ strncpy(pfnafontmtab[pfnamcnt].str, fontname, i);
+ pfnafontmtab[pfnamcnt].str[i] = '\0';
+ pfnafontmtab[pfnamcnt].used = 0;
+ return(pfnamcnt++);
+ }
+ return(-1);
+}
+
+char postroffdirname[] = "/sys/lib/postscript/troff"; /* "/sys/lib/postscript/troff/"; */
+char troffmetricdirname[] = "/sys/lib/troff/font"; /* "/sys/lib/troff/font/devutf/"; */
+
+int
+readpsfontdesc(char *fontname, int trindex) {
+ static char *filename = 0;
+ Biobuf *bfd;
+ Biobufhdr *Bfd;
+ int warn = 0, errorflg = 0, line =1, rv;
+ int start, end, offset;
+ int startfont, endfont, startchar, endchar, i, pfid;
+ char psfontnam[128];
+ struct troffont *tp;
+ struct charent *cp[];
+
+ if (debug) Bprint(Bstderr, "readpsfontdesc(%s,%d)\n", fontname, trindex);
+ filename=galloc(filename, strlen(postroffdirname)+1+strlen(fontname)+1, "readpsfontdesc: cannot allocate memory\n");
+ sprint(filename, "%s/%s", postroffdirname, fontname);
+
+ bfd = Bopen(filename, OREAD);
+ if (bfd == 0) {
+ error(WARNING, "cannot open file %s\n", filename);
+ return(0);
+ }
+ Bfd = &(bfd->Biobufhdr);
+
+ do {
+ offset = 0;
+ if ((rv=Bgetfield(Bfd, 'd', &start, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal start value\n", filename, line);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &end, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal end value\n", filename, line);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &offset, 0)) < 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal offset value\n", filename, line);
+ }
+ if ((rv=Bgetfield(Bfd, 's', psfontnam, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal fontname value\n", filename, line);
+ } else if (rv < 0) break;
+ Brdline(Bfd, '\n');
+ if (!errorflg) {
+ struct psfent *psfentp;
+ startfont = RUNEGETGROUP(start);
+ startchar = RUNEGETCHAR(start);
+ endfont = RUNEGETGROUP(end);
+ endchar = RUNEGETCHAR(end);
+ pfid = findpfn(psfontnam, 1);
+ if (startfont != endfont) {
+ error(WARNING, "font descriptions must not cross 256 glyph block boundary\n");
+ errorflg = 1;
+ break;
+ }
+ tp = &(troffontab[trindex]);
+ tp->psfmap = galloc(tp->psfmap, ++(tp->psfmapsize)*sizeof(struct psfent), "readpsfontdesc():psfmap");
+ psfentp = &(tp->psfmap[tp->psfmapsize-1]);
+ psfentp->start = start;
+ psfentp->end = end;
+ psfentp->offset = offset;
+ psfentp->psftid = pfid;
+ if (debug) {
+ Bprint(Bstderr, "\tpsfmap->start=0x%x\n", start);
+ Bprint(Bstderr, "\tpsfmap->end=0x%x\n", end);
+ Bprint(Bstderr, "\tpsfmap->offset=0x%x\n", offset);
+ Bprint(Bstderr, "\tpsfmap->pfid=0x%x\n", pfid);
+ }
+/*
+ for (i=startchar; i<=endchar; i++) {
+ tp->charent[startfont][i].postfontid = pfid;
+ tp->charent[startfont][i].postcharid = i + offset - startchar;
+ }
+ */
+ if (debug) {
+ Bprint(Bstderr, "%x %x ", start, end);
+ if (offset) Bprint(Bstderr, "%x ", offset);
+ Bprint(Bstderr, "%s\n", psfontnam);
+ }
+ line++;
+ }
+ } while(errorflg != 1);
+ Bterm(Bfd);
+ return(1);
+}
+
+int
+readtroffmetric(char *fontname, int trindex) {
+ static char *filename = 0;
+ Biobuf *bfd;
+ Biobufhdr *Bfd;
+ int warn = 0, errorflg = 0, line =1, rv;
+ struct troffont *tp;
+ struct charent **cp;
+ char stoken[128], *str;
+ int ntoken;
+ Rune troffchar, quote;
+ int width, flag, charnum, thisfont, thischar;
+ BOOLEAN specharflag;
+
+ if (debug) Bprint(Bstderr, "readtroffmetric(%s,%d)\n", fontname, trindex);
+ filename=galloc(filename, strlen(troffmetricdirname)+4+strlen(devname)+1+strlen(fontname)+1, "readtroffmetric():filename");
+ sprint(filename, "%s/dev%s/%s", troffmetricdirname, devname, fontname);
+
+ bfd = Bopen(filename, OREAD);
+ if (bfd == 0) {
+ error(WARNING, "cannot open file %s\n", filename);
+ return(0);
+ }
+ Bfd = &(bfd->Biobufhdr);
+ do {
+ /* deal with the few lines at the beginning of the
+ * troff font metric files.
+ */
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ if (debug) {
+ Bprint(Bstderr, "%s\n", stoken);
+ }
+
+ if (strcmp(stoken, "name") == 0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ } else if (strcmp(stoken, "named") == 0) {
+ Brdline(Bfd, '\n');
+ } else if (strcmp(stoken, "fontname") == 0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ } else if (strcmp(stoken, "spacewidth") == 0) {
+ if ((rv=Bgetfield(Bfd, 'd', &ntoken, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ troffontab[trindex].spacewidth = ntoken;
+ thisfont = RUNEGETGROUP(' ');
+ thischar = RUNEGETCHAR(' ');
+ for (cp = &(troffontab[trindex].charent[thisfont][thischar]); *cp != 0; cp = &((*cp)->next))
+ if ((*cp)->name)
+ if (strcmp((*cp)->name, " ") == 0)
+ break;
+
+ if (*cp == 0) *cp = galloc(0, sizeof(struct charent), "readtroffmetric:charent");
+ (*cp)->postfontid = thisfont;
+ (*cp)->postcharid = thischar;
+ (*cp)->troffcharwidth = ntoken;
+ (*cp)->name = galloc(0, 2, "readtroffmetric: char name");
+ (*cp)->next = 0;
+ strcpy((*cp)->name, " ");
+ } else if (strcmp(stoken, "special") == 0) {
+ troffontab[trindex].special = TRUE;
+ } else if (strcmp(stoken, "charset") == 0) {
+ line++;
+ break;
+ }
+ if (!errorflg) {
+ line++;
+ }
+ } while(!errorflg && rv>=0);
+ while(!errorflg && rv>=0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal rune token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if (utflen(stoken) > 1) specharflag = TRUE;
+ else specharflag = FALSE;
+ /* if this character is a quote we have to use the previous characters info */
+ if ((rv=Bgetfield(Bfd, 'r', &quote, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal width or quote token <0x%x> rv=%d\n", filename, line, quote, rv);
+ } else if (rv < 0) break;
+ if (quote == '"') {
+ /* need some code here */
+
+ goto flush;
+ } else {
+ Bungetrune(Bfd);
+ }
+
+ if ((rv=Bgetfield(Bfd, 'd', &width, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal width token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &flag, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal flag token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &charnum, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal character number token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+flush:
+ str = Brdline(Bfd, '\n');
+ /* stash the crap from the end of the line for debugging */
+ if (debug) {
+ if (str == 0) {
+ Bprint(Bstderr, "premature EOF\n");
+ return(0);
+ }
+ str[Blinelen(Bfd)-1] = '\0';
+ }
+ line++;
+ chartorune(&troffchar, stoken);
+ if (specharflag) {
+ if (debug)
+ Bprint(Bstderr, "%s %d %d 0x%x %s # special\n",stoken, width, flag, charnum, str);
+ }
+ if (strcmp(stoken, "---") == 0) {
+ thisfont = RUNEGETGROUP(charnum);
+ thischar = RUNEGETCHAR(charnum);
+ stoken[0] = '\0';
+ } else {
+ thisfont = RUNEGETGROUP(troffchar);
+ thischar = RUNEGETCHAR(troffchar);
+ }
+ for (cp = &(troffontab[trindex].charent[thisfont][thischar]); *cp != 0; cp = &((*cp)->next))
+ if ((*cp)->name) {
+ if (debug) Bprint(Bstderr, "installing <%s>, found <%s>\n", stoken, (*cp)->name);
+ if (strcmp((*cp)->name, stoken) == 0)
+ break;
+ }
+ if (*cp == 0) *cp = galloc(0, sizeof(struct charent), "readtroffmetric:charent");
+ (*cp)->postfontid = RUNEGETGROUP(charnum);
+ (*cp)->postcharid = RUNEGETCHAR(charnum);
+ (*cp)->troffcharwidth = width;
+ (*cp)->name = galloc(0, strlen(stoken)+1, "readtroffmetric: char name");
+ (*cp)->next = 0;
+ strcpy((*cp)->name, stoken);
+ if (debug) {
+ if (specharflag)
+ Bprint(Bstderr, "%s", stoken);
+ else
+ Bputrune(Bstderr, troffchar);
+ Bprint(Bstderr, " %d %d 0x%x %s # psfontid=0x%x pscharid=0x%x thisfont=0x%x thischar=0x%x\n",
+ width, flag, charnum, str,
+ (*cp)->postfontid,
+ (*cp)->postcharid,
+ thisfont, thischar);
+ }
+ }
+ Bterm(Bfd);
+ Bflush(Bstderr);
+ return(1);
+}
+
+/* find index of troff font name in table
+ * returns -1 if name is not in table
+ * returns -2 if it cannot allocate memory
+ * returns -3 if there is a font mapping problem
+ * If insflg is not zero
+ * and the name is not found in the table, insert it.
+ */
+int
+findtfn(char *fontname, BOOLEAN insflg) {
+ struct troffont *tp;
+ int i, j;
+
+ if (debug) {
+ if (fontname==0) fprint(2, "findtfn(0x%x,%d)\n", fontname, insflg);
+ else fprint(2, "findtfn(%s,%d)\n", fontname, insflg);
+ }
+ for (i=0; i<troffontcnt; i++) {
+ if (troffontab[i].trfontid==0) {
+ error(WARNING, "findtfn:troffontab[%d].trfontid=0x%x, botch!\n",
+ i, troffontab[i].trfontid);
+ continue;
+ }
+ if (strcmp(troffontab[i].trfontid, fontname) == 0)
+ return(i);
+ }
+ if (insflg) {
+ tp = (struct troffont *)galloc(troffontab, sizeof(struct troffont)*(troffontcnt+1), "findtfn: struct troffont:");
+ if (tp == 0)
+ return(-2);
+ troffontab = tp;
+ tp = &(troffontab[troffontcnt]);
+ i = strlen(fontname);
+ tp->trfontid = galloc(0, i+1, "findtfn: trfontid:");
+
+ /* initialize new troff font entry with name and numeric fields to 0 */
+ strncpy(tp->trfontid, fontname, i);
+ tp->trfontid[i] = '\0';
+ tp->special = FALSE;
+ tp->spacewidth = 0;
+ tp->psfmapsize = 0;
+ tp->psfmap = 0;
+ for (i=0; i<NUMOFONTS; i++)
+ for (j=0; j<FONTSIZE; j++)
+ tp->charent[i][j] = 0;
+ troffontcnt++;
+ if (!readtroffmetric(fontname, troffontcnt-1))
+ return(-3);
+ if (!readpsfontdesc(fontname, troffontcnt-1))
+ return(-3);
+ return(troffontcnt-1);
+ }
+ return(-1);
+}
+
+void
+finish(void) {
+ int i;
+
+ Bprint(Bstdout, "%s", TRAILER);
+ Bprint(Bstdout, "done\n");
+ Bprint(Bstdout, "%s", DOCUMENTFONTS);
+
+ for (i=0; i<pfnamcnt; i++)
+ if (pfnafontmtab[i].used)
+ Bprint(Bstdout, " %s", pfnafontmtab[i].str);
+ Bprint(Bstdout, "\n");
+
+ Bprint(Bstdout, "%s %d\n", PAGES, pages_printed);
+
+}
+
+/* Set slant to n degrees. Disable slanting if n is 0. */
+void
+t_slant(int n) {
+ fontslant = n;
+ curpostfontid = -1;
+}
+
+/* Set character height to n points. Disabled if n is 0 or the current size. */
+
+void
+t_charht(int n) {
+ fontheight = (n == fontsize) ? 0 : n;
+ curpostfontid = -1;
+}
diff --git a/sys/src/cmd/postscript/tr2post/conv.c b/sys/src/cmd/postscript/tr2post/conv.c
new file mode 100755
index 000000000..ebc1f43d1
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/conv.c
@@ -0,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+void
+conv(Biobufhdr *Bp) {
+ long c, n;
+ int r;
+ char special[10];
+ int save;
+
+ inputlineno = 1;
+ if (debug) Bprint(Bstderr, "conv(Biobufhdr *Bp=0x%x)\n", Bp);
+ while ((r = Bgetrune(Bp)) >= 0) {
+/* Bprint(Bstderr, "r=<%c>,0x%x\n", r, r); */
+/* Bflush(Bstderr); */
+ switch (r) {
+ case 's': /* set point size */
+ Bgetfield(Bp, 'd', &fontsize, 0);
+ break;
+ case 'f': /* set font to postion */
+ Bgetfield(Bp, 'd', &fontpos, 0);
+ save = inputlineno;
+ settrfont();
+ inputlineno = save; /* ugh */
+ break;
+ case 'c': /* print rune */
+ r = Bgetrune(Bp);
+ runeout(r);
+ break;
+ case 'C': /* print special character */
+ Bgetfield(Bp, 's', special, 10);
+ specialout(special);
+ break;
+ case 'N': /* print character with numeric value from current font */
+ Bgetfield(Bp, 'd', &n, 0);
+ break;
+ case 'H': /* go to absolute horizontal position */
+ Bgetfield(Bp, 'd', &n, 0);
+ hgoto(n);
+ break;
+ case 'V': /* go to absolute vertical position */
+ Bgetfield(Bp, 'd', &n, 0);
+ vgoto(n);
+ break;
+ case 'h': /* go to relative horizontal position */
+ Bgetfield(Bp, 'd', &n, 0);
+ hmot(n);
+ break;
+ case 'v': /* go to relative vertical position */
+ Bgetfield(Bp, 'd', &n, 0);
+ vmot(n);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* move right nn units, then print character c */
+ n = (r - '0') * 10;
+ r = Bgetrune(Bp);
+ if (r < 0)
+ error(FATAL, "EOF or error reading input\n");
+ else if (r < '0' || r > '9')
+ error(FATAL, "integer expected\n");
+ n += r - '0';
+ r = Bgetrune(Bp);
+ hmot(n);
+ runeout(r);
+ break;
+ case 'p': /* begin page */
+ Bgetfield(Bp, 'd', &n, 0);
+ endpage();
+ startpage();
+ break;
+ case 'n': /* end of line (information only 'b a' follows) */
+ Brdline(Bp, '\n'); /* toss rest of line */
+ inputlineno++;
+ break;
+ case 'w': /* paddable word space (information only) */
+ break;
+ case 'D': /* graphics function */
+ draw(Bp);
+ break;
+ case 'x': /* device control functions */
+ devcntl(Bp);
+ break;
+ case '#': /* comment */
+ Brdline(Bp, '\n'); /* toss rest of line */
+ case '\n':
+ inputlineno++;
+ break;
+ default:
+ error(WARNING, "unknown troff function <%c>\n", r);
+ break;
+ }
+ }
+ endpage();
+ if (debug) Bprint(Bstderr, "r=0x%x\n", r);
+ if (debug) Bprint(Bstderr, "leaving conv\n");
+}
diff --git a/sys/src/cmd/postscript/tr2post/devcntl.c b/sys/src/cmd/postscript/tr2post/devcntl.c
new file mode 100755
index 000000000..2566229e8
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/devcntl.c
@@ -0,0 +1,178 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+char devname[20] = { 'u', 't', 'f', '\0' };
+int resolution;
+int minx, miny;
+
+struct sjt {
+ char *str;
+ void (*func)(void *);
+};
+
+/* I won't need this if getfields can replace sscanf
+
+extern void picture(Biobufhdr *);
+extern void notavail(char *);
+
+void
+PSInclude(Biobufhdr *inp) {
+ char buf[256];
+
+ Bgetfield(inp, 's', buf, 256);
+ if(pageon()) {
+ endstring();
+ Bprint(Bstdout, "%s\n", buf);
+ }
+}
+
+struct sjt specialjumptable[] = {
+ {"PI", picture},
+ {"PictureInclusion", picture},
+ {"InlinePicture", NULL},
+ {"BeginPath", NULL},
+ {"DrawPath", NULL},
+ {"BeginObject", NULL},
+ {"EndObject", NULL},
+ {"NewBaseline", NULL},
+ {"DrawText", NULL},
+ {"SetText", NULL},
+ {"SetColor", NULL},
+ {"INFO", NULL},
+ {"PS", PSInclude},
+ {"Postscript", PSInclude},
+ {"ExportPS", notavail("ExportPS")},
+ {NULL, NULL}
+};
+*/
+
+void
+devcntl(Biobufhdr *inp) {
+
+ char cmd[50], buf[256], str[MAXTOKENSIZE], *line;
+ int c, n, linelen;
+
+/*
+ *
+ * Interpret device control commands, ignoring any we don't recognize. The
+ * "x X ..." commands are a device dependent collection generated by troff's
+ * \X'...' request.
+ *
+ */
+
+ Bgetfield(inp, 's', cmd, 50);
+ if (debug) Bprint(Bstderr, "devcntl(cmd=%s)\n", cmd);
+ switch (cmd[0]) {
+ case 'f': /* mount font in a position */
+ Bgetfield(inp, 'd', &n, 0);
+ Bgetfield(inp, 's', str, 100);
+ mountfont(n, str);
+ break;
+
+ case 'i': /* initialize */
+ initialize();
+ break;
+
+ case 'p': /* pause */
+ break;
+
+ case 'r': /* resolution assumed when prepared */
+ Bgetfield(inp, 'd', &resolution, 0);
+ Bgetfield(inp, 'd', &minx, 0);
+ Bgetfield(inp, 'd', &miny, 0);
+ break;
+
+ case 's': /* stop */
+ case 't': /* trailer */
+ /* flushtext(); */
+ break;
+
+ case 'H': /* char height */
+ Bgetfield(inp, 'd', &n, 0);
+ t_charht(n);
+ break;
+
+ case 'S': /* slant */
+ Bgetfield(inp, 'd', &n, 0);
+ t_slant(n);
+ break;
+
+ case 'T': /* device name */
+ Bgetfield(inp, 's', &devname, 16);
+ if (debug) Bprint(Bstderr, "devname=%s\n", devname);
+ break;
+
+ case 'E': /* input encoding - not in troff yet */
+ Bgetfield(inp, 's', &str, 100);
+/* if ( strcmp(str, "UTF") == 0 )
+ reading = UTFENCODING;
+ else reading = ONEBYTE;
+ */
+ break;
+
+ case 'X': /* copy through - from troff */
+ if (Bgetfield(inp, 's', str, MAXTOKENSIZE-1) <= 0)
+ error(FATAL, "incomplete devcntl line\n");
+ if ((line = Brdline(inp, '\n')) == 0)
+ error(FATAL, "incomplete devcntl line\n");
+ strncpy(buf, line, Blinelen(inp)-1);
+ buf[Blinelen(inp)-1] = '\0';
+ Bungetc(inp);
+
+ if (strncmp(str, "PI", sizeof("PI")-1) == 0 || strncmp(str, "PictureInclusion", sizeof("PictureInclusion")-1) == 0) {
+ picture(inp, str);
+ } else if (strncmp(str, "InlinePicture", sizeof("InlinePicture")-1) == 0) {
+ error(FATAL, "InlinePicture not implemented yet.\n");
+/* inlinepic(inp, buf); */
+ } else if (strncmp(str, "BeginPath", sizeof("BeginPath")-1) == 0) {
+ beginpath(buf, FALSE);
+ } else if (strncmp(str, "DrawPath", sizeof("DrawPath")-1) == 0) {
+ drawpath(buf, FALSE);
+ } else if (strncmp(str, "BeginObject", sizeof("BeginObject")-1) == 0) {
+ beginpath(buf, TRUE);
+ } else if (strncmp(str, "EndObject", sizeof("EndObject")-1) == 0) {
+ drawpath(buf, TRUE);
+ } else if (strncmp(str, "NewBaseline", sizeof("NewBaseline")-1) == 0) {
+ error(FATAL, "NewBaseline not implemented yet.\n");
+/* newbaseline(buf); */
+ } else if (strncmp(str, "DrawText", sizeof("DrawText")-1) == 0) {
+ error(FATAL, "DrawText not implemented yet.\n");
+/* drawtext(buf); */
+ } else if (strncmp(str, "SetText", sizeof("SetText")-1) == 0) {
+ error(FATAL, "SetText not implemented yet.\n");
+/* settext(buf); */
+ } else if (strncmp(str, "SetColor", sizeof("SetColor")-1) == 0) {
+ error(FATAL, "SetColor not implemented yet.\n");
+/* newcolor(buf); */
+/* setcolor(); */
+ } else if (strncmp(str, "INFO", sizeof("INFO")-1) == 0) {
+ error(FATAL, "INFO not implemented yet.\n");
+/* flushtext(); */
+/* Bprint(outp, "%%INFO%s", buf); */
+ } else if (strncmp(str, "PS", sizeof("PS")-1) == 0 || strncmp(str, "PostScript", sizeof("PostScript")-1) == 0) {
+ if(pageon()) {
+ endstring();
+ Bprint(Bstdout, "%s\n", buf);
+ }
+ } else if (strncmp(str, "ExportPS", sizeof("ExportPS")-1) == 0) { /* dangerous!! */
+ error(FATAL, "ExportPS not implemented yet.\n");
+/* if (Bfildes(outp) == 1) { */
+/* restore(); */
+/* Bprint(outp, "%s", buf); */
+/* save(); */
+/* } */
+ }
+/* else
+ error(WARNING, "Unknown string <%s %s> after x X\n", str, buf);
+*/
+
+ break;
+ }
+ while ((c = Bgetc(inp)) != '\n' && c != Beof);
+ inputlineno++;
+}
+
diff --git a/sys/src/cmd/postscript/tr2post/draw.c b/sys/src/cmd/postscript/tr2post/draw.c
new file mode 100755
index 000000000..0cb368ae4
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/draw.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+BOOLEAN drawflag = FALSE;
+BOOLEAN inpath = FALSE; /* TRUE if we're putting pieces together */
+
+void
+cover(double x, double y) {
+}
+
+void
+drawspline(Biobufhdr *Bp, int flag) { /* flag!=1 connect end points */
+ int x[100], y[100];
+ int i, N;
+/*
+ *
+ * Spline drawing routine for Postscript printers. The complicated stuff is
+ * handled by procedure Ds, which should be defined in the library file. I've
+ * seen wrong implementations of troff's spline drawing, so fo the record I'll
+ * write down the parametric equations and the necessary conversions to Bezier
+ * cubic splines (as used in Postscript).
+ *
+ *
+ * Parametric equation (x coordinate only):
+ *
+ *
+ * (x2 - 2 * x1 + x0) 2 (x0 + x1)
+ * x = ------------------ * t + (x1 - x0) * t + ---------
+ * 2 2
+ *
+ *
+ * The coefficients in the Bezier cubic are,
+ *
+ *
+ * A = 0
+ * B = (x2 - 2 * x1 + x0) / 2
+ * C = x1 - x0
+ *
+ *
+ * while the current point is,
+ *
+ * current-point = (x0 + x1) / 2
+ *
+ * Using the relationships given in the Postscript manual (page 121) it's easy to
+ * see that the control points are given by,
+ *
+ *
+ * x0' = (x0 + 5 * x1) / 6
+ * x1' = (x2 + 5 * x1) / 6
+ * x2' = (x1 + x2) / 2
+ *
+ *
+ * where the primed variables are the ones used by curveto. The calculations
+ * shown above are done in procedure Ds using the coordinates set up in both
+ * the x[] and y[] arrays.
+ *
+ * A simple test of whether your spline drawing is correct would be to use cip
+ * to draw a spline and some tangent lines at appropriate points and then print
+ * the file.
+ *
+ */
+
+ for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
+ if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
+ break;
+
+ x[0] = x[1] = hpos;
+ y[0] = y[1] = vpos;
+
+ for (i = 1; i < N; i++) {
+ x[i+1] += x[i];
+ y[i+1] += y[i];
+ }
+
+ x[N] = x[N-1];
+ y[N] = y[N-1];
+
+ for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
+/* if (dobbox == TRUE) { /* could be better */
+/* cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
+/* cover((double)x[i+1], (double)-y[i+1]);
+/* cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
+/* }
+ */
+ }
+
+ hpos = x[N]; /* where troff expects to be */
+ vpos = y[N];
+}
+
+void
+draw(Biobufhdr *Bp) {
+
+ int r, x1, y1, x2, y2, i;
+ int d1, d2;
+
+ drawflag = TRUE;
+ r = Bgetrune(Bp);
+ switch(r) {
+ case 'l':
+ if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
+ error(FATAL, "draw line function, destination coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
+ hpos += x1;
+ vpos += y1;
+ break;
+ case 'c':
+ if (Bgetfield(Bp, 'd', &d1, 0)<=0)
+ error(FATAL, "draw circle function, diameter coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
+ hpos += d1;
+ break;
+ case 'e':
+ if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
+ error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
+ hpos += d1;
+ break;
+ case 'a':
+ if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'd', &x2, 0)<=0 || Bgetfield(Bp, 'd', &y2, 0)<=0)
+ error(FATAL, "draw arc function, coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
+ hpos += x1 + x2;
+ vpos += y1 + y2;
+ break;
+ case 'q':
+ drawspline(Bp, 1);
+ break;
+ case '~':
+ drawspline(Bp, 2);
+ break;
+ default:
+ error(FATAL, "unknown draw function <%c>\n", r);
+ break;
+ }
+}
+
+void
+beginpath(char *buf, int copy) {
+
+/*
+ * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
+ * to mark the start of a sequence of drawing commands that should be grouped
+ * together and treated as a single path. By default the drawing procedures in
+ * *drawfile treat each drawing command as a separate object, and usually start
+ * with a newpath (just as a precaution) and end with a stroke. The newpath and
+ * stroke isolate individual drawing commands and make it impossible to deal with
+ * composite objects. "x X BeginPath" can be used to mark the start of drawing
+ * commands that should be grouped together and treated as a single object, and
+ * part of what's done here ensures that the PostScript drawing commands defined
+ * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
+ * command. At that point the path that's been built up can be manipulated in
+ * various ways (eg. filled and/or stroked with a different line width).
+ *
+ * Color selection is one of the options that's available in parsebuf(),
+ * so if we get here we add *colorfile to the output file before doing
+ * anything important.
+ *
+ */
+ if (inpath == FALSE) {
+ endstring();
+ /* getdraw(); */
+ /* getcolor(); */
+ Bprint(Bstdout, "gsave\n");
+ Bprint(Bstdout, "newpath\n");
+ Bprint(Bstdout, "%d %d m\n", hpos, vpos);
+ Bprint(Bstdout, "/inpath true def\n");
+ if ( copy == TRUE )
+ Bprint(Bstdout, "%s\n", buf);
+ inpath = TRUE;
+ }
+}
+
+static void parsebuf(char*);
+
+void
+drawpath(char *buf, int copy) {
+
+/*
+ *
+ * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
+ * end of the path started by the last "x X BeginPath" command and uses whatever
+ * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
+ * the path). Once that's been done the drawing procedures are restored to their
+ * default behavior in which each drawing command is treated as an isolated path.
+ * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
+ * parsebuf() to figure out what goes in the output file. It's a feeble attempt
+ * to free users and preprocessors (like pic) from having to know PostScript. The
+ * comments in parsebuf() describe what's handled.
+ *
+ * In the early version a path was started with "x X BeginObject" and ended with
+ * "x X EndObject". In both cases *buf was just copied to the output file, and
+ * was expected to be legitimate PostScript that manipulated the current path.
+ * The old escape sequence will be supported for a while (for Ravi), and always
+ * call this routine with copy set to TRUE.
+ *
+ *
+ */
+
+ if ( inpath == TRUE ) {
+ if ( copy == TRUE )
+ Bprint(Bstdout, "%s\n", buf);
+ else
+ parsebuf(buf);
+ Bprint(Bstdout, "grestore\n");
+ Bprint(Bstdout, "/inpath false def\n");
+/* reset(); */
+ inpath = FALSE;
+ }
+}
+
+
+/*****************************************************************************/
+
+static void
+parsebuf(char *buf)
+{
+ char *p; /* usually the next token */
+ char *q;
+ int gsavelevel = 0; /* non-zero if we've done a gsave */
+
+/*
+ *
+ * Simple minded attempt at parsing the string that followed an "x X DrawPath"
+ * command. Everything not recognized here is simply ignored - there's absolutely
+ * no error checking and what was originally in buf is clobbered by strtok().
+ * A typical *buf might look like,
+ *
+ * gray .9 fill stroke
+ *
+ * to fill the current path with a gray level of .9 and follow that by stroking the
+ * outline of the path. Since unrecognized tokens are ignored the last example
+ * could also be written as,
+ *
+ * with gray .9 fill then stroke
+ *
+ * The "with" and "then" strings aren't recognized tokens and are simply discarded.
+ * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
+ * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
+ * level or color) are reset to default values immediately after the stroke, fill,
+ * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
+ * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
+ *
+ * The tokens that cause temporary changes to the graphics state are "gray" (for
+ * setting the gray level), "color" (for selecting a known color from the colordict
+ * dictionary defined in *colorfile), and "line" (for setting the line width). All
+ * three tokens can be extended since strncmp() makes the comparison. For example
+ * the strings "line" and "linewidth" accomplish the same thing. Colors are named
+ * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
+ * tokens must be followed immediately by their single argument. The gray level
+ * (ie. the argument that follows "gray") should be a number between 0 and 1, with
+ * 0 for black and 1 for white.
+ *
+ * To pass straight PostScript through enclose the appropriate commands in double
+ * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
+ * pair (ie. the one from the initial "x X BeginPath") although that's probably
+ * a mistake. Suspect I may have to change the double quote delimiters.
+ *
+ */
+
+ for( ; p != nil ; p = q ) {
+ if( q = strchr(p, ' ') ) {
+ *q++ = '\0';
+ }
+
+ if ( gsavelevel == 0 ) {
+ Bprint(Bstdout, "gsave\n");
+ gsavelevel++;
+ }
+ if ( strcmp(p, "stroke") == 0 ) {
+ Bprint(Bstdout, "closepath stroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "openstroke") == 0 ) {
+ Bprint(Bstdout, "stroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "fill") == 0 ) {
+ Bprint(Bstdout, "eofill\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "wfill") == 0 ) {
+ Bprint(Bstdout, "fill\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "sfill") == 0 ) {
+ Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "%s setgray\n", p);
+ }
+ } else if ( strncmp(p, "color", strlen("color")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "/%s setcolor\n", p);
+ }
+ } else if ( strncmp(p, "line", strlen("line")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
+ }
+ } else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
+ Bprint(Bstdout, "reversepath\n");
+ else if ( *p == '"' ) {
+ for ( ; gsavelevel > 0; gsavelevel-- )
+ Bprint(Bstdout, "grestore\n");
+ if ( q != nil )
+ *--q = ' ';
+ if ( (q = strchr(p, '"')) != nil ) {
+ *q++ = '\0';
+ Bprint(Bstdout, "%s\n", p);
+ }
+ }
+ }
+
+ for ( ; gsavelevel > 0; gsavelevel-- )
+ Bprint(Bstdout, "grestore\n");
+
+}
diff --git a/sys/src/cmd/postscript/tr2post/mkfile b/sys/src/cmd/postscript/tr2post/mkfile
new file mode 100755
index 000000000..57864bf5d
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/mkfile
@@ -0,0 +1,35 @@
+</$objtype/mkfile
+
+<../config
+
+COMMONDIR=../common
+
+TARG=tr2post
+
+OFILES=tr2post.$O\
+ chartab.$O\
+ Bgetfield.$O\
+ conv.$O\
+ utils.$O\
+ devcntl.$O\
+ draw.$O\
+ readDESC.$O\
+ ps_include.$O\
+ pictures.$O\
+ common.$O\
+
+HFILES=tr2post.h\
+ ps_include.h\
+ $COMMONDIR/common.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/path.h\
+ $COMMONDIR/ext.h\
+
+BIN=$POSTBIN
+
+</sys/src/cmd/mkone
+
+CFLAGS=-c -D'PROGRAMVERSION="0.1"' -D'DOROUND=1' -I$COMMONDIR -D'PROGRAMNAME="troff, Plan 9 edition"'
+
+%.$O: $COMMONDIR/%.c
+ $CC $CFLAGS $COMMONDIR/$stem.c
diff --git a/sys/src/cmd/postscript/tr2post/pictures.c b/sys/src/cmd/postscript/tr2post/pictures.c
new file mode 100755
index 000000000..806c22544
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/pictures.c
@@ -0,0 +1,295 @@
+/*
+ *
+ * PostScript picture inclusion routines. Support for managing in-line pictures
+ * has been added, and works in combination with the simple picpack pre-processor
+ * that's supplied with this package. An in-line picture begins with a special
+ * device control command that looks like,
+ *
+ * x X InlinPicture name size
+ *
+ * where name is the pathname of the original picture file and size is the number
+ * of bytes in the picture, which begins immediately on the next line. When dpost
+ * encounters the InlinePicture device control command inlinepic() is called and
+ * that routine appends the string name and the integer size to a temporary file
+ * (fp_pic) and then adds the next size bytes read from the current input file to
+ * file fp_pic. All in-line pictures are saved in fp_pic and located later using
+ * the name string and picture file size that separate pictures saved in fp_pic.
+ *
+ * When a picture request (ie. an "x X PI" command) is encountered picopen() is
+ * called and it first looks for the picture file in fp_pic. If it's found there
+ * the entire picture (ie. size bytes) is copied from fp_pic to a new temp file
+ * and that temp file is used as the picture file. If there's nothing in fp_pic
+ * or if the lookup failed the original route is taken.
+ *
+ * Support for in-line pictures is an attempt to address requirements, expressed
+ * by several organizations, of being able to store a document as a single file
+ * (usually troff input) that can then be sent through dpost and ultimately to
+ * a PostScript printer. The mechanism may help some users, but the are obvious
+ * disadvantages to this approach, and the original mechanism is the recommended
+ * approach! Perhaps the most important problem is that troff output, with in-line
+ * pictures included, doesn't fit the device independent language accepted by
+ * important post-processors (like proff) and that means you won't be able to
+ * reliably preview a packed file on your 5620 (or whatever).
+ *
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "ext.h"
+#include "common.h"
+#include "tr2post.h"
+/* PostScript file structuring comments */
+#include "comments.h"
+/* general purpose definitions */
+/* #include "gen.h" */
+/* just for TEMPDIR definition */
+#include "path.h"
+/* external variable declarations */
+/* #include "ext.h" */
+
+Biobuf *bfp_pic = NULL;
+Biobufhdr *Bfp_pic;
+Biobufhdr *picopen(char *);
+
+#define MAXGETFIELDS 16
+char *fields[MAXGETFIELDS];
+int nfields;
+
+extern int devres, hpos, vpos;
+extern int picflag;
+
+/*****************************************************************************/
+
+void
+picture(Biobufhdr *inp, char *buf) {
+ int poffset; /* page offset */
+ int indent; /* indent */
+ int length; /* line length */
+ int totrap; /* distance to next trap */
+ char name[100]; /* picture file and page string */
+ char hwo[40], *p; /* height, width and offset strings */
+ char flags[20]; /* miscellaneous stuff */
+ int page = 1; /* page number pulled from name[] */
+ double frame[4]; /* height, width, y, and x offsets from hwo[] */
+ char units; /* scale indicator for frame dimensions */
+ int whiteout = 0; /* white out the box? */
+ int outline = 0; /* draw a box around the picture? */
+ int scaleboth = 0; /* scale both dimensions? */
+ double adjx = 0.5; /* left-right adjustment */
+ double adjy = 0.5; /* top-bottom adjustment */
+ double rot = 0; /* rotation in clockwise degrees */
+ Biobufhdr *fp_in; /* for *name */
+ int i; /* loop index */
+
+/*
+ *
+ * Called from devcntrl() after an 'x X PI' command is found. The syntax of that
+ * command is:
+ *
+ * x X PI:args
+ *
+ * with args separated by colons and given by:
+ *
+ * poffset
+ * indent
+ * length
+ * totrap
+ * file[(page)]
+ * height[,width[,yoffset[,xoffset]]]
+ * [flags]
+ *
+ * poffset, indent, length, and totrap are given in machine units. height, width,
+ * and offset refer to the picture frame in inches, unless they're followed by
+ * the u scale indicator. flags is a string that provides a little bit of control
+ * over the placement of the picture in the frame. Rotation of the picture, in
+ * clockwise degrees, is set by the a flag. If it's not followed by an angle
+ * the current rotation angle is incremented by 90 degrees, otherwise the angle
+ * is set by the number that immediately follows the a.
+ *
+ */
+
+ if (!picflag) /* skip it */
+ return;
+ endstring();
+
+ flags[0] = '\0'; /* just to be safe */
+
+ nfields = getfields(buf, fields, MAXGETFIELDS, 0, ":\n");
+ if (nfields < 6) {
+ error(WARNING, "too few arguments to specify picture");
+ return;
+ }
+ poffset = atoi(fields[1]);
+ indent = atoi(fields[2]);
+ length = atoi(fields[3]);
+ totrap = atoi(fields[4]);
+ strncpy(name, fields[5], sizeof(name));
+ strncpy(hwo, fields[6], sizeof(hwo));
+ if (nfields >= 6)
+ strncpy(flags, fields[7], sizeof(flags));
+
+ nfields = getfields(buf, fields, MAXGETFIELDS, 0, "()");
+ if (nfields == 2) {
+ strncpy(name, fields[0], sizeof(name));
+ page = atoi(fields[1]);
+ }
+
+ if ((fp_in = picopen(name)) == NULL) {
+ error(WARNING, "can't open picture file %s\n", name);
+ return;
+ }
+
+ frame[0] = frame[1] = -1; /* default frame height, width */
+ frame[2] = frame[3] = 0; /* and y and x offsets */
+
+ for (i = 0, p = hwo-1; i < 4 && p != NULL; i++, p = strchr(p, ','))
+ if (sscanf(++p, "%lf%c", &frame[i], &units) == 2)
+ if (units == 'i' || units == ',' || units == '\0')
+ frame[i] *= devres;
+
+ if (frame[0] <= 0) /* check what we got for height */
+ frame[0] = totrap;
+
+ if (frame[1] <= 0) /* and width - check too big?? */
+ frame[1] = length - indent;
+
+ frame[3] += poffset + indent; /* real x offset */
+
+ for (i = 0; flags[i]; i++)
+ switch (flags[i]) {
+ case 'c': adjx = adjy = 0.5; break; /* move to the center */
+ case 'l': adjx = 0; break; /* left */
+ case 'r': adjx = 1; break; /* right */
+ case 't': adjy = 1; break; /* top */
+ case 'b': adjy = 0; break; /* or bottom justify */
+ case 'o': outline = 1; break; /* outline the picture */
+ case 'w': whiteout = 1; break; /* white out the box */
+ case 's': scaleboth = 1; break; /* scale both dimensions */
+ case 'a': if ( sscanf(&flags[i+1], "%lf", &rot) != 1 )
+ rot += 90;
+ }
+
+ /* restore(); */
+ endstring();
+ Bprint(Bstdout, "cleartomark\n");
+ Bprint(Bstdout, "saveobj restore\n");
+
+ ps_include(fp_in, Bstdout, page, whiteout, outline, scaleboth,
+ frame[3]+frame[1]/2, -vpos-frame[2]-frame[0]/2, frame[1], frame[0], adjx, adjy, -rot);
+ /* save(); */
+ Bprint(Bstdout, "/saveobj save def\n");
+ Bprint(Bstdout, "mark\n");
+ Bterm(fp_in);
+
+}
+
+/*
+ *
+ * Responsible for finding and opening the next picture file. If we've accumulated
+ * any in-line pictures fp_pic won't be NULL and we'll look there first. If *path
+ * is found in *fp_pic we create another temp file, open it for update, unlink it,
+ * copy in the picture, seek back to the start of the new temp file, and return
+ * the file pointer to the caller. If fp_pic is NULL or the lookup fails we just
+ * open file *path and return the resulting file pointer to the caller.
+ *
+ */
+Biobufhdr *
+picopen(char *path) {
+/* char name[100]; /* pathnames */
+/* long pos; /* current position */
+/* long total; /* and sizes - from *fp_pic */
+ Biobuf *bfp;
+ Biobufhdr *Bfp; /* and pointer for the new temp file */
+
+
+ if ((bfp = Bopen(path, OREAD)) == 0)
+ error(FATAL, "can't open %s\n", path);
+ Bfp = &(bfp->Biobufhdr);
+ return(Bfp);
+#ifdef UNDEF
+ if (Bfp_pic != NULL) {
+ Bseek(Bfp_pic, 0L, 0);
+ while (Bgetfield(Bfp_pic, 's', name, 99)>0
+ && Bgetfield(Bfp_pic, 'd', &total, 0)>0) {
+ pos = Bseek(Bfp_pic, 0L, 1);
+ if (strcmp(path, name) == 0) {
+ if (tmpnam(pictmpname) == NULL)
+ error(FATAL, "can't generate temp file name");
+ if ( (bfp = Bopen(pictmpname, ORDWR)) == NULL )
+ error(FATAL, "can't open %s", pictmpname);
+ Bfp = &(bfp->Biobufhdr);
+ piccopy(Bfp_pic, Bfp, total);
+ Bseek(Bfp, 0L, 0);
+ return(Bfp);
+ }
+ Bseek(Bfp_pic, total+pos, 0);
+ }
+ }
+ if ((bfp = Bopen(path, OREAD)) == 0)
+ Bfp = 0;
+ else
+ Bfp = &(bfp->Biobufhdr);
+ return(Bfp);
+#endif
+}
+
+/*
+ *
+ * Adds an in-line picture file to the end of temporary file *Bfp_pic. All pictures
+ * grabbed from the input file are saved in the same temp file. Each is preceeded
+ * by a one line header that includes the original picture file pathname and the
+ * size of the picture in bytes. The in-line picture file is opened for update,
+ * left open, and unlinked so it disappears when we do.
+ *
+ */
+/* *fp; /* current input file */
+/* *buf; /* whatever followed "x X InlinePicture" */
+
+#ifdef UNDEF
+void
+inlinepic(Biobufhdr *Bfp, char *buf) {
+ char name[100]; /* picture file pathname */
+ long total; /* and size - both from *buf */
+
+
+ if (Bfp_pic == NULL ) {
+ tmpnam(pictmpname);
+ if ((bfp_pic = Bopen(pictmpname, ORDWR)) == 0)
+ error(FATAL, "can't open in-line picture file %s", ipictmpname);
+ unlink(pictmpname);
+ }
+
+ if ( sscanf(buf, "%s %ld", name, &total) != 2 )
+ error(FATAL, "in-line picture error");
+
+ fseek(Bfp_pic, 0L, 2);
+ fprintf(Bfp_pic, "%s %ld\n", name, total);
+ getc(fp);
+ fflush(fp_pic);
+ piccopy(fp, fp_pic, total);
+ ungetc('\n', fp);
+
+}
+#endif
+
+/*
+ *
+ * Copies total bytes from file fp_in to fp_out. Used to append picture files to
+ * *fp_pic and then copy them to yet another temporary file immediately before
+ * they're used (in picture()).
+ *
+ */
+/* *fp_in; input */
+/* *fp_out; and output file pointers */
+/* total; number of bytes to be copied */
+void
+piccopy(Biobufhdr *Bfp_in, Biobufhdr *Bfp_out, long total) {
+ long i;
+
+ for (i = 0; i < total; i++)
+ if (Bputc(Bfp_out, Bgetc(Bfp_in)) < 0)
+ error(FATAL, "error copying in-line picture file");
+ Bflush(Bfp_out);
+}
diff --git a/sys/src/cmd/postscript/tr2post/ps_include.c b/sys/src/cmd/postscript/tr2post/ps_include.c
new file mode 100755
index 000000000..27d020a3a
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/ps_include.c
@@ -0,0 +1,191 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "../common/common.h"
+#include "ps_include.h"
+
+extern int curpostfontid;
+extern int curfontsize;
+
+typedef struct {long start, end;} Section;
+static char *buf;
+
+static
+copy(Biobufhdr *fin, Biobufhdr *fout, Section *s) {
+ int cond;
+ if (s->end <= s->start)
+ return;
+ Bseek(fin, s->start, 0);
+ while (Bseek(fin, 0L, 1) < s->end && (buf=Brdline(fin, '\n')) != NULL){
+ /*
+ * We have to be careful here, because % can legitimately appear
+ * in Ascii85 encodings, and must not be elided.
+ * The goal here is to make any DSC comments impotent without
+ * actually changing the behavior of the Postscript.
+ * Since stripping ``comments'' breaks Ascii85, we can instead just
+ * indent comments a space, which turns DSC comments into non-DSC comments
+ * and has no effect on binary encodings, which are whitespace-blind.
+ */
+ if(buf[0] == '%')
+ Bputc(fout, ' ');
+ Bwrite(fout, buf, Blinelen(fin));
+ }
+}
+
+/*
+ *
+ * Reads a PostScript file (*fin), and uses structuring comments to locate the
+ * prologue, trailer, global definitions, and the requested page. After the whole
+ * file is scanned, the special ps_include PostScript definitions are copied to
+ * *fout, followed by the prologue, global definitions, the requested page, and
+ * the trailer. Before returning the initial environment (saved in PS_head) is
+ * restored.
+ *
+ * By default we assume the picture is 8.5 by 11 inches, but the BoundingBox
+ * comment, if found, takes precedence.
+ *
+ */
+/* *fin, *fout; /* input and output files */
+/* page_no; /* physical page number from *fin */
+/* whiteout; /* erase picture area */
+/* outline; /* draw a box around it and */
+/* scaleboth; /* scale both dimensions - if not zero */
+/* cx, cy; /* center of the picture and */
+/* sx, sy; /* its size - in current coordinates */
+/* ax, ay; /* left-right, up-down adjustment */
+/* rot; /* rotation - in clockwise degrees */
+
+void
+ps_include(Biobufhdr *fin, Biobufhdr *fout, int page_no, int whiteout,
+ int outline, int scaleboth, double cx, double cy, double sx, double sy,
+ double ax, double ay, double rot) {
+ char **strp;
+ int foundpage = 0; /* found the page when non zero */
+ int foundpbox = 0; /* found the page bounding box */
+ int nglobal = 0; /* number of global defs so far */
+ int maxglobal = 0; /* and the number we've got room for */
+ Section prolog, page, trailer; /* prologue, page, and trailer offsets */
+ Section *global; /* offsets for all global definitions */
+ double llx, lly; /* lower left and */
+ double urx, ury; /* upper right corners - default coords */
+ double w = whiteout != 0; /* mostly for the var() macro */
+ double o = outline != 0;
+ double s = scaleboth != 0;
+ int i; /* loop index */
+
+#define has(word) (strncmp(buf, word, strlen(word)) == 0)
+#define grab(n) ((Section *)(nglobal \
+ ? realloc((char *)global, n*sizeof(Section)) \
+ : calloc(n, sizeof(Section))))
+
+ llx = lly = 0; /* default BoundingBox - 8.5x11 inches */
+ urx = 72 * 8.5;
+ ury = 72 * 11.0;
+
+ /* section boundaries and bounding box */
+
+ prolog.start = prolog.end = 0;
+ page.start = page.end = 0;
+ trailer.start = 0;
+ Bseek(fin, 0L, 0);
+
+ while ((buf=Brdline(fin, '\n')) != NULL) {
+ buf[Blinelen(fin)-1] = '\0';
+ if (!has("%%"))
+ continue;
+ else if (has("%%Page: ")) {
+ if (!foundpage)
+ page.start = Bseek(fin, 0L, 1);
+ sscanf(buf, "%*s %*s %d", &i);
+ if (i == page_no)
+ foundpage = 1;
+ else if (foundpage && page.end <= page.start)
+ page.end = Bseek(fin, 0L, 1);
+ } else if (has("%%EndPage: ")) {
+ sscanf(buf, "%*s %*s %d", &i);
+ if (i == page_no) {
+ foundpage = 1;
+ page.end = Bseek(fin, 0L, 1);
+ }
+ if (!foundpage)
+ page.start = Bseek(fin, 0L, 1);
+ } else if (has("%%PageBoundingBox: ")) {
+ if (i == page_no) {
+ foundpbox = 1;
+ sscanf(buf, "%*s %lf %lf %lf %lf",
+ &llx, &lly, &urx, &ury);
+ }
+ } else if (has("%%BoundingBox: ")) {
+ if (!foundpbox)
+ sscanf(buf,"%*s %lf %lf %lf %lf",
+ &llx, &lly, &urx, &ury);
+ } else if (has("%%EndProlog") || has("%%EndSetup") || has("%%EndDocumentSetup"))
+ prolog.end = page.start = Bseek(fin, 0L, 1);
+ else if (has("%%Trailer"))
+ trailer.start = Bseek(fin, 0L, 1);
+ else if (has("%%BeginGlobal")) {
+ if (page.end <= page.start) {
+ if (nglobal >= maxglobal) {
+ maxglobal += 20;
+ global = grab(maxglobal);
+ }
+ global[nglobal].start = Bseek(fin, 0L, 1);
+ }
+ } else if (has("%%EndGlobal"))
+ if (page.end <= page.start)
+ global[nglobal++].end = Bseek(fin, 0L, 1);
+ }
+ Bseek(fin, 0L, 2);
+ if (trailer.start == 0)
+ trailer.start = Bseek(fin, 0L, 1);
+ trailer.end = Bseek(fin, 0L, 1);
+
+ if (page.end <= page.start)
+ page.end = trailer.start;
+
+/*
+fprint(2, "prolog=(%d,%d)\n", prolog.start, prolog.end);
+fprint(2, "page=(%d,%d)\n", page.start, page.end);
+for(i = 0; i < nglobal; i++)
+ fprint(2, "global[%d]=(%d,%d)\n", i, global[i].start, global[i].end);
+fprint(2, "trailer=(%d,%d)\n", trailer.start, trailer.end);
+*/
+
+ /* all output here */
+ for (strp = PS_head; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ Bprint(fout, "/llx %g def\n", llx);
+ Bprint(fout, "/lly %g def\n", lly);
+ Bprint(fout, "/urx %g def\n", urx);
+ Bprint(fout, "/ury %g def\n", ury);
+ Bprint(fout, "/w %g def\n", w);
+ Bprint(fout, "/o %g def\n", o);
+ Bprint(fout, "/s %g def\n", s);
+ Bprint(fout, "/cx %g def\n", cx);
+ Bprint(fout, "/cy %g def\n", cy);
+ Bprint(fout, "/sx %g def\n", sx);
+ Bprint(fout, "/sy %g def\n", sy);
+ Bprint(fout, "/ax %g def\n", ax);
+ Bprint(fout, "/ay %g def\n", ay);
+ Bprint(fout, "/rot %g def\n", rot);
+
+ for (strp = PS_setup; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ copy(fin, fout, &prolog);
+ for(i = 0; i < nglobal; i++)
+ copy(fin, fout, &global[i]);
+ copy(fin, fout, &page);
+ copy(fin, fout, &trailer);
+ for (strp = PS_tail; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ if(nglobal)
+ free(global);
+
+ /* force the program to reestablish its state */
+ curpostfontid = -1;
+ curfontsize = -1;
+}
diff --git a/sys/src/cmd/postscript/tr2post/ps_include.h b/sys/src/cmd/postscript/tr2post/ps_include.h
new file mode 100755
index 000000000..cef490508
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/ps_include.h
@@ -0,0 +1,66 @@
+static char *PS_head[] = {
+ "%ps_include: begin\n",
+ "save\n",
+ "/ed {exch def} def\n",
+ "{} /showpage ed\n",
+ "{} /copypage ed\n",
+ "{} /erasepage ed\n",
+ "{} /letter ed\n",
+ "currentdict /findfont known systemdict /findfont known and {\n",
+ " /findfont systemdict /findfont get def\n",
+ "} if\n",
+ "36 dict dup /PS-include-dict-dw ed begin\n",
+ "/context ed\n",
+ "count array astore /o-stack ed\n",
+ "%ps_include: variables begin\n",
+ 0
+};
+
+static char *PS_setup[] = {
+ "%ps_include: variables end\n",
+ "{llx lly urx ury} /bbox ed\n",
+ "{newpath 2 index exch 2 index exch dup 6 index exch\n",
+ " moveto 3 {lineto} repeat closepath} /boxpath ed\n",
+ "{dup mul exch dup mul add sqrt} /len ed\n",
+ "{2 copy gt {exch} if pop} /min ed\n",
+ "{2 copy lt {exch} if pop} /max ed\n",
+ "{transform round exch round exch A itransform} /nice ed\n",
+ "{6 array} /n ed\n",
+ "n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed\n",
+ "urx llx sub 0 A dtransform len /Sx ed\n",
+ "0 ury lly sub A dtransform len /Sy ed\n",
+ "llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed\n",
+ "rot dup sin abs /S ed cos abs /C ed\n",
+ "Sx S mul Sy C mul add /H ed\n",
+ "Sx C mul Sy S mul add /W ed\n",
+ "sy H div /Scaley ed\n",
+ "sx W div /Scalex ed\n",
+ "s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if\n",
+ "sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed\n",
+ "sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed\n",
+ "urx llx sub 0 A dtransform exch atan rot exch sub /rot ed\n",
+ "n currentmatrix initgraphics setmatrix\n",
+ "cx cy translate\n",
+ "Scalex Scaley scale\n",
+ "rot rotate\n",
+ "Cx neg Cy neg translate\n",
+ "A concat\n",
+ "bbox boxpath clip newpath\n",
+ "w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if\n",
+ "end\n",
+ "gsave\n",
+ "%ps_include: inclusion begin\n",
+ 0
+};
+
+static char *PS_tail[] = {
+ "%ps_include: inclusion end\n",
+ "grestore\n",
+ "PS-include-dict-dw begin\n",
+ "o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice\n",
+ " initgraphics 0.1 setlinewidth boxpath stroke grestore} if\n",
+ "clear o-stack aload pop\n",
+ "context end restore\n",
+ "%ps_include: end\n",
+ 0
+};
diff --git a/sys/src/cmd/postscript/tr2post/readDESC.c b/sys/src/cmd/postscript/tr2post/readDESC.c
new file mode 100755
index 000000000..473470d38
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/readDESC.c
@@ -0,0 +1,139 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+char *printdesclang = 0;
+char *encoding = 0;
+int devres;
+int unitwidth;
+int nspechars = 0;
+struct charent spechars[MAXSPECHARS];
+
+#define NDESCTOKS 9
+static char *desctoks[NDESCTOKS] = {
+ "PDL",
+ "Encoding",
+ "fonts",
+ "sizes",
+ "res",
+ "hor",
+ "vert",
+ "unitwidth",
+ "charset"
+};
+
+char *spechar[MAXSPECHARS];
+
+int
+hash(char *s, int l) {
+ unsigned i;
+
+ for (i=0; *s; s++)
+ i = i*10 + *s;
+ return(i % l);
+}
+
+BOOLEAN
+readDESC(void) {
+ char token[MAXTOKENSIZE];
+ char *descnameformat = "%s/dev%s/DESC";
+ char *descfilename = 0;
+ Biobuf *bfd;
+ Biobufhdr *Bfd;
+ int i, state = -1;
+ int fontindex = 0;
+
+ if (debug) Bprint(Bstderr, "readDESC()\n");
+ descfilename = galloc(descfilename, strlen(descnameformat)+strlen(FONTDIR)
+ +strlen(devname), "readdesc");
+ sprint(descfilename, descnameformat, FONTDIR, devname);
+ if ((bfd = Bopen(descfilename, OREAD)) == 0) {
+ error(WARNING, "cannot open file %s\n", descfilename);
+ return(0);
+ }
+ Bfd = &(bfd->Biobufhdr);
+
+ while (Bgetfield(Bfd, 's', token, MAXTOKENSIZE) > 0) {
+ for (i=0; i<NDESCTOKS; i++) {
+ if (strcmp(desctoks[i], token) == 0) {
+ state = i;
+ break;
+ }
+ }
+ if (i<NDESCTOKS) continue;
+ switch (state) {
+ case 0:
+ printdesclang=galloc(printdesclang, strlen(token)+1, "readdesc:");
+ strcpy(printdesclang, token);
+ if (debug) Bprint(Bstderr, "PDL %s\n", token);
+ break;
+ case 1:
+ encoding=galloc(encoding, strlen(token)+1, "readdesc:");
+ strcpy(encoding, token);
+ if (debug) Bprint(Bstderr, "encoding %s\n", token);
+ break;
+ case 2:
+ if (fontmnt <=0) {
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting number of fonts in mount table.\n");
+ return(FALSE);
+ }
+ fontmnt = atoi(token) + 1;
+ fontmtab = galloc(fontmtab, fontmnt*sizeof(char *), "readdesc:");
+
+ for (i=0; i<fontmnt; i++)
+ fontmtab[i] = 0;
+ fontindex = 0;
+ } else {
+ mountfont(++fontindex, token);
+ findtfn(token, TRUE);
+ }
+ break;
+ case 3:
+ /* I don't really care about sizes */
+ break;
+ case 4:
+ /* device resolution in dots per inch */
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting device resolution.\n");
+ return(FALSE);
+ }
+ devres = atoi(token);
+ if (debug) Bprint(Bstderr, "res %d\n", devres);
+ break;
+ case 5:
+ /* I don't really care about horizontal motion resolution */
+ if (debug) Bprint(Bstderr, "ignoring horizontal resolution\n");
+ break;
+ case 6:
+ /* I don't really care about vertical motion resolution */
+ if (debug) Bprint(Bstderr, "ignoring vertical resolution\n");
+ break;
+ case 7:
+ /* unitwidth is the font size at which the character widths are 1:1 */
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting unitwidth.\n");
+ return(FALSE);
+ }
+ unitwidth = atoi(token);
+ if (debug) Bprint(Bstderr, "unitwidth %d\n", unitwidth);
+ break;
+ case 8:
+ /* I don't really care about this list of special characters */
+ if (debug) Bprint(Bstderr, "ignoring special character <%s>\n", token);
+ break;
+ default:
+ if (*token == '#')
+ Brdline(Bfd, '\n');
+ else
+ error(WARNING, "unknown token %s in DESC file.\n", token);
+ break;
+ }
+ }
+ Bterm(Bfd);
+}
diff --git a/sys/src/cmd/postscript/tr2post/shell.lib b/sys/src/cmd/postscript/tr2post/shell.lib
new file mode 100755
index 000000000..d96e65655
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/shell.lib
@@ -0,0 +1,1238 @@
+#
+# Shell library - for building devutf tables.
+#
+
+RESOLUTION=720
+UNITWIDTH=10
+
+OCTALESCAPES=${OCTALESCAPES:-160} # <= code means add \0ddd names
+DOWNLOADVECTOR=FALSE # TRUE can mean incomplete tables
+
+#
+# BuiltinTables returns command lines that generate PostScript programs
+# for building a typesetter description file and font width tables for
+# a relatively standard collection of fonts. Use awk to select a command
+# line or modify an existing command to build a width table for a new
+# font.
+#
+
+BuiltinTables() {
+ cat <<-'//End of BuiltinTables'
+ Proportional R Times-Roman
+ Proportional I Times-Italic
+ Proportional B Times-Bold
+ Proportional BI Times-BoldItalic
+ Proportional AB AvantGarde-Demi
+ Proportional AI AvantGarde-BookOblique
+ Proportional AR AvantGarde-Book
+ Proportional AX AvantGarde-DemiOblique
+ Proportional H Helvetica
+ Proportional HB Helvetica-Bold
+ Proportional HI Helvetica-Oblique
+ Proportional HX Helvetica-BoldOblique
+ Proportional Hb Helvetica-Narrow-Bold
+ Proportional Hi Helvetica-Narrow-Oblique
+ Proportional Hr Helvetica-Narrow
+ Proportional Hx Helvetica-Narrow-BoldOblique
+ Proportional KB Bookman-Demi
+ Proportional KI Bookman-LightItalic
+ Proportional KR Bookman-Light
+ Proportional KX Bookman-DemiItalic
+ Proportional NB NewCenturySchlbk-Bold
+ Proportional NI NewCenturySchlbk-Italic
+ Proportional NR NewCenturySchlbk-Roman
+ Proportional NX NewCenturySchlbk-BoldItalic
+ Proportional PA Palatino-Roman
+ Proportional PB Palatino-Bold
+ Proportional PI Palatino-Italic
+ Proportional PX Palatino-BoldItalic
+ Proportional ZI ZapfChancery-MediumItalic
+ FixedWidth C Courier
+ FixedWidth CB Courier-Bold
+ FixedWidth CI Courier-Oblique
+ FixedWidth CO Courier
+ FixedWidth CW Courier
+ FixedWidth CX Courier-BoldOblique
+ Dingbats ZD ZapfDingbats
+ Greek GR Symbol
+ Symbol S Symbol
+ Special S1 Times-Roman
+ Description DESC ---
+ //End of BuiltinTables
+}
+
+#
+# AllTables prints the complete list of builtin font names.
+#
+
+AllTables() {
+ BuiltinTables | awk '{print $2}'
+}
+
+#
+# Charset functions generate keyword/value pairs (as PostScript objects)
+# that describe the character set available in a font. The keyword is a
+# PostScript string that represents troff's name for the character. The
+# value is usually the literal name (i.e. begins with a /) assigned to
+# the character in the PostScript font. The value can also be an integer
+# or a PostScript string. An integer value is used as an index in the
+# current font's Encoding array. A string value is returned to the host
+# unchanged when the entry for the character is constructed. Entries that
+# have (") as their value are synonyms for the preceeding character.
+#
+# The 18 characters missing from ROM resident fonts on older printers are
+# flagged with the PostScript comment "% missing".
+#
+
+StandardCharset() {
+ cat <<-'//End of StandardCharset'
+ (!) /exclam
+ (") /quotedbl
+ (dq) (") % synonym
+ (#) /numbersign
+ ($) /dollar
+ (%) /percent
+ (&) /ampersand
+ (') /quoteright
+ (\() /parenleft
+ (\)) /parenright
+ (*) /asterisk
+ (+) /plus
+ (,) /comma
+ (-) /hyphen % changed from minus by request
+ (.) /period
+ (/) /slash
+ (0) /zero
+ (1) /one
+ (2) /two
+ (3) /three
+ (4) /four
+ (5) /five
+ (6) /six
+ (7) /seven
+ (8) /eight
+ (9) /nine
+ (:) /colon
+ (;) /semicolon
+ (<) /less
+ (=) /equal
+ (>) /greater
+ (?) /question
+ (@) /at
+ (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
+ ([) /bracketleft
+ (\\) /backslash
+ (bs) (") % synonym
+ (]) /bracketright
+ (^) /asciicircum
+ (_) /underscore
+ (`) /quoteleft
+ (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
+ ({) /braceleft
+ (|) /bar
+ (}) /braceright
+ (~) /asciitilde
+ (\\`) /grave % devpost character
+ (ga) (") % synonym
+ (!!) /exclamdown
+ (c|) /cent
+ (ct) (") % devpost synonym
+ (L-) /sterling
+ (ps) (") % devpost synonym
+ (xo) /currency
+ (cr) (") % devpost synonym
+ (Y-) /yen
+ (yn) (") % devpost synonym
+ (||) /brokenbar % missing
+ (so) /section
+ (sc) (") % devpost synonym
+ ("") /dieresis
+ (:a) (") % devpost synonym
+ (co) /copyright
+ (a_) /ordfeminine
+ (<<) /guillemotleft
+ (-,) /logicalnot
+ (hy) /hyphen
+ (--) /minus
+ (ro) /registered
+ (rg) (") % devpost synonym
+ (-^) /macron
+ (-a) (") % devpost synonym
+ (0^) /degree % missing
+ (+-) /plusminus % missing
+ (2^) /twosuperior % missing
+ (3^) /threesuperior % missing
+ (\\') /acute
+ (aa) (") % devpost synonym
+ (/u) /mu % missing
+ (P!) /paragraph
+ (pg) (") % devpost synonym
+ (.^) /periodcentered
+ (,,) /cedilla
+ (,a) (") % devpost synonym
+ (1^) /onesuperior % missing
+ (o_) /ordmasculine
+ (>>) /guillemotright
+ (14) /onequarter % missing
+ (12) /onehalf % missing
+ (34) /threequarters % missing
+ (??) /questiondown
+ (A`) /Agrave
+ (A') /Aacute
+ (A^) /Acircumflex
+ (A~) /Atilde
+ (A") /Adieresis
+ (A*) /Aring
+ (AE) /AE
+ (C,) /Ccedilla
+ (E`) /Egrave
+ (E') /Eacute
+ (E^) /Ecircumflex
+ (E") /Edieresis
+ (I`) /Igrave
+ (I') /Iacute
+ (I^) /Icircumflex
+ (I") /Idieresis
+ (D-) /Eth % missing
+ (N~) /Ntilde
+ (O`) /Ograve
+ (O') /Oacute
+ (O^) /Ocircumflex
+ (O~) /Otilde
+ (O") /Odieresis
+ (xx) /multiply % missing
+ (O/) /Oslash
+ (U`) /Ugrave
+ (U') /Uacute
+ (U^) /Ucircumflex
+ (U") /Udieresis
+ (Y') /Yacute % missing
+ (TH) /Thorn % missing
+ (ss) /germandbls
+ (a`) /agrave
+ (a') /aacute
+ (a^) /acircumflex
+ (a~) /atilde
+ (a") /adieresis
+ (a*) /aring
+ (ae) /ae
+ (c,) /ccedilla
+ (e`) /egrave
+ (e') /eacute
+ (e^) /ecircumflex
+ (e") /edieresis
+ (i`) /igrave
+ (i') /iacute
+ (i^) /icircumflex
+ (i") /idieresis
+ (d-) /eth % missing
+ (n~) /ntilde
+ (o`) /ograve
+ (o') /oacute
+ (o^) /ocircumflex
+ (o~) /otilde
+ (o") /odieresis
+ (-:) /divide % missing
+ (o/) /oslash
+ (u`) /ugrave
+ (u') /uacute
+ (u^) /ucircumflex
+ (u") /udieresis
+ (y') /yacute % missing
+ (th) /thorn % missing
+ (y") /ydieresis
+ (^a) /circumflex % devpost accent
+ (~a) /tilde % devpost accent
+ (Ua) /breve % devpost accent
+ (.a) /dotaccent % devpost accent
+ (oa) /ring % devpost accent
+ ("a) /hungarumlaut % devpost accent
+ (Ca) /ogonek % devpost accent
+ (va) /caron % devpost accent
+ //End of StandardCharset
+}
+
+#
+# DingbatsCharset guarantees changes in StandardCharset don't show up in ZD.
+#
+
+DingbatsCharset() {
+ cat <<-'//End of DingbatsCharset'
+ (!) /exclam
+ (") /quotedbl
+ (#) /numbersign
+ ($) /dollar
+ (%) /percent
+ (&) /ampersand
+ (') /quoteright
+ (\() /parenleft
+ (\)) /parenright
+ (*) /asterisk
+ (+) /plus
+ (,) /comma
+ (-) /minus % also hyphen in devpost
+ (.) /period
+ (/) /slash
+ (0) /zero
+ (1) /one
+ (2) /two
+ (3) /three
+ (4) /four
+ (5) /five
+ (6) /six
+ (7) /seven
+ (8) /eight
+ (9) /nine
+ (:) /colon
+ (;) /semicolon
+ (<) /less
+ (=) /equal
+ (>) /greater
+ (?) /question
+ (@) /at
+ (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
+ ([) /bracketleft
+ (\\) /backslash
+ (]) /bracketright
+ (^) /asciicircum
+ (_) /underscore
+ (`) /quoteleft
+ (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
+ ({) /braceleft
+ (|) /bar
+ (}) /braceright
+ (~) /asciitilde
+ (\\`) /grave % devpost character
+ (!!) /exclamdown
+ (c|) /cent
+ (L-) /sterling
+ (xo) /currency
+ (Y-) /yen
+ (||) /brokenbar % missing
+ (so) /section
+ ("") /dieresis
+ (co) /copyright
+ (a_) /ordfeminine
+ (<<) /guillemotleft
+ (-,) /logicalnot
+ (hy) /hyphen
+ (ro) /registered
+ (-^) /macron
+ (0^) /degree % missing
+ (+-) /plusminus % missing
+ (2^) /twosuperior % missing
+ (3^) /threesuperior % missing
+ (\\') /acute
+ (/u) /mu % missing
+ (P!) /paragraph
+ (.^) /periodcentered
+ (,,) /cedilla
+ (1^) /onesuperior % missing
+ (o_) /ordmasculine
+ (>>) /guillemotright
+ (14) /onequarter % missing
+ (12) /onehalf % missing
+ (34) /threequarters % missing
+ (??) /questiondown
+ (A`) /Agrave
+ (A') /Aacute
+ (A^) /Acircumflex
+ (A~) /Atilde
+ (A") /Adieresis
+ (A*) /Aring
+ (AE) /AE
+ (C,) /Ccedilla
+ (E`) /Egrave
+ (E') /Eacute
+ (E^) /Ecircumflex
+ (E") /Edieresis
+ (I`) /Igrave
+ (I') /Iacute
+ (I^) /Icircumflex
+ (I") /Idieresis
+ (D-) /Eth % missing
+ (N~) /Ntilde
+ (O`) /Ograve
+ (O') /Oacute
+ (O^) /Ocircumflex
+ (O~) /Otilde
+ (O") /Odieresis
+ (xx) /multiply % missing
+ (O/) /Oslash
+ (U`) /Ugrave
+ (U') /Uacute
+ (U^) /Ucircumflex
+ (U") /Udieresis
+ (Y') /Yacute % missing
+ (TH) /Thorn % missing
+ (ss) /germandbls
+ (a`) /agrave
+ (a') /aacute
+ (a^) /acircumflex
+ (a~) /atilde
+ (a") /adieresis
+ (a*) /aring
+ (ae) /ae
+ (c,) /ccedilla
+ (e`) /egrave
+ (e') /eacute
+ (e^) /ecircumflex
+ (e") /edieresis
+ (i`) /igrave
+ (i') /iacute
+ (i^) /icircumflex
+ (i") /idieresis
+ (d-) /eth % missing
+ (n~) /ntilde
+ (o`) /ograve
+ (o') /oacute
+ (o^) /ocircumflex
+ (o~) /otilde
+ (o") /odieresis
+ (-:) /divide % missing
+ (o/) /oslash
+ (u`) /ugrave
+ (u') /uacute
+ (u^) /ucircumflex
+ (u") /udieresis
+ (y') /yacute % missing
+ (th) /thorn % missing
+ (y") /ydieresis
+ //End of DingbatsCharset
+}
+
+SymbolCharset() {
+ cat <<-'//End of SymbolCharset'
+ (---) /exclam
+ (fa) /universal
+ (---) /numbersign
+ (te) /existential
+ (---) /percent
+ (---) /ampersand
+ (st) /suchthat
+ (---) /parenleft
+ (---) /parenright
+ (**) /asteriskmath
+ (pl) /plus
+ (---) /comma
+ (mi) /minus
+ (---) /period
+ (sl) /slash
+ (---) /zero
+ (---) /one
+ (---) /two
+ (---) /three
+ (---) /four
+ (---) /five
+ (---) /six
+ (---) /seven
+ (---) /eight
+ (---) /nine
+ (---) /colon
+ (---) /semicolon
+ (<) /less
+ (eq) /equal
+ (>) /greater
+ (---) /question
+ (cg) /congruent
+ (*A) /Alpha
+ (\244x) (")
+ (*B) /Beta
+ (\244y) (")
+ (*X) /Chi
+ (\244\257) (")
+ (*D) /Delta
+ (\244{) (")
+ (*E) /Epsilon
+ (\244|) (")
+ (*F) /Phi
+ (\244\256) (")
+ (*G) /Gamma
+ (\244z) (")
+ (*Y) /Eta
+ (\244~) (")
+ (*I) /Iota
+ (\244\241) (")
+ (---) /theta1
+ (\244\331) (")
+ (*K) /Kappa
+ (\244\242) (")
+ (*L) /Lambda
+ (\244\243) (")
+ (*M) /Mu
+ (\244\244) (")
+ (*N) /Nu
+ (\244\245) (")
+ (*O) /Omicron
+ (\244\247) (")
+ (*P) /Pi
+ (\244\250) (")
+ (*H) /Theta
+ (\244\240) (")
+ (*R) /Rho
+ (\244\251) (")
+ (*S) /Sigma
+ (\244\253) (")
+ (*T) /Tau
+ (\244\254) (")
+ (*U) /Upsilon
+ (\244\255) (")
+ (ts) /sigma1
+ (\244\312) (")
+ (*W) /Omega
+ (\244\261) (")
+ (*C) /Xi
+ (\244\246) (")
+ (*Q) /Psi
+ (\244\260) (")
+ (*Z) /Zeta
+ (\244}) (")
+ (---) /bracketleft
+ (tf) /therefore
+ (---) /bracketright
+ (pp) /perpendicular
+ (ul) /underscore
+ (_) (") % synonym
+ (rn) /radicalex
+ (*a) /alpha
+ (\244\271) (")
+ (*b) /beta
+ (\244\272) (")
+ (*x) /chi
+ (\244\317) (")
+ (*d) /delta
+ (\244\274) (")
+ (*e) /epsilon
+ (\244\275) (")
+ (*f) /phi
+ (\244\316) (")
+ (*g) /gamma
+ (\244\273) (")
+ (*y) /eta
+ (\244\277) (")
+ (*i) /iota
+ (\244\301) (")
+ (---) /phi1
+ (\244\335) (")
+ (*k) /kappa
+ (\244\302) (")
+ (*l) /lambda
+ (\244\303) (")
+ (*m) /mu
+ (\244\304) (")
+ (*n) /nu
+ (\244\305) (")
+ (*o) /omicron
+ (\244\307) (")
+ (*p) /pi
+ (\244\310) (")
+ (*h) /theta
+ (\244\300) (")
+ (*r) /rho
+ (\244\311) (")
+ (*s) /sigma
+ (\244\313) (")
+ (*t) /tau
+ (\244\314) (")
+ (*u) /upsilon
+ (\244\315) (")
+ (---) /omega1
+ (\244\336) (")
+ (*w) /omega
+ (\244\321) (")
+ (*c) /xi
+ (\244\306) (")
+ (*q) /psi
+ (\244\320) (")
+ (*z) /zeta
+ (\244\276) (")
+ (---) /braceleft
+ (or) /bar
+ (---) /braceright
+ (ap) /similar
+ (---) /Upsilon1
+ (fm) /minute
+ (<=) /lessequal
+ (fr) /fraction % devpost character
+ (if) /infinity
+ (fn) /florin % devpost character
+ (---) /club
+ (---) /diamond
+ (---) /heart
+ (---) /spade
+ (ab) /arrowboth
+ (<-) /arrowleft
+ (ua) /arrowup
+ (->) /arrowright
+ (da) /arrowdown
+ (de) /degree
+ (+-) /plusminus
+ (---) /second
+ (>=) /greaterequal
+ (mu) /multiply
+ (pt) /proportional
+ (pd) /partialdiff
+ (bu) /bullet
+ (di) /divide
+ (!=) /notequal
+ (==) /equivalence
+ (~~) /approxequal
+ (el) /ellipsis
+ (av) /arrowvertex
+ (ah) /arrowhorizex
+ (CR) /carriagereturn
+ (af) /aleph
+ (If) /Ifraktur
+ (Rf) /Rfraktur
+ (ws) /weierstrass
+ (Ox) /circlemultiply
+ (O+) /circleplus
+ (es) /emptyset
+ (ca) /intersection
+ (cu) /union
+ (sp) /propersuperset
+ (ip) /reflexsuperset
+ (!b) /notsubset
+ (sb) /propersubset
+ (ib) /reflexsubset
+ (mo) /element
+ (!m) /notelement
+ (an) /angle
+ (gr) /gradient
+ (rg) /registerserif
+ (co) /copyrightserif
+ (tm) /trademarkserif
+ (---) /product
+ (sr) /radical
+ (c.) /dotmath
+ (no) /logicalnot
+ (l&) /logicaland
+ (l|) /logicalor
+ (---) /arrowdblboth
+ (---) /arrowdblleft
+ (---) /arrowdblup
+ (---) /arrowdblright
+ (---) /arrowdbldown
+ (lz) /lozenge
+ (b<) /angleleft
+ (RG) /registersans
+ (CO) /copyrightsans
+ (TM) /trademarksans
+ (---) /summation
+ (LT) /parenlefttp
+ (br) /parenleftex
+ (LX) (") % synonym
+ (LB) /parenleftbt
+ (lc) /bracketlefttp
+ (lx) /bracketleftex
+ (lf) /bracketleftbt
+ (lt) /bracelefttp
+ (lk) /braceleftmid
+ (lb) /braceleftbt
+ (bv) /braceex
+ (|) (") % synonym
+ (b>) /angleright
+ (is) /integral
+ (---) /integraltp
+ (---) /integralex
+ (---) /integralbt
+ (RT) /parenrighttp
+ (RX) /parenrightex
+ (RB) /parenrightbt
+ (rc) /bracketrighttp
+ (rx) /bracketrightex
+ (rf) /bracketrightbt
+ (rt) /bracerighttp
+ (rk) /bracerightmid
+ (rb) /bracerightbt
+ (~=) (55 0 1) % charlib
+ //End of SymbolCharset
+}
+
+SpecialCharset() {
+ cat <<-'//End of SpecialCharset'
+ (ru) /underscore
+ ('') /quotedblright % devpost character
+ (``) /quotedblleft % devpost character
+ (dg) /dagger % devpost character
+ (dd) /daggerdbl % devpost character
+ (en) /endash % devpost character
+ (\\-) (") % synonym
+ (em) /emdash
+% (ff) (60 2 1) % charlib
+% (Fi) (84 2 1) % charlib
+% (Fl) (84 2 1) % charlib
+ (14) (75 2 1) % charlib
+ (12) (75 2 1) % charlib
+ (34) (75 2 1) % charlib
+ (bx) (50 2 1) % charlib
+ (ob) (38 2 1) % charlib
+ (ci) (75 0 1) % charlib
+ (sq) (50 2 1) % charlib
+ (Sl) (50 2 1) % charlib
+ (L1) (110 1 1) % charlib
+ (LA) (110 1 1) % charlib
+ (LV) (110 3 1) % charlib
+ (LH) (210 1 1) % charlib
+ (lh) (100 0 1) % charlib
+ (rh) (100 0 1) % charlib
+ (lH) (100 0 1) % charlib
+ (rH) (100 0 1) % charlib
+ (PC) (220 2 1) % charlib
+ (DG) (185 2 1) % charlib
+ //End of SpecialCharset
+}
+
+#
+# Latin1 ensures a font uses the ISOLatin1Encoding vector, although only
+# text fonts should be re-encoded. Downloading the Encoding vector doesn't
+# often make sense. No ISOLatin1Encoding array likely means ROM based fonts
+# on your printer are incomplete. Type 1 fonts with a full Latin1 character
+# set appeared sometime after Version 50.0.
+#
+
+Latin1() {
+ if [ "$DOWNLOADVECTOR" = TRUE ]; then
+ cat <<-'//End of ISOLatin1Encoding'
+ /ISOLatin1Encoding [
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /space
+ /exclam
+ /quotedbl
+ /numbersign
+ /dollar
+ /percent
+ /ampersand
+ /quoteright
+ /parenleft
+ /parenright
+ /asterisk
+ /plus
+ /comma
+ /minus
+ /period
+ /slash
+ /zero
+ /one
+ /two
+ /three
+ /four
+ /five
+ /six
+ /seven
+ /eight
+ /nine
+ /colon
+ /semicolon
+ /less
+ /equal
+ /greater
+ /question
+ /at
+ /A
+ /B
+ /C
+ /D
+ /E
+ /F
+ /G
+ /H
+ /I
+ /J
+ /K
+ /L
+ /M
+ /N
+ /O
+ /P
+ /Q
+ /R
+ /S
+ /T
+ /U
+ /V
+ /W
+ /X
+ /Y
+ /Z
+ /bracketleft
+ /backslash
+ /bracketright
+ /asciicircum
+ /underscore
+ /quoteleft
+ /a
+ /b
+ /c
+ /d
+ /e
+ /f
+ /g
+ /h
+ /i
+ /j
+ /k
+ /l
+ /m
+ /n
+ /o
+ /p
+ /q
+ /r
+ /s
+ /t
+ /u
+ /v
+ /w
+ /x
+ /y
+ /z
+ /braceleft
+ /bar
+ /braceright
+ /asciitilde
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /dotlessi
+ /grave
+ /acute
+ /circumflex
+ /tilde
+ /macron
+ /breve
+ /dotaccent
+ /dieresis
+ /.notdef
+ /ring
+ /cedilla
+ /.notdef
+ /hungarumlaut
+ /ogonek
+ /caron
+ /space
+ /exclamdown
+ /cent
+ /sterling
+ /currency
+ /yen
+ /brokenbar
+ /section
+ /dieresis
+ /copyright
+ /ordfeminine
+ /guillemotleft
+ /logicalnot
+ /hyphen
+ /registered
+ /macron
+ /degree
+ /plusminus
+ /twosuperior
+ /threesuperior
+ /acute
+ /mu
+ /paragraph
+ /periodcentered
+ /cedilla
+ /onesuperior
+ /ordmasculine
+ /guillemotright
+ /onequarter
+ /onehalf
+ /threequarters
+ /questiondown
+ /Agrave
+ /Aacute
+ /Acircumflex
+ /Atilde
+ /Adieresis
+ /Aring
+ /AE
+ /Ccedilla
+ /Egrave
+ /Eacute
+ /Ecircumflex
+ /Edieresis
+ /Igrave
+ /Iacute
+ /Icircumflex
+ /Idieresis
+ /Eth
+ /Ntilde
+ /Ograve
+ /Oacute
+ /Ocircumflex
+ /Otilde
+ /Odieresis
+ /multiply
+ /Oslash
+ /Ugrave
+ /Uacute
+ /Ucircumflex
+ /Udieresis
+ /Yacute
+ /Thorn
+ /germandbls
+ /agrave
+ /aacute
+ /acircumflex
+ /atilde
+ /adieresis
+ /aring
+ /ae
+ /ccedilla
+ /egrave
+ /eacute
+ /ecircumflex
+ /edieresis
+ /igrave
+ /iacute
+ /icircumflex
+ /idieresis
+ /eth
+ /ntilde
+ /ograve
+ /oacute
+ /ocircumflex
+ /otilde
+ /odieresis
+ /divide
+ /oslash
+ /ugrave
+ /uacute
+ /ucircumflex
+ /udieresis
+ /yacute
+ /thorn
+ /ydieresis
+ ] def
+ //End of ISOLatin1Encoding
+ fi
+
+ echo "ISOLatin1Encoding /$1 ReEncode"
+}
+
+#
+# Generating functions output PostScript programs that build font width
+# tables or a typesetter description file. Send the program to a printer
+# and the complete table will come back on the serial port. All write on
+# stdout and assume the prologue and other required PostScript files are
+# all available.
+#
+
+Proportional() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ # Get <>_ and | from S. Use accents for ascii ^ and ~.
+ StandardCharset | awk '
+ $1 == "(<)" && $2 == "/less" {$1 = "(---)"}
+ $1 == "(>)" && $2 == "/greater" {$1 = "(---)"}
+ $1 == "(_)" && $2 == "/underscore" {$1 = "(---)"}
+ $1 == "(|)" && $2 == "/bar" {$1 = "(---)"}
+ $1 == "(^)" && $2 == "/asciicircum" {
+ printf "(^)\t/circumflex\n"
+ $1 = "(---)"
+ }
+ $1 == "(~)" && $2 == "/asciitilde" {
+ printf "(~)\t/tilde\n"
+ $1 = "(---)"
+ }
+ {printf "%s\t%s\n", $1, $2}
+ '
+ echo "] def"
+
+ Latin1 $2
+ echo "/$2 SelectFont"
+ echo "(opO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+FixedWidth() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ StandardCharset
+ echo "] def"
+
+ Latin1 $2
+ echo "/$2 SelectFont"
+ echo "(opO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Dingbats() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ DingbatsCharset | awk '$1 != "(---)" && $2 ~ /^\/[a-zA-Z]/ {
+ printf "%s\tISOLatin1Encoding %s GetCode\n", $1, $2
+ }'
+ echo "] def"
+
+ echo "/$2 SelectFont"
+ echo "( ) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Greek() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SymbolCharset | awk '
+ BEGIN {hit = -1}
+ $1 ~ /\(\*[a-zA-Z]\)/ {print; hit = NR}
+ $2 == "(\")" && hit == NR-1 {print; hit = NR}
+ '
+ echo "] def"
+
+ echo "/$2 SelectFont"
+ echo "(orO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Symbol() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SymbolCharset
+ echo "] def"
+
+ echo "ChangeMetrics"
+ echo "/S SelectFont"
+ echo "(orO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(special\\\\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Special() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SpecialCharset
+ echo "] def"
+
+ echo "ChangeMetrics"
+ echo "/S1 SelectFont"
+
+ echo "(# Times-Roman special font\\\\n) Print"
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(special\\\\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+#
+# The DESC file doesn't have to be built on a printer. It's only here for
+# consistency.
+#
+
+Description() {
+ echo "/charset [" # awk - so the stack doesn't overflow
+ StandardCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ SymbolCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ SpecialCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ echo "] def"
+
+ cat <<-//DESC
+ (#Device Description - utf character set
+
+ PDL PostScript
+ Encoding Latin1
+
+ fonts 10 R I B BI CW H HI HB S1 S
+ sizes 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 38 40 42 44 46
+ 48 50 52 54 56 58 60 64 68 72 78 84 90 96 100 105 110 115
+ 120 125 130 135 140 145 150 155 160 0
+ res $RESOLUTION
+ hor 1
+ vert 1
+ unitwidth $UNITWIDTH
+
+ ) Print
+ //DESC
+ echo "(charset\\\\n) Print"
+ echo "BuildDescCharset"
+ echo "(\\\\n) Print"
+}
+
diff --git a/sys/src/cmd/postscript/tr2post/tr2post.c b/sys/src/cmd/postscript/tr2post/tr2post.c
new file mode 100755
index 000000000..aeb8bbcf2
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/tr2post.c
@@ -0,0 +1,219 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+int formsperpage = 1;
+int picflag = 1;
+double aspectratio = 1.0;
+int copies = 1;
+int landscape = 0;
+double magnification = 1.0;
+int linesperpage = 66;
+int pointsize = 10;
+double xoffset = .25;
+double yoffset = .25;
+char *passthrough = 0;
+
+Biobuf binp, *bstdout, bstderr;
+Biobufhdr *Bstdin, *Bstdout, *Bstderr;
+int debug = 0;
+
+char tmpfilename[MAXTOKENSIZE];
+char copybuf[BUFSIZ];
+
+
+struct charent **build_char_list = 0;
+int build_char_cnt = 0;
+
+void
+prologues(void) {
+ int i;
+ char charlibname[MAXTOKENSIZE];
+
+ Bprint(Bstdout, "%s", CONFORMING);
+ Bprint(Bstdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ Bprint(Bstdout, "%s %s\n", CREATOR, PROGRAMNAME);
+ Bprint(Bstdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ Bprint(Bstdout, "%s %s\n", PAGES, ATEND);
+ Bprint(Bstdout, "%s", ENDCOMMENTS);
+
+ if (cat(DPOST)) {
+ Bprint(Bstderr, "can't read %s\n", DPOST);
+ exits("dpost prologue");
+ }
+
+ if (drawflag) {
+ if (cat(DRAW)) {
+ Bprint(Bstderr, "can't read %s\n", DRAW);
+ exits("draw prologue");
+ }
+ }
+
+ if (DOROUND)
+ cat(ROUNDPAGE);
+
+ Bprint(Bstdout, "%s", ENDPROLOG);
+ Bprint(Bstdout, "%s", BEGINSETUP);
+ Bprint(Bstdout, "mark\n");
+ if (formsperpage > 1) {
+ Bprint(Bstdout, "%s %d\n", FORMSPERPAGE, formsperpage);
+ Bprint(Bstdout, "/formsperpage %d def\n", formsperpage);
+ }
+ if (aspectratio != 1) Bprint(Bstdout, "/aspectratio %g def\n", aspectratio);
+ if (copies != 1) Bprint(Bstdout, "/#copies %d store\n", copies);
+ if (landscape) Bprint(Bstdout, "/landscape true def\n");
+ if (magnification != 1) Bprint(Bstdout, "/magnification %g def\n", magnification);
+ if (pointsize != 10) Bprint(Bstdout, "/pointsize %d def\n", pointsize);
+ if (xoffset != .25) Bprint(Bstdout, "/xoffset %g def\n", xoffset);
+ if (yoffset != .25) Bprint(Bstdout, "/yoffset %g def\n", yoffset);
+ cat(ENCODINGDIR"/Latin1.enc");
+ if (passthrough != 0) Bprint(Bstdout, "%s\n", passthrough);
+
+ Bprint(Bstdout, "setup\n");
+ if (formsperpage > 1) {
+ cat(FORMFILE);
+ Bprint(Bstdout, "%d setupforms \n", formsperpage);
+ }
+/* output Build character info from charlib if necessary. */
+
+ for (i=0; i<build_char_cnt; i++) {
+ sprint(charlibname, "%s/%s", CHARLIB, build_char_list[i]->name);
+ if (cat(charlibname))
+ Bprint(Bstderr, "cannot open %s\n", charlibname);
+ }
+
+ Bprint(Bstdout, "%s", ENDSETUP);
+}
+
+void
+cleanup(void) {
+ remove(tmpfilename);
+}
+
+main(int argc, char *argv[]) {
+ Biobuf *binp;
+ Biobufhdr *Binp;
+ int i, tot, ifd;
+ char *t;
+
+ programname = argv[0];
+ if (Binit(&bstderr, 2, OWRITE) == Beof) {
+ exits("Binit");
+ }
+ Bstderr = &bstderr.Biobufhdr;
+
+ tmpnam(tmpfilename);
+ if ((bstdout=Bopen(tmpfilename, OWRITE)) == 0) {
+ Bprint(Bstderr, "cannot open temporary file %s\n", tmpfilename);
+ exits("Bopen");
+ }
+ atexit(cleanup);
+ Bstdout = &bstdout->Biobufhdr;
+
+ ARGBEGIN{
+ case 'a': /* aspect ratio */
+ aspectratio = atof(ARGF());
+ break;
+ case 'c': /* copies */
+ copies = atoi(ARGF());
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'm': /* magnification */
+ magnification = atof(ARGF());
+ break;
+ case 'n': /* forms per page */
+ formsperpage = atoi(ARGF());
+ break;
+ case 'o': /* output page list */
+ pagelist(ARGF());
+ break;
+ case 'p': /* landscape or portrait mode */
+ if ( ARGF()[0] == 'l' )
+ landscape = 1;
+ else
+ landscape = 0;
+ break;
+ case 'x': /* shift things horizontally */
+ xoffset = atof(ARGF());
+ break;
+ case 'y': /* and vertically on the page */
+ yoffset = atof(ARGF());
+ break;
+ case 'P': /* PostScript pass through */
+ t = ARGF();
+ i = strlen(t) + 1;
+ passthrough = malloc(i);
+ if (passthrough == 0) {
+ Bprint(Bstderr, "cannot allocate memory for argument string\n");
+ exits("malloc");
+ }
+ strncpy(passthrough, t, i);
+ break;
+ default: /* don't know what to do for ch */
+ Bprint(Bstderr, "unknown option %C\n", ARGC());
+ break;
+ }ARGEND;
+ readDESC();
+ if (argc == 0) {
+ if ((binp = (Biobuf *)malloc(sizeof(Biobuf))) == nil) {
+ Bprint(Bstderr, "malloc failed.\n");
+ exits("malloc");
+ }
+ if (Binit(binp, 0, OREAD) == Beof) {
+ Bprint(Bstderr, "Binit of <stdin> failed.\n");
+ exits("Binit");
+ }
+ Binp = &(binp->Biobufhdr);
+ if (debug) Bprint(Bstderr, "using standard input\n");
+ conv(Binp);
+ Bterm(Binp);
+ }
+ for (i=0; i<argc; i++) {
+ if ((binp=Bopen(argv[i], OREAD)) == 0) {
+ Bprint(Bstderr, "cannot open file %s\n", argv[i]);
+ continue;
+ }
+ Binp = &(binp->Biobufhdr);
+ inputfilename = argv[i];
+ conv(Binp);
+ Bterm(Binp);
+ }
+ Bterm(Bstdout);
+
+ if ((ifd=open(tmpfilename, OREAD)) < 0) {
+ Bprint(Bstderr, "open of %s failed.\n", tmpfilename);
+ exits("open");
+ }
+
+ bstdout = galloc(0, sizeof(Biobuf), "bstdout");
+ if (Binit(bstdout, 1, OWRITE) == Beof) {
+ Bprint(Bstderr, "Binit of <stdout> failed.\n");
+ exits("Binit");
+ }
+ Bstdout = &(bstdout->Biobufhdr);
+ prologues();
+ Bflush(Bstdout);
+ tot = 0; i = 0;
+ while ((i=read(ifd, copybuf, BUFSIZ)) > 0) {
+ if (write(1, copybuf, i) != i) {
+ Bprint(Bstderr, "write error on copying from temp file.\n");
+ exits("write");
+ }
+ tot += i;
+ }
+ if (debug) Bprint(Bstderr, "copied %d bytes to final output i=%d\n", tot, i);
+ if (i < 0) {
+ Bprint(Bstderr, "read error on copying from temp file.\n");
+ exits("read");
+ }
+ finish();
+
+ exits("");
+}
diff --git a/sys/src/cmd/postscript/tr2post/tr2post.h b/sys/src/cmd/postscript/tr2post/tr2post.h
new file mode 100755
index 000000000..030c6cb7a
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/tr2post.h
@@ -0,0 +1,103 @@
+#define MAXSPECHARS 512
+#define MAXTOKENSIZE 128
+#define CHARLIB "/sys/lib/troff/font/devutf/charlib"
+
+extern int debug;
+extern int fontsize;
+extern int fontpos;
+extern int resolution; /* device resolution, goobies per inch */
+extern int minx; /* minimum x motion */
+extern int miny; /* minimum y motion */
+extern char devname[];
+extern int devres;
+extern int unitwidth;
+extern char *printdesclang;
+extern char *encoding;
+extern int fontmnt;
+extern char **fontmtab;
+
+extern int curtrofffontid; /* index into trofftab of current troff font */
+extern int troffontcnt;
+
+extern BOOLEAN drawflag;
+
+struct specname {
+ char *str;
+ struct specname *next;
+};
+
+/* character entries for special characters (those pointed
+ * to by multiple character names, e.g. \(mu for multiply.
+ */
+struct charent {
+ char postfontid; /* index into pfnamtab */
+ char postcharid; /* e.g., 0x00 */
+ short troffcharwidth;
+ char *name;
+ struct charent *next;
+};
+
+extern struct charent **build_char_list;
+extern int build_char_cnt;
+
+struct pfnament {
+ char *str;
+ int used;
+};
+
+/* these entries map troff character code ranges to
+ * postscript font and character ranges.
+ */
+struct psfent {
+ int start;
+ int end;
+ int offset;
+ int psftid;
+};
+
+struct troffont {
+ char *trfontid; /* the common troff font name e.g., `R' */
+ BOOLEAN special; /* flag says this is a special font. */
+ int spacewidth;
+ int psfmapsize;
+ struct psfent *psfmap;
+ struct charent *charent[NUMOFONTS][FONTSIZE];
+};
+
+extern struct troffont *troffontab;
+extern struct charent spechars[];
+
+/** prototypes **/
+void initialize(void);
+void mountfont(int, char*);
+int findtfn(char *, int);
+void runeout(Rune);
+void specialout(char *);
+long nametorune(char *);
+void conv(Biobufhdr *);
+void hgoto(int);
+void vgoto(int);
+void hmot(int);
+void vmot(int);
+void draw(Biobufhdr *);
+void devcntl(Biobufhdr *);
+void notavail(char *);
+void error(int, char *, ...);
+void loadfont(int, char *);
+void flushtext(void);
+void t_charht(int);
+void t_slant(int);
+void startstring(void);
+void endstring(void);
+BOOLEAN pageon(void);
+void setpsfont(int, int);
+void settrfont(void);
+int hash(char *, int);
+BOOLEAN readDESC(void);
+void finish(void);
+void ps_include(Biobufhdr *, Biobufhdr *, int, int,
+ int, int, double, double, double, double,
+ double, double, double);
+void picture(Biobufhdr *, char *);
+void beginpath(char*, int);
+void drawpath(char*, int);
diff --git a/sys/src/cmd/postscript/tr2post/utfmap b/sys/src/cmd/postscript/tr2post/utfmap
new file mode 100755
index 000000000..2f9d6dc37
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/utfmap
@@ -0,0 +1,47 @@
+¡ !! ¢ c$ £ l$ ¤ g$
+¥ y$ ¦ || § SS ¨ ""
+© cO ª sa « << ¬ no
+­ -- ® rO ¯ __ ° de
+± +- ² s2 ³ s3 ´ ''
+µ mi ¶ pg · .. ¸ ,,
+¹ s1 º s0 » >> ¼ 14
+½ 12 ¾ 34 ¿ ?? À `A
+Á 'A Â ^A Ã ~A Ä "A
+Å oA Æ AE Ç ,C È `E
+É 'E Ê ^E Ë "E Ì `I
+Í 'I Î ^I Ï "I Ð D-
+Ñ ~N Ò `O Ó 'O Ô ^O
+Õ ~O Ö "O × mu Ø /O
+Ù `U Ú 'U Û ^U Ü "U
+Ý 'Y Þ |P ß ss à `a
+á 'a â ^a ã ~a ä "a
+å oa æ ae ç ,c è `e
+é 'e ê ^e ë "e ì `i
+í 'i î ^i ï "i ð d-
+ñ ~n ò `o ó 'o ô ^o
+õ ~o ö "o ÷ -: ø /o
+ù `u ú 'u û ^u ü "u
+ý 'y þ |p ÿ "y α *a
+β *b γ *g δ *d ε *e
+ζ *z η *y θ *h ι *i
+κ *k λ *l *m μ ν *n
+ξ *c ο *o π *p ρ *r
+ς ts σ *s τ *t υ *u
+φ *f χ *x ψ *q ω *w
+Α *A Β *B Γ *G Δ *D
+Ε *E Ζ *Z Η *Y Θ *H
+Ι *I Κ *K Λ *L Μ *M
+Ν *N Ξ *C Ο *O Π *P
+Ρ *R Σ *S Τ *T Υ *U
+Φ *F Χ *X Ψ *Q Ω *W
+← <- ↑ ua → -> ↓ da
+↔ ab ∀ fa ∃ te ∂ pd
+∅ es ∆ *D ∇ gr ∉ !m
+∍ st ∗ ** ∙ bu √ sr
+∝ pt ∞ if ∠ an ∧ l&
+∨ l| ∩ ca ∪ cu ∫ is
+∴ tf ≃ ~= ≅ cg ≈ ~~
+≠ != ≡ == ≦ <= ≧ >=
+⊂ sb ⊃ sp ⊄ !b ⊆ ib
+⊇ ip ⊕ O+ ⊖ O- ⊗ Ox
+⊢ tu ⊨ Tu ⋄ lz ⋯ el
diff --git a/sys/src/cmd/postscript/tr2post/utils.c b/sys/src/cmd/postscript/tr2post/utils.c
new file mode 100755
index 000000000..e9979ff1e
--- /dev/null
+++ b/sys/src/cmd/postscript/tr2post/utils.c
@@ -0,0 +1,264 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+int hpos = 0, vpos = 0;
+int fontsize, fontpos;
+
+#define MAXSTR 128
+int trindex; /* index into trofftab of current troff font */
+static int expecthmot = 0;
+
+void
+initialize(void) {
+}
+
+void
+hgoto(int x) {
+ hpos = x;
+ if (pageon()) {
+ endstring();
+/* Bprint(Bstdout, "%d %d m\n", hpos, vpos); */
+ }
+}
+
+void
+vgoto(int y) {
+ vpos = y;
+ if (pageon()) {
+ endstring();
+/* Bprint(Bstdout, "%d %d m\n", hpos, vpos); */
+ }
+}
+
+void
+hmot(int x) {
+ int delta;
+
+ if ((x<expecthmot-1) || (x>expecthmot+1)) {
+ delta = x - expecthmot;
+ if (curtrofffontid <0 || curtrofffontid >= troffontcnt) {
+ Bprint(Bstderr, "troffontcnt=%d curtrofffontid=%d\n", troffontcnt, curtrofffontid);
+ Bflush(Bstderr);
+ exits("");
+ }
+ if (delta == troffontab[curtrofffontid].spacewidth*fontsize/10 && isinstring()) {
+ if (pageon()) runeout(' ');
+ } else {
+ if (pageon()) {
+ endstring();
+ /* Bprint(Bstdout, " %d 0 rmoveto ", delta); */
+/* Bprint(Bstdout, " %d %d m ", hpos+x, vpos); */
+ if (debug) Bprint(Bstderr, "x=%d expecthmot=%d\n", x, expecthmot);
+ }
+ }
+ }
+ hpos += x;
+ expecthmot = 0;
+}
+
+void
+vmot(int y) {
+ endstring();
+/* Bprint(Bstdout, " 0 %d rmoveto ", -y); */
+ vpos += y;
+}
+
+struct charent **
+findglyph(int trfid, Rune rune, char *stoken) {
+ struct charent **cp;
+
+ for (cp = &(troffontab[trfid].charent[RUNEGETGROUP(rune)][RUNEGETCHAR(rune)]); *cp != 0; cp = &((*cp)->next)) {
+ if ((*cp)->name) {
+ if (debug) Bprint(Bstderr, "looking for <%s>, have <%s> in font %s\n", stoken, (*cp)->name, troffontab[trfid].trfontid);
+ if (strcmp((*cp)->name, stoken) == 0)
+ break;
+ }
+ }
+ return(cp);
+}
+
+/* output glyph. Use first rune to look up character (hash)
+ * then use stoken UTF string to find correct glyph in linked
+ * list of glyphs in bucket.
+ */
+void
+glyphout(Rune rune, char *stoken, BOOLEAN specialflag) {
+ struct charent **cp;
+ struct troffont *tfp;
+ struct psfent *psfp;
+ int i, t;
+ int fontid; /* this is the troff font table index, not the mounted font table index */
+ int mi, fi, wid;
+ Rune r;
+
+ settrfont();
+
+ /* check current font for the character, special or not */
+ fontid = curtrofffontid;
+if (debug) fprint(2, " looking through current font: trying %s\n", troffontab[fontid].trfontid);
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+
+ if (specialflag) {
+ if (expecthmot) hmot(0);
+
+ /* check special fonts for the special character */
+ /* cycle through the (troff) mounted fonts starting at the next font */
+ for (mi=0; mi<fontmnt; mi++) {
+ if (troffontab[fontid].trfontid==0) error(WARNING, "glyphout:troffontab[%d].trfontid=0x%x, botch!\n",
+ fontid, troffontab[fontid].trfontid);
+ if (fontmtab[mi]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x, fontmnt=%d\n", mi, fontmtab[mi], fontmnt);
+ continue;
+ }
+ if (strcmp(troffontab[fontid].trfontid, fontmtab[mi])==0) break;
+ }
+ if (mi==fontmnt) error(FATAL, "current troff font is not mounted, botch!\n");
+ for (i=(mi+1)%fontmnt; i!=mi; i=(i+1)%fontmnt) {
+ if (fontmtab[i]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x, fontmnt=%d\n", i, fontmtab[i], fontmnt);
+ continue;
+ }
+ fontid = findtfn(fontmtab[i], TRUE);
+if (debug) fprint(2, " looking through special fonts: trying %s\n", troffontab[fontid].trfontid);
+ if (troffontab[fontid].special) {
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ /* check font 1 (if current font is not font 1) for the special character */
+ if (mi != 1) {
+ fontid = findtfn(fontmtab[1], TRUE);;
+if (debug) fprint(2, " looking through font at position 1: trying %s\n", troffontab[fontid].trfontid);
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ if (*cp == 0) {
+ error(WARNING, "cannot find glyph, rune=0x%x stoken=<%s> troff font %s\n", rune, stoken,
+ troffontab[curtrofffontid].trfontid);
+ expecthmot = 0;
+ }
+
+ /* use the peter face in lieu of the character that we couldn't find */
+ rune = 'p'; stoken = "pw";
+ for (i=(mi+1)%fontmnt; i!=mi; i=(i+1)%fontmnt) {
+ if (fontmtab[i]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x\n", i, fontmtab[i]);
+ continue;
+ }
+ fontid = findtfn(fontmtab[i], TRUE);
+if (debug) fprint(2, " looking through special fonts: trying %s\n", troffontab[fontid].trfontid);
+ if (troffontab[fontid].special) {
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ if (*cp == 0) {
+ error(WARNING, "cannot find glyph, rune=0x%x stoken=<%s> troff font %s\n", rune, stoken,
+ troffontab[curtrofffontid].trfontid);
+ expecthmot = 0;
+ return;
+ }
+
+foundit:
+ t = (((*cp)->postfontid&0xff)<<8) | ((*cp)->postcharid&0xff);
+ if (debug) {
+ Bprint(Bstderr, "runeout(0x%x)<%C> postfontid=0x%x postcharid=0x%x troffcharwidth=%d\n",
+ rune, rune, (*cp)->postfontid, (*cp)->postcharid, (*cp)->troffcharwidth);
+ }
+
+ tfp = &(troffontab[fontid]);
+ for (i=0; i<tfp->psfmapsize; i++) {
+ psfp = &(tfp->psfmap[i]);
+ if(t>=psfp->start && t<=psfp->end) break;
+ }
+ if (i >= tfp->psfmapsize)
+ error(FATAL, "character <0x%x> does not have a Postscript font defined.\n", rune);
+
+ setpsfont(psfp->psftid, fontsize);
+
+ if (t == 0x0001) { /* character is in charlib */
+ endstring();
+ if (pageon()) {
+ struct charent *tcp;
+
+ Bprint(Bstdout, "%d %d m ", hpos, vpos);
+ /* if char is unicode character rather than name, clean up for postscript */
+ wid = chartorune(&r, (*cp)->name);
+ if(' '<r && r<0x7F)
+ Bprint(Bstdout, "%d build_%s\n", (*cp)->troffcharwidth, (*cp)->name);
+ else{
+ if((*cp)->name[wid] != 0)
+ error(FATAL, "character <%s> badly named\n", (*cp)->name);
+ Bprint(Bstdout, "%d build_X%.4x\n", (*cp)->troffcharwidth, r);
+ }
+
+ /* stash charent pointer in a list so that we can print these character definitions
+ * in the prologue.
+ */
+ for (i=0; i<build_char_cnt; i++)
+ if (*cp == build_char_list[i]) break;
+ if (i == build_char_cnt) {
+ build_char_list = galloc(build_char_list, sizeof(struct charent *) * ++build_char_cnt,
+ "build_char_list");
+ build_char_list[build_char_cnt-1] = *cp;
+ }
+ }
+ expecthmot = (*cp)->troffcharwidth * fontsize / unitwidth;
+ } else if (isinstring() || rune != ' ') {
+ startstring();
+ if (pageon()) {
+ if (rune == ' ')
+ Bprint(Bstdout, " ");
+ else
+ Bprint(Bstdout, "%s", charcode[RUNEGETCHAR(t)].str);
+ }
+ expecthmot = (*cp)->troffcharwidth * fontsize / unitwidth;
+ }
+}
+
+/* runeout puts a symbol into a string (queue) to be output.
+ * It also has to keep track of the current and last symbol
+ * output to check that the spacing is correct by default
+ * or needs to be adjusted with a spacing operation.
+ */
+
+void
+runeout(Rune rune) {
+ char stoken[UTFmax+1];
+ int i;
+
+ i = runetochar(stoken, &rune);
+ stoken[i] = '\0';
+ glyphout(rune, stoken, TRUE);
+}
+
+void
+specialout(char *stoken) {
+ Rune rune;
+ int i;
+
+ i = chartorune(&rune, stoken);
+ glyphout(rune, stoken, TRUE);
+}
+
+void
+graphfunc(Biobufhdr *bp) {
+}
+
+long
+nametorune(char *name) {
+ return(0);
+}
+
+void
+notavail(char *msg) {
+ Bprint(Bstderr, "%s is not available at this time.\n", msg);
+}
diff --git a/sys/src/cmd/postscript/trofftable/mkfile b/sys/src/cmd/postscript/trofftable/mkfile
new file mode 100755
index 000000000..38f0720d0
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/mkfile
@@ -0,0 +1,42 @@
+</$objtype/mkfile
+
+MAKE=mk
+MAKEFILE=postscript.mk
+
+SYSTEM=plan9
+VERSION=3.3.1
+
+ROOT=
+FONTDIR=$ROOT/sys/lib/troff/font
+MAN1DIR=$ROOT/tmp
+POSTBIN=$ROOT/sys/lib/postscript/bin
+POSTLIB=$ROOT/sys/lib/postscript/prologues
+
+
+all :V: trofftable
+
+install :V: $POSTBIN/rc/trofftable $POSTLIB/trofftable.ps $MAN1DIR/trofftable.1
+
+installall :V: install
+
+clean :V:
+ rm -f trofftable
+
+clobber :V: clean
+
+$POSTBIN/rc/trofftable : trofftable
+ cp $prereq $target
+
+$POSTLIB/trofftable.ps : trofftable.ps
+ cp $prereq $target
+
+$MAN1DIR/trofftable.1 : trofftable.1
+ cp $prereq $target
+
+trofftable : trofftable.rc
+ sed \
+ -e 's?^FONTDIR=.*?FONTDIR='$FONTDIR'?' \
+ -e 's?^POSTBIN=.*?POSTBIN='$POSTBIN'?' \
+ -e 's?^POSTLIB=.*?POSTLIB='$POSTLIB'?' \
+ trofftable.rc >trofftable
+ chmod 775 trofftable
diff --git a/sys/src/cmd/postscript/trofftable/trofftable.1 b/sys/src/cmd/postscript/trofftable/trofftable.1
new file mode 100755
index 000000000..031e2ed04
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/trofftable.1
@@ -0,0 +1,164 @@
+.ds dF /usr/lib/font
+.ds dQ /usr/lib/postscript
+.TH TROFFTABLE 1 "DWB 3.2"
+.SH NAME
+.B trofftable
+\- output a PostScript program that builds a font width table
+.SH SYNOPSIS
+\*(mBtrofftable\f1
+.OP "" options []
+.OP "" shortname
+.OP "" longname []
+.SH DESCRIPTION
+.B trofftable
+writes a PostScript program on the standard output that builds a
+font width table or typesetter description file.
+The following
+.I options
+are understood:
+.TP 1.0i
+.OP \-t name
+Use
+.I name
+as the template for fonts not in the default set.
+Choose
+.MW R
+for proportionally spaced fonts and
+.MW CW
+for fixed width fonts.
+Try
+.MW ZD
+(ZapfDingbats) if the font has a non-standard
+character set.
+The default is
+.MR R .
+.TP 1.0i
+.OP \-C file
+Copy
+.I file
+into each PostScript table program;
+.I file
+must contain legitimate PostScript.
+.TP 1.0i
+.OP \-H hostdir
+Use
+.I hostdir
+as the host resident font directory.
+A file in
+.I hostdir
+that matches the name of the troff font is assumed to be a host
+resident font program and is included in the PostScript width
+table program.
+There is no default.
+.TP 1.0i
+.OP \-L file
+Use
+.I file
+as the PostScript prologue.
+.br
+The default is
+.MW \*(dQ/trofftable.ps
+.TP 1.0i
+.OP \-S file
+Use
+.I file
+as the shell library file.
+Overrides the choice made with the
+.OP \-T
+option.
+.TP 1.0i
+.OP \-T name
+Set the target device to
+.IR name .
+Device
+.I name
+means
+.br
+.MI \*(dF/dev name /shell.lib
+is the shell library file.
+There is no default.
+.PP
+One of
+.OP \-T
+or
+.OP \-S
+is required.
+If both are given
+.OP \-S
+wins.
+Either
+.OP \-H
+or
+.OP \-C
+can be used to include a host resident font.
+.PP
+The shell library file defines a collection of functions used to
+build troff tables.
+The default set of tables is the list of names returned by the
+.MW AllTables
+function.
+Changes to the default list can be made by updating the
+.MW BuiltinTables
+function.
+.PP
+.I Shortname
+is the name of the
+.B troff
+font and
+.I longname
+is the name of the PostScript font;
+.I longname
+can be omitted only if
+.I shortname
+is a default table name.
+PostScript table programs created by
+.B trofftable
+return data to the host computer using PostScript's
+.MW print
+operator.
+See
+.BR hardcopy (1)
+if you don't have access to the printer's serial port.
+.SH EXAMPLES
+Get the PostScript program that builds a width table for font
+.MR R :
+.EX
+trofftable -Tpost R >R.ps
+.EE
+If a font is not in the default set include the
+.B troff
+and PostScript font names:
+.EX
+trofftable -TLatin1 GL Garamond-Light >GL.ps
+.EE
+A font must be available on the printer when the table is built.
+Use
+.OP \-H
+or
+.OP \-C
+to include host resident fonts.
+.SH WARNINGS
+A width table will not build properly if the printer cannot access
+the PostScript font.
+.PP
+The
+.OP -TLatin1
+option only works on PostScript printers that support the full
+.SM ISO
+Latin-1 character set.
+The error message from older printers will likely indicate a missing
+.MW ISOLatin1Encoding
+array.
+.SH FILES
+.MW \*(dF/dev*/shell.lib
+.br
+.MW \*(dQ/dpost.ps
+.br
+.MW \*(dQ/trofftable.ps
+.SH SEE ALSO
+.BR dpost (1),
+.BR hardcopy (1),
+.BR postio (1),
+.BR troff (1),
+.BR buildtables (1),
+.BR font (5)
diff --git a/sys/src/cmd/postscript/trofftable/trofftable.mk b/sys/src/cmd/postscript/trofftable/trofftable.mk
new file mode 100755
index 000000000..d12b1fea5
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/trofftable.mk
@@ -0,0 +1,74 @@
+MAKE=/bin/make
+MAKEFILE=trofftable.mk
+
+SYSTEM=V9
+VERSION=3.3.2
+
+GROUP=bin
+OWNER=bin
+
+FONTDIR=/usr/lib/font
+POSTBIN=/usr/bin/postscript
+POSTLIB=/usr/lib/postscript
+MAN1DIR=/tmp
+
+all : trofftable
+
+install : all
+ @if [ ! -d $(POSTBIN) ]; then \
+ mkdir $(POSTBIN); \
+ chmod 755 $(POSTBIN); \
+ chgrp $(GROUP) $(POSTBIN); \
+ chown $(OWNER) $(POSTBIN); \
+ fi
+ @if [ ! -d "$(POSTLIB)" ]; then \
+ mkdir $(POSTLIB); \
+ chmod 755 $(POSTLIB); \
+ chgrp $(GROUP) $(POSTLIB); \
+ chown $(OWNER) $(POSTLIB); \
+ fi
+ cp trofftable $(POSTBIN)/trofftable
+ @chmod 755 $(POSTBIN)/trofftable
+ @chgrp $(GROUP) $(POSTBIN)/trofftable
+ @chown $(OWNER) $(POSTBIN)/trofftable
+ cp trofftable.ps $(POSTLIB)/trofftable.ps
+ @chmod 644 $(POSTLIB)/trofftable.ps
+ @chgrp $(GROUP) $(POSTLIB)/trofftable.ps
+ @chown $(OWNER) $(POSTLIB)/trofftable.ps
+ cp trofftable.1 $(MAN1DIR)/trofftable.1
+ @chmod 644 $(MAN1DIR)/trofftable.1
+ @chgrp $(GROUP) $(MAN1DIR)/trofftable.1
+ @chown $(OWNER) $(MAN1DIR)/trofftable.1
+
+clean :
+
+clobber : clean
+ rm -f trofftable
+
+trofftable : trofftable.sh
+ sed \
+ -e "s'^FONTDIR=.*'FONTDIR=$(FONTDIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ trofftable.sh >trofftable
+ @chmod 755 trofftable
+
+changes :
+ @trap "" 1 2 3 15; \
+ sed \
+ -e "s'^SYSTEM=.*'SYSTEM=$(SYSTEM)'" \
+ -e "s'^VERSION=.*'VERSION=$(VERSION)'" \
+ -e "s'^GROUP=.*'GROUP=$(GROUP)'" \
+ -e "s'^OWNER=.*'OWNER=$(OWNER)'" \
+ -e "s'^FONTDIR=.*'FONTDIR=$(FONTDIR)'" \
+ -e "s'^POSTBIN=.*'POSTBIN=$(POSTBIN)'" \
+ -e "s'^POSTLIB=.*'POSTLIB=$(POSTLIB)'" \
+ -e "s'^MAN1DIR=.*'MAN1DIR=$(MAN1DIR)'" \
+ $(MAKEFILE) >XXX.mk; \
+ mv XXX.mk $(MAKEFILE); \
+ sed \
+ -e "s'^.ds dF.*'.ds dF $(FONTDIR)'" \
+ -e "s'^.ds dQ.*'.ds dQ $(POSTLIB)'" \
+ trofftable.1 >XXX.1; \
+ mv XXX.1 trofftable.1
+
diff --git a/sys/src/cmd/postscript/trofftable/trofftable.ps b/sys/src/cmd/postscript/trofftable/trofftable.ps
new file mode 100755
index 000000000..dcc23200c
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/trofftable.ps
@@ -0,0 +1,189 @@
+%
+% Prologue for building troff width tables. The gsave/grestore pairs are
+% for hardcopy.
+%
+
+/slowdown 25 def
+/flagduplicates false def
+
+/ascenderheight -1 def
+/descenderdepth 0 def
+/octalescapes 256 def
+/startcomments 256 def
+/currentfontdict null def
+/scratchstring 512 string def
+
+/Print {
+ scratchstring cvs print flush
+ slowdown {1 pop} repeat
+} def
+
+/ReEncode { % vector fontname ReEncode -
+ dup
+ findfont dup length dict begin
+ {1 index /FID ne {def}{pop pop} ifelse} forall
+ /Encoding 3 -1 roll def
+ currentdict
+ end
+ definefont pop
+} bind def
+
+/SelectFont { % fontname SelectFont -
+ findfont
+ dup /PaintType get 0 eq {
+ /scaling 1 def
+ unitwidth resolution 72.0 div mul
+ }{
+ /scaling resolution 72 div def
+ unitwidth
+ } ifelse
+ scalefont
+ /currentfontdict exch def
+} def
+
+/ChangeMetrics {DpostPrologue begin addmetrics end} def
+
+/NamedInPrologue {
+ dup
+ DpostPrologue exch known {
+ DpostPrologue exch get type /nametype eq {
+ (named in prologue\n) Print
+ } if
+ }{pop} ifelse
+} def
+
+/SetAscender {
+ /str exch def
+
+ gsave
+ currentfontdict setfont
+ newpath
+ 0 0 moveto
+ str false charpath flattenpath pathbbox
+ /descenderdepth 4 -1 roll .5 mul def
+ exch pop exch pop
+
+ newpath
+ 0 0 moveto
+ str 0 1 getinterval false charpath flattenpath pathbbox
+ 4 1 roll pop pop pop
+ dup 3 1 roll sub .25 mul add
+ /ascenderheight exch def
+ grestore
+} def
+
+/GetAscender {
+ ascenderheight descenderdepth ge {
+ gsave
+ currentfontdict setfont
+ newpath
+ 0 0 moveto
+ ( ) dup 0 4 -1 roll put
+ false charpath flattenpath pathbbox
+ exch pop 3 -1 roll pop
+ ascenderheight gt {2}{0} ifelse
+ exch descenderdepth lt {1}{0} ifelse
+ or
+ grestore
+ }{0} ifelse
+} def
+
+/GetWidth {
+ gsave
+ currentfontdict setfont
+ ( ) dup 0 4 -1 roll put
+ stringwidth pop scaling mul round cvi
+ grestore
+} def
+
+/GetCode {
+ 256 3 1 roll % last unprintable match
+ 0 3 -1 roll {
+ 2 index eq {
+ dup 127 and 32 ge {exit} if
+ 3 -1 roll pop
+ dup 3 1 roll
+ } if
+ 1 add
+ } forall
+ exch pop
+ dup 255 gt {pop}{exch pop} ifelse
+} def
+
+/BuildFontCharset {
+ 0 2 charset length 2 sub {
+ /i exch def
+ /chcode -1 def
+ /chname null def
+ /key charset i get def
+ /val charset i 1 add get def
+
+ val type /integertype eq {
+ /chcode val def
+ /chname currentfontdict /Encoding get chcode get def
+ } if
+
+ val type /nametype eq {
+ /chname val def
+ /chcode currentfontdict /Encoding get chname GetCode def
+ } if
+
+ val type /stringtype eq {/chcode 0 def} if
+
+ chcode 0 lt chcode 255 gt or {
+ chcode 0 lt {(syntaxerror: )}{(undefinedname: )} ifelse
+ Print key Print (\t) Print val Print (\n) Print
+ quit
+ } if
+
+ val type /stringtype eq {
+ key Print
+ (\t) Print val Print
+ (\n) Print
+ }{
+ chcode octalescapes ge key (---) eq and {
+ (\\0) Print chcode 8 ( ) cvrs Print
+ }{key Print} ifelse
+ (\t) Print chcode GetWidth Print
+ (\t) Print chcode GetAscender Print
+ (\t) Print chcode Print
+ chcode startcomments ge {
+ (\t# ) Print chname Print
+ } if
+ (\n) Print
+ chcode octalescapes ge (---) key ne and {
+ key (\\0) anchorsearch not {
+ pop
+ (\\0) Print chcode 8 ( ) cvrs Print
+ (\t"\n) Print
+ }{pop pop} ifelse
+ } if
+ } ifelse
+ } for
+} def
+
+/BuildDescCharset {
+ /DescDict 512 dict def
+ /Characters 0 def
+
+ 0 1 charset length 1 sub {
+ /i exch def
+ /key charset i get def
+
+ key length 2 eq {
+ DescDict key cvn known {
+ flagduplicates { % for debugging
+ (<<<duplicated character: ) Print
+ key Print
+ (>>>\n) Print
+ } if
+ }{
+ DescDict key cvn 1 put
+ key Print
+ /Characters Characters 1 add def
+ Characters 20 mod 0 eq {(\n)}{( )} ifelse Print
+ } ifelse
+ } if
+ } for
+} def
+
diff --git a/sys/src/cmd/postscript/trofftable/trofftable.rc b/sys/src/cmd/postscript/trofftable/trofftable.rc
new file mode 100755
index 000000000..47c56b5bd
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/trofftable.rc
@@ -0,0 +1,123 @@
+#!/bin/rc
+# Writes a PostScript program on standard output that builds a width
+# table or typesetter description file. The program uses PostScript's
+# print procedure, which means the table comes back on the printer's
+# serial port. Try hardcopy if you don't have access to the port.
+#
+
+POSTLIB=/sys/lib/postscript/prologues
+FONTDIR=/sys/lib/troff/font
+
+PROLOGUE=$POSTLIB/trofftable.ps
+DPOSTPROLOGUE=$POSTLIB/dpost.ps
+
+COPYFILE=()
+HOSTFONTDIR=()
+DEVICE=()
+LIBRARY=()
+TEMPLATE=-R
+
+SLOWDOWN=25
+STARTCOMMENTS=256
+
+NONCONFORMING='%!PS'
+ENDPROLOG='%%EndProlog'
+BEGINSETUP='%%BeginSetup'
+ENDSETUP='%%EndSetup'
+TRAILER='%%Trailer'
+
+while (! ~ $#* 0 && ~ $1 -*) {
+ switch ($1) {
+ case -C; shift; COPYFILE=$COPYFILE' '$1
+ case -C*; COPYFILE=$COPYFILE' '`{echo $1 | sed s/-C//}
+
+ case -F; shift; FONTDIR=$1
+ case -F*; FONTDIR=`{echo $1 | sed s/-F//}
+
+ case -H; shift; HOSTFONTDIR=$1
+ case -H*; HOSTFONTDIR=`{echo $1 | sed s/-H//}
+
+ case -L; shift; PROLOGUE=$1
+ case -L*; PROLOGUE=`{echo $1 | sed s/-L//}
+
+ case -S; shift; LIBRARY=$1
+ case -S*; LIBRARY=`{echo $1 | sed s/-S//}
+
+ case -T; shift; DEVICE=$1
+ case -T*; DEVICE=`{echo $1 | sed s/-T//}
+
+ case -c; shift; STARTCOMMENTS=$1
+ case -c*; STARTCOMMENTS=`{echo $1 | sed s/-c//}
+
+ case -o; shift; OCTALESCAPES=$1 # only for Latin1 tables
+ case -o*; OCTALESCAPES=`{echo $1 | sed s/-o//}
+
+ case -s; shift; SLOWDOWN=$1
+ case -s*; SLOWDOWN=`{echo $1 | sed s/-s//}
+
+ case -t; shift; TEMPLATE=$1
+ case -t*; TEMPLATE=`{echo $1 | sed s/-t//}
+
+ case -*; echo $0: illegal option $1 >[1=2]; exit 1
+ }
+ shift
+}
+
+if (! ~ $#DEVICE 0 && ! ~ $#LIBRARY 0) {
+ echo $0: no device or shell library >[1=2]
+ exit 1
+}
+
+if (! ~ $#* 1 2) {
+ echo $0: bad argument count >[1=2]
+ exit 1
+}
+
+if (test -d $HOSTFONTDIR -a -f $HOSTFONTDIR/$1) {
+ COPYFILE=$COPYFILE' '$HOSTFONTDIR/$1
+}
+
+#
+# Include the shell library and get the command used to build the table.
+# Make awk call a separate library function??
+#
+if (~ $#LIBRARY 0 || ~ $LIBRARY '') LIBRARY=$FONTDIR/dev$DEVICE/shell.lib
+
+. $LIBRARY
+
+if (~ $#* 1) TEMPLATE=$1
+
+CMD=`{BuiltinTables | awk '$2 == template"" {
+ if ( pname == "" )
+ pname = $3
+ printf "%s %s %s", $1, tname, pname
+ exit 0
+}' template=$TEMPLATE tname=$1 pname=$2}
+
+if (! $CMD) {
+ echo $0: $TEMPLATE not found >[1=2]
+ exit 1
+}
+
+#
+# Build the PostScript font table program.
+#
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo /DpostPrologue 100 dict dup begin
+cat $DPOSTPROLOGUE
+echo end def
+echo $ENDPROLOG
+
+echo $BEGINSETUP
+if (~ $COPYFILE '') COPYFILE=/dev/null
+cat $COPYFILE
+echo /slowdown $SLOWDOWN def
+echo /startcomments $STARTCOMMENTS def
+echo $ENDSETUP
+
+$CMD
+
+echo $TRAILER
+
diff --git a/sys/src/cmd/postscript/trofftable/trofftable.sh b/sys/src/cmd/postscript/trofftable/trofftable.sh
new file mode 100755
index 000000000..528bb8f84
--- /dev/null
+++ b/sys/src/cmd/postscript/trofftable/trofftable.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+#
+# Writes a PostScript program on standard output that builds a width
+# table or typesetter description file. The program uses PostScript's
+# print procedure, which means the table comes back on the printer's
+# serial port. Try hardcopy if you don't have access to the port.
+#
+
+POSTBIN=/usr/lbin/postscript
+POSTLIB=/usr/lib/postscript
+FONTDIR=/usr/lib/font
+
+PROLOGUE=$POSTLIB/trofftable.ps
+DPOSTPROLOGUE=$POSTLIB/dpost.ps
+
+COPYFILE=
+HOSTFONTDIR=
+DEVICE=
+LIBRARY=
+TEMPLATE=
+
+SLOWDOWN=25
+STARTCOMMENTS=256
+
+NONCONFORMING="%!PS"
+ENDPROLOG="%%EndProlog"
+BEGINSETUP="%%BeginSetup"
+ENDSETUP="%%EndSetup"
+TRAILER="%%Trailer"
+
+while [ -n "$1" ]; do
+ case $1 in
+ -C) shift; COPYFILE="$COPYFILE $1";;
+ -C*) COPYFILE="$COPYFILE `echo $1 | sed s/-C//`";;
+
+ -F) shift; FONTDIR=$1;;
+ -F*) FONTDIR=`echo $1 | sed s/-F//`;;
+
+ -H) shift; HOSTFONTDIR=$1;;
+ -H*) HOSTFONTDIR=`echo $1 | sed s/-H//`;;
+
+ -L) shift; PROLOGUE=$1;;
+ -L*) PROLOGUE=`echo $1 | sed s/-L//`;;
+
+ -S) shift; LIBRARY=$1;;
+ -S*) LIBRARY=`echo $1 | sed s/-S//`;;
+
+ -T) shift; DEVICE=$1;;
+ -T*) DEVICE=`echo $1 | sed s/-T//`;;
+
+ -c) shift; STARTCOMMENTS=$1;;
+ -c*) STARTCOMMENTS=`echo $1 | sed s/-c//`;;
+
+ -o) shift; OCTALESCAPES=$1;; # only for Latin1 tables
+ -o*) OCTALESCAPES=`echo $1 | sed s/-o//`;;
+
+ -s) shift; SLOWDOWN=$1;;
+ -s*) SLOWDOWN=`echo $1 | sed s/-s//`;;
+
+ -t) shift; TEMPLATE=$1;;
+ -t*) TEMPLATE=`echo $1 | sed s/-t//`;;
+
+ -*) echo "$0: illegal option $1" >&2; exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if [ ! "$DEVICE" -a ! "$LIBRARY" ]; then
+ echo "$0: no device or shell library" >&2
+ exit 1
+fi
+
+if [ $# -ne 1 -a $# -ne 2 ]; then
+ echo "$0: bad argument count" >&2
+ exit 1
+fi
+
+if [ -d "$HOSTFONTDIR" -a -f "$HOSTFONTDIR/$1" ]; then
+ COPYFILE="$COPYFILE $HOSTFONTDIR/$1"
+fi
+
+#
+# Include the shell library and get the command used to build the table.
+# Make awk call a separate library function??
+#
+
+. ${LIBRARY:-${FONTDIR}/dev${DEVICE}/shell.lib}
+
+if [ $# -eq 1 ]
+ then TEMPLATE=$1
+ else TEMPLATE=${TEMPLATE:-R}
+fi
+
+CMD=`BuiltinTables | awk '$2 == template"" {
+ if ( pname == "" )
+ pname = $3
+ printf "%s %s %s", $1, tname, pname
+ exit 0
+}' template="$TEMPLATE" tname="$1" pname="$2"`
+
+if [ ! "$CMD" ]; then
+ echo "$0: $TEMPLATE not found" >&2
+ exit 1
+fi
+
+#
+# Build the PostScript font table program.
+#
+
+echo $NONCONFORMING
+cat $PROLOGUE
+echo "/DpostPrologue 100 dict dup begin"
+cat $DPOSTPROLOGUE
+echo "end def"
+echo $ENDPROLOG
+
+echo $BEGINSETUP
+cat ${COPYFILE:-/dev/null}
+echo "/slowdown $SLOWDOWN def"
+echo "/startcomments $STARTCOMMENTS def"
+echo $ENDSETUP
+
+$CMD
+
+echo $TRAILER
+