/* SCAN_DWG.C  - for scanning AutoCAD DWG files
*  Copyright (C) 1995,96 Frans Faase, Reini Urban
*
*  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Authors:
*  Frans Faase  <faase@cs.utwente.nl>       http://wwwtranscoop.cs.utwente.nl:8080/~faase/
*  Reini Urban  <rurban@sbox.tu-graz.ac.at> http://xarch.tu-graz.ac.at/home/rurban/
*
* Pointers:
*  reini's version: http://xarch.tu-graz.ac.at/home/rurban/software/scan_dwg.c
*  frans'  version: http://www_is.cs.utwente.nl:8080/~faase/DWG/scan_dwg.c.txt
*
* This file can be compiled correctly with gcc on linux (__GNUC__)
* and Watcom C on DOS (__WATCOMC__)
***************************************************************************/

/* A warning!
   This version parses not all DWG's correctly, it's not very
   well written so far. Its just the most up-to-date version because of the
   header vars. I've got problems with the option handling, eg. just
   to parse the header with -h, or the blocks with -b
   (lot of goto's, very ugly)
   Also there's lot of DOS specific stuff and also Watcom specific stuff
   included, like:
     _splitpath(), _makepath();
   and some Watcom and GCC specific functions like:
     readdir(), opendir(), closedir();
   Borland or other compilers should translate it to dos_findfirst() and
     dos_findnext() and send me a working copy or remove the dir walker.
   With this version it does not compile on linux with gcc, because of the
   DOS specificic stuff mentioned above.
   If this does'nt work try Frans' version.

   If you find out more, send it me! Frans is working on the R13 version now.

   Use it, share it, correct it, sell it, but only with source code included!
   And mail me bugfixes.
*/

/* Revisions */

/* 15.01.96
  Fixed a serios (crash) bug in this web version in assign()
*/

/*-----------------24.11.95 12:17-------------------
 #ifdef __GNUC__
   _assign():  ftell bug
 gnu license reader as arguments
 earlier versions had no CRC32 at the table end, checked
 earlier versions missed some tables, checked
--------------------------------------------------*/

/*-----------------07.11.95 13:16-------------------
 added:
    options support
    support for in and output
    more than one file (even recurse subdirs)
 bugfixes:
    last_pos in _assign() was wrong
--------------------------------------------------*/

/*-----------------30.10.95 21:34-------------------
  new double header vars
  julian date conversion corrected (after the comma)
  Header output beautified
--------------------------------------------------*/

/*-----------------22.10.95 22:59-------------------
changed by Reini Urban:
 new features:
  Julian dates formatted
  DIMSTYLE Table section (former P12)
  VIEW Table section     (former P8)
  UCS Table section      (former P9)
  VPORT Table section
  found some header vars from the active VPORT at scan_header()
  changed ADS to APPID in table section
  group 3 in BLOCK entity with opt 2

 for debugging:
  defined POINTER_VALUE
  changed all pointer output from long to hex "%ld" -> "%05lX"
   (for my hex editor)
  added output of pointer values to some functions in []:
    echo_16_bytes(), get_pointer()
  moved rest of header to scan_header()

 comment:
  somewhere the last handle must be stored,
    (only in the DWG, not in the DXF)
  missing  flags opts:
    01 01 PLINE
    01 01 VERTEX
    01    S/BEND
    40    INSERT
--------------------------------------------------*/

/*#define HEADER*/

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <time.h>

#ifdef WINDOWS
#include <windows.h>
#endif

#include <stdlib.h> /* path funcs*/
#include <direct.h> /* directory funcs*/
#include <math.h>
#include "getopts.h"

#define TRUE     1
#define FALSE    0

char version[]= "0.9";       /* you see, doesnt work yet correctly */
char date[]   = "24.11.95";  /* last update */


#define DEBUG_P(X)    if (debug) printf X
#define VERBOSE_P(X)  if (verbose) printf X
/*#define DEBUG_P(X)  printf X*/

#ifdef DEBUG
#define ASSERT
#endif

#ifdef ASSERT
#include <assert.h>
#endif
#define POINTER_VALUE

#ifdef ASSERT
#define MALLOC(BUF,X) malloc(X);if (BUF==NULL) \
    fprintf (stderr, "Not enough memory for %ld byte",(long) X); assert(BUF)
#else
#define MALLOC(BUF,X)   checknull(malloc(X))
#endif

#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define OPTS(X) if (opts & (1 << (X-1)))
#define SCAN_POINT(G,C) { DEBUG_P((" %d:", G)); scan_point(C); }
#define SCAN_DOUBLE(G)  { double d = _read_double(); \
    DEBUG_P((" %d:%f", G, d)); }
#define SCAN_STRING(G)  { char *s= _read_string(); \
    DEBUG_P((" %d:`%s'", G, s)); }
/*  new */
#define SCAN_FIXED_STRING(G,L) { char *s= _read_fixed_string(L); \
    DEBUG_P((" %d:`%s'", G, s)); }
#define SCAN_BYTE(G)    { byte b =_read_byte(); DEBUG_P((" %d:%02X", G, b)); }
#define SCAN_WORD(G)    { word w =_read_word(); DEBUG_P((" %d:%d",   G, w)); }
#define SCAN_LONG(G)    { longword l = _read_long(); \
    DEBUG_P((" %d:%ld",  G, l)); }
#define SCAN_2DPOINT(G) { DEBUG_P((" %d:", G)); scan_point(FALSE); }
#define SCAN_3DPOINT(G) { DEBUG_P((" %d:", G)); scan_point(TRUE); }

#define ACT_POS     DEBUG_P(("\n[%05X]: ",cur_pos))

#define UNKNOWN_BYTE   {byte b= _read_byte(); DEBUG_P(("\n?b: %3hd ", b)); \
    print_bytes(&b,1); }
#define UNKNOWN_WORD   {word w=_read_word(); DEBUG_P(("\n?w: %3d ", w)); \
    print_bytes(&w,2); }
#define UNKNOWN_LONG   {longword l=_read_long();DEBUG_P(("\n?l: %3ld ", l)); \
    print_bytes(&l,4); }
#define UNKNOWN_DOUBLE  print_double("?f:")

#define KNOWN_BYTE(X)   {byte b=_read_byte();DEBUG_P(("\n%-10s %3hd ", X, b)); \
    print_bytes(&b,1); }
#define KNOWN_WORD(X)   {word w=_read_word();DEBUG_P(("\n%-10s %3d ", X, w)); \
    print_bytes(&w,2); }
#define KNOWN_DOUBLE(X)  print_double(X)
#define KNOWN_2DPOINT(X) print_2dpoint(X)
#define KNOWN_3DPOINT(X) print_3dpoint(X)
#define KNOWN_STRING(X,LEN) {char *s=_read_fixed_string(LEN); \
    DEBUG_P(("%s: %s ",X,s)); }

typedef unsigned char byte;
typedef unsigned int word;
typedef unsigned long int longword;
typedef int bool;


longword cur_pos,
         last_pos;

typedef struct
{   longword start,
             nr;
    word     size;
} ttable, *ptable;

static FILE *fin  = NULL;
static FILE *fout = NULL;

int     header  = FALSE;
int     tables  = FALSE;
int     blocks  = FALSE;
int     entities= FALSE;

int     handles = FALSE;

int     help    = FALSE;   /* displays usage*/
#ifdef DEBUG
int     debug   = TRUE;
#else
int     debug   = FALSE;
#endif
int     verbose = FALSE;
int     recurse = FALSE;
char    outpath[_MAX_DIR]  =""; /* write out to path */
char    outname[_MAX_FNAME]=""; /* outputname */

unsigned numdone=0, numchg=0, numerr=0, numunknown=0;
static bool version_13 = FALSE;
static char execpath [_MAX_PATH];   /* for show_file */

opt_t opttable[] =
{

    { "h", OPTBOOL, &header },
    { "t", OPTBOOL, &tables },
    { "b", OPTBOOL, &blocks },
    { "e", OPTBOOL, &entities },

    { "x", OPTBOOL, &handles }, /* kill em! */

    { "n", OPTSTR,  outname },
    { "p", OPTSTR,  outpath },
    { "?", OPTBOOL, &help },
    { "d", OPTBOOL, &debug },
    { "v", OPTBOOL, &verbose },

    { "s", OPTBOOL, &recurse },
    { NULL, 0, NULL }
};

/* prototypes: */

int main(int argc, char *argv[]);
void *checknull (void *p);
void show_help (int);
void doit (char *);
int  check (char *);
char *force_ext (char *new, char *old, char *ext);
int  check_if_dwg (char *s);
void set_all_opts (int value);

