/ .. / / -> download
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include "s1kd_tools.h"

#define PROG_NAME "s1kd-mvref"
#define VERSION "2.6.0"

#define ERR_PREFIX PROG_NAME ": ERROR: "

#define E_ENCODING_ERROR ERR_PREFIX "Error encoding path name.\n"
#define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n"

#define EXIT_ENCODING_ERROR 1
#define EXIT_NO_FILE 2

#define ADDR_PATH     "//dmAddress|//dmaddres|//pmAddress|//pmaddres"
#define REFS_PATH_CONTENT BAD_CAST "//content//dmRef|//content//refdm[*]|//content//pmRef|//content/refpm"
#define REFS_PATH BAD_CAST "//dmRef|//pmRef|//refdm[*]|//refpm"

static enum verbosity { QUIET, NORMAL, VERBOSE } verbosity = NORMAL;

static xmlNodePtr firstXPathNode(const char *xpath, xmlDocPtr doc, xmlNodePtr node)
{
	xmlXPathContextPtr ctx;
	xmlXPathObjectPtr obj;
	xmlNodePtr first;

	if (doc) {
		ctx = xmlXPathNewContext(doc);
	} else if (node) {
		ctx = xmlXPathNewContext(node->doc);
	} else {
		return NULL;
	}

	ctx->node = node;
	
	obj = xmlXPathEvalExpression(BAD_CAST xpath, ctx);

	if (xmlXPathNodeSetIsEmpty(obj->nodesetval))
		first = NULL;
	else
		first = obj->nodesetval->nodeTab[0];

	xmlXPathFreeObject(obj);
	xmlXPathFreeContext(ctx);

	return first;
}

static char *firstXPathString(const char *xpath, xmlDocPtr doc, xmlNodePtr node)
{
	return (char *) xmlNodeGetContent(firstXPathNode(xpath, doc, node));
}

static xmlNodePtr findChild(xmlNodePtr parent, const char *name)
{
	xmlNodePtr cur;

	if (!parent) return NULL;

	for (cur = parent->children; cur; cur = cur->next) {
		if (strcmp((char *) cur->name, name) == 0) {
			return cur;
		}
	}

	return NULL;
}

static void replaceNode(xmlNodePtr a, xmlNodePtr b)
{
	xmlNodePtr c;
	c = xmlCopyNode(b, 1);
	xmlNodeSetName(c, a->name);
	xmlAddNextSibling(a, c);
	xmlUnlinkNode(a);
	xmlFreeNode(a);
}

static void getPmCode(char *dst, xmlNodePtr ident, bool withIssue, bool withLang)
{
	xmlNodePtr identExtension, pmCode, issueInfo, language;

	char *modelIdentCode, *pmIssuer, *pmNumber, *pmVolume;

	char cat[256];

	identExtension = findChild(ident, "identExtension");
	pmCode         = firstXPathNode("pmCode|pmc", NULL, ident);
	issueInfo      = firstXPathNode("issueInfo|issno", NULL, ident);
	language       = findChild(ident, "language");

	strcpy(dst, "");

	if (identExtension) {
		char *extensionProducer, *extensionCode;

		extensionProducer = (char *) xmlGetProp(identExtension, BAD_CAST "extensionProducer");
		extensionCode     = (char *) xmlGetProp(identExtension, BAD_CAST "extensionCode");

		snprintf(cat, 256, "%s-%s-", extensionProducer, extensionCode);

		xmlFree(extensionProducer);
		xmlFree(extensionCode);

		strcat(dst, cat);
	}

	modelIdentCode = firstXPathString("@modelIdentCode|modelic", NULL, pmCode);
	pmIssuer       = firstXPathString("@pmIssuer|pmissuer", NULL, pmCode);
	pmNumber       = firstXPathString("@pmNumber|pmnumber", NULL, pmCode);
	pmVolume       = firstXPathString("@pmVolume|pmvolume", NULL, pmCode);

	snprintf(cat, 256, "%s-%s-%s-%s", modelIdentCode, pmIssuer, pmNumber, pmVolume);

	xmlFree(modelIdentCode);
	xmlFree(pmIssuer);
	xmlFree(pmNumber);
	xmlFree(pmVolume);

	strcat(dst, cat);

	if (withIssue && issueInfo) {
		char *issueNumber, *inWork;

		issueNumber = firstXPathString("@issueNumber|@issno", NULL, issueInfo);
		inWork      = firstXPathString("@inWork|@inwork", NULL, issueInfo);

		snprintf(cat, 256, "_%s-%s", issueNumber, inWork ? inWork : "00");

		xmlFree(issueNumber);
		xmlFree(inWork);

		strcat(dst, cat);
	}

	if (withLang && language) {
		char *languageIsoCode, *countryIsoCode;

		languageIsoCode = firstXPathString("@languageIsoCode|@language", NULL, language);
		countryIsoCode  = firstXPathString("@countryIsoCode|@country", NULL, language);

		snprintf(cat, 256, "_%s-%s", languageIsoCode, countryIsoCode);

		xmlFree(languageIsoCode);
		xmlFree(countryIsoCode);

		strcat(dst, cat);
	}
}

