/* CGFont .atc file builder for PPage etc.  File "mkatc.c".
 * (C) Adrian Aylward 1991
 *
 * You may freely copy, use, and modify this file.
 *
 * This program prints builds a CGFont encoding (.atc) file in the format
 * used by Professional Page from a PostScript style encoding table.
 *
 * 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 <proto/dos.h>
# include <proto/exec.h>
# include <string.h>
# include <setjmp.h>
# include <stdio.h>

/* Atc file format */

struct atcfile
{   int af_uid;
    unsigned short af_code[256];
};

/* External data (initialised to zero) */

char *argatcfile;
char *argencodingfile, *argcharcodefile;

int optread, optverify, optuid;
int uid;
int lochar, hichar;

BPTR newfh;

int retcode;

# define memsize 10000

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

struct atcmem
{   struct atcfile file;
    int dummy;
};

struct atcmem atcmem;

struct charentry
{   short code;
    char *str;
};

# define charcodesize 2000

struct charentry charcode[charcodesize];

int charcodelen;

char *currentfile;
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];

/* 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 readcharcode(void);
extern void rdtoken(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 errmemory(void);
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;
    int *ip, i, j, 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 'C': case 'c':
                    if (argc == 0) goto badargs;
                    argcharcodefile = *argv++;
                    argc--;
                    continue;

                case 'U': case 'u':
                    optuid = 1;
                    ip = &uid;
                    break;

                case 'R': case 'r':
                    optread = 1;
                    continue;

                case 'v':
                    optverify = 1;
                    continue;

                case 'V':
                    optverify = 2;
                    continue;

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

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

                default:
                    fprintf(stderr, "mkatc: 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, "mkatc: LoChar/HiChar out of range "
                                "(0 <= Lo <= Hi <= 255)\n");
        goto errorexit;
    }

    if (argc != 1) goto badargs;
    argatcfile = argv[0];

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

    /* Read the encoding file */

    if (optread) goto skipenc;
    currentfile = argencodingfile;
    currentfptr = fopen(currentfile, "r");
    if (currentfptr == NULL)
    {   fprintf(stderr, "mkatc: can't open encoding file %s\n", currentfile);
        goto errorexit;
    }
    readencoding();
    if (retcode != 0) goto errorexit;
    fclose(currentfptr);
    currentfptr = NULL;

    /* Read the charcode file */

skipenc:
    currentfile = argcharcodefile;
    currentfptr = fopen(currentfile, "r");
    if (currentfptr == NULL)
    {   fprintf(stderr, "mkatc: can't open charcode file %s\n", currentfile);
        goto errorexit;
    }
    readcharcode();
    if (retcode != 0) goto errorexit;
    fclose(currentfptr);
    currentfptr = NULL;

    /* Read the old .atc file */

    if (optread || !optuid)
    {   newfh = Open(argatcfile, MODE_OLDFILE);
        if (newfh == NULL)
        {   fprintf(stderr, "mkatc: can't open atc file %s\n",
                    argatcfile);
            goto errorexit;
        }
        i = Read(newfh, (UBYTE *) &atcmem, sizeof (atcmem));
        Close(newfh);
        if (i == -1)
        {   fprintf(stderr, "mkatc: error reading atc file %s\n",
                    argatcfile);
            goto errorexit;
        }
        if (i != sizeof (struct atcfile))
        {   fprintf(stderr, "mkatc: atc file %s format error\n",
                    argatcfile);
            goto errorexit;
        }
    }

    /* Update the uid */

    if (optuid) atcmem.file.af_uid = uid;
    if (optverify)
        fprintf(stdout, "% file \"%s\" uid = %d\n",
                        argatcfile, atcmem.file.af_uid);

    /* If reading, we look up the codes in the charcodes */

    if (optread)
    {   for (i = 0; i < 256; i++)
        {   ch = atcmem.file.af_code[i];
            if (ch != 0)
            {   for (j = 0; j < charcodelen; j++)
                    if (charcode[j].code == ch)
                    {   s = charcode[j].str;
                        goto gotit1;
                    }
                    fprintf(stderr, "mkatc: character %4d "
                                    "not in charcodes, name unknown\n", ch);
                    retcode = 5;
                    s = "NULL";
gotit1:             if (optverify == 1)
                        fprintf(stdout, "%3d /%-20s %% %4d\n", i, s, ch);
                    if (optverify == 2)
                        fprintf(stdout, "  %-20s %3d %4d\n", s, i, ch);
            }
        }
    }

    /* If writing, we look up the names in the charcodes */

    else
    {   for (i = 0; i < 256; i++)
        {   ch = 0;
            if (i >= lochar && i <= hichar)
            {   s = encoding[i];
                if (s != NULL)
                {   for (j = 0; j < charcodelen; j++)
                        if (strcmp(charcode[j].str, s) == 0)
                        {   ch = charcode[j].code;
                            if (ch == 0) goto skipv2;
                            goto gotit2;
                        }
                    fprintf(stderr, "mkatc: character \"%s\" "
                                    "not in charcodes, omitted\n", s);
                    retcode = 5;

gotit2:             if (optverify == 1)
                        fprintf(stdout, "%3d /%-20s %% %4d\n", i, s, ch);
                    if (optverify == 2)
                        fprintf(stdout, "  %-20s %3d %4d\n", s, i, ch);
                }
            }
skipv2:     atcmem.file.af_code[i] = ch;
        }
    }

    /* Write out the new .atc file */

    if (!optread)
    {   newfh = Open(argatcfile, MODE_NEWFILE);
        if (newfh == NULL)
        {   fprintf(stderr, "mkatc: can't open atc file %s\n",
                    argatcfile);
            goto errorexit;
        }
        i = Write(newfh, (UBYTE *) &atcmem, sizeof (struct atcfile));
        Close(newfh);
        if (i != sizeof (struct atcfile))
        {   fprintf(stderr, "mkatc: error writing atc file %s\n",
                    argatcfile);
            goto errorexit;
        }
    }

    goto tidyexit;

    /* Argument errors and usage query */

