/ .. / / -> download
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <ctype.h>
#include <dirent.h>
#include <libgen.h>
#include <errno.h>
#include <sys/stat.h>

#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>

#include "templates.h"
#include "dmtypes.h"
#include "sns.h"
#include "s1kd_tools.h"

#define PROG_NAME "s1kd-newdm"
#define VERSION "4.0.0"

#define ERR_PREFIX PROG_NAME ": ERROR: "

#define E_BREX_NOT_FOUND ERR_PREFIX "Could not find BREX: %s\n"
#define E_BAD_TEMPL_DIR ERR_PREFIX "Cannot dump templates in directory: %s\n"
#define E_ENCODING_ERROR ERR_PREFIX "Error encoding path name.\n"
#define E_NO_SCHEMA ERR_PREFIX "No schema defined for information type %s%s-%s\n"
#define E_NO_SCHEMA_LEARN ERR_PREFIX "No schema defined for information type %s%s-%s-%s%s\n"

#define MAX_MODEL_IDENT_CODE		14	+ 2
#define MAX_SYSTEM_DIFF_CODE		 4	+ 2
#define MAX_SYSTEM_CODE			 3	+ 2
#define MAX_SUB_SYSTEM_CODE		 1	+ 2
#define MAX_SUB_SUB_SYSTEM_CODE		 1	+ 2
#define MAX_ASSY_CODE			 4	+ 2
#define MAX_DISASSY_CODE		 2	+ 2
#define MAX_DISASSY_CODE_VARIANT	 3	+ 2
#define MAX_INFO_CODE			 3	+ 2
#define MAX_INFO_CODE_VARIANT		 1	+ 2
#define MAX_ITEM_LOCATION_CODE		 1	+ 2
#define MAX_LEARN_CODE                   3      + 2
#define MAX_LEARN_EVENT_CODE		 1	+ 2
#define MAX_LANGUAGE_ISO_CODE		 3	+ 2
#define MAX_COUNTRY_ISO_CODE		 2	+ 2
#define MAX_ISSUE_NUMBER		 5	+ 2
#define MAX_IN_WORK			 2	+ 2
#define MAX_SECURITY_CLASSIFICATION	 2	+ 2

#define MAX_DATAMODULE_CODE 256

#define MAX_ENTERPRISE_NAME 256
#define MAX_ENTERPRISE_CODE 7

#define MAX_TECH_NAME 256
#define MAX_INFO_NAME 256

#define EXIT_DM_EXISTS 1
#define EXIT_UNKNOWN_DMTYPE 2
#define EXIT_BAD_DMC 3
#define EXIT_BAD_BREX_DMC 4
#define EXIT_BAD_DATE 5
#define EXIT_BAD_ISSUE 6
#define EXIT_BAD_TEMPL_DIR 7
#define EXIT_ENCODING_ERROR 8
#define EXIT_OS_ERROR 9

static char modelIdentCode[MAX_MODEL_IDENT_CODE] = "";
static char systemDiffCode[MAX_SYSTEM_DIFF_CODE] = "";
static char systemCode[MAX_SYSTEM_CODE] = "";
static char subSystemCode[MAX_SUB_SYSTEM_CODE] = "";
static char subSubSystemCode[MAX_SUB_SUB_SYSTEM_CODE] = "";
static char assyCode[MAX_ASSY_CODE] = "";
static char disassyCode[MAX_DISASSY_CODE] = "";
static char disassyCodeVariant[MAX_DISASSY_CODE_VARIANT] = "";
static char infoCode[MAX_INFO_CODE] = "";
static char infoCodeVariant[MAX_INFO_CODE_VARIANT] = "";
static char itemLocationCode[MAX_ITEM_LOCATION_CODE] = "";
static char learnCode[MAX_LEARN_CODE] = "";
static char learnEventCode[MAX_LEARN_EVENT_CODE] = "";

static char languageIsoCode[MAX_LANGUAGE_ISO_CODE] = "";
static char countryIsoCode[MAX_COUNTRY_ISO_CODE] = "";

static char securityClassification[MAX_SECURITY_CLASSIFICATION] = "";

static char issueNumber[MAX_ISSUE_NUMBER] = "";
static char inWork[MAX_IN_WORK] = "";

static char responsiblePartnerCompany_enterpriseName[MAX_ENTERPRISE_NAME] = "";
static char originator_enterpriseName[MAX_ENTERPRISE_NAME] = "";

static char responsiblePartnerCompany_enterpriseCode[MAX_ENTERPRISE_CODE] = "";
static char originator_enterpriseCode[MAX_ENTERPRISE_CODE] = "";

static char techName_content[MAX_TECH_NAME] = "";
static char infoName_content[MAX_INFO_NAME] = "";
static xmlChar *info_name_variant = NULL;

static char dmtype[32] = "";

static char schema[PATH_MAX] = "";
static char brex_dmcode[PATH_MAX] = "";
static char *sns_fname = NULL;
static char *maint_sns = NULL;
static char issue_date[16] = "";
static xmlChar *issue_type = NULL;

static xmlChar *remarks = NULL;
static xmlChar *skill_level_code = NULL;

/* Omit the issue information from the object filename. */
static bool no_issue = false;
static bool no_issue_set = false;

static enum issue { NO_ISS, ISS_20, ISS_21, ISS_22, ISS_23, ISS_30, ISS_40, ISS_41, ISS_42, ISS_50 } issue = NO_ISS;

#define DEFAULT_S1000D_ISSUE ISS_50

#define ISS_22_DEFAULT_BREX "AE-A-04-10-0301-00A-022A-D"
#define ISS_23_DEFAULT_BREX "AE-A-04-10-0301-00A-022A-D"
#define ISS_30_DEFAULT_BREX "AE-A-04-10-0301-00A-022A-D"
#define ISS_40_DEFAULT_BREX "S1000D-A-04-10-0301-00A-022A-D"
#define ISS_41_DEFAULT_BREX "S1000D-E-04-10-0301-00A-022A-D"
#define ISS_42_DEFAULT_BREX "S1000D-F-04-10-0301-00A-022A-D"

/* ISO language and country codes if none can be determined. */
#define DEFAULT_LANGUAGE_ISO_CODE "und"
#define DEFAULT_COUNTRY_ISO_CODE "ZZ"

static char *template_dir = NULL;

/* Include the previous level of SNS title in a tech name. */
static int sns_title_levels = 0;

static bool no_info_name = false;

static char *act_dmcode = NULL;

#define BREX_INFOCODE_USE BAD_CAST "The information code used is not in the allowed set."

static enum issue get_issue(const char *iss)
{
	if (strcmp(iss, "5.0") == 0)
		return ISS_50;
	else if (strcmp(iss, "4.2") == 0)
		return ISS_42;
	else if (strcmp(iss, "4.1") == 0)
		return ISS_41;
	else if (strcmp(iss, "4.0") == 0)
		return ISS_40;
	else if (strcmp(iss, "3.0") == 0)
		return ISS_30;
	else if (strcmp(iss, "2.3") == 0)
		return ISS_23;
	else if (strcmp(iss, "2.2") == 0)
		return ISS_22;
	else if (strcmp(iss, "2.1") == 0)
		return ISS_21;
	else if (strcmp(iss, "2.0") == 0)
		return ISS_20;
	
	fprintf(stderr, ERR_PREFIX "Unsupported issue: %s\n", iss);
	exit(EXIT_BAD_ISSUE);

	return NO_ISS;
}

static const char *issue_name(enum issue iss)
{
	switch (iss) {
		case ISS_50: return "5.0";
		case ISS_42: return "4.2";
		case ISS_41: return "4.1";
		case ISS_40: return "4.0";
		case ISS_30: return "3.0";
		case ISS_23: return "2.3";
		case ISS_22: return "2.2";
		case ISS_21: return "2.1";
		case ISS_20: return "2.0";
		default: return "";
	}
}

static void prompt(const char *prompt, char *str, int n)
{
	if (strcmp(str, "") == 0) {
		printf("%s: ", prompt);

		while (!strchr(fgets(str, n, stdin), '\n')) {
			fprintf(stderr, ERR_PREFIX "Bad input for \"%s\".\n", prompt);
			while (getchar() != '\n');
			printf("%s: ", prompt);
		}

		*(strchr(str, '\n')) = '\0';
	} else {
		char temp[512] = "";

		printf("%s [%s]: ", prompt, str);

		while (!strchr(fgets(temp, n, stdin), '\n')) {
			fprintf(stderr, ERR_PREFIX "Bad input for \"%s\".\n", prompt);
			while (getchar() != '\n');
			printf("%s [%s]:  ", prompt, str);
		}

		*(strchr(temp, '\n')) = '\0';

		if (strcmp(temp, "") != 0) {
			memcpy(str, temp, n - 1);
		}
	}
}

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

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

	return NULL;
}

