Set the default CQL qualifier file to /usr/local/share/yaz/etc/pqf.properties
[yaz-moved-to-github.git] / client / client.c
index 9c3ecd5..38ea168 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1995-2003, Index Data
  * See the file LICENSE for details.
  *
- * $Id: client.c,v 1.190 2003-04-29 21:53:54 adam Exp $
+ * $Id: client.c,v 1.197 2003-05-22 23:55:19 mike Exp $
  */
 
 #include <stdio.h>
@@ -41,6 +41,7 @@
 #include <yaz/ill.h>
 #include <yaz/srw.h>
 #include <yaz/yaz-ccl.h>
+#include <yaz/cql.h>
 
 #if HAVE_READLINE_READLINE_H
 #include <readline/readline.h>
 static char *codeset = 0;               /* character set for output */
 
 static ODR out, in, print;              /* encoding and decoding streams */
+#ifdef THESE_ARE_NOT_USED
 static ODR srw_sr_odr_out = 0;
 static Z_SRW_PDU *srw_sr = 0;
+#endif /*THESE_ARE_NOT_USED*/
 static FILE *apdu_file = 0;
 static FILE *ber_file = 0;
 static COMSTACK conn = 0;               /* our z-association */
@@ -77,7 +80,6 @@ static Z_ElementSetNames *elementSetNames = 0;
 static int setno = 1;                   /* current set offset */
 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
 static enum oid_value recordsyntax = VAL_USMARC;
-//static enum oid_value schema = VAL_NONE;
 static char *schema = 0;
 static int sent_close = 0;
 static NMEM session_mem = NULL;         /* memory handle for init-response */
@@ -85,10 +87,14 @@ static Z_InitResponse *session = 0;     /* session parameters */
 static char last_scan_line[512] = "0";
 static char last_scan_query[512] = "0";
 static char ccl_fields[512] = "default.bib";
-static char* esPackageName = 0;
-static char* yazProxy = 0;
+/* ### How can I set this path to use wherever YAZ is installed? */
+static char cql_fields[512] = "/usr/local/share/yaz/etc/pqf.properties";
+static char *esPackageName = 0;
+static char *yazProxy = 0;
 static int kilobytes = 1024;
-static char* yazCharset = 0;
+static char *negotiationCharset = 0;
+static char *outputCharset = 0;
+static char *marcCharset = 0;
 static char* yazLang = 0;
 
 static char last_cmd[32] = "?";
@@ -101,12 +107,14 @@ typedef enum {
     QueryType_Prefix,
     QueryType_CCL,
     QueryType_CCL2RPN,
-    QueryType_CQL
+    QueryType_CQL,
+    QueryType_CQL2RPN
 } QueryType;
 
 static QueryType queryType = QueryType_Prefix;
 
 static CCL_bibset bibset;               /* CCL bibset handle */
+static cql_transform_t cqltrans;       /* CQL qualifier-set handle */
 
 #if HAVE_READLINE_COMPLETION_OVER
 
@@ -145,6 +153,7 @@ const char* query_type_as_string(QueryType q)
     case QueryType_CCL: return "CCL (CCL sent to server) ";
     case QueryType_CCL2RPN: return "CCL -> RPN (RPN sent to server)";
     case QueryType_CQL: return "CQL (CQL sent to server)";
+    case QueryType_CQL2RPN: return "CQL -> RPN (RPN sent to server)";
     default: 
         return "unknown Query type internal yaz-client error";
     }
@@ -181,7 +190,7 @@ void add_otherInfos(Z_APDU *a)
     }   
 }
 
-void send_apdu(Z_APDU *a)
+int send_apdu(Z_APDU *a)
 {
     char *buf;
     int len;
@@ -197,7 +206,7 @@ void send_apdu(Z_APDU *a)
     {
         odr_perror(out, "Encoding APDU");
         close_session();
-        return;
+        return 0;
     }
     buf = odr_getbuf(out, &len, 0);
     if (ber_file)
@@ -207,10 +216,11 @@ void send_apdu(Z_APDU *a)
     {
         fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
         close_session();
-        return;
+        return 0;
     }
     do_hex_dump(buf,len);
     odr_reset(out); /* release the APDU structure  */
+    return 1;
 }
 
 static void print_stringn(const unsigned char *buf, size_t len)
