#include #include #include #include #include #include #include #include #include #include #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] [...]"); 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 Search for CIRs in ."); 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 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 of CIR references to check."); puts(" -v, --verbose Verbose output."); puts(" -X, --xsl 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(" 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; }