yaz-client: fix bad encoding of "itemorder item"
[yaz-moved-to-github.git] / client / client.c
index c9351d2..c778d77 100644 (file)
@@ -1,16 +1,18 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2010 Index Data
+ * Copyright (C) 1995-2012 Index Data
  * See the file LICENSE for details.
  */
 /** \file client.c
  *  \brief yaz-client program
  */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <time.h>
-#include <ctype.h>
 #ifndef WIN32
 #include <signal.h>
 #endif
@@ -46,7 +48,6 @@
 #include <yaz/comstack.h>
 
 #include <yaz/oid_db.h>
-#define NO_OID 1
 #include <yaz/proto.h>
 #include <yaz/marcdisp.h>
 #include <yaz/diagbib1.h>
@@ -106,6 +107,7 @@ static int largeSetLowerBound = 1;
 static int mediumSetPresentNumber = 0;
 static Z_ElementSetNames *elementSetNames = 0;
 static Z_FacetList *facet_list = 0;
+static ODR facet_odr = 0;
 static Odr_int setno = 1;                   /* current set offset */
 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
 #define RECORDSYNTAX_MAX 20
@@ -140,10 +142,12 @@ static int auto_wait = 1;
 static Odr_bitmask z3950_options;
 static int z3950_version = 3;
 static int scan_stepSize = 0;
-static int scan_position = 1;
+static char scan_position[64];
 static int scan_size = 20;
 static char cur_host[200];
 static Odr_int last_hit_count = 0;
+static int pretty_xml = 0;
+static Odr_int sru_maximumRecords = 0;
 
 typedef enum {
     QueryType_Prefix,
@@ -280,7 +284,6 @@ int send_apdu(Z_APDU *a)
     buf = odr_getbuf(out, &len, 0);
     if (ber_file)
         odr_dumpBER(ber_file, buf, len);
-    /* printf ("sending APDU of size %d\n", len); */
     do_hex_dump(buf, len);
     if (cs_put(conn, buf, len) < 0)
     {
@@ -759,10 +762,6 @@ static int cmd_open(const char *arg)
         strncpy(cur_host, arg, sizeof(cur_host)-1);
         cur_host[sizeof(cur_host)-1] = 0;
     }
-    /* TODO Make facet definition survive the open command without crashing */
-    /* TODO Fix deallocation */
-    facet_list = 0;
-
     set_base("");
     r = session_connect(cur_host);
     if (conn && conn->protocol == PROTO_HTTP)
@@ -846,6 +845,31 @@ static void print_record(const char *buf, size_t len)
         printf("\n");
 }
 
+static void print_xml_record(const char *buf, size_t len)
+{
+    int has_printed = 0;
+#if YAZ_HAVE_XML2
+    if (pretty_xml)
+    {
+        xmlDocPtr doc;
+        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
+        doc = xmlParseMemory(buf, len);
+        if (doc)
+        {
+            xmlChar *xml_mem;
+            int xml_size;
+            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
+            fwrite(xml_mem, 1, xml_size, stdout);
+            xmlFree(xml_mem);
+            xmlFreeDoc(doc);
+            has_printed = 1;
+        }
+    }
+#endif
+    if (!has_printed)
+        fwrite(buf, 1, len, stdout);
+}
+
 static void display_record(Z_External *r)
 {
     const Odr_oid *oid = r->direct_reference;
@@ -905,7 +929,8 @@ static void display_record(Z_External *r)
             || !oid_oidcmp(oid, yaz_oid_recsyn_xml)
             || !oid_oidcmp(oid, yaz_oid_recsyn_html))
         {
-            fwrite(octet_buf, 1, octet_len, stdout);
+            print_xml_record(octet_buf, octet_len);
+
         }
         else if (yaz_oid_is_iso2709(oid))
         {
@@ -1388,7 +1413,7 @@ static char *encode_SRW_term(ODR o, const char *q)
 }
 
 
-static int send_SRW_scanRequest(const char *arg, int pos, int num)
+static int send_SRW_scanRequest(const char *arg, Odr_int *pos, int num)
 {
     Z_SRW_PDU *sr = 0;
 
@@ -1409,7 +1434,7 @@ static int send_SRW_scanRequest(const char *arg, int pos, int num)
         printf("Only CQL and PQF supported in SRW\n");
         return 0;
     }
-    sr->u.scan_request->responsePosition = odr_intdup(out, pos);
+    sr->u.scan_request->responsePosition = pos;
     sr->u.scan_request->maximumTerms = odr_intdup(out, num);
     return send_srw(sr);
 }