void check_table_pos(char *name, ptable pt, int t_nr);
void scan_table_position(char *name, ptable pt);
void scan_header(void);
void scan_block_table(ptable pt);
void scan_layer_table(ptable pt);
void scan_style_table(ptable pt);
void scan_ltype_table(ptable pt);
void scan_ads_table  (ptable pt);   /* new name */
void scan_ucs_table  (ptable pt);
void scan_view_table (ptable pt);
void scan_vport_table(ptable pt);
void scan_table(char *name, ptable pt);
void scan_entities(longword start, longword endi, longword sce_offset);
void scan_xdata(void);
void scan_handle(void);

void scan_point(bool z_coord);
void scan_fixed_ident(int len);
void print_bytes (void *buf, int len);
void print_double (char *str);
void print_2dpoint (char *str);
void print_3dpoint (char *str);

void echo_16_bytes(word len);
void echo_bytes(long int len);
longword get_pointer();
void check_pointer(longword p);
void get_check_2(void);
void get_check_32(void);
void skip_to_check_2(longword p);
void goto_pointer(longword p);
char *conv_date_string (char *s, double date);

/* fio prototypes: */

void    _assign(FILE *file);
longword _tell(void);
void    _seek(long int pos);
void    _advance(word steps);
bool    _eof(void);
byte    _read_byte(void);
byte    _read_byte_late();  /* new*/
short   _read_word(void);
longword _read_long(void);
double   _read_date(void);
double   _read_double(void);
char    *_read_string(void);
char    *_read_fixed_string(int len);

void show_preamble ();
void show_warranty ();
void show_conditions ();
int  show_file (char *arg, int from, int to);

longword pointer[20];
#define P_ENTITIES  0
#define P_ENTEND    1
#define P_BLOCKSEC  2
#define P_BSEND     3

ttable table[11];
/*-----------------22.10.95 00:56-------------------
 missing tables VIEW, UCS, VPORT added             */
#define T_BLOCK     1
#define T_LAYER     2
#define T_STYLE     3
#define T_LTYPE     4
#define T_VIEW      5   /* changed */
#define T_UCS       6   /* changed */
#define T_VPORT     7
#define T_APPID     8   /* APPID section */
#define T_DIMSTYLE  9
#define T_13       10

/**************************************************************************/
/* Function definitions */
/**************************************************************************/
void *checknull (void *p)
{
    if (p== (void *) 0) {
        fprintf (stderr, "\nNull pointer - aborted.");
        abort();
    }
    return p;
}

void show_help (int full)
{
	char s[_MAX_FNAME];

    /* needs global char *execpath, usually argv[0] */
    _splitpath (execpath, NULL, NULL, s, NULL);
    printf ("%s V%s  %s\n",s,version,date);
    printf ("Copyright (C) 1995 by Frans J. Faase <faase@cs.utwente.nl>\n");
    printf ("              and Reini Urban <rurban@sbox.tu-graz.ac.at>\n");
    printf ("%s comes with ABSOLUTELY NO WARRANTY; for details type `%s w'\n",s,s);
    printf ("This is free software, and you are welcome to redistribute it\n");
    printf ("under certain conditions; type `%s c' for details.\n",s);
    printf ("type `%s g' for the explanation of `free software'.\n\n",s);
    if ( full )
    {
        printf ("Usage:\n");
        printf ("   %s [Options] [file(s)...]\n",s);
        puts (" \
Description:\n \
   Scans given AutoCAD drawing files\n \
Arguments:\n \
   infile(s)...   none or more filenames, default: *.DWG\n \
   g              description of GNU free software\n \
   c              conditions to distribute\n \
   w              warranty\n \
 Options:\n \
   /?           help\n \
   /h           only header\n \
   /t           only tables\n \
   /b           only blocks\n \
   /e           only entities\n \
\n \
   /x           kill handles\n \
   /nfilename   outname\n \
   /ppath       outpath\n \
\n \
   /s           recurse into subdirectories \n \
   /v           verbose printing\n \
   /d           debug, prints internal information too \n \
");
    }	/* end of full */
}

/*-----------------24.11.95 15:15-------------------
 added for the GNU license
--------------------------------------------------*/
int show_file (char *arg, int from, int to)
{
    FILE *f;
    char s[80];
    int i=1, line=0;
    char path[_MAX_PATH];
    char dir[_MAX_DIR],
         fname[_MAX_FNAME],
         drive[_MAX_DRIVE],
         ext[_MAX_EXT];

    /* search for the gnu license in the program dir,
       needs global char *execpath, usually argv[0] */
    _splitpath (arg, NULL, NULL, fname, ext);
    _splitpath (execpath, drive, dir, NULL, NULL);
    _makepath  (path, drive, dir, fname, ext);

    if ( (f = fopen (path,"rt")) == NULL)
    {   fprintf (stderr, "\nFile `%' not found!",s);
        return FALSE;
    }

    while ( fgets(s,sizeof(s),f) != NULL && line < from)
        line++;

    do
    {   printf (s);
        if (i >= 23)
        {   printf("... Continue with <Enter> ...\r");
            getchar();
            printf(s);
            i = 0;
        }
        i++;
        line++;
    }
    while ( fgets(s,sizeof(s),f) != NULL && line < to);
    fclose (f);
    return TRUE;
}

void show_conditions ()
{   if (show_file("LICENSE",60,258) == FALSE)
        abort();
    else
        exit(0);
}
void show_warranty ()
{   if (show_file("LICENSE",258,285) == FALSE)
        abort();
    else
        exit(0);
}
void show_preamble ()
{   if (show_file("LICENSE",0,59) == FALSE)
        abort();
    else
        exit(0);
}

void check_table_pos(char *name, ptable pt, int t_nr)
{   word t, size, nr;
    longword start;

    ACT_POS;
    t     = _read_word();
    size  = _read_word();
    nr    = _read_word();
    start = _read_long();
    DEBUG_P(("%-8s num: %3d  size: %3d  pos: (%05lx) table:%d",
            name, nr, size, start, t));
    if (t != t_nr)
    {   numerr++;
        DEBUG_P((" t %d!=%d", t, t_nr));
    }
    if (nr != (pt->nr & 0xFFFF))
    {    numerr++;
        DEBUG_P((" nr %d!=%ld", nr, pt->nr));
    }
    if (size != pt->size)
    {   numerr++;
        DEBUG_P((" size %d!=%d", size, pt->size));
    }
    if (start != pt->start)
    {   numerr++;
        DEBUG_P((" start %ld!=%ld", start, pt->start));
    }
}


void scan_table_position(char *name, ptable pt)
{
    ACT_POS;
    pt->size  = _read_word();
    pt->nr    = _read_long();
    pt->start = _read_long();
    DEBUG_P(("%-8s num: %3ld size: %3d (%05lX-%05lX)",
         name,
         pt->nr, pt->size,
         pt->start,
         pt->start + pt->size * pt->nr));
}


/* 00 00 00 00*/
void print_bytes (void *buf, int len)
{   int i;
    char *p;

    p = buf;
    DEBUG_P(("["));
    for ( i=0; i<len; i++ )
    {
        if (i > 0 && i % 4 == 0)
            DEBUG_P(("³ "));
        DEBUG_P(("%02X ", *p));
        p++;
    }
    DEBUG_P(("] "));
}

void print_double (char *str)
{
    static double d;
    long pos;

    pos = cur_pos;
    d = _read_double();
    DEBUG_P( ("\n[%05lX]: %-10s %f  ", pos, str, d));
    if (debug) print_bytes (&d, 8);
}

void print_2dpoint (char *str)
{
    static double d1,d2;
    long pos;

    pos = cur_pos;
    d1 = _read_double(); d2 = _read_double();
    DEBUG_P(("\n[%05lX]: %-10s (%f,%f)  ", pos,str,d1,d2));
    if (debug)
    {   print_bytes (&d1, 8); print_bytes (&d2, 8);
    }
}
void print_3dpoint (char *str)
{
    static double d1,d2,d3;
    long pos;

    pos = cur_pos;
    d1 = _read_double(); d2 = _read_double(); d3 = _read_double();
    DEBUG_P(("\n[%05lX]: %-10s (%f,%f,%f)  ",pos,str,d1,d2,d3));
    if (debug)
    {   print_bytes (&d1, 8); print_bytes (&d2, 8); print_bytes (&d3, 8);
    }
}

