/*****************************************************************************/
/*                                                                           */
/*   dvimfj  ---  generates an MFJOB program from a .dvi file.               */
/*                                                                           */
/*****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include "commands.h"
#if defined(MSDOS)
        #include <stdlib.h>
        #include <string.h>
        #include <math.h>
        #include <process.h>
#endif

#define LASTCHAR        127    /* max dvi character, above are commands    */

#define get1()           num(1)
#define get2()           num(2)
#define get3()           num(3)
#define get4()           num(4)
#define sget1()         snum(1)
#define sget2()         snum(2)
#define sget3()         snum(3)
#define sget4()         snum(4)

typedef struct{
    char *name;
    char *mag;
    int  base;
    int  mode;
} FONTDEF;

typedef struct{
    char *pattern;
    int  base;
    int  mode;
} FONTPAT;

#define MaxBAidx  10      /* maximum number of bases (plain, cm, etc.) */
#define MaxMOidx  10      /* maximum number of modes (lj, fx, etc.) */

int FDsize, FDidx; FONTDEF **Fontdefs;
int FPsize, FPidx; FONTPAT **FPatterns;
int BAidx;         char *BAses[MaxBAidx];
int MOidx;         char *MOdes[MaxMOidx];
int Nomfj;

char *Inputmodes, *MFjobcmd, *Outname="dvimfj.mfj";

FILE * dvifp;
char * dvi_name;
long   pc;

long Numerator, Denominator, Magnification;
double Scale = 1;

char *          realloc          ();

void            main             ();
void            read_dvi_file    ();
void            bop              ();
void            preamble         ();
void            postamble        ();
void            postpostamble    ();
void            fontdef          ();
void            special          ();
unsigned long   num              ();
long            snum             ();
void            addfontdef       ();
void *          Realloc          ();
char *          Strdup           ();
void            read_config_file ();
void            read_config_80   ();
void            addpatterndef    ();
void            write_mfj        ();
void            run_mfjob        ();




/*---------------------------------------------------------------------------*/

void main(argc, argv)
int argc;
char **argv;
{
    register int i, jx;
    long scale;

    if (argc < 2) {
        usage:
        printf("\nUsage: %s dvifile [magstep] [no]\n", *argv);
        printf("\tFonts scaled (1.2^magstep) or (magstep/1000).\n"
               "\tNo MFjob spawn.\n");
        exit(1);
    }

    else{
        if ((i = strlen(argv[1])) == 0) {
            printf("Illegal empty filename\n");
            exit(2);
        }
        if ((i >= 5) && !strcmpi(argv[1]+i-4, ".dvi")) dvi_name = argv[1];
        else {
            dvi_name = Realloc(0, (i+5) * sizeof(char));
            strcpy(dvi_name, argv[1]);
            strcat(dvi_name, ".dvi");
        }
        if ((dvifp = fopen(dvi_name, "rb")) == NULL) {
            printf("Cant open %s\n", dvi_name);
            exit(3);
        }
        if (argc > 2){
            for (jx = 2; jx < argc; jx++){
                if ('N' == toupper(argv[jx][0])){
                    Nomfj = 1;
                    continue;
                }
                scale = 0;
                if (1 > sscanf(argv[jx], " %ld", &scale)) goto usage;
                if (scale > 30) Scale = scale/1000.f;
                else            Scale = pow((double)1.2, (double)scale);
            }
        }
    }

    read_config_file(argv[0]);
    read_dvi_file();
    write_mfj();
    run_mfjob();
    exit(0);

} /* main */


