/****************************************************************************
*
*  This code is Public Domain.
*
*  ========================================================================
*
* Description:  STRUCT, UNION, RECORD and TYPEDEF directives.
*
****************************************************************************/

#include <ctype.h>

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "segment.h"
#include "proc.h"
#include "input.h"
#include "tokenize.h"
#include "types.h"
#include "expreval.h"
#include "label.h"
#include "listing.h"
#include "fastpass.h"
#include "myassert.h"

/* v2.04: changed to 0 */
//#define ANYNAME 1 /* fixme: this probably should be changed to 0 */
#define ANYNAME 0
#define TYPEOPT 0

#if AMD64_SUPPORT
#define MAXRECBITS ( ModuleInfo.Ofssize == USE64 ? 64 : 32 )
#else
#define MAXRECBITS 32
#endif

struct dsym *CurrStruct;
static struct dsym *redef_struct;
/* text constants for 'Non-benign <x> redefinition' error msg */
static const char szStructure[] = "structure";
static const char szRecord[] = "record";

static const char szNonUnique[] = "NONUNIQUE";

void TypesInit( void )
/********************/
{
    CurrStruct   = NULL;
    redef_struct = NULL;
}

/* create a SYM_TYPE symbol.
 * <sym> must be NULL or of state SYM_UNDEFINED.
 * <name> and <global> are only used if <sym> is NULL.
 * <name> might be an empty string.
 */

struct asym *CreateTypeSymbol( struct asym *sym, const char *name, bool global )
/******************************************************************************/
{
    struct struct_info *si;

    if ( sym )
        sym_remove_table( &SymTables[TAB_UNDEF], (struct dsym *)sym );
    else
        sym = ( global ? SymCreate( name ) : SymAlloc( name ) );

    if ( sym ) {
        sym->state = SYM_TYPE;
        sym->typekind = TYPE_NONE;
        ((struct dsym *)sym)->e.structinfo = si = LclAlloc( sizeof( struct struct_info ) );
        si->head = NULL;
        si->tail = NULL;
        si->alignment = 0;
        si->flags = 0;
    }
    return( sym );
}

/* search a name in a struct's fieldlist */

struct asym *SearchNameInStruct( const struct asym *tstruct, const char *name, uint_32 *poffset, int level )
/**********************************************************************************************************/
{
    int len = strlen( name );
    struct sfield *fl = ((struct dsym *)tstruct)->e.structinfo->head;
    struct asym *sym = NULL;

    //if (ModuleInfo.oldstructs == TRUE) {
    //    return( SymSearch( name ) );
    //}
    if ( level >= MAX_STRUCT_NESTING ) {
        EmitError( NESTING_LEVEL_TOO_DEEP );
        return( NULL );
    }
    level++;
    for ( ; fl; fl = fl->next ) {
        /* recursion: if member has no name, check if it is a structure
         and scan this structure's fieldlist then */
        if ( *( fl->sym.name ) == NULLC ) {
            /* there are 2 cases: an anonymous inline struct ... */
            if ( fl->sym.state == SYM_TYPE ) {
                if ( sym = SearchNameInStruct( &fl->sym, name, poffset, level ) ) {
                    *poffset += fl->sym.offset;
                    break;
                }
            /* or an anonymous structured field */
            } else if ( fl->sym.mem_type == MT_TYPE ) {
                if ( sym = SearchNameInStruct( fl->sym.type, name, poffset, level ) ) {
                    *poffset += fl->sym.offset;
                    break;
                }
            }
        } else if ( len == fl->sym.name_size && SymCmpFunc( name, fl->sym.name, len ) == 0 ) {
            DebugMsg(("SearchNameInStruct: '%s' found in struct %s\n", name, tstruct->name ));
            sym = &fl->sym;
            break;
        }
    }
    return( sym );
}

/* check if a struct has changed */

static bool AreStructsEqual( const struct dsym *newstr, const struct dsym *oldstr )
/*********************************************************************************/
{
    struct sfield *fold = oldstr->e.structinfo->head;
    struct sfield *fnew = newstr->e.structinfo->head;

    DebugMsg(("AreStructsEqual(%s) enter\n", oldstr->sym.name ));

    /* kind of structs must be identical */
    if ( oldstr->sym.typekind != newstr->sym.typekind )
        return( FALSE );

    for ( ; fold; fold = fold->next, fnew = fnew->next ) {
        if ( !fnew ) {
            DebugMsg(("AreStructsEqual: fields don't match\n"));
            return( FALSE );
        }
        /* for global member names, don't check the name if it's "" */
        if ( ModuleInfo.oldstructs && *fnew->sym.name == NULLC )
            ;
        else if ( 0 != strcmp( fold->sym.name, fnew->sym.name ) ) {
            DebugMsg(("AreStructsEqual: type name of field changed\n"));
            return( FALSE );
        }
        if ( fold->sym.offset != fnew->sym.offset ) {
            DebugMsg(("AreStructsEqual: offset of field %s changed: %u - %u\n", fold->sym.name, fold->sym.offset, fnew->sym.offset));
            return( FALSE );
        }
        if ( fold->sym.total_size != fnew->sym.total_size ) {
            DebugMsg(("AreStructsEqual: total_size of field changed\n"));
            return( FALSE );
        }
    }
    if ( fnew )
        return( FALSE );
    return( TRUE );
}

/* handle STRUCT, STRUC, UNION directives
 * i = index of directive token
 */
