From bb778c05dd86986c19fbd14bb8e7ffaa91634427 Mon Sep 17 00:00:00 2001 From: Nathan Straz Date: Tue, 5 Aug 2008 09:58:59 -0400 Subject: Add gxpm which allows us to add things to XML files. --- Makefile | 2 +- gxpm.1 | 54 +++++++++++++++++++++++++ gxpm.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 gxpm.1 create mode 100644 gxpm.c 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 + + + C of foo + + + C of bar + + + C of baz + + + +.B # echo '' | gxpm '/A' example.xml +.B # cat example.xml + + + + C of foo + + + C of bar + + + + +.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 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} + -- cgit