void scan_header()
{   char tmpstr[40];
    double date;
    int i;
    longword tmp;

    DEBUG_P(("HEADER\n"));
    KNOWN_3DPOINT ("$INSBASE");
    UNKNOWN_WORD;
    KNOWN_3DPOINT ("$EXTMIN");
    KNOWN_3DPOINT ("$EXTMAX");
    KNOWN_2DPOINT ("$LIMMIN");
    KNOWN_2DPOINT ("$LIMMAX");
    KNOWN_2DPOINT ("$VIEWCTR (*12)");
    UNKNOWN_DOUBLE;
    KNOWN_DOUBLE  ("$VIEWSIZE (*40)");
    KNOWN_WORD    ("$SNAPMODE");
    KNOWN_2DPOINT ("$SNAPUNIT (*14)");
    for ( i=0; i<15; i++ )
        UNKNOWN_WORD;
    KNOWN_2DPOINT ("$GRIDUNIT (*15)");  /*new*/
    KNOWN_WORD    ("$ORTHOMODE"); /* new*/
    KNOWN_WORD    ("$HANDLES"); /* new*/
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    KNOWN_WORD    ("$DRAGMODE");   /*new*/
    KNOWN_DOUBLE  ("$LTSCALE");
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    KNOWN_DOUBLE  ("$TRACEWID");
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_DOUBLE;
    UNKNOWN_WORD;
    KNOWN_DOUBLE ("VIEW: ASPECTRATIO (*41)");  /*at 15c*/
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    KNOWN_2DPOINT ("$AXISUNIT");
    KNOWN_DOUBLE  ("$SKETCHINC");
    KNOWN_DOUBLE  ("$FILLETRAD");
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    /* menuname: length of pathstr 128 ?? */
    DEBUG_P(("\n$MENUNAME: %s ", _read_fixed_string(144))); /*at 0x194*/
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_BYTE;
    KNOWN_DOUBLE  ("$ELEVATION");
    KNOWN_DOUBLE  ("$THICKNESS");

    UNKNOWN_DOUBLE;
    UNKNOWN_DOUBLE;
    UNKNOWN_DOUBLE;

    echo_bytes(149);
    /*echo_bytes(0x2f6 - cur_pos - 16);*/
    /*echo_bytes((9*16)+4+12+8);*/
    KNOWN_DOUBLE  ("$DIMRND");
    KNOWN_DOUBLE  ("$DIMDLE");
    DEBUG_P(("\n$DIMBLK: %s ", _read_fixed_string(32)));    /*at 0x2f6*/
    UNKNOWN_BYTE;
    UNKNOWN_BYTE;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_BYTE;
    UNKNOWN_BYTE;
    UNKNOWN_BYTE;
    date = _read_date();
    DEBUG_P(("\n$TDCREATE: %.8f \"%s\" ", date, conv_date_string(tmpstr,date)));/*at 0x31f*/
    date = _read_date();
    DEBUG_P(("\n$TDUPDATE: %.8f \"%s\" ", date, conv_date_string(tmpstr,date)));/*at 0x327*/
/*      ACT_POS;   32F*/
    UNKNOWN_LONG;
    UNKNOWN_LONG;   /* TDINDWG format ? */
    UNKNOWN_LONG;
    UNKNOWN_LONG;
    for ( i=0; i<10; i++ )
        UNKNOWN_WORD;
    KNOWN_DOUBLE  ("$ANGBASE"); /* 353*/
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    KNOWN_DOUBLE  ("$PDSIZE");
    KNOWN_DOUBLE  ("$PLINEWID");
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;

/*      echo_bytes(0x379 - cur_pos);*/
/*      ACT_POS;*/
/*      goto_pointer(0x379);*/
    UNKNOWN_DOUBLE;   /*??*/

    echo_bytes(68);
/*      ACT_POS;*/
/*      goto_pointer(0x3c5);*/
    KNOWN_DOUBLE  ("$DIMALTF");
    KNOWN_DOUBLE  ("$DIMLFAC");
    KNOWN_WORD    ("$SPLINESEGS");
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    UNKNOWN_WORD;
    /*ACT_POS;*/
    KNOWN_DOUBLE  ("$CHAMFERA");
    KNOWN_DOUBLE  ("$CHAMFERB");
    UNKNOWN_WORD;
    DEBUG_P(("\n"));

    /*echo_bytes(pointer[P_ENTITIES]-cur_pos);*/
    /*goto_pointer(pointer[P_ENTITIES]);*/
}

void scan_block_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: BLOCK TABLE\n",pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        byte flag, b1, b2;
        word used, w1, w2, w3, w4;

        goto_pointer(begin);
        flag = _read_byte();
        scan_fixed_ident(32);
        used = _read_word();
        b1 = _read_byte();
        w1 = _read_word();
        b2 = _read_byte();
        w3 = _read_word();
        w4 = _read_word();

        DEBUG_P(("%02x %5d   %3d %5d %3d %5d %5d %5d",
               flag, used, b1, w1, b2, w2, w3, w4));


        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}

void scan_layer_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: LAYER TABLE\n",pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        int flag,
            used,
            color,
            style;

        goto_pointer(begin);

        flag  = _read_byte();
        scan_fixed_ident(32);
        used  = _read_word();
        color = _read_word();
        style = _read_word();
        DEBUG_P(("%6d %s %s %s %s color: %3d ltype: %3d ",
               used,
               color < 0 ? "off" : "on ",
               flag & 1 ? "fr" : "  ",
               flag & 4 ? "lc" : "  ",
               flag & 64 ? "    " : "used",
               color < 0 ? -color : color,
               style));

        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}

void scan_style_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: STYLE TABLE\n",pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;
        int flag;

        goto_pointer(begin);
        SCAN_BYTE (70);

        /*KNOWN_STRING ("name",32);*/
        SCAN_FIXED_STRING (1,32);
        /*scan_fixed_ident(32);*/
        SCAN_WORD(0);
        SCAN_3DPOINT(0);
        SCAN_BYTE(0);
        SCAN_DOUBLE(0);
        SCAN_FIXED_STRING (1,128);

        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}

void scan_ltype_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: LTYPE TABLE\n"));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;
        char *s;

        goto_pointer(begin);
        DEBUG_P(("[%05X]: ",begin));
        SCAN_BYTE(0);
        scan_fixed_ident(32);
        SCAN_WORD(0);
        SCAN_FIXED_STRING (1,48);
/*      s = _read_fixed_string(48);*/
/*        DEBUG_P(("`%s' ", s));*/
        SCAN_BYTE(0);
        {   word j,
                 k = _read_byte();

            for (j = 0; j < 13; j++)
            {   double d = _read_double();
                DEBUG_P(("%s%f ", j < k ? "" : "*", d));
            }
        }
        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}

void scan_ads_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: APPID TABLE\n",pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        SCAN_BYTE (0);
        scan_fixed_ident(32);
        SCAN_WORD (0);
        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}


void scan_dim_table(ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: DIMSTYLE TABLE\n",pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        int flag;

        goto_pointer(begin);

        SCAN_BYTE (70);

        scan_fixed_ident(32);
        SCAN_WORD (0);

        DEBUG_P( ("\n"));
        SCAN_DOUBLE(40);   /* DIMSCALE:  */
        SCAN_DOUBLE(41);   /* DIMASZ:    */
        SCAN_DOUBLE(42);   /* DIMEXO:    */
        SCAN_DOUBLE(43);   /* DIMDLI:    */
        SCAN_DOUBLE(44);   /* DIMEXE:    */
        SCAN_DOUBLE(45);   /* DIMRND:    */
        SCAN_DOUBLE(46);   /* DIMDLE:    */
        SCAN_DOUBLE(47);   /* DIMTP:     */
        SCAN_DOUBLE(48);   /* DIMTM:     */
        DEBUG_P( ("\n"));
        SCAN_DOUBLE(140);  /* DIMTXT :   */
        SCAN_DOUBLE(141);  /* DIMCEN :   */
        SCAN_DOUBLE(142);  /* DIMTSZ :   */
        SCAN_DOUBLE(143);  /* DIMALTF:   */
        SCAN_DOUBLE(144);  /* DIMLFAC:   */
        SCAN_DOUBLE(145);  /* DIMTVP :   */
        DEBUG_P( ("\n"));
        SCAN_BYTE (71);
        SCAN_BYTE (72);
        SCAN_BYTE (73);
        SCAN_BYTE (74);
        SCAN_BYTE (75);
        SCAN_BYTE (76);
        SCAN_BYTE (77);
        SCAN_BYTE (78);
        DEBUG_P( ("\n"));
        SCAN_BYTE (170);
        SCAN_BYTE (171);
        SCAN_BYTE (172);
        SCAN_BYTE (173);
        SCAN_BYTE (174);  /*?*/
        SCAN_BYTE (175);  /*?*/

        SCAN_FIXED_STRING (3,16);  /* length ? */
        SCAN_FIXED_STRING (4,16);  /* length ? */
        SCAN_FIXED_STRING (5,32);
        SCAN_FIXED_STRING (6,32);  /*wrong*/
        SCAN_FIXED_STRING (7,32);  /*wrong*/

        echo_bytes (3);   /*?*/
        SCAN_WORD (176);  /* Colors as int ? */
        SCAN_WORD (177);
        SCAN_WORD (178);
        SCAN_DOUBLE(146);  /* DIMTFAC  */
        SCAN_DOUBLE(147);  /* DIMDIST  */

        skip_to_check_2(end);
    }
    if (pt->nr > 0) get_check_32();
}