ret_code StructDirective( int i, struct asm_tok tokenarray[] )
/************************************************************/
{
    char *name;
    unsigned alignment;
    uint_32 offset;
    uint_8 typekind = ( tokenarray[i].tokval == T_UNION ? TYPE_UNION : TYPE_STRUCT );
    //unsigned int size;
    struct asym *sym;
    struct dsym *dir;

    DebugMsg1(("StructDirective(%s) enter, i=%u, CurrStruct=%s\n", tokenarray[i].string_ptr, i, CurrStruct ? CurrStruct->sym.name : "NULL" ));

    /* top level structs/unions must have an identifier at pos 0.
     * for embedded structs, the directive must be at pos 0,
     * an identifier is optional then.
     */
    if (( CurrStruct == NULL && i != 1 ) ||
        ( CurrStruct != NULL && i != 0 ) ) {
        DebugMsg(("StructDirective(%s): error: either currstruct or i must be 0\n", tokenarray[i].string_ptr ));
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
    }

    alignment = ( 1 << ModuleInfo.fieldalign );

    i++; /* go past STRUCT/UNION */

    if ( i == 1 ) { /* embedded struct? */
        /* scan for the optional name */
#if ANYNAME
        /* the name might be a reserved word!
         * Masm won't allow those.
         */
        //if ( tokenarray[i].token != T_FINAL && is_valid_id_first_char(*(tokenarray[i].string_ptr) ) ) {
#else
        if ( tokenarray[i].token == T_ID ) {
#endif
            name = tokenarray[i].string_ptr;
            i++;
        } else {
            name = "";
        }
    } else {
        name = tokenarray[0].string_ptr;
    }

    /* get an optional alignment argument: 1,2,4,8,16 or 32 */
    if ( CurrStruct == NULL && tokenarray[i].token != T_FINAL ) {
        int power;
        struct expr opndx;
        /* get the optional alignment parameter.
         * forward references aren't accepted, but EXPF_NOUNDEF isn't used here!
         */
        if ( EvalOperand( &i, tokenarray, Token_Count, &opndx, 0 ) != ERROR ) {
            /* an empty expression is accepted */
            if ( opndx.kind == EXPR_EMPTY ) {
                ;
            } else if ( opndx.kind != EXPR_CONST ) {
                /* v2.09: better error msg */
                if ( opndx.sym && opndx.sym->state == SYM_UNDEFINED )
                    EmitErr( SYMBOL_NOT_DEFINED, opndx.sym->name );
                else
                    EmitError( CONSTANT_EXPECTED );
            } else if( opndx.value > MAX_STRUCT_ALIGN ) {
                EmitError( STRUCT_ALIGN_TOO_HIGH );
            } else {
                for( power = 1; power < opndx.value; power <<= 1 );
                if( power != opndx.value ) {
                    EmitErr( POWER_OF_2, opndx.value );
                } else
                    alignment = opndx.value;
            }
            DebugMsg1(("StructDirective(%s) alignment=%u\n", name, alignment));
        }
        /* there might also be the NONUNIQUE parameter */
        if ( tokenarray[i].token == T_COMMA ) {
            i++;
            if ( tokenarray[i].token == T_ID &&
                (_stricmp( tokenarray[i].string_ptr, szNonUnique ) == 0 ) ) {
                /* currently NONUNIQUE is ignored */
                EmitWarn( 2, TOKEN_IGNORED, szNonUnique );
                i++;
            }
        }
    }
    if ( tokenarray[i].token != T_FINAL ) {
        DebugMsg(("StructDirective(%s): error: unexpected token %u >%s<\n", tokenarray[i].token, tokenarray[i].tokpos ));
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
    }

    /* does struct have a name? */
    if ( *name ) {
        if ( CurrStruct == NULL ) {
            /* the "top-level" struct is part of the global namespace */
            sym = SymSearch( name );
            DebugMsg1(("StructDirective: SymSearch (%s)=%X (curr struct=%X)\n", name, sym, CurrStruct ));
        } else {
            sym = SearchNameInStruct( (struct asym *)CurrStruct, name, &offset, 0 );
            DebugMsg1(("StructDirective(%s): SearchNameInStruc()=%X\n", name, sym));
        }
    } else {
        sym = NULL;   /* anonymous struct */
    }

    if ( ModuleInfo.list ) {
        if ( CurrStruct )
            /* v2.17: don't use total_size as argument for LstWrite(),
             * it differs for pass==1 and pass>1. In many cases this doesn't matter,
             * because structs/unions are defined BEFORE data/code and FASTPASS skips
             * such definitions for subsequent passes.
             */
            //LstWrite( LSTTYPE_STRUCT, CurrStruct->sym.total_size, NULL );
            LstWrite( LSTTYPE_STRUCT, CurrStruct->sym.offset, NULL );
        else
            LstWrite( LSTTYPE_STRUCT, 0, NULL );
    }
    /* if pass is > 1, update struct stack + CurrStruct.offset and exit */
    if ( Parse_Pass > PASS_1 ) {
        /* v2.04 changed. the previous implementation was insecure.
         * See also change in data.c, behind CreateStructField().
         */
        if ( CurrStruct ) {
            sym = CurrStruct->e.structinfo->tail->sym.type;
            /**/myassert( sym );
            CurrStruct->e.structinfo->tail = CurrStruct->e.structinfo->tail->next;
        }
        /**/myassert( sym );
        dir = (struct dsym *)sym;
        dir->e.structinfo->tail = dir->e.structinfo->head;
        sym->offset = 0;
        sym->isdefined = TRUE;
        ((struct dsym *)sym)->next = CurrStruct;
        CurrStruct = (struct dsym *)sym;
        return( NOT_ERROR );
    }

    if( sym == NULL ) {

        /* embedded or global STRUCT? */
        if ( CurrStruct == NULL )
            sym = CreateTypeSymbol( NULL, name, TRUE );
        else {
            /* an embedded struct is split in an anonymous STRUCT type
             * and a struct field with/without name
             */
            sym = CreateTypeSymbol( NULL, name, FALSE );
            /* v2: don't create the struct field here. First the
             * structure must be read in ( because of alignment issues
             */
            // sym = CreateStructField( name_loc, -1, MT_TYPE, dir, 0 );

            alignment = CurrStruct->e.structinfo->alignment;
        }

    } else if( sym->state == SYM_UNDEFINED ) {

        /* forward reference */
        CreateTypeSymbol( sym, NULL, CurrStruct == NULL );

    } else if( sym->state == SYM_TYPE && CurrStruct == NULL ) {

        switch ( sym->typekind ) {
        case TYPE_STRUCT:
        case TYPE_UNION:
            /* if a struct is redefined as a union ( or vice versa )
             * do accept the directive and just check if the redefinition
             * is compatible (usually it isn't) */
            redef_struct = (struct dsym *)sym;
            sym = CreateTypeSymbol( NULL, name, FALSE );
            break;
        case TYPE_NONE:  /* TYPE_NONE is forward reference */
            break;
        default:
            return( EmitErr( SYMBOL_REDEFINITION, sym->name ) );
        }

    } else {
        return( EmitErr( SYMBOL_REDEFINITION, sym->name ) );
    }

    sym->offset = 0;
    sym->typekind = typekind;
    dir = (struct dsym *)sym;
    dir->e.structinfo->alignment = alignment;
    dir->e.structinfo->isOpen = TRUE;
    if ( CurrStruct )
        dir->e.structinfo->isInline = TRUE;

    dir->next = CurrStruct;
    CurrStruct = dir;

#if 0 //def DEBUG_OUT
    {
        struct dsym *struc;
        for ( struc = CurrStruct; struc; struc = struc->next ) {
            DebugMsg(("StructDirective stack: %X, name=>%s<\n", struc, struc->sym.name ));
        }
    }
#endif

    return( NOT_ERROR );
}

/* handle ENDS directive when a struct/union definition is active */

