/* Amiga bitmapped font builder.  File "mkbmap.c".
 * (C) Adrian Aylward 1991
 *
 * You may freely copy, use, and modify this file.
 *
 * This program prints builds an Amiga format bitmapped screen font file
 * from the corresponding PostScript font, using post.library. It is totally
 * Amiga specific.
 *
 * The program was tested using Lattice C V5.05.  It has various Lattice
 * dependencies.
 *
 * This is version 1.0.
 */

# include <dos.h>
# include <exec/exec.h>
# include <libraries/diskfont.h>
# include <proto/diskfont.h>
# include <proto/dos.h>
# include <proto/exec.h>
# include <string.h>
# include <stdarg.h>
# include <stdio.h>

# include "postlib.h"

# undef  POSTVERNO
# define POSTVERNO 15  /* We need post.library version 1.5+ */

/* The following definitions appear to be missing from the Lattice headers */

# ifdef LATTICE
extern struct FontContentsHeader *NewFontContents(BPTR lock, char *name);
extern void DisposeFontContents(struct FontContentsHeader *fc);

# pragma libcall DiskfontBase NewFontContents     2a   9802 ; d0 = (a0, a1)
# pragma libcall DiskfontBase DisposeFontContents 30    901 ;      (a1)
# endif

/* Assembler routines */

extern void insertftrap(void);
extern void deleteftrap(void);

/* Object module hunks */

# define Hunk_Header  0x3F3
# define Hunk_Code    0x3E9
# define Hunk_Reloc32 0x3EC
# define Hunk_End     0x3F2

# define MOVFFD0RTS   0x70FF4E75

/* External data (initialised to zero) */

char *argbmapfile, *argfontname, *argpointsizes;
char *argstartupfile, *argencodingfile;
int optfontencoding, optnew;
int optmonospaced, optbold, optitalic, opttrace;
int optden, optxden, optyden, optbaseline, optwidth;
int pointc, pointn, pointv[100];

int retcode;

int arec;

struct library *PSbase;
struct PSparm parm;

APTR functab[10];

int ftrapset;

int pointsize;
char namebuf[100 + 5];
char psstring[200];
int pserror;

int lochar, hichar;
int baseline, nomwidth, nomcount;

int llx[256], lly[256], urx[256], ury[256];

int cposx, cposy;

void *fontmem;
int baselen;
int maxfontlen, maxcharlen, maxmodulo, maxwidth; 
int actfontlen, actcharlen, actmodulo, actwidth; 
int hunklen, hunknum, *hunkptr;

BPTR fdlock, cdlock, newfh;

struct FontContentsHeader *fcheader;

struct DiskFontHeader *dfheader;

char *chardata;
short *charloc, *charspace, *charkern;

/* Routines */

extern int strtoint(char **sp, int *ip);
extern char *buildfnumname(char *nbuf, char *name, int num);
extern char *buildfdirname(char *nbuf, char *name, int type);
extern void pstrace(char *format, ...);
extern void pssintf(char *format, ...);
extern void __saveds setbbox(int ch, int lx, int ly, int rx, int uy);
extern void __saveds setcpos(int cpx, int cpy);

/* Main program */