@@ -1454,6 +1479,7 @@ static int send_SRW_searchRequest(const char *arg)
         printf("Only CQL and PQF supported in SRW\n");
         return 0;
     }
+    sru_maximumRecords = 0;
     sr->u.request->maximumRecords = odr_intdup(out, 0);
     sr->u.request->facetList = facet_list;
     if (record_schema)
@@ -1636,16 +1662,16 @@ static void display_term(Z_Term *term)
     switch (term->which)
     {
     case Z_Term_general:
-        printf("    %.*s", term->u.general->len, term->u.general->buf);
+        printf("%.*s", term->u.general->len, term->u.general->buf);
         break;
     case Z_Term_characterString:
-        printf("    %s", term->u.characterString);
+        printf("%s", term->u.characterString);
         break;
     case Z_Term_numeric:
-        printf("    " ODR_INT_PRINTF, *term->u.numeric);
+        printf(ODR_INT_PRINTF, *term->u.numeric);
         break;
     case Z_Term_null:
-        printf("    null");
+        printf("null");
         break;
     }
 }
@@ -1674,17 +1700,18 @@ static void display_facet(Z_FacetField *facet)
         struct yaz_facet_attr attr_values;
         attr_values.errcode = 0;
         attr_values.limit = -1;
-        attr_values.useattr = 0;
+        attr_values.useattr = "NONE";
         attr_values.relation = "default";
 
         yaz_facet_attr_get_z_attributes(al, &attr_values);
         if (!attr_values.errcode)
         {
             int term_index;
-            printf("  %s (%d): \n", attr_values.useattr, /* attr_values.relation, attr_values.limit, */ facet->num_terms);
+            printf("  %s (%d): \n", attr_values.useattr, facet->num_terms);
             for (term_index = 0 ; term_index < facet->num_terms; term_index++)
             {
                 Z_FacetTerm *facetTerm = facet->terms[term_index];
+                printf("    ");
                 display_term(facetTerm->term);
                 printf(" (" NMEM_INT_PRINTF ")\n", *facetTerm->count);
             }
@@ -2103,6 +2130,7 @@ static Z_External *create_external_itemRequest(void)
     }
     else
     {
+        item_request_buf = odr_getbuf (out, &item_request_size, 0);
         r = (Z_External *) odr_malloc(out, sizeof(*r));
         r->direct_reference = odr_oiddup(out, yaz_oid_general_isoill_1);
         r->indirect_reference = 0;
@@ -2280,22 +2308,6 @@ static int only_z3950(void)
     return 0;
 }
 
-static int is_SRW(void)
-{
-    if (!conn)
-    {
-        printf("Not connected yet\n");
-        return 1;
-    }
-    if (protocol == PROTO_HTTP && yaz_matchstr(sru_method, "solr"))
-    {
-        printf("Not supported by SRW\n");
-        return 1;
-    }
-    return 0;
-}
-
-
 static int cmd_update_common(const char *arg, int version);
 
 static int cmd_update(const char *arg)
@@ -2367,9 +2379,9 @@ static int cmd_update_common(const char *arg, int version)
 
 #if YAZ_HAVE_XML2
     if (protocol == PROTO_HTTP)
-        return send_SRW_update(action_no, recid_buf, rec_buf, rec_len);
+        return send_SRW_update(action_no, recid, rec_buf, rec_len);
 #endif
-    return send_Z3950_update(version, action_no, recid_buf, rec_buf, rec_len);
+    return send_Z3950_update(version, action_no, recid, rec_buf, rec_len);
 }
 
 #if YAZ_HAVE_XML2
