ZOOM: For redirect, reconnect always YAZ-722
[yaz-moved-to-github.git] / src / cql2ccl.c
index 90ee5d6..d08536f 100644 (file)
@@ -1,10 +1,10 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2011 Index Data
+ * Copyright (C) Index Data
  * See the file LICENSE for details.
  */
 /**
- * \file xcqlutil.c
- * \brief Implements CQL to XCQL conversion.
+ * \file cql2ccl.c
+ * \brief Implements CQL to CCL conversion.
  */
 #if HAVE_CONFIG_H
 #include <config.h>
 
 #include <yaz/cql.h>
 
-static int cql_to_ccl_r(struct cql_node *cn, 
+static int cql_to_ccl_r(struct cql_node *cn,
                         void (*pr)(const char *buf, void *client_data),
                         void *client_data);
 
-static void pr_term(struct cql_node *cn,
-                void (*pr)(const char *buf, void *client_data),
-                void *client_data)
+static void pr_term(const char **cpp, int stop_at_space,
+                    void (*pr)(const char *buf, void *client_data),
+                    void *client_data)
 {
-    while (cn)
+    const char *cp;
+    int quote_mode = 0;
+    for (cp = *cpp; *cp; cp++)
     {
-        pr("\"", client_data);
-        pr(cn->u.st.term, client_data);
-        pr("\"", client_data);
-        if (cn->u.st.extra_terms)
-            pr(" ", client_data);
-        cn = cn->u.st.extra_terms;
+        char x[4];
+
+        if (*cp == '\\' && cp[1])
+        {
+            if (!quote_mode)
+            {
+                pr("\"", client_data);
+                quote_mode = 1;
+            }
+            cp++;
+            if (*cp == '\"' || *cp == '\\')
+                pr("\\", client_data);
+            x[0] = *cp;
+            x[1] = '\0';
+            pr(x, client_data);
+        }
+        else if (*cp == '*')
+        {
+            if (quote_mode)
+            {
+                pr("\"", client_data);
+                quote_mode = 0;
+            }
+            pr("?", client_data);
+        }
+        else if (*cp == '?')
+        {
+            if (quote_mode)
+            {
+                pr("\"", client_data);
+                quote_mode = 0;
+            }
+            pr("#", client_data);
+        }
+        else if (*cp == ' ' && stop_at_space)
+            break;
+        else
+        {
+            if (!quote_mode)
+            {
+                pr("\"", client_data);
+                quote_mode = 1;
+            }
+            x[0] = *cp;
+            x[1] = '\0';
+            pr(x, client_data);
+        }
     }
+    if (quote_mode)
+        pr("\"", client_data);
+    if (cp == *cpp)
+        pr("\"\"", client_data);
+    *cpp = cp;
 }
 
-static int node(struct cql_node *cn, 
+static int node(struct cql_node *cn,
                 void (*pr)(const char *buf, void *client_data),
                 void *client_data)
 {
@@ -73,71 +121,113 @@ static int node(struct cql_node *cn,
         /* unsupported relation */
         return -1;
     }
-    if (!split_op)
-    {
-        if (ccl_field && ccl_rel)
-        {
-            pr(ccl_field, client_data);
-            pr(ccl_rel, client_data);
-        }
-        pr_term(cn, pr, client_data);
-    }
-    else
+    for (; cn; cn = cn->u.st.extra_terms)
     {
         const char *cp = cn->u.st.term;
-        
         while (1)
         {
-            if (*cp == '\0')
-                break;
             if (ccl_field && ccl_rel)
             {
                 pr(ccl_field, client_data);
                 pr(ccl_rel, client_data);
+                if (!split_op)
+                    ccl_rel = 0;
             }
-            while (*cp && *cp != ' ')
-            {
-                char x[2];
-                x[0] = *cp;
-                x[1] = '\0';
-                pr(x, client_data);
-                cp++;
-            }
+            pr_term(&cp, split_op ? 1 : 0, pr, client_data);
             while (*cp == ' ')
                 cp++;
             if (*cp == '\0')
                 break;
             pr(" ", client_data);
-            pr(split_op, client_data);
-            pr(" ", client_data);            
+            if (split_op)
+            {
+                pr(split_op, client_data);
+                pr(" ", client_data);
+            }
+        }
+        if (cn->u.st.extra_terms)
+        {
+            pr(" ", client_data);
+            if (split_op)
+            {
+                pr(split_op, client_data);
+                pr(" ", client_data);
+            }
         }
-        return -1;
     }
     return 0;
 }
 
 
-static int bool(struct cql_node *cn, 
+static int bool(struct cql_node *cn,
                 void (*pr)(const char *buf, void *client_data),
                 void *client_data)
 {
+    char *value = cn->u.boolean.value;
     int r;
 
     pr("(", client_data);
     r = cql_to_ccl_r(cn->u.boolean.left, pr, client_data);
     if (r)
         return r;
-    
-    pr(" ", client_data);
-    pr(cn->u.boolean.value, client_data);
-    pr(" ", client_data);
+
+    pr(") ", client_data);
+
+    if (strcmp(value, "prox"))
+    {   /* not proximity. assuming boolean */
+        pr(value, client_data);
+    }
+    else
+    {
+        struct cql_node *n = cn->u.boolean.modifiers;
+        int ordered = 0;
+        int distance = 1;
+        for (; n ; n = n->u.st.modifiers)
+            if (n->which == CQL_NODE_ST)
+            {
+                if (!strcmp(n->u.st.index, "unit"))
+                {
+                    if (!strcmp(n->u.st.term, "word"))
+                        ;
+                    else
+                        return -1;
+                }
+                else if (!strcmp(n->u.st.index, "distance"))
+                {
+                    if (!strcmp(n->u.st.relation, "<="))
+                        distance = atoi(n->u.st.term);
+                    else if (!strcmp(n->u.st.relation, "<"))
+                            distance = atoi(n->u.st.term) - 1;
+                    else
+                        return -1;
+                }
+                else if (!strcmp(n->u.st.index, "unordered"))
+                {
+                    ordered = 0;
+                }
+                else if (!strcmp(n->u.st.index, "ordered"))
+                {
+                    ordered = 1;
+                }
+                else
+                    return -1;
+            }
+        pr(ordered ? "!" : "%", client_data);
+        if (distance != 1)
+        {
+            char x[40];
+            sprintf(x, "%d", distance);
+            pr(x, client_data);
+        }
+    }
+    pr(" (", client_data);
 
     r = cql_to_ccl_r(cn->u.boolean.right, pr, client_data);
     pr(")", client_data);
     return r;
 }
 
-static int cql_to_ccl_r(struct cql_node *cn, 
+static int cql_to_ccl_r(struct cql_node *cn,
                         void (*pr)(const char *buf, void *client_data),
                         void *client_data)
 {
@@ -156,7 +246,7 @@ static int cql_to_ccl_r(struct cql_node *cn,
     return -1;
 }
 
-int cql_to_ccl(struct cql_node *cn, 
+int cql_to_ccl(struct cql_node *cn,
                void (*pr)(const char *buf, void *client_data),
                void *client_data)
 {
@@ -171,13 +261,16 @@ void cql_to_ccl_stdio(struct cql_node *cn, FILE *f)
 int cql_to_ccl_buf(struct cql_node *cn, char *out, int max)
 {
     struct cql_buf_write_info info;
+    int r;
     info.off = 0;
     info.max = max;
     info.buf = out;
-    cql_to_ccl(cn, cql_buf_write_handler, &info);
+    r = cql_to_ccl(cn, cql_buf_write_handler, &info);
     if (info.off >= 0)
         info.buf[info.off] = '\0';
-    return info.off;
+    else
+        return -2; /* buffer overflow */
+    return r;
 }
 
 /*