void main(int argc, char **argv)
{   char *s, *t;
    int *ip, i, l, ch;
    int y1, y2, z1, z2, zz, ww;

    /* Parse arguments.  No workbench startup */

    optxden = optyden = 75;
    optbaseline = optwidth = -1;
    lochar = 32;
    hichar = 255;

    if (argc == 0) goto tidyexit;
    argv++;
    argc--;
    if (argc == 0 || (argc == 1 && strcmp(*argv, "?") == 0)) goto query;

    while (argc)
    {   s = *argv;
        if (*s != '-') break;
        argv++;
        argc--;
        if (strcmp(s, "--") == 0) break;
        s++;
        for (;;)
        {   ch = *s++;
            if (ch == 0) break;
            switch (ch)
            {   case 'S': case 's':
                    if (argc == 0) goto badargs;
                    argstartupfile = *argv++;
                    argc--;

                case 'E': case 'e':
                    if (argc == 0) goto badargs;
                    argencodingfile = *argv++;
                    argc--;

                case 'F': case 'f':
                    optfontencoding = 1;
                    continue;

                case 'N': case 'n':
                    optnew = 1;
                    continue;

                case 'D': case 'd':
                    ip = &optden;
                    break;

                case 'X': case 'x':
                    ip = &optxden;
                    break;

                case 'Y': case 'y':
                    ip = &optyden;
                    break;

                case 'Z': case 'z':
                    ip = &optbaseline;
                    break;

                case 'L': case 'l':
                    ip = &lochar;
                    break;

                case 'W': case 'w':
                    ip = &optwidth;
                    break;

                case 'H': case 'h':
                    ip = &hichar;
                    break;

                case 'M': case 'm':
                    optmonospaced = 1;
                    continue;

                case 'B': case 'b':
                    optbold = 1;
                    continue;

                case 'I': case 'i':
                    optitalic = 1;
                    continue;

                case 'T': case 't':
                    opttrace = 1;
                    continue;

                default:
                    fprintf(stderr, "mkbmap: unknown option \"-%c\"", ch);
                    goto badusage;
            }
            if (!strtoint(&s, ip)) goto badargs;
            if (ip == &optden) optxden = optyden = optden;
        }

        if (*s == '-' && *(s + 1) == 0) break;
    }

    if (lochar > hichar || hichar > 255)
    {   fprintf(stderr, "mkbmap: LoChar/HiChar out of range "
                                "(0 <= Lo <= Hi <= 255)\n");
        goto errorexit;
    }

    if (argc != 1 && argc != 3) goto badargs;
    argbmapfile = argv[0];
    if (argc == 3)
    {   argfontname = argv[1];
        argpointsizes = argv[2];

        s = argpointsizes;
        while (*s)
        {   i = 0;
            for (;;)
            {   ch = *s;
                if (ch == 0) break;
                s++;
                if (ch == ',') break;
                if (ch >= '0' && ch <= '9')
                    i = i * 10 + (ch - '0');
                else
                    goto badargs;
            }
            if (i != 0)
            {   if (i < 5 || i > 500)
                {   fprintf(stderr, "mkbmap: point size %d out of range "
                                            "(5 - 500)\n", pointsize);
                    goto errorexit;
                }
                if (pointc == 100) goto badargs;
                pointv[pointc++] = i;
            }
        }
    }

    if (pointc == 0) goto newfont;

    if (argencodingfile == NULL) argencodingfile = "PSFonts:encoding.ps";
    if (argstartupfile  == NULL) argstartupfile  = "PSFonts:init.ps";

    /* Initialise the PostScript library */

    pstrace("%% Opening PostScript library\n");
    PSbase = OpenLibrary("post.library", POSTVERNO);
    if (PSbase == NULL)
    {   fprintf(stderr, "mkbmap: can't open post.library (V15+)\n");
        goto errorexit;
    }

    pstrace("%% Initialising PostScript activation\n");
    parm.page.buf[0] = namebuf;
    parm.page.len = 100;
    parm.page.depth = 1;
    parm.page.xoff = 0;
    parm.page.yoff = 0;
    parm.page.xbytes = 10;
    parm.page.xsize = 80;
    parm.page.ysize = 10;
    parm.page.ybase = 0;
    parm.page.yheight = 10;
    parm.page.xden = 72;
    parm.page.yden = 72;
    parm.page.ydir = -1;
    parm.memvlen = 20000;
    parm.memflen = 10000;
    parm.memllen = defmemllen;
    parm.memhlen = minmemhlen;
    parm.infh = Input();
    parm.outfh = Output();
    parm.errfh = Output();
    parm.funcmax = 2;
    functab[0] = (APTR) setbbox;
    functab[1] = (APTR) setcpos;
    parm.functab = functab;
    insertftrap();
    ftrapset = 1;
    arec = PScreateact(&parm);
    if (arec == 0)
    {   fprintf(stderr, "mkbmap: post.library can't get memory\n");
        goto errorexit;
    }
    if ((unsigned) arec <= errmax)
    {   pserror = arec;
        arec = 0;
        goto pserror;
    }

    /* Load the startup file */

    pstrace("%% Loading startup file\n");
    pssintf("(%.100s) run clear\n", argstartupfile);

    /* Load the encoding file */

    if (optfontencoding == 0)
    {   pstrace("% Loading encoding file\n");
        pssintf("/encoding StandardEncoding 256 array copy def\n");
        pssintf("[ (%.100s) run ] aload length 2 idiv\n", argencodingfile);
        pssintf("{ encoding 3 1 roll put } repeat\n");
    }


    /* Load the font.  Make sure we don't get a substitute */

    pstrace("%% Loading font\n");
    pssintf("/fontname /%.100s def\n", argfontname);
    pssintf("fontname findfont pop\n");
    pssintf("/font fontname .findfont def\n");

    /* Encode the font */

    pstrace("%% Encoding font\n");
    pssintf("/newfont font maxlength dict def\n");
    pssintf("font\n"
            "{ exch dup /FID ne "
            "{ exch newfont 3 1 roll put } { pop pop } ifelse }\n"
            "forall\n");
    if (optfontencoding == 0)
           pssintf("newfont /Encoding encoding put\n");
    pssintf("/_%.100s newfont definefont pop\n", argfontname);
    if (pserror != 0) goto pserror;

    pstrace("%% LoChar = %d; HiChar = %d\n", lochar, hichar);

    /* Determine the bounding boxes for all the characters */

    pstrace("%% Generating bounding boxes\n");
    pssintf("/cstr 1 string def\n");
    pssintf("newfont 1000 scalefont setfont\n");
    pssintf("%d 1 %d\n"
            "{ /i exch def cstr 0 i put null i\n"
            "  0 0 moveto cstr false charpath pathbbox newpath\n"
            "  4 { round cvi 4 1 roll } repeat\n"
            "  5 0 callextfunc\n"
            "} for\n", lochar, hichar);
    if (pserror != 0) goto pserror;

    /* Position the baseline, if we have not set it explicitly */

    if (optbaseline == -1)
    {
        /* This is what we have room for */

        zz = (1000 * 72) / optyden;

        /* Calculate the lower and upper limits of the whole character set */

        z1 = z2 = 0;
        for (i = 0; i <= 255 ; i++)
        {   if (lly[i] < z1) z1 = lly[i];
            if (ury[i] > z2) z2 = ury[i];
        }
        if (z2 == z1)
        {   fprintf(stderr, "mkbmap: all characters in the font are null\n");
            goto errorexit;
        }

        /* If we have room for everything, or the font has a non-standard
         * character set, set the baseline in proportion to the relative
         * sizes of the ascenders and descenders */

        optbaseline = (1000 * (-z1) + (z2 - z1) / 2) / (z2 - z1);

        /* Otherwise, for fonts with standard character sets, calculate the
         * limits of the alphabet.  If we have room for that then leave just
         * enough room for the descenders, or else set the baseline
         * proportionately */

        if (z2 - z1 > zz && optfontencoding == 0)
        {   y1 = y2 = 0;
            for (i = 'A'; i <= 'Z' ; i++)
            {   if (lly[i] < y1) y1 = lly[i];
                if (ury[i] > y2) y2 = ury[i];
            }
            for (i = 'a'; i <= 'z' ; i++)
            {   if (lly[i] < y1) y1 = lly[i];
                if (ury[i] > y2) y2 = ury[i];
            }
            if (y2 - y1 <= zz)
                optbaseline = (1000 * (-y1) + zz / 2) / zz;
            else
                optbaseline = (1000 * (-y1) + (y2 - y1) / 2) / (y2 - y1);
        }
        pstrace("%% Baseline set at %d/1000 of the pixel height\n",
                optbaseline);
    }

    /* Validate the font file name and create the font subdirectory */

    if (buildfdirname(namebuf, argbmapfile, 0) == NULL)
    {   fprintf(stderr, "mkbmap: bmap file has no directory path\n");
        goto errorexit;
    }
    if (optnew)
    {   fdlock = CreateDir(namebuf);
        if (fdlock)
        {   UnLock(fdlock);
            fdlock = 0;
        }
    }

    /* Loop through all the point sizes */

    for (pointn = 0; pointn < pointc; pointn++)
    {   if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) goto broken;

        pointsize = pointv[pointn];
        pstrace("%% Point size %d\n", pointsize);
        baseline = (pointsize * optbaseline + 500) / 1000;
        if (baseline < 0) baseline = 0;
        if (baseline > pointsize - 1) baseline = pointsize - 1;


        /* Build the bitmapped font in memory.  Estimate the maximum
         * possible width of the bitmap from the total widths of the
         * bounding boxes, allowing 2 extra pixels per character for
         * rounding and stem alignment.  Calculate the mamimum font
         * length, allocate the memory, and initialise the headers */

        baselen = 4 + sizeof (struct DiskFontHeader) +
                      8 * (hichar - lochar + 2);

        maxwidth = 0;
        for (ch = lochar; ch <= hichar ; ch++)
            maxwidth +=
                ((urx[ch] - llx[ch]) * pointsize * optxden) / 72000 + 2;
        maxmodulo = ((maxwidth + 15) >> 3) & ~1;
        maxcharlen = maxmodulo * pointsize;
        maxfontlen = baselen + maxcharlen + 2 + 76;

        fontmem = AllocMem(maxfontlen, MEMF_PUBLIC|MEMF_CLEAR);
        if (fontmem == NULL)
        {   fprintf(stderr, "mkbmap: can't get font memory\n");
            goto errorexit;
        }

        hunkptr   = fontmem;
        dfheader  = (void *) &hunkptr[9];
        charloc   = (void *) &dfheader[1];
        charspace = (void *) &charloc[(hichar - lochar + 2) * 2];
        charkern  = (void *) &charspace[hichar - lochar + 2];
        chardata  = (void *) &charkern[hichar - lochar + 2];

        /* Set up the bitmap to render the characters */

        pstrace("%% Rendering bitmaps\n");
        parm.page.buf[0] = (char *) chardata;
        parm.page.len = maxcharlen;
        parm.page.depth = 1;
        parm.page.xoff = 0;
        parm.page.yoff = 0;
        parm.page.xbytes = maxmodulo;
        parm.page.xsize = maxwidth;
        parm.page.ysize = pointsize;
        parm.page.ybase = 0;
        parm.page.yheight = pointsize;
        PSsetdevice(arec, &parm.page);

        pssintf("initmatrix\n");
        pssintf("/xscale %d %d mul 72 div def\n", pointsize, optxden);
        pssintf("/yscale %d %d mul 72 div def\n", pointsize, optyden);
        pssintf("newfont [ xscale 0 0 yscale 0 0 ] makefont setfont\n");

        actwidth = 0;
        nomwidth = 0;
        nomcount = 0;

        /* Loop to render each character.  We estimate the bounds of the
         * characters conservatively (hinting any shift them a little) and
         * trim the bitmap if possible.  We clip on the left, so any part
         * of the character projecting to the left is truncated, and does
         * not overwrite the previous one.  We erase the character cell so
         * it does not matter if the clipping of the right of the previous
         * character was not complete.  We render the portiona above and
         * below the baseline separately, so they can be indepently scaled
         * to fit */

        for (ch = lochar; ch <= hichar; ch++)
        {   z1 = (llx[ch] * pointsize * optxden - 41000) / 72000;
            z2 = (urx[ch] * pointsize * optxden + 41000) / 72000;
            if (z1 < 0) z1 = 0;
            pssintf("cstr 0 %d put\n", ch);
            pssintf("%d %d moveto\n", actwidth, baseline);
            pssintf("gsave %d 0 rlineto 0 %d rlineto "
                    "-%d 0 rlineto closepath clip\n",
                    z2, -baseline, z2);
            pssintf("1 setgray fill 0 setgray\n");
            pssintf("%d %d moveto ", actwidth - z1, baseline);
            y1 = ((-lly[ch]) * pointsize * optxden) / 72;
            y2 = baseline * 1000;
            if (y1 > y2)
                pssintf("1 %d %d div scale\n", y2, y1);
            pssintf("cstr show\n"
                    "grestore\n");
            pssintf("gsave %d 0 rlineto 0 %d rlineto "
                    "-%d 0 rlineto closepath clip\n",
                    z2, pointsize - baseline, z2);
            pssintf("1 setgray fill newpath 0 setgray\n");
            pssintf("%d %d moveto ", actwidth - z1, baseline);
            y1 = (ury[ch] * pointsize * optxden) / 72;
            y2 = (pointsize - baseline) * 1000;
            if (y1 > y2)
                pssintf("1 %d %d div scale\n", y2, y1);
            pssintf("cstr show\n"
                    "null currentpoint 2 { round cvi exch } repeat "
                    "2 1 callextfunc\n"
                    "grestore\n");
            ww = cposx - actwidth;
            zz = z2 - z1;
            if (zz > ww) zz = ww;
            i = ch - lochar;
            charloc[i * 2] = actwidth;
            charloc[i * 2 + 1] = zz;
            charspace[i] = ww;
            charkern[i] = z1;
            actwidth += zz;
            if (lly[ch] != ury[ch])
            {   nomwidth += z1 + ww;
                nomcount++;
            }
            if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
                goto broken;
        }

        if (pserror != 0) goto pserror;

        /* If we have not explicitly set the nominal character width,
         * calculate it as the average width of the non-null characters
         * we have generated */

        if (optwidth == -1)
        {   if (nomcount != 0)
                nomwidth = (nomwidth + nomcount / 2) / nomcount;
        }
        else
            nomwidth = (optwidth * pointsize * optxden + 36000) / 72000;

        /* We now know the actual width, so we can calculate the actual
         * modulo and file lengths.  Then we must reformat the bitmap to
         * the new width */

        if (actwidth > maxwidth)
        {   fprintf(stderr, "mkbmap: width estimate exceeded\n");
            goto errorexit;
        }
        actmodulo = ((actwidth + 15) >> 3) & ~1;
        actcharlen = actmodulo * pointsize;
        hunklen = baselen + actcharlen;
        if (hunklen & 2) hunklen += 2;
        hunknum = hunklen >> 2;
        actfontlen = hunklen + 76;

        i = 1;
        s = chardata + maxmodulo;
        t = chardata + actmodulo;
        while (i < pointsize)
        {   l = actmodulo;
            while (l--) *t++ = *s++;
            i++;
            s += (maxmodulo - actmodulo);
        }

        /* Finish the bitmapped font.  Write it out to the file */

        buildfdirname(namebuf, argbmapfile, 2);

        hunkptr[0] = Hunk_Header;
        hunkptr[1] = 0;
        hunkptr[2] = 1;
        hunkptr[3] = 0;
        hunkptr[4] = 0;
        hunkptr[5] = hunknum;
        hunkptr[6] = Hunk_Code;
        hunkptr[7] = hunknum;
        hunkptr += 8;

        hunkptr[0] = MOVFFD0RTS;

        dfheader->dfh_DF.ln_Type = NT_FONT;
        dfheader->dfh_DF.ln_Name = (char *) 0x0000001A;
        dfheader->dfh_FileID = DFH_ID;
        strncpy((char *)&dfheader->dfh_Name, namebuf, MAXFONTNAME);
        dfheader->dfh_TF.tf_Message.mn_Node.ln_Type = NT_FONT;
        dfheader->dfh_TF.tf_Message.mn_Node.ln_Name = (char *) 0x0000001A;
        dfheader->dfh_TF.tf_Message.mn_Length = hunklen - 58;
        dfheader->dfh_TF.tf_YSize = pointsize;
        dfheader->dfh_TF.tf_Style =
            (optbold ? FSF_BOLD : 0) | (optitalic ? FSF_ITALIC : 0);
        dfheader->dfh_TF.tf_Flags =
            (optmonospaced ? 0 : FPF_PROPORTIONAL) | FPF_DESIGNED;
        dfheader->dfh_TF.tf_XSize = nomwidth;
        dfheader->dfh_TF.tf_Baseline = pointsize - baseline - 1;
        dfheader->dfh_TF.tf_BoldSmear = pointsize / 20 + 1;
        dfheader->dfh_TF.tf_LoChar = lochar;
        dfheader->dfh_TF.tf_HiChar = hichar;
        dfheader->dfh_TF.tf_CharData =
            (APTR) ((char *) chardata  - (char *) hunkptr);
        dfheader->dfh_TF.tf_Modulo = actmodulo;
        dfheader->dfh_TF.tf_CharLoc =
            (APTR) ((char *) charloc   - (char *) hunkptr);
        dfheader->dfh_TF.tf_CharSpace =
            (APTR) ((char *) charspace - (char *) hunkptr);
        dfheader->dfh_TF.tf_CharKern =
            (APTR) ((char *) charkern  - (char *) hunkptr);

        hunkptr += hunknum;

        hunkptr[0] = Hunk_Reloc32;
        hunkptr[1] = 6;
        hunkptr[2] = 0;
        hunkptr[3] = 0x0000006A;
        hunkptr[4] = 0x00000066;
        hunkptr[5] = 0x00000062;
        hunkptr[6] = 0x0000005C;
        hunkptr[7] = 0x00000044;
        hunkptr[8] = 0x0000000E;
        hunkptr[9] = 0;
        hunkptr[10] = Hunk_End;

        pstrace("%% Writing bmap file\n");
        if (buildfnumname(namebuf, argbmapfile, pointsize) == NULL)
        {   fprintf(stderr, "mkbmap: bmap file name too long\n");
            goto errorexit;
        }
        newfh = Open(namebuf, MODE_NEWFILE);
        if (newfh == NULL)
        {   fprintf(stderr, "mkbmap: can't open bmap file %s\n", namebuf);
            goto errorexit;
        }
        i = Write(newfh, (UBYTE *) fontmem, actfontlen);
        Close(newfh);
        if (i == -1)
        {   fprintf(stderr, "mkbmap: error writing bmap file %s\n", namebuf);
            goto errorexit;
        }
    }

    /* Update font contents file */