static void getDmCode(char *dst, xmlNodePtr ident, bool withIssue, bool withLang)
{
	xmlNodePtr identExtension, dmCode, issueInfo, language;
	char *modelIdentCode;
	char *systemDiffCode;
	char *systemCode;
	char *subSystemCode;
	char *subSubSystemCode;
	char *assyCode;
	char *disassyCode;
	char *disassyCodeVariant;
	char *infoCode;
	char *infoCodeVariant;
	char *itemLocationCode;
	char *learnCode;
	char *learnEventCode;
	
	char cat[256];

	identExtension = firstXPathNode("identExtension|dmcextension", NULL, ident);
	dmCode         = firstXPathNode("dmCode|.//avee", NULL, ident);
	issueInfo      = firstXPathNode("issueInfo|issno", NULL, ident);
	language       = findChild(ident, "language");

	strcpy(dst, "");

	if (identExtension) {
		char *extensionProducer, *extensionCode;

		extensionProducer = firstXPathString("@extensionProducer|dmeproducer", NULL, identExtension);
		extensionCode     = firstXPathString("@extensionCode|dmecode", NULL, identExtension);

		snprintf(cat, 256, "%s-%s-", extensionProducer, extensionCode);

		xmlFree(extensionProducer);
		xmlFree(extensionCode);

		strcat(dst, cat);
	}

	modelIdentCode     = firstXPathString("@modelIdentCode|modelic", NULL, dmCode);
	systemDiffCode     = firstXPathString("@systemDiffCode|sdc", NULL, dmCode);
	systemCode         = firstXPathString("@systemCode|chapnum", NULL, dmCode);
	subSystemCode      = firstXPathString("@subSystemCode|section", NULL, dmCode);
	subSubSystemCode   = firstXPathString("@subSubSystemCode|subsect", NULL, dmCode);
	assyCode           = firstXPathString("@assyCode|subject", NULL, dmCode);
	disassyCode        = firstXPathString("@disassyCode|discode", NULL, dmCode);
	disassyCodeVariant = firstXPathString("@disassyCodeVariant|discodev", NULL, dmCode);
	infoCode           = firstXPathString("@infoCode|incode", NULL, dmCode);
	infoCodeVariant    = firstXPathString("@infoCodeVariant|incodev", NULL, dmCode);
	itemLocationCode   = firstXPathString("@itemLocationCode|itemloc", NULL, dmCode);
	learnCode          = firstXPathString("@learnCode", NULL, dmCode);
	learnEventCode     = firstXPathString("@learnEventCode", NULL, dmCode);

	snprintf(cat, 256, "%s-%s-%s-%s%s-%s-%s%s-%s%s-%s",
		modelIdentCode,
		systemDiffCode,
		systemCode,
		subSystemCode,
		subSubSystemCode,
		assyCode,
		disassyCode,
		disassyCodeVariant,
		infoCode,
		infoCodeVariant,
		itemLocationCode);
	
	xmlFree(modelIdentCode);
	xmlFree(systemDiffCode);
	xmlFree(systemCode);
	xmlFree(subSystemCode);
	xmlFree(subSubSystemCode);
	xmlFree(assyCode);
	xmlFree(disassyCode);
	xmlFree(disassyCodeVariant);
	xmlFree(infoCode);
	xmlFree(infoCodeVariant);
	xmlFree(itemLocationCode);

	strcat(dst, cat);

	if (learnCode && learnEventCode) {
		snprintf(cat, 256, "-%s%s", learnCode, learnEventCode);
		strcat(dst, cat);
	}

	xmlFree(learnCode);
	xmlFree(learnEventCode);

	if (withIssue && issueInfo) {
		char *issueNumber, *inWork;

		issueNumber = firstXPathString("@issueNumber|@issno", NULL, issueInfo);
		inWork      = firstXPathString("@inWork|@inwork", NULL, issueInfo);

		snprintf(cat, 256, "_%s-%s", issueNumber, inWork ? inWork : "00");

		xmlFree(issueNumber);
		xmlFree(inWork);

		strcat(dst, cat);
	}

	if (withLang && language) {
		char *languageIsoCode, *countryIsoCode;

		languageIsoCode = firstXPathString("@languageIsoCode|@language", NULL, language);
		countryIsoCode  = firstXPathString("@countryIsoCode|@country", NULL, language);

		snprintf(cat, 256, "_%s-%s", languageIsoCode, countryIsoCode);

		xmlFree(languageIsoCode);
		xmlFree(countryIsoCode);

		strcat(dst, cat);
	}
}

