/* qdtexvpl.c -- Version 1.0 -- Copyright (c) 1992 by Eberhard Mattes */
/*
 made to work with gcc. april 30 1993 S Rahtz CERN 
*/

/* Written: ??-Apr-1990, modified 15-Feb-1992 for release */

/* Quick & dirty conversion of TeX code to VPL */

/* No warranty! */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef MSDOS
#else
#include <unistd.h>
#include <errno.h>
#endif

#define FALSE 0
#define TRUE  1

#define DVI_STACK_SIZE   50


#define DVI_set_char_0    0
#define DVI_set1        128
#define DVI_set2        129
#define DVI_set3        130
#define DVI_set4        131
#define DVI_set_rule    132
#define DVI_put1        133
#define DVI_put2        134
#define DVI_put3        135
#define DVI_put4        136
#define DVI_put_rule    137
#define DVI_nop         138
#define DVI_bop         139
#define DVI_eop         140
#define DVI_push        141
#define DVI_pop         142
#define DVI_right1      143
#define DVI_right2      144
#define DVI_right3      145
#define DVI_right4      146
#define DVI_w0          147
#define DVI_w1          148
#define DVI_w2          149
#define DVI_w3          150
#define DVI_w4          151
#define DVI_x0          152
#define DVI_x1          153
#define DVI_x2          154
#define DVI_x3          155
#define DVI_x4          156
#define DVI_down1       157
#define DVI_down2       158
#define DVI_down3       159
#define DVI_down4       160
#define DVI_y0          161
#define DVI_y1          162
#define DVI_y2          163
#define DVI_y3          164
#define DVI_y4          165
#define DVI_z0          166
#define DVI_z1          167
#define DVI_z2          168
#define DVI_z3          169
#define DVI_z4          170
#define DVI_fnt_num_0   171
#define DVI_fnt1        235
#define DVI_fnt2        236
#define DVI_fnt3        237
#define DVI_fnt4        238
#define DVI_xxx1        239
#define DVI_xxx2        240
#define DVI_xxx3        241
#define DVI_xxx4        242
#define DVI_fnt_def1    243
#define DVI_fnt_def2    244
#define DVI_fnt_def3    245
#define DVI_fnt_def4    246
#define DVI_pre         247
#define DVI_post        248
#define DVI_post_post   249

#define DVI_EOF         223
#define DVI_id_byte       2


typedef unsigned char  u_byte;
typedef signed short   s_word;
typedef unsigned short u_word;
typedef int            boolean;

struct mem
    {
    u_byte *ptr, *add;
    int size;
    int alloc;
    
    };

struct map
    {
    long code;                  /* Character code */
    long wd, ht, dp;
    struct mem repl;
    struct map *next;
    };

struct font
    {
    struct mem dvi;
    struct font *next;
    };

struct dvi_stack
    {
    long h, v, w, x, y, z;
    };

static FILE *dvi_file;
static FILE *ins_file;
static FILE *out_file;
static char *dvi_fname;
static char *ins_fname;
static char *out_fname;
static u_byte buffer[1000];     /* fnt_def: area+name */
static u_byte *dst;
static struct map *map;
static struct dvi_stack position;
static struct dvi_stack dvi_stack[DVI_STACK_SIZE];
static int dvi_sp;
static int out_sp;
static struct mem *mem;
static u_byte *hex = "0123456789abcdef";
static struct map *map_head, **map_add;
static struct font *font_head, **font_add;
static double design_size;
static boolean opt_d;
static boolean opt_i;


#if defined (MSDOS)

static char *read_binary = "rb";
static char *read_text = "rt";
static char *write_text = "wt";

#else

static char *read_binary = "r";
static char *read_text = "r";
static char *write_text = "w";

#endif


