summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--gxpm.154
-rw-r--r--gxpm.c136
3 files changed, 191 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index b82aecc..9c9bd14 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-TARGETS := gxpp gxpd
+TARGETS := gxpp gxpd gxpm
all: $(TARGETS)
diff --git a/gxpm.1 b/gxpm.1
new file mode 100644
index 0000000..f5ce915
--- /dev/null
+++ b/gxpm.1
@@ -0,0 +1,54 @@
+.TH GXPM 1 "3 July 2008"
+.SH NAME
+gxpm \- Merge XML documents using XPath expressions
+.SH SYNOPSIS
+\fBgxpm\fR [\fIoptions\fR] \fIXPATH\fR [\fIFILE\fR...] < \fIXML\fR
+.SH DESCRIPTION
+
+.B Gxpm
+takes an XML document from standard input and merges into \fIFILE\fR
+as a child of the locations which match XPATH. Changes are done in-place
+to \fIFILE\fR.
+
+.SH OPTIONS
+
+.TP
+.BI \-p " PRE"
+The prefix to use in the XPath expression to match the default XML
+name space in the document. XPath 1.0 does not define a way to match
+the default name space when an XML document contains XML name spaces.
+By specifying a default name space prefix here, you can match on the
+default prefix in the document.
+
+.SH EXAMPLES
+.nf
+.B Example document
+<A>
+ <B name="foo">
+ <C>C of foo</C>
+ </B>
+ <B name="bar">
+ <C>C of bar</C>
+ </B>
+ <B name="baz">
+ <C>C of baz</C>
+ </B>
+</A>
+
+.B # echo '<B name="frank"/>' | gxpm '/A' example.xml
+.B # cat example.xml
+<?xml version="1.0"?>
+<A>
+ <B name="foo">
+ <C>C of foo</C>
+ </B>
+ <B name="bar">
+ <C>C of bar</C>
+ </B>
+
+<B name="frank"/></A>
+
+.fi
+
+.SH "SEE ALSO"
+gxpp(1), gxpd(1), grep(1), http://www.w3.org/TR/xpath
diff --git a/gxpm.c b/gxpm.c
new file mode 100644
index 0000000..b384530
--- /dev/null
+++ b/gxpm.c
@@ -0,0 +1,136 @@
+/*
+ * gxpm.c -- Simple XPath Merge Util
+ *
+ * Copyright © 2006-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions of the
+ * GNU General Public License v.2. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY expressed or implied,
+ * including the implied warranties of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
+ * trademarks that are incorporated in the source code or documentation are not
+ * subject to the GNU General Public License and may only be used or replicated
+ * with the express permission of Red Hat, Inc.
+ *
+ * Red Hat Author(s): Nathan Straz <nstraz@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/xinclude.h>
+
+#include "gxp-int.h"
+
+static void usage(const char *pname);
+static int gxpm(char *fn, const char *xpath_query, const char *prefix, xmlDocPtr snip);
+
+int
+main(int argc, char **argv)
+{
+ xmlDocPtr snip;
+ char *xpath_query = NULL;
+ char *prefix = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "+p:h")) != -1) {
+ switch (c) {
+ case 'p':
+ prefix = strdup(optarg);
+ break;
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ exit(2);
+ break;
+ }
+ }
+ xpath_query = argv[optind];
+ if (!xpath_query) {
+ fprintf(stderr, "Missing XPath Query\n");
+ usage(argv[0]);
+ exit(2);
+ }
+
+ if (!(snip = xmlReadFd(fileno(stdin), NULL, NULL, 0))) {
+ fprintf(stderr, "Failed to parse XML snippet on standard input\n");
+ exit(1);
+ }
+
+ optind++;
+ if (!argv[optind]) {
+ /* No files given, filter stdin to stdout */
+ gxpm("-", xpath_query, prefix, snip);
+ } else {
+ /* Rest of the args are the files to change */
+#if 0
+ if (((argc - optind) > 1)
+ && (!gxpp_opts.omitfileprefix)) gxpp_opts.addfileprefix = 1;
+#endif
+ for (c = optind; c < argc; c++) {
+ /* Make sure we can write to the file */
+ gxpm(argv[c], xpath_query, prefix, snip);
+ }
+ }
+ xmlFreeDoc(snip);
+ return 0;
+}
+
+static int
+gxpm(char *fn, const char *xpath_query, const char *prefix, xmlDocPtr snip)
+{
+ xmlDocPtr doc;
+ xmlXPathContextPtr ctx;
+ xmlXPathObjectPtr obj;
+ int i;
+
+ if (!(doc = xmlParseFile(fn))) {
+ fprintf(stderr, "Failed to parse XML file\n");
+ exit(1);
+ }
+ /* xmlXIncludeProcess(doc); */
+ ctx = generate_context(doc, prefix);
+ obj = xmlXPathEvalExpression((xmlChar *)xpath_query, ctx);
+ if (!obj) {
+ fprintf(stderr, "XPath expression '%s' did not match in '%s'\n",
+ xpath_query, fn);
+ exit(1);
+ }
+ if (obj->type != XPATH_NODESET) {
+ fprintf(stderr, "XPath expression '%s' did not match a nodeset.\n", xpath_query);
+ exit(1);
+ }
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ xmlAddChild(obj->nodesetval->nodeTab[i], xmlCopyNode(xmlDocGetRootElement(snip), 1));
+ }
+
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctx);
+ xmlSaveFile(fn, doc);
+
+ xmlFreeDoc(doc);
+
+ return 0;
+}
+
+
+static void
+usage(const char *pname)
+{
+ fprintf(stderr,"%s [OPTION]... XPATH [FILE]... < XML\n", pname);
+ fprintf(stderr,"Options:\n"
+ "-p prefix Set prefix for default namespace\n");
+ return;
+}
+