static bool isDmRef(xmlNodePtr ref)
{
	return xmlStrcmp(ref->name, BAD_CAST "dmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "refdm") == 0;
}

static bool isDmAddress(xmlNodePtr address)
{
	return xmlStrcmp(address->name, BAD_CAST "dmAddress") == 0 || xmlStrcmp(address->name, BAD_CAST "dmaddres") == 0;
}

static bool sameDm(xmlNodePtr ref, xmlNodePtr address)
{
	char refcode[256], addcode[256];
	bool withIssue, withLang;

	xmlNodePtr ref_dmIdent;
	xmlNodePtr add_dmIdent;

	if (!isDmRef(ref) || !isDmAddress(address))
		return false;

	ref_dmIdent = firstXPathNode("dmRefIdent|self::refdm", NULL, ref);
	add_dmIdent = firstXPathNode("dmIdent|self::dmaddres", NULL, address);

	withIssue = firstXPathNode(".//issueInfo|.//issno", NULL, ref);
	withLang  = firstXPathNode(".//language", NULL, ref);

	getDmCode(refcode, ref_dmIdent, withIssue, withLang);
	getDmCode(addcode, add_dmIdent, withIssue, withLang);

	if (verbosity >= VERBOSE && strcmp(refcode, addcode) == 0) {
		fprintf(stderr, "    Updating reference to data module %s...\n", addcode);
	}

	return strcmp(refcode, addcode) == 0;
}

static bool isPmRef(xmlNodePtr ref)
{
	return xmlStrcmp(ref->name, BAD_CAST "pmRef") == 0 || xmlStrcmp(ref->name, BAD_CAST "refpm") == 0;
}

static bool isPmAddress(xmlNodePtr address)
{
	return xmlStrcmp(address->name, BAD_CAST "pmAddress") == 0 || xmlStrcmp(address->name, BAD_CAST "pmaddres") == 0;
}

static bool samePm(xmlNodePtr ref, xmlNodePtr address)
{
	char refcode[256], addcode[256];
	bool withIssue, withLang;

	xmlNodePtr ref_pmIdent;
	xmlNodePtr add_pmIdent;

	if (!isPmRef(ref) || !isPmAddress(address))
		return false;

	ref_pmIdent = firstXPathNode("pmRefIdent|self::refpm", NULL, ref);
	add_pmIdent = firstXPathNode("pmIdent|self::pmaddres", NULL, address);

	withIssue = firstXPathNode("issueInfo|issno", NULL, ref_pmIdent);
	withLang  = findChild(ref_pmIdent, "language");

	getPmCode(refcode, ref_pmIdent, withIssue, withLang);
	getPmCode(addcode, add_pmIdent, withIssue, withLang);

	if (verbosity >= VERBOSE && strcmp(refcode, addcode) == 0) {
		fprintf(stderr, "    Updating reference to pub module %s...\n", addcode);
	}

	return strcmp(refcode, addcode) == 0;
}

static void updateRef(xmlNodePtr ref, xmlNodePtr addresses, const char *fname, xmlNodePtr recode)
{
	xmlNodePtr cur;

	for (cur = addresses->children; cur; cur = cur->next) {
		if (sameDm(ref, cur)) {
			xmlNodePtr dmAddressItems, issueDate, dmTitle,
			           dmRefAddressItems, dmRefIssueDate,
				   dmRefTitle, dmIdent, dmCode, dmRefIdent,
				   dmRefCode, issueInfo, refIssueInfo,
				   language, refLanguage;

			dmIdent = firstXPathNode("dmIdent|self::dmaddres", NULL, recode);
			dmCode = firstXPathNode("dmCode|.//avee", NULL, dmIdent);
			issueInfo = firstXPathNode("issueInfo|issno", NULL, dmIdent);
			language = findChild(dmIdent, "language");

			dmRefIdent = firstXPathNode("dmRefIdent|self::refdm", NULL, ref);
			dmRefCode  = firstXPathNode("dmCode|.//avee", NULL, dmRefIdent);
			refIssueInfo = firstXPathNode("issueInfo|issno", NULL, dmRefIdent);
			refLanguage = findChild(dmRefIdent, "language");

			if (verbosity >= VERBOSE) {
				char code[256];
				getDmCode(code, dmIdent, refIssueInfo, refLanguage);
				fprintf(stderr, "      Recoding to %s...\n", code);
			}

			replaceNode(dmRefCode, dmCode);
			if (refIssueInfo) replaceNode(refIssueInfo, issueInfo);
			if (refLanguage) replaceNode(refLanguage, language);

			dmAddressItems = firstXPathNode("dmAddressItems|self::dmaddres", NULL, recode);

			issueDate           = findChild(dmAddressItems, "issueDate");
			dmTitle             = firstXPathNode("dmTitle|dmtitle", NULL, dmAddressItems);

			dmRefAddressItems   = firstXPathNode("dmRefAddressItems|self::refdm", NULL, ref);
			dmRefIssueDate      = findChild(dmRefAddressItems, "issueDate");
			dmRefTitle          = firstXPathNode("dmTitle|dmtitle", NULL, dmRefAddressItems);

			if (dmRefIssueDate) replaceNode(dmRefIssueDate, issueDate);
			if (dmRefTitle)     replaceNode(dmRefTitle, dmTitle);
		} else if (samePm(ref, cur)) {
			xmlNodePtr pmAddressItems, issueDate, pmTitle,
			           pmRefAddressItems, pmRefIssueDate,
				   pmRefTitle, pmIdent, pmCode, pmRefIdent,
				   pmRefCode, issueInfo, refIssueInfo,
				   language, refLanguage;

			pmIdent = firstXPathNode("pmIdent|self::pmaddres", NULL, recode);
			pmCode  = firstXPathNode("pmCode|pmc", NULL, pmIdent);
			issueInfo = firstXPathNode("issueInfo|issno", NULL, pmIdent);
			language = findChild(pmIdent, "language");

			pmRefIdent = firstXPathNode("pmRefIdent|self::refpm", NULL, ref);
			pmRefCode  = firstXPathNode("pmCode|pmc", NULL, pmRefIdent);
			refIssueInfo = firstXPathNode("issueInfo|issno", NULL, pmRefIdent);
			refLanguage = findChild(pmRefIdent, "language");

			if (verbosity >= VERBOSE) {
				char code[256];
				getPmCode(code, pmIdent, refIssueInfo, refLanguage);
				fprintf(stderr, "      Recoding to %s...\n", code);
			}

			replaceNode(pmRefCode, pmCode);
			if (refIssueInfo) replaceNode(refIssueInfo, issueInfo);
			if (refLanguage) replaceNode(refLanguage, language);

			pmAddressItems = firstXPathNode("pmAddressItems|self::pmaddres", NULL, recode);

			issueDate           = firstXPathNode("issueDate|issdate", NULL, pmAddressItems);
			pmTitle             = firstXPathNode("pmTitle|pmtitle", NULL, pmAddressItems);

			pmRefAddressItems   = firstXPathNode("pmRefAddressItems|self::refpm", NULL, ref);
			pmRefIssueDate      = firstXPathNode("issueDate|issdate", NULL, pmRefAddressItems);
			pmRefTitle          = firstXPathNode("pmTitle|pmtitle", NULL, pmRefAddressItems);

			if (pmRefIssueDate) replaceNode(pmRefIssueDate, issueDate);
			if (pmRefTitle)     replaceNode(pmRefTitle, pmTitle);
		}
	}
}

static void updateRefs(xmlNodeSetPtr refs, xmlNodePtr addresses, const char *fname, xmlNodePtr recode)
{
	int i;

	for (i = 0; i < refs->nodeNr; ++i) {
		updateRef(refs->nodeTab[i], addresses, fname, recode);
	}
}

static void show_help(void)
{
	puts("Usage: " PROG_NAME " [-d <dir>] [-s <source>] [-t <target>] [-clqvh?] [<object>...]");
	puts("");
	puts("Options:");
	puts("  -c, --content          Only move references in content section of targets.");
	puts("  -d, --dir <dir>        Update data modules in directory <dir>.");
	puts("  -f, --overwrite        Overwrite input objects.");
	puts("  -h, -?, --help         Show help/usage message.");
	puts("  -l, --list             Input is a list of data module filenames.");
	puts("  -q, --quiet            Quiet mode.");
	puts("  -s, --source <source>  Source object.");
	puts("  -t, --target <target>  Change refs to <source> into refs to <target>.");
	puts("  -v, --verbose          Verbose output.");
	puts("  --version              Show version information.");
	puts("  <object>...            Objects to change refs in.");
	LIBXML2_PARSE_LONGOPT_HELP
}

static void show_version(void)
{
	printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION);
	printf("Using libxml %s\n", xmlParserVersion);
}