void scan_view_table (ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: VIEW TABLE\n", pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        SCAN_BYTE (70);

        scan_fixed_ident(32);
        SCAN_WORD (0);
        SCAN_DOUBLE(40);
        SCAN_2DPOINT (10);
        SCAN_DOUBLE(41);
        SCAN_3DPOINT (11);
        SCAN_3DPOINT (12);

        SCAN_WORD(71);  /*?*/

        SCAN_DOUBLE(42);
        SCAN_DOUBLE(43);
        SCAN_DOUBLE(44);
        SCAN_DOUBLE(50);

        echo_bytes(2);  /*?*/

        skip_to_check_2(end);
    }
    get_check_32();
}

void scan_ucs_table (ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: UCS TABLE\n", pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        SCAN_BYTE (70);

        scan_fixed_ident(32);
        SCAN_WORD (0);
        SCAN_3DPOINT (10);
        SCAN_3DPOINT (11);
        SCAN_3DPOINT (12);

        skip_to_check_2(end);
    }
    get_check_32();
}

void scan_vport_table (ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: VPORT TABLE\n", pt->start));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;

        goto_pointer(begin);
        SCAN_BYTE (70);

        scan_fixed_ident(32);
        SCAN_WORD (0);
        SCAN_2DPOINT (10); /*ok*/
        SCAN_2DPOINT (11); /*ok*/
        DEBUG_P( ("\n"));
        SCAN_3DPOINT (17);  /*ok*/
        SCAN_3DPOINT (16);  /*ok*/
        /*43-51*/
        DEBUG_P( ("\n"));
        SCAN_DOUBLE(50);      /*wrong*/
        SCAN_DOUBLE(40);      /*ok*/
        SCAN_2DPOINT (12);    /*ok*/

        DEBUG_P( ("\n"));
        SCAN_DOUBLE(41);      /*ok*/
        SCAN_DOUBLE(42);      /*ok*/
        SCAN_DOUBLE(43);      /*ok*/
        SCAN_DOUBLE(44);      /*ok*/
        DEBUG_P( ("\n"));
        SCAN_WORD(71);        /*ok*/
        SCAN_WORD(72);
        SCAN_WORD(73);
        SCAN_WORD(74);
        SCAN_WORD(75);
        SCAN_WORD(76);
        SCAN_WORD(77);
        SCAN_WORD(78);        /*ok*/
        DEBUG_P( ("\n"));
        SCAN_DOUBLE(51);      /*wrong*/

        DEBUG_P( ("\n"));
        SCAN_2DPOINT (13); /*ok*/
        SCAN_2DPOINT (14); /*ok*/
        SCAN_2DPOINT (15); /*ok*/

        skip_to_check_2(end);
    }
    get_check_32();
}



void scan_table(char *name, ptable pt)
{   longword i;

    if ( pt->start == 0 ) return;
    DEBUG_P(("\n[%05lX]: %s\n", pt->start,name));

    for (i = 0L; i < pt->nr; i++)
    {   longword begin = pt->start + i * pt->size,
                 end = begin + pt->size - 2;
        int flag;

        goto_pointer(begin);
        SCAN_BYTE (70);

        scan_fixed_ident(32);
        SCAN_WORD (0);

        skip_to_check_2(end);
    }
    get_check_32();
}


#define NR_ENT 25


struct {
  char *name;
  int  kn_flag,
       kn_opts;
} ent_type[NR_ENT] = {
   { NULL, 0, 0 },
   { "LINE   ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "POINT  ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "CIRCLE ", 0x6F /* 1 2 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "SHAPE  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { NULL, 0, 0 },
   { NULL, 0, 0 },
   { "TEXT   ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x17B/* 1 2 + 8 10 20 40 ++      50 41 51 7 71 72 72p ? 73
                                                        1 . . . .. .. .. .. */},
   { "ARC    ", 0x6D /* 1 . 4 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { "TRACE  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "SOLID  ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x03 /* 1 2 . . .. .. .. .. */},
   /*-----------------23.10.95 12:59-------------------
    opts changed
   --------------------------------------------------*/
   { "BLOCK  ", 0    /* . . . . .. .. .. .. */, 0x06 /* . 2 4 . .. .. .. .. */},
   { "ENDBLK ", 0, 0x004 },
   { "INSERT ", 0xA2 /* . 2 . . .. 20 .. 80 */, 0x0F /* 1 2 4 8 .. .. .. .. */},
   { "ATTDEF ", 0x6B /* 1 2 . 8 .. 20 40 .. */, 0x030/* + + + + 10 20 ++ ++      73 50 41 51 7 71 72 72'
                                                        1 . . . .. .. .. .. */},
   { "ATTRIB ", 0x22 /* . . . . .. .. .. .. */, 0x116/* + 2 4 + 10 ++ ++ ++
                                                        1 . . . .. .. .. .. */},
                     /* 1 */
   { "SEQEND ", 0x22 /* . 2 . . .. 20 .. .. */, 0x00 /* . . . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "PLINE  ", 0xA2 /* . 2 . . .. 20 .. 80 */, 0x01 /* 1 . . . .. .. .. .. */},
   { "VERTEX ", 0x22 /* . 2 . . .. 20 .. .. */, 0x00 /* + + . . .. .. .. .. */},
   { NULL, 0, 0 },
   { "3DFACE ", 0x26 /* . 2 . . .. 20 .. .. */, 0x00 /* . . . . .. .. .. .. */},
   { "DIM    ", 0x22 /* . . . . .. .. .. .. */, 0x11A/* + 2 + 8 10 ++ ++ ++
                                                        1 . . . .. .. .. .. */},
   { "VPORT  ", 0x63 /* 1 2 . . .. 20 40 .. */, 0x00 /* . . . . .. .. .. .. */},
};


