/* dv2dt - convert DVI file to human-readable "DTL" format.
   
   This file is public domain.
   Originally written 1995, Geoffrey Tobin.
   The author has expressed the hope that any modification will retain enough content to remain useful. He would also appreciate being acknowledged as the original author in the documentation.
   This declaration added 2008/11/14 by Clea F. Rees with the permission of Geoffrey Tobin.

   - (ANSI C) version 0.6.0 - 17:54 GMT +11  Wed 8 March 1995
   - author:  Geoffrey Tobin    ecsgrt@luxor.latrobe.edu.au
   - patch:  Michal Tomczak-Jaegermann   ntomczak@vm.ucs.ualberta.ca
   - Reference:  "The DVI Driver Standard, Level 0",
                 by  The TUG DVI Driver Standards Committee.
                 Appendix A, "Device-Independent File Format".
*/

/* unix version; read from stdin, write to stdout, by default. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "dtl.h"

#define PRINT_BCOM   if (group) fprintf (dtl, "%s", BCOM)
#define PRINT_ECOM   if (group) fprintf (dtl, "%s", ECOM)

/*
  operation's:
     opcode,
     name,
     number of args,
     string of arguments.
*/
struct op_info_st {int code; char * name; int nargs; char * args; };

typedef  struct op_info_st  op_info;

/*
  table's:
     name,
     first opcode,
     last opcode,
     pointer to opcode info.
*/
struct op_table_st {char * name; int first; int last; op_info * list; };

typedef  struct op_table_st  op_table;

/* Table for opcodes 128 to 170 inclusive. */

op_info  op_info_128_170 [] =
{
  {128, "s1", 1, "1"},
  {129, "s2", 1, "2"},
  {130, "s3", 1, "3"},
  {131, "s4", 1, "-4"},
  {132, "sr", 2, "-4 -4"},
  {133, "p1", 1, "1"},
  {134, "p2", 1, "2"},
  {135, "p3", 1, "3"},
  {136, "p4", 1, "-4"},
  {137, "pr", 2, "-4 -4"},
  {138, "nop", 0, ""},
  {139, "bop", 11, "-4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4"},
  {140, "eop", 0, ""},
  {141, "[", 0, ""},
  {142, "]", 0, ""},
  {143, "r1", 1, "-1"},
  {144, "r2", 1, "-2"},
  {145, "r3", 1, "-3"},
  {146, "r4", 1, "-4"},
  {147, "w0", 0, ""},
  {148, "w1", 1, "-1"},
  {149, "w2", 1, "-2"},
  {150, "w3", 1, "-3"},
  {151, "w4", 1, "-4"},
  {152, "x0", 0, ""},
  {153, "x1", 1, "-1"},
  {154, "x2", 1, "-2"},
  {155, "x3", 1, "-3"},
  {156, "x4", 1, "-4"},
  {157, "d1", 1, "-1"},
  {158, "d2", 1, "-2"},
  {159, "d3", 1, "-3"},
  {160, "d4", 1, "-4"},
  {161, "y0", 0, ""},
  {162, "y1", 1, "-1"},
  {163, "y2", 1, "-2"},
  {164, "y3", 1, "-3"},
  {165, "y4", 1, "-4"},
  {166, "z0", 0, ""},
  {167, "z1", 1, "-1"},
  {168, "z2", 1, "-2"},
  {169, "z3", 1, "-3"},
  {170, "z4", 1, "-4"}
};  /* op_info  op_info_128_170 [] */

op_table  op_128_170  =  {"op_128_170", 128, 170, op_info_128_170};

/* Table for font with 1 to 4 bytes (opcodes 235 to 238) inclusive. */

op_info  fnt_n [] =
{
  {235, "f1", 1, "1"},
  {236, "f2", 1, "2"},
  {237, "f3", 1, "3"},
  {238, "f4", 1, "-4"}
};  /* op_info  fnt_n [] */

op_table  fnt  =  {"f", 235, 238, fnt_n};


/* function prototypes */

int open_dvi ARGS((char * dvi_file, FILE ** dvi));
int open_dtl ARGS((char * dtl_file, FILE ** dtl));
int dv2dt ARGS((FILE * dvi, FILE * dtl));