static void usage (void);
static void out_of_mem (void);
static void dvi_eof (void);
static void dvi_seek (long pos);
static s_word dvi_get1u (void);
static s_word dvi_get1s (void);
static u_word dvi_get2u (void);
static s_word dvi_get2s (void);
static long dvi_get3u (void);
static long dvi_get3s (void);
static long dvi_get4 (void);
static long dvi_get_u (s_word l);
static long dvi_get_s (s_word l);
static void internal (int err_no);
static void dvi_preamble (void);
static void dvi_push (void);
static void dvi_pop (void);
static void dvi_char (long c, boolean move);
static void dvi_rule (boolean move);
static void dvi_fnt_num (long n);
static void dvi_fnt_def (s_word b);
static void dvi_right (long n);
static void dvi_down (long n);
static void dvi_special (long n);
static void dvi_bop (void);
static void init_pos (struct dvi_stack *dst);
static void dvi_main (void);
static void init_mem (struct mem *dst);
static void add_mem (struct mem *dst, u_byte src);
static void add1u (u_byte x);
static void add4 (long x);
static void copy (void);
static void out_prop (char *txt);
static void out_end (void);
static void out_fnt_def (s_word b);
static void out_fnt_num (long n);
static void out_down (long n);
static void out_right (long n);
static void out_set_char (long n, boolean move);
static void out_set_rule (boolean move);
static void out_special (long n);
static void out_real (double x);
static void out_scaled (long n);
static void out_dec (long n);
static void out_oct (long n);
static void out_char (long n);
static void out_str (char *txt, int len);
static void output (struct mem *src);
static void make_output (void);
long parse_pt (u_byte **pp, int *nn);
int main (int argc, char *argv[]);


#define OUTPUT (map != NULL && dvi_sp > out_sp)

static void init_pos (struct dvi_stack *dst)
    {
    dst->h = 0;
    dst->v = 0;
    }


#define MEM_CHECK(n) {if (mem != NULL && (mem->size -= n) < 0) dvi_eof ();}
#define DVI_GET (mem != NULL ? *mem->ptr++ : (*dst++ = (u_byte)getc (dvi_file)))
#define DVI_EOF_CHECK {if (mem == NULL && feof (dvi_file)) dvi_eof ();}

static void dvi_eof (void)
    {
    (void)fprintf (stderr, "*** Premature end of DVI file\n");
    exit (2);
    }



static void internal (int err_no)
    {
    (void)fprintf (stderr, "*** Internal error %.4d\n", err_no);
    }


static void out_of_mem (void)
    {
    (void)fprintf (stderr, "*** Out of memory\n");
    }


static void dvi_seek (long n)
    {
    if (fseek (dvi_file, n, SEEK_SET) != 0)
        {
        (void)fprintf (stderr, "*** dvi_seek failed\n");
        exit (2);
        }
    }


/* ------------------------------------------------------------------------ */
/* Read an unsigned byte from the DVI file.                                 */
/* ------------------------------------------------------------------------ */

static s_word dvi_get1u (void)
    {
    s_word x;

    MEM_CHECK (1);
    x = DVI_GET;
    DVI_EOF_CHECK;
    return (x);
    }


/* ------------------------------------------------------------------------ */
/* Read a signed byte from the DVI file.                                    */
/* ------------------------------------------------------------------------ */

static s_word dvi_get1s (void)
    {
    s_word n;

    MEM_CHECK (1);
    n = DVI_GET;
    DVI_EOF_CHECK;
    if (n >= 128)
        return (n-256);
    else
        return (n);
    }


/* ------------------------------------------------------------------------ */
/* Read an unsigned word (2 bytes) from the DVI file.                       */
/* ------------------------------------------------------------------------ */

static u_word dvi_get2u (void)
    {
    u_word a, b;

    MEM_CHECK (2);
    a = (u_word)DVI_GET;
    b = (u_word)DVI_GET;
    DVI_EOF_CHECK;
    return (a*256+b);
    }


/* ------------------------------------------------------------------------ */
/* Read a signed word (2 bytes) from the DVI file.                          */
/* ------------------------------------------------------------------------ */

static s_word dvi_get2s (void)
    {
    s_word a, b;

    MEM_CHECK (2);
    a = DVI_GET;
    b = DVI_GET;
    DVI_EOF_CHECK;
    if (a >= 128)
        return ((a-256)*256+b);
    else
        return (a*256+b);
    }


