/* This file contains basic routines for reading a database file */ /* Mike.c...reading a database file */ /* Typedefs */ typedef double Mfloat; typedef int Mint; typedef char Mchar; typedef unsigned char MUchar; typedef short Mshort; typedef char MWord[255]; typedef char boolean /* Defines */ #define EOF_BYTE (MUchar)0x1a #define DBASE_FIELD_CHAR 'C' #define DBASE_FIELD_DATE 'D' #define DBASE_FIELD_FLOAT 'F' #define DBASE_FIELD_LOGICAL 'L' #define DBASE_FIELD_NUMBER 'N' #define DBASE_FIELD_NULL '\0' /* Structures */ typedef struct DBaseHdrRec { MUchar Signature; MUchar Year; MUchar Month; MUchar Day; Mint NumRecs; Mshort HdrLen; Mshort RecLen; MUchar Filler1[2]; MUchar TransFlag; MUchar Encrypted; MUchar MultiUser[12]; MUchar ProdMdx; MUchar Filler2[3]; } DBaseHdrRec, *DBaseHdrPtr; /* dBase field record. */ typedef struct DBaseFieldRec { Mchar Name[11]; Mchar Type; Mshort Position; MUchar Width; MUchar NumDecimals; } DBaseFieldRec, *DBaseFieldPtr; /* dBase file record. */ typedef struct DBaseFileRec *DBaseFilePtr; typedef struct DBaseFileRec { FILE *File; MUchar *IOBuff; Mint RecLen; Mint NumRecs; Mint NumFields; Mint HdrLen; DBaseFieldRec *Fields; } DBaseFileRec; /* Header field record definition. */ typedef struct { Mchar Name[11]; Mchar Type; Mint DataAddress; /* Not used for disk files. */ MUchar Width; MUchar NumDecimals; MUchar Filler[14]; } DBaseHdrFieldRec, *DBaseHdrFieldPtr; /* Functions */ /* ----------------------------------------------------------------------------- NAME: giReadDBaseFile PURPOSE: Reads a database file. PRE: POST: RETURN: AUTHOR: DATE: MODIFIED: ----------------------------------------------------------------------------- */ Mint giReadDBaseFile (MWord dbfilename) { DBaseFilePtr dbdata = NULL; DBaseHdrRec dbheader; FILE *dbfp = NULL; dbfp = fopen(dbfilename, "rb"); if (dbfp) { dbdata = (DBaseFilePtr)malloc(sizeof(DBaseFileRec)); if (!dbdata) { printf("Out of memory!"); giiFreeShapefileAttributeData(&dbfp, &dbdata); return MFALSE; } memset(dbdata, 0, sizeof(DBaseFileRec)); dbdata->File = dbfp; if (!giiReadHeader(dbdata, &dbheader)) { printf("Out of memory!"); giiFreeShapefileAttributeData(&dbfp, &dbdata); return MFALSE; } dbdata->RecLen = (Mint)dbheader.RecLen; dbdata->HdrLen = (Mint)dbheader.HdrLen; dbdata->NumRecs = (Mint)dbheader.NumRecs; if (dbdata->NumRecs & (Mint)0xffff0000) { dbdata->NumRecs = giiSwapInt(dbdata->NumRecs); dbdata->RecLen = giiSwapShort(dbdata->RecLen); dbdata->HdrLen = giiSwapShort(dbdata->HdrLen); } dbdata->NumFields = (Mint)((dbdata->HdrLen - sizeof(DBaseHdrRec)) / sizeof(DBaseHdrFieldRec)); dbdata->Fields = giiLoadFields(dbdata); if ((dbdata->IOBuff = (MUchar *) malloc(dbdata->RecLen * sizeof(MUchar))) == NULL) { printf("Out of memory!"); giiFreeShapefileAttributeData(&dbfp, &dbdata); xgCloseDialog(dlog[0]); return MFALSE; } giiDoWhatYouWantWithTheDBaseFile(dbdata); giiFreeShapefileAttributeData(&dbfp, &dbdata); } else printf("Cannot find database file"); } /* giReadDBaseFile */ /* ----------------------------------------------------------------------------- NAME: giiRemoveLeadBlanks PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static void giiRemoveLeadBlanks (Mchar *s) { Mchar *in, *out; /* Does the string contain leading blanks? If not, exit now. */ if (*s != ' ') return; in = out = s; while (*in && *in == ' ') in++; while (*in) *out++ = *in++; *out = '\0'; return; } /* giiRemoveLeadBlanks */ /* ----------------------------------------------------------------------------- NAME: giiDecode PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static boolean giiDecode (DBaseFilePtr db, DBaseFieldPtr fld, Mchar *s, Mfloat *dval) { MUshort pos = (MUshort)fld->Position; MUshort width = (MUshort)fld->Width; switch(fld->Type) { case DBASE_FIELD_FLOAT: case DBASE_FIELD_CHAR: case DBASE_FIELD_NUMBER: case DBASE_FIELD_DATE: case DBASE_FIELD_LOGICAL: default: memcpy(s, &(db->IOBuff[pos]), width); s[width] = '\0'; giiRemoveLeadBlanks(s); *dval = atof(s); break; } return MTRUE; } /* giiDecode */ /* ----------------------------------------------------------------------------- NAME: giiGetFieldDef PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static DBaseFieldPtr giiGetFieldDef (DBaseFilePtr db, MWord name) { Mint i; DBaseFieldPtr fld; for(i=0; iNumFields; i++) { fld = &(db->Fields[i]); #ifdef PC if(!(Mint)stricmp(name, fld->Name)) #else /* For Unix */ if(!(Mint)strcasecmp(name, fld->Name)) #endif return(fld); } return((DBaseFieldPtr)NULL); } /* giiGetFieldDef */ /* ----------------------------------------------------------------------------- NAME: giiReadRec PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static boolean giiReadRec (DBaseFilePtr db, Mint rec) { Mint pos = db->HdrLen + (db->RecLen * rec); fseek(db->File, pos, SEEK_SET); if (fread(db->IOBuff, db->RecLen, 1, db->File) != 1) { printf("Error reading record."); return MFALSE; } return MTRUE; } /* giiReadRec */ /* ----------------------------------------------------------------------------- NAME: giiDoWhatYouWantWithTheDBaseFile PURPOSE: Lets you do what you want with a database file PRE: POST: RETURN: AUTHOR: DATE: MODIFIED: ----------------------------------------------------------------------------- */ static boolean giiDoWhatYouWantWithTheDBaseFile (DBaseFilePtr db) { DBaseFieldPtr fld; boolean error = MFALSE; Mint i; MWord buf, message; Mfloat dval; Mint index; if (!db) return MFALSE; for (i = 0; iNumRecs && !error; i++) { if (!giiReadRec(db, i)) { sprintf(message, "Could not read Record #%d.", i); printf(message); error = MTRUE; } for (index=0;(indexNumFields);index++) { fld = giiGetFieldDef(db, db->Fields[index].Name); if (!fld) { sprintf(message, "Could not find the %s field", db->Fields[index].Name); printf(message); error = MTRUE; } giiDecode(db, fld, buf, &dval); /* Do what you want with buf or dval here. "buf" is set to the field as a string, "dval" is set to this field as a floating point number */ /* For example, you can do: */ switch (fld->Type) { case DBASE_FIELD_FLOAT: case DBASE_FIELD_CHAR: case DBASE_FIELD_NUMBER: case DBASE_FIELD_DATE: case DBASE_FIELD_LOGICAL: default: printf("Record %d, field %d is: %s\n", i, index, buf); printf("Record %d, field %d is: %1.5lf\n", i, index, dval); break; } } } return MTRUE; } /* giiDoWhatYouWantWithTheDBaseFile */ /* ----------------------------------------------------------------------------- NAME: giiFreeShapefileAttributeData PURPOSE: Frees Database File attribute data. PRE: the values passed to this function have been initialized, opened, and/or malloced POST: the values are freed or closed and set to NULL. RETURN: NONE ----------------------------------------------------------------------------- */ static void giiFreeShapefileAttributeData(FILE **dbfp, DBaseFilePtr *dbdata) { if ((*dbfp)) fclose((*dbfp)); (*dbfp) = NULL; if ((*dbdata)) { if ((*dbdata)->Fields) free((*dbdata)->Fields); (*dbdata)->Fields = NULL; if ((*dbdata)->IOBuff) free((*dbdata)->IOBuff); (*dbdata)->IOBuff = NULL; free((*dbdata)); } (*dbdata) = NULL; } /* giiFreeShapefileAttributeData */ /* ----------------------------------------------------------------------------- NAME: giiHeaderFieldToField PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static boolean giiHeaderFieldToField (DBaseHdrFieldPtr hfld, DBaseFieldPtr fld) { strncpy(fld->Name, hfld->Name, (sizeof(fld->Name) - 1)); if (strlen(hfld->Name) > (sizeof(fld->Name) - 2)) fld->Name[(sizeof(fld->Name) - 1)] = '\0'; fld->Position = (Mshort)0; fld->Type = hfld->Type; fld->Width = hfld->Width; fld->NumDecimals = hfld->NumDecimals; return MTRUE; } /* giiHeaderFieldToField */ /* ----------------------------------------------------------------------------- NAME: giiReadHeaderField PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static boolean giiReadHeaderField (DBaseFilePtr db, DBaseFieldPtr fld) { DBaseHdrFieldRec rec; if (fread(&rec, sizeof(DBaseHdrFieldRec), 1, db->File) != 1) printf("Header Field not read correctly"); giiHeaderFieldToField(&rec, fld); switch (fld->Type) { case DBASE_FIELD_CHAR : case DBASE_FIELD_DATE : case DBASE_FIELD_FLOAT : case DBASE_FIELD_LOGICAL : case DBASE_FIELD_NUMBER : case DBASE_FIELD_NULL : break; default : return FALSE; } if ((fld->Type != DBASE_FIELD_NULL) && (fld->NumDecimals > fld->Width)) return FALSE; return MTRUE; } /* giiReadHeaderField */ /* ----------------------------------------------------------------------------- NAME: giiLoadFields PURPOSE: PRE: POST: RETURN: AUTHOR: ----------------------------------------------------------------------------- */ static DBaseFieldPtr giiLoadFields (DBaseFilePtr db) { DBaseFieldPtr flds; Mint i, j, num = db->NumFields; Mint last = 0, pos = 1; if (num == 0) return((DBaseFieldPtr)NULL); flds = (DBaseFieldPtr)malloc((sizeof(DBaseFieldRec) * num)); if (!flds) { printf("Out of memory!"); return((DBaseFieldPtr)NULL); } fseek(db->File, sizeof(DBaseHdrRec), SEEK_SET); for(i=0; iNumFields--; num--; i--; flds = (DBaseFieldPtr)realloc(flds, (sizeof(DBaseFieldRec) * num)); } else { flds[i].Position = (Mshort)(last + pos); pos = flds[i].Position; last = flds[i].Width; } } return(flds); } /* giiLoadFields */ /* ----------------------------------------------------------------------------- NAME: giiReadHeader PURPOSE: Reads in the header of a DBase file. PRE: POST: RETURN: AUTHOR: Glenn Hammond ----------------------------------------------------------------------------- */ static boolean giiReadHeader (DBaseFilePtr db, DBaseHdrPtr hdr) { MWord message; strcpy(message, "Error reading in DBase file. Scalar info not stored! "); strcat(message, "(Check permissions or disk space on current device)"); fseek(db->File, 0, SEEK_SET); if (fread(hdr, sizeof(DBaseHdrRec), 1, db->File) != 1) { printf(message); return FALSE; } /* Check if this is a valid dBase header. */ if (hdr->Signature != 0x03 || hdr->Year <= 0 || hdr->Month <= 0 || hdr->Month > 12 || hdr->Day <= 0 || hdr->Day > 31 || ((!(hdr->NumRecs & (Mint)0xffff0000)) && ((hdr->NumRecs < 0) || (hdr->HdrLen < sizeof(DBaseHdrRec)) || (hdr->RecLen < 0)))) return FALSE; return MTRUE; } /* giiReadHeader */ /* ----------------------------------------------------------------------------- NAME: giiSwapShort PURPOSE: swap the bit order of an short (2 bytes) PRE: POST: RETURN: ----------------------------------------------------------------------------- */ #ifdef DECA2 /* If you're compiling on a DEC Alpha Workstation */ short giiSwapShort(const short int_arg) #else short giiSwapShort(short int_arg) #endif { union int_bytes { short s; unsigned char b[2]; }; union int_bytes int_rev, int_fwd; int_rev.s = int_arg; int_fwd.b[0] = int_rev.b[1]; int_fwd.b[1] = int_rev.b[0]; return int_fwd.s; } /* giiSwapShort */ /* ----------------------------------------------------------------------------- NAME: giiSwapInt PURPOSE: swap the bit order of an int (4 bytes) PRE: POST: RETURN: ----------------------------------------------------------------------------- */ #ifdef DECA2 /* If you're compiling on a DEC Alpha Workstation */ Mint giiSwapInt(const int int_arg) #else Mint giiSwapInt(int int_arg) #endif { union int_bytes { int s; unsigned char b[4]; }; union int_bytes int_rev, int_fwd; int_rev.s = int_arg; int_fwd.b[0] = int_rev.b[3]; int_fwd.b[1] = int_rev.b[2]; int_fwd.b[2] = int_rev.b[1]; int_fwd.b[3] = int_rev.b[0]; return int_fwd.s; } /* giiSwapInt */