void scan_entities(longword start, longword end, longword sce_offset)
{ long s_comp_ent = 0L;
  DEBUG_P(("\n[%05lX]: ENTITIES\n",start));
  goto_pointer(start);

  if ( start == end ) return;
  while (cur_pos < end - 32L)
  {    word kind,
            flag,
            length,
            layer,
            opts,
            color = 0,
            extra = 0;
       longword b_pos = cur_pos,
                pos = cur_pos;

       /*DEBUG_P(("cur_pos %ld end %ld\n", cur_pos, end));*/

       DEBUG_P(("[%05lX]", cur_pos));

       kind = _read_byte();

       flag = _read_byte();

       length = _read_word();
       pos += (long)(length - 2);
       if (pos > end - 2)
          pos = end - 2;

       /* DEBUG_P(("cur_pos %ld, till %ld, end %ld", cur_pos, pos, end)); */

       if (kind < NR_ENT && ent_type[kind].name != NULL)
            { DEBUG_P(("%s %02X ", ent_type[kind].name, (int)flag));
              if (flag & ~ent_type[kind].kn_flag)
			  {
				 numunknown++;
                 DEBUG_P(("!%02X", flag & ~ent_type[kind].kn_flag));
			  }
            }
       else
	   {
			numunknown++;
            DEBUG_P(("ENT_%d! %02X (%d) ", (int)kind, (int)flag, length));
	   }
       /*ent_8F: 3*double,  6-7 byte*/

       layer = _read_word();

       opts = _read_word();

       DEBUG_P(("l:%d %04X ", layer, opts));
       if (   ent_type[kind].name != NULL
           && opts & ~ent_type[kind].kn_opts)
	   {
		  numunknown++;
          DEBUG_P(("!%02X", opts & ~ent_type[kind].kn_opts));
	   }

       if (flag & 1)
          color = _read_byte();

       DEBUG_P(("c:%d ", color));

       if (flag & 0x40)
          extra = _read_byte();

       if (extra & ~6)           /* . 2 4 . .. .. .. .. */
	   {
		  numunknown++;
          DEBUG_P(("!(extra)"));
	   }

       if (extra & 2)
          scan_xdata();

       if (flag & 2)
          SCAN_WORD (0);    /*type*/
          /*DEBUG_P((" TYPE:%d ", _read_word()));*/

       if (flag & 4 && kind > 2 && kind != 22)
          { DEBUG_P((" z:"));
            SCAN_DOUBLE (0);
          }

       if (flag & 8)
          { DEBUG_P((" TH:"));
            SCAN_DOUBLE (0);
          }

       if (flag & 0x20)
          scan_handle();

       if (extra & 4)
       {
          DEBUG_P((" PAPER:"));
          SCAN_WORD (0);    /*paper*/
       }

       switch (kind)

       { case 1:   /* LINE */

                    SCAN_POINT(10, !(flag & 4));
                    SCAN_POINT(11, !(flag & 4));
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 2:  /* POINT */

                    SCAN_POINT(10, !(flag & 4));
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 3:  /* CIRCLE */

                    SCAN_2DPOINT (10);
                    SCAN_DOUBLE(40);
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 4:  /* SHAPE */

                    SCAN_2DPOINT (10);
                    SCAN_WORD(2);
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 7: /* TEXT */

                    SCAN_2DPOINT (10);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
            OPTS(1) SCAN_DOUBLE(50);
            OPTS(2) SCAN_DOUBLE(41);
            OPTS(3) SCAN_DOUBLE(51);             /*?*/
            OPTS(4) SCAN_BYTE(7);
            OPTS(5) SCAN_BYTE(71);
            OPTS(6) SCAN_BYTE(72);
            OPTS(7) SCAN_2DPOINT (11);
            OPTS(9) SCAN_BYTE(73);

            break;

         case 8:  /* ARC */

                    SCAN_2DPOINT (10);
                    SCAN_DOUBLE(40);
                    SCAN_DOUBLE(50);
                    SCAN_DOUBLE(51);
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 9:   /* TRACE */

                    SCAN_2DPOINT (10);
                    SCAN_2DPOINT (11);
                    SCAN_2DPOINT (12);
                    SCAN_2DPOINT (13);
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;

         case 11:   /* SOLID */

                    SCAN_2DPOINT (11);
                    SCAN_2DPOINT (12);
                    SCAN_2DPOINT (13);
                    SCAN_2DPOINT (14);
            OPTS(1) SCAN_3DPOINT (210);
            OPTS(2) SCAN_DOUBLE(38);

            break;


         case 12:   /* BLOCK */

                    SCAN_2DPOINT (10);   /*?*/
                    SCAN_STRING(1);
            OPTS(2) SCAN_STRING(3);         /* nur bei opts 2 */

            break;

         case 13:  /* ENDBLK */

            break;

         case 14:   /* INSERT */

            s_comp_ent = b_pos - sce_offset;

                    SCAN_WORD(1);
                    SCAN_2DPOINT (10);
            OPTS(1) SCAN_DOUBLE(41);
            OPTS(2) SCAN_DOUBLE(42);
            OPTS(3) SCAN_DOUBLE(43);
            OPTS(4) SCAN_DOUBLE(50);
            OPTS(5) SCAN_BYTE(70);                            /*?*/
            OPTS(6) SCAN_BYTE(71);                            /*?*/
            OPTS(7) SCAN_DOUBLE(44);                          /*?*/
            OPTS(8) SCAN_DOUBLE(45);                          /*?*/

            break;

         case 15:    /* ATTDEF */

                    SCAN_2DPOINT (10);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
                    SCAN_STRING(3);
                    SCAN_STRING(2);
                    SCAN_BYTE(70);
            OPTS(1) SCAN_BYTE(73);           /*?*/
            OPTS(2) SCAN_DOUBLE(50);         /*?*/
            OPTS(3) SCAN_DOUBLE(41);
            OPTS(4) SCAN_DOUBLE(42);
            OPTS(5) SCAN_BYTE(7);
            OPTS(6) SCAN_BYTE(71);
            OPTS(7) SCAN_BYTE(72);
            OPTS(8) SCAN_2DPOINT (11);   /*?*/
            OPTS(9) SCAN_3DPOINT (210);
            OPTS(10) SCAN_DOUBLE(38);        /*?*/

            break;

         case 16:   /* ATTRIB */

                    SCAN_2DPOINT (10);
                    SCAN_DOUBLE(40);
                    SCAN_STRING(1);
                    SCAN_STRING(2);
                    SCAN_BYTE(70);
            OPTS(1) SCAN_BYTE(73);           /*?*/
            OPTS(2) SCAN_DOUBLE(50);         /*?*/
            OPTS(3) SCAN_DOUBLE(41);
            OPTS(4) SCAN_DOUBLE(42);
            OPTS(5) SCAN_BYTE(7);
            OPTS(6) SCAN_BYTE(71);
            OPTS(7) SCAN_BYTE(72);
            OPTS(8) SCAN_2DPOINT (11);   /*?*/
            OPTS(9) SCAN_3DPOINT (210);
            OPTS(10) SCAN_DOUBLE(38);        /*?*/

            break;

         case 17:   /* SEQEND */
            /* pointer to main entity: */
            /* For the entities the pointer is absolute, for the blocks
              it seems relative to the start minus 0x4000000. */
            {   longword p = _read_long();
                DEBUG_P ((" -1: %ld ",p));
                if (p != s_comp_ent)
				{
		  			numunknown++;
                    DEBUG_P((" != %04lX", s_comp_ent));
				}
            }
            break;

         case 19:   /* PLINE */
            s_comp_ent = b_pos - sce_offset;

            OPTS(1) SCAN_BYTE(70);
            OPTS(2) SCAN_DOUBLE(40);                   /*?*/
            OPTS(3) SCAN_BYTE(71);                     /*?*/
            OPTS(4) SCAN_BYTE(72);                     /*?*/
            OPTS(5) SCAN_BYTE(73);                     /*?*/
            OPTS(6) SCAN_BYTE(74);                     /*?*/
            OPTS(7) SCAN_BYTE(75);                     /*?*/

            break;

         case 20:   /* VERTEX */

                    SCAN_2DPOINT (10);
            OPTS(1) SCAN_DOUBLE(40);           /*?*/
            OPTS(2) SCAN_DOUBLE(41);           /*?*/
            OPTS(3) SCAN_BYTE(70);             /*?*/
            OPTS(4) SCAN_DOUBLE(50);           /*?*/

            break;

         case 22:   /* 3DFACE */

                    SCAN_POINT(10, !(flag & 4));
                    SCAN_POINT(11, !(flag & 4));
                    SCAN_POINT(12, !(flag & 4));
                    SCAN_POINT(13, !(flag & 4));

            break;

         case 23:   /* DIM */

                    SCAN_WORD(1);
                    SCAN_3DPOINT (10);
                    SCAN_2DPOINT (11);   /*?*/
            OPTS(2) SCAN_BYTE(70);
            OPTS(1) SCAN_3DPOINT (12);    /*?*/
            OPTS(3) SCAN_STRING(1);
            OPTS(4) SCAN_3DPOINT (13);
            OPTS(5) SCAN_3DPOINT (14);
            OPTS(6) SCAN_3DPOINT (15);
            OPTS(7) SCAN_3DPOINT (16);
            OPTS(8) SCAN_DOUBLE(40);
            OPTS(9) SCAN_DOUBLE(50);
            OPTS(10) SCAN_DOUBLE(51);
            OPTS(11) SCAN_DOUBLE(52);
            OPTS(12) SCAN_DOUBLE(53);

            break;

         case 24:   /* VPORT */

                    SCAN_3DPOINT (10);
                    SCAN_DOUBLE(40);
                    SCAN_DOUBLE(41);
                    SCAN_WORD(68);

            break;
       }

       skip_to_check_2(pos);
  }

  if (end > start)  /* AC1004 had no CRC32 */
  { goto_pointer (end);
    get_check_32();
  }
}

void scan_xdata()
{ int xd_length = _read_word(),
      i;
  bool go = TRUE;
  DEBUG_P(("XDATA %d: (", xd_length));

  while (xd_length > 0 && go)
    { int c = _read_byte();
      xd_length--;
      DEBUG_P((" %d:", c));
      switch (c)
      { case  0:
           if   (xd_length < 0)
                go = FALSE;
           else
                { int l = _read_byte();
                  xd_length--;

                  if (version_13)
                     { _read_byte();
                       xd_length--;
                     }
                  if   (l <= xd_length)
                       { SCAN_FIXED_IDENT (c,l);
                         /*DEBUG_P(("`%s'", _read_fixed_string(l)));*/
                         xd_length -= l;
                       }
                  else
                       go = FALSE;
                }
           break;
        case  1:
        case  3:
        case  70:
           if   (xd_length < 2)
                go = FALSE;
           else
                {
                  SCAN_WORD (c);
                  xd_length -= 2;
                }
           break;
        case 2:
           if   (xd_length < 1)
                go = FALSE;
           else
                { byte b=_read_byte();
                  DEBUG_P(("%c", c == 0 ? '{' : '}'));
                  xd_length--;
                }
           break;
        case 5:
           if   (xd_length < 8)
                go = FALSE;
           else
                { longword l1,l2;
                  l1=_read_long();
                  l2=_read_long();
                  if ( handles || debug )
                  {
                    printf("1005:<%08lX:%08lX>", l1, l2);
                  }
                  xd_length -= 8;
                }
           break;
        case 40:
        case 41:
        case 42:
           if   (xd_length < 8)
                go = FALSE;
           else
                { SCAN_DOUBLE (c);
                  xd_length -= 8;
                }
           break;
        case 10:
        case 11:
        case 12:
           if   (xd_length < 24)
                go = FALSE;
           else
                { scan_point(TRUE);
                  xd_length -= 24;
                }
           break;
        case 71:
           if   (xd_length < 4)
                go = FALSE;
           else
                { SCAN_LONG (c);
                  xd_length -= 4;
                }
           break;
        default:
           go = FALSE;
      }
    }
  if (!go)
  {
	 numerr++;
     DEBUG_P(("!"));
  }

  for (i = 0; i < xd_length; i++)
      { int w = _read_byte();
        if (w < 0) w += 256;
        DEBUG_P(("%02X ", w));
      }
  DEBUG_P((") "));
}

