Making drawing databases compatible consisted of several subprojects. The result of all of this is that an AutoCAD with the new code installed can read existing drawing databases written by the machine on which it is executing, old MS-DOS databases, and new portable databases. It writes new portable databases, which can be read by any AutoCAD with this code installed.
The ability to read both formats of databases is implemented via the flag rstructs. When a drawing database header is read by MVALID, if it is an old, nonportable database, rstructs is set to TRUE and the file pointer used to read the file is saved. Subsequent reads from that file will use the old code to read aggregate data. At the end of every database reading operation, such as INSERT or PLOT, rstructs is cleared.
The drawing header is managed by code in MASTREC.C. The header is defined, for I/O purposes, by a table called MTAB. This table previously contained pointers and lengths for all the items in the header, and each was written or read with an individual call on FREAD or FWRITE. Compatibility problems were created by the fact that the header contained several kinds of composite objects: symbol table descriptors, transformation matrices, the ``header header'', a view direction array, Julian dates, and calendar dates. I modified the table to contain an item type and implemented a switch to read and write each item with the correct calls on the Binary I/O package. Special code had to be added for each composite type to read and write it; just adding entries to the table for the components of the composite types falls afoul of the mechanism that allows addition of new fields to the header. I tried it; it doesn't work. The symbol table descriptors have a several unique problems: first, their definition contains a ``FILE *'' item. The length of this item varies depending on the system's pointer length, so the structure changes based on this length. On MS-DOS systems, data in the structure totals 37 bytes, and different compilers pad this structure differently. The file pointer field means nothing in a drawing database, but it is present in all existing databases and it varies in length. But if you think that it never uses a pointer read from a file, you haven't looked at the code in WBLOCK.C that saves and restores the header around its diddling with it. Look and see the horror I had to install to fix that one.
The symbol tables, managed by SMIO.C, were an utter catastrophe from the standpoint of portability. The problems encountered in MASTREC with their headers was only a faint shadow of the beast lurking within SMIO. To refresh your memory, each symbol table has a descriptor which is usually in the drawing header (another symbol table is used for active font and shape files, but it is not saved with the drawing and does not enter this discussion). The descriptor for the symbol table contains its length, the number of items in the table, the file descriptor used to read and write it, and the address within the file where it starts. There is no type field in a symbol table. Symbol tables are read and written by the routines GETSM and PUTSM, which are passed the descriptor. Each symbol table entry consists of a structure containing several fields of various types.
Previously, GETSM and PUTSM did not care about the content of the symbol table record; they just read and wrote the structure as one monolithic block. That, of course, won't work if you want the tables to be portable: each field has to be handled separately with the Binary I/O package. So in order to do this, GETSM and PUTSM must know the type of table they are processing.
``So,'' said Galt, ``add a type field to the table.''
``Heh, heh, heh,'' I said, walking over to the Sun and bringing up all the references to the block symbol table descriptor in CSCOPE. There are few data structures within any program that are chopped, diced, sliced, shuffled, and maimed as much as an AutoCAD symbol table descriptor. Most (but not all) live within the drawing header. They can point to their own file or be part of a monolithic database. They contain that ghastly variable length file pointer which gets written in the drawing header. They get copied, created dynamically in allocated buffers, and in WBLOCK, saved to a file, modified to refer to another file, then read back in. And that ``length'' field I mentioned, sm_eln. Well, it may include a trailing pad byte on the disc depending on which compiler and options made your MS-DOS database. And it gets used both to seek into the file and to dynamically allocate symbol table descriptors except in the places where it uses sizeof(struct whatever) instead. One week into this project, I had the feeling that I had not stuck my head into the lion's mouth--I had climbed into the lion's stomach.
The most severe fundamental problem was that I had to both decouple the symbol table descriptor on disc from the one in memory, and also introduce separate lengths for the symbol table as stored on disc (used to seek to records) and in memory (used to allocate buffers, copy tables, and so on). I ended up adding two fields to the symbol table item in memory, sm_typeid and sm_dlen, which specify the type of the symbol table (mnemonics are defined in SMIO.H) and its length as stored on the disc. When a symbol table is in memory, sm_eln specifies the length of the structure in memory. When a symbol table is written out, the two new fields are not written: instead the disc length is written into the sm_eln field and the type is expressed implicitly in the symbol table's position in the drawing header.
By the way, the lack of a type code in symbol tables has been felt before: there is some marvelous to behold code in WBLOCK.C that figures out which symbol table it is working on by testing the pointer against the descriptor address. I did not fix these to use my new type codes. Somebody should some day. Once the type codes and disc lengths were present, the changes to process the symbol tables separately were straightforward to install in SMIO.C.
Because the code to process the symbol tables field by field is substantially larger and also somewhat slower than reading a single structure, I set up conditional compilation to use the old code on MS-DOS. Since MS-DOS already writes the tables in canonical form and has the most severe memory constraints, there's no reason it should have to pay the price of compatibility code it doesn't need. Note that if you remove the #ifdefs on MSDOS from the file, it will still work fine: it will just be bigger and slower.
There is a fixed set of fields which precedes every entity in the drawing database to specify its type, flags, length of the packed data which follows, and a pointer. When Bob made the entity data compatible, he could not use his scatter/gather mechanism for these fields because they control the scatter/gather process. I modified EREAD.C to use the Binary I/O package for these fields. In addition, if REALTRAN has been defined on this system, the gathreal and scatreal functions call realenc and realdec routines to translate floating point formats. If REALTRAN is not defined, no additional code is compiled or executed, so IEEE-compatible systems pay no price for the possibility of floating point format conversion. The floating point conversion mechanism has never been tested.
Editor: John Walker