static bool isS1000D(const char *fname)
{
	return (strncmp(fname, "DMC-", 4) == 0 ||
		strncmp(fname, "DME-", 4) == 0 ||
		strncmp(fname, "PMC-", 4) == 0 ||
		strncmp(fname, "PME-", 4) == 0 ||
		strncmp(fname, "COM-", 4) == 0 ||
		strncmp(fname, "IMF-", 4) == 0 ||
		strncmp(fname, "DDN-", 4) == 0 ||
		strncmp(fname, "DML-", 4) == 0 ||
		strncmp(fname, "UPF-", 4) == 0 ||
		strncmp(fname, "UPE-", 4) == 0 ||
		strncmp(fname, "SMC-", 4) == 0 ||
		strncmp(fname, "SME-", 4) == 0) && strncasecmp(fname + strlen(fname) - 4, ".XML", 4) == 0;
}

static void addAddress(const char *fname, xmlNodePtr addresses)
{
	xmlDocPtr doc;
	xmlNodePtr address;

	doc = read_xml_doc(fname);

	if (!doc)
		return;

	if (verbosity >= VERBOSE)
		fprintf(stderr, "Registering %s...\n", fname);

	address = firstXPathNode(ADDR_PATH, doc, NULL);

	if (address) {
		xmlAddChild(addresses, xmlCopyNode(address, 1));
	}

	xmlFreeDoc(doc);
}

