#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "o.h"

#define LINE_SIZE   80

/*
** Local Prototypes
*/
SEG_T *find_seg_by_name( char * );
char *ignore_whitespace( char * );
char *get_text( char *, char * );
char *get_num( char *, dword * );

struct hint_word_s {
    char    *text;
    int     type;
};

struct hint_word_s hint_words[] = {
    { "DB", BYTE_PTR },
    { "DW", WORD_PTR },
    { "DD", DWORD_PTR },
    { "DF", FWORD_PTR },
    { "DQ", QWORD_PTR },
    { "DT", TBYTE_PTR },
};

int hint_nwords = sizeof(hint_words)/sizeof(char *);

int hint_compare( rec_1, rec_2 )
    HINT_T  *rec_1;
    HINT_T  *rec_2;
{
    if ( rec_1->seg_idx > rec_2->seg_idx ) {
        return( LEFT );
    } else {
        if ( rec_1->seg_idx < rec_2->seg_idx ) {
            return( RIGHT );
        } else {
            if ( rec_1->offset > rec_2->offset ) {
                return( LEFT );
            } else {
                if ( rec_1->offset < rec_2->offset ) {
                    return( RIGHT );
                } else {
                    return( EQUAL );
                }
            }
        }
    }
}



void hint_insert( int seg_idx, dword offset, int hint_type, dword length )
{
    HINT_T          *hint_rec;

    hint_rec = (HINT_T *)o_malloc( sizeof(HINT_T) );
    hint_rec->seg_idx   = seg_idx;
    hint_rec->offset    = offset;
    hint_rec->hint_type = hint_type;
    hint_rec->length    = length;
    insert( (char *)hint_rec, hint_tree, TC hint_compare );
}

SEG_T *find_seg_by_name( seg_text )
    char    *seg_text;
{
    SEG_T   *seg_rec;
    NODE_T  *seg_node;
    NAME_T  search;
    NAME_T  *name;

    seg_node = start( segment_tree, RIGHT );
    while ( seg_node != segment_tree ) {
        seg_rec = (SEG_T *)seg_node->data;
        search.index = seg_rec->name;
        name = (NAME_T *)find( (char *)&search, name_tree,
                                            TC name_compare, NULL );
        if ( name == NULL ) fmt_error( "Undefined segment name" );

        if ( strcmp(name->name,seg_text) == 0 ) {
            return( seg_rec );
        }
        seg_node = traverse( seg_node, RIGHT );
    }
    return( NULL );
}

char *ignore_whitespace( start )
    char    *start;
{
    char    *pc;
    char    ch;

    pc = start;
    while ( (ch=*pc) != '\0' ) {
        if ( ch != ' ' && ch != '\t' && ch != '\n' ) {
            break;
        }
        pc++;
    }
    return( pc );
}

char *get_text( destination, start )
    char    *destination;
    char    *start;
{
    char    *pc;
    char    ch;
    int     len;

    len = 0;
    pc = start;
    while ( (ch=*pc) != ' ' && ch != '\t' && ch != '=' && ch != '\0'
            && ch != ':' && ch != '\n' ) {
        *destination++ = ch;
        pc++;
        len++;
        if ( len == NAMESIZE ) {
            break;
        }
    }
    *destination = '\0';
    return( pc );
}

char *get_num( start, value )
    char            *start;
    dword   *value;
{
    char            *pc;
    char            ch;
    dword   dec_form;
    dword   hex_form;
    int             is_hex;
    int             decable;
    int             char_ok;

    dec_form = 0L;
    hex_form = 0L;

    pc = start;
    is_hex = FALSE;
    decable = TRUE;

    while ( (ch=*pc) != '\0' ) {
        char_ok = FALSE;
        if ( ch >= '0' && ch <= '9' ) {
            dec_form *= 10;
            dec_form += ch - '0';
            hex_form *= 16;
            hex_form += ch - '0';
            char_ok = TRUE;
        }
        if ( ch >= 'a' && ch <= 'f' ) {
            decable = FALSE;
            hex_form *= 10;
            hex_form += ch - 'a' + 10;
            char_ok = TRUE;
        }
        if ( ch >= 'A' && ch <= 'F' ) {
            decable = FALSE;
            hex_form *= 10;
            hex_form += ch - 'A' + 10;
            char_ok = TRUE;
        }
        if ( ch == 'h' || ch == 'H' ) {
            is_hex = TRUE;
            pc++;
            break;
        }
        if ( !char_ok ) {
            break;
        }
        pc++;
    }
    if ( is_hex ) {
        *value = hex_form;
    } else {
        if ( decable ) {
            *value = dec_form;
        } else {
            *value = 0L;
        }
    }
    return( pc );
}

