..
/
download
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#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 <out>] [<dms>]");
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 <out> Output to <out> instead of stdout.");
puts(" -q, --quiet Quiet mode.");
puts(" -v, --verbose Verbose output.");
puts(" --version Show version information.");
puts(" <dms> 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;
}
gopher://khzae.net/0/s1kd/s1kd-tools/src/tools/s1kd-syncrefs/s1kd-syncrefs.c