newfont:
    if (optnew)
    {   pstrace("%% Updating font contents file\n");
        buildfdirname(namebuf, argbmapfile, 1);
        fdlock = Lock(namebuf, SHARED_LOCK);
        if (fdlock == NULL)
        {   fprintf(stderr, "mkbmap: can't lock font directory %s\n",
                            namebuf);
            goto errorexit;
        }
        buildfdirname(namebuf, argbmapfile, 2);
        strcat(namebuf, ".font");
        DiskfontBase = OpenLibrary("diskfont.library", 34);
        if (DiskfontBase) fcheader = NewFontContents(fdlock, namebuf);
        if (fcheader == NULL)
        {   fprintf(stderr, "mkbmap: can't create new font contents %s\n",
                    namebuf);
            goto errorexit;
        }
        if (fcheader->fch_NumEntries == 0)
        {   fprintf(stderr, "mkbmap: font %s directory contains no fonts\n",
                    namebuf);
            retcode = 10;
            goto tidyexit;
        }

        /* Swap to the font directory, update the file, and swap back */

        cdlock = CurrentDir(fdlock);
        newfh = Open(namebuf, MODE_NEWFILE);
        if (newfh)
        {   i = Write(newfh, (UBYTE *)fcheader,
                    sizeof (struct FontContentsHeader) +
                    sizeof (struct FontContents) * fcheader->fch_NumEntries);
            Close(newfh);
        }
        CurrentDir(cdlock);
        if      (newfh == NULL)
        {   fprintf(stderr, "mkbmap: can't open font contents file %s\n",
                    namebuf);
            goto errorexit;
        }
        else if (i == -1)
        {   fprintf(stderr, "mkbmap: error writing font contents file %s\n",
                    namebuf);
            goto errorexit;
        }

        /* Tidy up */

        DisposeFontContents(fcheader);
        fcheader = NULL;
        CloseLibrary(DiskfontBase);
        DiskfontBase = NULL;
        UnLock(fdlock);
        fdlock = NULL;
    }

    /* All done */

    pstrace("%% All done\n");
    goto tidyexit;

    /* Argument errors and usage query */