COUNT wunsigned ARGS((int n,  FILE * dvi,  FILE * dtl));
COUNT wsigned   ARGS((int n,  FILE * dvi,  FILE * dtl));
S4 rsigned   ARGS((int n,  FILE * dvi));
U4 runsigned ARGS((int n,  FILE * dvi));

COUNT wtable ARGS((op_table table, int opcode, FILE * dvi, FILE * dtl));

COUNT setseq ARGS((int opcode, FILE * dvi, FILE * dtl));
Void setpchar ARGS((int charcode, FILE * dtl));
Void xferstring ARGS((int k, FILE * dvi, FILE * dtl));

COUNT special ARGS((FILE * dvi,  FILE * dtl,  int n));
COUNT fontdef ARGS((FILE * dvi,  FILE * dtl,  int n));
COUNT preamble  ARGS((FILE * dvi,  FILE * dtl));
COUNT postamble ARGS((FILE * dvi,  FILE * dtl));
COUNT postpost  ARGS((FILE * dvi,  FILE * dtl));


String program;  /* name of dv2dt program */

int
main
#ifdef STDC
  (int argc,  char * argv[])
#else
  (argc, argv)
  int argc;
  char * argv[];
#endif
{
  FILE * dvi = stdin;
  FILE * dtl = stdout;

  /* Watch out:  C's standard library's string functions are dicey */
  strncpy (program, argv[0], MAXSTRLEN);

  if (argc > 1)
    open_dvi (argv[1], &dvi);

  if (argc > 2)
    open_dtl (argv[2], &dtl);

  dv2dt (dvi, dtl);

  return 0;  /* OK */
}
/* end main */

int
open_dvi
#ifdef STDC
  (char * dvi_file, FILE ** pdvi)
#else
  (dvi_file, pdvi)
  char * dvi_file;
  FILE ** pdvi;
#endif
/* I:  dvi_file;  I:  pdvi;  O:  *pdvi. */
{
  if (pdvi == NULL)
  {
    fprintf (stderr, "%s:  address of dvi variable is NULL.\n", program);
    exit (1);
  }

  *pdvi = fopen (dvi_file, "rb");

  if (*pdvi == NULL)
  {
    fprintf (stderr, "%s:  Cannot open \"%s\" for binary reading.\n",
      program, dvi_file);
    exit (1);
  }

  return 1;  /* OK */
}
/* open_dvi */

int
open_dtl
#ifdef STDC
  (char * dtl_file, FILE ** pdtl)
#else
  (dtl_file, pdtl)
  char * dtl_file;
  FILE ** pdtl;
#endif
/* I:  dtl_file;  I:  pdtl;  O:  *pdtl. */
{
  if (pdtl == NULL)
  {
    fprintf (stderr, "%s:  address of dtl variable is NULL.\n", program);
    exit (1);
  }

  *pdtl = fopen (dtl_file, "w");

  if (*pdtl == NULL)
  {
    fprintf (stderr, "%s:  Cannot open \"%s\" for text writing.\n",
      program, dtl_file);
    exit (1);
  }

  return 1;  /* OK */
}
/* open_dtl */

int
dv2dt
#ifdef STDC
  (FILE * dvi, FILE * dtl)
#else
  (dvi, dtl)
  FILE * dvi;
  FILE * dtl;
