static char rcsver[] = "$Id: header.c,v 1.5 2000/06/05 17:14:50 asbms Exp $";
 
/**
 ** $Source: /tes/cvs/vanilla/header.c,v $
 **
 ** $Log: header.c,v $
 ** Revision 1.5  2000/06/05 17:14:50  asbms
 ** Removed check for VANILLA keyword
 **
 ** Revision 1.4  2000/05/26 15:04:52  asbms
 ** Added fixes for smoother return from errors
 **
 ** Revision 1.3  2000/05/25 02:24:26  saadat
 ** Fixed IRTM temperature generation.
 **
 ** Revision 1.2  1999/11/19 21:19:46  gorelick
 ** Version 3.1, post PDS delivery of 3.0
 **
 ** Revision 1.1.1.1  1999/10/15 19:30:35  gorelick
 ** Version 3.0
 **
 **
 ** Revision 1.4  1998/11/12 22:58:55  gorelick
 ** first release version
 **
 **/

#include <limits.h>
#include "header.h"
#include "proto.h"
#include "io_lablib3.h"

#define GetKey(ob, name)        OdlFindKwd(ob, name, NULL, 0, ODL_THIS_OBJECT)

FIELD *MakeField(OBJDESC *, LABEL *);
int DetermineFieldType(char *type, int size);
IFORMAT eformat_to_iformat(EFORMAT e);
void MakeBitFields(OBJDESC *col, FIELD *f, LIST *list);
FIELD * MakeBitField(OBJDESC *col, FIELD *f);
EFORMAT ConvertType(char *type);

/**
** LoadLabel() - Read and decode a PDS label, including individual fields.
 **/
LABEL *
LoadLabel(char *fname)
{
    OBJDESC *ob, *tbl, *col;
    KEYWORD *kw;
    FIELD *f;
    LIST *list;
    LABEL *l;
    int i;
    ushort scope;
    int reclen, nfields;
    char *name, *alias = NULL;
	 char *Id;

    ob = OdlParseLabelFile(fname, NULL, ODL_EXPAND_STRUCTURE, 1);
    if (ob == NULL) {
        fprintf(stderr, "Unable to read file: %s\n", fname);
        return (NULL);
    }
    /* find the first (and only?) table object */
    if ((tbl = OdlFindObjDesc(ob,"TABLE",NULL,NULL, 0, ODL_TO_END)) == NULL) {
        fprintf(stderr, "Unable to find TABLE object: %s\n", fname);
        fprintf(stderr, "Is this a vanilla file?\n");
        return (NULL);
    }


    if ((kw = OdlFindKwd(tbl, "ROW_BYTES", NULL,0, ODL_THIS_OBJECT)) != NULL) {
        reclen = atoi(OdlGetKwdValue(kw));
    } else {
        fprintf(stderr, "Unable to find keyword: ROW_BYTES: %s\n", fname);
        fprintf(stderr, "Is this a vanilla file?  Is it's ^STRUCTURE file ok?\n");
        return (NULL);
    }

    if ((kw = OdlFindKwd(tbl, "COLUMNS", NULL, 0, ODL_THIS_OBJECT)) != NULL) {
        nfields = atoi(OdlGetKwdValue(kw));
    } else {
        fprintf(stderr, "Unable to find keyword: COLUMNS\n");
        return (NULL);
    }


    if ((kw = OdlFindKwd(tbl, "NAME", NULL, 0, ODL_THIS_OBJECT)) != NULL) {
        name = OdlGetKwdValue(kw);
    }

    l = calloc(1, sizeof(LABEL));
    l->reclen = reclen;
    l->nfields = nfields;
    l->name = name;

    /**
     ** get all the column descriptions
     **/

    list = new_list();
    i = 0;
    scope = ODL_CHILDREN_ONLY;
    col = tbl ; 
    while ((col = OdlNextObjDesc(col, 0, &scope)) != NULL) {
        if ((f = MakeField(col, l)) != NULL) {
			list_add(list, f);
			i++;

			/**
			 ** Fake up some additional fields for bit columns.
			 **/
			if (f->eformat == MSB_BIT_FIELD) {
				MakeBitFields(col, f, list);
			}
		}
    }

    if (i != nfields) {
        fprintf(stderr,
                "Wrong number of column definitions.  Expected %d, got %d.\n",
                nfields,i);
    }

    l->fields = list;

	/**
	 ** Get list of key fields
	 **/

    if ((kw = GetKey(tbl, "PRIMARY_KEYS")) != NULL ||
		(kw = GetKey(tbl, "PRIMARY_KEY")) != NULL || 
		(kw = GetKey(tbl, "KEYS")) != NULL) {
        int j;
        LIST *keylist;
        FIELD *f;
        char keyname[256];
        char **array;

        i = OdlGetAllKwdValuesArray(kw, &array);

        keylist = new_list();

        for (j = 0; j < i; j++) {
            strcpy(keyname, &array[j][1]);
            keyname[strlen(keyname) - 1] = 0;
            if ((f = FindFieldInLabel(keyname, l)) == NULL) {
                fprintf(stderr,
                        "Unable to find key: \"%s\" in label \"%s\"\n", 
                        keyname, l->name);
                return(NULL);
            }
            list_add(keylist, f);
        }
        l->keys = keylist;
    }
    return (l);
}

