dnl Zebra, Index Data Aps, 1995-2002
-dnl $Id: configure.in,v 1.61 2002-12-30 10:25:24 adam Exp $
+dnl $Id: configure.in,v 1.62 2003-02-04 12:06:46 pop Exp $
dnl
AC_INIT(include/zebraver.h)
AM_INIT_AUTOMAKE(idzebra,1.3.4)
doc/zebraphp.dsl
doc/tkl.xsl
test/Makefile test/gils/Makefile test/usmarc/Makefile test/api/Makefile
- perl/Makefile.PL
+ perl/Makefile.PL test/xelm/Makefile
test/dmoz/Makefile test/xpath/Makefile test/sort/Makefile
examples/Makefile examples/gils/Makefile examples/zthes/Makefile
])
-/* $Id: d1_absyn.c,v 1.5 2002-12-16 22:59:34 adam Exp $
+/* $Id: d1_absyn.c,v 1.6 2003-02-04 12:06:46 pop Exp $
Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
Index Data Aps
#include <yaz/oid.h>
#include <yaz/log.h>
#include <data1.h>
+#include <zebra_xpath.h>
#define D1_MAX_NESTING 128
/ -> none
pop, 2002-12-13
+
+ Now [] predicates are supported
+
+ pop, 2003-01-17
+
*/
const char * mk_xpath_regexp (data1_handle dh, char *expr)
{
char *p = expr;
+ char *pp;
+ char *s;
int abs = 1;
int i;
+ int j;
int e=0;
+ int is_predicate = 0;
static char *stack[32];
static char res[1024];
while (*p) {
i=0;
- while (*p && !strchr("/",*p)) { i++; p++; }
+ while (*p && !strchr("/",*p)) {
+ i++; p++;
+ }
stack[e] = (char *) nmem_malloc (data1_nmem_get (dh), i+1);
- memcpy (stack[e], p - i, i);
- stack[e][i] = 0;
+ s = stack[e];
+ for (j=0; j< i; j++) {
+ pp = p-i+j;
+ if (*pp == '[') {
+ is_predicate=1;
+ }
+ else if (*pp == ']') {
+ is_predicate=0;
+ }
+ else {
+ if (!is_predicate) {
+ if (*pp == '*')
+ *s++ = '.';
+ *s++ = *pp;
+ }
+ }
+ }
+ *s = 0;
e++;
if (*p) {p++;}
}
if (!abs) { sprintf (p, ".*"); p+=2; }
sprintf (p, "$"); p++;
r = nmem_strdup (data1_nmem_get (dh), res);
+ yaz_log(LOG_DEBUG,"Got regexp: %s",r);
return (r);
}
return default_value;
}
+#define l_isspace(c) ((c) == '\t' || (c) == ' ' || (c) == '\n' || (c) == '\r')
+
+int read_absyn_line(FILE *f, int *lineno, char *line, int len,
+ char *argv[], int num)
+{
+ char *p;
+ int argc;
+ int quoted = 0;
+
+ while ((p = fgets(line, len, f)))
+ {
+ (*lineno)++;
+ while (*p && l_isspace(*p))
+ p++;
+ if (*p && *p != '#')
+ break;
+ }
+ if (!p)
+ return 0;
+
+ for (argc = 0; *p ; argc++)
+ {
+ if (*p == '#') /* trailing comment */
+ break;
+ argv[argc] = p;
+ while (*p && !(l_isspace(*p) && !quoted)) {
+ if (*p =='"') quoted = 1 - quoted;
+ if (*p =='[') quoted = 1;
+ if (*p ==']') quoted = 0;
+ p++;
+ }
+ if (*p)
+ {
+ *(p++) = '\0';
+ while (*p && l_isspace(*p))
+ p++;
+ }
+ }
+ return argc;
+}
+
+
data1_absyn *data1_read_absyn (data1_handle dh, const char *file,
int file_must_exist)
{
res->main_elements = NULL;
res->xp_elements = NULL;
- while (f && (argc = readconf_line(f, &lineno, line, 512, argv, 50)))
+ while (f && (argc = read_absyn_line(f, &lineno, line, 512, argv, 50)))
{
char *cmd = *argv;
if (!strcmp(cmd, "elm") || !strcmp(cmd, "element"))
maybe we should use a simple sscanf instead of dfa?
pop, 2002-12-13
+
+ Now [] predicates are supported. regexps and xpath structure is
+ a bit redundant, however it's comfortable later...
+
+ pop, 2003-01-17
*/
else if (!strcmp(cmd, "xelm")) {
dfa_mkstate (dfa);
cur_xpelement->dfa = dfa;
-
+
+#ifdef ENHANCED_XELM
+ cur_xpelement->xpath_len = parse_xpath_str(xpath_expr,
+ cur_xpelement->xpath,
+ data1_nmem_get(dh));
+
+ /*
+ dump_xp_steps(cur_xpelement->xpath,cur_xpelement->xpath_len);
+ */
+#endif
cur_xpelement->termlists = 0;
tp = &cur_xpelement->termlists;
data1.h dfa.h dict.h direntz.h isamb.h isamc.h isamd.h isamg.h isam.h \
isams.h mfile.h passwddb.h recctrl.h res.h rsbetween.h rsbool.h rset.h \
rsisamb.h rsisamc.h rsisamd.h rsisam.h rsisams.h rsm_or.h rsnull.h \
-rstemp.h set.h sortidx.h str.h zebra-lock.h zebramap.h zebrautl.h zebraver.h
+rstemp.h set.h sortidx.h str.h zebra-lock.h zebramap.h zebrautl.h \
+zebra_xpath.h zebraver.h
-/* $Id: data1.h,v 1.4 2002-12-16 20:27:18 adam Exp $
+/* $Id: data1.h,v 1.5 2003-02-04 12:06:47 pop Exp $
Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
Index Data Aps
#ifndef DATA1_H
#define DATA1_H
+#define ENHANCED_XELM 1
+
#include <stdio.h>
#include <yaz/nmem.h>
#include <yaz/yaz-util.h>
#include <yaz/wrbuf.h>
#include <dfa.h> /* pop */
+#include <zebra_xpath.h> /* pop */
#define d1_isspace(c) strchr(" \r\n\t\f", c)
#define d1_isdigit(c) ((c) <= '9' && (c) >= '0')
typedef struct data1_xpelement
{
char *xpath_expr;
+#ifdef ENHANCED_XELM
+ struct xpath_location_step xpath[XPATH_STEP_COUNT];
+ int xpath_len;
+#endif
struct DFA *dfa;
data1_termlist *termlists;
struct data1_xpelement *next;
--- /dev/null
+/* Moved from zrpn.c -pop */
+
+#ifndef ZEBRA_XPATH_H
+#define ZEBRA_XPATH_H
+
+#define XPATH_STEP_COUNT 10
+struct xpath_predicate {
+ int which;
+ union {
+#define XPATH_PREDICATE_RELATION 1
+ struct {
+ char *name;
+ char *op;
+ char *value;
+ } relation;
+#define XPATH_PREDICATE_BOOLEAN 2
+ struct {
+ const char *op;
+ struct xpath_predicate *left;
+ struct xpath_predicate *right;
+ } boolean;
+ } u;
+};
+
+struct xpath_location_step {
+ char *part;
+ struct xpath_predicate *predicate;
+};
+
+int parse_xpath_str(const char *xpath_string,
+ struct xpath_location_step *xpath, NMEM mem);
+
+void dump_xp_steps (struct xpath_location_step *xpath, int no);
+
+#endif
-## $Id: Makefile.am,v 1.17 2002-11-26 22:18:17 adam Exp $
+## $Id: Makefile.am,v 1.18 2003-02-04 12:06:47 pop Exp $
noinst_PROGRAMS = apitest kdump
libzebra_a_SOURCES = dir.c dirs.c trav.c kinput.c kcompare.c \
attribute.c symtab.c recindex.c recstat.c lockutil.c \
zebraapi.c zinfo.c invstat.c sortidx.c compact.c zsets.c zrpn.c \
- rank1.c trunc.c retrieve.c extract.c index.h recindex.h recindxp.h \
+ rank1.c trunc.c retrieve.c extract.c \
+ index.h recindex.h recindxp.h \
zebraapi.h zinfo.h zserver.h
libzebra_a_LIBADD = \
-/* $Id: zrpn.c,v 1.126 2002-12-16 22:59:34 adam Exp $
+/* $Id: zrpn.c,v 1.127 2003-02-04 12:06:47 pop Exp $
Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
Index Data Aps
#include <ctype.h>
#include "index.h"
+#include <zebra_xpath.h>
#include <charmap.h>
#include <rstemp.h>
int reg_type;
};
+typedef struct {
+ int type;
+ int major;
+ int minor;
+ Z_AttributesPlusTerm *zapt;
+} AttrType;
+
+
static const char **rpn_char_map_handler (void *vp, const char **from, int len)
{
struct rpn_char_map_info *p = (struct rpn_char_map_info *) vp;
dict_grep_cmap (reg->dict, map_info, rpn_char_map_handler);
}
-typedef struct {
- int type;
- int major;
- int minor;
- Z_AttributesPlusTerm *zapt;
-} AttrType;
-
static int attr_find_ex (AttrType *src, oid_value *attributeSetP,
const char **string_value)
{
return rset_create (rset_kind_null, &parms);
}
+/* pop - moved to xpath.c */
+#if 0
+
struct xpath_predicate {
int which;
union {
struct xpath_predicate *predicate;
};
+#endif
+
static int parse_xpath(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
oid_value attributeSet,
struct xpath_location_step *xpath, NMEM mem)
oid_value curAttributeSet = attributeSet;
AttrType use;
const char *use_string = 0;
- const char *cp;
int no = 0;
attr_init (&use, zapt, 1);
if (!use_string || *use_string != '/')
return -1;
- cp = use_string;
- while (*cp)
- {
- int i = 0;
- while (*cp && !strchr("/[",*cp))
- {
- i++;
- cp++;
- }
- xpath[no].predicate = 0;
- xpath[no].part = nmem_malloc (mem, i+1);
- memcpy (xpath[no].part, cp - i, i);
- xpath[no].part[i] = 0;
- if (*cp == '[')
- {
- struct xpath_predicate *p = xpath[no].predicate =
- nmem_malloc (mem, sizeof(struct xpath_predicate));
-
- p->which = XPATH_PREDICATE_RELATION;
- cp++;
- while (*cp == ' ')
- cp++;
-
- for (i = 0; *cp && !strchr("><=] ", *cp); i++)
- cp++;
- p->u.relation.name = nmem_malloc (mem, i+1);
- memcpy (p->u.relation.name, cp - i, i);
- p->u.relation.name[i] = 0;
- while (*cp == ' ')
- cp++;
- if (*cp != ']')
- {
- for (i = 0; *cp && strchr(">=<!", *cp); i++)
- cp++;
-
- p->u.relation.op = nmem_malloc (mem, i+1);
- if (i)
- memcpy (p->u.relation.op, cp - i, i);
- p->u.relation.op[i] = 0;
-
- while (*cp == ' ')
- cp++;
-
- if (strchr("\"'", *cp))
- {
- cp++;
- for (i = 0; *cp && !strchr("\"'", *cp); i++)
- cp++;
-
- p->u.relation.value = nmem_malloc (mem, i+1);
- if (i)
- memcpy (p->u.relation.value, cp - i, i);
- p->u.relation.value[i] = 0;
- yaz_log (LOG_LOG, "value=%s", p->u.relation.value);
-
- cp++;
- }
- else
- {
- for (i = 0; *cp && !strchr(" ]", *cp); i++)
- cp++;
- p->u.relation.value = nmem_malloc (mem, i+1);
- if (i)
- memcpy (p->u.relation.value, cp - i, i);
- p->u.relation.value[i] = 0;
- }
- while (*cp == ' ')
- cp++;
- }
- if (*cp == ']')
- cp++;
- } /* end of ] predicate */
- no++;
- if (*cp != '/')
- break;
- cp++;
- }
- return no;
+ return (parse_xpath_str(use_string, xpath, mem));
}
-
+
+
static RSET xpath_trunc(ZebraHandle zh, NMEM stream,
int reg_type, const char *term, int use,
-/* $Id: isamb.c,v 1.21 2002-09-03 19:05:02 adam Exp $
+/* $Id: isamb.c,v 1.22 2003-02-04 12:06:47 pop Exp $
Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
Index Data Aps
02111-1307, USA.
*/
-
+#include <string.h>
#include <yaz/xmalloc.h>
#include <yaz/log.h>
#include <isamb.h>
-/* $Id: recgrs.c,v 1.71 2002-12-16 20:27:18 adam Exp $
+/* $Id: recgrs.c,v 1.72 2003-02-04 12:06:47 pop Exp $
Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
Index Data Aps
xfree (h);
}
+int d1_check_xpath_predicate(data1_node *n, struct xpath_predicate *p) {
+ int res = 1;
+ char *attname;
+ data1_xattr *attr;
+
+ if (!p) {
+ return (1);
+ } else {
+ if (p->which == XPATH_PREDICATE_RELATION) {
+ if (p->u.relation.name[0]) {
+ if (*p->u.relation.name != '@') {
+ logf(LOG_WARN,
+ " Only attributes (@) are supported in xelm xpath predicates");
+ logf(LOG_WARN, "predicate %s ignored", p->u.relation.name);
+ return (1);
+ }
+ attname = p->u.relation.name + 1;
+ res = 0;
+ /* looking for the attribute with a specified name */
+ for (attr = n->u.tag.attributes; attr; attr = attr->next) {
+ logf(LOG_DEBUG," - attribute %s <-> %s", attname, attr->name );
+
+ if (!strcmp(attr->name, attname)) {
+ if (p->u.relation.op[0]) {
+ if (*p->u.relation.op != '=') {
+ logf(LOG_WARN,
+ "Only '=' relation is supported (%s)",p->u.relation.op);
+ logf(LOG_WARN, "predicate %s ignored", p->u.relation.name);
+ res = 1; break;
+ } else {
+ logf(LOG_DEBUG," - value %s <-> %s",
+ p->u.relation.value, attr->value );
+ if (!strcmp(attr->value, p->u.relation.value)) {
+ res = 1; break;
+ }
+ }
+ } else {
+ /* attribute exists, no value specified */
+ res = 1; break;
+ }
+ }
+ }
+ return (res);
+ } else {
+ return (1);
+ }
+ }
+ else if (p->which == XPATH_PREDICATE_BOOLEAN) {
+ if (!strcmp(p->u.boolean.op,"and")) {
+ return (d1_check_xpath_predicate(n, p->u.boolean.left)
+ && d1_check_xpath_predicate(n, p->u.boolean.right));
+ }
+ else if (!strcmp(p->u.boolean.op,"or")) {
+ return (d1_check_xpath_predicate(n, p->u.boolean.left)
+ || d1_check_xpath_predicate(n, p->u.boolean.right));
+ } else {
+ logf(LOG_WARN, "Unknown boolean relation %s, ignored",p->u.boolean.op);
+ return (1);
+ }
+ }
+ }
+ return 0;
+}
+
+
/* *ostrich*
New function, looking for xpath "element" definitions in abs, by
against the given tagpath. The first matching entry is returned.
pop, 2002-12-13
+
+ Added support for enhanced xelm. Now [] predicates are considered
+ as well, when selecting indexing rules... (why the hell it's called
+ termlist???)
+
+ pop, 2003-01-17
+
*/
data1_termlist *xpath_termlist_by_tagpath(char *tagpath, data1_node *n)
{
data1_absyn *abs = n->root->u.root.absyn;
data1_xpelement *xpe = abs->xp_elements;
+ data1_node *nn;
+#ifdef ENHANCED_XELM
+ struct xpath_location_step *xp;
+
+#endif
char *pexpr = malloc(strlen(tagpath)+2);
int ok = 0;
} while (i >= 0);
}
pexpr--;
- if (ok) break;
+ if (ok) {
+#ifdef ENHANCED_XELM
+ /* we have to check the perdicates up to the root node */
+ xp = xpe->xpath;
+
+ /* find the first tag up in the node structure */
+ nn = n; while (nn && nn->which != DATA1N_tag) {
+ nn = nn->parent;
+ }
+
+ /* go from inside out in the node structure, while going
+ backwards trough xpath location steps ... */
+ for (i=xpe->xpath_len - 1; i>0; i--) {
+
+ logf(LOG_DEBUG,"Checking step %d: %s on tag %s",
+ i,xp[i].part,nn->u.tag.tag);
+
+ if (!d1_check_xpath_predicate(nn, xp[i].predicate)) {
+ logf(LOG_DEBUG," Predicates didn't match");
+ ok = 0;
+ break;
+ }
+
+ if (nn->which == DATA1N_tag) {
+ nn = nn->parent;
+ }
+ }
+#endif
+ if (ok) {
+ break;
+ }
+ }
xpe = xpe->next;
}
if (ok) {
+ logf(LOG_DEBUG,"Got it");
return xpe->termlists;
} else {
return NULL;
tag_path_full[flen] = 0;
/* If we have a matching termlist... */
- if (tl = xpath_termlist_by_tagpath(tag_path_full, n)) {
+ if ((tl = xpath_termlist_by_tagpath(tag_path_full, n))) {
for (; tl; tl = tl->next) {
wrd->reg_type = *tl->structure;
/* this is the ! case, so structure is for the xpath index */
wrd->length = strlen(xp->value);
wrd->reg_type = 'w';
- if (tl = xpath_termlist_by_tagpath(attr_tag_path_full,
- n)) {
+ if ((tl = xpath_termlist_by_tagpath(attr_tag_path_full,
+ n))) {
for (; tl; tl = tl->next) {
wrd->reg_type = *tl->structure;
if (!tl->att) {
-## $Id: Makefile.am,v 1.6 2002-10-30 14:35:09 adam Exp $
+## $Id: Makefile.am,v 1.7 2003-02-04 12:06:48 pop Exp $
noinst_LIBRARIES = libutil.a
AM_CPPFLAGS = -I$(srcdir)/../include @YAZINC@ -DDEFAULT_PROFILE_PATH=\"$(pkgdatadir)/tab\"
LDADD = libutil.a $(YAZLIB) $(TCL_LIB)
-libutil_a_SOURCES = res.c charmap.c zebramap.c passwddb.c zebra-lock.c dirent.c
+libutil_a_SOURCES = res.c charmap.c zebramap.c passwddb.c zebra-lock.c dirent.c xpath.c
passtest_SOURCES = passtest.c
--- /dev/null
+/* $Id: xpath.c,v 1.1 2003-02-04 12:06:48 pop Exp $
+ Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
+ Index Data Aps
+
+This file is part of the Zebra server.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty 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 Zebra; see the file LICENSE.zebra. If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <yaz/nmem.h>
+#include <zebra_xpath.h>
+
+char *get_xp_part (char **strs, NMEM mem) {
+ char *str = *strs;
+ char *res = '\0';
+ char *cp = str;
+ char *co;
+ int quoted = 0;
+
+ /* ugly */
+ char *sep = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\" ";
+
+ while (*cp == ' ') {cp++; str++;}
+ if (!strchr("><=] ", *cp)) sep = "><=] ";
+
+ while (*cp && !(strchr(sep,*cp) && !quoted) && (*cp != ']')) {
+ if (*cp =='"') quoted = 1 - quoted;
+ cp++;
+ }
+ /* removing leading and trailing " */
+ co = cp;
+ if (*str == '"') str++;
+ if (*(cp-1) == '"') cp--;
+ if (str < co) {
+ res = nmem_malloc(mem, cp - str + 1);
+ memcpy (res, str, (cp-str));
+ *(res + (cp-str)) = '\0';
+ *strs = co;
+ }
+
+ return (res);
+}
+
+
+struct xpath_predicate *get_xpath_predicate(char *predicates, NMEM mem) {
+ char *p1;
+ char *p2;
+ char *p3;
+ char *p4;
+
+ struct xpath_predicate *r1;
+ struct xpath_predicate *r2;
+ struct xpath_predicate *res = 0;
+
+ char *pr = predicates;
+
+ if ((p1 = get_xp_part(&pr, mem))) {
+ if ((p2 = get_xp_part(&pr, mem))) {
+ if (!strcmp (p2, "and") || !strcmp (p2, "or") || !strcmp (p2, "not")) {
+ r1=nmem_malloc(mem, sizeof(struct xpath_predicate));
+ r1->which = XPATH_PREDICATE_RELATION;
+ r1->u.relation.name = p1;
+ r1->u.relation.op = "";
+ r1->u.relation.value = "";
+
+ r2 = get_xpath_predicate (pr, mem);
+
+ res = nmem_malloc(mem, sizeof(struct xpath_predicate));
+ res->which = XPATH_PREDICATE_BOOLEAN;
+ res->u.boolean.op = p2;
+ res->u.boolean.left = r1;
+ res->u.boolean.right = r2;
+
+ return (res);
+ }
+
+ if (strchr("><=] ", *p2)) {
+ r1 = nmem_malloc(mem, sizeof(struct xpath_predicate));
+
+ r1->which = XPATH_PREDICATE_RELATION;
+ r1->u.relation.name = p1;
+ r1->u.relation.op = p2;
+
+ if ((p3 = get_xp_part(&pr, mem))) {
+ r1->u.relation.value = p3;
+ } else {
+ /* error */
+ }
+ }
+
+ if ((p4 = get_xp_part(&pr, mem))) {
+ if (!strcmp (p4, "and") || !strcmp (p4, "or") || !strcmp (p4, "not")) {
+
+ r2 = get_xpath_predicate (pr, mem);
+
+ res = nmem_malloc(mem, sizeof(struct xpath_predicate));
+ res->which = XPATH_PREDICATE_BOOLEAN;
+ res->u.boolean.op = p4;
+ res->u.boolean.left = r1;
+ res->u.boolean.right = r2;
+ return (res);
+ } else {
+ /* error */
+ }
+ } else {
+ return (r1);
+ }
+
+ } else {
+ r1 = nmem_malloc(mem, sizeof(struct xpath_predicate));
+
+ r1->which = XPATH_PREDICATE_RELATION;
+ r1->u.relation.name = p1;
+ r1->u.relation.op = "";
+ r1->u.relation.value = "";
+
+ return (r1);
+ }
+ }
+ return 0;
+}
+
+int parse_xpath_str(const char *xpath_string,
+ struct xpath_location_step *xpath, NMEM mem)
+{
+ const char *cp;
+ char *a;
+
+ int no = 0;
+
+ if (!xpath_string || *xpath_string != '/')
+ return -1;
+ cp = xpath_string;
+
+ while (*cp)
+ {
+ int i = 0;
+ while (*cp && !strchr("/[",*cp))
+ {
+ i++;
+ cp++;
+ }
+ xpath[no].predicate = 0;
+ xpath[no].part = nmem_malloc (mem, i+1);
+ memcpy (xpath[no].part, cp - i, i);
+ xpath[no].part[i] = 0;
+
+ if (*cp == '[')
+ {
+ cp++;
+ while (*cp == ' ')
+ cp++;
+
+ a = (char *)cp;
+ xpath[no].predicate = get_xpath_predicate(a, mem);
+ while(*cp && *cp != ']') {
+ cp++;
+ }
+ if (*cp == ']')
+ cp++;
+ } /* end of ] predicate */
+ no++;
+ if (*cp != '/')
+ break;
+ cp++;
+ }
+ return no;
+}
+
+void dump_xp_predicate (struct xpath_predicate *p) {
+ if (p) {
+ if (p->which == XPATH_PREDICATE_RELATION &&
+ p->u.relation.name[0]) {
+ fprintf (stderr, "%s,%s,%s",
+ p->u.relation.name,
+ p->u.relation.op,
+ p->u.relation.value);
+ } else {
+ fprintf (stderr, "(");
+ dump_xp_predicate(p->u.boolean.left);
+ fprintf (stderr, ") %s (", p->u.boolean.op);
+ dump_xp_predicate(p->u.boolean.right);
+ fprintf (stderr, ")");
+ }
+ }
+}
+
+void dump_xp_steps (struct xpath_location_step *xpath, int no) {
+ int i;
+ for (i=0; i<no; i++) {
+ fprintf (stderr, "Step %d: %s ",i,xpath[i].part);
+ dump_xp_predicate(xpath[i].predicate);
+ fprintf (stderr, "\n");
+ }
+}
+