..
/
download
#include "s1kd_tools.h"
#define PROGRESS_BAR_WIDTH 60
/* Default global XML parsing options.
*
* XML_PARSE_NOERROR / XML_PARSE_NOWARNING
* Suppress the error logging built-in to the parser. The tools will handle
* reporting errors.
*
* XML_PARSE_NONET
* Disable network access as a safety precaution.
*/
int DEFAULT_PARSE_OPTS = XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET;
/* Return the full path name from a relative path. */
char *real_path(const char *path, char *real)
{
#ifdef _WIN32
if (!GetFullPathName(path, PATH_MAX, real, NULL)) {
#else
if (!realpath(path, real)) {
#endif
strcpy(real, path);
}
return real;
}
/* Search up the directory tree to find a configuration file. */
bool find_config(char *dst, const char *name)
{
char cwd[PATH_MAX], prev[PATH_MAX];
bool found = true;
real_path(".", cwd);
strcpy(prev, cwd);
while (access(name, F_OK) == -1) {
char cur[PATH_MAX];
if (chdir("..") || strcmp(real_path(".", cur), prev) == 0) {
found = false;
break;
}
strcpy(prev, cur);
}
if (found) {
real_path(name, dst);
} else {
strcpy(dst, name);
}
return chdir(cwd) == 0 && found;
}
/* Generate an XPath expression for a node. */
xmlChar *xpath_of(xmlNodePtr node)
{
xmlNodePtr path, cur;
xmlChar *dst = NULL;
path = xmlNewNode(NULL, BAD_CAST "path");
/* Build XPath expression node by traversing up the tree. */
while (node && node->type != XML_DOCUMENT_NODE) {
xmlNodePtr e;
const xmlChar *name;
e = xmlNewChild(path, NULL, BAD_CAST "node", NULL);
switch (node->type) {
case XML_COMMENT_NODE:
name = BAD_CAST "comment()";
break;
case XML_PI_NODE:
name = BAD_CAST "processing-instruction()";
break;
case XML_TEXT_NODE:
name = BAD_CAST "text()";
break;
default:
name = node->name;
break;
}
if (node->ns != NULL) {
xmlSetProp(e, BAD_CAST "ns", node->ns->prefix);
}
xmlSetProp(e, BAD_CAST "name", name);
/* Locate the node's position within its parent. */
if (node->type != XML_ATTRIBUTE_NODE) {
int n = 1;
xmlChar pos[16];
for (cur = node->parent->children; cur; cur = cur->next) {
if (cur == node) {
break;
} else if (cur->type == node->type && (node->type != XML_ELEMENT_NODE || xmlStrcmp(cur->name, node->name) == 0)) {
++n;
}
}
xmlStrPrintf(pos, 16, "%d", n);
xmlSetProp(e, BAD_CAST "pos", pos);
}
node = node->parent;
}
/* Convert XPath expression node to string. */
for (cur = path->last; cur; cur = cur->prev) {
xmlChar *ns, *name, *pos;
ns = xmlGetProp(cur, BAD_CAST "ns");
name = xmlGetProp(cur, BAD_CAST "name");
pos = xmlGetProp(cur, BAD_CAST "pos");
dst = xmlStrcat(dst, BAD_CAST "/");
if (!pos) {
dst = xmlStrcat(dst, BAD_CAST "@");
}
if (ns) {
dst = xmlStrcat(dst, ns);
dst = xmlStrcat(dst, BAD_CAST ":");
}
dst = xmlStrcat(dst, name);
if (pos) {
dst = xmlStrcat(dst, BAD_CAST "[");
dst = xmlStrcat(dst, pos);
dst = xmlStrcat(dst, BAD_CAST "]");
}
xmlFree(ns);
xmlFree(name);
xmlFree(pos);
}
xmlFreeNode(path);
return dst;
}
/* Make a copy of a file. */
int copy(const char *from, const char *to)
{
FILE *f1, *f2;
char buf[4096];
size_t n;
struct stat sf, st;
if (stat(from, &sf) == -1) {
return 1;
}
if (stat(to, &st) == 0 && sf.st_dev == st.st_dev && sf.st_ino == st.st_ino) {
return 1;
}
f1 = fopen(from, "rb");
f2 = fopen(to, "wb");
while ((n = fread(buf, 1, 4096, f1)) > 0) {
fwrite(buf, 1, n, f2);
}
fclose(f1);
fclose(f2);
return 0;
}
/* Determine if path is a directory. */
bool isdir(const char *path, bool recursive)
{
struct stat st;
char s[PATH_MAX], *b;
strcpy(s, path);
b = basename(s);
if (recursive && (strcmp(b, ".") == 0 || strcmp(b, "..") == 0)) {
return false;
}
if (stat(path, &st) != 0) {
return false;
}
return S_ISDIR(st.st_mode);
}
/* Not exposed by the libxml API < 2.12.0 */
#if LIBXML_VERSION < 21200
void xmlFreeEntity(xmlEntityPtr entity)
{
xmlDictPtr dict = NULL;
if (entity == NULL)
return;
if (entity->doc != NULL)
dict = entity->doc->dict;
if ((entity->children) && (entity->owner == 1) &&
(entity == (xmlEntityPtr) entity->children->parent))
xmlFreeNodeList(entity->children);
if (dict != NULL) {
if ((entity->name != NULL) && (!xmlDictOwns(dict, entity->name)))
xmlFree((char *) entity->name);
if ((entity->ExternalID != NULL) &&
(!xmlDictOwns(dict, entity->ExternalID)))
xmlFree((char *) entity->ExternalID);
if ((entity->SystemID != NULL) &&
(!xmlDictOwns(dict, entity->SystemID)))
xmlFree((char *) entity->SystemID);
if ((entity->URI != NULL) && (!xmlDictOwns(dict, entity->URI)))
xmlFree((char *) entity->URI);
if ((entity->content != NULL)
&& (!xmlDictOwns(dict, entity->content)))
xmlFree((char *) entity->content);
if ((entity->orig != NULL) && (!xmlDictOwns(dict, entity->orig)))
xmlFree((char *) entity->orig);
} else {
if (entity->name != NULL)
xmlFree((char *) entity->name);
if (entity->ExternalID != NULL)
xmlFree((char *) entity->ExternalID);
if (entity->SystemID != NULL)
xmlFree((char *) entity->SystemID);
if (entity->URI != NULL)
xmlFree((char *) entity->URI);
if (entity->content != NULL)
xmlFree((char *) entity->content);
if (entity->orig != NULL)
xmlFree((char *) entity->orig);
}
xmlFree(entity);
}
#endif
/* Compare the codes of two CSDB objects. */
static int codecmp(const char *p1, const char *p2)
{
char s1[PATH_MAX], s2[PATH_MAX], *b1, *b2;
strcpy(s1, p1);
strcpy(s2, p2);
b1 = basename(s1);
b2 = basename(s2);
return strcasecmp(b1, b2);
}
/* Match a string with a pattern case-insensitively, using ? as a wildcard. */
bool strmatch(const char *p, const char *s)
{
const unsigned char *cp = (const unsigned char *) p;
const unsigned char *cs = (const unsigned char *) s;
while (*cp) {
if (tolower(*cp) != tolower(*cs) && *cp != '?') {
return false;
}
++cp;
++cs;
}
return true;
}
/* Match a string with a pattern case-insensitvely, using ? as a wildcard, up to a certain length. */
bool strnmatch(const char *p, const char *s, int n)
{
const unsigned char *cp = (const unsigned char *) p;
const unsigned char *cs = (const unsigned char *) s;
int i = 0;
while (*cp && i < n) {
if (tolower(*cp) != tolower(*cs) && *cp != '?') {
return false;
}
++cp;
++cs;
++i;
}
return true;
}
/* Find a CSDB object in a directory hierarchy based on its code. */
bool find_csdb_object(char *dst, const char *path, const char *code, bool (*is)(const char *), bool recursive)
{
DIR *dir;
struct dirent *cur;
bool found = false;
int len = strlen(path);
char fpath[PATH_MAX], cpath[PATH_MAX];
if (!isdir(path, false)) {
return false;
}
if (!(dir = opendir(path))) {
return false;
}
if (strcmp(path, ".") == 0) {
strcpy(fpath, "");
} else if (path[len - 1] != '/') {
strcpy(fpath, path);
strcat(fpath, "/");
} else {
strcpy(fpath, path);
}
while ((cur = readdir(dir))) {
strcpy(cpath, fpath);
strcat(cpath, cur->d_name);
if (recursive && isdir(cpath, true)) {
char tmp[PATH_MAX];
if (find_csdb_object(tmp, cpath, code, is, true) && (!found || codecmp(tmp, dst) > 0)) {
strcpy(dst, tmp);
found = true;
}
} else if ((!is || is(cur->d_name)) && strmatch(code, cur->d_name)) {
if (!found || codecmp(cpath, dst) > 0) {
strcpy(dst, cpath);
found = true;
}
}
}
closedir(dir);
return found;
}
/* Find a CSDB object in a list of paths. */
bool find_csdb_object_in_list(char *dst, char (*objects)[PATH_MAX], int n, const char *code)
{
int i;
bool found = false;
for (i = 0; i < n; ++i) {
char *name, *base;
name = strdup(objects[i]);
base = basename(name);
found = strmatch(code, base);
free(name);
if (found) {
strcpy(dst, objects[i]);
break;
}
}
return found;
}
/* Convert string to double. Returns true if the string contained only a
* correct double value or false if it contained extra content. */
static bool strtodbl(double *d, const char *s)
{
char *e;
*d = strtod(s, &e);
return e != s && *e == 0;
}
/* Tests whether a value is in an S1000D range (a~c is equivalent to a|b|c) */
bool is_in_range(const char *value, const char *range)
{
char *ran, *first, *last;
bool ret;
double f, l, v;
if (!strchr(range, '~')) {
return strcmp(value, range) == 0;
}
ran = malloc(strlen(range) + 1);
strcpy(ran, range);
first = strtok(ran, "~");
last = strtok(NULL, "~");
/* Attempt to compare the values numerically. If any of the values are
* non-numeric, fall back to lexicographic comparison.
*
* For example, if the range is 20~100, 30 falls in this range
* numerically but not lexicographically.
*/
if (strtodbl(&f, first) && strtodbl(&l, last) && strtodbl(&v, value)) {
ret = v - f >= 0 && v - l <= 0;
} else {
ret = strcmp(value, first) >= 0 && strcmp(value, last) <= 0;
}
free(ran);
return ret;
}
/* Tests whether a value is in an S1000D set (a|b|c) */
bool is_in_set(const char *value, const char *set)
{
char *s, *val = NULL;
bool ret = false;
if (!strchr(set, '|')) {
return is_in_range(value, set);
}
s = malloc(strlen(set) + 1);
strcpy(s, set);
while ((val = strtok(val ? NULL : s, "|"))) {
if (is_in_range(value, val)) {
ret = true;
break;
}
}
free(s);
return ret;
}
/* Add a NOTATION to the DTD. */
void add_notation(xmlDocPtr doc, const xmlChar *name, const xmlChar *pubId, const xmlChar *sysId)
{
xmlValidCtxtPtr valid;
if (!doc->intSubset) {
xmlCreateIntSubset(doc, xmlDocGetRootElement(doc)->name, NULL, NULL);
}
if (!xmlHashLookup(doc->intSubset->notations, BAD_CAST name)) {
valid = xmlNewValidCtxt();
xmlAddNotationDecl(valid, doc->intSubset, name, pubId, sysId);
xmlFreeValidCtxt(valid);
}
}
/* Add an ICN entity from a file path. */
xmlEntityPtr add_icn(xmlDocPtr doc, const char *path, bool fullpath)
{
char *full, *base, *name;
char *infoEntityIdent;
char *notation;
xmlEntityPtr e;
full = strdup(path);
base = basename(full);
name = strdup(base);
infoEntityIdent = strtok(name, ".");
notation = strtok(NULL, "");
add_notation(doc, BAD_CAST notation, NULL, BAD_CAST notation);
e = xmlAddDocEntity(doc, BAD_CAST infoEntityIdent,
XML_EXTERNAL_GENERAL_UNPARSED_ENTITY, NULL,
BAD_CAST (fullpath ? path : base), BAD_CAST notation);
free(name);
free(full);
return e;
}
/* Make a file read-only. */
void mkreadonly(const char *path)
{
#ifdef _WIN32
SetFileAttributesA(path, FILE_ATTRIBUTE_READONLY);
#else
struct stat st;
stat(path, &st);
chmod(path, (st.st_mode & 07777) & ~(S_IWUSR | S_IWGRP | S_IWOTH));
#endif
}
/* Insert a child node instead of appending one. */
xmlNodePtr add_first_child(xmlNodePtr parent, xmlNodePtr child)
{
if (parent->children) {
return xmlAddPrevSibling(parent->children, child);
} else {
return xmlAddChild(parent, child);
}
}
/* Convert string to lowercase. */
void lowercase(char *s)
{
int i;
for (i = 0; s[i]; ++i) {
s[i] = tolower(s[i]);
}
}
/* Convert string to uppercase. */
void uppercase(char *s)
{
int i;
for (i = 0; s[i]; ++i) {
s[i] = toupper(s[i]);
}
}
/* Return whether a bitset contains an option. */
bool optset(int opts, int opt)
{
return ((opts & opt) == opt);
}
/* Read an XML document from a file. */
xmlDocPtr read_xml_doc(const char *path)
{
xmlDocPtr doc;
doc = xmlReadFile(path, NULL, DEFAULT_PARSE_OPTS);
if (optset(DEFAULT_PARSE_OPTS, XML_PARSE_XINCLUDE)) {
xmlXIncludeProcessFlags(doc, DEFAULT_PARSE_OPTS);
}
return doc;
}
/* Read an XML document from memory. */
xmlDocPtr read_xml_mem(const char *buffer, int size)
{
xmlDocPtr doc;
doc = xmlReadMemory(buffer, size, NULL, NULL, DEFAULT_PARSE_OPTS);
if (optset(DEFAULT_PARSE_OPTS, XML_PARSE_XINCLUDE)) {
xmlXIncludeProcessFlags(doc, DEFAULT_PARSE_OPTS);
}
return doc;
}
/* Save an XML document to a file. */
int save_xml_doc(xmlDocPtr doc, const char *path)
{
return xmlSaveFile(path, doc);
}
/* Return the first node matching an XPath expression. */
xmlNodePtr xpath_first_node(xmlDocPtr doc, xmlNodePtr node, const xmlChar *path)
{
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
xmlNodePtr first;
if (doc) {
ctx = xmlXPathNewContext(doc);
} else {
ctx = xmlXPathNewContext(node->doc);
}
ctx->node = node;
obj = xmlXPathEvalExpression(BAD_CAST path, ctx);
if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
first = NULL;
} else {
first = obj->nodesetval->nodeTab[0];
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
return first;
}
/* Return the value of the first node matching an XPath expression. */
xmlChar *xpath_first_value(xmlDocPtr doc, xmlNodePtr node, const xmlChar *path)
{
return xmlNodeGetContent(xpath_first_node(doc, node, path));
}
/* Add a dependency test to an assertion if it contains any of the dependent
* values.
*
* If the assertion uses a set (|), the values will be split up in order to
* only add the dependency to the appropriate values.
*/
static void add_cct_depend_to_assert(xmlNodePtr *assert, const xmlChar *id, const xmlChar *forval, xmlNodePtr applic)
{
xmlChar *vals, *v = NULL;
bool match = false;
xmlNodePtr eval;
char *end = NULL;
vals = xmlGetProp(*assert, BAD_CAST "applicPropertyValues");
eval = xmlNewNode(NULL, BAD_CAST "evaluate");
xmlSetProp(eval, BAD_CAST "andOr", BAD_CAST "or");
/* Split any sets (|) */
while ((v = BAD_CAST strtok_r(v ? NULL : (char *) vals, "|", &end))) {
xmlNodePtr a;
a = xmlNewNode(NULL, BAD_CAST "assert");
xmlSetProp(a, BAD_CAST "applicPropertyIdent", id);
xmlSetProp(a, BAD_CAST "applicPropertyType", BAD_CAST "condition");
xmlSetProp(a, BAD_CAST "applicPropertyValues", v);
/* If the current value has a dependency, add the depedency
* test to it with an AND evaluate.
*/
if (xmlStrcmp(v, forval) == 0) {
xmlNodePtr cur, e;
match = true;
e = xmlNewChild(eval, NULL, BAD_CAST "evaluate", NULL);
xmlSetProp(e, BAD_CAST "andOr", BAD_CAST "and");
for (cur = applic->children; cur; cur = cur->next) {
if (xmlStrcmp(cur->name, BAD_CAST "assert") == 0 || xmlStrcmp(cur->name, BAD_CAST "evaluate") == 0) {
xmlAddChild(e, xmlCopyNode(cur, 1));
}
}
xmlAddChild(e, a);
} else {
xmlAddChild(eval, a);
}
}
xmlFree(vals);
/* If any of the values in the set had a dependency, replace the assert
* with the new evaluation.
*/
if (match) {
xmlChar *op;
op = xmlGetProp((*assert)->parent, BAD_CAST "andOr");
/* If the dependency test is being added to an OR evaluate,
* simplify the output by combining the new OR and the existing
* OR.
*/
if (!eval->children->next || (op && xmlStrcmp(op, BAD_CAST "or") == 0)) {
xmlNodePtr cur, last;
for (cur = eval->children, last = *assert; cur; cur = cur->next) {
xmlChar *o;
o = xmlGetProp(cur, BAD_CAST "andOr");
/* Combine evaluates with the same operation. */
if (o && xmlStrcmp(o, op) == 0) {
xmlNodePtr c;
for (c = cur->children; c; c = c->next) {
last = xmlAddNextSibling(last, xmlCopyNode(c, 1));
}
} else {
last = xmlAddNextSibling(last, xmlCopyNode(cur, 1));
}
xmlFree(o);
}
xmlFreeNode(eval);
/* Otherwise, just add the new OR. */
} else {
xmlAddNextSibling(*assert, eval);
}
xmlFree(op);
xmlUnlinkNode(*assert);
xmlFreeNode(*assert);
*assert = NULL;
} else {
xmlFreeNode(eval);
}
}
/* Add a dependency from the CCT. */
static void add_cct_depend(xmlDocPtr doc, xmlNodePtr dep)
{
xmlChar *id, *test, *vals, *xpath;
int n;
xmlNodePtr applic;
id = xmlGetProp(dep->parent, BAD_CAST "id");
test = xmlGetProp(dep, BAD_CAST "dependencyTest");
vals = xmlGetProp(dep, BAD_CAST "forCondValues");
/* Find the annotation for the dependency test. */
n = xmlStrlen(test) + 17;
xpath = malloc(n * sizeof(xmlChar));
xmlStrPrintf(xpath, n, "//applic[@id='%s']", test);
applic = xpath_first_node(dep->doc, NULL, xpath);
xmlFree(xpath);
if (applic) {
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
xmlChar *v = NULL;
char *end = NULL;
ctx = xmlXPathNewContext(doc);
/* Add dependency tests to assertions that use the dependant values. */
while ((v = BAD_CAST strtok_r(v ? NULL : (char *) vals, "|", &end))) {
n = xmlStrlen(id) + 70;
xpath = malloc(n * sizeof(xmlChar));
xmlStrPrintf(xpath, n, "//assert[@applicPropertyIdent='%s' and @applicPropertyType='condition']", id);
obj = xmlXPathEvalExpression(xpath, ctx);
xmlFree(xpath);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
add_cct_depend_to_assert(&obj->nodesetval->nodeTab[i], id, v, applic);
}
}
xmlXPathFreeObject(obj);
}
xmlXPathFreeContext(ctx);
/* Handle subdependencies, that is, dependant values which themselves
* have dependencies.
*/
ctx = xmlXPathNewContext(applic->doc);
ctx->node = applic;
obj = xmlXPathEvalExpression(BAD_CAST ".//assert[@applicPropertyIdent and @applicPropertyType='condition']", ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
xmlChar *ident;
ident = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "applicPropertyIdent");
add_cct_depends(doc, applic->doc, ident);
xmlFree(ident);
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
}
xmlFree(id);
xmlFree(test);
xmlFree(vals);
}
/* Add CCT dependencies to the source object.
*
* If id is NULL, all conditions will be added.
*
* If id is not NULL, then only the dependencies for condition with that id
* will be added. This is useful when handling sub-depedencies, to only handle
* the conditions which pertain to a particular dependency test.
*/
void add_cct_depends(xmlDocPtr doc, xmlDocPtr cct, xmlChar *id)
{
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
ctx = xmlXPathNewContext(cct);
if (id) {
int n;
xmlChar *xpath;
n = xmlStrlen(id) + 26;
xpath = malloc(n * sizeof(xmlChar));
xmlStrPrintf(xpath, n, "//cond[@id='%s']/dependency", id);
obj = xmlXPathEvalExpression(xpath, ctx);
xmlFree(xpath);
} else {
obj = xmlXPathEvalExpression(BAD_CAST "//cond/dependency", ctx);
}
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
add_cct_depend(doc, obj->nodesetval->nodeTab[i]);
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
}
/* Test whether an object value matches a regex pattern. */
bool match_pattern(const xmlChar *value, const xmlChar *pattern)
{
xmlRegexpPtr regex;
bool match;
regex = xmlRegexpCompile(BAD_CAST pattern);
match = xmlRegexpExec(regex, BAD_CAST value);
xmlRegFreeRegexp(regex);
return match;
}
/* Display a progress bar. */
void print_progress_bar(float cur, float total)
{
float p;
int i, b;
p = cur / total;
b = PROGRESS_BAR_WIDTH * p;
fprintf(stderr, "\r[");
for (i = 0; i < PROGRESS_BAR_WIDTH; ++i) {
if (i < b) {
fputc('=', stderr);
} else {
fputc(' ', stderr);
}
}
fprintf(stderr, "] %d%% (%d/%d) ", (int)(p * 100.0), (int) cur, (int) total);
if (cur == total) {
fputc('\n', stderr);
}
fflush(stderr);
}
/* Print progress information in the zenity --progress format. */
void print_zenity_progress(const char *message, float cur, float total)
{
int p;
p = (int)((cur / total) * 100.0);
printf("# %s %d%% (%d/%d)\n%d\n", message, p, (int) cur, (int) total, p);
}
/* Determine if the file is an XML file. */
static bool is_xml(const char *name)
{
return strncasecmp(name + strlen(name) - 4, ".XML", 4) == 0;
}
/* Determine if the file is a data module. */
bool is_dm(const char *name)
{
return strncmp(name, "DMC-", 4) == 0 && is_xml(name);
}
/* Determine if the file is a publication module. */
bool is_pm(const char *name)
{
return strncmp(name, "PMC-", 4) == 0 && is_xml(name);
}
/* Determine if the file is a comment. */
bool is_com(const char *name)
{
return strncmp(name, "COM-", 4) == 0 && is_xml(name);
}
/* Determine if the file is an ICN metadata file. */
bool is_imf(const char *name)
{
return strncmp(name, "IMF-", 4) == 0 && is_xml(name);
}
/* Determine if the file is a data dispatch note. */
bool is_ddn(const char *name)
{
return strncmp(name, "DDN-", 4) == 0 && is_xml(name);
}
/* Determine if the file is a data management list. */
bool is_dml(const char *name)
{
return strncmp(name, "DML-", 4) == 0 && is_xml(name);
}
/* Determine if the file is an ICN. */
bool is_icn(const char *name)
{
return strncmp(name, "ICN-", 4) == 0;
}
/* Determine if the file is a SCORM content package. */
bool is_smc(const char *name)
{
return (strncmp(name, "SMC-", 4) == 0 || strncmp(name, "SME-", 4) == 0) && is_xml(name);
}
/* Determine if the file is a data update file. */
bool is_upf(const char *name)
{
return (strncmp(name, "UPF-", 4) == 0 || strncmp(name, "UPE-", 4) == 0) && is_xml(name);
}
/* Interpolate a command string with a file name and execute it. */
int execfile(const char *execstr, const char *path)
{
int i, j, n, e, c = 0;
char *fmtstr, *cmd;
n = strlen(execstr);
fmtstr = malloc(n * 2);
for (i = 0, j = 0; i < n; ++i) {
switch (execstr[i]) {
case '{':
if (execstr[i+1] && execstr[i+1] == '}') {
fmtstr[j++] = '%';
fmtstr[j++] = '1';
fmtstr[j++] = '$';
fmtstr[j++] = 's';
++c;
++i;
}
break;
case '%':
case '\\':
fmtstr[j++] = execstr[i];
fmtstr[j++] = execstr[i];
break;
default:
fmtstr[j++] = execstr[i];
}
}
fmtstr[j] = 0;
n = strlen(fmtstr) + strlen(path) * c;
cmd = malloc(n);
snprintf(cmd, n, fmtstr, path);
free(fmtstr);
e = system(cmd);
free(cmd);
if (WIFSIGNALED(e)) {
raise(WTERMSIG(e));
}
return WEXITSTATUS(e);
}
/* Copy only the latest issues of CSDB objects. */
int extract_latest_csdb_objects(char (*latest)[PATH_MAX], char (*files)[PATH_MAX], int nfiles)
{
int i, nlatest = 0;
for (i = 0; i < nfiles; ++i) {
char *name1, *name2, *base1, *base2;
name1 = strdup(files[i]);
base1 = basename(name1);
if (i > 0) {
name2 = strdup(files[i - 1]);
base2 = basename(name2);
} else {
name2 = NULL;
}
if (i == 0 || strncmp(base1, base2, strchr(base1, '_') - base1) != 0) {
strcpy(latest[nlatest++], files[i]);
} else {
strcpy(latest[nlatest - 1], files[i]);
}
free(name1);
free(name2);
}
return nlatest;
}
/* Compare the base names of two files. */
int compare_basename(const void *a, const void *b)
{
char *sa, *sb, *ba, *bb;
int d;
sa = strdup((const char *) a);
sb = strdup((const char *) b);
ba = basename(sa);
bb = basename(sb);
d = strcasecmp(ba, bb);
free(sa);
free(sb);
return d;
}
/* Determine if a CSDB object is a CIR. */
bool is_cir(const char *path, const bool ignore_del)
{
xmlDocPtr doc;
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
bool is;
if (!(doc = read_xml_doc(path))) {
return false;
}
ctx = xmlXPathNewContext(doc);
/* Check that this is a CIR/TIR DM. */
obj = xmlXPathEvalExpression(BAD_CAST "//commonRepository|//techRepository|//techrep", ctx);
is = !xmlXPathNodeSetIsEmpty(obj->nodesetval);
xmlXPathFreeObject(obj);
/* Check that the DM is not "deleted" if ignore_del = true. */
if (is && ignore_del) {
obj = xmlXPathEvalExpression(BAD_CAST "//dmodule[identAndStatusSection/dmStatus/@issueType='deleted' or status/issno/@type='deleted']", ctx);
is = xmlXPathNodeSetIsEmpty(obj->nodesetval);
xmlXPathFreeObject(obj);
}
xmlXPathFreeContext(ctx);
xmlFreeDoc(doc);
return is;
}
/* Recursively remove nodes marked as "delete". */
static void rem_delete_nodes(xmlNodePtr node)
{
xmlChar *change;
xmlNodePtr cur;
if (!node) {
return;
}
change = xmlGetProp(node, BAD_CAST "change");
if (!change) {
change = xmlGetProp(node, BAD_CAST "changeType");
}
if (xmlStrcmp(change, BAD_CAST "delete") == 0) {
xmlUnlinkNode(node);
xmlFreeNode(node);
return;
}
cur = node->children;
while (cur) {
xmlNodePtr next = cur->next;
rem_delete_nodes(cur);
cur = next;
}
}
/* Remove elements marked as "delete". */
void rem_delete_elems(xmlDocPtr doc)
{
rem_delete_nodes(xmlDocGetRootElement(doc));
}
/* Evaluate an applic statement, returning whether it is valid or invalid given
* the user-supplied applicability settings.
*
* If assume is true, undefined attributes and conditions are ignored. This is
* primarily useful for determining which elements are not applicable in content
* and may be removed.
*
* If assume is false, undefined attributes or conditions will cause an applic
* statement to evaluate as invalid. This is primarily useful for determining
* which applic statements and references are unambiguously true (they do not
* rely on any undefined attributes or conditions) and therefore may be removed.
*
* An undefined attribute/condition is a product attribute (ACT) or
* condition (CCT) for which a value is asserted in the applic statement but
* for which no value was supplied by the user.
*/
bool eval_applic(xmlNodePtr defs, xmlNodePtr node, bool assume);
/* Evaluate multiple values for a property */
static bool eval_multi(xmlNodePtr multi, const char *ident, const char *type, const char *value)
{
xmlNodePtr cur;
bool result = false;
for (cur = multi->children; cur; cur = cur->next) {
xmlChar *cur_value;
bool in_set;
cur_value = xmlNodeGetContent(cur);
in_set = is_in_set((char *) cur_value, value);
xmlFree(cur_value);
if (in_set) {
result = true;
break;
}
}
return result;
}
/* Tests whether ident:type=value was defined by the user */
bool is_applic(xmlNodePtr defs, const char *ident, const char *type, const char *value, bool assume)
{
xmlNodePtr cur;
bool result = assume;
if (!(ident && type && value)) {
return assume;
}
for (cur = defs->children; cur; cur = cur->next) {
char *cur_ident = (char *) xmlGetProp(cur, BAD_CAST "applicPropertyIdent");
char *cur_type = (char *) xmlGetProp(cur, BAD_CAST "applicPropertyType");
char *cur_value = (char *) xmlGetProp(cur, BAD_CAST "applicPropertyValues");
bool match = strcmp(cur_ident, ident) == 0 && strcmp(cur_type, type) == 0;
if (match) {
if (cur_value) {
result = is_in_set(cur_value, value);
} else if (assume) {
result = eval_multi(cur, ident, type, value);
}
}
xmlFree(cur_ident);
xmlFree(cur_type);
xmlFree(cur_value);
if (match) {
break;
}
}
return result;
}
/* Tests whether an <assert> element is applicable */
bool eval_assert(xmlNodePtr defs, xmlNodePtr assert, bool assume)
{
xmlNodePtr ident_attr, type_attr, values_attr;
char *ident, *type, *values;
bool ret;
ident_attr = xpath_first_node(NULL, assert, BAD_CAST "@applicPropertyIdent|@actidref");
type_attr = xpath_first_node(NULL, assert, BAD_CAST "@applicPropertyType|@actreftype");
values_attr = xpath_first_node(NULL, assert, BAD_CAST "@applicPropertyValues|@actvalues");
ident = (char *) xmlNodeGetContent(ident_attr);
type = (char *) xmlNodeGetContent(type_attr);
values = (char *) xmlNodeGetContent(values_attr);
ret = is_applic(defs, ident, type, values, assume);
xmlFree(ident);
xmlFree(type);
xmlFree(values);
return ret;
}
enum operator { AND, OR };
/* Test whether an <evaluate> element is applicable. */
bool eval_evaluate(xmlNodePtr defs, xmlNodePtr evaluate, bool assume)
{
xmlChar *andOr;
enum operator op;
bool ret = assume;
xmlNodePtr cur;
andOr = xpath_first_value(NULL, evaluate, BAD_CAST "@andOr|@operator");
if (!andOr) {
return false;
}
if (xmlStrcmp(andOr, BAD_CAST "and") == 0) {
op = AND;
} else {
op = OR;
}
xmlFree(andOr);
for (cur = evaluate->children; cur; cur = cur->next) {
if (xmlStrcmp(cur->name, BAD_CAST "assert") == 0 || xmlStrcmp(cur->name, BAD_CAST "evaluate") == 0) {
ret = eval_applic(defs, cur, assume);
if ((op == AND && !ret) || (op == OR && ret)) {
break;
}
}
}
return ret;
}
/* Generic test for either <assert> or <evaluate> */
bool eval_applic(xmlNodePtr defs, xmlNodePtr node, bool assume)
{
if (xmlStrcmp(node->name, BAD_CAST "assert") == 0) {
return eval_assert(defs, node, assume);
} else if (xmlStrcmp(node->name, BAD_CAST "evaluate") == 0) {
return eval_evaluate(defs, node, assume);
}
return false;
}
gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/common/s1kd_tools.c