#endif
{
  int opcode;
  COUNT count;  /* intended to count bytes to DVI file; as yet unused. */

  PRINT_BCOM;
  fprintf (dtl, "variety ");
/*  fprintf (dtl, BMES); */
  fprintf (dtl, VARIETY);
/*  fprintf (dtl, EMES); */
  PRINT_ECOM;
  fprintf (dtl, "\n");

  /* start counting DVI bytes */
  count = 0;
  while ((opcode = fgetc (dvi)) != EOF)
  {
    PRINT_BCOM;  /* start of command and parameters */
    if (opcode < 0 || opcode > 255)
    {
      count += 1;
      fprintf (stderr, "%s:  Non-byte from \"fgetc()\"!\n", program);
      exit (1);
    }
    else if (opcode <= 127)
    {
      /* setchar commands */
      /* count += 1; */
      /* fprintf (dtl, "%s%d", SETCHAR, opcode); */
      count +=
      setseq (opcode, dvi, dtl);
    }
    else if (opcode >= 128 && opcode <= 170)
    {
      count +=
      wtable (op_128_170, opcode, dvi, dtl);
    }
    else if (opcode >= 171 && opcode <= 234)
    {
      count += 1;
      fprintf (dtl, "%s%d", FONTNUM, opcode - 171);
    }
    else if (opcode >= 235 && opcode <= 238)
    {
      count +=
      wtable (fnt, opcode, dvi, dtl);
    }
    else if (opcode >= 239 && opcode <= 242)
    {
      count +=
      special (dvi, dtl, opcode - 238);
    }
    else if (opcode >= 243 && opcode <= 246)
    {
      count +=
      fontdef (dvi, dtl, opcode - 242);
    }
    else if (opcode == 247)
    {
      count +=
      preamble (dvi, dtl);
    }
    else if (opcode == 248)
    {
      count +=
      postamble (dvi, dtl);
    }
    else if (opcode == 249)
    {
      count +=
      postpost (dvi, dtl);
    }
    else if (opcode >= 250 && opcode <= 255)
    {
      count += 1;
      fprintf (dtl, "opcode%d", opcode);
    }
    else
    {
      count += 1;
      fprintf (stderr, "%s:  unknown byte.\n", program);
      exit (1);
    }
    PRINT_ECOM;  /* end of command and parameters */
    fprintf (dtl, "\n");
    if (fflush (dtl) == EOF)
    {
      fprintf (stderr, "%s:  fflush on dtl file gave write error!\n", program);
      exit (1);
    }
  } /* end while */

  return 1;  /* OK */
}
/* dv2dt */


COUNT
wunsigned
#ifdef STDC
  (int n, FILE * dvi, FILE * dtl)
#else
  (n, dvi, dtl)
  int n;
  FILE * dvi;
  FILE * dtl;
#endif
{
  U4 unum;

  fprintf (dtl, " ");
  unum = runsigned (n, dvi);
  fprintf (dtl, UF4, unum);
  return n;
}
/* end wunsigned */

COUNT
wsigned
#ifdef STDC
  (int n, FILE * dvi, FILE * dtl)
#else
  (n, dvi, dtl)
  int n;
  FILE * dvi;
  FILE * dtl;
#endif
{
  S4 snum;

  fprintf (dtl, " ");
  snum = rsigned (n, dvi);
  fprintf (dtl, SF4, snum);
  return n;
}
/* end wsigned */

U4
runsigned
#ifdef STDC
  (int n,  FILE * dvi)
#else
  (n, dvi)
  int n;
  FILE * dvi;
#endif
/* read 1 <= n <= 4 bytes for an unsigned integer from dvi file */
/* DVI format uses Big-endian storage of numbers. */
{
  U4 integer;
  int ibyte = 0;
  int i;

  if (n < 1 || n > 4)
  {
    fprintf (stderr,
      "%s:  runsigned() asked for %d bytes.  Must be 1 to 4.\n", program, n);
    exit (1);
  }

  /* Following calculation works iff storage is big-endian. */
  integer = 0;
  for (i = 0; i < n; i++)
  {
    integer *= 256;
    ibyte = fgetc (dvi);
    integer += ibyte;
  }

  return integer;
}
/* end runsigned */

S4
rsigned
#ifdef STDC
  (int n,  FILE * dvi)
#else
  (n, dvi)
  int n;
  FILE * dvi;
#endif
/* read 1 <= n <= 4 bytes for a signed integer from dvi file */
/* DVI format uses Big-endian storage of numbers. */
{
  S4 integer;
  int ibyte = 0;
  int i;

  if (n < 1 || n > 4)
  {
    fprintf (stderr,
      "%s:  rsigned() asked for %d bytes.  Must be 1 to 4.\n", program, n);
    exit (1);
  }

  /* Following calculation works iff storage is big-endian. */
  integer = 0;
  for (i = 0; i < n; i++)
  {
    integer *= 256;
    ibyte = fgetc (dvi);
    /* Big-endian implies sign byte is first byte. */
    if (i == 0 && ibyte >= 128)
    {
      ibyte -= 256;
    }
    integer += ibyte;
  }

  return integer;
}
/* end rsigned */

COUNT
wtable
#ifdef STDC
  (op_table table, int opcode, FILE * dvi, FILE * dtl)
