summaryrefslogtreecommitdiffstats
path: root/gxpp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gxpp.c')
-rw-r--r--gxpp.c351
1 files changed, 351 insertions, 0 deletions
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;
+}