/*****************************************************************************/
/*                                                                           */
/*   disdvi  ---  disassembles TeX dvi files.                                */
/*                                                                           */
/*                                                                           */
/*   2.26 23oct2010  TexLivel    support pTex and XeTex                      */
/*   2.25 23jan2003  M.J.E. Mol  fixed bug in copying font name (name[i])    */
/*   2.24 27may96 M.J.E. Mol     A few typecasts added                       */
/*   2.23 23jul95 M.J.E. Mol     Cleanups from [email protected]   */
/*   2.22 13dec90 M.J.E. Mol     Fixed bug in num(). Cleaned up code.        */
/*   2.21 03may90 M.J.E. Mol     Created usage().                            */
/*    2.2 02may90 M.J.E. Mol     Included port to VAX/VMS VAXC by            */
/*                               Robert Schneider.                           */
/*    2.1 19jan90 M.J.E. Mol     Maintain a list of fonts and                */
/*                               show fontnames in font changes.             */
/*                               Show character code when printing ligatures */
/*    2.0 23jan89 M.J.E. Mol (c) 1989                                        */
/*                                               [email protected]              */
/*                                                                           */
/*****************************************************************************/

/*
* dvi2tty
* Copyright (C) 2003 Marcel J.E. Mol <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*/

const char *disdvi = "@(#) disdvi.c  2.26 20101027 M.J.E. Mol (c) 1989-2010, [email protected]";

/*
* Include files
*/

#if defined(KPATHSEA)
# define NO_DEBUG 1
# include <kpathsea/config.h>
# include <kpathsea/lib.h>
#if defined(WIN32)
# include <kpathsea/variable.h>
#endif
#endif

#include <stdio.h>
#include <stdlib.h>
#if defined(MSDOS) || defined(WIN32)
# include <fcntl.h>
#else
# if !defined(THINK_C)          /* Macintosh */
# include <unistd.h>
# endif
#endif
#include <ctype.h>
#include <string.h>
#include "commands.h"
#if defined(THINK_C)
# include "macintosh.h"
#endif
#if defined(WIN32) && defined(KPATHSEA)
#undef fopen
#define fopen fsyscp_fopen
#endif


/*
* Constant definitions
*/

#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)



/*
* Type definitions
*/

typedef struct _font {
   long    num;
   struct _font * next;
   char  * name;
} font;



/*
* Variable declarations
*/

font * fonts = NULL;
FILE * dvifp;
char * dvi_name;
long   pc = 0;

char * progname;

int is_ptex = 0;
int is_xetex = 0;
const char * dvi_ext = ".dvi";

/*
* Function declarations
*/

#if defined(VMS)
               main            (int argc, char ** argv);
#endif
void            bop             (void);
void            preamble        (void);
void            postamble       (void);
void            postpostamble   (void);
void            fontdef         (int x);
const char *    fontname        (unsigned long fntnum);
void            special         (int x);
void            printnonprint   (int ch);
unsigned long   num             (int size);
long            snum            (int size);
void            usage           (void);
void            picfile         (int opcode);
void            natfontdef      (int opcode);
void            glyphs          (int opcode);
void            dvidir          (int opcode);
void            invalid         (int opcode);




/*
* MAIN --
*/