#else
  (table, opcode, dvi, dtl)
  op_table table;
  int opcode;
  FILE * dvi;
  FILE * dtl;
#endif
/* write command with given opcode in given table */
/* return number of DVI bytes in this command */
{
  op_info op;  /* pointer into table of operations and arguments */
  COUNT bcount = 0;  /* number of bytes in arguments of this opcode */
  String args;  /* arguments string */
  int i;  /* count of arguments read from args */
  int pos;  /* position in args */

  /* Defensive programming. */
  if (opcode < table.first || opcode > table.last)
  {
    fprintf (stderr,
      "%s: opcode %d is outside table %s [ %d to %d ] !\n",
      program, opcode, table.name, table.first, table.last);
    exit (1);
  }

  op = table.list [opcode - table.first];

  /* Further defensive programming. */
  if (op.code != opcode)
  {
    fprintf (stderr, "%s: internal table %s wrong!\n", program, table.name);
    exit (1);
  }

  bcount = 1;
  fprintf (dtl, "%s", op.name);

  /* NB:  sscanf does an ungetc, */
  /*      so args must be writable. */

  strncpy (args, op.args, MAXSTRLEN);

  pos = 0;
  for (i = 0; i < op.nargs; i++)
  {
    int argtype;  /* sign and number of bytes in current argument */
    int nconv;  /* number of successful conversions from args */
    int nread;  /* number of bytes read from args */

    nconv = sscanf (args + pos, "%d%n", &argtype, &nread);

    /* internal consistency checks */
    if (nconv != 1 || nread <= 0)
    {
      fprintf (stderr,
        "%s: internal read of table %s failed!\n", program, table.name);
      exit (1);
    }

    pos += nread;

    bcount += ( argtype < 0 ?
               wsigned  (-argtype, dvi, dtl) :
               wunsigned (argtype, dvi, dtl)  ) ;
  } /* end for */

  return bcount;

}
/* wtable */

COUNT
setseq
#ifdef STDC
  (int opcode, FILE * dvi, FILE * dtl)
#else
  (opcode, dvi, dtl)
  int opcode;
  FILE * dvi;
  FILE * dtl;
#endif
/* write a sequence of setchar commands */
/* return count of DVI bytes interpreted into DTL */
{
  int charcode = opcode;  /* fortuitous */
  int ccount = 0;

  if (!isprint (charcode))
  {
    ccount = 1;
    fprintf (dtl, "%s%02X", SETCHAR, opcode);
  }
  else
  {
    /* start of sequence of font characters */
    fprintf (dtl, BSEQ);

    /* first character */
    ccount = 1;
    setpchar (charcode, dtl);

    /* subsequent characters */
    while ((opcode = fgetc (dvi)) != EOF)
    {
      if (opcode < 0 || opcode > 127)
      {
        break;  /* not a setchar command, so sequence has ended */
      }
      charcode = opcode;  /* fortuitous */
      if (!isprint (charcode))  /* not printable ascii */
      {
        break;  /* end of font character sequence, as for other commands */
      }
      else  /* printable ASCII */
      {
        ccount += 1;
        setpchar (charcode, dtl);
      }
    }  /* end for loop */

    /* prepare to reread opcode of next DVI command */
    if (ungetc (opcode, dvi) == EOF)
    {
      fprintf (stderr, "setseq:  cannot push back a byte\n");
      exit (1);
    }

    /* end of sequence of font characters */
    fprintf (dtl, ESEQ);
  }
  return ccount;
}
/* setseq */

Void
setpchar
#ifdef STDC
  (int charcode, FILE * dtl)
#else
  (charcode, dtl)
  int charcode;
  FILE * dtl;
#endif
/* set printable character */
{
  switch (charcode)
  {
    case ESC_CHAR:
      fprintf (dtl, "%c", ESC_CHAR);
      fprintf (dtl, "%c", ESC_CHAR);
      break;
    case QUOTE_CHAR:
      fprintf (dtl, "%c", ESC_CHAR);
      fprintf (dtl, "%c", QUOTE_CHAR);
      break;
    case BSEQ_CHAR:
      fprintf (dtl, "%c", ESC_CHAR);
      fprintf (dtl, "%c", BSEQ_CHAR);
      break;
    case ESEQ_CHAR:
      fprintf (dtl, "%c", ESC_CHAR);
      fprintf (dtl, "%c", ESEQ_CHAR);
      break;
    default:
      fprintf (dtl, "%c", charcode);
      break;
  }
}
/* setpchar */