@@ -276,7 +286,7 @@ static void send_initRequest(const char* type_and_host)
         yaz_oi_set_string_oidval(&req->otherInfo, out, VAL_PROXY,
         1, type_and_host);
     
-    if (yazCharset || yazLang) {
+    if (negotiationCharset || yazLang) {
        Z_OtherInformation **p;
        Z_OtherInformationUnit *p0;
        
@@ -287,14 +297,16 @@ static void send_initRequest(const char* type_and_host)
                
                p0->which = Z_OtherInfo_externallyDefinedInfo;
                p0->information.externallyDefinedInfo =
-                       yaz_set_proposal_charneg(out,
-                               (const char**)&yazCharset, (yazCharset)?1:0,
-                               (const char**)&yazLang, (yazLang)?1:0, 1);
+                       yaz_set_proposal_charneg(
+                            out,
+                            (const char**)&negotiationCharset, 
+                            negotiationCharset ? 1 : 0,
+                            (const char**)&yazLang, yazLang ? 1 : 0, 1);
        }
     }
     
-    send_apdu(apdu);
-    printf("Sent initrequest.\n");
+    if (send_apdu(apdu))
+        printf("Sent initrequest.\n");
 }
 
 static int process_initResponse(Z_InitResponse *res)
@@ -494,6 +506,7 @@ int session_connect(const char *arg)
         return 0;
     }
 #endif
+    protocol = conn->protocol;
     if (conn->protocol == PROTO_HTTP)
         set_base("");
     else
@@ -516,7 +529,7 @@ int session_connect(const char *arg)
     printf("OK.\n");
     if (basep && *basep)
         set_base (basep);
-    if (conn->protocol == PROTO_Z3950)
+    if (protocol == PROTO_Z3950)
     {
         send_initRequest(type_and_host);
         return 2;
@@ -703,20 +716,25 @@ static void display_record(Z_External *r)
                                         &result, &rlen)> 0)
                 {
                     char *from = 0;
-                    if (ent->value == VAL_USMARC)
+                    if (marcCharset && strcmp(marcCharset, "auto"))
+                        from = marcCharset;
+                    else
                     {
-                        if (octet_buf[9] == 'a')
-                            from = "UTF-8";
+                        if (ent->value == VAL_USMARC)
+                        {
+                            if (octet_buf[9] == 'a')
+                                from = "UTF-8";
+                            else
+                                from = "MARC8";
+                        }
                         else
-                            from = "MARC8";
+                            from = "ISO-8859-1";
                     }
-                    else
-                        from = "ISO-8859-1";
-
-                    if (codeset && from)
+                    if (outputCharset && from)
                     {   
-                        printf ("convert from %s to %s\n", from, codeset);
-                        cd = yaz_iconv_open(codeset, from);
+                        printf ("convert from %s to %s\n", from, 
+                                outputCharset);
+                        cd = yaz_iconv_open(outputCharset, from);
                     }
                     if (!cd)
                         fwrite (result, 1, rlen, stdout);
@@ -901,7 +919,7 @@ static int send_deleteResultSetRequest(const char *arg)
 #if HAVE_XML2
 static int send_srw(Z_SRW_PDU *sr)
 {
-    const char *charset = 0;
+    const char *charset = negotiationCharset;
     const char *host_port = 0;
     char *path = 0;
     char ctype[50];
@@ -999,6 +1017,8 @@ static int send_SRW_searchRequest(const char *arg)
     }
     odr_reset(srw_sr_odr_out);
 
+    setno = 1;
+
     /* save this for later .. when fetching individual records */
     srw_sr = sr = yaz_srw_get(srw_sr_odr_out, Z_SRW_searchRetrieve_request);
     sr->u.request->query_type = Z_SRW_query_type_cql;
@@ -1026,8 +1046,10 @@ static int send_searchRequest(const char *arg)
     Odr_oct ccl_query;
     YAZ_PQF_Parser pqf_parser;
     Z_External *ext;
+    QueryType myQueryType = queryType;
+    char pqfbuf[512];
 
-    if (queryType == QueryType_CCL2RPN)
+    if (myQueryType == QueryType_CCL2RPN)
     {
         rpn = ccl_find_str(bibset, arg, &error, &pos);
         if (error)
@@ -1035,7 +1057,33 @@ static int send_searchRequest(const char *arg)
             printf("CCL ERROR: %s\n", ccl_err_msg(error));
             return 0;
         }
+    } else if (myQueryType == QueryType_CQL2RPN) {
+       /* ### All this code should be wrapped in a utility function */
+       CQL_parser parser;
+       struct cql_node *node;
+       const char *addinfo;
+       if (cqltrans == 0) {
+            printf("Can't use CQL: no translation file.  Try set_cqlfile\n");
+           return 0;
+       }
+       parser = cql_parser_create();
+       if ((error = cql_parser_string(parser, arg)) != 0) {
+           /* ### must do better with the reporting here */
+            printf("CQL ERROR %d: presumably a syntax error?\n", error);
+            return 0;
+       }
+       node = cql_parser_result(parser);
+       if ((error = cql_transform_buf(cqltrans, node, pqfbuf,
+                                      sizeof pqfbuf)) != 0) {
+           error = cql_transform_error(cqltrans, &addinfo);
+            printf ("Couldn't convert CQL to PQF: error #%d (addinfo=%s)\n",
+                   error, addinfo);
+            return 0;
+        }
+       arg = pqfbuf;
+       myQueryType = QueryType_Prefix;
     }