static void show_help(void)
{
	puts("Usage: " PROG_NAME " [options]");
	puts("");
	puts("Options:");
	puts("  -$, --issue <issue>               Specify which S1000D issue to use.");
	puts("  -@, --out <path>                  Output to specified file or directory.");
	puts("  -%, --templates <dir>             Use templates in specified directory.");
	puts("  -~, --dump-templates <dir>        Dump default templates to a directory.");
	puts("  -,, --dump-dmtypes-xml            Dump default dmtypes XML.");
	puts("  -., --dump-dmtypes                Dump default dmtypes text file.");
	puts("  -!, --no-infoname                 Do not include an info name.");
	puts("  -B, --generate-brex-rules         Generate BREX rules from .defaults file.");
	puts("  -D, --dmtypes <dmtypes>           Specify .dmtypes file name.");
	puts("  -d, --defaults <defaults>         Specify .defaults file name.");
	puts("  -f, --overwrite                   Overwrite existing file.");
	puts("  -j, --brexmap <map>               Use a custom .brexmap file.");
	puts("  -M, --maintained-sns <SNS>        Use one of the maintained SNS.");
	puts("  -N, --omit-issue                  Omit issue/inwork from filename.");
	puts("  -P, --sns-levels <levels>         Levels of SNS to include in tech name.");
	puts("  -p, --prompt                      Prompt the user for each value.");
	puts("  -q, --quiet                       Don't report an error if file exists.");
	puts("  -S, --sns <BREX>                  Get tech name from BREX SNS.");
	puts("  -v, --verbose                     Print file name of new data module.");
	puts("  --version                         Show version information.");
	puts("");
	puts("In addition, the following pieces of meta data can be set:");
	puts("  -#, --code <code>                 Data module code");
	puts("  -a, --act <ACT>                   ACT data module code");
	puts("  -b, --brex <BREX>                 BREX data module code");
	puts("  -C, --country <country>           Country ISO code");
	puts("  -c, --security <sec>              Security classification");
	puts("  -I, --date <date>                 Issue date");
	puts("  -i, --infoname <info>             Info name");
	puts("  -k, --skill <skill>               Skill level");
	puts("  -L, --language <lang>             Language ISO code");
	puts("  -m, --remarks <remarks>           Remarks");
	puts("  -n, --issno <iss>                 Issue number");
	puts("  -O, --origcode <CAGE>             Originator CAGE code.");
	puts("  -o, --origname <orig>             Originator enterprise name");
	puts("  -R, --rpccode <CAGE>              Responsible partner company CAGE code.");
	puts("  -r, --rpcname <RPC>               Responsible partner company enterprise name");
	puts("  -s, --schema <schema>             Schema");
	puts("  -T, --type <type>                 DM type (descript, proced, frontmatter, etc.)");
	puts("  -t, --techname <tech>             Tech name");
	puts("  -V, --infoname-variant <variant>  Info name variant");
	puts("  -w, --inwork <inwork>             Inwork issue");
	puts("  -z, --issue-type <type>           Issue type");
	LIBXML2_PARSE_LONGOPT_HELP
}

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

static void copy_default_value(const char *key, const char *val)
{
	if (strcmp(key, "modelIdentCode") == 0 && strcmp(modelIdentCode, "") == 0)
		strncpy(modelIdentCode, val, MAX_MODEL_IDENT_CODE - 2);
	else if (strcmp(key, "systemDiffCode") == 0 && strcmp(systemDiffCode, "") == 0)
		strncpy(systemDiffCode, val, MAX_SYSTEM_DIFF_CODE - 2);
	else if (strcmp(key, "systemCode") == 0 && strcmp(systemCode, "") == 0)
		strncpy(systemCode, val, MAX_SYSTEM_CODE - 2);
	else if (strcmp(key, "subSystemCode") == 0 && strcmp(subSystemCode, "") == 0)
		strncpy(subSystemCode, val, MAX_SUB_SYSTEM_CODE - 2);
	else if (strcmp(key, "subSubSystemCode") == 0 && strcmp(subSubSystemCode, "") == 0)
		strncpy(subSubSystemCode, val, MAX_SUB_SUB_SYSTEM_CODE - 2);
	else if (strcmp(key, "assyCode") == 0 && strcmp(assyCode, "") == 0)
		strncpy(assyCode, val, MAX_ASSY_CODE - 2);
	else if (strcmp(key, "disassyCode") == 0 && strcmp(disassyCode, "") == 0)
		strncpy(disassyCode, val, MAX_DISASSY_CODE - 2);
	else if (strcmp(key, "disassyCodeVariant") == 0 && strcmp(disassyCodeVariant, "") == 0)
		strncpy(disassyCodeVariant, val, MAX_DISASSY_CODE_VARIANT - 2);
	else if (strcmp(key, "infoCode") == 0 && strcmp(infoCode, "") == 0)
		strncpy(infoCode, val, MAX_INFO_CODE - 2);
	else if (strcmp(key, "infoCodeVariant") == 0 && strcmp(infoCodeVariant, "") == 0)
		strncpy(infoCodeVariant, val, MAX_INFO_CODE_VARIANT - 2);
	else if (strcmp(key, "itemLocationCode") == 0 && strcmp(itemLocationCode, "") == 0)
		strncpy(itemLocationCode, val, MAX_ITEM_LOCATION_CODE - 2);
	else if (strcmp(key, "learnCode") == 0 && strcmp(learnCode, "") == 0)
		strncpy(learnCode, val, MAX_LEARN_CODE - 2);
	else if (strcmp(key, "learnEventCode") == 0 && strcmp(learnEventCode, "") == 0)
		strncpy(learnEventCode, val, MAX_LEARN_EVENT_CODE - 2);
	else if (strcmp(key, "languageIsoCode") == 0 && strcmp(languageIsoCode, "") == 0)
		strncpy(languageIsoCode, val, MAX_LANGUAGE_ISO_CODE - 2);
	else if (strcmp(key, "countryIsoCode") == 0 && strcmp(countryIsoCode, "") == 0)
		strncpy(countryIsoCode, val, MAX_COUNTRY_ISO_CODE - 2);
	else if (strcmp(key, "issueNumber") == 0 && strcmp(issueNumber, "") == 0)
		strncpy(issueNumber, val, MAX_ISSUE_NUMBER - 2);
	else if (strcmp(key, "inWork") == 0 && strcmp(inWork, "") == 0)
		strncpy(inWork, val, MAX_IN_WORK - 2);
	else if (strcmp(key, "securityClassification") == 0 && strcmp(securityClassification, "") == 0)
		strncpy(securityClassification, val, MAX_SECURITY_CLASSIFICATION - 2);
	else if (strcmp(key, "responsiblePartnerCompany") == 0 && strcmp(responsiblePartnerCompany_enterpriseName, "") == 0)
		strncpy(responsiblePartnerCompany_enterpriseName, val, MAX_ENTERPRISE_NAME - 2);
	else if (strcmp(key, "responsiblePartnerCompanyCode") == 0 && strcmp(responsiblePartnerCompany_enterpriseCode, "") == 0)
		strncpy(responsiblePartnerCompany_enterpriseCode, val, MAX_ENTERPRISE_CODE - 2);
	else if (strcmp(key, "originator") == 0 && strcmp(originator_enterpriseName, "") == 0)
		strncpy(originator_enterpriseName, val, MAX_ENTERPRISE_NAME - 2);
	else if (strcmp(key, "originatorCode") == 0 && strcmp(originator_enterpriseCode, "") == 0)
		strncpy(originator_enterpriseCode, val, MAX_ENTERPRISE_CODE - 2);
	else if (strcmp(key, "techName") == 0 && strcmp(techName_content, "") == 0)
		strncpy(techName_content, val, MAX_TECH_NAME - 2);
	else if (strcmp(key, "infoName") == 0 && strcmp(infoName_content, "") == 0 && !no_info_name)
		strncpy(infoName_content, val, MAX_INFO_CODE - 2);
	else if (strcmp(key, "infoNameVariant") == 0 && !info_name_variant)
		info_name_variant = xmlStrdup(BAD_CAST val);
	else if (strcmp(key, "schema") == 0 && strcmp(schema, "") == 0)
		strncpy(schema, val, PATH_MAX - 1);
	else if (strcmp(key, "brex") == 0 && strcmp(brex_dmcode, "") == 0)
		strncpy(brex_dmcode, val, 255);
	else if (strcmp(key, "sns") == 0 && !sns_fname)
		sns_fname = strdup(val);
	else if (strcmp(key, "issue") == 0 && issue == NO_ISS)
		issue = get_issue(val);
	else if (strcmp(key, "remarks") == 0 && !remarks)
		remarks = xmlStrdup(BAD_CAST val);
	else if (strcmp(key, "templates") == 0 && !template_dir)
		template_dir = strdup(val);
	else if (strcmp(key, "maintainedSns") == 0 && !maint_sns)
		maint_sns = strdup(val);
	else if (strcmp(key, "snsLevels") == 0 && sns_title_levels == 0)
		sns_title_levels = atoi(val);
	else if (strcmp(key, "skillLevelCode") == 0 && !skill_level_code)
		skill_level_code = xmlStrdup(BAD_CAST val);
	else if (strcmp(key, "act") == 0 && !act_dmcode)
		act_dmcode = strdup(val);
	else if (strcmp(key, "issueType") == 0 && !issue_type)
		issue_type = xmlStrdup(BAD_CAST val);
}

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

	ctx = xmlXPathNewContext(doc ? doc : node->doc);
	ctx->node = node;

	obj = xmlXPathEvalExpression(BAD_CAST xpath, ctx);

	first = xmlXPathNodeSetIsEmpty(obj->nodesetval) ? NULL : obj->nodesetval->nodeTab[0];

	xmlXPathFreeObject(obj);
	xmlXPathFreeContext(ctx);

	return first;
}