/* ------------------------------------------------------------------------ */
/* Read an unsigned word (3 bytes) from the DVI file.                       */
/* ------------------------------------------------------------------------ */

static long dvi_get3u (void)
    {
    s_word a, b, c;

    MEM_CHECK (3);
    a = DVI_GET;
    b = DVI_GET;
    c = DVI_GET;
    DVI_EOF_CHECK;
    return (a*65536L+b*256L+c);
    }


/* ------------------------------------------------------------------------ */
/* Read a signed word (3 bytes) from the DVI file.                          */
/* ------------------------------------------------------------------------ */

static long dvi_get3s (void)
    {
    s_word a, b, c;

    MEM_CHECK (3);
    a = DVI_GET;
    b = DVI_GET;
    c = DVI_GET;
    DVI_EOF_CHECK;
    if (a >= 128)
        return ((a-256)*65536L+b*256L+c);
    else
        return (a*65536L+b*256L+c);
    }


/* ------------------------------------------------------------------------ */
/* Read a word (4 bytes) from the DVI file.                                 */
/* ------------------------------------------------------------------------ */

static long dvi_get4 (void)
    {
    s_word a, b, c, d;

    MEM_CHECK (4);
    a = DVI_GET;
    b = DVI_GET;
    c = DVI_GET;
    d = DVI_GET;
    DVI_EOF_CHECK;
    if (a >= 128)
        return ((a-256)*16777216L+b*65536L+c*256L+d);
    else
        return (a*16777216L+b*65536L+c*256L+d);
    }


/* ------------------------------------------------------------------------ */
/* Read an unsigned word (1 to 4 bytes) from the DVI file.                  */
/* ------------------------------------------------------------------------ */

static long dvi_get_u (s_word l)
    {
    switch (l)
        {
        case 0:
            return ((long)dvi_get1u ());
        case 1:
            return ((long)dvi_get2u ());
        case 2:
            return (dvi_get3u ());
        case 3:
            return (dvi_get4 ());
        default:
            internal (3002);
        }
    }


/* ------------------------------------------------------------------------ */
/* Read a signed word (1 to 4 bytes) from the DVI file.                     */
/* ------------------------------------------------------------------------ */

static long dvi_get_s (s_word l)
    {
    switch (l)
        {
        case 0:
            return ((long)dvi_get1s ());
        case 1:
            return ((long)dvi_get2s ());
        case 2:
            return (dvi_get3s ());
        case 3:
            return (dvi_get4 ());
        default:
            internal (3003);
        }
    }


static void dvi_preamble (void)
    {
    s_word b1, b2;

    dst = buffer; mem = NULL;
    b1 = dvi_get1u ();
    b2 = dvi_get1u ();
    if (b1 != DVI_pre || b2 != DVI_id_byte)
        {
        (void)fprintf (stderr, "*** The input file is not a dvi file\n");
        exit (2);
        }
    (void)dvi_get4 ();  /* num */
    (void)dvi_get4 ();  /* den */
    (void)dvi_get4 ();  /* mag */
    b1 = dvi_get1u ();
    for (b2 = 0; b2 < b1; ++b2)
        (void)dvi_get1u ();
    }


static void init_mem (struct mem *dst)
    {
    dst->size = 0; dst->alloc = 0;
    dst->ptr = NULL; dst->add = NULL;
    }


static void add_mem (struct mem *dst, u_byte src)
    {
    if (dst->size >= dst->alloc)
        {
        dst->alloc += 10;
        dst->ptr = realloc (dst->ptr, dst->alloc);
        if (dst->ptr == NULL)
            out_of_mem ();
        dst->add = dst->ptr+dst->size;
        }
    *dst->add++ = src;
    ++dst->size;
    }


static void add1u (u_byte x)
    {
    add_mem (&map->repl, x);
    }


static void add4 (long x)
    {
    add1u ((u_byte)((x >> 24) & 0xff));
    add1u ((u_byte)((x >> 16) & 0xff));
    add1u ((u_byte)((x >> 8) & 0xff));
    add1u ((u_byte)(x & 0xff));
    }


