xelm support enhanced, now supports simpler predicates
authorpop <pop>
Tue, 4 Feb 2003 12:06:46 +0000 (12:06 +0000)
committerpop <pop>
Tue, 4 Feb 2003 12:06:46 +0000 (12:06 +0000)
configure.in
data1/d1_absyn.c
include/Makefile.am
include/data1.h
include/zebra_xpath.h [new file with mode: 0644]
index/Makefile.am
index/zrpn.c
isamb/isamb.c
recctrl/recgrs.c
util/Makefile.am
util/xpath.c [new file with mode: 0644]

index 317ef3f..3da6705 100644 (file)
@@ -1,5 +1,5 @@
 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)
@@ -333,7 +333,7 @@ AC_OUTPUT([
   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
 ])
index e2434c4..c7f8ba7 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
 
@@ -28,6 +28,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <yaz/oid.h>
 #include <yaz/log.h>
 #include <data1.h>
+#include <zebra_xpath.h>
 
 #define D1_MAX_NESTING  128
 
@@ -282,14 +283,23 @@ void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
       /      ->    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];
@@ -301,10 +311,28 @@ const char * mk_xpath_regexp (data1_handle dh, char *expr)
     
     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++;}
     }
@@ -319,6 +347,7 @@ const char * mk_xpath_regexp (data1_handle dh, char *expr)
     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);
 }
 
@@ -399,6 +428,48 @@ const char *data1_systag_lookup(data1_absyn *absyn, const char *tag,
     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)
 {
@@ -451,7 +522,7 @@ data1_absyn *data1_read_absyn (data1_handle dh, const char *file,
     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"))
@@ -582,6 +653,11 @@ data1_absyn *data1_read_absyn (data1_handle dh, const char *file,
           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")) {
@@ -624,7 +700,16 @@ data1_absyn *data1_read_absyn (data1_handle dh, const char *file,
            
            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;
             
index 2d36402..f1d5408 100644 (file)
@@ -3,4 +3,5 @@ noinst_HEADERS = bfile.h bset.h charmap.h d1_attset.h d1_map.h \
 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
index 8b48a06..34dd37d 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
 
@@ -23,6 +23,8 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #ifndef DATA1_H
 #define DATA1_H
 
+#define ENHANCED_XELM 1
+
 #include <stdio.h>
 
 #include <yaz/nmem.h>
@@ -34,6 +36,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #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')
@@ -192,6 +195,10 @@ typedef struct data1_sub_elements {
 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;
diff --git a/include/zebra_xpath.h b/include/zebra_xpath.h
new file mode 100644 (file)
index 0000000..d09bf16
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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
index 062b0b4..1f3f2b8 100644 (file)
@@ -1,4 +1,4 @@
-## $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
 
@@ -7,7 +7,8 @@ noinst_LIBRARIES = libzebra.a
 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 = \
index 0a2c29c..2f35cfa 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
 
@@ -31,6 +31,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <ctype.h>
 
 #include "index.h"
+#include <zebra_xpath.h>
 
 #include <charmap.h>
 #include <rstemp.h>
@@ -43,6 +44,14 @@ struct rpn_char_map_info {
     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;
@@ -70,13 +79,6 @@ static void rpn_char_map_prepare (struct zebra_register *reg, int reg_type,
     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)
 {
@@ -2034,6 +2036,9 @@ static RSET rpn_sort_spec (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
     return rset_create (rset_kind_null, &parms);
 }
 
+/* pop - moved to xpath.c */
+#if 0
+
 struct xpath_predicate {
     int which;
     union {
@@ -2057,6 +2062,8 @@ struct xpath_location_step {
     struct xpath_predicate *predicate;
 };
 
+#endif
+
 static int parse_xpath(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
                        oid_value attributeSet,
                        struct xpath_location_step *xpath, NMEM mem)
@@ -2064,7 +2071,6 @@ static int parse_xpath(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
     oid_value curAttributeSet = attributeSet;
     AttrType use;
     const char *use_string = 0;
-    const char *cp;
     int no = 0;
     
     attr_init (&use, zapt, 1);
@@ -2072,87 +2078,11 @@ static int parse_xpath(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
 
     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,
index ca6ba68..4fa613a 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
 
@@ -20,7 +20,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA.
 */
 
-
+#include <string.h>
 #include <yaz/xmalloc.h>
 #include <yaz/log.h>
 #include <isamb.h>
index 4f3ab50..ae5a782 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
 
@@ -123,6 +123,71 @@ static void grs_destroy(void *clientData)
     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
@@ -131,12 +196,24 @@ static void grs_destroy(void *clientData)
    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;
     
@@ -168,11 +245,43 @@ data1_termlist *xpath_termlist_by_tagpath(char *tagpath, data1_node *n)
             } 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;
@@ -237,7 +346,7 @@ static void index_xpath (data1_node *n, struct recExtractCtrl *p,
             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 */
@@ -354,8 +463,8 @@ static void index_xpath (data1_node *n, struct recExtractCtrl *p,
                         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) {
index 5c859a2..fac1cb9 100644 (file)
@@ -1,4 +1,4 @@
-## $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
 
@@ -9,6 +9,6 @@ EXTRA_DIST = zebrasrv.rh
 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
diff --git a/util/xpath.c b/util/xpath.c
new file mode 100644 (file)
index 0000000..1155cb1
--- /dev/null
@@ -0,0 +1,214 @@
+/* $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");
+  }
+}
+