ret_code EndstructDirective( int i, struct asm_tok tokenarray[] )
/***************************************************************/
{
    //char *name;
    //unsigned int offset;
    unsigned int size;
    //struct asym *sym;
    //memtype mem_type;
    struct dsym *dir;

    dir = CurrStruct; /* cannot be NULL */

    DebugMsg1(("EndstructDirective(%s), ofs=%" I32_SPEC "u, struct size=%" I32_SPEC "u, max_mbr=%" I32_SPEC "u, alignment=%u\n",
              dir->sym.name,
              dir->sym.offset,
              dir->sym.total_size,
              dir->sym.max_mbr_size,
              dir->e.structinfo->alignment));

    /* if pass is > 1 just do minimal work */
    if ( Parse_Pass > PASS_1 ) {
        CurrStruct->sym.offset = 0;
        size = CurrStruct->sym.total_size;
        CurrStruct = CurrStruct->next;
        if ( CurrStruct )
            UpdateStructSize( (struct asym *)dir );
        //LstWrite( LSTTYPE_STRUCT, size, dir ); /* v2.18: dir not used by LSTTYPE_STRUCT */
        LstWrite( LSTTYPE_STRUCT, size, NULL );
        return( NOT_ERROR );
    }

    /* syntax is either "<name> ENDS" (i=1) or "ENDS" (i=0).
     * first case must be top level (next=NULL), latter case must NOT be top level (next!=NULL)
     */
    if ( ( i == 1 && dir->next == NULL ) ||
        ( i == 0 && dir->next != NULL ) ) {
        ;
    } else {
        /* v2.04: error msg improved */
        //EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr );
        return( EmitErr( UNMATCHED_BLOCK_NESTING, i == 1 ? tokenarray[0].string_ptr : "" ) );
    }

    if ( i == 1 ) { /* an global struct ends with <name ENDS> */
        if ( SymCmpFunc( tokenarray[0].string_ptr, dir->sym.name, dir->sym.name_size ) != 0 ) {
            /* names don't match */
            DebugMsg(("EndstructDirective: names don't match, i=%u, name=%s - %s\n", i, tokenarray[0].string_ptr, dir->sym.name));
            return( EmitErr( UNMATCHED_BLOCK_NESTING, tokenarray[0].string_ptr ) );
        }
    }

    i++; /* go past ENDS */

    /* v2.07: if ORG was used inside the struct, the struct's size
     * has to be calculated now - there may exist negative offsets.
     */
    if ( dir->e.structinfo->OrgInside ) {
        struct sfield *f;
        int_32 min = 0;
        for ( f = dir->e.structinfo->head; f; f = f->next )
            if ( f->sym.offset < min )
                min = f->sym.offset;
        dir->sym.total_size = dir->sym.total_size - min;
    }

    /* Pad bytes at the end of the structure. */
#if 1
    /* v2.02: this is to be done in any case, whether -Zg is set or not */
    //if ( dir->e.structinfo->alignment > 1 && Options.masm_compat_gencode == FALSE ) {
    if ( dir->e.structinfo->alignment > 1 ) {
        size = dir->sym.max_mbr_size;
        if ( size == 0 )
            size++;
        if ( size > dir->e.structinfo->alignment )
            size = dir->e.structinfo->alignment;
        dir->sym.total_size = (dir->sym.total_size + size - 1) & (-size);
        DebugMsg1(("EndstructDirective:, struct size after final alignment=%" I32_SPEC "u\n", dir->sym.total_size));
    }
#endif
    dir->e.structinfo->isOpen = FALSE;
    dir->sym.isdefined = TRUE;

    /* if there's a negative offset, size will be wrong! */
    size = dir->sym.total_size;

    /* reset offset, it's just used during the definition */
    dir->sym.offset = 0;

    CurrStruct = dir->next;
    /* v2.0: add the embedded struct AFTER it has been parsed! */
    if ( i == 1 ) {
        struct asym *sym;
        /* v2.06: the struct name is needed for checks */
        sym = CreateStructField( -1, NULL, *dir->sym.name ? dir->sym.name : NULL, MT_TYPE, &dir->sym, dir->sym.total_size );
        /* the member name was stored in the type name */
        //sym->name = dir->sym.name;
        //sym->name_size = strlen( dir->sym.name );
        sym->total_size = dir->sym.total_size;
        dir->sym.name = ""; /* the type becomes anonymous */
        dir->sym.name_size = 0;
    }

    //LstWrite( LSTTYPE_STRUCT, size, dir ); /* v2.18: dir not used by LSTTYPE_STRUCT */
    LstWrite( LSTTYPE_STRUCT, size, NULL );
#if 1
    /* to allow direct structure access */
    switch ( dir->sym.total_size ) {
    case 1:  dir->sym.mem_type = MT_BYTE;   break;
    case 2:  dir->sym.mem_type = MT_WORD;   break;
    case 4:  dir->sym.mem_type = MT_DWORD;  break;
    case 6:  dir->sym.mem_type = MT_FWORD;  break;
    case 8:  dir->sym.mem_type = MT_QWORD;  break;
    //case 16:  dir->sym.mem_type = MT_OWORD;  break;
    default: dir->sym.mem_type = MT_EMPTY;
    }
#endif
    /* reset redefine */
    if ( CurrStruct == NULL ) {
        if ( redef_struct ) {
            if ( AreStructsEqual( dir, redef_struct) == FALSE ) {
                EmitErr( NON_BENIGN_XXX_REDEFINITION, szStructure, dir->sym.name );
            }
            DebugMsg(("EndstructDirective: delete the redefinition of %s\n", dir->sym.name ));
            SymFree( (struct asym *)dir );
            redef_struct = NULL;
        }
    } else {

        if ( dir->sym.max_mbr_size > CurrStruct->sym.max_mbr_size )
            CurrStruct->sym.max_mbr_size = dir->sym.max_mbr_size;

        UpdateStructSize( (struct asym *)dir );
        DebugMsg1(("EndstructDirective: new size of restored structure=%u\n", CurrStruct->sym.total_size));
    }
    //dir->sym.max_mbr_size = 0;
    if ( tokenarray[i].token != T_FINAL ) {
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
    }
    return( NOT_ERROR );
}

/* v2.06: new function to check fields of anonymous struct members */

static ret_code CheckAnonymousStruct( struct dsym *type )
/*******************************************************/
{
    uint_32 disp;
    struct asym  *sym;
    struct sfield *f;

    for ( f = type->e.structinfo->head; f; f = f->next ) {
        if ( *f->sym.name ) {
            sym = SearchNameInStruct((struct asym *)CurrStruct, f->sym.name, &disp, 0 );
            if ( sym ) {
                return( EmitErr( SYMBOL_REDEFINITION, sym->name ) );
            }
        } else if ( f->sym.type ) {
            struct dsym *stype = (struct dsym *)f->sym.type;
            if ( stype->sym.typekind == TYPE_STRUCT ||
                stype->sym.typekind == TYPE_UNION ) {
            if ( CheckAnonymousStruct( stype ) == ERROR )
                return( ERROR );
            }
        }
    }
    return( NOT_ERROR );
}