static void copy (void)
    {
    s_word i, n;

    if (OUTPUT)
        {
        n = dst-buffer;
        for (i = 0; i < n; ++i)
            add1u (buffer[i]);
        dst = buffer;
        }
    }


static void dvi_char (long c, boolean move)
    {
    if (move)
        {
        position.h += 0;          /*...to be implemented...*/
        }
    copy ();
    }


static void dvi_rule (boolean move)
    {
    long width, height;

    height = dvi_get4 ();
    width = dvi_get4 ();
    if (!OUTPUT)
        {
        fprintf (stderr, "*** Rule set outside \\texvpl macro\n");
        exit (2);
        }
    if (move)
        position.h += width;
    copy ();
    }


/* Returns scaled points */

long parse_pt (u_byte **pp, int *nn)
    {
    double x;
    u_byte *e, *p;
    char buf[20];
    int i, n;

    i = 0; p = *pp; n = *nn;
    while (n >= 1 && i < sizeof (buf)-1 && (isdigit (*p) || *p == '.'))
        {
        buf[i++] = *p++;
        --n;
        }
    buf[i] = 0;
    errno = 0;
    x = strtod (buf, &e);
    if (errno != 0 || *e != 0 || n < 2 || memcmp (p, "pt", 2) != 0)
        {
        (void)fprintf (stderr, "*** Invalid dimension in \\special{>...}\n");
        exit (2);
        }
    *pp = p+2; *nn = n-2;
    return ((long)(x*65536.0));
    }


static void dvi_special (long n)
    {
    int i;
    u_byte *p;
    long code;

    p = dst;
    if (n < 0 || (p-buffer)+n > sizeof (buffer))
        {
        (void)fprintf (stderr, "*** Invalid size of \\special\n");
        exit (2);
        }
    for (i = 0; i < (s_word)n; ++i)
        (void)dvi_get1u ();
    if (n >= 1)
        {
        if (*p == '<')
            {
            if (map == NULL && n == 2)
                code = (long)p[1];
            else if (map == NULL && n == 4 && p[1] == '^' && p[2] == '^' && p[3] < 128)
                {
                code = (long)p[3];
                if (code < 64) code += 64; else code -= 64;
                }
            else if (map == NULL && n == 5 && p[1] == '^' && p[2] == '^' &&
                     strchr (hex, p[3]) && strchr (hex, p[4]))
                {
#ifdef MSDOS
                code = 16*(strchr (hex, p[3])-hex);
                code += (strchr (hex, p[4])-hex);
#else
                code = 16* (int)(strchr (hex, p[3])-(int)hex);
                code += (strchr (hex, p[4])-(int)hex);
#endif
                }
            else
                {
                (void)fprintf (stderr, "*** Invalid \\special{<...}\n");
                exit (2);
                }
            map = (struct map *)malloc (sizeof (struct map));
            if (map == NULL) out_of_mem ();
            map->code = code;
            init_mem (&map->repl);
            map->next = NULL; *map_add = map; map_add = &map->next;
            out_sp = dvi_sp;
            return;
            }
        else if (*p == '>')
            {
            if (map == NULL || n < 1)
                {
                (void)fprintf (stderr, "*** Invalid \\special{>...}\n");
                exit (2);
                }
            ++p; i = (int)(n-1);
            map->wd = parse_pt (&p, &i);
            map->ht = parse_pt (&p, &i);
            map->dp = parse_pt (&p, &i);
            map = NULL;
            return;
            }
        }
    if (map == NULL)
        {
        (void)fprintf (stderr, "*** \\special outside \\texvpl macro\n");
        exit (2);
        }
    copy ();
    }


static void dvi_right (long n)
    {
    position.h += n;
    copy ();
    }


static void dvi_down (long n)
    {
    position.v += n;
    copy ();
    }


static void dvi_fnt_num (long n)
    {
    copy ();
    }


