12 #include "graphics.h" /* for SETTINGS_FILE */
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
,
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
));
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
));
63 static struct resource_list_t
*append_resource_list(
64 struct resource_list_t
*list
, struct resource_data_t
*data
) {
67 list
= new_resource_list();
70 list
->data
= realloc(list
->data
, (list
->n
+ 1) * sizeof(*list
->data
));
71 list
->data
[list
->n
++] = data
;
76 int parse_resource(struct resource_t
*resource
, const char *filename
) {
77 FILE *fp
= fopen(filename
, "rt");
80 /*printf("---- Failed to open resource file \"%s\"\n", filename);*/
84 /*printf("++++ Parsing resource file \"%s\"\n", filename);*/
85 parse_resource_file(resource
, filename
, 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
);
100 static void include_resources(void *vdata
, struct resource_list_t
*list
) {
101 struct resource_t
*resource
= vdata
;
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
);
111 parse_resource(resource
, list
->data
[x
]->data
.text
->data
);
116 struct resource_list_t
*first_resource_reference(struct resource_list_t
*list
,
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
;
134 char *first_resource_text(struct resource_list_t
*list
) {
139 for(x
= 0; x
< list
->n
; x
++) {
140 if(list
->data
[x
]->type
== RESOURCE_TEXT
) {
141 return list
->data
[x
]->data
.text
->data
;
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
,
155 struct resource_list_t
*p
= resource
->list
;
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
;
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
;
183 const char *lookup_resource_string(struct resource_t
*resource
,
184 const char *def
, ...) {
190 data
= lookup_resource_va(resource
, arg
);
193 return data
? data
: def
;
196 double lookup_resource_number(struct resource_t
*resource
, double def
, ...) {
201 data
= lookup_resource_va(resource
, 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
;
216 while(list
&& (name
= va_arg(arg
, const char *))) {
217 list
= first_resource_reference(list
, name
);
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
) {
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
);
241 search_resource_reference(list
->data
[x
]->data
.reference
->list
,
242 data
, recursive
> 0 ? recursive
- 1 : -1,
243 search_resource_func
, vdata
);
250 static int navigate_resource_type(char c
, enum resource_type_t
*type
) {
253 *type
= RESOURCE_TEXT
;
256 *type
= RESOURCE_REFERENCE
;
259 log_message(ERROR_TYPE_WARNING
, 0, __FILE__
, __LINE__
,
260 "navigate_resource_va(): Invalid resource type: '%c'", c
);
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
;
273 if(navigate_resource_type(*format
, &type
)) return 0;
275 for(x
= 0; x
< list
->n
; x
++) {
276 if(list
->data
[x
]->type
== type
) {
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
;
293 va_start(arg
, format
);
294 data
= navigate_resource_va(list
, format
, arg
);
301 static void free_resource_data(struct resource_data_t
*data
) {
305 free(data
->data
.text
->data
);
306 free(data
->data
.text
);
308 case RESOURCE_REFERENCE
:
309 free_resource_list(data
->data
.reference
->list
);
310 free(data
->data
.reference
->name
);
311 free(data
->data
.reference
);
318 static void free_resource_list(struct resource_list_t
*list
) {
321 for(x
= 0; x
< list
->n
; x
++) {
322 free_resource_data(list
->data
[x
]);
329 void free_resource(struct resource_t
*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
342 #ifdef RESOURCE_LIBRARY_EXPAT
346 struct resource_stack_data_t
{
347 struct resource_data_t
**data
;
351 struct resource_stack_t
{
352 struct resource_t
*resource
;
354 struct resource_stack_data_t
*stack
;
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
,
370 static void add_new_string(struct resource_data_t
*element
, const char *text
,
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
,
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
,
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
];
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
);
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;
425 static void free_resource_stack(struct resource_stack_t
*stack
) {
426 free(stack
->stack
->data
);
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
;
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
,
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
,
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
);
486 static void add_new_string(struct resource_data_t
*element
, const char *text
,
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
);
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
,
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
));
531 process_attribute(data
, attribute
[0], attribute
[1]);
536 static void process_attribute(struct resource_stack_t
*data
, const char *name
,
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",
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
573 while(len
&& isspace(*text
)) {
578 if(len
) add_new_string(p
, text
, len
);
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
)));
597 static void dump_list(struct resource_list_t
*list
) {
598 puts("Dumping disabled");
601 static int dumpi
= 0;
603 static void dump_data(struct resource_data_t
*data
);
605 static void dump_indent(void) {
608 for(x
= 0; x
< dumpi
* 4; x
++) putchar(' ');
612 static void dump(struct resource_stack_t
*data
) {
614 dump_list(data
->resource
->list
);
617 static void dump_list(struct resource_list_t
*list
) {
623 printf("*** NULL list\n");
627 for(x
= 0; x
< list
->n
; x
++) {
628 dump_data(list
->data
[x
]);
634 static void dump_data(struct resource_data_t
*data
) {
636 printf("*** NULL data\n");
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
);
650 printf("*** invalid resource_data_type_t: %i\n", (int)data
->type
);
656 #error No resource parsing method defined