Void
xferstring
#ifdef STDC
  (int k, FILE * dvi, FILE * dtl)
#else
  (k, dvi, dtl)
  int k;
  FILE * dvi;
  FILE * dtl;
#endif
/* copy string of k characters from dvi file to dtl file */
{
  int i;
  int ch;

  fprintf (dtl, " ");
  fprintf (dtl, "'");
  for (i=0; i < k; i++)
  {
    ch = fgetc (dvi);
    if (ch == ESC_CHAR || ch == EMES_CHAR)
    {
      fprintf (dtl, "%c", ESC_CHAR);
    }
    fprintf (dtl, "%c", ch);
  }
  fprintf (dtl, "'");
}
/* xferstring */

COUNT
special
#ifdef STDC
  (FILE * dvi,  FILE * dtl,  int n)
#else
  (dvi, dtl, n)
  FILE * dvi;
  FILE * dtl;
  int n;
#endif
/* read special 1 .. 4 from dvi and write in dtl */
/* return number of DVI bytes interpreted into DTL */
{
  U4  k;

  if (n < 1 || n > 4)
  {
    fprintf (stderr, "%s:  special %d, range is 1 to 4.\n", program, n);
    exit (1);
  }

  fprintf (dtl, "%s%d", SPECIAL, n);

  /* k[n] = length of special string */
  fprintf (dtl, " ");
  k = runsigned (n, dvi);
  fprintf (dtl, UF4, k);

  /* x[k] = special string */
  xferstring (k, dvi, dtl);

  return (1 + n + k);
}
/* end special */

COUNT
fontdef
#ifdef STDC
  (FILE * dvi,  FILE * dtl,  int n)
#else
  (dvi,  dtl,  n)
  FILE * dvi;
  FILE * dtl;
  int n;
#endif
/* read fontdef 1 .. 4 from dvi and write in dtl */
/* return number of DVI bytes interpreted into DTL */
{
  U4 ku, c, s, d, a, l;
  S4 ks;

  if (n < 1 || n > 4)
  {
    fprintf (stderr, "%s:  font def %d, range is 1 to 4.\n", program, n);
    exit (1);
  }

  fprintf (dtl, "%s%d", FONTDEF, n);

  /* k[n] = font number */
  fprintf (dtl, " ");
  if (n == 4)
  {
    ks = rsigned (n, dvi);
    fprintf (dtl, SF4, ks);
  }
  else
  {
    ku = runsigned (n, dvi);
    fprintf (dtl, UF4, ku);
  }

  /* c[4] = checksum */
  fprintf (dtl, " ");
  c = runsigned (4, dvi);
#ifdef HEX_CHECKSUM
  fprintf (dtl, XF4, c);
#else /* NOT HEX_CHECKSUM */
  /* write in octal, to allow quick comparison with tftopl's output */
  fprintf (dtl, OF4, c);
#endif

  /* s[4] = scale factor */
  fprintf (dtl, " ");
  s = runsigned (4, dvi);
  fprintf (dtl, UF4, s);

  /* d[4] = design size */
  fprintf (dtl, " ");
  d = runsigned (4, dvi);
  fprintf (dtl, UF4, d);

  /* a[1] = length of area (directory) name */
  a = runsigned (1, dvi);
  fprintf (dtl, " ");
  fprintf (dtl, UF4, a);

  /* l[1] = length of font name */
  l = runsigned (1, dvi);
  fprintf (dtl, " ");
  fprintf (dtl, UF4, l);

  /* n[a+l] = font pathname string => area (directory) + font */
  xferstring (a, dvi, dtl);
  xferstring (l, dvi, dtl);

  return (1 + n + 4 + 4 + 4 + 1 + 1 + a + l);
}
/* end fontdef */

COUNT
preamble
#ifdef STDC
  (FILE * dvi,  FILE * dtl)
#else
  (dvi,  dtl)
  FILE * dvi;
  FILE * dtl;
