summaryrefslogtreecommitdiffstats
path: root/libhb/plist.c
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2015-05-06 16:04:08 +0000
committerjstebbins <[email protected]>2015-05-06 16:04:08 +0000
commit9c0e97cc2c369e0c720441d182bbde20210742f4 (patch)
tree8b8f74ed24d0f617c9980d9043ebd53c33c531f3 /libhb/plist.c
parent81bcee10d2ad88f44c0f7791f2dd8da4ee2c1b76 (diff)
libhb,cli: add preset management to libhb, use it in cli
This results in custom preset support in the CLI and additional command line options to fully support all preset keys. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7158 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/plist.c')
-rw-r--r--libhb/plist.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/libhb/plist.c b/libhb/plist.c
new file mode 100644
index 000000000..f61014537
--- /dev/null
+++ b/libhb/plist.c
@@ -0,0 +1,678 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include "libxml/parser.h"
+
+#include "common.h"
+#include "hb_dict.h"
+#include "plist.h"
+
+#define BUF_SZ (128*1024)
+
+static char *preamble =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n";
+static char *postfix =
+ "</plist>\n";
+
+typedef struct queue_item_s queue_item_t;
+struct queue_item_s
+{
+ void *value;
+ queue_item_t *next;
+};
+
+typedef struct
+{
+ queue_item_t *head;
+} queue_t;
+
+queue_t * queue_new()
+{
+ return calloc(1, sizeof(queue_t));
+}
+
+void queue_free(queue_t **_q)
+{
+ queue_t *q = *_q;
+ if (q == NULL)
+ return;
+
+ queue_item_t *n, *i = q->head;
+ while (i != NULL)
+ {
+ n = i->next;
+ free(i);
+ i = n;
+ }
+ free(q);
+ *_q = NULL;
+}
+
+void queue_push_head(queue_t *q, void *v)
+{
+ queue_item_t *i = calloc(1, sizeof(queue_item_t));
+ i->value = v;
+ i->next = q->head;
+ q->head = i;
+}
+
+void * queue_peek_head(queue_t *q)
+{
+ if (q->head != NULL)
+ return q->head->value;
+ return NULL;
+}
+
+void * queue_pop_head(queue_t *q)
+{
+ void *result;
+ queue_item_t *i;
+
+ if (q->head == NULL)
+ return NULL;
+
+ i = q->head;
+ result = i->value;
+ q->head = i->next;
+ free(i);
+
+ return result;
+}
+
+int queue_is_empty(queue_t *q)
+{
+ return q->head == NULL;
+}
+
+char * markup_escape_text(const char *str)
+{
+ int ii, jj;
+ int len = strlen(str);
+ int step = 40;
+ int alloc = len + step;
+ char *markup = malloc(alloc);
+
+ for (ii = 0, jj = 0; ii < len; ii++)
+ {
+ if (jj > alloc - 8)
+ {
+ alloc += step;
+ char *tmp = realloc(markup, alloc);
+ if (tmp == NULL)
+ {
+ markup[jj] = 0;
+ return markup;
+ }
+ markup = tmp;
+ }
+ switch (str[ii])
+ {
+ case '<':
+ markup[jj++] = '&';
+ markup[jj++] = 'l';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '>':
+ markup[jj++] = '&';
+ markup[jj++] = 'g';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '\'':
+ markup[jj++] = '&';
+ markup[jj++] = 'a';
+ markup[jj++] = 'p';
+ markup[jj++] = 'o';
+ markup[jj++] = 's';
+ markup[jj++] = ';';
+ break;
+ case '"':
+ markup[jj++] = '&';
+ markup[jj++] = 'q';
+ markup[jj++] = 'u';
+ markup[jj++] = 'o';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '&':
+ markup[jj++] = '&';
+ markup[jj++] = 'a';
+ markup[jj++] = 'm';
+ markup[jj++] = 'p';
+ markup[jj++] = ';';
+ break;
+ default:
+ markup[jj++] = str[ii];
+ break;
+ }
+ markup[jj] = 0;
+ }
+ return markup;
+}
+
+enum
+{
+ P_NONE = 0,
+ P_PLIST,
+ P_KEY,
+ P_ARRAY,
+ P_DICT,
+ P_INTEGER,
+ P_REAL,
+ P_STRING,
+ P_DATE,
+ P_TRUE,
+ P_FALSE,
+ P_DATA,
+};
+
+typedef struct
+{
+ char *tag;
+ int id;
+} tag_map_t;
+
+static tag_map_t tag_map[] =
+{
+ {"plist", P_PLIST},
+ {"key", P_KEY},
+ {"array", P_ARRAY},
+ {"dict", P_DICT},
+ {"integer", P_INTEGER},
+ {"real", P_REAL},
+ {"string", P_STRING},
+ {"date", P_DATE},
+ {"true", P_TRUE},
+ {"false", P_FALSE},
+ {"data", P_DATA},
+};
+#define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t))
+
+typedef struct
+{
+ char *key;
+ char *value;
+ hb_value_t *plist;
+ queue_t *stack;
+ queue_t *tag_stack;
+ int closed_top;
+} parse_data_t;
+
+static void
+start_element(
+ void *ud,
+ const xmlChar *xname,
+ const xmlChar **attr_names)
+{
+ char *name = (char*)xname;
+ parse_data_t *pd = (parse_data_t*)ud;
+ union
+ {
+ int id;
+ void * pid;
+ } id;
+ int ii;
+
+ // Check to see if the first element found has been closed
+ // If so, ignore any junk following it.
+ if (pd->closed_top)
+ return;
+
+ for (ii = 0; ii < TAG_MAP_SZ; ii++)
+ {
+ if (strcmp(name, tag_map[ii].tag) == 0)
+ {
+ id.id = tag_map[ii].id;
+ break;
+ }
+ }
+ if (ii == TAG_MAP_SZ)
+ {
+ hb_error("Unrecognized start tag (%s)", name);
+ return;
+ }
+ if (pd->value)
+ {
+ free(pd->value);
+ pd->value = NULL;
+ }
+ queue_push_head(pd->tag_stack, id.pid);
+ hb_value_type_t gtype = 0;
+ hb_value_t *gval = NULL;
+ hb_value_t *current = queue_peek_head(pd->stack);
+ switch (id.id)
+ {
+ case P_PLIST:
+ { // Ignore
+ } break;
+ case P_KEY:
+ {
+ if (pd->key) free(pd->key);
+ pd->key = NULL;
+ } break;
+ case P_DICT:
+ {
+ gval = hb_dict_init();
+ queue_push_head(pd->stack, gval);
+ } break;
+ case P_ARRAY:
+ {
+ gval = hb_value_array_init();
+ queue_push_head(pd->stack, gval);
+ } break;
+ case P_INTEGER:
+ {
+ } break;
+ case P_REAL:
+ {
+ } break;
+ case P_STRING:
+ {
+ } break;
+ case P_DATE:
+ {
+ } break;
+ case P_TRUE:
+ {
+ } break;
+ case P_FALSE:
+ {
+ } break;
+ case P_DATA:
+ {
+ } break;
+ }
+ // Add the element to the current container
+ if (gval)
+ { // There's an element to add
+ if (current == NULL)
+ {
+ pd->plist = gval;
+ return;
+ }
+ gtype = hb_value_type(current);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_array_append(current, gval);
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ if (pd->key == NULL)
+ {
+ hb_error("No key for dictionary item");
+ hb_value_free(&gval);
+ }
+ else
+ {
+ hb_dict_set(current, pd->key, gval);
+ }
+ }
+ else
+ {
+ hb_error("Invalid container type. This shouldn't happen");
+ }
+ }
+}
+
+static void
+end_element(
+ void *ud,
+ const xmlChar *xname)
+{
+ char *name = (char*)xname;
+ parse_data_t *pd = (parse_data_t*)ud;
+ int id;
+ union
+ {
+ int id;
+ void * pid;
+ } start_id;
+ int ii;
+
+ // Check to see if the first element found has been closed
+ // If so, ignore any junk following it.
+ if (pd->closed_top)
+ return;
+
+ for (ii = 0; ii < TAG_MAP_SZ; ii++)
+ {
+ if (strcmp(name, tag_map[ii].tag) == 0)
+ {
+ id = tag_map[ii].id;
+ break;
+ }
+ }
+ if (ii == TAG_MAP_SZ)
+ {
+ hb_error("Unrecognized start tag (%s)", name);
+ return;
+ }
+ start_id.pid = queue_pop_head(pd->tag_stack);
+ if (start_id.id != id)
+ hb_error("start tag != end tag: (%s %d) %d", name, id, id);
+
+ hb_value_t *gval = NULL;
+ hb_value_t *current = queue_peek_head(pd->stack);
+ hb_value_type_t gtype = 0;
+ const char *value;
+ if (pd->value != NULL)
+ value = pd->value;
+ else
+ value = "";
+ switch (id)
+ {
+ case P_PLIST:
+ { // Ignore
+ } break;
+ case P_KEY:
+ {
+ if (pd->key) free(pd->key);
+ pd->key = strdup(value);
+ return;
+ } break;
+ case P_DICT:
+ {
+ queue_pop_head(pd->stack);
+ } break;
+ case P_ARRAY:
+ {
+ queue_pop_head(pd->stack);
+ } break;
+ case P_INTEGER:
+ {
+ uint64_t val = strtoll(value, NULL, 0);
+ gval = hb_value_int(val);
+ } break;
+ case P_REAL:
+ {
+ double val = strtod(value, NULL);
+ gval = hb_value_double(val);
+ } break;
+ case P_STRING:
+ {
+ gval = hb_value_string(value);
+ } break;
+ case P_TRUE:
+ {
+ gval = hb_value_bool(1);
+ } break;
+ case P_FALSE:
+ {
+ gval = hb_value_bool(0);
+ } break;
+ default:
+ {
+ hb_error("Unhandled plist type %d", id);
+ } break;
+ }
+ if (gval)
+ {
+ // Get the top of the data structure stack and if it's an array
+ // or dict, add the current element
+ if (current == NULL)
+ {
+ pd->plist = gval;
+ pd->closed_top = 1;
+ return;
+ }
+ gtype = hb_value_type(current);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_array_append(current, gval);
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ if (pd->key == NULL)
+ {
+ hb_error("No key for dictionary item");
+ hb_value_free(&gval);
+ }
+ else
+ {
+ hb_dict_set(current, pd->key, gval);
+ }
+ }
+ else
+ {
+ hb_error("Invalid container type. This shouldn't happen");
+ }
+ }
+ if (queue_is_empty(pd->stack))
+ pd->closed_top = 1;
+}
+
+static void
+text_data(
+ void *ud,
+ const xmlChar *xtext,
+ int len)
+{
+ char *text = (char*)xtext;
+ parse_data_t *pd = (parse_data_t*)ud;
+ if (pd->value) free(pd->value);
+ pd->value = malloc(len + 1);
+ strncpy(pd->value, text, len);
+ pd->value[len] = 0;
+}
+
+static void
+parse_warning(void *ud, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ hb_valog(0, "Plist parse warning: ", msg, args);
+ va_end(args);
+}
+
+static void
+parse_error(void *ud, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ hb_valog(0, "Plist parse error: ", msg, args);
+ va_end(args);
+}
+
+hb_value_t*
+hb_plist_parse(const char *buf, size_t len)
+{
+ xmlSAXHandler parser;
+ parse_data_t pd;
+
+ pd.stack = queue_new();
+ pd.tag_stack = queue_new();
+ pd.key = NULL;
+ pd.value = NULL;
+ pd.plist = NULL;
+ pd.closed_top = 0;
+
+ memset(&parser, 0, sizeof(parser));
+ parser.initialized = XML_SAX2_MAGIC;
+ parser.startElement = start_element;
+ parser.endElement = end_element;
+ parser.characters = text_data;
+ parser.warning = parse_warning;
+ parser.error = parse_error;
+ int result = xmlSAXUserParseMemory(&parser, &pd, buf, len);
+ if (result != 0)
+ {
+ hb_error("Plist parse failed");
+ return NULL;
+ }
+ xmlCleanupParser();
+
+ if (pd.key) free(pd.key);
+ if (pd.value) free(pd.value);
+ queue_free(&pd.stack);
+ queue_free(&pd.tag_stack);
+
+ return pd.plist;
+}
+
+hb_value_t*
+hb_plist_parse_file(const char *filename)
+{
+ char *buffer;
+ size_t size;
+ hb_value_t *gval;
+ FILE *fd;
+
+ fd = fopen(filename, "r");
+ if (fd == NULL)
+ {
+ // File doesn't exist
+ return NULL;
+ }
+ fseek(fd, 0, SEEK_END);
+ size = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ buffer = malloc(size+1);
+ size = fread(buffer, 1, size, fd);
+ buffer[size] = 0;
+ gval = hb_plist_parse(buffer, size);
+ free(buffer);
+ fclose(fd);
+ return gval;
+}
+
+static void
+indent_fprintf(FILE *file, int indent, const char *fmt, ...)
+{
+ va_list ap;
+
+ for (; indent; indent--)
+ putc('\t', file);
+ va_start(ap, fmt);
+ vfprintf(file, fmt, ap);
+ va_end(ap);
+}
+
+static void
+gval_write(FILE *file, hb_value_t *gval)
+{
+ static int indent = 0;
+ int ii;
+ hb_value_type_t gtype;
+
+ if (gval == NULL) return;
+ gtype = hb_value_type(gval);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_t *val;
+ int count;
+
+ indent_fprintf(file, indent, "<array>\n");
+ indent++;
+ count = hb_value_array_len(gval);
+ for (ii = 0; ii < count; ii++)
+ {
+ val = hb_value_array_get(gval, ii);
+ gval_write(file, val);
+ }
+ indent--;
+ indent_fprintf(file, indent, "</array>\n");
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ const char *key;
+ hb_value_t *val;
+ hb_dict_iter_t iter;
+
+ indent_fprintf(file, indent, "<dict>\n");
+ indent++;
+
+ for (iter = hb_dict_iter_init(gval);
+ iter != HB_DICT_ITER_DONE;
+ iter = hb_dict_iter_next(gval, iter))
+ {
+ key = hb_dict_iter_key(iter);
+ val = hb_dict_iter_value(iter);
+ indent_fprintf(file, indent, "<key>%s</key>\n", key);
+ gval_write(file, val);
+ }
+
+ indent--;
+ indent_fprintf(file, indent, "</dict>\n");
+ }
+ else if (gtype == HB_VALUE_TYPE_BOOL)
+ {
+ char *tag;
+ if (hb_value_get_bool(gval))
+ {
+ tag = "true";
+ }
+ else
+ {
+ tag = "false";
+ }
+ indent_fprintf(file, indent, "<%s />\n", tag);
+ }
+ else if (gtype == HB_VALUE_TYPE_DOUBLE)
+ {
+ double val = hb_value_get_double(gval);
+ indent_fprintf(file, indent, "<real>%.17g</real>\n", val);
+ }
+ else if (gtype == HB_VALUE_TYPE_INT)
+ {
+ int64_t val = hb_value_get_int(gval);
+ indent_fprintf(file, indent, "<integer>%"PRId64"</integer>\n", val);
+ }
+ else if (gtype == HB_VALUE_TYPE_STRING)
+ {
+ const char *str = hb_value_get_string(gval);
+ char *esc = markup_escape_text(str);
+ indent_fprintf(file, indent, "<string>%s</string>\n", esc);
+ free(esc);
+ }
+ else
+ {
+ // Try to make anything thats unrecognized into a string
+ hb_error("Unhandled data type %d", gtype);
+ }
+}
+
+void
+hb_plist_write(FILE *file, hb_value_t *gval)
+{
+ fprintf(file, "%s", preamble);
+ gval_write(file, gval);
+ fprintf(file, "%s", postfix);
+}
+
+void
+hb_plist_write_file(const char *filename, hb_value_t *gval)
+{
+ FILE *file;
+
+ file = fopen(filename, "w");
+ if (file == NULL)
+ return;
+
+ hb_plist_write(file, gval);
+ fclose(file);
+}
+
+
+#if defined(PL_TEST)
+int
+main(int argc, char *argv[])
+{
+ hb_value_t *gval;
+
+ file = fopen(argv[1], "r");
+ gval = hb_plist_parse_file(file);
+ if (argc > 2)
+ hb_plist_write_file(argv[2], gval);
+ else
+ hb_plist_write(stdout, gval);
+ if (file) fclose (file);
+ return 0;
+}
+#endif