#include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif #include "xsl.h" #include "defaults.h" #include "s1kd_tools.h" #define PROG_NAME "s1kd-defaults" #define VERSION "2.8.0" #define ERR_PREFIX PROG_NAME ": ERROR: " #define S_DMTYPES_ERR ERR_PREFIX "Could not create " DEFAULT_DMTYPES_FNAME " file.\n" #define S_FMTYPES_ERR ERR_PREFIX "Could not create " DEFAULT_FMTYPES_FNAME " file.\n" #define S_NO_FILE_ERR ERR_PREFIX "Could not open file: %s\n" #define S_MKDIR_FAILED ERR_PREFIX "Could not create directory %s: %s\n" #define S_CHDIR_FAILED ERR_PREFIX "Could not change to directory %s: %s\n" #define EXIT_NO_FILE 2 #define EXIT_OS_ERROR 3 enum format {TEXT, XML}; enum file {NONE, DEFAULTS, DMTYPES, FMTYPES}; /* Show the help/usage message. */ static void show_help(void) { puts("Usage: " PROG_NAME " [-Ddfisth?] [-b ] [-j ] [-n -v ...] [-o ] [...]"); puts(""); puts("Options:"); puts(" -b, --brex Create from a BREX DM."); puts(" -D, --dmtypes Convert a .dmtypes file."); puts(" -d, --defaults Convert a .defaults file."); puts(" -F, --fmtypes Convert a .fmtypes file."); puts(" -f, --overwrite Overwrite an existing file."); puts(" -h, -?, --help Show usage message."); puts(" -i, --init Initialize a new CSDB."); puts(" -J, --dump-brexmap Dump default .brexmap file."); puts(" -j, --brexmap Use a custom .brexmap file."); puts(" -n, --name Default to set a value for with -v."); puts(" -o, --dir Use instead of current directory."); puts(" -s, --sort Sort entries."); puts(" -t, --text Output in the simple text format."); puts(" -v, --value Value for default specified with -n."); puts(" --version Show version information."); LIBXML2_PARSE_LONGOPT_HELP } static void show_version(void) { printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION); printf("Using libxml %s and libxslt %s\n", xmlParserVersion, xsltEngineVersion); } static xmlDocPtr transform_doc_with(xmlDocPtr doc, xmlDocPtr styledoc) { xsltStylesheetPtr style; xmlDocPtr res; style = xsltParseStylesheetDoc(styledoc); res = xsltApplyStylesheet(style, doc, NULL); xsltFreeStylesheet(style); return res; } /* Apply a built-in XSLT stylesheet to an XML document. */ static xmlDocPtr transform_doc(xmlDocPtr doc, unsigned char *xml, unsigned int len) { xmlDocPtr styledoc, res; styledoc = read_xml_mem((const char *) xml, len); res = transform_doc_with(doc, styledoc); return res; } /* Sort entries in defaults/dmtypes files. */ static xmlDocPtr sort_entries(xmlDocPtr doc) { return transform_doc(doc, xsl_sort_xsl, xsl_sort_xsl_len); } /* Convert XML defaults to the simple text version. */ static xmlDocPtr xml_defaults_to_text(xmlDocPtr doc) { return transform_doc(doc, xsl_xml_defaults_to_text_xsl, xsl_xml_defaults_to_text_xsl_len); } /* Convert XML dmtypes to the simple text version. */ static xmlDocPtr xml_dmtypes_to_text(xmlDocPtr doc) { return transform_doc(doc, xsl_xml_dmtypes_to_text_xsl, xsl_xml_dmtypes_to_text_xsl_len); } /* Convert XML fmtypes to the simple text version. */ static xmlDocPtr xml_fmtypes_to_text(xmlDocPtr doc) { return transform_doc(doc, xsl_xml_fmtypes_to_text_xsl, xsl_xml_fmtypes_to_text_xsl_len); } /* Convert simple text defaults to the XML version. */ static xmlDocPtr text_defaults_to_xml(const char *path) { FILE *f; char line[1024]; xmlDocPtr doc; xmlNodePtr defaults; if (!path) { return NULL; } if ((doc = read_xml_doc(path))) { return doc; } if (strcmp(path, "-") == 0) { f = stdin; } else if (!(f = fopen(path, "r"))) { return NULL; } doc = xmlNewDoc(BAD_CAST "1.0"); defaults = xmlNewNode(NULL, BAD_CAST "defaults"); xmlDocSetRootElement(doc, defaults); while (fgets(line, 1024, f)) { char key[32], val[256]; xmlNodePtr def; if (sscanf(line, "%31s %255[^\n]", key, val) != 2) continue; def = xmlNewChild(defaults, NULL, BAD_CAST "default", NULL); xmlSetProp(def, BAD_CAST "ident", BAD_CAST key); xmlSetProp(def, BAD_CAST "value", BAD_CAST val); } fclose(f); return doc; } /* Convert simple text dmtypes to the XML version. */ static xmlDocPtr text_dmtypes_to_xml(const char *path) { FILE *f; char line[1024]; xmlDocPtr doc; xmlNodePtr dmtypes; if ((doc = read_xml_doc(path))) { return doc; } if (strcmp(path, "-") == 0) { f = stdin; } else if (!(f = fopen(path, "r"))) { return NULL; } doc = xmlNewDoc(BAD_CAST "1.0"); dmtypes = xmlNewNode(NULL, BAD_CAST "dmtypes"); xmlDocSetRootElement(doc, dmtypes); while (fgets(line, 1024, f)) { char code[6], schema[64], infname[256]; int n; xmlNodePtr type; n = sscanf(line, "%5s %63s %255[^\n]", code, schema, infname); if (n < 2) { continue; } type = xmlNewChild(dmtypes, NULL, BAD_CAST "type", NULL); xmlSetProp(type, BAD_CAST "infoCode", BAD_CAST code); xmlSetProp(type, BAD_CAST "schema", BAD_CAST schema); if (n > 2) { xmlSetProp(type, BAD_CAST "infoName", BAD_CAST infname); } } fclose(f); return doc; } /* Convert simple text fmtypes to the XML version. */ static xmlDocPtr text_fmtypes_to_xml(const char *path) { FILE *f; char line[1024]; xmlDocPtr doc; xmlNodePtr fmtypes; if ((doc = read_xml_doc(path))) { return doc; } if (strcmp(path, "-") == 0) { f = stdin; } else if (!(f = fopen(path, "r"))) { return NULL; } doc = xmlNewDoc(BAD_CAST "1.0"); fmtypes = xmlNewNode(NULL, BAD_CAST "fmtypes"); xmlDocSetRootElement(doc, fmtypes); while (fgets(line, 1024, f)) { char code[5] = "", type[32] = "", xsl[1024] = ""; int n; xmlNodePtr fm; n = sscanf(line, "%4s %31s %1023[^\n]", code, type, xsl); if (n < 2) { continue; } fm = xmlNewChild(fmtypes, NULL, BAD_CAST "fm", NULL); xmlSetProp(fm, BAD_CAST "infoCode", BAD_CAST code); xmlSetProp(fm, BAD_CAST "type", BAD_CAST type); if (n == 3) { xmlSetProp(fm, BAD_CAST "xsl", BAD_CAST xsl); } } fclose(f); return doc; } /* Return the first node matching an XPath expression. */ static xmlNodePtr first_xpath_node(xmlDocPtr doc, xmlNodePtr node, const char *xpath) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlNodePtr first; ctx = xmlXPathNewContext(doc ? doc : node->doc); ctx->node = node; obj = xmlXPathEvalExpression(BAD_CAST xpath, ctx); first = xmlXPathNodeSetIsEmpty(obj->nodesetval) ? NULL : obj->nodesetval->nodeTab[0]; xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); return first; } /* Obtain some defaults values from the user as arguments. */ static void set_user_defaults(xmlDocPtr doc, xmlNodePtr user_defs) { xmlNodePtr root, cur; root = xmlDocGetRootElement(doc); for (cur = user_defs->children; cur; cur = cur->next) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlChar *ident, *value; ident = xmlGetProp(cur, BAD_CAST "ident"); value = xmlGetProp(cur, BAD_CAST "value"); ctx = xmlXPathNewContext(doc); xmlXPathSetContextNode(root, ctx); xmlXPathRegisterVariable(ctx, BAD_CAST "ident", xmlXPathNewString(ident)); obj = xmlXPathEvalExpression(BAD_CAST "default[@ident=$ident]", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { xmlAddChild(root, xmlCopyNode(cur, 1)); } else { xmlSetProp(obj->nodesetval->nodeTab[0], BAD_CAST "value", value); } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); xmlFree(ident); xmlFree(value); } } /* Obtain some defaults values from the environment. */ static void set_defaults(xmlDocPtr doc) { char *env; if ((env = getenv("LANG"))) { char *lang, *lang_l, *lang_c; xmlNodePtr liso, ciso; lang = strdup(env); lang_l = strtok(lang, "_"); lang_c = strtok(NULL, "."); liso = first_xpath_node(doc, NULL, "//default[@ident = 'languageIsoCode']"); ciso = first_xpath_node(doc, NULL, "//default[@ident = 'countryIsoCode']"); if (lang_l) { xmlSetProp(liso, BAD_CAST "value", BAD_CAST lang_l); } if (lang_c) { xmlSetProp(ciso, BAD_CAST "value", BAD_CAST lang_c); } free(lang); } } /* Use the .brexmap to create the XSL to transform a BREX DM to a .defaults * file. */ static xmlDocPtr make_brex2defaults_xsl(xmlDocPtr brexmap) { xmlDocPtr res; res = transform_doc(brexmap, xsl_brexmap_defaults_xsl, xsl_brexmap_defaults_xsl_len); return res; } /* Use the .brexmap to create the XSL to transform a BREX DM to a .dmtypes * file. */ static xmlDocPtr make_brex2dmtypes_xsl(xmlDocPtr brexmap) { xmlDocPtr res; res = transform_doc(brexmap, xsl_brexmap_dmtypes_xsl, xsl_brexmap_dmtypes_xsl_len); return res; } /* Create a .defaults file from a BREX DM. */ static xmlDocPtr new_defaults_from_brex(xmlDocPtr brex, xmlDocPtr brexmap) { xmlDocPtr styledoc, res, sorted; styledoc = make_brex2defaults_xsl(brexmap); res = transform_doc_with(brex, styledoc); sorted = sort_entries(res); xmlFreeDoc(res); return sorted; } /* Create a .dmtypes file from a BREX DM. */ static xmlDocPtr new_dmtypes_from_brex(xmlDocPtr brex, xmlDocPtr brexmap) { xmlDocPtr styledoc, res, sorted; styledoc = make_brex2dmtypes_xsl(brexmap); res = transform_doc_with(brex, styledoc); sorted = sort_entries(res); xmlFreeDoc(res); return sorted; } /* Dump the built-in defaults in the XML format. */ static void dump_defaults_xml(const char *fname, bool overwrite, xmlDocPtr brex, xmlDocPtr brexmap, xmlNodePtr user_defs) { xmlDocPtr doc; if (brex) { doc = new_defaults_from_brex(brex, brexmap); } else { doc = read_xml_mem((const char *) defaults_xml, defaults_xml_len); set_defaults(doc); set_user_defaults(doc, user_defs); } if (overwrite) { save_xml_doc(doc, fname); } else { save_xml_doc(doc, "-"); } xmlFreeDoc(doc); } /* Dump the built-in defaults in the simple text format. */ static void dump_defaults_text(const char *fname, bool overwrite, xmlDocPtr brex, xmlDocPtr brexmap, xmlNodePtr user_defs) { xmlDocPtr doc, res; FILE *f; if (brex) { doc = new_defaults_from_brex(brex, brexmap); } else { doc = read_xml_mem((const char *) defaults_xml, defaults_xml_len); set_defaults(doc); set_user_defaults(doc, user_defs); } res = transform_doc(doc, xsl_xml_defaults_to_text_xsl, xsl_xml_defaults_to_text_xsl_len); if (overwrite) { f = fopen(fname, "w"); } else { f = stdout; } fprintf(f, "%s", res->children->content); if (overwrite) { fclose(f); } xmlFreeDoc(res); xmlFreeDoc(doc); } static xmlDocPtr simple_text_to_xml(const char *path, enum file f, bool sort, xmlDocPtr brex, xmlDocPtr brexmap) { xmlDocPtr doc = NULL; switch (f) { case NONE: case DEFAULTS: doc = brex ? new_defaults_from_brex(brex, brexmap) : text_defaults_to_xml(path); break; case DMTYPES: doc = brex ? new_dmtypes_from_brex(brex, brexmap) : text_dmtypes_to_xml(path); break; case FMTYPES: doc = text_fmtypes_to_xml(path); break; } if (sort) { xmlDocPtr res; res = sort_entries(doc); xmlFreeDoc(doc); doc = res; } return doc; } /* Convert an XML defaults/dmtypes file to the simple text version. */ static void xml_to_text(const char *path, enum file f, bool overwrite, bool sort, xmlDocPtr brex, xmlDocPtr brexmap, xmlNodePtr user_defs) { xmlDocPtr doc, res = NULL; if (!(doc = read_xml_doc(path))) { doc = simple_text_to_xml(path, f, sort, brex, brexmap); } if (f == DEFAULTS) { set_user_defaults(doc, user_defs); } switch (f) { case NONE: case DEFAULTS: res = xml_defaults_to_text(doc); break; case DMTYPES: res = xml_dmtypes_to_text(doc); break; case FMTYPES: res = xml_fmtypes_to_text(doc); break; } if (res->children) { FILE *f; if (overwrite) { f = fopen(path, "w"); } else { f = stdout; } fprintf(f, "%s", (char *) res->children->content); if (overwrite) { fclose(f); } } xmlFreeDoc(res); xmlFreeDoc(doc); } /* Convert a simple text defaults/dmtypes file to the XML version. */ static void text_to_xml(const char *path, enum file f, bool overwrite, bool sort, xmlDocPtr brex, xmlDocPtr brexmap, xmlNodePtr user_defs) { xmlDocPtr doc; doc = simple_text_to_xml(path, f, sort, brex, brexmap); if (f == DEFAULTS) { set_user_defaults(doc, user_defs); } if (sort) { xmlDocPtr res; res = sort_entries(doc); xmlFreeDoc(doc); doc = res; } if (overwrite) { save_xml_doc(doc, path); } else { save_xml_doc(doc, "-"); } xmlFreeDoc(doc); } static void convert_or_dump(enum format fmt, enum file f, const char *fname, bool overwrite, bool sort, xmlDocPtr brex, xmlDocPtr brexmap, xmlNodePtr user_defs) { if (f != NONE && (!brex || f == FMTYPES) && access(fname, F_OK) == -1) { fprintf(stderr, S_NO_FILE_ERR, fname); exit(EXIT_NO_FILE); } if (fmt == TEXT) { if (f == NONE) { dump_defaults_text(fname, overwrite, brex, brexmap, user_defs); } else { xml_to_text(fname, f, overwrite, sort, brex, brexmap, user_defs); } } else if (fmt == XML) { if (f == NONE) { dump_defaults_xml(fname, overwrite, brex, brexmap, user_defs); } else { text_to_xml(fname, f, overwrite, sort, brex, brexmap, user_defs); } } } static xmlDocPtr read_default_brexmap(void) { char fname[PATH_MAX]; if (find_config(fname, DEFAULT_BREXMAP_FNAME)) { return read_xml_doc(fname); } else { return read_xml_mem((const char *) ___common_brexmap_xml, ___common_brexmap_xml_len); } } static void dump_brexmap(void) { printf("%.*s", ___common_brexmap_xml_len, ___common_brexmap_xml); } int main(int argc, char **argv) { int i; enum format fmt = XML; enum file f = NONE; char *fname = NULL; bool overwrite = false; bool initialize = false; bool sort = false; xmlDocPtr brex = NULL; xmlDocPtr brexmap = NULL; char *dir = NULL; xmlNodePtr user_defs, cur = NULL; const char *sopts = "b:DdFfiJj:n:o:stv:h?"; struct option lopts[] = { {"version" , no_argument , 0, 0}, {"help" , no_argument , 0, 'h'}, {"brex" , required_argument, 0, 'b'}, {"dmtypes" , no_argument , 0, 'D'}, {"defaults" , no_argument , 0, 'd'}, {"fmtypes" , no_argument , 0, 'F'}, {"overwrite" , no_argument , 0, 'f'}, {"init" , no_argument , 0, 'i'}, {"name" , required_argument, 0, 'n'}, {"dir" , required_argument, 0, 'o'}, {"dump-brexmap", no_argument , 0, 'J'}, {"brexmap" , required_argument, 0, 'j'}, {"sort" , no_argument , 0, 's'}, {"text" , no_argument , 0, 't'}, {"value" , required_argument, 0, 'v'}, LIBXML2_PARSE_LONGOPT_DEFS {0, 0, 0, 0} }; int loptind = 0; user_defs = xmlNewNode(NULL, BAD_CAST "defs"); while ((i = getopt_long(argc, argv, sopts, lopts, &loptind)) != -1) { switch (i) { case 0: if (strcmp(lopts[loptind].name, "version") == 0) { show_version(); return 0; } LIBXML2_PARSE_LONGOPT_HANDLE(lopts, loptind, optarg) break; case 'b': if (!brex) brex = read_xml_doc(optarg); break; case 'D': f = DMTYPES; if (!fname) fname = strdup(DEFAULT_DMTYPES_FNAME); break; case 'd': f = DEFAULTS; if (!fname) fname = strdup(DEFAULT_DEFAULTS_FNAME); break; case 'F': f = FMTYPES; if (!fname) fname = strdup(DEFAULT_FMTYPES_FNAME); break; case 'f': overwrite = true; break; case 'i': initialize = true; break; case 'J': dump_brexmap(); return 0; case 'j': if (!brexmap) brexmap = read_xml_doc(optarg); break; case 'n': cur = xmlNewChild(user_defs, NULL, BAD_CAST "default", NULL); xmlSetProp(cur, BAD_CAST "ident", BAD_CAST optarg); break; case 'o': free(dir); dir = strdup(optarg); break; case 's': sort = true; break; case 't': fmt = TEXT; break; case 'v': if (cur) { xmlSetProp(cur, BAD_CAST "value", BAD_CAST optarg); } break; case 'h': case '?': show_help(); exit(0); } } if (!fname) { fname = strdup(DEFAULT_DEFAULTS_FNAME); } if (!brexmap) { brexmap = read_default_brexmap(); } if (dir) { if (access(dir, F_OK) == -1) { int err; #ifdef _WIN32 err = mkdir(dir); #else err = mkdir(dir, S_IRWXU); #endif if (err) { fprintf(stderr, S_MKDIR_FAILED, dir, strerror(errno)); exit(EXIT_OS_ERROR); } } if (chdir(dir) != 0) { fprintf(stderr, S_CHDIR_FAILED, dir, strerror(errno)); exit(EXIT_OS_ERROR); } } if (initialize) { char sys[256]; const char *opt; void (*fn)(const char *, bool, xmlDocPtr, xmlDocPtr, xmlNodePtr); if (fmt == TEXT) { opt = "-."; fn = dump_defaults_text; } else { opt = "-,"; fn = dump_defaults_xml; } if (overwrite || access(DEFAULT_DEFAULTS_FNAME, F_OK) == -1) { fn(DEFAULT_DEFAULTS_FNAME, true, brex, brexmap, user_defs); #ifdef _WIN32 SetFileAttributes(DEFAULT_DEFAULTS_FNAME, FILE_ATTRIBUTE_HIDDEN); #endif } if (overwrite || access(DEFAULT_DMTYPES_FNAME, F_OK) == -1) { if (brex) { xmlDocPtr dmtypes; dmtypes = new_dmtypes_from_brex(brex, brexmap); if (fmt == TEXT) { xmlDocPtr res; FILE *f; res = xml_dmtypes_to_text(dmtypes); f = fopen(DEFAULT_DMTYPES_FNAME, "w"); fprintf(f, "%s", (char *) res->children->content); fclose(f); xmlFreeDoc(res); } else { save_xml_doc(dmtypes, DEFAULT_DMTYPES_FNAME); } xmlFreeDoc(dmtypes); } else { snprintf(sys, 256, "s1kd-newdm %s > %s", opt, DEFAULT_DMTYPES_FNAME); if (system(sys) != 0) { fprintf(stderr, S_DMTYPES_ERR); } } #ifdef _WIN32 SetFileAttributes(DEFAULT_DMTYPES_FNAME, FILE_ATTRIBUTE_HIDDEN); #endif } if (overwrite || access(DEFAULT_FMTYPES_FNAME, F_OK) == -1) { snprintf(sys, 256, "s1kd-fmgen %s > %s", opt, DEFAULT_FMTYPES_FNAME); if (system(sys) != 0) { fprintf(stderr, S_FMTYPES_ERR); } #ifdef _WIN32 SetFileAttributes(DEFAULT_FMTYPES_FNAME, FILE_ATTRIBUTE_HIDDEN); #endif } } else if (optind < argc) { for (i = optind; i < argc; ++i) { convert_or_dump(fmt, f, argv[i], overwrite, sort, brex, brexmap, user_defs); } } else { convert_or_dump(fmt, f, fname, overwrite, sort, brex, brexmap, user_defs); } free(fname); free(dir); xmlFreeDoc(brex); xmlFreeDoc(brexmap); xmlFreeNode(user_defs); xsltCleanupGlobals(); xmlCleanupParser(); return 0; }