/* Metric file builder for PPage etc.  File "mkmetric.c".
 * (C) Adrian Aylward 1991
 *
 * You may freely copy, use, and modify this file.
 *
 * This program prints builds a font metric (.metric) file in the format
 * used by Professional Page.  Its input is one or more Adobe font metric
 * (.afm) files.
 *
 * The program was tested using Lattice C V5.05.  It has various Lattice
 * dependencies.
 *
 * This is version 1.1.
 */

# include <dos.h>
# include <exec/exec.h>
# include <proto/dos.h>
# include <proto/exec.h>
# include <string.h>
# include <setjmp.h>
# include <stdio.h>

/* Metric file format */

struct mfile_header
{   int mf_sect[5];
};

struct msect_header
{   char ms_fontname[40];
    char ms_filler1[26];
    int ms_widthtable;
    int ms_kerntable;
    char ms_filler2[6];
    char ms_fixedpitch;           /* 0 = fixed, 1 = variable */
    char ms_encoding;             /* 0 = font, 1 = standard */
    short ms_fontbbox[4];
    short ms_underlineposition;
    short ms_underlinethickness;
    short ms_capheight;
    short ms_xheight;
    short ms_ascender;
    short ms_descender;
    short ms_stype;
};

struct mkern_entry
{   short mk_value;
    short mk_code;
};

/* External data (initialised to zero) */

char *argmetricfile, *argafmfiles;
char *argencodingfile;
int optfontencoding, optsetfontencoding;

int lochar, hichar;

BPTR newfh;

# define memsize 10000

char *membeg[100], *memptr;
int memsegs, memfree;


# define filesize 100000

char *filemem;
int filelen;

struct mfile_header *mfhdr;
struct msect_header *mshdr;

int sectno, sectbeg;

short *widthbeg;

int kernflag;
struct mkern_entry *kernbeg;

char currentfile[100];
FILE *currentfptr;

# define NOURCH   (EOF - 1)

int lineno, urch;
int scanmode;

int retcode;
jmp_buf errjmp;

# define typeeof     0
# define typeeol     1
# define typesemi    2
# define typepercent 3
# define typename    4
# define typeint     5

int tokentype, tokenival;

char namebuf[256];

char charname[256], charpair[256];

char *charstr;

int charcode, charwidth, charkern;
int fontwidth;

int kernsize;
short *kerntable[256];

/* Encoding vector, initially Adobe StandardEncoding */

char *encoding[256] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  "space",         "exclam",        "quotedbl",      "numbersign", /* 20 */
  "dollar",        "percent",       "ampersand",     "quoteright",
  "parenleft",     "parenright",    "asterisk",      "plus",
  "comma",         "hyphen",        "period",        "slash",
  "zero",          "one",           "two",           "three",      /* 30 */
  "four",          "five",          "six",           "seven",
  "eight",         "nine",          "colon",         "semicolon",
  "less",          "equal",         "greater",       "question",
  "at",            "A",             "B",             "C",          /* 40 */
  "D",             "E",             "F",             "G",
  "H",             "I",             "J",             "K",
  "L",             "M",             "N",             "O",
  "P",             "Q",             "R",             "S",          /* 50 */
  "T",             "U",             "V",             "W",
  "X",             "Y",             "Z",             "bracketleft",
  "backslash",     "bracketright",  "asciicircum",   "underscore",
  "quoteleft",     "a",             "b",             "c",          /* 60 */
  "d",             "e",             "f",             "g",
  "h",             "i",             "j",             "k",
  "l",             "m",             "n",             "o",
  "p",             "q",             "r",             "s",          /* 70 */
  "t",             "u",             "v",             "w",
  "x",             "y",             "z",             "braceleft",
  "bar",           "braceright",    "asciitilde",    0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0,               "exclamdown",    "cent",          "sterling",   /* A0 */
  "fraction",      "yen",           "florin",        "section",
  "currency",      "quotesingle",   "quotedblleft",  "guillemotleft",
  "guilsinglleft", "guilsinglright","fi",            "fl",
  0,               "endash",        "dagger",        "daggerdbl",  /* B0 */
  "periodcentered",0,               "paragraph",     "bullet",
  "quotesinglbase","quotedblbase",  "quotedblright", "guillemotright",
  "ellipsis",      "perthousand",   0,               "questiondown",
  0,               "grave",         "acute",         "circumflex", /* C0 */
  "tilde",         "macron",        "breve",         "dotaccent",
  "dieresis",      0,               "ring",          "cedilla",
  0,               "hungarumlaut",  "ogonek",        "caron",
  "emdash",        0,               0,               0,            /* D0 */
  0,               0,               0,               0,
  0,               0,               0,               0,
  0,               0,               0,               0,
  0,               "AE",            0,               "ordfeminine",/* E0 */
  0,               0,               0,               0,
  "Lslash",        "Oslash",        "OE",            "ordmasculine",
  0,               0,               0,               0,
  0,               "ae",            0,               0,            /* F0 */
  0,               "dotlessi",      0,               0,
  "lslash",        "oslash",        "oe",            "germandbls",
  0,               0,               0,               0
};

