/* LibRCC - module handling XML configuration Copyright (C) 2005-2008 Suren A. Chilingaryan This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include "../config.h" #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #ifdef HAVE_FCNTL_H # include #endif /* HAVE_FCNTL_H */ #ifdef HAVE_SYS_TYPES_H # include #endif /* HAVE_SYS_TYPES_H */ #ifdef HAVE_SYS_STAT_H # include #endif /* HAVE_SYS_STAT_H */ #ifdef HAVE_SYS_FILE_H # include #endif /* HAVE_SYS_FILE_H */ #include #include #include #include "internal.h" #include "rccconfig.h" #include "plugin.h" #include "rcchome.h" #define MAX_HOME_CHARS 96 #define XPATH_LANGUAGE "//Language[@name]" static xmlDocPtr xmlctx = NULL; rcc_config rccGetConfiguration() { return (rcc_config)xmlctx; } static const char *rccXmlGetText(xmlNodePtr node) { if ((node)&&(node->children)&&(node->children->type == XML_TEXT_NODE)&&(node->children->content)) return (const char*)node->children->content; return NULL; } int rccXmlInit(int LoadConfiguration) { FILE *f; char config[MAX_HOME_CHARS + 32]; xmlXPathContextPtr xpathctx = NULL; xmlXPathObjectPtr obj = NULL; xmlNodeSetPtr node_set; unsigned long i, nnodes; xmlNodePtr enode, cnode, pnode, node; xmlAttrPtr attr; const char *lang, *engine_name; unsigned int pos, lpos, epos, cpos; const char *alias, *parent; unsigned int j, apos, rpos; rcc_engine *engine; xmlInitParser(); xmlInitCharEncodingHandlers(); xmlKeepBlanksDefault(0); if (LoadConfiguration) { if (strlen(rcc_home_dir)>MAX_HOME_CHARS) config[0] = 0; else { sprintf(config, "%s/.rcc/rcc.xml", rcc_home_dir); f = fopen(config, "r"); if (f) fclose(f); else config[0] = 0; } if (!config[0]) { strcpy(config, "/etc/rcc.xml"); f = fopen(config, "r"); if (f) fclose(f); else config[0] = 0; } } else config[0] = 0; for (apos=0;rcc_default_aliases[apos].alias;apos++); // Load Extra Languages if (config[0]) { xmlctx = xmlReadFile(config, NULL, 0); if (!xmlctx) goto clear; xpathctx = xmlXPathNewContext(xmlctx); if (!xpathctx) goto clear; obj = xmlXPathEvalExpression((xmlChar*)XPATH_LANGUAGE, xpathctx); if (!obj) goto clear; node_set = obj->nodesetval; if (!node_set) goto clear; for (lpos = 0; rcc_default_languages[lpos].sn; lpos++); nnodes = node_set->nodeNr; for (i=0;inodeTab[i]; attr = xmlHasProp(pnode, (xmlChar*)"name"); lang = (const char*)attr->children->content; if ((!lang)||(!lang[0])) continue; pos = rccDefaultGetLanguageByName(lang); if (!pos) continue; if (pos == (rcc_language_id)-1) { for (rpos=0;rcc_default_relations[rpos].lang;rpos++); if (rpos < RCC_MAX_RELATIONS) { rcc_default_relations[rpos].parent = rcc_english_language_sn; rcc_default_relations[rpos++].lang = lang; rcc_default_relations[rpos].parent = NULL; rcc_default_relations[rpos].lang = NULL; } pos = lpos; } else if (pos == RCC_MAX_LANGUAGES) continue; for (epos = 1, cpos = 1,node=pnode->children;node;node=node->next) { if (node->type != XML_ELEMENT_NODE) continue; if (!xmlStrcmp(node->name, (xmlChar*)"Charsets")) { for (cnode=node->children;cnode;cnode=cnode->next) { if (cnode->type != XML_ELEMENT_NODE) continue; if ((!xmlStrcmp(cnode->name, (xmlChar*)"Charset"))&&(rccXmlGetText(cnode))&&(cposname, (xmlChar*)"Engines")) { for (enode=node->children;enode;enode=enode->next) { if (enode->type != XML_ELEMENT_NODE) continue; if ((!xmlStrcmp(enode->name, (xmlChar*)"Engine"))&&(eposname, (xmlChar*)"Aliases")) { for (enode=node->children;enode;enode=enode->next) { if (enode->type != XML_ELEMENT_NODE) continue; if ((!xmlStrcmp(enode->name, (xmlChar*)"Alias"))&&(aposname, (xmlChar*)"Relations")) { rpos = rccDefaultDropLanguageRelations(lang); for (enode=node->children;enode;enode=enode->next) { if (enode->type != XML_ELEMENT_NODE) continue; if ((!xmlStrcmp(enode->name, (xmlChar*)"Parrent"))&&(rpos 1)||(epos > 1)) { rcc_default_languages[pos].sn = lang; rcc_default_languages[pos].charsets[0] = rcc_default_charset; if (cpos > 1) rcc_default_languages[pos].charsets[cpos] = NULL; else { rcc_default_languages[pos].charsets[1] = rcc_utf8_charset; rcc_default_languages[pos].charsets[2] = NULL; } rcc_default_languages[pos].engines[0] = &rcc_default_engine; rcc_default_languages[pos].engines[epos] = NULL; if (pos == lpos) rcc_default_languages[++lpos].sn = NULL; } } clear: if (xmlctx) { if (xpathctx) { xmlXPathFreeContext(xpathctx); if (obj) { xmlXPathFreeObject(obj); } } } } return 0; } void rccXmlFree() { if (xmlctx) { xmlFreeDoc(xmlctx); xmlctx = NULL; } xmlCleanupCharEncodingHandlers(); xmlCleanupParser(); } static xmlNodePtr rccNodeFind(xmlXPathContextPtr xpathctx, const char *request, ...) { xmlXPathObjectPtr obj; xmlNodeSetPtr node_set; xmlNodePtr res = NULL; unsigned int i, args = 0; unsigned int size = 64; va_list ap; char *req; if (!xpathctx) return NULL; for (req = strstr(request, "%s"); req; req = strstr(req + 1, "%s")) args++; if (args) { va_start(ap, request); for (i=0;inodesetval; if ((node_set)&&(node_set->nodeNr > 0)) { res = node_set->nodeTab[0]; } xmlXPathFreeObject(obj); } if (args) free(req); return res; } #define XPATH_SELECTED "/Config" #define XPATH_SELECTED_LANGUAGE "/Config/Language" #define XPATH_SELECTED_OPTIONS "/Config/Options" #define XPATH_SELECTED_OPTION "/Config/Options/Option[@name=\"%s\"]" #define XPATH_SELECTED_LANGS "/Config/Languages" #define XPATH_SELECTED_LANG "/Config/Languages/Language[@name=\"%s\"]" #define XPATH_SELECTED_ENGINE "/Config/Languages/Language[@name=\"%s\"]/Engine" #define XPATH_SELECTED_CLASSES "/Config/Languages/Language[@name=\"%s\"]/Classes" #define XPATH_SELECTED_CLASS "/Config/Languages/Language[@name=\"%s\"]/Classes/Class[@name=\"%s\"]" int rccSave(rcc_context ctx, const char *name) { int fd; char *config; struct stat st; unsigned int i, j, size; rcc_language_ptr *languages; rcc_language_ptr language; rcc_language_config cfg; rcc_class_ptr *classes; rcc_class_ptr cl; xmlXPathContextPtr xpathctx = NULL; xmlDocPtr doc = NULL; xmlNodePtr pnode, lnode, onode, llnode, cnode, enode, node; unsigned char oflag = 0, llflag = 0, cflag; rcc_option_description *odesc; rcc_option_value ovalue; const char *oname, *ovname; char value[16]; int memsize; xmlChar *mem; if (!ctx) { if (rcc_default_ctx) ctx = rcc_default_ctx; else return -1; } if ((!name)||(!strcmp(name, "rcc"))||(strlen(rcc_home_dir)<3)) name = "default"; size = strlen(rcc_home_dir) + strlen(name) + 32; config = (char*)malloc(size*sizeof(char)); if (!config) return -1; sprintf(config,"%s/.rcc/",rcc_home_dir); mkdir(config, 00755); sprintf(config,"%s/.rcc/%s.xml",rcc_home_dir,name); fd = open(config, O_CREAT|O_RDWR,00644); if (fd == -1) goto clear; flock(fd, LOCK_EX); if ((!fstat(fd, &st))&&(st.st_size)) { doc = xmlReadFd(fd, config, NULL, 0); } if (!doc) { doc = xmlNewDoc((xmlChar*)"1.0"); if (!doc) goto clear; pnode = NULL; } else { xpathctx = xmlXPathNewContext(doc); pnode = rccNodeFind(xpathctx, XPATH_SELECTED); } if (pnode) { lnode = rccNodeFind(xpathctx, XPATH_SELECTED_LANGUAGE); onode = rccNodeFind(xpathctx, XPATH_SELECTED_OPTIONS); llnode = rccNodeFind(xpathctx, XPATH_SELECTED_LANGS); } else { pnode = xmlNewChild((xmlNodePtr)doc, NULL, (xmlChar*)"Config", NULL); lnode = NULL; onode = NULL; llnode = NULL; } if (lnode) xmlNodeSetContent(lnode, (xmlChar*)rccGetSelectedLanguageName(ctx)); else lnode = xmlNewChild(pnode,NULL, (xmlChar*)"Language", (xmlChar*)rccGetSelectedLanguageName(ctx)); if (onode) oflag = 1; else onode = xmlNewChild(pnode, NULL, (xmlChar*)"Options", NULL); for (i=0;ilanguages; classes = ctx->classes; for (i=1;languages[i];i++) { language = languages[i]; cfg = rccCheckConfig(ctx, (rcc_language_id)i); if ((!cfg)||(!cfg->configured)) continue; if (llflag) lnode = rccNodeFind(xpathctx, XPATH_SELECTED_LANG, language->sn); else lnode = NULL; if (lnode) { enode = rccNodeFind(xpathctx, XPATH_SELECTED_ENGINE, language->sn); cnode = rccNodeFind(xpathctx, XPATH_SELECTED_CLASSES, language->sn); } else { lnode = xmlNewChild(llnode, NULL, (xmlChar*)"Language", NULL); xmlSetProp(lnode, (xmlChar*)"name", (xmlChar*)language->sn); enode = NULL; cnode = NULL; } if (enode) xmlNodeSetContent(enode, (xmlChar*)rccConfigGetSelectedEngineName(cfg)); else xmlNewChild(lnode, NULL, (xmlChar*)"Engine", (xmlChar*)rccConfigGetSelectedEngineName(cfg)); if (cnode) cflag = 1; else { cnode = xmlNewChild(lnode, NULL, (xmlChar*)"Classes", NULL); cflag = 0; } for (j=0;classes[j];j++) { cl = classes[j]; if (cl->flags&RCC_CLASS_FLAG_SKIP_SAVELOAD) continue; if (cflag) node = rccNodeFind(xpathctx, XPATH_SELECTED_CLASS, language->sn, cl->name); else node = NULL; if (node) xmlNodeSetContent(node, (xmlChar*)rccConfigGetSelectedCharsetName(cfg, (rcc_class_id)j)); else { node = xmlNewChild(cnode, NULL, (xmlChar*)"Class", (xmlChar*)rccConfigGetSelectedCharsetName(cfg, (rcc_class_id)j)); xmlSetProp(node, (xmlChar*)"name", (xmlChar*)cl->name); } } } xmlDocDumpFormatMemory(doc,&mem,&memsize,1); ftruncate(fd, 0); lseek(fd, SEEK_SET, 0); if (mem) { write(fd, mem, memsize); free(mem); } clear: if (config) { if (fd != -1) { if (doc) { if (xpathctx) { xmlXPathFreeContext(xpathctx); } xmlFreeDoc(doc); } fsync(fd); flock(fd, LOCK_UN); close(fd); } free(config); } return 0; } int rccLoad(rcc_context ctx, const char *name) { int err; int fd, sysfd; char *config; struct stat st; unsigned int i, j, size; const char *tmp; rcc_option_description *odesc; rcc_option_value ovalue; const char *oname; rcc_language_config cfg; rcc_language_ptr *languages; rcc_language_ptr language; rcc_class_ptr *classes; rcc_class_ptr cl; xmlXPathContextPtr xpathctx = NULL, sysxpathctx = NULL, curxpathctx; xmlDocPtr doc = NULL, sysdoc = NULL; xmlNodePtr node, lnode; if (!ctx) { if (rcc_default_ctx) ctx = rcc_default_ctx; else return -1; } if ((!name)||(!strcmp(name, "rcc"))||(strlen(rcc_home_dir)<3)) name = "default"; size = strlen(rcc_home_dir) + strlen(name) + 32; config = (char*)malloc(size*sizeof(char)); if (!config) return -1; sprintf(config,"%s/.rcc/%s.xml",rcc_home_dir,name); fd = open(config, O_RDONLY); sprintf(config, "/etc/rcc/%s.xml",name); sysfd = open(config, O_RDONLY); free(config); if (fd != -1) { flock(fd, LOCK_EX); if ((!fstat(fd, &st))&&(st.st_size)) { doc = xmlReadFd(fd, name, NULL, 0); } flock(fd, LOCK_UN); close(fd); if (doc) { xpathctx = xmlXPathNewContext(doc); if (!xpathctx) { xmlFreeDoc(doc); doc = NULL; } } } if (sysfd != -1) { flock(sysfd, LOCK_EX); if ((!fstat(sysfd, &st))&&(st.st_size)) { sysdoc = xmlReadFd(sysfd, name, NULL, 0); } flock(sysfd, LOCK_UN); close(sysfd); if (sysdoc) { sysxpathctx = xmlXPathNewContext(sysdoc); if (!sysxpathctx) { xmlFreeDoc(sysdoc); sysdoc = NULL; } } } if ((!doc)&&(!sysdoc)) goto clear; node = rccNodeFind(xpathctx, XPATH_SELECTED_LANGUAGE); if (!node) node = rccNodeFind(sysxpathctx, XPATH_SELECTED_LANGUAGE); if (node) { tmp = rccXmlGetText(node); if (tmp) err = rccSetLanguageByName(ctx, tmp); else err = -1; } else err = -1; if (err) rccSetLanguage(ctx, 0); for (i=0;ilanguages; classes = ctx->classes; for (i=1;languages[i];i++) { language = languages[i]; lnode = rccNodeFind(xpathctx, XPATH_SELECTED_LANG, language->sn); if (lnode) curxpathctx = xpathctx; else { lnode = rccNodeFind(sysxpathctx, XPATH_SELECTED_LANG, language->sn); if (lnode) curxpathctx = sysxpathctx; else continue; } cfg = rccGetConfig(ctx, (rcc_language_id)i); if (!cfg) continue; node = rccNodeFind(curxpathctx, XPATH_SELECTED_ENGINE, language->sn); if (node) { tmp = rccXmlGetText(node); if (tmp) err = rccConfigSetEngineByName(cfg, tmp); else err = -1; } else err = -1; if (err) rccConfigSetEngineByName(cfg, NULL); for (j=0;classes[j];j++) { cl = classes[j]; if (cl->flags&RCC_CLASS_FLAG_SKIP_SAVELOAD) continue; node = rccNodeFind(curxpathctx, XPATH_SELECTED_CLASS, language->sn, cl->name); if (node) { tmp = rccXmlGetText(node); if (tmp) err = rccConfigSetCharsetByName(cfg, (rcc_class_id)j, tmp); else err = -1; } else err = -1; if (err) rccConfigSetCharset(cfg, (rcc_class_id)j, 0); } } clear: if (sysdoc) { if (sysxpathctx) { xmlXPathFreeContext(sysxpathctx); } xmlFreeDoc(sysdoc); } if (doc) { if (xpathctx) { xmlXPathFreeContext(xpathctx); } xmlFreeDoc(doc); } if ((!ctx->current_language)&&(rccGetOption(ctx, RCC_OPTION_CONFIGURED_LANGUAGES_ONLY))) { ctx->current_config = rccGetCurrentConfig(ctx); ctx->configure = 1; } return 0; }