void read_dvi_file()
{
    int opcode;

    while ((opcode = (int) get1()) != EOF) {    /* process until end of file */
        if ((opcode <= LASTCHAR) && isprint(opcode)) {
            while ((opcode <= LASTCHAR) && isprint(opcode)) {
                opcode = (int) get1();
            }
        }

        if (opcode <= LASTCHAR) ;
        else if ((opcode >= FONT_00) && (opcode <= FONT_63)) ;
        else switch (opcode) {
            case SET1     :
            case SET2     :
            case SET3     :
            case SET4     : num(opcode - SET1 + 1);         break;
            case SET_RULE : sget4(); sget4();               break;
            case PUT1     :
            case PUT2     :
            case PUT3     :
            case PUT4     : num(opcode - PUT1 + 1);         break;
            case PUT_RULE : sget4(); sget4();
            case NOP      : break;
            case BOP      : bop();                          break;
            case EOP      :
            case PUSH     :
            case POP      : break;
            case RIGHT1   :
            case RIGHT2   :
            case RIGHT3   :
            case RIGHT4   : snum(opcode - RIGHT1 + 1);
            case W0       : break;
            case W1       :
            case W2       :
            case W3       :
            case W4       : snum(opcode - W0);
            case X0       : break;
            case X1       :
            case X2       :
            case X3       :
            case X4       : snum(opcode - X0);              break;
            case DOWN1    :
            case DOWN2    :
            case DOWN3    :
            case DOWN4    : snum(opcode - DOWN1 + 1);
            case Y0       : break;
            case Y1       :
            case Y2       :
            case Y3       :
            case Y4       : snum(opcode - Y0);
            case Z0       : break;
            case Z1       :
            case Z2       :
            case Z3       :
            case Z4       : snum(opcode - Z0);              break;
            case FNT1     :
            case FNT2     :
            case FNT3     :
            case FNT4     : num(opcode - FNT1 + 1);         break;
            case XXX1     :
            case XXX2     :
            case XXX3     :
            case XXX4     : special(opcode - XXX1 + 1);     break;
            case FNT_DEF1 :
            case FNT_DEF2 :
            case FNT_DEF3 :
            case FNT_DEF4 : fontdef(opcode - FNT_DEF1 + 1); break;
            case PRE      : preamble();                     break;
            case POST     : postamble();                    break;
            case POST_POST: postpostamble();                break;
        }
    }

} /* read_dvi_file */


/*----------------------------------------------------------------------------*/


void bop()
{
    int i;

    for (i=0; i < 11; i++) get4();

} /* bop */


/*---------------------------------------------------------------------------*/

void postamble()
{
    sget4();
    if (Numerator     != get4() ||
        Denominator   != get4() ||
        Magnification != get4()){

        printf("Postamble does not match preamble\n");
        exit(3);
    }
    get4();
    get4();
    get2();
    get2();

} /* postamble */

void preamble()
{
    long ix;

    get1();
    Numerator     = get4();
    Denominator   = get4();
    Magnification = get4();
    ix = (long)(Scale*1000);    /* truncate at third decimal place */
    Scale = ((double)ix)/1000;
    printf("\nmagnification: %ld   scale: %05.3f\n\n", Magnification, Scale);
    get1();

} /* preamble */


void postpostamble()
{
    register int i;

    get4();
    get1();
    while ((i = (int) get1()) == TRAILER) ;
    while (i != EOF) {
        printf("%06ld: ", pc - 1);
        printf("BAD DVI FILE END: 0x%02X\n", i);
        i = (int) get1();
    }

} /* postpostamble */



void special(x)
register int x;
{
    register long len;
    register long i;

    len = num(x);
    for (i = 0; i < len; i++) get1();

} /* special */



void fontdef(x)
register int x;
{
    register int ix, jx;
    long scale, design;
    double mag;
    char fontname[10], fontmag[10];

    num(x);
    get4();
    scale = get4();
    design = get4();
    if (0==design) mag = -1;
    else{
        mag = (double)scale/(double)design;
        mag = Scale*Magnification*mag/1000;
    }
    sprintf(fontmag, "%07.3f", mag);
    ix = (int) get1() + (int) get1();

#if defined(MSDOS)
    #define LIMIT8   && jx < 8
#else
    #define LIMIT8
#endif

    for (jx=0; jx < ix LIMIT8; jx++) fontname[jx] = (char) get1();
    fontname[jx] = 0;
    for (; jx < ix; jx++) get1();
    addfontdef(fontname, fontmag);

} /* fontdef */



unsigned long num(size)
register int size;
{
    register int i;
    register long x = 0;

    pc += size;
    for (i = 0; i < size; i++)
        x = (x << 8) + (unsigned) getc(dvifp);
    return x;

} /* num */



long snum(size)
register int size;
{
    register int i;
    register long x = 0;

    pc += size;
    x = getc(dvifp);
    if (x & 0x80)
        x -= 0x100;
    for (i = 1; i < size; i++)
        x = (x << 8) + (unsigned) getc(dvifp);
    return x;

} /* snum */