/* Routines */

extern int  strtoint(char **sp, int *ip);
extern void readencoding(void);
extern void convertafm(void);
extern int  mvalue(int val);
extern void rdkey(void);
extern void rdname(void);
extern void rdbool(void);
extern void rdint(void);
extern void rdnumb(void);
extern void rdstring(void);
extern void rdtoken(void);
extern void checksemi(void);
extern void checkeol(void);
extern void skipsemi(void);
extern void skipeol(void);
extern void skipspace(void);
extern int  rdch(void);
extern void unrdch(int ch);
extern void errsyntax(char *f);
extern void errfilesize(void);
extern void errmemory(void);
extern void makekern(int code, int pair, int kern);
extern int  buildkern(int maxlen);
extern char *makestring(char *str);
extern void *allocmem(int size);
extern void freeall(void);
extern void stub(void);
extern void chkabort(void);

/* Main program */

void main(int argc, char **argv)
{   char *s, *t;
    int *ip, i, ch;

    /* Parse arguments.  No workbench startup */

    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 'E': case 'e':
                    if (argc == 0) goto badargs;
                    argencodingfile = *argv++;
                    argc--;
                    continue;

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

                case 'S': case 's':
                    optsetfontencoding = 1;
                    continue;

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

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

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

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

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

    if (argc != 2) goto badargs;
    argmetricfile = argv[0];
    argafmfiles = argv[1];

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

    /* Read the encoding file */

    if (optfontencoding == 0)
    {   strncpy(currentfile, argencodingfile, 99);
        currentfile[99] = 0;
        currentfptr = fopen(argencodingfile, "r");
        if (currentfptr == NULL)
        {   fprintf(stderr, "mkmetric: can't open encoding file %s\n",
                    argencodingfile);
            goto errorexit;
        }
        readencoding();
        if (retcode != 0) goto errorexit;
        fclose(currentfptr);
        currentfptr = NULL;
    }
    else
        memset((char *) encoding, 0, sizeof (encoding));

    /* Allocate the metric file memory */

    filemem = AllocMem(filesize, MEMF_CLEAR);
    if (filemem == NULL)
    {   fprintf(stderr, "mkbmap: can't get metrci file memory\n");
        goto errorexit;
    }
    mfhdr = (void *) filemem;
    filelen = sizeof (struct mfile_header);

    /* Loop to process all the AFM files */

    sectno = 0;
    s = argafmfiles;
    for (;;)
    {   t = s;
        while (*t != ',' && *t != 0) t++;
        i = t - s;
        if (i >= 100)
        {   fprintf(stderr, "mkmetric: afm file name too long\n");
            goto errorexit;
        }
        if (i == 0)
        {   if (sectno == 0)
            {   fprintf(stderr,
                        "mkmetric: plain version afm file name missing\n");
                goto errorexit;
            }
        }
        else
        {   memcpy(currentfile, s, i);
            currentfile[i] = 0;
            currentfptr = fopen(currentfile, "r");
            if (currentfptr == NULL)
            {   fprintf(stderr,
                        "mkmetric: can't open afm file %s\n", currentfile);
                goto errorexit;
            }
            mfhdr->mf_sect[sectno] = filelen;
            convertafm();
            if (retcode != 0) goto errorexit;
            fclose(currentfptr);
            currentfptr = NULL;
        }
        sectno++;
        if (*t == 0) break;
        s = t + 1;
    }
    mfhdr->mf_sect[4] = filelen;

    /* All done. Write out the file an exit */

    newfh = Open(argmetricfile, MODE_NEWFILE);
    if (newfh == NULL)
    {   fprintf(stderr, "mkmetric: can't open metric file %s\n",
                argmetricfile);
        goto errorexit;
    }
    i = Write(newfh, (UBYTE *) filemem, filelen);
    Close(newfh);
    if (i == -1)
    {   fprintf(stderr, "mkmetric: error writing metric file %s\n",
                argmetricfile);
        goto errorexit;
    }
    goto tidyexit;

    /* Argument errors and usage query */

query:
    fprintf(stderr, "Metric file builder.  MkMetric version 1.1\n"
                    "Makes PPage .metric files from Adobe .afm files\n"
                    "\n"
                    "  Usage:\n"
                    "\n"
                    "    mkmetric -options metricfile "
                                "plain.afm,bold..,italic..,bolditalic..\n"
                    "\n"
                    "      -e encodingfile  Encoding file name\n"
                    "      -f               Font specific encoding\n"
                    "      -s               Set font specific encoding\n"
                    "      -n               No character names\n");
    fprintf(stderr, "      -lnnn            LoChar\n"
                    "      -hnnn            Hichar\n"
                    "\n"
                    "  For example:\n"
                    "\n"
                    "    mkmetric fonts:CooperBlack.metric "
                                "CooperBlack.afm,,CooperBlack-Italic.afm\n");
    goto tidyexit;

badargs:
    fprintf(stderr, "mkmetric: arguments bad, or value missing");
badusage:
    retcode = 20;
    fprintf(stderr, ".  Usage:\n"
                    "    mkmetric -options metricfile "
                                "plain.afm,bold..,italic..,bolditalic..\n");
    goto tidyexit;

    /* Tidy up and exit */

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

errorexit:
    retcode = 20;

tidyexit:
    if (currentfptr) fclose(currentfptr);
    currentfptr = NULL;
    if (filemem) FreeMem(filemem, filesize);
    freeall();
    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;
    }
}

