#include #include #include #include #include #include #include #include #include #include #include #include #include "s1kd_tools.h" #include "xsl.h" #define PROG_NAME "s1kd-fmgen" #define VERSION "3.6.0" #define ERR_PREFIX PROG_NAME ": ERROR: " #define INF_PREFIX PROG_NAME ": INFO: " #define EXIT_BAD_DATE 1 #define EXIT_NO_TYPE 2 #define EXIT_BAD_TYPE 3 #define EXIT_MERGE 4 #define EXIT_BAD_STYLESHEET 5 #define S_NO_PM_ERR ERR_PREFIX "No publication module.\n" #define S_NO_TYPE_ERR ERR_PREFIX "No FM type specified.\n" #define S_BAD_TYPE_ERR ERR_PREFIX "Unknown front matter type: %s\n" #define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n" #define E_BAD_DATE ERR_PREFIX "Bad date: %s\n" #define E_MERGE_NAME ERR_PREFIX "Failed to update %s: no <%s> element to merge on.\n" #define E_MERGE_ELEM ERR_PREFIX "Failed to update %s: no front matter contents generated.\n" #define E_BAD_STYLESHEET ERR_PREFIX "Failed to update %s: %s is not a valid stylesheet.\n" #define I_GENERATE INF_PREFIX "Generating FM content for %s (%s)...\n" #define I_NO_INFOCODE INF_PREFIX "Skipping %s as no FM type is associated with info code: %s%s\n" #define I_TRANSFORM INF_PREFIX "Applying transformation %s...\n" #define I_XPROC_TRANSFORM INF_PREFIX "Applying XProc transforation %s/%s...\n" #define I_XPROC_TRANSFORM_NONAME INF_PREFIX "Applying XProc transformation %s/line %ld...\n" static enum verbosity { QUIET, NORMAL, VERBOSE, DEBUG } verbosity = NORMAL; static xmlNodePtr first_xpath_node(xmlDocPtr doc, xmlNodePtr node, const xmlChar *expr) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlNodePtr first; ctx = xmlXPathNewContext(doc ? doc : node->doc); ctx->node = node; obj = xmlXPathEvalExpression(BAD_CAST expr, ctx); first = xmlXPathNodeSetIsEmpty(obj->nodesetval) ? NULL : obj->nodesetval->nodeTab[0]; xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); return first; } static xmlChar *first_xpath_string(xmlDocPtr doc, xmlNodePtr node, const xmlChar *expr) { return xmlNodeGetContent(first_xpath_node(doc, node, expr)); } /* Determine whether a set of parameters already contains a name. */ static bool has_param(const char **params, const char *name) { int i; for (i = 0; params[i]; i += 2) { if (strcmp(params[i], name) == 0) { return true; } } return false; } /* Apply an XProc XSLT step to a document. */ static xmlDocPtr apply_xproc_xslt(const xmlDocPtr doc, const char *xslpath, const xmlNodePtr xslt, const char **params) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlDocPtr res; const char **combined_params; int i, nparams = 0; if (verbosity >= DEBUG) { xmlChar *name = xmlGetProp(xslt, BAD_CAST "name"); if (name) { fprintf(stderr, I_XPROC_TRANSFORM, xslpath, name); } else { fprintf(stderr, I_XPROC_TRANSFORM_NONAME, xslpath, xmlGetLineNo(xslt)); } xmlFree(name); } ctx = xmlXPathNewContext(xslt->doc); xmlXPathRegisterNs(ctx, BAD_CAST "p", BAD_CAST "http://www.w3.org/ns/xproc"); xmlXPathSetContextNode(xslt, ctx); /* Get number of current params. */ for (i = 0; params[i]; i += 2, ++nparams); /* Combine the current params with additional params from the XSLT step. */ obj = xmlXPathEvalExpression(BAD_CAST "p:with-param", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { /* If there are no additional params, simply copy the current * params. */ combined_params = malloc((nparams * 2 + 1) * sizeof(char *)); for (i = 0; params[i]; ++i) { combined_params[i] = strdup(params[i]); } combined_params[i] = NULL; } else { /* If there are additional params, copy the current params and * then append the additional ones. */ int j; combined_params = malloc(((nparams + obj->nodesetval->nodeNr) * 2 + 1) * sizeof(char *)); for (j = 0; params[j]; ++j) { combined_params[j] = strdup(params[j]); } for (i = 0; i < obj->nodesetval->nodeNr; ++i) { char *name; name = (char *) xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "name"); /* User-defined parameters override XProc parameters. */ if (has_param(params, name)) { xmlFree(name); } else { char *select; select = (char *) xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "select"); combined_params[j++] = name; combined_params[j++] = select; } } combined_params[j] = NULL; } xmlXPathFreeObject(obj); /* Read the XProc stylesheet input. */ obj = xmlXPathEvalExpression(BAD_CAST "p:input[@port='stylesheet']/p:*", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { res = xmlCopyDoc(doc, 1); } else { xmlNodePtr src = obj->nodesetval->nodeTab[0]; xmlDocPtr s; if (xmlStrcmp(src->name, BAD_CAST "document") == 0) { xmlChar *href, *URI; href = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "href"); URI = xmlBuildURI(href, BAD_CAST xslpath); s = read_xml_doc((char *) URI); xmlFree(href); xmlFree(URI); } else if (xmlStrcmp(src->name, BAD_CAST "inline") == 0) { s = xmlNewDoc(BAD_CAST "1.0"); xmlDocSetRootElement(s, xmlCopyNode(xmlFirstElementChild(src), 1)); } else { s = NULL; } if (s) { xsltStylesheetPtr style; style = xsltParseStylesheetDoc(s); res = xsltApplyStylesheet(style, doc, combined_params); xsltFreeStylesheet(style); } else { res = xmlCopyDoc(doc, 1); } } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); for (i = 0; combined_params[i]; i += 2) { xmlFree((char *) combined_params[i]); xmlFree((char *) combined_params[i + 1]); } free(combined_params); return res; } static xmlDocPtr transform_doc(xmlDocPtr doc, const char *xslpath, const char **params) { xmlDocPtr styledoc, res = NULL; xsltStylesheetPtr style; xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; if (verbosity >= DEBUG) { fprintf(stderr, I_TRANSFORM, xslpath); } styledoc = read_xml_doc(xslpath); ctx = xmlXPathNewContext(styledoc); xmlXPathRegisterNs(ctx, BAD_CAST "p", BAD_CAST "http://www.w3.org/ns/xproc"); obj = xmlXPathEvalExpression(BAD_CAST "/p:pipeline/p:xslt", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { style = xsltParseStylesheetDoc(styledoc); res = xsltApplyStylesheet(style, doc, params); xsltFreeStylesheet(style); styledoc = NULL; } else { int i; xmlDocPtr d = xmlCopyDoc(doc, 1); for (i =0; i < obj->nodesetval->nodeNr; ++i) { res = apply_xproc_xslt(d, xslpath, obj->nodesetval->nodeTab[i], params); xmlFreeDoc(d); d = res; } } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); xmlFreeDoc(styledoc); return res; } static xmlDocPtr transform_doc_builtin(xmlDocPtr doc, unsigned char *xsl, unsigned int len, const char **params) { xmlDocPtr styledoc, res; xsltStylesheetPtr style; styledoc = read_xml_mem((const char *) xsl, len); style = xsltParseStylesheetDoc(styledoc); res = xsltApplyStylesheet(style, doc, params); xsltFreeStylesheet(style); return res; } static void get_builtin_xsl(const char *type, unsigned char **xsl, unsigned int *len) { if (strcmp(type, "TP") == 0) { *xsl = xsl_tp_xsl; *len = xsl_tp_xsl_len; } else if (strcmp(type, "TOC") == 0) { *xsl = xsl_toc_xsl; *len = xsl_toc_xsl_len; } else if (strcmp(type, "HIGH") == 0) { *xsl = xsl_high_xsl; *len = xsl_high_xsl_len; } else if (strcmp(type, "LOEDM") == 0) { *xsl = xsl_loedm_xsl; *len = xsl_loedm_xsl_len; } else if (strcmp(type, "LOA") == 0) { *xsl = xsl_loa_xsl; *len = xsl_loa_xsl_len; } else if (strcmp(type, "LOASD") == 0) { *xsl = xsl_loasd_xsl; *len = xsl_loasd_xsl_len; } else if (strcmp(type, "LOI") == 0) { *xsl = xsl_loi_xsl; *len = xsl_loi_xsl_len; } else if (strcmp(type, "LOS") == 0) { *xsl = xsl_los_xsl; *len = xsl_los_xsl_len; } else if (strcmp(type, "LOT") == 0) { *xsl = xsl_lot_xsl; *len = xsl_lot_xsl_len; } else if (strcmp(type, "LOTBL") == 0) { *xsl = xsl_lotbl_xsl; *len = xsl_lotbl_xsl_len; } else { if (verbosity >= NORMAL) { fprintf(stderr, S_BAD_TYPE_ERR, type); } exit(EXIT_BAD_TYPE); } } static xmlDocPtr generate_fm(xmlDocPtr doc, const char *type, const char **params) { unsigned char *xsl; unsigned int len; get_builtin_xsl(type, &xsl, &len); return transform_doc_builtin(doc, xsl, len, params); } static void set_def_param(const char **params, int i, const char *val) { char *s; s = malloc(strlen(val) + 3); sprintf(s, "'%s'", val); free((char *) params[i]); params[i] = s; } /* Default types of frontmatter where "deleted" objects and elements should be * ignored. */ static bool default_ignore_del(const char *type) { return strcmp(type, "LOA") == 0 || strcmp(type, "LOASD") == 0 || strcmp(type, "LOEDM") == 0 || strcmp(type, "LOI") == 0 || strcmp(type, "LOS") == 0 || strcmp(type, "LOT") == 0 || strcmp(type, "LOTBL") == 0 || strcmp(type, "TOC") == 0 || strcmp(type, "TP") == 0; } /* Remove "deleted" DMs from the flattend PM format. */ static void rem_deleted_dmodules(xmlDocPtr doc) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; ctx = xmlXPathNewContext(doc); obj = xmlXPathEvalExpression(BAD_CAST "//dmodule[identAndStatusSection/dmStatus/@issueType='deleted' or status/issno/@isstype='deleted']", ctx); if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) { int i; for (i = 0; i < obj->nodesetval->nodeNr; ++i) { xmlUnlinkNode(obj->nodesetval->nodeTab[i]); xmlFreeNode(obj->nodesetval->nodeTab[i]); obj->nodesetval->nodeTab[i] = NULL; } } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); } static xmlDocPtr generate_fm_content_for_type(xmlDocPtr doc, const char *type, const char *fmxsl, const char *xslpath, const char **params, const bool ignore_del) { xmlDocPtr src, res = NULL; int i = 0; /* Supply values to default parameters. */ for (i = 0; params[i]; i += 2) { if (strcmp(params[i], "type") == 0) { set_def_param(params, i + 1, type); } } /* Certain types of FM should ignore "deleted" objects/elements. */ if (ignore_del) { src = xmlCopyDoc(doc, 1); rem_deleted_dmodules(src); rem_delete_elems(src); } else { src = doc; } /* Generate contents. */ if (xslpath) { res = transform_doc(src, xslpath, params); } else if (fmxsl) { res = transform_doc(src, fmxsl, params); } else { res = generate_fm(src, type, params); } if (ignore_del) { xmlFreeDoc(src); } return res; } static xmlNodePtr find_fm(xmlDocPtr fmtypes, const xmlChar *incode, const xmlChar *incodev) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlNodePtr node; ctx = xmlXPathNewContext(fmtypes); xmlXPathRegisterVariable(ctx, BAD_CAST "c", xmlXPathNewString(BAD_CAST incode)); xmlXPathRegisterVariable(ctx, BAD_CAST "v", xmlXPathNewString(BAD_CAST incodev)); obj = xmlXPathEvalExpression(BAD_CAST "//fm[starts-with(concat($c,$v),@infoCode)]", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { node = NULL; } else { node = obj->nodesetval->nodeTab[0]; } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); return node; } /* Copy elements from the source TP DM that can't be derived from the PM. */ static void copy_tp_elems(xmlDocPtr res, xmlDocPtr doc) { xmlNodePtr fmtp, node; fmtp = first_xpath_node(res, NULL, BAD_CAST "//frontMatterTitlePage"); if (!fmtp) { return; } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//productIntroName"))) { xmlAddPrevSibling(first_xpath_node(res, fmtp, BAD_CAST "pmTitle"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//externalPubCode"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "issueDate"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//productAndModel"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(issueDate|externalPubCode)[last()]"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//productIllustration"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(security|derivativeClassification|dataRestrictions)[last()]"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//enterpriseSpec"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(security|derivativeClassification|dataRestrictions|productIllustration)[last()]"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//enterpriseLogo"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(security|derivativeClassification|dataRestrictions|productIllustration|enterpriseSpec)[last()]"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//barCode"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(responsiblePartnerCompany|publisherLogo)[last()]"), xmlCopyNode(node, 1)); } if ((node = first_xpath_node(doc, NULL, BAD_CAST "//frontMatterInfo"))) { xmlAddNextSibling(first_xpath_node(res, fmtp, BAD_CAST "(responsiblePartnerCompany|publisherLogo|barCode)[last()]"), xmlCopyNode(node, 1)); } } static void set_issue_date(xmlDocPtr doc, xmlDocPtr pm, const char *issdate) { xmlNodePtr dm_issue_date; xmlChar *year, *month, *day; if (!(dm_issue_date = first_xpath_node(doc, NULL, BAD_CAST "//issueDate|//issdate"))) { return; } if (strcasecmp(issdate, "pm") == 0) { xmlNodePtr pm_issue_date; if (!(pm_issue_date = first_xpath_node(pm, NULL, BAD_CAST "//issueDate|//issdate"))) { return; } year = xmlGetProp(pm_issue_date, BAD_CAST "year"); month = xmlGetProp(pm_issue_date, BAD_CAST "month"); day = xmlGetProp(pm_issue_date, BAD_CAST "day"); } else { year = malloc(5 * sizeof(xmlChar)); month = malloc(3 * sizeof(xmlChar)); day = malloc(3 * sizeof(xmlChar)); if (strcmp(issdate, "-") == 0) { time_t now; struct tm *local; time(&now); local = localtime(&now); xmlStrPrintf(year, 5, "%.4d", local->tm_year + 1900); xmlStrPrintf(month, 3, "%.2d", local->tm_mon + 1); xmlStrPrintf(day, 3, "%.2d", local->tm_mday); } else { int n; n = sscanf(issdate, "%4s-%2s-%2s", year, month, day); if (n != 3) { if (verbosity >= NORMAL) { fprintf(stderr, E_BAD_DATE, issdate); } exit(EXIT_BAD_DATE); } } } xmlSetProp(dm_issue_date, BAD_CAST "year", year); xmlSetProp(dm_issue_date, BAD_CAST "month", month); xmlSetProp(dm_issue_date, BAD_CAST "day", day); xmlFree(year); xmlFree(month); xmlFree(day); } static void generate_fm_content_for_dm( xmlDocPtr pm, const char *dmpath, xmlDocPtr fmtypes, const char *fmtype, bool overwrite, const char *xslpath, const char **params, const char *issdate) { xmlDocPtr doc, res = NULL; char *type, *fmxsl; bool ignore_del; if (!(doc = read_xml_doc(dmpath))) { return; } if (fmtype) { type = strdup(fmtype); fmxsl = NULL; ignore_del = default_ignore_del(type); } else { xmlChar *incode, *incodev; xmlNodePtr fm; incode = first_xpath_string(doc, NULL, BAD_CAST "//@infoCode|//incode"); incodev = first_xpath_string(doc, NULL, BAD_CAST "//@infoCodeVariant|//incodev"); fm = find_fm(fmtypes, incode, incodev); if (fm) { xmlChar *igndel; type = (char *) xmlGetProp(fm, BAD_CAST "type"); fmxsl = (char *) xmlGetProp(fm, BAD_CAST "xsl"); if ((igndel = xmlGetProp(fm, BAD_CAST "ignoreDel"))) { ignore_del = xmlStrcmp(igndel, BAD_CAST "yes") == 0; } else { ignore_del = default_ignore_del(type); } xmlFree(igndel); } else { if (verbosity >= DEBUG) { fprintf(stderr, I_NO_INFOCODE, dmpath, incode, incodev); } type = NULL; fmxsl = NULL; ignore_del = false; } xmlFree(incode); xmlFree(incodev); } if (type) { xmlNodePtr root; if (verbosity >= VERBOSE) { fprintf(stderr, I_GENERATE, dmpath, type); } if (!(res = generate_fm_content_for_type(pm, type, fmxsl, xslpath, params, ignore_del))) { if (verbosity >= NORMAL) { fprintf(stderr, E_BAD_STYLESHEET, dmpath, xslpath ? xslpath : fmxsl); } exit(EXIT_BAD_STYLESHEET); } if (strcmp(type, "TP") == 0) { copy_tp_elems(res, doc); } /* Merge the results of the transformation with the original DM * based on the name of the root element. */ if ((root = xmlDocGetRootElement(res))) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; ctx = xmlXPathNewContext(doc); xmlXPathRegisterVariable(ctx, BAD_CAST "name", xmlXPathNewString(root->name)); obj = xmlXPathEvalExpression(BAD_CAST "//*[name()=$name]", ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { if (verbosity >= NORMAL) { fprintf(stderr, E_MERGE_NAME, dmpath, (char *) root->name); } exit(EXIT_MERGE); } else { xmlAddNextSibling(obj->nodesetval->nodeTab[0], xmlCopyNode(root, 1)); xmlUnlinkNode(obj->nodesetval->nodeTab[0]); xmlFreeNode(obj->nodesetval->nodeTab[0]); obj->nodesetval->nodeTab[0] = NULL; } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); } else { if (verbosity >= NORMAL) { fprintf(stderr, E_MERGE_ELEM, dmpath); } exit(EXIT_MERGE); } if (issdate) { set_issue_date(doc, pm, issdate); } if (overwrite) { save_xml_doc(doc, dmpath); } else { save_xml_doc(doc, "-"); } } xmlFree(type); xmlFree(fmxsl); xmlFreeDoc(doc); xmlFreeDoc(res); } static void generate_fm_content_for_list( xmlDocPtr pm, const char *path, xmlDocPtr fmtypes, const char *fmtype, bool overwrite, const char *xslpath, const char **params, const char *issdate) { FILE *f; char line[PATH_MAX]; if (path) { if (!(f = fopen(path, "r"))) { if (verbosity >= NORMAL) { fprintf(stderr, E_BAD_LIST, path); } return; } } else { f = stdin; } while (fgets(line, PATH_MAX, f)) { strtok(line, "\t\r\n"); generate_fm_content_for_dm(pm, line, fmtypes, fmtype, overwrite, xslpath, params, issdate); } if (path) { fclose(f); } } static void dump_fmtypes_xml(void) { xmlDocPtr doc; doc = read_xml_mem((const char *) fmtypes_xml, fmtypes_xml_len); save_xml_doc(doc, "-"); xmlFreeDoc(doc); } static void dump_fmtypes_txt(void) { printf("%.*s", fmtypes_txt_len, fmtypes_txt); } static void dump_builtin_xsl(const char *type) { unsigned char *xsl; unsigned int len; get_builtin_xsl(type, &xsl, &len); printf("%.*s", len, xsl); } static void fix_fmxsl_paths(xmlDocPtr doc, const char *path) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; ctx = xmlXPathNewContext(doc); obj = xmlXPathEvalExpression(BAD_CAST "//@xsl", ctx); if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) { int i; for (i = 0; i < obj->nodesetval->nodeNr; ++i) { xmlChar *xsl = xmlNodeGetContent(obj->nodesetval->nodeTab[i]); xmlChar *URI = xmlBuildURI(xsl, BAD_CAST path); xmlNodeSetContent(obj->nodesetval->nodeTab[i], URI); xmlFree(xsl); xmlFree(URI); } } xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); } static xmlDocPtr read_fmtypes(const char *path) { xmlDocPtr doc; if (!(doc = read_xml_doc(path))) { FILE *f; char line[256]; xmlNodePtr root; doc = xmlNewDoc(BAD_CAST "1.0"); root = xmlNewNode(NULL, BAD_CAST "fmtypes"); xmlDocSetRootElement(doc, root); f = fopen(path, "r"); while (fgets(line, 256, f)) { char incode[5] = "", type[32] = "", xsl[1024] = ""; xmlNodePtr fm; int n; n = sscanf(line, "%4s %31s %1023[^\n]", incode, type, xsl); fm = xmlNewChild(root, NULL, BAD_CAST "fm", NULL); xmlSetProp(fm, BAD_CAST "infoCode", BAD_CAST incode); xmlSetProp(fm, BAD_CAST "type", BAD_CAST type); if (n == 3) { xmlSetProp(fm, BAD_CAST "xsl", BAD_CAST xsl); } } fclose(f); } fix_fmxsl_paths(doc, path); return doc; } static void add_param(xmlNodePtr params, char *s) { char *n, *v; xmlNodePtr p; n = strtok(s, "="); v = strtok(NULL, ""); p = xmlNewChild(params, NULL, BAD_CAST "param", NULL); xmlSetProp(p, BAD_CAST "name", BAD_CAST n); xmlSetProp(p, BAD_CAST "value", BAD_CAST v); } static void add_def_param(xmlNodePtr params, const char *s) { xmlNodePtr p; p = xmlNewChild(params, NULL, BAD_CAST "param", NULL); xmlSetProp(p, BAD_CAST "name", BAD_CAST s); xmlSetProp(p, BAD_CAST "value", BAD_CAST ""); } static void show_help(void) { puts("Usage: " PROG_NAME " [-D ] [-F ] [-I ] [-P ] [-p = ...] [-t ] [-x ] [-,flqvh?] [...]"); puts(""); puts("Options:"); puts(" -,, --dump-fmtypes-xml Dump the built-in .fmtypes file in XML format."); puts(" -., --dump-fmtypes Dump the built-in .fmtypes file in simple text format."); puts(" -D, --dump-xsl Dump the built-in XSLT for a type of front matter."); puts(" -F, --fmtypes Specify .fmtypes file."); puts(" -f, --overwrite Overwrite input data modules."); puts(" -h, -?, --help Show usage message."); puts(" -I, --date Set the issue date of the generated front matter."); puts(" -l, --list Treat input as list of data modules."); puts(" -P, --pm Generate front matter from the specified PM."); puts(" -p, --param = Pass parameters to the XSLT used to generate the front matter."); puts(" -q, --quiet Quiet mode."); puts(" -t, --type Generate the specified type of front matter."); puts(" -v, --verbose Verbose output."); puts(" -x, --xsl Override built-in or user-configured XSLT."); puts(" --version Show version information."); puts(" Generate front matter content based on the specified data modules."); 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); } int main(int argc, char **argv) { int i; const char *sopts = ",.D:F:fI:lP:p:qt:vx:h?"; struct option lopts[] = { {"version" , no_argument , 0, 0}, {"help" , no_argument , 0, 'h'}, {"dump-fmtypes-xml", no_argument , 0, ','}, {"dump-fmtypes" , no_argument , 0, '.'}, {"dump-xsl" , required_argument, 0, 'D'}, {"fmtypes" , required_argument, 0, 'F'}, {"date" , required_argument, 0, 'I'}, {"overwrite" , no_argument , 0, 'f'}, {"list" , no_argument , 0, 'l'}, {"pm" , required_argument, 0, 'P'}, {"param" , required_argument, 0, 'p'}, {"quiet" , no_argument , 0, 'q'}, {"type" , required_argument, 0, 't'}, {"verbose" , no_argument , 0, 'v'}, {"xsl" , required_argument, 0, 'x'}, LIBXML2_PARSE_LONGOPT_DEFS {0, 0, 0, 0} }; int loptind = 0; char *pmpath = NULL; char *fmtype = NULL; xmlDocPtr pm, fmtypes = NULL; bool overwrite = false; bool islist = false; char *xslpath = NULL; xmlNodePtr params_node; int nparams = 0; const char **params = NULL; char *issdate = NULL; params_node = xmlNewNode(NULL, BAD_CAST "params"); /* Default parameter placeholders. */ add_def_param(params_node, "type"); ++nparams; 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 ',': dump_fmtypes_xml(); return 0; case '.': dump_fmtypes_txt(); return 0; case 'D': dump_builtin_xsl(optarg); return 0; case 'F': if (!fmtypes) { fmtypes = read_fmtypes(optarg); } break; case 'f': overwrite = true; break; case 'I': issdate = strdup(optarg); break; case 'l': islist = true; break; case 'P': pmpath = strdup(optarg); break; case 'p': add_param(params_node, optarg); ++nparams; break; case 'q': --verbosity; break; case 't': fmtype = strdup(optarg); break; case 'v': ++verbosity; break; case 'x': xslpath = strdup(optarg); break; case 'h': case '?': show_help(); return 0; } } if (nparams > 0) { xmlNodePtr p; int n = 0; params = malloc((nparams * 2 + 1) * sizeof(char *)); for (p = params_node->children; p; p = p->next) { char *name, *value; name = (char *) xmlGetProp(p, BAD_CAST "name"); value = (char *) xmlGetProp(p, BAD_CAST "value"); params[n++] = name; params[n++] = value; } params[n] = NULL; } xmlFreeNode(params_node); if (!fmtypes) { char fmtypes_fname[PATH_MAX]; if (find_config(fmtypes_fname, DEFAULT_FMTYPES_FNAME)) { fmtypes = read_fmtypes(fmtypes_fname); } else { fmtypes = read_xml_mem((const char *) fmtypes_xml, fmtypes_xml_len); } } if (!pmpath) { pmpath = strdup("-"); } pm = read_xml_doc(pmpath); if (optind < argc) { void (*gen_fn)(xmlDocPtr, const char *, xmlDocPtr, const char *, bool, const char *, const char **, const char *); if (islist) { gen_fn = generate_fm_content_for_list; } else { gen_fn = generate_fm_content_for_dm; } for (i = optind; i < argc; ++i) { gen_fn(pm, argv[i], fmtypes, fmtype, overwrite, xslpath, params, issdate); } } else if (fmtype) { xmlDocPtr res; res = generate_fm_content_for_type(pm, fmtype, NULL, xslpath, params, default_ignore_del(fmtype)); save_xml_doc(res, "-"); xmlFreeDoc(res); } else if (islist) { generate_fm_content_for_list(pm, NULL, fmtypes, fmtype, overwrite, xslpath, params, issdate); } else { if (verbosity >= NORMAL) { fprintf(stderr, S_NO_TYPE_ERR); } exit(EXIT_NO_TYPE); } for (i = 0; i < nparams; ++i) { xmlFree((char *) params[i * 2]); xmlFree((char *) params[i * 2 + 1]); } free(params); xmlFreeDoc(pm); xmlFreeDoc(fmtypes); free(pmpath); free(fmtype); free(xslpath); xmlFree(issdate); xsltCleanupGlobals(); xmlCleanupParser(); return 0; }