#if defined(VMS)
    main(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
   register int opcode;                /* dvi opcode */
   register int i;
   unsigned long fontnum;

#if defined(WIN32) && defined(KPATHSEA)
   char **av, *enc;
   int ac;
   kpse_set_program_name(argv[0], "disdvi");
   enc = kpse_var_value("command_line_encoding");
   if (get_command_line_args_utf8(enc, &ac, &av)) {
       argv = av;
       argc = ac;
   }
#endif

#if defined(THINK_C)
   argc = process_disdvi_command_line(&argv);
#endif

   progname = *argv++;

   if ((argc > 1) && (*argv[0] == '-')) {
       if (!strcmp(*argv, "-h")) {
           usage();
           exit(0);
       }
       if (!strcmp(*argv, "-p"))
           is_ptex = 1;
       else if (!strcmp(*argv, "-x")) {
           is_xetex = 1;
           dvi_ext = ".xdv";
       } else {
           fprintf(stderr, "Invalid option `%s'\n", *argv);
           usage();
           exit(3);
       }
       argv++;
       argc--;
   }

   if (argc > 2) {
       fprintf(stderr, "To many arguments\n");
       usage();
       exit(1);
   }

   if (argc == 2) {
       if ((i = strlen(*argv)) == 0) {
           fprintf(stderr, "Illegal empty filename\n");
           usage();
           exit(2);
       }
       if ((i >= 5) && (!strcmp(*argv+i-4, dvi_ext)))
           dvi_name = *argv;
       else {
           if ((dvi_name = malloc((i+5) * sizeof(char))) == NULL) {
               perror("dvi_name");
               exit(1);
           }
           strcpy(dvi_name, *argv);
           strcat(dvi_name, dvi_ext);
       }
       if ((dvifp = fopen(dvi_name, "r")) == NULL) {
           perror(dvi_name);
           exit(3);
       }
   }
   else
       dvifp = stdin;

#if defined(MSDOS) || defined(WIN32)
   setmode(fileno(dvifp), O_BINARY);
#endif

   while ((opcode = (int) get1()) != EOF) {    /* process until end of file */
       printf("%06ld: ", pc - 1);
       if ((opcode <= LASTCHAR) && isprint(opcode)) {
           printf("Char:     ");
           while ((opcode <= LASTCHAR) && isprint(opcode)) {
               putchar(opcode);
               opcode = (int) get1();
           }
           putchar('\n');
           printf("%06ld: ", pc - 1);
       }

       if (opcode <= LASTCHAR)
           printnonprint(opcode);              /* it must be a non-printable */
       else if ((opcode >= FONT_00) && (opcode <= FONT_63))
           printf("FONT_%02d              /* %s */\n", opcode - FONT_00,
                                   fontname((unsigned long) opcode - FONT_00));
       else
           switch (opcode) {
               case SET1     :
               case SET2     :
               case SET3     :
               case SET4     : printf("SET%d:    %ld\n", opcode - SET1 + 1,
                                                      num(opcode - SET1 + 1));
                               break;
               case SET_RULE : printf("SET_RULE: height: %ld\n", sget4());
                               printf("%06ld: ", pc);
                               printf("          length: %ld\n", sget4());
                               break;
               case PUT1     :
               case PUT2     :
               case PUT3     :
               case PUT4     : printf("PUT%d:     %ld\n", opcode - PUT1 + 1,
                                                      num(opcode - PUT1 + 1));
                               break;
               case PUT_RULE : printf("PUT_RULE: height: %ld\n", sget4());
                               printf("%06ld: ", pc);
                               printf("          length: %ld\n", sget4());
                               break;
               case NOP      : printf("NOP\n");  break;
               case BOP      : bop();            break;
               case EOP      : printf("EOP\n");  break;
               case PUSH     : printf("PUSH\n"); break;
               case POP      : printf("POP\n");  break;
               case RIGHT1   :
               case RIGHT2   :
               case RIGHT3   :
               case RIGHT4   : printf("RIGHT%d:   %ld\n", opcode - RIGHT1 + 1,
                                                    snum(opcode - RIGHT1 + 1));
                               break;
               case W0       : printf("W0\n");   break;
               case W1       :
               case W2       :
               case W3       :
               case W4       : printf("W%d:       %ld\n", opcode - W0,
                                                     snum(opcode - W0));
                               break;
               case X0       : printf("X0\n");   break;
               case X1       :
               case X2       :
               case X3       :
               case X4       : printf("X%d:       %ld\n", opcode - X0,
                                                     snum(opcode - X0));
                               break;
               case DOWN1    :
               case DOWN2    :
               case DOWN3    :
               case DOWN4    : printf("DOWN%d:    %ld\n", opcode - DOWN1 + 1,
                                                     snum(opcode - DOWN1 + 1));
                               break;
               case Y0       : printf("Y0\n");   break;
               case Y1       :
               case Y2       :
               case Y3       :
               case Y4       : printf("Y%d:       %ld\n", opcode - Y0,
                                                     snum(opcode - Y0));
                               break;
               case Z0       : printf("Z0\n");   break;
               case Z1       :
               case Z2       :
               case Z3       :
               case Z4       : printf("Z%d:       %ld\n", opcode - Z0,
                                                     snum(opcode - Z0));
                               break;
               case FNT1     :
               case FNT2     :
               case FNT3     :
               case FNT4     : fontnum = num(opcode -FNT1 + 1);
                               printf("FNT%d:     %ld    /* %s */\n",
                                      opcode - FNT1 + 1, fontnum,
                                      fontname(fontnum));
                               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;
               case PIC_FILE : picfile(opcode);                break;
               case NAT_FNT  : natfontdef(opcode);             break;
               case SET_GL_AR:
               case SET_GL_ST: glyphs(opcode);                 break;
               case DVI_DIR  : dvidir(opcode);                 break;
               default       : invalid(opcode);
           }
   }

   exit(0);

} /* main */



/*
* BOP -- Process beginning of page.
*/

void bop(void)
{
   int i;

   printf("BOP       page number      : %ld", sget4());
   for (i=9; i > 0; i--) {
       if (i % 3 == 0)
           printf("\n%06ld:         ", pc);
       printf("  %6ld", sget4());
   }
   printf("\n%06ld: ", pc);
   printf("          prev page offset : %06ld\n", sget4());

   return;

} /* bop */



/*
* POSTAMBLE -- Process post amble.
*/