/* Read the encoding file */

void readencoding(void)
{   lineno = 1;
    urch = NOURCH;
    scanmode = 0;

    retcode = setjmp(errjmp);
    if (retcode != 0) return;

    for (;;)
    {   rdtoken();
        if (tokentype == typeeof) break;
        if (tokentype == typeeol) continue;
        if (tokentype != typeint) errsyntax("integer expected");
        charcode = tokenival;
        rdtoken();
        if (tokentype != typename) errsyntax("name expected");
        if (namebuf[0] == '/')
           charstr = makestring(&namebuf[1]);
        else
           charstr = makestring(&namebuf[0]);
        rdtoken();
        if (tokentype != typeeol) errsyntax("end of line expected");
        if (charcode < 0 || charcode > 255)
            errsyntax("character code out of range");
        encoding[charcode] = charstr;
    }
}

/* Convert a single AFM file */

void convertafm(void)
{   int i, j;

    sectbeg = filelen;
    mshdr = (void *) (filemem + filelen);
    filelen += sizeof (struct msect_header);
    if (filelen > filesize) errfilesize();
    mshdr->ms_fixedpitch = 1;
    mshdr->ms_encoding = (optfontencoding | optsetfontencoding) ? 0 : 1;
    mshdr->ms_stype = sectno + 1;
    mshdr->ms_widthtable = filelen - sectbeg;
    widthbeg = (void *) (filemem + filelen);
    filelen += sizeof (short) * 256;
    if (filelen > filesize) errfilesize();

    lineno = 1;
    urch = NOURCH;
    scanmode = 1;

    retcode = setjmp(errjmp);
    if (retcode != 0) return;

    kernflag = 0;
    for (i = lochar; i <= hichar; i++) kerntable[i] = NULL;

    rdkey();
    if (strcmp(namebuf, "StartFontMetrics") != 0)
        errsyntax("StartFontMetrics expected\n");
    skipeol();

rdl:
    rdkey();
    if (strcmp(namebuf, "EndFontMetrics") == 0)
    {   skipeol();
        goto eof;
    }
    if (strcmp(namebuf, "FontName") == 0)
    {   rdstring();
        strncpy(mshdr->ms_fontname, namebuf, 40);
        goto rdl;
    }
    if (strcmp(namebuf, "FontBBox") == 0)
    {   for (i = 0 ; i < 4; i++)
        {   rdint();
            mshdr->ms_fontbbox[i] = mvalue(tokenival);
        }
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "CapHeight") == 0)
    {   rdnumb();
        mshdr->ms_capheight = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "XHeight") == 0)
    {   rdnumb();
        mshdr->ms_xheight = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "Ascender") == 0)
    {   rdnumb();
        mshdr->ms_ascender = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "Descender") == 0)
    {   rdnumb();
        mshdr->ms_descender = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "UnderlinePosition") == 0)
    {   rdnumb();
        mshdr->ms_underlineposition = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "UnderlineThickness") == 0)
    {   rdnumb();
        mshdr->ms_underlinethickness = mvalue(tokenival);
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "CharWidth") == 0)
    {   rdnumb();
        fontwidth = mvalue(tokenival);
        rdnumb();
        checkeol();
        goto rdl;
    }
    if (strcmp(namebuf, "IsFixedPitch") == 0)
    {   rdbool();
        mshdr->ms_fixedpitch = tokenival ? 0 : 1;
        checkeol();
        goto rdl;
    }

    if (strcmp(namebuf, "C") == 0)
    {   charcode = -1;
        charname[0] = 0;
        charwidth = fontwidth;
        rdint();
        charcode = tokenival;
        if (charcode > 255) errsyntax("character code out of range");
        checksemi();
        for (;;)
        {   rdtoken();
            if (tokentype == typeeol) break;
            if (tokentype != typename) errsyntax("key expected");
            if      (strcmp(namebuf, "N") == 0)
            {   rdname();
                strcpy(charname, namebuf);
                checksemi();
            }
            else if (strcmp(namebuf, "WX") == 0)
            {   rdnumb();
                charwidth = mvalue(tokenival);
                checksemi();
            }
            else
                skipsemi();
        }
        if (optfontencoding == 0)
        {   if (charname[0] == 0) errsyntax("character name missing");
            for (i = lochar; i <= hichar; i++)
                if (encoding[i] != NULL &&
                        strcmp(encoding[i], charname) == 0)
                    widthbeg[i] = charwidth;
        }
        else
            if (charcode >= lochar && charcode <= hichar)
            {   if (charname[0] != 0)
                    encoding[charcode] = makestring(charname);
                widthbeg[charcode] = charwidth;
            }
        goto rdl;
    }

    if (strcmp(namebuf, "KPX") == 0)
    {   rdname();
        strcpy(charname, namebuf);
        rdname();
        strcpy(charpair, namebuf);
        rdnumb();
        charkern = mvalue(tokenival);
        checkeol();
        if (charkern != 0)
            for (i = lochar; i <= hichar; i++)
                if (encoding[i] != NULL &&
                        strcmp(encoding[i], charname) == 0)
                    for (j = lochar; j <= hichar; j++)
                        if (encoding[j] != NULL &&
                                strcmp(encoding[j], charname) == 0)
                        {   kernflag = 1;
                            makekern(i, j, charkern);
                        }
        goto rdl;
    }

    skipeol();
    goto rdl;

    /* End of file.  Build the kerning (sparse) array */
