High-level changes:
[xuni.git] / src / resource.c
blobd9a031fe7883a592433b3736f9d59ae368cf14a0
1 /*! \file resource.c
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdarg.h>
8 #include <string.h>
9 #include <ctype.h>
11 #include "error.h"
12 #include "graphics.h" /* for SETTINGS_FILE */
13 #include "resource.h"
14 #include "xuni.h" /* for get_expression_value() */
16 static void dump_list(struct resource_list_t *list);
18 static struct resource_list_t *new_resource_list(void);
19 static struct resource_data_t *new_resource_data(
20 enum resource_data_type_t type, struct resource_data_t *base);
21 static struct resource_list_t *append_resource_list(
22 struct resource_list_t *list, struct resource_data_t *data);
24 static void include_resources(void *vdata, struct resource_list_t *list);
26 static const char *lookup_resource_va(struct resource_t *resource,
27 va_list arg);
29 static void free_resource_data(struct resource_data_t *data);
30 static void free_resource_list(struct resource_list_t *list);
32 static void parse_resource_file(struct resource_t *resource,
33 const char *filename, FILE *fp);
35 void init_resource(struct resource_t *resource) {
36 resource->list = new_resource_list();
39 void write_default_settings(void) {
40 printf("*** Could not open \"" SETTINGS_FILE "\", generating\n");
43 static struct resource_list_t *new_resource_list(void) {
44 struct resource_list_t *list = malloc(sizeof(*list));
46 list->data = 0;
47 list->n = 0;
49 return list;
52 static struct resource_data_t *new_resource_data(
53 enum resource_data_type_t type, struct resource_data_t *base) {
55 struct resource_data_t *data = malloc(sizeof(*data));
57 data->base = base;
58 data->type = type;
60 return data;
63 static struct resource_list_t *append_resource_list(
64 struct resource_list_t *list, struct resource_data_t *data) {
66 if(!list) {
67 list = new_resource_list();
70 list->data = realloc(list->data, (list->n + 1) * sizeof(*list->data));
71 list->data[list->n ++] = data;
73 return list;
76 int parse_resource(struct resource_t *resource, const char *filename) {
77 FILE *fp = fopen(filename, "rt");
79 if(!fp) {
80 /*printf("---- Failed to open resource file \"%s\"\n", filename);*/
81 return 1;
84 /*printf("++++ Parsing resource file \"%s\"\n", filename);*/
85 parse_resource_file(resource, filename, fp);
87 fclose(fp);
89 if(resource->list->n && resource->list->data[resource->list->n - 1]->type
90 == RESOURCE_REFERENCE) {
92 search_resource_reference(resource->list->data
93 [resource->list->n - 1]->data.reference->list, "include",
94 1, include_resources, resource);
97 return 0;
100 static void include_resources(void *vdata, struct resource_list_t *list) {
101 struct resource_t *resource = vdata;
102 size_t x;
104 for(x = 0; x < list->n; x ++) {
105 if(list->data[x]->type != RESOURCE_TEXT) {
106 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
107 "data in [include] is not of type text but of type %i",
108 (int)list->data[x]->type);
110 else {
111 parse_resource(resource, list->data[x]->data.text->data);
116 struct resource_list_t *first_resource_reference(struct resource_list_t *list,
117 const char *name) {
119 size_t x;
121 if(!list) return 0;
123 for(x = 0; x < list->n; x ++) {
124 if(list->data[x]->type == RESOURCE_REFERENCE) {
125 if(!strcmp(list->data[x]->data.reference->name, name)) {
126 return list->data[x]->data.reference->list;
131 return 0;
134 char *first_resource_text(struct resource_list_t *list) {
135 size_t x;
137 if(!list) return 0;
139 for(x = 0; x < list->n; x ++) {
140 if(list->data[x]->type == RESOURCE_TEXT) {
141 return list->data[x]->data.text->data;
145 return 0;
148 char *find_resource_text(struct resource_list_t *list, const char *data) {
149 return first_resource_text(first_resource_reference(list, data));
152 static const char *lookup_resource_va(struct resource_t *resource,
153 va_list arg) {
155 struct resource_list_t *p = resource->list;
156 size_t x;
157 const char *name;
159 if(!p) return 0;
161 while((name = va_arg(arg, const char *))) {
162 for(x = 0; x < p->n; x ++) {
163 if(p->data[x]->type == RESOURCE_REFERENCE
164 && !strcmp(p->data[x]->data.reference->name, name)) {
166 p = p->data[x]->data.reference->list;
167 x = (size_t)-1;
168 break;
172 if(x != (size_t)-1) return 0;
175 if(p->n && p->data[0]->type == RESOURCE_TEXT) {
176 /*printf("Found: \"%s\"\n", p->data[0]->data.text->data);*/
177 return p->data[0]->data.text->data;
180 return 0;
183 const char *lookup_resource_string(struct resource_t *resource,
184 const char *def, ...) {
186 const char *data;
187 va_list arg;
189 va_start(arg, def);
190 data = lookup_resource_va(resource, arg);
191 va_end(arg);
193 return data ? data : def;
196 double lookup_resource_number(struct resource_t *resource, double def, ...) {
197 const char *data;
198 va_list arg;
200 va_start(arg, def);
201 data = lookup_resource_va(resource, arg);
202 va_end(arg);
204 return get_expression_value(data, def);
207 struct resource_list_t *lookup_resource_reference(struct resource_t *resource,
208 struct resource_list_t *def, ...) {
210 struct resource_list_t *list = resource->list;
211 const char *name;
212 va_list arg;
214 va_start(arg, def);
216 while(list && (name = va_arg(arg, const char *))) {
217 list = first_resource_reference(list, name);
220 va_end(arg);
222 return list ? list : def;
225 void search_resource_reference(struct resource_list_t *list, const char *data,
226 int recursive, search_resource_func_t search_resource_func, void *vdata) {
228 size_t x;
230 if(!list || !data) return;
232 for(x = 0; x < list->n; x ++) {
233 if(list->data[x]->type == RESOURCE_REFERENCE) {
234 if(!strcmp(list->data[x]->data.reference->name, data)) {
235 if(list->data[x]->data.reference->list) {
236 (*search_resource_func)
237 (vdata, list->data[x]->data.reference->list);
240 else if(recursive) {
241 search_resource_reference(list->data[x]->data.reference->list,
242 data, recursive > 0 ? recursive - 1 : -1,
243 search_resource_func, vdata);
249 #if !1
250 static int navigate_resource_type(char c, enum resource_type_t *type) {
251 switch(c) {
252 case 's':
253 *type = RESOURCE_TEXT;
254 break;
255 case 'r':
256 *type = RESOURCE_REFERENCE;
257 break;
258 default:
259 log_message(ERROR_TYPE_WARNING, 0, __FILE__, __LINE__,
260 "navigate_resource_va(): Invalid resource type: '%c'", c);
261 return 1;
264 return 0;
267 static resource_data_t *navigate_resource_va(struct resource_list_t *list,
268 const char *format, va_list arg) {
270 enum resource_type_t type;
271 size_t x;
273 if(navigate_resource_type(*format, &type)) return 0;
275 for(x = 0; x < list->n; x ++) {
276 if(list->data[x]->type == type) {
277 switch(type) {
278 case RESOURCE_TEXT:
280 case RESOURCE_REFERENCE:
287 struct resource_data_t *navigate_resource(struct resource_list_t *list,
288 const char *format, ...) {
290 struct resource_data_t *data;
291 va_list arg;
293 va_start(arg, format);
294 data = navigate_resource_va(list, format, arg);
295 va_end(arg);
297 return data;
299 #endif
301 static void free_resource_data(struct resource_data_t *data) {
302 switch(data->type) {
303 default:
304 case RESOURCE_TEXT:
305 free(data->data.text->data);
306 free(data->data.text);
307 break;
308 case RESOURCE_REFERENCE:
309 free_resource_list(data->data.reference->list);
310 free(data->data.reference->name);
311 free(data->data.reference);
312 break;
315 free(data);
318 static void free_resource_list(struct resource_list_t *list) {
319 size_t x;
321 for(x = 0; x < list->n; x ++) {
322 free_resource_data(list->data[x]);
325 free(list->data);
326 free(list);
329 void free_resource(struct resource_t *resource) {
330 if(resource) {
331 /*printf("==== Resources dump prior to freeing:\n");
332 dump_list(resource->list);*/
334 free_resource_list(resource->list);
338 #ifdef TRACE_RESOURCE_FREE
339 #undef TRACE_RESOURCE_FREE
340 #endif
342 #ifdef RESOURCE_LIBRARY_EXPAT
344 #include "expat.h"
346 struct resource_stack_data_t {
347 struct resource_data_t **data;
348 size_t n;
351 struct resource_stack_t {
352 struct resource_t *resource;
354 struct resource_stack_data_t *stack;
356 XML_Parser parser;
359 static struct resource_stack_t *new_resource_stack(
360 struct resource_t *resource);
361 static void free_resource_stack(struct resource_stack_t *stack);
362 static void push_resource_stack(struct resource_stack_data_t *stack,
363 struct resource_data_t *data);
364 static void pop_resource_stack(struct resource_stack_data_t *stack);
365 static struct resource_data_t *top_of_stack(struct resource_stack_t *stack);
366 static void push_resource_reference(struct resource_stack_t *stack,
367 struct resource_data_t *reference);
368 static struct resource_data_t *add_new_element(struct resource_stack_t *data,
369 const char *name);
370 static void add_new_string(struct resource_data_t *element, const char *text,
371 size_t len);
372 static void resize_existing_string(char **str, const char *text, size_t len);
374 static void XMLCALL default_handler(void *vdata, const XML_Char *text,
375 int len);
376 static void XMLCALL start_element(void *vdata, const XML_Char *name,
377 const XML_Char **attribute);
378 static void process_attribute(struct resource_stack_t *data, const char *name,
379 const char *text);
380 static void XMLCALL end_element(void *vdata, const XML_Char *name);
381 static void XMLCALL handle_text(void *vdata, const XML_Char *text, int len);
383 static void resource_parse_error(const char *filename, XML_Parser parser);
385 static void parse_resource_file(struct resource_t *resource,
386 const char *filename, FILE *fp) {
388 char buffer[RESOURCE_BUFFER_SIZE];
389 size_t len;
390 struct resource_stack_t *stack = new_resource_stack(resource);
391 stack->parser = XML_ParserCreate(NULL);
393 XML_SetUserData(stack->parser, stack);
394 XML_SetDefaultHandler(stack->parser, default_handler);
395 XML_SetElementHandler(stack->parser, start_element, end_element);
396 XML_SetCharacterDataHandler(stack->parser, handle_text);
398 while((len = fread(buffer, 1, sizeof(buffer), fp))) {
399 if(XML_Parse(stack->parser, buffer, len,
400 feof(fp) || ferror(fp)) == XML_STATUS_ERROR) {
402 resource_parse_error(filename, stack->parser);
404 break;
408 XML_ParserFree(stack->parser);
409 free_resource_stack(stack);
412 static struct resource_stack_t *new_resource_stack(
413 struct resource_t *resource) {
415 struct resource_stack_t *stack = malloc(sizeof(*stack));
417 stack->resource = resource;
418 stack->stack = malloc(sizeof(*stack->stack));
419 stack->stack->data = 0;
420 stack->stack->n = 0;
422 return stack;
425 static void free_resource_stack(struct resource_stack_t *stack) {
426 free(stack->stack->data);
427 free(stack->stack);
428 free(stack);
431 static void push_resource_stack(struct resource_stack_data_t *stack,
432 struct resource_data_t *data) {
434 stack->data = realloc(stack->data, (stack->n + 1) * sizeof(*stack->data));
435 stack->data[stack->n] = data;
437 stack->n ++;
440 static void pop_resource_stack(struct resource_stack_data_t *stack) {
441 if(!stack->n) return;
443 stack->data = realloc(stack->data, (-- stack->n) * sizeof(*stack->data));
446 static struct resource_data_t *top_of_stack(struct resource_stack_t *stack) {
447 if(!stack->stack->n) return 0;
449 return stack->stack->data[stack->stack->n - 1];
452 static void push_resource_reference(struct resource_stack_t *stack,
453 struct resource_data_t *reference) {
455 struct resource_reference_t *temp;
457 if(!stack->stack->n) {
458 stack->resource->list = append_resource_list(stack->resource->list,
459 reference);
461 else {
462 temp = stack->stack->data[stack->stack->n - 1]->data.reference;
464 temp->list = append_resource_list(temp->list, reference);
468 static struct resource_data_t *add_new_element(struct resource_stack_t *data,
469 const char *name) {
471 struct resource_data_t *element
472 = new_resource_data(RESOURCE_REFERENCE, top_of_stack(data));
474 element->data.reference = malloc(sizeof(*element->data.reference));
476 element->data.reference->name = malloc(strlen(name) + 1);
477 strcpy(element->data.reference->name, name);
479 element->data.reference->list = 0;
481 push_resource_reference(data, element);
483 return element;
486 static void add_new_string(struct resource_data_t *element, const char *text,
487 size_t len) {
489 struct resource_data_t *rdata
490 = new_resource_data(RESOURCE_TEXT, element);
492 rdata->data.text = malloc(sizeof(*element->data.text));
494 rdata->data.text->data = malloc(len + 1);
495 memcpy(rdata->data.text->data, text, len);
496 rdata->data.text->data[len] = 0;
498 if(element->type != RESOURCE_REFERENCE) {
499 printf("**** ERROR: type is not REFERENCE: %i\n", element->type);
501 else {
502 element->data.reference->list = append_resource_list(
503 element->data.reference->list, rdata);
507 static void resize_existing_string(char **str, const char *text, size_t len) {
508 size_t slen = strlen(*str);
510 *str = realloc(*str, slen + len + 1);
511 memcpy(*str + slen, text, len);
512 (*str)[slen + len] = 0;
515 static void XMLCALL default_handler(void *vdata, const XML_Char *text,
516 int len) {
518 /*printf("default_handler(): \"%.*s\" [%i]\n", len, text, len);*/
521 static void XMLCALL start_element(void *vdata, const XML_Char *name,
522 const XML_Char **attribute) {
524 struct resource_stack_t *data = vdata;
526 /*printf("start_element(): %s\n", name);*/
528 push_resource_stack(data->stack, add_new_element(data, name));
530 while(*attribute) {
531 process_attribute(data, attribute[0], attribute[1]);
532 attribute += 2;
536 static void process_attribute(struct resource_stack_t *data, const char *name,
537 const char *text) {
539 add_new_string(add_new_element(data, name), text, strlen(text));
542 static void XMLCALL end_element(void *vdata, const XML_Char *name) {
543 struct resource_stack_t *data = vdata;
544 struct resource_data_t *top = top_of_stack(data);
546 if(top->type != RESOURCE_REFERENCE) {
547 printf("*** end_element() called at data of type: %i\n",
548 (int)top->type);
550 else if(strcmp(top->data.reference->name, name) != 0) {
551 printf("*** end_element(): tag \"%s\" closed with \"%s\"\n",
552 top->data.reference->name, name);
555 pop_resource_stack(data->stack);
558 static void XMLCALL handle_text(void *vdata, const XML_Char *text, int len) {
559 struct resource_stack_t *data = vdata;
560 struct resource_data_t *p = top_of_stack(data);
562 /* !!! This shows the byte offset of each text fragment:
563 printf("[%li]: \"%.*s\" [%i]\n", XML_GetCurrentByteIndex(data->parser), len, text, len);*/
565 if(!p->data.reference->list) {
566 p->data.reference->list = new_resource_list();
569 if(!p->data.reference->list->n
570 || p->data.reference->list->data[p->data.reference->list->n - 1]->type
571 != RESOURCE_TEXT) {
573 while(len && isspace(*text)) {
574 text ++;
575 len --;
578 if(len) add_new_string(p, text, len);
580 else {
581 resize_existing_string(
582 &p->data.reference->list->data[
583 p->data.reference->list->n - 1]->data.text->data, text, len);
587 static void resource_parse_error(const char *filename, XML_Parser parser) {
588 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
589 "Parse error in %s line %i column %i:", filename,
590 XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser));
592 log_message(ERROR_TYPE_RESOURCE, ERROR_FLAG_CONTINUED, __FILE__, __LINE__,
593 "expat: %s", XML_ErrorString(XML_GetErrorCode(parser)));
596 #if 1
597 static void dump_list(struct resource_list_t *list) {
598 puts("Dumping disabled");
600 #else
601 static int dumpi = 0;
603 static void dump_data(struct resource_data_t *data);
605 static void dump_indent(void) {
606 int x;
608 for(x = 0; x < dumpi * 4; x ++) putchar(' ');
611 /* for gdb */
612 static void dump(struct resource_stack_t *data) {
613 puts("--- Dumping");
614 dump_list(data->resource->list);
617 static void dump_list(struct resource_list_t *list) {
618 size_t x;
620 dumpi ++;
622 if(!list) {
623 printf("*** NULL list\n");
624 return;
627 for(x = 0; x < list->n; x ++) {
628 dump_data(list->data[x]);
631 dumpi --;
634 static void dump_data(struct resource_data_t *data) {
635 if(!data) {
636 printf("*** NULL data\n");
637 return;
640 dump_indent();
642 if(data->type == RESOURCE_TEXT) {
643 printf("\"%s\"\n", data->data.text->data);
645 else if(data->type == RESOURCE_REFERENCE) {
646 printf("[%s]\n", data->data.reference->name);
647 dump_list(data->data.reference->list);
649 else {
650 printf("*** invalid resource_data_type_t: %i\n", (int)data->type);
653 #endif
655 #else
656 #error No resource parsing method defined
657 #endif