static void FreeAttrValue (VStringConst routine, VAttrRec *a) { VTypeMethods *methods; switch (a->repn) { case VAttrListRepn: VDestroyAttrList (a->value); break; case VBundleRepn: VDestroyBundle (a->value); break; case VPointerRepn: case VStringRepn: break; default: if (! (methods = VRepnMethods (a->repn))) VError ("%s: %s attribute has invalid repn %d", routine, a->name, a->repn); (methods->destroy) (a->value); } }
VBoolean VWriteFile (FILE *f, VAttrList list) { DataBlock *db; VBundle b; VTypeMethods *methods; VRepnKind repn; VPointer value, ptr; VBoolean result, free_it; VList data_list; /* Write the FIL_Vista data file header, attribute list, and delimeter while queuing on data_list any binary data blocks to be written: */ long offset = 0; data_list = VListCreate (); FailTest (fprintf (f, "%s %d ", VFileHeader, VFileVersion)); if (! WriteAttrList (f, list, 1, &data_list, &offset)) { VListDestroy (data_list, VFree); return FALSE; } FailTest (fputs ("\n" VFileDelimiter, f)); fflush (f); /* Traverse data_list to write the binary data blocks: */ for (db = VListFirst (data_list); db; db = VListNext (data_list)) { repn = VGetAttrRepn (& db->posn); if (repn == VBundleRepn) { /* A typed value includes its binary data block explicitly: */ VGetAttrValue (& db->posn, NULL, VBundleRepn, & b); ptr = b->data; free_it = FALSE; } else { /* For any other representation, obtain the binary data block from its encode_data method: */ VGetAttrValue (& db->posn, NULL, repn, & value); methods = VRepnMethods (repn); ptr = (methods->encode_data) (value, db->list, db->length, & free_it); if (! ptr) goto Fail; } /* Write the binary data and free the buffer containing it if it was allocated temporarily by an encode_data method: */ if (db->length > 0) { result = fwrite (ptr, 1, db->length, f) == db->length; if (free_it) VFree (ptr); if (! result) goto Fail; } } VListDestroy (data_list, VFree); return TRUE; Fail: VWarning ("VWriteFile: Write to stream failed"); VListDestroy (data_list, VFree); return FALSE; }
static VBoolean WriteAttr (FILE *f, VAttrListPosn *posn, int indent, VList *data_list, long *offset) { int i; char *str; VRepnKind repn; VAttrList sublist; VBundle b; DataBlock *db; VTypeMethods *methods; size_t length; VPointer value; VBoolean result; VAttrListPosn subposn; /* Indent by the specified amount: */ for (i = 0; i < indent; i++) FailTest (fputc ('\t', f)); indent++; /* Output the attribute's name: */ FailTest (fprintf (f, "%s: ", VGetAttrName (posn))); /* Ouput its value: */ switch (repn = VGetAttrRepn (posn)) { case VAttrListRepn: VGetAttrValue (posn, NULL, VAttrListRepn, (VPointer) & sublist); result = WriteAttrList (f, sublist, indent, data_list, offset); break; case VBundleRepn: VGetAttrValue (posn, NULL, VBundleRepn, (VBundle) & b); if (! WriteString (f, b->type_name)) return FALSE; FailTest (fputc (' ', f)); /* If it's a typed value with binary data... */ if (b->length > 0) { /* Include "data" and "length" attributes in its attribute list: */ VPrependAttr (b->list, VLengthAttr, NULL, VLongRepn, (VLong) b->length); VPrependAttr (b->list, VDataAttr, NULL, VLongRepn, (VLong) *offset); /* Add it to the queue of binary data blocks to be written: */ *offset += b->length; db = VNew (DataBlock); db->posn = *posn; db->list = b->list; db->length = b->length; VListAppend (*data_list, db); } /* Write the typed value's attribute list: */ result = WriteAttrList (f, b->list, indent, data_list, offset); /* Remove the "data" and "length" attributes added earlier: */ if (b->length > 0) { VFirstAttr (b->list, & subposn); VDeleteAttr (& subposn); VDeleteAttr (& subposn); } break; case VStringRepn: VGetAttrValue (posn, NULL, VStringRepn, (VPointer) & str); result = WriteString (f, str); break; default: if (! (methods = VRepnMethods (repn)) || ! methods->encode_attr || ! methods->encode_data) { VWarning ("VWriteFile: " "%s attribute has unwriteable representation: %s", VGetAttrName (posn), VRepnName (repn)); return FALSE; } /* Write the type name: */ if (! WriteString (f, VRepnName (repn))) return FALSE; FailTest (fputc (' ', f)); /* Invoke the object type's encode_attr method to obtain an attribute list: */ VGetAttrValue (posn, NULL, repn, & value); sublist = (methods->encode_attr) (value, & length); /* If binary data is indicated... */ if (length > 0) { /* Include "data" and "length" attributes in the attr list: */ VPrependAttr (sublist, VLengthAttr, NULL, VLongRepn, (VLong) length); VPrependAttr (sublist, VDataAttr, NULL, VLongRepn, (VLong) *offset); *offset += length; } /* Add the object to the queue of binary data blocks to be written: */ db = VNew (DataBlock); db->posn = *posn; db->list = sublist; db->length = length; VListAppend (*data_list, db); /* Write the typed value's attribute list: */ result = WriteAttrList (f, sublist, indent, data_list, offset); /* Remove the "data" and "length" attributes added earlier: */ if (length > 0) { VFirstAttr (sublist, & subposn); VDeleteAttr (& subposn); VDeleteAttr (& subposn); } } /* Output a trailing newline: */ if (result) FailTest (fputc ('\n', f)); return result; Fail: VWarning ("VWriteFile: Write to stream failed"); return FALSE; }
static VBoolean ReadData (FILE *f, VAttrList list, VReadFileFilterProc *filter, long *offset) { VAttrListPosn posn, subposn; VAttrList sublist; VBundle b; VRepnKind repn; VBoolean read_data, data_found, length_found; VLong data, length; VTypeMethods *methods; VPointer value; for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) { switch (VGetAttrRepn (& posn)) { case VAttrListRepn: /* Recurse on nested attribute list: */ VGetAttrValue (& posn, NULL, VAttrListRepn, & sublist); if (! ReadData (f, sublist, filter, offset)) return FALSE; break; case VBundleRepn: VGetAttrValue (& posn, NULL, VBundleRepn, & b); repn = VLookupType (b->type_name); /* If a filter routine was supplied, ask it whether to bother with the binary data: */ read_data = ! filter || (*filter) (b, repn); /* Extract any data and length attributes in the object's value: */ data_found = VLookupAttr (b->list, VDataAttr, & subposn); if (data_found) { if (! VGetAttrValue (& subposn, NULL, VLongRepn, & data)) { VWarning ("VReadFile: " "%s attribute's data attribute incorrect", VGetAttrName (& posn)); return FALSE; } VDeleteAttr (& subposn); } length_found = VLookupAttr (b->list, VLengthAttr, & subposn); if (length_found) { if (! VGetAttrValue (& subposn, NULL, VLongRepn, & length)) { VWarning ("VReadFile: " "%s attribute's length attribute incorrect", VGetAttrName (& posn)); return FALSE; } VDeleteAttr (& subposn); } /* None or both must be present: */ if (data_found ^ length_found) { VWarning ("VReadFile: %s attribute has %s but not %s", VGetAttrName (& posn), data_found ? "data" : "length", data_found ? "length" : "data"); return FALSE; } /* Read the binary data associated with the object: */ if (data_found) { if (data < *offset) { VWarning ("VReadFile: " "%s attribute's data attribute incorrect", VGetAttrName (& posn)); return FALSE; } if (! read_data) data += length; /* To seek forward to the start of the data block we first try fseek. That will fail on a pipe, in which case we seek by reading. */ if (data != *offset && fseek (f, (long) data - *offset, SEEK_CUR) == -1 && errno == ESPIPE && ! MySeek (f, data - *offset)) { VSystemWarning ("VReadFile: Seek within file failed"); return FALSE; } if (read_data) { b->data = VMalloc (b->length = length); if (fread (b->data, 1, length, f) != length) { VWarning ("VReadFile: Read from stream failed"); return FALSE; } *offset = data + length; } else /* bug: read error occured when bundle was not read by a filter function. FK 24/03/98 */ *offset = data; } /* Recurse to read binary data for sublist attributes: */ if (! ReadData (f, b->list, filter, offset)) return FALSE; /* If the object's type is registered and has a decode method, invoke it to decode the binary data: */ if (read_data && repn != VUnknownRepn && (methods = VRepnMethods (repn)) && methods->decode) { if (! (value = (methods->decode) (VGetAttrName (& posn), b))) return FALSE; /* Replace the old typed value with the newly decoded one: */ VSetAttrValue (& posn, NULL, repn, value); VDestroyBundle (b); } break; default: break; } } return TRUE; }
VAttrList VCopyAttrList (VAttrList list) { VAttrList new_list = VCreateAttrList (); size_t name_size, value_size; VAttrRec *old_a, *new_a; VBundle old_b, new_b; VTypeMethods *methods; /* For each node of the old list: */ for (old_a = list->next; old_a; old_a = old_a->next) { /* Compute the amount of storage needed for a copy of the node: */ name_size = strlen (old_a->name); value_size = (old_a->repn == VStringRepn) ? strlen ((VStringConst) old_a->value) + 1 : 0; /* Allocate that size and fill in the node's value: */ new_a = VMalloc (sizeof (VAttrRec) + name_size + value_size); strcpy (new_a->name, old_a->name); switch (new_a->repn = old_a->repn) { case VAttrListRepn: new_a->value = VCopyAttrList (old_a->value); break; case VBundleRepn: old_b = old_a->value; new_b = VCreateBundle (old_b->type_name, VCopyAttrList (old_b->list), old_b->length, NULL); if (old_b->length > 0) { new_b->data = VMalloc (old_b->length); memcpy (new_b->data, old_b->data, old_b->length); } new_a->value = new_b; break; case VPointerRepn: new_a->value = old_a->value; break; case VStringRepn: new_a->value = (VPointer) (new_a->name + name_size + 1); strcpy (new_a->value, old_a->value); break; default: if (methods = VRepnMethods (new_a->repn)) new_a->value = (methods->copy) (old_a->value); else VError ("VCopyAttrList: %s attribute has invalid repn %d", old_a->name, old_a->repn); } /* Append it to the new list: */ new_a->next = NULL; if (new_a->prev = new_list->prev) new_a->prev->next = new_a; if (! new_list->next) new_list->next = new_a; new_list->prev = new_a; } return new_list; }