eof:
    if (kernflag)
    {   mshdr->ms_kerntable = filelen - sectbeg;
        kernbeg = (void *) (filemem + filelen);
        filelen += buildkern(filesize - filelen);
    }
}

/* Scale a character coordinate value for the .metric file */

int  mvalue(int val)
{   return (val * 1024) / 1000;
}

/* Read key from beginning of line */

void rdkey(void)
{
rdl:
    rdtoken();
    if (tokentype == typeeol) goto rdl;
    if (tokentype != typename) errsyntax("key name expected");
    if (strcmp(namebuf, "Comment") == 0)
    {   skipeol();
        goto rdl;
    }
}

/* Read a name */

void rdname(void)
{   rdtoken();
    if (tokentype != typename) errsyntax("name expected");
}

/* Read a boolean */

void rdbool(void)
{   rdtoken();
    if      (tokentype == typename && strcmp(namebuf, "true") == 0)
        tokenival = 1;
    else if (tokentype == typename && strcmp(namebuf, "false") == 0)
        tokenival = 0;
    else
        errsyntax("boolean expected");
}

/* Read an integer */

void rdint(void)
{   rdtoken();
    if (tokentype != typeint) errsyntax("integer expected");
}

/* Read a number (no support for reals) */

void rdnumb(void)
{   rdtoken();
    if (tokentype != typeint) errsyntax("number expected");
}