/**
 ** Convert a field description into a FIELD struct
 **/

FIELD *
MakeField(OBJDESC *col, LABEL *l)
{
    FIELD *f;
    VARDATA *vardata;
    KEYWORD *kw;
    int i = 0;
    char *ptr;
	
    do {
        f = (FIELD *) calloc(1, sizeof(FIELD));
        f->label = l;

        if ((kw = GetKey(col, "NAME")) == NULL) {
            fprintf(stderr, "Column %d has no name.\n", i);
            break;
        }
        f->name = OdlGetKwdValue(kw);

		f->alias = NULL;
        if ((kw = GetKey(col, "ALIAS_NAME")) != NULL) {
			f->alias = OdlGetKwdValue(kw);
        }


        if ((kw = GetKey(col, "START_BYTE")) == NULL) {
            fprintf(stderr, "Column %s: START_BYTE not specified.\n", f->name);
            break;
        }
        f->start = atoi(OdlGetKwdValue(kw)) - 1;

        if ((kw = GetKey(col, "BYTES")) == NULL) {
            fprintf(stderr, "Column %s: BYTES not specified.\n", f->name);
            break;
        }
        f->size = atoi(OdlGetKwdValue(kw));


        if ((kw = GetKey(col, "ITEMS")) != NULL) {
            f->dimension = atoi(OdlGetKwdValue(kw));
            /**
            ** If BYTES was specified, this will overwrite the value
            ** with the indivdual element size, as we expect.
            **/
            if ((kw = GetKey(col, "ITEM_BYTES")) != NULL) {
                f->size = atoi(OdlGetKwdValue(kw));
            } else {
                fprintf(stderr, "Column %s: ITEM_BYTES not specified, dividing BYTES by ITEMS.\n", f->name);
                f->size = f->size / f->dimension;
            }
        }

        if ((kw = GetKey(col, "DATA_TYPE")) == NULL) {
            fprintf(stderr, "Column %s: DATA_TYPE not specified.\n", f->name);
            break;
        }
		f->type = OdlGetKwdValue(kw);
		if ((f->eformat = ConvertType(f->type)) == INVALID_EFORMAT) {
            fprintf(stderr, "Unrecognized type: %s, %s %d bytes\n",
                    f->name, f->type, f->size);
			break;
		}

		f->iformat = eformat_to_iformat(f->eformat);

        if ((kw = GetKey(col, "SCALING_FACTOR")) != NULL) {
            f->scale = atof(OdlGetKwdValue(kw));
        }

        if ((kw = GetKey(col, "OFFSET")) != NULL) {
            f->offset = atof(OdlGetKwdValue(kw));
        }

        if ((kw = GetKey(col, "VAR_RECORD_TYPE")) != NULL) {
            ptr = OdlGetKwdValue(kw);
            vardata = f->vardata = calloc(1, sizeof(VARDATA));

            if (!strcmp(ptr, "VAX_VARIABLE_LENGTH")) vardata->type = VAX_VAR;
            else if (!strcmp(ptr, "Q15")) vardata->type = Q15;
            else {
                fprintf(stderr, "Unrecognized VAR_DATA_TYPE: %s\n", ptr);
            }

            if ((kw = GetKey(col, "VAR_ITEM_BYTES")) != NULL) {
                vardata->size = atoi(OdlGetKwdValue(kw));
            
                if ((kw = GetKey(col, "VAR_DATA_TYPE")) != NULL) {
                    ptr = OdlGetKwdValue(kw);
                } else {
					fprintf(stderr, "VAR_DATA_TYPE not specified for field: %s\n", 
								f->name);
					exit(1);
				}
                if ((vardata->eformat = ConvertType(ptr)) == INVALID_EFORMAT) {
                    fprintf(stderr, "Unrecognized vartype: %s, %s %d bytes\n",
                            f->name, ptr, vardata->size);
                }
				vardata->iformat = eformat_to_iformat(vardata->eformat);
            } else {
				fprintf(stderr, "VAR_ITEM_BYTES not specified for field: %s\n", 
							f->name);
				exit(1);
			}
        }

		if (f->eformat == BYTE_OFFSET) {
			f->eformat = MSB_INTEGER;
			f->iformat = eformat_to_iformat(f->eformat);
            vardata = f->vardata = calloc(1, sizeof(VARDATA));
			vardata->size = 2;
			vardata->eformat = MSB_INTEGER;
			vardata->iformat = INT;
			vardata->type = Q15;
		}

		return(f);
    } while(0);

	return(NULL);
}