/* CreateStructField() - creates a symbol of state SYM_STRUCT_FIELD.
 * this function is called in pass 1 only, by
 * 1. for struct members by data_dir() in data.c.
 * 2. for embedded structs by EndStructDirective().
 * - loc: initializer index location, -1 means no initializer (is an embedded struct)
 * - name: field name, may be NULL
 * - mem_type: mem_type of item
 * - vartype: user-defined type of item if memtype is MT_TYPE
 * - size: size of type - used for alignment only
 */
struct asym *CreateStructField( int loc, struct asm_tok tokenarray[], const char *name, enum memtype mem_type, struct asym *vartype, uint_32 size )
/*************************************************************************************************************************************************/
{
    int_32 offset;
    //int count;
    int i;
    int len;
    uint_32 disp;
    char *init;
    struct struct_info *si;
    struct sfield *f;
    struct asym  *gsym;

    si = CurrStruct->e.structinfo;
    offset = CurrStruct->sym.offset;

    DebugMsg1(("CreateStructField(%s): name=%s, curr ofs=%" I32_SPEC "u, vartype=%s, size=%" I32_SPEC "u\n",
               CurrStruct->sym.name, name ? name : "<anonymous>", offset,
               vartype ? vartype->name : "NULL", size ));

    if ( name ) {
        struct asym  *sym;
        len = strlen( name );
        if( len > MAX_ID_LEN ) {
            EmitError( IDENTIFIER_TOO_LONG );
            return( NULL );
        }
        sym = SearchNameInStruct((struct asym *)CurrStruct, name, &disp, 0 );
        if ( sym ) {
            EmitErr( SYMBOL_ALREADY_DEFINED, sym->name );
            return( NULL );
        }
    } else {
        /* v2.06: check fields of anonymous struct member */
        if ( vartype &&
            ( vartype->typekind == TYPE_STRUCT ||
             vartype->typekind == TYPE_UNION ) ) {
            CheckAnonymousStruct( (struct dsym *)vartype );
        }
        name = "";
        len = 0;
    }

    if ( loc != -1 ) {

        //i = strlen( tokenarray[loc].string_ptr ) + 1;
        //DebugMsg1(("CreateStructField(%s): type=>%s<\n", CurrStruct->sym.name, tokenarray[loc].string_ptr ));
        //f->init_dir = LclAlloc( i );
        //memcpy( f->init_dir, tokenarray[loc].string_ptr, i );

        /* now add the value to initialize the struct to */

        /* v2.03: the initializer value may contain assembly time
         * variables ( $ inside structs is also one ). It's crucial that
         * the variable's CURRENT value is used then.
         * v2.08: modified. avoid usage of token->string_ptr,
         * and prefer to use token->tokpos.
         */
        init = StringBufferEnd;
        for ( i = loc+1; tokenarray[i].token != T_FINAL; i++ ) {
            if ( tokenarray[i].token == T_ID ) {
                struct asym *sym2 = SymSearch( tokenarray[i].string_ptr );
                if ( sym2 && sym2->isvariable ) {
                    if ( sym2->predefined && sym2->sfunc_ptr )
                        sym2->sfunc_ptr( sym2, NULL );
                    myltoa( sym2->uvalue, init, ModuleInfo.radix, sym2->value3264 < 0, TRUE );
                    init += strlen( init );
                    *init++= ' ';
                    continue;
                }
            }
            memcpy( init, tokenarray[i].tokpos, tokenarray[i+1].tokpos - tokenarray[i].tokpos );
            init += tokenarray[i+1].tokpos - tokenarray[i].tokpos;
        }
        *init = NULLC;
        f = LclAlloc( sizeof( struct sfield ) + ( init - StringBufferEnd ) );
        //f->value = LclAlloc( init - StringBufferEnd + 1 );
        memset( f, 0, sizeof( struct sfield ) );
        strcpy( f->ivalue, StringBufferEnd );
        DebugMsg1(("CreateStructField(%s): initializer=>%s<\n", CurrStruct->sym.name, f->ivalue ));

    } else {
        f = LclAlloc( sizeof( struct sfield ) );
        memset( f, 0, sizeof( struct sfield ) );
        DebugMsg1(("CreateStructField(%s): no initializer<\n", CurrStruct->sym.name ));
        //f->init_dir = NULL;
        f->ivalue[0] = NULLC;
    }

    /* create the struct field symbol */

    //sym = SymAlloc( name );
    f->sym.name_size = len;
    if ( len ) {
        f->sym.name = LclAlloc( len + 1 );
        memcpy( f->sym.name, name, len );
        f->sym.name[len] = NULLC;
    } else
        f->sym.name = "";
    f->sym.state = SYM_STRUCT_FIELD;
    f->sym.list = ModuleInfo.cref;
    f->sym.isdefined = TRUE;
    f->sym.mem_type = mem_type;
    f->sym.type = vartype;
    /* fields total/first_size, total/first_length are set in data_item() */
    // sym->total_size = SizeFromMemtype( mem_type, ModuleInfo.Ofssize );

    f->next = NULL;
    //f->sym = sym;

    if( si->head == NULL ) {
        si->head = si->tail = f;
    } else {
        si->tail->next = f;
        si->tail = f;
    }

#if 1
    /* v2.0: for STRUCTs, don't use the struct's size for alignment calculations,
     * instead use the size of the "max" member!
     */
    if ( mem_type == MT_TYPE &&
        ( vartype->typekind == TYPE_STRUCT ||
         vartype->typekind == TYPE_UNION ) ) {
        size = vartype->max_mbr_size;
    }
#endif
    /* align the field if an alignment argument was given */
    if ( si->alignment > 1 ) {
        //enum memtype mt;
        //struct dsym *tdir;
        DebugMsg1(("CreateStructField(%s): align=%u, size=%u, ofs=%u\n", CurrStruct->sym.name, si->alignment, size, offset ));
        /* if it's the first field to add, use offset of the parent's current field */
#if 0
        /* v2: removed. An embedded struct is now added AFTER it has
         * been parsed. */
        if ( offset == 0 && CurrStruct->next ) {
            struct dsym *parent = CurrStruct->next;
            if ( si->alignment < size )
                parent->e.structinfo->tail->sym->offset =
                    (parent->e.structinfo->tail->sym->offset + (si->alignment - 1)) & ( - si->alignment);
            else if ( size )
                parent->e.structinfo->tail->sym->offset =
                    (parent->e.structinfo->tail->sym->offset + (size - 1)) & (-size);
        } else
#endif
        {
            if ( si->alignment < size )
                offset = (offset + (si->alignment - 1)) & ( - si->alignment);
            else if ( size )
                offset = (offset + (size - 1)) & (-size);
        }
        /* adjust the struct's current offset + size.
         The field's size is added in UpdateStructSize()
         */
        if ( CurrStruct->sym.typekind != TYPE_UNION ) {
            CurrStruct->sym.offset = offset;
            if ( offset > CurrStruct->sym.total_size )
                CurrStruct->sym.total_size = offset;
        }
    }
    /* v2.0: for padding, save the max member size */
    if ( size > CurrStruct->sym.max_mbr_size ) {
        DebugMsg1(("CreateStructField(%s): max_mbr_size set to %u\n", CurrStruct->sym.name, size ));
        CurrStruct->sym.max_mbr_size = size;
    }
    f->sym.offset = offset;

    /* if -Zm is on, create a global symbol */
    if ( ModuleInfo.oldstructs == TRUE && *name != NULLC ) {
        DebugMsg(("CreateStructField(%s): Masm51 compat on, lookup %s in global symbol table\n", CurrStruct->sym.name, name ));
        gsym  = SymLookup( name );
        /* v2.11: cannot fail */
        //if ( gsym ) {
            if ( gsym->state == SYM_UNDEFINED )
                gsym->state = SYM_STRUCT_FIELD;
            if ( gsym->state == SYM_STRUCT_FIELD ) {
                struct dsym *dir;
                gsym->mem_type = mem_type;
                gsym->type = vartype;
                gsym->offset = offset; /* added v2.0 */
                /* v2.01: must be the full offset.
                 * (there's still a problem if alignment is > 1!)
                 */
                for ( dir = CurrStruct->next; dir; dir = dir->next )
                    gsym->offset += dir->sym.offset;
                gsym->isdefined = TRUE;
            }
        //}
    }

    return( &f->sym );
}