/* Read a string */

void rdstring(void)
{   int i, ch;
    skipspace();
    i = 0;
    for (;;)
    {   ch = rdch();
        if (ch == EOF || ch == '\n') break;
        if (i >= 255) errsyntax("string too long");
        namebuf[i++] = ch;
    }
    namebuf[i] = 0;
    unrdch(ch);
}

/* Read the next token */

void rdtoken(void)
{   int val, i, j, ch;
    skipspace();
    i = 0;
    for (;;)
    {   ch = rdch();
        if (ch == EOF)
        {   if (i == 0)
            {   tokentype = typeeof;
                return;
            }
            else
                break;
        }
        if (scanmode == 0)
        {   if (ch == '%')
            {   if (i == 0)
                {   skipeol();
                    tokentype = typeeol;
                    return;
                }
                else
                    break;
            }
        }
        else
        {   if (ch == ';')
            {   if (i == 0)
                {   tokentype = typesemi;
                    return;
                }
                else
                    break;
            }
        }
        if (ch == '\n')
        {   if (i == 0)
            {   tokentype = typeeol;
                return;
            }
            else
                break;
        }
        if (ch == ' ' || ch == '\t') break;
        if (i >= 255) errsyntax("token too long");
        namebuf[i++] = ch;
    }
    namebuf[i] = 0;
    unrdch(ch);
    tokentype = typename;
    {   j = 0;
        if (namebuf[0] == '-')
        {   j++;
            if (i <= j) return;
        }
        val = 0;
        while (j < i)
        {   ch = namebuf[j];
            if (ch < '0' || ch > '9') return;
            val = val * 10 + (ch - '0');
            j++;
        }
        if (namebuf[0] == '-') val = -val;
        tokentype = typeint;
        tokenival = val;
    }
}

/* Check for semicolon */

void checksemi(void)
{   rdtoken();
    if (tokentype != typesemi) errsyntax("semicolon expected");
}

/* Check for end of line */

void checkeol(void)
{   rdtoken();
    if (tokentype != typeeol) errsyntax("end of line expected");
}

/* Skip to semicolon */

void skipsemi(void)
{   for (;;)
    {   rdtoken();
        if (tokentype == typesemi) break;
    }
}

/* Skip to end of line */

void skipeol(void)
{   int ch;
    for (;;)
    {   ch = rdch();
        if (ch == EOF)
        {   unrdch(EOF);
            break;
        }
        if (ch == '\n') break;
    }
}

/* Skip white space */

void skipspace(void)
{   int ch;
    for (;;)
    {   ch = rdch();
        if (ch != ' ' && ch != '\t') break;
    }
    unrdch(ch);
}