/*-----------------24.11.95 13:04-------------------
  reset handle to 0 with option /x
  won't kill it, don't wanna update the pointers so far
--------------------------------------------------*/
void scan_handle()
{ int n = _read_byte(),
      i;
  VERBOSE_P (("\nKill handle "));
  if ( handles || debug )
    printf(" 5:");
  for (i = 0; i < n; i++)
  {    int w = _read_byte_late();
       if (w < 0) w += 256;
       DEBUG_P (("%02X", w));
       if ( handles )
       {   if ( w != 0 )
		   	  numchg++;
           if ( fout ) fputc (0,fout);          /* write out 0 */
	   }
  }
}


void echo_16_bytes(word len)
{   int i;
    unsigned char buffer[16];

    DEBUG_P(("[%05lX]  ", cur_pos));
    for (i = 0; i < len; i++)
        buffer[i] = _read_byte();


    for (i = 0; i < 16; i++)
    {   if (i > 0 && i % 4 == 0)
        DEBUG_P(("³ "));
        if (i < len)
        {   int w = buffer[i];
            if (w < 0)
                w += 256;
            DEBUG_P(("%02X ", w));
        }
        else
            DEBUG_P(("   "));
    }
    DEBUG_P(("  "));

    for (i = 0; i < len; i++)
    {
        if (buffer[i] < 32)
            DEBUG_P(("."));
        else
            DEBUG_P(("%c", buffer[i]));
    }
    DEBUG_P(("\n"));
}

void echo_bytes(long int len)
{ DEBUG_P(("\ndata: %ld\n", len));
  while(len > 0)
    { int len16 = (int)MIN(len, 16);
      echo_16_bytes(len16);
      len -= (longword)len16;
    }
}


void scan_point(bool z_coord)
{   double x = _read_double(),
           y = _read_double(),
           z = z_coord ? _read_double() : 0.0;
    if (z_coord)
        DEBUG_P(("(%f,%f,%f)", x, y, z));
    else
        DEBUG_P(("(%f,%f)", x, y));
}


void scan_fixed_ident(int len)
{   char *s = _read_fixed_string(len);
    if (version_13)
        s++;
    DEBUG_P(("`%-*.*s'", len, len, s));
}

longword get_pointer()
{ longword result = _read_long();
  DEBUG_P(("(pointer: %05lX)\n", result));
  return result;
}


void check_pointer(longword p)
{   longword l = _read_long();
    DEBUG_P(("(pointer: %05lX) %s\n", p, l == p ? "Correct" : "Wrong"));
    if (l == p ) numerr++;
}

void get_check_2()
{   word w = _read_word();
    DEBUG_P(("(check_2: %04X)\n",w));
}

void get_check_32()
{   longword crc = _read_long();
    DEBUG_P(("(check_32) "));
    print_bytes (&crc,32);
}

void skip_to_check_2(longword pos)
{
    if (cur_pos < pos)
    {  DEBUG_P(("!\n"));
       numerr++;
       echo_bytes(pos - cur_pos);
    }
    if (cur_pos > pos)
    {   DEBUG_P(("!%05lX>", cur_pos - pos));
        numerr++;
        _seek(pos);
    }
    get_check_2();
}

void goto_pointer(longword pos)
{
    if (cur_pos != pos)
    {   DEBUG_P(("!(pointer (%05lX), act: %05lx, diff: %ld)\n", pos, cur_pos, pos-cur_pos));  /* /n inserted */
        numerr++;
        _seek(pos);
    }
}


/************** File IO *************************/

static FILE *f = NULL;

#define MAX_STRING_LENGTH 1000
static char str_buffer[MAX_STRING_LENGTH];

#define GETBYTE (byte)fgetc(fin)

void _assign(file)
FILE *file;
{ fin = file;
  cur_pos = 0;

#ifdef __GNUC__                 /* GNU bug */
  last_pos = fseek (fin, 0L, SEEK_END);
#else
  fseek (fin, 0L, SEEK_END);
  last_pos = (longword) ftell (fin);
  DEBUG_P (("\nfilesize: 0x%lX",last_pos));
#endif

  fseek(fin, 0L, SEEK_SET);
}

void _advance(word len)
{
    if ( len == 0 )
        return;
    if ( len > last_pos-cur_pos )
    {
        VERBOSE_P (("\nerror: wrong pointer at %05lX",cur_pos));
        return;
    }
    if ( fout )     /* output support */
    {
        if ( len > MAX_STRING_LENGTH)
        {
            void *buf;
            /*buf = checknull(malloc(len));*/
            buf = MALLOC (buf,len);
            fread  (buf, len, 1, fin);
            fwrite (buf, len, 1, fout);
            free(buf);
        }
        else
        {
            fread  (str_buffer, len, 1, fin);
            fwrite (str_buffer, len, 1, fout);
        }
    }
    else
        fseek(fin, len, SEEK_CUR);

    cur_pos += len;
}

longword _tell()
{
    return cur_pos;
}

/* had to be changed for output reasons*/
void _seek(long int pos)
{
    if ( pos == cur_pos )
      return;

    if ( pos < cur_pos )     /* go back (easy)*/
    {
        VERBOSE_P (("\nerror: unwriting %ld bytes at %05lX",cur_pos-pos,cur_pos));
        if (fout) fseek (fout, pos, SEEK_SET);
    }
    else
    if ( pos <= last_pos )
    {
        VERBOSE_P (("\nerror: skipping %ld bytes at %05lX",pos-cur_pos,cur_pos));
        _advance (pos-cur_pos);
    }
    else
    {
        VERBOSE_P (("\nerror: wrong pointer at %05lX",cur_pos));
        numerr++;
    }

    cur_pos = pos;
}


bool _eof()
{
    return cur_pos >= last_pos;
}

byte _read_byte()
{   byte c;
    cur_pos++;
    c = GETBYTE;
    if (fout) fputc ((int) c, fout);
    return c;
}
/* with later output (due to changes)*/
byte _read_byte_late()
{   cur_pos++;
    return GETBYTE;
}

short _read_word()
{   short result;

    *((byte *)&result) = GETBYTE;
    *((byte *)&result + 1) = GETBYTE;

    cur_pos += 2;
    if (fout) fwrite (&result, sizeof(result), 1, fout);

    return result;
}

longword _read_long()
{   longword result;

    {   short i;
        byte *p = (byte *)&result;

        for (i = 0; i < sizeof(longword); i++)
            *p++ = GETBYTE;
    }
    cur_pos += sizeof(longword);
    fwrite (&result, sizeof(result), 1, fout);

    return result;
}

double _read_date()
{
    longword l1,l2;
    double d1;

    /* <julian date> . <daytime ratio>*/
    /* seconds = 86400 * <daytime ratio>*/
    l1 = _read_long();
    l2 = _read_long();
    print_bytes (&l1, 4);
    print_bytes (&l2, 4);

    return (double)l1 + (double)l2 / 86400000.0;
}


double _read_double()
{   double result;

    {   short i;
        byte *p = (byte *)&result;

        for (i = 0; i < sizeof(double); i++)
            *p++ = GETBYTE;
    }
    cur_pos += sizeof(double);
    if (fout) fwrite (&result, sizeof(result), 1, fout);

    return result;
}

char *_read_string()
{
    return _read_fixed_string(_read_word());
}

char *_read_fixed_string(len)
int len;
{   int i;
    char *str = str_buffer;

    for (i = 0; i < len; i++)
         *str++ = GETBYTE;
    *str = '\0';
    cur_pos += len;
    if (fout) fwrite (str_buffer, len, 1, fout);

    return str_buffer;
}

/*-----------------25.10.95 21:47-------------------
  conversion of julian date into calendar date
  returns formatted date output
  from "Numerical Recipes in Pascal", p.12
  room for s must be allocated!
--------------------------------------------------*/
char *conv_date_string (char *s, double date)
{   double t,ss;
    int y,m,d,hh,mm;
    long ja,jalpha,jb,jc,jd,je;

#define TRUNC(n) (long) floor(n)

    t = 86400*(date-floor(date));    /* in seconds*/
    date = floor (date);
    if ( date>2299161.0 )
    {   jalpha = TRUNC(((date-1867216)-0.25)/36524.25);
        ja = (long) (date+1+jalpha-TRUNC(0.25*jalpha));
    } else ja = (long)date;
    jb = ja+1524;
    jc = TRUNC(6680.0+((jb-2439870)-122.1)/365.25);
    jd = 365*jc+TRUNC(0.25*jc);
    je = TRUNC((jb-jd)/30.6001);
    d = (int) (jb-jd-TRUNC(30.6001*je));

    m = (int) (je-1);
    if ( m>12 ) mm -= 12;
    y = (int) (jc-4715);
    if ( m>2 ) y--;
    if ( y<=0 ) y--;

    hh = (int) floor(t/3600.0);
    t -= hh*3600.0;
    mm = (int) floor(t/60.0);
    ss = t- (mm*60.0);

    sprintf(s, "%02d.%02d.%4d  %02d:%02d:%05.2f", d,m,y,hh,mm,ss);
    return s;
}


