/* getObj - get an object out of a file in the same format * it was put into the file with (parents, commas, quotes etc.). * This was first called 'scan' and most of the local names * are still that way. The other side of this lives in the * print module. */ #include "common.h" #include "hash.h" #include "dystring.h" #include "../compiler/pfPreamble.h" static void scanField(FILE *f, void *data, struct _pf_type *type, struct hash *idHash); /* Print out a data from a single field of given type. */ static _pf_Int scanInt(FILE *f); /* Read integer or die trying */ static void *idLookup(struct hash *idHash, int id) /* Look up object in hash. */ { char buf[17]; struct _pf_object *obj; safef(buf, sizeof(buf), "%X", id); obj = hashFindVal(idHash, buf); if (obj == NULL) errAbort("no such ID"); obj->_pf_refCount += 1; return obj; } static void idAdd(struct hash *idHash, void *v) /* Add to object hash */ { char buf[17]; safef(buf, sizeof(buf), "%X", idHash->elCount+1); hashAdd(idHash, buf, v); } static void *idUse(struct hash *idHash, FILE *f) /* This will look at next character. If it's a parenthesis * it will eat it and return NULL. If it's a $ then it * will read the next number and then return the associated * object. */ { int c = fgetc(f); if (c == '$') { int id = scanInt(f); return idLookup(idHash, id); } else if (c != '(') errAbort("Expecting parenthesis"); return NULL; } _pf_Int scanInt(FILE *f) /* Read integer or die trying */ { _pf_Int i; if (fscanf(f, "%d", &i) != 1) errAbort("Not an int"); return i; } _pf_Long scanLong(FILE *f) /* Read integer or die trying */ { _pf_Long i; if (fscanf(f, "%lld", &i) != 1) errAbort("Not a long"); return i; } _pf_Double scanDouble(FILE *f) /* Read double or die trying */ { _pf_Double i; if (fscanf(f, "%lf", &i) != 1) errAbort("Not a double"); return i; } _pf_Char scanChar(FILE *f) /* Read char or die trying */ { _pf_Char i; if (fscanf(f, "%c", &i) != 1) errAbort("Not a char"); return i; } static void scanDyString(FILE *f, struct dyString *dy) /* Scan between quotes into dy. */ { boolean isEscaped = FALSE; int c = fgetc(f); if (c != '\'') errAbort("Not a string"); for (;;) { c = fgetc(f); if (c == EOF) break; if (isEscaped) { isEscaped = FALSE; switch (c) { case 'n': c = '\n'; break; } dyStringAppendC(dy, c); } else { if (c == '\\') isEscaped = TRUE; else if (c == '\'') break; else dyStringAppendC(dy, c); } } } static struct _pf_string *scanString(FILE *f) /* Read quoted string from file. */ { struct dyString *dy = dyStringNew(0); struct _pf_string *s; scanDyString(f, dy); s = _pf_string_from_const(dy->string); dyStringFree(&dy); return s; } static boolean gotNil(FILE *f) /* Return TRUE if next characters are 'nil' * and eat the nil. */ { int c = getc(f); if (c == 'n') { int c1 = getc(f); int c2 = getc(f); if (c1 != 'i' || c2 != 'l') errAbort("expecting nil"); return TRUE; } else { ungetc(c, f); return FALSE; } } static struct _pf_object *scanClass(FILE *f, struct _pf_type *type, struct hash *idHash) /* Read in a class. */ { if (gotNil(f)) return NULL; else { struct _pf_base *base = type->base; struct _pf_object *obj = idUse(idHash, f); if (obj == NULL) { int c; struct _pf_type *field; char *s; obj = needMem(base->objSize); s = (char *)(obj); idAdd(idHash, obj); obj->_pf_refCount = 1; obj->_pf_cleanup = _pf_class_cleanup; obj->_pf_polyTable = base->polyTable; for (field = base->fields; field != NULL; field = field->next) { scanField(f, s+field->offset, field, idHash); if (field->next != NULL) { c = fgetc(f); if (c != ',') errAbort("Expecting comma"); } } c = fgetc(f); if (c != ')') errAbort("Expecting )"); } return obj; } } static struct _pf_array *scanArray(FILE *f, struct _pf_type *type, struct hash *idHash) /* Read in a array. */ { struct _pf_array *array = NULL; if (!gotNil(f)) { array = idUse(idHash, f); if (array == NULL) { struct _pf_type *elType = type->children; struct _pf_base *elBase = elType->base; int size = 0, offset = 0; boolean needComma = FALSE; int c; array = _pf_dim_array(16, elType->typeId); idAdd(idHash, array); for (;;) { c = fgetc(f); if (c == ')') break; if (needComma) { if (c != ',') errAbort("Expecting comma"); } else { needComma = TRUE; ungetc(c, f); } if (size >= array->allocated) { int toAdd = size; if (toAdd > 64*1024) toAdd = 64*1024; array->allocated += toAdd; array->elements = needLargeMemResize(array->elements, array->allocated * array->elSize); } scanField(f, array->elements + offset, elType, idHash); offset += array->elSize; size += 1; } array->size = size; } } return array; } static struct _pf_dir *scanDir(FILE *f, struct _pf_type *type, struct hash *idHash) /* Read in a dir. */ { struct _pf_dir *dir = NULL; if (!gotNil(f)) { dir = idUse(idHash, f); if (dir == NULL) { struct dyString *dy = dyStringNew(0); struct _pf_type *elType = type->children; struct _pf_base *elBase = elType->base; boolean needComma = FALSE; int c; char toBuf[5]; void *v; dir = pfDirNew(0, elType); idAdd(idHash, dir); for (;;) { c = fgetc(f); if (c == ')') break; if (needComma) { if (c != ',') errAbort("Expecting comma"); } else { needComma = TRUE; ungetc(c, f); } dyStringClear(dy); scanDyString(f, dy); if (fread(toBuf, 1, 1, f) != 1) errAbort("Unexpected end of file"); if (memcmp(toBuf, "@", 1) != 0) errAbort("Expecting '@'"); switch (elBase->singleType) { case pf_stArray: case pf_stClass: case pf_stString: case pf_stDir: { scanField(f, &v, elType, idHash); break; } case pf_stBit: { _pf_Bit b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stByte: { _pf_Byte b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stShort: { _pf_Short b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stInt: { _pf_Int b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stLong: { _pf_Long b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stFloat: { _pf_Float b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stDouble: { _pf_Double b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stChar: { _pf_Char b; scanField(f, &b, elType, idHash); v = CloneVar(&b); break; } case pf_stVar: errAbort("Can't scan data containing var types."); break; default: internalErr(); break; } hashAdd(dir->hash, dy->string, v); } dyStringFree(&dy); } } return dir; } static void scanField(FILE *f, void *data, struct _pf_type *type, struct hash *idHash) /* Print out a data from a single field of given type. */ { switch (type->base->singleType) { case pf_stBit: { _pf_Bit *p = data; *p = scanInt(f); break; } case pf_stByte: { _pf_Byte *p = data; *p = scanInt(f); break; } case pf_stShort: { _pf_Short *p = data; *p = scanInt(f); break; } case pf_stInt: { _pf_Int *p = data; *p = scanInt(f); break; } case pf_stLong: { _pf_Long *p = data; *p = scanLong(f); break; } case pf_stFloat: { _pf_Float *p = data; *p = scanDouble(f); break; } case pf_stDouble: { _pf_Double *p = data; *p = scanDouble(f); break; } case pf_stChar: { _pf_Char *p = data; *p = scanChar(f); break; } case pf_stString: { _pf_String *p = data; *p = scanString(f); break; } case pf_stClass: { struct _pf_object **p = data; *p = scanClass(f, type, idHash); break; } case pf_stArray: { struct _pf_array **p = data; *p = scanArray(f, type, idHash); break; } case pf_stDir: { struct _pf_dir **p = data; *p = scanDir(f, type, idHash); break; } case pf_stToPt: case pf_stFlowPt: errAbort("Can't scan data containing var of (function pointer) types."); break; case pf_stVar: errAbort("Can't scan data containing var types."); break; default: internalErr(); break; } } void _pf_getObj(FILE *f, _pf_Stack *stack) /* Read in variable from file. */ { int typeId = stack[1].Var.typeId; struct _pf_type *type = _pf_type_table[typeId]; struct _pf_base *base = type->base; union _pf_varless val = stack[1].Var.val; struct hash *idHash = NULL; int c; switch (base->singleType) { case pf_stBit: c = getc(f); if (c == '0') stack[0].Bit = 0; else if (c == '1') stack[0].Bit = 1; else errAbort("Not a bit"); break; case pf_stByte: { stack[0].Byte = scanInt(f); break; } case pf_stShort: stack[0].Short = scanInt(f); break; case pf_stInt: stack[0].Int = scanInt(f); break; case pf_stLong: stack[0].Long = scanLong(f); break; case pf_stFloat: stack[0].Float = scanDouble(f); break; case pf_stDouble: stack[0].Double = scanDouble(f); break; case pf_stChar: stack[0].Char = scanChar(f); break; case pf_stString: stack[0].String = scanString(f); break; case pf_stClass: idHash = newHash(18); stack[0].Obj = scanClass(f, type, idHash); break; case pf_stArray: idHash = newHash(18); stack[0].Array = scanArray(f, type, idHash); break; case pf_stDir: idHash = newHash(18); stack[0].Dir = scanDir(f, type, idHash); break; case pf_stToPt: case pf_stFlowPt: errAbort("Can't scan data containing var of (function pointer) types."); break; case pf_stVar: errAbort("Can't scan data containing var types."); break; default: errAbort("unknown type"); break; } hashFree(&idHash); /* Clean up input. */ if (base->needsCleanup) { if (val.Obj != NULL && --val.Obj->_pf_refCount <= 0) val.Obj->_pf_cleanup(val.Obj, stack->Var.typeId); } /* Return type rest of output. */ stack[0].Var.typeId = typeId; }