fc0b771cdf
the node cache prevents recursively walking the xml tree over and over again to find the specified nodes.
442 lines
11 KiB
C
442 lines
11 KiB
C
#include <stdio.h>
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef _MSC_VER
|
|
# include <strings.h>
|
|
# include <unistd.h> /* read */
|
|
#else
|
|
# define strncasecmp strnicmp
|
|
# include <stdlib.h>
|
|
# include <io.h>
|
|
#endif
|
|
#include <assert.h>
|
|
#include <sys/stat.h> /* fstat */
|
|
#include <fcntl.h> /* open */
|
|
|
|
#include "xml.h"
|
|
|
|
static const char *_static_root = "/";
|
|
static const char *_static_element = "*";
|
|
static unsigned int _fcount = 0;
|
|
static char **_filenames = 0;
|
|
static char *_element = 0;
|
|
static char *_value = 0;
|
|
static char *_root = 0;
|
|
static char *_print = 0;
|
|
static char *_attribute = 0;
|
|
static int print_filenames = 0;
|
|
|
|
static void free_and_exit(int i);
|
|
|
|
#define USE_BUFFER 0
|
|
#define NODE_NAME_LEN 256
|
|
#define STRING_LEN 2048
|
|
|
|
#define SHOW_NOVAL(opt) \
|
|
{ \
|
|
printf("option '%s' requires a value\n\n", (opt)); \
|
|
free_and_exit(-1); \
|
|
}
|
|
|
|
void
|
|
show_help ()
|
|
{
|
|
printf("usage: xmlgrep [options] [file ...]\n\n");
|
|
printf("Options:\n");
|
|
printf("\t-h\t\tshow this help message\n");
|
|
printf("\t-a <string>\tprint this attribute as the output\n");
|
|
printf("\t-e <id>\t\tshow sections that contain this element\n");
|
|
printf("\t-p <id>\t\tprint this element as the output\n");
|
|
printf("\t-r <path>\tspecify the XML search root\n");
|
|
printf("\t-v <string>\tfilter sections that contain this vale\n\n");
|
|
printf(" To print the contents of the 'type' element of the XML section ");
|
|
printf("that begins\n at '/printer/output' use the following command:\n\n");
|
|
printf("\txmlgrep -r /printer/output -p type sample.xml\n\n");
|
|
printf(" To filter 'output' elements under '/printer' that have attribute");
|
|
printf(" 'n' set to '1'\n use the following command:\n\n");
|
|
printf("\txmlgrep -r /printer -p output -a n -v 1 sample.xml\n\n");
|
|
printf(" To filter out sections that contain the 'driver' element with ");
|
|
printf("'generic' as\n it's value use the following command:");
|
|
printf("\n\n\txmlgrep -r /printer/output -e driver -v generic sample.xml");
|
|
printf("\n\n");
|
|
free_and_exit(0);
|
|
}
|
|
|
|
void
|
|
free_and_exit(int ret)
|
|
{
|
|
if (_root && _root != _static_root) free(_root);
|
|
if (_element && _element != _static_element) free(_element);
|
|
if (_value) free(_value);
|
|
if (_print) free(_print);
|
|
if (_attribute) free(_attribute);
|
|
if (_filenames)
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < _fcount; i++)
|
|
{
|
|
if (_filenames[i])
|
|
{
|
|
if (print_filenames) printf("%s\n", _filenames[i]);
|
|
free(_filenames[i]);
|
|
}
|
|
}
|
|
free(_filenames);
|
|
}
|
|
|
|
exit(ret);
|
|
}
|
|
|
|
int
|
|
parse_option(char **args, int n, int max)
|
|
{
|
|
char *opt, *arg = 0;
|
|
unsigned int alen = 0;
|
|
unsigned int olen;
|
|
|
|
opt = args[n];
|
|
if (strncmp(opt, "--", 2) == 0)
|
|
opt++;
|
|
|
|
if ((arg = strchr(opt, '=')) != NULL)
|
|
{
|
|
*arg++ = 0;
|
|
}
|
|
else if (++n < max)
|
|
{
|
|
arg = args[n];
|
|
#if 0
|
|
if (arg && arg[0] == '-')
|
|
arg = 0;
|
|
#endif
|
|
}
|
|
|
|
olen = strlen(opt);
|
|
if (strncmp(opt, "-help", olen) == 0)
|
|
{
|
|
show_help();
|
|
}
|
|
else if (strncmp(opt, "-root", olen) == 0)
|
|
{
|
|
if (arg == 0) SHOW_NOVAL(opt);
|
|
alen = strlen(arg)+1;
|
|
if (_root) free(_root);
|
|
_root = malloc(alen);
|
|
memcpy(_root, arg, alen);
|
|
return 2;
|
|
}
|
|
else if (strncmp(opt, "-element", olen) == 0)
|
|
{
|
|
if (arg == 0) SHOW_NOVAL(opt);
|
|
alen = strlen(arg)+1;
|
|
if (_element) free(_element);
|
|
_element = malloc(alen);
|
|
memcpy(_element, arg, alen);
|
|
return 2;
|
|
}
|
|
else if (strncmp(opt, "-value", olen) == 0)
|
|
{
|
|
if (arg == 0) SHOW_NOVAL(opt);
|
|
alen = strlen(arg)+1;
|
|
if (_value) free(_value);
|
|
_value = malloc(alen);
|
|
memcpy(_value, arg, alen);
|
|
return 2;
|
|
}
|
|
else if (strncmp(opt, "-print", olen) == 0)
|
|
{
|
|
if (arg == 0) SHOW_NOVAL(opt);
|
|
alen = strlen(arg)+1;
|
|
if (_print) free(_print);
|
|
_print = malloc(alen);
|
|
memcpy(_print, arg, alen);
|
|
return 2;
|
|
}
|
|
else if (strncmp(opt, "-attribute", olen) == 0)
|
|
{
|
|
if (arg == 0) SHOW_NOVAL(opt);
|
|
alen = strlen(arg)+1;
|
|
if (_attribute) free(_attribute);
|
|
_attribute = malloc(alen);
|
|
memcpy(_attribute, arg, alen);
|
|
return 2;
|
|
}
|
|
else if (strncmp(opt, "-list-filenames", olen) == 0)
|
|
{ /* undocumented test argument */
|
|
print_filenames = 1;
|
|
return 1;
|
|
}
|
|
else if (opt[0] == '-')
|
|
{
|
|
printf("Unknown option %s\n", opt);
|
|
free_and_exit(-1);
|
|
}
|
|
else
|
|
{
|
|
int pos = _fcount++;
|
|
if (_filenames == 0)
|
|
{
|
|
_filenames = (char **)malloc(sizeof(char*));
|
|
}
|
|
else
|
|
{
|
|
char **ptr = (char **)realloc(_filenames, _fcount*sizeof(char*));
|
|
if (ptr == 0)
|
|
{
|
|
printf("Out of memory.\n\n");
|
|
free_and_exit(-1);
|
|
}
|
|
_filenames = ptr;
|
|
}
|
|
|
|
alen = strlen(opt)+1;
|
|
_filenames[pos] = malloc(alen);
|
|
memcpy(_filenames[pos], opt, alen);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void walk_the_tree(size_t num, void *xid, char *tree)
|
|
{
|
|
unsigned int i, no_elements;
|
|
|
|
if (!tree) /* last node from the tree */
|
|
{
|
|
void *xmid = xmlMarkId(xid);
|
|
if (xmid && _print)
|
|
{
|
|
no_elements = xmlNodeGetNum(xid, _print);
|
|
for (i=0; i<no_elements; i++)
|
|
{
|
|
if (xmlNodeGetPos(xid, xmid, _print, i) != 0)
|
|
{
|
|
char *value;
|
|
|
|
value = xmlGetString(xmid);
|
|
if (_value && _attribute && value)
|
|
{
|
|
#if 1
|
|
char *a = xmlAttributeGetString(xmid, _attribute);
|
|
if (a && !strcmp(a, _value))
|
|
#else
|
|
if (!xmlAttributeCompareString(xmid, _attribute, _value))
|
|
#endif
|
|
{
|
|
printf("%s: <%s %s=\"%s\">%s</%s>\n",
|
|
_filenames[num], _print, _attribute, _value,
|
|
value, _print);
|
|
}
|
|
if (value) free(value);
|
|
}
|
|
else
|
|
{
|
|
printf("%s: <%s>%s</%s>\n",
|
|
_filenames[num], _print, value, _print);
|
|
}
|
|
}
|
|
}
|
|
free(xmid);
|
|
}
|
|
else if (xmid && _value)
|
|
{
|
|
no_elements = xmlNodeGetNum(xmid, _element);
|
|
for (i=0; i<no_elements; i++)
|
|
{
|
|
if (xmlNodeGetPos(xid, xmid, _element, i) != 0)
|
|
{
|
|
char nodename[NODE_NAME_LEN];
|
|
|
|
xmlNodeCopyName(xmid, (char *)&nodename, NODE_NAME_LEN);
|
|
if (xmlCompareString(xmid, _value) == 0)
|
|
{
|
|
printf("%s: <%s>%s</%s>\n",
|
|
_filenames[num], nodename, _value, nodename);
|
|
}
|
|
}
|
|
}
|
|
free(xmid);
|
|
}
|
|
else if (xmid && _element)
|
|
{
|
|
char parentname[NODE_NAME_LEN];
|
|
|
|
xmlNodeCopyName(xid, (char *)&parentname, NODE_NAME_LEN);
|
|
|
|
no_elements = xmlNodeGetNum(xmid, _element);
|
|
for (i=0; i<no_elements; i++)
|
|
{
|
|
if (xmlNodeGetPos(xid, xmid, _element, i) != 0)
|
|
{
|
|
char nodename[NODE_NAME_LEN];
|
|
|
|
xmlNodeCopyName(xmid, (char *)&nodename, NODE_NAME_LEN);
|
|
if (!strncasecmp((char*)&nodename, _element, NODE_NAME_LEN))
|
|
{
|
|
char value[NODE_NAME_LEN];
|
|
xmlCopyString(xmid, (char *)&value, NODE_NAME_LEN);
|
|
printf("%s: <%s> <%s>%s</%s> </%s>\n",
|
|
_filenames[num], parentname, nodename, value,
|
|
nodename, parentname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else printf("Error executing xmlMarkId\n");
|
|
}
|
|
else if (xid) /* walk the rest of the tree */
|
|
{
|
|
char *elem, *next;
|
|
void *xmid;
|
|
|
|
elem = tree;
|
|
if (*elem == '/') elem++;
|
|
|
|
next = strchr(elem, '/');
|
|
|
|
xmid = xmlMarkId(xid);
|
|
if (xmid)
|
|
{
|
|
if (next)
|
|
{
|
|
*next++ = 0;
|
|
}
|
|
|
|
no_elements = xmlNodeGetNum(xid, elem);
|
|
for (i=0; i<no_elements; i++)
|
|
{
|
|
if (xmlNodeGetPos(xid, xmid, elem, i) != 0)
|
|
walk_the_tree(num, xmid, next);
|
|
}
|
|
|
|
if (next)
|
|
{
|
|
*--next = '/';
|
|
}
|
|
|
|
free(xmid);
|
|
}
|
|
else printf("Error executing xmlMarkId\n");
|
|
}
|
|
}
|
|
|
|
void grep_file(unsigned num)
|
|
{
|
|
void *xid;
|
|
|
|
xid = xmlOpen(_filenames[num]);
|
|
if (xid)
|
|
{
|
|
void *xrid = xmlMarkId(xid);
|
|
int r = 0;
|
|
|
|
walk_the_tree(num, xrid, _root);
|
|
|
|
r = xmlErrorGetNo(xrid, 0);
|
|
if (r)
|
|
{
|
|
size_t n = xmlErrorGetLineNo(xrid, 0);
|
|
size_t c = xmlErrorGetColumnNo(xrid, 0);
|
|
const char *s = xmlErrorGetString(xrid, 1); /* clear the error */
|
|
printf("%s: at line %u, column %u: '%s'\n",_filenames[num], n,c, s);
|
|
}
|
|
|
|
free(xrid);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error reading file '%s'\n", _filenames[num]);
|
|
}
|
|
|
|
xmlClose(xid);
|
|
}
|
|
|
|
|
|
void grep_file_buffer(unsigned num)
|
|
{
|
|
struct stat st;
|
|
void *xid, *buf;
|
|
int fd, res;
|
|
|
|
fd = open(_filenames[num], O_RDONLY);
|
|
if (fd == -1)
|
|
{
|
|
printf("read error opening file '%s'\n", _filenames[num]);
|
|
return;
|
|
}
|
|
|
|
fstat(fd, &st);
|
|
buf = malloc(st.st_size);
|
|
if (!buf)
|
|
{
|
|
printf("unable to allocate enough memory for reading.\n");
|
|
return;
|
|
}
|
|
|
|
res = read(fd, buf, st.st_size);
|
|
if (res == -1)
|
|
{
|
|
printf("unable to read from file '%s'.\n", _filenames[num]);
|
|
return;
|
|
}
|
|
close(fd);
|
|
|
|
xid = xmlInitBuffer(buf, st.st_size);
|
|
if (xid)
|
|
{
|
|
void *xrid = xmlMarkId(xid);
|
|
int r = 0;
|
|
|
|
walk_the_tree(num, xrid, _root);
|
|
|
|
r = xmlErrorGetNo(xrid, 0);
|
|
if (r)
|
|
{
|
|
size_t n = xmlErrorGetLineNo(xrid, 0);
|
|
size_t c = xmlErrorGetColumnNo(xrid, 0);
|
|
const char *s = xmlErrorGetString(xrid, 1); /* clear the error */
|
|
printf("%s: at line %u, column %u: '%s'\n",_filenames[num], n,c, s);
|
|
}
|
|
|
|
free(xrid);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error reading file '%s'\n", _filenames[num]);
|
|
}
|
|
|
|
xmlClose(xid);
|
|
free(buf);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
unsigned int u;
|
|
int i;
|
|
|
|
if (argc == 1)
|
|
show_help();
|
|
|
|
for (i=1; i<argc;)
|
|
{
|
|
int ret = parse_option(argv, i, argc);
|
|
i += ret;
|
|
}
|
|
|
|
if (_root == 0) _root = (char *)_static_root;
|
|
if (_element == 0) _element = (char *)_static_element;
|
|
|
|
for (u=0; u<_fcount; u++)
|
|
#if USE_BUFFER
|
|
grep_file_buffer(u);
|
|
#else
|
|
grep_file(u);
|
|
#endif
|
|
|
|
free_and_exit(0);
|
|
|
|
return 0;
|
|
}
|