query:
    fprintf(stderr, "Bitmap font generator.  MkBmap version 1.0\n"
                    "Makes Amiga bitmapped fonts from PostScript fonts\n"
                    "\n"
                    "  Usage:\n"
                    "\n"
                    "    mkbmap -options bmapfile fontname nn,nn,...\n"
                    "\n"
                    "      -s startupfile  Startup file name\n"
                    "      -e encodingfile Encoding file name\n"
                    "      -f              Font specific encoding\n"
                    "      -n              Make new .font contents\n");
    fprintf(stderr, "      -dnnn           Density: x and y (dpi)\n"
                    "      -xnnn           Density: x (dpi)\n"
                    "      -ynnn           Density: y (dpi)\n"
                    "      -znnn           Baseline (1/1000)\n"
                    "      -wnnn           Width    (1/1000)\n"
                    "      -lnnn           LoChar\n"
                    "      -hnnn           Hichar\n"
                    "      -m              Monospaced\n"
                    "      -b              Bold\n"
                    "      -i              Italic\n"
                    "      -t              Trace (for debuggindg)\n"
                    "\n"
                    "  For example:\n"
                    "\n"
                    "    mkbmap -n fonts:Times/* Times-Roman 10,12,14\n");
    goto tidyexit;

badargs:
    fprintf(stderr, "mkbmap: arguments bad, or value missing");