static void set_dmcode(xmlNodePtr dmCode, const char *fname)
{
	int n, offset;
	char *path, *code;

	char modelIdentCode[MAX_MODEL_IDENT_CODE] = "";
	char systemDiffCode[MAX_SYSTEM_DIFF_CODE] = "";
	char systemCode[MAX_SYSTEM_CODE] = "";
	char subSystemCode[MAX_SUB_SYSTEM_CODE] = "";
	char subSubSystemCode[MAX_SUB_SUB_SYSTEM_CODE] = "";
	char assyCode[MAX_ASSY_CODE] = "";
	char disassyCode[MAX_DISASSY_CODE] = "";
	char disassyCodeVariant[MAX_DISASSY_CODE_VARIANT] = "";
	char infoCode[MAX_INFO_CODE] = "";
	char infoCodeVariant[MAX_INFO_CODE_VARIANT] = "";
	char itemLocationCode[MAX_ITEM_LOCATION_CODE] = "";
	char learnCode[MAX_LEARN_CODE] = "";
	char learnEventCode[MAX_LEARN_EVENT_CODE] = "";

	path = strdup(fname);
	code = basename(path);

	offset = strncmp(code, "DMC-", 4) == 0 ? 4 : 0;

	n = sscanf(code + offset, "%14[^-]-%4[^-]-%3[^-]-%c%c-%4[^-]-%2s%3[^-]-%3s%c-%c-%3s%1s",
		modelIdentCode,
		systemDiffCode,
		systemCode,
		subSystemCode,
		subSubSystemCode,
		assyCode,
		disassyCode,
		disassyCodeVariant,
		infoCode,
		infoCodeVariant,
		itemLocationCode,
		learnCode,
		learnEventCode);

	if (n != 11 && n != 13) {
		fprintf(stderr, ERR_PREFIX "Bad BREX data module code.\n");
		exit(EXIT_BAD_BREX_DMC);
	}

	xmlSetProp(dmCode, BAD_CAST "modelIdentCode", BAD_CAST modelIdentCode);
	xmlSetProp(dmCode, BAD_CAST "systemDiffCode", BAD_CAST systemDiffCode);
	xmlSetProp(dmCode, BAD_CAST "systemCode", BAD_CAST systemCode);
	xmlSetProp(dmCode, BAD_CAST "subSystemCode", BAD_CAST subSystemCode);
	xmlSetProp(dmCode, BAD_CAST "subSubSystemCode", BAD_CAST subSubSystemCode);
	xmlSetProp(dmCode, BAD_CAST "assyCode", BAD_CAST assyCode);
	xmlSetProp(dmCode, BAD_CAST "disassyCode", BAD_CAST disassyCode);
	xmlSetProp(dmCode, BAD_CAST "disassyCodeVariant", BAD_CAST disassyCodeVariant);
	xmlSetProp(dmCode, BAD_CAST "infoCode", BAD_CAST infoCode);
	xmlSetProp(dmCode, BAD_CAST "infoCodeVariant", BAD_CAST infoCodeVariant);
	xmlSetProp(dmCode, BAD_CAST "itemLocationCode", BAD_CAST itemLocationCode);

	if (strcmp(learnCode, "") != 0) xmlSetProp(dmCode, BAD_CAST "learnCode", BAD_CAST learnCode);
	if (strcmp(learnEventCode, "") != 0) xmlSetProp(dmCode, BAD_CAST "learnEventCode", BAD_CAST learnEventCode);

	free(path);
}

static void set_brex(xmlDocPtr doc, const char *fname)
{
	xmlNodePtr dmCode;
	dmCode = firstXPathNode(doc, NULL, "//brexDmRef/dmRef/dmRefIdent/dmCode");
	set_dmcode(dmCode, fname);
}

static void set_act(xmlDocPtr doc, const char *fname)
{
	xmlNodePtr dmCode;
	dmCode = firstXPathNode(doc, NULL, "//applicCrossRefTableRef/dmRef/dmRefIdent/dmCode");
	set_dmcode(dmCode, fname);
}

static void unset_act(xmlDocPtr doc)
{
	xmlNodePtr dmCode;
	dmCode = firstXPathNode(doc, NULL, "//applicCrossRefTableRef");
	xmlUnlinkNode(dmCode);
	xmlFreeNode(dmCode);
}

#define SNS_XPATH_1 "//snsSystem[snsCode='%s']/snsSubSystem[snsCode='%s']/snsSubSubSystem[snsCode='%s']/snsAssy[snsCode='%s']/snsTitle"
#define SNS_XPATH_2 "//snsSystem[snsCode='%s']/snsSubSystem[snsCode='%s']/snsSubSubSystem[snsCode='%s']/snsTitle"
#define SNS_XPATH_3 "//snsSystem[snsCode='%s']/snsSubSystem[snsCode='%s']/snsTitle"
#define SNS_XPATH_4 "//snsSystem[snsCode='%s']/snsTitle"

static void set_sns_title(xmlNodePtr snsTitle)
{
	xmlChar *title, *last;
	int n;
	xmlNodePtr cur;

	title = xmlNodeGetContent(snsTitle);
	last = xmlStrdup(title);

	strcpy(techName_content, "");

	for (n = sns_title_levels, cur = snsTitle; n > 1 && cur; --n, cur = cur->parent) {
		xmlNodePtr prev;

		if ((prev = firstXPathNode(NULL, cur, "parent::*/parent::*/snsTitle"))) {
			xmlChar *p;

			p = xmlNodeGetContent(prev);

			if (xmlStrcmp(p, last) != 0) {
				char *s;

				s = strdup(techName_content);
				strcpy(techName_content, (char *) p);
				strcat(techName_content, " - ");
				strcat(techName_content, s);

				free(s);
			}

			xmlFree(last);
			last = xmlStrdup(p);

			xmlFree(p);
		} else {
			break;
		}
	}

	strcat(techName_content, (char *) title);

	xmlFree(title);
	xmlFree(last);
}

/* Find the filename of the latest version of a BREX DM by its code. */
static bool find_brex_file(char *dst, const char *dir, const char *code)
{
	char s[PATH_MAX];

	if (strncmp(code, "DMC-", 4) == 0) {
		if (snprintf(s, PATH_MAX, "%s", code) < 0) {
			fprintf(stderr, E_ENCODING_ERROR);
			exit(EXIT_ENCODING_ERROR);
		}
	} else {
		if (snprintf(s, PATH_MAX, "DMC-%s", code) < 0) {
			fprintf(stderr, E_ENCODING_ERROR);
			exit(EXIT_ENCODING_ERROR);
		}
	}

	return find_csdb_object(dst, dir, s, NULL, true);
}

struct inmem_xml {
	unsigned char *xml;
	unsigned int len;
};

/* Map maintained SNS title to XML template. */
static struct inmem_xml maint_sns_xml(void)
{
	struct inmem_xml res;

	if (strcasecmp(maint_sns, "Generic") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0100_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0100_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "Support and training equipment") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0200_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0200_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "Ordnance") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0300_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0300_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "General communications") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0400_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0400_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "Air vehicle, engines and equipment") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0500_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0500_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "Tactical missiles") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0600_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0600_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "General surface vehicles") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0700_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0700_00A_022A_D_EN_CA_XML_len;
	} else if (strcasecmp(maint_sns, "General sea vehicles") == 0) {
		res.xml = sns_DMC_S1000D_A_08_02_0800_00A_022A_D_EN_CA_XML;
		res.len = sns_DMC_S1000D_A_08_02_0800_00A_022A_D_EN_CA_XML_len;
	} else {
		fprintf(stderr, ERR_PREFIX "No maintained SNS: %s\n", maint_sns);
		res.xml = NULL;
		res.len = 0;
	}

	return res;
}


static xmlDocPtr maint_sns_doc(void)
{
	struct inmem_xml xml;

	xml = maint_sns_xml();

	return read_xml_mem((const char *) xml.xml, xml.len);
}

static xmlDocPtr set_tech_from_sns(const char *dir)
{
	xmlDocPtr brex = NULL;
	char xpath[256];
	xmlNodePtr snsTitle;
	char fname[PATH_MAX];

	if (maint_sns) {
		brex = maint_sns_doc();
	} else if (sns_fname && find_brex_file(fname, dir, sns_fname)) {
		brex = read_xml_doc(fname);
	} else if (strcmp(brex_dmcode, "") != 0 && find_brex_file(fname, dir, brex_dmcode)) {
		brex = read_xml_doc(fname);
	}

	if (!brex) {
		return NULL;
	}

	sprintf(xpath, SNS_XPATH_1, systemCode, subSystemCode, subSubSystemCode, assyCode);
	if ((snsTitle = firstXPathNode(brex, NULL, xpath))) {
		set_sns_title(snsTitle);
		return brex;
	}

	sprintf(xpath, SNS_XPATH_2, systemCode, subSystemCode, subSubSystemCode);
	if ((snsTitle = firstXPathNode(brex, NULL, xpath))) {
		set_sns_title(snsTitle);
		return brex;
	}

	sprintf(xpath, SNS_XPATH_3, systemCode, subSystemCode);
	if ((snsTitle = firstXPathNode(brex, NULL, xpath))) {
		set_sns_title(snsTitle);
		return brex;
	}

	sprintf(xpath, SNS_XPATH_4, systemCode);
	if ((snsTitle = firstXPathNode(brex, NULL, xpath))) {
		set_sns_title(snsTitle);
		return brex;
	}

	return brex;
}