/* called by AlignDirective() if ALIGN/EVEN has been found inside
 * a struct. It's already verified that <value> is a power of 2.
 */
ret_code AlignInStruct( int value )
/*********************************/
{
    if ( CurrStruct->sym.typekind != TYPE_UNION ) {
        int offset;
        offset = CurrStruct->sym.offset;
        offset = (offset + (value - 1)) & (-value);
        CurrStruct->sym.offset = offset;
        if ( offset > CurrStruct->sym.total_size )
            CurrStruct->sym.total_size = offset;
    }
    return( NOT_ERROR );
}

/* called by data_dir() when a structure field has been created.
 */
void UpdateStructSize( struct asym *sym )
/***************************************/
{
    if ( CurrStruct->sym.typekind == TYPE_UNION ) {
        //if ( no_of_bytes > CurrStruct->sym.total_size )
        //    CurrStruct->sym.total_size = no_of_bytes;
        if ( sym->total_size > CurrStruct->sym.total_size )
            CurrStruct->sym.total_size = sym->total_size;
    } else {
        /* v2.17:replace signed field "offset" by unsigned "uvalue"?
         * allows structures with size > 2GB, but isn't masm compatible;
         * also, struct size may be incorrect if start offset of struct is
         * negative ( due to an ORG at struct start with negative argument )
         */
#if 1
        CurrStruct->sym.offset += sym->total_size;
        if ( CurrStruct->sym.offset > (int_32)CurrStruct->sym.total_size )
            CurrStruct->sym.total_size = CurrStruct->sym.offset;
#else
        CurrStruct->sym.uvalue += sym->total_size;
        if ( CurrStruct->sym.uvalue > CurrStruct->sym.total_size )
            CurrStruct->sym.total_size = CurrStruct->sym.uvalue;
#endif
    }
    DebugMsg1(("UpdateStructSize(%s.%s): %s, curr mbr size=%u curr struc/union size=%u\n",
               CurrStruct->sym.name,
               sym->name,
               CurrStruct->sym.typekind == TYPE_UNION ? "union" : "struct",
               sym->total_size,
               CurrStruct->sym.total_size));
    return;
}

/* called if ORG occurs inside STRUCT/UNION definition */

ret_code SetStructCurrentOffset( int_32 offset )
/**********************************************/
{
    if ( CurrStruct->sym.typekind == TYPE_UNION ) {
        return( EmitError( ORG_NOT_ALLOWED_IN_UNIONS ) );
    }
    CurrStruct->sym.offset = offset;
    /* if an ORG is inside the struct, it cannot be instanced anymore */
    CurrStruct->e.structinfo->OrgInside = TRUE;
    if ( offset > (int_32)CurrStruct->sym.total_size )
        CurrStruct->sym.total_size = offset;

    return( NOT_ERROR );
}

/* get a qualified type.
 * Used by
 * - TYPEDEF
 * - PROC/PROTO params and LOCALs
 * - EXTERNDEF
 * - EXTERN
 * - LABEL
 * - ASSUME for GPRs
 */