void
MakeBitFields(OBJDESC *col, FIELD *f, LIST *list)
{
    ushort scope;
	FIELD *b;

    scope = ODL_CHILDREN_ONLY;
    while ((col = OdlNextObjDesc(col, 0, &scope)) != NULL) {
        if ((b = MakeBitField(col, f)) != NULL) {
			list_add(list, b);
		}
    }
}

FIELD *
MakeBitField(OBJDESC *col, FIELD *f)
{
	FIELD *f2;
    KEYWORD *kw;
	BITFIELD *b;
	char name[256], *ptr;
	int i = 1;

	/**
	 ** Do this once, and allow for breaks
	 **/
	do {
        f2 = (FIELD *) calloc(1, sizeof(FIELD));
		*f2 = *f;

        b = (BITFIELD *) calloc(1, sizeof(BITFIELD));
		f2->bitfield = b;

        if ((kw = GetKey(col, "NAME")) == NULL) {
            fprintf(stderr, "Bitfield %d has no name.\n", i);
            break;
        }
		sprintf(name, "%s:%s", f->name, OdlGetKwdValue(kw));
        f2->name = strdup(name);

        if ((kw = GetKey(col, "ALIAS_NAME")) != NULL) {
			sprintf(name, "%s:%s", f->name, OdlGetKwdValue(kw));
			f2->alias = strdup(name);
		}

        if ((kw = GetKey(col, "BIT_DATA_TYPE")) == NULL) {
            fprintf(stderr, "Bitfield %s has no data type.\n", f2->name);
            break;
        }
		ptr = OdlGetKwdValue(kw);
		b->type = ConvertType(ptr); /* b->type contains the EFORMAT */
		f2->iformat = eformat_to_iformat(b->type); /* Saadat - I think! */

		if ((kw = GetKey(col, "START_BIT")) == NULL) {
            fprintf(stderr, "Bitfield %s has no start bit.\n", f2->name);
            break;
        }
		b->start_bit = atoi(OdlGetKwdValue(kw));

		if ((kw = GetKey(col, "BITS")) == NULL) {
            fprintf(stderr, "Bitfield %s has no BITS value.\n", f2->name);
            break;
        }
		b->bits = atoi(OdlGetKwdValue(kw));

		b->shifts = (f->size * 8) - b->start_bit - b->bits + 1;
		/* b->mask = (1 << b->bits) - 1; -- fails for bits=32 */
		b->mask = UINT_MAX;
		b->mask >>= -(b->bits - 32);

		return(f2);
	} while (0);

	return(NULL);
}