static void set_issue_date(xmlNodePtr issueDate)
{
	char year_s[5], month_s[3], day_s[3];

	if (strcmp(issue_date, "") == 0) {
		time_t now;
		struct tm *local;
		unsigned short year, month, day;

		time(&now);
		local = localtime(&now);

		year = local->tm_year + 1900;
		month = local->tm_mon + 1;
		day = local->tm_mday;

		if (snprintf(day_s, 3, "%.2u", day) < 0 ||
		    snprintf(month_s, 3, "%.2u", month) < 0 ||
		    snprintf(year_s, 5, "%u", year) < 0)
			exit(EXIT_BAD_DATE);
	} else {
		if (sscanf(issue_date, "%4s-%2s-%2s", year_s, month_s, day_s) != 3) {
			fprintf(stderr, ERR_PREFIX "Bad issue date: %s\n", issue_date);
			exit(EXIT_BAD_DATE);
		}
	}

	xmlSetProp(issueDate, BAD_CAST "year", BAD_CAST year_s);
	xmlSetProp(issueDate, BAD_CAST "month", BAD_CAST month_s);
	xmlSetProp(issueDate, BAD_CAST "day", BAD_CAST day_s);
}

static xmlDocPtr xml_skeleton(const char *dmtype, enum issue iss)
{
	unsigned char *xml = NULL;
	unsigned int len;

	if (strcmp(dmtype, "") == 0) {
		if (strcmp(learnCode, "") == 0) {
			fprintf(stderr, E_NO_SCHEMA, infoCode, infoCodeVariant,
				itemLocationCode);
		} else {
			fprintf(stderr, E_NO_SCHEMA_LEARN, infoCode, infoCodeVariant,
				itemLocationCode, learnCode, learnEventCode);
		}
		exit(EXIT_UNKNOWN_DMTYPE);
	} else if (template_dir) {
		char src[PATH_MAX];
		sprintf(src, "%s/%s.xml", template_dir, dmtype);

		if (access(src, F_OK) == -1) {
			fprintf(stderr, ERR_PREFIX "No schema %s in template directory \"%s\".\n", dmtype, template_dir);
			exit(EXIT_UNKNOWN_DMTYPE);
		}

		return read_xml_doc(src);
	} else if (strcmp(dmtype, "descript") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_descript_xml;
				len = templates_descript_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "proced") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_proced_xml;
				len = templates_proced_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "frontmatter") == 0) {
		switch (iss) {
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_frontmatter_xml;
				len = templates_frontmatter_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "brex") == 0) {
		switch (iss) {
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				if (maint_sns) {
					struct inmem_xml res;
					res = maint_sns_xml();
					xml = res.xml;
					len = res.len;
				} else {
					xml = templates_brex_xml;
					len = templates_brex_xml_len;
				}
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "brdoc") == 0) {
		switch (iss) {
			case ISS_42:
			case ISS_50:
				xml = templates_brdoc_xml;
				len = templates_brdoc_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "appliccrossreftable") == 0) {
		switch (iss) {
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_appliccrossreftable_xml;
				len = templates_appliccrossreftable_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "prdcrossreftable") == 0) {
		switch (iss) {
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_prdcrossreftable_xml;
				len = templates_prdcrossreftable_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "condcrossreftable") == 0) {
		switch (iss) {
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_condcrossreftable_xml;
				len = templates_condcrossreftable_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "comrep") == 0) {
		switch (iss) {
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_comrep_xml;
				len = templates_comrep_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "process") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_process_xml;
				len = templates_process_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "ipd") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_ipd_xml;
				len = templates_ipd_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "fault") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_fault_xml;
				len = templates_fault_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "checklist") == 0) {
		switch (iss) {
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_checklist_xml;
				len = templates_checklist_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "learning") == 0) {
		switch (iss) {
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_learning_xml;
				len = templates_learning_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "container") == 0) {
		switch (iss) {
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_container_xml;
				len = templates_container_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "crew") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_crew_xml;
				len = templates_crew_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "sb") == 0) {
		switch (iss) {
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_sb_xml;
				len = templates_sb_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "schedul") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_schedul_xml;
				len = templates_schedul_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "wrngdata") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_wrngdata_xml;
				len = templates_wrngdata_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "wrngflds") == 0) {
		switch (iss) {
			case ISS_20:
			case ISS_21:
			case ISS_22:
			case ISS_23:
			case ISS_30:
			case ISS_40:
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_wrngflds_xml;
				len = templates_wrngflds_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "scocontent") == 0) {
		switch (iss) {
			case ISS_41:
			case ISS_42:
			case ISS_50:
				xml = templates_scocontent_xml;
				len = templates_scocontent_xml_len;
				break;
			default:
				break;
		}
	} else if (strcmp(dmtype, "techrep") == 0) {
		switch (iss) {
			case ISS_23:
			case ISS_30:
			case ISS_40:
				xml = templates_techrep_xml;
				len = templates_techrep_xml_len;
				break;
			default:
				break;
		}
	} else {
		fprintf(stderr, ERR_PREFIX "Unknown schema %s\n", dmtype);
		exit(EXIT_UNKNOWN_DMTYPE);
	}

	if (!xml) {
		fprintf(stderr, ERR_PREFIX "No schema %s for issue %s\n", dmtype, issue_name(iss));
		exit(EXIT_UNKNOWN_DMTYPE);
	}

	return read_xml_mem((const char *) xml, len);
}

static xmlDocPtr toissue(xmlDocPtr doc, enum issue iss)
{
	xsltStylesheetPtr style;
	xmlDocPtr styledoc, res, orig;
	xmlNodePtr old;
	unsigned char *xml = NULL;
	unsigned int len;

	switch (iss) {
		case ISS_42:
			xml = ___common_to42_xsl;
			len = ___common_to42_xsl_len;
			break;
		case ISS_41:
			xml = ___common_to41_xsl;
			len = ___common_to41_xsl_len;
			break;
		case ISS_40:
			xml = ___common_to40_xsl;
			len = ___common_to40_xsl_len;
			break;
		case ISS_30:
			xml = ___common_to30_xsl;
			len = ___common_to30_xsl_len;
			break;
		case ISS_23:
			xml = ___common_to23_xsl;
			len = ___common_to23_xsl_len;
			break;
		case ISS_22:
			xml = ___common_to22_xsl;
			len = ___common_to22_xsl_len;
			break;
		case ISS_21:
			xml = ___common_to21_xsl;
			len = ___common_to21_xsl_len;
			break;
		case ISS_20:
			xml = ___common_to20_xsl;
			len = ___common_to20_xsl_len;
			break;
		default:
			return NULL;
	}

	orig = xmlCopyDoc(doc, 1);
			
	styledoc = read_xml_mem((const char *) xml, len);
	style = xsltParseStylesheetDoc(styledoc);

	res = xsltApplyStylesheet(style, doc, NULL);

	xmlFreeDoc(doc);
	xsltFreeStylesheet(style);

	old = xmlDocSetRootElement(orig, xmlCopyNode(xmlDocGetRootElement(res), 1));

	xmlFreeNode(old);
	xmlFreeDoc(res);

	return orig;
}

static void add_dmtypes_brex_val(xmlNodePtr rules, const char *key, const char *val)
{
	xmlNodePtr objval;
	xmlChar *name;
	objval = xmlNewChild(rules->children, NULL, BAD_CAST "objectValue", NULL);
	xmlSetProp(objval, BAD_CAST "valueAllowed", BAD_CAST key);
	name = xmlEncodeEntitiesReentrant(NULL, BAD_CAST val);
	xmlNodeSetContent(objval, name);
	xmlFree(name);
}

static void process_dmtypes_xml(xmlDocPtr defaults_xml, xmlNodePtr brex_rules)
{
	xmlNodePtr cur;

	for (cur = xmlDocGetRootElement(defaults_xml)->children; cur; cur = cur->next) {
		char *def_key, *def_val, *infname;
		xmlChar *infnamev;
		char code[4], variant[2], itemloc[2], learn[4], levent[2];
		int p;

		if (cur->type != XML_ELEMENT_NODE) continue;
		if (!xmlHasProp(cur, BAD_CAST "infoCode")) continue;
		if (!xmlHasProp(cur, BAD_CAST "schema")) continue;

		def_key  = (char *) xmlGetProp(cur, BAD_CAST "infoCode");
		def_val  = (char *) xmlGetProp(cur, BAD_CAST "schema");
		infname  = (char *) xmlGetProp(cur, BAD_CAST "infoName");
		infnamev = xmlGetProp(cur, BAD_CAST "infoNameVariant");

		p = sscanf(def_key, "%3s%1s-%1s-%3s%1s", code, variant, itemloc, learn, levent);

		/* Get schema */
		if (strcmp(dmtype, "") == 0 &&
		    strcmp(code, infoCode) == 0 &&
		    (p < 2 || strcmp(variant, "*") == 0 || strcmp(variant, infoCodeVariant) == 0) &&
		    (p < 3 || strcmp(itemloc, "*") == 0 || strcmp(itemloc, itemLocationCode) == 0) &&
		    (p < 4 || strcmp(learn, "***") == 0   || strcmp(learn, learnCode) == 0) &&
		    (p < 5 || strcmp(levent, "*") == 0  || strcmp(levent, learnEventCode) == 0)) {
			strcpy(dmtype, def_val);
		}

		/* Get info name */
		if (infname &&
		    strcmp(infoName_content, "") == 0 &&
		    !no_info_name &&
		    strcmp(code, infoCode) == 0 &&
		    (p < 2 || strcmp(variant, "*") == 0 || strcmp(variant, infoCodeVariant) == 0) &&
		    (p < 3 || strcmp(itemloc, "*") == 0 || strcmp(itemloc, itemLocationCode) == 0) &&
		    (p < 4 || strcmp(learn, "***") == 0   || strcmp(learn, learnCode) == 0) &&
		    (p < 5 || strcmp(levent, "*") == 0  || strcmp(levent, learnEventCode) == 0)) {
			strcpy(infoName_content, infname);

			if (infnamev && !info_name_variant) {
				info_name_variant = xmlStrdup(infnamev);
			}
		}

		if (brex_rules) {
			add_dmtypes_brex_val(brex_rules, def_key, infname);
		}

		xmlFree(def_key);
		xmlFree(def_val);
		xmlFree(infname);
		xmlFree(infnamev);
	}
}

