#include #include #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_LIBXML2 #include #include #include #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; ipages; i++) if ((cc = IDE(de, i))->type) { char *encname = encodeXml(data+cc->name); fprintf(f, "type); free(encname); switch (cc->type) { case NC_DIR: fprintf(f, "%u\">\n", cc->pages); store(cc, f); fprintf(f, "\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; jpages; 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", "\n"); fprintf(f, "\n"); lockFile(NC_L_RO); store(rdir, f); unLockFile(); fprintf(f, "\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; iwhich->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 */ }