badusage:
    retcode = 20;
    fprintf(stderr, ".  Usage:\n"
                    "    mkbmap -options bmapfile fontname nn,nn,...\n");
    goto tidyexit;

    /* PostScript library error */

pserror:
    fprintf(stderr, "mkbmap: post.library interpreter error\n");
    goto errorexit;

    /* Tidy up and exit */

broken:
    fprintf(stderr, "mkbmap: *** Break\n");
    retcode = 10;
    goto tidyexit;

errorexit:
    retcode = 20;

tidyexit:
    if (ftrapset)
    {   deleteftrap();
        ftrapset = 0;
    }

    if (arec) PSdeleteact(arec);
    if (PSbase) CloseLibrary(PSbase);

    if (fontmem) FreeMem(fontmem, maxfontlen);

    if (fcheader) DisposeFontContents(fcheader);
    if (DiskfontBase) CloseLibrary(DiskfontBase);
    if (fdlock) UnLock(fdlock);

    exit(retcode);
}

/* String to integer conversion; digits only, with error check */

int strtoint(char **sp, int *ip)
{   char *s = *sp;
    int i = 0;
    int ch;
    for (;;)
    {   ch = *s;
        if (ch < '0' || ch > '9') break;
        i = i * 10 + (ch - '0');
        s++;
    }
    if (s == *sp)
        return 0;
    else
    {   *sp = s;
        *ip = i;
        return 1;
    }
}