static void dvi_fnt_def (s_word b)
    {
    s_word area_len, name_len;
    int i;
    struct font *fp;

    (void)dvi_get_u (b-DVI_fnt_def1);        /* font number */
    (void)dvi_get4 ();                       /* check sum */
    (void)dvi_get4 ();                       /* scale factor */
    (void)dvi_get4 ();                       /* Design size */
    area_len = dvi_get1u ();
    name_len = dvi_get1u ();
    for (i = 0; i < area_len; ++i)
        (void)dvi_get1u ();
    for (i = 0; i < name_len; ++i)
        (void)dvi_get1u ();
    if (map != NULL)
        {
        fp = (struct font *)malloc (sizeof (struct font));
        if (fp == NULL) out_of_mem ();
        init_mem (&fp->dvi);
        i = dst-buffer;
        fp->dvi.ptr = (u_byte *)malloc (i);
        if (fp->dvi.ptr == NULL) out_of_mem ();
        fp->dvi.size = i; fp->dvi.alloc = i;
#ifdef MSDOS
        memmove (fp->dvi.ptr, buffer, i);
#else
        memcpy (fp->dvi.ptr, buffer, i);
#endif
        fp->next = NULL; *font_add = fp; font_add = &fp->next;
        }
    }


static void dvi_push ()
    {
    copy ();             /* Nota bene: call this before changing dvi_sp */
    if (dvi_sp >= DVI_STACK_SIZE)
        {
        (void)fprintf (stderr, "*** dvi stack overflow\n");
        exit (2);
        }
    dvi_stack[dvi_sp++] = position;
    }


static void dvi_pop ()
    {
    if (dvi_sp <= 0)
        {
        (void)fprintf (stderr, "*** dvi stack underflow\n");
        exit (2);
        }
    position = dvi_stack[--dvi_sp];
    copy ();             /* Nota bene: call this after changing dvi_sp */
    }


static void dvi_bop (void)
    {
    s_word i;

    for (i = 0; i < 10; ++i)
        (void)dvi_get4 ();           /* \count0 to \count9 */
    (void)dvi_get4 ();               /* Back pointer */
    }


static void dvi_main (void)
    {
    s_word b;

    map = NULL; dvi_sp = 0;
    map_head = NULL; map_add = &map_head;
    font_head = NULL; font_add = &font_head;
    init_pos (&position);
    position.w = 0; position.x = 0; position.y = 0; position.z = 0;
    for (;;)
        {
        dst = buffer;
        b = dvi_get1u ();
        if (b >= DVI_set_char_0 && b < DVI_set_char_0+128)
            dvi_char ((long)b-DVI_set_char_0, TRUE);
        else if (b >= DVI_fnt_num_0 && b < DVI_fnt_num_0+64)
            dvi_fnt_num ((long)(b-DVI_fnt_num_0));
        else
            switch (b)
                {
                case DVI_set1:
                case DVI_set2:
                case DVI_set3:
                case DVI_set4:
                    dvi_char (dvi_get_u (b-DVI_set1), TRUE);
                    break;
                case DVI_put1:
                case DVI_put2:
                case DVI_put3:
                case DVI_put4:
                    dvi_char (dvi_get_u (b-DVI_set1), FALSE);
                    break;
                case DVI_set_rule:
                    dvi_rule (TRUE);
                    break;
                case DVI_put_rule:
                    dvi_rule (FALSE);
                    break;
                case DVI_nop:
                    break;
                case DVI_bop:
                    dvi_bop ();
                    break;
                case DVI_eop:
                    break;
                case DVI_push:
                    dvi_push ();
                    break;
                case DVI_pop:
                    dvi_pop ();
                    break;
                case DVI_right1:
                case DVI_right2:
                case DVI_right3:
                case DVI_right4:
                    dvi_right (dvi_get_s (b-DVI_right1));
                    break;
                case DVI_down1:
                case DVI_down2:
                case DVI_down3:
                case DVI_down4:
                    dvi_down (dvi_get_s (b-DVI_down1));
                    break;
                case DVI_w0:
                    dvi_right (position.w);
                    break;
                case DVI_w1:
                case DVI_w2:
                case DVI_w3:
                case DVI_w4:
                    position.w = dvi_get_s (b-DVI_w1);
                    dvi_right (position.w);
                    break;
                case DVI_x0:
                    dvi_right (position.x);
                    break;
                case DVI_x1:
                case DVI_x2:
                case DVI_x3:
                case DVI_x4:
                    position.x = dvi_get_s (b-DVI_x1);
                    dvi_right (position.x);
                    break;
                case DVI_y0:
                    dvi_down (position.y);
                    break;
                case DVI_y1:
                case DVI_y2:
                case DVI_y3:
                case DVI_y4:
                    position.y = dvi_get_s (b-DVI_y1);
                    dvi_down (position.y);
                    break;
                case DVI_z0:
                    dvi_down (position.z);
                    break;
                case DVI_z1:
                case DVI_z2:
                case DVI_z3:
                case DVI_z4:
                    position.z = dvi_get_s (b-DVI_z1);
                    dvi_down (position.z);
                    break;
                case DVI_fnt1:
                case DVI_fnt2:
                case DVI_fnt3:
                case DVI_fnt4:
                    dvi_fnt_num (dvi_get_s (b-DVI_fnt1));
                    break;
                case DVI_xxx1:
                case DVI_xxx2:
                case DVI_xxx3:
                case DVI_xxx4:
                    dvi_special (dvi_get_u (b-DVI_xxx1));
                    break;
                case DVI_fnt_def1:
                case DVI_fnt_def2:
                case DVI_fnt_def3:
                case DVI_fnt_def4:
                    dvi_fnt_def (b);
                    break;
                case DVI_post:
                    goto done;
                default:
                    (void)fprintf (stderr, "*** Bad dvi file\n");
                    exit (2);
                }
        }
done:;
    }