void load_extra( exename, filename )
    char            *exename;
    char            *filename;
{
    FILE            *e_file;
    char            temp_name[50];
    char            e_line[LINE_SIZE+1];
    int             line_num;
    char            *pc;
    char            *semicolon;
    char            *equal;
    char            *colon;
    char            seg_text[NAMESIZE+1];
    char            lab_text[NAMESIZE+1];
    int             segment_type;
    dword   disp;
    int             count;

#ifdef DEBUG
    printf("Loading from extra file %s\n", filename );
#endif

    strcpy( temp_name, filename );
    if ( strchr(temp_name,'.') == NULL ) {  /* Append ".add" if not extension */
        strcat( temp_name, ".add" );        /* is supplied                    */
    }
    e_file = fopen( temp_name, "r" );
    if ( e_file == NULL ) {
        fprintf( stderr, "%s: Cannot open %s\n", exename, temp_name );
        exit(6);
    }

    line_num = 1;

    /*
    ** Process lines of format:
    **
    **  SEG segname CODE/DATA                 Pick segment type
    **  labname = segname : offset            Create label
    **  segname : offset : DB/DW/DD/DF/DQ/DT  Control dis-assembly
    */

    while ( fgets(e_line,LINE_SIZE,e_file) != NULL ) {
        semicolon = strchr( e_line, ';' );
        if ( semicolon ) {
            *semicolon = '\0';
        }
        pc = ignore_whitespace( e_line );
#ifdef DEBUG
fprintf(stderr, "%s", pc );
#endif
        if ( strnicmp(pc,"SEG ",4) == 0 ) {
            pc = ignore_whitespace( pc+4 );
            pc = get_text( seg_text, pc );
            pc = ignore_whitespace( pc );
            segment_type = 0;
            if ( strnicmp(pc,"CODE",4) == 0 ) {
                pc += 4;
                segment_type = 1;
            }
            if ( strnicmp(pc,"DATA",4) == 0 ) {
                pc += 4;
                segment_type = 2;
            }
            if ( segment_type == 0 ) {
                fprintf( stderr,
                  "%s: Syntax error on line %d of %s (should be CODE/DATA).\n",
                  exename, line_num, temp_name );
                exit(7);
            }
            seg_rec = find_seg_by_name( seg_text );
            if ( !seg_rec ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (segment '%s' not found)\n",
                    exename, line_num, temp_name, seg_text );
                exit(7);
            }
            if ( segment_type == 1 ) {
                seg_rec->code = TRUE;
            } else {
                seg_rec->code = FALSE;
            }
        }
        equal = strchr(pc, '=' );
        if ( equal ) {
            pc = get_text(lab_text, pc );
            pc = ignore_whitespace(pc);
            if ( strnicmp(pc,"=",1) != 0 ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (expecting '=' after label)\n",
                    exename, line_num, temp_name );
                exit(7);
            }
            pc=ignore_whitespace(pc+1);
            colon=strchr(pc,':');
            if ( colon == NULL ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (expecting ':')\n",
                    exename, line_num, temp_name );
                exit(7);
            }
            pc = get_text( seg_text, pc );
            pc = ignore_whitespace( colon+1 );
            pc = get_num( pc, &disp );
            seg_rec = find_seg_by_name( seg_text );
            if ( !seg_rec ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (segment '%s' not found)\n",
                    exename, line_num, temp_name, seg_text );
                exit(7);
            }
            pub_insert( seg_rec->index, disp, lab_text, LOCAL, FALSE );
        }
        colon = strchr(pc,':');
        if ( colon ) {
            pc = get_text( seg_text, pc );
            seg_rec = find_seg_by_name( seg_text );
            if ( !seg_rec ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (segment '%s' not found)\n",
                    exename, line_num, temp_name, seg_text );
                exit(7);
            }
            pc = ignore_whitespace( pc );
            if ( strnicmp(pc,":",1) != 0 ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (expecting ':' seperator)\n",
                    exename, line_num, temp_name );
                exit(7);
            }
            pc = get_num( pc+1, &disp );
            pc = ignore_whitespace( pc );
            if ( strnicmp(pc,":",1) != 0 ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (expecting ':' seperator)\n",
                    exename, line_num, temp_name );
                exit(7);
            }
            pc = ignore_whitespace( pc+1 );
            pc = get_text( lab_text, pc );  /* Is really data type text */
            count = 0;
            while ( count < hint_nwords ) {
                if ( stricmp(lab_text,hint_words[count].text) == 0 ) {
                    break;
                }
                count++;
            }
            if ( count == hint_nwords ) {
                fprintf( stderr,
            "%s: Syntax error on line %d of %s (expecting DB/DW/DD/DF/DQ/DT)\n",
                    exename, line_num, temp_name );
                exit(7);
            }
            hint_insert(seg_rec->index,disp,hint_words[count].type,1);
        }
        pc = ignore_whitespace( pc );
        if ( strlen(pc) != 0 ) {
                fprintf( stderr,
       "%s: Syntax error on line %d of %s (too many characters on line '%s')\n",
                    exename, line_num, temp_name, pc );
                exit(7);
        }
        line_num++;
    }

#ifdef DEBUG
fprintf(stderr, "\n", e_line );
#endif

    fclose( e_file );
}
