6 #include <glib/gstdio.h>
7 #include <glib-object.h>
12 #define BUF_SZ (128*1024)
14 static gchar *preamble =
15 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
16 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
17 "<plist version=\"1.0\">\n";
18 static gchar *postfix =
43 static tag_map_t tag_map[] =
49 {"integer", P_INTEGER},
57 #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t))
71 GMarkupParseContext *ctx,
73 const gchar **attr_names,
74 const gchar **attr_values,
78 parse_data_t *pd = (parse_data_t*)ud;
86 // Check to see if the first element found has been closed
87 // If so, ignore any junk following it.
91 for (ii = 0; ii < TAG_MAP_SZ; ii++)
93 if (strcmp(name, tag_map[ii].tag) == 0)
95 id.id = tag_map[ii].id;
101 g_warning("Unrecognized start tag (%s)", name);
104 g_queue_push_head(pd->tag_stack, id.pid);
107 GValue *current = g_queue_peek_head(pd->stack);
115 if (pd->key) g_free(pd->key);
120 gval = ghb_dict_value_new();
121 g_queue_push_head(pd->stack, gval);
125 gval = ghb_array_value_new(128);
126 g_queue_push_head(pd->stack, gval);
150 // Add the element to the current container
152 { // There's an element to add
158 gtype = G_VALUE_TYPE(current);
159 if (gtype == ghb_array_get_type())
161 ghb_array_append(current, gval);
163 else if (gtype == ghb_dict_get_type())
167 g_warning("No key for dictionary item");
168 ghb_value_free(gval);
172 ghb_dict_insert(current, g_strdup(pd->key), gval);
177 g_error("Invalid container type. This shouldn't happen");
184 GMarkupParseContext *ctx,
189 parse_data_t *pd = (parse_data_t*)ud;
198 // Check to see if the first element found has been closed
199 // If so, ignore any junk following it.
203 for (ii = 0; ii < TAG_MAP_SZ; ii++)
205 if (strcmp(name, tag_map[ii].tag) == 0)
211 if (ii == TAG_MAP_SZ)
213 g_warning("Unrecognized start tag (%s)", name);
216 start_id.pid = g_queue_pop_head(pd->tag_stack);
217 if (start_id.id != id)
218 g_warning("start tag != end tag: (%s %d) %d", name, id, id);
221 GValue *current = g_queue_peek_head(pd->stack);
230 if (pd->key) g_free(pd->key);
231 pd->key = g_strdup(pd->value);
236 g_queue_pop_head(pd->stack);
240 g_queue_pop_head(pd->stack);
244 gint64 val = g_strtod(pd->value, NULL);
245 gval = ghb_int64_value_new(val);
249 gdouble val = g_strtod(pd->value, NULL);
250 gval = ghb_double_value_new(val);
254 gval = ghb_string_value_new(pd->value);
260 g_time_val_from_iso8601(pd->value, &time);
261 g_date_set_time_val(&date, &time);
262 gval = ghb_date_value_new(&date);
266 gval = ghb_boolean_value_new(TRUE);
270 gval = ghb_boolean_value_new(FALSE);
275 data = g_malloc(sizeof(ghb_rawdata_t));
276 data->data = g_base64_decode(pd->value, &(data->size));
277 gval = ghb_rawdata_value_new(data);
282 // Get the top of the data structure stack and if it's an array
283 // or dict, add the current element
287 pd->closed_top = TRUE;
290 gtype = G_VALUE_TYPE(current);
291 if (gtype == ghb_array_get_type())
293 ghb_array_append(current, gval);
295 else if (gtype == ghb_dict_get_type())
299 g_warning("No key for dictionary item");
300 ghb_value_free(gval);
304 ghb_dict_insert(current, g_strdup(pd->key), gval);
309 g_error("Invalid container type. This shouldn't happen");
312 if (g_queue_is_empty(pd->stack))
313 pd->closed_top = TRUE;
318 GMarkupParseContext *ctx,
324 parse_data_t *pd = (parse_data_t*)ud;
325 if (pd->value) g_free(pd->value);
326 pd->value = g_strdup(text);
331 GMarkupParseContext *ctx,
337 //parse_data_t *pd = (parse_data_t*)ud;
339 //g_debug("passthrough %s", text);
343 parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud)
345 g_warning("Plist parse error: %s", error->message);
348 // This is required or the parser crashes
350 destroy_notify(gpointer data)
352 //g_debug("destroy parser");
356 ghb_plist_parse(const gchar *buf, gssize len)
358 GMarkupParseContext *ctx;
359 GMarkupParser parser;
363 pd.stack = g_queue_new();
364 pd.tag_stack = g_queue_new();
368 pd.closed_top = FALSE;
370 parser.start_element = start_element;
371 parser.end_element = end_element;
372 parser.text = text_data;
373 parser.passthrough = passthrough;
374 parser.error = parse_error;
375 ctx = g_markup_parse_context_new(&parser, 0, &pd, destroy_notify);
377 g_markup_parse_context_parse(ctx, buf, len, &err);
378 g_markup_parse_context_end_parse(ctx, &err);
379 g_markup_parse_context_free(ctx);
380 if (pd.key) g_free(pd.key);
381 if (pd.value) g_free(pd.value);
382 g_queue_free(pd.stack);
383 g_queue_free(pd.tag_stack);
388 ghb_plist_parse_file(const gchar *filename)
395 fd = g_fopen(filename, "r");
398 g_warning("Plist parse: failed to open %s", filename);
401 fseek(fd, 0, SEEK_END);
403 fseek(fd, 0, SEEK_SET);
404 buffer = g_malloc(size+1);
405 size = fread(buffer, 1, size, fd);
407 gval = ghb_plist_parse(buffer, (gssize)size);
414 indent_fprintf(FILE *file, gint indent, const gchar *fmt, ...)
418 for (; indent; indent--)
421 vfprintf(file, fmt, ap);
425 // Used for sorting dictionaries.
427 key_cmp(gconstpointer a, gconstpointer b)
429 gchar *stra = (gchar*)a;
430 gchar *strb = (gchar*)b;
432 return strcmp(stra, strb);
436 gval_write(FILE *file, GValue *gval)
438 static gint indent = 0;
442 if (gval == NULL) return;
443 gtype = G_VALUE_TYPE(gval);
444 if (gtype == ghb_array_get_type())
449 indent_fprintf(file, indent, "<array>\n");
451 count = ghb_array_len(gval);
452 for (ii = 0; ii < count; ii++)
454 val = ghb_array_get_nth(gval, ii);
455 gval_write(file, val);
458 indent_fprintf(file, indent, "</array>\n");
460 else if (gtype == ghb_dict_get_type())
463 GHashTable *dict = g_value_get_boxed(gval);
465 keys = g_hash_table_get_keys(dict);
466 // Sort the dictionary. Not really necessray, but it makes
467 // finding things easier
468 keys = g_list_sort(keys, key_cmp);
470 indent_fprintf(file, indent, "<dict>\n");
474 gchar *key = (gchar*)link->data;
475 val = g_hash_table_lookup(dict, key);
476 indent_fprintf(file, indent, "<key>%s</key>\n", key);
477 gval_write(file, val);
481 indent_fprintf(file, indent, "</dict>\n");
484 else if (gtype == G_TYPE_BOOLEAN)
487 if (g_value_get_boolean(gval))
495 indent_fprintf(file, indent, "<%s />\n", tag);
497 else if (gtype == g_date_get_type())
500 date = g_value_get_boxed(gval);
501 indent_fprintf(file, indent, "<date>%d-%d-%d</date>\n",
502 g_date_get_year(date),
503 g_date_get_month(date),
507 else if (gtype == ghb_rawdata_get_type())
511 data = g_value_get_boxed(gval);
512 base64 = g_base64_encode(data->data, data->size);
513 indent_fprintf(file, indent, "<data>\n");
514 indent_fprintf(file, 0, "%s\n", base64);
515 indent_fprintf(file, indent, "</data>\n");
518 else if (gtype == G_TYPE_DOUBLE)
520 gdouble val = g_value_get_double(gval);
521 indent_fprintf(file, indent, "<real>%.17g</real>\n", val);
523 else if (gtype == G_TYPE_INT64)
525 gint val = g_value_get_int64(gval);
526 indent_fprintf(file, indent, "<integer>%d</integer>\n", val);
528 else if (gtype == G_TYPE_STRING)
530 const gchar *str = g_value_get_string(gval);
531 gchar *esc = g_markup_escape_text(str, -1);
532 indent_fprintf(file, indent, "<string>%s</string>\n", esc);
537 // Try to make anything thats unrecognized into a string
540 g_value_init(&val, G_TYPE_STRING);
541 if (g_value_transform(gval, &val))
543 str = g_value_get_string(&val);
544 gchar *esc = g_markup_escape_text(str, -1);
545 indent_fprintf(file, indent, "<string>%s</string>\n", esc);
550 g_message("failed to transform");
557 ghb_plist_write(FILE *file, GValue *gval)
559 fprintf(file, "%s", preamble);
560 gval_write(file, gval);
561 fprintf(file, "%s", postfix);
565 ghb_plist_write_file(const gchar *filename, GValue *gval)
569 file = fopen(filename, "w");
573 fprintf(file, "%s", preamble);
574 gval_write(file, gval);
575 fprintf(file, "%s", postfix);
581 main(gint argc, gchar *argv[])
587 file = g_fopen(argv[1], "r");
588 gval = ghb_plist_parse_file(file);
590 ghb_plist_write_file(argv[2], gval);
592 ghb_plist_write(stdout, gval);
593 if (file) fclose (file);