#include #include #include #include #include #include #include #include #include "s1kd_tools.h" /* Order of references */ #define DM "0" /* dmRef */ #define PM "1" /* pmRef */ #define EP "2" /* externalPubRef */ #define PROG_NAME "s1kd-syncrefs" #define VERSION "1.9.0" #define ERR_PREFIX PROG_NAME ": ERROR: " #define INF_PREFIX PROG_NAME ": INFO: " #define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n" #define E_MAX_REFS ERR_PREFIX "Maximum references reached: %d\n" #define I_SYNCREFS INF_PREFIX "Synchronizing references in %s...\n" #define I_DELREFS INF_PREFIX "Deleting refs table in %s...\n" #define EXIT_INVALID_DM 1 #define EXIT_MAX_REFS 2 static unsigned MAX_REFS = 1; struct ref { char code[256]; xmlNodePtr ref; }; static bool only_delete = false; static enum verbosity { QUIET, NORMAL, VERBOSE } verbosity = NORMAL; static struct ref *refs; static int nrefs; static bool contains_code(const char *code) { int i; for (i = 0; i < nrefs; ++i) { if (strcmp(refs[i].code, code) == 0) { return true; } } return false; } static xmlNodePtr find_child(xmlNodePtr parent, const char *child_name) { xmlNodePtr cur; for (cur = parent->children; cur; cur = cur->next) { if (strcmp((char *) cur->name, child_name) == 0) { return cur; } } return NULL; } static xmlNodePtr first_xpath_node(xmlDocPtr doc, xmlNodePtr node, const char *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 char *first_xpath_string(xmlDocPtr doc, xmlNodePtr node, const char *expr) { return (char *) xmlNodeGetContent(first_xpath_node(doc, node, expr)); } static bool is_ref(xmlNodePtr node) { return node->type == XML_ELEMENT_NODE && ( xmlStrcmp(node->name, BAD_CAST "dmRef") == 0 || xmlStrcmp(node->name, BAD_CAST "refdm") == 0 || xmlStrcmp(node->name, BAD_CAST "pmRef") == 0 || xmlStrcmp(node->name, BAD_CAST "reftp") == 0 || xmlStrcmp(node->name, BAD_CAST "externalPubRef") == 0); } static void copy_code(char *dst, xmlNodePtr ref) { xmlNodePtr code; char *model_ident_code; if (xmlStrcmp(ref->name, BAD_CAST "dmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "refdm") == 0) { char *system_diff_code; char *system_code; char *sub_system_code; char *sub_sub_system_code; char *assy_code; char *disassy_code; char *disassy_code_variant; char *info_code; char *info_code_variant; char *item_location_code; char *learn_code; char *learn_event_code; char learn[6] = ""; code = first_xpath_node(NULL, ref, ".//dmCode|.//avee"); model_ident_code = first_xpath_string(NULL, code, "@modelIdentCode|modelic"); system_diff_code = first_xpath_string(NULL, code, "@systemDiffCode|sdc"); system_code = first_xpath_string(NULL, code, "@systemCode|chapnum"); sub_system_code = first_xpath_string(NULL, code, "@subSystemCode|section"); sub_sub_system_code = first_xpath_string(NULL, code, "@subSubSystemCode|subsect"); assy_code = first_xpath_string(NULL, code, "@assyCode|subject"); disassy_code = first_xpath_string(NULL, code, "@disassyCode|discode"); disassy_code_variant = first_xpath_string(NULL, code, "@disassyCodeVariant|discodev"); info_code = first_xpath_string(NULL, code, "@infoCode|incode"); info_code_variant = first_xpath_string(NULL, code, "@infoCodeVariant|incodev"); item_location_code = first_xpath_string(NULL, code, "@itemLocationCode|itemloc"); learn_code = first_xpath_string(NULL, code, "@learnCode"); learn_event_code = first_xpath_string(NULL, code, "@learnEventCode"); if (learn_code && learn_event_code) sprintf(learn, "-%s%s", learn_code, learn_event_code); sprintf(dst, DM"%s-%s-%s-%s%s-%s-%s%s-%s%s-%s%s", model_ident_code, system_diff_code, system_code, sub_system_code, sub_sub_system_code, assy_code, disassy_code, disassy_code_variant, info_code, info_code_variant, item_location_code, learn); xmlFree(model_ident_code); xmlFree(system_diff_code); xmlFree(system_code); xmlFree(sub_system_code); xmlFree(sub_sub_system_code); xmlFree(assy_code); xmlFree(disassy_code); xmlFree(disassy_code_variant); xmlFree(info_code); xmlFree(info_code_variant); xmlFree(item_location_code); xmlFree(learn_code); xmlFree(learn_event_code); } else if (xmlStrcmp(ref->name, BAD_CAST "pmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "reftp") == 0) { char *pm_issuer; char *pm_number; char *pm_volume; code = first_xpath_node(NULL, ref, ".//pmCode|.//pmc"); model_ident_code = (char *) xmlGetProp(code, BAD_CAST "modelIdentCode"); pm_issuer = (char *) xmlGetProp(code, BAD_CAST "pmIssuer"); pm_number = (char *) xmlGetProp(code, BAD_CAST "pmNumber"); pm_volume = (char *) xmlGetProp(code, BAD_CAST "pmVolume"); sprintf(dst, PM"%s-%s-%s-%s", model_ident_code, pm_issuer, pm_number, pm_volume); xmlFree(model_ident_code); xmlFree(pm_issuer); xmlFree(pm_number); xmlFree(pm_volume); } else if (xmlStrcmp(ref->name, BAD_CAST "externalPubRef") == 0) { xmlNodePtr title; code = first_xpath_node(NULL, ref, ".//externalPubCode"); title = first_xpath_node(NULL, ref, ".//externalPubTitle"); if (code) { char *code_content; code_content = (char *) xmlNodeGetContent(code); sprintf(dst, EP"%s", code_content); xmlFree(code_content); } else if (title) { char *title_content; title_content = (char *) xmlNodeGetContent(title); sprintf(dst, EP"%s", title_content); xmlFree(title_content); } } else { strcpy(dst, ""); } } static void resize(void) { if (!(refs = realloc(refs, (MAX_REFS *= 2) * sizeof(struct ref)))) { if (verbosity >= NORMAL) { fprintf(stderr, E_MAX_REFS, nrefs); } exit(EXIT_MAX_REFS); } } static void find_refs(xmlNodePtr node) { xmlNodePtr cur; if (is_ref(node)) { char code[256]; copy_code(code, node); if (!contains_code(code)) { if (nrefs == MAX_REFS) { resize(); } strcpy(refs[nrefs].code, code); refs[nrefs].ref = node; ++nrefs; } } else { for (cur = node->children; cur; cur = cur->next) { find_refs(cur); } } } static int compare_refs(const void *a, const void *b) { struct ref *ref1 = (struct ref *) a; struct ref *ref2 = (struct ref *) b; return strcmp(ref1->code, ref2->code); } static void sync_refs(xmlNodePtr dmodule) { int i; xmlNodePtr content, old_refs, new_refs, searchable, new_node, refgrp = NULL, refdms = NULL, reftp = NULL, rdandrt = NULL; nrefs = 0; content = find_child(dmodule, "content"); old_refs = find_child(content, "refs"); if (old_refs) { refgrp = first_xpath_node(NULL, old_refs, "norefs|refdms|reftp|rdandrt"); xmlUnlinkNode(old_refs); xmlFreeNode(old_refs); } if (only_delete) return; searchable = xmlLastElementChild(content); if (!searchable) { if (verbosity >= NORMAL) { fprintf(stderr, ERR_PREFIX "Invalid data module.\n"); } exit(EXIT_INVALID_DM); } find_refs(searchable); if (nrefs < 1) { return; } new_refs = xmlNewNode(NULL, BAD_CAST "refs"); xmlAddPrevSibling(content->children, new_refs); if (refgrp) { refdms = xmlNewChild(new_refs, NULL, BAD_CAST "refdms", NULL); reftp = xmlNewChild(new_refs, NULL, BAD_CAST "reftp", NULL); rdandrt = xmlNewChild(new_refs, NULL, BAD_CAST "rdandrt", NULL); } qsort(refs, nrefs, sizeof(struct ref), compare_refs); for (i = 0; i < nrefs; ++i) { if (refgrp) { if (xmlStrcmp(refs[i].ref->name, BAD_CAST "refdm") == 0) { new_node = xmlAddChild(refdms, xmlCopyNode(refs[i].ref, 1)); xmlUnsetProp(new_node, BAD_CAST "id"); new_node = xmlAddChild(rdandrt, xmlCopyNode(refs[i].ref, 1)); xmlUnsetProp(new_node, BAD_CAST "id"); } else if (xmlStrcmp(refs[i].ref->name, BAD_CAST "reftp") == 0) { new_node = xmlAddChild(reftp, xmlCopyNode(refs[i].ref, 1)); xmlUnsetProp(new_node, BAD_CAST "id"); new_node = xmlAddChild(reftp, xmlCopyNode(refs[i].ref, 1)); xmlUnsetProp(new_node, BAD_CAST "id"); } } else { new_node = xmlAddChild(new_refs, xmlCopyNode(refs[i].ref, 1)); xmlUnsetProp(new_node, BAD_CAST "id"); } } if (refgrp) { if (!refdms->children) { xmlUnlinkNode(refdms); xmlFreeNode(refdms); xmlUnlinkNode(rdandrt); xmlFreeNode(rdandrt); } else if (!reftp->children) { xmlUnlinkNode(reftp); xmlFreeNode(reftp); xmlUnlinkNode(rdandrt); xmlFreeNode(rdandrt); } else { xmlUnlinkNode(rdandrt); xmlFreeNode(rdandrt); } } } static void sync_refs_file(const char *path, const char *out, bool overwrite) { xmlDocPtr dm; xmlNodePtr dmodule; if (verbosity >= VERBOSE) { if (only_delete) { fprintf(stderr, I_DELREFS, path); } else { fprintf(stderr, I_SYNCREFS, path); } } if (!(dm = read_xml_doc(path))) { return; } dmodule = xmlDocGetRootElement(dm); sync_refs(dmodule); if (overwrite) { save_xml_doc(dm, path); } else { save_xml_doc(dm, out); } xmlFreeDoc(dm); } static void sync_refs_list(const char *path, const char *out, bool overwrite) { 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"); sync_refs_file(line, out, overwrite); } if (path) { fclose(f); } } static void show_help(void) { puts("Usage: " PROG_NAME " [-dflqvh?] [-o ] []"); puts(""); puts("Options:"); puts(" -d, --delete Delete the references table."); puts(" -f, --overwrite Overwrite the data modules automatically."); puts(" -h, -?, --help Show help/usage message."); puts(" -l, --list Treat input as list of CSDB objects."); puts(" -o, --out Output to instead of stdout."); puts(" -q, --quiet Quiet mode."); puts(" -v, --verbose Verbose output."); puts(" --version Show version information."); puts(" Any number of data modules. Otherwise, read from stdin."); LIBXML2_PARSE_LONGOPT_HELP } static void show_version(void) { printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION); printf("Using libxml %s\n", xmlParserVersion); } int main(int argc, char *argv[]) { int i; char out[PATH_MAX] = "-"; bool overwrite = false; bool islist = false; const char *sopts = "dflo:qvh?"; struct option lopts[] = { {"version" , no_argument , 0, 0}, {"help" , no_argument , 0, 'h'}, {"delete" , no_argument , 0, 'd'}, {"overwrite", no_argument , 0, 'f'}, {"list" , no_argument , 0, 'l'}, {"out" , required_argument, 0, 'o'}, {"quiet" , no_argument , 0, 'q'}, {"verbose" , no_argument , 0, 'v'}, LIBXML2_PARSE_LONGOPT_DEFS {0, 0, 0, 0} }; int loptind = 0; refs = malloc(MAX_REFS * sizeof(struct ref)); 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 'd': only_delete = true; break; case 'f': overwrite = true; break; case 'l': islist = true; break; case 'o': strcpy(out, optarg); break; case 'q': --verbosity; break; case 'v': ++verbosity; break; case 'h': case '?': show_help(); return 0; } } if (optind < argc) { for (i = optind; i < argc; ++i) { if (islist) { sync_refs_list(argv[i], out, overwrite); } else { sync_refs_file(argv[i], out, overwrite); } } } else if (islist) { sync_refs_list(NULL, out, overwrite); } else { sync_refs_file("-", out, false); } free(refs); xmlCleanupParser(); return 0; }