summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile44
-rw-r--r--gxp-int.c47
-rw-r--r--gxp-int.h20
-rw-r--r--gxpp.190
-rw-r--r--gxpp.c351
-rw-r--r--gxpp.spec42
6 files changed, 594 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1aa1d9f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+
+TARGETS := gxpp
+
+all: $(TARGETS)
+
+CFLAGS := -Wall -g
+gxpp: gxp-int.c
+gxpp: CFLAGS += -I/usr/include/libxml2
+gxpp: LOADLIBES := -lxml2
+
+install: all
+ for i in $(TARGETS); do \
+ install -D $$i $(DESTDIR)/usr/bin/$$i; \
+ install -D $$i.1 $(DESTDIR)/usr/share/man/man1/$$i.1; \
+ done
+
+clean:
+ $(RM) $(TARGETS)
+
+
+VERSION := $(shell awk '/^Version:/ { print $$2 }' gxpp.spec)
+
+ifdef DIST
+ RPMDEFS := -D "dist $(DIST)"
+endif
+
+tarfiles := gxpp.spec Makefile
+tarfiles += $(patsubst %,%.c,$(TARGETS))
+tarfiles += gxp-int.c gxp-int.h
+tarfiles += $(patsubst %,%.1,$(TARGETS))
+
+tarball := gxpp-$(VERSION).tar.bz2
+
+$(tarball): $(tarfiles)
+ -$(RM) $@
+ tar cjf $@ $^
+
+tarball: $(tarball)
+
+rpm: $(tarball) $(tarfiles)
+ rpmbuild -ta $< $(RPMDEFS)
+
+srpm: $(tarball) $(tarfiles)
+ rpmbuild -ts $< $(RPMDEFS)
diff --git a/gxp-int.c b/gxp-int.c
new file mode 100644
index 0000000..c36aa5f
--- /dev/null
+++ b/gxp-int.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 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 <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+xmlXPathContextPtr
+generate_context(xmlDocPtr doc, const char *defprefix)
+{
+ xmlXPathContextPtr ctx;
+ xmlNodePtr root;
+ xmlNsPtr ns;
+
+ root = xmlDocGetRootElement(doc);
+ ctx = xmlXPathNewContext(doc);
+
+ for (ns = root->ns; ns; ns=ns->next) {
+ if (ns->href) {
+ if (ns->prefix) {
+ xmlXPathRegisterNs(ctx, ns->prefix, ns->href);
+ } else if (defprefix) {
+ xmlXPathRegisterNs(ctx, (xmlChar *)defprefix, ns->href);
+ } else {
+ fprintf(stderr, "WARNING: document uses default namespace, use -p option to specify a prefix\n");
+ }
+ }
+ }
+
+ return ctx;
+}
diff --git a/gxp-int.h b/gxp-int.h
new file mode 100644
index 0000000..a9b1096
--- /dev/null
+++ b/gxp-int.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright © 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>
+ */
+
+xmlXPathContextPtr generate_context(xmlDocPtr doc, const char *defprefix);
diff --git a/gxpp.1 b/gxpp.1
new file mode 100644
index 0000000..70c8d27
--- /dev/null
+++ b/gxpp.1
@@ -0,0 +1,90 @@
+.TH GXPP 1 "3 July 2008"
+.SH NAME
+gxpp \- print elements matching an XPath expression
+.SH SYNOPSIS
+\fBgxpp\fR [\fIoptions\fR] \fIXPATH \fR[\fIFILE\fR...]
+.SH DESCRIPTION
+
+.B Gxpp
+prints the results of an XPath expression when applied to the
+specified XML document or standard input.
+
+.SH OPTIONS
+
+.TP
+.B \-c
+Print the count of the expression matches instead of printing the
+matches.
+
+.TP
+.B \-H
+Print the filename for each match.
+
+.TP
+.B \-h
+Suppress the printing of filenames when multiple files are specified.
+
+.TP
+.B \-L
+Suppress normal output; instead print the name of each input document for which the XPath expression does not match.
+
+.TP
+.B \-l
+Suppress normal output; instead print the name of each input document for which the XPath expression matches.
+
+.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.
+
+.TP
+.B \-q
+Exit with the normal exit status, but don't generate any output.
+
+.TP
+.B \-x
+Print the XML sub-tree(s) which match the XPath expression instead of
+the contents of the XML elements.
+
+.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 # gxpp /A/B/@name example.xml
+foo
+bar
+baz
+
+.B # gxpp -c /A/B/@name example.xml
+3
+
+.B # gxpp -x "/A/B[@name='baz']" example.xml
+<B name="baz">
+ <C>C of baz</C>
+</B>
+
+.B # gxpp "/A/B[@name='bar']/C" example.xml
+C of bar
+
+.B # gxpp //C example.xml
+C of foo
+C of bar
+C of baz
+
+.fi
+.SH "SEE ALSO"
+xpm(1), grep(1), http://www.w3.org/TR/xpath
diff --git a/gxpp.c b/gxpp.c
new file mode 100644
index 0000000..19b3cb7
--- /dev/null
+++ b/gxpp.c
@@ -0,0 +1,351 @@
+/*
+ * gxpp.c -- Simple XPath Util
+ *
+ * Print the string result of the XPath query to stdout.
+ * Optionaly print only the number of matches.
+ *
+ * Copyright © 2006-20078 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): Dean Jansa <djansa@redhat.com>
+ * Nathan Straz <nstraz@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.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 int gxpp(char *xfile, const char *xpath_query, const char *prefix);
+static int xpath(xmlDocPtr doc, xmlXPathContextPtr ctx, const char *xpathstr,
+ const char *xfile);
+static int xpath_nodedump(xmlXPathContextPtr ctx, const char *xpathstr,
+ const char *xfile);
+static void usage(const char *pname);
+
+static struct {
+ unsigned countonly : 1; /* -c */
+ unsigned addfileprefix : 1; /* -H or multi file args */
+ unsigned omitfileprefix : 1; /* -h with multi file args */
+ unsigned filenomatch : 1; /* -L */
+ unsigned filematch : 1; /* -l */
+ unsigned quiet : 1; /* -q */
+ unsigned xmlnodedump : 1; /* -x */
+} gxpp_opts;
+
+
+
+int
+main(int argc, char **argv)
+{
+ char *xpath_query;
+ char *prefix;
+ int retval;
+ int c;
+
+ gxpp_opts.countonly = 0;
+ gxpp_opts.addfileprefix = 0;
+ gxpp_opts.omitfileprefix = 0;
+ gxpp_opts.filenomatch = 0;
+ gxpp_opts.filematch = 0;
+ gxpp_opts.quiet = 0;
+ gxpp_opts.xmlnodedump = 0;
+ xpath_query = NULL;
+ prefix = NULL;
+
+ while ((c = getopt(argc, argv, "+cHhLlqxp:")) != -1) {
+ switch (c) {
+ case 'c':
+ gxpp_opts.countonly = 1;
+ break;
+ case 'H':
+ gxpp_opts.addfileprefix = 1;
+ gxpp_opts.omitfileprefix = 0;
+ break;
+ case 'h':
+ gxpp_opts.addfileprefix = 0;
+ gxpp_opts.omitfileprefix = 1;
+ break;
+ case 'L':
+ gxpp_opts.filenomatch = 1;
+ gxpp_opts.filematch = 0;
+ gxpp_opts.quiet = 1;
+ break;
+ case 'l':
+ gxpp_opts.filenomatch = 0;
+ gxpp_opts.filematch = 1;
+ gxpp_opts.quiet = 1;
+ break;
+ case 'p':
+ prefix = strdup(optarg);
+ break;
+ case 'q':
+ gxpp_opts.quiet = 1;
+ break;
+ case 'x':
+ gxpp_opts.xmlnodedump = 1;
+ break;
+ 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);
+ }
+
+ optind++;
+ if (!argv[optind]) {
+ /* No files given, use stdin */
+ retval = gxpp("-", xpath_query, prefix);
+ } else {
+ /* Rest of the args are the files to search */
+ if (((argc - optind) > 1)
+ && (!gxpp_opts.omitfileprefix)) gxpp_opts.addfileprefix = 1;
+
+ for (c = optind; c < argc; c++) {
+ retval = gxpp(argv[c], xpath_query, prefix);
+ if (retval < 0) {
+ break;
+ }
+ if (gxpp_opts.filematch && retval > 0) printf("%s\n", argv[c]);
+ if (gxpp_opts.filenomatch && !retval) printf("%s\n", argv[c]);
+ }
+ }
+
+ if (retval == -1) {
+ retval = 2;
+ } else {
+ retval = !retval;
+ }
+
+ exit(retval);
+}
+
+
+/*
+ * gxpp --
+ *
+ * Open file, parse doc and run the xpath query.
+ *
+ * Returns:
+ * Number of matches on success, less than zero on failure.
+ * No matches is not a failure, return of 0 is just no matches found.
+ */
+
+static int
+gxpp(char *xfile, const char *xpath_query, const char *prefix)
+{
+ xmlDocPtr doc;
+ xmlXPathContextPtr ctx;
+ char *cp;
+ int retval;
+
+ if (!(doc = xmlParseFile(xfile))) {
+ fprintf(stderr, "XML Parse failed\n");
+ return -1;
+ }
+
+ ctx = generate_context(doc, prefix);
+
+ /* FIXME: do some error checking */
+ xmlXIncludeProcess(doc);
+ if (gxpp_opts.xmlnodedump) {
+ retval = xpath_nodedump(ctx, xpath_query, xfile);
+ } else {
+ if (gxpp_opts.countonly) {
+ cp = malloc(strlen(xpath_query) + strlen("count()") + 1);
+ sprintf(cp, "count(%s)", xpath_query);
+ xpath_query = cp;
+ }
+
+ retval = xpath(doc, ctx, xpath_query, xfile);
+ }
+
+ xmlXPathFreeContext(ctx);
+ xmlFreeDoc(doc);
+
+ return retval;
+}
+
+
+/*
+ * xpath_nodedump --
+ *
+ * Dump all XML nodes which match XPath query to stdout.
+ *
+ * Returns:
+ * Number of nodes which matched XPath query
+ */
+
+static int
+xpath_nodedump(xmlXPathContextPtr ctx, const char *xpathstr, const char *xfile)
+{
+ xmlXPathObjectPtr obj = NULL;
+ xmlNodePtr node;
+ xmlBufferPtr xmlbp;
+ int i;
+
+ obj = xmlXPathEvalExpression((xmlChar *)xpathstr, ctx);
+
+ if ((obj == NULL) || (obj->type != XPATH_NODESET)
+ || (obj->nodesetval == NULL)) {
+ return 0;
+ }
+
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ node = xmlXPathNodeSetItem(obj->nodesetval, i);
+
+ xmlbp = xmlBufferCreate();
+ xmlNodeDump(xmlbp, NULL, node, 0, 0);
+ if (!gxpp_opts.quiet) {
+ if (gxpp_opts.addfileprefix) printf("%s: ", xfile);
+ printf("%s\n", xmlBufferContent(xmlbp));
+ }
+ xmlBufferFree(xmlbp);
+ }
+
+ xmlXPathFreeObject(obj);
+
+ return i;
+}
+
+
+/*
+ * xpath --
+ *
+ * Dump all XML nodes string values which match XPath query to stdout.
+ *
+ * Returns:
+ * Number of nodes which matched XPath query.
+ * -1 on error.
+ */
+
+static int
+xpath(xmlDocPtr doc, xmlXPathContextPtr ctx, const char *xpathstr,
+ const char *xfile)
+{
+ xmlNodeSetPtr nodeset;
+ xmlXPathObjectPtr result;
+ xmlChar *strval;
+ int matchc = 0;
+ double num;
+ int i;
+
+ result = xmlXPathEvalExpression((xmlChar *)xpathstr, ctx);
+ if (result == NULL) {
+ return -1;
+ }
+
+ switch (result->type) {
+ case XPATH_NODESET:
+ if (xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ return 0;
+ }
+
+ nodeset = result->nodesetval;
+ matchc=nodeset->nodeNr;
+ for (i=0; i < nodeset->nodeNr; i++) {
+ strval = xmlNodeListGetString(doc,
+ nodeset->nodeTab[i]->xmlChildrenNode, 1);
+ if (strval) {
+ if (!gxpp_opts.quiet) {
+ if (gxpp_opts.addfileprefix) printf("%s: ", xfile);
+ printf("%s\n", strval);
+ }
+ xmlFree(strval);
+ }
+ }
+
+ break;
+
+ case XPATH_BOOLEAN:
+ matchc=1;
+ if (!gxpp_opts.quiet) {
+ if (gxpp_opts.addfileprefix) printf("%s: ", xfile);
+ printf("%d\n", xmlXPathCastToBoolean(result));
+ }
+ break;
+
+ case XPATH_NUMBER:
+ matchc=1;
+ num = xmlXPathCastToNumber(result);
+ if (!gxpp_opts.quiet) {
+ if (gxpp_opts.addfileprefix) printf("%s: ", xfile);
+ if ((ceil(num) - num) == 0) {
+ printf("%lld\n", (long long int)num);
+ } else {
+ printf("%f\n", num);
+ }
+ }
+ break;
+
+ case XPATH_STRING:
+ matchc=1;
+ /*
+ * xmlXPathCastToString(result) -- return the string value of
+ * the object, NULL in case of error. A new string is allocated
+ * only if needed (result isn't a string object). We are only
+ * in this case if result is a string obj, no need to free()
+ * the return value.
+ */
+ if (!gxpp_opts.quiet) {
+ if (gxpp_opts.addfileprefix) printf("%s: ", xfile);
+ printf("%s\n", xmlXPathCastToString(result));
+ }
+ break;
+
+ default:
+ matchc=0;
+ break;
+ }
+
+ xmlXPathFreeObject(result);
+ return matchc;
+}
+
+
+static void
+usage(const char *pname)
+{
+ fprintf(stderr,"Usage: %s [OPTION]... XPATH [FILE]...\n", pname);
+ fprintf(stderr,"Options:\n"
+ "-c Print count of matches in file only\n"
+ "-H Print filename for each match. \n"
+ "-h Suppress filenames when multiple files are searched\n"
+ "-L Print the filename of files without matches\n"
+ "-l Print the filename of files with matches\n"
+ "-q Suppress output of matches\n"
+ "-x Dump nodes which match in XML format\n"
+ "-p prefix Set prefix for default namespace\n");
+
+ return;
+}
diff --git a/gxpp.spec b/gxpp.spec
new file mode 100644
index 0000000..5dbe4fa
--- /dev/null
+++ b/gxpp.spec
@@ -0,0 +1,42 @@
+Name: gxpp
+Version: 1.0
+Release: 1%{?dist}
+Summary: Simple XPath command line tools
+
+Group: QA
+License: GPL
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Source0: gxpp-%{version}.tar.bz2
+BuildRequires: libxml2-devel
+
+
+%description
+This is a set of simple but powerful tools to query and manipulate
+XML files using XPath expressions.
+
+%prep
+%setup -q -c
+
+
+%build
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+/usr/bin/gxpp
+%doc %{_mandir}/*
+
+
+%changelog
+* Mon Aug 04 2008 1.0-1
+- Initial packaging