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

#define PROG_NAME "xml-merge"
#define VERSION "1.5.0"

static void show_help(void)
{
	puts("Usage: " PROG_NAME "[-fh?] <dst> <src>");
	puts("");
	puts("Options:");
	puts("  -f, --overwrite  Overwrite <dst> file.");
	puts("  -h, -?, --help   Show help/usage message");
	puts("  --version        Show version information");
	puts("  dst              XML file to <src> in to");
	puts("  src              XML file to merge in to <dst>");
	LIBXML2_PARSE_LONGOPT_HELP
}

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

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

	ctx = xmlXPathNewContext(doc);
	obj = xmlXPathEvalExpression(BAD_CAST xpath, ctx);

	if (xmlXPathNodeSetIsEmpty(obj->nodesetval))
		node = NULL;
	else
		node = obj->nodesetval->nodeTab[0];
	
	xmlXPathFreeObject(obj);
	xmlXPathFreeContext(ctx);

	return node;
}

int main(int argc, char **argv)
{
	const char *fname1 = "-";
	const char *fname2 = "-";

	char xpath[256];

	xmlNodePtr target1;
	xmlNodePtr target2;

	xmlDocPtr doc1;
	xmlDocPtr doc2;

	int c;

	const char *sopts = "fh?";
	struct option lopts[] = {
		{"version"  , no_argument, 0, 0},
		{"help"     , no_argument, 0, 'h'},
		{"overwrite", no_argument, 0, 'f'},
		LIBXML2_PARSE_LONGOPT_DEFS
		{0, 0, 0, 0}
	};
	int loptind = 0;

	bool overwrite = false;

	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 'f':
				overwrite = true;
				break;
			case 'h':
			case '?':
				show_help();
				return 0;
		}
	}

	if (optind < argc) fname1 = argv[optind];
	if (optind + 1 < argc) fname2 = argv[optind + 1];

	doc1 = read_xml_doc(fname1);
	doc2 = read_xml_doc(fname2);

	target2 = xmlDocGetRootElement(doc2);

	sprintf(xpath, "//%s", (char *) target2->name);

	target1 = firstXPathNode(xpath, doc1);

	if (target1) {
		xmlAddNextSibling(target1, xmlCopyNode(target2, 1));
		xmlUnlinkNode(target1);
		xmlFreeNode(target1);
	} else {
		fprintf(stderr, "xml-merge: ERROR: No element %s to merge on.\n", target2->name);
	}

	if (overwrite) {
		save_xml_doc(doc1, fname1);
	} else {
		save_xml_doc(doc1, "-");
	}

	xmlFreeDoc(doc1);
	xmlFreeDoc(doc2);

	xmlCleanupParser();

	return 0;
}


/ gopher://khzae.net/0/s1000d/xml/xml-utils/src/utils/xml-merge/xml-merge.c
Styles: Light Dark Classic