/* Whether the CSDB objects were created with the -N option. */
static int no_issue = 0;
/* Command string to execute with the -e option. */
static char *execstr = NULL;
static void printfiles(char (*files)[PATH_MAX], int n)
{
int i;
if (execstr) {
for (i = 0; i < n; ++i) execfile(execstr, files[i]);
} else {
for (i = 0; i < n; ++i) printf("%s%c", files[i], sep);
}
}
/* Compare two ICN files, grouped by file extension. */
static int compare_icn(const void *a, const void *b)
{
char *sa, *sb, *ba, *bb, *e, *ea, *eb;
int d;
sa = strdup((const char *) a);
sb = strdup((const char *) b);
ba = basename(sa);
bb = basename(sb);
/* Move the file extension to the front. */
ea = malloc(strlen(ba) + 1);
eb = malloc(strlen(bb) + 1);
if ((e = strchr(ba, '.'))) {
d = e - ba;
sprintf(ea, "%s%.*s", e, d, ba);
} else {
strcpy(ea, ba);
}
if ((e = strchr(bb, '.'))) {
d = e - bb;
sprintf(eb, "%s%.*s", e, d, bb);
} else {
strcpy(eb, bb);
}
d = strcasecmp(ea, eb);
free(ea);
free(eb);
free(sa);
free(sb);
return d;
}
/* Show usage message. */
static void show_help(void)
{
puts("Usage: " PROG_NAME " [-0CDGIiLlMNnoPRrSUwX7] [<object>|<dir> ...]");
puts("");
puts("Options:");
puts(" -0, --null Output null-delimited list.");
puts(" -C, --com List comments.");
puts(" -D, --dm List data modules.");
puts(" -e, --exec <cmd> Execute <cmd> for each CSDB object.");
puts(" -G, --icn List ICN files.");
puts(" -I, --inwork Show only inwork issues.");
puts(" -i, --official Show only official issues.");
puts(" -h, -?, --help Show this help message.");
puts(" -L, --dml List DMLs.");
puts(" -l, --latest Show only latest official/inwork issue.");
puts(" -M, --imf List ICN metadata files.");
puts(" -N, --omit-issue Assume issue/inwork numbers are omitted.");
puts(" -n, --other List non-S1000D files.");
puts(" -o, --old Show only old official/inwork issues.");
puts(" -P, --pm List publication modules.");
puts(" -R, --read-only Show only non-writable object files.");
puts(" -r, --recursive Recursively search directories.");
puts(" -S, --smc List SCORM content packages.");
puts(" -U, --upf List data update files.");
puts(" -w, --writable Show only writable object files.");
puts(" -X, --ddn List DDNs.");
puts(" -7, --list Treat input as list of CSDB objects.");
puts(" --version Show version information.");
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\n", xmlParserVersion);
}
/* Determine if file is not a CSDB object. */
static int is_non(const char *base)
{
return !(base[0] == '.' || is_com(base) || is_ddn(base) || is_dm(base) || is_dml(base) || is_icn(base) || is_imf(base) || is_pm(base) || is_smc(base) || is_upf(base));
}
/* Find CSDB objects in a given directory. */
static void list_dir(const char *path, int only_writable, int only_readonly, int recursive)
{
DIR *dir;
struct dirent *cur;
int len = strlen(path);
char fpath[PATH_MAX];
char cpath[PATH_MAX];
/* Return the content of the first node matching an XPath expression. */
static xmlChar *first_xpath_value(xmlDocPtr doc, xmlNodePtr node, const char *xpath)
{
return xmlNodeGetContent(first_xpath_node(doc, node, xpath));
}
/* Checks if a CSDB object is in the official state (inwork = 00). */
static int is_official_issue(const char *fname, const char *path)
{
if (no_issue) {
xmlDocPtr doc;
xmlChar *inwork;
int official;
official = !inwork || xmlStrcmp(inwork, BAD_CAST "00") == 0;
xmlFree(inwork);
xmlFreeDoc(doc);
return official;
} else {
char inwork[3] = "";
int n;
n = sscanf(fname, "%*[^_]_%*3s-%2s", inwork);
return n < 1 || strcmp(inwork, "00") == 0;
}
}
static int extract_latest_icns(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, *s;
int n;
if (i == 0 || strncmp(base1, base2, n - 3) != 0 || strcmp(s, base2 + n) != 0) {
strcpy(latest[nlatest++], files[i]);
} else {
strcpy(latest[nlatest - 1], files[i]);
}
free(name1);
free(name2);
}
return nlatest;
}
/* Copy only old issues of CSDB objects. */
static int remove_latest(char (*latest)[PATH_MAX], char (*files)[PATH_MAX], int nfiles)
{
int i, nlatest = 0;
for (i = 0; i < nfiles; ++i) {
char *name1, *name3, *base1, *base3, *s;
if (name3 && strncmp(base1, base3, s - base1) == 0) {
strcpy(latest[nlatest++], files[i]);
}
free(name1);
free(name3);
}
return nlatest;
}
static int remove_latest_icns(char (*latest)[PATH_MAX], char (*files)[PATH_MAX], int nfiles)
{
int i, nlatest = 0;
for (i = 0; i < nfiles; ++i) {
char *name1, *name3, *base1, *base3, *s;
int n;
/* Copy only official issues of CSDB objects. */
static int extract_official(char (*official)[PATH_MAX], char (*files)[PATH_MAX], int nfiles)
{
int i, nofficial = 0;
for (i = 0; i < nfiles; ++i) {
char *name = strdup(files[i]);
char *base = basename(name);
if (is_official_issue(base, files[i])) {
strcpy(official[nofficial++], files[i]);
}
free(name);
}
return nofficial;
}
/* Copy a list, removing official CSDB objects. */
static int remove_official(char (*official)[PATH_MAX], char (*files)[PATH_MAX], int nfiles)
{
int i, nofficial = 0;
for (i = 0; i < nfiles; ++i) {
char *name = strdup(files[i]);
char *base = basename(name);
if (!is_official_issue(base, files[i])) {
strcpy(official[nofficial++], files[i]);
}
free(name);
}
return nofficial;
}
/* Add a CSDB object to the appropriate list. */
static void list_path(const char *path, int only_writable, int only_readonly, int recursive)
{
char tmp[PATH_MAX], *base;
strcpy(tmp, path);
base = basename(tmp);
if (access(path, R_OK) != 0) {
return;
} else if (only_writable && access(path, W_OK) != 0) {
return;
} else if (only_readonly && access(path, W_OK) == 0) {
return;
} else if (dms && is_dm(base)) {
if (ndms == DM_MAX) {
resize(&dms, &DM_MAX);
}
strcpy(dms[ndms++], path);
} else if (pms && is_pm(base)) {
if (npms == PM_MAX) {
resize(&pms, &PM_MAX);
}
strcpy(pms[npms++], path);
} else if (coms && is_com(base)) {
if (ncoms == COM_MAX) {
resize(&coms, &COM_MAX);
}
strcpy(coms[ncoms++], path);
} else if (icns && is_icn(base)) {
if (nicns == ICN_MAX) {
resize(&icns, &ICN_MAX);
}
strcpy(icns[nicns++], path);
} else if (imfs && is_imf(base)) {
if (nimfs == IMF_MAX) {
resize(&imfs, &IMF_MAX);
}
strcpy(imfs[nimfs++], path);
} else if (ddns && is_ddn(base)) {
if (nddns == DDN_MAX) {
resize(&ddns, &DDN_MAX);
}
strcpy(ddns[nddns++], path);
} else if (dmls && is_dml(base)) {
if (ndmls == DML_MAX) {
resize(&dmls, &DML_MAX);
}
strcpy(dmls[ndmls++], path);
} else if (smcs && is_smc(base)) {
if (nsmcs == SMC_MAX) {
resize(&smcs, &SMC_MAX);
}
strcpy(smcs[nsmcs++], path);
} else if (upfs && is_upf(base)) {
if (nupfs == UPF_MAX) {
resize(&upfs, &UPF_MAX);
}
strcpy(upfs[nupfs++], path);
} else if (isdir(path, 0)) {
list_dir(path, only_writable, only_readonly, recursive);
} else if (nons && is_non(base)) {
if (nnons == NON_MAX) {
resize(&nons, &NON_MAX);
}
strcpy(nons[nnons++], path);
}
}
static void read_list(const char *path, int only_writable, int only_readonly, int recursive)
{
FILE *f;
char line[PATH_MAX];
if (path) {
if (!(f = fopen(path, "r"))) {
fprintf(stderr, E_BAD_LIST, path);
return;
}
} else {
f = stdin;
}
int only_latest = 0;
int only_official_issue = 0;
int only_writable = 0;
int only_readonly = 0;
int only_old = 0;
int only_inwork = 0;
int recursive = 0;
int show = 0;
int list = 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 '0': sep = '\0'; break;
case 'C': show |= SHOW_COM; break;
case 'D': show |= SHOW_DM; break;
case 'e': execstr = strdup(optarg); break;
case 'G': show |= SHOW_ICN; break;
case 'i': only_official_issue = 1; break;
case 'L': show |= SHOW_DML; break;
case 'l': only_latest = 1; break;
case 'M': show |= SHOW_IMF; break;
case 'P': show |= SHOW_PM; break;
case 'R': only_readonly = 1; break;
case 'r': recursive = 1; break;
case 'S': show |= SHOW_SMC; break;
case 'w': only_writable = 1; break;
case 'X': show |= SHOW_DDN; break;
case 'o': only_old = 1; break;
case 'I': only_inwork = 1; break;
case 'N': no_issue = 1; break;
case 'n': show |= SHOW_NON; break;
case 'U': show |= SHOW_UPF; break;
case '7': list = 1; break;
case 'h':
case '?': show_help();
return 0;
}
}