+
     req->referenceId = set_refid (out);
     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
     {
@@ -1075,7 +1123,7 @@ static int send_searchRequest(const char *arg)
 
     req->query = &query;
 
-    switch (queryType)
+    switch (myQueryType)
     {
     case QueryType_Prefix:
         query.which = Z_Query_type_1;
@@ -1126,9 +1174,9 @@ static int send_searchRequest(const char *arg)
         printf ("Unsupported query type\n");
         return 0;
     }
-    send_apdu(apdu);
+    if (send_apdu(apdu))
+        printf("Sent searchRequest.\n");
     setno = 1;
-    printf("Sent searchRequest.\n");
     return 2;
 }
 
@@ -1665,7 +1713,7 @@ static int send_itemorder(const char *type, int itemno)
 
 static int only_z3950()
 {
-    if (conn && conn->protocol == PROTO_HTTP)
+    if (protocol == PROTO_HTTP)
     {
        printf ("Not supported by SRW\n");
        return 1;
@@ -1820,18 +1868,11 @@ static int cmd_find(const char *arg)
         printf("Find what?\n");
         return 0;
     }
-    if (!conn)
-    {
-        try_reconnect(); 
-        
-        if (!conn) {                                   
-            printf("Not connected yet\n");
-            return 0;
-        }
-    }
-    if (conn->protocol == PROTO_HTTP)
+    if (protocol == PROTO_HTTP)
     {
 #if HAVE_XML2
+        if (!conn)
+            cmd_open(0);
         if (!send_SRW_searchRequest(arg))
             return 0;
 #else
@@ -1840,6 +1881,15 @@ static int cmd_find(const char *arg)
     }
     else
     {
+        if (!conn)
+        {
+            try_reconnect(); 
+            
+            if (!conn) {                                       
+                printf("Not connected yet\n");
+                return 0;
+            }
+        }
         if (!send_searchRequest(arg))
             return 0;
     }
@@ -2036,20 +2086,14 @@ static int send_SRW_presentRequest(const char *arg)
 
 static void close_session (void)
 {
-    cs_close (conn);
+    if (conn)
+        cs_close (conn);
     conn = 0;
     if (session_mem)
     {
         nmem_destroy (session_mem);
         session_mem = NULL;
     }
-    if (srw_sr)
-    {
-        odr_destroy(srw_sr_odr_out);
-        srw_sr_odr_out = 0;
-        srw_sr = 0;
-    }
-    assert (srw_sr_odr_out == 0);
     sent_close = 0;
     odr_reset(out);
     odr_reset(in);
@@ -2090,14 +2134,11 @@ void process_close(Z_Close *req)
 
 static int cmd_show(const char *arg)
 {
-    if (!conn)
-    {
-        printf("Not connected yet\n");
-        return 0;
-    }
-    if (conn->protocol == PROTO_HTTP)
+    if (protocol == PROTO_HTTP)
     {
 #if HAVE_XML2
+        if (!conn)
+            cmd_open(0);
         if (!send_SRW_presentRequest(arg))
             return 0;
 #else
@@ -2106,6 +2147,11 @@ static int cmd_show(const char *arg)
     }
     else
     {
+        if (!conn)
+        {
+            printf("Not connected yet\n");
+            return 0;
+        }
         if (!send_presentRequest(arg))
             return 0;
     }
@@ -2477,6 +2523,8 @@ int cmd_querytype (const char *arg)
         queryType = QueryType_CCL2RPN;
     else if (!strcmp(arg, "cql"))
         queryType = QueryType_CQL;        
+    else if (!strcmp (arg, "cql2rpn") || !strcmp (arg, "cqlrpn"))
+        queryType = QueryType_CQL2RPN;
     else
     {
         printf ("Querytype must be one of:\n");
@@ -2484,6 +2532,7 @@ int cmd_querytype (const char *arg)
         printf (" ccl            - CCL query\n");
         printf (" ccl2rpn        - CCL query converted to RPN\n");
         printf (" cql            - CQL\n");
+        printf (" cql2rpn        - CQL query converted to RPN\n");
         return 0;
     }
     return 1;
@@ -2524,27 +2573,30 @@ int cmd_packagename(const char* arg)
     xfree (esPackageName);
     esPackageName = NULL;
     if (*arg)
-    {
-        esPackageName = (char *) xmalloc (strlen(arg)+1);
-        strcpy (esPackageName, arg);
-    }
+        esPackageName = xstrdup(arg);
     return 1;
 }
 
 int cmd_proxy(const char* arg)
 {
-    if (*arg == '\0') {
-               xfree (yazProxy);
-               yazProxy = NULL;
-       
-    }
     xfree (yazProxy);
     yazProxy = NULL;
     if (*arg)
-    {
-        yazProxy = (char *) xmalloc (strlen(arg)+1);
-        strcpy (yazProxy, arg);
-    } 
+        yazProxy = xstrdup (arg);
+    return 1;
+}
+
+int cmd_marccharset(const char *arg)
+{
+    char l1[30];
+
+    *l1 = 0;
+    if (sscanf(arg, "%29s", l1) < 1)
+        return 1;
+    xfree (marcCharset);
+    marcCharset = 0;
+    if (strcmp(l1, "-"))
+        marcCharset = xstrdup(l1);
     return 1;
 }
 
@@ -2555,17 +2607,51 @@ int cmd_charset(const char* arg)
     *l1 = *l2 = 0;
     if (sscanf(arg, "%29s %29s", l1, l2) < 1)
     {
-       printf("Current character set is `%s'\n", (yazCharset) ? yazCharset:NULL);
+       printf("Current negotiation character set is `%s'\n", 
+               negotiationCharset ? negotiationCharset: "none");
+       printf("Current output character set is `%s'\n", 
+               outputCharset ? outputCharset: "none");
        return 1;
     }
-    xfree (yazCharset);
-    yazCharset = NULL;
-    if (*l1)
-        yazCharset = xstrdup(l1);
+    xfree (negotiationCharset);
+    negotiationCharset = NULL;
+    if (*l1 && strcmp(l1, "-"))
+    {
+        negotiationCharset = xstrdup(l1);
+        printf ("Character set negotiation : %s\n", negotiationCharset);
+    }
+    else
+        printf ("Character set negotiation disabled\n");
     if (*l2)
     {
-        odr_set_charset (out, l1, l2);
-        odr_set_charset (in, l2, l1);
+        xfree (outputCharset);
+        outputCharset = 0;
+        if (!strcmp(l2, "auto") && codeset)
+        {
+            if (codeset)
+            {
+                printf ("output charset: %s\n", codeset);
+                outputCharset = xstrdup(codeset);
+
+
+            }
+            else
+                printf ("No codeset found on this system\n");
+        }
+        else if (strcmp(l2, "-"))
+            outputCharset = xstrdup(l2);
+        else
+            printf ("Output charset conversion disabled\n");
+    } 
+    if (outputCharset && negotiationCharset)
+    {
+        odr_set_charset (out, negotiationCharset, outputCharset);
+        odr_set_charset (in, outputCharset, negotiationCharset);
+    }
+    else
+    {
+        odr_set_charset (out, 0, 0);
+        odr_set_charset (in, 0, 0);
     }
     return 1;
 }
@@ -2684,6 +2770,22 @@ int cmd_set_cclfile(const char* arg)
     return 0;
 }
 
