6 #include <glib-object.h>
11 #define BUF_SZ (128*1024)
13 static gchar *preamble =
14 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
15 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
16 "<plist version=\"1.0\">\n";
17 static gchar *postfix =
42 static tag_map_t tag_map[] =
48 {"integer", P_INTEGER},
56 #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t))
70 GMarkupParseContext *ctx,
72 const gchar **attr_names,
73 const gchar **attr_values,
77 parse_data_t *pd = (parse_data_t*)ud;
85 // Check to see if the first element found has been closed
86 // If so, ignore any junk following it.
90 for (ii = 0; ii < TAG_MAP_SZ; ii++)
92 if (strcmp(name, tag_map[ii].tag) == 0)
94 id.id = tag_map[ii].id;
100 g_warning("Unrecognized start tag (%s)", name);
103 g_queue_push_head(pd->tag_stack, id.pid);
106 GValue *current = g_queue_peek_head(pd->stack);
114 if (pd->key) g_free(pd->key);
119 gval = ghb_dict_value_new();
120 g_queue_push_head(pd->stack, gval);
124 gval = ghb_array_value_new(128);
125 g_queue_push_head(pd->stack, gval);
149 // Add the element to the current container
151 { // There's an element to add
157 gtype = G_VALUE_TYPE(current);
158 if (gtype == ghb_array_get_type())
160 ghb_array_append(current, gval);
162 else if (gtype == ghb_dict_get_type())
166 g_warning("No key for dictionary item");
167 ghb_value_free(gval);
171 ghb_dict_insert(current, g_strdup(pd->key), gval);
176 g_error("Invalid container type. This shouldn't happen");
183 GMarkupParseContext *ctx,
188 parse_data_t *pd = (parse_data_t*)ud;
197 // Check to see if the first element found has been closed
198 // If so, ignore any junk following it.
202 for (ii = 0; ii < TAG_MAP_SZ; ii++)
204 if (strcmp(name, tag_map[ii].tag) == 0)
210 if (ii == TAG_MAP_SZ)
212 g_warning("Unrecognized start tag (%s)", name);
215 start_id.pid = g_queue_pop_head(pd->tag_stack);
216 if (start_id.id != id)
217 g_warning("start tag != end tag: (%s %d) %d", name, id, id);
220 GValue *current = g_queue_peek_head(pd->stack);
229 if (pd->key) g_free(pd->key);
230 pd->key = g_strdup(pd->value);
235 g_queue_pop_head(pd->stack);
239 g_queue_pop_head(pd->stack);
243 gint64 val = g_strtod(pd->value, NULL);
244 gval = ghb_int64_value_new(val);
248 gdouble val = g_strtod(pd->value, NULL);
249 gval = ghb_double_value_new(val);
253 gval = ghb_string_value_new(pd->value);
259 g_time_val_from_iso8601(pd->value, &time);
260 g_date_set_time_val(&date, &time);
261 gval = ghb_date_value_new(&date);
265 gval = ghb_boolean_value_new(TRUE);
269 gval = ghb_boolean_value_new(FALSE);
274 data = g_malloc(sizeof(ghb_rawdata_t));
275 data->data = g_base64_decode(pd->value, &(data->size));
276 gval = ghb_rawdata_value_new(data);
281 // Get the top of the data structure stack and if it's an array
282 // or dict, add the current element
286 pd->closed_top = TRUE;
289 gtype = G_VALUE_TYPE(current);
290 if (gtype == ghb_array_get_type())
292 ghb_array_append(current, gval);
294 else if (gtype == ghb_dict_get_type())
298 g_warning("No key for dictionary item");
299 ghb_value_free(gval);
303 ghb_dict_insert(current, g_strdup(pd->key), gval);
308 g_error("Invalid container type. This shouldn't happen");
311 if (g_queue_is_empty(pd->stack))
312 pd->closed_top = TRUE;
317 GMarkupParseContext *ctx,
323 parse_data_t *pd = (parse_data_t*)ud;
324 if (pd->value) g_free(pd->value);
325 pd->value = g_strdup(text);
330 GMarkupParseContext *ctx,
336 //parse_data_t *pd = (parse_data_t*)ud;
338 //g_debug("passthrough %s", text);
342 parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud)
344 g_warning("Plist parse error: %s", error->message);
347 // This is required or the parser crashes
349 destroy_notify(gpointer data)
351 g_debug("destroy parser");
355 ghb_plist_parse(const gchar *buf, gssize len)
357 GMarkupParseContext *ctx;
358 GMarkupParser parser;
362 pd.stack = g_queue_new();
363 pd.tag_stack = g_queue_new();
367 pd.closed_top = FALSE;
369 parser.start_element = start_element;
370 parser.end_element = end_element;
371 parser.text = text_data;
372 parser.passthrough = passthrough;
373 parser.error = parse_error;
374 ctx = g_markup_parse_context_new(&parser, 0, &pd, destroy_notify);
376 g_markup_parse_context_parse(ctx, buf, len, &err);
377 g_markup_parse_context_end_parse(ctx, &err);
378 g_markup_parse_context_free(ctx);
379 g_queue_free(pd.stack);
380 g_queue_free(pd.tag_stack);
385 ghb_plist_parse_file(FILE *fd)
393 fseek(fd, 0, SEEK_END);
395 fseek(fd, 0, SEEK_SET);
396 buffer = g_malloc(size+1);
397 size = fread(buffer, 1, size, fd);
399 gval = ghb_plist_parse(buffer, (gssize)size);
405 indent_fprintf(FILE *file, gint indent, const gchar *fmt, ...)
409 for (; indent; indent--)
412 vfprintf(file, fmt, ap);
416 // Used for sorting dictionaries.
418 key_cmp(gconstpointer a, gconstpointer b)
420 gchar *stra = (gchar*)a;
421 gchar *strb = (gchar*)b;
423 return strcmp(stra, strb);
427 gval_write(FILE *file, GValue *gval)
429 static gint indent = 0;
433 if (gval == NULL) return;
434 gtype = G_VALUE_TYPE(gval);
435 if (gtype == ghb_array_get_type())
440 indent_fprintf(file, indent, "<array>\n");
442 count = ghb_array_len(gval);
443 for (ii = 0; ii < count; ii++)
445 val = ghb_array_get_nth(gval, ii);
446 gval_write(file, val);
449 indent_fprintf(file, indent, "</array>\n");
451 else if (gtype == ghb_dict_get_type())
454 GHashTable *dict = g_value_get_boxed(gval);
456 keys = g_hash_table_get_keys(dict);
457 // Sort the dictionary. Not really necessray, but it makes
458 // finding things easier
459 keys = g_list_sort(keys, key_cmp);
461 indent_fprintf(file, indent, "<dict>\n");
465 gchar *key = (gchar*)link->data;
466 val = g_hash_table_lookup(dict, key);
467 indent_fprintf(file, indent, "<key>%s</key>\n", key);
468 gval_write(file, val);
472 indent_fprintf(file, indent, "</dict>\n");
475 else if (gtype == G_TYPE_BOOLEAN)
478 if (g_value_get_boolean(gval))
486 indent_fprintf(file, indent, "<%s />\n", tag);
488 else if (gtype == g_date_get_type())
491 date = g_value_get_boxed(gval);
492 indent_fprintf(file, indent, "<date>%d-%d-%d</date>\n",
493 g_date_get_year(date),
494 g_date_get_month(date),
498 else if (gtype == ghb_rawdata_get_type())
502 data = g_value_get_boxed(gval);
503 base64 = g_base64_encode(data->data, data->size);
504 indent_fprintf(file, indent, "<data>\n");
505 indent_fprintf(file, 0, "%s\n", base64);
506 indent_fprintf(file, indent, "</data>\n");
509 else if (gtype == G_TYPE_DOUBLE)
511 gdouble val = g_value_get_double(gval);
512 indent_fprintf(file, indent, "<real>%.17g</real>\n", val);
514 else if (gtype == G_TYPE_INT64)
516 gint val = g_value_get_int64(gval);
517 indent_fprintf(file, indent, "<integer>%d</integer>\n", val);
519 else if (gtype == G_TYPE_STRING)
521 const gchar *str = g_value_get_string(gval);
522 gchar *esc = g_markup_escape_text(str, -1);
523 indent_fprintf(file, indent, "<string>%s</string>\n", esc);
528 // Try to make anything thats unrecognized into a string
531 g_value_init(&val, G_TYPE_STRING);
532 if (g_value_transform(gval, &val))
534 str = g_value_get_string(&val);
535 gchar *esc = g_markup_escape_text(str, -1);
536 indent_fprintf(file, indent, "<string>%s</string>\n", esc);
541 g_message("failed to transform");
548 ghb_plist_write(FILE *file, GValue *gval)
550 fprintf(file, "%s", preamble);
551 gval_write(file, gval);
552 fprintf(file, "%s", postfix);
556 ghb_plist_write_file(const gchar *filename, GValue *gval)
560 file = fopen(filename, "w");
564 fprintf(file, "%s", preamble);
565 gval_write(file, gval);
566 fprintf(file, "%s", postfix);
572 main(gint argc, gchar *argv[])
578 file = g_fopen(argv[1], "r");
579 gval = ghb_plist_parse_file(file);
581 ghb_plist_write_file(argv[2], gval);
583 ghb_plist_write(stdout, gval);
584 if (file) fclose (file);