ret_code GetQualifiedType( int *pi, struct asm_tok tokenarray[], struct qualified_type *pti )
/*******************************************************************************************/
{
    int             type;
    int             tmp;
    enum memtype    mem_type;
    int             i = *pi;
    int             distance = FALSE;
    struct asym     *sym;

    /* convert PROC token to a type qualifier */
    for ( tmp = i; tokenarray[tmp].token != T_FINAL && tokenarray[tmp].token != T_COMMA; tmp++ )
        if ( tokenarray[tmp].token == T_DIRECTIVE && tokenarray[tmp].tokval == T_PROC ) {
            tokenarray[tmp].token = T_STYPE;
            tokenarray[tmp].tokval = ( ( SIZE_CODEPTR & ( 1 << ModuleInfo.model ) ) ? T_FAR : T_NEAR );
        }
    /* with NEAR/FAR, there are several syntax variants allowed:
     * 1. NEARxx | FARxx
     * 2. PTR NEARxx | FARxx
     * 3. NEARxx | FARxx PTR [<type>]
     */
    /* read qualified type */
    for ( type = ERROR; tokenarray[i].token == T_STYPE || tokenarray[i].token == T_BINARY_OPERATOR; i++ ) {
        if ( tokenarray[i].token == T_STYPE ) {
            tmp = tokenarray[i].tokval;
            if ( type == ERROR )
                type = tmp;
            mem_type = GetMemtypeSp( tmp );
            if ( mem_type == MT_FAR || mem_type == MT_NEAR ) {
                if ( distance == FALSE ) {
                    uint_8 Ofssize = GetSflagsSp( tmp );
                    pti->is_far = ( mem_type == MT_FAR );
                    if ( Ofssize != USE_EMPTY )
                        pti->Ofssize = Ofssize;
                    distance = TRUE;
                } else if ( tokenarray[i-1].tokval != T_PTR )
                    break;
            } else {
                if ( pti->is_ptr )
                    pti->ptr_memtype = mem_type;
                i++;
                break;
            }
        } else if ( tokenarray[i].tokval == T_PTR ) {
            /* v2.06: avoid to use ST_PTR */
            //type = ST_PTR;
            type = EMPTY;
            pti->is_ptr++;
        } else
            break;
    }

    /* v2.06: don't use ST_PTR anymore! */
    //if ( type == ST_PTR ) {
    if ( type == EMPTY ) {
        if ( tokenarray[i].token == T_ID && tokenarray[i-1].tokval == T_PTR ) {
            pti->symtype = SymSearch( tokenarray[i].string_ptr );
            if ( pti->symtype == NULL || pti->symtype->state == SYM_UNDEFINED )
                pti->symtype = CreateTypeSymbol( pti->symtype, tokenarray[i].string_ptr, TRUE );
            else if ( pti->symtype->state != SYM_TYPE ) {
                return( EmitErr( INVALID_QUALIFIED_TYPE, tokenarray[i].string_ptr ) );
            } else {
                sym = pti->symtype;
                /* if it's a typedef, simplify the info */
                if ( sym->typekind == TYPE_TYPEDEF ) {
                    pti->is_ptr     += sym->is_ptr;
                    if ( sym->is_ptr == 0 ) {
                        /* v2.06b: alias types have MT_TYPE, dont use for ptr_memtype! */
                        //pti->ptr_memtype = sym->mem_type;
                        pti->ptr_memtype = ( sym->mem_type != MT_TYPE ? sym->mem_type : MT_EMPTY );
                        if ( distance == FALSE && pti->is_ptr == 1 &&
                            ( sym->mem_type == MT_NEAR ||
                             sym->mem_type == MT_PROC ||
                             sym->mem_type == MT_FAR ) )
                            pti->is_far = sym->isfar;
                            if ( sym->Ofssize != USE_EMPTY )
                                pti->Ofssize = sym->Ofssize;
                    } else {
                        pti->ptr_memtype = sym->ptr_memtype;
                        if ( distance == FALSE && pti->is_ptr == 1 ) {
                            pti->is_far = sym->isfar;
                            if ( sym->Ofssize != USE_EMPTY )
                                pti->Ofssize = sym->Ofssize;
                        }
                    }
                    if ( sym->mem_type == MT_TYPE )
                        pti->symtype  = sym->type;
                    else {
                        DebugMsg1(("GetQualifiedType: memtype=%X, symtype set by target_type\n", sym->mem_type ));
                        pti->symtype  = sym->target_type;
                    }
                }
            }
            i++;
        }
    }

    if( type == ERROR ) {
        if ( tokenarray[i].token != T_ID ) {
            if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_COMMA )
                EmitError( QUALIFIED_TYPE_EXPECTED );
            else {
                EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr );
                i++;
            }
            return( ERROR );
        }
        pti->symtype = SymSearch( tokenarray[i].string_ptr );
        if( pti->symtype == NULL || pti->symtype->state != SYM_TYPE ) {
            DebugMsg(("GetQualifiedType: invalid type : %s\n", tokenarray[i].string_ptr ));
            if ( pti->symtype == NULL || pti->symtype ->state == SYM_UNDEFINED )
                EmitErr( SYMBOL_NOT_DEFINED, tokenarray[i].string_ptr );
            else
                EmitErr( INVALID_QUALIFIED_TYPE, tokenarray[i].string_ptr );
            return( ERROR );
        }
        sym = pti->symtype;
        if ( sym->typekind == TYPE_TYPEDEF ) {
            pti->mem_type = sym->mem_type;
            pti->is_far   = sym->isfar;
            pti->is_ptr   = sym->is_ptr;
            pti->Ofssize  = sym->Ofssize;
            pti->size     = sym->total_size;
            pti->ptr_memtype = sym->ptr_memtype;
            if ( sym->mem_type == MT_TYPE )
                pti->symtype  = sym->type;
            else {
                DebugMsg1(("GetQualifiedType: memtype=%X, symtype set by target_type\n", sym->mem_type ));
                pti->symtype  = sym->target_type;
            }
        } else {
            pti->mem_type = MT_TYPE;
            pti->size = sym->total_size;
        }
        i++;
    } else {
        if ( pti->is_ptr )
            pti->mem_type = MT_PTR;
        else
            pti->mem_type = GetMemtypeSp( type );
        if ( pti->mem_type == MT_PTR ) {
            /* v2.16: if pointer to data and no distance entered, use memory model to set pointer distance */
            if ( distance == FALSE && !(pti->ptr_memtype &  MT_SPECIAL_MASK ) )
                pti->is_far = ( SIZE_DATAPTR & ( 1 << ModuleInfo.model ) ) ? 1 : 0;
            pti->size = SizeFromMemtype( pti->is_far ? MT_FAR : MT_NEAR, pti->Ofssize, NULL );
        } else
            pti->size = SizeFromMemtype( pti->mem_type, pti->Ofssize, NULL );
    }
    *pi = i;
    DebugMsg1(("GetQualifiedType: i=%u, memtype=%Xh, ptr=%u, far=%u, ofssize=%d, arbtype=%s:%X\n",
               i, pti->mem_type, pti->is_ptr, pti->is_far, pti->Ofssize,
               pti->symtype ? pti->symtype->name : "NULL",
               pti->symtype ? pti->symtype->mem_type : 0 ));
    return( NOT_ERROR );
}

/* TYPEDEF directive. Syntax is:
 * <type name> TYPEDEF [proto|[far|near [ptr]]<qualified type>]
 */
