/* * This file was written by Bill Cox. It is hereby placed into the public domain. */ /*-------------------------------------------------------------------------------------------------- This module provides basic database administration capabilities. --------------------------------------------------------------------------------------------------*/ #include #include #include "ddutil.h" #include "utpersist.h" static char *utLineBuffer; static uint32 utLineSize, utLinePosition; static uint64 utLineNum; static bool utAtEndOfFile; static char *utLastToken; static bool utShowHidden; static FILE *utInputFile, *utOutputFile; static utField *utFieldTranslationTable; static uint32 utNumFields; /*-------------------------------------------------------------------------------------------------- Read a module header. --------------------------------------------------------------------------------------------------*/ static void reportError( char *format, ...) { char *buff; va_list ap; va_start(ap, format); buff = utVsprintf(format, ap); va_end(ap); if(utInputFile != stdin) { utWarning("Line %lld, token \"%s\": %s", utLineNum, utLastToken, buff); } else { utWarning("Token \"%s\": %s", utLastToken, buff); } } /*-------------------------------------------------------------------------------------------------- Return an integer of the given size. --------------------------------------------------------------------------------------------------*/ uint64 utFindIntValue( void *values, uint8 size) { switch(size) { case 1: return *(uint8 *)values; case 2: return *(uint16 *)values; case 4: return *(uint32 *)values; case 8: return *(uint64 *)values; } utExit("Illegal integer size"); return 0; } /*-------------------------------------------------------------------------------------------------- Print out help. --------------------------------------------------------------------------------------------------*/ static void printHelp(void) { fprintf(utOutputFile, "create - allocate a new object, and return it's object number\n" "compact - Compact the database, and delete the recent_changes file\n" "destroy - Destroy an object\n" "help - this command\n" "list - list the modules in the database, their object counts and memory usage\n" "list - list classes in the module, their object counts and memory usage\n" "list - list fields of a class\n" "quit - quit the database manager\n" "set = comma separated values – \n" " set all fields of an object\n" "set = - set a value\n" "show - show all field values of all objects of the class\n" "show - show an object's fields\n" "show_hidden - Enable/disable listing of DataDraw internal fields\n" "load_binary - Read the data from the binary database file into the database\n" "save_binary - Write out the database in binary format to the file\n" "load_text - Read the data from the text database file into the database\n" "save_text - Write out the database in text format to the file\n"); } /*-------------------------------------------------------------------------------------------------- Return the number of objects allocated for the module. --------------------------------------------------------------------------------------------------*/ static uint64 countModuleObjects( utModule module) { utClass theClass; uint64 numObjects = 0; utForeachModuleClass(module, theClass) { numObjects += utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); } utEndModuleClass; return numObjects; } /*-------------------------------------------------------------------------------------------------- Return the total memory size used by the class. --------------------------------------------------------------------------------------------------*/ static uint64 findClassMemory( utClass theClass) { utField field; uint64 memory = 0; uint64 numUsed; utForeachClassField(theClass, field) { if(utFieldArray(field)) { numUsed = *utFieldGetNumUsedPtr(field); } else { numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); } memory += utFieldGetSize(field)*numUsed; } utEndClassField; return memory; } /*-------------------------------------------------------------------------------------------------- List the modules in the database. --------------------------------------------------------------------------------------------------*/ static uint64 findModuleMemory( utModule module) { utClass theClass; uint64 memory = 0; utForeachModuleClass(module, theClass) { memory += findClassMemory(theClass); } utEndModuleClass; return memory; } /*-------------------------------------------------------------------------------------------------- Find the ammount in memory units. --------------------------------------------------------------------------------------------------*/ char *utMemoryUnits( uint64 memory) { #if (_MSC_VER >= 1200) && (_MSC_VER < 1300) //for Visual Studio 6 use a signed __int64 signed __int64 tempMem = memory; if(memory > 1024*1024) { return utSprintf("%.5g MB", tempMem/(1024.0*1024.0)); } else if(memory > 1024) { return utSprintf("%.5g KB", tempMem/1024.0); } return utSprintf("%lu bytes", tempMem); #else if(memory > 1024*1024) { return utSprintf("%.5g MB", memory/(1024.0*1024.0)); } else if(memory > 1024) { return utSprintf("%.5g KB", memory/1024.0); } return utSprintf("%lu bytes", memory); #endif } /*-------------------------------------------------------------------------------------------------- List the modules in the database. --------------------------------------------------------------------------------------------------*/ static void listModules(void) { utModule module; fprintf(utOutputFile, "modules:\n"); utForeachModule(module) { if(utModuleInitialized(module)) { fprintf(utOutputFile, " %s - %llu objects, %s memory\n", utModuleGetPrefix(module), countModuleObjects(module), utMemoryUnits(findModuleMemory(module))); } } utEndModule; } /*-------------------------------------------------------------------------------------------------- List the classes in the module. --------------------------------------------------------------------------------------------------*/ static void listClasses( utModule module) { utClass theClass; uint64 numUsed; fprintf(utOutputFile, "Module %s classes:\n", utModuleGetPrefix(module)); utForeachModuleClass(module, theClass) { numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); fprintf(utOutputFile, " %s - %llu objects, %s memory\n", utClassGetName(theClass), numUsed, utMemoryUnits(findClassMemory(theClass))); } utEndModuleClass; } /*-------------------------------------------------------------------------------------------------- Just return a name for the field type. --------------------------------------------------------------------------------------------------*/ static char *findBaseFieldTypeName( utField field) { switch(utFieldGetType(field)) { case UT_INT: return utSprintf("int%u", utFieldGetSize(field) << 3); case UT_UINT: return utSprintf("uint%u", utFieldGetSize(field) << 3); case UT_FLOAT: return "float"; case UT_DOUBLE: return "double"; case UT_BIT: return "bit"; case UT_BOOL: return "bool"; case UT_CHAR: return "char"; case UT_SYM: return "sym"; case UT_ENUM: return "enum"; case UT_TYPEDEF: return "typedef"; case UT_POINTER: return utSprintf("pointer %s", utFieldGetDestName(field)); case UT_UNION: return utSprintf("union %s", utFieldGetDestName(field)); default: utExit("Unknown property type"); } return NULL; /* Dummy return */ } /*-------------------------------------------------------------------------------------------------- Just return a name for the field type. --------------------------------------------------------------------------------------------------*/ static char *findFieldTypeName( utField field) { char *baseName = findBaseFieldTypeName(field); if(utFieldArray(field)) { return utSprintf("array %s", baseName); } return baseName; } /*-------------------------------------------------------------------------------------------------- List the fields of the class. --------------------------------------------------------------------------------------------------*/ static void listFields( utClass theClass) { utField field; fprintf(utOutputFile, "Class %s fields:\n", utClassGetName(theClass)); utForeachClassField(theClass, field) { if(!utFieldHidden(field) || utShowHidden) { fprintf(utOutputFile, " %s %s\n", findFieldTypeName(field), utFieldGetName(field)); } } utEndClassField; } /*-------------------------------------------------------------------------------------------------- Find the enum with the given name. --------------------------------------------------------------------------------------------------*/ static utEnum findEnum( utModule module, char *name) { utEnum theEnum; utForeachModuleEnum(module, theEnum) { if(!strcmp(utEnumGetName(theEnum), name)) { return theEnum; } } utEndModuleEnum; return NULL; } /*-------------------------------------------------------------------------------------------------- Find the entry with the given value. --------------------------------------------------------------------------------------------------*/ static utEntry findEntryFromValue( utEnum theEnum, uint32 value) { utEntry entry; utForeachEnumEntry(theEnum, entry) { if(utEntryGetValue(entry) == value) { return entry; } } utEndEnumEntry; return NULL; } /*-------------------------------------------------------------------------------------------------- Find the entry with the given name. --------------------------------------------------------------------------------------------------*/ static utEntry findEntryFromName( utEnum theEnum, char *name) { utEntry entry; utForeachEnumEntry(theEnum, entry) { if(!strcmp(utEntryGetName(entry), name)) { return entry; } } utEndEnumEntry; return NULL; } /*-------------------------------------------------------------------------------------------------- Return a hexidecimal digit representation of the 4-bit value. --------------------------------------------------------------------------------------------------*/ static char findHexDigit( uint8 value) { if(value <= 9) { return '0' + value; } return 'A' + value - 10; } /*-------------------------------------------------------------------------------------------------- Return a long hexidecimal string representing the binary value. --------------------------------------------------------------------------------------------------*/ char *utFindHexString( uint8 *values, uint32 size) { char *buffer = utMakeString((size << 1) + 1); char *p = buffer + (size << 1) - 1; buffer[size*2] = '\0'; while(size-- != 0) { *p-- = findHexDigit((uint8)(*values & 0xf)); *p-- = findHexDigit((uint8)((*values++) >> 4)); } return buffer; } /*-------------------------------------------------------------------------------------------------- Count the non "isprint" characters in the string. --------------------------------------------------------------------------------------------------*/ static uint32 countNonprintableChars( char *string) { uint32 nonPrintableChars = 0; while(*string) { if(!isprint(*string)) { nonPrintableChars++; } string++; } return nonPrintableChars; } /*-------------------------------------------------------------------------------------------------- Convert any non-string friendly characters to \" or \ format. --------------------------------------------------------------------------------------------------*/ static char *mungeString( char *string) { char *buffer = utMakeString(strlen(string) + 4*countNonprintableChars(string) + 1); char *p = buffer; char intValue[4]; while(*string != '\0') { if(isprint(*string)) { if(*string == '"' || *string == '\\') { *p++ = '\\'; } *p++ = *string++; } else { sprintf(intValue, "\\%u", *string); intValue[3] = '\0'; strcpy(p, intValue); p += strlen(intValue); string++; } } *p = '\0'; return buffer; } /*-------------------------------------------------------------------------------------------------- Find the union case based on the switch field. --------------------------------------------------------------------------------------------------*/ static utUnioncase findUnioncase( utUnion theUnion, uint64 objectNumber) { utField switchField = utUnionGetSwitchField(theUnion); utUnioncase unioncase; uint32 value = *(*(uint32 **)(utFieldGetArrayPtr(switchField)) + objectNumber); utForeachUnionUnioncase(theUnion, unioncase) { if(utUnioncaseGetValue(unioncase) == value) { return unioncase; } } utEndUnionUnioncase; return NULL; } /*-------------------------------------------------------------------------------------------------- Return a string representation of the field value. --------------------------------------------------------------------------------------------------*/ static char *findFieldValue( utField field, uint64 objectNumber) { utModule module; utClass theClass; utEnum theEnum; utEntry entry; utUnion theUnion; utUnioncase unioncase; utSym sym; uint8 *values; uint32 size = utFieldGetSize(field); utFieldType type = utFieldGetType(field); if(type == UT_BIT) { values = *(uint8 **)(utFieldGetArrayPtr(field)) + (objectNumber >> 3)*utFieldGetSize(field); } else { values = *(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field); } if(type == UT_UNION) { theUnion = utFieldGetUnion(field); unioncase = findUnioncase(theUnion, objectNumber); if(unioncase != NULL) { type = utUnioncaseGetType(unioncase); size = utUnioncaseGetSize(unioncase); } } switch(type) { case UT_BIT: if((*values >> (objectNumber & 7)) & 1) { return "true"; } return "false"; case UT_BOOL: if(*values == 1) { return "true"; } else if(*values == 0) { return "false"; } return utSprintf("%d", *values); case UT_INT: switch(size) { case 1: return utSprintf("%d", *(int8 *)values); case 2: return utSprintf("%d", *(int16 *)values); case 4: return utSprintf("%d", *(int32 *)values); case 8: return utSprintf("%d", *(int64 *)values); } utExit("Invalid integer size"); case UT_UINT: switch(size) { case 1: return utSprintf("%u", *(uint8 *)values); case 2: return utSprintf("%u", *(uint16 *)values); case 4: return utSprintf("%u", *(uint32 *)values); case 8: return utSprintf("%u", *(uint64 *)values); } utExit("Invalid integer size"); case UT_CHAR: if(isprint(*values)) { return utSprintf("'%c'", *values); } return utSprintf("'\\%u'", *values); case UT_FLOAT: return utSprintf("%g", *(float *)values); case UT_DOUBLE: return utSprintf("%g", *(double *)values); case UT_POINTER: switch(size) { case 1: return utSprintf("0x%x", *(uint8 *)values); case 2: return utSprintf("0x%x", *(uint16 *)values); case 4: return utSprintf("0x%x", *(uint32 *)values); case 8: return utSprintf("0x%x", *(uint64 *)values); } case UT_TYPEDEF: case UT_UNION: return utFindHexString(values, size); case UT_ENUM: theClass = utFieldGetClass(field); module = utClassGetModule(theClass); theEnum = findEnum(module, utFieldGetDestName(field)); entry = findEntryFromValue(theEnum, *(uint32 *)values); return utEntryGetName(entry); case UT_SYM: sym = *(utSym *)values; if(sym == utSymNull) { return utSprintf("0x%x", utSymNull); } return utSprintf("\"%s\"", mungeString(utSymGetName(*(utSym *)values))); } utExit("Unknow field type"); return NULL; } /*-------------------------------------------------------------------------------------------------- Determine if the only '\0' is at the end of the character array. --------------------------------------------------------------------------------------------------*/ static bool zeroTerminated( char *values, uint32 length) { if(values[--length] != '\0') { return false; } while(length != 0 && values[length] != '\0') { length--; } return values[length] == '\0'; } /*-------------------------------------------------------------------------------------------------- Show the data in the array. If we go over maxFields, we print '...'. --------------------------------------------------------------------------------------------------*/ static void writeArray( utField field, uint64 objectNumber, uint32 maxFields) { uint32 numElements; uint32 xElement; bool firstTime = true; char *values = utFieldGetGetValues(field)(objectNumber, &numElements); uint32 firstElement = (values - *(char **)(utFieldGetArrayPtr(field)))/utFieldGetSize(field); if(numElements != 0 && utFieldGetType(field) == UT_CHAR && zeroTerminated(values, numElements)) { fprintf(utOutputFile, "\"%s\"", mungeString(values)); return; } fprintf(utOutputFile, "("); for(xElement = 0; xElement < numElements && xElement < maxFields; xElement++) { if(!firstTime) { fprintf(utOutputFile, ", "); } firstTime = false; fprintf(utOutputFile, "%s", findFieldValue(field, firstElement + xElement)); } if(xElement == maxFields) { fprintf(utOutputFile, ", ..."); } fprintf(utOutputFile, ")"); } /*-------------------------------------------------------------------------------------------------- Show the fields of an object. --------------------------------------------------------------------------------------------------*/ static void showObject( utClass theClass, uint64 objectNumber) { utField field; bool firstTime = true; utForeachClassField(theClass, field) { if(!utFieldHidden(field) || utShowHidden) { if(!firstTime) { fprintf(utOutputFile, ", "); } firstTime = false; fprintf(utOutputFile, "%s=", utFieldGetName(field)); if(!utFieldArray(field)) { fprintf(utOutputFile, "%s", findFieldValue(field, objectNumber)); } else { writeArray(field, objectNumber, 16); } } } utEndClassField; fprintf(utOutputFile, "\n"); } /*-------------------------------------------------------------------------------------------------- Just print the field names. --------------------------------------------------------------------------------------------------*/ static void printColumnHeaders( utClass theClass) { utField field; bool firstTime = true; fprintf(utOutputFile, "ObjectNumber, "); utForeachClassField(theClass, field) { if(!utFieldHidden(field)) { if(!firstTime) { fprintf(utOutputFile, ", "); } firstTime = false; fprintf(utOutputFile, "%s", utFieldGetName(field)); } } utEndClassField; fprintf(utOutputFile, "\n"); } /*-------------------------------------------------------------------------------------------------- Just print the field types. --------------------------------------------------------------------------------------------------*/ static void printColumnTypes( utClass theClass) { utField field; bool firstTime = true; fprintf(utOutputFile, "uint%u, ", utClassGetReferenceSize(theClass) << 3); utForeachClassField(theClass, field) { if(!utFieldHidden(field)) { if(!firstTime) { fprintf(utOutputFile, ", "); } firstTime = false; fprintf(utOutputFile, "%s", findFieldTypeName(field)); } } utEndClassField; fprintf(utOutputFile, "\n"); } /*-------------------------------------------------------------------------------------------------- Print the object's values in a comma separated list. --------------------------------------------------------------------------------------------------*/ static void showObjectFields( utClass theClass, uint64 objectNumber) { utField field; bool firstTime = true; fprintf(utOutputFile, "0x%llx, ", objectNumber); utForeachClassField(theClass, field) { if(!utFieldHidden(field) || utShowHidden) { if(!firstTime) { fprintf(utOutputFile, ", "); } firstTime = false; if(!utFieldArray(field)) { fprintf(utOutputFile, "%s", findFieldValue(field, objectNumber)); } else { writeArray(field, objectNumber, UINT32_MAX); } } } utEndClassField; fprintf(utOutputFile, "\n"); } /*-------------------------------------------------------------------------------------------------- Return the null reference for references of the given size. --------------------------------------------------------------------------------------------------*/ static uint64 findNullReference( uint8 size) { switch(size) { case 1: return UINT8_MAX; case 2: return UINT16_MAX; case 4: return UINT32_MAX; case 8: return UINT64_MAX; default: utExit("Invalid reference size"); } return 0; } /*-------------------------------------------------------------------------------------------------- Set the used flags for the objects of the class. --------------------------------------------------------------------------------------------------*/ static void setObjectFreeFlags( utModule module, utClass theClass, uint8 *objectFree) { utField field = utClassGetNextFreeField(theClass); uint64 objectNumber = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass)); while(objectNumber != findNullReference(utClassGetReferenceSize(theClass))) { objectFree[objectNumber >> 3] |= 1 << (objectNumber & 7); objectNumber = utFindIntValue(*(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field), (uint8)utFieldGetSize(field)); } } /*-------------------------------------------------------------------------------------------------- Show all the objects of a class. --------------------------------------------------------------------------------------------------*/ static void showClass( utModule module, utClass theClass) { uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); uint64 objectNumber; bool hasFreeList = utClassGetDestructor(theClass) != NULL; uint64 spaceNeeded = (numUsed + 7) >> 3; uint8 *objectFree = NULL; if(hasFreeList) { objectFree = calloc(spaceNeeded, sizeof(uint8)); setObjectFreeFlags(module, theClass, objectFree); } fprintf(utOutputFile, "class %s %llu\n", utClassGetName(theClass), numUsed); printColumnHeaders(theClass); printColumnTypes(theClass); for(objectNumber = 0; objectNumber < numUsed; objectNumber++) { if(!hasFreeList || !(objectFree[objectNumber >> 3] & (1 << (objectNumber & 7)))) { showObjectFields(theClass, objectNumber); } } if(hasFreeList) { free(objectFree); } } /*-------------------------------------------------------------------------------------------------- Write the integer, given the width. --------------------------------------------------------------------------------------------------*/ void utSetInteger( uint8 *dest, uint64 value, uint8 width) { switch(width) { case 1: *dest = (uint8)value; break; case 2: *(uint16 *)dest = (uint16)value; break; case 4: *(uint32 *)dest = (uint32)value; break; case 8: *(uint64 *)dest = (uint64)value; break; default: utExit("Invalid integer width"); } } /*-------------------------------------------------------------------------------------------------- Parse the integer and return it's value. Allow hex if we see "0x" in front. Allow negation. Set passed to indicate success. --------------------------------------------------------------------------------------------------*/ static uint64 parseInteger( char *string, uint8 width, bool *passed) { uint64 value = 0, oldValue = 0; char c; uint8 digit; bool negate = false; if(*string == '0' && *(string + 1) == 'x') { string += 2; while(*string) { c = toupper(*string++); if(c >= '0' && c <= '9') { digit = c - '0'; } else if(c >= 'A' && c <= 'F') { digit = c - 'A' + 10; } else { *passed = false; return 0; } value <<= 4; value |= digit; if(value < oldValue) { *passed = false; return 0; } oldValue = value; } } else { if(*string == '-') { string++; negate = true; } while(*string) { c = *string++; if(c < '0' || c > '9') { *passed = false; return 0; } value *= 10; value += c - '0'; if(value < oldValue) { *passed = false; return 0; } oldValue = value; } } *passed = true; return value; } /*-------------------------------------------------------------------------------------------------- Read an integer and complain if it has bad width or syntax. --------------------------------------------------------------------------------------------------*/ static bool readInteger( uint8 *dest, char *string, uint8 width, bool unsignedInt) { uint64 value; bool passed; value = parseInteger(string, width, &passed); if(!passed) { return false; } utSetInteger(dest, value, width); return true; } /*-------------------------------------------------------------------------------------------------- Parse a character value. --------------------------------------------------------------------------------------------------*/ static bool readChar( uint8 *dest, char *value) { char *end = value + strlen(value) - 1; if(*value++ != '\'') { return false; } if(*--end != '\'') { return false; } *end = '\0'; if(*value == '\\') { return readInteger(dest, value, 1, true); } *(char *)dest = *value; return true; } /*-------------------------------------------------------------------------------------------------- Parse a floating point number. --------------------------------------------------------------------------------------------------*/ static bool readFloat( uint8 *dest, char *value, bool isFloat) { double floatVal; char *endPtr; floatVal = strtod(value, &endPtr); if(endPtr == NULL || *endPtr != '\0') { return false; } if(isFloat) { *(float *)dest = (float)floatVal; } else { *(double *)dest = floatVal; } return true; } /*-------------------------------------------------------------------------------------------------- Read a 2-character hex byte. --------------------------------------------------------------------------------------------------*/ static bool readHex( uint8 *dest, char *value) { char c = toupper(*value++); uint8 byte; if(c >= '0' && c <= '9') { byte = c - '0'; } else if(c >= 'A' && c <= 'F') { byte = c - 'A' + 10; } else { return false; } byte <<= 4; c = toupper(*value); if(c >= '0' && c <= '9') { byte |= c - '0'; } else if(c >= 'A' && c <= 'F') { byte |= c - 'A' + 10; } else { return false; } *dest = byte; return true; } /*-------------------------------------------------------------------------------------------------- Parse a typedef value. --------------------------------------------------------------------------------------------------*/ static bool readTypedef( uint8 *dest, char *value, uint32 size) { uint32 length = 0; while(*value != '\0' && length <= size) { if(!readHex(dest, value)) { return false; } dest++; value += 2; length++; } return length == size; } /*-------------------------------------------------------------------------------------------------- Convert any \ escapes into characters. --------------------------------------------------------------------------------------------------*/ static char *unmungeString( char *string, char **end) { char *buffer = utMakeString(strlen(string) + 1); char *p = buffer; char value; *end = NULL; if(*string++ != '"') { return NULL; } while(*string != '\0' && *string != '"') { if(*string == '\\') { string++; if(*string >= '0' && *string <= '9') { value = 0; do { value = 10*value + *string++ - '0'; } while(*string >= '0' && *string <= '9'); *p++ = value; } else { *p++ = *string++; } } else { if(!isprint(*string)) { return NULL; } *p++ = *string++; } } *p = '\0'; if(*string == '\0') { return NULL; } *end = string + 1; return buffer; } /*-------------------------------------------------------------------------------------------------- Parse a symbol. Valid values are a string, or 0xffffffff --------------------------------------------------------------------------------------------------*/ static bool readSym( uint8 *dest, char *value) { utSym *symPtr = (utSym *)dest; char *end; char *string; if(*value == '"') { string = unmungeString(value, &end); if(end == NULL) { return false; } *symPtr = utSymCreate(string); } else { readInteger(dest, value, sizeof(utSym), true); if(*symPtr != utSymNull) { return false; } } return true; } /*-------------------------------------------------------------------------------------------------- Parse an enumerated type, and write it to the destination. --------------------------------------------------------------------------------------------------*/ static bool readEnum( utEnum theEnum, uint8 *dest, char *value) { utEntry entry; entry = findEntryFromName(theEnum, value); if(entry == NULL) { reportError("Unknown enumerated type value %s", value); entry = utEnumGetFirstEntry(theEnum); } *(uint32 *)dest = utEntryGetValue(entry); return true; } /*-------------------------------------------------------------------------------------------------- Parse a value of a given type, and write it to the destination. --------------------------------------------------------------------------------------------------*/ static bool parseSpecificValue( uint8 *dest, char *value, utFieldType type, uint32 size, uint64 objectNumber) { switch(type) { case UT_BIT: if(!strcasecmp(value, "true")) { *(uint8 *)dest |= 1 << (objectNumber & 7); } else if(!strcasecmp(value, "false")) { *(uint8 *)dest &= ~(1 << (objectNumber & 7)); } else { return false; } return true; case UT_BOOL: if(!strcasecmp(value, "true")) { *(bool *)dest = true; } else if(!strcasecmp(value, "false")) { *(bool *)dest = false; } else { return readInteger(dest, value, 1, true); } return true; case UT_INT: return readInteger(dest, value, (uint8)size, false); case UT_UINT: return readInteger(dest, value, (uint8)size, true); case UT_CHAR: return readChar(dest, value); case UT_FLOAT: return readFloat(dest, value, true); case UT_DOUBLE: return readFloat(dest, value, true); case UT_POINTER: return readInteger(dest, value, (uint8)size, true); case UT_TYPEDEF: return readTypedef(dest, value, size); case UT_SYM: return readSym(dest, value); default: utExit("Invalid type"); } return false; /* Dummy return */ } /*-------------------------------------------------------------------------------------------------- Parse a value of a given type, and write it to the destination. --------------------------------------------------------------------------------------------------*/ static bool parseValue( uint8 *dest, char *value, utField field, uint64 objectNumber) { utClass theClass = utFieldGetClass(field); utModule module = utClassGetModule(theClass); utUnion theUnion; utUnioncase unioncase; utEnum theEnum; if(utFieldGetType(field) == UT_ENUM) { theEnum = findEnum(module, utFieldGetDestName(field)); return readEnum(theEnum, dest, value); } if(utFieldGetType(field) != UT_UNION) { return parseSpecificValue(dest, value, utFieldGetType(field), utFieldGetSize(field), objectNumber); } theUnion = utFieldGetUnion(field); unioncase = findUnioncase(theUnion, objectNumber); if(unioncase == NULL) { return readTypedef(dest, value, utFieldGetSize(field)); } if(utUnioncaseGetType(unioncase) == UT_ENUM) { theEnum = findEnum(module, utFieldGetDestName(field)); return readEnum(theEnum, dest, value); } return parseSpecificValue(dest, value, utUnioncaseGetType(unioncase), utUnioncaseGetSize(unioncase), objectNumber); } /*-------------------------------------------------------------------------------------------------- Skip to the next comma or the end of a value list. If we find a syntax error of some sort, return NULL. --------------------------------------------------------------------------------------------------*/ static char *skipToNextComma( char *valueList) { char *end, *prevChar; if(*valueList == ',') { valueList++; } while(isspace(*valueList)) { valueList++; } if(*valueList == '"') { unmungeString(valueList, &end); if(end == NULL) { return NULL; } valueList = end; } else if(*valueList == '(') { valueList++; do { valueList = skipToNextComma(valueList); prevChar = valueList - 1; while(isspace(*prevChar)) { prevChar--; } } while(valueList != NULL && *prevChar != ')'); if(valueList == NULL) { return NULL; } } while(*valueList != '\0' && *valueList != ',') { valueList++; } if(*valueList != ',' && *valueList != '\0') { return NULL; } return valueList; } /*-------------------------------------------------------------------------------------------------- Count how many values are in the list. --------------------------------------------------------------------------------------------------*/ static uint32 countListValues( char *valueList) { uint32 numValues = 0; while(isspace(*valueList)) { valueList++; } while(valueList != NULL && *valueList != '\0') { valueList = skipToNextComma(valueList); numValues++; } if(valueList == NULL || *valueList != '\0') { return 0; /* Invalid list */ } return numValues; } /*-------------------------------------------------------------------------------------------------- Return the next element in a list of array values. Update the list pointer to the next element. --------------------------------------------------------------------------------------------------*/ static char *parseArrayValue( char **valueList) { char *end, *buffer, *prevChar; uint32 length; bool isArray; while(isspace(**valueList)) { (*valueList)++; } isArray = **valueList == '('; end = skipToNextComma(*valueList); if(end == NULL) { return NULL; } prevChar = end; if(isArray) { (*valueList)++; prevChar = end - 1; while(isspace(*prevChar)) { prevChar--; } } length = prevChar - *valueList; buffer = utMakeString(length + 1); strncpy(buffer, *valueList, length); buffer[length] = '\0'; if(*end == ',') { end++; } *valueList = end; utLastToken = buffer; return buffer; } /*-------------------------------------------------------------------------------------------------- Parse an array of values. --------------------------------------------------------------------------------------------------*/ static bool parseArray( utField field, uint64 objectNumber, char *valueList) { uint32 numValues; uint8 *dest; char *value, *string, *end; if(utFieldGetType(field) == UT_CHAR && *valueList == '"') { string = unmungeString(valueList, &end); if(end == NULL || *end != '\0') { return false; } numValues = strlen(string) + 1; dest = utFieldGetAllocValues(field)(objectNumber, numValues); strcpy((char *)dest, string); return true; } numValues = countListValues(valueList); dest = utFieldGetAllocValues(field)(objectNumber, numValues); while(numValues-- != 0) { value = parseArrayValue(&valueList); if(!parseValue(dest, value, field, objectNumber)) { return false; } dest += utFieldGetSize(field); } if(*valueList != '\0') { return false; } return true; } /*-------------------------------------------------------------------------------------------------- Set a field of an object. --------------------------------------------------------------------------------------------------*/ static bool setObjectField( utClass theClass, utField field, uint64 objectNumber, char *value) { uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); uint8 *dest; bool passed; if(objectNumber >= numUsed) { reportError("Invalid object ID"); return false; } if(!utFieldArray(field)) { if(utFieldGetType(field) == UT_BIT) { dest = *(uint8 **)(utFieldGetArrayPtr(field)) + (objectNumber >> 3)*utFieldGetSize(field); } else { dest = *(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field); } passed = parseValue(dest, value, field, objectNumber); } else { passed = parseArray(field, objectNumber, value); } if(!passed) { reportError("Invalid value for field %s", utFieldGetName(field)); } return passed; } /*-------------------------------------------------------------------------------------------------- Set all the fields of an object. --------------------------------------------------------------------------------------------------*/ static bool setObjectFields( utClass theClass, uint64 objectNumber, char *valueList, utField *fieldTranslationTable) { utField field; uint16 numValues = countListValues(valueList); uint16 xField; char *value; if(numValues != utClassGetNumFields(theClass) - utClassGetNumHiddenFields(theClass)) { reportError("Incorrect number of values for class %s", utClassGetName(theClass)); return false; } for(xField = 0; xField < numValues; xField++) { if(fieldTranslationTable == NULL) { field = utClassGetiField(theClass, xField); } else { field = fieldTranslationTable[xField]; } value = parseArrayValue(&valueList); if(field != NULL && !setObjectField(theClass, field, objectNumber, value)) { return false; } } return true; } /*-------------------------------------------------------------------------------------------------- Read a line of text from stdin. --------------------------------------------------------------------------------------------------*/ static bool readLine(void) { uint32 linePosition = 0; int c; utDo { c = getc(utInputFile); } utWhile(c != '\n' && c != EOF) { if(linePosition == utLineSize) { utLineSize <<= 1; utResizeArray(utLineBuffer, utLineSize); } if(isprint(c)) { utLineBuffer[linePosition++] = c; } } utRepeat; if(c == EOF) { utAtEndOfFile = true; } if(linePosition == utLineSize) { utLineSize <<= 1; utResizeArray(utLineBuffer, utLineSize); } utLineBuffer[linePosition] = '\0'; utLinePosition = 0; utLineNum++; return c != EOF; } /*-------------------------------------------------------------------------------------------------- Find the length of the token. --------------------------------------------------------------------------------------------------*/ static uint32 findTokenLength(void) { uint32 linePosition = utLinePosition; char c = utLineBuffer[linePosition++]; if(c == '\0') { return 0; } if(!isalnum(c) && c != '_') { return 1; } do { c = utLineBuffer[linePosition++]; } while(isalnum(c) || c == '_'); return linePosition - utLinePosition - 1; } /*-------------------------------------------------------------------------------------------------- Just skip space. --------------------------------------------------------------------------------------------------*/ void skipSpace(void) { while(isspace(utLineBuffer[utLinePosition])) { utLinePosition++; } } /*-------------------------------------------------------------------------------------------------- Read a token from the line buffer. --------------------------------------------------------------------------------------------------*/ char *readToken(void) { char *token; uint32 length; skipSpace(); length = findTokenLength(); token = utMakeString(length + 1); strncpy(token, utLineBuffer + utLinePosition, length); utLinePosition += length; token[length] = '\0'; utLastToken = token; return token; } /*-------------------------------------------------------------------------------------------------- Find the module with the given prefix. --------------------------------------------------------------------------------------------------*/ utModule utFindModule( char *prefix) { utModule module; uint8 xModule; for(xModule = 0; xModule < utUsedModules; xModule++) { module = utModules + xModule; if(!strcmp(utModuleGetPrefix(module), prefix)) { return module; } } return NULL; } /*-------------------------------------------------------------------------------------------------- Find the class with the given name. --------------------------------------------------------------------------------------------------*/ static utClass findClass( utModule module, char *name) { utClass theClass; utForeachModuleClass(module, theClass) { if(!strcmp(utClassGetName(theClass), name)) { return theClass; } } utEndModuleClass; return NULL; } /*-------------------------------------------------------------------------------------------------- Find the field with the given name. --------------------------------------------------------------------------------------------------*/ utField utFindField( utClass theClass, char *name) { utField field; utForeachClassField(theClass, field) { if(!strcmp(utFieldGetName(field), name)) { return field; } } utEndClassField; return NULL; } /*-------------------------------------------------------------------------------------------------- Check that we parsed the whole line, and complain if we didn't. Return true if the end was correct. --------------------------------------------------------------------------------------------------*/ static bool expectEnd(void) { char *token = readToken(); if(*token != '\0') { reportError("Extra characters at end of command.\n"); return false; } return true; } /*-------------------------------------------------------------------------------------------------- Parse a module name. --------------------------------------------------------------------------------------------------*/ static utModule parseModule( bool required, bool *error) { utModule module; char *token = readToken(); *error = false; if(*token == '\0') { if(required) { reportError("Expecting a module name."); *error = true; } return NULL; } module = utFindModule(token); if(module == NULL) { reportError("Invalid module name."); *error = true; } return module; } /*-------------------------------------------------------------------------------------------------- Parse a class name. --------------------------------------------------------------------------------------------------*/ static utClass parseClass( utModule module, bool required, bool *error) { utClass theClass; char *token = readToken(); *error = false; if(*token == '\0') { if(required) { reportError("Expecting a class name."); *error = true; } return NULL; } theClass = findClass(module, token); if(theClass == NULL) { reportError("Invalid class name."); *error = true; } return theClass; } /*-------------------------------------------------------------------------------------------------- Parse an object number. --------------------------------------------------------------------------------------------------*/ static uint64 parseObjectNumber( utClass theClass, bool required, bool *error) { uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); char *token = readToken(); uint64 objectNumber; *error = false; if(*token == '\0') { if(required) { reportError("Expecting an object number"); *error = true; } return UINT64_MAX; } if(!readInteger((uint8 *)&objectNumber, token, 8, true)) { reportError("Invalid number"); *error = true; return UINT64_MAX; } if(objectNumber >= numUsed) { reportError("Object number too large"); *error = true; return UINT64_MAX; } return objectNumber; } /*-------------------------------------------------------------------------------------------------- Process a list command. --------------------------------------------------------------------------------------------------*/ static void processListCommand(void) { bool error; utModule module = parseModule(false, &error); utClass theClass; if(error) { return; } if(module == NULL) { listModules(); return; } theClass = parseClass(module, false, &error); if(error) { return; } if(theClass == NULL) { listClasses(module); return; } if(!expectEnd()) { return; } listFields(theClass); } /*-------------------------------------------------------------------------------------------------- Process a show command. --------------------------------------------------------------------------------------------------*/ static void processShowCommand(void) { bool error; utModule module = parseModule(true, &error); utClass theClass; uint64 objectNumber; if(error) { return; } theClass = parseClass(module, true, &error); if(error) { return; } objectNumber = parseObjectNumber(theClass, false, &error); if(error) { return; } if(objectNumber == UINT64_MAX) { showClass(module, theClass); return; } if(!expectEnd()) { return; } showObject(theClass, objectNumber); } /*-------------------------------------------------------------------------------------------------- Process a set command. "set = comma separated values – \n" " set all fields of an object\n" "set = – set a field value\n" --------------------------------------------------------------------------------------------------*/ static void processSetCommand(void) { bool error; utModule module = parseModule(true, &error); utClass theClass; utField field; uint64 objectNumber; char *token; if(error) { return; } theClass = parseClass(module, true, &error); if(error) { return; } objectNumber = parseObjectNumber(theClass, true, &error); if(error) { return; } token = readToken(); if(!strcmp(token, "=")) { skipSpace(); setObjectFields(theClass, objectNumber, utLineBuffer + utLinePosition, NULL); return; } field = utFindField(theClass, token); if(field == NULL) { reportError("Field not found"); return; } token = readToken(); if(strcmp(token, "=")) { reportError("Expected '='"); return; } skipSpace(); setObjectField(theClass, field, objectNumber, utLineBuffer + utLinePosition); } /*-------------------------------------------------------------------------------------------------- Process a create command. --------------------------------------------------------------------------------------------------*/ static void processCreateCommand(void) { bool error; utModule module = parseModule(true, &error); utClass theClass; if(error) { return; } theClass = parseClass(module, true, &error); if(error) { return; } if(utClassGetConstructor(theClass) == NULL) { reportError("This is a class extension. Call the base class constructor instead"); return; } if(!expectEnd()) { return; } fprintf(utOutputFile, "New %s object 0x%llx\n", utClassGetName(theClass), utClassGetConstructor(theClass)()); } /*-------------------------------------------------------------------------------------------------- Destroy an object. --------------------------------------------------------------------------------------------------*/ static void processDestroyCommand(void) { bool error; utModule module = parseModule(true, &error); utClass theClass; uint64 objectNumber; if(error) { return; } theClass = parseClass(module, true, &error); if(error) { return; } objectNumber = parseObjectNumber(theClass, true, &error); if(error) { return; } if(!expectEnd()) { return; } if(utClassGetDestructor(theClass) == NULL) { reportError("This class has no destructor, either because it is an extension, or " "because it is declared 'create_only'"); return; } utClassGetDestructor(theClass)(objectNumber); fprintf(utOutputFile, "Destroyed %s object 0x%llx\n", utClassGetName(theClass), objectNumber); } /*-------------------------------------------------------------------------------------------------- Process a show_hidden command. --------------------------------------------------------------------------------------------------*/ static void processShowHiddenCommand(void) { char *token = readToken(); if(!expectEnd()) { return; } if(!strcasecmp(token, "true")) { utShowHidden = true; } else if(!strcasecmp(token, "false")) { utShowHidden = false; } else { reportError("Expecting 'true' or 'false'\n"); } } /*-------------------------------------------------------------------------------------------------- Process a load text command. --------------------------------------------------------------------------------------------------*/ static void processLoadTextCommand(void) { char *fileName = readToken(); FILE *file; if(!expectEnd()) { return; } file = fopen(fileName, "r"); if(file == NULL) { reportError("Could not open file %s for reading", fileName); return; } utLoadTextDatabase(file); fclose(file); utMemCheck(); } /*-------------------------------------------------------------------------------------------------- Process a write command. --------------------------------------------------------------------------------------------------*/ static void processSaveTextCommand(void) { char *fileName = readToken(); FILE *file; if(!expectEnd()) { return; } file = fopen(fileName, "w"); if(file == NULL) { reportError("Could not open file %s for writing", fileName); return; } utSaveTextDatabase(file); fclose(file); } /*-------------------------------------------------------------------------------------------------- Process a load binary command. --------------------------------------------------------------------------------------------------*/ static void processLoadBinaryCommand(void) { char *fileName = readToken(); FILE *file; if(!expectEnd()) { return; } file = fopen(fileName, "rb"); if(file == NULL) { reportError("Could not open file %s for reading", fileName); return; } utLoadBinaryDatabase(file); fclose(file); } /*-------------------------------------------------------------------------------------------------- Process a save binary command. --------------------------------------------------------------------------------------------------*/ static void processSaveBinaryCommand(void) { char *fileName = readToken(); FILE *file; if(!expectEnd()) { return; } file = fopen(fileName, "wb"); if(file == NULL) { reportError("Could not open file %s for writing", fileName); return; } utSaveBinaryDatabase(file); fclose(file); } /*-------------------------------------------------------------------------------------------------- Process the command in the line buffer. --------------------------------------------------------------------------------------------------*/ static bool processCommand(void) { char *token = readToken(); if(*token == '\0') { return true; /* Empty line */ } if(!strcmp(token, "create")) { processCreateCommand(); } else if(!strcmp(token, "compact")) { if(utPersistenceInitialized) { utCompactDatabase(); } else { reportError("Compact is only for persitent databases."); } } else if(!strcmp(token, "destroy")) { processDestroyCommand(); } else if(!strcmp(token, "help")) { printHelp(); return true; } else if(!strcmp(token, "list")) { processListCommand(); } else if(!strcmp(token, "quit")) { return false; } else if(!strcmp(token, "set")) { processSetCommand(); } else if(!strcmp(token, "show")) { processShowCommand(); } else if(!strcmp(token, "show_hidden")) { processShowHiddenCommand(); } else if(!strcmp(token, "load_text")) { processLoadTextCommand(); } else if(!strcmp(token, "save_text")) { processSaveTextCommand(); } else if(!strcmp(token, "load_binary")) { processLoadBinaryCommand(); } else if(!strcmp(token, "save_binary")) { processSaveBinaryCommand(); } else { fprintf(utOutputFile, "Invalid command. Type 'help' for a list of commands\n"); } return true; } /*-------------------------------------------------------------------------------------------------- Initialize the database manager. --------------------------------------------------------------------------------------------------*/ void utDatabaseManagerStart(void) { utLineSize = 42; utLineBuffer = utNewA(char, utLineSize); utOutputFile = stdout; utInputFile = stdin; } /*-------------------------------------------------------------------------------------------------- Clean up after the database manager. --------------------------------------------------------------------------------------------------*/ void utDatabaseManagerStop(void) { utFree(utLineBuffer); } /*-------------------------------------------------------------------------------------------------- Interpret commands to manage the database. --------------------------------------------------------------------------------------------------*/ void utManager(void) { utAtEndOfFile = false; fprintf(utOutputFile, "For help, enter the 'help' command\n"); do { fprintf(utOutputFile, "> "); readLine(); } while(processCommand()); } /*-------------------------------------------------------------------------------------------------- Skip blank lines. --------------------------------------------------------------------------------------------------*/ static void skipBlankLines(void) { while(readLine()) { if(utLineBuffer[0] != '\0') { return; } } } /*-------------------------------------------------------------------------------------------------- Read a module header. --------------------------------------------------------------------------------------------------*/ static utModule readModuleHeader(void) { utModule module; char *token; token = readToken(); if(strcmp(token, "module")) { if(!utAtEndOfFile) { reportError("Expected 'module' keyword"); } return NULL; } token = readToken(); if(token == NULL) { reportError("Expected module name"); return NULL; } module = utFindModule(token); if(module == NULL) { reportError("Module %s not found", token); return NULL; } if(!expectEnd()) { return NULL; } return module; } /*-------------------------------------------------------------------------------------------------- Reallocate object fields for the class. --------------------------------------------------------------------------------------------------*/ static void reallocObjects( utClass theClass, uint64 numUsed) { utField field; utSetInteger(utClassGetNumAllocatedPtr(theClass), numUsed, utClassGetReferenceSize(theClass)); utForeachClassField(theClass, field) { if(!utFieldArray(field)) { *(uint8 **)(utFieldGetArrayPtr(field)) = utRealloc(*(uint8 **)utFieldGetArrayPtr(field), numUsed, utFieldGetSize(field)); } } utEndClassField; } /*-------------------------------------------------------------------------------------------------- Read a class header, and allocate space for the objects. --------------------------------------------------------------------------------------------------*/ static utClass readClassHeader( utModule module) { utClass theClass; char *token; uint64 numUsed; skipBlankLines(); token = readToken(); if(strcmp(token, "class")) { utLinePosition = 0; return NULL; } token = readToken(); if(token == NULL) { reportError("Expected class name"); return NULL; } theClass = findClass(module, token); if(theClass == NULL) { reportError("Class %s not found", token); return NULL; } token = readToken(); if(!readInteger(utClassGetNumUsedPtr(theClass), token, utClassGetReferenceSize(theClass), true)) { reportError("Expected number of objects"); return NULL; } if(!expectEnd()) { return NULL; } numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); numUsed = utMax(2, numUsed); reallocObjects(theClass, numUsed); return theClass; } /*-------------------------------------------------------------------------------------------------- Read in column headers. Build a translation table from column number to field. The caller will have to free utFieldTranslationTable. --------------------------------------------------------------------------------------------------*/ static bool readColumnHeaders( utClass theClass) { utField field; char *valueList, *value; uint32 numFields, xField; if(!readLine()) { reportError("Expected column headers"); return false; } valueList = utLineBuffer; numFields = countListValues(valueList) - 1; utFieldTranslationTable = calloc(numFields, sizeof(utField)); value = parseArrayValue(&valueList); if(value == NULL || strcmp(value, "ObjectNumber")) { reportError("Expected 'objectNumber' to be the first field"); return false; } for(xField = 0; xField < numFields; xField++) { value = parseArrayValue(&valueList); if(value == NULL) { return false; } field = utFindField(theClass, value); if(field == NULL) { reportError("Unknown field %s in class %s -- dropping", value, utClassGetName(theClass)); } utFieldTranslationTable[xField] = field; } utNumFields = numFields; return true; } /*-------------------------------------------------------------------------------------------------- Read in column types. If it is incompatible with the field, print a warning, and null out the entry in the translation table. --------------------------------------------------------------------------------------------------*/ static bool readColumnTypes( utClass theClass) { char *valueList, *value; uint32 numFields, xField; if(!readLine()) { reportError("Expected column types"); return false; } valueList = utLineBuffer; numFields = countListValues(valueList) - 1; if(numFields != utNumFields) { reportError("Column number mismatch"); return false; } value = parseArrayValue(&valueList); if(value == NULL) { reportError("Expected class reference type to be the first field"); return false; } for(xField = 0; xField < numFields; xField++) { value = parseArrayValue(&valueList); if(value == NULL) { return false; } } return true; } /*-------------------------------------------------------------------------------------------------- Add the objects between the start and stop to the free list. --------------------------------------------------------------------------------------------------*/ static void addObjectsToFreeList( utModule module, utClass theClass, uint32 start, uint32 stop) { utField field = utClassGetNextFreeField(theClass); uint64 firstFree = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass)); uint64 xObject; uint8 *dest; for(xObject = start + 1; xObject < stop; xObject++) { firstFree = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass)); dest = *(uint8 **)(utFieldGetArrayPtr(field)) + xObject*utFieldGetSize(field); utSetInteger(dest, firstFree, utClassGetReferenceSize(theClass)); utSetInteger(utClassGetFirstFreePtr(theClass), xObject, utClassGetReferenceSize(theClass)); } } /*-------------------------------------------------------------------------------------------------- Read in a table of objects. A blank line ends the table. --------------------------------------------------------------------------------------------------*/ static bool readClassTable( utModule module, utClass theClass) { char *valueList; uint32 numValues; uint64 objectNumber, lastObjectNumber = 0; bool error; utDo { if(!readLine()) { return false; } skipSpace(); } utWhile(utLineBuffer[utLinePosition] != '\0') { objectNumber = parseObjectNumber(theClass, true, &error); if(error) { return false; } if(strcmp(readToken(), ",")) { reportError("Expecting a ','"); } valueList = utLineBuffer + utLinePosition; numValues = countListValues(valueList); if(numValues != utNumFields) { reportError("Mismatched number of fields"); return false; } if(!setObjectFields(theClass, objectNumber, valueList, utFieldTranslationTable)) { return false; } if(utClassGetDestructor(theClass) != NULL) { addObjectsToFreeList(module, theClass, lastObjectNumber, objectNumber); } lastObjectNumber = objectNumber; } utRepeat; return true; } /*-------------------------------------------------------------------------------------------------- Read in the module. --------------------------------------------------------------------------------------------------*/ static bool readModule( utModule module) { utClass theClass; bool passed; utDo { theClass = readClassHeader(module); } utWhile(theClass != NULL) { if(!readColumnHeaders(theClass)) { return false; } if(!readColumnTypes(theClass)) { return false; } passed = readClassTable(module, theClass); free(utFieldTranslationTable); if(!passed) { return false; } } utRepeat; return true; } /*-------------------------------------------------------------------------------------------------- Read in the database in ASCII. --------------------------------------------------------------------------------------------------*/ void utLoadTextDatabase( FILE *file) { char *fileName; utModule module; FILE *savedFile = utInputFile; bool useDefault = file == NULL; if(useDefault) { fileName = utSprintf("%s%cdatabase", utDatabaseDirectory, UTDIRSEP); file = fopen(fileName, "r"); } if(file == NULL) { utExit("Could not read from %s", fileName); } utResetDatabase(); utAtEndOfFile = false; utLineNum = 0; utInputFile = file; skipBlankLines(); do { module = readModuleHeader(); } while(module != NULL && readModule(module)); utInputFile = savedFile; if(useDefault) { fclose(file); } } /*-------------------------------------------------------------------------------------------------- Write out the database in ASCII. Don't write the utility module. --------------------------------------------------------------------------------------------------*/ void utSaveTextDatabase( FILE *file) { char *fileName; utModule module; utClass theClass; FILE *savedFile = utOutputFile; bool useDefault = file == NULL; if(useDefault) { fileName = utSprintf("%s%cdatabase", utDatabaseDirectory, UTDIRSEP); file = fopen(fileName, "w"); } if(file == NULL) { utExit("Could not write to %s", fileName); } utOutputFile = file; utForeachModule(module) { if(strcmp(utModuleGetPrefix(module), "ut") && utModuleInitialized(module)) { fprintf(utOutputFile, "module %s\n\n", utModuleGetPrefix(module)); utForeachModuleClass(module, theClass) { showClass(module, theClass); fprintf(utOutputFile, "\n"); } utEndModuleClass; } } utEndModule; if(useDefault) { fclose(file); } utOutputFile = savedFile; } /*-------------------------------------------------------------------------------------------------- Write out the fields of an object. --------------------------------------------------------------------------------------------------*/ void utDatabaseShowObject( char *modulePrefix, char *className, uint64 objectNumber) { utModule module = utFindModule(modulePrefix); utClass theClass; uint64 numUsed; FILE *savedFile = utOutputFile; if(module == NULL) { reportError("Invalid module %s\n", modulePrefix); return; } theClass = findClass(module, className); if(theClass == NULL) { reportError("Invalid class %s\n", className); return; } utOutputFile = stdout; showObject(theClass, objectNumber); utOutputFile = savedFile; numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass)); if(objectNumber >= numUsed) { reportError("Object number too large"); return; } }