tmore files - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 61f5c35c9465f0702739b41249a664d409f0482c
 (DIR) parent 173302913ebce353eadcbb12d71c3759cbe79e34
 (HTM) Author: rsc <devnull@localhost>
       Date:   Sat, 15 May 2004 23:55:53 +0000
       
       more files
       
       Diffstat:
         M src/cmd/mkfile                      |       2 +-
         A src/cmd/postscript/common/bbox.c    |     257 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/comments… |     127 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/common.c  |     264 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/common.h  |      43 ++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/ext.h     |      40 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/gen.h     |      65 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/getopt.c  |      56 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/glob.c    |      29 +++++++++++++++++++++++++++++
         A src/cmd/postscript/common/misc.c    |     230 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/mkfile    |      23 +++++++++++++++++++++++
         A src/cmd/postscript/common/path.h    |      32 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/request.c |     119 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/request.h |      22 ++++++++++++++++++++++
         A src/cmd/postscript/common/rune.c    |     142 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/common/rune.h    |      19 +++++++++++++++++++
         A src/cmd/postscript/common/tempnam.c |      27 +++++++++++++++++++++++++++
         A src/cmd/postscript/download/README  |      11 +++++++++++
         A src/cmd/postscript/download/downlo… |     545 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/download/downlo… |      14 ++++++++++++++
         A src/cmd/postscript/download/mkfile  |      25 +++++++++++++++++++++++++
         A src/cmd/postscript/mkfile           |      25 +++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/Bgetfie… |     156 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/chartab… |     458 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/conv.c   |     100 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/devcntl… |     178 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/draw.c   |     342 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/mkfile   |      36 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/picture… |     295 ++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/ps_incl… |     191 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/ps_incl… |      66 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/readDES… |     139 ++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/shell.l… |    1238 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/tr2post… |     218 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/tr2post… |     103 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/utfmap   |      47 +++++++++++++++++++++++++++++++
         A src/cmd/postscript/tr2post/utils.c  |     264 +++++++++++++++++++++++++++++++
       
       37 files changed, 5947 insertions(+), 1 deletion(-)
       ---
 (DIR) diff --git a/src/cmd/mkfile b/src/cmd/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/common/bbox.c b/src/cmd/postscript/common/bbox.c
       t@@ -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 */
       +
       +/*****************************************************************************/
       +
 (DIR) diff --git a/src/cmd/postscript/common/comments.h b/src/cmd/postscript/common/comments.h
       t@@ -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:"
       +
 (DIR) diff --git a/src/cmd/postscript/common/common.c b/src/cmd/postscript/common/common.c
       t@@ -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");
       +}
 (DIR) diff --git a/src/cmd/postscript/common/common.h b/src/cmd/postscript/common/common.h
       t@@ -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 *);
 (DIR) diff --git a/src/cmd/postscript/common/ext.h b/src/cmd/postscript/common/ext.h
       t@@ -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();
       + */
 (DIR) diff --git a/src/cmd/postscript/common/gen.h b/src/cmd/postscript/common/gen.h
       t@@ -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))
       +
 (DIR) diff --git a/src/cmd/postscript/common/getopt.c b/src/cmd/postscript/common/getopt.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/postscript/common/glob.c b/src/cmd/postscript/common/glob.c
       t@@ -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 */
       +
 (DIR) diff --git a/src/cmd/postscript/common/misc.c b/src/cmd/postscript/common/misc.c
       t@@ -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 */
       +
       +/*****************************************************************************/
       +
 (DIR) diff --git a/src/cmd/postscript/common/mkfile b/src/cmd/postscript/common/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/common/path.h b/src/cmd/postscript/common/path.h
       t@@ -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"
       +
 (DIR) diff --git a/src/cmd/postscript/common/request.c b/src/cmd/postscript/common/request.c
       t@@ -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 */
       +
       +/*****************************************************************************/
       +
 (DIR) diff --git a/src/cmd/postscript/common/request.h b/src/cmd/postscript/common/request.h
       t@@ -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;
       +
 (DIR) diff --git a/src/cmd/postscript/common/rune.c b/src/cmd/postscript/common/rune.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/postscript/common/rune.h b/src/cmd/postscript/common/rune.h
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/common/tempnam.c b/src/cmd/postscript/common/tempnam.c
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/download/README b/src/cmd/postscript/download/README
       t@@ -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).
       +
 (DIR) diff --git a/src/cmd/postscript/download/download.c b/src/cmd/postscript/download/download.c
       t@@ -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 */
       +
       +/*****************************************************************************/
       +
 (DIR) diff --git a/src/cmd/postscript/download/download.h b/src/cmd/postscript/download/download.h
       t@@ -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();
       +
 (DIR) diff --git a/src/cmd/postscript/download/mkfile b/src/cmd/postscript/download/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/mkfile b/src/cmd/postscript/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/tr2post/Bgetfield.c b/src/cmd/postscript/tr2post/Bgetfield.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/chartab.c b/src/cmd/postscript/tr2post/chartab.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/conv.c b/src/cmd/postscript/tr2post/conv.c
       t@@ -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");
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/devcntl.c b/src/cmd/postscript/tr2post/devcntl.c
       t@@ -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++;
       +}
       +
 (DIR) diff --git a/src/cmd/postscript/tr2post/draw.c b/src/cmd/postscript/tr2post/draw.c
       t@@ -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");
       +
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/mkfile b/src/cmd/postscript/tr2post/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/tr2post/pictures.c b/src/cmd/postscript/tr2post/pictures.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/ps_include.c b/src/cmd/postscript/tr2post/ps_include.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/ps_include.h b/src/cmd/postscript/tr2post/ps_include.h
       t@@ -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
       +};
 (DIR) diff --git a/src/cmd/postscript/tr2post/readDESC.c b/src/cmd/postscript/tr2post/readDESC.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/shell.lib b/src/cmd/postscript/tr2post/shell.lib
       t@@ -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"
       +}
       +
 (DIR) diff --git a/src/cmd/postscript/tr2post/tr2post.c b/src/cmd/postscript/tr2post/tr2post.c
       t@@ -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("");
       +}
 (DIR) diff --git a/src/cmd/postscript/tr2post/tr2post.h b/src/cmd/postscript/tr2post/tr2post.h
       t@@ -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);
 (DIR) diff --git a/src/cmd/postscript/tr2post/utfmap b/src/cmd/postscript/tr2post/utfmap
       t@@ -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
 (DIR) diff --git a/src/cmd/postscript/tr2post/utils.c b/src/cmd/postscript/tr2post/utils.c
       t@@ -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);
       +}