1 /*
2  * Copyright 2010 Jerome Glisse <glisse@freedesktop.org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *      Jerome Glisse
25  */
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "bof.h"
30 
31 /*
32  * helpers
33  */
bof_entry_grow(bof_t * bof)34 static int bof_entry_grow(bof_t *bof)
35 {
36 	bof_t **array;
37 
38 	if (bof->array_size < bof->nentry)
39 		return 0;
40 	array = realloc(bof->array, (bof->nentry + 16) * sizeof(void*));
41 	if (array == NULL)
42 		return -ENOMEM;
43 	bof->array = array;
44 	bof->nentry += 16;
45 	return 0;
46 }
47 
48 /*
49  * object
50  */
bof_object(void)51 bof_t *bof_object(void)
52 {
53 	bof_t *object;
54 
55 	object = calloc(1, sizeof(bof_t));
56 	if (object == NULL)
57 		return NULL;
58 	object->refcount = 1;
59 	object->type = BOF_TYPE_OBJECT;
60 	object->size = 12;
61 	return object;
62 }
63 
bof_object_get(bof_t * object,const char * keyname)64 bof_t *bof_object_get(bof_t *object, const char *keyname)
65 {
66 	unsigned i;
67 
68 	for (i = 0; i < object->array_size; i += 2) {
69 		if (!strcmp(object->array[i]->value, keyname)) {
70 			return object->array[i + 1];
71 		}
72 	}
73 	return NULL;
74 }
75 
bof_object_set(bof_t * object,const char * keyname,bof_t * value)76 int bof_object_set(bof_t *object, const char *keyname, bof_t *value)
77 {
78 	bof_t *key;
79 	int r;
80 
81 	if (object->type != BOF_TYPE_OBJECT)
82 		return -EINVAL;
83 	r = bof_entry_grow(object);
84 	if (r)
85 		return r;
86 	key = bof_string(keyname);
87 	if (key == NULL)
88 		return -ENOMEM;
89 	object->array[object->array_size++] = key;
90 	object->array[object->array_size++] = value;
91 	object->size += value->size;
92 	object->size += key->size;
93 	bof_incref(value);
94 	return 0;
95 }
96 
97 /*
98  * array
99  */
bof_array(void)100 bof_t *bof_array(void)
101 {
102 	bof_t *array = bof_object();
103 
104 	if (array == NULL)
105 		return NULL;
106 	array->type = BOF_TYPE_ARRAY;
107 	array->size = 12;
108 	return array;
109 }
110 
bof_array_append(bof_t * array,bof_t * value)111 int bof_array_append(bof_t *array, bof_t *value)
112 {
113 	int r;
114 	if (array->type != BOF_TYPE_ARRAY)
115 		return -EINVAL;
116 	r = bof_entry_grow(array);
117 	if (r)
118 		return r;
119 	array->array[array->array_size++] = value;
120 	array->size += value->size;
121 	bof_incref(value);
122 	return 0;
123 }
124 
bof_array_get(bof_t * bof,unsigned i)125 bof_t *bof_array_get(bof_t *bof, unsigned i)
126 {
127 	if (!bof_is_array(bof) || i >= bof->array_size)
128 		return NULL;
129 	return bof->array[i];
130 }
131 
bof_array_size(bof_t * bof)132 unsigned bof_array_size(bof_t *bof)
133 {
134 	if (!bof_is_array(bof))
135 		return 0;
136 	return bof->array_size;
137 }
138 
139 /*
140  * blob
141  */
bof_blob(unsigned size,void * value)142 bof_t *bof_blob(unsigned size, void *value)
143 {
144 	bof_t *blob = bof_object();
145 
146 	if (blob == NULL)
147 		return NULL;
148 	blob->type = BOF_TYPE_BLOB;
149 	blob->value = calloc(1, size);
150 	if (blob->value == NULL) {
151 		bof_decref(blob);
152 		return NULL;
153 	}
154 	blob->size = size;
155 	memcpy(blob->value, value, size);
156 	blob->size += 12;
157 	return blob;
158 }
159 
bof_blob_size(bof_t * bof)160 unsigned bof_blob_size(bof_t *bof)
161 {
162 	if (!bof_is_blob(bof))
163 		return 0;
164 	return bof->size - 12;
165 }
166 
bof_blob_value(bof_t * bof)167 void *bof_blob_value(bof_t *bof)
168 {
169 	if (!bof_is_blob(bof))
170 		return NULL;
171 	return bof->value;
172 }
173 
174 /*
175  * string
176  */
bof_string(const char * value)177 bof_t *bof_string(const char *value)
178 {
179 	bof_t *string = bof_object();
180 
181 	if (string == NULL)
182 		return NULL;
183 	string->type = BOF_TYPE_STRING;
184 	string->size = strlen(value) + 1;
185 	string->value = calloc(1, string->size);
186 	if (string->value == NULL) {
187 		bof_decref(string);
188 		return NULL;
189 	}
190 	strcpy(string->value, value);
191 	string->size += 12;
192 	return string;
193 }
194 
195 /*
196  *  int32
197  */
bof_int32(int32_t value)198 bof_t *bof_int32(int32_t value)
199 {
200 	bof_t *int32 = bof_object();
201 
202 	if (int32 == NULL)
203 		return NULL;
204 	int32->type = BOF_TYPE_INT32;
205 	int32->size = 4;
206 	int32->value = calloc(1, int32->size);
207 	if (int32->value == NULL) {
208 		bof_decref(int32);
209 		return NULL;
210 	}
211 	memcpy(int32->value, &value, 4);
212 	int32->size += 12;
213 	return int32;
214 }
215 
bof_int32_value(bof_t * bof)216 int32_t bof_int32_value(bof_t *bof)
217 {
218 	return *((uint32_t*)bof->value);
219 }
220 
221 /*
222  *  common
223  */
bof_indent(int level)224 static void bof_indent(int level)
225 {
226 	int i;
227 
228 	for (i = 0; i < level; i++)
229 		fprintf(stderr, " ");
230 }
231 
bof_print_bof(bof_t * bof,int level,int entry)232 static void bof_print_bof(bof_t *bof, int level, int entry)
233 {
234 	bof_indent(level);
235 	if (bof == NULL) {
236 		fprintf(stderr, "--NULL-- for entry %d\n", entry);
237 		return;
238 	}
239 	switch (bof->type) {
240 	case BOF_TYPE_STRING:
241 		fprintf(stderr, "%p string [%s %d]\n", bof, (char*)bof->value, bof->size);
242 		break;
243 	case BOF_TYPE_INT32:
244 		fprintf(stderr, "%p int32 [%d %d]\n", bof, *(int*)bof->value, bof->size);
245 		break;
246 	case BOF_TYPE_BLOB:
247 		fprintf(stderr, "%p blob [%d]\n", bof, bof->size);
248 		break;
249 	case BOF_TYPE_NULL:
250 		fprintf(stderr, "%p null [%d]\n", bof, bof->size);
251 		break;
252 	case BOF_TYPE_OBJECT:
253 		fprintf(stderr, "%p object [%d %d]\n", bof, bof->array_size / 2, bof->size);
254 		break;
255 	case BOF_TYPE_ARRAY:
256 		fprintf(stderr, "%p array [%d %d]\n", bof, bof->array_size, bof->size);
257 		break;
258 	default:
259 		fprintf(stderr, "%p unknown [%d]\n", bof, bof->type);
260 		return;
261 	}
262 }
263 
bof_print_rec(bof_t * bof,int level,int entry)264 static void bof_print_rec(bof_t *bof, int level, int entry)
265 {
266 	unsigned i;
267 
268 	bof_print_bof(bof, level, entry);
269 	for (i = 0; i < bof->array_size; i++) {
270 		bof_print_rec(bof->array[i], level + 2, i);
271 	}
272 }
273 
bof_print(bof_t * bof)274 void bof_print(bof_t *bof)
275 {
276 	bof_print_rec(bof, 0, 0);
277 }
278 
bof_read(bof_t * root,FILE * file,long end,int level)279 static int bof_read(bof_t *root, FILE *file, long end, int level)
280 {
281 	bof_t *bof = NULL;
282 	int r;
283 
284 	if (ftell(file) >= end) {
285 		return 0;
286 	}
287 	r = bof_entry_grow(root);
288 	if (r)
289 		return r;
290 	bof = bof_object();
291 	if (bof == NULL)
292 		return -ENOMEM;
293 	bof->offset = ftell(file);
294 	r = fread(&bof->type, 4, 1, file);
295 	if (r != 1)
296 		goto out_err;
297 	r = fread(&bof->size, 4, 1, file);
298 	if (r != 1)
299 		goto out_err;
300 	r = fread(&bof->array_size, 4, 1, file);
301 	if (r != 1)
302 		goto out_err;
303 	switch (bof->type) {
304 	case BOF_TYPE_STRING:
305 	case BOF_TYPE_INT32:
306 	case BOF_TYPE_BLOB:
307 		bof->value = calloc(1, bof->size - 12);
308 		if (bof->value == NULL) {
309 			goto out_err;
310 		}
311 		r = fread(bof->value, bof->size - 12, 1, file);
312 		if (r != 1) {
313 			fprintf(stderr, "error reading %d\n", bof->size - 12);
314 			goto out_err;
315 		}
316 		break;
317 	case BOF_TYPE_NULL:
318 		return 0;
319 	case BOF_TYPE_OBJECT:
320 	case BOF_TYPE_ARRAY:
321 		r = bof_read(bof, file, bof->offset + bof->size, level + 2);
322 		if (r)
323 			goto out_err;
324 		break;
325 	default:
326 		fprintf(stderr, "invalid type %d\n", bof->type);
327 		goto out_err;
328 	}
329 	root->array[root->centry++] = bof;
330 	return bof_read(root, file, end, level);
331 out_err:
332 	bof_decref(bof);
333 	return -EINVAL;
334 }
335 
bof_load_file(const char * filename)336 bof_t *bof_load_file(const char *filename)
337 {
338 	bof_t *root = bof_object();
339 	int r;
340 
341 	if (root == NULL) {
342 		fprintf(stderr, "%s failed to create root object\n", __func__);
343 		return NULL;
344 	}
345 	root->file = fopen(filename, "r");
346 	if (root->file == NULL)
347 		goto out_err;
348 	r = fseek(root->file, 0L, SEEK_SET);
349 	if (r) {
350 		fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
351 		goto out_err;
352 	}
353 	root->offset = ftell(root->file);
354 	r = fread(&root->type, 4, 1, root->file);
355 	if (r != 1)
356 		goto out_err;
357 	r = fread(&root->size, 4, 1, root->file);
358 	if (r != 1)
359 		goto out_err;
360 	r = fread(&root->array_size, 4, 1, root->file);
361 	if (r != 1)
362 		goto out_err;
363 	r = bof_read(root, root->file, root->offset + root->size, 2);
364 	if (r)
365 		goto out_err;
366 	return root;
367 out_err:
368 	bof_decref(root);
369 	return NULL;
370 }
371 
bof_incref(bof_t * bof)372 void bof_incref(bof_t *bof)
373 {
374 	bof->refcount++;
375 }
376 
bof_decref(bof_t * bof)377 void bof_decref(bof_t *bof)
378 {
379 	unsigned i;
380 
381 	if (bof == NULL)
382 		return;
383 	if (--bof->refcount > 0)
384 		return;
385 	for (i = 0; i < bof->array_size; i++) {
386 		bof_decref(bof->array[i]);
387 		bof->array[i] = NULL;
388 	}
389 	bof->array_size = 0;
390 	if (bof->file) {
391 		fclose(bof->file);
392 		bof->file = NULL;
393 	}
394 	free(bof->array);
395 	free(bof->value);
396 	free(bof);
397 }
398 
bof_file_write(bof_t * bof,FILE * file)399 static int bof_file_write(bof_t *bof, FILE *file)
400 {
401 	unsigned i;
402 	int r;
403 
404 	r = fwrite(&bof->type, 4, 1, file);
405 	if (r != 1)
406 		return -EINVAL;
407 	r = fwrite(&bof->size, 4, 1, file);
408 	if (r != 1)
409 		return -EINVAL;
410 	r = fwrite(&bof->array_size, 4, 1, file);
411 	if (r != 1)
412 		return -EINVAL;
413 	switch (bof->type) {
414 	case BOF_TYPE_NULL:
415 		if (bof->size)
416 			return -EINVAL;
417 		break;
418 	case BOF_TYPE_STRING:
419 	case BOF_TYPE_INT32:
420 	case BOF_TYPE_BLOB:
421 		r = fwrite(bof->value, bof->size - 12, 1, file);
422 		if (r != 1)
423 			return -EINVAL;
424 		break;
425 	case BOF_TYPE_OBJECT:
426 	case BOF_TYPE_ARRAY:
427 		for (i = 0; i < bof->array_size; i++) {
428 			r = bof_file_write(bof->array[i], file);
429 			if (r)
430 				return r;
431 		}
432 		break;
433 	default:
434 		return -EINVAL;
435 	}
436 	return 0;
437 }
438 
bof_dump_file(bof_t * bof,const char * filename)439 int bof_dump_file(bof_t *bof, const char *filename)
440 {
441 	unsigned i;
442 	int r = 0;
443 
444 	if (bof->file) {
445 		fclose(bof->file);
446 		bof->file = NULL;
447 	}
448 	bof->file = fopen(filename, "w");
449 	if (bof->file == NULL) {
450 		fprintf(stderr, "%s failed to open file %s\n", __func__, filename);
451 		r = -EINVAL;
452 		goto out_err;
453 	}
454 	r = fseek(bof->file, 0L, SEEK_SET);
455 	if (r) {
456 		fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
457 		goto out_err;
458 	}
459 	r = fwrite(&bof->type, 4, 1, bof->file);
460 	if (r != 1)
461 		goto out_err;
462 	r = fwrite(&bof->size, 4, 1, bof->file);
463 	if (r != 1)
464 		goto out_err;
465 	r = fwrite(&bof->array_size, 4, 1, bof->file);
466 	if (r != 1)
467 		goto out_err;
468 	for (i = 0; i < bof->array_size; i++) {
469 		r = bof_file_write(bof->array[i], bof->file);
470 		if (r)
471 			return r;
472 	}
473 out_err:
474 	fclose(bof->file);
475 	bof->file = NULL;
476 	return r;
477 }
478