/* Build the bitmap font file name.  Copy it, replacing "*" by the point
 * size.  Then scan it backwards replacing "?" by digits */

char *buildfnumname(char *nbuf, char *name, int num)
{   char number[10];
    int numlen, i, j, k, ch;
    numlen = 0;
    while (num)
    {   number[numlen++] = num % 10 + '0';
        num /= 10;
    }
    i = j = 0;
    for (;;)
    {   if (j > 100) return NULL;
        ch = name[i++];
        if (ch == '*')
        {   k = numlen;
            while (k--) nbuf[j++] = number[k];
        }
        else
           nbuf[j++] = ch;
        if (ch == 0) break;
    }
    k = 0;
    while (--j)
    {   if (nbuf[j] == '?')
            nbuf[j] = (k < numlen) ? number[k++] : '0';
    }
    return nbuf;
}

/* Build the font file/directory name.  We copy it, striping off the last
 * two components:
 *
 * type = 0: fontdir/fontname/* => fontdir/fontname
 * type = 1: fontdir/fontname/* => fontdir
 * type = 2: fontdir/fontname/* => fontname
 *
 *       (or fontdir:fontname/* => ...)
 */

char *buildfdirname(char *nbuf, char *name, int type)
{   int i, j, k;
    i = strlen(name);
    for (;;)
    {   if (i == 0) return NULL;
        i--;
        if (name[i] == ':') return NULL;
        if (name[i] == '/') break;
    }
    j = i;
    for (;;)
    {   if (j == 0) return NULL;
        j--;
        if (name[j] == ':')
        {   k = j = j + 1;
            break;
        }
        if (name[j] == '/')
        {   k = j;
            j = j + 1;
            break;
        }
    }
    if (k == 0) return NULL;
    if (i >= 100) return NULL;
    if      (type == 1)
        i = k;
    else if (type == 2)
    {   name += j;
        i -= j;
    }
    memcpy(nbuf, name, i);
    nbuf[i] = 0;
    return nbuf;
}