ret_code TypedefDirective( int i, struct asm_tok tokenarray[] )
/*************************************************************/
{
    struct asym         *sym;
    char                *name;
    struct qualified_type ti;

    DebugMsg1(("TypedefDirective(%d) enter\n", i));

    if( i != 1 ) {
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
    }
    name = tokenarray[0].string_ptr;

    i++; /* go past TYPEDEF */

    sym = SymSearch( name );
    if ( sym == NULL || sym->state == SYM_UNDEFINED ) {
        sym = CreateTypeSymbol( sym, name, TRUE );
        if ( sym == NULL )
            return( ERROR );
#if TYPEOPT
        /* release the structinfo data extension */
        LclFree( ((struct dsym *)sym)->e.structinfo );
        ((struct dsym *)sym)->e.structinfo = NULL;
#endif
    } else {
        /* MASM allows to have the TYPEDEF included multiple times */
        /* but the types must be identical! */
        if ( ( sym->state != SYM_TYPE ) ||
            ( sym->typekind != TYPE_TYPEDEF &&
             sym->typekind != TYPE_NONE ) ) {
            return( EmitErr( SYMBOL_REDEFINITION, sym->name ) );
        }
    }

    sym->isdefined = TRUE;
    if ( Parse_Pass > PASS_1 )
        return( NOT_ERROR );
    sym->typekind = TYPE_TYPEDEF;

    /* PROTO is special */
    if ( tokenarray[i].token == T_DIRECTIVE && tokenarray[i].tokval == T_PROTO ) {
        struct dsym *proto;  /* create a PROTOtype item without name */
        /* v2.04: added check if prototype is set already */
        if ( sym->target_type == NULL && sym->mem_type == MT_EMPTY ) {
            proto = (struct dsym *)CreateProc( NULL, "", SYM_TYPE );
            DebugMsg1(("TypedefDirective PROTO, created new unnamed prototype %p\n", proto ));
        } else if ( sym->mem_type == MT_PROC ) {
            proto = (struct dsym *)sym->target_type;
        } else {
            return( EmitErr( SYMBOL_TYPE_CONFLICT, sym->name ) );
        }
        i++;
        DebugMsg1(("TypedefDirective PROTO, call ParseProc(sym=%p i=%d, 0)\n", proto, i));
        if( ParseProc( proto, i, tokenarray, FALSE, ModuleInfo.langtype ) == ERROR )
            return ( ERROR );
        DebugMsg1(("TypedefDirective PROTO, ParseProc() returned status ok\n"));
        sym->mem_type = MT_PROC;
        /* v2.11: member isproc was set inside ParseProc() */
        //proto->sym.isproc = TRUE; /* v2.05: added */
        sym->Ofssize = proto->sym.seg_ofssize;
        /* v2.03: set value of field total_size (previously was 0) */
        sym->total_size = ( 2 << sym->Ofssize );
        if( proto->sym.mem_type != MT_NEAR ) {
            sym->isfar = TRUE; /* v2.04: added */
            sym->total_size += 2;
        }
        sym->target_type = (struct asym *)proto;
        DebugMsg1(("TypedefDirective(%s) ok, mem_type=%Xh, ofssize=%u\n", sym->name, sym->mem_type, sym->Ofssize ));
        return( NOT_ERROR );
    }
    ti.size = 0;
    ti.is_ptr = 0;
    ti.is_far = FALSE;
    ti.mem_type = MT_EMPTY;
    ti.ptr_memtype = MT_EMPTY;
    ti.symtype = NULL;
    ti.Ofssize = ModuleInfo.Ofssize;

    /* "empty" type is ok for TYPEDEF */
    if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_COMMA )
        ;
    else if ( GetQualifiedType( &i, tokenarray, &ti ) == ERROR )
        return( ERROR );

    /* if type did exist already, check for type conflicts
     * v2.05: this code has been rewritten */
    if ( sym->mem_type != MT_EMPTY ) {
        struct asym *to;
        struct asym *tn;
        char oo;
        char on;
        for( tn = ti.symtype; tn && tn->type; tn = tn->type );
        to = ( sym->mem_type == MT_TYPE ) ? sym->type : sym->target_type;
        for( ; to && to->type; to = to->type );
        oo = ( sym->Ofssize != USE_EMPTY ) ? sym->Ofssize : ModuleInfo.Ofssize;
        on = ( ti.Ofssize != USE_EMPTY ) ? ti.Ofssize : ModuleInfo.Ofssize;
        if ( ti.mem_type != sym->mem_type ||
            ( ti.mem_type == MT_TYPE && tn != to ) ||
            ( ti.mem_type == MT_PTR &&
             ( ti.is_far != sym->isfar ||
              on != oo ||
              ti.ptr_memtype != sym->ptr_memtype ||
              tn != to ))) {
            DebugMsg(("TypedefDirective: old-new memtype=%X-%X type=%X(%s)-%X(%s) far=%u-%u ind=%u-%u ofss=%d-%d pmt=%X-%X\n",
                      sym->mem_type, ti.mem_type,
                      (sym->mem_type == MT_TYPE) ? sym->type : sym->target_type,
                      (sym->mem_type == MT_TYPE) ? sym->type->name : sym->target_type ? sym->target_type->name : "",
                      ti.symtype, ti.symtype ? ti.symtype->name : "",
                      sym->isfar, ti.is_far,
                      sym->is_ptr, ti.is_ptr,
                      sym->Ofssize, ti.Ofssize,
                      sym->ptr_memtype, ti.ptr_memtype ));
            return( EmitErr( SYMBOL_TYPE_CONFLICT, name ) );
        }
    }

    sym->mem_type = ti.mem_type;
    sym->Ofssize = ti.Ofssize;
    sym->total_size = ti.size;
    sym->is_ptr = ti.is_ptr;
    sym->isfar = ti.is_far;
    if ( ti.mem_type == MT_TYPE )
        sym->type = ti.symtype;
    else
        sym->target_type = ti.symtype;
    sym->ptr_memtype = ti.ptr_memtype;
    DebugMsg1(("TypedefDirective(%s) ok, mem_type=MT_TYPE, size=%" I32_SPEC "u, type=%p type.memtype=%X\n",
               sym->name, sym->total_size, sym->type, ti.symtype ? ti.symtype->mem_type : 0 ));

    DebugMsg1(("TypedefDirective(%s) ok, mem_type=%Xh, size=%u, indirection=%u target=%p\n", sym->name, sym->mem_type, ti.size, ti.is_ptr, ti.symtype ));

    if ( tokenarray[i].token != T_FINAL ) {
        DebugMsg(("TypedefDirective: unexpected token %u, idx=%u\n", tokenarray[i].token, i));
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
    }

    return( NOT_ERROR );
}

/* RECORD directive
 * syntax: <label> RECORD <bitfield_name>:<size>[,...]
 * defines a RECORD type (state=SYM_TYPE).
 * the memtype will be MT_BYTE, MT_WORD, MT_DWORD [, MT_QWORD in 64-bit].
 */