void postamble(void)
{

   printf("POST      last page offset : %06ld\n", sget4());
   printf("%06ld: ", pc);
   printf("          numerator        : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          denominator      : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          magnification    : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          max page height  : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          max page width   : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          stack size needed: %d\n", (int) get2());
   printf("%06ld: ", pc);
   printf("          number of pages  : %d\n", (int) get2());

   return;

} /* postamble */



/*
* PREAMBLE -- Process pre amble.
*/

void preamble(void)
{
   register int i;

   printf("PRE       version          : %d\n", (int) get1());
   printf("%06ld: ", pc);
   printf("          numerator        : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          denominator      : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          magnification    : %ld\n", get4());
   printf("%06ld: ", pc);
   i = (int) get1();
   printf("          job name (%3d)   :", i);
   while (i-- > 0)
       putchar((int) get1());
   putchar('\n');

   return;

} /* preamble */



/*
* POSTPOSTAMBLE -- Process post post amble.
*/

void postpostamble(void)
{
   register int i;

   printf("POSTPOST  postamble offset : %06ld\n", get4());
   printf("%06ld: ", pc);
   printf("          version          : %d\n", (int) get1());
   while ((i = (int) get1()) == TRAILER) {
       printf("%06ld: ", pc - 1);
       printf("TRAILER\n");
   }
   while (i != EOF) {
       printf("%06ld: ", pc - 1);
       printf("BAD DVI FILE END: 0x%02X\n", i);
       i = (int) get1();
   }

   return;

} /* postpostamble */



/*
* SPECIAL -- Process special opcode.
*/

void special(int x)
{
   register long len;

   len = num(x);
   printf("XXX%d:     %ld bytes\n", x, len);
   printf("%06ld: ", pc);
   for (; len>0; len--)           /* a bit dangerous ... */
       putchar((int) get1());     /*   can be non-printables */
   putchar('\n');

   return;

} /* special */



/*
* FONTDEF -- Process a font definition.
*/

void fontdef(int x)
{
   register int i;
   char * name;
   font * fnt;
   int namelen;
   unsigned long fntnum;
   int new = 0;

   fntnum = num(x);
   printf("FNT_DEF%d: %ld\n", x, fntnum);
   printf("%06ld: ", pc);           /* avoid side-effect on pc in get4() */
   printf("          checksum         : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          scale            : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          design           : %ld\n", get4());
   printf("%06ld: ", pc);
   printf("          name             : ");
   namelen = (int) get1() + (int) get1();
   fnt = fonts;
   while (fnt != NULL && fnt->num != fntnum)
       fnt = fnt->next;
   if (fnt == NULL) {
       if ((fnt = (font *) malloc(sizeof(font))) == NULL) {
           perror("fontdef");
           exit(1);
       }
       fnt->num = fntnum;
       new = 1;
   }
   else
       free(fnt->name);    /* free old name */
   if ((name = (char *) malloc((namelen+1) * sizeof(char))) == NULL) {
       perror("fontdef");
       exit(1);
   }

   for (i = 0; i < namelen; i++)
       name[i] = get1();
   name[i] = '\0';
   fnt->name = name;
   if (new) {
       fnt->next = fonts;
       fonts = fnt;
   }

   printf("%s\n", name);

   return;

} /* fontdef */



/*
* FONTNAME -- Return font name.
*/

const char * fontname(unsigned long fntnum)
{
   font * fnt;

   fnt = fonts;
   while (fnt != NULL && fnt->num != fntnum)
       fnt = fnt->next;
   if (fnt != NULL)
       return fnt->name;

   return "unknown fontname";

} /* fontname */



/*
* PRINTNONPRINT -- Translate non-printable characters.
*/

