#include #include #include #include #include #include #include #include #include "s1kd_tools.h" #define PROG_NAME "s1kd-mvref" #define VERSION "2.6.0" #define ERR_PREFIX PROG_NAME ": ERROR: " #define E_ENCODING_ERROR ERR_PREFIX "Error encoding path name.\n" #define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n" #define EXIT_ENCODING_ERROR 1 #define EXIT_NO_FILE 2 #define ADDR_PATH "//dmAddress|//dmaddres|//pmAddress|//pmaddres" #define REFS_PATH_CONTENT BAD_CAST "//content//dmRef|//content//refdm[*]|//content//pmRef|//content/refpm" #define REFS_PATH BAD_CAST "//dmRef|//pmRef|//refdm[*]|//refpm" static enum verbosity { QUIET, NORMAL, VERBOSE } verbosity = NORMAL; static xmlNodePtr firstXPathNode(const char *xpath, xmlDocPtr doc, xmlNodePtr node) { xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlNodePtr first; if (doc) { ctx = xmlXPathNewContext(doc); } else if (node) { ctx = xmlXPathNewContext(node->doc); } else { return NULL; } ctx->node = node; obj = xmlXPathEvalExpression(BAD_CAST xpath, ctx); if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) first = NULL; else first = obj->nodesetval->nodeTab[0]; xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); return first; } static char *firstXPathString(const char *xpath, xmlDocPtr doc, xmlNodePtr node) { return (char *) xmlNodeGetContent(firstXPathNode(xpath, doc, node)); } static xmlNodePtr findChild(xmlNodePtr parent, const char *name) { xmlNodePtr cur; if (!parent) return NULL; for (cur = parent->children; cur; cur = cur->next) { if (strcmp((char *) cur->name, name) == 0) { return cur; } } return NULL; } static void replaceNode(xmlNodePtr a, xmlNodePtr b) { xmlNodePtr c; c = xmlCopyNode(b, 1); xmlNodeSetName(c, a->name); xmlAddNextSibling(a, c); xmlUnlinkNode(a); xmlFreeNode(a); } static void getPmCode(char *dst, xmlNodePtr ident, bool withIssue, bool withLang) { xmlNodePtr identExtension, pmCode, issueInfo, language; char *modelIdentCode, *pmIssuer, *pmNumber, *pmVolume; char cat[256]; identExtension = findChild(ident, "identExtension"); pmCode = firstXPathNode("pmCode|pmc", NULL, ident); issueInfo = firstXPathNode("issueInfo|issno", NULL, ident); language = findChild(ident, "language"); strcpy(dst, ""); if (identExtension) { char *extensionProducer, *extensionCode; extensionProducer = (char *) xmlGetProp(identExtension, BAD_CAST "extensionProducer"); extensionCode = (char *) xmlGetProp(identExtension, BAD_CAST "extensionCode"); snprintf(cat, 256, "%s-%s-", extensionProducer, extensionCode); xmlFree(extensionProducer); xmlFree(extensionCode); strcat(dst, cat); } modelIdentCode = firstXPathString("@modelIdentCode|modelic", NULL, pmCode); pmIssuer = firstXPathString("@pmIssuer|pmissuer", NULL, pmCode); pmNumber = firstXPathString("@pmNumber|pmnumber", NULL, pmCode); pmVolume = firstXPathString("@pmVolume|pmvolume", NULL, pmCode); snprintf(cat, 256, "%s-%s-%s-%s", modelIdentCode, pmIssuer, pmNumber, pmVolume); xmlFree(modelIdentCode); xmlFree(pmIssuer); xmlFree(pmNumber); xmlFree(pmVolume); strcat(dst, cat); if (withIssue && issueInfo) { char *issueNumber, *inWork; issueNumber = firstXPathString("@issueNumber|@issno", NULL, issueInfo); inWork = firstXPathString("@inWork|@inwork", NULL, issueInfo); snprintf(cat, 256, "_%s-%s", issueNumber, inWork ? inWork : "00"); xmlFree(issueNumber); xmlFree(inWork); strcat(dst, cat); } if (withLang && language) { char *languageIsoCode, *countryIsoCode; languageIsoCode = firstXPathString("@languageIsoCode|@language", NULL, language); countryIsoCode = firstXPathString("@countryIsoCode|@country", NULL, language); snprintf(cat, 256, "_%s-%s", languageIsoCode, countryIsoCode); xmlFree(languageIsoCode); xmlFree(countryIsoCode); strcat(dst, cat); } } static void getDmCode(char *dst, xmlNodePtr ident, bool withIssue, bool withLang) { xmlNodePtr identExtension, dmCode, issueInfo, language; char *modelIdentCode; char *systemDiffCode; char *systemCode; char *subSystemCode; char *subSubSystemCode; char *assyCode; char *disassyCode; char *disassyCodeVariant; char *infoCode; char *infoCodeVariant; char *itemLocationCode; char *learnCode; char *learnEventCode; char cat[256]; identExtension = firstXPathNode("identExtension|dmcextension", NULL, ident); dmCode = firstXPathNode("dmCode|.//avee", NULL, ident); issueInfo = firstXPathNode("issueInfo|issno", NULL, ident); language = findChild(ident, "language"); strcpy(dst, ""); if (identExtension) { char *extensionProducer, *extensionCode; extensionProducer = firstXPathString("@extensionProducer|dmeproducer", NULL, identExtension); extensionCode = firstXPathString("@extensionCode|dmecode", NULL, identExtension); snprintf(cat, 256, "%s-%s-", extensionProducer, extensionCode); xmlFree(extensionProducer); xmlFree(extensionCode); strcat(dst, cat); } modelIdentCode = firstXPathString("@modelIdentCode|modelic", NULL, dmCode); systemDiffCode = firstXPathString("@systemDiffCode|sdc", NULL, dmCode); systemCode = firstXPathString("@systemCode|chapnum", NULL, dmCode); subSystemCode = firstXPathString("@subSystemCode|section", NULL, dmCode); subSubSystemCode = firstXPathString("@subSubSystemCode|subsect", NULL, dmCode); assyCode = firstXPathString("@assyCode|subject", NULL, dmCode); disassyCode = firstXPathString("@disassyCode|discode", NULL, dmCode); disassyCodeVariant = firstXPathString("@disassyCodeVariant|discodev", NULL, dmCode); infoCode = firstXPathString("@infoCode|incode", NULL, dmCode); infoCodeVariant = firstXPathString("@infoCodeVariant|incodev", NULL, dmCode); itemLocationCode = firstXPathString("@itemLocationCode|itemloc", NULL, dmCode); learnCode = firstXPathString("@learnCode", NULL, dmCode); learnEventCode = firstXPathString("@learnEventCode", NULL, dmCode); snprintf(cat, 256, "%s-%s-%s-%s%s-%s-%s%s-%s%s-%s", modelIdentCode, systemDiffCode, systemCode, subSystemCode, subSubSystemCode, assyCode, disassyCode, disassyCodeVariant, infoCode, infoCodeVariant, itemLocationCode); xmlFree(modelIdentCode); xmlFree(systemDiffCode); xmlFree(systemCode); xmlFree(subSystemCode); xmlFree(subSubSystemCode); xmlFree(assyCode); xmlFree(disassyCode); xmlFree(disassyCodeVariant); xmlFree(infoCode); xmlFree(infoCodeVariant); xmlFree(itemLocationCode); strcat(dst, cat); if (learnCode && learnEventCode) { snprintf(cat, 256, "-%s%s", learnCode, learnEventCode); strcat(dst, cat); } xmlFree(learnCode); xmlFree(learnEventCode); if (withIssue && issueInfo) { char *issueNumber, *inWork; issueNumber = firstXPathString("@issueNumber|@issno", NULL, issueInfo); inWork = firstXPathString("@inWork|@inwork", NULL, issueInfo); snprintf(cat, 256, "_%s-%s", issueNumber, inWork ? inWork : "00"); xmlFree(issueNumber); xmlFree(inWork); strcat(dst, cat); } if (withLang && language) { char *languageIsoCode, *countryIsoCode; languageIsoCode = firstXPathString("@languageIsoCode|@language", NULL, language); countryIsoCode = firstXPathString("@countryIsoCode|@country", NULL, language); snprintf(cat, 256, "_%s-%s", languageIsoCode, countryIsoCode); xmlFree(languageIsoCode); xmlFree(countryIsoCode); strcat(dst, cat); } } static bool isDmRef(xmlNodePtr ref) { return xmlStrcmp(ref->name, BAD_CAST "dmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "refdm") == 0; } static bool isDmAddress(xmlNodePtr address) { return xmlStrcmp(address->name, BAD_CAST "dmAddress") == 0 || xmlStrcmp(address->name, BAD_CAST "dmaddres") == 0; } static bool sameDm(xmlNodePtr ref, xmlNodePtr address) { char refcode[256], addcode[256]; bool withIssue, withLang; xmlNodePtr ref_dmIdent; xmlNodePtr add_dmIdent; if (!isDmRef(ref) || !isDmAddress(address)) return false; ref_dmIdent = firstXPathNode("dmRefIdent|self::refdm", NULL, ref); add_dmIdent = firstXPathNode("dmIdent|self::dmaddres", NULL, address); withIssue = firstXPathNode(".//issueInfo|.//issno", NULL, ref); withLang = firstXPathNode(".//language", NULL, ref); getDmCode(refcode, ref_dmIdent, withIssue, withLang); getDmCode(addcode, add_dmIdent, withIssue, withLang); if (verbosity >= VERBOSE && strcmp(refcode, addcode) == 0) { fprintf(stderr, " Updating reference to data module %s...\n", addcode); } return strcmp(refcode, addcode) == 0; } static bool isPmRef(xmlNodePtr ref) { return xmlStrcmp(ref->name, BAD_CAST "pmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "refpm") == 0; } static bool isPmAddress(xmlNodePtr address) { return xmlStrcmp(address->name, BAD_CAST "pmAddress") == 0 || xmlStrcmp(address->name, BAD_CAST "pmaddres") == 0; } static bool samePm(xmlNodePtr ref, xmlNodePtr address) { char refcode[256], addcode[256]; bool withIssue, withLang; xmlNodePtr ref_pmIdent; xmlNodePtr add_pmIdent; if (!isPmRef(ref) || !isPmAddress(address)) return false; ref_pmIdent = firstXPathNode("pmRefIdent|self::refpm", NULL, ref); add_pmIdent = firstXPathNode("pmIdent|self::pmaddres", NULL, address); withIssue = firstXPathNode("issueInfo|issno", NULL, ref_pmIdent); withLang = findChild(ref_pmIdent, "language"); getPmCode(refcode, ref_pmIdent, withIssue, withLang); getPmCode(addcode, add_pmIdent, withIssue, withLang); if (verbosity >= VERBOSE && strcmp(refcode, addcode) == 0) { fprintf(stderr, " Updating reference to pub module %s...\n", addcode); } return strcmp(refcode, addcode) == 0; } static void updateRef(xmlNodePtr ref, xmlNodePtr addresses, const char *fname, xmlNodePtr recode) { xmlNodePtr cur; for (cur = addresses->children; cur; cur = cur->next) { if (sameDm(ref, cur)) { xmlNodePtr dmAddressItems, issueDate, dmTitle, dmRefAddressItems, dmRefIssueDate, dmRefTitle, dmIdent, dmCode, dmRefIdent, dmRefCode, issueInfo, refIssueInfo, language, refLanguage; dmIdent = firstXPathNode("dmIdent|self::dmaddres", NULL, recode); dmCode = firstXPathNode("dmCode|.//avee", NULL, dmIdent); issueInfo = firstXPathNode("issueInfo|issno", NULL, dmIdent); language = findChild(dmIdent, "language"); dmRefIdent = firstXPathNode("dmRefIdent|self::refdm", NULL, ref); dmRefCode = firstXPathNode("dmCode|.//avee", NULL, dmRefIdent); refIssueInfo = firstXPathNode("issueInfo|issno", NULL, dmRefIdent); refLanguage = findChild(dmRefIdent, "language"); if (verbosity >= VERBOSE) { char code[256]; getDmCode(code, dmIdent, refIssueInfo, refLanguage); fprintf(stderr, " Recoding to %s...\n", code); } replaceNode(dmRefCode, dmCode); if (refIssueInfo) replaceNode(refIssueInfo, issueInfo); if (refLanguage) replaceNode(refLanguage, language); dmAddressItems = firstXPathNode("dmAddressItems|self::dmaddres", NULL, recode); issueDate = findChild(dmAddressItems, "issueDate"); dmTitle = firstXPathNode("dmTitle|dmtitle", NULL, dmAddressItems); dmRefAddressItems = firstXPathNode("dmRefAddressItems|self::refdm", NULL, ref); dmRefIssueDate = findChild(dmRefAddressItems, "issueDate"); dmRefTitle = firstXPathNode("dmTitle|dmtitle", NULL, dmRefAddressItems); if (dmRefIssueDate) replaceNode(dmRefIssueDate, issueDate); if (dmRefTitle) replaceNode(dmRefTitle, dmTitle); } else if (samePm(ref, cur)) { xmlNodePtr pmAddressItems, issueDate, pmTitle, pmRefAddressItems, pmRefIssueDate, pmRefTitle, pmIdent, pmCode, pmRefIdent, pmRefCode, issueInfo, refIssueInfo, language, refLanguage; pmIdent = firstXPathNode("pmIdent|self::pmaddres", NULL, recode); pmCode = firstXPathNode("pmCode|pmc", NULL, pmIdent); issueInfo = firstXPathNode("issueInfo|issno", NULL, pmIdent); language = findChild(pmIdent, "language"); pmRefIdent = firstXPathNode("pmRefIdent|self::refpm", NULL, ref); pmRefCode = firstXPathNode("pmCode|pmc", NULL, pmRefIdent); refIssueInfo = firstXPathNode("issueInfo|issno", NULL, pmRefIdent); refLanguage = findChild(pmRefIdent, "language"); if (verbosity >= VERBOSE) { char code[256]; getPmCode(code, pmIdent, refIssueInfo, refLanguage); fprintf(stderr, " Recoding to %s...\n", code); } replaceNode(pmRefCode, pmCode); if (refIssueInfo) replaceNode(refIssueInfo, issueInfo); if (refLanguage) replaceNode(refLanguage, language); pmAddressItems = firstXPathNode("pmAddressItems|self::pmaddres", NULL, recode); issueDate = firstXPathNode("issueDate|issdate", NULL, pmAddressItems); pmTitle = firstXPathNode("pmTitle|pmtitle", NULL, pmAddressItems); pmRefAddressItems = firstXPathNode("pmRefAddressItems|self::refpm", NULL, ref); pmRefIssueDate = firstXPathNode("issueDate|issdate", NULL, pmRefAddressItems); pmRefTitle = firstXPathNode("pmTitle|pmtitle", NULL, pmRefAddressItems); if (pmRefIssueDate) replaceNode(pmRefIssueDate, issueDate); if (pmRefTitle) replaceNode(pmRefTitle, pmTitle); } } } static void updateRefs(xmlNodeSetPtr refs, xmlNodePtr addresses, const char *fname, xmlNodePtr recode) { int i; for (i = 0; i < refs->nodeNr; ++i) { updateRef(refs->nodeTab[i], addresses, fname, recode); } } static void show_help(void) { puts("Usage: " PROG_NAME " [-d ] [-s ] [-t ] [-clqvh?] [...]"); puts(""); puts("Options:"); puts(" -c, --content Only move references in content section of targets."); puts(" -d, --dir Update data modules in directory ."); puts(" -f, --overwrite Overwrite input objects."); puts(" -h, -?, --help Show help/usage message."); puts(" -l, --list Input is a list of data module filenames."); puts(" -q, --quiet Quiet mode."); puts(" -s, --source Source object."); puts(" -t, --target Change refs to into refs to ."); puts(" -v, --verbose Verbose output."); puts(" --version Show version information."); puts(" ... Objects to change refs in."); LIBXML2_PARSE_LONGOPT_HELP } static void show_version(void) { printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION); printf("Using libxml %s\n", xmlParserVersion); } static bool isS1000D(const char *fname) { return (strncmp(fname, "DMC-", 4) == 0 || strncmp(fname, "DME-", 4) == 0 || strncmp(fname, "PMC-", 4) == 0 || strncmp(fname, "PME-", 4) == 0 || strncmp(fname, "COM-", 4) == 0 || strncmp(fname, "IMF-", 4) == 0 || strncmp(fname, "DDN-", 4) == 0 || strncmp(fname, "DML-", 4) == 0 || strncmp(fname, "UPF-", 4) == 0 || strncmp(fname, "UPE-", 4) == 0 || strncmp(fname, "SMC-", 4) == 0 || strncmp(fname, "SME-", 4) == 0) && strncasecmp(fname + strlen(fname) - 4, ".XML", 4) == 0; } static void addAddress(const char *fname, xmlNodePtr addresses) { xmlDocPtr doc; xmlNodePtr address; doc = read_xml_doc(fname); if (!doc) return; if (verbosity >= VERBOSE) fprintf(stderr, "Registering %s...\n", fname); address = firstXPathNode(ADDR_PATH, doc, NULL); if (address) { xmlAddChild(addresses, xmlCopyNode(address, 1)); } xmlFreeDoc(doc); } static void updateRefsFile(const char *fname, xmlNodePtr addresses, bool contentOnly, const char *recode, bool overwrite) { xmlDocPtr doc, recodeDoc; xmlXPathContextPtr ctx; xmlXPathObjectPtr obj; xmlNodePtr recodeIdent; if (!(doc = read_xml_doc(fname))) { return; } if (recode) { recodeDoc = read_xml_doc(recode); recodeIdent = firstXPathNode(ADDR_PATH, recodeDoc, NULL); } else { recodeIdent = NULL; } if (verbosity >= VERBOSE) { fprintf(stderr, "Checking refs in %s...\n", fname); } ctx = xmlXPathNewContext(doc); if (contentOnly) { obj = xmlXPathEvalExpression(REFS_PATH_CONTENT, ctx); } else { obj = xmlXPathEvalExpression(REFS_PATH, ctx); } if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) updateRefs(obj->nodesetval, addresses, fname, recodeIdent); xmlXPathFreeObject(obj); xmlXPathFreeContext(ctx); if (overwrite) { save_xml_doc(doc, fname); } else { save_xml_doc(doc, "-"); } xmlFreeDoc(doc); } static void addDirectory(const char *path, xmlNodePtr addresses) { DIR *dir; struct dirent *cur; dir = opendir(path); if (!dir) { if (verbosity >= NORMAL) { fprintf(stderr, ERR_PREFIX "Directory %s does not exist.\n", path); } exit(EXIT_NO_FILE); } while ((cur = readdir(dir))) { if (isS1000D(cur->d_name)) { char fname[PATH_MAX]; if (snprintf(fname, PATH_MAX, "%s/%s", path, cur->d_name) < 0) { if (verbosity >= NORMAL) { fprintf(stderr, E_ENCODING_ERROR); } exit(EXIT_ENCODING_ERROR); } addAddress(fname, addresses); } } closedir(dir); } static void updateRefsDirectory(const char *path, xmlNodePtr addresses, bool contentOnly, const char *recode, bool overwrite) { DIR *dir; struct dirent *cur; dir = opendir(path); while ((cur = readdir(dir))) { if (isS1000D(cur->d_name)) { char fname[PATH_MAX]; if (snprintf(fname, PATH_MAX, "%s/%s", path, cur->d_name) < 0) { if (verbosity >= NORMAL) { fprintf(stderr, E_ENCODING_ERROR); } exit(EXIT_ENCODING_ERROR); } updateRefsFile(fname, addresses, contentOnly, recode, overwrite); } } closedir(dir); } static xmlNodePtr addAddressList(const char *fname, xmlNodePtr addresses, xmlNodePtr paths) { FILE *f; char path[PATH_MAX]; if (fname) { if (!(f = fopen(fname, "r"))) { if (verbosity >= NORMAL) { fprintf(stderr, E_BAD_LIST, fname); } return paths; } } else { f = stdin; } while (fgets(path, PATH_MAX, f)) { strtok(path, "\t\r\n"); addAddress(path, addresses); xmlNewChild(paths, NULL, BAD_CAST "path", BAD_CAST path); } if (fname) { fclose(f); } return paths; } static void updateRefsList(xmlNodePtr addresses, xmlNodePtr paths, bool contentOnly, const char *recode, bool overwrite) { xmlNodePtr cur; for (cur = paths->children; cur; cur = cur->next) { char *path; path = (char *) xmlNodeGetContent(cur); updateRefsFile(path, addresses, contentOnly, recode, overwrite); xmlFree(path); } } int main(int argc, char **argv) { xmlNodePtr addresses, paths; int i; bool contentOnly = false; char *source = NULL; char *directory = NULL; bool isList = false; char *recode = NULL; bool overwrite = false; const char *sopts = "s:cfqvd:lt:qh?"; struct option lopts[] = { {"version" , no_argument , 0, 0}, {"help" , no_argument , 0, 'h'}, {"source" , required_argument, 0, 's'}, {"content" , no_argument , 0, 'c'}, {"overwrite", no_argument , 0, 'f'}, {"quiet" , no_argument , 0, 'q'}, {"verbose" , no_argument , 0, 'v'}, {"dir" , required_argument, 0, 'd'}, {"list" , no_argument , 0, 'l'}, {"target" , required_argument, 0, 't'}, LIBXML2_PARSE_LONGOPT_DEFS {0, 0, 0, 0} }; int loptind = 0; 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 's': if (!source) source = strdup(optarg); break; case 'c': contentOnly = true; break; case 'f': overwrite = true; break; case 'q': --verbosity; break; case 'v': ++verbosity; break; case 'd': if (!directory) directory = strdup(optarg); break; case 'l': isList = true; break; case 't': if (!recode) recode = strdup(optarg); break; case 'h': case '?': show_help(); return 0; } } if (recode && !source) { if (verbosity >= NORMAL) { fprintf(stderr, ERR_PREFIX "Source object must be specified with -s to be moved with -m.\n"); } exit(EXIT_NO_FILE); } addresses = xmlNewNode(NULL, BAD_CAST "addresses"); paths = xmlNewNode(NULL, BAD_CAST "paths"); if (directory) { addDirectory(directory, addresses); } if (source) { addAddress(source, addresses); } else if (optind < argc) { for (i = optind; i < argc; ++i) { if (isList) { addAddressList(argv[i], addresses, paths); } else { addAddress(argv[i], addresses); } } } else if (isList) { addAddressList(NULL, addresses, paths); } if (directory) { updateRefsDirectory(directory, addresses, contentOnly, recode, overwrite); } else if (optind < argc) { for (i = optind; i < argc; ++i) { if (isList) { updateRefsList(addresses, paths, contentOnly, recode, overwrite); } else { updateRefsFile(argv[i], addresses, contentOnly, recode, overwrite); } } } else if (isList) { updateRefsList(addresses, paths, contentOnly, recode, overwrite); } free(source); free(directory); free(recode); xmlFreeNode(addresses); xmlFreeNode(paths); xmlCleanupParser(); return 0; }