ret_code RecordDirective( int i, struct asm_tok tokenarray[] )
/************************************************************/
{
    char *name;
    struct asym *sym;
    struct dsym *newr;
    struct dsym *oldr = NULL;
    struct sfield *f;
    int cntBits;
    int define;
    int len;
    int redef_err;
    int count;
    //int value;
    int name_loc;
    int init_loc;
    struct expr opndx;

    DebugMsg1(("RecordDirective(%d) enter\n", i));
    if ( i != 1 ) {
        return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
    }

    name = tokenarray[0].string_ptr;
    sym = SymSearch( name );
    if ( sym == NULL || sym->state == SYM_UNDEFINED ) {
        sym = CreateTypeSymbol( sym, name, TRUE );
    } else if ( sym->state == SYM_TYPE &&
               ( sym->typekind == TYPE_RECORD ||
               sym->typekind == TYPE_NONE ) ) {
        /* v2.04: allow redefinition of record and forward references.
         * the record redefinition may have different initial values,
         * but those new values are IGNORED! ( Masm bug? )
         */
        if ( Parse_Pass == PASS_1 && sym->typekind == TYPE_RECORD ) {
            oldr = (struct dsym *)sym;
            sym = CreateTypeSymbol( NULL, name, FALSE );
            redef_err = 0;
        }
    } else {
        return( EmitErr( SYMBOL_REDEFINITION, name ) );
    }
    sym->isdefined = TRUE;

    if ( Parse_Pass > PASS_1 )
        return( NOT_ERROR );

    newr = (struct dsym *)sym;
    newr->sym.typekind = TYPE_RECORD;

    i++; /* go past RECORD */

    cntBits = 0; /* counter for total of bits in record */
    /* parse bitfields */
    do {
        if ( tokenarray[i].token != T_ID ) {
            EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr );
            break;
        }
        len = strlen( tokenarray[i].string_ptr );
        if( len > MAX_ID_LEN ) {
            EmitError( IDENTIFIER_TOO_LONG );
            break;
        }
        name_loc = i;
        i++;
        if ( tokenarray[i].token != T_COLON ) {
            EmitError( COLON_EXPECTED );
            break;
        }
        i++;
        /* get width */
        if ( EvalOperand( &i, tokenarray, Token_Count, &opndx, 0 ) == ERROR )
            break;
        if ( opndx.kind != EXPR_CONST ) {
            EmitError( CONSTANT_EXPECTED );
            opndx.value = 1;
        }
        if ( opndx.value == 0 ) {
            EmitErr( TOO_FEW_BITS_IN_RECORD, tokenarray[name_loc].string_ptr );
            break;
        } else if ( ( opndx.value + cntBits ) > MAXRECBITS ) {
            EmitErr( TOO_MANY_BITS_IN_RECORD, tokenarray[name_loc].string_ptr );
            break;
        }
        count = 0;
        /* is there an initializer? ('=') */
        if ( tokenarray[i].token == T_DIRECTIVE &&
            tokenarray[i].dirtype == DRT_EQUALSGN ) {
            i++;
            for( init_loc = i; tokenarray[i].token != T_FINAL && tokenarray[i].token != T_COMMA; i++ );
            /* no value? */
            if ( init_loc == i ) {
                EmitErr( SYNTAX_ERROR_EX, tokenarray[name_loc].tokpos );
                break;
            }
            /* v2.09: initial values of record redefinitions are ignored! */
            if ( oldr == NULL )
                count = tokenarray[i].tokpos - tokenarray[init_loc].tokpos;
        }
        /* record field names are global! (Masm design flaw) */
        sym = SymSearch( tokenarray[name_loc].string_ptr );
        define = TRUE;
        if ( oldr ) {
            if ( sym == NULL ||
                sym->state != SYM_STRUCT_FIELD ||
                sym->mem_type != MT_BITS ||
                sym->total_size != opndx.value ) {
                EmitErr( NON_BENIGN_XXX_REDEFINITION, szRecord, tokenarray[name_loc].string_ptr );
                redef_err++;
                define = FALSE; /* v2.06: added */
            }
        } else {
            if ( sym ) {
                EmitErr( SYMBOL_REDEFINITION, sym->name );
                break;
            }
        }

        if ( define ) { /* v2.06: don't add field if there was an error */
            cntBits += opndx.value;
            f = LclAlloc( sizeof( struct sfield ) + count );
            memset( f, 0, sizeof( struct sfield ) );
            f->sym.name_size = len;
            f->sym.name = LclAlloc( len + 1 );
            memcpy( f->sym.name, tokenarray[name_loc].string_ptr, len + 1 );
            f->sym.list = ModuleInfo.cref;
            f->sym.state = SYM_STRUCT_FIELD;
            f->sym.mem_type = MT_BITS;
            f->sym.total_size = opndx.value;
            if ( !oldr ) {
                SymAddGlobal( &f->sym );
            }
            f->next = NULL;
            //f->sym = sym;
            f->ivalue[0] = NULLC;
            //f->init_dir = NULL;
            if( newr->e.structinfo->head == NULL ) {
                newr->e.structinfo->head = newr->e.structinfo->tail = f;
            } else {
                newr->e.structinfo->tail->next = f;
                newr->e.structinfo->tail = f;
            }
            if ( count ) {
                //f->value = LclAlloc( count + 1 );
                memcpy( f->ivalue, tokenarray[init_loc].tokpos, count );
                f->ivalue[count] = NULLC;
            }
        }

        if ( i < Token_Count ) {
            if ( tokenarray[i].token != T_COMMA || tokenarray[i+1].token == T_FINAL ) {
                EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
                break;
            }
            i++;
        }

    } while ( i < Token_Count );

    /* now calc size in bytes and set the bit positions */

    if ( cntBits > 16 ) {
#if AMD64_SUPPORT
        if ( cntBits > 32 ) {
            newr->sym.total_size = sizeof( uint_64 );
            newr->sym.mem_type = MT_QWORD;
        } else {
#endif
            newr->sym.total_size = sizeof( uint_32 );
            newr->sym.mem_type = MT_DWORD;
#if AMD64_SUPPORT
        }
#endif
    } else if ( cntBits > 8 ) {
        newr->sym.total_size = sizeof( uint_16 );
        newr->sym.mem_type = MT_WORD;
    } else {
        newr->sym.total_size = sizeof( uint_8 );
        newr->sym.mem_type = MT_BYTE;
    }
    /* if the BYTE/WORD/DWORD isn't used fully, shift bits to the right! */
    // cntBits = dir->sym.total_size * 8;

    /* set the bit position */
    for ( f = newr->e.structinfo->head; f; f = f->next ) {
        cntBits = cntBits - f->sym.total_size;
        f->sym.offset = cntBits;
    }
    if ( oldr ) {
        if ( redef_err > 0 ||
            AreStructsEqual( newr, oldr ) == FALSE )
            EmitErr( NON_BENIGN_XXX_REDEFINITION, szRecord, newr->sym.name );
        /* record can be freed, because the record's fields are global items.
         * And initial values of the new definition are ignored!
         */
        SymFree( (struct asym *)newr );
    }
    DebugMsg(("RecordDirective(%s) exit, no error\n", name ));
    return( NOT_ERROR );
}

void DeleteType( struct dsym *dir )
/*********************************/
{
    struct sfield      *curr;
    struct sfield      *next;

    DebugMsg(("DeleteType(%s) enter, typekind=%u, memtype=%Xh\n", dir->sym.name, dir->sym.typekind, dir->sym.mem_type ));
#if FASTMEM==0
    /* release prototype in target_type if typedef is PROTO */
    if ( dir->sym.mem_type == MT_PROC ) {
        /* v2.11: change the prototype type to an external, because SymFree()
         * doesn't expect a prototype in a type.
         */
        dir->sym.target_type->state = SYM_EXTERNAL;
        SymFree( dir->sym.target_type );
    }
#endif
#if TYPEOPT
    if ( dir->sym.typekind == TYPE_TYPEDEF )
        return;
#endif
    /* bitfields field names are global, don't free them here! */
    if ( dir->sym.typekind != TYPE_RECORD )
        for( curr = dir->e.structinfo->head; curr != NULL; curr = next ) {
            next = curr->next;
#if FASTMEM==0
            if ( curr->sym.name_size ) LclFree( curr->sym.name );
#endif
            LclFree( curr );
        }
    LclFree( dir->e.structinfo );
    return;
}

