Commit Diff


commit - 173302913ebce353eadcbb12d71c3759cbe79e34
commit + 61f5c35c9465f0702739b41249a664d409f0482c
blob - d08461e90c24753cd296545260dc03f5b43febbd
blob + c1c2f917d0dff650805deada8f9ccac2d6aeb9c9
--- src/cmd/mkfile
+++ src/cmd/mkfile
@@ -5,7 +5,7 @@ SHORTLIB=sec fs mux regexp9 thread bio 9
 
 <$PLAN9/src/mkmany
 
-BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|lex|vncv|grap|eqn|troff|pic|tbl'
+BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|lex|vncv|grap|eqn|troff|postscript|pic|tbl'
 DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"`
 
 <$PLAN9/src/mkdirs
blob - /dev/null
blob + 7e1f14a50ab20af7d4b1df307cd1a1cdf5781496 (mode 644)
--- /dev/null
+++ 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 */
+
+/*****************************************************************************/
+
blob - /dev/null
blob + 6b409cad163e520b4c607d971b45aa2d7cbc10ee (mode 644)
--- /dev/null
+++ 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:"
+
blob - /dev/null
blob + 945ef6a059eeafa356909958df59f85c955698c9 (mode 644)
--- /dev/null
+++ 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;
+	Biobuf *Bfile;
+	int n;
+	static char buf[Bsize];
+
+	if ((bfile = Bopen(unsharp(filename), OREAD)) == 0) {
+		return(1);
+	}
+	Bfile = bfile;
+	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");
+}
blob - /dev/null
blob + 62eba08e94b5248cddf4e5204e3af737a8e7a7ca (mode 644)
--- /dev/null
+++ 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 Biobuf *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(Biobuf *, int, void *, int);
+void *galloc(void *, int, char *);
+void pagelist(char *);
blob - /dev/null
blob + e260cee1b2d0ebf5ae21052436a8f685f9a051fb (mode 644)
--- /dev/null
+++ 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();
+ */
blob - /dev/null
blob + ba8cbbac3b447a9739c81e431065721d75dcc0e5 (mode 644)
--- /dev/null
+++ 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))
+
blob - /dev/null
blob + cf6619f72f2b87b9e9c0a8d076d4a2c5c290c10f (mode 644)
--- /dev/null
+++ 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], "--") == NULL) {
+			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;
+}
blob - /dev/null
blob + 2826f4e5fe9f21d7e5e545c93d7c0b20892bdf64 (mode 644)
--- /dev/null
+++ 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 */
+
blob - /dev/null
blob + 25bd37aac2ffc8fe776d4b5f6afd18f007e46298 (mode 644)
--- /dev/null
+++ 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 */
+
+/*****************************************************************************/
+
blob - /dev/null
blob + 09d430358986aefb3d709293810392cf8d897af1 (mode 644)
--- /dev/null
+++ src/cmd/postscript/common/mkfile
@@ -0,0 +1,23 @@
+<$PLAN9/src/mkhdr
+
+<../config
+
+LIB=com.a
+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\
+
+<$PLAN9/src/mklib
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE
blob - /dev/null
blob + a369213940bc48d25d7a9d29c560f3301cc498eb (mode 644)
--- /dev/null
+++ src/cmd/postscript/common/path.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * pathname definitions for important files and directories.
+ *
+ */
+
+#define DPOST		"#9/sys/lib/postscript/prologues/dpost.ps"
+#define POSTBGI		"#9/sys/lib/postscript/prologues/postbgi.ps"
+#define POSTDAISY	"#9/sys/lib/postscript/prologues/postdaisy.ps"
+#define POSTDMD		"#9/sys/lib/postscript/prologues/postdmd.ps"
+#define POSTMD		"#9/sys/lib/postscript/prologues/postmd.ps"
+#define POSTPLOT	"#9/sys/lib/postscript/prologues/postplot.ps"
+#define POSTPRINT	"#9/sys/lib/postscript/prologues/postprint.ps"
+#define POSTNPRINT	"#9/sys/lib/postscript/prologues/postnprint.ps"
+#define POSTTEK		"#9/sys/lib/postscript/prologues/posttek.ps"
+#define POSTGIF		"#9/sys/lib/postscript/prologues/postgif.ps"
+
+#define BASELINE	"#9/sys/lib/postscript/prologues/baseline.ps"
+#define COLOR		"#9/sys/lib/postscript/prologues/color.ps"
+#define DRAW		"#9/sys/lib/postscript/prologues/draw.ps"
+#define FORMFILE	"#9/sys/lib/postscript/prologues/forms.ps"
+#define SHADEFILE	"#9/sys/lib/postscript/prologues/shade.ps"
+#define KERNING		"#9/sys/lib/postscript/prologues/kerning.ps"
+#define REQUESTFILE	"#9/sys/lib/postscript/prologues/ps.requests"
+#define ROUNDPAGE	"#9/sys/lib/postscript/prologues/roundpage.ps"
+
+#define ENCODINGDIR	"#9/sys/lib/postscript/prologues"
+#define HOSTDIR		"#9/sys/lib/postscript/font"
+#define FONTDIR		"#9/sys/lib/troff/font"
+#define POSTLIBDIR	"#9/sys/lib/postscript/prologues"
+#define TEMPDIR		"/tmp"
+
blob - /dev/null
blob + d8d7dd130bf936cd2035f93aef68b59744934031 (mode 644)
--- /dev/null
+++ 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 */
+
+/*****************************************************************************/
+
blob - /dev/null
blob + 25d94d01a1a26c8acab131e700a41ee6c79b5c08 (mode 644)
--- /dev/null
+++ 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;
+
blob - /dev/null
blob + 01ee6ba812f1aa305c2bb4a1f39baedce3f30af9 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 2df5f767ef23dd466f90a55cac70b8197511b569 (mode 644)
--- /dev/null
+++ 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	= 0x80,		/* decoding error in utf */
+};
+#endif
blob - /dev/null
blob + 529025ed60c346a3b1453eb5784ce14d5fcba8c3 (mode 644)
--- /dev/null
+++ src/cmd/postscript/common/tempnam.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <errno.h>
+
+#if defined(V9) || defined(BSD4_2) || defined(plan9)
+char *tempnam(char *dir, char *pfx) {
+	int pid;
+	unsigned int len;
+	char *tnm, *malloc();
+	static int seq = 0;
+
+	pid = getpid();
+	len = strlen(dir) + strlen(pfx) + 10;
+	if ((tnm = malloc(len)) != NULL) {
+		sprintf(tnm, "%s", dir);
+		if (access(tnm, 7) == -1)
+			return(NULL);
+		do {
+			sprintf(tnm, "%s/%s%d%d", dir, pfx, pid, seq++);
+			errno = 0;
+			if (access(tnm, 7) == -1)
+				if (errno == ENOENT)
+					return(tnm);
+		} while (1);
+	}
+	return(tnm);
+}
+#endif
blob - /dev/null
blob + 9bfce12a83217b35f9d76bb6ba1e047ed634811b (mode 644)
--- /dev/null
+++ 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).
+
blob - /dev/null
blob + ab6bdd52984424ff6d0e17ee0ab5e251cf842f53 (mode 644)
--- /dev/null
+++ src/cmd/postscript/download/download.c
@@ -0,0 +1,545 @@
+/*
+ *
+ * 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;				/* 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.
+ *
+ */
+
+    fp_in = 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 */
+    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(unsharp(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(unsharp(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(unsharp(*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(unsharp(path));
+		free(path);
+	    } else cat(unsharp(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 */
+
+/*****************************************************************************/
+
blob - /dev/null
blob + f88cc57ad9be1cbeb5a5e49e9149625c05765934 (mode 644)
--- /dev/null
+++ 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();
+
blob - /dev/null
blob + d2321e65bf47348b5cc91f43ebcfa7b3483cb694 (mode 644)
--- /dev/null
+++ src/cmd/postscript/download/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+<../config
+TARG=psdownload
+
+OFILES=download.$O
+
+COMMONDIR=../common
+
+HFILES=download.h\
+	$COMMONDIR/comments.h\
+	$COMMONDIR/gen.h\
+	$COMMONDIR/path.h\
+	$COMMONDIR/ext.h\
+
+LIB=$COMMONDIR/com.a
+BIN=$POSTBIN
+
+<$PLAN9/src/mkone
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR
+
+$LIB:
+	cd $COMMONDIR
+	mk install
+	mk clean
blob - /dev/null
blob + 158be819dcdb8c43c7dce186aa844fadf6fa039a (mode 644)
--- /dev/null
+++ src/cmd/postscript/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+<config
+
+DIRS=\
+	common\
+	tr2post\
+	download\
+#	cropmarks\
+#	grabit\
+#	hardcopy\
+#	mpictures\
+#	postgif\
+#	postprint\
+#	postreverse\
+#	posttek\
+#	printfont\
+#	psencoding\
+#	psfiles\
+#	g3p9bit\
+#	p9bitpost\
+#	tcpostio\
+#	text2post\
+
+<$PLAN9/src/mkdirs
blob - /dev/null
blob + 8922139d15696ae011f5a352d42a4be2252a8b07 (mode 644)
--- /dev/null
+++ src/cmd/postscript/tr2post/Bgetfield.c
@@ -0,0 +1,156 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+#undef isspace
+#define isspace bisspace
+
+int
+isspace(Rune r)
+{
+	return(r==' ' || r=='\t' || r=='\n' || r == '\r' || r=='\f');
+}
+
+int
+Bskipws(Biobuf *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(Biobuf *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);
+}
blob - /dev/null
blob + 87ab556baf9abcf37802a46d91d9532ddb094168 (mode 644)
--- /dev/null
+++ src/cmd/postscript/tr2post/chartab.c
@@ -0,0 +1,458 @@
+/*    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[] = "#9/sys/lib/postscript/troff";		/* "/sys/lib/postscript/troff/"; */
+char troffmetricdirname[] = "#9/sys/lib/troff/font";	/* "/sys/lib/troff/font/devutf/"; */
+
+int
+readpsfontdesc(char *fontname, int trindex) {
+	static char *filename = 0;
+	Biobuf *bfd;
+	Biobuf *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;
+
+	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(unsharp(filename), OREAD);
+	if (bfd == 0) {
+		error(WARNING, "cannot open file %s\n", filename);
+		return(0);
+	}
+	Bfd = bfd;
+
+	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;
+	Biobuf *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(unsharp(filename), OREAD);
+	if (bfd == 0) {
+		error(WARNING, "cannot open file %s\n", filename);
+		return(0);
+	}
+	Bfd = bfd;
+	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;
+}
blob - /dev/null
blob + 4b7d97f6415327aa420db54e1d01750ae1bf1142 (mode 644)
--- /dev/null
+++ 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(Biobuf *Bp) {
+	long c, n;
+	int r;
+	char special[10];
+	int save;
+
+	inputlineno = 1;
+	if (debug) Bprint(Bstderr, "conv(Biobuf *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");
+}
blob - /dev/null
blob + 8b438f76308ca40129439aae18210de4da6b91fb (mode 644)
--- /dev/null
+++ 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(Biobuf *);
+extern void notavail(char *);
+
+void
+PSInclude(Biobuf *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(Biobuf *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++;
+}
+
blob - /dev/null
blob + 575ec884f3c00cd0856ae0b92ae45f13a758b5d0 (mode 644)
--- /dev/null
+++ 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(Biobuf *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(Biobuf *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");
+
+}
blob - /dev/null
blob + e1e5aec2564f6a878126687320c6deb72c2fa12d (mode 644)
--- /dev/null
+++ src/cmd/postscript/tr2post/mkfile
@@ -0,0 +1,36 @@
+<$PLAN9/src/mkhdr
+
+<../config
+
+COMMONDIR=../common
+
+SHORTLIB=bio 9
+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
+
+<$PLAN9/src/mkone
+
+CFLAGS=$CFLAGS -c -D'PROGRAMVERSION="0.1"' -D'DOROUND=1' -I$COMMONDIR
+
+%.$O:	$COMMONDIR/%.c
+	$CC $CFLAGS $COMMONDIR/$stem.c
blob - /dev/null
blob + a20b7ade9e4cb86c60d99f961eeffa3e916640ff (mode 644)
--- /dev/null
+++ 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;
+Biobuf	*Bfp_pic;
+Biobuf	*picopen(char *);
+
+#define MAXGETFIELDS	16
+char *fields[MAXGETFIELDS];
+int nfields;
+
+extern int	devres, hpos, vpos;
+extern int	picflag;
+
+/*****************************************************************************/
+
+void
+picture(Biobuf *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 */
+	Biobuf	*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.
+ *
+ */
+Biobuf *
+picopen(char *path) {
+/*	char	name[100];	/* pathnames */
+/*	long	pos;			/* current position */
+/*	long	total;			/* and sizes - from *fp_pic */
+	Biobuf *bfp;
+	Biobuf	*Bfp;		/* and pointer for the new temp file */
+
+
+	if ((bfp = Bopen(path, OREAD)) == 0)
+		error(FATAL, "can't open %s\n", path);
+	Bfp = bfp;
+	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;
+				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;
+	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(Biobuf *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(Biobuf *Bfp_in, Biobuf *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);
+}
blob - /dev/null
blob + af30ff212593fc337b7e90f7815f71d6ea1a4670 (mode 644)
--- /dev/null
+++ 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 void
+copy(Biobuf *fin, Biobuf *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(Biobuf *fin, Biobuf *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;
+}
blob - /dev/null
blob + cef490508d6801957cede80090f51605a46da5f4 (mode 644)
--- /dev/null
+++ 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
+};
blob - /dev/null
blob + 03b8b64db618cd81980ff92afb72581900315139 (mode 644)
--- /dev/null
+++ 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;
+	Biobuf *Bfd;
+	int i, state = -1;
+	int fontindex = 0;
+
+	if (debug) Bprint(Bstderr, "readDESC()\n");
+	descfilename = galloc(descfilename, strlen(descnameformat)+strlen(FONTDIR)
+		+strlen(devname)+1, "readdesc");
+	sprint(descfilename, descnameformat, FONTDIR, devname);
+	if ((bfd = Bopen(unsharp(descfilename), OREAD)) == 0) {
+		error(WARNING, "cannot open file %s\n", descfilename);
+		return(0);
+	}
+	Bfd = bfd;
+
+	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);
+}
blob - /dev/null
blob + d96e656555dccfc516350e3e60c84a93622db718 (mode 644)
--- /dev/null
+++ 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"
+}
+
blob - /dev/null
blob + d60e0c7aeed3b0414ee9338a48cea821fded8787 (mode 644)
--- /dev/null
+++ src/cmd/postscript/tr2post/tr2post.c
@@ -0,0 +1,218 @@
+#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;
+Biobuf *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", DOCUMENTFONTS, ATEND);
+	Bprint(Bstdout, "%s %s\n", PAGES, ATEND);
+	Bprint(Bstdout, "%s", ENDCOMMENTS);
+
+	if (cat(unsharp(DPOST))) {
+		Bprint(Bstderr, "can't read %s\n", DPOST);
+		exits("dpost prologue");
+	}
+
+	if (drawflag) {
+		if (cat(unsharp(DRAW))) {
+			Bprint(Bstderr, "can't read %s\n", DRAW);
+			exits("draw prologue");
+		}
+	}
+
+	if (DOROUND)
+		cat(unsharp(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(unsharp(ENCODINGDIR"/Latin1.enc"));
+	if (passthrough != 0) Bprint(Bstdout, "%s\n", passthrough);
+
+	Bprint(Bstdout, "setup\n");
+	if (formsperpage > 1) {
+		cat(unsharp(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(unsharp(charlibname)))
+		Bprint(Bstderr, "cannot open %s\n", charlibname);
+	}
+
+	Bprint(Bstdout, "%s", ENDSETUP);
+}
+
+void
+cleanup(void) {
+	remove(tmpfilename);
+}
+
+main(int argc, char *argv[]) {
+	Biobuf *binp;
+	Biobuf *Binp;
+	int i, tot, ifd;
+	char *t;
+
+	programname = argv[0];
+	if (Binit(&bstderr, 2, OWRITE) == Beof) {
+		exits("Binit");
+	}
+	Bstderr = &bstderr;
+
+	tmpnam(tmpfilename);
+	if ((bstdout=Bopen(tmpfilename, OWRITE)) == 0) {
+		Bprint(Bstderr, "cannot open temporary file %s\n", tmpfilename);
+		exits("Bopen");
+	}
+	atexit(cleanup);
+	Bstdout = bstdout;
+	
+	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))) < (Biobuf *)0) {
+			Bprint(Bstderr, "malloc failed.\n");
+			exits("malloc");
+		}
+		if (Binit(binp, 0, OREAD) == Beof) {
+			Bprint(Bstderr, "Binit of <stdin> failed.\n");
+			exits("Binit");
+		}
+		Binp = binp;
+		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;
+		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;
+	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("");
+}
blob - /dev/null
blob + b07e0b131391693ce8f72d8f037869142f1c515b (mode 644)
--- /dev/null
+++ src/cmd/postscript/tr2post/tr2post.h
@@ -0,0 +1,103 @@
+#define MAXSPECHARS 	512
+#define MAXTOKENSIZE	128
+#define CHARLIB	"#9/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(Biobuf *);
+void hgoto(int);
+void vgoto(int);
+void hmot(int);
+void vmot(int);
+void draw(Biobuf *);
+void devcntl(Biobuf *);
+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(Biobuf *, Biobuf *, int, int,
+	int, int, double, double, double, double,
+	double, double, double);
+void picture(Biobuf *, char *);
+void beginpath(char*, int);
+void drawpath(char*, int);
blob - /dev/null
blob + 2f9d6dc3759446d058392fd7c97e6d7963cef7cf (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + 8f58ea45940edb6ff5abff9219d6cd574657f411 (mode 644)
--- /dev/null
+++ 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(Biobuf *bp) {
+}
+
+long
+nametorune(char *name) {
+	return(0);
+}
+
+void
+notavail(char *msg) {
+	Bprint(Bstderr, "%s is not available at this time.\n", msg);
+}