void addfontdef(name, mag)
char *name, *mag;
{
    int ix, jx, found, base, mode;
    char *pat;

    if (FDsize < 2+FDidx){
        FDsize = 30+FDidx;
        Fontdefs = Realloc(Fontdefs, FDsize * sizeof Fontdefs[0]);
    }
    for (ix=0; ix < FDidx; ix++){
        if (!strcmp(Fontdefs[ix]->name, name) &&
            !strcmp(Fontdefs[ix]->mag, mag)){
            return;
        }
    }
    for (found=ix=0; ix < FPidx; ix++){
        pat = FPatterns[ix]->pattern;
        for (jx=0; pat[jx] && pat[jx]==name[jx]; jx++);
        if (pat[jx]=='*' || !strcmp(pat, name)){
            base = FPatterns[ix]->base;
            mode = FPatterns[ix]->mode;
            found = 1;
            break;
        }
    }
    if (!found){
        printf("Font %s not found in .CFG file\n", name);
        exit(3);
    }
    Fontdefs[FDidx] = Realloc(NULL, sizeof(FONTDEF));
    Fontdefs[FDidx]->name = Strdup(name);
    Fontdefs[FDidx]->mag  = Strdup(mag);
    Fontdefs[FDidx]->base = base;
    Fontdefs[FDidx]->mode = mode;
    FDidx++;
    printf("font: %-8s mag: %s  base: %-8s mode: %s\n",
     name, mag, BAses[base], MOdes[mode]);

} /* addfontdef */

void *Realloc(ptr, size)
void *ptr;
int size;
{
    void *outpntr;

    if (size < 0) exit(11);
    outpntr = realloc(ptr, size);
    if (0==outpntr){
        printf("Memory error\n");
        exit(4);
    }
    return outpntr;

} /* Realloc */

char *Strdup(str)
char *str;
{
    char *newstr;

    newstr = strdup(str);
    if (0==newstr){
        printf("Strdup error\n");
        exit(4);
    }
    return newstr;

} /* Strdup */


char Buf80[81];
FILE *Cfp;

void read_config_file(name)
char *name;
{
int ix, sl;

    sl = strlen(name);  if (sl >= (80-5)) exit(10);  strcpy(Buf80, name);
    for (ix = sl-1; ix > 0 && ix > sl-5; ix--){
        if (Buf80[ix]=='.'){
            strcpy(Buf80+ix+1, "CFG");
            Cfp = fopen(Buf80, "r");  if (0==Cfp){
                printf("Can't open %s\n", Buf80);
                exit(3);
            }
            read_config_80(); MFjobcmd   = Strdup(Buf80);
            read_config_80(); Inputmodes = Strdup(Buf80);
            for (;;){
                read_config_80();

                if (!strcmp(Buf80, "*end")){
                    fclose(Cfp);
                    return;
                }
                if (!strcmp(Buf80, "*base")){
                    read_config_80();
                    BAses[BAidx++] = Strdup(Buf80);
                    if (BAidx >= MaxBAidx){
                        printf("Too many bases\n");
                        exit(3);
                    }
                    continue;
                }
                if (!strcmp(Buf80, "*mode")){
                    read_config_80();
                    MOdes[MOidx++] = Strdup(Buf80);
                    if (MOidx >= MaxMOidx){
                        printf("Too many modes\n");
                        exit(3);
                    }
                    continue;
                }
                if (!BAidx){
                    printf("Missing *base command\n");
                    exit(3);
                }
                if (!MOidx){
                    printf("Missing *mode command\n");
                    exit(3);
                }
                addpatterndef(Buf80, BAidx-1, MOidx-1);
            }
        }
    }
    printf("Missing *end command\n");
    exit(3);

} /* read_config_file */

void read_config_80()
{
    if (1==fscanf(Cfp, " %80s", Buf80)) return;
    printf("Can't read config file\n");
    exit(3);
}

void addpatterndef(pattern, base, mode)
char *pattern;
int base, mode;
{
    if (FPsize < 2+FPidx){
        FPsize = 10+FPidx;
        FPatterns = Realloc(FPatterns, FPsize * sizeof FPatterns[0]);
    }
    FPatterns[FPidx] = Realloc(NULL, sizeof(FONTDEF));
    FPatterns[FPidx]->pattern = Strdup(pattern);
    FPatterns[FPidx]->base    = base;
    FPatterns[FPidx]->mode    = mode;
    FPidx++;

} /* addpatterndef */

