aboutsummaryrefslogtreecommitdiff
path: root/lib/base/nxml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/base/nxml.cpp')
-rw-r--r--lib/base/nxml.cpp339
1 files changed, 339 insertions, 0 deletions
diff --git a/lib/base/nxml.cpp b/lib/base/nxml.cpp
new file mode 100644
index 00000000..f32880a6
--- /dev/null
+++ b/lib/base/nxml.cpp
@@ -0,0 +1,339 @@
+#include <lib/base/nconfig.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_LIBXML2
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#else
+#define xmlChar char
+#endif /* HAVE_LIBXML2 */
+
+#define DE(x) ((struct nc_de_s *) (data+(x)))
+#define IDE(x, y) (DE(((unsigned *) (data+(x)->offset))[(y)]))
+#define XML_DE ((const xmlChar *) "dirEntry")
+#define XML_NS ((const xmlChar *) "http://hq.alert.sk/projects/nconfig")
+#define XML_ROOT ((const xmlChar *) "NConfigExport")
+
+static char *encodeXml(const char *what)
+{
+ unsigned p = 0, size = 6*strlen(what)+1;
+ char *ret = (char *)malloc(size);
+ for (; *what; what++) {
+ switch (*what) {
+ case '"':
+ ret[p++] = '&';
+ ret[p++] = 'q';
+ ret[p++] = 'u';
+ ret[p++] = 'o';
+ ret[p++] = 't';
+ ret[p++] = ';';
+ continue;
+ case '>':
+ ret[p++] = '&';
+ ret[p++] = 'q';
+ ret[p++] = 't';
+ ret[p++] = ';';
+ continue;
+ case '<':
+ ret[p++] = '&';
+ ret[p++] = 'l';
+ ret[p++] = 't';
+ ret[p++] = ';';
+ continue;
+ case '&':
+ ret[p++] = '&';
+ ret[p++] = 'a';
+ ret[p++] = 'm';
+ ret[p++] = 'p';
+ ret[p++] = ';';
+ continue;
+ }
+ if (*what >= 0x20 || *what == '\n' || *what == '\r' || *what == '\t')
+ ret[p++] = *what;
+ else
+ p += sprintf(ret+p, "&#%d;", *what);
+ }
+ ret[p] = '\0';
+ return ret;
+}
+
+void NConfig::store(nc_de_s *de, FILE *f)
+{
+ struct nc_de_s *cc;
+ for (unsigned i=0; i<de->pages; i++)
+ if ((cc = IDE(de, i))->type) {
+ char *encname = encodeXml(data+cc->name);
+ fprintf(f, "<nc:%s name=\"%s\" type=\"%d\" value=\"", XML_DE, encname, cc->type);
+ free(encname);
+ switch (cc->type) {
+ case NC_DIR:
+ fprintf(f, "%u\">\n", cc->pages);
+ store(cc, f);
+ fprintf(f, "</nc:%s>\n", XML_DE);
+ break;
+ case NC_STRING:
+ fprintf(f, "%s\"/>\n", encname = encodeXml(data+cc->offset));
+ free(encname);
+ break;
+ case NC_INT:
+ fprintf(f, "%lld\"/>\n", *((signed long long *) (data+cc->offset)));
+ break;
+ case NC_UINT:
+ fprintf(f, "%llu\"/>\n", *((unsigned long long *) (data+cc->offset)));
+ break;
+ case NC_DOUBLE:
+ fprintf(f, "%La\"/>\n", *((long double *) (data+cc->offset)));
+ break;
+ case NC_RAW:
+ {
+ const char *raw = data+cc->offset;
+ for (unsigned j=0; j<cc->pages; j++)
+ fprintf(f, "%d%d%d", raw[j] / 100, (raw[j] % 100) / 10, raw[j] % 10);
+ fprintf(f, "\"/>\n");
+ }
+ }
+ }
+}
+
+int NConfig::toXML(const char *filename)
+{
+ if (fd < 0)
+ return NC_ERR_NFILE;
+
+ FILE *f = fopen(filename, "w");
+ if (!f)
+ return NC_ERR_PERM;
+
+ fprintf(f, "%s", "<?xml version=\"1.0\"?>\n");
+ fprintf(f, "<nc:%s xmlns:nc=\"%s\" libVersion=\"%s\"", XML_ROOT, XML_NS, VERSION);
+#ifdef HAVE_TIME_H
+ time_t t = time(NULL);
+ char *tim = ctime(&t);
+ tim[strlen(tim)-1] = 0;
+ fprintf(f, " time=\"%s\"", tim);
+#endif /* HAVE_TIME_H */
+ fprintf(f, ">\n");
+ lockFile(NC_L_RO);
+
+ store(rdir, f);
+
+ unLockFile();
+ fprintf(f, "</nc:%s>\n", XML_ROOT);
+ fclose(f);
+ return NC_ERR_OK;
+}
+
+#ifdef HAVE_LIBXML2
+static xmlSAXHandler sh;
+enum stateEnum {noRoot = 0, inRoot, inDir, inEnt, unknown};
+
+struct ncParseState {
+ stateEnum state, pState;
+ xmlChar *ns;
+ unsigned depth;
+ unsigned unDepth;
+ unsigned force;
+ NConfig *which;
+};
+
+static int ncXmlSAXParseFile(xmlSAXHandlerPtr sax, void *user_data, const char *filename)
+{
+ int ret = 0;
+ xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
+ if (!ctxt)
+ return -1;
+ ctxt->sax = sax;
+ ctxt->userData = user_data;
+ xmlParseDocument(ctxt);
+ ret = ctxt->wellFormed ? 0 : -1;
+ if (sax)
+ ctxt->sax = NULL;
+ xmlFreeParserCtxt(ctxt);
+ return ret;
+}
+
+static xmlEntityPtr ncXmlGetEntity(void *user_data, const CHAR *name)
+{
+ return xmlGetPredefinedEntity(name);
+}
+
+static void ncXmlStartElement(void *user_data, const CHAR *name, const CHAR **attrs)
+{
+ struct ncParseState *p = (struct ncParseState *)user_data;
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "New element %s state=%d %s\n", name, p->state, p->ns);
+#endif
+ if (p->state == unknown) {
+ p->unDepth++;
+ return;
+ }
+ if (p->state == noRoot) {
+ while (*attrs) {
+ if (!xmlStrncmp(*attrs, (const xmlChar *) "xmlns:", 6)) {
+ if (!xmlStrcmp(attrs[1], XML_NS)) {
+ p->ns = xmlStrdup((*attrs)+6);
+ break;
+ }
+ }
+ attrs += 2;
+ }
+ char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_ROOT)+2);
+ sprintf(b, "%s:%s", p->ns, XML_ROOT);
+ if (xmlStrcmp(name, (xmlChar *)b)) {
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "NewElement, entering unknown %s\n", name);
+#endif
+ p->pState = p->state;
+ p->state = unknown;
+ } else
+ p->state = inRoot;
+ free(b);
+ return;
+ }
+ if (p->state == inRoot || p->state == inDir) {
+ const xmlChar *value = NULL, *n = NULL;
+ int type = 0;
+ while (*attrs) {
+ if (!xmlStrcmp(*attrs, (const xmlChar *)"value"))
+ value = attrs[1];
+ if (!xmlStrcmp(*attrs, (const xmlChar *)"name"))
+ n = attrs[1];
+ if (!xmlStrcmp(*attrs, (const xmlChar *)"type"))
+ type = atoi(attrs[1]);
+ attrs += 2;
+ }
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "%s %s %s %d %d\n", name, n, value, type, p->state);
+#endif
+ char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_DE)+2);
+ sprintf(b, "%s:%s", p->ns, XML_DE);
+ if (xmlStrcmp(name, (xmlChar *)b) || !type || !value || !n) {
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "NewElement, entering unknown on mismatch\n");
+#endif
+ p->pState = p->state;
+ p->state = unknown;
+ free(b);
+ return;
+ }
+ free(b);
+ if (p->force)
+ p->which->delKey((const char *)n);
+
+ switch (type) {
+ case NC_DIR:
+ if (p->which->createDir((const char *)n, strtoul((const char *)value, NULL, 0)) != NC_ERR_OK) {
+ p->pState = p->state;
+ p->state = unknown;
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "NewElement, entering unknown on failed mkdir\n");
+#endif
+ return;
+ }
+ p->which->chDir((const char *)n);
+ break;
+ case NC_STRING:
+ p->which->setKey((const char *)n, (const char *)value);
+ break;
+ case NC_INT:
+ p->which->setKey((const char *)n, strtoll((const char *)value, NULL, 0));
+ break;
+ case NC_UINT:
+ p->which->setKey((const char *)n, strtoull((const char *)value, NULL, 0));
+ break;
+ case NC_DOUBLE:
+ {
+ long double c;
+ sscanf((const char *)value, "%La", &c);
+ p->which->setKey((const char *)n, c);
+ }
+ break;
+ case NC_RAW:
+ {
+ unsigned size = xmlStrlen(value) / 3;
+ char *dec = NULL;
+ if (size) {
+ dec = (char *)malloc(size);
+ for (unsigned i=0, k=0; i<size; i++, k += 3)
+ dec[i] = value[k] * 100 + value[k+1] * 10 + value[k+2];
+ }
+ p->which->setKey((const char *)n, dec, size);
+ free(dec);
+ }
+ }
+ if (type == NC_DIR) {
+ p->state = inDir;
+ p->depth++;
+ } else {
+ p->pState = p->state;
+ p->state = inEnt;
+ }
+ return;
+ }
+}
+
+static void ncXmlEndElement(void *user_data, const CHAR *name)
+{
+ struct ncParseState *p = (struct ncParseState *)user_data;
+#ifdef NC_DEBUG_XML
+ fprintf(stderr, "EndElement %s %s %d\n", name, p->ns, p->state);
+#endif
+ if (p->state == inEnt) {
+ p->state = p->pState;
+ return;
+ }
+ if (p->state == unknown) {
+ if (p->unDepth)
+ p->unDepth--;
+ else
+ p->state = p->pState;
+ return;
+ }
+ if (p->state == inRoot) {
+ p->state = noRoot;
+ free(p->ns);
+ p->ns = NULL;
+ return;
+ }
+ if (p->state == inDir) {
+ p->depth--;
+ if (!p->depth)
+ p->state = inRoot;
+ p->which->chDir("..");
+ }
+}
+#endif /* HAVE_LIBXML2 */
+
+int NConfig::fromXML(const char *filename, int force)
+{
+ if (fd < 0)
+ return NC_ERR_NFILE;
+ if (omode != NC_O_RW)
+ return NC_ERR_PERM;
+#ifndef HAVE_LIBXML2
+ return NC_ERR_NOSUPPORT;
+#else
+ struct ncParseState state = { noRoot, noRoot, NULL, 0, 0, force, this };
+ sh.getEntity = ncXmlGetEntity;
+ sh.startElement = ncXmlStartElement;
+ sh.endElement = ncXmlEndElement;
+
+ lockFile(NC_L_RW);
+ cdir = rdir;
+ int ret = ncXmlSAXParseFile(&sh, &state, filename);
+ cdir = rdir;
+ unLockFile();
+
+ return ret < 0 ? NC_ERR_NVAL : NC_ERR_OK;
+#endif /* HAVE_LIBXML2 */
+}
+