static void updateRefsFile(const char *fname, xmlNodePtr addresses, bool contentOnly, const char *recode, bool overwrite)
{
	xmlDocPtr doc, recodeDoc;
	xmlXPathContextPtr ctx;
	xmlXPathObjectPtr obj;
	xmlNodePtr recodeIdent;

	if (!(doc = read_xml_doc(fname))) {
		return;
	}

	if (recode) {
		recodeDoc = read_xml_doc(recode);
		recodeIdent = firstXPathNode(ADDR_PATH, recodeDoc, NULL);
	} else {
		recodeIdent = NULL;
	}

	if (verbosity >= VERBOSE) {
		fprintf(stderr, "Checking refs in %s...\n", fname);
	}

	ctx = xmlXPathNewContext(doc);

	if (contentOnly) {
		obj = xmlXPathEvalExpression(REFS_PATH_CONTENT, ctx);
	} else {
		obj = xmlXPathEvalExpression(REFS_PATH, ctx);
	}

	if (!xmlXPathNodeSetIsEmpty(obj->nodesetval))
		updateRefs(obj->nodesetval, addresses, fname, recodeIdent);
	
	xmlXPathFreeObject(obj);
	xmlXPathFreeContext(ctx);

	if (overwrite) {
		save_xml_doc(doc, fname);
	} else {
		save_xml_doc(doc, "-");
	}

	xmlFreeDoc(doc);
}