#endif
/* read preamble from dvi and write in dtl */
/* return number of DVI bytes interpreted into DTL */
{
  U4 id, num, den, mag, k;

  fprintf (dtl, "pre");

  /* i[1] = DVI format identification */
  fprintf (dtl, " ");
  id = runsigned (1, dvi);
  fprintf (dtl, UF4, id);

  /* num[4] = numerator of DVI unit */
  fprintf (dtl, " ");
  num = runsigned (4, dvi);
  fprintf (dtl, UF4, num);

  /* den[4] = denominator of DVI unit */
  fprintf (dtl, " ");
  den = runsigned (4, dvi);
  fprintf (dtl, UF4, den);

  /* mag[4] = 1000 x magnification */
  fprintf (dtl, " ");
  mag = runsigned (4, dvi);
  fprintf (dtl, UF4, mag);

  /* k[1] = length of comment */
  fprintf (dtl, " ");
  k = runsigned (1, dvi);
  fprintf (dtl, UF4, k);

  /* x[k] = comment string */
  xferstring (k, dvi, dtl);

  return (1 + 1 + 4 + 4 + 4 + 1 + k);
}
/* end preamble */

COUNT
postamble
#ifdef STDC
  (FILE * dvi,  FILE * dtl)
#else
  (dvi,  dtl)
  FILE * dvi;
  FILE * dtl;
#endif
/* read postamble from dvi and write in dtl */
/* return number of bytes */
{
  U4 p, num, den, mag, l, u, s, t;

  fprintf (dtl, "post");

  /* p[4] = pointer to final bop */
  fprintf (dtl, " ");
  p = runsigned (4, dvi);
  fprintf (dtl, UF4, p);

  /* num[4] = numerator of DVI unit */
  fprintf (dtl, " ");
  num = runsigned (4, dvi);
  fprintf (dtl, UF4, num);

  /* den[4] = denominator of DVI unit */
  fprintf (dtl, " ");
  den = runsigned (4, dvi);
  fprintf (dtl, UF4, den);

  /* mag[4] = 1000 x magnification */
  fprintf (dtl, " ");
  mag = runsigned (4, dvi);
  fprintf (dtl, UF4, mag);

  /* l[4] = height + depth of tallest page */
  fprintf (dtl, " ");
  l = runsigned (4, dvi);
  fprintf (dtl, UF4, l);

  /* u[4] = width of widest page */
  fprintf (dtl, " ");
  u = runsigned (4, dvi);
  fprintf (dtl, UF4, u);

  /* s[2] = maximum stack depth */
  fprintf (dtl, " ");
  s = runsigned (2, dvi);
  fprintf (dtl, UF4, s);

  /* t[2] = total number of pages (bop commands) */
  fprintf (dtl, " ");
  t = runsigned (2, dvi);
  fprintf (dtl, UF4, t);

/*  return (29);  */
  return (1 + 4 + 4 + 4 + 4 + 4 + 4 + 2 + 2);
}
/* end postamble */

COUNT
postpost
#ifdef STDC
  (FILE * dvi,  FILE * dtl)
#else
  (dvi,  dtl)
  FILE * dvi;
  FILE * dtl;
#endif
/* read post_post from dvi and write in dtl */
/* return number of bytes */
{
  U4 q, id;
  int b223;  /* hope this is 8-bit clean */
  int n223;  /* number of "223" bytes in final padding */

  fprintf (dtl, "post_post");

  /* q[4] = pointer to post command */
  fprintf (dtl, " ");
  q = runsigned (4, dvi);
  fprintf (dtl, UF4, q);

  /* i[1] = DVI identification byte */
  fprintf (dtl, " ");
  id = runsigned (1, dvi);
  fprintf (dtl, UF4, id);

  /* final padding by "223" bytes */
  /* hope this way of obtaining b223 is 8-bit clean */
  for (n223 = 0; (b223 = fgetc (dvi)) == 223; n223++)
  {
    fprintf (dtl, " ");
    fprintf (dtl, "%d", 223);
  }
  if (n223 < 4)
  {
    fprintf (stderr,
      "%s:  bad post_post:  fewer than four \"223\" bytes.\n", program);
    exit (1);
  }
  if (b223 != EOF)
  {
    fprintf (stderr,
      "%s:  bad post_post:  doesn't end with a \"223\".\n", program);
    exit (1);
  }

  return (1 + 4 + 1 + n223);
}
/* end postpost */

/* end of "dv2dt.c" */
