/* * plist.c * XML plist implementation * * Copyright (c) 2008 Jonathan Beck All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "utils.h" #include "plist.h" #include #include #include #include #include const char *plist_base = "\n\ \n\ \n\ \0"; /** Formats a block of text to be a given indentation and width. * * The total width of the return string will be depth + cols. * * @param buf The string to format. * @param cols The number of text columns for returned block of text. * @param depth The number of tabs to indent the returned block of text. * * @return The formatted string. */ char *format_string(const char *buf, int cols, int depth) { int colw = depth + cols + 1; int len = strlen(buf); int nlines = len / cols + 1; char *new_buf = (char *) malloc(nlines * colw + depth + 1); int i = 0; int j = 0; assert(cols >= 0); assert(depth >= 0); // Inserts new lines and tabs at appropriate locations for (i = 0; i < nlines; i++) { new_buf[i * colw] = '\n'; for (j = 0; j < depth; j++) new_buf[i * colw + 1 + j] = '\t'; memcpy(new_buf + i * colw + 1 + depth, buf + i * cols, cols); } new_buf[len + (1 + depth) * nlines] = '\n'; // Inserts final row of indentation and termination character for (j = 0; j < depth; j++) new_buf[len + (1 + depth) * nlines + 1 + j] = '\t'; new_buf[len + (1 + depth) * nlines + depth + 1] = '\0'; return new_buf; } struct xml_node { xmlNodePtr xml; uint32_t depth; }; /** Creates a new plist XML document. * * @return The plist XML document. */ xmlDocPtr new_xml_plist() { char *plist = strdup(plist_base); xmlDocPtr plist_xml = xmlReadMemory(plist, strlen(plist), NULL, NULL, 0); if (!plist_xml) return NULL; free(plist); return plist_xml; } /** Destroys a previously created XML document. * * @param plist The XML document to destroy. */ void free_plist(xmlDocPtr plist) { if (!plist) return; xmlFreeDoc(plist); } void node_to_xml(GNode * node, gpointer xml_struct) { if (!node) return; struct xml_node *xstruct = (struct xml_node *) xml_struct; struct plist_data *node_data = (struct plist_data *) node->data; xmlNodePtr child_node = NULL; char isStruct = FALSE; gchar *tag = NULL; gchar *val = NULL; switch (node_data->type) { case PLIST_BOOLEAN: { if (node_data->boolval) tag = "true"; else tag = "false"; } break; case PLIST_UINT: tag = "integer"; val = g_strdup_printf("%lu", (long unsigned int) node_data->intval); break; case PLIST_REAL: tag = "real"; val = g_strdup_printf("%Lf", (long double) node_data->realval); break; case PLIST_STRING: tag = "string"; val = g_strdup(node_data->strval); break; case PLIST_UNICODE: tag = "string"; val = g_strdup((gchar *) node_data->unicodeval); break; case PLIST_KEY: tag = "key"; val = g_strdup((gchar *) node_data->strval); break; case PLIST_DATA: tag = "data"; gchar *valtmp = g_base64_encode(node_data->buff, node_data->length); val = format_string(valtmp, 60, xstruct->depth); g_free(valtmp); break; case PLIST_ARRAY: tag = "array"; isStruct = TRUE; break; case PLIST_DICT: tag = "dict"; isStruct = TRUE; break; case PLIST_DATE: //TODO : handle date tag default: break; } int i = 0; for (i = 0; i < xstruct->depth; i++) { xmlNodeAddContent(xstruct->xml, "\t"); } child_node = xmlNewChild(xstruct->xml, NULL, tag, val); xmlNodeAddContent(xstruct->xml, "\n"); g_free(val); //add return for structured types if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT || node_data->type == PLIST_DATA) xmlNodeAddContent(child_node, "\n"); if (isStruct) { struct xml_node child = { child_node, xstruct->depth + 1 }; g_node_children_foreach(node, G_TRAVERSE_ALL, node_to_xml, &child); } //fix indent for structured types if (node_data->type == PLIST_ARRAY || node_data->type == PLIST_DICT || node_data->type == PLIST_DATA) { for (i = 0; i < xstruct->depth; i++) { xmlNodeAddContent(child_node, "\t"); } } return; } void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node) { xmlNodePtr node = NULL; for (node = xml_node->children; node; node = node->next) { while (node && !xmlStrcmp(node->name, "text")) node = node->next; if (!node) break; struct plist_data *data = (struct plist_data *) calloc(sizeof(struct plist_data), 1); GNode *subnode = g_node_new(data); if (*plist_node) g_node_append(*plist_node, subnode); else *plist_node = subnode; if (!xmlStrcmp(node->name, "true")) { data->boolval = 1; data->type = PLIST_BOOLEAN; continue; } if (!xmlStrcmp(node->name, "false")) { data->boolval = 0; data->type = PLIST_BOOLEAN; continue; } if (!xmlStrcmp(node->name, "integer")) { char *strval = xmlNodeGetContent(node); data->intval = g_ascii_strtoull(strval, NULL, 0); data->type = PLIST_UINT; continue; } if (!xmlStrcmp(node->name, "real")) { char *strval = xmlNodeGetContent(node); data->realval = atof(strval); data->type = PLIST_REAL; continue; } if (!xmlStrcmp(node->name, "date")) continue; //TODO : handle date tag if (!xmlStrcmp(node->name, "string")) { data->strval = strdup(xmlNodeGetContent(node)); data->type = PLIST_STRING; continue; } if (!xmlStrcmp(node->name, "key")) { data->strval = strdup(xmlNodeGetContent(node)); data->type = PLIST_KEY; continue; } if (!xmlStrcmp(node->name, "data")) { gsize size = 0; data->buff = g_base64_decode(xmlNodeGetContent(node), &size); data->length = size; data->type = PLIST_DATA; continue; } if (!xmlStrcmp(node->name, "array")) { data->type = PLIST_ARRAY; xml_to_node(node, &subnode); continue; } if (!xmlStrcmp(node->name, "dict")) { data->type = PLIST_DICT; xml_to_node(node, &subnode); continue; } } } void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) { if (!plist || !plist_xml || *plist_xml) return; xmlDocPtr plist_doc = new_xml_plist(); xmlNodePtr root_node = xmlDocGetRootElement(plist_doc); struct xml_node root = { root_node, 0 }; node_to_xml(plist, &root); xmlDocDumpMemory(plist_doc, (xmlChar **) plist_xml, length); } void xml_to_plist(const char *plist_xml, uint32_t length, plist_t * plist) { xmlDocPtr plist_doc = xmlReadMemory(plist_xml, length, NULL, NULL, 0); xmlNodePtr root_node = xmlDocGetRootElement(plist_doc); xml_to_node(root_node, plist); }