..
/
download
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <dirent.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxslt/transform.h>
#include "s1kd_tools.h"
#include "xsl.h"
/* Program information. */
#define PROG_NAME "s1kd-repcheck"
#define VERSION "1.10.0"
/* Message prefixes. */
#define ERR_PREFIX PROG_NAME ": ERROR: "
#define WRN_PREFIX PROG_NAME ": WARNING: "
#define INF_PREFIX PROG_NAME ": INFO: "
#define SUC_PREFIX PROG_NAME ": SUCCESS: "
#define FLD_PREFIX PROG_NAME ": FAILED: "
/* Error messages. */
#define E_MAX_OBJECTS ERR_PREFIX "Out of memory\n"
#define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n"
#define E_NOT_FOUND ERR_PREFIX "%s (%ld): %s not found.\n"
#define E_UNHANDLED_REF ERR_PREFIX "Unhandled CIR ref type: %s\n"
/* Warning messages. */
#define W_MISSING_REF_DM WRN_PREFIX "Could not read referenced object: %s\n"
/* Informational messages. */
#define I_CHECK INF_PREFIX "Checking CIR references in %s...\n"
#define I_SEARCH_PART INF_PREFIX "Searching for %s in CIR %s...\n"
#define I_FOUND INF_PREFIX "Found %s in CIR %s\n"
#define I_NOT_FOUND INF_PREFIX "Not found in CIR %s\n"
#define I_FIND_CIR INF_PREFIX "Searching for CIRs in \"%s\"...\n"
#define I_FIND_CIR_FOUND INF_PREFIX "Found CIR %s...\n"
#define I_FIND_CIR_ADD INF_PREFIX "Added CIR %s\n"
/* Success messages. */
#define S_VALID SUC_PREFIX "All CIR references were resolved in %s.\n"
/* Failure messages. */
#define F_INVALID FLD_PREFIX "Could not resolve some CIR references in %s.\n"
/* Exit status codes. */
#define EXIT_MAX_OBJECTS 2
/* Progress formats. */
#define PROGRESS_OFF 0
#define PROGRESS_CLI 1
#define PROGRESS_ZENITY 2
/* Namespace for special attributes used to extract CIR references. */
#define S1KD_REPCHECK_NS BAD_CAST "urn:s1kd-tools:s1kd-repcheck"
/* Verbosity of messages. */
enum verbosity { QUIET, NORMAL, VERBOSE, DEBUG };
/* List of CSDB objects. */
struct objects {
char (*paths)[PATH_MAX];
unsigned count;
unsigned max;
};
enum show_filenames { SHOW_NONE, SHOW_INVALID, SHOW_VALID };
/* Program options. */
struct opts {
enum verbosity verbosity;
enum show_filenames show_filenames;
char *search_dir;
bool recursive;
bool no_issue;
bool search_all_objs;
bool output_valid;
bool list_refs;
bool rem_delete;
xmlDocPtr cir_refs_xsl;
char *type;
struct objects objects;
struct objects cirs;
xmlNodePtr report;
};
/* Match a CIR ref to a CIR spec in a given CIR data module. */
static xmlNodePtr find_ref_in_cir(xmlNodePtr ref, const xmlChar *ident, const xmlChar *xpath, const char *cirpath, struct opts *opts)
{
xmlDocPtr doc;
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
xmlNodePtr node = NULL;
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_SEARCH_PART, ident, cirpath);
}
if (!(doc = read_xml_doc(cirpath))) {
return NULL;
}
if (opts->rem_delete) {
rem_delete_elems(doc);
}
ctx = xmlXPathNewContext(doc);
if ((obj = xmlXPathEvalExpression(xpath, ctx))) {
if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_NOT_FOUND, cirpath);
}
} else {
node = obj->nodesetval->nodeTab[0];
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_FOUND, ident, cirpath);
}
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
xmlFreeDoc(doc);
return node;
}
/* Add a reference to the XML report. */
static void add_ref_to_report(xmlNodePtr rpt, xmlNodePtr ref, const xmlChar *type, const xmlChar *ident, long int lineno, const char *cir, struct opts *opts)
{
xmlNodePtr node;
xmlChar line_s[16], *xpath;
/* Check if XML report is enabled. */
if (!rpt) {
return;
}
node = xmlNewChild(rpt, NULL, BAD_CAST "ref", NULL);
xmlSetProp(node, BAD_CAST "type", type);
xmlSetProp(node, BAD_CAST "name", ident);
xmlStrPrintf(line_s, 16, "%ld", lineno);
xmlSetProp(node, BAD_CAST "line", BAD_CAST line_s);
xpath = xpath_of(ref);
xmlSetProp(node, BAD_CAST "xpath", xpath);
xmlFree(xpath);
if (cir) {
xmlSetProp(node, BAD_CAST "cir", BAD_CAST cir);
}
node = xmlAddChild(node, xmlCopyNode(ref, 1));
}
/* Find a data module filename in the current directory based on the dmRefIdent
* element. */
static bool find_dmod_fname(char *dst, xmlNodePtr dmRefIdent, struct opts *opts)
{
char *model_ident_code;
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 code[64];
xmlNodePtr dmCode, issueInfo, language;
dmCode = xpath_first_node(NULL, dmRefIdent, BAD_CAST "dmCode|avee");
issueInfo = xpath_first_node(NULL, dmRefIdent, BAD_CAST "issueInfo|issno");
language = xpath_first_node(NULL, dmRefIdent, BAD_CAST "language");
model_ident_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "modelic|@modelIdentCode");
system_diff_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "sdc|@systemDiffCode");
system_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "chapnum|@systemCode");
sub_system_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "section|@subSystemCode");
sub_sub_system_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "subsect|@subSubSystemCode");
assy_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "subject|@assyCode");
disassy_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "discode|@disassyCode");
disassy_code_variant = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "discodev|@disassyCodeVariant");
info_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "incode|@infoCode");
info_code_variant = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "incodev|@infoCodeVariant");
item_location_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "itemloc|@itemLocationCode");
learn_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "@learnCode");
learn_event_code = (char *) xpath_first_value(NULL, dmCode, BAD_CAST "@learnEventCode");
snprintf(code, 64, "DMC-%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);
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);
if (learn_code) {
char learn[8];
snprintf(learn, 8, "-%s%s", learn_code, learn_event_code);
strcat(code, learn);
}
xmlFree(learn_code);
xmlFree(learn_event_code);
if (!opts->no_issue) {
if (issueInfo) {
char *issue_number;
char *in_work;
char iss[8];
issue_number = (char *) xpath_first_value(NULL, issueInfo, BAD_CAST "@issno|@issueNumber");
in_work = (char *) xpath_first_value(NULL, issueInfo, BAD_CAST "@inwork|@inWork");
snprintf(iss, 8, "_%s-%s", issue_number, in_work ? in_work : "00");
strcat(code, iss);
xmlFree(issue_number);
xmlFree(in_work);
} else if (language) {
strcat(code, "_\?\?\?-\?\?");
}
}
if (language) {
char *language_iso_code;
char *country_iso_code;
char lang[8];
language_iso_code = (char *) xpath_first_value(NULL, language, BAD_CAST "@language|@languageIsoCode");
country_iso_code = (char *) xpath_first_value(NULL, language, BAD_CAST "@country|@countryIsoCode");
snprintf(lang, 8, "_%s-%s", language_iso_code, country_iso_code);
strcat(code, lang);
xmlFree(language_iso_code);
xmlFree(country_iso_code);
}
/* Look for DM in the directory hierarchy. */
if (find_csdb_object(dst, opts->search_dir, code, is_dm, opts->recursive)) {
return true;
}
/* Look for DM in the list of CIRs. */
if (find_csdb_object_in_list(dst, opts->cirs.paths, opts->cirs.count, code)) {
return true;
}
/* Look for DM in the list of objects to check. */
if (find_csdb_object_in_list(dst, opts->objects.paths, opts->objects.count, code)) {
return true;
}
fprintf(stderr, W_MISSING_REF_DM, code);
return false;
}
/* Remove namespace declaration added by tool. */
static void remove_repcheck_ns(xmlNodePtr node)
{
xmlNsPtr cur, prev;
cur = node->nsDef;
prev = NULL;
while (cur) {
xmlNsPtr next;
next = cur->next;
if (xmlStrcmp(cur->href, S1KD_REPCHECK_NS) == 0) {
if (prev == NULL) {
node->nsDef = next;
} else {
prev->next = next;
}
xmlFreeNode((xmlNodePtr) cur);
} else {
prev = cur;
}
cur = next;
}
}
/* Remove attributes added by tool. */
static void remove_repcheck_attrs(xmlNodePtr ref, xmlNsPtr ns)
{
xmlUnsetNsProp(ref, ns, BAD_CAST "type");
xmlUnsetNsProp(ref, ns, BAD_CAST "name");
xmlUnsetNsProp(ref, ns, BAD_CAST "test");
remove_repcheck_ns(ref);
}
/* Check a specific CIR reference. */
static int check_cir_ref(xmlNodePtr ref, const char *path, xmlNodePtr rpt, struct opts *opts)
{
int i, err = 0;
xmlAttrPtr type_attr, ident_attr, xpath_attr;
xmlChar *type, *ident, *xpath;
long int lineno;
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
lineno = xmlGetLineNo(ref);
type_attr = xmlHasNsProp(ref, BAD_CAST "type", S1KD_REPCHECK_NS);
ident_attr = xmlHasNsProp(ref, BAD_CAST "name", S1KD_REPCHECK_NS);
xpath_attr = xmlHasNsProp(ref, BAD_CAST "test", S1KD_REPCHECK_NS);
type = xmlNodeGetContent((xmlNodePtr) type_attr);
ident = xmlNodeGetContent((xmlNodePtr) ident_attr);
xpath = xmlNodeGetContent((xmlNodePtr) xpath_attr);
remove_repcheck_attrs(ref, ident_attr->ns);
/* Check if there is an explicit CIR reference. */
ctx = xmlXPathNewContext(ref->doc);
xmlXPathSetContextNode(ref, ctx);
obj = xmlXPathEvalExpression(BAD_CAST "refs/dmRef/dmRefIdent|refs/refdm", ctx);
/* If there is not, use any of the specified/found CIRs. */
if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
/* Search in all CIRs. */
for (i = 0; i < opts->cirs.count; ++i) {
if (find_ref_in_cir(ref, ident, xpath, opts->cirs.paths[i], opts)) {
add_ref_to_report(rpt, ref, type, ident, lineno, opts->cirs.paths[i], opts);
goto done;
}
}
/* Search in all other specified objects, if allowed. */
if (opts->search_all_objs) {
for (i = 0; i < opts->objects.count; ++i) {
if (find_ref_in_cir(ref, ident, xpath, opts->objects.paths[i], opts)) {
add_ref_to_report(rpt, ref, type, ident, lineno, opts->objects.paths[i], opts);
goto done;
}
}
}
/* If there is an explicit reference, only check against that. */
} else {
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
char fname[PATH_MAX];
if (find_dmod_fname(fname, obj->nodesetval->nodeTab[i], opts)) {
if (find_ref_in_cir(ref, ident, xpath, fname, opts)) {
add_ref_to_report(rpt, ref, type, ident, lineno, fname, opts);
goto done;
}
}
}
}
if (opts->verbosity >= NORMAL) {
fprintf(stderr, E_NOT_FOUND, path, lineno, ident);
}
add_ref_to_report(rpt, ref, type, ident, lineno, NULL, opts);
err = 1;
done:
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
xmlFree(type);
xmlFree(ident);
xmlFree(xpath);
return err;
}
/* List a CIR reference without validating it. */
static void list_cir_ref(const xmlNodePtr ref, const char *path, xmlNodePtr rpt, struct opts *opts)
{
xmlAttrPtr type_attr, ident_attr;
xmlChar *type, *ident;
long int lineno;
lineno = xmlGetLineNo(ref);
type_attr = xmlHasNsProp(ref, BAD_CAST "type", S1KD_REPCHECK_NS);
ident_attr = xmlHasNsProp(ref, BAD_CAST "name", S1KD_REPCHECK_NS);
type = xmlNodeGetContent((xmlNodePtr) type_attr);
ident = xmlNodeGetContent((xmlNodePtr) ident_attr);
remove_repcheck_attrs(ref, ident_attr->ns);
if (rpt) {
add_ref_to_report(rpt, ref, type, ident, lineno, NULL, opts);
} else {
printf("%s:%ld:%s\n", path, lineno, (char *) ident);
}
xmlFree(type);
xmlFree(ident);
}
/* Check all CIR references in a document. */
static int check_cir_refs(xmlDocPtr doc, const char *path, struct opts *opts)
{
int err = 0;
xmlDocPtr styledoc, res;
xsltStylesheetPtr style;
xmlNodePtr rpt;
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;
/* Add object to report. */
if (opts->report) {
rpt = xmlNewChild(opts->report, NULL, BAD_CAST "object", NULL);
xmlSetProp(rpt, BAD_CAST "path", BAD_CAST path);
} else {
rpt = NULL;
}
styledoc = xmlCopyDoc(opts->cir_refs_xsl, 1);
style = xsltParseStylesheetDoc(styledoc);
res = xsltApplyStylesheet(style, doc, NULL);
ctx = xmlXPathNewContext(res);
xmlXPathRegisterNs(ctx, BAD_CAST "s1kd-repcheck", S1KD_REPCHECK_NS);
if (opts->type) {
xmlXPathRegisterVariable(ctx, BAD_CAST "type", xmlXPathNewString(BAD_CAST opts->type));
obj = xmlXPathEval(BAD_CAST "//*[@s1kd-repcheck:test and @s1kd-repcheck:type=$type]", ctx);
} else {
obj = xmlXPathEval(BAD_CAST "//*[@s1kd-repcheck:test]", ctx);
}
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
if (opts->list_refs) {
list_cir_ref(obj->nodesetval->nodeTab[i], path, rpt, opts);
} else if (check_cir_ref(obj->nodesetval->nodeTab[i], path, rpt, opts) != 0) {
err = 1;
}
}
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctx);
xmlFreeDoc(res);
xsltFreeStylesheet(style);
if (!opts->list_refs) {
if (err) {
xmlSetProp(rpt, BAD_CAST "valid", BAD_CAST "no");
} else {
xmlSetProp(rpt, BAD_CAST "valid", BAD_CAST "yes");
}
}
return err;
}
/* Check all CIR references in the specified CSDB object. */
static int check_cir_refs_in_file(const char *path, struct opts *opts)
{
xmlDocPtr doc;
int err = 0;
xmlDocPtr validtree = NULL;
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_CHECK, path);
}
if (!(doc = read_xml_doc(path))) {
return 1;
}
/* Make a copy of the XML tree before performing additional
* processing on it. */
if (opts->output_valid) {
validtree = xmlCopyDoc(doc, 1);
}
if (opts->rem_delete) {
rem_delete_elems(doc);
}
err = check_cir_refs(doc, path, opts);
if (opts->verbosity >= VERBOSE) {
if (err) {
fprintf(stderr, F_INVALID, path);
} else {
fprintf(stderr, S_VALID, path);
}
}
if ((err && opts->show_filenames == SHOW_INVALID) || (!err && opts->show_filenames == SHOW_VALID)) {
puts(path);
}
if (opts->output_valid) {
if (err == 0) {
save_xml_doc(validtree, "-");
}
xmlFreeDoc(validtree);
}
xmlFreeDoc(doc);
return err;
}
/* Add a CSDB object to a list. */
static void add_object(struct objects *objects, const char *path, struct opts *opts)
{
if (objects->count == objects->max) {
if (!(objects->paths = realloc(objects->paths, (objects->max *= 2) * PATH_MAX))) {
if (opts->verbosity > QUIET) {
fprintf(stderr, E_MAX_OBJECTS);
}
exit(EXIT_MAX_OBJECTS);
}
}
strcpy(objects->paths[(objects->count)++], path);
}
/* Add a list of CSDB objects to a list. */
static void add_object_list(struct objects *objects, const char *list, struct opts *opts)
{
FILE *f;
char path[PATH_MAX];
if (list) {
if (!(f = fopen(list, "r"))) {
if (opts->verbosity >= NORMAL) {
fprintf(stderr, E_BAD_LIST, list);
}
return;
}
} else {
f = stdin;
}
while (fgets(path, PATH_MAX, f)) {
strtok(path, "\t\r\n");
add_object(objects, path, opts);
}
if (list) {
fclose(f);
}
}
/* Initialize a list of CSDB objects. */
static void init_objects(struct objects *objects)
{
objects->paths = malloc(PATH_MAX);
objects->max = 1;
objects->count = 0;
}
/* Free a list of CSDB objects. */
static void free_objects(struct objects *objects)
{
free(objects->paths);
}
/* Find CIRs in directories and add them to the list. */
static void find_cirs(struct objects *cirs, char *search_dir, struct opts *opts)
{
DIR *dir;
struct dirent *cur;
char fpath[PATH_MAX], cpath[PATH_MAX];
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_FIND_CIR, search_dir);
}
if (!(dir = opendir(search_dir))) {
return;
}
/* Clean up the directory string. */
if (strcmp(search_dir, ".") == 0) {
strcpy(fpath, "");
} else if (search_dir[strlen(search_dir) - 1] != '/') {
strcpy(fpath, search_dir);
strcat(fpath, "/");
} else {
strcpy(fpath, search_dir);
}
/* Search for CIRs. */
while ((cur = readdir(dir))) {
strcpy(cpath, fpath);
strcat(cpath, cur->d_name);
if (opts->recursive && isdir(cpath, true)) {
find_cirs(cirs, cpath, opts);
} else if (is_dm(cur->d_name) && is_cir(cpath, opts->rem_delete)) {
if (opts->verbosity >= DEBUG) {
fprintf(stderr, I_FIND_CIR_FOUND, cpath);
}
add_object(cirs, cpath, opts);
}
}
closedir(dir);
}
/* Use only the latest issue of a CIR. */
static void extract_latest_cirs(struct objects *cirs)
{
struct objects latest;
qsort(cirs->paths, cirs->count, PATH_MAX, compare_basename);
latest.paths = malloc(cirs->count * PATH_MAX);
latest.count = extract_latest_csdb_objects(latest.paths, cirs->paths, cirs->count);
free(cirs->paths);
cirs->paths = latest.paths;
cirs->count = latest.count;
}
/* Show a summary of the check. */
static void print_stats(xmlDocPtr doc)
{
xmlDocPtr styledoc;
xsltStylesheetPtr style;
xmlDocPtr res;
styledoc = read_xml_mem((const char *) xsl_stats_xsl, xsl_stats_xsl_len);
style = xsltParseStylesheetDoc(styledoc);
res = xsltApplyStylesheet(style, doc, NULL);
fprintf(stderr, "%s", (char *) res->children->content);
xmlFreeDoc(res);
xsltFreeStylesheet(style);
}
/* Show usage message. */
static void show_help(void)
{
puts("Usage: " PROG_NAME " [options] [<object>...]");
puts("");
puts("Options:");
puts(" -A, --all-refs Validate indirect CIR references.");
puts(" -a, --all Resolve against CIRs specified as objects to check.");
puts(" -d, --dir <dir> Search for CIRs in <dir>.");
puts(" -F, --valid-filenames List valid files.");
puts(" -f, --filenames List invalid files.");
puts(" -h, -?, --help Show help/usage message.");
puts(" -L, --list-refs List CIR refs instead of validating them.");
puts(" -l, --list Treat input as list of CSDB objects.");
puts(" -N, --omit-issue Assume issue/inwork numbers are omitted.");
puts(" -o, --output-valid Output valid CSDB objects to stdout.");
puts(" -p, --progress Display a progress bar.");
puts(" -q, --quiet Quiet mode.");
puts(" -R, --cir <CIR> Check references against the given CIR.");
puts(" -r, --recursive Search for CIRs recursively.");
puts(" -T, --summary Print a summary of the check.");
puts(" -t, --type <type> Type of CIR references to check.");
puts(" -v, --verbose Verbose output.");
puts(" -X, --xsl <file> Custom XSLT for extracting CIR references.");
puts(" -x, --xml Output XML report.");
puts(" -^, --remove-deleted Validate with elements marked as \"delete\" removed.");
puts(" --version Show version information.");
puts(" --zenity-progress Print progress information in the zenity --progress format.");
puts(" <object> CSDB object(s) to check.");
LIBXML2_PARSE_LONGOPT_HELP
}
/* Show version information. */
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, err = 0;
const char *sopts = "AaDd:FfLlNopqR:rTt:vX:x^h?";
struct option lopts[] = {
{"version" , no_argument , 0, 0},
{"help" , no_argument , 0, 'h'},
{"all-refs" , no_argument , 0, 'A'},
{"all" , no_argument , 0, 'a'},
{"dump-xsl" , no_argument , 0, 'D'},
{"dir" , required_argument, 0, 'd'},
{"valid-filenames", no_argument , 0, 'F'},
{"filenames" , no_argument , 0, 'f'},
{"list-refs" , no_argument , 0, 'L'},
{"list" , no_argument , 0, 'l'},
{"omit-issue" , no_argument , 0, 'N'},
{"output-valid" , no_argument , 0, 'o'},
{"progress" , no_argument , 0, 'p'},
{"quiet" , no_argument , 0, 'q'},
{"cir" , required_argument, 0, 'R'},
{"recursive" , no_argument , 0, 'r'},
{"summary" , no_argument , 0, 'T'},
{"type" , required_argument, 0, 't'},
{"verbose" , no_argument , 0, 'v'},
{"xsl" , required_argument, 0, 'X'},
{"xml" , no_argument , 0, 'x'},
{"remove-deleted" , no_argument , 0, '^'},
{"zenity-progress", no_argument , 0, 0},
LIBXML2_PARSE_LONGOPT_DEFS
{0, 0, 0, 0}
};
int loptind = 0;
struct opts opts;
bool is_list = false;
int show_progress = PROGRESS_OFF;
bool find_cir = false;
bool show_stats = false;
bool xml_report = false;
bool all_refs = false;
bool dump_xsl = false;
xmlDocPtr report_doc = NULL;
/* Initialize program options. */
opts.verbosity = NORMAL;
opts.show_filenames = SHOW_NONE;
opts.recursive = false;
opts.no_issue = false;
opts.search_all_objs = false;
opts.output_valid = false;
opts.list_refs = false;
opts.rem_delete = false;
opts.cir_refs_xsl = NULL;
opts.type = NULL;
init_objects(&opts.objects);
init_objects(&opts.cirs);
opts.search_dir = strdup(".");
while ((i = getopt_long(argc, argv, sopts, lopts, &loptind)) != -1) {
switch (i) {
case 0:
if (strcmp(lopts[loptind].name, "version") == 0) {
show_version();
goto cleanup;
} else if (strcmp(lopts[loptind].name, "zenity-progress") == 0) {
show_progress = PROGRESS_ZENITY;
}
LIBXML2_PARSE_LONGOPT_HANDLE(lopts, loptind, optarg)
break;
case 'A':
all_refs = true;
break;
case 'a':
opts.search_all_objs = true;
break;
case 'D':
dump_xsl = true;
break;
case 'd':
free(opts.search_dir);
opts.search_dir = strdup(optarg);
break;
case 'F':
opts.show_filenames = SHOW_VALID;
break;
case 'f':
opts.show_filenames = SHOW_INVALID;
break;
case 'L':
opts.list_refs = true;
break;
case 'l':
is_list = true;
break;
case 'N':
opts.no_issue = true;
break;
case 'o':
opts.output_valid = true;
break;
case 'p':
show_progress = PROGRESS_CLI;
break;
case 'q':
--opts.verbosity;
break;
case 'R':
if (strcmp(optarg, "*") == 0) {
find_cir = true;
} else {
add_object(&opts.cirs, optarg, &opts);
}
break;
case 'r':
opts.recursive = true;
break;
case 'T':
show_stats = true;
break;
case 't':
free(opts.type);
opts.type = strdup(optarg);
break;
case 'v':
++opts.verbosity;
break;
case 'X':
free(opts.cir_refs_xsl);
opts.cir_refs_xsl = read_xml_doc(optarg);
break;
case 'x':
xml_report = true;
break;
case '^':
opts.rem_delete = true;
break;
case 'h':
case '?':
show_help();
goto cleanup;
}
}
/* Load XSLT to extract CIR refs. */
if (opts.cir_refs_xsl == NULL) {
if (all_refs) {
opts.cir_refs_xsl = read_xml_mem((const char *) xsl_cirrefsall_xsl, xsl_cirrefsall_xsl_len);
} else {
opts.cir_refs_xsl = read_xml_mem((const char *) xsl_cirrefs_xsl, xsl_cirrefs_xsl_len);
}
}
/* Dump built-in XSLT if the -D option is specified. */
if (dump_xsl) {
save_xml_doc(opts.cir_refs_xsl, "-");
goto cleanup;
}
/* Initialize the XML report if the -x option is specified. */
if (xml_report || show_stats) {
report_doc = xmlNewDoc(BAD_CAST "1.0");
opts.report = xmlNewNode(NULL, BAD_CAST "repCheck");
xmlDocSetRootElement(report_doc, opts.report);
} else {
opts.report = NULL;
}
/* Search for CIRs when -R* is specified. */
if (find_cir) {
find_cirs(&opts.cirs, opts.search_dir, &opts);
extract_latest_cirs(&opts.cirs);
/* Print the final CIR list in DEBUG mode. */
if (opts.verbosity >= DEBUG) {
int i;
for (i = 0; i < opts.cirs.count; ++i) {
fprintf(stderr, I_FIND_CIR_ADD, opts.cirs.paths[i]);
}
}
}
/* Read specified objects into a list in memory. */
if (optind < argc) {
for (i = optind; i < argc; ++i) {
if (is_list) {
add_object_list(&opts.objects, argv[i], &opts);
} else {
add_object(&opts.objects, argv[i], &opts);
}
}
} else if (is_list) {
add_object_list(&opts.objects, NULL, &opts);
} else {
add_object(&opts.objects, "-", &opts);
}
/* Check CIR references in the objects in the list. */
for (i = 0; i < opts.objects.count; ++i) {
if (check_cir_refs_in_file(opts.objects.paths[i], &opts) != 0) {
err = 1;
}
switch (show_progress) {
case PROGRESS_OFF:
break;
case PROGRESS_CLI:
print_progress_bar(i, opts.objects.count);
break;
case PROGRESS_ZENITY:
print_zenity_progress("Performing repository check...", i, opts.objects.count);
break;
}
}
if (opts.objects.count > 0) {
switch (show_progress) {
case PROGRESS_OFF:
break;
case PROGRESS_CLI:
print_progress_bar(i, opts.objects.count);
break;
case PROGRESS_ZENITY:
print_zenity_progress("Repository check complete.", i, opts.objects.count);
break;
}
}
if (xml_report) {
save_xml_doc(report_doc, "-");
}
if (show_stats) {
print_stats(report_doc);
}
cleanup:
free_objects(&opts.objects);
free_objects(&opts.cirs);
free(opts.search_dir);
xmlFreeDoc(opts.cir_refs_xsl);
free(opts.type);
xmlFreeDoc(report_doc);
xmlCleanupParser();
xsltCleanupGlobals();
return err;
}
gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/s1kd-repcheck/s1kd-repcheck.c