/* Read next character */

int rdch(void)
{   int ch;
    if (urch == NOURCH)
    {   ch = getc(currentfptr);
        if (ch == EOF)
        {   if (ferror(currentfptr)) errsyntax("file read error");
            return EOF;
        }
        if      (ch == '\n')
        {   ch = getc(currentfptr);
            if (ch != '\r' && ch != EOF) ungetc(ch, currentfptr);
            ch = '\n';
            lineno++;
        }
        else if (ch == '\r')
        {   ch = '\n';
            lineno++;
        }
    }
    else
    {   ch = urch;
        urch = NOURCH;
    }
    return ch;
}

/* Unread last character */

void unrdch(int ch)
{   urch = ch;
}

/* Syntax error */

void errsyntax(char *str)
{   fprintf(stderr, "mkmetric: syntax error in \"%s\", line %d - %s\n",
            currentfile, lineno, str);
    longjmp(errjmp, 20);
}

/* File size error */

void errfilesize(void)
{   fprintf(stderr, "mkmetric: file memory size exceeded\n");
    longjmp(errjmp, 20);
}

/* Memory allocation error */

void errmemory(void)
{   fprintf(stderr, "mkmetric: memory exhausted\n");
    longjmp(errjmp, 20);
}

/* Make a kerning pair */

void makekern(int code, int pair, int kern)
{   short *kvec;
    kvec = kerntable[code];
    if (kvec == NULL)
    {   kvec = allocmem(256 * sizeof (short));
        if (kvec == NULL) errmemory();
        kerntable[code] = kvec;
    }
    kvec[pair] = kern;
}

/* Build the kerning (sparse) array */

int buildkern(int maxlen)
{   struct mkern_entry entry;
    short *ivec;
    int i, j;
    int moff, ioff, xoff;

    if (maxlen < 512 * sizeof (struct mkern_entry)) errfilesize();

    moff = maxlen / sizeof (struct mkern_entry) - 256;
    xoff = 0;

    for (i = lochar; i <= hichar; i++)
    {   ivec = kerntable[i];
        if (ivec == NULL) continue;
        ioff = 256;
        for (;;)
        {   if (ioff >= moff) errfilesize();
            for (j = lochar; j <= hichar; j++)
            {   if (ivec[j] != 0)
                    if (kernbeg[ioff + j].mk_value != 0) goto retry;
            }
            break;

retry:      ioff++;
        }

        if (ioff > xoff) xoff = ioff;
        entry.mk_value = ioff;
        entry.mk_code = i;
        kernbeg[i] = entry;
        for (j = lochar; j <= hichar; j++)
        {   if (ivec[j] != 0)
            {   entry.mk_value = ivec[j];
                entry.mk_code = i;
                kernbeg[ioff + j] = entry;
            }
        }
    }

    return (int) ((ioff + 256) * sizeof (struct mkern_entry));
}

/* Make a string */

char *makestring(char *str)
{   char *s;
    s = allocmem(strlen(str) + 1);
    if (s == NULL) errmemory();
    strcpy(s, str);
    return s;
}

/* Allocate memory */

void *allocmem(int size)
{   char *ptr;
    int msize;
    msize = (size + 3) & ~3;
    if (msize > memfree)
    {   if (msize > memsize) return NULL;
        if (memsegs == 100) return NULL;
        ptr = AllocMem(memsize, MEMF_CLEAR);
        if (ptr == NULL) return NULL;
        membeg[memsegs++] = ptr;
        memptr = ptr;
        memfree = memsize;
    }
    ptr = memptr;
    memfree -= msize;
    memptr += msize;
    return (void *) ptr;
}

/* Free all memory */

void freeall(void)
{   while (memsegs)
    {   memsegs--;
        FreeMem(membeg[memsegs], memsize);
    }
}

/* Dummy stub routine */

void stub(void)
{   return;
}

/* Dummy check abort routine */

void chkabort(void)
{   return;
}

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