+int cmd_set_cqlfile(const char* arg)
+{
+    cql_transform_t newcqltrans;
+
+    if ((newcqltrans = cql_transform_open_fname(arg)) == 0) {
+        perror("unable to open CQL file");
+       return 0;
+    }
+    if (cqltrans != 0)
+       cql_transform_close(cqltrans);
+
+    cqltrans = newcqltrans;
+    strcpy(cql_fields, arg);
+    return 0;
+}
+
 int cmd_set_auto_reconnect(const char* arg)
 {  
     if(strlen(arg)==0) {
@@ -2870,6 +2972,10 @@ static void initialize(void)
         ccl_qual_file (bibset, inf);
         fclose (inf);
     }
+
+    cqltrans = cql_transform_open_fname(cql_fields);
+    /* If this fails, no problem: we detect cqltrans == 0 later */
+
 #if HAVE_READLINE_READLINE_H
     rl_attempted_completion_function = (CPPFunction*)readline_completer;
 #endif
@@ -2892,14 +2998,16 @@ static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
 {
     int i;
 
-    printf ("Received SRW SearchRetrieveResponse\n");
+    printf ("Received SRW SearchRetrieve Response\n");
     
     for (i = 0; i<res->num_diagnostics; i++)
     {
-        printf ("SRW diagnostic %d %s\nDetails: %s\n",
+        printf ("SRW diagnostic %d %s\n",
                 *res->diagnostics[i].code,
-                yaz_diag_srw_str(*res->diagnostics[i].code),
-                res->diagnostics[i].details);
+                yaz_diag_srw_str(*res->diagnostics[i].code));
+
+       if (res->diagnostics[i].details)
+            printf ("Details: %s\n", res->diagnostics[i].details);
     }
     if (res->numberOfRecords)
         printf ("Number of hits: %d\n", *res->numberOfRecords);
@@ -2913,7 +3021,7 @@ static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
             setno = *rec->recordPosition + 1;
         }
         if (rec->recordSchema)
-            printf (" scheam=%d", *rec->recordSchema);
+            printf (" schema=%s", rec->recordSchema);
         printf ("\n");
         if (rec->recordData_buf && rec->recordData_len)
         {
@@ -3002,7 +3110,7 @@ void wait_and_handle_response()
     while(conn)
     {
         res = cs_get(conn, &netbuffer, &netbufferlen);
-        if (reconnect_ok && res <= 0 && conn->protocol == PROTO_HTTP)
+        if (reconnect_ok && res <= 0 && protocol == PROTO_HTTP)
         {
             cs_close(conn);
             conn = 0;
@@ -3269,7 +3377,8 @@ int cmd_list_all(const char* args) {
             printf("Authentication       : Unknown\n");
         }
     }
-    if ( yazCharset ) printf("Character set        : `%s'\n", (yazCharset) ? yazCharset:NULL);
+    if (negotiationCharset)
+        printf("Neg. Character set   : `%s'\n", negotiationCharset);
     
     /* bases */
     printf("Bases                : ");
@@ -3278,6 +3387,7 @@ int cmd_list_all(const char* args) {
     
     /* Query options */
     printf("CCL file             : %s\n",ccl_fields);
+    printf("CQL file             : %s\n",cql_fields);
     printf("Query type           : %s\n",query_type_as_string(queryType));
     
     printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
@@ -3368,7 +3478,8 @@ static struct {
     {"update", cmd_update, "<action> <recid> [<file>]",NULL,0,NULL},
     {"packagename", cmd_packagename, "<packagename>",NULL,0,NULL},
     {"proxy", cmd_proxy, "[('tcp'|'ssl')]<host>[':'<port>]",NULL,0,NULL},
-    {"charset", cmd_charset, "<charset_name>",NULL,0,NULL},
+    {"charset", cmd_charset, "<nego_charset> <output_charset>",NULL,0,NULL},
+    {"marccharset", cmd_marccharset, "<charset_name>",NULL,0,NULL},
     {"lang", cmd_lang, "<language_code>",NULL,0,NULL},
     {".", cmd_source, "<filename>",NULL,1,NULL},
     {"!", cmd_subshell, "Subshell command",NULL,1,NULL},
@@ -3376,6 +3487,7 @@ static struct {
     {"set_berfile", cmd_set_berfile, "<filename>",NULL,1,NULL},
     {"set_marcdump", cmd_set_marcdump," <filename>",NULL,1,NULL},
     {"set_cclfile", cmd_set_cclfile," <filename>",NULL,1,NULL},
+    {"set_cqlfile", cmd_set_cqlfile," <filename>",NULL,1,NULL},
     {"set_auto_reconnect", cmd_set_auto_reconnect," on|off",complete_auto_reconnect,1,NULL},
        {"set_otherinfo", cmd_set_otherinfo,"<otherinfoinddex> <oid> <string>",NULL,0,NULL},
     {"register_oid", cmd_register_oid,"<name> <class> <oid>",NULL,0,NULL},
@@ -3663,6 +3775,17 @@ static void client(void)
     }
 }
 
+static void show_version(void)
+{
+    char vstr[20];
+
+    yaz_version(vstr, 0);
+    printf ("YAZ version: %s\n", YAZ_VERSION);
+    if (strcmp(vstr, YAZ_VERSION))
+       printf ("YAZ DLL/SO: %s\n", vstr);
+    exit(0);
+}
+
 int main(int argc, char **argv)
 {
     char *prog = *argv;
@@ -3681,7 +3804,7 @@ int main(int argc, char **argv)
 #endif
 #endif
 
-    while ((ret = options("k:c:a:b:m:v:p:u:t:", argv, argc, &arg)) != -2)
+    while ((ret = options("k:c:q:a:b:m:v:p:u:t:V", argv, argc, &arg)) != -2)
     {
         switch (ret)
         {
@@ -3704,12 +3827,16 @@ int main(int argc, char **argv)
             }
             break;
         case 't':
-            codeset = arg;
+            outputCharset = xstrdup(arg);
             break;
         case 'c':
             strncpy (ccl_fields, arg, sizeof(ccl_fields)-1);
             ccl_fields[sizeof(ccl_fields)-1] = '\0';
             break;
+        case 'q':
+            strncpy (cql_fields, arg, sizeof(cql_fields)-1);
+            cql_fields[sizeof(cql_fields)-1] = '\0';
+            break;
         case 'b':
             if (!strcmp(arg, "-"))
                 ber_file=stderr;
@@ -3734,12 +3861,16 @@ int main(int argc, char **argv)
             }
             break;
         case 'v':
-            yaz_log_init (yaz_log_mask_str(arg), "", NULL);
+            yaz_log_init(yaz_log_mask_str(arg), "", 0);
+            break;
+        case 'V':
+           show_version();
             break;
         default:
             fprintf (stderr, "Usage: %s [-m <marclog>] [ -a <apdulog>] "
-                     "[-b berdump] [-c cclfields]\n      [-p <proxy-addr>] [-u <auth>] "
-                     "[-k size] [<server-addr>]\n",
+                     "[-b berdump] [-c cclfields] \n"
+                    "[-q cqlfields] [-p <proxy-addr>] [-u <auth>] "
+                     "[-k size] [-V] [<server-addr>]\n",
                      prog);
             exit (1);
         }