/* new: 07.11.95 13:22 */
char *force_ext (char *s, char *old, char *ext)
{
    char path[_MAX_PATH];
    char dir[_MAX_DIR],
         fname[_MAX_FNAME],
         drive[_MAX_DRIVE];

    _splitpath (old,drive,dir,fname,NULL);
    _makepath  (s,drive,dir,fname,ext);

    return s;
}

/* check() for each file matching the argument*/
void doit (char *arg)
{
    DIR     *dirp;
    struct dirent *dd;
    char    path [_MAX_PATH];
    char    dir  [_MAX_DIR],
            fname[_MAX_FNAME],
            drive[_MAX_DRIVE],
            ext  [_MAX_EXT];

    static char *tmpdir; /* dynamic paths*/
    static char tmpfn[_MAX_FNAME],
                tmpext[_MAX_EXT];    /* to keep the stack smaller*/

/* note: POSIX compliant directory handling with*/
/* opendir(),readdir(), instead of the old dos_findfirst()*/
/* Borland guys:
   make a #ifdef __BORLANDC__ and do it with _dos_findfirst()
   and send me working copy
*/
    DEBUG_P (("\ndoit ('%s')",arg));

    _splitpath (arg, drive, dir, fname, ext);
    if ( (strcmp(arg,"..") == 0) )  /* special case*/
    {   strcpy (dir, "..\\");
        *fname=0; *ext=0;
    }
    if ( (strcmp(arg,".") == 0) )   /* special case*/
    {   *dir=0; *fname=0; *ext=0;
    }
    if ( !(*ext) )  /* empty ext*/
        strcpy (ext, ".DWG");
    if ( !(*fname) )    /* empty ext*/
        strcpy (fname, "*");
    _makepath (path, drive, dir, fname, ext);
    DEBUG_P (("\n path   : '%s'",path));

    if ((dirp = opendir (path) ))  /* first check all the files*/
    {   for(;;)
        {   dd = readdir ( dirp );
            if( dd == NULL ) break;
            if ( (dd->d_attr & 0x1E) !=0 ) continue;
            /* only normal files 0x21 and read-only, archived*/
            /* split into fname and extension*/
            _splitpath (dd->d_name, NULL, NULL, tmpfn, tmpext);
            _makepath (path, drive, dir, tmpfn, tmpext); /* got only the filename*/
            check (path);       /*for each matching file*/
            fclose (fin);
        }
        closedir ( dirp );
    }
    if ( *outname || *outpath)
    {
    /*  only needed if the file should be changed (to file2)*/
    /*  in the first pass write it to *.TMP,*/
    /*  this is the second pass, rename *.TMP to original and save the old*/
        if ((dirp = opendir (path) ))
        {   for(;;)
            {   static char file2 [_MAX_PATH];

                dd = readdir ( dirp );
                if( dd == NULL ) break;
                if ( (dd->d_attr & 0x1E) !=0 ) continue;
                _splitpath (dd->d_name, NULL, NULL, tmpfn, tmpext);
                _makepath (path, drive, dir, tmpfn, tmpext);
                force_ext (file2, path, "TMP");
                if ( *outname )
                {   _splitpath (outname, NULL, NULL, tmpfn, tmpext);
                    if ( !(*tmpext) )  /* empty ext*/
                        strcpy (tmpext, ".DWG");
                    _makepath (path, drive, dir, tmpfn, tmpext);
                }
                else if ( *outpath )
                {
                    tmpdir = (char *)MALLOC(tmpdir,_MAX_DIR);
                    _splitpath (outpath, drive, tmpdir, NULL, NULL);
                    _makepath (path, drive, tmpdir, tmpfn, tmpext);
                    free (tmpdir);
                }
                rename (file2, path);   /* old -> new */
            }
            closedir ( dirp );
        }
    }
    /* now check all the subdirs*/
    if ( recurse )  /* check all the files in the dir*/
    {   _makepath (path, drive, dir, "*", ".*");
        DEBUG_P (("\nchecking subdirs ..."));
        DEBUG_P (("\n path   : '%s'",path));
        if ((dirp = opendir (path) ))
        {   for(;;)
            {   dd = readdir ( dirp );
                if( dd == NULL ) break;
                if (((dd->d_attr & _A_SUBDIR) != 0) &&
                     strcmp (dd->d_name,".") &&
                     strcmp (dd->d_name,".."))
                {   /* add it to dir (dir already has a trailing \\)*/
                    tmpdir = (char *)MALLOC(tmpdir,_MAX_DIR);
                    strcpy (tmpdir, dir);
                    strcat (tmpdir, dd->d_name);
                    strcat (tmpdir, "\\");
                    _makepath (path, drive, tmpdir, fname, ext);
                    DEBUG_P   (("\n  recurse into path:  '%s'",path));
                    VERBOSE_P (("\n  recurse into '%s'", path));
                    doit (path);
                    free (tmpdir);
                }
            }
            closedir ( dirp );
        }
    }
}

void set_all_opts (int value)
{
    header  = value;
    tables  = value;
    blocks  = value;
    entities= value;
}

int main(int argc, char *argv[])
{
    int l,nextarg;
    char defarg[] = "*.DWG";

    strcpy (execpath, argv[0]);
    nextarg = getopts(argc,argv,opttable);  /*external function*/
    /* check params for getopts() error*/
    if ( !header && !tables && !blocks && !entities )
        set_all_opts (TRUE);
    if ( outname[0]!='\0' )
        set_all_opts (TRUE);
    for ( l=0; l<argc; l++ )
    {   if ( argv[l][0]=='-') argv[l][0]='/';
        DEBUG_P (("\nargv[%d]: \"%s\"",l,argv[l]));
    }
    DEBUG_P (("\nhelp    : %s",(help   == TRUE)   ? "TRUE" : "FALSE"));
    DEBUG_P (("\nheader  : %s",(header == TRUE)   ? "TRUE" : "FALSE"));
    DEBUG_P (("\ntables  : %s",(tables == TRUE)   ? "TRUE" : "FALSE"));
    DEBUG_P (("\nblocks  : %s",(blocks == TRUE)   ? "TRUE" : "FALSE"));
    DEBUG_P (("\nentities: %s",(entities== TRUE)  ? "TRUE" : "FALSE"));
    DEBUG_P (("\ndebug   : %s",(debug  == TRUE)   ? "TRUE" : "FALSE"));
    DEBUG_P (("\nverbose : %s\n",(verbose== TRUE) ? "TRUE" : "FALSE"));
    if ( help || argc == 1)
	{   show_help (1);
	    return 0;
	}
	else
        show_help (0);

    if ( handles && !*outname )
    {   printf ("\nFor option /x a outname with /n must be specified!");
        printf ("\nDon't want ot edit in place so far, too dangerous!");
        return 1;
    }
    if ( nextarg < argc )
    {
        if ( strcmp (argv[nextarg],"g") == 0 )
            show_preamble();
        if ( strcmp (argv[nextarg],"w") == 0 )
            show_warranty();
        if ( strcmp (argv[nextarg],"c") == 0 )
            show_conditions();
    }
    if ( nextarg == argc )
        doit (defarg); /* no argument given */
    else
    for ( ; nextarg < argc; nextarg++)
    {   doit (argv[nextarg]);
    }
    printf ("\n%d drawings processed, %d errors occured", numdone, numerr);

    return 0;
}

int check_if_dwg (char *s)
{
FILE *fp;
char header[7];

    fp = fopen ( s,"rb" );
    if(!fp) {
        VERBOSE_P(( "\nFile %8s not found ",s ));
        return FALSE;
    }

    fseek ( fp, 0, SEEK_SET );
    fread ( &header, 1, 6, fp );
    fclose ( fp );

    header[6]=0;
    /* check if dwg*/
    /* AC1004: ??*/
    /* AC1006: R10*/
    /* AC1009: R11-12*/
    /* AC1010: R13*/
    /* AC1011: R13c1    ?*/
    /* AC1012: R13c2-3  ?*/
    /* please update this list someone. I've got no old versions here! */
    if ((strncmp ( header,"AC10",4 ) != 0))     /*accept only ACAD dwg's */
      goto nodwg;
    if ((strcmp ( &header[4],"00" ) < 0))       /*only above version 1.0 */
      goto nodwg;
    if ((strcmp ( &header[4],"09" ) > 0))       /*specific R13 msg*/
    {						/*cannot parse this so far */
        VERBOSE_P(( "\n%12s is an AutoCAD R13 DWG", s));
        return TRUE;
    }
    if ((strcmp ( &header[4],"09" ) < 0))
        VERBOSE_P(( "\n%12s is an earlier AutoCAD DWG - not checked yet", s));
    return TRUE;

nodwg:
    VERBOSE_P(( "\n%12s is no AutoCAD R12 DWG - ignored", s));
    return FALSE;
}