static void set_remarks(xmlDocPtr doc, xmlChar *text)
{
	xmlNodePtr remarks;
	
	remarks = firstXPathNode(doc, NULL, "//remarks");

	if (text) {
		xmlNodePtr simplePara;
		simplePara = xmlNewChild(remarks, NULL, BAD_CAST "simplePara", NULL);
		xmlNodeSetContent(simplePara, text);
	} else {
		xmlUnlinkNode(remarks);
		xmlFreeNode(remarks);
	}
}

static void set_skill_level(xmlDocPtr doc, xmlChar *code)
{
	xmlNodePtr skill_level;

	skill_level = firstXPathNode(doc, NULL, "//skillLevel");

	if (code) {
		xmlSetProp(skill_level, BAD_CAST "skillLevelCode", code);
	} else {
		xmlUnlinkNode(skill_level);
		xmlFreeNode(skill_level);
	}
}

/* Dump the built-in dmtypes XML or text */
static void print_dmtypes(void)
{
	printf("%.*s", dmtypes_xml_len, dmtypes_xml);
}
static void print_dmtypes_txt(void)
{
	printf("%.*s", dmtypes_txt_len, dmtypes_txt);
}

/* Try reading the ISO language and country codes from the environment,
 * otherwise default to "und" (undetermined) for language and ZZ for
 * country.
 */
static void set_env_lang(void)
{
	char *env, *lang, *lang_l, *lang_c;

	if (!(env = getenv("LANG"))) {
		if (strcmp(languageIsoCode, "") == 0) {
			strcpy(languageIsoCode, DEFAULT_LANGUAGE_ISO_CODE);
		}
		if (strcmp(countryIsoCode, "") == 0) {
			strcpy(countryIsoCode, DEFAULT_COUNTRY_ISO_CODE);
		}
		return;
	}

	lang = strdup(env);
	lang_l = strtok(lang, "_");
	lang_c = strtok(NULL, ".");

	if (strcmp(languageIsoCode, "") == 0) {
		if (lang_l) {
			strncpy(languageIsoCode, lang_l, 3);
		} else {
			strcpy(languageIsoCode, DEFAULT_LANGUAGE_ISO_CODE);
		}
	}
	if (strcmp(countryIsoCode, "") == 0) {
		if (lang_c) {
			strncpy(countryIsoCode, lang_c, 2);
		} else {
			strcpy(countryIsoCode, DEFAULT_COUNTRY_ISO_CODE);
		}
	}

	free(lang);
}

static void add_brex_rule(xmlNodePtr rules, xmlDocPtr brexmap, const char *key, const char *val)
{
	xmlXPathContextPtr ctx;
	xmlXPathObjectPtr obj;

	ctx = xmlXPathNewContext(brexmap);
	xmlXPathRegisterVariable(ctx, BAD_CAST "key", xmlXPathNewString(BAD_CAST key));
	obj = xmlXPathEval(BAD_CAST "//default[@ident=$key]", ctx);

	if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
		xmlChar *id, *path, use[256];
		xmlNodePtr rule, objpath, objval;

		id   = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "id");
		path = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "path");

		xmlStrPrintf(use, 256, "%s must be %s", key, val);

		rule = xmlNewChild(rules, NULL, BAD_CAST "structureObjectRule", NULL);
		if (id) {
			xmlSetProp(rule, BAD_CAST "id", id);
		}
		objpath = xmlNewChild(rule, NULL, BAD_CAST "objectPath", BAD_CAST path);
		xmlNewChild(rule, NULL, BAD_CAST "objectUse", use);
		objval = xmlNewChild(rule, NULL, BAD_CAST "objectValue", NULL);

		xmlSetProp(objpath, BAD_CAST "allowedObjectFlag", BAD_CAST "2");
		xmlSetProp(objval, BAD_CAST "valueAllowed", BAD_CAST val);

		xmlFree(id);
		xmlFree(path);
	}

	xmlXPathFreeObject(obj);
	xmlXPathFreeContext(ctx);
}

static xmlDocPtr read_default_brexmap(void)
{
	char fname[PATH_MAX];

	if (find_config(fname, DEFAULT_BREXMAP_FNAME)) {
		return read_xml_doc(fname);
	} else {
		return read_xml_mem((const char *) ___common_brexmap_xml, ___common_brexmap_xml_len);
	}
}

static void dump_templ(const char *fname, const unsigned char *xml, const unsigned int len)
{
	FILE *f;
	f = fopen(fname, "w");
	fprintf(f, "%.*s", len, xml);
	fclose(f);
}

static void dump_templates(const char *path)
{
	if (access(path, W_OK) == -1 || chdir(path)) {
		fprintf(stderr, E_BAD_TEMPL_DIR, path);
		exit(EXIT_BAD_TEMPL_DIR);
	}

	dump_templ("appliccrossreftable.xml",
		templates_appliccrossreftable_xml,
		templates_appliccrossreftable_xml_len);
	dump_templ("brdoc.xml",
		templates_brdoc_xml,
		templates_brdoc_xml_len);
	dump_templ("brex.xml",
		templates_brex_xml,
		templates_brex_xml_len);
	dump_templ("checklist.xml",
		templates_checklist_xml,
		templates_checklist_xml_len);
	dump_templ("comrep.xml",
		templates_comrep_xml,
		templates_comrep_xml_len);
	dump_templ("condcrossreftable.xml",
		templates_condcrossreftable_xml,
		templates_condcrossreftable_xml_len);
	dump_templ("container.xml",
		templates_container_xml,
		templates_container_xml_len);
	dump_templ("crew.xml",
		templates_crew_xml,
		templates_crew_xml_len);
	dump_templ("descript.xml",
		templates_descript_xml,
		templates_descript_xml_len);
	dump_templ("fault.xml",
		templates_fault_xml,
		templates_fault_xml_len);
	dump_templ("frontmatter.xml",
		templates_frontmatter_xml,
		templates_frontmatter_xml_len);
	dump_templ("ipd.xml",
		templates_ipd_xml,
		templates_ipd_xml_len);
	dump_templ("learning.xml",
		templates_learning_xml,
		templates_learning_xml_len);
	dump_templ("prdcrossreftable.xml",
		templates_prdcrossreftable_xml,
		templates_prdcrossreftable_xml_len);
	dump_templ("proced.xml",
		templates_proced_xml,
		templates_proced_xml_len);
	dump_templ("process.xml",
		templates_process_xml,
		templates_process_xml_len);
	dump_templ("sb.xml",
		templates_sb_xml,
		templates_sb_xml_len);
	dump_templ("schedul.xml",
		templates_schedul_xml,
		templates_schedul_xml_len);
	dump_templ("scocontent.xml",
		templates_scocontent_xml,
		templates_scocontent_xml_len);
	dump_templ("techrep.xml",
		templates_techrep_xml,
		templates_techrep_xml_len);
	dump_templ("wrngdata.xml",
		templates_wrngdata_xml,
		templates_wrngdata_xml_len);
	dump_templ("wrngflds.xml",
		templates_wrngflds_xml,
		templates_wrngflds_xml_len);
}

/* Generate a random code. */
static void random_code(char *dst, size_t n, const char *modelid)
{
	static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVXWYZ0123456789";

	if (strcmp(modelid, "") != 0) {
		snprintf(dst, n, "%s-%c%c%c%c-%c%c-%c%c-%c%c%c%c-%c%c%c%c%c-000A-D",
			modelid,
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)]);
	} else {
		snprintf(dst, n, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c-%c%c%c%c-%c%c-%c%c-%c%c%c%c-%c%c%c%c%c-000A-D",
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)],
			alphanum[rand() % (sizeof(alphanum) - 1)]);
	}
}