void printnonprint(int ch)
{

   printf("Char:     ");
   switch (ch) {
       case 11  :  printf("ff         /* ligature (non-printing) 0x%02X */",
                          ch);
                   break;
       case 12  :  printf("fi         /* ligature (non-printing) 0x%02X */",
                          ch);
                   break;
       case 13  :  printf("fl         /* ligature (non-printing) 0x%02X */",
                          ch);
                   break;
       case 14  :  printf("ffi        /* ligature (non-printing) 0x%02X */",
                          ch);
                   break;
       case 15  :  printf("ffl        /* ligature (non-printing) 0x%02X */",
                          ch);
                   break;
       case 16  :  printf("i          /* (non-printing) 0x%02X */", ch);
                   break;
       case 17  :  printf("j          /* (non-printing) 0x%02X */", ch);
                   break;
       case 25  :  printf("ss         /* german (non-printing) 0x%02X */", ch);
                   break;
       case 26  :  printf("ae         /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       case 27  :  printf("oe         /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       case 28  :  printf("o          /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       case 29  :  printf("AE         /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       case 30  :  printf("OE         /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       case 31  :  printf("O          /* scandinavian (non-printing) 0x%02X */",
                          ch);
                   break;
       default  :  printf("0x%02X", ch); break;
   }
   putchar('\n');

   return;

} /* printnonprint */



/*
* NUM --
*/

unsigned long num(int size)
{
   register int i;
   register unsigned long x = 0;

   pc += size;
   for (i = size; i > 0; i--)
       x = (x << 8) + (unsigned) getc(dvifp);

   return x;

} /* num */



/*
* SNUM --
*/

long snum(int size)
{
   register int i;
   register long x;

   pc += size;
   x = getc(dvifp);
   if (x & 0x80)
       x -= 0x100;
   for (i = size - 1; i > 0; i--)
       x = (x << 8) + (unsigned) getc(dvifp);

   return x;

} /* snum */



/*
* USAGE --
*/

void usage(void)
{

   fprintf(stderr, "\n%s\n\n", disdvi);
   fprintf(stderr, "    disassembles (p)TeX dvi and XeTeX xdv files\n");
   fprintf(stderr, "Usage: %s [-h | [-p] [dvi_file[.dvi]]\n", progname);
   fprintf(stderr, "              | -x [xdv_file[.xdv]]]\n");
   fprintf(stderr, "\n If you like this code and want to support is feel free\n to donate at Paypal [email protected]. Thanks.\n\n");


   return;

} /* usage */



void picfile(int opcode)
{
   int i;

   if (!is_xetex) {
       invalid(opcode);
       return;
   }

   printf("PIC_FILE  flags            : %ld\n", get1());
   printf("%06ld:           trans :", pc);
   for (i=0; i<6; i++)
       printf(" %ld", sget4());
   printf("\n%06ld: ", pc);
   printf("          page              : %ld\n", get2());
   printf("%06ld: ", pc);
   i = (int) get1();
   printf("          path name (%3d)   :", i);
   while (i-- > 0)
       putchar((int) get1());
   putchar('\n');
} /* picfile */



void natfontdef(int opcode)
{
   register int i;
   char * name;
   font * fnt;
   int flags, namelen, famlen, stylen;
   long fntnum;
   int new = 0;

   if (!is_xetex) {
       invalid(opcode);
       return;
   }

   fntnum = num(4);
   printf("NAT_FNT:  %ld\n", fntnum);
   printf("%06ld: ", pc);
   printf("          scale            : %ld\n", get4());
   printf("%06ld: ", pc);
   flags = get2();
   printf("          flags            : %d\n", flags);
   printf("%06ld: ", pc);
   printf("          name             : ");
   namelen = (int) get1();
   famlen = (int) get1();
   stylen = (int) get1();
   fnt = fonts;
   while (fnt != NULL && fnt->num != fntnum)
       fnt = fnt->next;
   if (fnt == NULL) {
       if ((fnt = (font *) malloc(sizeof(font))) == NULL) {
           perror("fontdef");
           exit(1);
       }
       fnt->num = fntnum;
       new = 1;
   }
   else
       free(fnt->name);    /* free old name */
   if ((name = (char *) malloc((namelen+1) * sizeof(char))) == NULL) {
       perror("fontdef");
       exit(1);
   }

   for (i = 0; i < namelen; i++)
       name[i] = get1();
   name[namelen] = '\0';
   fnt->name = name;
   if (new) {
       fnt->next = fonts;
       fonts = fnt;
   }

   printf("%s\n", name);

   if (famlen) {
       printf("                  family           : ");
       while (famlen-- > 0)
           putchar((int) get1());
       putchar('\n');
   }

   if (stylen) {
       printf("                  style            : ");
       while (stylen-- > 0)
           putchar((int) get1());
       putchar('\n');
   }
} /* fontdef */



void glyphs(int opcode)
{
   long width;
   int x, n, i, j;
   long * xy;

   if (!is_xetex) {
       invalid(opcode);
       return;
   }

   x = SET_GL_ST - opcode + 1;
   width = sget4();
   n = get2();
   printf("GLYPH_%s width            : %ld\n", x == 2 ? "ARR" : "STR", width);
   if ((xy = (long *) malloc(n * x * sizeof(long))) == NULL) {
       perror("glyphs");
       exit(1);
   }
   for (j=0; j < n * x; j++)
       xy[j] = sget4();
   for (i=0, j=0; i < n; i++) {
       printf("           x: %ld", xy[j++]);
       if (x == 2)
           printf("    y: %ld", xy[j++]);
       printf("    g: %ld\n", get2());
   }
} /* glyphs */



void dvidir(int opcode)
{
   if (!is_ptex) {
       invalid(opcode);
       return;
   }

   printf("DVI_DIR:  %ld\n", get1());
} /* dvidir */



void invalid(int opcode)
{
   printf("INVALID   %d\n", opcode);
} /* invalid */