/* Trace output */

void pstrace(char *format, ...)
{   va_list ap;
    if (pserror != 0) return;
    if (opttrace)
    {   va_start(ap, format);
        vfprintf(stdout, format, ap);
        fflush(stdout);
        va_end(ap);
    }
}

/* Interpret postscript string */

void pssintf(char *format, ...)
{   va_list ap;
    if (pserror != 0) return;
    va_start(ap, format);
    if (opttrace)
    {   vfprintf(stdout, format, ap);
        fflush(stdout);
    }
    vsprintf(psstring, format, ap);
    pserror = PSintstring(arec, psstring, -1, PSFLAGSTRING)
    va_end(ap);
}

/* Set character bounding box */

void __saveds setbbox(int ch, int lx, int ly, int rx, int uy)
{   llx[ch] = lx;
    lly[ch] = ly;
    urx[ch] = rx;
    ury[ch] = uy;
}

/* Set current point */

void __saveds setcpos(int cpx, int cpy)
{   cposx = cpx;
    cposy = cpy;
}

/* Signal an interrupt */

void __saveds sigint()
{   PSsignalint(arec, 1);
}

/* Signal a floating point error */

void __saveds sigfpe()
{   PSsignalfpe(arec);
}

/* Dummy stub routine */

void stub(void)
{   return;
}

/* Dummy check abort routine */

void chkabort(void)
{   return;
}

/* End of file "mkbmap.c" */