IFORMAT
eformat_to_iformat(EFORMAT e)
{
	switch(e) {
		case MSB_INTEGER:
		case ASCII_INTEGER:
			return(INT);

		case MSB_BIT_FIELD:
		case MSB_UNSIGNED_INTEGER:
		case BYTE_OFFSET:
			return(UINT);

		case IEEE_REAL:
		case ASCII_REAL:
			return ( REAL );

		case CHARACTER:
			return ( STRING );

		default:
			fprintf(stderr, "Unrecognized etype: %d\n", e);
	}

	return INVALID_IFORMAT;
}

EFORMAT
ConvertType(char *type) 
{
    if (!strcasecmp(type, "MSB_INTEGER") ||
		!strcasecmp(type, "SUN_INTEGER") ||
		!strcasecmp(type, "MAC_INTEGER") ||
		!strcasecmp(type, "INTEGER")) {
			return(MSB_INTEGER);
    } else if (!strcasecmp(type, "MSB_UNSIGNED_INTEGER") ||
               !strcasecmp(type, "SUN_UNSIGNED_INTEGER") || 
               !strcasecmp(type, "MAC_UNSIGNED_INTEGER") || 
               !strcasecmp(type, "UNSIGNED_INTEGER")) {
			return(MSB_UNSIGNED_INTEGER);
    } else if (!strcasecmp(type, "IEEE_REAL") ||
               !strcasecmp(type, "SUN_REAL") ||
               !strcasecmp(type, "MAC_REAL") ||
               !strcasecmp(type, "REAL")) {
			return(IEEE_REAL);
    } else if (!strcasecmp(type, "CHARACTER")) {
		return(CHARACTER);
    } else if (!strcasecmp(type, "ASCII_INTEGER")) {
		return(ASCII_INTEGER);
    } else if (!strcasecmp(type, "ASCII_REAL")) {
		return(ASCII_REAL);
    } else if (!strcasecmp(type, "BYTE_OFFSET")) {
		return(BYTE_OFFSET);
    } else if (!strcasecmp(type, "MSB_BIT_STRING")) {
		return(MSB_BIT_FIELD);
	}
	return(INVALID_EFORMAT);
}

 
/**
 ** Given a field name, locate it in the list of labels.
 **/
FIELD *
FindField(char *name, LIST *tables)
{
    char buf[256];
    char *p;
    char *field_name;
    char *label_name;
    int i;
	TABLE *t;
    LABEL *l;
    FIELD *f;

    strcpy(buf, name);

    if ((p = strchr(buf, '.')) != NULL) {
        *p = '\0';
        label_name = buf;
        field_name = p + 1;
    } else {
        label_name = NULL;
        field_name = buf;
    }

    /**
    ** If this field name includes a dimension, get rid of it
    **/
    if ((p = strchr(field_name, '[')) != NULL) {
        *p = '\0';
    }
    for (i = 0; i < tables->number; i++) {
        t = (tables->ptr)[i];
		l = t->label;
        /*
        ** If the user told us what struct the field is in, skip all others
        */
        if (label_name && strcasecmp(label_name, l->name))
            continue;

        if ((f = FindFieldInLabel(field_name, l)) != NULL)  {
            return(f);
        }
    }

    return (NULL);
}

FIELD *
FindFieldInLabel(char *name, LABEL * l)
{
    int i;
    FIELD **f = (FIELD **) l->fields->ptr;
    int nfields = l->fields->number;

    for (i = 0; i < nfields; i++) {
        if (!strcasecmp(name, f[i]->name)) {
            return (f[i]);
        }
        if (f[i]->alias && !strcasecmp(name, f[i]->alias)) {
            return (f[i]);
        }
    }
    return (NULL);
}


/**
 ** Load the header values specific to an individual file
 **/