@@ -2903,21 +2915,17 @@ static int cmd_find(const char *arg)
 
 static int cmd_facets(const char *arg)
 {
-    /* TODO Wrong odr. Loosing memory */
-    ODR odr = odr_createmem(ODR_ENCODE);
-    int size = 0;
+    if (!facet_odr)
+        facet_odr = odr_createmem(ODR_ENCODE);
+    odr_reset(facet_odr);
+
     if (!*arg)
     {
         facet_list = 0;
         printf("Facets cleared.\n");
         return 0;
     }
-    size = strlen(arg);
-    if (is_SRW()) {
-        printf("WARN: No supported for SRW/SRU.\n");
-    }
-    facet_list = yaz_pqf_parse_facet_list(odr, arg);
-
+    facet_list = yaz_pqf_parse_facet_list(facet_odr, arg);
     if (!facet_list)
     {
         printf("Invalid facet list: %s", arg);
@@ -2926,7 +2934,6 @@ static int cmd_facets(const char *arg)
     return 1;
 }
 
-
 static int cmd_delete(const char *arg)
 {
     if (only_z3950())
@@ -2985,21 +2992,50 @@ static int cmd_setnames(const char *arg)
 
 /* PRESENT SERVICE ----------------------------- */
 
+size_t check_token(const char *haystack, const char *token)
+{
+    size_t len = strlen(token);
+    size_t extra;
+    if (strncmp(haystack, token, len))
+        return 0;
+    for (extra = 0; haystack[extra + len] != '\0'; extra++)
+        if (!strchr(" \r\n\t", haystack[extra + len]))
+        {
+            if (extra)
+                break;
+            else
+                return 0;  /* no whitespace after token */
+        }
+    return extra + len;
+}
+
 static int parse_show_args(const char *arg_c, char *setstring,
                            Odr_int *start, Odr_int *number)
 {
     char *end_ptr;
     Odr_int start_position;
+    size_t token_len;
 
     if (setnumber >= 0)
         sprintf(setstring, "%d", setnumber);
     else
         *setstring = '\0';
+    
+    token_len = check_token(arg_c, "format");
+    if (token_len)
+    {
+        pretty_xml = 1;
+        arg_c += token_len;
+    }
+    else
+        pretty_xml = 0;
 
-    if (!strcmp(arg_c, "all"))
+    token_len = check_token(arg_c, "all");
+    if (token_len)
     {
         *number = last_hit_count;
         *start = 1;
+        return 1;
     }
     start_position = odr_strtol(arg_c, &end_ptr, 10);
     if (end_ptr == arg_c)
@@ -3007,7 +3043,7 @@ static int parse_show_args(const char *arg_c, char *setstring,
     *start = start_position;
     if (*end_ptr == '\0')
         return 1;
-    while (isspace(*(unsigned char *)end_ptr))
+    while (yaz_isspace(*end_ptr))
         end_ptr++;
     if (*end_ptr != '+')
     {
@@ -3024,7 +3060,7 @@ static int parse_show_args(const char *arg_c, char *setstring,
     }
     if (*end_ptr == '\0')
         return 1;
-    while (isspace(*(unsigned char *)end_ptr))
+    while (yaz_isspace(*end_ptr))
         end_ptr++;
     if (*end_ptr != '+')
     {
@@ -3140,6 +3176,7 @@ static int send_SRW_presentRequest(const char *arg)
     if (!parse_show_args(arg, setstring, &setno, &nos))
         return 0;
     sr->u.request->startRecord = odr_intdup(out, setno);
+    sru_maximumRecords = nos;
     sr->u.request->maximumRecords = odr_intdup(out, nos);
     if (record_schema)
         sr->u.request->recordSchema = record_schema;
@@ -3287,13 +3324,14 @@ static int cmd_cancel_find(const char *arg)
 }
 
 static int send_Z3950_scanrequest(const char *set,  const char *query,
-                                  Odr_int pp, Odr_int num, const char *term)
+                                  Odr_int *pos, Odr_int num, const char *term)
 {
     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
     Z_ScanRequest *req = apdu->u.scanRequest;
 
     if (only_z3950())
         return 0;
+    printf("query: %s\n", query);
     if (queryType == QueryType_CCL2RPN)
     {
         int error, pos;
@@ -3363,7 +3401,7 @@ static int send_Z3950_scanrequest(const char *set,  const char *query,
     req->num_databaseNames = num_databaseNames;
     req->databaseNames = databaseNames;
     req->numberOfTermsRequested = &num;
-    req->preferredPositionInResponse = &pp;
+    req->preferredPositionInResponse = pos;
     req->stepSize = odr_intdup(out, scan_stepSize);
 
     if (set)
@@ -3538,9 +3576,17 @@ static int cmd_scanstep(const char *arg)
 
 static int cmd_scanpos(const char *arg)
 {
-    int r = sscanf(arg, "%d", &scan_position);
-    if (r == 0)
-        scan_position = 1;
+    if (!strcmp(arg, "none"))
+        strcpy(scan_position, "none");
+    else
+    {
+        int dummy;
+        int r = sscanf(arg, "%d", &dummy);
+        if (r == 1 && strlen(arg) < sizeof(scan_position)-1)
+            strcpy(scan_position, arg);
+        else
+            printf("specify number of none for scanpos\n");
+    }
     return 0;
 }
 
@@ -3554,6 +3600,28 @@ static int cmd_scansize(const char *arg)
 
 static int cmd_scan_common(const char *set, const char *arg)
 {
+    Odr_int pos, *pos_p = 0;
+    const char *scan_term = 0;
+    const char *scan_query = 0;
+
+    if (!*arg)
+    {
+        pos = 1;
+        pos_p = &pos;
+        scan_query = last_scan_query;
+        scan_term = last_scan_line;
+    }
+    else 
+    {
+        strcpy(last_scan_query, arg);
+        scan_query = arg;
+        if (strcmp(scan_position, "none"))
+        {
+            pos = odr_atoi(scan_position);
+            pos_p = &pos;
+        }
+    }
+
     if (protocol == PROTO_HTTP)
     {
 #if YAZ_HAVE_XML2
@@ -3561,16 +3629,8 @@ static int cmd_scan_common(const char *set, const char *arg)
             session_connect(cur_host);
         if (!conn)
             return 0;
-        if (*arg)
-        {
-            if (send_SRW_scanRequest(arg, scan_position, scan_size) < 0)
-                return 0;
-        }
-        else
-        {
-            if (send_SRW_scanRequest(last_scan_line, 1, scan_size) < 0)
-                return 0;
-        }
+        if (send_SRW_scanRequest(scan_query, pos_p, scan_size) < 0)
+            return 0;
         return 2;
 #else
         return 0;
@@ -3591,19 +3651,9 @@ static int cmd_scan_common(const char *set, const char *arg)
             printf("Target doesn't support scan\n");
             return 0;
         }
-        if (*arg)
-        {
-            strcpy(last_scan_query, arg);
-            if (send_Z3950_scanrequest(set, arg,
-                                       scan_position, scan_size, 0) < 0)
-                return 0;
-        }
-        else
-        {
-            if (send_Z3950_scanrequest(set, last_scan_query,
-                                      1, scan_size, last_scan_line) < 0)
-                return 0;
-        }
+        if (send_Z3950_scanrequest(set, scan_query, pos_p,
+                                   scan_size, scan_term) < 0)
+            return 0;
         return 2;
     }
 }
@@ -4229,6 +4279,8 @@ static void initialize(const char *rc_file)
         exit(1);
     }
     
+    strcpy(scan_position, "1");
+
     setvbuf(stdout, 0, _IONBF, 0);
     if (apdu_file)
         odr_setprint(print, apdu_file);
@@ -4282,7 +4334,7 @@ static void handle_srw_record(Z_SRW_record *rec)
     printf("\n");
     if (rec->recordData_buf && rec->recordData_len)
     {
-        printf("%.*s", rec->recordData_len, rec->recordData_buf);
+        print_xml_record(rec->recordData_buf, rec->recordData_len);
         marc_file_write(rec->recordData_buf, rec->recordData_len);
     }
     else
@@ -4315,11 +4367,21 @@ static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
     }
     if (res->numberOfRecords)
         printf("Number of hits: " ODR_INT_PRINTF "\n", *res->numberOfRecords);
-    if (res->facetList) {
+    if (res->facetList)
         display_facets(res->facetList);
-    }
-    for (i = 0; i<res->num_records; i++)
+    if (res->suggestions)
+        printf("Suggestions:\n%s\n", res->suggestions);
+    for (i = 0; i < res->num_records; i++)
+    {
+        if (i >= sru_maximumRecords)
+        {
+            printf("SRU server returns extra records. Skipping "
+                   ODR_INT_PRINTF " records.\n",
+                   res->num_records - sru_maximumRecords);
+            break;
+        }
         handle_srw_record(res->records + i);
+    }
 }
 
 static void handle_srw_scan_term(Z_SRW_scanTerm *term)
@@ -4397,8 +4459,9 @@ static void http_response(Z_HTTP_Response *hres)
         {
             Z_SOAP *soap_package = 0;
             ODR o = odr_createmem(ODR_DECODE);
-            Z_SOAP_Handler soap_handlers[3] = {
+            Z_SOAP_Handler soap_handlers[4] = {
                 {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+                {YAZ_XMLNS_SRU_v2_response, 0, (Z_SOAP_fun) yaz_srw_codec},
                 {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
                 {0, 0, 0}
             };
@@ -5120,7 +5183,7 @@ static void process_cmd_line(char* line)
 
         for (; *p; ++p)
         {
-            if (!isspace(*(unsigned char *) p))
+            if (!yaz_isspace(*p))
                 lastnonspace = p;
         }
         if (lastnonspace)