static void addDirectory(const char *path, xmlNodePtr addresses)
{
	DIR *dir;
	struct dirent *cur;

	dir = opendir(path);

	if (!dir) {
		if (verbosity >= NORMAL) {
			fprintf(stderr, ERR_PREFIX "Directory %s does not exist.\n", path);
		}
		exit(EXIT_NO_FILE);
	}

	while ((cur = readdir(dir))) {
		if (isS1000D(cur->d_name)) {
			char fname[PATH_MAX];
			if (snprintf(fname, PATH_MAX, "%s/%s", path, cur->d_name) < 0) {
				if (verbosity >= NORMAL) {
					fprintf(stderr, E_ENCODING_ERROR);
				}
				exit(EXIT_ENCODING_ERROR);
			}
			addAddress(fname, addresses);
		}
	}

	closedir(dir);
}

static void updateRefsDirectory(const char *path, xmlNodePtr addresses, bool contentOnly, const char *recode, bool overwrite)
{
	DIR *dir;
	struct dirent *cur;

	dir = opendir(path);

	while ((cur = readdir(dir))) {
		if (isS1000D(cur->d_name)) {
			char fname[PATH_MAX];
			if (snprintf(fname, PATH_MAX, "%s/%s", path, cur->d_name) < 0) {
				if (verbosity >= NORMAL) {
					fprintf(stderr, E_ENCODING_ERROR);
				}
				exit(EXIT_ENCODING_ERROR);
			}
			updateRefsFile(fname, addresses, contentOnly, recode, overwrite);
		}
	}

	closedir(dir);
}

static xmlNodePtr addAddressList(const char *fname, xmlNodePtr addresses, xmlNodePtr paths)
{
	FILE *f;
	char path[PATH_MAX];

	if (fname) {
		if (!(f = fopen(fname, "r"))) {
			if (verbosity >= NORMAL) {
				fprintf(stderr, E_BAD_LIST, fname);
			}
			return paths;
		}
	} else {
		f = stdin;
	}

	while (fgets(path, PATH_MAX, f)) {
		strtok(path, "\t\r\n");
		addAddress(path, addresses);
		xmlNewChild(paths, NULL, BAD_CAST "path", BAD_CAST path);
	}

	if (fname) {
		fclose(f);
	}

	return paths;
}