/* the scanner */
int check (char *file1)
{ char version[13];
  static char file2[_MAX_PATH];
  static char file3[_MAX_PATH];

  printf ("\nprocessing: '%s'",file1);
  { FILE *f;

    if (!(f = fopen(file1, "rb")))
    {   fprintf(stderr,"error: cannot open %s - No such file\n",file1);
        numerr++;
        return FALSE;
    }
    if ( !check_if_dwg(file1) )
    {   numerr++;
        return FALSE;
    }

    if ( *outname || *outpath)
    {
        force_ext (file2, file1, "TMP");
        if (!(fout = fopen(file2, "wb")))
        {   fprintf(stderr,"error: cannot open %s for writing\n",file2);
            numerr++;
            return FALSE;
        }
    }

    _assign(f);
  }

  DEBUG_P (( "\ncheck ('%s') ",file1));

  numunknown=0;
  strcpy (version, _read_fixed_string(12));
  DEBUG_P(("Version: '%s'  ", version));
  version_13 = (strcmp(version, "AC1009") > 0);
  { int b1 = _read_byte(), /* 1*/
        w1 = _read_word(), /* 3*/
        w2 = _read_word(), /* 5*/
        w3 = _read_word(), /* ?? */
        b2 = _read_byte(); /* 0*/
    DEBUG_P((" %d %d %d %d %d\n", b1, w1, w2, w3, b2));
  }
  DEBUG_P( ("Version 13 specific: %s\n", version_13 ? "YES" : "NO"));

  pointer[P_ENTITIES] = _read_long();
  pointer[P_ENTEND]   = _read_long();
  DEBUG_P(("ENTITIES                 (%05lX-%05lX)",
         pointer[P_ENTITIES], pointer[P_ENTEND]));

  ACT_POS;
  DEBUG_P(("BLOCK start "));
  pointer[P_BLOCKSEC] = get_pointer();
  KNOWN_WORD ("Block size: ");
  UNKNOWN_WORD;
  ACT_POS;
  DEBUG_P(("EOF / CRC-32 after BLOCK"));
  pointer[P_BSEND] = get_pointer();
  echo_bytes(4);
  /*DEBUG_P(("\n"));*/
  DEBUG_P(("BLOCKSECTION             (%05lX-%05lX)",
         pointer[P_BLOCKSEC], pointer[P_BSEND]));

  memset (table,0,sizeof(table));

  scan_table_position("BLOCK", &table[T_BLOCK]);
  scan_table_position("LAYER", &table[T_LAYER]);
  scan_table_position("STYLE", &table[T_STYLE]);
  scan_table_position("LTYPE", &table[T_LTYPE]);
  scan_table_position("VIEW",  &table[T_VIEW]);

  if (header) scan_header();

  if ( cur_pos == pointer[P_ENTITIES] )
    goto entity_sec;
  /* not in AC1004 */
  if (tables)
  {
      scan_table_position("UCS", &table[T_UCS]);
          {   /* actual UCS settings*/
              UNKNOWN_WORD; DEBUG_P(("\n"));
              SCAN_3DPOINT (10);
              SCAN_3DPOINT (11);
              SCAN_3DPOINT (12);

              UNKNOWN_DOUBLE; UNKNOWN_DOUBLE;
              UNKNOWN_DOUBLE; UNKNOWN_DOUBLE;

              echo_bytes(157);
    /*        echo_bytes(0x500 - cur_pos);*/
          }

      if ( cur_pos == pointer[P_ENTITIES] )
        goto entity_sec;
      scan_table_position("VPORT", &table[T_VPORT]);
          {
              UNKNOWN_WORD;  UNKNOWN_WORD;
              UNKNOWN_WORD;  UNKNOWN_WORD;
          }

      if ( cur_pos == pointer[P_ENTITIES] )
        goto entity_sec;
      scan_table_position("APPID", &table[T_APPID]);
          {
              UNKNOWN_WORD;  UNKNOWN_WORD;
              UNKNOWN_WORD;
          }

      if ( cur_pos == pointer[P_ENTITIES] )
        goto entity_sec;

      scan_table_position("DIMSTYLE", &table[T_DIMSTYLE]);  /* prev. P12 */
      {   int i;
          KNOWN_WORD ("act. style #");      /*0 based*/
            echo_bytes(17);

          for ( i=0; i<37; i++ )
            UNKNOWN_DOUBLE;

            echo_bytes(56);
    /*      echo_bytes(0x69F - cur_pos);*/
      }


      if ( cur_pos == pointer[P_ENTITIES] )
        goto entity_sec;
      scan_table_position("P13", &table[T_13]);
      {
          UNKNOWN_WORD;
          UNKNOWN_WORD;
          UNKNOWN_WORD;
          get_check_32();
      }
  }
entity_sec:
  if ( entities )
  {
    goto_pointer (P_ENTITIES);
    scan_entities(pointer[P_ENTITIES], pointer[P_ENTEND], 0L);
    if (pointer[P_ENTEND] < table[T_BLOCK].start) get_check_32();
  }

  if ( blocks )
  {
    goto_pointer (P_ENTEND);
    scan_block_table(&table[T_BLOCK]);
  }
  /*if (table[T_BLOCK].end < table[T_LAYER].start) get_check_32();*/

  if ( tables )
  {
      scan_layer_table(&table[T_LAYER]);
      /*if (table[T_LAYER].end < table[T_STYLE].start) get_check_32();*/
      scan_style_table(&table[T_STYLE]);
      /*if (table[T_STYLE].end < table[T_LTYPE].start) get_check_32();*/
      scan_ltype_table(&table[T_LTYPE]);
      /*if (table[T_LTYPE].end < table[T_VIEW].start)  get_check_32();*/
      scan_view_table (&table[T_VIEW]);
      /*if (table[T_VIEW].end < pointer[P_BLOCKSEC])   get_check_32();*/
  }

  if ( cur_pos == pointer[P_BLOCKSEC] )
    goto block_sec;

  if ( tables )
  {
      /* not in AC1004*/
      scan_ucs_table  (&table[T_UCS]);
      /*if (table[T_UCS].end < table[T_UCS+1].start)  get_check_32();*/
      scan_vport_table(&table[T_VPORT]);
      /*if (table[T_VPORT].end < table[T_VPORT].start)  get_check_32();*/
      scan_ads_table  (&table[T_APPID]);
      /*if (table[T_APPID].end < table[T_APPID+1].start)  get_check_32();*/
      scan_dim_table  (&table[T_DIMSTYLE]);
      /*if (table[T_UCS].end < table[T_UCS+1].start)  get_check_32();*/
      scan_table("P13 TABLE", &table[T_13]);
      /*if (table[T_T13].end < pointer[P_BLOCKSEC])  get_check_32();*/
  }

block_sec:
  if ( blocks )
  {
      goto_pointer (P_BLOCKSEC);
      DEBUG_P(("BLOCK SECTION"));
      scan_entities(pointer[P_BLOCKSEC], pointer[P_BSEND],
                    pointer[P_BLOCKSEC] - 0x40000000);
      if ( _eof() ) return 0;   /* AC1004*/
  }
  goto_pointer (pointer[P_BSEND]);
  if ( header )
  {
    DEBUG_P(("P3 Section"));    /* missing header vars or CRC table ?? */
    echo_bytes(36);
    if ( _eof() ) return 0;
  }
  else
    _advance(36);
  if ( tables || blocks || entities )
  {
      check_pointer(pointer[P_ENTITIES]);
      check_pointer(pointer[P_ENTEND]);
      check_pointer(pointer[P_BLOCKSEC]);
      check_pointer(pointer[P_BSEND]);
      echo_bytes(10);     /* ^Z  = eof*/
      KNOWN_WORD ( "num tables:");

      if ( _eof() ) return 0;
      check_table_pos("BLOCK", &table[T_BLOCK], 1);
      check_table_pos("LAYER", &table[T_LAYER], 2);
      check_table_pos("STYLE", &table[T_STYLE], 3);
      check_table_pos("LTYPE", &table[T_LTYPE], 5);
      check_table_pos("VIEW", &table[T_VIEW], 6);
      check_table_pos("UCS", &table[T_UCS], 7);
      check_table_pos("VPORT", &table[T_VPORT], 8);
      check_table_pos("APPID", &table[T_APPID], 9);
      check_table_pos("DIMSTYLE", &table[T_DIMSTYLE], 10);
      check_table_pos("P13", &table[T_13], 11);

      if ( _eof() ) return 0;
      echo_bytes(last_pos - cur_pos);
  }

  if (numunknown) VERBOSE_P(("\n%u internal unknowns ", numunknown));
  numunknown=0;
  if (handles) printf ("\n%u handles set to zero ", numchg);
  numchg=0;

  return TRUE;
}
