/* * Copyright 2010 Jerome Glisse <glisse@freedesktop.org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * Authors: * Jerome Glisse */ #include <errno.h> #include <stdlib.h> #include <string.h> #include "bof.h" /* * helpers */ static int bof_entry_grow(bof_t *bof) { bof_t **array; if (bof->array_size < bof->nentry) return 0; array = realloc(bof->array, (bof->nentry + 16) * sizeof(void*)); if (array == NULL) return -ENOMEM; bof->array = array; bof->nentry += 16; return 0; } /* * object */ bof_t *bof_object(void) { bof_t *object; object = calloc(1, sizeof(bof_t)); if (object == NULL) return NULL; object->refcount = 1; object->type = BOF_TYPE_OBJECT; object->size = 12; return object; } bof_t *bof_object_get(bof_t *object, const char *keyname) { unsigned i; for (i = 0; i < object->array_size; i += 2) { if (!strcmp(object->array[i]->value, keyname)) { return object->array[i + 1]; } } return NULL; } int bof_object_set(bof_t *object, const char *keyname, bof_t *value) { bof_t *key; int r; if (object->type != BOF_TYPE_OBJECT) return -EINVAL; r = bof_entry_grow(object); if (r) return r; key = bof_string(keyname); if (key == NULL) return -ENOMEM; object->array[object->array_size++] = key; object->array[object->array_size++] = value; object->size += value->size; object->size += key->size; bof_incref(value); return 0; } /* * array */ bof_t *bof_array(void) { bof_t *array = bof_object(); if (array == NULL) return NULL; array->type = BOF_TYPE_ARRAY; array->size = 12; return array; } int bof_array_append(bof_t *array, bof_t *value) { int r; if (array->type != BOF_TYPE_ARRAY) return -EINVAL; r = bof_entry_grow(array); if (r) return r; array->array[array->array_size++] = value; array->size += value->size; bof_incref(value); return 0; } bof_t *bof_array_get(bof_t *bof, unsigned i) { if (!bof_is_array(bof) || i >= bof->array_size) return NULL; return bof->array[i]; } unsigned bof_array_size(bof_t *bof) { if (!bof_is_array(bof)) return 0; return bof->array_size; } /* * blob */ bof_t *bof_blob(unsigned size, void *value) { bof_t *blob = bof_object(); if (blob == NULL) return NULL; blob->type = BOF_TYPE_BLOB; blob->value = calloc(1, size); if (blob->value == NULL) { bof_decref(blob); return NULL; } blob->size = size; memcpy(blob->value, value, size); blob->size += 12; return blob; } unsigned bof_blob_size(bof_t *bof) { if (!bof_is_blob(bof)) return 0; return bof->size - 12; } void *bof_blob_value(bof_t *bof) { if (!bof_is_blob(bof)) return NULL; return bof->value; } /* * string */ bof_t *bof_string(const char *value) { bof_t *string = bof_object(); if (string == NULL) return NULL; string->type = BOF_TYPE_STRING; string->size = strlen(value) + 1; string->value = calloc(1, string->size); if (string->value == NULL) { bof_decref(string); return NULL; } strcpy(string->value, value); string->size += 12; return string; } /* * int32 */ bof_t *bof_int32(int32_t value) { bof_t *int32 = bof_object(); if (int32 == NULL) return NULL; int32->type = BOF_TYPE_INT32; int32->size = 4; int32->value = calloc(1, int32->size); if (int32->value == NULL) { bof_decref(int32); return NULL; } memcpy(int32->value, &value, 4); int32->size += 12; return int32; } int32_t bof_int32_value(bof_t *bof) { return *((uint32_t*)bof->value); } /* * common */ static void bof_indent(int level) { int i; for (i = 0; i < level; i++) fprintf(stderr, " "); } static void bof_print_bof(bof_t *bof, int level, int entry) { bof_indent(level); if (bof == NULL) { fprintf(stderr, "--NULL-- for entry %d\n", entry); return; } switch (bof->type) { case BOF_TYPE_STRING: fprintf(stderr, "%p string [%s %d]\n", bof, (char*)bof->value, bof->size); break; case BOF_TYPE_INT32: fprintf(stderr, "%p int32 [%d %d]\n", bof, *(int*)bof->value, bof->size); break; case BOF_TYPE_BLOB: fprintf(stderr, "%p blob [%d]\n", bof, bof->size); break; case BOF_TYPE_NULL: fprintf(stderr, "%p null [%d]\n", bof, bof->size); break; case BOF_TYPE_OBJECT: fprintf(stderr, "%p object [%d %d]\n", bof, bof->array_size / 2, bof->size); break; case BOF_TYPE_ARRAY: fprintf(stderr, "%p array [%d %d]\n", bof, bof->array_size, bof->size); break; default: fprintf(stderr, "%p unknown [%d]\n", bof, bof->type); return; } } static void bof_print_rec(bof_t *bof, int level, int entry) { unsigned i; bof_print_bof(bof, level, entry); for (i = 0; i < bof->array_size; i++) { bof_print_rec(bof->array[i], level + 2, i); } } void bof_print(bof_t *bof) { bof_print_rec(bof, 0, 0); } static int bof_read(bof_t *root, FILE *file, long end, int level) { bof_t *bof = NULL; int r; if (ftell(file) >= end) { return 0; } r = bof_entry_grow(root); if (r) return r; bof = bof_object(); if (bof == NULL) return -ENOMEM; bof->offset = ftell(file); r = fread(&bof->type, 4, 1, file); if (r != 1) goto out_err; r = fread(&bof->size, 4, 1, file); if (r != 1) goto out_err; r = fread(&bof->array_size, 4, 1, file); if (r != 1) goto out_err; switch (bof->type) { case BOF_TYPE_STRING: case BOF_TYPE_INT32: case BOF_TYPE_BLOB: bof->value = calloc(1, bof->size - 12); if (bof->value == NULL) { goto out_err; } r = fread(bof->value, bof->size - 12, 1, file); if (r != 1) { fprintf(stderr, "error reading %d\n", bof->size - 12); goto out_err; } break; case BOF_TYPE_NULL: return 0; case BOF_TYPE_OBJECT: case BOF_TYPE_ARRAY: r = bof_read(bof, file, bof->offset + bof->size, level + 2); if (r) goto out_err; break; default: fprintf(stderr, "invalid type %d\n", bof->type); goto out_err; } root->array[root->centry++] = bof; return bof_read(root, file, end, level); out_err: bof_decref(bof); return -EINVAL; } bof_t *bof_load_file(const char *filename) { bof_t *root = bof_object(); int r; if (root == NULL) { fprintf(stderr, "%s failed to create root object\n", __func__); return NULL; } root->file = fopen(filename, "r"); if (root->file == NULL) goto out_err; r = fseek(root->file, 0L, SEEK_SET); if (r) { fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename); goto out_err; } root->offset = ftell(root->file); r = fread(&root->type, 4, 1, root->file); if (r != 1) goto out_err; r = fread(&root->size, 4, 1, root->file); if (r != 1) goto out_err; r = fread(&root->array_size, 4, 1, root->file); if (r != 1) goto out_err; r = bof_read(root, root->file, root->offset + root->size, 2); if (r) goto out_err; return root; out_err: bof_decref(root); return NULL; } void bof_incref(bof_t *bof) { bof->refcount++; } void bof_decref(bof_t *bof) { unsigned i; if (bof == NULL) return; if (--bof->refcount > 0) return; for (i = 0; i < bof->array_size; i++) { bof_decref(bof->array[i]); bof->array[i] = NULL; } bof->array_size = 0; if (bof->file) { fclose(bof->file); bof->file = NULL; } free(bof->array); free(bof->value); free(bof); } static int bof_file_write(bof_t *bof, FILE *file) { unsigned i; int r; r = fwrite(&bof->type, 4, 1, file); if (r != 1) return -EINVAL; r = fwrite(&bof->size, 4, 1, file); if (r != 1) return -EINVAL; r = fwrite(&bof->array_size, 4, 1, file); if (r != 1) return -EINVAL; switch (bof->type) { case BOF_TYPE_NULL: if (bof->size) return -EINVAL; break; case BOF_TYPE_STRING: case BOF_TYPE_INT32: case BOF_TYPE_BLOB: r = fwrite(bof->value, bof->size - 12, 1, file); if (r != 1) return -EINVAL; break; case BOF_TYPE_OBJECT: case BOF_TYPE_ARRAY: for (i = 0; i < bof->array_size; i++) { r = bof_file_write(bof->array[i], file); if (r) return r; } break; default: return -EINVAL; } return 0; } int bof_dump_file(bof_t *bof, const char *filename) { unsigned i; int r = 0; if (bof->file) { fclose(bof->file); bof->file = NULL; } bof->file = fopen(filename, "w"); if (bof->file == NULL) { fprintf(stderr, "%s failed to open file %s\n", __func__, filename); r = -EINVAL; goto out_err; } r = fseek(bof->file, 0L, SEEK_SET); if (r) { fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename); goto out_err; } r = fwrite(&bof->type, 4, 1, bof->file); if (r != 1) goto out_err; r = fwrite(&bof->size, 4, 1, bof->file); if (r != 1) goto out_err; r = fwrite(&bof->array_size, 4, 1, bof->file); if (r != 1) goto out_err; for (i = 0; i < bof->array_size; i++) { r = bof_file_write(bof->array[i], bof->file); if (r) return r; } out_err: fclose(bof->file); bof->file = NULL; return r; }