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