1 /*
2  * This file was written by Bill Cox.  It is hereby placed into the public domain.
3  */
4 
5 /*--------------------------------------------------------------------------------------------------
6   This module provides basic database administration capabilities.
7 --------------------------------------------------------------------------------------------------*/
8 #include <stdlib.h>
9 #include <ctype.h>
10 #include "ddutil.h"
11 #include "utpersist.h"
12 
13 static char *utLineBuffer;
14 static uint32 utLineSize, utLinePosition;
15 static uint64 utLineNum;
16 static bool utAtEndOfFile;
17 static char *utLastToken;
18 static bool utShowHidden;
19 static FILE *utInputFile, *utOutputFile;
20 static utField *utFieldTranslationTable;
21 static uint32 utNumFields;
22 
23 /*--------------------------------------------------------------------------------------------------
24   Read a module header.
25 --------------------------------------------------------------------------------------------------*/
reportError(char * format,...)26 static void reportError(
27     char *format,
28     ...)
29 {
30     char *buff;
31     va_list ap;
32 
33     va_start(ap, format);
34     buff = utVsprintf(format, ap);
35     va_end(ap);
36     if(utInputFile != stdin) {
37         utWarning("Line %lld, token \"%s\": %s", utLineNum, utLastToken, buff);
38     } else {
39         utWarning("Token \"%s\": %s", utLastToken, buff);
40     }
41 }
42 
43 /*--------------------------------------------------------------------------------------------------
44   Return an integer of the given size.
45 --------------------------------------------------------------------------------------------------*/
utFindIntValue(void * values,uint8 size)46 uint64 utFindIntValue(
47     void *values,
48     uint8 size)
49 {
50     switch(size) {
51     case 1: return *(uint8 *)values;
52     case 2: return *(uint16 *)values;
53     case 4: return *(uint32 *)values;
54     case 8: return *(uint64 *)values;
55     }
56     utExit("Illegal integer size");
57     return 0;
58 }
59 
60 /*--------------------------------------------------------------------------------------------------
61   Print out help.
62 --------------------------------------------------------------------------------------------------*/
printHelp(void)63 static void printHelp(void)
64 {
65     fprintf(utOutputFile,
66         "create <module> <class> - allocate a new object, and return it's object number\n"
67         "compact - Compact the database, and delete the recent_changes file\n"
68         "destroy <module> <class> <object number> - Destroy an object\n"
69         "help - this command\n"
70         "list - list the modules in the database, their object counts and memory usage\n"
71         "list <module> - list classes in the module, their object counts and memory usage\n"
72         "list <module> <class> - list fields of a class\n"
73         "quit - quit the database manager\n"
74         "set <module> <class> <object number> = comma separated values – \n"
75         "    set all fields of an object\n"
76         "set <module> <class> <object number> <field> = <value> - set a value\n"
77         "show <module> <class> - show all field values of all objects of the class\n"
78         "show <module> <class> <object number> - show an object's fields\n"
79         "show_hidden <true or false> - Enable/disable listing of DataDraw internal fields\n"
80         "load_binary <file> - Read the data from the binary database file into the database\n"
81         "save_binary <file> - Write out the database in binary format to the file\n"
82         "load_text <file> - Read the data from the text database file into the database\n"
83         "save_text <file> - Write out the database in text format to the file\n");
84 }
85 
86 /*--------------------------------------------------------------------------------------------------
87   Return the number of objects allocated for the module.
88 --------------------------------------------------------------------------------------------------*/
countModuleObjects(utModule module)89 static uint64 countModuleObjects(
90     utModule module)
91 {
92     utClass theClass;
93     uint64 numObjects = 0;
94 
95     utForeachModuleClass(module, theClass) {
96         numObjects += utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
97     } utEndModuleClass;
98     return numObjects;
99 }
100 
101 /*--------------------------------------------------------------------------------------------------
102   Return the total memory size used by the class.
103 --------------------------------------------------------------------------------------------------*/
findClassMemory(utClass theClass)104 static uint64 findClassMemory(
105     utClass theClass)
106 {
107     utField field;
108     uint64 memory = 0;
109     uint64 numUsed;
110 
111     utForeachClassField(theClass, field) {
112         if(utFieldArray(field)) {
113             if(!utFieldFixedSize(field)) {
114                 numUsed = *utFieldGetNumUsedPtr(field);
115             } else {
116                 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
117                 numUsed *= utFieldGetLength(field);
118             }
119         } else {
120             numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
121         }
122         memory += utFieldGetSize(field)*numUsed;
123     } utEndClassField;
124     return memory;
125 }
126 
127 /*--------------------------------------------------------------------------------------------------
128   List the modules in the database.
129 --------------------------------------------------------------------------------------------------*/
findModuleMemory(utModule module)130 static uint64 findModuleMemory(
131     utModule module)
132 {
133     utClass theClass;
134     uint64 memory = 0;
135 
136     utForeachModuleClass(module, theClass) {
137         memory += findClassMemory(theClass);
138     } utEndModuleClass;
139     return memory;
140 }
141 
142 /*--------------------------------------------------------------------------------------------------
143   Find the ammount in memory units.
144 --------------------------------------------------------------------------------------------------*/
utMemoryUnits(uint64 memory)145 char *utMemoryUnits(
146     uint64 memory)
147 {
148 #if (_MSC_VER >= 1200) && (_MSC_VER < 1300)
149    //for Visual Studio 6 use a signed __int64
150    signed __int64 tempMem = memory;
151    if(memory > 1024*1024) {
152       return utSprintf("%.5g MB", tempMem/(1024.0*1024.0));
153    } else if(memory > 1024) {
154       return utSprintf("%.5g KB", tempMem/1024.0);
155    }
156    return utSprintf("%lu bytes", tempMem);
157 #else
158    if(memory > 1024*1024) {
159        return utSprintf("%.5g MB", memory/(1024.0*1024.0));
160    } else if(memory > 1024) {
161        return utSprintf("%.5g KB", memory/1024.0);
162    }
163    return utSprintf("%lu bytes", memory);
164 #endif
165 }
166 
167 /*--------------------------------------------------------------------------------------------------
168   List the modules in the database.
169 --------------------------------------------------------------------------------------------------*/
listModules(void)170 static void listModules(void)
171 {
172     utModule module;
173 
174     fprintf(utOutputFile, "modules:\n");
175     utForeachModule(module) {
176         if(utModuleInitialized(module)) {
177             fprintf(utOutputFile, "    %s - %llu objects, %s memory\n", utModuleGetPrefix(module),
178                 countModuleObjects(module), utMemoryUnits(findModuleMemory(module)));
179         }
180     } utEndModule;
181 }
182 
183 /*--------------------------------------------------------------------------------------------------
184   List the classes in the module.
185 --------------------------------------------------------------------------------------------------*/
listClasses(utModule module)186 static void listClasses(
187     utModule module)
188 {
189     utClass theClass;
190     uint64 numUsed;
191 
192     fprintf(utOutputFile, "Module %s classes:\n", utModuleGetPrefix(module));
193     utForeachModuleClass(module, theClass) {
194         numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
195         fprintf(utOutputFile, "    %s - %llu objects, %s memory\n", utClassGetName(theClass), numUsed,
196             utMemoryUnits(findClassMemory(theClass)));
197     } utEndModuleClass;
198 }
199 
200 /*--------------------------------------------------------------------------------------------------
201   Just return a name for the field type.
202 --------------------------------------------------------------------------------------------------*/
findBaseFieldTypeName(utField field)203 static char *findBaseFieldTypeName(
204     utField field)
205 {
206     switch(utFieldGetType(field)) {
207     case UT_INT: return utSprintf("int%u", utFieldGetSize(field) << 3);
208     case UT_UINT: return utSprintf("uint%u", utFieldGetSize(field) << 3);
209     case UT_FLOAT: return "float";
210     case UT_DOUBLE: return "double";
211     case UT_BIT: return "bit";
212     case UT_BOOL: return "bool";
213     case UT_CHAR: return "char";
214     case UT_SYM: return "sym";
215     case UT_ENUM: return "enum";
216     case UT_TYPEDEF: return "typedef";
217     case UT_POINTER: return utSprintf("pointer %s", utFieldGetDestName(field));
218     case UT_UNION: return utSprintf("union %s", utFieldGetDestName(field));
219     default:
220         utExit("Unknown property type");
221     }
222     return NULL; /* Dummy return */
223 }
224 
225 /*--------------------------------------------------------------------------------------------------
226   Just return a name for the field type.
227 --------------------------------------------------------------------------------------------------*/
findFieldTypeName(utField field)228 static char *findFieldTypeName(
229     utField field)
230 {
231     char *baseName = findBaseFieldTypeName(field);
232 
233     if(utFieldArray(field)) {
234         if(!utFieldFixedSize(field)) {
235             return utSprintf("array %s", baseName);
236         }
237         return utSprintf("array %s[%u]", baseName, utFieldGetLength(field));
238     }
239     return baseName;
240 }
241 
242 /*--------------------------------------------------------------------------------------------------
243   List the fields of the class.
244 --------------------------------------------------------------------------------------------------*/
listFields(utClass theClass)245 static void listFields(
246     utClass theClass)
247 {
248     utField field;
249     char *length;
250 
251     fprintf(utOutputFile, "Class %s fields:\n", utClassGetName(theClass));
252     utForeachClassField(theClass, field) {
253         if(!utFieldHidden(field) || utShowHidden) {
254             length = "";
255             if(utFieldFixedSize(field)) {
256                 length = utSprintf("[%u]", utFieldGetLength(field));
257             }
258             fprintf(utOutputFile, "    %s %s%s\n", findFieldTypeName(field), utFieldGetName(field), length);
259         }
260     } utEndClassField;
261 }
262 
263 /*--------------------------------------------------------------------------------------------------
264   Find the enum with the given name.
265 --------------------------------------------------------------------------------------------------*/
findEnum(utModule module,char * name)266 static utEnum findEnum(
267     utModule module,
268     char *name)
269 {
270     utEnum theEnum;
271 
272     utForeachModuleEnum(module, theEnum) {
273         if(!strcmp(utEnumGetName(theEnum), name)) {
274             return theEnum;
275         }
276     } utEndModuleEnum;
277     return NULL;
278 }
279 
280 /*--------------------------------------------------------------------------------------------------
281   Find the entry with the given value.
282 --------------------------------------------------------------------------------------------------*/
findEntryFromValue(utEnum theEnum,uint32 value)283 static utEntry findEntryFromValue(
284     utEnum theEnum,
285     uint32 value)
286 {
287     utEntry entry;
288 
289     utForeachEnumEntry(theEnum, entry) {
290         if(utEntryGetValue(entry) == value) {
291             return entry;
292         }
293     } utEndEnumEntry;
294     return NULL;
295 }
296 
297 /*--------------------------------------------------------------------------------------------------
298   Find the entry with the given name.
299 --------------------------------------------------------------------------------------------------*/
findEntryFromName(utEnum theEnum,char * name)300 static utEntry findEntryFromName(
301     utEnum theEnum,
302     char *name)
303 {
304     utEntry entry;
305 
306     utForeachEnumEntry(theEnum, entry) {
307         if(!strcmp(utEntryGetName(entry), name)) {
308             return entry;
309         }
310     } utEndEnumEntry;
311     return NULL;
312 }
313 
314 /*--------------------------------------------------------------------------------------------------
315   Return a hexidecimal digit representation of the 4-bit value.
316 --------------------------------------------------------------------------------------------------*/
findHexDigit(uint8 value)317 static char findHexDigit(
318     uint8 value)
319 {
320     if(value <= 9) {
321         return '0' + value;
322     }
323     return 'A' + value - 10;
324 }
325 
326 /*--------------------------------------------------------------------------------------------------
327   Return a long hexidecimal string representing the binary value.
328 --------------------------------------------------------------------------------------------------*/
utFindHexString(uint8 * values,uint32 size)329 char *utFindHexString(
330     uint8 *values,
331     uint32 size)
332 {
333     char *buffer = utMakeString((size << 1) + 1);
334     char *p = buffer + (size << 1) - 1;
335 
336     buffer[size*2] = '\0';
337     while(size-- != 0) {
338         *p-- = findHexDigit((uint8)(*values & 0xf));
339         *p-- = findHexDigit((uint8)((*values++) >> 4));
340     }
341     return buffer;
342 }
343 
344 /*--------------------------------------------------------------------------------------------------
345   Count the non "isprint" characters in the string.
346 --------------------------------------------------------------------------------------------------*/
countNonprintableChars(char * string)347 static uint32 countNonprintableChars(
348     char *string)
349 {
350     uint32 nonPrintableChars = 0;
351 
352     while(*string) {
353         if(!isprint(*string)) {
354             nonPrintableChars++;
355         }
356         string++;
357     }
358     return nonPrintableChars;
359 }
360 
361 /*--------------------------------------------------------------------------------------------------
362   Convert any non-string friendly characters to \" or \<number> format.
363 --------------------------------------------------------------------------------------------------*/
mungeString(char * string)364 static char *mungeString(
365     char *string)
366 {
367     char *buffer = utMakeString(strlen(string) + 4*countNonprintableChars(string) + 1);
368     char *p = buffer;
369     char intValue[4];
370 
371     while(*string != '\0') {
372         if(isprint(*string)) {
373             if(*string == '"' || *string == '\\') {
374                 *p++ = '\\';
375             }
376             *p++ = *string++;
377         } else {
378             sprintf(intValue, "\\%u", *string);
379             intValue[3] = '\0';
380             strcpy(p, intValue);
381             p += strlen(intValue);
382             string++;
383         }
384     }
385     *p = '\0';
386     return buffer;
387 }
388 
389 /*--------------------------------------------------------------------------------------------------
390   Find the union case based on the switch field.
391 --------------------------------------------------------------------------------------------------*/
findUnioncase(utUnion theUnion,uint64 objectNumber)392 static utUnioncase findUnioncase(
393     utUnion theUnion,
394     uint64 objectNumber)
395 {
396     utField switchField = utUnionGetSwitchField(theUnion);
397     utUnioncase unioncase;
398     uint32 value = *(*(uint32 **)(utFieldGetArrayPtr(switchField)) + objectNumber);
399 
400     utForeachUnionUnioncase(theUnion, unioncase) {
401         if(utUnioncaseGetValue(unioncase) == value) {
402             return unioncase;
403         }
404     } utEndUnionUnioncase;
405     return NULL;
406 }
407 
408 /*--------------------------------------------------------------------------------------------------
409   Return a string representation of the field value.
410 --------------------------------------------------------------------------------------------------*/
findFieldValue(utField field,uint64 objectNumber)411 static char *findFieldValue(
412     utField field,
413     uint64 objectNumber)
414 {
415     utModule module;
416     utClass theClass;
417     utEnum theEnum;
418     utEntry entry;
419     utUnion theUnion;
420     utUnioncase unioncase;
421     utSym sym;
422     uint8 *values;
423     uint32 size = utFieldGetSize(field);
424     utFieldType type = utFieldGetType(field);
425 
426     if(type == UT_BIT) {
427         values = *(uint8 **)(utFieldGetArrayPtr(field)) + (objectNumber >> 3)*utFieldGetSize(field);
428     } else {
429         values = *(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field);
430     }
431     if(type == UT_UNION) {
432         theUnion = utFieldGetUnion(field);
433         unioncase = findUnioncase(theUnion, objectNumber);
434         if(unioncase != NULL) {
435             type = utUnioncaseGetType(unioncase);
436             size = utUnioncaseGetSize(unioncase);
437         }
438     }
439     switch(type) {
440     case UT_BIT:
441         if((*values >> (objectNumber & 7)) & 1) {
442             return "true";
443         }
444         return "false";
445     case UT_BOOL:
446         if(*values == 1) {
447             return "true";
448         } else if(*values == 0) {
449             return "false";
450         }
451         return utSprintf("%d", *values);
452     case UT_INT:
453         switch(size) {
454         case 1: return utSprintf("%d", *(int8 *)values);
455         case 2: return utSprintf("%d", *(int16 *)values);
456         case 4: return utSprintf("%d", *(int32 *)values);
457         case 8: return utSprintf("%d", *(int64 *)values);
458         }
459         utExit("Invalid integer size");
460     case UT_UINT:
461         switch(size) {
462         case 1: return utSprintf("%u", *(uint8 *)values);
463         case 2: return utSprintf("%u", *(uint16 *)values);
464         case 4: return utSprintf("%u", *(uint32 *)values);
465         case 8: return utSprintf("%u", *(uint64 *)values);
466         }
467         utExit("Invalid integer size");
468     case UT_CHAR:
469         if(isprint(*values)) {
470             return utSprintf("'%c'", *values);
471         }
472         return utSprintf("'\\%u'", *values);
473     case UT_FLOAT:
474         return utSprintf("%g", *(float *)values);
475     case UT_DOUBLE:
476         return utSprintf("%g", *(double *)values);
477     case UT_POINTER:
478         switch(size) {
479         case 1: return utSprintf("0x%x", *(uint8 *)values);
480         case 2: return utSprintf("0x%x", *(uint16 *)values);
481         case 4: return utSprintf("0x%x", *(uint32 *)values);
482         case 8: return utSprintf("0x%x", *(uint64 *)values);
483         }
484     case UT_TYPEDEF: case UT_UNION:
485         return utFindHexString(values, size);
486     case UT_ENUM:
487         theClass = utFieldGetClass(field);
488         module = utClassGetModule(theClass);
489         theEnum = findEnum(module, utFieldGetDestName(field));
490         entry = findEntryFromValue(theEnum, *(uint32 *)values);
491         return utEntryGetName(entry);
492     case UT_SYM:
493         sym = *(utSym *)values;
494         if(sym == utSymNull) {
495             return utSprintf("0x%x", utSymNull);
496         }
497         return utSprintf("\"%s\"", mungeString(utSymGetName(*(utSym *)values)));
498     }
499     utExit("Unknow field type");
500     return NULL;
501 }
502 
503 /*--------------------------------------------------------------------------------------------------
504   Determine if the only '\0' is at the end of the character array.
505 --------------------------------------------------------------------------------------------------*/
zeroTerminated(char * values,uint32 length)506 static bool zeroTerminated(
507     char *values,
508     uint32 length)
509 {
510     if(values[--length] != '\0') {
511         return false;
512     }
513     while(length != 0 && values[length] != '\0') {
514         length--;
515     }
516     return values[length] == '\0';
517 }
518 
519 /*--------------------------------------------------------------------------------------------------
520   Show the data in the array.  If we go over maxFields, we print '...'.
521 --------------------------------------------------------------------------------------------------*/
writeArray(utField field,uint64 objectNumber,uint32 maxFields)522 static void writeArray(
523     utField field,
524     uint64 objectNumber,
525     uint32 maxFields)
526 {
527     uint32 numElements;
528     uint32 xElement;
529     bool firstTime = true;
530     char *values = utFieldGetGetValues(field)(objectNumber, &numElements);
531     uint32 firstElement = (values - *(char **)(utFieldGetArrayPtr(field)))/utFieldGetSize(field);
532 
533     if(numElements != 0 && utFieldGetType(field) == UT_CHAR && zeroTerminated(values, numElements)) {
534         fprintf(utOutputFile, "\"%s\"", mungeString(values));
535         return;
536     }
537     fprintf(utOutputFile, "(");
538     for(xElement = 0; xElement < numElements && xElement < maxFields; xElement++) {
539         if(!firstTime) {
540             fprintf(utOutputFile, ", ");
541         }
542         firstTime = false;
543         fprintf(utOutputFile, "%s", findFieldValue(field, firstElement + xElement));
544     }
545     if(xElement == maxFields) {
546         fprintf(utOutputFile, ", ...");
547     }
548     fprintf(utOutputFile, ")");
549 }
550 
551 /*--------------------------------------------------------------------------------------------------
552   Show the fields of an object.
553 --------------------------------------------------------------------------------------------------*/
showObject(utClass theClass,uint64 objectNumber)554 static void showObject(
555     utClass theClass,
556     uint64 objectNumber)
557 {
558     utField field;
559     bool firstTime = true;
560 
561     utForeachClassField(theClass, field) {
562         if(!utFieldHidden(field) || utShowHidden) {
563             if(!firstTime) {
564                 fprintf(utOutputFile, ", ");
565             }
566             firstTime = false;
567             fprintf(utOutputFile, "%s=", utFieldGetName(field));
568             if(!utFieldArray(field)) {
569                 fprintf(utOutputFile, "%s", findFieldValue(field, objectNumber));
570             } else {
571                 writeArray(field, objectNumber, 16);
572             }
573         }
574     } utEndClassField;
575     fprintf(utOutputFile, "\n");
576 }
577 
578 /*--------------------------------------------------------------------------------------------------
579   Just print the field names.
580 --------------------------------------------------------------------------------------------------*/
printColumnHeaders(utClass theClass)581 static void printColumnHeaders(
582     utClass theClass)
583 {
584     utField field;
585     bool firstTime = true;
586 
587     fprintf(utOutputFile, "ObjectNumber, ");
588     utForeachClassField(theClass, field) {
589         if(!utFieldHidden(field)) {
590             if(!firstTime) {
591                 fprintf(utOutputFile, ", ");
592             }
593             firstTime = false;
594             fprintf(utOutputFile, "%s", utFieldGetName(field));
595         }
596     } utEndClassField;
597     fprintf(utOutputFile, "\n");
598 }
599 
600 /*--------------------------------------------------------------------------------------------------
601   Just print the field types.
602 --------------------------------------------------------------------------------------------------*/
printColumnTypes(utClass theClass)603 static void printColumnTypes(
604     utClass theClass)
605 {
606     utField field;
607     bool firstTime = true;
608 
609     fprintf(utOutputFile, "uint%u, ", utClassGetReferenceSize(theClass) << 3);
610     utForeachClassField(theClass, field) {
611         if(!utFieldHidden(field)) {
612             if(!firstTime) {
613                 fprintf(utOutputFile, ", ");
614             }
615             firstTime = false;
616             fprintf(utOutputFile, "%s", findFieldTypeName(field));
617         }
618     } utEndClassField;
619     fprintf(utOutputFile, "\n");
620 }
621 
622 /*--------------------------------------------------------------------------------------------------
623   Print the object's values in a comma separated list.
624 --------------------------------------------------------------------------------------------------*/
showObjectFields(utClass theClass,uint64 objectNumber)625 static void showObjectFields(
626     utClass theClass,
627     uint64 objectNumber)
628 {
629     utField field;
630     bool firstTime = true;
631 
632     fprintf(utOutputFile, "0x%llx, ", objectNumber);
633     utForeachClassField(theClass, field) {
634         if(!utFieldHidden(field) || utShowHidden) {
635             if(!firstTime) {
636                 fprintf(utOutputFile, ", ");
637             }
638             firstTime = false;
639             if(!utFieldArray(field)) {
640                 fprintf(utOutputFile, "%s", findFieldValue(field, objectNumber));
641             } else {
642                 writeArray(field, objectNumber, UINT32_MAX);
643             }
644         }
645     } utEndClassField;
646     fprintf(utOutputFile, "\n");
647 }
648 
649 /*--------------------------------------------------------------------------------------------------
650   Return the null reference for references of the given size.
651 --------------------------------------------------------------------------------------------------*/
findNullReference(uint8 size)652 static uint64 findNullReference(
653     uint8 size)
654 {
655     switch(size) {
656     case 1: return UINT8_MAX;
657     case 2: return UINT16_MAX;
658     case 4: return UINT32_MAX;
659     case 8: return UINT64_MAX;
660     default:
661         utExit("Invalid reference size");
662     }
663     return 0;
664 }
665 
666 /*--------------------------------------------------------------------------------------------------
667   Find the base class for this class.
668 --------------------------------------------------------------------------------------------------*/
utClassGetBaseClass(utClass theClass)669 utClass utClassGetBaseClass(
670     utClass theClass)
671 {
672     uint16 baseClassIndex = utClassGetBaseClassIndex(theClass);
673 
674     if(baseClassIndex == UINT16_MAX) {
675         return utClassNull;
676     }
677     return utClasses + baseClassIndex;
678 }
679 
680 /*--------------------------------------------------------------------------------------------------
681   Find the root class for this class, where we will find the memory management stuff.
682 --------------------------------------------------------------------------------------------------*/
utClassFindRootClass(utClass theClass)683 utClass utClassFindRootClass(
684     utClass theClass)
685 {
686     utClass baseClass;
687 
688     utDo {
689         baseClass = utClassGetBaseClass(theClass);
690     } utWhile(baseClass != utClassNull) {
691         theClass = baseClass;
692     } utRepeat;
693     return theClass;
694 }
695 
696 /*--------------------------------------------------------------------------------------------------
697   Set the used flags for the objects of the class.
698 --------------------------------------------------------------------------------------------------*/
setObjectFreeFlags(utClass theClass,uint8 * objectFree)699 static void setObjectFreeFlags(
700     utClass theClass,
701     uint8 *objectFree)
702 {
703     utField field;
704     uint64 objectNumber;
705 
706     theClass = utClassFindRootClass(theClass);
707     field = utClassGetNextFreeField(theClass);
708     objectNumber = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass));
709     while(objectNumber != findNullReference(utClassGetReferenceSize(theClass))) {
710         objectFree[objectNumber >> 3] |= 1 << (objectNumber & 7);
711         objectNumber = utFindIntValue(*(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field),
712             (uint8)utFieldGetSize(field));
713     }
714 }
715 
716 /*--------------------------------------------------------------------------------------------------
717   Show all the objects of a class.
718 --------------------------------------------------------------------------------------------------*/
showClass(utModule module,utClass theClass)719 static void showClass(
720     utModule module,
721     utClass theClass)
722 {
723     uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
724     uint64 objectNumber;
725     bool hasFreeList = utClassGetDestructor(theClass) != NULL;
726     uint64 spaceNeeded = (numUsed + 7) >> 3;
727     uint8 *objectFree = NULL;
728 
729     if(hasFreeList) {
730         objectFree = calloc(spaceNeeded, sizeof(uint8));
731         setObjectFreeFlags(theClass, objectFree);
732     }
733     fprintf(utOutputFile, "class %s %llu\n", utClassGetName(theClass), numUsed);
734     printColumnHeaders(theClass);
735     printColumnTypes(theClass);
736     for(objectNumber = 0; objectNumber < numUsed; objectNumber++) {
737         if(!hasFreeList || !(objectFree[objectNumber >> 3] & (1 << (objectNumber & 7)))) {
738             showObjectFields(theClass, objectNumber);
739         }
740     }
741     if(hasFreeList) {
742         free(objectFree);
743     }
744 }
745 
746 /*--------------------------------------------------------------------------------------------------
747   Write the integer, given the width.
748 --------------------------------------------------------------------------------------------------*/
utSetInteger(uint8 * dest,uint64 value,uint8 width)749 void utSetInteger(
750     uint8 *dest,
751     uint64 value,
752     uint8 width)
753 {
754     switch(width) {
755     case 1:
756         *dest = (uint8)value;
757         break;
758     case 2:
759         *(uint16 *)dest = (uint16)value;
760         break;
761     case 4:
762         *(uint32 *)dest = (uint32)value;
763         break;
764     case 8:
765         *(uint64 *)dest = (uint64)value;
766         break;
767     default:
768         utExit("Invalid integer width");
769     }
770 }
771 
772 /*--------------------------------------------------------------------------------------------------
773   Parse the integer and return it's value.  Allow hex if we see "0x" in front.  Allow negation.
774   Set passed to indicate success.
775 --------------------------------------------------------------------------------------------------*/
utParseInteger(int64 * dest,char * string)776 bool utParseInteger(
777     int64 *dest,
778     char *string)
779 {
780     uint64 value = 0, oldValue = 0;
781     char c;
782     uint8 digit;
783     bool negate = false;
784 
785     if(*string == '0' && *(string + 1) == 'x') {
786         string += 2;
787         while(*string) {
788             c = toupper(*string++);
789             if(c >= '0' && c <= '9') {
790                 digit = c - '0';
791             } else if(c >= 'A' && c <= 'F') {
792                 digit = c - 'A' + 10;
793             } else {
794                 return false;
795             }
796             value <<= 4;
797             value |= digit;
798             if(value < oldValue) {
799                 return false;
800             }
801             oldValue = value;
802         }
803     } else {
804         if(*string == '-') {
805             string++;
806             negate = true;
807         }
808         while(*string) {
809             c = *string++;
810             if(c < '0' || c > '9') {
811                 return false;
812             }
813             value *= 10;
814             value += c - '0';
815             if(value < oldValue) {
816                 return false;
817             }
818             oldValue = value;
819         }
820     }
821     *dest = negate? -value : value;
822     return true;
823 }
824 
825 /*--------------------------------------------------------------------------------------------------
826   Read an integer and complain if it has bad width or syntax.
827 --------------------------------------------------------------------------------------------------*/
readInteger(uint8 * dest,char * string,uint8 width,bool unsignedInt)828 static bool readInteger(
829     uint8 *dest,
830     char *string,
831     uint8 width,
832     bool unsignedInt)
833 {
834     int64 value;
835 
836     if(!utParseInteger(&value, string)) {
837         return false;
838     }
839     utSetInteger(dest, value, width);
840     return true;
841 }
842 
843 /*--------------------------------------------------------------------------------------------------
844   Parse a character value.
845 --------------------------------------------------------------------------------------------------*/
readChar(uint8 * dest,char * value)846 static bool readChar(
847     uint8 *dest,
848     char *value)
849 {
850     char *end = value + strlen(value) - 1;
851 
852     if(*value++ != '\'') {
853         return false;
854     }
855     if(*--end != '\'') {
856         return false;
857     }
858     *end = '\0';
859     if(*value == '\\') {
860         return readInteger(dest, value, 1, true);
861     }
862     *(char *)dest = *value;
863     return true;
864 }
865 
866 /*--------------------------------------------------------------------------------------------------
867   Parse a floating point number.
868 --------------------------------------------------------------------------------------------------*/
readFloat(uint8 * dest,char * value,bool isFloat)869 static bool readFloat(
870     uint8 *dest,
871     char *value,
872     bool isFloat)
873 {
874     double floatVal;
875     char *endPtr;
876 
877     floatVal = strtod(value, &endPtr);
878     if(endPtr == NULL || *endPtr != '\0') {
879         return false;
880     }
881     if(isFloat) {
882         *(float *)dest = (float)floatVal;
883     } else {
884         *(double *)dest = floatVal;
885     }
886     return true;
887 }
888 
889 /*--------------------------------------------------------------------------------------------------
890   Read a 2-character hex byte.
891 --------------------------------------------------------------------------------------------------*/
readHex(uint8 * dest,char * value)892 static bool readHex(
893     uint8 *dest,
894     char *value)
895 {
896     char c = toupper(*value++);
897     uint8 byte;
898 
899     if(c >= '0' && c <= '9') {
900         byte = c - '0';
901     } else if(c >= 'A' && c <= 'F') {
902         byte = c - 'A' + 10;
903     } else {
904         return false;
905     }
906     byte <<= 4;
907     c = toupper(*value);
908     if(c >= '0' && c <= '9') {
909         byte |= c - '0';
910     } else if(c >= 'A' && c <= 'F') {
911         byte |= c - 'A' + 10;
912     } else {
913         return false;
914     }
915     *dest = byte;
916     return true;
917 }
918 
919 /*--------------------------------------------------------------------------------------------------
920   Parse a typedef value.
921 --------------------------------------------------------------------------------------------------*/
utReadHex(uint8 * dest,char * value,uint32 size)922 bool utReadHex(
923     uint8 *dest,
924     char *value,
925     uint32 size)
926 {
927     uint32 length = 0;
928 
929     while(*value != '\0' && length <= size) {
930         if(!readHex(dest, value)) {
931             return false;
932         }
933         dest++;
934         value += 2;
935         length++;
936     }
937     return length == size;
938 }
939 
940 /*--------------------------------------------------------------------------------------------------
941   Convert any \ escapes into characters.
942 --------------------------------------------------------------------------------------------------*/
unmungeString(char * string,char ** end)943 static char *unmungeString(
944     char *string,
945     char **end)
946 {
947     char *buffer = utMakeString(strlen(string) + 1);
948     char *p = buffer;
949     char value;
950 
951     *end = NULL;
952     if(*string++ != '"') {
953         return NULL;
954     }
955     while(*string != '\0' && *string != '"') {
956         if(*string == '\\') {
957             string++;
958             if(*string >= '0' && *string <= '9') {
959                 value = 0;
960                 do {
961                     value = 10*value + *string++ - '0';
962                 } while(*string >= '0' && *string <= '9');
963                 *p++ = value;
964             } else {
965                 *p++ = *string++;
966             }
967         } else {
968             if(!isprint(*string)) {
969                 return NULL;
970             }
971             *p++ = *string++;
972         }
973     }
974     *p = '\0';
975     if(*string == '\0') {
976         return NULL;
977     }
978     *end = string + 1;
979     return buffer;
980 }
981 
982 /*--------------------------------------------------------------------------------------------------
983   Parse a symbol.  Valid values are a string, or 0xffffffff
984 --------------------------------------------------------------------------------------------------*/
readSym(uint8 * dest,char * value)985 static bool readSym(
986     uint8 *dest,
987     char *value)
988 {
989     utSym *symPtr = (utSym *)dest;
990     char *end;
991     char *string;
992 
993     if(*value == '"') {
994         string = unmungeString(value, &end);
995         if(end == NULL) {
996             return false;
997         }
998         *symPtr = utSymCreate(string);
999     } else {
1000         readInteger(dest, value, sizeof(utSym), true);
1001         if(*symPtr != utSymNull) {
1002             return false;
1003         }
1004     }
1005     return true;
1006 }
1007 
1008 /*--------------------------------------------------------------------------------------------------
1009   Parse an enumerated type, and write it to the destination.
1010 --------------------------------------------------------------------------------------------------*/
readEnum(utEnum theEnum,uint8 * dest,char * value)1011 static bool readEnum(
1012     utEnum theEnum,
1013     uint8 *dest,
1014     char *value)
1015 {
1016     utEntry entry;
1017 
1018     entry = findEntryFromName(theEnum, value);
1019     if(entry == NULL) {
1020         reportError("Unknown enumerated type value %s", value);
1021         entry = utEnumGetFirstEntry(theEnum);
1022     }
1023     *(uint32 *)dest = utEntryGetValue(entry);
1024     return true;
1025 }
1026 
1027 /*--------------------------------------------------------------------------------------------------
1028   Parse a value of a given type, and write it to the destination.
1029 --------------------------------------------------------------------------------------------------*/
parseSpecificValue(uint8 * dest,char * value,utFieldType type,uint32 size,uint64 objectNumber)1030 static bool parseSpecificValue(
1031     uint8 *dest,
1032     char *value,
1033     utFieldType type,
1034     uint32 size,
1035     uint64 objectNumber)
1036 {
1037     switch(type) {
1038     case UT_BIT:
1039         if(!strcasecmp(value, "true")) {
1040             *(uint8 *)dest |= 1 << (objectNumber & 7);
1041         } else if(!strcasecmp(value, "false")) {
1042             *(uint8 *)dest &= ~(1 << (objectNumber & 7));
1043         } else {
1044             return false;
1045         }
1046         return true;
1047     case UT_BOOL:
1048         if(!strcasecmp(value, "true")) {
1049             *(bool *)dest = true;
1050         } else if(!strcasecmp(value, "false")) {
1051             *(bool *)dest = false;
1052         } else {
1053             return readInteger(dest, value, 1, true);
1054         }
1055         return true;
1056     case UT_INT:
1057         return readInteger(dest, value, (uint8)size, false);
1058     case UT_UINT:
1059         return readInteger(dest, value, (uint8)size, true);
1060     case UT_CHAR:
1061         return readChar(dest, value);
1062     case UT_FLOAT:
1063         return readFloat(dest, value, true);
1064     case UT_DOUBLE:
1065         return readFloat(dest, value, true);
1066     case UT_POINTER:
1067         return readInteger(dest, value, (uint8)size, true);
1068     case UT_TYPEDEF:
1069         return utReadHex(dest, value, size);
1070     case UT_SYM:
1071         return readSym(dest, value);
1072     default:
1073         utExit("Invalid type");
1074     }
1075     return false; /* Dummy return */
1076 }
1077 
1078 /*--------------------------------------------------------------------------------------------------
1079   Parse a value of a given type, and write it to the destination.
1080 --------------------------------------------------------------------------------------------------*/
parseValue(uint8 * dest,char * value,utField field,uint64 objectNumber)1081 static bool parseValue(
1082     uint8 *dest,
1083     char *value,
1084     utField field,
1085     uint64 objectNumber)
1086 {
1087     utClass theClass = utFieldGetClass(field);
1088     utModule module = utClassGetModule(theClass);
1089     utUnion theUnion;
1090     utUnioncase unioncase;
1091     utEnum theEnum;
1092 
1093     if(utFieldGetType(field) == UT_ENUM) {
1094         theEnum = findEnum(module, utFieldGetDestName(field));
1095         return readEnum(theEnum, dest, value);
1096     }
1097     if(utFieldGetType(field) != UT_UNION) {
1098         return parseSpecificValue(dest, value, utFieldGetType(field), utFieldGetSize(field), objectNumber);
1099     }
1100     theUnion = utFieldGetUnion(field);
1101     unioncase = findUnioncase(theUnion, objectNumber);
1102     if(unioncase == NULL) {
1103         return utReadHex(dest, value, utFieldGetSize(field));
1104     }
1105     if(utUnioncaseGetType(unioncase) == UT_ENUM) {
1106         theEnum = findEnum(module, utFieldGetDestName(field));
1107         return readEnum(theEnum, dest, value);
1108     }
1109     return parseSpecificValue(dest, value, utUnioncaseGetType(unioncase), utUnioncaseGetSize(unioncase), objectNumber);
1110 }
1111 
1112 /*--------------------------------------------------------------------------------------------------
1113   Skip to the next comma or the end of a value list.  If we find a syntax error of some sort,
1114   return NULL.
1115 --------------------------------------------------------------------------------------------------*/
skipToNextComma(char * valueList)1116 static char *skipToNextComma(
1117     char *valueList)
1118 {
1119     char *end, *prevChar;
1120 
1121     if(*valueList == ',') {
1122         valueList++;
1123     }
1124     while(isspace(*valueList)) {
1125         valueList++;
1126     }
1127     if(*valueList == '"') {
1128         unmungeString(valueList, &end);
1129         if(end == NULL) {
1130             return NULL;
1131         }
1132         valueList = end;
1133     } else if(*valueList == '(') {
1134         valueList++;
1135         do {
1136             valueList = skipToNextComma(valueList);
1137             prevChar = valueList - 1;
1138             while(isspace(*prevChar)) {
1139                 prevChar--;
1140             }
1141         } while(valueList != NULL && *prevChar != ')');
1142         if(valueList == NULL) {
1143             return NULL;
1144         }
1145     }
1146     while(*valueList != '\0' && *valueList != ',') {
1147         valueList++;
1148     }
1149     if(*valueList != ',' && *valueList != '\0') {
1150         return NULL;
1151     }
1152     return valueList;
1153 }
1154 
1155 /*--------------------------------------------------------------------------------------------------
1156   Count how many values are in the list.
1157 --------------------------------------------------------------------------------------------------*/
countListValues(char * valueList)1158 static uint32 countListValues(
1159     char *valueList)
1160 {
1161     uint32 numValues = 0;
1162 
1163     while(isspace(*valueList)) {
1164         valueList++;
1165     }
1166     while(valueList != NULL && *valueList != '\0') {
1167         valueList = skipToNextComma(valueList);
1168         numValues++;
1169     }
1170     if(valueList == NULL || *valueList != '\0') {
1171         return 0; /* Invalid list */
1172     }
1173     return numValues;
1174 }
1175 
1176 /*--------------------------------------------------------------------------------------------------
1177   Return the next element in a list of array values.  Update the list pointer to the next element.
1178 --------------------------------------------------------------------------------------------------*/
parseArrayValue(char ** valueList)1179 static char *parseArrayValue(
1180     char **valueList)
1181 {
1182     char *end, *buffer, *prevChar;
1183     uint32 length;
1184     bool isArray;
1185 
1186     while(isspace(**valueList)) {
1187         (*valueList)++;
1188     }
1189     isArray = **valueList == '(';
1190     end = skipToNextComma(*valueList);
1191     if(end == NULL) {
1192         return NULL;
1193     }
1194     prevChar = end;
1195     if(isArray) {
1196         (*valueList)++;
1197         prevChar = end - 1;
1198         while(isspace(*prevChar)) {
1199             prevChar--;
1200         }
1201     }
1202     length = prevChar - *valueList;
1203     buffer = utMakeString(length + 1);
1204     strncpy(buffer, *valueList, length);
1205     buffer[length] = '\0';
1206     if(*end == ',') {
1207         end++;
1208     }
1209     *valueList = end;
1210     utLastToken = buffer;
1211     return buffer;
1212 }
1213 
1214 /*--------------------------------------------------------------------------------------------------
1215   Parse an array of values.
1216 --------------------------------------------------------------------------------------------------*/
parseArray(utField field,uint64 objectNumber,char * valueList)1217 static bool parseArray(
1218     utField field,
1219     uint64 objectNumber,
1220     char *valueList)
1221 {
1222     uint32 numValues, destSize;
1223     uint8 *dest;
1224     char *value, *string, *end;
1225 
1226     if(utFieldGetType(field) == UT_CHAR && *valueList == '"') {
1227         string = unmungeString(valueList, &end);
1228         if(end == NULL || *end != '\0') {
1229             return false;
1230         }
1231         numValues = strlen(string) + 1;
1232         dest = utFieldGetAllocValues(field)(objectNumber, numValues);
1233         strcpy((char *)dest, string);
1234         return true;
1235     }
1236     numValues = countListValues(valueList);
1237     if(!utFieldFixedSize(field)) {
1238         dest = utFieldGetAllocValues(field)(objectNumber, numValues);
1239     } else {
1240         dest = utFieldGetGetValues(field)(objectNumber, &destSize);
1241         numValues = utMin(numValues, destSize);
1242     }
1243     while(numValues-- != 0) {
1244         value = parseArrayValue(&valueList);
1245         if(!parseValue(dest, value, field, objectNumber)) {
1246             return false;
1247         }
1248         dest += utFieldGetSize(field);
1249     }
1250     if(*valueList != '\0' && !utFieldFixedSize(field)) {
1251         return false;
1252     }
1253     return true;
1254 }
1255 
1256 /*--------------------------------------------------------------------------------------------------
1257   Set a field of an object.
1258 --------------------------------------------------------------------------------------------------*/
setObjectField(utClass theClass,utField field,uint64 objectNumber,char * value)1259 static bool setObjectField(
1260     utClass theClass,
1261     utField field,
1262     uint64 objectNumber,
1263     char *value)
1264 {
1265     uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
1266     uint8 *dest;
1267     bool passed;
1268 
1269     if(objectNumber >= numUsed) {
1270         reportError("Invalid object ID");
1271         return false;
1272     }
1273     if(!utFieldArray(field)) {
1274         if(utFieldGetType(field) == UT_BIT) {
1275             dest = *(uint8 **)(utFieldGetArrayPtr(field)) + (objectNumber >> 3)*utFieldGetSize(field);
1276         } else {
1277             dest = *(uint8 **)(utFieldGetArrayPtr(field)) + objectNumber*utFieldGetSize(field);
1278         }
1279         passed = parseValue(dest, value, field, objectNumber);
1280     } else {
1281         passed = parseArray(field, objectNumber, value);
1282     }
1283     if(!passed) {
1284         reportError("Invalid value for field %s", utFieldGetName(field));
1285     }
1286     return passed;
1287 }
1288 
1289 /*--------------------------------------------------------------------------------------------------
1290   Set all the fields of an object.
1291 --------------------------------------------------------------------------------------------------*/
setObjectFields(utClass theClass,uint64 objectNumber,char * valueList,utField * fieldTranslationTable)1292 static bool setObjectFields(
1293     utClass theClass,
1294     uint64 objectNumber,
1295     char *valueList,
1296     utField *fieldTranslationTable)
1297 {
1298     utField field;
1299     uint16 numValues = countListValues(valueList);
1300     uint16 xField;
1301     char *value;
1302 
1303     if(numValues != utClassGetNumFields(theClass) - utClassGetNumHiddenFields(theClass)) {
1304         reportError("Incorrect number of values for class %s", utClassGetName(theClass));
1305         return false;
1306     }
1307     for(xField = 0; xField < numValues; xField++) {
1308         if(fieldTranslationTable == NULL) {
1309             field = utClassGetiField(theClass, xField);
1310         } else {
1311             field = fieldTranslationTable[xField];
1312         }
1313         value = parseArrayValue(&valueList);
1314         if(field != NULL && !setObjectField(theClass, field, objectNumber, value)) {
1315             return false;
1316         }
1317     }
1318     return true;
1319 }
1320 
1321 /*--------------------------------------------------------------------------------------------------
1322   Read a line of text from stdin.
1323 --------------------------------------------------------------------------------------------------*/
readLine(void)1324 static bool readLine(void)
1325 {
1326     uint32 linePosition = 0;
1327     int c;
1328 
1329     utDo {
1330         c = getc(utInputFile);
1331     } utWhile(c != '\n' && c != EOF) {
1332         if(linePosition == utLineSize) {
1333             utLineSize <<= 1;
1334             utResizeArray(utLineBuffer, utLineSize);
1335         }
1336         if(isprint(c)) {
1337             utLineBuffer[linePosition++] = c;
1338         }
1339     } utRepeat;
1340     if(c == EOF) {
1341         utAtEndOfFile = true;
1342     }
1343     if(linePosition == utLineSize) {
1344         utLineSize <<= 1;
1345         utResizeArray(utLineBuffer, utLineSize);
1346     }
1347     utLineBuffer[linePosition] = '\0';
1348     utLinePosition = 0;
1349     utLineNum++;
1350     return c != EOF;
1351 }
1352 
1353 /*--------------------------------------------------------------------------------------------------
1354   Find the length of the token.
1355 --------------------------------------------------------------------------------------------------*/
findTokenLength(void)1356 static uint32 findTokenLength(void)
1357 {
1358     uint32 linePosition = utLinePosition;
1359     char c = utLineBuffer[linePosition++];
1360 
1361     if(c == '\0') {
1362         return 0;
1363     }
1364     if(!isalnum(c) && c != '_') {
1365         return 1;
1366     }
1367     do {
1368         c = utLineBuffer[linePosition++];
1369     } while(isalnum(c) || c == '_');
1370     return linePosition - utLinePosition - 1;
1371 }
1372 
1373 /*--------------------------------------------------------------------------------------------------
1374   Just skip space.
1375 --------------------------------------------------------------------------------------------------*/
skipSpace(void)1376 void skipSpace(void)
1377 {
1378     while(isspace(utLineBuffer[utLinePosition])) {
1379         utLinePosition++;
1380     }
1381 }
1382 
1383 /*--------------------------------------------------------------------------------------------------
1384   Read a token from the line buffer.
1385 --------------------------------------------------------------------------------------------------*/
readToken(void)1386 char *readToken(void)
1387 {
1388     char *token;
1389     uint32 length;
1390 
1391     skipSpace();
1392     length = findTokenLength();
1393     token = utMakeString(length + 1);
1394     strncpy(token, utLineBuffer + utLinePosition, length);
1395     utLinePosition += length;
1396     token[length] = '\0';
1397     utLastToken = token;
1398     return token;
1399 }
1400 
1401 /*--------------------------------------------------------------------------------------------------
1402   Find the module with the given prefix.
1403 --------------------------------------------------------------------------------------------------*/
utFindModule(char * prefix)1404 utModule utFindModule(
1405     char *prefix)
1406 {
1407     utModule module;
1408     uint8 xModule;
1409 
1410     for(xModule = 0; xModule < utUsedModules; xModule++) {
1411         module = utModules + xModule;
1412         if(!strcmp(utModuleGetPrefix(module), prefix)) {
1413             return module;
1414         }
1415     }
1416     return NULL;
1417 }
1418 
1419 /*--------------------------------------------------------------------------------------------------
1420   Find the class with the given name.
1421 --------------------------------------------------------------------------------------------------*/
findClass(utModule module,char * name)1422 static utClass findClass(
1423     utModule module,
1424     char *name)
1425 {
1426     utClass theClass;
1427 
1428     utForeachModuleClass(module, theClass) {
1429         if(!strcmp(utClassGetName(theClass), name)) {
1430             return theClass;
1431         }
1432     } utEndModuleClass;
1433     return NULL;
1434 }
1435 
1436 /*--------------------------------------------------------------------------------------------------
1437   Find the field with the given name.
1438 --------------------------------------------------------------------------------------------------*/
utFindField(utClass theClass,char * name)1439 utField utFindField(
1440     utClass theClass,
1441     char *name)
1442 {
1443     utField field;
1444 
1445     utForeachClassField(theClass, field) {
1446         if(!strcmp(utFieldGetName(field), name)) {
1447             return field;
1448         }
1449     } utEndClassField;
1450     return NULL;
1451 }
1452 
1453 /*--------------------------------------------------------------------------------------------------
1454   Check that we parsed the whole line, and complain if we didn't.  Return true if the end was
1455   correct.
1456 --------------------------------------------------------------------------------------------------*/
expectEnd(void)1457 static bool expectEnd(void)
1458 {
1459     char *token = readToken();
1460 
1461     if(*token != '\0') {
1462         reportError("Extra characters at end of command.\n");
1463         return false;
1464     }
1465     return true;
1466 }
1467 
1468 /*--------------------------------------------------------------------------------------------------
1469   Parse a module name.
1470 --------------------------------------------------------------------------------------------------*/
parseModule(bool required,bool * error)1471 static utModule parseModule(
1472     bool required,
1473     bool *error)
1474 {
1475     utModule module;
1476     char *token = readToken();
1477 
1478     *error = false;
1479     if(*token == '\0') {
1480         if(required) {
1481             reportError("Expecting a module name.");
1482             *error = true;
1483         }
1484         return NULL;
1485     }
1486     module = utFindModule(token);
1487     if(module == NULL) {
1488         reportError("Invalid module name.");
1489         *error = true;
1490     }
1491     return module;
1492 }
1493 
1494 /*--------------------------------------------------------------------------------------------------
1495   Parse a class name.
1496 --------------------------------------------------------------------------------------------------*/
parseClass(utModule module,bool required,bool * error)1497 static utClass parseClass(
1498     utModule module,
1499     bool required,
1500     bool *error)
1501 {
1502     utClass theClass;
1503     char *token = readToken();
1504 
1505     *error = false;
1506     if(*token == '\0') {
1507         if(required) {
1508             reportError("Expecting a class name.");
1509             *error = true;
1510         }
1511         return NULL;
1512     }
1513     theClass = findClass(module, token);
1514     if(theClass == NULL) {
1515         reportError("Invalid class name.");
1516         *error = true;
1517     }
1518     return theClass;
1519 }
1520 
1521 /*--------------------------------------------------------------------------------------------------
1522   Parse an object number.
1523 --------------------------------------------------------------------------------------------------*/
parseObjectNumber(utClass theClass,bool required,bool * error)1524 static uint64 parseObjectNumber(
1525     utClass theClass,
1526     bool required,
1527     bool *error)
1528 {
1529     uint64 numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
1530     char *token = readToken();
1531     uint64 objectNumber;
1532 
1533     *error = false;
1534     if(*token == '\0') {
1535         if(required) {
1536             reportError("Expecting an object number");
1537             *error = true;
1538         }
1539         return UINT64_MAX;
1540     }
1541     if(!readInteger((uint8 *)&objectNumber, token, 8, true)) {
1542         reportError("Invalid number");
1543         *error = true;
1544         return UINT64_MAX;
1545     }
1546     if(objectNumber >= numUsed) {
1547         reportError("Object number too large");
1548         *error = true;
1549         return UINT64_MAX;
1550     }
1551     return objectNumber;
1552 }
1553 
1554 /*--------------------------------------------------------------------------------------------------
1555   Process a list command.
1556 --------------------------------------------------------------------------------------------------*/
processListCommand(void)1557 static void processListCommand(void)
1558 {
1559     bool error;
1560     utModule module = parseModule(false, &error);
1561     utClass theClass;
1562 
1563     if(error) {
1564         return;
1565     }
1566     if(module == NULL) {
1567         listModules();
1568         return;
1569     }
1570     theClass = parseClass(module, false, &error);
1571     if(error) {
1572         return;
1573     }
1574     if(theClass == NULL) {
1575         listClasses(module);
1576         return;
1577     }
1578     if(!expectEnd()) {
1579         return;
1580     }
1581     listFields(theClass);
1582 }
1583 
1584 /*--------------------------------------------------------------------------------------------------
1585   Process a show command.
1586 --------------------------------------------------------------------------------------------------*/
processShowCommand(void)1587 static void processShowCommand(void)
1588 {
1589     bool error;
1590     utModule module = parseModule(true, &error);
1591     utClass theClass;
1592     uint64 objectNumber;
1593 
1594     if(error) {
1595         return;
1596     }
1597     theClass = parseClass(module, true, &error);
1598     if(error) {
1599         return;
1600     }
1601     objectNumber = parseObjectNumber(theClass, false, &error);
1602     if(error) {
1603         return;
1604     }
1605     if(objectNumber == UINT64_MAX) {
1606         showClass(module, theClass);
1607         return;
1608     }
1609     if(!expectEnd()) {
1610         return;
1611     }
1612     showObject(theClass, objectNumber);
1613 }
1614 
1615 /*--------------------------------------------------------------------------------------------------
1616   Process a set command.
1617         "set <module> <class> <object number> = comma separated values – \n"
1618         "    set all fields of an object\n"
1619         "set <module> <class> <object number> <field> = <value> – set a field value\n"
1620 --------------------------------------------------------------------------------------------------*/
processSetCommand(void)1621 static void processSetCommand(void)
1622 {
1623     bool error;
1624     utModule module = parseModule(true, &error);
1625     utClass theClass;
1626     utField field;
1627     uint64 objectNumber;
1628     char *token;
1629 
1630     if(error) {
1631         return;
1632     }
1633     theClass = parseClass(module, true, &error);
1634     if(error) {
1635         return;
1636     }
1637     objectNumber = parseObjectNumber(theClass, true, &error);
1638     if(error) {
1639         return;
1640     }
1641     token = readToken();
1642     if(!strcmp(token, "=")) {
1643         skipSpace();
1644         setObjectFields(theClass, objectNumber, utLineBuffer + utLinePosition, NULL);
1645         return;
1646     }
1647     field = utFindField(theClass, token);
1648     if(field == NULL) {
1649         reportError("Field not found");
1650         return;
1651     }
1652     token = readToken();
1653     if(strcmp(token, "=")) {
1654         reportError("Expected '='");
1655         return;
1656     }
1657     skipSpace();
1658     setObjectField(theClass, field, objectNumber, utLineBuffer + utLinePosition);
1659 }
1660 
1661 /*--------------------------------------------------------------------------------------------------
1662   Process a create command.
1663 --------------------------------------------------------------------------------------------------*/
processCreateCommand(void)1664 static void processCreateCommand(void)
1665 {
1666     bool error;
1667     utModule module = parseModule(true, &error);
1668     utClass theClass;
1669 
1670     if(error) {
1671         return;
1672     }
1673     theClass = parseClass(module, true, &error);
1674     if(error) {
1675         return;
1676     }
1677     if(utClassGetConstructor(theClass) == NULL) {
1678         reportError("This is a class extension.  Call the base class constructor instead");
1679         return;
1680     }
1681     if(!expectEnd()) {
1682         return;
1683     }
1684     fprintf(utOutputFile, "New %s object 0x%llx\n", utClassGetName(theClass), utClassGetConstructor(theClass)());
1685 }
1686 
1687 /*--------------------------------------------------------------------------------------------------
1688   Destroy an object.
1689 --------------------------------------------------------------------------------------------------*/
processDestroyCommand(void)1690 static void processDestroyCommand(void)
1691 {
1692     bool error;
1693     utModule module = parseModule(true, &error);
1694     utClass theClass;
1695     uint64 objectNumber;
1696 
1697     if(error) {
1698         return;
1699     }
1700     theClass = parseClass(module, true, &error);
1701     if(error) {
1702         return;
1703     }
1704     objectNumber = parseObjectNumber(theClass, true, &error);
1705     if(error) {
1706         return;
1707     }
1708     if(!expectEnd()) {
1709         return;
1710     }
1711     if(utClassGetDestructor(theClass) == NULL) {
1712         reportError("This class has no destructor, either because it is an extension, or "
1713             "because it is declared 'create_only'");
1714         return;
1715     }
1716     utClassGetDestructor(theClass)(objectNumber);
1717     fprintf(utOutputFile, "Destroyed %s object 0x%llx\n", utClassGetName(theClass), objectNumber);
1718 }
1719 
1720 /*--------------------------------------------------------------------------------------------------
1721   Process a show_hidden command.
1722 --------------------------------------------------------------------------------------------------*/
processShowHiddenCommand(void)1723 static void processShowHiddenCommand(void)
1724 {
1725     char *token = readToken();
1726 
1727     if(!expectEnd()) {
1728         return;
1729     }
1730     if(!strcasecmp(token, "true")) {
1731         utShowHidden = true;
1732     } else if(!strcasecmp(token, "false")) {
1733         utShowHidden = false;
1734     } else {
1735         reportError("Expecting 'true' or 'false'\n");
1736     }
1737 }
1738 
1739 /*--------------------------------------------------------------------------------------------------
1740   Process a load text command.
1741 --------------------------------------------------------------------------------------------------*/
processLoadTextCommand(void)1742 static void processLoadTextCommand(void)
1743 {
1744     char *fileName = readToken();
1745     FILE *file;
1746 
1747     if(!expectEnd()) {
1748         return;
1749     }
1750     file = fopen(fileName, "r");
1751     if(file == NULL) {
1752         reportError("Could not open file %s for reading", fileName);
1753         return;
1754     }
1755     utLoadTextDatabase(file);
1756     fclose(file);
1757     utMemCheck();
1758 }
1759 
1760 /*--------------------------------------------------------------------------------------------------
1761   Process a write command.
1762 --------------------------------------------------------------------------------------------------*/
processSaveTextCommand(void)1763 static void processSaveTextCommand(void)
1764 {
1765     char *fileName = readToken();
1766     FILE *file;
1767 
1768     if(!expectEnd()) {
1769         return;
1770     }
1771     file = fopen(fileName, "w");
1772     if(file == NULL) {
1773         reportError("Could not open file %s for writing", fileName);
1774         return;
1775     }
1776     utSaveTextDatabase(file);
1777     fclose(file);
1778 }
1779 
1780 /*--------------------------------------------------------------------------------------------------
1781   Process a load binary command.
1782 --------------------------------------------------------------------------------------------------*/
processLoadBinaryCommand(void)1783 static void processLoadBinaryCommand(void)
1784 {
1785     char *fileName = readToken();
1786     FILE *file;
1787 
1788     if(!expectEnd()) {
1789         return;
1790     }
1791     file = fopen(fileName, "rb");
1792     if(file == NULL) {
1793         reportError("Could not open file %s for reading", fileName);
1794         return;
1795     }
1796     utLoadBinaryDatabase(file);
1797     fclose(file);
1798 }
1799 
1800 /*--------------------------------------------------------------------------------------------------
1801   Process a save binary command.
1802 --------------------------------------------------------------------------------------------------*/
processSaveBinaryCommand(void)1803 static void processSaveBinaryCommand(void)
1804 {
1805     char *fileName = readToken();
1806     FILE *file;
1807 
1808     if(!expectEnd()) {
1809         return;
1810     }
1811     file = fopen(fileName, "wb");
1812     if(file == NULL) {
1813         reportError("Could not open file %s for writing", fileName);
1814         return;
1815     }
1816     utSaveBinaryDatabase(file);
1817     fclose(file);
1818 }
1819 
1820 /*--------------------------------------------------------------------------------------------------
1821   Process the command in the line buffer.
1822 --------------------------------------------------------------------------------------------------*/
processCommand(void)1823 static bool processCommand(void)
1824 {
1825     char *token = readToken();
1826 
1827     if(*token == '\0') {
1828         return true; /* Empty line */
1829     }
1830     if(!strcmp(token, "create")) {
1831         processCreateCommand();
1832     } else if(!strcmp(token, "compact")) {
1833         if(utPersistenceInitialized) {
1834             utCompactDatabase();
1835         } else {
1836             reportError("Compact is only for persitent databases.");
1837         }
1838     } else if(!strcmp(token, "destroy")) {
1839         processDestroyCommand();
1840     } else if(!strcmp(token, "help")) {
1841         printHelp();
1842         return true;
1843     } else if(!strcmp(token, "list")) {
1844         processListCommand();
1845     } else if(!strcmp(token, "quit")) {
1846         return false;
1847     } else if(!strcmp(token, "set")) {
1848         processSetCommand();
1849     } else if(!strcmp(token, "show")) {
1850         processShowCommand();
1851     } else if(!strcmp(token, "show_hidden")) {
1852         processShowHiddenCommand();
1853     } else if(!strcmp(token, "load_text")) {
1854         processLoadTextCommand();
1855     } else if(!strcmp(token, "save_text")) {
1856         processSaveTextCommand();
1857     } else if(!strcmp(token, "load_binary")) {
1858         processLoadBinaryCommand();
1859     } else if(!strcmp(token, "save_binary")) {
1860         processSaveBinaryCommand();
1861     } else {
1862         fprintf(utOutputFile, "Invalid command.  Type 'help' for a list of commands\n");
1863     }
1864     return true;
1865 }
1866 
1867 /*--------------------------------------------------------------------------------------------------
1868   Initialize the database manager.
1869 --------------------------------------------------------------------------------------------------*/
utDatabaseManagerStart(void)1870 void utDatabaseManagerStart(void)
1871 {
1872     utLineSize = 42;
1873     utLineBuffer = utNewA(char, utLineSize);
1874     utOutputFile = stdout;
1875     utInputFile = stdin;
1876 }
1877 
1878 /*--------------------------------------------------------------------------------------------------
1879   Clean up after the database manager.
1880 --------------------------------------------------------------------------------------------------*/
utDatabaseManagerStop(void)1881 void utDatabaseManagerStop(void)
1882 {
1883     utFree(utLineBuffer);
1884 }
1885 
1886 /*--------------------------------------------------------------------------------------------------
1887   Interpret commands to manage the database.
1888 --------------------------------------------------------------------------------------------------*/
utManager(void)1889 void utManager(void)
1890 {
1891     utAtEndOfFile = false;
1892     fprintf(utOutputFile, "For help, enter the 'help' command\n");
1893     do {
1894         fprintf(utOutputFile, "> ");
1895         readLine();
1896     } while(processCommand());
1897 }
1898 
1899 /*--------------------------------------------------------------------------------------------------
1900   Skip blank lines.
1901 --------------------------------------------------------------------------------------------------*/
skipBlankLines(void)1902 static void skipBlankLines(void)
1903 {
1904     while(readLine()) {
1905         if(utLineBuffer[0] != '\0') {
1906             return;
1907         }
1908     }
1909 }
1910 
1911 /*--------------------------------------------------------------------------------------------------
1912   Read a module header.
1913 --------------------------------------------------------------------------------------------------*/
readModuleHeader(void)1914 static utModule readModuleHeader(void)
1915 {
1916     utModule module;
1917     char *token;
1918 
1919     token = readToken();
1920     if(strcmp(token, "module")) {
1921         if(!utAtEndOfFile) {
1922             reportError("Expected 'module' keyword");
1923         }
1924         return NULL;
1925     }
1926     token = readToken();
1927     if(token == NULL) {
1928         reportError("Expected module name");
1929         return NULL;
1930     }
1931     module = utFindModule(token);
1932     if(module == NULL) {
1933         reportError("Module %s not found", token);
1934         return NULL;
1935     }
1936     if(!expectEnd()) {
1937         return NULL;
1938     }
1939     return module;
1940 }
1941 
1942 /*--------------------------------------------------------------------------------------------------
1943   Reallocate object fields for the class.
1944 --------------------------------------------------------------------------------------------------*/
reallocObjects(utClass theClass,uint64 numUsed)1945 static void reallocObjects(
1946     utClass theClass,
1947     uint64 numUsed)
1948 {
1949     utField field;
1950 
1951     utSetInteger(utClassGetNumAllocatedPtr(theClass), numUsed, utClassGetReferenceSize(theClass));
1952     utForeachClassField(theClass, field) {
1953         if(!utFieldArray(field)) {
1954             *(uint8 **)(utFieldGetArrayPtr(field)) = utRealloc(*(uint8 **)utFieldGetArrayPtr(field), numUsed,
1955                 utFieldGetSize(field));
1956         } else if(utFieldFixedSize(field)) {
1957             *(uint8 **)(utFieldGetArrayPtr(field)) = utRealloc(*(uint8 **)utFieldGetArrayPtr(field),
1958                 numUsed*utFieldGetLength(field), utFieldGetSize(field));
1959         }
1960     } utEndClassField;
1961 }
1962 
1963 /*--------------------------------------------------------------------------------------------------
1964   Read a class header, and allocate space for the objects.
1965 --------------------------------------------------------------------------------------------------*/
readClassHeader(utModule module)1966 static utClass readClassHeader(
1967     utModule module)
1968 {
1969     utClass theClass;
1970     char *token;
1971     uint64 numUsed;
1972 
1973     skipBlankLines();
1974     token = readToken();
1975     if(strcmp(token, "class")) {
1976         utLinePosition = 0;
1977         return NULL;
1978     }
1979     token = readToken();
1980     if(token == NULL) {
1981         reportError("Expected class name");
1982         return NULL;
1983     }
1984     theClass = findClass(module, token);
1985     if(theClass == NULL) {
1986         reportError("Class %s not found", token);
1987         return NULL;
1988     }
1989     token = readToken();
1990     if(!readInteger(utClassGetNumUsedPtr(theClass), token, utClassGetReferenceSize(theClass), true)) {
1991         reportError("Expected number of objects");
1992         return NULL;
1993     }
1994     if(!expectEnd()) {
1995         return NULL;
1996     }
1997     numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
1998     numUsed = utMax(2, numUsed);
1999     reallocObjects(theClass, numUsed);
2000     return theClass;
2001 }
2002 
2003 /*--------------------------------------------------------------------------------------------------
2004   Read in column headers.  Build a translation table from column number to field.  The caller
2005   will have to free utFieldTranslationTable.
2006 --------------------------------------------------------------------------------------------------*/
readColumnHeaders(utClass theClass)2007 static bool readColumnHeaders(
2008     utClass theClass)
2009 {
2010     utField field;
2011     char *valueList, *value;
2012     uint32 numFields, xField;
2013 
2014     if(!readLine()) {
2015         reportError("Expected column headers");
2016         return false;
2017     }
2018     valueList = utLineBuffer;
2019     numFields = countListValues(valueList) - 1;
2020     utFieldTranslationTable = calloc(numFields, sizeof(utField));
2021     value = parseArrayValue(&valueList);
2022     if(value == NULL || strcmp(value, "ObjectNumber")) {
2023         reportError("Expected 'objectNumber' to be the first field");
2024         return false;
2025     }
2026     for(xField = 0; xField < numFields; xField++) {
2027         value = parseArrayValue(&valueList);
2028         if(value == NULL) {
2029             return false;
2030         }
2031         field = utFindField(theClass, value);
2032         if(field == NULL) {
2033             reportError("Unknown field %s in class %s -- dropping", value, utClassGetName(theClass));
2034         }
2035         utFieldTranslationTable[xField] = field;
2036     }
2037     utNumFields = numFields;
2038     return true;
2039 }
2040 
2041 /*--------------------------------------------------------------------------------------------------
2042   Read in column types.  If it is incompatible with the field, print a warning, and null out the
2043   entry in the translation table.
2044 --------------------------------------------------------------------------------------------------*/
readColumnTypes(utClass theClass)2045 static bool readColumnTypes(
2046     utClass theClass)
2047 {
2048     char *valueList, *value;
2049     uint32 numFields, xField;
2050 
2051     if(!readLine()) {
2052         reportError("Expected column types");
2053         return false;
2054     }
2055     valueList = utLineBuffer;
2056     numFields = countListValues(valueList) - 1;
2057     if(numFields != utNumFields) {
2058         reportError("Column number mismatch");
2059         return false;
2060     }
2061     value = parseArrayValue(&valueList);
2062     if(value == NULL) {
2063         reportError("Expected class reference type to be the first field");
2064         return false;
2065     }
2066     for(xField = 0; xField < numFields; xField++) {
2067         value = parseArrayValue(&valueList);
2068         if(value == NULL) {
2069             return false;
2070         }
2071     }
2072     return true;
2073 }
2074 
2075 /*--------------------------------------------------------------------------------------------------
2076   Add the objects between the start and stop to the free list.
2077 --------------------------------------------------------------------------------------------------*/
addObjectsToFreeList(utModule module,utClass theClass,uint32 start,uint32 stop)2078 static void addObjectsToFreeList(
2079     utModule module,
2080     utClass theClass,
2081     uint32 start,
2082     uint32 stop)
2083 {
2084     utField field = utClassGetNextFreeField(theClass);
2085     uint64 firstFree = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass));
2086     uint64 xObject;
2087     uint8 *dest;
2088 
2089     for(xObject = start + 1; xObject < stop; xObject++) {
2090         firstFree = utFindIntValue(utClassGetFirstFreePtr(theClass), utClassGetReferenceSize(theClass));
2091         dest = *(uint8 **)(utFieldGetArrayPtr(field)) + xObject*utFieldGetSize(field);
2092         utSetInteger(dest, firstFree, utClassGetReferenceSize(theClass));
2093         utSetInteger(utClassGetFirstFreePtr(theClass), xObject, utClassGetReferenceSize(theClass));
2094     }
2095 }
2096 
2097 /*--------------------------------------------------------------------------------------------------
2098   Read in a table of objects.  A blank line ends the table.
2099 --------------------------------------------------------------------------------------------------*/
readClassTable(utModule module,utClass theClass)2100 static bool readClassTable(
2101     utModule module,
2102     utClass theClass)
2103 {
2104     char *valueList;
2105     uint32 numValues;
2106     uint64 objectNumber, lastObjectNumber = 0;
2107     uint64 numUsed;
2108     bool error;
2109 
2110     utDo {
2111         if(!readLine()) {
2112             return false;
2113         }
2114         skipSpace();
2115     } utWhile(utLineBuffer[utLinePosition] != '\0') {
2116         objectNumber = parseObjectNumber(theClass, true, &error);
2117         if(error) {
2118             return false;
2119         }
2120         if(strcmp(readToken(), ",")) {
2121             reportError("Expecting a ','");
2122         }
2123         valueList = utLineBuffer + utLinePosition;
2124         numValues = countListValues(valueList);
2125         if(numValues != utNumFields) {
2126             reportError("Mismatched number of fields");
2127             return false;
2128         }
2129         if(!setObjectFields(theClass, objectNumber, valueList, utFieldTranslationTable)) {
2130             return false;
2131         }
2132         if(utClassGetDestructor(theClass) != NULL) {
2133             addObjectsToFreeList(module, theClass, lastObjectNumber, objectNumber);
2134         }
2135         lastObjectNumber = objectNumber;
2136     } utRepeat;
2137     if(utClassGetDestructor(theClass) != NULL) {
2138         numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
2139         addObjectsToFreeList(module, theClass, lastObjectNumber, numUsed);
2140     }
2141     return true;
2142 }
2143 
2144 /*--------------------------------------------------------------------------------------------------
2145   Read in the module.
2146 --------------------------------------------------------------------------------------------------*/
readModule(utModule module)2147 static bool readModule(
2148     utModule module)
2149 {
2150     utClass theClass;
2151     bool passed;
2152 
2153     utDo {
2154         theClass = readClassHeader(module);
2155     } utWhile(theClass != NULL) {
2156         if(!readColumnHeaders(theClass)) {
2157             return false;
2158         }
2159         if(!readColumnTypes(theClass)) {
2160             return false;
2161         }
2162         passed = readClassTable(module, theClass);
2163         free(utFieldTranslationTable);
2164         if(!passed) {
2165             return false;
2166         }
2167     } utRepeat;
2168     return true;
2169 }
2170 
2171 /*--------------------------------------------------------------------------------------------------
2172   Read in the database in ASCII.
2173 --------------------------------------------------------------------------------------------------*/
utLoadTextDatabase(FILE * file)2174 void utLoadTextDatabase(
2175     FILE *file)
2176 {
2177     char *fileName = NULL;
2178     utModule module;
2179     FILE *savedFile = utInputFile;
2180     bool useDefault = file == NULL;
2181 
2182     if(useDefault) {
2183         fileName = utSprintf("%s%cdatabase", utDatabaseDirectory, UTDIRSEP);
2184         file = fopen(fileName, "r");
2185     }
2186     if(file == NULL) {
2187         utExit("Could not read from %s", fileName);
2188     }
2189     utResetDatabase();
2190     utAtEndOfFile = false;
2191     utLineNum = 0;
2192     utInputFile = file;
2193     skipBlankLines();
2194     do {
2195         module = readModuleHeader();
2196     } while(module != NULL && readModule(module));
2197     utInputFile = savedFile;
2198     if(useDefault) {
2199         fclose(file);
2200     }
2201 }
2202 
2203 /*--------------------------------------------------------------------------------------------------
2204   Write out the database in ASCII.  Don't write the utility module.
2205 --------------------------------------------------------------------------------------------------*/
utSaveTextDatabase(FILE * file)2206 void utSaveTextDatabase(
2207     FILE *file)
2208 {
2209     char *fileName = NULL;
2210     utModule module;
2211     utClass theClass;
2212     FILE *savedFile = utOutputFile;
2213     bool useDefault = file == NULL;
2214 
2215     if(useDefault) {
2216         fileName = utSprintf("%s%cdatabase", utDatabaseDirectory, UTDIRSEP);
2217         file = fopen(fileName, "w");
2218     }
2219     if(file == NULL) {
2220         utExit("Could not write to %s", fileName);
2221     }
2222     utOutputFile = file;
2223     utForeachModule(module) {
2224         if(strcmp(utModuleGetPrefix(module), "ut") && utModuleInitialized(module) && utModulePersistent(module)) {
2225             fprintf(utOutputFile, "module %s\n\n", utModuleGetPrefix(module));
2226             utForeachModuleClass(module, theClass) {
2227                 showClass(module, theClass);
2228                 fprintf(utOutputFile, "\n");
2229             } utEndModuleClass;
2230         }
2231     } utEndModule;
2232     if(useDefault) {
2233         fclose(file);
2234     }
2235     utOutputFile = savedFile;
2236 }
2237 
2238 /*--------------------------------------------------------------------------------------------------
2239   Write out the fields of an object.
2240 --------------------------------------------------------------------------------------------------*/
utDatabaseShowObject(char * modulePrefix,char * className,uint64 objectNumber)2241 void utDatabaseShowObject(
2242     char *modulePrefix,
2243     char *className,
2244     uint64 objectNumber)
2245 {
2246     utModule module = utFindModule(modulePrefix);
2247     utClass theClass;
2248     uint64 numUsed;
2249     FILE *savedFile = utOutputFile;
2250 
2251     if(module == NULL) {
2252         reportError("Invalid module %s\n", modulePrefix);
2253         return;
2254     }
2255     theClass = findClass(module, className);
2256     if(theClass == NULL) {
2257         reportError("Invalid class %s\n", className);
2258         return;
2259     }
2260     utOutputFile = stdout;
2261     showObject(theClass, objectNumber);
2262     utOutputFile = savedFile;
2263     numUsed = utFindIntValue(utClassGetNumUsedPtr(theClass), utClassGetReferenceSize(theClass));
2264     if(objectNumber >= numUsed) {
2265         reportError("Object number too large");
2266         return;
2267     }
2268 }
2269