int main(int argc, char **argv)
{
	char learn[8] = "";
	char iss[16] = "";

	xmlDocPtr dm;
	xmlNode *dmodule;
	xmlNode *identAndStatusSection;
	xmlNode *dmAddress;
	xmlNode *dmIdent;
	xmlNode *dmCode;
	xmlNode *language;
	xmlNode *issueInfo;
	xmlNode *dmAddressItems;
	xmlNode *issueDate;
	xmlNode *dmStatus;
	xmlNode *security;
	xmlNode *dmTitle;
	xmlNode *techName;
	xmlNode *infoName;
	xmlNode *infoNameVariant;
	xmlNode *responsiblePartnerCompany;
	xmlNode *originator;

	FILE *defaults;

	char defaults_fname[PATH_MAX];
	char dmtypes_fname[PATH_MAX];
	bool custom_defaults = false;
	bool custom_dmtypes = false;

	int i;
	int c;

	bool showprompts = false;
	char dmcode[256] = "";
	bool skipdmc = false;
	bool verbose = false;
	bool overwrite = false;
	char *out = NULL;
	bool tech_name_flag = false;
	bool no_overwrite_error = false;

	xmlDocPtr defaults_xml;
	xmlNodePtr brex_rules = NULL;
	xmlDocPtr brexmap = NULL;

	char *defaults_dir_str;
	char *defaults_dir;

	char *outdir = NULL;

	const char *sopts = "a:pd:D:L:C:n:w:c:r:R:o:O:t:i:T:#:Ns:Bb:S:I:v$:@:fm:,.%:qM:P:!k:j:~:z:V:h?";
	struct option lopts[] = {
		{"version"               , no_argument      , 0, 0},
		{"help"                  , no_argument      , 0, 'h'},
		{"act"                   , required_argument, 0, 'a'},
		{"prompt"                , no_argument      , 0, 'p'},
		{"defaults"              , required_argument, 0, 'd'},
		{"dmtypes"               , required_argument, 0, 'D'},
		{"language"              , required_argument, 0, 'L'},
		{"country"               , required_argument, 0, 'C'},
		{"issno"                 , required_argument, 0, 'n'},
		{"inwork"                , required_argument, 0, 'w'},
		{"security"              , required_argument, 0, 'c'},
		{"rpcname"               , required_argument, 0, 'r'},
		{"rpccode"               , required_argument, 0, 'R'},
		{"orgname"               , required_argument, 0, 'o'},
		{"orgcode"               , required_argument, 0, 'O'},
		{"techname"              , required_argument, 0, 't'},
		{"infoname"              , required_argument, 0, 'i'},
		{"type"                  , required_argument, 0, 'T'},
		{"code"                  , required_argument, 0, '#'},
		{"omit-issue"            , no_argument      , 0, 'N'},
		{"schema"                , required_argument, 0, 's'},
		{"generate-brex-rules"   , no_argument      , 0, 'B'},
		{"brex"                  , required_argument, 0, 'b'},
		{"sns"                   , required_argument, 0, 'S'},
		{"date"                  , required_argument, 0, 'I'},
		{"verbose"               , no_argument      , 0, 'v'},
		{"overwrite"             , no_argument      , 0, 'f'},
		{"issue"                 , required_argument, 0, '$'},
		{"out"                   , required_argument, 0, '@'},
		{"remarks"               , required_argument, 0, 'm'},
		{"dump-dmtypes"          , no_argument      , 0, '.'},
		{"dump-dmtypes-xml"      , no_argument      , 0, ','},
		{"templates"             , required_argument, 0, '%'},
		{"quiet"                 , no_argument      , 0, 'q'},
		{"maintained-sns"        , required_argument, 0, 'M'},
		{"sns-levels"            , required_argument, 0, 'P'},
		{"no-infoname"           , no_argument      , 0, '!'},
		{"skill"                 , required_argument, 0, 'k'},
		{"brexmap"               , required_argument, 0, 'j'},
		{"dump-templates"        , required_argument, 0, '~'},
		{"issue-type"            , required_argument, 0, 'z'},
		{"infoname-variant"      , required_argument, 0, 'V'},
		LIBXML2_PARSE_LONGOPT_DEFS
		{0, 0, 0, 0}
	};
	int loptind = 0;

	srand(time(NULL) + getpid());

	while ((c = getopt_long(argc, argv, sopts, lopts, &loptind)) != -1) {
		switch (c) {
			case 0:
				if (strcmp(lopts[loptind].name, "version") == 0) {
					show_version();
					return 0;
				}
				LIBXML2_PARSE_LONGOPT_HANDLE(lopts, loptind, optarg)
				break;
			case 'a': act_dmcode = strdup(optarg); break;
			case 'p': showprompts = true; break;
			case 'd': strcpy(defaults_fname, optarg); custom_defaults = true; break;
			case 'D': strcpy(dmtypes_fname, optarg); custom_dmtypes = true; break;
			case 'L': strcpy(languageIsoCode, optarg); break;
			case 'C': strcpy(countryIsoCode, optarg); break;
			case 'n': strcpy(issueNumber, optarg); break;
			case 'w': strcpy(inWork, optarg); break;
			case 'c': strcpy(securityClassification, optarg); break;
			case 'r': strcpy(responsiblePartnerCompany_enterpriseName, optarg); break;
			case 'R': strcpy(responsiblePartnerCompany_enterpriseCode, optarg); break;
			case 'o': strcpy(originator_enterpriseName, optarg); break;
			case 'O': strcpy(originator_enterpriseCode, optarg); break;
			case 't': strcpy(techName_content, optarg); tech_name_flag = true; break;
			case 'i': strcpy(infoName_content, optarg); break;
			case 'T': strcpy(dmtype, optarg); break;
			case '#': if (strchr(optarg, '-')) {
					  strncpy(dmcode, optarg, 255);
				  } else {
					  random_code(dmcode, 256, optarg);
				  }
				  skipdmc = true;
				  break;
			case 'N': no_issue = true; no_issue_set = true; break;
			case 's': strcpy(schema, optarg); break;
			case 'B': if (!brex_rules) brex_rules = xmlNewNode(NULL, BAD_CAST "structureObjectRuleGroup"); break;
			case 'b': strcpy(brex_dmcode, optarg); break;
			case 'S': sns_fname = strdup(optarg); break;
			case 'I': strcpy(issue_date, optarg); break;
			case 'V': info_name_variant = xmlStrdup(BAD_CAST optarg); break;
			case 'v': verbose = true; break;
			case 'f': overwrite = true; break;
			case '$': issue = get_issue(optarg); break;
			case '@': out = strdup(optarg); break;
			case 'm': remarks = xmlStrdup(BAD_CAST optarg); break;
			case ',': print_dmtypes(); return 0;
			case '.': print_dmtypes_txt(); return 0;
			case '%': template_dir = strdup(optarg); break;
			case 'q': no_overwrite_error = true; break;
			case 'M': maint_sns = strdup(optarg); break;
			case 'P': sns_title_levels = atoi(optarg); break;
			case '!': no_info_name = true; break;
			case 'k': skill_level_code = xmlStrdup(BAD_CAST optarg); break;
			case 'j': if (!brexmap) brexmap = read_xml_doc(optarg); break;
			case '~': dump_templates(optarg); return 0;
			case 'z': issue_type = xmlStrdup(BAD_CAST optarg); break;
			case 'h':
			case '?': show_help(); return 0;
		}
	}

	if (!custom_defaults) {
		find_config(defaults_fname, DEFAULT_DEFAULTS_FNAME);
	}
	if (!custom_dmtypes) {
		find_config(dmtypes_fname, DEFAULT_DMTYPES_FNAME);
	}

	defaults_dir_str = strdup(defaults_fname);
	defaults_dir = dirname(defaults_dir_str);

	if (!brexmap) {
		brexmap = read_default_brexmap();
	}

	if (brex_rules) {
		xmlNodePtr dmtypes_brex_rule, objpath, def;
		xmlChar *id, *path;

		def  = xpath_first_node(brexmap, NULL, BAD_CAST "//dmtypes");
		id   = xmlGetProp(def, BAD_CAST "id");
		path = xmlGetProp(def, BAD_CAST "path");

		dmtypes_brex_rule = xmlNewChild(brex_rules, NULL, BAD_CAST "structureObjectRule", NULL);

		if (id) {
			xmlSetProp(dmtypes_brex_rule, BAD_CAST "id", id);
		}

		objpath = xmlNewChild(dmtypes_brex_rule, NULL, BAD_CAST "objectPath", path);
		xmlSetProp(objpath, BAD_CAST "allowedObjectFlag", BAD_CAST "2");

		xmlNewChild(dmtypes_brex_rule, NULL, BAD_CAST "objectUse", BREX_INFOCODE_USE);

		xmlFree(id);
		xmlFree(path);
	}

	if ((defaults_xml = read_xml_doc(defaults_fname))) {
		xmlNodePtr cur;

		for (cur = xmlDocGetRootElement(defaults_xml)->children; cur; cur = cur->next) {
			char *def_key, *def_val;

			if (cur->type != XML_ELEMENT_NODE) continue;
			if (!xmlHasProp(cur, BAD_CAST "ident")) continue;
			if (!xmlHasProp(cur, BAD_CAST "value")) continue;

			def_key = (char *) xmlGetProp(cur, BAD_CAST "ident");
			def_val = (char *) xmlGetProp(cur, BAD_CAST "value");

			copy_default_value(def_key, def_val);

			if (brex_rules) {
				add_brex_rule(brex_rules, brexmap, def_key, def_val);
			}

			xmlFree(def_key);
			xmlFree(def_val);
		}

		xmlFreeDoc(defaults_xml);
	} else if ((defaults = fopen(defaults_fname, "r"))) {
		char default_line[1024];

		while (fgets(default_line, 1024, defaults)) {
			char def_key[32], def_val[256];

			if (sscanf(default_line, "%31s %255[^\n]", def_key, def_val) != 2)
				continue;

			copy_default_value(def_key, def_val);

			if (brex_rules) {
				add_brex_rule(brex_rules, brexmap, def_key, def_val);
			}
		}

		fclose(defaults);
	}

	if (strcmp(dmcode, "-") == 0) {
		random_code(dmcode, 256, modelIdentCode);
	}

	if (strcmp(dmcode, "") != 0) {
		int n, offset;

		offset = strncmp(dmcode, "DMC-", 4) == 0 ? 4 : 0;

		n = sscanf(dmcode + offset, "%14[^-]-%4[^-]-%3[^-]-%c%c-%4[^-]-%2s%3[^-]-%3s%c-%c-%3s%1s",
			modelIdentCode,
			systemDiffCode,
			systemCode,
			subSystemCode,
			subSubSystemCode,
			assyCode,
			disassyCode,
			disassyCodeVariant,
			infoCode,
			infoCodeVariant,
			itemLocationCode,
			learnCode,
			learnEventCode);

		if (n != 11 && n != 13) {
			fprintf(stderr, ERR_PREFIX "Bad data module code: %s\n", dmcode);
			exit(EXIT_BAD_DMC);
		}
	}

	if (strcmp(dmtype, "") == 0 || (strcmp(infoName_content, "") == 0 && !no_info_name)) {
		if ((defaults_xml = read_xml_doc(dmtypes_fname))) {
			process_dmtypes_xml(defaults_xml, brex_rules);
			xmlFreeDoc(defaults_xml);
		} else if ((defaults = fopen(dmtypes_fname, "r"))) {
			char default_line[1024];

			while (fgets(default_line, 1024, defaults)) {
				char def_key[32], def_val[256], infname[256];
				int n;
				char code[4], variant[2], itemloc[2], learn[4], levent[2];
				int p;

				n = sscanf(default_line, "%31s %255s %255[^\n]", def_key, def_val, infname);

				if (n < 2)
					continue;

				p = sscanf(def_key, "%3s%1s-%1s-%3s%1s", code, variant, itemloc, learn, levent);

				/* Get schema */
				if (strcmp(dmtype, "") == 0 &&
				    strcmp(code, infoCode) == 0 &&
				    (p < 2 || strcmp(variant, "*") == 0 || strcmp(variant, infoCodeVariant) == 0) &&
				    (p < 3 || strcmp(itemloc, "*") == 0 || strcmp(itemloc, itemLocationCode) == 0) &&
				    (p < 4 || strcmp(learn, "***") == 0   || strcmp(learn, learnCode) == 0) &&
				    (p < 5 || strcmp(levent, "*") == 0  || strcmp(levent, learnEventCode) == 0)) {
					strcpy(dmtype, def_val);
				}

				/* Get info name */
				if (n == 3 &&
				    strcmp(infoName_content, "") == 0 &&
				    !no_info_name &&
				    strcmp(code, infoCode) == 0 &&
				    (p < 2 || strcmp(variant, "*") == 0 || strcmp(variant, infoCodeVariant) == 0) &&
				    (p < 3 || strcmp(itemloc, "*") == 0 || strcmp(itemloc, itemLocationCode) == 0) &&
				    (p < 4 || strcmp(learn, "***") == 0   || strcmp(learn, learnCode) == 0) &&
				    (p < 5 || strcmp(levent, "*") == 0  || strcmp(levent, learnEventCode) == 0)) {
					strcpy(infoName_content, infname);
				}

				if (brex_rules) {
					add_dmtypes_brex_val(brex_rules, def_key, n == 3 ? infname : NULL);
				}
			}

			fclose(defaults);
		} else {
			defaults_xml = read_xml_mem((const char *) dmtypes_xml, dmtypes_xml_len);
			process_dmtypes_xml(defaults_xml, brex_rules);
			xmlFreeDoc(defaults_xml);
		}
	}

	if (showprompts) {
		if (!skipdmc) {
			prompt("Model identification code", modelIdentCode, MAX_MODEL_IDENT_CODE);
			prompt("System difference code", systemDiffCode, MAX_SYSTEM_DIFF_CODE);
			prompt("System code", systemCode, MAX_SYSTEM_CODE);
			prompt("Sub-system code", subSystemCode, MAX_SUB_SYSTEM_CODE);
			prompt("Sub-sub-system code", subSubSystemCode, MAX_SUB_SUB_SYSTEM_CODE);
			prompt("Assembly code", assyCode, MAX_ASSY_CODE);
			prompt("Disassembly code", disassyCode, MAX_DISASSY_CODE);
			prompt("Disassembly code variant", disassyCodeVariant, MAX_DISASSY_CODE_VARIANT);
			prompt("Information code", infoCode, MAX_INFO_CODE);
			prompt("Information code variant", infoCodeVariant, MAX_INFO_CODE_VARIANT);
			prompt("Item location code", itemLocationCode, MAX_ITEM_LOCATION_CODE);
			prompt("Learn code", learnCode, MAX_LEARN_CODE);
			prompt("Learn event code", learnEventCode, MAX_LEARN_EVENT_CODE);
		}
		prompt("Language ISO code", languageIsoCode, MAX_LANGUAGE_ISO_CODE);
		prompt("Country ISO code", countryIsoCode, MAX_COUNTRY_ISO_CODE);
		prompt("Issue number", issueNumber, MAX_ISSUE_NUMBER);
		prompt("In-work issue", inWork, MAX_IN_WORK);
		prompt("Security classification", securityClassification, MAX_SECURITY_CLASSIFICATION);
		prompt("Responsible partner company", responsiblePartnerCompany_enterpriseName, MAX_ENTERPRISE_NAME);
		prompt("Originator", originator_enterpriseName, MAX_ENTERPRISE_NAME);
		prompt("Tech name", techName_content, MAX_TECH_NAME);
		prompt("Info name", infoName_content, MAX_INFO_NAME);
		prompt("DM type", dmtype, 32);
		prompt("Schema", schema, 1024);
	}

	if (strcmp(modelIdentCode, "") == 0 ||
	    strcmp(systemDiffCode, "") == 0 ||
	    strcmp(systemCode, "") == 0 ||
	    strcmp(subSystemCode, "") == 0 ||
	    strcmp(subSubSystemCode, "") == 0 ||
	    strcmp(assyCode, "") == 0 ||
	    strcmp(disassyCode, "") == 0 ||
	    strcmp(disassyCodeVariant, "") == 0 ||
	    strcmp(infoCode, "") == 0 ||
	    strcmp(infoCodeVariant, "") == 0 ||
	    strcmp(itemLocationCode, "") == 0) {

	    fprintf(stderr, ERR_PREFIX "Missing required DMC components: ");
	    fprintf(stderr, "DMC-%s-%s-%s-%s%s-%s-%s%s-%s%s-%s\n",
		strcmp(modelIdentCode, "") == 0     ? "???" : modelIdentCode,
		strcmp(systemDiffCode, "") == 0     ? "???" : systemDiffCode,
		strcmp(systemCode, "") == 0         ? "???" : systemCode,
		strcmp(subSystemCode, "") == 0      ? "???" : subSystemCode,
		strcmp(subSubSystemCode, "") == 0   ? "???" : subSubSystemCode,
		strcmp(assyCode, "") == 0           ? "???" : assyCode,
		strcmp(disassyCode, "") == 0        ? "???" : disassyCode,
		strcmp(disassyCodeVariant, "") == 0 ? "???" : disassyCodeVariant,
		strcmp(infoCode, "") == 0           ? "???" : infoCode,
		strcmp(infoCodeVariant, "") == 0    ? "???" : infoCodeVariant,
		strcmp(itemLocationCode, "") == 0   ? "???" : itemLocationCode);

		exit(EXIT_BAD_DMC);
	}

	if (!tech_name_flag && (maint_sns || sns_fname || strcmp(brex_dmcode, "") != 0)) {
		xmlDocPtr brex;
		brex = set_tech_from_sns(defaults_dir);
		xmlFreeDoc(brex);
	}

	if (issue == NO_ISS) issue = DEFAULT_S1000D_ISSUE;
	if (strcmp(issueNumber, "") == 0) strcpy(issueNumber, "000");
	if (strcmp(inWork, "") == 0) strcpy(inWork, "01");
	if (strcmp(securityClassification, "") == 0) strcpy(securityClassification, "01");

	set_env_lang();
	for (i = 0; languageIsoCode[i]; ++i) {
		languageIsoCode[i] = tolower(languageIsoCode[i]);
	}
	for (i = 0; countryIsoCode[i]; ++i) {
		countryIsoCode[i] = toupper(countryIsoCode[i]);
	}

	if (sns_title_levels == 0) {
		sns_title_levels = 1;
	}

	dm = xml_skeleton(dmtype, issue);

	dmodule = xmlDocGetRootElement(dm);
	identAndStatusSection = find_child(dmodule, "identAndStatusSection");
	dmAddress = find_child(identAndStatusSection, "dmAddress");
	dmIdent = find_child(dmAddress, "dmIdent");
	dmCode = find_child(dmIdent, "dmCode");
	language = find_child(dmIdent, "language");
	issueInfo = find_child(dmIdent, "issueInfo");
	dmAddressItems = find_child(dmAddress, "dmAddressItems");
	issueDate = find_child(dmAddressItems, "issueDate");
	dmStatus = find_child(identAndStatusSection, "dmStatus");
	security = find_child(dmStatus, "security");
	dmTitle = find_child(dmAddressItems, "dmTitle");
	techName = find_child(dmTitle, "techName");
	infoName = find_child(dmTitle, "infoName");
	infoNameVariant = find_child(dmTitle, "infoNameVariant");

	if (strcmp(schema, "") != 0) {
		xmlSetProp(dmodule, BAD_CAST "xsi:noNamespaceSchemaLocation", BAD_CAST schema);
	}

	xmlSetProp(dmCode, BAD_CAST "modelIdentCode", BAD_CAST modelIdentCode);
	xmlSetProp(dmCode, BAD_CAST "systemDiffCode", BAD_CAST systemDiffCode);
	xmlSetProp(dmCode, BAD_CAST "systemCode", BAD_CAST systemCode);
	xmlSetProp(dmCode, BAD_CAST "subSystemCode", BAD_CAST subSystemCode);
	xmlSetProp(dmCode, BAD_CAST "subSubSystemCode", BAD_CAST subSubSystemCode);
	xmlSetProp(dmCode, BAD_CAST "assyCode", BAD_CAST assyCode);
	xmlSetProp(dmCode, BAD_CAST "disassyCode", BAD_CAST disassyCode);
	xmlSetProp(dmCode, BAD_CAST "disassyCodeVariant", BAD_CAST disassyCodeVariant);
	xmlSetProp(dmCode, BAD_CAST "infoCode", BAD_CAST infoCode);
	xmlSetProp(dmCode, BAD_CAST "infoCodeVariant", BAD_CAST infoCodeVariant);
	xmlSetProp(dmCode, BAD_CAST "itemLocationCode", BAD_CAST itemLocationCode);

	if (strcmp(learnCode, "") != 0) xmlSetProp(dmCode, BAD_CAST "learnCode", BAD_CAST learnCode);
	if (strcmp(learnEventCode, "") != 0) xmlSetProp(dmCode, BAD_CAST "learnEventCode", BAD_CAST learnEventCode);

	xmlSetProp(language, BAD_CAST "languageIsoCode", BAD_CAST languageIsoCode);
	xmlSetProp(language, BAD_CAST "countryIsoCode", BAD_CAST countryIsoCode);

	xmlSetProp(issueInfo, BAD_CAST "issueNumber", BAD_CAST issueNumber);
	xmlSetProp(issueInfo, BAD_CAST "inWork", BAD_CAST inWork);

	set_issue_date(issueDate);

	if (issue_type) xmlSetProp(dmStatus, BAD_CAST "issueType", issue_type);

	/* SB DMs also contain an "original issue date" */
	if (strcmp(dmtype, "sb") == 0) {
		xmlNodePtr sbissdate;
		sbissdate = firstXPathNode(dm, NULL, "//sbOriginalIssueDate/issueDate");
		if (sbissdate) set_issue_date(sbissdate);
	}

	xmlSetProp(security, BAD_CAST "securityClassification", BAD_CAST securityClassification);

	xmlNodeSetContent(techName, BAD_CAST techName_content);

	if (strcmp(infoName_content, "") == 0) {
		xmlUnlinkNode(infoName);
		xmlFreeNode(infoName);
	} else {
		xmlChar *s;
		s = xmlEncodeEntitiesReentrant(dm, BAD_CAST infoName_content);
		xmlNodeSetContent(infoName, s);
		xmlFree(s);
	}

	if (info_name_variant) {
		xmlChar *s;
		s = xmlEncodeEntitiesReentrant(dm, info_name_variant);
		xmlNodeSetContent(infoNameVariant, s);
		xmlFree(s);
	} else {
		xmlUnlinkNode(infoNameVariant);
		xmlFreeNode(infoNameVariant);
	}

	responsiblePartnerCompany = find_child(dmStatus, "responsiblePartnerCompany");
	if (strcmp(responsiblePartnerCompany_enterpriseCode, "") != 0) {
		xmlSetProp(responsiblePartnerCompany, BAD_CAST "enterpriseCode", BAD_CAST responsiblePartnerCompany_enterpriseCode);
	}
	if (strcmp(responsiblePartnerCompany_enterpriseName, "") != 0) {
		xmlNodePtr node;
		if ((node = firstXPathNode(dm, NULL, "//responsiblePartnerCompany/enterpriseName"))) {
			xmlNodeSetContent(node, BAD_CAST responsiblePartnerCompany_enterpriseName);
		} else {
			xmlNewChild(responsiblePartnerCompany, NULL, BAD_CAST "enterpriseName", BAD_CAST responsiblePartnerCompany_enterpriseName);
		}
	}

	originator = find_child(dmStatus, "originator");
	if (strcmp(originator_enterpriseCode, "") != 0) {
		xmlSetProp(originator, BAD_CAST "enterpriseCode", BAD_CAST originator_enterpriseCode);
	}
	if (strcmp(originator_enterpriseName, "") != 0) {
		xmlNodePtr node;
		if ((node = firstXPathNode(dm, NULL, "//originator/enterpriseName"))) {
			xmlNodeSetContent(node, BAD_CAST originator_enterpriseName);
		} else {
			xmlNewChild(originator, NULL, BAD_CAST "enterpriseName", BAD_CAST originator_enterpriseName);
		}
	}

	set_skill_level(dm, skill_level_code);

	set_remarks(dm, remarks);

	if (act_dmcode) {
		set_act(dm, act_dmcode);
	} else {
		unset_act(dm);
	}

	if (strcmp(brex_dmcode, "") != 0)
		set_brex(dm, brex_dmcode);

	if (brex_rules) {
		xmlNodePtr context_rules;
		if ((context_rules = firstXPathNode(dm, NULL, "//contextRules"))) {
			xmlAddChild(context_rules, brex_rules);
		} else {
			xmlFreeNode(brex_rules);
		}
	}

	for (i = 0; languageIsoCode[i]; ++i) languageIsoCode[i] = toupper(languageIsoCode[i]);

	if (strcmp(learnCode, "") != 0 && strcmp(learnEventCode, "") != 0) {
		snprintf(learn, 8, "-%s%s", learnCode, learnEventCode);
	}

	if (!no_issue) {
		snprintf(iss, 16, "_%s-%s", issueNumber, inWork);
	}

	if (issue < ISS_50) {
		if (strcmp(brex_dmcode, "") == 0) {
			switch (issue) {
				case ISS_22:
					set_brex(dm, ISS_22_DEFAULT_BREX);
					break;
				case ISS_23:
					set_brex(dm, ISS_23_DEFAULT_BREX);
					break;
				case ISS_30:
					set_brex(dm, ISS_30_DEFAULT_BREX);
					break;
				case ISS_40:
					set_brex(dm, ISS_40_DEFAULT_BREX);
					break;
				case ISS_41:
					set_brex(dm, ISS_41_DEFAULT_BREX);
					break;
				case ISS_42:
					set_brex(dm, ISS_42_DEFAULT_BREX);
					break;
				default:
					break;
			}
		}

		dm = toissue(dm, issue);
	}

	if (out && isdir(out, false)) {
		outdir = out;
		out = NULL;
	}

	if (!out) {
		char dmc[MAX_DATAMODULE_CODE];

		snprintf(dmc, MAX_DATAMODULE_CODE,
			"DMC-%s-%s-%s-%s%s-%s-%s%s-%s%s-%s%s%s_%s-%s.XML",
			modelIdentCode,
			systemDiffCode,
			systemCode,
			subSystemCode,
			subSubSystemCode,
			assyCode,
			disassyCode,
			disassyCodeVariant,
			infoCode,
			infoCodeVariant,
			itemLocationCode,
			learn,
			iss,
			languageIsoCode,
			countryIsoCode);

		out = strdup(dmc);
	}

	if (outdir) {
		if (chdir(outdir) != 0) {
			fprintf(stderr, ERR_PREFIX "Could not change to directory %s: %s\n", outdir, strerror(errno));
			exit(EXIT_OS_ERROR);
		}
	}

	if (!overwrite && access(out, F_OK) != -1) {
		if (no_overwrite_error) return 0;
		if (outdir) {
			fprintf(stderr, ERR_PREFIX "%s/%s already exists. Use -f to overwrite.\n", outdir, out);
		} else {
			fprintf(stderr, ERR_PREFIX "%s already exists. Use -f to overwrite.\n", out);
		}
		exit(EXIT_DM_EXISTS);
	}

	save_xml_doc(dm, out);

	if (verbose) {
		if (outdir) {
			printf("%s/%s\n", outdir, out);
		} else {
			puts(out);
		}
	}

	free(out);
	free(outdir);
	free(template_dir);
	free(act_dmcode);
	free(sns_fname);
	free(maint_sns);
	free(skill_level_code);
	free(defaults_dir_str);

	xmlFree(issue_type);
	xmlFree(remarks);
	xmlFree(info_name_variant);

	xmlFreeDoc(brexmap);
	xmlFreeDoc(dm);

	xmlCleanupParser();
	xsltCleanupGlobals();

	return 0;
}


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