void write_mfj()
{
    int ix, jx, mx, nx, flag;
    FILE *fp;

    fp = fopen(Outname, "w");
    if (0==fp){
        printf("Can't create %s\n", Outname);
        exit(3);
    }
    fprintf(fp, "%s\n", Inputmodes);
    for (mx=0; mx < MOidx; mx++){
        for (jx=0; jx < BAidx; jx++){
            flag = 0;
            for (ix=0; ix < FDidx; ix++){
                if (mx != Fontdefs[ix]->mode) continue;
                if (jx != Fontdefs[ix]->base) continue;
                if (!flag){
                    fprintf(fp, "{\n  base=%s;\n  %s;\n",
                     BAses[jx], MOdes[mx]);
                    flag = 1;
                }
                nx = fprintf(fp, "  { font=%s;", Fontdefs[ix]->name);
                fprintf(fp, "%*smag=%s; }\n", 21-nx, " ", Fontdefs[ix]->mag);
            }
            if (flag) fprintf(fp, "}\n");
        }
    }
    fclose(fp);

} /* write_mfj */

void run_mfjob()
{
    int retcode;

    if (Nomfj) return;
    printf("\nSpawning %s %s\n\n", MFjobcmd, Outname);
    retcode = spawnl(P_WAIT, MFjobcmd, MFjobcmd, Outname, NULL);
    if (0==retcode){
       printf("\nDeleting %s\n", Outname);
       unlink(Outname);
    }

} /* run_mfjob */



/*


================================================================================
==                          DVI file format                                   ==
================================================================================
no_ops          >= 0 bytes     (NOP, nops before the preamble)
preamble_marker    1 ubyte     (PRE)
version_id         1 ubyte     (should be version 2)
numerator          4 ubytes    (numerater must equal the one in postamble)
denominator        4 ubytes    (denominator must equal the one in postamble)
magnification      4 ubytes    (magnification must equal the one in postamble)
id_len             1 ubyte     (lenght of identification string)
id_string     id_len ubytes    (identification string)

no_ops          >= 0 bytes     (NOP, nops before a page)
begin_of_page      1 ubyte     (BOP)
page_nr            4 sbytes    (page number)
do_be_do          36 bytes     (filler ????)
prev_page_offset   4 sbytes    (offset in file where previous page starts, -1 for none)
... PAGE DATA ...
end_of_page        1 ubyte     (EOP)

no_ops ???      >= 0 bytes     (NOPS, I think they are allowed here...)
postamble_marker   1 ubyte     (POST)
last_page_offset   4 sbytes    (offset in file where last page starts)
numerator          4 ubytes    (numerater must equal the one in preamble)
denominator        4 ubytes    (denominator must equal the one in preamble)
magnification      4 ubytes    (magnification must equal the one in preamble)
max_page_height    4 ubytes    (maximum page height)
max_page_width     4 ubytes    (maximum page width)
max_stack          2 ubytes    (maximum stack depth needed)
total_pages        2 ubytes    (number of pages in file)
... FONT DEFINITIONS ...

postamble_offset   4 sbytes    (offset in file where postamble starts)
version_id         1 ubyte     (should be version 2)
trailer         >= 4 ubytes    (TRAILER)
<EOF>


FONT DEFINITIONS:
   do {
      switch (c = getc(dvi_fp) {
          case FNTDEF1  :
          case FNTDEF2  :
          case FNTDEF3  :
          case FNTDEF4  :  define_font(c);
          case POSTPOST : break;
          default       : error;
      }
   } while (c != POSTPOST);

===== A font def looks like:

1,2,3 or 4 ubytes TeXfontnumber for FNTDEF1 .. FNTDEF4
4 ubytes checksum
4 ubytes scale
4 ubytes design size
1 byte deflen1
1 byte deflen2
deflen1 + deflen2 bytes fontname.

===== A special looks like:

1,2,3 or 4 ubytes telling length of special command for XXX1 .. XXX4
all bytes in the special command are used as defined in the dvi driver.


*/