FRAGMENT *
LoadFragment(char *fname, TABLE *table)
{
    OBJDESC *ob, *tbl, *col;
    KEYWORD *kw;
    LIST *startlist=NULL, *endlist=NULL;
    FRAGMENT *f;
    int rows;
    int offset;
    struct stat sbuf;

    if (stat(fname, &sbuf) == -1) {
        fprintf(stderr, "Unable to find file: %s\n", fname);
        return(NULL);
    }
    
    ob = OdlParseLabelFile(fname, NULL, ODL_EXPAND_STRUCTURE, 1);
    if (ob == NULL) {
        fprintf(stderr, "Unable to read file: %s\n", fname);
        return (NULL);
    }
    
    if ((kw = OdlFindKwd(ob, "^TABLE", NULL, 0, ODL_THIS_OBJECT)) != NULL) {
        offset = atoi(OdlGetKwdValue(kw));
    } else {
        fprintf(stderr, "Unable to find table pointer (^TABLE) in: %s\n",
                fname);
        return (NULL);
    }


    /* find the first (and only?) table object */
    if ((tbl = OdlFindObjDesc(ob, "TABLE", NULL, NULL, 0, ODL_TO_END)) == NULL) {
        fprintf(stderr, "Unable to find TABLE object: %s\n", fname);
        return (NULL);
    }

    if ((kw = OdlFindKwd(tbl, "ROWS", NULL,0, ODL_THIS_OBJECT)) != NULL) {
        rows = atoi(OdlGetKwdValue(kw));
    } else {
        fprintf(stderr, "Unable to find keyword ROWS: %s\n", fname);
        return (NULL);
    }
    
    if ((kw = GetKey(tbl, "START_KEYS")) != NULL || 
		(kw = GetKey(tbl, "START_PRIMARY_KEY")) != NULL) {
        int i,j;
        char **array;
        DATA *dataval;
        FIELD **keys = (FIELD **)table->label->keys->ptr;

        i = OdlGetAllKwdValuesArray(kw, &array);

        startlist = new_list();
        dataval = calloc(i, sizeof(DATA));

        for (j = 0; j < i; j++) {
            dataval[j] = ConvertASCIItoData(array[j], keys[j]->iformat);
            list_add(startlist, &dataval[j]);
        }
    } else if ((kw = GetKey(tbl, "START_KEY")) != NULL) {
        DATA *dataval;
        FIELD **keys = (FIELD **)table->label->keys->ptr;

        startlist = new_list();
        dataval = calloc(1, sizeof(DATA));
		dataval[0] = ConvertASCIItoData(OdlGetKwdValue(kw), keys[0]->iformat);
		list_add(startlist, &dataval[0]);
	}

    if ((kw = GetKey(tbl, "END_KEYS")) != NULL || 
        (kw = GetKey(tbl, "STOP_PRIMARY_KEY")) != NULL) {
        int i,j;
        char **array;
        DATA *dataval;
        FIELD **keys = (FIELD **)table->label->keys->ptr;

        i = OdlGetAllKwdValuesArray(kw, &array);

        endlist = new_list();
        dataval = calloc(i, sizeof(DATA));

        for (j = 0; j < i; j++) {
            dataval[j] = ConvertASCIItoData(array[j], keys[j]->iformat);
            list_add(endlist, &dataval[j]);
        }
    } else if ((kw = GetKey(tbl, "STOP_KEY")) != NULL) {
        DATA *dataval;
        FIELD **keys = (FIELD **)table->label->keys->ptr;

        endlist = new_list();
        dataval = calloc(1, sizeof(DATA));
		dataval[0] = ConvertASCIItoData(OdlGetKwdValue(kw), keys[0]->iformat);
		list_add(endlist, &dataval[0]);
	}

    f = calloc(1, sizeof(FRAGMENT));

    /**
     ** This is the all important ^PTR conversion
     **/
    f->offset = (offset - 1) * table->label->reclen;
    f->nrows = rows;
    f->start_keys = startlist;
    f->end_keys = endlist;
    f->sbuf = sbuf;

    if (sbuf.st_size != f->offset + f->nrows * table->label->reclen) {
        fprintf(stderr, "File is an odd size: %s\n", fname);
    }

    return(f);
}

void
FreeFragment(FRAGMENT *f)
{
	list_free(f->start_keys);
	list_free(f->end_keys);
	free(f);
}
