WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / plist.c
blobbc6c9d1bd8a9b1eeb7c4eaaa310fff36eacb5364
1 /* plist.c
3 Copyright (c) 2003-2015 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <inttypes.h>
15 #include "libxml/parser.h"
17 #include "common.h"
18 #include "hb_dict.h"
19 #include "plist.h"
21 #define BUF_SZ (128*1024)
23 static char *preamble =
24 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
25 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
26 "<plist version=\"1.0\">\n";
27 static char *postfix =
28 "</plist>\n";
30 typedef struct queue_item_s queue_item_t;
31 struct queue_item_s
33 void *value;
34 queue_item_t *next;
37 typedef struct
39 queue_item_t *head;
40 } queue_t;
42 queue_t * queue_new()
44 return calloc(1, sizeof(queue_t));
47 void queue_free(queue_t **_q)
49 queue_t *q = *_q;
50 if (q == NULL)
51 return;
53 queue_item_t *n, *i = q->head;
54 while (i != NULL)
56 n = i->next;
57 free(i);
58 i = n;
60 free(q);
61 *_q = NULL;
64 void queue_push_head(queue_t *q, void *v)
66 queue_item_t *i = calloc(1, sizeof(queue_item_t));
67 i->value = v;
68 i->next = q->head;
69 q->head = i;
72 void * queue_peek_head(queue_t *q)
74 if (q->head != NULL)
75 return q->head->value;
76 return NULL;
79 void * queue_pop_head(queue_t *q)
81 void *result;
82 queue_item_t *i;
84 if (q->head == NULL)
85 return NULL;
87 i = q->head;
88 result = i->value;
89 q->head = i->next;
90 free(i);
92 return result;
95 int queue_is_empty(queue_t *q)
97 return q->head == NULL;
100 char * markup_escape_text(const char *str)
102 int ii, jj;
103 int len = strlen(str);
104 int step = 40;
105 int alloc = len + step;
106 char *markup = malloc(alloc);
108 for (ii = 0, jj = 0; ii < len; ii++)
110 if (jj > alloc - 8)
112 alloc += step;
113 char *tmp = realloc(markup, alloc);
114 if (tmp == NULL)
116 markup[jj] = 0;
117 return markup;
119 markup = tmp;
121 switch (str[ii])
123 case '<':
124 markup[jj++] = '&';
125 markup[jj++] = 'l';
126 markup[jj++] = 't';
127 markup[jj++] = ';';
128 break;
129 case '>':
130 markup[jj++] = '&';
131 markup[jj++] = 'g';
132 markup[jj++] = 't';
133 markup[jj++] = ';';
134 break;
135 case '\'':
136 markup[jj++] = '&';
137 markup[jj++] = 'a';
138 markup[jj++] = 'p';
139 markup[jj++] = 'o';
140 markup[jj++] = 's';
141 markup[jj++] = ';';
142 break;
143 case '"':
144 markup[jj++] = '&';
145 markup[jj++] = 'q';
146 markup[jj++] = 'u';
147 markup[jj++] = 'o';
148 markup[jj++] = 't';
149 markup[jj++] = ';';
150 break;
151 case '&':
152 markup[jj++] = '&';
153 markup[jj++] = 'a';
154 markup[jj++] = 'm';
155 markup[jj++] = 'p';
156 markup[jj++] = ';';
157 break;
158 default:
159 markup[jj++] = str[ii];
160 break;
162 markup[jj] = 0;
164 return markup;
167 enum
169 P_NONE = 0,
170 P_PLIST,
171 P_KEY,
172 P_ARRAY,
173 P_DICT,
174 P_INTEGER,
175 P_REAL,
176 P_STRING,
177 P_DATE,
178 P_TRUE,
179 P_FALSE,
180 P_DATA,
183 typedef struct
185 char *tag;
186 int id;
187 } tag_map_t;
189 static tag_map_t tag_map[] =
191 {"plist", P_PLIST},
192 {"key", P_KEY},
193 {"array", P_ARRAY},
194 {"dict", P_DICT},
195 {"integer", P_INTEGER},
196 {"real", P_REAL},
197 {"string", P_STRING},
198 {"date", P_DATE},
199 {"true", P_TRUE},
200 {"false", P_FALSE},
201 {"data", P_DATA},
203 #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t))
205 typedef struct
207 char *key;
208 char *value;
209 hb_value_t *plist;
210 queue_t *stack;
211 queue_t *tag_stack;
212 int closed_top;
213 } parse_data_t;
215 static void
216 start_element(
217 void *ud,
218 const xmlChar *xname,
219 const xmlChar **attr_names)
221 char *name = (char*)xname;
222 parse_data_t *pd = (parse_data_t*)ud;
223 union
225 int id;
226 void * pid;
227 } id;
228 int ii;
230 // Check to see if the first element found has been closed
231 // If so, ignore any junk following it.
232 if (pd->closed_top)
233 return;
235 for (ii = 0; ii < TAG_MAP_SZ; ii++)
237 if (strcmp(name, tag_map[ii].tag) == 0)
239 id.id = tag_map[ii].id;
240 break;
243 if (ii == TAG_MAP_SZ)
245 hb_error("Unrecognized start tag (%s)", name);
246 return;
248 if (pd->value)
250 free(pd->value);
251 pd->value = NULL;
253 queue_push_head(pd->tag_stack, id.pid);
254 hb_value_type_t gtype = 0;
255 hb_value_t *gval = NULL;
256 hb_value_t *current = queue_peek_head(pd->stack);
257 switch (id.id)
259 case P_PLIST:
260 { // Ignore
261 } break;
262 case P_KEY:
264 if (pd->key) free(pd->key);
265 pd->key = NULL;
266 } break;
267 case P_DICT:
269 gval = hb_dict_init();
270 queue_push_head(pd->stack, gval);
271 } break;
272 case P_ARRAY:
274 gval = hb_value_array_init();
275 queue_push_head(pd->stack, gval);
276 } break;
277 case P_INTEGER:
279 } break;
280 case P_REAL:
282 } break;
283 case P_STRING:
285 } break;
286 case P_DATE:
288 } break;
289 case P_TRUE:
291 } break;
292 case P_FALSE:
294 } break;
295 case P_DATA:
297 } break;
299 // Add the element to the current container
300 if (gval)
301 { // There's an element to add
302 if (current == NULL)
304 pd->plist = gval;
305 return;
307 gtype = hb_value_type(current);
308 if (gtype == HB_VALUE_TYPE_ARRAY)
310 hb_value_array_append(current, gval);
312 else if (gtype == HB_VALUE_TYPE_DICT)
314 if (pd->key == NULL)
316 hb_error("No key for dictionary item");
317 hb_value_free(&gval);
319 else
321 hb_dict_set(current, pd->key, gval);
324 else
326 hb_error("Invalid container type. This shouldn't happen");
331 static void
332 end_element(
333 void *ud,
334 const xmlChar *xname)
336 char *name = (char*)xname;
337 parse_data_t *pd = (parse_data_t*)ud;
338 int id;
339 union
341 int id;
342 void * pid;
343 } start_id;
344 int ii;
346 // Check to see if the first element found has been closed
347 // If so, ignore any junk following it.
348 if (pd->closed_top)
349 return;
351 for (ii = 0; ii < TAG_MAP_SZ; ii++)
353 if (strcmp(name, tag_map[ii].tag) == 0)
355 id = tag_map[ii].id;
356 break;
359 if (ii == TAG_MAP_SZ)
361 hb_error("Unrecognized start tag (%s)", name);
362 return;
364 start_id.pid = queue_pop_head(pd->tag_stack);
365 if (start_id.id != id)
366 hb_error("start tag != end tag: (%s %d) %d", name, id, id);
368 hb_value_t *gval = NULL;
369 hb_value_t *current = queue_peek_head(pd->stack);
370 hb_value_type_t gtype = 0;
371 const char *value;
372 if (pd->value != NULL)
373 value = pd->value;
374 else
375 value = "";
376 switch (id)
378 case P_PLIST:
379 { // Ignore
380 } break;
381 case P_KEY:
383 if (pd->key) free(pd->key);
384 pd->key = strdup(value);
385 return;
386 } break;
387 case P_DICT:
389 queue_pop_head(pd->stack);
390 } break;
391 case P_ARRAY:
393 queue_pop_head(pd->stack);
394 } break;
395 case P_INTEGER:
397 uint64_t val = strtoll(value, NULL, 0);
398 gval = hb_value_int(val);
399 } break;
400 case P_REAL:
402 double val = strtod(value, NULL);
403 gval = hb_value_double(val);
404 } break;
405 case P_STRING:
407 gval = hb_value_string(value);
408 } break;
409 case P_TRUE:
411 gval = hb_value_bool(1);
412 } break;
413 case P_FALSE:
415 gval = hb_value_bool(0);
416 } break;
417 default:
419 hb_error("Unhandled plist type %d", id);
420 } break;
422 if (gval)
424 // Get the top of the data structure stack and if it's an array
425 // or dict, add the current element
426 if (current == NULL)
428 pd->plist = gval;
429 pd->closed_top = 1;
430 return;
432 gtype = hb_value_type(current);
433 if (gtype == HB_VALUE_TYPE_ARRAY)
435 hb_value_array_append(current, gval);
437 else if (gtype == HB_VALUE_TYPE_DICT)
439 if (pd->key == NULL)
441 hb_error("No key for dictionary item");
442 hb_value_free(&gval);
444 else
446 hb_dict_set(current, pd->key, gval);
449 else
451 hb_error("Invalid container type. This shouldn't happen");
454 if (queue_is_empty(pd->stack))
455 pd->closed_top = 1;
458 static void
459 text_data(
460 void *ud,
461 const xmlChar *xtext,
462 int len)
464 char *text = (char*)xtext;
465 parse_data_t *pd = (parse_data_t*)ud;
467 int pos = 0;
468 if (pd->value != NULL)
470 pos = strlen(pd->value);
472 char *tmp = realloc(pd->value, pos + len + 1);
473 if (tmp == NULL)
474 return;
475 pd->value = tmp;
476 strncpy(pd->value + pos, text, len);
477 pd->value[pos + len] = 0;
480 static void
481 parse_warning(void *ud, const char *msg, ...)
483 va_list args;
485 va_start(args, msg);
486 hb_valog(0, "Plist parse warning: ", msg, args);
487 va_end(args);
490 static void
491 parse_error(void *ud, const char *msg, ...)
493 va_list args;
495 va_start(args, msg);
496 hb_valog(0, "Plist parse error: ", msg, args);
497 va_end(args);
500 hb_value_t*
501 hb_plist_parse(const char *buf, size_t len)
503 xmlSAXHandler parser;
504 parse_data_t pd;
506 pd.stack = queue_new();
507 pd.tag_stack = queue_new();
508 pd.key = NULL;
509 pd.value = NULL;
510 pd.plist = NULL;
511 pd.closed_top = 0;
513 memset(&parser, 0, sizeof(parser));
514 parser.initialized = XML_SAX2_MAGIC;
515 parser.startElement = start_element;
516 parser.endElement = end_element;
517 parser.characters = text_data;
518 parser.warning = parse_warning;
519 parser.error = parse_error;
520 int result = xmlSAXUserParseMemory(&parser, &pd, buf, len);
521 if (result != 0)
523 hb_error("Plist parse failed");
524 return NULL;
526 xmlCleanupParser();
528 if (pd.key) free(pd.key);
529 if (pd.value) free(pd.value);
530 queue_free(&pd.stack);
531 queue_free(&pd.tag_stack);
533 return pd.plist;
536 hb_value_t*
537 hb_plist_parse_file(const char *filename)
539 char *buffer;
540 size_t size;
541 hb_value_t *gval;
542 FILE *fd;
544 fd = fopen(filename, "r");
545 if (fd == NULL)
547 // File doesn't exist
548 return NULL;
550 fseek(fd, 0, SEEK_END);
551 size = ftell(fd);
552 fseek(fd, 0, SEEK_SET);
553 buffer = malloc(size+1);
554 size = fread(buffer, 1, size, fd);
555 buffer[size] = 0;
556 gval = hb_plist_parse(buffer, size);
557 free(buffer);
558 fclose(fd);
559 return gval;
562 static void
563 indent_fprintf(FILE *file, int indent, const char *fmt, ...)
565 va_list ap;
567 for (; indent; indent--)
568 putc('\t', file);
569 va_start(ap, fmt);
570 vfprintf(file, fmt, ap);
571 va_end(ap);
574 static void
575 gval_write(FILE *file, hb_value_t *gval)
577 static int indent = 0;
578 int ii;
579 hb_value_type_t gtype;
581 if (gval == NULL) return;
582 gtype = hb_value_type(gval);
583 if (gtype == HB_VALUE_TYPE_ARRAY)
585 hb_value_t *val;
586 int count;
588 indent_fprintf(file, indent, "<array>\n");
589 indent++;
590 count = hb_value_array_len(gval);
591 for (ii = 0; ii < count; ii++)
593 val = hb_value_array_get(gval, ii);
594 gval_write(file, val);
596 indent--;
597 indent_fprintf(file, indent, "</array>\n");
599 else if (gtype == HB_VALUE_TYPE_DICT)
601 const char *key;
602 hb_value_t *val;
603 hb_dict_iter_t iter;
605 indent_fprintf(file, indent, "<dict>\n");
606 indent++;
608 for (iter = hb_dict_iter_init(gval);
609 iter != HB_DICT_ITER_DONE;
610 iter = hb_dict_iter_next(gval, iter))
612 key = hb_dict_iter_key(iter);
613 val = hb_dict_iter_value(iter);
614 indent_fprintf(file, indent, "<key>%s</key>\n", key);
615 gval_write(file, val);
618 indent--;
619 indent_fprintf(file, indent, "</dict>\n");
621 else if (gtype == HB_VALUE_TYPE_BOOL)
623 char *tag;
624 if (hb_value_get_bool(gval))
626 tag = "true";
628 else
630 tag = "false";
632 indent_fprintf(file, indent, "<%s />\n", tag);
634 else if (gtype == HB_VALUE_TYPE_DOUBLE)
636 double val = hb_value_get_double(gval);
637 indent_fprintf(file, indent, "<real>%.17g</real>\n", val);
639 else if (gtype == HB_VALUE_TYPE_INT)
641 int64_t val = hb_value_get_int(gval);
642 indent_fprintf(file, indent, "<integer>%"PRId64"</integer>\n", val);
644 else if (gtype == HB_VALUE_TYPE_STRING)
646 const char *str = hb_value_get_string(gval);
647 char *esc = markup_escape_text(str);
648 indent_fprintf(file, indent, "<string>%s</string>\n", esc);
649 free(esc);
651 else
653 // Try to make anything thats unrecognized into a string
654 hb_error("Unhandled data type %d", gtype);
658 void
659 hb_plist_write(FILE *file, hb_value_t *gval)
661 fprintf(file, "%s", preamble);
662 gval_write(file, gval);
663 fprintf(file, "%s", postfix);
666 void
667 hb_plist_write_file(const char *filename, hb_value_t *gval)
669 FILE *file;
671 file = fopen(filename, "w");
672 if (file == NULL)
673 return;
675 hb_plist_write(file, gval);
676 fclose(file);
680 #if defined(PL_TEST)
682 main(int argc, char *argv[])
684 hb_value_t *gval;
686 file = fopen(argv[1], "r");
687 gval = hb_plist_parse_file(file);
688 if (argc > 2)
689 hb_plist_write_file(argv[2], gval);
690 else
691 hb_plist_write(stdout, gval);
692 if (file) fclose (file);
693 return 0;
695 #endif