/* * gxpp.c -- Simple XPath Util * * Print the string result of the XPath query to stdout. * Optionaly print only the number of matches. * * Copyright © 2006-2008 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 * Nathan Straz */ #include #include #include #include #include #include #include #include #include #include #include #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; }