static int out_level;
static boolean out_in_line;


static void out_prop (char *txt)
    {
    int i;

    if (out_in_line)
        {
        (void)fputc ('\n', out_file);
        out_in_line = FALSE;
        }
    for (i = 0; i < out_level; ++i)
        (void)fputs ("    ", out_file);
    (void)fputc ('(', out_file);
    (void)fputs (txt, out_file);
    ++out_level;
    out_in_line = TRUE;
    }


static void out_end (void)
    {
    (void)fputc (')', out_file);
    --out_level;
    }


static void out_real (double x)
    {
    (void)fprintf (out_file, " R %lf", x);
    }


static void out_scaled (long n)
    {
    out_real ((double)n/65536.0/design_size);
    }


static void out_char (long n)
    {
    if (n >= 0 && n <= 127 && isalnum ((int)n))
        (void)fprintf (out_file, " C %c", (char)n);
    else
        (void)fprintf (out_file, " O %lo", n);
    }


static void out_dec (long n)
    {
    (void)fprintf (out_file, " D %ld", n);
    }


static void out_oct (long n)
    {
    (void)fprintf (out_file, " O %lo", n);
    }



static void out_str (char *txt, int len)
    {
    int i, level;
    u_byte c;

    level = 0;
    (void)fputc (' ', out_file);
    for (i = 0; i < len; ++i)
        {
        c = txt[i];
        if (c == '(') ++level;
        else if (c == ')') --level;
        if (level < 0)
            {
            fprintf (stderr, "*** Unbalanced parentheses in string\n");
            exit (2);
            }
        (void)fputc (c, out_file);
        }
    if (level != 0)
        {
        fprintf (stderr, "*** Unbalanced parentheses in string\n");
        exit (2);
        }
    }


static void out_down (long n)
    {
    out_prop ("MOVEDOWN");
    out_scaled (n);
    out_end ();
    }


static void out_right (long n)
    {
    out_prop ("MOVERIGHT");
    out_scaled (n);
    out_end ();
    }


static void out_set_char (long n, boolean move)
    {
    if (!move)
        {
        out_prop ("PUSH"); out_end ();
        }
    out_prop ("SETCHAR");
    out_char (n);
    out_end ();
    if (!move)
        {
        out_prop ("POP"); out_end ();
        }
    }