query:
    fprintf(stderr, "Atc file builder.  MkAtc version 1.0\n"
                    "Makes CGFont .atc files from the encoding file\n"
                    "\n"
                    "  Usage:\n"
                    "\n"
                    "    mkatc -options atcfile\n"
                    "\n"
                    "      -e encodingfile  Encoding file name\n"
                    "      -c charcodesfile Character codes file name\n"
                    "      -lnnn            LoChar\n"
                    "      -hnnn            Hichar\n"
                    "      -unnn            Unique id\n"
                    "      -r               Read .atc file, do not update\n"
                    "      -v               Verify encoding table\n"
                    "      -V               Verify encoding, "
                                           "alternative format\n"
                    "\n"
                    "  For example:\n"
                    "\n"
                    "    mkatc cgfonts:Times.atc\n");
    goto tidyexit;

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

    /* Tidy up and exit */

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

errorexit:
    retcode = 20;

tidyexit:
    if (currentfptr) fclose(currentfptr);
    currentfptr = NULL;
    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)
{   char *str;
    int ch;

    lineno = 1;
    urch = NOURCH;

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

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

/* Read the charcode file */

void readcharcode(void)
{   char *str;
    int ch;

    lineno = 1;
    urch = NOURCH;

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

    for (;;)
    {   rdtoken();
        if (tokentype == typeeof) break;
        if (tokentype == typeeol) continue;
        if (tokentype != typeint) errsyntax("integer expected");
        ch = tokenival;
        rdtoken();
        if (tokentype != typename) errsyntax("name expected");
        if (namebuf[0] == '/')
           str = makestring(&namebuf[1]);
        else
           str = makestring(&namebuf[0]);
        rdtoken();
        if (tokentype != typeeol) errsyntax("end of line expected");
        if (ch < 0 || ch > 65535)
            errsyntax("character code out of range");
        if (charcodelen >= charcodesize)
            errsyntax("too many characters");
        charcode[charcodelen].code = ch;
        charcode[charcodelen].str = str;
        charcodelen++;
    }
}

/* 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 (ch == '%')
        {   if (i == 0)
            {   skipeol();
                tokentype = typeeol;
                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;
    }
}

/* 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, "mkatc: syntax error in \"%s\", line %d - %s\n",
            currentfile, lineno, str);
    longjmp(errjmp, 20);
}

/* Memory allocation error */

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

/* 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 "mkatc.c" */