static void updateRefsList(xmlNodePtr addresses, xmlNodePtr paths, bool contentOnly, const char *recode, bool overwrite)
{
	xmlNodePtr cur;
	for (cur = paths->children; cur; cur = cur->next) {
		char *path;
		path = (char *) xmlNodeGetContent(cur);
		updateRefsFile(path, addresses, contentOnly, recode, overwrite);
		xmlFree(path);
	}
}

int main(int argc, char **argv)
{
	xmlNodePtr addresses, paths;

	int i;

	bool contentOnly = false;
	char *source = NULL;
	char *directory = NULL;
	bool isList = false;
	char *recode = NULL;
	bool overwrite = false;

	const char *sopts = "s:cfqvd:lt:qh?";
	struct option lopts[] = {
		{"version"  , no_argument      , 0, 0},
		{"help"     , no_argument      , 0, 'h'},
		{"source"   , required_argument, 0, 's'},
		{"content"  , no_argument      , 0, 'c'},
		{"overwrite", no_argument      , 0, 'f'},
		{"quiet"    , no_argument      , 0, 'q'},
		{"verbose"  , no_argument      , 0, 'v'},
		{"dir"      , required_argument, 0, 'd'},
		{"list"     , no_argument      , 0, 'l'},
		{"target"   , required_argument, 0, 't'},
		LIBXML2_PARSE_LONGOPT_DEFS
		{0, 0, 0, 0}
	};
	int loptind = 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 's':
				if (!source) source = strdup(optarg);
				break;
			case 'c':
				contentOnly = true;
				break;
			case 'f':
				overwrite = true;
				break;
			case 'q':
				--verbosity;
				break;
			case 'v':
				++verbosity;
				break;
			case 'd':
				if (!directory) directory = strdup(optarg);
				break;
			case 'l':
				isList = true;
				break;
			case 't':
				if (!recode) recode = strdup(optarg);
				break;
			case 'h':
			case '?':
				show_help();
				return 0;
		}
	}

	if (recode && !source) {
		if (verbosity >= NORMAL) {
			fprintf(stderr, ERR_PREFIX "Source object must be specified with -s to be moved with -m.\n");
		}
		exit(EXIT_NO_FILE);
	}

	addresses = xmlNewNode(NULL, BAD_CAST "addresses");
	paths = xmlNewNode(NULL, BAD_CAST "paths");

	if (directory) {
		addDirectory(directory, addresses);
	}

	if (source) {
		addAddress(source, addresses);
	} else if (optind < argc) {
		for (i = optind; i < argc; ++i) {
			if (isList) {
				addAddressList(argv[i], addresses, paths);
			} else {
				addAddress(argv[i], addresses);
			}
		}
	} else if (isList) {
		addAddressList(NULL, addresses, paths);
	}

	if (directory) {
		updateRefsDirectory(directory, addresses, contentOnly, recode, overwrite);
	} else if (optind < argc) {
		for (i = optind; i < argc; ++i) {
			if (isList) {
				updateRefsList(addresses, paths, contentOnly, recode, overwrite);
			} else {
				updateRefsFile(argv[i], addresses, contentOnly, recode, overwrite);
			}
		}
	} else if (isList) {
		updateRefsList(addresses, paths, contentOnly, recode, overwrite);
	}

	free(source);
	free(directory);
	free(recode);

	xmlFreeNode(addresses);
	xmlFreeNode(paths);

	xmlCleanupParser();

	return 0;
}


/ gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/s1kd-mvref/s1kd-mvref.c
Styles: Light Dark Classic