static void out_set_rule (boolean move)
    {
    long n1, n2;        

    n1 = dvi_get4 ();
    n2 = dvi_get4 ();
    if (!move)
        {
        out_prop ("PUSH"); out_end ();
        }
    out_prop ("SETRULE");
    out_scaled (n1); out_scaled (n2);
    out_end ();
    if (!move)
        {
        out_prop ("PUSH"); out_end ();
        }
    }


static void out_fnt_def (s_word b)
    {
    long font_number, check_sum, dsize;
    s_word area_len, name_len;
    u_byte name[256], area[256];
    s_word i;

    font_number = dvi_get_u (b-DVI_fnt_def1);
    check_sum = dvi_get4 ();
    (void)dvi_get4 ();                       /* scale factor */
    dsize = dvi_get4 ();
    area_len = dvi_get1u ();
    name_len = dvi_get1u ();
    for (i = 0; i < area_len; ++i)
        area[i] = (u_byte)dvi_get1u ();
    for (i = 0; i < name_len; ++i)
        name[i] = (u_byte)dvi_get1u ();
    out_prop ("MAPFONT");
    out_dec (font_number);
    out_prop ("FONTNAME"); out_str (name, name_len); out_end ();
    if (area_len != 0)
        {
        out_prop ("FONTAREA"); out_str (area, area_len); out_end ();
        }
    if (check_sum != 0)
        {
        out_prop ("FONTCHECKSUM"); out_oct (check_sum); out_end ();
        }
    out_prop ("FONTDSIZE");
    out_real ((double)dsize/(double)0x10000);
    out_end ();
    out_end ();
    }


static void out_fnt_num (long n)
    {
    out_prop ("SELECTFONT");
    out_dec (n);
    out_end ();
    }


static void out_special (long n)
    {
    out_prop ("SPECIAL");
    out_str (mem->ptr, (int)n);
    out_end ();
    mem->ptr += n; mem->size -= n;
    }


static void output (struct mem *src)
    {
    s_word b;

    mem = src; dst = NULL;
    while (mem->size > 0)
        {
        b = dvi_get1u ();
        if (b >= DVI_set_char_0 && b < DVI_set_char_0+128)
            out_set_char ((long)b, TRUE);
        else if (b >= DVI_fnt_num_0 && b < DVI_fnt_num_0+64)
            out_fnt_num ((long)(b-DVI_fnt_num_0));
        else
            switch (b)
                {
                case DVI_set1:
                case DVI_set2:
                case DVI_set3:
                case DVI_set4:
                    out_set_char (dvi_get_u (b-DVI_set1), TRUE);
                    break;
                case DVI_put1:
                case DVI_put2:
                case DVI_put3:
                case DVI_put4:
                    out_set_char (dvi_get_u (b-DVI_put1), FALSE);
                    break;
                case DVI_set_rule:
                    out_set_rule (TRUE);
                    break;
                case DVI_put_rule:
                    out_set_rule (FALSE);
                    break;
                case DVI_push:
                    out_prop ("PUSH"); out_end ();
                    break;
                case DVI_pop:
                    out_prop ("POP"); out_end ();
                    break;
                case DVI_right1:
                case DVI_right2:
                case DVI_right3:
                case DVI_right4:
                    out_right (dvi_get_s (b-DVI_right1));
                    break;
                case DVI_down1:
                case DVI_down2:
                case DVI_down3:
                case DVI_down4:
                    out_down (dvi_get_s (b-DVI_down1));
                    break;
                case DVI_w0:
                    out_right (position.w);
                    break;
                case DVI_w1:
                case DVI_w2:
                case DVI_w3:
                case DVI_w4:
                    position.w = dvi_get_s (b-DVI_w1);
                    out_right (position.w);
                    break;
                case DVI_x0:
                    out_right (position.x);
                    break;
                case DVI_x1:
                case DVI_x2:
                case DVI_x3:
                case DVI_x4:
                    position.x = dvi_get_s (b-DVI_x1);
                    out_right (position.x);
                    break;
                case DVI_y0:
                    out_down (position.y);
                    break;
                case DVI_y1:
                case DVI_y2:
                case DVI_y3:
                case DVI_y4:
                    position.y = dvi_get_s (b-DVI_y1);
                    out_down (position.y);
                    break;
                case DVI_z0:
                    out_down (position.z);
                    break;
                case DVI_z1:
                case DVI_z2:
                case DVI_z3:
                case DVI_z4:
                    position.z = dvi_get_s (b-DVI_z1);
                    out_down (position.z);
                    break;
                case DVI_fnt1:
                case DVI_fnt2:
                case DVI_fnt3:
                case DVI_fnt4:
                    out_fnt_num (dvi_get_s (b-DVI_fnt1));
                    break;
                case DVI_xxx1:
                case DVI_xxx2:
                case DVI_xxx3:
                case DVI_xxx4:
                    out_special (dvi_get_u (b-DVI_xxx1));
                    break;
                case DVI_fnt_def1:
                case DVI_fnt_def2:
                case DVI_fnt_def3:
                case DVI_fnt_def4:
                    out_fnt_def (b);
                    break;
                default:
                    internal (3043);
                    exit (2);
                }
        }
    mem = NULL;
    }



static void make_output (void)
    {
    struct map *mp;
    struct font *fp;
    char line[256];

    out_in_line = FALSE;
    out_level = 0;
    for (fp = font_head; fp != NULL; fp = fp->next)
        {
        output (&fp->dvi);
        }
    if (opt_i)
        {
        while (fgets (line, sizeof (line), ins_file) != NULL)
            fputs (line, out_file);
        fclose (ins_file);
        opt_i = FALSE;
        }
    for (mp = map_head; mp != NULL; mp = mp->next)
        {
        out_prop ("CHARACTER");
        out_char (mp->code);
        out_prop ("CHARWD"); out_scaled (mp->wd); out_end ();
        out_prop ("CHARHT"); out_scaled (mp->ht); out_end ();
        out_prop ("CHARDP"); out_scaled (mp->dp); out_end ();
        out_prop ("MAP");
        output (&mp->repl);
        out_end ();
        out_end ();
        }
    if (out_in_line)
        (void)fputc ('\n', out_file);
    }


static void usage (void)
    {
    (void)fputs ("Usage:\n", stderr);
    (void)fputs ("  qdtexvpl -d<design_size> [-i<insert.pl>] <input.dvi> <output.vpl>\n",
                 stderr);
    exit (1);
    }


int main (int argc, char *argv[])
    {
    int i;
    char *p;

    opt_d = FALSE; opt_i = FALSE;
    ins_fname = NULL;
    for (i = 1; i < argc; ++i)
        if (argv[i][0] == '-')
            switch (argv[i][1])
                {
                case 'd':
                    opt_d = TRUE;
                    errno = 0;
                    design_size = strtod (argv[i]+2, &p);
                    if (errno != 0 || design_size <= 0.0 || *p != 0)
                        usage ();
                    break;
                case 'i':
                    opt_i = TRUE;
                    ins_fname = argv[i]+2;
                    break;
                default:
                    usage ();
                }
        else
            break;
    if (!opt_d) usage ();
    if (argc - i != 2) usage ();
    dvi_fname = argv[i+0];
    out_fname = argv[i+1];
    dvi_file = fopen (dvi_fname, read_binary);
    if (dvi_file == NULL)
        {
        (void)fprintf (stderr, "*** Cannot open input file '%s'\n", dvi_fname);
        exit (1);
        }
    if (opt_i)
        {
        ins_file = fopen (ins_fname, read_text);
        if (dvi_file == NULL)
            {
            (void)fprintf (stderr, "*** Cannot open -i file '%s'\n", ins_fname);
            exit (1);
            }
        }
    out_file = fopen (out_fname, write_text);
    if (out_file == NULL)
        {
        (void)fprintf (stderr, "*** Cannot open output file '%s'\n", out_fname);
        exit (1);
        }
    dvi_preamble ();
    dvi_main ();
    make_output ();
    (void)fflush (out_file);
    if (ferror (out_file))
        {
        (void)fprintf (stderr, "*** Cannot write to output file '%s'\n",
                       out_fname);
        exit (1);
        }
    (void)fclose (out_file);
    return (0);
    }
